From f57064d2fa563ee4cfeeccd715850ba8b8aa4f60 Mon Sep 17 00:00:00 2001
From: liyong <18434998025@163.com>
Date: 星期一, 18 五月 2026 09:44:33 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/dev_New_pro' into dev_宁夏_英泽防锈
---
src/main/java/com/ruoyi/production/service/impl/ProductionBomStructureServiceImpl.java | 614 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 612 insertions(+), 2 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 cbae0dc..31cdc79 100644
--- a/src/main/java/com/ruoyi/production/service/impl/ProductionBomStructureServiceImpl.java
+++ b/src/main/java/com/ruoyi/production/service/impl/ProductionBomStructureServiceImpl.java
@@ -1,18 +1,44 @@
package com.ruoyi.production.service.impl;
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.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.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;
/**
* <p>
@@ -26,13 +52,24 @@
@RequiredArgsConstructor()
public class ProductionBomStructureServiceImpl extends ServiceImpl<ProductionBomStructureMapper, ProductionBomStructure> implements ProductionBomStructureService {
- private final ProductionBomStructureMapper productionBomStructureMapper;
+ 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 TechnologyOperationMapper technologyOperationMapper;
+ private final TechnologyOperationParamMapper technologyOperationParamMapper;
+ private final TechnologyParamMapper technologyParamMapper;
/**
* 鏍规嵁BOM鏌ヨ骞剁粍瑁呯粨鏋勬爲銆�
*/
@Override
public List<ProductionBomStructureVo> listByBomId(Long bomId) {
+ // 鎸塀OMID鏌ヨ鐢熶骇缁撴瀯鏁版嵁
List<ProductionBomStructureVo> list = productionBomStructureMapper.listByBomId(bomId);
Map<Long, ProductionBomStructureVo> map = new HashMap<>();
for (ProductionBomStructureVo node : list) {
@@ -58,13 +95,17 @@
@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);
+ // 鏌ヨ鏁版嵁搴撳凡鏈夌粨鏋勶紝鐢ㄤ簬鍚庣画鍋氬鍒犳敼瀵规瘮
List<ProductionBomStructure> dbList = this.list(new LambdaQueryWrapper<ProductionBomStructure>()
.eq(ProductionBomStructure::getProductionOrderBomId, orderBomId));
+ // 鏀堕泦鍓嶇浠嶇劧瀛樺湪鐨勮妭鐐笽D
Set<Long> frontendIds = new HashSet<>();
for (ProductionBomStructureDto item : flatDtoList) {
if (item.getId() != null) {
@@ -72,16 +113,19 @@
}
}
+ // 璁$畻闇�瑕佸垹闄ょ殑鑺傜偣锛堟暟鎹簱鏈夈�佸墠绔凡鍒犻櫎锛�
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鏄犲皠
List<ProductionBomStructure> insertList = new ArrayList<>();
List<ProductionBomStructure> updateList = new ArrayList<>();
Map<String, ProductionBomStructure> tempEntityMap = new HashMap<>();
@@ -99,10 +143,12 @@
}
}
+ // 鎵归噺鏂板锛屾嬁鍒版暟鎹簱鐢熸垚鐨勭湡瀹濱D
if (!insertList.isEmpty()) {
this.saveBatch(insertList);
}
+ // 鏂板鑺傜偣浜屾鍥炲啓鐖禝D锛堝墠绔紶鐨勬槸涓存椂鐖禝D锛�
List<ProductionBomStructure> parentFixList = new ArrayList<>();
for (ProductionBomStructureDto item : flatDtoList) {
if (item.getId() == null && item.getParentTempId() != null) {
@@ -111,25 +157,589 @@
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());
+ return true;
+ }
+
+ private void syncDemandedQuantityAndTaskPlanQuantity(Long orderBomId, Long productionOrderId) {
+ if (orderBomId == null) {
+ return;
+ }
+ ProductionOrderBom orderBom = productionOrderBomMapper.selectById(orderBomId);
+ if (orderBom == null) {
+ return;
+ }
+ Long currentProductionOrderId = productionOrderId != null ? productionOrderId : orderBom.getProductionOrderId();
+ if (currentProductionOrderId == null) {
+ return;
+ }
+ ProductionOrder productionOrder = productionOrderMapper.selectById(currentProductionOrderId);
+ if (productionOrder == null) {
+ return;
+ }
+
+ BigDecimal orderQuantity = defaultDecimal(productionOrder.getQuantity());
+ List<ProductionBomStructure> structureList = this.list(
+ Wrappers.<ProductionBomStructure>lambdaQuery()
+ .eq(ProductionBomStructure::getProductionOrderBomId, orderBomId)
+ .orderByAsc(ProductionBomStructure::getId));
+ //鍚屾闇�姹傛暟閲�
+ syncStructureDemandedQuantity(structureList, orderQuantity);
+ Long rootProductModelId = orderBom.getProductModelId() != null ? orderBom.getProductModelId() : productionOrder.getProductModelId();
+ //鍚屾鐢熶骇宸ヨ壓璺嚎
+ syncRoutingOperationsByBom(currentProductionOrderId, productionOrder, orderBom, structureList, rootProductModelId);
+ //鍚屾宸ュ崟
+ syncTaskPlanQuantity(
+ currentProductionOrderId,
+ structureList,
+ orderQuantity,
+ rootProductModelId);
+ }
+
+ private void syncStructureDemandedQuantity(List<ProductionBomStructure> structureList, BigDecimal orderQuantity) {
+ if (structureList == null || structureList.isEmpty()) {
+ return;
+ }
+ List<ProductionBomStructure> updateList = new ArrayList<>();
+ BigDecimal lastProcessDemandedQuantity = orderQuantity;
+ for (ProductionBomStructure structure : structureList) {
+ if (structure == null || structure.getId() == null) {
+ continue;
+ }
+
+ BigDecimal demandedQuantity = lastProcessDemandedQuantity.multiply(defaultDecimal(structure.getUnitQuantity()));
+// if (compareDecimal(structure.getDemandedQuantity(), demandedQuantity) == 0) {
+// continue;
+// }
+ ProductionBomStructure update = new ProductionBomStructure();
+ update.setId(structure.getId());
+ update.setDemandedQuantity(demandedQuantity);
+ updateList.add(update);
+ structure.setDemandedQuantity(demandedQuantity);
+ lastProcessDemandedQuantity = demandedQuantity;
}
if (!updateList.isEmpty()) {
this.updateBatchById(updateList);
}
- return true;
+ }
+
+ private void syncTaskPlanQuantity(Long productionOrderId,
+ List<ProductionBomStructure> structureList,
+ BigDecimal orderQuantity,
+ Long rootProductModelId) {
+ List<ProductionOperationTask> taskList = productionOperationTaskMapper.selectList(
+ Wrappers.<ProductionOperationTask>lambdaQuery()
+ .eq(ProductionOperationTask::getProductionOrderId, productionOrderId)
+ .orderByAsc(ProductionOperationTask::getId));
+ 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) {
+ continue;
+ }
+ ProductionOrderRoutingOperation routingOperation = routingOperationMap.get(task.getProductionOrderRoutingOperationId());
+ if (routingOperation == null) {
+ continue;
+ }
+ BigDecimal planQuantity = resolveTaskPlanQuantity(
+ routingOperation,
+ demandedQuantityMap,
+ orderQuantity,
+ rootProductModelId);
+ if (compareDecimal(task.getPlanQuantity(), planQuantity) == 0) {
+ continue;
+ }
+ ProductionOperationTask update = new ProductionOperationTask();
+ update.setId(task.getId());
+ update.setPlanQuantity(planQuantity);
+ productionOperationTaskMapper.updateById(update);
+ }
+ }
+
+ 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(desiredOperation, orderRouting.getId(), productionOrderId, matchedOperation);
+ }
+ finalOperationList.add(matchedOperation);
+ }
+ for (Deque<ProductionOrderRoutingOperation> queue : existingBucketMap.values()) {
+ while (queue != null && !queue.isEmpty()) {
+ removeRoutingOperationSnapshot(queue.pollFirst());
+ }
+ }
+ syncRoutingOperationTasks(productionOrderId, finalOperationList);
+ }
+
+ 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;
+ }
+
+ private List<ProductionOrderRoutingOperation> buildDesiredRoutingOperationList(List<ProductionBomStructure> structureList,
+ Long rootProductModelId) {
+ if (structureList == null || structureList.isEmpty()) {
+ return Collections.emptyList();
+ }
+ 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, ProductionBomStructure> uniqueOperationMap = new LinkedHashMap<>();
+ for (ProductionBomStructure bomStructure : structureList) {
+ if (bomStructure == null || bomStructure.getTechnologyOperationId() == null) {
+ continue;
+ }
+ Long outputProductModelId = resolveOutputProductModelId(resolveOperationOutputNode(bomStructure, structureById), rootProductModelId);
+ uniqueOperationMap.putIfAbsent(buildBomOperationDedupKey(bomStructure, outputProductModelId), bomStructure);
+ }
+ List<ProductionOrderRoutingOperation> desiredOperationList = new ArrayList<>();
+ int dragSort = 1;
+ for (ProductionBomStructure bomStructure : uniqueOperationMap.values()) {
+ Long outputProductModelId = resolveOutputProductModelId(resolveOperationOutputNode(bomStructure, structureById), rootProductModelId);
+ TechnologyOperation technologyOperation = getTechnologyOperation(bomStructure.getTechnologyOperationId());
+ ProductionOrderRoutingOperation routingOperation = new ProductionOrderRoutingOperation();
+ routingOperation.setProductModelId(outputProductModelId);
+ routingOperation.setTechnologyOperationId(bomStructure.getTechnologyOperationId());
+ 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 鍙樻洿鍒犻櫎瀵瑰簲宸ュ簭蹇収");
+ }
+ 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 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;
+ }
+ // 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);
+ }
+ return demandedQuantityMap;
+ }
+
+ private BigDecimal resolveTaskPlanQuantity(ProductionOrderRoutingOperation routingOperation,
+ Map<String, BigDecimal> demandedQuantityMap,
+ BigDecimal orderQuantity,
+ Long rootProductModelId) {
+ if (routingOperation == null || demandedQuantityMap == null || demandedQuantityMap.isEmpty()) {
+ return orderQuantity;
+ }
+ Long outputProductModelId = routingOperation.getProductModelId() != null
+ ? routingOperation.getProductModelId()
+ : rootProductModelId;
+ String key = buildOperationDemandedQuantityKey(
+ routingOperation.getTechnologyOperationId(),
+ outputProductModelId);
+ BigDecimal planQuantity = demandedQuantityMap.get(key);
+ return planQuantity != null ? planQuantity : orderQuantity;
+ }
+
+ private String buildOperationDemandedQuantityKey(Long operationId, Long outputProductModelId) {
+ return String.valueOf(operationId) + "#" + String.valueOf(outputProductModelId);
+ }
+
+ private String buildRoutingOperationBucketKey(Long operationId, Long outputProductModelId) {
+ return String.valueOf(operationId) + "#" + String.valueOf(outputProductModelId);
+ }
+
+ private String buildBomOperationDedupKey(ProductionBomStructure bomStructure, Long outputProductModelId) {
+ Long operationId = bomStructure == null ? null : bomStructure.getTechnologyOperationId();
+ Long parentId = bomStructure == null ? null : bomStructure.getParentId();
+ return operationId + "#" + outputProductModelId + "#" + parentId;
+ }
+
+ 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) {
+ 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;
+ }
+
+ private Long resolveOutputProductModelId(ProductionBomStructure outputNode,
+ Long rootProductModelId) {
+ if (outputNode == null) {
+ return rootProductModelId;
+ }
+ return outputNode.getProductModelId() != null ? outputNode.getProductModelId() : rootProductModelId;
+ }
+
+ private TechnologyOperation getTechnologyOperation(Long technologyOperationId) {
+ if (technologyOperationId == null) {
+ return null;
+ }
+ return technologyOperationMapper.selectById(technologyOperationId);
+ }
+
+ 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 "GD" + String.format("%s%03d", datePrefix, sequenceNumber);
+ }
+
+ private BigDecimal defaultDecimal(BigDecimal value) {
+ return value == null ? BigDecimal.ZERO : value;
+ }
+
+ 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;
}
--
Gitblit v1.9.3