소스 검색

部门画像--站画像--团队画像统计

wangxx 3 주 전
부모
커밋
cf9cc25469

+ 37 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/score/DeptPortraitController.java

@@ -6,6 +6,7 @@ import com.sundot.airport.ledger.dto.DeptMemberDistributionDTO;
6 6
 import com.sundot.airport.ledger.dto.DeptMemberDTO;
7 7
 import com.sundot.airport.ledger.dto.DeptPortraitQueryDTO;
8 8
 import com.sundot.airport.ledger.dto.GroupPortraitDTO;
9
+import com.sundot.airport.ledger.dto.StationTeamStatsDTO;
9 10
 import com.sundot.airport.ledger.service.IDeptPortraitService;
10 11
 import com.sundot.airport.ledger.service.IGroupPortraitService;
11 12
 import io.swagger.annotations.Api;
@@ -177,4 +178,40 @@ public class DeptPortraitController extends BaseController {
177 178
             return error("获取组织画像失败:" + e.getMessage());
178 179
         }
179 180
     }
181
+
182
+    /**
183
+     * 获取站级别下所有部门的团队画像统计
184
+     * <p>
185
+     * 展示站下各部门的:
186
+     * 部门名称、员工数量、党员数量、平均年龄、平均工龄、
187
+     * 职业资格证书等级、平均升级年龄、综合得分
188
+     * </p>
189
+     *
190
+     * @param query 查询参数对象
191
+     * @return 站下各部门的团队画像统计列表
192
+     */
193
+    @ApiOperation("获取站级别下所有部门的团队画像统计")
194
+    @PostMapping("/station-team-stats")
195
+    public AjaxResult getStationTeamStats(@RequestBody DeptPortraitQueryDTO query) {
196
+        try {
197
+            // 参数校验
198
+            if (query == null || query.getDeptId() == null) {
199
+                return error("部门ID不能为空");
200
+            }
201
+
202
+            // 如果没有传日期范围,则默认为91天
203
+            if (query.getStartDate() == null || query.getEndDate() == null) {
204
+                java.time.LocalDate today = java.time.LocalDate.now();
205
+                query.setEndDate(today.toString());
206
+                query.setStartDate(today.minusDays(91).toString());
207
+            }
208
+
209
+            List<StationTeamStatsDTO> stats = deptPortraitService.getStationTeamStats(query);
210
+
211
+            return success(stats);
212
+        } catch (Exception e) {
213
+            logger.error("获取站团队画像统计失败", e);
214
+            return error("获取站团队画像统计失败:" + e.getMessage());
215
+        }
216
+    }
180 217
 }

+ 65 - 0
airport-ledger/src/main/java/com/sundot/airport/ledger/dto/StationTeamStatsDTO.java

@@ -0,0 +1,65 @@
1
+package com.sundot.airport.ledger.dto;
2
+
3
+import lombok.Data;
4
+
5
+import java.io.Serializable;
6
+import java.math.BigDecimal;
7
+
8
+/**
9
+ * <b>功能名:</b>StationTeamStatsDTO<br>
10
+ * <b>说明:</b> 站级别团队画像统计DTO(展示站下各部门的团队成员统计信息)<br>
11
+ * <b>著作权:</b> Copyright (C) 2025 SUNDOT CORPORATION<br>
12
+ *
13
+ * @author Claude
14
+ */
15
+@Data
16
+public class StationTeamStatsDTO implements Serializable {
17
+
18
+    private static final long serialVersionUID = 1L;
19
+
20
+    /**
21
+     * 部门ID
22
+     */
23
+    private Long deptId;
24
+
25
+    /**
26
+     * 部门名称
27
+     */
28
+    private String deptName;
29
+
30
+    /**
31
+     * 员工数量
32
+     */
33
+    private Integer employeeCount;
34
+
35
+    /**
36
+     * 党员数量
37
+     */
38
+    private Integer partyMemberCount;
39
+
40
+    /**
41
+     * 平均年龄
42
+     */
43
+    private Double avgAge;
44
+
45
+    /**
46
+     * 平均工龄
47
+     */
48
+    private Double avgWorkYears;
49
+
50
+    /**
51
+     * 平均开机年限
52
+     */
53
+    private Double avgXrayOperatorYears;
54
+
55
+    /**
56
+     * 职业资格证书等级
57
+     */
58
+    private String qualificationLevel;
59
+
60
+
61
+    /**
62
+     * 综合得分
63
+     */
64
+    private BigDecimal totalScore;
65
+}

