Bläddra i källkod

质控分析报告-风险隐患

chenshudong 1 månad sedan
förälder
incheckning
88c7ec1f52

+ 240 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/quality/ItemCategoryStatsController.java

@@ -0,0 +1,240 @@
1
+package com.sundot.airport.web.controller.quality;
2
+
3
+import com.sundot.airport.common.core.controller.BaseController;
4
+import com.sundot.airport.common.core.domain.AjaxResult;
5
+import com.sundot.airport.common.core.domain.BaseLargeScreenQueryParamDto;
6
+import com.sundot.airport.common.statistics.TimeDimensionParams;
7
+import com.sundot.airport.common.utils.DeptUtils;
8
+import com.sundot.airport.item.domain.ItemLargeScreenTimeSpanDto;
9
+import com.sundot.airport.item.domain.dto.ChannelRankingStatsDTO;
10
+import com.sundot.airport.item.domain.dto.ConcealmentPositionStatsDTO;
11
+import com.sundot.airport.item.domain.dto.ItemCategoryStatsDTO;
12
+import com.sundot.airport.item.domain.dto.PostCategoryStatsDTO;
13
+import com.sundot.airport.item.domain.home.SeizureReportDTO;
14
+import com.sundot.airport.item.service.IItemCategoryStatsService;
15
+import com.sundot.airport.item.service.ItemLargeScreenService;
16
+import com.sundot.airport.system.service.ISysDeptService;
17
+import io.swagger.annotations.Api;
18
+import io.swagger.annotations.ApiOperation;
19
+import lombok.extern.slf4j.Slf4j;
20
+import org.springframework.beans.factory.annotation.Autowired;
21
+import org.springframework.web.bind.annotation.GetMapping;
22
+import org.springframework.web.bind.annotation.RequestMapping;
23
+import org.springframework.web.bind.annotation.RestController;
24
+
25
+import java.util.ArrayList;
26
+import java.util.Calendar;
27
+import java.util.List;
28
+
29
+/**
30
+ * 物品分类统计Controller
31
+ * 提供物品分类统计相关接口
32
+ *
33
+ * @author wangxx
34
+ * @date 2024-07-23
35
+ */
36
+@Slf4j
37
+@RestController
38
+@RequestMapping("/quality/item-category-stats")
39
+@Api(tags = "物品分类统计")
40
+public class ItemCategoryStatsController extends BaseController {
41
+
42
+    @Autowired
43
+    private ISysDeptService sysDeptService;
44
+
45
+
46
+    @Autowired
47
+    private IItemCategoryStatsService iItemCategoryStatsService;
48
+
49
+    @Autowired
50
+    private ItemLargeScreenService itemLargeScreenService;
51
+
52
+
53
+    /**
54
+     * 获取物品分类统计(全站整体数据)
55
+     * 支持按年、季度、月维度统计
56
+     */
57
+    @ApiOperation("获取物品分类统计")
58
+    @GetMapping("/category-stats")
59
+    public AjaxResult getItemCategoryStats(TimeDimensionParams params) {
60
+        try {
61
+            // 获取当前用户所在站点ID
62
+            Long topSiteId = DeptUtils.getTopSiteId(sysDeptService.selectDeptById(getDeptId()));
63
+            if (topSiteId == null) {
64
+                return AjaxResult.error("无法找到有效的站点信息");
65
+            }
66
+
67
+            ItemCategoryStatsDTO stationCategoryStats = iItemCategoryStatsService.getStationCategoryStats(params, topSiteId);
68
+
69
+            return AjaxResult.success("获取成功", stationCategoryStats);
70
+
71
+        } catch (Exception e) {
72
+            log.error("获取物品分类统计失败", e);
73
+            return AjaxResult.error("获取物品分类统计失败: " + e.getMessage());
74
+        }
75
+    }
76
+
77
+    /**
78
+     * 获取查获时段趋势图
79
+     */
80
+    @ApiOperation("获取查获时段趋势图")
81
+    @GetMapping("/seizure-time-trend")
82
+    public AjaxResult getSeizureTimeTrend(TimeDimensionParams params) {
83
+        try {
84
+            // 获取当前用户所在站点ID
85
+            Long topSiteId = DeptUtils.getTopSiteId(sysDeptService.selectDeptById(getDeptId()));
86
+            if (topSiteId == null) {
87
+                return AjaxResult.error("无法找到有效的站点信息");
88
+            }
89
+
90
+            // 计算时间范围
91
+            TimeDimensionParams timeParams = calculateTimeRange(params);
92
+            BaseLargeScreenQueryParamDto dto = new BaseLargeScreenQueryParamDto();
93
+            dto.setStartDate(timeParams.getStartTime());
94
+            dto.setEndDate(timeParams.getEndTime());
95
+            dto.setInspectStationId(topSiteId);
96
+            List<ItemLargeScreenTimeSpanDto> result = itemLargeScreenService.appTimeSpan(dto);
97
+            return AjaxResult.success("获取成功", result);
98
+
99
+        } catch (Exception e) {
100
+            log.error("获取查获时段趋势失败", e);
101
+            return AjaxResult.error("获取查获时段趋势失败: " + e.getMessage());
102
+        }
103
+    }
104
+
105
+    /**
106
+     * 获取隐匿夹带部位分布统计(饼状图数据)
107
+     * 支持按年、季度、月维度统计
108
+     */
109
+    @ApiOperation("获取隐匿夹带部位分布统计")
110
+    @GetMapping("/concealment-position-stats")
111
+    public AjaxResult getConcealmentPositionStats(TimeDimensionParams params) {
112
+        try {
113
+            // 获取当前用户所在站点ID
114
+            Long topSiteId = DeptUtils.getTopSiteId(sysDeptService.selectDeptById(getDeptId()));
115
+            if (topSiteId == null) {
116
+                return AjaxResult.error("无法找到有效的站点信息");
117
+            }
118
+
119
+            ConcealmentPositionStatsDTO stationConcealmentPositionStats =
120
+                    iItemCategoryStatsService.getStationConcealmentPositionStats(params, topSiteId);
121
+
122
+            return AjaxResult.success("获取成功", stationConcealmentPositionStats);
123
+
124
+        } catch (Exception e) {
125
+            log.error("获取隐匿夹带部位分布统计失败", e);
126
+            return AjaxResult.error("获取隐匿夹带部位分布统计失败: " + e.getMessage());
127
+        }
128
+    }
129
+
130
+    /**
131
+     * 获取岗位分类统计(饼状图数据)
132
+     * 支持按年、季度、月维度统计
133
+     */
134
+    @ApiOperation("获取岗位分类统计")
135
+    @GetMapping("/post-category-stats")
136
+    public AjaxResult getPostCategoryStats(TimeDimensionParams params) {
137
+        try {
138
+            // 获取当前用户所在站点ID
139
+            Long topSiteId = DeptUtils.getTopSiteId(sysDeptService.selectDeptById(getDeptId()));
140
+            if (topSiteId == null) {
141
+                return AjaxResult.error("无法找到有效的站点信息");
142
+            }
143
+
144
+            PostCategoryStatsDTO stationPostCategoryStats =
145
+                    iItemCategoryStatsService.getStationPostCategoryStats(params, topSiteId);
146
+
147
+            return AjaxResult.success("获取成功", stationPostCategoryStats);
148
+
149
+        } catch (Exception e) {
150
+            log.error("获取岗位分类统计失败", e);
151
+            return AjaxResult.error("获取岗位分类统计失败: " + e.getMessage());
152
+        }
153
+    }
154
+
155
+    /**
156
+     * 获取通道排名统计(表格数据)
157
+     * 支持按年、季度、月维度统计
158
+     */
159
+    @ApiOperation("获取通道排名统计")
160
+    @GetMapping("/channel-ranking-stats")
161
+    public AjaxResult getChannelRankingStats(TimeDimensionParams params) {
162
+        try {
163
+            // 获取当前用户所在站点ID
164
+            Long topSiteId = DeptUtils.getTopSiteId(sysDeptService.selectDeptById(getDeptId()));
165
+            if (topSiteId == null) {
166
+                return AjaxResult.error("无法找到有效的站点信息");
167
+            }
168
+
169
+            ChannelRankingStatsDTO stationChannelRankingStats =
170
+                    iItemCategoryStatsService.getStationChannelRankingStats(params, topSiteId);
171
+
172
+            return AjaxResult.success("获取成功", stationChannelRankingStats);
173
+
174
+        } catch (Exception e) {
175
+            log.error("获取通道排名统计失败", e);
176
+            return AjaxResult.error("获取通道排名统计失败: " + e.getMessage());
177
+        }
178
+    }
179
+
180
+
181
+    /**
182
+     * "各大队查获排名
183
+     */
184
+    @ApiOperation("获取各大队查获排名")
185
+    @GetMapping("/brigade-ranking")
186
+    public AjaxResult getBrigadeRanking(TimeDimensionParams params) {
187
+        List<SeizureReportDTO.BrigadeRankingItem> brigadeRankings = new ArrayList<>();
188
+        try {
189
+            // 获取当前用户所在站点ID
190
+            Long topSiteId = DeptUtils.getTopSiteId(sysDeptService.selectDeptById(getDeptId()));
191
+            if (topSiteId == null) {
192
+                return AjaxResult.error("无法找到有效的站点信息");
193
+            }
194
+            brigadeRankings = iItemCategoryStatsService.getBrigadeRanking(topSiteId, params);
195
+        } catch (Exception e) {
196
+            log.error("获取各科室查获排名失败", e);
197
+            return AjaxResult.error("获取各科室查获排名失败: " + e.getMessage());
198
+        }
199
+        return AjaxResult.success("获取成功", brigadeRankings);
200
+    }
201
+
202
+
203
+    /**
204
+     * 计算时间范围
205
+     */
206
+    private TimeDimensionParams calculateTimeRange(TimeDimensionParams params) {
207
+        TimeDimensionParams result = new TimeDimensionParams();
208
+
209
+        if (params.getYear() != null) {
210
+            // 按年、季度、月计算时间范围
211
+            int year = params.getYear();
212
+            int startMonth = 1;
213
+            int endMonth = 12;
214
+
215
+            if (params.getQuarter() != null) {
216
+                // 按季度
217
+                startMonth = (params.getQuarter() - 1) * 3 + 1;
218
+                endMonth = startMonth + 2;
219
+            } else if (params.getMonth() != null) {
220
+                // 按月
221
+                startMonth = params.getMonth();
222
+                endMonth = params.getMonth();
223
+            }
224
+
225
+            Calendar cal = Calendar.getInstance();
226
+            cal.set(year, startMonth - 1, 1, 0, 0, 0);
227
+            result.setStartTime(cal.getTime());
228
+
229
+            cal.set(year, endMonth - 1, cal.getActualMaximum(Calendar.DAY_OF_MONTH), 23, 59, 59);
230
+            result.setEndTime(cal.getTime());
231
+        } else {
232
+            // 使用传入的时间范围
233
+            result.setStartTime(params.getStartTime());
234
+            result.setEndTime(params.getEndTime());
235
+        }
236
+
237
+        return result;
238
+    }
239
+
240
+}

