Procházet zdrojové kódy

feat(dispatch): 附件相对路径存储、按钮权限、排序规则与注释净化

- 附件路径治理:新增 DispatchAttachUrlSupport,库存相对路径,读出口拼
  minio.endpoint+bucket 返回完整 URL(http 开头存量原样返回不二次拼接);
  事件日志/佐证、预案分页详情版本等写入口统一 toRelative
- 按钮权限:4 个 Controller 全部业务接口加 @PreAuthorize(事件/分级/预案/模板库,
  动作接口含核实/转接/报送/处置/跟踪/解除/催办;查询接口由 @Anonymous 收紧为
  xxx:list;保留 /home、下拉选项、分级匹配等跨页共用接口匿名)
- 排序规则:DisEventPageDTO 加 orderAsc,队列页(核实/转接报送/跟踪解除)接报时间
  升序先报先办,接报管理默认倒序;分级/模板库改创建时间倒序
- 核实强校验:责任处置人必须关联系统用户(handlerId 兜底反查,关联不到拒绝保存),
  保证催办通知/SSE 有账号可投递
- 注释净化:dispatch 包 53 文件去除 <p>/<b>/{@code}/{@link}/markdown 粗体/①② 编号,
  长类注释精简为工程口语陈述句(代码零改动,逐行校验)
null před 4 dny
rodič
revize
c9a266ac90
54 změnil soubory, kde provedl 319 přidání a 294 odebrání
  1. 18 7
      pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/dispatch/DisEventController.java
  2. 8 4
      pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/dispatch/DisEventGradeController.java
  3. 13 7
      pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/dispatch/DisPlanController.java
  4. 4 1
      pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/dispatch/DisPlanLibraryController.java
  5. 4 8
      pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/domain/DisEvent.java
  6. 7 9
      pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/domain/DisEventGrade.java
  7. 4 7
      pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/domain/DisEventLog.java
  8. 4 7
      pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/domain/DisPlan.java
  9. 3 6
      pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/domain/DisPlanLog.java
  10. 4 7
      pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/domain/DisPlanVersion.java
  11. 0 1
      pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/domain/DispatchNotify.java
  12. 4 6
      pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/dto/DisDeptOptionVO.java
  13. 5 3
      pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/dto/DisEventPageDTO.java
  14. 2 3
      pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/dto/DisEventVO.java
  15. 8 10
      pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/dto/DisHomeVO.java
  16. 3 5
      pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/dto/DisUserOptionVO.java
  17. 2 3
      pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/dto/EnumOptionVO.java
  18. 1 2
      pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/dto/EventReleaseDTO.java
  19. 2 3
      pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/dto/EventSaveDTO.java
  20. 2 3
      pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/dto/EventTrackDTO.java
  21. 1 2
      pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/dto/EventTransferDTO.java
  22. 2 3
      pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/dto/EventUrgeDTO.java
  23. 2 3
      pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/dto/EventUrgeRowVO.java
  24. 2 3
      pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/dto/EventVerifyDTO.java
  25. 2 3
      pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/dto/PlanApproveDTO.java
  26. 1 2
      pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/dto/PlanPageDTO.java
  27. 3 5
      pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/dto/PlanSaveDTO.java
  28. 1 3
      pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/dto/SseMessageDto.java
  29. 3 5
      pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/enums/EventLogOperTypeEnum.java
  30. 2 7
      pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/enums/EventStatusEnum.java
  31. 2 4
      pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/enums/GradeStatusEnum.java
  32. 1 3
      pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/enums/PlanLogOperTypeEnum.java
  33. 3 5
      pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/enums/PlanStatusEnum.java
  34. 4 6
      pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/enums/PlanVersionStatusEnum.java
  35. 2 3
      pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/mapper/DisEventGradeMapper.java
  36. 1 2
      pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/mapper/DisEventMapper.java
  37. 10 16
      pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/mapper/DispatchUserMapper.java
  38. 5 9
      pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/service/DisEventLogService.java
  39. 6 10
      pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/service/DisEventService.java
  40. 2 5
      pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/service/DisPlanLibraryService.java
  41. 1 3
      pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/service/DisPlanLogService.java
  42. 2 3
      pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/service/DisPlanService.java
  43. 5 8
      pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/service/DispatchNotifyService.java
  44. 2 3
      pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/service/impl/DisEventGradeServiceImpl.java
  45. 13 5
      pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/service/impl/DisEventLogServiceImpl.java
  46. 43 26
      pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/service/impl/DisEventServiceImpl.java
  47. 1 2
      pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/service/impl/DisPlanLibraryServiceImpl.java
  48. 21 11
      pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/service/impl/DisPlanServiceImpl.java
  49. 2 3
      pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/service/impl/DisPlanVersionServiceImpl.java
  50. 0 1
      pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/service/impl/DispatchNotifyServiceImpl.java
  51. 66 0
      pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/support/DispatchAttachUrlSupport.java
  52. 6 18
      pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/support/DispatchNumberSupport.java
  53. 2 4
      pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/support/DispatchUserSupport.java
  54. 2 6
      pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/support/SseEmitterManager.java

+ 18 - 7
pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/dispatch/DisEventController.java

