package com.tcm.training.admin.app.register.service;
|
|
import com.baomidou.mybatisplus.core.MybatisConfiguration;
|
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.tcm.training.admin.app.auth.service.AppAuthSessionService;
|
import com.tcm.training.admin.app.auth.service.AppSmsCodeService;
|
import com.tcm.training.admin.app.auth.vo.AppLoginResponse;
|
import com.tcm.training.admin.app.integration.haizhentong.entity.HztAppUser;
|
import com.tcm.training.admin.app.integration.haizhentong.entity.HztClinic;
|
import com.tcm.training.admin.app.integration.haizhentong.mapper.HztAppUserMapper;
|
import com.tcm.training.admin.app.integration.haizhentong.mapper.HztClinicMapper;
|
import com.tcm.training.admin.app.register.dto.AppRegisterOperatorCompleteRequest;
|
import com.tcm.training.admin.app.register.dto.AppRegisterOperatorPrepareRequest;
|
import com.tcm.training.admin.app.register.dto.AppRegisterUserRequest;
|
import com.tcm.training.admin.app.register.vo.AppRegisterOperatorPrepareResponse;
|
import com.tcm.training.admin.app.share.service.AppShareService;
|
import com.tcm.training.admin.live.entity.Clinic;
|
import com.tcm.training.admin.live.mapper.ClinicMapper;
|
import com.tcm.training.admin.member.entity.AppUser;
|
import com.tcm.training.admin.member.mapper.UserMapper;
|
import cn.dev33.satoken.stp.SaTokenInfo;
|
import com.tcm.training.admin.utils.VzanHttpUtil;
|
import org.apache.ibatis.builder.MapperBuilderAssistant;
|
import org.junit.jupiter.api.BeforeAll;
|
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.mockito.ArgumentCaptor;
|
import org.mockito.Captor;
|
import org.mockito.Mock;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
import org.springframework.data.redis.core.StringRedisTemplate;
|
import org.springframework.data.redis.core.ValueOperations;
|
|
import java.util.concurrent.TimeUnit;
|
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.anyLong;
|
import static org.mockito.Mockito.doNothing;
|
import static org.mockito.Mockito.never;
|
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.when;
|
|
/**
|
* APP 注册服务测试。
|
*/
|
@ExtendWith(MockitoExtension.class)
|
class AppRegisterServiceTest {
|
|
@BeforeAll
|
static void initLambdaCache() {
|
MapperBuilderAssistant assistant = new MapperBuilderAssistant(new MybatisConfiguration(), "");
|
TableInfoHelper.initTableInfo(assistant, AppUser.class);
|
TableInfoHelper.initTableInfo(assistant, Clinic.class);
|
TableInfoHelper.initTableInfo(assistant, HztAppUser.class);
|
TableInfoHelper.initTableInfo(assistant, HztClinic.class);
|
}
|
|
@Mock
|
private UserMapper userMapper;
|
|
@Mock
|
private ClinicMapper clinicMapper;
|
|
@Mock
|
private AppSmsCodeService appSmsCodeService;
|
|
@Mock
|
private AppAuthSessionService appAuthSessionService;
|
|
@Mock
|
private AppRegisterRegionService appRegisterRegionService;
|
|
@Mock
|
private HztAppUserMapper hztAppUserMapper;
|
|
@Mock
|
private HztClinicMapper hztClinicMapper;
|
|
@Mock
|
private StringRedisTemplate stringRedisTemplate;
|
|
@Mock
|
private ValueOperations<String, String> valueOperations;
|
|
private AppRegisterService appRegisterService;
|
|
@Captor
|
private ArgumentCaptor<AppUser> appUserCaptor;
|
@Mock
|
private AppShareService appShareService;
|
@Mock
|
private VzanHttpUtil vzanHttpUtil;
|
/**
|
* 使用 Mockito mock 重新组装被测服务。
|
*/
|
@BeforeEach
|
void setUp() {
|
appRegisterService = new AppRegisterService(
|
userMapper,
|
clinicMapper,
|
appSmsCodeService,
|
appAuthSessionService,
|
appRegisterRegionService,
|
hztAppUserMapper,
|
hztClinicMapper,
|
stringRedisTemplate,
|
new ObjectMapper(),
|
appShareService,
|
vzanHttpUtil
|
);
|
}
|
|
/**
|
* 普通用户注册时应同步海诊通资料并绑定邀请码对应的上级诊所用户。
|
*/
|
@Test
|
void registerUserShouldSyncHaizhentongProfileAndBindSuperiorClinicUser() {
|
AppRegisterUserRequest request = new AppRegisterUserRequest();
|
request.setPhone("13800138000");
|
request.setCode("666666");
|
request.setInviteCode("abc123");
|
|
HztAppUser hztAppUser = new HztAppUser();
|
hztAppUser.setNickName("海诊通昵称");
|
hztAppUser.setAvatar("https://cdn.test/avatar.png");
|
hztAppUser.setSex(2);
|
|
Clinic clinic = new Clinic();
|
clinic.setId(19L);
|
|
AppUser clinicUser = new AppUser();
|
clinicUser.setId(88L);
|
clinicUser.setClinicId(19L);
|
|
AppShareService.InviteBindingTarget bindingTarget = new AppShareService.InviteBindingTarget();
|
bindingTarget.setClinicId(19L);
|
bindingTarget.setSuperiorClinicUserId(88L);
|
when(appShareService.resolveInviteBindingTarget("ABC123")).thenReturn(bindingTarget);
|
when(hztAppUserMapper.selectOne(any())).thenReturn(hztAppUser);
|
when(userMapper.selectOne(any())).thenReturn(null);
|
doNothing().when(appSmsCodeService).validateRegisterCode("13800138000", "666666");
|
when(userMapper.insert(any(AppUser.class))).thenAnswer(invocation -> {
|
AppUser inserted = invocation.getArgument(0);
|
inserted.setId(1001L);
|
return 1;
|
});
|
when(appAuthSessionService.getTokenInfo()).thenReturn(buildTokenInfo("register-token", 7200L));
|
|
AppLoginResponse result = appRegisterService.registerUser(request);
|
|
verify(userMapper).insert(appUserCaptor.capture());
|
verify(appAuthSessionService).login(1001L);
|
AppUser inserted = appUserCaptor.getValue();
|
assertEquals("海诊通昵称", inserted.getNickname());
|
assertEquals("https://cdn.test/avatar.png", inserted.getAvatar());
|
assertEquals(Integer.valueOf(0), inserted.getGender());
|
assertEquals(Long.valueOf(19L), inserted.getClinicId());
|
assertEquals(Long.valueOf(88L), inserted.getSuperiorClinicUserId());
|
assertNull(inserted.getPassword());
|
assertEquals("register-token", result.getToken());
|
assertEquals("Bearer", result.getTokenType());
|
assertEquals(7200L, result.getExpiresIn());
|
assertEquals(Long.valueOf(1001L), result.getUser().getId());
|
assertEquals(Integer.valueOf(0), result.getUser().getType());
|
}
|
|
/**
|
* 普通用户注册在海诊通未命中时应回退为手机号兜底昵称。
|
*/
|
@Test
|
void registerUserShouldFallbackToPhoneNicknameWhenHaizhentongUserMissing() {
|
AppRegisterUserRequest request = new AppRegisterUserRequest();
|
request.setPhone("13800138123");
|
request.setCode("666666");
|
|
when(userMapper.selectOne(any())).thenReturn(null);
|
when(hztAppUserMapper.selectOne(any())).thenReturn(null);
|
doNothing().when(appSmsCodeService).validateRegisterCode("13800138123", "666666");
|
when(userMapper.insert(any(AppUser.class))).thenAnswer(invocation -> {
|
AppUser inserted = invocation.getArgument(0);
|
inserted.setId(1002L);
|
return 1;
|
});
|
when(appAuthSessionService.getTokenInfo()).thenReturn(buildTokenInfo("fallback-token", 3600L));
|
|
AppLoginResponse result = appRegisterService.registerUser(request);
|
|
verify(userMapper).insert(appUserCaptor.capture());
|
verify(appAuthSessionService).login(1002L);
|
AppUser inserted = appUserCaptor.getValue();
|
assertEquals("138****8123", inserted.getNickname());
|
assertNull(inserted.getAvatar());
|
assertNull(inserted.getGender());
|
assertNull(inserted.getPassword());
|
assertEquals(Long.valueOf(1002L), result.getUser().getId());
|
assertEquals(Integer.valueOf(0), result.getUser().getType());
|
}
|
|
/**
|
* 经营者第一步在海诊通未命中诊所时仍应允许继续进入第二步手填。
|
*/
|
@Test
|
void prepareOperatorShouldAllowManualWhenClinicNotMatched() {
|
AppRegisterOperatorPrepareRequest request = new AppRegisterOperatorPrepareRequest();
|
request.setPhone("13800138001");
|
request.setCode("666666");
|
|
when(userMapper.selectOne(any())).thenReturn(null);
|
when(hztClinicMapper.selectOne(any())).thenReturn(null);
|
when(stringRedisTemplate.opsForValue()).thenReturn(valueOperations);
|
doNothing().when(appSmsCodeService).validateRegisterCode("13800138001", "666666");
|
|
AppRegisterOperatorPrepareResponse response = appRegisterService.prepareOperatorRegister(request);
|
|
assertTrue(response.getRegisterTicket() != null && !response.getRegisterTicket().trim().isEmpty());
|
assertFalse(response.isClinicMatched());
|
assertNotNull(response.getClinicInfo());
|
assertNull(response.getClinicInfo().getInstitutionName());
|
verify(valueOperations).set(any(String.class), any(String.class), anyLong(), any(TimeUnit.class));
|
}
|
|
/**
|
* 经营者第一步应使用 sys_region 解析后的地区编码回填诊所信息。
|
*/
|
@Test
|
void prepareOperatorShouldFillResolvedRegionSelection() {
|
AppRegisterOperatorPrepareRequest request = new AppRegisterOperatorPrepareRequest();
|
request.setPhone("13800138002");
|
request.setCode("666666");
|
|
HztClinic hztClinic = new HztClinic();
|
hztClinic.setClinicName("海诊通诊所");
|
hztClinic.setProvinceName("浙江省");
|
hztClinic.setCityName("杭州市");
|
hztClinic.setDistrictName("西湖区");
|
hztClinic.setClinicAddress("文三路 1 号");
|
|
AppRegisterRegionService.RegionSelection regionSelection = new AppRegisterRegionService.RegionSelection();
|
regionSelection.setProvinceCode("330000");
|
regionSelection.setProvinceName("浙江省");
|
regionSelection.setCityCode("330100");
|
regionSelection.setCityName("杭州市");
|
regionSelection.setDistrictCode("330106");
|
regionSelection.setDistrictName("西湖区");
|
|
when(userMapper.selectOne(any())).thenReturn(null);
|
when(hztClinicMapper.selectOne(any())).thenReturn(hztClinic);
|
when(appRegisterRegionService.resolveRegionSelection(null, "浙江省", null, "杭州市", null, "西湖区")).thenReturn(regionSelection);
|
when(stringRedisTemplate.opsForValue()).thenReturn(valueOperations);
|
doNothing().when(appSmsCodeService).validateRegisterCode("13800138002", "666666");
|
|
AppRegisterOperatorPrepareResponse response = appRegisterService.prepareOperatorRegister(request);
|
|
assertTrue(response.isClinicMatched());
|
assertEquals("海诊通诊所", response.getClinicInfo().getInstitutionName());
|
assertEquals("330000", response.getClinicInfo().getProvinceCode());
|
assertEquals("330100", response.getClinicInfo().getCityCode());
|
assertEquals("330106", response.getClinicInfo().getDistrictCode());
|
assertEquals("文三路 1 号", response.getClinicInfo().getDetailAddress());
|
}
|
|
/**
|
* 经营者第二步完成注册后应直接返回登录响应。
|
*/
|
@Test
|
void completeOperatorShouldReturnLoginResponse() throws Exception {
|
AppRegisterOperatorCompleteRequest request = new AppRegisterOperatorCompleteRequest();
|
request.setRegisterTicket("ticket-1");
|
request.setInstitutionName("本地诊所");
|
request.setProvinceName("浙江省");
|
request.setCityName("杭州市");
|
request.setDistrictName("西湖区");
|
request.setDetailAddress("文三路 2 号");
|
request.setBusinessLicenseUrl("https://cdn.test/license.png");
|
|
AppRegisterService.OperatorRegisterDraft draft = new AppRegisterService.OperatorRegisterDraft();
|
draft.setPhone("13800138003");
|
draft.setContactName("负责人");
|
|
Clinic clinicExists = null;
|
when(stringRedisTemplate.opsForValue()).thenReturn(valueOperations);
|
when(valueOperations.get("app:register:operator:ticket-1")).thenReturn(new ObjectMapper().writeValueAsString(draft));
|
when(userMapper.selectOne(any())).thenReturn(null);
|
when(clinicMapper.selectOne(any())).thenReturn(clinicExists);
|
when(clinicMapper.insert(any(Clinic.class))).thenAnswer(invocation -> {
|
Clinic inserted = invocation.getArgument(0);
|
inserted.setId(2001L);
|
inserted.setInviteCode("ABC123");
|
return 1;
|
});
|
when(userMapper.insert(any(AppUser.class))).thenAnswer(invocation -> {
|
AppUser inserted = invocation.getArgument(0);
|
inserted.setId(3001L);
|
return 1;
|
});
|
when(appAuthSessionService.getTokenInfo()).thenReturn(buildTokenInfo("operator-token", 7200L));
|
|
AppLoginResponse response = appRegisterService.completeOperatorRegister(request);
|
|
verify(userMapper).insert(appUserCaptor.capture());
|
verify(appAuthSessionService).login(3001L);
|
AppUser inserted = appUserCaptor.getValue();
|
assertEquals("本地诊所", inserted.getNickname());
|
assertEquals("13800138003", inserted.getPhone());
|
assertNull(inserted.getPassword());
|
assertEquals(Integer.valueOf(1), inserted.getType());
|
assertEquals(Long.valueOf(2001L), inserted.getClinicId());
|
assertEquals("operator-token", response.getToken());
|
assertEquals(Long.valueOf(3001L), response.getUser().getId());
|
assertEquals(Integer.valueOf(1), response.getUser().getType());
|
assertEquals(Long.valueOf(2001L), response.getUser().getClinicId());
|
}
|
|
/**
|
* 构造测试用登录 token 信息。
|
*/
|
private SaTokenInfo buildTokenInfo(String tokenValue, long timeout) {
|
SaTokenInfo tokenInfo = new SaTokenInfo();
|
tokenInfo.setTokenValue(tokenValue);
|
tokenInfo.setTokenTimeout(timeout);
|
return tokenInfo;
|
}
|
}
|