package com.ruoyi.user.service.impl; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import com.ruoyi.common.core.exception.ServiceException; import com.ruoyi.user.domain.AccountCertificationApply; import com.ruoyi.user.domain.AccountUser; import com.ruoyi.user.domain.dto.CompanyCertificationApplyDto; import com.ruoyi.user.domain.vo.AccountCertificationStatusVo; import com.ruoyi.user.domain.vo.CompanyCertificationStatusVo; import com.ruoyi.user.mapper.AccountCertificationApplyMapper; import com.ruoyi.user.mapper.AccountCertificationMapper; import com.ruoyi.user.mapper.AccountUserMapper; /** * 会员主体认证申请 服务单元测试 * * 覆盖 C 端个人主体认证提交:正常提交(落待审核申请 + 联动认证状态为审核中)、 * 待审核申请存在时防重拒绝、以及会员ID/姓名/身份证号缺失时的入参校验; * 另覆盖认证状态查询:有申请时回填资料与驳回原因、无申请/无登录态时返回空对象。 * * @author hugray */ @ExtendWith(MockitoExtension.class) class AccountCertificationApplyServiceImplTest { private static final Long USER_ID = 66L; private static final String REAL_NAME = "张三"; private static final String ID_CARD = "110101199003071234"; @Mock private AccountCertificationApplyMapper certificationApplyMapper; @Mock private AccountCertificationMapper certificationMapper; @Mock private AccountUserMapper accountUserMapper; @InjectMocks private AccountCertificationApplyServiceImpl certificationApplyService; @Test @DisplayName("正常提交应落一条待审核的个人申请并把认证状态置为审核中") void submitPersonalCertification_shouldInsertPendingApplyAndSetAuditing() { when(certificationApplyMapper.countPendingPersonalApplyByUserId(USER_ID)).thenReturn(0); when(certificationApplyMapper.insertCertificationApply(any(AccountCertificationApply.class))).thenReturn(1); certificationApplyService.submitPersonalCertification(USER_ID, " 张三 ", "11010119900307123x"); // 申请快照:个人主体、待审核、姓名去空白、身份证号末位小写 x 统一转大写 ArgumentCaptor applyCaptor = ArgumentCaptor.forClass(AccountCertificationApply.class); verify(certificationApplyMapper).insertCertificationApply(applyCaptor.capture()); AccountCertificationApply saved = applyCaptor.getValue(); assertEquals(USER_ID, saved.getUserId()); assertEquals("1", saved.getSubjectType()); assertEquals("0", saved.getAuditStatus()); assertEquals(REAL_NAME, saved.getRealName()); assertEquals("11010119900307123X", saved.getIdCard()); // 状态联动:account_user.auth_status = '1'(审核中) ArgumentCaptor userCaptor = ArgumentCaptor.forClass(AccountUser.class); verify(accountUserMapper).updateUserAuthStatus(userCaptor.capture()); assertEquals(USER_ID, userCaptor.getValue().getUserId()); assertEquals("1", userCaptor.getValue().getAuthStatus()); } @Test @DisplayName("已有待审核申请时应拒绝重复提交且不落库") void submitPersonalCertification_shouldThrow_whenPendingApplyExists() { when(certificationApplyMapper.countPendingPersonalApplyByUserId(USER_ID)).thenReturn(1); ServiceException ex = assertThrows(ServiceException.class, () -> certificationApplyService.submitPersonalCertification(USER_ID, REAL_NAME, ID_CARD)); assertEquals("已有实名认证申请正在审核中,请勿重复提交", ex.getMessage()); verify(certificationApplyMapper, never()).insertCertificationApply(any(AccountCertificationApply.class)); } @Test @DisplayName("会员ID为空时应抛出提交失败") void submitPersonalCertification_shouldThrow_whenUserIdNull() { ServiceException ex = assertThrows(ServiceException.class, () -> certificationApplyService.submitPersonalCertification(null, REAL_NAME, ID_CARD)); assertEquals("提交实名认证失败", ex.getMessage()); verify(certificationApplyMapper, never()).insertCertificationApply(any(AccountCertificationApply.class)); } @Test @DisplayName("姓名为空时应抛出提示且不落库") void submitPersonalCertification_shouldThrow_whenRealNameBlank() { ServiceException ex = assertThrows(ServiceException.class, () -> certificationApplyService.submitPersonalCertification(USER_ID, "", ID_CARD)); assertEquals("姓名与身份证号不能为空", ex.getMessage()); verify(certificationApplyMapper, never()).insertCertificationApply(any(AccountCertificationApply.class)); } @Test @DisplayName("身份证号为空时应抛出提示且不落库") void submitPersonalCertification_shouldThrow_whenIdCardBlank() { ServiceException ex = assertThrows(ServiceException.class, () -> certificationApplyService.submitPersonalCertification(USER_ID, REAL_NAME, null)); assertEquals("姓名与身份证号不能为空", ex.getMessage()); verify(certificationApplyMapper, never()).insertCertificationApply(any(AccountCertificationApply.class)); } @Test @DisplayName("查询认证状态应回填最近一次申请的资料与驳回原因") void getCertificationStatus_shouldFillFromLatestApply() { AccountCertificationApply apply = new AccountCertificationApply(); apply.setRealName(REAL_NAME); apply.setIdCard(ID_CARD); apply.setAuditStatus("2"); apply.setAuditRemark("身份证照片不清晰,请重新提交"); when(certificationApplyMapper.selectLatestPersonalApplyByUserId(USER_ID)).thenReturn(apply); AccountCertificationStatusVo vo = certificationApplyService.getCertificationStatus(USER_ID); assertEquals(REAL_NAME, vo.getRealName()); assertEquals(ID_CARD, vo.getIdCard()); assertEquals("2", vo.getAuditStatus()); assertEquals("身份证照片不清晰,请重新提交", vo.getAuditRemark()); } @Test @DisplayName("从未提交时查询认证状态应返回空对象而非 null") void getCertificationStatus_shouldReturnEmpty_whenNoApply() { when(certificationApplyMapper.selectLatestPersonalApplyByUserId(USER_ID)).thenReturn(null); AccountCertificationStatusVo vo = certificationApplyService.getCertificationStatus(USER_ID); assertNull(vo.getRealName()); assertNull(vo.getIdCard()); assertNull(vo.getAuditStatus()); assertNull(vo.getAuditRemark()); } @Test @DisplayName("会员ID为空时查询认证状态应返回空对象且不查库") void getCertificationStatus_shouldReturnEmpty_whenUserIdNull() { AccountCertificationStatusVo vo = certificationApplyService.getCertificationStatus(null); assertNull(vo.getAuditStatus()); verify(certificationApplyMapper, never()).selectLatestPersonalApplyByUserId(any()); } @Test @DisplayName("正常提交应落一条待审核的企业申请并把企业认证状态置为审核中") void submitCompanyCertification_shouldInsertPendingApplyAndSetCompanyAuditing() { when(certificationApplyMapper.countPendingCompanyApplyByUserId(USER_ID)).thenReturn(0); when(certificationApplyMapper.insertCertificationApply(any(AccountCertificationApply.class))).thenReturn(1); certificationApplyService.submitCompanyCertification(USER_ID, buildCompanyDto("3", "1", null)); // 申请快照:企业主体(3)、待审核、无有效期(长期) ArgumentCaptor applyCaptor = ArgumentCaptor.forClass(AccountCertificationApply.class); verify(certificationApplyMapper).insertCertificationApply(applyCaptor.capture()); AccountCertificationApply saved = applyCaptor.getValue(); assertEquals(USER_ID, saved.getUserId()); assertEquals("3", saved.getSubjectType()); assertEquals("0", saved.getAuditStatus()); assertEquals("测试企业有限公司", saved.getCompanyName()); // 状态联动:account_user.company_auth_status = '1'(审核中),且不动个人 auth_status ArgumentCaptor userCaptor = ArgumentCaptor.forClass(AccountUser.class); verify(accountUserMapper).updateUserCompanyAuthStatus(userCaptor.capture()); assertEquals("1", userCaptor.getValue().getCompanyAuthStatus()); verify(accountUserMapper, never()).updateUserAuthStatus(any(AccountUser.class)); } @Test @DisplayName("有效期选「短期」但未填起止日期时应抛出提示且不落库") void submitCompanyCertification_shouldThrow_whenShortTermPeriodMissing() { ServiceException ex = assertThrows(ServiceException.class, () -> certificationApplyService.submitCompanyCertification(USER_ID, buildCompanyDto("3", "2", null))); assertEquals("请选择营业执照有效期起止日期", ex.getMessage()); verify(certificationApplyMapper, never()).insertCertificationApply(any(AccountCertificationApply.class)); } @Test @DisplayName("已有待审核的企业申请时应拒绝重复提交且不落库") void submitCompanyCertification_shouldThrow_whenPendingApplyExists() { when(certificationApplyMapper.countPendingCompanyApplyByUserId(USER_ID)).thenReturn(1); ServiceException ex = assertThrows(ServiceException.class, () -> certificationApplyService.submitCompanyCertification(USER_ID, buildCompanyDto("2", "1", null))); assertEquals("已有企业认证申请正在审核中,请勿重复提交", ex.getMessage()); verify(certificationApplyMapper, never()).insertCertificationApply(any(AccountCertificationApply.class)); } @Test @DisplayName("会员ID为空时提交企业认证应抛出提交失败") void submitCompanyCertification_shouldThrow_whenUserIdNull() { ServiceException ex = assertThrows(ServiceException.class, () -> certificationApplyService.submitCompanyCertification(null, buildCompanyDto("3", "1", null))); assertEquals("提交企业认证失败", ex.getMessage()); verify(certificationApplyMapper, never()).insertCertificationApply(any(AccountCertificationApply.class)); } @Test @DisplayName("查询企业认证状态应回填最近一次申请的资料与驳回原因") void getCompanyCertificationStatus_shouldFillFromLatestApply() { AccountCertificationApply apply = new AccountCertificationApply(); apply.setSubjectType("3"); apply.setCompanyName("测试企业有限公司"); apply.setAuditStatus("2"); apply.setAuditRemark("营业执照照片不清晰,请重新提交"); when(certificationApplyMapper.selectLatestCompanyApplyByUserId(USER_ID)).thenReturn(apply); CompanyCertificationStatusVo vo = certificationApplyService.getCompanyCertificationStatus(USER_ID); assertEquals("3", vo.getSubjectType()); assertEquals("测试企业有限公司", vo.getCompanyName()); assertEquals("2", vo.getAuditStatus()); assertEquals("营业执照照片不清晰,请重新提交", vo.getAuditRemark()); } @Test @DisplayName("从未提交企业认证时查询状态应返回空对象而非 null") void getCompanyCertificationStatus_shouldReturnEmpty_whenNoApply() { when(certificationApplyMapper.selectLatestCompanyApplyByUserId(USER_ID)).thenReturn(null); CompanyCertificationStatusVo vo = certificationApplyService.getCompanyCertificationStatus(USER_ID); assertNull(vo.getCompanyName()); assertNull(vo.getAuditStatus()); assertNull(vo.getAuditRemark()); } /** 构造一份字段齐全的企业认证入参:validType 控制营业执照有效期类型,licensePeriod 为起止日期 */ private CompanyCertificationApplyDto buildCompanyDto(String subjectType, String validType, String licensePeriod) { CompanyCertificationApplyDto dto = new CompanyCertificationApplyDto(); dto.setSubjectType(subjectType); dto.setCompanyName("测试企业有限公司"); dto.setLicenseImage("/upload/license.png"); dto.setCreditCode("91310000MA1FL0XXXX"); dto.setLicenseValidType(validType); dto.setLicenseValidPeriod(licensePeriod); dto.setRegisteredRegion("上海市 上海市 浦东新区"); dto.setCompanyAddress("浦东新区世纪大道 1 号"); dto.setLegalPerson("李四"); dto.setLegalIdCardFront("/upload/id-front.png"); dto.setLegalIdCardBack("/upload/id-back.png"); dto.setLegalIdValidType("1"); dto.setLegalIdValidPeriod(null); dto.setLegalIdAddress("浦东新区世纪大道 1 号"); dto.setContactName("王五"); dto.setContactPhone("13800000000"); dto.setContactEmail("test@example.com"); dto.setPayeeType("1"); dto.setBankCardImage("/upload/bank.png"); dto.setBankCardNo("6222020000000000000"); dto.setBankCity("上海市"); return dto; } }