+ 4 - 0
airport-common/src/main/java/com/sundot/airport/common/core/domain/BaseLargeScreenQueryParamDto.java

@@ -108,4 +108,8 @@ public class BaseLargeScreenQueryParamDto implements Serializable {
108 108
      */
109 109
     @ApiModelProperty("数据来源:individual=个人数据,team=班组数据")
110 110
     private String dataSource;
111
+
112
+    @ApiModelProperty("站id")
113
+    private Long inspectStationId;
114
+
111 115
 }

+ 68 - 0
airport-common/src/main/java/com/sundot/airport/common/statistics/TimeDimensionParams.java

@@ -0,0 +1,68 @@
1
+package com.sundot.airport.common.statistics;
2
+
3
+import com.fasterxml.jackson.annotation.JsonFormat;
4
+import io.swagger.annotations.ApiModelProperty;
5
+import lombok.Data;
6
+import org.springframework.format.annotation.DateTimeFormat;
7
+
8
+import java.util.Date;
9
+
10
+/**
11
+ * 时间维度参数
12
+ * 用于封装年、季度、月等时间维度参数
13
+ */
14
+@Data
15
+public class TimeDimensionParams {
16
+
17
+    /**
18
+     * 年份
19
+     */
20
+    @ApiModelProperty(value = "年份")
21
+    private Integer year;
22
+
23
+    /**
24
+     * 季度
25
+     */
26
+    @ApiModelProperty(value = "季度")
27
+    private Integer quarter;
28
+
29
+    /**
30
+     * 月份
31
+     */
32
+    @ApiModelProperty(value = "月份")
33
+    private Integer month;
34
+
35
+    /**
36
+     * 开始时间
37
+     */
38
+    @ApiModelProperty(value = "开始时间")
39
+    @JsonFormat(pattern = "yyyy-MM-dd")
40
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
41
+    private Date startTime;
42
+
43
+    /**
44
+     * 结束时间
45
+     */
46
+    @ApiModelProperty(value = "结束时间")
47
+    @JsonFormat(pattern = "yyyy-MM-dd")
48
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
49
+    private Date endTime;
50
+
51
+    /**
52
+     * 是否环比
53
+     */
54
+    @ApiModelProperty(value = "是否环比")
55
+    private Boolean chainRatio = false;
56
+
57
+    /**
58
+     * 是否同比
59
+     */
60
+    @ApiModelProperty(value = "是否同比")
61
+    private Boolean yearOnYear = false;
62
+
63
+    /**
64
+     * 对比周期数(用于环比,如前1个月、前3个月等)
65
+     */
66
+    @ApiModelProperty(value = "对比周期数")
67
+    private Integer comparePeriods = 1;
68
+}

+ 156 - 0
airport-common/src/main/java/com/sundot/airport/common/statistics/TimeDimensionProcessor.java

