Selaa lähdekoodia

21.本地自测”查询当天开航每小时区域过检人数“接口,

sunpanhu 3 viikkoa sitten
vanhempi
commit
1045d1f71f

+ 2 - 2
airport-ledger/src/main/java/com/sundot/airport/ledger/domain/OperationStationHourlyThroughput.java

@@ -53,12 +53,12 @@ public class OperationStationHourlyThroughput extends BaseEntity {
53 53
     @Excel(name = "通道号")
54 54
     private String laneId;
55 55
 
56
-    /** 记录日期(YYYYMMDD) */
56
+    /** 记录日期(yyyy-MM-dd) */
57 57
     @JsonFormat(pattern = "yyyy-MM-dd")
58 58
     @Excel(name = "记录日期", width = 20, dateFormat = "yyyy-MM-dd")
59 59
     private Date recordDate;
60 60
 
61
-    /** 小时(0:00~1:00、1:00~2:00、23:00~24:00等) */
61
+    /** 小时(0:00、1:00、2:00等) */
62 62
     @Excel(name = "小时")
63 63
     private String hourOfDay;
64 64
 

+ 4 - 0
airport-ledger/src/main/java/com/sundot/airport/ledger/domain/vo/AreaFlowVO.java

@@ -1,6 +1,8 @@
1 1
 package com.sundot.airport.ledger.domain.vo;
2 2
 
3
+import lombok.AllArgsConstructor;
3 4
 import lombok.Data;
5
+import lombok.NoArgsConstructor;
4 6
 
5 7
 import java.io.Serializable;
6 8
 
@@ -11,6 +13,8 @@ import java.io.Serializable;
11 13
  * @date 2026/5/19 16:55
12 14
  */
13 15
 @Data
16
+@NoArgsConstructor
17
+@AllArgsConstructor
14 18
 public class AreaFlowVO implements Serializable {
15 19
     /**
16 20
      * 区域ID

+ 2 - 1
airport-ledger/src/main/java/com/sundot/airport/ledger/domain/vo/LaneThroughputResVO.java

@@ -45,10 +45,11 @@ public class LaneThroughputResVO implements Serializable {
45 45
      */
46 46
     private BigDecimal throughputRate;
47 47
 
48
-    public LaneThroughputResVO(Date date) {
48
+    public LaneThroughputResVO(Date date, String groupName) {
49 49
         this.recordDate = date;
50 50
         this.totalThroughput = 0;
51 51
         this.totalRecord = 0;
52 52
         this.throughputRate = java.math.BigDecimal.ZERO;
53
+        this.groupName = groupName;
53 54
     }
54 55
 }

+ 6 - 1
airport-ledger/src/main/java/com/sundot/airport/ledger/domain/vo/StationHourlyThroughputGroupResVO.java

@@ -14,11 +14,16 @@ import java.util.List;
14 14
 @Data
15 15
 public class StationHourlyThroughputGroupResVO implements Serializable {
16 16
     /**
17
-     * 小时(0,1,2...23
17
+     * 小时(2026-05-20 16:00
18 18
      */
19 19
     private String hourOfDay;
20 20
 
21 21
     /**
22
+     * 小时(16:00)
23
+     */
24
+    private String hour;
25
+
26
+    /**
22 27
      * 当前小时 所有区域总人流量
23 28
      */
24 29
     private Integer totalHourFlow;

+ 9 - 15
airport-ledger/src/main/java/com/sundot/airport/ledger/service/impl/OperationLanePeakThroughputServiceImpl.java

@@ -128,7 +128,13 @@ public class OperationLanePeakThroughputServiceImpl extends ServiceImpl<Operatio
128 128
                 ));
129 129
         
130 130
         log.info("数据Map中的日期键: {}", dataMap.keySet());
131
-        
131
+
132
+        // 获取小组名称
133
+        String groupName = "";
134
+        if (CollUtil.isNotEmpty(laneThroughputResList)) {
135
+            groupName = laneThroughputResList.get(0).getGroupName();
136
+        }
137
+
132 138
         // 补齐缺失的日期数据
133 139
         List<LaneThroughputResVO> resultList = Lists.newArrayList();
134 140
         for (Date date : last30Days) {
@@ -138,8 +144,8 @@ public class OperationLanePeakThroughputServiceImpl extends ServiceImpl<Operatio
138 144
                 resultList.add(dataMap.get(dateKey));
139 145
             } else {
140 146
                 // 缺失的日期,补充0数据
141
-                LaneThroughputResVO emptyVO = this.createEmptyVO(date, laneThroughputResList);
142
-                resultList.add(emptyVO);
147
+                LaneThroughputResVO emptyLaneThroughput = new LaneThroughputResVO(date, groupName);
148
+                resultList.add(emptyLaneThroughput);
143 149
             }
144 150
         }
