Selaa lähdekoodia

质控分析报告-风险隐患【移交公安数据+X光机漏检数据+可能异常查获数据】

chenshudong 2 viikkoa sitten
vanhempi
commit
ef5519a218

+ 166 - 1
airport-admin/src/main/java/com/sundot/airport/web/controller/quality/ItemCategoryStatsController.java

@@ -5,6 +5,8 @@ import com.sundot.airport.common.core.domain.AjaxResult;
5 5
 import com.sundot.airport.common.core.domain.BaseLargeScreenQueryParamDto;
6 6
 import com.sundot.airport.common.statistics.TimeDimensionParams;
7 7
 import com.sundot.airport.common.utils.DeptUtils;
8
+import com.sundot.airport.item.domain.*;
9
+import com.sundot.airport.item.domain.ItemLargeScreenInfoDto;
8 10
 import com.sundot.airport.item.domain.ItemLargeScreenTimeSpanDto;
9 11
 import com.sundot.airport.item.domain.dto.ChannelRankingStatsDTO;
10 12
 import com.sundot.airport.item.domain.dto.ConcealmentPositionStatsDTO;
@@ -25,6 +27,7 @@ import org.springframework.web.bind.annotation.RestController;
25 27
 import java.util.ArrayList;
26 28
 import java.util.Calendar;
27 29
 import java.util.List;
30
+import java.util.stream.Collectors;
28 31
 
