1、新增接入组件修改加工方案组;2、新增组件加工方案组列表接口;

This commit is contained in:
gaoqr
2026-08-04 16:18:52 +08:00
parent 93227a8599
commit 4c23669864
6 changed files with 203 additions and 9 deletions
@@ -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.plan.vo.*;
import com.cf.imes.module.executor.controller.admin.plate.vo.OrderViewPlatePageRespVO; import com.cf.imes.module.executor.controller.admin.plate.vo.OrderViewPlatePageRespVO;
import com.cf.imes.module.executor.service.plan.PlanService; 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.Operation;
import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
@@ -22,6 +23,7 @@ import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid; import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size; import jakarta.validation.constraints.Size;
import java.io.IOException; import java.io.IOException;
@@ -202,4 +204,18 @@ public class PlanController {
planService.releasePlanLock(orderPlanLockReqVO); planService.releasePlanLock(orderPlanLockReqVO);
return success(true); return success(true);
} }
@GetMapping("/producing/workflow-scheme/list/{page}")
@Operation(summary = "查询组件生产系统工作流方案列表")
@PreAuthorize("@ss.hasAnyPermissions('manage:order-plan:process-scheme','producePlan:order-plan:process-scheme')")
public CommonResult<JsonNode> 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));
}
} }
@@ -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.plan.vo.*;
import com.cf.imes.module.executor.controller.admin.plate.vo.OrderViewPlatePageRespVO; import com.cf.imes.module.executor.controller.admin.plate.vo.OrderViewPlatePageRespVO;
import com.fasterxml.jackson.databind.JsonNode;
import jakarta.validation.Valid; import jakarta.validation.Valid;
import java.util.List; import java.util.List;
@@ -112,4 +113,17 @@ public interface PlanService {
* @param orderPlanLockReqVO * @param orderPlanLockReqVO
*/ */
void releasePlanLock(OrderPlanLockReqVO orderPlanLockReqVO); void releasePlanLock(OrderPlanLockReqVO orderPlanLockReqVO);
/**
* 查询组件生产系统工作流方案列表。
*
* @param page 页码
* @param type 工作流类型,可为空
* @param parentId 父级方案 ID,可为空
* @param pageSize 每页数量
* @param isPaging 是否分页
* @return 工作流方案列表响应数据
*/
JsonNode getProducingWorkflowSchemeList(Integer page, Integer type, Long parentId,
Integer pageSize, Boolean isPaging);
} }
@@ -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.ProducingPlanOrderClient;
import com.cf.imes.module.executor.service.plan.producing.ProducingPlanOrderMaterialGroup; import com.cf.imes.module.executor.service.plan.producing.ProducingPlanOrderMaterialGroup;
import com.cf.imes.module.system.api.auth.AuthApi; import com.cf.imes.module.system.api.auth.AuthApi;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.aop.framework.AopContext; import org.springframework.aop.framework.AopContext;
@@ -247,6 +248,13 @@ public class PlanServiceImpl implements PlanService {
Long processId = planDO.getProcessId(); Long processId = planDO.getProcessId();
if (ObjectUtil.notEqual(processId, currProcessId)) { if (ObjectUtil.notEqual(processId, currProcessId)) {
planDO.setPreviousProcessId(processId); 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); planDO.setProcessId(currProcessId);
@@ -2169,6 +2177,14 @@ public class PlanServiceImpl implements PlanService {
return authApi.getMesJwtToken().getCheckedData(); 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 @Override
public void getPlanLock(OrderPlanLockReqVO orderPlanLockReqVO) { public void getPlanLock(OrderPlanLockReqVO orderPlanLockReqVO) {
Long planId = orderPlanLockReqVO.getPlanId(); Long planId = orderPlanLockReqVO.getPlanId();
@@ -55,7 +55,7 @@ public class ProducingPlanOrderClient {
JsonNode planOrderIdNode = result; JsonNode planOrderIdNode = result;
if (result != null && result.isObject()) { if (result != null && result.isObject()) {
JsonNode code = result.get("code"); 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"); String message = result.path("message").asText("business error");
logRequestFailure("create", method, url, requestBody, logRequestFailure("create", method, url, requestBody,
response.statusCode(), response.body(), null); response.statusCode(), response.body(), null);
@@ -101,10 +101,112 @@ public class ProducingPlanOrderClient {
validateBusinessResponse(response, "delete", method, url, null); 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<String> 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<String> response = send(
url, token, method, requestBody, "change-scheme-id");
validateBusinessResponse(
response, "change-scheme-id", method, url, requestBody);
}
private String resourceUrl(Long planOrderId, String action) { private String resourceUrl(Long planOrderId, String action) {
return properties.getBaseUrl() + "/" + planOrderId + "/" + 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) { private String serialize(Object request) {
try { try {
return objectMapper.writeValueAsString(request); return objectMapper.writeValueAsString(request);
@@ -162,7 +264,7 @@ public class ProducingPlanOrderClient {
try { try {
JsonNode result = objectMapper.readTree(response.body()); JsonNode result = objectMapper.readTree(response.body());
JsonNode code = result == null || !result.isObject() ? null : result.get("code"); 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"); String message = result.path("message").asText("business error");
logRequestFailure(operation, method, url, requestBody, logRequestFailure(operation, method, url, requestBody,
response.statusCode(), response.body(), null); response.statusCode(), response.body(), null);
@@ -194,6 +296,11 @@ public class ProducingPlanOrderClient {
responseBodyForLog(responseBody), ex); 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) { private String responseBodyForLog(String body) {
if (body == null) { if (body == null) {
return ""; return "";
@@ -225,4 +332,10 @@ public class ProducingPlanOrderClient {
private static class DeleteBlocksRequest { private static class DeleteBlocksRequest {
private List<String> deleteBlockIds; private List<String> deleteBlockIds;
} }
@Data
@AllArgsConstructor
private static class ChangeSchemeRequest {
private Long workflowSchemeId;
}
} }
@@ -17,6 +17,11 @@ public class ProducingPlanOrderProperties {
private String baseUrl = private String baseUrl =
"https://chenfeng.tech:38113/api-branch=opt_split_order/api/v1/producing/plan-order"; "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 connectTimeout = Duration.ofSeconds(5);
private Duration readTimeout = Duration.ofSeconds(10); private Duration readTimeout = Duration.ofSeconds(10);
@@ -27,6 +27,7 @@ class ProducingPlanOrderClientTest {
void setUp() throws IOException { void setUp() throws IOException {
server = HttpServer.create(new InetSocketAddress(0), 0); server = HttpServer.create(new InetSocketAddress(0), 0);
server.createContext("/api/v1/producing/plan-order", this::handle); server.createContext("/api/v1/producing/plan-order", this::handle);
server.createContext("/api/v1/producing/workflow-scheme", this::handle);
server.start(); server.start();
ProducingPlanOrderProperties properties = new ProducingPlanOrderProperties(); ProducingPlanOrderProperties properties = new ProducingPlanOrderProperties();
@@ -90,24 +91,53 @@ class ProducingPlanOrderClientTest {
.get("deleteBlockIds").get(0).textValue()).isEqualTo("source-2"); .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 { private void handle(HttpExchange exchange) throws IOException {
String body = new String(exchange.getRequestBody().readAllBytes(), StandardCharsets.UTF_8); String body = new String(exchange.getRequestBody().readAllBytes(), StandardCharsets.UTF_8);
requests.add(new RecordedRequest( requests.add(new RecordedRequest(
exchange.getRequestMethod(), exchange.getRequestMethod(),
exchange.getRequestURI().getPath(), exchange.getRequestURI().getPath(),
exchange.getRequestURI().getQuery(),
exchange.getRequestHeaders().getFirst("Authorization"), exchange.getRequestHeaders().getFirst("Authorization"),
body)); body));
byte[] response = exchange.getRequestURI().getPath() String path = exchange.getRequestURI().getPath();
.equals("/api/v1/producing/plan-order") byte[] response;
? """ if (path.equals("/api/v1/producing/plan-order")) {
response = """
{"data":98765,"code":0,"message":"Request processed successfully."} {"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.sendResponseHeaders(200, response.length);
exchange.getResponseBody().write(response); exchange.getResponseBody().write(response);
exchange.close(); exchange.close();
} }
private record RecordedRequest( private record RecordedRequest(
String method, String path, String authorization, String body) { String method, String path, String query, String authorization, String body) {
} }
} }