145 151
         
@@ -150,16 +156,4 @@ public class OperationLanePeakThroughputServiceImpl extends ServiceImpl<Operatio
150 156
         
151 157
         return resultList;
152 158
     }
153
-    
154
-    /**
155
-     * 创建空的VO对象
156
-     */
157
-    private LaneThroughputResVO createEmptyVO(Date date, List<LaneThroughputResVO> existingList) {
158
-        LaneThroughputResVO emptyVO = new LaneThroughputResVO(date);
159
-        // 从已有数据中获取groupName(如果有的话)
160
-        if (CollUtil.isNotEmpty(existingList)) {
161
-            emptyVO.setGroupName(existingList.get(0).getGroupName());
162
-        }
163
-        return emptyVO;
164
-    }
165 159
 }

+ 153 - 34
airport-ledger/src/main/java/com/sundot/airport/ledger/service/impl/OperationStationHourlyThroughputServiceImpl.java

@@ -1,25 +1,23 @@
1 1
 package com.sundot.airport.ledger.service.impl;
2 2
 
3
-import java.util.Collections;
4
-import java.util.HashMap;
5
-import java.util.List;
6
-import java.util.Map;
7
-import java.util.Objects;
8
-import java.util.Set;
3
+import java.util.*;
9 4
 import java.util.stream.Collectors;
10 5
 
11 6
 import cn.hutool.core.collection.CollUtil;
12 7
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
8
+import com.google.common.collect.Lists;
13 9
 import com.google.common.collect.Maps;
14 10
 import com.sundot.airport.common.utils.DateUtils;
15 11
 import com.sundot.airport.ledger.domain.OperationStationHourlyThroughput;
16 12
 import com.sundot.airport.ledger.domain.vo.AreaFlowVO;
13
+import com.sundot.airport.ledger.domain.vo.LaneThroughputResVO;
17 14
 import com.sundot.airport.ledger.domain.vo.StationHourlyThroughputGroupResVO;
18 15
 import com.sundot.airport.ledger.domain.vo.StationHourlyThroughputVO;
19 16
 import com.sundot.airport.ledger.mapper.OperationStationHourlyThroughputMapper;
20 17
 import com.sundot.airport.ledger.service.IOperationStationHourlyThroughputService;
21 18
 import com.sundot.airport.system.domain.BasePosition;
22 19
 import com.sundot.airport.system.mapper.BasePositionMapper;
20
+import lombok.extern.slf4j.Slf4j;
23 21
 import org.springframework.beans.factory.annotation.Autowired;
24 22
 import org.springframework.stereotype.Service;
25 23
 import org.springframework.transaction.annotation.Transactional;
@@ -27,6 +25,7 @@ import org.springframework.transaction.annotation.Transactional;
27 25
 /**
28 26
  * 站级时间段别总过检Service业务层处理
29 27
  */
28
+@Slf4j
30 29
 @Service