29 32
 /**
30 33
  * 物品分类统计Controller
@@ -226,7 +229,7 @@ public class ItemCategoryStatsController extends BaseController {
226 229
             cal.set(year, startMonth - 1, 1, 0, 0, 0);
227 230
             result.setStartTime(cal.getTime());
228 231
 
229
-            cal.set(year, endMonth - 1, cal.getActualMaximum(Calendar.DAY_OF_MONTH), 23, 59, 59);
232
+            cal.set(year, endMonth - 1, cal.getActualMaximum(Calendar.DAY_OF_MONTH), 0, 0, 0);
230 233
             result.setEndTime(cal.getTime());
231 234
         } else {
232 235
             // 使用传入的时间范围
@@ -237,4 +240,166 @@ public class ItemCategoryStatsController extends BaseController {
237 240
         return result;
238 241
     }
239 242
 
243
+    /**
244
+     * 获取当前用户所在站点 ID
245
+     */
246
+    private Long getCurrentTopSiteId() {
247
+        return DeptUtils.getTopSiteId(sysDeptService.selectDeptById(getDeptId()));
248
+    }
249
+
250
+    /**
251
+     * 将 TimeDimensionParams 转换为 BaseLargeScreenQueryParamDto
252
+     */
253
+    private BaseLargeScreenQueryParamDto convertToQueryParamDto(TimeDimensionParams params) {
254
+        BaseLargeScreenQueryParamDto dto = new BaseLargeScreenQueryParamDto();
255
+
256
+        // 获取当前用户所在站点 ID
257
+        Long topSiteId = getCurrentTopSiteId();
258
+        if (topSiteId != null) {
259
+            dto.setInspectStationId(topSiteId);
260
+        }
261
+
262
+        // 计算时间范围
263
+        TimeDimensionParams timeParams = calculateTimeRange(params);
264
+        dto.setStartDate(timeParams.getStartTime());
265
+        dto.setEndDate(timeParams.getEndTime());
266
+
267
+        return dto;
268
+    }
269
+
270
+    /**
271
+     * 移交公安数据
272
+     * 支持按年、季度、月维度统计
273
+     */
274
+    @ApiOperation("移交公安数据")
275
+    @GetMapping("/police-data")
276
+    public AjaxResult getPoliceData(TimeDimensionParams params) {
277
+        try {
278
+            BaseLargeScreenQueryParamDto dto = convertToQueryParamDto(params);
279
+            List<ItemLargeScreenInfoDto> result = itemLargeScreenService.police(dto);
280
+
281
+            List<ItemLargeScreenPoliceGroupDto> tableData = result.stream().map(item -> {
282
+                ItemLargeScreenPoliceGroupDto groupDto = new ItemLargeScreenPoliceGroupDto();
283
+                groupDto.setBrigadeName(item.getInspectBrigadeName());
284
+                groupDto.setDepartmentName(item.getInspectDepartmentName());
285
+                groupDto.setTeamName(item.getInspectTeamName());
286
+                groupDto.setUserName(item.getInspectUserName());
287
+                groupDto.setItemName(item.getCategoryNameTwo());
288
+                groupDto.setQuantity(item.getQuantity());
289
+
290
+                // 查获部位拼接
291
+                StringBuilder position = new StringBuilder();
292
+                if (item.getCheckPositionNameOne() != null && !item.getCheckPositionNameOne().isEmpty()) {
293
+                    position.append(item.getCheckPositionNameOne());
294
+                }
295
+                if (item.getCheckPositionNameTwo() != null && !item.getCheckPositionNameTwo().isEmpty()) {
296
+                    if (position.length() > 0) {
297
+                        position.append("/");
298
+                    }
299
+                    position.append(item.getCheckPositionNameTwo());
300
+                }
301
+                if (item.getCheckPositionSpecific() != null && !item.getCheckPositionSpecific().isEmpty()) {
302
+                    if (position.length() > 0) {
303
+                        position.append("/");
304
+                    }
305
+                    position.append(item.getCheckPositionSpecific());
306
+                }
307
+                groupDto.setPositionName(position.toString());
308
+                groupDto.setSeizureTime(item.getSeizureTime());
309
+                groupDto.setChannelName(item.getChannelName());
310
+                return groupDto;
311
+            }).collect(Collectors.toList());
312
+
313
+            return AjaxResult.success("获取成功", tableData);
314
+        } catch (Exception e) {
315
+            log.error("获取移交公安数据失败", e);
316
+            return AjaxResult.error("获取移交公安数据失败:" + e.getMessage());
317
+        }
318
+    }
319
+
320
+    /**
321
+     * 移交公安数据统计
322
+     * 支持按年、季度、月维度统计
323
+     */
324
+    @ApiOperation("移交公安数据统计")
325
+    @GetMapping("/police-stats")
326
+    public AjaxResult getPoliceStats(TimeDimensionParams params) {
327
+        try {
328
+            BaseLargeScreenQueryParamDto dto = convertToQueryParamDto(params);
329
+            ItemLargeScreenPoliceStatsDto result = itemLargeScreenService.getPoliceStats(dto);
330
+            return AjaxResult.success("获取成功", result);
331
+        } catch (Exception e) {
332
+            log.error("获取移交公安数据统计失败", e);
333
+            return AjaxResult.error("获取移交公安数据统计失败:" + e.getMessage());
334
+        }
335
+    }
336
+
337
+    /**
338
+     * X 光机漏检数据
339
+     * 支持按年、季度、月维度统计
340
+     */
341
+    @ApiOperation("X 光机漏检数据")
342
+    @GetMapping("/xray-miss-check")
343
+    public AjaxResult getXRayMissCheck(TimeDimensionParams params) {
344
+        try {
345
+            BaseLargeScreenQueryParamDto dto = convertToQueryParamDto(params);
346
+            List<ItemXRayMissCheckDto> result = itemLargeScreenService.xRayMissCheck(dto);
347
+            return AjaxResult.success("获取成功", result);
348
+        } catch (Exception e) {
349
+            log.error("获取 X 光机漏检数据失败", e);
350
+            return AjaxResult.error("获取 X 光机漏检数据失败:" + e.getMessage());
351
+        }
352
+    }
353
+
354
+    /**
355
+     * X 光机漏检人员统计 TOP3
356
+     * 支持按年、季度、月维度统计
357
+     */
358
+    @ApiOperation("X 光机漏检人员统计 TOP3")
359
+    @GetMapping("/xray-miss-check-top3")
360
+    public AjaxResult getXRayMissCheckTop3(TimeDimensionParams params) {
361
+        try {
362
+            BaseLargeScreenQueryParamDto dto = convertToQueryParamDto(params);
363
+            List<ItemXRayMissCheckUserStatsDto> result = itemLargeScreenService.xRayMissCheckUserStatsTop3(dto);
364
+            return AjaxResult.success("获取成功", result);
365
+        } catch (Exception e) {
366
+            log.error("获取 X 光机漏检人员统计 TOP3 失败", e);
367
+            return AjaxResult.error("获取 X 光机漏检人员统计 TOP3 失败:" + e.getMessage());
368
+        }
369
+    }
370
+
371
+    /**
372
+     * 异常查获数据
373
+     * 支持按年、季度、月维度统计
374
+     */
375
+    @ApiOperation("异常查获数据")
376
+    @GetMapping("/abnormal-seizure-data")
377
+    public AjaxResult getAbnormalSeizureData(TimeDimensionParams params) {
378
+        try {
379
+            BaseLargeScreenQueryParamDto dto = convertToQueryParamDto(params);
380
+            List<ItemAbnormalSeizureDto> result = itemLargeScreenService.getAbnormalSeizureData(dto);
381
+            return AjaxResult.success("获取成功", result);
382
+        } catch (Exception e) {
383
+            log.error("获取异常查获数据失败", e);
384
+            return AjaxResult.error("获取异常查获数据失败:" + e.getMessage());
385
+        }
386
+    }
387
+
388
+    /**
389
+     * 异常查获数据 TOP3(3 个高于 +3 个低于)
390
+     * 支持按年、季度、月维度统计
391
+     */
392
+    @ApiOperation("异常查获数据 TOP3")
393
+    @GetMapping("/abnormal-seizure-data-top3")
394
+    public AjaxResult getAbnormalSeizureDataTop3(TimeDimensionParams params) {
395
+        try {
396
+            BaseLargeScreenQueryParamDto dto = convertToQueryParamDto(params);
397
+            ItemAbnormalSeizureRankDto result = itemLargeScreenService.getAbnormalSeizureDataTop3(dto);
398
+            return AjaxResult.success("获取成功", result);
399
+        } catch (Exception e) {
400
+            log.error("获取异常查获数据 TOP3 失败", e);
401
+            return AjaxResult.error("获取异常查获数据 TOP3 失败:" + e.getMessage());
402
+        }
403
+    }
404
+
240 405
 }

+ 55 - 0
airport-item/src/main/java/com/sundot/airport/item/domain/ItemAbnormalSeizureDto.java

@@ -0,0 +1,55 @@
1
+package com.sundot.airport.item.domain;
2
+
3
+import com.sundot.airport.common.annotation.Excel;
4
+import lombok.Data;
5
+
6
+import java.io.Serializable;
7
+import java.math.BigDecimal;
8
+
9
+/**
10
+ * 异常查获数据 DTO
11
+ *
12
+ * @author wangxx
13
+ * @date 2026-03-23
14
+ */
15
+@Data
16
+public class ItemAbnormalSeizureDto implements Serializable {
17
+
18
+    private static final long serialVersionUID = 1L;
19
+
20
+    /**
21
+     * 大队名称
22
+     */
23
+    @Excel(name = "大队")
24
+    private String brigadeName;
25
+
26
+    /**
27
+     * 科室名称
28
+     */
29
+    @Excel(name = "科室")
30
+    private String departmentName;
31
+
32
+    /**
33
+     * 班组名称
34
+     */
35
+    @Excel(name = "班组")
36
+    private String teamName;
37
+
38
+    /**
39
+     * 姓名
40
+     */
41
+    @Excel(name = "姓名")
42
+    private String userName;
43
+
44
+    /**
45
+     * 查获数量
46
+     */
47
+    @Excel(name = "查获数量")
48
+    private BigDecimal seizureQuantity;
49
+
50
+    /**
51
+     * 排名
52
+     */
53
+    private Integer rank;
54
+
55
+}

