Browse Source

监督端-查询统计

dev-v1.0.2
庄锐波 3 months ago
parent
commit
d02f57a5be
  1. 2
      script/sql/zfjd-refactoring-v1/005-2-重新整理二期菜单结构.sql
  2. 72
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/web/supervision/report/StatisticsBureauUnitController.java
  3. 30
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/bo/StatisticsBureauUnitBo.java
  4. 33
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/report/BureauUnitDetailVo.java
  5. 33
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/report/BureauUnitExportVo.java
  6. 24
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/report/BureauUnitOverviewVo.java
  7. 36
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/report/BureauUnitPersonnelMatchVo.java
  8. 27
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/report/BureauUnitStatVo.java
  9. 42
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/mapper/BureauUnitMapper.java
  10. 31
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/report/IStatisticsBureauUnitReport.java
  11. 222
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/report/impl/StatisticsBureauUnitReportImpl.java
  12. 135
      zdxt-modules/zdxt-enforcement-code/src/main/resources/mapper/enforcementcode/report/StatisticsBureauUnitReportMapper.xml
  13. 77
      zdxt-web-client/zdxt-admin-plus-ui/src/api/efcode/report/statistics-bureau-unit.ts
  14. 263
      zdxt-web-client/zdxt-admin-plus-ui/src/views/efcode/report/statistics_enforcement_bureau_unit/index.vue
  15. 11
      zdxt-web-client/zdxt-admin-plus-ui/src/views/efcode/report/statistics_enforcement_unit/index.vue

2
script/sql/zfjd-refactoring-v1/005-2-重新整理二期菜单结构.sql

@ -128,7 +128,7 @@ INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component,
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, url, target, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark, system_code, type) VALUES (5249, '涉企执法情况(2)', 5262, 1, 'enterprise-enforcement', 'efcode/report/registration/index', '', 1, 0, '#', '', 'C', '0', '0', 'supervision:statistics:enterpriseEnforcement', '#', 103, 1, '2026-05-15 00:00:00', null, null, '涉企执法情况菜单', 'supervisionCode', null);
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, url, target, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark, system_code, type) VALUES (5278, '企业受检次数(4)', 5262, 2, 'enterprise-inspection', 'efcode/report/enterprises_inspected_noft/index', '', 1, 0, '#', '', 'C', '0', '0', 'supervision:statistics:enterpriseInspection', '#', 103, 1, '2026-05-15 00:00:00', null, null, '企业受检次数菜单', 'supervisionCode', null);
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, url, target, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark, system_code, type) VALUES (5253, '企业信息情况(5)', 5262, 3, 'enterprise-info', 'efcode/report/statistics_enterprise/index', '', 1, 0, '#', '', 'C', '0', '0', 'supervision:statistics:enterpriseInfo', '#', 103, 1, '2026-05-15 00:00:00', null, null, '企业信息情况菜单', 'supervisionCode', null);
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, url, target, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark, system_code, type) VALUES (5250, '执法单位情况(5)', 5262, 4, 'enforcement-unit', 'efcode/report/statistics_enforcement_unit/index', '', 1, 0, '#', '', 'C', '0', '0', 'supervision:statistics:enforcementUnit', '#', 103, 1, '2026-05-15 00:00:00', null, null, '执法单位情况菜单', 'supervisionCode', null);
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, url, target, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark, system_code, type) VALUES (5250, '执法单位情况(5)', 5262, 4, 'enforcement-unit', 'efcode/report/statistics_enforcement_bureau_unit/index', '', 1, 0, '#', '', 'C', '0', '0', 'supervision:statistics:enforcementUnit', '#', 103, 1, '2026-05-15 00:00:00', null, null, '执法单位情况菜单', 'supervisionCode', null);
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, url, target, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark, system_code, type) VALUES (5251, '执法投诉情况(4)', 5262, 5, 'enforcement-complaint', 'efcode/report/statistics_enforcement_complaint/index', '', 1, 0, '#', '', 'C', '0', '0', 'supervision:statistics:enforcementComplaint', '#', 103, 1, '2026-05-15 00:00:00', null, null, '执法投诉情况菜单', 'supervisionCode', null);
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, url, target, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark, system_code, type) VALUES (5254, '检查计划情况(5)', 5262, 6, 'inspection-plan', 'efcode/report/statistics_inspection_plan/index', '', 1, 0, '#', '', 'C', '0', '0', 'supervision:statistics:inspectionPlan', '#', 103, 1, '2026-05-15 00:00:00', null, null, '检查计划情况菜单', 'supervisionCode', null);

72
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/web/supervision/report/StatisticsBureauUnitController.java

