From f4c288c55d08c04cd026508b358beebfcdce5fc2 Mon Sep 17 00:00:00 2001
From: liding <756868258@qq.com>
Date: 星期五, 22 五月 2026 09:29:09 +0800
Subject: [PATCH] feat(product): 添加产品型号向下复制功能并优化生产报工重量计算 - 在ProductController中新增downCopy接口实现产品型号批量复制功能 - 将ProductionProductMainDto中的bomInputQty字段重命名为inputWeight - 在ProductionProductMainServiceImpl中添加JSON解析逻辑支持从otherData中提取投入重量 - 新增resolveInputWeight、findParameterValue、findFieldValue等工具方法处理复杂参数解析 - 为ProductModelDto添加targetProductId字段用于指定复制目标 - 修复销售台账按调度员ID和姓名分组的SQL查询问题 - 优化库存服务中剩余数量计算的空值处理逻辑 - 完善生产投料数量为空时的默认值处理机制

---
 src/main/java/com/ruoyi/production/service/impl/ProductBomServiceImpl.java |  208 +++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 204 insertions(+), 4 deletions(-)

diff --git a/src/main/java/com/ruoyi/production/service/impl/ProductBomServiceImpl.java b/src/main/java/com/ruoyi/production/service/impl/ProductBomServiceImpl.java
index 2554b5e..b91ba31 100644
--- a/src/main/java/com/ruoyi/production/service/impl/ProductBomServiceImpl.java
+++ b/src/main/java/com/ruoyi/production/service/impl/ProductBomServiceImpl.java
@@ -2,6 +2,7 @@
 
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.ruoyi.basic.pojo.Product;
@@ -15,13 +16,12 @@
 import com.ruoyi.production.dto.BomImportDto;
 import com.ruoyi.production.dto.ProductBomDto;
 import com.ruoyi.production.dto.ProductStructureDto;
-import com.ruoyi.production.mapper.ProductBomMapper;
-import com.ruoyi.production.pojo.ProductBom;
-import com.ruoyi.production.pojo.ProductProcess;
-import com.ruoyi.production.pojo.ProductStructure;
+import com.ruoyi.production.mapper.*;
+import com.ruoyi.production.pojo.*;
 import com.ruoyi.production.service.ProductBomService;
 import com.ruoyi.production.service.ProductProcessService;
 import com.ruoyi.production.service.ProductStructureService;
+import org.jetbrains.annotations.NotNull;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
@@ -59,6 +59,18 @@
     @Autowired
     private ProductProcessService productProcessService;
 
