package jnpf.dmsService;
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
|
import jnpf.base.UserInfo;
|
import jnpf.base.DictionaryDataApi;
|
import jnpf.base.entity.DictionaryDataEntity;
|
import jnpf.dmsEntity.watermark.DmsWatermarkParameters;
|
import jnpf.dmsEntity.permission.entity.DmsDocumentEntity;
|
import jnpf.dmsEntity.permission.entity.DmsFileVersionEntity;
|
import jnpf.dmsEntity.preview.model.DmsWatermarkSnapshot;
|
import jnpf.dmsEntity.watermark.DmsWatermarkConfigEntity;
|
import jnpf.dmsEntity.watermark.DmsWatermarkPositionEntity;
|
import jnpf.dmsIntegration.audit.DmsVersionStateAuditRow;
|
import jnpf.dmsMapper.DmsDocumentMapper;
|
import jnpf.dmsMapper.DmsVersionStateMapper;
|
import jnpf.dmsMapper.DmsWatermarkConfigMapper;
|
import jnpf.dmsMapper.DmsWatermarkPositionMapper;
|
import jnpf.permission.OrganizeApi;
|
import org.apache.ibatis.builder.MapperBuilderAssistant;
|
import org.apache.ibatis.session.Configuration;
|
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.mockito.Mock;
|
import org.mockito.ArgumentCaptor;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
|
import java.math.BigDecimal;
|
import java.util.Collections;
|
import java.util.List;
|
import java.util.UUID;
|
|
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.never;
|
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.lenient;
|
|
@ExtendWith(MockitoExtension.class)
|
class DmsWatermarkPreviewResolverTest {
|
private static final UUID VERSION_ID = UUID.fromString("0199d0e5-8e70-7000-8000-000000000010");
|
private static final UUID DOCUMENT_ID = UUID.fromString("0199d0e5-8e70-7000-8000-000000000011");
|
private static final UUID CONFIG_ID = UUID.fromString("0199d0e5-8e70-7000-8000-000000000012");
|
|
@Mock private DmsWatermarkConfigMapper configMapper;
|
@Mock private DmsWatermarkPositionMapper positionMapper;
|
@Mock private DmsDocumentMapper documentMapper;
|
@Mock private DmsVersionStateMapper versionStateMapper;
|
@Mock private OrganizeApi organizeApi;
|
@Mock private DictionaryDataApi dictionaryDataApi;
|
private DmsWatermarkPreviewResolver resolver;
|
|
@BeforeEach
|
void setUp() {
|
initTable(DmsWatermarkConfigEntity.class);
|
initTable(DmsWatermarkPositionEntity.class);
|
resolver = new DmsWatermarkPreviewResolver(configMapper, positionMapper, documentMapper,
|
versionStateMapper, organizeApi, dictionaryDataApi);
|
DictionaryDataEntity entry = new DictionaryDataEntity();
|
entry.setDictionaryTypeId("879384732964547653");
|
entry.setEnCode("RECORD");
|
entry.setEnabledMark(1);
|
lenient().when(dictionaryDataApi.getList("879384732964547653", "true")).thenReturn(List.of(entry));
|
lenient().when(configMapper.selectMatchingConfigIds(any(), any(), any())).thenReturn(List.of(CONFIG_ID));
|
}
|
|
@Test
|
void resolvesPreviewWatermarkForCurrentDocxAndReplacesTrustedVariables() {
|
UserInfo user = user();
|
user.setOrganizeIds(Collections.emptyList());
|
DmsVersionStateAuditRow state = new DmsVersionStateAuditRow();
|
state.setLifecycleStatus("EFFECTIVE");
|
when(versionStateMapper.selectAuditState("tenant-1", VERSION_ID)).thenReturn(state);
|
DmsWatermarkConfigEntity aligned = config();
|
aligned.setHorizontalAlign("RIGHT");
|
aligned.setVerticalAlign("MIDDLE");
|
when(configMapper.selectList(any())).thenReturn(Collections.singletonList(aligned));
|
when(positionMapper.selectList(any())).thenReturn(Collections.singletonList(position()));
|
DmsDocumentEntity document = new DmsDocumentEntity();
|
document.setDocumentNo("SOP-001");
|
when(documentMapper.selectByTenantAndId("tenant-1", DOCUMENT_ID)).thenReturn(document);
|
|
List<DmsWatermarkSnapshot> result = resolver.resolve(user, "tenant-1", version("manual.docx"), types());
|
|
assertEquals(1, result.size());
|
assertEquals("张三 / 质量部 / SOP-001 / V1.0 / 现行版本", result.get(0).getContent());
|
assertEquals(1, result.get(0).getPositions().size());
|
for (DmsWatermarkSnapshot snapshot : List.of(result.get(0),
|
resolver.resolveDownload(user, "tenant-1", version("manual.docx"), types()).get(0),
|
resolver.resolvePrint(user, "tenant-1", version("manual.docx"), types()).get(0))) {
|
assertEquals("RIGHT", snapshot.getHorizontalAlign());
|
assertEquals("MIDDLE", snapshot.getVerticalAlign());
|
}
|
}
|
|
@Test
|
void copiesBarcodeTextDisplayIntoPreviewSnapshot() {
|
DmsVersionStateAuditRow state = new DmsVersionStateAuditRow();
|
state.setLifecycleStatus("EFFECTIVE");
|
when(versionStateMapper.selectAuditState("tenant-1", VERSION_ID)).thenReturn(state);
|
DmsWatermarkConfigEntity config = config();
|
config.setWatermarkType("BARCODE");
|
config.setBarcodeFormat("QR_CODE");
|
config.setContentTemplate("{/FILENO}");
|
config.setShowBarcodeText(true);
|
when(configMapper.selectList(any())).thenReturn(Collections.singletonList(config));
|
when(positionMapper.selectList(any())).thenReturn(Collections.singletonList(position()));
|
DmsDocumentEntity document = new DmsDocumentEntity();
|
document.setDocumentNo("SOP-001");
|
when(documentMapper.selectByTenantAndId("tenant-1", DOCUMENT_ID)).thenReturn(document);
|
|
List<DmsWatermarkSnapshot> result = resolver.resolve(user(), "tenant-1", version("manual.docx"), types());
|
|
assertEquals(Boolean.TRUE, result.get(0).getShowBarcodeText());
|
assertEquals("SOP-001", result.get(0).getContent());
|
}
|
|
@Test
|
void downloadUsesDownloadSceneConfiguration() {
|
DmsVersionStateAuditRow state = new DmsVersionStateAuditRow();
|
state.setLifecycleStatus("EFFECTIVE");
|
when(versionStateMapper.selectAuditState("tenant-1", VERSION_ID)).thenReturn(state);
|
when(configMapper.selectList(any())).thenReturn(Collections.singletonList(config()));
|
when(positionMapper.selectList(any())).thenReturn(Collections.singletonList(position()));
|
|
resolver.resolveDownload(user(), "tenant-1", version("manual.docx"), types());
|
|
@SuppressWarnings("unchecked")
|
ArgumentCaptor<LambdaQueryWrapper<DmsWatermarkConfigEntity>> captor =
|
ArgumentCaptor.forClass(LambdaQueryWrapper.class);
|
verify(configMapper).selectList(captor.capture());
|
String sql = captor.getValue().getSqlSegment();
|
assertTrue(sql.contains("load_download"));
|
assertTrue(!sql.contains("load_preview"));
|
}
|
|
@Test
|
void printUsesPrintSceneConfiguration() {
|
DmsVersionStateAuditRow state = new DmsVersionStateAuditRow();
|
state.setLifecycleStatus("EFFECTIVE");
|
when(versionStateMapper.selectAuditState("tenant-1", VERSION_ID)).thenReturn(state);
|
when(configMapper.selectList(any())).thenReturn(Collections.singletonList(config()));
|
when(positionMapper.selectList(any())).thenReturn(Collections.singletonList(position()));
|
|
resolver.resolvePrint(user(), "tenant-1", version("manual.docx"), types());
|
|
@SuppressWarnings("unchecked")
|
ArgumentCaptor<LambdaQueryWrapper<DmsWatermarkConfigEntity>> captor =
|
ArgumentCaptor.forClass(LambdaQueryWrapper.class);
|
verify(configMapper).selectList(captor.capture());
|
String sql = captor.getValue().getSqlSegment();
|
assertTrue(sql.contains("load_print"));
|
assertTrue(!sql.contains("load_preview"));
|
assertTrue(!sql.contains("load_download"));
|
}
|
|
@Test
|
void rejectsChineseFileNameInCode128BeforeIssuingPreview() {
|
DmsVersionStateAuditRow state = new DmsVersionStateAuditRow();
|
state.setLifecycleStatus("EFFECTIVE");
|
when(versionStateMapper.selectAuditState("tenant-1", VERSION_ID)).thenReturn(state);
|
DmsWatermarkConfigEntity config = config();
|
config.setName("测试2");
|
config.setWatermarkType("BARCODE");
|
config.setBarcodeFormat("CODE_128");
|
config.setContentTemplate("{/FILENAME}-{/VERSION}");
|
when(configMapper.selectList(any())).thenReturn(Collections.singletonList(config));
|
when(positionMapper.selectList(any())).thenReturn(Collections.singletonList(position()));
|
jnpf.exception.DataException error = assertThrows(jnpf.exception.DataException.class,
|
() -> resolver.resolve(user(), "tenant-1", version("玻思韬.docx"), types()));
|
assertTrue(error.getMessage().contains("测试2"));
|
assertTrue(error.getMessage().contains("请将条码格式改为二维码"));
|
}
|
|
@Test
|
void historicalVersionUsesAllScopeWatermarksAndHistoricalStateText() {
|
DmsVersionStateAuditRow state = new DmsVersionStateAuditRow();
|
state.setLifecycleStatus("SUPERSEDED");
|
when(versionStateMapper.selectAuditState("tenant-1", VERSION_ID)).thenReturn(state);
|
DmsWatermarkConfigEntity allVersions = config();
|
allVersions.setVersionScope("ALL");
|
when(configMapper.selectList(any())).thenReturn(Collections.singletonList(allVersions));
|
when(positionMapper.selectList(any())).thenReturn(Collections.singletonList(position()));
|
|
List<DmsWatermarkSnapshot> result = resolver.resolve(user(), "tenant-1", version("manual.docx"), types());
|
|
assertEquals(1, result.size());
|
assertTrue(result.get(0).getContent().contains("历史版本"));
|
@SuppressWarnings("unchecked")
|
ArgumentCaptor<LambdaQueryWrapper<DmsWatermarkConfigEntity>> captor =
|
ArgumentCaptor.forClass(LambdaQueryWrapper.class);
|
verify(configMapper).selectList(captor.capture());
|
String sql = captor.getValue().getSqlSegment();
|
assertTrue(sql.contains("version_scope"), sql);
|
assertTrue(!sql.contains("version_scope IN"), sql);
|
}
|
|
@Test
|
void pendingVersionDoesNotResolveWatermarks() {
|
DmsVersionStateAuditRow state = new DmsVersionStateAuditRow();
|
state.setLifecycleStatus("PENDING");
|
when(versionStateMapper.selectAuditState("tenant-1", VERSION_ID)).thenReturn(state);
|
|
assertEquals(0, resolver.resolve(user(), "tenant-1", version("manual.docx"), types()).size());
|
verify(configMapper, never()).selectList(any());
|
}
|
|
@Test
|
void resolvesPreviewWatermarkForSupportedImages() {
|
DmsVersionStateAuditRow state = new DmsVersionStateAuditRow();
|
state.setLifecycleStatus("EFFECTIVE");
|
when(versionStateMapper.selectAuditState("tenant-1", VERSION_ID)).thenReturn(state);
|
when(configMapper.selectList(any())).thenReturn(Collections.singletonList(config()));
|
when(positionMapper.selectList(any())).thenReturn(Collections.singletonList(position()));
|
|
assertEquals(1, resolver.resolve(user(), "tenant-1", version("diagram.PNG"), types()).size());
|
assertEquals(1, resolver.resolve(user(), "tenant-1", version("photo.jpg"), types()).size());
|
DmsFileVersionEntity mimeOnlyImage = version("diagram");
|
mimeOnlyImage.setMimeType("image/png");
|
assertEquals(1, resolver.resolve(user(), "tenant-1", mimeOnlyImage, types()).size());
|
}
|
|
@Test
|
void resolvesWatermarksForPdfInEveryScene() {
|
DmsVersionStateAuditRow state = new DmsVersionStateAuditRow();
|
state.setLifecycleStatus("EFFECTIVE");
|
when(versionStateMapper.selectAuditState("tenant-1", VERSION_ID)).thenReturn(state);
|
when(configMapper.selectList(any())).thenReturn(Collections.singletonList(config()));
|
when(positionMapper.selectList(any())).thenReturn(Collections.singletonList(position()));
|
|
DmsFileVersionEntity pdf = version("manual.pdf");
|
assertEquals(1, resolver.resolve(user(), "tenant-1", pdf, types()).size());
|
assertEquals(1, resolver.resolveDownload(user(), "tenant-1", pdf, types()).size());
|
assertEquals(1, resolver.resolvePrint(user(), "tenant-1", pdf, types()).size());
|
}
|
|
@Test
|
void eachRuntimeEntryProducesItsOwnBarcodeWithIndependentPayloadAndLabelInEveryScene() {
|
customAndOrdinaryConfigurations();
|
jnpf.dmsEntity.watermark.DmsWatermarkParameters parameters = new jnpf.dmsEntity.watermark.DmsWatermarkParameters();
|
parameters.setCustomBarcode(List.of(entry(5, "REC-005", "第五页"),
|
entry(4, "记录 {/CUN} <&>", "文字 {/CDN} <&>"),
|
entry(4, "REC-SECOND-004", "同页第二条"), entry(null, "EVERY-PAGE", "每页")));
|
parameters.setWatermarkBusinessTypes(List.of("RECORD"));
|
for (List<DmsWatermarkSnapshot> snapshots : List.of(
|
resolver.resolve(user(), "tenant-1", version("manual.docx"), parameters),
|
resolver.resolveDownload(user(), "tenant-1", version("manual.docx"), parameters),
|
resolver.resolvePrint(user(), "tenant-1", version("manual.docx"), parameters))) {
|
assertEquals(5, snapshots.size());
|
assertEquals("REC-005", snapshots.get(0).getContent());
|
assertEquals("第五页", snapshots.get(0).getBarcodeText());
|
assertEquals(List.of(5), snapshots.get(0).getRenderPages());
|
assertEquals(Boolean.TRUE, snapshots.get(0).getShowBarcodeText());
|
assertEquals("记录 {/CUN} <&>", snapshots.get(1).getContent());
|
assertEquals("文字 {/CDN} <&>", snapshots.get(1).getBarcodeText());
|
assertEquals(List.of(4), snapshots.get(1).getRenderPages());
|
assertEquals("REC-SECOND-004", snapshots.get(2).getContent());
|
assertEquals(List.of(4), snapshots.get(2).getRenderPages());
|
org.junit.jupiter.api.Assertions.assertNull(snapshots.get(3).getRenderPages());
|
org.junit.jupiter.api.Assertions.assertNull(snapshots.get(4).getRenderPages());
|
assertTrue(snapshots.get(4).getContent().contains("张三"));
|
}
|
}
|
|
@Test
|
void frozenCopySerialUsesExistingCustomBarcodeSourceWithOrWithoutRequiredGuard() {
|
customAndOrdinaryConfigurations();
|
var request = new jnpf.dmsEntity.print.form.DmsFilePrintTicketRequest();
|
request.setCustomBarcode(List.of(entry(null, "20261003000001",
|
"打印流水号: 20261003000001 计数流水号: 20260001")));
|
request.setWatermarkBusinessTypes(List.of("RECORD"));
|
for (boolean required : List.of(false, true)) {
|
request.setRequireSerialWatermark(required);
|
var result = resolver.resolvePrint(user(), "tenant-1", version("manual.docx"), request);
|
assertEquals(2, result.size());
|
assertEquals(Boolean.TRUE, result.getFirst().getCustomBarcode());
|
assertEquals("20261003000001", result.getFirst().getContent());
|
assertEquals("打印流水号: 20261003000001 计数流水号: 20260001", result.getFirst().getBarcodeText());
|
org.junit.jupiter.api.Assertions.assertNull(result.getFirst().getRenderPages());
|
assertEquals(1, result.getFirst().getPositions().size());
|
assertEquals("TEXT", result.get(1).getWatermarkType());
|
}
|
}
|
|
@Test
|
void absentRuntimeTextSkipsCustomConfigurationAndPreservesOrdinaryWatermarks() {
|
customAndOrdinaryConfigurations();
|
List<DmsWatermarkSnapshot> snapshots = resolver.resolve(user(), "tenant-1", version("manual.docx"), types());
|
assertEquals(1, snapshots.size());
|
assertEquals("TEXT", snapshots.get(0).getWatermarkType());
|
}
|
|
@Test
|
void entryWithoutPageProducesEveryPageSnapshot() {
|
customAndOrdinaryConfigurations();
|
jnpf.dmsEntity.watermark.DmsWatermarkParameters parameters = new jnpf.dmsEntity.watermark.DmsWatermarkParameters();
|
parameters.setCustomBarcode(List.of(entry(null, "REC-001", "Independent label")));
|
parameters.setWatermarkBusinessTypes(List.of("RECORD"));
|
List<DmsWatermarkSnapshot> snapshots = resolver.resolve(user(), "tenant-1", version("manual.docx"), parameters);
|
assertEquals("REC-001", snapshots.get(0).getContent());
|
assertEquals("Independent label", snapshots.get(0).getBarcodeText());
|
org.junit.jupiter.api.Assertions.assertNull(snapshots.get(0).getRenderPages());
|
}
|
|
private jnpf.dmsEntity.watermark.DmsWatermarkParameters.CustomBarcode entry(Integer page, String value, String text) {
|
jnpf.dmsEntity.watermark.DmsWatermarkParameters.CustomBarcode entry = new jnpf.dmsEntity.watermark.DmsWatermarkParameters.CustomBarcode();
|
entry.setPage(page);
|
entry.setBarValue(value);
|
entry.setBarText(text);
|
return entry;
|
}
|
|
private DmsWatermarkParameters types() {
|
DmsWatermarkParameters parameters = new DmsWatermarkParameters();
|
parameters.setWatermarkBusinessTypes(List.of("RECORD"));
|
return parameters;
|
}
|
|
@Test
|
void emptyBusinessTypesDoNotQueryWatermarkConfigurations() {
|
assertTrue(resolver.resolve(user(), "tenant-1", version("manual.docx")).isEmpty());
|
DmsWatermarkParameters empty = new DmsWatermarkParameters();
|
empty.setWatermarkBusinessTypes(List.of());
|
assertTrue(resolver.resolveDownload(user(), "tenant-1", version("manual.docx"), empty).isEmpty());
|
verify(configMapper, never()).selectList(any());
|
}
|
|
@Test
|
void multipleBusinessTypesSelectEachConfigurationOnce() {
|
DictionaryDataEntity second = new DictionaryDataEntity();
|
second.setDictionaryTypeId("879384732964547653");
|
second.setEnCode("REPORT");
|
second.setEnabledMark(1);
|
DictionaryDataEntity first = new DictionaryDataEntity();
|
first.setDictionaryTypeId("879384732964547653");
|
first.setEnCode("RECORD");
|
first.setEnabledMark(1);
|
when(dictionaryDataApi.getList("879384732964547653", "true")).thenReturn(List.of(first, second));
|
DmsVersionStateAuditRow state = new DmsVersionStateAuditRow();
|
state.setLifecycleStatus("EFFECTIVE");
|
when(versionStateMapper.selectAuditState("tenant-1", VERSION_ID)).thenReturn(state);
|
when(configMapper.selectList(any())).thenReturn(List.of(config()));
|
when(positionMapper.selectList(any())).thenReturn(List.of(position()));
|
DmsWatermarkParameters parameters = new DmsWatermarkParameters();
|
parameters.setWatermarkBusinessTypes(List.of("RECORD", "REPORT"));
|
|
assertEquals(1, resolver.resolve(user(), "tenant-1", version("manual.docx"), parameters).size());
|
verify(configMapper).selectMatchingConfigIds(any(), any(), any());
|
}
|
|
private void customAndOrdinaryConfigurations() {
|
DmsVersionStateAuditRow state = new DmsVersionStateAuditRow();
|
state.setLifecycleStatus("EFFECTIVE");
|
when(versionStateMapper.selectAuditState("tenant-1", VERSION_ID)).thenReturn(state);
|
DmsWatermarkConfigEntity custom = config();
|
custom.setWatermarkType("BARCODE");
|
custom.setBarcodeFormat("QR_CODE");
|
custom.setShowBarcodeText(false);
|
custom.setContentTemplate(jnpf.dmsEntity.watermark.DmsWatermarkParameters.CUSTOM_BARCODE_TEMPLATE);
|
when(configMapper.selectList(any())).thenReturn(List.of(custom, config()));
|
when(positionMapper.selectList(any())).thenReturn(List.of(position()));
|
}
|
|
private UserInfo user() {
|
UserInfo user = new UserInfo();
|
user.setUserId("user-1");
|
user.setUserName("张三");
|
user.setOrganize("质量部");
|
user.setOrganizeIds(Collections.singletonList("department-1"));
|
return user;
|
}
|
|
private DmsFileVersionEntity version(String fileName) {
|
DmsFileVersionEntity version = new DmsFileVersionEntity();
|
version.setId(VERSION_ID);
|
version.setDocumentId(DOCUMENT_ID);
|
version.setFileName(fileName);
|
version.setVersionLabel("V1.0");
|
return version;
|
}
|
|
private DmsWatermarkConfigEntity config() {
|
DmsWatermarkConfigEntity config = new DmsWatermarkConfigEntity();
|
config.setId(CONFIG_ID);
|
config.setWatermarkType("TEXT");
|
config.setContentTemplate("{/CUN} / {/CDN} / {/FILENO} / {/VERSION} / {/FILESTATE}");
|
config.setFontFamily("SimHei");
|
config.setFontColor("#666666");
|
config.setFontSize(24);
|
config.setRotationAngle(-30);
|
config.setOpacity(30);
|
config.setLayoutMode("POSITION");
|
config.setMarkWidthMm(new BigDecimal("80"));
|
config.setMarkHeightMm(new BigDecimal("20"));
|
return config;
|
}
|
|
private DmsWatermarkPositionEntity position() {
|
DmsWatermarkPositionEntity position = new DmsWatermarkPositionEntity();
|
position.setConfigId(CONFIG_ID);
|
position.setPaperType("A4");
|
position.setPaperWidthMm(new BigDecimal("210"));
|
position.setPaperHeightMm(new BigDecimal("297"));
|
position.setPortraitXMm(new BigDecimal("65"));
|
position.setPortraitYMm(new BigDecimal("138"));
|
position.setLandscapeXMm(new BigDecimal("108"));
|
position.setLandscapeYMm(new BigDecimal("95"));
|
return position;
|
}
|
|
private void initTable(Class<?> type) {
|
if (TableInfoHelper.getTableInfo(type) == null) {
|
TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new Configuration(), ""), type);
|
}
|
}
|
}
|