Bladeren bron

优化抽问抽答准确率排名的执行时间:减少数据库查询次数;批量计算正确率

simonlll 2 maanden geleden
bovenliggende
commit
5b49ac9a0b

+ 493 - 194
airport-exam/src/main/java/com/sundot/airport/exam/service/impl/AccuracyStatisticsServiceImpl.java

@@ -66,67 +66,207 @@ public class AccuracyStatisticsServiceImpl implements IAccuracyStatisticsService
66 66
     @Autowired
67 67
     private ISysUserService userService;
68 68
 
69
-    @Override
70
-    public AccuracyStatisticsDTO getAccuracyStatistics(AccuracyStatisticsQueryDTO query) {
71
-        // 获取当前用户信息(从登录信息中获取,确保包含角色信息)
72
-        Long currentUserId = SecurityUtils.getUserId();
73
-        Long currentDeptId = SecurityUtils.getDeptId();
74
-        SysUser currentUser = SecurityUtils.getLoginUser().getUser();
69
+    /**
70
+     * 部门数据缓存(请求级别)
71
+     */
72
+    private static class DeptCache {
73
+        List<SysDept> allDepts;
74
+        Map<Long, SysDept> deptMap;
75
+        Map<Long, DeptHierarchy> hierarchyMap;
76
+        // 按部门类型分组的部门列表
77
+        List<SysDept> teamDepts;
78
+        List<SysDept> managerDepts;
79
+        List<SysDept> brigadeDepts;
80
+        // 班组正确率缓存
81
+        Map<Long, BigDecimal> teamAccuracyMap;
82
+
83
+        DeptCache(List<SysDept> allDepts) {
84
+            this.allDepts = allDepts;
85
+            this.deptMap = new HashMap<>();
86
+            this.hierarchyMap = new HashMap<>();
87
+            this.teamDepts = new ArrayList<>();
88
+            this.managerDepts = new ArrayList<>();
89
+            this.brigadeDepts = new ArrayList<>();
90
+            this.teamAccuracyMap = new HashMap<>();
91
+
92
+            // 初始化部门Map
93
+            for (SysDept dept : allDepts) {
94
+                if ("0".equals(dept.getDelFlag())) {
95
+                    deptMap.put(dept.getDeptId(), dept);
96
+                    // 按类型分组
97
+                    if (StrUtil.equals(DeptType.TEAMS.getCode(), dept.getDeptType())) {
98
+                        teamDepts.add(dept);
99
+                    } else if (StrUtil.equals(DeptType.MANAGER.getCode(), dept.getDeptType())) {
100
+                        managerDepts.add(dept);
101
+                    } else if (StrUtil.equals(DeptType.BRIGADE.getCode(), dept.getDeptType())) {
102
+                        brigadeDepts.add(dept);
103
+                    }
104
+                }
105
+            }
106
+        }
75 107
 
76
-        // 调试日志
77
-        log.info("getAccuracyStatistics - currentUserId: {}, currentDeptId: {}", currentUserId, currentDeptId);
78
-        log.info("getAccuracyStatistics - currentUser: {}", currentUser != null ? currentUser.getUserName() : "null");
79
-        log.info("getAccuracyStatistics - currentUser.getRoles(): {}", currentUser != null ? currentUser.getRoles() : "null");
108
+        /**
109
+         * 获取部门层级(带缓存)
110
+         */
111
+        DeptHierarchy getHierarchy(Long deptId) {
112
+            if (deptId == null) return new DeptHierarchy();
113
+            return hierarchyMap.computeIfAbsent(deptId, this::computeHierarchy);
114
+        }
80 115
 
81
-        AccuracyStatisticsDTO result = new AccuracyStatisticsDTO();
116
+        private DeptHierarchy computeHierarchy(Long deptId) {
117
+            DeptHierarchy hierarchy = new DeptHierarchy();
118
+            SysDept dept = deptMap.get(deptId);
119
+            if (dept == null) return hierarchy;
82 120
 
83
-        // 获取用户角色
84
-        String roleKey = getUserRoleKey(currentUser);
85
-        log.info("getAccuracyStatistics - roleKey: {}", roleKey);
86
-        if (currentUser != null && currentUser.getRoles() != null && !currentUser.getRoles().isEmpty()) {
87
-            result.setUserRole(currentUser.getRoles().get(0).getRoleName());
121
+            String ancestors = dept.getAncestors();
122
+            String deptType = dept.getDeptType();
123
+
124
+            if (ancestors == null || ancestors.trim().isEmpty()) {
125
+                hierarchy.stationId = deptId;
126
+                return hierarchy;
127
+            }
128
+
129
+            String[] ancestorIds = ancestors.split(",");
130
+            if (ancestorIds.length >= 2) {
131
+                try { hierarchy.stationId = Long.parseLong(ancestorIds[1].trim()); } catch (NumberFormatException ignored) {}
132
+            }
133
+            if (ancestorIds.length >= 3) {
134
+                try { hierarchy.brigadeId = Long.parseLong(ancestorIds[2].trim()); } catch (NumberFormatException ignored) {}
135
+            }
136
+            if (ancestorIds.length >= 4) {
137
+                try { hierarchy.managerId = Long.parseLong(ancestorIds[3].trim()); } catch (NumberFormatException ignored) {}
138
+            }
139
+
140
+            if (StrUtil.equals(DeptType.STATION.getCode(), deptType)) {
141
+                hierarchy.stationId = deptId;
142
+            } else if (StrUtil.equals(DeptType.BRIGADE.getCode(), deptType)) {
143
+                hierarchy.brigadeId = deptId;
144
+            } else if (StrUtil.equals(DeptType.MANAGER.getCode(), deptType)) {
145
+                hierarchy.managerId = deptId;
146
+            } else if (StrUtil.equals(DeptType.TEAMS.getCode(), deptType)) {
147
+                hierarchy.teamId = deptId;
148
+            }
149
+
150
+            return hierarchy;
88 151
         }
152
+    }
89 153
 
90
-        // 解析时间范围
91
-        Date[] dateRange = parseDateRange(query);
92
-        Date startDate = dateRange[0];
93
-        Date endDate = dateRange[1];
154
+    /**
155
+     * ThreadLocal缓存,用于在同一请求中共享部门数据
156
+     */
157
+    private static final ThreadLocal<DeptCache> DEPT_CACHE = new ThreadLocal<>();
94 158
 
95
-        // 设置时间范围描述
96
-        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
97
-        if (startDate != null && endDate != null) {
98
-            result.setTimePeriod(sdf.format(startDate) + " ~ " + sdf.format(endDate));
159
+    /**
160
+     * 初始化部门缓存
161
+     */
162
+    private DeptCache initDeptCache() {
163
+        DeptCache cache = DEPT_CACHE.get();
164
+        if (cache == null) {
165
+            long start = System.currentTimeMillis();
166
+            List<SysDept> allDepts = deptService.selectDeptInfoAll(new SysDept());
167
+            cache = new DeptCache(allDepts);
168
+            DEPT_CACHE.set(cache);
169
+            log.info("initDeptCache: loaded {} depts in {}ms", allDepts.size(), System.currentTimeMillis() - start);
99 170
         }
171
+        return cache;
172
+    }
100 173
 
101
-        // 获取部门层级信息
102
-        DeptHierarchy hierarchy = getDeptHierarchy(currentDeptId);
174
+    /**
175
+     * 清理缓存
176
+     */
177
+    private void clearDeptCache() {
178
+        DEPT_CACHE.remove();
179
+    }
103 180
 
104
-        // 检查是否有待完成任务
105
-        checkPendingTask(result, currentUserId);
181
+    /**
182
+     * 获取当前缓存(如果存在)
183
+     */
184
+    private DeptCache getDeptCache() {
185
+        return DEPT_CACHE.get();
186
+    }
106 187
 
107
-        // 根据角色返回不同的数据
108
-        if (RoleTypeEnum.SecurityCheck.getCode().equals(roleKey)) {
109
-            // 安检员
110
-            calculateSecurityCheckData(result, currentUserId, hierarchy, startDate, endDate);
111
-        } else if (RoleTypeEnum.banzuzhang.getCode().equals(roleKey)) {
112
-            // 班组长
113
-            calculateTeamLeaderData(result, currentUserId, hierarchy, startDate, endDate);
114
-        } else if (RoleTypeEnum.kezhang.getCode().equals(roleKey)) {
115
-            // 主管
116
-            calculateManagerData(result, currentUserId, hierarchy, startDate, endDate);
117
-        } else if (RoleTypeEnum.jingli.getCode().equals(roleKey) || RoleTypeEnum.xingzheng.getCode().equals(roleKey)) {
118
-            // 大队长(经理/行政)
119
-            calculateBrigadeLeaderData(result, hierarchy, startDate, endDate);
120
-        } else if (RoleTypeEnum.test.getCode().equals(roleKey)) {
121
-            // 站长
122
-            calculateStationLeaderData(result, hierarchy, startDate, endDate);
123
-        } else {
124
-            // 其他角色:返回基本数据
125
-            calculatePersonalAccuracy(result, currentUserId, startDate, endDate);
126
-            calculateLevelAvgAccuracy(result, hierarchy, startDate, endDate);
188
+    @Override
189
+    public AccuracyStatisticsDTO getAccuracyStatistics(AccuracyStatisticsQueryDTO query) {
190
+        long methodStart = System.currentTimeMillis();
191
+        try {
192
+            // 初始化部门缓存(整个请求中复用)
193
+            initDeptCache();
194
+
195
+            // 获取当前用户信息(从登录信息中获取,确保包含角色信息)
196
+            Long currentUserId = SecurityUtils.getUserId();
197
+            Long currentDeptId = SecurityUtils.getDeptId();
198
+            SysUser currentUser = SecurityUtils.getLoginUser().getUser();
199
+
200
+            // 调试日志
201
+            log.info("getAccuracyStatistics - currentUserId: {}, currentDeptId: {}", currentUserId, currentDeptId);
202
+            log.info("getAccuracyStatistics - currentUser: {}", currentUser != null ? currentUser.getUserName() : "null");
203
+            log.info("getAccuracyStatistics - currentUser.getRoles(): {}", currentUser != null ? currentUser.getRoles() : "null");
204
+
205
+            AccuracyStatisticsDTO result = new AccuracyStatisticsDTO();
206
+
207
+            // 获取用户角色
208
+            String roleKey = getUserRoleKey(currentUser);
209
+            log.info("getAccuracyStatistics - roleKey: {}", roleKey);
210
+            if (currentUser != null && currentUser.getRoles() != null && !currentUser.getRoles().isEmpty()) {
211
+                result.setUserRole(currentUser.getRoles().get(0).getRoleName());
212
+            }
213
+
214
+            // 解析时间范围
215
+            Date[] dateRange = parseDateRange(query);
216
+            Date startDate = dateRange[0];
217
+            Date endDate = dateRange[1];
218
+
219
+            // 设置时间范围描述
220
+            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
221
+            if (startDate != null && endDate != null) {
222
+                result.setTimePeriod(sdf.format(startDate) + " ~ " + sdf.format(endDate));
223
+            }
224
+
225
+            // 获取部门层级信息(使用缓存)
226
+            DeptHierarchy hierarchy = getDeptHierarchyCached(currentDeptId);
227
+
228
+            // 检查是否有待完成任务
229
+            checkPendingTask(result, currentUserId);
230
+
231
+            // 根据角色返回不同的数据
232
+            if (RoleTypeEnum.SecurityCheck.getCode().equals(roleKey)) {
233
+                // 安检员
234
+                calculateSecurityCheckData(result, currentUserId, hierarchy, startDate, endDate);
235
+            } else if (RoleTypeEnum.banzuzhang.getCode().equals(roleKey)) {
236
+                // 班组长
237
+                calculateTeamLeaderData(result, currentUserId, hierarchy, startDate, endDate);
238
+            } else if (RoleTypeEnum.kezhang.getCode().equals(roleKey)) {
239
+                // 主管
240
+                calculateManagerData(result, currentUserId, hierarchy, startDate, endDate);
241
+            } else if (RoleTypeEnum.jingli.getCode().equals(roleKey) || RoleTypeEnum.xingzheng.getCode().equals(roleKey)) {
242
+                // 大队长(经理/行政)
243
+                calculateBrigadeLeaderData(result, hierarchy, startDate, endDate);
244
+            } else if (RoleTypeEnum.test.getCode().equals(roleKey)) {
245
+                // 站长
246
+                calculateStationLeaderData(result, hierarchy, startDate, endDate);
247
+            } else {
248
+                // 其他角色:返回基本数据
249
+                calculatePersonalAccuracy(result, currentUserId, startDate, endDate);
250
+                calculateLevelAvgAccuracy(result, hierarchy, startDate, endDate);
251
+            }
252
+
253
+            log.info("getAccuracyStatistics completed in {}ms", System.currentTimeMillis() - methodStart);
254
+            return result;
255
+        } finally {
256
+            // 清理缓存,避免内存泄漏
257
+            clearDeptCache();
127 258
         }
259
+    }
128 260
 
129
-        return result;
261
+    /**
262
+     * 获取部门层级(使用缓存)
263
+     */
264
+    private DeptHierarchy getDeptHierarchyCached(Long deptId) {
265
+        DeptCache cache = getDeptCache();
266
+        if (cache != null) {
267
+            return cache.getHierarchy(deptId);
268
+        }
269
+        return getDeptHierarchy(deptId);
130 270
     }
131 271
 
132 272
     /**
@@ -345,45 +485,60 @@ public class AccuracyStatisticsServiceImpl implements IAccuracyStatisticsService
345 485
     }
346 486
 
347 487
     /**
348
-     * 计算大队下所有主管的正确率
488
+     * 计算大队下所有主管的正确率(使用缓存和批量计算优化)
349 489
      */
350 490
     private List<TeamAccuracyInfo> calculateManagerAccuraciesUnderBrigade(Long brigadeId, Date startDate, Date endDate) {
351 491
         List<TeamAccuracyInfo> result = new ArrayList<>();
492
+        DeptCache cache = getDeptCache();
352 493
 
353
-        // 查询大队下所有主管
354
-        List<SysDept> allDepts = deptService.selectDeptInfoAll(new SysDept());
494
+        // 使用缓存的主管列表
495
+        List<SysDept> managerDepts = cache != null ? cache.managerDepts : getManagerDeptsFromDb();
355 496
 
356
-        for (SysDept dept : allDepts) {
357
-            if (!"0".equals(dept.getDelFlag())) {
358
-                continue;
359
-            }
360
-            if (!StrUtil.equals(DeptType.MANAGER.getCode(), dept.getDeptType())) {
361
-                continue;
362
-            }
497
+        // 收集大队下所有主管及其下属班组
498
+        List<SysDept> managersInBrigade = new ArrayList<>();
499
+        Map<Long, List<Long>> managerTeamIdsMap = new HashMap<>();
500
+        List<Long> allTeamIds = new ArrayList<>();
363 501
 
502
+        for (SysDept dept : managerDepts) {
364 503
             // 检查是否属于该大队
365 504
             if (brigadeId.equals(dept.getParentId())) {
366
-                TeamAccuracyInfo info = new TeamAccuracyInfo();
367
-                info.deptId = dept.getDeptId();
368
-                info.deptName = dept.getDeptName();
369
-
370
-                DeptHierarchy h = getDeptHierarchy(dept.getDeptId());
505
+                managersInBrigade.add(dept);
506
+                DeptHierarchy h = cache != null ? cache.getHierarchy(dept.getDeptId()) : getDeptHierarchy(dept.getDeptId());
371 507
                 List<Long> teamIds = getTeamIdsUnderDept(dept.getDeptId(), h);
508
+                managerTeamIdsMap.put(dept.getDeptId(), teamIds);
509
+                allTeamIds.addAll(teamIds);
510
+            }
511
+        }
372 512
 
373
-                if (!teamIds.isEmpty()) {
374
-                    long[] counts = calculateAccuracyCountsForTeams(teamIds, startDate, endDate);
375
-                    if (counts[1] > 0) {
376
-                        info.accuracy = BigDecimal.valueOf(counts[0])
377
-                                .multiply(BigDecimal.valueOf(100))
378
-                                .divide(BigDecimal.valueOf(counts[1]), 2, RoundingMode.HALF_UP);
379
-                    } else {
380
-                        info.accuracy = BigDecimal.ZERO;
381
-                    }
382
-                } else {
383
-                    info.accuracy = BigDecimal.ZERO;
513
+        // 批量计算所有班组的正确率
514
+        Map<Long, long[]> teamCountsMap = allTeamIds.isEmpty() ? new HashMap<>() : batchCalculateAccuracyCountsForTeams(allTeamIds, startDate, endDate);
515
+
516
+        // 为每个主管汇总正确率
517
+        for (SysDept dept : managersInBrigade) {
518
+            TeamAccuracyInfo info = new TeamAccuracyInfo();
519
+            info.deptId = dept.getDeptId();
520
+            info.deptName = dept.getDeptName();
521
+
522
+            List<Long> teamIds = managerTeamIdsMap.get(dept.getDeptId());
523
+            long totalCorrect = 0;
524
+            long totalCount = 0;
525
+
526
+            if (teamIds != null && !teamIds.isEmpty()) {
527
+                for (Long teamId : teamIds) {
528
+                    long[] counts = teamCountsMap.getOrDefault(teamId, new long[]{0, 0});
529
+                    totalCorrect += counts[0];
530
+                    totalCount += counts[1];
384 531
                 }
385
-                result.add(info);
386 532
             }
533
+
534
+            if (totalCount > 0) {
535
+                info.accuracy = BigDecimal.valueOf(totalCorrect)
536
+                        .multiply(BigDecimal.valueOf(100))
537
+                        .divide(BigDecimal.valueOf(totalCount), 2, RoundingMode.HALF_UP);
538
+            } else {
539
+                info.accuracy = BigDecimal.ZERO;
540
+            }
541
+            result.add(info);
387 542
         }
388 543
 
389 544
         // 按正确率降序排序
@@ -393,45 +548,70 @@ public class AccuracyStatisticsServiceImpl implements IAccuracyStatisticsService
393 548
     }
394 549
 
395 550
     /**
396
-     * 计算站级下所有大队的正确率
551
+     * 从数据库获取主管部门列表(无缓存时使用)
552
+     */
553
+    private List<SysDept> getManagerDeptsFromDb() {
554
+        List<SysDept> allDepts = deptService.selectDeptInfoAll(new SysDept());
555
+        return allDepts.stream()
556
+                .filter(d -> "0".equals(d.getDelFlag()) && StrUtil.equals(DeptType.MANAGER.getCode(), d.getDeptType()))
557
+                .collect(Collectors.toList());
558
+    }
559
+
560
+    /**
561
+     * 计算站级下所有大队的正确率(使用缓存和批量计算优化)
397 562
      */
398 563
     private List<TeamAccuracyInfo> calculateBrigadeAccuraciesUnderStation(Long stationId, Date startDate, Date endDate) {
399 564
         List<TeamAccuracyInfo> result = new ArrayList<>();
400 565
 
401
-        // 查询站级下所有大队
402
-        List<SysDept> allDepts = deptService.selectDeptInfoAll(new SysDept());
566
+        // 使用缓存的大队列表
567
+        DeptCache cache = getDeptCache();
568
+        List<SysDept> brigadeDepts = cache != null ? cache.brigadeDepts : getBrigadeDeptsFromDb();
403 569
 
404
-        for (SysDept dept : allDepts) {
405
-            if (!"0".equals(dept.getDelFlag())) {
406
-                continue;
407
-            }
408
-            if (!StrUtil.equals(DeptType.BRIGADE.getCode(), dept.getDeptType())) {
409
-                continue;
410
-            }
570
+        // 收集站级下所有大队及其下属班组
571
+        List<SysDept> brigadesInStation = new ArrayList<>();
572
+        Map<Long, List<Long>> brigadeTeamIdsMap = new HashMap<>();
573
+        List<Long> allTeamIds = new ArrayList<>();
411 574
 
575
+        for (SysDept dept : brigadeDepts) {
412 576
             // 检查是否属于该站级
413 577
             if (stationId.equals(dept.getParentId())) {
414
-                TeamAccuracyInfo info = new TeamAccuracyInfo();
415
-                info.deptId = dept.getDeptId();
416
-                info.deptName = dept.getDeptName();
417
-
418
-                DeptHierarchy h = getDeptHierarchy(dept.getDeptId());
578
+                brigadesInStation.add(dept);
579
+                DeptHierarchy h = cache != null ? cache.getHierarchy(dept.getDeptId()) : getDeptHierarchy(dept.getDeptId());
419 580
                 List<Long> teamIds = getTeamIdsUnderDept(dept.getDeptId(), h);
581
+                brigadeTeamIdsMap.put(dept.getDeptId(), teamIds);
582
+                allTeamIds.addAll(teamIds);
583
+            }
584
+        }
420 585
 
421
-                if (!teamIds.isEmpty()) {
422
-                    long[] counts = calculateAccuracyCountsForTeams(teamIds, startDate, endDate);
423
-                    if (counts[1] > 0) {
424
-                        info.accuracy = BigDecimal.valueOf(counts[0])
425
-                                .multiply(BigDecimal.valueOf(100))
426
-                                .divide(BigDecimal.valueOf(counts[1]), 2, RoundingMode.HALF_UP);
427
-                    } else {
428
-                        info.accuracy = BigDecimal.ZERO;
429
-                    }
430
-                } else {
431
-                    info.accuracy = BigDecimal.ZERO;
586
+        // 批量计算所有班组的正确率
587
+        Map<Long, long[]> teamCountsMap = allTeamIds.isEmpty() ? new HashMap<>() : batchCalculateAccuracyCountsForTeams(allTeamIds, startDate, endDate);
588
+
589
+        // 为每个大队汇总正确率
590
+        for (SysDept dept : brigadesInStation) {
591
+            TeamAccuracyInfo info = new TeamAccuracyInfo();
592
+            info.deptId = dept.getDeptId();
593
+            info.deptName = dept.getDeptName();
594
+
595
+            List<Long> teamIds = brigadeTeamIdsMap.get(dept.getDeptId());
596
+            long totalCorrect = 0;
597
+            long totalCount = 0;
598
+
599
+            if (teamIds != null && !teamIds.isEmpty()) {
600
+                for (Long teamId : teamIds) {
601
+                    long[] counts = teamCountsMap.getOrDefault(teamId, new long[]{0, 0});
602
+                    totalCorrect += counts[0];
603
+                    totalCount += counts[1];
432 604
                 }
433
-                result.add(info);
434 605
             }
606
+
607
+            if (totalCount > 0) {
608
+                info.accuracy = BigDecimal.valueOf(totalCorrect)
609
+                        .multiply(BigDecimal.valueOf(100))
610
+                        .divide(BigDecimal.valueOf(totalCount), 2, RoundingMode.HALF_UP);
611
+            } else {
612
+                info.accuracy = BigDecimal.ZERO;
613
+            }
614
+            result.add(info);
435 615
         }
436 616
 
437 617
         // 按正确率降序排序
@@ -441,6 +621,16 @@ public class AccuracyStatisticsServiceImpl implements IAccuracyStatisticsService
441 621
     }
442 622
 
443 623
     /**
624
+     * 从数据库获取大队部门列表(无缓存时使用)
625
+     */
626
+    private List<SysDept> getBrigadeDeptsFromDb() {
627
+        List<SysDept> allDepts = deptService.selectDeptInfoAll(new SysDept());
628
+        return allDepts.stream()
629
+                .filter(d -> "0".equals(d.getDelFlag()) && StrUtil.equals(DeptType.BRIGADE.getCode(), d.getDeptType()))
630
+                .collect(Collectors.toList());
631
+    }
632
+
633
+    /**
444 634
      * 将班组排名转换为带大队名称的格式
445 635
      */
446 636
     private List<AccuracyStatisticsDTO.RankingItem> convertToTeamRankingItems(List<TeamAccuracyInfo> rankings, int limit, boolean fromTop, Long brigadeId) {
@@ -518,22 +708,23 @@ public class AccuracyStatisticsServiceImpl implements IAccuracyStatisticsService
518 708
     }
519 709
 
520 710
     /**
521
-     * 获取班组显示名称,格式为"大队名+班组名",如"一大队张三班组"
711
+     * 获取班组显示名称,格式为"大队名+班组名",如"一大队张三班组"(使用缓存优化)
522 712
      */
523 713
     private String getTeamDisplayNameWithBrigade(Long teamId, String teamName) {
524
-        SysDept teamDept = deptService.selectDeptById(teamId);
714
+        DeptCache cache = getDeptCache();
715
+        SysDept teamDept = cache != null ? cache.deptMap.get(teamId) : deptService.selectDeptById(teamId);
525 716
         if (teamDept == null) {
526 717
             return teamName;
527 718
         }
528 719
         // 获取班组所属的主管
529 720
         Long managerId = teamDept.getParentId();
530 721
         if (managerId != null) {
531
-            SysDept managerDept = deptService.selectDeptById(managerId);
722
+            SysDept managerDept = cache != null ? cache.deptMap.get(managerId) : deptService.selectDeptById(managerId);
532 723
             if (managerDept != null) {
533 724
                 // 获取主管所属的大队
534 725
                 Long brigadeId = managerDept.getParentId();
535 726
                 if (brigadeId != null) {
536
-                    SysDept brigadeDept = deptService.selectDeptById(brigadeId);
727
+                    SysDept brigadeDept = cache != null ? cache.deptMap.get(brigadeId) : deptService.selectDeptById(brigadeId);
537 728
                     if (brigadeDept != null && StrUtil.equals(DeptType.BRIGADE.getCode(), brigadeDept.getDeptType())) {
538 729
                         return brigadeDept.getDeptName() + teamName;
539 730
                     }
@@ -665,22 +856,26 @@ public class AccuracyStatisticsServiceImpl implements IAccuracyStatisticsService
665 856
 
666 857
     @Override
667 858
     public DailyTaskAccuracyRankingDto getAccuracyRanking(BaseLargeScreenQueryParamDto dto) {
668
-        DailyTaskAccuracyRankingDto result = new DailyTaskAccuracyRankingDto();
859
+        try {
860
+            // 初始化部门缓存(整个请求中复用)
861
+            initDeptCache();
669 862
 
670
-        // 获取当前用户信息
671
-        Long currentUserId = SecurityUtils.getUserId();
672
-        Long currentDeptId = SecurityUtils.getDeptId();
673
-        SysUser currentUser = userService.selectUserById(currentUserId);
863
+            DailyTaskAccuracyRankingDto result = new DailyTaskAccuracyRankingDto();
674 864
 
675
-        // 解析日期范围
676
-        Date startDate = dto.getStartDate();
677
-        Date endDate = dto.getEndDate();
678
-        if (endDate != null) {
679
-            endDate = DateUtils.addSeconds(DateUtils.truncate(endDate, Calendar.DAY_OF_MONTH), 86399);
680
-        }
865
+            // 获取当前用户信息
866
+            Long currentUserId = SecurityUtils.getUserId();
867
+            Long currentDeptId = SecurityUtils.getDeptId();
868
+            SysUser currentUser = userService.selectUserById(currentUserId);
869
+
870
+            // 解析日期范围
871
+            Date startDate = dto.getStartDate();
872
+            Date endDate = dto.getEndDate();
873
+            if (endDate != null) {
874
+                endDate = DateUtils.addSeconds(DateUtils.truncate(endDate, Calendar.DAY_OF_MONTH), 86399);
875
+            }
681 876
 
682
-        // 获取当前用户的部门层级
683
-        DeptHierarchy hierarchy = getDeptHierarchy(currentDeptId);
877
+            // 获取当前用户的部门层级(使用缓存)
878
+            DeptHierarchy hierarchy = getDeptHierarchyCached(currentDeptId);
684 879
 
685 880
         // 获取同级别的所有用户正确率排名
686 881
         List<UserAccuracyInfo> userRankings = calculateUserRankingsInScope(hierarchy, startDate, endDate);
@@ -723,7 +918,11 @@ public class AccuracyStatisticsServiceImpl implements IAccuracyStatisticsService
723 918
             result.setMedalTypeDesc(medalType.getDesc());
724 919
         }
725 920
 
726
-        return result;
921
+            return result;
922
+        } finally {
923
+            // 清理缓存,避免内存泄漏
924
+            clearDeptCache();
925
+        }
727 926
     }
728 927
 
729 928
     @Override
@@ -945,10 +1144,10 @@ public class AccuracyStatisticsServiceImpl implements IAccuracyStatisticsService
945 1144
     }
946 1145
 
947 1146
     /**
948
-     * 计算部门平均正确率
1147
+     * 计算部门平均正确率(使用缓存优化)
949 1148
      */
950 1149
     private BigDecimal calculateDeptAvgAccuracy(Long deptId, Date startDate, Date endDate) {
951
-        DeptHierarchy hierarchy = getDeptHierarchy(deptId);
1150
+        DeptHierarchy hierarchy = getDeptHierarchyCached(deptId);
952 1151
         List<Long> teamIds = getTeamIdsUnderDept(deptId, hierarchy);
953 1152
 
954 1153
         if (teamIds.isEmpty()) {
@@ -994,11 +1193,11 @@ public class AccuracyStatisticsServiceImpl implements IAccuracyStatisticsService
994 1193
     }
995 1194
 
996 1195
     /**
997
-     * 计算用户在指定部门范围内的排名
1196
+     * 计算用户在指定部门范围内的排名(使用缓存优化)
998 1197
      * 如果用户没有答题记录,将其视为正确率0排在最后
999 1198
      */
1000 1199
     private AccuracyStatisticsDTO.RankingInfo calculateUserRankingInDept(Long userId, Long deptId, Date startDate, Date endDate) {
1001
-        DeptHierarchy hierarchy = getDeptHierarchy(deptId);
1200
+        DeptHierarchy hierarchy = getDeptHierarchyCached(deptId);
1002 1201
         List<Long> teamIds = getTeamIdsUnderDept(deptId, hierarchy);
1003 1202
 
1004 1203
         AccuracyStatisticsDTO.RankingInfo rankingInfo = new AccuracyStatisticsDTO.RankingInfo();
@@ -1205,38 +1404,48 @@ public class AccuracyStatisticsServiceImpl implements IAccuracyStatisticsService
1205 1404
     }
1206 1405
 
1207 1406
     /**
1208
-     * 获取部门下所有班组ID
1407
+     * 获取部门下所有班组ID(使用缓存优化)
1209 1408
      */
1210 1409
     private List<Long> getTeamIdsUnderDept(Long deptId, DeptHierarchy hierarchy) {
1211 1410
         List<Long> teamIds = new ArrayList<>();
1411
+        DeptCache cache = getDeptCache();
1212 1412
 
1213
-        SysDept dept = deptService.selectDeptById(deptId);
1413
+        // 优先使用缓存
1414
+        SysDept dept = cache != null ? cache.deptMap.get(deptId) : deptService.selectDeptById(deptId);
1214 1415
         if (dept == null) {
1215 1416
             log.warn("getTeamIdsUnderDept: dept not found for deptId={}", deptId);
1216 1417
             return teamIds;
1217 1418
         }
1218 1419
 
1219 1420
         String deptType = dept.getDeptType();
1220
-        log.debug("getTeamIdsUnderDept: deptId={}, deptName={}, deptType={}", deptId, dept.getDeptName(), deptType);
1221 1421
 
1222 1422
         if (StrUtil.equals(DeptType.TEAMS.getCode(), deptType)) {
1223 1423
             // 已经是班组,直接返回
1224 1424
             teamIds.add(deptId);
1225
-            log.debug("getTeamIdsUnderDept: dept is already a team, returning [{}]", deptId);
1226
-        } else {
1227
-            // 查询下属所有班组
1228
-            List<SysDept> allDepts = deptService.selectDeptInfoAll(new SysDept());
1229
-            log.debug("getTeamIdsUnderDept: total depts count={}", allDepts.size());
1425
+        } else if (cache != null) {
1426
+            // 使用缓存的班组列表(性能优化核心)
1427
+            for (SysDept d : cache.teamDepts) {
1428
+                DeptHierarchy h = cache.getHierarchy(d.getDeptId());
1429
+                boolean match = false;
1230 1430
 
1231
-            int teamCount = 0;
1232
-            for (SysDept d : allDepts) {
1233
-                if (!"0".equals(d.getDelFlag())) {
1234
-                    continue;
1431
+                if (StrUtil.equals(DeptType.STATION.getCode(), deptType)) {
1432
+                    match = deptId.equals(h.stationId);
1433
+                } else if (StrUtil.equals(DeptType.BRIGADE.getCode(), deptType)) {
1434
+                    match = deptId.equals(h.brigadeId);
1435
+                } else if (StrUtil.equals(DeptType.MANAGER.getCode(), deptType)) {
1436
+                    match = deptId.equals(h.managerId);
1235 1437
                 }
1236
-                if (!StrUtil.equals(DeptType.TEAMS.getCode(), d.getDeptType())) {
1237
-                    continue;
1438
+
1439
+                if (match) {
1440
+                    teamIds.add(d.getDeptId());
1238 1441
                 }
1239
-                teamCount++;
1442
+            }
1443
+        } else {
1444
+            // 无缓存时的原始逻辑
1445
+            List<SysDept> allDepts = deptService.selectDeptInfoAll(new SysDept());
1446
+            for (SysDept d : allDepts) {
1447
+                if (!"0".equals(d.getDelFlag())) continue;
1448
+                if (!StrUtil.equals(DeptType.TEAMS.getCode(), d.getDeptType())) continue;
1240 1449
 
1241 1450
                 DeptHierarchy h = getDeptHierarchy(d.getDeptId());
1242 1451
                 boolean match = false;
@@ -1253,33 +1462,43 @@ public class AccuracyStatisticsServiceImpl implements IAccuracyStatisticsService
1253 1462
                     teamIds.add(d.getDeptId());
1254 1463
                 }
1255 1464
             }
1256
-            log.debug("getTeamIdsUnderDept: found {} teams total, {} matching deptId={}", teamCount, teamIds.size(), deptId);
1257 1465
         }
1258 1466
 
1259 1467
         return teamIds;
1260 1468
     }
1261 1469
 
1262 1470
     /**
1263
-     * 计算指定范围内所有用户的正确率排名(用于首页排名)
1471
+     * 计算指定范围内所有用户的正确率排名(用于首页排名,使用缓存优化
1264 1472
      */
1265 1473
     private List<UserAccuracyInfo> calculateUserRankingsInScope(DeptHierarchy hierarchy, Date startDate, Date endDate) {
1266 1474
         // 获取该层级下所有班组ID
1267 1475
         List<Long> teamIds = new ArrayList<>();
1476
+        DeptCache cache = getDeptCache();
1268 1477
 
1269 1478
         if (hierarchy.stationId != null) {
1270
-            // 查询站级下所有班组
1271
-            List<SysDept> allDepts = deptService.selectDeptInfoAll(new SysDept());
1272
-            for (SysDept dept : allDepts) {
1273
-                if (!"0".equals(dept.getDelFlag())) {
1274
-                    continue;
1275
-                }
1276
-                if (!StrUtil.equals(DeptType.TEAMS.getCode(), dept.getDeptType())) {
1277
-                    continue;
1479
+            // 优先使用缓存
1480
+            if (cache != null) {
1481
+                for (SysDept dept : cache.teamDepts) {
1482
+                    DeptHierarchy deptHierarchy = cache.getHierarchy(dept.getDeptId());
1483
+                    if (hierarchy.stationId.equals(deptHierarchy.stationId)) {
1484
+                        teamIds.add(dept.getDeptId());
1485
+                    }
1278 1486
                 }
1487
+            } else {
1488
+                // 无缓存时查询数据库
1489
+                List<SysDept> allDepts = deptService.selectDeptInfoAll(new SysDept());
1490
+                for (SysDept dept : allDepts) {
1491
+                    if (!"0".equals(dept.getDelFlag())) {
1492
+                        continue;
1493
+                    }
1494
+                    if (!StrUtil.equals(DeptType.TEAMS.getCode(), dept.getDeptType())) {
1495
+                        continue;
1496
+                    }
1279 1497
 
1280
-                DeptHierarchy deptHierarchy = getDeptHierarchy(dept.getDeptId());
1281
-                if (hierarchy.stationId.equals(deptHierarchy.stationId)) {
1282
-                    teamIds.add(dept.getDeptId());
1498
+                    DeptHierarchy deptHierarchy = getDeptHierarchy(dept.getDeptId());
1499
+                    if (hierarchy.stationId.equals(deptHierarchy.stationId)) {
1500
+                        teamIds.add(dept.getDeptId());
1501
+                    }
1283 1502
                 }
1284 1503
             }
1285 1504
         }
@@ -1528,21 +1747,25 @@ public class AccuracyStatisticsServiceImpl implements IAccuracyStatisticsService
1528 1747
     }
1529 1748
 
1530 1749
     /**
1531
-     * 计算指定部门下所有班组的正确率
1750
+     * 计算指定部门下所有班组的正确率(批量优化版本)
1532 1751
      */
1533 1752
     private List<TeamAccuracyInfo> calculateTeamAccuraciesUnderDept(Long deptId, Date startDate, Date endDate) {
1534 1753
         List<TeamAccuracyInfo> result = new ArrayList<>();
1754
+        DeptCache cache = getDeptCache();
1535 1755
 
1536
-        DeptHierarchy hierarchy = getDeptHierarchy(deptId);
1756
+        DeptHierarchy hierarchy = cache != null ? cache.getHierarchy(deptId) : getDeptHierarchy(deptId);
1537 1757
         List<Long> teamIds = getTeamIdsUnderDept(deptId, hierarchy);
1538 1758
 
1539 1759
         if (teamIds.isEmpty()) {
1540 1760
             return result;
1541 1761
         }
1542 1762
 
1543
-        // 为每个班组计算正确率
1763
+        // 批量计算所有班组的正确率(核心优化:一次查询代替多次查询)
1764
+        Map<Long, long[]> teamCountsMap = batchCalculateAccuracyCountsForTeams(teamIds, startDate, endDate);
1765
+
1766
+        // 为每个班组设置正确率
1544 1767
         for (Long teamId : teamIds) {
1545
-            SysDept teamDept = deptService.selectDeptById(teamId);
1768
+            SysDept teamDept = cache != null ? cache.deptMap.get(teamId) : deptService.selectDeptById(teamId);
1546 1769
             if (teamDept == null) {
1547 1770
                 continue;
1548 1771
             }
@@ -1551,7 +1774,7 @@ public class AccuracyStatisticsServiceImpl implements IAccuracyStatisticsService
1551 1774
             info.deptId = teamId;
1552 1775
             info.deptName = teamDept.getDeptName();
1553 1776
 
1554
-            long[] counts = calculateAccuracyCountsForTeams(Collections.singletonList(teamId), startDate, endDate);
1777
+            long[] counts = teamCountsMap.getOrDefault(teamId, new long[]{0, 0});
1555 1778
             if (counts[1] > 0) {
1556 1779
                 info.accuracy = BigDecimal.valueOf(counts[0])
1557 1780
                         .multiply(BigDecimal.valueOf(100))
@@ -1569,52 +1792,127 @@ public class AccuracyStatisticsServiceImpl implements IAccuracyStatisticsService
1569 1792
     }
1570 1793
 
1571 1794
     /**
1572
-     * 计算站级下所有主管的正确率
1795
+     * 批量计算多个班组的正确率数量(性能优化核心方法)
1796
+     * 一次查询所有班组的数据,然后在内存中分组统计
1797
+     * @return Map<班组ID, long[]{正确数, 总数}>
1798
+     */
1799
+    private Map<Long, long[]> batchCalculateAccuracyCountsForTeams(List<Long> teamIds, Date startDate, Date endDate) {
1800
+        Map<Long, long[]> result = new HashMap<>();
1801
+        if (teamIds.isEmpty()) {
1802
+            return result;
1803
+        }
1804
+
1805
+        // 一次性查询所有班组的任务
1806
+        LambdaQueryWrapper<DailyTask> taskWrapper = new LambdaQueryWrapper<>();
1807
+        taskWrapper.in(DailyTask::getDtDeptId, teamIds);
1808
+        taskWrapper.eq(DailyTask::getDtStatus, DailyTaskStatusEnum.COMPLETED.getCode());
1809
+        taskWrapper.eq(DailyTask::getDelStatus, 0);
1810
+        if (startDate != null) {
1811
+            taskWrapper.ge(DailyTask::getDtBusinessDate, startDate);
1812
+        }
1813
+        if (endDate != null) {
1814
+            taskWrapper.le(DailyTask::getDtBusinessDate, endDate);
1815
+        }
1816
+        List<DailyTask> tasks = dailyTaskMapper.selectList(taskWrapper);
1817
+
1818
+        if (tasks.isEmpty()) {
1819
+            return result;
1820
+        }
1821
+
1822
+        // 按班组分组任务
1823
+        Map<Long, List<DailyTask>> tasksByDept = tasks.stream()
1824
+                .filter(t -> t.getDtDeptId() != null)
1825
+                .collect(Collectors.groupingBy(DailyTask::getDtDeptId));
1826
+
1827
+        // 一次性查询所有任务明细
1828
+        List<String> allTaskIds = tasks.stream()
1829
+                .map(DailyTask::getDtId)
1830
+                .collect(Collectors.toList());
1831
+
1832
+        LambdaQueryWrapper<DailyTaskDetail> detailWrapper = new LambdaQueryWrapper<>();
1833
+        detailWrapper.in(DailyTaskDetail::getDtdTaskId, allTaskIds);
1834
+        detailWrapper.eq(DailyTaskDetail::getDelStatus, 0);
1835
+        detailWrapper.isNotNull(DailyTaskDetail::getDtdIsCorrect);
1836
+        List<DailyTaskDetail> allDetails = dailyTaskDetailMapper.selectList(detailWrapper);
1837
+
1838
+        // 按任务ID分组明细
1839
+        Map<String, List<DailyTaskDetail>> detailsByTaskId = allDetails.stream()
1840
+                .collect(Collectors.groupingBy(DailyTaskDetail::getDtdTaskId));
1841
+
1842
+        // 计算每个班组的统计数据
1843
+        for (Map.Entry<Long, List<DailyTask>> entry : tasksByDept.entrySet()) {
1844
+            Long deptId = entry.getKey();
1845
+            List<DailyTask> deptTasks = entry.getValue();
1846
+
1847
+            long totalCount = 0;
1848
+            long correctCount = 0;
1849
+
1850
+            for (DailyTask task : deptTasks) {
1851
+                List<DailyTaskDetail> taskDetails = detailsByTaskId.get(task.getDtId());
1852
+                if (taskDetails != null) {
1853
+                    totalCount += taskDetails.size();
1854
+                    correctCount += taskDetails.stream()
1855
+                            .filter(d -> Boolean.TRUE.equals(d.getDtdIsCorrect()))
1856
+                            .count();
1857
+                }
1858
+            }
1859
+
1860
+            result.put(deptId, new long[]{correctCount, totalCount});
1861
+        }
1862
+
1863
+        return result;
1864
+    }
1865
+
1866
+    /**
1867
+     * 计算站级下所有主管的正确率(使用缓存和批量计算优化)
1573 1868
      */
1574 1869
     private List<TeamAccuracyInfo> calculateManagerAccuraciesUnderStation(Long stationId, Date startDate, Date endDate) {
1575 1870
         List<TeamAccuracyInfo> result = new ArrayList<>();
1871
+        DeptCache cache = getDeptCache();
1576 1872
 
1577
-        // 查询站级下所有主管
1578
-        List<SysDept> allDepts = deptService.selectDeptInfoAll(new SysDept());
1579
-        List<Long> managerIds = new ArrayList<>();
1873
+        // 使用缓存的主管列表
1874
+        List<SysDept> managerDepts = cache != null ? cache.managerDepts : getManagerDeptsFromDb();
1580 1875
 
1581
-        for (SysDept dept : allDepts) {
1582
-            if (!"0".equals(dept.getDelFlag())) {
1583
-                continue;
1584
-            }
1585
-            if (!StrUtil.equals(DeptType.MANAGER.getCode(), dept.getDeptType())) {
1586
-                continue;
1587
-            }
1876
+        // 收集站级下所有主管及其下属班组
1877
+        List<SysDept> managersInStation = new ArrayList<>();
1878
+        Map<Long, List<Long>> managerTeamIdsMap = new HashMap<>();
1879
+        List<Long> allTeamIds = new ArrayList<>();
1588 1880
 
1589
-            DeptHierarchy h = getDeptHierarchy(dept.getDeptId());
1881
+        for (SysDept dept : managerDepts) {
1882
+            DeptHierarchy h = cache != null ? cache.getHierarchy(dept.getDeptId()) : getDeptHierarchy(dept.getDeptId());
1590 1883
             if (stationId.equals(h.stationId)) {
1591
-                managerIds.add(dept.getDeptId());
1884
+                managersInStation.add(dept);
1885
+                List<Long> teamIds = getTeamIdsUnderDept(dept.getDeptId(), h);
1886
+                managerTeamIdsMap.put(dept.getDeptId(), teamIds);
1887
+                allTeamIds.addAll(teamIds);
1592 1888
             }
1593 1889
         }
1594 1890
 
1595
-        // 为每个主管计算正确率
1596
-        for (Long managerId : managerIds) {
1597
-            SysDept managerDept = deptService.selectDeptById(managerId);
1598
-            if (managerDept == null) {
1599
-                continue;
1600
-            }
1891
+        // 批量计算所有班组的正确率
1892
+        Map<Long, long[]> teamCountsMap = allTeamIds.isEmpty() ? new HashMap<>() : batchCalculateAccuracyCountsForTeams(allTeamIds, startDate, endDate);
1601 1893
 
1894
+        // 为每个主管汇总正确率
1895
+        for (SysDept dept : managersInStation) {
1602 1896
             TeamAccuracyInfo info = new TeamAccuracyInfo();
1603
-            info.deptId = managerId;
1604
-            info.deptName = managerDept.getDeptName();
1605
-
1606
-            DeptHierarchy h = getDeptHierarchy(managerId);
1607
-            List<Long> teamIds = getTeamIdsUnderDept(managerId, h);
1608
-
1609
-            if (!teamIds.isEmpty()) {
1610
-                long[] counts = calculateAccuracyCountsForTeams(teamIds, startDate, endDate);
1611
-                if (counts[1] > 0) {
1612
-                    info.accuracy = BigDecimal.valueOf(counts[0])
1613
-                            .multiply(BigDecimal.valueOf(100))
1614
-                            .divide(BigDecimal.valueOf(counts[1]), 2, RoundingMode.HALF_UP);
1615
-                } else {
1616
-                    info.accuracy = BigDecimal.ZERO;
1897
+            info.deptId = dept.getDeptId();
1898
+            info.deptName = dept.getDeptName();
1899
+
1900
+            List<Long> teamIds = managerTeamIdsMap.get(dept.getDeptId());
1901
+            long totalCorrect = 0;
1902
+            long totalCount = 0;
1903
+
1904
+            if (teamIds != null && !teamIds.isEmpty()) {
1905
+                for (Long teamId : teamIds) {
1906
+                    long[] counts = teamCountsMap.getOrDefault(teamId, new long[]{0, 0});
1907
+                    totalCorrect += counts[0];
1908
+                    totalCount += counts[1];
1617 1909
                 }
1910
+            }
1911
+
1912
+            if (totalCount > 0) {
1913
+                info.accuracy = BigDecimal.valueOf(totalCorrect)
1914
+                        .multiply(BigDecimal.valueOf(100))
1915
+                        .divide(BigDecimal.valueOf(totalCount), 2, RoundingMode.HALF_UP);
1618 1916
             } else {
1619 1917
                 info.accuracy = BigDecimal.ZERO;
1620 1918
             }
@@ -1747,17 +2045,18 @@ public class AccuracyStatisticsServiceImpl implements IAccuracyStatisticsService
1747 2045
     }
1748 2046
 
1749 2047
     /**
1750
-     * 获取主管显示名称,格式为"大队名+主管名",如"一大队方圆主管"
2048
+     * 获取主管显示名称,格式为"大队名+主管名",如"一大队方圆主管"(使用缓存优化)
1751 2049
      */
1752 2050
     private String getManagerDisplayName(Long managerId, String managerName) {
1753
-        SysDept managerDept = deptService.selectDeptById(managerId);
2051
+        DeptCache cache = getDeptCache();
2052
+        SysDept managerDept = cache != null ? cache.deptMap.get(managerId) : deptService.selectDeptById(managerId);
1754 2053
         if (managerDept == null) {
1755 2054
             return managerName;
1756 2055
         }
1757 2056
         // 获取主管所属大队
1758 2057
         Long brigadeId = managerDept.getParentId();
1759 2058
         if (brigadeId != null) {
1760
-            SysDept brigadeDept = deptService.selectDeptById(brigadeId);
2059
+            SysDept brigadeDept = cache != null ? cache.deptMap.get(brigadeId) : deptService.selectDeptById(brigadeId);
1761 2060
             if (brigadeDept != null && StrUtil.equals(DeptType.BRIGADE.getCode(), brigadeDept.getDeptType())) {
1762 2061
                 return brigadeDept.getDeptName() + managerName;
1763 2062
             }