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,则不许改bom;否则删除之前关联的产品结构, 修改关联订单的产品信息
        if (!oldBom.getProductModelId().equals(productBom.getProductModelId())) {
            ProductModel productModel = productModelService.getById(productBom.getProductModelId());
            if (productModel == null) {
                throw new ServiceException("选择的产品模型不存在");
            }
            // 关联的生产订单如果订单完成数量>0,则不许改bom
            // 先查询与该BOM关联的工艺路线
            List<ProductProcessRoute> productProcessRoutes = productProcessRouteMapper.selectList(new LambdaQueryWrapper<ProductProcessRoute>()
                    .eq(ProductProcessRoute::getBomId, oldBom.getId()));
            // 检查是否有关联的工艺路线
            if (!productProcessRoutes.isEmpty()) {
                // 提取工艺路线关联的生产订单ID
                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("该BOM已关联生产订单且有完成数量,无法修改");
                        } 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("复制源BOM ID不能为空");
        }
        ProductBom sourceBom = productBomMapper.selectById(copyId);
        if (sourceBom == null) {
            throw new ServiceException("复制源BOM不存在");
        }
        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;