package jnpf.elnService.template.impl; import jnpf.elnEntity.template.ElnTemplateStageEntity; import jnpf.elnEntity.template.ElnTemplateStepEntity; import jnpf.elnEntity.template.ElnTemplateForms; import jnpf.elnEntity.template.ElnTemplateBindingEntity; import jnpf.elnEntity.template.ElnTemplateRepeatGroupEntity; import jnpf.elnEntity.template.ElnTemplateVersionEntity; import jnpf.elnEntity.template.ElnTemplateVos; import org.junit.Test; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; public class ElnTemplateStepServiceImplTest { @Test public void flattensLegacyStagesIntoStableGlobalStepOrder() { MemoryStore store = new MemoryStore(); store.template = template("template-1", "DRAFT"); store.stages.add(stage("stage-2", "样品处理", 2)); store.stages.add(stage("stage-1", "实验准备", 1)); store.steps.add(step("step-3", "stage-2", "结果记录", 1)); store.steps.add(step("step-2", "stage-1", "环境确认", 2)); store.steps.add(step("step-1", "stage-1", "仪器准备", 1)); store.steps.add(step("step-4", null, "未分阶段记录", 1)); ElnTemplateStepServiceImpl service = new ElnTemplateStepServiceImpl( store, () -> new ElnTemplateStepServiceImpl.UserContext("tenant-1", "user-1")); ElnTemplateVos.StepConfiguration result = service.configuration("template-1"); assertFalse(result.isSingleStep()); assertEquals(Arrays.asList("step-1", "step-2", "step-3", "step-4"), stepIds(result.getSteps())); assertEquals(Integer.valueOf(1), result.getSteps().get(0).getOrder()); assertEquals(Integer.valueOf(2), result.getSteps().get(1).getOrder()); assertEquals(Integer.valueOf(3), result.getSteps().get(2).getOrder()); } @Test public void rejectsTemplateWithoutAnyStep() { MemoryStore store = new MemoryStore(); store.template = template("template-1", "DRAFT"); ElnTemplateStepServiceImpl service = new ElnTemplateStepServiceImpl( store, () -> new ElnTemplateStepServiceImpl.UserContext("tenant-1", "user-1")); try { service.configuration("template-1"); fail("每个模板版本必须至少有一个步骤"); } catch (jnpf.exception.DataException expected) { assertEquals("模板版本未配置步骤", expected.getMessage()); } } @Test public void aggregatesEffectiveControlAndRepeatGroupOwnershipForFill() { MemoryStore store = new MemoryStore(); store.template = template("template-1", "PUBLISHED"); store.steps.add(step("step-1", null, "准备", 1)); store.steps.add(step("step-2", null, "记录", 2)); ElnTemplateBindingEntity primary = binding("primary-1", "template-1", "step-1"); primary.setKongjianBiaoqian("eln.field.result"); ElnTemplateBindingEntity reference = binding("reference-1", "template-1", null); reference.setKongjianJuese("REFERENCE"); reference.setYinyongShiliId("primary-1"); reference.setKongjianBiaoqian("eln.ref.result.1"); ElnTemplateBindingEntity repeat = binding("repeat-1", "template-1", null); repeat.setKongjianJuese("REPEAT"); repeat.setRepeatGroupCode("samples"); repeat.setKongjianBiaoqian("eln.repeat.samples.prototype.result"); store.bindings.add(primary); store.bindings.add(reference); store.bindings.add(repeat); store.repeatGroups.add(repeatGroup( "group-1", "template-1", "samples", "step-2")); ElnTemplateStepServiceImpl service = new ElnTemplateStepServiceImpl( store, () -> new ElnTemplateStepServiceImpl.UserContext("tenant-1", "user-1")); ElnTemplateVos.StepFillStructure result = service.fillStructure( "tenant-1", "template-1"); assertFalse(result.getConfiguration().isSingleStep()); assertEquals("step-1", result.getControlSteps().get("eln.field.result")); assertEquals("step-1", result.getControlSteps().get("eln.ref.result.1")); assertEquals("step-2", result.getControlSteps().get( "eln.repeat.samples.prototype.result")); assertEquals("step-2", result.getRepeatGroupSteps().get("samples")); } @Test public void savesOrderedDraftStepsWithoutRebuildingExistingIdentity() { MemoryStore store = new MemoryStore(); store.template = template("template-1", "DRAFT"); store.steps.add(step("step-1", null, "原步骤", 1)); ElnTemplateStepServiceImpl service = new ElnTemplateStepServiceImpl( store, () -> new ElnTemplateStepServiceImpl.UserContext("tenant-1", "user-1"), new SequenceIds()); ElnTemplateForms.SaveStepConfiguration form = new ElnTemplateForms.SaveStepConfiguration(); ElnTemplateForms.Step existing = new ElnTemplateForms.Step(); existing.setId("step-1"); existing.setName("准备"); ElnTemplateForms.Step added = new ElnTemplateForms.Step(); added.setName("检测"); form.setSteps(Arrays.asList(existing, added)); ElnTemplateVos.StepConfiguration result = service.save("template-1", form); assertEquals(Arrays.asList("step-1", "generated-1"), stepIds(result.getSteps())); assertEquals(Arrays.asList("准备", "检测"), stepNames(result.getSteps())); assertEquals(Integer.valueOf(1), result.getSteps().get(0).getOrder()); assertEquals(Integer.valueOf(2), result.getSteps().get(1).getOrder()); } @Test public void releasesActiveUniqueSlotsBeforeSwappingLegacyStagedSteps() { MemoryStore store = new MemoryStore(); store.template = template("template-1", "DRAFT"); store.stages.add(stage("stage-1", "阶段甲", 1)); store.stages.add(stage("stage-2", "阶段乙", 2)); store.steps.add(step("step-1", "stage-1", "准备", 1)); store.steps.add(step("step-2", "stage-2", "检测", 2)); ElnTemplateStepServiceImpl service = new ElnTemplateStepServiceImpl( store, () -> new ElnTemplateStepServiceImpl.UserContext("tenant-1", "user-1")); ElnTemplateForms.SaveStepConfiguration form = new ElnTemplateForms.SaveStepConfiguration(); ElnTemplateForms.Step firstStep = new ElnTemplateForms.Step(); firstStep.setId("step-2"); firstStep.setName("准备"); ElnTemplateForms.Step secondStep = new ElnTemplateForms.Step(); secondStep.setId("step-1"); secondStep.setName("检测"); form.setSteps(Arrays.asList(firstStep, secondStep)); ElnTemplateVos.StepConfiguration result = service.save("template-1", form); assertTrue(store.stepUniquenessReleased); assertEquals(Arrays.asList("step-2", "step-1"), stepIds(result.getSteps())); assertNull(store.steps.get(0).getJieduanId()); assertNull(store.steps.get(1).getJieduanId()); } @Test public void migratesPrimaryBindingsAndRepeatGroupsBeforeDeletingStep() { MemoryStore store = new MemoryStore(); store.template = template("template-1", "DRAFT"); store.steps.add(step("step-1", null, "旧步骤", 1)); store.steps.add(step("step-2", null, "目标步骤", 2)); ElnTemplateBindingEntity primary = new ElnTemplateBindingEntity(); primary.setId("binding-1"); primary.setMubanId("template-1"); primary.setKongjianJuese("PRIMARY"); primary.setBangdingZhuangtai("ACTIVE"); primary.setStepId("step-1"); primary.setFDeleteMark(0); store.bindings.add(primary); ElnTemplateRepeatGroupEntity group = new ElnTemplateRepeatGroupEntity(); group.setId("group-1"); group.setMubanId("template-1"); group.setRepeatGroupCode("results"); group.setStepId("step-1"); group.setFDeleteMark(0); store.repeatGroups.add(group); ElnTemplateStepServiceImpl service = new ElnTemplateStepServiceImpl( store, () -> new ElnTemplateStepServiceImpl.UserContext("tenant-1", "user-1"), new SequenceIds()); ElnTemplateForms.SaveStepConfiguration form = new ElnTemplateForms.SaveStepConfiguration(); ElnTemplateForms.Step kept = new ElnTemplateForms.Step(); kept.setId("step-2"); kept.setName("目标步骤"); form.setSteps(Collections.singletonList(kept)); Map migrations = new LinkedHashMap<>(); migrations.put("step-1", "step-2"); form.setStepMigrations(migrations); ElnTemplateVos.StepConfiguration result = service.save("template-1", form); assertEquals(Collections.singletonList("step-2"), stepIds(result.getSteps())); assertEquals("step-2", primary.getStepId()); assertEquals("step-2", group.getStepId()); assertEquals(Integer.valueOf(1), store.steps.get(0).getFDeleteMark()); } @Test public void copiesVersionStructureAndRemapsCopiedOwnership() { MemoryStore store = new MemoryStore(); ElnTemplateStageEntity sourceStage = stage("source-stage", "检测", 1); sourceStage.setMubanId("source-template"); store.stages.add(sourceStage); ElnTemplateStepEntity sourceFirst = step("source-step-1", "source-stage", "准备", 1); sourceFirst.setMubanId("source-template"); ElnTemplateStepEntity sourceSecond = step("source-step-2", "source-stage", "记录", 2); sourceSecond.setMubanId("source-template"); store.steps.add(sourceFirst); store.steps.add(sourceSecond); ElnTemplateStepEntity targetDefault = step("target-default", null, "实验步骤", 1); targetDefault.setMubanId("target-template"); store.steps.add(targetDefault); ElnTemplateBindingEntity sourceBinding = binding( "source-binding", "source-template", "source-step-1"); ElnTemplateBindingEntity targetBinding = binding( "target-binding", "target-template", null); targetBinding.setLaiyuanBangdingId("source-binding"); store.bindings.add(sourceBinding); store.bindings.add(targetBinding); ElnTemplateRepeatGroupEntity sourceGroup = repeatGroup( "source-group", "source-template", "results", "source-step-2"); ElnTemplateRepeatGroupEntity targetGroup = repeatGroup( "target-group", "target-template", "results", null); store.repeatGroups.add(sourceGroup); store.repeatGroups.add(targetGroup); ElnTemplateStepServiceImpl service = new ElnTemplateStepServiceImpl( store, () -> new ElnTemplateStepServiceImpl.UserContext("tenant-1", "user-1"), new SequenceIds()); service.copyForVersion("tenant-1", "source-template", "target-template"); assertEquals("target-default", targetBinding.getStepId()); assertEquals("generated-1", targetGroup.getStepId()); assertEquals("source-step-1", targetDefault.getLaiyuanBuzhouId()); assertNull(targetDefault.getJieduanId()); assertEquals(Integer.valueOf(1), targetDefault.getPaixu()); } @Test public void publishValidationRejectsUnassignedPrimaryAndEmptyStep() { MemoryStore store = new MemoryStore(); store.steps.add(step("step-1", null, "准备", 1)); store.steps.add(step("step-2", null, "记录", 2)); store.bindings.add(binding("binding-1", "template-1", null)); store.repeatGroups.add(repeatGroup( "group-1", "template-1", "results", "step-2")); ElnTemplateStepServiceImpl service = new ElnTemplateStepServiceImpl( store, () -> new ElnTemplateStepServiceImpl.UserContext("tenant-1", "user-1"), new SequenceIds()); List diagnostics = service.validate( "tenant-1", "template-1", null); assertTrue(codes(diagnostics).contains("UNASSIGNED_PRIMARY_BINDING")); assertTrue(codes(diagnostics).contains("EMPTY_STEP")); } @Test public void resolvesMissingAssignmentOnlyForSingleStepTemplate() { MemoryStore store = new MemoryStore(); store.steps.add(step("step-1", null, "实验步骤", 1)); ElnTemplateStepServiceImpl service = new ElnTemplateStepServiceImpl( store, () -> new ElnTemplateStepServiceImpl.UserContext("tenant-1", "user-1"), new SequenceIds()); assertEquals("step-1", service.resolveAssignment( "tenant-1", "template-1", null)); store.steps.add(step("step-2", null, "记录", 2)); try { service.resolveAssignment("tenant-1", "template-1", null); fail("多步骤模板必须显式选择步骤"); } catch (jnpf.exception.DataException expected) { assertEquals("多步骤模板必须选择当前步骤", expected.getMessage()); } } private static ElnTemplateVersionEntity template(String id, String status) { ElnTemplateVersionEntity entity = new ElnTemplateVersionEntity(); entity.setId(id); entity.setFTenantId("tenant-1"); entity.setBizStatus(status); entity.setFDeleteMark(0); return entity; } private static ElnTemplateStageEntity stage(String id, String name, int order) { ElnTemplateStageEntity entity = new ElnTemplateStageEntity(); entity.setId(id); entity.setFTenantId("tenant-1"); entity.setMubanId("template-1"); entity.setJieduanMingcheng(name); entity.setPaixu(order); entity.setFDeleteMark(0); return entity; } private static ElnTemplateStepEntity step(String id, String stageId, String name, int order) { ElnTemplateStepEntity entity = new ElnTemplateStepEntity(); entity.setId(id); entity.setFTenantId("tenant-1"); entity.setMubanId("template-1"); entity.setJieduanId(stageId); entity.setBuzhouMingcheng(name); entity.setPaixu(order); entity.setFDeleteMark(0); return entity; } private static ElnTemplateBindingEntity binding( String id, String templateId, String stepId) { ElnTemplateBindingEntity entity = new ElnTemplateBindingEntity(); entity.setId(id); entity.setFTenantId("tenant-1"); entity.setMubanId(templateId); entity.setKongjianJuese("PRIMARY"); entity.setBangdingZhuangtai("ACTIVE"); entity.setStepId(stepId); entity.setFDeleteMark(0); return entity; } private static ElnTemplateRepeatGroupEntity repeatGroup( String id, String templateId, String code, String stepId) { ElnTemplateRepeatGroupEntity entity = new ElnTemplateRepeatGroupEntity(); entity.setId(id); entity.setFTenantId("tenant-1"); entity.setMubanId(templateId); entity.setRepeatGroupCode(code); entity.setStepId(stepId); entity.setFDeleteMark(0); return entity; } private static List stepIds(List steps) { List result = new ArrayList<>(); for (ElnTemplateVos.Step step : steps) result.add(step.getId()); return result; } private static List stepNames(List steps) { List result = new ArrayList<>(); for (ElnTemplateVos.Step step : steps) result.add(step.getName()); return result; } private static List codes(List diagnostics) { List result = new ArrayList<>(); for (ElnTemplateVos.Diagnostic item : diagnostics) result.add(item.getCode()); return result; } private static final class SequenceIds implements java.util.function.Supplier { private int next; @Override public String get() { return "generated-" + (++next); } } private static final class MemoryStore implements ElnTemplateStepServiceImpl.Store { private ElnTemplateVersionEntity template; private final List stages = new ArrayList<>(); private final List steps = new ArrayList<>(); private final List bindings = new ArrayList<>(); private final List repeatGroups = new ArrayList<>(); private boolean stepUniquenessReleased; @Override public ElnTemplateVersionEntity findTemplate(String tenantId, String templateId) { return template; } @Override public ElnTemplateVersionEntity findTemplateForUpdate( String tenantId, String templateId) { return template; } @Override public List listStages(String tenantId, String templateId) { List result = new ArrayList<>(); for (ElnTemplateStageEntity stage : stages) { if (templateId.equals(stage.getMubanId()) && !Integer.valueOf(1).equals(stage.getFDeleteMark())) result.add(stage); } return result; } @Override public List listSteps(String tenantId, String templateId) { List result = new ArrayList<>(); for (ElnTemplateStepEntity step : steps) { if (templateId.equals(step.getMubanId()) && !Integer.valueOf(1).equals(step.getFDeleteMark())) result.add(step); } return result; } @Override public List listBindings( String tenantId, String templateId) { List result = new ArrayList<>(); for (ElnTemplateBindingEntity binding : bindings) { if (templateId.equals(binding.getMubanId())) result.add(binding); } return result; } @Override public List listRepeatGroups( String tenantId, String templateId) { List result = new ArrayList<>(); for (ElnTemplateRepeatGroupEntity group : repeatGroups) { if (templateId.equals(group.getMubanId())) result.add(group); } return result; } @Override public void releaseStepUniqueness(String tenantId, String templateId) { stepUniquenessReleased = true; } @Override public void insertStep(ElnTemplateStepEntity entity) { steps.add(entity); } @Override public void updateStep(ElnTemplateStepEntity entity) { for (int i = 0; i < steps.size(); i++) { if (steps.get(i).getId().equals(entity.getId())) { steps.set(i, entity); return; } } } @Override public void updateBindingStep(ElnTemplateBindingEntity entity) { } @Override public void updateRepeatGroupStep(ElnTemplateRepeatGroupEntity entity) { } } }