15 KiB
Task 服务创建任务与外部系统下发参数转换设计方案
1. 问题背景
旧版 LMS 中,类似 StockAreaCallTubeTask 这样的任务子类会实现:
public List<AcsTaskDto> addTask()
旧版 addTask 的核心职责是:
查询 SCH_BASE_Task
-> 读取任务字段、request_param、point_code、vehicle_code 等
-> 组装 AcsTaskDto
-> 下发 ACS
也就是说,在旧架构中:
业务任务创建、任务表保存、外部系统 DTO 组装、ACS 下发
都在 LMS 内的具体 xxxTask 子类中完成。
新架构中,Task 服务独立出来:
LMS/WMS
-> 调用 Task 服务创建任务
Task 服务
-> 保存任务
-> 下发外部系统
此时会遇到一个关键问题:
LMS/WMS 创建任务时,传给 Task 服务的是业务任务信息,不一定已经是
AcsTaskDto。但 Task 服务下发 ACS 时需要AcsTaskDto或类似外部系统 DTO。这个转换逻辑应该放在哪里?
如果让 LMS/WMS 直接传 AcsTaskDto,会有两个问题:
AcsTaskDto是外部系统下发模型,不适合作为业务创建任务模型;- Task 服务还需要保存标准任务表,不能只保存一个外部 DTO。
因此需要重新设计“创建任务模型”和“下发模型”的边界。
2. 核心结论
建议将旧版 addTask 拆成两部分:
LMS/WMS:负责构建业务任务创建请求 TaskCreateReqDTO
Task 服务:负责保存标准任务数据,并在下发前转换成外部系统 DTO
也就是说:
业务服务不要直接传 AcsTaskDto
Task 服务不要依赖 LMS/WMS 业务类
双方通过标准 TaskCreateReqDTO 交互
Task 服务内部通过 ExternalTaskPayloadBuilder 构建 AcsTaskDto
推荐架构:
LMS/WMS
-> TaskCreateReqDTO 标准创建请求
Task 服务
-> 保存 task_task 标准字段
-> 保存 request_payload 扩展字段
-> 根据 external_system + external_task_type + dispatch_template_code 选择构建器
-> 转换为 AcsTaskDto / WcsTaskDto / StackerTaskDto
-> 下发外部系统
3. 新旧职责对照
| 职责 | 旧版 | 新版 |
|---|---|---|
| 业务校验 | xxxTask#createTask |
LMS/WMS 业务服务 |
| 任务保存 | LMS SCH_BASE_Task |
Task 服务 task_task |
| 业务回调定位 | handle_class |
owner_service + handler_code |
| 外部 DTO 组装 | xxxTask#addTask |
Task 服务 ExternalTaskPayloadBuilder |
| 外部系统下发 | AbstractAcsTask#notifyAcs |
Task 服务 ExternalTaskDispatcher |
| 完成/取消业务逻辑 | xxxTask#updateTaskStatus |
LMS/WMS TaskBizCallbackHandler |
4. 为什么不建议 LMS/WMS 直接传 AcsTaskDto
不建议让 LMS/WMS 创建任务时直接传 AcsTaskDto,原因如下:
4.1 业务创建模型和外部下发模型不是一回事
业务创建任务时关心的是:
我要搬什么
从哪里搬
搬到哪里
归属哪个业务
完成后回调哪个 handler
业务扩展参数是什么
外部系统下发时关心的是:
ACS task_type
start_device_code
next_device_code
vehicle_code
agv_system_type
route_plan_code
interaction_json
两者不应该强耦合。
4.2 Task 服务需要标准化保存任务
Task 服务需要保存:
task_id
task_no
owner_service
biz_type
biz_id
handler_code
external_system
status
start_point_code
end_point_code
vehicle_code
request_payload
如果只收到 AcsTaskDto,Task 服务会丢失业务归属、回调 handler、业务 ID 等关键字段。
4.3 外部系统 DTO 可能变化
未来可能不止 ACS:
ACS
WCS
AGV
STACKER
PLC
如果业务服务直接构建外部 DTO,会导致业务服务感知所有外部系统协议,不利于统一下发和协议升级。
5. 推荐数据模型
5.1 TaskCreateReqDTO
LMS/WMS 调用 Task 服务创建任务时,传标准创建请求。
@Data
public class TaskCreateReqDTO {
private String ownerService;
private String bizType;
private String bizId;
private String handlerCode;
private String externalSystem;
private String externalTaskType;
private String dispatchTemplateCode;
private String startPointCode;
private String endPointCode;
private String startPointCode2;
private String endPointCode2;
private String vehicleCode;
private String vehicleCode2;
private Integer priority;
private String productArea;
private Map<String, Object> requestPayload;
}
字段说明:
| 字段 | 说明 |
|---|---|
ownerService |
LMS/WMS,决定完成取消回调发给谁 |
bizType |
业务类型,例如 STOCK_AREA_CALL_TUBE |
bizId |
业务侧 ID |
handlerCode |
业务回调 handler 编码 |
externalSystem |
外部系统,例如 ACS/WCS/STACKER |
externalTaskType |
外部系统任务类型 |
dispatchTemplateCode |
下发参数构建模板编码 |
startPointCode |
起点 |
endPointCode |
终点 |
vehicleCode |
载具、架子、托盘等 |
requestPayload |
扩展参数 |
5.2 TaskDO 保存字段
Task 服务保存标准字段和扩展 JSON:
id
task_no
owner_service
biz_type
biz_id
handler_code
external_system
external_task_type
dispatch_template_code
start_point_code
end_point_code
start_point_code2
end_point_code2
vehicle_code
vehicle_code2
priority
product_area
status
request_payload
dispatch_payload
result_payload
6. 下发参数转换设计
6.1 定义统一构建器接口
Task 服务内部定义:
public interface ExternalTaskPayloadBuilder<T> {
/**
* 外部系统,例如 ACS/WCS/STACKER
*/
String getExternalSystem();
/**
* 下发模板编码,例如 STOCK_AREA_CALL_TUBE
*/
String getDispatchTemplateCode();
/**
* 根据 TaskDO 构建外部系统 DTO
*/
T build(TaskDO task);
}
6.2 ACS 纸管搬运构建器
旧版 StockAreaCallTubeTask#addTask 逻辑迁移到 Task 服务的构建器中。
@Component
public class AcsStockAreaCallTubePayloadBuilder implements ExternalTaskPayloadBuilder<AcsTaskDto> {
@Override
public String getExternalSystem() {
return "ACS";
}
@Override
public String getDispatchTemplateCode() {
return "STOCK_AREA_CALL_TUBE";
}
@Override
public AcsTaskDto build(TaskDO task) {
Map<String, Object> payload = task.getRequestPayloadMap();
return AcsTaskDto.builder()
.extTaskId(String.valueOf(task.getId()))
.taskCode(task.getTaskNo())
.taskType(task.getExternalTaskType())
.startDeviceCode(task.getStartPointCode())
.nextDeviceCode(task.getEndPointCode())
.startDeviceCode2(task.getStartPointCode2())
.nextDeviceCode2(task.getEndPointCode2())
.vehicleCode(task.getVehicleCode())
.interactionJson(payload)
.agvSystemType("2")
.priority(String.valueOf(task.getPriority()))
.productArea(task.getProductArea())
.build();
}
}
对应旧版逻辑:
StockAreaCallTubeTask#addTask
-> 查询任务
-> 构建 AcsTaskDto
新版变为:
AcsStockAreaCallTubePayloadBuilder#build
-> 根据 TaskDO 构建 AcsTaskDto
6.3 通用 ACS 构建器
如果很多 ACS 任务字段差不多,也可以提供一个通用构建器:
@Component
public class AcsDefaultPayloadBuilder implements ExternalTaskPayloadBuilder<AcsTaskDto> {
@Override
public String getExternalSystem() {
return "ACS";
}
@Override
public String getDispatchTemplateCode() {
return "DEFAULT";
}
@Override
public AcsTaskDto build(TaskDO task) {
return AcsTaskDto.builder()
.extTaskId(String.valueOf(task.getId()))
.taskCode(task.getTaskNo())
.taskType(task.getExternalTaskType())
.startDeviceCode(task.getStartPointCode())
.nextDeviceCode(task.getEndPointCode())
.vehicleCode(task.getVehicleCode())
.priority(String.valueOf(task.getPriority()))
.productArea(task.getProductArea())
.interactionJson(task.getRequestPayloadMap())
.build();
}
}
特殊任务再单独实现专用 Builder。
7. Builder 注册表
Task 服务启动时收集所有 Builder。
@Component
public class ExternalTaskPayloadBuilderRegistry {
private final Map<String, ExternalTaskPayloadBuilder<?>> builderMap = new HashMap<>();
public ExternalTaskPayloadBuilderRegistry(List<ExternalTaskPayloadBuilder<?>> builders) {
for (ExternalTaskPayloadBuilder<?> builder : builders) {
String key = buildKey(builder.getExternalSystem(), builder.getDispatchTemplateCode());
if (builderMap.containsKey(key)) {
throw new IllegalStateException("重复的下发参数构建器:" + key);
}
builderMap.put(key, builder);
}
}
public ExternalTaskPayloadBuilder<?> getRequiredBuilder(String externalSystem, String dispatchTemplateCode) {
String key = buildKey(externalSystem, dispatchTemplateCode);
ExternalTaskPayloadBuilder<?> builder = builderMap.get(key);
if (builder == null) {
throw new IllegalArgumentException("未找到下发参数构建器:" + key);
}
return builder;
}
private String buildKey(String externalSystem, String dispatchTemplateCode) {
return externalSystem + ":" + dispatchTemplateCode;
}
}
例如:
ACS:DEFAULT -> AcsDefaultPayloadBuilder
ACS:STOCK_AREA_CALL_TUBE -> AcsStockAreaCallTubePayloadBuilder
STACKER:INBOUND -> StackerInboundPayloadBuilder
8. 外部系统下发器设计
Builder 只负责构建 DTO,下发器负责调用外部系统。
public interface ExternalTaskDispatcher {
String getExternalSystem();
DispatchResult dispatch(TaskDO task, Object payload);
CancelExternalResult cancelExternal(TaskDO task);
}
ACS 下发器示例:
@Component
public class AcsTaskDispatcher implements ExternalTaskDispatcher {
@Override
public String getExternalSystem() {
return "ACS";
}
@Override
public DispatchResult dispatch(TaskDO task, Object payload) {
AcsTaskDto acsTask = (AcsTaskDto) payload;
// 调用 ACS HTTP 接口
// acsClient.createTask(Collections.singletonList(acsTask));
return DispatchResult.success();
}
@Override
public CancelExternalResult cancelExternal(TaskDO task) {
// 调用 ACS 取消接口
return CancelExternalResult.success();
}
}
9. Task 下发流程
Task 服务下发任务时:
1. 查询 READY 状态任务
2. 根据 external_system + dispatch_template_code 找 Builder
3. Builder 根据 TaskDO 构建 AcsTaskDto/WcsTaskDto
4. 保存 dispatch_payload
5. 根据 external_system 找 Dispatcher
6. Dispatcher 调用外部系统
7. 成功后状态改为 ISSUED
伪代码:
public void dispatchTask(Long taskId) {
TaskDO task = taskMapper.selectById(taskId);
ExternalTaskPayloadBuilder<?> builder = builderRegistry.getRequiredBuilder(
task.getExternalSystem(),
task.getDispatchTemplateCode()
);
Object externalPayload = builder.build(task);
task.setDispatchPayload(JsonUtils.toJsonString(externalPayload));
task.setStatus(TaskStatus.ISSUING);
taskMapper.updateById(task);
ExternalTaskDispatcher dispatcher = dispatcherRegistry.getRequiredDispatcher(task.getExternalSystem());
DispatchResult result = dispatcher.dispatch(task, externalPayload);
if (result.isSuccess()) {
task.setStatus(TaskStatus.ISSUED);
} else {
task.setStatus(TaskStatus.FAILED);
task.setErrorMsg(result.getMessage());
}
taskMapper.updateById(task);
}
10. StockAreaCallTubeTask 新版完整链路
10.1 LMS 创建任务
LMS 不传 AcsTaskDto,而是传标准 TaskCreateReqDTO:
{
"ownerService": "LMS",
"bizType": "STOCK_AREA_CALL_TUBE",
"bizId": "LMS-BIZ-10001",
"handlerCode": "LMS_STOCK_AREA_CALL_TUBE",
"externalSystem": "ACS",
"externalTaskType": "3",
"dispatchTemplateCode": "STOCK_AREA_CALL_TUBE",
"startPointCode": "STOCK_A01",
"endPointCode": "ROBOT_STOCK_A01",
"startPointCode2": "",
"endPointCode2": "",
"vehicleCode": "TUBE_CAR_001",
"priority": 1,
"productArea": "BLK",
"requestPayload": {
"to_material": "M001,M002"
}
}
10.2 Task 保存任务
Task 保存为标准任务记录:
owner_service = LMS
handler_code = LMS_STOCK_AREA_CALL_TUBE
external_system = ACS
dispatch_template_code = STOCK_AREA_CALL_TUBE
request_payload = {to_material: ...}
10.3 Task 下发 ACS
Task 根据:
external_system = ACS
dispatch_template_code = STOCK_AREA_CALL_TUBE
找到:
AcsStockAreaCallTubePayloadBuilder
构建:
AcsTaskDto
再由:
AcsTaskDispatcher
下发 ACS。
10.4 ACS 完成后业务回调
ACS 完成后:
Task 接收反馈
-> 发布 TASK_FINISHED
-> owner_service = LMS
-> handler_code = LMS_STOCK_AREA_CALL_TUBE
-> LMS 消费
-> StockAreaCallTubeTaskHandler#onTaskFinished
11. 设计重点
11.1 TaskCreateReqDTO 不是 AcsTaskDto
TaskCreateReqDTO 是任务服务标准创建模型。
AcsTaskDto 是外部系统 ACS 的下发模型。
二者不要混用。
11.2 addTask 职责迁移到 Builder
旧版:
xxxTask#addTask -> AcsTaskDto
新版:
ExternalTaskPayloadBuilder#build -> AcsTaskDto
11.3 特殊字段放 request_payload
如果某个任务需要特殊参数,例如:
interaction_json
route_plan_code
agv_system_type
paper_array
truss_type
可以通过两种方式处理:
- 常用字段提升为 TaskDO 标准字段;
- 特殊字段放入
request_payload,由专用 Builder 读取。
11.4 dispatch_template_code 决定怎么转 DTO
同样是 ACS,不同任务可能 DTO 组装方式不同,所以需要:
external_system + dispatch_template_code
联合定位 Builder。
12. 最终建议
不要让 LMS/WMS 直接封装 AcsTaskDto。
推荐方式是:
LMS/WMS
-> TaskCreateReqDTO
Task 服务
-> 保存标准任务字段 + request_payload
-> ExternalTaskPayloadBuilder 转换外部 DTO
-> ExternalTaskDispatcher 下发外部系统
这样既能让 Task 服务保存标准任务数据,又能保留旧版 addTask 对不同任务差异化组装外部 DTO 的能力。
一句话总结:
旧 addTask 不放 LMS/WMS,也不让业务服务直接传 AcsTaskDto;
旧 addTask 的 DTO 转换职责迁移到 Task 服务中的 ExternalTaskPayloadBuilder。