@@ -33,6 +33,7 @@ import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestParam;
@@ -64,7 +65,7 @@ public class DisEventController {
 
     @GetMapping("/findByPage")
     @ApiOperation("事件分页查询(多条件 + 状态过滤,接报/核实/转接/跟踪/催办共用)")
-    @Anonymous
+    @PreAuthorize("@ss.hasPermi('dispatch:event:list')")
     public AjaxResult findByPage(DisEventPageDTO dto) {
         Page<DisEventVO> page = service.findByPage(dto);
         return AjaxResult.success(page);
@@ -72,7 +73,7 @@ public class DisEventController {
 
     @GetMapping("/statusOptions")
     @ApiOperation("事件状态下拉项(EventStatusEnum,供前端筛选使用)")
-    @Anonymous
+    @PreAuthorize("@ss.hasPermi('dispatch:event:list')")
     public AjaxResult statusOptions() {
         List<EnumOptionVO> options = Arrays.stream(EventStatusEnum.values())
                 .map(item -> new EnumOptionVO(item.getCode(), item.getDesc()))
@@ -82,14 +83,14 @@ public class DisEventController {
 
     @GetMapping("/getDetail/{id}")
     @ApiOperation("事件详情(主表当前值 + 操作日志时间线)")
-    @Anonymous
+    @PreAuthorize("@ss.hasPermi('dispatch:event:list')")
     public AjaxResult getDetail(@PathVariable Long id) {
         return AjaxResult.success(service.getDetail(id));
     }
 
     @GetMapping("/stats")
     @ApiOperation("多维条件统计(事件总数/损失合计/平均损失/重大及以上事件数)")
-    @Anonymous
+    @PreAuthorize("@ss.hasPermi('dispatch:event:list')")
     public AjaxResult stats(DisEventPageDTO dto) {
         EventStatsVO stats = service.stats(dto);
         return AjaxResult.success(stats);
@@ -97,7 +98,7 @@ public class DisEventController {
 
     @GetMapping("/logs/{eventId}")
     @ApiOperation("事件操作日志时间线(可选按 operType 过滤,如催办记录 operType=8)")
-    @Anonymous
+    @PreAuthorize("@ss.hasPermi('dispatch:event:list')")
     public AjaxResult logs(@PathVariable Long eventId,
                            @RequestParam(required = false) Integer operType) {
         List<DisEventLog> logs = eventLogService.listByEventId(eventId);
@@ -111,14 +112,14 @@ public class DisEventController {
 
     @GetMapping("/logs")
     @ApiOperation("事件操作日志分页查询")
-    @Anonymous
+    @PreAuthorize("@ss.hasPermi('dispatch:event:list')")
     public AjaxResult logPage(EventLogPageDTO dto) {
         return AjaxResult.success(eventLogService.findByPage(dto));
     }
 
     @GetMapping("/urge/findByPage")
     @ApiOperation("工作催办分页查询(事件 + 催办次数 + 最近催办信息)")
-    @Anonymous
+    @PreAuthorize("@ss.hasPermi('dispatch:event:list')")
     public AjaxResult findUrgePage(EventUrgePageDTO dto) {
         Page<EventUrgeRowVO> page = service.findUrgePage(dto);
         return AjaxResult.success(page);
@@ -129,6 +130,7 @@ public class DisEventController {
     @PostMapping("/saveReport")
     @ApiOperation("新增接报(自动生成事件编号,状态置为待核实)")
     @Log(title = "新增事件接报", businessType = BusinessType.INSERT)
+    @PreAuthorize("@ss.hasPermi('dispatch:event:add')")
     public AjaxResult saveReport(@RequestBody EventSaveDTO dto) {
         DisEvent saved = service.saveReport(dto);
         return AjaxResult.success("接报成功", saved);
@@ -137,6 +139,7 @@ public class DisEventController {
     @PutMapping("/updateById")
     @ApiOperation("编辑接报信息(仅待核实可改,编号与状态不可改)")
     @Log(title = "修改事件接报", businessType = BusinessType.UPDATE)
+    @PreAuthorize("@ss.hasAnyPermi('dispatch:event:add,dispatch:event:edit')")
     public AjaxResult updateById(@RequestBody EventSaveDTO dto) {
         service.updateEvent(dto);
         return AjaxResult.success("修改成功");
@@ -145,6 +148,7 @@ public class DisEventController {
     @DeleteMapping("/deleteById")
     @ApiOperation("删除事件(仅待核实可删)")
     @Log(title = "删除事件", businessType = BusinessType.DELETE)
+    @PreAuthorize("@ss.hasPermi('dispatch:event:remove')")
     public AjaxResult deleteById(@RequestParam Long id) {
         service.deleteEvent(id);
         return AjaxResult.success("删除成功");
@@ -176,6 +180,7 @@ public class DisEventController {
     @PutMapping("/verify/{id}")
     @ApiOperation("初判核实(真实→已核实、不实→不实事件)")
     @Log(title = "初判核实事件", businessType = BusinessType.UPDATE)
+    @PreAuthorize("@ss.hasPermi('dispatch:event:verify')")
     public AjaxResult verify(@PathVariable Long id, @RequestBody EventVerifyDTO dto) {
         service.verify(id, dto);
         return AjaxResult.success("核实成功");
@@ -186,6 +191,7 @@ public class DisEventController {
     @PostMapping("/transfer")
     @ApiOperation("电话转接(写日志,不改事件状态)")
     @Log(title = "转接事件", businessType = BusinessType.UPDATE)
+    @PreAuthorize("@ss.hasPermi('dispatch:event:transfer')")
     public AjaxResult transfer(@RequestBody EventTransferDTO dto) {
         service.transfer(dto);
         return AjaxResult.success("转接成功");
@@ -194,6 +200,7 @@ public class DisEventController {
     @PostMapping("/report")
     @ApiOperation("上报报送(写日志,不改事件状态)")
     @Log(title = "报送事件", businessType = BusinessType.UPDATE)
+    @PreAuthorize("@ss.hasPermi('dispatch:event:report')")
     public AjaxResult report(@RequestBody EventTransferDTO dto) {
         service.report(dto);
         return AjaxResult.success("报送成功");
@@ -204,6 +211,7 @@ public class DisEventController {
     @PostMapping("/startDispose")
     @ApiOperation("开始处置(已核实 → 处置中)")
     @Log(title = "开始处置", businessType = BusinessType.UPDATE)
+    @PreAuthorize("@ss.hasPermi('dispatch:event:dispose')")
     public AjaxResult startDispose(@RequestBody EventTrackDTO dto) {
         service.startDispose(dto);
         return AjaxResult.success("已开始处置");
@@ -212,6 +220,7 @@ public class DisEventController {
     @PostMapping("/track")
     @ApiOperation("处置跟踪(可多次记录,不改事件状态)")
     @Log(title = "处置跟踪", businessType = BusinessType.INSERT)
+    @PreAuthorize("@ss.hasPermi('dispatch:event:track')")
     public AjaxResult track(@RequestBody EventTrackDTO dto) {
         service.track(dto);
         return AjaxResult.success("跟踪记录保存成功");
@@ -220,6 +229,7 @@ public class DisEventController {
     @PostMapping("/release/{id}")
     @ApiOperation("事件解除(处置中 → 已解除)")
     @Log(title = "解除事件", businessType = BusinessType.UPDATE)
+    @PreAuthorize("@ss.hasPermi('dispatch:event:release')")
     public AjaxResult release(@PathVariable Long id, @RequestBody EventReleaseDTO dto) {
         service.release(id, dto);
         return AjaxResult.success("事件已解除");
@@ -230,6 +240,7 @@ public class DisEventController {
     @PostMapping("/urge")
     @ApiOperation("工作催办(写日志,不改事件状态)")
     @Log(title = "工作催办", businessType = BusinessType.UPDATE)
+    @PreAuthorize("@ss.hasPermi('dispatch:event:urge')")
     public AjaxResult urge(@RequestBody EventUrgeDTO dto) {
         service.urge(dto);
         return AjaxResult.success("催办已发送");

+ 8 - 4
pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/dispatch/DisEventGradeController.java

@@ -19,6 +19,7 @@ import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestParam;
@@ -45,7 +46,7 @@ public class DisEventGradeController {
 
     @GetMapping("/findByPage")
     @ApiOperation("分级标准分页查询")
-    @Anonymous
+    @PreAuthorize("@ss.hasPermi('dispatch:grade:list')")
     public AjaxResult findByPage(DisEventGradePageDTO dto) {
         Page<DisEventGrade> page = service.findByPage(dto);
         return AjaxResult.success(page);
@@ -53,14 +54,14 @@ public class DisEventGradeController {
 
     @GetMapping("/getById/{id}")
     @ApiOperation("根据ID查询分级标准")
-    @Anonymous
+    @PreAuthorize("@ss.hasPermi('dispatch:grade:list')")
     public AjaxResult getById(@PathVariable Long id) {
         DisEventGrade entity = service.getById(id);
         return entity != null ? AjaxResult.success(entity) : AjaxResult.error("分级标准不存在");
     }
 
     @GetMapping("/listByCategory")
-    @ApiOperation("按事件类别查询启用中的分级标准(初判核实辅助匹配)")
+    @ApiOperation("按事件类别查询启用中的分级标准(初判核实辅助匹配,跨页面共用保持匿名)")
     @Anonymous
     public AjaxResult listByCategory(@RequestParam(required = false) String eventCategory) {
         return AjaxResult.success(service.listByCategory(eventCategory));
@@ -68,7 +69,7 @@ public class DisEventGradeController {
 
     @GetMapping("/statusOptions")
     @ApiOperation("启用状态下拉项(GradeStatusEnum,供前端筛选使用)")
-    @Anonymous
+    @PreAuthorize("@ss.hasPermi('dispatch:grade:list')")
     public AjaxResult statusOptions() {
         List<EnumOptionVO> options = Arrays.stream(GradeStatusEnum.values())
                 .map(item -> new EnumOptionVO(item.getCode(), item.getDesc()))
@@ -79,6 +80,7 @@ public class DisEventGradeController {
     @PostMapping("/save")
     @ApiOperation("新增分级标准(自动生成标准编号,默认启用)")
     @Log(title = "新增分级标准", businessType = BusinessType.INSERT)
+    @PreAuthorize("@ss.hasPermi('dispatch:grade:add')")
     public AjaxResult save(@RequestBody DisEventGrade entity) {
         return AjaxResult.success("新增成功", service.saveGrade(entity));
     }
@@ -86,6 +88,7 @@ public class DisEventGradeController {
     @PutMapping("/updateById")
     @ApiOperation("修改分级标准(标准编号不可改)")
     @Log(title = "修改分级标准", businessType = BusinessType.UPDATE)
+    @PreAuthorize("@ss.hasPermi('dispatch:grade:edit')")
     public AjaxResult updateById(@RequestBody DisEventGrade entity) {
         service.updateGrade(entity);
         return AjaxResult.success("修改成功");
@@ -94,6 +97,7 @@ public class DisEventGradeController {
     @DeleteMapping("/deleteById")
     @ApiOperation("删除分级标准")
     @Log(title = "删除分级标准", businessType = BusinessType.DELETE)
+    @PreAuthorize("@ss.hasPermi('dispatch:grade:remove')")
     public AjaxResult deleteById(@RequestParam Long id) {
         boolean deleted = service.removeById(id);
         return deleted ? AjaxResult.success("删除成功") : AjaxResult.error("删除失败");

+ 13 - 7
pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/dispatch/DisPlanController.java

@@ -20,6 +20,7 @@ import org.springframework.web.bind.annotation.DeleteMapping;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestParam;
@@ -46,7 +47,7 @@ public class DisPlanController {
 
     @GetMapping("/findByPage")
     @ApiOperation("预案分页查询(审批页传 status=1 只看待审核)")
-    @Anonymous
+    @PreAuthorize("@ss.hasPermi('dispatch:plan:list')")
     public AjaxResult findByPage(PlanPageDTO dto) {
         Page<DisPlan> page = service.findByPage(dto);
         return AjaxResult.success(page);
@@ -54,14 +55,14 @@ public class DisPlanController {
 
     @GetMapping("/getDetail/{id}")
     @ApiOperation("预案详情")
-    @Anonymous
+    @PreAuthorize("@ss.hasPermi('dispatch:plan:list')")
     public AjaxResult getDetail(@PathVariable Long id) {
         return AjaxResult.success(service.getDetail(id));
     }
 
     @GetMapping("/statusOptions")
     @ApiOperation("预案状态下拉项(PlanStatusEnum,供前端筛选使用)")
-    @Anonymous
+    @PreAuthorize("@ss.hasPermi('dispatch:plan:list')")
     public AjaxResult statusOptions() {
         List<EnumOptionVO> options = Arrays.stream(PlanStatusEnum.values())
                 .map(item -> new EnumOptionVO(item.getCode(), item.getDesc()))
@@ -71,7 +72,7 @@ public class DisPlanController {
 
     @GetMapping("/templates")
     @ApiOperation("预案模板列表(新建预案时可选模板套用标准章节结构)")
-    @Anonymous
+    @PreAuthorize("@ss.hasPermi('dispatch:plan:list')")
     public AjaxResult templates(PlanPageDTO dto) {
         dto.setIsTemplate(1);
         return AjaxResult.success(service.findByPage(dto));
@@ -79,21 +80,21 @@ public class DisPlanController {
 
     @GetMapping("/versions/{planId}")
     @ApiOperation("预案版本列表(当前生效版本在前,不含回滚)")
-    @Anonymous
+    @PreAuthorize("@ss.hasPermi('dispatch:plan:list')")
     public AjaxResult versions(@PathVariable Long planId) {
         return AjaxResult.success(service.listVersions(planId));
     }
 
     @GetMapping("/version/{versionId}")
     @ApiOperation("单个版本正文快照")
-    @Anonymous
+    @PreAuthorize("@ss.hasPermi('dispatch:plan:list')")
     public AjaxResult version(@PathVariable Long versionId) {
         return AjaxResult.success(service.getVersion(versionId));
     }
 
     @GetMapping("/logs/{planId}")
     @ApiOperation("预案操作日志(新建/提交审核/审批通过/审批驳回/修订)")
-    @Anonymous
+    @PreAuthorize("@ss.hasPermi('dispatch:plan:list')")
     public AjaxResult logs(@PathVariable Long planId) {
         return AjaxResult.success(service.listLogs(planId));
     }
@@ -101,6 +102,7 @@ public class DisPlanController {
     @PostMapping("/save")
     @ApiOperation("新增草稿 / 修改预案 / 修订已通过预案(修订会生成审核中的候选版本)")
     @Log(title = "保存预案", businessType = BusinessType.INSERT)
+    @PreAuthorize("@ss.hasAnyPermi('dispatch:plan:add,dispatch:plan:edit')")
     public AjaxResult save(@RequestBody PlanSaveDTO dto) {
         DisPlan saved = service.savePlan(dto);
         return AjaxResult.success("保存成功", saved);
@@ -109,6 +111,7 @@ public class DisPlanController {
     @DeleteMapping("/deleteById")
     @ApiOperation("删除预案(仅草稿与已驳回可删)")
     @Log(title = "删除预案", businessType = BusinessType.DELETE)
+    @PreAuthorize("@ss.hasPermi('dispatch:plan:remove')")
     public AjaxResult deleteById(@RequestParam Long id) {
         service.deletePlan(id);
         return AjaxResult.success("删除成功");
@@ -117,6 +120,7 @@ public class DisPlanController {
     @PostMapping("/submit/{id}")
     @ApiOperation("提交审核(草稿 / 已驳回 → 待审核)")
     @Log(title = "提交预案审核", businessType = BusinessType.UPDATE)
+    @PreAuthorize("@ss.hasPermi('dispatch:plan:submit')")
     public AjaxResult submit(@PathVariable Long id) {
         service.submit(id);
         return AjaxResult.success("提交审核成功");
@@ -125,6 +129,7 @@ public class DisPlanController {
     @PostMapping("/approve/{id}")
     @ApiOperation("审批通过(候选版本生效,旧版本降为历史)")
     @Log(title = "审批通过预案", businessType = BusinessType.UPDATE)
+    @PreAuthorize("@ss.hasPermi('dispatch:plan:approve')")
     public AjaxResult approve(@PathVariable Long id, @RequestBody(required = false) PlanApproveDTO dto) {
         service.approve(id, dto == null ? new PlanApproveDTO() : dto);
         return AjaxResult.success("审批通过");
@@ -133,6 +138,7 @@ public class DisPlanController {
     @PostMapping("/reject/{id}")
     @ApiOperation("审批不通过(候选版本驳回,原生效版本仍生效)")
     @Log(title = "驳回预案", businessType = BusinessType.UPDATE)
+    @PreAuthorize("@ss.hasPermi('dispatch:plan:approve')")
     public AjaxResult reject(@PathVariable Long id, @RequestBody PlanApproveDTO dto) {
         service.reject(id, dto);
         return AjaxResult.success("已驳回");

+ 4 - 1
pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/dispatch/DisPlanLibraryController.java

@@ -13,6 +13,7 @@ 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.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 
 import java.util.List;
@@ -28,7 +29,7 @@ public class DisPlanLibraryController {
 
     @GetMapping("/list")
     @ApiOperation("查询数字预案库全部预案")
-    @Anonymous
+    @PreAuthorize("@ss.hasPermi('dispatch:planLibrary:list')")
     public AjaxResult list() {
         return AjaxResult.success(service.listAll());
     }
@@ -36,6 +37,7 @@ public class DisPlanLibraryController {
     @PostMapping("/save")
     @ApiOperation("新增/编辑模板(按 id 判空)")
     @Log(title = "预案模板保存", businessType = BusinessType.UPDATE)
+    @PreAuthorize("@ss.hasPermi('dispatch:planLibrary:add')")
     public AjaxResult save(@RequestBody com.zksy.dispatch.domain.DisPlanLibrary entity) {
         if (entity == null || org.apache.commons.lang3.StringUtils.isBlank(entity.getPlanName())) {
             return AjaxResult.error("请输入模板名称");
@@ -59,6 +61,7 @@ public class DisPlanLibraryController {
     @DeleteMapping("/{id}")
     @ApiOperation("删除模板")
     @Log(title = "预案模板删除", businessType = BusinessType.DELETE)
+    @PreAuthorize("@ss.hasPermi('dispatch:planLibrary:remove')")
     public AjaxResult remove(@PathVariable Long id) {
         service.removeById(id);
         return AjaxResult.success();

+ 4 - 8
pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/domain/DisEvent.java

@@ -16,16 +16,12 @@ import java.time.LocalDateTime;
 
 /**
  * 应急事件主表(dis_event)
- *
- * <p>一个事件一行,承载全生命周期「当前值」;过程留痕一律写 {@link DisEventLog}。</p>
- *
- * <p>状态机:
+ * 一个事件一行,承载全生命周期「当前值」;过程留痕一律写 DisEventLog。
+ * 状态机:
  * 0 待核实 → 1 已核实 → 2 处置中 → 3 已解除;
  * 旁路 4 不实事件(初判核实判定为不实,直接终止)。终态为 3、4。
- * 转接、报送、处置跟踪、工作催办均不改变状态。</p>
- *
- * <p>字段统一加 {@code event_} 前缀,规避 KingbaseES 下 {@code level} 为保留字需转义的问题。</p>
- *
+ * 转接、报送、处置跟踪、工作催办均不改变状态。
+ * 字段统一加 event_ 前缀,规避 KingbaseES 下 level 为保留字需转义的问题。
  * @author zengy
  */
 @Data

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

@@ -15,17 +15,15 @@ import java.time.LocalDateTime;
 
 /**
  * 应急事件分级分类标准(dis_event_grade)
- *
- * <p>改造要点:</p>
+ * 改造要点:
  * <ul>
- *   <li>补业务编号 {@code gradeNo}(GJ+yyyyMMdd+3位随机),主键仍为 int8 自增</li>
- *   <li>原 {@code category} / {@code level} 改名 {@code event_category} / {@code event_level},
- *       复用字典 {@code dis_event_category} / {@code dis_event_level},顺带消掉
- *       {@code level} 保留字要写 {@code "level"} 转义的坑</li>
- *   <li>{@code status} 由中文 varchar 改为 int2:见 {@link com.zksy.dispatch.enums.GradeStatusEnum}</li>
- *   <li>补 {@code response} 响应要求列(原型「应急预案分级」有此列)</li>
+ * <li>补业务编号 gradeNo(GJ+yyyyMMdd+3位随机),主键仍为 int8 自增</li>
+ * <li>原 category / level 改名 event_category / event_level,
+ * 复用字典 dis_event_category / dis_event_level,顺带消掉
+ * level 保留字要写 "level" 转义的坑</li>
+ * <li>status 由中文 varchar 改为 int2:见 com.zksy.dispatch.enums.GradeStatusEnum</li>
+ * <li>补 response 响应要求列(原型「应急预案分级」有此列)</li>
  * </ul>
- *
  * @author zengy
  */
 @Data

+ 4 - 7
pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/domain/DisEventLog.java

@@ -14,13 +14,10 @@ import java.time.LocalDateTime;
 
 /**
  * 应急事件操作日志(dis_event_log)
- *
- * <p>一个事件 N 行,记录全部环节动作与状态变更轨迹。
- * 替代旧模型的 dis_event_transfer_log / dis_event_track_log / dis_event_urge_detail 三张明细表。</p>
- *
- * <p>oper_type 取值见 {@link com.zksy.dispatch.enums.EventLogOperTypeEnum};
- * 日志完全由业务代码写入,不使用触发器或自定义函数。</p>
- *
+ * 一个事件 N 行,记录全部环节动作与状态变更轨迹。
+ * 替代旧模型的 dis_event_transfer_log / dis_event_track_log / dis_event_urge_detail 三张明细表。
+ * oper_type 取值见 com.zksy.dispatch.enums.EventLogOperTypeEnum;
+ * 日志完全由业务代码写入,不使用触发器或自定义函数。
  * @author zengy
  */
 @Data

+ 4 - 7
pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/domain/DisPlan.java

@@ -15,14 +15,11 @@ import java.time.LocalDateTime;
 
 /**
  * 应急预案主表(dis_plan)
- *
- * <p>原型「预案编辑」与「预案审批」是同一个预案的两个视图,共用本表:
- * 编辑页写草稿 / 提交审核,审批页按 {@code status=1 待审核} 过滤后审批。</p>
- *
- * <p>状态机:0 草稿 → 1 待审核 → 2 已通过 / 3 已驳回。
+ * 原型「预案编辑」与「预案审批」是同一个预案的两个视图,共用本表:
+ * 编辑页写草稿 / 提交审核,审批页按 status=1 待审核 过滤后审批。
+ * 状态机:0 草稿 → 1 待审核 → 2 已通过 / 3 已驳回。
  * 已通过的预案再次编辑属于「修订」,会生成一条审核中的候选版本,
- * 当前生效版本在 {@link DisPlanVersion#getVerStatus()} 为 1 的行上。</p>
- *
+ * 当前生效版本在 DisPlanVersion#getVerStatus() 为 1 的行上。
  * @author zengy
  */
 @Data

+ 3 - 6
pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/domain/DisPlanLog.java

@@ -14,12 +14,9 @@ import java.time.LocalDateTime;
 
 /**
  * 应急预案操作日志(dis_plan_log)
- *
- * <p>替代旧模型 dis_plan_approval + dis_plan_approval_step 两张表:
- * 原型审批是单环节「通过 / 不通过 + 意见」,不需要环节配置表,一次操作一行日志即可。</p>
- *
- * <p>日志完全由业务代码写入,不使用触发器或自定义函数。</p>
- *
+ * 替代旧模型 dis_plan_approval + dis_plan_approval_step 两张表:
+ * 原型审批是单环节「通过 / 不通过 + 意见」,不需要环节配置表,一次操作一行日志即可。
+ * 日志完全由业务代码写入,不使用触发器或自定义函数。
  * @author zengy
  */
 @Data

+ 4 - 7
pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/domain/DisPlanVersion.java

@@ -14,15 +14,12 @@ import java.time.LocalDateTime;
 
 /**
  * 应急预案版本快照(dis_plan_version)
- *
- * <p>版本只在预案正文「审核通过」时生效,因此本表同时承担两种角色:</p>
+ * 版本只在预案正文「审核通过」时生效,因此本表同时承担两种角色:
  * <ul>
- *   <li>{@code ver_status=1} 当前生效版本 —— 预案正式发布的内容</li>
- *   <li>{@code ver_status=2} 审核中候选版本 —— 修订已通过预案后产生,审批通过才转 1</li>
+ * <li>ver_status=1 当前生效版本 —— 预案正式发布的内容</li>
+ * <li>ver_status=2 审核中候选版本 —— 修订已通过预案后产生,审批通过才转 1</li>
  * </ul>
- *
- * <p>与项目约定一致:版本表不做逻辑删除,删除预案时物理清理本预案的版本行。</p>
- *
+ * 与项目约定一致:版本表不做逻辑删除,删除预案时物理清理本预案的版本行。
  * @author zengy
  */
 @Data

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

@@ -14,7 +14,6 @@ import java.time.LocalDateTime;
 
 /**
  * 催办系统消息通知(工作催办勾选「系统消息」时按用户落一条站内消息)
- *
  * @author zengy
  */
 @Data

+ 4 - 6
pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/dto/DisDeptOptionVO.java

@@ -8,12 +8,10 @@ import java.io.Serializable;
 
 /**
  * 责任单位下拉项(系统部门)
- *
- * <p>初判核实时「责任单位」不再手填,改为选择系统部门(sys_dept);前端用
- * {@code deptId} 树形展示并用它过滤责任处置人候选(该部门所属人员),提交时把
- * {@code deptName} 写入 {@code dis_event.department}(该列存部门中文名,工作催办、
- * 多条件筛选均按名称口径,保持不变)。</p>
- *
+ * 初判核实时「责任单位」不再手填,改为选择系统部门(sys_dept);前端用
+ * deptId 树形展示并用它过滤责任处置人候选(该部门所属人员),提交时把
+ * deptName 写入 dis_event.department(该列存部门中文名,工作催办、
+ * 多条件筛选均按名称口径,保持不变)。
  * @author zengy
  */
 @Data

+ 5 - 3
pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/dto/DisEventPageDTO.java

@@ -9,9 +9,8 @@ import java.util.List;
 
 /**
  * 应急事件分页查询入参
- *
- * <p>接报管理 / 初判核实 / 转接报送 / 跟踪解除共用一套筛选条件,
- * 通过 status / statusList 区分各页面所处的环节。</p>
+ * 接报管理 / 初判核实 / 转接报送 / 跟踪解除共用一套筛选条件,
+ * 通过 status / statusList 区分各页面所处的环节。
  */
 @Data
 @ApiModel(value = "应急事件分页查询入参", description = "应急事件分页查询入参")
@@ -64,6 +63,9 @@ public class DisEventPageDTO implements Serializable {
     @ApiModelProperty(value = "直接经济损失区间:0-1万 1-10万 10-100万 100万以上")
     private String lossRange;
 
+    @ApiModelProperty(value = "排序方向:true=接报时间升序(先报先办的队列页),不传或 false=倒序(记录回看页)")
+    private Boolean orderAsc;
+
     @ApiModelProperty(value = "当前记录起始索引", required = true)
     private Long pageNum = 1L;
 

+ 2 - 3
pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/dto/DisEventVO.java

@@ -13,9 +13,8 @@ import java.util.List;
 
 /**
  * 应急事件展示 VO
- *
- * <p>在 dis_event 主表字段基础上,补充由 dis_event_log 聚合出的最近环节信息,
- * 供接报管理 / 初判核实 / 转接报送 / 工作催办 / 跟踪解除各页面直接展示。</p>
+ * 在 dis_event 主表字段基础上,补充由 dis_event_log 聚合出的最近环节信息,
+ * 供接报管理 / 初判核实 / 转接报送 / 工作催办 / 跟踪解除各页面直接展示。
  */
 @Data
 @ApiModel(value = "应急事件展示VO", description = "应急事件列表与详情展示VO")

+ 8 - 10
pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/dto/DisHomeVO.java

@@ -13,18 +13,16 @@ import java.util.Map;
 
 /**
  * 调度首页看板 VO
- *
- * <p>一次请求返回首页全部数据:顶部统计卡、GIS 点位、处置中事件列表、
- * 近 7 日管网类型分布与事件等级占比。统计口径:</p>
+ * 一次请求返回首页全部数据:顶部统计卡、GIS 点位、处置中事件列表、
+ * 近 7 日管网类型分布与事件等级占比。统计口径:
  * <ul>
- *   <li>今日接报 / 较昨日:按 reportTime 落在今日 / 昨日的事件数;</li>
- *   <li>处置中:status=2,其中等级为 Ⅰ级(紧急) 的单独计数;</li>
- *   <li>已解除:status=3;处置率 = (已解除 + 处置中) / 全部事件 × 100;</li>
- *   <li>可用预案:dis_plan.status=2(已通过);已发布:存在 verStatus=1(当前版本)的预案版本数;</li>
- *   <li>类型分布:近 7 日按 event_type 分组(type 为字典编码,前端 useDict 翻译);</li>
- *   <li>等级占比:已解除单列一段,其余按 event_level(1紧急/2较大/3一般)分组。</li>
+ * <li>今日接报 / 较昨日:按 reportTime 落在今日 / 昨日的事件数;</li>
+ * <li>处置中:status=2,其中等级为 Ⅰ级(紧急) 的单独计数;</li>
+ * <li>已解除:status=3;处置率 = (已解除 + 处置中) / 全部事件 × 100;</li>
+ * <li>可用预案:dis_plan.status=2(已通过);已发布:存在 verStatus=1(当前版本)的预案版本数;</li>
+ * <li>类型分布:近 7 日按 event_type 分组(type 为字典编码,前端 useDict 翻译);</li>
+ * <li>等级占比:已解除单列一段,其余按 event_level(1紧急/2较大/3一般)分组。</li>
  * </ul>
- *
  * @author zengy
  */
 @Data

+ 3 - 5
pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/dto/DisUserOptionVO.java

@@ -8,11 +8,9 @@ import java.io.Serializable;
 
 /**
  * 责任处置人下拉项(系统用户)
- *
- * <p>初判核实时「责任处置人」不再手填,改为选择系统用户;选中后前端用
- * {@code phonenumber} 自动带出「联系电话」,用 {@code nickName} 写入
- * {@code dis_event.handler}(工作催办即按该字段找催办对象,故必须是可读姓名)。</p>
- *
+ * 初判核实时「责任处置人」不再手填,改为选择系统用户;选中后前端用
+ * phonenumber 自动带出「联系电话」,用 nickName 写入
+ * dis_event.handler(工作催办即按该字段找催办对象,故必须是可读姓名)。
  * @author zengy
  */
 @Data

+ 2 - 3
pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/dto/EnumOptionVO.java

@@ -10,9 +10,8 @@ import java.io.Serializable;
 
 /**
  * 枚举下拉项
- *
- * <p>状态类字段取值走 Java 枚举(从 0 起始),前端筛选下拉需要「编码 + 中文」列表。
- * 由后端把枚举暴露成选项,避免把中文常量复制到 Vue 里造成两处维护。</p>
+ * 状态类字段取值走 Java 枚举(从 0 起始),前端筛选下拉需要「编码 + 中文」列表。
+ * 由后端把枚举暴露成选项,避免把中文常量复制到 Vue 里造成两处维护。
  */
 @Data
 @AllArgsConstructor

+ 1 - 2
pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/dto/EventReleaseDTO.java

@@ -9,8 +9,7 @@ import java.math.BigDecimal;
 
 /**
  * 事件解除入参
- *
- * <p>状态 2 处置中 → 3 已解除。</p>
+ * 状态 2 处置中 → 3 已解除。
  */
 @Data
 @ApiModel(value = "事件解除入参", description = "事件解除入参")

+ 2 - 3
pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/dto/EventSaveDTO.java

@@ -11,10 +11,9 @@ import java.time.LocalDateTime;
 
 /**
  * 接报登记 新增 / 编辑 入参
- *
- * <p>只承载「接报登记」环节可填的业务字段:
+ * 只承载「接报登记」环节可填的业务字段:
  * 事件编号、事件状态由后端生成,不能由前端传入;
- * 核实结论、等级、责任单位等属于后续环节,走各自的动作接口。</p>
+ * 核实结论、等级、责任单位等属于后续环节,走各自的动作接口。
  */
 @Data
 @ApiModel(value = "接报登记入参", description = "接报登记新增与编辑入参")

+ 2 - 3
pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/dto/EventTrackDTO.java

@@ -10,9 +10,8 @@ import java.time.LocalDateTime;
 
 /**
  * 开始处置 / 处置跟踪入参
- *
- * <p>两者都是写 dis_event_log:开始处置额外把状态 1 已核实 → 2 处置中;
- * 处置跟踪可多次记录,<b>不改变状态</b>,但会把回访时间与是否需要升级回填到主表当前值。</p>
+ * 两者都是写 dis_event_log:开始处置额外把状态 1 已核实 → 2 处置中;
+ * 处置跟踪可多次记录,不改变状态,但会把回访时间与是否需要升级回填到主表当前值。
  */
 @Data
 @ApiModel(value = "处置跟踪入参", description = "开始处置与处置跟踪入参")

+ 1 - 2
pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/dto/EventTransferDTO.java

@@ -9,8 +9,7 @@ import java.util.List;
 
 /**
  * 转接 / 报送入参
- *
- * <p>转接与报送均为「已核实」之后的操作记录,<b>只写 dis_event_log,不改变事件状态</b>。</p>
+ * 转接与报送均为「已核实」之后的操作记录,只写 dis_event_log,不改变事件状态。
  */
 @Data
 @ApiModel(value = "转接报送入参", description = "转接报送入参")

+ 2 - 3
pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/dto/EventUrgeDTO.java

@@ -8,9 +8,8 @@ import java.io.Serializable;
 
 /**
  * 工作催办入参
- *
- * <p>催办不建独立表:一次催办就是 dis_event_log 里 operType=8 的一条记录,
- * 列表页的「催办次数」「最近催办时间」由日志聚合得出,状态取事件当前状态。</p>
+ * 催办不建独立表:一次催办就是 dis_event_log 里 operType=8 的一条记录,
+ * 列表页的「催办次数」「最近催办时间」由日志聚合得出,状态取事件当前状态。
  */
 @Data
 @ApiModel(value = "工作催办入参", description = "工作催办入参")

+ 2 - 3
pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/dto/EventUrgeRowVO.java

@@ -10,9 +10,8 @@ import java.time.LocalDateTime;
 
 /**
  * 工作催办列表行
- *
- * <p>按原型口径:一行 = 一个事件 + 最近一次催办信息 + 催办次数,
- * 「催办事项」即事件描述,「状态」即事件当前状态。</p>
+ * 按原型口径:一行 = 一个事件 + 最近一次催办信息 + 催办次数,
+ * 「催办事项」即事件描述,「状态」即事件当前状态。
  */
 @Data
 @ApiModel(value = "工作催办列表行", description = "工作催办列表行")

+ 2 - 3
pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/dto/EventVerifyDTO.java

@@ -8,9 +8,8 @@ import java.io.Serializable;
 
 /**
  * 初判核实入参
- *
- * <p>真实性判定为「真实」时补齐管网类型 / 事件类别 / 事件等级 / 责任处置人;
- * 判定为「不实」时只需填写核销原因,事件直接结束。</p>
+ * 真实性判定为「真实」时补齐管网类型 / 事件类别 / 事件等级 / 责任处置人;
+ * 判定为「不实」时只需填写核销原因,事件直接结束。
  */
 @Data
 @ApiModel(value = "初判核实入参", description = "初判核实入参")

+ 2 - 3
pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/dto/PlanApproveDTO.java

@@ -8,9 +8,8 @@ import java.io.Serializable;
 
 /**
  * 预案审批入参
- *
- * <p>审批为单环节:通过 / 不通过 + 意见,通过与否由调用的接口决定
- * ({@code /approve/{id}} 或 {@code /reject/{id}})。</p>
+ * 审批为单环节:通过 / 不通过 + 意见,通过与否由调用的接口决定
+ * (/approve/{id} 或 /reject/{id})。
  */
 @Data
 @ApiModel(value = "预案审批入参", description = "预案审批入参")

+ 1 - 2
pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/dto/PlanPageDTO.java

@@ -8,8 +8,7 @@ import java.io.Serializable;
 
 /**
  * 预案分页查询入参
- *
- * <p>预案编辑与预案审批共用:审批页传 {@code status=1} 即只看待审核。</p>
+ * 预案编辑与预案审批共用:审批页传 status=1 即只看待审核。
  */
 @Data
 @ApiModel(value = "预案分页查询入参", description = "预案分页查询入参")

+ 3 - 5
pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/dto/PlanSaveDTO.java

@@ -8,11 +8,9 @@ import java.io.Serializable;
 
 /**
  * 预案新增 / 编辑入参
- *
- * <p>「草稿 / 已驳回」保存是直接改本预案当前值;
- * 「已通过」保存视为<b>修订</b>,会生成一条审核中的候选版本,审批通过才替换生效版本。</p>
- *
- * <p>预案编号与当前生效版本号均由后端管理,不由前端传入。</p>
+ * 「草稿 / 已驳回」保存是直接改本预案当前值;
+ * 「已通过」保存视为修订,会生成一条审核中的候选版本,审批通过才替换生效版本。
+ * 预案编号与当前生效版本号均由后端管理,不由前端传入。
  */
 @Data
 @ApiModel(value = "预案保存入参", description = "预案新增与编辑入参")

+ 1 - 3
pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/dto/SseMessageDto.java

@@ -9,9 +9,7 @@ import java.util.List;
 
 /**
  * SSE 消息体(轻量版,参照 RuoYi-Vue-Plus 5.x SseMessageDto 设计)
- *
- * <p>序列化后作为 EventSource message 事件负载下发,前端按 type 分发。</p>
- *
+ * 序列化后作为 EventSource message 事件负载下发,前端按 type 分发。
  * @author zengy
  */
 @Data

+ 3 - 5
pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/enums/EventLogOperTypeEnum.java

@@ -7,11 +7,9 @@ import java.util.Objects;
 
 /**
  * 应急事件操作日志类型枚举(dis_event_log.oper_type)
- *
- * <p>与事件生命周期各环节一一对应,从 0 开始编号。</p>
- * <p>其中 3 电话转接 / 4 上报报送 / 6 处置跟踪 / 8 工作催办 / 9 信息修改
- * <b>不改变事件状态</b>,写入日志时 status_from 与 status_to 相同或为 NULL。</p>
- *
+ * 与事件生命周期各环节一一对应,从 0 开始编号。
+ * 其中 3 电话转接 / 4 上报报送 / 6 处置跟踪 / 8 工作催办 / 9 信息修改
+ * 不改变事件状态,写入日志时 status_from 与 status_to 相同或为 NULL。
  * @author zengy
  */
 @Getter

+ 2 - 7
pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/enums/EventStatusEnum.java

@@ -11,13 +11,8 @@ import java.util.stream.Collectors;
 
 /**
  * 应急事件状态枚举(dis_event.status)
- *
- * <p>主流程:0 待核实 → 1 已核实 → 2 处置中 → 3 已解除</p>
- * <p>旁路:4 不实事件(初判核实判定为不实,事件直接结束)</p>
- *
- * <p>转接、报送、处置跟踪、工作催办只写 dis_event_log,<b>不改变事件状态</b>。</p>
- * <p>旧库中文状态映射:待受理/待核实→0,已核实→1,处置中→2,已解除/已关闭→3,不实事件→4。</p>
- *
+ * 主流程:0 待核实 → 1 已核实 → 2 处置中 → 3 已解除;旁路 4 不实事件(终态)。
+ * 转接、报送、处置跟踪、工作催办只写 dis_event_log,不改变事件状态。
  * @author zengy
  */
 @Getter

+ 2 - 4
pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/enums/GradeStatusEnum.java

@@ -7,10 +7,8 @@ import java.util.Objects;
 
 /**
  * 应急事件分级标准启用状态枚举(dis_event_grade.status)
- *
- * <p>取值从 0 起始,与其它枚举保持一致;等级标准本身不做停用删除,
- * 只通过本状态控制是否参与初判核实的辅助匹配。</p>
- *
+ * 取值从 0 起始,与其它枚举保持一致;等级标准本身不做停用删除,
+ * 只通过本状态控制是否参与初判核实的辅助匹配。
  * @author zengy
  */
 @Getter

+ 1 - 3
pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/enums/PlanLogOperTypeEnum.java

@@ -7,9 +7,7 @@ import java.util.Objects;
 
 /**
  * 应急预案操作日志类型枚举(dis_plan_log.oper_type)
- *
- * <p>从 0 开始编号。</p>
- *
+ * 从 0 开始编号。
  * @author zengy
  */
 @Getter

+ 3 - 5
pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/enums/PlanStatusEnum.java

@@ -7,11 +7,9 @@ import java.util.Objects;
 
 /**
  * 应急预案状态枚举(dis_plan.status)
- *
- * <p>流程:0 草稿 → 1 待审核 → 2 已通过 / 3 已驳回</p>
- * <p>「已驳回」可修改后重新提交,回到 1 待审核。</p>
- * <p>旧库状态映射:编辑中→0,审批中/终审中→1,已发布→2,待修正→3。</p>
- *
+ * 流程:0 草稿 → 1 待审核 → 2 已通过 / 3 已驳回
+ * 「已驳回」可修改后重新提交,回到 1 待审核。
+ * 旧库状态映射:编辑中→0,审批中/终审中→1,已发布→2,待修正→3。
  * @author zengy
  */
 @Getter

+ 4 - 6
pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/enums/PlanVersionStatusEnum.java

@@ -7,14 +7,12 @@ import java.util.Objects;
 
 /**
  * 应急预案版本状态枚举(dis_plan_version.ver_status)
- *
- * <p>版本是「审核通过」后落地的快照:</p>
+ * 版本是「审核通过」后落地的快照:
  * <ul>
- *   <li>修订已通过预案并保存 → 生成一条 2 审核中候选版本,旧版本仍为 1 当前生效</li>
- *   <li>审核通过 → 候选版本转 1 当前生效,旧版本降 0 历史</li>
- *   <li>审核不通过 → 候选版本转 3 已驳回,旧版本保持 1 当前生效</li>
+ * <li>修订已通过预案并保存 → 生成一条 2 审核中候选版本,旧版本仍为 1 当前生效</li>
+ * <li>审核通过 → 候选版本转 1 当前生效,旧版本降 0 历史</li>
+ * <li>审核不通过 → 候选版本转 3 已驳回,旧版本保持 1 当前生效</li>
  * </ul>
- *
  * @author zengy
  */
 @Getter

+ 2 - 3
pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/mapper/DisEventGradeMapper.java

@@ -26,11 +26,10 @@ public interface DisEventGradeMapper extends BaseMapper<DisEventGrade> {
 
     /**
      * 当前最大主键 id(显式发号用:id = max(id) + 1)
-     *
-     * <p>背景:本表数据由 MySQL 迁移时带显式 id 插入,identity 序列未对齐
+     * 背景:本表数据由 MySQL 迁移时带显式 id 插入,identity 序列未对齐
      * (自动发号会发出 0 撞主键);且 KES 的 pg_get_serial_sequence 对 identity
      * 列兼容性差(查不到/类型转换失败)。因此改为在取号锁内显式发号:
-     * GENERATED BY DEFAULT 模式允许显式 id,不依赖序列元数据,必定可执行。</p>
+     * GENERATED BY DEFAULT 模式允许显式 id,不依赖序列元数据,必定可执行。
      */
     @Select("SELECT COALESCE(MAX(id), 0) FROM app_user.dis_event_grade")
     @Options(useCache = false, flushCache = Options.FlushCachePolicy.TRUE)

+ 1 - 2
pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/mapper/DisEventMapper.java

@@ -12,8 +12,7 @@ public interface DisEventMapper extends BaseMapper<DisEvent> {
 
     /**
      * 事件编号生成事务锁(与 alarm_data 取号方式一致)。
-     *
-     * <p>取号与落库必须在同一事务内持锁,只有「查重」没有「加锁」仍会并发撞号。</p>
+     * 取号与落库必须在同一事务内持锁,只有「查重」没有「加锁」仍会并发撞号。
      */
     @Select("SELECT 1 FROM pg_advisory_xact_lock(hashtext('dispatch-event-number-generation'))")
     @Options(useCache = false, flushCache = Options.FlushCachePolicy.TRUE, timeout = 5)

+ 10 - 16
pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/mapper/DispatchUserMapper.java

@@ -11,12 +11,10 @@ import java.util.List;
 
 /**
  * 应急与指挥调度模块的人员引用查询(对 sys_user / sys_dept 只读)
- *
- * <p>本模块的业务表(事件 / 预案 / 预案版本 / 分级标准 / 事后评估)都按
+ * 本模块的业务表(事件 / 预案 / 预案版本 / 分级标准 / 事后评估)都按
  * 「用户ID + 名称快照」双存人员信息,因此这里集中提供两个查询:
- * ① 选人下拉(责任处置人);② 按ID批量反查昵称(列表展示时还原姓名)。
- * 集中放置避免在每个业务 Mapper 里重复同一段 SQL。</p>
- *
+ * 选人下拉(责任处置人); 按ID批量反查昵称(列表展示时还原姓名)。
+ * 集中放置避免在每个业务 Mapper 里重复同一段 SQL。
  * @author zengy
  */
 @Mapper
@@ -24,9 +22,8 @@ public interface DispatchUserMapper {
 
     /**
      * 选人下拉:全部启用状态的系统用户(昵称 + 联系电话 + 所属部门ID/名称)
-     *
-     * <p>「初判核实」责任处置人由此选择,选中后前端据 phonenumber 自动带出联系电话,
-     * 并据 deptId 把候选限定在所选责任单位内。</p>
+     * 「初判核实」责任处置人由此选择,选中后前端据 phonenumber 自动带出联系电话,
+     * 并据 deptId 把候选限定在所选责任单位内。
      */
     @Select("SELECT u.user_id, u.nick_name, u.phonenumber, u.dept_id, d.dept_name "
             + "FROM app_user.sys_user u "
@@ -37,9 +34,8 @@ public interface DispatchUserMapper {
 
     /**
      * 选部门下拉:全部正常状态的部门(ID + 父ID + 名称 + 显示顺序)
-     *
-     * <p>「初判核实」责任单位由此选择(原为手填);前端据 parentId 建树,
-     * 提交时取 deptName 写入 dis_event.department。</p>
+     * 「初判核实」责任单位由此选择(原为手填);前端据 parentId 建树,
+     * 提交时取 deptName 写入 dis_event.department。
      */
     @Select("SELECT d.dept_id, d.parent_id, d.dept_name, d.order_num "
             + "FROM app_user.sys_dept d "
@@ -49,8 +45,7 @@ public interface DispatchUserMapper {
 
     /**
      * 按用户昵称反查 userId(催办推送兜底:老数据 handler_id 为空时按接收人姓名找)
-     *
-     * <p>同名用户取最小 ID(异常数据兜底,正常命名应唯一);查不到返回 null。</p>
+     * 同名用户取最小 ID(异常数据兜底,正常命名应唯一);查不到返回 null。
      */
     @Select("SELECT min(u.user_id) FROM app_user.sys_user u "
             + "WHERE u.del_flag = '0' AND u.status = '0' AND u.nick_name = #{nickName}")
@@ -58,9 +53,8 @@ public interface DispatchUserMapper {
 
     /**
      * 按用户ID批量反查昵称,用于列表/详情把人员字段由「ID」还原为「姓名」展示。
-     *
-     * <p>只取 user_id + nick_name 两列;这里**不限定 status**——已停用(离职)用户的
-     * 历史操作仍应显示姓名,调用方在查不到时才退回名称快照。</p>
+     * 只取 user_id + nick_name 两列;这里不限定 status——已停用(离职)用户的
+     * 历史操作仍应显示姓名,调用方在查不到时才退回名称快照。
      */
     @Select("<script>"
             + "SELECT u.user_id, u.nick_name FROM app_user.sys_user u "

+ 5 - 9
pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/service/DisEventLogService.java

@@ -10,15 +10,13 @@ import java.util.List;
 
 /**
  * 应急事件操作日志 Service
- *
- * <p>全模块唯一的日志写入口:事件各环节的动作一律通过本 Service 落库,
- * 保证 dis_event_log 是事件生命周期轨迹的唯一来源。</p>
+ * 全模块唯一的日志写入口:事件各环节的动作一律通过本 Service 落库,
+ * 保证 dis_event_log 是事件生命周期轨迹的唯一来源。
  */
 public interface DisEventLogService extends IService<DisEventLog> {
 
     /**
      * 追加一条事件日志(自动补事件编号与操作人)
-     *
      * @param log 日志内容,eventId 与 operType 必填
      */
     void append(DisEventLog log);
@@ -40,11 +38,9 @@ public interface DisEventLogService extends IService<DisEventLog> {
 
     /**
      * 批量查询多个事件指定操作类型的日志,按操作时间正序返回。
-     *
-     * <p>列表页展示「最近一次转接对象」「催办次数」「最近催办时间」时用它一次取回,
-     * 避免按行循环查库。</p>
-     *
-     * @param eventIds  事件ID集合,为空时返回空列表
+     * 列表页展示「最近一次转接对象」「催办次数」「最近催办时间」时用它一次取回,
+     * 避免按行循环查库。
+     * @param eventIds 事件ID集合,为空时返回空列表
      * @param operTypes 操作类型集合,为空时不限制类型
      */
     List<DisEventLog> listByEventIds(Collection<Long> eventIds, Collection<Integer> operTypes);

+ 6 - 10
pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/service/DisEventService.java

@@ -22,12 +22,10 @@ import java.util.List;
 
 /**
  * 应急事件统一档案 Service(全生命周期闭环)
- *
- * <p>生命周期:接报登记(0 待核实)→ 初判核实(1 已核实 / 4 不实事件)
- * → 开始处置(2 处置中)→ 事件解除(3 已解除)。</p>
- *
- * <p>转接、报送、处置跟踪、工作催办都是「动作留痕」:只追加 dis_event_log,
- * <b>不改变事件状态</b>。</p>
+ * 生命周期:接报登记(0 待核实)→ 初判核实(1 已核实 / 4 不实事件)
+ * → 开始处置(2 处置中)→ 事件解除(3 已解除)。
+ * 转接、报送、处置跟踪、工作催办都是「动作留痕」:只追加 dis_event_log,
+ * 不改变事件状态。
  */
 public interface DisEventService extends IService<DisEvent> {
 
@@ -53,15 +51,13 @@ public interface DisEventService extends IService<DisEvent> {
 
     /**
      * 责任处置人候选用户列表(启用状态的系统用户:昵称 + 联系电话 + 所属部门)
-     *
-     * <p>「初判核实」责任处置人选择用;选中后前端据 phonenumber 自动带出联系电话。</p>
+     * 「初判核实」责任处置人选择用;选中后前端据 phonenumber 自动带出联系电话。
      */
     List<DisUserOptionVO> listUserOptions();
 
     /**
      * 责任单位候选部门列表(正常状态的 sys_dept,前端据 parentId 建树)
-     *
-     * <p>「初判核实」责任单位选择用;提交时取部门名称写入 dis_event.department。</p>
+     * 「初判核实」责任单位选择用;提交时取部门名称写入 dis_event.department。
      */
     List<DisDeptOptionVO> listDeptOptions();
 

+ 2 - 5
pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/service/DisPlanLibraryService.java

@@ -17,8 +17,7 @@ public interface DisPlanLibraryService extends IService<DisPlanLibrary> {
 
     /**
      * 按事件条件加权匹配预案(类型40/级别25/区域20/单位15)
-     *
-     * @param dto      匹配条件
+     * @param dto 匹配条件
      * @param operUser 操作人
      * @return 按匹配度降序的结果,并记录匹配历史
      */
@@ -26,8 +25,7 @@ public interface DisPlanLibraryService extends IService<DisPlanLibrary> {
 
     /**
      * 按专题匹配预案
-     *
-     * @param topic    专题名称
+     * @param topic 专题名称
      * @param operUser 操作人
      * @return 该专题下的预案列表(匹配度90),并记录匹配历史
      */
@@ -35,7 +33,6 @@ public interface DisPlanLibraryService extends IService<DisPlanLibrary> {
 
     /**
      * 查询最近匹配/应用记录
-     *
      * @param limit 记录条数
      */
     List<DisPlanMatchRecord> getHistory(int limit);

+ 1 - 3
pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/service/DisPlanLogService.java

@@ -7,14 +7,12 @@ import java.util.List;
 
 /**
  * 应急预案操作日志 Service
- *
- * <p>预案域唯一的日志写入口,日志只增不改不删,全部由业务代码写入。</p>
+ * 预案域唯一的日志写入口,日志只增不改不删,全部由业务代码写入。
  */
 public interface DisPlanLogService extends IService<DisPlanLog> {
 
     /**
      * 追加一条预案日志(自动补操作人与操作时间)
-     *
      * @param log 日志内容,planId 与 operType 必填
      */
     void append(DisPlanLog log);

+ 2 - 3
pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/service/DisPlanService.java

@@ -13,10 +13,9 @@ import java.util.List;
 
 /**
  * 应急预案 Service(编辑 + 审批共用一套数据)
- *
- * <p>流程:0 草稿 → 1 待审核 → 2 已通过 / 3 已驳回。
+ * 流程:0 草稿 → 1 待审核 → 2 已通过 / 3 已驳回。
  * 已通过的预案再次保存视为「修订」,生成审核中的候选版本,
- * 审批通过才把候选版本提升为当前生效版本。</p>
+ * 审批通过才把候选版本提升为当前生效版本。
  */
 public interface DisPlanService extends IService<DisPlan> {
 

+ 5 - 8
pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/service/DispatchNotifyService.java

@@ -7,27 +7,24 @@ import java.util.List;
 
 /**
  * 催办系统消息通知 Service
- *
  * @author zengy
  */
 public interface DispatchNotifyService extends IService<DispatchNotify> {
 
     /**
      * 推送一条催办通知(推送失败不阻断催办主流程,仅记日志)
-     *
-     * @param userId     接收人用户ID
-     * @param eventId    事件ID
-     * @param eventNo    事件编号
-     * @param content    催办内容
+     * @param userId 接收人用户ID
+     * @param eventId 事件ID
+     * @param eventNo 事件编号
+     * @param content 催办内容
      * @param urgingUser 催办人姓名
      */
     void pushUrging(Long userId, Long eventId, String eventNo, String content, String urgingUser);
 
     /**
      * 查询当前用户的通知(最近 limit 条,未读在前)
-     *
      * @param userId 用户ID
-     * @param limit  条数
+     * @param limit 条数
      * @return 通知列表
      */
     List<DispatchNotify> listMy(Long userId, int limit);

+ 2 - 3
pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/service/impl/DisEventGradeServiceImpl.java

@@ -49,8 +49,7 @@ public class DisEventGradeServiceImpl extends ServiceImpl<DisEventGradeMapper, D
         } catch (DateTimeParseException e) {
             log.warn("分级标准查询日期格式错误: {} ~ {}", dto.getStartDate(), dto.getEndDate());
         }
-        wrapper.orderByAsc(DisEventGrade::getEventCategory).orderByAsc(DisEventGrade::getEventLevel);
-        wrapper.orderByAsc(DisEventGrade::getId);
+        wrapper.orderByDesc(DisEventGrade::getCreateTime).orderByDesc(DisEventGrade::getId);
         Page<DisEventGrade> result = page(page, wrapper);
         result.getRecords().forEach(this::fillStatusName);
         return result;
@@ -82,7 +81,7 @@ public class DisEventGradeServiceImpl extends ServiceImpl<DisEventGradeMapper, D
         LocalDateTime now = LocalDateTime.now();
         // 本表数据由 MySQL 迁移时带显式 id 插入,identity 序列未对齐(自动发号发 0 撞主键);
         // 且 KES 的序列元数据查询兼容性差(pg_get_serial_sequence 查不到、|| 是逻辑或不能拼串)。
-        // 因此在取号锁内**显式发号**:id = max(id) + 1,不依赖序列,必定可执行。
+        // 因此在取号锁内显式发号:id = max(id) + 1,不依赖序列,必定可执行。
         Long maxId = baseMapper.selectMaxId();
         entity.setId((maxId == null ? 0L : maxId) + 1);
         entity.setGradeNo(DispatchNumberSupport.nextGradeNo(baseMapper::existsGradeNo));

+ 13 - 5
pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/service/impl/DisEventLogServiceImpl.java

@@ -8,9 +8,11 @@ import com.zksy.dispatch.dto.EventLogPageDTO;
 import com.zksy.dispatch.enums.EventLogOperTypeEnum;
 import com.zksy.dispatch.mapper.DisEventLogMapper;
 import com.zksy.dispatch.service.DisEventLogService;
+import com.zksy.dispatch.support.DispatchAttachUrlSupport;
 import com.zksy.dispatch.support.DispatchUserSupport;
 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.util.CollectionUtils;
 
@@ -21,14 +23,17 @@ import java.util.List;
 
 /**
  * 应急事件操作日志 Service 实现
- *
- * <p>日志只增不改不删,全部由业务代码写入。</p>
+ * 日志只增不改不删,全部由业务代码写入。
  */
 @Slf4j
 @Service
 public class DisEventLogServiceImpl extends ServiceImpl<DisEventLogMapper, DisEventLog>
         implements DisEventLogService {
 
+    /** 附件地址支撑:日志附件库内相对路径 → 出口拼 MinIO 配置地址 */
+    @Autowired
+    private DispatchAttachUrlSupport attachUrlSupport;
+
     @Override
     public void append(DisEventLog log) {
         if (log.getEventId() == null) {
@@ -52,9 +57,10 @@ public class DisEventLogServiceImpl extends ServiceImpl<DisEventLogMapper, DisEv
     @Override
     public Page<DisEventLog> findByPage(EventLogPageDTO dto) {
         Page<DisEventLog> page = new Page<>(dto.getPageNum(), dto.getPageSize());
-        Page<DisEventLog> result = this.page(page, buildWrapper(dto));
-        result.getRecords().forEach(this::fillOperTypeName);
-        return result;
+        List<DisEventLog> result = this.page(page, buildWrapper(dto)).getRecords();
+        result.forEach(this::fillOperTypeName);
+        result.forEach(l -> l.setAttachUrl(attachUrlSupport.toAccessibleUrl(l.getAttachUrl())));
+        return page;
     }
 
     @Override
@@ -64,6 +70,7 @@ public class DisEventLogServiceImpl extends ServiceImpl<DisEventLogMapper, DisEv
         wrapper.orderByAsc(DisEventLog::getOperTime).orderByAsc(DisEventLog::getId);
         List<DisEventLog> list = this.list(wrapper);
         list.forEach(this::fillOperTypeName);
+        list.forEach(l -> l.setAttachUrl(attachUrlSupport.toAccessibleUrl(l.getAttachUrl())));
         return list;
     }
 
@@ -91,6 +98,7 @@ public class DisEventLogServiceImpl extends ServiceImpl<DisEventLogMapper, DisEv
         wrapper.orderByAsc(DisEventLog::getOperTime).orderByAsc(DisEventLog::getId);
         List<DisEventLog> list = this.list(wrapper);
         list.forEach(this::fillOperTypeName);
+        list.forEach(l -> l.setAttachUrl(attachUrlSupport.toAccessibleUrl(l.getAttachUrl())));
         return list;
     }
 

+ 43 - 26
pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/service/impl/DisEventServiceImpl.java

@@ -22,6 +22,7 @@ import com.zksy.dispatch.dto.EventUrgePageDTO;
 import com.zksy.dispatch.dto.EventUrgeRowVO;
 import com.zksy.dispatch.dto.EventVerifyDTO;
 import com.zksy.dispatch.dto.EventReleaseDTO;
+import com.zksy.dispatch.support.DispatchAttachUrlSupport;
 import com.zksy.dispatch.dto.SseMessageDto;
 import com.zksy.dispatch.enums.EventLogOperTypeEnum;
 import com.zksy.dispatch.enums.EventStatusEnum;
@@ -61,14 +62,9 @@ import java.util.Objects;
 import java.util.stream.Collectors;
 
 /**
- * 应急事件统一档案 Service 实现(全生命周期闭环)。
- *
- * <p>改造要点:所有环节共用一张 {@code dis_event} 主表 + 一张 {@code dis_event_log} 日志表。
- * 状态变更覆盖主表当前值,过程留痕一律追加日志;状态取值全部走 {@link EventStatusEnum},
- * 代码中不出现任何状态中文字面量。</p>
- *
- * <p>并发控制:流转前统一 {@code SELECT ... FOR UPDATE} 锁行,编号生成前持有
- * {@code pg_advisory_xact_lock},避免并发重复流转与撞号。</p>
+ * 应急事件 Service 实现。
+ * 状态变更只覆盖主表当前值,过程留痕一律追加日志;流转前锁行(SELECT ... FOR UPDATE
+ * + pg_advisory_xact_lock),防止并发重复流转与撞号。
  */
 @Slf4j
 @Service
@@ -88,6 +84,10 @@ public class DisEventServiceImpl extends ServiceImpl<DisEventMapper, DisEvent>
     @Autowired
     private DisEventLogService eventLogService;
 
+    /** 附件地址支撑:库内相对路径 ↔ 展示完整 URL(MinIO 配置拼接) */
+    @Autowired
+    private DispatchAttachUrlSupport attachUrlSupport;
+
     /** 人员引用查询(选人下拉 + 按ID批量反查昵称) */
     @Autowired
     private DispatchUserMapper userMapper;
@@ -98,8 +98,13 @@ public class DisEventServiceImpl extends ServiceImpl<DisEventMapper, DisEvent>
     public Page<DisEventVO> findByPage(DisEventPageDTO dto) {
         Page<DisEvent> page = new Page<>(dto.getPageNum(), dto.getPageSize());
         LambdaQueryWrapper<DisEvent> wrapper = buildWrapper(dto);
-        // 默认按接报时间倒序,接报时间为空的排在最后
-        wrapper.orderByDesc(DisEvent::getReportTime).orderByDesc(DisEvent::getId);
+        // 排序规则:orderAsc=true 按接报时间升序(核实/转接报送/跟踪解除等「先报先办」队列页),
+        // 默认倒序(接报管理等记录回看页);接报时间为空的排在最后
+        if (Boolean.TRUE.equals(dto.getOrderAsc())) {
+            wrapper.orderByAsc(DisEvent::getReportTime).orderByAsc(DisEvent::getId);
+        } else {
+            wrapper.orderByDesc(DisEvent::getReportTime).orderByDesc(DisEvent::getId);
+        }
         page(page, wrapper);
 
         Page<DisEventVO> voPage = new Page<>(page.getCurrent(), page.getSize(), page.getTotal());
@@ -517,8 +522,21 @@ public class DisEventServiceImpl extends ServiceImpl<DisEventMapper, DisEvent>
             db.setEventCategory(dto.getEventCategory());
             db.setEventLevel(dto.getEventLevel());
             db.setEventRegion(dto.getEventRegion());
-            db.setHandler(dto.getHandler());
-            db.setHandlerId(dto.getHandlerId());
+            // 责任处置人必须关联系统用户账号:handlerId 是后续催办通知/SSE 推送的投递目标
+            Long handlerId = dto.getHandlerId();
+            if (handlerId == null && StringUtils.isNotBlank(dto.getHandler())) {
+                handlerId = userMapper.selectUserIdByName(dto.getHandler());
+            }
+            if (handlerId == null) {
+                throw new ServiceException("责任处置人必须从系统用户中选择");
+            }
+            String handlerName = dto.getHandler();
+            if (StringUtils.isBlank(handlerName)) {
+                List<DisUserOptionVO> hit = userMapper.selectUserNamesByIds(Collections.singletonList(handlerId));
+                handlerName = hit.isEmpty() ? null : hit.get(0).getNickName();
+            }
+            db.setHandler(handlerName);
+            db.setHandlerId(handlerId);
             db.setHandlerPhone(dto.getHandlerPhone());
             db.setDepartment(dto.getDepartment());
             db.setStatus(EventStatusEnum.VERIFIED.getCode());
@@ -567,7 +585,7 @@ public class DisEventServiceImpl extends ServiceImpl<DisEventMapper, DisEvent>
         DisEventLog log = buildLog(db, operType, null, null);
         log.setTargets(String.join("、", depts));
         log.setOperDesc(dto.getOperDesc());
-        log.setAttachUrl(dto.getAttachUrl());
+        log.setAttachUrl(attachUrlSupport.toRelative(dto.getAttachUrl()));
         eventLogService.append(log);
     }
 
@@ -591,7 +609,7 @@ public class DisEventServiceImpl extends ServiceImpl<DisEventMapper, DisEvent>
 
         DisEventLog log = buildLog(db, EventLogOperTypeEnum.START_DISPOSE, statusFrom, db.getStatus());
         log.setOperDesc(StringUtils.defaultIfBlank(dto.getOperDesc(), "开始处置"));
-        log.setAttachUrl(dto.getAttachUrl());
+        log.setAttachUrl(attachUrlSupport.toRelative(dto.getAttachUrl()));
         eventLogService.append(log);
     }
 
@@ -620,7 +638,7 @@ public class DisEventServiceImpl extends ServiceImpl<DisEventMapper, DisEvent>
         log.setTrend(dto.getTrend());
         log.setNeedUpgrade(dto.getNeedUpgrade());
         log.setNextVisitTime(dto.getNextVisitTime());
-        log.setAttachUrl(dto.getAttachUrl());
+        log.setAttachUrl(attachUrlSupport.toRelative(dto.getAttachUrl()));
         eventLogService.append(log);
     }
 
@@ -644,9 +662,9 @@ public class DisEventServiceImpl extends ServiceImpl<DisEventMapper, DisEvent>
         if (dto.getLossAmount() != null) {
             db.setLossAmount(dto.getLossAmount());
         }
-        // 处置前/后佐证附件(解除原因为「事件已处置完成」时上传,多文件逗号分隔)
-        db.setBeforeUrl(dto.getBeforeUrl());
-        db.setAfterUrl(dto.getAfterUrl());
+        // 处置前/后佐证附件(解除原因为「事件已处置完成」时上传,多文件逗号分隔);库内存相对路径
+        db.setBeforeUrl(attachUrlSupport.toRelative(dto.getBeforeUrl()));
+        db.setAfterUrl(attachUrlSupport.toRelative(dto.getAfterUrl()));
         db.setUpdateBy(DispatchUserSupport.currentLoginName());
         db.setUpdateUserId(DispatchUserSupport.currentUserId());
         db.setUpdateTime(now);
@@ -681,8 +699,7 @@ public class DisEventServiceImpl extends ServiceImpl<DisEventMapper, DisEvent>
         log.setOperDesc(dto.getOperDesc());
         eventLogService.append(log);
 
-        // 催办方式含「系统消息」时,向接收人推一条站内催办通知(旁路动作,失败不阻断):
-        // ① 落 dispatch_notify 表(离线可查,首页铃铛);② SSE 实时下发(在线立即弹窗)。
+        // 催办方式含「系统消息」时推站内通知:落 dispatch_notify 表 + SSE 实时下发;失败不阻断主流程
         if (dto.getMethod().contains("系统消息")) {
             Long receiverUserId = db.getHandlerId();
             if (receiverUserId == null && StringUtils.isNotBlank(dto.getReceiver())) {
@@ -773,13 +790,15 @@ public class DisEventServiceImpl extends ServiceImpl<DisEventMapper, DisEvent>
 
     /**
      * 实体转展示 VO,补齐状态名称、可编辑标记与已过时长。
-     *
-     * <p>接报人 / 核验人 / 责任处置人:库里以「用户ID + 名称快照」双存,
-     * 这里按用户ID反查 sys_user 的**最新昵称**覆盖快照,用户改名后列表即刻跟上。</p>
+     * 接报人 / 核验人 / 责任处置人:库里以「用户ID + 名称快照」双存,
+     * 这里按用户ID反查 sys_user 的最新昵称覆盖快照,用户改名后列表即刻跟上。
      */
     private DisEventVO toVO(DisEvent event, Map<Long, String> userNames) {
         DisEventVO vo = new DisEventVO();
         BeanUtils.copyProperties(event, vo);
+        // 佐证附件:库内相对路径 → 拼上 MinIO 配置地址返回前端展示/下载
+        vo.setBeforeUrl(attachUrlSupport.toAccessibleUrl(event.getBeforeUrl()));
+        vo.setAfterUrl(attachUrlSupport.toAccessibleUrl(event.getAfterUrl()));
         vo.setReporter(resolveUserName(userNames, event.getReporterId(), event.getReporter()));
         vo.setVerifier(resolveUserName(userNames, event.getVerifierId(), event.getVerifier()));
         vo.setHandler(resolveUserName(userNames, event.getHandlerId(), event.getHandler()));
@@ -791,7 +810,6 @@ public class DisEventServiceImpl extends ServiceImpl<DisEventMapper, DisEvent>
 
     /**
      * 批量取出事件列表里出现的人员ID,一次查询回昵称映射,避免逐行查库。
-     *
      * @return 用户ID -> 昵称;列表里没有任何人员ID时返回空映射
      */
     private Map<Long, String> loadUserNames(List<DisEvent> events) {
@@ -834,9 +852,8 @@ public class DisEventServiceImpl extends ServiceImpl<DisEventMapper, DisEvent>
 
     /**
      * 构造日志骨架:事件标识 + 操作类型 + 状态变更轨迹
-     *
      * @param statusFrom 变更前状态,留痕型动作传 null
-     * @param statusTo   变更后状态,留痕型动作传 null
+     * @param statusTo 变更后状态,留痕型动作传 null
      */
     private DisEventLog buildLog(DisEvent event, EventLogOperTypeEnum operType,
                                  Integer statusFrom, Integer statusTo) {

+ 1 - 2
pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/service/impl/DisPlanLibraryServiceImpl.java

@@ -44,8 +44,7 @@ public class DisPlanLibraryServiceImpl extends ServiceImpl<DisPlanLibraryMapper,
     @Override
     public List<DisPlanLibrary> listAll() {
         LambdaQueryWrapper<DisPlanLibrary> wrapper = new LambdaQueryWrapper<>();
-        wrapper.orderByAsc(DisPlanLibrary::getEventType);
-        wrapper.orderByDesc(DisPlanLibrary::getId);
+        wrapper.orderByDesc(DisPlanLibrary::getCreateTime).orderByDesc(DisPlanLibrary::getId);
         return this.list(wrapper);
     }
 

+ 21 - 11
pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/service/impl/DisPlanServiceImpl.java

@@ -21,6 +21,7 @@ import com.zksy.dispatch.mapper.DispatchUserMapper;
 import com.zksy.dispatch.service.DisPlanLogService;
 import com.zksy.dispatch.service.DisPlanService;
 import com.zksy.dispatch.service.DisPlanVersionService;
+import com.zksy.dispatch.support.DispatchAttachUrlSupport;
 import com.zksy.dispatch.support.DispatchNumberSupport;
 import com.zksy.dispatch.support.DispatchUserSupport;
 import lombok.extern.slf4j.Slf4j;
@@ -47,10 +48,9 @@ import java.util.stream.Collectors;
 
 /**
  * 应急预案 Service 实现
- *
- * <p>编辑与审批共表:审批页读的就是编辑页写的那条记录,不再跨表复制。
- * 状态取值全部走 {@link PlanStatusEnum} / {@link PlanVersionStatusEnum},
- * 版本号由后端生成,前端不可指定。</p>
+ * 编辑与审批共表:审批页读的就是编辑页写的那条记录,不再跨表复制。
+ * 状态取值全部走 PlanStatusEnum / PlanVersionStatusEnum,
+ * 版本号由后端生成,前端不可指定。
  */
 @Slf4j
 @Service
@@ -66,6 +66,10 @@ public class DisPlanServiceImpl extends ServiceImpl<DisPlanMapper, DisPlan>
     @Autowired
     private DisPlanVersionService versionService;
 
+    /** 附件地址支撑:库内相对路径 ↔ 展示完整 URL(MinIO 配置拼接) */
+    @Autowired
+    private DispatchAttachUrlSupport attachUrlSupport;
+
     @Autowired
     private DisPlanLogService planLogService;
 
@@ -116,7 +120,11 @@ public class DisPlanServiceImpl extends ServiceImpl<DisPlanMapper, DisPlan>
         for (DisPlanVersion version : versionService.list(vw)) {
             candidatePlanIds.add(version.getPlanId());
         }
-        records.forEach(plan -> fillTransient(plan, candidatePlanIds.contains(plan.getId())));
+        records.forEach(plan -> {
+            // 附件:库内相对路径 → 拼 MinIO 配置地址返回前端展示/下载
+            plan.setAttachPath(attachUrlSupport.toAccessibleUrl(plan.getAttachPath()));
+            fillTransient(plan, candidatePlanIds.contains(plan.getId()));
+        });
         return page;
     }
 
@@ -126,6 +134,7 @@ public class DisPlanServiceImpl extends ServiceImpl<DisPlanMapper, DisPlan>
         if (plan == null) {
             throw new ServiceException("预案不存在:" + id);
         }
+        plan.setAttachPath(attachUrlSupport.toAccessibleUrl(plan.getAttachPath()));
         fillTransient(plan, versionService.getPendingVersion(id) != null);
         return plan;
     }
@@ -220,7 +229,7 @@ public class DisPlanServiceImpl extends ServiceImpl<DisPlanMapper, DisPlan>
             candidate.setVersion(nextVersion);
             candidate.setContent(dto.getContent());
             candidate.setAttachName(dto.getAttachName());
-            candidate.setAttachPath(dto.getAttachPath());
+            candidate.setAttachPath(attachUrlSupport.toRelative(dto.getAttachPath()));
             candidate.setChangeNote(dto.getChangeNote());
             candidate.setEditor(DispatchUserSupport.currentUserName());
             candidate.setEditorId(DispatchUserSupport.currentUserId());
@@ -322,7 +331,7 @@ public class DisPlanServiceImpl extends ServiceImpl<DisPlanMapper, DisPlan>
             version.setVersion(targetVersion);
             version.setContent(db.getContent());
             version.setAttachName(db.getAttachName());
-            version.setAttachPath(db.getAttachPath());
+            version.setAttachPath(attachUrlSupport.toRelative(db.getAttachPath()));
             version.setChangeNote("通过审核并发布");
             version.setEditor(DispatchUserSupport.currentUserName());
             version.setEditorId(DispatchUserSupport.currentUserId());
@@ -393,7 +402,7 @@ public class DisPlanServiceImpl extends ServiceImpl<DisPlanMapper, DisPlan>
         plan.setPublishUnit(dto.getPublishUnit());
         plan.setPublishDate(dto.getPublishDate());
         plan.setAttachName(dto.getAttachName());
-        plan.setAttachPath(dto.getAttachPath());
+        plan.setAttachPath(attachUrlSupport.toRelative(dto.getAttachPath()));
         plan.setContent(dto.getContent());
         if (dto.getIsTemplate() != null) {
             plan.setIsTemplate(dto.getIsTemplate());
@@ -442,13 +451,14 @@ public class DisPlanServiceImpl extends ServiceImpl<DisPlanMapper, DisPlan>
 
     /**
      * 版本实体转展示 VO。
-     *
-     * <p>修订人库里以「用户ID + 名称快照」双存,这里按用户ID反查最新昵称覆盖快照;
-     * 用户已被删除时退回快照,保证历史版本仍能看到当时的修订人。</p>
+     * 修订人库里以「用户ID + 名称快照」双存,这里按用户ID反查最新昵称覆盖快照;
+     * 用户已被删除时退回快照,保证历史版本仍能看到当时的修订人。
      */
     private PlanVersionVO toVersionVO(DisPlanVersion version, Map<Long, String> editorNames) {
         PlanVersionVO vo = new PlanVersionVO();
         BeanUtils.copyProperties(version, vo);
+        // 版本附件快照:库内相对路径 → 拼 MinIO 配置地址
+        vo.setAttachPath(attachUrlSupport.toAccessibleUrl(version.getAttachPath()));
         vo.setVerStatusName(PlanVersionStatusEnum.getDescByCode(version.getVerStatus()));
         String editor = version.getEditorId() == null ? null : editorNames.get(version.getEditorId());
         if (StringUtils.isNotBlank(editor)) {

+ 2 - 3
pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/service/impl/DisPlanVersionServiceImpl.java

@@ -14,9 +14,8 @@ import java.util.List;
 
 /**
  * 应急预案版本快照 Service 实现
- *
- * <p>版本表不设逻辑删除:删除预案时物理清理本预案的版本行,
- * 与「日志表只增不改」的约定不同 —— 版本是预案的从属快照,预案没了快照也就没有意义。</p>
+ * 版本表不设逻辑删除:删除预案时物理清理本预案的版本行,
+ * 与「日志表只增不改」的约定不同 —— 版本是预案的从属快照,预案没了快照也就没有意义。
  */
 @Slf4j
 @Service

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

@@ -14,7 +14,6 @@ import java.util.List;
 
 /**
  * 催办系统消息通知 Service 实现
- *
  * @author zengy
  */
 @Slf4j

+ 66 - 0
pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/support/DispatchAttachUrlSupport.java

@@ -0,0 +1,66 @@
+package com.zksy.dispatch.support;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+
+/**
+ * 调度附件地址支撑:数据库统一存相对路径(如 /dispatch/2026/10/16/x.docx),
+ * 读出口拼 minio.endpoint + bucket 返回完整 URL。
+ * 两个方法都兼容 http 开头的历史存量值,不会二次处理。
+ * @author zengy
+ */
+@Component
+public class DispatchAttachUrlSupport {
+
+    /** MinIO 访问地址(minio.endpoint,与 MinioController 的 readPath 同源) */
+    @Value("${minio.endpoint}")
+    private String endpoint;
+
+    /** MinIO 存储桶(minio.bucket) */
+    @Value("${minio.bucket}")
+    private String bucket;
+
+    /**
+     * 库内存储值 → 可访问完整 URL(展示/下载出口统一走这里)
+     */
+    public String toAccessibleUrl(String stored) {
+        if (!StringUtils.hasText(stored)) {
+            return stored;
+        }
+        String value = stored.trim();
+        // 历史存量已是完整 URL(或外部链接),原样返回,避免二次拼接
+        if (value.startsWith("http://") || value.startsWith("https://")) {
+            return value;
+        }
+        String base = endpoint == null ? "" : (endpoint.endsWith("/") ? endpoint : endpoint + "/");
+        String path = value.startsWith("/") ? value.substring(1) : value;
+        return base + (bucket == null ? "" : bucket + "/") + path;
+    }
+
+    /**
+     * 前端/入参值 → 库内相对路径(写入口统一走这里)
+     */
+    public String toRelative(String input) {
+        if (!StringUtils.hasText(input)) {
+            return input;
+        }
+        String value = input.trim();
+        if (!value.startsWith("http://") && !value.startsWith("https://")) {
+            // 已是相对路径,规整为以 / 开头后返回
+            return value.startsWith("/") ? value : "/" + value;
+        }
+        String base = endpoint == null ? "" : (endpoint.endsWith("/") ? endpoint : endpoint + "/");
+        String prefix = base + (bucket == null ? "" : bucket + "/");
+        if (value.startsWith(prefix)) {
+            return "/" + value.substring(prefix.length());
+        }
+        // 域名与当前配置不一致的历史数据:按「/bucket/路径」截取,尽量还原相对路径
+        int idx = value.indexOf("/" + bucket + "/");
+        if (idx >= 0) {
+            return value.substring(idx + ("/" + bucket).length());
+        }
+        // 实在没有 bucket 段(外部地址),原样保留
+        return value;
+    }
+}

+ 6 - 18
pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/support/DispatchNumberSupport.java

@@ -8,17 +8,9 @@ import java.util.concurrent.ThreadLocalRandom;
 import java.util.function.Predicate;
 
 /**
- * 应急与指挥调度模块业务序号生成。
- *
- * <p>与 {@code AlarmLifecycleSupport#nextAlarmNo} 保持同一套规则:
- * 后端在保存前生成 {@code 前缀 + 时间 + 随机数},数据库只保留唯一索引,
- * <b>不使用数据库触发器或序列取号</b>;历史数据不回填,缺失时前端展示 {@code —}。</p>
- *
- * <p>主键仍使用 int8 自增,业务序号是独立列;普通更新接口不可修改序号。</p>
- *
- * <p><b>调用方须先持有数据库事务内的单号锁</b>,随机碰撞时重新取号;
- * 不能只查重而不加锁。</p>
- *
+ * 调度模块业务序号生成:前缀 + 时间 + 随机数,保存前在事务内取号。
+ * 唯一性靠唯一索引 + 碰撞重试(调用方须持有单号锁,不能只查重不加锁)。
+ * 规则与 AlarmLifecycleSupport#nextAlarmNo 保持一致;历史数据不回填。
  * @author zengy
  */
 public final class DispatchNumberSupport {
@@ -40,7 +32,6 @@ public final class DispatchNumberSupport {
 
     /**
      * 事件编号:SJ + yyyyMMddHHmmss + 4 位随机
-     *
      * @param exists 查重函数,返回 true 表示该编号已存在
      */
     public static String nextEventNo(Predicate<String> exists) {
@@ -49,7 +40,6 @@ public final class DispatchNumberSupport {
 
     /**
      * 预案编号:YA + yyyyMMdd + 4 位随机
-     *
      * @param exists 查重函数,返回 true 表示该编号已存在
      */
     public static String nextPlanNo(Predicate<String> exists) {
@@ -58,7 +48,6 @@ public final class DispatchNumberSupport {
 
     /**
      * 事件分级标准编号:GJ + yyyyMMdd + 3 位随机
-     *
      * @param exists 查重函数,返回 true 表示该编号已存在
      */
     public static String nextGradeNo(Predicate<String> exists) {
@@ -67,11 +56,10 @@ public final class DispatchNumberSupport {
 
     /**
      * 生成候选编号并查重,最多尝试 {@value #MAX_ATTEMPT} 次
-     *
-     * @param head      前缀 + 时间部分
+     * @param head 前缀 + 时间部分
      * @param randomLen 随机数位数
-     * @param exists    查重函数
-     * @param label     业务名称,用于异常提示
+     * @param exists 查重函数
+     * @param label 业务名称,用于异常提示
      */
     private static String generate(String head, int randomLen, Predicate<String> exists, String label) {
         int bound = (int) Math.pow(10, randomLen);

+ 2 - 4
pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/support/DispatchUserSupport.java

@@ -5,10 +5,8 @@ import com.zksy.common.utils.SecurityUtils;
 
 /**
  * 应急与指挥调度模块当前操作人取值。
- *
- * <p>事件日志需要同时记录操作人ID与昵称,且必须容忍未登录/定时任务等无上下文场景,
- * 统一在此收敛,避免各 Service 各写一份 try-catch。</p>
- *
+ * 事件日志需要同时记录操作人ID与昵称,且必须容忍未登录/定时任务等无上下文场景,
+ * 统一在此收敛,避免各 Service 各写一份 try-catch。
  * @author zengy
  */
 public final class DispatchUserSupport {

+ 2 - 6
pipe-network-service/zksy-system/src/main/java/com/zksy/dispatch/support/SseEmitterManager.java

@@ -12,12 +12,8 @@ import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 
 /**
- * SSE 连接管理器(轻量版,参照 RuoYi-Vue-Plus 5.x SseEmitterManager 设计)
- *
- * <p>仅做「在线用户连接表 + 实时下发」:进程内存连接表,服务重启/多实例下
- * 连接各自独立;离线消息不在此层缓存,由 {@code dispatch_notify} 表兜底
- * (用户上线后在首页铃铛可见)。与设备状态 WebSocket 通道互不影响。</p>
- *
+ * SSE 连接管理器(参照 RuoYi-Vue-Plus 5.x 同名类):进程内在线用户连接表 + 实时下发。
+ * 离线消息不在此层缓存,由 dispatch_notify 表兜底。
  * @author zengy
  */
 @Slf4j