From bdd22ed132e0481202dfe7bc415908ac5cd3f0a0 Mon Sep 17 00:00:00 2001 From: chenbowen Date: Thu, 18 Dec 2025 22:26:05 +0800 Subject: [PATCH] =?UTF-8?q?1.=20=E5=8E=BB=E9=99=A4=E9=83=A8=E9=97=A8?= =?UTF-8?q?=E7=BC=96=E7=A0=81=E4=B8=BA=E7=A9=BA=E6=97=B6=EF=BC=8C=E9=BB=98?= =?UTF-8?q?=E8=AE=A4=E4=BD=BF=E7=94=A8=20id=20=E4=BD=9C=E4=B8=BA=20code=20?= =?UTF-8?q?=E8=BF=9B=E8=A1=8C=E6=98=A0=E5=B0=84=E7=9A=84=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../iwork/impl/IWorkSyncProcessorImpl.java | 4 +- .../impl/IWorkSyncProcessorImplTest.java | 141 ++++++++++++++++++ .../iwork/impl/IWorkSyncServiceImplTest.java | 62 ++++++++ 3 files changed, 205 insertions(+), 2 deletions(-) create mode 100644 zt-module-system/zt-module-system-server/src/test/java/com/zt/plat/module/system/service/integration/iwork/impl/IWorkSyncProcessorImplTest.java create mode 100644 zt-module-system/zt-module-system-server/src/test/java/com/zt/plat/module/system/service/integration/iwork/impl/IWorkSyncServiceImplTest.java diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/integration/iwork/impl/IWorkSyncProcessorImpl.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/integration/iwork/impl/IWorkSyncProcessorImpl.java index bbabb064..abf94ac4 100644 --- a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/integration/iwork/impl/IWorkSyncProcessorImpl.java +++ b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/integration/iwork/impl/IWorkSyncProcessorImpl.java @@ -513,7 +513,7 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor { req.setIsGroup(Boolean.FALSE); req.setDeptSource(DeptSourceEnum.IWORK.getSource()); req.setExternalSystemCode(ExternalPlatformEnum.IWORK.getCode()); - req.setExternalDeptCode(StrUtil.blankToDefault(trimToNull(data.getSubcompanycode()), String.valueOf(data.getId()))); + req.setExternalDeptCode(trimToNull(data.getSubcompanycode())); req.setExternalDeptName(data.getSubcompanyname()); return req; } @@ -533,7 +533,7 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor { req.setIsGroup(Boolean.FALSE); req.setDeptSource(DeptSourceEnum.IWORK.getSource()); req.setExternalSystemCode(ExternalPlatformEnum.IWORK.getCode()); - req.setExternalDeptCode(StrUtil.blankToDefault(trimToNull(data.getDepartmentcode()), String.valueOf(data.getId()))); + req.setExternalDeptCode(trimToNull(data.getDepartmentcode())); req.setExternalDeptName(data.getDepartmentname()); return req; } diff --git a/zt-module-system/zt-module-system-server/src/test/java/com/zt/plat/module/system/service/integration/iwork/impl/IWorkSyncProcessorImplTest.java b/zt-module-system/zt-module-system-server/src/test/java/com/zt/plat/module/system/service/integration/iwork/impl/IWorkSyncProcessorImplTest.java new file mode 100644 index 00000000..a16f5f84 --- /dev/null +++ b/zt-module-system/zt-module-system-server/src/test/java/com/zt/plat/module/system/service/integration/iwork/impl/IWorkSyncProcessorImplTest.java @@ -0,0 +1,141 @@ +package com.zt.plat.module.system.service.integration.iwork.impl; + +import com.zt.plat.framework.test.core.ut.BaseMockitoUnitTest; +import com.zt.plat.module.system.controller.admin.dept.vo.dept.DeptSaveReqVO; +import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkHrDepartmentPageRespVO; +import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkHrSubcompanyPageRespVO; +import com.zt.plat.module.system.dal.mysql.dept.PostMapper; +import com.zt.plat.module.system.dal.mysql.user.AdminUserMapper; +import com.zt.plat.module.system.service.dept.DeptService; +import com.zt.plat.module.system.service.dept.PostService; +import com.zt.plat.module.system.service.integration.iwork.IWorkSyncProcessor; +import com.zt.plat.module.system.service.user.AdminUserService; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +/** + * Tests for cross-page pending handling and placeholder backfill in IWorkSyncProcessorImpl. + */ +class IWorkSyncProcessorImplTest extends BaseMockitoUnitTest { + + @InjectMocks + private IWorkSyncProcessorImpl processor; + + @Mock + private DeptService deptService; + @Mock + private PostService postService; + @Mock + private PostMapper postMapper; + @Mock + private AdminUserService adminUserService; + @Mock + private AdminUserMapper adminUserMapper; + + @Test + void shouldProcessPendingChildWhenParentArrivesInLaterPage() { + IWorkSyncProcessor.DeptSyncContext context = new IWorkSyncProcessor.DeptSyncContext(); + IWorkSyncProcessor.SyncOptions options = IWorkSyncProcessor.SyncOptions.custom(true, true, true); + + IWorkHrDepartmentPageRespVO.Department child = new IWorkHrDepartmentPageRespVO.Department(); + child.setId(200); + child.setDepartmentname("child"); + child.setSupdepid(100); // parent comes later + + IWorkHrDepartmentPageRespVO.Department parent = new IWorkHrDepartmentPageRespVO.Department(); + parent.setId(100); + parent.setDepartmentname("parent"); + parent.setSupdepid(0); // root + + when(deptService.getDept(anyLong())).thenReturn(null); + when(deptService.createDept(any(DeptSaveReqVO.class))).thenReturn(100L, 200L); + + processor.syncDepartments(List.of(child), options, context); + + verify(deptService, never()).createDept(any()); + assertEquals(1, context.getPendingDepartments().size()); + + processor.syncDepartments(List.of(parent), options, context); + + verify(deptService, times(2)).createDept(any()); + assertTrue(context.getPendingDepartments().isEmpty(), "pending should be cleared after parent processed"); + } + + @Test + void shouldInsertPlaceholderWhenParentMissingAfterFlush() { + IWorkSyncProcessor.DeptSyncContext context = new IWorkSyncProcessor.DeptSyncContext(); + IWorkSyncProcessor.SyncOptions options = IWorkSyncProcessor.SyncOptions.custom(true, true, true); + + IWorkHrDepartmentPageRespVO.Department child = new IWorkHrDepartmentPageRespVO.Department(); + child.setId(300); + child.setDepartmentname("orphan"); + child.setSupdepid(9999); // never provided + + when(deptService.getDept(anyLong())).thenReturn(null); + when(deptService.createDept(any(DeptSaveReqVO.class))).thenReturn(300L); + + processor.syncDepartments(List.of(child), options, context); + assertEquals(1, context.getPendingDepartments().size()); + + IWorkSyncProcessor.BatchResult flushResult = processor.flushDeptPending(context, options); + assertNotNull(flushResult); + + ArgumentCaptor captor = ArgumentCaptor.forClass(DeptSaveReqVO.class); + verify(deptService, times(1)).createDept(captor.capture()); + DeptSaveReqVO placeholderReq = captor.getValue(); + assertTrue(Boolean.TRUE.equals(placeholderReq.getDelayCodeGeneration())); + assertNull(placeholderReq.getCode()); + + assertTrue(context.getPendingDepartments().isEmpty(), "pending should be cleared after placeholder insert"); + assertTrue(context.getPlaceholderDeptIds().contains(300L)); + } + + @Test + void shouldKeepExternalCodeNullWhenDepartmentCodeBlank() { + IWorkSyncProcessor.SyncOptions options = IWorkSyncProcessor.SyncOptions.custom(true, true, true); + + IWorkHrDepartmentPageRespVO.Department dept = new IWorkHrDepartmentPageRespVO.Department(); + dept.setId(500); + dept.setDepartmentname("blank-code-dept"); + dept.setDepartmentcode(" "); + dept.setSupdepid(0); + + when(deptService.getDept(anyLong())).thenReturn(null); + when(deptService.createDept(any(DeptSaveReqVO.class))).thenReturn(500L); + + processor.syncDepartments(List.of(dept), options, null); + + ArgumentCaptor captor = ArgumentCaptor.forClass(DeptSaveReqVO.class); + verify(deptService, times(1)).createDept(captor.capture()); + DeptSaveReqVO req = captor.getValue(); + assertNull(req.getExternalDeptCode(), "externalDeptCode should remain null when source code is blank"); + } + + @Test + void shouldKeepExternalCodeNullWhenSubcompanyCodeBlank() { + IWorkSyncProcessor.SyncOptions options = IWorkSyncProcessor.SyncOptions.custom(true, true, true); + + IWorkHrSubcompanyPageRespVO.Subcompany subcompany = new IWorkHrSubcompanyPageRespVO.Subcompany(); + subcompany.setId(600); + subcompany.setSubcompanyname("blank-code-sub"); + subcompany.setSubcompanycode(null); + subcompany.setSupsubcomid(0); + + when(deptService.getDept(anyLong())).thenReturn(null); + when(deptService.createDept(any(DeptSaveReqVO.class))).thenReturn(600L); + + processor.syncSubcompanies(List.of(subcompany), options, null); + + ArgumentCaptor captor = ArgumentCaptor.forClass(DeptSaveReqVO.class); + verify(deptService, times(1)).createDept(captor.capture()); + DeptSaveReqVO req = captor.getValue(); + assertNull(req.getExternalDeptCode(), "externalDeptCode should remain null when source code is null or blank"); + } +} diff --git a/zt-module-system/zt-module-system-server/src/test/java/com/zt/plat/module/system/service/integration/iwork/impl/IWorkSyncServiceImplTest.java b/zt-module-system/zt-module-system-server/src/test/java/com/zt/plat/module/system/service/integration/iwork/impl/IWorkSyncServiceImplTest.java new file mode 100644 index 00000000..b8b1eb32 --- /dev/null +++ b/zt-module-system/zt-module-system-server/src/test/java/com/zt/plat/module/system/service/integration/iwork/impl/IWorkSyncServiceImplTest.java @@ -0,0 +1,62 @@ +package com.zt.plat.module.system.service.integration.iwork.impl; + +import com.zt.plat.framework.test.core.ut.BaseMockitoUnitTest; +import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkFullSyncReqVO; +import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkHrDepartmentPageRespVO; +import com.zt.plat.module.system.enums.integration.IWorkSyncEntityTypeEnum; +import com.zt.plat.module.system.service.dept.DeptService; +import com.zt.plat.module.system.service.integration.iwork.IWorkOrgRestService; +import com.zt.plat.module.system.service.integration.iwork.IWorkSyncProcessor; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.stubbing.Answer; + +import java.util.List; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.*; + +class IWorkSyncServiceImplTest extends BaseMockitoUnitTest { + + @InjectMocks + private IWorkSyncServiceImpl syncService; + + @Mock + private IWorkOrgRestService orgRestService; + @Mock + private IWorkSyncProcessor syncProcessor; + @Mock + private DeptService deptService; + + @Test + void shouldBackfillCodesWhenPlaceholdersExistAfterFullSync() { + IWorkFullSyncReqVO reqVO = new IWorkFullSyncReqVO(); + reqVO.setPageSize(1); + reqVO.setMaxPages(1); + + IWorkHrDepartmentPageRespVO pageResp = new IWorkHrDepartmentPageRespVO(); + pageResp.setSuccess(true); + IWorkHrDepartmentPageRespVO.Department dept = new IWorkHrDepartmentPageRespVO.Department(); + dept.setId(1); + pageResp.setDataList(List.of(dept)); + when(orgRestService.listDepartments(any())).thenReturn(pageResp); + + // 在部门同步时标记占位 ID + doAnswer((Answer) invocation -> { + IWorkSyncProcessor.DeptSyncContext context = invocation.getArgument(2); + if (context != null) { + context.getPlaceholderDeptIds().add(500L); + } + return IWorkSyncProcessor.BatchResult.empty(); + }).when(syncProcessor).syncDepartments(any(), any(), any(IWorkSyncProcessor.DeptSyncContext.class)); + + when(syncProcessor.flushDeptPending(any(), any())).thenReturn(IWorkSyncProcessor.BatchResult.empty()); + + syncService.fullSyncDepartments(reqVO); + + verify(deptService, times(1)).backfillMissingCodesWithoutEvent(argThat(set -> set.contains(500L))); + } +}