gongchunyi
9 小时以前 19effb2444eaf331f61fd0d43d3735f8d1baf768
fix: BOM的子集取消第一层限制
已修改5个文件
144 ■■■■ 文件已修改
src/main/java/com/ruoyi/production/controller/ProductBomController.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/dto/BomImportDto.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/impl/ProductBomServiceImpl.java 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/impl/ProductStructureServiceImpl.java 19 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/production/ProductStructureMapper.xml 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/controller/ProductBomController.java
@@ -1,5 +1,6 @@
package com.ruoyi.production.controller;
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;
@@ -101,6 +102,14 @@
        return AjaxResult.success(productBoms);
    }
    @GetMapping("/getDictCode")
    @Log(title = "BOM-根据选择的产品型号查询存在的bom", businessType = BusinessType.OTHER)
    @ApiOperation("BOM-根据选择的产品型号查询存在的bom")
    public AjaxResult getDictCode(Long dictCode) {
        List<ProductBom> bomList = productBomService.list(new LambdaQueryWrapper<ProductBom>().eq(ProductBom::getDictCode, dictCode));
        return AjaxResult.success(bomList);
    }
    @PostMapping("uploadBom")
    @PreAuthorize("@ss.hasPermi('product:bom:import')")
src/main/java/com/ruoyi/production/dto/BomImportDto.java
@@ -8,11 +8,17 @@
@Data
public class BomImportDto {
    @Excel(name = "编号")
    private String code;
    @Excel(name = "父项产品名称")
    private String parentName;
    @Excel(name = "父项编号")
    private String parentCode;
    @Excel(name = "父项产品规格")
    private String parentSpec;
    @Excel(name = "子项产品名称")
    private String childName;
    @Excel(name = "子项产品规格")
    private String childSpec;
    @Excel(name = "单位用量")
    private BigDecimal unitQty;
@@ -22,4 +28,4 @@
    @Excel(name = "备注")
    private String remark;
}
}
src/main/java/com/ruoyi/production/service/impl/ProductBomServiceImpl.java
@@ -4,6 +4,8 @@
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.production.pojo.ProductMaterial;
import com.ruoyi.production.pojo.ProductMaterialSku;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
@@ -42,12 +44,17 @@
    @Autowired
    private ProductBomMapper productBomMapper;
    @Autowired
    private ProductStructureService productStructureService;
    @Autowired
    private ProductProcessService productProcessService;
    @Autowired
    private ProductMaterialService productMaterialService;
    @Autowired
    private ProductMaterialSkuService productMaterialSkuService;
    @Autowired
    private SysDictDataMapper sysDictDataMapper;
