wangxx 2 днів тому
батько
коміт
865caafa03

+ 7 - 5
airport-blocked/src/main/java/com/sundot/airport/blocked/mapper/BlockedDashboardMapper.java

@@ -5,6 +5,7 @@ import org.apache.ibatis.annotations.Mapper;
5 5
 import org.apache.ibatis.annotations.Param;
6 6
 
7 7
 import java.util.List;
8
+import java.util.Map;
8 9
 
9 10
 /**
10 11
  * 查堵大屏Mapper接口
@@ -233,11 +234,12 @@ public interface BlockedDashboardMapper {
233 234
 
234 235
     /**
235 236
      * 查询大队开机人员证书分布(用于柱状图)
237
+     * 返回原始数据,由Service层按大队分组统计
236 238
      * 
237 239
      * @param queryDTO 查询参数
238
-     * @return 大队证书分布统计列表
240
+     * @return 用户和部门信息列表
239 241
      */
240
-    List<BlockedCertificateLevelDTO> selectBrigadeCertificateDistribution(BlockedDashboardQueryDTO queryDTO);
242
+    List<Map<String, Object>> selectBrigadeCertificateDistribution(BlockedDashboardQueryDTO queryDTO);
241 243
 
242 244
     /**
243 245
      * 查询两楼每日查堵走势(按大队)
@@ -357,12 +359,12 @@ public interface BlockedDashboardMapper {
357 359
     /**
358 360
      * 查询证书级别人员基数(分大队统计)
359 361
      * 数据源:用户表(sys_user) + 部门表(sys_dept)
360
-     * 统计大队人员里面的人员证书级别构成
362
+     * 返回原始数据,由Service层处理
361 363
      * 
362 364
      * @param queryDTO 查询参数(大队ID必填)
363
-     * @return 证书级别人员基数统计列表
365
+     * @return 用户和部门信息列表
364 366
      */
365
-    List<BlockedCertificateLevelDistributionDTO> selectBrigadePersonnelCertificateBase(BlockedDashboardQueryDTO queryDTO);
367
+    List<Map<String, Object>> selectBrigadePersonnelCertificateBase(BlockedDashboardQueryDTO queryDTO);
366 368
 
367 369
     /**
368 370
      * 查询查堵人员开机年限分布(用于饼图)

+ 247 - 20
airport-blocked/src/main/java/com/sundot/airport/blocked/service/impl/BlockedDashboardServiceImpl.java

@@ -34,12 +34,13 @@ import com.sundot.airport.blocked.dto.BlockedTimePeriodLuggageStatsDTO;
34 34
 import com.sundot.airport.blocked.dto.BlockedTimePeriodStatsDTO;
35 35
 import com.sundot.airport.blocked.mapper.BlockedDashboardMapper;
36 36
 import com.sundot.airport.blocked.service.IBlockedDashboardService;
37
+import com.sundot.airport.common.core.domain.entity.SysDept;
38
+import com.sundot.airport.system.mapper.SysDeptMapper;
37 39
 import org.springframework.beans.factory.annotation.Autowired;
38 40
 import org.springframework.stereotype.Service;
39 41
 
40 42
 import java.math.BigDecimal;
41
-import java.util.Collections;
42
-import java.util.List;
43
+import java.util.*;
43 44
 import java.util.stream.Collectors;
44 45
 
45 46
 /**
@@ -53,6 +54,9 @@ public class BlockedDashboardServiceImpl implements IBlockedDashboardService {
53 54
 
54 55
     @Autowired
55 56
     private BlockedDashboardMapper blockedDashboardMapper;
57
+    
58
+    @Autowired
59
+    private SysDeptMapper sysDeptMapper;
56 60
 
57 61
     /**
58 62
      * 查询查堵总数
@@ -476,14 +480,213 @@ public class BlockedDashboardServiceImpl implements IBlockedDashboardService {
476 480
 
477 481
     /**
478 482
      * 查询大队开机人员证书分布(用于柱状图)
483
+     * 通过ancestors字段找到每个用户所属的最上级大队,然后按大队分组统计
479 484
      * 
480 485
      * @param queryDTO 查询参数
481 486
      * @return 大队证书分布统计列表
482 487
      */
483 488
     @Override
