From 3066a8f5c1f475997bfe23857ba4bae9395900c4 Mon Sep 17 00:00:00 2001
From: yuan <123@>
Date: 星期四, 13 八月 2026 10:19:24 +0800
Subject: [PATCH] fix: 领料功能修改,移除订单领料,添加工序领料功能,支持按工序查询物料与已领料记录
---
src/main/java/com/ruoyi/production/service/impl/ProductionBomStructureServiceImpl.java | 684 ++++++++++++++++++++++++++++++++++++++------------------
1 files changed, 467 insertions(+), 217 deletions(-)
diff --git a/src/main/java/com/ruoyi/production/service/impl/ProductionBomStructureServiceImpl.java b/src/main/java/com/ruoyi/production/service/impl/ProductionBomStructureServiceImpl.java
index 2bded77..304d0e8 100644
--- a/src/main/java/com/ruoyi/production/service/impl/ProductionBomStructureServiceImpl.java
+++ b/src/main/java/com/ruoyi/production/service/impl/ProductionBomStructureServiceImpl.java
@@ -3,25 +3,41 @@
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.basic.mapper.ProductModelMapper;
+import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.production.bean.dto.ProductionBomStructureDto;
import com.ruoyi.production.bean.vo.ProductionBomStructureVo;
import com.ruoyi.production.mapper.ProductionBomStructureMapper;
import com.ruoyi.production.mapper.ProductionOperationTaskMapper;
import com.ruoyi.production.mapper.ProductionOrderBomMapper;
import com.ruoyi.production.mapper.ProductionOrderMapper;
+import com.ruoyi.production.mapper.ProductionOrderRoutingMapper;
import com.ruoyi.production.mapper.ProductionOrderRoutingOperationMapper;
+import com.ruoyi.production.mapper.ProductionOrderRoutingOperationParamMapper;
+import com.ruoyi.production.mapper.ProductionProductMainMapper;
import com.ruoyi.production.pojo.ProductionBomStructure;
import com.ruoyi.production.pojo.ProductionOperationTask;
import com.ruoyi.production.pojo.ProductionOrder;
import com.ruoyi.production.pojo.ProductionOrderBom;
+import com.ruoyi.production.pojo.ProductionOrderRouting;
import com.ruoyi.production.pojo.ProductionOrderRoutingOperation;
+import com.ruoyi.production.pojo.ProductionOrderRoutingOperationParam;
+import com.ruoyi.production.pojo.ProductionProductMain;
import com.ruoyi.production.service.ProductionBomStructureService;
+import com.ruoyi.production.util.TaskPlanQuantityUtil;
+import com.ruoyi.technology.mapper.TechnologyOperationMapper;
+import com.ruoyi.technology.mapper.TechnologyOperationParamMapper;
+import com.ruoyi.technology.mapper.TechnologyParamMapper;
+import com.ruoyi.technology.pojo.TechnologyOperation;
+import com.ruoyi.technology.pojo.TechnologyOperationParam;
+import com.ruoyi.technology.pojo.TechnologyParam;
import lombok.RequiredArgsConstructor;
-import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
@@ -40,122 +56,142 @@
private final ProductionBomStructureMapper productionBomStructureMapper;
private final ProductionOrderBomMapper productionOrderBomMapper;
private final ProductionOrderMapper productionOrderMapper;
+ private final ProductionOrderRoutingMapper productionOrderRoutingMapper;
private final ProductionOrderRoutingOperationMapper productionOrderRoutingOperationMapper;
+ private final ProductionOrderRoutingOperationParamMapper productionOrderRoutingOperationParamMapper;
private final ProductionOperationTaskMapper productionOperationTaskMapper;
+ private final ProductionProductMainMapper productionProductMainMapper;
+ private final ProductModelMapper productModelMapper;
+ private final TechnologyOperationMapper technologyOperationMapper;
+ private final TechnologyOperationParamMapper technologyOperationParamMapper;
+ private final TechnologyParamMapper technologyParamMapper;
/**
- * 鏍规嵁BOM鏌ヨ骞剁粍瑁呯粨鏋勬爲銆�
+ * 鎸夎鍗旴OM鏌ヨ鎵佸钩鐗╂枡鏄庣粏銆�
*/
@Override
public List<ProductionBomStructureVo> listByBomId(Long bomId) {
- // 鎸塀OMID鏌ヨ鐢熶骇缁撴瀯鏁版嵁
- List<ProductionBomStructureVo> list = productionBomStructureMapper.listByBomId(bomId);
- Map<Long, ProductionBomStructureVo> map = new HashMap<>();
- for (ProductionBomStructureVo node : list) {
- node.setChildren(new ArrayList<>());
- map.put(node.getId(), node);
+ if (bomId == null) {
+ return new ArrayList<>();
}
-
- List<ProductionBomStructureVo> tree = new ArrayList<>();
- for (ProductionBomStructureVo node : list) {
- Long parentId = node.getParentId();
- if (parentId == null || parentId == 0L) {
- tree.add(node);
- continue;
- }
- ProductionBomStructureVo parent = map.get(parentId);
- if (parent != null) {
- parent.getChildren().add(node);
- }
- }
- return tree;
+ return productionBomStructureMapper.listByBomId(bomId);
}
+ /**
+ * 鎸夌敓浜ц鍗曟煡璇㈣鍗旴OM蹇収鐨勬墎骞崇墿鏂欐槑缁嗐��
+ */
+ @Override
+ public List<ProductionBomStructureVo> listByOrderId(Long productionOrderId) {
+ if (productionOrderId == null) {
+ return new ArrayList<>();
+ }
+ ProductionOrderBom orderBom = productionOrderBomMapper.selectOne(
+ Wrappers.<ProductionOrderBom>lambdaQuery()
+ .eq(ProductionOrderBom::getProductionOrderId, productionOrderId)
+ .orderByDesc(ProductionOrderBom::getId)
+ .last("limit 1"));
+ if (orderBom == null || orderBom.getId() == null) {
+ return new ArrayList<>();
+ }
+ return listByBomId(orderBom.getId());
+ }
+
+ /**
+ * 鍏ㄩ噺鍚屾璁㈠崟BOM蹇収鐨勬墎骞崇墿鏂欐槑缁嗭紝骞舵寜鍏朵腑鐨勫伐搴忛噸寤鸿鍗曞伐鑹鸿矾绾垮揩鐓т笌宸ュ崟銆�
+ */
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean addProductionBomStructure(ProductionBomStructureDto dto) {
- // 鏂板鐢熶骇BOM缁撴瀯
- // 璇诲彇褰撳墠璁㈠崟BOM涓婚敭锛屽苟鎶婂墠绔爲缁撴瀯鎷嶅钩鎴愬垪琛�
Long orderBomId = dto.getProductionOrderBomId();
- List<ProductionBomStructureDto> flatDtoList = new ArrayList<>();
- flattenTree(dto.getChildren(), flatDtoList);
+ if (orderBomId == null) {
+ throw new ServiceException("璁㈠崟BOM ID涓嶈兘涓虹┖");
+ }
+ ProductionOrderBom orderBom = productionOrderBomMapper.selectById(orderBomId);
+ if (orderBom == null) {
+ throw new ServiceException("璁㈠崟BOM涓嶅瓨鍦�");
+ }
+ Long productionOrderId = dto.getProductionOrderId() != null
+ ? dto.getProductionOrderId()
+ : orderBom.getProductionOrderId();
- // 鏌ヨ鏁版嵁搴撳凡鏈夌粨鏋勶紝鐢ㄤ簬鍚庣画鍋氬鍒犳敼瀵规瘮
+ List<ProductionBomStructureDto> detailList = dto.getDetailList() == null
+ ? Collections.emptyList()
+ : dto.getDetailList();
+
List<ProductionBomStructure> dbList = this.list(new LambdaQueryWrapper<ProductionBomStructure>()
.eq(ProductionBomStructure::getProductionOrderBomId, orderBomId));
+ Set<Long> dbIds = dbList.stream().map(ProductionBomStructure::getId).collect(Collectors.toSet());
- // 鏀堕泦鍓嶇浠嶇劧瀛樺湪鐨勮妭鐐笽D
- Set<Long> frontendIds = new HashSet<>();
- for (ProductionBomStructureDto item : flatDtoList) {
- if (item.getId() != null) {
- frontendIds.add(item.getId());
- }
- }
-
- // 璁$畻闇�瑕佸垹闄ょ殑鑺傜偣锛堟暟鎹簱鏈夈�佸墠绔凡鍒犻櫎锛�
- Set<Long> deleteIds = new HashSet<>();
- for (ProductionBomStructure dbItem : dbList) {
- if (!frontendIds.contains(dbItem.getId())) {
- deleteIds.add(dbItem.getId());
- }
- }
- // 鍏堝垹鎺夊墠绔凡缁忕Щ闄ょ殑鑺傜偣
- if (!deleteIds.isEmpty()) {
- this.removeByIds(deleteIds);
- }
-
- // 鎸夋槸鍚︽湁ID鎷嗗垎涓烘柊澧炲拰鏇存柊锛屽悓鏃剁紦瀛樻柊澧炶妭鐐圭殑涓存椂ID鏄犲皠
+ Set<Long> submittedIds = new HashSet<>();
+ Set<String> relationKeys = new HashSet<>();
List<ProductionBomStructure> insertList = new ArrayList<>();
List<ProductionBomStructure> updateList = new ArrayList<>();
- Map<String, ProductionBomStructure> tempEntityMap = new HashMap<>();
- for (ProductionBomStructureDto item : flatDtoList) {
+ for (ProductionBomStructureDto item : detailList) {
+ Long productModelId = item.getProductModelId();
+ Long technologyOperationId = item.getTechnologyOperationId();
+ if (productModelId == null) {
+ throw new ServiceException("璇烽�夋嫨鐗╂枡瑙勬牸");
+ }
+ if (productModelMapper.selectById(productModelId) == null) {
+ throw new ServiceException("鐗╂枡瑙勬牸涓嶅瓨鍦�");
+ }
+ if (technologyOperationId == null) {
+ throw new ServiceException("璇烽�夋嫨娑堣�楀伐搴�");
+ }
+ if (technologyOperationMapper.selectById(technologyOperationId) == null) {
+ throw new ServiceException("娑堣�楀伐搴忎笉瀛樺湪");
+ }
+ if (productModelId.equals(orderBom.getProductModelId())) {
+ throw new ServiceException("鎴愬搧瑙勬牸涓嶈兘浣滀负鑷韩鐨勭墿鏂�");
+ }
+ if (!relationKeys.add(productModelId + "#" + technologyOperationId)) {
+ throw new ServiceException("鍚屼竴鐗╂枡瑙勬牸鍦ㄥ悓涓�宸ュ簭涓嬩笉鑳介噸澶嶉厤缃�");
+ }
+
ProductionBomStructure entity = new ProductionBomStructure();
- BeanUtils.copyProperties(item, entity);
entity.setProductionOrderBomId(orderBomId);
+ entity.setProductionOrderId(productionOrderId);
+ entity.setProductModelId(productModelId);
+ entity.setTechnologyOperationId(technologyOperationId);
+ // 鎵佸钩蹇収涓嶅啀浣跨敤灞傜骇涓庢暟閲忚涔�
+ entity.setParentId(null);
+ entity.setUnitQuantity(null);
+ entity.setDemandedQuantity(null);
+ entity.setUnit(null);
+
if (item.getId() == null) {
- entity.setParentId(null);
insertList.add(entity);
- tempEntityMap.put(item.getTempId(), entity);
} else {
+ if (!dbIds.contains(item.getId())) {
+ throw new ServiceException("璁㈠崟BOM鐗╂枡鏄庣粏涓嶅瓨鍦ㄦ垨涓嶅睘浜庡綋鍓嶈鍗旴OM");
+ }
+ entity.setId(item.getId());
+ submittedIds.add(item.getId());
updateList.add(entity);
}
}
- // 鎵归噺鏂板锛屾嬁鍒版暟鎹簱鐢熸垚鐨勭湡瀹濱D
+ Set<Long> deleteIds = new HashSet<>(dbIds);
+ deleteIds.removeAll(submittedIds);
+ if (!deleteIds.isEmpty()) {
+ this.removeByIds(deleteIds);
+ }
+ if (!updateList.isEmpty()) {
+ this.updateBatchById(updateList);
+ }
if (!insertList.isEmpty()) {
this.saveBatch(insertList);
}
- // 鏂板鑺傜偣浜屾鍥炲啓鐖禝D锛堝墠绔紶鐨勬槸涓存椂鐖禝D锛�
- List<ProductionBomStructure> parentFixList = new ArrayList<>();
- for (ProductionBomStructureDto item : flatDtoList) {
- if (item.getId() == null && item.getParentTempId() != null) {
- ProductionBomStructure child = tempEntityMap.get(item.getTempId());
- if (child == null) {
- continue;
- }
- ProductionBomStructure parent = tempEntityMap.get(item.getParentTempId());
- // 鐖惰妭鐐规槸鏈鏂板鏃讹紝鐩存帴鐢ㄦ柊澧炲悗鐨勭湡瀹濱D锛涘惁鍒欏洖閫�涓哄墠绔紶鍏ョ埗ID
- Long realParentId = parent != null ? parent.getId() : Long.valueOf(item.getParentTempId());
- child.setParentId(realParentId);
- parentFixList.add(child);
- }
- }
-
- // 鍥炲啓鏂板鑺傜偣鐨勭埗瀛愬叧绯�
- if (!parentFixList.isEmpty()) {
- this.updateBatchById(parentFixList);
- }
- // 鎵归噺鏇存柊宸叉湁鑺傜偣
- if (!updateList.isEmpty()) {
- this.updateBatchById(updateList);
- }
- syncDemandedQuantityAndTaskPlanQuantity(orderBomId, dto.getProductionOrderId());
+ syncRoutingAndTaskByStructure(orderBomId, productionOrderId);
return true;
}
- private void syncDemandedQuantityAndTaskPlanQuantity(Long orderBomId, Long productionOrderId) {
+ /**
+ * 璁㈠崟BOM鐗╂枡鍙樺寲鍚庯紝鎸夊叾宸ュ簭闆嗗悎閲嶅缓璁㈠崟宸ヨ壓璺嚎宸ュ簭蹇収涓庡伐鍗曡鍒掓暟閲忋��
+ */
+ private void syncRoutingAndTaskByStructure(Long orderBomId, Long productionOrderId) {
if (orderBomId == null) {
return;
}
@@ -172,47 +208,20 @@
return;
}
- BigDecimal orderQuantity = defaultDecimal(productionOrder.getQuantity());
List<ProductionBomStructure> structureList = this.list(
Wrappers.<ProductionBomStructure>lambdaQuery()
.eq(ProductionBomStructure::getProductionOrderBomId, orderBomId)
.orderByAsc(ProductionBomStructure::getId));
- syncStructureDemandedQuantity(structureList, orderQuantity);
- syncTaskPlanQuantity(
- currentProductionOrderId,
- structureList,
- orderQuantity,
- orderBom.getProductModelId() != null ? orderBom.getProductModelId() : productionOrder.getProductModelId());
+ Long rootProductModelId = orderBom.getProductModelId() != null
+ ? orderBom.getProductModelId()
+ : productionOrder.getProductModelId();
+ // 鍚屾鐢熶骇宸ヨ壓璺嚎蹇収
+ syncRoutingOperationsByBom(currentProductionOrderId, productionOrder, orderBom, structureList, rootProductModelId);
+ // 鍚屾宸ュ崟璁″垝鏁伴噺锛氭墎骞矪OM鏃犳暟閲忛厤缃紝璁″垝鏁伴噺缁熶竴鍙栬鍗曟暟閲�
+ syncTaskPlanQuantity(currentProductionOrderId, TaskPlanQuantityUtil.resolveTaskPlanQuantity(productionOrder));
}
- private void syncStructureDemandedQuantity(List<ProductionBomStructure> structureList, BigDecimal orderQuantity) {
- if (structureList == null || structureList.isEmpty()) {
- return;
- }
- List<ProductionBomStructure> updateList = new ArrayList<>();
- for (ProductionBomStructure structure : structureList) {
- if (structure == null || structure.getId() == null) {
- continue;
- }
- BigDecimal demandedQuantity = defaultDecimal(structure.getUnitQuantity()).multiply(orderQuantity);
- if (compareDecimal(structure.getDemandedQuantity(), demandedQuantity) == 0) {
- continue;
- }
- ProductionBomStructure update = new ProductionBomStructure();
- update.setId(structure.getId());
- update.setDemandedQuantity(demandedQuantity);
- updateList.add(update);
- structure.setDemandedQuantity(demandedQuantity);
- }
- if (!updateList.isEmpty()) {
- this.updateBatchById(updateList);
- }
- }
-
- private void syncTaskPlanQuantity(Long productionOrderId,
- List<ProductionBomStructure> structureList,
- BigDecimal orderQuantity,
- Long rootProductModelId) {
+ private void syncTaskPlanQuantity(Long productionOrderId, BigDecimal planQuantity) {
List<ProductionOperationTask> taskList = productionOperationTaskMapper.selectList(
Wrappers.<ProductionOperationTask>lambdaQuery()
.eq(ProductionOperationTask::getProductionOrderId, productionOrderId)
@@ -220,33 +229,10 @@
if (taskList == null || taskList.isEmpty()) {
return;
}
- Set<Long> routingOperationIds = taskList.stream()
- .map(ProductionOperationTask::getProductionOrderRoutingOperationId)
- .filter(Objects::nonNull)
- .collect(Collectors.toSet());
- if (routingOperationIds.isEmpty()) {
- return;
- }
- Map<Long, ProductionOrderRoutingOperation> routingOperationMap = productionOrderRoutingOperationMapper
- .selectBatchIds(routingOperationIds)
- .stream()
- .filter(item -> item != null && item.getId() != null)
- .collect(Collectors.toMap(ProductionOrderRoutingOperation::getId, item -> item, (left, right) -> left));
- // Keep task plan quantities aligned with the same order BOM snapshot demand used during snapshot creation.
- Map<String, BigDecimal> demandedQuantityMap = buildOperationDemandedQuantityMap(structureList, rootProductModelId);
for (ProductionOperationTask task : taskList) {
- if (task == null || task.getId() == null || task.getProductionOrderRoutingOperationId() == null) {
+ if (task == null || task.getId() == null) {
continue;
}
- ProductionOrderRoutingOperation routingOperation = routingOperationMap.get(task.getProductionOrderRoutingOperationId());
- if (routingOperation == null || routingOperation.getTechnologyRoutingOperationId() == null) {
- continue;
- }
- BigDecimal planQuantity = resolveTaskPlanQuantity(
- routingOperation,
- demandedQuantityMap,
- orderQuantity,
- rootProductModelId);
if (compareDecimal(task.getPlanQuantity(), planQuantity) == 0) {
continue;
}
@@ -256,82 +242,360 @@
productionOperationTaskMapper.updateById(update);
}
}
- private Map<String, BigDecimal> buildOperationDemandedQuantityMap(List<ProductionBomStructure> structureList,
- Long rootProductModelId) {
- if (structureList == null || structureList.isEmpty()) {
- return Collections.emptyMap();
- }
- Map<Long, ProductionBomStructure> structureById = structureList.stream()
- .filter(item -> item != null && item.getId() != null)
- .collect(Collectors.toMap(ProductionBomStructure::getId, item -> item, (left, right) -> left));
- Map<String, BigDecimal> demandedQuantityMap = new HashMap<>();
- Set<String> mergedOutputNodeKeySet = new HashSet<>();
- for (ProductionBomStructure bomStructure : structureList) {
- if (bomStructure == null || bomStructure.getTechnologyOperationId() == null) {
- continue;
+
+ private void syncRoutingOperationsByBom(Long productionOrderId,
+ ProductionOrder productionOrder,
+ ProductionOrderBom orderBom,
+ List<ProductionBomStructure> structureList,
+ Long rootProductModelId) {
+ ProductionOrderRouting orderRouting = getOrCreateOrderRoutingSnapshot(productionOrderId, productionOrder, orderBom, rootProductModelId);
+ List<ProductionOrderRoutingOperation> desiredOperationList = buildDesiredRoutingOperationList(structureList, rootProductModelId);
+ List<ProductionOrderRoutingOperation> existingOperationList = productionOrderRoutingOperationMapper.selectList(
+ Wrappers.<ProductionOrderRoutingOperation>lambdaQuery()
+ .eq(ProductionOrderRoutingOperation::getOrderRoutingId, orderRouting.getId())
+ .eq(ProductionOrderRoutingOperation::getProductionOrderId, productionOrderId)
+ .orderByAsc(ProductionOrderRoutingOperation::getDragSort)
+ .orderByAsc(ProductionOrderRoutingOperation::getId));
+ Map<String, Deque<ProductionOrderRoutingOperation>> existingBucketMap = buildExistingRoutingOperationBucketMap(existingOperationList);
+ List<ProductionOrderRoutingOperation> finalOperationList = new ArrayList<>();
+ for (ProductionOrderRoutingOperation desiredOperation : desiredOperationList) {
+ String bucketKey = buildRoutingOperationBucketKey(
+ desiredOperation.getTechnologyOperationId(),
+ desiredOperation.getProductModelId());
+ Deque<ProductionOrderRoutingOperation> matchedQueue = existingBucketMap.get(bucketKey);
+ ProductionOrderRoutingOperation matchedOperation = matchedQueue == null ? null : matchedQueue.pollFirst();
+ if (matchedOperation == null) {
+ matchedOperation = insertRoutingOperationSnapshot(orderRouting.getId(), productionOrderId, desiredOperation);
+ } else {
+ updateRoutingOperationSnapshotIfNecessary(matchedOperation, orderRouting.getId(), productionOrderId, desiredOperation);
}
- // Resolve the output node first, then read the output node demand for the task plan quantity.
- ProductionBomStructure outputNode = resolveOperationOutputNode(bomStructure, structureById);
- Long outputProductModelId = resolveOutputProductModelId(outputNode, rootProductModelId);
- if (outputProductModelId == null) {
- continue;
- }
- String mergedOutputNodeKey = buildOperationOutputNodeKey(
- bomStructure.getTechnologyOperationId(),
- outputNode == null ? null : outputNode.getId(),
- outputProductModelId);
- if (!mergedOutputNodeKeySet.add(mergedOutputNodeKey)) {
- continue;
- }
- // Multiple input rows can point to the same output node, so only count that output demand once.
- String key = buildOperationDemandedQuantityKey(bomStructure.getTechnologyOperationId(), outputProductModelId);
- demandedQuantityMap.merge(key, defaultDecimal(outputNode == null ? null : outputNode.getDemandedQuantity()), BigDecimal::add);
+ finalOperationList.add(matchedOperation);
}
- return demandedQuantityMap;
- }
- private BigDecimal resolveTaskPlanQuantity(ProductionOrderRoutingOperation routingOperation,
- Map<String, BigDecimal> demandedQuantityMap,
- BigDecimal orderQuantity,
- Long rootProductModelId) {
- if (routingOperation == null || demandedQuantityMap == null || demandedQuantityMap.isEmpty()) {
- return orderQuantity;
+ for (Deque<ProductionOrderRoutingOperation> queue : existingBucketMap.values()) {
+ while (queue != null && !queue.isEmpty()) {
+ removeRoutingOperationSnapshot(queue.pollFirst());
+ }
}
- Long outputProductModelId = routingOperation.getProductModelId() != null
- ? routingOperation.getProductModelId()
- : rootProductModelId;
- String key = buildOperationDemandedQuantityKey(
- routingOperation.getTechnologyOperationId(),
- outputProductModelId);
- BigDecimal planQuantity = demandedQuantityMap.get(key);
- return planQuantity != null ? planQuantity : orderQuantity;
+ syncRoutingOperationTasks(productionOrderId, finalOperationList);
}
- private String buildOperationDemandedQuantityKey(Long operationId, Long outputProductModelId) {
+ private ProductionOrderRouting getOrCreateOrderRoutingSnapshot(Long productionOrderId,
+ ProductionOrder productionOrder,
+ ProductionOrderBom orderBom,
+ Long rootProductModelId) {
+ ProductionOrderRouting orderRouting = productionOrderRoutingMapper.selectOne(
+ Wrappers.<ProductionOrderRouting>lambdaQuery()
+ .eq(ProductionOrderRouting::getProductionOrderId, productionOrderId)
+ .orderByDesc(ProductionOrderRouting::getId)
+ .last("limit 1"));
+ if (orderRouting == null) {
+ orderRouting = new ProductionOrderRouting();
+ orderRouting.setProductionOrderId(productionOrderId);
+ orderRouting.setProductModelId(rootProductModelId);
+ orderRouting.setTechnologyRoutingId(productionOrder == null ? null : productionOrder.getTechnologyRoutingId());
+ orderRouting.setBomId(orderBom == null ? null : orderBom.getBomId());
+ orderRouting.setOrderBomId(orderBom == null ? null : orderBom.getId());
+ productionOrderRoutingMapper.insert(orderRouting);
+ return orderRouting;
+ }
+ ProductionOrderRouting update = new ProductionOrderRouting();
+ update.setId(orderRouting.getId());
+ boolean changed = false;
+ if (!Objects.equals(orderRouting.getProductModelId(), rootProductModelId)) {
+ update.setProductModelId(rootProductModelId);
+ orderRouting.setProductModelId(rootProductModelId);
+ changed = true;
+ }
+ Long technologyRoutingId = productionOrder == null ? null : productionOrder.getTechnologyRoutingId();
+ if (!Objects.equals(orderRouting.getTechnologyRoutingId(), technologyRoutingId)) {
+ update.setTechnologyRoutingId(technologyRoutingId);
+ orderRouting.setTechnologyRoutingId(technologyRoutingId);
+ changed = true;
+ }
+ Long bomId = orderBom == null ? null : orderBom.getBomId();
+ if (!Objects.equals(orderRouting.getBomId(), bomId)) {
+ update.setBomId(bomId);
+ orderRouting.setBomId(bomId);
+ changed = true;
+ }
+ Long orderBomId = orderBom == null ? null : orderBom.getId();
+ if (!Objects.equals(orderRouting.getOrderBomId(), orderBomId)) {
+ update.setOrderBomId(orderBomId);
+ orderRouting.setOrderBomId(orderBomId);
+ changed = true;
+ }
+ if (changed) {
+ productionOrderRoutingMapper.updateById(update);
+ }
+ return orderRouting;
+ }
+
+ /**
+ * 鎵佸钩BOM鎸夊伐搴忓幓閲嶇敓鎴愭湡鏈涚殑璁㈠崟宸ュ簭鍒楄〃锛�
+ * 椤哄簭鍙栫墿鏂欐槑缁嗕腑宸ュ簭棣栨鍑虹幇鐨勯『搴忥紝浜у嚭瑙勬牸鍥哄畾涓鸿鍗曟垚鍝佽鏍笺��
+ */
+ private List<ProductionOrderRoutingOperation> buildDesiredRoutingOperationList(List<ProductionBomStructure> structureList,
+ Long rootProductModelId) {
+ if (structureList == null || structureList.isEmpty()) {
+ return Collections.emptyList();
+ }
+ Set<Long> uniqueOperationIdList = new LinkedHashSet<>();
+ for (ProductionBomStructure structure : structureList) {
+ if (structure == null || structure.getTechnologyOperationId() == null) {
+ continue;
+ }
+ uniqueOperationIdList.add(structure.getTechnologyOperationId());
+ }
+
+ List<ProductionOrderRoutingOperation> desiredOperationList = new ArrayList<>(uniqueOperationIdList.size());
+ int dragSort = 1;
+ for (Long operationId : uniqueOperationIdList) {
+ TechnologyOperation technologyOperation = getTechnologyOperation(operationId);
+ ProductionOrderRoutingOperation routingOperation = new ProductionOrderRoutingOperation();
+ routingOperation.setProductModelId(rootProductModelId);
+ routingOperation.setTechnologyOperationId(operationId);
+ routingOperation.setOperationName(technologyOperation == null ? null : technologyOperation.getName());
+ routingOperation.setIsQuality(technologyOperation == null ? null : technologyOperation.getIsQuality());
+ routingOperation.setIsProduction(technologyOperation == null ? null : technologyOperation.getIsProduction());
+ routingOperation.setType(technologyOperation == null ? null : technologyOperation.getType());
+ routingOperation.setDragSort(dragSort++);
+ desiredOperationList.add(routingOperation);
+ }
+ return desiredOperationList;
+ }
+
+ private Map<String, Deque<ProductionOrderRoutingOperation>> buildExistingRoutingOperationBucketMap(List<ProductionOrderRoutingOperation> existingOperationList) {
+ Map<String, Deque<ProductionOrderRoutingOperation>> existingBucketMap = new LinkedHashMap<>();
+ if (existingOperationList == null || existingOperationList.isEmpty()) {
+ return existingBucketMap;
+ }
+ for (ProductionOrderRoutingOperation routingOperation : existingOperationList) {
+ String bucketKey = buildRoutingOperationBucketKey(
+ routingOperation.getTechnologyOperationId(),
+ routingOperation.getProductModelId());
+ existingBucketMap.computeIfAbsent(bucketKey, key -> new ArrayDeque<>()).addLast(routingOperation);
+ }
+ return existingBucketMap;
+ }
+
+ private ProductionOrderRoutingOperation insertRoutingOperationSnapshot(Long orderRoutingId,
+ Long productionOrderId,
+ ProductionOrderRoutingOperation desiredOperation) {
+ ProductionOrderRoutingOperation insert = new ProductionOrderRoutingOperation();
+ insert.setOrderRoutingId(orderRoutingId);
+ insert.setProductionOrderId(productionOrderId);
+ insert.setProductModelId(desiredOperation.getProductModelId());
+ insert.setTechnologyOperationId(desiredOperation.getTechnologyOperationId());
+ insert.setOperationName(desiredOperation.getOperationName());
+ insert.setIsQuality(desiredOperation.getIsQuality());
+ insert.setIsProduction(desiredOperation.getIsProduction());
+ insert.setType(desiredOperation.getType());
+ insert.setDragSort(desiredOperation.getDragSort());
+ productionOrderRoutingOperationMapper.insert(insert);
+ syncRoutingOperationParams(insert.getId(), productionOrderId, insert.getTechnologyOperationId());
+ return insert;
+ }
+
+ private void updateRoutingOperationSnapshotIfNecessary(ProductionOrderRoutingOperation currentOperation,
+ Long orderRoutingId,
+ Long productionOrderId,
+ ProductionOrderRoutingOperation desiredOperation) {
+ if (currentOperation == null || currentOperation.getId() == null) {
+ return;
+ }
+ ProductionOrderRoutingOperation update = new ProductionOrderRoutingOperation();
+ update.setId(currentOperation.getId());
+ boolean changed = false;
+ if (!Objects.equals(currentOperation.getOrderRoutingId(), orderRoutingId)) {
+ update.setOrderRoutingId(orderRoutingId);
+ currentOperation.setOrderRoutingId(orderRoutingId);
+ changed = true;
+ }
+ if (!Objects.equals(currentOperation.getProductionOrderId(), productionOrderId)) {
+ update.setProductionOrderId(productionOrderId);
+ currentOperation.setProductionOrderId(productionOrderId);
+ changed = true;
+ }
+ if (!Objects.equals(currentOperation.getProductModelId(), desiredOperation.getProductModelId())) {
+ update.setProductModelId(desiredOperation.getProductModelId());
+ currentOperation.setProductModelId(desiredOperation.getProductModelId());
+ changed = true;
+ }
+ if (!Objects.equals(currentOperation.getTechnologyOperationId(), desiredOperation.getTechnologyOperationId())) {
+ update.setTechnologyOperationId(desiredOperation.getTechnologyOperationId());
+ currentOperation.setTechnologyOperationId(desiredOperation.getTechnologyOperationId());
+ changed = true;
+ }
+ if (!Objects.equals(currentOperation.getOperationName(), desiredOperation.getOperationName())) {
+ update.setOperationName(desiredOperation.getOperationName());
+ currentOperation.setOperationName(desiredOperation.getOperationName());
+ changed = true;
+ }
+ if (!Objects.equals(currentOperation.getIsQuality(), desiredOperation.getIsQuality())) {
+ update.setIsQuality(desiredOperation.getIsQuality());
+ currentOperation.setIsQuality(desiredOperation.getIsQuality());
+ changed = true;
+ }
+ if (!Objects.equals(currentOperation.getIsProduction(), desiredOperation.getIsProduction())) {
+ update.setIsProduction(desiredOperation.getIsProduction());
+ currentOperation.setIsProduction(desiredOperation.getIsProduction());
+ changed = true;
+ }
+ if (!Objects.equals(currentOperation.getType(), desiredOperation.getType())) {
+ update.setType(desiredOperation.getType());
+ currentOperation.setType(desiredOperation.getType());
+ changed = true;
+ }
+ if (!Objects.equals(currentOperation.getDragSort(), desiredOperation.getDragSort())) {
+ update.setDragSort(desiredOperation.getDragSort());
+ currentOperation.setDragSort(desiredOperation.getDragSort());
+ changed = true;
+ }
+ if (changed) {
+ productionOrderRoutingOperationMapper.updateById(update);
+ }
+ }
+
+ private void removeRoutingOperationSnapshot(ProductionOrderRoutingOperation routingOperation) {
+ if (routingOperation == null || routingOperation.getId() == null) {
+ return;
+ }
+ ProductionOperationTask task = productionOperationTaskMapper.selectOne(
+ Wrappers.<ProductionOperationTask>lambdaQuery()
+ .eq(ProductionOperationTask::getProductionOrderRoutingOperationId, routingOperation.getId())
+ .last("limit 1"));
+ if (task != null) {
+ validateTaskCanRemove(task);
+ productionOperationTaskMapper.deleteById(task.getId());
+ }
+ productionOrderRoutingOperationParamMapper.delete(
+ Wrappers.<ProductionOrderRoutingOperationParam>lambdaQuery()
+ .eq(ProductionOrderRoutingOperationParam::getProductionOrderRoutingOperationId, routingOperation.getId()));
+ productionOrderRoutingOperationMapper.deleteById(routingOperation.getId());
+ }
+
+ private void syncRoutingOperationTasks(Long productionOrderId, List<ProductionOrderRoutingOperation> routingOperationList) {
+ if (routingOperationList == null || routingOperationList.isEmpty()) {
+ return;
+ }
+ List<Long> routingOperationIdList = routingOperationList.stream()
+ .map(ProductionOrderRoutingOperation::getId)
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList());
+ if (routingOperationIdList.isEmpty()) {
+ return;
+ }
+ Map<Long, ProductionOperationTask> taskByRoutingOperationId = productionOperationTaskMapper.selectList(
+ Wrappers.<ProductionOperationTask>lambdaQuery()
+ .in(ProductionOperationTask::getProductionOrderRoutingOperationId, routingOperationIdList)
+ .orderByAsc(ProductionOperationTask::getId))
+ .stream()
+ .filter(item -> item != null && item.getProductionOrderRoutingOperationId() != null)
+ .collect(Collectors.toMap(
+ ProductionOperationTask::getProductionOrderRoutingOperationId,
+ item -> item,
+ (left, right) -> left,
+ LinkedHashMap::new));
+ for (int i = 0; i < routingOperationList.size(); i++) {
+ ProductionOrderRoutingOperation routingOperation = routingOperationList.get(i);
+ if (routingOperation == null || routingOperation.getId() == null) {
+ continue;
+ }
+ boolean shouldHaveTask = i == routingOperationList.size() - 1 || Boolean.TRUE.equals(routingOperation.getIsProduction());
+ ProductionOperationTask existingTask = taskByRoutingOperationId.get(routingOperation.getId());
+ if (shouldHaveTask) {
+ if (existingTask == null) {
+ ProductionOperationTask task = new ProductionOperationTask();
+ task.setProductionOrderId(productionOrderId);
+ task.setProductionOrderRoutingOperationId(routingOperation.getId());
+ task.setPlanQuantity(BigDecimal.ZERO);
+ task.setCompleteQuantity(BigDecimal.ZERO);
+ task.setWorkOrderNo(generateNextTaskNo());
+ task.setStatus(2);
+ productionOperationTaskMapper.insert(task);
+ }
+ continue;
+ }
+ if (existingTask != null) {
+ validateTaskCanRemove(existingTask);
+ productionOperationTaskMapper.deleteById(existingTask.getId());
+ }
+ }
+ }
+
+ private void validateTaskCanRemove(ProductionOperationTask task) {
+ if (task == null || task.getId() == null) {
+ return;
+ }
+ if (defaultDecimal(task.getCompleteQuantity()).compareTo(BigDecimal.ZERO) > 0) {
+ throw new ServiceException("宸ュ簭宸蹭骇鐢熸姤宸ヨ褰曪紝鏃犳硶鏍规嵁 BOM 鍙樻洿鍒犻櫎瀵瑰簲宸ュ簭蹇収" + task.getWorkOrderNo());
+ }
+ long reportCount = productionProductMainMapper.selectCount(
+ Wrappers.<ProductionProductMain>lambdaQuery()
+ .eq(ProductionProductMain::getProductionOperationTaskId, task.getId()));
+ if (reportCount > 0) {
+ throw new ServiceException("宸ュ簭宸蹭骇鐢熸姤宸ヨ褰曪紝鏃犳硶鏍规嵁 BOM 鍙樻洿鍒犻櫎瀵瑰簲宸ュ崟");
+ }
+ }
+
+ private void syncRoutingOperationParams(Long routingOperationId, Long productionOrderId, Long technologyOperationId) {
+ if (routingOperationId == null || technologyOperationId == null) {
+ return;
+ }
+ List<TechnologyOperationParam> operationParamList = technologyOperationParamMapper.selectList(
+ Wrappers.<TechnologyOperationParam>lambdaQuery()
+ .eq(TechnologyOperationParam::getTechnologyOperationId, technologyOperationId)
+ .orderByAsc(TechnologyOperationParam::getId));
+ for (TechnologyOperationParam operationParam : operationParamList) {
+ TechnologyParam technologyParam = technologyParamMapper.selectById(operationParam.getTechnologyParamId());
+ if (technologyParam == null) {
+ continue;
+ }
+ ProductionOrderRoutingOperationParam snapshot = new ProductionOrderRoutingOperationParam();
+ snapshot.setProductionOrderId(productionOrderId);
+ snapshot.setProductionOrderRoutingOperationId(routingOperationId);
+ snapshot.setTechnologyOperationId(operationParam.getTechnologyOperationId());
+ snapshot.setTechnologyOperationParamId(operationParam.getId());
+ snapshot.setParamId(technologyParam.getId());
+ snapshot.setParamCode(technologyParam.getParamCode());
+ snapshot.setParamName(technologyParam.getParamName());
+ snapshot.setParamType(technologyParam.getParamType());
+ snapshot.setParamFormat(technologyParam.getParamFormat());
+ snapshot.setUnit(technologyParam.getUnit());
+ snapshot.setIsRequired(technologyParam.getIsRequired());
+ snapshot.setRemark(technologyParam.getRemark());
+ snapshot.setStandardValue(operationParam.getStandardValue());
+ productionOrderRoutingOperationParamMapper.insert(snapshot);
+ }
+ }
+
+ private String buildRoutingOperationBucketKey(Long operationId, Long outputProductModelId) {
return String.valueOf(operationId) + "#" + String.valueOf(outputProductModelId);
}
- private String buildOperationOutputNodeKey(Long operationId, Long outputNodeId, Long outputProductModelId) {
- return String.valueOf(operationId) + "#" + String.valueOf(outputNodeId) + "#" + String.valueOf(outputProductModelId);
- }
-
- private ProductionBomStructure resolveOperationOutputNode(ProductionBomStructure bomStructure,
- Map<Long, ProductionBomStructure> structureById) {
- if (bomStructure == null) {
+ private TechnologyOperation getTechnologyOperation(Long technologyOperationId) {
+ if (technologyOperationId == null) {
return null;
}
- // The root node is the first output node; other rows use their direct parent as the current operation output.
- if (bomStructure.getParentId() == null) {
- return bomStructure;
- }
- ProductionBomStructure parent = structureById.get(bomStructure.getParentId());
- return parent != null ? parent : bomStructure;
+ return technologyOperationMapper.selectById(technologyOperationId);
}
- private Long resolveOutputProductModelId(ProductionBomStructure outputNode,
- Long rootProductModelId) {
- if (outputNode == null) {
- return rootProductModelId;
+
+ private String generateNextTaskNo() {
+ String datePrefix = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
+ ProductionOperationTask latestTask = productionOperationTaskMapper.selectOne(
+ Wrappers.<ProductionOperationTask>lambdaQuery()
+ .likeRight(ProductionOperationTask::getWorkOrderNo, "GD" + datePrefix)
+ .orderByDesc(ProductionOperationTask::getWorkOrderNo)
+ .last("limit 1"));
+ int sequenceNumber = 1;
+ if (latestTask != null && latestTask.getWorkOrderNo() != null && latestTask.getWorkOrderNo().startsWith("GD" + datePrefix)) {
+ try {
+ sequenceNumber = Integer.parseInt(latestTask.getWorkOrderNo().substring(("GD" + datePrefix).length())) + 1;
+ } catch (NumberFormatException ignored) {
+ sequenceNumber = 1;
+ }
}
- return outputNode.getProductModelId() != null ? outputNode.getProductModelId() : rootProductModelId;
+ return "GD" + String.format("%s%03d", datePrefix, sequenceNumber);
}
private BigDecimal defaultDecimal(BigDecimal value) {
@@ -340,20 +604,6 @@
private int compareDecimal(BigDecimal left, BigDecimal right) {
return defaultDecimal(left).compareTo(defaultDecimal(right));
- }
-
- /**
- * 灏嗘爲褰㈢粨鏋勬媿骞虫垚鍒楄〃锛屼究浜庣粺涓�淇濆瓨銆�
- */
- private void flattenTree(List<ProductionBomStructureDto> source, List<ProductionBomStructureDto> result) {
- // 鎵佸钩鍖栧鐞嗘爲
- if (source == null) {
- return;
- }
- for (ProductionBomStructureDto node : source) {
- result.add(node);
- flattenTree(node.getChildren(), result);
- }
}
}
--
Gitblit v1.9.3