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;
|
}
|
}
|
}
|