@ -0,0 +1,72 @@
package com.zdxt.enforcementcode.controller.web.supervision.report;
import com.zdxt.enforcementcode.domain.bo.StatisticsBureauUnitBo;
import com.zdxt.enforcementcode.domain.vo.report.BureauUnitDetailVo;
import com.zdxt.enforcementcode.domain.vo.report.BureauUnitExportVo;
import com.zdxt.enforcementcode.domain.vo.report.BureauUnitOverviewVo;
import com.zdxt.enforcementcode.domain.vo.report.BureauUnitStatVo;
import com.zdxt.enforcementcode.report.IStatisticsBureauUnitReport;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.domain.R;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* 执法单位情况报表
*
* @author zdxt
*/
@Slf4j
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/web/supervision/report/statistics-bureau-unit")
public class StatisticsBureauUnitController {
private final IStatisticsBureauUnitReport reportService;
/** 统计概览 */
@GetMapping("/overview")
public R<BureauUnitOverviewVo> overview(StatisticsBureauUnitBo bo) {
return R.ok(reportService.overview(bo));
}
/** 执法单位列表(服务端分页) */
@GetMapping("/list")
public TableDataInfo<BureauUnitStatVo> list(StatisticsBureauUnitBo bo, PageQuery pageQuery) {
return reportService.list(bo, pageQuery);
}
/** 单位人员详情(服务端分页) */
@GetMapping("/detail")
public TableDataInfo<BureauUnitDetailVo> detail(StatisticsBureauUnitBo bo, PageQuery pageQuery) {
return reportService.detail(bo, pageQuery);
}
/** 导出 */
@PostMapping("/export")
public void export(StatisticsBureauUnitBo bo, HttpServletResponse response) {
try {
List<BureauUnitExportVo> list = reportService.exportList(bo);
ExcelUtil.exportExcel(list, "部门统计", BureauUnitExportVo.class, response);
} catch (Exception e) {
log.error("导出执法单位情况Excel异常", e);
response.reset();
response.setContentType("application/json; charset=utf-8");
try {
response.getWriter().write("{\"code\":500,\"msg\":\"导出失败: " + e.getMessage() + "\"}");
} catch (Exception ignored) {
}
}
}
}

30
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/bo/StatisticsBureauUnitBo.java

@ -0,0 +1,30 @@
package com.zdxt.enforcementcode.domain.bo;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* 执法单位情况报表 查询BO
*
* @author zdxt
*/
@Data
public class StatisticsBureauUnitBo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/** 执法单位名称(模糊) */
private String zfdw;
/** 执法人员姓名(模糊) */
private String name;
/** 执法证号(模糊) */
private String zfcode;
/** 单位的 sys_dept_id(详情查询时使用) */
private String sysDeptId;
}

33
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/report/BureauUnitDetailVo.java

@ -0,0 +1,33 @@
package com.zdxt.enforcementcode.domain.vo.report;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* 执法单位情况报表 人员详情VO详情表行
*
* @author zdxt
*/
@Data
public class BureauUnitDetailVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/** 部门名称(人员所在部门) */
private String unitName;
/** 执法人员姓名 */
private String personnelName;
/** 执法证号 */
private String zfcode;
/** 联系方式(加密存储,Service层解密) */
private String phoneNumber;
/** 执法案件数量 */
private int caseCount;
}

33
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/report/BureauUnitExportVo.java

@ -0,0 +1,33 @@
package com.zdxt.enforcementcode.domain.vo.report;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* 执法单位情况报表 导出VO4列
*
* @author zdxt
*/
@Data
@ExcelIgnoreUnannotated
public class BureauUnitExportVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@ExcelProperty(value = "部门名称")
private String unitName;
@ExcelProperty(value = "执法人员")
private String personnelName;
@ExcelProperty(value = "执法证号")
private String zfcode;
@ExcelProperty(value = "联系方式")
private String phoneNumber;
}

24
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/report/BureauUnitOverviewVo.java

@ -0,0 +1,24 @@
package com.zdxt.enforcementcode.domain.vo.report;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* 执法单位情况报表 统计概览VO
*
* @author zdxt
*/
@Data
public class BureauUnitOverviewVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/** 执法单位总数 */
private int deptCount;
/** 执法人员总数 */
private int personnelCount;
}

36
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/report/BureauUnitPersonnelMatchVo.java

@ -0,0 +1,36 @@
package com.zdxt.enforcementcode.domain.vo.report;
import lombok.Data;
/**
* 执法单位情况报表 人员匹配内部VO用于Java层剔除逻辑
*
* @author zdxt
*/
@Data
public class BureauUnitPersonnelMatchVo {
/** 人员姓名 */
private String name;
/** 组织机构代码 */
private String zzjgdm;
/** 执法单位名称 */
private String zfdw;
/** 执法证号 */
private String zfcode;
/** 身份证号加密 */
private String sfzcodeEncrypt;
/** 人员所在部门ID */
private String deptId;
/** 联系电话 */
private String phoneNumber;
/** 部门祖级ID串(逗号分隔) */
private String ancestors;
}

