423ac2a5e7e451248d8cdfc2cda3f32dba0ec8f8..a8a78a1b733914ea97393ccc5a81bf7ea76ed5aa
6 天以前 huminmin
Merge branch 'dev_宁夏_中盛建材' of http://114.132.189.42:9002/r/product-inventor...
a8a78a 对比 | 目录
6 天以前 huminmin
判断下发数量
7aea75 对比 | 目录
6 天以前 gongchunyi
Merge remote-tracking branch 'origin/dev_宁夏_中盛建材' into dev_宁夏_中盛建材
83f6a9 对比 | 目录
6 天以前 gongchunyi
fix: 产品保存时唯一性导致数据丢失
c3b81e 对比 | 目录
6 天以前 huminmin
Merge branch 'dev_宁夏_中盛建材' of http://114.132.189.42:9002/r/product-inventor...
6f234a 对比 | 目录
6 天以前 huminmin
合并下发时判断ids是否存在
b78f04 对比 | 目录
6 天以前 gongchunyi
fix: 生产计划查询未携带申请单编号
a0c2c0 对比 | 目录
6 天以前 gongchunyi
feat: 生产计划列表返回物料id
d4ad30 对比 | 目录
6 天以前 gongchunyi
fix: 移除生产计划多余字段
666ddd 对比 | 目录
6 天以前 gongchunyi
fix: 生产计划的产品规格与物料编码数据为空
153616 对比 | 目录
6 天以前 gongchunyi
feat: 物料信息表配置表
bd8435 对比 | 目录
6 天以前 gongchunyi
feat: 物料类型与存货类别的新增、删除和修改,物料规格的导入数据
52e93e 对比 | 目录
6 天以前 gongchunyi
feat: 物料规格和物料的新增、修改和删除
a274b8 对比 | 目录
6 天以前 zss
能耗管理时间注解
496728 对比 | 目录
6 天以前 huminmin
主生产计划:产品信息改为关联查询
4a82ac 对比 | 目录
已添加10个文件
已修改17个文件
1531 ■■■■ 文件已修改
doc/宁夏-中盛建材.sql 175 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/energy/dto/EnergyCostDto.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/energy/vo/EnergyStatisticsVo.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/controller/ProductMaterialController.java 86 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/controller/ProductMaterialSkuController.java 85 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/dto/ProductMaterialConfigDto.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/dto/ProductMaterialGroupDto.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/dto/ProductMaterialSkuDto.java 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/enums/MaterialConfigTypeEnum.java 45 ●●●●● 补丁 | 查看 | 原始文档 | 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/pojo/ProductMaterialSkuImportDto.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/ProductMaterialConfigService.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/ProductMaterialService.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/ProductMaterialSkuService.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/impl/ProductMaterialConfigServiceImpl.java 96 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/impl/ProductMaterialServiceImpl.java 244 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/impl/ProductMaterialSkuServiceImpl.java 271 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/productionPlan/controller/ProductionPlanController.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/productionPlan/dto/ProductionPlanDto.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/productionPlan/mapper/ProductionPlanMapper.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/productionPlan/pojo/ProductionPlan.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/productionPlan/service/impl/ProductionPlanServiceImpl.java 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/production/ProductMaterialMapper.xml 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/production/ProductMaterialSkuMapper.xml 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/productionPlan/ProductionPlanMapper.xml 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
doc/ÄþÏÄ-ÖÐÊ¢½¨²Ä.sql
@@ -39,54 +39,57 @@
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8mb4 COMMENT ='销售生产计划';
CREATE TABLE `product-inventory-management-zsjc`.`energy`  (
    `id` int NOT NULL AUTO_INCREMENT,
    `energy_tyep` varchar(255) NULL COMMENT '能源类型',
    `energy_name` varchar(255) NULL COMMENT '能源名称',
    `unit` varchar(255) NULL COMMENT '单位',
    `unit_price` decimal(10, 2) NULL COMMENT '单价',
    `remark` varchar(255) NULL COMMENT '备注',
    `create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
    `update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间',
    `create_user` bigint NULL DEFAULT NULL COMMENT '创建者',
    `update_user` bigint NULL DEFAULT NULL COMMENT '更新者',
CREATE TABLE `product-inventory-management-zsjc`.`energy`
(
    `id`          int            NOT NULL AUTO_INCREMENT,
    `energy_tyep` varchar(255)   NULL COMMENT '能源类型',
    `energy_name` varchar(255)   NULL COMMENT '能源名称',
    `unit`        varchar(255)   NULL COMMENT '单位',
    `unit_price`  decimal(10, 2) NULL COMMENT '单价',
    `remark`      varchar(255)   NULL COMMENT '备注',
    `create_time` datetime(0)    NULL DEFAULT NULL COMMENT '创建时间',
    `update_time` datetime(0)    NULL DEFAULT NULL COMMENT '更新时间',
    `create_user` bigint         NULL DEFAULT NULL COMMENT '创建者',
    `update_user` bigint         NULL DEFAULT NULL COMMENT '更新者',
    PRIMARY KEY (`id`)
    ) COMMENT = '能源类型';
) COMMENT = '能源类型';
CREATE TABLE `product-inventory-management-zsjc`.`energy_consumption_detail`  (
    `id` int NOT NULL AUTO_INCREMENT,
    `energy_id` int NULL COMMENT '外检关联能源类型id',
    `type` varchar(255) NULL COMMENT '办公/生产',
    `meter_reading_location` varchar(255) NULL COMMENT '抄表位置',
    `meter_reading_date` date NULL COMMENT '抄表日期',
    `start_code` decimal(10, 2) NULL COMMENT '起码',
    `stop_code` decimal(10, 2) NULL COMMENT '止码',
    `dosage` decimal(10, 2) NULL COMMENT '用量',
    `remark` varchar(255) NULL COMMENT '备注',
    `create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
    `update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间',
    `create_user` bigint NULL DEFAULT NULL COMMENT '创建者',
    `update_user` bigint NULL DEFAULT NULL COMMENT '更新者',
CREATE TABLE `product-inventory-management-zsjc`.`energy_consumption_detail`
(
    `id`                     int            NOT NULL AUTO_INCREMENT,
    `energy_id`              int            NULL COMMENT '外检关联能源类型id',
    `type`                   varchar(255)   NULL COMMENT '办公/生产',
    `meter_reading_location` varchar(255)   NULL COMMENT '抄表位置',
    `meter_reading_date`     date           NULL COMMENT '抄表日期',
    `start_code`             decimal(10, 2) NULL COMMENT '起码',
    `stop_code`              decimal(10, 2) NULL COMMENT '止码',
    `dosage`                 decimal(10, 2) NULL COMMENT '用量',
    `remark`                 varchar(255)   NULL COMMENT '备注',
    `create_time`            datetime(0)    NULL DEFAULT NULL COMMENT '创建时间',
    `update_time`            datetime(0)    NULL DEFAULT NULL COMMENT '更新时间',
    `create_user`            bigint         NULL DEFAULT NULL COMMENT '创建者',
    `update_user`            bigint         NULL DEFAULT NULL COMMENT '更新者',
    PRIMARY KEY (`id`)
    ) COMMENT = '能源类型-能耗抄表明细';
) COMMENT = '能源类型-能耗抄表明细';
CREATE TABLE `product-inventory-management-zsjc`.`energy_consumption_detail_file`  (
    `id` int NOT NULL AUTO_INCREMENT,
    `energy_consumption_detail_id` int NULL COMMENT '关联外键能耗抄表id',
    `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
    `url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
    `file_size` double NULL DEFAULT NULL,
    `create_time` timestamp(0) NULL DEFAULT NULL,
    `create_user` int NULL DEFAULT NULL,
    `update_time` timestamp(0) NULL DEFAULT NULL,
    `update_user` int NULL DEFAULT NULL,
CREATE TABLE `product-inventory-management-zsjc`.`energy_consumption_detail_file`
(
    `id`                           int                                                           NOT NULL AUTO_INCREMENT,
    `energy_consumption_detail_id` int                                                           NULL COMMENT '关联外键能耗抄表id',
    `name`                         varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
    `url`                          varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
    `file_size`                    double                                                        NULL DEFAULT NULL,
    `create_time`                  timestamp(0)                                                  NULL DEFAULT NULL,
    `create_user`                  int                                                           NULL DEFAULT NULL,
    `update_time`                  timestamp(0)                                                  NULL DEFAULT NULL,
    `update_user`                  int                                                           NULL DEFAULT NULL,
    PRIMARY KEY (`id`)
    ) COMMENT = '能源类型-能耗抄表明细_附件';
) COMMENT = '能源类型-能耗抄表明细_附件';
alter table product_order
drop column sales_ledger_id,
drop column sale_ledger_product_id,
drop column product_model_id;
    drop column sales_ledger_id,
    drop column sale_ledger_product_id,
    drop column product_model_id;
