package jnpf.elnService.template; import jnpf.elnEntity.field.ElnFieldDefEntity; import jnpf.elnEntity.field.ElnFieldVersionEntity; import jnpf.elnEntity.template.ElnTemplateBindingEntity; import jnpf.elnEntity.template.ElnTemplateVersionEntity; import jnpf.elnEntity.template.ElnTemplateVos; import jnpf.elnService.template.docx.DocxControlSnapshot; import jnpf.elnService.template.impl.ElnTemplateHealthServiceImpl; import jnpf.exception.DataException; import org.junit.Test; import java.lang.reflect.Constructor; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; 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 ElnTemplateHealthServiceImplTest { @Test public void reportsMissingRequiredTestResultBindings() { Fixture fixture = new Fixture(); ElnTemplateHealthService.HealthReport report = fixture.service.inspect("template-1"); assertCode(report, "TEST_RESULT_BINDING_REQUIRED"); assertCode(report, "TEST_CONCLUSION_BINDING_REQUIRED"); assertEquals("ERROR", diagnostic(report, "TEST_RESULT_BINDING_REQUIRED").getSeverity()); assertEquals("ERROR", diagnostic(report, "TEST_CONCLUSION_BINDING_REQUIRED").getSeverity()); } @Test public void reportsSingleCheckboxGroupMemberWithDraftAndPublishedSeverity() { Fixture draft = new Fixture(); draft.template.setBizStatus("DRAFT"); draft.checkboxBinding("passed", "result_choice", "step-a", false); ElnTemplateHealthService.HealthDiagnostic warning = diagnostic( draft.service.inspect("template-1"), "CHECKBOX_GROUP_SINGLE_MEMBER"); assertEquals("WARNING", warning.getSeverity()); assertEquals("result_choice", warning.getCheckboxGroupCode()); assertEquals(Collections.singletonList("passed"), warning.getBindingIds()); assertEquals("passed", warning.getBindingId()); assertEquals(Collections.singletonList("LOCATE"), warning.getActions()); assertTrue(warning.getMessage().contains("只有一个成员")); Fixture published = new Fixture(); published.template.setBizStatus("PUBLISHED"); published.checkboxBinding("passed", "result_choice", "step-a", false); assertEquals("ERROR", diagnostic(published.service.inspect("template-1"), "CHECKBOX_GROUP_SINGLE_MEMBER").getSeverity()); } @Test public void acceptsValidCheckboxGroupAndRejectsScopeOrDefaultConflicts() { Fixture valid = new Fixture(); valid.template.setBizStatus("DRAFT"); valid.checkboxBinding("passed", "result_choice", "step-a", true); valid.checkboxBinding("failed", "result_choice", "step-a", false); assertNoCode(valid.service.inspect("template-1"), "CHECKBOX_GROUP_SINGLE_MEMBER"); assertNoCode(valid.service.inspect("template-1"), "CHECKBOX_GROUP_SCOPE_MISMATCH"); assertNoCode(valid.service.inspect("template-1"), "CHECKBOX_GROUP_DEFAULT_COUNT_OUT_OF_RANGE"); Fixture scope = new Fixture(); scope.template.setBizStatus("DRAFT"); scope.checkboxBinding("passed", "result_choice", "step-a", false); scope.checkboxBinding("failed", "result_choice", "step-b", false); ElnTemplateHealthService.HealthDiagnostic scopeError = diagnostic( scope.service.inspect("template-1"), "CHECKBOX_GROUP_SCOPE_MISMATCH"); assertEquals(Arrays.asList("failed", "passed"), scopeError.getBindingIds()); assertTrue(scopeError.getMessage().contains("同一步骤")); Fixture defaults = new Fixture(); defaults.template.setBizStatus("DRAFT"); defaults.checkboxBinding("passed", "result_choice", "step-a", true); defaults.checkboxBinding("failed", "result_choice", "step-a", true); ElnTemplateHealthService.HealthDiagnostic defaultError = diagnostic( defaults.service.inspect("template-1"), "CHECKBOX_GROUP_DEFAULT_COUNT_OUT_OF_RANGE"); assertEquals("ERROR", defaultError.getSeverity()); assertTrue(defaultError.getMessage().contains("默认勾选")); } @Test public void reportsMissingAndOrphanSelectionGroupRules() { Fixture missing = new Fixture(); missing.checkboxBinding("passed", "result_choice", "step-a", false); missing.checkboxBinding("failed", "result_choice", "step-a", false); missing.checkboxGroups.clear(); assertCode(missing.service.inspect("template-1"), "CHECKBOX_GROUP_RULE_MISSING"); Fixture orphan = new Fixture(); orphan.configureCheckboxGroup("orphan", 0, 1); assertCode(orphan.service.inspect("template-1"), "CHECKBOX_GROUP_WITHOUT_MEMBER"); } @Test public void reportsInvalidBoundsAndMinimumAboveMemberCount() { Fixture invalid = new Fixture(); invalid.checkboxBinding("passed", "result_choice", "step-a", false); invalid.checkboxBinding("failed", "result_choice", "step-a", false); invalid.configureCheckboxGroup("result_choice", 3, 2); assertCode(invalid.service.inspect("template-1"), "CHECKBOX_GROUP_RULE_INVALID"); Fixture tooFewMembers = new Fixture(); tooFewMembers.checkboxBinding("passed", "result_choice", "step-a", true); tooFewMembers.checkboxBinding("failed", "result_choice", "step-a", false); tooFewMembers.configureCheckboxGroup("result_choice", 3, null); assertCode(tooFewMembers.service.inspect("template-1"), "CHECKBOX_GROUP_MIN_EXCEEDS_MEMBERS"); } @Test public void validatesDefaultCheckedCountAgainstArbitraryRuleBounds() { Fixture twoAllowed = new Fixture(); twoAllowed.checkboxBinding("passed", "result_choice", "step-a", true); twoAllowed.checkboxBinding("failed", "result_choice", "step-a", true); twoAllowed.configureCheckboxGroup("result_choice", 1, 2); assertNoCode(twoAllowed.service.inspect("template-1"), "CHECKBOX_GROUP_DEFAULT_COUNT_OUT_OF_RANGE"); Fixture minimumMissing = new Fixture(); minimumMissing.checkboxBinding("passed", "result_choice", "step-a", false); minimumMissing.checkboxBinding("failed", "result_choice", "step-a", false); minimumMissing.configureCheckboxGroup("result_choice", 1, null); assertCode(minimumMissing.service.inspect("template-1"), "CHECKBOX_GROUP_DEFAULT_COUNT_OUT_OF_RANGE"); } @Test public void reportsInvalidCheckboxGroupCodeMemberAndOwnershipWithoutNullFailure() { Fixture fixture = new Fixture(); fixture.template.setBizStatus("DRAFT"); fixture.checkboxBinding("passed", "含中文", "step-a", false); fixture.checkboxBinding("failed", "含中文", "step-a", false); fixture.bindings.get(0).setKongjianLeixing("TEXT"); fixture.bindings.get(1).setZiduanBanbenId("missing-version"); ElnTemplateHealthService.HealthReport report = fixture.service.inspect("template-1"); assertCode(report, "CHECKBOX_GROUP_CODE_INVALID"); assertCode(report, "CHECKBOX_GROUP_MEMBER_INVALID"); } @Test public void rejectsMixedAndDifferentRepeatCheckboxGroupScopes() { Fixture mixed = new Fixture(); mixed.checkboxBinding("primary", "result_choice", "step-a", false); mixed.repeatCheckboxBinding("repeat", "result_choice", "samples", false); assertCode(mixed.service.inspect("template-1"), "CHECKBOX_GROUP_SCOPE_MISMATCH"); Fixture differentRepeats = new Fixture(); differentRepeats.repeatCheckboxBinding("sample-result", "result_choice", "samples", false); differentRepeats.repeatCheckboxBinding("result-result", "result_choice", "results", false); assertCode(differentRepeats.service.inspect("template-1"), "CHECKBOX_GROUP_SCOPE_MISMATCH"); } @Test public void doesNotReportCheckboxGroupDiagnosticsForUngroupedCheckboxes() { Fixture fixture = new Fixture(); fixture.checkboxBinding("passed", null, "step-a", false); fixture.checkboxBinding("failed", null, "step-a", false); ElnTemplateHealthService.HealthReport report = fixture.service.inspect("template-1"); assertFalse(report.getDiagnostics().stream().anyMatch(item -> item.getCode().startsWith("CHECKBOX_GROUP_"))); } @Test public void downloadsOnceReconcilesThenReportsAllAuthoritativeDiagnostics() { Fixture fixture = new Fixture(); fixture.template.setBizStatus("DRAFT"); fixture.addStandardCases(); ElnTemplateHealthService.HealthReport report = fixture.service.inspect("template-1"); assertEquals("template-1", report.getTemplateId()); assertEquals("template-1.docx", report.getFileId()); assertEquals(1, fixture.downloads); assertEquals(1, fixture.reconciliations); assertEquals(1, fixture.bindingReads); for (String code : Arrays.asList("ORPHAN_CONTROL", "MISSING_CONTROL", "DUPLICATE_TAG", "ROLE_MISMATCH", "TYPE_MISMATCH", "CONFIG_MISMATCH", "REFERENCE_WITHOUT_PRIMARY", "FIELD_VERSION_MISMATCH", "FIELD_DISABLED", "FIELD_VERSION_NOT_PUBLISHED", "PENDING_BINDING", "PENDING_CONFIRMATION_REQUIRED")) { assertCode(report, code); } ElnTemplateHealthService.HealthDiagnostic missing = diagnostic(report, "MISSING_CONTROL"); assertEquals("missing", missing.getBindingId()); assertEquals("缺失字段", missing.getFieldDisplayName()); assertEquals(Arrays.asList("REPAIR", "CONFIRM_REMOVE"), missing.getActions()); assertEquals(Collections.singletonList("LOCATE"), diagnostic(report, "ORPHAN_CONTROL").getActions()); ElnTemplateHealthService.HealthDiagnostic duplicate = diagnostic(report, "DUPLICATE_TAG"); assertEquals("duplicate", duplicate.getBindingId()); assertEquals("重复字段", duplicate.getFieldDisplayName()); assertEquals("word/header1.xml", diagnostic(report, "ORPHAN_CONTROL").getPartName()); assertFalse(report.getDiagnostics().stream() .anyMatch(item -> "cancelled".equals(item.getBindingId()))); } @Test public void publishedTemplateCheckDoesNotReconcileAndOnlyReturnsLocateActions() { Fixture fixture = new Fixture(); fixture.template.setBizStatus("PUBLISHED"); fixture.addStandardCases(); ElnTemplateHealthService.HealthReport report = fixture.service.inspect("template-1"); assertEquals(1, fixture.downloads); assertEquals(0, fixture.reconciliations); assertEquals(1, fixture.bindingReads); assertTrue(diagnostic(report, "MISSING_CONTROL").getActions().isEmpty()); assertEquals(Collections.singletonList("LOCATE"), diagnostic(report, "ORPHAN_CONTROL").getActions()); assertEquals(Collections.singletonList("LOCATE"), diagnostic(report, "ROLE_MISMATCH").getActions()); assertTrue(report.getDiagnostics().stream().allMatch(item -> item.getActions().isEmpty() || Collections.singletonList("LOCATE").equals(item.getActions()))); } @Test public void failsClosedForAnotherTenantTemplate() { Fixture fixture = new Fixture(); fixture.template.setFTenantId("tenant-b"); try { fixture.service.inspect("template-1"); fail("预期拒绝跨租户模板"); } catch (DataException expected) { assertFalse(expected.getMessage().isEmpty()); } assertEquals(0, fixture.downloads); } @Test public void ignoresUnobservableTextConfigAndCanonicalizesDropdownOptions() { Fixture fixture = new Fixture(); fixture.field("text-field", "enabled", "text-version"); fixture.version("text-version", "text-field", "PUBLISHED", "文本字段", "{\"placeholder\":\"请输入\",\"defaultValue\":\"默认文本\"}"); fixture.binding("text", "text-field", "text-version", "eln.field.text", "TEXT", "PRIMARY", null, "ACTIVE"); fixture.control("eln.field.text", "TEXT", "PRIMARY", Collections.emptyMap(), "word/document.xml"); fixture.field("dropdown-field", "enabled", "dropdown-version"); fixture.version("dropdown-version", "dropdown-field", "PUBLISHED", "下拉字段", dropdownConfigJson("不合格")); fixture.binding("dropdown", "dropdown-field", "dropdown-version", "eln.field.dropdown", "DROPDOWN", "PRIMARY", null, "ACTIVE"); Map reordered = new LinkedHashMap<>(); reordered.put("FAIL", "不合格"); reordered.put("PASS", "合格"); fixture.control("eln.field.dropdown", "DROPDOWN", "PRIMARY", reordered, "word/document.xml"); ElnTemplateHealthService.HealthReport report = fixture.service.inspect("template-1"); assertNoDiagnostic(report, "CONFIG_MISMATCH", "text"); assertNoDiagnostic(report, "CONFIG_MISMATCH", "dropdown"); } @Test public void reportsObservableDropdownOptionDifference() { Fixture fixture = new Fixture(); fixture.field("dropdown-field", "enabled", "dropdown-version"); fixture.version("dropdown-version", "dropdown-field", "PUBLISHED", "下拉字段", dropdownConfigJson("不合格")); fixture.binding("dropdown", "dropdown-field", "dropdown-version", "eln.field.dropdown", "DROPDOWN", "PRIMARY", null, "ACTIVE"); Map different = new LinkedHashMap<>(); different.put("PASS", "合格"); different.put("FAIL", "异常"); fixture.control("eln.field.dropdown", "DROPDOWN", "PRIMARY", different, "word/document.xml"); ElnTemplateHealthService.HealthReport report = fixture.service.inspect("template-1"); assertDiagnostic(report, "CONFIG_MISMATCH", "dropdown"); } @Test public void missingControlReturnsExpectedVersionAndEmptyDependencies() { Fixture fixture = new Fixture(); fixture.field("missing-field", "enabled", "missing-version"); fixture.version("missing-version", "missing-field", "PUBLISHED", "缺失字段", null); fixture.binding("missing", "missing-field", "missing-version", "eln.field.missing", "TEXT", "PRIMARY", null, "ACTIVE"); fixture.bindings.get(0).setFVersion(7); ElnTemplateHealthService.HealthDiagnostic missing = diagnostic( fixture.service.inspect("template-1"), "MISSING_CONTROL"); assertEquals(Integer.valueOf(7), missing.getExpectedVersion()); assertNotNull(missing.getDependencies()); assertTrue(missing.getDependencies().isEmpty()); } @Test public void missingPrimaryMergesActiveReferenceAndValidatorDependencies() { Fixture fixture = new Fixture(); fixture.field("field-1", "enabled", "version-1"); fixture.version("version-1", "field-1", "PUBLISHED", "样品编号", null); fixture.binding("primary", "field-1", "version-1", "eln.field.sample_id", "TEXT", "PRIMARY", null, "ACTIVE"); fixture.bindings.get(0).setFVersion(9); fixture.binding("reference", "field-1", "version-1", "eln.ref.sample_id.01", "TEXT", "REFERENCE", "primary", "ACTIVE"); fixture.control("eln.ref.sample_id.01", "TEXT", "REFERENCE", Collections.emptyMap(), "word/document.xml"); ElnTemplateVos.Diagnostic formula = new ElnTemplateVos.Diagnostic(); formula.setSeverity("ERROR"); formula.setCode("FORMULA_DEPENDENCY"); formula.setMessage("公式仍引用该字段"); fixture.validatorDiagnostics = Collections.singletonList(formula); ElnTemplateHealthService.HealthDiagnostic missing = diagnostic( fixture.service.inspect("template-1"), "MISSING_CONTROL"); assertEquals(Integer.valueOf(9), missing.getExpectedVersion()); assertTrue(missing.getDependencies().stream().anyMatch(item -> "ACTIVE_REFERENCE_DEPENDENCY".equals(item.getCode()) && "reference".equals(item.getBindingId()))); assertTrue(missing.getDependencies().stream().anyMatch(item -> "FORMULA_DEPENDENCY".equals(item.getCode()))); assertEquals(Collections.singleton("primary"), fixture.validatedRemovalIds); } @Test public void acceptsSingleRepeatPrototypeGroupWhenAllColumnsShareOnePhysicalRow() { Fixture fixture = new Fixture(); fixture.repeatBinding("repeat-result", "result", "version-result"); fixture.repeatControl("eln.repeat.samples.prototype.result", 0, 2, false, false); ElnTemplateHealthService.HealthReport report = fixture.service.inspect("template-1"); assertFalse(report.getDiagnostics().stream().anyMatch(item -> item.getCode().startsWith("REPEAT_") || item.getCode().startsWith("MULTIPLE_REPEAT_"))); } @Test public void rejectsNestedMergedOrSplitRepeatPrototypeRows() { Fixture fixture = new Fixture(); fixture.repeatBinding("repeat-result", "result", "version-result"); fixture.repeatBinding("repeat-unit", "unit", "version-unit"); fixture.repeatControl("eln.repeat.samples.prototype.result", 0, 2, true, true); fixture.repeatControl("eln.repeat.samples.prototype.unit", 0, 3, false, false); ElnTemplateHealthService.HealthReport report = fixture.service.inspect("template-1"); assertCode(report, "NESTED_REPEAT_TABLE"); assertCode(report, "REPEAT_VERTICAL_MERGE"); assertCode(report, "MULTIPLE_REPEAT_PROTOTYPE_ROWS"); } @Test public void rejectsNestedContentControlsEvenWhenBindingMatches() { Fixture fixture = new Fixture(); fixture.field("nested-field", "enabled", "nested-version"); fixture.version("nested-version", "nested-field", "PUBLISHED", "嵌套字段", null); fixture.binding("nested", "nested-field", "nested-version", "eln.field.nested", "TEXT", "PRIMARY", null, "ACTIVE"); fixture.nestedControl("eln.field.nested", "TEXT", "PRIMARY"); ElnTemplateHealthService.HealthDiagnostic diagnostic = diagnostic( fixture.service.inspect("template-1"), "NESTED_CONTENT_CONTROL"); assertEquals("nested", diagnostic.getBindingId()); assertEquals("ERROR", diagnostic.getSeverity()); } @Test public void acceptsIndependentRepeatGroupsOnDifferentRows() { Fixture fixture = new Fixture(); fixture.repeatBinding("repeat-result", "result", "version-result"); fixture.repeatBinding("repeat-sample", "sample", "version-sample"); ElnTemplateBindingEntity second = fixture.bindings.get(1); second.setRepeatGroupCode("results"); second.setKongjianBiaoqian("eln.repeat.results.prototype.sample"); fixture.configureGroup("results"); fixture.repeatControl("eln.repeat.samples.prototype.result", 0, 2, false, false); fixture.repeatControl("eln.repeat.results.prototype.sample", 0, 5, false, false); ElnTemplateHealthService.HealthReport report = fixture.service.inspect("template-1"); assertFalse(report.getDiagnostics().stream().anyMatch(item -> "MULTIPLE_REPEAT_GROUPS".equals(item.getCode()) || "OVERLAPPING_REPEAT_GROUPS".equals(item.getCode()))); } @Test public void rejectsIndependentRepeatGroupsOnSamePhysicalRow() { Fixture fixture = new Fixture(); fixture.repeatBinding("repeat-result", "result", "version-result"); fixture.repeatBinding("repeat-sample", "sample", "version-sample"); ElnTemplateBindingEntity second = fixture.bindings.get(1); second.setRepeatGroupCode("results"); second.setKongjianBiaoqian("eln.repeat.results.prototype.sample"); fixture.configureGroup("results"); fixture.repeatControl("eln.repeat.samples.prototype.result", 0, 2, false, false); fixture.repeatControl("eln.repeat.results.prototype.sample", 0, 2, false, false); assertCode(fixture.service.inspect("template-1"), "OVERLAPPING_REPEAT_GROUPS"); } @Test public void rejectsRepeatBindingsWithoutGroupConfiguration() { Fixture fixture = new Fixture(); fixture.repeatBinding("repeat-result", "result", "version-result"); fixture.repeatControl("eln.repeat.samples.prototype.result", 0, 2, false, false); fixture.repeatGroups.clear(); assertCode(fixture.service.inspect("template-1"), "REPEAT_GROUP_CONFIG_MISSING"); } private static void assertCode(ElnTemplateHealthService.HealthReport report, String code) { diagnostic(report, code); } private static void assertNoCode(ElnTemplateHealthService.HealthReport report, String code) { assertFalse("不应存在诊断: " + code, report.getDiagnostics().stream().anyMatch(item -> code.equals(item.getCode()))); } private static ElnTemplateHealthService.HealthDiagnostic diagnostic( ElnTemplateHealthService.HealthReport report, String code) { return report.getDiagnostics().stream().filter(item -> code.equals(item.getCode())) .findFirst().orElseThrow(() -> new AssertionError("缺少诊断: " + code)); } private static void assertDiagnostic(ElnTemplateHealthService.HealthReport report, String code, String bindingId) { assertTrue("缺少诊断: " + code + "/" + bindingId, report.getDiagnostics().stream().anyMatch(item -> code.equals(item.getCode()) && bindingId.equals(item.getBindingId()))); } private static void assertNoDiagnostic(ElnTemplateHealthService.HealthReport report, String code, String bindingId) { assertFalse("不应存在诊断: " + code + "/" + bindingId, report.getDiagnostics().stream().anyMatch(item -> code.equals(item.getCode()) && bindingId.equals(item.getBindingId()))); } private static String dropdownConfigJson(String failedDisplay) { return "{\"controlType\":\"dropdown\",\"placeholder\":\"请选择\"," + "\"defaultOptionValue\":\"PASS\",\"options\":[" + "{\"display\":\"合格\",\"value\":\"PASS\"}," + "{\"display\":\"" + failedDisplay + "\",\"value\":\"FAIL\"}]}"; } private static final class Fixture implements ElnTemplateHealthServiceImpl.Store { private final ElnTemplateVersionEntity template = new ElnTemplateVersionEntity(); private final Map fields = new LinkedHashMap<>(); private final Map versions = new LinkedHashMap<>(); private final List bindings = new ArrayList<>(); private final List controls = new ArrayList<>(); private final List repeatGroups = new ArrayList<>(); private final List checkboxGroups = new ArrayList<>(); private int downloads; private int reconciliations; private int bindingReads; private List validatorDiagnostics = Collections.emptyList(); private Set validatedRemovalIds = Collections.emptySet(); private final ElnTemplateHealthService service; private Fixture() { template.setId("template-1"); template.setFTenantId("tenant-a"); template.setMubanFile("[{\"fileId\":\"template-1.docx\",\"fileExtension\":\"docx\"}]"); template.setFDeleteMark(0); ElnTemplateDocumentEvidence evidence = (tenant, file) -> { downloads++; assertEquals("tenant-a", tenant); assertEquals("template-1.docx", file); return new ArrayList<>(controls); }; ElnTemplateHealthServiceImpl.PendingReconciler reconciler = (tenant, templateId, now, snapshots) -> { reconciliations++; ElnTemplateVos.Diagnostic item = new ElnTemplateVos.Diagnostic(); item.setSeverity("WARNING"); item.setCode("PENDING_CONFIRMATION_REQUIRED"); item.setBindingId("pending"); item.setControlTag("eln.field.pending"); item.setMessage("需要确认"); return Collections.singletonList(item); }; ElnTemplateBindingDependencyValidator validator = new ElnTemplateBindingDependencyValidator() { @Override public List validateUpgrade(String tenantId, String templateId, Set bindingIds, String targetFieldVersionId) { return Collections.emptyList(); } @Override public List validateRemoval(String tenantId, String templateId, Set bindingIds) { validatedRemovalIds = bindingIds; return validatorDiagnostics; } @Override public List validatePromotion(String tenantId, String templateId, String primaryBindingId, String referenceBindingId) { return Collections.emptyList(); } }; service = construct(this, evidence, reconciler, () -> "tenant-a", Collections.singletonList(validator), (tenantId, templateId) -> new ArrayList<>(repeatGroups), (tenantId, templateId) -> new ArrayList<>(checkboxGroups)); } private void addStandardCases() { field("normal-field", "enabled", "normal-version"); version("normal-version", "normal-field", "PUBLISHED", "正常字段", null); binding("normal", "normal-field", "normal-version", "eln.field.normal", "TEXT", "PRIMARY", null, "ACTIVE"); control("eln.field.normal", "TEXT", "PRIMARY", null, "word/document.xml"); field("missing-field", "enabled", "missing-version"); version("missing-version", "missing-field", "PUBLISHED", "缺失字段", null); binding("missing", "missing-field", "missing-version", "eln.field.missing", "TEXT", "PRIMARY", null, "ACTIVE"); field("dup-field", "enabled", "dup-version"); version("dup-version", "dup-field", "PUBLISHED", "重复字段", null); binding("duplicate", "dup-field", "dup-version", "eln.field.duplicate", "TEXT", "PRIMARY", null, "ACTIVE"); control("eln.field.duplicate", "TEXT", "PRIMARY", null, "word/document.xml"); control("eln.field.duplicate", "TEXT", "PRIMARY", null, "word/footer1.xml"); field("role-field", "enabled", "role-version"); version("role-version", "role-field", "PUBLISHED", "角色字段", null); binding("role", "role-field", "role-version", "eln.field.role", "TEXT", "PRIMARY", null, "ACTIVE"); control("eln.field.role", "TEXT", "REFERENCE", null, "word/document.xml"); field("type-field", "enabled", "type-version"); version("type-version", "type-field", "PUBLISHED", "类型字段", null); binding("type", "type-field", "type-version", "eln.field.type", "TEXT", "PRIMARY", null, "ACTIVE"); control("eln.field.type", "DATE", "PRIMARY", null, "word/document.xml"); field("config-field", "enabled", "config-version"); version("config-version", "config-field", "PUBLISHED", "配置字段", dropdownConfigJson("不合格")); binding("config", "config-field", "config-version", "eln.field.config", "DROPDOWN", "PRIMARY", null, "ACTIVE"); Map differentOptions = new LinkedHashMap<>(); differentOptions.put("PASS", "合格"); differentOptions.put("FAIL", "异常"); control("eln.field.config", "DROPDOWN", "PRIMARY", differentOptions, "word/document.xml"); field("ref-field", "enabled", "ref-version"); version("ref-version", "ref-field", "PUBLISHED", "引用字段", null); binding("reference", "ref-field", "ref-version", "eln.ref.reference.01", "TEXT", "REFERENCE", "missing-primary", "ACTIVE"); control("eln.ref.reference.01", "TEXT", "REFERENCE", null, "word/document.xml"); field("version-field", "enabled", "wrong-owner-version"); version("wrong-owner-version", "another-field", "PUBLISHED", "错误版本", null); binding("version-mismatch", "version-field", "wrong-owner-version", "eln.field.version_mismatch", "TEXT", "PRIMARY", null, "ACTIVE"); control("eln.field.version_mismatch", "TEXT", "PRIMARY", null, "word/document.xml"); field("disabled-field", "disabled", "disabled-version"); version("disabled-version", "disabled-field", "PUBLISHED", "停用字段", null); binding("disabled", "disabled-field", "disabled-version", "eln.field.disabled", "TEXT", "PRIMARY", null, "ACTIVE"); control("eln.field.disabled", "TEXT", "PRIMARY", null, "word/document.xml"); field("draft-field", "enabled", "draft-version"); version("draft-version", "draft-field", "DRAFT", "草稿字段", null); binding("draft", "draft-field", "draft-version", "eln.field.draft", "TEXT", "PRIMARY", null, "ACTIVE"); control("eln.field.draft", "TEXT", "PRIMARY", null, "word/document.xml"); field("pending-field", "enabled", "pending-version"); version("pending-version", "pending-field", "PUBLISHED", "待确认字段", null); binding("pending", "pending-field", "pending-version", "eln.field.pending", "TEXT", "PRIMARY", null, "PENDING"); control("eln.field.pending", "TEXT", "PRIMARY", null, "word/document.xml"); binding("cancelled", "normal-field", "normal-version", "eln.field.cancelled", "TEXT", "PRIMARY", null, "CANCELLED"); control("eln.field.orphan", "TEXT", "PRIMARY", null, "word/header1.xml"); } private void repeatBinding(String id, String fieldCode, String versionId) { configureGroup("samples"); field(id + "-field", "enabled", versionId); version(versionId, id + "-field", "PUBLISHED", fieldCode, null); binding(id, id + "-field", versionId, "eln.repeat.samples.prototype." + fieldCode, "TEXT", "REPEAT", null, "ACTIVE"); ElnTemplateBindingEntity item = bindings.get(bindings.size() - 1); item.setZiduanBianma(fieldCode); item.setRepeatGroupCode("samples"); item.setRepeatPrototypeRowId("prototype"); } private void checkboxBinding(String id, String groupCode, String stepId, boolean defaultChecked) { if (groupCode != null && checkboxGroups.stream().noneMatch( item -> groupCode.equals(item.getGroupCode()))) { configureCheckboxGroup(groupCode, 0, 1); } String fieldId = id + "-field"; String versionId = id + "-version"; field(fieldId, "enabled", versionId); version(versionId, fieldId, "PUBLISHED", id, "{\"defaultChecked\":" + defaultChecked + "}"); versions.get(versionId).setShujuLeixing("CHECKBOX"); binding(id, fieldId, versionId, "eln.field." + id, "CHECKBOX", "PRIMARY", null, "ACTIVE"); ElnTemplateBindingEntity item = bindings.get(bindings.size() - 1); item.setCheckboxGroupCode(groupCode); item.setStepId(stepId); control(item.getKongjianBiaoqian(), "CHECKBOX", "PRIMARY", Collections.singletonMap("defaultChecked", defaultChecked), "word/document.xml"); } private void repeatCheckboxBinding(String id, String checkboxGroupCode, String repeatGroupCode, boolean defaultChecked) { if (checkboxGroupCode != null && checkboxGroups.stream().noneMatch( item -> checkboxGroupCode.equals(item.getGroupCode()))) { configureCheckboxGroup(checkboxGroupCode, 0, 1); } configureGroup(repeatGroupCode); String fieldId = id + "-field"; String versionId = id + "-version"; String tag = "eln.repeat." + repeatGroupCode + ".prototype." + id; field(fieldId, "enabled", versionId); version(versionId, fieldId, "PUBLISHED", id, "{\"defaultChecked\":" + defaultChecked + "}"); versions.get(versionId).setShujuLeixing("CHECKBOX"); binding(id, fieldId, versionId, tag, "CHECKBOX", "REPEAT", null, "ACTIVE"); ElnTemplateBindingEntity item = bindings.get(bindings.size() - 1); item.setCheckboxGroupCode(checkboxGroupCode); item.setRepeatGroupCode(repeatGroupCode); item.setRepeatPrototypeRowId("prototype"); controls.add(new DocxControlSnapshot(tag, null, null, "CHECKBOX", "REPEAT", "word/document.xml", "OK", Collections.singletonMap("defaultChecked", defaultChecked), null, 0, repeatGroups.size(), false, false)); } private void configureGroup(String groupCode) { if (repeatGroups.stream().anyMatch(item -> groupCode.equals(item.getGroupCode()))) { return; } ElnTemplateVos.RepeatGroup group = new ElnTemplateVos.RepeatGroup(); group.setGroupCode(groupCode); group.setMinRows(0); group.setMaxRows(100); repeatGroups.add(group); } private void configureCheckboxGroup(String groupCode, Integer minSelected, Integer maxSelected) { checkboxGroups.removeIf(item -> groupCode.equals(item.getGroupCode())); ElnTemplateVos.CheckboxGroup group = new ElnTemplateVos.CheckboxGroup(); group.setGroupCode(groupCode); group.setMinSelected(minSelected); group.setMaxSelected(maxSelected); group.setVersion(0); checkboxGroups.add(group); } private void repeatControl(String tag, int tableIndex, int rowIndex, boolean nested, boolean verticalMerge) { controls.add(new DocxControlSnapshot(tag, null, null, "TEXT", "REPEAT", "word/document.xml", "OK", Collections.emptyMap(), null, tableIndex, rowIndex, nested, verticalMerge)); } private void nestedControl(String tag, String type, String role) { controls.add(new DocxControlSnapshot(tag, null, null, type, role, "word/document.xml", "OK", Collections.emptyMap(), null, null, null, false, false, true)); } private void field(String id, String enabled, String currentVersion) { ElnFieldDefEntity item = new ElnFieldDefEntity(); item.setId(id); item.setFTenantId("tenant-a"); item.setBizEnabled(enabled); item.setDangqianZiduanBanbenId(currentVersion); fields.put(id, item); } private void version(String id, String fieldId, String status, String name, String config) { ElnFieldVersionEntity item = new ElnFieldVersionEntity(); item.setId(id); item.setFTenantId("tenant-a"); item.setZiduanDingyiId(fieldId); item.setBizStatus(status); item.setZiduanMingcheng(name); item.setKongjianPeizhi(config); versions.put(id, item); } private void binding(String id, String fieldId, String versionId, String tag, String type, String role, String primaryId, String status) { ElnTemplateBindingEntity item = new ElnTemplateBindingEntity(); item.setId(id); item.setFTenantId("tenant-a"); item.setMubanId("template-1"); item.setZiduanDingyiId(fieldId); item.setZiduanBanbenId(versionId); item.setKongjianBiaoqian(tag); item.setKongjianLeixing(type); item.setKongjianJuese(role); item.setYinyongShiliId(primaryId); item.setBangdingZhuangtai(status); bindings.add(item); } private void control(String tag, String type, String role, Map config, String part) { controls.add(new DocxControlSnapshot(tag, null, null, type, role, part, "OK", config)); } @Override public ElnTemplateVersionEntity findTemplate(String tenantId, String templateId) { return tenantId.equals(template.getFTenantId()) && templateId.equals(template.getId()) ? template : null; } @Override public List listBindings(String tenantId, String templateId) { bindingReads++; return new ArrayList<>(bindings); } @Override public ElnFieldDefEntity findField(String tenantId, String fieldId) { ElnFieldDefEntity item = fields.get(fieldId); return item != null && tenantId.equals(item.getFTenantId()) ? item : null; } @Override public ElnFieldVersionEntity findFieldVersion(String tenantId, String versionId) { ElnFieldVersionEntity item = versions.get(versionId); return item != null && tenantId.equals(item.getFTenantId()) ? item : null; } } private static ElnTemplateHealthService construct(ElnTemplateHealthServiceImpl.Store store, ElnTemplateDocumentEvidence evidence, ElnTemplateHealthServiceImpl.PendingReconciler reconciler, java.util.function.Supplier tenantSupplier, List validators, ElnTemplateHealthServiceImpl.RepeatGroupReader repeatGroupReader, ElnTemplateHealthServiceImpl.CheckboxGroupReader checkboxGroupReader) { try { Constructor constructor = ElnTemplateHealthServiceImpl.class.getDeclaredConstructor( ElnTemplateHealthServiceImpl.Store.class, ElnTemplateDocumentEvidence.class, ElnTemplateHealthServiceImpl.PendingReconciler.class, java.util.function.Supplier.class, List.class, ElnTemplateHealthServiceImpl.RepeatGroupReader.class, ElnTemplateHealthServiceImpl.CheckboxGroupReader.class); constructor.setAccessible(true); return constructor.newInstance(store, evidence, reconciler, tenantSupplier, validators, repeatGroupReader, checkboxGroupReader); } catch (ReflectiveOperationException e) { throw new AssertionError(e); } } }