diff --git a/src/main/java/com/biutag/supervision/controller/work/NegativeController.java b/src/main/java/com/biutag/supervision/controller/work/NegativeController.java index 1ad8335..35471ba 100644 --- a/src/main/java/com/biutag/supervision/controller/work/NegativeController.java +++ b/src/main/java/com/biutag/supervision/controller/work/NegativeController.java @@ -225,6 +225,13 @@ public class NegativeController { return Result.success(); } + + @PostMapping("calculate-optimized") + public Result calculateOptimized() { + negativeScoreService.updateNegativeScoreOptimized(); + return Result.success(); + } + private final ApplyCompletionAction applyCompletionAction; @PostMapping("{id}/spotCheck") diff --git a/src/main/java/com/biutag/supervision/service/NegativeScoreService.java b/src/main/java/com/biutag/supervision/service/NegativeScoreService.java index 666d417..31b8cdb 100644 --- a/src/main/java/com/biutag/supervision/service/NegativeScoreService.java +++ b/src/main/java/com/biutag/supervision/service/NegativeScoreService.java @@ -135,6 +135,109 @@ public class NegativeScoreService { System.out.println("calculateScore-------------------------------------------------End"); } + /** 使用索引减少重算时的重复遍历,原重算入口保持不变。 */ + @Transactional(rollbackFor = Exception.class) + public void updateNegativeScoreOptimized() { + if (!flag.compareAndSet(false, true)) { + throw new RuntimeException("风险指数正在计算中"); + } + long startTime = System.currentTimeMillis(); + try { + log.info("风险指数优化重算开始"); + List negativeBlames = blameMapper.selectVerifyTrue(); + int total = negativeBlames.size(); + log.info("风险指数优化重算:待处理责任记录 {} 条,加载耗时 {} 秒", total, + (System.currentTimeMillis() - startTime) / 1000); + if (negativeBlames.isEmpty()) { + return; + } + + negativeScorePoliceService.remove(new LambdaQueryWrapper().isNotNull(NegativeScorePolice::getNegativeId)); + negativeScoreDepartService.remove(new LambdaQueryWrapper().isNotNull(NegativeScoreDepart::getNegativeId)); + + Set negativeIds = negativeBlames.stream() + .map(NegativeBlame::getNegativeId).collect(Collectors.toSet()); + List problemsAll = problemRelationService.list(negativeIds); + List negatives = negativeService.listByIds(negativeIds); + List departs = departService.listByIds(negatives.stream() + .map(Negative::getInvolveDepartId).collect(Collectors.toSet())); + + record ProblemKey(String negativeId, String blameId) {} + record PoliceScoreKey(String negativeId, String idCode) {} + Map negativeById = negatives.stream().collect(Collectors.toMap(Negative::getId, negative -> negative)); + Map departById = departs.stream().collect(Collectors.toMap(SupDepart::getId, depart -> depart)); + Map> problemsByBlame = problemsAll.stream().collect(Collectors.groupingBy(problem -> new ProblemKey(problem.getNegativeId(), problem.getBlameId()))); + log.info("风险指数优化重算:索引就绪,问题 {} 条、单位 {} 个、问题关系 {} 条,累计耗时 {} 秒", negatives.size(), departs.size(), problemsAll.size(), (System.currentTimeMillis() - startTime) / 1000); + String formula = "%s + (%s * %s) + (%s * %s)"; + Map scorePolices = new LinkedHashMap<>(); + Map scoreDeparts = new LinkedHashMap<>(); + for (int i = 0; i < total; i++) { + if (i > 0 && i % 1000 == 0) { + log.info("风险指数优化重算:已处理 {}/{} 条,耗时 {} 秒", i, total, (System.currentTimeMillis() - startTime) / 1000); + } + NegativeBlame blame = negativeBlames.get(i); + Negative negative = Objects.requireNonNull(negativeById.get(blame.getNegativeId()), + "问题不存在:" + blame.getNegativeId()); + SupDepart depart = departById.get(negative.getInvolveDepartId()); + if (depart == null) { + continue; + } + List codes = problemsByBlame.getOrDefault( + new ProblemKey(blame.getNegativeId(), blame.getBlameId()), Collections.emptyList()) + .stream().map(NegativeProblemRelation::getThreeLevelCode).toList(); + if (codes.isEmpty()) { + continue; + } + + // 保留原来的逐条查字典和一年内次数规则。 + SupDictProblemType problemType = supDictContentMapper.selectOneByMaxScore(codes); + LocalDateTime endTime = negative.getDiscoveryTime(); + int frequency = blameService.countByTrue(blame.getBlameIdCode(), + problemType.getParentCode(), endTime.minusYears(1), endTime); + String expression = String.format(formula, + problemType.getScore(), problemType.getScore(), + NegativeLevelEnum.GENERAL_IMPACT.getScore(), problemType.getScore(), + ScoreRule.getFrequencyScore(frequency)); + double score = NumberUtil.roundHalfEven(NumberUtil.calculate(expression), 2).doubleValue(); + + NegativeScorePolice policeScore = new NegativeScorePolice() + .setNegativeId(blame.getNegativeId()) + .setIdCode(blame.getBlameIdCode()) + .setDiscoveryTime(endTime) + .setBusinessTypeCode(negative.getBusinessTypeCode()) + .setDepartGroupId(depart.getStatisticsGroupId()) + .setCreateTime(LocalDateTime.now()) + .setScore(score) + .setExpression(expression); + scorePolices.merge(new PoliceScoreKey(blame.getNegativeId(), blame.getBlameIdCode()), + policeScore, (oldScore, newScore) -> oldScore.getScore() < newScore.getScore() ? newScore : oldScore); + + NegativeScoreDepart departScore = scoreDeparts.get(negative.getId()); + if (departScore == null) { + departScore = new NegativeScoreDepart(); + BeanUtil.copyProperties(policeScore, departScore); + departScore.setDepartId(negative.getInvolveDepartId()); + scoreDeparts.put(negative.getId(), departScore); + } else if (departScore.getScore() < score) { + departScore.setScore(score); + departScore.setExpression(expression); + } + } + + log.info("风险指数优化重算:处理完 {} 条,开始写入个人 {} 条、单位 {} 条,耗时 {} 秒", + total, scorePolices.size(), scoreDeparts.size(), + (System.currentTimeMillis() - startTime) / 1000); + negativeScorePoliceService.saveBatch(scorePolices.values()); + negativeScoreDepartService.saveBatch(scoreDeparts.values()); + log.info("风险指数优化重算:写入方法结束,等待事务提交,累计耗时 {} 秒", + (System.currentTimeMillis() - startTime) / 1000); + } catch (RuntimeException e) { + log.error("风险指数优化重算失败,累计耗时 {} 秒", (System.currentTimeMillis() - startTime) / 1000, e); + throw e; + } finally { + flag.set(false); + } + } private final BusinessDepartService businessDepartService;