From 624b51292fe6eeabaca445b629c37c6462be28b7 Mon Sep 17 00:00:00 2001 From: wkc <978997012@qq.com> Date: Mon, 20 Apr 2026 15:22:56 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A1=A5=E5=85=85=E4=B8=AD=E4=BB=8B=E5=AF=BC?= =?UTF-8?q?=E5=85=A5=E6=B5=8B=E8=AF=95=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CcdiIntermediaryControllerTest.java | 172 +++++++++++++++++ ...terpriseRelationImportServiceImplTest.java | 172 +++++++++++++++++ ...termediaryPersonImportServiceImplTest.java | 176 ++++++++++++++++++ .../unit/intermediary-import-api.test.js | 45 +++++ .../unit/intermediary-import-dialog.test.js | 40 ++++ .../unit/intermediary-import-state.test.js | 28 +++ .../unit/intermediary-import-toolbar.test.js | 22 +++ 7 files changed, 655 insertions(+) create mode 100644 ccdi-info-collection/src/test/java/com/ruoyi/info/collection/controller/CcdiIntermediaryControllerTest.java create mode 100644 ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiIntermediaryEnterpriseRelationImportServiceImplTest.java create mode 100644 ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiIntermediaryPersonImportServiceImplTest.java create mode 100644 ruoyi-ui/tests/unit/intermediary-import-api.test.js create mode 100644 ruoyi-ui/tests/unit/intermediary-import-dialog.test.js create mode 100644 ruoyi-ui/tests/unit/intermediary-import-state.test.js create mode 100644 ruoyi-ui/tests/unit/intermediary-import-toolbar.test.js diff --git a/ccdi-info-collection/src/test/java/com/ruoyi/info/collection/controller/CcdiIntermediaryControllerTest.java b/ccdi-info-collection/src/test/java/com/ruoyi/info/collection/controller/CcdiIntermediaryControllerTest.java new file mode 100644 index 00000000..ce48450b --- /dev/null +++ b/ccdi-info-collection/src/test/java/com/ruoyi/info/collection/controller/CcdiIntermediaryControllerTest.java @@ -0,0 +1,172 @@ +package com.ruoyi.info.collection.controller; + +import com.ruoyi.common.constant.HttpStatus; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.info.collection.domain.excel.CcdiIntermediaryEnterpriseRelationExcel; +import com.ruoyi.info.collection.domain.excel.CcdiIntermediaryPersonExcel; +import com.ruoyi.info.collection.domain.vo.ImportResultVO; +import com.ruoyi.info.collection.domain.vo.ImportStatusVO; +import com.ruoyi.info.collection.domain.vo.IntermediaryEnterpriseRelationImportFailureVO; +import com.ruoyi.info.collection.domain.vo.IntermediaryPersonImportFailureVO; +import com.ruoyi.info.collection.service.ICcdiIntermediaryEnterpriseRelationImportService; +import com.ruoyi.info.collection.service.ICcdiIntermediaryPersonImportService; +import com.ruoyi.info.collection.service.ICcdiIntermediaryService; +import com.ruoyi.info.collection.utils.EasyExcelUtil; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.mock.web.MockMultipartFile; + +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class CcdiIntermediaryControllerTest { + + @InjectMocks + private CcdiIntermediaryController controller; + + @Mock + private ICcdiIntermediaryService intermediaryService; + + @Mock + private ICcdiIntermediaryPersonImportService personImportService; + + @Mock + private ICcdiIntermediaryEnterpriseRelationImportService enterpriseRelationImportService; + + @Test + void importPersonData_shouldReturnWarnWhenExcelHasNoRows() throws Exception { + MockMultipartFile file = new MockMultipartFile( + "file", + "intermediary-empty.xlsx", + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + "empty".getBytes(StandardCharsets.UTF_8) + ); + + try (MockedStatic mocked = mockStatic(EasyExcelUtil.class)) { + mocked.when(() -> EasyExcelUtil.importExcel(any(InputStream.class), eq(CcdiIntermediaryPersonExcel.class))) + .thenReturn(List.of()); + + AjaxResult result = controller.importPersonData(file); + + assertEquals(HttpStatus.ERROR, result.get(AjaxResult.CODE_TAG)); + assertEquals("至少需要一条数据", result.get(AjaxResult.MSG_TAG)); + } + } + + @Test + void importPersonData_shouldReturnSuccessWhenTaskCreated() throws Exception { + MockMultipartFile file = new MockMultipartFile( + "file", + "intermediary-person.xlsx", + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + "person".getBytes(StandardCharsets.UTF_8) + ); + CcdiIntermediaryPersonExcel excel = new CcdiIntermediaryPersonExcel(); + excel.setPersonId("320101199001010011"); + when(intermediaryService.importIntermediaryPerson(List.of(excel))).thenReturn("task-person"); + + try (MockedStatic mocked = mockStatic(EasyExcelUtil.class)) { + mocked.when(() -> EasyExcelUtil.importExcel(any(InputStream.class), eq(CcdiIntermediaryPersonExcel.class))) + .thenReturn(List.of(excel)); + + AjaxResult result = controller.importPersonData(file); + + assertEquals(HttpStatus.SUCCESS, result.get(AjaxResult.CODE_TAG)); + ImportResultVO data = (ImportResultVO) result.get(AjaxResult.DATA_TAG); + assertEquals("task-person", data.getTaskId()); + } + } + + @Test + void importEnterpriseRelationData_shouldReturnSuccessWhenTaskCreated() throws Exception { + MockMultipartFile file = new MockMultipartFile( + "file", + "intermediary-relation.xlsx", + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + "relation".getBytes(StandardCharsets.UTF_8) + ); + CcdiIntermediaryEnterpriseRelationExcel excel = new CcdiIntermediaryEnterpriseRelationExcel(); + excel.setOwnerPersonId("320101199001010011"); + excel.setSocialCreditCode("91330100MA27X12345"); + when(intermediaryService.importIntermediaryEnterpriseRelation(List.of(excel))).thenReturn("task-relation"); + + try (MockedStatic mocked = mockStatic(EasyExcelUtil.class)) { + mocked.when(() -> EasyExcelUtil.importExcel(any(InputStream.class), eq(CcdiIntermediaryEnterpriseRelationExcel.class))) + .thenReturn(List.of(excel)); + + AjaxResult result = controller.importEnterpriseRelationData(file); + + assertEquals(HttpStatus.SUCCESS, result.get(AjaxResult.CODE_TAG)); + ImportResultVO data = (ImportResultVO) result.get(AjaxResult.DATA_TAG); + assertEquals("task-relation", data.getTaskId()); + } + } + + @Test + void getEnterpriseRelationImportStatus_shouldDelegateToRelationImportService() { + ImportStatusVO statusVO = new ImportStatusVO(); + statusVO.setTaskId("task-status"); + when(enterpriseRelationImportService.getImportStatus("task-status")).thenReturn(statusVO); + + AjaxResult result = controller.getEnterpriseRelationImportStatus("task-status"); + + assertEquals(HttpStatus.SUCCESS, result.get(AjaxResult.CODE_TAG)); + assertEquals(statusVO, result.get(AjaxResult.DATA_TAG)); + } + + @Test + void getEnterpriseRelationImportFailures_shouldReturnPagedRows() { + IntermediaryEnterpriseRelationImportFailureVO failure1 = new IntermediaryEnterpriseRelationImportFailureVO(); + failure1.setOwnerPersonId("A1"); + IntermediaryEnterpriseRelationImportFailureVO failure2 = new IntermediaryEnterpriseRelationImportFailureVO(); + failure2.setOwnerPersonId("A2"); + when(enterpriseRelationImportService.getImportFailures("task-failures")).thenReturn(List.of(failure1, failure2)); + + TableDataInfo result = controller.getEnterpriseRelationImportFailures("task-failures", 2, 1); + + assertEquals(2, result.getTotal()); + assertEquals(1, result.getRows().size()); + assertEquals("A2", ((IntermediaryEnterpriseRelationImportFailureVO) result.getRows().get(0)).getOwnerPersonId()); + } + + @Test + void getPersonImportFailures_shouldReturnPagedRows() { + IntermediaryPersonImportFailureVO failure1 = new IntermediaryPersonImportFailureVO(); + failure1.setPersonId("A1"); + IntermediaryPersonImportFailureVO failure2 = new IntermediaryPersonImportFailureVO(); + failure2.setPersonId("A2"); + when(personImportService.getImportFailures("task-person-failures")).thenReturn(List.of(failure1, failure2)); + + TableDataInfo result = controller.getPersonImportFailures("task-person-failures", 2, 1); + + assertEquals(2, result.getTotal()); + assertEquals(1, result.getRows().size()); + assertEquals("A2", ((IntermediaryPersonImportFailureVO) result.getRows().get(0)).getPersonId()); + } + + @Test + void importEnterpriseRelationTemplate_shouldUseRelationTemplateName() { + try (MockedStatic mocked = mockStatic(EasyExcelUtil.class)) { + controller.importEnterpriseRelationTemplate(null); + + mocked.verify(() -> EasyExcelUtil.importTemplateWithDictDropdown( + null, + CcdiIntermediaryEnterpriseRelationExcel.class, + "中介实体关联关系信息" + )); + } + } +} diff --git a/ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiIntermediaryEnterpriseRelationImportServiceImplTest.java b/ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiIntermediaryEnterpriseRelationImportServiceImplTest.java new file mode 100644 index 00000000..d4eff2f9 --- /dev/null +++ b/ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiIntermediaryEnterpriseRelationImportServiceImplTest.java @@ -0,0 +1,172 @@ +package com.ruoyi.info.collection.service; + +import com.ruoyi.info.collection.domain.CcdiBizIntermediary; +import com.ruoyi.info.collection.domain.CcdiEnterpriseBaseInfo; +import com.ruoyi.info.collection.domain.CcdiIntermediaryEnterpriseRelation; +import com.ruoyi.info.collection.domain.excel.CcdiIntermediaryEnterpriseRelationExcel; +import com.ruoyi.info.collection.domain.vo.IntermediaryEnterpriseRelationImportFailureVO; +import com.ruoyi.info.collection.mapper.CcdiBizIntermediaryMapper; +import com.ruoyi.info.collection.mapper.CcdiEnterpriseBaseInfoMapper; +import com.ruoyi.info.collection.mapper.CcdiIntermediaryEnterpriseRelationMapper; +import com.ruoyi.info.collection.service.impl.CcdiIntermediaryEnterpriseRelationImportServiceImpl; +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 org.springframework.data.redis.core.HashOperations; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.ValueOperations; + +import java.util.List; +import java.util.concurrent.TimeUnit; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class CcdiIntermediaryEnterpriseRelationImportServiceImplTest { + + @InjectMocks + private CcdiIntermediaryEnterpriseRelationImportServiceImpl service; + + @Mock + private CcdiIntermediaryEnterpriseRelationMapper relationMapper; + + @Mock + private CcdiBizIntermediaryMapper intermediaryMapper; + + @Mock + private CcdiEnterpriseBaseInfoMapper enterpriseBaseInfoMapper; + + @Mock + private RedisTemplate redisTemplate; + + @Mock + private HashOperations hashOperations; + + @Mock + private ValueOperations valueOperations; + + @Test + void importEnterpriseRelationAsync_shouldFailWhenOwnerPersonIdDoesNotExist() { + CcdiIntermediaryEnterpriseRelationExcel excel = buildExcel("320101199001010014", "91330100MA27X12345"); + prepareFailureRedisMocks(); + when(intermediaryMapper.selectList(any())).thenReturn(List.of()); + when(enterpriseBaseInfoMapper.selectList(any())).thenReturn(List.of(enterprise("91330100MA27X12345"))); + + service.importAsync(List.of(excel), "task-owner-miss", "tester"); + + verify(relationMapper, never()).insertBatch(any()); + IntermediaryEnterpriseRelationImportFailureVO failure = + firstFailure("import:intermediary-enterprise-relation:task-owner-miss:failures"); + assertEquals("320101199001010014", failure.getOwnerPersonId()); + assertTrue(failure.getErrorMessage().contains("中介本人不存在")); + } + + @Test + void importEnterpriseRelationAsync_shouldFailWhenEnterpriseDoesNotExist() { + CcdiIntermediaryEnterpriseRelationExcel excel = buildExcel("320101199001010014", "91330100MA27X12345"); + prepareFailureRedisMocks(); + when(intermediaryMapper.selectList(any())).thenReturn(List.of(owner("owner-biz", "320101199001010014"))); + when(enterpriseBaseInfoMapper.selectList(any())).thenReturn(List.of()); + when(relationMapper.batchExistsByCombinations(any())).thenReturn(List.of()); + + service.importAsync(List.of(excel), "task-ent-miss", "tester"); + + verify(relationMapper, never()).insertBatch(any()); + IntermediaryEnterpriseRelationImportFailureVO failure = + firstFailure("import:intermediary-enterprise-relation:task-ent-miss:failures"); + assertEquals("91330100MA27X12345", failure.getSocialCreditCode()); + assertTrue(failure.getErrorMessage().contains("机构表")); + } + + @Test + void importEnterpriseRelationAsync_shouldRejectFileDuplicateAndDbDuplicate() { + CcdiIntermediaryEnterpriseRelationExcel duplicateInDb = buildExcel("320101199001010014", "91330100MA27X12345"); + CcdiIntermediaryEnterpriseRelationExcel duplicateInFile1 = buildExcel("320101199003030035", "91330100MA27X12346"); + CcdiIntermediaryEnterpriseRelationExcel duplicateInFile2 = buildExcel("320101199003030035", "91330100MA27X12346"); + prepareFailureRedisMocks(); + when(intermediaryMapper.selectList(any())).thenReturn(List.of( + owner("owner-biz-1", "320101199001010014"), + owner("owner-biz-2", "320101199003030035") + )); + when(enterpriseBaseInfoMapper.selectList(any())).thenReturn(List.of( + enterprise("91330100MA27X12345"), + enterprise("91330100MA27X12346") + )); + when(relationMapper.batchExistsByCombinations(any())).thenReturn(List.of("owner-biz-1|91330100MA27X12345")); + + service.importAsync(List.of(duplicateInDb, duplicateInFile1, duplicateInFile2), "task-duplicate", "tester"); + + ArgumentCaptor> captor = ArgumentCaptor.forClass(List.class); + verify(relationMapper).insertBatch(captor.capture()); + assertEquals(1, captor.getValue().size()); + assertEquals("owner-biz-2", captor.getValue().get(0).getIntermediaryBizId()); + IntermediaryEnterpriseRelationImportFailureVO failure = + firstFailure("import:intermediary-enterprise-relation:task-duplicate:failures"); + assertTrue(failure.getErrorMessage().contains("重复") || failure.getErrorMessage().contains("已存在")); + } + + @Test + void importEnterpriseRelationAsync_shouldImportSuccessWhenOwnerAndEnterpriseExist() { + CcdiIntermediaryEnterpriseRelationExcel excel = buildExcel("320101199001010014", "91330100MA27X12345"); + prepareStatusRedisMock(); + when(intermediaryMapper.selectList(any())).thenReturn(List.of(owner("owner-biz", "320101199001010014"))); + when(enterpriseBaseInfoMapper.selectList(any())).thenReturn(List.of(enterprise("91330100MA27X12345"))); + when(relationMapper.batchExistsByCombinations(any())).thenReturn(List.of()); + + service.importAsync(List.of(excel), "task-success", "tester"); + + ArgumentCaptor> captor = ArgumentCaptor.forClass(List.class); + verify(relationMapper).insertBatch(captor.capture()); + assertEquals(1, captor.getValue().size()); + assertEquals("owner-biz", captor.getValue().get(0).getIntermediaryBizId()); + verify(valueOperations, never()).set(any(), any(), any(Long.class), any(TimeUnit.class)); + } + + private void prepareStatusRedisMock() { + when(redisTemplate.opsForHash()).thenReturn(hashOperations); + } + + private void prepareFailureRedisMocks() { + prepareStatusRedisMock(); + when(redisTemplate.opsForValue()).thenReturn(valueOperations); + } + + private IntermediaryEnterpriseRelationImportFailureVO firstFailure(String key) { + ArgumentCaptor failureCaptor = ArgumentCaptor.forClass(Object.class); + verify(valueOperations).set(org.mockito.ArgumentMatchers.eq(key), failureCaptor.capture(), + org.mockito.ArgumentMatchers.eq(7L), org.mockito.ArgumentMatchers.eq(TimeUnit.DAYS)); + return (IntermediaryEnterpriseRelationImportFailureVO) ((List) failureCaptor.getValue()).get(0); + } + + private CcdiIntermediaryEnterpriseRelationExcel buildExcel(String ownerPersonId, String socialCreditCode) { + CcdiIntermediaryEnterpriseRelationExcel excel = new CcdiIntermediaryEnterpriseRelationExcel(); + excel.setOwnerPersonId(ownerPersonId); + excel.setSocialCreditCode(socialCreditCode); + excel.setRelationPersonPost("董事"); + excel.setRemark("备注"); + return excel; + } + + private CcdiBizIntermediary owner(String bizId, String personId) { + CcdiBizIntermediary owner = new CcdiBizIntermediary(); + owner.setBizId(bizId); + owner.setPersonId(personId); + owner.setPersonSubType("本人"); + return owner; + } + + private CcdiEnterpriseBaseInfo enterprise(String socialCreditCode) { + CcdiEnterpriseBaseInfo enterprise = new CcdiEnterpriseBaseInfo(); + enterprise.setSocialCreditCode(socialCreditCode); + enterprise.setEnterpriseName("机构" + socialCreditCode.substring(socialCreditCode.length() - 2)); + return enterprise; + } +} diff --git a/ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiIntermediaryPersonImportServiceImplTest.java b/ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiIntermediaryPersonImportServiceImplTest.java new file mode 100644 index 00000000..31680827 --- /dev/null +++ b/ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiIntermediaryPersonImportServiceImplTest.java @@ -0,0 +1,176 @@ +package com.ruoyi.info.collection.service; + +import com.ruoyi.common.annotation.DictDropdown; +import com.ruoyi.info.collection.domain.CcdiBizIntermediary; +import com.ruoyi.info.collection.domain.excel.CcdiIntermediaryPersonExcel; +import com.ruoyi.info.collection.domain.vo.IntermediaryPersonImportFailureVO; +import com.ruoyi.info.collection.mapper.CcdiBizIntermediaryMapper; +import com.ruoyi.info.collection.service.impl.CcdiIntermediaryPersonImportServiceImpl; +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 org.springframework.data.redis.core.HashOperations; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.ValueOperations; + +import java.lang.reflect.Field; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class CcdiIntermediaryPersonImportServiceImplTest { + + @InjectMocks + private CcdiIntermediaryPersonImportServiceImpl service; + + @Mock + private CcdiBizIntermediaryMapper intermediaryMapper; + + @Mock + private RedisTemplate redisTemplate; + + @Mock + private HashOperations hashOperations; + + @Mock + private ValueOperations valueOperations; + + @Test + void intermediaryPersonExcel_shouldUsePersonSubTypeDropdownAndDropRelationType() throws Exception { + Field personSubTypeField = CcdiIntermediaryPersonExcel.class.getDeclaredField("personSubType"); + DictDropdown dictDropdown = personSubTypeField.getAnnotation(DictDropdown.class); + + assertNotNull(dictDropdown); + assertEquals("ccdi_person_sub_type", dictDropdown.dictType()); + assertThrows(NoSuchFieldException.class, () -> CcdiIntermediaryPersonExcel.class.getDeclaredField("relationType")); + assertThrows(NoSuchFieldException.class, () -> IntermediaryPersonImportFailureVO.class.getDeclaredField("relationType")); + } + + @Test + void importPersonAsync_shouldFailWhenOwnerRowContainsOwnerPersonIdReference() { + CcdiIntermediaryPersonExcel owner = buildOwnerExcel("320101199001010014"); + owner.setRelatedNumId("320101199105050053"); + prepareFailureRedisMocks(); + + service.importPersonAsync(List.of(owner), "task-owner", "tester"); + + verify(intermediaryMapper, never()).insertBatch(any()); + IntermediaryPersonImportFailureVO failure = firstFailure("import:intermediary:task-owner:failures"); + assertEquals("320101199105050053", failure.getRelatedNumId()); + assertTrue(failure.getErrorMessage().contains("本人")); + } + + @Test + void importPersonAsync_shouldFailWhenRelativeRowMissesOwnerPersonId() { + CcdiIntermediaryPersonExcel relative = buildRelativeExcel("320101199201010027", "配偶", null); + prepareFailureRedisMocks(); + + service.importPersonAsync(List.of(relative), "task-relative", "tester"); + + verify(intermediaryMapper, never()).insertBatch(any()); + IntermediaryPersonImportFailureVO failure = firstFailure("import:intermediary:task-relative:failures"); + assertEquals("320101199201010027", failure.getPersonId()); + assertTrue(failure.getErrorMessage().contains("关联中介本人证件号码")); + } + + @Test + void importPersonAsync_shouldAllowRelativeReferencingSuccessfulOwnerInSameFile() { + CcdiIntermediaryPersonExcel owner = buildOwnerExcel("320101199001010014"); + CcdiIntermediaryPersonExcel relative = buildRelativeExcel("320101199201010027", "配偶", "320101199001010014"); + prepareStatusRedisMock(); + when(intermediaryMapper.selectList(any())).thenReturn(List.of()); + + service.importPersonAsync(List.of(owner, relative), "task-mixed", "tester"); + + ArgumentCaptor> captor = ArgumentCaptor.forClass(List.class); + verify(intermediaryMapper).insertBatch(captor.capture()); + assertEquals(2, captor.getValue().size()); + assertEquals("本人", captor.getValue().get(0).getPersonSubType()); + assertEquals("320101199001010014", captor.getValue().get(1).getRelatedNumId()); + verify(valueOperations, never()).set(any(), any(), any(Long.class), any(TimeUnit.class)); + } + + @Test + void importPersonAsync_shouldAllowSameRelativePersonIdUnderDifferentOwners() { + CcdiIntermediaryPersonExcel owner1 = buildOwnerExcel("320101199001010014"); + CcdiIntermediaryPersonExcel owner2 = buildOwnerExcel("320101199003030035"); + CcdiIntermediaryPersonExcel relative1 = buildRelativeExcel("320101199201010027", "配偶", "320101199001010014"); + CcdiIntermediaryPersonExcel relative2 = buildRelativeExcel("320101199201010027", "配偶", "320101199003030035"); + prepareStatusRedisMock(); + when(intermediaryMapper.selectList(any())).thenReturn(List.of()); + + service.importPersonAsync(List.of(owner1, owner2, relative1, relative2), "task-owner-scope", "tester"); + + ArgumentCaptor> captor = ArgumentCaptor.forClass(List.class); + verify(intermediaryMapper).insertBatch(captor.capture()); + assertEquals(4, captor.getValue().size()); + verify(valueOperations, never()).set(any(), any(), any(Long.class), any(TimeUnit.class)); + } + + @Test + void importPersonAsync_shouldRejectDuplicateRelativeUnderSameOwner() { + CcdiIntermediaryPersonExcel owner = buildOwnerExcel("320101199001010014"); + CcdiIntermediaryPersonExcel relative1 = buildRelativeExcel("320101199201010027", "配偶", "320101199001010014"); + CcdiIntermediaryPersonExcel relative2 = buildRelativeExcel("320101199201010027", "配偶", "320101199001010014"); + prepareFailureRedisMocks(); + when(intermediaryMapper.selectList(any())).thenReturn(List.of()); + + service.importPersonAsync(List.of(owner, relative1, relative2), "task-duplicate", "tester"); + + ArgumentCaptor> captor = ArgumentCaptor.forClass(List.class); + verify(intermediaryMapper).insertBatch(captor.capture()); + assertEquals(2, captor.getValue().size()); + IntermediaryPersonImportFailureVO failure = firstFailure("import:intermediary:task-duplicate:failures"); + assertEquals("320101199201010027", failure.getPersonId()); + assertTrue(failure.getErrorMessage().contains("重复")); + } + + private void prepareStatusRedisMock() { + when(redisTemplate.opsForHash()).thenReturn(hashOperations); + } + + private void prepareFailureRedisMocks() { + prepareStatusRedisMock(); + when(redisTemplate.opsForValue()).thenReturn(valueOperations); + } + + private IntermediaryPersonImportFailureVO firstFailure(String key) { + ArgumentCaptor failureCaptor = ArgumentCaptor.forClass(Object.class); + verify(valueOperations).set(org.mockito.ArgumentMatchers.eq(key), failureCaptor.capture(), + org.mockito.ArgumentMatchers.eq(7L), org.mockito.ArgumentMatchers.eq(TimeUnit.DAYS)); + return (IntermediaryPersonImportFailureVO) ((List) failureCaptor.getValue()).get(0); + } + + private CcdiIntermediaryPersonExcel buildOwnerExcel(String personId) { + CcdiIntermediaryPersonExcel excel = new CcdiIntermediaryPersonExcel(); + excel.setName("中介本人" + personId.substring(personId.length() - 2)); + excel.setPersonType("中介"); + excel.setPersonSubType("本人"); + excel.setIdType("身份证"); + excel.setPersonId(personId); + return excel; + } + + private CcdiIntermediaryPersonExcel buildRelativeExcel(String personId, String personSubType, String ownerPersonId) { + CcdiIntermediaryPersonExcel excel = new CcdiIntermediaryPersonExcel(); + excel.setName("中介亲属" + personId.substring(personId.length() - 2)); + excel.setPersonType("中介"); + excel.setPersonSubType(personSubType); + excel.setIdType("身份证"); + excel.setPersonId(personId); + excel.setRelatedNumId(ownerPersonId); + return excel; + } +} diff --git a/ruoyi-ui/tests/unit/intermediary-import-api.test.js b/ruoyi-ui/tests/unit/intermediary-import-api.test.js new file mode 100644 index 00000000..49074d20 --- /dev/null +++ b/ruoyi-ui/tests/unit/intermediary-import-api.test.js @@ -0,0 +1,45 @@ +const assert = require("assert"); +const fs = require("fs"); +const path = require("path"); + +const apiPath = path.resolve( + __dirname, + "../../src/api/ccdiIntermediary.js" +); +const source = fs.readFileSync(apiPath, "utf8"); + +[ + "export function importPersonTemplate()", + "export function importPersonData(data, updateSupport)", + "export function getPersonImportStatus(taskId)", + "export function getPersonImportFailures(taskId, pageNum, pageSize)", + "export function importEnterpriseRelationTemplate()", + "export function importEnterpriseRelationData(data, updateSupport)", + "export function getEnterpriseRelationImportStatus(taskId)", + "export function getEnterpriseRelationImportFailures(taskId, pageNum, pageSize)", + "/ccdi/intermediary/importPersonTemplate", + "/ccdi/intermediary/importPersonData", + "/ccdi/intermediary/importPersonStatus/", + "/ccdi/intermediary/importPersonFailures/", + "/ccdi/intermediary/importEnterpriseRelationTemplate", + "/ccdi/intermediary/importEnterpriseRelationData", + "/ccdi/intermediary/importEnterpriseRelationStatus/", + "/ccdi/intermediary/importEnterpriseRelationFailures/", +].forEach((token) => { + assert(source.includes(token), `中介导入 API 缺少关键方法或路径: ${token}`); +}); + +[ + "export function importEntityTemplate()", + "export function importEntityData(data, updateSupport)", + "export function getEntityImportStatus(taskId)", + "export function getEntityImportFailures(taskId, pageNum, pageSize)", + "/ccdi/intermediary/importEntityTemplate", + "/ccdi/intermediary/importEntityData", + "/ccdi/intermediary/importEntityStatus/", + "/ccdi/intermediary/importEntityFailures/", +].forEach((token) => { + assert(!source.includes(token), `中介导入 API 不应继续保留旧机构导入接口: ${token}`); +}); + +console.log("intermediary-import-api test passed"); diff --git a/ruoyi-ui/tests/unit/intermediary-import-dialog.test.js b/ruoyi-ui/tests/unit/intermediary-import-dialog.test.js new file mode 100644 index 00000000..6137f3a3 --- /dev/null +++ b/ruoyi-ui/tests/unit/intermediary-import-dialog.test.js @@ -0,0 +1,40 @@ +const assert = require("assert"); +const fs = require("fs"); +const path = require("path"); + +const componentPath = path.resolve( + __dirname, + "../../src/views/ccdiIntermediary/components/ImportDialog.vue" +); +const source = fs.readFileSync(componentPath, "utf8"); + +[ + "scene: {", + "default: PERSON_SCENE", + "this.scene === ENTERPRISE_RELATION_SCENE", + "/ccdi/intermediary/importPersonData", + "/ccdi/intermediary/importEnterpriseRelationData", + "ccdi/intermediary/importPersonTemplate", + "ccdi/intermediary/importEnterpriseRelationTemplate", + "getPersonImportStatus", + "getEnterpriseRelationImportStatus", + "personSubType 为字典下拉;", + "本人行 relatedNumId 为空;", + "亲属行 relatedNumId 填关联中介本人证件号码。", + "只导入中介与机构关系;", + "统一社会信用代码必须已存在于系统机构表。" +].forEach((token) => { + assert(source.includes(token), `中介导入弹窗缺少场景驱动能力: ${token}`); +}); + +[ + 'label="导入类型"', + "个人中介", + "机构中介", + "formData.importType", + "handleImportTypeChange" +].forEach((token) => { + assert(!source.includes(token), `中介导入弹窗不应继续保留内部类型切换: ${token}`); +}); + +console.log("intermediary-import-dialog test passed"); diff --git a/ruoyi-ui/tests/unit/intermediary-import-state.test.js b/ruoyi-ui/tests/unit/intermediary-import-state.test.js new file mode 100644 index 00000000..7dfffcaf --- /dev/null +++ b/ruoyi-ui/tests/unit/intermediary-import-state.test.js @@ -0,0 +1,28 @@ +const assert = require("assert"); +const fs = require("fs"); +const path = require("path"); + +const pagePath = path.resolve( + __dirname, + "../../src/views/ccdiIntermediary/index.vue" +); +const source = fs.readFileSync(pagePath, "utf8"); + +[ + "ccdi_intermediary_person_import_task", + "ccdi_intermediary_enterprise_relation_import_task", + "personFailureDialogVisible", + "enterpriseRelationFailureDialogVisible", + "personFailureList", + "enterpriseRelationFailureList", + "getPersonImportFailures", + "getEnterpriseRelationImportFailures", + "getPersonImportStatus", + "getEnterpriseRelationImportStatus", + "refreshCurrentDetail()", + "restoreImportTasks()" +].forEach((token) => { + assert(source.includes(token), `中介导入页面缺少任务恢复或失败记录状态: ${token}`); +}); + +console.log("intermediary-import-state test passed"); diff --git a/ruoyi-ui/tests/unit/intermediary-import-toolbar.test.js b/ruoyi-ui/tests/unit/intermediary-import-toolbar.test.js new file mode 100644 index 00000000..b09333c8 --- /dev/null +++ b/ruoyi-ui/tests/unit/intermediary-import-toolbar.test.js @@ -0,0 +1,22 @@ +const assert = require("assert"); +const fs = require("fs"); +const path = require("path"); + +const pagePath = path.resolve( + __dirname, + "../../src/views/ccdiIntermediary/index.vue" +); +const source = fs.readFileSync(pagePath, "utf8"); + +[ + "导入中介信息", + "导入中介实体关联关系", + "查看中介信息导入失败记录", + "查看中介实体关联关系导入失败记录", + "personImportTask", + "enterpriseRelationImportTask" +].forEach((token) => { + assert(source.includes(token), `中介导入页面缺少按钮或任务状态: ${token}`); +}); + +console.log("intermediary-import-toolbar test passed");