Просмотр исходного кода

feat(warning): 添加预警仪表盘功能

- 新增 WarningDashboardController 提供预警大屏可视化接口
- 新增 WarningDashboardService 定义预警统计服务接口
- 新增 WarningDashboardServiceImpl 实现预警数据统计逻辑
- 提供今日预警概况、历史预警概况、今日未处置预警三个核心接口
- 实现预警数据按专项分类统计和未处理预警集中展示功能
- 集成待办任务与预警信息关联查询,支持预警处置状态跟踪
林仔 2 недель назад
Родитель
Сommit
f64a473661

+ 73 - 0
pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/warning/WarningDashboardController.java

@@ -0,0 +1,73 @@
+package com.zksy.web.controller.warning;
+
+import com.zksy.base.warning.service.WarningDashboardService;
+import com.zksy.common.annotation.Anonymous;
+import com.zksy.common.core.domain.AjaxResult;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 预警仪表盘控制器
+ * <p>
+ * 提供预警大屏可视化所需的三个核心接口:
+ * <ul>
+ *   <li>今日预警概况 — 未解除/已解除预警统计及详情</li>
+ *   <li>历史预警概况 — 各专项预警累计数量和分布</li>
+ *   <li>今日未处置预警 — 未及时处置的预警集中展示</li>
+ * </ul>
+ *
+ * @author zksy
+ */
+@Slf4j
+@RestController
+@RequestMapping("/api/warning-dashboard")
+@Api(tags = "预警仪表盘")
+public class WarningDashboardController {
+
+    @Autowired
+    private WarningDashboardService warningDashboardService;
+
+    /**
+     * 今日预警概况
+     * <p>
+     * 以当前日期维度统计:截止目前仍未解除的预警数量和今日已解除的预警数量,
+     * 并给出预警的详情描述信息。
+     */
+    @GetMapping("/today-overview")
+    @ApiOperation("今日预警概况 — 未解除/已解除预警统计及详情")
+    @Anonymous
+    public AjaxResult getTodayOverview() {
+        return AjaxResult.success(warningDashboardService.getTodayOverview());
+    }
+
+    /**
+     * 历史预警概况
+     * <p>
+     * 以历史预警事件累计维度,展示各专项预警总体数量和分布情况,
+     * 支持对具体预警事件进行详情查看。
+     */
+    @GetMapping("/history-overview")
+    @ApiOperation("历史预警概况 — 各专项预警累计数量和分布")
+    @Anonymous
+    public AjaxResult getHistoryOverview() {
+        return AjaxResult.success(warningDashboardService.getHistoryOverview());
+    }
+
+    /**
+     * 今日未处置预警
+     * <p>
+     * 针对截至当前仍未及时处置的预警事件进行集中展示,
+     * 让监管者或相关领导重点关注仍未处置的预警,督促权属单位及时完成警情处置。
+     */
+    @GetMapping("/today-unprocessed")
+    @ApiOperation("今日未处置预警 — 未及时处置的预警集中展示")
+    @Anonymous
+    public AjaxResult getTodayUnprocessed() {
+        return AjaxResult.success(warningDashboardService.getTodayUnprocessed());
+    }
+}

+ 49 - 0
pipe-network-service/zksy-system/src/main/java/com/zksy/base/warning/service/WarningDashboardService.java

@@ -0,0 +1,49 @@
+package com.zksy.base.warning.service;
+
+import java.util.Map;
+
+/**
+ * 预警仪表盘服务接口
+ * <p>
+ * 提供预警大屏可视化所需的统计数据,包括:
+ * <ul>
+ *   <li>今日预警概况 — 截至当前仍未解除 / 今日已解除的预警统计</li>
+ *   <li>历史预警概况 — 各专项预警累计数量和分布</li>
+ *   <li>今日未处置预警 — 今日仍未及时处置的预警列表</li>
+ * </ul>
+ *
+ * @author zksy
+ */
+public interface WarningDashboardService {
+
+    /**
+     * 今日预警概况
+     * <p>
+     * 以当前日期为维度,统计截止目前仍未解除的预警数量和今日已解除的预警数量,
+     * 并返回预警详情描述信息。
+     *
+     * @return { unresolvedCount, resolvedCount, unresolvedList, resolvedList }
+     */
+    Map<String, Object> getTodayOverview();
+
+    /**
+     * 历史预警概况
+     * <p>
+     * 以城市设施运行的历史预警事件累计维度,展示各专项预警总体数量和分布情况,
+     * 支持对具体预警事件进行详情查看。
+     *
+     * @return { totalCount, specialStats: [{specialName, count, percentage}], detailList }
+     */
+    Map<String, Object> getHistoryOverview();
+
+    /**
+     * 今日未处置预警
+     * <p>
+     * 针对截至当前仍未及时处置的预警事件进行集中展示,让监管者重点关注。
+     *
+     * @return { total, list: [{warningId, warningName, warningType, warningLevel, warningSpecial,
+     *          location, ownershipUnit, publisher, publishTime, status, warningContent,
+     *          todoInfo: {userName, taskName, createTime}}] }
+     */
+    Map<String, Object> getTodayUnprocessed();
+}

