73 lines
4.5 KiB
Markdown
73 lines
4.5 KiB
Markdown
# 任务完成与取消双通道回调设计
|
||
|
||
## 目标
|
||
|
||
任务模块根据 YAML 配置选择 MQ 异步或 Feign 同步方式通知 LMS/WMS 执行完成、取消业务;业务处理成功后,两条链路都由 Task 服务统一把任务从回调待处理状态推进到最终完成或取消状态。
|
||
|
||
## 当前问题
|
||
|
||
- `TransportTaskOperationManager#handleFinished`、`handleCancelled` 固定发布 RocketMQ 消息,无法切换为同步 Feign。
|
||
- LMS/WMS 的 MQ Consumer 调用 `AbstractTask#doHandleFinish`、`doHandleCancel` 后没有回调 Task,任务停留在 `FINISHED_CALLBACK_PENDING` 或 `CANCEL_CALLBACK_PENDING`。
|
||
- `TransportTaskCallbackServiceImpl` 使用 `%03d` 格式化字符串状态码,回调处理存在运行时类型异常。
|
||
- `TaskCommonApi` 只暴露取货、进出等动作,没有完成和取消接口。
|
||
|
||
## 配置
|
||
|
||
在 Task 服务 YAML 中增加必填配置:
|
||
|
||
```yaml
|
||
nl:
|
||
task:
|
||
callback-type: mq
|
||
```
|
||
|
||
允许值为 `mq`、`feign`。使用枚举解析配置;配置缺失或值非法时启动失败,不做静默兜底。默认项目配置使用 `mq`,保持现有部署行为。
|
||
|
||
## 执行链路
|
||
|
||
### MQ 异步模式
|
||
|
||
1. Task 将任务置为对应的回调待处理状态,并把 `callbackStatus` 置为 `PENDING`。
|
||
2. 当前事务提交后发布完成或取消消息。
|
||
3. LMS/WMS Consumer 校验任务仍处于当前事件对应的待处理状态,然后路由到 `AbstractTask` 子类执行业务。
|
||
4. 业务成功后,Consumer 写入带有效期的 Redis 成功标识,再通过 `TransportTaskApi.receiveCallbackResult` 回调 `SUCCESS`;若成功回调失败,MQ 重试时只重试回调,不重复执行业务。
|
||
5. Task 的统一回调服务把任务更新为 `FINISHED` 或 `CANCELLED`,并把 `callbackStatus` 置为 `SUCCESS`。
|
||
6. 业务异常时,Consumer 尝试回调 `FAILED` 记录错误与重试次数,再重新抛出异常,让 MQ 执行重试。
|
||
|
||
### Feign 同步模式
|
||
|
||
1. Task 将任务置为对应的回调待处理状态,并把 `callbackStatus` 置为 `PENDING`。
|
||
2. Task 根据 `ownerService` 从 `TaskCommonApiFactory` 获取 LMS/WMS Feign Client。
|
||
3. 调用新增的 `doHandleFinished` 或 `doHandleCancelled`,业务服务通过 `TaskFactory` 路由到相同的 `AbstractTask` 子类。
|
||
4. Feign 返回成功后,Task 直接调用统一回调服务,以相同规则更新最终状态。
|
||
5. Feign 返回失败或抛出异常时不进入最终状态,异常沿现有任务操作链路上抛。
|
||
|
||
## 接口与数据
|
||
|
||
- 在 `TaskCommonApi` 增加 `doHandleFinished`、`doHandleCancelled`。
|
||
- 两个接口继续复用 `TaskStatusCallApiReqDTO`,携带任务标识、业务归属、业务标识、处理器编码和 payload。
|
||
- 同步与异步链路都复用 `TaskCallbackResultReqDTO` 和 `TransportTaskCallbackService` 完成最终状态收口。
|
||
- `eventId` 使用任务 ID 与事件类型组成的稳定值;Task 按当前 pending 状态校验 eventId,拒绝延迟或错序事件推进错误终态。
|
||
|
||
## 幂等与错误处理
|
||
|
||
- Task 已处于 `FINISHED` 或 `CANCELLED` 时忽略重复成功回调。
|
||
- 只有 `FINISHED_CALLBACK_PENDING` 能推进到 `FINISHED`,只有 `CANCEL_CALLBACK_PENDING` 能推进到 `CANCELLED`。
|
||
- MQ 重复消息在消费前查询 Task 状态;状态已不匹配时直接跳过。
|
||
- MQ 业务成功后使用七天 Redis 标识隔离业务执行与 Task 成功回调重试;成功回调完成后删除标识。
|
||
- Feign `CommonResult` 必须调用 `getCheckedData` 或检查 `isSuccess`,业务失败不允许误更新最终状态。
|
||
- MQ 回调失败不吞掉业务异常,保留 RocketMQ 重试能力。
|
||
- 任务状态分布式锁延迟到本地事务完成后释放,避免提交前窗口发生并发重复处理。
|
||
|
||
## 验证
|
||
|
||
- Spring 集成测试验证 execute starter 的完成、取消 Feign 入口能正确路由到任务处理器。
|
||
- 编译 execute starter、task-api、task-server、lms-server、wms-server,验证跨模块接口一致。
|
||
- 检查 MQ 成功回调和 Feign 成功返回最终都进入统一状态处理服务。
|
||
|
||
## 一致性边界
|
||
|
||
- MQ 和 Feign 均向 `AbstractTask` 透传稳定的 `eventId` 与 `eventType`,业务处理器应以该事件标识在业务数据库中实现幂等。
|
||
- Redis 成功标识可以避免“业务已成功、Task 成功回调失败”时重复执行业务,但不能覆盖业务事务提交后、Redis 标识写入前进程崩溃的极小窗口,因此业务数据库幂等仍是最终保障。
|
||
- Feign 调用与 Task 本地事务不构成分布式事务;远端成功后若本地事务回滚,重试仍依赖上述事件幂等。
|