diff --git a/CLAUDE.md b/CLAUDE.md index 51b33a30..80313aeb 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -10,6 +10,7 @@ 3. **测试先于实现** — 写代码前先写测试(TDD) 4. **验证先于完成** — 声称完成前必须运行验证命令 5. **代码开发规则** — 遵守 `.claude/skills/nl-java-style/SKILL.md` 技能的规范要求 +6. 不需要频繁提交代码,除非用户说允许测试,否则优先不做测试 ## 可用 Skills diff --git a/doc/remark.txt b/doc/remark.txt index e69de29b..8dedced1 100644 --- a/doc/remark.txt +++ b/doc/remark.txt @@ -0,0 +1,2 @@ +旧版本的代码可以参照:D:\Code\nl\lanzhouhailiang_one\lms\nladmin-system +里面与task相关的,是本次开发任务模块的旧版本 \ No newline at end of file diff --git a/doc/task-service-technical-development.md b/doc/task-service-technical-development.md new file mode 100644 index 00000000..9c800bcb --- /dev/null +++ b/doc/task-service-technical-development.md @@ -0,0 +1,588 @@ +# Task 服务技术开发文档 + +## 1. 服务定位 + +`Task` 服务基于 `task_transport_job` 表,负责搬运任务的通用生命周期管理和 ACS 交互。 + +整体链路: + +```text +LMS/WMS 创建搬运任务 + -> Task 保存任务 + -> Task 自动或手动下发 ACS + -> ACS 反馈执行中、取货、完成、取消 + -> Task 更新任务状态 + -> Task 通过 MQ 通知 LMS/WMS 执行业务完成/取消 + -> LMS/WMS 处理后回传结果 + -> Task 更新最终完成/取消状态 +``` + +Task 服务不处理 LMS/WMS 的具体业务数据,例如生产计划、备货区点位、库存、入库单、出库单。业务逻辑由 LMS/WMS 内部 handler 处理。 + +## 2. Task 服务除 CRUD 外的核心业务 + +Task 服务核心能力包括: + +```text +1. 接收 LMS/WMS 创建任务 +2. 保存任务标准数据 +3. 定时扫描并自动下发任务 +4. 将 task_transport_job 转换为 AcsTaskDto +5. 调用 ACS 下发任务 +6. 接收 ACS 状态反馈 +7. 维护任务状态机 +8. 发布完成/取消 MQ 给 LMS/WMS +9. 接收 LMS/WMS 业务处理结果 +10. 处理下发失败、回调失败、超时补偿 +11. 幂等和并发控制 +12. 任务过程日志记录 +``` + +## 3. 表字段和核心职责对应关系 + +### 3.1 业务路由字段 + +| 字段 | 作用 | +| --- | --- | +| `owner_service` | 决定 MQ 发送给 LMS 还是 WMS | +| `biz_type` | 业务类型,用于查询、统计、防重复创建 | +| `biz_id` | 业务侧记录 ID,LMS/WMS 回调时定位业务数据 | +| `handle_code` | LMS/WMS 内部 handler 编码 | + +完成/取消时路由: + +```text +owner_service -> MQ Tag 或目标服务 +handle_code -> LMS/WMS 内部 handlerMap +``` + +### 3.2 ACS 下发字段 + +| 字段 | 对应 AcsTaskDto | +| --- | --- | +| `task_id` | `taskId` | +| `task_code` | `taskCode` | +| `acs_task_type` | `taskType` | +| `point_code1` | `startDeviceCode` | +| `point_code2` | `nextDeviceCode` | +| `point_code3` | `startDeviceCode2` | +| `point_code4` | `nextDeviceCode2` | +| `vehicle_code` | `vehicleCode` | +| `vehicle_code2` | `vehicleCode2` | +| `priority` | `priority` | +| `product_area` | `productArea` | +| `agv_system_type` | `agvSystemType` | +| `request_param` | `interactionJson` | + +### 3.3 参数快照字段 + +| 字段 | 说明 | +| --- | --- | +| `request_param` | LMS/WMS 创建任务扩展参数,同时作为 `interactionJson` 来源 | +| `dispatch_param` | Task 下发 ACS 的 `AcsTaskDto` 报文快照 | +| `result_param` | ACS 反馈报文 | + +### 3.4 回调字段 + +| 字段 | 说明 | +| --- | --- | +| `callback_status` | 业务回调状态:PENDING/SUCCESS/FAILED | +| `callback_retry_count` | 业务回调重试次数 | +| `callback_error_msg` | 业务回调失败原因 | + +## 4. 任务状态设计 + +推荐状态码: + +| 状态码 | 状态名称 | 修改阶段 | +| --- | --- | --- | +| `10` | 生成 | 创建任务但条件不完整 | +| `40` | 待下发 | 起点终点确认,允许下发 | +| `45` | 下发中 | Task 准备调用 ACS 前 | +| `50` | 已下发 | ACS 接收任务成功 | +| `60` | 执行中 | ACS 反馈执行中 | +| `61` | 已取货搬运中 | ACS 反馈取货完成,部分任务有 | +| `67` | 外部完成待业务处理 | ACS 完成,等待 LMS/WMS 业务完成 | +| `68` | 外部取消中 | Task 正在调用 ACS 取消 | +| `69` | 外部取消待业务处理 | ACS 取消成功,等待 LMS/WMS 回滚 | +| `70` | 完成 | LMS/WMS 业务完成成功 | +| `80` | 取消 | LMS/WMS 业务取消成功 | +| `90` | 失败 | 下发失败、取消失败、不可恢复异常 | + +常用查询: + +```sql +-- 外部未完成任务,用于防止重复创建 +SELECT * +FROM task_transport_job +WHERE deleted = b'0' + AND biz_type = ? + AND task_status < '067'; +``` + +```sql +-- 未最终结束任务 +SELECT * +FROM task_transport_job +WHERE deleted = b'0' + AND task_status < '070'; +``` + +```sql +-- 业务回调失败任务 +SELECT * +FROM task_transport_job +WHERE deleted = b'0' + AND task_status IN ('067', '069') + AND callback_status = 'FAILED'; +``` + +## 5. 创建任务 + +### 5.1 接口 + +```text +POST /rpc-api/task/transport/create +``` + +### 5.2 创建请求核心字段 + +```java +public class TransportTaskCreateReqDTO { + private String taskName; + private String ownerService; + private String bizType; + private String bizId; + private String handleCode; + private String taskType; + private String acsTaskType; + private String agvSystemType; + private String pointCode1; + private String pointCode2; + private String pointCode3; + private String pointCode4; + private String vehicleCode; + private String vehicleCode2; + private String priority; + private String productArea; + private String isAutoIssue; + private Map requestParam; +} +``` + +### 5.3 创建逻辑 + +```text +1. 校验 owner_service、handle_code、acs_task_type 等必要字段 +2. 生成 task_id、task_code +3. 保存业务归属字段 owner_service、biz_type、biz_id、handle_code +4. 保存 ACS 下发字段 point_code、vehicle_code、acs_task_type 等 +5. 保存 request_param +6. 参数完整则 task_status = 040,否则 task_status = 010 +``` + +## 6. 自动下发任务 + +### 6.1 定时器 + +推荐定时任务: + +```text +autoIssueTransportTaskJob +``` + +扫描条件: + +```sql +SELECT * +FROM task_transport_job +WHERE deleted = b'0' + AND is_auto_issue = '1' + AND task_status = '040' +ORDER BY priority DESC, create_time ASC +LIMIT ?; +``` + +### 6.2 抢占任务 + +为避免并发重复下发,先做状态抢占: + +```sql +UPDATE task_transport_job +SET task_status = '045', update_time = NOW() +WHERE task_id = ? + AND task_status = '040'; +``` + +影响行数为 1 才继续下发。 + +### 6.3 下发流程 + +```text +1. 查询 040 待下发任务 +2. 抢占为 045 下发中 +3. 转换成 AcsTaskDto +4. 保存 dispatch_param +5. 调用 ACS 下发接口 +6. 成功:task_status = 050,保存 external_task_no +7. 失败:task_status = 090 或恢复 040,记录错误 +``` + +## 7. AcsTaskDto 转换 + +### 7.1 DTO + +```java +public class AcsTaskDto { + private String taskId; + private String taskCode; + private String taskType; + private String startDeviceCode; + private String nextDeviceCode; + private String startDeviceCode2; + private String nextDeviceCode2; + private String vehicleCode; + private String vehicleCode2; + private String priority; + private String productArea; + private String agvSystemType; + private Map interactionJson; +} +``` + +### 7.2 转换器 + +```java +@Component +public class AcsTaskDtoConverter { + + public AcsTaskDto convert(TaskTransportJobDO task) { + AcsTaskDto dto = new AcsTaskDto(); + dto.setTaskId(String.valueOf(task.getTaskId())); + dto.setTaskCode(task.getTaskCode()); + dto.setTaskType(task.getAcsTaskType()); + dto.setStartDeviceCode(task.getPointCode1()); + dto.setNextDeviceCode(task.getPointCode2()); + dto.setStartDeviceCode2(task.getPointCode3()); + dto.setNextDeviceCode2(task.getPointCode4()); + dto.setVehicleCode(task.getVehicleCode()); + dto.setVehicleCode2(task.getVehicleCode2()); + dto.setPriority(task.getPriority()); + dto.setProductArea(task.getProductArea()); + dto.setAgvSystemType(task.getAgvSystemType()); + dto.setInteractionJson(JsonUtils.parseMap(task.getRequestParam())); + return dto; + } +} +``` + +## 8. ACS 下发服务 + +### 8.1 ACS Client + +```java +public interface AcsClient { + AcsIssueResult issueTask(AcsTaskDto taskDto); + AcsCancelResult cancelTask(String externalTaskNo, Long taskId); +} +``` + +### 8.2 下发服务职责 + +```text +TransportTaskIssueService.issue(taskId) + -> 查询任务 + -> 抢占状态 040 -> 045 + -> AcsTaskDtoConverter.convert + -> 保存 dispatch_param + -> acsClient.issueTask + -> 成功 045 -> 050 + -> 失败记录错误 +``` + +## 9. ACS 反馈接收 + +### 9.1 接口 + +```text +POST /api/task/transport/acs-feedback +``` + +### 9.2 反馈 DTO + +```java +public class AcsFeedbackReqDTO { + private Long taskId; + private String taskCode; + private String externalTaskNo; + private String status; + private String eventId; + private Map payload; +} +``` + +### 9.3 状态处理 + +| ACS 反馈 | Task 更新 | +| --- | --- | +| 执行中 | `task_status = 060` | +| 已取货 | `task_status = 061` | +| 完成 | `task_status = 067`, `callback_status = PENDING` | +| 取消成功 | `task_status = 069`, `callback_status = PENDING` | + +所有反馈均保存到: + +```text +result_param +``` + +完成和取消反馈后,需要发布 MQ 给 LMS/WMS。 + +## 10. 完成/取消 MQ 事件 + +### 10.1 Topic 设计 + +```text +Topic = TASK_EVENT_TOPIC +Tag = owner_service +``` + +示例: + +```text +TASK_EVENT_TOPIC:LMS +TASK_EVENT_TOPIC:WMS +``` + +### 10.2 消息结构 + +```java +public class TaskEventMessage { + private String eventId; + private String eventType; // TASK_FINISHED / TASK_CANCELLED + private Long taskId; + private String taskCode; + private String ownerService; + private String bizType; + private String bizId; + private String handleCode; + private Map payload; +} +``` + +### 10.3 发布逻辑 + +```text +1. 查询 task_transport_job +2. 构造 TaskEventMessage +3. topic = TASK_EVENT_TOPIC +4. tag = owner_service +5. 发送 MQ +6. 发送失败:callback_status = FAILED,记录 callback_error_msg +``` + +## 11. 接收业务回调结果 + +LMS/WMS 消费 MQ 后,内部通过 `handle_code` 找 handler 执行业务逻辑。处理完成后回调 Task。 + +接口: + +```text +POST /rpc-api/task/transport/callback-result +``` + +DTO: + +```java +public class TaskCallbackResultReqDTO { + private Long taskId; + private String eventId; + private String eventType; + private String result; // SUCCESS / FAILED + private String message; +} +``` + +状态更新: + +| 场景 | Task 更新 | +| --- | --- | +| 完成业务成功 | `task_status = 070`, `callback_status = SUCCESS` | +| 完成业务失败 | `task_status = 067`, `callback_status = FAILED`, `callback_retry_count + 1` | +| 取消业务成功 | `task_status = 080`, `callback_status = SUCCESS` | +| 取消业务失败 | `task_status = 069`, `callback_status = FAILED`, `callback_retry_count + 1` | + +## 12. 人工完成和取消 + +### 12.1 人工完成 + +```text +PC 点击完成 + -> Task 校验任务 + -> task_status = 067 + -> callback_status = PENDING + -> 发布 TASK_FINISHED MQ + -> 页面提示:完成请求已提交,业务处理中 +``` + +### 12.2 人工取消 + +未下发任务: + +```text +010/040 -> 069 -> 发布 TASK_CANCELLED MQ +``` + +已下发任务: + +```text +050/060/061 -> 068 -> 调 ACS 取消 -> 069 -> 发布 TASK_CANCELLED MQ +``` + +## 13. 补偿和重试 + +### 13.1 回调失败重试 + +扫描: + +```sql +SELECT * +FROM task_transport_job +WHERE deleted = b'0' + AND task_status IN ('067', '069') + AND callback_status = 'FAILED' + AND callback_retry_count < ?; +``` + +处理: + +```text +重新发布 TASK_FINISHED 或 TASK_CANCELLED +callback_retry_count + 1 +``` + +### 13.2 PENDING 超时补偿 + +扫描: + +```text +task_status IN ('067', '069') +callback_status = PENDING +update_time 超过阈值 +``` + +处理: + +```text +重新发布 MQ 或标记 FAILED +``` + +### 13.3 下发中超时补偿 + +扫描: + +```text +task_status = 045 +update_time 超过阈值 +``` + +处理: + +```text +查询 ACS 是否已收到任务;无法确认则恢复 040 或标记 090 +``` + +## 14. 幂等控制 + +### 14.1 创建幂等 + +防重复创建示例: + +```sql +SELECT * +FROM task_transport_job +WHERE deleted = b'0' + AND owner_service = ? + AND biz_type = ? + AND biz_id = ? + AND task_status < '067'; +``` + +### 14.2 下发幂等 + +通过 `040 -> 045` 条件更新抢占。 + +### 14.3 ACS 反馈幂等 + +建议引入反馈日志表记录 `eventId`。第一版至少根据状态判断: + +```text +已是 070,不重复处理完成 +已是 067,不重复发布完成 MQ +``` + +### 14.4 业务回调结果幂等 + +如果已是最终态: + +```text +task_status = 070 或 080 +``` + +再次收到成功回调时直接返回成功。 + +## 15. 推荐模块结构 + +```text +nl-module-task-server + controller + TransportTaskController + AcsFeedbackController + api + TransportTaskApiImpl + service + TransportTaskService + TransportTaskIssueService + TransportTaskFeedbackService + TransportTaskCallbackService + TransportTaskCancelService + TransportTaskRetryService + converter + AcsTaskDtoConverter + mq + TaskEventProducer + job + AutoIssueTransportTaskJob + CallbackRetryJob + dal + dataobject/TaskTransportJobDO + mapper/TaskTransportJobMapper + enums + TransportTaskStatusEnum + CallbackStatusEnum + TaskEventTypeEnum +``` + +## 16. 总结 + +Task 服务的核心不是简单 CRUD,而是搬运任务生命周期中台。 + +核心职责: + +```text +接收任务 -> 保存任务 -> 自动下发 -> 转换 AcsTaskDto -> 调 ACS -> 接收反馈 -> 发布业务事件 -> 接收业务结果 -> 最终完成/取消 -> 补偿重试 +``` + +关键路由: + +```text +owner_service 决定事件给 LMS/WMS +handle_code 决定业务服务内部哪个 handler 处理 +``` + +关键状态: + +```text +task_status 表示任务流转到哪一步 +callback_status 表示 LMS/WMS 业务回调是否成功 +``` diff --git a/docs/superpowers/plans/2026-07-14-task-core-implementation.md b/docs/superpowers/plans/2026-07-14-task-core-implementation.md index 96eed23a..504166a1 100644 --- a/docs/superpowers/plans/2026-07-14-task-core-implementation.md +++ b/docs/superpowers/plans/2026-07-14-task-core-implementation.md @@ -793,7 +793,7 @@ git commit -m "feat: 新增 TransportTaskStatusClient 同步 HTTP 回调客户 ```java package cn.code.nl.module.task.mq; -import cn.code.nl.module.task.dto.TaskEventMessage; +import cn.code.nl.module.task.mq.message.TaskEventMessage; import lombok.extern.slf4j.Slf4j; import org.apache.rocketmq.spring.core.RocketMQTemplate; import org.springframework.stereotype.Component; @@ -889,7 +889,7 @@ import cn.code.nl.module.task.client.TransportTaskStatusClient; import cn.code.nl.module.task.dal.dataobject.transporttask.TransportTaskDO; import cn.code.nl.module.task.dal.mysql.transporttask.TransportTaskMapper; import cn.code.nl.module.task.dto.AcsFeedbackReqDTO; -import cn.code.nl.module.task.mq.TaskEventProducer; +import cn.code.nl.module.task.mq.producer.TaskEventProducer; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; @@ -977,13 +977,13 @@ import cn.code.nl.module.task.client.TransportTaskStatusClient; import cn.code.nl.module.task.dal.dataobject.transporttask.TransportTaskDO; import cn.code.nl.module.task.dal.mysql.transporttask.TransportTaskMapper; import cn.code.nl.module.task.dto.AcsFeedbackReqDTO; -import cn.code.nl.module.task.dto.TaskEventMessage; +import cn.code.nl.module.task.mq.message.TaskEventMessage; import cn.code.nl.module.task.dto.TaskStatusCallbackReqDTO; import cn.code.nl.module.task.dto.TaskStatusCallbackRespDTO; import cn.code.nl.module.task.enums.CallbackStatusEnum; import cn.code.nl.module.task.enums.TaskEventTypeEnum; import cn.code.nl.module.task.enums.TransportTaskStatusEnum; -import cn.code.nl.module.task.mq.TaskEventProducer; +import cn.code.nl.module.task.mq.producer.TaskEventProducer; import cn.hutool.core.util.StrUtil; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; diff --git a/nl-framework/nl-spring-boot-starter-mq/pom.xml b/nl-framework/nl-spring-boot-starter-mq/pom.xml index 8eeed4f3..39916d10 100644 --- a/nl-framework/nl-spring-boot-starter-mq/pom.xml +++ b/nl-framework/nl-spring-boot-starter-mq/pom.xml @@ -36,7 +36,6 @@ org.apache.rocketmq rocketmq-spring-boot-starter - true diff --git a/nl-module-task/nl-module-task-api/src/main/java/cn/code/nl/module/task/api/package-info.java b/nl-module-task/nl-module-task-api/src/main/java/cn/code/nl/module/task/api/package-info.java index 6f52d4a6..8feb3cc7 100644 --- a/nl-module-task/nl-module-task-api/src/main/java/cn/code/nl/module/task/api/package-info.java +++ b/nl-module-task/nl-module-task-api/src/main/java/cn/code/nl/module/task/api/package-info.java @@ -1,5 +1,6 @@ /** - * + * 外部引用任务的task接口 * @Author: liyongde * @Date: 2026/7/14 18:57 - */package cn.code.nl.module.task.api; \ No newline at end of file + */ +package cn.code.nl.module.task.api; \ No newline at end of file diff --git a/nl-module-task/nl-module-task-api/src/main/java/cn/code/nl/module/task/dto/TransportTaskCreateReqDTO.java b/nl-module-task/nl-module-task-api/src/main/java/cn/code/nl/module/task/dto/TransportTaskCreateReqDTO.java index 96271cbf..7a00c298 100644 --- a/nl-module-task/nl-module-task-api/src/main/java/cn/code/nl/module/task/dto/TransportTaskCreateReqDTO.java +++ b/nl-module-task/nl-module-task-api/src/main/java/cn/code/nl/module/task/dto/TransportTaskCreateReqDTO.java @@ -68,6 +68,9 @@ public class TransportTaskCreateReqDTO { @Schema(description = "是否自动下发") private String isAutoIssue; + @Schema(description = "是否创建完成") + private Boolean isCreateFinish; + @Schema(description = "任务类型") private String taskType; @@ -87,5 +90,5 @@ public class TransportTaskCreateReqDTO { private Long sortSeq; @Schema(description = "扩展参数(JSON)") - private Map requestParam; + private String requestParam; } diff --git a/nl-module-task/nl-module-task-api/src/main/java/cn/code/nl/module/task/enums/ErrorCodeConstants.java b/nl-module-task/nl-module-task-api/src/main/java/cn/code/nl/module/task/enums/ErrorCodeConstants.java index 2d7ee424..c01ed4f5 100644 --- a/nl-module-task/nl-module-task-api/src/main/java/cn/code/nl/module/task/enums/ErrorCodeConstants.java +++ b/nl-module-task/nl-module-task-api/src/main/java/cn/code/nl/module/task/enums/ErrorCodeConstants.java @@ -8,8 +8,10 @@ import cn.code.nl.framework.common.exception.ErrorCode; * @Date: 2026/7/14 13:03 */ public interface ErrorCodeConstants { - ErrorCode TRANSPORT_TASK_NOT_EXISTS = new ErrorCode(1, "搬运任务不存在"); - ErrorCode TRANSPORT_TASK_EXISTS_UNFINISHED = new ErrorCode(2, "同业务类型下存在未完结的搬运任务"); - ErrorCode TRANSPORT_TASK_STATUS_NOT_ALLOW = new ErrorCode(3, "当前任务状态不允许此操作"); - ErrorCode TRANSPORT_TASK_CALLBACK_SEND_FAILED = new ErrorCode(4, "业务回调发送失败"); + ErrorCode TRANSPORT_TASK_NOT_EXISTS = new ErrorCode(5001, "搬运任务不存在"); + ErrorCode TRANSPORT_TASK_EXISTS_UNFINISHED = new ErrorCode(5002, "同业务类型下存在未完结的搬运任务"); + ErrorCode TRANSPORT_TASK_STATUS_NOT_ALLOW = new ErrorCode(5003, "当前任务状态不允许此操作"); + ErrorCode TRANSPORT_TASK_CALLBACK_SEND_FAILED = new ErrorCode(5004, "业务回调发送失败"); + ErrorCode TRANSPORT_TASK_RUNNING_ALREADY_EXIST = new ErrorCode(5005, "已存在运行中的任务"); + } diff --git a/nl-module-task/nl-module-task-api/src/main/java/cn/code/nl/module/task/enums/TransportTaskStatusEnum.java b/nl-module-task/nl-module-task-api/src/main/java/cn/code/nl/module/task/enums/TransportTaskStatusEnum.java index 39bef81f..e5b49c5f 100644 --- a/nl-module-task/nl-module-task-api/src/main/java/cn/code/nl/module/task/enums/TransportTaskStatusEnum.java +++ b/nl-module-task/nl-module-task-api/src/main/java/cn/code/nl/module/task/enums/TransportTaskStatusEnum.java @@ -1,8 +1,44 @@ package cn.code.nl.module.task.enums; +import lombok.Getter; + /** - * + * 任务状态的枚举 * @Author: liyongde * @Date: 2026/7/14 16:14 - */public enum TransportTaskStatusEnum { + */ +@Getter +public enum TransportTaskStatusEnum { + + CREATED(10, "生成"), + + READY(40, "待下发-起点终点确认"), + + ISSUING(45, "下发中"), + + ISSUED(50, "已下发"), + + EXECUTING(60, "执行中"), + + PICKED(61, "已取货搬运中"), + + FINISHED_CALLBACK_PENDING(67, "外部完成待业务处理"), + + CANCEL_EXTERNAL_PENDING(68, "外部取消中"), + + CANCEL_CALLBACK_PENDING(69, "外部取消待业务处理"), + + FINISHED(70, "完成"), + + CANCELLED(80, "取消"), + + FAILED(90, "失败"); + + private final Integer code; + private final String name; + + TransportTaskStatusEnum(Integer code, String name) { + this.code = code; + this.name = name; + } } diff --git a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/controller/admin/transporttask/AcsFeedbackController.java b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/controller/admin/transporttask/AcsFeedbackController.java index 6ca2d92e..6cf432f0 100644 --- a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/controller/admin/transporttask/AcsFeedbackController.java +++ b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/controller/admin/transporttask/AcsFeedbackController.java @@ -1,4 +1,4 @@ -package cn.code.nl.module.task.controller; +package cn.code.nl.module.task.controller.admin.transporttask; import cn.code.nl.framework.common.pojo.CommonResult; import cn.code.nl.module.task.dto.AcsFeedbackReqDTO; @@ -22,7 +22,7 @@ import static cn.code.nl.framework.common.pojo.CommonResult.success; */ @Tag(name = "ACS 反馈") @RestController -@RequestMapping("/api/task/transport") +@RequestMapping("/task/transport") @Validated public class AcsFeedbackController { diff --git a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/controller/admin/transporttask/TaskCallbackController.java b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/controller/admin/transporttask/TaskCallbackController.java index 6c8fb27f..dc7840d8 100644 --- a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/controller/admin/transporttask/TaskCallbackController.java +++ b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/controller/admin/transporttask/TaskCallbackController.java @@ -1,4 +1,4 @@ -package cn.code.nl.module.task.controller; +package cn.code.nl.module.task.controller.admin.transporttask; import cn.code.nl.framework.common.pojo.CommonResult; import cn.code.nl.module.task.dto.TaskCallbackResultReqDTO; diff --git a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/dal/mysql/transporttask/TransportTaskMapper.java b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/dal/mysql/transporttask/TransportTaskMapper.java index 66d2a5f4..52c9baa6 100644 --- a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/dal/mysql/transporttask/TransportTaskMapper.java +++ b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/dal/mysql/transporttask/TransportTaskMapper.java @@ -4,6 +4,7 @@ import java.util.*; import cn.code.nl.framework.common.pojo.PageResult; import cn.code.nl.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.code.nl.module.task.enums.TransportTaskStatusEnum; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import cn.code.nl.framework.mybatis.core.mapper.BaseMapperX; import cn.code.nl.module.task.dal.dataobject.transporttask.TransportTaskDO; @@ -65,7 +66,7 @@ public interface TransportTaskMapper extends BaseMapperX { return selectOne(new LambdaQueryWrapperX() .eq(TransportTaskDO::getBizType, bizType) .eq(TransportTaskDO::getBizId, bizId) - .lt(TransportTaskDO::getTaskStatus, "067")); + .lt(TransportTaskDO::getTaskStatus, TransportTaskStatusEnum.FINISHED_CALLBACK_PENDING.getCode())); } /** diff --git a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/framework/rpc/config/RpcConfiguration.java b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/framework/rpc/config/RpcConfiguration.java index af510b99..0befd253 100644 --- a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/framework/rpc/config/RpcConfiguration.java +++ b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/framework/rpc/config/RpcConfiguration.java @@ -1,15 +1,12 @@ package cn.code.nl.module.task.framework.rpc.config; -import cn.code.nl.module.task.api.TransportTaskApi; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.context.annotation.Configuration; /** * Task 服务 RPC 配置 - *

