feat(kg): 推理引擎+数据导入

This commit is contained in:
2026-06-19 10:33:41 +08:00
parent 523a64daf0
commit ed1dd56ad4
6 changed files with 725 additions and 0 deletions

View File

@@ -0,0 +1,13 @@
package com.healthlink.his.web.knowledgegraph.appservice;
import com.healthlink.his.web.knowledgegraph.dto.ImportResultDto;
import org.springframework.web.multipart.MultipartFile;
public interface IKgDataImportAppService {
ImportResultDto importDiseaseFromCsv(MultipartFile file);
ImportResultDto importDrugFromCsv(MultipartFile file);
ImportResultDto importRelationsFromCsv(MultipartFile file);
}

View File

@@ -0,0 +1,17 @@
package com.healthlink.his.web.knowledgegraph.appservice;
import com.healthlink.his.web.knowledgegraph.dto.*;
import java.util.List;
import java.util.Map;
public interface IKgReasoningAppService {
List<DiagnosisResultDto> suggestDiagnosis(List<String> symptoms, Integer topN);
List<ExaminationResultDto> suggestExaminations(String diseaseCode, Integer topN);
List<DrugInteractionResultDto> checkDrugInteractions(List<String> drugCodes);
Map<String, Object> suggestPathway(String diseaseCode);
}

View File

