gongchunyi
8 小时以前 bc116c55a44bc344e5575b5bdd4c2591bc0475aa
fix: BOM模块的导入、模板下载、新增、编辑修改
已修改10个文件
228 ■■■■■ 文件已修改
doc/宁夏-中盛建材.sql 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/controller/ProductBomController.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/dto/BomImportDto.java 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/dto/ProductBomDto.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/mapper/ProductBomMapper.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/pojo/ProductBom.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/ProductBomService.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/impl/ProductBomServiceImpl.java 122 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/production/ProductBomMapper.xml 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/production/ProductStructureMapper.xml 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
doc/ÄþÏÄ-ÖÐÊ¢½¨²Ä.sql
@@ -268,3 +268,8 @@
ADD COLUMN `sample_state` varchar(255) NULL COMMENT '试样状态' AFTER `sample_code`,
ADD COLUMN `sample_time` date NULL COMMENT '取样日期' AFTER `sample_state`,
ADD COLUMN `license_plate_number` varchar(255) NULL COMMENT '车牌号' AFTER `sample_time`;
ALTER TABLE `product_bom`
    ADD COLUMN `dict_code` bigint NOT NULL COMMENT '关联字典数据编码(sys_dict_data.dict_code)';
ALTER TABLE `product_bom` ADD INDEX `idx_dict_code` (`dict_code`);
src/main/java/com/ruoyi/production/controller/ProductBomController.java
@@ -57,7 +57,7 @@
    @GetMapping("/listPage")
    @Log(title = "BOM-分页查询", businessType = BusinessType.OTHER)
    @ApiOperation("BOM-分页查询")
    public AjaxResult listPage(Page page, ProductBomDto productBomDto) {
    public AjaxResult listPage(Page<ProductBom> page, ProductBomDto productBomDto) {
        IPage<ProductBomDto> listPage = productBomService.listPage(page, productBomDto);
        return AjaxResult.success(listPage);
    }
@@ -106,8 +106,8 @@
    @PreAuthorize("@ss.hasPermi('product:bom:import')")
    @Log(title = "根据Excel导入BOM", businessType = BusinessType.IMPORT)
    @ApiOperation("根据Excel导入BOM")
    public AjaxResult uploadBom(@RequestParam("file") MultipartFile file) {
        return productBomService.uploadBom(file);
    public AjaxResult uploadBom(@RequestParam("file") MultipartFile file, @RequestParam("dictCode") Long dictCode) {
        return productBomService.uploadBom(file, dictCode);
    }
    @PostMapping("exportBom")
src/main/java/com/ruoyi/production/dto/BomImportDto.java
@@ -8,23 +8,11 @@
@Data
public class BomImportDto {
    @Excel(name = "父项产品编号")
    @Excel(name = "编号")
    private String code;
    @Excel(name = "父项编号")
    private String parentCode;
    @Excel(name = "父项产品名称")
    private String parentName;
    @Excel(name = "父项产品规格")
    private String parentSpec;
    @Excel(name = "子项产品编号")
    private String childCode;
    @Excel(name = "子项产品名称")
    private String childName;
    @Excel(name = "子项产品规格")
    private String childSpec;
    @Excel(name = "单位用量")
    private BigDecimal unitQty;
@@ -34,4 +22,4 @@
    @Excel(name = "备注")
    private String remark;
}
}
src/main/java/com/ruoyi/production/dto/ProductBomDto.java
@@ -18,4 +18,7 @@
    //产品编码
    private String productCode;
    //  äº§å“ç±»åž‹
    private String dictLabel;
}
src/main/java/com/ruoyi/production/mapper/ProductBomMapper.java
@@ -20,7 +20,7 @@
@Mapper
public interface ProductBomMapper extends BaseMapper<ProductBom> {
    IPage<ProductBomDto> listPage(Page page, @Param("c") ProductBomDto productBomDto);
    IPage<ProductBomDto> listPage(Page<ProductBom> page, @Param("c") ProductBomDto productBomDto);
    ProductBomDto getById(@Param("bomId") Long bomId);
}
src/main/java/com/ruoyi/production/pojo/ProductBom.java
@@ -5,6 +5,7 @@
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.time.LocalDateTime;
@@ -38,9 +39,13 @@
    private String bomNo;
    @ApiModelProperty("产品规格id")
    @TableField(exist = false)
    //宁夏-中盛建材使用新的产品表关联product_material_sku
    private Long productModelId;
    @ApiModelProperty("关联字典数据编码(sys_dict_data.dict_code)")
    private Long dictCode;
    @ApiModelProperty("备注")
    private String remark;
