package jnpf.elnService.impl; import jnpf.base.ActionResult; import jnpf.eln.RecordFreezeCheckBatch; import jnpf.eln.RecordFreezeCheckRequest; import jnpf.eln.RecordFreezeCheckResult; import jnpf.eln.RecordFreezeReleaseRequest; import jnpf.elnEntity.field.ElnFieldVersionEntity; import jnpf.elnEntity.formula.ElnTemplateFormulaBindingEntity; import jnpf.elnEntity.runtime.ElnRecordEntity; import jnpf.elnEntity.runtime.ElnRecordFieldValueEntity; import jnpf.elnEntity.runtime.ElnRecordFormulaCalculationResult; import jnpf.elnEntity.runtime.ElnRecordRepeatRowEntity; import jnpf.elnEntity.runtime.ElnRecordRepeatValueEntity; import jnpf.elnEntity.runtime.ElnRecordRevisionEntity; import jnpf.elnEntity.template.ElnTemplateBindingEntity; import jnpf.elnMapper.ElnFieldVersionMapper; import jnpf.elnMapper.ElnRecordFieldValueMapper; import jnpf.elnMapper.ElnRecordMapper; import jnpf.elnMapper.ElnRecordRepeatValueMapper; import jnpf.elnMapper.ElnRecordRevisionMapper; import jnpf.elnMapper.ElnRecordSaveOperationMapper; import jnpf.elnMapper.ElnTemplateBindingMapper; import jnpf.elnMapper.ElnTemplateFormulaBindingMapper; import jnpf.elnService.ElnRecordFormulaCalculationService; import jnpf.elnService.ElnCrossTemplateSnapshotResolver; import jnpf.elnService.ElnRecordSourceSyncOperationCoordinator; import jnpf.elnService.field.ElnTestResultFields; import jnpf.elnService.template.ElnTemplateDocumentEvidence; import jnpf.elnService.template.docx.DocxControlSnapshot; import jnpf.onlyoffice.RecordRevisionFenceApi; import org.junit.Before; import org.junit.Test; import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.List; import java.math.BigDecimal; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.never; import static org.mockito.Mockito.when; public class ElnRecordFreezeCheckServiceImplTest { private ElnRecordMapper recordMapper; private ElnRecordRevisionMapper revisionMapper; private ElnRecordFieldValueMapper valueMapper; private ElnRecordRepeatValueMapper repeatValueMapper; private ElnRecordSaveOperationMapper operationMapper; private ElnTemplateBindingMapper bindingMapper; private ElnTemplateFormulaBindingMapper formulaBindingMapper; private ElnFieldVersionMapper versionMapper; private ElnRecordFormulaCalculationService calculation; private ElnTemplateDocumentEvidence evidence; private RecordRevisionFenceApi fence; private ElnCrossTemplateSnapshotResolver crossTemplateSnapshotResolver; private ElnRecordSourceSyncOperationCoordinator sourceSyncOperations; private ElnRecordFreezeCheckServiceImpl service; @Before public void setUp() { recordMapper = mock(ElnRecordMapper.class); revisionMapper = mock(ElnRecordRevisionMapper.class); valueMapper = mock(ElnRecordFieldValueMapper.class); repeatValueMapper = mock(ElnRecordRepeatValueMapper.class); operationMapper = mock(ElnRecordSaveOperationMapper.class); bindingMapper = mock(ElnTemplateBindingMapper.class); formulaBindingMapper = mock(ElnTemplateFormulaBindingMapper.class); versionMapper = mock(ElnFieldVersionMapper.class); calculation = mock(ElnRecordFormulaCalculationService.class); evidence = mock(ElnTemplateDocumentEvidence.class); fence = mock(RecordRevisionFenceApi.class); crossTemplateSnapshotResolver = mock(ElnCrossTemplateSnapshotResolver.class); sourceSyncOperations = mock(ElnRecordSourceSyncOperationCoordinator.class); service = new ElnRecordFreezeCheckServiceImpl(recordMapper, revisionMapper, valueMapper, repeatValueMapper, operationMapper, bindingMapper, formulaBindingMapper, versionMapper, calculation, evidence, fence, new ElnRecordFreezeCheckServiceImpl.TenantRoute() { @Override public void switchTo(String tenantId) { } @Override public void clear() { } }, crossTemplateSnapshotResolver, sourceSyncOperations); } @Test public void returnsNotApplicableForItemsWithoutElnRecord() { RecordFreezeCheckBatch batch = service.prepare(request("item-1")); assertEquals(RecordFreezeCheckResult.NOT_APPLICABLE, batch.getItems().get(0).getStatus()); } @Test public void blocksFreezeWhileSourceSynchronizationAwaitsDocumentConfirmation() { arrangeRecord("item-1", "value", "value", 0, "SUCCESS", "PRIMARY", "field-1"); when(sourceSyncOperations.hasPendingDocumentConfirmation("tenant-1", "record-1")) .thenReturn(true); RecordFreezeCheckResult result = service.prepare(request("item-1")).getItems().get(0); assertEquals(RecordFreezeCheckResult.BLOCKED, result.getStatus()); assertTrue(result.getDiagnostics().stream().anyMatch(issue -> "SOURCE_SYNC_DOCUMENT_PENDING".equals(issue.getCode()))); } @Test public void usesRecalculatedValueForDocumentComparisonAndKeepsFenceUntilRelease() { arrangeRecord("item-1", "old", "new", 0, "SUCCESS", "PRIMARY", "field-1"); when(calculation.recalculateAll(anyString(), any(), any(), any(), anyString(), anyString())).thenAnswer(invocation -> { ElnRecordFieldValueEntity value = ((List) invocation.getArgument(3)).get(0); value.setZhiWenben("new"); value.setJisuanZhuangtai("SUCCESS"); return Collections.singletonList(value); }); RecordFreezeCheckResult result = service.prepare(request("item-1")).getItems().get(0); assertEquals(result.getDiagnostics().toString(), RecordFreezeCheckResult.READY, result.getStatus()); assertTrue(result.getReleaseToken().startsWith("freeze:op-1:item-1")); verify(fence).acquireForOperation("tenant-1", "record-1", "record.docx", "annex", "op-1:item-1"); RecordFreezeReleaseRequest release = new RecordFreezeReleaseRequest(); release.setTenantId("tenant-1"); release.setOperationCode("op-1"); release.setTestWorkItemIds(Collections.singletonList("item-1")); assertTrue(service.release(release)); verify(fence).release("tenant-1", "record-1", "record.docx", "annex", "freeze:op-1:item-1"); } @Test public void returnsStableTestSubmissionValuesOnlyForReadyRecord() { arrangeRecord("item-1", "expected", "expected", 0, "SUCCESS", "PRIMARY", "field-1"); when(calculation.recalculateAll(anyString(), any(), any(), any(), anyString(), anyString())) .thenReturn(Collections.emptyList()); RecordFreezeCheckResult result = service.prepare(request("item-1")).getItems().get(0); assertEquals(RecordFreezeCheckResult.READY, result.getStatus()); assertEquals("ELN 检验结果", result.getTestResult()); assertEquals("compliant", result.getTestConclusion()); } @Test public void aggregatesRequiredFormulaDocumentReferenceAndConcurrencyIssues() { arrangeRecord("item-1", "", "x", 1, "ERROR", "WRONG_ROLE", "field-1"); ElnTemplateBindingEntity reference = binding("reference-1", "REFERENCE", "eln.ref.field", "field-other"); reference.setYinyongShiliId("binding-1"); when(bindingMapper.selectList(any())).thenReturn(Arrays.asList(binding("binding-1", "PRIMARY", "eln.field.field", "field-1"), reference)); when(operationMapper.selectCount(any())).thenReturn(1L); when(calculation.recalculateAll(anyString(), any(), any(), any(), anyString(), anyString())).thenReturn(Collections.emptyList()); RecordFreezeCheckResult result = service.prepare(request("item-1")).getItems().get(0); assertEquals(RecordFreezeCheckResult.BLOCKED, result.getStatus()); assertTrue(hasCategory(result, "REQUIRED")); assertTrue(hasCategory(result, "FORMULA")); assertTrue(hasCategory(result, "DOCUMENT")); assertTrue(hasCategory(result, "REFERENCE")); assertTrue(hasCategory(result, "CONCURRENCY")); } @Test public void blocksRecordWhoseConfiguredSourceSnapshotWasNotInitialized() { arrangeRecord("item-1", "legacy value", "legacy value", 0, "SUCCESS", "PRIMARY", "field-1"); when(versionMapper.selectById(anyString())).thenAnswer(invocation -> { String id = invocation.getArgument(0); ElnFieldVersionEntity version = fieldVersion(id, "version-result".equals(id) ? "TEXTAREA" : "version-conclusion".equals(id) ? "SELECT" : "TEXT", "version-result".equals(id) || "version-conclusion".equals(id) ? 1 : 0); if ("version-1".equals(id)) { version.setBangdingPeizhi("{\"read\":{\"source\":\"LIMS_TASK\"," + "\"property\":\"verification.sample_name\",\"propertyRevision\":1}}"); } return version; }); RecordFreezeCheckResult result = service.prepare(request("item-1")).getItems().get(0); assertEquals(RecordFreezeCheckResult.BLOCKED, result.getStatus()); assertTrue(result.getDiagnostics().toString(), result.getDiagnostics().stream().anyMatch(issue -> "SOURCE_INITIALIZATION_INCOMPLETE".equals(issue.getCode()) && "binding-1".equals(issue.getTargetBindingId()) && "field-1".equals(issue.getTargetFieldCode()))); } @Test public void marksMissingConfiguredSourceValueAsInitializationIncomplete() { arrangeRecord("item-1", "legacy value", "legacy value", 0, "SUCCESS", "PRIMARY", "field-1"); when(valueMapper.selectList(any())).thenReturn(Arrays.asList( value("binding-result", "version-result", ElnTestResultFields.RESULT, "ELN 检验结果"), value("binding-conclusion", "version-conclusion", ElnTestResultFields.CONCLUSION, "compliant"))); when(versionMapper.selectById(anyString())).thenAnswer(invocation -> { String id = invocation.getArgument(0); ElnFieldVersionEntity version = fieldVersion(id, "version-result".equals(id) ? "TEXTAREA" : "version-conclusion".equals(id) ? "SELECT" : "TEXT", "version-result".equals(id) || "version-conclusion".equals(id) ? 1 : 0); if ("version-1".equals(id)) { version.setBangdingPeizhi("{\"read\":{\"source\":\"LIMS_TASK\"," + "\"property\":\"verification.sample_name\",\"propertyRevision\":1}}"); } return version; }); RecordFreezeCheckResult result = service.prepare(request("item-1")).getItems().get(0); assertEquals(RecordFreezeCheckResult.BLOCKED, result.getStatus()); assertTrue(result.getDiagnostics().toString(), result.getDiagnostics().stream().anyMatch(issue -> "SOURCE_INITIALIZATION_INCOMPLETE".equals(issue.getCode()) && "binding-1".equals(issue.getTargetBindingId()) && "field-1".equals(issue.getTargetFieldCode()))); } @Test public void allowsRequiredConfiguredDecimalSnapshotToFreeze() { arrangeConfiguredStructuredValue("DECIMAL", "12.34", new BigDecimal("12.340"), null); RecordFreezeCheckResult result = service.prepare(request("item-1")).getItems().get(0); assertEquals(result.getDiagnostics().toString(), RecordFreezeCheckResult.READY, result.getStatus()); } @Test public void allowsRequiredConfiguredDateSnapshotToFreeze() { arrangeConfiguredStructuredValue("DATE", "2026-09-07", null, java.sql.Date.valueOf("2026-09-07")); RecordFreezeCheckResult result = service.prepare(request("item-1")).getItems().get(0); assertEquals(result.getDiagnostics().toString(), RecordFreezeCheckResult.READY, result.getStatus()); } @Test public void blocksAllDriftedCrossTemplateSnapshotsWithoutWritingTargetValues() { arrangeRecord("item-1", "expected", "expected", 0, "SUCCESS", "PRIMARY", "field-1"); ElnCrossTemplateSnapshotResolver.SnapshotIssue first = new ElnCrossTemplateSnapshotResolver.SnapshotIssue("SOURCE_VALUE_DRIFT", "源字段值已变化", "binding-1", "field-1", "SOURCE-A"); ElnCrossTemplateSnapshotResolver.SnapshotIssue second = new ElnCrossTemplateSnapshotResolver.SnapshotIssue("SOURCE_REVISION_DRIFT", "源记录当前修订已变化", "binding-2", "field-2", "SOURCE-B"); when(crossTemplateSnapshotResolver.validatePersistedSnapshots( eq("tenant-1"), eq("item-1"), any())) .thenReturn(Arrays.asList(first, second)); RecordFreezeCheckResult result = service.prepare(request("item-1")).getItems().get(0); assertEquals(RecordFreezeCheckResult.BLOCKED, result.getStatus()); assertEquals(2, result.getDiagnostics().stream() .filter(issue -> "CROSS_TEMPLATE".equals(issue.getCategory())).count()); assertTrue(result.getDiagnostics().stream().anyMatch(issue -> "binding-1".equals(issue.getTargetBindingId()) && "field-1".equals(issue.getTargetFieldCode()) && "SOURCE-A".equals(issue.getSourceTemplateCode()))); verify(valueMapper, never()).updateById(any(ElnRecordFieldValueEntity.class)); } @Test public void permitsNonRequiredMissingFormulaInputButBlocksRecalculatedDocxMismatch() { arrangeRecord("item-1", "expected", "expected", 0, "MISSING_INPUT", "PRIMARY", "field-1"); when(calculation.recalculateAll(anyString(), any(), any(), any(), anyString(), anyString())).thenReturn(Collections.emptyList()); RecordFreezeCheckResult ready = service.prepare(request("item-1")).getItems().get(0); assertEquals(ready.getDiagnostics().toString(), RecordFreezeCheckResult.READY, ready.getStatus()); assertFalse(ready.getDiagnostics().get(0).isBlocking()); arrangeRecord("item-1", "old", "different", 0, "SUCCESS", "PRIMARY", "field-1"); RecordFreezeCheckResult blocked = service.prepare(request("item-1")).getItems().get(0); assertEquals(RecordFreezeCheckResult.BLOCKED, blocked.getStatus()); } @Test public void blocksFormulaWhenItsActiveResultBindingOrCurrentValueIsMissing() { arrangeRecord("item-1", "expected", "expected", 0, "SUCCESS", "PRIMARY", "field-1"); ElnTemplateFormulaBindingEntity formula = new ElnTemplateFormulaBindingEntity(); formula.setId("formula-1"); formula.setMubanId("template-1"); formula.setBangdingZhuangtai("ACTIVE"); formula.setJieguoBangdingId("missing-result-binding"); when(formulaBindingMapper.selectList(any())).thenReturn(Collections.singletonList(formula)); when(calculation.recalculateAll(anyString(), any(), any(), any(), anyString(), anyString())).thenReturn(Collections.emptyList()); RecordFreezeCheckResult result = service.prepare(request("item-1")).getItems().get(0); assertEquals(RecordFreezeCheckResult.BLOCKED, result.getStatus()); assertTrue(result.getDiagnostics().stream().anyMatch(issue -> "FORMULA".equals(issue.getCategory()) && issue.getCode().equals("FORMULA_RESULT_BINDING_INVALID"))); } @Test public void blocksRequiredRepeatFormulaWhenAnActiveRowIsMissingInput() { ElnRecordRepeatValueEntity repeatValue = repeatFormulaValue("MISSING_INPUT", null); repeatValue.setJisuanZhuangtai("MISSING_INPUT"); ElnRecordFormulaCalculationResult calculationResult = new ElnRecordFormulaCalculationResult(); calculationResult.setRepeatRows(Collections.singletonList(repeatRow())); calculationResult.setRepeatValues(Collections.singletonList(repeatValue)); arrangeRequiredRepeatFormula(calculationResult, ""); RecordFreezeCheckResult result = service.prepare(request("item-1")).getItems().get(0); assertEquals(RecordFreezeCheckResult.BLOCKED, result.getStatus()); assertTrue(result.getDiagnostics().stream().anyMatch(issue -> "REQUIRED_REPEAT_FORMULA_NOT_SUCCESS".equals(issue.getCode()))); } @Test public void blocksWhenRequiredRepeatFormulaCellIsMissingForActiveRow() { ElnRecordFormulaCalculationResult calculationResult = new ElnRecordFormulaCalculationResult(); calculationResult.setRepeatRows(Collections.singletonList(repeatRow())); arrangeRequiredRepeatFormula(calculationResult, ""); RecordFreezeCheckResult result = service.prepare(request("item-1")).getItems().get(0); assertEquals(RecordFreezeCheckResult.BLOCKED, result.getStatus()); assertTrue(result.getDiagnostics().stream().anyMatch(issue -> "REPEAT_FORMULA_RESULT_MISSING".equals(issue.getCode()))); } @Test public void blocksWhenRecalculatedRepeatFormulaDiffersFromDocx() { ElnRecordFormulaCalculationResult calculationResult = new ElnRecordFormulaCalculationResult(); calculationResult.setRepeatRows(Collections.singletonList(repeatRow())); calculationResult.setRepeatValues(Collections.singletonList( repeatFormulaValue("SUCCESS", "expected"))); arrangeRequiredRepeatFormula(calculationResult, "tampered"); RecordFreezeCheckResult result = service.prepare(request("item-1")).getItems().get(0); assertEquals(RecordFreezeCheckResult.BLOCKED, result.getStatus()); assertTrue(result.getDiagnostics().stream().anyMatch(issue -> "DOCX_REPEAT_FORMULA_VALUE_MISMATCH".equals(issue.getCode()))); } private void arrangeRequiredRepeatFormula( ElnRecordFormulaCalculationResult calculationResult, String runtimeDocxValue) { arrangeRecord("item-1", "expected", "expected", 0, "SUCCESS", "PRIMARY", "field-1"); ElnTemplateBindingEntity repeatResult = binding("repeat-result", "REPEAT", "eln.repeat.group-a.prototype.repeat-result", "repeat-result"); repeatResult.setRepeatGroupCode("group-a"); repeatResult.setZiduanBanbenId("repeat-version"); ElnTemplateBindingEntity testResult = binding("binding-result", "PRIMARY", "eln.field.test.result", ElnTestResultFields.RESULT); testResult.setZiduanBanbenId("version-result"); ElnTemplateBindingEntity conclusion = binding("binding-conclusion", "PRIMARY", "eln.field.test.conclusion", ElnTestResultFields.CONCLUSION); conclusion.setZiduanBanbenId("version-conclusion"); when(bindingMapper.selectList(any())).thenReturn(Arrays.asList( binding("binding-1", "PRIMARY", "eln.field.field", "field-1"), testResult, conclusion, repeatResult)); when(formulaBindingMapper.selectList(any())).thenReturn(Collections.singletonList( formula("repeat-formula", "repeat-result"))); when(versionMapper.selectById(anyString())).thenAnswer(invocation -> { String id = invocation.getArgument(0); if ("repeat-version".equals(id)) return fieldVersion(id, "TEXT", 1); if ("version-result".equals(id)) return fieldVersion(id, "TEXTAREA", 1); if ("version-conclusion".equals(id)) return fieldVersion(id, "SELECT", 1); return fieldVersion(id, "TEXT", 0); }); when(evidence.readControls("tenant-1", "record.docx")).thenReturn(Arrays.asList( new DocxControlSnapshot("eln.field.field", "", "", "TEXT", "PRIMARY", "word/document.xml", "", Collections.emptyMap(), "expected"), new DocxControlSnapshot("eln.field.test.result", "", "", "TEXTAREA", "PRIMARY", "word/document.xml", "", Collections.emptyMap(), "ELN 检验结果"), new DocxControlSnapshot("eln.field.test.conclusion", "", "", "SELECT", "PRIMARY", "word/document.xml", "", Collections.emptyMap(), "compliant"), new DocxControlSnapshot("eln.repeat.group-a.prototype.repeat-result", "", "", "TEXT", "REPEAT", "word/document.xml", "", Collections.emptyMap(), ""), new DocxControlSnapshot("eln.repeat.group-a.row-1.repeat-result", "", "", "TEXT", "REPEAT", "word/document.xml", "", Collections.emptyMap(), runtimeDocxValue))); when(calculation.recalculateAllWithRepeats(anyString(), any(), any(), any(), anyString(), anyString())).thenReturn(calculationResult); when(repeatValueMapper.updateById(any(ElnRecordRepeatValueEntity.class))).thenReturn(1); } private static ElnRecordRepeatRowEntity repeatRow() { ElnRecordRepeatRowEntity row = new ElnRecordRepeatRowEntity(); row.setRepeatGroupCode("group-a"); row.setRowId("row-1"); row.setOperationStatus("ACTIVE"); row.setFDeleteMark(0); return row; } private static ElnRecordRepeatValueEntity repeatFormulaValue(String status, String text) { ElnRecordRepeatValueEntity value = new ElnRecordRepeatValueEntity(); value.setRepeatGroupCode("group-a"); value.setRepeatRowId("row-1"); value.setMubanBangdingId("repeat-result"); value.setZiduanBanbenId("repeat-version"); value.setZiduanBianma("repeat-result"); value.setZhiWenben(text); value.setJisuanZhuangtai(status); return value; } private void arrangeRecord(String itemId, String storedValue, String docxValue, int required, String formulaStatus, String controlRole, String fieldCode) { ElnRecordEntity record = new ElnRecordEntity(); record.setId("record-1"); record.setJianyanXiangId(itemId); record.setDangqianXiudingId("revision-1"); record.setMubanId("template-1"); record.setFVersion(1); ElnRecordRevisionEntity revision = new ElnRecordRevisionEntity(); revision.setId("revision-1"); revision.setDianziJiluId("record-1"); revision.setBizStatus("DRAFT"); revision.setJiluWendang("record.docx"); ElnRecordFieldValueEntity value = new ElnRecordFieldValueEntity(); value.setMubanBangdingId("binding-1"); value.setZiduanBanbenId("version-1"); value.setZiduanBianma(fieldCode); value.setZhiWenben(storedValue); value.setJisuanZhuangtai(formulaStatus); ElnRecordFieldValueEntity testResult = value("binding-result", "version-result", ElnTestResultFields.RESULT, "ELN 检验结果"); ElnRecordFieldValueEntity testConclusion = value("binding-conclusion", "version-conclusion", ElnTestResultFields.CONCLUSION, "compliant"); ElnFieldVersionEntity version = new ElnFieldVersionEntity(); version.setId("version-1"); version.setFTenantId("tenant-1"); version.setShujuLeixing("TEXT"); version.setShifouBitian(required); when(recordMapper.selectLiveByTestWorkItem("tenant-1", itemId)).thenReturn(record); when(recordMapper.selectLiveForUpdate("tenant-1", "record-1")).thenReturn(record); when(revisionMapper.selectOne(any())).thenReturn(revision); when(valueMapper.selectList(any())).thenReturn(Arrays.asList(value, testResult, testConclusion)); when(operationMapper.selectCount(any())).thenReturn(0L); ElnTemplateBindingEntity resultBinding = binding("binding-result", "PRIMARY", "eln.field.test.result", ElnTestResultFields.RESULT); resultBinding.setZiduanBanbenId("version-result"); ElnTemplateBindingEntity conclusionBinding = binding("binding-conclusion", "PRIMARY", "eln.field.test.conclusion", ElnTestResultFields.CONCLUSION); conclusionBinding.setZiduanBanbenId("version-conclusion"); when(bindingMapper.selectList(any())).thenReturn(Arrays.asList( binding("binding-1", "PRIMARY", "eln.field.field", fieldCode), resultBinding, conclusionBinding)); when(formulaBindingMapper.selectList(any())).thenReturn(Collections.singletonList( formula("formula-1", "binding-1"))); when(versionMapper.selectById(anyString())).thenAnswer(invocation -> { String id = invocation.getArgument(0); if ("version-result".equals(id)) return fieldVersion(id, "TEXTAREA", 1); if ("version-conclusion".equals(id)) return fieldVersion(id, "SELECT", 1); return version; }); when(evidence.readControls("tenant-1", "record.docx")).thenReturn(Arrays.asList( new DocxControlSnapshot("eln.field.field", "", "", "TEXT", controlRole, "word/document.xml", "", Collections.emptyMap(), docxValue), new DocxControlSnapshot("eln.field.test.result", "", "", "TEXTAREA", "PRIMARY", "word/document.xml", "", Collections.emptyMap(), "ELN 检验结果"), new DocxControlSnapshot("eln.field.test.conclusion", "", "", "SELECT", "PRIMARY", "word/document.xml", "", Collections.emptyMap(), "compliant"))); when(recordMapper.advanceVersion("tenant-1", "record-1", 1, "user-1")).thenReturn(1); when(valueMapper.updateById(any(ElnRecordFieldValueEntity.class))).thenReturn(1); when(fence.acquireForOperation(anyString(), anyString(), anyString(), anyString(), anyString())).thenAnswer(invocation -> response("freeze:" + invocation.getArgument(4))); when(fence.release(anyString(), anyString(), anyString(), anyString(), anyString())).thenReturn(response(true)); } private void arrangeConfiguredStructuredValue(String type, String docxValue, BigDecimal number, Date date) { arrangeRecord("item-1", null, docxValue, 1, "SUCCESS", "PRIMARY", "field-1"); ElnRecordFieldValueEntity value = valueMapper.selectList(null).get(0); value.setZhiShuzhi(number); value.setZhiRiqi(date); value.setZhiLaiyuan("LIMS_TASK"); value.setYuanShuxing("configured.value"); value.setYuanShuxingBanben(1); value.setYuanQuzhiZhuangtai("SUCCESS"); value.setYuanXueyuan("configured.dataset@1/value"); value.setYuanHuoquShijian(java.sql.Timestamp.valueOf("2026-09-07 10:00:00")); when(versionMapper.selectById(anyString())).thenAnswer(invocation -> { String id = invocation.getArgument(0); ElnFieldVersionEntity version = fieldVersion(id, "version-result".equals(id) ? "TEXTAREA" : "version-conclusion".equals(id) ? "SELECT" : type, 1); if ("version-1".equals(id)) { version.setBangdingPeizhi("{\"read\":{\"source\":\"LIMS_TASK\"," + "\"property\":\"configured.value\",\"propertyRevision\":1}}"); } return version; }); when(calculation.recalculateAll(anyString(), any(), any(), any(), anyString(), anyString())) .thenReturn(Collections.emptyList()); } private static ElnTemplateBindingEntity binding(String id, String role, String tag, String code) { ElnTemplateBindingEntity binding = new ElnTemplateBindingEntity(); binding.setId(id); binding.setMubanId("template-1"); binding.setBangdingZhuangtai("ACTIVE"); binding.setKongjianJuese(role); binding.setKongjianBiaoqian(tag); binding.setKongjianLeixing( ElnTestResultFields.RESULT.equals(code) ? "TEXTAREA" : ElnTestResultFields.CONCLUSION.equals(code) ? "SELECT" : "TEXT"); binding.setZiduanDingyiId("definition-1"); binding.setZiduanBanbenId("version-1"); binding.setZiduanBianma(code); return binding; } private static ElnRecordFieldValueEntity value(String bindingId, String versionId, String code, String text) { ElnRecordFieldValueEntity value = new ElnRecordFieldValueEntity(); value.setMubanBangdingId(bindingId); value.setZiduanBanbenId(versionId); value.setZiduanBianma(code); value.setZhiWenben(text); return value; } private static ElnFieldVersionEntity fieldVersion(String id, String type, int required) { ElnFieldVersionEntity version = new ElnFieldVersionEntity(); version.setId(id); version.setFTenantId("tenant-1"); version.setShujuLeixing(type); version.setShifouBitian(required); return version; } private static ElnTemplateFormulaBindingEntity formula(String id, String resultBindingId) { ElnTemplateFormulaBindingEntity formula = new ElnTemplateFormulaBindingEntity(); formula.setId(id); formula.setMubanId("template-1"); formula.setBangdingZhuangtai("ACTIVE"); formula.setJieguoBangdingId(resultBindingId); return formula; } private static RecordFreezeCheckRequest request(String itemId) { RecordFreezeCheckRequest request = new RecordFreezeCheckRequest(); request.setTenantId("tenant-1"); request.setUserId("user-1"); request.setOperationCode("op-1"); request.setTestWorkItemIds(Collections.singletonList(itemId)); return request; } private static boolean hasCategory(RecordFreezeCheckResult result, String category) { return result.getDiagnostics().stream().anyMatch(issue -> category.equals(issue.getCategory())); } private static ActionResult response(T data) { ActionResult response = new ActionResult<>(); response.setCode(200); response.setData(data); return response; } }