31 30
 public class OperationStationHourlyThroughputServiceImpl extends ServiceImpl<OperationStationHourlyThroughputMapper, OperationStationHourlyThroughput> implements IOperationStationHourlyThroughputService {
32 31
 
@@ -107,8 +106,15 @@ public class OperationStationHourlyThroughputServiceImpl extends ServiceImpl<Ope
107 106
     @Override
108 107
     public List<StationHourlyThroughputGroupResVO> countStationHourlyThroughput() {
109 108
         List<StationHourlyThroughputVO> stationHourlyThroughputList = this.baseMapper.countStationHourlyThroughput();
110
-        if (CollUtil.isEmpty(stationHourlyThroughputList)) {
111
-            return Collections.emptyList();
109
+        stationHourlyThroughputList = CollUtil.emptyIfNull(stationHourlyThroughputList);
110
+
111
+        log.info("SQL查询返回数据条数: {}", stationHourlyThroughputList.size());
112
+        if (!stationHourlyThroughputList.isEmpty()) {
113
+            log.info("SQL查询返回的时间点列表: {}", 
114
+                    stationHourlyThroughputList.stream()
115
+                            .map(StationHourlyThroughputVO::getHourOfDay)
116
+                            .distinct()
117
+                            .collect(Collectors.toList()));
112 118
         }
113 119
 
114 120
         Map<Long, BasePosition> areaInfoMap = Maps.newHashMap();
@@ -124,33 +130,146 @@ public class OperationStationHourlyThroughputServiceImpl extends ServiceImpl<Ope
124 130
             areaInfoMap = CollUtil.emptyIfNull(basePositions).stream().collect(Collectors.toMap(BasePosition::getId, v -> v));
125 131
         }
126 132
 
127
-        // 2. 核心:按小时分组 → 组装成前端需要的层级结构
128
-        Map<String, List<StationHourlyThroughputVO>> hourGroupMap = stationHourlyThroughputList.stream().collect(Collectors.groupingBy(StationHourlyThroughputVO::getHourOfDay));
133
+        // 2. 按时间点分组
134
+        Map<String, List<StationHourlyThroughputVO>> hourGroupMap = stationHourlyThroughputList.stream()
135
+                .collect(Collectors.groupingBy(StationHourlyThroughputVO::getHourOfDay));
136
+
137
+        // 3. 生成近24个具体时间点(从当前时间向前推24小时)
138
+        List<String> last24Hours = this.generateLast24Hours();
139
+        
140
+        log.info("需要补齐的24个时间点: {}", last24Hours);
129 141
 
130 142
         Map<Long, BasePosition> finalAreaInfoMap = areaInfoMap;
131
-        return hourGroupMap.entrySet().stream().map(entry -> {
132
-            String hour = entry.getKey();
133
-            List<StationHourlyThroughputVO> hourDataList = entry.getValue();
134
-
135
-            // 构建区域列表
136
-            List<AreaFlowVO> areaList = hourDataList.stream().map(stats -> {
137
-                BasePosition basePosition = finalAreaInfoMap.get(stats.getAreaId());
138
-
139
-                AreaFlowVO areaFlowVO = new AreaFlowVO();
140
-                areaFlowVO.setAreaId(stats.getAreaId());
141
-                areaFlowVO.setAreaName(Objects.nonNull(basePosition) ? basePosition.getName() : "");
142
-                areaFlowVO.setAreaFlow(stats.getAreaFlow());
143
-                return areaFlowVO;
144
-            }).collect(Collectors.toList());
145
-
146
-            // 构建小时分组对象
147
-            StationHourlyThroughputGroupResVO groupVO = new StationHourlyThroughputGroupResVO();
148
-            groupVO.setHourOfDay(hour);
149
-            // 总人流量(取第一条即可,同小时数据总流量一致)
150
-            groupVO.setTotalHourFlow(hourDataList.get(0).getTotalHourFlow());
151
-            // 区域信息
152
-            groupVO.setAreaList(areaList);
153
-            return groupVO;
154
-        }).collect(Collectors.toList());
143
+        
144
+        // 4. 补齐24个时间点数据
145
+        List<StationHourlyThroughputGroupResVO> resultList = Lists.newArrayList();
146
+        for (String hourTime : last24Hours) {
147
+            // 处理日期 转换hourTime格式的日期,例如:2026-05-21 00:00转成0:00,2026-05-21 01:00转成01:00,2026-05-21 11:00转成11:00
148
+            // 提取小时和分钟部分(去掉日期)
149
+            String hourOnly = this.extractHourAndMinute(hourTime);
150
+
151
+            if (hourGroupMap.containsKey(hourTime)) {
152
+                // 存在数据的时间点,正常组装
153
+                List<StationHourlyThroughputVO> hourDataList = hourGroupMap.get(hourTime);
154
+                
155
+                List<AreaFlowVO> areaList = hourDataList.stream().map(stats -> {
156
+                    BasePosition basePosition = finalAreaInfoMap.get(stats.getAreaId());
157
+                    AreaFlowVO areaFlowVO = new AreaFlowVO();
158
+                    areaFlowVO.setAreaId(stats.getAreaId());
159
+                    areaFlowVO.setAreaName(Objects.nonNull(basePosition) ? basePosition.getName() : "");
160
+                    areaFlowVO.setAreaFlow(stats.getAreaFlow());
161
+                    return areaFlowVO;
162
+                }).collect(Collectors.toList());
163
+
164
+                // 补充缺失的区域信息
165
+                if (CollUtil.isEmpty(areaList)){
166
+                    areaInfoMap.forEach((id, basePosition) -> {
167
+                        areaList.add(new AreaFlowVO(id, basePosition.getName(), 0));
168
+                    });
169
+                } else {
170
+                    if (areaList.size() != areaInfoMap.size()) {
171
+                        areaInfoMap.forEach((id, basePosition) -> {
172
+                            AreaFlowVO newAreaFlow = areaList.stream().filter(areaFlowVO -> areaFlowVO.getAreaId().equals(id)).findFirst().orElse(null);
173
+                            if (Objects.isNull(newAreaFlow)) {
174
+                                areaList.add(new AreaFlowVO(id, basePosition.getName(), 0));
175
+                            }
176
+                        });
177
+                    }
178
+                }
179
+
180
+                // 按区域id升序排序
181
+                if (CollUtil.isNotEmpty(areaList)){
182
+                    areaList.sort(Comparator.comparing(AreaFlowVO::getAreaId));
183
+                }
184
+
185
+                StationHourlyThroughputGroupResVO groupVO = new StationHourlyThroughputGroupResVO();
186
+                groupVO.setHourOfDay(hourTime);
187
+                groupVO.setHour(hourOnly);
188
+                groupVO.setTotalHourFlow(hourDataList.get(0).getTotalHourFlow());
189
+                groupVO.setAreaList(areaList);
190
+                resultList.add(groupVO);
191
+            } else {
192
+                List<AreaFlowVO> areaList = Lists.newArrayList();
193
+                areaInfoMap.forEach((id, basePosition) -> {
194
+                    areaList.add(new AreaFlowVO(id, basePosition.getName(), 0));
195
+                });
196
+
197
+                // 按区域id升序排序
198
+                if (CollUtil.isNotEmpty(areaList)){
199
+                    areaList.sort(Comparator.comparing(AreaFlowVO::getAreaId));
200
+                }
201
+
202
+                // 缺失的时间点,补充空数据
203
+                StationHourlyThroughputGroupResVO emptyVO = new StationHourlyThroughputGroupResVO();
204
+                emptyVO.setHourOfDay(hourTime);
205
+                emptyVO.setHour(hourOnly);
206
+                emptyVO.setTotalHourFlow(0);
207
+                emptyVO.setAreaList(areaList);
208
+                resultList.add(emptyVO);
209
+            }
210
+        }
211
+        
212
+        log.info("补齐后返回的总时间点数: {}", resultList.size());
213
+        return resultList;
214
+    }
215
+    
216
+    /**
217
+     * 生成近24个具体时间点(从当前时间向前推24小时)
218
+     * 格式:yyyy-MM-dd HH:00
219
+     */
220
+    private List<String> generateLast24Hours() {
221
+        List<String> hours = new ArrayList<>();
222
+        Calendar calendar = Calendar.getInstance();
223
+        calendar.set(Calendar.MINUTE, 0);
224
+        calendar.set(Calendar.SECOND, 0);
225
+        calendar.set(Calendar.MILLISECOND, 0);
226
+        
227
+        // 从当前小时向前推23小时,共24个时间点
228
+        for (int i = 23; i >= 0; i--) {
229
+            Calendar cal = (Calendar) calendar.clone();
230
+            cal.add(Calendar.HOUR_OF_DAY, -i);
231
+            
232
+            String hourTime = String.format("%04d-%02d-%02d %02d:00",
233
+                    cal.get(Calendar.YEAR),
234
+                    cal.get(Calendar.MONTH) + 1,
235
+                    cal.get(Calendar.DAY_OF_MONTH),
236
+                    cal.get(Calendar.HOUR_OF_DAY));
237
+            hours.add(hourTime);
238
+        }
239
+        
240
+        return hours;
241
+    }
242
+    
243
+    /**
244
+     * 从完整日期时间中提取小时和分钟部分
245
+     * 例如:2026-05-21 00:00 → 0:00
246
+     *      2026-05-21 01:00 → 01:00
247
+     *      2026-05-21 11:00 → 11:00
248
+     * 
249
+     * @param hourTime 完整日期时间字符串(格式:yyyy-MM-dd HH:00)
250
+     * @return 小时和分钟字符串(格式:H:00 或 HH:00)
251
+     */
252
+    private String extractHourAndMinute(String hourTime) {
253
+        if (hourTime == null || hourTime.trim().isEmpty()) {
254
+            return "";
255
+        }
256
+        
257
+        // 按空格分割,取时间部分
258
+        String[] parts = hourTime.split(" ");
259
+        if (parts.length < 2) {
260
+            return hourTime;
261
+        }
262
+        
263
+        String timePart = parts[1]; // 获取 "00:00" 部分
264
+        
265
+        // 去掉小时的前导0(如果有的话)
266
+        String[] timeComponents = timePart.split(":");
267
+        if (timeComponents.length >= 2) {
268
+            int hour = Integer.parseInt(timeComponents[0]);
269
+            String minute = timeComponents[1];
270
+            return hour + ":" + minute;
271
+        }
272
+        
273
+        return timePart;
155 274
     }
156 275
 }

