Browse Source

研判分析--step6基本重构完成

master
buaixuexideshitongxue 2 weeks ago
parent
commit
f73e82c4d4
  1. 4
      src/main/java/com/biutag/supervision/pojo/dto/report/ReportViewModel.java
  2. 2
      src/main/java/com/biutag/supervision/pojo/dto/report/accountability/AccountabilityUnitSection.java
  3. 21
      src/main/java/com/biutag/supervision/pojo/dto/report/accountability/ReportAccountabilitySectionRequestDTO.java
  4. 24
      src/main/java/com/biutag/supervision/pojo/dto/report/accountability/ReportAccountabilityStatsRequestDTO.java
  5. 41
      src/main/java/com/biutag/supervision/pojo/dto/report/accountability/ReportAccountabilityStatsResponseDTO.java
  6. 16
      src/main/java/com/biutag/supervision/pojo/dto/report/base/ReportBaseQueryRequestDTO.java
  7. 25
      src/main/java/com/biutag/supervision/pojo/dto/report/base/ReportBaseQueryResponseDTO.java
  8. 21
      src/main/java/com/biutag/supervision/pojo/dto/report/businessLine/ReportBusinessLineSectionRequestDTO.java
  9. 19
      src/main/java/com/biutag/supervision/pojo/dto/report/closed/ClosedStatsCalcRequestDTO.java
  10. 44
      src/main/java/com/biutag/supervision/pojo/dto/report/closed/ClosedStatsCalcResponseDTO.java
  11. 21
      src/main/java/com/biutag/supervision/pojo/dto/report/overview/ReportOverviewSectionRequestDTO.java
  12. 21
      src/main/java/com/biutag/supervision/pojo/dto/report/unitInvestigation/ReportUnitInvestigationSectionRequestDTO.java
  13. 30
      src/main/java/com/biutag/supervision/service/report/ReportAccountabilitySectionService.java
  14. 17
      src/main/java/com/biutag/supervision/service/report/ReportAccountabilityStatsService.java
  15. 17
      src/main/java/com/biutag/supervision/service/report/ReportBaseQueryService.java
  16. 27
      src/main/java/com/biutag/supervision/service/report/ReportBusinessLineSectionService.java
  17. 14
      src/main/java/com/biutag/supervision/service/report/ReportClosedStatsService.java
  18. 875
      src/main/java/com/biutag/supervision/service/report/ReportDataServiceImpl.java
  19. 17
      src/main/java/com/biutag/supervision/service/report/ReportOverviewSectionService.java
  20. 16
      src/main/java/com/biutag/supervision/service/report/ReportUnitInvestigationSectionService.java
  21. 175
      src/main/java/com/biutag/supervision/service/report/impl/ReportAccountabilitySectionServiceImpl.java
  22. 76
      src/main/java/com/biutag/supervision/service/report/impl/ReportAccountabilityStatsServiceImpl.java
  23. 53
      src/main/java/com/biutag/supervision/service/report/impl/ReportBaseQueryServiceImpl.java
  24. 425
      src/main/java/com/biutag/supervision/service/report/impl/ReportBusinessLineSectionServiceImpl.java
  25. 63
      src/main/java/com/biutag/supervision/service/report/impl/ReportClosedStatsServiceImpl.java
  26. 68
      src/main/java/com/biutag/supervision/service/report/impl/ReportDataServiceImpl.java
  27. 268
      src/main/java/com/biutag/supervision/service/report/impl/ReportOverviewSectionServiceImpl.java
  28. 118
      src/main/java/com/biutag/supervision/service/report/impl/ReportUnitInvestigationSectionServiceImpl.java
  29. BIN
      src/main/resources/static/templates/督审一体化平台研判分析报告.docx

4
src/main/java/com/biutag/supervision/pojo/dto/report/ReportViewModel.java

