From 4c23669864b71903b2cb94909d44ff20fbf7ef6e Mon Sep 17 00:00:00 2001 From: gaoqr <13665037151@163.com> Date: Tue, 4 Aug 2026 16:18:52 +0800 Subject: [PATCH] =?UTF-8?q?1=E3=80=81=E6=96=B0=E5=A2=9E=E6=8E=A5=E5=85=A5?= =?UTF-8?q?=E7=BB=84=E4=BB=B6=E4=BF=AE=E6=94=B9=E5=8A=A0=E5=B7=A5=E6=96=B9?= =?UTF-8?q?=E6=A1=88=E7=BB=84=EF=BC=9B2=E3=80=81=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E7=BB=84=E4=BB=B6=E5=8A=A0=E5=B7=A5=E6=96=B9=E6=A1=88=E7=BB=84?= =?UTF-8?q?=E5=88=97=E8=A1=A8=E6=8E=A5=E5=8F=A3=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/admin/plan/PlanController.java | 18 ++- .../executor/service/plan/PlanService.java | 16 ++- .../service/plan/PlanServiceImpl.java | 16 +++ .../producing/ProducingPlanOrderClient.java | 117 +++++++++++++++++- .../ProducingPlanOrderProperties.java | 5 + .../ProducingPlanOrderClientTest.java | 40 +++++- 6 files changed, 203 insertions(+), 9 deletions(-) diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/plan/PlanController.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/plan/PlanController.java index 10851e737..f7a5315ca 100644 --- a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/plan/PlanController.java +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/plan/PlanController.java @@ -11,6 +11,7 @@ import com.cf.imes.module.executor.controller.admin.plan.bo.OrderRoomBodyList; import com.cf.imes.module.executor.controller.admin.plan.vo.*; import com.cf.imes.module.executor.controller.admin.plate.vo.OrderViewPlatePageRespVO; import com.cf.imes.module.executor.service.plan.PlanService; +import com.fasterxml.jackson.databind.JsonNode; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; @@ -22,6 +23,7 @@ import jakarta.annotation.Resource; import jakarta.servlet.http.HttpServletResponse; import jakarta.validation.Valid; import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.Min; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; import java.io.IOException; @@ -202,4 +204,18 @@ public class PlanController { planService.releasePlanLock(orderPlanLockReqVO); return success(true); } -} \ No newline at end of file + + @GetMapping("/producing/workflow-scheme/list/{page}") + @Operation(summary = "查询组件生产系统工作流方案列表") + @PreAuthorize("@ss.hasAnyPermissions('manage:order-plan:process-scheme','producePlan:order-plan:process-scheme')") + public CommonResult getProducingWorkflowSchemeList( + @PathVariable @Min(value = 1, message = "页码必须大于 0") Integer page, + @RequestParam(required = false) Integer type, + @RequestParam(required = false) Long parentId, + @RequestParam(defaultValue = "20") + @Min(value = 1, message = "每页数量必须大于 0") Integer pageSize, + @RequestParam(defaultValue = "true") Boolean isPaging) { + return success(planService.getProducingWorkflowSchemeList( + page, type, parentId, pageSize, isPaging)); + } +} diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/service/plan/PlanService.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/service/plan/PlanService.java index 359f77626..2e69f6526 100644 --- a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/service/plan/PlanService.java +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/service/plan/PlanService.java @@ -7,6 +7,7 @@ import com.cf.imes.module.executor.controller.admin.plan.bo.OrderRoomBodyList; import com.cf.imes.module.executor.controller.admin.plan.vo.*; import com.cf.imes.module.executor.controller.admin.plate.vo.OrderViewPlatePageRespVO; +import com.fasterxml.jackson.databind.JsonNode; import jakarta.validation.Valid; import java.util.List; @@ -112,4 +113,17 @@ public interface PlanService { * @param orderPlanLockReqVO */ void releasePlanLock(OrderPlanLockReqVO orderPlanLockReqVO); -} \ No newline at end of file + + /** + * 查询组件生产系统工作流方案列表。 + * + * @param page 页码 + * @param type 工作流类型,可为空 + * @param parentId 父级方案 ID,可为空 + * @param pageSize 每页数量 + * @param isPaging 是否分页 + * @return 工作流方案列表响应数据 + */ + JsonNode getProducingWorkflowSchemeList(Integer page, Integer type, Long parentId, + Integer pageSize, Boolean isPaging); +} diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/service/plan/PlanServiceImpl.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/service/plan/PlanServiceImpl.java index e41369d45..c857a09cf 100644 --- a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/service/plan/PlanServiceImpl.java +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/service/plan/PlanServiceImpl.java @@ -61,6 +61,7 @@ import com.cf.imes.module.executor.enums.OrderStatusEnum; import com.cf.imes.module.executor.service.plan.producing.ProducingPlanOrderClient; import com.cf.imes.module.executor.service.plan.producing.ProducingPlanOrderMaterialGroup; import com.cf.imes.module.system.api.auth.AuthApi; +import com.fasterxml.jackson.databind.JsonNode; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.aop.framework.AopContext; @@ -247,6 +248,13 @@ public class PlanServiceImpl implements PlanService { Long processId = planDO.getProcessId(); if (ObjectUtil.notEqual(processId, currProcessId)) { planDO.setPreviousProcessId(processId); + if (planDO.getProducingPlanOrderId() == null) { + throw new ServiceException(PRODUCING_PLAN_ORDER_SYNC_FAILED, + "producingPlanOrderId is required"); + } + producingPlanOrderClient.changeScheme( + getProducingToken(), planDO.getProducingPlanOrderId(), + currProcessId); } planDO.setProcessId(currProcessId); @@ -2169,6 +2177,14 @@ public class PlanServiceImpl implements PlanService { return authApi.getMesJwtToken().getCheckedData(); } + @Override + public JsonNode getProducingWorkflowSchemeList(Integer page, Integer type, + Long parentId, Integer pageSize, + Boolean isPaging) { + return producingPlanOrderClient.listWorkflowSchemes( + getProducingToken(), page, type, parentId, pageSize, isPaging); + } + @Override public void getPlanLock(OrderPlanLockReqVO orderPlanLockReqVO) { Long planId = orderPlanLockReqVO.getPlanId(); diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/service/plan/producing/ProducingPlanOrderClient.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/service/plan/producing/ProducingPlanOrderClient.java index 5dc7e1e23..dcf03175b 100644 --- a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/service/plan/producing/ProducingPlanOrderClient.java +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/service/plan/producing/ProducingPlanOrderClient.java @@ -55,7 +55,7 @@ public class ProducingPlanOrderClient { JsonNode planOrderIdNode = result; if (result != null && result.isObject()) { JsonNode code = result.get("code"); - if (code == null || !code.isIntegralNumber() || code.intValue() != 0) { + if (code == null || !isSuccessCode(code)) { String message = result.path("message").asText("business error"); logRequestFailure("create", method, url, requestBody, response.statusCode(), response.body(), null); @@ -101,10 +101,112 @@ public class ProducingPlanOrderClient { validateBusinessResponse(response, "delete", method, url, null); } + /** + * 查询组件生产系统的工作流方案列表。 + * + * @param token MES JWT + * @param page 页码 + * @param type 工作流类型,可为空 + * @param parentId 父级方案 ID,可为空 + * @param pageSize 每页数量 + * @param isPaging 是否分页 + * @return 对方统一响应中的 data;响应没有 data 时返回完整响应对象 + */ + public JsonNode listWorkflowSchemes(String token, Integer page, Integer type, + Long parentId, Integer pageSize, Boolean isPaging) { + String method = "GET"; + String url = workflowSchemeListUrl(page, type, parentId, pageSize, isPaging); + HttpResponse response = send( + url, token, method, null, "list-workflow-scheme"); + try { + JsonNode result = objectMapper.readTree(response.body()); + if (result == null) { + logRequestFailure("list-workflow-scheme", method, url, null, + response.statusCode(), response.body(), null); + throw syncError("list-workflow-scheme: empty response"); + } + if (result.isObject() && result.has("code")) { + JsonNode code = result.get("code"); + if (!isSuccessCode(code)) { + String message = result.path("message").asText("business error"); + logRequestFailure("list-workflow-scheme", method, url, null, + response.statusCode(), response.body(), null); + throw syncError("list-workflow-scheme: " + message); + } + JsonNode data = result.get("data"); + return data == null ? result : data; + } + return result; + } catch (JsonProcessingException ex) { + logRequestFailure("list-workflow-scheme", method, url, null, + response.statusCode(), response.body(), ex); + throw syncError("list-workflow-scheme: invalid response"); + } + } + + /** + * 修改组件生产排单使用的工作流方案。 + * + * @param token MES JWT + * @param planOrderId 组件生产排单 ID + * @param workflowSchemeId 新工作流方案 ID + */ + public void changeScheme(String token, Long planOrderId, Long workflowSchemeId) { + String method = "PUT"; + String url = resourceUrl(planOrderId, "change-scheme-id"); + String requestBody = serialize(new ChangeSchemeRequest(workflowSchemeId)); + HttpResponse response = send( + url, token, method, requestBody, "change-scheme-id"); + validateBusinessResponse( + response, "change-scheme-id", method, url, requestBody); + } + private String resourceUrl(Long planOrderId, String action) { return properties.getBaseUrl() + "/" + planOrderId + "/" + action; } + private String workflowSchemeListUrl(Integer page, Integer type, Long parentId, + Integer pageSize, Boolean isPaging) { + StringBuilder url = new StringBuilder(workflowSchemeBaseUrl()) + .append("/list/").append(page == null ? 1 : page); + String separator = "?"; + if (type != null) { + url.append(separator).append("type=").append(type); + separator = "&"; + } + if (parentId != null) { + url.append(separator).append("parentId=").append(parentId); + separator = "&"; + } + if (pageSize != null) { + url.append(separator).append("pageSize=").append(pageSize); + separator = "&"; + } + if (isPaging != null) { + url.append(separator).append("isPaging=").append(isPaging); + } + return url.toString(); + } + + private String workflowSchemeBaseUrl() { + String configuredUrl = properties.getWorkflowSchemeBaseUrl(); + if (configuredUrl != null && !configuredUrl.isBlank()) { + return removeTrailingSlash(configuredUrl); + } + String planOrderBaseUrl = removeTrailingSlash(properties.getBaseUrl()); + String suffix = "/plan-order"; + if (planOrderBaseUrl == null || !planOrderBaseUrl.endsWith(suffix)) { + throw syncError("cannot derive workflow scheme url from baseUrl"); + } + return planOrderBaseUrl.substring(0, planOrderBaseUrl.length() - suffix.length()) + + "/workflow-scheme"; + } + + private String removeTrailingSlash(String url) { + return url != null && url.endsWith("/") + ? url.substring(0, url.length() - 1) : url; + } + private String serialize(Object request) { try { return objectMapper.writeValueAsString(request); @@ -162,7 +264,7 @@ public class ProducingPlanOrderClient { try { JsonNode result = objectMapper.readTree(response.body()); JsonNode code = result == null || !result.isObject() ? null : result.get("code"); - if (code != null && code.isIntegralNumber() && code.intValue() != 0) { + if (code != null && !isSuccessCode(code)) { String message = result.path("message").asText("business error"); logRequestFailure(operation, method, url, requestBody, response.statusCode(), response.body(), null); @@ -194,6 +296,11 @@ public class ProducingPlanOrderClient { responseBodyForLog(responseBody), ex); } + private boolean isSuccessCode(JsonNode code) { + return code.isIntegralNumber() && code.intValue() == 0 + || code.isTextual() && "0".equals(code.textValue()); + } + private String responseBodyForLog(String body) { if (body == null) { return ""; @@ -225,4 +332,10 @@ public class ProducingPlanOrderClient { private static class DeleteBlocksRequest { private List deleteBlockIds; } + + @Data + @AllArgsConstructor + private static class ChangeSchemeRequest { + private Long workflowSchemeId; + } } diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/service/plan/producing/ProducingPlanOrderProperties.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/service/plan/producing/ProducingPlanOrderProperties.java index dda6bc88f..7515ca497 100644 --- a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/service/plan/producing/ProducingPlanOrderProperties.java +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/service/plan/producing/ProducingPlanOrderProperties.java @@ -17,6 +17,11 @@ public class ProducingPlanOrderProperties { private String baseUrl = "https://chenfeng.tech:38113/api-branch=opt_split_order/api/v1/producing/plan-order"; + /** + * 组件生产系统工作流方案接口地址。为空时根据 {@link #baseUrl} 自动推导。 + */ + private String workflowSchemeBaseUrl; + private Duration connectTimeout = Duration.ofSeconds(5); private Duration readTimeout = Duration.ofSeconds(10); diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/test/java/com/cf/imes/module/executor/service/plan/producing/ProducingPlanOrderClientTest.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/test/java/com/cf/imes/module/executor/service/plan/producing/ProducingPlanOrderClientTest.java index a051913bc..99d273646 100644 --- a/cf-module-prod-executor/cf-module-prod-executor-biz/src/test/java/com/cf/imes/module/executor/service/plan/producing/ProducingPlanOrderClientTest.java +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/test/java/com/cf/imes/module/executor/service/plan/producing/ProducingPlanOrderClientTest.java @@ -27,6 +27,7 @@ class ProducingPlanOrderClientTest { void setUp() throws IOException { server = HttpServer.create(new InetSocketAddress(0), 0); server.createContext("/api/v1/producing/plan-order", this::handle); + server.createContext("/api/v1/producing/workflow-scheme", this::handle); server.start(); ProducingPlanOrderProperties properties = new ProducingPlanOrderProperties(); @@ -90,24 +91,53 @@ class ProducingPlanOrderClientTest { .get("deleteBlockIds").get(0).textValue()).isEqualTo("source-2"); } + @Test + void shouldListWorkflowSchemesAndChangePlanScheme() throws Exception { + JsonNode data = client.listWorkflowSchemes( + "mes-token", 2, 1, 10L, 50, false); + client.changeScheme("mes-token", 98765L, 36L); + + assertThat(data.get("list").get(0).get("id").longValue()).isEqualTo(36L); + assertThat(requests).hasSize(2); + assertThat(requests).extracting(RecordedRequest::method) + .containsExactly("GET", "PUT"); + assertThat(requests).extracting(RecordedRequest::path) + .containsExactly( + "/api/v1/producing/workflow-scheme/list/2", + "/api/v1/producing/plan-order/98765/change-scheme-id"); + assertThat(requests.get(0).query()) + .isEqualTo("type=1&parentId=10&pageSize=50&isPaging=false"); + assertThat(objectMapper.readTree(requests.get(1).body()) + .get("workflowSchemeId").longValue()).isEqualTo(36L); + } + private void handle(HttpExchange exchange) throws IOException { String body = new String(exchange.getRequestBody().readAllBytes(), StandardCharsets.UTF_8); requests.add(new RecordedRequest( exchange.getRequestMethod(), exchange.getRequestURI().getPath(), + exchange.getRequestURI().getQuery(), exchange.getRequestHeaders().getFirst("Authorization"), body)); - byte[] response = exchange.getRequestURI().getPath() - .equals("/api/v1/producing/plan-order") - ? """ + String path = exchange.getRequestURI().getPath(); + byte[] response; + if (path.equals("/api/v1/producing/plan-order")) { + response = """ {"data":98765,"code":0,"message":"Request processed successfully."} - """.getBytes(StandardCharsets.UTF_8) : new byte[0]; + """.getBytes(StandardCharsets.UTF_8); + } else if (path.equals("/api/v1/producing/workflow-scheme/list/2")) { + response = """ + {"data":{"list":[{"id":36,"name":"scheme"}]},"code":0,"message":"success"} + """.getBytes(StandardCharsets.UTF_8); + } else { + response = new byte[0]; + } exchange.sendResponseHeaders(200, response.length); exchange.getResponseBody().write(response); exchange.close(); } private record RecordedRequest( - String method, String path, String authorization, String body) { + String method, String path, String query, String authorization, String body) { } }