Jelajahi Sumber

工单流程重构

zengy 1 bulan lalu
induk
melakukan
13d5bd1e7c
69 mengubah file dengan 1418 tambahan dan 1802 penghapusan
  1. 0 1
      pipe-network-service/pom.xml
  2. 58 0
      pipe-network-service/sql/migration_case_workorder_list_menu.sql
  3. 52 0
      pipe-network-service/sql/migration_equipment_type_id.sql
  4. 1 1
      pipe-network-service/sql/migration_gas_workorder_menu.sql
  5. 1 1
      pipe-network-service/sql/migration_work_order_level.sql
  6. 100 0
      pipe-network-service/sql/migration_workorder_menu_reuse.sql
  7. 5 7
      pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/WaterSupply/DeviceAbnormal/DeviceAbnormalController.java
  8. 5 6
      pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/WaterSupply/DeviceMaintain/DeviceMaintainController.java
  9. 4 4
      pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/alarm/AlarmController.java
  10. 0 152
      pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/manhole/WorkOrderController.java
  11. 0 183
      pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/ops/PworkOrderController.java
  12. 194 0
      pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/ops/WorkOrderController.java
  13. 4 4
      pipe-network-service/zksy-admin/src/main/java/com/zksy/web/websocket/DeviceStatusWebSocketHandler.java
  14. 4 4
      pipe-network-service/zksy-admin/src/main/java/com/zksy/web/websocket/WorkOrderWebSocketPushTask.java
  15. 8 8
      pipe-network-service/zksy-admin/src/test/java/com/zksy/web/controller/WaterSupply/DeviceAbnormal/DeviceAbnormalControllerTest.java
  16. 2 2
      pipe-network-service/zksy-admin/src/test/java/com/zksy/web/websocket/DeviceStatusCockpitWebSocketHandlerTest.java
  17. 2 2
      pipe-network-service/zksy-system/src/main/java/com/zksy/WaterSupply/MonitorAlarm/dto/out/AlarmFlowOutDTO.java
  18. 4 4
      pipe-network-service/zksy-system/src/main/java/com/zksy/WaterSupply/MonitorAlarm/service/impl/WaterSupplyAlarmServiceImpl.java
  19. 8 8
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/alarm/service/impl/AlarmDataServiceImpl.java
  20. 6 6
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/drill/service/DrillAlarmSyncService.java
  21. 0 20
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/mapper/WorkOrderLogMapper.java
  22. 0 28
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/mapper/WorkOrderMapper.java
  23. 0 123
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/service/BusinessWorkOrderService.java
  24. 6 6
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/service/DrillAlarmSyncService.java
  25. 1 1
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/service/WaterSupplyDeviceService.java
  26. 0 50
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/service/WorkOrderService.java
  27. 2 0
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/service/impl/EquipmentBaseServiceImpl.java
  28. 2 2
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/service/impl/HazardHiddenAccountServiceImpl.java
  29. 2 2
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/service/impl/MaintenanceDataStatServiceImpl.java
  30. 7 7
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/service/impl/WaterSupplyDeviceServiceImpl.java
  31. 0 24
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/service/impl/WorkOrderLogServiceImpl.java
  32. 0 405
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/service/impl/WorkOrderServiceImpl.java
  33. 6 6
      pipe-network-service/zksy-system/src/main/java/com/zksy/cockpit/workorder/BusinessCockpitWorkOrderReadAdapter.java
  34. 0 54
      pipe-network-service/zksy-system/src/main/java/com/zksy/manhole/dto/in/WorkOrderAddInDTO.java
  35. 0 49
      pipe-network-service/zksy-system/src/main/java/com/zksy/manhole/dto/in/WorkOrderFinishedInDTO.java
  36. 0 46
      pipe-network-service/zksy-system/src/main/java/com/zksy/manhole/dto/in/WorkOrderOperInDTO.java
  37. 0 33
      pipe-network-service/zksy-system/src/main/java/com/zksy/manhole/dto/in/WorkOrderPageInDTO.java
  38. 0 39
      pipe-network-service/zksy-system/src/main/java/com/zksy/manhole/dto/in/WorkOrderProcessingInDTO.java
  39. 0 19
      pipe-network-service/zksy-system/src/main/java/com/zksy/manhole/dto/out/WorkOrderLogOutDTO.java
  40. 0 29
      pipe-network-service/zksy-system/src/main/java/com/zksy/manhole/dto/out/WorkOrderOutDTO.java
  41. 2 2
      pipe-network-service/zksy-system/src/main/java/com/zksy/ops/domain/WorkOrder.java
  42. 10 50
      pipe-network-service/zksy-system/src/main/java/com/zksy/ops/domain/WorkOrderLog.java
  43. 1 1
      pipe-network-service/zksy-system/src/main/java/com/zksy/ops/domain/vo/WorkOrderPushVO.java
  44. 53 0
      pipe-network-service/zksy-system/src/main/java/com/zksy/ops/dto/WorkOrderAddDTO.java
  45. 0 5
      pipe-network-service/zksy-system/src/main/java/com/zksy/ops/dto/WorkOrderDelayApplyDTO.java
  46. 0 5
      pipe-network-service/zksy-system/src/main/java/com/zksy/ops/dto/WorkOrderDelayAuditDTO.java
  47. 2 2
      pipe-network-service/zksy-system/src/main/java/com/zksy/ops/dto/WorkOrderDetailVO.java
  48. 0 5
      pipe-network-service/zksy-system/src/main/java/com/zksy/ops/dto/WorkOrderDispatchDTO.java
  49. 0 6
      pipe-network-service/zksy-system/src/main/java/com/zksy/ops/dto/WorkOrderOperateDTO.java
  50. 4 0
      pipe-network-service/zksy-system/src/main/java/com/zksy/ops/dto/WorkOrderPageDTO.java
  51. 67 0
      pipe-network-service/zksy-system/src/main/java/com/zksy/ops/dto/WorkOrderStatusStatVO.java
  52. 0 5
      pipe-network-service/zksy-system/src/main/java/com/zksy/ops/dto/WorkOrderVerifyDTO.java
  53. 2 2
      pipe-network-service/zksy-system/src/main/java/com/zksy/ops/enums/WorkOrderDelayApplyStatusEnum.java
  54. 2 2
      pipe-network-service/zksy-system/src/main/java/com/zksy/ops/enums/WorkOrderLevelEnum.java
  55. 2 2
      pipe-network-service/zksy-system/src/main/java/com/zksy/ops/enums/WorkOrderLogOperTypeEnum.java
  56. 2 2
      pipe-network-service/zksy-system/src/main/java/com/zksy/ops/enums/WorkOrderStatusEnum.java
  57. 19 0
      pipe-network-service/zksy-system/src/main/java/com/zksy/ops/mapper/WorkOrderLogMapper.java
  58. 41 0
      pipe-network-service/zksy-system/src/main/java/com/zksy/ops/mapper/WorkOrderMapper.java
  59. 2 2
      pipe-network-service/zksy-system/src/main/java/com/zksy/ops/service/WorkOrderLogService.java
  60. 172 0
      pipe-network-service/zksy-system/src/main/java/com/zksy/ops/service/WorkOrderService.java
  61. 34 52
      pipe-network-service/zksy-system/src/main/java/com/zksy/ops/service/impl/InspectTaskServiceImpl.java
  62. 18 0
      pipe-network-service/zksy-system/src/main/java/com/zksy/ops/service/impl/WorkOrderLogServiceImpl.java
  63. 333 244
      pipe-network-service/zksy-system/src/main/java/com/zksy/ops/service/impl/WorkOrderServiceImpl.java
  64. 2 2
      pipe-network-service/zksy-system/src/main/resources/mapper/ops/WorkOrderLogMapper.xml
  65. 12 2
      pipe-network-service/zksy-system/src/main/resources/mapper/ops/WorkOrderMapper.xml
  66. 0 62
      pipe-network-service/zksy-system/src/test/java/com/zksy/base/service/impl/BusinessWorkOrderServiceImplTest.java
  67. 12 12
      pipe-network-service/zksy-system/src/test/java/com/zksy/base/service/impl/WaterSupplyDeviceServiceImplTest.java
  68. 140 0
      pipe-network-service/zksy-system/src/test/java/com/zksy/ops/service/impl/WorkOrderServiceImplTest.java
  69. 2 1
      pom.xml

+ 0 - 1
pipe-network-service/pom.xml

@@ -21,7 +21,6 @@
         <zksy.version>3.9.0</zksy.version>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
         <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
-        <org.projectlombok.version>1.18.20</org.projectlombok.version>
         <java.version>11</java.version>
         <maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
         <spring-boot.version>2.5.15</spring-boot.version>

+ 58 - 0
pipe-network-service/sql/migration_case_workorder_list_menu.sql

@@ -0,0 +1,58 @@
+-- =====================================================================
+-- 案件管理 - 经办工单 / 办结工单 统一复用列表页 list.vue
+--
+-- 背景:caseHandling 下原有两个列表页各写了一套工单列表:
+--         handlingWorkO.vue(经办工单)
+--         closeWorkOrder.vue(办结工单)
+--       两者展示与运维工单列表 index.vue 一致,现统一指向新页面
+--       subSystem/pipeNetwork/ops/workOrder/list.vue(只读,无新增工单)。
+--
+-- 业务口径(两个菜单都只查 equipment_type.parent_type_id = 4 的案件类设备工单):
+--       经办工单:order_status IN (0 待派单, 1 待接单, 2 已接单, 3 处理中, 4 待验收, 6 延期中)
+--       办结工单:order_status = 5 已结案
+--   该口径由前端固定传 orderStatus / deviceTypeParentId,后端
+--   WorkOrderPageDTO + WorkOrderServiceImpl#applyDeviceTypeScope 实现。
+--
+-- 本脚本幂等,重复执行无副作用。执行前请备份数据!
+-- =====================================================================
+
+-- 1. 【诊断】先查看这两个菜单当前的 path / component,用于核对下面前端路由判定关键字
+--    SELECT menu_id, menu_name, path, component, perms
+--      FROM sys_menu
+--     WHERE component LIKE '%caseHandling%'
+--        OR path LIKE '%caseHandling%'
+--     ORDER BY menu_id;
+
+-- 2. 经办工单 / 办结工单 → 统一指向 list.vue
+--    用 LIKE 匹配,兼容 component 实际值带 .vue 后缀或前缀写法不同的情况
+UPDATE sys_menu
+   SET component = 'subSystem/pipeNetwork/ops/workOrder/list'
+ WHERE (component LIKE '%caseHandling/handlingWorkO%'
+     OR component LIKE '%caseHandling/closeWorkOrder%')
+   AND component NOT LIKE '%ops/workOrder/list%';
+
+-- 3. 校验
+--    SELECT menu_id, menu_name, path, component FROM sys_menu
+--     WHERE component = 'subSystem/pipeNetwork/ops/workOrder/list';
+
+-- 4. 回滚(如需退回旧页面,需先恢复已删除的两个 vue 文件)
+--    UPDATE sys_menu
+--       SET component = CASE
+--             WHEN path LIKE '%handlingWorkO%' THEN 'subSystem/manholeCover/caseHandling/handlingWorkO'
+--             ELSE 'subSystem/manholeCover/caseHandling/closeWorkOrder'
+--           END
+--     WHERE component = 'subSystem/pipeNetwork/ops/workOrder/list';
+
+-- 5. 说明
+--    - 不修改 path 与 perms:菜单访问权限标识保持不变,sys_role_menu 授权无需调整。
+--    - 前端路由判定(list.vue 内 MENU_SCOPES):
+--        路由 path 含 'close'    或 菜单名含 '办结'  → 办结工单(order_status = 5)
+--        路由 path 含 'handling' 或 菜单名含 '经办'  → 经办工单(未办结状态集合)
+--      判定顺序为先「办结」后「经办」,避免 path 中的父级目录 caseHandling 被误判为经办。
+--      若菜单 path 与菜单名都不是上述关键字,请同步调整 MENU_SCOPES,否则会退化为
+--      「不限状态 + parent_type_id = 4」,页面不报错但查询范围不符合预期。
+--    - 已无菜单引用、已删除的旧页面:
+--        subSystem/manholeCover/caseHandling/handlingWorkO.vue(已删除)
+--        subSystem/manholeCover/caseHandling/closeWorkOrder.vue(已删除)
+--      同目录 circulation.vue(流转工单)未改动,仍独立使用。
+-- =====================================================================

+ 52 - 0
pipe-network-service/sql/migration_equipment_type_id.sql

@@ -0,0 +1,52 @@
+-- =====================================================================
+-- 设备类型关联键统一:equipment_base.equipment_type_id -> equipment_type.id
+--
+-- 背景:
+--   2026-09-02 的提交 b46c1c1 曾把设备管理的新增/修改改为关联 equipment_type.type_id,
+--   但列表与详情的批量翻译一直是按 equipment_type.id,导致存、取两套口径不一致:
+--   一些设备存入的是 type_id 业务编码,展示时按 id 关联查不到,类型显示空白。
+--   本次代码已统一为 equipment_type.id,本脚本用于修正历史数据。
+--
+-- 为什么不能用 type_id 作为关联键:
+--   equipment_type.type_id 是业务编码,库中存在重复记录,按它关联会得到歧义结果。
+--
+-- 执行前请备份数据!建议先执行第 1、2 步化验老板一致后再执行第 3 步。
+-- =====================================================================
+
+-- 1. 先看总体现状:equipment_type_id 命中 type_id 但命中不到 id 的记录数
+--    (这些就是需要修正的数据)
+SELECT count(*) AS need_fix_count
+  FROM app_user.equipment_base eb
+  JOIN app_user.equipment_type et
+    ON eb.equipment_type_id = et.type_id
+ WHERE eb.equipment_type_id <> et.id;
+
+-- 2. 【重要】检查 type_id 重复情况:若结果 > 0,说明同一 type_id 对应多个 id,
+--    自动修正会产生歧义,必须先人工梳理去重或确认取哪一个,暂不要执行第 3 步。
+SELECT et.type_id, count(*) AS dup_count, array_agg(et.id) AS candidate_ids
+  FROM app_user.equipment_type et
+ GROUP BY et.type_id
+HAVING count(*) > 1;
+
+-- 3. 确认第 2 步无重复(结果 0 行)后,再执行修正:
+--    把 equipment_type_id 从 type_id 值改为对应的 equipment_type.id
+UPDATE app_user.equipment_base eb
+   SET equipment_type_id = et.id
+  FROM app_user.equipment_type et
+ WHERE eb.equipment_type_id = et.type_id
+   AND eb.equipment_type_id <> et.id;
+
+-- =====================================================================
+-- 4. 校验:不应再有「命中 type_id 但命中不到 id」的记录(期望 0)
+--    SELECT count(*) FROM app_user.equipment_base eb
+--      JOIN app_user.equipment_type et ON eb.equipment_type_id = et.type_id
+--     WHERE eb.equipment_type_id <> et.id;
+--
+-- 5. 兜底检查:还存在多少 equipment_type_id 无法关联到任何 equipment_type.id
+--    (这些可能是真正的脏数据,或本就存的是 id 但类型记录已被删除)
+--    SELECT count(*) FROM app_user.equipment_base eb
+--     WHERE NOT EXISTS (SELECT 1 FROM app_user.equipment_type et WHERE et.id = eb.equipment_type_id)
+--       AND eb.equipment_type_id IS NOT NULL;
+--
+-- 说明:第 3 步是幂等的,重复执行不会二次修改已修正的数据。
+-- =====================================================================

+ 1 - 1
pipe-network-service/sql/migration_gas_workorder_menu.sql

@@ -10,7 +10,7 @@
 --       equipment_base.equipment_type_id -> equipment_type.id
 --       equipment_type.parent_type_id    = '3'
 --   该过滤由前端固定传 deviceTypeParentId=3、后端 WorkOrderPageDTO /
---   BusinessWorkOrderServiceImpl#applyDeviceTypeScope 实现;运维菜单不传该参数,仍查全量。
+--   WorkOrderServiceImpl#applyDeviceTypeScope 实现;运维菜单不传该参数,仍查全量。
 --
 -- 执行前请备份数据!
 -- =====================================================================

+ 1 - 1
pipe-network-service/sql/migration_work_order_level.sql

@@ -5,7 +5,7 @@
 -- 新:0-紧急 1-一般 2-低
 --
 -- 配套代码改动:
---   com.zksy.base.enums.WorkOrderLevelEnum  URGENT(0) NORMAL(1) LOW(2)
+--   com.zksy.ops.enums.WorkOrderLevelEnum  URGENT(0) NORMAL(1) LOW(2)
 --   前端 src/views/subSystem/pipeNetwork/ops/workOrder/workOrderDict.js
 --     的 ORDER_LEVEL_MAP 同步改为 0/1/2
 --

+ 100 - 0
pipe-network-service/sql/migration_workorder_menu_reuse.sql

@@ -0,0 +1,100 @@
+-- =====================================================================
+-- 各业务菜单统一复用运维工单处置页面 dispose.vue
+--
+-- 背景:燃气 / 排水 / 案件三个业务菜单原本各有一套重复实现的工单页面,
+--       现统一指向 subSystem/pipeNetwork/ops/workOrder/dispose.vue。
+--
+-- 业务口径:按菜单收窄为各自业务设备产生的工单,过滤链路统一为
+--       work_order.device_id            -> equipment_base.equipment_id
+--       equipment_base.equipment_type_id -> equipment_type.id
+--       equipment_type.parent_type_id     = 各业务的父类别ID
+--
+--   父类别取值:
+--       燃气   parent_type_id = '3'  菜单 燃气工单-工单管理     → dispose.vue
+--       排水   parent_type_id = '2'  菜单 工单管理-排水工单     → dispose.vue
+--       案件   parent_type_id = '4'  菜单 案件管理-工单处置管理 → dispose.vue
+--       供水   parent_type_id = '1'  菜单 设备维修派单         → dispose.vue
+--       供水   parent_type_id = '1'  菜单 设备维修记录         → index.vue(列表页)
+--
+--   该过滤由前端固定传 deviceTypeParentId、后端 WorkOrderPageDTO /
+--   WorkOrderServiceImpl#applyDeviceTypeScope 实现;
+--   运维工单菜单不匹配任何业务口径,不传该参数,仍查全量。
+--
+-- 本脚本幂等,重复执行无副作用。执行前请备份数据!
+-- =====================================================================
+
+-- 1. 燃气:工单管理
+UPDATE sys_menu
+   SET component = 'subSystem/pipeNetwork/ops/workOrder/dispose'
+ WHERE component = 'subSystem/basic/BusinessWorkOrder';
+
+-- 2. 排水:工单管理 - 排水工单
+UPDATE sys_menu
+   SET component = 'subSystem/pipeNetwork/ops/workOrder/dispose'
+ WHERE component = 'subSystem/drainage/DrainageWorkOrder';
+
+-- 3. 案件:案件管理 - 工单处置管理
+--    已核实 sys_menu 中实际值为 caseHandling 路径,不存在 subSystem/manholeCover/orderDisposal
+UPDATE sys_menu
+   SET component = 'subSystem/pipeNetwork/ops/workOrder/dispose'
+ WHERE component = 'subSystem/manholeCover/caseHandling/orderDisposal';
+
+-- 4. 监测设备 - 设备维修派单:复用工单处置页 dispose.vue(parent_type_id = 1)
+--    用 LIKE 匹配,兼容 component 实际值带 .vue 后缀或前缀写法不同的情况
+UPDATE sys_menu
+   SET component = 'subSystem/pipeNetwork/ops/workOrder/dispose'
+ WHERE component LIKE '%waterSupply/device/maintain%'
+   AND component NOT LIKE '%ops/workOrder%';
+
+-- 5. 监测设备 - 设备维修记录:复用工单列表页 index.vue(parent_type_id = 1)
+--    注意这里是 index.vue(列表页),不是 dispose.vue
+UPDATE sys_menu
+   SET component = 'subSystem/pipeNetwork/ops/workOrder/index'
+ WHERE component LIKE '%waterSupply/device/record%'
+   AND component NOT LIKE '%ops/workOrder%';
+
+-- 5.1 【诊断】若执行后 maintain 菜单仍未生效,先查它当前的真实 component / path:
+--     SELECT menu_id, menu_name, parent_id, path, component
+--       FROM sys_menu
+--      WHERE component LIKE '%maintain%' OR path LIKE '%maintain%'
+--      ORDER BY menu_id;
+--     把查到的 path 值发出来,用于核对前端 MENU_DEVICE_TYPE_SCOPE 的 pathKey 是否能命中。
+
+-- =====================================================================
+-- 6. 校验:确认各菜单已更新
+--    SELECT menu_id, menu_name, path, component FROM sys_menu
+--     WHERE component IN ('subSystem/pipeNetwork/ops/workOrder/dispose',
+--                         'subSystem/pipeNetwork/ops/workOrder/index');
+--
+--    若上面查不到某业务菜单,说明其 component 与本脚本写法不一致,
+--    请先执行下句确认实际值,再按实际路径调整上面的 UPDATE:
+--    SELECT menu_id, menu_name, path, component FROM sys_menu
+--     WHERE component LIKE '%DrainageWorkOrder%'
+--        OR component LIKE '%orderDisposal%'
+--        OR component LIKE '%BusinessWorkOrder%'
+--        OR component LIKE '%waterSupply/device/%';
+--
+-- 7. 说明
+--    - 不修改 path 与 perms:原菜单的访问权限标识保持不变,
+--      已分配的 sys_role_menu 授权无需调整。
+--    - 路由判定:组件依据「路由 path 关键字 → 设备父类别」映射(前端常量
+--      MENU_DEVICE_TYPE_SCOPE)判定业务来源,两个组件各有一份:
+--
+--      dispose.vue(工单处置页):
+--          BusinessWorkOrder -> 3  燃气
+--          DrainageWorkOrder -> 2  排水
+--          orderDisposal     -> 4  案件
+--          maintain          -> 1  设备维修派单
+--
+--      index.vue(工单列表页):
+--          record            -> 1  设备维修记录
+--
+--      若菜单 path 非上述值,需同步调整对应常量,否则会退化为查全量(不会报错但范围不对)。
+--      注意:运维自身菜单(pworkOrder/dispose、pworkOrder/index)不含上述关键字,仍查全量。
+--    - 已无菜单引用、确认后可删除的旧页面:
+--      subSystem/basic/BusinessWorkOrder.vue(已删除)
+--      subSystem/drainage/DrainageWorkOrder.vue(已删除)
+--      subSystem/manholeCover/caseHandling/orderDisposal.vue(已删除)
+--      subSystem/waterSupply/device/maintain.vue(本次替换,暂保留)
+--      subSystem/waterSupply/device/record.vue(本次替换,暂保留)
+-- =====================================================================

+ 5 - 7
pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/WaterSupply/DeviceAbnormal/DeviceAbnormalController.java

@@ -3,17 +3,16 @@ package com.zksy.web.controller.WaterSupply.DeviceAbnormal;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.zksy.base.domain.vo.AbnormalDeviceVO;
 import com.zksy.base.domain.vo.MaintainOrderVO;
-import com.zksy.base.domain.WorkOrder;
+import com.zksy.ops.domain.WorkOrder;
 import com.zksy.base.service.WaterSupplyDeviceService;
-import com.zksy.base.service.BusinessWorkOrderService;
+import com.zksy.ops.service.WorkOrderService;
 import com.zksy.WaterSupply.export.WaterDeviceAbnormalExportDTO;
 import com.zksy.common.annotation.Log;
 import com.zksy.common.core.domain.AjaxResult;
 import com.zksy.common.enums.BusinessType;
 import com.zksy.common.utils.poi.ExcelUtil;
-import com.zksy.common.utils.SecurityUtils;
 import com.zksy.common.exception.ServiceException;
-import com.zksy.base.enums.WorkOrderStatusEnum;
+import com.zksy.ops.enums.WorkOrderStatusEnum;
 import com.zksy.ops.dto.WorkOrderDispatchDTO;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
