package com.ruoyi.technology.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.ruoyi.basic.mapper.ProductModelMapper; import com.ruoyi.basic.pojo.ProductModel; import com.ruoyi.common.exception.ServiceException; import com.ruoyi.technology.bean.dto.TechnologyBomStructureDto; import com.ruoyi.technology.bean.vo.TechnologyBomStructureVo; import com.ruoyi.technology.mapper.TechnologyBomMapper; import com.ruoyi.technology.mapper.TechnologyBomStructureMapper; import com.ruoyi.technology.mapper.TechnologyOperationMapper; import com.ruoyi.technology.pojo.TechnologyBom; import com.ruoyi.technology.pojo.TechnologyBomStructure; import com.ruoyi.technology.service.TechnologyBomStructureService; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; @Service @RequiredArgsConstructor public class TechnologyBomStructureServiceImpl extends ServiceImpl implements TechnologyBomStructureService { private final TechnologyBomStructureMapper technologyBomStructureMapper; private final TechnologyBomMapper technologyBomMapper; private final ProductModelMapper productModelMapper; private final TechnologyOperationMapper technologyOperationMapper; /** * 全量同步 BOM 的扁平物料明细:一行只表示“某个物料规格在某道工序被消耗”。 * 不再有父子层级、单位用量、需求数量和单位配置。 */ @Override @Transactional(rollbackFor = Exception.class) public Boolean addTechnologyBomStructure(TechnologyBomStructureDto dto) { Long bomId = dto.getBomId(); if (bomId == null) { throw new ServiceException("BOM ID不能为空"); } TechnologyBom bom = technologyBomMapper.selectById(bomId); if (bom == null) { throw new ServiceException("BOM不存在"); } List detailList = dto.getDetailList() == null ? Collections.emptyList() : dto.getDetailList(); List dbList = this.list(new LambdaQueryWrapper() .eq(TechnologyBomStructure::getBomId, bomId)); Set dbIds = new HashSet<>(); for (TechnologyBomStructure dbItem : dbList) { dbIds.add(dbItem.getId()); } Set submittedIds = new HashSet<>(); Set relationKeys = new HashSet<>(); List insertList = new ArrayList<>(); List updateList = new ArrayList<>(); for (TechnologyBomStructureDto item : detailList) { Long productModelId = item.getProductModelId(); Long operationId = item.getOperationId(); validateMaterial(bom, productModelId); validateOperation(operationId); if (!relationKeys.add(productModelId + "#" + operationId)) { throw new ServiceException("同一物料规格在同一工序下不能重复配置"); } TechnologyBomStructure entity = new TechnologyBomStructure(); entity.setBomId(bomId); entity.setProductModelId(productModelId); entity.setOperationId(operationId); // 扁平 BOM 不再使用层级和数量语义,历史列统一置空。 entity.setParentId(null); entity.setUnitQuantity(null); entity.setDemandedQuantity(null); entity.setUnit(null); if (item.getId() == null) { insertList.add(entity); } else { if (!dbIds.contains(item.getId())) { throw new ServiceException("BOM物料明细不存在或不属于当前BOM"); } entity.setId(item.getId()); submittedIds.add(item.getId()); updateList.add(entity); } } Set 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); } return true; } /** * 根据BOM查询扁平物料明细,按稳定顺序返回。 */ @Override public List listByBomId(Long bomId) { if (bomId == null) { return new ArrayList<>(); } return technologyBomStructureMapper.listByBomId(bomId); } /** * 校验物料规格:必须存在,且不能是 BOM 自身的成品规格。 */ private void validateMaterial(TechnologyBom bom, Long productModelId) { if (productModelId == null) { throw new ServiceException("请选择物料规格"); } ProductModel productModel = productModelMapper.selectById(productModelId); if (productModel == null) { throw new ServiceException("物料规格不存在"); } if (productModelId.equals(bom.getProductModelId())) { throw new ServiceException("成品规格不能作为自身的物料"); } } /** * 校验消耗工序必填且存在。 */ private void validateOperation(Long operationId) { if (operationId == null) { throw new ServiceException("请选择消耗工序"); } if (technologyOperationMapper.selectById(operationId) == null) { throw new ServiceException("消耗工序不存在"); } } }