package jnpf.dmsService; import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import jnpf.base.UserInfo; import jnpf.dmsEntity.print.DmsPrintOperationEntity; import jnpf.dmsEntity.print.DmsPrintOperationLogEntity; import jnpf.dmsEntity.print.form.DmsPrintOperationCreateForm; import jnpf.dmsEntity.print.form.DmsPrintOperationQuery; import jnpf.dmsEntity.print.form.DmsPrintTargetForm; import jnpf.dmsMapper.DmsPrintOperationLogMapper; import jnpf.dmsMapper.DmsPrintOperationMapper; import jnpf.dmsService.support.DmsPrintOperationRequestContext; import jnpf.exception.DataException; import jnpf.util.RandomUtil; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import java.util.Arrays; import java.util.Collections; import static org.junit.jupiter.api.Assertions.assertEquals; 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.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; class DmsPrintOperationLogServiceTest { private final DmsPrintOperationMapper operationMapper = mock(DmsPrintOperationMapper.class); private final DmsPrintOperationLogMapper logMapper = mock(DmsPrintOperationLogMapper.class); private final DmsPrintOperationRequestContext requestContext = mock(DmsPrintOperationRequestContext.class); private final DmsPrintOperationLogService service = new DmsPrintOperationLogService( operationMapper, logMapper, requestContext); @BeforeEach void setUp() { UserInfo user = new UserInfo(); user.setUserId("user-1"); user.setUserName("张三"); when(requestContext.currentUser()).thenReturn(user); when(requestContext.requestIp()).thenReturn("127.0.0.1"); when(requestContext.userAgent()).thenReturn("test-agent"); when(operationMapper.insertIgnore(any())).thenReturn(1); } @Test void batchPrintCreatesOneRowPerDistinctTargetWithSharedOperationId() throws Exception { DmsPrintOperationCreateForm form = form(); form.setTargets(Arrays.asList(target("record-1"), target("record-2"), target("record-1"))); assertEquals(2, service.create(form).getRecordCount()); ArgumentCaptor rows = ArgumentCaptor.forClass(DmsPrintOperationLogEntity.class); verify(logMapper, org.mockito.Mockito.times(2)).insert(rows.capture()); assertEquals(rows.getAllValues().get(0).getOperationId(), rows.getAllValues().get(1).getOperationId()); assertTrue(rows.getAllValues().get(0).getId().matches("\\d{1,19}")); assertTrue(rows.getAllValues().get(0).getOperationId().matches("\\d{1,19}")); assertEquals(2, rows.getAllValues().get(0).getCopies()); } @Test void identicalClientEventReturnsExistingOperationWithoutDuplicateRows() throws Exception { DmsPrintOperationCreateForm form = form(); service.create(form); ArgumentCaptor attempted = ArgumentCaptor.forClass(DmsPrintOperationEntity.class); verify(operationMapper).insertIgnore(attempted.capture()); DmsPrintOperationEntity existing = new DmsPrintOperationEntity(); existing.setId(RandomUtil.uuId()); existing.setTargetCount(1); existing.setRequestHash(attempted.getValue().getRequestHash()); clearInvocations(logMapper); when(operationMapper.insertIgnore(any())).thenReturn(0); when(operationMapper.selectByClientEventId(form.getClientEventId())).thenReturn(existing); service.create(form); verify(logMapper, never()).insert(any(DmsPrintOperationLogEntity.class)); } @Test void menuIdResolvesMenuNameOnceAndWritesSnapshotToAllRows() throws Exception { when(requestContext.header("Jnpf-Menu-Id")).thenReturn("menu-1"); when(requestContext.header("Jnpf-Parent-Id")).thenReturn("parent-1"); when(logMapper.selectMenuNameById("menu-1")).thenReturn("打印管理"); when(logMapper.selectParentBizModuleName("menu-1", "parent-1")).thenReturn("质量管理"); DmsPrintOperationCreateForm form = form(); form.setTargets(Arrays.asList(target("record-1"), target("record-2"))); service.create(form); ArgumentCaptor rows = ArgumentCaptor.forClass(DmsPrintOperationLogEntity.class); verify(logMapper).selectMenuNameById("menu-1"); verify(logMapper).selectParentBizModuleName("menu-1", "parent-1"); verify(logMapper, org.mockito.Mockito.times(2)).insert(rows.capture()); for (DmsPrintOperationLogEntity row : rows.getAllValues()) { assertEquals("menu-1", row.getSourceMenuId()); assertEquals("打印管理", row.getSourceMenuName()); assertEquals("质量管理", row.getParentBizModule()); } } @Test void unverifiedParentIsNotStored() throws Exception { when(requestContext.header("Jnpf-Menu-Id")).thenReturn("menu-1"); when(requestContext.header("Jnpf-Parent-Id")).thenReturn("unrelated-parent"); service.create(form()); ArgumentCaptor row = ArgumentCaptor.forClass(DmsPrintOperationLogEntity.class); verify(logMapper).selectParentBizModuleName("menu-1", "unrelated-parent"); verify(logMapper).insert(row.capture()); assertEquals(null, row.getValue().getParentBizModule()); } @Test void missingMenuIdDoesNotQueryMenuTable() throws Exception { when(requestContext.header("Jnpf-Parent-Id")).thenReturn("parent-1"); service.create(form()); verify(logMapper, never()).selectMenuNameById(any()); verify(logMapper, never()).selectParentBizModuleName(any(), any()); } @Test void sameClientEventWithDifferentPayloadIsRejected() { DmsPrintOperationCreateForm form = form(); when(operationMapper.insertIgnore(any())).thenReturn(0); DmsPrintOperationEntity existing = new DmsPrintOperationEntity(); existing.setRequestHash("different"); when(operationMapper.selectByClientEventId(form.getClientEventId())).thenReturn(existing); DataException error = assertThrows(DataException.class, () -> service.create(form)); assertTrue(error.getMessage().contains("CLIENT_EVENT_CONFLICT")); verify(logMapper, never()).insert(any(DmsPrintOperationLogEntity.class)); } @Test @SuppressWarnings({"rawtypes", "unchecked"}) void listDoesNotRestrictRowsToCurrentOperator() throws Exception { when(logMapper.selectPage(any(Page.class), any(Wrapper.class))).thenAnswer(invocation -> { Page page = invocation.getArgument(0); page.setRecords(Collections.emptyList()); page.setTotal(0); return page; }); service.list(new DmsPrintOperationQuery()); verify(requestContext, never()).currentUser(); } private DmsPrintOperationCreateForm form() { DmsPrintOperationCreateForm form = new DmsPrintOperationCreateForm(); form.setClientEventId("123e4567-e89b-12d3-a456-426614174000"); form.setPrintName("检验报告"); form.setPrintType("RECORD_DOCUMENT"); form.setSourceType("FORM_DETAIL"); form.setCopies(2); form.setTargets(Collections.singletonList(target("record-1"))); return form; } private DmsPrintTargetForm target(String id) { DmsPrintTargetForm target = new DmsPrintTargetForm(); target.setTargetType("BUSINESS_RECORD"); target.setTargetId(id); target.setDocumentCode("BG-" + id); target.setDocumentName("报告 " + id); return target; } }