mirror of
http://192.168.1.205:9980/cf_devdept2/cf_imes_server.git
synced 2026-08-12 21:02:08 +08:00
1、拉取组件业务数据接口order_plate和order_item批量改为2k;2、新增接入组件排单 相关四个接口:新增/删除排单、新增/删除小板;
This commit is contained in:
+1
@@ -24,6 +24,7 @@ public class ErrorCodeConstants {
|
||||
public static final ErrorCode ORDER_PLATE_UPDATE_CONCURRENCY_ERROR = new ErrorCode(1_003_107_012, "order.plate.update.concurrency.error");
|
||||
public static final ErrorCode GOODS_PLATE_UPDATE_CONCURRENCY_ERROR = new ErrorCode(1_003_107_013, "goods.plate.update.concurrency.error");
|
||||
public static final ErrorCode BODY_PLATE_UPDATE_CONCURRENCY_ERROR = new ErrorCode(1_003_107_014, "body.plate.update.concurrency.error");
|
||||
public static final ErrorCode PRODUCING_PLAN_ORDER_SYNC_FAILED = new ErrorCode(1_003_107_016, "producing.plan.order.sync.failed");
|
||||
public static final ErrorCode ORDER_PLAN_LOCKED = new ErrorCode(1_003_107_015, "当前排单已锁定,请稍后再试");
|
||||
|
||||
|
||||
|
||||
+5
@@ -152,4 +152,9 @@ public class PlanDO extends BaseDO {
|
||||
* 已开板件数量
|
||||
*/
|
||||
private Integer cuttedPlateNum;
|
||||
|
||||
/**
|
||||
* 组件生产系统返回的生产排单 ID。
|
||||
*/
|
||||
private Long producingPlanOrderId;
|
||||
}
|
||||
+3
-1
@@ -2,6 +2,7 @@ package com.cf.imes.module.executor.framework.rpc.config;
|
||||
|
||||
import com.cf.imes.module.infra.api.file.FileApi;
|
||||
import com.cf.imes.module.system.api.application.ApplicationApi;
|
||||
import com.cf.imes.module.system.api.auth.AuthApi;
|
||||
import com.cf.imes.module.system.api.assemblyconfig.AssemblyConfigApi;
|
||||
import com.cf.imes.module.system.api.customplateno.CustomPlateNoSeqApi;
|
||||
import com.cf.imes.module.system.api.dataSource.DataSourceApi;
|
||||
@@ -20,6 +21,7 @@ import org.springframework.context.annotation.Configuration;
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableFeignClients(clients = {AdminUserApi.class, MachineApi.class, DictDataApi.class,
|
||||
FileApi.class, FileApi.class, DataSourceApi.class, ApplicationApi.class,
|
||||
OrganApi.class, SystemConfigApi.class, CustomPlateNoSeqApi.class, PermissionApi.class, AssemblyConfigApi.class})
|
||||
OrganApi.class, SystemConfigApi.class, CustomPlateNoSeqApi.class, PermissionApi.class,
|
||||
AssemblyConfigApi.class, AuthApi.class})
|
||||
public class RpcConfiguration {
|
||||
}
|
||||
|
||||
+142
-1
@@ -58,6 +58,8 @@ import com.cf.imes.module.executor.dal.mysql.remainplaten.RemainPlateMapper;
|
||||
import com.cf.imes.module.executor.enums.ErrorCodeConstants;
|
||||
import com.cf.imes.module.executor.enums.OrderPlanQueryTypeEnum;
|
||||
import com.cf.imes.module.executor.enums.OrderStatusEnum;
|
||||
import com.cf.imes.module.executor.service.plan.producing.ProducingPlanOrderClient;
|
||||
import com.cf.imes.module.system.api.auth.AuthApi;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.aop.framework.AopContext;
|
||||
@@ -131,6 +133,12 @@ public class PlanServiceImpl implements PlanService {
|
||||
@Resource
|
||||
private StringRedisTemplate stringRedisTemplate;
|
||||
|
||||
@Resource
|
||||
private AuthApi authApi;
|
||||
|
||||
@Resource
|
||||
private ProducingPlanOrderClient producingPlanOrderClient;
|
||||
|
||||
// 重复使用的常量
|
||||
private static final String FIELD_ORDER_ID = "o.id";
|
||||
private static final String FIELD_ORDER_TYPE = "o.order_type";
|
||||
@@ -203,6 +211,7 @@ public class PlanServiceImpl implements PlanService {
|
||||
}
|
||||
// 增加排单
|
||||
planMapper.insertBatch(planDOS);
|
||||
createProducingPlanOrders(planDOS);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -319,6 +328,7 @@ public class PlanServiceImpl implements PlanService {
|
||||
.in(OrderItemDO::getPlateId, plateIdList));
|
||||
|
||||
}
|
||||
deleteProducingPlanOrders(planDOS);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1291,7 +1301,7 @@ public class PlanServiceImpl implements PlanService {
|
||||
|
||||
List<Long> deletePlanIds = deleteOrderPlateIds.stream().map(OrderPlateIds::getPlanId).distinct().toList();
|
||||
|
||||
validatePlanExists(deletePlanIds);
|
||||
List<PlanDO> deletePlans = validatePlanExists(deletePlanIds);
|
||||
|
||||
List<Integer> plateStatus = Arrays.asList(OrderPlateCutStatusEnum.OPENING.getStatus(),OrderPlateCutStatusEnum.OPENED.getStatus());
|
||||
|
||||
@@ -1312,6 +1322,18 @@ public class PlanServiceImpl implements PlanService {
|
||||
|
||||
AssertUtils.empty(plateDOList, PLAN_PLATE_IS_CUTTED);
|
||||
|
||||
Map<Long, String> sourceBlockIdByPlateId = plateDOS.stream()
|
||||
.filter(plate -> StringUtils.isNotBlank(plate.getSourceBlockItemId()))
|
||||
.collect(Collectors.toMap(PlateDO::getId, PlateDO::getSourceBlockItemId));
|
||||
Map<Long, List<String>> sourceBlockIdsByPlanId = deleteOrderPlateIds.stream()
|
||||
.filter(item -> sourceBlockIdByPlateId.containsKey(item.getPlateId()))
|
||||
.collect(Collectors.groupingBy(
|
||||
OrderPlateIds::getPlanId,
|
||||
Collectors.mapping(
|
||||
item -> sourceBlockIdByPlateId.get(item.getPlateId()),
|
||||
Collectors.collectingAndThen(Collectors.toList(),
|
||||
list -> list.stream().distinct().toList()))));
|
||||
|
||||
// 移除排单板件 & 重新计算订单和材质下的数量和面积
|
||||
removePlanOrderAndGoodsPlate(plateDOS, deleteOrderIds);
|
||||
|
||||
@@ -1341,6 +1363,8 @@ public class PlanServiceImpl implements PlanService {
|
||||
.set(PlanDO::getOrderNos, orderNos));
|
||||
}
|
||||
}
|
||||
syncDeletedProducingPlanBlocks(
|
||||
deletePlans, sourceBlockIdsByPlanId, new HashSet<>(deleteEmptyPlanIdList));
|
||||
}
|
||||
|
||||
|
||||
@@ -1390,6 +1414,12 @@ public class PlanServiceImpl implements PlanService {
|
||||
// 新增排单板件 & 重新计算订单和材质下的数量和面积
|
||||
insertPlanOrderAndGoodsPlate(planDO.getId(), insertOrderPlateIds, orderIds);
|
||||
|
||||
List<String> sourceBlockIds = getSourceBlockIds(
|
||||
insertOrderPlateIds.stream().map(OrderPlateIds::getPlateId).toList());
|
||||
if (CollUtil.isNotEmpty(sourceBlockIds)) {
|
||||
appendProducingPlanBlocks(planDO, sourceBlockIds);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1941,6 +1971,117 @@ public class PlanServiceImpl implements PlanService {
|
||||
return resultList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 为包含组件来源板件的本地排单创建组件生产排单。
|
||||
*/
|
||||
private void createProducingPlanOrders(List<PlanDO> plans) {
|
||||
String token = null;
|
||||
for (PlanDO plan : plans) {
|
||||
List<String> sourceBlockIds = getSourceBlockIdsByPlanId(plan.getId());
|
||||
if (CollUtil.isEmpty(sourceBlockIds)) {
|
||||
continue;
|
||||
}
|
||||
if (plan.getProcessId() == null) {
|
||||
throw new ServiceException(PRODUCING_PLAN_ORDER_SYNC_FAILED,
|
||||
"workflowSchemeId is required");
|
||||
}
|
||||
if (token == null) {
|
||||
token = getProducingToken();
|
||||
}
|
||||
Long producingPlanOrderId = producingPlanOrderClient.create(
|
||||
token, plan.getProcessId(), sourceBlockIds);
|
||||
plan.setProducingPlanOrderId(producingPlanOrderId);
|
||||
planMapper.update(new LambdaUpdateWrapper<PlanDO>()
|
||||
.eq(PlanDO::getId, plan.getId())
|
||||
.eq(PlanDO::getOrganId, getUserOrganId())
|
||||
.set(PlanDO::getProducingPlanOrderId, producingPlanOrderId));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 向已有组件生产排单追加板件。
|
||||
*
|
||||
* <p>{@code producingPlanOrderId} 为空表示接入组件系统之前创建的历史排单,
|
||||
* 此类排单不调用组件生产系统,也不补建远端排单。</p>
|
||||
*/
|
||||
private void appendProducingPlanBlocks(PlanDO plan, List<String> sourceBlockIds) {
|
||||
if (plan.getProducingPlanOrderId() == null) {
|
||||
return;
|
||||
}
|
||||
producingPlanOrderClient.appendBlocks(
|
||||
getProducingToken(), plan.getProducingPlanOrderId(), sourceBlockIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步删除排单板件;本地排单已变空时直接删除对方排单。
|
||||
*/
|
||||
private void syncDeletedProducingPlanBlocks(
|
||||
List<PlanDO> plans,
|
||||
Map<Long, List<String>> sourceBlockIdsByPlanId,
|
||||
Set<Long> deletedPlanIds) {
|
||||
String token = null;
|
||||
for (PlanDO plan : plans) {
|
||||
if (plan.getProducingPlanOrderId() == null) {
|
||||
continue;
|
||||
}
|
||||
List<String> sourceBlockIds = sourceBlockIdsByPlanId.get(plan.getId());
|
||||
if (!deletedPlanIds.contains(plan.getId()) && CollUtil.isEmpty(sourceBlockIds)) {
|
||||
continue;
|
||||
}
|
||||
if (token == null) {
|
||||
token = getProducingToken();
|
||||
}
|
||||
if (deletedPlanIds.contains(plan.getId())) {
|
||||
producingPlanOrderClient.delete(token, plan.getProducingPlanOrderId());
|
||||
} else {
|
||||
producingPlanOrderClient.deleteBlocks(
|
||||
token, plan.getProducingPlanOrderId(), sourceBlockIds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除本地排单时同步删除其组件生产排单。
|
||||
*/
|
||||
private void deleteProducingPlanOrders(List<PlanDO> plans) {
|
||||
String token = null;
|
||||
for (PlanDO plan : plans) {
|
||||
if (plan.getProducingPlanOrderId() == null) {
|
||||
continue;
|
||||
}
|
||||
if (token == null) {
|
||||
token = getProducingToken();
|
||||
}
|
||||
producingPlanOrderClient.delete(token, plan.getProducingPlanOrderId());
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> getSourceBlockIdsByPlanId(Long planId) {
|
||||
return plateMapper.selectList(new LambdaQueryWrapper<PlateDO>()
|
||||
.eq(PlateDO::getPlanId, planId)
|
||||
.eq(PlateDO::getOrganId, getUserOrganId()))
|
||||
.stream()
|
||||
.map(PlateDO::getSourceBlockItemId)
|
||||
.filter(StringUtils::isNotBlank)
|
||||
.distinct()
|
||||
.toList();
|
||||
}
|
||||
|
||||
private List<String> getSourceBlockIds(Collection<Long> plateIds) {
|
||||
if (CollUtil.isEmpty(plateIds)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return plateMapper.selectByIds(plateIds).stream()
|
||||
.map(PlateDO::getSourceBlockItemId)
|
||||
.filter(StringUtils::isNotBlank)
|
||||
.distinct()
|
||||
.toList();
|
||||
}
|
||||
|
||||
private String getProducingToken() {
|
||||
return authApi.getMesJwtToken().getCheckedData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getPlanLock(OrderPlanLockReqVO orderPlanLockReqVO) {
|
||||
Long planId = orderPlanLockReqVO.getPlanId();
|
||||
|
||||
+191
@@ -0,0 +1,191 @@
|
||||
package com.cf.imes.module.executor.service.plan.producing;
|
||||
|
||||
import com.cf.imes.framework.common.exception.ServiceException;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
|
||||
import static com.cf.imes.module.executor.enums.ErrorCodeConstants.PRODUCING_PLAN_ORDER_SYNC_FAILED;
|
||||
|
||||
/**
|
||||
* 组件生产系统生产排单客户端。
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class ProducingPlanOrderClient {
|
||||
|
||||
private final ProducingPlanOrderProperties properties;
|
||||
private final ObjectMapper objectMapper;
|
||||
private final HttpClient httpClient;
|
||||
|
||||
public ProducingPlanOrderClient(ProducingPlanOrderProperties properties,
|
||||
ObjectMapper objectMapper) {
|
||||
this.properties = properties;
|
||||
this.objectMapper = objectMapper;
|
||||
this.httpClient = HttpClient.newBuilder()
|
||||
.connectTimeout(properties.getConnectTimeout())
|
||||
.build();
|
||||
}
|
||||
|
||||
public Long create(String token, Long workflowSchemeId, List<String> blockIds) {
|
||||
CreateRequest request = new CreateRequest(blockIds, workflowSchemeId);
|
||||
HttpResponse<String> response = send(
|
||||
properties.getBaseUrl(), token, "POST", serialize(request), "create");
|
||||
try {
|
||||
JsonNode result = objectMapper.readTree(response.body());
|
||||
JsonNode planOrderIdNode = result;
|
||||
if (result != null && result.isObject()) {
|
||||
JsonNode code = result.get("code");
|
||||
if (code == null || !code.isIntegralNumber() || code.intValue() != 0) {
|
||||
String message = result.path("message").asText("business error");
|
||||
log.error("Producing plan order create failed, responseBody={}",
|
||||
responseBodyForLog(response.body()));
|
||||
throw syncError("create: " + message);
|
||||
}
|
||||
planOrderIdNode = result.get("data");
|
||||
}
|
||||
if (planOrderIdNode == null
|
||||
|| (!planOrderIdNode.isIntegralNumber() && !planOrderIdNode.isTextual())) {
|
||||
throw syncError("create: invalid plan order id");
|
||||
}
|
||||
return planOrderIdNode.isIntegralNumber()
|
||||
? planOrderIdNode.longValue() : Long.valueOf(planOrderIdNode.textValue());
|
||||
} catch (JsonProcessingException | NumberFormatException ex) {
|
||||
log.error("Producing plan order create response cannot be parsed, responseBody={}",
|
||||
responseBodyForLog(response.body()), ex);
|
||||
throw syncError("create: invalid plan order id");
|
||||
}
|
||||
}
|
||||
|
||||
public void appendBlocks(String token, Long planOrderId, List<String> blockIds) {
|
||||
HttpResponse<String> response = send(
|
||||
resourceUrl(planOrderId, "append-block-id"), token, "POST",
|
||||
serialize(new AppendRequest(blockIds)), "append-block-id");
|
||||
validateBusinessResponse(response, "append-block-id");
|
||||
}
|
||||
|
||||
public void deleteBlocks(String token, Long planOrderId, List<String> blockIds) {
|
||||
HttpResponse<String> response = send(
|
||||
resourceUrl(planOrderId, "delete-block-id"), token, "DELETE",
|
||||
serialize(new DeleteBlocksRequest(blockIds)), "delete-block-id");
|
||||
validateBusinessResponse(response, "delete-block-id");
|
||||
}
|
||||
|
||||
public void delete(String token, Long planOrderId) {
|
||||
HttpResponse<String> response = send(
|
||||
properties.getBaseUrl() + "/" + planOrderId, token, "DELETE",
|
||||
null, "delete");
|
||||
validateBusinessResponse(response, "delete");
|
||||
}
|
||||
|
||||
private String resourceUrl(Long planOrderId, String action) {
|
||||
return properties.getBaseUrl() + "/" + planOrderId + "/" + action;
|
||||
}
|
||||
|
||||
private String serialize(Object request) {
|
||||
try {
|
||||
return objectMapper.writeValueAsString(request);
|
||||
} catch (JsonProcessingException ex) {
|
||||
throw syncError("cannot serialize request");
|
||||
}
|
||||
}
|
||||
|
||||
private HttpResponse<String> send(String url, String token, String method,
|
||||
String body, String operation) {
|
||||
HttpRequest.BodyPublisher publisher = body == null
|
||||
? HttpRequest.BodyPublishers.noBody()
|
||||
: HttpRequest.BodyPublishers.ofString(body, StandardCharsets.UTF_8);
|
||||
HttpRequest request = HttpRequest.newBuilder(URI.create(url))
|
||||
.timeout(properties.getReadTimeout())
|
||||
.header("Authorization", "Bearer " + token)
|
||||
.header("Accept", "application/json")
|
||||
.header("Content-Type", "application/json; charset=utf-8")
|
||||
.method(method, publisher)
|
||||
.build();
|
||||
try {
|
||||
HttpResponse<String> response = httpClient.send(
|
||||
request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
|
||||
if (response.statusCode() < 200 || response.statusCode() >= 300) {
|
||||
log.error("Producing plan order operation {} failed, url={}, httpStatus={}, "
|
||||
+ "responseBody={}",
|
||||
operation, url, response.statusCode(),
|
||||
responseBodyForLog(response.body()));
|
||||
throw syncError(operation + ": HTTP " + response.statusCode());
|
||||
}
|
||||
return response;
|
||||
} catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw syncError(operation + ": request interrupted");
|
||||
} catch (IOException ex) {
|
||||
log.warn("Producing plan order operation {} failed", operation, ex);
|
||||
throw syncError(operation + ": " + ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 对方写接口可能以 HTTP 200 返回统一业务响应,此处补充业务码校验。
|
||||
* 空响应或未包含 code 的响应仍按接口文档视为成功。
|
||||
*/
|
||||
private void validateBusinessResponse(HttpResponse<String> response, String operation) {
|
||||
if (response.body() == null || response.body().isBlank()) {
|
||||
return;
|
||||
}
|
||||
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) {
|
||||
String message = result.path("message").asText("business error");
|
||||
log.error("Producing plan order operation {} failed, responseBody={}",
|
||||
operation, responseBodyForLog(response.body()));
|
||||
throw syncError(operation + ": " + message);
|
||||
}
|
||||
} catch (JsonProcessingException ex) {
|
||||
log.warn("Producing plan order operation {} returned a non-JSON success response, "
|
||||
+ "responseBody={}",
|
||||
operation, responseBodyForLog(response.body()));
|
||||
}
|
||||
}
|
||||
|
||||
private String responseBodyForLog(String body) {
|
||||
if (body == null) {
|
||||
return "";
|
||||
}
|
||||
int maxLength = 4000;
|
||||
return body.length() <= maxLength ? body : body.substring(0, maxLength) + "...(truncated)";
|
||||
}
|
||||
|
||||
private ServiceException syncError(String message) {
|
||||
return new ServiceException(PRODUCING_PLAN_ORDER_SYNC_FAILED, message);
|
||||
}
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
private static class CreateRequest {
|
||||
private List<String> blockIds;
|
||||
private Long workflowSchemeId;
|
||||
}
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
private static class AppendRequest {
|
||||
private List<String> appendBlockIds;
|
||||
}
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
private static class DeleteBlocksRequest {
|
||||
private List<String> deleteBlockIds;
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package com.cf.imes.module.executor.service.plan.producing;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
/**
|
||||
* 组件生产系统生产排单接口配置。
|
||||
*/
|
||||
@Data
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "chenfeng.executor.producing-plan-order")
|
||||
public class ProducingPlanOrderProperties {
|
||||
|
||||
private String baseUrl =
|
||||
"http://proc.mes.cf/api-branch=opt_split_order/api/v1/producing/plan-order";
|
||||
|
||||
private Duration connectTimeout = Duration.ofSeconds(5);
|
||||
|
||||
private Duration readTimeout = Duration.ofSeconds(10);
|
||||
}
|
||||
+1
@@ -152,3 +152,4 @@ parts.heapsplit.num.not.enough=The number of accessories that can be stacked is
|
||||
parts.heapsplit.item.source.not.exist=Abnormal accessory stacking data, please refresh and stack again
|
||||
|
||||
plan.not.allow.delete=Plan number: {0} has been cut and cannot be deleted
|
||||
producing.plan.order.sync.failed=Failed to synchronize component production plan order: {0}
|
||||
|
||||
+1
@@ -152,3 +152,4 @@ parts.heapsplit.num.not.enough=配件可分堆数量不足,请刷新后再次
|
||||
parts.heapsplit.item.source.not.exist=配件分堆数据异常,请刷新后再次分堆
|
||||
|
||||
plan.not.allow.delete=排单号:{0}已开料无法删除
|
||||
producing.plan.order.sync.failed=同步组件生产排单失败:{0}
|
||||
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
package com.cf.imes.module.executor.service.plan.producing;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import com.sun.net.httpserver.HttpServer;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class ProducingPlanOrderClientTest {
|
||||
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
private final List<RecordedRequest> requests = new ArrayList<>();
|
||||
private HttpServer server;
|
||||
private ProducingPlanOrderClient client;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() throws IOException {
|
||||
server = HttpServer.create(new InetSocketAddress(0), 0);
|
||||
server.createContext("/api/v1/producing/plan-order", this::handle);
|
||||
server.start();
|
||||
|
||||
ProducingPlanOrderProperties properties = new ProducingPlanOrderProperties();
|
||||
properties.setBaseUrl("http://localhost:" + server.getAddress().getPort()
|
||||
+ "/api/v1/producing/plan-order");
|
||||
client = new ProducingPlanOrderClient(properties, objectMapper);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
server.stop(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldUseSourceBlockIdsForAllFourOperations() throws Exception {
|
||||
Long remotePlanId = client.create("mes-token", 36L, List.of("source-1", "source-2"));
|
||||
client.appendBlocks("mes-token", remotePlanId, List.of("source-3"));
|
||||
client.deleteBlocks("mes-token", remotePlanId, List.of("source-2"));
|
||||
client.delete("mes-token", remotePlanId);
|
||||
|
||||
assertThat(remotePlanId).isEqualTo(98765L);
|
||||
assertThat(requests).hasSize(4);
|
||||
assertThat(requests).extracting(RecordedRequest::method)
|
||||
.containsExactly("POST", "POST", "DELETE", "DELETE");
|
||||
assertThat(requests).extracting(RecordedRequest::path)
|
||||
.containsExactly(
|
||||
"/api/v1/producing/plan-order",
|
||||
"/api/v1/producing/plan-order/98765/append-block-id",
|
||||
"/api/v1/producing/plan-order/98765/delete-block-id",
|
||||
"/api/v1/producing/plan-order/98765");
|
||||
assertThat(requests).extracting(RecordedRequest::authorization)
|
||||
.containsOnly("Bearer mes-token");
|
||||
|
||||
JsonNode createBody = objectMapper.readTree(requests.get(0).body());
|
||||
assertThat(createBody.get("workflowSchemeId").longValue()).isEqualTo(36L);
|
||||
assertThat(createBody.get("blockIds").toString())
|
||||
.isEqualTo("[\"source-1\",\"source-2\"]");
|
||||
assertThat(objectMapper.readTree(requests.get(1).body())
|
||||
.get("appendBlockIds").get(0).textValue()).isEqualTo("source-3");
|
||||
assertThat(objectMapper.readTree(requests.get(2).body())
|
||||
.get("deleteBlockIds").get(0).textValue()).isEqualTo("source-2");
|
||||
}
|
||||
|
||||
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.getRequestHeaders().getFirst("Authorization"),
|
||||
body));
|
||||
byte[] response = exchange.getRequestURI().getPath()
|
||||
.equals("/api/v1/producing/plan-order")
|
||||
? """
|
||||
{"data":98765,"code":0,"message":"Request processed successfully."}
|
||||
""".getBytes(StandardCharsets.UTF_8) : 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) {
|
||||
}
|
||||
}
|
||||
+16
-6
@@ -104,6 +104,16 @@ public class ProducingOrderImportFactory {
|
||||
*/
|
||||
private static final int BATCH_THRESHOLD_NUMBER = 999;
|
||||
|
||||
/**
|
||||
* 板件批量写入阈值。板件字段较多,单独设置以便与其他表的批量写入性能进行对比。
|
||||
*/
|
||||
private static final int ORDER_PLATE_BATCH_THRESHOLD_NUMBER = 2000;
|
||||
|
||||
/**
|
||||
* 订单元素及关系批量写入阈值。
|
||||
*/
|
||||
private static final int ORDER_ITEM_BATCH_THRESHOLD_NUMBER = 2000;
|
||||
|
||||
private SnowFlakeGenerator snowFlakeGenerator;
|
||||
|
||||
private OrderMapper orderMapper;
|
||||
@@ -173,8 +183,8 @@ public class ProducingOrderImportFactory {
|
||||
// 对方 partsItems.id:iMES 基础 order_item.id
|
||||
private Map<String, String> partSerialNoMap = new LinkedHashMap<>();
|
||||
|
||||
private List<PlateDO> plateDOS = new ArrayList<>();
|
||||
private List<OrderItemDO> orderItemDOS = new ArrayList<>();
|
||||
private List<PlateDO> plateDOS = ListUtils.newArrayListWithExpectedSize(ORDER_PLATE_BATCH_THRESHOLD_NUMBER);
|
||||
private List<OrderItemDO> orderItemDOS = ListUtils.newArrayListWithExpectedSize(ORDER_ITEM_BATCH_THRESHOLD_NUMBER);
|
||||
private List<OrderPartsDO> orderPartsDOS = new ArrayList<>();
|
||||
private List<GoodsDO> goodsDOS = new ArrayList<>();
|
||||
private List<OrderComponentDO> orderComponentDOS = new ArrayList<>();
|
||||
@@ -2388,7 +2398,7 @@ public class ProducingOrderImportFactory {
|
||||
*/
|
||||
private void orderPlateBatchInsertWithinThreshold(List<PlateDO> list, boolean isLast) {
|
||||
// 达到批量的阈值就做一次插入
|
||||
if (CollUtil.isNotEmpty(list) && (list.size() >= BATCH_THRESHOLD_NUMBER || isLast)) {
|
||||
if (CollUtil.isNotEmpty(list) && (list.size() >= ORDER_PLATE_BATCH_THRESHOLD_NUMBER || isLast)) {
|
||||
int batchRows = list.size();
|
||||
long batchStartedAt = System.nanoTime();
|
||||
long[] compressionNanos = {0L};
|
||||
@@ -2495,7 +2505,7 @@ public class ProducingOrderImportFactory {
|
||||
* 重建板件批量写入缓冲区。
|
||||
*/
|
||||
private void clearPlate() {
|
||||
plateDOS = ListUtils.newArrayListWithExpectedSize(BATCH_THRESHOLD_NUMBER);
|
||||
plateDOS = ListUtils.newArrayListWithExpectedSize(ORDER_PLATE_BATCH_THRESHOLD_NUMBER);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2506,7 +2516,7 @@ public class ProducingOrderImportFactory {
|
||||
*/
|
||||
private void orderItemBatchInsertWithinThreshold(List<OrderItemDO> list, boolean isLast) {
|
||||
// 达到批量的阈值就做一次插入
|
||||
if (CollUtil.isNotEmpty(list) && (list.size() >= BATCH_THRESHOLD_NUMBER || isLast)) {
|
||||
if (CollUtil.isNotEmpty(list) && (list.size() >= ORDER_ITEM_BATCH_THRESHOLD_NUMBER || isLast)) {
|
||||
int batchRows = list.size();
|
||||
long batchStartedAt = System.nanoTime();
|
||||
|
||||
@@ -2573,7 +2583,7 @@ public class ProducingOrderImportFactory {
|
||||
* 重建订单元素批量写入缓冲区。
|
||||
*/
|
||||
private void clearItem() {
|
||||
orderItemDOS = ListUtils.newArrayListWithExpectedSize(BATCH_THRESHOLD_NUMBER);
|
||||
orderItemDOS = ListUtils.newArrayListWithExpectedSize(ORDER_ITEM_BATCH_THRESHOLD_NUMBER);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user