gongchunyi
3 天以前 2f58ddd5085b9e51bcc4b7e1ad2e850a95e43bbd
feat: 同步物料编码数据
已添加12个文件
已修改4个文件
683 ■■■■■ 文件已修改
doc/宁夏-中盛建材.sql 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/framework/config/AliDingConfig.java 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/controller/ProductMaterialController.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/enums/MaterialConfigTypeEnum.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/mapper/ProductMaterialConfigMapper.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/mapper/ProductMaterialMapper.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/pojo/ProductMaterial.java 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/pojo/ProductMaterialConfig.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/ProductMaterialConfigService.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/ProductMaterialService.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/impl/ProductMaterialConfigServiceImpl.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/impl/ProductMaterialServiceImpl.java 306 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/task/ProductMaterialTask.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/productionPlan/service/impl/ProductionPlanServiceImpl.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-zsjc.yml 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/production/ProductMaterialMapper.xml 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
doc/ÄþÏÄ-ÖÐÊ¢½¨²Ä.sql
@@ -107,3 +107,38 @@
    index idx_production_plan_id (production_plan_id),
    unique idx_product_order_production_plan (product_order_id, production_plan_id)
);
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 '宁夏中创绿能实业集团有限公司',
    `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`)
) 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`
(
    `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 ='物料信息表配置表';
src/main/java/com/ruoyi/framework/config/AliDingConfig.java
@@ -42,9 +42,15 @@
    private String appType;
    /**
     * å®œæ­è¡¨å•ID
     * é”€å”®ç”Ÿäº§éœ€æ±‚-宜搭表单ID
     */
    private String formUuid;
    private String producePlanFormUuid;
    /**
     * ç‰©æ–™ç¼–码-宜搭表单ID
     */
    private String materialCodeFormUuid;
    /**
     * å®œæ­åº”用密钥
src/main/java/com/ruoyi/production/controller/ProductMaterialController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,37 @@
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.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;
/**
 * <br>
 * äº§å“ç‰©æ–™ä¿¡æ¯æŽ§åˆ¶å±‚
 * </br>
 *
 * @author deslrey
 * @version 1.0
 * @since 2026/03/11 16:38
 */
@RestController
@RequestMapping("/productMaterial")
public class ProductMaterialController {
    @Autowired
    private ProductMaterialService productMaterialService;
    @GetMapping("/loadData")
    @ApiOperation("拉取物料编码数据")
    @Log(title = "拉取物料编码数据", businessType = BusinessType.INSERT)
    public AjaxResult loadProductMaterialData() {
        productMaterialService.loadProductMaterialData();
        return AjaxResult.success();
    }
}
src/main/java/com/ruoyi/production/enums/MaterialConfigTypeEnum.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,25 @@
package com.ruoyi.production.enums;
/**
 * <br>
 * ç‰©æ–™é…ç½®ç±»åž‹æžšä¸¾
 * </br>
 *
 * @author deslrey
 * @version 1.0
 * @since 2026/03/11 17:24
 */
public enum MaterialConfigTypeEnum {
    /**
     * ç‰©æ–™ç±»åž‹
     * å¯¹åº”数据库 config_type = MATERIAL_TYPE
     */
    MATERIAL_TYPE,
    /**
     * å­˜è´§ç±»åˆ«
     * å¯¹åº”数据库 config_type = INVENTORY_CAT
     */
    INVENTORY_CAT
}
src/main/java/com/ruoyi/production/mapper/ProductMaterialConfigMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,16 @@
package com.ruoyi.production.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.production.pojo.ProductMaterialConfig;
/**
 * <br>
 * ç‰©æ–™ä¿¡æ¯è¡¨é…ç½®Mapper
 * </br>
 *
 * @author deslrey
 * @version 1.0
 * @since 2026/03/11 16:58
 */
public interface ProductMaterialConfigMapper extends BaseMapper<ProductMaterialConfig> {
}
src/main/java/com/ruoyi/production/mapper/ProductMaterialMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,16 @@
package com.ruoyi.production.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.production.pojo.ProductMaterial;
/**
 * <br>
 * äº§å“ç‰©æ–™ä¿¡æ¯Mapper
 * </br>
 *
 * @author deslrey
 * @version 1.0
 * @since 2026/03/11 16:35
 */
public interface ProductMaterialMapper extends BaseMapper<ProductMaterial> {
}
src/main/java/com/ruoyi/production/pojo/ProductMaterial.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,87 @@
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/11 16:31
 */
