Prechádzať zdrojové kódy

feat:排水和燃气接入工单

null 1 mesiac pred
rodič
commit
59c84769e8
36 zmenil súbory, kde vykonal 2884 pridanie a 1203 odobranie
  1. 1569 1167
      logs/pipe-network-service/spring.log
  2. 187 0
      pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/common/MinioController.java
  3. 139 0
      pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/gasbasic/BusinessWorkOrderController.java
  4. 476 9
      pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/gasbasic/DrillPracticalController.java
  5. 3 2
      pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/gasbasic/GasEquipmentController.java
  6. 3 0
      pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/gasbasic/GasRepairRecordController.java
  7. 5 0
      pipe-network-service/zksy-system/pom.xml
  8. 16 0
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/domain/DrillPracticalMonitoring.java
  9. 10 1
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/domain/DrillPracticalPlan.java
  10. 25 0
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/domain/DrillPracticalRecord.java
  11. 14 3
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/domain/DrillPracticalResult.java
  12. 8 1
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/domain/DrillPracticalTemplate.java
  13. 6 0
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/domain/EquipmentBase.java
  14. 3 0
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/domain/HazardHiddenAccount.java
  15. 3 0
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/domain/PipeRepairRecord.java
  16. 9 3
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/domain/WorkOrder.java
  17. 6 0
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/domain/vo/EquipmentFullVO.java
  18. 9 0
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/mapper/DrillPracticalMonitoringMapper.java
  19. 9 0
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/mapper/DrillPracticalRecordMapper.java
  20. 63 0
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/service/BusinessWorkOrderService.java
  21. 1 0
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/service/DrillAlarmSyncService.java
  22. 2 1
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/service/GasEquipmentService.java
  23. 229 0
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/service/impl/BusinessWorkOrderServiceImpl.java
  24. 43 5
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/service/impl/GasEquipmentServiceImpl.java
  25. 2 0
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/service/impl/ManholeBaseServiceImpl.java
  26. 2 2
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/service/impl/WorkOrderServiceImpl.java
  27. 2 0
      pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/domain/DisEventGrade.java
  28. 2 0
      pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/domain/DisEventTrack.java
  29. 1 0
      pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/domain/DisEventTransfer.java
  30. 2 0
      pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/domain/DisEventVerify.java
  31. 2 0
      pipe-network-service/zksy-system/src/main/java/com/zksy/drainage/domain/DrainagePartition.java
  32. 18 1
      pipe-network-service/zksy-system/src/main/java/com/zksy/system/service/impl/SysMenuServiceImpl.java
  33. 3 2
      pipe-network-service/zksy-system/src/main/resources/mapper/WorkOrderMapper.xml
  34. 2 2
      pipe-network-service/zksy-system/src/main/resources/mapper/system/SysDeptMapper.xml
  35. 1 1
      pipe-network-service/zksy-system/src/main/resources/mapper/system/SysUserMapper.xml
  36. 9 3
      zk-api-service/src/main/java/com/zksy/api/domain/WorkOrder.java

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 1569 - 1167
logs/pipe-network-service/spring.log


+ 187 - 0
pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/common/MinioController.java

