package jnpf.elnService.impl; import jnpf.elnEntity.runtime.ElnRecordEntity; import jnpf.elnEntity.runtime.ElnRecordFieldValueEntity; import jnpf.elnEntity.runtime.ElnRecordForms; import jnpf.elnEntity.runtime.ElnRecordRevisionEntity; import jnpf.elnEntity.runtime.ElnRecordVos; import jnpf.elnEntity.ElnDocumentFileVo; import jnpf.elnEntity.field.ElnFieldVersionEntity; import jnpf.elnEntity.template.ElnTemplateBindingEntity; import jnpf.elnEntity.template.ElnTemplateVersionEntity; import jnpf.elnMapper.ElnTemplateMapper; import jnpf.elnMapper.ElnFieldVersionMapper; import jnpf.elnMapper.ElnRecordFieldValueMapper; import jnpf.elnMapper.ElnRecordMapper; import jnpf.elnMapper.ElnRecordRevisionMapper; import jnpf.elnMapper.ElnTemplateBindingMapper; import jnpf.elnService.ElnTestWorkItemSnapshotProvider; import jnpf.elnService.ElnCrossTemplateSnapshotResolver; import jnpf.elnService.ElnRecordFormulaCalculationService; import jnpf.elnService.template.ElnTemplateFileService; import jnpf.exception.DataException; import jnpf.lims.api.model.TestWorkItemSnapshotBatch; import jnpf.lims.api.model.TestWorkItemSnapshot; import jnpf.lims.api.model.TestWorkItemSnapshotProperty; import jnpf.lims.api.model.ElnSourceSnapshot; import jnpf.lims.api.model.ElnSourcePropertyRef; import org.junit.Test; import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; public class ElnRecordCreationServiceImplTest { @Test public void createsSnapshotFromPinnedSourceRevisionDespiteANewerPublishedRevision() { Fixture fixture = new Fixture(); fixture.versions.get(0).setBangdingPeizhi( "{\"read\":{\"source\":\"LIMS_TASK\",\"property\":\"test.basis\",\"propertyRevision\":1}}"); fixture.versions.get(0).setShujuLeixing("TEXT"); fixture.configuredValues.put("test.basis@1", "CONFIGURED-V1"); fixture.configuredValues.put("test.basis@2", "CONFIGURED-V2"); fixture.service.create(command()); ElnRecordFieldValueEntity value = fixture.fieldValues.get(0); assertEquals("CONFIGURED-V1", value.getZhiWenben()); assertEquals(Integer.valueOf(1), value.getYuanShuxingBanben()); assertEquals("SUCCESS", value.getYuanQuzhiZhuangtai()); assertEquals("task.details@1/basis", value.getYuanXueyuan()); assertEquals(java.util.Date.from(java.time.Instant.parse("2026-09-06T02:00:00Z")), value.getYuanHuoquShijian()); } @Test public void createsExclusiveDocumentAndInitialFieldSnapshots() { Fixture fixture = new Fixture(); ElnRecordVos.Created created = fixture.service.create(command()); assertFalse(created.isExisting()); assertNotNull(created.getRecordId()); assertNotNull(created.getRevisionId()); assertEquals(created.getRevisionId() + ".docx", created.getDocument().getFileId()); assertEquals(1, fixture.copyCalls.get()); assertEquals(created.getRevisionId(), fixture.copyTarget.get()); assertEquals(1, fixture.recordInsertCalls.get()); assertEquals(1, fixture.revisionInsertCalls.get()); assertEquals(1, fixture.calculationCalls.get()); assertEquals("tenant-1", fixture.snapshotTenant.get()); assertEquals("user-1", fixture.snapshotUser.get()); assertEquals("HD_jyxm_0008", fixture.templateLookupProjectCode.get()); assertEquals(created.getDocument().getFileId(), fixture.revision.get().getJiluWendang()); assertFalse(fixture.revision.get().getJiluWendang().startsWith("[")); assertEquals(created.getRevisionId(), fixture.record.get().getDangqianXiudingId()); assertEquals(2, fixture.fieldValues.size()); ElnRecordFieldValueEntity lims = fixture.fieldValues.get(0); assertEquals("LIMS_TASK", lims.getZhiLaiyuan()); assertEquals("GB/T 123", lims.getZhiWenben()); assertEquals("basis-1", lims.getYuanYinyongId()); ElnRecordFieldValueEntity system = fixture.fieldValues.get(1); assertEquals("SYSTEM", system.getZhiLaiyuan()); assertEquals("测试员", system.getZhiWenben()); assertEquals("user-1", system.getYuanYinyongId()); } @Test public void preservesConfiguredDecimalPrecisionInTheTypedSnapshot() { Fixture fixture = new Fixture(); fixture.versions.get(0).setBangdingPeizhi( "{\"read\":{\"source\":\"LIMS_TASK\",\"property\":\"result.amount\",\"propertyRevision\":1}}"); fixture.versions.get(0).setShujuLeixing("DECIMAL"); fixture.configuredValues.put("result.amount@1", "123456789012345.123456789012345"); fixture.configuredResponse = value -> value.setDataType("DECIMAL"); fixture.service.create(command()); assertEquals(new java.math.BigDecimal("123456789012345.123456789012345"), fixture.fieldValues.get(0).getZhiShuzhi()); } @Test public void rejectsNumbersThatWouldBeRoundedOrOverflowTheSnapshotColumn() { for (String input : Arrays.asList("0.1234567890123456789", "100000000000000000000", "1e100000")) { Fixture fixture = configuredFixture(); fixture.versions.get(0).setShujuLeixing("DECIMAL"); fixture.configuredValues.put("test.basis@1", input); fixture.configuredResponse = value -> value.setDataType("DECIMAL"); try { fixture.service.create(command()); fail("Unrepresentable numeric snapshot must be rejected"); } catch (DataException expected) { fixture.assertNoWrites(); } } } @Test public void textSourcesCanFillCompatibleTextControls() { for (String type : Arrays.asList("TEXTAREA", "SELECT")) { Fixture fixture = configuredFixture(); fixture.versions.get(0).setShujuLeixing(type); fixture.service.create(command()); assertEquals("CONFIGURED-V1", fixture.fieldValues.get(0).getZhiWenben()); } } @Test public void recordsOptionalMissingAndNotApplicableWithoutInventingAValue() { for (String outcome : Arrays.asList("MISSING", "NOT_APPLICABLE")) { Fixture fixture = configuredFixture(); fixture.configuredResponse = value -> { value.setValue(null); value.setOutcomeCode(outcome); value.setReasonCode("MISSING".equals(outcome) ? "NAME_MISSING" : null); }; fixture.service.create(command()); ElnRecordFieldValueEntity value = fixture.fieldValues.get(0); assertEquals(null, value.getZhiWenben()); assertEquals(outcome, value.getYuanQuzhiZhuangtai()); assertEquals(Integer.valueOf(1), value.getYuanShuxingBanben()); } } private static Fixture configuredFixture() { Fixture fixture = new Fixture(); fixture.versions.get(0).setBangdingPeizhi( "{\"read\":{\"source\":\"LIMS_TASK\",\"property\":\"test.basis\",\"propertyRevision\":1}}"); fixture.versions.get(0).setShujuLeixing("TEXT"); fixture.configuredValues.put("test.basis@1", "CONFIGURED-V1"); return fixture; } @Test public void rejectsRequiredMissingAndSourceFaultsBeforeCreatingAnything() { for (String outcome : Arrays.asList("MISSING", "NOT_APPLICABLE", "SOURCE_INVALID", "SOURCE_FORBIDDEN", "SOURCE_AMBIGUOUS")) { Fixture fixture = configuredFixture(); if ("MISSING".equals(outcome) || "NOT_APPLICABLE".equals(outcome)) fixture.versions.get(0).setShifouBitian(1); fixture.configuredResponse = value -> value.setOutcomeCode(outcome); try { fixture.service.create(command()); fail("Source failure must prevent creation: " + outcome); } catch (DataException expected) { fixture.assertNoWrites(); } } } @Test public void rejectsWrongTaskVersionAndTypeInsteadOfUsingLegacyValues() { List> invalid = Arrays.asList( value -> value.setSourceRef("another-task"), value -> value.setPropertyRevision(2), value -> value.setDataType("DECIMAL"), value -> value.setAcquiredAt("invalid")); for (java.util.function.Consumer response : invalid) { Fixture fixture = configuredFixture(); fixture.configuredResponse = response; try { fixture.service.create(command()); fail("Invalid source response must be rejected"); } catch (DataException expected) { fixture.assertNoWrites(); } } } @Test public void storesConfiguredDateInTheDateSlot() { Fixture fixture = configuredFixture(); fixture.versions.get(0).setShujuLeixing("DATE"); fixture.configuredValues.put("test.basis@1", "2026-09-06"); fixture.configuredResponse = value -> value.setDataType("DATE"); fixture.service.create(command()); assertEquals(java.sql.Date.valueOf("2026-09-06"), fixture.fieldValues.get(0).getZhiRiqi()); } @Test public void storesConfiguredDateTimeAndBooleanUsingDeclaredTypes() { Fixture time = configuredFixture(); time.versions.get(0).setShujuLeixing("DATETIME"); time.configuredValues.put("test.basis@1", "2026-09-06T10:30:00+08:00"); time.configuredResponse = value -> value.setDataType("DATETIME"); time.service.create(command()); assertEquals(java.util.Date.from(java.time.Instant.parse("2026-09-06T02:30:00Z")), time.fieldValues.get(0).getZhiRiqi()); Fixture checkbox = configuredFixture(); checkbox.versions.get(0).setShujuLeixing("CHECKBOX"); checkbox.configuredValues.put("test.basis@1", "false"); checkbox.configuredResponse = value -> value.setDataType("BOOLEAN"); checkbox.service.create(command()); assertEquals("false", checkbox.fieldValues.get(0).getZhiWenben()); } @Test public void acceptsPostgresLocalTimestampWithoutInventingAnOffset() { Fixture fixture = configuredFixture(); fixture.versions.get(0).setShujuLeixing("DATETIME"); fixture.configuredValues.put("test.basis@1", "2026-09-06T10:30:00"); fixture.configuredResponse = value -> value.setDataType("DATETIME"); fixture.service.create(command()); assertEquals(java.sql.Timestamp.valueOf("2026-09-06 10:30:00"), fixture.fieldValues.get(0).getZhiRiqi()); } @Test public void rejectsSubsecondSourceTimesInsteadOfRoundingTheSnapshot() { for (String input : Arrays.asList("2026-09-06T10:30:00.123456", "2026-09-06T10:30:00.123456+08:00")) { Fixture fixture = configuredFixture(); fixture.versions.get(0).setShujuLeixing("DATETIME"); fixture.configuredValues.put("test.basis@1", input); fixture.configuredResponse = value -> value.setDataType("DATETIME"); try { fixture.service.create(command()); fail("Subsecond snapshot must not be silently rounded"); } catch (DataException expected) { fixture.assertNoWrites(); } } } @Test public void requestsEveryLimsPropertyUsedByActiveTemplateBindings() { Fixture fixture = new Fixture(); fixture.bindings.add(binding("binding-3", "field-3", "sample_name", "version-3")); fixture.versions.add(version("version-3", "field-3", "{\"read\":{\"source\":\"LIMS_TASK\",\"property\":\"sample.name\"}}")); fixture.service.create(command()); assertEquals(Arrays.asList( Collections.emptyList(), Arrays.asList("test.basis", "sample.name")), fixture.snapshotPropertyRequests); assertEquals(3, fixture.fieldValues.size()); assertEquals("sample.name", fixture.fieldValues.get(2).getYuanShuxing()); assertEquals("阿司匹林片", fixture.fieldValues.get(2).getZhiWenben()); } @Test public void createsEmptyValueWhenOptionalLimsPropertyIsMissing() { Fixture fixture = new Fixture(); fixture.missingProperty = "test.basis"; fixture.service.create(command()); assertEquals(2, fixture.fieldValues.size()); ElnRecordFieldValueEntity optional = fixture.fieldValues.get(0); assertEquals("LIMS_TASK", optional.getZhiLaiyuan()); assertEquals("test.basis", optional.getYuanShuxing()); assertEquals(null, optional.getZhiWenben()); } @Test public void rejectsMissingRequiredLimsPropertyBeforePersistingOrCopying() { Fixture fixture = new Fixture(); fixture.versions.get(0).setShifouBitian(1); fixture.missingProperty = "test.basis"; expectDataException(() -> fixture.service.create(command()), "检验任务快照中缺少检验依据"); fixture.assertNoWrites(); } @Test public void returnsExistingDraftWithoutCopyingOrInsertingAgain() { Fixture fixture = new Fixture(); ElnRecordVos.Created first = fixture.service.create(command()); ElnRecordVos.Created second = fixture.service.create(command()); assertTrue(second.isExisting()); assertEquals(first.getRecordId(), second.getRecordId()); assertEquals(first.getRevisionId(), second.getRevisionId()); assertEquals(first.getDocument().getFileId(), second.getDocument().getFileId()); assertEquals(1, fixture.copyCalls.get()); assertEquals(1, fixture.recordInsertCalls.get()); assertEquals(1, fixture.revisionInsertCalls.get()); assertEquals(2, fixture.fieldValues.size()); } @Test public void rejectsMissingTemplateBeforePersistingOrCopying() { Fixture fixture = new Fixture(); fixture.templates.clear(); expectDataException(() -> fixture.service.create(command()), "未配置"); fixture.assertNoWrites(); } @Test public void rejectsAmbiguousTemplatesBeforePersistingOrCopying() { Fixture fixture = new Fixture(); fixture.templates.add(template("template-2")); expectDataException(() -> fixture.service.create(command()), "多个"); fixture.assertNoWrites(); } @Test public void rejectsSnapshotWithoutTestDefinitionCode() { Fixture fixture = new Fixture(); fixture.testDefinitionCode = " "; expectDataException(() -> fixture.service.create(command()), "检验项目编码"); fixture.assertNoWrites(); } @Test public void rejectsTemplateWithMismatchedTestDefinitionCode() { Fixture fixture = new Fixture(); fixture.templates.get(0).setJianyanXiangmuBianma("HD_jyxm_9999"); expectDataException(() -> fixture.service.create(command()), "编码与任务快照不一致"); fixture.assertNoWrites(); } @Test public void createsCrossTemplateScalarSnapshotWithCompleteLineageBeforePersistence() { ElnRecordFieldValueEntity source = new ElnRecordFieldValueEntity(); source.setId("source-value-1"); source.setDianziJiluId("source-record-1"); source.setXiudingId("source-revision-1"); source.setZiduanBanbenId("source-version-1"); source.setZhiWenben("cross template result"); Fixture fixture = new Fixture((tenantId, testWorkItemId, bindings) -> { assertEquals("tenant-1", tenantId); assertEquals("test-work-item-1", testWorkItemId); Map result = new LinkedHashMap<>(); result.put("binding-1", source); return result; }); fixture.service.create(command()); ElnRecordFieldValueEntity target = fixture.fieldValues.get(0); assertEquals("cross template result", target.getZhiWenben()); assertEquals("CROSS_TEMPLATE", target.getZhiLaiyuan()); assertEquals("source-record-1", target.getKuamoLaiyuanJiluId()); assertEquals("source-revision-1", target.getKuamoLaiyuanXiudingId()); assertEquals("source-value-1", target.getKuamoLaiyuanZhiId()); assertEquals("source-version-1", target.getKuamoLaiyuanZiduanBanbenId()); assertNotNull(target.getKuamoHuoquShijian()); } @Test public void rejectsCrossTemplateResolutionBeforeAnyDatabaseOrDocumentWrite() { Fixture fixture = new Fixture((tenantId, testWorkItemId, bindings) -> { throw new DataException("SOURCE_FIELD_VALUE_INVALID: source empty"); }); expectDataException(() -> fixture.service.create(command()), "SOURCE_FIELD_VALUE_INVALID"); fixture.assertNoWrites(); } private static ElnRecordForms.Create command() { ElnRecordForms.Create command = new ElnRecordForms.Create(); command.setTestWorkItemId("test-work-item-1"); command.setOperationCode("eln-single-test-work-item-1-1"); return command; } private static void expectDataException(Runnable action, String messagePart) { try { action.run(); fail("预期抛出 DataException"); } catch (DataException expected) { assertTrue(expected.getMessage().contains(messagePart)); } } private static ElnTemplateVersionEntity template(String id) { ElnTemplateVersionEntity template = new ElnTemplateVersionEntity(); template.setId(id); template.setFTenantId("tenant-1"); template.setMubanBianhao("TPL-001"); template.setMubanBanben("V1"); template.setJianyanXiangmuBianma("HD_jyxm_0008"); template.setMubanFile("[{\"fileId\":\"source.docx\",\"name\":\"source.docx\"," + "\"fileExtension\":\"docx\"}]"); return template; } private static ElnTemplateBindingEntity binding(String id, String fieldId, String fieldCode, String versionId) { ElnTemplateBindingEntity binding = new ElnTemplateBindingEntity(); binding.setId(id); binding.setMubanId("template-1"); binding.setZiduanDingyiId(fieldId); binding.setZiduanBianma(fieldCode); binding.setZiduanBanbenId(versionId); binding.setKongjianJuese("PRIMARY"); binding.setBangdingZhuangtai("ACTIVE"); binding.setFTenantId("tenant-1"); return binding; } private static ElnFieldVersionEntity version(String id, String fieldId, String bindingConfig) { ElnFieldVersionEntity version = new ElnFieldVersionEntity(); version.setId(id); version.setZiduanDingyiId(fieldId); version.setShifouBitian(0); version.setBangdingPeizhi(bindingConfig); version.setFTenantId("tenant-1"); return version; } @SuppressWarnings("unchecked") private static T mapper(Class type, MapperInvocation invocation) { return (T) Proxy.newProxyInstance(type.getClassLoader(), new Class[]{type}, (proxy, method, args) -> { if (method.getDeclaringClass() == Object.class) { if ("toString".equals(method.getName())) { return type.getSimpleName() + "Fake"; } if ("hashCode".equals(method.getName())) { return System.identityHashCode(proxy); } if ("equals".equals(method.getName())) { return proxy == args[0]; } } Object result = invocation.invoke(method.getName(), args); if (result != null || !method.getReturnType().isPrimitive()) { return result; } if (method.getReturnType() == boolean.class) { return false; } return 0; }); } @FunctionalInterface private interface MapperInvocation { Object invoke(String method, Object[] args); } private static final class Fixture { private final List templates = new ArrayList<>(); private final List bindings = new ArrayList<>(); private final List versions = new ArrayList<>(); private final List fieldValues = new ArrayList<>(); private final AtomicReference record = new AtomicReference<>(); private final AtomicReference revision = new AtomicReference<>(); private final AtomicReference copyTarget = new AtomicReference<>(); private final AtomicReference snapshotTenant = new AtomicReference<>(); private final AtomicReference snapshotUser = new AtomicReference<>(); private final AtomicReference templateLookupProjectCode = new AtomicReference<>(); private final List> snapshotPropertyRequests = new ArrayList<>(); private final AtomicInteger versionReads = new AtomicInteger(); private final AtomicInteger copyCalls = new AtomicInteger(); private final AtomicInteger recordInsertCalls = new AtomicInteger(); private final AtomicInteger revisionInsertCalls = new AtomicInteger(); private final AtomicInteger calculationCalls = new AtomicInteger(); private String testDefinitionCode = "HD_jyxm_0008"; private String missingProperty; private final Map configuredValues = new LinkedHashMap<>(); private java.util.function.Consumer configuredResponse = value -> { }; private final ElnRecordCreationServiceImpl service; private Fixture() { this(ElnCrossTemplateSnapshotResolver.none()); } private Fixture(ElnCrossTemplateSnapshotResolver crossTemplateSnapshotResolver) { templates.add(template("template-1")); bindings.add(binding("binding-1", "field-1", "test_basis", "version-1")); bindings.add(binding("binding-2", "field-2", "operator_name", "version-2")); versions.add(version("version-1", "field-1", "{\"read\":{\"source\":\"LIMS_TASK\",\"property\":\"test.basis\"}}")); versions.add(version("version-2", "field-2", "{\"read\":{\"source\":\"SYSTEM\",\"property\":\"current_user.real_name\"}}")); ElnTemplateMapper templateMapper = mapper(ElnTemplateMapper.class, (method, args) -> { if ("selectCurrentPublishedByTestDefinition".equals(method)) { templateLookupProjectCode.set((String) args[1]); return new ArrayList<>(templates); } return null; }); ElnTemplateBindingMapper bindingMapper = mapper(ElnTemplateBindingMapper.class, (method, args) -> "selectList".equals(method) ? new ArrayList<>(bindings) : null); ElnFieldVersionMapper fieldVersionMapper = mapper(ElnFieldVersionMapper.class, (method, args) -> "selectOne".equals(method) ? versions.get(versionReads.getAndIncrement()) : null); ElnRecordMapper recordMapper = mapper(ElnRecordMapper.class, (method, args) -> { if ("selectLiveByTestWorkItem".equals(method)) { return record.get(); } if ("insertIfAbsent".equals(method)) { recordInsertCalls.incrementAndGet(); record.set((ElnRecordEntity) args[0]); return 1; } if ("updateById".equals(method)) { record.set((ElnRecordEntity) args[0]); return 1; } return null; }); ElnRecordRevisionMapper revisionMapper = mapper(ElnRecordRevisionMapper.class, (method, args) -> { if ("selectOne".equals(method)) { return revision.get(); } if ("insert".equals(method)) { revisionInsertCalls.incrementAndGet(); revision.set((ElnRecordRevisionEntity) args[0]); return 1; } return null; }); ElnRecordFieldValueMapper fieldValueMapper = mapper(ElnRecordFieldValueMapper.class, (method, args) -> { if ("insert".equals(method)) { fieldValues.add((ElnRecordFieldValueEntity) args[0]); return 1; } return null; }); ElnTemplateFileService fileService = new ElnTemplateFileService(null, null) { @Override public String copyDocx(String sourceAttachments, String targetTemplateId) { copyCalls.incrementAndGet(); copyTarget.set(targetTemplateId); return "[{\"fileId\":\"" + targetTemplateId + ".docx\"," + "\"name\":\"" + targetTemplateId + ".docx\"," + "\"fileExtension\":\"docx\",\"fileSize\":128}]"; } }; ElnRecordFormulaCalculationService formulaCalculation = new ElnRecordFormulaCalculationService() { @Override public List recalculateAffected( String tenantId, ElnRecordEntity record, ElnRecordRevisionEntity revision, List values, Set changedBindingIds, String operationCode, String operatorUserId) { calculationCalls.incrementAndGet(); return Collections.emptyList(); } @Override public List recalculateAll( String tenantId, ElnRecordEntity record, ElnRecordRevisionEntity revision, List values, String operationCode, String operatorUserId) { return Collections.emptyList(); } }; ElnTestWorkItemSnapshotProvider taskProvider = new ElnTestWorkItemSnapshotProvider() { @Override public List resolveConfiguredAsActor(String tenantId, String userId, List testWorkItemIds, List references) { List result = new ArrayList<>(); for (ElnSourcePropertyRef reference : references) { ElnSourceSnapshot item = new ElnSourceSnapshot(); item.setSourceRef(testWorkItemIds.get(0)); item.setPropertyCode(reference.getPropertyCode()); item.setPropertyRevision(reference.getRevision()); item.setDataType("TEXT"); item.setValue(configuredValues.get(reference.getPropertyCode() + "@" + reference.getRevision())); item.setOutcomeCode("SUCCESS"); item.setAcquiredAt("2026-09-06T02:00:00Z"); item.setLineage("task.details@1/basis"); configuredResponse.accept(item); result.add(item); } return result; } @Override public TestWorkItemSnapshotBatch resolve( List testWorkItemIds, List properties) { fail("记录创建必须通过显式 actor 快照接口读取 LIMS 任务"); return null; } @Override public TestWorkItemSnapshotBatch resolveAsActor( String tenantId, String userId, List testWorkItemIds, List properties) { snapshotTenant.set(tenantId); snapshotUser.set(userId); snapshotPropertyRequests.add(new ArrayList<>(properties)); return snapshot(properties); } }; service = new ElnRecordCreationServiceImpl(taskProvider, templateMapper, bindingMapper, fieldVersionMapper, recordMapper, revisionMapper, fieldValueMapper, fileService, formulaCalculation, command -> { ElnRecordVos.Created existing = new ElnRecordVos.Created(); existing.setRecordId(record.get().getId()); existing.setRevisionId(revision.get().getId()); ElnDocumentFileVo document = new ElnDocumentFileVo(); document.setFileId(revision.get().getJiluWendang()); document.setName(revision.get().getJiluWendang()); document.setFileExtension("docx"); existing.setDocument(document); existing.setExisting(true); return existing; }, () -> ElnRecordCreationServiceImpl.userContext( "tenant-1", "user-1", "测试员"), crossTemplateSnapshotResolver); } private void assertNoWrites() { assertEquals(0, copyCalls.get()); assertEquals(0, recordInsertCalls.get()); assertEquals(0, revisionInsertCalls.get()); assertTrue(fieldValues.isEmpty()); } private TestWorkItemSnapshotBatch snapshot(List requestedProperties) { List properties = new ArrayList<>(); for (String requested : requestedProperties) { TestWorkItemSnapshotProperty property = new TestWorkItemSnapshotProperty(); property.setProperty(requested); property.setSource("LIMS_TASK"); property.setSourceRef("sample.name".equals(requested) ? "sample-1" : "basis-1"); if (requested.equals(missingProperty)) { property.setOutcomeCode("MISSING"); property.setReason("检验任务快照中缺少检验依据"); } else { property.setValue("sample.name".equals(requested) ? "阿司匹林片" : "GB/T 123"); property.setOutcomeCode("SUCCESS"); } properties.add(property); } TestWorkItemSnapshot item = new TestWorkItemSnapshot(); item.setTestWorkItemId("test-work-item-1"); item.setTestDefinitionId("test-definition-1"); item.setTestDefinitionCode(testDefinitionCode); item.setTestDefinitionName("pH值"); item.setOutcomeCode("SUCCESS"); item.setProperties(properties); TestWorkItemSnapshotBatch batch = new TestWorkItemSnapshotBatch(); batch.setItems(Arrays.asList(item)); return batch; } } }