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