@@ -0,0 +1,156 @@
1
+package com.sundot.airport.common.statistics;
2
+
3
+import org.springframework.stereotype.Component;
4
+
5
+import java.time.LocalDate;
6
+import java.time.LocalDateTime;
7
+import java.time.ZoneId;
8
+import java.time.temporal.ChronoUnit;
9
+import java.util.Date;
10
+
11
+/**
12
+ * 时间维度参数处理器
13
+ * 处理年、季度、月等时间维度参数的计算和转换
14
+ */
15
+@Component
16
+public class TimeDimensionProcessor {
17
+
18
+    /**
19
+     * 根据年、季度、月参数计算开始时间和结束时间
20
+     *
21
+     * @param params 时间维度参数
22
+     * @return 包含开始时间和结束时间的参数对象
23
+     */
24
+    public TimeDimensionParams calculateTimeRange(TimeDimensionParams params) {
25
+        LocalDate startDate = null;
26
+        LocalDate endDate = null;
27
+
28
+        if (params.getYear() != null) {
29
+            if (params.getMonth() != null) {
30
+                // 按年月计算
31
+                startDate = LocalDate.of(params.getYear(), params.getMonth(), 1);
32
+                endDate = startDate.withDayOfMonth(startDate.lengthOfMonth());
33
+            } else if (params.getQuarter() != null) {
34
+                // 按年季度计算
35
+                int startMonth = (params.getQuarter() - 1) * 3 + 1;
36
+                startDate = LocalDate.of(params.getYear(), startMonth, 1);
37
+                endDate = startDate.plusMonths(2).withDayOfMonth(
38
+                        LocalDate.of(params.getYear(), startMonth + 2, 1).lengthOfMonth());
39
+            } else {
40
+                // 按年计算
41
+                startDate = LocalDate.of(params.getYear(), 1, 1);
42
+                endDate = LocalDate.of(params.getYear(), 12, 31);
43
+            }
44
+        } else {
45
+            // 如果没有指定年份,使用传入的时间范围
46
+            if (params.getStartTime() != null && params.getEndTime() != null) {
47
+                return params; // 直接返回原参数
48
+            } else {
49
+                // 默认使用当前年份
50
+                LocalDate now = LocalDate.now();
51
+                startDate = LocalDate.of(now.getYear(), 1, 1);
52
+                endDate = LocalDate.of(now.getYear(), 12, 31);
53
+            }
54
+        }
55
+
56
+        params.setStartTime(Date.from(startDate.atStartOfDay(ZoneId.systemDefault()).toInstant()));
57
+        params.setEndTime(Date.from(endDate.atTime(23, 59, 59).atZone(ZoneId.systemDefault()).toInstant()));
58
+
59
+        return params;
60
+    }
61
+
62
+    /**
63
+     * 计算环比时间范围
64
+     *
65
+     * @param params  原始时间参数
66
+     * @param periods 间隔周期数
67
+     * @return 环比时间参数
68
+     */
69
+    public TimeDimensionParams calculateChainRatioTimeRange(TimeDimensionParams params, int periods) {
70
+        TimeDimensionParams chainRatioParams = new TimeDimensionParams();
71
+        chainRatioParams.setChainRatio(true);
72
+        chainRatioParams.setComparePeriods(periods);
73
+
74
+        if (params.getYear() != null) {
75
+            if (params.getMonth() != null) {
76
+                // 按月环比
77
+                LocalDate currentMonth = LocalDate.of(params.getYear(), params.getMonth(), 1);
78
+                LocalDate previousMonth = currentMonth.minusMonths(periods);
79
+
80
+                chainRatioParams.setYear(previousMonth.getYear());
81
+                chainRatioParams.setMonth(previousMonth.getMonthValue());
82
+            } else if (params.getQuarter() != null) {
83
+                // 按季度环比
84
+                int currentQuarter = params.getQuarter();
85
+                int currentYear = params.getYear();
86
+
87
+                // 计算上一个季度
88
+                int totalQuarters = currentYear * 4 + currentQuarter - 1;
89
+                totalQuarters -= periods;
90
+
91
+                int newYear = totalQuarters / 4;
92
+                int newQuarter = (totalQuarters % 4) + 1;
93
+
94
+                chainRatioParams.setYear(newYear);
95
+                chainRatioParams.setQuarter(newQuarter);
96
+            } else {
97
+                // 按年环比
98
+                chainRatioParams.setYear(params.getYear() - periods);
99
+            }
100
+        } else if (params.getStartTime() != null && params.getEndTime() != null) {
101
+            // 基于具体时间范围计算环比
102
+            long diffInDays = ChronoUnit.DAYS.between(
103
+                    params.getStartTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDate(),
104
+                    params.getEndTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDate()) + 1;
105
+
106
+            LocalDateTime startDateTime = params.getStartTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
107
+            LocalDateTime endDateTime = params.getEndTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
108
+
109
+            LocalDateTime chainStart = startDateTime.minusDays(diffInDays * periods);
110
+            LocalDateTime chainEnd = endDateTime.minusDays(diffInDays * periods);
111
+
112
+            chainRatioParams.setStartTime(Date.from(chainStart.atZone(ZoneId.systemDefault()).toInstant()));
113
+            chainRatioParams.setEndTime(Date.from(chainEnd.atZone(ZoneId.systemDefault()).toInstant()));
114
+        }
115
+
116
+        return chainRatioParams;
117
+    }
118
+
119
+    /**
120
+     * 计算同比时间范围
121
+     *
122
+     * @param params 原始时间参数
123
+     * @return 同比时间参数
124
+     */
125
+    public TimeDimensionParams calculateYearOnYearTimeRange(TimeDimensionParams params) {
126
+        TimeDimensionParams yearOnYearParams = new TimeDimensionParams();
127
+        yearOnYearParams.setYearOnYear(true);
128
+
129
+        if (params.getYear() != null) {
130
+            if (params.getMonth() != null) {
131
+                // 按月同比
132
+                yearOnYearParams.setYear(params.getYear() - 1);
133
+                yearOnYearParams.setMonth(params.getMonth());
134
+            } else if (params.getQuarter() != null) {
135
+                // 按季度同比
136
+                yearOnYearParams.setYear(params.getYear() - 1);
137
+                yearOnYearParams.setQuarter(params.getQuarter());
138
+            } else {
139
+                // 按年同比
140
+                yearOnYearParams.setYear(params.getYear() - 1);
141
+            }
142
+        } else if (params.getStartTime() != null && params.getEndTime() != null) {
143
+            // 基于具体时间范围计算同比
144
+            LocalDateTime startDateTime = params.getStartTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
145
+            LocalDateTime endDateTime = params.getEndTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
146
+
147
+            LocalDateTime yearOnYearStart = startDateTime.minusYears(1);
148
+            LocalDateTime yearOnYearEnd = endDateTime.minusYears(1);
149
+
150
+            yearOnYearParams.setStartTime(Date.from(yearOnYearStart.atZone(ZoneId.systemDefault()).toInstant()));
151
+            yearOnYearParams.setEndTime(Date.from(yearOnYearEnd.atZone(ZoneId.systemDefault()).toInstant()));
152
+        }
153
+
154
+        return yearOnYearParams;
155
+    }
156
+}

+ 44 - 0
airport-common/src/main/java/com/sundot/airport/common/utils/DeptUtils.java

@@ -0,0 +1,44 @@
1
+package com.sundot.airport.common.utils;
2
+
3
+import com.sundot.airport.common.core.domain.entity.SysDept;
4
+
5
+/**
6
+ * 部门工具类
7
+ *
8
+ * @author sundot
9
+ */
10
+public class DeptUtils {
11
+
12
+
13
+    /**
14
+     * 获取顶级站点ID - 通用版本
15
+     *
16
+     * @return 站点ID
17
+     */
18
+    public static Long getTopSiteId(SysDept sysDept) {
19
+        if (sysDept == null) {
20
+            return null;
21
+        }
22
+
23
+        // 向上查找父级直到站点
24
+        String ancestors = sysDept.getAncestors();
25
+        if (ancestors != null && !ancestors.isEmpty()) {
26
+            String[] ancestorIds = ancestors.split(",");
27
+            if (ancestorIds.length > 0) {
28
+                try {
29
+                    // 如果包含0排除
30
+                    if ("0".equals(ancestorIds[0]) && ancestorIds.length > 1) {
31
+                        return Long.parseLong(ancestorIds[1]);
32
+                    }
33
+                    if (ancestorIds.length == 1 && "0".equals(ancestorIds[0])) {
34
+                        return sysDept.getDeptId();
35
+                    }
36
+                } catch (NumberFormatException e) {
37
+                    return 0L;
38
+                }
39
+            }
40
+        }
41
+
42
+        return null;
43
+    }
44
+}