+ 21 - 8
airport-ledger/src/main/resources/mapper/ledger/OperationStationHourlyThroughputMapper.xml

@@ -53,25 +53,38 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
53 53
 
54 54
     <select id="countStationHourlyThroughput" resultType="com.sundot.airport.ledger.domain.vo.StationHourlyThroughputVO">
55 55
         SELECT
56
-            t.hour_of_day AS hourOfDay,
56
+            DATE_FORMAT(STR_TO_DATE(CONCAT(t.record_date, ' ', t.hour_of_day), '%Y-%m-%d %H:%i'), '%Y-%m-%d %H:00') AS hourOfDay,
57 57
             t.area_id AS areaId,
58 58
             IFNULL(SUM(t.throughput), 0) AS areaFlow,
59
-            t.total_hour_flow AS totalHourFlow
59
+            IFNULL(total_flow.total_hour_flow, 0) AS totalHourFlow
60 60
         FROM (
61 61
             SELECT
62
+                record_date,
62 63
                 hour_of_day,
63 64
                 area_id,
64
-                throughput,
65
-                SUM(throughput) OVER (PARTITION BY hour_of_day) AS total_hour_flow
65
+                throughput
66 66
             FROM operation_station_hourly_throughput
67 67
             WHERE
68 68
                 del_flag = '0'
69 69
                 AND hour_of_day IS NOT NULL
70 70
                 AND hour_of_day != ''
71
-            -- 近24小时自动匹配
72
-            AND STR_TO_DATE(CONCAT(record_date, hour_of_day), '%Y%m%d%H') >= DATE_SUB(NOW(), INTERVAL 24 HOUR)
71
+                -- 近24小时自动匹配
72
+                AND STR_TO_DATE(CONCAT(record_date, ' ', hour_of_day), '%Y-%m-%d %H:%i') >= DATE_SUB(NOW(), INTERVAL 24 HOUR)
73 73
         ) t