- * 注册 Feign 客户端,使 LMS/WMS 等调用方可通过 @Resource 注入 TransportTaskApi */ -@Configuration -@EnableFeignClients(clients = {TransportTaskApi.class}) +@Configuration(value = "taskRpcConfiguration", proxyBeanMethods = false) +@EnableFeignClients() public class RpcConfiguration { } diff --git a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/framework/security/config/SecurityConfiguration.java b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/framework/security/config/SecurityConfiguration.java index 08562973..b15f0f50 100644 --- a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/framework/security/config/SecurityConfiguration.java +++ b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/framework/security/config/SecurityConfiguration.java @@ -1,14 +1,14 @@ package cn.code.nl.module.task.framework.security.config; import cn.code.nl.framework.security.config.AuthorizeRequestsCustomizer; -import cn.code.nl.module.infra.enums.ApiConstants; +import cn.code.nl.module.task.enums.ApiConstants; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer; /** - * Infra 模块的 Security 配置 + * Task 模块的 Security 配置 */ @Configuration(proxyBeanMethods = false, value = "taskSecurityConfiguration") public class SecurityConfiguration { diff --git a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/mq/consumer/package-info.java b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/mq/consumer/package-info.java index 0ed7b3e0..1c61ebcd 100644 --- a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/mq/consumer/package-info.java +++ b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/mq/consumer/package-info.java @@ -1,5 +1,6 @@ /** - * + * 消费者 * @Author: liyongde * @Date: 2026/7/14 18:47 - */package cn.code.nl.module.task.mq.consumer; \ No newline at end of file + */ +package cn.code.nl.module.task.mq.consumer; \ No newline at end of file diff --git a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/mq/message/TaskEventMessage.java b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/mq/message/TaskEventMessage.java index 5f8ee3b4..d6446b4f 100644 --- a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/mq/message/TaskEventMessage.java +++ b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/mq/message/TaskEventMessage.java @@ -1,4 +1,4 @@ -package cn.code.nl.module.task.dto; +package cn.code.nl.module.task.mq.message; import lombok.Data; diff --git a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/mq/message/package-info.java b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/mq/message/package-info.java index b5f74af7..d8b4e268 100644 --- a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/mq/message/package-info.java +++ b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/mq/message/package-info.java @@ -1,5 +1,6 @@ /** - * + * 信息体 * @Author: liyongde * @Date: 2026/7/14 18:47 - */package cn.code.nl.module.task.mq.message; \ No newline at end of file + */ +package cn.code.nl.module.task.mq.message; \ No newline at end of file diff --git a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/mq/package-info.java b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/mq/package-info.java index 59cee774..e083d45b 100644 --- a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/mq/package-info.java +++ b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/mq/package-info.java @@ -1,4 +1,4 @@ /** * 消息队列的消息 */ -package cn.code.nl.module.infra.mq.message; +package cn.code.nl.module.task.mq; diff --git a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/mq/producer/TaskEventProducer.java b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/mq/producer/TaskEventProducer.java index ab119a52..6de3f0d1 100644 --- a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/mq/producer/TaskEventProducer.java +++ b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/mq/producer/TaskEventProducer.java @@ -1,6 +1,6 @@ -package cn.code.nl.module.task.mq; +package cn.code.nl.module.task.mq.producer; -import cn.code.nl.module.task.dto.TaskEventMessage; +import cn.code.nl.module.task.mq.message.TaskEventMessage; import lombok.extern.slf4j.Slf4j; import org.apache.rocketmq.spring.core.RocketMQTemplate; import org.springframework.stereotype.Component; diff --git a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/service/transporttask/TransportTaskFeedbackServiceImpl.java b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/service/transporttask/TransportTaskFeedbackServiceImpl.java index 051d9be9..94d4df46 100644 --- a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/service/transporttask/TransportTaskFeedbackServiceImpl.java +++ b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/service/transporttask/TransportTaskFeedbackServiceImpl.java @@ -4,13 +4,13 @@ import cn.code.nl.module.task.client.TransportTaskStatusClient; import cn.code.nl.module.task.dal.dataobject.transporttask.TransportTaskDO; import cn.code.nl.module.task.dal.mysql.transporttask.TransportTaskMapper; import cn.code.nl.module.task.dto.AcsFeedbackReqDTO; -import cn.code.nl.module.task.dto.TaskEventMessage; +import cn.code.nl.module.task.mq.message.TaskEventMessage; import cn.code.nl.module.task.dto.TaskStatusCallbackReqDTO; import cn.code.nl.module.task.dto.TaskStatusCallbackRespDTO; import cn.code.nl.module.task.enums.CallbackStatusEnum; import cn.code.nl.module.task.enums.TaskEventTypeEnum; import cn.code.nl.module.task.enums.TransportTaskStatusEnum; -import cn.code.nl.module.task.mq.TaskEventProducer; +import cn.code.nl.module.task.mq.producer.TaskEventProducer; import cn.hutool.core.util.StrUtil; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; diff --git a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/service/transporttask/TransportTaskServiceImpl.java b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/service/transporttask/TransportTaskServiceImpl.java index a4367c3d..f4fa2e49 100644 --- a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/service/transporttask/TransportTaskServiceImpl.java +++ b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/service/transporttask/TransportTaskServiceImpl.java @@ -1,5 +1,6 @@ package cn.code.nl.module.task.service.transporttask; +import cn.code.nl.framework.common.exception.ServiceException; import cn.code.nl.framework.common.pojo.PageResult; import cn.code.nl.framework.common.util.object.BeanUtils; import cn.code.nl.module.task.controller.admin.transporttask.vo.TransportTaskPageReqVO; @@ -43,21 +44,12 @@ public class TransportTaskServiceImpl implements TransportTaskService { @Override public Long createTransportTaskByRpc(TransportTaskCreateReqDTO reqDTO) { - // 1. 幂等检查:同 bizType + bizId 下是否有未完结任务 - TransportTaskDO existing = transportTaskMapper.selectUnfinishedByBiz( - reqDTO.getBizType(), reqDTO.getBizId()); - if (existing != null) { - return existing.getTaskId(); - } - - // 2. 构建 DO 并保存 + // 1. 构建 DO 并保存 TransportTaskDO task = BeanUtils.toBean(reqDTO, TransportTaskDO.class); // 参数完整 → 待下发(040),否则 → 生成(010) - boolean paramComplete = StrUtil.isNotBlank(reqDTO.getPointCode1()) - && StrUtil.isNotBlank(reqDTO.getPointCode2()); - task.setTaskStatus(paramComplete - ? String.format("%03d", TransportTaskStatusEnum.READY.getCode()) - : String.format("%03d", TransportTaskStatusEnum.CREATED.getCode())); + task.setTaskStatus(reqDTO.getIsCreateFinish() + ? String.valueOf(TransportTaskStatusEnum.READY.getCode()) + : String.valueOf(TransportTaskStatusEnum.CREATED.getCode())); task.setCallbackStatus(CallbackStatusEnum.PENDING.getCode()); task.setCallbackRetryCount(0); task.setCreateMode("RPC"); diff --git a/nl-module-task/nl-module-task-server/src/main/resources/application-dev.yaml b/nl-module-task/nl-module-task-server/src/main/resources/application-dev.yaml index bf2df653..c2782a65 100644 --- a/nl-module-task/nl-module-task-server/src/main/resources/application-dev.yaml +++ b/nl-module-task/nl-module-task-server/src/main/resources/application-dev.yaml @@ -79,6 +79,8 @@ spring: # rocketmq 配置项,对应 RocketMQProperties 配置类 rocketmq: name-server: 127.0.0.1:9876 # RocketMQ Namesrv + producer: + group: ${spring.application.name}_TASK_DEV_PRODUCER # 生产者分组 spring: # RabbitMQ 配置项,对应 RabbitProperties 配置类 diff --git a/nl-module-task/nl-module-task-server/src/main/resources/application.yaml b/nl-module-task/nl-module-task-server/src/main/resources/application.yaml index b08d22d2..6787012b 100644 --- a/nl-module-task/nl-module-task-server/src/main/resources/application.yaml +++ b/nl-module-task/nl-module-task-server/src/main/resources/application.yaml @@ -64,10 +64,10 @@ mybatis-plus: map-underscore-to-camel-case: true # 虽然默认为 true ,但是还是显示去指定下。 global-config: db-config: - id-type: NONE # “智能”模式,基于 IdTypeEnvironmentPostProcessor + 数据源的类型,自动适配成 AUTO、INPUT 模式。 + id-type: ASSIGN_ID # 分配 ID,默认使用雪花算法。注意,Oracle、PostgreSQL、Kingbase、DB2、H2 数据库时,需要去除实体类上的 @KeySequence 注解 +# id-type: NONE # “智能”模式,基于 IdTypeEnvironmentPostProcessor + 数据源的类型,自动适配成 AUTO、INPUT 模式。 # id-type: AUTO # 自增 ID,适合 MySQL 等直接自增的数据库 # id-type: INPUT # 用户输入 ID,适合 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库 - # id-type: ASSIGN_ID # 分配 ID,默认使用雪花算法。注意,Oracle、PostgreSQL、Kingbase、DB2、H2 数据库时,需要去除实体类上的 @KeySequence 注解 logic-delete-value: 1 # 逻辑已删除值(默认为 1) logic-not-delete-value: 0 # 逻辑未删除值(默认为 0) banner: false # 关闭控制台的 Banner 打印 diff --git a/nl-module-task/nl-module-task-server/src/test/java/cn/code/nl/module/task/service/transporttask/TransportTaskServiceImplTest.java b/nl-module-task/nl-module-task-server/src/test/java/cn/code/nl/module/task/service/transporttask/TransportTaskServiceImplTest.java index 80a320e2..1f52d897 100644 --- a/nl-module-task/nl-module-task-server/src/test/java/cn/code/nl/module/task/service/transporttask/TransportTaskServiceImplTest.java +++ b/nl-module-task/nl-module-task-server/src/test/java/cn/code/nl/module/task/service/transporttask/TransportTaskServiceImplTest.java @@ -1,8 +1,11 @@ package cn.code.nl.module.task.service.transporttask; +import cn.code.nl.framework.common.pojo.CommonResult; +import cn.code.nl.module.task.api.TransportTaskApi; import cn.code.nl.module.task.dal.dataobject.transporttask.TransportTaskDO; import cn.code.nl.module.task.dal.mysql.transporttask.TransportTaskMapper; import cn.code.nl.module.task.dto.TransportTaskCreateReqDTO; +import jakarta.annotation.Resource; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; @@ -22,6 +25,34 @@ class TransportTaskServiceImplTest { @InjectMocks private TransportTaskServiceImpl transportTaskService; + /** + * Feign 客户端 —— 真实发起 HTTP 调用,直连本机 Controller + */ + @Mock + private TransportTaskApi transportTaskApi; + + /** + * 端到端:Feign → Controller → Service → Mapper + */ + @Test + void testApiCreateTask() { + // given + TransportTaskCreateReqDTO req = new TransportTaskCreateReqDTO(); + req.setTaskName("测试任务"); + req.setOwnerService("LMS"); + req.setBizType("INBOUND"); + req.setBizId("INB-001"); + req.setHandleCode("inboundHandler"); + req.setAcsTaskType("MOVE"); + req.setAgvSystemType("AGV_TYPE_A"); + req.setPointCode1("1"); + req.setPointCode2("2"); + req.setPointCode3("3"); + req.setPointCode4("4"); + + transportTaskApi.createTransportTask(req); + } + @Test void testCreateTransportTaskByRpc_shouldReturnTaskId() { // given