+ 75 - 0
airport-item/src/main/java/com/sundot/airport/item/domain/dto/ChannelRankingStatsDTO.java

@@ -0,0 +1,75 @@
1
+package com.sundot.airport.item.domain.dto;
2
+
3
+import io.swagger.annotations.ApiModel;
4
+import io.swagger.annotations.ApiModelProperty;
5
+import lombok.Data;
6
+
7
+import java.io.Serializable;
8
+import java.math.BigDecimal;
9
+import java.util.List;
10
+
11
+/**
12
+ * 通道排名统计DTO
13
+ * 展示排名前五的通道信息
14
+ */
15
+@Data
16
+@ApiModel("通道排名统计DTO")
17
+public class ChannelRankingStatsDTO implements Serializable {
18
+
19
+    private static final long serialVersionUID = 1L;
20
+
21
+    /**
22
+     * 统计时间维度(年/季度/月)
23
+     */
24
+    @ApiModelProperty("统计时间维度")
25
+    private String timeDimension;
26
+
27
+    /**
28
+     * 当前时间点标签
29
+     */
30
+    @ApiModelProperty("当前时间点标签")
31
+    private String currentTimeLabel;
32
+
33
+    /**
34
+     * 通道排名统计数据列表(前五名)
35
+     */
36
+    @ApiModelProperty("通道排名统计数据列表(前五名)")
37
+    private List<ChannelRankingItem> channelRankings;
38
+
39
+
40
+    /**
41
+     * 通道排名数据项
42
+     */
43
+    @Data
44
+    @ApiModel("通道排名数据项")
45
+    public static class ChannelRankingItem implements Serializable {
46
+        private static final long serialVersionUID = 1L;
47
+
48
+        /**
49
+         * 通道名称
50
+         */
51
+        @ApiModelProperty("通道名称")
52
+        private String channelName;
53
+
54
+        /**
55
+         * 查获数量
56
+         */
57
+        @ApiModelProperty("查获数量")
58
+        private BigDecimal seizureQuantity;
59
+
60
+        /**
61
+         * 通道所属区域
62
+         */
63
+        @ApiModelProperty("通道所属区域")
64
+        private String regionalName;
65
+
66
+        /**
67
+         * 排名
68
+         */
69
+        @ApiModelProperty("排名")
70
+        private Integer ranking;
71
+
72
+    }
73
+
74
+
75
+}

+ 81 - 0
airport-item/src/main/java/com/sundot/airport/item/domain/dto/ConcealmentPositionStatsDTO.java

@@ -0,0 +1,81 @@
1
+package com.sundot.airport.item.domain.dto;
2
+
3
+import io.swagger.annotations.ApiModel;
4
+import io.swagger.annotations.ApiModelProperty;
5
+import lombok.Data;
6
+
7
+import java.io.Serializable;
8
+import java.math.BigDecimal;
9
+import java.util.List;
10
+
11
+/**
12
+ * 隐匿夹带部位分布统计DTO
13
+ * 统计隐匿夹带违禁品在各部位的查获数量分布
14
+ */
15
+@Data
16
+@ApiModel("隐匿夹带部位分布统计DTO")
17
+public class ConcealmentPositionStatsDTO implements Serializable {
18
+
19
+    private static final long serialVersionUID = 1L;
20
+
21
+    /**
22
+     * 统计时间维度(年/季度/月)
23
+     */
24
+    @ApiModelProperty("统计时间维度")
25
+    private String timeDimension;
26
+
27
+    /**
28
+     * 当前时间点标签
29
+     */
30
+    @ApiModelProperty("当前时间点标签")
31
+    private String currentTimeLabel;
32
+
33
+    /**
34
+     * 部位分布统计数据列表
35
+     */
36
+    @ApiModelProperty("部位分布统计数据列表")
37
+    private List<PositionStat> positionStats;
38
+
39
+    /**
40
+     * 总查获数量(隐匿夹带)
41
+     */
42
+    @ApiModelProperty("总查获数量(隐匿夹带)")
43
+    private BigDecimal totalQuantity;
44
+
45
+
46
+    /**
47
+     * 部位统计数据
48
+     */
49
+    @Data
50
+    @ApiModel("部位统计数据")
51
+    public static class PositionStat implements Serializable {
52
+        private static final long serialVersionUID = 1L;
53
+
54
+        /**
55
+         * 部位编码
56
+         */
57
+        @ApiModelProperty("部位编码")
58
+        private String positionCode;
59
+
60
+        /**
61
+         * 部位名称
62
+         */
63
+        @ApiModelProperty("部位名称")
64
+        private String positionName;
65
+
66
+        /**
67
+         * 查获数量
68
+         */
69
+        @ApiModelProperty("查获数量")
70
+        private BigDecimal quantity;
71
+
72
+        /**
73
+         * 占比(百分比)
74
+         */
75
+        @ApiModelProperty("占比(百分比)")
76
+        private BigDecimal percentage;
77
+
78
+    }
79
+
80
+
81
+}

+ 75 - 0
airport-item/src/main/java/com/sundot/airport/item/domain/dto/ItemCategoryStatsDTO.java

@@ -0,0 +1,75 @@
1
+package com.sundot.airport.item.domain.dto;
2
+
3
+import io.swagger.annotations.ApiModel;
4
+import io.swagger.annotations.ApiModelProperty;
5
+import lombok.Data;
6
+
7
+import java.io.Serializable;
8
+import java.math.BigDecimal;
9
+import java.util.List;
10
+
11
+/**
12
+ * 查获物品分类统计DTO
13
+ * 按查获物品一级分类统计全站整体查获数据
14
+ */
15
+@Data
16
+@ApiModel("查获物品分类统计DTO")
17
+public class ItemCategoryStatsDTO implements Serializable {
18
+
19
+    private static final long serialVersionUID = 1L;
20
+
21
+    /**
22
+     * 统计时间维度(年/季度/月)
23
+     */
24
+    @ApiModelProperty("统计时间维度")
25
+    private String timeDimension;
26
+
27
+    /**
28
+     * 当前时间点标签
29
+     */
30
+    @ApiModelProperty("当前时间点标签")
31
+    private String currentTimeLabel;
32
+
33
+    /**
34
+     * 分类统计数据列表
35
+     */
36
+    @ApiModelProperty("分类统计数据列表")
37
+    private List<CategoryStat> categoryStats;
38
+
39
+    /**
40
+     * 总查获数量
41
+     */
42
+    @ApiModelProperty("总查获数量")
43
+    private BigDecimal totalQuantity;
44
+
45
+
46
+    /**
47
+     * 分类统计数据
48
+     */
49
+    @Data
50
+    @ApiModel("分类统计数据")
51
+    public static class CategoryStat implements Serializable {
52
+        private static final long serialVersionUID = 1L;
53
+
54
+        /**
55
+         * 一级分类编码
56
+         */
57
+        @ApiModelProperty("一级分类编码")
58
+        private String categoryCodeOne;
59
+
60
+        /**
61
+         * 一级分类名称
62
+         */
63
+        @ApiModelProperty("一级分类名称")
64
+        private String categoryNameOne;
65
+
66
+        /**
67
+         * 查获数量
68
+         */
69
+        @ApiModelProperty("查获数量")
70
+        private BigDecimal quantity;
71
+
72
+    }
73
+
74
+
75
+}

+ 81 - 0
airport-item/src/main/java/com/sundot/airport/item/domain/dto/PostCategoryStatsDTO.java