@@ -37,7 +36,7 @@ public class DeviceAbnormalController {
     private WaterSupplyDeviceService waterSupplyDeviceService;
 
     @Autowired
-    private BusinessWorkOrderService businessWorkOrderService;
+    private WorkOrderService businessWorkOrderService;
 
     @GetMapping("/page")
     @ApiOperation("异常设备分页查询")
@@ -81,10 +80,9 @@ public class DeviceAbnormalController {
                 return AjaxResult.success("工单创建成功,待派单", order);
             }
             WorkOrderDispatchDTO dispatchDTO = new WorkOrderDispatchDTO();
-            dispatchDTO.setOrderId(order.getOrderId());
             dispatchDTO.setHandlerId(receiveUserId);
             dispatchDTO.setRemarks(vo.getMaintainRemark());
-            boolean dispatched = businessWorkOrderService.dispatch(dispatchDTO);
+            boolean dispatched = businessWorkOrderService.dispatch(order.getOrderId(), dispatchDTO);
             if (!dispatched) {
                 return AjaxResult.error("派单失败:工单仍为待派单状态,请稍后重试");
             }

+ 5 - 6
pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/WaterSupply/DeviceMaintain/DeviceMaintainController.java

@@ -3,15 +3,15 @@ package com.zksy.web.controller.WaterSupply.DeviceMaintain;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.zksy.base.domain.vo.EquipmentMaintainDataVO;
 import com.zksy.base.domain.vo.MaintainOrderVO;
-import com.zksy.base.domain.WorkOrder;
-import com.zksy.base.service.BusinessWorkOrderService;
+import com.zksy.ops.domain.WorkOrder;
+import com.zksy.ops.service.WorkOrderService;
 import com.zksy.base.service.WaterSupplyDeviceService;
 import com.zksy.common.annotation.Log;
 import com.zksy.common.core.domain.AjaxResult;
 import com.zksy.common.enums.BusinessType;
 import com.zksy.common.utils.SecurityUtils;
 import com.zksy.common.exception.ServiceException;
-import com.zksy.base.enums.WorkOrderStatusEnum;
+import com.zksy.ops.enums.WorkOrderStatusEnum;
 import com.zksy.ops.dto.WorkOrderDispatchDTO;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
@@ -34,7 +34,7 @@ public class DeviceMaintainController {
     private WaterSupplyDeviceService waterSupplyDeviceService;
 
     @Autowired
-    private BusinessWorkOrderService businessWorkOrderService;
+    private WorkOrderService businessWorkOrderService;
 
     @PostMapping("/order")
     @ApiOperation("创建维修派单")
@@ -52,10 +52,9 @@ public class DeviceMaintainController {
                 return AjaxResult.success("工单创建成功,待派单", order);
             }
             WorkOrderDispatchDTO dispatchDTO = new WorkOrderDispatchDTO();
-            dispatchDTO.setOrderId(order.getOrderId());
             dispatchDTO.setHandlerId(receiveUserId);
             dispatchDTO.setRemarks(vo.getMaintainRemark());
-            boolean dispatched = businessWorkOrderService.dispatch(dispatchDTO);
+            boolean dispatched = businessWorkOrderService.dispatch(order.getOrderId(), dispatchDTO);
             if (!dispatched) {
                 return AjaxResult.error("派单失败:工单仍为待派单状态,请稍后重试");
             }

+ 4 - 4
pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/alarm/AlarmController.java

@@ -4,9 +4,9 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.zksy.base.alarm.domain.AlarmData;
 import com.zksy.base.alarm.service.AlarmDataService;
 import com.zksy.base.domain.EquipmentBase;
-import com.zksy.base.domain.WorkOrder;
+import com.zksy.ops.domain.WorkOrder;
 import com.zksy.base.mapper.EquipmentBaseMapper;
-import com.zksy.base.service.BusinessWorkOrderService;
+import com.zksy.ops.service.WorkOrderService;
 import com.zksy.common.annotation.Anonymous;
 import com.zksy.common.annotation.Log;
 import com.zksy.common.core.domain.AjaxResult;
@@ -33,7 +33,7 @@ public class AlarmController {
     private AlarmDataService service;
 
     @Autowired
-    private BusinessWorkOrderService businessWorkOrderService;
+    private WorkOrderService businessWorkOrderService;
 
     @Autowired
     private EquipmentBaseMapper equipmentBaseMapper;
@@ -93,7 +93,7 @@ public class AlarmController {
         Object orderDesc = body.get("orderDesc");
         order.setOrderDesc(orderDesc == null || orderDesc.toString().trim().isEmpty()
                 ? buildOrderDesc(alarm) : orderDesc.toString());
-        Long orderId = businessWorkOrderService.addWorkOrder(order);
+        Long orderId = businessWorkOrderService.createWorkOrder(order);
 
         alarm.setAlarmStatus(1);
         alarm.setHandleTime(LocalDateTime.now());

+ 0 - 152
pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/manhole/WorkOrderController.java

@@ -1,152 +0,0 @@
-package com.zksy.web.controller.manhole;
-
-
-import cn.hutool.core.bean.BeanUtil;
-import cn.hutool.core.collection.CollUtil;
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import com.zksy.base.domain.*;
-import com.zksy.base.service.*;
-import com.zksy.common.annotation.Anonymous;
-import com.zksy.common.core.controller.BaseController;
-import com.zksy.common.core.domain.AjaxResult;
-import com.zksy.common.core.domain.AjaxResult2;
-import com.zksy.manhole.dto.in.WorkOrderAddInDTO;
-import com.zksy.manhole.dto.in.WorkOrderFinishedInDTO;
-import com.zksy.manhole.dto.in.WorkOrderOperInDTO;
-import com.zksy.manhole.dto.in.WorkOrderPageInDTO;
-import com.zksy.manhole.dto.in.WorkOrderProcessingInDTO;
-import com.zksy.manhole.dto.out.EquipmentStatusOutDTO;
-import com.zksy.manhole.dto.out.WorkOrderLogOutDTO;
-import com.zksy.manhole.dto.out.WorkOrderOutDTO;
-import io.swagger.annotations.Api;
-import io.swagger.annotations.ApiOperation;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.*;
-
-import java.util.List;
-
-/**
- * 窨井盖监测系统-工单处置管理Controller
- * 
- * @author xianggx
- * @date 2026-05-06
- */
-@RestController
-@RequestMapping("/manhole/workOrder")
-@Api(tags = "窨井盖监测系统-工单处置管理")
-public class WorkOrderController extends BaseController
-{
-    @Autowired
-    private WorkOrderService workOrderService;
-
-    @Autowired
-    private WorkOrderLogService workOrderLogService;
-
-    @Autowired
-    private EquipmentBaseService equipmentBaseService;
-
-    @Autowired
-    private EquipmentStatusService equipmentStatusService;
-
-    @Autowired
-    private EquipmentTypeService equipmentTypeService;
-
-    /**
-     * 分页查询工单信息列表
-     */
-    @Anonymous
-    @PostMapping(value = "/findByPage")
-    @ApiOperation(value = "分页查询工单信息列表")
-    public AjaxResult2<Page<WorkOrderOutDTO>> findByPage(@RequestBody WorkOrderPageInDTO pageInDTO)
-    {
-        return AjaxResult2.success(workOrderService.findByPage(pageInDTO));
-    }
-
-    /**
-     * 获取工单信息详情
-     */
-    @Anonymous
-    @GetMapping(value = "/getById/{id}")
-    @ApiOperation(value = "获取工单信息详情")
-    public AjaxResult2<WorkOrderOutDTO> getById(@PathVariable("id") Long id)
-    {
-        WorkOrder workOrder = workOrderService.getById(id);
-        if(workOrder == null){
-            return AjaxResult2.error("工单ID不存在");
-        }
-        WorkOrderOutDTO outDTO = BeanUtil.copyProperties(workOrder, WorkOrderOutDTO.class);
-        // 查询设备状态
-        EquipmentStatus equipmentStatus = equipmentStatusService.lambdaQuery()
-                .eq(EquipmentStatus::getEquipmentId, outDTO.getDeviceId())
-                .one();
-        if(equipmentStatus != null){
-            EquipmentStatusOutDTO equipmentStatusOutDTO = BeanUtil.copyProperties(equipmentStatus, EquipmentStatusOutDTO.class);
-            outDTO.setEquipmentStatus(equipmentStatusOutDTO);
-        }
-        // 查询设备类别名称
-        EquipmentBase equipmentBase = equipmentBaseService.getById(outDTO.getDeviceId());
-        if(equipmentBase != null){
-            outDTO.setDeviceCode(equipmentBase.getEquipmentCode());
-            EquipmentType equipmentType = equipmentTypeService.getById(equipmentBase.getEquipmentTypeId());
-            if(equipmentType != null){
-                outDTO.setEquipmentTypeName(equipmentType.getTypeName());
-            }
-        }
-        // 查询工单操作日志
-        List<WorkOrderLog> workOrderLogList = workOrderLogService.list(new LambdaQueryWrapper<WorkOrderLog>()
-                .eq(WorkOrderLog::getOrderId, outDTO.getOrderId())
-                .orderByAsc(WorkOrderLog::getOperTime));
-        if(CollUtil.isNotEmpty(workOrderLogList)){
-            List<WorkOrderLogOutDTO> workOrderLogOutDTOList = BeanUtil.copyToList(workOrderLogList, WorkOrderLogOutDTO.class);
-            outDTO.setWorkOrderLogList(workOrderLogOutDTOList);
-        }
-        return AjaxResult2.success(outDTO);
-    }
-
-    /**
-     * 经办工单分页查询(处理中:待接单、已接单、处理中、待验收)
-     */
-    @Anonymous
-    @PostMapping(value = "/findProcessingPage")
-    @ApiOperation(value = "经办工单分页查询")
-    public AjaxResult2<Page<WorkOrderOutDTO>> findProcessingPage(@RequestBody WorkOrderProcessingInDTO inDTO) {
-        return AjaxResult2.success(workOrderService.findProcessingPage(inDTO));
-    }
-
-    /**
-     * 办结工单分页查询(已办结:已结案、已驳回)
-     */
-    @Anonymous
-    @PostMapping(value = "/findFinishedPage")
-    @ApiOperation(value = "办结工单分页查询")
-    public AjaxResult2<Page<WorkOrderOutDTO>> findFinishedPage(@RequestBody WorkOrderFinishedInDTO inDTO) {
-        return AjaxResult2.success(workOrderService.findFinishedPage(inDTO));
-    }
-
-    /**
-     * 新增工单
-     */
-    @Anonymous
-    @PostMapping(value = "/addWorkOrder")
-    @ApiOperation(value = "新增工单")
-    public AjaxResult addWorkOrder(@RequestBody WorkOrderAddInDTO inDTO) {
-        Long orderId = workOrderService.addWorkOrder(inDTO);
-        return orderId != null ? AjaxResult.success("新增成功", orderId)
-                : AjaxResult.error("新增失败");
-    }
-
-    /**
-     * 操作工单
-     * @param inDTO
-     * @return
-     */
-    @Anonymous
-    @PostMapping("/operWorkOrder")
-    @ApiOperation(value = "操作工单")
-    public AjaxResult operWorkOrder(@RequestBody WorkOrderOperInDTO inDTO) {
-        boolean result = workOrderService.operWorkOrder(inDTO);
-        return result ? AjaxResult.success("操作成功",result)
-                : AjaxResult.error("操作失败",result);
-    }
-}

+ 0 - 183
pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/ops/PworkOrderController.java

@@ -1,183 +0,0 @@
-package com.zksy.web.controller.ops;
-
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import com.zksy.base.domain.WorkOrder;
-import com.zksy.base.domain.WorkOrderLog;
-import com.zksy.base.enums.WorkOrderStatusEnum;
-import com.zksy.base.service.BusinessWorkOrderService;
-import com.zksy.common.annotation.DataScope;
-import com.zksy.common.annotation.Log;
-import com.zksy.common.core.domain.AjaxResult;
-import com.zksy.common.core.domain.TreeSelect;
-import com.zksy.common.core.domain.entity.SysUser;
-import com.zksy.common.enums.BusinessType;
-import com.zksy.ops.dto.WorkOrderDelayApplyDTO;
-import com.zksy.ops.dto.WorkOrderDelayAuditDTO;
-import com.zksy.ops.dto.WorkOrderDispatchDTO;
-import com.zksy.ops.dto.WorkOrderOperateDTO;
-import com.zksy.ops.dto.WorkOrderPageDTO;
-import com.zksy.ops.dto.WorkOrderVerifyDTO;
-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.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.web.bind.annotation.PutMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.RestController;
-
-import javax.validation.Valid;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * 运维管理-故障工单
- *
- * @author zksy
- */
-@Slf4j
-@RestController
-@RequestMapping("/api/ops/pworkOrder")
-@Api(tags = "运维管理-故障工单")
-public class PworkOrderController {
-
-    @Autowired
-    private BusinessWorkOrderService workOrderService;
-
-    @PreAuthorize("@ss.hasPermi('ops:pworkOrder:query')")
-    @GetMapping("/findByPage")
-    @ApiOperation("分页查询工单列表")
-    @DataScope(deptAlias = "app_user.work_order", permission = "ops:pworkOrder:query")
-    public AjaxResult findByPage(WorkOrderPageDTO dto) {
-        Page<WorkOrder> page = workOrderService.findByPage(dto);
-        Map<String, Object> data = new HashMap<>(4);
-        data.put("records", page.getRecords());
-        data.put("total", page.getTotal());
-        data.put("pageNum", page.getCurrent());
-        data.put("pageSize", page.getSize());
-        return AjaxResult.success(data);
-    }
-
-    @PreAuthorize("@ss.hasPermi('ops:pworkOrder:query')")
-    @GetMapping("/getById/{orderId}")
-    @ApiOperation("获取工单详情(含操作日志)")
-    public AjaxResult getById(@PathVariable("orderId") Long orderId) {
-        return AjaxResult.success(workOrderService.getDetailById(orderId));
-    }
-
-    @PreAuthorize("@ss.hasPermi('ops:pworkOrder:query')")
-    @GetMapping("/logs/{orderId}")
-    @ApiOperation("查询工单操作日志")
-    public AjaxResult logs(@PathVariable("orderId") Long orderId) {
-        List<WorkOrderLog> logs = workOrderService.getWorkOrderLogs(orderId);
-        return AjaxResult.success(logs);
-    }
-
-    @PreAuthorize("@ss.hasPermi('ops:pworkOrder:query')")
-    @GetMapping("/assignableDeptTree")
-    @ApiOperation("获取可指派部门树")
-    public AjaxResult assignableDeptTree() {
-        List<TreeSelect> tree = workOrderService.assignableDeptTree();
-        return AjaxResult.success(tree);
-    }
-
-    @PreAuthorize("@ss.hasPermi('ops:pworkOrder:query')")
-    @GetMapping("/assignableUsers")
-    @ApiOperation("查询可指派用户")
-    public AjaxResult assignableUsers(@RequestParam(value = "deptId", required = false) Long deptId) {
-        List<SysUser> users = workOrderService.assignableUsers(deptId);
-        return AjaxResult.success(users);
-    }
-
-    @PreAuthorize("@ss.hasPermi('ops:pworkOrder:add')")
-    @PostMapping("/add")
-    @ApiOperation("新增故障工单(初始状态:待派单)")
-    @Log(title = "新增故障工单", businessType = BusinessType.INSERT)
-    public AjaxResult add(@Valid @RequestBody WorkOrder workOrder) {
-        Long orderId = workOrderService.addWorkOrder(workOrder);
-        return AjaxResult.success("工单创建成功", orderId);
-    }
-
-    @PreAuthorize("@ss.hasPermi('ops:pworkOrder:dispatch')")
-    @PutMapping("/dispatch")
-    @ApiOperation("派单(待派单 → 待接单)")
-    @Log(title = "工单派单", businessType = BusinessType.UPDATE)
-    public AjaxResult dispatch(@Valid @RequestBody WorkOrderDispatchDTO dto) {
-        return workOrderService.dispatch(dto) ? AjaxResult.success("派单成功") : AjaxResult.error("派单失败");
-    }
-
-    @PreAuthorize("@ss.hasPermi('ops:pworkOrder:accept')")
-    @PutMapping("/accept")
-    @ApiOperation("核查接单(待接单 → 已接单)")
-    @Log(title = "工单接单", businessType = BusinessType.UPDATE)
-    public AjaxResult accept(@Valid @RequestBody WorkOrderOperateDTO dto) {
-        return workOrderService.accept(dto) ? AjaxResult.success("接单成功") : AjaxResult.error("接单失败");
-    }
-
-    @PreAuthorize("@ss.hasPermi('ops:pworkOrder:process')")
-    @PutMapping("/process")
-    @ApiOperation("工单处置(已接单/处理中/延期中 → 处理中,可多次处置)")
-    @Log(title = "工单处置", businessType = BusinessType.UPDATE)
-    public AjaxResult process(@Valid @RequestBody WorkOrderOperateDTO dto) {
-        return workOrderService.process(dto) ? AjaxResult.success("处置成功") : AjaxResult.error("处置失败");
-    }
-
-    @PreAuthorize("@ss.hasPermi('ops:pworkOrder:submitVerify')")
-    @PutMapping("/submitVerify")
-    @ApiOperation("提交验收(处理中/延期中 → 待验收)")
-    @Log(title = "工单提交验收", businessType = BusinessType.UPDATE)
-    public AjaxResult submitVerify(@Valid @RequestBody WorkOrderOperateDTO dto) {
-        return workOrderService.submitVerify(dto) ? AjaxResult.success("提交验收成功") : AjaxResult.error("提交验收失败");
-    }
-
-    @PreAuthorize("@ss.hasPermi('ops:pworkOrder:verify')")
-    @PutMapping("/verify")
-    @ApiOperation("工单验收(待验收 → 已结案 / 处理中)")
-    @Log(title = "工单验收", businessType = BusinessType.UPDATE)
-    public AjaxResult verify(@Valid @RequestBody WorkOrderVerifyDTO dto) {
-        return workOrderService.verify(dto) ? AjaxResult.success("验收完成") : AjaxResult.error("验收失败");
-    }
-
-    @PreAuthorize("@ss.hasPermi('ops:pworkOrder:cancel')")
-    @PutMapping("/cancel")
-    @ApiOperation("取消工单(待派单/待接单/已接单/处理中/待验收 → 已取消)")
-    @Log(title = "取消工单", businessType = BusinessType.UPDATE)
-    public AjaxResult cancel(@Valid @RequestBody WorkOrderOperateDTO dto) {
-        return workOrderService.cancel(dto) ? AjaxResult.success("取消成功") : AjaxResult.error("取消失败");
-    }
-
-    @PreAuthorize("@ss.hasPermi('ops:pworkOrder:delay')")
-    @PutMapping("/delay/apply")
-    @ApiOperation("申请延期(处理中且无延期申请 → 待延期审核)")
-    @Log(title = "工单申请延期", businessType = BusinessType.UPDATE)
-    public AjaxResult applyDelay(@Valid @RequestBody WorkOrderDelayApplyDTO dto) {
-        return workOrderService.applyDelay(dto) ? AjaxResult.success("延期申请已提交") : AjaxResult.error("延期申请失败");
-    }
-
-    @PreAuthorize("@ss.hasPermi('ops:pworkOrder:delayAudit')")
-    @PutMapping("/delay/audit")
-    @ApiOperation("延期审核(待延期审核 → 延期审核通过 / 延期审核驳回)")
-    @Log(title = "工单延期审核", businessType = BusinessType.UPDATE)
-    public AjaxResult auditDelay(@Valid @RequestBody WorkOrderDelayAuditDTO dto) {
-        return workOrderService.auditDelay(dto) ? AjaxResult.success("延期审核完成") : AjaxResult.error("延期审核失败");
-    }
-
-    @PreAuthorize("@ss.hasPermi('ops:pworkOrder:remove')")
-    @DeleteMapping("/deleteById/{orderId}")
-    @ApiOperation("删除工单(仅限已取消工单)")
-    @Log(title = "删除工单", businessType = BusinessType.DELETE)
-    public AjaxResult deleteById(@PathVariable("orderId") Long orderId) {
-        WorkOrder order = workOrderService.getById(orderId);
-        if (order != null && !WorkOrderStatusEnum.CANCELLED.getCode().equals(order.getOrderStatus())) {
-            return AjaxResult.error("仅已取消工单允许删除");
-        }
-        return workOrderService.deleteWithLogs(orderId) ? AjaxResult.success("删除成功") : AjaxResult.error("删除失败");
-    }
-}

+ 194 - 0
pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/ops/WorkOrderController.java

@@ -0,0 +1,194 @@
+package com.zksy.web.controller.ops;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.zksy.ops.domain.WorkOrder;
+import com.zksy.ops.domain.WorkOrderLog;
+import com.zksy.common.annotation.DataScope;
+import com.zksy.common.annotation.Log;
+import com.zksy.common.core.domain.AjaxResult;
+import com.zksy.common.core.domain.TreeSelect;
+import com.zksy.common.core.domain.entity.SysUser;
+import com.zksy.common.enums.BusinessType;
+import com.zksy.ops.dto.WorkOrderAddDTO;
+import com.zksy.ops.dto.WorkOrderDelayApplyDTO;
+import com.zksy.ops.dto.WorkOrderDelayAuditDTO;
+import com.zksy.ops.dto.WorkOrderDispatchDTO;
+import com.zksy.ops.dto.WorkOrderOperateDTO;
+import com.zksy.ops.dto.WorkOrderPageDTO;
+import com.zksy.ops.dto.WorkOrderVerifyDTO;
+import com.zksy.ops.service.WorkOrderService;
+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.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.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.validation.Valid;
+import java.util.List;
+
+/**
+ * 故障工单
+ *
+ * @author zengy
+ */
+@Slf4j
+@RestController
+@RequestMapping("/api/ops/workOrder")
+@Api(tags = "故障工单")
+public class WorkOrderController {
+
+    @Autowired
+    private WorkOrderService workOrderService;
+
+    @PreAuthorize("@ss.hasPermi('ops:workOrder:query')")
+    @GetMapping
+    @ApiOperation("分页查询工单列表")
+    @DataScope(deptAlias = "app_user.work_order", permission = "ops:workOrder:query")
+    public AjaxResult page(WorkOrderPageDTO query) {
+        Page<WorkOrder> page = workOrderService.findByPage(query);
+        return AjaxResult.success(page);
+    }
+
+    @PreAuthorize("@ss.hasPermi('ops:workOrder:query')")
+    @GetMapping("/statusCount")
+    @ApiOperation("按状态统计工单数量(列表状态筛选条使用,忽略入参中的状态筛选)")
+    @DataScope(deptAlias = "app_user.work_order", permission = "ops:workOrder:query")
+    public AjaxResult statusCount(WorkOrderPageDTO query) {
+        return AjaxResult.success(workOrderService.countByStatus(query));
+    }
+
+    @PreAuthorize("@ss.hasPermi('ops:workOrder:query')")
+    @GetMapping("/{orderId}")
+    @ApiOperation("获取工单详情(含操作日志)")
+    public AjaxResult detail(@PathVariable("orderId") Long orderId) {
+        return AjaxResult.success(workOrderService.getDetail(orderId));
+    }
+
+    @PreAuthorize("@ss.hasPermi('ops:workOrder:query')")
+    @GetMapping("/{orderId}/logs")
+    @ApiOperation("查询工单操作日志")
+    public AjaxResult logs(@PathVariable("orderId") Long orderId) {
+        List<WorkOrderLog> logs = workOrderService.listLogs(orderId);
+        return AjaxResult.success(logs);
+    }
+
+    @PreAuthorize("@ss.hasPermi('ops:workOrder:query')")
+    @GetMapping("/assignable/deptTree")
+    @ApiOperation("获取可指派部门树")
+    public AjaxResult assignableDeptTree() {
+        List<TreeSelect> tree = workOrderService.assignableDeptTree();
+        return AjaxResult.success(tree);
+    }
+
+    @PreAuthorize("@ss.hasPermi('ops:workOrder:query')")
+    @GetMapping("/assignable/users")
+    @ApiOperation("查询可指派用户")
+    public AjaxResult assignableUsers(@RequestParam(value = "deptId", required = false) Long deptId) {
+        List<SysUser> users = workOrderService.assignableUsers(deptId);
+        return AjaxResult.success(users);
+    }
+
+    @PreAuthorize("@ss.hasPermi('ops:workOrder:add')")
+    @PostMapping
+    @ApiOperation("新增故障工单(初始状态:待派单)")
+    @Log(title = "新增故障工单", businessType = BusinessType.INSERT)
+    public AjaxResult create(@Valid @RequestBody WorkOrderAddDTO dto) {
+        Long orderId = workOrderService.createWorkOrder(toEntity(dto));
+        return AjaxResult.success("工单创建成功", orderId);
+    }
+
+    @PreAuthorize("@ss.hasPermi('ops:workOrder:dispatch')")
+    @PutMapping("/{orderId}/dispatch")
+    @ApiOperation("派单(待派单 → 待接单)")
+    @Log(title = "工单派单", businessType = BusinessType.UPDATE)
+    public AjaxResult dispatch(@PathVariable("orderId") Long orderId, @Valid @RequestBody WorkOrderDispatchDTO dto) {
+        return workOrderService.dispatch(orderId, dto) ? AjaxResult.success("派单成功") : AjaxResult.error("派单失败");
+    }
+
+    @PreAuthorize("@ss.hasPermi('ops:workOrder:accept')")
+    @PutMapping("/{orderId}/accept")
+    @ApiOperation("核查接单(待接单 → 已接单)")
+    @Log(title = "工单接单", businessType = BusinessType.UPDATE)
+    public AjaxResult accept(@PathVariable("orderId") Long orderId, @Valid @RequestBody WorkOrderOperateDTO dto) {
+        return workOrderService.accept(orderId, dto) ? AjaxResult.success("接单成功") : AjaxResult.error("接单失败");
+    }
+
+    @PreAuthorize("@ss.hasPermi('ops:workOrder:process')")
+    @PutMapping("/{orderId}/process")
+    @ApiOperation("工单处置(已接单/处理中/延期中 → 处理中,可多次处置)")
+    @Log(title = "工单处置", businessType = BusinessType.UPDATE)
+    public AjaxResult process(@PathVariable("orderId") Long orderId, @Valid @RequestBody WorkOrderOperateDTO dto) {
+        return workOrderService.process(orderId, dto) ? AjaxResult.success("处置成功") : AjaxResult.error("处置失败");
+    }
+
+    @PreAuthorize("@ss.hasPermi('ops:workOrder:submitVerify')")
+    @PutMapping("/{orderId}/submitVerify")
+    @ApiOperation("提交验收(处理中/延期中 → 待验收)")
+    @Log(title = "工单提交验收", businessType = BusinessType.UPDATE)
+    public AjaxResult submitVerify(@PathVariable("orderId") Long orderId, @Valid @RequestBody WorkOrderOperateDTO dto) {
+        return workOrderService.submitVerify(orderId, dto) ? AjaxResult.success("提交验收成功") : AjaxResult.error("提交验收失败");
+    }
+
+    @PreAuthorize("@ss.hasPermi('ops:workOrder:verify')")
+    @PutMapping("/{orderId}/verify")
+    @ApiOperation("工单验收(待验收 → 已结案 / 处理中)")
+    @Log(title = "工单验收", businessType = BusinessType.UPDATE)
+    public AjaxResult verify(@PathVariable("orderId") Long orderId, @Valid @RequestBody WorkOrderVerifyDTO dto) {
+        return workOrderService.verify(orderId, dto) ? AjaxResult.success("验收完成") : AjaxResult.error("验收失败");
+    }
+
+    @PreAuthorize("@ss.hasPermi('ops:workOrder:cancel')")
+    @PutMapping("/{orderId}/cancel")
+    @ApiOperation("取消工单(待派单/待接单/已接单/处理中/待验收 → 已取消)")
+    @Log(title = "取消工单", businessType = BusinessType.UPDATE)
+    public AjaxResult cancel(@PathVariable("orderId") Long orderId, @Valid @RequestBody WorkOrderOperateDTO dto) {
+        return workOrderService.cancel(orderId, dto) ? AjaxResult.success("取消成功") : AjaxResult.error("取消失败");
+    }
+
+    @PreAuthorize("@ss.hasPermi('ops:workOrder:delay')")
+    @PutMapping("/{orderId}/delay/apply")
+    @ApiOperation("申请延期(处理中且无延期申请 → 待延期审核)")
+    @Log(title = "工单申请延期", businessType = BusinessType.UPDATE)
+    public AjaxResult applyDelay(@PathVariable("orderId") Long orderId, @Valid @RequestBody WorkOrderDelayApplyDTO dto) {
+        return workOrderService.applyDelay(orderId, dto) ? AjaxResult.success("延期申请已提交") : AjaxResult.error("延期申请失败");
+    }
+
+    @PreAuthorize("@ss.hasPermi('ops:workOrder:delayAudit')")
+    @PutMapping("/{orderId}/delay/audit")
+    @ApiOperation("延期审核(待延期审核 → 延期审核通过 / 延期审核驳回)")
+    @Log(title = "工单延期审核", businessType = BusinessType.UPDATE)
+    public AjaxResult auditDelay(@PathVariable("orderId") Long orderId, @Valid @RequestBody WorkOrderDelayAuditDTO dto) {
+        return workOrderService.auditDelay(orderId, dto) ? AjaxResult.success("延期审核完成") : AjaxResult.error("延期审核失败");
+    }
+
+    @PreAuthorize("@ss.hasPermi('ops:workOrder:remove')")
+    @DeleteMapping("/{orderId}")
+    @ApiOperation("删除工单(仅限已取消工单)")
+    @Log(title = "删除工单", businessType = BusinessType.DELETE)
+    public AjaxResult delete(@PathVariable("orderId") Long orderId) {
+        return workOrderService.deleteWorkOrder(orderId) ? AjaxResult.success("删除成功") : AjaxResult.error("删除失败");
+    }
+
+    /**
+     * 入参 DTO 转工单实体:只映射创建阶段客户端可决定的字段,
+     * 工单编号、状态、归属部门、创建时间等一律由 Service 生成。
+     */
+    private WorkOrder toEntity(WorkOrderAddDTO dto) {
+        WorkOrder workOrder = new WorkOrder();
+        workOrder.setOrderDesc(dto.getOrderDesc());
+        workOrder.setDeviceId(dto.getDeviceId());
+        workOrder.setOrderLevel(dto.getOrderLevel());
+        workOrder.setFaultTime(dto.getFaultTime());
+        workOrder.setPlanFinishTime(dto.getPlanFinishTime());
+        return workOrder;
+    }
+}

+ 4 - 4
pipe-network-service/zksy-admin/src/main/java/com/zksy/web/websocket/DeviceStatusWebSocketHandler.java

@@ -4,8 +4,8 @@ import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
 import com.zksy.base.domain.EquipmentStatus;
 import com.zksy.base.service.EquipmentStatusService;
-import com.zksy.base.domain.vo.WorkOrderPushVO;
-import com.zksy.base.service.BusinessWorkOrderService;
+import com.zksy.ops.domain.vo.WorkOrderPushVO;
+import com.zksy.ops.service.WorkOrderService;
 import com.zksy.common.core.domain.model.LoginUser;
 import com.zksy.common.utils.DateUtils;
 import com.zksy.framework.web.service.TokenService;
@@ -98,7 +98,7 @@ public class DeviceStatusWebSocketHandler extends TextWebSocketHandler {
     private TokenService tokenService;
 
     @Autowired
-    private BusinessWorkOrderService businessWorkOrderService;
+    private WorkOrderService businessWorkOrderService;
 
     @Value("${websocket.work-order.max-items:100}")
     private int workOrderMaxItems;
@@ -245,7 +245,7 @@ public class DeviceStatusWebSocketHandler extends TextWebSocketHandler {
             sendResponse(session, "workOrderReminder", false, null, "请先完成 WebSocket 鉴权");
             return;
         }
-        List<WorkOrderPushVO> candidates = businessWorkOrderService.selectPendingOrOverdueWorkOrders(new java.util.Date());
+        List<WorkOrderPushVO> candidates = businessWorkOrderService.listPendingOrOverdue(new java.util.Date());
         if (candidates == null) {
             candidates = Collections.emptyList();
         }

+ 4 - 4
pipe-network-service/zksy-admin/src/main/java/com/zksy/web/websocket/WorkOrderWebSocketPushTask.java

@@ -3,8 +3,8 @@ package com.zksy.web.websocket;
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
 import com.alibaba.fastjson.serializer.SerializerFeature;
-import com.zksy.base.domain.vo.WorkOrderPushVO;
-import com.zksy.base.service.BusinessWorkOrderService;
+import com.zksy.ops.domain.vo.WorkOrderPushVO;
+import com.zksy.ops.service.WorkOrderService;
 import com.zksy.common.core.domain.model.LoginUser;
 import com.zksy.common.utils.DateUtils;
 import lombok.extern.slf4j.Slf4j;
@@ -34,7 +34,7 @@ public class WorkOrderWebSocketPushTask {
     public static final String PUSH_TYPE = "workOrderReminderPush";
 
     @Autowired
-    private BusinessWorkOrderService businessWorkOrderService;
+    private WorkOrderService businessWorkOrderService;
 
     @Autowired
     private DeviceStatusWebSocketHandler webSocketHandler;
@@ -71,7 +71,7 @@ public class WorkOrderWebSocketPushTask {
         }
 
         Date now = new Date();
-        List<WorkOrderPushVO> candidates = businessWorkOrderService.selectPendingOrOverdueWorkOrders(now);
+        List<WorkOrderPushVO> candidates = businessWorkOrderService.listPendingOrOverdue(now);
         if (candidates == null) {
             candidates = Collections.emptyList();
         }

+ 8 - 8
pipe-network-service/zksy-admin/src/test/java/com/zksy/web/controller/WaterSupply/DeviceAbnormal/DeviceAbnormalControllerTest.java

@@ -2,9 +2,9 @@ package com.zksy.web.controller.WaterSupply.DeviceAbnormal;
 
 import com.zksy.base.domain.vo.AbnormalDeviceVO;
 import com.zksy.base.service.WaterSupplyDeviceService;
-import com.zksy.base.domain.WorkOrder;
+import com.zksy.ops.domain.WorkOrder;
 import com.zksy.base.domain.vo.MaintainOrderVO;
-import com.zksy.base.service.BusinessWorkOrderService;
+import com.zksy.ops.service.WorkOrderService;
 import com.zksy.common.core.domain.AjaxResult;
 import com.zksy.common.core.domain.entity.SysUser;
 import com.zksy.common.core.domain.model.LoginUser;
@@ -34,13 +34,13 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
 class DeviceAbnormalControllerTest {
 
     private WaterSupplyDeviceService deviceService;
-    private BusinessWorkOrderService businessWorkOrderService;
+    private WorkOrderService businessWorkOrderService;
     private MockMvc mockMvc;
 
     @BeforeEach
     void setUp() {
         deviceService = mock(WaterSupplyDeviceService.class);
-        businessWorkOrderService = mock(BusinessWorkOrderService.class);
+        businessWorkOrderService = mock(WorkOrderService.class);
         DeviceAbnormalController controller = new DeviceAbnormalController();
         ReflectionTestUtils.setField(controller, "waterSupplyDeviceService", deviceService);
         ReflectionTestUtils.setField(controller, "businessWorkOrderService", businessWorkOrderService);
@@ -79,7 +79,7 @@ class DeviceAbnormalControllerTest {
         order.setOrderNo("WO-T6-001");
         order.setOrderStatus(1);
         when(deviceService.createAbnormalWorkOrder(any())).thenReturn(order);
-        when(businessWorkOrderService.dispatch(any(WorkOrderDispatchDTO.class))).thenReturn(true);
+        when(businessWorkOrderService.dispatch(any(), any(WorkOrderDispatchDTO.class))).thenReturn(true);
 
         mockMvc.perform(post("/waterSupply/device/abnormal/order")
                         .contentType("application/json")
@@ -93,7 +93,7 @@ class DeviceAbnormalControllerTest {
                     assertTrue(body.contains("\"orderStatus\":1"));
                     assertTrue(body.contains("\"receiveUser\":88"));
                 });
-        verify(businessWorkOrderService).dispatch(any(WorkOrderDispatchDTO.class));
+        verify(businessWorkOrderService).dispatch(any(), any(WorkOrderDispatchDTO.class));
     }
 
     @Test
@@ -115,7 +115,7 @@ class DeviceAbnormalControllerTest {
         assertEquals(200, result.get("code"));
         assertEquals(0, ((WorkOrder) result.get("data")).getOrderStatus());
         org.mockito.Mockito.verify(businessWorkOrderService, org.mockito.Mockito.never())
-                .dispatch(org.mockito.ArgumentMatchers.any(WorkOrderDispatchDTO.class));
+                .dispatch(org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any(WorkOrderDispatchDTO.class));
     }
 
     @Test
@@ -139,7 +139,7 @@ class DeviceAbnormalControllerTest {
         order.setOrderNo("WO-T6-002");
         order.setOrderStatus(1);
         when(deviceService.createAbnormalWorkOrder(any())).thenReturn(order);
-        when(businessWorkOrderService.dispatch(any(WorkOrderDispatchDTO.class))).thenReturn(false);
+        when(businessWorkOrderService.dispatch(any(), any(WorkOrderDispatchDTO.class))).thenReturn(false);
 
         mockMvc.perform(post("/waterSupply/device/abnormal/order")
                         .contentType("application/json")

+ 2 - 2
pipe-network-service/zksy-admin/src/test/java/com/zksy/web/websocket/DeviceStatusCockpitWebSocketHandlerTest.java

@@ -3,7 +3,7 @@ package com.zksy.web.websocket;
 import com.alibaba.fastjson.JSONObject;
 import com.zksy.base.domain.EquipmentStatus;
 import com.zksy.base.service.EquipmentStatusService;
-import com.zksy.base.service.BusinessWorkOrderService;
+import com.zksy.ops.service.WorkOrderService;
 import com.zksy.framework.web.service.TokenService;
 import org.springframework.data.redis.connection.Message;
 import org.junit.jupiter.api.BeforeEach;
@@ -47,7 +47,7 @@ class DeviceStatusCockpitWebSocketHandlerTest {
         ReflectionTestUtils.setField(handler, "equipmentStatusService", equipmentStatusService);
         ReflectionTestUtils.setField(handler, "subscriptionManager", subscriptionManager);
         ReflectionTestUtils.setField(handler, "tokenService", mock(TokenService.class));
-        ReflectionTestUtils.setField(handler, "businessWorkOrderService", mock(BusinessWorkOrderService.class));
+        ReflectionTestUtils.setField(handler, "businessWorkOrderService", mock(WorkOrderService.class));
         ReflectionTestUtils.setField(handler, "workOrderMaxItems", 100);
     }
 

+ 2 - 2
pipe-network-service/zksy-system/src/main/java/com/zksy/WaterSupply/MonitorAlarm/dto/out/AlarmFlowOutDTO.java

@@ -1,7 +1,7 @@
 package com.zksy.WaterSupply.MonitorAlarm.dto.out;
 
-import com.zksy.base.domain.WorkOrder;
-import com.zksy.base.domain.WorkOrderLog;
+import com.zksy.ops.domain.WorkOrder;
+import com.zksy.ops.domain.WorkOrderLog;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;

+ 4 - 4
pipe-network-service/zksy-system/src/main/java/com/zksy/WaterSupply/MonitorAlarm/service/impl/WaterSupplyAlarmServiceImpl.java

@@ -18,11 +18,11 @@ import com.zksy.WaterSupply.MonitorAlarm.service.IWaterSupplyAlarmService;
 import com.zksy.base.alarm.domain.AlarmData;
 import com.zksy.base.alarm.service.AlarmDataService;
 import com.zksy.base.domain.EquipmentBase;
-import com.zksy.base.domain.WorkOrder;
-import com.zksy.base.domain.WorkOrderLog;
+import com.zksy.ops.domain.WorkOrder;
+import com.zksy.ops.domain.WorkOrderLog;
 import com.zksy.base.mapper.EquipmentBaseMapper;
-import com.zksy.base.mapper.WorkOrderLogMapper;
-import com.zksy.base.mapper.WorkOrderMapper;
+import com.zksy.ops.mapper.WorkOrderLogMapper;
+import com.zksy.ops.mapper.WorkOrderMapper;
 import com.zksy.base.service.EquipmentBaseService;
 import com.zksy.common.core.domain.model.LoginUser;
 import com.zksy.common.exception.ServiceException;

+ 8 - 8
pipe-network-service/zksy-system/src/main/java/com/zksy/base/alarm/service/impl/AlarmDataServiceImpl.java

@@ -8,7 +8,7 @@ import com.zksy.base.alarm.mapper.AlarmDataMapper;
 import com.zksy.base.alarm.service.AlarmDataService;
 import com.zksy.base.domain.EquipmentBase;
 import com.zksy.base.mapper.EquipmentBaseMapper;
-import com.zksy.base.mapper.WorkOrderMapper;
+import com.zksy.ops.mapper.WorkOrderMapper;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
@@ -85,16 +85,16 @@ public class AlarmDataServiceImpl extends ServiceImpl<AlarmDataMapper, AlarmData
         if (alarmIds.isEmpty()) {
             return;
         }
-        List<com.zksy.base.domain.WorkOrder> orders = workOrderMapper.selectList(
-                new LambdaQueryWrapper<com.zksy.base.domain.WorkOrder>()
-                        .in(com.zksy.base.domain.WorkOrder::getAlarmId, alarmIds)
-                        .orderByDesc(com.zksy.base.domain.WorkOrder::getOrderId));
-        java.util.Map<String, com.zksy.base.domain.WorkOrder> orderMap = orders.stream()
+        List<com.zksy.ops.domain.WorkOrder> orders = workOrderMapper.selectList(
+                new LambdaQueryWrapper<com.zksy.ops.domain.WorkOrder>()
+                        .in(com.zksy.ops.domain.WorkOrder::getAlarmId, alarmIds)
+                        .orderByDesc(com.zksy.ops.domain.WorkOrder::getOrderId));
+        java.util.Map<String, com.zksy.ops.domain.WorkOrder> orderMap = orders.stream()
                 .filter(order -> order.getAlarmId() != null)
-                .collect(Collectors.toMap(com.zksy.base.domain.WorkOrder::getAlarmId,
+                .collect(Collectors.toMap(com.zksy.ops.domain.WorkOrder::getAlarmId,
                         order -> order, (first, ignored) -> first));
         for (AlarmData record : records) {
-            com.zksy.base.domain.WorkOrder order = orderMap.get(record.getId());
+            com.zksy.ops.domain.WorkOrder order = orderMap.get(record.getId());
             boolean hasOrder = order != null;
             record.setHasWorkOrder(hasOrder);
             record.setWorkOrderId(hasOrder ? order.getOrderId() : null);

+ 6 - 6
pipe-network-service/zksy-system/src/main/java/com/zksy/base/drill/service/DrillAlarmSyncService.java

@@ -5,14 +5,14 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.zksy.base.alarm.domain.AlarmData;
 import com.zksy.base.domain.CaseInfo;
 import com.zksy.base.domain.EquipmentBase;
-import com.zksy.base.enums.WorkOrderLogOperTypeEnum;
-import com.zksy.base.enums.WorkOrderStatusEnum;
-import com.zksy.base.domain.WorkOrder;
-import com.zksy.base.domain.WorkOrderLog;
+import com.zksy.ops.enums.WorkOrderLogOperTypeEnum;
+import com.zksy.ops.enums.WorkOrderStatusEnum;
+import com.zksy.ops.domain.WorkOrder;
+import com.zksy.ops.domain.WorkOrderLog;
 import com.zksy.base.mapper.CaseInfoMapper;
 import com.zksy.base.mapper.EquipmentBaseMapper;
-import com.zksy.base.mapper.WorkOrderLogMapper;
-import com.zksy.base.mapper.WorkOrderMapper;
+import com.zksy.ops.mapper.WorkOrderLogMapper;
+import com.zksy.ops.mapper.WorkOrderMapper;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;

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

@@ -1,20 +0,0 @@
-package com.zksy.base.mapper;
-
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import com.zksy.base.domain.WorkOrderLog;
-import org.apache.ibatis.annotations.Mapper;
-
-/**
-* @author Administrator
-* @description 针对表【work_order_log(工单操作日志表)】的数据库操作Mapper
-* @createDate 2026-06-11 11:51:09
-* @Entity com.zksy.base.domain.WorkOrderLog
-*/
-@Mapper
-public interface WorkOrderLogMapper extends BaseMapper<WorkOrderLog> {
-
-}
-
-
-
-

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

@@ -1,28 +0,0 @@
-package com.zksy.base.mapper;
-
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import com.zksy.base.domain.WorkOrder;
-import org.apache.ibatis.annotations.Mapper;
-import org.apache.ibatis.annotations.Param;
-
-import java.util.Date;
-import java.util.List;
-
-/**
-* @author Administrator
-* @description 针对表【work_order(运维工单表)】的数据库操作Mapper
-* @createDate 2026-06-11 11:50:08
-* @Entity com.zksy.base.domain.WorkOrder
-*/
-@Mapper
-public interface WorkOrderMapper extends BaseMapper<WorkOrder> {
-
-    /**
-     * 查询待处理或已超时的未办结工单。
-     */
-    List<WorkOrder> selectPendingOrOverdue(@Param("now") Date now);
-}
-
-
-
-

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

@@ -1,123 +0,0 @@
-package com.zksy.base.service;
-
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import com.baomidou.mybatisplus.extension.service.IService;
-import com.zksy.base.domain.WorkOrder;
-import com.zksy.base.domain.WorkOrderLog;
-import com.zksy.base.domain.vo.WorkOrderPushVO;
-import com.zksy.common.core.domain.TreeSelect;
-import com.zksy.common.core.domain.entity.SysUser;
-import com.zksy.ops.dto.WorkOrderDelayApplyDTO;
-import com.zksy.ops.dto.WorkOrderDelayAuditDTO;
-import com.zksy.ops.dto.WorkOrderDetailVO;
-import com.zksy.ops.dto.WorkOrderDispatchDTO;
-import com.zksy.ops.dto.WorkOrderOperateDTO;
-import com.zksy.ops.dto.WorkOrderPageDTO;
-import com.zksy.ops.dto.WorkOrderVerifyDTO;
-
-import java.util.Date;
-import java.util.List;
-
-/**
- * 运维工单 Service(ops 工单流程)
- *
- * <p>状态流转:0待派单 →(派单) 1待接单 →(核查) 2已接单 →(处置) 3处理中
- * →(提交验收) 4待验收 →(验收通过) 5已结案;旁路:6延期中、7已取消</p>
- *
- * @author zksy
- */
-public interface BusinessWorkOrderService extends IService<WorkOrder> {
-
-    /**
-     * 分页查询工单列表(含数据权限过滤)
-     */
-    Page<WorkOrder> findByPage(WorkOrderPageDTO dto);
-
-    /**
-     * 查询工单详情(含操作日志)
-     */
-    WorkOrderDetailVO getDetailById(Long orderId);
-
-    /**
-     * 查询工单操作日志列表
-     */
-    List<WorkOrderLog> getWorkOrderLogs(Long orderId);
-
-    /**
-     * 新增故障工单(初始状态:待派单)
-     *
-     * @return 工单ID
-     */
-    Long addWorkOrder(WorkOrder workOrder);
-
-    /**
-     * 派单:待派单 → 待接单
-     */
-    boolean dispatch(WorkOrderDispatchDTO dto);
-
-    /**
-     * 核查接单:待接单 → 已接单
-     */
-    boolean accept(WorkOrderOperateDTO dto);
-
-    /**
-     * 处置:已接单/处理中/延期中 → 处理中(首次处置同步写入维修前附件)
-     */
-    boolean process(WorkOrderOperateDTO dto);
-
-    /**
-     * 提交验收:处理中/延期中 → 待验收(同步写入维修后附件)
-     */
-    boolean submitVerify(WorkOrderOperateDTO dto);
-
-    /**
-     * 验收:待验收 → 已结案(通过) / 处理中(驳回)
-     */
-    boolean verify(WorkOrderVerifyDTO dto);
-
-    /**
-     * 取消工单:待派单/待接单/已接单/处理中/待验收 → 已取消
-     */
-    boolean cancel(WorkOrderOperateDTO dto);
-
-    /**
-     * 延期申请:处理中且无延期申请 → 待延期审核
-     */
-    boolean applyDelay(WorkOrderDelayApplyDTO dto);
-
-    /**
-     * 延期审核:待延期审核 → 审核通过 / 审核驳回
-     */
-    boolean auditDelay(WorkOrderDelayAuditDTO dto);
-
-    /**
-     * 删除工单,并级联删除该工单下的操作日志(work_order_log)
-     *
-     * @param orderId 工单ID
-     * @return 是否删除成功
-     */
-    boolean deleteWithLogs(Long orderId);
-
-    /**
-     * 获取可指派部门树
-     */
-    List<TreeSelect> assignableDeptTree();
-
-    /**
-     * 查询可指派用户
-     */
-    List<SysUser> assignableUsers(Long deptId);
-
-    /**
-     * 从隐患创建工单
-     *
-     * @return 工单ID
-     */
-    Long createWorkOrderFromHazard(String hazardId, String hazardNo, String hazardDesc, String equipmentId,
-                                    String hazardLevel, String hazardImages, Date planFinishTime);
-
-    /**
-     * 查询需要通过 WebSocket 提醒的工单(待派单/待接单 或 已超时)
-     */
-    List<WorkOrderPushVO> selectPendingOrOverdueWorkOrders(Date now);
-}

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

@@ -5,14 +5,14 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.zksy.base.alarm.domain.AlarmData;
 import com.zksy.base.domain.CaseInfo;
 import com.zksy.base.domain.EquipmentBase;
-import com.zksy.base.enums.WorkOrderLogOperTypeEnum;
-import com.zksy.base.enums.WorkOrderStatusEnum;
-import com.zksy.base.domain.WorkOrder;
-import com.zksy.base.domain.WorkOrderLog;
+import com.zksy.ops.enums.WorkOrderLogOperTypeEnum;
+import com.zksy.ops.enums.WorkOrderStatusEnum;
+import com.zksy.ops.domain.WorkOrder;
+import com.zksy.ops.domain.WorkOrderLog;
 import com.zksy.base.mapper.CaseInfoMapper;
 import com.zksy.base.mapper.EquipmentBaseMapper;
-import com.zksy.base.mapper.WorkOrderLogMapper;
-import com.zksy.base.mapper.WorkOrderMapper;
+import com.zksy.ops.mapper.WorkOrderLogMapper;
+import com.zksy.ops.mapper.WorkOrderMapper;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;

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

@@ -3,7 +3,7 @@ package com.zksy.base.service;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.zksy.base.domain.EquipmentBase;
-import com.zksy.base.domain.WorkOrder;
+import com.zksy.ops.domain.WorkOrder;
 import com.zksy.base.domain.vo.*;
 
 import java.util.List;

+ 0 - 50
pipe-network-service/zksy-system/src/main/java/com/zksy/base/service/WorkOrderService.java

@@ -1,50 +0,0 @@
-package com.zksy.base.service;
-
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import com.baomidou.mybatisplus.extension.service.IService;
-import com.zksy.base.domain.WorkOrder;
-import com.zksy.manhole.dto.in.*;
-import com.zksy.manhole.dto.out.WorkOrderOutDTO;
-
-/**
-* @author Administrator
-* @description 针对表【work_order(运维工单表)】的数据库操作Service
-* @createDate 2026-06-11 11:50:08
-*/
-public interface WorkOrderService extends IService<WorkOrder> {
-
-    /**
-     * 分页查询工单信息列表
-     * @param pageInDTO
-     * @return
-     */
-    Page<WorkOrderOutDTO> findByPage(WorkOrderPageInDTO pageInDTO);
-
-    /**
-     * 新增工单
-     * @param inDTO
-     * @return 工单ID(新增成功返回 orderId,失败返回 null)
-     */
-    Long addWorkOrder(WorkOrderAddInDTO inDTO);
-
-    /**
-     * 经办工单分页查询(处理中:待接单、已接单、处理中、待验收)
-     * @param inDTO
-     * @return
-     */
-    Page<WorkOrderOutDTO> findProcessingPage(WorkOrderProcessingInDTO inDTO);
-
-    /**
-     * 办结工单分页查询(已办结:已结案、已驳回)
-     * @param inDTO
-     * @return
-     */
-    Page<WorkOrderOutDTO> findFinishedPage(WorkOrderFinishedInDTO inDTO);
-
-    /**
-     * 操作工单
-     * @param inDTO
-     * @return
-     */
-    boolean operWorkOrder(WorkOrderOperInDTO inDTO);
-}

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

@@ -25,6 +25,8 @@ import com.zksy.manhole.dto.in.ManholeDeviceListInDTO;
 import com.zksy.manhole.dto.in.ManholeDevicePageInDTO;
 import com.zksy.manhole.dto.in.ManholeDeviceStatisticsInDTO;
 import com.zksy.manhole.dto.out.*;
+import com.zksy.ops.domain.WorkOrder;
+import com.zksy.ops.mapper.WorkOrderMapper;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;

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

@@ -6,7 +6,7 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.zksy.base.domain.HazardHiddenAccount;
 import com.zksy.base.dto.HazardPageInDTO;
 import com.zksy.base.mapper.HazardHiddenAccountMapper;
-import com.zksy.base.service.BusinessWorkOrderService;
+import com.zksy.ops.service.WorkOrderService;
 import com.zksy.base.service.HazardHiddenAccountService;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
@@ -35,7 +35,7 @@ public class HazardHiddenAccountServiceImpl extends ServiceImpl<HazardHiddenAcco
     private ISysDictDataService dictDataService;
 
     @Autowired
-    private BusinessWorkOrderService businessWorkOrderService;
+    private WorkOrderService businessWorkOrderService;
 
     @Override
     public Page<HazardHiddenAccount> findByPage(HazardPageInDTO dto) {

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

@@ -8,14 +8,14 @@ import com.zksy.base.domain.EquipmentBase;
 import com.zksy.base.domain.EquipmentStatus;
 import com.zksy.base.domain.EquipmentType;
 import com.zksy.base.domain.MaintenanceDataStat;
-import com.zksy.base.domain.WorkOrder;
+import com.zksy.ops.domain.WorkOrder;
 import com.zksy.base.manhole.domain.ManholeData;
 import com.zksy.base.manhole.mapper.ManholeDataMapper;
 import com.zksy.base.mapper.EquipmentBaseMapper;
 import com.zksy.base.mapper.EquipmentStatusMapper;
 import com.zksy.base.mapper.EquipmentTypeMapper;
 import com.zksy.base.mapper.MaintenanceDataStatMapper;
-import com.zksy.base.mapper.WorkOrderMapper;
+import com.zksy.ops.mapper.WorkOrderMapper;
 import com.zksy.base.service.MaintenanceDataStatService;
 import com.zksy.common.exception.ServiceException;
 import com.zksy.manhole.dto.in.MaintenanceDataStatDashboardInDTO;

+ 7 - 7
pipe-network-service/zksy-system/src/main/java/com/zksy/base/service/impl/WaterSupplyDeviceServiceImpl.java

@@ -14,11 +14,11 @@ import com.zksy.base.domain.EquipmentBase;
 import com.zksy.base.domain.EquipmentMaintain;
 import com.zksy.base.domain.EquipmentStatus;
 import com.zksy.base.domain.EquipmentType;
-import com.zksy.base.domain.WorkOrder;
+import com.zksy.ops.domain.WorkOrder;
 import com.zksy.base.domain.vo.*;
 import com.zksy.base.event.DeviceStatusChangePublisher;
 import com.zksy.base.environment.domain.ERealTimeData;
-import com.zksy.base.enums.WorkOrderStatusEnum;
+import com.zksy.ops.enums.WorkOrderStatusEnum;
 import com.zksy.ops.dto.WorkOrderDispatchDTO;
 import com.zksy.base.environment.mapper.ERealTimeDataMapper;
 import com.zksy.base.gas.domain.GasMonitorData;
@@ -27,7 +27,7 @@ import com.zksy.base.mapper.EquipmentBaseMapper;
 import com.zksy.base.mapper.EquipmentMaintainMapper;
 import com.zksy.base.mapper.EquipmentStatusMapper;
 import com.zksy.base.mapper.EquipmentTypeMapper;
-import com.zksy.base.mapper.WorkOrderMapper;
+import com.zksy.ops.mapper.WorkOrderMapper;
 import com.zksy.base.pressure.domain.FirefightingPressure;
 import com.zksy.base.pressure.mapper.FirefightingPressureMapper;
 import com.zksy.base.radar.domain.RadarData;
@@ -41,6 +41,7 @@ import com.zksy.base.water.mapper.WaterMonitorDataMapper;
 import com.zksy.common.sms.SmsTemplate;
 import com.zksy.common.core.domain.entity.SysUser;
 import com.zksy.common.utils.SecurityUtils;
+import com.zksy.ops.service.WorkOrderService;
 import com.zksy.system.service.ISysUserService;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -101,7 +102,7 @@ public class WaterSupplyDeviceServiceImpl extends ServiceImpl<EquipmentBaseMappe
     private WorkOrderMapper workOrderMapper;
 
     @Autowired
-    private com.zksy.base.service.BusinessWorkOrderService businessWorkOrderService;
+    private WorkOrderService businessWorkOrderService;
 
     @Autowired
     private ISysUserService sysUserService;
@@ -454,10 +455,9 @@ public class WaterSupplyDeviceServiceImpl extends ServiceImpl<EquipmentBaseMappe
             return true;
         }
         WorkOrderDispatchDTO dispatchDTO = new WorkOrderDispatchDTO();
-        dispatchDTO.setOrderId(workOrder.getOrderId());
         dispatchDTO.setHandlerId(vo.getReceiveUserId());
         dispatchDTO.setRemarks(vo.getMaintainRemark());
-        return businessWorkOrderService.dispatch(dispatchDTO);
+        return businessWorkOrderService.dispatch(workOrder.getOrderId(), dispatchDTO);
     }
 
     private Long currentUserIdOrNull() {
@@ -494,7 +494,7 @@ public class WaterSupplyDeviceServiceImpl extends ServiceImpl<EquipmentBaseMappe
         workOrder.setDeviceCode(device.getEquipmentCode());
         workOrder.setOrderLevel(vo.getOrderLevel() == null ? 1 : vo.getOrderLevel());
         workOrder.setOrderDesc(StrUtil.isNotBlank(vo.getFaultDescription()) ? vo.getFaultDescription() : vo.getMaintainContent());
-        Long orderId = businessWorkOrderService.addWorkOrder(workOrder);
+        Long orderId = businessWorkOrderService.createWorkOrder(workOrder);
         workOrder.setOrderId(orderId);
         return workOrder;
     }

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

@@ -1,24 +0,0 @@
-package com.zksy.base.service.impl;
-
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import com.zksy.base.domain.WorkOrderLog;
-import com.zksy.base.mapper.WorkOrderLogMapper;
-import com.zksy.base.service.WorkOrderLogService;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.stereotype.Service;
-
-/**
-* @author Administrator
-* @description 针对表【work_order_log(工单操作日志表)】的数据库操作Service实现
-* @createDate 2026-06-11 11:51:09
-*/
-@Slf4j
-@Service
-public class WorkOrderLogServiceImpl extends ServiceImpl<WorkOrderLogMapper, WorkOrderLog>
-    implements WorkOrderLogService{
-
-}
-
-
-
-

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

@@ -1,405 +0,0 @@
-package com.zksy.base.service.impl;
-
-import cn.hutool.core.bean.BeanUtil;
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import com.zksy.base.domain.CaseInfo;
-import com.zksy.base.domain.EquipmentBase;
-import com.zksy.base.domain.WorkOrder;
-import com.zksy.base.domain.WorkOrderLog;
-import com.zksy.base.enums.WorkOrderDelayApplyStatusEnum;
-import com.zksy.base.enums.WorkOrderLogOperTypeEnum;
-import com.zksy.base.enums.WorkOrderStatusEnum;
-import com.zksy.base.warning.domain.EarlyWarning;
-import com.zksy.base.warning.service.EarlyWarningService;
-import com.zksy.base.mapper.CaseInfoMapper;
-import com.zksy.base.mapper.EquipmentBaseMapper;
-import com.zksy.base.mapper.WorkOrderLogMapper;
-import com.zksy.base.mapper.WorkOrderMapper;
-import com.zksy.base.service.WorkOrderService;
-import com.zksy.common.core.domain.entity.SysUser;
-import com.zksy.common.core.domain.model.LoginUser;
-import com.zksy.common.exception.ServiceException;
-import com.zksy.common.utils.SecurityUtils;
-import com.zksy.manhole.dto.in.*;
-import com.zksy.manhole.dto.out.WorkOrderOutDTO;
-import com.zksy.system.service.ISysUserService;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.lang3.StringUtils;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-
-import java.time.LocalDateTime;
-import java.time.format.DateTimeFormatter;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Date;
-import java.util.List;
-import java.util.Random;
-import java.util.stream.Collectors;
-
-/**
-* @author Administrator
-* @description 针对表【work_order(运维工单表)】的数据库操作Service实现
-* @createDate 2026-06-11 11:50:08
-*/
-@Slf4j
-@Service
-public class WorkOrderServiceImpl extends ServiceImpl<WorkOrderMapper, WorkOrder>
-    implements WorkOrderService{
-
-    @Autowired
-    private ISysUserService sysUserService;
-
-    @Autowired
-    private WorkOrderLogMapper workOrderLogMapper;
-
-    @Autowired
-    private CaseInfoMapper caseInfoMapper;
-
-    @Autowired
-    private EquipmentBaseMapper equipmentBaseMapper;
-
-    @Autowired
-    private EarlyWarningService earlyWarningService;
-
-    /**
-     * 查询窨井类型设备ID列表(equipment_type_id: 9=排水窨井, 10=供水窨井)
-     */
-    private List<String> getManholeDeviceIds() {
-        LambdaQueryWrapper<EquipmentBase> equipmentQuery = new LambdaQueryWrapper<>();
-        equipmentQuery.in(EquipmentBase::getEquipmentTypeId, Arrays.asList("5", "9", "10"));
-        return equipmentBaseMapper.selectList(equipmentQuery).stream()
-                .map(EquipmentBase::getEquipmentId)
-                .collect(Collectors.toList());
-    }
-
-    /**
-     * 构造空分页结果
-     */
-    private Page<WorkOrderOutDTO> emptyPage(long pageNum, long pageSize) {
-        Page<WorkOrderOutDTO> emptyPage = new Page<>(pageNum, pageSize, 0);
-        emptyPage.setRecords(new ArrayList<>());
-        return emptyPage;
-    }
-
-    /**
-     * 分页查询工单信息列表(仅窨井类型设备)
-     * @param pageInDTO
-     * @return
-     */
-    @Override
-    public Page<WorkOrderOutDTO> findByPage(WorkOrderPageInDTO pageInDTO) {
-        log.info("分页查询工单信息列表-入参:{}", pageInDTO);
-        List<String> manholeDeviceIds = getManholeDeviceIds();
-        if (manholeDeviceIds.isEmpty()) {
-            return emptyPage(pageInDTO.getPageNum(), pageInDTO.getPageSize());
-        }
-        Page<WorkOrder> page = new Page<>(pageInDTO.getPageNum(), pageInDTO.getPageSize());
-        LambdaQueryWrapper<WorkOrder> queryWrapper = new LambdaQueryWrapper<>();
-        queryWrapper.in(WorkOrder::getDeviceId, manholeDeviceIds)
-                .eq(StringUtils.isNotEmpty(pageInDTO.getDeviceId()), WorkOrder::getDeviceId, pageInDTO.getDeviceId())
-                .like(StringUtils.isNotEmpty(pageInDTO.getOrderNo()), WorkOrder::getOrderNo, pageInDTO.getOrderNo())
-                .like(StringUtils.isNotEmpty(pageInDTO.getCaseNo()), WorkOrder::getCaseNo, pageInDTO.getCaseNo())
-                .orderByDesc(WorkOrder::getUpdateTime);
-        Page<WorkOrder> rs = this.page(page, queryWrapper);
-        List<WorkOrderOutDTO> outDTOList = BeanUtil.copyToList(rs.getRecords(), WorkOrderOutDTO.class);
-        Page<WorkOrderOutDTO> outPage = new Page<>(pageInDTO.getPageNum(), pageInDTO.getPageSize(), rs.getTotal());
-        outPage.setRecords(outDTOList);
-        return outPage;
-    }
-
-    /**
-     * 新增工单
-     * @param inDTO
-     * @return 工单ID(新增成功返回 orderId,失败返回 null)
-     */
-    @Override
-    public Long addWorkOrder(WorkOrderAddInDTO inDTO) {
-        log.info("新增工单-入参:{}", inDTO);
-        resolveWarningDevice(inDTO);
-        // 同一设备存在未办结工单(非 已结案/已取消)时禁止重复建单
-        if (inDTO.getDeviceId() != null) {
-            Long count = this.lambdaQuery()
-                    .eq(WorkOrder::getDeviceId, inDTO.getDeviceId())
-                    .notIn(WorkOrder::getOrderStatus, WorkOrderStatusEnum.finishedCodes())
-                    .count();
-            if (count > 0) {
-                throw new ServiceException("该设备已有处理中的工单,需结案或取消后才能创建新工单");
-            }
-        }
-        WorkOrder workOrder = BeanUtil.copyProperties(inDTO, WorkOrder.class);
-        workOrder.setOrderNo(generateOrderNo());
-        // 初始状态:待派单
-        workOrder.setOrderStatus(WorkOrderStatusEnum.PENDING_DISPATCH.getCode());
-        workOrder.setDelayApplyStatus(WorkOrderDelayApplyStatusEnum.NONE.getCode());
-        if (workOrder.getFaultTime() == null) {
-            workOrder.setFaultTime(new Date());
-        }
-        workOrder.setCreateTime(new Date());
-        workOrder.setUpdateTime(new Date());
-        if(inDTO.getCaseId() != null){
-            CaseInfo caseInfo = caseInfoMapper.selectById(inDTO.getCaseId());
-            if(caseInfo != null){
-                workOrder.setCaseNo(caseInfo.getCaseNo());
-            }
-        }
-        if(inDTO.getDeviceId() != null){
-            EquipmentBase equipmentBase = equipmentBaseMapper.selectById(inDTO.getDeviceId());
-            if(equipmentBase != null){
-                workOrder.setDeviceCode(equipmentBase.getEquipmentCode());
-            }
-        }
-        // 设置维修前后图片(如果 DTO 中有传递)
-        if (inDTO.getMaintenanceBeforeImg() != null) {
-            workOrder.setMaintenanceBeforeImg(inDTO.getMaintenanceBeforeImg());
-        }
-        if (inDTO.getMaintenanceAfterImg() != null) {
-            workOrder.setMaintenanceAfterImg(inDTO.getMaintenanceAfterImg());
-        }
-        this.save(workOrder);
-        WorkOrderLog workOrderLog = new WorkOrderLog();
-        workOrderLog.setOrderId(workOrder.getOrderId());
-        // 创建工单
-        workOrderLog.setOperType(WorkOrderLogOperTypeEnum.CREATE.getCode());
-        workOrderLog.setOperDesc(inDTO.getOrderDesc() + ",创建工单");
-        LoginUser loginUser = SecurityUtils.getLoginUser();
-        if (loginUser != null) {
-            workOrderLog.setOperUserId(loginUser.getUserId());
-            workOrderLog.setOperUserName(loginUser.getUser().getNickName());
-        }
-        workOrderLogMapper.insert(workOrderLog);
-
-        // 返回工单ID,供后续派单使用
-        return workOrder.getOrderId();
-    }
-
-    /**
-     * 确认预警时前端不再重复选择设备,按预警已保存的设备编码回填工单设备信息。
-     */
-    private void resolveWarningDevice(WorkOrderAddInDTO inDTO) {
-        if (inDTO == null || StringUtils.isBlank(inDTO.getAlarmId())) {
-            return;
-        }
-        EarlyWarning warning = earlyWarningService.getById(inDTO.getAlarmId());
-        if (warning == null) {
-            return;
-        }
-        if (StringUtils.isBlank(inDTO.getDeviceCode())) {
-            inDTO.setDeviceCode(warning.getDeviceCode());
-        }
-        if (StringUtils.isNotBlank(inDTO.getDeviceId()) || StringUtils.isBlank(inDTO.getDeviceCode())) {
-            return;
-        }
-        EquipmentBase equipment = equipmentBaseMapper.selectOne(new LambdaQueryWrapper<EquipmentBase>()
-                .eq(EquipmentBase::getEquipmentCode, inDTO.getDeviceCode())
-                .last("LIMIT 1"));
-        if (equipment != null) {
-            inDTO.setDeviceId(equipment.getEquipmentId());
-        }
-    }
-
-    /**
-     * 生成工单编号
-     * @return
-     */
-    public static String generateOrderNo() {
-        // 1. 获取当前时间,格式化为14位年月日时分秒
-        String currentDateTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
-        // 2. 生成4位随机数,不足补0
-        String randomNum = String.format("%04d", new Random().nextInt(10000));
-        // 3. 拼接完整编号
-        return "WO" + currentDateTime + randomNum;
-    }
-
-    /**
-     * 经办工单分页查询(处理中:待接单、已接单、处理中、待验收,仅窨井类型设备)
-     * @param inDTO
-     * @return
-     */
-    @Override
-    public Page<WorkOrderOutDTO> findProcessingPage(WorkOrderProcessingInDTO inDTO) {
-        log.info("经办工单分页查询-入参:{}", inDTO);
-        List<String> manholeDeviceIds = getManholeDeviceIds();
-        if (manholeDeviceIds.isEmpty()) {
-            return emptyPage(inDTO.getPageNum(), inDTO.getPageSize());
-        }
-        Page<WorkOrder> page = new Page<>(inDTO.getPageNum(), inDTO.getPageSize());
-        // 经办工单状态:待接单/已接单/处理中/待验收/延期中
-        List<Integer> processingStatusList = WorkOrderStatusEnum.activeCodes();
-        LambdaQueryWrapper<WorkOrder> queryWrapper = new LambdaQueryWrapper<>();
-        queryWrapper.in(WorkOrder::getDeviceId, manholeDeviceIds)
-                .in(WorkOrder::getOrderStatus, processingStatusList)
-                .like(StringUtils.isNotEmpty(inDTO.getOrderNo()), WorkOrder::getOrderNo, inDTO.getOrderNo())
-                .like(StringUtils.isNotEmpty(inDTO.getCaseNo()), WorkOrder::getCaseNo, inDTO.getCaseNo())
-                .eq(inDTO.getOrderLevel() != null, WorkOrder::getOrderLevel, inDTO.getOrderLevel())
-                .eq(inDTO.getReceiveUser() != null, WorkOrder::getReceiveUser,
-                        inDTO.getReceiveUser() == null ? null : Long.valueOf(inDTO.getReceiveUser().toString()))
-                .orderByDesc(WorkOrder::getUpdateTime);
-        Page<WorkOrder> rs = this.page(page, queryWrapper);
-        List<WorkOrderOutDTO> outDTOList = BeanUtil.copyToList(rs.getRecords(), WorkOrderOutDTO.class);
-        Page<WorkOrderOutDTO> outPage = new Page<>(inDTO.getPageNum(), inDTO.getPageSize(), rs.getTotal());
-        outPage.setRecords(outDTOList);
-        return outPage;
-    }
-
-    /**
-     * 办结工单分页查询(已办结:已结案、已驳回,仅窨井类型设备)
-     * @param inDTO
-     * @return
-     */
-    @Override
-    public Page<WorkOrderOutDTO> findFinishedPage(WorkOrderFinishedInDTO inDTO) {
-        log.info("办结工单分页查询-入参:{}", inDTO);
-        List<String> manholeDeviceIds = getManholeDeviceIds();
-        if (manholeDeviceIds.isEmpty()) {
-            return emptyPage(inDTO.getPageNum(), inDTO.getPageSize());
-        }
-        Page<WorkOrder> page = new Page<>(inDTO.getPageNum(), inDTO.getPageSize());
-        // 办结工单状态:已结案、已取消
-        List<Integer> finishedStatusList = WorkOrderStatusEnum.finishedCodes();
-        LambdaQueryWrapper<WorkOrder> queryWrapper = new LambdaQueryWrapper<>();
-        queryWrapper.in(WorkOrder::getDeviceId, manholeDeviceIds)
-                .in(WorkOrder::getOrderStatus, finishedStatusList)
-                .like(StringUtils.isNotEmpty(inDTO.getOrderNo()), WorkOrder::getOrderNo, inDTO.getOrderNo())
-                .like(StringUtils.isNotEmpty(inDTO.getCaseNo()), WorkOrder::getCaseNo, inDTO.getCaseNo())
-                .eq(inDTO.getOrderLevel() != null, WorkOrder::getOrderLevel, inDTO.getOrderLevel())
-                .eq(inDTO.getVerifyUser() != null, WorkOrder::getVerifyUser, inDTO.getVerifyUser())
-                .ge(inDTO.getStartTime() != null, WorkOrder::getRealFinishTime, inDTO.getStartTime())
-                .le(inDTO.getEndTime() != null, WorkOrder::getRealFinishTime, inDTO.getEndTime())
-                .orderByDesc(WorkOrder::getUpdateTime);
-        Page<WorkOrder> rs = this.page(page, queryWrapper);
-        List<WorkOrderOutDTO> outDTOList = BeanUtil.copyToList(rs.getRecords(), WorkOrderOutDTO.class);
-        Page<WorkOrderOutDTO> outPage = new Page<>(inDTO.getPageNum(), inDTO.getPageSize(), rs.getTotal());
-        outPage.setRecords(outDTOList);
-        return outPage;
-    }
-
-    /**
-     * 操作工单
-     * @param inDTO
-     * @return
-     */
-    @Override
-    public boolean operWorkOrder(WorkOrderOperInDTO inDTO) {
-        log.info("操作工单-入参:{}", inDTO);
-        WorkOrder workOrder = this.getById(inDTO.getOrderId());
-        if(workOrder == null){
-            throw new ServiceException("工单ID不存在");
-        }
-        SysUser user = sysUserService.selectUserById(inDTO.getOperUserId());
-        if(user == null){
-            throw new ServiceException("操作人ID不存在");
-        }
-        if(inDTO.getOperType() == null){
-            throw new ServiceException("操作类型不能为空");
-        }
-        WorkOrder entity = new WorkOrder();
-        entity.setOrderId(inDTO.getOrderId());
-        WorkOrderLogOperTypeEnum operTypeEnum = WorkOrderLogOperTypeEnum.getByCode(inDTO.getOperType());
-        if (operTypeEnum == null || operTypeEnum == WorkOrderLogOperTypeEnum.CREATE) {
-            throw new ServiceException("操作类型不合法");
-        }
-        if (operTypeEnum == WorkOrderLogOperTypeEnum.DISPATCH){
-            // 派单:待派单 → 待接单
-            entity.setOrderStatus(WorkOrderStatusEnum.PENDING_ACCEPT.getCode());
-            entity.setDispatchUser(inDTO.getOperUserId());
-            entity.setDispatchTime(new Date());
-            entity.setReceiveUser(longValue(inDTO.getReceiveUser()));
-        } else if(operTypeEnum == WorkOrderLogOperTypeEnum.ACCEPT){
-            // 接单:待接单 → 已接单
-            entity.setOrderStatus(WorkOrderStatusEnum.ACCEPTED.getCode());
-            entity.setReceiveUser(inDTO.getOperUserId());
-        } else if(operTypeEnum == WorkOrderLogOperTypeEnum.PROCESS){
-            // 处置:→ 处理中
-            entity.setOrderStatus(WorkOrderStatusEnum.PROCESSING.getCode());
-            if (StringUtils.isNotBlank(inDTO.getMaintenanceBeforeImg())) {
-                entity.setMaintenanceBeforeImg(inDTO.getMaintenanceBeforeImg());
-            }
-        } else if(operTypeEnum == WorkOrderLogOperTypeEnum.SUBMIT_VERIFY){
-            // 提交验收:→ 待验收
-            entity.setOrderStatus(WorkOrderStatusEnum.PENDING_VERIFY.getCode());
-            entity.setVerifyUser(workOrder.getDispatchUser());
-            if (StringUtils.isNotBlank(inDTO.getMaintenanceAfterImg())) {
-                entity.setMaintenanceAfterImg(inDTO.getMaintenanceAfterImg());
-            }
-        } else if(operTypeEnum == WorkOrderLogOperTypeEnum.VERIFY_PASS){
-            // 验收通过:→ 已结案
-            entity.setOrderStatus(WorkOrderStatusEnum.CLOSED.getCode());
-            entity.setRealFinishTime(new Date());
-            entity.setVerifyUser(inDTO.getOperUserId());
-            entity.setVerifyTime(new Date());
-            // 同步将案件状态改为已办结
-            if(workOrder.getCaseId() != null){
-                CaseInfo caseInfo = caseInfoMapper.selectById(workOrder.getCaseId());
-                if(caseInfo != null){
-                    CaseInfo caseEntity = new CaseInfo();
-                    caseEntity.setCaseId(caseInfo.getCaseId());
-                    caseEntity.setCaseStatus(3);
-                    caseEntity.setFinishTime(new Date());
-                    caseInfoMapper.updateById(caseEntity);
-                }
-            }
-            // 若工单关联了预警(alarmId 对应 early_warning 记录),验收通过时自动解除预警
-            if(workOrder.getAlarmId() != null){
-                try {
-                    EarlyWarning warning = earlyWarningService.getById(workOrder.getAlarmId());
-                    if(warning != null){
-                        earlyWarningService.resolveWarning(warning.getWarningId(),
-                                "工单验收通过(" + workOrder.getOrderNo() + "),预警已解除");
-                        log.info("工单验收通过,自动解除预警: orderNo={}, warningId={}",
-                                workOrder.getOrderNo(), workOrder.getAlarmId());
-                    }
-                } catch (Exception e) {
-                    log.warn("工单验收通过后解除预警失败: orderNo={}, warningId={}",
-                            workOrder.getOrderNo(), workOrder.getAlarmId(), e);
-                }
-            }
-        } else if(operTypeEnum == WorkOrderLogOperTypeEnum.VERIFY_REJECT){
-            // 验收驳回:→ 退回处理中
-            entity.setOrderStatus(WorkOrderStatusEnum.PROCESSING.getCode());
-            entity.setVerifyUser(inDTO.getOperUserId());
-            entity.setVerifyTime(new Date());
-        } else if(operTypeEnum == WorkOrderLogOperTypeEnum.CANCEL){
-            // 取消工单:→ 已取消
-            entity.setOrderStatus(WorkOrderStatusEnum.CANCELLED.getCode());
-        } else if(operTypeEnum == WorkOrderLogOperTypeEnum.DELAY_APPLY){
-            // 申请延期:延期申请状态 → 待延期审核
-            entity.setDelayApplyStatus(WorkOrderDelayApplyStatusEnum.PENDING_AUDIT.getCode());
-            entity.setDelayedReason(inDTO.getDelayedReason());
-            entity.setDelayedDeadline(inDTO.getDelayedDeadline());
-        } else if(operTypeEnum == WorkOrderLogOperTypeEnum.DELAY_APPROVE){
-            // 延期审核通过:→ 延期中
-            entity.setDelayApplyStatus(WorkOrderDelayApplyStatusEnum.APPROVED.getCode());
-            entity.setOrderStatus(WorkOrderStatusEnum.DELAYING.getCode());
-        } else if(operTypeEnum == WorkOrderLogOperTypeEnum.DELAY_REJECT){
-            // 延期审核驳回
-            entity.setDelayApplyStatus(WorkOrderDelayApplyStatusEnum.REJECTED.getCode());
-        }
-        WorkOrderLog workOrderLog = BeanUtil.copyProperties(inDTO, WorkOrderLog.class);
-        workOrderLog.setAttachUrl(inDTO.getMaintenanceBeforeImg() != null
-                ? inDTO.getMaintenanceBeforeImg() : inDTO.getMaintenanceAfterImg());
-        workOrderLog.setOperUserName(user.getNickName());
-        workOrderLogMapper.insert(workOrderLog);
-        return this.updateById(entity);
-    }
-
-    /**
-     * 责任人入参兼容 Long / String,统一转换为 Long
-     */
-    private Long longValue(Object value) {
-        if (value == null) {
-            return null;
-        }
-        if (value instanceof Number) {
-            return ((Number) value).longValue();
-        }
-        String text = value.toString().trim();
-        return text.isEmpty() ? null : Long.valueOf(text);
-    }
-}
-
-
-
-

+ 6 - 6
pipe-network-service/zksy-system/src/main/java/com/zksy/cockpit/workorder/BusinessCockpitWorkOrderReadAdapter.java

@@ -1,9 +1,9 @@
 package com.zksy.cockpit.workorder;
 
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.zksy.base.domain.WorkOrder;
-import com.zksy.base.domain.WorkOrderLog;
-import com.zksy.base.service.BusinessWorkOrderService;
+import com.zksy.ops.domain.WorkOrder;
+import com.zksy.ops.domain.WorkOrderLog;
+import com.zksy.ops.service.WorkOrderService;
 import com.zksy.base.warning.domain.AlarmWarningRel;
 import com.zksy.base.warning.domain.EarlyWarning;
 import com.zksy.base.warning.mapper.AlarmWarningRelMapper;
@@ -24,12 +24,12 @@ import java.util.stream.Collectors;
 public class BusinessCockpitWorkOrderReadAdapter implements CockpitWorkOrderReadAdapter {
     private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ISO_OFFSET_DATE_TIME;
 
-    private final BusinessWorkOrderService workOrderService;
+    private final WorkOrderService workOrderService;
     private final EarlyWarningService earlyWarningService;
     private final AlarmWarningRelMapper alarmWarningRelMapper;
 
     public BusinessCockpitWorkOrderReadAdapter(
-            BusinessWorkOrderService workOrderService,
+            WorkOrderService workOrderService,
             EarlyWarningService earlyWarningService,
             AlarmWarningRelMapper alarmWarningRelMapper) {
         this.workOrderService = workOrderService;
@@ -96,7 +96,7 @@ public class BusinessCockpitWorkOrderReadAdapter implements CockpitWorkOrderRead
         if (order == null) {
             return null;
         }
-        List<WorkOrderLog> logs = workOrderService.getWorkOrderLogs(order.getOrderId());
+        List<WorkOrderLog> logs = workOrderService.listLogs(order.getOrderId());
         List<CockpitWorkOrderTimelineEntry> entries = logs.stream()
                 .map(log -> toTimelineEntry(order, log))
                 .collect(Collectors.toList());

+ 0 - 54
pipe-network-service/zksy-system/src/main/java/com/zksy/manhole/dto/in/WorkOrderAddInDTO.java

@@ -1,54 +0,0 @@
-package com.zksy.manhole.dto.in;
-
-import com.fasterxml.jackson.annotation.JsonFormat;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-
-import java.io.Serializable;
-import java.util.Date;
-
-/**
- * 新增工单-入参
- *
- * @author xianggx
- */
-@Data
-@ApiModel(value = "新增工单-入参", description = "新增工单-入参")
-public class WorkOrderAddInDTO implements Serializable {
-    private static final long serialVersionUID = 1L;
-
-    @ApiModelProperty(value = "报警ID")
-    private String alarmId;
-
-    @ApiModelProperty(value = "案件ID")
-    private Long caseId;
-
-    @ApiModelProperty(value = "设备ID", required = false)
-    private String deviceId;
-
-    @ApiModelProperty(value = "设备编码")
-    private String deviceCode;
-
-    @ApiModelProperty(value = "工单类型:1-故障维修 2-日常巡检 3-设备保养", required = true)
-    private Integer orderType;
-
-    @ApiModelProperty(value = "工单优先级:1-紧急 2-一般 3-低", required = true)
-    private Integer orderLevel;
-
-    @ApiModelProperty(value = "工单问题描述", required = true)
-    private String orderDesc;
-
-    @ApiModelProperty(value = "负责部门ID")
-    private Long deptId;
-
-    @ApiModelProperty(value = "计划完成时间")
-    @JsonFormat(locale = "zh", pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
-    private Date planFinishTime;
-
-    @ApiModelProperty(value = "维修前图片 URL")
-    private String maintenanceBeforeImg;
-
-    @ApiModelProperty(value = "维修后图片 URL")
-    private String maintenanceAfterImg;
-}

+ 0 - 49
pipe-network-service/zksy-system/src/main/java/com/zksy/manhole/dto/in/WorkOrderFinishedInDTO.java

@@ -1,49 +0,0 @@
-package com.zksy.manhole.dto.in;
-
-import com.fasterxml.jackson.annotation.JsonFormat;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-
-import java.io.Serializable;
-import java.util.Date;
-
-/**
- * 办结工单(已办结)分页查询-入参
- *
- * @author xianggx
- */
-@Data
-@ApiModel(value = "办结工单-入参", description = "办结工单分页查询-入参")
-public class WorkOrderFinishedInDTO implements Serializable {
-    private static final long serialVersionUID = 1L;
-
-    @ApiModelProperty(value = "工单编号")
-    private String orderNo;
-
-    @ApiModelProperty(value = "案件编号")
-    private String caseNo;
-
-    @ApiModelProperty(value = "工单类型:1-故障维修 2-日常巡检 3-设备保养")
-    private Integer orderType;
-
-    @ApiModelProperty(value = "工单优先级:1-紧急 2-一般 3-低")
-    private Integer orderLevel;
-
-    @ApiModelProperty(value = "验收人ID")
-    private Long verifyUser;
-
-    @ApiModelProperty(value = "办结开始时间")
-    @JsonFormat(locale = "zh", pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
-    private Date startTime;
-
-    @ApiModelProperty(value = "办结结束时间")
-    @JsonFormat(locale = "zh", pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
-    private Date endTime;
-
-    @ApiModelProperty(value = "当前记录起始索引", required = true)
-    private Long pageNum;
-
-    @ApiModelProperty(value = "每页显示记录数", required = true)
-    private Long pageSize;
-}

+ 0 - 46
pipe-network-service/zksy-system/src/main/java/com/zksy/manhole/dto/in/WorkOrderOperInDTO.java

@@ -1,46 +0,0 @@
-package com.zksy.manhole.dto.in;
-
-import com.fasterxml.jackson.annotation.JsonFormat;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-
-import java.io.Serializable;
-import java.util.Date;
-
-/**
- * 操作工单-入参
- *
- * @author xianggx
- */
-@Data
-@ApiModel(value = "操作工单-入参", description = "操作工单-入参")
-public class WorkOrderOperInDTO implements Serializable {
-    private static final long serialVersionUID = 1L;
-
-    @ApiModelProperty(value = "工单ID", required = true)
-    private Long orderId;
-
-    @ApiModelProperty(value = "操作类型:2-派单 3-接单 4-开始维修 5-提交验收 6-验收通过 7-驳回工单 8-申请延期 9-延期审核通过", required = true)
-    private Integer operType;
-
-    @ApiModelProperty(value = "操作详情描述", required = true)
-    private String operDesc;
-
-    @ApiModelProperty(value = "操作人ID", required = true)
-    private Long operUserId;
-
-    @ApiModelProperty(value = "接单运维人员ID(操作类型为 2-派单 时,必填)")
-    private Long receiveUser;
-
-    @ApiModelProperty(value = "延期原因(操作类型为 8-申请延期 时,必填)")
-    private String delayedReason;
-
-    @ApiModelProperty(value = "延期截止时间(操作类型为 8-申请延期 时,必填)")
-    @JsonFormat(locale = "zh",pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
-    private Date delayedDeadline;
-
-    private String maintenanceBeforeImg;
-
-    private String maintenanceAfterImg;
-}

+ 0 - 33
pipe-network-service/zksy-system/src/main/java/com/zksy/manhole/dto/in/WorkOrderPageInDTO.java

@@ -1,33 +0,0 @@
-package com.zksy.manhole.dto.in;
-
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-
-import java.io.Serializable;
-
-/**
- * 分页查询工单信息列表-入参
- *
- * @author xianggx
- */
-@Data
-@ApiModel(value = "分页查询工单信息列表-入参", description = "分页查询工单信息列表-入参")
-public class WorkOrderPageInDTO implements Serializable {
-    private static final long serialVersionUID = 1L;
-
-    @ApiModelProperty(value = "工单编号")
-    private String orderNo;
-
-    @ApiModelProperty(value = "案件编号")
-    private String caseNo;
-
-    @ApiModelProperty(value = "设备ID")
-    private String deviceId;
-
-    @ApiModelProperty(value = "当前记录起始索引", required = true)
-    private Long pageNum;
-
-    @ApiModelProperty(value = "每页显示记录数", required = true)
-    private Long pageSize;
-}

+ 0 - 39
pipe-network-service/zksy-system/src/main/java/com/zksy/manhole/dto/in/WorkOrderProcessingInDTO.java

@@ -1,39 +0,0 @@
-package com.zksy.manhole.dto.in;
-
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-
-import java.io.Serializable;
-
-/**
- * 经办工单(处理中)分页查询-入参
- *
- * @author xianggx
- */
-@Data
-@ApiModel(value = "经办工单-入参", description = "经办工单分页查询-入参")
-public class WorkOrderProcessingInDTO implements Serializable {
-    private static final long serialVersionUID = 1L;
-
-    @ApiModelProperty(value = "工单编号")
-    private String orderNo;
-
-    @ApiModelProperty(value = "案件编号")
-    private String caseNo;
-
-    @ApiModelProperty(value = "工单类型:1-故障维修 2-日常巡检 3-设备保养")
-    private Integer orderType;
-
-    @ApiModelProperty(value = "工单优先级:1-紧急 2-一般 3-低")
-    private Integer orderLevel;
-
-    @ApiModelProperty(value = "接单运维人员ID")
-    private Long receiveUser;
-
-    @ApiModelProperty(value = "当前记录起始索引", required = true)
-    private Long pageNum;
-
-    @ApiModelProperty(value = "每页显示记录数", required = true)
-    private Long pageSize;
-}

+ 0 - 19
pipe-network-service/zksy-system/src/main/java/com/zksy/manhole/dto/out/WorkOrderLogOutDTO.java

@@ -1,19 +0,0 @@
-package com.zksy.manhole.dto.out;
-
-import com.zksy.base.domain.WorkOrderLog;
-import io.swagger.annotations.ApiModel;
-import lombok.Data;
-
-import java.io.Serializable;
-
-/**
- * 工单操作日志-出参
- *
- * @author xianggx
- */
-@Data
-@ApiModel(value = "工单操作日志-出参", description = "工单操作日志-出参")
-public class WorkOrderLogOutDTO extends WorkOrderLog implements Serializable {
-    private static final long serialVersionUID = 1L;
-
-}

+ 0 - 29
pipe-network-service/zksy-system/src/main/java/com/zksy/manhole/dto/out/WorkOrderOutDTO.java

@@ -1,29 +0,0 @@
-package com.zksy.manhole.dto.out;
-
-import com.zksy.base.domain.WorkOrder;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-
-import java.io.Serializable;
-import java.util.List;
-
-/**
- * 获取工单信息详情-出参
- *
- * @author xianggx
- */
-@Data
-@ApiModel(value = "获取工单信息详情-出参", description = "获取工单信息详情-出参")
-public class WorkOrderOutDTO extends WorkOrder implements Serializable {
-    private static final long serialVersionUID = 1L;
-
-    @ApiModelProperty("设备类别名称")
-    private String equipmentTypeName;
-
-    @ApiModelProperty(value = "设备状态详情")
-    private EquipmentStatusOutDTO equipmentStatus;
-
-    @ApiModelProperty(value = "工单操作日志列表")
-    private List<WorkOrderLogOutDTO> workOrderLogList;
-}

+ 2 - 2
pipe-network-service/zksy-system/src/main/java/com/zksy/base/domain/WorkOrder.java → pipe-network-service/zksy-system/src/main/java/com/zksy/ops/domain/WorkOrder.java

@@ -1,4 +1,4 @@
-package com.zksy.base.domain;
+package com.zksy.ops.domain;
 
 import com.baomidou.mybatisplus.annotation.IdType;
 import com.baomidou.mybatisplus.annotation.TableField;
@@ -59,7 +59,7 @@ public class WorkOrder implements Serializable {
     private Long receiveUser;
     @ApiModelProperty(value = "工单状态:0-待派单 1-待接单 2-已接单 3-处理中 4-待验收 5-已结案 6-延期中 7-已取消 (见WorkOrderStatusEnum)")
     private Integer orderStatus;
-    @ApiModelProperty(value = "延期申请状态:0-无延期申请 1-待延期审核 2-延期审核通过 3-延期审核驳回 见 WorkOrderDelayApplyStatusEnum)")
+    @ApiModelProperty(value = "延期申请状态:0-无延期申请 1-待延期审核 2-延期审核通过 3-延期审核驳回 (见WorkOrderDelayApplyStatusEnum)")
     private Integer delayApplyStatus;
     @ApiModelProperty(value = "延期原因")
     private String delayedReason;

+ 10 - 50
pipe-network-service/zksy-system/src/main/java/com/zksy/base/domain/WorkOrderLog.java → pipe-network-service/zksy-system/src/main/java/com/zksy/ops/domain/WorkOrderLog.java

@@ -1,4 +1,4 @@
-package com.zksy.base.domain;
+package com.zksy.ops.domain;
 
 import com.baomidou.mybatisplus.annotation.IdType;
 import com.baomidou.mybatisplus.annotation.TableField;
@@ -23,69 +23,29 @@ public class WorkOrderLog implements Serializable {
 
     @TableField(exist = false)
     private static final long serialVersionUID = 1L;
-
-    /**
-     * 日志ID
-     */
     @ApiModelProperty(value = "日志ID")
     @TableId(type = IdType.AUTO)
     private Long logId;
-
-    /**
-     * 关联工单ID
-     */
     @ApiModelProperty(value = "工单ID")
     private Long orderId;
-
-    /**
-     * 操作类型:0-创建工单 1-派单 2-接单 3-处置 4-提交验收 5-验收通过 6-验收驳回 7-取消工单
-     * 8-申请延期 9-延期审核通过 10-延期审核驳回(见 WorkOrderLogOperTypeEnum)
-     */
-    @ApiModelProperty(value = "操作类型:0-创建工单 1-派单 2-接单 3-处置 4-提交验收 5-验收通过 6-验收驳回 7-取消工单 8-申请延期 9-延期审核通过 10-延期审核驳回")
+    @ApiModelProperty(value = "操作类型:0-创建工单 1-派单 2-接单 3-处置 4-提交验收 5-验收通过 6-验收驳回 7-取消工单 8-申请延期 9-延期审核通过 10-延期审核驳回 (见WorkOrderLogOperTypeEnum)")
     private Integer operType;
-
-    /**
-     * 操作类型描述(非数据库字段,查询时回填)
-     */
-    @ApiModelProperty(value = "操作类型描述")
-    @TableField(exist = false)
-    private String operTypeDesc;
-
-    /**
-     * 操作详情描述
-     */
     @ApiModelProperty(value = "操作详情描述")
     private String operDesc;
-
-    /**
-     * 操作附件 URL,多个使用逗号分隔
-     */
     @ApiModelProperty(value = "操作附件 URL,多个使用逗号分隔")
     private String attachUrl;
-
-    /**
-     * 操作附件 URL 列表(非数据库字段,查询时回填,便于前端预览)
-     */
-    @ApiModelProperty(value = "操作附件列表")
-    @TableField(exist = false)
-    private List<String> attachList;
-
-    /**
-     * 操作人ID
-     */
     @ApiModelProperty(value = "操作人ID")
     private Long operUserId;
-
-    /**
-     * 操作人姓名
-     */
     @ApiModelProperty(value = "操作人姓名")
     private String operUserName;
-
-    /**
-     * 操作时间
-     */
     @ApiModelProperty(value = "操作时间")
-    @JsonFormat(locale = "zh",pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
+    @JsonFormat(locale = "zh", pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
     private Date operTime;
+
+    @ApiModelProperty(value = "操作类型描述")
+    @TableField(exist = false)
+    private String operTypeDesc;
+    @ApiModelProperty(value = "操作附件列表")
+    @TableField(exist = false)
+    private List<String> attachList;
 }

+ 1 - 1
pipe-network-service/zksy-system/src/main/java/com/zksy/base/domain/vo/WorkOrderPushVO.java → pipe-network-service/zksy-system/src/main/java/com/zksy/ops/domain/vo/WorkOrderPushVO.java

@@ -1,4 +1,4 @@
-package com.zksy.base.domain.vo;
+package com.zksy.ops.domain.vo;
 
 import com.fasterxml.jackson.annotation.JsonFormat;
 import io.swagger.annotations.ApiModel;

+ 53 - 0
pipe-network-service/zksy-system/src/main/java/com/zksy/ops/dto/WorkOrderAddDTO.java

@@ -0,0 +1,53 @@
+package com.zksy.ops.dto;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.Max;
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotBlank;
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 工单新增入参(初始状态:待派单)
+ *
+ * <p>只暴露创建阶段允许客户端决定的字段。工单编号、工单状态、延期申请状态、
+ * 创建/更新时间等一律由 {@code WorkOrderServiceImpl#createWorkOrder} 生成;
+ * 归属部门默认取创建人所在部门,不接受客户端指定,避免绕过数据权限隔离。</p>
+ *
+ * @author zksy
+ */
+@Data
+@ApiModel(value = "WorkOrderAddDTO", description = "工单新增入参")
+public class WorkOrderAddDTO implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /** 工单问题描述(故障描述) */
+    @NotBlank(message = "故障描述不能为空")
+    @ApiModelProperty(value = "工单问题描述(故障描述)", required = true)
+    private String orderDesc;
+
+    /** 设备ID(equipment_base.equipment_id):后端据此回填设备编码,并校验同设备是否已有进行中工单 */
+    @ApiModelProperty(value = "设备ID,传入后由后端回填设备编码")
+    private String deviceId;
+
+    /** 工单优先级:0-紧急 1-一般 2-低,不传默认 1-一般 */
+    @Min(value = 0, message = "工单优先级取值为 0-紧急、1-一般、2-低")
+    @Max(value = 2, message = "工单优先级取值为 0-紧急、1-一般、2-低")
+    @ApiModelProperty(value = "工单优先级:0-紧急 1-一般 2-低,不传默认 1-一般")
+    private Integer orderLevel;
+
+    /** 故障发生时间,不传取当前时间 */
+    @JsonFormat(locale = "zh", pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    @ApiModelProperty(value = "故障发生时间,yyyy-MM-dd HH:mm:ss,不传取当前时间")
+    private Date faultTime;
+
+    /** 计划完成时间 */
+    @JsonFormat(locale = "zh", pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    @ApiModelProperty(value = "计划完成时间,yyyy-MM-dd HH:mm:ss")
+    private Date planFinishTime;
+}

+ 0 - 5
pipe-network-service/zksy-system/src/main/java/com/zksy/ops/dto/WorkOrderDelayApplyDTO.java

@@ -18,11 +18,6 @@ public class WorkOrderDelayApplyDTO implements Serializable {
 
     private static final long serialVersionUID = 1L;
 
-    /** 工单ID */
-    @NotNull(message = "工单ID不能为空")
-    @ApiModelProperty(value = "工单ID", required = true)
-    private Long orderId;
-
     /** 延期截止时间 */
     @NotNull(message = "延期截止时间不能为空")
     @ApiModelProperty(value = "延期截止时间", required = true)

+ 0 - 5
pipe-network-service/zksy-system/src/main/java/com/zksy/ops/dto/WorkOrderDelayAuditDTO.java

@@ -18,11 +18,6 @@ public class WorkOrderDelayAuditDTO implements Serializable {
 
     private static final long serialVersionUID = 1L;
 
-    /** 工单ID */
-    @NotNull(message = "工单ID不能为空")
-    @ApiModelProperty(value = "工单ID", required = true)
-    private Long orderId;
-
     /** 审核结果:true-通过 false-驳回 */
     @NotNull(message = "审核结果不能为空")
     @ApiModelProperty(value = "审核结果:true-通过 false-驳回", required = true)

+ 2 - 2
pipe-network-service/zksy-system/src/main/java/com/zksy/ops/dto/WorkOrderDetailVO.java

@@ -1,7 +1,7 @@
 package com.zksy.ops.dto;
 
-import com.zksy.base.domain.WorkOrder;
-import com.zksy.base.domain.WorkOrderLog;
+import com.zksy.ops.domain.WorkOrder;
+import com.zksy.ops.domain.WorkOrderLog;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;

+ 0 - 5
pipe-network-service/zksy-system/src/main/java/com/zksy/ops/dto/WorkOrderDispatchDTO.java

@@ -18,11 +18,6 @@ public class WorkOrderDispatchDTO implements Serializable {
 
     private static final long serialVersionUID = 1L;
 
-    /** 工单ID */
-    @NotNull(message = "工单ID不能为空")
-    @ApiModelProperty(value = "工单ID", required = true)
-    private Long orderId;
-
     /** 责任人用户ID */
     @NotNull(message = "责任人不能为空")
     @ApiModelProperty(value = "责任人用户ID", required = true)

+ 0 - 6
pipe-network-service/zksy-system/src/main/java/com/zksy/ops/dto/WorkOrderOperateDTO.java

@@ -4,7 +4,6 @@ import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 
-import javax.validation.constraints.NotNull;
 import java.io.Serializable;
 
 /**
@@ -18,11 +17,6 @@ public class WorkOrderOperateDTO implements Serializable {
 
     private static final long serialVersionUID = 1L;
 
-    /** 工单ID */
-    @NotNull(message = "工单ID不能为空")
-    @ApiModelProperty(value = "工单ID", required = true)
-    private Long orderId;
-
     /** 操作内容:核查结论 / 处置内容 / 验收说明 / 取消原因 */
     @ApiModelProperty(value = "操作内容:核查结论/处置内容/验收说明/取消原因")
     private String content;

+ 4 - 0
pipe-network-service/zksy-system/src/main/java/com/zksy/ops/dto/WorkOrderPageDTO.java

@@ -42,6 +42,10 @@ public class WorkOrderPageDTO extends BaseEntity {
     @ApiModelProperty(value = "延期申请状态:0-无延期申请 1-待延期审核 2-延期审核通过 3-延期审核驳回")
     private Integer delayApplyStatus;
 
+    /** 设备ID(equipment_base.equipment_id) */
+    @ApiModelProperty(value = "设备ID")
+    private String deviceId;
+
     /** 设备编码 */
     @ApiModelProperty(value = "设备编码")
     private String deviceCode;

+ 67 - 0
pipe-network-service/zksy-system/src/main/java/com/zksy/ops/dto/WorkOrderStatusStatVO.java

@@ -0,0 +1,67 @@
+package com.zksy.ops.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * 工单状态统计(列表页状态筛选条)
+ *
+ * <p>统计口径与列表查询保持一致(数据权限、关键字、优先级、故障时间、设备类别等),
+ * 但不叠加状态筛选,因此点击任一状态后其它状态的计数仍然可见。</p>
+ *
+ * @author zksy
+ */
+@Data
+@ApiModel(value = "WorkOrderStatusStatVO", description = "工单状态统计")
+public class WorkOrderStatusStatVO implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /** 全部状态合计 */
+    @ApiModelProperty(value = "全部状态合计")
+    private Long total = 0L;
+
+    /** 各状态数量,按状态码升序;无数据的合法状态为 0 */
+    @ApiModelProperty(value = "各状态数量")
+    private List<StatusCount> counts = new ArrayList<>();
+
+    /**
+     * 累加指定状态的数量:状态已存在时累加,不存在时追加,保证同一状态只出现一条记录
+     *
+     * @param status 状态码
+     * @param count  本次累加数量,为空按 0 处理
+     */
+    public void addCount(Integer status, Long count) {
+        long value = count == null ? 0L : count;
+        for (StatusCount item : counts) {
+            if (Objects.equals(item.getStatus(), status)) {
+                item.setCount((item.getCount() == null ? 0L : item.getCount()) + value);
+                return;
+            }
+        }
+        StatusCount item = new StatusCount();
+        item.setStatus(status);
+        item.setCount(value);
+        counts.add(item);
+    }
+
+    /** 单个状态的工单数量 */
+    @Data
+    @ApiModel(value = "WorkOrderStatusStatVO.StatusCount", description = "单个状态的工单数量")
+    public static class StatusCount implements Serializable {
+
+        private static final long serialVersionUID = 1L;
+
+        @ApiModelProperty(value = "工单状态码,见 WorkOrderStatusEnum")
+        private Integer status;
+
+        @ApiModelProperty(value = "该状态下的工单数量")
+        private Long count;
+    }
+}

+ 0 - 5
pipe-network-service/zksy-system/src/main/java/com/zksy/ops/dto/WorkOrderVerifyDTO.java

@@ -18,11 +18,6 @@ public class WorkOrderVerifyDTO implements Serializable {
 
     private static final long serialVersionUID = 1L;
 
-    /** 工单ID */
-    @NotNull(message = "工单ID不能为空")
-    @ApiModelProperty(value = "工单ID", required = true)
-    private Long orderId;
-
     /** 是否验收通过:true-通过(已结案) false-驳回(回到处理中) */
     @NotNull(message = "验收结果不能为空")
     @ApiModelProperty(value = "是否验收通过:true-通过 false-驳回", required = true)

+ 2 - 2
pipe-network-service/zksy-system/src/main/java/com/zksy/base/enums/WorkOrderDelayApplyStatusEnum.java → pipe-network-service/zksy-system/src/main/java/com/zksy/ops/enums/WorkOrderDelayApplyStatusEnum.java

@@ -1,4 +1,4 @@
-package com.zksy.base.enums;
+package com.zksy.ops.enums;
 
 import lombok.AllArgsConstructor;
 import lombok.Getter;
@@ -8,7 +8,7 @@ import java.util.Objects;
 /**
  * 工单延期申请状态枚举(work_order.delay_apply_status)
  *
- * @author zksy
+ * @author zengy
  */
 @Getter
 @AllArgsConstructor

+ 2 - 2
pipe-network-service/zksy-system/src/main/java/com/zksy/base/enums/WorkOrderLevelEnum.java → pipe-network-service/zksy-system/src/main/java/com/zksy/ops/enums/WorkOrderLevelEnum.java

@@ -1,4 +1,4 @@
-package com.zksy.base.enums;
+package com.zksy.ops.enums;
 
 import lombok.AllArgsConstructor;
 import lombok.Getter;
@@ -8,7 +8,7 @@ import java.util.Objects;
 /**
  * 工单优先级枚举(work_order.order_level)
  *
- * @author zksy
+ * @author zengy
  */
 @Getter
 @AllArgsConstructor

+ 2 - 2
pipe-network-service/zksy-system/src/main/java/com/zksy/base/enums/WorkOrderLogOperTypeEnum.java → pipe-network-service/zksy-system/src/main/java/com/zksy/ops/enums/WorkOrderLogOperTypeEnum.java

@@ -1,4 +1,4 @@
-package com.zksy.base.enums;
+package com.zksy.ops.enums;
 
 import lombok.AllArgsConstructor;
 import lombok.Getter;
@@ -10,7 +10,7 @@ import java.util.Objects;
  *
  * <p>与工单处置流程一一对应,从 0 开始编号</p>
  *
- * @author zksy
+ * @author zengy
  */
 @Getter
 @AllArgsConstructor

+ 2 - 2
pipe-network-service/zksy-system/src/main/java/com/zksy/base/enums/WorkOrderStatusEnum.java → pipe-network-service/zksy-system/src/main/java/com/zksy/ops/enums/WorkOrderStatusEnum.java

@@ -1,4 +1,4 @@
-package com.zksy.base.enums;
+package com.zksy.ops.enums;
 
 import lombok.AllArgsConstructor;
 import lombok.Getter;
@@ -15,7 +15,7 @@ import java.util.stream.Collectors;
  * <p>流程:0待派单 → 1待接单 → 2已接单 → 3处理中 → 4待验收 → 5已结案</p>
  * <p>旁路:6延期中(延期申请审核通过)、7已取消</p>
  *
- * @author zksy
+ * @author zengy
  */
 @Getter
 @AllArgsConstructor

+ 19 - 0
pipe-network-service/zksy-system/src/main/java/com/zksy/ops/mapper/WorkOrderLogMapper.java

@@ -0,0 +1,19 @@
+package com.zksy.ops.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.zksy.ops.domain.WorkOrderLog;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 工单日志 Mapper
+ *
+ * @author zengy
+ */
+@Mapper
+public interface WorkOrderLogMapper extends BaseMapper<WorkOrderLog> {
+
+}
+
+
+
+

+ 41 - 0
pipe-network-service/zksy-system/src/main/java/com/zksy/ops/mapper/WorkOrderMapper.java

@@ -0,0 +1,41 @@
+package com.zksy.ops.mapper;
+
+import com.baomidou.mybatisplus.core.conditions.Wrapper;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.toolkit.Constants;
+import com.zksy.ops.domain.WorkOrder;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 工单 Mapper
+ *
+ * @author zengy
+ */
+@Mapper
+public interface WorkOrderMapper extends BaseMapper<WorkOrder> {
+
+    /**
+     * 查询待处理或已超时的未办结工单。
+     */
+    List<WorkOrder> selectPendingOrOverdue(@Param("now") Date now);
+
+    /**
+     * 按状态分组统计工单数量(状态筛选条使用)。
+     *
+     * <p>过滤条件由 wrapper 承载,调用方需保证其中不含状态筛选,
+     * 否则点击某个状态后其它状态的计数会全部变成 0。</p>
+     *
+     * @param wrapper 与列表查询一致的过滤条件
+     * @return 每行包含 status(状态码)与 cnt(数量)
+     */
+    List<Map<String, Object>> countByStatus(@Param(Constants.WRAPPER) Wrapper<WorkOrder> wrapper);
+}
+
+
+
+

+ 2 - 2
pipe-network-service/zksy-system/src/main/java/com/zksy/base/service/WorkOrderLogService.java → pipe-network-service/zksy-system/src/main/java/com/zksy/ops/service/WorkOrderLogService.java

@@ -1,6 +1,6 @@
-package com.zksy.base.service;
+package com.zksy.ops.service;
 
-import com.zksy.base.domain.WorkOrderLog;
+import com.zksy.ops.domain.WorkOrderLog;
 import com.baomidou.mybatisplus.extension.service.IService;
 
 /**

+ 172 - 0
pipe-network-service/zksy-system/src/main/java/com/zksy/ops/service/WorkOrderService.java

@@ -0,0 +1,172 @@
+package com.zksy.ops.service;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.zksy.ops.domain.WorkOrder;
+import com.zksy.ops.domain.WorkOrderLog;
+import com.zksy.ops.domain.vo.WorkOrderPushVO;
+import com.zksy.common.core.domain.TreeSelect;
+import com.zksy.common.core.domain.entity.SysUser;
+import com.zksy.ops.dto.WorkOrderDelayApplyDTO;
+import com.zksy.ops.dto.WorkOrderDelayAuditDTO;
+import com.zksy.ops.dto.WorkOrderDetailVO;
+import com.zksy.ops.dto.WorkOrderDispatchDTO;
+import com.zksy.ops.dto.WorkOrderOperateDTO;
+import com.zksy.ops.dto.WorkOrderPageDTO;
+import com.zksy.ops.dto.WorkOrderStatusStatVO;
+import com.zksy.ops.dto.WorkOrderVerifyDTO;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 运维工单 Service(ops 工单流程)
+ *
+ * <p>状态流转:0待派单 →(派单) 1待接单 →(核查) 2已接单 →(处置) 3处理中
+ * →(提交验收) 4待验收 →(验收通过) 5已结案;旁路:6延期中、7已取消。</p>
+ *
+ * <p>约定:工单标识由调用方以独立参数传入(对应 REST 路径变量 {orderId}),
+ * 各操作入参 DTO 只承载本次操作的业务字段,不再重复携带 orderId。</p>
+ *
+ * @author zengy
+ */
+public interface WorkOrderService extends IService<WorkOrder> {
+
+    /**
+     * 分页查询工单列表(含数据权限过滤)
+     *
+     * @param query 查询条件
+     * @return 分页结果
+     */
+    Page<WorkOrder> findByPage(WorkOrderPageDTO query);
+
+    /**
+     * 按状态统计工单数量(列表页状态筛选条使用)。
+     *
+     * <p>筛选条件与列表查询一致,但不叠加入参中的状态筛选,
+     * 保证点击任一状态后仍能看到其它状态的数量。</p>
+     *
+     * @param query 查询条件(orderStatus 会被忽略)
+     */
+    WorkOrderStatusStatVO countByStatus(WorkOrderPageDTO query);
+
+    /**
+     * 查询工单详情(含操作日志)
+     *
+     * @param orderId 工单ID
+     */
+    WorkOrderDetailVO getDetail(Long orderId);
+
+    /**
+     * 查询工单操作日志列表(按操作时间倒序)
+     *
+     * @param orderId 工单ID
+     */
+    List<WorkOrderLog> listLogs(Long orderId);
+
+    /**
+     * 新增故障工单(初始状态:待派单)
+     *
+     * @param workOrder 工单实体,工单编号/状态/归属部门等由本方法统一生成
+     * @return 工单ID
+     */
+    Long createWorkOrder(WorkOrder workOrder);
+
+    /**
+     * 从隐患创建工单(初始状态:待派单)
+     *
+     * @return 工单ID
+     */
+    Long createWorkOrderFromHazard(String hazardId, String hazardNo, String hazardDesc, String equipmentId, String hazardLevel, String hazardImages, Date planFinishTime);
+
+    /**
+     * 派单:待派单 → 待接单
+     *
+     * @param orderId 工单ID
+     * @param dto     派单入参(责任人、备注)
+     */
+    boolean dispatch(Long orderId, WorkOrderDispatchDTO dto);
+
+    /**
+     * 核查接单:待接单 → 已接单
+     *
+     * @param orderId 工单ID
+     * @param dto     操作入参(核查结论、现场附件)
+     */
+    boolean accept(Long orderId, WorkOrderOperateDTO dto);
+
+    /**
+     * 处置:已接单/处理中/延期中 → 处理中(支持多次处置)
+     *
+     * @param orderId 工单ID
+     * @param dto     操作入参(处置内容、附件)
+     */
+    boolean process(Long orderId, WorkOrderOperateDTO dto);
+
+    /**
+     * 提交验收:处理中/延期中 → 待验收(同步写入维修后附件)
+     *
+     * @param orderId 工单ID
+     * @param dto     操作入参(验收说明、维修后附件)
+     */
+    boolean submitVerify(Long orderId, WorkOrderOperateDTO dto);
+
+    /**
+     * 验收:待验收 → 已结案(通过) / 处理中(驳回)
+     *
+     * @param orderId 工单ID
+     * @param dto     验收入参(验收结果、验收意见)
+     */
+    boolean verify(Long orderId, WorkOrderVerifyDTO dto);
+
+    /**
+     * 取消工单:待派单/待接单/已接单/处理中/待验收 → 已取消
+     *
+     * @param orderId 工单ID
+     * @param dto     操作入参(取消原因)
+     */
+    boolean cancel(Long orderId, WorkOrderOperateDTO dto);
+
+    /**
+     * 申请延期:处理中且无延期申请 → 待延期审核
+     *
+     * @param orderId 工单ID
+     * @param dto     延期申请入参(延期截止时间、延期原因)
+     */
+    boolean applyDelay(Long orderId, WorkOrderDelayApplyDTO dto);
+
+    /**
+     * 延期审核:待延期审核 → 审核通过(延期中) / 审核驳回(回到处理中)
+     *
+     * @param orderId 工单ID
+     * @param dto     延期审核入参(审核结果、审核意见)
+     */
+    boolean auditDelay(Long orderId, WorkOrderDelayAuditDTO dto);
+
+    /**
+     * 删除工单,并级联删除该工单下的操作日志(work_order_log)
+     *
+     * @param orderId 工单ID
+     * @return 是否删除成功
+     */
+    boolean deleteWorkOrder(Long orderId);
+
+    /**
+     * 获取可指派部门树
+     */
+    List<TreeSelect> assignableDeptTree();
+
+    /**
+     * 查询可指派用户
+     *
+     * @param deptId 部门ID,为空表示不限部门
+     */
+    List<SysUser> assignableUsers(Long deptId);
+
+    /**
+     * 查询需要通过 WebSocket 提醒的工单(待派单/待接单 或 已超时)
+     *
+     * @param now 当前时间,为空时取系统当前时间
+     */
+    List<WorkOrderPushVO> listPendingOrOverdue(Date now);
+}

+ 34 - 52
pipe-network-service/zksy-system/src/main/java/com/zksy/ops/service/impl/InspectTaskServiceImpl.java

@@ -38,33 +38,43 @@ import java.util.stream.Collectors;
 /**
  * 运维管理-巡检任务 Service 实现
  *
- * @author zksy
+ * @author zengy
  */
 @Slf4j
 @Service
-public class InspectTaskServiceImpl extends ServiceImpl<InspectTaskMapper, InspectTask>
-        implements InspectTaskService {
+public class InspectTaskServiceImpl extends ServiceImpl<InspectTaskMapper, InspectTask> implements InspectTaskService {
 
-    /** 巡检编号前缀 */
+    /**
+     * 巡检编号前缀
+     */
     private static final String INSPECT_NO_PREFIX = "XJ";
 
-    /** 巡检编号最大生成重试次数(并发唯一性兜底) */
+    /**
+     * 巡检编号最大生成重试次数(并发唯一性兜底)
+     */
     private static final int INSPECT_NO_MAX_RETRY = 5;
 
-    /** 状态:待巡检 */
+    /**
+     * 状态:待巡检
+     */
     private static final int STATUS_PENDING = 0;
 
-    /** 状态:已完成 */
+    /**
+     * 状态:已完成
+     */
     private static final int STATUS_DONE = 1;
 
-    /** 删除标志:存在 */
+    /**
+     * 删除标志:存在
+     */
     private static final String DEL_FLAG_NORMAL = "0";
 
-    /** 现场照片地址最大长度,对应 photo_url varchar(1000) */
+    /**
+     * 现场照片地址最大长度,对应 photo_url varchar(1000)
+     */
     private static final int PHOTO_URL_MAX_LENGTH = 1000;
 
-    private static final DateTimeFormatter DATE_TIME_FORMATTER =
-            DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+    private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
 
     @Autowired
     private ISysUserService userService;
@@ -90,9 +100,7 @@ public class InspectTaskServiceImpl extends ServiceImpl<InspectTaskMapper, Inspe
         // 关键字:模糊匹配巡检编号、巡检内容
         if (StringUtils.isNotEmpty(dto.getKeyword())) {
             String keyword = dto.getKeyword().trim();
-            wrapper.and(w -> w.like(InspectTask::getInspectTaskNo, keyword)
-                    .or()
-                    .like(InspectTask::getInspectContent, keyword));
+            wrapper.and(w -> w.like(InspectTask::getInspectTaskNo, keyword).or().like(InspectTask::getInspectContent, keyword));
         }
         // 状态精确匹配
         wrapper.eq(dto.getStatus() != null, InspectTask::getStatus, dto.getStatus());
@@ -109,12 +117,9 @@ public class InspectTaskServiceImpl extends ServiceImpl<InspectTaskMapper, Inspe
         }
         // 超级管理员(userId=1)可查看全部记录
         if (!SecurityUtils.isAdmin(dataAuthUserId)) {
-            wrapper.and(w -> w.eq(InspectTask::getAssignUserId, dataAuthUserId)
-                    .or()
-                    .eq(InspectTask::getCreateBy, dataAuthUserId));
+            wrapper.and(w -> w.eq(InspectTask::getAssignUserId, dataAuthUserId).or().eq(InspectTask::getCreateBy, dataAuthUserId));
         }
-        wrapper.orderByDesc(InspectTask::getPlanTime)
-                .orderByDesc(InspectTask::getCreateTime);
+        wrapper.orderByDesc(InspectTask::getPlanTime).orderByDesc(InspectTask::getCreateTime);
 
         Page<InspectTask> result = this.page(page, wrapper);
         // 批量翻译指派巡检人名称,避免逐条查库
@@ -205,14 +210,7 @@ public class InspectTaskServiceImpl extends ServiceImpl<InspectTaskMapper, Inspe
         }
         // 仅更新计划字段,不修改巡检编号、执行结果、状态与删除标志
         LambdaUpdateWrapper<InspectTask> updateWrapper = new LambdaUpdateWrapper<>();
-        updateWrapper.set(InspectTask::getInspectContent, task.getInspectContent())
-                .set(InspectTask::getAssignUserId, task.getAssignUserId())
-                .set(InspectTask::getPlanTime, task.getPlanTime())
-                .set(InspectTask::getUpdateBy, currentUserId())
-                .set(InspectTask::getUpdateTime, LocalDateTime.now())
-                .eq(InspectTask::getInspectTaskId, task.getInspectTaskId())
-                .eq(InspectTask::getStatus, STATUS_PENDING)
-                .eq(InspectTask::getDelFlag, DEL_FLAG_NORMAL);
+        updateWrapper.set(InspectTask::getInspectContent, task.getInspectContent()).set(InspectTask::getAssignUserId, task.getAssignUserId()).set(InspectTask::getPlanTime, task.getPlanTime()).set(InspectTask::getUpdateBy, currentUserId()).set(InspectTask::getUpdateTime, LocalDateTime.now()).eq(InspectTask::getInspectTaskId, task.getInspectTaskId()).eq(InspectTask::getStatus, STATUS_PENDING).eq(InspectTask::getDelFlag, DEL_FLAG_NORMAL);
         return this.update(updateWrapper);
     }
 
@@ -247,17 +245,9 @@ public class InspectTaskServiceImpl extends ServiceImpl<InspectTaskMapper, Inspe
             throw new ServiceException("该巡检任务已完成,不可重复提交");
         }
         LambdaUpdateWrapper<InspectTask> updateWrapper = new LambdaUpdateWrapper<>();
-        updateWrapper.set(InspectTask::getRealTime, realTime)
-                .set(InspectTask::getProblem, dto.getProblem())
-                .set(InspectTask::getProcess, dto.getProcess())
-                .set(InspectTask::getPhotoUrl, dto.getPhotoUrl())
-                .set(InspectTask::getStatus, STATUS_DONE)
-                .set(InspectTask::getUpdateBy, currentUserId())
-                .set(InspectTask::getUpdateTime, LocalDateTime.now())
-                .eq(InspectTask::getInspectTaskId, dto.getInspectTaskId())
+        updateWrapper.set(InspectTask::getRealTime, realTime).set(InspectTask::getProblem, dto.getProblem()).set(InspectTask::getProcess, dto.getProcess()).set(InspectTask::getPhotoUrl, dto.getPhotoUrl()).set(InspectTask::getStatus, STATUS_DONE).set(InspectTask::getUpdateBy, currentUserId()).set(InspectTask::getUpdateTime, LocalDateTime.now()).eq(InspectTask::getInspectTaskId, dto.getInspectTaskId())
                 // 状态门禁:仅待巡检且未删除的记录可被完成,防止并发重复提交
-                .eq(InspectTask::getStatus, STATUS_PENDING)
-                .eq(InspectTask::getDelFlag, DEL_FLAG_NORMAL);
+                .eq(InspectTask::getStatus, STATUS_PENDING).eq(InspectTask::getDelFlag, DEL_FLAG_NORMAL);
         boolean updated = this.update(updateWrapper);
         if (!updated) {
             throw new ServiceException("该巡检任务已完成或已被删除,无法重复完成巡检");
@@ -291,11 +281,8 @@ public class InspectTaskServiceImpl extends ServiceImpl<InspectTaskMapper, Inspe
      */
     private String generateInspectTaskNo() {
         for (int i = 0; i < INSPECT_NO_MAX_RETRY; i++) {
-            String no = INSPECT_NO_PREFIX
-                    + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"))
-                    + String.format("%04d", new Random().nextInt(10000));
-            Long count = this.count(new LambdaQueryWrapper<InspectTask>()
-                    .eq(InspectTask::getInspectTaskNo, no));
+            String no = INSPECT_NO_PREFIX + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")) + String.format("%04d", new Random().nextInt(10000));
+            Long count = this.count(new LambdaQueryWrapper<InspectTask>().eq(InspectTask::getInspectTaskNo, no));
             if (count == null || count == 0) {
                 return no;
             }
@@ -328,8 +315,7 @@ public class InspectTaskServiceImpl extends ServiceImpl<InspectTaskMapper, Inspe
             Map<Long, String> userMap = new HashMap<>(users.size() * 2);
             for (SysUser user : users) {
                 if (user.getUserId() != null) {
-                    userMap.put(user.getUserId(),
-                            StringUtils.isNotEmpty(user.getNickName()) ? user.getNickName() : user.getUserName());
+                    userMap.put(user.getUserId(), StringUtils.isNotEmpty(user.getNickName()) ? user.getNickName() : user.getUserName());
                 }
             }
             return userMap;
@@ -368,8 +354,8 @@ public class InspectTaskServiceImpl extends ServiceImpl<InspectTaskMapper, Inspe
     /**
      * 解析时间字符串,兼容 yyyy-MM-dd HH:mm:ss / yyyy-MM-dd HH:mm / yyyy-MM-dd
      *
-     * @param text    时间字符串
-     * @param isEnd   是否为结束时间(结束时间补齐到当日 23:59:59)
+     * @param text  时间字符串
+     * @param isEnd 是否为结束时间(结束时间补齐到当日 23:59:59)
      * @return 解析结果,为空或解析失败返回 null
      */
     private LocalDateTime parseDateTime(String text, boolean isEnd) {
@@ -385,9 +371,7 @@ public class InspectTaskServiceImpl extends ServiceImpl<InspectTaskMapper, Inspe
                 return LocalDateTime.parse(value + ":00", DATE_TIME_FORMATTER);
             }
             if (value.length() == 10) {
-                return isEnd
-                        ? LocalDateTime.parse(value + " 23:59:59", DATE_TIME_FORMATTER)
-                        : LocalDateTime.parse(value + " 00:00:00", DATE_TIME_FORMATTER);
+                return isEnd ? LocalDateTime.parse(value + " 23:59:59", DATE_TIME_FORMATTER) : LocalDateTime.parse(value + " 00:00:00", DATE_TIME_FORMATTER);
             }
         } catch (DateTimeParseException e) {
             log.warn("巡检任务时间参数解析失败:{}", value);
@@ -495,9 +479,7 @@ public class InspectTaskServiceImpl extends ServiceImpl<InspectTaskMapper, Inspe
         if (allDepts == null || allDepts.isEmpty()) {
             return Collections.emptyList();
         }
-        List<SysDept> scoped = allDepts.stream()
-                .filter(dept -> dept.getDeptId() != null && scope.contains(dept.getDeptId()))
-                .collect(Collectors.toList());
+        List<SysDept> scoped = allDepts.stream().filter(dept -> dept.getDeptId() != null && scope.contains(dept.getDeptId())).collect(Collectors.toList());
         return deptService.buildDeptTreeSelect(scoped);
     }
 

+ 18 - 0
pipe-network-service/zksy-system/src/main/java/com/zksy/ops/service/impl/WorkOrderLogServiceImpl.java

@@ -0,0 +1,18 @@
+package com.zksy.ops.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.zksy.ops.domain.WorkOrderLog;
+import com.zksy.ops.mapper.WorkOrderLogMapper;
+import com.zksy.ops.service.WorkOrderLogService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+@Slf4j
+@Service
+public class WorkOrderLogServiceImpl extends ServiceImpl<WorkOrderLogMapper, WorkOrderLog> implements WorkOrderLogService {
+
+}
+
+
+
+

+ 333 - 244
pipe-network-service/zksy-system/src/main/java/com/zksy/base/service/impl/BusinessWorkOrderServiceImpl.java → pipe-network-service/zksy-system/src/main/java/com/zksy/ops/service/impl/WorkOrderServiceImpl.java

@@ -1,4 +1,4 @@
-package com.zksy.base.service.impl;
+package com.zksy.ops.service.impl;
 
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
@@ -6,22 +6,21 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.zksy.base.domain.EquipmentBase;
 import com.zksy.base.domain.HazardHiddenAccount;
-import com.zksy.base.domain.WorkOrder;
-import com.zksy.base.domain.WorkOrderLog;
-import com.zksy.base.domain.vo.WorkOrderPushVO;
-import com.zksy.base.enums.WorkOrderDelayApplyStatusEnum;
-import com.zksy.base.enums.WorkOrderLevelEnum;
-import com.zksy.base.enums.WorkOrderLogOperTypeEnum;
-import com.zksy.base.enums.WorkOrderStatusEnum;
+import com.zksy.ops.domain.WorkOrder;
+import com.zksy.ops.domain.WorkOrderLog;
+import com.zksy.ops.domain.vo.WorkOrderPushVO;
+import com.zksy.ops.enums.WorkOrderDelayApplyStatusEnum;
+import com.zksy.ops.enums.WorkOrderLevelEnum;
+import com.zksy.ops.enums.WorkOrderLogOperTypeEnum;
+import com.zksy.ops.enums.WorkOrderStatusEnum;
 import com.zksy.base.mapper.EquipmentBaseMapper;
 import com.zksy.base.mapper.HazardHiddenAccountMapper;
-import com.zksy.base.mapper.WorkOrderLogMapper;
-import com.zksy.base.mapper.WorkOrderMapper;
-import com.zksy.base.service.BusinessWorkOrderService;
+import com.zksy.ops.mapper.WorkOrderLogMapper;
+import com.zksy.ops.mapper.WorkOrderMapper;
+import com.zksy.common.constant.UserConstants;
 import com.zksy.common.core.domain.TreeSelect;
 import com.zksy.common.core.domain.entity.SysUser;
 import com.zksy.common.core.domain.model.LoginUser;
-import com.zksy.common.constant.UserConstants;
 import com.zksy.common.exception.ServiceException;
 import com.zksy.common.utils.DateUtils;
 import com.zksy.common.utils.SecurityUtils;
@@ -32,7 +31,9 @@ import com.zksy.ops.dto.WorkOrderDetailVO;
 import com.zksy.ops.dto.WorkOrderDispatchDTO;
 import com.zksy.ops.dto.WorkOrderOperateDTO;
 import com.zksy.ops.dto.WorkOrderPageDTO;
+import com.zksy.ops.dto.WorkOrderStatusStatVO;
 import com.zksy.ops.dto.WorkOrderVerifyDTO;
+import com.zksy.ops.service.WorkOrderService;
 import com.zksy.ops.support.OpsAssignScopeSupport;
 import com.zksy.system.service.ISysUserService;
 import lombok.extern.slf4j.Slf4j;
@@ -54,6 +55,7 @@ import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.ThreadLocalRandom;
+import java.util.function.Consumer;
 import java.util.stream.Collectors;
 
 /**
@@ -62,11 +64,14 @@ import java.util.stream.Collectors;
  * <p>状态流转:0待派单 →(派单) 1待接单 →(核查) 2已接单 →(处置) 3处理中
  * →(提交验收) 4待验收 →(验收通过) 5已结案;延期审核通过后进入 6延期中;任意前置状态可 7已取消。</p>
  *
- * @author zksy
+ * <p>实现约定:所有状态写入统一走 {@link #transition} / {@link #updateWrapper},
+ * 由 UPDATE 的 WHERE 条件兜住并发(乐观推进),避免「先查再改」产生竞态。</p>
+ *
+ * @author zengy
  */
 @Slf4j
 @Service
-public class BusinessWorkOrderServiceImpl extends ServiceImpl<WorkOrderMapper, WorkOrder> implements BusinessWorkOrderService {
+public class WorkOrderServiceImpl extends ServiceImpl<WorkOrderMapper, WorkOrder> implements WorkOrderService {
 
     /**
      * 工单编号前缀
@@ -83,6 +88,11 @@ public class BusinessWorkOrderServiceImpl extends ServiceImpl<WorkOrderMapper, W
      */
     private static final int ORDER_NO_RANDOM_BOUND = 10000;
 
+    /**
+     * 分页默认每页条数
+     */
+    private static final long DEFAULT_PAGE_SIZE = 10L;
+
     /**
      * 框架数据范围参数名,与 DataScopeAspect.DATA_SCOPE 保持一致。
      * zksy-system 模块不依赖 zksy-framework(DataScopeAspect 所在模块),故此处使用字面量。
@@ -105,84 +115,119 @@ public class BusinessWorkOrderServiceImpl extends ServiceImpl<WorkOrderMapper, W
     private OpsAssignScopeSupport assignScopeSupport;
 
     @Override
-    public Page<WorkOrder> findByPage(WorkOrderPageDTO dto) {
-        if (dto == null) {
-            dto = new WorkOrderPageDTO();
-        }
-        long pageNum = (dto.getPageNum() == null || dto.getPageNum() < 1) ? 1L : dto.getPageNum();
-        long pageSize = (dto.getPageSize() == null || dto.getPageSize() < 1) ? 10L : dto.getPageSize();
-        Page<WorkOrder> page = new Page<>(pageNum, pageSize);
+    public Page<WorkOrder> findByPage(WorkOrderPageDTO query) {
+        WorkOrderPageDTO condition = query == null ? new WorkOrderPageDTO() : query;
+        long pageNum = condition.getPageNum() == null || condition.getPageNum() < 1 ? 1L : condition.getPageNum();
+        long pageSize = condition.getPageSize() == null || condition.getPageSize() < 1 ? DEFAULT_PAGE_SIZE : condition.getPageSize();
 
         if (assignScopeSupport.currentUserId() == null) {
             // 未获取到登录用户时返回空结果,避免越权查看
             return new Page<>(pageNum, pageSize);
         }
 
-        LambdaQueryWrapper<WorkOrder> wrapper = new LambdaQueryWrapper<>();
-        String dataScope = dataScopeSql(dto);
-        if (StringUtils.isNotEmpty(dataScope)) {
-            wrapper.apply(dataScope);
+        Page<WorkOrder> result = this.page(new Page<>(pageNum, pageSize), buildQueryWrapper(condition));
+        fillUserName(result.getRecords());
+        return result;
+    }
+
+    @Override
+    public WorkOrderStatusStatVO countByStatus(WorkOrderPageDTO query) {
+        WorkOrderPageDTO condition = query == null ? new WorkOrderPageDTO() : query;
+        WorkOrderStatusStatVO stat = new WorkOrderStatusStatVO();
+        // 先把全部合法状态补 0,前端筛选条不依赖「缺失即 0」的兜底
+        for (WorkOrderStatusEnum status : WorkOrderStatusEnum.values()) {
+            stat.addCount(status.getCode(), 0L);
         }
-        wrapper.eq(dto.getOrderLevel() != null, WorkOrder::getOrderLevel, dto.getOrderLevel()).eq(dto.getDelayApplyStatus() != null, WorkOrder::getDelayApplyStatus, dto.getDelayApplyStatus()).eq(StringUtils.isNotEmpty(dto.getDeviceCode()), WorkOrder::getDeviceCode, dto.getDeviceCode()).ge(StringUtils.isNotEmpty(dto.getFaultTimeStart()), WorkOrder::getFaultTime, DateUtils.parseDate(dto.getFaultTimeStart())).le(StringUtils.isNotEmpty(dto.getFaultTimeEnd()), WorkOrder::getFaultTime, DateUtils.parseDate(dto.getFaultTimeEnd()));
-        if (StringUtils.isNotEmpty(dto.getOrderStatus())) {
-            List<Integer> statusList = parseStatusList(dto.getOrderStatus());
-            if (!statusList.isEmpty()) {
-                wrapper.in(WorkOrder::getOrderStatus, statusList);
+
+        if (assignScopeSupport.currentUserId() == null) {
+            // 与 findByPage 保持一致:未获取到登录用户时返回全 0,避免越权查看数据量
+            return stat;
+        }
+
+        // 不叠加状态筛选,否则点击某个状态后其它状态的计数会全部变成 0
+        List<Map<String, Object>> rows = baseMapper.countByStatus(buildFilterWrapper(condition, false));
+        long total = 0L;
+        if (!CollectionUtils.isEmpty(rows)) {
+            for (Map<String, Object> row : rows) {
+                Long count = toLong(row.get("cnt"));
+                if (count == null) {
+                    continue;
+                }
+                Long statusCode = toLong(row.get("status"));
+                stat.addCount(statusCode == null ? null : statusCode.intValue(), count);
+                total += count;
             }
         }
-        if (StringUtils.isNotEmpty(dto.getKeyword())) {
-            String keyword = dto.getKeyword().trim();
-            wrapper.and(w -> w.like(WorkOrder::getOrderNo, keyword).or().like(WorkOrder::getDeviceCode, keyword).or().like(WorkOrder::getOrderDesc, keyword));
-        }
-        applyDeviceTypeScope(wrapper, dto.getDeviceTypeParentId());
-        wrapper.orderByAsc(WorkOrder::getOrderLevel).orderByAsc(WorkOrder::getOrderStatus).orderByDesc(WorkOrder::getFaultTime).orderByDesc(WorkOrder::getCreateTime);
-
-        Page<WorkOrder> result = this.page(page, wrapper);
-        fillUserName(result.getRecords());
-        return result;
+        stat.setTotal(total);
+        return stat;
     }
 
     @Override
-    public WorkOrderDetailVO getDetailById(Long orderId) {
+    public WorkOrderDetailVO getDetail(Long orderId) {
         WorkOrder order = getOrderById(orderId);
         fillUserName(Collections.singletonList(order));
-        WorkOrderDetailVO vo = new WorkOrderDetailVO();
-        vo.setOrder(order);
-        vo.setLogs(getWorkOrderLogs(orderId));
-        return vo;
+
+        WorkOrderDetailVO detail = new WorkOrderDetailVO();
+        detail.setOrder(order);
+        detail.setLogs(listLogs(orderId));
+        return detail;
     }
 
     @Override
-    public List<WorkOrderLog> getWorkOrderLogs(Long orderId) {
+    public List<WorkOrderLog> listLogs(Long orderId) {
         if (orderId == null) {
             return Collections.emptyList();
         }
         List<WorkOrderLog> logs = workOrderLogMapper.selectList(new LambdaQueryWrapper<WorkOrderLog>().eq(WorkOrderLog::getOrderId, orderId).orderByDesc(WorkOrderLog::getOperTime));
-        if (!CollectionUtils.isEmpty(logs)) {
-            for (WorkOrderLog item : logs) {
-                item.setOperTypeDesc(WorkOrderLogOperTypeEnum.getDescByCode(item.getOperType()));
-                item.setAttachList(splitAttach(item.getAttachUrl()));
-            }
+        for (WorkOrderLog item : logs) {
+            item.setOperTypeDesc(WorkOrderLogOperTypeEnum.getDescByCode(item.getOperType()));
+            item.setAttachList(splitAttach(item.getAttachUrl()));
         }
         return logs;
     }
 
-    // ==================== 新增 ====================
+    @Override
+    public List<TreeSelect> assignableDeptTree() {
+        return assignScopeSupport.assignableDeptTree();
+    }
+
+    @Override
+    public List<SysUser> assignableUsers(Long deptId) {
+        return assignScopeSupport.assignableUsers(deptId);
+    }
+
+    @Override
+    public List<WorkOrderPushVO> listPendingOrOverdue(Date now) {
+        Date currentTime = now == null ? new Date() : now;
+        List<WorkOrder> orders = baseMapper.selectPendingOrOverdue(currentTime);
+        if (CollectionUtils.isEmpty(orders)) {
+            return new ArrayList<>();
+        }
+        List<WorkOrderPushVO> result = new ArrayList<>(orders.size());
+        for (WorkOrder order : orders) {
+            WorkOrderPushVO item = toPushVO(order, currentTime);
+            if (item != null) {
+                result.add(item);
+            }
+        }
+        return result;
+    }
 
     @Override
     @Transactional(rollbackFor = Exception.class)
-    public Long addWorkOrder(WorkOrder workOrder) {
+    public Long createWorkOrder(WorkOrder workOrder) {
         if (workOrder == null || StringUtils.isEmpty(workOrder.getOrderDesc())) {
             throw new ServiceException("故障描述不能为空");
         }
         checkDeviceOrderConflict(workOrder.getDeviceId());
+
+        Date now = new Date();
         workOrder.setOrderId(null);
         workOrder.setOrderNo(generateOrderNo());
         workOrder.setOrderLevel(WorkOrderLevelEnum.getByCode(workOrder.getOrderLevel()).getCode());
         if (StringUtils.isNotEmpty(workOrder.getDeviceId()) && StringUtils.isEmpty(workOrder.getDeviceCode())) {
             workOrder.setDeviceCode(resolveDeviceCode(workOrder.getDeviceId()));
         }
-        Date now = new Date();
         if (workOrder.getFaultTime() == null) {
             workOrder.setFaultTime(now);
         }
@@ -209,6 +254,7 @@ public class BusinessWorkOrderServiceImpl extends ServiceImpl<WorkOrderMapper, W
         }
         checkDeviceOrderConflict(equipmentId);
 
+        Date now = new Date();
         WorkOrder workOrder = new WorkOrder();
         workOrder.setOrderNo(generateOrderNo());
         workOrder.setHazardId(hazardId);
@@ -225,7 +271,6 @@ public class BusinessWorkOrderServiceImpl extends ServiceImpl<WorkOrderMapper, W
         if (StringUtils.isNotEmpty(hazardImages)) {
             workOrder.setMaintenanceBeforeImg(hazardImages);
         }
-        Date now = new Date();
         workOrder.setFaultTime(now);
         workOrder.setCreateTime(now);
         workOrder.setUpdateTime(now);
@@ -236,17 +281,15 @@ public class BusinessWorkOrderServiceImpl extends ServiceImpl<WorkOrderMapper, W
         return workOrder.getOrderId();
     }
 
-    // ==================== 流程操作 ====================
-
     @Override
     @Transactional(rollbackFor = Exception.class)
-    public boolean dispatch(WorkOrderDispatchDTO dto) {
-        Long orderId = requireOrderId(dto == null ? null : dto.getOrderId());
+    public boolean dispatch(Long orderId, WorkOrderDispatchDTO dto) {
+        Long id = requireOrderId(orderId);
         Long handlerId = dto.getHandlerId();
         if (handlerId == null) {
             throw new ServiceException("责任人不能为空");
         }
-        WorkOrder order = getOrderById(orderId);
+        WorkOrder order = getOrderById(id);
         if (!Objects.equals(WorkOrderStatusEnum.PENDING_DISPATCH.getCode(), order.getOrderStatus())) {
             throw new ServiceException("仅待派单工单允许派单");
         }
@@ -256,137 +299,127 @@ public class BusinessWorkOrderServiceImpl extends ServiceImpl<WorkOrderMapper, W
             throw new ServiceException("责任人不存在或已停用");
         }
 
-        Long operUserId = currentUserId();
         Date now = new Date();
-        this.update(new LambdaUpdateWrapper<WorkOrder>().set(WorkOrder::getOrderStatus, WorkOrderStatusEnum.PENDING_ACCEPT.getCode()).set(WorkOrder::getReceiveUser, handlerId).set(WorkOrder::getDispatchUser, operUserId).set(WorkOrder::getDispatchTime, now)
+        this.update(updateWrapper(id).set(WorkOrder::getOrderStatus, WorkOrderStatusEnum.PENDING_ACCEPT.getCode()).set(WorkOrder::getReceiveUser, handlerId).set(WorkOrder::getDispatchUser, currentUserId()).set(WorkOrder::getDispatchTime, now)
                 // 数据归属部门跟随派单指定的责任人
-                .set(handler.getDeptId() != null, WorkOrder::getDeptId, handler.getDeptId()).set(WorkOrder::getUpdateTime, now).eq(WorkOrder::getOrderId, orderId).eq(WorkOrder::getOrderStatus, WorkOrderStatusEnum.PENDING_DISPATCH.getCode()));
+                .set(handler.getDeptId() != null, WorkOrder::getDeptId, handler.getDeptId()).eq(WorkOrder::getOrderStatus, WorkOrderStatusEnum.PENDING_DISPATCH.getCode()));
 
         String logContent = "派单给责任人:" + userLabel(handler) + (StringUtils.isEmpty(dto.getRemarks()) ? "" : ",备注:" + dto.getRemarks());
-        writeLog(orderId, WorkOrderLogOperTypeEnum.DISPATCH, logContent, null);
+        writeLog(id, WorkOrderLogOperTypeEnum.DISPATCH, logContent, null);
         return true;
     }
 
     @Override
     @Transactional(rollbackFor = Exception.class)
-    public boolean accept(WorkOrderOperateDTO dto) {
-        WorkOrder order = loadForOperate(dto, "核查接单");
-        if (!Objects.equals(WorkOrderStatusEnum.PENDING_ACCEPT.getCode(), order.getOrderStatus())) {
-            throw new ServiceException("仅待接单工单允许核查接单");
-        }
+    public boolean accept(Long orderId, WorkOrderOperateDTO dto) {
+        WorkOrder order = getOrderById(orderId);
+        requireResponsible(order, currentUserId(), "核查接单");
+        requireStatus(order, WorkOrderStatusEnum.PENDING_ACCEPT, "仅待接单工单允许核查接单");
         // 核查接单必须上传现场情况附件
-        if (StringUtils.isEmpty(attach(dto))) {
+        if (StringUtils.isEmpty(dto.getAttachUrl())) {
             throw new ServiceException("核查接单必须上传现场情况附件");
         }
-        Date now = new Date();
-        LambdaUpdateWrapper<WorkOrder> uw = new LambdaUpdateWrapper<WorkOrder>().set(WorkOrder::getOrderStatus, WorkOrderStatusEnum.ACCEPTED.getCode())
-                // 维修前图片固定来源于核查接单上传的现场情况附件
-                .set(WorkOrder::getMaintenanceBeforeImg, attach(dto)).set(WorkOrder::getUpdateTime, now).eq(WorkOrder::getOrderId, order.getOrderId()).eq(WorkOrder::getOrderStatus, WorkOrderStatusEnum.PENDING_ACCEPT.getCode());
-        this.update(uw);
 
-        writeLog(order.getOrderId(), WorkOrderLogOperTypeEnum.ACCEPT, buildDesc("", content(dto)), attach(dto));
-        return true;
+        Long id = order.getOrderId();
+        // 维修前图片固定来源于核查接单上传的现场情况附件
+        boolean updated = transition(id, WorkOrderStatusEnum.PENDING_ACCEPT.getCode(), WorkOrderStatusEnum.ACCEPTED.getCode(), wrapper -> wrapper.set(WorkOrder::getMaintenanceBeforeImg, dto.getAttachUrl()));
+        writeLog(id, WorkOrderLogOperTypeEnum.ACCEPT, buildDesc("", dto.getContent()), dto.getAttachUrl());
+        return updated;
     }
 
     @Override
     @Transactional(rollbackFor = Exception.class)
-    public boolean process(WorkOrderOperateDTO dto) {
-        WorkOrder order = loadForOperate(dto, "处置");
+    public boolean process(Long orderId, WorkOrderOperateDTO dto) {
+        WorkOrder order = getOrderById(orderId);
+        requireResponsible(order, currentUserId(), "处置");
         Integer status = order.getOrderStatus();
         if (!WorkOrderStatusEnum.canProcess(status)) {
             throw new ServiceException("当前工单状态不允许处置");
         }
-        Date now = new Date();
+
         // 已接单 → 处理中;处理中/延期中 保持原状态,支持多次处置
         Integer targetStatus = Objects.equals(WorkOrderStatusEnum.ACCEPTED.getCode(), status) ? WorkOrderStatusEnum.PROCESSING.getCode() : status;
-        LambdaUpdateWrapper<WorkOrder> uw = new LambdaUpdateWrapper<WorkOrder>().set(WorkOrder::getOrderStatus, targetStatus).set(WorkOrder::getUpdateTime, now).eq(WorkOrder::getOrderId, order.getOrderId()).eq(WorkOrder::getOrderStatus, status);
-        this.update(uw);
+        boolean updated = transition(order.getOrderId(), status, targetStatus, null);
 
-        syncHazardStatus(order, "整改中", content(dto));
-        writeLog(order.getOrderId(), WorkOrderLogOperTypeEnum.PROCESS, buildDesc("", content(dto)), attach(dto));
-        return true;
+        syncHazardStatus(order, "整改中", dto.getContent());
+        writeLog(order.getOrderId(), WorkOrderLogOperTypeEnum.PROCESS, buildDesc("", dto.getContent()), dto.getAttachUrl());
+        return updated;
     }
 
     @Override
     @Transactional(rollbackFor = Exception.class)
-    public boolean submitVerify(WorkOrderOperateDTO dto) {
-        WorkOrder order = loadForOperate(dto, "提交验收");
+    public boolean submitVerify(Long orderId, WorkOrderOperateDTO dto) {
+        WorkOrder order = getOrderById(orderId);
+        requireResponsible(order, currentUserId(), "提交验收");
         Integer status = order.getOrderStatus();
         if (!WorkOrderStatusEnum.canSubmitVerify(status)) {
             throw new ServiceException("仅处理中/延期中工单允许提交验收");
         }
         // 提交验收必须上传维修后的情况附件
-        if (StringUtils.isEmpty(attach(dto))) {
+        if (StringUtils.isEmpty(dto.getAttachUrl())) {
             throw new ServiceException("提交验收必须上传维修后的情况附件");
         }
-        Date now = new Date();
-        // 提交验收:附件已在上方强制必填,直接写入工单维修后图片
-        LambdaUpdateWrapper<WorkOrder> uw = new LambdaUpdateWrapper<WorkOrder>().set(WorkOrder::getOrderStatus, WorkOrderStatusEnum.PENDING_VERIFY.getCode()).set(WorkOrder::getMaintenanceAfterImg, attach(dto)).set(WorkOrder::getUpdateTime, now).eq(WorkOrder::getOrderId, order.getOrderId()).eq(WorkOrder::getOrderStatus, status);
-        this.update(uw);
 
-        writeLog(order.getOrderId(), WorkOrderLogOperTypeEnum.SUBMIT_VERIFY, buildDesc("", content(dto)), attach(dto));
-        return true;
+        // 附件已在上方强制必填,直接写入工单维修后图片
+        boolean updated = transition(order.getOrderId(), status, WorkOrderStatusEnum.PENDING_VERIFY.getCode(), wrapper -> wrapper.set(WorkOrder::getMaintenanceAfterImg, dto.getAttachUrl()));
+        writeLog(order.getOrderId(), WorkOrderLogOperTypeEnum.SUBMIT_VERIFY, buildDesc("", dto.getContent()), dto.getAttachUrl());
+        return updated;
     }
 
     @Override
     @Transactional(rollbackFor = Exception.class)
-    public boolean verify(WorkOrderVerifyDTO dto) {
-        Long orderId = requireOrderId(dto == null ? null : dto.getOrderId());
-        Boolean passed = dto == null ? null : dto.getPassed();
+    public boolean verify(Long orderId, WorkOrderVerifyDTO dto) {
+        Long id = requireOrderId(orderId);
+        Boolean passed = dto.getPassed();
         if (passed == null) {
             throw new ServiceException("验收结果不能为空");
         }
-        WorkOrder order = getOrderById(orderId);
-        if (!Objects.equals(WorkOrderStatusEnum.PENDING_VERIFY.getCode(), order.getOrderStatus())) {
-            throw new ServiceException("仅待验收工单允许验收");
-        }
-        Long operUserId = currentUserId();
+        WorkOrder order = getOrderById(id);
+        requireStatus(order, WorkOrderStatusEnum.PENDING_VERIFY, "仅待验收工单允许验收");
+
         Date now = new Date();
         WorkOrderStatusEnum targetStatus = passed ? WorkOrderStatusEnum.CLOSED : WorkOrderStatusEnum.PROCESSING;
-        LambdaUpdateWrapper<WorkOrder> uw = new LambdaUpdateWrapper<WorkOrder>().set(WorkOrder::getOrderStatus, targetStatus.getCode()).set(WorkOrder::getVerifyUser, operUserId).set(WorkOrder::getVerifyTime, now).set(WorkOrder::getUpdateTime, now).eq(WorkOrder::getOrderId, orderId).eq(WorkOrder::getOrderStatus, WorkOrderStatusEnum.PENDING_VERIFY.getCode());
-        if (passed) {
-            uw.set(WorkOrder::getRealFinishTime, now);
-        }
-        if (StringUtils.isNotEmpty(dto.getContent())) {
-            uw.set(WorkOrder::getRepairResult, dto.getContent());
-        }
-        this.update(uw);
+        boolean updated = transition(id, WorkOrderStatusEnum.PENDING_VERIFY.getCode(), targetStatus.getCode(), wrapper -> {
+            wrapper.set(WorkOrder::getVerifyUser, currentUserId()).set(WorkOrder::getVerifyTime, now);
+            if (passed) {
+                wrapper.set(WorkOrder::getRealFinishTime, now);
+            }
+            if (StringUtils.isNotEmpty(dto.getContent())) {
+                wrapper.set(WorkOrder::getRepairResult, dto.getContent());
+            }
+        });
 
         if (passed) {
             syncHazardStatus(order, "已整改", dto.getContent());
         }
-        writeLog(orderId, passed ? WorkOrderLogOperTypeEnum.VERIFY_PASS : WorkOrderLogOperTypeEnum.VERIFY_REJECT, buildDesc("验收意见", dto.getContent()), dto.getAttachUrl());
-        return true;
+        writeLog(id, passed ? WorkOrderLogOperTypeEnum.VERIFY_PASS : WorkOrderLogOperTypeEnum.VERIFY_REJECT, buildDesc("验收意见", dto.getContent()), dto.getAttachUrl());
+        return updated;
     }
 
     @Override
     @Transactional(rollbackFor = Exception.class)
-    public boolean cancel(WorkOrderOperateDTO dto) {
-        Long orderId = requireOrderId(dto == null ? null : dto.getOrderId());
-        WorkOrder order = getOrderById(orderId);
+    public boolean cancel(Long orderId, WorkOrderOperateDTO dto) {
+        Long id = requireOrderId(orderId);
+        WorkOrder order = getOrderById(id);
         if (!WorkOrderStatusEnum.canCancel(order.getOrderStatus())) {
             throw new ServiceException("当前工单状态不允许取消");
         }
-        if (StringUtils.isEmpty(content(dto))) {
+        if (StringUtils.isEmpty(dto.getContent())) {
             throw new ServiceException("请填写取消原因");
         }
-        Date now = new Date();
-        this.update(new LambdaUpdateWrapper<WorkOrder>().set(WorkOrder::getOrderStatus, WorkOrderStatusEnum.CANCELLED.getCode()).set(WorkOrder::getUpdateTime, now).eq(WorkOrder::getOrderId, orderId).eq(WorkOrder::getOrderStatus, order.getOrderStatus()));
 
-        writeLog(orderId, WorkOrderLogOperTypeEnum.CANCEL, buildDesc("原因", content(dto)), attach(dto));
-        return true;
+        boolean updated = transition(id, order.getOrderStatus(), WorkOrderStatusEnum.CANCELLED.getCode(), null);
+        writeLog(id, WorkOrderLogOperTypeEnum.CANCEL, buildDesc("原因", dto.getContent()), dto.getAttachUrl());
+        return updated;
     }
 
     @Override
     @Transactional(rollbackFor = Exception.class)
-    public boolean applyDelay(WorkOrderDelayApplyDTO dto) {
-        Long orderId = requireOrderId(dto == null ? null : dto.getOrderId());
-        WorkOrder order = getOrderById(orderId);
-        Long currentUserId = currentUserId();
-        requireResponsible(order, currentUserId, "申请延期");
-        if (!Objects.equals(WorkOrderStatusEnum.PROCESSING.getCode(), order.getOrderStatus())) {
-            throw new ServiceException("仅处理中工单允许申请延期");
-        }
+    public boolean applyDelay(Long orderId, WorkOrderDelayApplyDTO dto) {
+        Long id = requireOrderId(orderId);
+        WorkOrder order = getOrderById(id);
+        requireResponsible(order, currentUserId(), "申请延期");
+        requireStatus(order, WorkOrderStatusEnum.PROCESSING, "仅处理中工单允许申请延期");
         if (!Objects.equals(WorkOrderDelayApplyStatusEnum.NONE.getCode(), order.getDelayApplyStatus())) {
             throw new ServiceException("该工单已提交过延期申请,请勿重复提交");
         }
@@ -398,47 +431,111 @@ public class BusinessWorkOrderServiceImpl extends ServiceImpl<WorkOrderMapper, W
             throw new ServiceException("延期截止时间格式不正确");
         }
 
-        Date now = new Date();
-        this.update(new LambdaUpdateWrapper<WorkOrder>().set(WorkOrder::getDelayApplyStatus, WorkOrderDelayApplyStatusEnum.PENDING_AUDIT.getCode()).set(WorkOrder::getDelayedDeadline, delayedDeadline).set(WorkOrder::getDelayedReason, dto.getDelayedReason()).set(WorkOrder::getUpdateTime, now).eq(WorkOrder::getOrderId, orderId).eq(WorkOrder::getOrderStatus, WorkOrderStatusEnum.PROCESSING.getCode()).eq(WorkOrder::getDelayApplyStatus, WorkOrderDelayApplyStatusEnum.NONE.getCode()));
+        this.update(updateWrapper(id).set(WorkOrder::getDelayApplyStatus, WorkOrderDelayApplyStatusEnum.PENDING_AUDIT.getCode()).set(WorkOrder::getDelayedDeadline, delayedDeadline).set(WorkOrder::getDelayedReason, dto.getDelayedReason()).eq(WorkOrder::getOrderStatus, WorkOrderStatusEnum.PROCESSING.getCode()).eq(WorkOrder::getDelayApplyStatus, WorkOrderDelayApplyStatusEnum.NONE.getCode()));
 
-        writeLog(orderId, WorkOrderLogOperTypeEnum.DELAY_APPLY, "申请延期至 " + dto.getDelayedDeadline() + ",原因:" + dto.getDelayedReason(), dto.getAttachUrl());
+        writeLog(id, WorkOrderLogOperTypeEnum.DELAY_APPLY, "申请延期至 " + dto.getDelayedDeadline() + ",原因:" + dto.getDelayedReason(), dto.getAttachUrl());
         return true;
     }
 
     @Override
     @Transactional(rollbackFor = Exception.class)
-    public boolean auditDelay(WorkOrderDelayAuditDTO dto) {
-        Long orderId = requireOrderId(dto == null ? null : dto.getOrderId());
+    public boolean auditDelay(Long orderId, WorkOrderDelayAuditDTO dto) {
+        Long id = requireOrderId(orderId);
         Boolean approved = dto.getApproved();
         if (approved == null) {
             throw new ServiceException("延期审核结果不能为空");
         }
-        WorkOrder order = getOrderById(orderId);
+        WorkOrder order = getOrderById(id);
         if (!Objects.equals(WorkOrderDelayApplyStatusEnum.PENDING_AUDIT.getCode(), order.getDelayApplyStatus())) {
             throw new ServiceException("该工单没有待审核的延期申请");
         }
+
         WorkOrderDelayApplyStatusEnum applyStatus = approved ? WorkOrderDelayApplyStatusEnum.APPROVED : WorkOrderDelayApplyStatusEnum.REJECTED;
         Integer orderStatus = approved ? WorkOrderStatusEnum.DELAYING.getCode() : order.getOrderStatus();
-        Date now = new Date();
-        this.update(new LambdaUpdateWrapper<WorkOrder>().set(WorkOrder::getDelayApplyStatus, applyStatus.getCode()).set(WorkOrder::getOrderStatus, orderStatus).set(WorkOrder::getUpdateTime, now).eq(WorkOrder::getOrderId, orderId).eq(WorkOrder::getDelayApplyStatus, WorkOrderDelayApplyStatusEnum.PENDING_AUDIT.getCode()));
+        this.update(updateWrapper(id).set(WorkOrder::getDelayApplyStatus, applyStatus.getCode()).set(WorkOrder::getOrderStatus, orderStatus).eq(WorkOrder::getDelayApplyStatus, WorkOrderDelayApplyStatusEnum.PENDING_AUDIT.getCode()));
 
-        writeLog(orderId, approved ? WorkOrderLogOperTypeEnum.DELAY_APPROVE : WorkOrderLogOperTypeEnum.DELAY_REJECT, buildDesc("审核意见", dto.getAuditOpinion()), null);
+        writeLog(id, approved ? WorkOrderLogOperTypeEnum.DELAY_APPROVE : WorkOrderLogOperTypeEnum.DELAY_REJECT, buildDesc("审核意见", dto.getAuditOpinion()), null);
         return true;
     }
 
     @Override
     @Transactional(rollbackFor = Exception.class)
-    public boolean deleteWithLogs(Long orderId) {
-        if (orderId == null) {
-            return false;
+    public boolean deleteWorkOrder(Long orderId) {
+        Long id = requireOrderId(orderId);
+        WorkOrder order = getOrderById(id);
+        // 业务规则:仅已取消工单允许删除
+        if (!WorkOrderStatusEnum.CANCELLED.getCode().equals(order.getOrderStatus())) {
+            throw new ServiceException("仅已取消工单允许删除");
         }
         // 先清理该工单的操作日志,避免 work_order_log 残留脏数据
-        workOrderLogMapper.delete(new LambdaQueryWrapper<WorkOrderLog>().eq(WorkOrderLog::getOrderId, orderId));
-        return this.removeById(orderId);
+        workOrderLogMapper.delete(new LambdaQueryWrapper<WorkOrderLog>().eq(WorkOrderLog::getOrderId, id));
+        return this.removeById(id);
+    }
+
+    /**
+     * 构建工单分页查询条件(含数据权限片段与设备类别收窄)
+     */
+    private LambdaQueryWrapper<WorkOrder> buildQueryWrapper(WorkOrderPageDTO condition) {
+        LambdaQueryWrapper<WorkOrder> wrapper = buildFilterWrapper(condition, true);
+        wrapper.orderByAsc(WorkOrder::getOrderLevel).orderByAsc(WorkOrder::getOrderStatus).orderByDesc(WorkOrder::getFaultTime).orderByDesc(WorkOrder::getCreateTime);
+        return wrapper;
+    }
+
+    /**
+     * 构建工单筛选条件(数据权限片段 + 业务条件 + 设备类别收窄),列表查询与状态统计共用。
+     *
+     * <p>返回值刻意不含 orderBy:状态统计要按状态分组,带排序在 MySQL ONLY_FULL_GROUP_BY 下会报错。</p>
+     *
+     * @param condition  查询条件
+     * @param withStatus 是否叠加状态筛选。状态统计必须传 false,
+     *                   否则点击某个状态后其它状态的计数会全部变成 0。
+     */
+    private LambdaQueryWrapper<WorkOrder> buildFilterWrapper(WorkOrderPageDTO condition, boolean withStatus) {
+        LambdaQueryWrapper<WorkOrder> wrapper = new LambdaQueryWrapper<>();
+
+        String dataScope = dataScopeSql(condition);
+        if (StringUtils.isNotEmpty(dataScope)) {
+            wrapper.apply(dataScope);
+        }
+
+        wrapper.eq(condition.getOrderLevel() != null, WorkOrder::getOrderLevel, condition.getOrderLevel()).eq(condition.getDelayApplyStatus() != null, WorkOrder::getDelayApplyStatus, condition.getDelayApplyStatus()).eq(StringUtils.isNotEmpty(condition.getDeviceId()), WorkOrder::getDeviceId, condition.getDeviceId()).eq(StringUtils.isNotEmpty(condition.getDeviceCode()), WorkOrder::getDeviceCode, condition.getDeviceCode()).ge(StringUtils.isNotEmpty(condition.getFaultTimeStart()), WorkOrder::getFaultTime, DateUtils.parseDate(condition.getFaultTimeStart())).le(StringUtils.isNotEmpty(condition.getFaultTimeEnd()), WorkOrder::getFaultTime, DateUtils.parseDate(condition.getFaultTimeEnd()));
+
+        if (withStatus) {
+            List<Integer> statusList = parseStatusList(condition.getOrderStatus());
+            wrapper.in(!statusList.isEmpty(), WorkOrder::getOrderStatus, statusList);
+        }
+
+        String keyword = StringUtils.isEmpty(condition.getKeyword()) ? null : condition.getKeyword().trim();
+        if (StringUtils.isNotEmpty(keyword)) {
+            wrapper.and(w -> w.like(WorkOrder::getOrderNo, keyword).or().like(WorkOrder::getDeviceCode, keyword).or().like(WorkOrder::getOrderDesc, keyword));
+        }
+
+        applyDeviceTypeScope(wrapper, condition.getDeviceTypeParentId());
+        return wrapper;
     }
 
-    private String dataScopeSql(WorkOrderPageDTO dto) {
-        Map<String, Object> params = dto.getParams();
+    /**
+     * Map 值转 Long:兼容不同驱动下 COUNT(*) 返回 Integer / Long / BigInteger / String 的差异
+     */
+    private Long toLong(Object value) {
+        if (value == null) {
+            return null;
+        }
+        if (value instanceof Number) {
+            return ((Number) value).longValue();
+        }
+        String text = String.valueOf(value).trim();
+        return StringUtils.isNumeric(text) ? Long.valueOf(text) : null;
+    }
+
+    /**
+     * 解析并剥离数据权限片段中多余的 AND / OR 前缀。
+     *
+     * <p>DataScopeAspect 注入的片段形如 {@code AND (...)},而 wrapper.apply 会自动追加 AND,
+     * 故此处仅保留条件本体。</p>
+     */
+    private String dataScopeSql(WorkOrderPageDTO condition) {
+        Map<String, Object> params = condition.getParams();
         if (params == null || params.isEmpty()) {
             return null;
         }
@@ -447,9 +544,6 @@ public class BusinessWorkOrderServiceImpl extends ServiceImpl<WorkOrderMapper, W
             return null;
         }
         String sql = String.valueOf(dataScope).trim();
-        if (sql.length() == 0) {
-            return null;
-        }
         String upper = sql.toUpperCase(Locale.ROOT);
         if (upper.startsWith("AND ")) {
             sql = sql.substring(4).trim();
@@ -465,89 +559,53 @@ public class BusinessWorkOrderServiceImpl extends ServiceImpl<WorkOrderMapper, W
      * <p>过滤链路:work_order.device_id → equipment_base.equipment_id
      * → equipment_base.equipment_type_id → equipment_type.id → equipment_type.parent_type_id。
      * 采用 EXISTS 子查询而非 JOIN,避免设备表一对多时放大分页结果集,也不影响已有的
-     * 数据权限片段(dataScope)与 MyBatis-Plus 分页计数。
+     * 数据权限片段(dataScope)与 MyBatis-Plus 分页计数。</p>
      *
-     * <p>parentTypeId 通过 MyBatis-Plus 的 apply 占位符 {0} 注入,由框架做 SQL 注入检查,不拼接原始入参。
+     * <p>parentTypeId 通过 MyBatis-Plus 的 apply 占位符 {0} 注入,由框架做 SQL 注入检查,不拼接原始入参。</p>
      *
-     * @param wrapper       工单查询条件
-     * @param parentTypeId  设备父类别ID,为空表示不限制
+     * @param wrapper      工单查询条件
+     * @param parentTypeId 设备父类别ID,为空表示不限制
      */
     private void applyDeviceTypeScope(LambdaQueryWrapper<WorkOrder> wrapper, String parentTypeId) {
         if (StringUtils.isEmpty(parentTypeId)) {
             return;
         }
-        wrapper.apply("EXISTS (SELECT 1 FROM app_user.equipment_base eb "
-                        + "INNER JOIN app_user.equipment_type et ON et.id = eb.equipment_type_id "
-                        + "WHERE eb.equipment_id = app_user.work_order.device_id "
-                        + "AND et.parent_type_id = {0})",
-                parentTypeId.trim());
+        wrapper.apply("EXISTS (SELECT 1 FROM app_user.equipment_base eb " + "INNER JOIN app_user.equipment_type et ON et.id = eb.equipment_type_id " + "WHERE eb.equipment_id = app_user.work_order.device_id " + "AND et.parent_type_id = {0})", parentTypeId.trim());
     }
 
-    @Override
-    public List<TreeSelect> assignableDeptTree() {
-        return assignScopeSupport.assignableDeptTree();
-    }
-
-    @Override
-    public List<SysUser> assignableUsers(Long deptId) {
-        return assignScopeSupport.assignableUsers(deptId);
+    /**
+     * 状态更新基础条件:固定工单ID 并刷新更新时间
+     */
+    private LambdaUpdateWrapper<WorkOrder> updateWrapper(Long orderId) {
+        return new LambdaUpdateWrapper<WorkOrder>().set(WorkOrder::getUpdateTime, new Date()).eq(WorkOrder::getOrderId, orderId);
     }
 
-    @Override
-    public List<WorkOrderPushVO> selectPendingOrOverdueWorkOrders(Date now) {
-        Date currentTime = now != null ? now : new Date();
-        List<WorkOrder> orders = baseMapper.selectPendingOrOverdue(currentTime);
-        List<WorkOrderPushVO> result = new ArrayList<>();
-        if (orders == null || orders.isEmpty()) {
-            return result;
-        }
-        for (WorkOrder order : orders) {
-            Integer status = order.getOrderStatus();
-            Date effectiveDeadline = order.getDelayedDeadline() != null ? order.getDelayedDeadline() : order.getPlanFinishTime();
-            boolean pending = Objects.equals(WorkOrderStatusEnum.PENDING_DISPATCH.getCode(), status) || Objects.equals(WorkOrderStatusEnum.PENDING_ACCEPT.getCode(), status);
-            boolean overdue = effectiveDeadline != null && effectiveDeadline.before(currentTime);
-            if (!pending && !overdue) {
-                continue;
-            }
-            WorkOrderPushVO item = new WorkOrderPushVO();
-            item.setOrderId(order.getOrderId());
-            item.setOrderNo(order.getOrderNo());
-            item.setOrderStatus(status);
-            item.setOrderStatusDesc(WorkOrderStatusEnum.getDescByCode(status));
-            item.setOrderLevel(order.getOrderLevel());
-            item.setDeptId(order.getDeptId());
-            item.setReceiveUser(order.getReceiveUser() == null ? null : String.valueOf(order.getReceiveUser()));
-            item.setDeviceId(order.getDeviceId());
-            item.setDeviceCode(order.getDeviceCode());
-            item.setOrderDesc(order.getOrderDesc());
-            item.setPlanFinishTime(order.getPlanFinishTime());
-            item.setDelayedDeadline(order.getDelayedDeadline());
-            item.setEffectiveDeadline(effectiveDeadline);
-            item.setOverdue(overdue);
-            if (overdue) {
-                item.setOverdueType(order.getDelayedDeadline() != null ? "DELAYED_DEADLINE" : "PLAN_FINISH_TIME");
-                item.setOverdueMinutes(Math.max(0L, (currentTime.getTime() - effectiveDeadline.getTime()) / 60000L));
-            } else {
-                item.setOverdueType("PENDING");
-                item.setOverdueMinutes(0L);
-            }
-            item.setCreateTime(order.getCreateTime());
-            item.setUpdateTime(order.getUpdateTime());
-            result.add(item);
+    /**
+     * 带前置状态校验的状态推进:WHERE 中同时匹配 orderId 与当前状态,
+     * 返回受影响行数是否为 1,天然抵御并发重复提交。
+     *
+     * @param orderId        工单ID
+     * @param expectedStatus 期望的当前状态
+     * @param targetStatus   目标状态
+     * @param extra          附加字段写入(可为 null)
+     * @return 是否更新成功
+     */
+    private boolean transition(Long orderId, Integer expectedStatus, Integer targetStatus, Consumer<LambdaUpdateWrapper<WorkOrder>> extra) {
+        LambdaUpdateWrapper<WorkOrder> wrapper = updateWrapper(orderId).set(WorkOrder::getOrderStatus, targetStatus).eq(expectedStatus != null, WorkOrder::getOrderStatus, expectedStatus);
+        if (extra != null) {
+            extra.accept(wrapper);
         }
-        return result;
+        return this.update(wrapper);
     }
 
-    // ==================== 私有方法 ====================
 
     /**
-     * 加载工单并校验操作人是责任人本人(超级管理员放行)
+     * 校验工单当前状态,不符合时抛出业务异常
      */
-    private WorkOrder loadForOperate(WorkOrderOperateDTO dto, String actionName) {
-        Long orderId = requireOrderId(dto == null ? null : dto.getOrderId());
-        WorkOrder order = getOrderById(orderId);
-        requireResponsible(order, currentUserId(), actionName);
-        return order;
+    private void requireStatus(WorkOrder order, WorkOrderStatusEnum expected, String message) {
+        if (!Objects.equals(expected.getCode(), order.getOrderStatus())) {
+            throw new ServiceException(message);
+        }
     }
 
     /**
@@ -566,10 +624,8 @@ public class BusinessWorkOrderServiceImpl extends ServiceImpl<WorkOrderMapper, W
     }
 
     private WorkOrder getOrderById(Long orderId) {
-        if (orderId == null) {
-            throw new ServiceException("工单ID不能为空");
-        }
-        WorkOrder order = this.getById(orderId);
+        Long id = requireOrderId(orderId);
+        WorkOrder order = this.getById(id);
         if (order == null) {
             throw new ServiceException("工单不存在或已被删除");
         }
@@ -583,6 +639,19 @@ public class BusinessWorkOrderServiceImpl extends ServiceImpl<WorkOrderMapper, W
         return orderId;
     }
 
+    /**
+     * 校验同一设备是否已存在未办结工单
+     */
+    private void checkDeviceOrderConflict(String deviceId) {
+        if (StringUtils.isEmpty(deviceId)) {
+            return;
+        }
+        boolean exists = this.exists(new LambdaQueryWrapper<WorkOrder>().eq(WorkOrder::getDeviceId, deviceId).in(WorkOrder::getOrderStatus, WorkOrderStatusEnum.activeCodes()));
+        if (exists) {
+            throw new ServiceException("该设备已有处理中的工单,需结案或取消后才能创建新工单");
+        }
+    }
+
     /**
      * 写入工单操作日志(操作人取当前登录用户)
      */
@@ -598,28 +667,14 @@ public class BusinessWorkOrderServiceImpl extends ServiceImpl<WorkOrderMapper, W
         workOrderLogMapper.insert(logEntry);
     }
 
-    /**
-     * 校验同一设备是否已存在未办结工单
-     */
-    private void checkDeviceOrderConflict(String deviceId) {
-        if (StringUtils.isEmpty(deviceId)) {
-            return;
-        }
-        Long count = this.lambdaQuery().eq(WorkOrder::getDeviceId, deviceId).in(WorkOrder::getOrderStatus, WorkOrderStatusEnum.activeCodes()).count();
-        if (count != null && count > 0) {
-            throw new ServiceException("该设备已有处理中的工单,需结案或取消后才能创建新工单");
-        }
-    }
-
     /**
      * 生成全局唯一工单编号:WO + 14位时间戳 + 4位随机数
      */
     private String generateOrderNo() {
-        String pattern = "yyyyMMddHHmmss";
+        SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMddHHmmss");
         for (int i = 0; i < ORDER_NO_MAX_RETRY; i++) {
-            String no = ORDER_NO_PREFIX + new SimpleDateFormat(pattern).format(new Date()) + String.format("%04d", ThreadLocalRandom.current().nextInt(ORDER_NO_RANDOM_BOUND));
-            Long count = this.count(new LambdaQueryWrapper<WorkOrder>().eq(WorkOrder::getOrderNo, no));
-            if (count == null || count == 0) {
+            String no = ORDER_NO_PREFIX + formatter.format(new Date()) + String.format("%04d", ThreadLocalRandom.current().nextInt(ORDER_NO_RANDOM_BOUND));
+            if (!this.exists(new LambdaQueryWrapper<WorkOrder>().eq(WorkOrder::getOrderNo, no))) {
                 return no;
             }
         }
@@ -649,6 +704,45 @@ public class BusinessWorkOrderServiceImpl extends ServiceImpl<WorkOrderMapper, W
         }
     }
 
+    /**
+     * 转换为 WebSocket 推送视图,非「待办或超时」的工单返回 null
+     */
+    private WorkOrderPushVO toPushVO(WorkOrder order, Date currentTime) {
+        Integer status = order.getOrderStatus();
+        Date effectiveDeadline = order.getDelayedDeadline() != null ? order.getDelayedDeadline() : order.getPlanFinishTime();
+        boolean pending = Objects.equals(WorkOrderStatusEnum.PENDING_DISPATCH.getCode(), status) || Objects.equals(WorkOrderStatusEnum.PENDING_ACCEPT.getCode(), status);
+        boolean overdue = effectiveDeadline != null && effectiveDeadline.before(currentTime);
+        if (!pending && !overdue) {
+            return null;
+        }
+
+        WorkOrderPushVO item = new WorkOrderPushVO();
+        item.setOrderId(order.getOrderId());
+        item.setOrderNo(order.getOrderNo());
+        item.setOrderStatus(status);
+        item.setOrderStatusDesc(WorkOrderStatusEnum.getDescByCode(status));
+        item.setOrderLevel(order.getOrderLevel());
+        item.setDeptId(order.getDeptId());
+        item.setReceiveUser(order.getReceiveUser() == null ? null : String.valueOf(order.getReceiveUser()));
+        item.setDeviceId(order.getDeviceId());
+        item.setDeviceCode(order.getDeviceCode());
+        item.setOrderDesc(order.getOrderDesc());
+        item.setPlanFinishTime(order.getPlanFinishTime());
+        item.setDelayedDeadline(order.getDelayedDeadline());
+        item.setEffectiveDeadline(effectiveDeadline);
+        item.setOverdue(overdue);
+        if (overdue) {
+            item.setOverdueType(order.getDelayedDeadline() != null ? "DELAYED_DEADLINE" : "PLAN_FINISH_TIME");
+            item.setOverdueMinutes(Math.max(0L, (currentTime.getTime() - effectiveDeadline.getTime()) / 60000L));
+        } else {
+            item.setOverdueType("PENDING");
+            item.setOverdueMinutes(0L);
+        }
+        item.setCreateTime(order.getCreateTime());
+        item.setUpdateTime(order.getUpdateTime());
+        return item;
+    }
+
     /**
      * 同步隐患状态:根据工单处理结果更新关联隐患记录的状态和整改反馈
      */
@@ -695,7 +789,7 @@ public class BusinessWorkOrderServiceImpl extends ServiceImpl<WorkOrderMapper, W
      * 按本页实际涉及的用户ID批量查询并构建「用户ID → 姓名」映射。
      *
      * <p>此前是全量拉取用户表(selectAllEnabledUser)再建 Map,用户量大时每次分页查询都要扫描全表。
-     * 实际只用到当页记录的 receiveUser / dispatchUser,故改为按 ID 精确查询。
+     * 实际只用到当页记录的 receiveUser / dispatchUser,故改为按 ID 精确查询。</p>
      *
      * @param records 当前页工单记录
      * @return 用户ID → 姓名的映射;查询失败时返回空 Map(降级为展示ID,不影响主流程)
@@ -744,7 +838,7 @@ public class BusinessWorkOrderServiceImpl extends ServiceImpl<WorkOrderMapper, W
 
     /**
      * 获取当前登录用户姓名:用户昵称(nickName)优先,缺省时回退到登录账号。
-     * 工单操作日志展示姓名比账号更直观,与 WaterSupplyAlarmServiceImpl 保持一致。
+     * 工单操作日志展示姓名比账号更直观。
      */
     private String currentUserName() {
         try {
@@ -763,17 +857,12 @@ public class BusinessWorkOrderServiceImpl extends ServiceImpl<WorkOrderMapper, W
     }
 
     private List<Integer> parseStatusList(String orderStatus) {
+        if (StringUtils.isEmpty(orderStatus)) {
+            return Collections.emptyList();
+        }
         return Arrays.stream(orderStatus.split(",")).map(String::trim).filter(StringUtils::isNumeric).map(Integer::valueOf).filter(WorkOrderStatusEnum::isValid).collect(Collectors.toList());
     }
 
-    private String content(WorkOrderOperateDTO dto) {
-        return dto == null ? null : dto.getContent();
-    }
-
-    private String attach(WorkOrderOperateDTO dto) {
-        return dto == null ? null : dto.getAttachUrl();
-    }
-
     private String buildDesc(String prefix, String detail) {
         boolean prefixEmpty = StringUtils.isEmpty(prefix);
         boolean detailEmpty = StringUtils.isEmpty(detail);

+ 2 - 2
pipe-network-service/zksy-system/src/main/resources/mapper/WorkOrderLogMapper.xml → pipe-network-service/zksy-system/src/main/resources/mapper/ops/WorkOrderLogMapper.xml

@@ -2,9 +2,9 @@
 <!DOCTYPE mapper
         PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
-<mapper namespace="com.zksy.base.mapper.WorkOrderLogMapper">
+<mapper namespace="com.zksy.ops.mapper.WorkOrderLogMapper">
 
-    <resultMap id="BaseResultMap" type="com.zksy.base.domain.WorkOrderLog">
+    <resultMap id="BaseResultMap" type="com.zksy.ops.domain.WorkOrderLog">
             <id property="logId" column="log_id" jdbcType="BIGINT"/>
             <result property="orderId" column="order_id" jdbcType="BIGINT"/>
             <result property="operType" column="oper_type" jdbcType="TINYINT"/>

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

@@ -2,9 +2,9 @@
 <!DOCTYPE mapper
         PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
-<mapper namespace="com.zksy.base.mapper.WorkOrderMapper">
+<mapper namespace="com.zksy.ops.mapper.WorkOrderMapper">
 
-    <resultMap id="BaseResultMap" type="com.zksy.base.domain.WorkOrder">
+    <resultMap id="BaseResultMap" type="com.zksy.ops.domain.WorkOrder">
             <id property="orderId" column="order_id" jdbcType="BIGINT"/>
             <result property="orderNo" column="order_no" jdbcType="VARCHAR"/>
             <result property="alarmId" column="alarm_id" jdbcType="VARCHAR"/>
@@ -60,4 +60,14 @@
                  create_time DESC NULLS LAST,
                  order_id DESC
     </select>
+
+    <!-- 状态筛选条统计:复用列表筛选条件(不含状态筛选)按状态分组计数。
+         条件由 wrapper 承载且不含 orderBy,避免 MySQL ONLY_FULL_GROUP_BY 下报错。 -->
+    <select id="countByStatus" resultType="java.util.HashMap">
+        SELECT order_status AS status,
+               COUNT(*)     AS cnt
+        FROM app_user.work_order
+        ${ew.customSqlSegment}
+        GROUP BY order_status
+    </select>
 </mapper>

+ 0 - 62
pipe-network-service/zksy-system/src/test/java/com/zksy/base/service/impl/BusinessWorkOrderServiceImplTest.java

@@ -1,62 +0,0 @@
-package com.zksy.base.service.impl;
-
-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.common.core.domain.entity.SysUser;
-import com.zksy.common.exception.ServiceException;
-import com.zksy.system.service.ISysUserService;
-import org.junit.jupiter.api.Test;
-import org.mockito.ArgumentCaptor;
-import org.springframework.test.util.ReflectionTestUtils;
-
-import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.*;
-
-class BusinessWorkOrderServiceImplTest {
-
-    @Test
-    void dispatchRejectsMissingOrInactiveReceiverBeforeUpdatingOrder() {
-        BusinessWorkOrderServiceImpl service = new BusinessWorkOrderServiceImpl();
-        WorkOrderMapper orderMapper = mock(WorkOrderMapper.class);
-        ISysUserService users = mock(ISysUserService.class);
-        ReflectionTestUtils.setField(service, "baseMapper", orderMapper);
-        ReflectionTestUtils.setField(service, "sysUserService", users);
-        when(users.selectUserById(88L)).thenReturn(null);
-
-        assertThrows(ServiceException.class, () -> service.dispatchOrder(1L, 88L, null, 7L, "operator"));
-        verify(orderMapper, never()).selectById(any());
-        verify(orderMapper, never()).updateById(any(WorkOrder.class));
-    }
-
-    @Test
-    void dispatchAcceptsActiveNonDeletedReceiverAndMovesOrderToStatusTwo() {
-        BusinessWorkOrderServiceImpl service = new BusinessWorkOrderServiceImpl();
-        WorkOrderMapper orderMapper = mock(WorkOrderMapper.class);
-        WorkOrderLogMapper logs = mock(WorkOrderLogMapper.class);
-        ISysUserService users = mock(ISysUserService.class);
-        ReflectionTestUtils.setField(service, "baseMapper", orderMapper);
-        ReflectionTestUtils.setField(service, "workOrderLogMapper", logs);
-        ReflectionTestUtils.setField(service, "sysUserService", users);
-
-        SysUser user = new SysUser();
-        user.setUserId(88L);
-        user.setStatus("0");
-        user.setDelFlag("0");
-        when(users.selectUserById(88L)).thenReturn(user);
-        WorkOrder pending = new WorkOrder();
-        pending.setOrderId(1L);
-        pending.setOrderStatus(1);
-        when(orderMapper.selectById(1L)).thenReturn(pending);
-
-        assertTrue(service.dispatchOrder(1L, 88L, "处理", 7L, "operator"));
-        ArgumentCaptor<WorkOrder> update = ArgumentCaptor.forClass(WorkOrder.class);
-        verify(orderMapper).updateById(update.capture());
-        org.junit.jupiter.api.Assertions.assertEquals(2, update.getValue().getOrderStatus());
-        org.junit.jupiter.api.Assertions.assertEquals("88", update.getValue().getReceiveUser());
-        verify(logs).insert(any(WorkOrderLog.class));
-    }
-}

+ 12 - 12
pipe-network-service/zksy-system/src/test/java/com/zksy/base/service/impl/WaterSupplyDeviceServiceImplTest.java

@@ -11,7 +11,7 @@ import com.zksy.base.domain.EquipmentBase;
 import com.zksy.base.domain.EquipmentMaintain;
 import com.zksy.base.domain.EquipmentStatus;
 import com.zksy.base.domain.EquipmentType;
-import com.zksy.base.domain.WorkOrder;
+import com.zksy.ops.domain.WorkOrder;
 import com.zksy.base.domain.vo.AbnormalDeviceVO;
 import com.zksy.base.domain.vo.DeviceGisVO;
 import com.zksy.base.domain.vo.DeviceLedgerStatisticsVO;
@@ -20,9 +20,11 @@ import com.zksy.base.domain.vo.MaintainOrderVO;
 import com.zksy.base.mapper.EquipmentMaintainMapper;
 import com.zksy.base.mapper.EquipmentStatusMapper;
 import com.zksy.base.mapper.EquipmentTypeMapper;
-import com.zksy.base.mapper.WorkOrderMapper;
+import com.zksy.ops.mapper.WorkOrderMapper;
 import com.zksy.base.service.EquipmentBaseService;
 import com.zksy.common.core.domain.entity.SysUser;
+import com.zksy.ops.dto.WorkOrderDispatchDTO;
+import com.zksy.ops.service.WorkOrderService;
 import com.zksy.system.service.ISysUserService;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.BeforeAll;
@@ -69,7 +71,7 @@ class WaterSupplyDeviceServiceImplTest {
     private EquipmentMaintainMapper equipmentMaintainMapper;
     private AlarmDataMapper alarmDataMapper;
     private WorkOrderMapper workOrderMapper;
-    private com.zksy.base.service.BusinessWorkOrderService businessWorkOrderService;
+    private WorkOrderService businessWorkOrderService;
 
     private EquipmentBase pressureDevice;
     private EquipmentBase repairDevice;
@@ -84,7 +86,7 @@ class WaterSupplyDeviceServiceImplTest {
         equipmentMaintainMapper = mock(EquipmentMaintainMapper.class);
         alarmDataMapper = mock(AlarmDataMapper.class);
         workOrderMapper = mock(WorkOrderMapper.class);
-        businessWorkOrderService = mock(com.zksy.base.service.BusinessWorkOrderService.class);
+        businessWorkOrderService = mock(WorkOrderService.class);
 
         ReflectionTestUtils.setField(service, "equipmentBaseService", equipmentBaseService);
         ReflectionTestUtils.setField(service, "equipmentTypeMapper", equipmentTypeMapper);
@@ -526,7 +528,6 @@ class WaterSupplyDeviceServiceImplTest {
         WorkOrder result = service.createAbnormalWorkOrder(request);
 
         assertEquals(99L, result.getOrderId());
-        assertEquals(1, result.getOrderType());
         assertEquals(2, result.getOrderLevel());
         assertEquals(1, result.getOrderStatus());
         assertNull(result.getReceiveUser());
@@ -535,7 +536,6 @@ class WaterSupplyDeviceServiceImplTest {
         WorkOrder submitted = workOrderCaptor.getValue();
         assertEquals(pressureDevice.getEquipmentId(), submitted.getDeviceId());
         assertEquals(pressureDevice.getEquipmentCode(), submitted.getDeviceCode());
-        assertEquals(1, submitted.getOrderType());
         assertEquals(2, submitted.getOrderLevel());
         assertEquals("压力异常", submitted.getOrderDesc());
         assertNull(submitted.getReceiveUser());
@@ -626,7 +626,7 @@ class WaterSupplyDeviceServiceImplTest {
             submitted.setOrderStatus(1);
             return 100L;
         });
-        when(businessWorkOrderService.dispatchOrder(100L, 88L, "请尽快处理", null, null))
+        when(businessWorkOrderService.dispatch(any(), any(WorkOrderDispatchDTO.class)))
                 .thenReturn(true);
 
         assertTrue(service.createMaintainOrder(request));
@@ -634,9 +634,11 @@ class WaterSupplyDeviceServiceImplTest {
         verify(equipmentMaintainMapper, never()).insert(any(EquipmentMaintain.class));
         ArgumentCaptor<WorkOrder> orderCaptor = ArgumentCaptor.forClass(WorkOrder.class);
         verify(businessWorkOrderService).createWorkOrder(orderCaptor.capture());
-        assertEquals(1, orderCaptor.getValue().getOrderType());
         assertEquals(1, orderCaptor.getValue().getOrderLevel());
-        verify(businessWorkOrderService).dispatchOrder(100L, 88L, "请尽快处理", null, null);
+        ArgumentCaptor<WorkOrderDispatchDTO> dispatchCaptor = ArgumentCaptor.forClass(WorkOrderDispatchDTO.class);
+        verify(businessWorkOrderService).dispatch(any(), dispatchCaptor.capture());
+        assertEquals(88L, dispatchCaptor.getValue().getHandlerId());
+        assertEquals("请尽快处理", dispatchCaptor.getValue().getRemarks());
     }
 
     @Test
@@ -649,7 +651,6 @@ class WaterSupplyDeviceServiceImplTest {
         WorkOrder activeOrder = new WorkOrder();
         activeOrder.setDeviceId(pressureDevice.getEquipmentId());
         activeOrder.setOrderNo("WO-ACTIVE");
-        activeOrder.setOrderType(1);
         activeOrder.setOrderStatus(2);
         when(workOrderMapper.selectList(any())).thenReturn(Collections.singletonList(activeOrder));
 
@@ -790,9 +791,8 @@ class WaterSupplyDeviceServiceImplTest {
         WorkOrder order = new WorkOrder();
         order.setDeviceId(deviceId);
         order.setOrderNo(orderNo);
-        order.setOrderType(1);
         order.setOrderStatus(orderStatus);
-        order.setReceiveUser(receiveUser);
+        order.setReceiveUser(receiveUser == null ? null : Long.valueOf(receiveUser));
         return order;
     }
 

+ 140 - 0
pipe-network-service/zksy-system/src/test/java/com/zksy/ops/service/impl/WorkOrderServiceImplTest.java

@@ -0,0 +1,140 @@
+package com.zksy.ops.service.impl;
+
+import com.zksy.ops.domain.WorkOrder;
+import com.zksy.ops.domain.WorkOrderLog;
+import com.zksy.ops.enums.WorkOrderStatusEnum;
+import com.zksy.ops.mapper.WorkOrderLogMapper;
+import com.zksy.ops.mapper.WorkOrderMapper;
+import com.zksy.common.core.domain.entity.SysUser;
+import com.zksy.common.exception.ServiceException;
+import com.zksy.ops.dto.WorkOrderDispatchDTO;
+import com.zksy.ops.support.OpsAssignScopeSupport;
+import com.zksy.system.service.ISysUserService;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.test.util.ReflectionTestUtils;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ * 工单派单(dispatch)单元测试
+ *
+ * <p>覆盖:责任人缺失、责任人已停用、工单状态不允许派单、正常派单四种分支。</p>
+ */
+class WorkOrderServiceImplTest {
+
+    private static final Long ORDER_ID = 1L;
+
+    private static final Long HANDLER_ID = 88L;
+
+    private WorkOrderServiceImpl service;
+
+    private WorkOrderMapper orderMapper;
+
+    private WorkOrderLogMapper logMapper;
+
+    private ISysUserService sysUserService;
+
+    @BeforeEach
+    void setUp() {
+        service = new WorkOrderServiceImpl();
+        orderMapper = mock(WorkOrderMapper.class);
+        logMapper = mock(WorkOrderLogMapper.class);
+        sysUserService = mock(ISysUserService.class);
+        ReflectionTestUtils.setField(service, "baseMapper", orderMapper);
+        ReflectionTestUtils.setField(service, "workOrderLogMapper", logMapper);
+        ReflectionTestUtils.setField(service, "sysUserService", sysUserService);
+        ReflectionTestUtils.setField(service, "assignScopeSupport", mock(OpsAssignScopeSupport.class));
+    }
+
+    @Test
+    void dispatchRejectsMissingHandlerId() {
+        ServiceException error = assertThrows(ServiceException.class,
+                () -> service.dispatch(ORDER_ID, new WorkOrderDispatchDTO()));
+        assertEquals("责任人不能为空", error.getMessage());
+        verify(orderMapper, never()).selectById(any());
+    }
+
+    @Test
+    void dispatchRejectsOrderNotFound() {
+        when(orderMapper.selectById(ORDER_ID)).thenReturn(null);
+        ServiceException error = assertThrows(ServiceException.class,
+                () -> service.dispatch(ORDER_ID, dispatchDTO()));
+        assertEquals("工单不存在或已被删除", error.getMessage());
+        verify(orderMapper, never()).update(isNull(), any());
+    }
+
+    @Test
+    void dispatchRejectsOrderNotInPendingDispatchStatus() {
+        WorkOrder processing = new WorkOrder();
+        processing.setOrderId(ORDER_ID);
+        processing.setOrderStatus(WorkOrderStatusEnum.PROCESSING.getCode());
+        when(orderMapper.selectById(ORDER_ID)).thenReturn(processing);
+
+        ServiceException error = assertThrows(ServiceException.class,
+                () -> service.dispatch(ORDER_ID, dispatchDTO()));
+        assertEquals("仅待派单工单允许派单", error.getMessage());
+        verify(sysUserService, never()).selectUserById(any());
+        verify(orderMapper, never()).update(isNull(), any());
+    }
+
+    @Test
+    void dispatchRejectsInactiveReceiverWithoutUpdatingOrder() {
+        when(orderMapper.selectById(ORDER_ID)).thenReturn(pendingOrder());
+        SysUser disabled = new SysUser();
+        disabled.setUserId(HANDLER_ID);
+        disabled.setStatus("1");
+        disabled.setDelFlag("0");
+        when(sysUserService.selectUserById(HANDLER_ID)).thenReturn(disabled);
+
+        ServiceException error = assertThrows(ServiceException.class,
+                () -> service.dispatch(ORDER_ID, dispatchDTO()));
+        assertEquals("责任人不存在或已停用", error.getMessage());
+        verify(orderMapper, never()).update(isNull(), any());
+    }
+
+    @Test
+    void dispatchMovesPendingDispatchOrderToPendingAccept() {
+        when(orderMapper.selectById(ORDER_ID)).thenReturn(pendingOrder());
+        when(sysUserService.selectUserById(HANDLER_ID)).thenReturn(activeReceiver());
+        when(orderMapper.update(isNull(), any())).thenReturn(1);
+
+        assertTrue(service.dispatch(ORDER_ID, dispatchDTO()));
+
+        verify(orderMapper).update(isNull(), any());
+        verify(logMapper).insert(any(WorkOrderLog.class));
+    }
+
+    private WorkOrder pendingOrder() {
+        WorkOrder order = new WorkOrder();
+        order.setOrderId(ORDER_ID);
+        order.setOrderStatus(WorkOrderStatusEnum.PENDING_DISPATCH.getCode());
+        return order;
+    }
+
+    private SysUser activeReceiver() {
+        SysUser user = new SysUser();
+        user.setUserId(HANDLER_ID);
+        user.setUserName("receiver");
+        user.setNickName("维修工");
+        user.setDeptId(7L);
+        user.setStatus("0");
+        user.setDelFlag("0");
+        return user;
+    }
+
+    private WorkOrderDispatchDTO dispatchDTO() {
+        WorkOrderDispatchDTO dto = new WorkOrderDispatchDTO();
+        dto.setHandlerId(HANDLER_ID);
+        dto.setRemarks("请尽快处理");
+        return dto;
+    }
+}

+ 2 - 1
pom.xml

@@ -33,7 +33,8 @@
         <maven.compiler.target>11</maven.compiler.target>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
         <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
-        <org.projectlombok.version>1.18.20</org.projectlombok.version>
+        <!-- lombok 版本:1.18.30 以下不支持 JDK 21 的注解处理,务必保持 >= 1.18.30 -->
+        <lombok.version>1.18.34</lombok.version>
         <spring-cloud.version>2021.0.3</spring-cloud.version>
         <spring-cloud-alibaba.version>2021.0.4.0</spring-cloud-alibaba.version>
         <mybatis-plus.version>3.5.7</mybatis-plus.version>