🐛 修复登录自动建档把脱敏手机号写入司机档案
This commit is contained in:
@@ -98,30 +98,30 @@ public class MpLoginSupport {
|
||||
account = ensureUser(tenantId, phone, openid);
|
||||
} else {
|
||||
account = openidAccount;
|
||||
if (!samePhone(openidAccount.getPhone(), phone)) {
|
||||
MpUser phoneAccount = findByPhone(tenantId, phone);
|
||||
if (phoneAccount != null && !Objects.equals(phoneAccount.getId(), openidAccount.getId())) {
|
||||
// ①-撞键:落到手机号账号,不迁移 openid
|
||||
account = phoneAccount;
|
||||
loginNotice = NOTICE_PHONE_OCCUPIED;
|
||||
openidOccupied = true;
|
||||
log.warn("微信换号撞键,登录落到手机号账号:租户={} openid账号={} 手机号账号={}",
|
||||
tenantId, openidAccount.getId(), phoneAccount.getId());
|
||||
} else {
|
||||
account = movePhone(openidAccount, phone);
|
||||
if (account == null) {
|
||||
// 更新手机号失败:并发时可能是手机号刚被别的账号占用,重查一次再决定落点
|
||||
MpUser concurrent = findByPhone(tenantId, phone);
|
||||
if (concurrent != null && !Objects.equals(concurrent.getId(), openidAccount.getId())) {
|
||||
account = concurrent;
|
||||
loginNotice = NOTICE_PHONE_OCCUPIED;
|
||||
openidOccupied = true;
|
||||
log.warn("更新手机号失败后命中手机号账号:租户={} openid账号={} 手机号账号={}",
|
||||
tenantId, openidAccount.getId(), concurrent.getId());
|
||||
} else {
|
||||
// 仍然用 openid 账号登录:手机号未能更新不影响身份识别,下次登录再试
|
||||
account = openidAccount;
|
||||
}
|
||||
// 账号 phone 经 feign 下发时是脱敏值(MpUser.phone 标了 @Sensitive),不能拿来比较。
|
||||
// 改按「该手机号是否已有账号」判断:命中自己是同一个人、命中别人是撞键、没命中就换号。
|
||||
MpUser phoneAccount = findByPhone(tenantId, phone);
|
||||
if (phoneAccount != null && !Objects.equals(phoneAccount.getId(), openidAccount.getId())) {
|
||||
// ①-撞键:落到手机号账号,不迁移 openid
|
||||
account = phoneAccount;
|
||||
loginNotice = NOTICE_PHONE_OCCUPIED;
|
||||
openidOccupied = true;
|
||||
log.warn("微信换号撞键,登录落到手机号账号:租户={} openid账号={} 手机号账号={}",
|
||||
tenantId, openidAccount.getId(), phoneAccount.getId());
|
||||
} else if (phoneAccount == null) {
|
||||
account = movePhone(openidAccount, phone);
|
||||
if (account == null) {
|
||||
// 更新手机号失败:并发时可能是手机号刚被别的账号占用,重查一次再决定落点
|
||||
MpUser concurrent = findByPhone(tenantId, phone);
|
||||
if (concurrent != null && !Objects.equals(concurrent.getId(), openidAccount.getId())) {
|
||||
account = concurrent;
|
||||
loginNotice = NOTICE_PHONE_OCCUPIED;
|
||||
openidOccupied = true;
|
||||
log.warn("更新手机号失败后命中手机号账号:租户={} openid账号={} 手机号账号={}",
|
||||
tenantId, openidAccount.getId(), concurrent.getId());
|
||||
} else {
|
||||
// 仍然用 openid 账号登录:手机号未能更新不影响身份识别,下次登录再试
|
||||
account = openidAccount;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -227,10 +227,14 @@ public class MpLoginSupport {
|
||||
if (account == null || !MpUserIdentity.has(account.getIdentities(), MpUserIdentity.DRIVER)) {
|
||||
return null;
|
||||
}
|
||||
// 租户与手机号取账号上的值:档案必须与账号落同一租户(唯一键对 NULL 不去重);
|
||||
// 账号手机号未能更新时,不使用入参手机号建档
|
||||
// 租户取账号上的值(档案必须与账号同租户,唯一键对 NULL 不去重);
|
||||
// 手机号只能取登录入参:账号 phone 经 feign 下发时是脱敏值,写进档案会让按手机号搜不到司机
|
||||
String tenantId = Func.toStr(account.getTenantId(), requestedTenantId);
|
||||
String phone = Func.toStr(account.getPhone(), requestedPhone);
|
||||
String phone = trimToNull(requestedPhone);
|
||||
if (phone == null) {
|
||||
log.warn("登录自动建档跳过:登录手机号为空,租户={} 账号={}", tenantId, account.getId());
|
||||
return null;
|
||||
}
|
||||
DriverEnsureProfileDTO param = new DriverEnsureProfileDTO();
|
||||
param.setTenantId(tenantId);
|
||||
param.setMpUserId(account.getId());
|
||||
@@ -316,10 +320,6 @@ public class MpLoginSupport {
|
||||
userInfo.setDetail(detail);
|
||||
}
|
||||
|
||||
private boolean samePhone(String left, String right) {
|
||||
return Objects.equals(trimToNull(left), trimToNull(right));
|
||||
}
|
||||
|
||||
private String trimToNull(String value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
|
||||
@@ -59,7 +59,8 @@ class MpLoginSupportTest {
|
||||
@Test
|
||||
@DisplayName("新手机号微信登录:建号 + 建档,登录成功且无提示")
|
||||
void newPhoneCreatesAccountAndProfile() {
|
||||
MpUser created = mpUser(11L, PHONE, OPENID, "driver");
|
||||
// feign 下发时 phone 是脱敏值(MpUser.phone 标了 @Sensitive),建档必须仍用登录入参的真实号
|
||||
MpUser created = mpUser(11L, "138****0001", OPENID, "driver");
|
||||
when(mpUserClient.userByOpenid(TENANT_ID, OPENID)).thenReturn(R.data(null));
|
||||
when(mpUserClient.ensureUser(any(MpUserEnsureDTO.class))).thenReturn(R.data(created));
|
||||
when(mpUserClient.userInfoById(11L)).thenReturn(R.data(userInfo(created, "driver")));
|
||||
@@ -154,7 +155,7 @@ class MpLoginSupportTest {
|
||||
@Test
|
||||
@DisplayName("openid 命中且手机号不同但新号空闲:更新账号手机号")
|
||||
void openidHitWithFreePhoneUpdatesPhone() {
|
||||
MpUser openidAccount = mpUser(31L, "13900000000", OPENID, "driver");
|
||||
MpUser openidAccount = mpUser(31L, "139****0000", OPENID, "driver");
|
||||
when(mpUserClient.userByOpenid(TENANT_ID, OPENID)).thenReturn(R.data(openidAccount));
|
||||
when(mpUserClient.userByPhone(TENANT_ID, NEW_PHONE)).thenReturn(R.data(null));
|
||||
when(mpUserClient.updateUser(any(MpUser.class))).thenReturn(R.data(Boolean.TRUE));
|
||||
@@ -171,6 +172,22 @@ class MpLoginSupportTest {
|
||||
verify(mpUserClient, never()).ensureUser(any(MpUserEnsureDTO.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("openid 命中且该手机号就是本账号:不重复更新手机号")
|
||||
void openidHitSameAccountDoesNotUpdatePhone() {
|
||||
MpUser openidAccount = mpUser(32L, "138****0001", OPENID, "driver");
|
||||
when(mpUserClient.userByOpenid(TENANT_ID, OPENID)).thenReturn(R.data(openidAccount));
|
||||
when(mpUserClient.userByPhone(TENANT_ID, PHONE)).thenReturn(R.data(openidAccount));
|
||||
when(mpUserClient.userInfoById(32L)).thenReturn(R.data(userInfo(openidAccount, "driver")));
|
||||
when(driverClient.ensureProfile(any(DriverEnsureProfileDTO.class))).thenReturn(R.data(profileResult()));
|
||||
|
||||
OAuth2User user = mpLoginSupport.ensureLoginAccount(TENANT_ID, PHONE, OPENID);
|
||||
|
||||
assertEquals("32", user.getUserId());
|
||||
verify(mpUserClient, never()).updateUser(any(MpUser.class));
|
||||
verify(mpUserClient, never()).ensureUser(any(MpUserEnsureDTO.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("纯 admin 身份:不建司机档案")
|
||||
void adminOnlyDoesNotCreateProfile() {
|
||||
|
||||
+4
@@ -134,6 +134,10 @@ public class DriverServiceImpl extends BaseServiceImpl<DriverMapper, Driver> imp
|
||||
if (mobile == null) {
|
||||
throw new ServiceException("手机号不能为空");
|
||||
}
|
||||
// 脱敏手机号(如 155****2748)不是可用的业务数据:写进档案后按手机号查不到司机
|
||||
if (mobile.contains("*")) {
|
||||
throw new ServiceException("手机号无效,不能写入司机档案");
|
||||
}
|
||||
// 查找顺序固定为 user_id → phone:user_id 是账号的稳定标识,
|
||||
// phone 会被运营商二次放号
|
||||
Driver bound = findDriverByUserId(mpUserId);
|
||||
|
||||
+8
@@ -214,6 +214,14 @@ class DriverEnsureProfileTest {
|
||||
|
||||
// ------------------------------------------------------------ 入参兜底
|
||||
|
||||
@Test
|
||||
@DisplayName("脱敏手机号:拒绝写库,不新建档案")
|
||||
void rejectsMaskedMobile() {
|
||||
assertThrows(ServiceException.class, () -> service.ensureProfile(TENANT_ID, MP_USER_ID, "155****2748"));
|
||||
verify(driverMapper, never()).insert(any(Driver.class));
|
||||
verify(driverMapper, never()).updateById(any(Driver.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("租户为空且当前上下文也没有租户:拒绝写库")
|
||||
void rejectsBlankTenant() {
|
||||
|
||||
Reference in New Issue
Block a user