@@ -0,0 +1,81 @@
1
+package com.sundot.airport.item.domain.dto;
2
+
3
+import io.swagger.annotations.ApiModel;
4
+import io.swagger.annotations.ApiModelProperty;
5
+import lombok.Data;
6
+
7
+import java.io.Serializable;
8
+import java.math.BigDecimal;
9
+import java.util.List;
10
+
11
+/**
12
+ * 岗位分类统计DTO
13
+ * 按岗位第二级分类统计违禁品查获数据
14
+ */
15
+@Data
16
+@ApiModel("岗位分类统计DTO")
17
+public class PostCategoryStatsDTO implements Serializable {
18
+
19
+    private static final long serialVersionUID = 1L;
20
+
21
+    /**
22
+     * 统计时间维度(年/季度/月)
23
+     */
24
+    @ApiModelProperty("统计时间维度")
25
+    private String timeDimension;
26
+
27
+    /**
28
+     * 当前时间点标签
29
+     */
30
+    @ApiModelProperty("当前时间点标签")
31
+    private String currentTimeLabel;
32
+
33
+    /**
34
+     * 岗位分类统计数据列表
35
+     */
36
+    @ApiModelProperty("岗位分类统计数据列表")
37
+    private List<PostStat> postStats;
38
+
39
+    /**
40
+     * 总查获数量
41
+     */
42
+    @ApiModelProperty("总查获数量")
43
+    private BigDecimal totalQuantity;
44
+
45
+
46
+    /**
47
+     * 岗位统计数据
48
+     */
49
+    @Data
50
+    @ApiModel("岗位统计数据")
51
+    public static class PostStat implements Serializable {
52
+        private static final long serialVersionUID = 1L;
53
+
54
+        /**
55
+         * 岗位编码
56
+         */
57
+        @ApiModelProperty("岗位编码")
58
+        private String postCode;
59
+
60
+        /**
61
+         * 岗位名称(第二级分类)
62
+         */
63
+        @ApiModelProperty("岗位名称(第二级分类)")
64
+        private String postName;
65
+
66
+        /**
67
+         * 查获数量
68
+         */
69
+        @ApiModelProperty("查获数量")
70
+        private BigDecimal quantity;
71
+
72
+        /**
73
+         * 占比(百分比)
74
+         */
75
+        @ApiModelProperty("占比(百分比)")
76
+        private BigDecimal percentage;
77
+
78
+    }
79
+
80
+
81
+}

+ 10 - 2
airport-item/src/main/java/com/sundot/airport/item/domain/home/SeizureReportDTO.java

