diff --git a/nl-framework/nl-common/src/main/java/cn/code/nl/framework/common/pojo/AcsBaseReqDTO.java b/nl-framework/nl-common/src/main/java/cn/code/nl/framework/common/pojo/AcsBaseReqDTO.java new file mode 100644 index 00000000..23fff4ab --- /dev/null +++ b/nl-framework/nl-common/src/main/java/cn/code/nl/framework/common/pojo/AcsBaseReqDTO.java @@ -0,0 +1,22 @@ +package cn.code.nl.framework.common.pojo; + +import lombok.Data; + +/** + * 基础DTO对象 + * @Author: liyongde + * @Date: 2026/7/24 8:49 + */ +@Data +public class AcsBaseReqDTO { + /** + * 请求号: traceId + */ + private String traceId; + + /** + * + * 时间 + */ + private Long timestamp; +} diff --git a/nl-framework/nl-common/src/main/java/cn/code/nl/framework/common/pojo/AcsBaseRespDTO.java b/nl-framework/nl-common/src/main/java/cn/code/nl/framework/common/pojo/AcsBaseRespDTO.java new file mode 100644 index 00000000..91c763ea --- /dev/null +++ b/nl-framework/nl-common/src/main/java/cn/code/nl/framework/common/pojo/AcsBaseRespDTO.java @@ -0,0 +1,26 @@ +package cn.code.nl.framework.common.pojo; + +import lombok.Data; + +/** + * acs返回基础对象 + * @Author: liyongde + * @Date: 2026/7/24 10:27 + */ +@Data +public class AcsBaseRespDTO { + /** + * 是否整体成功 + */ + private Boolean success; + + /** + * 响应编码 + */ + private String code; + + /** + * 响应消息 + */ + private String msg; +} diff --git a/nl-framework/nl-common/src/main/java/cn/code/nl/framework/common/util/http/AcsUtil.java b/nl-framework/nl-common/src/main/java/cn/code/nl/framework/common/util/http/AcsUtil.java new file mode 100644 index 00000000..0ada5947 --- /dev/null +++ b/nl-framework/nl-common/src/main/java/cn/code/nl/framework/common/util/http/AcsUtil.java @@ -0,0 +1,78 @@ +package cn.code.nl.framework.common.util.http; + +import cn.code.nl.framework.common.exception.ServerException; +import cn.code.nl.framework.common.exception.ServiceException; +import cn.code.nl.framework.common.util.json.JsonUtils; +import cn.hutool.core.util.StrUtil; + +import java.net.ConnectException; +import java.net.NoRouteToHostException; +import java.net.SocketException; +import java.net.SocketTimeoutException; +import java.net.UnknownHostException; +import java.util.Collections; +import java.util.Map; + +/** + * ACS 调用工具 + */ +public class AcsUtil { + + /** + * 发送 POST 请求并解析响应 + * + * @param serverAddress ACS 服务地址 + * @param api API 路径 + * @param request 请求参数 + * @param responseType 响应类型 + * @return 响应对象 + */ + public static T post(String serverAddress, String api, Object request, Class responseType) { + String url = buildUrl(serverAddress, api); + String response; + try { + response = HttpUtils.post(url, headers(), JsonUtils.toJsonString(request)); + } catch (RuntimeException ex) { + if (isNetworkException(ex)) { + throw new ServiceException(500, "ACS服务网络不通"); + } + throw ex; + } + return JsonUtils.parseObject(response, responseType); + } + + /** + * 拼接服务地址和 API + */ + private static String buildUrl(String serverAddress, String api) { + String baseUrl = StrUtil.removeSuffix(serverAddress, StrUtil.SLASH); + String apiPath = StrUtil.addPrefixIfNot(api, StrUtil.SLASH); + return baseUrl + apiPath; + } + + /** + * 构建 JSON 请求头 + */ + private static Map headers() { + return Collections.singletonMap("Content-Type", "application/json;charset=UTF-8"); + } + + /** + * 判断是否为 ACS 网络连接异常 + */ + private static boolean isNetworkException(Throwable ex) { + Throwable cause = ex; + while (cause != null) { + if (cause instanceof ConnectException + || cause instanceof SocketTimeoutException + || cause instanceof UnknownHostException + || cause instanceof NoRouteToHostException + || cause instanceof SocketException) { + return true; + } + cause = cause.getCause(); + } + return false; + } + +} diff --git a/nl-framework/nl-spring-boot-starter-execute/src/main/java/cn/code/nl/framework/execute/biz/api/AbstractTaskCommonApiImpl.java b/nl-framework/nl-spring-boot-starter-execute/src/main/java/cn/code/nl/framework/execute/biz/api/AbstractTaskCommonApiImpl.java new file mode 100644 index 00000000..9f4fb1e5 --- /dev/null +++ b/nl-framework/nl-spring-boot-starter-execute/src/main/java/cn/code/nl/framework/execute/biz/api/AbstractTaskCommonApiImpl.java @@ -0,0 +1,115 @@ +package cn.code.nl.framework.execute.biz.api; + +import cn.code.nl.framework.common.exception.ServiceException; +import cn.code.nl.framework.common.pojo.CommonResult; +import cn.code.nl.framework.execute.biz.dto.TaskStatusCallApiReqDTO; +import cn.code.nl.framework.execute.biz.vo.AcsApplyActionRespVO; +import cn.code.nl.framework.execute.core.AbstractTask; +import cn.code.nl.framework.execute.core.TaskFactory; +import cn.code.nl.framework.execute.core.dto.TaskExecuteDTO; +import jakarta.annotation.Resource; + +/** + * 任务通用 API 公共实现 + * + * @author liyongde + */ +public abstract class AbstractTaskCommonApiImpl implements TaskCommonApi { + + /** + * 任务处理器工厂 + */ + @Resource + private TaskFactory taskFactory; + + /** + * 处理取货完成 + */ + @Override + public CommonResult doHandlePicked(TaskStatusCallApiReqDTO taskStatusCallApiReqDTO) { + return execute(taskStatusCallApiReqDTO, (task, taskExecuteDTO) -> { + task.doHandlePicked(taskExecuteDTO); + return null; + }); + } + + /** + * 处理二次请求 + */ + @Override + public CommonResult doHandleApplyAgain(TaskStatusCallApiReqDTO taskStatusCallApiReqDTO) { + return execute(taskStatusCallApiReqDTO, AbstractTask::againApply); + } + + /** + * 处理请求放货 + */ + @Override + public CommonResult doHandleRequestRelease(TaskStatusCallApiReqDTO taskStatusCallApiReqDTO) { + return execute(taskStatusCallApiReqDTO, AbstractTask::requestPutAway); + } + + /** + * 处理请求取货 + */ + @Override + public CommonResult doHandleRequestPick(TaskStatusCallApiReqDTO taskStatusCallApiReqDTO) { + return execute(taskStatusCallApiReqDTO, AbstractTask::requestPickGoods); + } + + /** + * 处理请求离开 + */ + @Override + public CommonResult doHandleRequestLeave(TaskStatusCallApiReqDTO taskStatusCallApiReqDTO) { + return execute(taskStatusCallApiReqDTO, AbstractTask::requestOut); + } + + /** + * 处理请求进入 + */ + @Override + public CommonResult doHandleRequestEnter(TaskStatusCallApiReqDTO taskStatusCallApiReqDTO) { + return execute(taskStatusCallApiReqDTO, AbstractTask::requestIn); + } + + /** + * 根据 handleCode 路由到具体任务处理器 + */ + private CommonResult execute(TaskStatusCallApiReqDTO taskStatusCallApiReqDTO, + TaskActionInvoker taskActionInvoker) { + AbstractTask task = taskFactory.getTask(taskStatusCallApiReqDTO.getHandleCode()); + if (task == null) { + throw new ServiceException(500, "未找到任务处理器:" + taskStatusCallApiReqDTO.getHandleCode()); + } + Object data = taskActionInvoker.invoke(task, buildTaskExecuteDTO(taskStatusCallApiReqDTO)); + + AcsApplyActionRespVO respVO = new AcsApplyActionRespVO(); + respVO.setTaskId(taskStatusCallApiReqDTO.getTaskId()); + respVO.setTaskCode(taskStatusCallApiReqDTO.getTaskCode()); + respVO.setData(data); + return CommonResult.success(respVO); + } + + /** + * 构建任务执行参数 + */ + private TaskExecuteDTO buildTaskExecuteDTO(TaskStatusCallApiReqDTO taskStatusCallApiReqDTO) { + return TaskExecuteDTO.builder() + .taskId(taskStatusCallApiReqDTO.getTaskId()) + .payload(taskStatusCallApiReqDTO.getPayload()) + .build(); + } + + /** + * 任务动作调用器 + */ + @FunctionalInterface + private interface TaskActionInvoker { + + /** + * 调用任务动作 + */ + Object invoke(AbstractTask task, TaskExecuteDTO taskExecuteDTO); + } +} diff --git a/nl-framework/nl-spring-boot-starter-execute/src/main/java/cn/code/nl/framework/execute/biz/api/TaskCommonApi.java b/nl-framework/nl-spring-boot-starter-execute/src/main/java/cn/code/nl/framework/execute/biz/api/TaskCommonApi.java index 66d9a473..fff86e58 100644 --- a/nl-framework/nl-spring-boot-starter-execute/src/main/java/cn/code/nl/framework/execute/biz/api/TaskCommonApi.java +++ b/nl-framework/nl-spring-boot-starter-execute/src/main/java/cn/code/nl/framework/execute/biz/api/TaskCommonApi.java @@ -4,8 +4,8 @@ import cn.code.nl.framework.common.pojo.CommonResult; import cn.code.nl.framework.execute.biz.dto.TaskStatusCallApiReqDTO; import cn.code.nl.framework.execute.biz.vo.AcsApplyActionRespVO; import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.media.Schema; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; /** * @@ -16,25 +16,25 @@ public interface TaskCommonApi { @Operation(summary = "请求取货") @PostMapping("/do-handle-picked") - CommonResult doHandlePicked(TaskStatusCallApiReqDTO taskStatusCallApiReqDTO); + CommonResult doHandlePicked(@RequestBody TaskStatusCallApiReqDTO taskStatusCallApiReqDTO); @Operation(summary = "二次请求") @PostMapping("/do-handle-apply-again") - CommonResult doHandleApplyAgain(TaskStatusCallApiReqDTO taskStatusCallApiReqDTO); + CommonResult doHandleApplyAgain(@RequestBody TaskStatusCallApiReqDTO taskStatusCallApiReqDTO); @Operation(summary = "处理请求放货") @PostMapping("/do-handle-request-release") - CommonResult doHandleRequestRelease(TaskStatusCallApiReqDTO taskStatusCallApiReqDTO); + CommonResult doHandleRequestRelease(@RequestBody TaskStatusCallApiReqDTO taskStatusCallApiReqDTO); @Operation(summary = "处理请求取货") @PostMapping("/do-handle-request-pick") - CommonResult doHandleRequestPick(TaskStatusCallApiReqDTO taskStatusCallApiReqDTO); + CommonResult doHandleRequestPick(@RequestBody TaskStatusCallApiReqDTO taskStatusCallApiReqDTO); @Operation(summary = "处理请求离开") @PostMapping("/do-handle-request-leave") - CommonResult doHandleRequestLeave(TaskStatusCallApiReqDTO taskStatusCallApiReqDTO); + CommonResult doHandleRequestLeave(@RequestBody TaskStatusCallApiReqDTO taskStatusCallApiReqDTO); @Operation(summary = "处理请求进入") @PostMapping("/do-handle-request-enter") - CommonResult doHandleRequestEnter(TaskStatusCallApiReqDTO taskStatusCallApiReqDTO); + CommonResult doHandleRequestEnter(@RequestBody TaskStatusCallApiReqDTO taskStatusCallApiReqDTO); } diff --git a/nl-framework/nl-spring-boot-starter-execute/src/main/java/cn/code/nl/framework/execute/biz/api/lms/LmsTaskCommonApi.java b/nl-framework/nl-spring-boot-starter-execute/src/main/java/cn/code/nl/framework/execute/biz/api/lms/LmsTaskCommonApi.java index 06c2efc1..ac559a20 100644 --- a/nl-framework/nl-spring-boot-starter-execute/src/main/java/cn/code/nl/framework/execute/biz/api/lms/LmsTaskCommonApi.java +++ b/nl-framework/nl-spring-boot-starter-execute/src/main/java/cn/code/nl/framework/execute/biz/api/lms/LmsTaskCommonApi.java @@ -11,7 +11,7 @@ import io.swagger.v3.oas.annotations.tags.Tag; * @Author: liyongde * @Date: 2026/7/15 14:39 */ -@FeignClient(name = RpcConstants.LMS_NAME, path = "/lms", primary = false) // TODO 芋艿:fallbackFactory = +@FeignClient(name = RpcConstants.LMS_NAME, path = RpcConstants.RPC_API_PREFIX + "/lms", primary = false) // TODO 芋艿:fallbackFactory = @Tag(name = "RPC 服务 - lms任务") public interface LmsTaskCommonApi extends TaskCommonApi { } diff --git a/nl-framework/nl-spring-boot-starter-execute/src/main/java/cn/code/nl/framework/execute/biz/api/wms/WmsTaskCommonApi.java b/nl-framework/nl-spring-boot-starter-execute/src/main/java/cn/code/nl/framework/execute/biz/api/wms/WmsTaskCommonApi.java index dd2a83b3..d4977342 100644 --- a/nl-framework/nl-spring-boot-starter-execute/src/main/java/cn/code/nl/framework/execute/biz/api/wms/WmsTaskCommonApi.java +++ b/nl-framework/nl-spring-boot-starter-execute/src/main/java/cn/code/nl/framework/execute/biz/api/wms/WmsTaskCommonApi.java @@ -11,7 +11,7 @@ import io.swagger.v3.oas.annotations.tags.Tag; * @Author: liyongde * @Date: 2026/7/15 14:48 */ -@FeignClient(name = RpcConstants.WMS_NAME, path = "/wms", primary = false) // TODO 芋艿:fallbackFactory = +@FeignClient(name = RpcConstants.WMS_NAME, path = RpcConstants.RPC_API_PREFIX + "/wms", primary = false) // TODO 芋艿:fallbackFactory = @Tag(name = "RPC 服务 - wms任务") public interface WmsTaskCommonApi extends TaskCommonApi { } diff --git a/nl-framework/nl-spring-boot-starter-execute/src/main/java/cn/code/nl/framework/execute/biz/vo/AcsApplyActionRespVO.java b/nl-framework/nl-spring-boot-starter-execute/src/main/java/cn/code/nl/framework/execute/biz/vo/AcsApplyActionRespVO.java index 5096efe6..2a395188 100644 --- a/nl-framework/nl-spring-boot-starter-execute/src/main/java/cn/code/nl/framework/execute/biz/vo/AcsApplyActionRespVO.java +++ b/nl-framework/nl-spring-boot-starter-execute/src/main/java/cn/code/nl/framework/execute/biz/vo/AcsApplyActionRespVO.java @@ -1,6 +1,5 @@ package cn.code.nl.framework.execute.biz.vo; -import com.alibaba.fastjson.JSONObject; import lombok.Data; /** @@ -25,5 +24,5 @@ public class AcsApplyActionRespVO { /** * 数据 */ - private JSONObject data; + private Object data; } diff --git a/nl-framework/nl-spring-boot-starter-execute/src/main/java/cn/code/nl/framework/execute/core/dto/TaskExecuteDTO.java b/nl-framework/nl-spring-boot-starter-execute/src/main/java/cn/code/nl/framework/execute/core/dto/TaskExecuteDTO.java index dc07f545..e0cff2a4 100644 --- a/nl-framework/nl-spring-boot-starter-execute/src/main/java/cn/code/nl/framework/execute/core/dto/TaskExecuteDTO.java +++ b/nl-framework/nl-spring-boot-starter-execute/src/main/java/cn/code/nl/framework/execute/core/dto/TaskExecuteDTO.java @@ -2,7 +2,10 @@ package cn.code.nl.framework.execute.core.dto; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Data; +import lombok.NoArgsConstructor; import java.util.Map; @@ -12,6 +15,9 @@ import java.util.Map; * @Date: 2026/7/16 14:57 */ @Data +@Builder +@NoArgsConstructor +@AllArgsConstructor public class TaskExecuteDTO { @Schema(description = "任务id") diff --git a/nl-module-base/nl-module-base-api/src/main/java/cn/code/nl/module/base/api/classstandard/ClassStandardApi.java b/nl-module-base/nl-module-base-api/src/main/java/cn/code/nl/module/base/api/classstandard/ClassStandardApi.java new file mode 100644 index 00000000..503e325a --- /dev/null +++ b/nl-module-base/nl-module-base-api/src/main/java/cn/code/nl/module/base/api/classstandard/ClassStandardApi.java @@ -0,0 +1,34 @@ +package cn.code.nl.module.base.api.classstandard; + +import cn.code.nl.framework.common.pojo.CommonResult; +import cn.code.nl.module.base.enums.ApiConstants; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; + +import java.util.List; + +/** + * RPC 服务 - 基础数据分类标准 + * + * @author zhouz + */ +@FeignClient(name = ApiConstants.NAME) +@Tag(name = "RPC 服务 - 基础数据分类标准") +public interface ClassStandardApi { + + String PREFIX = ApiConstants.PREFIX + "/class-standard"; + + /** + * 根据分类编码获取该分类及所有子分类编码 + * + * @param classCode 分类编码 + * @return 分类编码列表 + */ + @GetMapping(PREFIX + "/code-list-by-code") + @Operation(summary = "根据分类编码获取该分类及所有子分类编码") + CommonResult> getClassStandardCodeListByCode(@RequestParam("classCode") String classCode); + +} diff --git a/nl-module-base/nl-module-base-server/src/main/java/cn/code/nl/module/base/api/ClassStandardApiController.java b/nl-module-base/nl-module-base-server/src/main/java/cn/code/nl/module/base/api/ClassStandardApiController.java new file mode 100644 index 00000000..78f90bbe --- /dev/null +++ b/nl-module-base/nl-module-base-server/src/main/java/cn/code/nl/module/base/api/ClassStandardApiController.java @@ -0,0 +1,46 @@ +package cn.code.nl.module.base.api; + +import cn.code.nl.framework.common.pojo.CommonResult; +import cn.code.nl.module.base.api.classstandard.ClassStandardApi; +import cn.code.nl.module.base.dal.dataobject.classstandard.ClassStandardDO; +import cn.code.nl.module.base.service.classstandard.ClassStandardService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +import static cn.code.nl.framework.common.pojo.CommonResult.success; +import static cn.code.nl.framework.common.util.collection.CollectionUtils.convertList; + +/** + * RPC API - 基础数据分类标准(供其它模块 Feign 调用) + * + * @author zhouz + */ +@Tag(name = "RPC API - 基础数据分类标准") +@RestController +@RequestMapping("/rpc-api/base/class-standard") +@Validated +public class ClassStandardApiController implements ClassStandardApi { + + @Resource + private ClassStandardService classStandardService; + + /** + * 根据分类编码获取该分类及所有子分类编码 + */ + @GetMapping("/code-list-by-code") + @Operation(summary = "根据分类编码获取该分类及所有子分类编码") + @Override + public CommonResult> getClassStandardCodeListByCode(@RequestParam("classCode") String classCode) { + List list = classStandardService.getClassStandardListByCode(classCode); + return success(convertList(list, ClassStandardDO::getClassCode)); + } + +} diff --git a/nl-module-base/nl-module-base-server/src/main/java/cn/code/nl/module/base/controller/admin/classstandard/vo/ClassStandardSimpleRespVO.java b/nl-module-base/nl-module-base-server/src/main/java/cn/code/nl/module/base/controller/admin/classstandard/vo/ClassStandardSimpleRespVO.java index a4de7532..12ba7fe9 100644 --- a/nl-module-base/nl-module-base-server/src/main/java/cn/code/nl/module/base/controller/admin/classstandard/vo/ClassStandardSimpleRespVO.java +++ b/nl-module-base/nl-module-base-server/src/main/java/cn/code/nl/module/base/controller/admin/classstandard/vo/ClassStandardSimpleRespVO.java @@ -15,6 +15,9 @@ public class ClassStandardSimpleRespVO { @Schema(description = "分类标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") private Long classId; + @Schema(description = "分类编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "原材料") + private String classCode; + @Schema(description = "分类名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "原材料") private String className; diff --git a/nl-module-lms/nl-module-lms-server/pom.xml b/nl-module-lms/nl-module-lms-server/pom.xml index 562d3f5f..b415622a 100644 --- a/nl-module-lms/nl-module-lms-server/pom.xml +++ b/nl-module-lms/nl-module-lms-server/pom.xml @@ -32,7 +32,11 @@ nl-module-lms-api ${revision} - + + cn.nl.cloud + nl-module-task-api + ${revision} + cn.nl.cloud diff --git a/nl-module-lms/nl-module-lms-server/src/main/java/cn/code/nl/module/lms/api/LmsTaskExecuteApiImpl.java b/nl-module-lms/nl-module-lms-server/src/main/java/cn/code/nl/module/lms/api/LmsTaskExecuteApiImpl.java new file mode 100644 index 00000000..994681d3 --- /dev/null +++ b/nl-module-lms/nl-module-lms-server/src/main/java/cn/code/nl/module/lms/api/LmsTaskExecuteApiImpl.java @@ -0,0 +1,21 @@ +package cn.code.nl.module.lms.api; + +import cn.code.nl.framework.common.enums.RpcConstants; +import cn.code.nl.framework.execute.biz.api.AbstractTaskCommonApiImpl; +import cn.code.nl.framework.execute.biz.api.lms.LmsTaskCommonApi; +import org.springframework.context.annotation.Primary; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * LMS 任务通用 API 实现 + * + * @author liyongde + */ +@RestController +@Validated +@Primary +@RequestMapping(RpcConstants.LMS_PREFIX) +public class LmsTaskExecuteApiImpl extends AbstractTaskCommonApiImpl implements LmsTaskCommonApi { +} diff --git a/nl-module-lms/nl-module-lms-server/src/main/java/cn/code/nl/module/lms/demo/DemoApi.java b/nl-module-lms/nl-module-lms-server/src/main/java/cn/code/nl/module/lms/demo/DemoApi.java deleted file mode 100644 index a1b44f70..00000000 --- a/nl-module-lms/nl-module-lms-server/src/main/java/cn/code/nl/module/lms/demo/DemoApi.java +++ /dev/null @@ -1,80 +0,0 @@ -package cn.code.nl.module.lms.demo; - -import cn.code.nl.framework.common.pojo.CommonResult; -import cn.code.nl.framework.execute.biz.api.lms.LmsTaskCommonApi; -import cn.code.nl.framework.execute.biz.dto.TaskStatusCallApiReqDTO; -import cn.code.nl.framework.execute.biz.vo.AcsApplyActionRespVO; -import com.alibaba.fastjson.JSON; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.RestController; - -import static cn.code.nl.framework.common.pojo.CommonResult.success; - -/** - * - * @Author: liyongde - * @Date: 2026/7/15 15:25 - */ -@RestController // 提供 RESTful API 接口,给 Feign 调用 -@Validated -public class DemoApi implements LmsTaskCommonApi { - @Override - public CommonResult doHandlePicked(TaskStatusCallApiReqDTO taskStatusCallApiReqDTO) { - // 构建外层VO - AcsApplyActionRespVO respVO = new AcsApplyActionRespVO(); - respVO.setTaskId(10001L); - respVO.setTaskCode("ACS20260716001"); - // 内层data赋值 "success" - return success(respVO); - } - - @Override - public CommonResult doHandleApplyAgain(TaskStatusCallApiReqDTO taskStatusCallApiReqDTO) { - DemoRespVO demoRespVO = new DemoRespVO(); - demoRespVO.setTargetPoint("A_10001"); - - // 构建外层VO - AcsApplyActionRespVO respVO = new AcsApplyActionRespVO(); - respVO.setTaskId(10001L); - respVO.setTaskCode("ACS20260716001"); - // 内层data赋值 "success" - respVO.setData(JSON.parseObject(JSON.toJSONString(demoRespVO))); - return success(respVO); - } - - @Override - public CommonResult doHandleRequestRelease(TaskStatusCallApiReqDTO taskStatusCallApiReqDTO) { - // 构建外层VO - AcsApplyActionRespVO respVO = new AcsApplyActionRespVO(); - respVO.setTaskId(10001L); - respVO.setTaskCode("ACS20260716001"); - return success(respVO); - } - - @Override - public CommonResult doHandleRequestPick(TaskStatusCallApiReqDTO taskStatusCallApiReqDTO) { - // 构建外层VO - AcsApplyActionRespVO respVO = new AcsApplyActionRespVO(); - respVO.setTaskId(10001L); - respVO.setTaskCode("ACS20260716001"); - return success(respVO); - } - - @Override - public CommonResult doHandleRequestLeave(TaskStatusCallApiReqDTO taskStatusCallApiReqDTO) { - // 构建外层VO - AcsApplyActionRespVO respVO = new AcsApplyActionRespVO(); - respVO.setTaskId(10001L); - respVO.setTaskCode("ACS20260716001"); - return success(respVO); - } - - @Override - public CommonResult doHandleRequestEnter(TaskStatusCallApiReqDTO taskStatusCallApiReqDTO) { - // 构建外层VO - AcsApplyActionRespVO respVO = new AcsApplyActionRespVO(); - respVO.setTaskId(10001L); - respVO.setTaskCode("ACS20260716001"); - return success(respVO); - } -} diff --git a/nl-module-lms/nl-module-lms-server/src/main/java/cn/code/nl/module/lms/manage/package-info.java b/nl-module-lms/nl-module-lms-server/src/main/java/cn/code/nl/module/lms/manage/package-info.java new file mode 100644 index 00000000..9e091556 --- /dev/null +++ b/nl-module-lms/nl-module-lms-server/src/main/java/cn/code/nl/module/lms/manage/package-info.java @@ -0,0 +1,6 @@ +/** + * + * @Author: liyongde + * @Date: 2026/7/23 14:41 + */ +package cn.code.nl.module.lms.manage; \ No newline at end of file diff --git a/nl-module-lms/nl-module-lms-server/src/main/java/cn/code/nl/module/lms/mq/consumer/LmsTaskStatusChangeConsumer.java b/nl-module-lms/nl-module-lms-server/src/main/java/cn/code/nl/module/lms/mq/consumer/LmsTaskStatusChangeConsumer.java new file mode 100644 index 00000000..ae2959ca --- /dev/null +++ b/nl-module-lms/nl-module-lms-server/src/main/java/cn/code/nl/module/lms/mq/consumer/LmsTaskStatusChangeConsumer.java @@ -0,0 +1,118 @@ +package cn.code.nl.module.lms.mq.consumer; + +import cn.code.nl.framework.common.pojo.CommonResult; +import cn.code.nl.framework.execute.core.AbstractTask; +import cn.code.nl.framework.execute.core.TaskFactory; +import cn.code.nl.framework.execute.core.dto.TaskExecuteDTO; +import cn.code.nl.module.task.api.TransportTaskApi; +import cn.code.nl.module.task.dto.TaskInfoDTO; +import cn.code.nl.module.task.enums.TransportTaskStatusEnum; +import cn.code.nl.module.task.message.TaskEventMessage; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; +import org.apache.rocketmq.spring.core.RocketMQListener; +import org.redisson.api.RLock; +import org.redisson.api.RedissonClient; +import org.springframework.stereotype.Component; + +import static cn.code.nl.module.task.message.LockKeyConstants.TASK_STATUS_CHANGE_LOCK_KEY; + +/** + * 监听任务状态变更,根据 handleCode 定位任务子类并执行完成/取消逻辑 + * + * @Author: liyongde + * @Date: 2026/7/20 10:28 + */ +@Slf4j +@Component +@RocketMQMessageListener( + topic = "${rocketmq.consumer.lms-task-operate.topic}", + consumerGroup = "${rocketmq.consumer.lms-task-operate.group}" +) +public class LmsTaskStatusChangeConsumer implements RocketMQListener { + + @Resource + private TaskFactory taskFactory; + + @Resource + private TransportTaskApi transportTaskApi; + + @Resource + private RedissonClient redissonClient; + + @Override + public void onMessage(TaskEventMessage message) { + String handleCode = message.getHandleCode(); + String eventType = message.getEventType(); + log.info("收到任务状态变更消息, handleCode={}, eventType={}, taskId={}", handleCode, eventType, message.getTaskId()); + + RLock lock = redissonClient.getLock(TASK_STATUS_CHANGE_LOCK_KEY + message.getTaskId()); + if (!lock.tryLock()) { + log.warn("任务状态变更消息正在消费中,等待 MQ 重试, taskId={}, eventType={}", message.getTaskId(), eventType); + throw new IllegalStateException("任务状态变更消息正在消费中"); + } + try { + if (!isTaskCallbackPending(message)) { + return; + } + executeTaskHandler(message, handleCode, eventType); + } finally { + if (lock.isHeldByCurrentThread()) { + lock.unlock(); + } + } + } + + /** + * 判断任务是否处于当前事件对应的待业务处理状态 + */ + private boolean isTaskCallbackPending(TaskEventMessage message) { + String eventType = message.getEventType(); + String expectedStatus; + if (TaskEventMessage.EVENT_TYPE_FINISHED.equals(eventType)) { + expectedStatus = TransportTaskStatusEnum.FINISHED_CALLBACK_PENDING.getCode(); + } else if (TaskEventMessage.EVENT_TYPE_CANCELLED.equals(eventType)) { + expectedStatus = TransportTaskStatusEnum.CANCEL_CALLBACK_PENDING.getCode(); + } else { + log.warn("未知任务事件类型, eventType={}, taskId={}", eventType, message.getTaskId()); + return false; + } + + CommonResult result = transportTaskApi.getTaskById(message.getTaskId()); + TaskInfoDTO taskInfo = result.getCheckedData(); + if (taskInfo == null) { + log.warn("任务不存在,跳过任务状态变更消息, taskId={}, eventType={}", message.getTaskId(), eventType); + return false; + } + if (!expectedStatus.equals(taskInfo.getTaskStatus())) { + log.info("任务状态已处理,跳过重复消息, taskId={}, eventType={}, currentStatus={}, expectedStatus={}", + message.getTaskId(), eventType, taskInfo.getTaskStatus(), expectedStatus); + return false; + } + return true; + } + + /** + * 执行任务完成或取消业务处理器 + */ + private void executeTaskHandler(TaskEventMessage message, String handleCode, String eventType) { + AbstractTask task = taskFactory.getTask(handleCode); + if (task == null) { + log.warn("未找到对应任务处理器, handleCode={}", handleCode); + return; + } + + TaskExecuteDTO dto = new TaskExecuteDTO(); + dto.setTaskId(message.getTaskId()); + dto.setPayload(message.getPayload()); + + if (TaskEventMessage.EVENT_TYPE_FINISHED.equals(eventType)) { + task.doHandleFinish(dto); + } else if (TaskEventMessage.EVENT_TYPE_CANCELLED.equals(eventType)) { + task.doHandleCancel(dto); + } else { + log.warn("未知事件类型, eventType={}, handleCode={}", eventType, handleCode); + } + } +} diff --git a/nl-module-lms/nl-module-lms-server/src/main/java/cn/code/nl/module/lms/mq/package-info.java b/nl-module-lms/nl-module-lms-server/src/main/java/cn/code/nl/module/lms/mq/package-info.java new file mode 100644 index 00000000..dfa0616a --- /dev/null +++ b/nl-module-lms/nl-module-lms-server/src/main/java/cn/code/nl/module/lms/mq/package-info.java @@ -0,0 +1,6 @@ +/** + * + * @Author: liyongde + * @Date: 2026/7/23 16:21 + */ +package cn.code.nl.module.lms.mq; \ No newline at end of file diff --git a/nl-module-lms/nl-module-lms-server/src/main/resources/application-mq-dev.yaml b/nl-module-lms/nl-module-lms-server/src/main/resources/application-mq-dev.yaml new file mode 100644 index 00000000..cb823736 --- /dev/null +++ b/nl-module-lms/nl-module-lms-server/src/main/resources/application-mq-dev.yaml @@ -0,0 +1,11 @@ +--- #################### MQ 消息队列相关配置 #################### +# rocketmq 配置项,对应 RocketMQProperties 配置类 +rocketmq: + name-server: 192.168.81.193:9876 + producer: + group: lms_producer_dev_group # 事务消息需要配置一样 + send-message-timeout: 3000 + consumer: + lms-task-operate: + group: lms_task_status_change_dev_group + topic: lms_task_status_change_dev_topic \ No newline at end of file diff --git a/nl-module-lms/nl-module-lms-server/src/main/resources/application.yaml b/nl-module-lms/nl-module-lms-server/src/main/resources/application.yaml index aa71aa8b..d811bc01 100644 --- a/nl-module-lms/nl-module-lms-server/src/main/resources/application.yaml +++ b/nl-module-lms/nl-module-lms-server/src/main/resources/application.yaml @@ -13,6 +13,7 @@ spring: import: - optional:classpath:application-${spring.profiles.active}.yaml # 加载【本地】配置 - optional:nacos:${spring.application.name}-${spring.profiles.active}.yaml # 加载【Nacos】的配置 + - optional:classpath:application-mq-${spring.profiles.active}.yaml # 加载 MQ 配置 # Servlet 配置 servlet: diff --git a/nl-module-system/nl-module-system-api/src/main/java/cn/code/nl/module/system/enums/LogRecordConstants.java b/nl-module-system/nl-module-system-api/src/main/java/cn/code/nl/module/system/enums/LogRecordConstants.java index 92daa953..75fdbaf9 100644 --- a/nl-module-system/nl-module-system-api/src/main/java/cn/code/nl/module/system/enums/LogRecordConstants.java +++ b/nl-module-system/nl-module-system-api/src/main/java/cn/code/nl/module/system/enums/LogRecordConstants.java @@ -34,4 +34,9 @@ public interface LogRecordConstants { String WMS_GROUP_PLATE = "组盘信息"; String WMS_GROUP_PLATE_UPDATE = "修改组盘信息"; String WMS_GROUP_PLATE_SUCCESS = "{{#loginUserNickname}} 更新了组盘信息: {{#group.vehicleCode}}"; + + // ======================= TASK 任务信息 ======================= + String TASK_INFO = "任务信息"; + String TASK_INFO_OPERATE_TYPE = "操作任务状态"; + String TASK_INFO_OPERATE_SUCCESS = "{{#loginUserNickname}}对任务操作了{{#operateName}}"; } diff --git a/nl-module-task/nl-module-task-api/src/main/java/cn/code/nl/module/task/api/TransportTaskApi.java b/nl-module-task/nl-module-task-api/src/main/java/cn/code/nl/module/task/api/TransportTaskApi.java index 05928de0..e6567d05 100644 --- a/nl-module-task/nl-module-task-api/src/main/java/cn/code/nl/module/task/api/TransportTaskApi.java +++ b/nl-module-task/nl-module-task-api/src/main/java/cn/code/nl/module/task/api/TransportTaskApi.java @@ -14,6 +14,7 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestParam; import jakarta.validation.Valid; +import java.util.List; /** * Task 服务 RPC API 接口 @@ -34,6 +35,10 @@ public interface TransportTaskApi { @Operation(summary = "接收 LMS/WMS 业务处理结果") CommonResult receiveCallbackResult(@Valid @RequestBody TaskCallbackResultReqDTO reqDTO); + @PostMapping(PREFIX + "/issue") + @Operation(summary = "根据 taskId 下发搬运任务") + CommonResult issueTransportTask(@RequestParam("taskId") Long taskId); + @GetMapping(PREFIX + "/getTaskById") @Operation(summary = "根据 taskId 查询任务") CommonResult getTaskById(@RequestParam("taskId") Long taskId); @@ -41,4 +46,14 @@ public interface TransportTaskApi { @GetMapping(PREFIX + "/getTaskByCode") @Operation(summary = "根据 taskCode 查询任务") CommonResult getTaskByCode(@RequestParam("taskCode") String taskCode); + + @GetMapping(PREFIX + "/getRunningTaskByMaterialId") + @Operation(summary = "根据 materialId 和 ownerService 查询运行中任务") + CommonResult> getRunningTaskByMaterialId(@RequestParam("materialId") Long materialId, + @RequestParam("ownerService") String ownerService); + + @GetMapping(PREFIX + "/getRunningTaskByMaterialCode") + @Operation(summary = "根据 materialCode 和 ownerService 查询运行中任务") + CommonResult> getRunningTaskByMaterialCode(@RequestParam("materialCode") String materialCode, + @RequestParam("ownerService") String ownerService); } diff --git a/nl-module-task/nl-module-task-api/src/main/java/cn/code/nl/module/task/dto/TaskInfoDTO.java b/nl-module-task/nl-module-task-api/src/main/java/cn/code/nl/module/task/dto/TaskInfoDTO.java index b4be444f..12177096 100644 --- a/nl-module-task/nl-module-task-api/src/main/java/cn/code/nl/module/task/dto/TaskInfoDTO.java +++ b/nl-module-task/nl-module-task-api/src/main/java/cn/code/nl/module/task/dto/TaskInfoDTO.java @@ -91,6 +91,14 @@ public class TaskInfoDTO implements Serializable { * 载具编码2 */ private String vehicleCode2; + /** + * 物料id + */ + private Long materialId; + /** + * 物料编码 + */ + private String materialCode; /** * 车号 */ 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 7a00c298..2334120f 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 @@ -21,14 +21,6 @@ public class TransportTaskCreateReqDTO { @NotEmpty(message = "业务归属服务不能为空") private String ownerService; - @Schema(description = "业务类型", requiredMode = Schema.RequiredMode.REQUIRED) - @NotEmpty(message = "业务类型不能为空") - private String bizType; - - @Schema(description = "业务侧标识", requiredMode = Schema.RequiredMode.REQUIRED) - @NotEmpty(message = "业务侧标识不能为空") - private String bizId; - @Schema(description = "业务回调处理器编码", requiredMode = Schema.RequiredMode.REQUIRED) @NotEmpty(message = "业务回调处理器编码不能为空") private String handleCode; @@ -59,6 +51,12 @@ public class TransportTaskCreateReqDTO { @Schema(description = "载具编码2") private String vehicleCode2; + @Schema(description = "物料id") + private Long materialId; + + @Schema(description = "物料编码") + private String materialCode; + @Schema(description = "优先级") private String priority; 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 41e2a96a..6d7f5290 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 @@ -16,5 +16,6 @@ public interface ErrorCodeConstants { ErrorCode TRANSPORT_TASK_RUNNING_ALREADY_EXIST = new ErrorCode(5005, "已存在运行中的任务"); ErrorCode TRANSPORT_TASK_OPERATION_NOT_SUPPORTED = new ErrorCode(5006, "不支持的任务操作类型"); ErrorCode TRANSPORT_TASK_ALREADY_FINAL = new ErrorCode(5007, "任务已处于终态,不允许操作"); + ErrorCode TRANSPORT_TASK_AUTO_ISSUE_NOT_ALLOW_MANUAL = new ErrorCode(5008, "自动任务不允许手动下发"); } diff --git a/nl-module-task/nl-module-task-api/src/main/java/cn/code/nl/module/task/enums/TaskCreateModelEnum.java b/nl-module-task/nl-module-task-api/src/main/java/cn/code/nl/module/task/enums/TaskCreateModelEnum.java new file mode 100644 index 00000000..6c919a64 --- /dev/null +++ b/nl-module-task/nl-module-task-api/src/main/java/cn/code/nl/module/task/enums/TaskCreateModelEnum.java @@ -0,0 +1,22 @@ +package cn.code.nl.module.task.enums; + +import lombok.Getter; + +/** + * + * @Author: liyongde + * @Date: 2026/7/24 16:13 + */ +@Getter +public enum TaskCreateModelEnum { + AUTO("1", "自动创建"), + MANUAL("0", "创建人工"); + + private final String code; + private final String name; + + TaskCreateModelEnum(String code, String name) { + this.code = code; + this.name = name; + } +} diff --git a/nl-module-task/nl-module-task-api/src/main/java/cn/code/nl/module/task/enums/TaskOperationTypeEnum.java b/nl-module-task/nl-module-task-api/src/main/java/cn/code/nl/module/task/enums/TaskOperationTypeEnum.java index 508e2e22..d89540d6 100644 --- a/nl-module-task/nl-module-task-api/src/main/java/cn/code/nl/module/task/enums/TaskOperationTypeEnum.java +++ b/nl-module-task/nl-module-task-api/src/main/java/cn/code/nl/module/task/enums/TaskOperationTypeEnum.java @@ -15,6 +15,8 @@ public enum TaskOperationTypeEnum { EXECUTING("EXECUTING", "执行中", true, false), + ISSUE("ISSUE", "下发任务", false, true), + FINISHED("FINISHED", "完成任务", true, true), CANCELLED("CANCELLED", "取消任务", true, true), diff --git a/nl-module-task/nl-module-task-api/src/main/java/cn/code/nl/module/task/message/LockKeyConstants.java b/nl-module-task/nl-module-task-api/src/main/java/cn/code/nl/module/task/message/LockKeyConstants.java new file mode 100644 index 00000000..689443b6 --- /dev/null +++ b/nl-module-task/nl-module-task-api/src/main/java/cn/code/nl/module/task/message/LockKeyConstants.java @@ -0,0 +1,12 @@ +package cn.code.nl.module.task.message; + +/** + * 全局锁的key 或 前缀 + * @Author: liyongde + * @Date: 2026/7/27 10:16 + */ +public interface LockKeyConstants { + + /** 任务状态变更消费锁前缀 */ + String TASK_STATUS_CHANGE_LOCK_KEY = "task:task-status-change:"; +} diff --git a/nl-module-task/nl-module-task-server/pom.xml b/nl-module-task/nl-module-task-server/pom.xml index 31c04b18..0941178c 100644 --- a/nl-module-task/nl-module-task-server/pom.xml +++ b/nl-module-task/nl-module-task-server/pom.xml @@ -42,6 +42,11 @@ nl-module-task-api ${revision} + + cn.nl.cloud + nl-module-base-api + ${revision} + @@ -164,4 +169,4 @@ - \ No newline at end of file + diff --git a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/api/TransportTaskApiImpl.java b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/api/TransportTaskApiImpl.java index c397dcac..bd873ea6 100644 --- a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/api/TransportTaskApiImpl.java +++ b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/api/TransportTaskApiImpl.java @@ -10,6 +10,8 @@ import jakarta.annotation.Resource; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.RestController; +import java.util.List; + import static cn.code.nl.framework.common.pojo.CommonResult.success; /** @@ -36,6 +38,12 @@ public class TransportTaskApiImpl implements TransportTaskApi { return success(true); } + @Override + public CommonResult issueTransportTask(Long taskId) { + transportTaskService.issueTransportTask(taskId); + return success(true); + } + @Override public CommonResult getTaskById(Long taskId) { return success(transportTaskService.getTaskInfoById(taskId)); @@ -45,4 +53,14 @@ public class TransportTaskApiImpl implements TransportTaskApi { public CommonResult getTaskByCode(String taskCode) { return success(transportTaskService.getTaskInfoByCode(taskCode)); } + + @Override + public CommonResult> getRunningTaskByMaterialId(Long materialId, String ownerService) { + return success(transportTaskService.getRunningTaskInfoByMaterialId(materialId, ownerService)); + } + + @Override + public CommonResult> getRunningTaskByMaterialCode(String materialCode, String ownerService) { + return success(transportTaskService.getRunningTaskInfoByMaterialCode(materialCode, ownerService)); + } } diff --git a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/controller/admin/transporttask/TransportTaskController.java b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/controller/admin/transporttask/TransportTaskController.java index dc056006..5f452518 100644 --- a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/controller/admin/transporttask/TransportTaskController.java +++ b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/controller/admin/transporttask/TransportTaskController.java @@ -1,112 +1,112 @@ -package cn.code.nl.module.task.controller.admin.transporttask; - -import org.springframework.web.bind.annotation.*; -import jakarta.annotation.Resource; -import org.springframework.validation.annotation.Validated; -import org.springframework.security.access.prepost.PreAuthorize; -import io.swagger.v3.oas.annotations.tags.Tag; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.Operation; - -import jakarta.validation.constraints.*; -import jakarta.validation.*; -import jakarta.servlet.http.*; -import java.util.*; -import java.io.IOException; - -import cn.code.nl.framework.common.pojo.PageParam; -import cn.code.nl.framework.common.pojo.PageResult; -import cn.code.nl.framework.common.pojo.CommonResult; -import cn.code.nl.framework.common.util.object.BeanUtils; -import static cn.code.nl.framework.common.pojo.CommonResult.success; - -import cn.code.nl.framework.excel.core.util.ExcelUtils; - -import cn.code.nl.framework.apilog.core.annotation.ApiAccessLog; -import static cn.code.nl.framework.apilog.core.enums.OperateTypeEnum.*; - -import cn.code.nl.module.task.controller.admin.transporttask.vo.*; -import cn.code.nl.module.task.dal.dataobject.transporttask.TransportTaskDO; -import cn.code.nl.module.task.service.transporttask.TransportTaskService; - -@Tag(name = "管理后台 - 搬运任务") -@RestController -@RequestMapping("/task/transport-task") -@Validated -public class TransportTaskController { - - @Resource - private TransportTaskService transportTaskService; - - @PostMapping("/create") - @Operation(summary = "创建搬运任务") - @PreAuthorize("@ss.hasPermission('task:transport-task:create')") - public CommonResult createTransportTask(@Valid @RequestBody TransportTaskSaveReqVO createReqVO) { - return success(transportTaskService.createTransportTask(createReqVO)); - } - - @PutMapping("/update") - @Operation(summary = "更新搬运任务") - @PreAuthorize("@ss.hasPermission('task:transport-task:update')") - public CommonResult updateTransportTask(@Valid @RequestBody TransportTaskSaveReqVO updateReqVO) { - transportTaskService.updateTransportTask(updateReqVO); - return success(true); - } - - @PostMapping("/operate") - @Operation(summary = "PC 端操作搬运任务(完成/取消/强制完成)") - @PreAuthorize("@ss.hasPermission('task:transport-task:operate')") - public CommonResult operateTransportTask(@Valid @RequestBody TransportTaskOperateReqVO reqVO) { - transportTaskService.operateTransportTask(reqVO); - return success(true); - } - - @DeleteMapping("/delete") - @Operation(summary = "删除搬运任务") - @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('task:transport-task:delete')") - public CommonResult deleteTransportTask(@RequestParam("id") Long id) { - transportTaskService.deleteTransportTask(id); - return success(true); - } - - @DeleteMapping("/delete-list") - @Parameter(name = "ids", description = "编号", required = true) - @Operation(summary = "批量删除搬运任务") - @PreAuthorize("@ss.hasPermission('task:transport-task:delete')") - public CommonResult deleteTransportTaskList(@RequestParam("ids") List ids) { - transportTaskService.deleteTransportTaskListByIds(ids); - return success(true); - } - - @GetMapping("/get") - @Operation(summary = "获得搬运任务") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('task:transport-task:query')") - public CommonResult getTransportTask(@RequestParam("id") Long id) { - TransportTaskDO transportTask = transportTaskService.getTransportTask(id); - return success(BeanUtils.toBean(transportTask, TransportTaskRespVO.class)); - } - - @GetMapping("/page") - @Operation(summary = "获得搬运任务分页") - @PreAuthorize("@ss.hasPermission('task:transport-task:query')") - public CommonResult> getTransportTaskPage(@Valid TransportTaskPageReqVO pageReqVO) { - PageResult pageResult = transportTaskService.getTransportTaskPage(pageReqVO); - return success(BeanUtils.toBean(pageResult, TransportTaskRespVO.class)); - } - - @GetMapping("/export-excel") - @Operation(summary = "导出搬运任务 Excel") - @PreAuthorize("@ss.hasPermission('task:transport-task:export')") - @ApiAccessLog(operateType = EXPORT) - public void exportTransportTaskExcel(@Valid TransportTaskPageReqVO pageReqVO, - HttpServletResponse response) throws IOException { - pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); - List list = transportTaskService.getTransportTaskPage(pageReqVO).getList(); - // 导出 Excel - ExcelUtils.write(response, "搬运任务.xls", "数据", TransportTaskRespVO.class, - BeanUtils.toBean(list, TransportTaskRespVO.class)); - } - -} \ No newline at end of file +package cn.code.nl.module.task.controller.admin.transporttask; + +import org.springframework.web.bind.annotation.*; +import jakarta.annotation.Resource; +import org.springframework.validation.annotation.Validated; +import org.springframework.security.access.prepost.PreAuthorize; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Operation; + +import jakarta.validation.constraints.*; +import jakarta.validation.*; +import jakarta.servlet.http.*; +import java.util.*; +import java.io.IOException; + +import cn.code.nl.framework.common.pojo.PageParam; +import cn.code.nl.framework.common.pojo.PageResult; +import cn.code.nl.framework.common.pojo.CommonResult; +import cn.code.nl.framework.common.util.object.BeanUtils; +import static cn.code.nl.framework.common.pojo.CommonResult.success; + +import cn.code.nl.framework.excel.core.util.ExcelUtils; + +import cn.code.nl.framework.apilog.core.annotation.ApiAccessLog; +import static cn.code.nl.framework.apilog.core.enums.OperateTypeEnum.*; + +import cn.code.nl.module.task.controller.admin.transporttask.vo.*; +import cn.code.nl.module.task.dal.dataobject.transporttask.TransportTaskDO; +import cn.code.nl.module.task.service.transporttask.TransportTaskService; + +@Tag(name = "管理后台 - 搬运任务") +@RestController +@RequestMapping("/task/transport-task") +@Validated +public class TransportTaskController { + + @Resource + private TransportTaskService transportTaskService; + + @PostMapping("/create") + @Operation(summary = "创建搬运任务") + @PreAuthorize("@ss.hasPermission('task:transport-task:create')") + public CommonResult createTransportTask(@Valid @RequestBody TransportTaskSaveReqVO createReqVO) { + return success(transportTaskService.createTransportTask(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新搬运任务") + @PreAuthorize("@ss.hasPermission('task:transport-task:update')") + public CommonResult updateTransportTask(@Valid @RequestBody TransportTaskSaveReqVO updateReqVO) { + transportTaskService.updateTransportTask(updateReqVO); + return success(true); + } + + @PostMapping("/operate") + @Operation(summary = "PC 端操作搬运任务(下发/完成/取消/强制完成)") + @PreAuthorize("@ss.hasPermission('task:transport-task:operate')") + public CommonResult operateTransportTask(@Valid @RequestBody TransportTaskOperateReqVO reqVO) { + transportTaskService.operateTransportTask(reqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除搬运任务") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('task:transport-task:delete')") + public CommonResult deleteTransportTask(@RequestParam("id") Long id) { + transportTaskService.deleteTransportTask(id); + return success(true); + } + + @DeleteMapping("/delete-list") + @Parameter(name = "ids", description = "编号", required = true) + @Operation(summary = "批量删除搬运任务") + @PreAuthorize("@ss.hasPermission('task:transport-task:delete')") + public CommonResult deleteTransportTaskList(@RequestParam("ids") List ids) { + transportTaskService.deleteTransportTaskListByIds(ids); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得搬运任务") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('task:transport-task:query')") + public CommonResult getTransportTask(@RequestParam("id") Long id) { + TransportTaskDO transportTask = transportTaskService.getTransportTask(id); + return success(BeanUtils.toBean(transportTask, TransportTaskRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得搬运任务分页") + @PreAuthorize("@ss.hasPermission('task:transport-task:query')") + public CommonResult> getTransportTaskPage(@Valid TransportTaskPageReqVO pageReqVO) { + PageResult pageResult = transportTaskService.getTransportTaskPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, TransportTaskRespVO.class)); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出搬运任务 Excel") + @PreAuthorize("@ss.hasPermission('task:transport-task:export')") + @ApiAccessLog(operateType = EXPORT) + public void exportTransportTaskExcel(@Valid TransportTaskPageReqVO pageReqVO, + HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = transportTaskService.getTransportTaskPage(pageReqVO).getList(); + // 导出 Excel + ExcelUtils.write(response, "搬运任务.xls", "数据", TransportTaskRespVO.class, + BeanUtils.toBean(list, TransportTaskRespVO.class)); + } + +} diff --git a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/controller/admin/transporttask/vo/TransportTaskOperateReqVO.java b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/controller/admin/transporttask/vo/TransportTaskOperateReqVO.java index 1d56ada9..4fa3f70e 100644 --- a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/controller/admin/transporttask/vo/TransportTaskOperateReqVO.java +++ b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/controller/admin/transporttask/vo/TransportTaskOperateReqVO.java @@ -19,7 +19,7 @@ public class TransportTaskOperateReqVO { @NotNull(message = "任务ID不能为空") private Long taskId; - @Schema(description = "操作类型:FINISHED/CANCELLED/FORCE-FINISH", requiredMode = Schema.RequiredMode.REQUIRED) + @Schema(description = "操作类型:ISSUE/FINISHED/CANCELLED/FORCE-FINISH", requiredMode = Schema.RequiredMode.REQUIRED) @NotEmpty(message = "操作类型不能为空") private String operationType; } diff --git a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/controller/admin/transporttask/vo/TransportTaskPageReqVO.java b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/controller/admin/transporttask/vo/TransportTaskPageReqVO.java index 1576581b..ddb17613 100644 --- a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/controller/admin/transporttask/vo/TransportTaskPageReqVO.java +++ b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/controller/admin/transporttask/vo/TransportTaskPageReqVO.java @@ -34,6 +34,9 @@ public class TransportTaskPageReqVO extends PageParam { @Schema(description = "任务类型", example = "2") private String taskType; + @Schema(description = "任务类型及子类型编码列表", hidden = true) + private List taskTypeList; + @Schema(description = "任务状态(支持多选)", example = "[\"10\", \"50\"]") private List taskStatus; @@ -119,4 +122,4 @@ public class TransportTaskPageReqVO extends PageParam { @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) private LocalDateTime[] createTime; -} \ No newline at end of file +} diff --git a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/controller/admin/transporttask/vo/TransportTaskRespVO.java b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/controller/admin/transporttask/vo/TransportTaskRespVO.java index 387e8aa4..80e71307 100644 --- a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/controller/admin/transporttask/vo/TransportTaskRespVO.java +++ b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/controller/admin/transporttask/vo/TransportTaskRespVO.java @@ -1,162 +1,162 @@ -package cn.code.nl.module.task.controller.admin.transporttask.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.*; -import java.util.*; -import org.springframework.format.annotation.DateTimeFormat; -import java.time.LocalDateTime; -import cn.idev.excel.annotation.*; -import cn.code.nl.framework.excel.core.annotations.DictFormat; -import cn.code.nl.framework.excel.core.convert.DictConvert; - -@Schema(description = "管理后台 - 搬运任务 Response VO") -@Data -@ExcelIgnoreUnannotated -public class TransportTaskRespVO { - - @Schema(description = "任务标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "11528") - @ExcelProperty("任务标识") - private Long taskId; - - @Schema(description = "任务编码", requiredMode = Schema.RequiredMode.REQUIRED) - @ExcelProperty("任务编码") - private String taskCode; - - @Schema(description = "任务名称", example = "张三") - @ExcelProperty("任务名称") - private String taskName; - - @Schema(description = "业务归属服务:LMS/WMS,用于完成取消事件一级路由", requiredMode = Schema.RequiredMode.REQUIRED) - @ExcelProperty("业务归属服务:LMS/WMS,用于完成取消事件一级路由") - private String ownerService; - - @Schema(description = "业务类型", example = "2") - @ExcelProperty("业务类型") - private String bizType; - - @Schema(description = "业务侧标识", example = "31008") - @ExcelProperty("业务侧标识") - private String bizId; - - @Schema(description = "业务回调处理器编码") - @ExcelProperty("业务回调处理器编码") - private String handleCode; - - @Schema(description = "任务类型", example = "2") - @ExcelProperty("任务类型") - private String taskType; - - @Schema(description = "任务状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") - @ExcelProperty("任务状态") - private String taskStatus; - - @Schema(description = "ACS任务类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @ExcelProperty("ACS任务类型") - private String acsTaskType; - - @Schema(description = "AGV系统类型", example = "1") - @ExcelProperty("AGV系统类型") - private String agvSystemType; - - @Schema(description = "ACS外部任务号") - @ExcelProperty("ACS外部任务号") - private String externalTaskNo; - - @Schema(description = "取货点1") - @ExcelProperty("取货点1") - private String pointCode1; - - @Schema(description = "放货点1") - @ExcelProperty("放货点1") - private String pointCode2; - - @Schema(description = "取货点2") - @ExcelProperty("取货点2") - private String pointCode3; - - @Schema(description = "放货点2") - @ExcelProperty("放货点2") - private String pointCode4; - - @Schema(description = "载具类型", example = "2") - @ExcelProperty("载具类型") - private String vehicleType; - - @Schema(description = "载具数量", requiredMode = Schema.RequiredMode.REQUIRED) - @ExcelProperty("载具数量") - private Long vehicleQty; - - @Schema(description = "载具编码") - @ExcelProperty("载具编码") - private String vehicleCode; - - @Schema(description = "载具编码2") - @ExcelProperty("载具编码2") - private String vehicleCode2; - - @Schema(description = "车号") - @ExcelProperty("车号") - private String carNo; - - @Schema(description = "优先级", requiredMode = Schema.RequiredMode.REQUIRED) - @ExcelProperty("优先级") - private String priority; - - @Schema(description = "生产区域") - @ExcelProperty("生产区域") - private String productArea; - - @Schema(description = "是否自动下发", requiredMode = Schema.RequiredMode.REQUIRED) - @ExcelProperty("是否自动下发") - private String isAutoIssue; - - @Schema(description = "任务组标识", example = "21169") - @ExcelProperty("任务组标识") - private Long taskGroupId; - - @Schema(description = "任务组顺序号", requiredMode = Schema.RequiredMode.REQUIRED) - @ExcelProperty("任务组顺序号") - private Long sortSeq; - - @Schema(description = "任务完成类型", example = "1") - @ExcelProperty("任务完成类型") - private String finishedType; - - @Schema(description = "业务回调状态:PENDING/SUCCESS/FAILED", example = "2") - @ExcelProperty("业务回调状态:PENDING/SUCCESS/FAILED") - private String callbackStatus; - - @Schema(description = "业务回调重试次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "2146") - @ExcelProperty("业务回调重试次数") - private Integer callbackRetryCount; - - @Schema(description = "业务回调失败原因") - @ExcelProperty("业务回调失败原因") - private String callbackErrorMsg; - - @Schema(description = "生成方式", requiredMode = Schema.RequiredMode.REQUIRED) - @ExcelProperty(value = "生成方式", converter = DictConvert.class) - @DictFormat("user_type") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中 - private String createMode; - - @Schema(description = "创建任务请求参数") - @ExcelProperty("创建任务请求参数") - private String requestParam; - - @Schema(description = "下发ACS的AcsTaskDto扩展报文") - @ExcelProperty("下发ACS的AcsTaskDto扩展报文") - private String dispatchParam; - - @Schema(description = "ACS反馈参数") - @ExcelProperty("ACS反馈参数") - private String resultParam; - - @Schema(description = "备注", example = "你说的对") - @ExcelProperty("备注") - private String remark; - - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) - @ExcelProperty("创建时间") - private LocalDateTime createTime; - -} \ No newline at end of file +package cn.code.nl.module.task.controller.admin.transporttask.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import java.util.*; +import org.springframework.format.annotation.DateTimeFormat; +import java.time.LocalDateTime; +import cn.idev.excel.annotation.*; +import cn.code.nl.framework.excel.core.annotations.DictFormat; +import cn.code.nl.framework.excel.core.convert.DictConvert; + +@Schema(description = "管理后台 - 搬运任务 Response VO") +@Data +@ExcelIgnoreUnannotated +public class TransportTaskRespVO { + + @Schema(description = "任务标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "11528") + @ExcelProperty("任务标识") + private Long taskId; + + @Schema(description = "任务编码", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("任务编码") + private String taskCode; + + @Schema(description = "任务名称", example = "张三") + @ExcelProperty("任务名称") + private String taskName; + + @Schema(description = "业务归属服务:LMS/WMS,用于完成取消事件一级路由", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("业务归属服务:LMS/WMS,用于完成取消事件一级路由") + private String ownerService; + + @Schema(description = "业务类型", example = "2") + @ExcelProperty("业务类型") + private String bizType; + + @Schema(description = "业务侧标识", example = "31008") + @ExcelProperty("业务侧标识") + private String bizId; + + @Schema(description = "业务回调处理器编码") + @ExcelProperty("业务回调处理器编码") + private String handleCode; + + @Schema(description = "任务类型", example = "2") + @ExcelProperty("任务类型") + private String taskType; + + @Schema(description = "任务状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @ExcelProperty("任务状态") + private String taskStatus; + + @Schema(description = "ACS任务类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @ExcelProperty("ACS任务类型") + private String acsTaskType; + + @Schema(description = "AGV系统类型", example = "1") + @ExcelProperty("AGV系统类型") + private String agvSystemType; + + @Schema(description = "ACS外部任务号") + @ExcelProperty("ACS外部任务号") + private String externalTaskNo; + + @Schema(description = "取货点1") + @ExcelProperty("取货点1") + private String pointCode1; + + @Schema(description = "放货点1") + @ExcelProperty("放货点1") + private String pointCode2; + + @Schema(description = "取货点2") + @ExcelProperty("取货点2") + private String pointCode3; + + @Schema(description = "放货点2") + @ExcelProperty("放货点2") + private String pointCode4; + + @Schema(description = "载具类型", example = "2") + @ExcelProperty("载具类型") + private String vehicleType; + + @Schema(description = "载具数量", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("载具数量") + private Long vehicleQty; + + @Schema(description = "载具编码") + @ExcelProperty("载具编码") + private String vehicleCode; + + @Schema(description = "载具编码2") + @ExcelProperty("载具编码2") + private String vehicleCode2; + + @Schema(description = "车号") + @ExcelProperty("车号") + private String carNo; + + @Schema(description = "优先级", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("优先级") + private String priority; + + @Schema(description = "生产区域") + @ExcelProperty("生产区域") + private String productArea; + + @Schema(description = "是否自动下发", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("是否自动下发") + private String isAutoIssue; + + @Schema(description = "任务组标识", example = "21169") + @ExcelProperty("任务组标识") + private Long taskGroupId; + + @Schema(description = "任务组顺序号", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("任务组顺序号") + private Long sortSeq; + + @Schema(description = "任务完成类型", example = "1") + @ExcelProperty("任务完成类型") + private String finishedType; + + @Schema(description = "业务回调状态:PENDING/SUCCESS/FAILED", example = "2") + @ExcelProperty("业务回调状态:PENDING/SUCCESS/FAILED") + private String callbackStatus; + + @Schema(description = "业务回调重试次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "2146") + @ExcelProperty("业务回调重试次数") + private Integer callbackRetryCount; + + @Schema(description = "业务回调失败原因") + @ExcelProperty("业务回调失败原因") + private String callbackErrorMsg; + + @Schema(description = "生成方式", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty(value = "生成方式", converter = DictConvert.class) + @DictFormat("task_create_mode") + private String createMode; + + @Schema(description = "创建任务请求参数") + @ExcelProperty("创建任务请求参数") + private String requestParam; + + @Schema(description = "下发ACS的AcsTaskDto扩展报文") + @ExcelProperty("下发ACS的AcsTaskDto扩展报文") + private String dispatchParam; + + @Schema(description = "ACS反馈参数") + @ExcelProperty("ACS反馈参数") + private String resultParam; + + @Schema(description = "备注", example = "你说的对") + @ExcelProperty("备注") + private String remark; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + private LocalDateTime createTime; + +} diff --git a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/convert/transporttask/TransportTaskConvert.java b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/convert/transporttask/TransportTaskConvert.java index 1f5ca666..fbdeab20 100644 --- a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/convert/transporttask/TransportTaskConvert.java +++ b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/convert/transporttask/TransportTaskConvert.java @@ -5,6 +5,8 @@ import cn.code.nl.module.task.dto.TaskInfoDTO; import org.mapstruct.Mapper; import org.mapstruct.factory.Mappers; +import java.util.List; + /** * 搬运任务 Convert * @@ -20,4 +22,9 @@ public interface TransportTaskConvert { * DO 转 RPC 全量信息 DTO(字段同名,零配置映射) */ TaskInfoDTO convert(TransportTaskDO bean); + + /** + * DO 列表转 RPC 全量信息 DTO 列表 + */ + List convertList(List list); } diff --git a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/dal/dataobject/transporttask/TransportTaskDO.java b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/dal/dataobject/transporttask/TransportTaskDO.java index 687fbc6c..a0c0c51a 100644 --- a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/dal/dataobject/transporttask/TransportTaskDO.java +++ b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/dal/dataobject/transporttask/TransportTaskDO.java @@ -1,170 +1,178 @@ -package cn.code.nl.module.task.dal.dataobject.transporttask; - -import lombok.*; -import java.util.*; -import java.time.LocalDateTime; -import java.time.LocalDateTime; -import com.baomidou.mybatisplus.annotation.*; -import cn.code.nl.framework.mybatis.core.dataobject.BaseDO; - -/** - * 搬运任务 DO - * - * @author 诺力管理员 - */ -@TableName("task_transport_job") -@KeySequence("task_transport_job_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class TransportTaskDO extends BaseDO { - - /** - * 任务标识 - */ - @TableId - private Long taskId; - /** - * 任务编码 - */ - private String taskCode; - /** - * 任务名称 - */ - private String taskName; - /** - * 业务归属服务:LMS/WMS,用于完成取消事件一级路由 - */ - private String ownerService; - /** - * 业务类型 - */ - private String bizType; - /** - * 业务侧标识 - */ - private String bizId; - /** - * 业务回调处理器编码 - */ - private String handleCode; - /** - * 任务类型 - */ - private String taskType; - /** - * 任务状态 - */ - private String taskStatus; - /** - * ACS任务类型 - */ - private String acsTaskType; - /** - * AGV系统类型 - */ - private String agvSystemType; - /** - * ACS外部任务号 - */ - private String externalTaskNo; - /** - * 取货点1 - */ - private String pointCode1; - /** - * 放货点1 - */ - private String pointCode2; - /** - * 取货点2 - */ - private String pointCode3; - /** - * 放货点2 - */ - private String pointCode4; - /** - * 载具类型 - */ - private String vehicleType; - /** - * 载具数量 - */ - private Long vehicleQty; - /** - * 载具编码 - */ - private String vehicleCode; - /** - * 载具编码2 - */ - private String vehicleCode2; - /** - * 车号 - */ - private String carNo; - /** - * 优先级 - */ - private String priority; - /** - * 生产区域 - */ - private String productArea; - /** - * 是否自动下发 - */ - private String isAutoIssue; - /** - * 任务组标识 - */ - private Long taskGroupId; - /** - * 任务组顺序号 - */ - private Long sortSeq; - /** - * 任务完成类型 - */ - private String finishedType; - /** - * 业务回调状态:PENDING/SUCCESS/FAILED - */ - private String callbackStatus; - /** - * 业务回调重试次数 - */ - private Integer callbackRetryCount; - /** - * 业务回调失败原因 - */ - private String callbackErrorMsg; - /** - * 生成方式 - * - * 枚举 {@link TODO user_type 对应的类} - */ - private String createMode; - /** - * 创建任务请求参数 - */ - private String requestParam; - /** - * 下发ACS的AcsTaskDto扩展报文 - */ - private String dispatchParam; - /** - * ACS反馈参数 - */ - private String resultParam; - /** - * 备注 - */ - private String remark; - - -} \ No newline at end of file +package cn.code.nl.module.task.dal.dataobject.transporttask; + +import lombok.*; +import java.util.*; +import java.time.LocalDateTime; +import java.time.LocalDateTime; +import com.baomidou.mybatisplus.annotation.*; +import cn.code.nl.framework.mybatis.core.dataobject.BaseDO; + +/** + * 搬运任务 DO + * + * @author 诺力管理员 + */ +@TableName("task_transport_job") +@KeySequence("task_transport_job_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class TransportTaskDO extends BaseDO { + + /** + * 任务标识 + */ + @TableId + private Long taskId; + /** + * 任务编码 + */ + private String taskCode; + /** + * 任务名称 + */ + private String taskName; + /** + * 业务归属服务:LMS/WMS,用于完成取消事件一级路由 + */ + private String ownerService; + /** + * 业务类型 todo: 暂时不用 + */ + private String bizType; + /** + * 业务侧标识 todo: 暂时不用 + */ + private String bizId; + /** + * 业务回调处理器编码 + */ + private String handleCode; + /** + * 任务类型 + */ + private String taskType; + /** + * 任务状态 + */ + private String taskStatus; + /** + * ACS任务类型 + */ + private String acsTaskType; + /** + * AGV系统类型 + */ + private String agvSystemType; + /** + * ACS外部任务号 + */ + private String externalTaskNo; + /** + * 取货点1 + */ + private String pointCode1; + /** + * 放货点1 + */ + private String pointCode2; + /** + * 取货点2 + */ + private String pointCode3; + /** + * 放货点2 + */ + private String pointCode4; + /** + * 载具类型 + */ + private String vehicleType; + /** + * 载具数量 + */ + private Long vehicleQty; + /** + * 载具编码 + */ + private String vehicleCode; + /** + * 载具编码2 + */ + private String vehicleCode2; + /** + * 物料id + */ + private Long materialId; + /** + * 物料编码 + */ + private String materialCode; + /** + * 车号 + */ + private String carNo; + /** + * 优先级 + */ + private String priority; + /** + * 生产区域 + */ + private String productArea; + /** + * 是否自动下发 + */ + private String isAutoIssue; + /** + * 任务组标识 + */ + private Long taskGroupId; + /** + * 任务组顺序号 + */ + private Long sortSeq; + /** + * 任务完成类型 + */ + private String finishedType; + /** + * 业务回调状态:PENDING/SUCCESS/FAILED + */ + private String callbackStatus; + /** + * 业务回调重试次数 + */ + private Integer callbackRetryCount; + /** + * 业务回调失败原因 + */ + private String callbackErrorMsg; + /** + * 生成方式 + * + * 枚举 + */ + private String createMode; + /** + * 创建任务请求参数 + */ + private String requestParam; + /** + * 下发ACS的AcsTaskDto扩展报文 + */ + private String dispatchParam; + /** + * ACS反馈参数 + */ + private String resultParam; + /** + * 备注 + */ + private String remark; + + +} 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 8acdbfac..3028c4f1 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 @@ -27,7 +27,7 @@ public interface TransportTaskMapper extends BaseMapperX { .eqIfPresent(TransportTaskDO::getBizType, reqVO.getBizType()) .eqIfPresent(TransportTaskDO::getBizId, reqVO.getBizId()) .eqIfPresent(TransportTaskDO::getHandleCode, reqVO.getHandleCode()) - .eqIfPresent(TransportTaskDO::getTaskType, reqVO.getTaskType()) + .inIfPresent(TransportTaskDO::getTaskType, reqVO.getTaskTypeList()) .inIfPresent(TransportTaskDO::getTaskStatus, reqVO.getTaskStatus()) .eqIfPresent(TransportTaskDO::getAcsTaskType, reqVO.getAcsTaskType()) .eqIfPresent(TransportTaskDO::getAgvSystemType, reqVO.getAgvSystemType()) @@ -66,6 +66,17 @@ public interface TransportTaskMapper extends BaseMapperX { return selectOne(TransportTaskDO::getTaskCode, taskCode); } + /** + * 查询自动下发的待下发任务 + */ + default List selectAutoIssueReadyList() { + return selectList(new LambdaQueryWrapperX() + .eq(TransportTaskDO::getIsAutoIssue, "1") + .eq(TransportTaskDO::getTaskStatus, TransportTaskStatusEnum.READY.getCode()) + .orderByDesc(TransportTaskDO::getPriority) + .orderByAsc(TransportTaskDO::getTaskId)); + } + /** * 按业务归属查询未完结任务(幂等校验用) */ @@ -76,6 +87,28 @@ public interface TransportTaskMapper extends BaseMapperX { .lt(TransportTaskDO::getTaskStatus, TransportTaskStatusEnum.FINISHED_CALLBACK_PENDING.getCode())); } + /** + * 根据物料ID和业务归属查询运行中任务 + */ + default List selectRunningListByMaterialId(Long materialId, String ownerService) { + return selectList(new LambdaQueryWrapperX() + .eq(TransportTaskDO::getMaterialId, materialId) + .eq(TransportTaskDO::getOwnerService, ownerService) + .lt(TransportTaskDO::getTaskStatus, TransportTaskStatusEnum.FINISHED_CALLBACK_PENDING.getCode()) + .orderByDesc(TransportTaskDO::getTaskId)); + } + + /** + * 根据物料编码和业务归属查询运行中任务 + */ + default List selectRunningListByMaterialCode(String materialCode, String ownerService) { + return selectList(new LambdaQueryWrapperX() + .eq(TransportTaskDO::getMaterialCode, materialCode) + .eq(TransportTaskDO::getOwnerService, ownerService) + .lt(TransportTaskDO::getTaskStatus, TransportTaskStatusEnum.FINISHED_CALLBACK_PENDING.getCode()) + .orderByDesc(TransportTaskDO::getTaskId)); + } + /** * 条件更新任务状态(CAS 抢占) */ @@ -87,4 +120,16 @@ public interface TransportTaskMapper extends BaseMapperX { return update(null, wrapper) > 0; } -} \ No newline at end of file + /** + * 更新下发结果 + */ + default void updateIssueResult(Long taskId, String taskStatus, String resultParam, String remark) { + TransportTaskDO updateObj = new TransportTaskDO(); + updateObj.setTaskId(taskId); + updateObj.setTaskStatus(taskStatus); + updateObj.setResultParam(resultParam); + updateObj.setRemark(remark); + updateById(updateObj); + } + +} diff --git a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/enums/AcsApiConstants.java b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/enums/AcsApiConstants.java new file mode 100644 index 00000000..7fa5265e --- /dev/null +++ b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/enums/AcsApiConstants.java @@ -0,0 +1,16 @@ +package cn.code.nl.module.task.enums; + +/** + * 请求ACS 接口 定义常量 + * @Author: liyongde + * @Date: 2026/7/27 9:45 + */ +public interface AcsApiConstants { + + /** 下发任务 */ + String ACS_TASK_API = "/acs-api/wms/issue-task"; + + /** 检测任务 */ + String ACS_OPERATE_CHECK_API = "/acs-api/wms/check-enable-operate"; + +} diff --git a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/enums/package-info.java b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/enums/package-info.java new file mode 100644 index 00000000..4cc3285b --- /dev/null +++ b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/enums/package-info.java @@ -0,0 +1,6 @@ +/** + * 任务模块自身使用的枚举 + * @Author: liyongde + * @Date: 2026/7/27 9:44 + */ +package cn.code.nl.module.task.enums; \ No newline at end of file diff --git a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/job/TaskScheduleJob.java b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/job/TaskScheduleJob.java index 3fc005da..40540120 100644 --- a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/job/TaskScheduleJob.java +++ b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/job/TaskScheduleJob.java @@ -1,28 +1,38 @@ package cn.code.nl.module.task.job; import cn.code.nl.framework.tenant.core.job.TenantJob; +import cn.code.nl.module.task.service.transporttask.TransportTaskService; import com.xxl.job.core.context.XxlJobHelper; import com.xxl.job.core.handler.annotation.XxlJob; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; /** * 任务相关的定时任务 + * * @Author: liyongde * @Date: 2026/7/13 14:01 */ +@Slf4j @Component public class TaskScheduleJob { + @Resource + private TransportTaskService transportTaskService; + @XxlJob("autoTaskAssignmentJob") @TenantJob public void autoTaskAssignmentJob() { + log.info("自动下发任务开始....."); XxlJobHelper.log("自动下发任务开始"); - // todo: 具体业务 + transportTaskService.autoIssueTransportTasks(); - String msg = "自动下发任务成功"; + String msg = "自动下发任务结束"; XxlJobHelper.log(msg); XxlJobHelper.handleSuccess(msg); + log.info(msg); } } diff --git a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/job/dto/AcsIssueResultRespDTO.java b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/job/dto/AcsIssueResultRespDTO.java new file mode 100644 index 00000000..388bce41 --- /dev/null +++ b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/job/dto/AcsIssueResultRespDTO.java @@ -0,0 +1,37 @@ +package cn.code.nl.module.task.job.dto; + +import cn.code.nl.framework.common.pojo.AcsBaseRespDTO; +import lombok.Data; + +import java.util.List; + +/** + * ACS 任务下发响应 + */ +@Data +public class AcsIssueResultRespDTO extends AcsBaseRespDTO { + + /** + * 下发失败的任务 + */ + private List failedTasks; + + /** + * ACS 失败任务明细 + */ + @Data + public static class FailedTask { + + /** + * 任务标识 + */ + private Long taskId; + + /** + * 错误信息 + */ + private String errorMessage; + + } + +} diff --git a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/job/dto/AcsOperateCheckReqDTO.java b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/job/dto/AcsOperateCheckReqDTO.java new file mode 100644 index 00000000..876c5865 --- /dev/null +++ b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/job/dto/AcsOperateCheckReqDTO.java @@ -0,0 +1,31 @@ +package cn.code.nl.module.task.job.dto; + +import lombok.Data; + +/** + * ACS 任务操作校验请求 + */ +@Data +public class AcsOperateCheckReqDTO { + + /** + * 任务标识 + */ + private Long taskId; + + /** + * 任务编码 + */ + private String taskCode; + + /** + * 操作类型 + */ + private String operationType; + + /** + * 生产区域 + */ + private String productArea; + +} diff --git a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/job/dto/AcsOperateCheckRespDTO.java b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/job/dto/AcsOperateCheckRespDTO.java new file mode 100644 index 00000000..ab4278bb --- /dev/null +++ b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/job/dto/AcsOperateCheckRespDTO.java @@ -0,0 +1,32 @@ +package cn.code.nl.module.task.job.dto; + +import cn.code.nl.framework.common.pojo.AcsBaseRespDTO; +import lombok.Data; + +/** + * ACS 任务操作校验响应 + */ +@Data +public class AcsOperateCheckRespDTO extends AcsBaseRespDTO { + + /** + * 是否允许操作 + */ + private Boolean enableOperate; + + /** + * 是否允许操作,兼容 ACS 字段 + */ + private Boolean canOperate; + + /** + * 是否允许操作,兼容通用 data 字段 + */ + private Boolean data; + + /** + * 不允许操作的原因 + */ + private String message; + +} diff --git a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/job/dto/AcsTaskDTO.java b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/job/dto/AcsTaskDTO.java new file mode 100644 index 00000000..7aa6bf79 --- /dev/null +++ b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/job/dto/AcsTaskDTO.java @@ -0,0 +1,74 @@ +package cn.code.nl.module.task.job.dto; + +import lombok.Data; + +import cn.code.nl.framework.common.pojo.AcsBaseReqDTO; + +import java.util.Map; + +/** + * 下发给ACS的实体 + * + * @Author: liyongde + * @Date: 2026/7/23 17:10 + */ +@Data +public class AcsTaskDTO extends AcsBaseReqDTO { + /** + * 任务标识 + */ + private Long taskId; + /** + * 任务编码 + */ + private String taskCode; + /** + * 取货点1 + */ + private String startDeviceCode; + /** + * 放货点1 + */ + private String nextDeviceCode; + /** + * 取货点2 + */ + private String startDeviceCode2; + /** + * 放货点2 + */ + private String nextDeviceCode2; + /** + * 优先级 + */ + private String priority; + /** + * 载具号 + */ + private String vehicleCode; + /** + * 载具号2 + */ + private String vehicleCode2; + /** + * 任务类型 + */ + private String taskType; + /** + * Agv系统类型 + */ + private String agvSystemType; + + /** + * 备注 + */ + private String remark; + /** + * 扩展参数 + */ + private Map payload; + /** + * 生产区域 + */ + private String productArea; +} diff --git a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/manage/TransportTaskBusinessOperationManager.java b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/manage/TransportTaskBusinessOperationManager.java index 1d24db11..7c492c2c 100644 --- a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/manage/TransportTaskBusinessOperationManager.java +++ b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/manage/TransportTaskBusinessOperationManager.java @@ -11,7 +11,6 @@ import cn.code.nl.module.task.dal.mysql.transporttask.TransportTaskMapper; import cn.code.nl.module.task.dto.AcsFeedbackReqDTO; import cn.code.nl.module.task.enums.AcsBusinessOperationTypeEnum; import cn.code.nl.module.task.enums.TransportTaskStatusEnum; -import com.alibaba.fastjson.JSON; import jakarta.annotation.PostConstruct; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; @@ -104,68 +103,63 @@ public class TransportTaskBusinessOperationManager { TaskCommonApi serverApi = taskCommonApiFactory.getByServerName(task.getOwnerService()); CommonResult result = serverApi.doHandlePicked(buildReq(task, reqDTO)); - return result.getData(); + return result.getCheckedData(); } /** * 处理二次请求 */ private AcsApplyActionRespVO handleApplyAgain(TransportTaskDO task, AcsFeedbackReqDTO reqDTO) { - // TODO 二次请求业务待开发 - log.info("二次请求业务待开发, taskId={}", task.getTaskId()); + log.info("二次请求, taskId={}", task.getTaskId()); // 调用具体的服务去执行取货完成操作。 TaskCommonApi serverApi = taskCommonApiFactory.getByServerName(task.getOwnerService()); CommonResult result = serverApi.doHandleApplyAgain(buildReq(task, reqDTO)); - return result.getData(); + return result.getCheckedData(); } /** * 处理请求放货 */ private Object handleRequestRelease(TransportTaskDO task, AcsFeedbackReqDTO reqDTO) { - // TODO 请求放货业务待开发 - log.info("请求放货业务待开发, taskId={}", task.getTaskId()); + log.info("请求放货, taskId={}", task.getTaskId()); TaskCommonApi serverApi = taskCommonApiFactory.getByServerName(task.getOwnerService()); CommonResult result = serverApi.doHandleRequestRelease(buildReq(task, reqDTO)); - return result.getData(); + return result.getCheckedData(); } /** * 处理请求取货 */ private Object handleRequestPick(TransportTaskDO task, AcsFeedbackReqDTO reqDTO) { - // TODO 请求取货业务待开发 - log.info("请求取货业务待开发, taskId={}", task.getTaskId()); + log.info("请求取货, taskId={}", task.getTaskId()); TaskCommonApi serverApi = taskCommonApiFactory.getByServerName(task.getOwnerService()); CommonResult result = serverApi.doHandleRequestPick(buildReq(task, reqDTO)); - return result.getData(); + return result.getCheckedData(); } /** * 处理请求离开 */ private Object handleRequestLeave(TransportTaskDO task, AcsFeedbackReqDTO reqDTO) { - // TODO 请求离开业务待开发 - log.info("请求离开业务待开发, taskId={}", task.getTaskId()); + log.info("请求离开, taskId={}", task.getTaskId()); TaskCommonApi serverApi = taskCommonApiFactory.getByServerName(task.getOwnerService()); CommonResult result = serverApi.doHandleRequestLeave(buildReq(task, reqDTO)); - return result.getData(); + return result.getCheckedData(); } /** * 处理请求进入 */ private Object handleRequestEnter(TransportTaskDO task, AcsFeedbackReqDTO reqDTO) { - // TODO 请求进入业务待开发 - log.info("请求进入业务待开发, taskId={}", task.getTaskId()); + log.info("请求进入, taskId={}", task.getTaskId()); TaskCommonApi serverApi = taskCommonApiFactory.getByServerName(task.getOwnerService()); CommonResult result = serverApi.doHandleRequestEnter(buildReq(task, reqDTO)); - return result.getData(); + return result.getCheckedData(); } /** @@ -175,7 +169,7 @@ public class TransportTaskBusinessOperationManager { AcsApplyActionRespVO respVO = new AcsApplyActionRespVO(); respVO.setTaskId(task.getTaskId()); respVO.setTaskCode(task.getTaskCode()); - respVO.setData(JSON.parseObject(JSON.toJSONString(data))); + respVO.setData(data); return respVO; } /** @@ -185,7 +179,7 @@ public class TransportTaskBusinessOperationManager { TaskStatusCallApiReqDTO req = new TaskStatusCallApiReqDTO(); req.setTaskId(task.getTaskId()); req.setTaskCode(task.getTaskCode()); - req.setStatus(TransportTaskStatusEnum.PICKED.getCode()); + req.setStatus(reqDTO.getStatus()); req.setOwnerService(task.getOwnerService()); req.setBizType(task.getBizType()); req.setBizId(task.getBizId()); diff --git a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/manage/TransportTaskIssueManager.java b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/manage/TransportTaskIssueManager.java new file mode 100644 index 00000000..53af8afe --- /dev/null +++ b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/manage/TransportTaskIssueManager.java @@ -0,0 +1,139 @@ +package cn.code.nl.module.task.manage; + +import cn.code.nl.framework.common.exception.ServiceException; +import cn.code.nl.framework.common.pojo.CommonResult; +import cn.code.nl.framework.common.util.http.AcsUtil; +import cn.code.nl.framework.common.util.json.JsonUtils; +import cn.code.nl.module.infra.api.config.ConfigApi; +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.enums.TransportTaskStatusEnum; +import cn.code.nl.module.task.job.dto.AcsIssueResultRespDTO; +import cn.code.nl.module.task.job.dto.AcsTaskDTO; +import cn.code.nl.module.task.utils.AcsTaskUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; + +import static cn.code.nl.module.task.enums.AcsApiConstants.ACS_TASK_API; + +/** + * 搬运任务下发管理器 + */ +@Slf4j +@Component +public class TransportTaskIssueManager { + + private static final String ACS_SERVER_ADDRESS_CONFIG_SUFFIX = "-acs-server-address"; + + @Resource + private TransportTaskMapper transportTaskMapper; + + @Resource + private ConfigApi configApi; + + /** + * 按生产区域分组下发任务 + * + * @param tasks 待下发任务 + */ + public void issueTasks(List tasks) { + tasks.stream() + .filter(task -> StrUtil.isBlank(task.getProductArea())) + .forEach(task -> + updateIssueFailed(task.getTaskId(), "生产区域为空,无法获取 ACS 服务地址", null)); + Map> taskMap = tasks.stream() + .filter(task -> StrUtil.isNotBlank(task.getProductArea())) + .collect(Collectors.groupingBy(TransportTaskDO::getProductArea)); + taskMap.forEach(this::issueProductAreaTasks); + } + + /** + * 下发单个生产区域的任务 + * + * @param productArea 生产区域 + * @param tasks 待下发任务 + */ + private void issueProductAreaTasks(String productArea, List tasks) { + List acsTasks = tasks.stream().map(AcsTaskUtil::buildAcsTaskDTO).toList(); + String requestJson = JsonUtils.toJsonString(acsTasks); + try { + String serverAddress = getAcsServerAddress(productArea); + AcsIssueResultRespDTO result = AcsUtil.post(serverAddress, ACS_TASK_API, acsTasks, AcsIssueResultRespDTO.class); + handleIssueResult(tasks, result); + } catch (Exception ex) { + log.error("自动下发任务失败,productArea={},tasks={}", productArea, requestJson, ex); + tasks.forEach(task -> updateIssueFailed(task.getTaskId(), ex.getMessage(), null)); + } + } + + /** + * 获取 ACS 服务地址 + * + * @param productArea 生产区域 + * @return ACS 服务地址 + */ + private String getAcsServerAddress(String productArea) { + String configKey = productArea + ACS_SERVER_ADDRESS_CONFIG_SUFFIX; + CommonResult result = configApi.getConfigValueByKey(configKey); + String serverAddress = result.getCheckedData(); + if (StrUtil.isBlank(serverAddress)) { + throw new ServiceException(500, "未配置 ACS 服务地址:" + configKey); + } + return serverAddress; + } + + /** + * 处理 ACS 下发响应 + * + * @param tasks 本次下发任务 + * @param result ACS 响应 + */ + private void handleIssueResult(List tasks, AcsIssueResultRespDTO result) { + if (result == null) { + tasks.forEach(task -> updateIssueFailed(task.getTaskId(), "ACS 返回为空", null)); + return; + } + String resultJson = JsonUtils.toJsonString(result); + List failedTasks = result.getFailedTasks() == null + ? Collections.emptyList() + : result.getFailedTasks(); + if (Boolean.FALSE.equals(result.getSuccess()) && CollUtil.isEmpty(failedTasks)) { + String errorMessage = StrUtil.blankToDefault(result.getMsg(), "ACS 下发失败"); + tasks.forEach(task -> updateIssueFailed(task.getTaskId(), errorMessage, resultJson)); + return; + } + Map failedTaskMap = failedTasks.stream() + .filter(failedTask -> failedTask.getTaskId() != null) + .collect(Collectors.toMap(AcsIssueResultRespDTO.FailedTask::getTaskId, Function.identity(), (first, second) -> first)); + Set failedTaskIds = failedTaskMap.keySet(); + for (TransportTaskDO task : tasks) { + if (failedTaskIds.contains(task.getTaskId())) { + updateIssueFailed(task.getTaskId(), failedTaskMap.get(task.getTaskId()).getErrorMessage(), resultJson); + } else { + transportTaskMapper.updateIssueResult(task.getTaskId(), TransportTaskStatusEnum.ISSUED.getCode(), resultJson, null); + } + } + } + + /** + * 更新任务为下发失败 + * + * @param taskId 任务标识 + * @param errorMessage 错误信息 + * @param resultJson ACS 响应 JSON + */ + private void updateIssueFailed(Long taskId, String errorMessage, String resultJson) { + transportTaskMapper.updateIssueResult(taskId, TransportTaskStatusEnum.FAILED.getCode(), resultJson, errorMessage); + } + +} diff --git a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/manage/TransportTaskOperateCheckManager.java b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/manage/TransportTaskOperateCheckManager.java new file mode 100644 index 00000000..bd6085bd --- /dev/null +++ b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/manage/TransportTaskOperateCheckManager.java @@ -0,0 +1,140 @@ +package cn.code.nl.module.task.manage; + +import cn.code.nl.framework.common.exception.ServiceException; +import cn.code.nl.framework.common.pojo.CommonResult; +import cn.code.nl.framework.common.util.http.AcsUtil; +import cn.code.nl.framework.common.util.json.JsonUtils; +import cn.code.nl.module.infra.api.config.ConfigApi; +import cn.code.nl.module.task.dal.dataobject.transporttask.TransportTaskDO; +import cn.code.nl.module.task.enums.TaskOperationTypeEnum; +import cn.code.nl.module.task.job.dto.AcsOperateCheckRespDTO; +import cn.code.nl.module.task.job.dto.AcsOperateCheckReqDTO; +import cn.hutool.core.util.StrUtil; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import static cn.code.nl.module.task.enums.AcsApiConstants.ACS_OPERATE_CHECK_API; + +/** + * PC 端任务操作 ACS 校验管理器 + */ +@Slf4j +@Component +public class TransportTaskOperateCheckManager { + + private static final String TASK_OPERATE_ENABLE_CONFIG_KEY = "task-operate-enable"; + private static final String TASK_OPERATE_ENABLE_VALUE = "1"; + private static final String ACS_SERVER_ADDRESS_CONFIG_SUFFIX = "-acs-server-address"; + + @Resource + private ConfigApi configApi; + + /** + * 按配置校验 PC 端是否允许完成或取消任务 + * + * @param task 任务信息 + * @param type 操作类型 + */ + public void checkEnableOperate(TransportTaskDO task, TaskOperationTypeEnum type) { + if (!needCheckAcs()) { + return; + } + String serverAddress = getAcsServerAddress(task.getProductArea()); + AcsOperateCheckReqDTO reqDTO = buildReqDTO(task, type); + AcsOperateCheckRespDTO result; + try { + result = AcsUtil.post(serverAddress, ACS_OPERATE_CHECK_API, reqDTO, AcsOperateCheckRespDTO.class); + } catch (ServiceException ex) { + log.error("ACS 操作校验请求失败,reqDTO={}", JsonUtils.toJsonString(reqDTO), ex); + throw ex; + } catch (Exception ex) { + log.error("ACS 操作校验请求失败,reqDTO={}", JsonUtils.toJsonString(reqDTO), ex); + throw new ServiceException(500, "ACS操作校验失败"); + } + handleCheckResult(reqDTO, result); + } + + /** + * 判断是否需要请求 ACS 校验 + */ + private boolean needCheckAcs() { + CommonResult result = configApi.getConfigValueByKey(TASK_OPERATE_ENABLE_CONFIG_KEY); + return TASK_OPERATE_ENABLE_VALUE.equals(result.getCheckedData()); + } + + /** + * 获取 ACS 服务地址 + * + * @param productArea 生产区域 + * @return ACS 服务地址 + */ + private String getAcsServerAddress(String productArea) { + if (StrUtil.isBlank(productArea)) { + throw new ServiceException(500, "生产区域为空,无法获取 ACS 服务地址"); + } + String configKey = productArea + ACS_SERVER_ADDRESS_CONFIG_SUFFIX; + CommonResult result = configApi.getConfigValueByKey(configKey); + String serverAddress = result.getCheckedData(); + if (StrUtil.isBlank(serverAddress)) { + throw new ServiceException(500, "未配置 ACS 服务地址:" + configKey); + } + return serverAddress; + } + + /** + * 构建 ACS 操作校验请求 + */ + private AcsOperateCheckReqDTO buildReqDTO(TransportTaskDO task, TaskOperationTypeEnum type) { + AcsOperateCheckReqDTO reqDTO = new AcsOperateCheckReqDTO(); + reqDTO.setTaskId(task.getTaskId()); + reqDTO.setTaskCode(task.getTaskCode()); + reqDTO.setOperationType(type.getCode()); + reqDTO.setProductArea(task.getProductArea()); + return reqDTO; + } + + /** + * 处理 ACS 操作校验结果 + */ + private void handleCheckResult(AcsOperateCheckReqDTO reqDTO, AcsOperateCheckRespDTO result) { + if (result == null) { + throw new ServiceException(500, "ACS 操作校验返回为空"); + } + Boolean enableOperate = getEnableOperate(result); + if (Boolean.TRUE.equals(enableOperate)) { + return; + } + String message = getMessage(result); + log.warn("ACS 拒绝 PC 端任务操作,reqDTO={},result={}", + JsonUtils.toJsonString(reqDTO), JsonUtils.toJsonString(result)); + throw new ServiceException(500, message); + } + + /** + * 获取 ACS 是否允许操作 + */ + private Boolean getEnableOperate(AcsOperateCheckRespDTO result) { + if (result.getEnableOperate() != null) { + return result.getEnableOperate(); + } + if (result.getCanOperate() != null) { + return result.getCanOperate(); + } + if (result.getData() != null) { + return result.getData(); + } + return result.getSuccess(); + } + + /** + * 获取 ACS 拒绝原因 + */ + private String getMessage(AcsOperateCheckRespDTO result) { + if (StrUtil.isNotBlank(result.getMessage())) { + return result.getMessage(); + } + return StrUtil.blankToDefault(result.getMsg(), "ACS 不允许执行该操作"); + } + +} diff --git a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/manage/TransportTaskOperationManager.java b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/manage/TransportTaskOperationManager.java index 9e9ee168..28e334b5 100644 --- a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/manage/TransportTaskOperationManager.java +++ b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/manage/TransportTaskOperationManager.java @@ -22,11 +22,17 @@ import org.springframework.transaction.support.TransactionSynchronization; import org.springframework.transaction.support.TransactionSynchronizationManager; import java.util.EnumMap; +import java.util.List; import java.util.Map; import java.util.function.BiConsumer; +import static cn.code.nl.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.code.nl.module.task.enums.ErrorCodeConstants.TRANSPORT_TASK_AUTO_ISSUE_NOT_ALLOW_MANUAL; +import static cn.code.nl.module.task.enums.ErrorCodeConstants.TRANSPORT_TASK_STATUS_NOT_ALLOW; import static cn.code.nl.module.task.framework.common.util.TaskUtil.isAllowedFrom; +import static cn.code.nl.module.task.message.LockKeyConstants.TASK_STATUS_CHANGE_LOCK_KEY; + /** * 搬运任务状态操作分发管理器 */ @@ -40,6 +46,9 @@ public class TransportTaskOperationManager { @Resource private TaskEventProducer taskEventProducer; + @Resource + private TransportTaskIssueManager transportTaskIssueManager; + @Resource private RedissonClient redissonClient; @@ -53,6 +62,7 @@ public class TransportTaskOperationManager { @PostConstruct public void initOperationHandlers() { operationHandlers.put(TaskOperationTypeEnum.EXECUTING, this::handleExecuting); + operationHandlers.put(TaskOperationTypeEnum.ISSUE, (task, reqDTO) -> handleIssue(task)); operationHandlers.put(TaskOperationTypeEnum.FINISHED, this::handleFinished); operationHandlers.put(TaskOperationTypeEnum.CANCELLED, this::handleCancelled); operationHandlers.put(TaskOperationTypeEnum.FORCE_FINISH, (task, reqDTO) -> handleForceFinish(task)); @@ -62,7 +72,7 @@ public class TransportTaskOperationManager { * 按操作类型查路由表分发 */ public void dispatchOperation(TransportTaskDO task, TaskOperationTypeEnum type, AcsFeedbackReqDTO reqDTO) { - RLock lock = redissonClient.getLock(String.valueOf(task.getTaskId())); + RLock lock = redissonClient.getLock(TASK_STATUS_CHANGE_LOCK_KEY + task.getTaskId()); if (lock.tryLock()) { try { BiConsumer handler = operationHandlers.get(type); @@ -72,7 +82,11 @@ public class TransportTaskOperationManager { } handler.accept(task, reqDTO); } catch (Exception ex) { + if (ex instanceof ServiceException serviceException) { + throw serviceException; + } log.error("[messageResend][执行异常][lockKey={}]", task.getTaskId(), ex); + throw new ServiceException(500, ex.getMessage()); } finally { if (lock.isHeldByCurrentThread()) { lock.unlock(); @@ -83,6 +97,19 @@ public class TransportTaskOperationManager { } } + /** + * 处理下发任务 + */ + private void handleIssue(TransportTaskDO task) { + if ("1".equals(task.getIsAutoIssue())) { + throw exception(TRANSPORT_TASK_AUTO_ISSUE_NOT_ALLOW_MANUAL); + } + if (!TransportTaskStatusEnum.READY.getCode().equals(task.getTaskStatus())) { + throw exception(TRANSPORT_TASK_STATUS_NOT_ALLOW); + } + transportTaskIssueManager.issueTasks(List.of(task)); + } + /** * 处理执行中 */ 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 2c5dae74..0c5c14ae 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 @@ -6,19 +6,21 @@ import org.apache.rocketmq.spring.core.RocketMQTemplate; import org.springframework.stereotype.Component; import jakarta.annotation.Resource; +import org.springframework.beans.factory.annotation.Value; import java.util.UUID; /** * 任务事件 MQ 生产者 *

