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>() {}); + List> rowsPack = materialBaseInfoApplicationService.extractTableDataRows(resultMap); + if (CollectionUtils.isEmpty(rowsPack)) { + packCache.put(packCacheKey, Collections.emptyMap()); + if (StringUtils.isNotBlank(row.getPackName())) { + errors.append("第").append(rowNum).append("行:包装规格「").append(row.getPackName()).append("」不存在或未在系统中维护;"); + } else { + errors.append("第").append(rowNum).append("行:包装规格未填写;"); + } + continue; + } + cachedPack = rowsPack.get(0); + packCache.put(packCacheKey, cachedPack); + } + if (cachedPack.isEmpty()) { + errors.append("第").append(rowNum).append("行:包装规格不存在(来自缓存);"); + continue; + } + String packCode = cachedPack.get("packCode") == null ? "" : String.valueOf(cachedPack.get("packCode")); + row.setPackCode(packCode); + String packId = cachedPack.get("packId") == null ? "" : String.valueOf(cachedPack.get("packId")); + if (StringUtils.isBlank(packId) || !StringUtils.isNumeric(packId)) { + errors.append("第").append(rowNum).append("行:包装规格数据异常(缺少包装ID);"); + continue; + } + row.setPackId(Long.valueOf(packId)); + String unitName = cachedPack.get("unitName") == null ? "" : String.valueOf(cachedPack.get("unitName")); + row.setUnitCode("EA"); + row.setUnitName(unitName); + + MaterialBaseInfoDO materialBaseInfoDO = new MaterialBaseInfoDO(); + BeanUtils.copyProperties(row, materialBaseInfoDO); + materialBaseInfoDO.setMaterialGoodsRule(materialBaseInfoApplicationService.buildImportMaterialGoodsRule(row)); + materialBaseInfoDO.setMaterialCommon(null); + materialBaseInfoDO.setMaterialWarehouseControl(null); + materialBaseInfoDO.setMaterialBarCodeList(materialBaseInfoApplicationService.buildImportBarCodeList(row)); + materialBaseInfoApplicationService.insert(materialBaseInfoDO); + Long materialBaseInfoId = materialBaseInfoDO.getMaterialBaseInfoId(); + + MaterialCommonDO materialCommonDO = materialBaseInfoDO.getMaterialCommon(); + if (materialCommonDO != null) { + materialCommonDO.setMaterialBaseInfoId(materialBaseInfoId); + materialCommonDomainService.insert(materialCommonDO); + } + MaterialWarehouseControlDO materialWarehouseControlDO = materialBaseInfoDO.getMaterialWarehouseControl(); + if (materialWarehouseControlDO != null) { + materialWarehouseControlDO.setMaterialBaseInfoId(materialBaseInfoId); + materialWarehouseControlDomainService.insert(materialWarehouseControlDO); + } + materialInventoryWarningDomainService.batchInsertOrUpdate(materialBaseInfoDO); + materialBarCodeDomainService.batchInsertOrUpdate(materialBaseInfoDO); + successCount++; + } catch (Exception e) { + log.error("物料主数据异步导入 - 第{}行处理异常", rowNum, e); + errors.append("第").append(rowNum).append("行:处理异常-").append(e.getMessage()).append(";"); + } + } + + long costTime = System.currentTimeMillis() - startTime; + if (errors.length() > 0) { + log.warn("=== 物料主数据异步导入完成,文件:{},总数:{},成功:{},失败:{},耗时:{}ms,失败原因:{} ===", + fileName, totalCount, successCount, totalCount - successCount, costTime, errors); + } else { + log.info("=== 物料主数据异步导入完成,文件:{},总数:{},成功:{},耗时:{}ms ===", + fileName, totalCount, successCount, costTime); + } + } finally { + try { + if (tmpFile.exists()) { + tmpFile.delete(); + } + } catch (Exception e) { + log.warn("物料主数据异步导入 - 临时文件清理失败:{}", e.getMessage()); + } + } + } + + private boolean isValidOrgId(Long id) { + return id != null && id > 0; + } +}