alter table production_plan
    add assigned_quantity DECIMAL(10, 4) default 0 not null COMMENT '下发数量';
@@ -98,12 +101,12 @@
drop table if exists product_order_plan;
create table product_order_plan
(
    id                       bigint auto_increment primary key,
    product_order_id bigint not null default 0 comment '生产订单id',
    production_plan_id  bigint not null default 0 comment '生产计划id',
    create_time              datetime null comment '录入时间',
    update_time              datetime null comment '更新时间',
    assigned_quantity DECIMAL(10, 4) default 0 not null comment '下发数量',
    id                 bigint auto_increment primary key,
    product_order_id   bigint   not null default 0 comment '生产订单id',
    production_plan_id bigint   not null default 0 comment '生产计划id',
    create_time        datetime null comment '录入时间',
    update_time        datetime null comment '更新时间',
    assigned_quantity  DECIMAL(10, 4)    default 0 not null comment '下发数量',
    index idx_product_order_id (product_order_id),
    index idx_production_plan_id (production_plan_id),
    unique idx_product_order_production_plan (product_order_id, production_plan_id)
@@ -111,42 +114,56 @@
CREATE TABLE `product_material`
(
    `id`                    INT NOT NULL AUTO_INCREMENT PRIMARY KEY 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 '规格型号',
    `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 '宁夏中创绿能实业集团有限公司',
    `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',
    `material_name`         VARCHAR(255) NOT NULL COMMENT '物料名称',
    `base_unit`             VARCHAR(50) DEFAULT NULL COMMENT '基本单位',
    `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`)
    `create_time`           DATETIME    DEFAULT CURRENT_TIMESTAMP,
    `update_time`           DATETIME    DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    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 = '物料信息表';
  DEFAULT CHARSET = utf8mb4 COMMENT = '物料主表';
CREATE TABLE `product_material_sku`
(
    `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 product_material
    ADD COLUMN form_instance_id   VARCHAR(100) DEFAULT NULL COMMENT '宜搭表单实例ID',
    ADD COLUMN form_modified_time DATETIME     DEFAULT NULL COMMENT '宜搭修改时间';
ALTER TABLE product_material_sku
    DROP FOREIGN KEY fk_material;
DROP TABLE IF EXISTS `product_material_config`;
CREATE TABLE `product_material_config`
(
    `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`          int                                                           NOT NULL AUTO_INCREMENT,
    `config_type` varchar(50) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci  NOT NULL COMMENT '区分类型: MATERIAL_TYPE æˆ– INVENTORY_CAT',
    `config_name` varchar(100) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL COMMENT '显示的名称',
    PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB
  AUTO_INCREMENT = 39
  CHARACTER SET = utf8mb3
  COLLATE = utf8mb3_general_ci COMMENT = '物料信息表配置表'
  ROW_FORMAT = Dynamic;
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`);
SET FOREIGN_KEY_CHECKS = 1;
src/main/java/com/ruoyi/energy/dto/EnergyCostDto.java
@@ -1,9 +1,11 @@
package com.ruoyi.energy.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.checkerframework.checker.units.qual.A;
import org.springframework.format.annotation.DateTimeFormat;
import java.math.BigDecimal;
import java.time.LocalDate;
@@ -14,6 +16,8 @@
public class EnergyCostDto {
    @ApiModelProperty("日期")
    @JsonFormat(pattern = "yyyy-MM-dd")
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private LocalDate meterReadingDate;
    @ApiModelProperty("用水量")
src/main/java/com/ruoyi/energy/vo/EnergyStatisticsVo.java
@@ -1,7 +1,9 @@
package com.ruoyi.energy.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDate;
@@ -10,9 +12,13 @@
public class EnergyStatisticsVo {
    //开始日期
    @JsonFormat(pattern = "yyyy-MM-dd")
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private LocalDate startDate;
    //结束日期
    @JsonFormat(pattern = "yyyy-MM-dd")
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private LocalDate endDate;
    //查询间隔天数
src/main/java/com/ruoyi/production/controller/ProductMaterialController.java
@@ -3,12 +3,17 @@
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.ProductMaterialConfigDto;
import com.ruoyi.production.dto.ProductMaterialGroupDto;
import com.ruoyi.production.pojo.ProductMaterial;
import com.ruoyi.production.pojo.ProductMaterialConfig;
import com.ruoyi.production.service.ProductMaterialConfigService;
import com.ruoyi.production.service.ProductMaterialService;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
 * <br>
@@ -26,6 +31,9 @@
    @Autowired
    private ProductMaterialService productMaterialService;
    @Autowired
    private ProductMaterialConfigService productMaterialConfigService;
    @GetMapping("/loadData")
    @ApiOperation("拉取物料编码数据")
    @Log(title = "拉取物料编码数据", businessType = BusinessType.INSERT)
@@ -34,4 +42,76 @@
        return AjaxResult.success();
    }
    @GetMapping("/list")
    @ApiOperation("物料数据")
    @Log(title = "物料数据", businessType = BusinessType.OTHER)
    public AjaxResult productMaterialList(String materialName) {
        List<ProductMaterialGroupDto> 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();
    }
    @GetMapping("/materialTypeList")
    @ApiOperation("物料类型数据集合")
    @Log(title = "物料类型数据集合", businessType = BusinessType.OTHER)
    public AjaxResult materialTypeList() {
        List<ProductMaterialConfig> list = productMaterialConfigService.materialTypeList();
        return AjaxResult.success(list);
    }
    @GetMapping("/inventoryCategoryList")
    @ApiOperation("'存货类别数据集合")
    @Log(title = "'存货类别数据集合", businessType = BusinessType.OTHER)
    public AjaxResult inventoryCategoryList() {
        List<ProductMaterialConfig> list = productMaterialConfigService.inventoryCategoryList();
        return AjaxResult.success(list);
    }
    @PostMapping("/config/add")
    @ApiOperation("新增物料配置")
    @Log(title = "新增物料配置", businessType = BusinessType.INSERT)
    public AjaxResult addProductMaterialConfig(@RequestBody ProductMaterialConfigDto config) {
        productMaterialConfigService.addProductMaterialConfig(config);
        return AjaxResult.success();
    }
    @PutMapping("/config/update")
    @ApiOperation("修改物料配置")
    @Log(title = "修改物料配置", businessType = BusinessType.UPDATE)
    public AjaxResult updateProductMaterialConfig(@RequestBody ProductMaterialConfigDto config) {
        productMaterialConfigService.updateProductMaterialConfig(config);
        return AjaxResult.success();
    }
    @DeleteMapping("/config/delete")
    @ApiOperation("删除物料配置")
    @Log(title = "删除物料配置", businessType = BusinessType.DELETE)
    public AjaxResult deleteProductMaterialConfig(@RequestBody List<Integer> ids) {
        productMaterialConfigService.deleteProductMaterialConfig(ids);
        return AjaxResult.success();
    }
}
src/main/java/com/ruoyi/production/controller/ProductMaterialSkuController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,85 @@
package com.ruoyi.production.controller;
import com.ruoyi.common.utils.poi.ExcelUtil;
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.pojo.ProductMaterialSkuImportDto;
import com.ruoyi.production.service.ProductMaterialSkuService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
/**
 * <br>
 * ç‰©æ–™è§„格控制层
 * </br>
 *
 * @author deslrey
 * @version 1.0
 * @since 2026/03/12 10:43
 */
@RestController
@RequestMapping("/productMaterialSku")
@Api(tags = "物料规格管理接口")
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();
    }
    @PostMapping("/downloadTemplate")
    @Log(title = "下载物料规格导入模板", businessType = BusinessType.EXPORT)
    @ApiOperation("下载物料规格导入模板")
    public void importTemplate(HttpServletResponse response) {
        ExcelUtil<ProductMaterialSkuImportDto> excelUtil = new ExcelUtil<>(ProductMaterialSkuImportDto.class);
        excelUtil.importTemplateExcel(response, "下载物料规格导入模板");
    }
    @PostMapping("/import")
    @ApiOperation("物料规格数据导入")
    @Log(title = "物料规格数据导入", businessType = BusinessType.IMPORT)
    public AjaxResult importProdData(@RequestParam("file") MultipartFile file, @RequestParam("materialId") Long materialId) {
        productMaterialSkuService.importProdData(file, materialId);
        return AjaxResult.success("导入成功");
    }
}
src/main/java/com/ruoyi/production/dto/ProductMaterialConfigDto.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,24 @@
package com.ruoyi.production.dto;
import com.baomidou.mybatisplus.annotation.TableField;
import com.ruoyi.production.pojo.ProductMaterialConfig;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
 * <br>
 * ç‰©æ–™ä¿¡æ¯è¡¨é…ç½®è¡¨Dto
 * </br>
 *
 * @author deslrey
 * @version 1.0
 * @since 2026/03/12 14:07
 */