74
-        GROUP BY t.hour_of_day, t.area_id
75
-        ORDER BY t.hour_of_day ASC
74
+        LEFT JOIN (
75
+            SELECT
76
+                DATE_FORMAT(STR_TO_DATE(CONCAT(record_date, ' ', hour_of_day), '%Y-%m-%d %H:%i'), '%Y-%m-%d %H:00') AS hour_time,
77
+                SUM(throughput) AS total_hour_flow
78
+            FROM operation_station_hourly_throughput
79
+            WHERE
80
+                del_flag = '0'
81
+                AND hour_of_day IS NOT NULL
82
+                AND hour_of_day != ''
83
+                -- 近24小时自动匹配
84
+                AND STR_TO_DATE(CONCAT(record_date, ' ', hour_of_day), '%Y-%m-%d %H:%i') >= DATE_SUB(NOW(), INTERVAL 24 HOUR)
85
+            GROUP BY hour_time
86
+        ) total_flow ON DATE_FORMAT(STR_TO_DATE(CONCAT(t.record_date, ' ', t.hour_of_day), '%Y-%m-%d %H:%i'), '%Y-%m-%d %H:00') = total_flow.hour_time
87
+        GROUP BY hourOfDay, t.area_id, total_flow.total_hour_flow
88
+        ORDER BY hourOfDay ASC
76 89
     </select>