@@ -0,0 +1,273 @@
package com.healthlink.his.web.knowledgegraph.appservice.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.healthlink.his.clinical.domain.KgEntityRelation;
import com.healthlink.his.clinical.service.IKgEntityRelationService;
import com.healthlink.his.knowledgegraph.domain.KgDisease;
import com.healthlink.his.knowledgegraph.domain.KgDrug;
import com.healthlink.his.knowledgegraph.service.IKgDiseaseService;
import com.healthlink.his.knowledgegraph.service.IKgDrugService;
import com.healthlink.his.web.knowledgegraph.appservice.IKgDataImportAppService;
import com.healthlink.his.web.knowledgegraph.dto.ImportResultDto;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
@Slf4j
@Service
public class KgDataImportAppServiceImpl implements IKgDataImportAppService {
@Autowired
private IKgDiseaseService diseaseService;
@Autowired
private IKgDrugService drugService;
@Autowired
private IKgEntityRelationService relationService;
@Override
@Transactional(rollbackFor = Exception.class)
public ImportResultDto importDiseaseFromCsv(MultipartFile file) {
ImportResultDto result = new ImportResultDto();
List<KgDisease> batch = new ArrayList<>();
int totalRows = 0;
int successCount = 0;
int failCount = 0;
try (BufferedReader reader = new BufferedReader(new InputStreamReader(file.getInputStream(), StandardCharsets.UTF_8))) {
String header = reader.readLine();
if (header == null) {
result.setMessage("CSV文件为空");
result.setTotalRows(0);
result.setSuccessCount(0);
result.setFailCount(0);
return result;
}
String line;
while ((line = reader.readLine()) != null) {
totalRows++;
try {
String[] parts = parseCsvLine(line);
if (parts.length < 2) {
failCount++;
continue;
}
KgDisease disease = new KgDisease();
disease.setDiseaseCode(getField(parts, 0));
disease.setDiseaseName(getField(parts, 1));
disease.setCategory(getField(parts, 2));
disease.setDepartment(getField(parts, 3));
disease.setSeverityLevel(getField(parts, 4));
disease.setDescription(getField(parts, 5));
disease.setKeywords(getField(parts, 6));
if (hasText(disease.getDiseaseCode()) && hasText(disease.getDiseaseName())) {
batch.add(disease);
} else {
failCount++;
}
} catch (Exception e) {
log.warn("解析疾病CSV行失败: {}", line, e);
failCount++;
}
}
if (!batch.isEmpty()) {
diseaseService.saveBatch(batch);
successCount = batch.size();
}
} catch (Exception e) {
log.error("导入疾病CSV失败", e);
failCount = totalRows - successCount;
}
result.setTotalRows(totalRows);
result.setSuccessCount(successCount);
result.setFailCount(failCount);
result.setMessage(String.format("导入完成: 共%d行, 成功%d条, 失败%d条", totalRows, successCount, failCount));
return result;
}
@Override
@Transactional(rollbackFor = Exception.class)
public ImportResultDto importDrugFromCsv(MultipartFile file) {
ImportResultDto result = new ImportResultDto();
List<KgDrug> batch = new ArrayList<>();
int totalRows = 0;
int successCount = 0;
int failCount = 0;
try (BufferedReader reader = new BufferedReader(new InputStreamReader(file.getInputStream(), StandardCharsets.UTF_8))) {
String header = reader.readLine();
if (header == null) {
result.setMessage("CSV文件为空");
result.setTotalRows(0);
result.setSuccessCount(0);
result.setFailCount(0);
return result;
}
String line;
while ((line = reader.readLine()) != null) {
totalRows++;
try {
String[] parts = parseCsvLine(line);
if (parts.length < 2) {
failCount++;
continue;
}
KgDrug drug = new KgDrug();
drug.setDrugCode(getField(parts, 0));
drug.setDrugName(getField(parts, 1));
drug.setGenericName(getField(parts, 2));
drug.setCategory(getField(parts, 3));
drug.setDosageForm(getField(parts, 4));
drug.setContraindications(getField(parts, 5));
drug.setSideEffects(getField(parts, 6));
if (hasText(drug.getDrugCode()) && hasText(drug.getDrugName())) {
batch.add(drug);
} else {
failCount++;
}
} catch (Exception e) {
log.warn("解析药物CSV行失败: {}", line, e);
failCount++;
}
}
if (!batch.isEmpty()) {
drugService.saveBatch(batch);
successCount = batch.size();
}
} catch (Exception e) {
log.error("导入药物CSV失败", e);
failCount = totalRows - successCount;
}
result.setTotalRows(totalRows);
result.setSuccessCount(successCount);
result.setFailCount(failCount);
result.setMessage(String.format("导入完成: 共%d行, 成功%d条, 失败%d条", totalRows, successCount, failCount));
return result;
}
@Override
@Transactional(rollbackFor = Exception.class)
public ImportResultDto importRelationsFromCsv(MultipartFile file) {
ImportResultDto result = new ImportResultDto();
List<KgEntityRelation> batch = new ArrayList<>();
int totalRows = 0;
int successCount = 0;
int failCount = 0;
try (BufferedReader reader = new BufferedReader(new InputStreamReader(file.getInputStream(), StandardCharsets.UTF_8))) {
String header = reader.readLine();
if (header == null) {
result.setMessage("CSV文件为空");
result.setTotalRows(0);
result.setSuccessCount(0);
result.setFailCount(0);
return result;
}
String line;
while ((line = reader.readLine()) != null) {
totalRows++;
try {
String[] parts = parseCsvLine(line);
if (parts.length < 5) {
failCount++;
continue;
}
KgEntityRelation relation = new KgEntityRelation();
relation.setSourceType(getField(parts, 0));
relation.setSourceId(getField(parts, 1));
relation.setTargetType(getField(parts, 2));
relation.setTargetId(getField(parts, 3));
relation.setRelationType(getField(parts, 4));
String strengthStr = getField(parts, 5);
if (hasText(strengthStr)) {
try {
relation.setRelationStrength(new BigDecimal(strengthStr));
} catch (NumberFormatException e) {
relation.setRelationStrength(BigDecimal.ONE);
}
} else {
relation.setRelationStrength(BigDecimal.ONE);
}
relation.setDescription(getField(parts, 6));
relation.setEvidenceSource(getField(parts, 7));
if (hasText(relation.getSourceType()) && hasText(relation.getSourceId())
&& hasText(relation.getTargetType()) && hasText(relation.getTargetId())) {
batch.add(relation);
} else {
failCount++;
}
} catch (Exception e) {
log.warn("解析关系CSV行失败: {}", line, e);
failCount++;
}
}
if (!batch.isEmpty()) {
relationService.saveBatch(batch);
successCount = batch.size();
}
} catch (Exception e) {
log.error("导入关系CSV失败", e);
failCount = totalRows - successCount;
}
result.setTotalRows(totalRows);
result.setSuccessCount(successCount);
result.setFailCount(failCount);
result.setMessage(String.format("导入完成: 共%d行, 成功%d条, 失败%d条", totalRows, successCount, failCount));
return result;
}
private String[] parseCsvLine(String line) {
List<String> fields = new ArrayList<>();
StringBuilder current = new StringBuilder();
boolean inQuotes = false;
for (char c : line.toCharArray()) {
if (c == '"') {
inQuotes = !inQuotes;
} else if (c == ',' && !inQuotes) {
fields.add(current.toString().trim());
current = new StringBuilder();
} else {
current.append(c);
}
}
fields.add(current.toString().trim());
return fields.toArray(new String[0]);
}
private String getField(String[] parts, int index) {
if (index < parts.length) {
String val = parts[index];
return hasText(val) ? val : null;
}
return null;
}
private boolean hasText(String s) {
return s != null && !s.trim().isEmpty();
}
}