+ 17 - 0
airport-item/src/main/java/com/sundot/airport/item/domain/ItemAbnormalSeizureRankDto.java

@@ -0,0 +1,17 @@
1
+package com.sundot.airport.item.domain;
2
+
3
+import lombok.Data;
4
+
5
+import java.util.List;
6
+
7
+@Data
8
+public class ItemAbnormalSeizureRankDto {
9
+    /**
10
+     * 低于整体水平
11
+     */
12
+    private List<ItemAbnormalSeizureDto> lowList;
13
+    /**
14
+     * 高于整体水平
15
+     */
16
+    private List<ItemAbnormalSeizureDto> higList;
17
+}

+ 50 - 0
airport-item/src/main/java/com/sundot/airport/item/domain/ItemLargeScreenBrigadeRankDto.java

@@ -0,0 +1,50 @@
1
+package com.sundot.airport.item.domain;
2
+
3
+import com.sundot.airport.common.annotation.Excel;
4
+import lombok.Data;
5
+
6
+import java.io.Serializable;
7
+import java.math.BigDecimal;
8
+
9
+/**
10
+ * 大队查获排名 DTO
11
+ *
12
+ * @author wangxx
13
+ * @date 2026-03-23
14
+ */
15
+@Data
16
+public class ItemLargeScreenBrigadeRankDto implements Serializable {
17
+
18
+    private static final long serialVersionUID = 1L;
19
+
20
+    /**
21
+     * 大队 ID
22
+     */
23
+    @Excel(name = "大队 ID")
24
+    private Long brigadeId;
25
+
26
+    /**
27
+     * 大队名称
28
+     */
29
+    @Excel(name = "大队名称")
30
+    private String brigadeName;
31
+
32
+    /**
33
+     * 查获数量
34
+     */
35
+    @Excel(name = "查获数量")
36
+    private BigDecimal quantity;
37
+
38
+    /**
39
+     * 排名
40
+     */
41
+    @Excel(name = "排名")
42
+    private Integer rank;
43
+
44
+    /**
45
+     * 占比
46
+     */
47
+    @Excel(name = "占比")
48
+    private BigDecimal scale;
49
+
50
+}

+ 36 - 0
airport-item/src/main/java/com/sundot/airport/item/domain/ItemLargeScreenInfoDto.java

