소스 검색

1、PC端绩效管理
2、绩效统计归一化公式变动

chenshudong 1 개월 전
부모
커밋
3918376604

+ 36 - 50
airport-admin/src/main/java/com/sundot/airport/web/controller/item/PerformanceDimensionController.java

@@ -64,11 +64,6 @@ public class PerformanceDimensionController {
64 64
     @Autowired
65 65
     private SysUserMapper userMapper;
66 66
 
67
-    //先写死,后期可能会变
68
-    private static final double a = 3;
69
-    private static final double b = 5;
70
-    private static final double c = 2;
71
-
72 67
     /**
73 68
      * 基于时间维度的绩效统计查询
74 69
      * 返回时间维度组织的数据结构:时间 -> [对象名称, 数值] 列表
@@ -179,21 +174,19 @@ public class PerformanceDimensionController {
179 174
             sortAndLimitDataItemsByAverage(timeDataList, param);
180 175
         }
181 176
 
182
-        // 根据维度类型决定是否限制数量
183
-        if (param.getDimension() != null && param.getDimension() != 4) {
184
-            // 对上个月的数据进行处理
185
-            if (lastTimeDataList != null && !lastTimeDataList.isEmpty()) {
177
+        // 对上个月数据进行排序和限制
178
+        if (lastTimeDataList != null && !lastTimeDataList.isEmpty()) {
179
+            if (param.getDimension() != null && param.getDimension() != 4) {
186 180
                 if (param.getDimensionType() == 1) {
187 181
                     sortDataItemsByProgressOnly(lastTimeDataList, param);
188 182
                 } else {
189 183
                     sortDataItemsByAverageOnly(lastTimeDataList, param);
190 184
                 }
191
-                Map<Long, List<BigDecimal>> idToValuesMap = new HashMap<>();
192
-                for (TimeBasedPerformanceResult.TimeData timeData : lastTimeDataList) {
193
-                    for (TimeBasedPerformanceResult.DataItem item : timeData.getDataItems()) {
194
-                        idToValuesMap.computeIfAbsent(item.getId(), k -> new ArrayList<>())
195
-                                .add(item.getValue() != null ? item.getValue() : BigDecimal.ZERO);
196
-                    }
185
+            }
186
+            for (TimeBasedPerformanceResult.TimeData timeData : lastTimeDataList) {
187
+                for (TimeBasedPerformanceResult.DataItem item : timeData.getDataItems()) {
188
+                    Long id = item.getId();
189
+                    lastMonthValues.putIfAbsent(id, item.getValue() != null ? item.getValue() : BigDecimal.ZERO);
197 190
                 }
198 191
             }
199 192
         }
@@ -828,7 +821,7 @@ public class PerformanceDimensionController {
828 821
         for (LargeScreenDateUtils.DateRange range : timeRanges) {
829 822
             PerformanceMetricsParamDto param = new PerformanceMetricsParamDto();
830 823
             param.setStartTime(range.getStartDate());
831
-            param.setEndTime(range.getEndDate());
824
+            param.setEndTime(DateUtils.addDays(range.getEndDate(), 1));
832 825
             param.setDimension(dimension);
833 826
             List<PerformanceDimensionResult> performanceDimensionResults = calculateCurrentPeriodResults(param);
834 827
             String timeKey = getTimeRangeKey(range);
@@ -1375,24 +1368,25 @@ public class PerformanceDimensionController {
1375 1368
             // 获取各项指标原始值
1376 1369
             BigDecimal seizureRaw = seizureData.stream().filter(item -> item.getId().equals(userId)).map(SeizureEfficiencyRankDto::getEfficiency).findFirst().orElse(BigDecimal.ZERO);
1377 1370
             BigDecimal inspectionRaw = inspectionData.stream().filter(item -> item.getId().equals(userId)).map(CheckEfficiencyRankDto::getPassRate).findFirst().orElse(BigDecimal.ZERO);
1378
-            BigDecimal trainingRaw = normalizeFromMap(userId, trainingData);
1371
+            BigDecimal trainingRaw = new BigDecimal("0.8").add(new BigDecimal("0.2").multiply(normalizeFromMap(userId, trainingData)));
1379 1372
 
1380 1373
             // 标准化处理(线性归一化)
1381
-            normalized.seizureEfficiency = normalizeValue(seizureRaw,
1374
+            normalized.seizureEfficiency = new BigDecimal("0.8").add(new BigDecimal("0.2").multiply(normalizeValue(seizureRaw,
1382 1375
                     getMinValue(seizureData, SeizureEfficiencyRankDto::getEfficiency),
1383
-                    getMaxValue(seizureData, SeizureEfficiencyRankDto::getEfficiency), "w1");
1376
+                    getMaxValue(seizureData, SeizureEfficiencyRankDto::getEfficiency))));
1384 1377
 
1385
-            normalized.inspectionPassRate = normalizeValue(inspectionRaw,
1378
+            normalized.inspectionPassRate = new BigDecimal("0.8").add(new BigDecimal("0.2").multiply(normalizeValue(inspectionRaw,
1386 1379
                     getMinValue(inspectionData, CheckEfficiencyRankDto::getPassRate),
1387
-                    getMaxValue(inspectionData, CheckEfficiencyRankDto::getPassRate), "w2");
1388
-
1389
-
1390
-            normalized.trainingScore = calculateWithWeights(trainingRaw, "w3");
1380
+                    getMaxValue(inspectionData, CheckEfficiencyRankDto::getPassRate))));
1391 1381
 
1392 1382
             // 计算总分
1393
-            normalized.totalScore = normalized.seizureEfficiency
1394
-                    .add(normalized.inspectionPassRate)
1395
-                    .add(normalized.trainingScore);
1383
+            normalized.totalScore = calculateWithWeights(normalized.seizureEfficiency, "w1")
1384
+                    .add(calculateWithWeights(normalized.inspectionPassRate, "w2"))
1385
+                    .add(calculateWithWeights(trainingRaw, "w3"));
1386
+
1387
+            normalized.trainingScore = trainingRaw.multiply(new BigDecimal(100));
1388
+            normalized.seizureEfficiency = normalized.seizureEfficiency.multiply(new BigDecimal(100));
1389
+            normalized.inspectionPassRate = normalized.inspectionPassRate.multiply(new BigDecimal(100));
1396 1390
 
1397 1391
             results.add(normalized);
1398 1392
         }
@@ -1449,24 +1443,26 @@ public class PerformanceDimensionController {
1449 1443
             // 获取各项指标原始值
1450 1444
             BigDecimal seizureRaw = seizureData.stream().filter(item -> item.getId().equals(groupId)).map(SeizureEfficiencyRankDto::getEfficiency).findFirst().orElse(BigDecimal.ZERO);
1451 1445
             BigDecimal inspectionRaw = inspectionData.stream().filter(item -> item.getId().equals(groupId)).map(CheckEfficiencyRankDto::getPassRate).findFirst().orElse(BigDecimal.ZERO);
1452
-            BigDecimal trainingRaw = normalizeFromMap(groupId, trainingData);
1446
+            BigDecimal trainingRaw = new BigDecimal("0.8").add(new BigDecimal("0.2").multiply(normalizeFromMap(groupId, trainingData)));
1453 1447
 
1454 1448
 
1455 1449
             // 标准化处理
1456
-            normalized.seizureEfficiency = normalizeValue(seizureRaw,
1450
+            normalized.seizureEfficiency = new BigDecimal("0.8").add(new BigDecimal("0.2").multiply(normalizeValue(seizureRaw,
1457 1451
                     getMinValue(seizureData, SeizureEfficiencyRankDto::getEfficiency),
1458
-                    getMaxValue(seizureData, SeizureEfficiencyRankDto::getEfficiency), "w1");
1452
+                    getMaxValue(seizureData, SeizureEfficiencyRankDto::getEfficiency))));
1459 1453
 
1460
-            normalized.inspectionPassRate = normalizeValue(inspectionRaw,
1454
+            normalized.inspectionPassRate = new BigDecimal("0.8").add(new BigDecimal("0.2").multiply(normalizeValue(inspectionRaw,
1461 1455
                     getMinValue(inspectionData, CheckEfficiencyRankDto::getPassRate),
1462
-                    getMaxValue(inspectionData, CheckEfficiencyRankDto::getPassRate), "w2");
1463
-
1464
-            normalized.trainingScore = calculateWithWeights(trainingRaw, "w3");
1456
+                    getMaxValue(inspectionData, CheckEfficiencyRankDto::getPassRate))));
1465 1457
 
1466 1458
             // 计算总分
1467
-            normalized.totalScore = normalized.seizureEfficiency
1468
-                    .add(normalized.inspectionPassRate)
1469
-                    .add(normalized.trainingScore);
1459
+            normalized.totalScore = calculateWithWeights(normalized.seizureEfficiency, "w1")
1460
+                    .add(calculateWithWeights(normalized.inspectionPassRate, "w2"))
1461
+                    .add(calculateWithWeights(trainingRaw, "w3"));
1462
+
1463
+            normalized.trainingScore = trainingRaw.multiply(new BigDecimal(100));
1464
+            normalized.seizureEfficiency = normalized.seizureEfficiency.multiply(new BigDecimal(100));
1465
+            normalized.inspectionPassRate = normalized.inspectionPassRate.multiply(new BigDecimal(100));
1470 1466
 
1471 1467
             results.add(normalized);
1472 1468
         }
@@ -1559,7 +1555,7 @@ public class PerformanceDimensionController {
1559 1555
                 .orElse(BigDecimal.ONE);
1560 1556
     }
1561 1557
 
1562
-    private BigDecimal normalizeValue(BigDecimal rawValue, BigDecimal minValue, BigDecimal maxValue, String w) {
1558
+    private BigDecimal normalizeValue(BigDecimal rawValue, BigDecimal minValue, BigDecimal maxValue) {
1563 1559
         if (rawValue == null) rawValue = BigDecimal.ZERO;
1564 1560
         BigDecimal range = maxValue.subtract(minValue);
1565 1561
         if (range.compareTo(BigDecimal.ZERO) == 0) {
@@ -1571,7 +1567,7 @@ public class PerformanceDimensionController {
1571 1567
         BigDecimal divide = rawValue.subtract(minValue)
1572 1568
                 .divide(range, 2, RoundingMode.HALF_UP);
1573 1569
 
1574
-        return calculateWithWeights(divide, w);
1570
+        return divide;
1575 1571
     }
1576 1572
 
1577 1573
 
@@ -1580,7 +1576,7 @@ public class PerformanceDimensionController {
1580 1576
      */
1581 1577
     private BigDecimal calculateWithWeights(BigDecimal value, String w) {
1582 1578
         BigDecimal bigDecimal = value != null ? value : BigDecimal.ZERO;
1583
-        GeometricMeanUtils.CalculationResult calculationResult = GeometricMeanUtils.calculateWithWeights(a, b, c);
1579
+        GeometricMeanUtils.CalculationResult calculationResult = GeometricMeanUtils.calculateWithWeights();
1584 1580
         double d = 0;
1585 1581
         if ("w1".equals(w)) {
1586 1582
             d = calculationResult.getW1();
@@ -1607,16 +1603,6 @@ public class PerformanceDimensionController {
1607 1603
     }
1608 1604
 
1609 1605
     /**
1610
-     * 按月统计数据内部类
1611
-     */
1612
-    private static class MonthlyData {
1613
-        String name;
1614
-        List<BigDecimal> monthlyValues;
1615
-        BigDecimal totalValue;
1616
-        BigDecimal progressValue; // 进步值 = 最后一个月 - 第一个月
1617
-    }
1618
-
1619
-    /**
1620 1606
      * 获取所有安检员用户ID
1621 1607
      */
1622 1608
     private List<Long> getAllInspectorUserIds() {

+ 291 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/item/PerformanceIndicatorsController.java

@@ -0,0 +1,291 @@
1
+package com.sundot.airport.web.controller.item;
2
+
3
+import cn.hutool.core.collection.CollectionUtil;
4
+import cn.hutool.core.date.DateUtil;
5
+import com.sundot.airport.common.core.controller.BaseController;
6
+import com.sundot.airport.common.core.domain.AjaxResult;
7
+import com.sundot.airport.common.dto.*;
8
+import com.sundot.airport.common.utils.GeometricMeanUtils;
9
+import com.sundot.airport.common.utils.poi.ExcelUtil;
10
+import com.sundot.airport.system.domain.SysConfig;
11
+import com.sundot.airport.system.service.ISysConfigService;
12
+import io.swagger.annotations.Api;
13
+import lombok.extern.slf4j.Slf4j;
14
+import org.springframework.beans.factory.annotation.Autowired;
15
+import org.springframework.web.bind.annotation.*;
16
+
17
+import javax.servlet.http.HttpServletResponse;
18
+import java.math.BigDecimal;
19
+import java.util.*;
20
+import java.util.stream.Collectors;
21
+
22
+/**
23
+ * 绩效指标管理 Controller
24
+ *
25
+ * @author sundot
26
+ * @date 2026-03-16
27
+ */
28
+@Slf4j
29
+@RestController
30
+@RequestMapping("/item/indicators")
31
+@Api(tags = "绩效指标管理")
32
+public class PerformanceIndicatorsController extends BaseController {
33
+
34
+    @Autowired
35
+    private ISysConfigService configService;
36
+
37
+    @Autowired
38
+    private PerformanceMetricsController performanceMetricsController;
39
+
40
+
41
+    // 绩效矩阵参数的配置键名
42
+    private static final String CONFIG_KEY_PARAM_A = "item.performance.matrix.param_a";
43
+    private static final String CONFIG_KEY_PARAM_B = "item.performance.matrix.param_b";
44
+    private static final String CONFIG_KEY_PARAM_C = "item.performance.matrix.param_c";
45
+
46
+
47
+    /**
48
+     * 保存绩效矩阵
49
+     * 保存前端传入的绩效矩阵参数 a、b、c
50
+     * 默认值 a=3, b=5, c=2
51
+     *
52
+     * @param a
53
+     * @param b
54
+     * @param c
55
+     * @return 保存结果
56
+     */
57
+    @GetMapping("/matrix/add")
58
+    public AjaxResult performanceMatrix(
59
+            @RequestParam(value = "a", defaultValue = "3") BigDecimal a,
60
+            @RequestParam(value = "b", defaultValue = "5") BigDecimal b,
61
+            @RequestParam(value = "c", defaultValue = "2") BigDecimal c) {
62
+
63
+        try {
64
+            // 保存到 sys_config 表
65
+            saveConfig(CONFIG_KEY_PARAM_A, "绩效矩阵参数 a", a.toString());
66
+            saveConfig(CONFIG_KEY_PARAM_B, "绩效矩阵参数 b", b.toString());
67
+            saveConfig(CONFIG_KEY_PARAM_C, "绩效矩阵参数 c", c.toString());
68
+
69
+            // 同步更新到 GeometricMeanUtils
70
+            GeometricMeanUtils.setParams(a.doubleValue(), b.doubleValue(), c.doubleValue());
71
+
72
+            return success("保存成功");
73
+        } catch (Exception e) {
74
+            log.error("保存绩效矩阵参数失败:{}", e.getMessage());
75
+            return error("保存失败:" + e.getMessage());
76
+        }
77
+    }
78
+
79
+    /**
80
+     * 获取绩效矩阵参数配置
81
+     *
82
+     * @return 绩效矩阵参数配置
83
+     */
84
+    @GetMapping("/matrix/config")
85
+    public AjaxResult getMatrixConfig() {
86
+        try {
87
+            String paramA = configService.selectConfigByKey(CONFIG_KEY_PARAM_A);
88
+            String paramB = configService.selectConfigByKey(CONFIG_KEY_PARAM_B);
89
+            String paramC = configService.selectConfigByKey(CONFIG_KEY_PARAM_C);
90
+
91
+            // 如果不存在,返回默认值
92
+            if (paramA == null || paramA.isEmpty()) paramA = "3";
93
+            if (paramB == null || paramB.isEmpty()) paramB = "5";
94
+            if (paramC == null || paramC.isEmpty()) paramC = "2";
95
+
96
+            Map<String, Object> result = new HashMap<>();
97
+            result.put("paramA", new BigDecimal(paramA));
98
+            result.put("paramB", new BigDecimal(paramB));
99
+            result.put("paramC", new BigDecimal(paramC));
100
+
101
+            return success(result);
102
+        } catch (Exception e) {
103
+            log.error("获取绩效矩阵参数失败:{}", e.getMessage());
104
+            return error("获取失败:" + e.getMessage());
105
+        }
106
+    }
107
+
108
+    /**
109
+     * 绩效查询
110
+     * 调用现有的 /metrics 接口获取绩效数据
111
+     *
112
+     * @param param 查询参数
113
+     * @return 绩效数据列表
114
+     */
115
+    @GetMapping("/query")
116
+    public AjaxResult queryPerformance(PerformanceMetricsParamDto param) {
117
+        try {
118
+            // 设置默认值
119
+            if (param.getDimension() == null) {
120
+                param.setDimension(4); // 默认查询大队
121
+            }
122
+            if (param.getSortOrder() == null) {
123
+                param.setSortOrder(2); // 默认降序
124
+            }
125
+
126
+            return performanceMetricsController.getPerformanceMetrics(param);
127
+        } catch (Exception e) {
128
+            log.error("绩效查询失败:{}", e.getMessage());
129
+            return AjaxResult.error("查询失败:" + e.getMessage());
130
+        }
131
+    }
132
+
133
+
134
+    /**
135
+     * 导出绩效数据
136
+     * 根据维度不同导出不同的内容
137
+     *
138
+     * @param response HTTP 响应
139
+     * @param param    查询参数
140
+     */
141
+    @GetMapping("/export")
142
+    public void exportPerformance(HttpServletResponse response, PerformanceMetricsParamDto param) {
143
+        // 设置默认值
144
+        if (param.getDimension() == null) {
145
+            param.setDimension(4); // 默认查询大队
146
+        }
147
+        if (param.getSortOrder() == null) {
148
+            param.setSortOrder(2); // 默认降序
149
+        }
150
+
151
+        // 查询绩效数据
152
+        AjaxResult result = performanceMetricsController.getPerformanceMetrics(param);
153
+        List<PerformanceMetricsResultDto> dataList = (List<PerformanceMetricsResultDto>) result.get("data");
154
+
155
+        String fileName = "绩效数据";
156
+
157
+        // 根据维度转换数据
158
+        if (param.getDimension() == 1) {
159
+            // 人员维度 - 导出个人绩效(包含姓名、班组名、科室名、大队名)
160
+            List<PerformanceMetricsPersonalExportDto> exportList = convertToPersonalExportList(dataList, param.getStartTime(), param.getEndTime());
161
+            ExcelUtil<PerformanceMetricsPersonalExportDto> util = new ExcelUtil<>(PerformanceMetricsPersonalExportDto.class);
162
+            util.exportExcel(response, exportList, fileName);
163
+        } else if (param.getDimension() == 2) {
164
+            // 班组维度 - 导出班组绩效(包含班组名、科室名、大队名)
165
+            List<PerformanceMetricsTeamExportDto> exportList = convertToTeamExportList(dataList, param.getStartTime(), param.getEndTime());
166
+            ExcelUtil<PerformanceMetricsTeamExportDto> util = new ExcelUtil<>(PerformanceMetricsTeamExportDto.class);
167
+            util.exportExcel(response, exportList, fileName);
168
+        } else if (param.getDimension() == 3) {
169
+            // 科室维度 - 导出科室绩效(包含科室名、大队名)
170
+            List<PerformanceMetricsDeptExportDto> exportList = convertToDeptExportList(dataList, param.getStartTime(), param.getEndTime());
171
+            ExcelUtil<PerformanceMetricsDeptExportDto> util = new ExcelUtil<>(PerformanceMetricsDeptExportDto.class);
172
+            util.exportExcel(response, exportList, fileName);
173
+        } else if (param.getDimension() == 4) {
174
+            // 大队维度 - 导出大队绩效(仅大队名)
175
+            List<PerformanceMetricsBrigadeExportDto> exportList = convertToBrigadeExportList(dataList, param.getStartTime(), param.getEndTime());
176
+            ExcelUtil<PerformanceMetricsBrigadeExportDto> util = new ExcelUtil<>(PerformanceMetricsBrigadeExportDto.class);
177
+            util.exportExcel(response, exportList, fileName);
178
+        }
179
+    }
180
+
181
+    /**
182
+     * 转换为人员导出 DTO 列表
183
+     */
184
+    private List<PerformanceMetricsPersonalExportDto> convertToPersonalExportList(List<PerformanceMetricsResultDto> dataList, Date startTime, Date endTime) {
185
+        return dataList.stream()
186
+                .map(item -> {
187
+                    PerformanceMetricsPersonalExportDto dto = new PerformanceMetricsPersonalExportDto();
188
+                    dto.setName(item.getName());
189
+                    dto.setClassName(item.getClassName());
190
+                    dto.setDeptName(item.getDeptName());
191
+                    dto.setBrigadeName(item.getBrigadeName());
192
+                    dto.setSeizureEfficiency(item.getSeizureEfficiency());
193
+                    dto.setInspectionPassRate(item.getInspectionPassRate());
194
+                    dto.setTrainingScore(item.getTrainingScore());
195
+                    dto.setTotalScore(item.getTotalScore());
196
+                    dto.setStartTime(DateUtil.format(startTime, "yyyy-MM-dd"));
197
+                    dto.setEndTime(DateUtil.format(endTime, "yyyy-MM-dd"));
198
+                    return dto;
199
+                })
200
+                .collect(Collectors.toList());
201
+    }
202
+
203
+    /**
204
+     * 转换为班组导出 DTO 列表
205
+     */
206
+    private List<PerformanceMetricsTeamExportDto> convertToTeamExportList(List<PerformanceMetricsResultDto> dataList, Date startTime, Date endTime) {
207
+        return dataList.stream()
208
+                .map(item -> {
209
+                    PerformanceMetricsTeamExportDto dto = new PerformanceMetricsTeamExportDto();
210
+                    dto.setName(item.getName());
211
+                    dto.setDeptName(item.getDeptName());
212
+                    dto.setBrigadeName(item.getBrigadeName());
213
+                    dto.setSeizureEfficiency(item.getSeizureEfficiency());
214
+                    dto.setInspectionPassRate(item.getInspectionPassRate());
215
+                    dto.setTrainingScore(item.getTrainingScore());
216
+                    dto.setTotalScore(item.getTotalScore());
217
+                    dto.setStartTime(DateUtil.format(startTime, "yyyy-MM-dd"));
218
+                    dto.setEndTime(DateUtil.format(endTime, "yyyy-MM-dd"));
219
+                    return dto;
220
+                })
221
+                .collect(Collectors.toList());
222
+    }
223
+
224
+    /**
225
+     * 转换为科室导出 DTO 列表
226
+     */
227
+    private List<PerformanceMetricsDeptExportDto> convertToDeptExportList(List<PerformanceMetricsResultDto> dataList, Date startTime, Date endTime) {
228
+        return dataList.stream()
229
+                .map(item -> {
230
+                    PerformanceMetricsDeptExportDto dto = new PerformanceMetricsDeptExportDto();
231
+                    dto.setName(item.getName());
232
+                    dto.setBrigadeName(item.getBrigadeName());
233
+                    dto.setSeizureEfficiency(item.getSeizureEfficiency());
234
+                    dto.setInspectionPassRate(item.getInspectionPassRate());
235
+                    dto.setTrainingScore(item.getTrainingScore());
236
+                    dto.setTotalScore(item.getTotalScore());
237
+                    dto.setStartTime(DateUtil.format(startTime, "yyyy-MM-dd"));
238
+                    dto.setEndTime(DateUtil.format(endTime, "yyyy-MM-dd"));
239
+                    return dto;
240
+                })
241
+                .collect(Collectors.toList());
242
+    }
243
+
244
+    /**
245
+     * 转换为大队导出 DTO 列表
246
+     */
247
+    private List<PerformanceMetricsBrigadeExportDto> convertToBrigadeExportList(List<PerformanceMetricsResultDto> dataList, Date startTime, Date endTime) {
248
+        return dataList.stream()
249
+                .map(item -> {
250
+                    PerformanceMetricsBrigadeExportDto dto = new PerformanceMetricsBrigadeExportDto();
251
+                    dto.setName(item.getName());
252
+                    dto.setSeizureEfficiency(item.getSeizureEfficiency());
253
+                    dto.setInspectionPassRate(item.getInspectionPassRate());
254
+                    dto.setTrainingScore(item.getTrainingScore());
255
+                    dto.setTotalScore(item.getTotalScore());
256
+                    dto.setStartTime(DateUtil.format(startTime, "yyyy-MM-dd"));
257
+                    dto.setEndTime(DateUtil.format(endTime, "yyyy-MM-dd"));
258
+                    return dto;
259
+                })
260
+                .collect(Collectors.toList());
261
+    }
262
+
263
+
264
+    /**
265
+     * 保存配置到 sys_config 表
266
+     */
267
+    private void saveConfig(String configKey, String configName, String configValue) {
268
+        try {
269
+            SysConfig queryConfig = new SysConfig();
270
+            queryConfig.setConfigKey(configKey);
271
+            List<SysConfig> configList = configService.selectConfigList(queryConfig);
272
+            if (CollectionUtil.isNotEmpty(configList)) {
273
+                SysConfig config = configList.get(0);
274
+                config.setConfigValue(configValue);
275
+                configService.updateConfig(config);
276
+            } else {
277
+                // 新增配置
278
+                SysConfig config = new SysConfig();
279
+                config.setConfigName(configName);
280
+                config.setConfigKey(configKey);
281
+                config.setConfigValue(configValue);
282
+                config.setConfigType("N"); // 非系统内置
283
+                configService.insertConfig(config);
284
+            }
285
+        } catch (Exception e) {
286
+            log.error("保存配置失败:{}", e.getMessage());
287
+            throw e;
288
+        }
289
+    }
290
+
291
+}

+ 88 - 19
airport-admin/src/main/java/com/sundot/airport/web/controller/item/PerformanceMetricsController.java

@@ -1,5 +1,6 @@
1 1
 package com.sundot.airport.web.controller.item;
2 2
 
3
+import cn.hutool.core.collection.CollectionUtil;
3 4
 import com.sundot.airport.check.domain.CheckEfficiencyDto;
4 5
 import com.sundot.airport.check.domain.CheckEfficiencyRankDto;
5 6
 import com.sundot.airport.check.service.ICheckEfficiencyService;
@@ -79,12 +80,6 @@ public class PerformanceMetricsController {
79 80
     @Autowired
80 81
     private ItemLargeScreenMapper itemLargeScreenMapper;
81 82
 
82
-    //先写死,后期可能会变
83
-    private static final double a = 3;
84
-    private static final double b = 5;
85
-    private static final double c = 2;
86
-
87
-
88 83
     /**
89 84
      * 查询查获效率统计
90 85
      */
@@ -247,8 +242,8 @@ public class PerformanceMetricsController {
247 242
      * 填充科室和班级信息
248 243
      */
249 244
     private void enrichDeptAndClassInfo(List<PerformanceMetricsResultDto> result, PerformanceMetricsParamDto param) {
250
-        // 只处理人员维度和班组维度
251
-        if (param.getDimension() == null || param.getDimension() == 3) {
245
+        // 只处理人员维度和班组维度和科室维度
246
+        if (param.getDimension() == null || param.getDimension() == 4) {
252 247
             return;
253 248
         }
254 249
 
@@ -421,8 +416,38 @@ public class PerformanceMetricsController {
421 416
         if (topSiteId == null) {
422 417
             throw new ServiceException("无法找到有效的站点信息");
423 418
         }
419
+
420
+        // 根据大队 ID 筛选
421
+        if (param.getBrigadeId() != null) {
422
+            topSiteId = param.getBrigadeId();
423
+        }
424
+
425
+        // 根据科室 ID 筛选
426
+        if (param.getDeptId() != null) {
427
+            topSiteId = param.getDeptId();
428
+        }
429
+
430
+        // 根据班组 ID 筛选
431
+        if (param.getTeamId() != null) {
432
+            topSiteId = param.getTeamId();
433
+        }
434
+
424 435
         List<SysUser> sysUserTempList = userMapper.selectUserByDeptId(topSiteId);
425
-        List<SysUser> users = sysUserTempList.stream().filter(item -> item.getRoles().stream().anyMatch(sysRole -> RoleTypeEnum.banzuzhang.getCode().equals(sysRole.getRoleKey()) || RoleTypeEnum.SecurityCheck.getCode().equals(sysRole.getRoleKey()))).collect(Collectors.toList());
436
+        List<SysUser> users = sysUserTempList.stream()
437
+                .filter(item -> item.getRoles().stream()
438
+                        .anyMatch(sysRole -> RoleTypeEnum.banzuzhang.getCode().equals(sysRole.getRoleKey())
439
+                                || RoleTypeEnum.SecurityCheck.getCode().equals(sysRole.getRoleKey())))
440
+                .collect(Collectors.toList());
441
+
442
+        if (users.isEmpty()) return result;
443
+
444
+        // 根据姓名模糊筛选
445
+        if (StringUtils.isNotEmpty(param.getUserName())) {
446
+            users = users.stream()
447
+                    .filter(user -> user.getNickName() != null
448
+                            && user.getNickName().contains(param.getUserName()))
449
+                    .collect(Collectors.toList());
450
+        }
426 451
 
427 452
         if (users.isEmpty()) return result;
428 453
 
@@ -464,10 +489,28 @@ public class PerformanceMetricsController {
464 489
         List<PerformanceMetricsResultDto> result = new ArrayList<>();
465 490
         // 查询所有班组
466 491
         Long topSiteId = DeptUtils.getTopSiteId(deptMapper.selectDeptById(getDeptId()));
492
+
493
+        // 根据大队 ID 筛选
494
+        if (param.getBrigadeId() != null) {
495
+            topSiteId = param.getBrigadeId();
496
+        }
497
+
498
+        // 根据科室 ID 筛选
499
+        if (param.getDeptId() != null) {
500
+            topSiteId = param.getDeptId();
501
+        }
502
+
467 503
         List<SysDept> teams = sysDeptService.selectChildrenDeptById(topSiteId).stream()
468 504
                 .filter(dept -> DeptTypeEnum.TEAMS.getCode().equals(dept.getDeptType()))
469 505
                 .collect(Collectors.toList());
470 506
 
507
+        // 根据班组 ID 筛选
508
+        if (param.getTeamId() != null && CollectionUtil.isNotEmpty(teams)) {
509
+            teams = teams.stream()
510
+                    .filter(item -> param.getTeamId().equals(item.getDeptId()))
511
+                    .collect(Collectors.toList());
512
+        }
513
+
471 514
         if (teams.isEmpty()) return result;
472 515
 
473 516
         //查获效率
@@ -509,10 +552,23 @@ public class PerformanceMetricsController {
509 552
 
510 553
         // 查询所有科室
511 554
         Long topSiteId = DeptUtils.getTopSiteId(deptMapper.selectDeptById(getDeptId()));
555
+
556
+        // 根据大队 ID 筛选
557
+        if (param.getBrigadeId() != null) {
558
+            topSiteId = param.getBrigadeId();
559
+        }
560
+
512 561
         List<SysDept> departments = sysDeptService.selectChildrenDeptById(topSiteId).stream()
513 562
                 .filter(dept -> DeptTypeEnum.MANAGER.getCode().equals(dept.getDeptType()))
514 563
                 .collect(Collectors.toList());
515 564
 
565
+        // 根据科室 ID 筛选
566
+        if (param.getDeptId() != null && CollectionUtil.isNotEmpty(departments)) {
567
+            departments = departments.stream()
568
+                    .filter(item -> param.getDeptId().equals(item.getDeptId()))
569
+                    .collect(Collectors.toList());
570
+        }
571
+
516 572
         if (departments.isEmpty()) return result;
517 573
 
518 574
         //查获效率
@@ -558,6 +614,13 @@ public class PerformanceMetricsController {
558 614
                 .filter(dept -> DeptTypeEnum.BRIGADE.getCode().equals(dept.getDeptType()))
559 615
                 .collect(Collectors.toList());
560 616
 
617
+        // 根据大队 ID 筛选
618
+        if (param.getBrigadeId() != null && CollectionUtil.isNotEmpty(brigades)) {
619
+            brigades = brigades.stream()
620
+                    .filter(item -> param.getBrigadeId().equals(item.getDeptId()))
621
+                    .collect(Collectors.toList());
622
+        }
623
+
561 624
         if (brigades.isEmpty()) return result;
562 625
 
563 626
         //查获效率
@@ -605,13 +668,13 @@ public class PerformanceMetricsController {
605 668
             NormalizedData normalized = new NormalizedData();
606 669
             normalized.id = data.id;
607 670
             normalized.name = data.name;
608
-            normalized.seizureEfficiency = normalizeValue(data.seizureEfficiency, "w1");
609
-            normalized.inspectionPassRate = normalizeValue(data.inspectionPassRate, "w2");
610
-            normalized.trainingScore = normalizeValue(data.trainingScore, "w3");
671
+            normalized.seizureEfficiency = data.seizureEfficiency.multiply(new BigDecimal(100));
672
+            normalized.inspectionPassRate = data.inspectionPassRate.multiply(new BigDecimal(100));
673
+            normalized.trainingScore = data.trainingScore.multiply(new BigDecimal(100));
611 674
             // 总分 = 查获效率 + 巡检合格率 + 抽问抽答正确率
612
-            normalized.totalScore = normalized.seizureEfficiency
613
-                    .add(normalized.inspectionPassRate)
614
-                    .add(normalized.trainingScore);
675
+            normalized.totalScore = normalizeValue(data.seizureEfficiency, "w1")
676
+                    .add(normalizeValue(data.inspectionPassRate, "w2"))
677
+                    .add(normalizeValue(data.trainingScore, "w3"));
615 678
             normalizedList.add(normalized);
616 679
         }
617 680
 
@@ -635,7 +698,7 @@ public class PerformanceMetricsController {
635 698
      */
636 699
     private BigDecimal normalizeValue(BigDecimal value, String w) {
637 700
         BigDecimal bigDecimal = value != null ? value : BigDecimal.ZERO;
638
-        GeometricMeanUtils.CalculationResult calculationResult = GeometricMeanUtils.calculateWithWeights(a, b, c);
701
+        GeometricMeanUtils.CalculationResult calculationResult = GeometricMeanUtils.calculateWithWeights();
639 702
         double d = 0;
640 703
         if ("w1".equals(w)) {
641 704
             d = calculationResult.getW1();
@@ -699,6 +762,7 @@ public class PerformanceMetricsController {
699 762
         BigDecimal max = rankList.stream().map(rank -> rank.getEfficiency() == null ? BigDecimal.ZERO : rank.getEfficiency()).max(BigDecimal::compareTo).orElse(BigDecimal.ZERO);
700 763
         BigDecimal difference = max.subtract(min);
701 764
         BigDecimal normalized = difference.compareTo(BigDecimal.ZERO) == 0 ? BigDecimal.ZERO : reduce.subtract(min).divide(difference, 2, RoundingMode.HALF_UP);
765
+        normalized = new BigDecimal("0.8").add(new BigDecimal("0.2").multiply(normalized));
702 766
         return normalized;
703 767
     }
704 768
 
@@ -715,6 +779,7 @@ public class PerformanceMetricsController {
715 779
         BigDecimal max = rankList.stream().map(rank -> rank.getPassRate() == null ? BigDecimal.ZERO : rank.getPassRate()).max(BigDecimal::compareTo).orElse(BigDecimal.ZERO);
716 780
         BigDecimal difference = max.subtract(min);
717 781
         BigDecimal normalized = difference.compareTo(BigDecimal.ZERO) == 0 ? BigDecimal.ZERO : reduce.subtract(min).divide(difference, 2, RoundingMode.HALF_UP);
782
+        normalized = new BigDecimal("0.8").add(new BigDecimal("0.2").multiply(normalized));
718 783
         return normalized;
719 784
     }
720 785
 
@@ -723,7 +788,7 @@ public class PerformanceMetricsController {
723 788
      * 归一化公式:(当前值 - min) / (max - min)
724 789
      */
725 790
     private BigDecimal queryPersonalTrainingScore(Long userId, Map<Long, BigDecimal> accuracyMap) {
726
-        return normalizeFromMap(userId, accuracyMap);
791
+        return new BigDecimal("0.8").add(new BigDecimal("0.2").multiply(normalizeFromMap(userId, accuracyMap)));
727 792
     }
728 793
 
729 794
     /**
@@ -736,6 +801,7 @@ public class PerformanceMetricsController {
736 801
         BigDecimal max = rankList.stream().map(rank -> rank.getEfficiency() == null ? BigDecimal.ZERO : rank.getEfficiency()).max(BigDecimal::compareTo).orElse(BigDecimal.ZERO);
737 802
         BigDecimal difference = max.subtract(min);
738 803
         BigDecimal normalized = difference.compareTo(BigDecimal.ZERO) == 0 ? BigDecimal.ZERO : reduce.subtract(min).divide(difference, 2, RoundingMode.HALF_UP);
804
+        normalized = new BigDecimal("0.8").add(new BigDecimal("0.2").multiply(normalized));
739 805
         return normalized;
740 806
     }
741 807
 
@@ -748,6 +814,7 @@ public class PerformanceMetricsController {
748 814
         BigDecimal max = rankList.stream().map(rank -> rank.getPassRate() == null ? BigDecimal.ZERO : rank.getPassRate()).max(BigDecimal::compareTo).orElse(BigDecimal.ZERO);
749 815
         BigDecimal difference = max.subtract(min);
750 816
         BigDecimal normalized = difference.compareTo(BigDecimal.ZERO) == 0 ? BigDecimal.ZERO : reduce.subtract(min).divide(difference, 2, RoundingMode.HALF_UP);
817
+        normalized = new BigDecimal("0.8").add(new BigDecimal("0.2").multiply(normalized));
751 818
         return normalized;
752 819
     }
753 820
 
@@ -756,7 +823,7 @@ public class PerformanceMetricsController {
756 823
      * 归一化公式:(当前值 - min) / (max - min)
757 824
      */
758 825
     private BigDecimal queryTeamTrainingScore(Long teamId, Map<Long, BigDecimal> accuracyMap) {
759
-        return normalizeFromMap(teamId, accuracyMap);
826
+        return new BigDecimal("0.8").add(new BigDecimal("0.2").multiply(normalizeFromMap(teamId, accuracyMap)));
760 827
     }
761 828
 
762 829
     /**
@@ -769,6 +836,7 @@ public class PerformanceMetricsController {
769 836
         BigDecimal max = rankList.stream().map(rank -> rank.getEfficiency() == null ? BigDecimal.ZERO : rank.getEfficiency()).max(BigDecimal::compareTo).orElse(BigDecimal.ZERO);
770 837
         BigDecimal difference = max.subtract(min);
771 838
         BigDecimal normalized = difference.compareTo(BigDecimal.ZERO) == 0 ? BigDecimal.ZERO : reduce.subtract(min).divide(difference, 2, RoundingMode.HALF_UP);
839
+        normalized = new BigDecimal("0.8").add(new BigDecimal("0.2").multiply(normalized));
772 840
         return normalized;
773 841
     }
774 842
 
@@ -781,6 +849,7 @@ public class PerformanceMetricsController {
781 849
         BigDecimal max = rankList.stream().map(rank -> rank.getPassRate() == null ? BigDecimal.ZERO : rank.getPassRate()).max(BigDecimal::compareTo).orElse(BigDecimal.ZERO);
782 850
         BigDecimal difference = max.subtract(min);
783 851
         BigDecimal normalized = difference.compareTo(BigDecimal.ZERO) == 0 ? BigDecimal.ZERO : reduce.subtract(min).divide(difference, 2, RoundingMode.HALF_UP);
852
+        normalized = new BigDecimal("0.8").add(new BigDecimal("0.2").multiply(normalized));
784 853
         return normalized;
785 854
     }
786 855
 
@@ -789,7 +858,7 @@ public class PerformanceMetricsController {
789 858
      * 归一化公式:(当前值 - min) / (max - min)
790 859
      */
791 860
     private BigDecimal queryDepartmentTrainingScore(Long deptId, Map<Long, BigDecimal> accuracyMap) {
792
-        return normalizeFromMap(deptId, accuracyMap);
861
+        return new BigDecimal("0.8").add(new BigDecimal("0.2").multiply(normalizeFromMap(deptId, accuracyMap)));
793 862
     }
794 863
 
795 864
     /**

+ 0 - 28
airport-admin/src/main/java/com/sundot/airport/web/controller/portrait/UserBasicPortraitController.java

@@ -118,32 +118,4 @@ public class UserBasicPortraitController extends BaseController {
118 118
         }
119 119
     }
120 120
 
121
-    /**
122
-     * 根据用户查找其所属的站点ID
123
-     *
124
-     * @param user 用户
125
-     * @return 站点ID
126
-     */
127
-    private Long findStationIdByUser(SysUser user) {
128
-        // 获取用户所在部门
129
-        SysDept userDept = user.getDept();
130
-        if (userDept == null) {
131
-            return null;
132
-        }
133
-
134
-        // 如果用户所在部门就是站点
135
-        if (DeptTypeEnum.STATION.getCode().equals(userDept.getDeptType())) {
136
-            return userDept.getDeptId();
137
-        }
138
-
139
-        // 向上查找父级部门,直到找到站点
140
-        SysDept currentDept = userDept;
141
-        while (currentDept != null && !DeptTypeEnum.STATION.getCode().equals(currentDept.getDeptType())) {
142
-            currentDept = sysDeptService.selectDeptById(currentDept.getParentId());
143
-        }
144
-
145
-        return currentDept != null ? currentDept.getDeptId() : null;
146
-    }
147
-
148
-
149 121
 }

+ 51 - 0
airport-common/src/main/java/com/sundot/airport/common/dto/PerformanceMetricsBrigadeExportDto.java

@@ -0,0 +1,51 @@
1
+package com.sundot.airport.common.dto;
2
+
3
+import com.fasterxml.jackson.annotation.JsonFormat;
4
+import com.sundot.airport.common.annotation.Excel;
5
+import io.swagger.annotations.ApiModel;
6
+import io.swagger.annotations.ApiModelProperty;
7
+import lombok.Data;
8
+
9
+import java.math.BigDecimal;
10
+
11
+/**
12
+ * 绩效指标导出 DTO - 大队维度
13
+ * 显示:排名、大队名称、总分、查获效率、巡检合格率、培训得分
14
+ * (不显示班级名称、科室名称、大队名称重复字段)
15
+ *
16
+ * @author wangxx
17
+ * @date 2026-03-16
18
+ */
19
+@Data
20
+@ApiModel("绩效指标导出 DTO - 大队维度")
21
+public class PerformanceMetricsBrigadeExportDto {
22
+    @Excel(name = "大队名称", sort = 1)
23
+    @ApiModelProperty("大队名称")
24
+    private String name;
25
+
26
+    @Excel(name = "总分", sort = 2)
27
+    @ApiModelProperty("总分")
28
+    private BigDecimal totalScore;
29
+
30
+    @Excel(name = "查获效率", sort = 3)
31
+    @ApiModelProperty("查获效率")
32
+    private BigDecimal seizureEfficiency;
33
+
34
+    @Excel(name = "巡检合格率", sort = 4)
35
+    @ApiModelProperty("巡检合格率")
36
+    private BigDecimal inspectionPassRate;
37
+
38
+    @Excel(name = "抽问抽答正确率", sort = 5)
39
+    @ApiModelProperty("抽问抽答正确率")
40
+    private BigDecimal trainingScore;
41
+
42
+    @Excel(name = "开始时间", sort = 6)
43
+    @ApiModelProperty("开始时间")
44
+    @JsonFormat(pattern = "yyyy-MM-dd")
45
+    private String startTime;
46
+
47
+    @Excel(name = "结束时间", sort = 7)
48
+    @ApiModelProperty("结束时间")
49
+    @JsonFormat(pattern = "yyyy-MM-dd")
50
+    private String endTime;
51
+}

+ 55 - 0
airport-common/src/main/java/com/sundot/airport/common/dto/PerformanceMetricsDeptExportDto.java

@@ -0,0 +1,55 @@
1
+package com.sundot.airport.common.dto;
2
+
3
+import com.fasterxml.jackson.annotation.JsonFormat;
4
+import com.sundot.airport.common.annotation.Excel;
5
+import io.swagger.annotations.ApiModel;
6
+import io.swagger.annotations.ApiModelProperty;
7
+import lombok.Data;
8
+
9
+import java.math.BigDecimal;
10
+
11
+/**
12
+ * 绩效指标导出 DTO - 科室维度
13
+ * 显示:排名、科室名称、大队名称、总分、查获效率、巡检合格率、培训得分
14
+ * (不显示班级名称、科室名称重复字段)
15
+ *
16
+ * @author wangxx
17
+ * @date 2026-03-16
18
+ */
19
+@Data
20
+@ApiModel("绩效指标导出 DTO - 科室维度")
21
+public class PerformanceMetricsDeptExportDto {
22
+    @Excel(name = "大队名称", sort = 1)
23
+    @ApiModelProperty("大队名称")
24
+    private String brigadeName;
25
+
26
+    @Excel(name = "科室名称", sort = 2)
27
+    @ApiModelProperty("科室名称")
28
+    private String name;
29
+
30
+    @Excel(name = "总分", sort = 3)
31
+    @ApiModelProperty("总分")
32
+    private BigDecimal totalScore;
33
+
34
+    @Excel(name = "查获效率", sort = 4)
35
+    @ApiModelProperty("查获效率")
36
+    private BigDecimal seizureEfficiency;
37
+
38
+    @Excel(name = "巡检合格率", sort = 5)
39
+    @ApiModelProperty("巡检合格率")
40
+    private BigDecimal inspectionPassRate;
41
+
42
+    @Excel(name = "抽问抽答正确率", sort = 6)
43
+    @ApiModelProperty("抽问抽答正确率")
44
+    private BigDecimal trainingScore;
45
+
46
+    @Excel(name = "开始时间", sort = 7)
47
+    @ApiModelProperty("开始时间")
48
+    @JsonFormat(pattern = "yyyy-MM-dd")
49
+    private String startTime;
50
+
51
+    @Excel(name = "结束时间", sort = 8)
52
+    @ApiModelProperty("结束时间")
53
+    @JsonFormat(pattern = "yyyy-MM-dd")
54
+    private String endTime;
55
+}

+ 12 - 0
airport-common/src/main/java/com/sundot/airport/common/dto/PerformanceMetricsParamDto.java

@@ -36,4 +36,16 @@ public class PerformanceMetricsParamDto {
36 36
     @JsonFormat(pattern = "yyyy-MM-dd")
37 37
     @DateTimeFormat(pattern = "yyyy-MM-dd")
38 38
     private Date endTime;
39
+
40
+    @ApiModelProperty("大队ID(维度为 1/2/3/4 时使用)")
41
+    private Long brigadeId;
42
+
43
+    @ApiModelProperty("科室ID(维度为 1/2/3 时使用)")
44
+    private Long deptId;
45
+
46
+    @ApiModelProperty("班组ID(维度为 1/2 时使用)")
47
+    private Long teamId;
48
+
49
+    @ApiModelProperty("姓名 (维度为 1 时使用)")
50
+    private String userName;
39 51
 }

+ 62 - 0
airport-common/src/main/java/com/sundot/airport/common/dto/PerformanceMetricsPersonalExportDto.java

@@ -0,0 +1,62 @@
1
+package com.sundot.airport.common.dto;
2
+
3
+import com.fasterxml.jackson.annotation.JsonFormat;
4
+import com.sundot.airport.common.annotation.Excel;
5
+import io.swagger.annotations.ApiModel;
6
+import io.swagger.annotations.ApiModelProperty;
7
+import lombok.Data;
8
+
9
+import java.math.BigDecimal;
10
+
11
+/**
12
+ * 绩效指标导出 DTO - 人员维度
13
+ * 显示:排名、姓名、班级名称、科室名称、大队名称、总分、查获效率、巡检合格率、培训得分
14
+ *
15
+ * @author wangxx
16
+ * @date 2026-03-16
17
+ */
18
+@Data
19
+@ApiModel("绩效指标导出 DTO - 人员维度")
20
+public class PerformanceMetricsPersonalExportDto {
21
+    @Excel(name = "大队名称", sort = 1)
22
+    @ApiModelProperty("大队名称")
23
+    private String brigadeName;
24
+
25
+    @Excel(name = "科室名称", sort = 2)
26
+    @ApiModelProperty("科室名称")
27
+    private String deptName;
28
+
29
+    @Excel(name = "班级名称", sort = 3)
30
+    @ApiModelProperty("班级名称")
31
+    private String className;
32
+
33
+    @Excel(name = "姓名", sort = 4)
34
+    @ApiModelProperty("姓名")
35
+    private String name;
36
+
37
+    @Excel(name = "总分", sort = 5)
38
+    @ApiModelProperty("总分")
39
+    private BigDecimal totalScore;
40
+
41
+    @Excel(name = "查获效率", sort = 6)
42
+    @ApiModelProperty("查获效率")
43
+    private BigDecimal seizureEfficiency;
44
+
45
+    @Excel(name = "巡检合格率", sort = 7)
46
+    @ApiModelProperty("巡检合格率")
47
+    private BigDecimal inspectionPassRate;
48
+
49
+    @Excel(name = "抽问抽答正确率", sort = 8)
50
+    @ApiModelProperty("抽问抽答正确率")
51
+    private BigDecimal trainingScore;
52
+
53
+    @Excel(name = "开始时间", sort = 9)
54
+    @ApiModelProperty("开始时间")
55
+    @JsonFormat(pattern = "yyyy-MM-dd")
56
+    private String startTime;
57
+
58
+    @Excel(name = "结束时间", sort = 10)
59
+    @ApiModelProperty("结束时间")
60
+    @JsonFormat(pattern = "yyyy-MM-dd")
61
+    private String endTime;
62
+}

+ 59 - 0
airport-common/src/main/java/com/sundot/airport/common/dto/PerformanceMetricsTeamExportDto.java

@@ -0,0 +1,59 @@
1
+package com.sundot.airport.common.dto;
2
+
3
+import com.fasterxml.jackson.annotation.JsonFormat;
4
+import com.sundot.airport.common.annotation.Excel;
5
+import io.swagger.annotations.ApiModel;
6
+import io.swagger.annotations.ApiModelProperty;
7
+import lombok.Data;
8
+
9
+import java.math.BigDecimal;
10
+
11
+/**
12
+ * 绩效指标导出 DTO - 班组维度
13
+ * 显示:排名、班组名称、科室名称、大队名称、总分、查获效率、巡检合格率、培训得分
14
+ * (不显示班级名称)
15
+ *
16
+ * @author wangxx
17
+ * @date 2026-03-16
18
+ */
19
+@Data
20
+@ApiModel("绩效指标导出 DTO - 班组维度")
21
+public class PerformanceMetricsTeamExportDto {
22
+    @Excel(name = "大队名称", sort = 1)
23
+    @ApiModelProperty("大队名称")
24
+    private String brigadeName;
25
+
26
+    @Excel(name = "科室名称", sort = 2)
27
+    @ApiModelProperty("科室名称")
28
+    private String deptName;
29
+
30
+    @Excel(name = "班组名称", sort = 3)
31
+    @ApiModelProperty("班组名称")
32
+    private String name;
33
+
34
+    @Excel(name = "总分", sort = 4)
35
+    @ApiModelProperty("总分")
36
+    private BigDecimal totalScore;
37
+
38
+    @Excel(name = "查获效率", sort = 5)
39
+    @ApiModelProperty("查获效率")
40
+    private BigDecimal seizureEfficiency;
41
+
42
+    @Excel(name = "巡检合格率", sort = 6)
43
+    @ApiModelProperty("巡检合格率")
44
+    private BigDecimal inspectionPassRate;
45
+
46
+    @Excel(name = "抽问抽答正确率", sort = 7)
47
+    @ApiModelProperty("抽问抽答正确率")
48
+    private BigDecimal trainingScore;
49
+
50
+    @Excel(name = "开始时间", sort = 8)
51
+    @ApiModelProperty("开始时间")
52
+    @JsonFormat(pattern = "yyyy-MM-dd")
53
+    private String startTime;
54
+
55
+    @Excel(name = "结束时间", sort = 9)
56
+    @ApiModelProperty("结束时间")
57
+    @JsonFormat(pattern = "yyyy-MM-dd")
58
+    private String endTime;
59
+}

+ 88 - 6
airport-common/src/main/java/com/sundot/airport/common/utils/GeometricMeanUtils.java

@@ -1,5 +1,7 @@
1 1
 package com.sundot.airport.common.utils;
2 2
 
3
+import java.util.concurrent.atomic.AtomicReference;
4
+
3 5
 /**
4 6
  * 几何平均数计算工具类
5 7
  * - g1 = ∛(1 × a × b)
@@ -8,14 +10,83 @@ package com.sundot.airport.common.utils;
8 10
  */
9 11
 public class GeometricMeanUtils {
10 12
 
13
+
14
+    private static final AtomicReference<WeightParams> currentParams = new AtomicReference<>(
15
+            new WeightParams(3.0, 5.0, 2.0) // 默认值 a=3, b=5, c=2
16
+    );
17
+
18
+    /**
19
+     * 权重参数内部类
20
+     */
21
+    private static class WeightParams {
22
+        final double a;
23
+        final double b;
24
+        final double c;
25
+
26
+        WeightParams(double a, double b, double c) {
27
+            this.a = a;
28
+            this.b = b;
29
+            this.c = c;
30
+        }
31
+    }
32
+
33
+    /**
34
+     * 设置权重参数(线程安全)
35
+     *
36
+     * @param a 参数 a
37
+     * @param b 参数 b
38
+     * @param c 参数 c
39
+     */
40
+    public static void setParams(double a, double b, double c) {
41
+        currentParams.set(new WeightParams(a, b, c));
42
+    }
43
+
44
+    /**
45
+     * 获取当前参数 a
46
+     *
47
+     * @return 参数 a
48
+     */
49
+    public static double getParamA() {
50
+        return currentParams.get().a;
51
+    }
52
+
53
+    /**
54
+     * 获取当前参数 b
55
+     *
56
+     * @return 参数 b
57
+     */
58
+    public static double getParamB() {
59
+        return currentParams.get().b;
60
+    }
61
+
62
+    /**
63
+     * 获取当前参数 c
64
+     *
65
+     * @return 参数 c
66
+     */
67
+    public static double getParamC() {
68
+        return currentParams.get().c;
69
+    }
70
+
71
+    /**
72
+     * 计算几何平均数总和(使用当前配置的参数)
73
+     *
74
+     * @return 总和值
75
+     */
76
+    public static double calculateSum() {
77
+        WeightParams params = currentParams.get();
78
+        return calculateSum(params.a, params.b, params.c);
79
+    }
80
+
11 81
     /**
12 82
      * 计算几何平均数总和
13 83
      *
14
-     * @param a 变量a
15
-     * @param b 变量b
16
-     * @param c 变量c
84
+     * @param a 变量 a
85
+     * @param b 变量 b
86
+     * @param c 变量 c
17 87
      * @return 总和值
18 88
      */
89
+    @Deprecated
19 90
     public static double calculateSum(double a, double b, double c) {
20 91
         double g1 = Math.cbrt(1.0 * a * b);
21 92
         double g2 = Math.cbrt((1.0 / a) * 1.0 * c);
@@ -25,13 +96,24 @@ public class GeometricMeanUtils {
25 96
     }
26 97
 
27 98
     /**
99
+     * 计算几何平均数及其权重(使用当前配置的参数)
100
+     *
101
+     * @return 包含总和和各权重的计算结果
102
+     */
103
+    public static CalculationResult calculateWithWeights() {
104
+        WeightParams params = currentParams.get();
105
+        return calculateWithWeights(params.a, params.b, params.c);
106
+    }
107
+
108
+    /**
28 109
      * 计算几何平均数及其权重
29 110
      *
30
-     * @param a 变量a
31
-     * @param b 变量b
32
-     * @param c 变量c
111
+     * @param a 变量 a
112
+     * @param b 变量 b
113
+     * @param c 变量 c
33 114
      * @return 包含总和和各权重的计算结果
34 115
      */
116
+    @Deprecated
35 117
     public static CalculationResult calculateWithWeights(double a, double b, double c) {
36 118
         double g1 = Math.cbrt(1.0 * a * b);
37 119
         double g2 = Math.cbrt((1.0 / a) * 1.0 * c);

+ 66 - 0
airport-framework/src/main/java/com/sundot/airport/framework/config/PerformanceConfigInitializer.java

@@ -0,0 +1,66 @@
1
+package com.sundot.airport.framework.config;
2
+
3
+import com.sundot.airport.common.utils.GeometricMeanUtils;
4
+import com.sundot.airport.system.service.ISysConfigService;
5
+import org.springframework.beans.factory.annotation.Autowired;
6
+import org.springframework.stereotype.Component;
7
+
8
+import javax.annotation.PostConstruct;
9
+import java.math.BigDecimal;
10
+
11
+/**
12
+ * 绩效参数配置初始化
13
+ *
14
+ * @author wangxx
15
+ * @date 2026-03-16
16
+ */
17
+@Component
18
+public class PerformanceConfigInitializer {
19
+
20
+    @Autowired
21
+    private ISysConfigService configService;
22
+
23
+    /**
24
+     * 应用启动时初始化绩效参数配置
25
+     */
26
+    @PostConstruct
27
+    public void init() {
28
+        try {
29
+            // 从数据库读取配置
30
+            String paramAStr = configService.selectConfigByKey("item.performance.matrix.param_a");
31
+            String paramBStr = configService.selectConfigByKey("item.performance.matrix.param_b");
32
+            String paramCStr = configService.selectConfigByKey("item.performance.matrix.param_c");
33
+
34
+            // 解析参数值,如果不存在则使用默认值
35
+            double paramA = parseParam(paramAStr, 3.0);
36
+            double paramB = parseParam(paramBStr, 5.0);
37
+            double paramC = parseParam(paramCStr, 2.0);
38
+
39
+            // 设置到 GeometricMeanUtils
40
+            GeometricMeanUtils.setParams(paramA, paramB, paramC);
41
+        } catch (Exception e) {
42
+            // 使用默认值
43
+            GeometricMeanUtils.setParams(3.0, 5.0, 2.0);
44
+        }
45
+    }
46
+
47
+    /**
48
+     * 解析参数值
49
+     *
50
+     * @param value        配置值
51
+     * @param defaultValue 默认值
52
+     * @return 解析后的 double 值
53
+     */
54
+    private double parseParam(String value, double defaultValue) {
55
+        if (value == null || value.isEmpty()) {
56
+            return defaultValue;
57
+        }
58
+
59
+        try {
60
+            return new BigDecimal(value).doubleValue();
61
+        } catch (Exception e) {
62
+            return defaultValue;
63
+        }
64
+    }
65
+
66
+}