+ 14 - 0
airport-ledger/src/main/java/com/sundot/airport/ledger/service/IDeptPortraitService.java

@@ -3,6 +3,7 @@ package com.sundot.airport.ledger.service;
3 3
 import com.sundot.airport.ledger.dto.DeptMemberDistributionDTO;
4 4
 import com.sundot.airport.ledger.dto.DeptMemberDTO;
5 5
 import com.sundot.airport.ledger.dto.DeptPortraitQueryDTO;
6
+import com.sundot.airport.ledger.dto.StationTeamStatsDTO;
6 7
 
7 8
 import java.util.List;
8 9
 
@@ -54,4 +55,17 @@ public interface IDeptPortraitService {
54 55
      * @return 成员职位情况分布数据
55 56
      */
56 57
     DeptMemberDistributionDTO getPositionDistribution(DeptPortraitQueryDTO query);
58
+
59
+    /**
60
+     * 获取站级别下所有部门的团队画像统计
61
+     * <p>
62
+     * 查询指定站(STATION)下所有部门(BRIGADE)的团队成员统计信息,包括:
63
+     * 部门名称、员工数量、党员数量、平均年龄、平均工龄、
64
+     * 职业资格证书等级、平均升级年龄、综合得分
65
+     * </p>
66
+     *
67
+     * @param query 查询参数对象(deptId=站ID, startDate, endDate)
68
+     * @return 站下各部门的团队画像统计列表
69
+     */
70
+    List<StationTeamStatsDTO> getStationTeamStats(DeptPortraitQueryDTO query);
57 71
 }

+ 139 - 1
airport-ledger/src/main/java/com/sundot/airport/ledger/service/impl/DeptPortraitServiceImpl.java

@@ -2,16 +2,22 @@ package com.sundot.airport.ledger.service.impl;
2 2
 
3 3
 import com.sundot.airport.common.core.domain.entity.SysRole;
4 4
 import com.sundot.airport.common.core.domain.entity.SysUser;
5
+import com.sundot.airport.common.core.domain.entity.SysDept;
6
+import com.sundot.airport.common.enums.DeptTypeEnum;
5 7
 import com.sundot.airport.ledger.domain.ScoreDimension;
6 8
 import com.sundot.airport.ledger.domain.ScoreEvent;
7 9
 import com.sundot.airport.ledger.dto.DeptMemberDistributionDTO;
8 10
 import com.sundot.airport.ledger.dto.DeptMemberDTO;
9 11
 import com.sundot.airport.ledger.dto.DeptPortraitQueryDTO;
12
+import com.sundot.airport.ledger.dto.StationTeamStatsDTO;
10 13
 import com.sundot.airport.ledger.mapper.ScoreDimensionMapper;
11 14
 import com.sundot.airport.ledger.mapper.ScoreEventMapper;
15
+import com.sundot.airport.ledger.dto.GroupPortraitDTO;
12 16
 import com.sundot.airport.ledger.service.IDeptPortraitService;
17
+import com.sundot.airport.ledger.service.IGroupPortraitService;
13 18
 import com.sundot.airport.system.domain.BasePosition;
14 19
 import com.sundot.airport.system.mapper.BasePositionMapper;
20
+import com.sundot.airport.system.mapper.SysDeptMapper;
15 21
 import com.sundot.airport.system.mapper.SysRoleMapper;
