doc/ÄþÏÄ-ÖÐÊ¢½¨²Ä.sql
@@ -111,42 +111,41 @@ CREATE TABLE `product_material` ( `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT '主é®ID', `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主é®ID', `tenant_id` BIGINT DEFAULT NULL COMMENT 'ç§æ·ID', `material_type_id` INT DEFAULT NULL COMMENT 'å ³èç©æç±»åID', `inventory_category_id` INT DEFAULT NULL COMMENT 'å ³èåè´§ç±»å«ID', `identifier_code` VARCHAR(100) DEFAULT NULL COMMENT 'æ è¯ç¼ç ', `material_code` VARCHAR(100) DEFAULT NULL COMMENT 'ç©æä»£ç ', `product_name` VARCHAR(255) DEFAULT NULL COMMENT '产ååç§°', `material_name` VARCHAR(255) DEFAULT NULL COMMENT 'ç©æåå', `specification` VARCHAR(255) DEFAULT NULL COMMENT 'è§æ ¼åå·', `material_type_id` INT DEFAULT NULL COMMENT 'ç©æç±»åID', `inventory_category_id` INT DEFAULT NULL COMMENT 'åè´§ç±»å«ID', `material_name` VARCHAR(255) NOT NULL COMMENT 'ç©æåç§°', `base_unit` VARCHAR(50) DEFAULT NULL COMMENT 'åºæ¬åä½', `material_attribute` VARCHAR(100) DEFAULT NULL COMMENT 'ç©æå±æ§', `finished_product_name` VARCHAR(100) DEFAULT NULL COMMENT 'æååå', `originator_name` VARCHAR(100) DEFAULT NULL COMMENT 'æäº¤äººå§å', `originator_org` VARCHAR(255) DEFAULT 'å®å¤ä¸å绿è½å®ä¸é墿éå ¬å¸', `remark` TEXT COMMENT '夿³¨', `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP, `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, INDEX `idx_type_id` (`material_type_id`), INDEX `idx_cat_id` (`inventory_category_id`) PRIMARY KEY (`id`), KEY `idx_type_id` (`material_type_id`), KEY `idx_cat_id` (`inventory_category_id`), UNIQUE KEY `uk_material_name` (`material_name`) ) ENGINE = INNODB DEFAULT CHARSET = utf8mb4 COMMENT = 'ç©æä¿¡æ¯è¡¨'; ALTER TABLE product_material ADD COLUMN form_instance_id VARCHAR(100) DEFAULT NULL COMMENT 'å®æè¡¨åå®ä¾ID', ADD COLUMN form_modified_time DATETIME DEFAULT NULL COMMENT 'å®æä¿®æ¹æ¶é´'; CREATE TABLE `product_material_config` DEFAULT CHARSET = utf8mb4 COMMENT = 'ç©æä¸»è¡¨'; CREATE TABLE `product_material_sku` ( `id` int NOT NULL AUTO_INCREMENT PRIMARY KEY, `config_type` varchar(50) NOT NULL COMMENT 'åºåç±»å: MATERIAL_TYPE æ INVENTORY_CAT', `config_name` varchar(100) NOT NULL COMMENT 'æ¾ç¤ºçåç§°' ) ENGINE = InnoDB COMMENT ='ç©æä¿¡æ¯è¡¨é 置表'; `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主é®ID', `material_id` BIGINT NOT NULL COMMENT 'ç©æID', `identifier_code` VARCHAR(100) DEFAULT NULL COMMENT 'æ è¯ç¼ç ', `material_code` VARCHAR(100) DEFAULT NULL COMMENT 'ç©æç¼ç ', `specification` VARCHAR(255) DEFAULT NULL COMMENT 'è§æ ¼åå·', `supply_type` VARCHAR(20) DEFAULT NULL COMMENT 'ä¾åºæ¹å¼', `originator_name` VARCHAR(100) DEFAULT NULL COMMENT 'æäº¤äººå§å', `originator_org` VARCHAR(255) DEFAULT 'å®å¤ä¸å绿è½å®ä¸é墿éå ¬å¸', `form_instance_id` VARCHAR(100) DEFAULT NULL COMMENT 'å®æè¡¨åå®ä¾ID', `form_modified_time` DATETIME DEFAULT NULL COMMENT 'å®æä¿®æ¹æ¶é´', `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP, `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_material_id` (`material_id`), UNIQUE KEY `uk_material_spec` (`material_id`, `specification`), CONSTRAINT `fk_material` FOREIGN KEY (`material_id`) REFERENCES `product_material` (`id`) ON DELETE CASCADE ) ENGINE = INNODB DEFAULT CHARSET = utf8mb4 COMMENT = 'ç©æè§æ ¼è¡¨'; ALTER TABLE `production_plan` ADD COLUMN `product_material_id` int DEFAULT NULL COMMENT 'å ³èç©æä¿¡æ¯è¡¨ID' AFTER `material_code`; -- 建议顺便å ä¸ç´¢å¼ï¼æåå ³èæ¥è¯¢é度 ALTER TABLE `production_plan` ADD INDEX `idx_product_material_id` (`product_material_id`); ALTER TABLE product_material_sku DROP FOREIGN KEY fk_material; src/main/java/com/ruoyi/production/controller/ProductMaterialController.java
@@ -3,12 +3,20 @@ import com.ruoyi.framework.aspectj.lang.annotation.Log; import com.ruoyi.framework.aspectj.lang.enums.BusinessType; import com.ruoyi.framework.web.domain.AjaxResult; import com.ruoyi.production.pojo.ProductMaterial; import com.ruoyi.production.service.ProductMaterialService; import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.List; import java.util.Map; /** * <br> @@ -34,4 +42,38 @@ return AjaxResult.success(); } @GetMapping("/list") @ApiOperation("ç©ææ°æ®") @Log(title = "ç©ææ°æ®", businessType = BusinessType.OTHER) public AjaxResult productMaterialList(String materialName) { Map<String, List<ProductMaterial>> productMaterialMap = productMaterialService.ProductMaterialList(materialName); return AjaxResult.success(productMaterialMap); } @PostMapping("/add") @ApiOperation("æ°å¢ç©æ") @Log(title = "æ°å¢ç©æ", businessType = BusinessType.INSERT) public AjaxResult addProductMaterial(@RequestBody ProductMaterial productMaterial) { productMaterialService.addProductMaterial(productMaterial); return AjaxResult.success(); } @PutMapping("/update") @ApiOperation("ä¿®æ¹ç©æ") @Log(title = "ä¿®æ¹ç©æ", businessType = BusinessType.UPDATE) public AjaxResult updateProductMaterial(@RequestBody ProductMaterial productMaterial) { productMaterialService.updateProductMaterial(productMaterial); return AjaxResult.success(); } @DeleteMapping("/delete") @ApiOperation("å é¤ç©æ") @Log(title = "å é¤ç©æ", businessType = BusinessType.DELETE) public AjaxResult deleteProductMaterial(@RequestBody List<Long> ids) { productMaterialService.deleteProductMaterial(ids); return AjaxResult.success(); } } src/main/java/com/ruoyi/production/controller/ProductMaterialSkuController.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,69 @@ package com.ruoyi.production.controller; import com.ruoyi.framework.aspectj.lang.annotation.Log; import com.ruoyi.framework.aspectj.lang.enums.BusinessType; import com.ruoyi.framework.web.domain.AjaxResult; import com.ruoyi.production.dto.ProductMaterialSkuDto; import com.ruoyi.production.pojo.ProductMaterialSku; import com.ruoyi.production.service.ProductMaterialSkuService; import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.util.List; /** * <br> * ç©æè§æ ¼æ§å¶å± * </br> * * @author deslrey * @version 1.0 * @since 2026/03/12 10:43 */ @RestController @RequestMapping("/productMaterialSku") public class ProductMaterialSkuController { @Autowired private ProductMaterialSkuService productMaterialSkuService; @GetMapping("/list") @ApiOperation("ç©æè§æ ¼æ°æ®éå") @Log(title = "ç©æè§æ ¼æ°æ®éå", businessType = BusinessType.OTHER) public AjaxResult productMaterialSkuList(@RequestParam("materialId") Long materialId) { List<ProductMaterialSkuDto> list = productMaterialSkuService.productMaterialSkuList(materialId); return AjaxResult.success(list); } @PostMapping("/add") @ApiOperation("æ°å¢ç©æè§æ ¼") @Log(title = "æ°å¢ç©æè§æ ¼", businessType = BusinessType.INSERT) public AjaxResult addProductMaterialSku(@RequestBody ProductMaterialSku productMaterialSku) { productMaterialSkuService.addProductMaterialSku(productMaterialSku); return AjaxResult.success(); } @PutMapping("/update") @ApiOperation("ä¿®æ¹ç©æè§æ ¼") @Log(title = "ä¿®æ¹ç©æè§æ ¼", businessType = BusinessType.UPDATE) public AjaxResult updateProductMaterialSku(@RequestBody ProductMaterialSku productMaterialSku) { productMaterialSkuService.updateProductMaterialSku(productMaterialSku); return AjaxResult.success(); } @DeleteMapping("/delete") @ApiOperation("å é¤ç©æè§æ ¼") @Log(title = "å é¤ç©æè§æ ¼", businessType = BusinessType.DELETE) public AjaxResult deleteProductMaterialSku(@RequestBody List<Long> ids) { productMaterialSkuService.deleteProductMaterialSku(ids); return AjaxResult.success(); } } src/main/java/com/ruoyi/production/dto/ProductMaterialSkuDto.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,38 @@ package com.ruoyi.production.dto; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; /** * <br> * ç©æ + è§æ ¼ DTO * </br> * * @author deslrey * @version 1.0 * @since 2026/03/12 10:50 */ @Data @ApiModel(value = "ProductMaterialSkuDto", description = "ç©æSKUä¿¡æ¯") public class ProductMaterialSkuDto { @ApiModelProperty("ç©æID") private Long materialId; @ApiModelProperty("ç©æåç§°") private String materialName; @ApiModelProperty("åä½") private String baseUnit; @ApiModelProperty("è§æ ¼ID") private Long skuId; @ApiModelProperty("è§æ ¼åå·") private String specification; @ApiModelProperty("ä¾åºæ¹å¼") private String supplyType; } src/main/java/com/ruoyi/production/mapper/ProductMaterialSkuMapper.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,16 @@ package com.ruoyi.production.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.ruoyi.production.pojo.ProductMaterialSku; /** * <br> * ç©æè§æ ¼Mapper * </br> * * @author deslrey * @version 1.0 * @since 2026/03/12 10:04 */ public interface ProductMaterialSkuMapper extends BaseMapper<ProductMaterialSku> { } src/main/java/com/ruoyi/production/pojo/ProductMaterial.java
@@ -21,12 +21,12 @@ */ @Data @TableName("product_material") @ApiModel(value = "ProductMaterial", description = "产åç©æä¿¡æ¯è¡¨") @ApiModel(value = "ProductMaterial", description = "ç©æä¸»è¡¨") public class ProductMaterial { @TableId(type = IdType.AUTO) @ApiModelProperty("主é®ID") private Integer id; private Long id; @ApiModelProperty("ç§æ·ID") private Long tenantId; @@ -37,32 +37,11 @@ @ApiModelProperty("åè´§ç±»å«ID") private Integer inventoryCategoryId; @ApiModelProperty("æ è¯ç¼ç ") private String identifierCode; @ApiModelProperty("ç©æä»£ç ") private String materialCode; @ApiModelProperty("ç©æåå") @ApiModelProperty("ç©æåç§°") private String materialName; @ApiModelProperty("è§æ ¼åå·") private String specification; @ApiModelProperty("åºæ¬åä½") private String baseUnit; @ApiModelProperty("ç©æå±æ§") private String materialAttribute; @ApiModelProperty("æååå") private String finishedProductName; @ApiModelProperty("æäº¤äººå§å") private String originatorName; @ApiModelProperty("æäº¤äººç»ç»") private String originatorOrg; @ApiModelProperty("夿³¨") private String remark; @@ -74,11 +53,4 @@ @ApiModelProperty("ä¿®æ¹æ¶é´") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime updateTime; @ApiModelProperty("å®æè¡¨åå®ä¾ID") private String formInstanceId; @ApiModelProperty("å®æä¿®æ¹æ¶é´") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime formModifiedTime; } src/main/java/com/ruoyi/production/pojo/ProductMaterialSku.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,66 @@ package com.ruoyi.production.pojo; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.fasterxml.jackson.annotation.JsonFormat; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import java.time.LocalDateTime; /** * <br> * ç©æè§æ ¼è¡¨ * </br> * * @author deslrey * @version 1.0 * @since 2026/03/12 10:02 */ @Data @TableName("product_material_sku") @ApiModel(value = "ProductMaterialSku", description = "ç©æè§æ ¼è¡¨") public class ProductMaterialSku { @TableId(type = IdType.AUTO) @ApiModelProperty("主é®ID") private Long id; @ApiModelProperty("ç©æID") private Long materialId; @ApiModelProperty("æ è¯ç¼ç ") private String identifierCode; @ApiModelProperty("ç©æç¼ç ") private String materialCode; @ApiModelProperty("è§æ ¼åå·") private String specification; @ApiModelProperty("ä¾åºæ¹å¼ï¼èªå¶ï¼å¤è´ï¼") private String supplyType; @ApiModelProperty("æäº¤äººå§å") private String originatorName; @ApiModelProperty("æäº¤äººç»ç»") private String originatorOrg; @ApiModelProperty("å®æè¡¨åå®ä¾ID") private String formInstanceId; @ApiModelProperty("å®æä¿®æ¹æ¶é´") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime formModifiedTime; @ApiModelProperty("å建æ¶é´") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime createTime; @ApiModelProperty("ä¿®æ¹æ¶é´") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime updateTime; } src/main/java/com/ruoyi/production/service/ProductMaterialService.java
@@ -3,6 +3,9 @@ import com.baomidou.mybatisplus.extension.service.IService; import com.ruoyi.production.pojo.ProductMaterial; import java.util.List; import java.util.Map; /** * <br> * 产åç©æä¿¡æ¯æ¥å£ @@ -17,4 +20,12 @@ void loadProductMaterialData(); void syncProductMaterialJob(); Map<String, List<ProductMaterial>> ProductMaterialList(String materialName); void addProductMaterial(ProductMaterial productMaterial); void updateProductMaterial(ProductMaterial productMaterial); void deleteProductMaterial(List<Long> ids); } src/main/java/com/ruoyi/production/service/ProductMaterialSkuService.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,26 @@ package com.ruoyi.production.service; import com.baomidou.mybatisplus.extension.service.IService; import com.ruoyi.production.dto.ProductMaterialSkuDto; import com.ruoyi.production.pojo.ProductMaterialSku; import java.util.List; /** * <br> * ç©æè§æ ¼æ¥å£ * </br> * * @author deslrey * @version 1.0 * @since 2026/03/12 10:04 */ public interface ProductMaterialSkuService extends IService<ProductMaterialSku> { List<ProductMaterialSkuDto> productMaterialSkuList(Long materialId); void addProductMaterialSku(ProductMaterialSku productMaterialSku); void updateProductMaterialSku(ProductMaterialSku productMaterialSku); void deleteProductMaterialSku(List<Long> ids); } src/main/java/com/ruoyi/production/service/impl/ProductMaterialServiceImpl.java
@@ -5,6 +5,7 @@ import com.alibaba.fastjson2.JSONObject; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.http.HttpUtils; import com.ruoyi.framework.config.AliDingConfig; @@ -12,8 +13,10 @@ import com.ruoyi.production.mapper.ProductMaterialMapper; import com.ruoyi.production.pojo.ProductMaterial; import com.ruoyi.production.pojo.ProductMaterialConfig; import com.ruoyi.production.pojo.ProductMaterialSku; import com.ruoyi.production.service.ProductMaterialConfigService; import com.ruoyi.production.service.ProductMaterialService; import com.ruoyi.production.service.ProductMaterialSkuService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -25,8 +28,7 @@ import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; import java.util.ArrayList; import java.util.List; import java.util.*; import java.util.concurrent.locks.ReentrantLock; /** @@ -48,17 +50,22 @@ @Autowired private ProductMaterialConfigService productMaterialConfigService; @Autowired private ProductMaterialSkuService productMaterialSkuService; /** * 忥éï¼é²æ¢æå¨å宿¶ä»»å¡åæ¶æ§è¡ */ private final ReentrantLock syncLock = new ReentrantLock(); @Override @Transactional(rollbackFor = Exception.class) public void loadProductMaterialData() { syncProductMaterialData(1); } @Override @Transactional(rollbackFor = Exception.class) public void syncProductMaterialJob() { syncProductMaterialData(2); } @@ -94,13 +101,7 @@ JSONObject searchParam = buildSearchParam(lastSyncTime, pageNumber, pageSize); // è°ç¨å®ææ¥å£æåæ°æ® String dataRes = HttpUtils.sendPostJson( aliDingConfig.getSearchFormDataUrl(), searchParam.toJSONString(), StandardCharsets.UTF_8.name(), null, accessToken ); String dataRes = HttpUtils.sendPostJson(aliDingConfig.getSearchFormDataUrl(), searchParam.toJSONString(), StandardCharsets.UTF_8.name(), null, accessToken); if (StringUtils.isEmpty(dataRes)) { log.warn("第 {} 页æåæ°æ®ä¸ºç©º", pageNumber); @@ -117,7 +118,7 @@ } // è§£æå¹¶ä¿åæ°æ® List<ProductMaterial> list = parseProductMaterials(dataArr, totalCount); List<ProductMaterialSku> list = parseProductMaterials(dataArr, totalCount); if (!list.isEmpty()) { // å¤çæ´æ°ææ°å¢ int affected = processSaveOrUpdate(list); @@ -141,8 +142,7 @@ } private String getAccessToken() { String params = "appkey=" + aliDingConfig.getAppKey() + "&appsecret=" + aliDingConfig.getAppSecret(); String params = "appkey=" + aliDingConfig.getAppKey() + "&appsecret=" + aliDingConfig.getAppSecret(); String tokenRes = HttpUtils.sendGet(aliDingConfig.getAccessTokenUrl(), params); JSONObject tokenObj = JSON.parseObject(tokenRes); String accessToken = tokenObj.getString("access_token"); @@ -153,9 +153,9 @@ } private LocalDateTime getLastSyncTime() { LambdaQueryWrapper<ProductMaterial> queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.orderByDesc(ProductMaterial::getFormModifiedTime).last("LIMIT 1"); ProductMaterial lastRecord = this.getOne(queryWrapper); LambdaQueryWrapper<ProductMaterialSku> queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.orderByDesc(ProductMaterialSku::getFormModifiedTime).last("LIMIT 1"); ProductMaterialSku lastRecord = productMaterialSkuService.getOne(queryWrapper); return lastRecord != null ? lastRecord.getFormModifiedTime() : null; } @@ -193,20 +193,18 @@ searchParam.put("orderConfigJson", "{\"gmt_modified\":\"+\"}"); if (lastSyncTime != null) { String startTime = lastSyncTime.plusSeconds(1).atZone(ZoneId.systemDefault()) .format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); String startTime = lastSyncTime.plusSeconds(1).atZone(ZoneId.systemDefault()).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); searchParam.put("modifiedFromTimeGMT", startTime); } String endTime = LocalDateTime.now().atZone(ZoneId.systemDefault()) .format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); String endTime = LocalDateTime.now().atZone(ZoneId.systemDefault()).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); searchParam.put("modifiedToTimeGMT", endTime); return searchParam; } private List<ProductMaterial> parseProductMaterials(JSONArray dataArr, Integer totalCount) { List<ProductMaterial> list = new ArrayList<>(); private List<ProductMaterialSku> parseProductMaterials(JSONArray dataArr, Integer totalCount) { List<ProductMaterialSku> list = new ArrayList<>(); LocalDateTime now = LocalDateTime.now(); for (int i = 0; i < dataArr.size(); i++) { @@ -214,47 +212,78 @@ String formInstanceId = item.getString("formInstanceId"); JSONObject originator = item.getJSONObject("originator"); String originatorName = originator != null && originator.containsKey("userName") ? originator.getJSONObject("userName").getString("nameInChinese") : "æªç¥"; String originatorName = originator != null && originator.containsKey("userName") ? originator.getJSONObject("userName").getString("nameInChinese") : "æªç¥"; JSONObject formData = item.getJSONObject("formData"); // å¤çç©æä¸»è¡¨æ°æ® ProductMaterial material = new ProductMaterial(); material.setFormInstanceId(formInstanceId); material.setIdentifierCode(formData.getString("textField_l92h77ju")); material.setMaterialCode(formData.getString("textField_l92f36f2")); material.setMaterialName(formData.getString("textField_l92f36f5")); material.setSpecification(formData.getString("textField_l92f36f6")); material.setBaseUnit(formData.getString("textField_la147lnw")); material.setMaterialAttribute(formData.getString("selectField_la14k51j")); material.setFinishedProductName(formData.getString("radioField_lbkk2nn2")); material.setRemark(formData.getString("textareaField_l92f36f9")); // å¤çç©æç±»åååè´§ç±»å« String materialType = formData.getString("selectField_l92f36fb"); String inventoryCat = formData.getString("selectField_la154noy"); material.setMaterialTypeId(getOrCreateConfigId(materialType, MaterialConfigTypeEnum.MATERIAL_TYPE.name())); material.setInventoryCategoryId(getOrCreateConfigId(inventoryCat, MaterialConfigTypeEnum.INVENTORY_CAT.name())); material.setOriginatorName(originatorName); material.setOriginatorOrg("å®å¤ä¸å绿è½å®ä¸é墿éå ¬å¸"); Long materialId = getOrCreateMaterial(material); material.setFormModifiedTime(parseUtcTime(item.getString("modifiedTimeGMT"))); material.setCreateTime(now); material.setUpdateTime(now); // å¤çç©æè§æ ¼æ°æ® ProductMaterialSku sku = new ProductMaterialSku(); sku.setMaterialId(materialId); sku.setFormInstanceId(formInstanceId); sku.setIdentifierCode(formData.getString("textField_l92h77ju")); sku.setMaterialCode(formData.getString("textField_l92f36f2")); sku.setSpecification(formData.getString("textField_l92f36f6")); sku.setSupplyType(formData.getString("selectField_la14k51j")); sku.setOriginatorName(originatorName); sku.setOriginatorOrg("å®å¤ä¸å绿è½å®ä¸é墿éå ¬å¸"); sku.setFormModifiedTime(parseUtcTime(item.getString("modifiedTimeGMT"))); sku.setCreateTime(now); sku.setUpdateTime(now); list.add(material); list.add(sku); } return list; } private Long getOrCreateMaterial(ProductMaterial material) { LambdaQueryWrapper<ProductMaterial> queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(ProductMaterial::getMaterialName, material.getMaterialName()); ProductMaterial exist = this.getOne(queryWrapper); if (exist == null) { material.setCreateTime(LocalDateTime.now()); material.setUpdateTime(LocalDateTime.now()); this.save(material); return material.getId(); } else { // 妿已åå¨ï¼ä½å ³é®å±æ§åçååï¼åè¿è¡æ´æ°ï¼ä»¥å®ææ°æ®ä¸ºåï¼ boolean needUpdate = false; if (material.getMaterialTypeId() != null && !material.getMaterialTypeId().equals(exist.getMaterialTypeId())) { exist.setMaterialTypeId(material.getMaterialTypeId()); needUpdate = true; } if (material.getInventoryCategoryId() != null && !material.getInventoryCategoryId().equals(exist.getInventoryCategoryId())) { exist.setInventoryCategoryId(material.getInventoryCategoryId()); needUpdate = true; } if (StringUtils.isNotEmpty(material.getBaseUnit()) && !material.getBaseUnit().equals(exist.getBaseUnit())) { exist.setBaseUnit(material.getBaseUnit()); needUpdate = true; } if (needUpdate) { exist.setUpdateTime(LocalDateTime.now()); this.updateById(exist); } return exist.getId(); } } private Integer getOrCreateConfigId(String name, String type) { if (StringUtils.isEmpty(name)) { return null; } ProductMaterialConfig config = productMaterialConfigService.getOne(new LambdaQueryWrapper<ProductMaterialConfig>() .eq(ProductMaterialConfig::getConfigName, name) .eq(ProductMaterialConfig::getConfigType, type)); ProductMaterialConfig config = productMaterialConfigService.getOne(new LambdaQueryWrapper<ProductMaterialConfig>().eq(ProductMaterialConfig::getConfigName, name).eq(ProductMaterialConfig::getConfigType, type)); if (config == null) { config = new ProductMaterialConfig(); config.setConfigName(name); @@ -264,27 +293,30 @@ return config.getId(); } private int processSaveOrUpdate(List<ProductMaterial> list) { private int processSaveOrUpdate(List<ProductMaterialSku> list) { if (list == null || list.isEmpty()) { return 0; } int affected = 0; for (ProductMaterial material : list) { ProductMaterial exist = this.getOne(new LambdaQueryWrapper<ProductMaterial>() .eq(ProductMaterial::getFormInstanceId, material.getFormInstanceId())); for (ProductMaterialSku sku : list) { ProductMaterialSku exist = productMaterialSkuService.getOne(new LambdaQueryWrapper<ProductMaterialSku>() .eq(ProductMaterialSku::getMaterialId, sku.getMaterialId()) .eq(ProductMaterialSku::getSpecification, sku.getSpecification())); if (exist == null) { this.save(material); productMaterialSkuService.save(sku); affected++; log.info("æ°å¢ç©ææ°æ® formInstanceId={}", material.getFormInstanceId()); log.info("æ°å¢ç©æè§æ ¼ {}", sku.getSpecification()); } else { if (exist.getFormModifiedTime() == null || !exist.getFormModifiedTime().equals(material.getFormModifiedTime())) { material.setId(exist.getId()); material.setCreateTime(exist.getCreateTime()); this.updateById(material); if (exist.getFormModifiedTime() == null || !exist.getFormModifiedTime().equals(sku.getFormModifiedTime())) { sku.setId(exist.getId()); sku.setCreateTime(exist.getCreateTime()); productMaterialSkuService.updateById(sku); affected++; log.info("æ´æ°ç©ææ°æ® formInstanceId={}", material.getFormInstanceId()); log.info("æ´æ°ç©æè§æ ¼ {}", sku.getSpecification()); } } } @@ -303,4 +335,103 @@ return null; } } @Override public Map<String, List<ProductMaterial>> ProductMaterialList(String materialName) { List<ProductMaterialConfig> materialConfigList = productMaterialConfigService.list(new LambdaQueryWrapper<ProductMaterialConfig>() .eq(ProductMaterialConfig::getConfigType, MaterialConfigTypeEnum.MATERIAL_TYPE.name())); Map<String, List<ProductMaterial>> productMaterialMap = new HashMap<>(); if (materialConfigList == null || materialConfigList.isEmpty()) { return productMaterialMap; } for (ProductMaterialConfig materialConfig : materialConfigList) { LambdaQueryWrapper<ProductMaterial> wrapper = new LambdaQueryWrapper<>(); wrapper.eq(ProductMaterial::getMaterialTypeId, materialConfig.getId()) .select(ProductMaterial::getId, ProductMaterial::getMaterialName) .like(materialName != null && !materialName.isEmpty(), ProductMaterial::getMaterialName, materialName); List<ProductMaterial> productMaterialList = list(wrapper); if (productMaterialList != null && !productMaterialList.isEmpty()) { productMaterialMap.put(materialConfig.getConfigName(), productMaterialList); } } return productMaterialMap; } @Override @Transactional(rollbackFor = Exception.class) public void addProductMaterial(ProductMaterial productMaterial) { validateProductMaterial(productMaterial, false); if (existsMaterialName(productMaterial.getMaterialName(), null)) { throw new ServiceException("ç©æåç§°å·²åå¨"); } LocalDateTime now = LocalDateTime.now(); if (productMaterial.getCreateTime() == null) { productMaterial.setCreateTime(now); } productMaterial.setUpdateTime(now); if (!this.save(productMaterial)) { throw new ServiceException("æ°å¢ç©æå¤±è´¥"); } log.info("æ°å¢ç©ææå materialName={}", productMaterial.getMaterialName()); } @Override @Transactional(rollbackFor = Exception.class) public void updateProductMaterial(ProductMaterial productMaterial) { validateProductMaterial(productMaterial, true); ProductMaterial exist = this.getById(productMaterial.getId()); if (exist == null) { throw new ServiceException("ç©æä¸åå¨"); } if (existsMaterialName(productMaterial.getMaterialName(), productMaterial.getId())) { throw new ServiceException("ç©æåç§°å·²åå¨"); } productMaterial.setUpdateTime(LocalDateTime.now()); if (!this.updateById(productMaterial)) { throw new ServiceException("ä¿®æ¹ç©æå¤±è´¥"); } log.info("ä¿®æ¹ç©ææå id={}", productMaterial.getId()); } @Override @Transactional(rollbackFor = Exception.class) public void deleteProductMaterial(List<Long> ids) { if (ids == null || ids.isEmpty()) { throw new ServiceException("è¯·éæ©è³å°ä¸æ¡æ°æ®"); } if (!this.removeByIds(ids)) { throw new ServiceException("å é¤ç©æå¤±è´¥"); } log.info("å é¤ç©ææå ids={}", ids); } private void validateProductMaterial(ProductMaterial productMaterial, boolean requireId) { if (productMaterial == null) { throw new ServiceException("åæ°ä¸è½ä¸ºç©º"); } if (requireId && productMaterial.getId() == null) { throw new ServiceException("主é®IDä¸è½ä¸ºç©º"); } if (StringUtils.isEmpty(productMaterial.getMaterialName())) { throw new ServiceException("ç©æåç§°ä¸è½ä¸ºç©º"); } } private boolean existsMaterialName(String materialName, Long excludeId) { if (StringUtils.isEmpty(materialName)) { return false; } LambdaQueryWrapper<ProductMaterial> queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(ProductMaterial::getMaterialName, materialName); if (excludeId != null) { queryWrapper.ne(ProductMaterial::getId, excludeId); } return this.count(queryWrapper) > 0; } } src/main/java/com/ruoyi/production/service/impl/ProductMaterialSkuServiceImpl.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,166 @@ package com.ruoyi.production.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.production.dto.ProductMaterialSkuDto; import com.ruoyi.production.mapper.ProductMaterialMapper; import com.ruoyi.production.mapper.ProductMaterialSkuMapper; import com.ruoyi.production.pojo.ProductMaterial; import com.ruoyi.production.pojo.ProductMaterialSku; import com.ruoyi.production.service.ProductMaterialSkuService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * <br> * ç©æè§æ ¼æ¥å£å®ç°ç±» * </br> * * @author deslrey * @version 1.0 * @since 2026/03/12 10:05 */ @Slf4j @Service public class ProductMaterialSkuServiceImpl extends ServiceImpl<ProductMaterialSkuMapper, ProductMaterialSku> implements ProductMaterialSkuService { @Autowired private ProductMaterialMapper productMaterialMapper; /** * æ¥è¯¢ç©æè§æ ¼å表 */ @Override public List<ProductMaterialSkuDto> productMaterialSkuList(Long materialId) { if (materialId == null) { return Collections.emptyList(); } LambdaQueryWrapper<ProductMaterialSku> queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(ProductMaterialSku::getMaterialId, materialId) .orderByAsc(ProductMaterialSku::getId); List<ProductMaterialSku> skuList = this.list(queryWrapper); if (skuList == null || skuList.isEmpty()) { return Collections.emptyList(); } // æ¥è¯¢ç©æä¿¡æ¯ ProductMaterial material = productMaterialMapper.selectById(materialId); String materialName = material != null ? material.getMaterialName() : null; String baseUnit = material != null ? material.getBaseUnit() : null; List<ProductMaterialSkuDto> result = new ArrayList<>(skuList.size()); for (ProductMaterialSku sku : skuList) { ProductMaterialSkuDto dto = new ProductMaterialSkuDto(); dto.setMaterialId(materialId); dto.setMaterialName(materialName); dto.setBaseUnit(baseUnit); dto.setSkuId(sku.getId()); dto.setSpecification(sku.getSpecification()); dto.setSupplyType(sku.getSupplyType()); result.add(dto); } return result; } /** * æ°å¢ç©æè§æ ¼ */ @Override public void addProductMaterialSku(ProductMaterialSku sku) { validateProductMaterialSku(sku, false); // æ ¡éªç©ææ¯å¦åå¨ ProductMaterial material = productMaterialMapper.selectById(sku.getMaterialId()); if (material == null) { throw new ServiceException("ç©æä¸åå¨"); } // æ ¡éªè§æ ¼æ¯å¦éå¤ if (existsSameSpecification(sku.getMaterialId(), sku.getSpecification(), null)) { throw new ServiceException("è¯¥ç©æå·²åå¨ç¸åè§æ ¼"); } LocalDateTime now = LocalDateTime.now(); if (sku.getCreateTime() == null) { sku.setCreateTime(now); } sku.setUpdateTime(now); if (!this.save(sku)) { throw new ServiceException("æ°å¢ç©æè§æ ¼å¤±è´¥"); } log.info("æ°å¢ç©æè§æ ¼æå materialId={}, specification={}", sku.getMaterialId(), sku.getSpecification()); } /** * ä¿®æ¹ç©æè§æ ¼ */ @Override public void updateProductMaterialSku(ProductMaterialSku sku) { validateProductMaterialSku(sku, true); // æ ¡éªè§æ ¼æ¯å¦éå¤ if (existsSameSpecification(sku.getMaterialId(), sku.getSpecification(), sku.getId())) { throw new ServiceException("è¯¥ç©æå·²åå¨ç¸åè§æ ¼"); } sku.setUpdateTime(LocalDateTime.now()); if (!this.updateById(sku)) { throw new ServiceException("ä¿®æ¹ç©æè§æ ¼å¤±è´¥"); } log.info("ä¿®æ¹ç©æè§æ ¼æå id={}", sku.getId()); } /** * å é¤ç©æè§æ ¼ */ @Override public void deleteProductMaterialSku(List<Long> ids) { if (ids == null || ids.isEmpty()) { throw new ServiceException("è¯·éæ©è³å°ä¸æ¡æ°æ®"); } if (!this.removeByIds(ids)) { throw new ServiceException("å é¤ç©æè§æ ¼å¤±è´¥"); } log.info("å é¤ç©æè§æ ¼æå ids={}", ids); } /** * åæ°æ ¡éª */ private void validateProductMaterialSku(ProductMaterialSku sku, boolean requireId) { if (sku == null) { throw new ServiceException("åæ°ä¸è½ä¸ºç©º"); } if (requireId && sku.getId() == null) { throw new ServiceException("主é®IDä¸è½ä¸ºç©º"); } if (sku.getMaterialId() == null) { throw new ServiceException("ç©æIDä¸è½ä¸ºç©º"); } if (StringUtils.isEmpty(sku.getSpecification())) { throw new ServiceException("è§æ ¼ä¸è½ä¸ºç©º"); } } /** * æ ¡éªæ¯å¦åå¨ç¸åè§æ ¼ */ private boolean existsSameSpecification(Long materialId, String specification, Long excludeId) { LambdaQueryWrapper<ProductMaterialSku> queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(ProductMaterialSku::getMaterialId, materialId) .eq(ProductMaterialSku::getSpecification, specification); if (excludeId != null) { queryWrapper.ne(ProductMaterialSku::getId, excludeId); } return this.count(queryWrapper) > 0; } } src/main/java/com/ruoyi/productionPlan/service/impl/ProductionPlanServiceImpl.java
@@ -15,9 +15,9 @@ import com.ruoyi.common.utils.http.HttpUtils; import com.ruoyi.common.utils.poi.ExcelUtil; import com.ruoyi.framework.config.AliDingConfig; import com.ruoyi.production.pojo.ProductMaterial; import com.ruoyi.production.pojo.ProductMaterialSku; import com.ruoyi.production.pojo.ProductOrder; import com.ruoyi.production.service.ProductMaterialService; import com.ruoyi.production.service.ProductMaterialSkuService; import com.ruoyi.production.service.ProductOrderService; import com.ruoyi.productionPlan.dto.ProductionPlanDto; import com.ruoyi.productionPlan.dto.ProductionPlanImportDto; @@ -72,7 +72,7 @@ private ProductOrderPlanMapper productOrderPlanMapper; @Autowired private ProductMaterialService productMaterialService; private ProductMaterialSkuService productMaterialSkuService; /** * 忥éï¼ç¡®ä¿æå¨å宿¶ä»»å¡ä¸åæ¶æ§è¡ @@ -386,11 +386,11 @@ String materialCode = row.getString("textField_l9xo62q5"); // æ ¹æ®ç©æç¼ç æ¥è¯¢ç©æä¿¡æ¯è¡¨ï¼å ³èç©æID if (StringUtils.isNotEmpty(materialCode)) { LambdaQueryWrapper<ProductMaterial> queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(ProductMaterial::getMaterialCode, materialCode); ProductMaterial productMaterial = productMaterialService.getOne(queryWrapper); if (productMaterial != null) { plan.setProductMaterialId(productMaterial.getId()); LambdaQueryWrapper<ProductMaterialSku> skuQueryWrapper = new LambdaQueryWrapper<>(); skuQueryWrapper.eq(ProductMaterialSku::getMaterialCode, materialCode); ProductMaterialSku sku = productMaterialSkuService.getOne(skuQueryWrapper); if (sku != null && sku.getMaterialId() != null) { plan.setProductMaterialId(sku.getMaterialId().intValue()); } } src/main/resources/mapper/production/ProductMaterialMapper.xml
@@ -3,20 +3,14 @@ "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.ruoyi.production.mapper.ProductMaterialMapper"> <resultMap id="ProductMaterialResultMap" type="com.ruoyi.production.pojo.ProductMaterial"> <id property="id" column="id"/> <result property="tenantId" column="tenant_id"/> <result property="materialTypeId" column="material_type_id"/> <result property="inventoryCategoryId" column="inventory_category_id"/> <result property="identifierCode" column="identifier_code"/> <result property="materialCode" column="material_code"/> <result property="materialName" column="material_name"/> <result property="specification" column="specification"/> <result property="baseUnit" column="base_unit"/> <result property="materialAttribute" column="material_attribute"/> <result property="finishedProductName" column="finished_product_name"/> <result property="originatorName" column="originator_name"/> <result property="originatorOrg" column="originator_org"/> <result property="remark" column="remark"/> <result property="createTime" column="create_time"/> <result property="updateTime" column="update_time"/> src/main/resources/mapper/production/ProductMaterialSkuMapper.xml
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,22 @@ <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.ruoyi.production.mapper.ProductMaterialSkuMapper"> <resultMap id="ProductMaterialSkuResultMap" type="com.ruoyi.production.pojo.ProductMaterialSku"> <id property="id" column="id"/> <result property="materialId" column="material_id"/> <result property="identifierCode" column="identifier_code"/> <result property="materialCode" column="material_code"/> <result property="specification" column="specification"/> <result property="supplyType" column="supply_type"/> <result property="originatorName" column="originator_name"/> <result property="originatorOrg" column="originator_org"/> <result property="formInstanceId" column="form_instance_id"/> <result property="formModifiedTime" column="form_modified_time"/> <result property="createTime" column="create_time"/> <result property="updateTime" column="update_time"/> </resultMap> </mapper>