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 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 physicalRecordList = diagnosisPhysicalRecordService.lambdaQuery() .isNotNull(DiagnosisPhysicalRecord::getPhysicalId) .eq(DiagnosisPhysicalRecord::getProcessId, processId).list(); Map 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 ancillaryRecordList = diagnosisAncillaryRecordService.lambdaQuery() .isNotNull(DiagnosisAncillaryRecord::getAncillaryId) .eq(DiagnosisAncillaryRecord::getProcessId, processId).list(); Map 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 diagnosisPrimaryList = diagnosisPrimaryService.lambdaQuery().eq(DiagnosisPrimary::getProcessId, processId).eq(DiagnosisPrimary::getExcludeFlag, 1).list(); Map 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 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> subgraphList = medicalRecDao.selectSubgraph(process.getGraphId()); List nodeList = new ArrayList<>(); List edgeList = new ArrayList<>(); for (NgSubgraph subgraph : subgraphList) { // 首先构建点 List> vertexes = subgraph.getVertexes(); for (NgVertex vertex : vertexes) { NodeVO nodeVO = new NodeVO(); String vid = vertex.getVid(); nodeVO.setId(vid); Map properties = vertex.getProperties(); for (Map.Entry 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> edges = subgraph.getEdges(); for (NgEdge edge : edges) { EdgeVO edgeVO = new EdgeVO(); edgeVO.setSource(edge.getSrcID()); edgeVO.setTarget(edge.getDstID()); Map 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 queryTreeGraph(String processId) { GraphVO graphVO = queryGraph(processId); List treeNodeList = graphVO.getNodes().stream().map(node -> BeanUtil.toBean(node, TreeNodeVO.class)).collect(Collectors.toList()); // 首先找到第一级节点 List firstNodeList = treeNodeList.stream().filter(node -> node.getNodeLevel() == 1).collect(Collectors.toList()); if (CollUtil.isEmpty(firstNodeList)) { return null; } // treeNodeList转换为map,key为节点ID,value为节点对象 Map 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 treeNodeMap, List edgeList) { // 通过preNode的ID找到所有的子节点 List 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); } } }