# Task 服务独立后的任务业务回调设计方案 ## 1. 背景 旧版 LMS 中,任务、业务逻辑和 `xxxTask` 都在同一个服务内,`SCH_BASE_Task.handle_class` 保存 Java 类名,`TaskService` 根据该类名获取 Spring Bean 后调用 `cancel`、`forceFinish`、`updateTaskStatus`。 新版本中,任务模块独立为 `nl-module-task`,LMS、WMS 会调用 Task 服务创建任务;Task 服务负责下发外部系统,后续完成、取消也先进入 Task 服务。此时如果继续让 Task 服务通过 `handle_class` 调用 LMS/WMS 的业务类,会破坏微服务边界。 ## 2. 核心结论 建议改为: ```text Task 服务负责通用任务生命周期 LMS/WMS 负责各自业务完成和取消 Task 不引用 lms-server / wms-server 旧 handle_class 改为 handler_code Task 通过 RPC 或 MQ 通知业务服务回调 ``` 也就是说: - Task 服务:任务创建、状态机、调度、下发、外部反馈、日志、重试、幂等; - LMS 服务:LMS 入库、出库、分配、点位、库存等业务完成/取消; - WMS 服务:WMS 库存、库位、出入库单据等业务完成/取消; - 跨服务不要存 Java 类名,要存稳定业务编码。 推荐任务记录保存: ```text owner_service = LMS / WMS biz_type = TWO_IN / OUTBOUND / MOVE_STORE biz_id = 业务侧 ID handler_code = LMS_TWO_IN_TASK / WMS_OUTBOUND_TASK external_system = ACS / WCS / AGV ``` ## 3. 不推荐的设计 ### 3.1 不推荐把 LMS/WMS 的 `xxxTask` 放到 Task 服务 如果把 `TwoInTask`、`InTask`、`OutTask` 等业务类放到 Task 服务,Task 服务就会依赖 LMS/WMS 的业务表、Service、枚举和业务规则,最终变成新的业务单体。 ### 3.2 不推荐 Task 服务引用 LMS/WMS server 不要设计成: ```text task-server -> lms-server -> 注入 LmsTwoInTask ``` Task 服务最多依赖 `lms-api`、`wms-api` 中的 Feign/RPC 接口和 DTO,不应依赖 server 实现。 ### 3.3 不推荐继续使用 Java 类名作为 `handle_class` 旧值: ```text org.nl.b_lms.sch.tasks.TwoInTask ``` 新架构下 Task 服务本地没有这个类,即使通过依赖引入也会破坏边界。建议改为: ```text handler_code = LMS_TWO_IN_TASK owner_service = LMS ``` ## 4. 推荐架构 ```text LMS/WMS 创建任务 -> Task 服务保存任务 Task 服务下发 ACS/WCS/AGV 外部系统反馈完成/取消 -> Task 服务维护任务状态 -> Task 服务通知 LMS/WMS LMS/WMS 根据 handler_code 找本地 handler -> 执行业务完成/取消 -> Task 服务更新最终状态 ``` 服务职责: | 服务 | 职责 | | --- | --- | | Task | 任务主数据、状态机、外部系统下发、反馈接收、重试、幂等 | | LMS | LMS 业务完成/取消,例如入库确认、分配回滚、点位处理 | | WMS | WMS 业务完成/取消,例如库存、库位、单据处理 | ## 5. 任务表字段建议 | 字段 | 示例 | 说明 | | --- | --- | --- | | `id` | `10001` | Task 任务 ID | | `task_no` | `T202607130001` | 任务编号 | | `owner_service` | `LMS` | 业务归属服务 | | `biz_type` | `TWO_IN` | 业务任务类型 | | `biz_id` | `1000001` | 业务侧 ID | | `handler_code` | `LMS_TWO_IN_TASK` | 业务处理器编码 | | `external_system` | `ACS` | 外部系统 | | `external_task_type` | `7` | 外部任务类型 | | `status` | `ISSUED` | Task 状态 | | `callback_status` | `PENDING/SUCCESS/FAILED` | 业务回调状态 | | `request_payload` | JSON | 创建扩展参数 | | `dispatch_payload` | JSON | 下发参数 | | `result_payload` | JSON | 外部反馈参数 | ## 6. 推荐流程 ### 6.1 创建任务 ```text LMS/WMS -> Task.createTask -> Task 保存任务 -> 返回 task_id/task_no ``` 创建请求示例: ```json { "ownerService": "LMS", "bizType": "TWO_IN", "bizId": "1000001", "handlerCode": "LMS_TWO_IN_TASK", "externalSystem": "ACS", "externalTaskType": "7", "startPointCode": "IN_001", "endPointCode": "A01_01_01", "vehicleCode": "P001", "priority": 1, "requestPayload": {} } ``` ### 6.2 下发任务 ```text Task 定时任务/手动触发 -> 查询可下发任务 -> 组装外部系统 DTO -> 调用 ACS/WCS/AGV -> 成功后 status = ISSUED ``` 如果下发参数依赖业务数据,优先由 LMS/WMS 创建任务时传给 Task,减少 Task 反查业务服务。 ### 6.3 完成任务 ```text 外部系统反馈完成 -> Task 幂等校验 -> status = FINISHED_PENDING_CALLBACK -> 通知 LMS/WMS 执行业务完成 -> 业务成功后 status = FINISHED ``` 不要外部一反馈完成就直接设为最终 `FINISHED`,因为业务确认可能失败。 ### 6.4 取消任务 ```text 用户取消 -> Task 判断是否允许取消 -> 如已下发,先取消外部任务 -> status = CANCEL_PENDING_CALLBACK -> 通知 LMS/WMS 执行业务取消回滚 -> 业务成功后 status = CANCELLED ``` ## 7. 业务回调方式 ### 7.1 第一阶段推荐:RPC 同步回调 Task 根据 `owner_service` 调用业务服务接口: ```text POST /rpc-api/lms/task-callback/handle POST /rpc-api/wms/task-callback/handle ``` 请求示例: ```json { "taskId": 10001, "taskNo": "T202607130001", "eventType": "FINISH", "bizType": "TWO_IN", "bizId": "1000001", "handlerCode": "LMS_TWO_IN_TASK", "payload": {} } ``` 优点:实现简单、链路清晰、Task 能立即知道业务成功或失败。缺点是业务服务不可用时需要重试。 ### 7.2 第二阶段演进:MQ 事件回调 ```text Task 发布 TaskFinishedEvent / TaskCancelledEvent -> LMS/WMS 消费 -> 执行业务逻辑 -> 回调 Task 确认处理结果 ``` 优点是解耦更彻底,缺点是最终一致性和排查复杂度更高。 ## 8. LMS/WMS 内部 handler 设计 业务服务内部定义统一接口: ```java public interface TaskBizCallbackHandler { String getHandlerCode(); void onTaskFinished(TaskCallbackRequest request); void onTaskCancelled(TaskCallbackRequest request); } ``` LMS 示例: ```java @Component public class LmsTwoInTaskCallbackHandler implements TaskBizCallbackHandler { @Override public String getHandlerCode() { return "LMS_TWO_IN_TASK"; } @Override public void onTaskFinished(TaskCallbackRequest request) { // 入库确认、分配明细完成、库存/点位处理 } @Override public void onTaskCancelled(TaskCallbackRequest request) { // 入库取消、分配回滚、点位释放 } } ``` 业务服务内用 `handler_code` 分发到对应 handler。核心原则是:**谁拥有业务数据,谁实现业务 handler**。 ## 9. Task 服务内部抽象 旧版 `AbstractAcsTask` 职责太多,新架构建议拆分: ### Task 服务内:外部系统下发策略 ```java public interface ExternalTaskDispatcher { String getExternalSystem(); DispatchResult dispatch(TaskDO task); CancelExternalResult cancelExternal(TaskDO task); } ``` 实现类例如:`AcsTaskDispatcher`、`WcsTaskDispatcher`、`AgvTaskDispatcher`。 ### LMS/WMS 内:业务回调策略 ```java public interface TaskBizCallbackHandler { String getHandlerCode(); void onTaskFinished(TaskCallbackRequest request); void onTaskCancelled(TaskCallbackRequest request); } ``` 这样旧版一个大 `AbstractAcsTask` 被拆成两部分: ```text Task 服务:负责外部任务下发 业务服务:负责完成/取消后的业务处理 ``` ## 10. 状态机建议 | 状态 | 含义 | | --- | --- | | `CREATED` | 已创建 | | `READY` | 可下发 | | `ISSUING` | 下发中 | | `ISSUED` | 已下发 | | `EXECUTING` | 执行中 | | `FINISHED_PENDING_CALLBACK` | 外部已完成,等待业务完成 | | `FINISHED` | 最终完成 | | `CANCEL_PENDING_EXTERNAL` | 等待外部系统取消 | | `CANCEL_PENDING_CALLBACK` | 等待业务取消回滚 | | `CANCELLED` | 最终取消 | | `FAILED` | 失败 | 中间状态用于区分:外部完成不等于业务完成,外部取消不等于业务回滚完成。 ## 11. 幂等和补偿 - Task 接收外部反馈时,按 `task_id + external_event_id + event_type` 幂等; - LMS/WMS 业务回调按 `task_id + event_type` 幂等; - 业务服务建议记录回调日志; - Task 服务增加定时补偿,扫描 `FINISHED_PENDING_CALLBACK`、`CANCEL_PENDING_CALLBACK`、`ISSUING` 超时等任务。 ## 12. 代码结构建议 Task API: ```text nl-module-task-api api/TaskApi.java dto/TaskCreateReqDTO.java dto/TaskCallbackReqDTO.java dto/TaskCancelReqDTO.java enums/TaskStatusEnum.java ``` Task Server: ```text nl-module-task-server service/TaskService.java service/TaskDispatchService.java service/TaskBizCallbackNotifyService.java dispatcher/ExternalTaskDispatcher.java dispatcher/AcsTaskDispatcher.java job/TaskScheduleJob.java job/TaskCallbackRetryJob.java ``` LMS Server: ```text nl-module-lms-server api/TaskCallbackApiImpl.java taskcallback/TaskBizCallbackHandler.java taskcallback/TaskBizCallbackDispatcher.java taskcallback/handler/LmsTwoInTaskCallbackHandler.java ``` ## 13. 对问题的直接回答 ### 抽象类子类放 Task 还是 LMS? 如果子类包含 LMS 入库、出库、分配、库存、点位等业务逻辑,就放 LMS。如果只负责外部系统下发,不访问业务数据,可以放 Task。 更推荐拆成: ```text Task 服务:ExternalTaskDispatcher LMS/WMS:TaskBizCallbackHandler ``` ### handler 放 LMS,Task 是否要引用 LMS? Task 不引用 `lms-server`。可以依赖 `lms-api` 做 RPC,或发布 MQ 事件让 LMS 消费。 ### 旧 handle_class 怎么办? 不再存 Java 类名,改为: ```text owner_service = LMS handler_code = LMS_TWO_IN_TASK ``` ## 14. 总结 Task 服务独立后,`handle_class` 不应该再表示 Java 类,也不应该让 Task 服务持有 LMS/WMS 的业务类。正确拆法是:Task 服务负责通用任务生命周期和外部系统交互;LMS/WMS 负责各自业务完成和取消;Task 通过 `owner_service + handler_code` 定位业务回调目标;业务服务内部再通过 `handler_code` 分发到本地 handler。