From cdc098fdd32b623da39d56b7b07cd77936920f8c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E7=A7=A6=E9=B8=BF=E5=B1=95?= <18031053041@163.com>
Date: Sat, 25 Jul 2026 10:09:26 +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
---
.../MaterialBaseInfoAsyncService.java | 259 ++++++++++++++++++
1 file changed, 259 insertions(+)
create mode 100644 mhd_wms/src/main/java/com/mhd/wms/application/service/materialBaseInfo/MaterialBaseInfoAsyncService.java
diff --git a/mhd_wms/src/main/java/com/mhd/wms/application/service/materialBaseInfo/MaterialBaseInfoAsyncService.java b/mhd_wms/src/main/java/com/mhd/wms/application/service/materialBaseInfo/MaterialBaseInfoAsyncService.java
new file mode 100644
index 000000000..0424c3865
--- /dev/null
+++ b/mhd_wms/src/main/java/com/mhd/wms/application/service/materialBaseInfo/MaterialBaseInfoAsyncService.java
@@ -0,0 +1,259 @@
+package com.mhd.wms.application.service.materialBaseInfo;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.TypeReference;
+import com.alibaba.fastjson2.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.mhd.common.core.domain.po.UserPo;
+import com.mhd.common.core.web.domain.AjaxResult;
+import com.mhd.system.api.SystemServiceFeign;
+import com.mhd.system.api.UserServiceFeign;
+import com.mhd.system.api.model.LoginUser;
+import com.mhd.wms.domain.materialBarCode.service.MaterialBarCodeDomainService;
+import com.mhd.wms.domain.materialBaseInfo.entity.MaterialBaseInfo;
+import com.mhd.wms.domain.materialBaseInfo.repository.facade.IMaterialBaseInfoService;
+import com.mhd.wms.domain.materialBaseInfo.repository.mapper.MaterialBaseInfoMapper;
+import com.mhd.wms.domain.materialBaseInfo.repository.po.MaterialBaseInfoPO;
+import com.mhd.wms.domain.materialBaseInfo.repository.todo.MaterialBaseInfoDO;
+import com.mhd.wms.domain.materialCommon.service.MaterialCommonDomainService;
+import com.mhd.wms.domain.materialCommon.repository.todo.MaterialCommonDO;
+import com.mhd.wms.domain.materialGoodsRule.service.MaterialGoodsRuleDomainService;
+import com.mhd.wms.domain.materialInventoryWarning.service.MaterialInventoryWarningDomainService;
+import com.mhd.wms.domain.materialWarehouseControl.service.MaterialWarehouseControlDomainService;
+import com.mhd.wms.domain.materialWarehouseControl.repository.todo.MaterialWarehouseControlDO;
+import com.mhd.wms.interfaces.dto.materialBaseInfo.MaterialBaseInfoDTO;
+import com.mhd.wms.util.MaterialBaseInfoImportExcelUtil;
+import com.mhd.wms.util.ZhTextNormalizeUtil;
+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.util.CollectionUtils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.math.BigDecimal;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 物料主数据异步导入服务
+ *
+ *
必须放在独立的 Service 类中,否则类内部 self-invocation 会导致 @Async 失效。
+ */
+@Slf4j
+@Service
+public class MaterialBaseInfoAsyncService {
+
+ @Autowired
+ private IMaterialBaseInfoService materialBaseInfoService;
+ @Autowired
+ private MaterialBaseInfoMapper materialBaseInfoMapper;
+ @Autowired
+ private MaterialBaseInfoApplicationService materialBaseInfoApplicationService;
+ @Autowired
+ private SystemServiceFeign systemServiceFeign;
+ @Autowired
+ private UserServiceFeign userServiceFeign;
+ @Autowired
+ private MaterialGoodsRuleDomainService materialGoodsRuleDomainService;
+ @Autowired
+ private MaterialCommonDomainService materialCommonDomainService;
+ @Autowired
+ private MaterialWarehouseControlDomainService materialWarehouseControlDomainService;
+ @Autowired
+ private MaterialInventoryWarningDomainService materialInventoryWarningDomainService;
+ @Autowired
+ private MaterialBarCodeDomainService materialBarCodeDomainService;
+
+ /**
+ * 异步执行物料主数据导入
+ *
+ * @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 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();
+
+ // 包装规格缓存
+ 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());
+ }
+ materialBaseInfoApplicationService.normalizeImportBarCodeWhenDuplicateOfHs(row);
+ materialBaseInfoApplicationService.fillIdsFromExistingMasterData(row);
+ materialBaseInfoApplicationService.fillOrganizationFromShipperIfNeeded(row);
+ if (!isValidOrgId(row.getOrganizationId()) && isValidOrgId(loginOrgId)) {
+ row.setOrganizationId(loginOrgId);
+ }
+ if (row.getTopOrganizationId() == null && isValidOrgId(loginTopOrgId)) {
+ row.setTopOrganizationId(loginTopOrgId);
+ }
+ materialBaseInfoApplicationService.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;
+ }
+ materialBaseInfoApplicationService.fillClassifyTypeAndUnit(row);
+
+ // 包装规格查询(带缓存)
+ 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