✨ 新增手机号唯一匹配司机档案 ID 的解析入口
This commit is contained in:
+12
@@ -111,4 +111,16 @@ public interface IDriverService extends BaseService<Driver> {
|
||||
*/
|
||||
List<DriverExcel> exportDriver(Wrapper<Driver> queryWrapper);
|
||||
|
||||
/**
|
||||
* 按手机号解析司机档案 ID
|
||||
* <p>
|
||||
* 归属主键是档案 ID,手机号只是派单时的号码快照:只有租户内唯一命中才认,
|
||||
* 0 条(承运商司机、未登录过小程序的司机)与多条(历史脏数据)都返回 null,不抛异常。
|
||||
*
|
||||
* @param tenantId 租户ID,为空时取当前登录租户
|
||||
* @param mobile 手机号
|
||||
* @return 司机档案ID;无法唯一确定时为 null
|
||||
*/
|
||||
Long resolveDriverIdByMobile(String tenantId, String mobile);
|
||||
|
||||
}
|
||||
|
||||
+26
@@ -25,6 +25,7 @@
|
||||
*/
|
||||
package org.springblade.transport.service.impl;
|
||||
|
||||
import cn.hutool.core.util.DesensitizedUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
@@ -171,6 +172,31 @@ public class DriverServiceImpl extends BaseServiceImpl<DriverMapper, Driver> imp
|
||||
return bindExistingProfile(existing.getId(), mpUserId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long resolveDriverIdByMobile(String tenantId, String mobile) {
|
||||
String resolvedTenantId = trimToNull(tenantId);
|
||||
if (resolvedTenantId == null) {
|
||||
resolvedTenantId = trimToNull(AuthUtil.getTenantId());
|
||||
}
|
||||
String mobileSnapshot = trimToNull(mobile);
|
||||
// 脱敏手机号(如 138****8000)与空号都不是可用业务数据,查不到也不该查
|
||||
if (resolvedTenantId == null || mobileSnapshot == null || mobileSnapshot.contains("*")) {
|
||||
return null;
|
||||
}
|
||||
// 手机号会被二次放号,同租户内可能残留多条历史档案:只有唯一命中才认,不猜
|
||||
List<Driver> matchedDrivers = list(Wrappers.<Driver>lambdaQuery()
|
||||
.select(Driver::getId)
|
||||
.eq(Driver::getTenantId, resolvedTenantId)
|
||||
.eq(Driver::getMobile, mobileSnapshot));
|
||||
if (matchedDrivers.size() != 1) {
|
||||
log.warn("司机档案解析未唯一命中,driver_id 留空。租户={} 手机号={} 命中={} 条",
|
||||
resolvedTenantId, DesensitizedUtil.mobilePhone(mobileSnapshot), matchedDrivers.size());
|
||||
return null;
|
||||
}
|
||||
Driver matchedDriver = matchedDrivers.get(0);
|
||||
return matchedDriver == null ? null : matchedDriver.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public DriverSyncMobileVO syncMobile(String tenantId, Long mpUserId, String oldPhone, String newPhone) {
|
||||
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
package org.springblade.transport.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.MybatisConfiguration;
|
||||
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
|
||||
import org.apache.ibatis.builder.MapperBuilderAssistant;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springblade.system.feign.IMpUserClient;
|
||||
import org.springblade.system.feign.ISysClient;
|
||||
import org.springblade.transport.mapper.DriverMapper;
|
||||
import org.springblade.transport.pojo.entity.Driver;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* 手机号 -> 司机档案 ID 解析入口的测试。
|
||||
* <p>
|
||||
* 归属主键是档案 ID,手机号只是派单时的号码快照:只有租户内唯一命中才认,0 条与多条都不认。
|
||||
* 解析结果是"能不能写归属"的判断,不是业务校验,因此任何入参都只返回 null,不抛异常。
|
||||
*/
|
||||
class DriverResolveMobileTest {
|
||||
|
||||
private static final String TENANT_ID = "000000";
|
||||
private static final String MOBILE = "13800138000";
|
||||
|
||||
private DriverMapper driverMapper;
|
||||
private DriverServiceImpl service;
|
||||
|
||||
@BeforeAll
|
||||
static void initTableInfo() {
|
||||
MybatisConfiguration configuration = new MybatisConfiguration();
|
||||
MapperBuilderAssistant assistant = new MapperBuilderAssistant(configuration, "");
|
||||
assistant.setCurrentNamespace("test");
|
||||
TableInfoHelper.initTableInfo(assistant, Driver.class);
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void setUp() throws Exception {
|
||||
driverMapper = mock(DriverMapper.class);
|
||||
service = new DriverServiceImpl(mock(IMpUserClient.class), mock(ISysClient.class));
|
||||
inject("baseMapper", driverMapper);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("租户内唯一命中:返回档案 ID")
|
||||
void returnsProfileIdWhenUnique() {
|
||||
when(driverMapper.selectList(any())).thenReturn(List.of(profile(9001L)));
|
||||
|
||||
assertEquals(9001L, service.resolveDriverIdByMobile(TENANT_ID, MOBILE));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("租户内没有档案:返回 null")
|
||||
void returnsNullWhenAbsent() {
|
||||
when(driverMapper.selectList(any())).thenReturn(Collections.emptyList());
|
||||
|
||||
assertNull(service.resolveDriverIdByMobile(TENANT_ID, MOBILE));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("租户内多条档案(历史同号):返回 null,不猜")
|
||||
void returnsNullWhenMultiple() {
|
||||
List<Driver> profiles = new ArrayList<>();
|
||||
profiles.add(profile(9001L));
|
||||
profiles.add(profile(9002L));
|
||||
when(driverMapper.selectList(any())).thenReturn(profiles);
|
||||
|
||||
assertNull(service.resolveDriverIdByMobile(TENANT_ID, MOBILE));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("手机号为空或脱敏串:返回 null,且不查库")
|
||||
void returnsNullWithoutQueryForBlankOrMaskedMobile() {
|
||||
assertNull(service.resolveDriverIdByMobile(TENANT_ID, null));
|
||||
assertNull(service.resolveDriverIdByMobile(TENANT_ID, " "));
|
||||
assertNull(service.resolveDriverIdByMobile(TENANT_ID, "138****8000"));
|
||||
verify(driverMapper, never()).selectList(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("租户为空:取当前登录租户,仍不抛异常")
|
||||
void fallsBackToCurrentTenant() {
|
||||
when(driverMapper.selectList(any())).thenReturn(List.of(profile(9001L)));
|
||||
|
||||
Long driverId = null;
|
||||
try (var ignored = org.mockito.Mockito.mockStatic(org.springblade.core.secure.utils.AuthUtil.class)) {
|
||||
ignored.when(org.springblade.core.secure.utils.AuthUtil::getTenantId).thenReturn(TENANT_ID);
|
||||
driverId = service.resolveDriverIdByMobile(null, MOBILE);
|
||||
}
|
||||
assertEquals(9001L, driverId);
|
||||
}
|
||||
|
||||
private Driver profile(Long id) {
|
||||
Driver profile = new Driver();
|
||||
profile.setId(id);
|
||||
profile.setIsDeleted(0);
|
||||
profile.setTenantId(TENANT_ID);
|
||||
profile.setMobile(MOBILE);
|
||||
return profile;
|
||||
}
|
||||
|
||||
private void inject(String field, Object value) throws Exception {
|
||||
Class<?> type = service.getClass();
|
||||
while (type != null) {
|
||||
try {
|
||||
Field declared = type.getDeclaredField(field);
|
||||
declared.setAccessible(true);
|
||||
declared.set(service, value);
|
||||
return;
|
||||
} catch (NoSuchFieldException ignored) {
|
||||
type = type.getSuperclass();
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException("未找到字段:" + field);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user