+    @Autowired
+    private ProductStructureMapper productStructureMapper;
+
+    @Autowired
+    private ProductOrderMapper productOrderMapper;
+
+    @Autowired
+    private ProductProcessRouteMapper productProcessRouteMapper;
+
+    @Autowired
+    private ProcessRouteMapper processRouteMapper;
+
     @Override
     public IPage<ProductBomDto> listPage(Page page, ProductBomDto productBomDto) {
         return productBomMapper.listPage(page, productBomDto);
@@ -99,6 +111,194 @@
 
     @Override
     @Transactional(rollbackFor = Exception.class)
+    public AjaxResult update(ProductBom productBom) {
+        //  鏌ヨ鍑轰骇鍝佹ā鍨嬩俊鎭�
+        if (productBom.getProductModelId() == null) {
+            throw new ServiceException("璇烽�夋嫨浜у搧妯″瀷");
+        }
+
+        ProductBom oldBom = productBomMapper.selectById(productBom.getId());
+        // 濡傛灉瑙勬牸鏀瑰彉锛屽叧鑱旂殑鐢熶骇璁㈠崟濡傛灉璁㈠崟瀹屾垚鏁伴噺>0锛屽垯涓嶈鏀筨om锛涘惁鍒欏垹闄や箣鍓嶅叧鑱旂殑浜у搧缁撴瀯, 淇敼鍏宠仈璁㈠崟鐨勪骇鍝佷俊鎭�
+        if (!oldBom.getProductModelId().equals(productBom.getProductModelId())) {
+            ProductModel productModel = productModelService.getById(productBom.getProductModelId());
+            if (productModel == null) {
+                throw new ServiceException("閫夋嫨鐨勪骇鍝佹ā鍨嬩笉瀛樺湪");
+            }
+
+            // 鍏宠仈鐨勭敓浜ц鍗曞鏋滆鍗曞畬鎴愭暟閲�>0锛屽垯涓嶈鏀筨om
+            // 鍏堟煡璇笌璇OM鍏宠仈鐨勫伐鑹鸿矾绾�
+            List<ProductProcessRoute> productProcessRoutes = productProcessRouteMapper.selectList(new LambdaQueryWrapper<ProductProcessRoute>()
+                    .eq(ProductProcessRoute::getBomId, oldBom.getId()));
+
+            // 妫�鏌ユ槸鍚︽湁鍏宠仈鐨勫伐鑹鸿矾绾�
+            if (!productProcessRoutes.isEmpty()) {
+                // 鎻愬彇宸ヨ壓璺嚎鍏宠仈鐨勭敓浜ц鍗旾D
+                List<Long> orderIds = productProcessRoutes.stream()
+                        .map(ProductProcessRoute::getProductOrderId)
+                        .filter(Objects::nonNull)
+                        .collect(Collectors.toList());
+
+                // 鏌ヨ鍏宠仈鐨勭敓浜ц鍗�
+                if (!orderIds.isEmpty()) {
+                    List<ProductOrder> productOrders = productOrderMapper.selectList(new LambdaQueryWrapper<ProductOrder>()
+                            .in(ProductOrder::getId, orderIds));
+
+                    // 妫�鏌ヨ鍗曞畬鎴愭暟閲�
+                    for (ProductOrder order : productOrders) {
+                        if (order.getCompleteQuantity() != null && order.getCompleteQuantity().compareTo(BigDecimal.ZERO) > 0) {
+                            throw new ServiceException("璇OM宸插叧鑱旂敓浜ц鍗曚笖鏈夊畬鎴愭暟閲忥紝鏃犳硶淇敼");
+                        } else {
+                            // 淇敼鍏宠仈璁㈠崟鐨勪骇鍝佷俊鎭�
+                            order.setProductModelId(productBom.getProductModelId());
+                            productOrderMapper.updateById(order);
+                        }
+                    }
+                }
+
+                // 淇敼鍏宠仈浜у搧宸ヨ壓璺嚎鐨勪骇鍝佷俊鎭�
+                for (ProductProcessRoute route : productProcessRoutes) {
+                    route.setProductModelId(productBom.getProductModelId());
+                    productProcessRouteMapper.updateById(route);
+                }
+
+                // 鏌ヨ鍏宠仈鐨勫伐鑹鸿矾绾�
+                List<ProcessRoute> processRoutes = processRouteMapper.selectList(new LambdaQueryWrapper<ProcessRoute>()
+                        .eq(ProcessRoute::getBomId, oldBom.getId()));
+                if (!processRoutes.isEmpty()) {
+                    // 淇敼鍏宠仈宸ヨ壓璺嚎鐨勪骇鍝佷俊鎭�
+                    for (ProcessRoute route : processRoutes) {
+                        route.setProductModelId(productBom.getProductModelId());
+                        processRouteMapper.updateById(route);
+                    }
+                }
+            }
+
+            // 鍒犻櫎涔嬪墠鍏宠仈鐨勪骇鍝佺粨鏋�
+            productStructureMapper.delete(new LambdaQueryWrapper<ProductStructure>().eq(ProductStructure::getBomId, productBom.getId()));
+
+            // 鍏宠仈鏂扮殑浜у搧缁撴瀯
+            ProductStructure productStructure = new ProductStructure();
+            productStructure.setProductModelId(productBom.getProductModelId());
+            productStructure.setUnit(productModel.getUnit());
+            productStructure.setUnitQuantity(BigDecimal.valueOf(1));
+            productStructure.setBomId(productBom.getId());
+            productStructureService.save(productStructure);
+        }
+        productBomMapper.updateById(productBom);
+        return AjaxResult.success();
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public AjaxResult copy(ProductBomDto productBom) {
+        Long copyId = productBom.getCopyId();
+        if (copyId == null) {
+            throw new ServiceException("澶嶅埗婧怋OM ID涓嶈兘涓虹┖");
+        }
+        ProductBom sourceBom = productBomMapper.selectById(copyId);
+        if (sourceBom == null) {
+            throw new ServiceException("澶嶅埗婧怋OM涓嶅瓨鍦�");
+        }
+
+
+        ProductBom newBom = getProductBom(productBom, sourceBom);
+        productBomMapper.insert(newBom);
+        newBom.setBomNo("BM." + String.format("%05d", newBom.getId()));
+        productBomMapper.updateById(newBom);
+
+
+
+        ProductModel productModel = productModelService.getById(newBom.getProductModelId());
+        if (productModel == null) {
+            throw new ServiceException("閫夋嫨鐨勪骇鍝佹ā鍨嬩笉瀛樺湪");
+        }
+
+        ProductStructure newRoot = getProductStructure(newBom, productModel);
+        productStructureService.save(newRoot);
+        List<ProductStructure> sourceStructures = productStructureMapper.selectList(
+                        Wrappers.<ProductStructure>lambdaQuery()
+                                .eq(ProductStructure::getBomId, copyId.intValue()));
+        if (sourceStructures == null || sourceStructures.isEmpty()) {
+            return AjaxResult.success();
+        }
+
+        ProductStructure oldRoot = sourceStructures.stream()
+                .filter(s -> s.getParentId() == null)
+                .findFirst().orElse(new ProductStructure());
+
+        List<ProductStructure> children = sourceStructures
+                .stream()
+                .filter(s -> !s.getId().equals(oldRoot.getId()))
+                .collect(Collectors.toList());
+
+        Map<Long, Long> oldNewIdMap = new HashMap<>();
+
+        oldNewIdMap.put(oldRoot.getId(), newRoot.getId());
+
+        List<ProductStructure> insertList = children
+                .stream()
+                .map(item -> getProductStructures(item, newBom))
+                .collect(Collectors.toList());
+
+        productStructureService.saveBatch(insertList);
+
+        for (int i = 0; i < children.size(); i++) {
+            oldNewIdMap.put(
+                    children.get(i).getId(),
+                    insertList.get(i).getId()
+            );
+        }
+
+        List<ProductStructure> updateList = new ArrayList<>();
+        for (int i = 0; i < children.size(); i++) {
+            ProductStructure source = children.get(i);
+            ProductStructure inserted = insertList.get(i);
+            Long newParentId = oldNewIdMap.get(source.getParentId());
+            if (newParentId != null) {
+                inserted.setParentId(newParentId);
+                updateList.add(inserted);
+            }
+        }
+        if (!updateList.isEmpty()) {
+            productStructureService.updateBatchById(updateList);
+        }
+
+        return AjaxResult.success();
+    }
+
+    @NotNull
+    private static ProductStructure getProductStructures(ProductStructure item, ProductBom newBom) {
+        ProductStructure copy = new ProductStructure();
+        copy.setProductModelId(item.getProductModelId());
+        copy.setProcessId(item.getProcessId());
+        copy.setUnitQuantity(item.getUnitQuantity());
+        copy.setDemandedQuantity(item.getDemandedQuantity());
+        copy.setUnit(item.getUnit());
+        copy.setBomId(newBom.getId());
+        return copy;
+    }
+
+    @NotNull
+    private static ProductStructure getProductStructure(ProductBom newBom, ProductModel productModel) {
+        ProductStructure newRoot = new ProductStructure();
+        newRoot.setProductModelId(newBom.getProductModelId());
+        newRoot.setUnitQuantity(BigDecimal.valueOf(1));
+        newRoot.setUnit(productModel.getUnit());
+        newRoot.setBomId(newBom.getId());
+        return newRoot;
+    }
+
+    @NotNull
+    private static ProductBom getProductBom(ProductBomDto productBom, ProductBom sourceBom) {
+        ProductBom newBom = new ProductBom();
+        newBom.setProductModelId(productBom.getProductModelId() != null ? productBom.getProductModelId() : sourceBom.getProductModelId());
+        newBom.setRemark(productBom.getRemark());
+        newBom.setVersion(productBom.getVersion() != null ? productBom.getVersion() : sourceBom.getVersion());
+        return newBom;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
     public AjaxResult uploadBom(MultipartFile file) {
         ExcelUtil<BomImportDto> util = new ExcelUtil<>(BomImportDto.class);
         List<BomImportDto> list;

--
Gitblit v1.9.3