View File

@@ -0,0 +1,243 @@
package com.healthlink.his.web.knowledgegraph.appservice.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.healthlink.his.clinical.domain.KgClinicalPathway;
import com.healthlink.his.clinical.domain.KgEntityRelation;
import com.healthlink.his.clinical.domain.KgPathwayStep;
import com.healthlink.his.clinical.service.IKgClinicalPathwayService;
import com.healthlink.his.clinical.service.IKgEntityRelationService;
import com.healthlink.his.clinical.service.IKgPathwayStepService;
import com.healthlink.his.knowledgegraph.domain.KgDisease;
import com.healthlink.his.knowledgegraph.domain.KgDrug;
import com.healthlink.his.knowledgegraph.domain.KgExamination;
import com.healthlink.his.knowledgegraph.domain.KgSymptom;
import com.healthlink.his.knowledgegraph.service.IKgDiseaseService;
import com.healthlink.his.knowledgegraph.service.IKgDrugService;
import com.healthlink.his.knowledgegraph.service.IKgExaminationService;
import com.healthlink.his.knowledgegraph.service.IKgSymptomService;
import com.healthlink.his.web.knowledgegraph.appservice.IKgReasoningAppService;
import com.healthlink.his.web.knowledgegraph.dto.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;
@Service
public class KgReasoningAppServiceImpl implements IKgReasoningAppService {
@Autowired
private IKgEntityRelationService relationService;
@Autowired
private IKgSymptomService symptomService;
@Autowired
private IKgDiseaseService diseaseService;
@Autowired
private IKgExaminationService examinationService;
@Autowired
private IKgDrugService drugService;
@Autowired
private IKgClinicalPathwayService pathwayService;
@Autowired
private IKgPathwayStepService pathwayStepService;
@Override
public List<DiagnosisResultDto> suggestDiagnosis(List<String> symptoms, Integer topN) {
if (symptoms == null || symptoms.isEmpty()) {
return Collections.emptyList();
}
if (topN == null || topN <= 0) {
topN = 5;
}
// 1. Find symptom IDs by name/code
LambdaQueryWrapper<KgSymptom> sw = new LambdaQueryWrapper<>();
sw.and(w -> {
for (String symptom : symptoms) {
w.or().like(KgSymptom::getSymptomName, symptom)
.or().like(KgSymptom::getSymptomCode, symptom);
}
});
List<KgSymptom> matchedSymptoms = symptomService.list(sw);
if (matchedSymptoms.isEmpty()) {
return Collections.emptyList();
}
List<String> symptomIds = matchedSymptoms.stream()
.map(s -> String.valueOf(s.getId()))
.collect(Collectors.toList());
// 2. Query relations: symptom -> disease
LambdaQueryWrapper<KgEntityRelation> rw = new LambdaQueryWrapper<>();
rw.eq(KgEntityRelation::getSourceType, "symptom")
.eq(KgEntityRelation::getTargetType, "disease")
.in(KgEntityRelation::getSourceId, symptomIds)
.orderByDesc(KgEntityRelation::getRelationStrength);
List<KgEntityRelation> relations = relationService.list(rw);
// 3. Group by disease, sum scores
Map<String, BigDecimal> diseaseScoreMap = new LinkedHashMap<>();
Map<String, List<String>> diseaseSymptomMap = new LinkedHashMap<>();
for (KgEntityRelation rel : relations) {
String diseaseId = rel.getTargetId();
BigDecimal strength = rel.getRelationStrength() != null ? rel.getRelationStrength() : BigDecimal.ONE;
diseaseScoreMap.merge(diseaseId, strength, BigDecimal::add);
diseaseSymptomMap.computeIfAbsent(diseaseId, k -> new ArrayList<>())
.add(rel.getSourceId());
}
// 4. Sort by score descending, take top N
List<Map.Entry<String, BigDecimal>> sorted = diseaseScoreMap.entrySet().stream()
.sorted(Map.Entry.<String, BigDecimal>comparingByValue().reversed())
.limit(topN)
.collect(Collectors.toList());
// 5. Build results with disease info
List<DiagnosisResultDto> results = new ArrayList<>();
for (Map.Entry<String, BigDecimal> entry : sorted) {
KgDisease disease = diseaseService.getById(Long.parseLong(entry.getKey()));
if (disease == null) continue;
DiagnosisResultDto dto = new DiagnosisResultDto();
dto.setDiseaseCode(disease.getDiseaseCode());
dto.setDiseaseName(disease.getDiseaseName());
dto.setCategory(disease.getCategory());
dto.setDepartment(disease.getDepartment());
dto.setScore(entry.getValue());
List<String> matched = diseaseSymptomMap.getOrDefault(entry.getKey(), Collections.emptyList());
dto.setMatchedSymptoms(String.join(",", matched));
results.add(dto);
}
return results;
}
@Override
public List<ExaminationResultDto> suggestExaminations(String diseaseCode, Integer topN) {
if (!StringUtils.hasText(diseaseCode)) {
return Collections.emptyList();
}
if (topN == null || topN <= 0) {
topN = 10;
}
// 1. Find disease by code
LambdaQueryWrapper<KgDisease> dw = new LambdaQueryWrapper<>();
dw.eq(KgDisease::getDiseaseCode, diseaseCode);
KgDisease disease = diseaseService.getOne(dw);
if (disease == null) {
return Collections.emptyList();
}
// 2. Query relations: disease -> examination
LambdaQueryWrapper<KgEntityRelation> rw = new LambdaQueryWrapper<>();
rw.eq(KgEntityRelation::getSourceType, "disease")
.eq(KgEntityRelation::getTargetType, "examination")
.eq(KgEntityRelation::getSourceId, String.valueOf(disease.getId()))
.orderByDesc(KgEntityRelation::getRelationStrength);
List<KgEntityRelation> relations = relationService.list(rw);
// 3. Build results
List<ExaminationResultDto> results = new ArrayList<>();
for (KgEntityRelation rel : relations) {
if (results.size() >= topN) break;
KgExamination exam = examinationService.getById(Long.parseLong(rel.getTargetId()));
if (exam == null) continue;
ExaminationResultDto dto = new ExaminationResultDto();
dto.setExamCode(exam.getExamCode());
dto.setExamName(exam.getExamName());
dto.setExamType(exam.getExamType());
dto.setClinicalSignificance(exam.getClinicalSignificance());
dto.setScore(rel.getRelationStrength());
results.add(dto);
}
return results;
}
@Override
public List<DrugInteractionResultDto> checkDrugInteractions(List<String> drugCodes) {
if (drugCodes == null || drugCodes.size() < 2) {
return Collections.emptyList();
}
// 1. Find drug IDs by code
LambdaQueryWrapper<KgDrug> dw = new LambdaQueryWrapper<>();
dw.in(KgDrug::getDrugCode, drugCodes);
List<KgDrug> drugs = drugService.list(dw);
if (drugs.isEmpty()) {
return Collections.emptyList();
}
Map<String, KgDrug> drugMap = drugs.stream()
.collect(Collectors.toMap(KgDrug::getDrugCode, d -> d, (a, b) -> a));
List<String> drugIds = drugs.stream()
.map(d -> String.valueOf(d.getId()))
.collect(Collectors.toList());
// 2. Query drug-drug interaction relations
LambdaQueryWrapper<KgEntityRelation> rw = new LambdaQueryWrapper<>();
rw.eq(KgEntityRelation::getSourceType, "drug")
.eq(KgEntityRelation::getTargetType, "drug")
.in(KgEntityRelation::getSourceId, drugIds)
.in(KgEntityRelation::getTargetId, drugIds);
List<KgEntityRelation> relations = relationService.list(rw);
// 3. Build results
List<DrugInteractionResultDto> results = new ArrayList<>();
Set<String> added = new HashSet<>();
for (KgEntityRelation rel : relations) {
KgDrug drugA = drugService.getById(Long.parseLong(rel.getSourceId()));
KgDrug drugB = drugService.getById(Long.parseLong(rel.getTargetId()));
if (drugA == null || drugB == null) continue;
String key = Collections.min(Arrays.asList(drugA.getDrugCode(), drugB.getDrugCode()))
+ "-" + Collections.max(Arrays.asList(drugA.getDrugCode(), drugB.getDrugCode()));
if (!added.add(key)) continue;
DrugInteractionResultDto dto = new DrugInteractionResultDto();
dto.setDrugCodeA(drugA.getDrugCode());
dto.setDrugNameA(drugA.getDrugName());
dto.setDrugCodeB(drugB.getDrugCode());
dto.setDrugNameB(drugB.getDrugName());
dto.setInteractionType(rel.getRelationType());
dto.setDescription(rel.getDescription());
dto.setSeverity(rel.getRelationStrength() != null
? (rel.getRelationStrength().compareTo(new BigDecimal("0.7")) >= 0 ? "严重" : "一般")
: "一般");
results.add(dto);
}
return results;
}
@Override
public Map<String, Object> suggestPathway(String diseaseCode) {
if (!StringUtils.hasText(diseaseCode)) {
return Collections.emptyMap();
}
// 1. Find pathway by disease code
LambdaQueryWrapper<KgClinicalPathway> pw = new LambdaQueryWrapper<>();
pw.eq(KgClinicalPathway::getDiseaseCode, diseaseCode)
.eq(KgClinicalPathway::getStatus, "ACTIVE");
KgClinicalPathway pathway = pathwayService.getOne(pw);
if (pathway == null) {
return Collections.emptyMap();
}
// 2. Get pathway steps
LambdaQueryWrapper<KgPathwayStep> sw = new LambdaQueryWrapper<>();
sw.eq(KgPathwayStep::getPathwayId, pathway.getId())
.orderByAsc(KgPathwayStep::getStepOrder);
List<KgPathwayStep> steps = pathwayStepService.list(sw);
Map<String, Object> result = new LinkedHashMap<>();
result.put("pathway", pathway);
result.put("steps", steps);
return result;
}
}