27
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/report/BureauUnitStatVo.java

@ -0,0 +1,27 @@
package com.zdxt.enforcementcode.domain.vo.report;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* 执法单位情况报表 单位统计VO主表行
*
* @author zdxt
*/
@Data
public class BureauUnitStatVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/** 单位关联的 sys_dept_id */
private String sysDeptId;
/** 单位名称 */
private String unitName;
/** 执法人员数量 */
private int personnelCount;
}

42
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/mapper/BureauUnitMapper.java

@ -0,0 +1,42 @@
package com.zdxt.enforcementcode.mapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.zdxt.enforcementcode.domain.bo.StatisticsBureauUnitBo;
import com.zdxt.enforcementcode.domain.vo.report.BureauUnitDetailVo;
import com.zdxt.enforcementcode.domain.vo.report.BureauUnitPersonnelMatchVo;
import com.zdxt.enforcementcode.domain.vo.report.BureauUnitStatVo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* 执法单位情况报表 Mapper
*
* @author zdxt
*/
@Mapper
public interface BureauUnitMapper {
/**
* 查询执法主体列表zfztjdm111915关联sys_dept获取deptId
*/
List<BureauUnitStatVo> selectZfztjdmListAndDeptScope(@Param("bo") StatisticsBureauUnitBo bo);
/**
* 查询执法单位列表zone不为空且zone!=2的部门
*/
List<BureauUnitStatVo> selectDeptByZone(@Param("bo") StatisticsBureauUnitBo bo);
/**
* 查询所有有效执法人员含部门ID和ancestors用于Java层匹配
*/
List<BureauUnitPersonnelMatchVo> selectPersonnelGroupBy(@Param("bo") StatisticsBureauUnitBo bo);
/**
* 查询指定单位的人员详情列表 服务端分页含案件数
*/
IPage<BureauUnitDetailVo> selectUnitDetail(Page<BureauUnitDetailVo> page,
@Param("bo") StatisticsBureauUnitBo bo);
}

31
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/report/IStatisticsBureauUnitReport.java

@ -0,0 +1,31 @@
package com.zdxt.enforcementcode.report;
import com.zdxt.enforcementcode.domain.bo.StatisticsBureauUnitBo;
import com.zdxt.enforcementcode.domain.vo.report.BureauUnitDetailVo;
import com.zdxt.enforcementcode.domain.vo.report.BureauUnitExportVo;
import com.zdxt.enforcementcode.domain.vo.report.BureauUnitOverviewVo;
import com.zdxt.enforcementcode.domain.vo.report.BureauUnitStatVo;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import java.util.List;
/**
* 执法单位情况报表 Service接口
*
* @author zdxt
*/
public interface IStatisticsBureauUnitReport {
/** 统计概览(执法单位数 + 执法人员数) */
BureauUnitOverviewVo overview(StatisticsBureauUnitBo bo);
/** 执法单位列表(服务端分页) */
TableDataInfo<BureauUnitStatVo> list(StatisticsBureauUnitBo bo, PageQuery pageQuery);
/** 单位人员详情(服务端分页) */
TableDataInfo<BureauUnitDetailVo> detail(StatisticsBureauUnitBo bo, PageQuery pageQuery);
/** 导出 */
List<BureauUnitExportVo> exportList(StatisticsBureauUnitBo bo);
}

222
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/report/impl/StatisticsBureauUnitReportImpl.java