@Data
public class ProductMaterialConfigDto extends ProductMaterialConfig {
    @ApiModelProperty("配置类型")
    @TableField(exist = false)
    private Integer type;
}
src/main/java/com/ruoyi/production/dto/ProductMaterialGroupDto.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,30 @@
package com.ruoyi.production.dto;
import com.ruoyi.production.pojo.ProductMaterial;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
/**
 * <br>
 * ç‰©æ–™é…ç½®åˆ†ç»„ DTO
 * </br>
 *
 * @author deslrey
 * @since 2026/03/12 13:43
 */
@Data
@ApiModel(value = "ProductMaterialGroupDto", description = "物料配置分组数据")
public class ProductMaterialGroupDto {
    @ApiModelProperty("配置ID")
    private Integer configId;
    @ApiModelProperty("配置名称")
    private String configName;
    @ApiModelProperty("物料列表")
    private List<ProductMaterial> materialList;
}
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/enums/MaterialConfigTypeEnum.java
@@ -13,13 +13,50 @@
    /**
     * ç‰©æ–™ç±»åž‹
     * å¯¹åº”数据库 config_type = MATERIAL_TYPE
     */
    MATERIAL_TYPE,
    MATERIAL_TYPE(1, "物料类型"),
    /**
     * å­˜è´§ç±»åˆ«
     * å¯¹åº”数据库 config_type = INVENTORY_CAT
     */
    INVENTORY_CAT
    INVENTORY_CAT(2, "存货类别");
    private final Integer type;
    private final String desc;
    MaterialConfigTypeEnum(Integer type, String desc) {
        this.type = type;
        this.desc = desc;
    }
    public Integer getType() {
        return type;
    }
    public String getDesc() {
        return desc;
    }
    /**
     * æ ¹æ® type èŽ·å–æžšä¸¾
     */
    public static MaterialConfigTypeEnum getByType(Integer type) {
        for (MaterialConfigTypeEnum value : values()) {
            if (value.type.equals(type)) {
                return value;
            }
        }
        return null;
    }
    /**
     * æ ¹æ® type èŽ·å–æ•°æ®åº“å­˜å‚¨å€¼
     */
    public static String getConfigType(Integer type) {
        MaterialConfigTypeEnum e = getByType(type);
        if (e == null) {
            throw new IllegalArgumentException("配置类型错误");
        }
        return e.name();
    }
}
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/pojo/ProductMaterialSkuImportDto.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,35 @@
package com.ruoyi.production.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.framework.aspectj.lang.annotation.Excel;
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 13:08
 */
