组件业务数据拉取增加成倍数量记录、部分算法优化

This commit is contained in:
gaoqr
2026-07-30 17:59:09 +08:00
parent debac10b90
commit f8db9594ff
5 changed files with 111 additions and 44 deletions
@@ -34,7 +34,7 @@ public class ProducingOrderImportReqVO {
private Long targetOrderId; private Long targetOrderId;
/** /**
* 对方板材 ID 与 iMES 板材库 plate.id 的一对一映射。 * 对方板材 ID 与 iMES 板材库 plate.id 的映射;多个对方板材可复用同一 iMES 板材
*/ */
@Schema(description = "组件系统板材与 iMES 板材映射", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(description = "组件系统板材与 iMES 板材映射", requiredMode = Schema.RequiredMode.REQUIRED)
@Valid @Valid
@@ -938,17 +938,14 @@ public class ProducingOrderImportFactory {
// 获取自定义板编号配置 // 获取自定义板编号配置
plateNoGenerateConfigVO = customPlateNoGenerateService.getPlateNoGenerateConfig(orderId); plateNoGenerateConfigVO = customPlateNoGenerateService.getPlateNoGenerateConfig(orderId);
// 所有房间名 // 一次性查询目标订单的全部已有房间,供板件和配件导入共同复用。
List<String> roomNames = douleRoomTree.stream().map(ProducingImportRoomTree::getName).collect(Collectors.toList()); // 配件可能位于本次板件数据未包含的房间,因此这里不能只按本次报文房间名查询。
// 查询当前订单的所有房间
existRoomIdMap = orderBodyMapper.selectMaps( existRoomIdMap = orderBodyMapper.selectMaps(
new QueryWrapper<OrderBodyDO>() new QueryWrapper<OrderBodyDO>()
.select("room_name,MIN(room_id) as room_id") .select("room_name,MIN(room_id) as room_id")
.eq(ORDER_ID_FIELD, orderId) .eq(ORDER_ID_FIELD, orderId)
.eq(ORGAN_ID_FIELD, organId) .eq(ORGAN_ID_FIELD, organId)
.eq(DELETED_FIELD, false) .eq(DELETED_FIELD, false)
.in(ROOM_NAME_FIELD, roomNames)
.groupBy(ROOM_NAME_FIELD)).stream() .groupBy(ROOM_NAME_FIELD)).stream()
.collect(Collectors.toMap( .collect(Collectors.toMap(
m -> String.valueOf(m.get(ROOM_NAME_FIELD)), m -> String.valueOf(m.get(ROOM_NAME_FIELD)),
@@ -1005,7 +1002,11 @@ public class ProducingOrderImportFactory {
OrderBodyDO orderBodyDO = OrderBodyDO.builder().id(bodyId).orderId(orderId).organId(organId) OrderBodyDO orderBodyDO = OrderBodyDO.builder().id(bodyId).orderId(orderId).organId(organId)
.roomId(roomId).roomName(roomName).name(boxName) .roomId(roomId).roomName(roomName).name(boxName)
.width(getZeroDouble(box.getWidth())).height(getZeroDouble(box.getHeight())) .width(getZeroDouble(box.getWidth())).height(getZeroDouble(box.getHeight()))
.depth(0.0).multiNum(count).multiSort(i + 1).plateNum(0).unregularNum(0).filename("").remark("").deleted(false).build(); .depth(0.0)
.multiNum(box.getMultiNum() == null ? count : box.getMultiNum())
.multiSort(box.getMultiSort() == null ? i + 1 : box.getMultiSort())
.plateNum(0).unregularNum(0).filename("").remark("")
.deleted(false).build();
orderBodyDO.setCreator(operatorName); orderBodyDO.setCreator(operatorName);
orderBodyDO.setDepth(getZeroDouble(box.getDepth())); orderBodyDO.setDepth(getZeroDouble(box.getDepth()));
orderBodyDO.setUpdater(operatorName); orderBodyDO.setUpdater(operatorName);
@@ -1757,29 +1758,9 @@ public class ProducingOrderImportFactory {
if (ObjectUtil.isNotNull(existRoomId)) { if (ObjectUtil.isNotNull(existRoomId)) {
roomId = existRoomId; roomId = existRoomId;
} else { } else {
// 可能有配件房间柜体不在板件的情况,再查一次 // 目标订单的已有房间已在板件解析开始时一次性加载;未命中即为新房间。
// 查询当前订单的所有房间 roomId = (Long) snowFlakeGenerator.nextId(null);
List<String> roomNames = parts.stream().map(ProducingImportRoomTree::getName).collect(Collectors.toList()); existRoomIdMap.put(roomName, roomId);
Map<String, Long> existPartsRoomIdMap = orderBodyMapper.selectMaps(
new QueryWrapper<OrderBodyDO>()
.select("room_name,MIN(room_id) as room_id")
.eq(ORDER_ID_FIELD, orderId)
.eq(ORGAN_ID_FIELD, organId)
.eq(DELETED_FIELD, false)
.in(ROOM_NAME_FIELD, roomNames)
.groupBy(ROOM_NAME_FIELD)).stream()
.collect(Collectors.toMap(
m -> String.valueOf(m.get(ROOM_NAME_FIELD)),
m -> ((Number) m.get("room_id")).longValue()
));
Long existPartRoomId = existPartsRoomIdMap.get(roomName);
if (ObjectUtil.isNotNull(existPartRoomId)) {
roomId = existPartRoomId;
} else {
roomId = (Long) snowFlakeGenerator.nextId(null);
existRoomIdMap.put(roomName, roomId);
}
} }
// 柜体列表 // 柜体列表
@@ -142,6 +142,9 @@ public class ProducingOrderConverter {
context.groupTypes = filterModules(order, 3); context.groupTypes = filterModules(order, 3);
context.groups = filterModules(order, 4); context.groups = filterModules(order, 4);
context.components = filterModules(order, 5); context.components = filterModules(order, 5);
context.bodiesByRoomId = indexModulesByParentId(context.bodies);
context.componentsByParentId = indexModulesByParentId(context.components);
context.bodyMultiplicities = resolveBodyMultiplicities(context);
return context; return context;
} }
@@ -177,21 +180,31 @@ public class ProducingOrderConverter {
} }
/** /**
* 校验板材映射为双方一对一关系,且来源物料必须是 type=1 板材 * 按父节点 ID 建立模块子节点索引,避免按房间或部件组递归处理时重复扫描全部模块
* 子节点列表沿用对方报文顺序,不改变柜体或部件组落库顺序。
*/
private Map<String, List<ProducingOrderResponse.Module>> indexModulesByParentId(
Map<String, ProducingOrderResponse.Module> modules) {
Map<String, List<ProducingOrderResponse.Module>> result = new LinkedHashMap<>();
for (ProducingOrderResponse.Module module : modules.values()) {
result.computeIfAbsent(module.getParentId(), key -> new ArrayList<>())
.add(module);
}
return result;
}
/**
* 校验每个对方板材只提交一次且来源物料必须是 type=1 板材。
* 多个对方板材允许映射到同一个 iMES 板材。
*/ */
private void validateMappings(List<ProducingOrderImportReqVO.MaterialMapping> mappings, private void validateMappings(List<ProducingOrderImportReqVO.MaterialMapping> mappings,
Map<String, ProducingOrderResponse.Material> materials) { Map<String, ProducingOrderResponse.Material> materials) {
Set<String> sourceIds = new HashSet<>(); Set<String> sourceIds = new HashSet<>();
Set<Long> localIds = new HashSet<>();
for (ProducingOrderImportReqVO.MaterialMapping mapping : mappings) { for (ProducingOrderImportReqVO.MaterialMapping mapping : mappings) {
if (!sourceIds.add(mapping.getSourceMaterialId())) { if (!sourceIds.add(mapping.getSourceMaterialId())) {
throw new ServiceException(PRODUCING_ORDER_MATERIAL_MAPPING_INVALID, throw new ServiceException(PRODUCING_ORDER_MATERIAL_MAPPING_INVALID,
"duplicate source material " + mapping.getSourceMaterialId()); "duplicate source material " + mapping.getSourceMaterialId());
} }
if (!localIds.add(mapping.getPlateId())) {
throw new ServiceException(PRODUCING_ORDER_MATERIAL_MAPPING_INVALID,
"duplicate iMES plate " + mapping.getPlateId());
}
ProducingOrderResponse.Material material = materials.get(mapping.getSourceMaterialId()); ProducingOrderResponse.Material material = materials.get(mapping.getSourceMaterialId());
if (material == null || !Objects.equals(material.getType(), 1)) { if (material == null || !Objects.equals(material.getType(), 1)) {
throw new ServiceException(PRODUCING_ORDER_MATERIAL_MAPPING_INVALID, throw new ServiceException(PRODUCING_ORDER_MATERIAL_MAPPING_INVALID,
@@ -500,10 +513,9 @@ public class ProducingOrderConverter {
size.setDepth(zero(sourceSize == null ? null : sourceSize.getDepth())); size.setDepth(zero(sourceSize == null ? null : sourceSize.getDepth()));
target.setBoxSize(size); target.setBoxSize(size);
List<ProducingImportComponentGroup> children = new ArrayList<>(); List<ProducingImportComponentGroup> children = new ArrayList<>();
for (ProducingOrderResponse.Module candidate : context.components.values()) { for (ProducingOrderResponse.Module child : context.componentsByParentId
if (Objects.equals(candidate.getParentId(), source.getId())) { .getOrDefault(source.getId(), Collections.emptyList())) {
children.add(toComponent(candidate, context)); children.add(toComponent(child, context));
}
} }
target.setChildren(children); target.setChildren(children);
return target; return target;
@@ -1012,14 +1024,17 @@ public class ProducingOrderConverter {
targetRoom.setName(room.getName()); targetRoom.setName(room.getName());
targetRoom.setCount(1); targetRoom.setCount(1);
List<ProducingImportRoomTree.ChildrenDTO> children = new ArrayList<>(); List<ProducingImportRoomTree.ChildrenDTO> children = new ArrayList<>();
for (ProducingOrderResponse.Module body : context.bodies.values()) { for (ProducingOrderResponse.Module body : context.bodiesByRoomId
if (!Objects.equals(body.getParentId(), room.getId())) { .getOrDefault(room.getId(), Collections.emptyList())) {
continue;
}
ProducingImportRoomTree.ChildrenDTO child = ProducingImportRoomTree.ChildrenDTO child =
new ProducingImportRoomTree.ChildrenDTO(); new ProducingImportRoomTree.ChildrenDTO();
child.setName(body.getName()); child.setName(body.getName());
child.setIdentityKey("producing:" + body.getId()); child.setIdentityKey("producing:" + body.getId());
BodyMultiplicity multiplicity =
context.bodyMultiplicities.get(body.getId());
child.setMultiNum(multiplicity.multiNum());
child.setMultiSort(multiplicity.multiSort());
// 对方已按成倍数量展开柜体及生产项,工厂不得再次复制。
child.setCount(1); child.setCount(1);
ProducingOrderResponse.Size3D bodySize = ProducingOrderResponse.Size3D bodySize =
body.getExtra() == null ? null : body.getExtra().getSize(); body.getExtra() == null ? null : body.getExtra().getSize();
@@ -1038,6 +1053,48 @@ public class ProducingOrderConverter {
return result; return result;
} }
/**
* 解析对方已展开的成倍柜体,并为每个柜体计算成倍总数及组内顺序。
*
* <p>{@code multipleNum} 只出现在成倍组的首个 type=2 柜体上;随后
* {@code multipleNum - 1} 个同房间、同名柜体是其余实体。各柜体下的
* {@code blockItems}/{@code partsItems} 已具有独立 ID,因此这里只生成
* 展示和统计元数据,不再放大生产项数量。</p>
*/
private Map<String, BodyMultiplicity> resolveBodyMultiplicities(Context context) {
Map<String, BodyMultiplicity> result = new HashMap<>();
for (ProducingOrderResponse.Module room : context.rooms.values()) {
List<ProducingOrderResponse.Module> roomBodies = context.bodiesByRoomId
.getOrDefault(room.getId(), Collections.emptyList());
for (int index = 0; index < roomBodies.size();) {
ProducingOrderResponse.Module first = roomBodies.get(index);
Integer declaredMultiple = first.getExtra() == null
? null : first.getExtra().getMultipleNum();
int multiNum = declaredMultiple == null ? 1 : declaredMultiple;
if (multiNum < 1 || index + multiNum > roomBodies.size()) {
throw new ServiceException(PRODUCING_ORDER_RESPONSE_INVALID,
"module " + first.getId()
+ " has invalid multipleNum=" + declaredMultiple);
}
for (int offset = 0; offset < multiNum; offset++) {
ProducingOrderResponse.Module member = roomBodies.get(index + offset);
Integer memberMultiple = member.getExtra() == null
? null : member.getExtra().getMultipleNum();
if (!Objects.equals(first.getName(), member.getName())
|| (offset > 0 && memberMultiple != null)) {
throw new ServiceException(PRODUCING_ORDER_RESPONSE_INVALID,
"module " + first.getId()
+ " multiple group is not contiguous");
}
result.put(member.getId(),
new BodyMultiplicity(multiNum, offset + 1));
}
index += multiNum;
}
}
return result;
}
/** /**
* 将实例级键值备注转换为板件备注二维列表。 * 将实例级键值备注转换为板件备注二维列表。
*/ */
@@ -1411,5 +1468,17 @@ public class ProducingOrderConverter {
private Map<String, ProducingOrderResponse.Module> groupTypes; private Map<String, ProducingOrderResponse.Module> groupTypes;
private Map<String, ProducingOrderResponse.Module> groups; private Map<String, ProducingOrderResponse.Module> groups;
private Map<String, ProducingOrderResponse.Module> components; private Map<String, ProducingOrderResponse.Module> components;
private Map<String, List<ProducingOrderResponse.Module>> bodiesByRoomId;
private Map<String, List<ProducingOrderResponse.Module>> componentsByParentId;
private Map<String, BodyMultiplicity> bodyMultiplicities;
}
/**
* 已展开柜体的成倍元数据。
*
* @param multiNum 成倍组总数量
* @param multiSort 当前柜体在组内的顺序
*/
private record BodyMultiplicity(int multiNum, int multiSort) {
} }
} }
@@ -42,6 +42,18 @@ public class ProducingImportRoomTree {
private Double depth; private Double depth;
/**
* 已展开柜体所属成倍组的总数量。
*
* <p>该字段只控制 {@code order_body.multi_num},不会再次复制物理生产项。</p>
*/
private Integer multiNum;
/**
* 当前已展开柜体在成倍组内的顺序,从 1 开始。
*/
private Integer multiSort;
@NotNull(message = "拆单倍数不能为空") @NotNull(message = "拆单倍数不能为空")
@Max(value = 5000, message = "拆单倍数不能超过5000") @Max(value = 5000, message = "拆单倍数不能超过5000")
@Min(value = 1, message = "拆单倍数最小值为1") @Min(value = 1, message = "拆单倍数最小值为1")
@@ -86,6 +86,11 @@ public class ProducingOrderResponse {
public static class ModuleExtra { public static class ModuleExtra {
private Size3D size; private Size3D size;
private Boolean isComposite; private Boolean isComposite;
/**
* 柜体成倍数量。对方已经按倍数展开柜体模块及物理生产项,
* 该字段仅用于还原同组成倍柜体的总数和顺序。
*/
private Integer multipleNum;
} }
@Data @Data