| 2026-03-17 | gongchunyi | ![]() |
| 2026-03-17 | gongchunyi | ![]() |
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/framework/util/AliDingUtils.java
@@ -152,22 +152,15 @@ LambdaQueryWrapper<T> queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.orderByDesc(timeField).last("LIMIT 1"); T lastRecord = service.getOne(queryWrapper); if (lastRecord == null) { return null; } try { Method writeReplace = timeField.getClass().getDeclaredMethod("writeReplace"); writeReplace.setAccessible(true); SerializedLambda lambda = (SerializedLambda) writeReplace.invoke(timeField); // è·åæ¹æ³å String methodName = lambda.getImplMethodName(); // è½¬åæ®µå String fieldName = methodName.substring(3, 4).toLowerCase() + methodName.substring(4); Field field = lastRecord.getClass().getDeclaredField(fieldName); field.setAccessible(true); return (LocalDateTime) field.get(lastRecord); } catch (Exception e) { throw new RuntimeException("è·åæå忥æ¶é´å¤±è´¥", e); Object value = timeField.apply(lastRecord); if (value instanceof LocalDateTime) { return (LocalDateTime) value; } return null; } } 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/java/com/ruoyi/production/service/impl/ProductMaterialServiceImpl.java
@@ -82,8 +82,6 @@ } try { JSONArray searchConditions = new JSONArray(); JSONObject statusCondition = new JSONObject(); statusCondition.put("key", "processInstanceStatus"); @@ -148,6 +146,9 @@ material.setRemark(formData.getString("textareaField_l92f36f9")); String materialType = formData.getString("selectField_l92f36fb"); if ("æ¿æ".equals(materialType) || "ç å".equals(materialType) || "æ ç ".equals(materialType)) { materialType = "æå"; } String inventoryCat = formData.getString("selectField_la154noy"); material.setMaterialTypeId(getOrCreateConfigId(materialType, MaterialConfigTypeEnum.MATERIAL_TYPE.name())); material.setInventoryCategoryId(getOrCreateConfigId(inventoryCat, MaterialConfigTypeEnum.INVENTORY_CAT.name())); @@ -224,10 +225,11 @@ if (list == null || list.isEmpty()) { return 0; } int affected = 0; List<ProductMaterialSku> toSave = new ArrayList<>(); List<ProductMaterialSku> toUpdate = new ArrayList<>(); for (ProductMaterialSku sku : list) { LambdaQueryWrapper<ProductMaterialSku> wrapper = new LambdaQueryWrapper<>(); wrapper.eq(ProductMaterialSku::getProductId, sku.getProductId()) .eq(ProductMaterialSku::getModel, sku.getModel()); @@ -240,20 +242,28 @@ ProductMaterialSku exist = productMaterialSkuService.getOne(wrapper); if (exist == null) { productMaterialSkuService.save(sku); affected++; log.info("æ°å¢ç©æè§æ ¼ {}", sku.getModel()); toSave.add(sku); } else { if (exist.getFormModifiedTime() == null || !exist.getFormModifiedTime().equals(sku.getFormModifiedTime())) { sku.setId(exist.getId()); sku.setCreateTime(exist.getCreateTime()); productMaterialSkuService.updateById(sku); affected++; log.info("æ´æ°ç©æè§æ ¼ {}", sku.getModel()); toUpdate.add(sku); } } } int affected = 0; if (!toSave.isEmpty()) { productMaterialSkuService.saveBatch(toSave); affected += toSave.size(); log.info("æ¹éæ°å¢ç©æè§æ ¼ {} æ¡", toSave.size()); } if (!toUpdate.isEmpty()) { productMaterialSkuService.updateBatchById(toUpdate); affected += toUpdate.size(); log.info("æ¹éæ´æ°ç©æè§æ ¼ {} æ¡", toUpdate.size()); } return affected; } src/main/java/com/ruoyi/production/task/ProductMaterialTask.java
@@ -20,7 +20,7 @@ @Autowired private ProductMaterialService productMaterialService; @Scheduled(cron = "0 0 * * * ?") @Scheduled(cron = "0 0 0 * * ?") public void syncProdDataJob() { productMaterialService.syncProductMaterialJob(); } src/main/java/com/ruoyi/productionPlan/task/ProductionPlanTask.java
@@ -23,7 +23,7 @@ @Autowired private ProductionPlanService productionPlanService; @Scheduled(cron = "0 0 * * * ?") @Scheduled(cron = "0 0 0 * * ?") public void syncProdDataJob() { productionPlanService.syncProdDataJob(); } 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}