From 34981c30a78e8bbd7791131059a9210f9928b62c Mon Sep 17 00:00:00 2001
From: 刘光辉 <347230014@qq.com>
Date: 星期四, 17 九月 2026 09:24:09 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/master' into master

---
 jnpf-dms/jnpf-dms-biz/src/test/java/jnpf/dmsService/impl/DmsWenjianBianhaoApplicationServiceImplTest.java |  345 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 345 insertions(+), 0 deletions(-)

diff --git a/jnpf-dms/jnpf-dms-biz/src/test/java/jnpf/dmsService/impl/DmsWenjianBianhaoApplicationServiceImplTest.java b/jnpf-dms/jnpf-dms-biz/src/test/java/jnpf/dmsService/impl/DmsWenjianBianhaoApplicationServiceImplTest.java
new file mode 100644
index 0000000..ba72811
--- /dev/null
+++ b/jnpf-dms/jnpf-dms-biz/src/test/java/jnpf/dmsService/impl/DmsWenjianBianhaoApplicationServiceImplTest.java
@@ -0,0 +1,345 @@
+package jnpf.dmsService.impl;
+
+import jnpf.base.Pagination;
+import jnpf.base.UserInfo;
+import jnpf.dmsEntity.DmsWenjianBianhaoEntity;
+import jnpf.dmsEntity.DmsWenjianBianhaoGuizeEntity;
+import jnpf.dmsEntity.DmsWenjianBianhaoGuizeZixiangEntity;
+import jnpf.dmsEntity.DmsWenjianBianhaoShenqingEntity;
+import jnpf.dmsEntity.DmsWenjianMingchengBiangengShenqingEntity;
+import jnpf.dmsEntity.wenjianbianhao.DmsFlowEventType;
+import jnpf.dmsMapper.DmsWenjianBianhaoGuizeMapper;
+import jnpf.dmsMapper.DmsWenjianBianhaoGuizeZixiangMapper;
+import jnpf.dmsMapper.DmsWenjianBianhaoLiushuiJishuMapper;
+import jnpf.dmsMapper.DmsWenjianBianhaoMapper;
+import jnpf.dmsMapper.DmsWenjianBianhaoShenqingMapper;
+import jnpf.dmsMapper.DmsWenjianMingchengBiangengShenqingMapper;
+import jnpf.dmsPermission.DmsCurrentIdentity;
+import jnpf.dmsService.DmsWenjianBianhaoPermissionService;
+import jnpf.exception.DataException;
+import jnpf.permission.UserApi;
+import jnpf.permission.entity.UserEntity;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.lenient;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+class DmsWenjianBianhaoApplicationServiceImplTest {
+
+    private static final String TENANT_ID = "tenant-1";
+    private static final String FLOW_ID = "flow-1";
+    private static final String TASK_ID = "task-1";
+
+    @Mock private DmsCurrentIdentity identity;
+    @Mock private DmsWenjianBianhaoPermissionService permissionService;
+    @Mock private DmsWenjianBianhaoGuizeMapper ruleMapper;
+    @Mock private DmsWenjianBianhaoGuizeZixiangMapper partMapper;
+    @Mock private DmsWenjianBianhaoShenqingMapper applicationMapper;
+    @Mock private DmsWenjianBianhaoMapper numberMapper;
+    @Mock private DmsWenjianBianhaoLiushuiJishuMapper counterMapper;
+    @Mock private DmsWenjianMingchengBiangengShenqingMapper nameChangeMapper;
+    @Mock private UserApi userApi;
+
+    @InjectMocks
+    private DmsWenjianBianhaoApplicationServiceImpl service;
+
+    @BeforeEach
+    void setUpIdentity() {
+        UserInfo user = new UserInfo();
+        user.setUserId("operator-1");
+        user.setTenantId(TENANT_ID);
+        when(identity.requireUser()).thenReturn(user);
+        lenient().when(identity.tenantId(user)).thenReturn(TENANT_ID);
+    }
+
+    @Test
+    void enabledRulesArePermissionFilteredBeforePaging() throws DataException {
+        Pagination pagination = new Pagination();
+        pagination.setCurrentPage(1);
+        pagination.setPageSize(20);
+        DmsWenjianBianhaoGuizeEntity allowed = rule("rule-1", "QC-SOP瑙勫垯");
+        allowed.setGuizeLeixing("SOP");
+        allowed.setWenjianMulu("/璐ㄩ噺鎺у埗閮≦C/SOP");
+        DmsWenjianBianhaoGuizeEntity denied = rule("rule-2", "QA-SOP瑙勫垯");
+        when(ruleMapper.selectEnabled(TENANT_ID)).thenReturn(Arrays.asList(allowed, denied));
+        when(permissionService.canApplyRule("operator-1", "rule-1")).thenReturn(true);
+        when(permissionService.canApplyRule("operator-1", "rule-2")).thenReturn(false);
+        when(ruleMapper.selectActiveById(TENANT_ID, "rule-1")).thenReturn(allowed);
+        when(partMapper.selectActiveByRule(TENANT_ID, "rule-1")).thenReturn(Arrays.asList(
+                part("1", "gudingzhi", "QC", "-"),
+                part("2", "liushuihao", "000", "")));
+
+        jnpf.dmsEntity.wenjianbianhao.vo.DmsWenjianBianhaoRuleVO result =
+                service.enabledRules(pagination, null, "SOP").get(0);
+
+        assertEquals("rule-1", result.getId());
+        assertEquals("QC-寰呭", result.getYulanBianhao());
+        assertEquals("QC-SOP瑙勫垯 / QC-寰呭", result.getGuizeXianshi());
+        assertEquals(1, pagination.getTotal());
+        verify(permissionService).canApplyRule("operator-1", "rule-1");
+        verify(permissionService).canApplyRule("operator-1", "rule-2");
+    }
+
+    @Test
+    void rejectsEventUntilFormAndWorkflowStatesMatch() {
+        DmsWenjianBianhaoShenqingEntity application = application(1);
+        when(applicationMapper.selectByFlowForUpdate(TENANT_ID, FLOW_ID, TASK_ID))
+                .thenReturn(Collections.singletonList(application));
+
+        assertThrows(DataException.class, () -> service.syncApplicationFlow(
+                FLOW_ID, TASK_ID, 2, DmsFlowEventType.END));
+
+        verify(numberMapper, never()).selectByApplicationForUpdate(any(), any());
+    }
+
+    @Test
+    void startEventFreezesOrderedRuleSnapshotAndUsesApplicantPermission() {
+        DmsWenjianBianhaoShenqingEntity application = application(1);
+        application.setShenqingRenId("applicant-1");
+        DmsWenjianBianhaoEntity number = number("number-1", "rule-1", "璁惧娓呮磥瑙勭▼");
+        when(applicationMapper.selectByFlowForUpdate(TENANT_ID, FLOW_ID, TASK_ID))
+                .thenReturn(Collections.singletonList(application));
+        when(numberMapper.selectByApplicationForUpdate(TENANT_ID, application.getId()))
+                .thenReturn(Collections.singletonList(number));
+        when(permissionService.canApplyRule("applicant-1", "rule-1")).thenReturn(true);
+        when(ruleMapper.selectActiveById(TENANT_ID, "rule-1"))
+                .thenReturn(rule("rule-1", "QC-SOP瑙勫垯"));
+        when(partMapper.selectActiveByRule(TENANT_ID, "rule-1")).thenReturn(Arrays.asList(
+                part("1", "gudingzhi", "QC", "-"),
+                part("2", "liushuihao", "000", "")));
+        when(numberMapper.updateById(number)).thenReturn(1);
+
+        service.syncApplicationFlow(FLOW_ID, TASK_ID, 1, DmsFlowEventType.START);
+        service.syncApplicationFlow(FLOW_ID, TASK_ID, 1, DmsFlowEventType.START);
+
+        assertEquals("pending_review", number.getBizStatus());
+        assertEquals("rule-1", number.getGuizeKuaizhaoId());
+        assertEquals("QC-{SERIAL:3}", number.getBianhaoMuban());
+        assertEquals("QC-寰呭", number.getYulanBianhao());
+        assertEquals("QC-寰呭", number.getShenqingBianhao());
+        assertEquals("璁惧娓呮磥瑙勭▼", number.getDangqianWenjianMingcheng());
+        assertEquals(1, number.getMingchengBanben());
+        verify(permissionService).canApplyRule("applicant-1", "rule-1");
+        verify(ruleMapper, times(1)).selectActiveById(TENANT_ID, "rule-1");
+        verify(partMapper, times(1)).selectActiveByRule(TENANT_ID, "rule-1");
+        verify(numberMapper, times(1)).updateById(number);
+    }
+
+    @Test
+    void repeatedPassedEventDoesNotConsumeAnotherSerial() {
+        DmsWenjianBianhaoShenqingEntity application = application(2);
+        DmsWenjianBianhaoEntity number = number("number-1", "rule-1", "璁惧娓呮磥瑙勭▼");
+        number.setGuizeKuaizhao("[{}]");
+        number.setGuizeKuaizhaoId("rule-1");
+        number.setBianhaoMuban("QC-{SERIAL:3}");
+        number.setBizStatus("pending_review");
+        when(applicationMapper.selectByFlowForUpdate(TENANT_ID, FLOW_ID, TASK_ID))
+                .thenReturn(Collections.singletonList(application));
+        when(numberMapper.selectByApplicationForUpdate(TENANT_ID, application.getId()))
+                .thenReturn(Collections.singletonList(number));
+        when(counterMapper.next(TENANT_ID, "rule-1", "QC-{SERIAL:3}")).thenReturn(7L);
+        when(numberMapper.updateById(number)).thenReturn(1);
+
+        service.syncApplicationFlow(FLOW_ID, TASK_ID, 2, DmsFlowEventType.END);
+        service.syncApplicationFlow(FLOW_ID, TASK_ID, 2, DmsFlowEventType.END);
+
+        assertEquals("QC-007", number.getWenjianBianhao());
+        assertEquals("QC-007", number.getShenqingBianhao());
+        assertEquals("007", number.getLiushuiHao());
+        assertEquals("using", number.getBizStatus());
+        org.junit.jupiter.api.Assertions.assertNotNull(number.getBianhaoFafangShijian());
+        verify(counterMapper, times(1)).next(TENANT_ID, "rule-1", "QC-{SERIAL:3}");
+        verify(numberMapper, times(1)).updateById(number);
+    }
+
+    @Test
+    void usingNumbersReturnsPopupFieldsFromTenantScopedQuery() throws DataException {
+        Pagination pagination = new Pagination();
+        pagination.setCurrentPage(1);
+        pagination.setPageSize(20);
+        DmsWenjianBianhaoEntity number = number("number-1", "rule-1", "鍘熸枃浠跺悕绉�");
+        number.setWenjianBianhao("QC-001");
+        number.setDangqianWenjianMingcheng("褰撳墠鏂囦欢鍚嶇О");
+        number.setMingchengBanben(2);
+        number.setGuizeMingchengKuaizhao("QC-SOP瑙勫垯");
+        number.setShiyongRenId("user-1");
+        number.setBianhaoFafangShijian(new Date());
+        when(numberMapper.selectUsing(TENANT_ID, "number-1", pagination))
+                .thenReturn(Collections.singletonList(number));
+        when(permissionService.canViewNumber("operator-1", "number-1")).thenReturn(true);
+        UserEntity user = new UserEntity();
+        user.setId("user-1");
+        user.setRealName("寮犱笁");
+        when(userApi.getUserName(Collections.singletonList("user-1")))
+                .thenReturn(Collections.singletonList(user));
+
+        jnpf.dmsEntity.wenjianbianhao.vo.DmsWenjianBianhaoOptionVO result =
+                service.usingNumbers(pagination, " number-1 ", "褰撳墠").get(0);
+
+        assertEquals("number-1", result.getId());
+        assertEquals("QC-001", result.getWenjianBianhao());
+        assertEquals("褰撳墠鏂囦欢鍚嶇О", result.getWenjianMingcheng());
+        assertEquals(2, result.getMingchengBanben());
+        assertEquals("寮犱笁", result.getShiyongRen());
+        assertEquals("QC-001 / 褰撳墠鏂囦欢鍚嶇О", result.getBianhaoXianshi());
+        assertEquals("褰撳墠", pagination.getKeyword());
+        verify(permissionService).canViewNumber("operator-1", "number-1");
+        verify(numberMapper).selectUsing(TENANT_ID, "number-1", pagination);
+    }
+
+    @Test
+    void usingNumbersExcludeRowsDeniedByPermissionService() throws DataException {
+        Pagination pagination = new Pagination();
+        pagination.setCurrentPage(1);
+        pagination.setPageSize(20);
+        DmsWenjianBianhaoEntity denied = number("number-1", "rule-1", "鍙楅檺鏂囦欢");
+        denied.setWenjianBianhao("QA-001");
+        when(numberMapper.selectUsing(TENANT_ID, "", pagination))
+                .thenReturn(Collections.singletonList(denied));
+        when(permissionService.canViewNumber("operator-1", "number-1")).thenReturn(false);
+
+        assertEquals(0, service.usingNumbers(pagination, null, null).size());
+
+        verify(permissionService).canViewNumber("operator-1", "number-1");
+        verify(userApi, never()).getUserName(any());
+    }
+
+    @Test
+    void recallOnlyRestoresUnissuedNumberToDraft() {
+        DmsWenjianBianhaoShenqingEntity application = application(9);
+        DmsWenjianBianhaoEntity pending = number("number-1", "rule-1", "鏂囦欢涓�");
+        pending.setBizStatus("pending_review");
+        DmsWenjianBianhaoEntity issued = number("number-2", "rule-1", "鏂囦欢浜�");
+        issued.setBizStatus("using");
+        issued.setWenjianBianhao("QC-001");
+        when(applicationMapper.selectByFlowForUpdate(TENANT_ID, FLOW_ID, TASK_ID))
+                .thenReturn(Collections.singletonList(application));
+        when(numberMapper.selectByApplicationForUpdate(TENANT_ID, application.getId()))
+                .thenReturn(Arrays.asList(pending, issued));
+        when(numberMapper.updateById(pending)).thenReturn(1);
+
+        service.syncApplicationFlow(FLOW_ID, TASK_ID, 9, DmsFlowEventType.RECALL);
+
+        assertEquals("draft", pending.getBizStatus());
+        assertEquals("using", issued.getBizStatus());
+        verify(numberMapper).updateById(pending);
+        verify(numberMapper, never()).updateById(issued);
+    }
+
+    @Test
+    void nameChangePassIsVersionCheckedAndIdempotent() {
+        DmsWenjianMingchengBiangengShenqingEntity change = new DmsWenjianMingchengBiangengShenqingEntity();
+        change.setId("change-1");
+        change.setTenantId(TENANT_ID);
+        change.setFlowId(FLOW_ID);
+        change.setFlowTaskId(TASK_ID);
+        change.setFlowState(1);
+        change.setBianhaoId("number-1");
+        change.setBiangengHouWenjianMingcheng("鏂版枃浠跺悕绉�");
+        DmsWenjianBianhaoEntity number = number("number-1", "rule-1", "鍘熸枃浠跺悕绉�");
+        number.setBizStatus("using");
+        number.setWenjianBianhao("QC-001");
+        number.setDangqianWenjianMingcheng("鍘熸枃浠跺悕绉�");
+        number.setMingchengBanben(3);
+        when(nameChangeMapper.selectByFlowForUpdate(TENANT_ID, FLOW_ID, TASK_ID))
+                .thenReturn(Collections.singletonList(change));
+        when(numberMapper.selectActiveByIdForUpdate(TENANT_ID, "number-1")).thenReturn(number);
+        when(nameChangeMapper.updateById(change)).thenReturn(1);
+        when(numberMapper.updateById(number)).thenReturn(1);
+
+        service.syncNameChangeFlow(FLOW_ID, TASK_ID, 1, DmsFlowEventType.START);
+        change.setFlowState(2);
+        service.syncNameChangeFlow(FLOW_ID, TASK_ID, 2, DmsFlowEventType.END);
+        service.syncNameChangeFlow(FLOW_ID, TASK_ID, 2, DmsFlowEventType.END);
+
+        assertEquals("鍘熸枃浠跺悕绉�", change.getYuanWenjianMingcheng());
+        assertEquals(3, change.getYuanMingchengBanben());
+        assertEquals("鏂版枃浠跺悕绉�", number.getDangqianWenjianMingcheng());
+        assertEquals(4, number.getMingchengBanben());
+        verify(numberMapper, times(1)).updateById(number);
+    }
+
+    @Test
+    void nameChangeStartRejectsAnotherActiveApplicationForTheSameNumber() {
+        DmsWenjianMingchengBiangengShenqingEntity change =
+                new DmsWenjianMingchengBiangengShenqingEntity();
+        change.setId("change-2");
+        change.setTenantId(TENANT_ID);
+        change.setFlowId(FLOW_ID);
+        change.setFlowTaskId(TASK_ID);
+        change.setFlowState(1);
+        change.setBianhaoId("number-1");
+        change.setBiangengHouWenjianMingcheng("鍙︿竴涓柊鍚嶇О");
+        DmsWenjianBianhaoEntity number = number("number-1", "rule-1", "鍘熸枃浠跺悕绉�");
+        number.setBizStatus("using");
+        number.setWenjianBianhao("QC-001");
+        when(nameChangeMapper.selectByFlowForUpdate(TENANT_ID, FLOW_ID, TASK_ID))
+                .thenReturn(Collections.singletonList(change));
+        when(numberMapper.selectActiveByIdForUpdate(TENANT_ID, "number-1")).thenReturn(number);
+        when(nameChangeMapper.countActiveByNumber(TENANT_ID, "number-1", "change-2"))
+                .thenReturn(1);
+
+        DataException exception = assertThrows(DataException.class,
+                () -> service.syncNameChangeFlow(FLOW_ID, TASK_ID, 1, DmsFlowEventType.START));
+
+        assertEquals("璇ユ枃浠剁紪鍙峰凡鏈夎繘琛屼腑鐨勫悕绉板彉鏇寸敵璇凤紝璇峰嬁閲嶅鎻愪氦", exception.getMessage());
+        verify(nameChangeMapper, never()).updateById(change);
+    }
+
+    private DmsWenjianBianhaoShenqingEntity application(int flowState) {
+        DmsWenjianBianhaoShenqingEntity application = new DmsWenjianBianhaoShenqingEntity();
+        application.setId("application-1");
+        application.setTenantId(TENANT_ID);
+        application.setFlowId(FLOW_ID);
+        application.setFlowTaskId(TASK_ID);
+        application.setFlowState(flowState);
+        return application;
+    }
+
+    private DmsWenjianBianhaoEntity number(String id, String ruleId, String name) {
+        DmsWenjianBianhaoEntity number = new DmsWenjianBianhaoEntity();
+        number.setId(id);
+        number.setForeignId("application-1");
+        number.setTenantId(TENANT_ID);
+        number.setGuizeId(ruleId);
+        number.setYuanWenjianMingcheng(name);
+        return number;
+    }
+
+    private DmsWenjianBianhaoGuizeEntity rule(String id, String name) {
+        DmsWenjianBianhaoGuizeEntity rule = new DmsWenjianBianhaoGuizeEntity();
+        rule.setId(id);
+        rule.setTenantId(TENANT_ID);
+        rule.setGuizeMingcheng(name);
+        rule.setBizEnabled("enabled");
+        return rule;
+    }
+
+    private DmsWenjianBianhaoGuizeZixiangEntity part(
+            String id, String type, String value, String separator) {
+        DmsWenjianBianhaoGuizeZixiangEntity part = new DmsWenjianBianhaoGuizeZixiangEntity();
+        part.setId(id);
+        part.setTenantId(TENANT_ID);
+        part.setForeignId("rule-1");
+        part.setBianhaoZuheLeixing(type);
+        part.setZhi(value);
+        part.setFengefu(separator);
+        return part;
+    }
+}

--
Gitblit v1.8.0