77 90
 </mapper>

+ 1 - 1
airport-system/src/main/java/com/sundot/airport/system/mapper/BasePositionMapper.java

@@ -79,7 +79,7 @@ public interface BasePositionMapper {
79 79
      * @author PanHu Sun
80 80
      * @date 2026/5/20 9:36
81 81
      */
82
-    List<BasePosition> selectPositionListByIds(Set<Long> ids);
82
+    List<BasePosition> selectPositionListByIds(@Param("ids") Set<Long> ids);
83 83
 
84 84
     /**
85 85
      * 修改子元素关系

+ 2 - 2
sql/station_hourly_throughput.sql

@@ -11,8 +11,8 @@ CREATE TABLE `operation_station_hourly_throughput` (
11 11
   `group_id` bigint(20) DEFAULT NULL COMMENT '通道/小组ID',
12 12
   `group_name` varchar(100) DEFAULT NULL COMMENT '通道/小组',
13 13
   `lane_id` varchar(50) DEFAULT NULL COMMENT '通道号',
14
-  `record_date` varchar(8) DEFAULT NULL COMMENT '记录日期(YYYYMMDD)',
15
-  `hour_of_day` varchar(20) DEFAULT NULL COMMENT '小时(0:00~1:00、1:00~2:00、23:00~24:00等)',
14
+  `record_date` varchar(8) DEFAULT NULL COMMENT '记录日期(yyyy-MM-ddYYYYMMDD)',
15
+  `hour_of_day` varchar(20) DEFAULT NULL COMMENT '小时(0:00、1:00、2:00等)',
16 16
   `throughput` int(11) DEFAULT NULL COMMENT '过检人数',
17 17
   `throughput_rate` decimal(10,2) DEFAULT NULL COMMENT '过检率',
18 18
   `remark` varchar(500) DEFAULT NULL COMMENT '备注',