@@ -0,0 +1,187 @@
+package com.zksy.web.controller.common;
+
+import com.zksy.common.annotation.Log;
+import com.zksy.common.core.domain.AjaxResult;
+import com.zksy.common.enums.BusinessType;
+import com.zksy.service.MinioFileStorageService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * MinIO 文件存储接口
+ * 支持上传任意类型文件(图片、文档、视频等)
+ *
+ * @author zksy
+ */
+@RestController
+@RequestMapping("/minio")
+@Api(tags = "MinIO 文件存储接口")
+public class MinioController
+{
+    private static final Logger log = LoggerFactory.getLogger(MinioController.class);
+
+    @Autowired
+    private MinioFileStorageService minioFileStorageService;
+
+    @Value("${minio.endpoint}")
+    private String readPath;
+
+    @Value("${minio.bucket}")
+    private String bucket;
+
+    /**
+     * MinIO 单文件上传
+     *
+     * @param file 文件
+     * @param dir  存储目录(可选,默认 upload)
+     * @return 上传结果,包含文件访问 URL、文件名等信息
+     */
+    @PostMapping("/upload")
+    @ApiOperation(value = "MinIO 单文件上传", notes = "支持上传任意类型文件,如图片、文档、视频等")
+    @Log(title = "MinIO 文件上传", businessType = BusinessType.INSERT)
+    public AjaxResult uploadFile(
+            @ApiParam(value = "文件", required = true) @RequestParam("file") MultipartFile file,
+            @ApiParam(value = "存储目录(可选,默认 upload)") @RequestParam(value = "dir", required = false, defaultValue = "upload") String dir)
+    {
+        try
+        {
+            if (file == null || file.isEmpty())
+            {
+                return AjaxResult.error("上传文件不能为空");
+            }
+
+            // 上传文件到 MinIO
+            String relativePath = minioFileStorageService.uploadFile(file, dir);
+            // 提取存储后的文件名
+            String fileName = relativePath.substring(relativePath.lastIndexOf("/") + 1);
+            // 构建完整 URL:endpoint/bucket/path/filename
+            String fullUrl = buildFullUrl(relativePath);
+
+            AjaxResult ajax = AjaxResult.success();
+            ajax.put("url", fullUrl);
+            ajax.put("fileName", fileName);
+            ajax.put("originalFilename", file.getOriginalFilename());
+            ajax.put("contentType", file.getContentType());
+            ajax.put("size", file.getSize());
+            return ajax;
+        }
+        catch (Exception e)
+        {
+            log.error("MinIO 文件上传失败", e);
+            return AjaxResult.error("文件上传失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * MinIO 多文件上传
+     *
+     * @param files 文件列表
+     * @param dir   存储目录(可选,默认 upload)
+     * @return 上传结果,包含每个文件的访问 URL、文件名等信息
+     */
+    @PostMapping("/uploads")
+    @ApiOperation(value = "MinIO 多文件上传", notes = "支持批量上传任意类型文件,如图片、文档、视频等")
+    @Log(title = "MinIO 多文件上传", businessType = BusinessType.INSERT)
+    public AjaxResult uploadFiles(
+            @ApiParam(value = "文件列表", required = true) @RequestParam("files") List<MultipartFile> files,
+            @ApiParam(value = "存储目录(可选,默认 upload)") @RequestParam(value = "dir", required = false, defaultValue = "upload") String dir)
+    {
+        try
+        {
+            if (files == null || files.isEmpty())
+            {
+                return AjaxResult.error("上传文件不能为空");
+            }
+
+            List<Map<String, Object>> fileList = new ArrayList<>();
+            for (MultipartFile file : files)
+            {
+                if (file.isEmpty())
+                {
+                    continue;
+                }
+
+                // 上传文件到 MinIO
+                String relativePath = minioFileStorageService.uploadFile(file, dir);
+                String fileName = relativePath.substring(relativePath.lastIndexOf("/") + 1);
+                // 构建完整 URL
+                String fullUrl = buildFullUrl(relativePath);
+
+                Map<String, Object> fileInfo = new HashMap<>();
+                fileInfo.put("url", fullUrl);
+                fileInfo.put("fileName", fileName);
+                fileInfo.put("originalFilename", file.getOriginalFilename());
+                fileInfo.put("contentType", file.getContentType());
+                fileInfo.put("size", file.getSize());
+                fileList.add(fileInfo);
+            }
+
+            AjaxResult ajax = AjaxResult.success();
+            ajax.put("files", fileList);
+            ajax.put("count", fileList.size());
+            return ajax;
+        }
+        catch (Exception e)
+        {
+            log.error("MinIO 多文件上传失败", e);
+            return AjaxResult.error("文件上传失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * MinIO 文件删除
+     *
+     * @param filePath 文件路径(上传时返回的 url)
+     * @return 删除结果
+     */
+    @DeleteMapping("/delete")
+    @ApiOperation(value = "MinIO 文件删除", notes = "根据文件路径删除 MinIO 中的文件")
+    @Log(title = "MinIO 文件删除", businessType = BusinessType.DELETE)
+    public AjaxResult deleteFile(
+            @ApiParam(value = "文件路径", required = true) @RequestParam("filePath") String filePath)
+    {
+        try
+        {
+            if (filePath == null || filePath.isEmpty())
+            {
+                return AjaxResult.error("文件路径不能为空");
+            }
+
+            minioFileStorageService.deleteFile(filePath);
+            return AjaxResult.success("删除成功");
+        }
+        catch (Exception e)
+        {
+            log.error("MinIO 文件删除失败", e);
+            return AjaxResult.error("文件删除失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 构建完整的 MinIO 文件访问 URL
+     * 
+     * @param relativePath 相对路径(如 upload/2026/07/30/file.jpg)
+     * @return 完整访问 URL(如 http://192.168.110.30:9000/bucket/upload/2026/07/30/file.jpg)
+     */
+    private String buildFullUrl(String relativePath) {
+        // 确保 readPath 以 / 结尾,relativePath 不以 / 开头
+        String baseUrl = readPath.endsWith("/") ? readPath : readPath + "/";
+        // 格式:http://endpoint/bucket/path/filename
+        if (relativePath.startsWith("/")) {
+            relativePath = relativePath.substring(1);
+        }
+        return baseUrl + bucket + "/" + relativePath;
+    }
+}

+ 139 - 0
pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/gasbasic/BusinessWorkOrderController.java

@@ -0,0 +1,139 @@
+package com.zksy.web.controller.gasbasic;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.zksy.base.domain.WorkOrder;
+import com.zksy.base.domain.WorkOrderLog;
+import com.zksy.base.service.BusinessWorkOrderService;
+import com.zksy.common.core.controller.BaseController;
+import com.zksy.common.core.domain.AjaxResult;
+import com.zksy.common.core.domain.entity.SysUser;
+import com.zksy.common.utils.SecurityUtils;
+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.*;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 业务工单管理 Controller
+ */
+@Slf4j
+@RestController
+@RequestMapping("/api/work-order")
+@Api(tags = "业务工单管理")
+public class BusinessWorkOrderController extends BaseController {
+
+    @Autowired
+    private BusinessWorkOrderService businessWorkOrderService;
+
+    @GetMapping("/list")
+    @ApiOperation("查询工单列表(equipmentType:gas-燃气 drainage-排水,为空默认燃气)")
+    public AjaxResult list(Integer orderType, Integer orderLevel, Integer orderStatus, String deviceCode, String keyword, String equipmentType, @RequestParam(defaultValue = "1") Integer pageNum, @RequestParam(defaultValue = "10") Integer pageSize) {
+
+        IPage<WorkOrder> page = businessWorkOrderService.selectWorkOrderList(orderType, orderLevel, orderStatus, deviceCode, keyword, equipmentType, pageNum, pageSize);
+
+        Map<String, Object> data = new HashMap<>();
+        data.put("records", page.getRecords());
+        data.put("total", page.getTotal());
+        data.put("pageNum", page.getCurrent());
+        data.put("pageSize", page.getSize());
+        return AjaxResult.success(data);
+    }
+
+    @GetMapping("/{orderId}")
+    @ApiOperation("查询工单详情(含操作日志)")
+    public AjaxResult detail(@PathVariable Long orderId) {
+        WorkOrder order = businessWorkOrderService.getWorkOrderDetail(orderId);
+        List<WorkOrderLog> logs = businessWorkOrderService.getWorkOrderLogs(orderId);
+
+        Map<String, Object> data = new HashMap<>();
+        data.put("order", order);
+        data.put("logs", logs);
+        return AjaxResult.success(data);
+    }
+
+    @PostMapping("/dispatch")
+    @ApiOperation("派单")
+    public AjaxResult dispatch(@RequestBody Map<String, Object> body) {
+        if (body.get("orderId") == null) {
+            return AjaxResult.error("工单ID不能为空");
+        }
+        Long orderId = Long.valueOf(body.get("orderId").toString());
+        String remarks = body.get("remarks") != null ? body.get("remarks").toString() : "";
+        Long userId = SecurityUtils.getUserId();
+        String userName = SecurityUtils.getUsername();
+
+        // 支持多人派单:优先使用 handlerIds 数组,兼容旧版 handlerId
+        if (body.get("handlerIds") != null) {
+            // 使用 List<?> 兼容 Jackson 解析出的 Integer/Long/String 类型
+            List<?> handlerIdList = (List<?>) body.get("handlerIds");
+            if (handlerIdList.isEmpty()) {
+                return AjaxResult.error("接单人不能为空");
+            }
+            List<Long> handlerIds = new ArrayList<>();
+            for (Object item : handlerIdList) {
+                if (item != null) {
+                    handlerIds.add(Long.valueOf(item.toString()));
+                }
+            }
+            businessWorkOrderService.batchDispatchOrder(orderId, handlerIds, remarks, userId, userName);
+        } else if (body.get("handlerId") != null) {
+            Long handlerId = Long.valueOf(body.get("handlerId").toString());
+            businessWorkOrderService.dispatchOrder(orderId, handlerId, remarks, userId, userName);
+        } else {
+            return AjaxResult.error("接单人不能为空");
+        }
+        return AjaxResult.success("派单成功");
+    }
+
+    @PutMapping("/verify")
+    @ApiOperation("核查")
+    public AjaxResult verify(@RequestBody Map<String, Object> body) {
+        Long orderId = Long.valueOf(body.get("orderId").toString());
+        String handleResult = body.get("handleResult") != null ? body.get("handleResult").toString() : "";
+        String solution = body.get("solution") != null ? body.get("solution").toString() : "";
+        Long userId = SecurityUtils.getUserId();
+        String userName = SecurityUtils.getUsername();
+
+        businessWorkOrderService.verifyOrder(orderId, handleResult, solution, userId, userName);
+        return AjaxResult.success("核查完成");
+    }
+
+    @PutMapping("/process")
+    @ApiOperation("处理")
+    public AjaxResult process(@RequestBody Map<String, Object> body) {
+        Long orderId = Long.valueOf(body.get("orderId").toString());
+        String handleResult = body.get("handleResult") != null ? body.get("handleResult").toString() : "";
+        String solution = body.get("solution") != null ? body.get("solution").toString() : "";
+        Long userId = SecurityUtils.getUserId();
+        String userName = SecurityUtils.getUsername();
+
+        businessWorkOrderService.processOrder(orderId, handleResult, solution, userId, userName);
+        return AjaxResult.success("处理完成");
+    }
+
+    @PutMapping("/complete")
+    @ApiOperation("完成/提交验收")
+    public AjaxResult complete(@RequestParam Long orderId, @RequestParam String result) {
+        Long userId = SecurityUtils.getUserId();
+        String userName = SecurityUtils.getUsername();
+
+        businessWorkOrderService.completeOrder(orderId, result, userId, userName);
+        return AjaxResult.success("提交验收成功");
+    }
+
+    @DeleteMapping("/{orderId}")
+    @ApiOperation("取消工单")
+    public AjaxResult cancel(@PathVariable Long orderId) {
+        Long userId = SecurityUtils.getUserId();
+        String userName = SecurityUtils.getUsername();
+
+        businessWorkOrderService.cancelOrder(orderId, userId, userName);
+        return AjaxResult.success("取消工单成功");
+    }
+}

+ 476 - 9
pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/gasbasic/DrillPracticalController.java

@@ -1,6 +1,7 @@
 package com.zksy.web.controller.gasbasic;
 
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.fasterxml.jackson.databind.ObjectMapper;
 import com.zksy.base.domain.*;
 import com.zksy.base.mapper.*;
 import com.zksy.common.annotation.Anonymous;
@@ -8,8 +9,15 @@ import com.zksy.common.core.domain.AjaxResult;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
 import org.springframework.web.bind.annotation.*;
 
+import java.nio.charset.StandardCharsets;
+import java.time.LocalDateTime;
+import java.util.*;
+
 @RestController
 @RequestMapping("/api/drill/practical")
 @Api(tags = "演练管理-实战演练")
@@ -18,16 +26,475 @@ public class DrillPracticalController {
     @Autowired private DrillPracticalTemplateMapper templateMapper;
     @Autowired private DrillPracticalPlanMapper planMapper;
     @Autowired private DrillPracticalResultMapper resultMapper;
+    @Autowired private DrillPracticalRecordMapper recordMapper;
+    @Autowired private DrillPracticalMonitoringMapper monitoringMapper;
+    @Autowired private ObjectMapper objectMapper;
+
+    // ============================================================
+    // 一、演练模板 CRUD
+    // ============================================================
+    @GetMapping("/template")
+    @Anonymous
+    public AjaxResult templates() {
+        return AjaxResult.success(templateMapper.selectList(
+            new LambdaQueryWrapper<DrillPracticalTemplate>()
+                .orderByDesc(DrillPracticalTemplate::getCreateTime)));
+    }
+
+    @PostMapping("/template")
+    @Anonymous
+    public AjaxResult saveTemplate(@RequestBody DrillPracticalTemplate t) {
+        t.setCreateTime(LocalDateTime.now());
+        templateMapper.insert(t);
+        return AjaxResult.success(t.getId());
+    }
+
+    @PutMapping("/template")
+    @Anonymous
+    public AjaxResult updateTemplate(@RequestBody DrillPracticalTemplate t) {
+        templateMapper.updateById(t);
+        return AjaxResult.success();
+    }
+
+    @DeleteMapping("/template/{id}")
+    @Anonymous
+    public AjaxResult deleteTemplate(@PathVariable Long id) {
+        templateMapper.deleteById(id);
+        return AjaxResult.success();
+    }
+
+    // ============================================================
+    // 二、演练计划 CRUD
+    // ============================================================
+    @GetMapping("/plan")
+    @Anonymous
+    public AjaxResult plans() {
+        return AjaxResult.success(planMapper.selectList(
+            new LambdaQueryWrapper<DrillPracticalPlan>()
+                .orderByDesc(DrillPracticalPlan::getCreateTime)));
+    }
+
+    @PostMapping("/plan")
+    @Anonymous
+    public AjaxResult savePlan(@RequestBody DrillPracticalPlan p) {
+        p.setCreateTime(LocalDateTime.now());
+        p.setUpdateTime(LocalDateTime.now());
+        if (p.getStatus() == null) p.setStatus("计划中");
+        if (p.getProgress() == null) p.setProgress(0);
+        if (p.getParticipantCount() == null) p.setParticipantCount(0);
+        planMapper.insert(p);
+        return AjaxResult.success(p.getId());
+    }
+
+    @PutMapping("/plan")
+    @Anonymous
+    public AjaxResult updatePlan(@RequestBody DrillPracticalPlan p) {
+        p.setUpdateTime(LocalDateTime.now());
+        planMapper.updateById(p);
+        return AjaxResult.success();
+    }
+
+    @DeleteMapping("/plan/{id}")
+    @Anonymous
+    public AjaxResult deletePlan(@PathVariable Long id) {
+        planMapper.deleteById(id);
+        // 同时删除关联的演练记录和监测数据
+        List<DrillPracticalRecord> records = recordMapper.selectList(
+            new LambdaQueryWrapper<DrillPracticalRecord>()
+                .eq(DrillPracticalRecord::getPlanId, id));
+        for (DrillPracticalRecord r : records) {
+            monitoringMapper.delete(new LambdaQueryWrapper<DrillPracticalMonitoring>()
+                .eq(DrillPracticalMonitoring::getRecordId, r.getId()));
+        }
+        recordMapper.delete(new LambdaQueryWrapper<DrillPracticalRecord>()
+            .eq(DrillPracticalRecord::getPlanId, id));
+        return AjaxResult.success();
+    }
+
+    // ============================================================
+    // 三、演练计划状态更新
+    // ============================================================
+    @PutMapping("/plan/status")
+    @Anonymous
+    @ApiOperation("更新演练计划状态")
+    public AjaxResult updatePlanStatus(@RequestParam Long planId, @RequestParam String status) {
+        DrillPracticalPlan plan = planMapper.selectById(planId);
+        if (plan != null) {
+            plan.setStatus(status);
+            plan.setUpdateTime(LocalDateTime.now());
+            if ("已完成".equals(status)) {
+                plan.setProgress(100);
+            } else if ("进行中".equals(status) && (plan.getProgress() == null || plan.getProgress() == 0)) {
+                plan.setProgress(10);
+            }
+            planMapper.updateById(plan);
+        }
+        return AjaxResult.success();
+    }
+
+    // ============================================================
+    // 四、演练记录 CRUD
+    // ============================================================
+    @GetMapping("/record/{planId}")
+    @Anonymous
+    @ApiOperation("按计划ID获取演练记录")
+    public AjaxResult getRecord(@PathVariable Long planId) {
+        DrillPracticalRecord record = recordMapper.selectOne(
+            new LambdaQueryWrapper<DrillPracticalRecord>()
+                .eq(DrillPracticalRecord::getPlanId, planId)
+                .orderByDesc(DrillPracticalRecord::getCreateTime)
+                .last("LIMIT 1"));
+        if (record == null) {
+            return AjaxResult.success(null);
+        }
+        // 将 JSON 字符串字段解析为对象后返回
+        Map<String, Object> result = new LinkedHashMap<>();
+        result.put("id", record.getId());
+        result.put("planId", record.getPlanId());
+        result.put("drillProjectName", record.getDrillProjectName());
+        result.put("recordTime", record.getRecordTime());
+        result.put("drillType", record.getDrillType());
+        result.put("participatingDept", record.getParticipatingDept());
+        result.put("responsiblePerson", record.getResponsiblePerson());
+        result.put("status", record.getStatus());
+        result.put("reportTime", record.getReportTime());
+        result.put("reportMethod", record.getReportMethod());
+        result.put("reporter", record.getReporter());
+        result.put("contactPhone", record.getContactPhone());
+        result.put("eventLocation", record.getEventLocation());
+        result.put("eventDescription", record.getEventDescription());
+        result.put("releaseContent", record.getReleaseContent());
+        result.put("dispatchInfo", parseJson(record.getDispatchInfo()));
+        result.put("decisionInfo", parseJson(record.getDecisionInfo()));
+        result.put("releaseChannels", parseJsonArray(record.getReleaseChannels()));
+        result.put("actions", parseJsonArray(record.getActions()));
+        return AjaxResult.success(result);
+    }
+
+    @PostMapping("/record")
+    @Anonymous
+    @ApiOperation("新建演练记录")
+    public AjaxResult saveRecord(@RequestBody Map<String, Object> body) {
+        DrillPracticalRecord record = new DrillPracticalRecord();
+        mapToRecord(body, record);
+        record.setCreateTime(LocalDateTime.now());
+        record.setUpdateTime(LocalDateTime.now());
+        recordMapper.insert(record);
+        return AjaxResult.success(record.getId());
+    }
+
+    @PutMapping("/record")
+    @Anonymous
+    @ApiOperation("更新演练记录")
+    public AjaxResult updateRecord(@RequestBody Map<String, Object> body) {
+        Long id = toLong(body.get("id"));
+        if (id == null) return AjaxResult.error("记录ID不能为空");
+        DrillPracticalRecord record = recordMapper.selectById(id);
+        if (record == null) return AjaxResult.error("记录不存在");
+        mapToRecord(body, record);
+        record.setUpdateTime(LocalDateTime.now());
+        recordMapper.updateById(record);
+        return AjaxResult.success();
+    }
+
+    // ============================================================
+    // 五、监测数据 CRUD
+    // ============================================================
+    @GetMapping("/monitoring/{recordId}")
+    @Anonymous
+    @ApiOperation("按记录ID获取监测数据列表")
+    public AjaxResult monitoring(@PathVariable Long recordId) {
+        return AjaxResult.success(monitoringMapper.selectList(
+            new LambdaQueryWrapper<DrillPracticalMonitoring>()
+                .eq(DrillPracticalMonitoring::getRecordId, recordId)
+                .orderByDesc(DrillPracticalMonitoring::getCreateTime)));
+    }
+
+    @PostMapping("/monitoring")
+    @Anonymous
+    @ApiOperation("保存/更新监测数据")
+    public AjaxResult saveMonitoring(@RequestBody DrillPracticalMonitoring m) {
+        if (m.getId() != null) {
+            monitoringMapper.updateById(m);
+        } else {
+            m.setCreateTime(LocalDateTime.now());
+            monitoringMapper.insert(m);
+        }
+        return AjaxResult.success(m.getId());
+    }
+
+    @DeleteMapping("/monitoring/{id}")
+    @Anonymous
+    @ApiOperation("删除监测数据")
+    public AjaxResult deleteMonitoring(@PathVariable Long id) {
+        monitoringMapper.deleteById(id);
+        return AjaxResult.success();
+    }
+
+    // ============================================================
+    // 六、分析评估
+    // ============================================================
+    @GetMapping("/analysis/{planId}")
+    @Anonymous
+    @ApiOperation("获取演练分析结果")
+    public AjaxResult getAnalysis(@PathVariable Long planId) {
+        DrillPracticalResult result = resultMapper.selectOne(
+            new LambdaQueryWrapper<DrillPracticalResult>()
+                .eq(DrillPracticalResult::getPlanId, planId)
+                .orderByDesc(DrillPracticalResult::getCreateTime)
+                .last("LIMIT 1"));
+        if (result == null) {
+            return AjaxResult.success(null);
+        }
+        Map<String, Object> data = new LinkedHashMap<>();
+        data.put("id", result.getId());
+        data.put("planId", result.getPlanId());
+        data.put("skillScore", result.getSkillScore());
+        data.put("equipmentScore", result.getEquipmentScore());
+        data.put("responseScore", result.getResponseScore());
+        data.put("cooperationScore", result.getCooperationScore());
+        data.put("totalTasks", result.getTotalTasks());
+        data.put("completedTasks", result.getCompletedTasks());
+        data.put("completionRate", result.getCompletionRate());
+        data.put("overtimeTasks", result.getOvertimeTasks());
+        data.put("overallLevel", result.getOverallLevel());
+        data.put("evalConclusion", result.getEvalConclusion());
+        data.put("improvementSuggestions", parseJsonArray(result.getImprovementSuggestions()));
+        data.put("analyzeTime", result.getEvalTime() != null
+            ? result.getEvalTime().toString() : null);
+        return AjaxResult.success(data);
+    }
+
+    @PostMapping("/analysis")
+    @Anonymous
+    @ApiOperation("发送演练数据至分析评估(生成分析结果)")
+    public AjaxResult sendAnalysis(@RequestBody Map<String, Object> body) {
+        Long planId = toLong(body.get("planId"));
+        if (planId == null) return AjaxResult.error("计划ID不能为空");
+
+        // 先保存/更新演练记录
+        DrillPracticalRecord record = recordMapper.selectOne(
+            new LambdaQueryWrapper<DrillPracticalRecord>()
+                .eq(DrillPracticalRecord::getPlanId, planId)
+                .orderByDesc(DrillPracticalRecord::getCreateTime)
+                .last("LIMIT 1"));
+        boolean isNew = (record == null);
+        if (isNew) record = new DrillPracticalRecord();
+        mapToRecord(body, record);
+        if (isNew) {
+            record.setCreateTime(LocalDateTime.now());
+            recordMapper.insert(record);
+        } else {
+            record.setUpdateTime(LocalDateTime.now());
+            recordMapper.updateById(record);
+        }
+
+        // 基于演练数据生成分析结果
+        List<?> monitoringInfo = (List<?>) body.get("monitoringInfo");
+        int monCount = monitoringInfo != null ? monitoringInfo.size() : 0;
+
+        Object dispatchInfo = body.get("dispatchInfo");
+        Object decisionInfo = body.get("decisionInfo");
+        Object releaseChannels = body.get("releaseChannels");
+        String releaseContent = (String) body.get("releaseContent");
+        List<?> actions = (List<?>) body.get("actions");
+
+        // 查找已有结果记录
+        DrillPracticalResult result = resultMapper.selectOne(
+            new LambdaQueryWrapper<DrillPracticalResult>()
+                .eq(DrillPracticalResult::getPlanId, planId)
+                .orderByDesc(DrillPracticalResult::getCreateTime)
+                .last("LIMIT 1"));
+        boolean newResult = (result == null);
+        if (newResult) result = new DrillPracticalResult();
+        result.setPlanId(planId);
+
+        // 生成评分(基于数据完整度)
+        int score = 60;
+        if (monCount > 0) score += Math.min(15, monCount * 3);
+        if (dispatchInfo != null) score += 8;
+        if (decisionInfo != null) score += 8;
+        if (releaseChannels != null && releaseContent != null && !releaseContent.isEmpty()) score += 5;
+        if (score > 98) score = 98;
+
+        result.setSkillScore(score);
+        result.setEquipmentScore(Math.max(60, score - 7));
+        result.setResponseScore(Math.max(65, score - 3));
+        result.setCooperationScore(Math.max(62, score - 5));
+        result.setTotalTasks(5 + monCount);
+        result.setCompletedTasks(dispatchInfo != null && decisionInfo != null ? 5 + monCount : 3 + monCount);
+        int compRate = result.getTotalTasks() > 0
+            ? (result.getCompletedTasks() * 100 / result.getTotalTasks()) : 0;
+        result.setCompletionRate(compRate);
+        result.setOvertimeTasks(Math.max(0, monCount / 5));
+        result.setOverallLevel(score >= 90 ? "优秀" : score >= 80 ? "良好" : score >= 70 ? "合格" : "待改进");
+        result.setEvalConclusion(
+            "本次演练整体表现" + result.getOverallLevel() + ","
+            + "共录入监测数据" + monCount + "条,"
+            + (dispatchInfo != null ? "指挥调度信息已录入," : "指挥调度信息缺失,")
+            + (decisionInfo != null ? "决策信息已录入。" : "决策信息缺失。")
+            + "任务完成率" + compRate + "%。"
+        );
+        List<String> suggestions = new ArrayList<>();
+        if (monCount < 3) suggestions.add("增加监测数据采集密度,提高演练覆盖范围");
+        if (dispatchInfo == null) suggestions.add("完善指挥调度信息记录");
+        if (decisionInfo == null) suggestions.add("加强决策信息录入规范");
+        if (compRate < 80) suggestions.add("提高任务完成率,减少超时任务");
+        suggestions.add("加强多部门协同配合训练");
+        suggestions.add("定期复盘演练过程,持续优化应急预案");
+        try {
+            result.setImprovementSuggestions(objectMapper.writeValueAsString(suggestions));
+        } catch (Exception e) {
+            result.setImprovementSuggestions("[]");
+        }
+        result.setEvalTime(LocalDateTime.now());
+        if (newResult) {
+            result.setCreateTime(LocalDateTime.now());
+            resultMapper.insert(result);
+        } else {
+            resultMapper.updateById(result);
+        }
+        return AjaxResult.success("分析数据已发送,评估结果已生成");
+    }
+
+    // ============================================================
+    // 七、导出分析报告
+    // ============================================================
+    @GetMapping("/report/{planId}")
+    @Anonymous
+    @ApiOperation("导出演练分析报告")
+    public ResponseEntity<byte[]> exportReport(@PathVariable Long planId) {
+        DrillPracticalPlan plan = planMapper.selectById(planId);
+        DrillPracticalResult result = resultMapper.selectOne(
+            new LambdaQueryWrapper<DrillPracticalResult>()
+                .eq(DrillPracticalResult::getPlanId, planId)
+                .orderByDesc(DrillPracticalResult::getCreateTime)
+                .last("LIMIT 1"));
+        DrillPracticalRecord record = recordMapper.selectOne(
+            new LambdaQueryWrapper<DrillPracticalRecord>()
+                .eq(DrillPracticalRecord::getPlanId, planId)
+                .orderByDesc(DrillPracticalRecord::getCreateTime)
+                .last("LIMIT 1"));
+        List<DrillPracticalMonitoring> monList = new ArrayList<>();
+        if (record != null) {
+            monList = monitoringMapper.selectList(
+                new LambdaQueryWrapper<DrillPracticalMonitoring>()
+                    .eq(DrillPracticalMonitoring::getRecordId, record.getId()));
+        }
+
+        StringBuilder sb = new StringBuilder();
+        sb.append("=".repeat(50)).append("\n");
+        sb.append("           实战演练分析报告\n");
+        sb.append("=".repeat(50)).append("\n\n");
+        sb.append("演练名称:").append(plan != null ? plan.getName() : "—").append("\n");
+        sb.append("演练类型:实战演练\n");
+        sb.append("所属系统:").append(plan != null ? plan.getCategory() : "—").append("\n");
+        if (plan != null && plan.getEndTime() != null) {
+            sb.append("完成时间:").append(plan.getEndTime()).append("\n");
+        }
+        sb.append("\n--- 演练记录 ---\n");
+        if (record != null) {
+            sb.append("记录时间:").append(record.getRecordTime()).append("\n");
+            sb.append("参演部门:").append(record.getParticipatingDept()).append("\n");
+            sb.append("演练状态:").append(record.getStatus()).append("\n");
+            sb.append("事件描述:").append(record.getEventDescription()).append("\n");
+        } else {
+            sb.append("(暂无演练记录)\n");
+        }
+        sb.append("\n--- 监测数据 (").append(monList.size()).append("条) ---\n");
+        for (DrillPracticalMonitoring m : monList) {
+            sb.append("  - ").append(m.getSensorType())
+              .append(" | ").append(m.getLocation())
+              .append(" | 值: ").append(m.getValue()).append(m.getUnit())
+              .append(" | 等级: ").append(m.getAlarmLevel())
+              .append(" | 时间: ").append(m.getTime()).append("\n");
+        }
+        sb.append("\n--- 分析评估 ---\n");
+        if (result != null) {
+            sb.append("综合等级:").append(result.getOverallLevel()).append("\n");
+            sb.append("技能掌握:").append(result.getSkillScore()).append("/100\n");
+            sb.append("装备使用:").append(result.getEquipmentScore()).append("/100\n");
+            sb.append("响应时效:").append(result.getResponseScore()).append("/100\n");
+            sb.append("协同配合:").append(result.getCooperationScore()).append("/100\n");
+            sb.append("任务总数:").append(result.getTotalTasks()).append("\n");
+            sb.append("完成任务:").append(result.getCompletedTasks()).append("\n");
+            sb.append("完成率:").append(result.getCompletionRate()).append("%\n");
+            sb.append("超时任务:").append(result.getOvertimeTasks()).append("\n");
+            sb.append("\n评估结论:").append(result.getEvalConclusion()).append("\n");
+        } else {
+            sb.append("(暂无分析数据)\n");
+        }
+        sb.append("\n").append("=".repeat(50)).append("\n");
+        sb.append("报告生成时间:").append(LocalDateTime.now()).append("\n");
+
+        byte[] bytes = sb.toString().getBytes(StandardCharsets.UTF_8);
+        HttpHeaders headers = new HttpHeaders();
+        headers.setContentType(MediaType.TEXT_PLAIN);
+        headers.set("Content-Disposition",
+            "attachment; filename=drill_report_" + planId + ".txt");
+        return ResponseEntity.ok().headers(headers).body(bytes);
+    }
+
+    // ============================================================
+    // 八、原有结果查询(保留,兼容旧前端)
+    // ============================================================
+    @GetMapping("/result/{planId}")
+    @Anonymous
+    public AjaxResult result(@PathVariable Long planId) {
+        return AjaxResult.success(resultMapper.selectOne(
+            new LambdaQueryWrapper<DrillPracticalResult>()
+                .eq(DrillPracticalResult::getPlanId, planId)
+                .orderByDesc(DrillPracticalResult::getCreateTime)
+                .last("LIMIT 1")));
+    }
+
+    // ============================================================
+    // 私有工具方法
+    // ============================================================
+    private void mapToRecord(Map<String, Object> body, DrillPracticalRecord r) {
+        r.setPlanId(toLong(body.get("planId")));
+        r.setDrillProjectName((String) body.get("drillProjectName"));
+        r.setRecordTime((String) body.get("recordTime"));
+        r.setDrillType((String) body.get("drillType"));
+        r.setParticipatingDept((String) body.get("participatingDept"));
+        r.setResponsiblePerson((String) body.get("responsiblePerson"));
+        r.setStatus((String) body.get("status"));
+        r.setReportTime((String) body.get("reportTime"));
+        r.setReportMethod((String) body.get("reportMethod"));
+        r.setReporter((String) body.get("reporter"));
+        r.setContactPhone((String) body.get("contactPhone"));
+        r.setEventLocation((String) body.get("eventLocation"));
+        r.setEventDescription((String) body.get("eventDescription"));
+        r.setDispatchInfo(toJsonStr(body.get("dispatchInfo")));
+        r.setDecisionInfo(toJsonStr(body.get("decisionInfo")));
+        r.setReleaseChannels(toJsonStr(body.get("releaseChannels")));
+        r.setReleaseContent((String) body.get("releaseContent"));
+        r.setActions(toJsonStr(body.get("actions")));
+    }
+
+    private Object parseJson(String json) {
+        if (json == null || json.isEmpty()) return null;
+        try { return objectMapper.readValue(json, Object.class); }
+        catch (Exception e) { return null; }
+    }
 
-    @GetMapping("/template") @Anonymous public AjaxResult templates() { return AjaxResult.success(templateMapper.selectList(new LambdaQueryWrapper<DrillPracticalTemplate>().orderByDesc(DrillPracticalTemplate::getCreateTime))); }
-    @PostMapping("/template") @Anonymous public AjaxResult saveTemplate(@RequestBody DrillPracticalTemplate t) { templateMapper.insert(t); return AjaxResult.success(t.getId()); }
-    @PutMapping("/template") @Anonymous public AjaxResult updateTemplate(@RequestBody DrillPracticalTemplate t) { templateMapper.updateById(t); return AjaxResult.success(); }
-    @DeleteMapping("/template/{id}") @Anonymous public AjaxResult deleteTemplate(@PathVariable Long id) { templateMapper.deleteById(id); return AjaxResult.success(); }
+    private List<?> parseJsonArray(String json) {
+        if (json == null || json.isEmpty()) return null;
+        try { return objectMapper.readValue(json, List.class); }
+        catch (Exception e) { return null; }
+    }
 
-    @GetMapping("/plan") @Anonymous public AjaxResult plans() { return AjaxResult.success(planMapper.selectList(new LambdaQueryWrapper<DrillPracticalPlan>().orderByDesc(DrillPracticalPlan::getCreateTime))); }
-    @PostMapping("/plan") @Anonymous public AjaxResult savePlan(@RequestBody DrillPracticalPlan p) { planMapper.insert(p); return AjaxResult.success(p.getId()); }
-    @PutMapping("/plan") @Anonymous public AjaxResult updatePlan(@RequestBody DrillPracticalPlan p) { planMapper.updateById(p); return AjaxResult.success(); }
-    @DeleteMapping("/plan/{id}") @Anonymous public AjaxResult deletePlan(@PathVariable Long id) { planMapper.deleteById(id); return AjaxResult.success(); }
+    private String toJsonStr(Object obj) {
+        if (obj == null) return null;
+        if (obj instanceof String) return (String) obj;
+        try { return objectMapper.writeValueAsString(obj); }
+        catch (Exception e) { return null; }
+    }
 
-    @GetMapping("/result/{planId}") @Anonymous public AjaxResult result(@PathVariable Long planId) { return AjaxResult.success(resultMapper.selectOne(new LambdaQueryWrapper<DrillPracticalResult>().eq(DrillPracticalResult::getPlanId, planId))); }
+    private Long toLong(Object val) {
+        if (val == null) return null;
+        if (val instanceof Number) return ((Number) val).longValue();
+        try { return Long.parseLong(val.toString()); }
+        catch (Exception e) { return null; }
+    }
 }

+ 3 - 2
pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/gasbasic/GasEquipmentController.java

@@ -184,9 +184,10 @@ public class GasEquipmentController {
                                      @RequestParam(required = false) String equipmentCode,
                                      @RequestParam(required = false) String equipmentName,
                                      @RequestParam(required = false) String equipmentModel,
-                                     @RequestParam(required = false) String manufacturer) {
+                                     @RequestParam(required = false) String manufacturer,
+                                     @RequestParam(required = false) String district) {
         Page<EquipmentFullVO> page = gasEquipmentService.findFullByPage(pageNum, pageSize,
-                equipmentCode, equipmentName, equipmentModel, manufacturer);
+                equipmentCode, equipmentName, equipmentModel, manufacturer, district);
         return AjaxResult.success(page);
     }
 }

+ 3 - 0
pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/gasbasic/GasRepairRecordController.java

@@ -14,6 +14,8 @@ import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 
+import java.time.LocalDateTime;
+
 @Slf4j
 @RestController
 @RequestMapping("/api/repair-record")
@@ -101,6 +103,7 @@ public class GasRepairRecordController {
     @ApiOperation("新增维修记录")
     @Log(title = "新增维修记录", businessType = BusinessType.INSERT)
     public AjaxResult save(@RequestBody PipeRepairRecord entity) {
+        entity.setCreateTime(LocalDateTime.now());
         boolean saved = service.save(entity);
         return saved ? AjaxResult.success("新增成功", saved) : AjaxResult.error("新增失败");
     }

+ 5 - 0
pipe-network-service/zksy-system/pom.xml

@@ -30,6 +30,11 @@
             <groupId>com.baomidou</groupId>
             <artifactId>mybatis-plus-boot-starter</artifactId>
         </dependency>
+        <dependency>
+            <groupId>cn.com.kingbase</groupId>
+            <artifactId>kingbase8</artifactId>
+            <version>9.0.0</version> <!-- 根据你的JDK版本选择合适的版本 -->
+        </dependency>
         <dependency>
             <groupId>com.github.xiaoymin</groupId>
             <artifactId>knife4j-openapi2-spring-boot-starter</artifactId>

+ 16 - 0
pipe-network-service/zksy-system/src/main/java/com/zksy/base/domain/DrillPracticalMonitoring.java

@@ -0,0 +1,16 @@
+package com.zksy.base.domain;
+import com.baomidou.mybatisplus.annotation.*;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import java.io.Serializable; import java.time.*;
+
+@Data @TableName(value = "drill_practical_monitoring", schema = "app_user")
+public class DrillPracticalMonitoring implements Serializable {
+    @TableId(type=IdType.AUTO) private Long id;
+    private Long recordId;
+    private String sensorType; private String location;
+    private String value; private String unit; private String alarmLevel;
+    private String time;
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private LocalDateTime createTime;
+}

+ 10 - 1
pipe-network-service/zksy-system/src/main/java/com/zksy/base/domain/DrillPracticalPlan.java

@@ -1,11 +1,20 @@
 package com.zksy.base.domain;
 import com.baomidou.mybatisplus.annotation.*;
+import com.fasterxml.jackson.annotation.JsonFormat;
 import lombok.Data;
 import java.io.Serializable; import java.time.*;
+
 @Data @TableName(value = "drill_practical_plan", schema = "app_user")
 public class DrillPracticalPlan implements Serializable {
     @TableId(type=IdType.AUTO) private Long id;
     private String name; private String category; private String target; private String project;
     private String eventDesc; private String personnel; private String status;
-    private LocalDateTime createTime; private LocalDateTime updateTime;
+    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+    private LocalDate endTime;
+    private Integer participantCount;
+    private Integer progress;
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private LocalDateTime createTime;
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private LocalDateTime updateTime;
 }

+ 25 - 0
pipe-network-service/zksy-system/src/main/java/com/zksy/base/domain/DrillPracticalRecord.java

@@ -0,0 +1,25 @@
+package com.zksy.base.domain;
+import com.baomidou.mybatisplus.annotation.*;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import java.io.Serializable; import java.time.*;
+
+@Data @TableName(value = "drill_practical_record", schema = "app_user")
+public class DrillPracticalRecord implements Serializable {
+    @TableId(type=IdType.AUTO) private Long id;
+    private Long planId;
+    private String drillProjectName; private String recordTime;
+    private String drillType; private String participatingDept; private String responsiblePerson;
+    private String status;
+    private String reportTime; private String reportMethod; private String reporter;
+    private String contactPhone; private String eventLocation; private String eventDescription;
+    private String dispatchInfo;    // JSON 字符串
+    private String decisionInfo;   // JSON 字符串
+    private String releaseChannels; // JSON 数组字符串
+    private String releaseContent;
+    private String actions;        // JSON 数组字符串
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private LocalDateTime createTime;
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private LocalDateTime updateTime;
+}

+ 14 - 3
pipe-network-service/zksy-system/src/main/java/com/zksy/base/domain/DrillPracticalResult.java

@@ -1,10 +1,21 @@
 package com.zksy.base.domain;
 import com.baomidou.mybatisplus.annotation.*;
+import com.fasterxml.jackson.annotation.JsonFormat;
 import lombok.Data;
 import java.io.Serializable; import java.time.*;
+
 @Data @TableName(value = "drill_practical_result", schema = "app_user")
 public class DrillPracticalResult implements Serializable {
-    @TableId(type=IdType.AUTO) private Long id; private Long planId;
-    private String taskCompletionRate; private String responseTimeScore; private String collaborationScore;
-    private String overallGrade; private String suggestion; private LocalDateTime evalTime; private LocalDateTime createTime;
+    @TableId(type=IdType.AUTO) private Long id;
+    private Long planId;
+    private Integer skillScore; private Integer equipmentScore;
+    private Integer responseScore; private Integer cooperationScore;
+    private Integer totalTasks; private Integer completedTasks;
+    private Integer completionRate; private Integer overtimeTasks;
+    private String overallLevel; private String evalConclusion;
+    private String improvementSuggestions; // JSON 数组字符串
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private LocalDateTime evalTime;
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private LocalDateTime createTime;
 }

+ 8 - 1
pipe-network-service/zksy-system/src/main/java/com/zksy/base/domain/DrillPracticalTemplate.java

@@ -1,10 +1,17 @@
 package com.zksy.base.domain;
 import com.baomidou.mybatisplus.annotation.*;
+import com.fasterxml.jackson.annotation.JsonFormat;
 import lombok.Data;
 import java.io.Serializable; import java.time.*;
+
 @Data @TableName(value = "drill_practical_template", schema = "app_user")
 public class DrillPracticalTemplate implements Serializable {
     @TableId(type=IdType.AUTO) private Long id;
-    private String name; private String category; private String content;
+    private String name; private String category;
+    private String target; private String project; private String eventDesc;
+    private String personnel;
+    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+    private LocalDate dueDate;
+    private String content;
     private LocalDate createDate; private LocalDateTime createTime;
 }

+ 6 - 0
pipe-network-service/zksy-system/src/main/java/com/zksy/base/domain/EquipmentBase.java

@@ -41,6 +41,9 @@ public class EquipmentBase {
     @ApiModelProperty("设备制造商")
     private String manufacturer;
 
+    @ApiModelProperty("所属片区(城南/城北/太常)")
+    private String district;
+
     @ApiModelProperty("设备生产日期")
     @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
     private Date productionDate;
@@ -79,6 +82,9 @@ public class EquipmentBase {
     @ApiModelProperty("备注信息")
     private String remark;
 
+    @ApiModelProperty("设备图片URL")
+    private String equipmentImage;
+
     @ApiModelProperty("创建时间")
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
     private LocalDateTime createTime;

+ 3 - 0
pipe-network-service/zksy-system/src/main/java/com/zksy/base/domain/HazardHiddenAccount.java

@@ -105,4 +105,7 @@ public class HazardHiddenAccount {
     @TableLogic(value = "0", delval = "2")
     @ApiModelProperty("删除标志(0=正常 2=删除)")
     private String delFlag;
+
+    @ApiModelProperty("隐患图片")
+    private String hazardImages;
 }

+ 3 - 0
pipe-network-service/zksy-system/src/main/java/com/zksy/base/domain/PipeRepairRecord.java

@@ -52,6 +52,9 @@ public class PipeRepairRecord {
     @ApiModelProperty("备注")
     private String remark;
 
+    @ApiModelProperty("维修图片URL(多图逗号分隔)")
+    private String repairImages;
+
     @ApiModelProperty("创建时间")
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
     private LocalDateTime createTime;

+ 9 - 3
pipe-network-service/zksy-system/src/main/java/com/zksy/base/domain/WorkOrder.java

@@ -105,10 +105,10 @@ public class WorkOrder implements Serializable {
     private Date dispatchTime;
 
     /**
-     * 接单运维人员ID
+     * 接单运维人员ID(多个用逗号分隔)
      */
-    @ApiModelProperty(value = "接单运维人员ID")
-    private Long receiveUser;
+    @ApiModelProperty(value = "接单运维人员ID,多个用逗号分隔")
+    private String receiveUser;
 
     /**
      * 工单状态:1-待派单 2-待接单 3-已接单 4-处理中 5-待验收 6-已结案 7-已驳回 8-延期审核 9-已延期
@@ -149,6 +149,12 @@ public class WorkOrder implements Serializable {
     @ApiModelProperty(value = "维修处置结果")
     private String repairResult;
 
+    /**
+     * 工单图片(多个图片用逗号分隔)
+     */
+    @ApiModelProperty(value = "工单图片,多个用逗号分隔")
+    private String workImage;
+
     /**
      * 验收人ID
      */

+ 6 - 0
pipe-network-service/zksy-system/src/main/java/com/zksy/base/domain/vo/EquipmentFullVO.java

@@ -30,6 +30,9 @@ public class EquipmentFullVO {
     @ApiModelProperty("规格参数")
     private String equipmentSpec;
 
+    @ApiModelProperty("所属片区(城南/城北/太常)")
+    private String district;
+
     @ApiModelProperty("制造商")
     private String manufacturer;
 
@@ -71,6 +74,9 @@ public class EquipmentFullVO {
     @ApiModelProperty("备注")
     private String remark;
 
+    @ApiModelProperty("设备图片URL")
+    private String equipmentImage;
+
     @ApiModelProperty("权属单位")
     private String ownershipUnit;
 

+ 9 - 0
pipe-network-service/zksy-system/src/main/java/com/zksy/base/mapper/DrillPracticalMonitoringMapper.java

@@ -0,0 +1,9 @@
+package com.zksy.base.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.zksy.base.domain.DrillPracticalMonitoring;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface DrillPracticalMonitoringMapper extends BaseMapper<DrillPracticalMonitoring> {
+}

+ 9 - 0
pipe-network-service/zksy-system/src/main/java/com/zksy/base/mapper/DrillPracticalRecordMapper.java

@@ -0,0 +1,9 @@
+package com.zksy.base.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.zksy.base.domain.DrillPracticalRecord;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface DrillPracticalRecordMapper extends BaseMapper<DrillPracticalRecord> {
+}

+ 63 - 0
pipe-network-service/zksy-system/src/main/java/com/zksy/base/service/BusinessWorkOrderService.java

@@ -0,0 +1,63 @@
+package com.zksy.base.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.zksy.base.domain.WorkOrder;
+import com.zksy.base.domain.WorkOrderLog;
+
+import java.util.List;
+
+/**
+ * 业务工单管理 Service(独立于已有 WorkOrderService)
+ */
+public interface BusinessWorkOrderService extends IService<WorkOrder> {
+
+    /**
+     * 分页查询工单列表(支持多条件筛选+排序)
+     * @param equipmentType 设备类型:gas-燃气 drainage-排水,为空查询全部
+     */
+    IPage<WorkOrder> selectWorkOrderList(Integer orderType, Integer orderLevel,
+                                         Integer orderStatus, String deviceCode,
+                                         String keyword, String equipmentType,
+                                         Integer pageNum, Integer pageSize);
+
+    /**
+     * 查询工单详情
+     */
+    WorkOrder getWorkOrderDetail(Long orderId);
+
+    /**
+     * 查询工单操作日志列表
+     */
+    List<WorkOrderLog> getWorkOrderLogs(Long orderId);
+
+    /**
+     * 派单
+     */
+    boolean dispatchOrder(Long orderId, Long handlerId, String remarks, Long operUserId, String operUserName);
+
+    /**
+     * 批量派单(支持多人)
+     */
+    boolean batchDispatchOrder(Long orderId, List<Long> handlerIds, String remarks, Long operUserId, String operUserName);
+
+    /**
+     * 核查
+     */
+    boolean verifyOrder(Long orderId, String handleResult, String solution, Long operUserId, String operUserName);
+
+    /**
+     * 处理
+     */
+    boolean processOrder(Long orderId, String handleResult, String solution, Long operUserId, String operUserName);
+
+    /**
+     * 完成/提交验收
+     */
+    boolean completeOrder(Long orderId, String result, Long operUserId, String operUserName);
+
+    /**
+     * 取消工单
+     */
+    boolean cancelOrder(Long orderId, Long operUserId, String operUserName);
+}

+ 1 - 0
pipe-network-service/zksy-system/src/main/java/com/zksy/base/service/DrillAlarmSyncService.java

@@ -75,6 +75,7 @@ public class DrillAlarmSyncService {
         workOrder.setOrderType(1);
         workOrder.setOrderLevel(orderLevel);
         workOrder.setOrderDesc(desc);
+        workOrder.setCreateTime(DateUtil.date());
         workOrder.setOrderStatus(1);
         if (caseInfo != null) {
             workOrder.setCaseId(caseInfo.getCaseId());

+ 2 - 1
pipe-network-service/zksy-system/src/main/java/com/zksy/base/service/GasEquipmentService.java

@@ -14,5 +14,6 @@ public interface GasEquipmentService {
 
     Page<EquipmentFullVO> findFullByPage(long pageNum, long pageSize,
                                          String equipmentCode, String equipmentName,
-                                         String equipmentModel, String manufacturer);
+                                         String equipmentModel, String manufacturer,
+                                         String district);
 }

+ 229 - 0
pipe-network-service/zksy-system/src/main/java/com/zksy/base/service/impl/BusinessWorkOrderServiceImpl.java

@@ -0,0 +1,229 @@
+package com.zksy.base.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.zksy.base.domain.WorkOrder;
+import com.zksy.base.domain.WorkOrderLog;
+import com.zksy.base.mapper.WorkOrderLogMapper;
+import com.zksy.base.mapper.WorkOrderMapper;
+import com.zksy.base.service.BusinessWorkOrderService;
+import com.zksy.common.exception.ServiceException;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 业务工单管理 Service 实现
+ */
+@Slf4j
+@Service
+public class BusinessWorkOrderServiceImpl extends ServiceImpl<WorkOrderMapper, WorkOrder> implements BusinessWorkOrderService {
+
+    @Autowired
+    private WorkOrderLogMapper workOrderLogMapper;
+
+    @Override
+    public IPage<WorkOrder> selectWorkOrderList(Integer orderType, Integer orderLevel, Integer orderStatus, String deviceCode, String keyword, String equipmentType, Integer pageNum, Integer pageSize) {
+        Page<WorkOrder> page = new Page<>(pageNum, pageSize);
+        LambdaQueryWrapper<WorkOrder> wrapper = new LambdaQueryWrapper<>();
+        // 按设备类型过滤工单:gas-燃气(父类别ID=3),drainage-排水(typeId为drainage的顶级分类)
+        if (StringUtils.isNotEmpty(equipmentType)) {
+            if ("gas".equals(equipmentType)) {
+                wrapper.inSql(WorkOrder::getDeviceId, "SELECT eb.equipment_id FROM app_user.equipment_base eb " + "INNER JOIN app_user.equipment_type et ON eb.equipment_type_id = et.id " + "WHERE et.parent_type_id = '3'");
+            } else if ("drainage".equals(equipmentType)) {
+                // 排水:typeId为drainage的顶级分类及其全部子类型
+                wrapper.inSql(WorkOrder::getDeviceId, "SELECT eb.equipment_id FROM app_user.equipment_base eb " + "INNER JOIN app_user.equipment_type et ON eb.equipment_type_id = et.id " + "WHERE et.id IN (SELECT id FROM app_user.equipment_type WHERE type_id = 'drainage' AND parent_type_id = '0' " + "UNION SELECT id FROM app_user.equipment_type WHERE parent_type_id = (SELECT id FROM app_user.equipment_type WHERE type_id = 'drainage' AND parent_type_id = '0'))");
+            }
+        } else {
+            // 默认只查询燃气设备类工单(兼容旧逻辑)
+            wrapper.inSql(WorkOrder::getDeviceId, "SELECT eb.equipment_id FROM app_user.equipment_base eb " + "INNER JOIN app_user.equipment_type et ON eb.equipment_type_id = et.id " + "WHERE et.parent_type_id = '3'");
+        }
+        if (orderType != null) wrapper.eq(WorkOrder::getOrderType, orderType);
+        if (orderLevel != null) wrapper.eq(WorkOrder::getOrderLevel, orderLevel);
+        if (orderStatus != null) wrapper.eq(WorkOrder::getOrderStatus, orderStatus);
+        if (StringUtils.isNotEmpty(deviceCode)) wrapper.eq(WorkOrder::getDeviceCode, deviceCode);
+        if (StringUtils.isNotEmpty(keyword)) {
+            wrapper.and(w -> w.like(WorkOrder::getOrderNo, keyword).or().like(WorkOrder::getDeviceCode, keyword).or().like(WorkOrder::getOrderDesc, keyword));
+        }
+        wrapper.orderByAsc(WorkOrder::getOrderLevel, WorkOrder::getOrderStatus);
+        wrapper.orderByDesc(WorkOrder::getCreateTime);
+        return this.page(page, wrapper);
+    }
+
+    @Override
+    public WorkOrder getWorkOrderDetail(Long orderId) {
+        WorkOrder order = this.getById(orderId);
+        if (order == null) throw new ServiceException("工单不存在");
+        return order;
+    }
+
+    @Override
+    public List<WorkOrderLog> getWorkOrderLogs(Long orderId) {
+        LambdaQueryWrapper<WorkOrderLog> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(WorkOrderLog::getOrderId, orderId);
+        wrapper.orderByDesc(WorkOrderLog::getOperTime);
+        return workOrderLogMapper.selectList(wrapper);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean dispatchOrder(Long orderId, Long handlerId, String remarks, Long operUserId, String operUserName) {
+        WorkOrder order = this.getById(orderId);
+        if (order == null) throw new ServiceException("工单不存在");
+        if (order.getOrderStatus() != 1) throw new ServiceException("当前工单状态不允许派单");
+
+        WorkOrder entity = new WorkOrder();
+        entity.setOrderId(orderId);
+        entity.setOrderStatus(2);
+        entity.setDispatchUser(operUserId);
+        entity.setDispatchTime(new Date());
+        entity.setReceiveUser(handlerId.toString());
+        this.updateById(entity);
+
+        WorkOrderLog logEntry = new WorkOrderLog();
+        logEntry.setOrderId(orderId);
+        logEntry.setOperType(2);
+        logEntry.setOperDesc("派单给处理人ID:" + handlerId + ",备注:" + (remarks != null ? remarks : ""));
+        logEntry.setOperUserId(operUserId);
+        logEntry.setOperUserName(operUserName);
+        workOrderLogMapper.insert(logEntry);
+        return true;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean batchDispatchOrder(Long orderId, List<Long> handlerIds, String remarks, Long operUserId, String operUserName) {
+        if (handlerIds == null || handlerIds.isEmpty()) {
+            throw new ServiceException("派单人员不能为空");
+        }
+        WorkOrder order = this.getById(orderId);
+        if (order == null) throw new ServiceException("工单不存在");
+        if (order.getOrderStatus() != 1) throw new ServiceException("当前工单状态不允许派单");
+
+        // 更新工单状态(只更新一次)
+        WorkOrder entity = new WorkOrder();
+        entity.setOrderId(orderId);
+        entity.setOrderStatus(2);
+        entity.setDispatchUser(operUserId);
+        entity.setDispatchTime(new Date());
+        // 多个处理人用逗号分隔存储
+        String receiveUserStr = handlerIds.stream()
+            .map(String::valueOf)
+            .collect(Collectors.joining(","));
+        entity.setReceiveUser(receiveUserStr);
+        this.updateById(entity);
+
+        // 为每个 handler 写入操作日志
+        for (Long handlerId : handlerIds) {
+            WorkOrderLog logEntry = new WorkOrderLog();
+            logEntry.setOrderId(orderId);
+            logEntry.setOperType(2);
+            logEntry.setOperDesc("派单给处理人ID:" + handlerId + ",备注:" + (remarks != null ? remarks : ""));
+            logEntry.setOperUserId(operUserId);
+            logEntry.setOperUserName(operUserName);
+            workOrderLogMapper.insert(logEntry);
+        }
+        return true;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean verifyOrder(Long orderId, String handleResult, String solution, Long operUserId, String operUserName) {
+        WorkOrder order = this.getById(orderId);
+        if (order == null) throw new ServiceException("工单不存在");
+        if (!Arrays.asList(2, 3).contains(order.getOrderStatus())) throw new ServiceException("当前工单状态不允许核查");
+
+        WorkOrder entity = new WorkOrder();
+        entity.setOrderId(orderId);
+        entity.setOrderStatus(3);
+        entity.setRepairResult(solution);
+        this.updateById(entity);
+
+        WorkOrderLog logEntry = new WorkOrderLog();
+        logEntry.setOrderId(orderId);
+        logEntry.setOperType(3);
+        logEntry.setOperDesc("核查结果:" + handleResult + ",说明:" + solution);
+        logEntry.setOperUserId(operUserId);
+        logEntry.setOperUserName(operUserName);
+        workOrderLogMapper.insert(logEntry);
+        return true;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean processOrder(Long orderId, String handleResult, String solution, Long operUserId, String operUserName) {
+        WorkOrder order = this.getById(orderId);
+        if (order == null) throw new ServiceException("工单不存在");
+        if (!Arrays.asList(3, 4).contains(order.getOrderStatus())) throw new ServiceException("当前工单状态不允许处理");
+
+        WorkOrder entity = new WorkOrder();
+        entity.setOrderId(orderId);
+        entity.setOrderStatus(4);
+        entity.setRepairResult(solution);
+        this.updateById(entity);
+
+        WorkOrderLog logEntry = new WorkOrderLog();
+        logEntry.setOrderId(orderId);
+        logEntry.setOperType(4);
+        logEntry.setOperDesc("处理结果:" + handleResult + ",说明:" + solution);
+        logEntry.setOperUserId(operUserId);
+        logEntry.setOperUserName(operUserName);
+        workOrderLogMapper.insert(logEntry);
+        return true;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean completeOrder(Long orderId, String result, Long operUserId, String operUserName) {
+        WorkOrder order = this.getById(orderId);
+        if (order == null) throw new ServiceException("工单不存在");
+        if (order.getOrderStatus() != 4)
+            throw new ServiceException("当前工单状态不允许提交验收");
+
+        WorkOrder entity = new WorkOrder();
+        entity.setOrderId(orderId);
+        entity.setOrderStatus(5);
+        entity.setRepairResult(result);
+        this.updateById(entity);
+
+        WorkOrderLog logEntry = new WorkOrderLog();
+        logEntry.setOrderId(orderId);
+        logEntry.setOperType(5);
+        logEntry.setOperDesc("提交验收,结果:" + result);
+        logEntry.setOperUserId(operUserId);
+        logEntry.setOperUserName(operUserName);
+        workOrderLogMapper.insert(logEntry);
+        return true;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean cancelOrder(Long orderId, Long operUserId, String operUserName) {
+        WorkOrder order = this.getById(orderId);
+        if (order == null) throw new ServiceException("工单不存在");
+        if (order.getOrderStatus() == 6) throw new ServiceException("已结案工单不可取消");
+
+        WorkOrder entity = new WorkOrder();
+        entity.setOrderId(orderId);
+        entity.setOrderStatus(7);
+        this.updateById(entity);
+
+        WorkOrderLog logEntry = new WorkOrderLog();
+        logEntry.setOrderId(orderId);
+        logEntry.setOperType(7);
+        logEntry.setOperDesc("取消工单");
+        logEntry.setOperUserId(operUserId);
+        logEntry.setOperUserName(operUserName);
+        workOrderLogMapper.insert(logEntry);
+        return true;
+    }
+}

+ 43 - 5
pipe-network-service/zksy-system/src/main/java/com/zksy/base/service/impl/GasEquipmentServiceImpl.java

@@ -55,23 +55,57 @@ public class GasEquipmentServiceImpl implements GasEquipmentService {
         String equipmentTypeId = entity.getEquipmentTypeId();
         EquipmentType equipmentType = equipmentTypeMapper.selectById(equipmentTypeId);
         if (equipmentType == null) {
-            throw new ServiceException("设备类型不存在,请检查equipment_type_id");
+            throw new ServiceException("设备类型不存在,请检查 equipment_type_id");
         }
+            
+        // ========== 新增:根据片区自动生成设备编码 ==========
+        String district = entity.getDistrict();
+        if (district != null && !district.trim().isEmpty() && 
+            (entity.getEquipmentCode() == null || entity.getEquipmentCode().trim().isEmpty())) {
+            String prefix = getDistrictPrefix(district);
+                
+            // 查询该片区已存在的设备数量
+            LambdaQueryWrapper<EquipmentBase> codeWrapper = new LambdaQueryWrapper<>();
+            codeWrapper.likeRight(EquipmentBase::getEquipmentCode, prefix + "-GAS-");
+            Long count = equipmentBaseMapper.selectCount(codeWrapper);
+                
+            // 生成下一个序列号
+            String seq = String.format("%03d", count + 1);
+            entity.setEquipmentCode(prefix + "-GAS-" + seq);
+        }
+        // ===============================================
+            
+        // 检查唯一性
         LambdaQueryWrapper<EquipmentBase> queryWrapper = new LambdaQueryWrapper<>();
         queryWrapper.eq(EquipmentBase::getEquipmentCode, entity.getEquipmentCode());
-        long count = equipmentBaseMapper.selectCount(queryWrapper);
-        if (count > 0) {
+        long totalCount = equipmentBaseMapper.selectCount(queryWrapper);
+        if (totalCount > 0) {
             throw new ServiceException("设备编码已存在,请更换:" + entity.getEquipmentCode());
         }
+            
         try {
             return equipmentBaseMapper.insert(entity) > 0;
         } catch (org.springframework.dao.DataIntegrityViolationException e) {
             if (e.getMessage() != null && e.getMessage().contains("uk_equipment_code")) {
-                throw new ServiceException("设备编码已存在, 请更换: " + entity.getEquipmentCode());
+                throw new ServiceException("设备编码已存在,请更换:" + entity.getEquipmentCode());
             }
             throw e;
         }
     }
+        
+    /**
+     * 获取片区前缀映射
+     */
+    private String getDistrictPrefix(String district) {
+        if ("城南".equals(district)) {
+            return "CN";
+        } else if ("城北".equals(district)) {
+            return "CB";
+        } else if ("太常".equals(district)) {
+            return "TC";
+        }
+        return "QT"; // 其他区域默认前缀
+    }
 
     @Override
     @Transactional
@@ -132,7 +166,7 @@ public class GasEquipmentServiceImpl implements GasEquipmentService {
     @Override
     public Page<EquipmentFullVO> findFullByPage(long pageNum, long pageSize,
                                                  String equipmentCode, String equipmentName,
-                                                 String equipmentModel, String manufacturer) {
+                                                 String equipmentModel, String manufacturer,String district) {
         // 只查燃气类型设备: parent_type_id = "3"
         List<String> gasTypeIds = equipmentTypeMapper.selectList(
                 new LambdaQueryWrapper<EquipmentType>().eq(EquipmentType::getParentTypeId, GasEquipmentTypeEnum.PARENT_TYPE_ID)
@@ -155,6 +189,10 @@ public class GasEquipmentServiceImpl implements GasEquipmentService {
         if (manufacturer != null && !manufacturer.isEmpty()) {
             qw.like(EquipmentBase::getManufacturer, manufacturer);
         }
+        // 添加片区筛选条件
+        if (district != null && !district.isEmpty()) {
+            qw.eq(EquipmentBase::getDistrict, district);
+        }
         qw.orderByDesc(EquipmentBase::getCreateTime);
         Page<EquipmentBase> basePage = equipmentBaseMapper.selectPage(page, qw);
         List<EquipmentBase> equipmentList = basePage.getRecords();

+ 2 - 0
pipe-network-service/zksy-system/src/main/java/com/zksy/base/service/impl/ManholeBaseServiceImpl.java

@@ -11,6 +11,7 @@ import com.zksy.common.exception.ServiceException;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 
+import java.time.LocalDateTime;
 import java.util.List;
 
 @Slf4j
@@ -44,6 +45,7 @@ public class ManholeBaseServiceImpl extends ServiceImpl<ManholeBaseMapper, Manho
         if (this.count(wrapper) > 0) {
             throw new ServiceException("窨井编码已存在:" + entity.getManholeCode());
         }
+        entity.setCreateTime(LocalDateTime.now());
         return this.save(entity);
     }
 

+ 2 - 2
pipe-network-service/zksy-system/src/main/java/com/zksy/base/service/impl/WorkOrderServiceImpl.java

@@ -205,11 +205,11 @@ public class WorkOrderServiceImpl extends ServiceImpl<WorkOrderMapper, WorkOrder
             entity.setOrderStatus(2);
             entity.setDispatchUser(inDTO.getOperUserId());
             entity.setDispatchTime(new Date());
-            entity.setReceiveUser(inDTO.getReceiveUser());
+            entity.setReceiveUser(inDTO.getReceiveUser().toString());
         } else if(inDTO.getOperType() == 3){
             //3-接单
             entity.setOrderStatus(3);
-            entity.setReceiveUser(inDTO.getOperUserId());
+            entity.setReceiveUser(inDTO.getOperUserId().toString());
         } else if(inDTO.getOperType() == 4){
             //4-开始维修
             entity.setOrderStatus(4);

+ 2 - 0
pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/domain/DisEventGrade.java

@@ -1,6 +1,7 @@
 package com.zksy.dispatch.domain;
 
 import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableLogic;
 import com.baomidou.mybatisplus.annotation.TableName;
@@ -29,6 +30,7 @@ public class DisEventGrade {
     private String category;
 
     @ApiModelProperty("级别")
+    @TableField("\"level\"")
     private String level;
 
     @ApiModelProperty("子分类")

+ 2 - 0
pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/domain/DisEventTrack.java

@@ -1,6 +1,7 @@
 package com.zksy.dispatch.domain;
 
 import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableLogic;
 import com.baomidou.mybatisplus.annotation.TableName;
@@ -32,6 +33,7 @@ public class DisEventTrack {
     private String category;
 
     @ApiModelProperty("事件级别")
+    @TableField("\"level\"")
     private String level;
 
     @ApiModelProperty("地点")

+ 1 - 0
pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/domain/DisEventTransfer.java

@@ -32,6 +32,7 @@ public class DisEventTransfer {
     private String category;
 
     @ApiModelProperty("事件级别")
+    @TableField("\"level\"")
     private String level;
 
     @ApiModelProperty("摘要")

+ 2 - 0
pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/domain/DisEventVerify.java

@@ -1,6 +1,7 @@
 package com.zksy.dispatch.domain;
 
 import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableLogic;
 import com.baomidou.mybatisplus.annotation.TableName;
@@ -35,6 +36,7 @@ public class DisEventVerify {
     private String category;
 
     @ApiModelProperty("事件级别")
+    @TableField("\"level\"")
     private String level;
 
     @ApiModelProperty("地点")

+ 2 - 0
pipe-network-service/zksy-system/src/main/java/com/zksy/drainage/domain/DrainagePartition.java

@@ -1,6 +1,7 @@
 package com.zksy.drainage.domain;
 
 import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
 import com.fasterxml.jackson.annotation.JsonFormat;
@@ -40,6 +41,7 @@ public class DrainagePartition {
     private BigDecimal waterlogging;
 
     @ApiModelProperty("积水等级")
+    @TableField("\"level\"")
     private Integer level;
 
     @ApiModelProperty("经度")

+ 18 - 1
pipe-network-service/zksy-system/src/main/java/com/zksy/system/service/impl/SysMenuServiceImpl.java

@@ -492,6 +492,23 @@ public class SysMenuServiceImpl implements ISysMenuService
      */
     private void recursionFn(List<SysMenu> list, SysMenu t)
     {
+        recursionFn(list, t, new HashSet<Long>());
+    }
+
+    /**
+     * 递归列表
+     *
+     * @param list 分类表
+     * @param t 子节点
+     * @param visited 已访问的菜单ID集合,用于防止循环引用导致栈溢出
+     */
+    private void recursionFn(List<SysMenu> list, SysMenu t, Set<Long> visited)
+    {
+        // 防止循环引用(如菜单A的父节点是B,B的父节点又是A)导致栈溢出
+        if (!visited.add(t.getMenuId()))
+        {
+            return;
+        }
         // 得到子节点列表
         List<SysMenu> childList = getChildList(list, t);
         t.setChildren(childList);
@@ -499,7 +516,7 @@ public class SysMenuServiceImpl implements ISysMenuService
         {
             if (hasChild(list, tChild))
             {
-                recursionFn(list, tChild);
+                recursionFn(list, tChild, visited);
             }
         }
     }

+ 3 - 2
pipe-network-service/zksy-system/src/main/resources/mapper/WorkOrderMapper.xml

@@ -18,13 +18,14 @@
             <result property="deptId" column="dept_id" jdbcType="BIGINT"/>
             <result property="dispatchUser" column="dispatch_user" jdbcType="BIGINT"/>
             <result property="dispatchTime" column="dispatch_time" jdbcType="TIMESTAMP"/>
-            <result property="receiveUser" column="receive_user" jdbcType="BIGINT"/>
+            <result property="receiveUser" column="receive_user" jdbcType="VARCHAR"/>
             <result property="orderStatus" column="order_status" jdbcType="TINYINT"/>
             <result property="delayedReason" column="delayed_reason" jdbcType="VARCHAR"/>
             <result property="delayedDeadline" column="delayed_deadline" jdbcType="TIMESTAMP"/>
             <result property="planFinishTime" column="plan_finish_time" jdbcType="TIMESTAMP"/>
             <result property="realFinishTime" column="real_finish_time" jdbcType="TIMESTAMP"/>
             <result property="repairResult" column="repair_result" jdbcType="VARCHAR"/>
+            <result property="workImage" column="work_image" jdbcType="VARCHAR"/>
             <result property="verifyUser" column="verify_user" jdbcType="BIGINT"/>
             <result property="verifyTime" column="verify_time" jdbcType="TIMESTAMP"/>
             <result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
@@ -38,7 +39,7 @@
         order_desc,dept_id,dispatch_user,
         dispatch_time,receive_user,order_status,
         delayed_reason,delayed_deadline,plan_finish_time,
-        real_finish_time,repair_result,verify_user,
+        real_finish_time,repair_result,work_image,verify_user,
         verify_time,create_time,update_time
     </sql>
 </mapper>

+ 2 - 2
pipe-network-service/zksy-system/src/main/resources/mapper/system/SysDeptMapper.xml

@@ -75,11 +75,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 	</select>
 	
 	<select id="selectChildrenDeptById" parameterType="Long" resultMap="SysDeptResult">
-		select * from app_user.sys_dept where POSITION(',' || #{deptId} || ',' IN ',' || ancestors || ',') > 0
+		select * from app_user.sys_dept where POSITION(CONCAT(',', #{deptId}, ',') IN CONCAT(',', ancestors, ',')) > 0
 	</select>
 	
 	<select id="selectNormalChildrenDeptById" parameterType="Long" resultType="int">
-		select count(*) from app_user.sys_dept where status = 0 and del_flag = '0' and POSITION(',' || #{deptId} || ',' IN ',' || ancestors || ',') > 0
+		select count(*) from app_user.sys_dept where status = 0 and del_flag = '0' and POSITION(CONCAT(',', #{deptId}, ',') IN CONCAT(',', ancestors, ',')) > 0
 	</select>
 	
 	<select id="checkDeptNameUnique" resultMap="SysDeptResult">

+ 1 - 1
pipe-network-service/zksy-system/src/main/resources/mapper/system/SysUserMapper.xml

@@ -80,7 +80,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 			AND TO_CHAR(u.create_time, 'YYYYMMDD') &lt;= TO_CHAR(#{params.endTime}::TIMESTAMP, 'YYYYMMDD')
 		</if>
 		<if test="deptId != null and deptId != 0">
-			AND (u.dept_id = #{deptId} OR u.dept_id IN ( SELECT t.dept_id from app_user.sys_dept t WHERE POSITION(',' || #{deptId} || ',' IN ',' || ancestors || ',') > 0 ))
+			AND (u.dept_id = #{deptId} OR u.dept_id IN ( SELECT t.dept_id from app_user.sys_dept t WHERE POSITION(CONCAT(',', #{deptId}, ',') IN CONCAT(',', ancestors, ',')) > 0 ))
 		</if>
 		<!-- 数据范围过滤 -->
 		${params.dataScope}

+ 9 - 3
zk-api-service/src/main/java/com/zksy/api/domain/WorkOrder.java

@@ -105,10 +105,10 @@ public class WorkOrder implements Serializable {
     private Date dispatchTime;
 
     /**
-     * 接单运维人员ID
+     * 接单运维人员ID(多个用逗号分隔)
      */
-    @ApiModelProperty(value = "接单运维人员ID")
-    private Long receiveUser;
+    @ApiModelProperty(value = "接单运维人员ID,多个用逗号分隔")
+    private String receiveUser;
 
     /**
      * 工单状态:1-待派单 2-待接单 3-已接单 4-处理中 5-待验收 6-已结案 7-已驳回 8-延期审核 9-已延期
@@ -149,6 +149,12 @@ public class WorkOrder implements Serializable {
     @ApiModelProperty(value = "维修处置结果")
     private String repairResult;
 
+    /**
+     * 工单图片(多个图片用逗号分隔)
+     */
+    @ApiModelProperty(value = "工单图片,多个用逗号分隔")
+    private String workImage;
+
     /**
      * 验收人ID
      */

Niektoré súbory nie sú zobrazené, pretože je v týchto rozdielových dátach zmenené mnoho súborov