@Data
@ApiModel(value = "ProductMaterialSkuImportDto", description = "物料规格表导入")
public class ProductMaterialSkuImportDto {
    @ApiModelProperty("规格型号")
    @Excel(name = "规格型号")
    private String specification;
    @ApiModelProperty("供应方式(自制,外购)")
    @Excel(name = "供应方式")
    private String supplyType;
}
src/main/java/com/ruoyi/production/service/ProductMaterialConfigService.java
@@ -1,7 +1,10 @@
package com.ruoyi.production.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.production.dto.ProductMaterialConfigDto;
import com.ruoyi.production.pojo.ProductMaterialConfig;
import java.util.List;
/**
 * <br>
@@ -13,4 +16,13 @@
 * @since 2026/03/11 16:58
 */
public interface ProductMaterialConfigService extends IService<ProductMaterialConfig> {
    List<ProductMaterialConfig> materialTypeList();
    List<ProductMaterialConfig> inventoryCategoryList();
    void addProductMaterialConfig(ProductMaterialConfigDto config);
    void updateProductMaterialConfig(ProductMaterialConfigDto config);
    void deleteProductMaterialConfig(List<Integer> ids);
}
src/main/java/com/ruoyi/production/service/ProductMaterialService.java
@@ -1,7 +1,10 @@
package com.ruoyi.production.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.production.dto.ProductMaterialGroupDto;
import com.ruoyi.production.pojo.ProductMaterial;
import java.util.List;
/**
 * <br>
@@ -17,4 +20,12 @@
    void loadProductMaterialData();
    void syncProductMaterialJob();
    List<ProductMaterialGroupDto> 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,29 @@
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 org.springframework.web.multipart.MultipartFile;
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);
    void importProdData(MultipartFile file, Long materialId);
}
src/main/java/com/ruoyi/production/service/impl/ProductMaterialConfigServiceImpl.java
@@ -1,11 +1,19 @@
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.ProductMaterialConfigDto;
import com.ruoyi.production.enums.MaterialConfigTypeEnum;
import com.ruoyi.production.mapper.ProductMaterialConfigMapper;
import com.ruoyi.production.pojo.ProductMaterialConfig;
import com.ruoyi.production.service.ProductMaterialConfigService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
 * <br>
@@ -20,5 +28,93 @@
@Service
public class ProductMaterialConfigServiceImpl extends ServiceImpl<ProductMaterialConfigMapper, ProductMaterialConfig> implements ProductMaterialConfigService {
    @Override
    public List<ProductMaterialConfig> materialTypeList() {
        return getByType(MaterialConfigTypeEnum.MATERIAL_TYPE);
    }
    @Override
    public List<ProductMaterialConfig> inventoryCategoryList() {
        return getByType(MaterialConfigTypeEnum.INVENTORY_CAT);
    }
    private List<ProductMaterialConfig> getByType(MaterialConfigTypeEnum type) {
        return list(new LambdaQueryWrapper<ProductMaterialConfig>().eq(ProductMaterialConfig::getConfigType, type.name()));
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addProductMaterialConfig(ProductMaterialConfigDto config) {
        config.setConfigType(MaterialConfigTypeEnum.getConfigType(config.getType()));
        validateConfig(config, false);
        if (existsConfig(config.getConfigType(), config.getConfigName(), null)) {
            throw new ServiceException("配置名称已存在");
        }
        if (!this.save(config)) {
            throw new ServiceException("新增配置失败");
        }
        log.info("新增物料配置成功 type={}, name={}", config.getConfigType(), config.getConfigName());
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateProductMaterialConfig(ProductMaterialConfigDto config) {
        config.setConfigType(MaterialConfigTypeEnum.getConfigType(config.getType()));
        validateConfig(config, true);
        ProductMaterialConfig exist = this.getById(config.getId());
        if (exist == null) {
            throw new ServiceException("配置不存在");
        }
        if (existsConfig(config.getConfigType(), config.getConfigName(), config.getId())) {
            throw new ServiceException("配置名称已存在");
        }
        if (!this.updateById(config)) {
            throw new ServiceException("修改配置失败");
        }
        log.info("修改物料配置成功 id={}", config.getId());
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteProductMaterialConfig(List<Integer> ids) {
        if (ids == null || ids.isEmpty()) {
            throw new ServiceException("请选择至少一条数据");
        }
        if (!this.removeByIds(ids)) {
            throw new ServiceException("删除配置失败");
        }
        log.info("删除物料配置成功 ids={}", ids);
    }
    private void validateConfig(ProductMaterialConfig config, boolean requireId) {
        if (config == null) {
            throw new ServiceException("参数不能为空");
        }
        if (requireId && config.getId() == null) {
            throw new ServiceException("主键ID不能为空");
        }
        if (StringUtils.isEmpty(config.getConfigType())) {
            throw new ServiceException("配置类型不能为空");
        }
        try {
            MaterialConfigTypeEnum.valueOf(config.getConfigType());
        } catch (IllegalArgumentException e) {
            throw new ServiceException("配置类型不合法");
        }
        if (StringUtils.isEmpty(config.getConfigName())) {
            throw new ServiceException("配置名称不能为空");
        }
    }
    private boolean existsConfig(String configType, String configName, Integer excludeId) {
        if (StringUtils.isEmpty(configName) || StringUtils.isEmpty(configType)) {
            return false;
        }
        LambdaQueryWrapper<ProductMaterialConfig> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(ProductMaterialConfig::getConfigType, configType).eq(ProductMaterialConfig::getConfigName, configName);
        if (excludeId != null) {
            wrapper.ne(ProductMaterialConfig::getId, excludeId);
        }
        return this.count(wrapper) > 0;
    }
}
src/main/java/com/ruoyi/production/service/impl/ProductMaterialServiceImpl.java
@@ -5,15 +5,19 @@
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;
import com.ruoyi.production.dto.ProductMaterialGroupDto;
import com.ruoyi.production.enums.MaterialConfigTypeEnum;
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 +29,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 +51,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 +102,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 +119,7 @@
                }
                // è§£æžå¹¶ä¿å­˜æ•°æ®
                List<ProductMaterial> list = parseProductMaterials(dataArr, totalCount);
                List<ProductMaterialSku> list = parseProductMaterials(dataArr, totalCount);
                if (!list.isEmpty()) {
                    // å¤„理更新或新增
                    int affected = processSaveOrUpdate(list);
@@ -141,8 +143,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 +154,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 +194,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 +213,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 +294,37 @@
        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) {
            if (exist == null) {
                this.save(material);
                affected++;
                log.info("新增物料数据 formInstanceId={}", material.getFormInstanceId());
            LambdaQueryWrapper<ProductMaterialSku> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(ProductMaterialSku::getMaterialId, sku.getMaterialId())
                    .eq(ProductMaterialSku::getSpecification, sku.getSpecification());
            if (StringUtils.isNotEmpty(sku.getMaterialCode())) {
                wrapper.eq(ProductMaterialSku::getMaterialCode, sku.getMaterialCode());
            } else {
                if (exist.getFormModifiedTime() == null || !exist.getFormModifiedTime().equals(material.getFormModifiedTime())) {
                    material.setId(exist.getId());
                    material.setCreateTime(exist.getCreateTime());
                    this.updateById(material);
                wrapper.isNull(ProductMaterialSku::getMaterialCode);
            }
            ProductMaterialSku exist = productMaterialSkuService.getOne(wrapper);
            if (exist == null) {
                productMaterialSkuService.save(sku);
                affected++;
                log.info("新增物料规格 {}", sku.getSpecification());
            } else {
                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 +343,100 @@
            return null;
        }
    }
    @Override
    public List<ProductMaterialGroupDto> ProductMaterialList(String materialName) {
        List<ProductMaterialConfig> materialConfigList = productMaterialConfigService.list(new LambdaQueryWrapper<ProductMaterialConfig>()
                .eq(ProductMaterialConfig::getConfigType, MaterialConfigTypeEnum.MATERIAL_TYPE.name()));
        List<ProductMaterialGroupDto> productMaterialMap = new ArrayList<>();
        if (materialConfigList == null || materialConfigList.isEmpty()) {
            return productMaterialMap;
        }
        for (ProductMaterialConfig materialConfig : materialConfigList) {
            LambdaQueryWrapper<ProductMaterial> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(ProductMaterial::getMaterialTypeId, materialConfig.getId())
                    .like(materialName != null && !materialName.isEmpty(), ProductMaterial::getMaterialName, materialName);
            List<ProductMaterial> productMaterialList = list(wrapper);
            ProductMaterialGroupDto dto = new ProductMaterialGroupDto();
            dto.setConfigId(materialConfig.getId());
            dto.setConfigName(materialConfig.getConfigName());
            dto.setMaterialList(productMaterialList);
            productMaterialMap.add(dto);
        }
        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,271 @@
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.common.utils.poi.ExcelUtil;
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.pojo.ProductMaterialSkuImportDto;
import com.ruoyi.production.service.ProductMaterialSkuService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
 * <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;
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void importProdData(MultipartFile file, Long materialId) {
        if (materialId == null) {
            throw new ServiceException("物料ID不能为空");
        }
        if (file == null || file.isEmpty()) {
            throw new ServiceException("导入文件不能为空");
        }
        ProductMaterial material = productMaterialMapper.selectById(materialId);
        if (material == null) {
            throw new ServiceException("物料不存在");
        }
        ExcelUtil<ProductMaterialSkuImportDto> excelUtil = new ExcelUtil<>(ProductMaterialSkuImportDto.class);
        List<ProductMaterialSkuImportDto> importList;
        try {
            importList = excelUtil.importExcel(file.getInputStream());
        } catch (Exception e) {
            log.error("导入物料规格Excel解析失败", e);
            throw new ServiceException("Excel解析失败");
        }
        if (importList == null || importList.isEmpty()) {
            throw new ServiceException("Excel没有数据");
        }
        Map<String, ProductMaterialSkuImportDto> specMap = new LinkedHashMap<>();
        for (ProductMaterialSkuImportDto dto : importList) {
            if (dto == null || StringUtils.isEmpty(dto.getSpecification())) {
                continue;
            }
            String specification = dto.getSpecification().trim();
            if (specification.isEmpty()) {
                continue;
            }
            specMap.putIfAbsent(specification, dto);
        }
        if (specMap.isEmpty()) {
            throw new ServiceException("Excel没有有效的规格数据");
        }
        Set<String> specifications = specMap.keySet();
        List<ProductMaterialSku> existList = this.list(new LambdaQueryWrapper<ProductMaterialSku>()
                .eq(ProductMaterialSku::getMaterialId, materialId)
                .in(ProductMaterialSku::getSpecification, specifications));
        Map<String, ProductMaterialSku> existMap = existList.stream()
                .collect(Collectors.toMap(ProductMaterialSku::getSpecification, sku -> sku, (a, b) -> a));
        LocalDateTime now = LocalDateTime.now();
        List<ProductMaterialSku> saveList = new ArrayList<>();
        List<ProductMaterialSku> updateList = new ArrayList<>();
        for (Map.Entry<String, ProductMaterialSkuImportDto> entry : specMap.entrySet()) {
            String specification = entry.getKey();
            ProductMaterialSkuImportDto dto = entry.getValue();
            String supplyType = StringUtils.isNotEmpty(dto.getSupplyType()) ? dto.getSupplyType().trim() : null;
            ProductMaterialSku exist = existMap.get(specification);
            if (exist == null) {
                ProductMaterialSku sku = new ProductMaterialSku();
                sku.setMaterialId(materialId);
                sku.setSpecification(specification);
                sku.setSupplyType(supplyType);
                sku.setCreateTime(now);
                sku.setUpdateTime(now);
                saveList.add(sku);
            } else {
                boolean needUpdate = false;
                if (supplyType != null && !supplyType.equals(exist.getSupplyType())) {
                    exist.setSupplyType(supplyType);
                    needUpdate = true;
                }
                if (needUpdate) {
                    exist.setUpdateTime(now);
                    updateList.add(exist);
                }
            }
        }
        if (saveList.isEmpty() && updateList.isEmpty()) {
            throw new ServiceException("Excel与现有数据一致,无需导入");
        }
        if (!saveList.isEmpty()) {
            this.saveBatch(saveList);
        }
        if (!updateList.isEmpty()) {
            this.updateBatchById(updateList);
        }
        log.info("物料规格导入完成 materialId={}, æ–°å¢ž{}条,更新{}条", materialId, saveList.size(), updateList.size());
    }
}
src/main/java/com/ruoyi/productionPlan/controller/ProductionPlanController.java
@@ -16,6 +16,7 @@
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.util.List;
/**
@@ -53,6 +54,13 @@
    @Log(title = "合并生产计划", businessType = BusinessType.INSERT)
    @ApiOperation("合并生产计划")
    public AjaxResult combine(@RequestBody ProductionPlanDto productionPlanDto) {
       if (productionPlanDto.getIds() == null || productionPlanDto.getIds().isEmpty()) {
            return AjaxResult.error("请选择要下发的生产计划");
        }
       if (productionPlanDto.getTotalAssignedQuantity() == null || productionPlanDto.getTotalAssignedQuantity().compareTo(BigDecimal.ZERO) <= 0) {
            return AjaxResult.error("请输入下发数量");
        }
        return AjaxResult.success(productionPlanService.combine(productionPlanDto));
    }
src/main/java/com/ruoyi/productionPlan/dto/ProductionPlanDto.java
@@ -1,6 +1,7 @@
package com.ruoyi.productionPlan.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.framework.aspectj.lang.annotation.Excel;
import com.ruoyi.productionPlan.pojo.ProductionPlan;
import io.swagger.annotations.ApiModelProperty;
@@ -13,6 +14,31 @@
@Data
public class ProductionPlanDto extends ProductionPlan {
    /**
     * ç‰©æ–™ç¼–码
     */
    @ApiModelProperty("物料编码")
    @Excel(name = "物料编码")
    private String materialCode;
    /**
     * äº§å“åç§°
     */
    @ApiModelProperty("产品名称")
    @Excel(name = "产品名称")
    private String productName;
    /**
     * äº§å“è§„æ ¼
     */
    @ApiModelProperty("产品规格")
    @Excel(name = "产品规格")
    private String specification;
    @ApiModelProperty("产品单位")
    @Excel(name = "产品单位")
    private String baseUnit;
    @ApiModelProperty(value = "生产计划id集合")
    private List<Long> ids;