@@ -44,6 +44,42 @@ public class ItemLargeScreenInfoDto implements Serializable {
44 44
     private String inspectUserName;
45 45
 
46 46
     /**
47
+     * 大队 ID
48
+     */
49
+    @Excel(name = "大队 ID")
50
+    private Long inspectBrigadeId;
51
+
52
+    /**
53
+     * 大队名称
54
+     */
55
+    @Excel(name = "大队名称")
56
+    private String inspectBrigadeName;
57
+
58
+    /**
59
+     * 科室 ID
60
+     */
61
+    @Excel(name = "科室 ID")
62
+    private Long inspectDepartmentId;
63
+
64
+    /**
65
+     * 科室名称
66
+     */
67
+    @Excel(name = "科室名称")
68
+    private String inspectDepartmentName;
69
+
70
+    /**
71
+     * 班组 ID
72
+     */
73
+    @Excel(name = "班组 ID")
74
+    private Long inspectTeamId;
75
+
76
+    /**
77
+     * 班组名称
78
+     */
79
+    @Excel(name = "班组名称")
80
+    private String inspectTeamName;
81
+
82
+    /**
47 83
      * 查获时间
48 84
      */
49 85
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")

+ 79 - 0
airport-item/src/main/java/com/sundot/airport/item/domain/ItemLargeScreenPoliceGroupDto.java

@@ -0,0 +1,79 @@
1
+package com.sundot.airport.item.domain;
2
+
3
+import com.fasterxml.jackson.annotation.JsonFormat;
4
+import com.sundot.airport.common.annotation.Excel;
5
+import lombok.Data;
6
+import org.springframework.format.annotation.DateTimeFormat;
7
+
8
+import java.io.Serializable;
9
+import java.math.BigDecimal;
10
+import java.util.Date;
11
+
12
+/**
13
+ * 移交公安数据表格 DTO
14
+ *
15
+ * @author wangxx
16
+ * @date 2026-03-23
17
+ */
18
+@Data
19
+public class ItemLargeScreenPoliceGroupDto implements Serializable {
20
+
21
+    private static final long serialVersionUID = 1L;
22
+
23
+    /**
24
+     * 大队名称
25
+     */
26
+    @Excel(name = "大队")
27
+    private String brigadeName;
28
+
29
+    /**
30
+     * 科室名称
31
+     */
32
+    @Excel(name = "科室")
33
+    private String departmentName;
34
+
35
+    /**
36
+     * 班组名称
37
+     */
38
+    @Excel(name = "班组")
39
+    private String teamName;
40
+
41
+    /**
42
+     * 姓名
43
+     */
44
+    @Excel(name = "姓名")
45
+    private String userName;
46
+
47
+    /**
48
+     * 查获物品
49
+     */
50
+    @Excel(name = "查获物品")
51
+    private String itemName;
52
+
53
+    /**
54
+     * 查获数量
55
+     */
56
+    @Excel(name = "查获数量")
57
+    private BigDecimal quantity;
58
+
59
+    /**
60
+     * 查获部位
61
+     */
62
+    @Excel(name = "查获部位")
63
+    private String positionName;
64
+
65
+    /**
66
+     * 查获时间
67
+     */
68
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
69
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
70
+    @Excel(name = "查获时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
71
+    private Date seizureTime;
72
+
73
+    /**
74
+     * 查获通道
75
+     */
76
+    @Excel(name = "查获通道")
77
+    private String channelName;
78
+
79
+}

+ 34 - 0
airport-item/src/main/java/com/sundot/airport/item/domain/ItemLargeScreenPoliceStatsDto.java

@@ -0,0 +1,34 @@
1
+package com.sundot.airport.item.domain;
2
+
3
+import com.sundot.airport.common.annotation.Excel;
4
+import lombok.Data;
5
+
6
+import java.io.Serializable;
7
+import java.math.BigDecimal;
8
+import java.util.List;
9
+
10
+/**
11
+ * 移交公安数据统计 DTO
12
+ * 用于展示查获总数和大队排名
13
+ *
14
+ * @author wangxx
15
+ * @date 2026-03-23
16
+ */
17
+@Data
18
+public class ItemLargeScreenPoliceStatsDto implements Serializable {
19
+
20
+    private static final long serialVersionUID = 1L;
21
+
22
+    /**
23
+     * 查获总数量
24
+     */
25
+    @Excel(name = "查获总数量")
26
+    private BigDecimal totalQuantity;
27
+
28
+    /**
29
+     * 大队查获排名列表
30
+     */
31
+    @Excel(name = "大队查获排名列表")
32
+    private List<ItemLargeScreenBrigadeRankDto> brigadeRankList;
33
+
34
+}

+ 79 - 0
airport-item/src/main/java/com/sundot/airport/item/domain/ItemXRayMissCheckDto.java

@@ -0,0 +1,79 @@
1
+package com.sundot.airport.item.domain;
2
+
3
+import com.fasterxml.jackson.annotation.JsonFormat;
4
+import com.sundot.airport.common.annotation.Excel;
5
+import lombok.Data;
6
+import org.springframework.format.annotation.DateTimeFormat;
7
+
8
+import java.io.Serializable;
9
+import java.math.BigDecimal;
10
+import java.util.Date;
11
+
12
+/**
13
+ * X 光机漏检数据表格 DTO
14
+ *
15
+ * @author wangxx
16
+ * @date 2026-03-23
17
+ */
18
+@Data
19
+public class ItemXRayMissCheckDto implements Serializable {
20
+
21
+    private static final long serialVersionUID = 1L;
22
+
23
+    /**
24
+     * 大队名称
25
+     */
26
+    @Excel(name = "大队")
27
+    private String brigadeName;
28
+
29
+    /**
30
+     * 科室名称
31
+     */
32
+    @Excel(name = "科室")
33
+    private String departmentName;
34
+
35
+    /**
36
+     * 班组名称
37
+     */
38
+    @Excel(name = "班组")
39
+    private String teamName;
40
+
41
+    /**
42
+     * 姓名(开机员)
43
+     */
44
+    @Excel(name = "姓名")
45
+    private String userName;
46
+
47
+    /**
48
+     * 漏检物品
49
+     */
50
+    @Excel(name = "漏检物品")
51
+    private String missItemName;
52
+
53
+    /**
54
+     * 漏检数量
55
+     */
56
+    @Excel(name = "漏检数量")
57
+    private BigDecimal missQuantity;
58
+
59
+    /**
60
+     * 漏检部位
61
+     */
62
+    @Excel(name = "漏检部位")
63
+    private String missPosition;
64
+
65
+    /**
66
+     * 漏检通道
67
+     */
68
+    @Excel(name = "漏检通道")
69
+    private String channelName;
70
+
71
+    /**
72
+     * 漏检时间
73
+     */
74
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
75
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
76
+    @Excel(name = "漏检时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
77
+    private Date missTime;
78
+
79
+}

+ 44 - 0
airport-item/src/main/java/com/sundot/airport/item/domain/ItemXRayMissCheckUserStatsDto.java

@@ -0,0 +1,44 @@
1
+package com.sundot.airport.item.domain;
2
+
3
+import com.sundot.airport.common.annotation.Excel;
4
+import lombok.Data;
5
+
6
+import java.io.Serializable;
7
+import java.math.BigDecimal;
8
+
9
+/**
10
+ * X 光机漏检人员统计 DTO
11
+ *
12
+ * @author wangxx
13
+ * @date 2026-03-23
14
+ */
15
+@Data
16
+public class ItemXRayMissCheckUserStatsDto implements Serializable {
17
+
18
+    private static final long serialVersionUID = 1L;
19
+
20
+    /**
21
+     * 开机员 ID
22
+     */
23
+    @Excel(name = "开机员 ID")
24
+    private Long xrayOperatorId;
25
+
26
+    /**
27
+     * 开机员姓名
28
+     */
29
+    @Excel(name = "开机员姓名")
30
+    private String xrayOperatorName;
31
+
32
+    /**
33
+     * 漏检总数量
34
+     */
35
+    @Excel(name = "漏检总数量")
36
+    private BigDecimal totalMissQuantity;
37
+
38
+    /**
39
+     * 排名
40
+     */
41
+    @Excel(name = "排名")
42
+    private Integer rank;
43
+
44
+}

+ 16 - 0
airport-item/src/main/java/com/sundot/airport/item/mapper/ItemLargeScreenMapper.java

@@ -206,4 +206,20 @@ public interface ItemLargeScreenMapper {
206 206
      */
207 207
     List<SysUsageReportSeizureDto> getSeizureModule(BaseLargeScreenQueryParamDto dto);
208 208
 
209
+    /**
210
+     * X 光机漏检数据
211
+     *
212
+     * @param dto 查询参数
213
+     * @return X 光机漏检数据
214
+     */
215
+    List<ItemXRayMissCheckDto> xRayMissCheck(BaseLargeScreenQueryParamDto dto);
216
+
217
+    /**
218
+     * X 光机漏检人员统计 TOP3
219
+     *
220
+     * @param dto 查询参数
221
+     * @return 漏检数量排名前 3 的开机员
222
+     */
223
+    List<ItemXRayMissCheckUserStatsDto> xRayMissCheckUserStatsTop3(BaseLargeScreenQueryParamDto dto);
224
+
209 225
 }

+ 40 - 0
airport-item/src/main/java/com/sundot/airport/item/service/ItemLargeScreenService.java

@@ -119,4 +119,44 @@ public interface ItemLargeScreenService {
119 119
      */
120 120
     public SysUsageReportDto.SeizureModule getSeizureModule(BaseLargeScreenQueryParamDto dto);
121 121
 
122
+    /**
123
+     * 移交公安数据统计(查获总数 + 大队排名)
124
+     *
125
+     * @param dto 大屏查询参数
126
+     * @return 移交公安数据统计
127
+     */
128
+    public ItemLargeScreenPoliceStatsDto getPoliceStats(BaseLargeScreenQueryParamDto dto);
129
+
130
+    /**
131
+     * X 光机漏检数据
132
+     *
133
+     * @param dto 大屏查询参数
134
+     * @return X 光机漏检数据
135
+     */
136
+    public List<ItemXRayMissCheckDto> xRayMissCheck(BaseLargeScreenQueryParamDto dto);
137
+
138
+    /**
139
+     * X 光机漏检人员统计 TOP3
140
+     *
141
+     * @param dto 大屏查询参数
142
+     * @return X 光机漏检人员统计 TOP3
143
+     */
144
+    public List<ItemXRayMissCheckUserStatsDto> xRayMissCheckUserStatsTop3(BaseLargeScreenQueryParamDto dto);
145
+
146
+    /**
147
+     * 异常查获数据
148
+     *
149
+     * @param dto 大屏查询参数
150
+     * @return 异常查获数据
151
+     */
152
+    public List<ItemAbnormalSeizureDto> getAbnormalSeizureData(BaseLargeScreenQueryParamDto dto);
153
+
154
+    /**
155
+     * 异常查获数据 TOP3(3 个高于 +3 个低于)
156
+     *
157
+     * @param dto 大屏查询参数
158
+     * @return 异常查获数据 TOP3
159
+     */
160
+    public ItemAbnormalSeizureRankDto getAbnormalSeizureDataTop3(BaseLargeScreenQueryParamDto dto);
161
+
122 162
 }

+ 262 - 0
airport-item/src/main/java/com/sundot/airport/item/service/impl/ItemLargeScreenServiceImpl.java

@@ -3,13 +3,18 @@ package com.sundot.airport.item.service.impl;
3 3
 import cn.hutool.core.collection.CollUtil;
4 4
 import cn.hutool.core.util.StrUtil;
5 5
 import com.sundot.airport.common.core.domain.BaseLargeScreenQueryParamDto;
6
+import com.sundot.airport.common.core.domain.BoxPlotDataDto;
7
+import com.sundot.airport.common.core.domain.GroupedBoxPlotDataDto;
8
+import com.sundot.airport.common.core.domain.LargeScreenHomePageUserInfoSqlDto;
6 9
 import com.sundot.airport.common.core.domain.SysUsageReportDto;
7 10
 import com.sundot.airport.common.core.domain.SysUsageReportSeizureDto;
8 11
 import com.sundot.airport.common.core.domain.entity.SysUser;
9 12
 import com.sundot.airport.common.dto.BaseCommonDto;
10 13
 import com.sundot.airport.item.domain.*;
11 14
 import com.sundot.airport.item.mapper.ItemLargeScreenMapper;
15
+import com.sundot.airport.item.mapper.SeizureReportMapper;
12 16
 import com.sundot.airport.item.service.ItemLargeScreenService;
17
+import com.sundot.airport.item.service.SeizureDistributionService;
13 18
 import com.sundot.airport.system.service.ISysUserService;
14 19
 import org.springframework.beans.factory.annotation.Autowired;
15 20
 import org.springframework.stereotype.Service;
@@ -35,6 +40,10 @@ public class ItemLargeScreenServiceImpl implements ItemLargeScreenService {
35 40
     private ItemLargeScreenMapper itemLargeScreenMapper;
36 41
     @Autowired
37 42
     private ISysUserService sysUserService;
43
+    @Autowired
44
+    private SeizureDistributionService seizureDistributionService;
45
+    @Autowired
46
+    private SeizureReportMapper seizureReportMapper;
38 47
 
39 48
     /**
40 49
      * 移交公安情况
@@ -339,4 +348,257 @@ public class ItemLargeScreenServiceImpl implements ItemLargeScreenService {
339 348
                 .collect(Collectors.toList());
340 349
     }
341 350
 
351
+    /**
352
+     * 移交公安数据统计(查获总数 + 大队排名)
353
+     *
354
+     * @param dto 大屏查询参数
355
+     * @return 移交公安数据统计
356
+     */
357
+    @Override
358
+    public ItemLargeScreenPoliceStatsDto getPoliceStats(BaseLargeScreenQueryParamDto dto) {
359
+        // 1. 查询移交公安的明细数据
360
+        List<ItemLargeScreenInfoDto> policeList = itemLargeScreenMapper.police(dto);
361
+
362
+        // 2. 计算查获总数量
363
+        BigDecimal totalQuantity = policeList.stream()
364
+                .map(ItemLargeScreenInfoDto::getQuantity)
365
+                .filter(java.util.Objects::nonNull)
366
+                .reduce(BigDecimal.ZERO, BigDecimal::add);
367
+
368
+        // 3. 按大队分组统计
369
+        Map<String, List<ItemLargeScreenInfoDto>> groupedByBrigade = policeList.stream()
370
+                .collect(Collectors.groupingBy(item ->
371
+                        item.getInspectBrigadeId() + "###" + item.getInspectBrigadeName()));
372
+
373
+        // 4. 转换为大队排名列表
374
+        List<ItemLargeScreenBrigadeRankDto> brigadeRankList = groupedByBrigade.entrySet().stream()
375
+                .map(entry -> {
376
+                    ItemLargeScreenBrigadeRankDto rankDto = new ItemLargeScreenBrigadeRankDto();
377
+                    List<ItemLargeScreenInfoDto> items = entry.getValue();
378
+
379
+                    if (!items.isEmpty()) {
380
+                        ItemLargeScreenInfoDto firstItem = items.get(0);
381
+                        rankDto.setBrigadeId(firstItem.getInspectBrigadeId());
382
+                        rankDto.setBrigadeName(firstItem.getInspectBrigadeName());
383
+
384
+                        // 计算该科室的查获总数量
385
+                        BigDecimal deptQuantity = items.stream()
386
+                                .map(ItemLargeScreenInfoDto::getQuantity)
387
+                                .filter(java.util.Objects::nonNull)
388
+                                .reduce(BigDecimal.ZERO, BigDecimal::add);
389
+                        rankDto.setQuantity(deptQuantity);
390
+
391
+                        // 计算占比
392
+                        if (totalQuantity.compareTo(BigDecimal.ZERO) > 0) {
393
+                            rankDto.setScale(deptQuantity.multiply(new BigDecimal("100"))
394
+                                    .divide(totalQuantity, 2, BigDecimal.ROUND_HALF_UP));
395
+                        } else {
396
+                            rankDto.setScale(BigDecimal.ZERO);
397
+                        }
398
+                    }
399
+
400
+                    return rankDto;
401
+                })
402
+                .sorted(Comparator.comparing(ItemLargeScreenBrigadeRankDto::getQuantity).reversed())
403
+                .collect(Collectors.toList());
404
+
405
+        // 5. 设置排名
406
+        for (int i = 0; i < brigadeRankList.size(); i++) {
407
+            brigadeRankList.get(i).setRank(i + 1);
408
+        }
409
+
410
+        // 6. 构造返回结果
411
+        ItemLargeScreenPoliceStatsDto result = new ItemLargeScreenPoliceStatsDto();
412
+        result.setTotalQuantity(totalQuantity);
413
+        result.setBrigadeRankList(brigadeRankList);
414
+
415
+        return result;
416
+    }
417
+
418
+    /**
419
+     * X 光机漏检数据
420
+     *
421
+     * @param dto 大屏查询参数
422
+     * @return X 光机漏检数据
423
+     */
424
+    @Override
425
+    public List<ItemXRayMissCheckDto> xRayMissCheck(BaseLargeScreenQueryParamDto dto) {
426
+        return itemLargeScreenMapper.xRayMissCheck(dto);
427
+    }
428
+
429
+    /**
430
+     * X 光机漏检人员统计 TOP3
431
+     *
432
+     * @param dto 大屏查询参数
433
+     * @return X 光机漏检人员统计 TOP3
434
+     */
435
+    @Override
436
+    public List<ItemXRayMissCheckUserStatsDto> xRayMissCheckUserStatsTop3(BaseLargeScreenQueryParamDto dto) {
437
+        // 直接查询并返回前 3 名
438
+        return itemLargeScreenMapper.xRayMissCheckUserStatsTop3(dto);
439
+    }
440
+
441
+    /**
442
+     * 异常查获数据
443
+     *
444
+     * @param dto 大屏查询参数
445
+     * @return 异常查获数据
446
+     */
447
+    @Override
448
+    public List<ItemAbnormalSeizureDto> getAbnormalSeizureData(BaseLargeScreenQueryParamDto dto) {
449
+        // 1. 获取箱线图数据(用于获取上下限)
450
+        List<GroupedBoxPlotDataDto> boxPlotDataList = seizureDistributionService.calculateBoxPlotData(dto);
451
+
452
+        if (boxPlotDataList == null || boxPlotDataList.isEmpty()) {
453
+            return Collections.emptyList();
454
+        }
455
+
456
+        // 2. 获取站级数据(第一个分组)的上下限
457
+        BigDecimal lowerBound = BigDecimal.ZERO;
458
+        BigDecimal upperBound = BigDecimal.ZERO;
459
+
460
+        if (!boxPlotDataList.isEmpty()) {
461
+            BoxPlotDataDto boxPlotData = boxPlotDataList.get(0).getBoxPlotData();
462
+            lowerBound = boxPlotData.getLowerBound();
463
+            upperBound = boxPlotData.getUpperBound();
464
+        }
465
+
466
+        // 3. 查询用户信息和查获数量
467
+        List<ItemLargeScreenHomePageSeizureReportSqlDto> seizureReportList = seizureReportMapper.homePageSeizureReport(dto);
468
+        List<LargeScreenHomePageUserInfoSqlDto> userInfoList = sysUserService.homePageUserInfo();
469
+
470
+        if (CollUtil.isEmpty(seizureReportList) || CollUtil.isEmpty(userInfoList)) {
471
+            return Collections.emptyList();
472
+        }
473
+
474
+        // 4. 按用户分组统计查获总数
475
+        Map<Long, BigDecimal> userSeizureMap = seizureReportList.stream()
476
+                .collect(Collectors.groupingBy(
477
+                        ItemLargeScreenHomePageSeizureReportSqlDto::getUserId,
478
+                        Collectors.reducing(BigDecimal.ZERO, ItemLargeScreenHomePageSeizureReportSqlDto::getQuantity, BigDecimal::add)
479
+                ));
480
+
481
+        // 5. 筛选出异常值
482
+        List<ItemAbnormalSeizureDto> abnormalList = new ArrayList<>();
483
+
484
+        for (LargeScreenHomePageUserInfoSqlDto userInfo : userInfoList) {
485
+            BigDecimal totalQuantity = userSeizureMap.getOrDefault(userInfo.getUserId(), BigDecimal.ZERO);
486
+
487
+            // 判断是否为异常值
488
+            if (totalQuantity.compareTo(lowerBound) < 0 || totalQuantity.compareTo(upperBound) > 0) {
489
+                ItemAbnormalSeizureDto abnormalDto = new ItemAbnormalSeizureDto();
490
+                abnormalDto.setBrigadeName(userInfo.getBrigadeName());
491
+                abnormalDto.setDepartmentName(userInfo.getDepartmentName());
492
+                abnormalDto.setTeamName(userInfo.getTeamName());
493
+                abnormalDto.setUserName(userInfo.getNickName());
494
+                abnormalDto.setSeizureQuantity(totalQuantity);
495
+                abnormalList.add(abnormalDto);
496
+            }
497
+        }
498
+
499
+        return abnormalList;
500
+    }
501
+
502
+    /**
503
+     * 异常查获数据 TOP3(3 个高于 +3 个低于)
504
+     *
505
+     * @param dto 大屏查询参数
506
+     * @return 异常查获数据 TOP3
507
+     */
508
+    @Override
509
+    public ItemAbnormalSeizureRankDto getAbnormalSeizureDataTop3(BaseLargeScreenQueryParamDto dto) {
510
+        ItemAbnormalSeizureRankDto result = new ItemAbnormalSeizureRankDto();
511
+        // 1. 获取箱线图数据(用于获取上下限)
512
+        List<GroupedBoxPlotDataDto> boxPlotDataList = seizureDistributionService.calculateBoxPlotData(dto);
513
+
514
+        if (boxPlotDataList == null || boxPlotDataList.isEmpty()) {
515
+            return result;
516
+        }
517
+
518
+        // 2. 获取站级数据(第一个分组)的上下限
519
+        BigDecimal lowerBound = BigDecimal.ZERO;
520
+        BigDecimal upperBound = BigDecimal.ZERO;
521
+
522
+        if (!boxPlotDataList.isEmpty()) {
523
+            BoxPlotDataDto boxPlotData = boxPlotDataList.get(0).getBoxPlotData();
524
+            lowerBound = boxPlotData.getLowerBound();
525
+            upperBound = boxPlotData.getUpperBound();
526
+        }
527
+
528
+        // 3. 查询用户信息和查获数量
529
+        List<ItemLargeScreenHomePageSeizureReportSqlDto> seizureReportList = seizureReportMapper.homePageSeizureReport(dto);
530
+        List<LargeScreenHomePageUserInfoSqlDto> userInfoList = sysUserService.homePageUserInfo();
531
+
532
+        if (CollUtil.isEmpty(seizureReportList) || CollUtil.isEmpty(userInfoList)) {
533
+            return result;
534
+        }
535
+
536
+        // 4. 按用户分组统计查获总数
537
+        Map<Long, BigDecimal> userSeizureMap = seizureReportList.stream()
538
+                .collect(Collectors.groupingBy(
539
+                        ItemLargeScreenHomePageSeizureReportSqlDto::getUserId,
540
+                        Collectors.reducing(BigDecimal.ZERO, ItemLargeScreenHomePageSeizureReportSqlDto::getQuantity, BigDecimal::add)
541
+                ));
542
+
543
+        // 5. 筛选出异常值并分类
544
+        List<ItemAbnormalSeizureDto> higherList = new ArrayList<>();
545
+        List<ItemAbnormalSeizureDto> lowerList = new ArrayList<>();
546
+
547
+        for (LargeScreenHomePageUserInfoSqlDto userInfo : userInfoList) {
548
+            BigDecimal totalQuantity = userSeizureMap.getOrDefault(userInfo.getUserId(), BigDecimal.ZERO);
549
+
550
+            // 判断是否为异常值
551
+            if (totalQuantity.compareTo(upperBound) > 0) {
552
+                // 显著高于
553
+                ItemAbnormalSeizureDto abnormalDto = new ItemAbnormalSeizureDto();
554
+                abnormalDto.setBrigadeName(userInfo.getBrigadeName());
555
+                abnormalDto.setDepartmentName(userInfo.getDepartmentName());
556
+                abnormalDto.setTeamName(userInfo.getTeamName());
557
+                abnormalDto.setUserName(userInfo.getNickName());
558
+                abnormalDto.setSeizureQuantity(totalQuantity);
559
+                higherList.add(abnormalDto);
560
+            } else if (totalQuantity.compareTo(lowerBound) < 0) {
561
+                // 显著低于
562
+                ItemAbnormalSeizureDto abnormalDto = new ItemAbnormalSeizureDto();
563
+                abnormalDto.setBrigadeName(userInfo.getBrigadeName());
564
+                abnormalDto.setDepartmentName(userInfo.getDepartmentName());
565
+                abnormalDto.setTeamName(userInfo.getTeamName());
566
+                abnormalDto.setUserName(userInfo.getNickName());
567
+                abnormalDto.setSeizureQuantity(totalQuantity);
568
+                lowerList.add(abnormalDto);
569
+            }
570
+        }
571
+
572
+        // 6. 排序并设置排名
573
+        higherList.sort(Comparator.comparing(ItemAbnormalSeizureDto::getSeizureQuantity).reversed());
574
+        lowerList.sort(Comparator.comparing(ItemAbnormalSeizureDto::getSeizureQuantity));
575
+
576
+        for (int i = 0; i < higherList.size(); i++) {
577
+            higherList.get(i).setRank(i + 1);
578
+        }
579
+
580
+        for (int i = 0; i < lowerList.size(); i++) {
581
+            lowerList.get(i).setRank(i + 1);
582
+        }
583
+
584
+        // 7. 取前 3 个
585
+        int higherCount = Math.min(3, higherList.size());
586
+        int lowerCount = Math.min(3, lowerList.size());
587
+        List<ItemAbnormalSeizureDto> lowList = new ArrayList<>();
588
+        List<ItemAbnormalSeizureDto> higList = new ArrayList<>();
589
+
590
+        // 添加高于的 3 个
591
+        for (int i = 0; i < higherCount; i++) {
592
+            higList.add(higherList.get(i));
593
+        }
594
+        result.setHigList(higList);
595
+
596
+        // 添加低于的 3 个
597
+        for (int i = 0; i < lowerCount; i++) {
598
+            lowList.add(lowerList.get(i));
599
+        }
600
+        result.setLowList(lowList);
601
+        return result;
602
+    }
603
+
342 604
 }

+ 67 - 0
airport-item/src/main/resources/mapper/item/ItemLargeScreenMapper.xml

@@ -37,6 +37,12 @@
37 37
         isr.channel_name channelName,
38 38
         isr.check_method checkMethod,
39 39
         isr.check_method_desc checkMethodDesc,
40
+        isr.inspect_brigade_id inspectBrigadeId,
41
+        isr.inspect_brigade_name inspectBrigadeName,
42
+        isr.inspect_department_id inspectDepartmentId,
43
+        isr.inspect_department_name inspectDepartmentName,
44
+        isr.inspect_team_id inspectTeamId,
45
+        isr.inspect_team_name inspectTeamName,
40 46
         isi.category_code_one categoryCodeOne,
41 47
         isi.category_name_one categoryNameOne,
42 48
         isi.category_code_two categoryCodeTwo,
@@ -666,4 +672,65 @@
666 672
         </if>
667 673
     </select>
668 674
 
675
+    <!-- X 光机漏检数据 -->
676
+    <select id="xRayMissCheck" resultType="com.sundot.airport.item.domain.ItemXRayMissCheckDto">
677
+        select
678
+        isr.inspect_brigade_name brigadeName,
679
+        isr.inspect_department_name departmentName,
680
+        isr.inspect_team_name teamName,
681
+        isr.xray_operator_name userName,
682
+        isi.category_name_two missItemName,
683
+        isi.quantity missQuantity,
684
+        concat(
685
+        ifnull(isi.check_position_name_one, ''),
686
+        if(isi.check_position_name_two is null or isi.check_position_name_two = '', '', concat('/',
687
+        isi.check_position_name_two)),
688
+        if(isi.check_position_specific is null or isi.check_position_specific = '', '', concat('/',
689
+        isi.check_position_specific))
690
+        ) missPosition,
691
+        isr.channel_name channelName,
692
+        isr.seizure_time missTime
693
+        from item_seizure_record isr
694
+        inner join item_seizure_items isi on isi.record_id = isr.id
695
+        where isr.power_on_instruction=1
696
+        <if test="specifiedDate != null">
697
+            and date(isr.seizure_time) = #{specifiedDate}
698
+        </if>
699
+        <if test="startDate != null and endDate != null">
700
+            and (isr.seizure_time >= #{startDate}
701
+            AND isr.seizure_time <![CDATA[ < ]]> date_add(#{endDate}, interval 1 day))
702
+        </if>
703
+        <if test="inspectStationId != null">
704
+            and isr.inspect_station_id = #{inspectStationId}
705
+        </if>
706
+        and isr.process_status = 3
707
+        order by isr.seizure_time desc
708
+    </select>
709
+
710
+    <!-- X 光机漏检人员统计 TOP3 -->
711
+    <select id="xRayMissCheckUserStatsTop3" resultType="com.sundot.airport.item.domain.ItemXRayMissCheckUserStatsDto">
712
+        select
713
+        isr.xray_operator_id xrayOperatorId,
714
+        isr.xray_operator_name xrayOperatorName,
715
+        sum(isi.quantity) totalMissQuantity
716
+        from item_seizure_record isr
717
+        inner join item_seizure_items isi on isi.record_id = isr.id
718
+        where isr.power_on_instruction=1
719
+        <if test="specifiedDate != null">
720
+            and date(isr.seizure_time) = #{specifiedDate}
721
+        </if>
722
+        <if test="startDate != null and endDate != null">
723
+            and (isr.seizure_time >= #{startDate}
724
+            AND isr.seizure_time <![CDATA[ < ]]> date_add(#{endDate}, interval 1 day))
725
+        </if>
726
+        <if test="inspectStationId != null">
727
+            and isr.inspect_station_id = #{inspectStationId}
728
+        </if>
729
+        and isr.process_status = 3
730
+        and isr.xray_operator_id is not null
731
+        group by isr.xray_operator_id, isr.xray_operator_name
732
+        order by totalMissQuantity desc
733
+        limit 3
734
+    </select>
735
+
669 736
 </mapper>