@Data
@TableName("product_material")
@ApiModel(value = "ProductMaterial", description = "产品物料信息表")
public class ProductMaterial {
    @TableId(type = IdType.AUTO)
    @ApiModelProperty("主键ID")
    private Integer id;
    @ApiModelProperty("租户ID")
    private Long tenantId;
    @ApiModelProperty("物料类型ID")
    private Integer materialTypeId;
    @ApiModelProperty("存货类别ID")
    private Integer inventoryCategoryId;
    @ApiModelProperty("标识编码")
    private String identifierCode;
    @ApiModelProperty("物料代码")
    private String materialCode;
    @ApiModelProperty("产品名称")
    private String productName;
    @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;
    @ApiModelProperty("创建时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime createTime;
    @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/ProductMaterialConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,33 @@
package com.ruoyi.production.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
 * <br>
 * ç‰©æ–™ä¿¡æ¯è¡¨é…ç½®è¡¨
 * </br>
 *
 * @author deslrey
 * @version 1.0
 * @since 2026/03/11 16:57
 */
@Data
@TableName("product_material_config")
@ApiModel(value = "ProductMaterialConfig", description = "物料信息配置表")
public class ProductMaterialConfig {
    @TableId(type = IdType.AUTO)
    @ApiModelProperty("主键ID")
    private Integer id;
    @ApiModelProperty("区分类型: MATERIAL_TYPE æˆ– INVENTORY_CAT")
    private String configType;
    @ApiModelProperty("配置名称")
    private String configName;
}
src/main/java/com/ruoyi/production/service/ProductMaterialConfigService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,16 @@
package com.ruoyi.production.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.production.pojo.ProductMaterialConfig;
/**
 * <br>
 * ç‰©æ–™ä¿¡æ¯è¡¨é…ç½®æŽ¥å£
 * </br>
 *
 * @author deslrey
 * @version 1.0
 * @since 2026/03/11 16:58
 */
public interface ProductMaterialConfigService extends IService<ProductMaterialConfig> {
}
src/main/java/com/ruoyi/production/service/ProductMaterialService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,20 @@
package com.ruoyi.production.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.production.pojo.ProductMaterial;
/**
 * <br>
 * äº§å“ç‰©æ–™ä¿¡æ¯æŽ¥å£
 * </br>
 *
 * @author deslrey
 * @version 1.0
 * @since 2026/03/11 16:35
 */
public interface ProductMaterialService extends IService<ProductMaterial> {
    void loadProductMaterialData();
    void syncProductMaterialJob();
}
src/main/java/com/ruoyi/production/service/impl/ProductMaterialConfigServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,24 @@
package com.ruoyi.production.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
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;
/**
 * <br>
 * ç‰©æ–™ä¿¡æ¯è¡¨é…ç½®æŽ¥å£å®žçŽ°ç±»
 * </br>
 *
 * @author deslrey
 * @version 1.0
 * @since 2026/03/11 16:59
 */
@Slf4j
@Service
public class ProductMaterialConfigServiceImpl extends ServiceImpl<ProductMaterialConfigMapper, ProductMaterialConfig> implements ProductMaterialConfigService {
}
src/main/java/com/ruoyi/production/service/impl/ProductMaterialServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,306 @@
package com.ruoyi.production.service.impl;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
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.utils.StringUtils;
import com.ruoyi.common.utils.http.HttpUtils;
import com.ruoyi.framework.config.AliDingConfig;
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.service.ProductMaterialConfigService;
import com.ruoyi.production.service.ProductMaterialService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
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.concurrent.locks.ReentrantLock;
/**
 * <br>
 * äº§å“ç‰©æ–™ä¿¡æ¯æŽ¥å£å®žçŽ°ç±»
 * </br>
 *
 * @author deslrey
 * @version 1.0
 * @since 2026/03/11 16:36
 */
@Slf4j
@Service
public class ProductMaterialServiceImpl extends ServiceImpl<ProductMaterialMapper, ProductMaterial> implements ProductMaterialService {
    @Autowired
    private AliDingConfig aliDingConfig;
    @Autowired
    private ProductMaterialConfigService productMaterialConfigService;
    /**
     * åŒæ­¥é”ï¼Œé˜²æ­¢æ‰‹åŠ¨å’Œå®šæ—¶ä»»åŠ¡åŒæ—¶æ‰§è¡Œ
     */
    private final ReentrantLock syncLock = new ReentrantLock();
    @Override
    public void loadProductMaterialData() {
        syncProductMaterialData(1);
    }
    @Override
    public void syncProductMaterialJob() {
        syncProductMaterialData(2);
    }
    /**
     * åŒæ­¥æ•°æ®
     */
    @Transactional(rollbackFor = Exception.class)
    public void syncProductMaterialData(Integer dataSyncType) {
        if (!syncLock.tryLock()) {
            log.warn("同步正在进行中,本次 {} åŒæ­¥è¯·æ±‚被跳过", dataSyncType == 1 ? "手动" : "定时任务");
            return;
        }
        try {
            // èŽ·å– AccessToken
            String accessToken = getAccessToken();
            if (StringUtils.isEmpty(accessToken)) {
                return;
            }
            // èŽ·å–æœ¬åœ°æœ€åŽåŒæ­¥æ—¶é—´
            LocalDateTime lastSyncTime = getLastSyncTime();
            log.info("开始物料编码增量同步,本地最后修改时间: {}", lastSyncTime);
            int pageNumber = 1;
            int pageSize = 50;
            boolean hasMore = true;
            int totalSynced = 0;
            while (hasMore) {
                // æŸ¥è¯¢å‚æ•°
                JSONObject searchParam = buildSearchParam(lastSyncTime, pageNumber, pageSize);
                // è°ƒç”¨å®œæ­æŽ¥å£æ‹‰å–数据
                String dataRes = HttpUtils.sendPostJson(
                        aliDingConfig.getSearchFormDataUrl(),
                        searchParam.toJSONString(),
                        StandardCharsets.UTF_8.name(),
                        null,
                        accessToken
                );
                if (StringUtils.isEmpty(dataRes)) {
                    log.warn("第 {} é¡µæ‹‰å–数据为空", pageNumber);
                    break;
                }
                JSONObject resultObj = JSON.parseObject(dataRes);
                JSONArray dataArr = resultObj.getJSONArray("data");
                Integer totalCount = resultObj.getInteger("totalCount");
                if (dataArr == null || dataArr.isEmpty()) {
                    log.info("没有更多新数据需要同步");
                    break;
                }
                // è§£æžå¹¶ä¿å­˜æ•°æ®
                List<ProductMaterial> list = parseProductMaterials(dataArr, totalCount);
                if (!list.isEmpty()) {
                    // å¤„理更新或新增
                    int affected = processSaveOrUpdate(list);
                    totalSynced += affected;
                }
                // åˆ¤æ–­æ˜¯å¦è¿˜æœ‰ä¸‹ä¸€é¡µ
                hasMore = (pageNumber * pageSize) < totalCount;
                pageNumber++;
                log.info("正在同步第 {} é¡µï¼Œå½“前已同步 {}/{}", pageNumber - 1, totalSynced, totalCount);
            }
            log.info("物料数据同步完成,共同步 {} æ¡æ•°æ®", totalSynced);
        } catch (Exception e) {
            log.error("同步物料编码异常", e);
        } finally {
            // é‡Šæ”¾é”
            syncLock.unlock();
        }
    }
    private String getAccessToken() {
        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");
        if (StringUtils.isEmpty(accessToken)) {
            log.error("获取钉钉AccessToken失败: {}", tokenRes);
        }
        return accessToken;
    }
    private LocalDateTime getLastSyncTime() {
        LambdaQueryWrapper<ProductMaterial> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.orderByDesc(ProductMaterial::getFormModifiedTime).last("LIMIT 1");
        ProductMaterial lastRecord = this.getOne(queryWrapper);
        return lastRecord != null ? lastRecord.getFormModifiedTime() : null;
    }
    private JSONObject buildSearchParam(LocalDateTime lastSyncTime, int pageNumber, int pageSize) {
        JSONObject searchParam = new JSONObject();
        searchParam.put("appType", aliDingConfig.getAppType());
        searchParam.put("systemToken", aliDingConfig.getSystemToken());
        searchParam.put("userId", aliDingConfig.getUserId());
        searchParam.put("formUuid", aliDingConfig.getMaterialCodeFormUuid());
        searchParam.put("currentPage", pageNumber);
        searchParam.put("pageSize", pageSize);
        JSONArray searchConditions = new JSONArray();
        JSONObject statusCondition = new JSONObject();
        statusCondition.put("key", "processInstanceStatus");
        JSONArray statusValueArray = new JSONArray();
        statusValueArray.add("COMPLETED");
        statusCondition.put("value", statusValueArray);
        statusCondition.put("type", "ARRAY");
        statusCondition.put("operator", "in");
        statusCondition.put("componentName", "SelectField");
        searchConditions.add(statusCondition);
        JSONObject resultCondition = new JSONObject();
        resultCondition.put("key", "processApprovedResult");
        JSONArray resultValueArray = new JSONArray();
        resultValueArray.add("agree");
        resultCondition.put("value", resultValueArray);
        resultCondition.put("type", "ARRAY");
        resultCondition.put("operator", "in");
        resultCondition.put("componentName", "SelectField");
        searchConditions.add(resultCondition);
        searchParam.put("searchFieldJson", searchConditions.toJSONString());
        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"));
            searchParam.put("modifiedFromTimeGMT", startTime);
        }
        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<>();
        LocalDateTime now = LocalDateTime.now();
        for (int i = 0; i < dataArr.size(); i++) {
            JSONObject item = dataArr.getJSONObject(i);
            String formInstanceId = item.getString("formInstanceId");
            JSONObject originator = item.getJSONObject("originator");
            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("宁夏中创绿能实业集团有限公司");
            material.setFormModifiedTime(parseUtcTime(item.getString("modifiedTimeGMT")));
            material.setCreateTime(now);
            material.setUpdateTime(now);
            list.add(material);
        }
        return list;
    }
    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));
        if (config == null) {
            config = new ProductMaterialConfig();
            config.setConfigName(name);
            config.setConfigType(type);
            productMaterialConfigService.save(config);
        }
        return config.getId();
    }
    private int processSaveOrUpdate(List<ProductMaterial> 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()));
            if (exist == null) {
                this.save(material);
                affected++;
                log.info("新增物料数据 formInstanceId={}", material.getFormInstanceId());
            } else {
                if (exist.getFormModifiedTime() == null || !exist.getFormModifiedTime().equals(material.getFormModifiedTime())) {
                    material.setId(exist.getId());
                    material.setCreateTime(exist.getCreateTime());
                    this.updateById(material);
                    affected++;
                    log.info("更新物料数据 formInstanceId={}", material.getFormInstanceId());
                }
            }
        }
        return affected;
    }
    private LocalDateTime parseUtcTime(String utcString) {
        if (StringUtils.isEmpty(utcString)) {
            return null;
        }
        try {
            OffsetDateTime odt = OffsetDateTime.parse(utcString);
            return odt.toLocalDateTime();
        } catch (DateTimeParseException ex) {
            log.warn("解析时间 {} å¤±è´¥: {}", utcString, ex.getMessage());
            return null;
        }
    }
}
src/main/java/com/ruoyi/production/task/ProductMaterialTask.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,27 @@
package com.ruoyi.production.task;
import com.ruoyi.production.service.ProductMaterialService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
/**
 * <br>
 *
 * </br>
 *
 * @author deslrey
 * @version 1.0
 * @since 2026/03/11 16:39
 */