16 22
 import com.sundot.airport.system.mapper.SysUserMapper;
17 23
 import org.slf4j.Logger;
@@ -44,6 +50,9 @@ public class DeptPortraitServiceImpl implements IDeptPortraitService {
44 50
     private SysUserMapper sysUserMapper;
45 51
 
46 52
     @Autowired
53
+    private SysDeptMapper sysDeptMapper;
54
+
55
+    @Autowired
47 56
     private SysRoleMapper sysRoleMapper;
48 57
 
49 58
     @Autowired
@@ -55,6 +64,9 @@ public class DeptPortraitServiceImpl implements IDeptPortraitService {
55 64
     @Autowired
56 65
     private BasePositionMapper basePositionMapper;
57 66
 
67
+    @Autowired
68
+    private IGroupPortraitService groupPortraitService;
69
+
58 70
     @Override
59 71
     public List<DeptMemberDTO> getDeptMembers(DeptPortraitQueryDTO query) {
60 72
         // 递归查询所有下级部门的用户
@@ -359,6 +371,132 @@ public class DeptPortraitServiceImpl implements IDeptPortraitService {
359 371
         return total.setScale(1, RoundingMode.HALF_UP);
360 372
     }
361 373
 
374
+    @Override
375
+    public List<StationTeamStatsDTO> getStationTeamStats(DeptPortraitQueryDTO query) {
376
+        // 1. 验证是站级别
377
+        SysDept station = sysDeptMapper.selectDeptById(query.getDeptId());
378
+        if (station == null || !"STATION".equals(station.getDeptType())) {
379
+            throw new RuntimeException("请选择站级别部门");
380
+        }
381
+
382
+        // 2. 查询站下所有部门(BRIGADE)
383
+        SysDept deptQuery = new SysDept();
384
+        deptQuery.setParentId(query.getDeptId());
385
+        deptQuery.setDeptType(DeptTypeEnum.BRIGADE.getCode());
386
+        deptQuery.setStatus("0");
387
+        List<SysDept> brigades = sysDeptMapper.selectDeptList(deptQuery);
388
+
389
+        if (brigades == null || brigades.isEmpty()) {
390
+            return Collections.emptyList();
391
+        }
392
+
393
+        // 3. 为每个部门计算统计信息
394
+        List<StationTeamStatsDTO> result = new ArrayList<>();
395
+        for (SysDept brigade : brigades) {
396
+            StationTeamStatsDTO stats = calculateDeptStats(brigade, query.getStartDate(), query.getEndDate());
397
+            if (stats != null) {
398
+                result.add(stats);
399
+            }
400
+        }
401
+
402
+        return result;
403
+    }
404
+
405
+    /**
406
+     * 计算单个部门的团队画像统计信息
407
+     */
408
+    private StationTeamStatsDTO calculateDeptStats(SysDept dept, String startDate, String endDate) {
409
+        // 查询部门所有成员
410
+        SysUser userQuery = new SysUser();
411
+        userQuery.setDeptId(dept.getDeptId());
412
+        userQuery.setStatus("0");
413
+        List<SysUser> members = sysUserMapper.selectUserList(userQuery);
414
+
415
+        if (members == null || members.isEmpty()) {
416
+            return null;
417
+        }
418
+
419
+        StationTeamStatsDTO stats = new StationTeamStatsDTO();
420
+        stats.setDeptId(dept.getDeptId());
421
+        stats.setDeptName(dept.getDeptName());
422
+        stats.setEmployeeCount(members.size());
423
+
424
+        // 计算各项统计
425
+        int partyCount = 0;
426
+        int totalAge = 0;
427
+        int totalWorkYears = 0;
428
+        int totalXrayOperatorYears = 0;
429
+        int validAgeCount = 0;
430
+        int validWorkYearsCount = 0;
431
+        int validXrayOperatorYearsCount = 0;
432
+        Map<String, Integer> qualLevelCount = new HashMap<>();
433
+        
434
+        for (SysUser user : members) {
435
+            // 党员数量(中共党员 + 中共预备党员)
436
+            String politicalStatus = decodePoliticalStatus(user.getPoliticalStatus());
437
+            if ("中共党员".equals(politicalStatus) || "中共预备党员".equals(politicalStatus)) {
438
+                partyCount++;
439
+            }
440
+        
441
+            // 年龄
442
+            Integer age = calculateAgeFromIdCard(user.getCardNumber());
443
+            if (age != null && age > 0) {
444
+                totalAge += age;
445
+                validAgeCount++;
446
+            }
447
+        
448
+            // 工龄(从入职时间计算)
449
+            if (user.getEntryDate() != null) {
450
+                Integer workYears = calculateYears(user.getEntryDate());
451
+                if (workYears != null) {
452
+                    totalWorkYears += workYears;
453
+                    validWorkYearsCount++;
454
+                }
455
+            }
456
+        
457
+            // 开机年限(从开机时间计算)
458
+            if (user.getXrayOperatorStarttime() != null) {
459
+                Integer xrayYears = calculateYears(user.getXrayOperatorStarttime());
460
+                if (xrayYears != null) {
461
+                    totalXrayOperatorYears += xrayYears;
462
+                    validXrayOperatorYearsCount++;
463
+                }
464
+            }
465
+        
466
+            // 职业资格等级
467
+            if (user.getQualificationLevel() != null && !user.getQualificationLevel().isEmpty()) {
468
+                String level = decodeQualificationLevel(user.getQualificationLevel());
469
+                qualLevelCount.merge(level, 1, Integer::sum);
470
+            }
471
+        }
472
+
473
+        stats.setPartyMemberCount(partyCount);
474
+        stats.setAvgAge(validAgeCount > 0 ? Math.round((double) totalAge / validAgeCount * 10) / 10.0 : 0);
475
+        stats.setAvgWorkYears(validWorkYearsCount > 0 ? Math.round((double) totalWorkYears / validWorkYearsCount * 10) / 10.0 : 0);
476
+        stats.setAvgXrayOperatorYears(validXrayOperatorYearsCount > 0 ? Math.round((double) totalXrayOperatorYears / validXrayOperatorYearsCount * 10) / 10.0 : 0);
477
+
478
+        // 职业资格证书等级(总个数)
479
+        int totalQualificationCount = qualLevelCount.values().stream()
480
+                .mapToInt(Integer::intValue)
481
+                .sum();
482
+        stats.setQualificationLevel(String.valueOf(totalQualificationCount));
483
+
484
+        // 计算综合得分(使用维度得分一览的总分逻辑)
485
+        try {
486
+            DeptPortraitQueryDTO portraitQuery = new DeptPortraitQueryDTO();
487
+            portraitQuery.setDeptId(dept.getDeptId());
488
+            portraitQuery.setStartDate(startDate);
489
+            portraitQuery.setEndDate(endDate);
490
+            GroupPortraitDTO portrait = groupPortraitService.getGroupPortrait(portraitQuery);
491
+            stats.setTotalScore(portrait.getTotalScore() != null ? portrait.getTotalScore() : BigDecimal.ZERO);
492
+        } catch (Exception e) {
493
+            log.warn("计算部门[{}]综合得分失败: {}", dept.getDeptName(), e.getMessage());
494
+            stats.setTotalScore(BigDecimal.ZERO);
495
+        }
496
+
497
+        return stats;
498
+    }
499
+
362 500
     private Integer calculateAgeFromIdCard(String idCard) {
363 501
         if (idCard == null || idCard.length() < 14) {
364 502
             return null;
@@ -405,4 +543,4 @@ public class DeptPortraitServiceImpl implements IDeptPortraitService {
405 543
             default: return v;
406 544
         }
407 545
     }
408
-}
546
+}