@@ -24,8 +24,12 @@ public class SeizureReportDTO {
24 24
     public static class BrigadeRankingItem {
25 25
         @ApiModelProperty("大队名称")
26 26
         private String brigadeName;
27
-        @ApiModelProperty("大队ID")
27
+        @ApiModelProperty("查获数量")
28 28
         private BigDecimal seizureCount;
29
+        @ApiModelProperty("当前排名")
30
+        private Integer currentRank;
31
+        @ApiModelProperty("当前占比")
32
+        private BigDecimal currentRatio;
29 33
     }
30 34
 
31 35
     // 用于表示科室排名项的内部类
@@ -33,8 +37,12 @@ public class SeizureReportDTO {
33 37
     public static class DepartmentRankingItem {
34 38
         @ApiModelProperty("科室名称")
35 39
         private String departmentName;
36
-        @ApiModelProperty("科室ID")
40
+        @ApiModelProperty("查获数量")
37 41
         private BigDecimal seizureCount;
42
+        @ApiModelProperty("当前排名")
43
+        private Integer currentRank;
44
+        @ApiModelProperty("当前占比")
45
+        private BigDecimal currentRatio;
38 46
     }
39 47
 
40 48
     // 用于表示班组排名项的内部类

+ 109 - 0
airport-item/src/main/java/com/sundot/airport/item/mapper/ItemCategoryStatsMapper.java

@@ -0,0 +1,109 @@
1
+package com.sundot.airport.item.mapper;
2
+
3
+import com.sundot.airport.item.domain.dto.ChannelRankingStatsDTO;
4
+import com.sundot.airport.item.domain.dto.ItemCategoryStatsDTO;
5
+import org.apache.ibatis.annotations.Mapper;
6
+import org.apache.ibatis.annotations.Param;
7
+
8
+import java.util.Date;
9
+import java.util.List;
10
+
11
+/**
12
+ * 查获物品分类统计Mapper接口
13
+ */
14
+@Mapper
15
+public interface ItemCategoryStatsMapper {
16
+
17
+    /**
18
+     * 按一级分类统计全站查获数据
19
+     *
20
+     * @param stationId 站点ID
21
+     * @param startTime 开始时间
22
+     * @param endTime   结束时间
23
+     * @return 分类统计数据列表
24
+     */
25
+    List<ItemCategoryStatsDTO.CategoryStat> selectStationCategoryStats(
26
+            @Param("stationId") Long stationId,
27
+            @Param("startTime") Date startTime,
28
+            @Param("endTime") Date endTime);
29
+
30
+    /**
31
+     * 统计全站查获总量
32
+     *
33
+     * @param stationId 站点ID
34
+     * @param startTime 开始时间
35
+     * @param endTime   结束时间
36
+     * @return 总查获数量
37
+     */
38
+    Long selectStationTotalQuantity(
39
+            @Param("stationId") Long stationId,
40
+            @Param("startTime") Date startTime,
41
+            @Param("endTime") Date endTime);
42
+
43
+    /**
44
+     * 按部位统计隐匿夹带查获数据
45
+     *
46
+     * @param stationId 站点ID
47
+     * @param startTime 开始时间
48
+     * @param endTime   结束时间
49
+     * @return 部位统计数据列表
50
+     */
51
+    List<ItemCategoryStatsDTO.CategoryStat> selectStationConcealmentPositionStats(
52
+            @Param("stationId") Long stationId,
53
+            @Param("startTime") Date startTime,
54
+            @Param("endTime") Date endTime);
55
+
56
+    /**
57
+     * 统计全站隐匿夹带查获总量
58
+     *
59
+     * @param stationId 站点ID
60
+     * @param startTime 开始时间
61
+     * @param endTime   结束时间
62
+     * @return 隐匿夹带总查获数量
63
+     */
64
+    Long selectStationConcealmentTotalQuantity(
65
+            @Param("stationId") Long stationId,
66
+            @Param("startTime") Date startTime,
67
+            @Param("endTime") Date endTime);
68
+
69
+    /**
70
+     * 按岗位第二级分类统计查获数据
71
+     *
72
+     * @param stationId 站点ID
73
+     * @param startTime 开始时间
74
+     * @param endTime   结束时间
75
+     * @return 岗位分类统计数据列表
76
+     */
77
+    List<ItemCategoryStatsDTO.CategoryStat> selectStationPostCategoryStats(
78
+            @Param("stationId") Long stationId,
79
+            @Param("startTime") Date startTime,
80
+            @Param("endTime") Date endTime);
81
+
82
+    /**
83
+     * 统计全站岗位分类查获总量
84
+     *
85
+     * @param stationId 站点ID
86
+     * @param startTime 开始时间
87
+     * @param endTime   结束时间
88
+     * @return 岗位分类总查获数量
89
+     */
90
+    Long selectStationPostCategoryTotalQuantity(
91
+            @Param("stationId") Long stationId,
92
+            @Param("startTime") Date startTime,
93
+            @Param("endTime") Date endTime);
94
+
95
+    /**
96
+     * 查询全站通道排名前五数据
97
+     *
98
+     * @param stationId 站点ID
99
+     * @param startTime 开始时间
100
+     * @param endTime   结束时间
101
+     * @return 通道排名数据列表(前五名)
102
+     */
103
+    List<ChannelRankingStatsDTO.ChannelRankingItem> selectTopFiveChannelRankings(
104
+            @Param("stationId") Long stationId,
105
+            @Param("startTime") Date startTime,
106
+            @Param("endTime") Date endTime);
107
+
108
+
109
+}

+ 62 - 0
airport-item/src/main/java/com/sundot/airport/item/service/IItemCategoryStatsService.java

@@ -0,0 +1,62 @@
1
+package com.sundot.airport.item.service;
2
+
3
+import com.sundot.airport.common.statistics.TimeDimensionParams;
4
+import com.sundot.airport.item.domain.dto.ChannelRankingStatsDTO;
5
+import com.sundot.airport.item.domain.dto.ConcealmentPositionStatsDTO;
6
+import com.sundot.airport.item.domain.dto.ItemCategoryStatsDTO;
7
+import com.sundot.airport.item.domain.dto.PostCategoryStatsDTO;
8
+import com.sundot.airport.item.domain.home.SeizureReportDTO;
9
+
10
+import java.util.List;
11
+
12
+
13
+/**
14
+ * 查获物品分类统计服务接口
15
+ */
16
+public interface IItemCategoryStatsService {
17
+
18
+    /**
19
+     * 按时间维度获取全站查获物品分类统计数据
20
+     *
21
+     * @param params    时间维度参数
22
+     * @param stationId 站点ID
23
+     * @return 分类统计数据
24
+     */
25
+    ItemCategoryStatsDTO getStationCategoryStats(TimeDimensionParams params, Long stationId);
26
+
27
+    /**
28
+     * 按时间维度获取全站隐匿夹带部位分布统计数据
29
+     *
30
+     * @param params    时间维度参数
31
+     * @param stationId 站点ID
32
+     * @return 隐匿夹带部位分布统计数据
33
+     */
34
+    ConcealmentPositionStatsDTO getStationConcealmentPositionStats(TimeDimensionParams params, Long stationId);
35
+
36
+    /**
37
+     * 按时间维度获取全站岗位分类统计数据
38
+     *
39
+     * @param params    时间维度参数
40
+     * @param stationId 站点ID
41
+     * @return 岗位分类统计数据
42
+     */
43
+    PostCategoryStatsDTO getStationPostCategoryStats(TimeDimensionParams params, Long stationId);
44
+
45
+    /**
46
+     * 按时间维度获取全站通道排名统计数据
47
+     *
48
+     * @param params    时间维度参数
49
+     * @param stationId 站点ID
50
+     * @return 通道排名统计数据
51
+     */
52
+    ChannelRankingStatsDTO getStationChannelRankingStats(TimeDimensionParams params, Long stationId);
53
+
54
+    /**
55
+     * 按时间维度获取全站大队排名统计数据
56
+     *
57
+     * @param topSiteId 站点ID
58
+     * @param params    时间维度参数
59
+     * @return 大队排名统计数据
60
+     */
61
+    List<SeizureReportDTO.BrigadeRankingItem> getBrigadeRanking(Long topSiteId, TimeDimensionParams params);
62
+}

+ 245 - 0
airport-item/src/main/java/com/sundot/airport/item/service/impl/ItemCategoryStatsServiceImpl.java

@@ -0,0 +1,245 @@
1
+package com.sundot.airport.item.service.impl;
2
+
3
+import com.sundot.airport.common.statistics.TimeDimensionParams;
4
+import com.sundot.airport.common.statistics.TimeDimensionProcessor;
5
+import com.sundot.airport.item.domain.dto.ChannelRankingStatsDTO;
6
+import com.sundot.airport.item.domain.dto.ConcealmentPositionStatsDTO;
7
+import com.sundot.airport.item.domain.dto.ItemCategoryStatsDTO;
8
+import com.sundot.airport.item.domain.dto.PostCategoryStatsDTO;
9
+import com.sundot.airport.item.domain.home.SeizureReportDTO;
10
+import com.sundot.airport.item.mapper.ItemCategoryStatsMapper;
11
+import com.sundot.airport.item.mapper.SeizureReportMapper;
12
+import com.sundot.airport.item.service.IItemCategoryStatsService;
13
+import org.springframework.beans.factory.annotation.Autowired;
14
+import org.springframework.stereotype.Service;
15
+
16
+import java.math.BigDecimal;
17
+import java.util.List;
18
+
19
+/**
20
+ * 查获物品分类统计服务实现类
21
+ */
22
+@Service
23
+public class ItemCategoryStatsServiceImpl implements IItemCategoryStatsService {
24
+
25
+    @Autowired
26
+    private ItemCategoryStatsMapper itemCategoryStatsMapper;
27
+
28
+    @Autowired
29
+    private TimeDimensionProcessor timeDimensionProcessor;
30
+
31
+    @Autowired
32
+    private SeizureReportMapper seizureReportMapper;
33
+
34
+    @Override
35
+    public ItemCategoryStatsDTO getStationCategoryStats(TimeDimensionParams params, Long stationId) {
36
+        // 计算时间范围
37
+        TimeDimensionParams timeParams = timeDimensionProcessor.calculateTimeRange(params);
38
+
39
+        ItemCategoryStatsDTO result = new ItemCategoryStatsDTO();
40
+        result.setTimeDimension(this.getTimeDimensionLabel(params));
41
+        result.setCurrentTimeLabel(this.getCurrentTimeLabel(params));
42
+
43
+        // 查询分类统计数据
44
+        List<ItemCategoryStatsDTO.CategoryStat> categoryStats =
45
+                itemCategoryStatsMapper.selectStationCategoryStats(
46
+                        stationId,
47
+                        timeParams.getStartTime(),
48
+                        timeParams.getEndTime());
49
+
50
+        // 查询总数量
51
+        Long totalQuantity = itemCategoryStatsMapper.selectStationTotalQuantity(
52
+                stationId,
53
+                timeParams.getStartTime(),
54
+                timeParams.getEndTime());
55
+
56
+
57
+        result.setCategoryStats(categoryStats);
58
+        result.setTotalQuantity(new BigDecimal(totalQuantity));
59
+
60
+        return result;
61
+    }
62
+
63
+    @Override
64
+    public ConcealmentPositionStatsDTO getStationConcealmentPositionStats(TimeDimensionParams params, Long stationId) {
65
+        // 计算时间范围
66
+        TimeDimensionParams timeParams = timeDimensionProcessor.calculateTimeRange(params);
67
+
68
+        ConcealmentPositionStatsDTO result = new ConcealmentPositionStatsDTO();
69
+        result.setTimeDimension(this.getTimeDimensionLabel(params));
70
+        result.setCurrentTimeLabel(this.getCurrentTimeLabel(params));
71
+
72
+        // 查询部位统计数据
73
+        List<ItemCategoryStatsDTO.CategoryStat> positionStats =
74
+                itemCategoryStatsMapper.selectStationConcealmentPositionStats(
75
+                        stationId,
76
+                        timeParams.getStartTime(),
77
+                        timeParams.getEndTime());
78
+
79
+        // 查询隐匿夹带总数量
80
+        Long totalQuantity = itemCategoryStatsMapper.selectStationConcealmentTotalQuantity(
81
+                stationId,
82
+                timeParams.getStartTime(),
83
+                timeParams.getEndTime());
84
+
85
+        // 转换为部位统计格式并计算占比
86
+        List<ConcealmentPositionStatsDTO.PositionStat> convertedStats = positionStats.stream()
87
+                .map(stat -> {
88
+                    ConcealmentPositionStatsDTO.PositionStat positionStat = new ConcealmentPositionStatsDTO.PositionStat();
89
+                    positionStat.setPositionCode(stat.getCategoryCodeOne());
90
+                    positionStat.setPositionName(stat.getCategoryNameOne());
91
+                    positionStat.setQuantity(BigDecimal.valueOf(stat.getQuantity().doubleValue()));
92
+
93
+                    // 计算占比
94
+                    if (totalQuantity > 0) {
95
+                        BigDecimal percentage = BigDecimal.valueOf(stat.getQuantity().doubleValue())
96
+                                .multiply(BigDecimal.valueOf(100))
97
+                                .divide(BigDecimal.valueOf(totalQuantity.doubleValue()), 2, BigDecimal.ROUND_HALF_UP);
98
+                        positionStat.setPercentage(percentage);
99
+                    } else {
100
+                        positionStat.setPercentage(BigDecimal.ZERO);
101
+                    }
102
+
103
+                    return positionStat;
104
+                })
105
+                .collect(java.util.stream.Collectors.toList());
106
+
107
+        result.setPositionStats(convertedStats);
108
+        result.setTotalQuantity(BigDecimal.valueOf(totalQuantity.doubleValue()));
109
+
110
+        return result;
111
+    }
112
+
113
+    /**
114
+     * 获取时间维度标签
115
+     */
116
+    private String getTimeDimensionLabel(TimeDimensionParams params) {
117
+        if (params.getYear() != null) {
118
+            if (params.getMonth() != null) {
119
+                return "月度";
120
+            } else if (params.getQuarter() != null) {
121
+                return "季度";
122
+            } else {
123
+                return "年度";
124
+            }
125
+        }
126
+        return "自定义";
127
+    }
128
+
129
+    @Override
130
+    public PostCategoryStatsDTO getStationPostCategoryStats(TimeDimensionParams params, Long stationId) {
131
+        // 计算时间范围
132
+        TimeDimensionParams timeParams = timeDimensionProcessor.calculateTimeRange(params);
133
+
134
+        PostCategoryStatsDTO result = new PostCategoryStatsDTO();
135
+        result.setTimeDimension(this.getTimeDimensionLabel(params));
136
+        result.setCurrentTimeLabel(this.getCurrentTimeLabel(params));
137
+
138
+        // 查询岗位分类统计数据
139
+        List<ItemCategoryStatsDTO.CategoryStat> postStats =
140
+                itemCategoryStatsMapper.selectStationPostCategoryStats(
141
+                        stationId,
142
+                        timeParams.getStartTime(),
143
+                        timeParams.getEndTime());
144
+
145
+        // 查询岗位分类总数量
146
+        Long totalQuantity = itemCategoryStatsMapper.selectStationPostCategoryTotalQuantity(
147
+                stationId,
148
+                timeParams.getStartTime(),
149
+                timeParams.getEndTime());
150
+
151
+        // 转换为岗位统计格式并计算占比
152
+        List<PostCategoryStatsDTO.PostStat> convertedStats = postStats.stream()
153
+                .map(stat -> {
154
+                    PostCategoryStatsDTO.PostStat postStat = new PostCategoryStatsDTO.PostStat();
155
+                    postStat.setPostCode(stat.getCategoryCodeOne());
156
+                    postStat.setPostName(stat.getCategoryNameOne());
157
+                    postStat.setQuantity(BigDecimal.valueOf(stat.getQuantity().doubleValue()));
158
+
159
+                    // 计算占比
160
+                    if (totalQuantity > 0) {
161
+                        BigDecimal percentage = BigDecimal.valueOf(stat.getQuantity().doubleValue())
162
+                                .multiply(BigDecimal.valueOf(100))
163
+                                .divide(BigDecimal.valueOf(totalQuantity.doubleValue()), 2, BigDecimal.ROUND_HALF_UP);
164
+                        postStat.setPercentage(percentage);
165
+                    } else {
166
+                        postStat.setPercentage(BigDecimal.ZERO);
167
+                    }
168
+
169
+                    return postStat;
170
+                })
171
+                .collect(java.util.stream.Collectors.toList());
172
+
173
+        result.setPostStats(convertedStats);
174
+        result.setTotalQuantity(BigDecimal.valueOf(totalQuantity.doubleValue()));
175
+
176
+        return result;
177
+    }
178
+
179
+    @Override
180
+    public ChannelRankingStatsDTO getStationChannelRankingStats(TimeDimensionParams params, Long stationId) {
181
+        // 计算时间范围
182
+        TimeDimensionParams timeParams = timeDimensionProcessor.calculateTimeRange(params);
183
+
184
+        ChannelRankingStatsDTO result = new ChannelRankingStatsDTO();
185
+        result.setTimeDimension(this.getTimeDimensionLabel(params));
186
+        result.setCurrentTimeLabel(this.getCurrentTimeLabel(params));
187
+
188
+        // 查询通道排名前五数据
189
+        List<ChannelRankingStatsDTO.ChannelRankingItem> channelRankings =
190
+                itemCategoryStatsMapper.selectTopFiveChannelRankings(
191
+                        stationId,
192
+                        timeParams.getStartTime(),
193
+                        timeParams.getEndTime());
194
+
195
+        // 转换数据类型并设置查获数量
196
+        List<ChannelRankingStatsDTO.ChannelRankingItem> convertedStats = channelRankings.stream()
197
+                .map(item -> {
198
+                    ChannelRankingStatsDTO.ChannelRankingItem rankingItem = new ChannelRankingStatsDTO.ChannelRankingItem();
199
+                    rankingItem.setChannelName(item.getChannelName());
200
+                    rankingItem.setSeizureQuantity(BigDecimal.valueOf(item.getSeizureQuantity().doubleValue()));
201
+                    rankingItem.setRegionalName(item.getRegionalName());
202
+                    rankingItem.setRanking(item.getRanking());
203
+                    return rankingItem;
204
+                })
205
+                .collect(java.util.stream.Collectors.toList());
206
+
207
+        result.setChannelRankings(convertedStats);
208
+
209
+        return result;
210
+    }
211
+
212
+    @Override
213
+    public List<SeizureReportDTO.BrigadeRankingItem> getBrigadeRanking(Long topSiteId, TimeDimensionParams params) {
214
+        // 计算时间范围
215
+        TimeDimensionParams timeParams = timeDimensionProcessor.calculateTimeRange(params);
216
+        List<SeizureReportDTO.BrigadeRankingItem> brigadeRankings = seizureReportMapper.selectBrigadeRankings(topSiteId, timeParams.getStartTime(), timeParams.getEndTime());
217
+        //计算总查获数量
218
+        BigDecimal totalSeizureQuantity = brigadeRankings.stream().map(item -> item.getSeizureCount()).reduce(BigDecimal.ZERO, BigDecimal::add);
219
+        //计算排名和占比
220
+        brigadeRankings.forEach(item -> {
221
+            item.setCurrentRank(brigadeRankings.indexOf(item) + 1);
222
+            item.setCurrentRatio(totalSeizureQuantity.compareTo(BigDecimal.ZERO) == 0 ? BigDecimal.ZERO : item.getSeizureCount().divide(totalSeizureQuantity, 2, BigDecimal.ROUND_HALF_UP).multiply(new BigDecimal(100)));
223
+        });
224
+        return brigadeRankings;
225
+    }
226
+
227
+    /**
228
+     * 获取当前时间标签
229
+     */
230
+    private String getCurrentTimeLabel(TimeDimensionParams params) {
231
+        if (params.getYear() != null) {
232
+            if (params.getMonth() != null) {
233
+                return params.getYear() + "年" + params.getMonth() + "月";
234
+            } else if (params.getQuarter() != null) {
235
+                return params.getYear() + "年第" + params.getQuarter() + "季度";
236
+            } else {
237
+                return params.getYear() + "年";
238
+            }
239
+        }
240
+        if (params.getStartTime() != null && params.getEndTime() != null) {
241
+            return params.getStartTime() + "至" + params.getEndTime();
242
+        }
243
+        return "当前期间";
244
+    }
245
+}

+ 118 - 0
airport-item/src/main/resources/mapper/item/ItemCategoryStatsMapper.xml

@@ -0,0 +1,118 @@
1
+<?xml version="1.0" encoding="UTF-8" ?>
2
+<!DOCTYPE mapper
3
+        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
4
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
5
+<mapper namespace="com.sundot.airport.item.mapper.ItemCategoryStatsMapper">
6
+
7
+    <!-- 按一级分类统计全站查获数据 -->
8
+    <select id="selectStationCategoryStats"
9
+            resultType="com.sundot.airport.item.domain.dto.ItemCategoryStatsDTO$CategoryStat">
10
+        SELECT isi.category_code_one AS categoryCodeOne,
11
+               isi.category_name_one AS categoryNameOne,
12
+               SUM(isi.quantity)     AS quantity
13
+        FROM item_seizure_record isr
14
+                 LEFT JOIN item_seizure_items isi ON isr.id = isi.record_id
15
+        WHERE isr.inspect_station_id = #{stationId}
16
+          AND isr.seizure_time >= #{startTime}
17
+          AND isr.seizure_time &lt;= #{endTime}
18
+          AND isr.process_status = '3' -- 已归档状态
19
+          AND isi.category_code_one IS NOT NULL
20
+          AND isi.category_name_one IS NOT NULL
21
+        GROUP BY isi.category_code_one, isi.category_name_one
22
+        ORDER BY quantity DESC
23
+    </select>
24
+
25
+    <!-- 统计全站查获总量 -->
26
+    <select id="selectStationTotalQuantity" resultType="java.lang.Long">
27
+        SELECT IFNULL(SUM(isi.quantity), 0)
28
+        FROM item_seizure_record isr
29
+                 LEFT JOIN item_seizure_items isi ON isr.id = isi.record_id
30
+        WHERE isr.inspect_station_id = #{stationId}
31
+          AND isr.seizure_time >= #{startTime}
32
+          AND isr.seizure_time &lt;= #{endTime}
33
+          AND isr.process_status = '3' -- 已归档状态
34
+    </select>
35
+
36
+    <!-- 按部位统计隐匿夹带查获数据 -->
37
+    <select id="selectStationConcealmentPositionStats"
38
+            resultType="com.sundot.airport.item.domain.dto.ItemCategoryStatsDTO$CategoryStat">
39
+        SELECT isi.check_position_code_two AS categoryCodeOne,
40
+               isi.check_position_name_two AS categoryNameOne,
41
+               SUM(isi.quantity)           AS quantity
42
+        FROM item_seizure_record isr
43
+                 LEFT JOIN item_seizure_items isi ON isr.id = isi.record_id
44
+        WHERE isr.inspect_station_id = #{stationId}
45
+          AND isr.seizure_time >= #{startTime}
46
+          AND isr.seizure_time &lt;= #{endTime}
47
+          AND isr.process_status = '3'      -- 已归档状态
48
+          AND isi.is_active_concealment = 1 -- 是否隐匿夹带为"是"
49
+          AND isi.check_position_code_two IS NOT NULL
50
+          AND isi.check_position_name_two IS NOT NULL
51
+        GROUP BY isi.check_position_code_two, isi.check_position_name_two
52
+        ORDER BY quantity DESC
53
+    </select>
54
+
55
+    <!-- 统计全站隐匿夹带查获总量 -->
56
+    <select id="selectStationConcealmentTotalQuantity" resultType="java.lang.Long">
57
+        SELECT IFNULL(SUM(isi.quantity), 0)
58
+        FROM item_seizure_record isr
59
+                 LEFT JOIN item_seizure_items isi ON isr.id = isi.record_id
60
+        WHERE isr.inspect_station_id = #{stationId}
61
+          AND isr.seizure_time >= #{startTime}
62
+          AND isr.seizure_time &lt;= #{endTime}
63
+          AND isr.process_status = '3' -- 已归档状态
64
+          AND isi.is_active_concealment = 1 -- 是否隐匿夹带为"是"
65
+    </select>
66
+
67
+    <!-- 按岗位第二级分类统计查获数据 -->
68
+    <select id="selectStationPostCategoryStats"
69
+            resultType="com.sundot.airport.item.domain.dto.ItemCategoryStatsDTO$CategoryStat">
70
+        SELECT isr.check_method      AS categoryCodeOne,
71
+               isr.check_method_desc AS categoryNameOne,
72
+               SUM(isi.quantity)     AS quantity
73
+        FROM item_seizure_record isr
74
+                 LEFT JOIN item_seizure_items isi ON isr.id = isi.record_id
75
+        WHERE isr.inspect_station_id = #{stationId}
76
+          AND isr.seizure_time >= #{startTime}
77
+          AND isr.seizure_time &lt;= #{endTime}
78
+          AND isr.process_status = '3' -- 已归档状态
79
+          AND isr.check_method IS NOT NULL
80
+          AND isr.check_method_desc IS NOT NULL
81
+        GROUP BY isr.check_method, isr.check_method_desc
82
+        ORDER BY quantity DESC
83
+    </select>
84
+
85
+    <!-- 统计全站岗位分类查获总量 -->
86
+    <select id="selectStationPostCategoryTotalQuantity" resultType="java.lang.Long">
87
+        SELECT IFNULL(SUM(isi.quantity), 0)
88
+        FROM item_seizure_record isr
89
+                 LEFT JOIN item_seizure_items isi ON isr.id = isi.record_id
90
+        WHERE isr.inspect_station_id = #{stationId}
91
+          AND isr.seizure_time >= #{startTime}
92
+          AND isr.seizure_time &lt;= #{endTime}
93
+          AND isr.process_status = '3' -- 已归档状态
94
+    </select>
95
+
96
+    <!-- 查询全站通道排名前五数据 -->
97
+    <select id="selectTopFiveChannelRankings"
98
+            resultType="com.sundot.airport.item.domain.dto.ChannelRankingStatsDTO$ChannelRankingItem">
99
+        SELECT t.channelName,
100
+               t.seizureQuantity,
101
+               t.regionalName,
102
+               (@row_number := @row_number + 1) AS ranking
103
+        FROM (SELECT isr.channel_name  AS channelName,
104
+                     SUM(isi.quantity) AS seizureQuantity,
105
+                     isr.regional_name AS regionalName
106
+              FROM item_seizure_record isr
107
+                       LEFT JOIN item_seizure_items isi ON isr.id = isi.record_id
108
+              WHERE isr.inspect_station_id = #{stationId}
109
+                AND isr.seizure_time >= #{startTime}
110
+                AND isr.seizure_time &lt;= #{endTime}
111
+                AND isr.process_status = '3' -- 已归档状态
112
+                AND isr.channel_name IS NOT NULL
113
+                AND isr.regional_name IS NOT NULL
114
+              GROUP BY isr.channel_name, isr.regional_name
115
+              ORDER BY seizureQuantity DESC LIMIT 5) t
116
+                 CROSS JOIN (SELECT @row_number := 0) r
117
+    </select>
118
+</mapper>