package com.tourguide.modules.user.service; import com.tourguide.modules.operation.mapper.OperationCityMapper; import com.tourguide.modules.operation.mapper.OperationScenicSpotMapper; import com.tourguide.modules.user.dto.GuideUserDetailUpdateRequest; import com.tourguide.modules.user.dto.GuideUserDetailResponse; import com.tourguide.modules.user.entity.GuideApplication; import com.tourguide.modules.user.entity.GuideUser; import com.tourguide.modules.user.mapper.GuideAutoDisableConfigMapper; import com.tourguide.modules.user.mapper.GuideApplicationMapper; import com.tourguide.modules.user.mapper.GuideScenicConfigMapper; import com.tourguide.modules.user.mapper.GuideTeamMapper; import com.tourguide.modules.user.mapper.GuideUserMapper; import com.tourguide.common.exception.BusinessException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.springframework.security.crypto.password.PasswordEncoder; import java.time.LocalDate; import java.time.Period; import java.util.Collections; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.mockito.Mockito.never; class GuideUserServiceProfileTest { private GuideUserMapper userMapper; private GuideApplicationMapper applicationMapper; private GuideScenicConfigMapper scenicConfigMapper; private GuideUserService service; @BeforeEach void setUp() { userMapper = mock(GuideUserMapper.class); applicationMapper = mock(GuideApplicationMapper.class); scenicConfigMapper = mock(GuideScenicConfigMapper.class); when(scenicConfigMapper.selectList(any())).thenReturn(Collections.emptyList()); when(userMapper.selectOne(any())).thenReturn(null); PasswordEncoder passwordEncoder = mock(PasswordEncoder.class); when(passwordEncoder.encode(any())).thenReturn("encoded-password"); service = new GuideUserService( userMapper, mock(GuideTeamMapper.class), applicationMapper, scenicConfigMapper, mock(GuideAutoDisableConfigMapper.class), mock(OperationScenicSpotMapper.class), mock(OperationCityMapper.class), passwordEncoder); } @Test void shouldDeriveBirthDateAndWorkYearsForPendingGuide() { GuideApplication application = new GuideApplication(); application.setMobile("13800138000"); application.setRealName("导游"); application.setGender(1); application.setIdCardNo("11010519491231002X"); application.setWorkYearRange("3-5年"); service.createPendingFromApplication(application); ArgumentCaptor captor = ArgumentCaptor.forClass(GuideUser.class); verify(userMapper).insert(captor.capture()); GuideUser guide = captor.getValue(); assertThat(guide.getBirthDate()).isEqualTo(LocalDate.of(1949, 12, 31)); assertThat(guide.getWorkYears()).isEqualTo(3); } @Test void shouldCopyRegistrationProfileWhenApprovingPendingGuide() { GuideUser pendingGuide = new GuideUser(); pendingGuide.setId(12L); pendingGuide.setStatus(GuideUserService.STATUS_PENDING); pendingGuide.setMobile("13800138000"); when(userMapper.selectOne(any())).thenReturn(pendingGuide); GuideApplication application = new GuideApplication(); application.setMobile("13800138000"); application.setIdCardNo("11010519491231002X"); application.setWorkYearRange("5年以上"); service.createFromApprovedApplication(application); ArgumentCaptor captor = ArgumentCaptor.forClass(GuideUser.class); verify(userMapper).updateById(captor.capture()); assertThat(captor.getValue().getBirthDate()).isEqualTo(LocalDate.of(1949, 12, 31)); assertThat(captor.getValue().getWorkYears()).isEqualTo(5); } @Test void shouldCreateNewGuideForCancelledRegistration() { GuideUser cancelledGuide = new GuideUser(); cancelledGuide.setId(12L); cancelledGuide.setStatus(GuideUserService.STATUS_CANCELLED); cancelledGuide.setMobile("13800138000"); cancelledGuide.setUsername("13800138000"); cancelledGuide.setCertificateNo("CERT-12"); cancelledGuide.setIdCardNo("11010519491231002X"); cancelledGuide.setAutoDisableWhitelisted(1); cancelledGuide.setAutoDisabled(1); when(userMapper.selectOne(any())).thenReturn(cancelledGuide); GuideApplication application = new GuideApplication(); application.setMobile("13800138000"); application.setRealName("瀵兼父"); application.setIdCardNo("11010519491231002X"); service.createPendingFromApplication(application); verify(userMapper).anonymizeCancelledGuide(eq(12L), org.mockito.ArgumentMatchers.argThat( username -> username.startsWith("deleted_") && username.length() == 32)); verify(userMapper).clearCancelledIdentityByIdCard("11010519491231002X"); ArgumentCaptor insertCaptor = ArgumentCaptor.forClass(GuideUser.class); verify(userMapper).insert(insertCaptor.capture()); GuideUser newGuide = insertCaptor.getValue(); assertThat(newGuide.getStatus()).isEqualTo(GuideUserService.STATUS_PENDING); assertThat(newGuide.getUsername()).isEqualTo("13800138000"); assertThat(newGuide.getMobile()).isEqualTo("13800138000"); assertThat(newGuide.getAutoDisableWhitelisted()).isNull(); assertThat(newGuide.getAutoDisabled()).isNull(); } @Test void shouldReleaseIdentityFromStaleCancelledGuideBeforeNewRegistration() { GuideApplication application = new GuideApplication(); application.setMobile("13900139000"); application.setRealName("新导游"); application.setIdCardNo("11010519491231002X"); when(userMapper.selectOne(any())).thenReturn(null); service.createPendingFromApplication(application); verify(userMapper).clearCancelledIdentityByIdCard("11010519491231002X"); verify(userMapper).insert(any(GuideUser.class)); } @Test void shouldReleaseIdentityFromStalePendingGuideBeforeNewRegistration() { GuideApplication application = new GuideApplication(); application.setMobile("13900139000"); application.setRealName("新导游"); application.setIdCardNo("11010519491231002X"); when(userMapper.selectOne(any())).thenReturn(null); service.createPendingFromApplication(application); verify(userMapper).clearCancelledIdentityByIdCard("11010519491231002X"); verify(userMapper).clearPendingIdentityByIdCard("11010519491231002X"); verify(userMapper).insert(any(GuideUser.class)); } @Test void shouldReleasePendingIdentityWhenRejectingApplication() { GuideUser pendingGuide = new GuideUser(); pendingGuide.setId(12L); pendingGuide.setStatus(GuideUserService.STATUS_PENDING); pendingGuide.setMobile("13800138000"); when(userMapper.selectOne(any())).thenReturn(pendingGuide); service.releaseIdentityForRejectedApplication("13800138000"); verify(userMapper).releasePendingIdentity(12L); } @Test void shouldTreatOneYearOrLessAsZeroWorkYears() { GuideApplication application = new GuideApplication(); application.setMobile("13800138000"); application.setIdCardNo("11010519491231002X"); application.setWorkYearRange("1年以内"); service.createPendingFromApplication(application); ArgumentCaptor captor = ArgumentCaptor.forClass(GuideUser.class); verify(userMapper).insert(captor.capture()); assertThat(captor.getValue().getWorkYears()).isZero(); } @Test void shouldUseRegistrationProfileAsDetailFallbackForExistingGuide() { GuideUser guide = new GuideUser(); guide.setId(12L); guide.setMobile("13800138000"); guide.setIdCardNo("11010519491231002X"); when(userMapper.selectById(12L)).thenReturn(guide); GuideApplication application = new GuideApplication(); application.setIdCardNo(guide.getIdCardNo()); application.setAgeRange("26-35岁"); application.setWorkYearRange("1-3年"); when(applicationMapper.selectOne(any())).thenReturn(application); GuideUserDetailResponse response = service.detail(12L); assertThat(response.getBirthDate()).isEqualTo(LocalDate.of(1949, 12, 31)); assertThat(response.getAge()).isEqualTo(Period.between(LocalDate.of(1949, 12, 31), LocalDate.now()).getYears()); assertThat(response.getWorkYears()).isEqualTo(1); assertThat(response.getAgeRange()).isEqualTo("26-35岁"); assertThat(response.getWorkYearRange()).isEqualTo("1-3年"); } @Test void adminPhoneChangeShouldUpdatePhoneLoginIdentity() { GuideUser guide = new GuideUser(); guide.setId(12L); guide.setStatus(GuideUserService.STATUS_ENABLED); guide.setUsername("13800138000"); guide.setMobile("13800138000"); when(userMapper.selectForUpdate(12L)).thenReturn(guide); when(userMapper.selectById(12L)).thenReturn(guide); GuideUserDetailUpdateRequest request = new GuideUserDetailUpdateRequest(); request.setRealName("导游"); request.setMobile("13900139000"); service.updateDetail(12L, request); assertThat(guide.getMobile()).isEqualTo("13900139000"); assertThat(guide.getUsername()).isEqualTo("13900139000"); verify(userMapper).updateById(guide); } @Test void shouldRequireWhitelistBeforeEnablingAutoDisabledGuide() { GuideUser guide = new GuideUser(); guide.setId(12L); guide.setStatus(GuideUserService.STATUS_DISABLED); guide.setAutoDisabled(1); guide.setAutoDisableWhitelisted(0); when(userMapper.selectForUpdate(12L)).thenReturn(guide); assertThatThrownBy(() -> service.updateStatus(12L, GuideUserService.STATUS_ENABLED)) .isInstanceOf(BusinessException.class) .hasMessage("该导游由自动禁用规则停用,请先在自动禁用设置中添加白名单后再启用"); verify(userMapper, never()).updateById(any()); } @Test void shouldEnableAutoDisabledGuideAfterWhitelistIsAdded() { GuideUser guide = new GuideUser(); guide.setId(12L); guide.setStatus(GuideUserService.STATUS_DISABLED); guide.setAutoDisabled(1); guide.setAutoDisableWhitelisted(1); when(userMapper.selectForUpdate(12L)).thenReturn(guide); service.updateStatus(12L, GuideUserService.STATUS_ENABLED); assertThat(guide.getStatus()).isEqualTo(GuideUserService.STATUS_ENABLED); assertThat(guide.getAutoDisabled()).isZero(); verify(userMapper).updateById(guide); } @Test void shouldAutomaticallyEnableGuideWhenAddedToWhitelist() { GuideUser guide = new GuideUser(); guide.setId(12L); guide.setStatus(GuideUserService.STATUS_DISABLED); guide.setAutoDisabled(1); guide.setAutoDisableWhitelisted(0); when(userMapper.selectById(12L)).thenReturn(guide); service.updateWhitelist(12L, true); assertThat(guide.getAutoDisableWhitelisted()).isEqualTo(1); assertThat(guide.getStatus()).isEqualTo(GuideUserService.STATUS_ENABLED); assertThat(guide.getAutoDisabled()).isZero(); verify(userMapper).updateById(guide); } @Test void shouldKeepManuallyDisabledGuideDisabledWhenAddedToWhitelist() { GuideUser guide = new GuideUser(); guide.setId(12L); guide.setStatus(GuideUserService.STATUS_DISABLED); guide.setAutoDisabled(0); guide.setAutoDisableWhitelisted(0); when(userMapper.selectById(12L)).thenReturn(guide); service.updateWhitelist(12L, true); assertThat(guide.getAutoDisableWhitelisted()).isEqualTo(1); assertThat(guide.getStatus()).isEqualTo(GuideUserService.STATUS_DISABLED); assertThat(guide.getAutoDisabled()).isZero(); verify(userMapper).updateById(guide); } }