diff --git a/nl-framework/nl-common/src/main/java/cn/code/nl/framework/common/pojo/AcsCommonResult.java b/nl-framework/nl-common/src/main/java/cn/code/nl/framework/common/pojo/AcsCommonResult.java new file mode 100644 index 00000000..ec3d6400 --- /dev/null +++ b/nl-framework/nl-common/src/main/java/cn/code/nl/framework/common/pojo/AcsCommonResult.java @@ -0,0 +1,53 @@ +package cn.code.nl.framework.common.pojo; + +import cn.code.nl.framework.common.exception.enums.GlobalErrorCodeConstants; +import lombok.Data; + +import java.util.Objects; + +/** + * acs 固定返回对象 + * @Author: liyongde + * @Date: 2026/7/30 9:05 + */ +@Data +public class AcsCommonResult { + /** + * 总条数 + */ + private Long totalElements; + + /** + * 业务数据体 + */ + private T data; + + /** + * 时间戳 + */ + private String timestamp; + + /** + * http状态码 + */ + private Integer code; + + /** + * 消息 + */ + private String message; + + /** + * 业务响应码 todo: 暂时不用 + */ + private Integer respCode; + + /** + * 业务响应信息 todo: 暂时不用 + */ + private String respMsg; + + public static boolean isSuccess(Integer code) { + return Objects.equals(code, GlobalErrorCodeConstants.SUCCESS.getCode()) || Objects.equals(code, 200); + } +} diff --git a/nl-framework/nl-spring-boot-starter-execute/src/main/java/cn/code/nl/framework/execute/util/http/AcsUtil.java b/nl-framework/nl-spring-boot-starter-execute/src/main/java/cn/code/nl/framework/execute/util/http/AcsUtil.java index d5706684..81fd560d 100644 --- a/nl-framework/nl-spring-boot-starter-execute/src/main/java/cn/code/nl/framework/execute/util/http/AcsUtil.java +++ b/nl-framework/nl-spring-boot-starter-execute/src/main/java/cn/code/nl/framework/execute/util/http/AcsUtil.java @@ -1,17 +1,22 @@ package cn.code.nl.framework.execute.util.http; -import cn.code.nl.framework.common.exception.ServiceException; -import cn.code.nl.framework.common.pojo.AcsBaseRespDTO; +import cn.code.nl.framework.common.exception.enums.GlobalErrorCodeConstants; +import cn.code.nl.framework.common.pojo.AcsCommonResult; import cn.code.nl.framework.common.util.http.HttpUtils; import cn.code.nl.framework.common.util.json.JsonUtils; import cn.code.nl.framework.common.util.spring.SpringUtils; import cn.code.nl.module.infra.api.config.ConfigApi; -import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; +import com.alibaba.fastjson.JSON; +import com.fasterxml.jackson.core.type.TypeReference; import lombok.extern.slf4j.Slf4j; -import java.net.*; +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; @@ -32,29 +37,32 @@ public class AcsUtil { private static final String ACS_DISABLED = "0"; /** - * 发送 POST 请求并解析响应 + * 发送 POST 请求并解析 ACS 通用响应 * * @param serverAddress ACS 服务地址 - * @param api API 路径 - * @param request 请求参数 - * @param responseType 响应类型 - * @return 响应对象 + * @param api API 路径 + * @param request 请求参数 + * @param responseType data 响应类型 + * @return ACS 通用响应 */ - public static T post(String serverAddress, String api, Object request, Class responseType) { + public static AcsCommonResult post(String serverAddress, String api, Object request, Class responseType) { if (isAcsDisabled()) { - return buildDefaultSuccessResponse(responseType); + return buildDefaultSuccessResponse(); } String url = buildUrl(serverAddress, api); String response; try { response = HttpUtils.post(url, headers(), JsonUtils.toJsonString(request)); + log.info("ACS 返回值:{}", JSON.toJSONString(response)); } catch (RuntimeException ex) { if (isNetworkException(ex)) { - throw new ServiceException(500, "ACS服务网络不通"); + log.error("ACS 服务网络不通,url={},request={}", url, JsonUtils.toJsonString(request), ex); + return buildErrorResponse("ACS服务网络不通"); } - throw ex; + log.error("ACS 请求失败,url={},request={}", url, JsonUtils.toJsonString(request), ex); + return buildErrorResponse(ex.getMessage()); } - return JsonUtils.parseObject(response, responseType); + return parseResponse(response, responseType); } /** @@ -82,13 +90,40 @@ public class AcsUtil { /** * 构建默认成功响应 */ - private static T buildDefaultSuccessResponse(Class responseType) { - AcsBaseRespDTO response = new AcsBaseRespDTO(); - response.setSuccess(true); - response.setCode("200"); - response.setTraceId(IdUtil.simpleUUID()); - response.setTimestamp(System.currentTimeMillis()); - return JsonUtils.convertObject(response, responseType); + private static AcsCommonResult buildDefaultSuccessResponse() { + AcsCommonResult response = new AcsCommonResult<>(); + response.setCode(GlobalErrorCodeConstants.SUCCESS.getCode()); + response.setMessage("ACS 已关闭,跳过请求"); + response.setTimestamp(String.valueOf(System.currentTimeMillis())); + return response; + } + + /** + * 构建失败响应,具体是否抛异常由业务层决定 + */ + private static AcsCommonResult buildErrorResponse(String message) { + AcsCommonResult response = new AcsCommonResult<>(); + response.setCode(GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR.getCode()); + response.setMessage(StrUtil.blankToDefault(message, "ACS 请求失败")); + response.setTimestamp(String.valueOf(System.currentTimeMillis())); + return response; + } + + /** + * 解析 ACS 响应,并将 data 转为业务指定类型 + */ + private static AcsCommonResult parseResponse(String response, Class responseType) { + AcsCommonResult rawResult = JsonUtils.parseObject(response, new TypeReference>() { + }); + AcsCommonResult result = new AcsCommonResult<>(); + result.setTotalElements(rawResult.getTotalElements()); + result.setData(JsonUtils.convertObject(rawResult.getData(), responseType)); + result.setTimestamp(rawResult.getTimestamp()); + result.setCode(rawResult.getCode()); + result.setMessage(rawResult.getMessage()); + result.setRespCode(rawResult.getRespCode()); + result.setRespMsg(rawResult.getRespMsg()); + return result; } /** 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 index 7fa5265e..a7cec543 100644 --- 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 @@ -8,9 +8,9 @@ package cn.code.nl.module.task.enums; public interface AcsApiConstants { /** 下发任务 */ - String ACS_TASK_API = "/acs-api/wms/issue-task"; + String ACS_TASK_API = "/acs-api/wms-to-acs/issue-task"; /** 检测任务 */ - String ACS_OPERATE_CHECK_API = "/acs-api/wms/check-enable-operate"; + String ACS_OPERATE_CHECK_API = "/acs-api/wms-to-acs/check-enable-operate"; } 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 index 388bce41..77c532d6 100644 --- 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 @@ -1,15 +1,14 @@ package cn.code.nl.module.task.job.dto; -import cn.code.nl.framework.common.pojo.AcsBaseRespDTO; import lombok.Data; import java.util.List; /** - * ACS 任务下发响应 + * ACS 任务下发 data 响应 */ @Data -public class AcsIssueResultRespDTO extends AcsBaseRespDTO { +public class AcsIssueResultRespDTO { /** * 下发失败的任务 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 index 876c5865..f4a690bb 100644 --- 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 @@ -1,12 +1,13 @@ package cn.code.nl.module.task.job.dto; +import cn.code.nl.framework.common.pojo.AcsBaseReqDTO; import lombok.Data; /** * ACS 任务操作校验请求 */ @Data -public class AcsOperateCheckReqDTO { +public class AcsOperateCheckReqDTO extends AcsBaseReqDTO { /** * 任务标识 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 index ab4278bb..c0da9980 100644 --- 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 @@ -1,29 +1,18 @@ package cn.code.nl.module.task.job.dto; -import cn.code.nl.framework.common.pojo.AcsBaseRespDTO; import lombok.Data; /** - * ACS 任务操作校验响应 + * ACS 任务操作校验 data 响应 */ @Data -public class AcsOperateCheckRespDTO extends AcsBaseRespDTO { +public class AcsOperateCheckRespDTO { /** * 是否允许操作 */ private Boolean enableOperate; - /** - * 是否允许操作,兼容 ACS 字段 - */ - private Boolean canOperate; - - /** - * 是否允许操作,兼容通用 data 字段 - */ - private Boolean data; - /** * 不允许操作的原因 */ 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 index 2cb22d7d..24a163aa 100644 --- 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 @@ -1,6 +1,7 @@ package cn.code.nl.module.task.manage; import cn.code.nl.framework.common.exception.ServiceException; +import cn.code.nl.framework.common.pojo.AcsCommonResult; import cn.code.nl.framework.common.pojo.CommonResult; import cn.code.nl.framework.common.util.json.JsonUtils; import cn.code.nl.framework.execute.util.http.AcsUtil; @@ -74,7 +75,8 @@ public class TransportTaskIssueManager { String requestJson = JsonUtils.toJsonString(acsTasks); try { String serverAddress = getAcsServerAddress(task.getProductArea()); - AcsIssueResultRespDTO result = AcsUtil.post(serverAddress, ACS_TASK_API, acsTasks, AcsIssueResultRespDTO.class); + AcsCommonResult result = AcsUtil.post(serverAddress, ACS_TASK_API, acsTasks, + AcsIssueResultRespDTO.class); handleSingleIssueResult(task, result); } catch (ServiceException ex) { throw ex; @@ -95,7 +97,8 @@ public class TransportTaskIssueManager { String requestJson = JsonUtils.toJsonString(acsTasks); try { String serverAddress = getAcsServerAddress(productArea); - AcsIssueResultRespDTO result = AcsUtil.post(serverAddress, ACS_TASK_API, acsTasks, AcsIssueResultRespDTO.class); + AcsCommonResult result = AcsUtil.post(serverAddress, ACS_TASK_API, acsTasks, + AcsIssueResultRespDTO.class); handleIssueResult(tasks, result); } catch (Exception ex) { log.error("自动下发任务失败,productArea={},tasks={}", productArea, requestJson, ex); @@ -125,20 +128,18 @@ public class TransportTaskIssueManager { * @param tasks 本次下发任务 * @param result ACS 响应 */ - private void handleIssueResult(List tasks, AcsIssueResultRespDTO result) { + private void handleIssueResult(List tasks, AcsCommonResult 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 下发失败"); + if (!AcsCommonResult.isSuccess(result.getCode())) { + String errorMessage = StrUtil.blankToDefault(result.getMessage(), "ACS 下发失败"); tasks.forEach(task -> updateIssueFailed(task.getTaskId(), errorMessage, resultJson)); return; } + List failedTasks = getFailedTasks(result); Map failedTaskMap = failedTasks.stream() .filter(failedTask -> failedTask.getTaskId() != null) .collect(Collectors.toMap(AcsIssueResultRespDTO.FailedTask::getTaskId, Function.identity(), (first, second) -> first)); @@ -158,21 +159,34 @@ public class TransportTaskIssueManager { * @param task 本次下发任务 * @param result ACS 响应 */ - private void handleSingleIssueResult(TransportTaskDO task, AcsIssueResultRespDTO result) { + private void handleSingleIssueResult(TransportTaskDO task, AcsCommonResult result) { if (result == null) { throw exception(TRANSPORT_TASK_ISSUE_FAILED, "ACS 返回为空"); } String resultJson = JsonUtils.toJsonString(result); - if (Boolean.TRUE.equals(result.getSuccess())) { + if (!AcsCommonResult.isSuccess(result.getCode())) { + throw exception(TRANSPORT_TASK_ISSUE_FAILED, StrUtil.blankToDefault(result.getMessage(), "ACS 下发失败")); + } + List failedTasks = getFailedTasks(result); + if (CollUtil.isEmpty(failedTasks)) { transportTaskMapper.updateIssueResult(task.getTaskId(), TransportTaskStatusEnum.ISSUED.getCode(), resultJson, null); return; } - String errorMessage = CollUtil.isNotEmpty(result.getFailedTasks()) - ? result.getFailedTasks().get(0).getErrorMessage() - : result.getMsg(); + String errorMessage = failedTasks.get(0).getErrorMessage(); throw exception(TRANSPORT_TASK_ISSUE_FAILED, StrUtil.blankToDefault(errorMessage, "ACS 下发失败")); } + /** + * 获取下发失败任务 + */ + private List getFailedTasks(AcsCommonResult result) { + AcsIssueResultRespDTO data = result.getData(); + if (data == null || data.getFailedTasks() == null) { + return Collections.emptyList(); + } + return data.getFailedTasks(); + } + /** * 更新任务为下发失败 * 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 index afd369f7..325cdd37 100644 --- 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 @@ -1,6 +1,7 @@ package cn.code.nl.module.task.manage; import cn.code.nl.framework.common.exception.ServiceException; +import cn.code.nl.framework.common.pojo.AcsCommonResult; import cn.code.nl.framework.common.pojo.CommonResult; import cn.code.nl.framework.common.util.json.JsonUtils; import cn.code.nl.framework.execute.util.http.AcsUtil; @@ -48,7 +49,7 @@ public class TransportTaskOperateCheckManager { } String serverAddress = getAcsServerAddress(task.getProductArea()); AcsOperateCheckReqDTO reqDTO = buildReqDTO(task, type); - AcsOperateCheckRespDTO result; + AcsCommonResult result; try { result = AcsUtil.post(serverAddress, ACS_OPERATE_CHECK_API, reqDTO, AcsOperateCheckRespDTO.class); } catch (ServiceException ex) { @@ -103,15 +104,23 @@ public class TransportTaskOperateCheckManager { /** * 处理 ACS 操作校验结果 */ - private void handleCheckResult(AcsOperateCheckReqDTO reqDTO, AcsOperateCheckRespDTO result) { + private void handleCheckResult(AcsOperateCheckReqDTO reqDTO, AcsCommonResult result) { if (result == null) { throw exception(TRANSPORT_TASK_ACS_OPERATE_CHECK_RESULT_EMPTY); } - Boolean enableOperate = getEnableOperate(result); + if (!AcsCommonResult.isSuccess(result.getCode())) { + log.warn("ACS 操作校验失败,reqDTO={},result={}", JsonUtils.toJsonString(reqDTO), JsonUtils.toJsonString(result)); + throw exception(TRANSPORT_TASK_ACS_OPERATE_CHECK_FAILED); + } + AcsOperateCheckRespDTO data = result.getData(); + if (data == null) { + throw exception(TRANSPORT_TASK_ACS_OPERATE_CHECK_RESULT_EMPTY); + } + Boolean enableOperate = data.getEnableOperate(); if (Boolean.TRUE.equals(enableOperate)) { return; } - String message = getMessage(result); + String message = getMessage(result, data); log.warn("ACS 拒绝 PC 端任务操作,reqDTO={},result={}", JsonUtils.toJsonString(reqDTO), JsonUtils.toJsonString(result)); throw exception(TRANSPORT_TASK_ACS_OPERATE_NOT_ALLOW, message); @@ -120,27 +129,11 @@ public class TransportTaskOperateCheckManager { /** * 获取 ACS 是否允许操作 */ - private Boolean getEnableOperate(AcsOperateCheckRespDTO result) { - if (result.getEnableOperate() != null) { - return result.getEnableOperate(); + private String getMessage(AcsCommonResult result, AcsOperateCheckRespDTO data) { + if (StrUtil.isNotBlank(data.getMessage())) { + return data.getMessage(); } - 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 不允许执行该操作"); + return StrUtil.blankToDefault(result.getMessage(), "ACS 不允许执行该操作"); } } diff --git a/nl-server/pom.xml b/nl-server/pom.xml index 22b3c9ce..cc5cb790 100644 --- a/nl-server/pom.xml +++ b/nl-server/pom.xml @@ -199,6 +199,12 @@ + + org.springframework.boot + spring-boot-starter-test + test + + diff --git a/nl-server/src/test/java/cn/code/nl/server/acs/AcsUtilLocalSpringTest.java b/nl-server/src/test/java/cn/code/nl/server/acs/AcsUtilLocalSpringTest.java new file mode 100644 index 00000000..f5095da7 --- /dev/null +++ b/nl-server/src/test/java/cn/code/nl/server/acs/AcsUtilLocalSpringTest.java @@ -0,0 +1,76 @@ +package cn.code.nl.server.acs; + +import cn.code.nl.framework.common.pojo.AcsCommonResult; +import cn.code.nl.framework.common.pojo.CommonResult; +import cn.code.nl.framework.common.util.spring.SpringUtils; +import cn.code.nl.framework.execute.util.http.AcsUtil; +import cn.code.nl.module.infra.api.config.ConfigApi; +import cn.code.nl.module.task.job.dto.AcsTaskDTO; +import lombok.Data; +import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; + +import java.util.List; +import java.util.Map; + +import static cn.code.nl.module.task.enums.AcsApiConstants.ACS_TASK_API; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +@SpringJUnitConfig +@ContextConfiguration(classes = AcsUtilLocalSpringTest.TestConfiguration.class) +class AcsUtilLocalSpringTest { + + private static final String SERVER_ADDRESS = "http://127.0.0.1:8011"; + + @Test + void shouldPostIssueTaskToLocalAcs() { + AcsCommonResult result = AcsUtil.post(SERVER_ADDRESS, ACS_TASK_API, + List.of(buildAcsTask()), AcsIssueTaskResp.class); + + assertNotNull(result); + } + + private AcsTaskDTO buildAcsTask() { + AcsTaskDTO task = new AcsTaskDTO(); + task.setTaskId(10001L); + task.setTaskCode("LOCAL-ACS-TEST-10001"); + task.setStartDeviceCode("START-TEST-01"); + task.setNextDeviceCode("END-TEST-01"); + task.setPriority("1"); + task.setVehicleCode("BOX-TEST-01"); + task.setTaskType("TRANSFER"); + task.setAgvSystemType("ACS"); + task.setProductArea("TEST"); + task.setRemark("local acs issue task test"); + task.setPayload(Map.of("source", "AcsUtilLocalSpringTest")); + return task; + } + + @Data + static class AcsIssueTaskResp { + private List failedTasks; + } + + @Data + static class FailedTask { + private Long taskId; + private String errorMessage; + } + + @Configuration(proxyBeanMethods = false) + static class TestConfiguration { + + @Bean + SpringUtils springUtils() { + return new SpringUtils(); + } + + @Bean + ConfigApi configApi() { + return key -> CommonResult.success("1"); + } + } +}