Files
huachuang/doc/task-service-business-callback-design.md

9.9 KiB
Raw Permalink Blame History

Task 服务独立后的任务业务回调设计方案

1. 背景

旧版 LMS 中,任务、业务逻辑和 xxxTask 都在同一个服务内,SCH_BASE_Task.handle_class 保存 Java 类名,TaskService 根据该类名获取 Spring Bean 后调用 cancelforceFinishupdateTaskStatus

新版本中,任务模块独立为 nl-module-taskLMS、WMS 会调用 Task 服务创建任务Task 服务负责下发外部系统,后续完成、取消也先进入 Task 服务。此时如果继续让 Task 服务通过 handle_class 调用 LMS/WMS 的业务类,会破坏微服务边界。

2. 核心结论

建议改为:

Task 服务负责通用任务生命周期
LMS/WMS 负责各自业务完成和取消
Task 不引用 lms-server / wms-server
旧 handle_class 改为 handler_code
Task 通过 RPC 或 MQ 通知业务服务回调

也就是说:

  • Task 服务:任务创建、状态机、调度、下发、外部反馈、日志、重试、幂等;
  • LMS 服务LMS 入库、出库、分配、点位、库存等业务完成/取消;
  • WMS 服务WMS 库存、库位、出入库单据等业务完成/取消;
  • 跨服务不要存 Java 类名,要存稳定业务编码。

推荐任务记录保存:

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 服务

如果把 TwoInTaskInTaskOutTask 等业务类放到 Task 服务Task 服务就会依赖 LMS/WMS 的业务表、Service、枚举和业务规则最终变成新的业务单体。

3.2 不推荐 Task 服务引用 LMS/WMS server

不要设计成:

task-server -> lms-server -> 注入 LmsTwoInTask

Task 服务最多依赖 lms-apiwms-api 中的 Feign/RPC 接口和 DTO不应依赖 server 实现。

3.3 不推荐继续使用 Java 类名作为 handle_class

旧值:

org.nl.b_lms.sch.tasks.TwoInTask

新架构下 Task 服务本地没有这个类,即使通过依赖引入也会破坏边界。建议改为:

handler_code = LMS_TWO_IN_TASK
owner_service = LMS

4. 推荐架构

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 创建任务

LMS/WMS -> Task.createTask -> Task 保存任务 -> 返回 task_id/task_no

创建请求示例:

{
  "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 下发任务

Task 定时任务/手动触发
  -> 查询可下发任务
  -> 组装外部系统 DTO
  -> 调用 ACS/WCS/AGV
  -> 成功后 status = ISSUED

如果下发参数依赖业务数据,优先由 LMS/WMS 创建任务时传给 Task减少 Task 反查业务服务。

6.3 完成任务

外部系统反馈完成
  -> Task 幂等校验
  -> status = FINISHED_PENDING_CALLBACK
  -> 通知 LMS/WMS 执行业务完成
  -> 业务成功后 status = FINISHED

不要外部一反馈完成就直接设为最终 FINISHED,因为业务确认可能失败。

6.4 取消任务

用户取消
  -> Task 判断是否允许取消
  -> 如已下发,先取消外部任务
  -> status = CANCEL_PENDING_CALLBACK
  -> 通知 LMS/WMS 执行业务取消回滚
  -> 业务成功后 status = CANCELLED

7. 业务回调方式

7.1 第一阶段推荐RPC 同步回调

Task 根据 owner_service 调用业务服务接口:

POST /rpc-api/lms/task-callback/handle
POST /rpc-api/wms/task-callback/handle

请求示例:

{
  "taskId": 10001,
  "taskNo": "T202607130001",
  "eventType": "FINISH",
  "bizType": "TWO_IN",
  "bizId": "1000001",
  "handlerCode": "LMS_TWO_IN_TASK",
  "payload": {}
}

优点实现简单、链路清晰、Task 能立即知道业务成功或失败。缺点是业务服务不可用时需要重试。

7.2 第二阶段演进MQ 事件回调

Task 发布 TaskFinishedEvent / TaskCancelledEvent
  -> LMS/WMS 消费
  -> 执行业务逻辑
  -> 回调 Task 确认处理结果

优点是解耦更彻底,缺点是最终一致性和排查复杂度更高。

8. LMS/WMS 内部 handler 设计

业务服务内部定义统一接口:

public interface TaskBizCallbackHandler {
    String getHandlerCode();
    void onTaskFinished(TaskCallbackRequest request);
    void onTaskCancelled(TaskCallbackRequest request);
}

LMS 示例:

@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 服务内:外部系统下发策略

public interface ExternalTaskDispatcher {
    String getExternalSystem();
    DispatchResult dispatch(TaskDO task);
    CancelExternalResult cancelExternal(TaskDO task);
}

实现类例如:AcsTaskDispatcherWcsTaskDispatcherAgvTaskDispatcher

LMS/WMS 内:业务回调策略

public interface TaskBizCallbackHandler {
    String getHandlerCode();
    void onTaskFinished(TaskCallbackRequest request);
    void onTaskCancelled(TaskCallbackRequest request);
}

这样旧版一个大 AbstractAcsTask 被拆成两部分:

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_CALLBACKCANCEL_PENDING_CALLBACKISSUING 超时等任务。

12. 代码结构建议

Task API

nl-module-task-api
  api/TaskApi.java
  dto/TaskCreateReqDTO.java
  dto/TaskCallbackReqDTO.java
  dto/TaskCancelReqDTO.java
  enums/TaskStatusEnum.java

Task Server

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

nl-module-lms-server
  api/TaskCallbackApiImpl.java
  taskcallback/TaskBizCallbackHandler.java
  taskcallback/TaskBizCallbackDispatcher.java
  taskcallback/handler/LmsTwoInTaskCallbackHandler.java

13. 对问题的直接回答

抽象类子类放 Task 还是 LMS

如果子类包含 LMS 入库、出库、分配、库存、点位等业务逻辑,就放 LMS。如果只负责外部系统下发不访问业务数据可以放 Task。

更推荐拆成:

Task 服务ExternalTaskDispatcher
LMS/WMSTaskBizCallbackHandler

handler 放 LMSTask 是否要引用 LMS

Task 不引用 lms-server。可以依赖 lms-api 做 RPC或发布 MQ 事件让 LMS 消费。

旧 handle_class 怎么办?

不再存 Java 类名,改为:

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。