From 82909efe6b2ba1f9588dbee568ba7f6164befb93 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E7=A7=A6=E9=B8=BF=E5=B1=95?= <18031053041@163.com>
Date: Fri, 24 Jul 2026 18:49:14 +0800
Subject: [PATCH] =?UTF-8?q?=E5=AF=BC=E5=85=A5=E7=89=A9=E6=96=99BUG?=
=?UTF-8?q?=E4=BF=AE=E6=94=B9?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../com/mhd/system/api/BmsServiceFeign.java | 2 +-
.../com/mhd/system/api/OmsServiceFeign.java | 2 +-
.../com/mhd/system/api/WlhyServiceFeign.java | 2 +-
.../com/mhd/system/api/WmsServiceFeign.java | 2 +-
.../com/mhd/system/api/YmsServiceFeign.java | 2 +-
.../MaterialBaseInfoApplicationService.java | 241 +++++++++++++++++-
.../materialBaseInfo/MaterialBaseInfoApi.java | 10 +-
mhd_wms/src/main/resources/bootstrap.yml | 8 +-
8 files changed, 250 insertions(+), 19 deletions(-)
diff --git a/mhd-api/mhd-api-system/src/main/java/com/mhd/system/api/BmsServiceFeign.java b/mhd-api/mhd-api-system/src/main/java/com/mhd/system/api/BmsServiceFeign.java
index e18484e8d..91f2f8a11 100644
--- a/mhd-api/mhd-api-system/src/main/java/com/mhd/system/api/BmsServiceFeign.java
+++ b/mhd-api/mhd-api-system/src/main/java/com/mhd/system/api/BmsServiceFeign.java
@@ -18,7 +18,7 @@ import java.util.Set;
/**
* bms财务结算系统feign调用接口
*/
-@FeignClient(contextId = "BmsService", value = ServiceNameConstants.BMS_SERVICE, url = "http://10.102.192.3:8019",fallbackFactory = RemoteBmsFeignFallbackFactory.class, configuration = FeignAutoConfiguration.class)
+@FeignClient(contextId = "BmsService", value = ServiceNameConstants.BMS_SERVICE, url = "http://10.33.0.109:8019",fallbackFactory = RemoteBmsFeignFallbackFactory.class, configuration = FeignAutoConfiguration.class)
public interface BmsServiceFeign {
diff --git a/mhd-api/mhd-api-system/src/main/java/com/mhd/system/api/OmsServiceFeign.java b/mhd-api/mhd-api-system/src/main/java/com/mhd/system/api/OmsServiceFeign.java
index e34bb9cd4..03d48d2f4 100644
--- a/mhd-api/mhd-api-system/src/main/java/com/mhd/system/api/OmsServiceFeign.java
+++ b/mhd-api/mhd-api-system/src/main/java/com/mhd/system/api/OmsServiceFeign.java
@@ -16,7 +16,7 @@ import org.springframework.web.bind.annotation.RequestBody;
import java.util.List;
import java.util.concurrent.CompletableFuture;
-@FeignClient(contextId = "OmsService", value = ServiceNameConstants.OMS_SERVICE,url = "http://10.102.192.4:8017", fallbackFactory = RemoteOmsFeignFallbackFactory.class, configuration = FeignAutoConfiguration.class)
+@FeignClient(contextId = "OmsService", value = ServiceNameConstants.OMS_SERVICE,url = "http://10.33.0.99:8017", fallbackFactory = RemoteOmsFeignFallbackFactory.class, configuration = FeignAutoConfiguration.class)
public interface OmsServiceFeign {
diff --git a/mhd-api/mhd-api-system/src/main/java/com/mhd/system/api/WlhyServiceFeign.java b/mhd-api/mhd-api-system/src/main/java/com/mhd/system/api/WlhyServiceFeign.java
index e4a817238..ba9a45148 100644
--- a/mhd-api/mhd-api-system/src/main/java/com/mhd/system/api/WlhyServiceFeign.java
+++ b/mhd-api/mhd-api-system/src/main/java/com/mhd/system/api/WlhyServiceFeign.java
@@ -26,7 +26,7 @@ import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
-@FeignClient(contextId = "commonWlhyServiceFeign",value = ServiceNameConstants.WLHY_SERVICE,url = "http://10.102.192.30:8014",configuration = FeignAutoConfiguration.class)
+@FeignClient(contextId = "commonWlhyServiceFeign",value = ServiceNameConstants.WLHY_SERVICE,url = "http://10.33.0.129:8014",configuration = FeignAutoConfiguration.class)
public interface WlhyServiceFeign {
/**
diff --git a/mhd-api/mhd-api-system/src/main/java/com/mhd/system/api/WmsServiceFeign.java b/mhd-api/mhd-api-system/src/main/java/com/mhd/system/api/WmsServiceFeign.java
index 743be7c29..688e5ea7c 100644
--- a/mhd-api/mhd-api-system/src/main/java/com/mhd/system/api/WmsServiceFeign.java
+++ b/mhd-api/mhd-api-system/src/main/java/com/mhd/system/api/WmsServiceFeign.java
@@ -21,7 +21,7 @@ import java.util.Map;
* @description: TODO
* @date 2024/5/10 8:58
**/
-@FeignClient(contextId = "remoteWmsServiceFeign",value = ServiceNameConstants.WMS_SERVICE, url = "http://10.102.192.3:8016", fallbackFactory = RemoteWmsFallbackFactory.class, configuration = FeignAutoConfiguration.class)
+@FeignClient(contextId = "remoteWmsServiceFeign",value = ServiceNameConstants.WMS_SERVICE, url = "http://10.33.0.109:8016", fallbackFactory = RemoteWmsFallbackFactory.class, configuration = FeignAutoConfiguration.class)
public interface WmsServiceFeign {
/**
diff --git a/mhd-api/mhd-api-system/src/main/java/com/mhd/system/api/YmsServiceFeign.java b/mhd-api/mhd-api-system/src/main/java/com/mhd/system/api/YmsServiceFeign.java
index dfac7337f..48d76d01e 100644
--- a/mhd-api/mhd-api-system/src/main/java/com/mhd/system/api/YmsServiceFeign.java
+++ b/mhd-api/mhd-api-system/src/main/java/com/mhd/system/api/YmsServiceFeign.java
@@ -9,7 +9,7 @@ import com.mhd.system.api.feign.FeignAutoConfiguration;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*;
-@FeignClient(contextId = "YmsService", value = ServiceNameConstants.YMS_SERVICE,fallbackFactory = RemoteBmsFeignFallbackFactory.class,url = "http://10.102.192.4:8018", configuration = FeignAutoConfiguration.class)
+@FeignClient(contextId = "YmsService", value = ServiceNameConstants.YMS_SERVICE,fallbackFactory = RemoteBmsFeignFallbackFactory.class,url = "http://10.33.0.99:8018", configuration = FeignAutoConfiguration.class)
public interface YmsServiceFeign {
/**
diff --git a/mhd_wms/src/main/java/com/mhd/wms/application/service/materialBaseInfo/MaterialBaseInfoApplicationService.java b/mhd_wms/src/main/java/com/mhd/wms/application/service/materialBaseInfo/MaterialBaseInfoApplicationService.java
index 401e4e028..7dc41f623 100644
--- a/mhd_wms/src/main/java/com/mhd/wms/application/service/materialBaseInfo/MaterialBaseInfoApplicationService.java
+++ b/mhd_wms/src/main/java/com/mhd/wms/application/service/materialBaseInfo/MaterialBaseInfoApplicationService.java
@@ -49,12 +49,15 @@ import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
+import java.io.File;
+import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.*;
import java.util.stream.Collectors;
@@ -436,8 +439,237 @@ public class MaterialBaseInfoApplicationService {
return materialBaseInfoDomainService.getInfoByNoOrderReceipt(materialBaseInfoDO);
}
+
/**
- * 导入物料主数据(仅导入主表字段)
+ * 导入物料主数据(异步入口)
+ *
+ *
接口层调用此方法,将文件保存到临时目录后立即返回"异步处理中",
+ * 实际导入由 {@link #doImportData(File, String, LoginUser)} 异步执行,
+ * 避免大数据量时接口超时。
+ */
+ public AjaxResult importDataAsync(MultipartFile file) {
+ if (file == null || file.isEmpty()) {
+ throw new ServiceException("上传文件不能为空");
+ }
+ String fileName = file.getOriginalFilename();
+ if (org.apache.commons.lang3.StringUtils.isBlank(fileName)
+ || (!fileName.endsWith(".xls") && !fileName.endsWith(".xlsx"))) {
+ throw new ServiceException("请上传xls或xlsx格式文件");
+ }
+
+ // 保存文件到临时目录
+ String tmpDir = System.getProperty("java.io.tmpdir") + File.separator + "material_import";
+ File dir = new File(tmpDir);
+ if (!dir.exists()) {
+ dir.mkdirs();
+ }
+ String tmpFileName = System.currentTimeMillis() + "_" + fileName;
+ File tmpFile = new File(dir, tmpFileName);
+ try (FileOutputStream fos = new FileOutputStream(tmpFile)) {
+ fos.write(file.getBytes());
+ } catch (Exception e) {
+ throw new ServiceException("文件保存失败:" + e.getMessage());
+ }
+
+ LoginUser loginUser = SecurityUtils.getLoginUser();
+ // 异步执行导入
+ doImportData(tmpFile, fileName, loginUser);
+
+ return AjaxResult.success("导入任务已提交,正在后台异步处理,请稍后查看导入结果日志");
+ }
+
+ /**
+ * 异步执行物料主数据导入
+ *
+ * @Async 确保在独立线程中执行,不阻塞接口响应。
+ * 每行独立事务,避免大事务锁表导致性能问题。
+ */
+ @Async
+ public void doImportData(File tmpFile, String fileName, LoginUser loginUser) {
+ long startTime = System.currentTimeMillis();
+ log.info("=== 物料主数据异步导入开始,文件:{},大小:{}KB ===", fileName, tmpFile.length() / 1024);
+ try {
+ List rows;
+ try (InputStream is = new java.io.FileInputStream(tmpFile)) {
+ rows = MaterialBaseInfoImportExcelUtil.importRows(is);
+ } catch (Exception e) {
+ log.error("物料主数据异步导入失败 - 文件解析异常:{}", e.getMessage(), e);
+ return;
+ }
+
+ if (CollectionUtils.isEmpty(rows)) {
+ log.warn("物料主数据异步导入结束 - 导入数据为空");
+ return;
+ }
+
+ Long loginOrgId = null;
+ Long loginTopOrgId = null;
+ if (loginUser != null && loginUser.getUserPo() != null) {
+ loginOrgId = loginUser.getUserPo().getOrganizationId();
+ loginTopOrgId = loginUser.getUserPo().getTopOrganizationId();
+ }
+
+ StringBuilder errors = new StringBuilder();
+ Set duplicateInFile = new HashSet<>();
+ int successCount = 0;
+ int totalCount = rows.size();
+
+ // 包装规格缓存:避免 5000 行数据对 system 服务发起 5000 次重复远程调用
+ Map> packCache = new HashMap<>();
+
+ for (int i = 0; i < rows.size(); i++) {
+ MaterialBaseInfoDTO row = rows.get(i);
+ int rowNum = i + 2;
+ if (row == null) {
+ continue;
+ }
+ try {
+ if (StringUtils.isBlank(row.getMaterialCode())) {
+ errors.append("第").append(rowNum).append("行:物料编码不能为空;");
+ continue;
+ }
+ if (StringUtils.isBlank(row.getMaterialName())) {
+ errors.append("第").append(rowNum).append("行:物料名称不能为空;");
+ continue;
+ }
+ if (row.getNullify() == null) {
+ row.setNullify(2);
+ }
+ if (row.getCommon() == null) {
+ row.setCommon(2);
+ }
+ if (StringUtils.isBlank(row.getHsCode()) && StringUtils.isNotBlank(row.getHsCodeImport())) {
+ row.setHsCode(row.getHsCodeImport().trim());
+ }
+ normalizeImportBarCodeWhenDuplicateOfHs(row);
+ fillIdsFromExistingMasterData(row);
+ fillOrganizationFromShipperIfNeeded(row);
+ if (!isValidOrgId(row.getOrganizationId()) && isValidOrgId(loginOrgId)) {
+ row.setOrganizationId(loginOrgId);
+ }
+ if (row.getTopOrganizationId() == null && isValidOrgId(loginTopOrgId)) {
+ row.setTopOrganizationId(loginTopOrgId);
+ }
+ fillOrganizationName(row, loginUser);
+
+ String shipperCode = StringUtils.defaultString(row.getShipperCode(), "");
+ String duplicateKey = row.getOrganizationId() + "|" + shipperCode + "|" + row.getMaterialCode();
+ if (!duplicateInFile.add(duplicateKey)) {
+ errors.append("第").append(rowNum).append("行:文件内存在重复物料(组织+货主编码+物料编码);");
+ continue;
+ }
+
+ LambdaQueryWrapper wrapper = new LambdaQueryWrapper()
+ .eq(MaterialBaseInfo::getDelFlag, 1)
+ .eq(MaterialBaseInfo::getOrganizationId, row.getOrganizationId())
+ .eq(MaterialBaseInfo::getMaterialCode, row.getMaterialCode());
+ if (StringUtils.isBlank(row.getShipperCode())) {
+ wrapper.and(w -> w.isNull(MaterialBaseInfo::getShipperCode).or().eq(MaterialBaseInfo::getShipperCode, ""));
+ } else {
+ wrapper.eq(MaterialBaseInfo::getShipperCode, row.getShipperCode());
+ }
+ if (materialBaseInfoService.count(wrapper) > 0) {
+ errors.append("第").append(rowNum).append("行:物料已存在(组织+货主编码+物料编码);");
+ continue;
+ }
+ fillClassifyTypeAndUnit(row);
+
+ // 包装规格查询(带本地缓存,避免 5000 行对 system 服务发起 5000 次远程调用)
+ String packCacheKey = row.getOrganizationId() + "|" + StringUtils.defaultString(row.getPackName());
+ Map cachedPack = packCache.get(packCacheKey);
+ if (cachedPack == null) {
+ Object result = systemServiceFeign.getPackList(null, row.getPackName(), row.getOrganizationId());
+ if (result == null) {
+ // 缓存空结果,避免重复查询不存在的包装规格
+ packCache.put(packCacheKey, Collections.emptyMap());
+ errors.append("第").append(rowNum).append("行:包装规格查询无返回,请稍后重试或检查系统服务;");
+ continue;
+ }
+ Map resultMap = JSON.parseObject(JSONObject.toJSONString(result), new TypeReference