diff --git a/src/main/java/com/biutag/supervision/pojo/dto/report/ReportViewModel.java b/src/main/java/com/biutag/supervision/pojo/dto/report/ReportViewModel.java index c479913..83e019c 100644 --- a/src/main/java/com/biutag/supervision/pojo/dto/report/ReportViewModel.java +++ b/src/main/java/com/biutag/supervision/pojo/dto/report/ReportViewModel.java @@ -1,6 +1,6 @@ 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.AccountabilityPersonalSection; import com.biutag.supervision.pojo.dto.report.businessLine.BusinessLineDetailSection; @@ -53,7 +53,7 @@ public class ReportViewModel { // 分开 预留可修改空间 @Schema(description = "单位问责追责情况明细") - private AccountabilityDepartmentSection accountabilityUnitDetailSection; + private AccountabilityUnitSection accountabilityUnitDetailSection; @Schema(description = "个人问责追责情况") private AccountabilityPersonalSection accountabilityPersonOverviewSection; diff --git a/src/main/java/com/biutag/supervision/pojo/dto/report/accountability/AccountabilityDepartmentSection.java b/src/main/java/com/biutag/supervision/pojo/dto/report/accountability/AccountabilityUnitSection.java similarity index 92% rename from src/main/java/com/biutag/supervision/pojo/dto/report/accountability/AccountabilityDepartmentSection.java rename to src/main/java/com/biutag/supervision/pojo/dto/report/accountability/AccountabilityUnitSection.java index 5bcaaea..a47d29c 100644 --- a/src/main/java/com/biutag/supervision/pojo/dto/report/accountability/AccountabilityDepartmentSection.java +++ b/src/main/java/com/biutag/supervision/pojo/dto/report/accountability/AccountabilityUnitSection.java @@ -10,7 +10,7 @@ import java.util.List; @Getter @Setter @Schema(description = "对单位追责问责情况") -public class AccountabilityDepartmentSection { +public class AccountabilityUnitSection { @Schema(description = "问责单位总次数", example = "6") private Integer totalCount; diff --git a/src/main/java/com/biutag/supervision/pojo/dto/report/accountability/ReportAccountabilitySectionRequestDTO.java b/src/main/java/com/biutag/supervision/pojo/dto/report/accountability/ReportAccountabilitySectionRequestDTO.java new file mode 100644 index 0000000..aa431d2 --- /dev/null +++ b/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; +} \ No newline at end of file diff --git a/src/main/java/com/biutag/supervision/pojo/dto/report/accountability/ReportAccountabilityStatsRequestDTO.java b/src/main/java/com/biutag/supervision/pojo/dto/report/accountability/ReportAccountabilityStatsRequestDTO.java new file mode 100644 index 0000000..654ad04 --- /dev/null +++ b/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 negativeIds = new ArrayList<>(); + +} \ No newline at end of file diff --git a/src/main/java/com/biutag/supervision/pojo/dto/report/accountability/ReportAccountabilityStatsResponseDTO.java b/src/main/java/com/biutag/supervision/pojo/dto/report/accountability/ReportAccountabilityStatsResponseDTO.java new file mode 100644 index 0000000..f28a91a --- /dev/null +++ b/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 personalBlames = new ArrayList<>(); + + @Schema(description = "领导问责列表") + private List leadBlames = new ArrayList<>(); + + @Schema(description = "单位问责列表") + private List unitBlames = new ArrayList<>(); + +} \ No newline at end of file diff --git a/src/main/java/com/biutag/supervision/pojo/dto/report/base/ReportBaseQueryRequestDTO.java b/src/main/java/com/biutag/supervision/pojo/dto/report/base/ReportBaseQueryRequestDTO.java new file mode 100644 index 0000000..42f5db3 --- /dev/null +++ b/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; + +} \ No newline at end of file diff --git a/src/main/java/com/biutag/supervision/pojo/dto/report/base/ReportBaseQueryResponseDTO.java b/src/main/java/com/biutag/supervision/pojo/dto/report/base/ReportBaseQueryResponseDTO.java new file mode 100644 index 0000000..df1fd64 --- /dev/null +++ b/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 currentList = new ArrayList<>(); + + @Schema(description = "环比数据列表") + private List momList = new ArrayList<>(); + + @Schema(description = "同比数据列表") + private List yoyList = new ArrayList<>(); + +} \ No newline at end of file diff --git a/src/main/java/com/biutag/supervision/pojo/dto/report/businessLine/ReportBusinessLineSectionRequestDTO.java b/src/main/java/com/biutag/supervision/pojo/dto/report/businessLine/ReportBusinessLineSectionRequestDTO.java new file mode 100644 index 0000000..76868bc --- /dev/null +++ b/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; +} \ No newline at end of file diff --git a/src/main/java/com/biutag/supervision/pojo/dto/report/closed/ClosedStatsCalcRequestDTO.java b/src/main/java/com/biutag/supervision/pojo/dto/report/closed/ClosedStatsCalcRequestDTO.java new file mode 100644 index 0000000..619b82c --- /dev/null +++ b/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 negativeQueryVoList = new ArrayList<>(); + +} \ No newline at end of file diff --git a/src/main/java/com/biutag/supervision/pojo/dto/report/closed/ClosedStatsCalcResponseDTO.java b/src/main/java/com/biutag/supervision/pojo/dto/report/closed/ClosedStatsCalcResponseDTO.java new file mode 100644 index 0000000..e953186 --- /dev/null +++ b/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 closedList = new ArrayList<>(); + + @Schema(description = "属实问题列表") + private List verifiedList = new ArrayList<>(); + + @Schema(description = "基本属实问题列表") + private List basicallyVerifiedList = new ArrayList<>(); + + @Schema(description = "不属实问题列表") + private List unverifiedList = new ArrayList<>(); + +} \ No newline at end of file diff --git a/src/main/java/com/biutag/supervision/pojo/dto/report/overview/ReportOverviewSectionRequestDTO.java b/src/main/java/com/biutag/supervision/pojo/dto/report/overview/ReportOverviewSectionRequestDTO.java new file mode 100644 index 0000000..ed701fd --- /dev/null +++ b/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; +} \ No newline at end of file diff --git a/src/main/java/com/biutag/supervision/pojo/dto/report/unitInvestigation/ReportUnitInvestigationSectionRequestDTO.java b/src/main/java/com/biutag/supervision/pojo/dto/report/unitInvestigation/ReportUnitInvestigationSectionRequestDTO.java new file mode 100644 index 0000000..62dc8cf --- /dev/null +++ b/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; +} \ No newline at end of file diff --git a/src/main/java/com/biutag/supervision/service/report/ReportAccountabilitySectionService.java b/src/main/java/com/biutag/supervision/service/report/ReportAccountabilitySectionService.java new file mode 100644 index 0000000..c895b1c --- /dev/null +++ b/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); +} \ No newline at end of file diff --git a/src/main/java/com/biutag/supervision/service/report/ReportAccountabilityStatsService.java b/src/main/java/com/biutag/supervision/service/report/ReportAccountabilityStatsService.java new file mode 100644 index 0000000..e4ad398 --- /dev/null +++ b/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); + +} \ No newline at end of file diff --git a/src/main/java/com/biutag/supervision/service/report/ReportBaseQueryService.java b/src/main/java/com/biutag/supervision/service/report/ReportBaseQueryService.java new file mode 100644 index 0000000..a13c338 --- /dev/null +++ b/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); + +} \ No newline at end of file diff --git a/src/main/java/com/biutag/supervision/service/report/ReportBusinessLineSectionService.java b/src/main/java/com/biutag/supervision/service/report/ReportBusinessLineSectionService.java new file mode 100644 index 0000000..457b795 --- /dev/null +++ b/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 buildBusinessLineDetailSections(ReportBusinessLineSectionRequestDTO requestDTO); +} \ No newline at end of file diff --git a/src/main/java/com/biutag/supervision/service/report/ReportClosedStatsService.java b/src/main/java/com/biutag/supervision/service/report/ReportClosedStatsService.java new file mode 100644 index 0000000..5852250 --- /dev/null +++ b/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); + +} \ No newline at end of file diff --git a/src/main/java/com/biutag/supervision/service/report/ReportDataServiceImpl.java b/src/main/java/com/biutag/supervision/service/report/ReportDataServiceImpl.java deleted file mode 100644 index bae68be..0000000 --- a/src/main/java/com/biutag/supervision/service/report/ReportDataServiceImpl.java +++ /dev/null @@ -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 ztNegativeList = negativeQueryService.page(ztnegativeQueryParam).getRecords(); - List ztNegativeIds = ztNegativeList.stream().map(NegativeQueryVo::getId).toList(); - AccountabilityStats accountabilityStats = buildAccountabilityStats(ztNegativeIds); - // 个人问责 - Map> leadMap = accountabilityStats.getLeadBlames().stream().collect(Collectors.groupingBy(NegativeBlame::getLeadHandleResultName)); - Map> personMap = accountabilityStats.getPersonalBlames().stream().collect(Collectors.groupingBy(NegativeBlame::getHandleResultName)); - Map> mergedMap = new HashMap<>(personMap); - leadMap.forEach((key, value) -> - mergedMap.merge(key, value, (list1, list2) -> { - List list = new ArrayList<>(list1); - list.addAll(list2); - return list; - }) - ); - AccountabilityPersonalSection accountabilityPersonalSection = new AccountabilityPersonalSection(); - accountabilityPersonalSection.setTotalCount(accountabilityStats.getPersonal()); - // 构造 typeItems - List 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 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 ztNegativeList = negativeQueryService.page(ztnegativeQueryParam).getRecords(); - List ztNegativeIds = ztNegativeList.stream().map(NegativeQueryVo::getId).toList(); - AccountabilityStats accountabilityStats = buildAccountabilityStats(ztNegativeIds); - // 单位问责 - Map> unitMap = accountabilityStats.getUnitBlames().stream().collect(Collectors.groupingBy(NegativeBlame::getHandleResultName)); - - AccountabilityDepartmentSection accountabilityDepartmentSection = new AccountabilityDepartmentSection(); - accountabilityDepartmentSection.setTotalCount(accountabilityStats.getUnit()); - // 构造 typeItems - List 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 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 ztNegativeList = negativeQueryService.page(ztnegativeQueryParam).getRecords(); - List 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 ztNegativeList = negativeQueryService.page(ztnegativeQueryParam).getRecords(); - SupDepartGroupParam supDepartGroupParam = new SupDepartGroupParam(); - supDepartGroupParam.setParentLevel(2); - supDepartGroupParam.setChildLevel(3); - Map departAndSubDepart = supDepartResourceService.getDepartAndSubDepart(supDepartGroupParam); - List topUnits = new ArrayList<>(); - for (DepartAndSubDepartDto value : departAndSubDepart.values()) { - Set allDepartIds = value.getAllDepartIds(); - List 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 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> seriesData = new LinkedHashMap<>(); - Map issuedMap = new LinkedHashMap<>(); - Map 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 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 currentList = negativeQueryService.page(currentQueryParam).getRecords(); - - // 环比数据 - NegativeQueryParam momQueryParam = request.newQueryParam(); - momQueryParam.setCrtTime(compareDateRange.mom()); - List momList = negativeQueryService.page(momQueryParam).getRecords(); - - // 同比数据 - NegativeQueryParam yoyQueryParam = request.newQueryParam(); - yoyQueryParam.setCrtTime(compareDateRange.yoy()); - List yoyList = negativeQueryService.page(yoyQueryParam).getRecords(); - - Map idMap = supDictProblemSourceService.buildIdMap(); - - // 按“父级业务条线”分组 - Map> currentGroup = currentList.stream() - .collect(Collectors.groupingBy( - vo -> parentLabel2Level(vo.getProblemSourcesCode(), idMap), - LinkedHashMap::new, - Collectors.toList() - )); - - Map> momGroup = momList.stream() - .collect(Collectors.groupingBy( - vo -> parentLabel2Level(vo.getProblemSourcesCode(), idMap), - LinkedHashMap::new, - Collectors.toList() - )); - - Map> yoyGroup = yoyList.stream() - .collect(Collectors.groupingBy( - vo -> parentLabel2Level(vo.getProblemSourcesCode(), idMap), - LinkedHashMap::new, - Collectors.toList() - )); - - // 按本期数量倒序 - List>> sortedEntries = currentGroup.entrySet().stream() - .sorted((a, b) -> Integer.compare(b.getValue().size(), a.getValue().size())) - .toList(); - - List result = new ArrayList<>(); - int index = 1; - - for (Map.Entry> entry : sortedEntries) { - String fatherBusinessLineName = entry.getKey(); - List 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 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 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 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 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 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 ztNegativeList = negativeQueryService.page(ztNegativeQueryParam).getRecords(); - Map idMap = supDictProblemSourceService.buildIdMap(); - // 计数 - Map aggByParentLabel = ztNegativeList.stream() - .map(NegativeQueryVo::getProblemSourcesCode) // 收集二级id - .map(childId -> parentLabel2Level(childId, idMap)) // 解析未一级label - .collect(Collectors.groupingBy(s -> s, Collectors.counting())); - // 排序 - List> 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 pieData = new LinkedHashMap<>(); - for (Map.Entry 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 ztNegativeList = negativeQueryService.page(ztnegativeQueryParam).getRecords(); - // 环比数据 - NegativeQueryParam hbNegativeQueryParam = request.newQueryParam(); - hbNegativeQueryParam.setCrtTime(compareDateRange.mom()); - List hbNegativList = negativeQueryService.page(hbNegativeQueryParam).getRecords(); - // 同比数据 - NegativeQueryParam tbNegativeQueryParam = request.newQueryParam(); - tbNegativeQueryParam.setCrtTime(compareDateRange.yoy()); - List tbNegativList = negativeQueryService.page(tbNegativeQueryParam).getRecords(); - // 市局下发数据 - List sjxfNegativeList = ztNegativeList.stream().filter(one -> Objects.equals(0, one.getCrtDepartLevel())).toList(); - // 分县市局下发数据 - List fxsjxfNegativeList = ztNegativeList.stream().filter(one -> Objects.equals(2, one.getCrtDepartLevel())).toList(); - // 办结数据 - List bjNegativeList = ztNegativeList.stream().filter(one -> ProcessingStatusEnum.completed.name().equals(one.getProcessingStatus())).toList(); - // 办结中属实数据 - List bjssNegativeList = bjNegativeList.stream().filter(one -> CheckStatusEnum.TRUE_SET.contains(one.getCheckStatusCode())).toList(); - // 办结中基本属实数据 - List bjjbssNegativeList = bjNegativeList.stream().filter(one -> CheckStatusEnum.PART_TRUE_SET.contains(one.getCheckStatusCode())).toList(); - // 办结中不属实数据 - List bjbssNegativeList = bjNegativeList.stream().filter(one -> CheckStatusEnum.FALSE_SET.contains(one.getCheckStatusCode())).toList(); - // 属实 基本属实中的问责数据 - List ssNegativeIds = Stream.concat(bjssNegativeList.stream(), bjjbssNegativeList.stream()) - .map(NegativeQueryVo::getId).filter(StrUtil::isNotBlank).distinct().toList(); - AccountabilityStats currentAccountabilityStats = buildAccountabilityStats(ssNegativeIds); - // ==================== 环比问责数据 ==================== - // 环比办结数据 - List hbBjNegativeList = hbNegativList.stream() - .filter(one -> ProcessingStatusEnum.completed.name().equals(one.getProcessingStatus())) - .toList(); - // 环比办结中属实数据 - List hbBjssNegativeList = hbBjNegativeList.stream() - .filter(one -> CheckStatusEnum.TRUE_SET.contains(one.getCheckStatusCode())) - .toList(); - // 环比办结中基本属实数据 - List hbBjjbssNegativeList = hbBjNegativeList.stream() - .filter(one -> CheckStatusEnum.PART_TRUE_SET.contains(one.getCheckStatusCode())) - .toList(); - // 环比属实 + 基本属实中的问责数据 - List hbNegativeIds = Stream.concat(hbBjssNegativeList.stream(), hbBjjbssNegativeList.stream()) - .map(NegativeQueryVo::getId) - .filter(StrUtil::isNotBlank) - .distinct() - .toList(); - AccountabilityStats momAccountabilityStats = buildAccountabilityStats(hbNegativeIds); - int hbAccountabilityTotal = momAccountabilityStats.getTotal(); - // ==================== 同比问责数据 ==================== - // 同比办结数据 - List tbBjNegativeList = tbNegativList.stream() - .filter(one -> ProcessingStatusEnum.completed.name().equals(one.getProcessingStatus())) - .toList(); - // 同比办结中属实数据 - List tbBjssNegativeList = tbBjNegativeList.stream() - .filter(one -> CheckStatusEnum.TRUE_SET.contains(one.getCheckStatusCode())) - .toList(); - // 同比办结中基本属实数据 - List tbBjjbssNegativeList = tbBjNegativeList.stream() - .filter(one -> CheckStatusEnum.PART_TRUE_SET.contains(one.getCheckStatusCode())) - .toList(); - // 同比属实 + 基本属实中的问责数据 - List 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 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 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 list) { - // 办结 - List 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 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 negativeIds) { - AccountabilityStats stats = new AccountabilityStats(); - if (CollUtil.isEmpty(negativeIds)) { - return stats; - } - NegativeBlameQueryParam queryParam = new NegativeBlameQueryParam(); - queryParam.setNegativeIds(negativeIds); - List negativeBlames = negativeBlameResourceService.query(queryParam); - // 个人问责 - List personalBlames = negativeBlames.stream() - .filter(one -> "personal".equals(one.getType())) - .filter(one -> StrUtil.isNotBlank(one.getHandleResultName())) - .filter(one -> !"不予追责".equals(one.getHandleResultName())) - .toList(); - // 个人问责中的领导问责 - Set seenLead = new HashSet<>(); - List 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 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 personalBlames = new ArrayList<>(); - - /** - * 领导问责 - */ - private List leadBlames = new ArrayList<>(); - - /** - * 单位问责 - */ - private List unitBlames = new ArrayList<>(); - } - - - private String leafProblemLabel(String problemSourceCode, Map 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 buildTopPersons(List lineList, int totalIssued) { - Map 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; - } - -} \ No newline at end of file diff --git a/src/main/java/com/biutag/supervision/service/report/ReportOverviewSectionService.java b/src/main/java/com/biutag/supervision/service/report/ReportOverviewSectionService.java new file mode 100644 index 0000000..3be54e1 --- /dev/null +++ b/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); + +} \ No newline at end of file diff --git a/src/main/java/com/biutag/supervision/service/report/ReportUnitInvestigationSectionService.java b/src/main/java/com/biutag/supervision/service/report/ReportUnitInvestigationSectionService.java new file mode 100644 index 0000000..765baac --- /dev/null +++ b/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); +} \ No newline at end of file diff --git a/src/main/java/com/biutag/supervision/service/report/impl/ReportAccountabilitySectionServiceImpl.java b/src/main/java/com/biutag/supervision/service/report/impl/ReportAccountabilitySectionServiceImpl.java new file mode 100644 index 0000000..2f012eb --- /dev/null +++ b/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 currentList = negativeQueryService.page(currentQueryParam).getRecords(); + + List 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 currentList = negativeQueryService.page(currentQueryParam).getRecords(); + + List negativeIds = currentList.stream() + .map(NegativeQueryVo::getId) + .filter(Objects::nonNull) + .toList(); + + ReportAccountabilityStatsRequestDTO statsRequestDTO = new ReportAccountabilityStatsRequestDTO(); + statsRequestDTO.setNegativeIds(negativeIds); + ReportAccountabilityStatsResponseDTO statsResponseDTO = + reportAccountabilityStatsService.calculateAccountabilityStats(statsRequestDTO); + + Map> unitMap = statsResponseDTO.getUnitBlames().stream() + .collect(java.util.stream.Collectors.groupingBy(NegativeBlame::getHandleResultName)); + + AccountabilityUnitSection section = new AccountabilityUnitSection(); + section.setTotalCount(statsResponseDTO.getUnit()); + + List 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 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 currentList = negativeQueryService.page(currentQueryParam).getRecords(); + + List negativeIds = currentList.stream() + .map(NegativeQueryVo::getId) + .filter(Objects::nonNull) + .toList(); + + ReportAccountabilityStatsRequestDTO statsRequestDTO = new ReportAccountabilityStatsRequestDTO(); + statsRequestDTO.setNegativeIds(negativeIds); + ReportAccountabilityStatsResponseDTO statsResponseDTO = + reportAccountabilityStatsService.calculateAccountabilityStats(statsRequestDTO); + + Map> leadMap = statsResponseDTO.getLeadBlames().stream() + .collect(java.util.stream.Collectors.groupingBy(NegativeBlame::getLeadHandleResultName)); + + Map> personMap = statsResponseDTO.getPersonalBlames().stream() + .collect(java.util.stream.Collectors.groupingBy(NegativeBlame::getHandleResultName)); + + Map> mergedMap = new HashMap<>(personMap); + leadMap.forEach((key, value) -> + mergedMap.merge(key, value, (list1, list2) -> { + List list = new ArrayList<>(list1); + list.addAll(list2); + return list; + }) + ); + + AccountabilityPersonalSection section = new AccountabilityPersonalSection(); + section.setTotalCount(statsResponseDTO.getPersonal()); + + List 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 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; + } +} \ No newline at end of file diff --git a/src/main/java/com/biutag/supervision/service/report/impl/ReportAccountabilityStatsServiceImpl.java b/src/main/java/com/biutag/supervision/service/report/impl/ReportAccountabilityStatsServiceImpl.java new file mode 100644 index 0000000..3ed55d6 --- /dev/null +++ b/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 negativeBlames = negativeBlameResourceService.query(queryParam); + + // 个人问责 + List personalBlames = negativeBlames.stream() + .filter(one -> "personal".equals(one.getType())) + .filter(one -> StrUtil.isNotBlank(one.getHandleResultName())) + .filter(one -> !"不予追责".equals(one.getHandleResultName())) + .toList(); + + // 个人问责中的领导问责 + Set seenLead = new HashSet<>(); + List 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 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; + } +} \ No newline at end of file diff --git a/src/main/java/com/biutag/supervision/service/report/impl/ReportBaseQueryServiceImpl.java b/src/main/java/com/biutag/supervision/service/report/impl/ReportBaseQueryServiceImpl.java new file mode 100644 index 0000000..119b6bb --- /dev/null +++ b/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 currentList = negativeQueryService.page(currentQueryParam).getRecords(); + // 环比 + NegativeQueryParam momQueryParam = request.newQueryParam(); + momQueryParam.setCrtTime(compareDateRange.mom()); + List momList = negativeQueryService.page(momQueryParam).getRecords(); + // 同比 + NegativeQueryParam yoyQueryParam = request.newQueryParam(); + yoyQueryParam.setCrtTime(compareDateRange.yoy()); + List 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; + } +} \ No newline at end of file diff --git a/src/main/java/com/biutag/supervision/service/report/impl/ReportBusinessLineSectionServiceImpl.java b/src/main/java/com/biutag/supervision/service/report/impl/ReportBusinessLineSectionServiceImpl.java new file mode 100644 index 0000000..a6c8b69 --- /dev/null +++ b/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 currentList = negativeQueryService.page(currentQueryParam).getRecords(); + + Map idMap = supDictProblemSourceService.buildIdMap(); + + Map aggByParentLabel = currentList.stream() + .map(NegativeQueryVo::getProblemSourcesCode) + .map(childId -> parentLabel2Level(childId, idMap)) + .collect(Collectors.groupingBy(s -> s, Collectors.counting())); + + List> 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 pieData = new LinkedHashMap<>(); + for (Map.Entry 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 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 currentList = baseQueryResponseDTO.getCurrentList(); + List momList = baseQueryResponseDTO.getMomList(); + List yoyList = baseQueryResponseDTO.getYoyList(); + + Map idMap = supDictProblemSourceService.buildIdMap(); + + Map> currentGroup = currentList.stream() + .collect(Collectors.groupingBy( + vo -> parentLabel2Level(vo.getProblemSourcesCode(), idMap), + LinkedHashMap::new, + Collectors.toList() + )); + + Map> momGroup = momList.stream() + .collect(Collectors.groupingBy( + vo -> parentLabel2Level(vo.getProblemSourcesCode(), idMap), + LinkedHashMap::new, + Collectors.toList() + )); + + Map> yoyGroup = yoyList.stream() + .collect(Collectors.groupingBy( + vo -> parentLabel2Level(vo.getProblemSourcesCode(), idMap), + LinkedHashMap::new, + Collectors.toList() + )); + + List>> sortedEntries = currentGroup.entrySet().stream() + .sorted((a, b) -> Integer.compare(b.getValue().size(), a.getValue().size())) + .toList(); + + List result = new ArrayList<>(); + int index = 1; + + for (Map.Entry> entry : sortedEntries) { + String fatherBusinessLineName = entry.getKey(); + List 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 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 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 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 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 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 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 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 buildTopPersons(List lineList, int totalIssued) { + Map 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; + } +} + diff --git a/src/main/java/com/biutag/supervision/service/report/impl/ReportClosedStatsServiceImpl.java b/src/main/java/com/biutag/supervision/service/report/impl/ReportClosedStatsServiceImpl.java new file mode 100644 index 0000000..ffd78c3 --- /dev/null +++ b/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 list = + requestDTO == null || requestDTO.getNegativeQueryVoList() == null + ? Collections.emptyList() + : requestDTO.getNegativeQueryVoList(); + + List closedList = list.stream() + .filter(one -> ProcessingStatusEnum.completed.name().equals(one.getProcessingStatus())) + .toList(); + + List verifiedList = closedList.stream() + .filter(one -> CheckStatusEnum.TRUE_SET.contains(one.getCheckStatusCode())) + .toList(); + + List basicallyVerifiedList = closedList.stream() + .filter(one -> CheckStatusEnum.PART_TRUE_SET.contains(one.getCheckStatusCode())) + .toList(); + + List 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; + } +} \ No newline at end of file diff --git a/src/main/java/com/biutag/supervision/service/report/impl/ReportDataServiceImpl.java b/src/main/java/com/biutag/supervision/service/report/impl/ReportDataServiceImpl.java new file mode 100644 index 0000000..091ef5f --- /dev/null +++ b/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; + } +} \ No newline at end of file diff --git a/src/main/java/com/biutag/supervision/service/report/impl/ReportOverviewSectionServiceImpl.java b/src/main/java/com/biutag/supervision/service/report/impl/ReportOverviewSectionServiceImpl.java new file mode 100644 index 0000000..e6fe0f2 --- /dev/null +++ b/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 ztNegativeList = baseQueryResponseDTO.getCurrentList(); + List hbNegativeList = baseQueryResponseDTO.getMomList(); + List tbNegativeList = baseQueryResponseDTO.getYoyList(); + + // 市局下发数据 + List sjxfNegativeList = ztNegativeList.stream() + .filter(one -> Objects.equals(0, one.getCrtDepartLevel())) + .toList(); + + // 分县市局下发数据 + List fxsjxfNegativeList = ztNegativeList.stream() + .filter(one -> Objects.equals(2, one.getCrtDepartLevel())) + .toList(); + + // 本期办结数据 + List bjNegativeList = ztNegativeList.stream() + .filter(one -> ProcessingStatusEnum.completed.name().equals(one.getProcessingStatus())) + .toList(); + + // 本期办结中属实数据 + List bjssNegativeList = bjNegativeList.stream() + .filter(one -> CheckStatusEnum.TRUE_SET.contains(one.getCheckStatusCode())) + .toList(); + + // 本期办结中基本属实数据 + List bjjbssNegativeList = bjNegativeList.stream() + .filter(one -> CheckStatusEnum.PART_TRUE_SET.contains(one.getCheckStatusCode())) + .toList(); + + // 本期办结中不属实数据 + List bjbssNegativeList = bjNegativeList.stream() + .filter(one -> CheckStatusEnum.FALSE_SET.contains(one.getCheckStatusCode())) + .toList(); + + // 本期属实 + 基本属实问题ID,用于计算问责数据 + List 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 hbBjNegativeList = hbNegativeList.stream() + .filter(one -> ProcessingStatusEnum.completed.name().equals(one.getProcessingStatus())) + .toList(); + + // 环比办结中属实数据 + List hbBjssNegativeList = hbBjNegativeList.stream() + .filter(one -> CheckStatusEnum.TRUE_SET.contains(one.getCheckStatusCode())) + .toList(); + + // 环比办结中基本属实数据 + List hbBjjbssNegativeList = hbBjNegativeList.stream() + .filter(one -> CheckStatusEnum.PART_TRUE_SET.contains(one.getCheckStatusCode())) + .toList(); + + // 环比属实 + 基本属实问题ID + List 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 tbBjNegativeList = tbNegativeList.stream() + .filter(one -> ProcessingStatusEnum.completed.name().equals(one.getProcessingStatus())) + .toList(); + + // 同比办结中属实数据 + List tbBjssNegativeList = tbBjNegativeList.stream() + .filter(one -> CheckStatusEnum.TRUE_SET.contains(one.getCheckStatusCode())) + .toList(); + + // 同比办结中基本属实数据 + List tbBjjbssNegativeList = tbBjNegativeList.stream() + .filter(one -> CheckStatusEnum.PART_TRUE_SET.contains(one.getCheckStatusCode())) + .toList(); + + // 同比属实 + 基本属实问题ID + List 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 problemEntry = + ReportTrendUtil.topBy(NegativeQueryVo::getProblemSources, bjssNegativeList, bjjbssNegativeList); + overviewSection.setTopProblemProject(problemEntry != null ? problemEntry.getKey() : null); + + // 重点关注单位 + Map.Entry departEntry = + ReportTrendUtil.topBy(NegativeQueryVo::getInvolveDepartName, bjssNegativeList, bjjbssNegativeList); + overviewSection.setTopProblemUnit(departEntry != null ? departEntry.getKey() : null); + return overviewSection; + } + + /** + * 计算查实率 + * 查实率 = (属实数 + 基本属实数)/ 办结数 * 100 + * + * @param list 问题数据列表 + * @return 查实率 + */ + private BigDecimal calcVerifiedRate(List list) { + // 办结数据 + List 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); + } +} \ No newline at end of file diff --git a/src/main/java/com/biutag/supervision/service/report/impl/ReportUnitInvestigationSectionServiceImpl.java b/src/main/java/com/biutag/supervision/service/report/impl/ReportUnitInvestigationSectionServiceImpl.java new file mode 100644 index 0000000..e931af5 --- /dev/null +++ b/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 currentList = negativeQueryService.page(currentQueryParam).getRecords(); + + // 查询父子部门关系 + SupDepartGroupParam supDepartGroupParam = new SupDepartGroupParam(); + supDepartGroupParam.setParentLevel(2); + supDepartGroupParam.setChildLevel(3); + Map departAndSubDepart = + supDepartResourceService.getDepartAndSubDepart(supDepartGroupParam); + + List topUnits = new ArrayList<>(); + for (DepartAndSubDepartDto value : departAndSubDepart.values()) { + Set allDepartIds = value.getAllDepartIds(); + + List 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 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> seriesData = new LinkedHashMap<>(); + Map issuedMap = new LinkedHashMap<>(); + Map 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; + } +} \ No newline at end of file diff --git a/src/main/resources/static/templates/督审一体化平台研判分析报告.docx b/src/main/resources/static/templates/督审一体化平台研判分析报告.docx index 0badf43..3fe6b2e 100644 Binary files a/src/main/resources/static/templates/督审一体化平台研判分析报告.docx and b/src/main/resources/static/templates/督审一体化平台研判分析报告.docx differ