@ -1,6 +1,6 @@
package com.biutag.supervision.pojo.dto.report; package com.biutag.supervision.pojo.dto.report;
import com.biutag.supervision.pojo.dto.report.accountability.AccountabilityDepartmentSection; import com.biutag.supervision.pojo.dto.report.accountability.AccountabilityUnitSection;
import com.biutag.supervision.pojo.dto.report.accountability.AccountabilityOverviewSection; import com.biutag.supervision.pojo.dto.report.accountability.AccountabilityOverviewSection;
import com.biutag.supervision.pojo.dto.report.accountability.AccountabilityPersonalSection; import com.biutag.supervision.pojo.dto.report.accountability.AccountabilityPersonalSection;
import com.biutag.supervision.pojo.dto.report.businessLine.BusinessLineDetailSection; import com.biutag.supervision.pojo.dto.report.businessLine.BusinessLineDetailSection;
@ -53,7 +53,7 @@ public class ReportViewModel {
// 分开 预留可修改空间 // 分开 预留可修改空间
@Schema(description = "单位问责追责情况明细") @Schema(description = "单位问责追责情况明细")
private AccountabilityDepartmentSection accountabilityUnitDetailSection; private AccountabilityUnitSection accountabilityUnitDetailSection;
@Schema(description = "个人问责追责情况") @Schema(description = "个人问责追责情况")
private AccountabilityPersonalSection accountabilityPersonOverviewSection; private AccountabilityPersonalSection accountabilityPersonOverviewSection;

2
src/main/java/com/biutag/supervision/pojo/dto/report/accountability/AccountabilityDepartmentSection.java → src/main/java/com/biutag/supervision/pojo/dto/report/accountability/AccountabilityUnitSection.java

@ -10,7 +10,7 @@ import java.util.List;
@Getter @Getter
@Setter @Setter
@Schema(description = "对单位追责问责情况") @Schema(description = "对单位追责问责情况")
public class AccountabilityDepartmentSection { public class AccountabilityUnitSection {
@Schema(description = "问责单位总次数", example = "6") @Schema(description = "问责单位总次数", example = "6")
private Integer totalCount; private Integer totalCount;

21
src/main/java/com/biutag/supervision/pojo/dto/report/accountability/ReportAccountabilitySectionRequestDTO.java

@ -0,0 +1,21 @@
package com.biutag.supervision.pojo.dto.report.accountability;
import com.biutag.supervision.pojo.param.NegativeQueryParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@Schema(description = "报表问责模块构建请求DTO")
public class ReportAccountabilitySectionRequestDTO {
@Schema(description = "查询参数")
private NegativeQueryParam negativeQueryParam;
@Schema(description = "统计开始时间", example = "2026年3月1日")
private String periodStart;
@Schema(description = "统计结束时间", example = "2026年3月31日")
private String periodEnd;
}

24
src/main/java/com/biutag/supervision/pojo/dto/report/accountability/ReportAccountabilityStatsRequestDTO.java

@ -0,0 +1,24 @@
package com.biutag.supervision.pojo.dto.report.accountability;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
import java.util.ArrayList;
import java.util.List;
/**
* @ClassName ReportAccountabilityStatsRequestDTO
* @Description 报表问责统计请求DTO
* @Author shihao
* @Date 2026/3/9 15:29
*/
@Getter
@Setter
@Schema(description = "报表问责统计请求DTO")
public class ReportAccountabilityStatsRequestDTO {
@Schema(description = "问题ID列表")
private List<String> negativeIds = new ArrayList<>();
}

41
src/main/java/com/biutag/supervision/pojo/dto/report/accountability/ReportAccountabilityStatsResponseDTO.java

@ -0,0 +1,41 @@
package com.biutag.supervision.pojo.dto.report.accountability;
import com.biutag.supervision.pojo.entity.NegativeBlame;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
import java.util.ArrayList;
import java.util.List;
/**
* @ClassName ReportAccountabilityStatsResponseDTO
* @Description 报表问责统计结果DTO
* @Author shihao
* @Date 2026/3/9 15:29
*/
@Getter
@Setter
@Schema(description = "报表问责统计结果DTO")
public class ReportAccountabilityStatsResponseDTO {
@Schema(description = "问责总数 = 个人问责 + 领导问责 + 单位问责", example = "12")
private int total;
@Schema(description = "个人问责数 = 个人问责 + 领导问责", example = "8")
private int personal;
@Schema(description = "单位问责数", example = "4")
private int unit;
@Schema(description = "个人问责列表")
private List<NegativeBlame> personalBlames = new ArrayList<>();
@Schema(description = "领导问责列表")
private List<NegativeBlame> leadBlames = new ArrayList<>();
@Schema(description = "单位问责列表")
private List<NegativeBlame> unitBlames = new ArrayList<>();
}

16
src/main/java/com/biutag/supervision/pojo/dto/report/base/ReportBaseQueryRequestDTO.java

@ -0,0 +1,16 @@
package com.biutag.supervision.pojo.dto.report.base;
import com.biutag.supervision.pojo.param.NegativeQueryParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@Schema(description = "报表基础查询请求DTO")
public class ReportBaseQueryRequestDTO {
@Schema(description = "报表查询参数")
private NegativeQueryParam negativeQueryParam;
}

25
src/main/java/com/biutag/supervision/pojo/dto/report/base/ReportBaseQueryResponseDTO.java

@ -0,0 +1,25 @@
package com.biutag.supervision.pojo.dto.report.base;
import com.biutag.supervision.pojo.vo.NegativeQueryVo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
import java.util.ArrayList;
import java.util.List;
@Getter
@Setter
@Schema(description = "报表基础查询结果DTO")
public class ReportBaseQueryResponseDTO {
@Schema(description = "本期数据列表")
private List<NegativeQueryVo> currentList = new ArrayList<>();
@Schema(description = "环比数据列表")
private List<NegativeQueryVo> momList = new ArrayList<>();
@Schema(description = "同比数据列表")
private List<NegativeQueryVo> yoyList = new ArrayList<>();
}

21
src/main/java/com/biutag/supervision/pojo/dto/report/businessLine/ReportBusinessLineSectionRequestDTO.java

@ -0,0 +1,21 @@
package com.biutag.supervision.pojo.dto.report.businessLine;
import com.biutag.supervision.pojo.param.NegativeQueryParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@Schema(description = "报表业务条线模块构建请求DTO")
public class ReportBusinessLineSectionRequestDTO {
@Schema(description = "查询参数")
private NegativeQueryParam negativeQueryParam;
@Schema(description = "统计开始时间", example = "2026年3月1日")
private String periodStart;
@Schema(description = "统计结束时间", example = "2026年3月31日")
private String periodEnd;
}

19
src/main/java/com/biutag/supervision/pojo/dto/report/closed/ClosedStatsCalcRequestDTO.java

@ -0,0 +1,19 @@
package com.biutag.supervision.pojo.dto.report.closed;
import com.biutag.supervision.pojo.vo.NegativeQueryVo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
import java.util.ArrayList;
import java.util.List;
@Getter
@Setter
@Schema(description = "办结统计计算请求DTO")
public class ClosedStatsCalcRequestDTO {
@Schema(description = "待统计的问题数据列表")
private List<NegativeQueryVo> negativeQueryVoList = new ArrayList<>();
}

44
src/main/java/com/biutag/supervision/pojo/dto/report/closed/ClosedStatsCalcResponseDTO.java

@ -0,0 +1,44 @@
package com.biutag.supervision.pojo.dto.report.closed;
import com.biutag.supervision.pojo.vo.NegativeQueryVo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
@Getter
@Setter
@Schema(description = "办结统计计算结果DTO")
public class ClosedStatsCalcResponseDTO {
@Schema(description = "办结总数")
private int closedCount;
@Schema(description = "属实数量")
private int verifiedCount;
@Schema(description = "基本属实数量")
private int basicallyVerifiedCount;
@Schema(description = "不属实数量")
private int unverifiedCount;
@Schema(description = "查实率(百分比)")
private BigDecimal verifiedRate;
@Schema(description = "办结问题列表")
private List<NegativeQueryVo> closedList = new ArrayList<>();
@Schema(description = "属实问题列表")
private List<NegativeQueryVo> verifiedList = new ArrayList<>();
@Schema(description = "基本属实问题列表")
private List<NegativeQueryVo> basicallyVerifiedList = new ArrayList<>();
@Schema(description = "不属实问题列表")
private List<NegativeQueryVo> unverifiedList = new ArrayList<>();
}

21
src/main/java/com/biutag/supervision/pojo/dto/report/overview/ReportOverviewSectionRequestDTO.java

@ -0,0 +1,21 @@
package com.biutag.supervision.pojo.dto.report.overview;
import com.biutag.supervision.pojo.param.NegativeQueryParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@Schema(description = "报表总体概览构建请求DTO")
public class ReportOverviewSectionRequestDTO {
@Schema(description = "查询参数")
private NegativeQueryParam negativeQueryParam;
@Schema(description = "统计开始时间", example = "2026年3月1日")
private String periodStart;
@Schema(description = "统计结束时间", example = "2026年3月31日")
private String periodEnd;
}

21
src/main/java/com/biutag/supervision/pojo/dto/report/unitInvestigation/ReportUnitInvestigationSectionRequestDTO.java

@ -0,0 +1,21 @@
package com.biutag.supervision.pojo.dto.report.unitInvestigation;
import com.biutag.supervision.pojo.param.NegativeQueryParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@Schema(description = "报表单位查处模块构建请求DTO")
public class ReportUnitInvestigationSectionRequestDTO {
@Schema(description = "查询参数")
private NegativeQueryParam negativeQueryParam;
@Schema(description = "统计开始时间", example = "2026年3月1日")
private String periodStart;
@Schema(description = "统计结束时间", example = "2026年3月31日")
private String periodEnd;
}

30
src/main/java/com/biutag/supervision/service/report/ReportAccountabilitySectionService.java

@ -0,0 +1,30 @@
package com.biutag.supervision.service.report;
import com.biutag.supervision.pojo.dto.report.accountability.*;
public interface ReportAccountabilitySectionService {
/**
* 构建问责追责总览
*
* @param requestDTO 请求参数
* @return 构建结果
*/
AccountabilityOverviewSection buildAccountabilityOverviewSection(ReportAccountabilitySectionRequestDTO requestDTO);
/**
* 构建单位问责明细
*
* @param requestDTO 请求参数
* @return 构建结果
*/
AccountabilityUnitSection buildAccountabilityUnitDetailSection(ReportAccountabilitySectionRequestDTO requestDTO);
/**
* 构建个人问责明细
*
* @param requestDTO 请求参数
* @return 构建结果
*/
AccountabilityPersonalSection buildAccountabilityPersonalOverviewSection(ReportAccountabilitySectionRequestDTO requestDTO);
}

17
src/main/java/com/biutag/supervision/service/report/ReportAccountabilityStatsService.java

@ -0,0 +1,17 @@
package com.biutag.supervision.service.report;
import com.biutag.supervision.pojo.dto.report.accountability.ReportAccountabilityStatsRequestDTO;
import com.biutag.supervision.pojo.dto.report.accountability.ReportAccountabilityStatsResponseDTO;
public interface ReportAccountabilityStatsService {
/**
* 计算问责统计数据
*
* @param requestDTO 请求参数
* @return 统计结果
*/
ReportAccountabilityStatsResponseDTO calculateAccountabilityStats(ReportAccountabilityStatsRequestDTO requestDTO);
}

17
src/main/java/com/biutag/supervision/service/report/ReportBaseQueryService.java

@ -0,0 +1,17 @@
package com.biutag.supervision.service.report;
import com.biutag.supervision.pojo.dto.report.base.ReportBaseQueryRequestDTO;
import com.biutag.supervision.pojo.dto.report.base.ReportBaseQueryResponseDTO;
public interface ReportBaseQueryService {
/**
* 查询报表本期环比同比基础数据
*
* @param requestDTO 查询请求
* @return 查询结果
*/
ReportBaseQueryResponseDTO queryBaseData(ReportBaseQueryRequestDTO requestDTO);
}

27
src/main/java/com/biutag/supervision/service/report/ReportBusinessLineSectionService.java

@ -0,0 +1,27 @@
package com.biutag.supervision.service.report;
import com.biutag.supervision.pojo.dto.report.businessLine.BusinessLineDetailSection;
import com.biutag.supervision.pojo.dto.report.businessLine.BusinessLineOverviewSection;
import com.biutag.supervision.pojo.dto.report.businessLine.ReportBusinessLineSectionRequestDTO;
import io.swagger.v3.oas.annotations.media.Schema;
import java.util.List;
public interface ReportBusinessLineSectionService {
/**
* 构建业务条线总览
*
* @param requestDTO 请求参数
* @return 构建结果
*/
BusinessLineOverviewSection buildBusinessLineOverviewSection(ReportBusinessLineSectionRequestDTO requestDTO);
/**
* 构建业务条线明细
*
* @param requestDTO 请求参数
* @return 构建结果
*/
List<BusinessLineDetailSection> buildBusinessLineDetailSections(ReportBusinessLineSectionRequestDTO requestDTO);
}

14
src/main/java/com/biutag/supervision/service/report/ReportClosedStatsService.java

@ -0,0 +1,14 @@
package com.biutag.supervision.service.report;
import com.biutag.supervision.pojo.dto.report.closed.ClosedStatsCalcRequestDTO;
import com.biutag.supervision.pojo.dto.report.closed.ClosedStatsCalcResponseDTO;
public interface ReportClosedStatsService {
/**
* 计算办结统计数据
*/
ClosedStatsCalcResponseDTO calculateClosedStats(ClosedStatsCalcRequestDTO requestDTO);
}

875
src/main/java/com/biutag/supervision/service/report/ReportDataServiceImpl.java

@ -1,875 +0,0 @@
package com.biutag.supervision.service.report;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.biutag.supervision.constants.enums.CheckStatusEnum;
import com.biutag.supervision.constants.enums.ProcessingStatusEnum;
import com.biutag.supervision.pojo.dto.DepartAndSubDepartDto;
import com.biutag.supervision.pojo.dto.report.OverviewSection;
import com.biutag.supervision.pojo.dto.report.ReportViewModel;
import com.biutag.supervision.pojo.dto.report.accountability.AccountabilityDepartmentSection;
import com.biutag.supervision.pojo.dto.report.accountability.AccountabilityOverviewSection;
import com.biutag.supervision.pojo.dto.report.accountability.AccountabilityPersonalSection;
import com.biutag.supervision.pojo.dto.report.accountability.AccountabilityTypeItem;
import com.biutag.supervision.pojo.dto.report.businessLine.BusinessLineDetailSection;
import com.biutag.supervision.pojo.dto.report.businessLine.BusinessLineOverviewSection;
import com.biutag.supervision.pojo.dto.report.businessLine.BusinessLinePersonRankItem;
import com.biutag.supervision.pojo.dto.report.businessLine.BusinessLineRankItem;
import com.biutag.supervision.pojo.dto.report.closed.ClosedStatsCalcRequestDTO;
import com.biutag.supervision.pojo.dto.report.closed.ClosedStatsCalcResponseDTO;
import com.biutag.supervision.pojo.dto.report.unitInvestigation.UnitInvestigationItem;
import com.biutag.supervision.pojo.dto.report.unitInvestigation.UnitInvestigationOverviewSection;
import com.biutag.supervision.pojo.entity.NegativeBlame;
import com.biutag.supervision.pojo.enums.report.TrendEnum;
import com.biutag.supervision.pojo.param.NegativeQueryParam;
import com.biutag.supervision.pojo.param.SupDepartGroupParam;
import com.biutag.supervision.pojo.param.negativeBlame.NegativeBlameQueryParam;
import com.biutag.supervision.pojo.vo.DictProblemSourceTree;
import com.biutag.supervision.pojo.vo.NegativeQueryVo;
import com.biutag.supervision.repository.negative.NegativeResourceService;
import com.biutag.supervision.repository.negativeBlame.NegativeBlameResourceService;
import com.biutag.supervision.repository.supdepart.SupDepartResourceService;
import com.biutag.supervision.service.NegativeQueryService;
import com.biutag.supervision.service.SupDictProblemSourceService;
import com.biutag.supervision.util.ChartRenderUtil;
import com.biutag.supervision.util.DateCompareRangeUtil;
import com.biutag.supervision.util.ReportTrendUtil;
import com.biutag.supervision.util.TimeUtil;
import com.deepoove.poi.data.PictureRenderData;
import com.deepoove.poi.data.PictureType;
import com.deepoove.poi.data.Pictures;
import jakarta.annotation.Resource;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@Service
@Slf4j
public class ReportDataServiceImpl implements ReportDataService {
@Resource
private NegativeResourceService negativeResourceService;
@Resource
private NegativeBlameResourceService negativeBlameResourceService;
@Resource
private NegativeQueryService negativeQueryService;
@Resource
private SupDictProblemSourceService supDictProblemSourceService;
@Resource
private SupDepartResourceService supDepartResourceService;
@Resource
private ReportClosedStatsService reportClosedStatsService;
@Resource
private ReportBaseQueryService reportBaseQueryService;
// 一定要拆分为弱领域模型 不然不能维护
@Override
public ReportViewModel buildViewModel(NegativeQueryParam request) {
String periodStart = TimeUtil.formatDate(request.getCrtTime().get(0));
String periodEnd = TimeUtil.formatDate(request.getCrtTime().get(1));
ReportViewModel vm = new ReportViewModel();
vm.setPeriodStart(periodStart);
vm.setPeriodEnd(periodEnd);
// 总体概览
vm.setOverviewSection(buildOverviewSection(request, periodStart, periodEnd));
// 各业务条线情况-总览部分
vm.setBusinessLineOverviewSection(buildBusinessLineOverviewSection(request, periodStart, periodEnd));
// 各业务条线情况-明细部分
vm.setBusinessLineDetailSections(buildBusinessLineDetailSections(request, periodStart, periodEnd));
// 单位查处情况总览
vm.setUnitInvestigationOverviewSection(buildUnitInvestigationOverviewSection(request, periodStart, periodEnd));
// 问责追责情况总览
vm.setAccountabilityOverviewSection(buildAccountabilityOverviewSection(request, periodStart, periodEnd));
// 单位问责追责情况明细
vm.setAccountabilityUnitDetailSection(buildAccountabilityUnitDetailSection(request, periodStart, periodEnd));
// 个人问责追责情况
vm.setAccountabilityPersonOverviewSection(buildAccountabilityPersonOverviewSection(request, periodStart, periodEnd));
// System.out.println(1/0);
return vm;
}
private AccountabilityPersonalSection buildAccountabilityPersonOverviewSection(NegativeQueryParam request, String periodStart, String periodEnd) {
NegativeQueryParam ztnegativeQueryParam = request.newQueryParam();
List<NegativeQueryVo> ztNegativeList = negativeQueryService.page(ztnegativeQueryParam).getRecords();
List<String> ztNegativeIds = ztNegativeList.stream().map(NegativeQueryVo::getId).toList();
AccountabilityStats accountabilityStats = buildAccountabilityStats(ztNegativeIds);
// 个人问责
Map<String, List<NegativeBlame>> leadMap = accountabilityStats.getLeadBlames().stream().collect(Collectors.groupingBy(NegativeBlame::getLeadHandleResultName));
Map<String, List<NegativeBlame>> personMap = accountabilityStats.getPersonalBlames().stream().collect(Collectors.groupingBy(NegativeBlame::getHandleResultName));
Map<String, List<NegativeBlame>> mergedMap = new HashMap<>(personMap);
leadMap.forEach((key, value) ->
mergedMap.merge(key, value, (list1, list2) -> {
List<NegativeBlame> list = new ArrayList<>(list1);
list.addAll(list2);
return list;
})
);
AccountabilityPersonalSection accountabilityPersonalSection = new AccountabilityPersonalSection();
accountabilityPersonalSection.setTotalCount(accountabilityStats.getPersonal());
// 构造 typeItems
List<AccountabilityTypeItem> typeItems = mergedMap.entrySet().stream()
.map(entry -> {
AccountabilityTypeItem item = new AccountabilityTypeItem();
item.setTypeName(entry.getKey());
item.setCount(entry.getValue().size());
return item;
})
.sorted(Comparator.comparing(AccountabilityTypeItem::getCount).reversed())
.toList();
accountabilityPersonalSection.setTypeItems(typeItems);
// 饼图
Map<String, Number> pieData = new LinkedHashMap<>();
for (AccountabilityTypeItem typeItem : typeItems) {
pieData.put(typeItem.getTypeName(), typeItem.getCount());
}
byte[] pieBytes = ChartRenderUtil.piePng("单位问责情况", pieData, 5, 900, 520);
PictureRenderData picture = Pictures.ofBytes(pieBytes, PictureType.PNG)
.size(500, 300)
.create();
accountabilityPersonalSection.setPersonPieChart(picture);
return accountabilityPersonalSection;
}
/**
* 单位问责情况
*
* @param request
* @param periodStart
* @param periodEnd
* @return
*/
private AccountabilityDepartmentSection buildAccountabilityUnitDetailSection(NegativeQueryParam request, String periodStart, String periodEnd) {
NegativeQueryParam ztnegativeQueryParam = request.newQueryParam();
List<NegativeQueryVo> ztNegativeList = negativeQueryService.page(ztnegativeQueryParam).getRecords();
List<String> ztNegativeIds = ztNegativeList.stream().map(NegativeQueryVo::getId).toList();
AccountabilityStats accountabilityStats = buildAccountabilityStats(ztNegativeIds);
// 单位问责
Map<String, List<NegativeBlame>> unitMap = accountabilityStats.getUnitBlames().stream().collect(Collectors.groupingBy(NegativeBlame::getHandleResultName));
AccountabilityDepartmentSection accountabilityDepartmentSection = new AccountabilityDepartmentSection();
accountabilityDepartmentSection.setTotalCount(accountabilityStats.getUnit());
// 构造 typeItems
List<AccountabilityTypeItem> typeItems = unitMap.entrySet().stream()
.map(entry -> {
AccountabilityTypeItem item = new AccountabilityTypeItem();
item.setTypeName(entry.getKey());
item.setCount(entry.getValue().size());
return item;
})
.sorted(Comparator.comparing(AccountabilityTypeItem::getCount).reversed())
.toList();
accountabilityDepartmentSection.setTypeItems(typeItems);
// 饼图
Map<String, Number> pieData = new LinkedHashMap<>();
for (AccountabilityTypeItem typeItem : typeItems) {
pieData.put(typeItem.getTypeName(), typeItem.getCount());
}
byte[] pieBytes = ChartRenderUtil.piePng("单位问责情况", pieData, 5, 900, 520);
PictureRenderData picture = Pictures.ofBytes(pieBytes, PictureType.PNG)
.size(500, 300)
.create();
accountabilityDepartmentSection.setDepartPieChart(picture);
return accountabilityDepartmentSection;
}
/**
* 问责追责总览
*
* @param request
* @param periodStart
* @param periodEnd
* @return
*/
private AccountabilityOverviewSection buildAccountabilityOverviewSection(NegativeQueryParam request, String periodStart, String periodEnd) {
// 总体数据
NegativeQueryParam ztnegativeQueryParam = request.newQueryParam();
List<NegativeQueryVo> ztNegativeList = negativeQueryService.page(ztnegativeQueryParam).getRecords();
List<String> tzNegativeIds = ztNegativeList.stream().map(NegativeQueryVo::getId).toList();
AccountabilityStats accountabilityStats = buildAccountabilityStats(tzNegativeIds);
AccountabilityOverviewSection accountabilityOverviewSection = new AccountabilityOverviewSection();
accountabilityOverviewSection.setPeriodStart(periodStart);
accountabilityOverviewSection.setPeriodEnd(periodEnd);
accountabilityOverviewSection.setProblemTypeCount(ztNegativeList.size());
accountabilityOverviewSection.setPersonalAccountabilityCount(accountabilityStats.personal);
accountabilityOverviewSection.setDepartmentAccountabilityCount(accountabilityStats.unit);
return accountabilityOverviewSection;
}
private UnitInvestigationOverviewSection buildUnitInvestigationOverviewSection(NegativeQueryParam request,
String periodStart,
String periodEnd) {
DateCompareRangeUtil.CompareDateRange compareDateRange = DateCompareRangeUtil.buildCompareDateRange(request.getCrtTime().get(0), request.getCrtTime().get(1));
// 总体数据
NegativeQueryParam ztnegativeQueryParam = request.newQueryParam();
List<NegativeQueryVo> ztNegativeList = negativeQueryService.page(ztnegativeQueryParam).getRecords();
SupDepartGroupParam supDepartGroupParam = new SupDepartGroupParam();
supDepartGroupParam.setParentLevel(2);
supDepartGroupParam.setChildLevel(3);
Map<String, DepartAndSubDepartDto> departAndSubDepart = supDepartResourceService.getDepartAndSubDepart(supDepartGroupParam);
List<UnitInvestigationItem> topUnits = new ArrayList<>();
for (DepartAndSubDepartDto value : departAndSubDepart.values()) {
Set<String> allDepartIds = value.getAllDepartIds();
List<NegativeQueryVo> voList = ztNegativeList.stream().filter(one -> allDepartIds.contains(one.getInvolveDepartId())).toList();
log.info(value.getParentName() + "的数量=====================" + voList.size());
if (CollectionUtil.isNotEmpty(voList)) {
UnitInvestigationItem unitInvestigationItem = new UnitInvestigationItem();
unitInvestigationItem.setUnitName(value.getParentName());
unitInvestigationItem.setIssuedProblemCount(voList.size());
List<NegativeQueryVo> csList = voList.stream()
.filter(one -> CheckStatusEnum.TRUE_SET.contains(one.getCheckStatusCode())
|| CheckStatusEnum.PART_TRUE_SET.contains(one.getCheckStatusCode()))
.toList();
unitInvestigationItem.setVerifiedProblemCount(csList.size());
unitInvestigationItem.setVerifiedRate(ReportTrendUtil.percent(csList.size(), voList.size()));
topUnits.add(unitInvestigationItem);
}
}
topUnits.sort(Comparator.comparing(UnitInvestigationItem::getVerifiedProblemCount).reversed());
topUnits = topUnits.stream().limit(3).toList();
UnitInvestigationOverviewSection section = new UnitInvestigationOverviewSection();
section.setPeriodStart(periodStart);
section.setPeriodEnd(periodEnd);
section.setTopUnits(topUnits);
// 柱状图
Map<String, Map<String, Number>> seriesData = new LinkedHashMap<>();
Map<String, Number> issuedMap = new LinkedHashMap<>();
Map<String, Number> verifiedMap = new LinkedHashMap<>();
for (UnitInvestigationItem item : topUnits) {
issuedMap.put(item.getUnitName(), item.getIssuedProblemCount());
verifiedMap.put(item.getUnitName(), item.getVerifiedProblemCount());
}
seriesData.put("下发问题数", issuedMap);
seriesData.put("查实问题数", verifiedMap);
byte[] barBytes = ChartRenderUtil.groupedBarPng("单位查处情况", seriesData, "单位", "数量", 1000, 520);
PictureRenderData picture = Pictures.ofBytes(barBytes, PictureType.PNG)
.size(500, 300)
.create();
section.setUnitBarChart(picture);
return section;
}
/**
* 业务条线的明细
*
* @param request
* @param periodStart
* @param periodEnd
* @return
*/
private List<BusinessLineDetailSection> buildBusinessLineDetailSections(NegativeQueryParam request,
String periodStart,
String periodEnd) {
DateCompareRangeUtil.CompareDateRange compareDateRange = DateCompareRangeUtil.buildCompareDateRange(request.getCrtTime().get(0), request.getCrtTime().get(1));
// 本期数据
NegativeQueryParam currentQueryParam = request.newQueryParam();
List<NegativeQueryVo> currentList = negativeQueryService.page(currentQueryParam).getRecords();
// 环比数据
NegativeQueryParam momQueryParam = request.newQueryParam();
momQueryParam.setCrtTime(compareDateRange.mom());
List<NegativeQueryVo> momList = negativeQueryService.page(momQueryParam).getRecords();
// 同比数据
NegativeQueryParam yoyQueryParam = request.newQueryParam();
yoyQueryParam.setCrtTime(compareDateRange.yoy());
List<NegativeQueryVo> yoyList = negativeQueryService.page(yoyQueryParam).getRecords();
Map<String, DictProblemSourceTree> idMap = supDictProblemSourceService.buildIdMap();
// 按“父级业务条线”分组
Map<String, List<NegativeQueryVo>> currentGroup = currentList.stream()
.collect(Collectors.groupingBy(
vo -> parentLabel2Level(vo.getProblemSourcesCode(), idMap),
LinkedHashMap::new,
Collectors.toList()
));
Map<String, List<NegativeQueryVo>> momGroup = momList.stream()
.collect(Collectors.groupingBy(
vo -> parentLabel2Level(vo.getProblemSourcesCode(), idMap),
LinkedHashMap::new,
Collectors.toList()
));
Map<String, List<NegativeQueryVo>> yoyGroup = yoyList.stream()
.collect(Collectors.groupingBy(
vo -> parentLabel2Level(vo.getProblemSourcesCode(), idMap),
LinkedHashMap::new,
Collectors.toList()
));
// 按本期数量倒序
List<Map.Entry<String, List<NegativeQueryVo>>> sortedEntries = currentGroup.entrySet().stream()
.sorted((a, b) -> Integer.compare(b.getValue().size(), a.getValue().size()))
.toList();
List<BusinessLineDetailSection> result = new ArrayList<>();
int index = 1;
for (Map.Entry<String, List<NegativeQueryVo>> entry : sortedEntries) {
String fatherBusinessLineName = entry.getKey();
List<NegativeQueryVo> lineList = entry.getValue();
if (CollUtil.isEmpty(lineList)) {
continue;
}
BusinessLineDetailSection section = new BusinessLineDetailSection();
section.setOrderNo(toChineseOrderNo(index++));
section.setBusinessFatherLineName(fatherBusinessLineName);
// 这里明细项名称按父级条线展示;若你模板里想展示别的,可自行替换
section.setBusinessLineName(fatherBusinessLineName);
// 1. 下发总数
int totalIssued = lineList.size();
section.setTotalIssued(totalIssued);
// 2. 已办结数
List<NegativeQueryVo> closedList = lineList.stream()
.filter(one -> ProcessingStatusEnum.completed.name().equals(one.getProcessingStatus()))
.toList();
section.setClosedCount(closedList.size());
// 3. 查实总数 / 查实率
ClosedStatsCalcRequestDTO closedStatsCalcRequestDTO = new ClosedStatsCalcRequestDTO();
closedStatsCalcRequestDTO.setNegativeQueryVoList(lineList);
ClosedStatsCalcResponseDTO closedStats = reportClosedStatsService.calcClosedStats(closedStatsCalcRequestDTO);
int verifiedTotal = Optional.ofNullable(closedStats.getClosedCount()).orElse(0);
section.setVerifiedTotal(verifiedTotal);
section.setVerifiedRate(Optional.ofNullable(closedStats.getVerifiedRate()).orElse(BigDecimal.ZERO));
// 4. 问责统计:按本条线内“属实 + 基本属实”的问题统计
List<String> verifiedNegativeIds = closedList.stream()
.filter(one -> CheckStatusEnum.TRUE_SET.contains(one.getCheckStatusCode())
|| CheckStatusEnum.PART_TRUE_SET.contains(one.getCheckStatusCode()))
.map(NegativeQueryVo::getId)
.filter(StrUtil::isNotBlank)
.distinct()
.toList();
AccountabilityStats accountabilityStats = buildAccountabilityStats(verifiedNegativeIds);
section.setAccountabilityTotal(accountabilityStats.getTotal());
section.setAccountabilityRate(ReportTrendUtil.calcRate(accountabilityStats.getTotal(), verifiedTotal));
section.setPersonalAccountabilityCount(accountabilityStats.getPersonal());
section.setDepartmentAccountabilityCount(accountabilityStats.getUnit());
// 5. 问题类型 TOP3(本条线下的二级/叶子问题类型)
List<BusinessLineRankItem> topProblemTypes = lineList.stream()
.collect(Collectors.groupingBy(
vo -> leafProblemLabel(vo.getProblemSourcesCode(), idMap),
Collectors.counting()
))
.entrySet().stream()
.sorted((a, b) -> Long.compare(b.getValue(), a.getValue()))
.limit(3)
.map(e -> buildRankItem(
e.getKey(),
e.getValue().intValue(),
safePercentString(e.getValue().intValue(), totalIssued)
))
.toList();
section.setTopProblemTypes(topProblemTypes);
// 6. 重点单位 TOP3
List<BusinessLineRankItem> topUnits = lineList.stream()
.filter(one -> StrUtil.isNotBlank(one.getInvolveDepartName()))
.collect(Collectors.groupingBy(NegativeQueryVo::getInvolveDepartName, Collectors.counting()))
.entrySet().stream()
.sorted((a, b) -> Long.compare(b.getValue(), a.getValue()))
.limit(3)
.map(e -> buildRankItem(
e.getKey(),
e.getValue().intValue(),
safePercentString(e.getValue().intValue(), totalIssued)
))
.toList();
section.setTopUnits(topUnits);
// 7. 重点人员 TOP3
List<BusinessLinePersonRankItem> topPersons = buildTopPersons(lineList, totalIssued);
section.setTopPersons(topPersons);
// 8. 环比 / 同比(按该父级条线的问题下发总数比较)
int currentCount = totalIssued;
int momCount = momGroup.getOrDefault(fatherBusinessLineName, Collections.emptyList()).size();
int yoyCount = yoyGroup.getOrDefault(fatherBusinessLineName, Collections.emptyList()).size();
section.setMomRate(ReportTrendUtil.calcRate(currentCount, momCount));
section.setMomTrend(ReportTrendUtil.calcTrend(currentCount, momCount));
section.setYoyRate(ReportTrendUtil.calcRate(currentCount, yoyCount));
section.setYoyTrend(ReportTrendUtil.calcTrend(currentCount, yoyCount));
result.add(section);
}
return result;
}
private BusinessLineRankItem buildRankItem(String name, Integer count, String rate) {
BusinessLineRankItem item = new BusinessLineRankItem();
item.setName(name);
item.setCount(count);
item.setRate(new BigDecimal(rate));
return item;
}
private BusinessLinePersonRankItem buildPersonRankItem(String personName, String unitName, String identityName, Integer count, String rate) {
BusinessLinePersonRankItem item = new BusinessLinePersonRankItem();
item.setPersonName(personName);
item.setUnitName(unitName);
item.setIdentityName(identityName);
item.setCount(count);
item.setRate(new BigDecimal(rate));
return item;
}
/**
* 业务条线的总览
*
* @param request
* @param periodStart
* @param periodEnd
* @return
*/
private BusinessLineOverviewSection buildBusinessLineOverviewSection(NegativeQueryParam request, String periodStart, String periodEnd) {
// 总体数据
NegativeQueryParam ztNegativeQueryParam = request.newQueryParam();
List<NegativeQueryVo> ztNegativeList = negativeQueryService.page(ztNegativeQueryParam).getRecords();
Map<String, DictProblemSourceTree> idMap = supDictProblemSourceService.buildIdMap();
// 计数
Map<String, Long> aggByParentLabel = ztNegativeList.stream()
.map(NegativeQueryVo::getProblemSourcesCode) // 收集二级id
.map(childId -> parentLabel2Level(childId, idMap)) // 解析未一级label
.collect(Collectors.groupingBy(s -> s, Collectors.counting()));
// 排序
List<Map.Entry<String, Long>> sorted = aggByParentLabel.entrySet().stream()
.sorted((a, b) -> Long.compare(b.getValue(), a.getValue()))
.toList();
// 3)拼接总览文本(全部列出)
StringBuilder businessLineOverviewText = new StringBuilder();
for (int i = 0; i < sorted.size(); i++) {
String label = sorted.get(i).getKey();
int cnt = Math.toIntExact(sorted.get(i).getValue());
businessLineOverviewText.append(i + 1)
.append("、")
.append(label)
.append(",")
.append(cnt)
.append("条,占比")
.append(ReportTrendUtil.percent(cnt, ztNegativeList.size()))
.append("%;");
}
BusinessLineOverviewSection businessLineOverviewSection = new BusinessLineOverviewSection();
businessLineOverviewSection.setPeriodStart(periodStart);
businessLineOverviewSection.setPeriodEnd(periodEnd);
businessLineOverviewSection.setBusinessLineTotal(ztNegativeList.size());
businessLineOverviewSection.setBusinessLineOverviewText(businessLineOverviewText.toString());
Map<String, Number> pieData = new LinkedHashMap<>();
for (Map.Entry<String, Long> entry : sorted) {
pieData.put(entry.getKey(), entry.getValue());
}
byte[] pieBytes = ChartRenderUtil.piePng("占比统计", pieData, 5, 900, 520);
PictureRenderData picture = Pictures.ofBytes(pieBytes, PictureType.PNG)
.size(500, 300)
.create();
businessLineOverviewSection.setProblemPieChart(picture);
return businessLineOverviewSection;
}
/**
* 总体情况计算
*
* @param request
* @param periodStart
* @param periodEnd
* @return
*/
private OverviewSection buildOverviewSection(NegativeQueryParam request, String periodStart, String periodEnd) {
DateCompareRangeUtil.CompareDateRange compareDateRange = DateCompareRangeUtil.buildCompareDateRange(request.getCrtTime().get(0), request.getCrtTime().get(1));
// 总体数据
NegativeQueryParam ztnegativeQueryParam = request.newQueryParam();
List<NegativeQueryVo> ztNegativeList = negativeQueryService.page(ztnegativeQueryParam).getRecords();
// 环比数据
NegativeQueryParam hbNegativeQueryParam = request.newQueryParam();
hbNegativeQueryParam.setCrtTime(compareDateRange.mom());
List<NegativeQueryVo> hbNegativList = negativeQueryService.page(hbNegativeQueryParam).getRecords();
// 同比数据
NegativeQueryParam tbNegativeQueryParam = request.newQueryParam();
tbNegativeQueryParam.setCrtTime(compareDateRange.yoy());
List<NegativeQueryVo> tbNegativList = negativeQueryService.page(tbNegativeQueryParam).getRecords();
// 市局下发数据
List<NegativeQueryVo> sjxfNegativeList = ztNegativeList.stream().filter(one -> Objects.equals(0, one.getCrtDepartLevel())).toList();
// 分县市局下发数据
List<NegativeQueryVo> fxsjxfNegativeList = ztNegativeList.stream().filter(one -> Objects.equals(2, one.getCrtDepartLevel())).toList();
// 办结数据
List<NegativeQueryVo> bjNegativeList = ztNegativeList.stream().filter(one -> ProcessingStatusEnum.completed.name().equals(one.getProcessingStatus())).toList();
// 办结中属实数据
List<NegativeQueryVo> bjssNegativeList = bjNegativeList.stream().filter(one -> CheckStatusEnum.TRUE_SET.contains(one.getCheckStatusCode())).toList();
// 办结中基本属实数据
List<NegativeQueryVo> bjjbssNegativeList = bjNegativeList.stream().filter(one -> CheckStatusEnum.PART_TRUE_SET.contains(one.getCheckStatusCode())).toList();
// 办结中不属实数据
List<NegativeQueryVo> bjbssNegativeList = bjNegativeList.stream().filter(one -> CheckStatusEnum.FALSE_SET.contains(one.getCheckStatusCode())).toList();
// 属实 基本属实中的问责数据
List<String> ssNegativeIds = Stream.concat(bjssNegativeList.stream(), bjjbssNegativeList.stream())
.map(NegativeQueryVo::getId).filter(StrUtil::isNotBlank).distinct().toList();
AccountabilityStats currentAccountabilityStats = buildAccountabilityStats(ssNegativeIds);
// ==================== 环比问责数据 ====================
// 环比办结数据
List<NegativeQueryVo> hbBjNegativeList = hbNegativList.stream()
.filter(one -> ProcessingStatusEnum.completed.name().equals(one.getProcessingStatus()))
.toList();
// 环比办结中属实数据
List<NegativeQueryVo> hbBjssNegativeList = hbBjNegativeList.stream()
.filter(one -> CheckStatusEnum.TRUE_SET.contains(one.getCheckStatusCode()))
.toList();
// 环比办结中基本属实数据
List<NegativeQueryVo> hbBjjbssNegativeList = hbBjNegativeList.stream()
.filter(one -> CheckStatusEnum.PART_TRUE_SET.contains(one.getCheckStatusCode()))
.toList();
// 环比属实 + 基本属实中的问责数据
List<String> hbNegativeIds = Stream.concat(hbBjssNegativeList.stream(), hbBjjbssNegativeList.stream())
.map(NegativeQueryVo::getId)
.filter(StrUtil::isNotBlank)
.distinct()
.toList();
AccountabilityStats momAccountabilityStats = buildAccountabilityStats(hbNegativeIds);
int hbAccountabilityTotal = momAccountabilityStats.getTotal();
// ==================== 同比问责数据 ====================
// 同比办结数据
List<NegativeQueryVo> tbBjNegativeList = tbNegativList.stream()
.filter(one -> ProcessingStatusEnum.completed.name().equals(one.getProcessingStatus()))
.toList();
// 同比办结中属实数据
List<NegativeQueryVo> tbBjssNegativeList = tbBjNegativeList.stream()
.filter(one -> CheckStatusEnum.TRUE_SET.contains(one.getCheckStatusCode()))
.toList();
// 同比办结中基本属实数据
List<NegativeQueryVo> tbBjjbssNegativeList = tbBjNegativeList.stream()
.filter(one -> CheckStatusEnum.PART_TRUE_SET.contains(one.getCheckStatusCode()))
.toList();
// 同比属实 + 基本属实中的问责数据
List<String> tbNegativeIds = Stream.concat(tbBjssNegativeList.stream(), tbBjjbssNegativeList.stream())
.map(NegativeQueryVo::getId)
.filter(StrUtil::isNotBlank)
.distinct()
.toList();
AccountabilityStats yoyAccountabilityStats = buildAccountabilityStats(tbNegativeIds);
int tbAccountabilityTotal = yoyAccountabilityStats.getTotal();
// 总问责数
int accountabilityTotal = currentAccountabilityStats.getTotal();
// 个人问责数
int personalAccountability = currentAccountabilityStats.getPersonal();
// 单位问责数
int unitAccountability = currentAccountabilityStats.getUnit();
// 第一段
int current = ztNegativeList.size();
int mom = hbNegativList.size();
int yoy = tbNegativList.size();
OverviewSection overviewSection = new OverviewSection();
// 时间
overviewSection.setPeriodStart(periodStart);
overviewSection.setPeriodEnd(periodEnd);
// 总条数
overviewSection.setTotalIssued(ztNegativeList.size());
// 总条数环比
overviewSection.setMomRate(ReportTrendUtil.calcRate(current, mom));
overviewSection.setMomTrend(ReportTrendUtil.calcTrend(current, mom));
// 总条数同比
overviewSection.setYoyRate(ReportTrendUtil.calcRate(current, yoy));
overviewSection.setYoyTrend(ReportTrendUtil.calcTrend(current, yoy));
// 市局下发
overviewSection.setCityIssued(sjxfNegativeList.size());
overviewSection.setCityRate(ReportTrendUtil.percent(sjxfNegativeList.size(), ztNegativeList.size()));
// 分县市局下发
overviewSection.setCountyIssued(fxsjxfNegativeList.size());
overviewSection.setCountyRate(ReportTrendUtil.percent(fxsjxfNegativeList.size(), ztNegativeList.size()));
// 办结总数据
overviewSection.setClosedCount(bjNegativeList.size());
overviewSection.setVerifiedCount(bjssNegativeList.size());
overviewSection.setBasicallyVerifiedCount(bjjbssNegativeList.size());
overviewSection.setUnverifiedCount(bjbssNegativeList.size());
// 办结查实率
// 1) 本期/环比/同比 查实率(都是 %,BigDecimal)
BigDecimal curVerifiedRate = calcVerifiedRate(ztNegativeList);
BigDecimal momVerifiedRate = calcVerifiedRate(hbNegativList);
BigDecimal yoyVerifiedRate = calcVerifiedRate(tbNegativList);
overviewSection.setVerifiedRate(curVerifiedRate);
// 3) 查实率环比(注意:率对率)
overviewSection.setVerifiedMomRate(ReportTrendUtil.calcRate(curVerifiedRate, momVerifiedRate));
overviewSection.setVerifiedMomTrend(ReportTrendUtil.calcTrend(curVerifiedRate, momVerifiedRate));
// 4) 查实率同比(注意:率对率)
overviewSection.setVerifiedYoyRate(ReportTrendUtil.calcRate(curVerifiedRate, yoyVerifiedRate));
overviewSection.setVerifiedYoyTrend(ReportTrendUtil.calcTrend(curVerifiedRate, yoyVerifiedRate));
// 5) 问责数据
overviewSection.setAccountabilityTotal(accountabilityTotal);
overviewSection.setPersonalAccountability(personalAccountability);
overviewSection.setUnitAccountability(unitAccountability);
overviewSection.setAccountabilityMomRate(ReportTrendUtil.calcRate(accountabilityTotal, hbAccountabilityTotal));
overviewSection.setAccountabilityMomTrend(ReportTrendUtil.calcTrend(accountabilityTotal, hbAccountabilityTotal));
overviewSection.setAccountabilityYoyRate(ReportTrendUtil.calcRate(accountabilityTotal, tbAccountabilityTotal));
overviewSection.setAccountabilityYoyTrend(ReportTrendUtil.calcTrend(accountabilityTotal, tbAccountabilityTotal));
// 集中问题
Map.Entry<String, Long> problemEntry = ReportTrendUtil.topBy(NegativeQueryVo::getProblemSources, bjssNegativeList, bjjbssNegativeList);
String problemSource = problemEntry != null ? problemEntry.getKey() : null;
Long problemSourceCount = problemEntry != null ? problemEntry.getValue() : 0;
overviewSection.setTopProblemProject(problemSource);
// 重点关注单位
Map.Entry<String, Long> departEntry = ReportTrendUtil.topBy(NegativeQueryVo::getInvolveDepartName, bjssNegativeList, bjjbssNegativeList);
String departName = departEntry != null ? departEntry.getKey() : null;
Long departNameCount = departEntry != null ? departEntry.getValue() : 0;
overviewSection.setTopProblemUnit(departName);
return overviewSection;
}
// 查实率
private BigDecimal calcVerifiedRate(List<NegativeQueryVo> list) {
// 办结
List<NegativeQueryVo> closed = list.stream().filter(one -> ProcessingStatusEnum.completed.name().equals(one.getProcessingStatus())).toList();
int closedCount = closed.size();
if (closedCount == 0) {
return BigDecimal.ZERO;
}
// 属实
int verified = (int) closed.stream()
.filter(n -> CheckStatusEnum.TRUE_SET.contains(n.getCheckStatusCode()))
.count();
// 基本属实
int partVerified = (int) closed.stream()
.filter(n -> CheckStatusEnum.PART_TRUE_SET.contains(n.getCheckStatusCode()))
.count();
// 查实率 = (属实 + 基本属实) / 办结
return ReportTrendUtil.percent(verified + partVerified, closedCount);
}
private String parentLabel2Level(String childId, Map<String, DictProblemSourceTree> idMap) {
if (StrUtil.isBlank(childId)) {
return "未归类";
}
DictProblemSourceTree child = idMap.get(childId);
if (child == null) {
return "未归类";
}
DictProblemSourceTree parent = idMap.get(child.getParentId());
// 若 child 没父,说明 child 自己就是一级(兜底)
return parent != null ? StrUtil.blankToDefault(parent.getLabel(), "未归类")
: StrUtil.blankToDefault(child.getLabel(), "未归类");
}
private AccountabilityStats buildAccountabilityStats(List<String> negativeIds) {
AccountabilityStats stats = new AccountabilityStats();
if (CollUtil.isEmpty(negativeIds)) {
return stats;
}
NegativeBlameQueryParam queryParam = new NegativeBlameQueryParam();
queryParam.setNegativeIds(negativeIds);
List<NegativeBlame> negativeBlames = negativeBlameResourceService.query(queryParam);
// 个人问责
List<NegativeBlame> personalBlames = negativeBlames.stream()
.filter(one -> "personal".equals(one.getType()))
.filter(one -> StrUtil.isNotBlank(one.getHandleResultName()))
.filter(one -> !"不予追责".equals(one.getHandleResultName()))
.toList();
// 个人问责中的领导问责
Set<String> seenLead = new HashSet<>();
List<NegativeBlame> leadBlames = personalBlames.stream()
.filter(one -> StrUtil.isNotBlank(one.getLeadHandleResultName()))
.filter(one -> !"不予追责".equals(one.getLeadHandleResultName()))
.filter(one -> seenLead.add(one.getNegativeId() + "_" + one.getLeadHandleResultName() + "_" + one.getLeadName()))
.toList();
// 单位问责
List<NegativeBlame> unitBlames = negativeBlames.stream()
.filter(one -> "department".equals(one.getType()))
.filter(one -> StrUtil.isNotBlank(one.getHandleResultName()))
.filter(one -> !"不予追责".equals(one.getHandleResultName()))
.toList();
int personalCount = personalBlames.size() + leadBlames.size();
int unitCount = unitBlames.size();
stats.setPersonal(personalCount);
stats.setUnit(unitCount);
stats.setTotal(personalCount + unitCount);
stats.setPersonalBlames(personalBlames);
stats.setLeadBlames(leadBlames);
stats.setUnitBlames(unitBlames);
return stats;
}
@Data
private static class AccountabilityStats {
/**
* 问责总数 = 个人问责 + 领导问责 + 单位问责
*/
private int total;
/**
* 个人问责数 = 个人问责 + 领导问责
*/
private int personal;
/**
* 单位问责数
*/
private int unit;
/**
* 个人问责
*/
private List<NegativeBlame> personalBlames = new ArrayList<>();
/**
* 领导问责
*/
private List<NegativeBlame> leadBlames = new ArrayList<>();
/**
* 单位问责
*/
private List<NegativeBlame> unitBlames = new ArrayList<>();
}
private String leafProblemLabel(String problemSourceCode, Map<String, DictProblemSourceTree> idMap) {
if (StrUtil.isBlank(problemSourceCode)) {
return "未归类";
}
DictProblemSourceTree node = idMap.get(problemSourceCode);
if (node == null || StrUtil.isBlank(node.getLabel())) {
return "未归类";
}
return node.getLabel();
}
private String safePercentString(int numerator, int denominator) {
return ReportTrendUtil.percent(numerator, denominator).toPlainString();
}
private String toChineseOrderNo(int index) {
String[] arr = {"一", "二", "三", "四", "五", "六", "七", "八", "九", "十"};
if (index >= 1 && index <= arr.length) {
return arr[index - 1];
}
return String.valueOf(index);
}
private List<BusinessLinePersonRankItem> buildTopPersons(List<NegativeQueryVo> lineList, int totalIssued) {
Map<String, PersonAgg> personAggMap = new LinkedHashMap<>();
for (NegativeQueryVo vo : lineList) {
String personName = readStringByGetter(vo,
"getInvolvePersonName",
"getPersonName",
"getPoliceName",
"getHandlePersonName",
"getName");
if (StrUtil.isBlank(personName)) {
continue;
}
String unitName = firstNotBlank(
readStringByGetter(vo, "getInvolveDepartName"),
readStringByGetter(vo, "getUnitName"),
readStringByGetter(vo, "getDepartName"),
"未知单位"
);
String identityName = firstNotBlank(
readStringByGetter(vo, "getIdentityName"),
readStringByGetter(vo, "getInvolveIdentityName"),
readStringByGetter(vo, "getPoliceTypeName"),
readStringByGetter(vo, "getUserTypeName"),
"未知身份"
);
String key = personName + "||" + unitName + "||" + identityName;
PersonAgg agg = personAggMap.computeIfAbsent(key, k -> {
PersonAgg x = new PersonAgg();
x.setPersonName(personName);
x.setUnitName(unitName);
x.setIdentityName(identityName);
x.setCount(0);
return x;
});
agg.setCount(agg.getCount() + 1);
}
return personAggMap.values().stream()
.sorted(Comparator.comparing(PersonAgg::getCount).reversed())
.limit(3)
.map(one -> buildPersonRankItem(
one.getPersonName(),
one.getUnitName(),
one.getIdentityName(),
one.getCount(),
safePercentString(one.getCount(), totalIssued)
))
.toList();
}
private String readStringByGetter(Object target, String... getterNames) {
if (target == null || getterNames == null) {
return null;
}
for (String getterName : getterNames) {
try {
java.lang.reflect.Method method = target.getClass().getMethod(getterName);
Object value = method.invoke(target);
if (value instanceof String str && StrUtil.isNotBlank(str)) {
return str;
}
} catch (Exception ignored) {
}
}
return null;
}
private String firstNotBlank(String... values) {
if (values == null) {
return null;
}
for (String value : values) {
if (StrUtil.isNotBlank(value)) {
return value;
}
}
return null;
}
@Data
private static class PersonAgg {
private String personName;
private String unitName;
private String identityName;
private int count;
}
}

17
src/main/java/com/biutag/supervision/service/report/ReportOverviewSectionService.java

@ -0,0 +1,17 @@
package com.biutag.supervision.service.report;
import com.biutag.supervision.pojo.dto.report.OverviewSection;
import com.biutag.supervision.pojo.dto.report.overview.ReportOverviewSectionRequestDTO;
import io.swagger.v3.oas.annotations.media.Schema;
public interface ReportOverviewSectionService {
/**
* 构建总体概览数据
*
* @param requestDTO 请求参数
* @return 构建结果
*/
OverviewSection buildOverviewSection(ReportOverviewSectionRequestDTO requestDTO);
}

16
src/main/java/com/biutag/supervision/service/report/ReportUnitInvestigationSectionService.java

@ -0,0 +1,16 @@
package com.biutag.supervision.service.report;
import com.biutag.supervision.pojo.dto.report.unitInvestigation.ReportUnitInvestigationSectionRequestDTO;
import com.biutag.supervision.pojo.dto.report.unitInvestigation.UnitInvestigationOverviewSection;
import io.swagger.v3.oas.annotations.media.Schema;
public interface ReportUnitInvestigationSectionService {
/**
* 构建单位查处情况总览
*
* @param requestDTO 请求参数
* @return 构建结果
*/
UnitInvestigationOverviewSection buildUnitInvestigationOverviewSection(ReportUnitInvestigationSectionRequestDTO requestDTO);
}

175
src/main/java/com/biutag/supervision/service/report/impl/ReportAccountabilitySectionServiceImpl.java

@ -0,0 +1,175 @@
package com.biutag.supervision.service.report.impl;
import com.biutag.supervision.pojo.dto.report.accountability.*;
import com.biutag.supervision.pojo.entity.NegativeBlame;
import com.biutag.supervision.pojo.param.NegativeQueryParam;
import com.biutag.supervision.pojo.vo.NegativeQueryVo;
import com.biutag.supervision.service.NegativeQueryService;
import com.biutag.supervision.service.report.ReportAccountabilitySectionService;
import com.biutag.supervision.service.report.ReportAccountabilityStatsService;
import com.biutag.supervision.util.ChartRenderUtil;
import com.deepoove.poi.data.PictureRenderData;
import com.deepoove.poi.data.PictureType;
import com.deepoove.poi.data.Pictures;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import java.util.*;
@Service
public class ReportAccountabilitySectionServiceImpl implements ReportAccountabilitySectionService {
@Resource
private NegativeQueryService negativeQueryService;
@Resource
private ReportAccountabilityStatsService reportAccountabilityStatsService;
@Override
public AccountabilityOverviewSection buildAccountabilityOverviewSection(ReportAccountabilitySectionRequestDTO requestDTO) {
if (requestDTO == null || requestDTO.getNegativeQueryParam() == null) {
return new AccountabilityOverviewSection();
}
NegativeQueryParam request = requestDTO.getNegativeQueryParam();
String periodStart = requestDTO.getPeriodStart();
String periodEnd = requestDTO.getPeriodEnd();
NegativeQueryParam currentQueryParam = request.newQueryParam();
List<NegativeQueryVo> currentList = negativeQueryService.page(currentQueryParam).getRecords();
List<String> negativeIds = currentList.stream()
.map(NegativeQueryVo::getId)
.filter(Objects::nonNull)
.toList();
ReportAccountabilityStatsRequestDTO statsRequestDTO = new ReportAccountabilityStatsRequestDTO();
statsRequestDTO.setNegativeIds(negativeIds);
ReportAccountabilityStatsResponseDTO statsResponseDTO =
reportAccountabilityStatsService.calculateAccountabilityStats(statsRequestDTO);
AccountabilityOverviewSection section = new AccountabilityOverviewSection();
section.setPeriodStart(periodStart);
section.setPeriodEnd(periodEnd);
section.setProblemTypeCount(currentList.size());
section.setPersonalAccountabilityCount(statsResponseDTO.getPersonal());
section.setDepartmentAccountabilityCount(statsResponseDTO.getUnit());
return section;
}
@Override
public AccountabilityUnitSection buildAccountabilityUnitDetailSection(ReportAccountabilitySectionRequestDTO requestDTO) {
if (requestDTO == null || requestDTO.getNegativeQueryParam() == null) {
return new AccountabilityUnitSection();
}
NegativeQueryParam request = requestDTO.getNegativeQueryParam();
NegativeQueryParam currentQueryParam = request.newQueryParam();
List<NegativeQueryVo> currentList = negativeQueryService.page(currentQueryParam).getRecords();
List<String> negativeIds = currentList.stream()
.map(NegativeQueryVo::getId)
.filter(Objects::nonNull)
.toList();
ReportAccountabilityStatsRequestDTO statsRequestDTO = new ReportAccountabilityStatsRequestDTO();
statsRequestDTO.setNegativeIds(negativeIds);
ReportAccountabilityStatsResponseDTO statsResponseDTO =
reportAccountabilityStatsService.calculateAccountabilityStats(statsRequestDTO);
Map<String, List<NegativeBlame>> unitMap = statsResponseDTO.getUnitBlames().stream()
.collect(java.util.stream.Collectors.groupingBy(NegativeBlame::getHandleResultName));
AccountabilityUnitSection section = new AccountabilityUnitSection();
section.setTotalCount(statsResponseDTO.getUnit());
List<AccountabilityTypeItem> typeItems = unitMap.entrySet().stream()
.map(entry -> {
AccountabilityTypeItem item = new AccountabilityTypeItem();
item.setTypeName(entry.getKey());
item.setCount(entry.getValue().size());
return item;
})
.sorted(Comparator.comparing(AccountabilityTypeItem::getCount).reversed())
.toList();
section.setTypeItems(typeItems);
Map<String, Number> pieData = new LinkedHashMap<>();
for (AccountabilityTypeItem typeItem : typeItems) {
pieData.put(typeItem.getTypeName(), typeItem.getCount());
}
byte[] pieBytes = ChartRenderUtil.piePng("单位问责情况", pieData, 5, 900, 520);
PictureRenderData picture = Pictures.ofBytes(pieBytes, PictureType.PNG)
.size(500, 300)
.create();
section.setDepartPieChart(picture);
return section;
}
@Override
public AccountabilityPersonalSection buildAccountabilityPersonalOverviewSection(ReportAccountabilitySectionRequestDTO requestDTO) {
if (requestDTO == null || requestDTO.getNegativeQueryParam() == null) {
return new AccountabilityPersonalSection();
}
NegativeQueryParam request = requestDTO.getNegativeQueryParam();
NegativeQueryParam currentQueryParam = request.newQueryParam();
List<NegativeQueryVo> currentList = negativeQueryService.page(currentQueryParam).getRecords();
List<String> negativeIds = currentList.stream()
.map(NegativeQueryVo::getId)
.filter(Objects::nonNull)
.toList();
ReportAccountabilityStatsRequestDTO statsRequestDTO = new ReportAccountabilityStatsRequestDTO();
statsRequestDTO.setNegativeIds(negativeIds);
ReportAccountabilityStatsResponseDTO statsResponseDTO =
reportAccountabilityStatsService.calculateAccountabilityStats(statsRequestDTO);
Map<String, List<NegativeBlame>> leadMap = statsResponseDTO.getLeadBlames().stream()
.collect(java.util.stream.Collectors.groupingBy(NegativeBlame::getLeadHandleResultName));
Map<String, List<NegativeBlame>> personMap = statsResponseDTO.getPersonalBlames().stream()
.collect(java.util.stream.Collectors.groupingBy(NegativeBlame::getHandleResultName));
Map<String, List<NegativeBlame>> mergedMap = new HashMap<>(personMap);
leadMap.forEach((key, value) ->
mergedMap.merge(key, value, (list1, list2) -> {
List<NegativeBlame> list = new ArrayList<>(list1);
list.addAll(list2);
return list;
})
);
AccountabilityPersonalSection section = new AccountabilityPersonalSection();
section.setTotalCount(statsResponseDTO.getPersonal());
List<AccountabilityTypeItem> typeItems = mergedMap.entrySet().stream()
.map(entry -> {
AccountabilityTypeItem item = new AccountabilityTypeItem();
item.setTypeName(entry.getKey());
item.setCount(entry.getValue().size());
return item;
})
.sorted(Comparator.comparing(AccountabilityTypeItem::getCount).reversed())
.toList();
section.setTypeItems(typeItems);
Map<String, Number> pieData = new LinkedHashMap<>();
for (AccountabilityTypeItem typeItem : typeItems) {
pieData.put(typeItem.getTypeName(), typeItem.getCount());
}
byte[] pieBytes = ChartRenderUtil.piePng("个人问责情况", pieData, 5, 900, 520);
PictureRenderData picture = Pictures.ofBytes(pieBytes, PictureType.PNG)
.size(500, 300)
.create();
section.setPersonPieChart(picture);
return section;
}
}

76
src/main/java/com/biutag/supervision/service/report/impl/ReportAccountabilityStatsServiceImpl.java

@ -0,0 +1,76 @@
package com.biutag.supervision.service.report.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.biutag.supervision.pojo.dto.report.accountability.ReportAccountabilityStatsRequestDTO;
import com.biutag.supervision.pojo.dto.report.accountability.ReportAccountabilityStatsResponseDTO;
import com.biutag.supervision.pojo.entity.NegativeBlame;
import com.biutag.supervision.pojo.param.negativeBlame.NegativeBlameQueryParam;
import com.biutag.supervision.repository.negativeBlame.NegativeBlameResourceService;
import com.biutag.supervision.service.report.ReportAccountabilityStatsService;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@Service
@Schema(description = "报表问责统计服务")
public class ReportAccountabilityStatsServiceImpl implements ReportAccountabilityStatsService {
@Resource
private NegativeBlameResourceService negativeBlameResourceService;
@Override
public ReportAccountabilityStatsResponseDTO calculateAccountabilityStats(ReportAccountabilityStatsRequestDTO requestDTO) {
ReportAccountabilityStatsResponseDTO responseDTO = new ReportAccountabilityStatsResponseDTO();
if (requestDTO == null || CollUtil.isEmpty(requestDTO.getNegativeIds())) {
return responseDTO;
}
NegativeBlameQueryParam queryParam = new NegativeBlameQueryParam();
queryParam.setNegativeIds(requestDTO.getNegativeIds());
List<NegativeBlame> negativeBlames = negativeBlameResourceService.query(queryParam);
// 个人问责
List<NegativeBlame> personalBlames = negativeBlames.stream()
.filter(one -> "personal".equals(one.getType()))
.filter(one -> StrUtil.isNotBlank(one.getHandleResultName()))
.filter(one -> !"不予追责".equals(one.getHandleResultName()))
.toList();
// 个人问责中的领导问责
Set<String> seenLead = new HashSet<>();
List<NegativeBlame> leadBlames = personalBlames.stream()
.filter(one -> StrUtil.isNotBlank(one.getLeadHandleResultName()))
.filter(one -> !"不予追责".equals(one.getLeadHandleResultName()))
.filter(one -> seenLead.add(
one.getNegativeId() + "_" + one.getLeadHandleResultName() + "_" + one.getLeadName()
))
.toList();
// 单位问责
List<NegativeBlame> unitBlames = negativeBlames.stream()
.filter(one -> "department".equals(one.getType()))
.filter(one -> StrUtil.isNotBlank(one.getHandleResultName()))
.filter(one -> !"不予追责".equals(one.getHandleResultName()))
.toList();
int personalCount = personalBlames.size() + leadBlames.size();
int unitCount = unitBlames.size();
responseDTO.setPersonal(personalCount);
responseDTO.setUnit(unitCount);
responseDTO.setTotal(personalCount + unitCount);
responseDTO.setPersonalBlames(personalBlames);
responseDTO.setLeadBlames(leadBlames);
responseDTO.setUnitBlames(unitBlames);
return responseDTO;
}
}

53
src/main/java/com/biutag/supervision/service/report/impl/ReportBaseQueryServiceImpl.java

@ -0,0 +1,53 @@
package com.biutag.supervision.service.report.impl;
import cn.hutool.core.collection.CollUtil;
import com.biutag.supervision.pojo.dto.report.base.ReportBaseQueryRequestDTO;
import com.biutag.supervision.pojo.dto.report.base.ReportBaseQueryResponseDTO;
import com.biutag.supervision.pojo.param.NegativeQueryParam;
import com.biutag.supervision.pojo.vo.NegativeQueryVo;
import com.biutag.supervision.service.NegativeQueryService;
import com.biutag.supervision.service.report.ReportBaseQueryService;
import com.biutag.supervision.util.DateCompareRangeUtil;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service
@Schema(description = "报表基础查询服务")
public class ReportBaseQueryServiceImpl implements ReportBaseQueryService {
@Resource
private NegativeQueryService negativeQueryService;
@Override
public ReportBaseQueryResponseDTO queryBaseData(ReportBaseQueryRequestDTO requestDTO) {
ReportBaseQueryResponseDTO responseDTO = new ReportBaseQueryResponseDTO();
if (requestDTO == null || requestDTO.getNegativeQueryParam() == null) {
return responseDTO;
}
NegativeQueryParam request = requestDTO.getNegativeQueryParam();
if (CollUtil.isEmpty(request.getCrtTime()) || request.getCrtTime().size() < 2) {
return responseDTO;
}
DateCompareRangeUtil.CompareDateRange compareDateRange = DateCompareRangeUtil.buildCompareDateRange(request.getCrtTime().get(0), request.getCrtTime().get(1));
// 本期
NegativeQueryParam currentQueryParam = request.newQueryParam();
List<NegativeQueryVo> currentList = negativeQueryService.page(currentQueryParam).getRecords();
// 环比
NegativeQueryParam momQueryParam = request.newQueryParam();
momQueryParam.setCrtTime(compareDateRange.mom());
List<NegativeQueryVo> momList = negativeQueryService.page(momQueryParam).getRecords();
// 同比
NegativeQueryParam yoyQueryParam = request.newQueryParam();
yoyQueryParam.setCrtTime(compareDateRange.yoy());
List<NegativeQueryVo> yoyList = negativeQueryService.page(yoyQueryParam).getRecords();
responseDTO.setCurrentList(currentList != null ? currentList : new ArrayList<>());
responseDTO.setMomList(momList != null ? momList : new ArrayList<>());
responseDTO.setYoyList(yoyList != null ? yoyList : new ArrayList<>());
return responseDTO;
}
}

425
src/main/java/com/biutag/supervision/service/report/impl/ReportBusinessLineSectionServiceImpl.java

@ -0,0 +1,425 @@
package com.biutag.supervision.service.report.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.biutag.supervision.constants.enums.CheckStatusEnum;
import com.biutag.supervision.constants.enums.ProcessingStatusEnum;
import com.biutag.supervision.pojo.dto.report.accountability.ReportAccountabilityStatsRequestDTO;
import com.biutag.supervision.pojo.dto.report.accountability.ReportAccountabilityStatsResponseDTO;
import com.biutag.supervision.pojo.dto.report.base.ReportBaseQueryRequestDTO;
import com.biutag.supervision.pojo.dto.report.base.ReportBaseQueryResponseDTO;
import com.biutag.supervision.pojo.dto.report.businessLine.*;
import com.biutag.supervision.pojo.dto.report.closed.ClosedStatsCalcRequestDTO;
import com.biutag.supervision.pojo.dto.report.closed.ClosedStatsCalcResponseDTO;
import com.biutag.supervision.pojo.param.NegativeQueryParam;
import com.biutag.supervision.pojo.vo.DictProblemSourceTree;
import com.biutag.supervision.pojo.vo.NegativeQueryVo;
import com.biutag.supervision.service.NegativeQueryService;
import com.biutag.supervision.service.SupDictProblemSourceService;
import com.biutag.supervision.service.report.ReportAccountabilityStatsService;
import com.biutag.supervision.service.report.ReportBaseQueryService;
import com.biutag.supervision.service.report.ReportBusinessLineSectionService;
import com.biutag.supervision.service.report.ReportClosedStatsService;
import com.biutag.supervision.util.ChartRenderUtil;
import com.biutag.supervision.util.ReportTrendUtil;
import com.deepoove.poi.data.PictureRenderData;
import com.deepoove.poi.data.PictureType;
import com.deepoove.poi.data.Pictures;
import jakarta.annotation.Resource;
import lombok.Data;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;
@Service
public class ReportBusinessLineSectionServiceImpl implements ReportBusinessLineSectionService {
@Resource
private NegativeQueryService negativeQueryService;
@Resource
private SupDictProblemSourceService supDictProblemSourceService;
@Resource
private ReportBaseQueryService reportBaseQueryService;
@Resource
private ReportClosedStatsService reportClosedStatsService;
@Resource
private ReportAccountabilityStatsService reportAccountabilityStatsService;
@Override
public BusinessLineOverviewSection buildBusinessLineOverviewSection(ReportBusinessLineSectionRequestDTO requestDTO) {
if (requestDTO == null || requestDTO.getNegativeQueryParam() == null) {
return new BusinessLineOverviewSection();
}
NegativeQueryParam request = requestDTO.getNegativeQueryParam();
String periodStart = requestDTO.getPeriodStart();
String periodEnd = requestDTO.getPeriodEnd();
NegativeQueryParam currentQueryParam = request.newQueryParam();
List<NegativeQueryVo> currentList = negativeQueryService.page(currentQueryParam).getRecords();
Map<String, DictProblemSourceTree> idMap = supDictProblemSourceService.buildIdMap();
Map<String, Long> aggByParentLabel = currentList.stream()
.map(NegativeQueryVo::getProblemSourcesCode)
.map(childId -> parentLabel2Level(childId, idMap))
.collect(Collectors.groupingBy(s -> s, Collectors.counting()));
List<Map.Entry<String, Long>> sorted = aggByParentLabel.entrySet().stream()
.sorted((a, b) -> Long.compare(b.getValue(), a.getValue()))
.toList();
StringBuilder businessLineOverviewText = new StringBuilder();
for (int i = 0; i < sorted.size(); i++) {
String label = sorted.get(i).getKey();
int cnt = Math.toIntExact(sorted.get(i).getValue());
businessLineOverviewText.append(i + 1)
.append("、")
.append(label)
.append(",")
.append(cnt)
.append("条,占比")
.append(ReportTrendUtil.percent(cnt, currentList.size()))
.append("%;");
}
BusinessLineOverviewSection section = new BusinessLineOverviewSection();
section.setPeriodStart(periodStart);
section.setPeriodEnd(periodEnd);
section.setBusinessLineTotal(currentList.size());
section.setBusinessLineOverviewText(businessLineOverviewText.toString());
Map<String, Number> pieData = new LinkedHashMap<>();
for (Map.Entry<String, Long> entry : sorted) {
pieData.put(entry.getKey(), entry.getValue());
}
byte[] pieBytes = ChartRenderUtil.piePng("占比统计", pieData, 5, 900, 520);
PictureRenderData picture = Pictures.ofBytes(pieBytes, PictureType.PNG)
.size(500, 300)
.create();
section.setProblemPieChart(picture);
return section;
}
@Override
public List<BusinessLineDetailSection> buildBusinessLineDetailSections(ReportBusinessLineSectionRequestDTO requestDTO) {
if (requestDTO == null || requestDTO.getNegativeQueryParam() == null) {
return new ArrayList<>();
}
NegativeQueryParam request = requestDTO.getNegativeQueryParam();
ReportBaseQueryRequestDTO baseRequestDTO = new ReportBaseQueryRequestDTO();
baseRequestDTO.setNegativeQueryParam(request);
ReportBaseQueryResponseDTO baseQueryResponseDTO = reportBaseQueryService.queryBaseData(baseRequestDTO);
List<NegativeQueryVo> currentList = baseQueryResponseDTO.getCurrentList();
List<NegativeQueryVo> momList = baseQueryResponseDTO.getMomList();
List<NegativeQueryVo> yoyList = baseQueryResponseDTO.getYoyList();
Map<String, DictProblemSourceTree> idMap = supDictProblemSourceService.buildIdMap();
Map<String, List<NegativeQueryVo>> currentGroup = currentList.stream()
.collect(Collectors.groupingBy(
vo -> parentLabel2Level(vo.getProblemSourcesCode(), idMap),
LinkedHashMap::new,
Collectors.toList()
));
Map<String, List<NegativeQueryVo>> momGroup = momList.stream()
.collect(Collectors.groupingBy(
vo -> parentLabel2Level(vo.getProblemSourcesCode(), idMap),
LinkedHashMap::new,
Collectors.toList()
));
Map<String, List<NegativeQueryVo>> yoyGroup = yoyList.stream()
.collect(Collectors.groupingBy(
vo -> parentLabel2Level(vo.getProblemSourcesCode(), idMap),
LinkedHashMap::new,
Collectors.toList()
));
List<Map.Entry<String, List<NegativeQueryVo>>> sortedEntries = currentGroup.entrySet().stream()
.sorted((a, b) -> Integer.compare(b.getValue().size(), a.getValue().size()))
.toList();
List<BusinessLineDetailSection> result = new ArrayList<>();
int index = 1;
for (Map.Entry<String, List<NegativeQueryVo>> entry : sortedEntries) {
String fatherBusinessLineName = entry.getKey();
List<NegativeQueryVo> lineList = entry.getValue();
if (CollUtil.isEmpty(lineList)) {
continue;
}
BusinessLineDetailSection section = new BusinessLineDetailSection();
section.setOrderNo(toChineseOrderNo(index++));
section.setBusinessFatherLineName(fatherBusinessLineName);
section.setBusinessLineName(fatherBusinessLineName);
int totalIssued = lineList.size();
section.setTotalIssued(totalIssued);
List<NegativeQueryVo> closedList = lineList.stream()
.filter(one -> ProcessingStatusEnum.completed.name().equals(one.getProcessingStatus()))
.toList();
section.setClosedCount(closedList.size());
ClosedStatsCalcRequestDTO closedStatsCalcRequestDTO = new ClosedStatsCalcRequestDTO();
closedStatsCalcRequestDTO.setNegativeQueryVoList(lineList);
ClosedStatsCalcResponseDTO closedStats = reportClosedStatsService.calculateClosedStats(closedStatsCalcRequestDTO);
int verifiedTotal = Optional.ofNullable(closedStats.getClosedCount()).orElse(0);
section.setVerifiedTotal(verifiedTotal);
section.setVerifiedRate(Optional.ofNullable(closedStats.getVerifiedRate()).orElse(BigDecimal.ZERO));
List<String> verifiedNegativeIds = closedList.stream()
.filter(one -> CheckStatusEnum.TRUE_SET.contains(one.getCheckStatusCode())
|| CheckStatusEnum.PART_TRUE_SET.contains(one.getCheckStatusCode()))
.map(NegativeQueryVo::getId)
.filter(StrUtil::isNotBlank)
.distinct()
.toList();
ReportAccountabilityStatsRequestDTO statsRequestDTO = new ReportAccountabilityStatsRequestDTO();
statsRequestDTO.setNegativeIds(verifiedNegativeIds);
ReportAccountabilityStatsResponseDTO accountabilityStats = reportAccountabilityStatsService.calculateAccountabilityStats(statsRequestDTO);
section.setAccountabilityTotal(accountabilityStats.getTotal());
section.setAccountabilityRate(ReportTrendUtil.calcRate(accountabilityStats.getTotal(), verifiedTotal));
section.setPersonalAccountabilityCount(accountabilityStats.getPersonal());
section.setDepartmentAccountabilityCount(accountabilityStats.getUnit());
List<BusinessLineRankItem> topProblemTypes = lineList.stream()
.collect(Collectors.groupingBy(
vo -> leafProblemLabel(vo.getProblemSourcesCode(), idMap),
Collectors.counting()
))
.entrySet().stream()
.sorted((a, b) -> Long.compare(b.getValue(), a.getValue()))
.limit(3)
.map(e -> buildRankItem(
e.getKey(),
e.getValue().intValue(),
safePercentString(e.getValue().intValue(), totalIssued)
))
.toList();
section.setTopProblemTypes(topProblemTypes);
List<BusinessLineRankItem> topUnits = lineList.stream()
.filter(one -> StrUtil.isNotBlank(one.getInvolveDepartName()))
.collect(Collectors.groupingBy(NegativeQueryVo::getInvolveDepartName, Collectors.counting()))
.entrySet().stream()
.sorted((a, b) -> Long.compare(b.getValue(), a.getValue()))
.limit(3)
.map(e -> buildRankItem(
e.getKey(),
e.getValue().intValue(),
safePercentString(e.getValue().intValue(), totalIssued)
))
.toList();
section.setTopUnits(topUnits);
List<BusinessLinePersonRankItem> topPersons = buildTopPersons(lineList, totalIssued);
section.setTopPersons(topPersons);
int currentCount = totalIssued;
int momCount = momGroup.getOrDefault(fatherBusinessLineName, Collections.emptyList()).size();
int yoyCount = yoyGroup.getOrDefault(fatherBusinessLineName, Collections.emptyList()).size();
section.setMomRate(ReportTrendUtil.calcRate(currentCount, momCount));
section.setMomTrend(ReportTrendUtil.calcTrend(currentCount, momCount));
section.setYoyRate(ReportTrendUtil.calcRate(currentCount, yoyCount));
section.setYoyTrend(ReportTrendUtil.calcTrend(currentCount, yoyCount));
result.add(section);
}
return result;
}
/**
* 根据问题名称数量占比构建排名项
*/
private BusinessLineRankItem buildRankItem(String name, Integer count, String rate) {
BusinessLineRankItem item = new BusinessLineRankItem();
item.setName(name);
item.setCount(count);
item.setRate(new BigDecimal(rate));
return item;
}
/**
* 根据人员单位身份数量占比构建人员排名项
*/
private BusinessLinePersonRankItem buildPersonRankItem(String personName, String unitName, String identityName, Integer count, String rate) {
BusinessLinePersonRankItem item = new BusinessLinePersonRankItem();
item.setPersonName(personName);
item.setUnitName(unitName);
item.setIdentityName(identityName);
item.setCount(count);
item.setRate(new BigDecimal(rate));
return item;
}
/**
* 根据子级问题来源ID解析父级业务条线名称
*/
private String parentLabel2Level(String childId, Map<String, DictProblemSourceTree> idMap) {
if (StrUtil.isBlank(childId)) {
return "未归类";
}
DictProblemSourceTree child = idMap.get(childId);
if (child == null) {
return "未归类";
}
DictProblemSourceTree parent = idMap.get(child.getParentId());
return parent != null ? StrUtil.blankToDefault(parent.getLabel(), "未归类")
: StrUtil.blankToDefault(child.getLabel(), "未归类");
}
/**
* 根据问题来源ID获取当前节点名称
*/
private String leafProblemLabel(String problemSourceCode, Map<String, DictProblemSourceTree> idMap) {
if (StrUtil.isBlank(problemSourceCode)) {
return "未归类";
}
DictProblemSourceTree node = idMap.get(problemSourceCode);
if (node == null || StrUtil.isBlank(node.getLabel())) {
return "未归类";
}
return node.getLabel();
}
/**
* 安全计算占比字符串
*/
private String safePercentString(int numerator, int denominator) {
return ReportTrendUtil.percent(numerator, denominator).toPlainString();
}
/**
* 阿拉伯数字序号转中文序号
*/
private String toChineseOrderNo(int index) {
String[] arr = {"一", "二", "三", "四", "五", "六", "七", "八", "九", "十"};
if (index >= 1 && index <= arr.length) {
return arr[index - 1];
}
return String.valueOf(index);
}
/**
* 构建重点人员TOP3
*/
private List<BusinessLinePersonRankItem> buildTopPersons(List<NegativeQueryVo> lineList, int totalIssued) {
Map<String, PersonAgg> personAggMap = new LinkedHashMap<>();
for (NegativeQueryVo vo : lineList) {
String personName = readStringByGetter(vo,
"getInvolvePersonName",
"getPersonName",
"getPoliceName",
"getHandlePersonName",
"getName");
if (StrUtil.isBlank(personName)) {
continue;
}
String unitName = firstNotBlank(
readStringByGetter(vo, "getInvolveDepartName"),
readStringByGetter(vo, "getUnitName"),
readStringByGetter(vo, "getDepartName"),
"未知单位"
);
String identityName = firstNotBlank(
readStringByGetter(vo, "getIdentityName"),
readStringByGetter(vo, "getInvolveIdentityName"),
readStringByGetter(vo, "getPoliceTypeName"),
readStringByGetter(vo, "getUserTypeName"),
"未知身份"
);
String key = personName + "||" + unitName + "||" + identityName;
PersonAgg agg = personAggMap.computeIfAbsent(key, k -> {
PersonAgg x = new PersonAgg();
x.setPersonName(personName);
x.setUnitName(unitName);
x.setIdentityName(identityName);
x.setCount(0);
return x;
});
agg.setCount(agg.getCount() + 1);
}
return personAggMap.values().stream()
.sorted(Comparator.comparing(PersonAgg::getCount).reversed())
.limit(3)
.map(one -> buildPersonRankItem(
one.getPersonName(),
one.getUnitName(),
one.getIdentityName(),
one.getCount(),
safePercentString(one.getCount(), totalIssued)
))
.toList();
}
/**
* 通过反射按顺序读取字符串属性
*/
private String readStringByGetter(Object target, String... getterNames) {
if (target == null || getterNames == null) {
return null;
}
for (String getterName : getterNames) {
try {
java.lang.reflect.Method method = target.getClass().getMethod(getterName);
Object value = method.invoke(target);
if (value instanceof String str && StrUtil.isNotBlank(str)) {
return str;
}
} catch (Exception ignored) {
}
}
return null;
}
/**
* 返回第一个非空白字符串
*/
private String firstNotBlank(String... values) {
if (values == null) {
return null;
}
for (String value : values) {
if (StrUtil.isNotBlank(value)) {
return value;
}
}
return null;
}
/**
* 重点人员聚合对象
*/
@Data
private static class PersonAgg {
private String personName;
private String unitName;
private String identityName;
private int count;
}
}

63
src/main/java/com/biutag/supervision/service/report/impl/ReportClosedStatsServiceImpl.java

@ -0,0 +1,63 @@
package com.biutag.supervision.service.report.impl;
import com.biutag.supervision.constants.enums.CheckStatusEnum;
import com.biutag.supervision.constants.enums.ProcessingStatusEnum;
import com.biutag.supervision.pojo.dto.report.closed.ClosedStatsCalcRequestDTO;
import com.biutag.supervision.pojo.dto.report.closed.ClosedStatsCalcResponseDTO;
import com.biutag.supervision.pojo.vo.NegativeQueryVo;
import com.biutag.supervision.service.report.ReportClosedStatsService;
import com.biutag.supervision.util.ReportTrendUtil;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.util.Collections;
import java.util.List;
@Service
public class ReportClosedStatsServiceImpl implements ReportClosedStatsService {
@Override
public ClosedStatsCalcResponseDTO calculateClosedStats(ClosedStatsCalcRequestDTO requestDTO) {
List<NegativeQueryVo> list =
requestDTO == null || requestDTO.getNegativeQueryVoList() == null
? Collections.emptyList()
: requestDTO.getNegativeQueryVoList();
List<NegativeQueryVo> closedList = list.stream()
.filter(one -> ProcessingStatusEnum.completed.name().equals(one.getProcessingStatus()))
.toList();
List<NegativeQueryVo> verifiedList = closedList.stream()
.filter(one -> CheckStatusEnum.TRUE_SET.contains(one.getCheckStatusCode()))
.toList();
List<NegativeQueryVo> basicallyVerifiedList = closedList.stream()
.filter(one -> CheckStatusEnum.PART_TRUE_SET.contains(one.getCheckStatusCode()))
.toList();
List<NegativeQueryVo> unverifiedList = closedList.stream()
.filter(one -> CheckStatusEnum.FALSE_SET.contains(one.getCheckStatusCode()))
.toList();
BigDecimal verifiedRate = ReportTrendUtil.percent(
verifiedList.size() + basicallyVerifiedList.size(),
closedList.size()
);
ClosedStatsCalcResponseDTO responseDTO = new ClosedStatsCalcResponseDTO();
responseDTO.setClosedCount(closedList.size());
responseDTO.setVerifiedCount(verifiedList.size());
responseDTO.setBasicallyVerifiedCount(basicallyVerifiedList.size());
responseDTO.setUnverifiedCount(unverifiedList.size());
responseDTO.setVerifiedRate(verifiedRate);
responseDTO.setClosedList(closedList);
responseDTO.setVerifiedList(verifiedList);
responseDTO.setBasicallyVerifiedList(basicallyVerifiedList);
responseDTO.setUnverifiedList(unverifiedList);
return responseDTO;
}
}

68
src/main/java/com/biutag/supervision/service/report/impl/ReportDataServiceImpl.java

@ -0,0 +1,68 @@
package com.biutag.supervision.service.report.impl;
import com.biutag.supervision.pojo.dto.report.ReportViewModel;
import com.biutag.supervision.pojo.dto.report.accountability.ReportAccountabilitySectionRequestDTO;
import com.biutag.supervision.pojo.dto.report.businessLine.ReportBusinessLineSectionRequestDTO;
import com.biutag.supervision.pojo.dto.report.overview.ReportOverviewSectionRequestDTO;
import com.biutag.supervision.pojo.dto.report.unitInvestigation.ReportUnitInvestigationSectionRequestDTO;
import com.biutag.supervision.pojo.param.NegativeQueryParam;
import com.biutag.supervision.service.report.*;
import com.biutag.supervision.util.TimeUtil;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@Service
@Slf4j
public class ReportDataServiceImpl implements ReportDataService {
@Resource
private ReportOverviewSectionService reportOverviewSectionService;
@Resource
private ReportAccountabilitySectionService reportAccountabilitySectionService;
@Resource
private ReportBusinessLineSectionService reportBusinessLineSectionService;
@Resource
private ReportUnitInvestigationSectionService reportUnitInvestigationSectionService;
// 仅负责报表各模块编排,不承载具体统计逻辑
@Override
public ReportViewModel buildViewModel(NegativeQueryParam request) {
String periodStart = TimeUtil.formatDate(request.getCrtTime().get(0));
String periodEnd = TimeUtil.formatDate(request.getCrtTime().get(1));
ReportViewModel vm = new ReportViewModel();
vm.setPeriodStart(periodStart);
vm.setPeriodEnd(periodEnd);
// 总体概览
ReportOverviewSectionRequestDTO overviewRequestDTO = new ReportOverviewSectionRequestDTO();
overviewRequestDTO.setNegativeQueryParam(request);
overviewRequestDTO.setPeriodStart(periodStart);
overviewRequestDTO.setPeriodEnd(periodEnd);
vm.setOverviewSection(reportOverviewSectionService.buildOverviewSection(overviewRequestDTO));
// 业务条线
ReportBusinessLineSectionRequestDTO businessLineRequestDTO = new ReportBusinessLineSectionRequestDTO();
businessLineRequestDTO.setNegativeQueryParam(request);
businessLineRequestDTO.setPeriodStart(periodStart);
businessLineRequestDTO.setPeriodEnd(periodEnd);
// 条线总览
vm.setBusinessLineOverviewSection(reportBusinessLineSectionService.buildBusinessLineOverviewSection(businessLineRequestDTO));
// 条线明细
vm.setBusinessLineDetailSections(reportBusinessLineSectionService.buildBusinessLineDetailSections(businessLineRequestDTO));
// 单位查处情况总览
ReportUnitInvestigationSectionRequestDTO unitRequestDTO = new ReportUnitInvestigationSectionRequestDTO();
unitRequestDTO.setNegativeQueryParam(request);
unitRequestDTO.setPeriodStart(periodStart);
unitRequestDTO.setPeriodEnd(periodEnd);
vm.setUnitInvestigationOverviewSection(reportUnitInvestigationSectionService.buildUnitInvestigationOverviewSection(unitRequestDTO));
// 问责情况
ReportAccountabilitySectionRequestDTO accountabilityRequestDTO = new ReportAccountabilitySectionRequestDTO();
accountabilityRequestDTO.setNegativeQueryParam(request);
accountabilityRequestDTO.setPeriodStart(periodStart);
accountabilityRequestDTO.setPeriodEnd(periodEnd);
vm.setAccountabilityOverviewSection(reportAccountabilitySectionService.buildAccountabilityOverviewSection(accountabilityRequestDTO));
vm.setAccountabilityUnitDetailSection(reportAccountabilitySectionService.buildAccountabilityUnitDetailSection(accountabilityRequestDTO));
vm.setAccountabilityPersonOverviewSection(reportAccountabilitySectionService.buildAccountabilityPersonalOverviewSection(accountabilityRequestDTO));
// System.out.println(1/0);
return vm;
}
}

268
src/main/java/com/biutag/supervision/service/report/impl/ReportOverviewSectionServiceImpl.java

@ -0,0 +1,268 @@
package com.biutag.supervision.service.report.impl;
import cn.hutool.core.util.StrUtil;
import com.biutag.supervision.constants.enums.CheckStatusEnum;
import com.biutag.supervision.constants.enums.ProcessingStatusEnum;
import com.biutag.supervision.pojo.dto.report.OverviewSection;
import com.biutag.supervision.pojo.dto.report.accountability.ReportAccountabilityStatsRequestDTO;
import com.biutag.supervision.pojo.dto.report.accountability.ReportAccountabilityStatsResponseDTO;
import com.biutag.supervision.pojo.dto.report.base.ReportBaseQueryRequestDTO;
import com.biutag.supervision.pojo.dto.report.base.ReportBaseQueryResponseDTO;
import com.biutag.supervision.pojo.dto.report.overview.ReportOverviewSectionRequestDTO;
import com.biutag.supervision.pojo.param.NegativeQueryParam;
import com.biutag.supervision.pojo.vo.NegativeQueryVo;
import com.biutag.supervision.service.report.ReportAccountabilityStatsService;
import com.biutag.supervision.service.report.ReportBaseQueryService;
import com.biutag.supervision.service.report.ReportOverviewSectionService;
import com.biutag.supervision.util.ReportTrendUtil;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Stream;
/**
* @ClassName ReportOverviewSectionServiceImpl
* @Description 报表总体概览构建服务实现类
* @Author shihao
* @Date 2026/3/9
*/
@Service
public class ReportOverviewSectionServiceImpl implements ReportOverviewSectionService {
/**
* 报表基础查询服务
* 统一查询本期环比同比三类基础数据
*/
@Resource
private ReportBaseQueryService reportBaseQueryService;
/**
* 问责统计服务
* 统一计算个人问责单位问责总问责等数据
*/
@Resource
private ReportAccountabilityStatsService reportAccountabilityStatsService;
/**
* 构建总体概览数据
*
* @param requestDTO 总体概览构建请求DTO
* @return 总体概览构建结果DTO
*/
@Override
public OverviewSection buildOverviewSection(ReportOverviewSectionRequestDTO requestDTO) {
if (requestDTO == null || requestDTO.getNegativeQueryParam() == null) {
return new OverviewSection();
}
NegativeQueryParam request = requestDTO.getNegativeQueryParam();
String periodStart = requestDTO.getPeriodStart();
String periodEnd = requestDTO.getPeriodEnd();
// 查询本期、环比、同比基础数据
ReportBaseQueryRequestDTO baseRequestDTO = new ReportBaseQueryRequestDTO();
baseRequestDTO.setNegativeQueryParam(request);
ReportBaseQueryResponseDTO baseQueryResponseDTO = reportBaseQueryService.queryBaseData(baseRequestDTO);
List<NegativeQueryVo> ztNegativeList = baseQueryResponseDTO.getCurrentList();
List<NegativeQueryVo> hbNegativeList = baseQueryResponseDTO.getMomList();
List<NegativeQueryVo> tbNegativeList = baseQueryResponseDTO.getYoyList();
// 市局下发数据
List<NegativeQueryVo> sjxfNegativeList = ztNegativeList.stream()
.filter(one -> Objects.equals(0, one.getCrtDepartLevel()))
.toList();
// 分县市局下发数据
List<NegativeQueryVo> fxsjxfNegativeList = ztNegativeList.stream()
.filter(one -> Objects.equals(2, one.getCrtDepartLevel()))
.toList();
// 本期办结数据
List<NegativeQueryVo> bjNegativeList = ztNegativeList.stream()
.filter(one -> ProcessingStatusEnum.completed.name().equals(one.getProcessingStatus()))
.toList();
// 本期办结中属实数据
List<NegativeQueryVo> bjssNegativeList = bjNegativeList.stream()
.filter(one -> CheckStatusEnum.TRUE_SET.contains(one.getCheckStatusCode()))
.toList();
// 本期办结中基本属实数据
List<NegativeQueryVo> bjjbssNegativeList = bjNegativeList.stream()
.filter(one -> CheckStatusEnum.PART_TRUE_SET.contains(one.getCheckStatusCode()))
.toList();
// 本期办结中不属实数据
List<NegativeQueryVo> bjbssNegativeList = bjNegativeList.stream()
.filter(one -> CheckStatusEnum.FALSE_SET.contains(one.getCheckStatusCode()))
.toList();
// 本期属实 + 基本属实问题ID,用于计算问责数据
List<String> ssNegativeIds = Stream.concat(bjssNegativeList.stream(), bjjbssNegativeList.stream())
.map(NegativeQueryVo::getId)
.filter(StrUtil::isNotBlank)
.distinct()
.toList();
ReportAccountabilityStatsRequestDTO currentRequestDTO = new ReportAccountabilityStatsRequestDTO();
currentRequestDTO.setNegativeIds(ssNegativeIds);
ReportAccountabilityStatsResponseDTO currentAccountabilityStats =
reportAccountabilityStatsService.calculateAccountabilityStats(currentRequestDTO);
// ==================== 环比问责数据 ====================
// 环比办结数据
List<NegativeQueryVo> hbBjNegativeList = hbNegativeList.stream()
.filter(one -> ProcessingStatusEnum.completed.name().equals(one.getProcessingStatus()))
.toList();
// 环比办结中属实数据
List<NegativeQueryVo> hbBjssNegativeList = hbBjNegativeList.stream()
.filter(one -> CheckStatusEnum.TRUE_SET.contains(one.getCheckStatusCode()))
.toList();
// 环比办结中基本属实数据
List<NegativeQueryVo> hbBjjbssNegativeList = hbBjNegativeList.stream()
.filter(one -> CheckStatusEnum.PART_TRUE_SET.contains(one.getCheckStatusCode()))
.toList();
// 环比属实 + 基本属实问题ID
List<String> hbNegativeIds = Stream.concat(hbBjssNegativeList.stream(), hbBjjbssNegativeList.stream())
.map(NegativeQueryVo::getId)
.filter(StrUtil::isNotBlank)
.distinct()
.toList();
ReportAccountabilityStatsRequestDTO momRequestDTO = new ReportAccountabilityStatsRequestDTO();
momRequestDTO.setNegativeIds(hbNegativeIds);
ReportAccountabilityStatsResponseDTO momAccountabilityStats =
reportAccountabilityStatsService.calculateAccountabilityStats(momRequestDTO);
// ==================== 同比问责数据 ====================
// 同比办结数据
List<NegativeQueryVo> tbBjNegativeList = tbNegativeList.stream()
.filter(one -> ProcessingStatusEnum.completed.name().equals(one.getProcessingStatus()))
.toList();
// 同比办结中属实数据
List<NegativeQueryVo> tbBjssNegativeList = tbBjNegativeList.stream()
.filter(one -> CheckStatusEnum.TRUE_SET.contains(one.getCheckStatusCode()))
.toList();
// 同比办结中基本属实数据
List<NegativeQueryVo> tbBjjbssNegativeList = tbBjNegativeList.stream()
.filter(one -> CheckStatusEnum.PART_TRUE_SET.contains(one.getCheckStatusCode()))
.toList();
// 同比属实 + 基本属实问题ID
List<String> tbNegativeIds = Stream.concat(tbBjssNegativeList.stream(), tbBjjbssNegativeList.stream())
.map(NegativeQueryVo::getId)
.filter(StrUtil::isNotBlank)
.distinct()
.toList();
ReportAccountabilityStatsRequestDTO yoyRequestDTO = new ReportAccountabilityStatsRequestDTO();
yoyRequestDTO.setNegativeIds(tbNegativeIds);
ReportAccountabilityStatsResponseDTO yoyAccountabilityStats =
reportAccountabilityStatsService.calculateAccountabilityStats(yoyRequestDTO);
// 本期、环比、同比问题总数
int current = ztNegativeList.size();
int mom = hbNegativeList.size();
int yoy = tbNegativeList.size();
OverviewSection overviewSection = new OverviewSection();
// 统计区间
overviewSection.setPeriodStart(periodStart);
overviewSection.setPeriodEnd(periodEnd);
// 总下发数及环比、同比
overviewSection.setTotalIssued(ztNegativeList.size());
overviewSection.setMomRate(ReportTrendUtil.calcRate(current, mom));
overviewSection.setMomTrend(ReportTrendUtil.calcTrend(current, mom));
overviewSection.setYoyRate(ReportTrendUtil.calcRate(current, yoy));
overviewSection.setYoyTrend(ReportTrendUtil.calcTrend(current, yoy));
// 市局 / 分县市局下发情况
overviewSection.setCityIssued(sjxfNegativeList.size());
overviewSection.setCityRate(ReportTrendUtil.percent(sjxfNegativeList.size(), ztNegativeList.size()));
overviewSection.setCountyIssued(fxsjxfNegativeList.size());
overviewSection.setCountyRate(ReportTrendUtil.percent(fxsjxfNegativeList.size(), ztNegativeList.size()));
// 办结情况
overviewSection.setClosedCount(bjNegativeList.size());
overviewSection.setVerifiedCount(bjssNegativeList.size());
overviewSection.setBasicallyVerifiedCount(bjjbssNegativeList.size());
overviewSection.setUnverifiedCount(bjbssNegativeList.size());
// 查实率及环比、同比
BigDecimal curVerifiedRate = calcVerifiedRate(ztNegativeList);
BigDecimal momVerifiedRate = calcVerifiedRate(hbNegativeList);
BigDecimal yoyVerifiedRate = calcVerifiedRate(tbNegativeList);
overviewSection.setVerifiedRate(curVerifiedRate);
overviewSection.setVerifiedMomRate(ReportTrendUtil.calcRate(curVerifiedRate, momVerifiedRate));
overviewSection.setVerifiedMomTrend(ReportTrendUtil.calcTrend(curVerifiedRate, momVerifiedRate));
overviewSection.setVerifiedYoyRate(ReportTrendUtil.calcRate(curVerifiedRate, yoyVerifiedRate));
overviewSection.setVerifiedYoyTrend(ReportTrendUtil.calcTrend(curVerifiedRate, yoyVerifiedRate));
// 问责数据及环比、同比
int accountabilityTotal = currentAccountabilityStats.getTotal();
overviewSection.setAccountabilityTotal(accountabilityTotal);
overviewSection.setPersonalAccountability(currentAccountabilityStats.getPersonal());
overviewSection.setUnitAccountability(currentAccountabilityStats.getUnit());
overviewSection.setAccountabilityMomRate(ReportTrendUtil.calcRate(accountabilityTotal, momAccountabilityStats.getTotal()));
overviewSection.setAccountabilityMomTrend(ReportTrendUtil.calcTrend(accountabilityTotal, momAccountabilityStats.getTotal()));
overviewSection.setAccountabilityYoyRate(ReportTrendUtil.calcRate(accountabilityTotal, yoyAccountabilityStats.getTotal()));
overviewSection.setAccountabilityYoyTrend(ReportTrendUtil.calcTrend(accountabilityTotal, yoyAccountabilityStats.getTotal()));
// 集中问题
Map.Entry<String, Long> problemEntry =
ReportTrendUtil.topBy(NegativeQueryVo::getProblemSources, bjssNegativeList, bjjbssNegativeList);
overviewSection.setTopProblemProject(problemEntry != null ? problemEntry.getKey() : null);
// 重点关注单位
Map.Entry<String, Long> departEntry =
ReportTrendUtil.topBy(NegativeQueryVo::getInvolveDepartName, bjssNegativeList, bjjbssNegativeList);
overviewSection.setTopProblemUnit(departEntry != null ? departEntry.getKey() : null);
return overviewSection;
}
/**
* 计算查实率
* 查实率 = 属实数 + 基本属实数/ 办结数 * 100
*
* @param list 问题数据列表
* @return 查实率
*/
private BigDecimal calcVerifiedRate(List<NegativeQueryVo> list) {
// 办结数据
List<NegativeQueryVo> closed = list.stream()
.filter(one -> ProcessingStatusEnum.completed.name().equals(one.getProcessingStatus()))
.toList();
int closedCount = closed.size();
if (closedCount == 0) {
return BigDecimal.ZERO;
}
// 属实数
int verified = (int) closed.stream()
.filter(n -> CheckStatusEnum.TRUE_SET.contains(n.getCheckStatusCode()))
.count();
// 基本属实数
int partVerified = (int) closed.stream()
.filter(n -> CheckStatusEnum.PART_TRUE_SET.contains(n.getCheckStatusCode()))
.count();
return ReportTrendUtil.percent(verified + partVerified, closedCount);
}
}

118
src/main/java/com/biutag/supervision/service/report/impl/ReportUnitInvestigationSectionServiceImpl.java

@ -0,0 +1,118 @@
package com.biutag.supervision.service.report.impl;
import cn.hutool.core.collection.CollectionUtil;
import com.biutag.supervision.constants.enums.CheckStatusEnum;
import com.biutag.supervision.pojo.dto.DepartAndSubDepartDto;
import com.biutag.supervision.pojo.dto.report.unitInvestigation.ReportUnitInvestigationSectionRequestDTO;
import com.biutag.supervision.pojo.dto.report.unitInvestigation.UnitInvestigationItem;
import com.biutag.supervision.pojo.dto.report.unitInvestigation.UnitInvestigationOverviewSection;
import com.biutag.supervision.pojo.param.NegativeQueryParam;
import com.biutag.supervision.pojo.param.SupDepartGroupParam;
import com.biutag.supervision.pojo.vo.NegativeQueryVo;
import com.biutag.supervision.repository.supdepart.SupDepartResourceService;
import com.biutag.supervision.service.NegativeQueryService;
import com.biutag.supervision.service.report.ReportUnitInvestigationSectionService;
import com.biutag.supervision.util.ChartRenderUtil;
import com.biutag.supervision.util.ReportTrendUtil;
import com.deepoove.poi.data.PictureRenderData;
import com.deepoove.poi.data.PictureType;
import com.deepoove.poi.data.Pictures;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.*;
/**
* @ClassName ReportUnitInvestigationSectionServiceImpl
* @Description 报表单位查处模块构建服务实现类
* @Author shihao
* @Date 2026/3/9
*/
@Service
@Slf4j
public class ReportUnitInvestigationSectionServiceImpl implements ReportUnitInvestigationSectionService {
@Resource
private NegativeQueryService negativeQueryService;
@Resource
private SupDepartResourceService supDepartResourceService;
@Override
public UnitInvestigationOverviewSection buildUnitInvestigationOverviewSection(ReportUnitInvestigationSectionRequestDTO requestDTO) {
if (requestDTO == null || requestDTO.getNegativeQueryParam() == null) {
return new UnitInvestigationOverviewSection();
}
NegativeQueryParam request = requestDTO.getNegativeQueryParam();
String periodStart = requestDTO.getPeriodStart();
String periodEnd = requestDTO.getPeriodEnd();
// 本期问题数据
NegativeQueryParam currentQueryParam = request.newQueryParam();
List<NegativeQueryVo> currentList = negativeQueryService.page(currentQueryParam).getRecords();
// 查询父子部门关系
SupDepartGroupParam supDepartGroupParam = new SupDepartGroupParam();
supDepartGroupParam.setParentLevel(2);
supDepartGroupParam.setChildLevel(3);
Map<String, DepartAndSubDepartDto> departAndSubDepart =
supDepartResourceService.getDepartAndSubDepart(supDepartGroupParam);
List<UnitInvestigationItem> topUnits = new ArrayList<>();
for (DepartAndSubDepartDto value : departAndSubDepart.values()) {
Set<String> allDepartIds = value.getAllDepartIds();
List<NegativeQueryVo> voList = currentList.stream()
.filter(one -> allDepartIds.contains(one.getInvolveDepartId()))
.toList();
log.info(value.getParentName() + "的数量=====================" + voList.size());
if (CollectionUtil.isNotEmpty(voList)) {
UnitInvestigationItem item = new UnitInvestigationItem();
item.setUnitName(value.getParentName());
item.setIssuedProblemCount(voList.size());
List<NegativeQueryVo> csList = voList.stream()
.filter(one -> CheckStatusEnum.TRUE_SET.contains(one.getCheckStatusCode())
|| CheckStatusEnum.PART_TRUE_SET.contains(one.getCheckStatusCode()))
.toList();
item.setVerifiedProblemCount(csList.size());
item.setVerifiedRate(ReportTrendUtil.percent(csList.size(), voList.size()));
topUnits.add(item);
}
}
topUnits.sort(Comparator.comparing(UnitInvestigationItem::getVerifiedProblemCount).reversed());
topUnits = topUnits.stream().limit(3).toList();
UnitInvestigationOverviewSection section = new UnitInvestigationOverviewSection();
section.setPeriodStart(periodStart);
section.setPeriodEnd(periodEnd);
section.setTopUnits(topUnits);
// 柱状图
Map<String, Map<String, Number>> seriesData = new LinkedHashMap<>();
Map<String, Number> issuedMap = new LinkedHashMap<>();
Map<String, Number> verifiedMap = new LinkedHashMap<>();
for (UnitInvestigationItem item : topUnits) {
issuedMap.put(item.getUnitName(), item.getIssuedProblemCount());
verifiedMap.put(item.getUnitName(), item.getVerifiedProblemCount());
}
seriesData.put("下发问题数", issuedMap);
seriesData.put("查实问题数", verifiedMap);
byte[] barBytes = ChartRenderUtil.groupedBarPng("单位查处情况", seriesData, "单位", "数量", 1000, 520);
PictureRenderData picture = Pictures.ofBytes(barBytes, PictureType.PNG)
.size(500, 300)
.create();
section.setUnitBarChart(picture);
return section;
}
}

BIN
src/main/resources/static/templates/督审一体化平台研判分析报告.docx

Binary file not shown.
Loading…
Cancel
Save