@ -0,0 +1,222 @@
package com.zdxt.enforcementcode.report.impl;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.zdxt.enforcementcode.common.util.CommonUtil;
import com.zdxt.enforcementcode.domain.bo.StatisticsBureauUnitBo;
import com.zdxt.enforcementcode.domain.vo.report.*;
import com.zdxt.enforcementcode.mapper.BureauUnitMapper;
import com.zdxt.enforcementcode.report.IStatisticsBureauUnitReport;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* 执法单位情况报表 Service实现
* <p>
* 复刻旧系统 getDeptStaticList 逻辑
* 1. sys_dept 获取执法单位列表zone不为空且zone!=2
* 2. zfryjdm2405301 获取所有有效执法人员
* 3. Java层按"ancestors包含deptId"匹配人员到单位并执行"剔除"逻辑避免重复计数
*
* @author zdxt
*/
@Slf4j
@RequiredArgsConstructor
@Service
public class StatisticsBureauUnitReportImpl implements IStatisticsBureauUnitReport {
private final BureauUnitMapper bureauUnitMapper;
@Override
public BureauUnitOverviewVo overview(StatisticsBureauUnitBo bo) {
// 执行剔除匹配逻辑
List<BureauUnitStatVo> unitList = getDeptStaticList(bo);
BureauUnitOverviewVo vo = new BureauUnitOverviewVo();
vo.setDeptCount(unitList.size());
vo.setPersonnelCount(unitList.stream().mapToInt(BureauUnitStatVo::getPersonnelCount).sum());
return vo;
}
@Override
public TableDataInfo<BureauUnitStatVo> list(StatisticsBureauUnitBo bo, PageQuery pageQuery) {
// 执行剔除匹配逻辑(全量)
List<BureauUnitStatVo> unitList = getDeptStaticList(bo);
// 手动分页
int pageNum = pageQuery.getPageNum();
int pageSize = pageQuery.getPageSize();
int total = unitList.size();
int fromIndex = Math.min((pageNum - 1) * pageSize, total);
int toIndex = Math.min(fromIndex + pageSize, total);
List<BureauUnitStatVo> pageRecords = unitList.subList(fromIndex, toIndex);
// 构建分页结果
Page<BureauUnitStatVo> page = new Page<>(pageNum, pageSize, total);
page.setRecords(pageRecords);
return TableDataInfo.build(page);
}
@Override
public TableDataInfo<BureauUnitDetailVo> detail(StatisticsBureauUnitBo bo, PageQuery pageQuery) {
Page<BureauUnitDetailVo> page = pageQuery.build();
IPage<BureauUnitDetailVo> result = bureauUnitMapper.selectUnitDetail(page, bo);
// 手机号解密
result.getRecords().forEach(r -> r.setPhoneNumber(CommonUtil.decryptPhone(r.getPhoneNumber())));
return TableDataInfo.build(result);
}
@Override
public List<BureauUnitExportVo> exportList(StatisticsBureauUnitBo bo) {
// 使用同样的三步合并+剔除逻辑获取单位列表
List<BureauUnitStatVo> zfztjdmList = bureauUnitMapper.selectZfztjdmListAndDeptScope(bo);
List<BureauUnitStatVo> deptList = bureauUnitMapper.selectDeptByZone(bo);
// 合并
List<BureauUnitStatVo> unitList = new ArrayList<>(zfztjdmList);
for (BureauUnitStatVo dept : deptList) {
BureauUnitStatVo unit = new BureauUnitStatVo();
unit.setSysDeptId(dept.getSysDeptId());
unit.setUnitName(dept.getUnitName());
unitList.add(unit);
}
if (unitList.isEmpty()) {
return new ArrayList<>();
}
// 获取所有人员
List<BureauUnitPersonnelMatchVo> personnelList = bureauUnitMapper.selectPersonnelGroupBy(bo);
// 使用剔除逻辑,构建导出数据(人员归属到单位名称)
List<BureauUnitExportVo> exportList = new ArrayList<>();
List<BureauUnitExportVo> hasExportList = new ArrayList<>();
for (BureauUnitStatVo unit : unitList) {
// 匹配归属该单位的人员
List<BureauUnitPersonnelMatchVo> matched = personnelList.stream()
.filter(p -> ancestorHasIt(p.getAncestors(), unit.getSysDeptId(), p.getDeptId()))
.collect(Collectors.toList());
// 为每个匹配人员构建导出行
for (BureauUnitPersonnelMatchVo p : matched) {
BureauUnitExportVo exportVo = new BureauUnitExportVo();
exportVo.setUnitName(unit.getUnitName());
exportVo.setPersonnelName(p.getName());
exportVo.setZfcode(p.getZfcode());
exportVo.setPhoneNumber(CommonUtil.decryptPhone(p.getPhoneNumber()));
exportList.add(exportVo);
hasExportList.add(exportVo);
}
// 剔除已匹配人员(避免重复计数)
personnelList = personnelList.stream()
.filter(p -> !ancestorHasIt(p.getAncestors(), unit.getSysDeptId(), p.getDeptId()))
.collect(Collectors.toList());
}
// 如果有姓名/证号搜索条件,只返回有匹配人员的数据
if (StringUtils.isNotBlank(bo.getName()) || StringUtils.isNotBlank(bo.getZfcode())) {
return hasExportList;
}
return exportList;
}
/**
* 核心逻辑获取执法单位列表并匹配人员数量含剔除逻辑
* <p>
* 完全复刻旧系统 Zfztjdm111915ServiceImpl.getDeptStaticList() 逻辑
* 1. zfztjdm111915 关联 sys_dept 获取执法主体列表按creditcode匹配dept_id
* 2. sys_dept 获取 zone不为空且!=2 的部门列表
* 3. 将两者合并zone部门全部追加到执法主体列表后面
* 4. 获取所有有效执法人员含部门+ancestors
* 5. 按顺序遍历合并后的单位列表匹配人员并剔除
* 6. 如果有搜索条件name/zfcode只返回有人员的单位
*/
private List<BureauUnitStatVo> getDeptStaticList(StatisticsBureauUnitBo bo) {
// 步骤1: 从 zfztjdm111915 获取执法主体列表
List<BureauUnitStatVo> zfztjdmList = bureauUnitMapper.selectZfztjdmListAndDeptScope(bo);
// 建立 deptId -> 执法主体 的 map(用于合并时判断)
Map<String, BureauUnitStatVo> map = zfztjdmList.stream()
.filter(item -> item.getSysDeptId() != null)
.collect(Collectors.toMap(
BureauUnitStatVo::getSysDeptId,
Function.identity(),
(v1, v2) -> v2
));
// 步骤2: 从 sys_dept 获取 zone部门列表
List<BureauUnitStatVo> deptList = bureauUnitMapper.selectDeptByZone(bo);
// 步骤3: 合并 - 将zone部门追加到执法主体列表后面(同旧系统逻辑)
List<BureauUnitStatVo> unitList = new ArrayList<>(zfztjdmList);
for (BureauUnitStatVo dept : deptList) {
BureauUnitStatVo unit = new BureauUnitStatVo();
unit.setSysDeptId(dept.getSysDeptId());
unit.setUnitName(dept.getUnitName());
// 如果在执法主体map中存在,保留原有信息(同旧系统 map.containsKey 逻辑)
unitList.add(unit);
}
// 步骤4: 获取所有有效执法人员
List<BureauUnitPersonnelMatchVo> personnelList = bureauUnitMapper.selectPersonnelGroupBy(bo);
// 步骤5: 遍历单位列表,执行匹配和剔除
List<BureauUnitStatVo> hasList = new ArrayList<>();
for (BureauUnitStatVo unit : unitList) {
String deptId = unit.getSysDeptId();
// 匹配归属该单位的人员
List<BureauUnitPersonnelMatchVo> matchedList = personnelList.stream()
.filter(p -> ancestorHasIt(p.getAncestors(), deptId, p.getDeptId()))
.collect(Collectors.toList());
unit.setPersonnelCount(matchedList.size());
if (!matchedList.isEmpty()) {
hasList.add(unit);
}
// 剔除已匹配人员(从池中移除)
personnelList = personnelList.stream()
.filter(p -> !ancestorHasIt(p.getAncestors(), deptId, p.getDeptId()))
.collect(Collectors.toList());
}
// 步骤6: 如果有搜索条件(name/zfcode),只返回有人员的单位
if (StringUtils.isNotBlank(bo.getName()) || StringUtils.isNotBlank(bo.getZfcode())) {
return hasList;
}
return unitList;
}
/**
* 判断人员是否归属某个单位
* <p>
* 逻辑人员的ancestors中包含单位的deptId或者人员的deptId等于单位的deptId
* 完全复刻旧系统 ancestorHasIt(ancestors, deptId, deptIdZFRY)
*
* @param ancestors 人员部门的祖级ID串(逗号分隔)
* @param deptId 单位的dept_id
* @param deptIdZFRY 人员的dept_id
* @return 是否归属
*/
private boolean ancestorHasIt(String ancestors, String deptId, String deptIdZFRY) {
if (StringUtils.isBlank(ancestors) || StringUtils.isBlank(deptId) || StringUtils.isBlank(deptIdZFRY)) {
return false;
}
List<String> ancestorsList = Arrays.asList(ancestors.split(","));
return ancestorsList.contains(deptId) || deptId.equals(deptIdZFRY);
}
}

