This commit is contained in:
wuweihua
2026-10-28 22:22:48 +08:00
14 changed files with 344 additions and 66 deletions
@@ -7,9 +7,9 @@ import java.util.Date;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import lombok.ToString;
import com.bms.common.annotation.Excel;
import com.bms.common.core.domain.BaseEntity;
@@ -52,16 +52,20 @@ public class BmsCustomer extends BaseEntity
private String isEnable;
/** 业务员用户ID */
@Excel(name = "业务员用户ID")
private String salesUserId;
/** 业务员姓名 */
@Excel(name = "业务员姓名")
/** 业务员姓名(查询结果只回填已关联系统用户的姓名) */
private String salesUser;
/** 业务员昵称(join sys_user.nick_name,sales_user_id 存工号=sys_user.user_name) */
/** 业务员昵称(关联 sys_user.nick_name) */
@Excel(name = "业务员")
private String salesUserNickName;
/** 客户同步源中的原始业务员工号,仅供内部同步和迁移使用。 */
@JsonIgnore
@ToString.Exclude
private String sourceSalesCode;
/** 资金额度(元) */
@Excel(name = "资金额度", readConverterExp = "元=")
private BigDecimal ed;
@@ -34,6 +34,9 @@ public interface BmsCustomerMapper
*/
public List<BmsCustomer> selectBmsCustomerList(BmsCustomer bmsCustomer);
/** 校验业务员用户ID仍对应未删除的系统用户。 */
public int countValidSalesUserId(@org.apache.ibatis.annotations.Param("salesUserId") String salesUserId);
/**
* 新增客户主
*
@@ -11,16 +11,15 @@ import com.bms.customer.customerinfo.domain.BmsCustomer;
*/
public interface CustomerSyncMapper
{
/**
* 库中未删除的全部客户编码,用于判定新增/更新
*/
/** 库中未删除的全部客户编码,用于判定新增/更新。 */
List<String> selectExistingCodes();
/**
* 未删除用户的 工号(user_name)->用户ID(user_id) 映射,用于 TMS BELONG_SALER 工号转换
*/
/** 未删除用户映射,用于唯一工号匹配。 */
List<SysUser> selectUserLoginMap();
/** 读取存量客户的人工业务员选择和上次同步的源工号。 */
List<BmsCustomer> selectExistingSalesAssignments();
/**
* 新增客户(同步专用,字段集与老系统导入脚本一致)
*/
@@ -587,7 +587,7 @@ public class BmsCustomerLogServiceImpl implements IBmsCustomerLogService
return s == null ? "" : s;
}
/** 人员字段展示:姓名(工号),缺姓名时只显示工号 */
/** 人员字段展示:姓名(系统用户ID),缺姓名时只显示系统用户ID。 */
private static String userDisplay(String code, String name)
{
String c = trimToNull(code);
@@ -82,7 +82,7 @@ public class BmsCustomerServiceImpl implements IBmsCustomerService
* @return 客户主
*/
@Override
// sales_user_id 存的是员工工号(=sys_user.user_name),仅本人/本部门均通过 sys_user 别名 su 关联匹配
// sales_user_id 只存系统 user_id;数据权限通过该用户的部门和用户范围判断。
@DataScope(deptAlias = "su", deptField = "dept_id", userAlias = "su", userField = "user_id")
public List<BmsCustomer> selectBmsCustomerList(BmsCustomer bmsCustomer)
{
@@ -275,6 +275,7 @@ public class BmsCustomerServiceImpl implements IBmsCustomerService
public int insertBmsCustomer(BmsCustomer bmsCustomer)
{
checkContractDate(bmsCustomer.getContractStartDate(), bmsCustomer.getContractEndDate());
normalizeAndValidateSalesUserId(bmsCustomer);
SysUser user = SecurityUtils.getLoginUser().getUser();
bmsCustomer.setCreateTime(DateUtils.getNowDate());
bmsCustomer.setCreateBy(user.getUserName());
@@ -303,6 +304,7 @@ public class BmsCustomerServiceImpl implements IBmsCustomerService
public int updateBmsCustomer(BmsCustomer bmsCustomer)
{
checkContractDate(bmsCustomer.getContractStartDate(), bmsCustomer.getContractEndDate());
normalizeAndValidateSalesUserId(bmsCustomer);
SysUser user = SecurityUtils.getLoginUser().getUser();
bmsCustomer.setUpdateTime(DateUtils.getNowDate());
bmsCustomer.setUpdateBy(user.getUserName());
@@ -518,4 +520,21 @@ public class BmsCustomerServiceImpl implements IBmsCustomerService
throw new ServiceException("合同结束时间不能早于开始时间");
}
}
/** 客户编辑仅允许写入真实存在的系统用户ID,不接受工号作为兼容值。 */
private void normalizeAndValidateSalesUserId(BmsCustomer customer)
{
String salesUserId = customer.getSalesUserId();
if (salesUserId == null || salesUserId.trim().isEmpty())
{
customer.setSalesUserId(null);
return;
}
salesUserId = salesUserId.trim();
if (bmsCustomerMapper.countValidSalesUserId(salesUserId) == 0)
{
throw new ServiceException("业务员不存在或已删除,请从业务员选择器中重新选择");
}
customer.setSalesUserId(salesUserId);
}
}
@@ -9,6 +9,7 @@ import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -23,7 +24,7 @@ import com.bms.customer.customerinfo.mapper.CustomerSyncMapper;
* 老系统 Oracle TAB_CUSTOMER -> ctr_customer 每日同步。
*
* 字段映射、字典转换与 D:\\生产客户数据\\scripts\\import_customers.py 完全一致:
* 按 customer_code 判定新增/更新;源为空的字段不写(走默认值/保留原值)。
* 按 customer_code 判定新增/更新;普通资料的源空字段不写,业务员归属则按匹配结果明确写入ID或NULL。
*
* @author bms
*/
@@ -59,7 +60,6 @@ public class CustomerSyncService
private static final int LEN_CODE = 50;
private static final int LEN_NAME = 100;
private static final int LEN_CODE_DW = 50;
private static final int LEN_USER_ID = 64;
private static final int LEN_USER = 50;
private static final int LEN_REMARK = 500;
/** 告警明细最多打印条数 */
@@ -86,8 +86,15 @@ public class CustomerSyncService
Set<String> existingCodes = new HashSet<>(customerSyncMapper.selectExistingCodes());
// 业务员归属转本系统用户ID:数据权限按 user_id 控制,工号后续修改不影响归属
Map<String, String> userIdByLogin = loadUserIdByLogin();
// 源工号只用于匹配;归属字段始终保存系统 user_id。
Map<String, List<String>> userIdsByLogin = loadUserIdsByLogin();
Set<String> validUserIds = new HashSet<>();
userIdsByLogin.values().forEach(validUserIds::addAll);
Map<String, BmsCustomer> existingSalesAssignments = new HashMap<>();
for (BmsCustomer existing : customerSyncMapper.selectExistingSalesAssignments())
{
existingSalesAssignments.put(existing.getCustomerCode(), existing);
}
List<BmsCustomer> inserts = new ArrayList<>();
List<BmsCustomer> updates = new ArrayList<>();
@@ -123,7 +130,8 @@ public class CustomerSyncService
continue;
}
BmsCustomer c = mapRow(row, code, name, userIdByLogin, warnings);
BmsCustomer c = mapRow(row, code, name, userIdsByLogin,
validUserIds, existingSalesAssignments.get(code), warnings);
if (existingCodes.contains(code))
{
c.setUpdateBy(SYNC_MARKER);
@@ -161,7 +169,8 @@ public class CustomerSyncService
* 单行映射(逻辑对齐 import_customers.py 的 build_row)
*/
private BmsCustomer mapRow(Map<String, Object> row, String code, String name,
Map<String, String> userIdByLogin, List<String> warnings)
Map<String, List<String>> userIdsByLogin, Set<String> validUserIds,
BmsCustomer existingAssignment, List<String> warnings)
{
BmsCustomer c = new BmsCustomer();
c.setCustomerCode(truncate(code, LEN_CODE, code, "customer_code", warnings));
@@ -169,7 +178,10 @@ public class CustomerSyncService
c.setDelFlag("0");
c.setCustomerCodeDw(truncate(getStr(row, "CUSTOMER_OUTER_CODE"), LEN_CODE_DW, code, "customer_code_dw", warnings));
c.setSalesUserId(resolveSalesUserId(clean(getStr(row, "BELONG_SALER")), userIdByLogin, code, warnings));
String sourceSalesCode = clean(getStr(row, "BELONG_SALER"));
c.setSourceSalesCode(sourceSalesCode);
c.setSalesUserId(resolveSalesUserId(sourceSalesCode, userIdsByLogin,
validUserIds, existingAssignment, code, warnings));
c.setSalesUser(truncate(getStr(row, "BELONG_SALER_NAME"), LEN_USER, code, "sales_user", warnings));
c.setSettlementUser(truncate(getStr(row, "SETTLEMENT_MAN"), LEN_USER, code, "settlement_user", warnings));
c.setServiceUser(truncate(getStr(row, "SERVICE_MAN"), LEN_USER, code, "service_user", warnings));
@@ -231,40 +243,56 @@ public class CustomerSyncService
return ("1".equals(s) || "1.0".equals(s)) ? "0" : "1";
}
/** 加载未删除用户的 工号(user_name)->用户ID(user_id) 映射 */
private Map<String, String> loadUserIdByLogin()
/** 加载未删除用户的工号映射;保留重复项以便将歧义匹配按未关联处理。 */
private Map<String, List<String>> loadUserIdsByLogin()
{
Map<String, String> map = new HashMap<>();
Map<String, List<String>> map = new HashMap<>();
for (SysUser u : customerSyncMapper.selectUserLoginMap())
{
if (u != null && u.getUserName() != null && u.getUserId() != null
&& !map.containsKey(u.getUserName()))
if (u != null && clean(u.getUserName()) != null && u.getUserId() != null)
{
map.put(u.getUserName(), String.valueOf(u.getUserId()));
map.computeIfAbsent(u.getUserName().trim(), key -> new ArrayList<>())
.add(String.valueOf(u.getUserId()));
}
}
return map;
}
/**
* 业务员工号 -> 本系统 sys_user.user_id 转换(数据权限按 user_id 控制,工号修改不影响归属)。
* 工号在系统用户中不存在时保留原工号并告警(常见于该用户尚未同步),避免归属信息丢失。
* 只在唯一匹配时关联系统用户。
* 源工号不变时保留客户页面人工选择的有效业务员;工号为空时也保留有效业务员。
*/
private String resolveSalesUserId(String salerCode, Map<String, String> userIdByLogin,
private String resolveSalesUserId(String salerCode, Map<String, List<String>> userIdsByLogin,
Set<String> validUserIds, BmsCustomer existingAssignment,
String code, List<String> warnings)
{
String s = truncate(salerCode, LEN_USER_ID, code, "sales_user_id", warnings);
if (s == null)
String existingUserId = existingAssignment == null ? null : clean(existingAssignment.getSalesUserId());
boolean existingUserValid = existingUserId != null && validUserIds.contains(existingUserId);
if (salerCode == null)
{
return null;
return existingUserValid ? existingUserId : null;
}
String userId = userIdByLogin.get(s);
if (userId != null)
boolean sourceCodeUnchanged = existingAssignment != null
&& Objects.equals(salerCode, clean(existingAssignment.getSourceSalesCode()));
boolean sourceCodeUnknown = existingAssignment != null
&& clean(existingAssignment.getSourceSalesCode()) == null;
if (existingUserValid && (sourceCodeUnchanged || sourceCodeUnknown))
{
return userId;
// 历史有效用户ID可能尚无可恢复的源工号;首次同步建立源工号基线时不覆盖已有人工归属。
return existingUserId;
}
warnings.add(code + ": 业务员工号 " + s + " 在系统用户中不存在,sales_user_id 保留原工号");
return s;
List<String> matchedUserIds = userIdsByLogin.get(salerCode);
if (matchedUserIds != null && matchedUserIds.size() == 1)
{
return matchedUserIds.get(0);
}
warnings.add(code + (matchedUserIds == null || matchedUserIds.isEmpty()
? ": 业务员工号未匹配到系统用户,客户保持未关联"
: ": 业务员工号匹配到多个系统用户,客户保持未关联"));
return null;
}
private static String clean(String v)