✨ 新增手机号唯一匹配司机档案 ID 的解析入口
This commit is contained in:
+12
@@ -111,4 +111,16 @@ public interface IDriverService extends BaseService<Driver> {
|
|||||||
*/
|
*/
|
||||||
List<DriverExcel> exportDriver(Wrapper<Driver> queryWrapper);
|
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;
|
package org.springblade.transport.service.impl;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.DesensitizedUtil;
|
||||||
import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
||||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||||
@@ -171,6 +172,31 @@ public class DriverServiceImpl extends BaseServiceImpl<DriverMapper, Driver> imp
|
|||||||
return bindExistingProfile(existing.getId(), mpUserId);
|
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
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public DriverSyncMobileVO syncMobile(String tenantId, Long mpUserId, String oldPhone, String newPhone) {
|
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