package jnpf.limsService.impl;
|
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
|
import jnpf.audit.AuditConsts;
|
import jnpf.audit.model.AuditEventDTO;
|
import jnpf.audit.sdk.AuditClient;
|
import jnpf.base.ModuleApi;
|
import jnpf.limsEntity.print.LimsPrintAuditParam;
|
import jnpf.limsEntity.print.LimsPrintAuditTarget;
|
import jnpf.util.JsonUtil;
|
import org.springframework.beans.factory.support.StaticListableBeanFactory;
|
|
import java.util.Arrays;
|
import java.util.Collections;
|
import java.util.List;
|
import java.util.Map;
|
|
/** Lightweight checks for the one-click-to-one-print-event mapping. */
|
public class LimsPrintAuditServiceImplChecks {
|
|
public static void main(String[] args) {
|
checkCamelCaseDeserialization();
|
checkSinglePrint();
|
checkBatchPrintAndDeduplication();
|
checkBatchRecordsOnce();
|
checkOptionalTemplate();
|
checkMenuMappingAndFallback();
|
checkInvalidInputs();
|
}
|
|
private static void checkCamelCaseDeserialization() {
|
String json = "{\"requestId\":\"123e4567e89b12d3a456426614174000\","
|
+ "\"printName\":\"仪器设备标签\",\"templateId\":\"template-1\","
|
+ "\"recordTitle\":\"仪器设备标签\",\"targetTable\":\"lims_shebei_yiqi_xinxi\","
|
+ "\"bizType\":\"DEVICE_LABEL\",\"source\":\"LIST_PRINT\","
|
+ "\"targets\":[{\"id\":\"record-1\",\"bizCode\":\"SB-001\",\"title\":\"设备一\"}]}";
|
ObjectMapper mapper = new ObjectMapper();
|
mapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
|
try {
|
LimsPrintAuditParam param = mapper.readValue(json, LimsPrintAuditParam.class);
|
require("123e4567e89b12d3a456426614174000".equals(param.getRequestId()),
|
"camelCase requestId must override the global snake-case strategy");
|
require("仪器设备标签".equals(param.getPrintName()), "camelCase printName must be deserialized");
|
require("lims_shebei_yiqi_xinxi".equals(param.getTargetTable()),
|
"camelCase targetTable must be deserialized");
|
require(param.getTargets() != null && param.getTargets().size() == 1,
|
"camelCase targets must be deserialized");
|
require("SB-001".equals(param.getTargets().get(0).getBizCode()),
|
"nested camelCase bizCode must be deserialized");
|
} catch (Exception e) {
|
throw new AssertionError("camelCase print audit payload must be deserialized", e);
|
}
|
}
|
|
private static void checkSinglePrint() {
|
LimsPrintAuditParam param = baseParam();
|
param.setTargets(Collections.singletonList(target("record-1", "BG-001", "检验报告 BG-001")));
|
|
AuditEventDTO event = LimsPrintAuditServiceImpl.buildEvent(param);
|
Map<String, Object> extra = extra(event);
|
|
require(AuditConsts.TYPE_PRINT.equals(event.getEventType()), "event type must be PRINT");
|
require("PRINT".equals(event.getActionCode()), "action code must be PRINT");
|
require("发起打印".equals(event.getActionLabel()), "action label must describe initiation");
|
require(Integer.valueOf(3).equals(event.getSourceLayer()), "explicit print events must use layer 3");
|
require("PRINT-123e4567e89b12d3a456426614174000".equals(event.getClientEventId()),
|
"request UUID must produce a stable client event id");
|
require(event.getClientEventId().equals(event.getOperationId()), "operation id must correlate the same click");
|
require("record-1".equals(event.getTargetId()), "single print must expose its target id");
|
require("BG-001".equals(event.getBizCode()), "single print must expose its business code");
|
require("SAMPLE_REPORT".equals(event.getBizType()), "frontend bizType must be retained");
|
require("SINGLE".equals(extra.get("printMode")), "single print mode is incorrect");
|
require(Integer.valueOf(1).equals(extra.get("printCount")), "single print count is incorrect");
|
}
|
|
private static void checkBatchPrintAndDeduplication() {
|
LimsPrintAuditParam param = baseParam();
|
param.setRecordTitle("检验报告");
|
param.setTargets(Arrays.asList(
|
target("record-1", "BG-001", "第一份"),
|
target("record-2", null, "第二份"),
|
target("record-1", "SHOULD-NOT-REPLACE", "重复项")));
|
|
AuditEventDTO event = LimsPrintAuditServiceImpl.buildEvent(param);
|
Map<String, Object> extra = extra(event);
|
List<?> targets = (List<?>) extra.get("printTargets");
|
List<?> subTitles = (List<?>) extra.get("recordSubTitles");
|
|
require(event.getTargetId() == null, "batch print must not impersonate the first target");
|
require(event.getBizCode() == null, "batch print must not expose one target's business code");
|
require("检验报告(批量打印 2 条)".equals(event.getRecordTitle()), "batch title must use the deduplicated count");
|
require("BATCH".equals(extra.get("printMode")), "batch print mode is incorrect");
|
require(Integer.valueOf(2).equals(extra.get("printCount")), "duplicate ids must be counted once");
|
require(targets.size() == 2, "batch event must contain all deduplicated targets in one DTO");
|
require(Arrays.asList("BG-001", "第二份").equals(subTitles), "target summaries must preserve stable order");
|
}
|
|
private static void checkOptionalTemplate() {
|
LimsPrintAuditParam param = baseParam();
|
param.setTemplateId(null);
|
param.setBizType(null);
|
param.setTargets(Collections.singletonList(target("record-1", null, null)));
|
|
AuditEventDTO event = LimsPrintAuditServiceImpl.buildEvent(param);
|
Map<String, Object> extra = extra(event);
|
|
require(!extra.containsKey("templateId"), "custom print must not require a template id");
|
require("LIMS_PRINT".equals(event.getBizType()), "missing bizType must use the print fallback");
|
}
|
|
private static void checkBatchRecordsOnce() {
|
CountingAuditClient client = new CountingAuditClient();
|
StaticListableBeanFactory factory = new StaticListableBeanFactory();
|
factory.addBean("auditClient", client);
|
LimsPrintAuditServiceImpl service = new LimsPrintAuditServiceImpl(
|
factory.getBeanProvider(AuditClient.class), factory.getBeanProvider(ModuleApi.class));
|
LimsPrintAuditParam param = baseParam();
|
param.setTargets(Arrays.asList(
|
target("record-1", "BG-001", null),
|
target("record-2", "BG-002", null)));
|
|
service.record(param);
|
|
require(client.recordCount == 1, "one batch click must invoke AuditClient.record exactly once");
|
require(client.event != null && "BATCH".equals(extra(client.event).get("printMode")),
|
"the single submitted event must contain the complete batch");
|
}
|
|
private static void checkMenuMappingAndFallback() {
|
LimsPrintAuditParam param = baseParam();
|
param.setTargets(Collections.singletonList(target("record-1", null, null)));
|
|
AuditEventDTO resolved = LimsPrintAuditServiceImpl.buildEvent(
|
param, LimsPrintAuditServiceImpl.MenuContext.resolved("menu-1", "检验报告菜单"));
|
Map<String, Object> resolvedExtra = extra(resolved);
|
require("检验报告菜单".equals(resolved.getBizModule()), "resolved menu must become the business module");
|
require("MENU".equals(resolved.getEntryType()), "resolved menu must set entry type");
|
require("menu-1".equals(resolved.getEntryId()), "resolved menu must retain entry id");
|
require("检验报告菜单".equals(resolved.getEntryName()), "resolved menu must retain entry name");
|
require("RESOLVED".equals(resolvedExtra.get("menuResolution")), "menu resolution status is incorrect");
|
|
AuditEventDTO unresolved = LimsPrintAuditServiceImpl.buildEvent(
|
param, LimsPrintAuditServiceImpl.MenuContext.unresolved("menu-2"));
|
require("表单详情打印".equals(unresolved.getBizModule()), "unresolved menu must use the source label fallback");
|
require("MENU".equals(unresolved.getEntryType()), "an unresolved header must still identify a menu entry");
|
require("menu-2".equals(unresolved.getEntryId()), "an unresolved header must retain its menu id");
|
require(unresolved.getEntryName() == null, "unresolved menu must not invent an entry name");
|
|
AuditEventDTO missing = LimsPrintAuditServiceImpl.buildEvent(param);
|
require("表单详情打印".equals(missing.getBizModule()), "missing menu must use the source label fallback");
|
require(missing.getEntryType() == null && missing.getEntryId() == null, "missing menu must leave entry fields empty");
|
}
|
|
private static void checkInvalidInputs() {
|
LimsPrintAuditParam invalidUuid = baseParam();
|
invalidUuid.setRequestId("not-a-uuid");
|
invalidUuid.setTargets(Collections.singletonList(target("record-1", null, null)));
|
requireThrows(() -> LimsPrintAuditServiceImpl.buildEvent(invalidUuid), "invalid UUID must be rejected");
|
|
LimsPrintAuditParam invalidTable = baseParam();
|
invalidTable.setTargetTable("lims_report;drop table audit_events");
|
invalidTable.setTargets(Collections.singletonList(target("record-1", null, null)));
|
requireThrows(() -> LimsPrintAuditServiceImpl.buildEvent(invalidTable), "invalid table name must be rejected");
|
}
|
|
private static LimsPrintAuditParam baseParam() {
|
LimsPrintAuditParam param = new LimsPrintAuditParam();
|
param.setRequestId("123e4567-e89b-12d3-a456-426614174000");
|
param.setPrintName("检验报告");
|
param.setTemplateId("report-template");
|
param.setTargetTable("lims_sample_report");
|
param.setBizType("SAMPLE_REPORT");
|
param.setSource("REPORT_VIEW");
|
return param;
|
}
|
|
private static LimsPrintAuditTarget target(String id, String bizCode, String title) {
|
LimsPrintAuditTarget target = new LimsPrintAuditTarget();
|
target.setId(id);
|
target.setBizCode(bizCode);
|
target.setTitle(title);
|
return target;
|
}
|
|
@SuppressWarnings("unchecked")
|
private static Map<String, Object> extra(AuditEventDTO event) {
|
return JsonUtil.getJsonToBean(event.getExtra(), Map.class);
|
}
|
|
private static void requireThrows(Runnable action, String message) {
|
try {
|
action.run();
|
throw new AssertionError(message);
|
} catch (IllegalArgumentException ignored) {
|
// Expected validation failure.
|
}
|
}
|
|
private static void require(boolean condition, String message) {
|
if (!condition) {
|
throw new AssertionError(message);
|
}
|
}
|
|
private static final class CountingAuditClient extends AuditClient {
|
private AuditEventDTO event;
|
private int recordCount;
|
|
private CountingAuditClient() {
|
super(null, null, false);
|
}
|
|
@Override
|
public void record(AuditEventDTO dto) {
|
recordCount++;
|
event = dto;
|
}
|
}
|
}
|