diff --git a/lab-service/lab-lims/pom.xml b/lab-service/lab-lims/pom.xml index c34a01e..a382e75 100644 --- a/lab-service/lab-lims/pom.xml +++ b/lab-service/lab-lims/pom.xml @@ -271,6 +271,11 @@ org.apache.maven.plugins maven-antrun-plugin + + org.apache.maven.plugins + maven-surefire-plugin + 2.22.2 + diff --git a/lab-service/lab-lims/src/main/java/org/springblade/lims/controller/ReagentFormulaController.java b/lab-service/lab-lims/src/main/java/org/springblade/lims/controller/ReagentFormulaController.java index 4c5e2f8..0efe4d7 100644 --- a/lab-service/lab-lims/src/main/java/org/springblade/lims/controller/ReagentFormulaController.java +++ b/lab-service/lab-lims/src/main/java/org/springblade/lims/controller/ReagentFormulaController.java @@ -15,10 +15,12 @@ import org.springblade.lims.entry.ExamineItem; import org.springblade.lims.entry.Reagent; import org.springblade.lims.entry.ReagentFormula; import org.springblade.lims.entry.TemplateField; +import org.springblade.lims.entry.OriginalRecordTemplate; import org.springblade.lims.service.IExamineItemService; import org.springblade.lims.service.IReagentFormulaService; import org.springblade.lims.service.IReagentService; import org.springblade.lims.service.ITemplateFieldService; +import org.springblade.lims.service.IOriginalRecordTemplateService; import org.springblade.lims.utils.FormulaValidationTool; import org.springframework.web.bind.annotation.*; @@ -45,6 +47,7 @@ public class ReagentFormulaController extends BladeController { private final IReagentService reagentService; private final IExamineItemService examineItemService; private final ITemplateFieldService templateFieldService; + private final IOriginalRecordTemplateService originalRecordTemplateService; /** * 分页查询 @@ -130,22 +133,35 @@ public class ReagentFormulaController extends BladeController { */ @GetMapping("/variables") @ApiOperation(value = "获取可用变量", notes = "根据试剂ID获取公式可用变量") - public R> getVariables(@ApiParam(value = "试剂ID") @RequestParam Long reagentId) { + public R>> getVariables(@ApiParam(value = "试剂ID") @RequestParam Long reagentId) { + if (reagentId == null) { + return R.data(new ArrayList<>()); + } + // 1. 查找所有使用该试剂的检验项(reagentId 为逗号分隔字符串,使用 LIKE 匹配) LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.like(ExamineItem::getReagentId, reagentId.toString()); List items = examineItemService.list(wrapper); - // 2. 从有模板的检验项中收集模板字段名 - Set variables = new LinkedHashSet<>(); + // 2. 从有模板的检验项中收集模板字段(返回 name/type/description 对象格式) + Map> variableMap = new LinkedHashMap<>(); boolean hasTemplateFields = false; for (ExamineItem item : items) { - if (item.getTemplateId() != null) { - List fields = templateFieldService.getFieldsByTemplateId(item.getTemplateId()); + List templates = originalRecordTemplateService.getByExamineItemId(item.getId()); + if (templates != null && !templates.isEmpty()) { + OriginalRecordTemplate template = templates.get(0); + List fields = templateFieldService.getFieldsByTemplateIdAndType(template.getId(), "number"); if (fields != null && !fields.isEmpty()) { hasTemplateFields = true; for (TemplateField field : fields) { - variables.add(field.getFieldName()); + String name = field.getFieldName(); + if (!variableMap.containsKey(name)) { + Map v = new HashMap<>(3); + v.put("name", name); + v.put("type", field.getFieldType() != null ? field.getFieldType() : "number"); + v.put("description", name); + variableMap.put(name, v); + } } } } @@ -158,12 +174,15 @@ public class ReagentFormulaController extends BladeController { String method = reagent.getResultDeterminationMethod(); List> oldVars = extractVariables(method); for (Map v : oldVars) { - variables.add((String) v.get("name")); + String name = (String) v.get("name"); + if (name != null && !variableMap.containsKey(name)) { + variableMap.put(name, v); + } } } } - return R.data(new ArrayList<>(variables)); + return R.data(new ArrayList<>(variableMap.values())); } /** diff --git a/lab-service/lab-lims/src/main/java/org/springblade/lims/service/IOriginalRecordTemplateService.java b/lab-service/lab-lims/src/main/java/org/springblade/lims/service/IOriginalRecordTemplateService.java index c077118..b6f2a62 100644 --- a/lab-service/lab-lims/src/main/java/org/springblade/lims/service/IOriginalRecordTemplateService.java +++ b/lab-service/lab-lims/src/main/java/org/springblade/lims/service/IOriginalRecordTemplateService.java @@ -18,4 +18,12 @@ public interface IOriginalRecordTemplateService extends BaseService> listWithFieldCount(); + + /** + * 根据检测项目ID获取关联的原始记录模板列表 + * + * @param examineItemId 检测项目ID + * @return 原始记录模板列表 + */ + List getByExamineItemId(Long examineItemId); } diff --git a/lab-service/lab-lims/src/main/java/org/springblade/lims/service/ITemplateFieldService.java b/lab-service/lab-lims/src/main/java/org/springblade/lims/service/ITemplateFieldService.java index d97c60b..46145f7 100644 --- a/lab-service/lab-lims/src/main/java/org/springblade/lims/service/ITemplateFieldService.java +++ b/lab-service/lab-lims/src/main/java/org/springblade/lims/service/ITemplateFieldService.java @@ -21,6 +21,15 @@ public interface ITemplateFieldService extends BaseService { */ List getFieldsByTemplateId(Long templateId); + /** + * 根据模板ID和字段类型获取字段列表 + * + * @param templateId 模板ID(为null时查询字段库) + * @param fieldType 字段类型(如"number") + * @return 字段列表 + */ + List getFieldsByTemplateIdAndType(Long templateId, String fieldType); + /** * 保存字段 * diff --git a/lab-service/lab-lims/src/main/java/org/springblade/lims/service/impl/OriginalRecordTemplateServiceImpl.java b/lab-service/lab-lims/src/main/java/org/springblade/lims/service/impl/OriginalRecordTemplateServiceImpl.java index a4ac504..19c3df4 100644 --- a/lab-service/lab-lims/src/main/java/org/springblade/lims/service/impl/OriginalRecordTemplateServiceImpl.java +++ b/lab-service/lab-lims/src/main/java/org/springblade/lims/service/impl/OriginalRecordTemplateServiceImpl.java @@ -54,4 +54,12 @@ public class OriginalRecordTemplateServiceImpl extends BaseServiceImpl getByExamineItemId(Long examineItemId) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(OriginalRecordTemplate::getExamineItemId, examineItemId) + .eq(OriginalRecordTemplate::getIsDeleted, 0); + return baseMapper.selectList(queryWrapper); + } } diff --git a/lab-service/lab-lims/src/main/java/org/springblade/lims/service/impl/TemplateFieldServiceImpl.java b/lab-service/lab-lims/src/main/java/org/springblade/lims/service/impl/TemplateFieldServiceImpl.java index 174bf4c..6b066f9 100644 --- a/lab-service/lab-lims/src/main/java/org/springblade/lims/service/impl/TemplateFieldServiceImpl.java +++ b/lab-service/lab-lims/src/main/java/org/springblade/lims/service/impl/TemplateFieldServiceImpl.java @@ -43,6 +43,23 @@ public class TemplateFieldServiceImpl extends BaseServiceImpl getFieldsByTemplateIdAndType(Long templateId, String fieldType) { + if (templateId == null) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(TemplateField::getIsDeleted, 0) + .eq(TemplateField::getFieldType, fieldType); + return baseMapper.selectList(queryWrapper); + } + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(TemplateField::getIsDeleted, 0) + .eq(TemplateField::getFieldType, fieldType); + queryWrapper.inSql(TemplateField::getId, + "SELECT m.field_id FROM t_template_field_mapping m WHERE m.template_id = " + templateId + " ORDER BY m.sort_order ASC" + ); + return baseMapper.selectList(queryWrapper); + } + @Override public boolean saveField(TemplateField field) { return baseMapper.insert(field) > 0; diff --git a/lab-service/lab-lims/src/test/java/org/springblade/lims/AssignRuleControllerTest.java b/lab-service/lab-lims/src/test/java/org/springblade/lims/AssignRuleControllerTest.java index 2dc8904..44341ca 100644 --- a/lab-service/lab-lims/src/test/java/org/springblade/lims/AssignRuleControllerTest.java +++ b/lab-service/lab-lims/src/test/java/org/springblade/lims/AssignRuleControllerTest.java @@ -2,15 +2,20 @@ package org.springblade.lims; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; import org.springblade.lims.controller.AssignRuleController; import org.springblade.lims.entry.AssignRule; import org.springblade.lims.service.IAssignRuleService; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; -import org.springframework.boot.test.mock.mockito.MockBean; +import org.springblade.lims.service.IExamineService; +import org.springblade.system.feign.ISysClient; import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; import java.util.Collections; import java.util.HashMap; @@ -23,18 +28,31 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -@WebMvcTest(AssignRuleController.class) +@ExtendWith(MockitoExtension.class) class AssignRuleControllerTest { - @Autowired private MockMvc mockMvc; - @Autowired private ObjectMapper objectMapper; - @MockBean + @Mock private IAssignRuleService service; + @Mock + private IExamineService examineService; + + @Mock + private ISysClient sysClient; + + @InjectMocks + private AssignRuleController controller; + + @BeforeEach + void setUp() { + objectMapper = new ObjectMapper(); + mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); + } + @Test void testInsert() throws Exception { AssignRule rule = new AssignRule(); diff --git a/lab-service/lab-lims/src/test/java/org/springblade/lims/controller/ReagentFormulaControllerTest.java b/lab-service/lab-lims/src/test/java/org/springblade/lims/controller/ReagentFormulaControllerTest.java new file mode 100644 index 0000000..12b2142 --- /dev/null +++ b/lab-service/lab-lims/src/test/java/org/springblade/lims/controller/ReagentFormulaControllerTest.java @@ -0,0 +1,140 @@ +package org.springblade.lims.controller; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springblade.lims.entry.ExamineItem; +import org.springblade.lims.entry.OriginalRecordTemplate; +import org.springblade.lims.entry.Reagent; +import org.springblade.lims.entry.TemplateField; +import org.springblade.lims.service.IExamineItemService; +import org.springblade.lims.service.IOriginalRecordTemplateService; +import org.springblade.lims.service.IReagentFormulaService; +import org.springblade.lims.service.IReagentService; +import org.springblade.lims.service.ITemplateFieldService; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +import java.util.Arrays; +import java.util.Collections; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@ExtendWith(MockitoExtension.class) +class ReagentFormulaControllerTest { + + private MockMvc mockMvc; + + @Mock + private IReagentFormulaService reagentFormulaService; + + @Mock + private IReagentService reagentService; + + @Mock + private IExamineItemService examineItemService; + + @Mock + private ITemplateFieldService templateFieldService; + + @Mock + private IOriginalRecordTemplateService originalRecordTemplateService; + + @InjectMocks + private ReagentFormulaController controller; + + @BeforeEach + void setUp() { + mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); + } + + @Test + void templatePath_ShouldReturnNumberTypeFieldNames() throws Exception { + ExamineItem item = new ExamineItem(); + item.setId(1L); + item.setReagentId("5"); + + OriginalRecordTemplate template = new OriginalRecordTemplate(); + template.setId(10L); + + TemplateField field1 = new TemplateField(); + field1.setFieldName("OD值"); + field1.setFieldType("number"); + TemplateField field2 = new TemplateField(); + field2.setFieldName("临界值"); + field2.setFieldType("number"); + + when(examineItemService.list(any())).thenReturn(Collections.singletonList(item)); + when(originalRecordTemplateService.getByExamineItemId(1L)).thenReturn(Collections.singletonList(template)); + when(templateFieldService.getFieldsByTemplateIdAndType(10L, "number")) + .thenReturn(Arrays.asList(field1, field2)); + + mockMvc.perform(get("/reagentFormula/variables") + .param("reagentId", "5")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(200)) + .andExpect(jsonPath("$.data").isArray()) + .andExpect(jsonPath("$.data.length()").value(2)) + .andExpect(jsonPath("$.data[0].name").value("OD值")) + .andExpect(jsonPath("$.data[0].type").value("number")) + .andExpect(jsonPath("$.data[1].name").value("临界值")) + .andExpect(jsonPath("$.data[1].type").value("number")); + } + + @Test + void fallback_WhenNoTemplateFields_ShouldUseResultDeterminationMethod() throws Exception { + ExamineItem item = new ExamineItem(); + item.setId(1L); + item.setReagentId("5"); + + OriginalRecordTemplate template = new OriginalRecordTemplate(); + template.setId(10L); + + Reagent reagent = new Reagent(); + reagent.setResultDeterminationMethod( + "{\"variables\":[{\"name\":\"OD值\",\"type\":\"number\"},{\"name\":\"临界值\",\"type\":\"number\"}]}" + ); + + when(examineItemService.list(any())).thenReturn(Collections.singletonList(item)); + when(originalRecordTemplateService.getByExamineItemId(1L)).thenReturn(Collections.singletonList(template)); + when(templateFieldService.getFieldsByTemplateIdAndType(10L, "number")) + .thenReturn(Collections.emptyList()); + when(reagentService.getById(5L)).thenReturn(reagent); + + mockMvc.perform(get("/reagentFormula/variables") + .param("reagentId", "5")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(200)) + .andExpect(jsonPath("$.data").isArray()) + .andExpect(jsonPath("$.data.length()").value(2)) + .andExpect(jsonPath("$.data[0].name").value("OD值")) + .andExpect(jsonPath("$.data[0].type").value("number")) + .andExpect(jsonPath("$.data[1].name").value("临界值")) + .andExpect(jsonPath("$.data[1].type").value("number")); + } + + @Test + void nullReagentId_ShouldReturn400() throws Exception { + mockMvc.perform(get("/reagentFormula/variables")) + .andExpect(status().isBadRequest()); + } + + @Test + void noMatchingItems_ShouldReturnEmptyList() throws Exception { + when(examineItemService.list(any())).thenReturn(Collections.emptyList()); + + mockMvc.perform(get("/reagentFormula/variables") + .param("reagentId", "999")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(200)) + .andExpect(jsonPath("$.data").isArray()) + .andExpect(jsonPath("$.data.length()").value(0)); + } +}