+ 232 - 0
pipe-network-service/zksy-system/src/main/java/com/zksy/base/warning/service/impl/WarningDashboardServiceImpl.java

@@ -0,0 +1,232 @@
+package com.zksy.base.warning.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.zksy.base.warning.domain.EarlyWarning;
+import com.zksy.base.warning.domain.WarningTodo;
+import com.zksy.base.warning.mapper.EarlyWarningMapper;
+import com.zksy.base.warning.mapper.WarningTodoMapper;
+import com.zksy.base.warning.service.WarningDashboardService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 预警仪表盘服务实现
+ * <p>
+ * 从 early_warning(预警主表)、warning_todo(预警待办表)、warning_archive(预警归档表)
+ * 中提取数据,提供大屏可视化所需的统计指标。
+ *
+ * @author zksy
+ */
+@Slf4j
+@Service
+public class WarningDashboardServiceImpl implements WarningDashboardService {
+
+    @Autowired
+    private EarlyWarningMapper earlyWarningMapper;
+
+    @Autowired
+    private WarningTodoMapper warningTodoMapper;
+
+    /**
+     * 已解除/已关闭的状态值集合
+     */
+    private static final Set<String> RESOLVED_STATUSES = new HashSet<>(Arrays.asList("CLOSED", "RESOLVED"));
+
+    @Override
+    public Map<String, Object> getTodayOverview() {
+        Map<String, Object> result = new LinkedHashMap<>();
+
+        LocalDate today = LocalDate.now();
+        LocalDateTime todayStart = today.atStartOfDay();
+        LocalDateTime todayEnd = today.atTime(23, 59, 59);
+
+        // 1. 查询今日所有预警
+        LambdaQueryWrapper<EarlyWarning> todayWrapper = new LambdaQueryWrapper<>();
+        todayWrapper.between(EarlyWarning::getCreateTime, todayStart, todayEnd);
+        List<EarlyWarning> todayWarnings = earlyWarningMapper.selectList(todayWrapper);
+
+        // 2. 分类:未解除 vs 已解除
+        List<EarlyWarning> unresolvedList = todayWarnings.stream()
+                .filter(w -> w.getStatus() == null || !RESOLVED_STATUSES.contains(w.getStatus()))
+                .collect(Collectors.toList());
+
+        List<EarlyWarning> resolvedList = todayWarnings.stream()
+                .filter(w -> w.getStatus() != null && RESOLVED_STATUSES.contains(w.getStatus()))
+                .collect(Collectors.toList());
+
+        result.put("unresolvedCount", unresolvedList.size());
+        result.put("resolvedCount", resolvedList.size());
+        result.put("unresolvedList", buildWarningDetailList(unresolvedList));
+        result.put("resolvedList", buildWarningDetailList(resolvedList));
+
+        log.info("今日预警概况: 未解除={}, 已解除={}", unresolvedList.size(), resolvedList.size());
+        return result;
+    }
+
+    @Override
+    public Map<String, Object> getHistoryOverview() {
+        Map<String, Object> result = new LinkedHashMap<>();
+
+        // 1. 查询所有预警(含归档表中的历史数据)
+        List<EarlyWarning> allWarnings = earlyWarningMapper.selectList(null);
+        long totalCount = allWarnings.size();
+
+        // 2. 按预警专项分组统计
+        Map<String, Long> specialCountMap = allWarnings.stream()
+                .filter(w -> w.getWarningSpecial() != null && !w.getWarningSpecial().isEmpty())
+                .collect(Collectors.groupingBy(
+                        w -> w.getWarningSpecial() != null ? w.getWarningSpecial() : "未分类",
+                        Collectors.counting()));
+
+        // 未分类的
+        long unclassified = allWarnings.stream()
+                .filter(w -> w.getWarningSpecial() == null || w.getWarningSpecial().isEmpty())
+                .count();
+        if (unclassified > 0) {
+            specialCountMap.put("未分类", unclassified);
+        }
+
+        // 3. 构建专项统计列表(按数量降序)
+        List<Map<String, Object>> specialStats = specialCountMap.entrySet().stream()
+                .sorted(Map.Entry.<String, Long>comparingByValue().reversed())
+                .map(entry -> {
+                    Map<String, Object> item = new LinkedHashMap<>();
+                    item.put("specialName", entry.getKey());
+                    item.put("count", entry.getValue());
+                    item.put("percentage", totalCount > 0
+                            ? Math.round(entry.getValue() * 10000.0 / totalCount) / 100.0
+                            : 0.0);
+                    return item;
+                })
+                .collect(Collectors.toList());
+
+        result.put("totalCount", totalCount);
+        result.put("specialStats", specialStats);
+
+        // 4. 各专项的预警详情列表(前 20 条,按时间倒序)
+        List<Map<String, Object>> detailList = allWarnings.stream()
+                .sorted(Comparator.comparing(EarlyWarning::getCreateTime,
+                        Comparator.nullsLast(Comparator.reverseOrder())))
+                .limit(20)
+                .map(this::buildSingleWarningDetail)
+                .collect(Collectors.toList());
+        result.put("detailList", detailList);
+
+        log.info("历史预警概况: 总数={}, 专项数={}", totalCount, specialStats.size());
+        return result;
+    }
+
+    @Override
+    public Map<String, Object> getTodayUnprocessed() {
+        Map<String, Object> result = new LinkedHashMap<>();
+
+        LocalDate today = LocalDate.now();
+        LocalDateTime todayStart = today.atStartOfDay();
+        LocalDateTime todayEnd = today.atTime(23, 59, 59);
+
+        // 1. 查询今日创建的待办(未完成)
+        LambdaQueryWrapper<WarningTodo> todoWrapper = new LambdaQueryWrapper<>();
+        todoWrapper.between(WarningTodo::getCreateTime, todayStart, todayEnd);
+        todoWrapper.isNull(WarningTodo::getCompleteTime);
+        List<WarningTodo> unprocessedTodos = warningTodoMapper.selectList(todoWrapper);
+
+        // 2. 提取 warningId 集合,批量查询关联预警
+        Set<String> warningIds = unprocessedTodos.stream()
+                .map(WarningTodo::getWarningId)
+                .filter(Objects::nonNull)
+                .collect(Collectors.toSet());
+
+        Map<String, EarlyWarning> warningMap;
+        if (!warningIds.isEmpty()) {
+            LambdaQueryWrapper<EarlyWarning> warningWrapper = new LambdaQueryWrapper<>();
+            warningWrapper.in(EarlyWarning::getWarningId, warningIds);
+            warningMap = earlyWarningMapper.selectList(warningWrapper).stream()
+                    .collect(Collectors.toMap(EarlyWarning::getWarningId, w -> w, (a, b) -> a));
+        } else {
+            warningMap = Collections.emptyMap();
+        }
+
+        // 3. 构建未处置预警列表
+        List<Map<String, Object>> list = unprocessedTodos.stream()
+                .filter(todo -> todo.getWarningId() != null)
+                .map(todo -> {
+                    Map<String, Object> item = new LinkedHashMap<>();
+                    EarlyWarning warning = warningMap.get(todo.getWarningId());
+
+                    if (warning != null) {
+                        item.put("warningId", warning.getWarningId());
+                        item.put("warningName", warning.getWarningName());
+                        item.put("warningType", warning.getWarningType());
+                        item.put("warningLevel", warning.getWarningLevel());
+                        item.put("warningSpecial", warning.getWarningSpecial());
+                        item.put("location", warning.getLocation());
+                        item.put("ownershipUnit", warning.getOwnershipUnit());
+                        item.put("publisher", warning.getPublisher());
+                        item.put("publishTime", warning.getPublishTime() != null
+                                ? warning.getPublishTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
+                                : null);
+                        item.put("status", warning.getStatus());
+                        item.put("warningContent", warning.getWarningContent());
+                    }
+
+                    Map<String, Object> todoInfo = new LinkedHashMap<>();
+                    todoInfo.put("todoId", todo.getTodoId());
+                    todoInfo.put("userName", todo.getUserName());
+                    todoInfo.put("taskName", todo.getTaskName());
+                    todoInfo.put("todoType", todo.getTodoType());
+                    todoInfo.put("createTime", todo.getCreateTime() != null
+                            ? todo.getCreateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
+                            : null);
+                    item.put("todoInfo", todoInfo);
+
+                    return item;
+                })
+                .collect(Collectors.toList());
+
+        result.put("total", list.size());
+        result.put("list", list);
+
+        log.info("今日未处置预警: 总数={}", list.size());
+        return result;
+    }
+
+    /**
+     * 将预警列表转换为详情 Map 列表
+     */
+    private List<Map<String, Object>> buildWarningDetailList(List<EarlyWarning> warnings) {
+        return warnings.stream()
+                .map(this::buildSingleWarningDetail)
+                .collect(Collectors.toList());
+    }
+
+    /**
+     * 将单个预警转换为详情 Map
+     */
+    private Map<String, Object> buildSingleWarningDetail(EarlyWarning w) {
+        Map<String, Object> item = new LinkedHashMap<>();
+        item.put("warningId", w.getWarningId());
+        item.put("warningName", w.getWarningName());
+        item.put("warningType", w.getWarningType());
+        item.put("warningLevel", w.getWarningLevel());
+        item.put("warningSpecial", w.getWarningSpecial());
+        item.put("location", w.getLocation());
+        item.put("ownershipUnit", w.getOwnershipUnit());
+        item.put("publisher", w.getPublisher());
+        item.put("publishTime", w.getPublishTime() != null
+                ? w.getPublishTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
+                : null);
+        item.put("status", w.getStatus());
+        item.put("warningContent", w.getWarningContent());
+        item.put("createTime", w.getCreateTime() != null
+                ? w.getCreateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
+                : null);
+        return item;
+    }
+}