package jnpf.dmsService;
|
|
import jnpf.dmsEntity.storage.approval.DmsApprovedItem;
|
import jnpf.dmsEntity.storage.approval.DmsApprovedBatch;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.SerializationFeature;
|
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
import jnpf.dmsEntity.*;
|
import jnpf.dmsEntity.center.DmsCenterVersionSnapshot;
|
import jnpf.dmsMapper.*;
|
import jnpf.exception.DataException;
|
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.mockito.*;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
import java.util.*;
|
import static org.junit.jupiter.api.Assertions.*;
|
import static org.mockito.Mockito.*;
|
|
@ExtendWith(MockitoExtension.class)
|
class DmsApprovedAuditSourceTest {
|
@Mock private jnpf.dmsService.DmsXiudingKanwuService revisionErrata;
|
@Mock private jnpf.file.FileContentApi fileContentApi;
|
@Mock DmsWenjianXinzengShenheMapper newMapper;
|
@Mock DmsWailaiWenjianXinzengShenheMapper externalMapper;
|
@Mock DmsWenjianXiudingShenheMapper revisionMapper;
|
@Mock DmsWenjianXinzengShenheJiluMingxiMapper newRecords;
|
@Mock DmsWenjianXiudingShenheJiluMingxiMapper revisionRecords;
|
@Mock DmsWenjianXiudingShenqingZixiangMapper revisionDetails;
|
@Mock DmsWenjianXiudingShenqingMapper revisionRequests;
|
@Mock DmsWenjianGuanxiMapper existingRelations;
|
@Mock DmsWenjianBianhaoMapper numbers;
|
@Mock DmsWenjianBanbenMapper businessVersions;
|
@Mock jnpf.dmsService.center.DmsCenterQueryService centerVersions;
|
@Mock DmsWenjianGuanlianMingxiMapper relations;
|
@Spy ObjectMapper json = new ObjectMapper();
|
@Mock DmsDisableApplicationGuard disableApplications;
|
@InjectMocks DmsApprovedAuditSource source;
|
String folder = "018f0000-0000-7000-8000-000000000001";
|
Date approved = new Date(System.currentTimeMillis() - 10000);
|
|
DmsWenjianXinzengShenheEntity arrangeNew(List<DmsWenjianXinzengShenheJiluMingxiEntity> rows) {
|
DmsWenjianXinzengShenheEntity a = new DmsWenjianXinzengShenheEntity(); a.setId("audit"); a.setTenantId("tenant");
|
a.setFlowId("flow"); a.setFlowTaskId("task"); a.setFlowState(2); a.setWenjianBianhaoId("number");
|
a.setWenjianBianhao("SOP-001"); a.setWenjianMingcheng("SOP"); a.setWenjianMuluId(folder);
|
a.setGuishuBumenId("dept"); a.setWenjian("[{\"url\":\"/api/file/Image/annex/main.docx\"}]");
|
a.setCreatorUserId("author"); a.setCreatorTime(approved); a.setShengxiaoRiqi(approved);
|
DmsWenjianBianhaoEntity n = new DmsWenjianBianhaoEntity(); n.setWenjianBianhao("SOP-001"); n.setBizStatus("using"); n.setDangqianWenjianMingcheng("SOP");
|
when(newMapper.selectByFlowForUpdate("tenant", "flow", "task")).thenReturn(List.of(a));
|
when(numbers.selectActiveByIdForUpdate("tenant", "number")).thenReturn(n);
|
when(newRecords.selectByAuditForUpdate("tenant", "audit")).thenReturn(rows);
|
return a;
|
}
|
DmsWenjianXinzengShenheJiluMingxiEntity record(String id) {
|
DmsWenjianXinzengShenheJiluMingxiEntity r = new DmsWenjianXinzengShenheJiluMingxiEntity(); r.setId(id); r.setFujianBianhao("REC-" + id);
|
r.setFujianMingcheng("记录" + id); r.setWenjianMuluId(folder); r.setGuishuBumenId("dept");
|
r.setJiluWenjian("[{\"url\":\"/api/file/Image/annex/" + id + ".docx\"}]");
|
r.setJiluLeixing("record"); r.setDengji("low"); r.setShifouShoukong("no"); return r;
|
}
|
DmsApprovedAuditSource.Source load() { return source.load("tenant", "flow", "task", "xinzeng_shenhe", "approver", approved); }
|
DmsApprovedAuditSource.Source loadRevision() { return source.load("tenant", "flow", "task", "xiuding_shenhe", "approver", approved); }
|
|
@Test void resolvesInitialTargetVersionsAsD00ForMainAndRecords() {
|
arrangeNew(List.of(record("one")));
|
assertEquals(List.of("D(00)", "D(00)"), load().batch().getItems().stream()
|
.map(DmsApprovedItem::getVersionLabel).toList());
|
}
|
|
@Test void resolvesUpgradeFromOriginalSequenceRatherThanFormOrCenterLabel() {
|
DmsWenjianBanbenEntity base = arrangeRevision("shengji");
|
base.setBanbenXuhao(9);
|
assertEquals("D(10)", loadRevision().batch().getItems().get(0).getVersionLabel());
|
}
|
|
@Test void resolvesUnchangedVersionFromOriginalCenterVersion() {
|
DmsWenjianBanbenEntity base = arrangeRevision("wubianhua");
|
base.setWenjianNeirong(revisionMapper.selectByFlowForUpdate("tenant", "flow", "task").get(0).getWenjian());
|
UUID versionId = UUID.fromString(base.getWenjianZhongxinBanbenId());
|
DmsCenterVersionSnapshot original = centerVersions.version("tenant", versionId);
|
when(centerVersions.version("tenant", versionId)).thenReturn(new DmsCenterVersionSnapshot(
|
original.documentId(), versionId, "D(07)", original.checksumSha256(), "author", null,
|
original.fileObjectId(), "EFFECTIVE", approved, null));
|
assertEquals("D(07)", loadRevision().batch().getItems().get(0).getVersionLabel());
|
}
|
|
@Test void departmentScopesAreIndependentFrozenAndAbsentLegacyFieldsKeepTheirDigest() {
|
var row = record("one"); var audit = arrangeNew(List.of(row));
|
String legacy = load().batch().getSourceDigest();
|
assertEquals("[]", audit.getChayueShouquanFanweiId());
|
assertEquals("[]", row.getChayueShouquanFanweiId());
|
audit.setChayueShouquanFanweiId("[\"main-dept\"]");
|
row.setChayueShouquanFanweiId("[\"record-dept--subOrg\"]");
|
var batch = load().batch();
|
assertEquals("[\"main-dept\"]", batch.getItems().get(0).getReadAccessScope());
|
assertEquals("[\"record-dept--subOrg\"]", batch.getItems().get(1).getReadAccessScope());
|
assertNotEquals(legacy, batch.getSourceDigest());
|
for (String empty : java.util.Arrays.asList(null, "", "null", "[]")) {
|
audit.setChayueShouquanFanweiId(empty); row.setChayueShouquanFanweiId(empty);
|
assertEquals(legacy, load().batch().getSourceDigest());
|
assertTrue(load().batch().getItems().stream().allMatch(i -> "[]".equals(i.getReadAccessScope())));
|
}
|
}
|
|
@Test void newAuditReadsPersistedReviewersAndProtectsThemInSourceDigest() {
|
var audit = arrangeNew(List.of());
|
String legacy = load().batch().getSourceDigest();
|
assertFalse(json.valueToTree(audit).has("shenheRenId"));
|
audit.setShenheRenId("[\"reviewer\"]");
|
var result = load();
|
assertEquals("[\"reviewer\"]", result.reviewerIds());
|
assertNotEquals(legacy, result.batch().getSourceDigest());
|
audit.setShenheRenId(null);
|
assertEquals(legacy, load().batch().getSourceDigest());
|
}
|
|
@Test void revisionReadsItsOwnPersistedReviewerField() {
|
arrangeRevision("shengji");
|
var audit = revisionMapper.selectByFlowForUpdate("tenant", "flow", "task").get(0);
|
audit.setShenheRenId("[\"revision-reviewer\"]");
|
assertEquals("[\"revision-reviewer\"]", loadRevision().reviewerIds());
|
}
|
|
@Test void carriesTrustedControlFlagsForApprovedMainAndRecordFiles() {
|
DmsWenjianXinzengShenheJiluMingxiEntity controlled = record("two");
|
controlled.setShifouShoukong("yes");
|
controlled.setJiluDengjiGuanliId("rule");
|
controlled.setJiluDengjiGuanliBanben(1);
|
arrangeNew(List.of(record("one"), controlled));
|
|
List<DmsApprovedItem> items = load().batch().getItems();
|
|
assertEquals("yes", items.get(0).getShifouShoukong());
|
assertEquals("no", items.get(1).getShifouShoukong());
|
assertEquals("yes", items.get(2).getShifouShoukong());
|
}
|
|
@org.junit.jupiter.params.ParameterizedTest
|
@org.junit.jupiter.params.provider.ValueSource(strings = {"yes", "no"})
|
void newAuditAcceptsDirectRecordModeWithoutLegacyMetadata(String controlled) {
|
var row = record("mode");
|
row.setJiluLeixing(null); row.setDengji(null); row.setShifouShoukong(controlled);
|
arrangeNew(List.of(row));
|
assertEquals(controlled, load().batch().getItems().get(1).getShifouShoukong());
|
}
|
|
@org.junit.jupiter.params.ParameterizedTest
|
@org.junit.jupiter.params.provider.ValueSource(strings = {"yes", "no"})
|
void revisionAuditAcceptsNewRecordWithDirectMode(String controlled) {
|
arrangeRevision("shengji");
|
var row = new DmsWenjianXiudingShenheJiluMingxiEntity();
|
row.setId("mode"); row.setWenjianMuluId(folder); row.setGuishuBumenId("dept");
|
row.setFujianMingcheng("Record"); row.setFujianBianhao("SOP-001-A");
|
row.setJiluWenjian("[{\"url\":\"/api/file/Image/annex/record.docx\"}]");
|
row.setBiangengFangshi("xinjian"); row.setShifouShoukong(controlled);
|
when(revisionRecords.selectByAuditForUpdate("tenant", "revision-audit")).thenReturn(List.of(row));
|
assertEquals(controlled, loadRevision().batch().getItems().get(1).getShifouShoukong());
|
}
|
|
@org.junit.jupiter.params.ParameterizedTest
|
@org.junit.jupiter.params.provider.NullAndEmptySource
|
@org.junit.jupiter.params.provider.ValueSource(strings = {"online", "offline", "YES"})
|
void auditSourceStillRejectsMissingOrInvalidControlFlag(String controlled) {
|
var row = record("mode"); row.setShifouShoukong(controlled);
|
arrangeNew(List.of(row));
|
assertThrows(DataException.class, this::load);
|
}
|
|
DmsWenjianBanbenEntity arrangeRevision(String change) {
|
DmsWenjianXiudingShenheEntity audit = new DmsWenjianXiudingShenheEntity();
|
audit.setId("revision-audit"); audit.setTenantId("tenant"); audit.setFlowId("flow");
|
audit.setFlowTaskId("task"); audit.setFlowState(2); audit.setXiudingShenqingZixiangId("detail");
|
audit.setBiangengFangshi(change); audit.setWenjianMingcheng("SOP");
|
audit.setWenjian("[{\"fileId\":\"revision\",\"url\":\"/api/file/Image/annex/revision.docx\"}]");
|
audit.setWenjianMuluId(folder); audit.setGuishuBumenId("dept");
|
audit.setCreatorUserId("author"); audit.setCreatorTime(approved); audit.setShengxiaoRiqi(approved);
|
DmsWenjianXiudingShenqingZixiangEntity detail = new DmsWenjianXiudingShenqingZixiangEntity();
|
detail.setId("detail"); detail.setForeignId("request"); detail.setWenjianId("resource");
|
detail.setYuanWenjianBanbenId("base-version"); detail.setWenjianBianhao("SOP-001");
|
DmsWenjianXiudingShenqingEntity request = new DmsWenjianXiudingShenqingEntity();
|
request.setId("request"); request.setFlowState(2);
|
request.setCreatorUserId("request-author"); request.setCreatorTime(approved);
|
UUID centerVersionId = UUID.randomUUID();
|
DmsWenjianBanbenEntity base = new DmsWenjianBanbenEntity();
|
base.setId("base-version"); base.setWenjianId("resource"); base.setBanbenXuhao(0);
|
base.setWenjianZhongxinBanbenId(centerVersionId.toString());
|
base.setWenjianBianhaoId("number"); base.setWenjianBianhao("SOP-001");
|
DmsCenterVersionSnapshot center = new DmsCenterVersionSnapshot(
|
UUID.randomUUID(), centerVersionId, "D(00)", "a".repeat(64), "author", null,
|
UUID.randomUUID(), "EFFECTIVE", approved, null);
|
when(revisionMapper.selectByFlowForUpdate("tenant", "flow", "task")).thenReturn(List.of(audit));
|
when(revisionRecords.selectByAuditForUpdate("tenant", "revision-audit")).thenReturn(List.of());
|
when(revisionDetails.selectByIdForUpdate("tenant", "detail")).thenReturn(detail);
|
when(revisionRequests.selectByIdForUpdate("tenant", "request")).thenReturn(request);
|
when(businessVersions.selectById("tenant", "base-version")).thenReturn(base);
|
when(centerVersions.version("tenant", centerVersionId)).thenReturn(center);
|
return base;
|
}
|
|
@Test void revisionDigestIncludesHandlingButIgnoresLiveProcessedFlag() {
|
arrangeRevision("shengji");
|
String noErrata = loadRevision().batch().getSourceDigest();
|
jnpf.dmsEntity.kanwu.DmsXiudingKanwuRow row = new jnpf.dmsEntity.kanwu.DmsXiudingKanwuRow();
|
row.setKanwuZixiangId("note"); row.setChuliJielun("bucaina"); row.setChuliYuanyin("reason");
|
when(revisionErrata.loadAndValidate("tenant", "revision-audit", "resource", "base-version")).thenReturn(List.of(row));
|
String handled = loadRevision().batch().getSourceDigest();
|
assertNotEquals(noErrata, handled);
|
row.setAlreadyProcessed(true);
|
assertEquals(handled, loadRevision().batch().getSourceDigest());
|
row.setChuliYuanyin("changed reason");
|
assertNotEquals(handled, loadRevision().batch().getSourceDigest());
|
row.setChuliYuanyin("reason"); row.setChuliJielun("yixiugai_queren");
|
assertNotEquals(handled, loadRevision().batch().getSourceDigest());
|
}
|
|
@Test void revisionFallsBackToApprovedRequestCreatorWhenAuditCopyIsMissing() {
|
arrangeRevision("shengji");
|
DmsWenjianXiudingShenheEntity audit = revisionMapper
|
.selectByFlowForUpdate("tenant", "flow", "task").get(0);
|
audit.setCreatorUserId(null);
|
audit.setCreatorTime(null);
|
|
DmsApprovedAuditSource.Source result = loadRevision();
|
|
assertEquals("request-author", result.preparedBy());
|
assertEquals(approved, result.preparedOn());
|
}
|
|
@Test void reuseAcceptsEquivalentFileJsonWithDifferentPropertyOrder() {
|
DmsWenjianBanbenEntity base = arrangeRevision("wubianhua");
|
base.setWenjianNeirong("[{\"name\":\"SOP.docx\",\"fileId\":\"revision\",\"url\":\"/api/file/Image/annex/revision.docx\"}]");
|
DmsWenjianXiudingShenheEntity audit = revisionMapper
|
.selectByFlowForUpdate("tenant", "flow", "task").get(0);
|
audit.setWenjian("[{\"fileId\":\"revision\",\"url\":\"/api/file/Image/annex/revision.docx\",\"name\":\"SOP.docx\"}]");
|
|
assertEquals("REUSE", loadRevision().batch().getItems().get(0).getChangeType());
|
}
|
|
@Test void reuseIgnoresDisplayMetadataButKeepsAuthoritativeOriginal() throws Exception {
|
var apiField = DmsApprovedAuditSource.class.getDeclaredField("fileContentApi");
|
apiField.setAccessible(true); apiField.set(source, fileContentApi);
|
DmsWenjianBanbenEntity base = arrangeRevision("wubianhua");
|
String original = "[{\"fileId\":\"original\",\"name\":\"source.txt\",\"url\":\"/api/file/Image/annex/source.txt\",\"uploadReceipt\":\"historical-token\",\"fileVersionId\":null}]";
|
base.setWenjianNeirong(original);
|
DmsWenjianXiudingShenheEntity audit = revisionMapper.selectByFlowForUpdate("tenant", "flow", "task").get(0);
|
audit.setWenjian(original);
|
assertEquals(original, loadRevision().batch().getItems().get(0).getFileJson());
|
for (String changed : List.of(original.replace(",\"uploadReceipt\":\"historical-token\"", ""),
|
original.replace(",\"fileVersionId\":null", ""))) {
|
audit.setWenjian(changed);
|
assertEquals(original, loadRevision().batch().getItems().get(0).getFileJson());
|
}
|
var response = new jnpf.base.ActionResult<Boolean>(); response.setCode(200); response.setData(false);
|
when(fileContentApi.sameContent(eq("jnpf-dms"), any())).thenReturn(response);
|
audit.setWenjian(original.replace("original", "other-file"));
|
assertEquals("沿用版本不能修改文件内容", assertThrows(DataException.class, this::loadRevision).getMessage());
|
}
|
|
@Test void reuseStillRejectsARealFileChange() {
|
DmsWenjianBanbenEntity base = arrangeRevision("wubianhua");
|
base.setWenjianNeirong("[{\"url\":\"/api/file/Image/annex/original.docx\"}]");
|
|
DataException exception = assertThrows(DataException.class, this::loadRevision);
|
|
assertEquals("沿用版本不能修改文件内容", exception.getMessage());
|
}
|
DmsApprovedAuditSource sourceWithJson(ObjectMapper mapper) {
|
return new DmsApprovedAuditSource(newMapper, externalMapper, revisionMapper, newRecords, revisionRecords,
|
revisionDetails, revisionRequests, existingRelations, numbers, businessVersions,
|
centerVersions, relations, mapper, disableApplications, revisionErrata);
|
}
|
|
@Test void externalApprovalUsesSavedVersionAndOneControlledFile() {
|
DmsWailaiWenjianXinzengShenheEntity audit = new DmsWailaiWenjianXinzengShenheEntity();
|
audit.setId("external"); audit.setFlowState(2);
|
audit.setWailaiWenjianBianhao("EXT-001"); audit.setWenjianMingcheng("外来文件");
|
audit.setBanbenhao("D(01)"); audit.setBizFiles("[{\"name\":\"external.pdf\",\"url\":\"/file\"}]");
|
audit.setWenjianMuluId(folder); audit.setBumenId("dept");
|
audit.setShenqingRen("applicant"); audit.setShenqingRiqi(approved);
|
when(externalMapper.selectByFlowForUpdate("tenant", "flow", "task")).thenReturn(List.of(audit));
|
DmsApprovedBatch batch = source.load("tenant", "flow", "task",
|
"wailai_wenjian_xinzeng_shenhe", "approver", approved).batch();
|
assertEquals(1, batch.getItems().size());
|
assertEquals("D(01)", batch.getItems().get(0).getVersionLabel());
|
assertEquals("yes", batch.getItems().get(0).getShifouShoukong());
|
assertNull(batch.getEffectiveTo());
|
String digest = batch.getSourceDigest();
|
audit.setBanbenhao("D(02)");
|
assertNotEquals(digest, source.load("tenant", "flow", "task",
|
"wailai_wenjian_xinzeng_shenhe", "approver", approved).batch().getSourceDigest());
|
}
|
@Test void approvalDigestIsIndependentOfApplicationJsonFormatting() {
|
DmsWenjianXinzengShenheEntity audit = arrangeNew(List.of(record("a")));
|
audit.setShengxiaoRiqi(new Date(1789660800000L));
|
ObjectMapper baseline = new ObjectMapper().disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
|
String expected = sourceWithJson(baseline).load("tenant", "flow", "task", "xinzeng_shenhe", "approver", approved).batch().getSourceDigest();
|
List<ObjectMapper> variants = Arrays.asList(
|
baseline.copy().enable(SerializationFeature.INDENT_OUTPUT),
|
baseline.copy().enable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS),
|
baseline.copy().setTimeZone(TimeZone.getTimeZone("Asia/Shanghai")),
|
baseline.copy().setSerializationInclusion(JsonInclude.Include.NON_NULL));
|
for (ObjectMapper variant : variants) {
|
String actual = sourceWithJson(variant).load("tenant", "flow", "task", "xinzeng_shenhe", "approver", approved).batch().getSourceDigest();
|
assertEquals(expected, actual, "Application JSON settings must not change a frozen approval digest");
|
}
|
}
|
@Test void nativeApprovalFieldsIgnoreApplicationPropertyNaming() {
|
arrangeNew(List.of(record("a")));
|
ObjectMapper mapper = new ObjectMapper().setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
|
DmsApprovedAuditSource.Source result = sourceWithJson(mapper).load("tenant", "flow", "task", "xinzeng_shenhe", "approver", approved);
|
assertEquals(2, result.batch().getItems().size());
|
assertEquals("SOP", result.batch().getItems().get(0).getDocumentName());
|
}
|
@Test void preservesNativePreparationMetadataWhenApproverAndApprovalDateDiffer() {
|
DmsWenjianXinzengShenheEntity audit = arrangeNew(List.of());
|
Date preparationDate = new Date(approved.getTime() - 86400000L);
|
audit.setCreatorTime(preparationDate);
|
DmsApprovedAuditSource.Source result = load();
|
assertEquals("author", result.preparedBy());
|
assertEquals(preparationDate, result.preparedOn());
|
assertEquals("approver", result.batch().getActorId());
|
assertEquals(approved, result.batch().getApprovedAt());
|
}
|
@Test void presetDateNeverSubstitutesForRequiredFormalDate() {
|
DmsWenjianXinzengShenheEntity audit = arrangeNew(List.of());
|
audit.setShengxiaoRiqi(null); audit.setYusheShengxiaoRiqi(approved);
|
assertThrows(DataException.class, this::load);
|
}
|
@Test void includesAllNativeRowsWithStableDistinctSourceKeys() {
|
DmsWenjianXinzengShenheJiluMingxiEntity a = record("a"); DmsWenjianXinzengShenheJiluMingxiEntity b = record("b"); arrangeNew(List.of(b, a));
|
DmsApprovedAuditSource.Source first = load();
|
assertEquals(List.of("xinzeng_shenhe:audit:zhuwenjian", "xinzeng_shenhe:audit:jilu:a", "xinzeng_shenhe:audit:jilu:b"),
|
first.batch().getItems().stream().map(i -> i.getSourceKey()).toList());
|
when(newRecords.selectByAuditForUpdate("tenant", "audit")).thenReturn(List.of(a, b));
|
assertEquals(first.batch().getSourceDigest(), load().batch().getSourceDigest());
|
}
|
@Test void detectsMetadataOrRowSetChangesButIgnoresSettlementOutputs() {
|
DmsWenjianXinzengShenheJiluMingxiEntity row = record("a"); DmsWenjianXinzengShenheEntity audit = arrangeNew(List.of(row)); String before = load().batch().getSourceDigest();
|
audit.setWenjianId("resource"); audit.setShengxiaoWenjianBanbenId("business-version");
|
row.setJiluWenjianId("record-resource"); row.setShengxiaoWenjianBanbenId("record-version"); row.setBanbenhao("D(00)");
|
assertEquals(before, load().batch().getSourceDigest()); assertTrue(load().settled());
|
row.setFujianMingcheng("changed"); assertNotEquals(before, load().batch().getSourceDigest());
|
when(newRecords.selectByAuditForUpdate("tenant", "audit")).thenReturn(List.of());
|
assertNotEquals(before, load().batch().getSourceDigest());
|
}
|
@Test void transportsFrozenSignaturePageOnlyOnMainFileAndPreservesUploadText() throws Exception {
|
arrangeNew(List.of(record("a")));
|
DmsWenjianBianhaoEntity number = numbers.selectActiveByIdForUpdate("tenant", "number");
|
String oldDigest = load().batch().getSourceDigest();
|
String files = "[ {\"name\":\"签批页.docx\",\"url\":\"/api/file/Image/annex/sign.docx\"} ]";
|
number.setGuizeKuaizhao(json.writeValueAsString(Map.of("qianpiye_id", "signature-id", "qianpiye_files", files)));
|
jnpf.dmsEntity.storage.approval.DmsApprovedBatch batch = load().batch();
|
com.fasterxml.jackson.databind.JsonNode wire = json.readTree(json.writeValueAsString(jnpf.dmsEntity.storage.form.DmsApprovedRegistrationForm.from(batch)));
|
assertEquals("signature-id", wire.path("files").get(0).path("qianpiyeId").asText());
|
assertEquals(files, wire.path("files").get(0).path("qianpiyeFiles").asText());
|
assertNull(batch.getItems().get(1).getQianpiyeId());
|
// The upstream approval-page input participates in the frozen approval digest.
|
assertNotEquals(oldDigest, batch.getSourceDigest());
|
}
|
@Test void acceptsHistoricalSnapshotsWithoutSignaturePage() {
|
arrangeNew(List.of());
|
DmsWenjianBianhaoEntity number = numbers.selectActiveByIdForUpdate("tenant", "number");
|
for (String snapshot : Arrays.asList(null, "", "{}", "{\"qianpiye_id\":null,\"qianpiye_files\":null}")) {
|
number.setGuizeKuaizhao(snapshot);
|
jnpf.dmsEntity.storage.approval.DmsApprovedItem main = load().batch().getItems().get(0);
|
assertNull(main.getQianpiyeId()); assertNull(main.getQianpiyeFiles());
|
}
|
}
|
@Test void rejectsMalformedSignaturePageSnapshotInsteadOfLookingUpCurrentRule() {
|
arrangeNew(List.of());
|
numbers.selectActiveByIdForUpdate("tenant", "number").setGuizeKuaizhao("{\"qianpiye_files\":[]}");
|
assertThrows(DataException.class, this::load);
|
}
|
@Test void rejectsIncompleteRowBeforeAnyFileUpload() {
|
DmsWenjianXinzengShenheJiluMingxiEntity row = record("a"); row.setJiluWenjian(null); arrangeNew(List.of(row));
|
assertThrows(DataException.class, this::load);
|
}
|
@Test void rejectsUnapprovedNativeSource() {
|
arrangeNew(List.of()).setFlowState(1); assertThrows(DataException.class, this::load);
|
}
|
|
@Test void sendsOnlyTheNewMainFilesFrozenApprovalPageAndIncludesItInTheDigest() {
|
arrangeNew(List.of(record("a")));
|
DmsWenjianBianhaoEntity number = number(
|
"{\"qianpiye_files\":\"[{\\\"url\\\":\\\"/api/file/Image/annex/approval.pdf\\\"}]\"}");
|
when(numbers.selectActiveByIdForUpdate("tenant", "number")).thenReturn(number);
|
|
DmsApprovedAuditSource.Source first = load();
|
|
assertEquals("[{\"url\":\"/api/file/Image/annex/approval.pdf\"}]",
|
first.batch().getItems().get(0).getApprovalPageFileJson());
|
assertNull(first.batch().getItems().get(1).getApprovalPageFileJson());
|
String firstDigest = first.batch().getSourceDigest();
|
number.setGuizeKuaizhao(
|
"{\"qianpiye_files\":\"[{\\\"url\\\":\\\"/api/file/Image/annex/changed.pdf\\\"}]\"}");
|
assertNotEquals(firstDigest, load().batch().getSourceDigest());
|
}
|
|
@Test void missingOrEmptyApprovalPageDoesNotAffectNewFileRegistration() {
|
arrangeNew(List.of());
|
DmsWenjianBianhaoEntity number = number("{\"qianpiye_files\":\"[]\"}");
|
when(numbers.selectActiveByIdForUpdate("tenant", "number")).thenReturn(number);
|
|
assertNull(load().batch().getItems().get(0).getApprovalPageFileJson());
|
|
number.setGuizeKuaizhao(null);
|
assertNull(load().batch().getItems().get(0).getApprovalPageFileJson());
|
}
|
|
@Test void revisionUsesTheOriginalBusinessVersionsNumberRuleApprovalPage() {
|
arrangeRevision("shengji");
|
DmsWenjianBianhaoEntity number = number(
|
"{\"qianpiye_files\":\"[{\\\"url\\\":\\\"/api/file/Image/annex/approval.docx\\\"}]\"}");
|
when(numbers.selectActiveByIdForUpdate("tenant", "number")).thenReturn(number);
|
|
DmsApprovedItem main = loadRevision().batch().getItems().get(0);
|
|
assertEquals("REVISE", main.getChangeType());
|
assertEquals("[{\"url\":\"/api/file/Image/annex/approval.docx\"}]",
|
main.getApprovalPageFileJson());
|
assertEquals(main.getApprovalPageFileJson(), main.getQianpiyeFiles());
|
}
|
|
@Test void revisionWithoutConfiguredApprovalPageContinuesNormally() {
|
arrangeRevision("shengji");
|
when(numbers.selectActiveByIdForUpdate("tenant", "number"))
|
.thenReturn(number("{\"qianpiye_files\":\"[]\"}"));
|
|
DmsApprovedItem main = loadRevision().batch().getItems().get(0);
|
assertNull(main.getApprovalPageFileJson());
|
assertNull(main.getQianpiyeFiles());
|
}
|
|
@Test void legacyRevisionWithoutResolvableNumberContinuesNormally() {
|
DmsWenjianBanbenEntity base = arrangeRevision("shengji");
|
base.setWenjianBianhaoId(null);
|
|
assertNull(loadRevision().batch().getItems().get(0).getApprovalPageFileJson());
|
verify(numbers, never()).selectActiveByIdForUpdate(eq("tenant"), any());
|
}
|
|
private DmsWenjianBianhaoEntity number(String snapshot) {
|
DmsWenjianBianhaoEntity number = new DmsWenjianBianhaoEntity();
|
number.setWenjianBianhao("SOP-001");
|
number.setBizStatus("using");
|
number.setDangqianWenjianMingcheng("SOP");
|
number.setGuizeKuaizhao(snapshot);
|
return number;
|
}
|
DmsWenjianXiudingShenheEntity arrangeRevision() {
|
DmsWenjianXiudingShenheEntity audit = new DmsWenjianXiudingShenheEntity();
|
audit.setId("revision"); audit.setFlowState(2); audit.setCreatorUserId("author");
|
audit.setCreatorTime(approved); audit.setShengxiaoRiqi(approved);
|
audit.setWenjianMuluId(folder); audit.setGuishuBumenId("dept"); audit.setWenjianMingcheng("main");
|
audit.setBiangengFangshi("wubianhua"); audit.setXiudingShenqingZixiangId("detail");
|
DmsWenjianXiudingShenqingZixiangEntity detail = new DmsWenjianXiudingShenqingZixiangEntity(); detail.setId("detail");
|
detail.setForeignId("request"); detail.setWenjianId("main"); detail.setYuanWenjianBanbenId("main-v1");
|
DmsWenjianXiudingShenqingEntity request = new DmsWenjianXiudingShenqingEntity(); request.setFlowState(2);
|
when(revisionMapper.selectByFlowForUpdate("tenant", "flow", "task")).thenReturn(List.of(audit));
|
when(revisionDetails.selectByIdForUpdate("tenant", "detail")).thenReturn(detail);
|
when(revisionRequests.selectByIdForUpdate("tenant", "request")).thenReturn(request);
|
DmsWenjianBanbenEntity main = businessVersion("main", "main-v1");
|
when(businessVersions.selectById("tenant", "main-v1")).thenReturn(main);
|
return audit;
|
}
|
DmsWenjianBanbenEntity businessVersion(String fileId, String versionId) {
|
DmsWenjianBanbenEntity value = new DmsWenjianBanbenEntity(); value.setId(versionId); value.setWenjianId(fileId);
|
value.setWenjianLeixing("main".equals(fileId) ? "zhuwenjian" : "jilu"); value.setBizStatus("effective");
|
UUID centerId = UUID.randomUUID(), documentId = UUID.randomUUID();
|
value.setWenjianZhongxinBanbenId(centerId.toString());
|
when(centerVersions.version("tenant", centerId)).thenReturn(new jnpf.dmsEntity.center.DmsCenterVersionSnapshot(
|
documentId, centerId, "D(00)", "a".repeat(64), "author", null, UUID.randomUUID(), "EFFECTIVE", approved, null));
|
return value;
|
}
|
@Test void freezesRemovedRecordsAndRejectsChangedRemovalBaselineByDigest() {
|
arrangeRevision();
|
DmsWenjianGuanxiEntity relation = new DmsWenjianGuanxiEntity(); relation.setGuanlianWenjianId("record");
|
when(existingRelations.selectEffectiveByMainAndType("tenant", "main", "jilu")).thenReturn(List.of(relation));
|
DmsWenjianBanbenEntity record = businessVersion("record", "record-v1");
|
when(businessVersions.selectCurrentByFileId("tenant", "record")).thenReturn(record);
|
DmsApprovedBatch first = source.load("tenant", "flow", "task", "xiuding_shenhe", "approver", approved).batch();
|
assertEquals(2, first.getItems().size());
|
DmsApprovedItem removed = first.getItems().get(1);
|
assertEquals("RETIRE", removed.getChangeType()); assertEquals("record", removed.getFileId());
|
assertEquals("record-v1", removed.getBusinessVersionId());
|
assertEquals(removed.getFileVersionId(), removed.getBaseFileVersionId());
|
assertFalse(removed.requiresContentRegistration());
|
record.setId("record-v2");
|
assertNotEquals(first.getSourceDigest(), source.load("tenant", "flow", "task", "xiuding_shenhe", "approver", approved).batch().getSourceDigest());
|
}
|
@Test void absentUnsavedRecordCreatesNoRetirementTarget() {
|
arrangeRevision();
|
DmsApprovedBatch result = source.load("tenant", "flow", "task", "xiuding_shenhe", "approver", approved).batch();
|
assertEquals(1, result.getItems().size());
|
assertEquals("REUSE", result.getItems().get(0).getChangeType());
|
}
|
|
}
|