feat(ops): 实现工单统计看板功能
Some checks failed
Java CI with Maven / build (11) (push) Has been cancelled
Java CI with Maven / build (17) (push) Has been cancelled
Java CI with Maven / build (8) (push) Has been cancelled

1. 修复 MyBatis 类型安全问题
   - 创建 9 个 DTO 类替换 List<Map<String, Object>>
   - 修复 @MapKey 错误,使用强类型返回值

2. 实现工单统计看板 5 大功能
   - 漏斗统计:支持时间范围过滤
   - 时段热力图:改为近 7 天,Y 轴显示日期(MM-dd)
   - 功能类型排行:替换区域排行,JOIN ops_bus_area 表
   - 今日工单时段分布:X 轴优化为每 2 小时展示
   - 近七天客流统计:独立接口,支持工作台实时趋势

3. 字典转换实现
   - 新增 DictTypeConstants.OPS_AREA_FUNCTION_TYPE(保留供未来扩展)
   - 使用硬编码 Map 实现功能类型中文转换(性能最优)
   - 添加 TODO 说明未来可切换 DictFrameworkUtils

4. SQL 优化
   - 功能类型统计:INNER JOIN ops_bus_area 表
   - 热力图查询:按日期和小时分组统计
   - 时段分布:仅统计当天数据

5. 缓存策略
   - 看板统计:5 分钟缓存(@Cacheable)
   - 客流监测:5 分钟缓存
   - 防止高并发查询压力

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
lzh
2026-02-10 23:28:02 +08:00
parent 113e90c726
commit 16441e7c25
19 changed files with 1630 additions and 2 deletions

View File

@@ -0,0 +1,50 @@
package com.viewsh.module.ops.config;
import com.viewsh.module.ops.api.badge.BadgeDeviceStatusDTO;
import com.viewsh.module.ops.dal.dataobject.area.OpsAreaDeviceRelationDO;
import com.viewsh.module.ops.environment.service.badge.BadgeDeviceStatusService;
import com.viewsh.module.ops.service.area.AreaDeviceService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.List;
import java.util.function.Supplier;
/**
* 统计服务配置,注册跨模块依赖的 Bean
*
* @author lzh
*/
@Configuration
public class StatisticsConfiguration {
@Autowired(required = false)
private BadgeDeviceStatusService badgeDeviceStatusService;
@Autowired(required = false)
private AreaDeviceService areaDeviceService;
@Bean
public Supplier<int[]> staffCountProvider() {
return () -> {
if (badgeDeviceStatusService == null) {
return new int[]{0, 0};
}
List<BadgeDeviceStatusDTO> activeBadges = badgeDeviceStatusService.listActiveBadges();
int onlineCount = activeBadges.size();
// 从区域设备关联表查询所有已注册的工牌设备总数
int totalCount = onlineCount;
if (areaDeviceService != null) {
try {
List<OpsAreaDeviceRelationDO> allBadges = areaDeviceService.listByAreaIdAndType(null, "BADGE");
totalCount = Math.max(onlineCount, allBadges.size());
} catch (Exception ignored) {
// 查询失败时使用在线数作为兜底
}
}
return new int[]{onlineCount, totalCount};
};
}
}

View File

@@ -3,10 +3,15 @@ package com.viewsh.module.ops.controller.admin;
import com.viewsh.framework.common.pojo.CommonResult;
import com.viewsh.framework.common.pojo.PageResult;
import com.viewsh.module.ops.api.clean.QuickStatsRespDTO;
import com.viewsh.module.ops.controller.admin.workorder.vo.statistics.DashboardStatsRespVO;
import com.viewsh.module.ops.controller.admin.workorder.vo.statistics.TrafficRealtimeRespVO;
import com.viewsh.module.ops.controller.admin.workorder.vo.statistics.TrafficTrendRespVO;
import com.viewsh.module.ops.controller.admin.workorder.vo.statistics.WorkspaceStatsRespVO;
import com.viewsh.module.ops.environment.service.dashboard.CleanDashboardService;
import com.viewsh.module.ops.service.OrderDetailVO;
import com.viewsh.module.ops.service.OrderQueryService;
import com.viewsh.module.ops.service.OrderSummaryVO;
import com.viewsh.module.ops.service.statistics.OpsStatisticsService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
@@ -14,10 +19,12 @@ import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDate;
import java.util.Map;
import static com.viewsh.framework.common.pojo.CommonResult.success;
@@ -42,6 +49,9 @@ public class OrderCenterController {
@Autowired(required = false)
private CleanDashboardService cleanDashboardService;
@Autowired(required = false)
private OpsStatisticsService opsStatisticsService;
@GetMapping("/page")
@Operation(summary = "工单中心分页查询")
@PreAuthorize("@ss.hasPermission('ops:order-center:query')")
@@ -87,4 +97,57 @@ public class OrderCenterController {
QuickStatsRespDTO stats = cleanDashboardService.getQuickStats();
return success(stats);
}
@GetMapping("/dashboard-stats")
@Operation(summary = "看板统计")
@PreAuthorize("@ss.hasPermission('ops:order-center:query')")
public CommonResult<DashboardStatsRespVO> getDashboardStats(
@RequestParam(value = "orderType", required = false, defaultValue = "CLEAN") String orderType,
@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) {
log.warn("[getDashboardStats] OpsStatisticsService 未注入,返回默认值");
return success(DashboardStatsRespVO.builder().build());
}
if (startDate == null) {
startDate = LocalDate.now().minusDays(6);
}
if (endDate == null) {
endDate = LocalDate.now();
}
return success(opsStatisticsService.getDashboardStats(orderType, startDate, endDate));
}
@GetMapping("/traffic-realtime")
@Operation(summary = "实时客流监测")
@PreAuthorize("@ss.hasPermission('ops:order-center:query')")
public CommonResult<TrafficRealtimeRespVO> getTrafficRealtime() {
if (opsStatisticsService == null) {
log.warn("[getTrafficRealtime] OpsStatisticsService 未注入,返回默认值");
return success(TrafficRealtimeRespVO.builder().build());
}
return success(opsStatisticsService.getTrafficRealtime());
}
@GetMapping("/workspace-stats")
@Operation(summary = "工作台统计")
@PreAuthorize("@ss.hasPermission('ops:order-center:query')")
public CommonResult<WorkspaceStatsRespVO> getWorkspaceStats() {
if (opsStatisticsService == null) {
log.warn("[getWorkspaceStats] OpsStatisticsService 未注入,返回默认值");
return success(WorkspaceStatsRespVO.builder().build());
}
return success(opsStatisticsService.getWorkspaceStats());
}
@GetMapping("/traffic-trend")
@Operation(summary = "近7天客流趋势统计")
@PreAuthorize("@ss.hasPermission('ops:order-center:query')")
public CommonResult<TrafficTrendRespVO> getTrafficTrend() {
if (opsStatisticsService == null) {
log.warn("[getTrafficTrend] OpsStatisticsService 未注入,返回默认值");
return success(TrafficTrendRespVO.builder().build());
}
return success(opsStatisticsService.getTrafficTrend());
}
}