- * Topic: TASK_EVENT_TOPIC + * Topic 从 YAML 配置 rocketmq.consumer.task.topic 读取 * Tag: ownerService (LMS / WMS) */ @Slf4j @Component public class TaskEventProducer { - private static final String TOPIC = "TASK_EVENT_TOPIC"; + @Value("${rocketmq.consumer.task.topic}") + private String topic; @Resource private RocketMQTemplate rocketMQTemplate; @@ -29,12 +31,9 @@ public class TaskEventProducer { * @param message 事件消息 */ public void publishEvent(TaskEventMessage message) { - // 补齐 eventId - if (message.getEventId() == null || message.getEventId().isEmpty()) { - message.setEventId(UUID.randomUUID().toString()); - } + message.setEventId(message.getTaskId().toString()); - String destination = TOPIC + "_" + message.getOwnerService(); + String destination = message.getOwnerService() + "_" + topic; try { rocketMQTemplate.syncSend(destination, message); log.info("MQ 发送成功, destination={}, taskId={}, eventType={}", diff --git a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/service/transporttask/TransportTaskService.java b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/service/transporttask/TransportTaskService.java index b7fdb0ef..92d41856 100644 --- a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/service/transporttask/TransportTaskService.java +++ b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/service/transporttask/TransportTaskService.java @@ -76,6 +76,13 @@ public interface TransportTaskService { */ void operateTransportTask(@Valid TransportTaskOperateReqVO reqVO); + /** + * 根据任务ID下发搬运任务到 ACS + * + * @param taskId 任务ID + */ + void issueTransportTask(Long taskId); + /** * 根据 taskId 查询任务全量信息(RPC 用,自动过滤逻辑删除,查不到返回 null) * @@ -92,4 +99,27 @@ public interface TransportTaskService { */ TaskInfoDTO getTaskInfoByCode(String taskCode); + /** + * 根据物料ID和业务归属查询运行中任务(状态小于75) + * + * @param materialId 物料ID + * @param ownerService 业务归属服务 + * @return 运行中任务列表 + */ + List getRunningTaskInfoByMaterialId(Long materialId, String ownerService); + + /** + * 根据物料编码和业务归属查询运行中任务(状态小于75) + * + * @param materialCode 物料编码 + * @param ownerService 业务归属服务 + * @return 运行中任务列表 + */ + List getRunningTaskInfoByMaterialCode(String materialCode, String ownerService); + + /** + * 自动下发待下发任务到 ACS + */ + void autoIssueTransportTasks(); + } 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 f8e26b69..8181a758 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 @@ -2,6 +2,8 @@ package cn.code.nl.module.task.service.transporttask; import cn.code.nl.framework.common.pojo.PageResult; import cn.code.nl.framework.common.util.object.BeanUtils; +import cn.code.nl.framework.security.core.util.SecurityFrameworkUtils; +import cn.code.nl.module.base.api.classstandard.ClassStandardApi; import cn.code.nl.module.task.controller.admin.transporttask.vo.TransportTaskOperateReqVO; import cn.code.nl.module.task.controller.admin.transporttask.vo.TransportTaskPageReqVO; import cn.code.nl.module.task.controller.admin.transporttask.vo.TransportTaskSaveReqVO; @@ -11,8 +13,17 @@ 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.TaskInfoDTO; import cn.code.nl.module.task.dto.TransportTaskCreateReqDTO; -import cn.code.nl.module.task.enums.*; +import cn.code.nl.module.task.enums.CallbackStatusEnum; +import cn.code.nl.module.task.enums.FinishedTypeEnum; +import cn.code.nl.module.task.enums.TaskOperationTypeEnum; +import cn.code.nl.module.task.enums.TransportTaskStatusEnum; +import cn.code.nl.module.task.manage.TransportTaskIssueManager; +import cn.code.nl.module.task.manage.TransportTaskOperateCheckManager; import cn.code.nl.module.task.manage.TransportTaskOperationManager; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.mzt.logapi.context.LogRecordContext; +import com.mzt.logapi.starter.annotation.LogRecord; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -22,6 +33,7 @@ import org.springframework.validation.annotation.Validated; import java.util.List; import static cn.code.nl.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.code.nl.module.system.enums.LogRecordConstants.*; import static cn.code.nl.module.task.enums.ErrorCodeConstants.TRANSPORT_TASK_ALREADY_FINAL; import static cn.code.nl.module.task.enums.ErrorCodeConstants.TRANSPORT_TASK_NOT_EXISTS; import static cn.code.nl.module.task.enums.ErrorCodeConstants.TRANSPORT_TASK_OPERATION_NOT_SUPPORTED; @@ -42,21 +54,25 @@ public class TransportTaskServiceImpl implements TransportTaskService { @Resource private TransportTaskOperationManager transportTaskOperationManager; + @Resource + private TransportTaskIssueManager transportTaskIssueManager; + + @Resource + private TransportTaskOperateCheckManager transportTaskOperateCheckManager; + + @Resource + private ClassStandardApi classStandardApi; + @Override public Long createTransportTask(TransportTaskSaveReqVO createReqVO) { - // 插入 TransportTaskDO transportTask = BeanUtils.toBean(createReqVO, TransportTaskDO.class); transportTaskMapper.insert(transportTask); - - // 返回 return transportTask.getTaskId(); } @Override public Long createTransportTaskByRpc(TransportTaskCreateReqDTO reqDTO) { - // 1. 构建 DO 并保存 TransportTaskDO task = BeanUtils.toBean(reqDTO, TransportTaskDO.class); - // 参数完整 → 待下发(040),否则 → 生成(010) task.setTaskStatus(reqDTO.getIsCreateFinish() ? TransportTaskStatusEnum.READY.getCode() : TransportTaskStatusEnum.CREATED.getCode()); @@ -70,28 +86,27 @@ public class TransportTaskServiceImpl implements TransportTaskService { @Override public void updateTransportTask(TransportTaskSaveReqVO updateReqVO) { - // 校验存在 validateTransportTaskExists(updateReqVO.getTaskId()); - // 更新 TransportTaskDO updateObj = BeanUtils.toBean(updateReqVO, TransportTaskDO.class); transportTaskMapper.updateById(updateObj); } @Override public void deleteTransportTask(Long id) { - // 校验存在 validateTransportTaskExists(id); - // 删除 transportTaskMapper.deleteById(id); } @Override - public void deleteTransportTaskListByIds(List ids) { - // 删除 + public void deleteTransportTaskListByIds(List ids) { transportTaskMapper.deleteByIds(ids); - } - + } + /** + * 校验搬运任务是否存在 + * + * @param id 任务标识 + */ private void validateTransportTaskExists(Long id) { if (transportTaskMapper.selectById(id) == null) { throw exception(TRANSPORT_TASK_NOT_EXISTS); @@ -105,59 +120,105 @@ public class TransportTaskServiceImpl implements TransportTaskService { @Override public PageResult getTransportTaskPage(TransportTaskPageReqVO pageReqVO) { + if (StrUtil.isNotBlank(pageReqVO.getTaskType())) { + List taskTypeList = classStandardApi.getClassStandardCodeListByCode(pageReqVO.getTaskType()).getCheckedData(); + pageReqVO.setTaskTypeList(CollUtil.isNotEmpty(taskTypeList) ? taskTypeList : List.of(pageReqVO.getTaskType())); + } return transportTaskMapper.selectPage(pageReqVO); } /** - * PC 端操作搬运任务(完成/取消/强制完成):先校验再走统一路由表分发 + * PC 端操作搬运任务,统一走状态操作分发 + * + * @param reqVO 操作请求 */ @Override @Transactional(rollbackFor = Exception.class) + @LogRecord(type = TASK_INFO, + subType = TASK_INFO_OPERATE_TYPE, + bizNo = "{{#reqVO.taskId}}", + success = TASK_INFO_OPERATE_SUCCESS) public void operateTransportTask(TransportTaskOperateReqVO reqVO) { - // 1. 校验任务存在 TransportTaskDO task = transportTaskMapper.selectById(reqVO.getTaskId()); if (task == null) { throw exception(TRANSPORT_TASK_NOT_EXISTS); } - // 2. 校验操作类型允许 PC 端触发 TaskOperationTypeEnum type = TaskOperationTypeEnum.getByCode(reqVO.getOperationType()); if (type == null || !type.isPcAllowed()) { throw exception(TRANSPORT_TASK_OPERATION_NOT_SUPPORTED); } - // 3. 终态校验:已完成/已取消不允许再操作 if (TransportTaskStatusEnum.FINISHED.getCode().equals(task.getTaskStatus()) || TransportTaskStatusEnum.CANCELLED.getCode().equals(task.getTaskStatus())) { throw exception(TRANSPORT_TASK_ALREADY_FINAL); } - // 4. PC 端与 ACS 的差异点:记录完成类型 + if (type == TaskOperationTypeEnum.FINISHED || type == TaskOperationTypeEnum.CANCELLED) { + transportTaskOperateCheckManager.checkEnableOperate(task, type); + } if (type == TaskOperationTypeEnum.FINISHED) { task.setFinishedType(FinishedTypeEnum.MANUAL.getCode()); } else if (type == TaskOperationTypeEnum.FORCE_FINISH) { task.setFinishedType(FinishedTypeEnum.MANUAL_FORCE.getCode()); } - // 5. 构造精简反馈对象,走统一路由表分发 AcsFeedbackReqDTO reqDTO = new AcsFeedbackReqDTO(); reqDTO.setTaskId(task.getTaskId()); reqDTO.setStatus(type.getCode()); transportTaskOperationManager.dispatchOperation(task, type, reqDTO); + LogRecordContext.putVariable("loginUserNickname", SecurityFrameworkUtils.getLoginUserNickname()); + LogRecordContext.putVariable("operateName", type.getName()); } /** - * 根据 taskId 查询任务全量信息:查不到返回 null,由调用方判断 + * 根据任务ID手动下发任务到 ACS,复用自动下发的数组接口逻辑 + * + * @param taskId 任务ID */ + @Override + public void issueTransportTask(Long taskId) { + TransportTaskDO task = transportTaskMapper.selectById(taskId); + if (task == null) { + throw exception(TRANSPORT_TASK_NOT_EXISTS); + } + AcsFeedbackReqDTO reqDTO = new AcsFeedbackReqDTO(); + reqDTO.setTaskId(task.getTaskId()); + reqDTO.setStatus(TaskOperationTypeEnum.ISSUE.getCode()); + transportTaskOperationManager.dispatchOperation(task, TaskOperationTypeEnum.ISSUE, reqDTO); + } + @Override public TaskInfoDTO getTaskInfoById(Long taskId) { TransportTaskDO task = transportTaskMapper.selectById(taskId); return TransportTaskConvert.INSTANCE.convert(task); } - /** - * 根据 taskCode 查询任务全量信息:查不到返回 null,由调用方判断 - */ @Override public TaskInfoDTO getTaskInfoByCode(String taskCode) { TransportTaskDO task = transportTaskMapper.selectByTaskCode(taskCode); return TransportTaskConvert.INSTANCE.convert(task); } + @Override + public List getRunningTaskInfoByMaterialId(Long materialId, String ownerService) { + List tasks = transportTaskMapper.selectRunningListByMaterialId(materialId, ownerService); + return TransportTaskConvert.INSTANCE.convertList(tasks); + } + + @Override + public List getRunningTaskInfoByMaterialCode(String materialCode, String ownerService) { + List tasks = transportTaskMapper.selectRunningListByMaterialCode(materialCode, ownerService); + return TransportTaskConvert.INSTANCE.convertList(tasks); + } + + /** + * 查询待下发任务,并委托下发管理器按生产区域下发到 ACS + */ + @Override + public void autoIssueTransportTasks() { + List tasks = transportTaskMapper.selectAutoIssueReadyList(); + if (CollUtil.isEmpty(tasks)) { + log.info("自动下发任务结束:没有待下发任务"); + return; + } + transportTaskIssueManager.issueTasks(tasks); + } + } diff --git a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/utils/AcsTaskUtil.java b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/utils/AcsTaskUtil.java new file mode 100644 index 00000000..418f7956 --- /dev/null +++ b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/utils/AcsTaskUtil.java @@ -0,0 +1,41 @@ +package cn.code.nl.module.task.utils; + +import cn.code.nl.framework.common.util.json.JsonUtils; +import cn.code.nl.module.task.dal.dataobject.transporttask.TransportTaskDO; +import cn.code.nl.module.task.job.dto.AcsTaskDTO; + +import java.util.UUID; + +/** + * ACS 任务工具类 + */ +public class AcsTaskUtil { + + /** + * 构建 ACS 下发任务 + * + * @param task 搬运任务 + * @return ACS 下发任务 + */ + public static AcsTaskDTO buildAcsTaskDTO(TransportTaskDO task) { + AcsTaskDTO dto = new AcsTaskDTO(); + dto.setTraceId(UUID.randomUUID().toString()); + dto.setTimestamp(System.currentTimeMillis()); + dto.setTaskId(task.getTaskId()); + dto.setTaskCode(task.getTaskCode()); + dto.setStartDeviceCode(task.getPointCode1()); + dto.setNextDeviceCode(task.getPointCode2()); + dto.setStartDeviceCode2(task.getPointCode3()); + dto.setNextDeviceCode2(task.getPointCode4()); + dto.setPriority(task.getPriority()); + dto.setVehicleCode(task.getVehicleCode()); + dto.setVehicleCode2(task.getVehicleCode2()); + dto.setTaskType(task.getAcsTaskType()); + dto.setAgvSystemType(task.getAgvSystemType()); + dto.setRemark(task.getRemark()); + dto.setPayload(JsonUtils.parseMap(task.getDispatchParam())); + dto.setProductArea(task.getProductArea()); + return dto; + } + +} diff --git a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/utils/package-info.java b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/utils/package-info.java new file mode 100644 index 00000000..136b5a0c --- /dev/null +++ b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/utils/package-info.java @@ -0,0 +1,6 @@ +/** + * 本模块业务员这中使用工具类 + * @Author: liyongde + * @Date: 2026/7/23 17:02 + */ +package cn.code.nl.module.task.utils; \ No newline at end of file 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 faf8d89f..06832135 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 @@ -78,9 +78,12 @@ spring: # rocketmq 配置项,对应 RocketMQProperties 配置类 rocketmq: - name-server: 127.0.0.1:9876 # RocketMQ Namesrv + name-server: 192.168.81.193:9876 # RocketMQ Namesrv producer: - group: ${spring.application.name}_TASK_DEV_PRODUCER # 生产者分组 + group: task_producer_dev_group # 生产者分组 + consumer: + task: + topic: task-status-change-dev-topic spring: # RabbitMQ 配置项,对应 RabbitProperties 配置类 diff --git a/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/api/WmsTaskExecuteApiImpl.java b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/api/WmsTaskExecuteApiImpl.java new file mode 100644 index 00000000..d1710fc2 --- /dev/null +++ b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/api/WmsTaskExecuteApiImpl.java @@ -0,0 +1,21 @@ +package cn.code.nl.module.wms.api; + +import cn.code.nl.framework.common.enums.RpcConstants; +import cn.code.nl.framework.execute.biz.api.AbstractTaskCommonApiImpl; +import cn.code.nl.framework.execute.biz.api.wms.WmsTaskCommonApi; +import org.springframework.context.annotation.Primary; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * WMS 任务通用 API 实现 + * + * @author liyongde + */ +@RestController +@Validated +@Primary +@RequestMapping(RpcConstants.WMS_PREFIX) +public class WmsTaskExecuteApiImpl extends AbstractTaskCommonApiImpl implements WmsTaskCommonApi { +} diff --git a/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/mq/consumer/TaskStatusChangeConsumer.java b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/mq/consumer/TaskStatusChangeConsumer.java deleted file mode 100644 index 57cbb940..00000000 --- a/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/mq/consumer/TaskStatusChangeConsumer.java +++ /dev/null @@ -1,54 +0,0 @@ -package cn.code.nl.module.wms.mq.consumer; - -import cn.code.nl.framework.execute.core.AbstractTask; -import cn.code.nl.framework.execute.core.TaskFactory; -import cn.code.nl.framework.execute.core.dto.TaskExecuteDTO; -import cn.code.nl.module.task.message.TaskEventMessage; -import jakarta.annotation.Resource; -import lombok.extern.slf4j.Slf4j; -import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; -import org.apache.rocketmq.spring.core.RocketMQListener; -import org.springframework.stereotype.Component; - -/** - * 监听任务状态变更,根据 handleCode 定位任务子类并执行完成/取消逻辑 - * - * @Author: liyongde - * @Date: 2026/7/20 10:28 - */ -@Slf4j -@Component -@RocketMQMessageListener( - topic = "${rocketmq.consumer.wms-task-operate.topic}", - consumerGroup = "${rocketmq.consumer.wms-task-operate.group}" -) -public class TaskStatusChangeConsumer implements RocketMQListener { - - @Resource - private TaskFactory taskFactory; - - @Override - public void onMessage(TaskEventMessage message) { - String handleCode = message.getHandleCode(); - String eventType = message.getEventType(); - log.info("收到任务状态变更消息, handleCode={}, eventType={}, taskId={}", handleCode, eventType, message.getTaskId()); - - AbstractTask task = taskFactory.getTask(handleCode); - if (task == null) { - log.warn("未找到对应任务处理器, handleCode={}", handleCode); - return; - } - - TaskExecuteDTO dto = new TaskExecuteDTO(); - dto.setTaskId(message.getTaskId()); - dto.setPayload(message.getPayload()); - - if (TaskEventMessage.EVENT_TYPE_FINISHED.equals(eventType)) { - task.doHandleFinish(dto); - } else if (TaskEventMessage.EVENT_TYPE_CANCELLED.equals(eventType)) { - task.doHandleCancel(dto); - } else { - log.warn("未知事件类型, eventType={}, handleCode={}", eventType, handleCode); - } - } -} diff --git a/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/mq/consumer/WmsTaskStatusChangeConsumer.java b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/mq/consumer/WmsTaskStatusChangeConsumer.java new file mode 100644 index 00000000..0c41724a --- /dev/null +++ b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/mq/consumer/WmsTaskStatusChangeConsumer.java @@ -0,0 +1,118 @@ +package cn.code.nl.module.wms.mq.consumer; + +import cn.code.nl.framework.common.pojo.CommonResult; +import cn.code.nl.framework.execute.core.AbstractTask; +import cn.code.nl.framework.execute.core.TaskFactory; +import cn.code.nl.framework.execute.core.dto.TaskExecuteDTO; +import cn.code.nl.module.task.api.TransportTaskApi; +import cn.code.nl.module.task.dto.TaskInfoDTO; +import cn.code.nl.module.task.enums.TransportTaskStatusEnum; +import cn.code.nl.module.task.message.TaskEventMessage; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; +import org.apache.rocketmq.spring.core.RocketMQListener; +import org.redisson.api.RLock; +import org.redisson.api.RedissonClient; +import org.springframework.stereotype.Component; + +import static cn.code.nl.module.task.message.LockKeyConstants.TASK_STATUS_CHANGE_LOCK_KEY; + +/** + * 监听任务状态变更,根据 handleCode 定位任务子类并执行完成/取消逻辑 + * + * @Author: liyongde + * @Date: 2026/7/20 10:28 + */ +@Slf4j +@Component +@RocketMQMessageListener( + topic = "${rocketmq.consumer.wms-task-operate.topic}", + consumerGroup = "${rocketmq.consumer.wms-task-operate.group}" +) +public class WmsTaskStatusChangeConsumer implements RocketMQListener { + + @Resource + private TaskFactory taskFactory; + + @Resource + private TransportTaskApi transportTaskApi; + + @Resource + private RedissonClient redissonClient; + + @Override + public void onMessage(TaskEventMessage message) { + String handleCode = message.getHandleCode(); + String eventType = message.getEventType(); + log.info("收到任务状态变更消息, handleCode={}, eventType={}, taskId={}", handleCode, eventType, message.getTaskId()); + + RLock lock = redissonClient.getLock(TASK_STATUS_CHANGE_LOCK_KEY + message.getTaskId()); + if (!lock.tryLock()) { + log.warn("任务状态变更消息正在消费中,等待 MQ 重试, taskId={}, eventType={}", message.getTaskId(), eventType); + throw new IllegalStateException("任务状态变更消息正在消费中......"); + } + try { + if (!isTaskCallbackPending(message)) { + return; + } + executeTaskHandler(message, handleCode, eventType); + } finally { + if (lock.isHeldByCurrentThread()) { + lock.unlock(); + } + } + } + + /** + * 判断任务是否处于当前事件对应的待业务处理状态 + */ + private boolean isTaskCallbackPending(TaskEventMessage message) { + String eventType = message.getEventType(); + String expectedStatus; + if (TaskEventMessage.EVENT_TYPE_FINISHED.equals(eventType)) { + expectedStatus = TransportTaskStatusEnum.FINISHED_CALLBACK_PENDING.getCode(); + } else if (TaskEventMessage.EVENT_TYPE_CANCELLED.equals(eventType)) { + expectedStatus = TransportTaskStatusEnum.CANCEL_CALLBACK_PENDING.getCode(); + } else { + log.warn("未知任务事件类型, eventType={}, taskId={}", eventType, message.getTaskId()); + return false; + } + + CommonResult result = transportTaskApi.getTaskById(message.getTaskId()); + TaskInfoDTO taskInfo = result.getCheckedData(); + if (taskInfo == null) { + log.warn("任务不存在,跳过任务状态变更消息, taskId={}, eventType={}", message.getTaskId(), eventType); + return false; + } + if (!expectedStatus.equals(taskInfo.getTaskStatus())) { + log.info("任务状态已处理,跳过重复消息, taskId={}, eventType={}, currentStatus={}, expectedStatus={}", + message.getTaskId(), eventType, taskInfo.getTaskStatus(), expectedStatus); + return false; + } + return true; + } + + /** + * 执行任务完成或取消业务处理器 + */ + private void executeTaskHandler(TaskEventMessage message, String handleCode, String eventType) { + AbstractTask task = taskFactory.getTask(handleCode); + if (task == null) { + log.warn("未找到对应任务处理器, handleCode={}", handleCode); + return; + } + + TaskExecuteDTO dto = new TaskExecuteDTO(); + dto.setTaskId(message.getTaskId()); + dto.setPayload(message.getPayload()); + + if (TaskEventMessage.EVENT_TYPE_FINISHED.equals(eventType)) { + task.doHandleFinish(dto); + } else if (TaskEventMessage.EVENT_TYPE_CANCELLED.equals(eventType)) { + task.doHandleCancel(dto); + } else { + log.warn("未知事件类型, eventType={}, handleCode={}", eventType, handleCode); + } + } +} diff --git a/nl-module-wms/nl-module-wms-server/src/main/resources/application-mq-dev.yaml b/nl-module-wms/nl-module-wms-server/src/main/resources/application-mq-dev.yaml index b0a11c37..883a1894 100644 --- a/nl-module-wms/nl-module-wms-server/src/main/resources/application-mq-dev.yaml +++ b/nl-module-wms/nl-module-wms-server/src/main/resources/application-mq-dev.yaml @@ -2,7 +2,10 @@ # rocketmq 配置项,对应 RocketMQProperties 配置类 rocketmq: name-server: 192.168.81.193:9876 + producer: + group: wms_producer_dev_group # 事务消息需要配置一样 + send-message-timeout: 3000 consumer: wms-task-operate: - group: wms-task-status-change-dev-group - topic: wms-task-status-change-dev-topic \ No newline at end of file + group: wms_task_status_change_dev_group + topic: wms_task_status_change_dev_topic \ No newline at end of file diff --git a/nl-server/src/main/resources/application-mq-dev.yaml b/nl-server/src/main/resources/application-mq-dev.yaml index b0a11c37..836b81dd 100644 --- a/nl-server/src/main/resources/application-mq-dev.yaml +++ b/nl-server/src/main/resources/application-mq-dev.yaml @@ -2,7 +2,15 @@ # rocketmq 配置项,对应 RocketMQProperties 配置类 rocketmq: name-server: 192.168.81.193:9876 + producer: + group: nl_producer_dev_group + send-message-timeout: 3000 consumer: wms-task-operate: - group: wms-task-status-change-dev-group - topic: wms-task-status-change-dev-topic \ No newline at end of file + group: wms_task_status_change_dev_group + topic: wms_task_status_change_dev_topic + lms-task-operate: + group: lms_task_status_change_dev_group + topic: lms_task_status_change_dev_topic + task: + topic: task_status_change_dev_topic \ No newline at end of file diff --git a/nl-server/src/main/resources/application-mq-test.yaml b/nl-server/src/main/resources/application-mq-test.yaml new file mode 100644 index 00000000..836b81dd --- /dev/null +++ b/nl-server/src/main/resources/application-mq-test.yaml @@ -0,0 +1,16 @@ +--- #################### MQ 消息队列相关配置 #################### +# rocketmq 配置项,对应 RocketMQProperties 配置类 +rocketmq: + name-server: 192.168.81.193:9876 + producer: + group: nl_producer_dev_group + send-message-timeout: 3000 + consumer: + wms-task-operate: + group: wms_task_status_change_dev_group + topic: wms_task_status_change_dev_topic + lms-task-operate: + group: lms_task_status_change_dev_group + topic: lms_task_status_change_dev_topic + task: + topic: task_status_change_dev_topic \ No newline at end of file diff --git a/nl-server/src/main/resources/application-test.yaml b/nl-server/src/main/resources/application-test.yaml index 67f2329e..5724823a 100644 --- a/nl-server/src/main/resources/application-test.yaml +++ b/nl-server/src/main/resources/application-test.yaml @@ -49,20 +49,20 @@ spring: master: url: jdbc:mysql://192.168.81.193:3306/huachuang_lms_dev?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例 username: root - password: root + password: root123 slave: # 模拟从库,可根据自己需要修改 # 模拟从库,可根据自己需要修改 lazy: true # 开启懒加载,保证启动速度 url: jdbc:mysql://192.168.81.193:3306/huachuang_lms_dev?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例 username: root - password: root + password: root123 # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 data: redis: - host: 192.168.81.193 # 地址 + host: 127.0.0.1 # 地址 port: 6379 # 端口 database: 1 # 数据库索引 - password: redis123 +# password: redis123 --- #################### 定时任务相关配置 #################### diff --git a/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/api/task/transporttask/index.ts b/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/api/task/transporttask/index.ts index 324cda8b..93edf5f8 100644 --- a/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/api/task/transporttask/index.ts +++ b/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/api/task/transporttask/index.ts @@ -92,5 +92,3 @@ export function operateTransportTask(data: { }) { return requestClient.post('/task/transport-task/operate', data); } - - diff --git a/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/components/table-action/icons.ts b/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/components/table-action/icons.ts index 67e8ad4e..17fb2e8a 100644 --- a/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/components/table-action/icons.ts +++ b/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/components/table-action/icons.ts @@ -14,5 +14,7 @@ export const ACTION_ICON = { BOOK: 'lucide:book', AUDIT: 'lucide:file-check', SEND: 'lucide:send', + CIRCLE_CHECK: 'lucide:circle-check', + CIRCLE_X: 'lucide:circle-x', CANCEL: 'lucide:ban', }; diff --git a/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/base/materialbase/data.ts b/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/base/materialbase/data.ts index 0937d1bd..79a612ff 100644 --- a/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/base/materialbase/data.ts +++ b/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/base/materialbase/data.ts @@ -1,12 +1,12 @@ -import type { VbenFormSchema } from '#/adapter/form'; -import type { VxeTableGridOptions } from '#/adapter/vxe-table'; -import type { BaseMaterialBaseApi } from '#/api/base/materialbase'; +import type {VbenFormSchema} from '#/adapter/form'; +import type {VxeTableGridOptions} from '#/adapter/vxe-table'; +import type {BaseMaterialBaseApi} from '#/api/base/materialbase'; -import { handleTree } from '@vben/utils'; +import {handleTree} from '@vben/utils'; -import { getClassStandardList, getClassStandardListByCode } from '#/api/base/classstandard'; -import { getSimpleMeasureUnitList } from '#/api/base/measureunit'; -import { getRangePickerDefaultProps } from '#/utils'; +import {getClassStandardList, getClassStandardListByCode} from '#/api/base/classstandard'; +import {getSimpleMeasureUnitList} from '#/api/base/measureunit'; +import {getRangePickerDefaultProps} from '#/utils'; // ========== 分类映射:classId → className ========== const classNameMap: Record = {}; @@ -46,102 +46,218 @@ async function loadMeasureUnitList() { // ========== 新增/修改的表单 ========== export function useFormSchema(): VbenFormSchema[] { return [ - { fieldName: 'materialId', component: 'Input', dependencies: { triggerFields: [''], show: () => false } }, - { fieldName: 'materialCode', label: '物料编码', rules: 'required', component: 'Input', componentProps: { placeholder: '请输入物料编码' } }, - { fieldName: 'materialName', label: '物料名称', rules: 'required', component: 'Input', componentProps: { placeholder: '请输入物料名称' } }, - { fieldName: 'materialSpec', label: '规格', component: 'Input', componentProps: { placeholder: '请输入规格' } }, - { fieldName: 'materialModel', label: '型号', component: 'Input', componentProps: { placeholder: '请输入型号' } }, - { fieldName: 'englishName', label: '外文名称', component: 'Input', componentProps: { placeholder: '请输入外文名称' } }, + { + fieldName: 'materialId', + component: 'Input', + dependencies: {triggerFields: [''], show: () => false} + }, + { + fieldName: 'materialCode', + label: '物料编码', + rules: 'required', + component: 'Input', + componentProps: {placeholder: '请输入物料编码'} + }, + { + fieldName: 'materialName', + label: '物料名称', + rules: 'required', + component: 'Input', + componentProps: {placeholder: '请输入物料名称'} + }, + { + fieldName: 'materialSpec', + label: '规格', + component: 'Input', + componentProps: {placeholder: '请输入规格'} + }, + { + fieldName: 'materialModel', + label: '型号', + component: 'Input', + componentProps: {placeholder: '请输入型号'} + }, + { + fieldName: 'englishName', + label: '外文名称', + component: 'Input', + componentProps: {placeholder: '请输入外文名称'} + }, { fieldName: 'materialTypeId', label: '物料分类', component: 'ApiTreeSelect', - componentProps: { api: loadMaterialTypeTree, labelField: 'className', valueField: 'classId', childrenField: 'children', placeholder: '请选择物料分类', allowClear: true, treeDefaultExpandAll: true }, + componentProps: { + api: loadMaterialTypeTree, + labelField: 'className', + valueField: 'classId', + childrenField: 'children', + placeholder: '请选择物料分类', + allowClear: true, + treeDefaultExpandAll: true + }, }, { fieldName: 'baseUnitId', label: '基本计量单位', rules: 'required', component: 'ApiSelect', - componentProps: { api: loadMeasureUnitList, labelField: 'unitCode', valueField: 'measureUnitId', placeholder: '请选择基本计量单位', allowClear: true }, + componentProps: { + api: loadMeasureUnitList, + labelField: 'unitCode', + valueField: 'measureUnitId', + placeholder: '请选择基本计量单位', + allowClear: true + }, }, { fieldName: 'assUnitId', label: '辅助计量单位', component: 'ApiSelect', - componentProps: { api: loadMeasureUnitList, labelField: 'unitCode', valueField: 'measureUnitId', placeholder: '请选择辅助计量单位', allowClear: true }, + componentProps: { + api: loadMeasureUnitList, + labelField: 'unitCode', + valueField: 'measureUnitId', + placeholder: '请选择辅助计量单位', + allowClear: true + }, }, { fieldName: 'lenUnitId', label: '长度单位', component: 'ApiSelect', - componentProps: { api: loadMeasureUnitList, labelField: 'unitCode', valueField: 'measureUnitId', placeholder: '请选择长度单位', allowClear: true }, + componentProps: { + api: loadMeasureUnitList, + labelField: 'unitCode', + valueField: 'measureUnitId', + placeholder: '请选择长度单位', + allowClear: true + }, }, { fieldName: 'weightUnitId', label: '重量单位', component: 'ApiSelect', - componentProps: { api: loadMeasureUnitList, labelField: 'unitCode', valueField: 'measureUnitId', placeholder: '请选择重量单位', allowClear: true }, + componentProps: { + api: loadMeasureUnitList, + labelField: 'unitCode', + valueField: 'measureUnitId', + placeholder: '请选择重量单位', + allowClear: true + }, }, { fieldName: 'cubageUnitId', label: '体积单位', component: 'ApiSelect', - componentProps: { api: loadMeasureUnitList, labelField: 'unitCode', valueField: 'measureUnitId', placeholder: '请选择体积单位', allowClear: true }, + componentProps: { + api: loadMeasureUnitList, + labelField: 'unitCode', + valueField: 'measureUnitId', + placeholder: '请选择体积单位', + allowClear: true + }, }, { fieldName: 'isUsed', label: '是否启用', rules: 'required', component: 'RadioGroup', - componentProps: { options: [{ label: '是', value: '1' }, { label: '否', value: '0' }], buttonStyle: 'solid', optionType: 'button' }, + componentProps: { + options: [{label: '是', value: '1'}, {label: '否', value: '0'}], + buttonStyle: 'solid', + optionType: 'button' + }, + }, + { + fieldName: 'extId', + label: '外部标识', + component: 'Input', + componentProps: {placeholder: '请输入外部标识'} }, - { fieldName: 'extId', label: '外部标识', component: 'Input', componentProps: { placeholder: '请输入外部标识' } }, ]; } // ========== 列表的搜索表单 ========== export function useGridFormSchema(): VbenFormSchema[] { return [ - { fieldName: 'materialCode', label: '物料编码', component: 'Input', componentProps: { allowClear: true, placeholder: '请输入物料编码' } }, - { fieldName: 'materialName', label: '物料名称', component: 'Input', componentProps: { allowClear: true, placeholder: '请输入物料名称' } }, + { + fieldName: 'materialCode', + label: '物料编码', + component: 'Input', + componentProps: {allowClear: true, placeholder: '请输入物料编码'} + }, + { + fieldName: 'materialName', + label: '物料名称', + component: 'Input', + componentProps: {allowClear: true, placeholder: '请输入物料名称'} + }, { fieldName: 'materialTypeId', label: '物料分类', component: 'ApiTreeSelect', - componentProps: { api: loadMaterialTypeTree, labelField: 'className', valueField: 'classId', childrenField: 'children', placeholder: '请选择物料分类', allowClear: true }, + componentProps: { + api: loadMaterialTypeTree, + labelField: 'className', + valueField: 'classId', + childrenField: 'children', + placeholder: '请选择物料分类', + allowClear: true + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: {...getRangePickerDefaultProps(), allowClear: true} + }, + { + fieldName: 'isUsed', + label: '是否启用', + component: 'Select', + componentProps: { + allowClear: true, + placeholder: '请选择', + options: [{label: '是', value: '1'}, {label: '否', value: '0'}] + } }, - { fieldName: 'createTime', label: '创建时间', component: 'RangePicker', componentProps: { ...getRangePickerDefaultProps(), allowClear: true } }, - { fieldName: 'isUsed', label: '是否启用', component: 'Select', componentProps: { allowClear: true, placeholder: '请选择', options: [{ label: '是', value: '1' }, { label: '否', value: '0' }] } }, ]; } // ========== 列表的字段 ========== export function useGridColumns(): VxeTableGridOptions['columns'] { return [ - { type: 'checkbox', width: 40 }, - { field: 'materialCode', title: '物料编码', minWidth: 120 }, - { field: 'materialName', title: '物料名称', minWidth: 200 }, - { field: 'materialSpec', title: '规格', minWidth: 120 }, - { field: 'materialModel', title: '型号', minWidth: 120 }, - { field: 'englishName', title: '外文名称', minWidth: 120 }, + {type: 'checkbox', width: 40}, + {field: 'materialCode', title: '物料编码', minWidth: 120}, + {field: 'materialName', title: '物料名称', minWidth: 200}, + {field: 'materialSpec', title: '规格', minWidth: 120}, + {field: 'materialModel', title: '型号', minWidth: 120}, + {field: 'englishName', title: '外文名称', minWidth: 120}, { field: 'materialTypeId', title: '物料分类', minWidth: 150, - formatter: ({ cellValue }: { cellValue: number }) => classNameMap[cellValue] || cellValue || '-', + formatter: ({cellValue}: { + cellValue: number + }) => classNameMap[cellValue] || cellValue || '-', }, { field: 'baseUnitId', title: '基本计量单位', minWidth: 120, - formatter: ({ cellValue }: { cellValue: number }) => unitCodeMap[cellValue] || cellValue || '-', + formatter: ({cellValue}: { cellValue: number }) => unitCodeMap[cellValue] || cellValue || '-', }, { field: 'assUnitId', title: '辅助计量单位', minWidth: 120, - formatter: ({ cellValue }: { cellValue: number }) => unitCodeMap[cellValue] || cellValue || '-', + formatter: ({cellValue}: { cellValue: number }) => unitCodeMap[cellValue] || cellValue || '-', }, { field: 'lenUnitId', title: '长度单位', minWidth: 120, - formatter: ({ cellValue }: { cellValue: number }) => unitCodeMap[cellValue] || cellValue || '-', + formatter: ({cellValue}: { cellValue: number }) => unitCodeMap[cellValue] || cellValue || '-', }, { field: 'weightUnitId', title: '重量单位', minWidth: 120, - formatter: ({ cellValue }: { cellValue: number }) => unitCodeMap[cellValue] || cellValue || '-', + formatter: ({cellValue}: { cellValue: number }) => unitCodeMap[cellValue] || cellValue || '-', }, { field: 'cubageUnitId', title: '体积单位', minWidth: 120, - formatter: ({ cellValue }: { cellValue: number }) => unitCodeMap[cellValue] || cellValue || '-', + formatter: ({cellValue}: { cellValue: number }) => unitCodeMap[cellValue] || cellValue || '-', }, - { field: 'createTime', title: '创建时间', minWidth: 180, formatter: 'formatDateTime' }, - { field: 'isUsed', title: '是否启用', minWidth: 100, formatter: ({ cellValue }: { cellValue: string }) => (cellValue === '1' ? '是' : '否') }, - { field: 'extId', title: '外部标识', minWidth: 120 }, - { title: '操作', width: 200, fixed: 'right', slots: { default: 'actions' } }, + {field: 'createTime', title: '创建时间', minWidth: 180, formatter: 'formatDateTime'}, + { + field: 'isUsed', + title: '是否启用', + minWidth: 100, + formatter: ({cellValue}: { cellValue: string }) => (cellValue === '1' ? '是' : '否') + }, + {field: 'extId', title: '外部标识', minWidth: 120}, + {title: '操作', width: 200, fixed: 'right', slots: {default: 'actions'}}, ]; } diff --git a/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/task/transporttask/data.ts b/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/task/transporttask/data.ts index da5443dd..8032b1bb 100644 --- a/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/task/transporttask/data.ts +++ b/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/task/transporttask/data.ts @@ -4,12 +4,31 @@ import type { TaskTransportTaskApi } from '#/api/task/transporttask'; import { DICT_TYPE } from '@vben/constants'; import { getDictOptions } from '@vben/hooks'; +import { handleTree } from '@vben/utils'; import dayjs from 'dayjs'; +import { getClassStandardListByCode } from '#/api/base/classstandard'; import { getRangePickerDefaultProps } from '#/utils'; +const taskTypeNameMap: Record = {}; + +async function loadTaskTypeTree() { + const data = await getClassStandardListByCode('0002'); + if (!data || data.length === 0) return []; + return handleTree(data, 'classId', 'parentClassId'); +} + /** 新增/修改的表单 */ +export async function loadAllLookupData() { + const taskTypeData = await getClassStandardListByCode('0002'); + taskTypeData?.forEach((item) => { + if (item.classCode) { + taskTypeNameMap[item.classCode] = item.className; + } + }); +} + export function useFormSchema(): VbenFormSchema[] { return [ { @@ -369,11 +388,15 @@ export function useGridFormSchema(): VbenFormSchema[] { { fieldName: 'taskType', label: '任务类型', - component: 'Select', + component: 'ApiTreeSelect', componentProps: { allowClear: true, - options: [], + api: loadTaskTypeTree, + childrenField: 'children', + labelField: 'className', placeholder: '请选择任务类型', + treeDefaultExpandAll: true, + valueField: 'classCode', }, }, { @@ -588,6 +611,8 @@ export function useGridColumns(): VxeTableGridOptions + taskTypeNameMap[cellValue] || cellValue || '-', }, { field: 'acsTaskType', @@ -608,6 +633,10 @@ export function useGridColumns(): VxeTableGridOptions { + await loadAllLookupData(); + ready.value = true; +}); + const [FormModal, formModalApi] = useVbenModal({ connectedComponent: Form, destroyOnClose: true, @@ -141,7 +147,7 @@ const [Grid, gridApi] = useVbenVxeGrid({