feat(kg): 数据导入+规则库

This commit is contained in:
2026-06-19 10:36:06 +08:00
parent 179d8c9c97
commit 844eb8b7ab
3 changed files with 185 additions and 0 deletions

View File

@@ -0,0 +1,11 @@
package com.healthlink.his.web.knowledgegraph.dto;
import lombok.Data;
@Data
public class ImportResultDto {
private int successCount;
private int failCount;
private int totalRows;
private String message;
}

View File

@@ -103,3 +103,43 @@ export function getPathwayPage(params) {
export function getPathwaySteps(id) {
return request({ url: `/knowledgegraph/pathway/${id}/steps`, method: 'get' })
}
// KG3: 推理引擎
export function suggestDiagnosis(data) {
return request({ url: '/knowledgegraph/reasoning/diagnosis', method: 'post', data })
}
export function suggestExaminations(data) {
return request({ url: '/knowledgegraph/reasoning/examination', method: 'post', data })
}
export function checkDrugInteractions(data) {
return request({ url: '/knowledgegraph/reasoning/drug-interaction', method: 'post', data })
}
export function suggestPathway(diseaseCode) {
return request({ url: `/knowledgegraph/reasoning/pathway/${diseaseCode}`, method: 'get' })
}
// KG4: 数据导入
export function importDisease(file) {
const formData = new FormData()
formData.append('file', file)
return request({ url: '/knowledgegraph/import/disease', method: 'post', data: formData, headers: { 'Content-Type': 'multipart/form-data' } })
}
export function importDrug(file) {
const formData = new FormData()
formData.append('file', file)
return request({ url: '/knowledgegraph/import/drug', method: 'post', data: formData, headers: { 'Content-Type': 'multipart/form-data' } })
}
export function importRelations(file) {
const formData = new FormData()
formData.append('file', file)
return request({ url: '/knowledgegraph/import/relation', method: 'post', data: formData, headers: { 'Content-Type': 'multipart/form-data' } })
}
export function downloadImportTemplate(type) {
return request({ url: `/knowledgegraph/import/template/${type}`, method: 'get', responseType: 'blob' })
}

View File

@@ -0,0 +1,134 @@
<template>
<div class="app-container">
<el-card shadow="never">
<template #header>
<span>知识图谱数据导入</span>
</template>
<el-tabs v-model="activeTab">
<el-tab-pane label="疾病导入" name="disease">
<div class="import-section">
<p class="tip">CSV格式疾病编码,疾病名称,分类,科室,严重等级,描述,关键词</p>
<el-upload ref="diseaseUpload" :auto-upload="false" :limit="1" accept=".csv" :on-change="(f) => handleFileChange(f, 'disease')" :on-remove="() => clearFile('disease')">
<el-button type="primary">选择CSV文件</el-button>
</el-upload>
<div class="mt16">
<el-button type="success" :loading="importing" :disabled="!files.disease" @click="doImport('disease')">开始导入</el-button>
<el-button link type="primary" @click="downloadTemplate('disease')">下载模板</el-button>
</div>
</div>
</el-tab-pane>
<el-tab-pane label="药物导入" name="drug">
<div class="import-section">
<p class="tip">CSV格式药物编码,药物名称,通用名,分类,剂型,禁忌症,不良反应</p>
<el-upload ref="drugUpload" :auto-upload="false" :limit="1" accept=".csv" :on-change="(f) => handleFileChange(f, 'drug')" :on-remove="() => clearFile('drug')">
<el-button type="primary">选择CSV文件</el-button>
</el-upload>
<div class="mt16">
<el-button type="success" :loading="importing" :disabled="!files.drug" @click="doImport('drug')">开始导入</el-button>
<el-button link type="primary" @click="downloadTemplate('drug')">下载模板</el-button>
</div>
</div>
</el-tab-pane>
<el-tab-pane label="关系导入" name="relation">
<div class="import-section">
<p class="tip">CSV格式来源类型,来源ID,目标类型,目标ID,关系类型,关系强度,描述,证据来源</p>
<el-upload ref="relationUpload" :auto-upload="false" :limit="1" accept=".csv" :on-change="(f) => handleFileChange(f, 'relation')" :on-remove="() => clearFile('relation')">
<el-button type="primary">选择CSV文件</el-button>
</el-upload>
<div class="mt16">
<el-button type="success" :loading="importing" :disabled="!files.relation" @click="doImport('relation')">开始导入</el-button>
<el-button link type="primary" @click="downloadTemplate('relation')">下载模板</el-button>
</div>
</div>
</el-tab-pane>
</el-tabs>
</el-card>
<el-card shadow="never" class="mt16" v-if="importResult">
<template #header>
<span>导入结果</span>
</template>
<el-descriptions :column="2" border>
<el-descriptions-item label="总行数">{{ importResult.totalRows }}</el-descriptions-item>
<el-descriptions-item label="成功">{{ importResult.successCount }}</el-descriptions-item>
<el-descriptions-item label="失败">{{ importResult.failCount }}</el-descriptions-item>
<el-descriptions-item label="状态">
<el-tag :type="importResult.failCount === 0 ? 'success' : 'warning'">
{{ importResult.failCount === 0 ? '全部成功' : '部分失败' }}
</el-tag>
</el-descriptions-item>
</el-descriptions>
<p class="mt16">{{ importResult.message }}</p>
</el-card>
</div>
</template>
<script setup name="KgDataImport">
import { ref, reactive } from 'vue'
import { importDisease, importDrug, importRelations, downloadImportTemplate } from '@/api/knowledgegraph/api'
import { ElMessage } from 'element-plus'
const activeTab = ref('disease')
const importing = ref(false)
const importResult = ref(null)
const files = reactive({ disease: null, drug: null, relation: null })
function handleFileChange(file, type) {
files[type] = file.raw
}
function clearFile(type) {
files[type] = null
}
async function doImport(type) {
const file = files[type]
if (!file) {
ElMessage.warning('请先选择文件')
return
}
importing.value = true
importResult.value = null
try {
let res
if (type === 'disease') res = await importDisease(file)
else if (type === 'drug') res = await importDrug(file)
else res = await importRelations(file)
if (res.code === 200) {
importResult.value = res.data
ElMessage.success('导入完成')
} else {
ElMessage.error(res.msg || '导入失败')
}
} finally {
importing.value = false
}
}
async function downloadTemplate(type) {
try {
const res = await downloadImportTemplate(type)
const blob = new Blob([res], { type: 'text/csv;charset=utf-8' })
const url = window.URL.createObjectURL(blob)
const link = document.createElement('a')
link.href = url
const names = { disease: '疾病导入模板', drug: '药物导入模板', relation: '关系导入模板' }
link.download = names[type] + '.csv'
link.click()
window.URL.revokeObjectURL(url)
} catch {
ElMessage.error('下载模板失败')
}
}
</script>
<style scoped>
.mt16 { margin-top: 16px; }
.tip { color: #909399; font-size: 13px; margin-bottom: 12px; }
.import-section { padding: 12px 0; }
</style>