@@ -23,4 +49,10 @@
    @JsonFormat(pattern = "yyyy-MM-dd")
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private LocalDate planCompleteTime;
    /**
     * å…³è”物料信息表
     */
    @ApiModelProperty("关联物料信息表ID")
    private Long productMaterialId;
}
src/main/java/com/ruoyi/productionPlan/mapper/ProductionPlanMapper.java
@@ -24,4 +24,6 @@
    List<ProductionPlanSummaryDto> selectSummaryByProductType(ProductionPlanSummaryDto query);
    List<ProductionPlanDto> selectWithMaterialByIds(@Param("ids") List<Long> ids);
}
src/main/java/com/ruoyi/productionPlan/pojo/ProductionPlan.java
@@ -59,32 +59,10 @@
    private String customerName;
    /**
     * ç‰©æ–™ç¼–码
     */
    @ApiModelProperty("物料编码")
    @Excel(name = "物料编码")
    private String materialCode;
    /**
     * å…³è”物料信息表ID
     */
    @ApiModelProperty("关联物料信息表ID")
    private Integer productMaterialId;
    /**
     * äº§å“åç§°
     */
    @ApiModelProperty("产品名称")
    @Excel(name = "产品名称")
    private String productName;
    /**
     * äº§å“è§„æ ¼
     */
    @ApiModelProperty("产品规格")
    @Excel(name = "产品规格")
    private String productSpec;
    private Long productMaterialSkuId;
    /**
     * é•¿
src/main/java/com/ruoyi/productionPlan/service/impl/ProductionPlanServiceImpl.java
@@ -16,8 +16,10 @@
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,6 +74,9 @@
    private ProductOrderPlanMapper productOrderPlanMapper;
    @Autowired
    private ProductMaterialSkuService productMaterialSkuService;
    @Autowired
    private ProductMaterialService productMaterialService;
    /**
@@ -111,8 +116,7 @@
        }
        //  æŸ¥è¯¢ä¸»ç”Ÿäº§è®¡åˆ’
        List<ProductionPlan> plans = productionPlanMapper.selectBatchIds(productionPlanDto.getIds());
        plans.sort(Comparator.comparingLong(ProductionPlan::getId));
        List<ProductionPlanDto> plans = productionPlanMapper.selectWithMaterialByIds(productionPlanDto.getIds());
        //  æ ¡éªŒæ˜¯å¦å­˜åœ¨ä¸åŒçš„产品名称
        String firstProductName = plans.get(0).getProductName();
@@ -121,11 +125,10 @@
        }
        // æ ¡éªŒæ˜¯å¦å­˜åœ¨ä¸åŒçš„产品规格
        String firstProductSpec = plans.get(0).getProductSpec();
        if (plans.stream().anyMatch(p -> !p.getProductSpec().equals(firstProductSpec))) {
        String firstProductSpec = plans.get(0).getSpecification();
        if (plans.stream().anyMatch(p -> !p.getSpecification().equals(firstProductSpec))) {
            throw new BaseException("合并失败,存在不同的产品规格");
        }
        // å åŠ å‰©ä½™æ–¹æ•°
        BigDecimal totalRemainingVolume = plans.stream()
@@ -386,20 +389,16 @@
                plan.setCustomerName(formData.getString("textField_lbkozohg"));
                String materialCode = row.getString("textField_l9xo62q5");
                plan.setMaterialCode(materialCode);
                // æ ¹æ®ç‰©æ–™ç¼–码查询物料信息表,关联物料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) {
                        plan.setProductMaterialSkuId(sku.getId());
                    }
                }
                plan.setProductName(row.getString("textField_l9xo62q7"));
                plan.setProductSpec(row.getString("textField_l9xo62q8"));
                plan.setLength(row.getInteger("numberField_lb7lgatg_value"));
                plan.setWidth(row.getInteger("numberField_lb7lgath_value"));
                plan.setHeight(row.getInteger("numberField_lb7lgati_value"));
@@ -473,19 +472,19 @@
        //  Map (formInstanceId + materialCode)
        Map<String, ProductionPlan> existMap = new HashMap<>();
        for (ProductionPlan p : existList) {
            String key = p.getFormInstanceId() + "_" + p.getMaterialCode();
            String key = p.getFormInstanceId() + "_" + p.getProductMaterialSkuId();
            existMap.put(key, p);
        }
        //  éåŽ†åŒæ­¥æ•°æ®
        for (ProductionPlan plan : list) {
            String key = plan.getFormInstanceId() + "_" + plan.getMaterialCode();
            String key = plan.getFormInstanceId() + "_" + plan.getProductMaterialSkuId();
            ProductionPlan exist = existMap.get(key);
            if (exist == null) {
                // æ–°å¢ž
                this.save(plan);
                affected++;
                log.info("新增数据 formInstanceId={}, materialCode={}", plan.getFormInstanceId(), plan.getMaterialCode());
                log.info("新增数据 formInstanceId={}, materialCode={}", plan.getFormInstanceId(), plan.getProductMaterialSkuId());
            } else {
                // åˆ¤æ–­æ˜¯å¦éœ€è¦æ›´æ–°
                if (exist.getFormModifiedTime() == null || !exist.getFormModifiedTime().equals(plan.getFormModifiedTime())) {
@@ -493,7 +492,7 @@
                    plan.setCreateTime(exist.getCreateTime());
                    this.updateById(plan);
                    affected++;
                    log.info("更新数据 formInstanceId={}, materialCode={}", plan.getFormInstanceId(), plan.getMaterialCode());
                    log.info("更新数据 formInstanceId={}, materialCode={}", plan.getFormInstanceId(), plan.getProductMaterialSkuId());
                }
            }
        }
@@ -547,6 +546,16 @@
            entity.setDataSourceType(2);
            entity.setDataSyncType(1);
            // æ ¹æ®ç‰©æ–™ç¼–码填充关联ID
            if (StringUtils.isNotEmpty(dto.getMaterialCode())) {
                LambdaQueryWrapper<ProductMaterialSku> skuQueryWrapper = new LambdaQueryWrapper<>();
                skuQueryWrapper.eq(ProductMaterialSku::getMaterialCode, dto.getMaterialCode());
                ProductMaterialSku sku = productMaterialSkuService.getOne(skuQueryWrapper);
                if (sku != null) {
                    entity.setProductMaterialSkuId(sku.getId());
                }
            }
            entityList.add(entity);
        }
        this.saveBatch(entityList);
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>
src/main/resources/mapper/productionPlan/ProductionPlanMapper.xml
@@ -11,10 +11,7 @@
        <result property="serialNo" column="serial_no"/>
        <result property="applyNo" column="apply_no"/>
        <result property="customerName" column="customer_name"/>
        <result property="materialCode" column="material_code"/>
        <result property="productMaterialId" column="product_material_id"/>
        <result property="productName" column="product_name"/>
        <result property="productSpec" column="product_spec"/>
        <result property="productMaterialSkuId" column="product_material_sku_id"/>
        <result property="length" column="length"/>
        <result property="width" column="width"/>
        <result property="height" column="height"/>
@@ -38,22 +35,32 @@
        <result property="totalCount" column="total_count"/>
    </resultMap>
    <select id="listPage" resultType="com.ruoyi.productionPlan.dto.ProductionPlanDto">
        SELECT *
        SELECT
        pp.*,
        pms.material_code AS materialCode,
        pms.specification AS specification,
        pms.material_id AS productMaterialId,
        pm.material_name AS productName,
        pm.base_unit
        FROM production_plan pp
        left join product_material_sku pms on pp.product_material_sku_id = pms.id
        left join product_material pm on pms.material_id = pm.id
        WHERE 1 = 1
        <if test="c.customerName != null and c.customerName != '' ">
            AND pp.customer_name LIKE CONCAT('%',#{c.customerName},'%')
        </if>
        <if test="c.productName != null and c.productName != '' ">
            AND pp.product_name LIKE CONCAT('%',#{c.productName},'%')
        </if>
        <if test="c.productSpec != null and c.productSpec != '' ">
            AND pp.product_spec LIKE CONCAT('%',#{c.productSpec},'%')
            AND pm.material_name LIKE CONCAT('%',#{c.productName},'%')
        </if>
        <if test="c.materialCode != null and c.materialCode != '' ">
            AND pp.material_code LIKE CONCAT('%',#{c.materialCode},'%')
            AND pms.material_code LIKE CONCAT('%',#{c.materialCode},'%')
        </if>
        <if test="c.specification != null and c.specification != '' ">
            AND pms.specification LIKE CONCAT('%',#{c.specification},'%')
        </if>
        <if test="c.applyNo != null and c.applyNo != '' ">
            AND pp.apply_no LIKE CONCAT('%',#{c.applyNo},'%')
        </if>
        <if test="c.startDate != null">
            AND pp.start_date &gt;= DATE_FORMAT(#{c.startDate},'%Y-%m-%d')
@@ -65,31 +72,51 @@
    <select id="selectSummaryByProductType" resultType="com.ruoyi.productionPlan.dto.ProductionPlanSummaryDto">
        SELECT
        material_code,
        product_name,
        product_spec,
        length,
        width,
        height,
        COALESCE(SUM(quantity),0) AS quantity,
        COALESCE(SUM(volume),0) AS volume
        FROM production_plan
        sku.material_code,
        pm.material_name AS product_name,
        sku.specification AS product_spec,
        pp.length,
        pp.width,
        pp.height,
        COALESCE(SUM(pp.quantity),0) AS quantity,
        COALESCE(SUM(pp.volume),0) AS volume
        FROM production_plan pp
        LEFT JOIN product_material_sku sku
        ON pp.product_material_sku_id = sku.id
        LEFT JOIN product_material pm
        ON sku.material_id = pm.id
        <where>
            <if test="materialCode != null and materialCode != ''">
                AND material_code LIKE CONCAT('%', #{materialCode}, '%')
                AND sku.material_code LIKE CONCAT('%', #{materialCode}, '%')
            </if>
            <if test="productName != null and productName != ''">
                AND product_name LIKE CONCAT('%', #{productName}, '%')
                AND pm.material_name LIKE CONCAT('%', #{productName}, '%')
            </if>
        </where>
        GROUP BY
        material_code,
        product_name,
        product_spec,
        length,
        width,
        height
        sku.material_code,
        pm.material_name,
        sku.specification,
        pp.length,
        pp.width,
        pp.height
    </select>
    <select id="selectWithMaterialByIds" resultType="com.ruoyi.productionPlan.dto.ProductionPlanDto">
        SELECT
        pp.*,
        pms.material_code AS materialCode,
        pms.specification AS specification,
        pm.material_name AS productName,
        pm.base_unit
        FROM production_plan pp
        LEFT JOIN product_material_sku pms ON pp.product_material_sku_id = pms.id
        LEFT JOIN product_material pm ON pms.material_id = pm.id
        WHERE pp.id IN
        <foreach collection="ids" item="id" open="(" separator="," close=")">
            #{id}
        </foreach>
        ORDER BY pp.id ASC
    </select>
</mapper>