View File

@@ -0,0 +1,103 @@
package com.healthlink.his.web.knowledgegraph.controller;
import com.core.common.core.domain.R;
import com.healthlink.his.web.knowledgegraph.appservice.IKgDataImportAppService;
import com.healthlink.his.web.knowledgegraph.dto.ImportResultDto;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.BufferedInputStream;
import java.io.InputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
@Slf4j
@Tag(name = "知识图谱-数据导入")
@RestController
@RequestMapping("/knowledgegraph/import")
@AllArgsConstructor
public class KgDataImportController {
private final IKgDataImportAppService kgDataImportAppService;
@Operation(summary = "导入疾病数据")
@PreAuthorize("@ss.hasPermi('system:knowledgegraph:edit')")
@PostMapping("/disease")
public R<ImportResultDto> importDisease(@RequestParam("file") MultipartFile file) {
try {
ImportResultDto result = kgDataImportAppService.importDiseaseFromCsv(file);
return R.ok(result);
} catch (Exception e) {
log.error("导入疾病数据失败", e);
return R.fail("导入疾病数据失败: " + e.getMessage());
}
}
@Operation(summary = "导入药物数据")
@PreAuthorize("@ss.hasPermi('system:knowledgegraph:edit')")
@PostMapping("/drug")
public R<ImportResultDto> importDrug(@RequestParam("file") MultipartFile file) {
try {
ImportResultDto result = kgDataImportAppService.importDrugFromCsv(file);
return R.ok(result);
} catch (Exception e) {
log.error("导入药物数据失败", e);
return R.fail("导入药物数据失败: " + e.getMessage());
}
}
@Operation(summary = "导入关系数据")
@PreAuthorize("@ss.hasPermi('system:knowledgegraph:edit')")
@PostMapping("/relation")
public R<ImportResultDto> importRelations(@RequestParam("file") MultipartFile file) {
try {
ImportResultDto result = kgDataImportAppService.importRelationsFromCsv(file);
return R.ok(result);
} catch (Exception e) {
log.error("导入关系数据失败", e);
return R.fail("导入关系数据失败: " + e.getMessage());
}
}
@Operation(summary = "下载导入模板")
@PreAuthorize("@ss.hasPermi('system:knowledgegraph:list')")
@GetMapping("/template/{type}")
public void downloadTemplate(@PathVariable String type, jakarta.servlet.http.HttpServletResponse response) throws Exception {
String filename;
String content;
switch (type) {
case "disease":
filename = "疾病导入模板.csv";
content = "疾病编码,疾病名称,分类,科室,严重等级,描述,关键词\n"
+ "J06.900,急性上呼吸道感染,感染性疾病,呼吸内科,轻度,急性上呼吸道感染,发热;咳嗽;咽痛\n";
break;
case "drug":
filename = "药物导入模板.csv";
content = "药物编码,药物名称,通用名,分类,剂型,禁忌症,不良反应\n"
+ "D00001,阿莫西林胶囊,阿莫西林,抗生素,胶囊剂,青霉素过敏者禁用,皮疹;腹泻\n";
break;
case "relation":
filename = "关系导入模板.csv";
content = "来源类型,来源ID,目标类型,目标ID,关系类型,关系强度,描述,证据来源\n"
+ "symptom,1001,disease,2001,has_symptom,0.85,发热是急性上呼吸道感染的常见症状,临床指南\n";
break;
default:
response.setStatus(400);
response.getWriter().write("不支持的模板类型");
return;
}
response.setContentType("text/csv;charset=UTF-8");
response.setHeader("Content-Disposition",
"attachment; filename=" + URLEncoder.encode(filename, StandardCharsets.UTF_8));
response.getOutputStream().write("\uFEFF".getBytes(StandardCharsets.UTF_8));
response.getOutputStream().write(content.getBytes(StandardCharsets.UTF_8));
response.getOutputStream().flush();
}
}

