You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
341 lines
19 KiB
Java
341 lines
19 KiB
Java
package com.supervision.service.impl;
|
|
|
|
import cn.hutool.core.bean.BeanUtil;
|
|
import cn.hutool.core.collection.CollUtil;
|
|
import cn.hutool.core.date.DateUtil;
|
|
import cn.hutool.core.lang.Assert;
|
|
import cn.hutool.core.util.NumberUtil;
|
|
import cn.hutool.core.util.ObjectUtil;
|
|
import cn.hutool.core.util.StrUtil;
|
|
import com.supervision.dao.*;
|
|
import com.supervision.domain.*;
|
|
import com.supervision.enums.TagEnum;
|
|
import com.supervision.exception.BusinessException;
|
|
import com.supervision.model.Process;
|
|
import com.supervision.model.*;
|
|
import com.supervision.service.*;
|
|
import com.supervision.vo.EdgeVO;
|
|
import com.supervision.vo.GraphVO;
|
|
import com.supervision.vo.NodeVO;
|
|
import com.supervision.vo.TreeNodeVO;
|
|
import lombok.RequiredArgsConstructor;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
import org.nebula.contrib.ngbatis.models.data.NgEdge;
|
|
import org.nebula.contrib.ngbatis.models.data.NgSubgraph;
|
|
import org.nebula.contrib.ngbatis.models.data.NgVertex;
|
|
import org.springframework.stereotype.Service;
|
|
|
|
import javax.annotation.Resource;
|
|
import java.util.*;
|
|
import java.util.function.Function;
|
|
import java.util.stream.Collectors;
|
|
|
|
@Slf4j
|
|
@Service
|
|
@RequiredArgsConstructor
|
|
public class GraphNebulaServiceImpl implements GraphNebulaService {
|
|
|
|
private final ProcessService processService;
|
|
private final TreatmentPlanRecordService treatmentPlanRecordService;
|
|
private final ProcessMedicalService processMedicalService;
|
|
private final MedicalRecService medicalRecService;
|
|
private final DiagnosisPrimaryService diagnosisPrimaryService;
|
|
private final DiagnosisAncillaryRecordService diagnosisAncillaryRecordService;
|
|
private final DiagnosisPhysicalRecordService diagnosisPhysicalRecordService;
|
|
private final ConfigPhysicalToolService configPhysicalToolService;
|
|
private final ConfigPhysicalLocationService configPhysicalLocationService;
|
|
private final ConfigAncillaryItemService configAncillaryItemService;
|
|
private final DiseaseService diseaseService;
|
|
private final DiagnosisPrimaryRelationService diagnosisPrimaryRelationService;
|
|
@Resource
|
|
private MedicalRecDao medicalRecDao;
|
|
@Resource
|
|
private ProcessMedicalDao processMedicalDao;
|
|
@Resource
|
|
private SelfDescDao selfDescDao;
|
|
@Resource
|
|
private IllnessHistoryDao illnessHistoryDao;
|
|
@Resource
|
|
private PersonalHistoryDao personalHistoryDao;
|
|
@Resource
|
|
private AllergyHistoryDao allergyHistoryDao;
|
|
@Resource
|
|
private PreviousHistoryDao previousHistoryDao;
|
|
@Resource
|
|
private FamilyHistoryDao familyHistoryDao;
|
|
@Resource
|
|
private OperationHistoryDao operationHistoryDao;
|
|
@Resource
|
|
private PatientDao patientDao;
|
|
@Resource
|
|
private PhysicalDao physicalDao;
|
|
@Resource
|
|
private AncillaryDao ancillaryDao;
|
|
@Resource
|
|
private DiagnosisDao diagnosisDao;
|
|
@Resource
|
|
private AncillaryResultDao ancillaryResultDao;
|
|
|
|
@Override
|
|
public void creatGraphByNebula(String processId) {
|
|
Process process = Optional.ofNullable(processService.getById(processId)).orElseThrow(() -> new BusinessException("未找到流程,创建图谱失败"));
|
|
// 根据processId找到对应的诊疗计划,如果没有找到,说明治疗流程未完成,诊断失败
|
|
List<TreatmentPlanRecord> treatmentPlanRecordList = treatmentPlanRecordService.lambdaQuery().eq(TreatmentPlanRecord::getProcessId, processId).list();
|
|
Assert.notEmpty(treatmentPlanRecordList, () -> new BusinessException("治疗计划为空,请先完成治疗计划"));
|
|
Integer disposalMethod = treatmentPlanRecordList.stream().findAny().orElseThrow(() -> new BusinessException("治疗计划为空,请先完成治疗计划")).getDisposalMethod();
|
|
|
|
// 首先创建一个病历
|
|
MedicalRecVertex medicalRecVertex = new MedicalRecVertex();
|
|
medicalRecVertex.setNodeValue((disposalMethod == 0 ? "门诊" : "住院") + "(" + DateUtil.format(process.getCreateTime(), "yyyy-MM-dd") + ")");
|
|
medicalRecDao.insert(medicalRecVertex);
|
|
log.info("病历图谱ID:{}", medicalRecVertex.getId());
|
|
// 根据processId找到电子病历
|
|
ProcessMedical processMedical = processMedicalService.lambdaQuery().eq(ProcessMedical::getProcessId, processId).oneOpt().orElseThrow(() -> new BusinessException("未找到电子病历"));
|
|
|
|
ProcessMedicalVertex processMedicalVertex = new ProcessMedicalVertex();
|
|
processMedicalVertex.setNodeValue("病历(" + DateUtil.format(processMedical.getCreateTime(), "yyyy-MM-dd") + ")");
|
|
processMedicalDao.insert(processMedicalVertex);
|
|
|
|
medicalRecDao.insertEdge(medicalRecVertex, new NoPropertyEdge(), processMedicalVertex);
|
|
// 创建主诉节点
|
|
if (StrUtil.isNotBlank(processMedical.getPatientSelfDesc())) {
|
|
SelfDescVertex selfDescVertex = new SelfDescVertex();
|
|
selfDescVertex.setNodeValue(processMedical.getPatientSelfDesc());
|
|
selfDescDao.insert(selfDescVertex);
|
|
// 保存节点之间的关系
|
|
processMedicalDao.insertEdge(processMedicalVertex, new NoPropertyEdge(), selfDescVertex);
|
|
}
|
|
// 创建现病史节点
|
|
if (StrUtil.isNotBlank(processMedical.getIllnessHistory())) {
|
|
IllnessHistoryVertex illnessHistoryVertex = new IllnessHistoryVertex();
|
|
illnessHistoryVertex.setNodeValue(processMedical.getIllnessHistory());
|
|
illnessHistoryDao.insert(illnessHistoryVertex);
|
|
// 保存节点之间的关系
|
|
processMedicalDao.insertEdge(processMedicalVertex, new NoPropertyEdge(), illnessHistoryVertex);
|
|
}
|
|
// 创建个人史节点
|
|
if (StrUtil.isNotBlank(processMedical.getPersonalHistory())) {
|
|
PersonalHistoryVertex personalHistoryVertex = new PersonalHistoryVertex();
|
|
personalHistoryVertex.setNodeValue(processMedical.getPersonalHistory());
|
|
personalHistoryDao.insert(personalHistoryVertex);
|
|
// 保存节点之间的关系
|
|
processMedicalDao.insertEdge(processMedicalVertex, new NoPropertyEdge(), personalHistoryVertex);
|
|
}
|
|
// 创建过敏史节点
|
|
if (ObjectUtil.isNotEmpty(processMedical.getAllergyHistoryFlag()) && 1 == processMedical.getAllergyHistoryFlag() && StrUtil.isNotBlank(processMedical.getAllergyHistory())) {
|
|
AllergyHistoryVertex allergyHistoryVertex = new AllergyHistoryVertex();
|
|
allergyHistoryVertex.setNodeValue(processMedical.getAllergyHistory());
|
|
allergyHistoryDao.insert(allergyHistoryVertex);
|
|
// 保存节点之间的关系
|
|
processMedicalDao.insertEdge(processMedicalVertex, new NoPropertyEdge(), allergyHistoryVertex);
|
|
}
|
|
// 创建既往史节点
|
|
if (ObjectUtil.isNotEmpty(processMedical.getPreviousHistoryFlag()) && 1 == processMedical.getPreviousHistoryFlag() && StrUtil.isNotBlank(processMedical.getPreviousHistory())) {
|
|
PreviousHistoryVertex previousHistoryVertex = new PreviousHistoryVertex();
|
|
previousHistoryVertex.setNodeValue(processMedical.getPreviousHistory());
|
|
previousHistoryDao.insert(previousHistoryVertex);
|
|
// 保存节点之间的关系
|
|
processMedicalDao.insertEdge(processMedicalVertex, new NoPropertyEdge(), previousHistoryVertex);
|
|
}
|
|
// 创建家族史节点
|
|
if (ObjectUtil.isNotEmpty(processMedical.getFamilyHistoryFlag()) && 1 == processMedical.getFamilyHistoryFlag() && StrUtil.isNotBlank(processMedical.getFamilyHistory())) {
|
|
FamilyHistoryVertex familyHistoryVertex = new FamilyHistoryVertex();
|
|
familyHistoryVertex.setNodeValue(processMedical.getFamilyHistory());
|
|
familyHistoryDao.insert(familyHistoryVertex);
|
|
// 保存节点之间的关系
|
|
processMedicalDao.insertEdge(processMedicalVertex, new NoPropertyEdge(), familyHistoryVertex);
|
|
}
|
|
// 创建手术史节点
|
|
if (ObjectUtil.isNotEmpty(processMedical.getOperationHistory()) && 1 == processMedical.getOperationHistoryFlag() && StrUtil.isNotBlank(processMedical.getOperationHistory())) {
|
|
OperationHistoryVertex operationHistoryVertex = new OperationHistoryVertex();
|
|
operationHistoryVertex.setNodeValue(processMedical.getOperationHistory());
|
|
operationHistoryDao.insert(operationHistoryVertex);
|
|
// 保存节点之间的关系
|
|
processMedicalDao.insertEdge(processMedicalVertex, new NoPropertyEdge(), operationHistoryVertex);
|
|
}
|
|
// 创建患者节点
|
|
MedicalRec medicalRec = Optional.ofNullable(medicalRecService.getById(process.getMedicalRecId())).orElseThrow(() -> new BusinessException("未找到病历"));
|
|
PatientVertex patientVertex = new PatientVertex();
|
|
patientVertex.setNodeValue(medicalRec.getPatientName());
|
|
patientDao.insert(patientVertex);
|
|
processMedicalDao.insertEdge(medicalRecVertex, new NoPropertyEdge(), patientVertex);
|
|
|
|
// 创建体格检查节点(physicalId不为空,即为只查配置了检查结果的结果)
|
|
List<DiagnosisPhysicalRecord> physicalRecordList = diagnosisPhysicalRecordService.lambdaQuery()
|
|
.isNotNull(DiagnosisPhysicalRecord::getPhysicalId)
|
|
.eq(DiagnosisPhysicalRecord::getProcessId, processId).list();
|
|
Map<String, PhysicalVertex> physicalConfirmMap = new HashMap<>();
|
|
for (DiagnosisPhysicalRecord physicalRecord : physicalRecordList) {
|
|
ConfigPhysicalTool tool = configPhysicalToolService.getById(physicalRecord.getToolId());
|
|
ConfigPhysicalLocation location = null;
|
|
if (StrUtil.isNotBlank(physicalRecord.getLocationId())) {
|
|
location = configPhysicalLocationService.getById(physicalRecord.getLocationId());
|
|
}
|
|
if (ObjectUtil.isNotEmpty(tool)) {
|
|
PhysicalVertex physicalVertex = new PhysicalVertex();
|
|
physicalVertex.setNodeValue(tool.getToolName() + (location != null ? ("-" + location.getLocationName()) : ""));
|
|
physicalDao.insert(physicalVertex);
|
|
processMedicalDao.insertEdge(medicalRecVertex, new NoPropertyEdge(), physicalVertex);
|
|
// 如果是证实诊断依据,添加到证实诊断依据里面去
|
|
if (NumberUtil.equals(1, physicalRecord.getBasisConfirmFlag())) {
|
|
physicalConfirmMap.put(physicalRecord.getId(), physicalVertex);
|
|
}
|
|
}
|
|
}
|
|
// 创建辅助检查节点
|
|
List<DiagnosisAncillaryRecord> ancillaryRecordList = diagnosisAncillaryRecordService.lambdaQuery()
|
|
.isNotNull(DiagnosisAncillaryRecord::getAncillaryId)
|
|
.eq(DiagnosisAncillaryRecord::getProcessId, processId).list();
|
|
Map<String, AncillaryVertex> ancillaryConfirmMap = new HashMap<>();
|
|
for (DiagnosisAncillaryRecord diagnosisAncillaryRecord : ancillaryRecordList) {
|
|
ConfigAncillaryItem configAncillaryItem = configAncillaryItemService.getById(diagnosisAncillaryRecord.getItemId());
|
|
if (ObjectUtil.isNotEmpty(configAncillaryItem)) {
|
|
AncillaryVertex ancillaryVertex = new AncillaryVertex();
|
|
ancillaryVertex.setNodeValue(configAncillaryItem.getItemName());
|
|
ancillaryDao.insert(ancillaryVertex);
|
|
// 保存病历和辅助检查连线
|
|
medicalRecDao.insertEdge(medicalRecVertex, new NoPropertyEdge(), ancillaryVertex);
|
|
// 结果节点
|
|
AncillaryResultVertex ancillaryResultVertex = new AncillaryResultVertex();
|
|
ancillaryResultVertex.setNodeValue(diagnosisAncillaryRecord.getResult());
|
|
ancillaryResultDao.insert(ancillaryResultVertex);
|
|
// 保存检查结果连线
|
|
ancillaryDao.insertEdge(ancillaryVertex, new SinglePropertyEdge("结果"), ancillaryResultVertex);
|
|
// 如果是证实诊断依据,添加到证实诊断依据里面去
|
|
if (NumberUtil.equals(1, diagnosisAncillaryRecord.getBasisConfirmFlag())) {
|
|
ancillaryConfirmMap.put(diagnosisAncillaryRecord.getId(), ancillaryVertex);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 创建诊断节点(这个诊断是最终诊断)
|
|
List<DiagnosisPrimary> diagnosisPrimaryList = diagnosisPrimaryService.lambdaQuery().eq(DiagnosisPrimary::getProcessId, processId).eq(DiagnosisPrimary::getExcludeFlag, 1).list();
|
|
Map<String, DiagnosisVertex> diagnosisMap = new HashMap<>();
|
|
for (DiagnosisPrimary diagnosisPrimary : diagnosisPrimaryList) {
|
|
Disease disease = diseaseService.getById(diagnosisPrimary.getPrimaryDiagnosisId());
|
|
if (ObjectUtil.isNotEmpty(disease)) {
|
|
DiagnosisVertex diagnosisVertex = new DiagnosisVertex();
|
|
diagnosisVertex.setNodeValue(disease.getDiseaseNameAlias());
|
|
diagnosisDao.insert(diagnosisVertex);
|
|
diagnosisDao.insertEdge(medicalRecVertex, new SinglePropertyEdge("诊断"), diagnosisVertex);
|
|
diagnosisMap.put(diagnosisPrimary.getId(), diagnosisVertex);
|
|
}
|
|
}
|
|
// 获取诊断和体格检查及辅助检查之间的关联关系
|
|
List<DiagnosisPrimaryRelation> relationList = diagnosisPrimaryRelationService.lambdaQuery().eq(DiagnosisPrimaryRelation::getProcessId, processId).list();
|
|
for (DiagnosisPrimaryRelation relation : relationList) {
|
|
// 根据关系找到对应的疾病诊断节点
|
|
DiagnosisVertex diagnosisVertex = diagnosisMap.get(relation.getPrimaryId());
|
|
if (ObjectUtil.isNotEmpty(diagnosisVertex)) {
|
|
// 如果疾病诊断不为空,则根据relationId,查询对应的体格检查节点
|
|
PhysicalVertex physicalVertex = physicalConfirmMap.get(relation.getRelationId());
|
|
if (ObjectUtil.isNotEmpty(physicalVertex)) {
|
|
physicalDao.insertEdge(diagnosisVertex, new SinglePropertyEdge("依据"), physicalVertex);
|
|
}
|
|
// 再尝试找辅助检查
|
|
AncillaryVertex ancillaryVertex = ancillaryConfirmMap.get(relation.getRelationId());
|
|
if (ObjectUtil.isNotEmpty(ancillaryVertex)) {
|
|
ancillaryDao.insertEdge(diagnosisVertex, new SinglePropertyEdge("依据"), ancillaryVertex);
|
|
}
|
|
}
|
|
}
|
|
log.info("病历图谱ID:{}", medicalRecVertex.getId());
|
|
processService.lambdaUpdate().set(Process::getGraphId, medicalRecVertex.getId()).eq(Process::getId, processId).update();
|
|
}
|
|
|
|
|
|
@Override
|
|
public GraphVO queryGraph(String processId) {
|
|
Process process = Optional.ofNullable(processService.getById(processId)).orElseThrow(() -> new BusinessException("未找到对应的问诊流程"));
|
|
List<NgSubgraph<String>> subgraphList = medicalRecDao.selectSubgraph(process.getGraphId());
|
|
|
|
List<NodeVO> nodeList = new ArrayList<>();
|
|
List<EdgeVO> edgeList = new ArrayList<>();
|
|
for (NgSubgraph<String> subgraph : subgraphList) {
|
|
// 首先构建点
|
|
List<NgVertex<String>> vertexes = subgraph.getVertexes();
|
|
for (NgVertex<String> vertex : vertexes) {
|
|
NodeVO nodeVO = new NodeVO();
|
|
String vid = vertex.getVid();
|
|
nodeVO.setId(vid);
|
|
Map<String, Object> properties = vertex.getProperties();
|
|
for (Map.Entry<String, Object> entry : properties.entrySet()) {
|
|
String key = entry.getKey();
|
|
TagEnum tagEnum = TagEnum.valueOf(key);
|
|
if (ObjectUtil.isNotEmpty(tagEnum)) {
|
|
nodeVO.setNodeType(tagEnum.name());
|
|
nodeVO.setNodeDesc(tagEnum.getType());
|
|
nodeVO.setNodeColour(tagEnum.getColour());
|
|
nodeVO.setNodeLevel(tagEnum.getLevel());
|
|
}
|
|
Object value = entry.getValue();
|
|
if (value instanceof Map) {
|
|
Map<?, ?> map = (Map<?, ?>) value;
|
|
Object nodeValue = map.get("nodeValue");
|
|
if (ObjectUtil.isNotEmpty(nodeValue)) {
|
|
nodeVO.setNodeValue(String.valueOf(nodeValue));
|
|
}
|
|
map.forEach((k, v) -> {
|
|
if (ObjectUtil.isNotEmpty(k)) {
|
|
nodeVO.getParams().put(String.valueOf(k), v);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
nodeList.add(nodeVO);
|
|
}
|
|
// 构建边
|
|
List<NgEdge<String>> edges = subgraph.getEdges();
|
|
for (NgEdge<String> edge : edges) {
|
|
EdgeVO edgeVO = new EdgeVO();
|
|
edgeVO.setSource(edge.getSrcID());
|
|
edgeVO.setTarget(edge.getDstID());
|
|
Map<String, Object> properties = edge.getProperties();
|
|
Object nameObject = properties.get("edgeValue");
|
|
if (ObjectUtil.isNotEmpty(nameObject)) {
|
|
edgeVO.setName(String.valueOf(nameObject));
|
|
}
|
|
edgeVO.setParams(properties);
|
|
edgeList.add(edgeVO);
|
|
}
|
|
}
|
|
return new GraphVO(nodeList, edgeList);
|
|
}
|
|
|
|
@Override
|
|
public List<TreeNodeVO> queryTreeGraph(String processId) {
|
|
GraphVO graphVO = queryGraph(processId);
|
|
List<TreeNodeVO> treeNodeList = graphVO.getNodes().stream().map(node -> BeanUtil.toBean(node, TreeNodeVO.class)).collect(Collectors.toList());
|
|
// 首先找到第一级节点
|
|
List<TreeNodeVO> firstNodeList = treeNodeList.stream().filter(node -> node.getNodeLevel() == 1).collect(Collectors.toList());
|
|
if (CollUtil.isEmpty(firstNodeList)) {
|
|
return null;
|
|
}
|
|
// treeNodeList转换为map,key为节点ID,value为节点对象
|
|
Map<String, TreeNodeVO> treeNodeMap = treeNodeList.stream().collect(Collectors.toMap(TreeNodeVO::getId, Function.identity()));
|
|
for (TreeNodeVO nodeVO : firstNodeList) {
|
|
recursionBuildTree(nodeVO, treeNodeMap, graphVO.getEdges());
|
|
}
|
|
return firstNodeList;
|
|
}
|
|
|
|
private void recursionBuildTree(TreeNodeVO preNode, Map<String, TreeNodeVO> treeNodeMap, List<EdgeVO> edgeList) {
|
|
// 通过preNode的ID找到所有的子节点
|
|
List<TreeNodeVO> childNode = new ArrayList<>();
|
|
for (EdgeVO edgeVO : edgeList) {
|
|
if (StrUtil.equals(edgeVO.getSource(), preNode.getId())) {
|
|
TreeNodeVO treeNodeVO = treeNodeMap.get(edgeVO.getTarget());
|
|
if (ObjectUtil.isNotEmpty(treeNodeVO)) {
|
|
childNode.add(treeNodeVO);
|
|
}
|
|
;
|
|
}
|
|
}
|
|
preNode.setChildNodeList(childNode);
|
|
for (TreeNodeVO treeNodeVO : childNode) {
|
|
recursionBuildTree(treeNodeVO, treeNodeMap, edgeList);
|
|
}
|
|
}
|
|
}
|
|
|