484 489
     public List<BlockedCertificateLevelDTO> getBrigadeCertificateDistribution(BlockedDashboardQueryDTO queryDTO) {
485
-        List<BlockedCertificateLevelDTO> resultList = blockedDashboardMapper.selectBrigadeCertificateDistribution(queryDTO);
486
-        return resultList != null ? resultList : Collections.emptyList();
490
+        // 1. 从数据库获取原始数据
491
+        List<Map<String, Object>> rawData = blockedDashboardMapper.selectBrigadeCertificateDistribution(queryDTO);
492
+        
493
+        if (rawData == null || rawData.isEmpty()) {
494
+            return Collections.emptyList();
495
+        }
496
+        
497
+        // 2. 收集所有需要查询的部门ID(包括ancestors中的所有ID和当前dept_id)
498
+        Set<Long> allDeptIds = new HashSet<>();
499
+        for (Map<String, Object> row : rawData) {
500
+            String ancestors = (String) row.get("ancestors");
501
+            Long deptId = ((Number) row.get("dept_id")).longValue();
502
+            
503
+            if (ancestors != null && !ancestors.isEmpty()) {
504
+                String[] ancestorIds = ancestors.split(",");
505
+                for (String idStr : ancestorIds) {
506
+                    try {
507
+                        Long id = Long.parseLong(idStr.trim());
508
+                        if (id > 0) {
509
+                            allDeptIds.add(id);
510
+                        }
511
+                    } catch (NumberFormatException e) {
512
+                        // 忽略
513
+                    }
514
+                }
515
+            }
516
+            if (deptId != null) {
517
+                allDeptIds.add(deptId);
518
+            }
519
+        }
520
+        
521
+        // 3. 批量查询所有部门信息,构建大队ID映射缓存
522
+        Map<Long, Long> deptToBrigadeMap = buildDeptToBrigadeMap(allDeptIds);
523
+        
524
+        // 4. 在Java层按大队分组统计
525
+        Map<Long, BlockedCertificateLevelDTO> brigadeMap = new LinkedHashMap<>();
526
+        
527
+        for (Map<String, Object> row : rawData) {
528
+            String qualificationLevel = (String) row.get("qualification_level");
529
+            Long deptId = ((Number) row.get("dept_id")).longValue();
530
+            
531
+            // 5. 从缓存中获取大队ID
532
+            Long brigadeId = deptToBrigadeMap.get(deptId);
533
+            
534
+            if (brigadeId == null) {
535
+                continue; // 跳过无法找到大队的用户
536
+            }
537
+            
538
+            // 6. 获取或创建大队统计对象
539
+            BlockedCertificateLevelDTO stats = brigadeMap.computeIfAbsent(brigadeId, id -> {
540
+                BlockedCertificateLevelDTO dto = new BlockedCertificateLevelDTO();
541
+                dto.setBrigadeId(id);
542
+                dto.setSeniorCount(0);
543
+                dto.setMiddleCount(0);
544
+                dto.setJuniorCount(0);
545
+                dto.setTotalCount(0);
546
+                return dto;
547
+            });
548
+            
549
+            // 7. 根据证书级别统计
550
+            if ("LEVEL_ONE".equals(qualificationLevel) || 
551
+                "LEVEL_TWO".equals(qualificationLevel) || 
552
+                "LEVEL_THREE".equals(qualificationLevel)) {
553
+                stats.setSeniorCount(stats.getSeniorCount() + 1);
554
+            } else if ("LEVEL_FOUR".equals(qualificationLevel)) {
555
+                stats.setMiddleCount(stats.getMiddleCount() + 1);
556
+            } else if ("LEVEL_FIVE".equals(qualificationLevel)) {
557
+                stats.setJuniorCount(stats.getJuniorCount() + 1);
558
+            }
559
+            
560
+            stats.setTotalCount(stats.getTotalCount() + 1);
561
+        }
562
+        
563
+        // 8. 转换为列表并填充大队名称
564
+        List<BlockedCertificateLevelDTO> resultList = new ArrayList<>(brigadeMap.values());
565
+        fillBrigadeNames(resultList);
566
+        
567
+        // 9. 按总人数降序排序
568
+        resultList.sort((a, b) -> b.getTotalCount().compareTo(a.getTotalCount()));
569
+        
570
+        return resultList;
571
+    }
572
+    
573
+    /**
574
+     * 构建部门到大队的映射关系
575
+     * 遍历所有部门的ancestors,找到每个部门所属的最上级大队
576
+     * 
577
+     * @param allDeptIds 所有需要查询的部门ID集合
578
+     * @return 部门ID -> 大队ID 的映射
579
+     */
580
+    private Map<Long, Long> buildDeptToBrigadeMap(Set<Long> allDeptIds) {
581
+        if (allDeptIds == null || allDeptIds.isEmpty()) {
582
+            return Collections.emptyMap();
583
+        }
584
+        
585
+        // 1. 批量查询所有部门信息
586
+        List<SysDept> allDepts = sysDeptMapper.selectDeptByIdList(new ArrayList<>(allDeptIds));
587
+        
588
+        if (allDepts == null || allDepts.isEmpty()) {
589
+            return Collections.emptyMap();
590
+        }
591
+        
592
+        // 2. 构建部门ID到部门对象的映射
593
+        Map<Long, SysDept> deptMap = new HashMap<>();
594
+        for (SysDept dept : allDepts) {
595
+            deptMap.put(dept.getDeptId(), dept);
596
+        }
597
+        
598
+        // 3. 找出所有大队类型的部门ID
599
+        Set<Long> brigadeIds = new HashSet<>();
600
+        for (SysDept dept : allDepts) {
601
+            if ("BRIGADE".equals(dept.getDeptType())) {
602
+                brigadeIds.add(dept.getDeptId());
603
+            }
604
+        }
605
+        
606
+        // 4. 为每个部门找到其所属的最上级大队
607
+        Map<Long, Long> deptToBrigadeMap = new HashMap<>();
608
+        for (SysDept dept : allDepts) {
609
+            Long brigadeId = findBrigadeIdFromAncestors(dept.getAncestors(), dept.getDeptId(), brigadeIds);
610
+            if (brigadeId != null) {
611
+                deptToBrigadeMap.put(dept.getDeptId(), brigadeId);
612
+            }
613
+        }
614
+        
615
+        return deptToBrigadeMap;
616
+    }
617
+    
618
+    /**
619
+     * 从ancestors中找到最上级大队ID
620
+     * 
621
+     * @param ancestors 祖级列表
622
+     * @param currentDeptId 当前部门ID
623
+     * @param brigadeIds 所有大队ID集合
624
+     * @return 最上级大队ID,如果找不到则返回null
625
+     */
626
+    private Long findBrigadeIdFromAncestors(String ancestors, Long currentDeptId, Set<Long> brigadeIds) {
627
+        if (ancestors == null || ancestors.isEmpty()) {
628
+            return null;
629
+        }
630
+        
631
+        // 将ancestors分割成ID列表
632
+        String[] ancestorIds = ancestors.split(",");
633
+        
634
+        // 遍历祖先节点,找到第一个大队类型的部门
635
+        for (String ancestorIdStr : ancestorIds) {
636
+            try {
637
+                Long ancestorId = Long.parseLong(ancestorIdStr.trim());
638
+                if (ancestorId > 0 && brigadeIds.contains(ancestorId)) {
639
+                    return ancestorId;
640
+                }
641
+            } catch (NumberFormatException e) {
642
+                // 忽略解析错误
643
+            }
644
+        }
645
+        
646
+        // 如果祖先节点中没有找到大队,检查当前部门是否为大队
647
+        if (currentDeptId != null && brigadeIds.contains(currentDeptId)) {
648
+            return currentDeptId;
649
+        }
650
+        
651
+        return null;
652
+    }
653
+    
654
+    /**
655
+     * 填充大队名称(批量查询)
656
+     * 
657
+     * @param resultList 统计结果列表
658
+     */
659
+    private void fillBrigadeNames(List<BlockedCertificateLevelDTO> resultList) {
660
+        if (resultList == null || resultList.isEmpty()) {
661
+            return;
662
+        }
663
+        
664
+        // 收集所有大队ID
665
+        List<Long> brigadeIds = resultList.stream()
666
+                .map(BlockedCertificateLevelDTO::getBrigadeId)
667
+                .filter(Objects::nonNull)
668
+                .distinct()
669
+                .collect(Collectors.toList());
670
+        
671
+        if (brigadeIds.isEmpty()) {
672
+            return;
673
+        }
674
+        
675
+        // 批量查询大队信息
676
+        List<SysDept> brigades = sysDeptMapper.selectDeptByIdList(brigadeIds);
677
+        
678
+        // 构建大队ID到名称的映射
679
+        Map<Long, String> brigadeNameMap = new HashMap<>();
680
+        for (SysDept dept : brigades) {
681
+            brigadeNameMap.put(dept.getDeptId(), dept.getDeptName());
682
+        }
683
+        
684
+        // 填充名称
685
+        for (BlockedCertificateLevelDTO dto : resultList) {
686
+            if (dto.getBrigadeId() != null) {
687
+                dto.setBrigadeName(brigadeNameMap.get(dto.getBrigadeId()));
688
+            }
689
+        }
487 690
     }
