refactor(ops): 重构统计模块,支持日期参数化查询及代码质量优化
- 客流接口支持指定日期查询(getTrafficRealtime、getTrafficTrend、getAreaTrafficRealtime) - 移除昨日对比趋势字段(yesterdayHourlyTrend),简化为单日期模式 - 漏斗图改为工单状态分布(FunnelItem→StatusDistributionItem),使用 SQL COUNT 替代内存分组 - 新增工牌队列统计(BadgeQueueStats),按 orderType 过滤避免跨类型数据混入 - 在线工牌计数仅统计 IDLE/BUSY 状态(排除 PAUSED/OFFLINE) - 修复通配符导入和全限定类名引用,规范化 import 语句 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -36,8 +36,8 @@ public class DashboardStatsRespVO {
|
|||||||
@Schema(description = "时效趋势数据")
|
@Schema(description = "时效趋势数据")
|
||||||
private TimeTrendData timeTrendData;
|
private TimeTrendData timeTrendData;
|
||||||
|
|
||||||
@Schema(description = "漏斗数据")
|
@Schema(description = "工单状态分布数据")
|
||||||
private List<FunnelItem> funnelData;
|
private List<StatusDistributionItem> statusDistribution;
|
||||||
|
|
||||||
@Schema(description = "热力图数据(近7天)")
|
@Schema(description = "热力图数据(近7天)")
|
||||||
private HeatmapData heatmapData;
|
private HeatmapData heatmapData;
|
||||||
@@ -45,6 +45,9 @@ public class DashboardStatsRespVO {
|
|||||||
@Schema(description = "功能类型排行")
|
@Schema(description = "功能类型排行")
|
||||||
private List<FunctionTypeRankingItem> functionTypeRanking;
|
private List<FunctionTypeRankingItem> functionTypeRanking;
|
||||||
|
|
||||||
|
@Schema(description = "工牌队列统计(近7天每天的排队数量)")
|
||||||
|
private BadgeQueueStats badgeQueueStats;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@Builder
|
@Builder
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@@ -86,8 +89,8 @@ public class DashboardStatsRespVO {
|
|||||||
@Builder
|
@Builder
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public static class FunnelItem {
|
public static class StatusDistributionItem {
|
||||||
@Schema(description = "阶段名称")
|
@Schema(description = "状态名称")
|
||||||
private String name;
|
private String name;
|
||||||
@Schema(description = "数量")
|
@Schema(description = "数量")
|
||||||
private Integer value;
|
private Integer value;
|
||||||
@@ -121,4 +124,15 @@ public class DashboardStatsRespVO {
|
|||||||
private Double rate;
|
private Double rate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public static class BadgeQueueStats {
|
||||||
|
@Schema(description = "日期列表")
|
||||||
|
private List<String> dates;
|
||||||
|
@Schema(description = "每日排队工牌数量")
|
||||||
|
private List<Integer> queueData;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,10 +39,10 @@ public class DeviceTrafficRealtimeRespVO {
|
|||||||
@Schema(description = "当前在场人数")
|
@Schema(description = "当前在场人数")
|
||||||
private Long currentOccupancy;
|
private Long currentOccupancy;
|
||||||
|
|
||||||
@Schema(description = "今日小时趋势")
|
@Schema(description = "统计日期")
|
||||||
|
private String statDate;
|
||||||
|
|
||||||
|
@Schema(description = "小时趋势")
|
||||||
private TrafficRealtimeRespVO.HourlyTrend hourlyTrend;
|
private TrafficRealtimeRespVO.HourlyTrend hourlyTrend;
|
||||||
|
|
||||||
@Schema(description = "昨日小时趋势")
|
|
||||||
private TrafficRealtimeRespVO.HourlyTrend yesterdayHourlyTrend;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,11 +27,11 @@ public class TrafficRealtimeRespVO {
|
|||||||
@Schema(description = "区域客流明细")
|
@Schema(description = "区域客流明细")
|
||||||
private List<AreaTrafficItem> areas;
|
private List<AreaTrafficItem> areas;
|
||||||
|
|
||||||
@Schema(description = "今日小时趋势")
|
@Schema(description = "统计日期")
|
||||||
private HourlyTrend hourlyTrend;
|
private String statDate;
|
||||||
|
|
||||||
@Schema(description = "昨日小时趋势")
|
@Schema(description = "小时趋势")
|
||||||
private HourlyTrend yesterdayHourlyTrend;
|
private HourlyTrend hourlyTrend;
|
||||||
|
|
||||||
@Schema(description = "提示信息,如区域暂未配置客流设备")
|
@Schema(description = "提示信息,如区域暂未配置客流设备")
|
||||||
private String message;
|
private String message;
|
||||||
|
|||||||
@@ -13,14 +13,14 @@ import java.util.List;
|
|||||||
*
|
*
|
||||||
* @author lzh
|
* @author lzh
|
||||||
*/
|
*/
|
||||||
@Schema(description = "管理后台 - 近7天客流统计 Response VO")
|
@Schema(description = "管理后台 - 客流趋势统计 Response VO")
|
||||||
@Data
|
@Data
|
||||||
@Builder
|
@Builder
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public class TrafficTrendRespVO {
|
public class TrafficTrendRespVO {
|
||||||
|
|
||||||
@Schema(description = "日期列表(近7天)")
|
@Schema(description = "日期列表")
|
||||||
private List<String> dates;
|
private List<String> dates;
|
||||||
|
|
||||||
@Schema(description = "每日进入人数")
|
@Schema(description = "每日进入人数")
|
||||||
|
|||||||
@@ -30,9 +30,10 @@ public interface OpsStatisticsService {
|
|||||||
/**
|
/**
|
||||||
* 获取实时客流监测数据
|
* 获取实时客流监测数据
|
||||||
*
|
*
|
||||||
|
* @param date 指定日期(可选,默认今天)
|
||||||
* @return 实时客流数据
|
* @return 实时客流数据
|
||||||
*/
|
*/
|
||||||
TrafficRealtimeRespVO getTrafficRealtime();
|
TrafficRealtimeRespVO getTrafficRealtime(LocalDate date);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取工作台统计数据
|
* 获取工作台统计数据
|
||||||
@@ -42,11 +43,13 @@ public interface OpsStatisticsService {
|
|||||||
WorkspaceStatsRespVO getWorkspaceStats();
|
WorkspaceStatsRespVO getWorkspaceStats();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取近7天客流趋势统计
|
* 获取客流趋势统计
|
||||||
*
|
*
|
||||||
* @return 近7天客流趋势数据
|
* @param startDate 开始日期(可选,默认近7天)
|
||||||
|
* @param endDate 结束日期(可选,默认今天)
|
||||||
|
* @return 客流趋势数据
|
||||||
*/
|
*/
|
||||||
TrafficTrendRespVO getTrafficTrend();
|
TrafficTrendRespVO getTrafficTrend(LocalDate startDate, LocalDate endDate);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 按设备查实时客流
|
* 按设备查实时客流
|
||||||
@@ -88,9 +91,10 @@ public interface OpsStatisticsService {
|
|||||||
* 按区域ID列表查实时客流(汇总)
|
* 按区域ID列表查实时客流(汇总)
|
||||||
*
|
*
|
||||||
* @param areaIds 区域ID列表
|
* @param areaIds 区域ID列表
|
||||||
|
* @param date 指定日期(可选,默认今天)
|
||||||
* @return 汇总的实时客流数据
|
* @return 汇总的实时客流数据
|
||||||
*/
|
*/
|
||||||
TrafficRealtimeRespVO getAreaTrafficRealtime(List<Long> areaIds);
|
TrafficRealtimeRespVO getAreaTrafficRealtime(List<Long> areaIds, LocalDate date);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 按区域ID列表查趋势(汇总)
|
* 按区域ID列表查趋势(汇总)
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ import com.viewsh.module.ops.dal.dataobject.statistics.OpsTrafficStatisticsDO;
|
|||||||
import com.viewsh.module.ops.dal.dataobject.workorder.OpsOrderDO;
|
import com.viewsh.module.ops.dal.dataobject.workorder.OpsOrderDO;
|
||||||
import com.viewsh.module.ops.dal.mysql.area.OpsBusAreaMapper;
|
import com.viewsh.module.ops.dal.mysql.area.OpsBusAreaMapper;
|
||||||
import com.viewsh.module.ops.dal.mysql.statistics.OpsTrafficStatisticsMapper;
|
import com.viewsh.module.ops.dal.mysql.statistics.OpsTrafficStatisticsMapper;
|
||||||
|
import com.viewsh.module.ops.dal.dataobject.queue.OpsOrderQueueDO;
|
||||||
|
import com.viewsh.module.ops.dal.mysql.queue.OpsOrderQueueMapper;
|
||||||
import com.viewsh.module.ops.dal.mysql.workorder.OpsOrderMapper;
|
import com.viewsh.module.ops.dal.mysql.workorder.OpsOrderMapper;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@@ -28,8 +30,10 @@ import java.time.LocalDateTime;
|
|||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -70,6 +74,9 @@ public class OpsStatisticsServiceImpl implements OpsStatisticsService {
|
|||||||
@Resource
|
@Resource
|
||||||
private OpsTrafficStatisticsMapper trafficStatisticsMapper;
|
private OpsTrafficStatisticsMapper trafficStatisticsMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private OpsOrderQueueMapper opsOrderQueueMapper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 在线员工数提供者(由 ops-server 层注入,从 environment-biz 获取工牌状态)
|
* 在线员工数提供者(由 ops-server 层注入,从 environment-biz 获取工牌状态)
|
||||||
* 返回 [onlineCount, totalCount]
|
* 返回 [onlineCount, totalCount]
|
||||||
@@ -118,18 +125,23 @@ public class OpsStatisticsServiceImpl implements OpsStatisticsService {
|
|||||||
// 7. 时效趋势(单次 GROUP BY 查询)
|
// 7. 时效趋势(单次 GROUP BY 查询)
|
||||||
TimeTrendData timeTrendData = buildTimeTrendData(orderType, startDate, endDate);
|
TimeTrendData timeTrendData = buildTimeTrendData(orderType, startDate, endDate);
|
||||||
|
|
||||||
// 8. 漏斗数据(基于指定时间范围的工单状态推算)
|
// 近7天时间范围(多个图表复用)
|
||||||
List<FunnelItem> funnelData = buildFunnelData(orderType, startDateTime, endDateTime);
|
|
||||||
|
|
||||||
// 9. 热力图(近7天)
|
|
||||||
LocalDate sevenDaysAgo = LocalDate.now().minusDays(6); // 包含今天共7天
|
LocalDate sevenDaysAgo = LocalDate.now().minusDays(6); // 包含今天共7天
|
||||||
LocalDateTime sevenDaysAgoStart = sevenDaysAgo.atStartOfDay();
|
LocalDateTime sevenDaysAgoStart = sevenDaysAgo.atStartOfDay();
|
||||||
LocalDateTime nowEnd = LocalDate.now().plusDays(1).atStartOfDay();
|
LocalDateTime nowEnd = LocalDate.now().plusDays(1).atStartOfDay();
|
||||||
|
|
||||||
|
// 8. 工单状态分布(近7天所有工单的状态占比)
|
||||||
|
List<StatusDistributionItem> statusDistribution = buildStatusDistribution(orderType, sevenDaysAgoStart, nowEnd);
|
||||||
|
|
||||||
|
// 9. 热力图(近7天)
|
||||||
HeatmapData heatmapData = buildHeatmapData(orderType, sevenDaysAgoStart, nowEnd);
|
HeatmapData heatmapData = buildHeatmapData(orderType, sevenDaysAgoStart, nowEnd);
|
||||||
|
|
||||||
// 10. 功能类型排行(单次 GROUP BY 查询)
|
// 10. 功能类型排行(单次 GROUP BY 查询)
|
||||||
List<FunctionTypeRankingItem> functionTypeRanking = buildFunctionTypeRanking(orderType);
|
List<FunctionTypeRankingItem> functionTypeRanking = buildFunctionTypeRanking(orderType);
|
||||||
|
|
||||||
|
// 11. 工牌队列统计(近7天每天排队的工单数 = QUEUED 状态)
|
||||||
|
BadgeQueueStats badgeQueueStats = buildBadgeQueueStats(orderType, sevenDaysAgo, LocalDate.now());
|
||||||
|
|
||||||
return DashboardStatsRespVO.builder()
|
return DashboardStatsRespVO.builder()
|
||||||
.pendingCount(pendingCount)
|
.pendingCount(pendingCount)
|
||||||
.inProgressCount(inProgressCount)
|
.inProgressCount(inProgressCount)
|
||||||
@@ -138,38 +150,33 @@ public class OpsStatisticsServiceImpl implements OpsStatisticsService {
|
|||||||
.trendData(trendData)
|
.trendData(trendData)
|
||||||
.hourlyDistribution(hourlyDistribution)
|
.hourlyDistribution(hourlyDistribution)
|
||||||
.timeTrendData(timeTrendData)
|
.timeTrendData(timeTrendData)
|
||||||
.funnelData(funnelData)
|
.statusDistribution(statusDistribution)
|
||||||
.heatmapData(heatmapData)
|
.heatmapData(heatmapData)
|
||||||
.functionTypeRanking(functionTypeRanking)
|
.functionTypeRanking(functionTypeRanking)
|
||||||
|
.badgeQueueStats(badgeQueueStats)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Cacheable(value = "ops:statistics:traffic#5m", unless = "#result == null")
|
@Cacheable(value = "ops:statistics:traffic#5m", key = "#date != null ? #date.toString() : T(java.time.LocalDate).now().toString()", unless = "#result == null")
|
||||||
public TrafficRealtimeRespVO getTrafficRealtime() {
|
public TrafficRealtimeRespVO getTrafficRealtime(LocalDate date) {
|
||||||
LocalDate today = LocalDate.now();
|
if (date == null) {
|
||||||
LocalDateTime todayStart = today.atStartOfDay();
|
date = LocalDate.now();
|
||||||
LocalDateTime todayEnd = todayStart.plusDays(1);
|
}
|
||||||
|
LocalDateTime dayStart = date.atStartOfDay();
|
||||||
|
LocalDateTime dayEnd = dayStart.plusDays(1);
|
||||||
|
|
||||||
// 从 ops_traffic_statistics 表查询今日汇总(使用 ge + lt 避免边界重叠)
|
// 从 ops_traffic_statistics 表查询指定日期汇总(使用 ge + lt 避免边界重叠)
|
||||||
List<OpsTrafficStatisticsDO> todayStats = trafficStatisticsMapper.selectList(
|
List<OpsTrafficStatisticsDO> dayStats = trafficStatisticsMapper.selectList(
|
||||||
new LambdaQueryWrapperX<OpsTrafficStatisticsDO>()
|
new LambdaQueryWrapperX<OpsTrafficStatisticsDO>()
|
||||||
.ge(OpsTrafficStatisticsDO::getStatHour, todayStart)
|
.ge(OpsTrafficStatisticsDO::getStatHour, dayStart)
|
||||||
.lt(OpsTrafficStatisticsDO::getStatHour, todayEnd));
|
.lt(OpsTrafficStatisticsDO::getStatHour, dayEnd));
|
||||||
|
|
||||||
// 查询昨日数据(用于昨日小时趋势对比)
|
|
||||||
LocalDate yesterday = today.minusDays(1);
|
|
||||||
LocalDateTime yesterdayStart = yesterday.atStartOfDay();
|
|
||||||
List<OpsTrafficStatisticsDO> yesterdayStats = trafficStatisticsMapper.selectList(
|
|
||||||
new LambdaQueryWrapperX<OpsTrafficStatisticsDO>()
|
|
||||||
.ge(OpsTrafficStatisticsDO::getStatHour, yesterdayStart)
|
|
||||||
.lt(OpsTrafficStatisticsDO::getStatHour, todayStart));
|
|
||||||
|
|
||||||
// 加载区域名称映射
|
// 加载区域名称映射
|
||||||
Map<Long, String> areaNameMap = loadAreaNameMap();
|
Map<Long, String> areaNameMap = loadAreaNameMap();
|
||||||
|
|
||||||
// 按区域汇总
|
// 按区域汇总
|
||||||
Map<Long, List<OpsTrafficStatisticsDO>> byArea = todayStats.stream()
|
Map<Long, List<OpsTrafficStatisticsDO>> byArea = dayStats.stream()
|
||||||
.filter(s -> s.getAreaId() != null)
|
.filter(s -> s.getAreaId() != null)
|
||||||
.collect(Collectors.groupingBy(OpsTrafficStatisticsDO::getAreaId));
|
.collect(Collectors.groupingBy(OpsTrafficStatisticsDO::getAreaId));
|
||||||
|
|
||||||
@@ -196,17 +203,16 @@ public class OpsStatisticsServiceImpl implements OpsStatisticsService {
|
|||||||
.build());
|
.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
// 小时趋势(今日 + 昨日)
|
// 小时趋势
|
||||||
HourlyTrend hourlyTrend = buildTrafficHourlyTrend(todayStats);
|
HourlyTrend hourlyTrend = buildTrafficHourlyTrend(dayStats);
|
||||||
HourlyTrend yesterdayHourlyTrend = buildTrafficHourlyTrend(yesterdayStats);
|
|
||||||
|
|
||||||
return TrafficRealtimeRespVO.builder()
|
return TrafficRealtimeRespVO.builder()
|
||||||
.totalIn(totalIn)
|
.totalIn(totalIn)
|
||||||
.totalOut(totalOut)
|
.totalOut(totalOut)
|
||||||
.currentOccupancy(Math.max(0, totalIn - totalOut))
|
.currentOccupancy(Math.max(0, totalIn - totalOut))
|
||||||
.areas(areas)
|
.areas(areas)
|
||||||
|
.statDate(date.toString())
|
||||||
.hourlyTrend(hourlyTrend)
|
.hourlyTrend(hourlyTrend)
|
||||||
.yesterdayHourlyTrend(yesterdayHourlyTrend)
|
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,8 +237,11 @@ public class OpsStatisticsServiceImpl implements OpsStatisticsService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 待处理工单数
|
// 2. 待处理工单数(所有未取消、未完成的工单)
|
||||||
Integer pendingCount = countByStatusAndType("PENDING", orderType);
|
Integer pendingCount = Math.toIntExact(opsOrderMapper.selectCount(
|
||||||
|
new LambdaQueryWrapperX<OpsOrderDO>()
|
||||||
|
.eq(OpsOrderDO::getOrderType, orderType)
|
||||||
|
.notIn(OpsOrderDO::getStatus, "COMPLETED", "CANCELLED")));
|
||||||
|
|
||||||
// 3. 平均响应时长(今日已完成的工单,使用 ge + lt 避免边界重叠)
|
// 3. 平均响应时长(今日已完成的工单,使用 ge + lt 避免边界重叠)
|
||||||
List<OpsOrderDO> completedToday = opsOrderMapper.selectList(
|
List<OpsOrderDO> completedToday = opsOrderMapper.selectList(
|
||||||
@@ -314,14 +323,24 @@ public class OpsStatisticsServiceImpl implements OpsStatisticsService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Cacheable(value = "ops:statistics:traffic-trend#5m", unless = "#result == null")
|
@Cacheable(value = "ops:statistics:traffic-trend#5m",
|
||||||
public TrafficTrendRespVO getTrafficTrend() {
|
key = "(#startDate != null ? #startDate.toString() : 'default') + ':' + (#endDate != null ? #endDate.toString() : 'default')", unless = "#result == null")
|
||||||
|
public TrafficTrendRespVO getTrafficTrend(LocalDate startDate, LocalDate endDate) {
|
||||||
LocalDate today = LocalDate.now();
|
LocalDate today = LocalDate.now();
|
||||||
LocalDate sevenDaysAgo = today.minusDays(6); // 包含今天共7天
|
if (startDate == null) {
|
||||||
|
startDate = today.minusDays(6);
|
||||||
|
}
|
||||||
|
if (endDate == null) {
|
||||||
|
endDate = today;
|
||||||
|
}
|
||||||
|
// 限制最大范围为31天
|
||||||
|
if (java.time.temporal.ChronoUnit.DAYS.between(startDate, endDate) > 30) {
|
||||||
|
startDate = endDate.minusDays(30);
|
||||||
|
}
|
||||||
|
|
||||||
// 从 ops_traffic_statistics 表查询近7天的客流数据
|
// 从 ops_traffic_statistics 表查询客流数据
|
||||||
LocalDateTime startDateTime = sevenDaysAgo.atStartOfDay();
|
LocalDateTime startDateTime = startDate.atStartOfDay();
|
||||||
LocalDateTime endDateTime = today.plusDays(1).atStartOfDay();
|
LocalDateTime endDateTime = endDate.plusDays(1).atStartOfDay();
|
||||||
|
|
||||||
List<OpsTrafficStatisticsDO> stats = trafficStatisticsMapper.selectList(
|
List<OpsTrafficStatisticsDO> stats = trafficStatisticsMapper.selectList(
|
||||||
new LambdaQueryWrapperX<OpsTrafficStatisticsDO>()
|
new LambdaQueryWrapperX<OpsTrafficStatisticsDO>()
|
||||||
@@ -352,7 +371,7 @@ public class OpsStatisticsServiceImpl implements OpsStatisticsService {
|
|||||||
long totalIn = 0;
|
long totalIn = 0;
|
||||||
long totalOut = 0;
|
long totalOut = 0;
|
||||||
|
|
||||||
for (LocalDate d = sevenDaysAgo; !d.isAfter(today); d = d.plusDays(1)) {
|
for (LocalDate d = startDate; !d.isAfter(endDate); d = d.plusDays(1)) {
|
||||||
dates.add(d.format(DATE_FORMATTER));
|
dates.add(d.format(DATE_FORMATTER));
|
||||||
long dayIn = dailyInMap.getOrDefault(d, 0L);
|
long dayIn = dailyInMap.getOrDefault(d, 0L);
|
||||||
long dayOut = dailyOutMap.getOrDefault(d, 0L);
|
long dayOut = dailyOutMap.getOrDefault(d, 0L);
|
||||||
@@ -389,15 +408,6 @@ public class OpsStatisticsServiceImpl implements OpsStatisticsService {
|
|||||||
.ge(OpsTrafficStatisticsDO::getStatHour, todayStart)
|
.ge(OpsTrafficStatisticsDO::getStatHour, todayStart)
|
||||||
.lt(OpsTrafficStatisticsDO::getStatHour, todayEnd));
|
.lt(OpsTrafficStatisticsDO::getStatHour, todayEnd));
|
||||||
|
|
||||||
// 查询昨日数据
|
|
||||||
LocalDate yesterday = today.minusDays(1);
|
|
||||||
LocalDateTime yesterdayStart = yesterday.atStartOfDay();
|
|
||||||
List<OpsTrafficStatisticsDO> yesterdayStats = trafficStatisticsMapper.selectList(
|
|
||||||
new LambdaQueryWrapperX<OpsTrafficStatisticsDO>()
|
|
||||||
.eq(OpsTrafficStatisticsDO::getDeviceId, deviceId)
|
|
||||||
.ge(OpsTrafficStatisticsDO::getStatHour, yesterdayStart)
|
|
||||||
.lt(OpsTrafficStatisticsDO::getStatHour, todayStart));
|
|
||||||
|
|
||||||
long totalIn = stats.stream().mapToLong(s -> s.getPeopleIn() != null ? s.getPeopleIn() : 0).sum();
|
long totalIn = stats.stream().mapToLong(s -> s.getPeopleIn() != null ? s.getPeopleIn() : 0).sum();
|
||||||
long totalOut = stats.stream().mapToLong(s -> s.getPeopleOut() != null ? s.getPeopleOut() : 0).sum();
|
long totalOut = stats.stream().mapToLong(s -> s.getPeopleOut() != null ? s.getPeopleOut() : 0).sum();
|
||||||
|
|
||||||
@@ -406,7 +416,6 @@ public class OpsStatisticsServiceImpl implements OpsStatisticsService {
|
|||||||
Map<Long, String> areaNameMap = loadAreaNameMap();
|
Map<Long, String> areaNameMap = loadAreaNameMap();
|
||||||
|
|
||||||
HourlyTrend hourlyTrend = buildTrafficHourlyTrend(stats);
|
HourlyTrend hourlyTrend = buildTrafficHourlyTrend(stats);
|
||||||
HourlyTrend yesterdayHourlyTrend = buildTrafficHourlyTrend(yesterdayStats);
|
|
||||||
|
|
||||||
return DeviceTrafficRealtimeRespVO.builder()
|
return DeviceTrafficRealtimeRespVO.builder()
|
||||||
.deviceId(deviceId)
|
.deviceId(deviceId)
|
||||||
@@ -415,8 +424,8 @@ public class OpsStatisticsServiceImpl implements OpsStatisticsService {
|
|||||||
.todayIn(totalIn)
|
.todayIn(totalIn)
|
||||||
.todayOut(totalOut)
|
.todayOut(totalOut)
|
||||||
.currentOccupancy(Math.max(0, totalIn - totalOut))
|
.currentOccupancy(Math.max(0, totalIn - totalOut))
|
||||||
|
.statDate(today.toString())
|
||||||
.hourlyTrend(hourlyTrend)
|
.hourlyTrend(hourlyTrend)
|
||||||
.yesterdayHourlyTrend(yesterdayHourlyTrend)
|
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -433,28 +442,14 @@ public class OpsStatisticsServiceImpl implements OpsStatisticsService {
|
|||||||
.ge(OpsTrafficStatisticsDO::getStatHour, todayStart)
|
.ge(OpsTrafficStatisticsDO::getStatHour, todayStart)
|
||||||
.lt(OpsTrafficStatisticsDO::getStatHour, todayEnd));
|
.lt(OpsTrafficStatisticsDO::getStatHour, todayEnd));
|
||||||
|
|
||||||
// 查询昨日数据
|
|
||||||
LocalDate yesterday = today.minusDays(1);
|
|
||||||
LocalDateTime yesterdayStart = yesterday.atStartOfDay();
|
|
||||||
List<OpsTrafficStatisticsDO> yesterdayStats = trafficStatisticsMapper.selectList(
|
|
||||||
new LambdaQueryWrapperX<OpsTrafficStatisticsDO>()
|
|
||||||
.eq(OpsTrafficStatisticsDO::getAreaId, areaId)
|
|
||||||
.ge(OpsTrafficStatisticsDO::getStatHour, yesterdayStart)
|
|
||||||
.lt(OpsTrafficStatisticsDO::getStatHour, todayStart));
|
|
||||||
|
|
||||||
Map<Long, String> areaNameMap = loadAreaNameMap();
|
Map<Long, String> areaNameMap = loadAreaNameMap();
|
||||||
String areaName = areaNameMap.getOrDefault(areaId, "未知区域");
|
String areaName = areaNameMap.getOrDefault(areaId, "未知区域");
|
||||||
|
|
||||||
// 按设备分组(今日)
|
// 按设备分组
|
||||||
Map<Long, List<OpsTrafficStatisticsDO>> byDevice = stats.stream()
|
Map<Long, List<OpsTrafficStatisticsDO>> byDevice = stats.stream()
|
||||||
.filter(s -> s.getDeviceId() != null)
|
.filter(s -> s.getDeviceId() != null)
|
||||||
.collect(Collectors.groupingBy(OpsTrafficStatisticsDO::getDeviceId));
|
.collect(Collectors.groupingBy(OpsTrafficStatisticsDO::getDeviceId));
|
||||||
|
|
||||||
// 按设备分组(昨日)
|
|
||||||
Map<Long, List<OpsTrafficStatisticsDO>> yesterdayByDevice = yesterdayStats.stream()
|
|
||||||
.filter(s -> s.getDeviceId() != null)
|
|
||||||
.collect(Collectors.groupingBy(OpsTrafficStatisticsDO::getDeviceId));
|
|
||||||
|
|
||||||
List<DeviceTrafficRealtimeRespVO> result = new ArrayList<>();
|
List<DeviceTrafficRealtimeRespVO> result = new ArrayList<>();
|
||||||
for (Map.Entry<Long, List<OpsTrafficStatisticsDO>> entry : byDevice.entrySet()) {
|
for (Map.Entry<Long, List<OpsTrafficStatisticsDO>> entry : byDevice.entrySet()) {
|
||||||
Long deviceId = entry.getKey();
|
Long deviceId = entry.getKey();
|
||||||
@@ -464,8 +459,6 @@ public class OpsStatisticsServiceImpl implements OpsStatisticsService {
|
|||||||
long deviceOut = deviceStats.stream().mapToLong(s -> s.getPeopleOut() != null ? s.getPeopleOut() : 0).sum();
|
long deviceOut = deviceStats.stream().mapToLong(s -> s.getPeopleOut() != null ? s.getPeopleOut() : 0).sum();
|
||||||
|
|
||||||
HourlyTrend hourlyTrend = buildTrafficHourlyTrend(deviceStats);
|
HourlyTrend hourlyTrend = buildTrafficHourlyTrend(deviceStats);
|
||||||
HourlyTrend yesterdayHourlyTrend = buildTrafficHourlyTrend(
|
|
||||||
yesterdayByDevice.getOrDefault(deviceId, List.of()));
|
|
||||||
|
|
||||||
result.add(DeviceTrafficRealtimeRespVO.builder()
|
result.add(DeviceTrafficRealtimeRespVO.builder()
|
||||||
.deviceId(deviceId)
|
.deviceId(deviceId)
|
||||||
@@ -474,8 +467,8 @@ public class OpsStatisticsServiceImpl implements OpsStatisticsService {
|
|||||||
.todayIn(deviceIn)
|
.todayIn(deviceIn)
|
||||||
.todayOut(deviceOut)
|
.todayOut(deviceOut)
|
||||||
.currentOccupancy(Math.max(0, deviceIn - deviceOut))
|
.currentOccupancy(Math.max(0, deviceIn - deviceOut))
|
||||||
|
.statDate(today.toString())
|
||||||
.hourlyTrend(hourlyTrend)
|
.hourlyTrend(hourlyTrend)
|
||||||
.yesterdayHourlyTrend(yesterdayHourlyTrend)
|
|
||||||
.build());
|
.build());
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
@@ -509,8 +502,8 @@ public class OpsStatisticsServiceImpl implements OpsStatisticsService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Cacheable(value = "ops:statistics:area-traffic-realtime#5m",
|
@Cacheable(value = "ops:statistics:area-traffic-realtime#5m",
|
||||||
key = "T(String).join(',', #areaIds.![toString()])", unless = "#result == null")
|
key = "T(String).join(',', #areaIds.![toString()]) + ':' + (#date != null ? #date.toString() : T(java.time.LocalDate).now().toString())", unless = "#result == null")
|
||||||
public TrafficRealtimeRespVO getAreaTrafficRealtime(List<Long> areaIds) {
|
public TrafficRealtimeRespVO getAreaTrafficRealtime(List<Long> areaIds, LocalDate date) {
|
||||||
if (areaIds == null || areaIds.isEmpty()) {
|
if (areaIds == null || areaIds.isEmpty()) {
|
||||||
return TrafficRealtimeRespVO.builder()
|
return TrafficRealtimeRespVO.builder()
|
||||||
.totalIn(0L).totalOut(0L).currentOccupancy(0L)
|
.totalIn(0L).totalOut(0L).currentOccupancy(0L)
|
||||||
@@ -521,42 +514,36 @@ public class OpsStatisticsServiceImpl implements OpsStatisticsService {
|
|||||||
if (areaIds.size() > 200) {
|
if (areaIds.size() > 200) {
|
||||||
areaIds = areaIds.subList(0, 200);
|
areaIds = areaIds.subList(0, 200);
|
||||||
}
|
}
|
||||||
|
if (date == null) {
|
||||||
|
date = LocalDate.now();
|
||||||
|
}
|
||||||
|
|
||||||
LocalDate today = LocalDate.now();
|
LocalDateTime dayStart = date.atStartOfDay();
|
||||||
LocalDateTime todayStart = today.atStartOfDay();
|
LocalDateTime dayEnd = dayStart.plusDays(1);
|
||||||
LocalDateTime todayEnd = todayStart.plusDays(1);
|
|
||||||
|
|
||||||
// 查询今日数据,条件 areaId IN (areaIds)
|
// 查询指定日期数据,条件 areaId IN (areaIds)
|
||||||
List<OpsTrafficStatisticsDO> todayStats = trafficStatisticsMapper.selectList(
|
List<OpsTrafficStatisticsDO> dayStats = trafficStatisticsMapper.selectList(
|
||||||
new LambdaQueryWrapperX<OpsTrafficStatisticsDO>()
|
new LambdaQueryWrapperX<OpsTrafficStatisticsDO>()
|
||||||
.in(OpsTrafficStatisticsDO::getAreaId, areaIds)
|
.in(OpsTrafficStatisticsDO::getAreaId, areaIds)
|
||||||
.ge(OpsTrafficStatisticsDO::getStatHour, todayStart)
|
.ge(OpsTrafficStatisticsDO::getStatHour, dayStart)
|
||||||
.lt(OpsTrafficStatisticsDO::getStatHour, todayEnd));
|
.lt(OpsTrafficStatisticsDO::getStatHour, dayEnd));
|
||||||
|
|
||||||
// 查询昨日数据
|
|
||||||
LocalDate yesterday = today.minusDays(1);
|
|
||||||
LocalDateTime yesterdayStart = yesterday.atStartOfDay();
|
|
||||||
List<OpsTrafficStatisticsDO> yesterdayStats = trafficStatisticsMapper.selectList(
|
|
||||||
new LambdaQueryWrapperX<OpsTrafficStatisticsDO>()
|
|
||||||
.in(OpsTrafficStatisticsDO::getAreaId, areaIds)
|
|
||||||
.ge(OpsTrafficStatisticsDO::getStatHour, yesterdayStart)
|
|
||||||
.lt(OpsTrafficStatisticsDO::getStatHour, todayStart));
|
|
||||||
|
|
||||||
// 无数据时设置提示信息
|
// 无数据时设置提示信息
|
||||||
if (todayStats.isEmpty() && yesterdayStats.isEmpty()) {
|
if (dayStats.isEmpty()) {
|
||||||
return TrafficRealtimeRespVO.builder()
|
return TrafficRealtimeRespVO.builder()
|
||||||
.totalIn(0L).totalOut(0L).currentOccupancy(0L)
|
.totalIn(0L).totalOut(0L).currentOccupancy(0L)
|
||||||
|
.statDate(date.toString())
|
||||||
.message("该区域暂未配置客流设备")
|
.message("该区域暂未配置客流设备")
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 汇总 totalIn、totalOut
|
// 汇总 totalIn、totalOut
|
||||||
long totalIn = todayStats.stream().mapToLong(s -> s.getPeopleIn() != null ? s.getPeopleIn() : 0).sum();
|
long totalIn = dayStats.stream().mapToLong(s -> s.getPeopleIn() != null ? s.getPeopleIn() : 0).sum();
|
||||||
long totalOut = todayStats.stream().mapToLong(s -> s.getPeopleOut() != null ? s.getPeopleOut() : 0).sum();
|
long totalOut = dayStats.stream().mapToLong(s -> s.getPeopleOut() != null ? s.getPeopleOut() : 0).sum();
|
||||||
|
|
||||||
// 按区域汇总明细
|
// 按区域汇总明细
|
||||||
Map<Long, String> areaNameMap = loadAreaNameMap();
|
Map<Long, String> areaNameMap = loadAreaNameMap();
|
||||||
Map<Long, List<OpsTrafficStatisticsDO>> byArea = todayStats.stream()
|
Map<Long, List<OpsTrafficStatisticsDO>> byArea = dayStats.stream()
|
||||||
.filter(s -> s.getAreaId() != null)
|
.filter(s -> s.getAreaId() != null)
|
||||||
.collect(Collectors.groupingBy(OpsTrafficStatisticsDO::getAreaId));
|
.collect(Collectors.groupingBy(OpsTrafficStatisticsDO::getAreaId));
|
||||||
|
|
||||||
@@ -579,16 +566,15 @@ public class OpsStatisticsServiceImpl implements OpsStatisticsService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 小时趋势
|
// 小时趋势
|
||||||
HourlyTrend hourlyTrend = buildTrafficHourlyTrend(todayStats);
|
HourlyTrend hourlyTrend = buildTrafficHourlyTrend(dayStats);
|
||||||
HourlyTrend yesterdayHourlyTrend = buildTrafficHourlyTrend(yesterdayStats);
|
|
||||||
|
|
||||||
return TrafficRealtimeRespVO.builder()
|
return TrafficRealtimeRespVO.builder()
|
||||||
.totalIn(totalIn)
|
.totalIn(totalIn)
|
||||||
.totalOut(totalOut)
|
.totalOut(totalOut)
|
||||||
.currentOccupancy(Math.max(0, totalIn - totalOut))
|
.currentOccupancy(Math.max(0, totalIn - totalOut))
|
||||||
.areas(areas)
|
.areas(areas)
|
||||||
|
.statDate(date.toString())
|
||||||
.hourlyTrend(hourlyTrend)
|
.hourlyTrend(hourlyTrend)
|
||||||
.yesterdayHourlyTrend(yesterdayHourlyTrend)
|
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -743,48 +729,42 @@ public class OpsStatisticsServiceImpl implements OpsStatisticsService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构建漏斗数据(统计指定时间范围内的工单)
|
* 构建工单状态分布数据 -- SQL GROUP BY 聚合,避免大量数据加载到内存
|
||||||
*/
|
*/
|
||||||
private List<FunnelItem> buildFunnelData(String orderType, LocalDateTime startDateTime, LocalDateTime endDateTime) {
|
private List<StatusDistributionItem> buildStatusDistribution(String orderType,
|
||||||
// 创建工单 = 时间范围内创建的所有非取消工单
|
LocalDateTime startDateTime,
|
||||||
long totalCreated = opsOrderMapper.selectCount(
|
LocalDateTime endDateTime) {
|
||||||
|
Map<String, String> statusLabelMap = new LinkedHashMap<>();
|
||||||
|
statusLabelMap.put("PENDING", "待处理");
|
||||||
|
statusLabelMap.put("QUEUED", "排队中");
|
||||||
|
statusLabelMap.put("DISPATCHED", "已派单");
|
||||||
|
statusLabelMap.put("ARRIVED", "已到岗");
|
||||||
|
statusLabelMap.put("COMPLETED", "已完成");
|
||||||
|
statusLabelMap.put("CANCELLED", "已取消");
|
||||||
|
statusLabelMap.put("PAUSED", "已暂停");
|
||||||
|
|
||||||
|
// 使用 selectCount 按状态逐个查询(状态数量有限,最多7次查询,避免大量数据加载到内存)
|
||||||
|
Map<String, Long> statusCountMap = new HashMap<>();
|
||||||
|
for (String status : statusLabelMap.keySet()) {
|
||||||
|
Long count = opsOrderMapper.selectCount(
|
||||||
new LambdaQueryWrapperX<OpsOrderDO>()
|
new LambdaQueryWrapperX<OpsOrderDO>()
|
||||||
.eq(OpsOrderDO::getOrderType, orderType)
|
.eq(OpsOrderDO::getOrderType, orderType)
|
||||||
.ne(OpsOrderDO::getStatus, "CANCELLED")
|
.eq(OpsOrderDO::getStatus, status)
|
||||||
.ge(OpsOrderDO::getCreateTime, startDateTime)
|
.ge(OpsOrderDO::getCreateTime, startDateTime)
|
||||||
.lt(OpsOrderDO::getCreateTime, endDateTime));
|
.lt(OpsOrderDO::getCreateTime, endDateTime));
|
||||||
|
if (count > 0) {
|
||||||
|
statusCountMap.put(status, count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 已分配 = 时间范围内创建且已进入工作流程的工单(排除 PENDING 和 CANCELLED)
|
List<StatusDistributionItem> result = new ArrayList<>();
|
||||||
long assigned = opsOrderMapper.selectCount(
|
for (Map.Entry<String, String> entry : statusLabelMap.entrySet()) {
|
||||||
new LambdaQueryWrapperX<OpsOrderDO>()
|
Long count = statusCountMap.get(entry.getKey());
|
||||||
.eq(OpsOrderDO::getOrderType, orderType)
|
if (count != null && count > 0) {
|
||||||
.notIn(OpsOrderDO::getStatus, "PENDING", "CANCELLED")
|
result.add(StatusDistributionItem.builder().name(entry.getValue()).value(count.intValue()).build());
|
||||||
.ge(OpsOrderDO::getCreateTime, startDateTime)
|
}
|
||||||
.lt(OpsOrderDO::getCreateTime, endDateTime));
|
}
|
||||||
|
return result;
|
||||||
// 已到岗 = 时间范围内创建且已到岗的工单(ARRIVED + COMPLETED + PAUSED)
|
|
||||||
long arrived = opsOrderMapper.selectCount(
|
|
||||||
new LambdaQueryWrapperX<OpsOrderDO>()
|
|
||||||
.eq(OpsOrderDO::getOrderType, orderType)
|
|
||||||
.in(OpsOrderDO::getStatus, "ARRIVED", "COMPLETED", "PAUSED")
|
|
||||||
.ge(OpsOrderDO::getCreateTime, startDateTime)
|
|
||||||
.lt(OpsOrderDO::getCreateTime, endDateTime));
|
|
||||||
|
|
||||||
// 已完成 = 时间范围内创建且已完成的工单
|
|
||||||
long completed = opsOrderMapper.selectCount(
|
|
||||||
new LambdaQueryWrapperX<OpsOrderDO>()
|
|
||||||
.eq(OpsOrderDO::getStatus, "COMPLETED")
|
|
||||||
.eq(OpsOrderDO::getOrderType, orderType)
|
|
||||||
.ge(OpsOrderDO::getCreateTime, startDateTime)
|
|
||||||
.lt(OpsOrderDO::getCreateTime, endDateTime));
|
|
||||||
|
|
||||||
List<FunnelItem> funnelData = new ArrayList<>();
|
|
||||||
funnelData.add(FunnelItem.builder().name("创建工单").value((int) totalCreated).build());
|
|
||||||
funnelData.add(FunnelItem.builder().name("已分配").value((int) assigned).build());
|
|
||||||
funnelData.add(FunnelItem.builder().name("已到岗").value((int) arrived).build());
|
|
||||||
funnelData.add(FunnelItem.builder().name("已完成").value((int) completed).build());
|
|
||||||
|
|
||||||
return funnelData;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -883,6 +863,61 @@ public class OpsStatisticsServiceImpl implements OpsStatisticsService {
|
|||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建工牌队列统计(近7天每天入队的工单数,查 ops_order_queue 表)
|
||||||
|
* 通过关联 ops_order 表按 orderType 过滤
|
||||||
|
*/
|
||||||
|
private BadgeQueueStats buildBadgeQueueStats(String orderType, LocalDate startDate, LocalDate endDate) {
|
||||||
|
LocalDateTime startDateTime = startDate.atStartOfDay();
|
||||||
|
LocalDateTime endDateTime = endDate.plusDays(1).atStartOfDay();
|
||||||
|
|
||||||
|
// 先查询该类型在时间范围内的工单ID集合
|
||||||
|
List<OpsOrderDO> orders = opsOrderMapper.selectList(
|
||||||
|
new LambdaQueryWrapperX<OpsOrderDO>()
|
||||||
|
.select(OpsOrderDO::getId)
|
||||||
|
.eq(OpsOrderDO::getOrderType, orderType)
|
||||||
|
.ge(OpsOrderDO::getCreateTime, startDateTime)
|
||||||
|
.lt(OpsOrderDO::getCreateTime, endDateTime));
|
||||||
|
Set<Long> orderIds = orders.stream()
|
||||||
|
.map(OpsOrderDO::getId)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
|
if (orderIds.isEmpty()) {
|
||||||
|
// 无工单,直接返回空数据
|
||||||
|
List<String> dates = new ArrayList<>();
|
||||||
|
List<Integer> queueData = new ArrayList<>();
|
||||||
|
for (LocalDate d = startDate; !d.isAfter(endDate); d = d.plusDays(1)) {
|
||||||
|
dates.add(d.format(DATE_FORMATTER));
|
||||||
|
queueData.add(0);
|
||||||
|
}
|
||||||
|
return BadgeQueueStats.builder().dates(dates).queueData(queueData).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询队列记录,按工单ID过滤
|
||||||
|
List<OpsOrderQueueDO> queueRecords =
|
||||||
|
opsOrderQueueMapper.selectList(
|
||||||
|
new LambdaQueryWrapperX<OpsOrderQueueDO>()
|
||||||
|
.in(OpsOrderQueueDO::getOpsOrderId, orderIds)
|
||||||
|
.ge(OpsOrderQueueDO::getEnqueueTime, startDateTime)
|
||||||
|
.lt(OpsOrderQueueDO::getEnqueueTime, endDateTime));
|
||||||
|
|
||||||
|
Map<String, Integer> dailyQueueMap = queueRecords.stream()
|
||||||
|
.filter(q -> q.getEnqueueTime() != null)
|
||||||
|
.collect(Collectors.groupingBy(
|
||||||
|
q -> q.getEnqueueTime().toLocalDate().toString(),
|
||||||
|
Collectors.summingInt(q -> 1)
|
||||||
|
));
|
||||||
|
|
||||||
|
List<String> dates = new ArrayList<>();
|
||||||
|
List<Integer> queueData = new ArrayList<>();
|
||||||
|
for (LocalDate d = startDate; !d.isAfter(endDate); d = d.plusDays(1)) {
|
||||||
|
dates.add(d.format(DATE_FORMATTER));
|
||||||
|
queueData.add(dailyQueueMap.getOrDefault(d.toString(), 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
return BadgeQueueStats.builder().dates(dates).queueData(queueData).build();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构建客流小时趋势
|
* 构建客流小时趋势
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.viewsh.module.ops.config;
|
|||||||
|
|
||||||
import com.viewsh.module.ops.api.badge.BadgeDeviceStatusDTO;
|
import com.viewsh.module.ops.api.badge.BadgeDeviceStatusDTO;
|
||||||
import com.viewsh.module.ops.dal.dataobject.area.OpsAreaDeviceRelationDO;
|
import com.viewsh.module.ops.dal.dataobject.area.OpsAreaDeviceRelationDO;
|
||||||
|
import com.viewsh.module.ops.enums.BadgeDeviceStatusEnum;
|
||||||
import com.viewsh.module.ops.environment.service.badge.BadgeDeviceStatusService;
|
import com.viewsh.module.ops.environment.service.badge.BadgeDeviceStatusService;
|
||||||
import com.viewsh.module.ops.service.area.AreaDeviceService;
|
import com.viewsh.module.ops.service.area.AreaDeviceService;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@@ -32,7 +33,11 @@ public class StatisticsConfiguration {
|
|||||||
return new int[]{0, 0};
|
return new int[]{0, 0};
|
||||||
}
|
}
|
||||||
List<BadgeDeviceStatusDTO> activeBadges = badgeDeviceStatusService.listActiveBadges();
|
List<BadgeDeviceStatusDTO> activeBadges = badgeDeviceStatusService.listActiveBadges();
|
||||||
int onlineCount = activeBadges.size();
|
// 只计算 IDLE 和 BUSY 状态的工牌作为在线数(不含 PAUSED 和 OFFLINE)
|
||||||
|
int onlineCount = (int) activeBadges.stream()
|
||||||
|
.filter(b -> b.getStatus() == BadgeDeviceStatusEnum.IDLE
|
||||||
|
|| b.getStatus() == BadgeDeviceStatusEnum.BUSY)
|
||||||
|
.count();
|
||||||
// 从区域设备关联表查询所有已注册的工牌设备总数
|
// 从区域设备关联表查询所有已注册的工牌设备总数
|
||||||
int totalCount = onlineCount;
|
int totalCount = onlineCount;
|
||||||
if (areaDeviceService != null) {
|
if (areaDeviceService != null) {
|
||||||
|
|||||||
@@ -42,23 +42,26 @@ public class OpsTrafficController {
|
|||||||
@GetMapping("/realtime")
|
@GetMapping("/realtime")
|
||||||
@Operation(summary = "全局实时客流监测")
|
@Operation(summary = "全局实时客流监测")
|
||||||
@PreAuthorize("@ss.hasPermission('ops:traffic:query')")
|
@PreAuthorize("@ss.hasPermission('ops:traffic:query')")
|
||||||
public CommonResult<TrafficRealtimeRespVO> getTrafficRealtime() {
|
public CommonResult<TrafficRealtimeRespVO> getTrafficRealtime(
|
||||||
|
@RequestParam(value = "date", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate date) {
|
||||||
if (opsStatisticsService == null) {
|
if (opsStatisticsService == null) {
|
||||||
log.warn("[getTrafficRealtime] OpsStatisticsService 未注入,返回默认值");
|
log.warn("[getTrafficRealtime] OpsStatisticsService 未注入,返回默认值");
|
||||||
return success(TrafficRealtimeRespVO.builder().build());
|
return success(TrafficRealtimeRespVO.builder().build());
|
||||||
}
|
}
|
||||||
return success(opsStatisticsService.getTrafficRealtime());
|
return success(opsStatisticsService.getTrafficRealtime(date));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/trend")
|
@GetMapping("/trend")
|
||||||
@Operation(summary = "全局近7天客流趋势统计")
|
@Operation(summary = "客流趋势统计")
|
||||||
@PreAuthorize("@ss.hasPermission('ops:traffic:query')")
|
@PreAuthorize("@ss.hasPermission('ops:traffic:query')")
|
||||||
public CommonResult<TrafficTrendRespVO> getTrafficTrend() {
|
public CommonResult<TrafficTrendRespVO> getTrafficTrend(
|
||||||
|
@RequestParam(value = "startDate", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate startDate,
|
||||||
|
@RequestParam(value = "endDate", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate endDate) {
|
||||||
if (opsStatisticsService == null) {
|
if (opsStatisticsService == null) {
|
||||||
log.warn("[getTrafficTrend] OpsStatisticsService 未注入,返回默认值");
|
log.warn("[getTrafficTrend] OpsStatisticsService 未注入,返回默认值");
|
||||||
return success(TrafficTrendRespVO.builder().build());
|
return success(TrafficTrendRespVO.builder().build());
|
||||||
}
|
}
|
||||||
return success(opsStatisticsService.getTrafficTrend());
|
return success(opsStatisticsService.getTrafficTrend(startDate, endDate));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/device/realtime")
|
@GetMapping("/device/realtime")
|
||||||
@@ -79,7 +82,8 @@ public class OpsTrafficController {
|
|||||||
@Parameter(name = "areaIds", description = "区域ID列表", required = true)
|
@Parameter(name = "areaIds", description = "区域ID列表", required = true)
|
||||||
@PreAuthorize("@ss.hasPermission('ops:traffic:query')")
|
@PreAuthorize("@ss.hasPermission('ops:traffic:query')")
|
||||||
public CommonResult<TrafficRealtimeRespVO> getAreaTrafficRealtime(
|
public CommonResult<TrafficRealtimeRespVO> getAreaTrafficRealtime(
|
||||||
@RequestParam("areaIds") List<Long> areaIds) {
|
@RequestParam("areaIds") List<Long> areaIds,
|
||||||
|
@RequestParam(value = "date", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate date) {
|
||||||
if (opsStatisticsService == null) {
|
if (opsStatisticsService == null) {
|
||||||
log.warn("[getAreaTrafficRealtime] OpsStatisticsService 未注入,返回默认值");
|
log.warn("[getAreaTrafficRealtime] OpsStatisticsService 未注入,返回默认值");
|
||||||
return success(TrafficRealtimeRespVO.builder().build());
|
return success(TrafficRealtimeRespVO.builder().build());
|
||||||
@@ -87,7 +91,7 @@ public class OpsTrafficController {
|
|||||||
if (areaIds.size() > 200) {
|
if (areaIds.size() > 200) {
|
||||||
areaIds = areaIds.subList(0, 200);
|
areaIds = areaIds.subList(0, 200);
|
||||||
}
|
}
|
||||||
return success(opsStatisticsService.getAreaTrafficRealtime(areaIds));
|
return success(opsStatisticsService.getAreaTrafficRealtime(areaIds, date));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/device/trend")
|
@GetMapping("/device/trend")
|
||||||
|
|||||||
Reference in New Issue
Block a user