diff --git a/src/main/java/com/biutag/supervision/controller/work/NegativeReportController.java b/src/main/java/com/biutag/supervision/controller/work/NegativeReportController.java index c268a6d..fd2279f 100644 --- a/src/main/java/com/biutag/supervision/controller/work/NegativeReportController.java +++ b/src/main/java/com/biutag/supervision/controller/work/NegativeReportController.java @@ -1,28 +1,48 @@ package com.biutag.supervision.controller.work; +import com.biutag.supervision.common.UserContextHolder; +import com.biutag.supervision.pojo.Result; +import com.biutag.supervision.pojo.entity.NegativeTask; +import com.biutag.supervision.pojo.model.UserAuth; import com.biutag.supervision.pojo.param.NegativeQueryParam; +import com.biutag.supervision.service.NegativeTaskService; import com.biutag.supervision.service.negativeReport.NegativeReportService; import io.swagger.v3.oas.annotations.Operation; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import java.io.UnsupportedEncodingException; - +@Slf4j @RequiredArgsConstructor @RequestMapping("negative") @RestController public class NegativeReportController { private final NegativeReportService negativeReportService; + private final NegativeTaskService negativeTaskService; + @Deprecated @Operation(summary = "生成研判分析报告") @PostMapping("/generateReport") - public void generateReport(@RequestBody NegativeQueryParam request, HttpServletResponse response) throws UnsupportedEncodingException { + public void generateReport(@RequestBody NegativeQueryParam request, HttpServletResponse response) { negativeReportService.generateReport(request, response); } + @Operation(summary = "生成研判分析报告任务") + @PostMapping("/export/report") + public Result generateReport(@RequestBody NegativeQueryParam request) { + request.setCurrent(1); + request.setSize(1000000); + UserAuth currentUser = UserContextHolder.getCurrentUser(); + request.setCurrentUser(currentUser); + NegativeTask negativeTask = negativeTaskService.saveReportTask(request); + negativeReportService.exportReport(request, negativeTask.getId()); + + return Result.success(); + } + } diff --git a/src/main/java/com/biutag/supervision/pojo/dto/report/OverviewSection.java b/src/main/java/com/biutag/supervision/pojo/dto/report/OverviewSection.java index f9cec7c..f336018 100644 --- a/src/main/java/com/biutag/supervision/pojo/dto/report/OverviewSection.java +++ b/src/main/java/com/biutag/supervision/pojo/dto/report/OverviewSection.java @@ -76,6 +76,9 @@ public class OverviewSection { @Schema(description = "问责问题数", example = "5") private Integer accountabilityProblemTotal; + @Schema(description = "问责率 问责问题条数/办结的条数") + private BigDecimal accountabilityNegativeRate; + @Schema(description = "问责总人次", example = "58") private Integer accountabilityTotal; diff --git a/src/main/java/com/biutag/supervision/pojo/dto/report/accountability/ReportAccountabilityStatsRequestDTO.java b/src/main/java/com/biutag/supervision/pojo/dto/report/accountability/ReportAccountabilityStatsRequestDTO.java index 7aa7bb8..cd3bbad 100644 --- a/src/main/java/com/biutag/supervision/pojo/dto/report/accountability/ReportAccountabilityStatsRequestDTO.java +++ b/src/main/java/com/biutag/supervision/pojo/dto/report/accountability/ReportAccountabilityStatsRequestDTO.java @@ -1,5 +1,6 @@ package com.biutag.supervision.pojo.dto.report.accountability; +import com.biutag.supervision.pojo.model.UserAuth; import com.biutag.supervision.pojo.vo.NegativeQueryVo; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Getter; @@ -27,5 +28,8 @@ public class ReportAccountabilityStatsRequestDTO { @Schema(description = "原始问题列表") private List negativeQueryVoList; + private UserAuth currentUser; + + } \ No newline at end of file diff --git a/src/main/java/com/biutag/supervision/pojo/dto/report/accountability/ReportAccountabilityStatsResponseDTO.java b/src/main/java/com/biutag/supervision/pojo/dto/report/accountability/ReportAccountabilityStatsResponseDTO.java index b6acb1b..29c88a0 100644 --- a/src/main/java/com/biutag/supervision/pojo/dto/report/accountability/ReportAccountabilityStatsResponseDTO.java +++ b/src/main/java/com/biutag/supervision/pojo/dto/report/accountability/ReportAccountabilityStatsResponseDTO.java @@ -9,6 +9,7 @@ import lombok.Setter; import java.math.BigDecimal; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -48,12 +49,12 @@ public class ReportAccountabilityStatsResponseDTO { private List unitBlames = new ArrayList<>(); @Schema(description = "问责人员TOP3") - private List topPersons; + private List topPersons = new ArrayList<>(); @Schema(description = "个人问责类型次数map") - private Map personalTypeCountMap; + private Map personalTypeCountMap = new HashMap<>(); @Schema(description = "单位问责类型次数map") - private Map unitTypeCountMap; + private Map unitTypeCountMap = new HashMap<>(); } \ No newline at end of file diff --git a/src/main/java/com/biutag/supervision/pojo/dto/report/businessLine/BusinessLineDetailSection.java b/src/main/java/com/biutag/supervision/pojo/dto/report/businessLine/BusinessLineDetailSection.java index 82f53d5..f54a6e4 100644 --- a/src/main/java/com/biutag/supervision/pojo/dto/report/businessLine/BusinessLineDetailSection.java +++ b/src/main/java/com/biutag/supervision/pojo/dto/report/businessLine/BusinessLineDetailSection.java @@ -40,6 +40,9 @@ public class BusinessLineDetailSection { @Schema(description = "查实率") private BigDecimal verifiedRate; + @Schema(description = "问责问题条数") + private int accountabilityNegativeCount; + @Schema(description = "问责总次数") private Integer accountabilityTotal; diff --git a/src/main/java/com/biutag/supervision/pojo/dto/report/businessLine/BusinessLinePersonRankItem.java b/src/main/java/com/biutag/supervision/pojo/dto/report/businessLine/BusinessLinePersonRankItem.java index 3089920..060aae5 100644 --- a/src/main/java/com/biutag/supervision/pojo/dto/report/businessLine/BusinessLinePersonRankItem.java +++ b/src/main/java/com/biutag/supervision/pojo/dto/report/businessLine/BusinessLinePersonRankItem.java @@ -13,6 +13,10 @@ import java.math.BigDecimal; @NoArgsConstructor public class BusinessLinePersonRankItem { + + @Schema(description = "序号") + private Integer orderNo; + @Schema(description = "人员姓名") private String personName; diff --git a/src/main/java/com/biutag/supervision/pojo/dto/report/businessLine/BusinessLineRankItem.java b/src/main/java/com/biutag/supervision/pojo/dto/report/businessLine/BusinessLineRankItem.java index 83a968c..484b005 100644 --- a/src/main/java/com/biutag/supervision/pojo/dto/report/businessLine/BusinessLineRankItem.java +++ b/src/main/java/com/biutag/supervision/pojo/dto/report/businessLine/BusinessLineRankItem.java @@ -15,6 +15,9 @@ import java.math.BigDecimal; @NoArgsConstructor public class BusinessLineRankItem { + @Schema(description = "序号") + private Integer orderNo; + @Schema(description = "名称,如 执法程序问题 / 某某派出所") private String name; diff --git a/src/main/java/com/biutag/supervision/pojo/dto/report/closed/ClosedStatsCalcRequestDTO.java b/src/main/java/com/biutag/supervision/pojo/dto/report/closed/ClosedStatsCalcRequestDTO.java index 619b82c..54d2365 100644 --- a/src/main/java/com/biutag/supervision/pojo/dto/report/closed/ClosedStatsCalcRequestDTO.java +++ b/src/main/java/com/biutag/supervision/pojo/dto/report/closed/ClosedStatsCalcRequestDTO.java @@ -2,7 +2,9 @@ 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.AllArgsConstructor; import lombok.Getter; +import lombok.NoArgsConstructor; import lombok.Setter; import java.util.ArrayList; @@ -11,6 +13,8 @@ import java.util.List; @Getter @Setter @Schema(description = "办结统计计算请求DTO") +@AllArgsConstructor +@NoArgsConstructor public class ClosedStatsCalcRequestDTO { @Schema(description = "待统计的问题数据列表") diff --git a/src/main/java/com/biutag/supervision/pojo/dto/report/problemType/ReportProblemTypeStatsRequestDTO.java b/src/main/java/com/biutag/supervision/pojo/dto/report/problemType/ReportProblemTypeStatsRequestDTO.java new file mode 100644 index 0000000..8e2eb02 --- /dev/null +++ b/src/main/java/com/biutag/supervision/pojo/dto/report/problemType/ReportProblemTypeStatsRequestDTO.java @@ -0,0 +1,24 @@ +package com.biutag.supervision.pojo.dto.report.problemType; + +import com.biutag.supervision.pojo.vo.NegativeQueryVo; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +/** + * @ClassName ReportProblemTypeStatsRequestDTO + * @Description TODO + * @Author shihao + * @Date 2026/3/11 14:39 + */ +@Getter +@Setter +@Schema(description = "问题统计请求DTO") +public class ReportProblemTypeStatsRequestDTO { + + @Schema(description = "原始问题列表") + private List negativeQueryVoList; + +} diff --git a/src/main/java/com/biutag/supervision/pojo/dto/report/problemType/ReportProblemTypeStatsResponseDTO.java b/src/main/java/com/biutag/supervision/pojo/dto/report/problemType/ReportProblemTypeStatsResponseDTO.java new file mode 100644 index 0000000..314d9b4 --- /dev/null +++ b/src/main/java/com/biutag/supervision/pojo/dto/report/problemType/ReportProblemTypeStatsResponseDTO.java @@ -0,0 +1,24 @@ +package com.biutag.supervision.pojo.dto.report.problemType; + +import com.biutag.supervision.pojo.dto.report.businessLine.BusinessLineRankItem; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +/** + * @ClassName ReportProblemTypeStatsResponseDTO + * @Description TODO + * @Author shihao + * @Date 2026/3/11 14:42 + */ +@Getter +@Setter +@Schema(description = "问题统计响应DTO") +public class ReportProblemTypeStatsResponseDTO { + + @Schema(description = "问题类型TOP列表") + private List topProblemTypes; + +} diff --git a/src/main/java/com/biutag/supervision/pojo/param/NegativeQueryParam.java b/src/main/java/com/biutag/supervision/pojo/param/NegativeQueryParam.java index a2a4f7f..4569db6 100644 --- a/src/main/java/com/biutag/supervision/pojo/param/NegativeQueryParam.java +++ b/src/main/java/com/biutag/supervision/pojo/param/NegativeQueryParam.java @@ -2,6 +2,7 @@ package com.biutag.supervision.pojo.param; import cn.hutool.core.bean.BeanUtil; import com.baomidou.mybatisplus.annotation.TableField; +import com.biutag.supervision.pojo.model.UserAuth; import com.fasterxml.jackson.annotation.JsonFormat; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Getter; @@ -116,6 +117,8 @@ public class NegativeQueryParam extends BasePage { @TableField("issuingDepartName") private String issuingDepartName; + private UserAuth currentUser; + public NegativeQueryParam newQueryParam() { NegativeQueryParam target = new NegativeQueryParam(); diff --git a/src/main/java/com/biutag/supervision/pojo/param/PoliceQueryParam.java b/src/main/java/com/biutag/supervision/pojo/param/PoliceQueryParam.java index cccc0a2..0677d5e 100644 --- a/src/main/java/com/biutag/supervision/pojo/param/PoliceQueryParam.java +++ b/src/main/java/com/biutag/supervision/pojo/param/PoliceQueryParam.java @@ -1,5 +1,6 @@ package com.biutag.supervision.pojo.param; +import com.biutag.supervision.pojo.model.UserAuth; import lombok.Getter; import lombok.Setter; @@ -25,4 +26,6 @@ public class PoliceQueryParam extends BasePage { private List idCodes; //启动or private Boolean isOr; + + private UserAuth currentUser; } diff --git a/src/main/java/com/biutag/supervision/pojo/vo/NegativeQueryVo.java b/src/main/java/com/biutag/supervision/pojo/vo/NegativeQueryVo.java index 789d7b5..466e481 100644 --- a/src/main/java/com/biutag/supervision/pojo/vo/NegativeQueryVo.java +++ b/src/main/java/com/biutag/supervision/pojo/vo/NegativeQueryVo.java @@ -209,4 +209,7 @@ public class NegativeQueryVo { @Schema(description = "核查结论code") private String checkStatusCode; + @Schema(description = "二级部门ID") + private String secondInvolveDepartId; + } diff --git a/src/main/java/com/biutag/supervision/repository/supdepart/SupDepartResourceService.java b/src/main/java/com/biutag/supervision/repository/supdepart/SupDepartResourceService.java index b322d84..acf209c 100644 --- a/src/main/java/com/biutag/supervision/repository/supdepart/SupDepartResourceService.java +++ b/src/main/java/com/biutag/supervision/repository/supdepart/SupDepartResourceService.java @@ -123,5 +123,27 @@ public class SupDepartResourceService extends BaseDAO { .collect(Collectors.toSet()); } + /** + * 查询所有二级单位,返回 id -> shortName + */ + public Map getAllSecondDepartNameMap() { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(SupDepart::getLevel, "2"); + + List secondDeparts = supDepartMapper.selectList(wrapper); + if (CollectionUtil.isEmpty(secondDeparts)) { + return Collections.emptyMap(); + } + + return secondDeparts.stream() + .filter(depart -> StrUtil.isNotBlank(depart.getId())) + .collect(Collectors.toMap( + SupDepart::getId, + SupDepart::getShortName, + (a, b) -> a, + LinkedHashMap::new + )); + } + } diff --git a/src/main/java/com/biutag/supervision/service/NegativeQueryService.java b/src/main/java/com/biutag/supervision/service/NegativeQueryService.java index 57cbe4a..bfa3acd 100644 --- a/src/main/java/com/biutag/supervision/service/NegativeQueryService.java +++ b/src/main/java/com/biutag/supervision/service/NegativeQueryService.java @@ -37,7 +37,10 @@ public class NegativeQueryService { private final SupDepartService departService; public Page page(NegativeQueryParam param) { - UserAuth user = UserContextHolder.getCurrentUser(); + UserAuth user = param.getCurrentUser(); + if (user == null) { + user = UserContextHolder.getCurrentUser(); + } if (!AppConstants.USER_TYPE_SUPER.equals(user.getUserType()) && (user.getRoleCodes().isEmpty() || user.getAuthSources().isEmpty() || user.getAuthDepartIds().isEmpty())) { return new Page().setTotal(0).setRecords(new ArrayList<>()); } @@ -212,7 +215,7 @@ public class NegativeQueryService { NegativeQueryVo vo = new NegativeQueryVo(); BeanUtils.copyProperties(item, vo); if (Objects.nonNull(item.getFirstDistributeTime()) && !ProcessingStatusEnum.completed.name().equals(item.getProcessingStatus())) { - vo.setRemainingDuration(TimeUtil.getRemainingDuration(item.getFirstDistributeTime(), item.getMaxSignDuration(), item.getMaxHandleDuration(), item.getExtensionDays(), item.getFlowKey())); +// vo.setRemainingDuration(TimeUtil.getRemainingDuration(item.getFirstDistributeTime(), item.getMaxSignDuration(), item.getMaxHandleDuration(), item.getExtensionDays(), item.getFlowKey())); } return vo; }).toList(); diff --git a/src/main/java/com/biutag/supervision/service/NegativeTaskService.java b/src/main/java/com/biutag/supervision/service/NegativeTaskService.java index 1f560b2..1ee8dbb 100644 --- a/src/main/java/com/biutag/supervision/service/NegativeTaskService.java +++ b/src/main/java/com/biutag/supervision/service/NegativeTaskService.java @@ -18,6 +18,7 @@ import com.biutag.supervision.pojo.dto.*; import com.biutag.supervision.pojo.entity.*; import com.biutag.supervision.pojo.model.ModelClueTaskDepartModel; import com.biutag.supervision.pojo.model.UserAuth; +import com.biutag.supervision.pojo.param.NegativeQueryParam; import com.biutag.supervision.pojo.param.NegativeTaskQueryParam; import com.biutag.supervision.pojo.vo.ExportNegativeBlameLeaderVo; import com.biutag.supervision.pojo.vo.ExportNegativeBlameVo; @@ -33,6 +34,8 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.time.LocalDate; import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; import java.util.*; import java.util.stream.Collectors; @@ -87,6 +90,32 @@ public class NegativeTaskService extends ServiceImpl data, String negativeTaskId) { List suspectProblem = dictDataService.listByDictType("suspectProblem"); diff --git a/src/main/java/com/biutag/supervision/service/SupPoliceService.java b/src/main/java/com/biutag/supervision/service/SupPoliceService.java index 806d5e1..9b8285d 100644 --- a/src/main/java/com/biutag/supervision/service/SupPoliceService.java +++ b/src/main/java/com/biutag/supervision/service/SupPoliceService.java @@ -33,7 +33,10 @@ public class SupPoliceService extends ServiceImpl { public Page page(PoliceQueryParam param) { QueryWrapper queryWrapper = new QueryWrapper<>(); - UserAuth user = UserContextHolder.getCurrentUser(); + UserAuth user = param.getCurrentUser(); + if (user == null) { + user = UserContextHolder.getCurrentUser(); + } // 权限 if (!AppConstants.USER_TYPE_SUPER.equals(user.getUserType()) && !user.getRoleCodes().contains(RoleCodeEnum.FIRST_ADMIN.getCode())) { if (user.getAuthDepartIds().isEmpty() || user.getRoleCodes().isEmpty()) { diff --git a/src/main/java/com/biutag/supervision/service/negativeReport/NegativeReportService.java b/src/main/java/com/biutag/supervision/service/negativeReport/NegativeReportService.java index dc83acc..e302ce8 100644 --- a/src/main/java/com/biutag/supervision/service/negativeReport/NegativeReportService.java +++ b/src/main/java/com/biutag/supervision/service/negativeReport/NegativeReportService.java @@ -1,9 +1,12 @@ package com.biutag.supervision.service.negativeReport; +import com.biutag.supervision.pojo.model.UserAuth; import com.biutag.supervision.pojo.param.NegativeQueryParam; import jakarta.servlet.http.HttpServletResponse; public interface NegativeReportService { void generateReport(NegativeQueryParam request, HttpServletResponse response); + + void exportReport(NegativeQueryParam request, String id); } diff --git a/src/main/java/com/biutag/supervision/service/negativeReport/NegativeReportServiceImpl.java b/src/main/java/com/biutag/supervision/service/negativeReport/NegativeReportServiceImpl.java index 5809329..0db55ab 100644 --- a/src/main/java/com/biutag/supervision/service/negativeReport/NegativeReportServiceImpl.java +++ b/src/main/java/com/biutag/supervision/service/negativeReport/NegativeReportServiceImpl.java @@ -1,7 +1,12 @@ package com.biutag.supervision.service.negativeReport; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.biutag.supervision.constants.enums.NegativeTaskStatusEnum; import com.biutag.supervision.pojo.dto.report.ReportViewModel; +import com.biutag.supervision.pojo.entity.NegativeTask; import com.biutag.supervision.pojo.param.NegativeQueryParam; +import com.biutag.supervision.service.FileService; +import com.biutag.supervision.service.NegativeTaskService; import com.biutag.supervision.service.report.ReportDataService; import com.deepoove.poi.XWPFTemplate; import io.swagger.v3.oas.annotations.media.Schema; @@ -10,12 +15,16 @@ import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.core.io.ClassPathResource; +import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; +import java.time.LocalDateTime; /** * @ClassName NegativeReportServiceImpl @@ -32,49 +41,136 @@ public class NegativeReportServiceImpl implements NegativeReportService { private final ReportDataService reportDataService; + private final NegativeTaskService negativeTaskService; + + private final FileService fileService; + public void generateReport(NegativeQueryParam request, HttpServletResponse response) { request.setCurrent(1); request.setSize(1000000); - // 你要返回的模板路径 + log.info("开始生成报告, crtTime={}, request={}", request.getCrtTime(), request); + String templatePath = "static/templates/督审一体化平台研判分析报告.docx"; ClassPathResource resource = new ClassPathResource(templatePath); + if (!resource.exists()) { - // 资源不存在,返回 404 + log.warn("报告模板不存在, templatePath={}", templatePath); response.setStatus(HttpServletResponse.SC_NOT_FOUND); response.setContentType("application/json;charset=UTF-8"); try { response.getWriter().write("{\"message\":\"模板文件不存在: " + templatePath + "\"}"); - } catch (IOException ignored) {} + } catch (IOException ignored) { + } return; } - // 1) 准备模板变量(含图表) - ReportViewModel vm = reportDataService.buildViewModel(request); - // 2) 响应头(docx 正确类型) + String downloadName = "督审一体化平台研判分析报告.docx"; String encoded = URLEncoder.encode(downloadName, StandardCharsets.UTF_8).replaceAll("\\+", "%20"); String contentDisposition = "attachment; filename=\"" + encoded + "\"; filename*=UTF-8''" + encoded; - response.reset(); - response.setCharacterEncoding("UTF-8"); - response.setHeader("Content-Disposition", contentDisposition); - response.setHeader("Access-Control-Expose-Headers", "Content-Disposition"); - response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document"); - response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); - response.setHeader("Pragma", "no-cache"); - response.setDateHeader("Expires", 0); - - // 3) 关键:compile + render + write(不要 StreamUtils.copy) try (InputStream in = resource.getInputStream(); - XWPFTemplate template = XWPFTemplate.compile(in).render(vm); - ServletOutputStream out = response.getOutputStream()) { + XWPFTemplate template = XWPFTemplate.compile(in); + ByteArrayOutputStream bos = new ByteArrayOutputStream()) { + + ReportViewModel vm = reportDataService.buildViewModel(request); + + template.render(vm); + + template.write(bos); + byte[] bytes = bos.toByteArray(); + log.info("报告生成完成,准备输出, size={} bytes", bytes.length); + + response.reset(); + response.setCharacterEncoding("UTF-8"); + response.setHeader("Content-Disposition", contentDisposition); + response.setHeader("Access-Control-Expose-Headers", "Content-Disposition"); + response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document"); + response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); + response.setHeader("Pragma", "no-cache"); + response.setDateHeader("Expires", 0); + response.setContentLengthLong(bytes.length); - template.write(out); + ServletOutputStream out = response.getOutputStream(); + out.write(bytes); out.flush(); + log.info("报告下载成功, fileName={}, size={} bytes", downloadName, bytes.length); } catch (Exception e) { log.error("生成/下载报告失败", e); - // 如果你们有统一异常处理,可以直接抛出去 - // throw new RuntimeException("生成报告失败", e); + if (!response.isCommitted()) { + try { + response.reset(); + response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + response.setContentType("application/json;charset=UTF-8"); + response.getWriter().write("{\"message\":\"生成报告失败\"}"); + } catch (IOException ioException) { + log.error("回写失败响应异常", ioException); + } + } + } + } + + + + @Async + public void exportReport(NegativeQueryParam request, String negativeTaskId) { + try { + byte[] bytes = buildReportBytes(request); + + String filePath = fileService.upload( + new ByteArrayInputStream(bytes), + bytes.length, + ".docx" + ); + + negativeTaskService.update( + new LambdaUpdateWrapper() + .eq(NegativeTask::getId, negativeTaskId) + .set(NegativeTask::getStatus, NegativeTaskStatusEnum.SUCCESS.getValue()) + .set(NegativeTask::getFilePath, filePath) + .set(NegativeTask::getUpdTime, LocalDateTime.now()) + ); + + log.info("报告任务成功, taskId={}, filePath={}", negativeTaskId, filePath); + } catch (Exception e) { + log.error("报告任务失败, taskId={}", negativeTaskId, e); + + negativeTaskService.update( + new LambdaUpdateWrapper() + .eq(NegativeTask::getId, negativeTaskId) + .set(NegativeTask::getStatus, NegativeTaskStatusEnum.FAIL.getValue()) + .set(NegativeTask::getUpdTime, LocalDateTime.now()) + ); + } + } + + + public byte[] buildReportBytes(NegativeQueryParam request) { + request.setCurrent(1); + request.setSize(1000000); + log.info("开始生成报告, crtTime={}, request={}", request.getCrtTime(), request); + + String templatePath = "static/templates/督审一体化平台研判分析报告.docx"; + ClassPathResource resource = new ClassPathResource(templatePath); + + if (!resource.exists()) { + throw new RuntimeException("报告模板不存在: " + templatePath); + } + + try (InputStream in = resource.getInputStream(); + XWPFTemplate template = XWPFTemplate.compile(in); + ByteArrayOutputStream bos = new ByteArrayOutputStream()) { + + ReportViewModel vm = reportDataService.buildViewModel(request); + template.render(vm); + + template.write(bos); + byte[] bytes = bos.toByteArray(); + + log.info("报告生成完成, size={} bytes", bytes.length); + return bytes; + } catch (Exception e) { + throw new RuntimeException("生成报告失败", e); } } } diff --git a/src/main/java/com/biutag/supervision/service/report/ReportProblemTypeStatsService.java b/src/main/java/com/biutag/supervision/service/report/ReportProblemTypeStatsService.java new file mode 100644 index 0000000..d4856cf --- /dev/null +++ b/src/main/java/com/biutag/supervision/service/report/ReportProblemTypeStatsService.java @@ -0,0 +1,14 @@ +package com.biutag.supervision.service.report; + +import com.biutag.supervision.pojo.dto.report.problemType.ReportProblemTypeStatsRequestDTO; +import com.biutag.supervision.pojo.dto.report.problemType.ReportProblemTypeStatsResponseDTO; + +public interface ReportProblemTypeStatsService { + + /** + * 计算问题类型相关 + * @param reportProblemTypeStatsRequestDTO + * @return + */ + ReportProblemTypeStatsResponseDTO calculateProblemTypeStats(ReportProblemTypeStatsRequestDTO reportProblemTypeStatsRequestDTO); +} diff --git a/src/main/java/com/biutag/supervision/service/report/impl/ReportAccountabilitySectionServiceImpl.java b/src/main/java/com/biutag/supervision/service/report/impl/ReportAccountabilitySectionServiceImpl.java index a3b4ea1..206ea94 100644 --- a/src/main/java/com/biutag/supervision/service/report/impl/ReportAccountabilitySectionServiceImpl.java +++ b/src/main/java/com/biutag/supervision/service/report/impl/ReportAccountabilitySectionServiceImpl.java @@ -42,6 +42,7 @@ public class ReportAccountabilitySectionServiceImpl implements ReportAccountabil List currentList = negativeQueryService.page(currentQueryParam).getRecords(); ReportAccountabilityStatsRequestDTO statsRequestDTO = new ReportAccountabilityStatsRequestDTO(); statsRequestDTO.setNegativeQueryVoList(currentList); + statsRequestDTO.setCurrentUser(currentQueryParam.getCurrentUser()); ReportAccountabilityStatsResponseDTO statsResponseDTO = reportAccountabilityStatsService.calculateAccountabilityStats(statsRequestDTO); AccountabilityOverviewSection section = new AccountabilityOverviewSection(); section.setPeriodStart(periodStart); @@ -65,6 +66,7 @@ public class ReportAccountabilitySectionServiceImpl implements ReportAccountabil List currentList = negativeQueryService.page(currentQueryParam).getRecords(); ReportAccountabilityStatsRequestDTO statsRequestDTO = new ReportAccountabilityStatsRequestDTO(); statsRequestDTO.setNegativeQueryVoList(currentList); + statsRequestDTO.setCurrentUser(currentQueryParam.getCurrentUser()); ReportAccountabilityStatsResponseDTO statsResponseDTO = reportAccountabilityStatsService.calculateAccountabilityStats(statsRequestDTO); AccountabilityUnitSection section = new AccountabilityUnitSection(); section.setTotalCount(statsResponseDTO.getUnit()); @@ -102,6 +104,7 @@ public class ReportAccountabilitySectionServiceImpl implements ReportAccountabil List currentList = negativeQueryService.page(currentQueryParam).getRecords(); ReportAccountabilityStatsRequestDTO statsRequestDTO = new ReportAccountabilityStatsRequestDTO(); statsRequestDTO.setNegativeQueryVoList(currentList); + statsRequestDTO.setCurrentUser(currentQueryParam.getCurrentUser()); ReportAccountabilityStatsResponseDTO statsResponseDTO = reportAccountabilityStatsService.calculateAccountabilityStats(statsRequestDTO); AccountabilityPersonalSection section = new AccountabilityPersonalSection(); section.setTotalCount(statsResponseDTO.getPersonal()); diff --git a/src/main/java/com/biutag/supervision/service/report/impl/ReportAccountabilityStatsServiceImpl.java b/src/main/java/com/biutag/supervision/service/report/impl/ReportAccountabilityStatsServiceImpl.java index 078509e..b769aff 100644 --- a/src/main/java/com/biutag/supervision/service/report/impl/ReportAccountabilityStatsServiceImpl.java +++ b/src/main/java/com/biutag/supervision/service/report/impl/ReportAccountabilityStatsServiceImpl.java @@ -27,6 +27,7 @@ import org.springframework.stereotype.Service; import java.math.BigDecimal; import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; @Service @@ -152,7 +153,7 @@ public class ReportAccountabilityStatsServiceImpl implements ReportAccountabilit ); // 11. 计算问责人员TOP3 - List topPersons = calculateTopPersons(personalBlames); + List topPersons = calculateTopPersons(personalBlames, requestDTO); // 问责率 responseDTO.setAccountabilityNegativeRate(accountabilityNegativeRate); @@ -211,7 +212,7 @@ public class ReportAccountabilityStatsServiceImpl implements ReportAccountabilit /** * 计算问责人员TOP3 */ - private List calculateTopPersons(List personalBlames) { + private List calculateTopPersons(List personalBlames, ReportAccountabilityStatsRequestDTO requestDTO) { if (CollUtil.isEmpty(personalBlames)) { return Collections.emptyList(); } @@ -223,6 +224,7 @@ public class ReportAccountabilityStatsServiceImpl implements ReportAccountabilit (v1, v2) -> v1, LinkedHashMap::new )); + AtomicInteger index = new AtomicInteger(1); return personalBlames.stream() .collect(Collectors.groupingBy( blame -> String.join("|", @@ -238,6 +240,7 @@ public class ReportAccountabilityStatsServiceImpl implements ReportAccountabilit PoliceQueryParam policeQueryParam = new PoliceQueryParam(); policeQueryParam.setEmpNo(blame.getBlameEmpNo()); + policeQueryParam.setCurrentUser(requestDTO.getCurrentUser()); List records = supPoliceService.page(policeQueryParam).getRecords(); String unitName = "未知"; String identityName = "未知"; @@ -247,6 +250,7 @@ public class ReportAccountabilityStatsServiceImpl implements ReportAccountabilit } return BusinessLinePersonRankItem.builder() + .orderNo(index.getAndIncrement()) .personName(blame.getBlameName()) .unitName(unitName) .identityName(identityName) diff --git a/src/main/java/com/biutag/supervision/service/report/impl/ReportBusinessLineSectionServiceImpl.java b/src/main/java/com/biutag/supervision/service/report/impl/ReportBusinessLineSectionServiceImpl.java index 679d311..8e94feb 100644 --- a/src/main/java/com/biutag/supervision/service/report/impl/ReportBusinessLineSectionServiceImpl.java +++ b/src/main/java/com/biutag/supervision/service/report/impl/ReportBusinessLineSectionServiceImpl.java @@ -11,18 +11,18 @@ 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.dto.report.problemType.ReportProblemTypeStatsRequestDTO; +import com.biutag.supervision.pojo.dto.report.problemType.ReportProblemTypeStatsResponseDTO; import com.biutag.supervision.pojo.entity.NegativeBlame; import com.biutag.supervision.pojo.entity.NegativeProblemRelation; 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.repository.supdepart.SupDepartResourceService; import com.biutag.supervision.service.NegativeProblemRelationService; 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.service.report.*; import com.biutag.supervision.util.ChartRenderUtil; import com.biutag.supervision.util.ReportTrendUtil; import com.deepoove.poi.data.PictureRenderData; @@ -31,6 +31,7 @@ import com.deepoove.poi.data.Pictures; import jakarta.annotation.Resource; import lombok.Data; import lombok.extern.slf4j.Slf4j; +import org.apache.ibatis.annotations.One; import org.springframework.stereotype.Service; import java.math.BigDecimal; @@ -55,8 +56,12 @@ public class ReportBusinessLineSectionServiceImpl implements ReportBusinessLineS @Resource private ReportAccountabilityStatsService reportAccountabilityStatsService; + + @Resource - private NegativeProblemRelationService negativeProblemRelationService; + private ReportProblemTypeStatsService reportProblemTypeStatsService; + + @Override public BusinessLineOverviewSection buildBusinessLineOverviewSection(ReportBusinessLineSectionRequestDTO requestDTO) { @@ -190,69 +195,41 @@ public class ReportBusinessLineSectionServiceImpl implements ReportBusinessLineS // 问责统计 ReportAccountabilityStatsRequestDTO statsRequestDTO = new ReportAccountabilityStatsRequestDTO(); statsRequestDTO.setNegativeQueryVoList(lineList); + statsRequestDTO.setCurrentUser(request.getCurrentUser()); ReportAccountabilityStatsResponseDTO accountabilityStats = reportAccountabilityStatsService.calculateAccountabilityStats(statsRequestDTO); + section.setAccountabilityNegativeCount(accountabilityStats.getAccountabilityNegativeCount()); section.setAccountabilityTotal(accountabilityStats.getTotal()); section.setAccountabilityRate(accountabilityStats.getAccountabilityNegativeRate()); section.setPersonalAccountabilityCount(accountabilityStats.getPersonal()); section.setDepartmentAccountabilityCount(accountabilityStats.getUnit()); //问题类型 TOP3 -// Set collect = lineList.stream().map(NegativeQueryVo::getId).collect(Collectors.toSet()); -// List negativeProblemRelations = negativeProblemRelationService.list(collect); -// // 1. 分组统计,value为所有数据的List -// Map> groupedMap = negativeProblemRelations.stream() -// .collect(Collectors.groupingBy( -// relation -> relation.getOneLevelContent() + -// relation.getTwoLevelContent() + -// relation.getThreeLevelContent() -// )); -// // 2. 按List大小(数量)降序排序 -// List>> sortedList = groupedMap.entrySet().stream() -// .sorted((entry1, entry2) -> Integer.compare(entry2.getValue().size(), entry1.getValue().size())) -// .collect(Collectors.toList()); -// section.setTopProblemTypes(topProblemTypes); - + ReportProblemTypeStatsRequestDTO reportProblemTypeStatsRequestDTO = new ReportProblemTypeStatsRequestDTO(); + reportProblemTypeStatsRequestDTO.setNegativeQueryVoList(lineList); + ReportProblemTypeStatsResponseDTO reportProblemTypeStatsResponseDTO = reportProblemTypeStatsService.calculateProblemTypeStats(reportProblemTypeStatsRequestDTO); + section.setTopProblemTypes(reportProblemTypeStatsResponseDTO.getTopProblemTypes()); // 重点单位 TOP3 section.setTopUnits(closedStats.getTopUnits().stream().limit(3).toList()); // 重点人员 TOP3 section.setTopPersons(accountabilityStats.getTopPersons().stream().limit(3).toList()); - - - 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)); + BigDecimal currentVerifiedRate = closedStats.getVerifiedRate(); + ClosedStatsCalcRequestDTO momRequestDTO = new ClosedStatsCalcRequestDTO(); + momRequestDTO.setNegativeQueryVoList(momGroup.getOrDefault(fatherBusinessLineName, Collections.emptyList())); + ClosedStatsCalcResponseDTO momClosedStats = reportClosedStatsService.calculateClosedStats(momRequestDTO); + ClosedStatsCalcRequestDTO yoyRequestDTO = new ClosedStatsCalcRequestDTO(); + yoyRequestDTO.setNegativeQueryVoList(yoyGroup.getOrDefault(fatherBusinessLineName, Collections.emptyList())); + ClosedStatsCalcResponseDTO yoyClosedStats = reportClosedStatsService.calculateClosedStats(yoyRequestDTO); + BigDecimal momVerifiedRate = momClosedStats.getVerifiedRate(); + BigDecimal yoyVerifiedRate = yoyClosedStats.getVerifiedRate(); + section.setMomRate(ReportTrendUtil.calcDiff(currentVerifiedRate, momVerifiedRate)); + section.setMomTrend(ReportTrendUtil.calcTrend(currentVerifiedRate, momVerifiedRate)); + section.setYoyRate(ReportTrendUtil.calcDiff(currentVerifiedRate, yoyVerifiedRate)); + section.setYoyTrend(ReportTrendUtil.calcTrend(currentVerifiedRate, yoyVerifiedRate)); result.add(section); } log.info("条线明细计算结束======================================================================================="); 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,解析父级业务条线名称 @@ -270,27 +247,6 @@ public class ReportBusinessLineSectionServiceImpl implements ReportBusinessLineS : StrUtil.blankToDefault(child.getLabel(), "未归类"); } - /** - * 根据问题来源ID获取当前节点名称 - */ - private String leafProblemLabel(String problemSourceCode, Map idMap) { - if (StrUtil.isBlank(problemSourceCode)) { - return "未归类"; - } - DictProblemSourceTree node = idMap.get(problemSourceCode); - if (node == null || StrUtil.isBlank(node.getLabel())) { - return "未归类"; - } - return node.getLabel(); - } - - /** - * 安全计算占比字符串 - */ - private String safePercentString(int numerator, int denominator) { - return ReportTrendUtil.percent(numerator, denominator).toPlainString(); - } - /** * 阿拉伯数字序号转中文序号 */ @@ -302,108 +258,8 @@ public class ReportBusinessLineSectionServiceImpl implements ReportBusinessLineS return String.valueOf(index); } - /** - * 构建重点人员TOP3 - */ - private List buildTopPersons(List lineList, int totalIssued) { - Map personAggMap = new LinkedHashMap<>(); - - for (NegativeQueryVo vo : lineList) { - String personName = readStringByGetter(vo, - "getInvolvePersonName", - "getPersonName", - "getPoliceName", - "getHandlePersonName", - "getName"); - - if (StrUtil.isBlank(personName)) { - continue; - } - - String unitName = firstNotBlank( - readStringByGetter(vo, "getInvolveDepartName"), - readStringByGetter(vo, "getUnitName"), - readStringByGetter(vo, "getDepartName"), - "未知单位" - ); - - String identityName = firstNotBlank( - readStringByGetter(vo, "getIdentityName"), - readStringByGetter(vo, "getInvolveIdentityName"), - readStringByGetter(vo, "getPoliceTypeName"), - readStringByGetter(vo, "getUserTypeName"), - "未知身份" - ); - - String key = personName + "||" + unitName + "||" + identityName; - PersonAgg agg = personAggMap.computeIfAbsent(key, k -> { - PersonAgg x = new PersonAgg(); - x.setPersonName(personName); - x.setUnitName(unitName); - x.setIdentityName(identityName); - x.setCount(0); - return x; - }); - agg.setCount(agg.getCount() + 1); - } - - return personAggMap.values().stream() - .sorted(Comparator.comparing(PersonAgg::getCount).reversed()) - .limit(3) - .map(one -> buildPersonRankItem( - one.getPersonName(), - one.getUnitName(), - one.getIdentityName(), - one.getCount(), - safePercentString(one.getCount(), totalIssued) - )) - .toList(); - } - - /** - * 通过反射按顺序读取字符串属性 - */ - private String readStringByGetter(Object target, String... getterNames) { - if (target == null || getterNames == null) { - return null; - } - for (String getterName : getterNames) { - try { - java.lang.reflect.Method method = target.getClass().getMethod(getterName); - Object value = method.invoke(target); - if (value instanceof String str && StrUtil.isNotBlank(str)) { - return str; - } - } catch (Exception ignored) { - } - } - return null; - } - - /** - * 返回第一个非空白字符串 - */ - private String firstNotBlank(String... values) { - if (values == null) { - return null; - } - for (String value : values) { - if (StrUtil.isNotBlank(value)) { - return value; - } - } - return null; - } - - /** - * 重点人员聚合对象 - */ - @Data - private static class PersonAgg { - private String personName; - private String unitName; - private String identityName; - private int count; + private BigDecimal defaultZero(BigDecimal value) { + return value == null ? BigDecimal.ZERO : value; } } diff --git a/src/main/java/com/biutag/supervision/service/report/impl/ReportClosedStatsServiceImpl.java b/src/main/java/com/biutag/supervision/service/report/impl/ReportClosedStatsServiceImpl.java index 3990754..8254f41 100644 --- a/src/main/java/com/biutag/supervision/service/report/impl/ReportClosedStatsServiceImpl.java +++ b/src/main/java/com/biutag/supervision/service/report/impl/ReportClosedStatsServiceImpl.java @@ -7,18 +7,25 @@ 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.vo.NegativeQueryVo; +import com.biutag.supervision.repository.supdepart.SupDepartResourceService; import com.biutag.supervision.service.report.ReportClosedStatsService; import com.biutag.supervision.util.ReportTrendUtil; +import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import java.math.BigDecimal; import java.util.Collections; import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; @Service public class ReportClosedStatsServiceImpl implements ReportClosedStatsService { + @Resource + private SupDepartResourceService supDepartResourceService; + @Override public ClosedStatsCalcResponseDTO calculateClosedStats(ClosedStatsCalcRequestDTO requestDTO) { @@ -50,17 +57,32 @@ public class ReportClosedStatsServiceImpl implements ReportClosedStatsService { // 4. 计算重点单位 TOP3(已办结且部门名称不为空) + Map allSecondDepartNameMap = supDepartResourceService.getAllSecondDepartNameMap(); + AtomicInteger index = new AtomicInteger(1); List topUnits = closedList.stream() - .filter(one -> StrUtil.isNotBlank(one.getInvolveDepartName())) - .collect(Collectors.groupingBy(NegativeQueryVo::getInvolveDepartName, Collectors.counting())) + .filter(one -> StrUtil.isNotBlank(one.getSecondInvolveDepartId())) + .collect(Collectors.groupingBy(NegativeQueryVo::getSecondInvolveDepartId, Collectors.counting())) .entrySet().stream() + .filter(e -> StrUtil.isNotBlank(allSecondDepartNameMap.get(e.getKey()))) .sorted((a, b) -> Long.compare(b.getValue(), a.getValue())) .map(e -> new BusinessLineRankItem( - e.getKey(), + index.getAndIncrement(), + allSecondDepartNameMap.get(e.getKey()), e.getValue().intValue(), ReportTrendUtil.percent(e.getValue().intValue(), closedList.size()) )) .toList(); +// List topUnits = closedList.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())) +// .map(e -> new BusinessLineRankItem( +// e.getKey(), +// e.getValue().intValue(), +// ReportTrendUtil.percent(e.getValue().intValue(), closedList.size()) +// )) +// .toList(); ClosedStatsCalcResponseDTO responseDTO = new ClosedStatsCalcResponseDTO(); responseDTO.setClosedCount(closedList.size()); diff --git a/src/main/java/com/biutag/supervision/service/report/impl/ReportDataServiceImpl.java b/src/main/java/com/biutag/supervision/service/report/impl/ReportDataServiceImpl.java index 091ef5f..40d166d 100644 --- a/src/main/java/com/biutag/supervision/service/report/impl/ReportDataServiceImpl.java +++ b/src/main/java/com/biutag/supervision/service/report/impl/ReportDataServiceImpl.java @@ -28,18 +28,24 @@ public class ReportDataServiceImpl implements ReportDataService { // 仅负责报表各模块编排,不承载具体统计逻辑 @Override public ReportViewModel buildViewModel(NegativeQueryParam request) { + + long start = System.currentTimeMillis(); + 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); // 总体概览 + long overviewStart = System.currentTimeMillis(); ReportOverviewSectionRequestDTO overviewRequestDTO = new ReportOverviewSectionRequestDTO(); overviewRequestDTO.setNegativeQueryParam(request); overviewRequestDTO.setPeriodStart(periodStart); overviewRequestDTO.setPeriodEnd(periodEnd); vm.setOverviewSection(reportOverviewSectionService.buildOverviewSection(overviewRequestDTO)); + log.info("总体概览耗时:{} ms", System.currentTimeMillis() - overviewStart); // 业务条线 + long businessStart = System.currentTimeMillis(); ReportBusinessLineSectionRequestDTO businessLineRequestDTO = new ReportBusinessLineSectionRequestDTO(); businessLineRequestDTO.setNegativeQueryParam(request); businessLineRequestDTO.setPeriodStart(periodStart); @@ -48,13 +54,17 @@ public class ReportDataServiceImpl implements ReportDataService { vm.setBusinessLineOverviewSection(reportBusinessLineSectionService.buildBusinessLineOverviewSection(businessLineRequestDTO)); // 条线明细 vm.setBusinessLineDetailSections(reportBusinessLineSectionService.buildBusinessLineDetailSections(businessLineRequestDTO)); + log.info("业务条线耗时:{} ms", System.currentTimeMillis() - businessStart); // 单位查处情况总览 + long unitStart = System.currentTimeMillis(); ReportUnitInvestigationSectionRequestDTO unitRequestDTO = new ReportUnitInvestigationSectionRequestDTO(); unitRequestDTO.setNegativeQueryParam(request); unitRequestDTO.setPeriodStart(periodStart); unitRequestDTO.setPeriodEnd(periodEnd); vm.setUnitInvestigationOverviewSection(reportUnitInvestigationSectionService.buildUnitInvestigationOverviewSection(unitRequestDTO)); + log.info("单位查处耗时:{} ms", System.currentTimeMillis() - unitStart); // 问责情况 + long accountabilityStart = System.currentTimeMillis(); ReportAccountabilitySectionRequestDTO accountabilityRequestDTO = new ReportAccountabilitySectionRequestDTO(); accountabilityRequestDTO.setNegativeQueryParam(request); accountabilityRequestDTO.setPeriodStart(periodStart); @@ -62,6 +72,8 @@ public class ReportDataServiceImpl implements ReportDataService { vm.setAccountabilityOverviewSection(reportAccountabilitySectionService.buildAccountabilityOverviewSection(accountabilityRequestDTO)); vm.setAccountabilityUnitDetailSection(reportAccountabilitySectionService.buildAccountabilityUnitDetailSection(accountabilityRequestDTO)); vm.setAccountabilityPersonOverviewSection(reportAccountabilitySectionService.buildAccountabilityPersonalOverviewSection(accountabilityRequestDTO)); + log.info("问责统计耗时:{} ms", System.currentTimeMillis() - accountabilityStart); + log.info("报表整体生成耗时:{} ms", System.currentTimeMillis() - start); // System.out.println(1/0); return vm; } diff --git a/src/main/java/com/biutag/supervision/service/report/impl/ReportOverviewSectionServiceImpl.java b/src/main/java/com/biutag/supervision/service/report/impl/ReportOverviewSectionServiceImpl.java index b286146..2670dce 100644 --- a/src/main/java/com/biutag/supervision/service/report/impl/ReportOverviewSectionServiceImpl.java +++ b/src/main/java/com/biutag/supervision/service/report/impl/ReportOverviewSectionServiceImpl.java @@ -1,5 +1,6 @@ package com.biutag.supervision.service.report.impl; +import cn.hutool.core.util.StrUtil; 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; @@ -10,6 +11,7 @@ import com.biutag.supervision.pojo.dto.report.closed.ClosedStatsCalcResponseDTO; 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.repository.supdepart.SupDepartResourceService; import com.biutag.supervision.service.report.ReportAccountabilityStatsService; import com.biutag.supervision.service.report.ReportBaseQueryService; import com.biutag.supervision.service.report.ReportClosedStatsService; @@ -55,6 +57,9 @@ public class ReportOverviewSectionServiceImpl implements ReportOverviewSectionSe @Resource private ReportClosedStatsService reportClosedStatsService; + @Resource + private SupDepartResourceService supDepartResourceService; + /** * 构建总体概览数据 * @@ -96,9 +101,9 @@ public class ReportOverviewSectionServiceImpl implements ReportOverviewSectionSe ClosedStatsCalcResponseDTO yoyClosedStats = calculateClosedStats(yoyList); // 4. 问责统计 - ReportAccountabilityStatsResponseDTO currentAccountabilityStats = calculateAccountabilityStats(currentList); - ReportAccountabilityStatsResponseDTO momAccountabilityStats = calculateAccountabilityStats(momList); - ReportAccountabilityStatsResponseDTO yoyAccountabilityStats = calculateAccountabilityStats(yoyList); + ReportAccountabilityStatsResponseDTO currentAccountabilityStats = calculateAccountabilityStats(currentList,requestDTO); + ReportAccountabilityStatsResponseDTO momAccountabilityStats = calculateAccountabilityStats(momList, requestDTO); + ReportAccountabilityStatsResponseDTO yoyAccountabilityStats = calculateAccountabilityStats(yoyList, requestDTO); // 5. 环比/同比基础值 int currentTotal = currentList.size(); @@ -139,36 +144,67 @@ public class ReportOverviewSectionServiceImpl implements ReportOverviewSectionSe overviewSection.setVerifiedAndBasicallyVerifiedCount(currentClosedStats.getVerifiedCount() + currentClosedStats.getBasicallyVerifiedCount()); // 查实率 + log.info("设置查实率前原始数据,currentVerifiedRate={}, momVerifiedRate={}, yoyVerifiedRate={}", currentVerifiedRate, momVerifiedRate, yoyVerifiedRate); overviewSection.setVerifiedRate(currentVerifiedRate); - overviewSection.setVerifiedMomRate(ReportTrendUtil.calcRate(currentVerifiedRate, momVerifiedRate)); + overviewSection.setVerifiedMomRate(ReportTrendUtil.calcAbsDiff(currentVerifiedRate, momVerifiedRate)); overviewSection.setVerifiedMomTrend(ReportTrendUtil.calcTrend(currentVerifiedRate, momVerifiedRate)); - overviewSection.setVerifiedYoyRate(ReportTrendUtil.calcRate(currentVerifiedRate, yoyVerifiedRate)); + overviewSection.setVerifiedYoyRate(ReportTrendUtil.calcAbsDiff(currentVerifiedRate, yoyVerifiedRate)); overviewSection.setVerifiedYoyTrend(ReportTrendUtil.calcTrend(currentVerifiedRate, yoyVerifiedRate)); // 问责统计 + log.info("设置问责统计前原始数据,accountabilityProblemTotal={}, currentAccountabilityTotal={}, personalAccountability={}, unitAccountability={}, momAccountabilityTotal={}, yoyAccountabilityTotal={}", + currentAccountabilityStats.getAccountabilityNegativeCount(), + currentAccountabilityTotal, + currentAccountabilityStats.getPersonal(), + currentAccountabilityStats.getUnit(), + momAccountabilityTotal, + yoyAccountabilityTotal); overviewSection.setAccountabilityProblemTotal(currentAccountabilityStats.getAccountabilityNegativeCount()); + overviewSection.setAccountabilityNegativeRate(currentAccountabilityStats.getAccountabilityNegativeRate()); overviewSection.setAccountabilityTotal(currentAccountabilityTotal); overviewSection.setPersonalAccountability(currentAccountabilityStats.getPersonal()); overviewSection.setUnitAccountability(currentAccountabilityStats.getUnit()); - overviewSection.setAccountabilityMomRate(ReportTrendUtil.calcRate(currentAccountabilityTotal, momAccountabilityTotal)); + overviewSection.setAccountabilityMomRate(ReportTrendUtil.calcAbsDiff(currentAccountabilityStats.getAccountabilityNegativeRate(), momAccountabilityStats.getAccountabilityNegativeRate())); overviewSection.setAccountabilityMomTrend(ReportTrendUtil.calcTrend(currentAccountabilityTotal, momAccountabilityTotal)); - overviewSection.setAccountabilityYoyRate(ReportTrendUtil.calcRate(currentAccountabilityTotal, yoyAccountabilityTotal)); + overviewSection.setAccountabilityYoyRate(ReportTrendUtil.calcAbsDiff(momAccountabilityStats.getAccountabilityNegativeRate(), yoyAccountabilityStats.getAccountabilityNegativeRate())); overviewSection.setAccountabilityYoyTrend(ReportTrendUtil.calcTrend(currentAccountabilityTotal, yoyAccountabilityTotal)); // 集中问题(基于属实 + 基本属实) - Map.Entry problemEntry = ReportTrendUtil.topBy( + List> problemRankList = ReportTrendUtil.rankBy( NegativeQueryVo::getProblemSources, currentClosedStats.getVerifiedList(), currentClosedStats.getBasicallyVerifiedList() ); - overviewSection.setTopProblemProject(problemEntry != null ? problemEntry.getKey() : null); + List> problemRankListTop3 = problemRankList.stream().limit(3).toList(); + String topProblemProject = ""; + for (Map.Entry stringLongEntry : problemRankListTop3) { + topProblemProject += stringLongEntry.getKey() +"、"; + } + if (StrUtil.isNotBlank(topProblemProject)) { + topProblemProject = StrUtil.removeSuffix(topProblemProject, "、"); + } // 重点关注单位(基于属实 + 基本属实) - Map.Entry departEntry = ReportTrendUtil.topBy(NegativeQueryVo::getInvolveDepartName, + List> departRankList = ReportTrendUtil.rankBy( + NegativeQueryVo::getSecondInvolveDepartId, currentClosedStats.getVerifiedList(), currentClosedStats.getBasicallyVerifiedList() ); - overviewSection.setTopProblemUnit(departEntry != null ? departEntry.getKey() : null); + Map allSecondDepartNameMap = supDepartResourceService.getAllSecondDepartNameMap(); + List> departRankListTop3 = departRankList.stream().limit(3).toList(); + String topProblemUnit = ""; + for (Map.Entry stringLongEntry : departRankListTop3) { + String departName = allSecondDepartNameMap.get(stringLongEntry.getKey()); + if (StrUtil.isNotBlank(departName)) { + topProblemUnit += departName + "、"; + } + } + if (StrUtil.isNotBlank(topProblemUnit)) { + topProblemUnit = StrUtil.removeSuffix(topProblemUnit, "、"); + } + + overviewSection.setTopProblemProject(topProblemProject); + overviewSection.setTopProblemUnit(topProblemUnit); log.info("总览数据计算结束======================================================================================="); return overviewSection; } @@ -183,9 +219,10 @@ public class ReportOverviewSectionServiceImpl implements ReportOverviewSectionSe /** * 统一计算问责统计 */ - private ReportAccountabilityStatsResponseDTO calculateAccountabilityStats(List list) { + private ReportAccountabilityStatsResponseDTO calculateAccountabilityStats(List list, ReportOverviewSectionRequestDTO reportOverviewSectionRequestDTO) { ReportAccountabilityStatsRequestDTO requestDTO = new ReportAccountabilityStatsRequestDTO(); requestDTO.setNegativeQueryVoList(list); + requestDTO.setCurrentUser(reportOverviewSectionRequestDTO.getNegativeQueryParam().getCurrentUser()); return reportAccountabilityStatsService.calculateAccountabilityStats(requestDTO); } diff --git a/src/main/java/com/biutag/supervision/service/report/impl/ReportProblemTypeStatsServiceImpl.java b/src/main/java/com/biutag/supervision/service/report/impl/ReportProblemTypeStatsServiceImpl.java new file mode 100644 index 0000000..8fdf924 --- /dev/null +++ b/src/main/java/com/biutag/supervision/service/report/impl/ReportProblemTypeStatsServiceImpl.java @@ -0,0 +1,102 @@ +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.ProcessingStatusEnum; +import com.biutag.supervision.pojo.dto.report.accountability.ReportAccountabilityStatsResponseDTO; +import com.biutag.supervision.pojo.dto.report.businessLine.BusinessLineRankItem; +import com.biutag.supervision.pojo.dto.report.problemType.ReportProblemTypeStatsRequestDTO; +import com.biutag.supervision.pojo.dto.report.problemType.ReportProblemTypeStatsResponseDTO; +import com.biutag.supervision.pojo.entity.NegativeProblemRelation; +import com.biutag.supervision.pojo.vo.NegativeQueryVo; +import com.biutag.supervision.service.NegativeProblemRelationService; +import com.biutag.supervision.service.report.ReportProblemTypeStatsService; +import com.biutag.supervision.util.ReportTrendUtil; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; + +/** + * @ClassName ReportProblemTypeStatsServiceImpl + * @Description ReportProblemTypeStatsServiceImpl + * @Author shihao + * @Date 2026/3/11 14:37 + */ +@Service +@Slf4j +public class ReportProblemTypeStatsServiceImpl implements ReportProblemTypeStatsService { + + + @Resource + private NegativeProblemRelationService negativeProblemRelationService; + + @Override + public ReportProblemTypeStatsResponseDTO calculateProblemTypeStats(ReportProblemTypeStatsRequestDTO requestDTO) { + ReportProblemTypeStatsResponseDTO responseDTO = new ReportProblemTypeStatsResponseDTO(); + if (requestDTO == null || CollUtil.isEmpty(requestDTO.getNegativeQueryVoList())) { + return responseDTO; + } + // 1. 过滤办结问题 + List closedNegativeList = requestDTO.getNegativeQueryVoList().stream() + .filter(one -> ProcessingStatusEnum.completed.name().equals(one.getProcessingStatus())) + .toList(); + if (CollUtil.isEmpty(closedNegativeList)) { + return responseDTO; + } + // 2. 查询问题类型关联数据 + Set negativeIds = closedNegativeList.stream() + .map(NegativeQueryVo::getId) + .filter(StrUtil::isNotBlank) + .collect(Collectors.toSet()); + List relationList = negativeProblemRelationService.list(negativeIds); + if (CollUtil.isEmpty(relationList)) { + return responseDTO; + } + // 3. 按问题ID去重,每个问题只保留一条问题类型记录 + List distinctRelationList = relationList.stream() + .collect(Collectors.collectingAndThen( + Collectors.toMap( + NegativeProblemRelation::getNegativeId, + relation -> relation, + (first, second) -> first + ), + map -> new ArrayList<>(map.values()) + )); + + // 4. 按问题类型分组统计数量 + Map problemTypeCountMap = distinctRelationList.stream() + .collect(Collectors.groupingBy( + this::buildProblemTypeName, + Collectors.counting() + )); + // 5. 取TOP3 + AtomicInteger index = new AtomicInteger(1); + List topProblemTypes = problemTypeCountMap.entrySet().stream() + .sorted((a, b) -> Long.compare(b.getValue(), a.getValue())) + .limit(3) + .map(entry -> new BusinessLineRankItem( + index.getAndIncrement(), + entry.getKey(), + entry.getValue().intValue(), + ReportTrendUtil.percent(entry.getValue().intValue(), distinctRelationList.size()) + )) + .toList(); + responseDTO.setTopProblemTypes(topProblemTypes); + return responseDTO; + } + + /** + * 构建问题类型展示名称:一级-二级-三级 + */ + private String buildProblemTypeName(NegativeProblemRelation relation) { + return String.join("/", + Objects.toString(relation.getOneLevelContent(), ""), + Objects.toString(relation.getTwoLevelContent(), ""), + Objects.toString(relation.getThreeLevelContent(), "") + ); + } +} diff --git a/src/main/java/com/biutag/supervision/util/ReportTrendUtil.java b/src/main/java/com/biutag/supervision/util/ReportTrendUtil.java index 28633f8..90562e5 100644 --- a/src/main/java/com/biutag/supervision/util/ReportTrendUtil.java +++ b/src/main/java/com/biutag/supervision/util/ReportTrendUtil.java @@ -107,6 +107,26 @@ public class ReportTrendUtil { return TrendEnum.STABLE; } + + @SafeVarargs + public static List> rankBy(Function mapper, List... lists) { + return Arrays.stream(lists) + .filter(Objects::nonNull) + .flatMap(List::stream) + .map(mapper) + .filter(Objects::nonNull) + .collect(Collectors.groupingBy( + Function.identity(), + Collectors.counting() + )) + .entrySet() + .stream() + .sorted((a, b) -> Long.compare(b.getValue(), a.getValue())) + .toList(); + } + + + @SafeVarargs public static Map.Entry topBy(Function mapper, List... lists) { @@ -124,5 +144,30 @@ public class ReportTrendUtil { .orElse(null); } + /** + * 计算百分比差值 + * 例如: + * 上月64%,本月40%,结果=-24.00 + */ + public static BigDecimal calcDiff(BigDecimal current, BigDecimal prev) { + if (current == null) { + current = BigDecimal.ZERO; + } + if (prev == null) { + prev = BigDecimal.ZERO; + } + + return current.subtract(prev).setScale(2, RoundingMode.HALF_UP); + } + + /** + * 计算百分比绝对变化值 + * 例如: + * 上月64%,本月40%,结果=24.00 + */ + public static BigDecimal calcAbsDiff(BigDecimal current, BigDecimal prev) { + return calcDiff(current, prev).abs().setScale(2, RoundingMode.HALF_UP); + } + } diff --git a/src/main/resources/static/templates/督审一体化平台研判分析报告.docx b/src/main/resources/static/templates/督审一体化平台研判分析报告.docx index dcd537f..dacb459 100644 Binary files a/src/main/resources/static/templates/督审一体化平台研判分析报告.docx and b/src/main/resources/static/templates/督审一体化平台研判分析报告.docx differ