Merge remote-tracking branch 'origin/dev_WMS20260401' into dev_WMS20260401

This commit is contained in:
zhaoqing
2026-05-29 10:56:42 +08:00
3 changed files with 66 additions and 26 deletions
@@ -435,12 +435,6 @@ public class MaterialBaseInfoApplicationService {
continue;
}
if (!isValidOrgId(row.getOrganizationId()) && isValidOrgId(loginOrgId)) {
row.setOrganizationId(loginOrgId);
}
if (row.getTopOrganizationId() == null && isValidOrgId(loginTopOrgId)) {
row.setTopOrganizationId(loginTopOrgId);
}
if (row.getNullify() == null) {
row.setNullify(2);
}
@@ -450,11 +444,16 @@ public class MaterialBaseInfoApplicationService {
if (StringUtils.isBlank(row.getHsCode()) && StringUtils.isNotBlank(row.getHsCodeImport())) {
row.setHsCode(row.getHsCodeImport().trim());
}
// Excel 表头错位或重复时,HS 可能被读入「物料条形码」列;与 HS 完全相同时视为误填,保留 hsCode、清空条码
normalizeImportBarCodeWhenDuplicateOfHs(row);
// 先解析货主并补全组织,再做重复/存在性校验(组织维度依赖 organizationId
// 客户模板通常只有「货主名称」无「货主id」:须先按名称解析货主,再回填组织;勿提前写入登录组织以免组织校验拦截货主匹配
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(), "");
@@ -677,15 +676,28 @@ public class MaterialBaseInfoApplicationService {
String shipperName = StringUtils.isNotBlank(row.getShipperName()) ? row.getShipperName().trim() : null;
String shipperCode = StringUtils.isNotBlank(row.getShipperCode()) ? row.getShipperCode().trim() : null;
UserPo matched = resolveShipperUserForImport(shipperName, shipperCode, importOrgId, true);
if (matched == null) {
matched = resolveShipperUserForImport(shipperName, shipperCode, importOrgId, false);
}
if (matched != null) {
applyUserPoToMaterialImportRow(row, matched);
}
}
/**
* @param enforceOrganization true=货主 organizationId 须与 importOrgId 一致;false=跨组织也接受并回填货主所属组织
*/
private UserPo resolveShipperUserForImport(String shipperName, String shipperCode, Long importOrgId,
boolean enforceOrganization) {
if (StringUtils.isNotBlank(shipperName)) {
for (String nameVariant : ZhTextNormalizeUtil.nameLookupVariants(shipperName)) {
try {
AjaxResult ajaxResult = userServiceFeign.getInfoByName(nameVariant);
if ("200".equals(String.valueOf(ajaxResult.get("code"))) && ajaxResult.get("data") != null) {
UserPo userPo = JSON.parseObject(JSONObject.toJSONString(ajaxResult.get("data")), UserPo.class);
if (userPo != null && shipperUserMatchesImportOrganization(userPo, importOrgId)) {
applyUserPoToMaterialImportRow(row, userPo);
return;
if (userPo != null && shipperUserMatchesImportOrganization(userPo, importOrgId, enforceOrganization)) {
return userPo;
}
}
} catch (Exception e) {
@@ -697,7 +709,7 @@ public class MaterialBaseInfoApplicationService {
try {
AjaxResult shipperListResult = userServiceFeign.userShipperListAll();
if (!"200".equals(String.valueOf(shipperListResult.get("code"))) || shipperListResult.get("data") == null) {
return;
return null;
}
Object data = shipperListResult.get("data");
List<Map<String, Object>> shipperList;
@@ -719,7 +731,7 @@ public class MaterialBaseInfoApplicationService {
shipperList = JSON.parseObject(JSONObject.toJSONString(data), new TypeReference<List<Map<String, Object>>>() { });
}
if (shipperList == null || shipperList.isEmpty()) {
return;
return null;
}
for (Map<String, Object> shipper : shipperList) {
if (shipper == null) {
@@ -738,15 +750,15 @@ public class MaterialBaseInfoApplicationService {
}
String normalizedName = StringUtils.isNotBlank(shipperName) ? shipperName : "";
String normalizedCode = StringUtils.isNotBlank(shipperCode) ? shipperCode : "";
boolean matched = false;
boolean nameMatched = false;
if (StringUtils.isNotEmpty(normalizedCode)) {
String c1 = userCode != null ? String.valueOf(userCode).trim() : "";
String c2 = userMemberCode != null ? String.valueOf(userMemberCode).trim() : "";
if (normalizedCode.equals(c1) || normalizedCode.equals(c2)) {
matched = true;
nameMatched = true;
}
}
if (!matched && StringUtils.isNotEmpty(normalizedName)) {
if (!nameMatched && StringUtils.isNotEmpty(normalizedName)) {
for (Object cand : new Object[] { userName, entName }) {
if (cand == null) {
continue;
@@ -754,12 +766,12 @@ public class MaterialBaseInfoApplicationService {
String s = String.valueOf(cand).trim();
if (ZhTextNormalizeUtil.textEquals(normalizedName, s)
|| ZhTextNormalizeUtil.textContains(s, normalizedName)) {
matched = true;
nameMatched = true;
break;
}
}
}
if (matched && userId != null) {
if (nameMatched && userId != null) {
try {
Long candidateId = Long.valueOf(String.valueOf(userId));
AjaxResult infoRes = userServiceFeign.getInfo(candidateId);
@@ -767,9 +779,8 @@ public class MaterialBaseInfoApplicationService {
continue;
}
UserPo up = JSON.parseObject(JSONObject.toJSONString(infoRes.get("data")), UserPo.class);
if (shipperUserMatchesImportOrganization(up, importOrgId)) {
applyUserPoToMaterialImportRow(row, up);
return;
if (shipperUserMatchesImportOrganization(up, importOrgId, enforceOrganization)) {
return up;
}
} catch (Exception ex) {
log.debug("物料导入: 从货主列表解析用户失败: {}", ex.getMessage());
@@ -779,6 +790,7 @@ public class MaterialBaseInfoApplicationService {
} catch (Exception e) {
log.debug("物料导入: userShipperListAll 失败: {}", e.getMessage());
}
return null;
}
private void applyUserPoToMaterialImportRow(MaterialBaseInfoDTO row, UserPo userPo) {
@@ -826,8 +838,8 @@ public class MaterialBaseInfoApplicationService {
return organizationId != null && organizationId > 0;
}
private boolean shipperUserMatchesImportOrganization(UserPo userPo, Long importOrganizationId) {
if (importOrganizationId == null) {
private boolean shipperUserMatchesImportOrganization(UserPo userPo, Long importOrganizationId, boolean enforceOrganization) {
if (!enforceOrganization || importOrganizationId == null) {
return true;
}
if (userPo == null) {
@@ -13,7 +13,9 @@ import org.apache.poi.ss.usermodel.WorkbookFactory;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 物料主数据导入:支持繁体中文表头的 Excel 解析。
@@ -23,6 +25,19 @@ public final class MaterialBaseInfoImportExcelUtil {
private MaterialBaseInfoImportExcelUtil() {
}
/** 客户模板常见表头别名 → 系统 @Excel 标准表头 */
private static final Map<String, String> HEADER_ALIASES = new HashMap<>();
static {
HEADER_ALIASES.put("条形码", "物料条形码");
HEADER_ALIASES.put("保質期(天)", "保质期 单位:天");
HEADER_ALIASES.put("保质期(天)", "保质期 单位:天");
HEADER_ALIASES.put("净重(kg)", "净重 单位:千克");
HEADER_ALIASES.put("淨重(kg)", "净重 单位:千克");
HEADER_ALIASES.put("体积(m³)", "体积 单位:立方米");
HEADER_ALIASES.put("體積(m³)", "体积 单位:立方米");
}
public static List<MaterialBaseInfoDTO> importRows(InputStream is) throws Exception {
Workbook workbook = WorkbookFactory.create(is);
try {
@@ -54,7 +69,9 @@ public final class MaterialBaseInfoImportExcelUtil {
if (StringUtils.isBlank(value)) {
continue;
}
cell.setCellValue(ZhTextNormalizeUtil.normalizeExcelHeader(value));
String normalized = ZhTextNormalizeUtil.normalizeExcelHeader(value);
String alias = HEADER_ALIASES.get(normalized);
cell.setCellValue(alias != null ? alias : normalized);
}
}
}
@@ -49,14 +49,25 @@ public final class ZhTextNormalizeUtil {
if (a == null || b == null) {
return false;
}
return toSimple(a).equals(toSimple(b));
return normalizeNameKey(a).equals(normalizeNameKey(b));
}
public static boolean textContains(String haystack, String needle) {
if (StringUtils.isBlank(haystack) || StringUtils.isBlank(needle)) {
return false;
}
return toSimple(haystack).contains(toSimple(needle));
return normalizeNameKey(haystack).contains(normalizeNameKey(needle));
}
/** 简繁体 + 全角括号等常见差异归一化,便于货主/分类名称匹配。 */
public static String normalizeNameKey(String text) {
if (StringUtils.isBlank(text)) {
return text;
}
String s = toSimple(text.trim());
s = s.replace('', '(').replace('', ')');
s = s.replace(' ', ' ');
return s;
}
/**