15 changed files with 1026 additions and 12 deletions
@ -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) { |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -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; |
|||
} |
|||
@ -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; |
|||
} |
|||
@ -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; |
|||
|
|||
/** |
|||
* 执法单位情况报表 — 导出VO(4列) |
|||
* |
|||
* @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; |
|||
} |
|||
@ -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; |
|||
} |
|||
@ -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; |
|||
} |
|||
@ -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; |
|||
} |
|||
@ -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); |
|||
} |
|||
@ -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); |
|||
} |
|||
@ -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); |
|||
} |
|||
} |
|||
@ -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> |
|||
@ -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' |
|||
}); |
|||
}; |
|||
@ -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> |
|||
@ -1,11 +0,0 @@ |
|||
<script setup lang="ts"> |
|||
|
|||
</script> |
|||
|
|||
<template> |
|||
|
|||
</template> |
|||
|
|||
<style scoped lang="scss"> |
|||
|
|||
</style> |
|||
Loading…
Reference in new issue