package jnpf.dmsService;
|
|
import jnpf.base.UserInfo;
|
import jnpf.dmsEntity.fenfa.*;
|
import jnpf.dmsEntity.fenfa.DmsWenjianDayinFasongEntity.Batch;
|
import jnpf.dmsEntity.fenfa.form.DmsWenjianChongdaForm;
|
import jnpf.dmsMapper.*;
|
import jnpf.dmsPermission.DmsCurrentIdentity;
|
import jnpf.exception.DataException;
|
import jnpf.util.JsonUtil;
|
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.Test;
|
import java.util.*;
|
import static org.junit.jupiter.api.Assertions.*;
|
import static org.mockito.Mockito.*;
|
|
class DmsWenjianChongdaServiceTest {
|
final DmsCurrentIdentity identity = mock(DmsCurrentIdentity.class);
|
final DmsWenjianFenfaMapper copies = mock(DmsWenjianFenfaMapper.class);
|
final DmsWenjianDayinQuerenMapper versions = mock(DmsWenjianDayinQuerenMapper.class);
|
final DmsWenjianChongdaMapper mapper = mock(DmsWenjianChongdaMapper.class);
|
final DmsWenjianBupinghengMapper documents = mock(DmsWenjianBupinghengMapper.class);
|
final DmsFileVersionMapper fileVersions = mock(DmsFileVersionMapper.class);
|
final DmsPermissionDecisionService permissions = mock(DmsPermissionDecisionService.class);
|
final jnpf.dmsIntegration.audit.DmsBusinessChangeAudit businessAudit = mock(jnpf.dmsIntegration.audit.DmsBusinessChangeAudit.class);
|
final DmsWenjianChongdaService service = new DmsWenjianChongdaService(businessAudit,identity,copies,versions,mapper,documents,fileVersions,permissions);
|
final DmsWenjianChongdaForm form = new DmsWenjianChongdaForm();
|
final DmsWenjianDayinJiluEntity copy = new DmsWenjianDayinJiluEntity();
|
DmsWenjianChongdaFasong saved;
|
@BeforeEach void setup() {
|
UserInfo user = new UserInfo(); user.setUserId("actor");
|
when(identity.requireUser()).thenReturn(user); when(identity.tenantId(user)).thenReturn("tenant");
|
copy.setId("copy"); copy.setDayinRenwuId("task"); copy.setBizStatus("daifafang");
|
copy.setDayinLiushuihao("original"); copy.setDayinJishuLiushuihao("20260003");
|
when(copies.selectPrintForUpdate("tenant","copy",null)).thenReturn(copy);
|
var version = new DmsWenjianDayinQuerenEntity(); version.setFileVersionId("019a1234-1234-7123-8123-123456789012");
|
when(versions.selectTaskVersion("tenant","task")).thenReturn(version);
|
var source = new jnpf.dmsEntity.permission.entity.DmsFileVersionEntity(); source.setShifouShoukong("no");
|
when(fileVersions.selectReadableByTenantAndId(eq("tenant"),any())).thenReturn(source);
|
when(mapper.validSignature(eq("tenant"),eq("actor"),eq("signed"),eq("copy"),anyString(),eq("纸张损坏"))).thenReturn(new DmsWenjianChongdaFasong());
|
when(mapper.selectActive("tenant","copy")).thenAnswer(c -> saved != null && List.of("OPEN","CONFIRMED").contains(saved.getState()) ? saved : null);
|
when(mapper.selectAttempt(eq("tenant"),anyString())).thenAnswer(c -> saved != null && saved.getId().equals(c.getArgument(1)) ? saved : null);
|
when(mapper.insertAttempt(any())).thenAnswer(c -> { saved=c.getArgument(0); return 1; });
|
when(mapper.updateAttempt(any())).thenReturn(1);
|
form.setRequestId(UUID.randomUUID().toString()); form.setPrintRecordId("copy"); form.setReason("纸张损坏"); form.setBizSign("signed");
|
}
|
Batch batch(int... pages) {
|
Batch b = new Batch(); b.setId(UUID.randomUUID().toString()); b.setItems(List.of(0));
|
b.setPages(Arrays.stream(pages).boxed().toList()); return b;
|
}
|
void registerNativeDocument() {
|
var linked = new DmsWenjianChongdaFasong(); linked.setDocumentId("doc");
|
when(mapper.findDocument("tenant", saved.getId())).thenReturn(linked);
|
var doc = new DmsWenjianBupinghengExecution(); doc.setBizSign("signed"); doc.setDengjiRenId("actor");
|
when(documents.lockDocument("tenant", "doc")).thenReturn(doc);
|
var item = new DmsWenjianBupinghengExecution.Item(); item.setDayinJiluId("copy"); item.setCuoshi("cuoshi3"); item.setYuanyin("纸张损坏");
|
when(documents.lockItems("tenant", "doc")).thenReturn(List.of(item));
|
}
|
void begin() throws Exception {
|
service.prepare(form); form.setBatches(List.of(batch(1,2))); service.plan(form, saved.getFileVersionId(), 2);
|
registerNativeDocument();
|
form.setBatchId(form.getBatches().get(0).getId()); form.setToken(UUID.randomUUID().toString()); service.begin(form);
|
}
|
@Test void dispatchBeforeNativeRegistrationAndCancelWithoutBusinessDocument() throws Exception {
|
service.prepare(form); form.setBatches(List.of(batch(1))); service.plan(form, saved.getFileVersionId(), 1);
|
form.setBatchId(form.getBatches().getFirst().getId()); form.setToken(UUID.randomUUID().toString());
|
service.begin(form);
|
assertTrue(saved.getBatchesJson().contains("SENDING"));
|
verifyNoInteractions(documents);
|
assertThrows(DataException.class, () -> service.finish(form));
|
service.submitted(form);
|
assertThrows(DataException.class, () -> service.finish(form));
|
assertEquals("CONFIRMED", saved.getState());
|
registerNativeDocument(); when(mapper.completeDocument(any())).thenReturn(1);
|
service.finish(form);
|
assertEquals("CLOSED", saved.getState());
|
}
|
@Test void dispatchChecksExistingLegacyRegistrationIfPresent() throws Exception {
|
service.prepare(form); form.setBatches(List.of(batch(1))); service.plan(form, saved.getFileVersionId(), 1);
|
form.setBatchId(form.getBatches().getFirst().getId()); form.setToken(UUID.randomUUID().toString());
|
registerNativeDocument();
|
documents.lockItems("tenant", "doc").getFirst().setYuanyin("changed");
|
assertThrows(DataException.class, () -> service.begin(form));
|
documents.lockItems("tenant", "doc").getFirst().setYuanyin("纸张损坏");
|
documents.lockDocument("tenant", "doc").setZhixingShijian(new Date());
|
assertThrows(DataException.class, () -> service.begin(form));
|
documents.lockDocument("tenant", "doc").setZhixingShijian(null);
|
service.begin(form);
|
assertEquals("doc", saved.getDocumentId());
|
assertTrue(saved.getBatchesJson().contains("SENDING"));
|
verify(mapper, never()).completeDocument(any());
|
}
|
@Test void freezesOriginalNumbersAndOneCopyWithoutConsumingTask() throws Exception {
|
form.setCopies(99); service.prepare(form);
|
assertEquals(1,saved.getCopies()); assertEquals("actor",saved.getUserId());
|
var number=JsonUtil.getJsonToList(saved.getNumbersJson(),DmsWenjianDayinHaoma.class).getFirst();
|
assertEquals("original",number.getSerialNumber()); assertEquals("20260003",number.getCountSerialNumber());
|
verify(copies,never()).countConsumedPrints(anyString(),anyString()); verifyNoInteractions(documents);
|
}
|
@Test void restoresFrozenReasonAndSignatureInsteadOfNewInput() throws Exception {
|
service.prepare(form); String id=saved.getId();
|
form.setReason("changed"); form.setBizSign("new sign");
|
assertEquals(id,service.prepare(form).getId()); assertEquals("纸张损坏",saved.getReason()); assertEquals("signed",saved.getBizSign());
|
verify(mapper,times(1)).insertAttempt(any());
|
}
|
@Test void anotherRequestCannotReplaceActiveAttempt() throws Exception {
|
service.prepare(form); form.setRequestId(UUID.randomUUID().toString());
|
assertThrows(DataException.class,()->service.prepare(form)); verify(mapper,times(1)).insertAttempt(any());
|
}
|
@Test void forgedSignatureCannotStart() {
|
when(mapper.validSignature(anyString(),anyString(),anyString(),anyString(),anyString(),anyString())).thenReturn(null);
|
assertThrows(DataException.class,()->service.prepare(form)); verify(mapper,never()).insertAttempt(any());
|
}
|
@Test void missingVersionCannotStart() {
|
when(fileVersions.selectReadableByTenantAndId(anyString(),any())).thenReturn(null);
|
assertThrows(DataException.class,()->service.prepare(form)); verify(mapper,never()).insertAttempt(any());
|
}
|
@Test void historicalRecordKeepsMissingCountSerial() throws Exception {
|
copy.setDayinJishuLiushuihao(null); service.prepare(form);
|
assertNull(JsonUtil.getJsonToList(saved.getNumbersJson(),DmsWenjianDayinHaoma.class).getFirst().getCountSerialNumber());
|
}
|
@Test void completedRequestResponseLossDoesNotPrintAgain() throws Exception {
|
service.prepare(form); saved.setState("CLOSED");
|
assertEquals("CLOSED",service.prepare(form).getState()); verify(mapper,times(1)).insertAttempt(any());
|
}
|
@Test void rejectsSignedReceivedAndVoidedRecords() {
|
for (String status: List.of("yiqianshou","yizuofei","yihuishou")) {
|
copy.setBizStatus(status); assertThrows(DataException.class,()->service.prepare(form));
|
}
|
verify(mapper,never()).insertAttempt(any());
|
}
|
@Test void anotherActorCannotResume() throws Exception {
|
service.prepare(form); saved.setUserId("other");
|
assertThrows(DataException.class,()->service.prepare(form)); assertThrows(DataException.class,()->service.active("copy"));
|
var summary=service.lockStatus("copy"); assertTrue(summary.isLocked()); assertFalse(summary.isOwner());
|
assertFalse(summary.isCanCancel()); assertFalse(summary.isCanResume());
|
assertThrows(DataException.class,()->service.cancel(form));
|
}
|
@Test void overlongReasonRejectedBeforePrintToMatchNativeColumn() {
|
form.setReason("字".repeat(256)); assertThrows(DataException.class,()->service.prepare(form));
|
verify(mapper,never()).insertAttempt(any());
|
}
|
@Test void noSignatureCannotStart() {
|
form.setBizSign(""); assertThrows(DataException.class,()->service.prepare(form));
|
}
|
@Test void unknownBatchNeverGetsSecondPermit() throws Exception {
|
begin(); assertThrows(DataException.class,()->service.begin(form));
|
assertThrows(DataException.class,()->service.finish(form)); verify(mapper,never()).completeDocument(any());
|
}
|
@Test void receiptSurvivesStatusChangeAndDuplicateKeepsSuccessTime() throws Exception {
|
begin(); copy.setBizStatus("yiqianshou"); service.submitted(form); Date time=saved.getSubmittedAt();
|
service.submitted(form); assertEquals(time,saved.getSubmittedAt()); assertEquals("CONFIRMED",saved.getState());
|
form.setToken(UUID.randomUUID().toString()); assertThrows(DataException.class,()->service.submitted(form));
|
}
|
@Test void partialReceiptDoesNotAllowRegistration() throws Exception {
|
service.prepare(form); form.setBatches(List.of(batch(1),batch(2))); service.plan(form, saved.getFileVersionId(), 2);
|
registerNativeDocument();
|
form.setBatchId(form.getBatches().getFirst().getId()); form.setToken(UUID.randomUUID().toString());
|
service.begin(form); service.submitted(form);
|
assertEquals("OPEN",saved.getState()); assertNull(saved.getSubmittedAt()); assertThrows(DataException.class,()->service.finish(form));
|
}
|
@Test void finishChecksNativeDocumentAndUsesOriginalActorOnce() throws Exception {
|
begin(); service.submitted(form);
|
var linked=new DmsWenjianChongdaFasong(); linked.setDocumentId("doc");
|
when(mapper.findDocument("tenant",saved.getId())).thenReturn(linked);
|
var doc=new DmsWenjianBupinghengExecution(); doc.setBizSign("signed"); doc.setDengjiRenId("actor");
|
when(documents.lockDocument("tenant","doc")).thenReturn(doc);
|
var item=new DmsWenjianBupinghengExecution.Item(); item.setDayinJiluId("copy"); item.setCuoshi("cuoshi3"); item.setYuanyin("纸张损坏");
|
when(documents.lockItems("tenant","doc")).thenReturn(List.of(item));
|
when(mapper.completeDocument(any())).thenReturn(1);
|
item.setCuoshi("cuoshi1"); assertThrows(DataException.class,()->service.finish(form));
|
item.setCuoshi("cuoshi3"); service.finish(form); service.finish(form);
|
assertEquals("CLOSED",saved.getState()); assertEquals("actor",saved.getUserId());
|
verify(mapper,times(1)).completeDocument(saved);
|
verify(documents,never()).voidPrint(anyString(),anyString(),anyString(),any());
|
}
|
@Test void originalOwnerCanCancelOnlyBeforeAnyPermitAndCancellationIsIdempotent() throws Exception {
|
service.prepare(form);
|
assertTrue(service.lockStatus("copy").isCanCancel());
|
service.cancel(form); Date cancelled=saved.getCancelledAt();
|
service.cancel(form); assertEquals(cancelled,saved.getCancelledAt());
|
assertEquals("CANCELLED",saved.getState()); assertFalse(service.lockStatus("copy").isLocked());
|
assertThrows(DataException.class,()->service.prepare(form));
|
verifyNoInteractions(documents);
|
}
|
@Test void cancelWithPlanBeforeSendingAndBeginCannotReviveCancelledTask() throws Exception {
|
service.prepare(form); form.setBatches(List.of(batch(1,2))); service.plan(form,saved.getFileVersionId(),2);
|
form.setBatchId(form.getBatches().getFirst().getId()); form.setToken(UUID.randomUUID().toString());
|
service.cancel(form); assertThrows(DataException.class,()->service.begin(form));
|
}
|
@Test void unknownAndConfirmedTasksCannotBeCancelled() throws Exception {
|
begin();
|
assertEquals("UNKNOWN",service.lockStatus("copy").getPhase());
|
assertFalse(service.lockStatus("copy").isCanCancel()); assertFalse(service.lockStatus("copy").isCanResume());
|
assertThrows(DataException.class,()->service.cancel(form));
|
service.submitted(form);
|
assertEquals("REGISTRATION_PENDING",service.lockStatus("copy").getPhase());
|
assertTrue(service.lockStatus("copy").isCanResume()); assertThrows(DataException.class,()->service.cancel(form));
|
}
|
@Test void incompleteOrWrongVersionPlanRejectedAgainstAuthoritativeCount() throws Exception {
|
service.prepare(form); form.setBatches(List.of(batch(1)));
|
assertThrows(DataException.class,()->service.plan(form,saved.getFileVersionId(),2));
|
assertNull(saved.getPageCount());
|
form.setBatches(List.of(batch(1,2)));
|
assertThrows(DataException.class,()->service.plan(form,"other-version",2));
|
service.plan(form,saved.getFileVersionId(),2); assertEquals(2,saved.getPageCount());
|
}
|
@Test void controlAuditTracksOnlyChangesAndDoesNotExposeDispatchToken() throws Exception {
|
service.prepare(form); verifyNoInteractions(businessAudit);
|
form.setBatches(List.of(batch(1))); service.plan(form, saved.getFileVersionId(), 1);
|
clearInvocations(businessAudit);
|
service.plan(form, saved.getFileVersionId(), 1); verifyNoInteractions(businessAudit);
|
form.setBatchId(form.getBatches().getFirst().getId()); form.setToken(UUID.randomUUID().toString());
|
service.begin(form);
|
verify(businessAudit).record(eq("tenant"), eq("DMS_REPRINT_BATCH_BEGIN"), anyString(), eq("dms_wenjian_chongda_fasong"),
|
eq(form.getRequestId()), argThat(m -> m.toString().contains("READY")),
|
argThat(m -> m.toString().contains("SENDING") && !m.toString().contains(form.getToken())),
|
eq("纸张损坏"), argThat(m -> "copy".equals(m.get("print_record_id")) && "signed".equals(m.get("biz_sign"))));
|
clearInvocations(businessAudit);
|
service.submitted(form); service.submitted(form);
|
verify(businessAudit, times(1)).record(eq("tenant"), eq("DMS_REPRINT_BATCH_RECEIPT"), anyString(), anyString(),
|
anyString(), anyMap(), argThat(m -> "CONFIRMED".equals(m.get("fasong_zhuangtai"))), anyString(), anyMap());
|
clearInvocations(businessAudit);
|
registerNativeDocument(); when(mapper.completeDocument(any())).thenReturn(1);
|
service.finish(form); service.finish(form);
|
verify(businessAudit, times(1)).record(eq("tenant"), eq("DMS_REPRINT_EXECUTION_CONFIRMED"), anyString(),
|
eq("dms_wenjian_bupingheng_dengji"), eq("doc"),
|
argThat(m -> m.get("zhixing_shijian") == null), argThat(m -> m.get("zhixing_shijian") != null), anyString(), anyMap());
|
verify(businessAudit, times(1)).record(eq("tenant"), eq("DMS_REPRINT_COMPLETE"), anyString(), anyString(),
|
anyString(), anyMap(), argThat(m -> "CLOSED".equals(m.get("fasong_zhuangtai"))), anyString(), anyMap());
|
verifyNoMoreInteractions(businessAudit);
|
}
|
|
}
|