488 691
 
489 692
     /**
@@ -665,31 +868,55 @@ public class BlockedDashboardServiceImpl implements IBlockedDashboardService {
665 868
     /**
666 869
      * 查询证书级别人员基数(分大队统计)
667 870
      * 数据源:用户表(sys_user) + 部门表(sys_dept)
668
-     * 统计大队人员里面的人员证书级别构成
669
-     * 
871
+     * 统计指定大队及其下属所有部门的人员证书级别构成
872
+     *
670 873
      * @param queryDTO 查询参数(大队ID必填)
671 874
      * @return 证书级别人员基数统计列表
672 875
      */
673 876
     @Override
674 877
     public List<BlockedCertificateLevelDistributionDTO> getBrigadePersonnelCertificateBase(BlockedDashboardQueryDTO queryDTO) {
675
-        List<BlockedCertificateLevelDistributionDTO> resultList = blockedDashboardMapper.selectBrigadePersonnelCertificateBase(queryDTO);
676
-        if (resultList != null && !resultList.isEmpty()) {
677
-            // 计算总数
678
-            long totalCount = resultList.stream().mapToLong(item -> item.getCount() != null ? item.getCount() : 0).sum();
878
+        // 1. 从数据库获取原始数据
879
+        List<Map<String, Object>> rawData = blockedDashboardMapper.selectBrigadePersonnelCertificateBase(queryDTO);
880
+        
881
+        if (rawData == null || rawData.isEmpty()) {
882
+            return Collections.emptyList();
883
+        }
884
+        
885
+        // 2. 按证书级别分组统计
886
+        Map<String, Long> levelCountMap = new HashMap<>();
887
+        long totalCount = 0;
888
+        
889
+        for (Map<String, Object> row : rawData) {
890
+            String qualificationLevel = (String) row.get("qualification_level");
891
+            if (qualificationLevel != null && !qualificationLevel.isEmpty()) {
892
+                levelCountMap.merge(qualificationLevel, 1L, Long::sum);
893
+                totalCount++;
894
+            }
895
+        }
896
+        
897
+        // 3. 构建结果列表
898
+        List<BlockedCertificateLevelDistributionDTO> resultList = new ArrayList<>();
899
+        for (Map.Entry<String, Long> entry : levelCountMap.entrySet()) {
900
+            BlockedCertificateLevelDistributionDTO dto = new BlockedCertificateLevelDistributionDTO();
901
+            dto.setCertificateLevel(convertCertificateLevel(entry.getKey()));
902
+            dto.setCount(entry.getValue().intValue());
903
+            
679 904
             // 计算百分比
680 905
             if (totalCount > 0) {
681
-                resultList.forEach(item -> {
682
-                    long count = item.getCount() != null ? item.getCount() : 0;
683
-                    double percentage = Math.round(count * 10000.0 / totalCount) / 100.0;
684
-                    item.setPercentage(java.math.BigDecimal.valueOf(percentage));
685
-                    // 转换证书级别为中文
686
-                    if (item.getCertificateLevel() != null) {
687
-                        item.setCertificateLevel(convertCertificateLevel(item.getCertificateLevel()));
688
-                    }
689
-                });
906
+                double percentage = Math.round(entry.getValue() * 10000.0 / totalCount) / 100.0;
907
+                dto.setPercentage(java.math.BigDecimal.valueOf(percentage));
690 908
             }
909
+            
910
+            resultList.add(dto);
691 911
         }
692
-        return resultList != null ? resultList : Collections.emptyList();
912
+        
913
+        // 4. 按数量降序排序
914
+        resultList.sort((a, b) -> Long.compare(
915
+            b.getCount() != null ? b.getCount() : 0,
916
+            a.getCount() != null ? a.getCount() : 0
917
+        ));
918
+        
919
+        return resultList;
693 920
     }
694 921
 
695 922
     /**

+ 19 - 19
airport-blocked/src/main/resources/mapper/blocked/BlockedDashboardMapper.xml

@@ -633,26 +633,22 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
633 633
     </select>
634 634
 
635 635
     <!-- 查询大队开机人员证书分布(用于柱状图) -->
636
-    <select id="selectBrigadeCertificateDistribution" resultType="com.sundot.airport.blocked.dto.BlockedCertificateLevelDTO">
637
-        SELECT 
638
-            d.dept_name as brigadeName,
639
-            d.dept_id as brigadeId,
640
-            COUNT(DISTINCT CASE WHEN u.qualification_level IN ('LEVEL_ONE', 'LEVEL_TWO', 'LEVEL_THREE') THEN u.user_id END) as seniorCount,
641
-            COUNT(DISTINCT CASE WHEN u.qualification_level = 'LEVEL_FOUR' THEN u.user_id END) as middleCount,
642
-            COUNT(DISTINCT CASE WHEN u.qualification_level = 'LEVEL_FIVE' THEN u.user_id END) as juniorCount,
643
-            COUNT(DISTINCT u.user_id) as totalCount
636
+    <!-- 查询大队证书等级分布(按大队) - 返回用户和部门信息,由Java层处理 -->
637
+    <select id="selectBrigadeCertificateDistribution" resultType="java.util.HashMap">
638
+        SELECT 
639
+            u.user_id,
640
+            u.qualification_level,
641
+            d.dept_id,
642
+            d.ancestors
644 643
         FROM sys_user u
645 644
         INNER JOIN sys_dept d ON u.dept_id = d.dept_id
646 645
         WHERE u.del_flag = '0'
647 646
         AND u.status = '0'
648 647
         AND u.qualification_level IS NOT NULL
649 648
         AND u.qualification_level != ''
650
-        AND d.dept_type = 'BRIGADE'
651 649
         <if test="terminalId != null">
652 650
             AND d.terminal_id = #{terminalId}
653 651
         </if>
654
-        GROUP BY d.dept_id, d.dept_name
655
-        ORDER BY totalCount DESC
656 652
     </select>
657 653
 
658 654
     <!-- 查询两楼每日查堵走势(按大队) -->
@@ -932,23 +928,27 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
932 928
         ORDER BY count DESC
933 929
     </select>
934 930
 
935
-    <!-- 查询证书级别人员基数(分大队统计) -->
936
-    <select id="selectBrigadePersonnelCertificateBase" resultType="com.sundot.airport.blocked.dto.BlockedCertificateLevelDistributionDTO">
931
+    <!-- 查询证书级别人员基数(分大队统计)-->
932
+    <select id="selectBrigadePersonnelCertificateBase" resultType="java.util.HashMap">
937 933
         SELECT 
938
-            u.qualification_level as certificateLevel,
939
-            COUNT(DISTINCT u.user_id) as count
934
+            u.user_id,
935
+            u.qualification_level,
936
+            d.dept_id,
937
+            d.ancestors
940 938
         FROM sys_user u
941 939
         INNER JOIN sys_dept d ON u.dept_id = d.dept_id
942 940
         WHERE u.del_flag = '0'
943 941
         AND u.status = '0'
944 942
         AND u.qualification_level IS NOT NULL
945 943
         AND u.qualification_level != ''
946
-        AND d.dept_type = 'BRIGADE'
947 944
         <if test="brigadeId != null">
948
-            AND d.dept_id = #{brigadeId}
945
+            AND (
946
+                d.dept_id = #{brigadeId}
947
+                OR d.ancestors LIKE CONCAT('%,', #{brigadeId}, ',%')
948
+                OR d.ancestors LIKE CONCAT(#{brigadeId}, ',%')
949
+                OR d.ancestors LIKE CONCAT('%,', #{brigadeId})
950
+            )
949 951
         </if>
950
-        GROUP BY u.qualification_level
951
-        ORDER BY count DESC
952 952
     </select>
953 953
 
954 954
     <!-- 查询查堵-主管分管次数分布(用于饼图) -->