View File

@@ -0,0 +1,76 @@
package com.healthlink.his.web.knowledgegraph.controller;
import com.core.common.core.domain.R;
import com.healthlink.his.web.knowledgegraph.appservice.IKgReasoningAppService;
import com.healthlink.his.web.knowledgegraph.dto.*;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
@Slf4j
@Tag(name = "知识图谱-推理引擎")
@RestController
@RequestMapping("/knowledgegraph/reasoning")
@AllArgsConstructor
public class KgReasoningController {
private final IKgReasoningAppService kgReasoningAppService;
@Operation(summary = "诊断推荐 - 基于症状推荐诊断")
@PreAuthorize("@ss.hasPermi('system:knowledgegraph:list')")
@PostMapping("/diagnosis")
public R<List<DiagnosisResultDto>> suggestDiagnosis(@RequestBody DiagnosisSuggestDto dto) {
try {
List<DiagnosisResultDto> results = kgReasoningAppService.suggestDiagnosis(dto.getSymptoms(), dto.getTopN());
return R.ok(results);
} catch (Exception e) {
log.error("诊断推荐失败", e);
return R.fail("诊断推荐失败: " + e.getMessage());
}
}
@Operation(summary = "检查推荐 - 基于诊断推荐检查")
@PreAuthorize("@ss.hasPermi('system:knowledgegraph:list')")
@PostMapping("/examination")
public R<List<ExaminationResultDto>> suggestExaminations(@RequestBody ExaminationSuggestDto dto) {
try {
List<ExaminationResultDto> results = kgReasoningAppService.suggestExaminations(dto.getDiseaseCode(), dto.getTopN());
return R.ok(results);
} catch (Exception e) {
log.error("检查推荐失败", e);
return R.fail("检查推荐失败: " + e.getMessage());
}
}
@Operation(summary = "药物相互作用检查")
@PreAuthorize("@ss.hasPermi('system:knowledgegraph:list')")
@PostMapping("/drug-interaction")
public R<List<DrugInteractionResultDto>> checkDrugInteractions(@RequestBody DrugInteractionDto dto) {
try {
List<DrugInteractionResultDto> results = kgReasoningAppService.checkDrugInteractions(dto.getDrugCodes());
return R.ok(results);
} catch (Exception e) {
log.error("药物相互作用检查失败", e);
return R.fail("药物相互作用检查失败: " + e.getMessage());
}
}
@Operation(summary = "临床路径推荐")
@PreAuthorize("@ss.hasPermi('system:knowledgegraph:list')")
@GetMapping("/pathway/{diseaseCode}")
public R<Map<String, Object>> suggestPathway(@PathVariable String diseaseCode) {
try {
Map<String, Object> result = kgReasoningAppService.suggestPathway(diseaseCode);
return result.isEmpty() ? R.fail("未找到临床路径") : R.ok(result);
} catch (Exception e) {
log.error("临床路径推荐失败", e);
return R.fail("临床路径推荐失败: " + e.getMessage());
}
}
}