src/main/java/com/ruoyi/production/service/ProductBomService.java
@@ -20,11 +20,11 @@
 */
public interface ProductBomService extends IService<ProductBom> {
    IPage<ProductBomDto> listPage(Page page, ProductBomDto productBomDto);
    IPage<ProductBomDto> listPage(Page<ProductBom> page, ProductBomDto productBomDto);
    AjaxResult add(ProductBom productBom);
    AjaxResult uploadBom(MultipartFile file);
    AjaxResult uploadBom(MultipartFile file, Long dictCode);
    void exportBom(HttpServletResponse response, Integer bomId);
}
src/main/java/com/ruoyi/production/service/impl/ProductBomServiceImpl.java
@@ -4,8 +4,6 @@
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;
@@ -18,6 +16,8 @@
import com.ruoyi.production.pojo.ProductProcess;
import com.ruoyi.production.pojo.ProductStructure;
import com.ruoyi.production.service.*;
import com.ruoyi.project.system.domain.SysDictData;
import com.ruoyi.project.system.mapper.SysDictDataMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -50,44 +50,30 @@
    private ProductProcessService productProcessService;
    @Autowired
    private ProductMaterialService productMaterialService;
    @Autowired
    private ProductMaterialSkuService productMaterialSkuService;
    private SysDictDataMapper sysDictDataMapper;
    @Override
    public IPage<ProductBomDto> listPage(Page page, ProductBomDto productBomDto) {
    public IPage<ProductBomDto> listPage(Page<ProductBom> page, ProductBomDto productBomDto) {
        return productBomMapper.listPage(page, productBomDto);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult add(ProductBom productBom) {
        if (productBom == null || productBom.getDictCode() == null) {
            throw new ServiceException("新增失败,产品类型不能为空");
        }
        SysDictData sysDictData = sysDictDataMapper.selectDictDataById(productBom.getDictCode());
        if (sysDictData == null) {
            throw new ServiceException("新增失败,产品类型不存在");
        }
        boolean save = productBomMapper.insert(productBom) > 0;
        if (save) {
            String no = "BM." + String.format("%05d", productBom.getId());
            productBom.setBomNo(no);
            productBomMapper.updateById(productBom);
            //  æŸ¥è¯¢å‡ºäº§å“æ¨¡åž‹ä¿¡æ¯
            if (productBom.getProductModelId() == null) {
                throw new ServiceException("请选择产品模型");
            }
            ProductMaterialSku productModel = productMaterialSkuService.getById(productBom.getProductModelId());
            if (productModel == null) {
                throw new ServiceException("选择的产品模型不存在");
            }
            ProductMaterial productMaterial = productMaterialService.getById(productModel.getProductId());
            //  æ·»åŠ åˆå§‹çš„äº§å“ç»“æž„
            ProductStructure productStructure = new ProductStructure();
            productStructure.setProductModelId(productBom.getProductModelId());
            productStructure.setUnit(productMaterial != null ? productMaterial.getUnit() : null);
            productStructure.setUnitQuantity(BigDecimal.valueOf(1));
            productStructure.setBomId(productBom.getId());
            productStructureService.save(productStructure);
            return AjaxResult.success();
        }
@@ -96,7 +82,15 @@
    @Override
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult uploadBom(MultipartFile file) {
    public AjaxResult uploadBom(MultipartFile file, Long dictCode) {
        if (dictCode == null) {
            return AjaxResult.error("导入失败,产品类型不能为空");
        }
        SysDictData sysDictData = sysDictDataMapper.selectDictDataById(dictCode);
        if (sysDictData == null) {
            return AjaxResult.error("导入失败,产品类型不存在");
        }
        ExcelUtil<BomImportDto> util = new ExcelUtil<>(BomImportDto.class);
        List<BomImportDto> list;
        try {
@@ -107,77 +101,66 @@
        if (list == null || list.isEmpty()) return AjaxResult.error("数据为空");
        //  å¤„理工序
        //  å¤„理字段清理
        list.forEach(dto -> {
            dto.setParentName(clean(dto.getParentName()));
            dto.setParentSpec(clean(dto.getParentSpec()));
            dto.setChildName(clean(dto.getChildName()));
            dto.setChildSpec(clean(dto.getChildSpec()));
            dto.setParentCode(clean(dto.getParentCode()));
            dto.setCode(clean(dto.getCode()));
        });
        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.setProductModelId(rootModel.getId());
        bom.setVersion("1.0");
        bom.setDictCode(dictCode);
        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 parentKey = dto.getParentName() + "|" + dto.getParentSpec();
            String childKey = dto.getChildName() + "|" + dto.getChildSpec();
            String currentCode = dto.getCode();
            String parentCode = dto.getParentCode();
            //处理根节点,第一行且子项为空
            if (i == 0 && StringUtils.isBlank(dto.getChildName())) {
            // å¤„理根节点:一般指第一行且没有父项编号
            if (i == 0 && StringUtils.isBlank(parentCode)) {
                ProductStructure rootNode = new ProductStructure();
                rootNode.setBomId(bom.getId());
                rootNode.setParentId(null); // é¡¶å±‚没有父节点
                rootNode.setProductModelId(rootModel.getId());
                if (processMap.containsKey(dto.getProcess())) {
                    rootNode.setProcessId(processMap.get(dto.getProcess()));
                }
                rootNode.setUnitQuantity(BigDecimal.ONE);
                ProductMaterial rootMaterial = productMaterialService.getById(rootModel.getProductId());
                rootNode.setUnit(rootMaterial != null ? rootMaterial.getUnit() : null);
                productStructureService.save(rootNode);
                treePathMap.put(parentKey, rootNode.getId());
                treePathMap.put(currentCode, rootNode.getId());
                continue;
            }
            //  å¤„理子层级节点
            //  æ‰¾åˆ°çˆ¶èŠ‚ç‚¹åœ¨æ•°æ®åº“é‡Œçš„ ID
            Long parentStructureId = treePathMap.get(parentKey);
            Long parentStructureId = treePathMap.get(parentCode);
            if (parentStructureId == null) {
                // å¦‚æžœ Map é‡Œæ‰¾ä¸åˆ°ï¼Œè¯´æ˜Ž Excel é¡ºåºä¹±äº†æˆ–者数据有误
                throw new ServiceException("导入失败: çˆ¶é¡¹[" + dto.getParentName() + "]必须在其子项之前定义");
                throw new ServiceException("导入失败: çˆ¶é¡¹[" + parentCode + "]必须在其子项之前定义");
            }
            //  èŽ·å–å­é¡¹æ¨¡åž‹ä¿¡æ¯
            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(childKey, node.getId());
            //  æŠŠå½“前项记录到 Map, ä½œä¸ºä»¥åŽæ›´æ·±å±‚级的父项查找依据
            treePathMap.put(currentCode, node.getId());
        }
        return AjaxResult.success("BOM导入成功");
@@ -206,8 +189,7 @@
        for (ProductStructureDto root : treeData) {
            //  æ·»åŠ æ ¹èŠ‚ç‚¹
            BomImportDto rootRow = new BomImportDto();
            rootRow.setParentName(root.getProductName());
            rootRow.setParentSpec(root.getModel());
            rootRow.setCode(root.getId().toString());
            rootRow.setUnitQty(root.getUnitQuantity());
            rootRow.setRemark("");
            exportList.add(rootRow);
@@ -229,14 +211,14 @@
                }
                BomImportDto row = new BomImportDto();
                // çˆ¶ç±»ä¿¡æ¯
                row.setParentName(parent.getProductName());
                row.setParentSpec(parent.getModel());
                // å­ç±»ä¿¡æ¯
                row.setChildName(child.getProductName());
                row.setChildSpec(child.getModel());
                // çˆ¶ç±»ç¼–号
                row.setParentCode(parent.getId().toString());
                // æœ¬èº«ç¼–号
                row.setCode(child.getId().toString());
                row.setUnitQty(child.getUnitQuantity());
                row.setProcess(child.getProcessName());
//                row.setProcess();
                row.setRemark("");
                exportList.add(row);
@@ -259,18 +241,6 @@
            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) {
src/main/resources/mapper/production/ProductBomMapper.xml
@@ -5,7 +5,7 @@
    <!-- é€šç”¨æŸ¥è¯¢æ˜ å°„结果 -->
    <resultMap id="BaseResultMap" type="com.ruoyi.production.pojo.ProductBom">
        <id column="id" property="id"/>
        <result column="product_model_id" property="productModelId"/>
        <result column="dict_code" property="dictCode"/>
        <result column="bom_no" property="bomNo"/>
        <result column="remark" property="remark"/>
        <result column="version" property="version"/>
@@ -17,35 +17,27 @@
    </resultMap>
    <select id="listPage" resultType="com.ruoyi.production.dto.ProductBomDto">
        SELECT * FROM (
        SELECT
        pb.*,
        pms.model AS productModelName,
        pm.product_name AS productName,
        pms.material_code AS productCode
        FROM product_bom pb
        LEFT JOIN product_material_sku pms ON pb.product_model_id = pms.id
        LEFT JOIN product_material pm ON pms.product_id = pm.id
        ) A
        pm.*,
        sdd.dict_label AS dictLabel
        FROM product_bom pm
        LEFT JOIN sys_dict_data sdd ON sdd.dict_code = pm.dict_code
        WHERE 1=1
        <if test="c.productModelName != null and c.productModelName != ''">
            AND productModelName LIKE CONCAT('%', #{c.productModelName}, '%')
        </if>
        <if test="c.productName != null and c.productName != ''">
            AND productName LIKE CONCAT('%', #{c.productName}, '%')
        <if test="c.dictCode != null">
            AND pm.dict_code = #{c.dictCode}
        </if>
        <if test="c.bomNo != null and c.bomNo != ''">
            AND bom_no = #{c.bomNo}
            AND pm.bom_no = #{c.bomNo}
        </if>
        <if test="c.version != null and c.version != ''">
            AND version = #{c.version}
            AND pm.version = #{c.version}
        </if>
    </select>
    <select id="getById" resultType="com.ruoyi.production.dto.ProductBomDto">
        select pb.*,
               pms.model AS productModelName,
               pm.product_name   AS productName
               pms.model       AS productModelName,
               pm.product_name AS productName
        from product_bom pb
                 left join product_material_sku pms on pb.product_model_id = pms.id
                 left join product_material pm on pms.product_id = pm.id
src/main/resources/mapper/production/ProductStructureMapper.xml
@@ -13,29 +13,22 @@
    </resultMap>
    <select id="listByBomId" resultType="com.ruoyi.production.dto.ProductStructureDto">
        select ps.*,
               pm.product_name as product_name,
               pp.name         as process_name,
               pms.product_id as product_id,
               pms.model       as model,
               pms.material_code       as productCode
        from product_structure ps
                 left join product_material_sku pms on ps.product_model_id = pms.id
                 left join product_material pm on pms.product_id = pm.id
                 left join product_process pp on ps.process_id = pp.id
        where ps.bom_id = #{bomId}
        order by ps.id
        SELECT ps.id,
               ps.parent_id,
               ps.bom_id,
               ps.unit_quantity,
               ps.process_id,
               pp.name AS process_name
        FROM product_structure ps
                 LEFT JOIN product_process pp ON ps.process_id = pp.id
        WHERE ps.bom_id = #{bomId}
        ORDER BY ps.id
    </select>
    <select id="listByBomAndProcess" resultType="com.ruoyi.production.dto.ProductStructureDto">
        select ps.*,
               pm.product_name as product_name,
               pp.name         as process_name,
               pms.product_id as product_id,
               pms.model       as model
               pp.name as process_name
        from product_structure ps
                 left join product_material_sku pms on ps.product_model_id = pms.id
                 left join product_material pm on pms.product_id = pm.id
                 left join product_process pp on ps.process_id = pp.id
        where ps.bom_id = #{bomId}
          and ps.process_id = #{processId}