@@ -61,12 +68,12 @@
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult add(ProductBom productBom) {
        if (productBom == null || productBom.getDictCode() == null) {
            throw new ServiceException("新增失败,产品类型不能为空");
            throw new ServiceException("新增BOM失败,产品类型不能为空");
        }
        SysDictData sysDictData = sysDictDataMapper.selectDictDataById(productBom.getDictCode());
        if (sysDictData == null) {
            throw new ServiceException("新增失败,产品类型不存在");
            throw new ServiceException("新增BOM失败,产品类型不存在");
        }
        boolean save = productBomMapper.insert(productBom) > 0;
@@ -90,7 +97,6 @@
        if (sysDictData == null) {
            return AjaxResult.error("导入失败,产品类型不存在");
        }
        ExcelUtil<BomImportDto> util = new ExcelUtil<>(BomImportDto.class);
        List<BomImportDto> list;
        try {
@@ -99,68 +105,81 @@
            return AjaxResult.error("Excel解析失败");
        }
        if (list == null || list.isEmpty()) return AjaxResult.error("数据为空");
        if (list == null || list.isEmpty()) {
            return AjaxResult.error("数据为空");
        }
        //  处理字段清理
        //  处理工序
        list.forEach(dto -> {
            dto.setParentCode(clean(dto.getParentCode()));
            dto.setCode(clean(dto.getCode()));
            dto.setParentName(clean(dto.getParentName()));
            dto.setParentSpec(clean(dto.getParentSpec()));
            dto.setChildName(clean(dto.getChildName()));
            dto.setChildSpec(clean(dto.getChildSpec()));
        });
        handleProcess(list);
        Map<String, Long> processMap = productProcessService.list().stream()
                .collect(Collectors.toMap(ProductProcess::getName, ProductProcess::getId, (k1, k2) -> k1));
        //  创建 BOM 数据
        BomImportDto first = list.get(0);
        ProductMaterialSku rootModel = findModel(first.getParentName(), first.getParentSpec());
        ProductBom bom = new ProductBom();
        bom.setVersion("1.0");
        bom.setDictCode(dictCode);
        bom.setVersion("1.0");
        productBomMapper.insert(bom);
        bom.setBomNo("BM." + String.format("%05d", bom.getId()));
        productBomMapper.updateById(bom);
        // 记录已经插入结构的节点:Key = "编码", Value = structure_id
        // 记录已经插入结构的节点:Key = "名称+规格", Value = structure_id
        Map<String, Long> treePathMap = new HashMap<>();
        for (int i = 0; i < list.size(); i++) {
            BomImportDto dto = list.get(i);
            String currentCode = dto.getCode();
            String parentCode = dto.getParentCode();
            String parentKey = dto.getParentName() + "|" + dto.getParentSpec();
            String childKey = dto.getChildName() + "|" + dto.getChildSpec();
            // 处理根节点:一般指第一行且没有父项编号
            if (i == 0 && StringUtils.isBlank(parentCode)) {
            //处理根节点,第一行且子项为空
            if (i == 0 && StringUtils.isBlank(dto.getChildName())) {
                ProductStructure rootNode = new ProductStructure();
                rootNode.setBomId(bom.getId());
                rootNode.setParentId(null); // 顶层没有父节点
                if (processMap.containsKey(dto.getProcess())) {
                    rootNode.setProcessId(processMap.get(dto.getProcess()));
                }
                rootNode.setProductModelId(rootModel.getId());
                rootNode.setUnitQuantity(BigDecimal.ONE);
                ProductMaterial rootMaterial = productMaterialService.getById(rootModel.getProductId());
                rootNode.setUnit(rootMaterial != null ? rootMaterial.getUnit() : null);
                productStructureService.save(rootNode);
                treePathMap.put(currentCode, rootNode.getId());
                treePathMap.put(parentKey, rootNode.getId());
                continue;
            }
            //  处理子层级节点
            //  找到父节点在数据库里的 ID
            Long parentStructureId = treePathMap.get(parentCode);
            Long parentStructureId = treePathMap.get(parentKey);
            if (parentStructureId == null) {
                // 如果 Map 里找不到,说明 Excel 顺序乱了或者数据有误
                throw new ServiceException("导入失败: 父项[" + parentCode + "]必须在其子项之前定义");
                throw new ServiceException("导入失败: 父项[" + dto.getParentName() + "]必须在其子项之前定义");
            }
            //  获取子项模型信息
            ProductMaterialSku childModel = findModel(dto.getChildName(), dto.getChildSpec());
            //  插入结构表
            ProductStructure node = new ProductStructure();
            node.setBomId(bom.getId());
            node.setParentId(parentStructureId); // 父节点ID
            node.setProductModelId(childModel.getId());
            node.setUnitQuantity(dto.getUnitQty());
            ProductMaterial childMaterial = productMaterialService.getById(childModel.getProductId());
            node.setUnit(childMaterial != null ? childMaterial.getUnit() : null);
            if (processMap.containsKey(dto.getProcess())) {
                node.setProcessId(processMap.get(dto.getProcess()));
            }
            productStructureService.save(node);
            //  把当前项记录到 Map, 作为以后更深层级的父项查找依据
            treePathMap.put(currentCode, node.getId());
            //  把当前子项记录到 Map,作为以后更深层级的父项查找依据
            //  同一父项下的同名子项不需要重复记录
            treePathMap.put(childKey, node.getId());
        }
        return AjaxResult.success("BOM导入成功");
@@ -189,7 +208,8 @@
        for (ProductStructureDto root : treeData) {
            //  添加根节点
            BomImportDto rootRow = new BomImportDto();
            rootRow.setCode(root.getId().toString());
            rootRow.setParentName(root.getProductName());
            rootRow.setParentSpec(root.getModel());
            rootRow.setUnitQty(root.getUnitQuantity());
            rootRow.setRemark("");
            exportList.add(rootRow);
@@ -211,14 +231,14 @@
                }
                BomImportDto row = new BomImportDto();
                // 父类编号
                row.setParentCode(parent.getId().toString());
                // 本身编号
                row.setCode(child.getId().toString());
                // 父类信息
                row.setParentName(parent.getProductName());
                row.setParentSpec(parent.getModel());
                // 子类信息
                row.setChildName(child.getProductName());
                row.setChildSpec(child.getModel());
                row.setUnitQty(child.getUnitQuantity());
                row.setProcess(child.getProcessName());
//                row.setProcess();
                row.setRemark("");
                exportList.add(row);
@@ -241,6 +261,18 @@
            map.put(node.getId(), node);
            populateMap(node.getChildren(), map);
        }
    }
    private ProductMaterialSku findModel(String name, String spec) {
        ProductMaterial product = productMaterialService.getOne(new LambdaQueryWrapper<ProductMaterial>()
                .eq(ProductMaterial::getProductName, name).last("limit 1"));
        if (product == null) throw new ServiceException("产品未维护:" + name);
        ProductMaterialSku model = productMaterialSkuService.getOne(new LambdaQueryWrapper<ProductMaterialSku>()
                .eq(ProductMaterialSku::getProductId, product.getId())
                .eq(ProductMaterialSku::getModel, spec).last("limit 1"));
        if (model == null) throw new ServiceException("规格未维护:" + name + "[" + spec + "]");
        return model;
    }
    private void handleProcess(List<BomImportDto> list) {
@@ -283,5 +315,3 @@
        return s.replaceAll("[\\u00A0\\u3000]", "").trim();
    }
}
src/main/java/com/ruoyi/production/service/impl/ProductStructureServiceImpl.java
@@ -86,17 +86,27 @@
        //  真实的父节点 ID
        Long realParentId;
        for (ProductStructureDto psDto : flatDtoList) {
            if (psDto.getId() == null && psDto.getParentTempId() != null) {
            String parentTempId = psDto.getParentTempId();
            if (psDto.getId() == null && parentTempId != null && !parentTempId.isEmpty()) {
                ProductStructure child = tempEntityMap.get(psDto.getTempId());
                if (tempEntityMap.containsKey(psDto.getParentTempId())) {
                if (tempEntityMap.containsKey(parentTempId)) {
                    // 父节点是新节点
                    realParentId = tempEntityMap.get(psDto.getParentTempId()).getId();
                    realParentId = tempEntityMap.get(parentTempId).getId();
                } else {
                    // 父节点是老节点
                    realParentId = Long.valueOf(psDto.getParentTempId());
                    try {
                        realParentId = Long.valueOf(parentTempId);
                    } catch (NumberFormatException e) {
                        realParentId = 0L;
                    }
                }
                child.setParentId(realParentId);
                parentFixList.add(child);
            } else if (psDto.getId() == null) {
                // 如果 parentTempId 为空,则是顶级节点
                ProductStructure child = tempEntityMap.get(psDto.getTempId());
                child.setParentId(0L);
                parentFixList.add(child);
            }
        }
@@ -104,7 +114,6 @@
        if (!parentFixList.isEmpty()) {
            this.updateBatchById(parentFixList);
        }
        if (!updateList.isEmpty()) {
            this.updateBatchById(updateList);
        }
src/main/resources/mapper/production/ProductStructureMapper.xml
@@ -18,9 +18,15 @@
               ps.bom_id,
               ps.unit_quantity,
               ps.process_id,
               pp.name AS process_name
               pp.name         AS process_name,
               pm.product_name AS productName,
               pm.unit,
               pms.model
        FROM product_structure ps
                 LEFT JOIN product_process pp ON ps.process_id = pp.id
                 LEFT JOIN product_material_sku pms ON pms.id = ps.product_model_id
                 LEFT JOIN product_material pm ON pm.id = pms.product_id
        WHERE ps.bom_id = #{bomId}
        ORDER BY ps.id
    </select>