导入物料BUG修改
This commit is contained in:
+14
-200
@@ -103,6 +103,10 @@ public class MaterialBaseInfoApplicationService {
|
|||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private MaterialBaseInfoMapper materialBaseInfoMapper;
|
private MaterialBaseInfoMapper materialBaseInfoMapper;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private MaterialBaseInfoAsyncService materialBaseInfoAsyncService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 分页查询物料基础信息列表
|
* 分页查询物料基础信息列表
|
||||||
*/
|
*/
|
||||||
@@ -472,202 +476,12 @@ public class MaterialBaseInfoApplicationService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
LoginUser loginUser = SecurityUtils.getLoginUser();
|
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||||
// 异步执行导入
|
// 通过独立的异步 Service 执行导入(避免类内部 self-invocation 导致 @Async 失效)
|
||||||
doImportData(tmpFile, fileName, loginUser);
|
materialBaseInfoAsyncService.doImportData(tmpFile, fileName, loginUser);
|
||||||
|
|
||||||
return AjaxResult.success("导入任务已提交,正在后台异步处理,请稍后查看导入结果日志");
|
return AjaxResult.success("导入任务已提交,正在后台异步处理,请稍后查看导入结果日志");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 异步执行物料主数据导入
|
|
||||||
*
|
|
||||||
* <p>@Async 确保在独立线程中执行,不阻塞接口响应。
|
|
||||||
* 每行独立事务,避免大事务锁表导致性能问题。</p>
|
|
||||||
*/
|
|
||||||
@Async
|
|
||||||
public void doImportData(File tmpFile, String fileName, LoginUser loginUser) {
|
|
||||||
long startTime = System.currentTimeMillis();
|
|
||||||
log.info("=== 物料主数据异步导入开始,文件:{},大小:{}KB ===", fileName, tmpFile.length() / 1024);
|
|
||||||
try {
|
|
||||||
List<MaterialBaseInfoDTO> 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<String> duplicateInFile = new HashSet<>();
|
|
||||||
int successCount = 0;
|
|
||||||
int totalCount = rows.size();
|
|
||||||
|
|
||||||
// 包装规格缓存:避免 5000 行数据对 system 服务发起 5000 次重复远程调用
|
|
||||||
Map<String, Map<String, Object>> 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<MaterialBaseInfo> wrapper = new LambdaQueryWrapper<MaterialBaseInfo>()
|
|
||||||
.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<String, Object> 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<String, Object> resultMap = JSON.parseObject(JSONObject.toJSONString(result), new TypeReference<Map<String, Object>>() {});
|
|
||||||
List<Map<String, Object>> rowsPack = 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;
|
|
||||||
}
|
|
||||||
if (cachedPack == null) {
|
|
||||||
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(buildImportMaterialGoodsRule(row));
|
|
||||||
materialBaseInfoDO.setMaterialCommon(null);
|
|
||||||
materialBaseInfoDO.setMaterialWarehouseControl(null);
|
|
||||||
materialBaseInfoDO.setMaterialBarCodeList(buildImportBarCodeList(row));
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 导入物料主数据(仅导入主表字段)- 原同步方法保留,供其他场景调用
|
* 导入物料主数据(仅导入主表字段)- 原同步方法保留,供其他场景调用
|
||||||
*/
|
*/
|
||||||
@@ -826,7 +640,7 @@ public class MaterialBaseInfoApplicationService {
|
|||||||
/**
|
/**
|
||||||
* 使用已存在主数据按名称/编码反查并回填ID
|
* 使用已存在主数据按名称/编码反查并回填ID
|
||||||
*/
|
*/
|
||||||
private void fillIdsFromExistingMasterData(MaterialBaseInfoDTO row) {
|
public void fillIdsFromExistingMasterData(MaterialBaseInfoDTO row) {
|
||||||
if (StringUtils.isBlank(row.getPackName()) && StringUtils.isNotBlank(row.getPackImport())) {
|
if (StringUtils.isBlank(row.getPackName()) && StringUtils.isNotBlank(row.getPackImport())) {
|
||||||
row.setPackName(row.getPackImport().trim());
|
row.setPackName(row.getPackImport().trim());
|
||||||
}
|
}
|
||||||
@@ -1146,7 +960,7 @@ public class MaterialBaseInfoApplicationService {
|
|||||||
/**
|
/**
|
||||||
* Excel 已填货主 ID、但未填组织时,从货主用户补全组织信息。
|
* Excel 已填货主 ID、但未填组织时,从货主用户补全组织信息。
|
||||||
*/
|
*/
|
||||||
private void fillOrganizationFromShipperIfNeeded(MaterialBaseInfoDTO row) {
|
public void fillOrganizationFromShipperIfNeeded(MaterialBaseInfoDTO row) {
|
||||||
if (isValidOrgId(row.getOrganizationId()) || row.getShipperId() == null) {
|
if (isValidOrgId(row.getOrganizationId()) || row.getShipperId() == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1205,7 +1019,7 @@ public class MaterialBaseInfoApplicationService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fillOrganizationName(MaterialBaseInfoDTO row, LoginUser loginUser) {
|
public void fillOrganizationName(MaterialBaseInfoDTO row, LoginUser loginUser) {
|
||||||
if (StringUtils.isNotBlank(row.getOrganizationName())) {
|
if (StringUtils.isNotBlank(row.getOrganizationName())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1225,7 +1039,7 @@ public class MaterialBaseInfoApplicationService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fillClassifyTypeAndUnit(MaterialBaseInfoDTO row) {
|
public void fillClassifyTypeAndUnit(MaterialBaseInfoDTO row) {
|
||||||
normalizeMaterialClassifyImportFields(row);
|
normalizeMaterialClassifyImportFields(row);
|
||||||
// 按组织从 system 主数据反查分类编码,与名称列表对齐
|
// 按组织从 system 主数据反查分类编码,与名称列表对齐
|
||||||
fillMaterialClassifyCodesFromSystem(row);
|
fillMaterialClassifyCodesFromSystem(row);
|
||||||
@@ -1406,7 +1220,7 @@ public class MaterialBaseInfoApplicationService {
|
|||||||
* 导入约定:「HS码/HS编码」与「物料条形码」为不同列。模板表头错位或两列误填相同 HS 时,
|
* 导入约定:「HS码/HS编码」与「物料条形码」为不同列。模板表头错位或两列误填相同 HS 时,
|
||||||
* barCode 会与 hsCode 相同;此时保留 hsCode,清空主表/子表条码,避免 HS 写入 bar_code。
|
* barCode 会与 hsCode 相同;此时保留 hsCode,清空主表/子表条码,避免 HS 写入 bar_code。
|
||||||
*/
|
*/
|
||||||
private void normalizeImportBarCodeWhenDuplicateOfHs(MaterialBaseInfoDTO row) {
|
public void normalizeImportBarCodeWhenDuplicateOfHs(MaterialBaseInfoDTO row) {
|
||||||
if (row == null) {
|
if (row == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1421,7 +1235,7 @@ public class MaterialBaseInfoApplicationService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<MaterialBarCodeDO> buildImportBarCodeList(MaterialBaseInfoDTO row) {
|
public List<MaterialBarCodeDO> buildImportBarCodeList(MaterialBaseInfoDTO row) {
|
||||||
if (StringUtils.isBlank(row.getBarCode())) {
|
if (StringUtils.isBlank(row.getBarCode())) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -1461,7 +1275,7 @@ public class MaterialBaseInfoApplicationService {
|
|||||||
return idObj == null ? null : Long.valueOf(String.valueOf(idObj));
|
return idObj == null ? null : Long.valueOf(String.valueOf(idObj));
|
||||||
}
|
}
|
||||||
|
|
||||||
private MaterialGoodsRuleDO buildImportMaterialGoodsRule(MaterialBaseInfoDTO row) {
|
public MaterialGoodsRuleDO buildImportMaterialGoodsRule(MaterialBaseInfoDTO row) {
|
||||||
String batchName = StringUtils.isNotBlank(row.getBatchRuleNameImport())
|
String batchName = StringUtils.isNotBlank(row.getBatchRuleNameImport())
|
||||||
? row.getBatchRuleNameImport().trim()
|
? row.getBatchRuleNameImport().trim()
|
||||||
: (StringUtils.isNotBlank(row.getBatchName())
|
: (StringUtils.isNotBlank(row.getBatchName())
|
||||||
@@ -1536,7 +1350,7 @@ public class MaterialBaseInfoApplicationService {
|
|||||||
.trim();
|
.trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Map<String, Object>> extractTableDataRows(Map<String, Object> resultMap) {
|
public List<Map<String, Object>> extractTableDataRows(Map<String, Object> resultMap) {
|
||||||
if (resultMap == null) {
|
if (resultMap == null) {
|
||||||
return new ArrayList<>();
|
return new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user