@Component
public class ProductMaterialTask {
    @Autowired
    private ProductMaterialService productMaterialService;
    @Scheduled(cron = "0 0 * * * ?")
    public void syncProdDataJob() {
        productMaterialService.syncProductMaterialJob();
    }
}
src/main/java/com/ruoyi/productionPlan/service/impl/ProductionPlanServiceImpl.java
@@ -306,7 +306,7 @@
        searchParam.put("appType", aliDingConfig.getAppType());
        searchParam.put("systemToken", aliDingConfig.getSystemToken());
        searchParam.put("userId", aliDingConfig.getUserId());
        searchParam.put("formUuid", aliDingConfig.getFormUuid());
        searchParam.put("formUuid", aliDingConfig.getProducePlanFormUuid());
        searchParam.put("currentPage", pageNumber);
        searchParam.put("pageSize", pageSize);
src/main/resources/application-zsjc.yml
@@ -30,7 +30,8 @@
    app-secret: "fO07qSZC5SMLw9It3Ydd3BuoFyVbRlsWXUnVr2kwPJXz0OpUntCAO5dqnr8G7zq5"
    user-id: "290166234126410562"
    app-type: "APP_UY8XMO7GNA8OF08EFNVQ"
    form-uuid: "FORM-4IA66891C5H3QWMDBSGO05C0OX9628GRPYF7L8"
    produce-plan-form-uuid: "FORM-4IA66891C5H3QWMDBSGO05C0OX9628GRPYF7L8"
    material-code-form-uuid: "FORM-8I666N718RW4ALO4ETYDDB0U6U8T3EIWTQ0ALL1"
    system-token: "4J766L91OFH3V612E7LG5B4DI8M13MQF9VF7LG4"
    # æŽ¥å£åœ°å€
    access-token-url: "https://oapi.dingtalk.com/gettoken"
src/main/resources/mapper/production/ProductMaterialMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,26 @@
<?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.ProductMaterialMapper">
    <resultMap id="ProductMaterialResultMap" type="com.ruoyi.production.pojo.ProductMaterial">
        <id property="id" column="id"/>
        <result property="tenantId" column="tenant_id"/>
        <result property="materialType" column="material_type"/>
        <result property="inventoryCategory" column="inventory_category"/>
        <result property="identifierCode" column="identifier_code"/>
        <result property="materialCode" column="material_code"/>
        <result property="productName" column="product_name"/>
        <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"/>
    </resultMap>
</mapper>