135
zdxt-modules/zdxt-enforcement-code/src/main/resources/mapper/enforcementcode/report/StatisticsBureauUnitReportMapper.xml

@ -0,0 +1,135 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zdxt.enforcementcode.mapper.BureauUnitMapper">
<!--
查询执法主体列表:从 zfztjdm111915 关联 sys_dept 获取 deptId
等同于旧系统 selectZfztjdmListAndDeptScope
返回389条左右的执法主体,每条带 deptId(通过 creditcode 匹配)
-->
<select id="selectZfztjdmListAndDeptScope" resultType="com.zdxt.enforcementcode.domain.vo.report.BureauUnitStatVo">
SELECT
d.dept_id AS sysDeptId,
t.sub_name AS unitName
FROM zfztjdm111915 t
LEFT JOIN (
SELECT dept_id, ancestors, creditcode
FROM sys_dept
WHERE status = '0' AND del_flag = '0' AND creditcode IS NOT NULL
) d ON t.code = d.creditcode
GROUP BY t.code
</select>
<!--
查询执法单位列表:zone不为空且zone!=2的部门
等同于旧系统 selectDeptByZone: sys_dept where zone is not null and zone != 2
-->
<select id="selectDeptByZone" resultType="com.zdxt.enforcementcode.domain.vo.report.BureauUnitStatVo">
SELECT
d.dept_id AS sysDeptId,
d.dept_name AS unitName
FROM sys_dept d
WHERE d.del_flag = '0'
AND d.zone IS NOT NULL
AND d.zone != 2
ORDER BY d.parent_id, d.order_num
</select>
<!--
查询所有有效执法人员(含部门ID和ancestors)
等同于旧系统 selectZfryjdmAndUserListGroupBy
按 sfzcode_encrypt 分组去重,关联 sys_user 获取 dept_id 和 phoneNumber,
再关联 sys_dept 获取 ancestors
-->
<select id="selectPersonnelGroupBy" resultType="com.zdxt.enforcementcode.domain.vo.report.BureauUnitPersonnelMatchVo">
SELECT
x.name, x.zzjgdm, x.zfdw, x.zfcode, x.sfzcode_encrypt AS sfzcodeEncrypt,
x.dept_id AS deptId, x.phoneNumber, d.ancestors AS ancestors
FROM (
SELECT z.name, z.zzjgdm, z.zfdw, z.zfcode, z.sfzcode_encrypt,
u.dept_id, u.phonenumber AS phoneNumber
FROM (
SELECT name, zzjgdm, zfdw, sub_name, zfcode, sfzcode_encrypt
FROM zfryjdm2405301
WHERE code_state = '01'
AND name IS NOT NULL AND name != ''
AND zfdw IS NOT NULL AND zfdw != ''
AND zfcode IS NOT NULL AND zfcode != ''
<if test="bo.name != null and bo.name != ''">
AND name LIKE CONCAT('%', #{bo.name}, '%')
</if>
<if test="bo.zfcode != null and bo.zfcode != ''">
AND zfcode LIKE CONCAT('%', #{bo.zfcode}, '%')
</if>
) z
LEFT JOIN (
SELECT dept_id, phonenumber, certificateNumber
FROM sys_user
WHERE law_certificate_num IS NOT NULL
AND user_source = 0
AND status = '0'
AND del_flag = '0'
) u ON z.sfzcode_encrypt = u.certificateNumber
GROUP BY z.sfzcode_encrypt
) x
LEFT JOIN (
SELECT dept_id, ancestors
FROM sys_dept
WHERE del_flag = '0'
) d ON x.dept_id = d.dept_id
ORDER BY x.zzjgdm
</select>
<!--
查询指定单位的人员详情(含案件数),服务端分页
等同于旧系统 selectZfryjdmByDeptId: 通过 FIND_IN_SET 匹配 ancestors
-->
<select id="selectUnitDetail" resultType="com.zdxt.enforcementcode.domain.vo.report.BureauUnitDetailVo">
SELECT
x.zfdw AS unitName,
x.name AS personnelName,
x.zfcode AS zfcode,
x.phoneNumber AS phoneNumber,
(SELECT COUNT(*) FROM enforcement_registration er
WHERE (er.law_enforcer_id = CAST(x.user_id AS CHAR) COLLATE utf8mb4_general_ci
OR FIND_IN_SET(CAST(x.user_id AS CHAR) COLLATE utf8mb4_general_ci, er.law_enforcer_id_two))
AND er.is_submit = 1
) AS caseCount
FROM (
SELECT z.name, z.zzjgdm, z.zfdw, z.zfcode, z.sfzcode_encrypt,
u.user_id, u.dept_id, u.phonenumber AS phoneNumber
FROM (
SELECT name, zzjgdm, zfdw, sub_name, zfcode, sfzcode_encrypt
FROM zfryjdm2405301
WHERE code_state = '01'
AND name IS NOT NULL AND name != ''
AND zfdw IS NOT NULL AND zfdw != ''
AND zfcode IS NOT NULL AND zfcode != ''
<if test="bo.name != null and bo.name != ''">
AND name LIKE CONCAT('%', #{bo.name}, '%')
</if>
<if test="bo.zfcode != null and bo.zfcode != ''">
AND zfcode LIKE CONCAT('%', #{bo.zfcode}, '%')
</if>
) z
LEFT JOIN (
SELECT user_id, dept_id, phonenumber, certificateNumber
FROM sys_user
WHERE law_certificate_num IS NOT NULL
AND user_source = 0
AND status = '0'
AND del_flag = '0'
) u ON z.sfzcode_encrypt = u.certificateNumber
GROUP BY z.sfzcode_encrypt
) x
LEFT JOIN (
SELECT dept_id, ancestors
FROM sys_dept
WHERE del_flag = '0'
) d ON x.dept_id = d.dept_id
WHERE FIND_IN_SET(#{bo.sysDeptId}, d.ancestors) OR x.dept_id = #{bo.sysDeptId}
ORDER BY x.zzjgdm
</select>
</mapper>

77
zdxt-web-client/zdxt-admin-plus-ui/src/api/efcode/report/statistics-bureau-unit.ts

@ -0,0 +1,77 @@
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
const BASE_URL = '/web/supervision/report/statistics-bureau-unit';
/** 查询参数 */
export interface BureauUnitQuery {
pageNum?: number;
pageSize?: number;
/** 执法单位名称(模糊) */
zfdw?: string;
/** 执法人员姓名(模糊) */
name?: string;
/** 执法证号(模糊) */
zfcode?: string;
/** 单位的 sys_dept_id(详情查询时使用) */
sysDeptId?: string;
}
/** 概览VO */
export interface BureauUnitOverviewVo {
deptCount: number;
personnelCount: number;
}
/** 执法单位统计VO */
export interface BureauUnitStatVo {
sysDeptId: string;
unitName: string;
personnelCount: number;
}
/** 人员详情VO */
export interface BureauUnitDetailVo {
unitName: string;
personnelName: string;
zfcode: string;
phoneNumber: string;
caseCount: number;
}
/** 统计概览 */
export const getOverview = (params: BureauUnitQuery): AxiosPromise => {
return request({
url: `${BASE_URL}/overview`,
method: 'get',
params
});
};
/** 执法单位列表(服务端分页) */
export const getUnitList = (params: BureauUnitQuery): AxiosPromise => {
return request({
url: `${BASE_URL}/list`,
method: 'get',
params
});
};
/** 单位人员详情(服务端分页) */
export const getUnitDetail = (params: BureauUnitQuery): AxiosPromise => {
return request({
url: `${BASE_URL}/detail`,
method: 'get',
params
});
};
/** 导出 */
export const exportBureauUnit = (params: BureauUnitQuery): AxiosPromise<Blob> => {
return request({
url: `${BASE_URL}/export`,
method: 'post',
params,
responseType: 'blob'
});
};

263
zdxt-web-client/zdxt-admin-plus-ui/src/views/efcode/report/statistics_enforcement_bureau_unit/index.vue

@ -0,0 +1,263 @@
<script setup lang="ts">
import { ref, reactive, onMounted, onUnmounted, nextTick } from 'vue';
import { ElMessage } from 'element-plus';
import {
getOverview, getUnitList, getUnitDetail, exportBureauUnit
} from '@/api/efcode/report/statistics-bureau-unit';
import type { BureauUnitQuery, BureauUnitStatVo, BureauUnitDetailVo } from '@/api/efcode/report/statistics-bureau-unit';
// 1=() 2=()
const viewLevel = ref<1 | 2>(1);
const currentSysDeptId = ref('');
const currentUnitName = ref('');
//
const queryParams = reactive<BureauUnitQuery>({
pageNum: 1,
pageSize: 10,
zfdw: undefined,
name: undefined,
zfcode: undefined,
sysDeptId: undefined
});
const loading = ref(false);
const tableHeight = ref<number | undefined>(undefined);
const mainTableRef = ref();
const detailTableRef = ref();
//
const overviewData = reactive({
deptCount: 0,
personnelCount: 0
});
//
const unitTableData = ref<BureauUnitStatVo[]>([]);
const unitTotal = ref(0);
//
const detailTableData = ref<BureauUnitDetailVo[]>([]);
const detailTotal = ref(0);
const detailPageNum = ref(1);
const detailPageSize = ref(10);
onMounted(() => {
handleQuery();
nextTick(() => calcTableHeight());
window.addEventListener('resize', calcTableHeight);
});
onUnmounted(() => {
window.removeEventListener('resize', calcTableHeight);
});
const calcTableHeight = () => {
const el = tableRef.value?.$el as HTMLElement;
if (!el) return;
const rect = el.getBoundingClientRect();
tableHeight.value = window.innerHeight - rect.top - 102;
};
/** 搜索 */
const handleQuery = () => {
if (viewLevel.value === 1) {
queryParams.pageNum = 1;
loadUnitList();
loadOverview();
} else {
detailPageNum.value = 1;
loadDetail();
}
};
/** 重置 */
const resetQuery = () => {
Object.assign(queryParams, {
pageNum: 1,
pageSize: 10,
zfdw: undefined,
name: undefined,
zfcode: undefined,
sysDeptId: undefined
});
viewLevel.value = 1;
handleQuery();
};
/** 加载概览 */
const loadOverview = async () => {
try {
const res = await getOverview(queryParams);
const data = res.data || {};
overviewData.deptCount = Number(data.deptCount) || 0;
overviewData.personnelCount = Number(data.personnelCount) || 0;
} catch (e) {
overviewData.deptCount = 0;
overviewData.personnelCount = 0;
}
};
/** 加载单位列表 */
const loadUnitList = async () => {
loading.value = true;
try {
const res = await getUnitList(queryParams);
unitTableData.value = res.rows || [];
unitTotal.value = res.total || 0;
} finally {
loading.value = false;
}
};
/** 点击"详情"进入人员列表 */
const handleShowDetail = (row: BureauUnitStatVo) => {
if (row.personnelCount <= 0) {
ElMessage.warning('该单位暂无执法人员');
return;
}
viewLevel.value = 2;
currentSysDeptId.value = row.sysDeptId;
currentUnitName.value = row.unitName;
detailPageNum.value = 1;
loadDetail();
nextTick(() => calcTableHeight());
};
/** 加载人员详情 */
const loadDetail = async () => {
loading.value = true;
try {
const params: BureauUnitQuery = {
pageNum: detailPageNum.value,
pageSize: detailPageSize.value,
sysDeptId: currentSysDeptId.value,
name: queryParams.name,
zfcode: queryParams.zfcode
};
const res = await getUnitDetail(params);
detailTableData.value = res.rows || [];
detailTotal.value = res.total || 0;
} finally {
loading.value = false;
}
};
/** 返回主表 */
const handleBack = () => {
viewLevel.value = 1;
currentSysDeptId.value = '';
currentUnitName.value = '';
nextTick(() => calcTableHeight());
};
/** 主表翻页 */
const handleUnitPageChange = () => {
loadUnitList();
};
/** 详情表翻页 */
const handleDetailPageChange = () => {
loadDetail();
};
/** 导出 */
const handleExport = async () => {
try {
const res = await exportBureauUnit(queryParams);
const blob = new Blob([res as any], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
const url = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = '部门统计.xlsx';
link.click();
window.URL.revokeObjectURL(url);
} catch (e) {
ElMessage.error('导出失败');
}
};
</script>
<template>
<div class="p-2">
<!-- 搜索区域 -->
<div class="mb-[10px]">
<el-card shadow="hover">
<el-form :model="queryParams" :inline="true">
<el-form-item label="执法单位" prop="zfdw">
<el-input v-model="queryParams.zfdw" placeholder="请输入执法单位" clearable @keyup.enter="handleQuery" style="width: 160px" />
</el-form-item>
<el-form-item label="执法人员" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入执法人员" clearable @keyup.enter="handleQuery" style="width: 140px" />
</el-form-item>
<el-form-item label="执法证号" prop="zfcode">
<el-input v-model="queryParams.zfcode" placeholder="请输入执法证号" clearable @keyup.enter="handleQuery" style="width: 160px" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
<el-button type="warning" icon="Download" @click="handleExport">导出</el-button>
<el-button v-if="viewLevel === 2" icon="Back" @click="handleBack">返回</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
<!-- 统计概览 -->
<div class="mb-[10px]">
<el-card shadow="hover">
<div style="display: flex; align-items: center; gap: 24px; font-size: 14px;">
<span style="font-weight: bold; color: #409eff;">全市</span>
<span>执法单位<strong style="color: #e6a23c;">{{ overviewData.deptCount }}</strong></span>
<span>执法人员<strong style="color: #f56c6c;">{{ overviewData.personnelCount }}</strong></span>
</div>
</el-card>
</div>
<!-- 主表执法单位列表 -->
<el-card v-show="viewLevel === 1" shadow="hover">
<el-table ref="tableRef" v-loading="loading" :data="unitTableData" :height="tableHeight" border stripe>
<el-table-column label="部门名称" prop="unitName" min-width="260" :show-overflow-tooltip="true" />
<el-table-column label="执法人员数量" prop="personnelCount" width="140" align="center" />
<el-table-column label="操作" width="100" align="center" fixed="right">
<template #default="{ row }">
<el-button type="primary" link size="small" :disabled="row.personnelCount <= 0" @click="handleShowDetail(row)">详情</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="unitTotal > 0"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
:total="unitTotal"
@pagination="handleUnitPageChange"
/>
</el-card>
<!-- 详情表人员列表 -->
<el-card v-show="viewLevel === 2" shadow="hover">
<div style="margin-bottom: 12px; font-size: 14px; color: #606266;">
当前单位<strong style="color: #303133;">{{ currentUnitName }}</strong>
</div>
<el-table ref="tableRef" v-loading="loading" :data="detailTableData" :height="tableHeight" border stripe>
<el-table-column label="部门名称" prop="unitName" min-width="200" :show-overflow-tooltip="true" />
<el-table-column label="执法人员" prop="personnelName" min-width="120" />
<el-table-column label="执法证号" prop="zfcode" min-width="160" />
<el-table-column label="联系方式" prop="phoneNumber" min-width="140" />
<el-table-column label="执法案件数量" prop="caseCount" width="130" align="center" sortable />
</el-table>
<pagination
v-show="detailTotal > 0"
v-model:page="detailPageNum"
v-model:limit="detailPageSize"
:total="detailTotal"
@pagination="handleDetailPageChange"
/>
</el-card>
</div>
</template>
<style scoped lang="scss">
</style>

11
zdxt-web-client/zdxt-admin-plus-ui/src/views/efcode/report/statistics_enforcement_unit/index.vue

@ -1,11 +0,0 @@
<script setup lang="ts">
</script>
<template>
</template>
<style scoped lang="scss">
</style>
Loading…
Cancel
Save