# 取消原因同步到DispatchOrd功能说明 ## 功能概述 在转运任务取消时,自动将取消原因同步到旧系统的DispatchOrd表中,记录到`DispatchOrdCancelReason`(取消原因ID)和`DispatchOrdCancelReasonTXT`(取消原因文本)字段。 ## 实现方式 ### 1. 核心流程 当任务状态变更为"已取消"时: 1. 监听器(`DispatchOrdRunningListener`)检测到取消状态 2. 从`sys_task_emergency`表获取取消原因(cancelReason字段,存储数据字典value) 3. 将取消原因value转换为Integer作为DispatchOrdCancelReason 4. 从数据字典`task_cancel_reason`查询对应的中文标签作为DispatchOrdCancelReasonTXT 5. 执行UPDATE语句更新DispatchOrd表 ### 2. 数据映射关系 | 新系统字段 | 新系统表 | 旧系统字段 | 旧系统表 | 说明 | |-----------|---------|-----------|---------|------| | cancel_reason | sys_task_emergency | DispatchOrdCancelReason | DispatchOrd | 取消原因ID(数字) | | 字典label | sys_dict_data | DispatchOrdCancelReasonTXT | DispatchOrd | 取消原因文本(中文) | ### 3. 取消原因字典 数据字典类型:`task_cancel_reason` 包含26个取消原因选项: 1. 价格不接受 2. 时间紧急 3. 出车速度慢 4. 选择其他车 5. 病人没有生命体征 6. 医生护士均不足 7. 病人情况有变 8. 其他 9. 第三方取消 10. 测试订单 11. 传染性疾病 12. 家属挂机/拒接/不接电话 13. 护士不足 14. 医生不足 15. 设备不足(呼吸机) 16. 家属没联系好床位 17. 移交分支机构执行 18. 移交办事处(湛江) 19. 移交办事处(茂名) 20. 家属不肯透露信息 21. 所在医院/目的地医院派车 22. 自驾车接送患者 23. 选择其他机构车辆 24. 外联通知取消 25. 外联无反馈 26. 家属无原因直接告知取消 ## 代码修改清单 ### 1. DispatchOrdMapper.java **文件路径**: `ruoyi-system/src/main/java/com/ruoyi/system/mapper/DispatchOrdMapper.java` **新增方法**: ```java /** * 更新调度单取消原因 * * @param dispatchOrdID 调度单ID * @param cancelReasonId 取消原因ID * @param cancelReasonText 取消原因文本 * @return 影响行数 */ public int updateDispatchOrdCancelReason(@Param("dispatchOrdID") Long dispatchOrdID, @Param("cancelReasonId") Integer cancelReasonId, @Param("cancelReasonText") String cancelReasonText); ``` ### 2. DispatchOrdMapper.xml **文件路径**: `ruoyi-system/src/main/resources/mapper/system/DispatchOrdMapper.xml` **新增SQL**: ```xml update DispatchOrd set DispatchOrdCancelReason = #{cancelReasonId}, DispatchOrdCancelReasonTXT = #{cancelReasonText} where DispatchOrdID = #{dispatchOrdID} ``` ### 3. DispatchOrdRunningListener.java **文件路径**: `ruoyi-system/src/main/java/com/ruoyi/system/listener/DispatchOrdRunningListener.java` **新增依赖注入**: ```java @Autowired private DispatchOrdMapper dispatchOrdMapper; @Autowired private SysDictDataMapper sysDictDataMapper; ``` **新增import**: ```java import com.ruoyi.system.mapper.DispatchOrdMapper; import com.ruoyi.system.mapper.SysDictDataMapper; ``` **修改handleTaskStatusChangedEvent方法**: 在状态转换后增加取消原因同步逻辑(第122-126行): ```java // 如果是取消状态,同步取消原因到DispatchOrd表 if (TaskStatus.CANCELLED.equals(newTaskStatus) && emergency.getCancelReason() != null) { syncCancelReasonToDispatchOrd(emergency.getLegacyDispatchOrdId(), emergency.getCancelReason()); } ``` **新增方法**: ```java /** * 同步取消原因到DispatchOrd表 * * @param dispatchOrdId 调度单ID * @param cancelReason 取消原因(字典value) */ private void syncCancelReasonToDispatchOrd(Long dispatchOrdId, String cancelReason) { try { if (cancelReason == null || cancelReason.isEmpty()) { log.debug("取消原因为空,跳过同步,DispatchOrdID: {}", dispatchOrdId); return; } // 将cancelReason(字典value)转换为Integer Integer cancelReasonId = null; try { cancelReasonId = Integer.valueOf(cancelReason); } catch (NumberFormatException e) { log.error("取消原因格式错误,无法转换为数字,cancelReason: {}, DispatchOrdID: {}", cancelReason, dispatchOrdId); return; } // 从数据字典中查询取消原因文本 String cancelReasonText = sysDictDataMapper.selectDictLabel("task_cancel_reason", cancelReason); if (cancelReasonText == null || cancelReasonText.isEmpty()) { log.warn("未找到取消原因对应的文本,cancelReason: {}, DispatchOrdID: {}", cancelReason, dispatchOrdId); cancelReasonText = cancelReason; // 使用原值作为预备 } log.info("开始同步取消原因到DispatchOrd,DispatchOrdID: {}, 取消原因ID: {}, 取消原因文本: {}", dispatchOrdId, cancelReasonId, cancelReasonText); // 调用Mapper更新DispatchOrd表 int rows = dispatchOrdMapper.updateDispatchOrdCancelReason(dispatchOrdId, cancelReasonId, cancelReasonText); if (rows > 0) { log.info("成功同步取消原因到DispatchOrd,DispatchOrdID: {}, 取消原因: {} ({})", dispatchOrdId, cancelReasonText, cancelReasonId); } else { log.warn("同步取消原因失败,未找到对应的调度单,DispatchOrdID: {}", dispatchOrdId); } } catch (Exception e) { log.error("同步取消原因到DispatchOrd异常,DispatchOrdID: {}, 取消原因: {}", dispatchOrdId, cancelReason, e); // 不抛出异常,避免影响主流程 } } ``` ## 执行SQL示例 当取消原因为"价格不接受"(value=1)时,实际执行的SQL: ```sql UPDATE DispatchOrd SET DispatchOrdCancelReason = 1, DispatchOrdCancelReasonTXT = '价格不接受' WHERE DispatchOrdID = 12345 ``` ## 同步条件 取消原因同步需要满足以下条件: 1. ✅ 旧系统同步已启用 2. ✅ 任务类型为急救转运任务(EMERGENCY_TRANSFER) 3. ✅ 任务已同步到旧系统(有legacyDispatchOrdId) 4. ✅ 任务状态变更为"已取消"(CANCELLED) 5. ✅ 任务记录了取消原因(cancelReason不为空) ## 执行时机 **异步执行**:任务状态变更为"已取消"时,通过事件监听器异步处理,不影响主业务流程。 ## 日志说明 ### 正常日志 ``` INFO - 收到任务状态变更事件,准备同步到DispatchOrd_Running,任务ID:1001,旧状态:PENDING,新状态:CANCELLED INFO - 开始同步取消原因到DispatchOrd,DispatchOrdID: 12345, 取消原因ID: 1, 取消原因文本: 价格不接受 INFO - 成功同步取消原因到DispatchOrd,DispatchOrdID: 12345, 取消原因: 价格不接受 (1) ``` ### 跳过日志 ``` DEBUG - 取消原因为空,跳过同步,DispatchOrdID: 12345 ``` ### 异常日志 ``` WARN - 未找到取消原因对应的文本,cancelReason: 99, DispatchOrdID: 12345 ERROR - 取消原因格式错误,无法转换为数字,cancelReason: abc, DispatchOrdID: 12345 WARN - 同步取消原因失败,未找到对应的调度单,DispatchOrdID: 12345 ERROR - 同步取消原因到DispatchOrd异常,DispatchOrdID: 12345, 取消原因: 1 ``` ## 验证方法 ### 1. 数据库验证 查询DispatchOrd表的取消原因字段: ```sql SELECT DispatchOrdID, DispatchOrdState, DispatchOrdCancelReason, DispatchOrdCancelReasonTXT FROM DispatchOrd WHERE DispatchOrdID = 12345 ``` ### 2. 测试步骤 1. 在新系统创建转运任务并同步到旧系统 2. 在任务详情页点击"取消"按钮 3. 选择取消原因(如"价格不接受") 4. 确认取消 5. 检查应用日志,确认同步成功 6. 查询DispatchOrd表,验证取消原因字段已更新 ### 3. 预期结果 - DispatchOrdCancelReason = 1 - DispatchOrdCancelReasonTXT = "价格不接受" ## 特性说明 1. **异步处理**:不阻塞主业务流程,提高系统响应速度 2. **容错机制**:同步失败不影响任务取消操作 3. **完整日志**:记录详细的同步过程和结果 4. **数据转换**:自动将字典value转换为ID和文本 5. **事件驱动**:利用Spring事件机制实现解耦 6. **自动同步**:无需手动触发,状态变更时自动执行 ## 注意事项 1. **仅限取消状态**:只有任务状态变更为CANCELLED时才会同步取消原因 2. **依赖调度单同步**:必须先同步到旧系统才能更新取消原因 3. **异常不中断**:同步异常只记录日志,不抛出异常,不影响主流程 4. **数据库字段**:确保DispatchOrd表有DispatchOrdCancelReason和DispatchOrdCancelReasonTXT字段 5. **字典依赖**:依赖数据字典task_cancel_reason正确配置 ## 相关文档 - [取消原因功能实现说明.md](./取消原因功能实现说明.md) - [转运任务状态变更记录同步功能说明.md](./prd/转运任务状态变更记录同步功能说明.md) - [旧系统同步-快速开始.md](./prd/旧系统同步-快速开始.md) ## 更新日志 - 2025-12-26: 实现取消原因同步到DispatchOrd功能