gongchunyi
2 天以前 a274b897e58c958903c3e00da6c1ccb16646a979
feat: 物料规格和物料的新增、修改和删除
已添加7个文件
已修改7个文件
810 ■■■■ 文件已修改
doc/宁夏-中盛建材.sql 61 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/controller/ProductMaterialController.java 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/controller/ProductMaterialSkuController.java 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/dto/ProductMaterialSkuDto.java 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/mapper/ProductMaterialSkuMapper.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/pojo/ProductMaterial.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/pojo/ProductMaterialSku.java 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/ProductMaterialService.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/ProductMaterialSkuService.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/impl/ProductMaterialServiceImpl.java 235 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/impl/ProductMaterialSkuServiceImpl.java 166 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/productionPlan/service/impl/ProductionPlanServiceImpl.java 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/production/ProductMaterialMapper.xml 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/production/ProductMaterialSkuMapper.xml 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
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>