From 50cabbeb32bb848ad7b0a4228b6d42d88b1cc25f Mon Sep 17 00:00:00 2001 From: chenqi Date: Fri, 19 Jun 2026 06:28:22 +0800 Subject: [PATCH 1/9] =?UTF-8?q?docs(architecture):=20=E6=B7=BB=E5=8A=A0=20?= =?UTF-8?q?HealthLink-HIS=20=E6=B7=B1=E5=BA=A6=E4=BC=98=E5=8C=96=E5=8D=87?= =?UTF-8?q?=E7=BA=A7=E8=AE=BE=E8=AE=A1=E6=96=B9=E6=A1=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 定义总体升级目标包括移动化、智能化、平台化、云化和生态化五个维度 - 设计微服务拆分方案涵盖19个核心服务及对应技术选型 - 制定云原生部署方案支持容器化和多租户架构 - 规划移动化方案包含移动护理APP和互联网医院功能 - 设计AI智能化方案含CDSS临床决策支持和NLP病历处理 - 构建数据平台方案实现数据中台和BI决策支持 - 制定安全加固方案确保系统认证授权和数据安全 - 制定四阶段实施路线图覆盖12个月的完整交付计划 - 明确团队扩展需求和年度预算估算 - 识别关键风险并制定相应应对措施 --- .../HEALTHLINK_HIS_UPGRADE_PLAN.md | 451 ++++++++++++++++++ 1 file changed, 451 insertions(+) create mode 100644 MD/architecture/HEALTHLINK_HIS_UPGRADE_PLAN.md diff --git a/MD/architecture/HEALTHLINK_HIS_UPGRADE_PLAN.md b/MD/architecture/HEALTHLINK_HIS_UPGRADE_PLAN.md new file mode 100644 index 000000000..277024d3d --- /dev/null +++ b/MD/architecture/HEALTHLINK_HIS_UPGRADE_PLAN.md @@ -0,0 +1,451 @@ +# HealthLink-HIS 深度优化升级设计方案 + +> **文档类型**: 架构设计+实施计划 +> **版本**: v1.0 +> **日期**: 2026-06-18 +> **目标**: 对标国内头部HIS厂商,补齐核心差距,提升竞争力 + +--- + +## 一、升级目标 + +### 1.1 总体目标 + +将HealthLink-HIS从"功能完整"升级为"行业领先",在以下5个维度达到国内一线水平: + +| 维度 | 当前状态 | 目标状态 | 时间 | +|------|---------|---------|:----:| +| **移动化** | 响应式Web | APP+小程序+H5全覆盖 | 3月 | +| **智能化** | 基础CDSS | AI辅助诊疗+智能推荐 | 6月 | +| **平台化** | 单体架构 | 微服务+数据中台 | 6月 | +| **云化** | 传统部署 | 云原生+SaaS多租户 | 12月 | +| **生态化** | 闭环系统 | 互联网医院+开放API | 12月 | + +### 1.2 核心KPI + +| KPI | 当前 | 目标 | 衡量方式 | +|-----|------|------|---------| +| 三甲医院客户 | 0 | 10+ | 签约数 | +| 并发用户支持 | 300 | 1000+ | 压测结果 | +| 接口响应时间 | 500ms | <200ms | APM监控 | +| 系统可用性 | 99.9% | 99.99% | 运维监控 | +| 移动端覆盖率 | 0% | 80%+ | 功能覆盖率 | + +--- + +## 二、架构升级方案 + +### 2.1 微服务拆分 + +#### 拆分策略 + +``` +当前单体架构 + ↓ +按业务域拆分为微服务 + ↓ +服务注册/发现 + API网关 + 配置中心 + ↓ +容器化部署 + 编排管理 +``` + +#### 微服务划分 + +| 服务名 | 职责 | 依赖 | 优先级 | +|--------|------|------|:------:| +| `gateway-service` | API网关、路由、限流、鉴权 | — | P0 | +| `auth-service` | 认证授权、SSO、OAuth2 | Redis | P0 | +| `user-service` | 用户管理、角色权限、组织架构 | PostgreSQL | P0 | +| `patient-service` | 患者主索引、EMPI | PostgreSQL | P0 | +| `registration-service` | 挂号预约、分诊叫号 | Redis | P0 | +| `doctor-service` | 门诊医生站、医嘱处方 | PostgreSQL | P0 | +| `nurse-service` | 护士站、护理评估、生命体征 | PostgreSQL | P0 | +| `inpatient-service` | 住院管理、入出转、床位 | PostgreSQL | P0 | +| `pharmacy-service` | 药品管理、药房、药库 | PostgreSQL | P0 | +| `lab-service` | LIS检验管理、质控 | PostgreSQL | P1 | +| `pacs-service` | PACS影像管理、报告 | MinIO | P1 | +| `surgery-service` | 手术麻醉、围术期管理 | PostgreSQL | P1 | +| `emr-service` | 电子病历、病历质控 | PostgreSQL | P0 | +| `mr-service` | 病案管理、DRG/DIP | PostgreSQL | P1 | +| `finance-service` | 收费结算、医保对接 | PostgreSQL | P0 | +| `report-service` | 统计报表、BI分析 | ClickHouse | P1 | +| `cdss-service` | 临床决策支持、规则引擎 | PostgreSQL | P1 | +| `message-service` | 消息通知、SSE推送 | Redis+RabbitMQ | P0 | +| `file-service` | 文件存储、影像归档 | MinIO | P0 | +| `audit-service` | 操作审计、日志 | Elasticsearch | P1 | + +#### 技术选型 + +| 组件 | 选型 | 说明 | +|------|------|------| +| 服务网关 | Spring Cloud Gateway | 路由、限流、鉴权 | +| 服务注册 | Nacos | 服务发现+配置中心 | +| 服务调用 | OpenFeign | 声明式HTTP客户端 | +| 消息队列 | RabbitMQ | 异步消息、事件驱动 | +| 缓存 | Redis Cluster | 分布式缓存、会话管理 | +| 搜索 | Elasticsearch | 全文搜索、日志分析 | +| 文件存储 | MinIO | 对象存储、影像归档 | +| 链路追踪 | SkyWalking | 分布式链路追踪 | +| 配置中心 | Nacos | 动态配置管理 | + +### 2.2 云原生部署 + +#### 容器化架构 + +``` +Docker容器 + ↓ +Kubernetes编排 + ↓ +Helm Charts部署 + ↓ +CI/CD流水线 (Jenkins/GitLab CI) +``` + +#### 基础设施 + +| 组件 | 选型 | 说明 | +|------|------|------| +| 容器运行时 | Docker | 容器化 | +| 编排 | Kubernetes | 自动扩缩容 | +| 服务网格 | Istio | 流量管理、安全 | +| 日志 | ELK Stack | 日志收集分析 | +| 监控 | Prometheus+Grafana | 指标监控告警 | +| CI/CD | GitLab CI | 持续集成部署 | + +#### 多租户架构 + +``` +租户隔离策略: +├── 数据隔离: 按tenant_id字段隔离(共享数据库) +├── 缓存隔离: Redis Key前缀隔离 +├── 文件隔离: MinIO Bucket隔离 +└── 配置隔离: Nacos Namespace隔离 +``` + +--- + +## 三、移动化方案 + +### 3.1 移动护理APP + +#### 功能清单 + +| 模块 | 功能 | 优先级 | +|------|------|:------:| +| **医嘱执行** | 扫码执行、医嘱查询、执行记录 | P0 | +| **生命体征** | 体温/脉搏/血压/血氧录入、趋势图 | P0 | +| **护理评估** | Braden/Morse/NRS2002量表、自动评分 | P0 | +| **输液管理** | 输液巡视、速度监控、异常报警 | P0 | +| **标本采集** | 条码扫描、采集记录、运送追踪 | P1 | +| **护理文书** | 护理记录单、交班报告 | P1 | +| **患者查询** | 患者列表、基本信息、费用查询 | P0 | +| **消息通知** | 危急值提醒、医嘱提醒、交班提醒 | P0 | + +#### 技术方案 + +| 维度 | 选型 | 说明 | +|------|------|------| +| 框架 | uni-app / Flutter | 跨平台一套代码 | +| 状态管理 | Pinia / Riverpod | 响应式状态 | +| 网络请求 | Dio / Axios | HTTP客户端 | +| 本地存储 | SQLite / Hive | 离线缓存 | +| 推送 | JPush / FCM | 消息推送 | +| 扫码 | ZXing / FlutterBarcode | 条码/二维码扫描 | + +### 3.2 互联网医院 + +#### 功能清单 + +| 模块 | 功能 | 优先级 | +|------|------|:------:| +| **在线问诊** | 图文/语音/视频问诊 | P0 | +| **复诊开方** | 慢病复诊、处方流转 | P0 | +| **药品配送** | 处方审核→药房→配送 | P0 | +| **预约挂号** | 线上预约、支付、取消 | P0 | +| **报告查询** | 检验检查报告在线查看 | P0 | +| **健康档案** | 个人健康档案、体检报告 | P1 | +| **健康管理** | 慢病管理、用药提醒 | P1 | +| **在线支付** | 微信/支付宝/医保在线支付 | P0 | + +### 3.3 小程序矩阵 + +| 小程序 | 用户 | 功能 | +|--------|------|------| +| **患者端** | 患者 | 预约挂号、报告查询、在线问诊、健康档案 | +| **医生端** | 医生 | 患者管理、处方开具、会诊协作 | +| **护士端** | 护士 | 医嘱执行、护理评估、交班报告 | +| **管理端** | 管理层 | 数据看板、审批流程、消息通知 | + +--- + +## 四、AI智能化方案 + +### 4.1 CDSS临床决策支持(增强版) + +#### 规则引擎升级 + +``` +当前: 简单字符串匹配 + ↓ +目标: 复杂表达式解析 + 知识图谱 + 机器学习 +``` + +#### AI能力矩阵 + +| 能力 | 描述 | 技术方案 | 优先级 | +|------|------|---------|:------:| +| **诊断辅助** | 基于症状推荐诊断 | NLP+知识图谱 | P0 | +| **用药审查** | 药物相互作用+个体化给药 | 规则引擎+ML | P0 | +| **检验预警** | 异常结果自动提醒 | 规则引擎 | P0 | +| **病历质控** | 自动检查病历完整性 | NLP+规则 | P1 | +| **编码推荐** | ICD-10自动编码 | NLP+ML | P1 | +| **风险预测** | 入院风险评估 | ML模型 | P2 | +| **影像AI** | CT/MRI辅助诊断 | 深度学习 | P2 | +| **语音录入** | 语音转病历 | ASR+NLP | P2 | + +### 4.2 智能推荐系统 + +| 推荐场景 | 描述 | 技术方案 | +|---------|------|---------| +| **诊断推荐** | 基于症状+病史推荐诊断 | 知识图谱+协同过滤 | +| **处方推荐** | 基于诊断推荐用药方案 | 协同过滤+规则 | +| **检查推荐** | 基于诊断推荐检查项目 | 规则+统计 | +| **路径推荐** | 基于诊断推荐临床路径 | 规则+ML | +| **费用预测** | 预估住院费用和DRG分组 | ML回归模型 | + +### 4.3 NLP病历处理 + +| 功能 | 描述 | 技术方案 | +|------|------|---------| +| **病历结构化** | 自由文本→结构化数据 | NLP+规则 | +| **关键词提取** | 提取诊断/症状/药物 | NER模型 | +| **病历摘要** | 自动生成病历摘要 | Seq2Seq模型 | +| **相似病例** | 查找相似病例 | 文本相似度 | +| **病历质控** | 检查病历规范性 | 规则+NLP | + +--- + +## 五、数据平台方案 + +### 5.1 数据中台架构 + +``` +数据源(HIS/LIS/PACS/EMR) + ↓ +数据采集(CDC/ETL) + ↓ +数据存储(ClickHouse/Hive) + ↓ +数据治理(质量/标准/安全) + ↓ +数据服务(API/报表/大屏) + ↓ +数据应用(BI/AI/运营) +``` + +### 5.2 数据仓库设计 + +| 数据域 | 数据表 | 说明 | +|--------|--------|------| +| **患者域** | 患者主索引、就诊记录、诊断记录 | 患者360°视图 | +| **临床域** | 医嘱、处方、检验、检查、手术 | 临床数据仓库 | +| **运营域** | 收入、成本、绩效、工作量 | 运营数据仓库 | +| **质量域** | 质控指标、不良事件、院感数据 | 质量数据仓库 | +| **科研域** | 病例数据、随访数据、科研数据 | 科研数据仓库 | + +### 5.3 BI决策支持 + +| 报表类型 | 描述 | 技术方案 | +|---------|------|---------| +| **经营分析** | 收入/成本/利润分析 | ClickHouse+Grafana | +| **临床分析** | 诊断/手术/用药分析 | ClickHouse+自研 | +| **质量分析** | 质控指标/不良事件分析 | ClickHouse+自研 | +| **绩效分析** | 医生/科室绩效分析 | ClickHouse+自研 | +| **数据大屏** | 实时数据可视化 | ECharts+WebSocket | + +--- + +## 六、安全加固方案 + +### 6.1 安全架构 + +``` +用户 → WAF → API网关(限流/鉴权) → 微服务 → 数据库 + ↓ + 审计日志(全量记录) + ↓ + 安全监控(SIEM) +``` + +### 6.2 安全能力 + +| 安全域 | 能力 | 优先级 | +|--------|------|:------:| +| **认证** | JWT+OAuth2+SSO+MFA | P0 | +| **授权** | RBAC+ABAC+数据权限 | P0 | +| **加密** | TLS+数据加密+密钥管理 | P0 | +| **审计** | 全量操作审计+合规报告 | P0 | +| **防护** | WAF+SQL注入防护+XSS防护 | P0 | +| **监控** | 安全事件监控+告警 | P1 | +| **等保** | 等保三级认证 | P1 | +| **密评** | 密码应用安全性评估 | P2 | + +--- + +## 七、实施路线图 + +### Phase 1: 移动化+安全加固(1-3月) + +``` +Month 1: +├── Week 1-2: 移动护理APP原型设计 +├── Week 3-4: 医嘱执行+生命体征模块开发 +├── Week 5-6: 护理评估+输液管理开发 +├── Week 7-8: 测试+上线 + +Month 2: +├── Week 1-2: 互联网医院架构设计 +├── Week 3-4: 在线问诊+复诊开方开发 +├── Week 5-6: 药品配送+预约挂号开发 +├── Week 7-8: 测试+上线 + +Month 3: +├── Week 1-2: 安全加固(认证授权升级) +├── Week 3-4: 审计日志+WAF部署 +├── Week 5-6: 等保三级准备 +├── Week 7-8: 安全测试+上线 +``` + +**交付物**: 移动护理APP + 互联网医院 + 安全加固 + +### Phase 2: AI+数据平台(4-6月) + +``` +Month 4: +├── Week 1-2: CDSS规则引擎升级 +├── Week 3-4: 诊断辅助+NLP病历 +├── Week 5-6: 用药审查增强 +├── Week 7-8: 测试+上线 + +Month 5: +├── Week 1-2: 数据中台架构设计 +├── Week 3-4: 数据采集+ETL开发 +├── Week 5-6: 数据仓库建设 +├── Week 7-8: BI报表开发 + +Month 6: +├── Week 1-2: 智能推荐系统 +├── Week 3-4: 影像AI集成 +├── Week 5-6: 语音录入 +├── Week 7-8: 测试+上线 +``` + +**交付物**: AI辅助诊疗 + 数据中台 + BI决策 + +### Phase 3: 微服务+云原生(7-9月) + +``` +Month 7: +├── Week 1-2: 微服务拆分方案设计 +├── Week 3-4: 网关+认证服务拆分 +├── Week 5-6: 核心业务服务拆分 +├── Week 7-8: 测试+灰度发布 + +Month 8: +├── Week 1-2: 容器化+K8s部署 +├── Week 3-4: CI/CD流水线 +├── Week 5-6: 监控+日志+链路追踪 +├── Week 7-8: 压测+优化 + +Month 9: +├── Week 1-2: 多租户架构 +├── Week 3-4: SaaS化改造 +├── Week 5-6: 云平台部署 +├── Week 7-8: 测试+上线 +``` + +**交付物**: 微服务架构 + 云原生部署 + SaaS平台 + +### Phase 4: 生态建设(10-12月) + +``` +Month 10: +├── Week 1-2: 开放API平台设计 +├── Week 3-4: API网关+开发者门户 +├── Week 5-6: 第三方集成SDK +├── Week 7-8: 测试+上线 + +Month 11: +├── Week 1-2: 区域医疗信息共享平台 +├── Week 3-4: 医联体云平台 +├── Week 5-6: 健康档案共享 +├── Week 7-8: 测试+上线 + +Month 12: +├── Week 1-2: 智慧病房(床旁交互+智能输液) +├── Week 3-4: 数字孪生医院 +├── Week 5-6: 全量测试+性能优化 +├── Week 7-8: 正式发布 +``` + +**交付物**: 开放API + 区域共享 + 智慧病房 + +--- + +## 八、资源需求 + +### 8.1 团队规模 + +| 角色 | 当前 | 目标 | 新增 | +|------|:----:|:----:|:----:| +| 后端开发 | 5 | 15 | +10 | +| 前端开发 | 3 | 8 | +5 | +| 移动端开发 | 0 | 4 | +4 | +| AI工程师 | 0 | 3 | +3 | +| 数据工程师 | 0 | 3 | +3 | +| 测试工程师 | 1 | 4 | +3 | +| 运维工程师 | 1 | 3 | +2 | +| 架构师 | 1 | 2 | +1 | +| 产品经理 | 1 | 2 | +1 | +| **合计** | **12** | **44** | **+32** | + +### 8.2 预算估算 + +| 类别 | 年度预算 | 说明 | +|------|:-------:|------| +| 人力成本 | 500万 | 32人×平均15万/年 | +| 云资源 | 50万 | 服务器+存储+带宽 | +| 软件授权 | 30万 | 中间件+工具+SDK | +| 培训费用 | 10万 | 团队培训+认证 | +| 其他 | 10万 | 差旅+办公+杂项 | +| **合计** | **600万** | | + +--- + +## 九、风险与应对 + +| 风险 | 概率 | 影响 | 应对措施 | +|------|:----:|:----:|---------| +| 微服务拆分复杂度高 | 高 | 延期 | 渐进式拆分,先拆核心服务 | +| AI模型效果不达预期 | 中 | 功能受限 | 先用规则引擎,ML逐步引入 | +| 移动端兼容性问题 | 中 | 用户体验差 | 多设备测试+灰度发布 | +| 团队扩招困难 | 高 | 进度延迟 | 提前招聘+外包协作 | +| 客户接受度低 | 低 | 推广受阻 | 先做标杆客户+案例营销 | + +--- + +## 十、成功标准 + +| 阶段 | 成功标准 | 验证方式 | +|------|---------|---------| +| Phase 1 | 移动护理上线+互联网医院上线 | 用户验收测试 | +| Phase 2 | AI辅助诊疗上线+数据中台上线 | 功能测试+性能测试 | +| Phase 3 | 微服务架构上线+SaaS平台上线 | 压测+安全测试 | +| Phase 4 | 开放API上线+区域共享上线 | 集成测试+客户验收 | + +--- + +> **文档版本**: v1.0 +> **最后更新**: 2026-06-18 +> **下一步**: 确认后从Phase 1开始执行 From e117022bb69f4408687a77a35b0f76993e528714 Mon Sep 17 00:00:00 2001 From: chenqi Date: Fri, 19 Jun 2026 06:38:07 +0800 Subject: [PATCH 2/9] =?UTF-8?q?feat(mobile+telehealth):=20=E7=A7=BB?= =?UTF-8?q?=E5=8A=A8=E6=8A=A4=E7=90=86=E8=AF=84=E4=BC=B0+=E8=BE=93?= =?UTF-8?q?=E6=B6=B2=E7=AE=A1=E7=90=86+=E4=BA=92=E8=81=94=E7=BD=91?= =?UTF-8?q?=E5=8C=BB=E9=99=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../appservice/INursingMobileAppService.java | 5 + .../impl/NursingMobileAppServiceImpl.java | 167 ++++++++ .../controller/NursingMobileController.java | 40 ++ .../dto/NursingMobileAssessmentDto.java | 24 ++ .../nursing/dto/NursingMobileInfusionDto.java | 30 ++ .../appservice/ITelehealthAppService.java | 15 + .../impl/TelehealthAppServiceImpl.java | 102 +++++ .../controller/TelehealthController.java | 75 ++++ .../domain/TelehealthConsultation.java | 46 ++ .../dto/TelehealthConsultationDto.java | 43 ++ .../mapper/TelehealthConsultationMapper.java | 10 + .../db/migration/V85__telehealth.sql | 19 + healthlink-his-ui/src/api/telehealth/index.js | 33 ++ .../nursingmobile/InfusionManagement.vue | 320 ++++++++++++++ .../views/nursingmobile/NursingAssessment.vue | 395 ++++++++++++++++++ .../src/views/nursingmobile/api.js | 20 + .../src/views/telehealth/doctor/index.vue | 159 +++++++ .../src/views/telehealth/patient/index.vue | 151 +++++++ 18 files changed, 1654 insertions(+) create mode 100644 healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/nursing/dto/NursingMobileAssessmentDto.java create mode 100644 healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/nursing/dto/NursingMobileInfusionDto.java create mode 100644 healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/telehealth/appservice/ITelehealthAppService.java create mode 100644 healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/telehealth/appservice/impl/TelehealthAppServiceImpl.java create mode 100644 healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/telehealth/controller/TelehealthController.java create mode 100644 healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/telehealth/domain/TelehealthConsultation.java create mode 100644 healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/telehealth/dto/TelehealthConsultationDto.java create mode 100644 healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/telehealth/mapper/TelehealthConsultationMapper.java create mode 100644 healthlink-his-server/healthlink-his-application/src/main/resources/db/migration/V85__telehealth.sql create mode 100644 healthlink-his-ui/src/api/telehealth/index.js create mode 100644 healthlink-his-ui/src/views/nursingmobile/InfusionManagement.vue create mode 100644 healthlink-his-ui/src/views/nursingmobile/NursingAssessment.vue create mode 100644 healthlink-his-ui/src/views/telehealth/doctor/index.vue create mode 100644 healthlink-his-ui/src/views/telehealth/patient/index.vue diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/nursing/appservice/INursingMobileAppService.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/nursing/appservice/INursingMobileAppService.java index b6ff95b36..4501dbcd7 100644 --- a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/nursing/appservice/INursingMobileAppService.java +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/nursing/appservice/INursingMobileAppService.java @@ -11,4 +11,9 @@ public interface INursingMobileAppService { Map executeOrder(Long requestId, String adviceTable, Long encounterId, Long patientId); NursingMobileVitalSignDto saveVitalSign(NursingMobileVitalSignDto vitalSign); NursingMobileVitalSignTrendDto getVitalSignTrend(Long patientId, Integer days); + NursingMobileAssessmentDto submitAssessment(NursingMobileAssessmentDto dto); + List getAssessmentList(Long patientId); + NursingMobileInfusionDto startInfusion(NursingMobileInfusionDto dto); + NursingMobileInfusionDto addPatrol(NursingMobileInfusionDto dto); + List getInfusionStatus(Long patientId); } diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/nursing/appservice/impl/NursingMobileAppServiceImpl.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/nursing/appservice/impl/NursingMobileAppServiceImpl.java index 62fa6ef5d..595e0c66c 100644 --- a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/nursing/appservice/impl/NursingMobileAppServiceImpl.java +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/nursing/appservice/impl/NursingMobileAppServiceImpl.java @@ -1,11 +1,16 @@ package com.healthlink.his.web.nursing.appservice.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.healthlink.his.nursing.domain.NursingAssessment; +import com.healthlink.his.nursing.domain.NursingInfusionPatrol; import com.healthlink.his.nursing.domain.NursingVitalSignsChart; +import com.healthlink.his.nursing.service.INursingAssessmentService; +import com.healthlink.his.nursing.service.INursingInfusionPatrolService; import com.healthlink.his.nursing.service.INursingVitalSignsChartService; import com.healthlink.his.web.nursing.appservice.INursingMobileAppService; import com.healthlink.his.web.nursing.dto.*; import com.healthlink.his.web.nursing.mapper.NursingMobileAppMapper; +import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -24,6 +29,14 @@ public class NursingMobileAppServiceImpl implements INursingMobileAppService { @Resource private INursingVitalSignsChartService vitalSignsChartService; + @Resource + private INursingAssessmentService assessmentService; + + @Resource + private INursingInfusionPatrolService infusionPatrolService; + + private final ObjectMapper objectMapper = new ObjectMapper(); + @Override public List getMobilePatientList(String wardName, String searchKey) { return mobileMapper.selectMobilePatientList(wardName, searchKey); @@ -156,4 +169,158 @@ public class NursingMobileAppServiceImpl implements INursingMobileAppService { return trend; } + + @Override + @Transactional(rollbackFor = Exception.class) + public NursingMobileAssessmentDto submitAssessment(NursingMobileAssessmentDto dto) { + NursingAssessment assessment = new NursingAssessment(); + assessment.setEncounterId(dto.getEncounterId()); + assessment.setPatientId(dto.getPatientId()); + assessment.setPatientName(dto.getPatientName()); + assessment.setAssessorId(dto.getAssessorId()); + assessment.setAssessorName(dto.getAssessorName()); + assessment.setAssessmentType(dto.getAssessmentType()); + assessment.setAssessmentTool(dto.getAssessmentTool()); + assessment.setTotalScore(dto.getTotalScore()); + assessment.setRiskLevel(calculateRiskLevel(dto.getAssessmentTool(), dto.getTotalScore())); + assessment.setDetail(dto.getDetail()); + assessment.setAssessmentTime(dto.getAssessmentTime() != null ? dto.getAssessmentTime() : new Date()); + assessment.setDeleteFlag("0"); + try { + assessment.setItemScores(dto.getItemScores() != null ? objectMapper.writeValueAsString(dto.getItemScores()) : null); + } catch (Exception e) { + assessment.setItemScores(null); + } + assessmentService.save(assessment); + dto.setId(assessment.getId()); + dto.setRiskLevel(assessment.getRiskLevel()); + return dto; + } + + @Override + public List getAssessmentList(Long patientId) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(NursingAssessment::getPatientId, patientId) + .orderByDesc(NursingAssessment::getAssessmentTime); + List records = assessmentService.list(wrapper); + List result = new ArrayList<>(); + for (NursingAssessment r : records) { + NursingMobileAssessmentDto dto = new NursingMobileAssessmentDto(); + dto.setId(r.getId()); + dto.setEncounterId(r.getEncounterId()); + dto.setPatientId(r.getPatientId()); + dto.setPatientName(r.getPatientName()); + dto.setAssessorName(r.getAssessorName()); + dto.setAssessmentType(r.getAssessmentType()); + dto.setAssessmentTool(r.getAssessmentTool()); + dto.setTotalScore(r.getTotalScore()); + dto.setRiskLevel(r.getRiskLevel()); + dto.setDetail(r.getDetail()); + dto.setAssessmentTime(r.getAssessmentTime()); + try { + if (r.getItemScores() != null) { + dto.setItemScores(objectMapper.readValue(r.getItemScores(), Map.class)); + } + } catch (Exception e) { + dto.setItemScores(null); + } + result.add(dto); + } + return result; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public NursingMobileInfusionDto startInfusion(NursingMobileInfusionDto dto) { + NursingInfusionPatrol patrol = new NursingInfusionPatrol(); + patrol.setEncounterId(dto.getEncounterId()); + patrol.setPatientId(dto.getPatientId()); + patrol.setPatientName(dto.getPatientName()); + patrol.setOrderId(dto.getOrderId()); + patrol.setDrugName(dto.getDrugName()); + patrol.setInfusionRate(dto.getInfusionRate()); + patrol.setTotalVolume(dto.getTotalVolume()); + patrol.setStartTime(dto.getStartTime() != null ? dto.getStartTime() : new Date()); + patrol.setPatencyStatus("NORMAL"); + patrol.setPatrolNurseId(dto.getPatrolNurseId()); + patrol.setPatrolNurseName(dto.getPatrolNurseName()); + patrol.setCreateTime(new Date()); + infusionPatrolService.save(patrol); + dto.setId(patrol.getId()); + dto.setPatencyStatus("NORMAL"); + dto.setStatus("RUNNING"); + return dto; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public NursingMobileInfusionDto addPatrol(NursingMobileInfusionDto dto) { + NursingInfusionPatrol patrol = new NursingInfusionPatrol(); + patrol.setEncounterId(dto.getEncounterId()); + patrol.setPatientId(dto.getPatientId()); + patrol.setPatientName(dto.getPatientName()); + patrol.setOrderId(dto.getOrderId()); + patrol.setDrugName(dto.getDrugName()); + patrol.setPatrolTime(new Date()); + patrol.setDripRate(dto.getDripRate()); + patrol.setPatencyStatus(dto.getPatencyStatus()); + patrol.setAdverseReaction(dto.getAdverseReaction()); + patrol.setPatrolNurseId(dto.getPatrolNurseId()); + patrol.setPatrolNurseName(dto.getPatrolNurseName()); + patrol.setCreateTime(new Date()); + infusionPatrolService.save(patrol); + dto.setId(patrol.getId()); + dto.setPatrolTime(patrol.getPatrolTime()); + return dto; + } + + @Override + public List getInfusionStatus(Long patientId) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(NursingInfusionPatrol::getPatientId, patientId) + .orderByDesc(NursingInfusionPatrol::getStartTime); + List records = infusionPatrolService.list(wrapper); + Map latestMap = new LinkedHashMap<>(); + for (NursingInfusionPatrol r : records) { + Long orderId = r.getOrderId(); + if (orderId == null) orderId = r.getId(); + if (!latestMap.containsKey(orderId)) { + NursingMobileInfusionDto dto = new NursingMobileInfusionDto(); + dto.setId(r.getId()); + dto.setEncounterId(r.getEncounterId()); + dto.setPatientId(r.getPatientId()); + dto.setPatientName(r.getPatientName()); + dto.setOrderId(r.getOrderId()); + dto.setDrugName(r.getDrugName()); + dto.setInfusionRate(r.getInfusionRate()); + dto.setTotalVolume(r.getTotalVolume()); + dto.setStartTime(r.getStartTime()); + dto.setPatrolTime(r.getPatrolTime()); + dto.setDripRate(r.getDripRate()); + dto.setPatencyStatus(r.getPatencyStatus()); + dto.setAdverseReaction(r.getAdverseReaction()); + dto.setPatrolNurseName(r.getPatrolNurseName()); + dto.setStatus("RUNNING"); + latestMap.put(orderId, dto); + } + } + return new ArrayList<>(latestMap.values()); + } + + private String calculateRiskLevel(String tool, Integer score) { + if (score == null) return "NORMAL"; + if ("BRADEN".equals(tool)) { + if (score <= 12) return "HIGH"; + if (score <= 14) return "MEDIUM"; + return "LOW"; + } else if ("MORSE".equals(tool)) { + if (score >= 45) return "HIGH"; + if (score >= 25) return "MEDIUM"; + return "LOW"; + } else if ("NRS2002".equals(tool)) { + if (score >= 3) return "HIGH"; + return "LOW"; + } + return "NORMAL"; + } } diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/nursing/controller/NursingMobileController.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/nursing/controller/NursingMobileController.java index 6b14d1a30..8d2219c5e 100644 --- a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/nursing/controller/NursingMobileController.java +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/nursing/controller/NursingMobileController.java @@ -69,4 +69,44 @@ public class NursingMobileController { NursingMobileVitalSignTrendDto trend = mobileAppService.getVitalSignTrend(patientId, days); return R.ok(trend); } + + @Operation(summary = "提交护理评估") + @PostMapping("/assessment/submit") + @PreAuthorize("hasAuthority('nursing:nursing:edit')") + public R submitAssessment(@RequestBody NursingMobileAssessmentDto assessment) { + NursingMobileAssessmentDto saved = mobileAppService.submitAssessment(assessment); + return R.ok(saved); + } + + @Operation(summary = "查询评估记录") + @GetMapping("/assessment/list/{patientId}") + @PreAuthorize("hasAuthority('nursing:nursing:list')") + public R getAssessmentList(@PathVariable Long patientId) { + List list = mobileAppService.getAssessmentList(patientId); + return R.ok(list); + } + + @Operation(summary = "开始输液") + @PostMapping("/infusion/start") + @PreAuthorize("hasAuthority('nursing:nursing:edit')") + public R startInfusion(@RequestBody NursingMobileInfusionDto infusion) { + NursingMobileInfusionDto saved = mobileAppService.startInfusion(infusion); + return R.ok(saved); + } + + @Operation(summary = "输液巡视记录") + @PostMapping("/infusion/patrol") + @PreAuthorize("hasAuthority('nursing:nursing:edit')") + public R addPatrol(@RequestBody NursingMobileInfusionDto patrol) { + NursingMobileInfusionDto saved = mobileAppService.addPatrol(patrol); + return R.ok(saved); + } + + @Operation(summary = "输液状态查询") + @GetMapping("/infusion/status/{patientId}") + @PreAuthorize("hasAuthority('nursing:nursing:list')") + public R getInfusionStatus(@PathVariable Long patientId) { + List list = mobileAppService.getInfusionStatus(patientId); + return R.ok(list); + } } diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/nursing/dto/NursingMobileAssessmentDto.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/nursing/dto/NursingMobileAssessmentDto.java new file mode 100644 index 000000000..6549ffbf6 --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/nursing/dto/NursingMobileAssessmentDto.java @@ -0,0 +1,24 @@ +package com.healthlink.his.web.nursing.dto; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.Data; +import java.util.Date; +import java.util.Map; + +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class NursingMobileAssessmentDto { + private Long id; + private Long encounterId; + private Long patientId; + private String patientName; + private Long assessorId; + private String assessorName; + private String assessmentType; + private String assessmentTool; + private Integer totalScore; + private String riskLevel; + private Map itemScores; + private String detail; + private Date assessmentTime; +} diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/nursing/dto/NursingMobileInfusionDto.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/nursing/dto/NursingMobileInfusionDto.java new file mode 100644 index 000000000..041b3ace4 --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/nursing/dto/NursingMobileInfusionDto.java @@ -0,0 +1,30 @@ +package com.healthlink.his.web.nursing.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.Data; +import java.util.Date; + +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class NursingMobileInfusionDto { + private Long id; + private Long encounterId; + private Long patientId; + private String patientName; + private Long orderId; + private String drugName; + private String infusionRate; + private Integer totalVolume; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date startTime; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date patrolTime; + private Integer dripRate; + private String patencyStatus; + private String adverseReaction; + private Long patrolNurseId; + private String patrolNurseName; + private String status; + private Integer remainingVolume; +} diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/telehealth/appservice/ITelehealthAppService.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/telehealth/appservice/ITelehealthAppService.java new file mode 100644 index 000000000..2fd32ff8f --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/telehealth/appservice/ITelehealthAppService.java @@ -0,0 +1,15 @@ +package com.healthlink.his.web.telehealth.appservice; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.healthlink.his.web.telehealth.dto.TelehealthConsultationDto; + +public interface ITelehealthAppService { + + Long createConsultation(TelehealthConsultationDto dto); + + Page pageConsultation(TelehealthConsultationDto dto); + + Boolean replyConsultation(TelehealthConsultationDto dto); + + Boolean prescribeConsultation(TelehealthConsultationDto dto); +} diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/telehealth/appservice/impl/TelehealthAppServiceImpl.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/telehealth/appservice/impl/TelehealthAppServiceImpl.java new file mode 100644 index 000000000..3824aa6af --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/telehealth/appservice/impl/TelehealthAppServiceImpl.java @@ -0,0 +1,102 @@ +package com.healthlink.his.web.telehealth.appservice.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.core.common.utils.SecurityUtils; +import com.healthlink.his.web.telehealth.appservice.ITelehealthAppService; +import com.healthlink.his.web.telehealth.domain.TelehealthConsultation; +import com.healthlink.his.web.telehealth.dto.TelehealthConsultationDto; +import com.healthlink.his.web.telehealth.mapper.TelehealthConsultationMapper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.BeanUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.StringUtils; + +import jakarta.annotation.Resource; +import java.util.Date; +import java.util.List; +import java.util.stream.Collectors; + +@Slf4j +@Service +public class TelehealthAppServiceImpl implements ITelehealthAppService { + + @Resource + private TelehealthConsultationMapper telehealthConsultationMapper; + + @Override + @Transactional(rollbackFor = Exception.class) + public Long createConsultation(TelehealthConsultationDto dto) { + TelehealthConsultation entity = new TelehealthConsultation(); + entity.setPatientId(dto.getPatientId()); + entity.setDoctorId(dto.getDoctorId()); + entity.setConsultationType(dto.getConsultationType()); + entity.setStatus("PENDING"); + entity.setChiefComplaint(dto.getChiefComplaint()); + entity.setConsultationTime(new Date()); + entity.setTenantId(SecurityUtils.getLoginUser().getTenantId()); + telehealthConsultationMapper.insert(entity); + return entity.getId(); + } + + @Override + public Page pageConsultation(TelehealthConsultationDto dto) { + Page page = new Page<>(dto.getPageNum(), dto.getPageSize()); + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + + if (StringUtils.hasText(dto.getStatus())) { + wrapper.eq(TelehealthConsultation::getStatus, dto.getStatus()); + } + if (dto.getDoctorId() != null) { + wrapper.eq(TelehealthConsultation::getDoctorId, dto.getDoctorId()); + } + if (dto.getPatientId() != null) { + wrapper.eq(TelehealthConsultation::getPatientId, dto.getPatientId()); + } + wrapper.orderByDesc(TelehealthConsultation::getCreateTime); + + Page result = telehealthConsultationMapper.selectPage(page, wrapper); + + Page dtoPage = new Page<>(result.getCurrent(), result.getSize(), result.getTotal()); + List dtoList = result.getRecords().stream() + .map(this::toDto) + .collect(Collectors.toList()); + dtoPage.setRecords(dtoList); + return dtoPage; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean replyConsultation(TelehealthConsultationDto dto) { + TelehealthConsultation entity = telehealthConsultationMapper.selectById(dto.getId()); + if (entity == null) { + throw new RuntimeException("问诊记录不存在"); + } + entity.setDiagnosis(dto.getDiagnosis()); + entity.setStatus("IN_PROGRESS"); + telehealthConsultationMapper.updateById(entity); + return true; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean prescribeConsultation(TelehealthConsultationDto dto) { + TelehealthConsultation entity = telehealthConsultationMapper.selectById(dto.getId()); + if (entity == null) { + throw new RuntimeException("问诊记录不存在"); + } + entity.setPrescription(dto.getPrescription()); + entity.setDiagnosis(dto.getDiagnosis()); + entity.setStatus("COMPLETED"); + entity.setEndTime(new Date()); + telehealthConsultationMapper.updateById(entity); + return true; + } + + private TelehealthConsultationDto toDto(TelehealthConsultation entity) { + TelehealthConsultationDto dto = new TelehealthConsultationDto(); + BeanUtils.copyProperties(entity, dto); + return dto; + } +} diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/telehealth/controller/TelehealthController.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/telehealth/controller/TelehealthController.java new file mode 100644 index 000000000..2fb19e249 --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/telehealth/controller/TelehealthController.java @@ -0,0 +1,75 @@ +package com.healthlink.his.web.telehealth.controller; + +import com.core.common.core.domain.R; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.healthlink.his.web.telehealth.appservice.ITelehealthAppService; +import com.healthlink.his.web.telehealth.dto.TelehealthConsultationDto; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import jakarta.annotation.Resource; + +@Slf4j +@Tag(name = "互联网医院-在线问诊") +@RestController +@RequestMapping("/telehealth/consultation") +public class TelehealthController { + + @Resource + private ITelehealthAppService telehealthAppService; + + @Operation(summary = "创建问诊") + @PostMapping("/create") + @PreAuthorize("@ss.hasPermi('outpatient:telehealth:edit')") + public R create(@RequestBody TelehealthConsultationDto dto) { + try { + Long id = telehealthAppService.createConsultation(dto); + return R.ok(id); + } catch (Exception e) { + log.error("创建问诊失败", e); + return R.fail("创建问诊失败: " + e.getMessage()); + } + } + + @Operation(summary = "问诊列表") + @GetMapping("/page") + @PreAuthorize("@ss.hasPermi('outpatient:telehealth:list')") + public R> page(TelehealthConsultationDto dto) { + try { + Page result = telehealthAppService.pageConsultation(dto); + return R.ok(result); + } catch (Exception e) { + log.error("查询问诊列表失败", e); + return R.fail("查询问诊列表失败: " + e.getMessage()); + } + } + + @Operation(summary = "医生回复") + @PostMapping("/reply") + @PreAuthorize("@ss.hasPermi('outpatient:telehealth:edit')") + public R reply(@RequestBody TelehealthConsultationDto dto) { + try { + telehealthAppService.replyConsultation(dto); + return R.ok("回复成功"); + } catch (Exception e) { + log.error("医生回复失败", e); + return R.fail("回复失败: " + e.getMessage()); + } + } + + @Operation(summary = "复诊开方") + @PostMapping("/prescribe") + @PreAuthorize("@ss.hasPermi('outpatient:telehealth:edit')") + public R prescribe(@RequestBody TelehealthConsultationDto dto) { + try { + telehealthAppService.prescribeConsultation(dto); + return R.ok("开方成功"); + } catch (Exception e) { + log.error("复诊开方失败", e); + return R.fail("开方失败: " + e.getMessage()); + } + } +} diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/telehealth/domain/TelehealthConsultation.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/telehealth/domain/TelehealthConsultation.java new file mode 100644 index 000000000..7fc1aad89 --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/telehealth/domain/TelehealthConsultation.java @@ -0,0 +1,46 @@ +package com.healthlink.his.web.telehealth.domain; + +import com.baomidou.mybatisplus.annotation.*; +import com.core.common.core.domain.HisBaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Date; + +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("telehealth_consultation") +public class TelehealthConsultation extends HisBaseEntity { + + private static final long serialVersionUID = 1L; + + @TableId(value = "id", type = IdType.ASSIGN_ID) + private Long id; + + @TableField("patient_id") + private Long patientId; + + @TableField("doctor_id") + private Long doctorId; + + @TableField("consultation_type") + private String consultationType; + + @TableField("status") + private String status; + + @TableField("chief_complaint") + private String chiefComplaint; + + @TableField("diagnosis") + private String diagnosis; + + @TableField("prescription") + private String prescription; + + @TableField("consultation_time") + private Date consultationTime; + + @TableField("end_time") + private Date endTime; +} diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/telehealth/dto/TelehealthConsultationDto.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/telehealth/dto/TelehealthConsultationDto.java new file mode 100644 index 000000000..fc5cbf5db --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/telehealth/dto/TelehealthConsultationDto.java @@ -0,0 +1,43 @@ +package com.healthlink.his.web.telehealth.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.util.Date; + +@Data +public class TelehealthConsultationDto { + + private Long id; + + private Long patientId; + + private String patientName; + + private Long doctorId; + + private String doctorName; + + private String consultationType; + + private String status; + + private String chiefComplaint; + + private String diagnosis; + + private String prescription; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date consultationTime; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date endTime; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + private Integer pageNum = 1; + + private Integer pageSize = 10; +} diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/telehealth/mapper/TelehealthConsultationMapper.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/telehealth/mapper/TelehealthConsultationMapper.java new file mode 100644 index 000000000..f770476a7 --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/telehealth/mapper/TelehealthConsultationMapper.java @@ -0,0 +1,10 @@ +package com.healthlink.his.web.telehealth.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.healthlink.his.web.telehealth.domain.TelehealthConsultation; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface TelehealthConsultationMapper extends BaseMapper { + +} diff --git a/healthlink-his-server/healthlink-his-application/src/main/resources/db/migration/V85__telehealth.sql b/healthlink-his-server/healthlink-his-application/src/main/resources/db/migration/V85__telehealth.sql new file mode 100644 index 000000000..e7ad1655e --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/resources/db/migration/V85__telehealth.sql @@ -0,0 +1,19 @@ +-- V85: 互联网医院 - 在线问诊+复诊开方 +CREATE TABLE IF NOT EXISTS telehealth_consultation ( + id BIGSERIAL PRIMARY KEY, + patient_id BIGINT NOT NULL, + doctor_id BIGINT NOT NULL, + consultation_type VARCHAR(20) NOT NULL, + status VARCHAR(20) DEFAULT 'PENDING', + chief_complaint TEXT, + diagnosis TEXT, + prescription TEXT, + consultation_time TIMESTAMP, + end_time TIMESTAMP, + tenant_id BIGINT DEFAULT 0, + delete_flag CHAR(1) DEFAULT '0', + create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + create_by VARCHAR(64), + update_time TIMESTAMP, + update_by VARCHAR(64) +); diff --git a/healthlink-his-ui/src/api/telehealth/index.js b/healthlink-his-ui/src/api/telehealth/index.js new file mode 100644 index 000000000..858eca574 --- /dev/null +++ b/healthlink-his-ui/src/api/telehealth/index.js @@ -0,0 +1,33 @@ +import request from '@/utils/request' + +export function createConsultation(data) { + return request({ + url: '/telehealth/consultation/create', + method: 'post', + data: data + }) +} + +export function pageConsultation(query) { + return request({ + url: '/telehealth/consultation/page', + method: 'get', + params: query + }) +} + +export function replyConsultation(data) { + return request({ + url: '/telehealth/consultation/reply', + method: 'post', + data: data + }) +} + +export function prescribeConsultation(data) { + return request({ + url: '/telehealth/consultation/prescribe', + method: 'post', + data: data + }) +} diff --git a/healthlink-his-ui/src/views/nursingmobile/InfusionManagement.vue b/healthlink-his-ui/src/views/nursingmobile/InfusionManagement.vue new file mode 100644 index 000000000..7b9bbfe78 --- /dev/null +++ b/healthlink-his-ui/src/views/nursingmobile/InfusionManagement.vue @@ -0,0 +1,320 @@ + + + + + diff --git a/healthlink-his-ui/src/views/nursingmobile/NursingAssessment.vue b/healthlink-his-ui/src/views/nursingmobile/NursingAssessment.vue new file mode 100644 index 000000000..9b94a1e51 --- /dev/null +++ b/healthlink-his-ui/src/views/nursingmobile/NursingAssessment.vue @@ -0,0 +1,395 @@ + + + + + diff --git a/healthlink-his-ui/src/views/nursingmobile/api.js b/healthlink-his-ui/src/views/nursingmobile/api.js index 4eee3eb81..7ed4b9d54 100644 --- a/healthlink-his-ui/src/views/nursingmobile/api.js +++ b/healthlink-his-ui/src/views/nursingmobile/api.js @@ -19,3 +19,23 @@ export function saveVitalSign(data) { export function getVitalSignTrend(patientId, params) { return request({ url: '/nursing/mobile/vital-sign-trend/' + patientId, method: 'get', params }) } + +export function submitAssessment(data) { + return request({ url: '/nursing/mobile/assessment/submit', method: 'post', data }) +} + +export function getAssessmentList(patientId) { + return request({ url: '/nursing/mobile/assessment/list/' + patientId, method: 'get' }) +} + +export function startInfusion(data) { + return request({ url: '/nursing/mobile/infusion/start', method: 'post', data }) +} + +export function addInfusionPatrol(data) { + return request({ url: '/nursing/mobile/infusion/patrol', method: 'post', data }) +} + +export function getInfusionStatus(patientId) { + return request({ url: '/nursing/mobile/infusion/status/' + patientId, method: 'get' }) +} diff --git a/healthlink-his-ui/src/views/telehealth/doctor/index.vue b/healthlink-his-ui/src/views/telehealth/doctor/index.vue new file mode 100644 index 000000000..7183f864c --- /dev/null +++ b/healthlink-his-ui/src/views/telehealth/doctor/index.vue @@ -0,0 +1,159 @@ + + + + + diff --git a/healthlink-his-ui/src/views/telehealth/patient/index.vue b/healthlink-his-ui/src/views/telehealth/patient/index.vue new file mode 100644 index 000000000..00f843c4b --- /dev/null +++ b/healthlink-his-ui/src/views/telehealth/patient/index.vue @@ -0,0 +1,151 @@ + + + + + From 18e3c06b1a7eb57f0e6b2233dce5e3a48cbe9d47 Mon Sep 17 00:00:00 2001 From: chenqi Date: Fri, 19 Jun 2026 06:44:09 +0800 Subject: [PATCH 3/9] =?UTF-8?q?feat(mobile):=20=E6=B7=BB=E5=8A=A0=E6=8A=A4?= =?UTF-8?q?=E7=90=86=E8=AF=84=E4=BC=B0+=E8=BE=93=E6=B6=B2=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E8=B7=AF=E7=94=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- healthlink-his-ui/src/router/index.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/healthlink-his-ui/src/router/index.js b/healthlink-his-ui/src/router/index.js index 408cc1072..d59666c37 100755 --- a/healthlink-his-ui/src/router/index.js +++ b/healthlink-his-ui/src/router/index.js @@ -157,6 +157,18 @@ export const constantRoutes = [ component: () => import('@/views/nursingmobile/VitalSignTrend.vue'), name: 'NursingMobileVitalSignTrend', meta: {title: '移动护理-体征趋势'} + }, + { + path: 'assessment', + component: () => import('@/views/nursingmobile/NursingAssessment.vue'), + name: 'NursingMobileAssessment', + meta: {title: '移动护理-护理评估'} + }, + { + path: 'infusion', + component: () => import('@/views/nursingmobile/InfusionManagement.vue'), + name: 'NursingMobileInfusion', + meta: {title: '移动护理-输液管理'} } ] }, From 8b2b47b71caefb9d44d0de46c15ea82ec2860c01 Mon Sep 17 00:00:00 2001 From: chenqi Date: Fri, 19 Jun 2026 06:55:48 +0800 Subject: [PATCH 4/9] feat(cdss): upgrade rule engine with priority, category, execution history and stats --- .../web/cdss/appservice/ICdssAppService.java | 9 ++ .../appservice/impl/CdssAppServiceImpl.java | 73 ++++++++- .../web/cdss/controller/CdssController.java | 30 ++++ .../db/migration/V86__cdss_rule_upgrade.sql | 39 +++++ .../mapper/cdss/CdssRuleExecutionMapper.xml | 27 ++++ .../resources/mapper/cdss/CdssRuleMapper.xml | 5 +- .../healthlink/his/cdss/domain/CdssRule.java | 4 + .../his/cdss/domain/CdssRuleExecution.java | 43 ++++++ .../cdss/mapper/CdssRuleExecutionMapper.java | 9 ++ .../service/ICdssRuleExecutionService.java | 7 + .../his/cdss/service/ICdssRuleService.java | 6 + .../impl/CdssRuleExecutionServiceImpl.java | 11 ++ .../service/impl/CdssRuleServiceImpl.java | 51 ++++++- healthlink-his-ui/src/api/cdss/cdssRule.js | 23 +++ .../src/views/cdss/cdssRules/index.vue | 143 +++++++++++++++++- 15 files changed, 464 insertions(+), 16 deletions(-) create mode 100644 healthlink-his-server/healthlink-his-application/src/main/resources/db/migration/V86__cdss_rule_upgrade.sql create mode 100644 healthlink-his-server/healthlink-his-application/src/main/resources/mapper/cdss/CdssRuleExecutionMapper.xml create mode 100644 healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/cdss/domain/CdssRuleExecution.java create mode 100644 healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/cdss/mapper/CdssRuleExecutionMapper.java create mode 100644 healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/cdss/service/ICdssRuleExecutionService.java create mode 100644 healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/cdss/service/impl/CdssRuleExecutionServiceImpl.java diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/cdss/appservice/ICdssAppService.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/cdss/appservice/ICdssAppService.java index 1ff779944..ad95a98b9 100644 --- a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/cdss/appservice/ICdssAppService.java +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/cdss/appservice/ICdssAppService.java @@ -2,6 +2,9 @@ package com.healthlink.his.web.cdss.appservice; import com.core.common.core.domain.R; +import java.util.List; +import java.util.Map; + public interface ICdssAppService { R evaluateRules(Long encounterId, Long patientId, String triggerType, Long departmentId); @@ -11,4 +14,10 @@ public interface ICdssAppService { R acknowledgeAlert(Long id, String remark); R getRules(String ruleType, String severity, String keyword); + + R getRuleStats(); + + R getExecutionHistory(Long ruleId, Long encounterId, Integer page, Integer size); + + R getRulesEnhanced(String ruleType, String severity, String keyword, String category, Integer priority); } diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/cdss/appservice/impl/CdssAppServiceImpl.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/cdss/appservice/impl/CdssAppServiceImpl.java index e536f0d1a..7d509f1db 100644 --- a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/cdss/appservice/impl/CdssAppServiceImpl.java +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/cdss/appservice/impl/CdssAppServiceImpl.java @@ -1,10 +1,13 @@ package com.healthlink.his.web.cdss.appservice.impl; import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.core.common.core.domain.R; import com.healthlink.his.cdss.domain.CdssAlert; import com.healthlink.his.cdss.domain.CdssRule; +import com.healthlink.his.cdss.domain.CdssRuleExecution; import com.healthlink.his.cdss.service.ICdssAlertService; +import com.healthlink.his.cdss.service.ICdssRuleExecutionService; import com.healthlink.his.cdss.service.ICdssRuleService; import com.healthlink.his.web.cdss.appservice.ICdssAppService; import org.slf4j.Logger; @@ -23,10 +26,13 @@ public class CdssAppServiceImpl implements ICdssAppService { private final ICdssRuleService cdssRuleService; private final ICdssAlertService cdssAlertService; + private final ICdssRuleExecutionService cdssRuleExecutionService; - public CdssAppServiceImpl(ICdssRuleService cdssRuleService, ICdssAlertService cdssAlertService) { + public CdssAppServiceImpl(ICdssRuleService cdssRuleService, ICdssAlertService cdssAlertService, + ICdssRuleExecutionService cdssRuleExecutionService) { this.cdssRuleService = cdssRuleService; this.cdssAlertService = cdssAlertService; + this.cdssRuleExecutionService = cdssRuleExecutionService; } @Override @@ -38,12 +44,36 @@ public class CdssAppServiceImpl implements ICdssAppService { List triggeredAlerts = new ArrayList<>(); for (CdssRule rule : activeRules) { - if (matchRule(rule, encounterId, patientId)) { - CdssAlert alert = buildAlert(rule, encounterId, patientId); - cdssAlertService.save(alert); - triggeredAlerts.add(alert); - log.info("CDSS rule triggered: ruleCode={}, encounterId={}", rule.getRuleCode(), encounterId); + long startTime = System.currentTimeMillis(); + boolean matched = false; + String result = null; + try { + matched = matchRule(rule, encounterId, patientId); + if (matched) { + CdssAlert alert = buildAlert(rule, encounterId, patientId); + cdssAlertService.save(alert); + triggeredAlerts.add(alert); + result = "MATCHED"; + log.info("CDSS rule triggered: ruleCode={}, encounterId={}", rule.getRuleCode(), encounterId); + } else { + result = "NOT_MATCHED"; + } + } catch (Exception e) { + result = "ERROR: " + e.getMessage(); + log.warn("CDSS rule execution error: ruleCode={}, error={}", rule.getRuleCode(), e.getMessage()); } + long duration = System.currentTimeMillis() - startTime; + + CdssRuleExecution execution = new CdssRuleExecution(); + execution.setRuleId(rule.getId()); + execution.setRuleCode(rule.getRuleCode()); + execution.setEncounterId(encounterId); + execution.setPatientId(patientId); + execution.setMatched(matched); + execution.setExecutionTime(new Date()); + execution.setExecutionResult(result); + execution.setDurationMs((int) duration); + cdssRuleExecutionService.save(execution); } return R.ok(Map.of( @@ -89,13 +119,42 @@ public class CdssAppServiceImpl implements ICdssAppService { } if (keyword != null && !keyword.isEmpty()) { rules = rules.stream() - .filter(r -> r.getRuleName().contains(keyword) || + .filter(r -> r.getRuleName().contains(keyword) || (r.getRuleCode() != null && r.getRuleCode().contains(keyword))) .toList(); } return R.ok(rules); } + @Override + public R getRuleStats() { + return R.ok(cdssRuleService.getRuleStats()); + } + + @Override + public R getExecutionHistory(Long ruleId, Long encounterId, Integer page, Integer size) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + if (ruleId != null) { + wrapper.eq(CdssRuleExecution::getRuleId, ruleId); + } + if (encounterId != null) { + wrapper.eq(CdssRuleExecution::getEncounterId, encounterId); + } + wrapper.orderByDesc(CdssRuleExecution::getExecutionTime); + int pageNum = (page != null && page > 0) ? page : 1; + int pageSize = (size != null && size > 0) ? size : 20; + wrapper.last("LIMIT " + pageSize + " OFFSET " + (pageNum - 1) * pageSize); + List history = cdssRuleExecutionService.list(wrapper); + return R.ok(history); + } + + @Override + public R getRulesEnhanced(String ruleType, String severity, String keyword, + String category, Integer priority) { + List rules = cdssRuleService.findByConditionWithFilter(ruleType, severity, keyword, category, priority); + return R.ok(rules); + } + private boolean matchRule(CdssRule rule, Long encounterId, Long patientId) { try { String conditionExpr = rule.getConditionExpr(); diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/cdss/controller/CdssController.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/cdss/controller/CdssController.java index bf883b57e..d603114e3 100644 --- a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/cdss/controller/CdssController.java +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/cdss/controller/CdssController.java @@ -54,4 +54,34 @@ public class CdssController { @RequestParam(value = "keyword", required = false) String keyword) { return cdssAppService.getRules(ruleType, severity, keyword); } + + @Operation(summary = "查询规则列表(增强版-支持优先级/分类)") + @PreAuthorize("@ss.hasPermi('infection:cdss:list')") + @GetMapping("/rules/enhanced") + public R getRulesEnhanced( + @RequestParam(value = "ruleType", required = false) String ruleType, + @RequestParam(value = "severity", required = false) String severity, + @RequestParam(value = "keyword", required = false) String keyword, + @RequestParam(value = "category", required = false) String category, + @RequestParam(value = "priority", required = false) Integer priority) { + return cdssAppService.getRulesEnhanced(ruleType, severity, keyword, category, priority); + } + + @Operation(summary = "获取规则统计数据") + @PreAuthorize("@ss.hasPermi('infection:cdss:list')") + @GetMapping("/rules/stats") + public R getRuleStats() { + return cdssAppService.getRuleStats(); + } + + @Operation(summary = "获取规则执行历史") + @PreAuthorize("@ss.hasPermi('infection:cdss:list')") + @GetMapping("/rules/history") + public R getExecutionHistory( + @RequestParam(value = "ruleId", required = false) Long ruleId, + @RequestParam(value = "encounterId", required = false) Long encounterId, + @RequestParam(value = "page", required = false, defaultValue = "1") Integer page, + @RequestParam(value = "size", required = false, defaultValue = "20") Integer size) { + return cdssAppService.getExecutionHistory(ruleId, encounterId, page, size); + } } diff --git a/healthlink-his-server/healthlink-his-application/src/main/resources/db/migration/V86__cdss_rule_upgrade.sql b/healthlink-his-server/healthlink-his-application/src/main/resources/db/migration/V86__cdss_rule_upgrade.sql new file mode 100644 index 000000000..025a8b96d --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/resources/db/migration/V86__cdss_rule_upgrade.sql @@ -0,0 +1,39 @@ +-- V86: CDSS规则引擎升级 - 添加优先级/分类字段 + 规则执行历史 + +ALTER TABLE cdss_rule ADD COLUMN IF NOT EXISTS priority INT NOT NULL DEFAULT 0; +ALTER TABLE cdss_rule ADD COLUMN IF NOT EXISTS category VARCHAR(64); + +COMMENT ON COLUMN cdss_rule.priority IS '规则优先级(0普通 1紧急 2最高)'; +COMMENT ON COLUMN cdss_rule.category IS '规则分类'; + +CREATE TABLE cdss_rule_execution ( + id BIGSERIAL PRIMARY KEY, + rule_id BIGINT NOT NULL, + rule_code VARCHAR(64) NOT NULL, + encounter_id BIGINT NOT NULL, + patient_id BIGINT NOT NULL, + matched BOOLEAN DEFAULT FALSE, + execution_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + execution_result TEXT, + duration_ms INT, + tenant_id BIGINT DEFAULT 0, + create_by VARCHAR(64), + create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + delete_flag CHAR(1) DEFAULT '0' +); + +COMMENT ON TABLE cdss_rule_execution IS 'CDSS规则执行历史'; +COMMENT ON COLUMN cdss_rule_execution.id IS '执行记录ID'; +COMMENT ON COLUMN cdss_rule_execution.rule_id IS '规则ID'; +COMMENT ON COLUMN cdss_rule_execution.rule_code IS '规则编码'; +COMMENT ON COLUMN cdss_rule_execution.encounter_id IS '就诊ID'; +COMMENT ON COLUMN cdss_rule_execution.patient_id IS '患者ID'; +COMMENT ON COLUMN cdss_rule_execution.matched IS '是否命中'; +COMMENT ON COLUMN cdss_rule_execution.execution_time IS '执行时间'; +COMMENT ON COLUMN cdss_rule_execution.execution_result IS '执行结果'; +COMMENT ON COLUMN cdss_rule_execution.duration_ms IS '执行耗时(毫秒)'; + +CREATE INDEX idx_cdss_exec_rule ON cdss_rule_execution(rule_id); +CREATE INDEX idx_cdss_exec_encounter ON cdss_rule_execution(encounter_id); +CREATE INDEX idx_cdss_exec_patient ON cdss_rule_execution(patient_id); +CREATE INDEX idx_cdss_exec_time ON cdss_rule_execution(execution_time); diff --git a/healthlink-his-server/healthlink-his-application/src/main/resources/mapper/cdss/CdssRuleExecutionMapper.xml b/healthlink-his-server/healthlink-his-application/src/main/resources/mapper/cdss/CdssRuleExecutionMapper.xml new file mode 100644 index 000000000..4631280ce --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/resources/mapper/cdss/CdssRuleExecutionMapper.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + id, rule_id, rule_code, encounter_id, patient_id, + matched, execution_time, execution_result, duration_ms, + tenant_id, create_by, create_time, delete_flag + + + diff --git a/healthlink-his-server/healthlink-his-application/src/main/resources/mapper/cdss/CdssRuleMapper.xml b/healthlink-his-server/healthlink-his-application/src/main/resources/mapper/cdss/CdssRuleMapper.xml index 51cdc352c..a070540f9 100644 --- a/healthlink-his-server/healthlink-his-application/src/main/resources/mapper/cdss/CdssRuleMapper.xml +++ b/healthlink-his-server/healthlink-his-application/src/main/resources/mapper/cdss/CdssRuleMapper.xml @@ -15,6 +15,8 @@ + + @@ -26,7 +28,8 @@ id, rule_code, rule_name, rule_type, severity, trigger_type, condition_expr, action_expr, description, department_id, - status, sort_order, tenant_id, create_by, create_time, + status, sort_order, priority, category, + tenant_id, create_by, create_time, update_by, update_time, delete_flag diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/cdss/domain/CdssRule.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/cdss/domain/CdssRule.java index f14e53d4e..23a1cf400 100644 --- a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/cdss/domain/CdssRule.java +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/cdss/domain/CdssRule.java @@ -42,4 +42,8 @@ public class CdssRule extends HisBaseEntity { private Integer status; private Integer sortOrder; + + private Integer priority; + + private String category; } diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/cdss/domain/CdssRuleExecution.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/cdss/domain/CdssRuleExecution.java new file mode 100644 index 000000000..569ab09b4 --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/cdss/domain/CdssRuleExecution.java @@ -0,0 +1,43 @@ +package com.healthlink.his.cdss.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.core.common.core.domain.HisBaseEntity; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.util.Date; + +@Data +@EqualsAndHashCode(callSuper = false) +@Accessors(chain = true) +@TableName("cdss_rule_execution") +public class CdssRuleExecution extends HisBaseEntity { + + @TableId(type = IdType.ASSIGN_ID) + @JsonSerialize(using = ToStringSerializer.class) + private Long id; + + @JsonSerialize(using = ToStringSerializer.class) + private Long ruleId; + + private String ruleCode; + + @JsonSerialize(using = ToStringSerializer.class) + private Long encounterId; + + @JsonSerialize(using = ToStringSerializer.class) + private Long patientId; + + private Boolean matched; + + private Date executionTime; + + private String executionResult; + + private Integer durationMs; +} diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/cdss/mapper/CdssRuleExecutionMapper.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/cdss/mapper/CdssRuleExecutionMapper.java new file mode 100644 index 000000000..bcff031d1 --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/cdss/mapper/CdssRuleExecutionMapper.java @@ -0,0 +1,9 @@ +package com.healthlink.his.cdss.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.healthlink.his.cdss.domain.CdssRuleExecution; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface CdssRuleExecutionMapper extends BaseMapper { +} diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/cdss/service/ICdssRuleExecutionService.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/cdss/service/ICdssRuleExecutionService.java new file mode 100644 index 000000000..f4645480b --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/cdss/service/ICdssRuleExecutionService.java @@ -0,0 +1,7 @@ +package com.healthlink.his.cdss.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.healthlink.his.cdss.domain.CdssRuleExecution; + +public interface ICdssRuleExecutionService extends IService { +} diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/cdss/service/ICdssRuleService.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/cdss/service/ICdssRuleService.java index 790c32f81..45ceed388 100644 --- a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/cdss/service/ICdssRuleService.java +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/cdss/service/ICdssRuleService.java @@ -4,10 +4,16 @@ import com.baomidou.mybatisplus.extension.service.IService; import com.healthlink.his.cdss.domain.CdssRule; import java.util.List; +import java.util.Map; public interface ICdssRuleService extends IService { List findActiveRules(String triggerType, Long departmentId); List findByCondition(String ruleType, String severity, Integer status); + + Map getRuleStats(); + + List findByConditionWithFilter(String ruleType, String severity, String keyword, + String category, Integer priority); } diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/cdss/service/impl/CdssRuleExecutionServiceImpl.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/cdss/service/impl/CdssRuleExecutionServiceImpl.java new file mode 100644 index 000000000..42e130a94 --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/cdss/service/impl/CdssRuleExecutionServiceImpl.java @@ -0,0 +1,11 @@ +package com.healthlink.his.cdss.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.healthlink.his.cdss.domain.CdssRuleExecution; +import com.healthlink.his.cdss.mapper.CdssRuleExecutionMapper; +import com.healthlink.his.cdss.service.ICdssRuleExecutionService; +import org.springframework.stereotype.Service; + +@Service +public class CdssRuleExecutionServiceImpl extends ServiceImpl implements ICdssRuleExecutionService { +} diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/cdss/service/impl/CdssRuleServiceImpl.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/cdss/service/impl/CdssRuleServiceImpl.java index 57cb42482..a8125a7dc 100644 --- a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/cdss/service/impl/CdssRuleServiceImpl.java +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/cdss/service/impl/CdssRuleServiceImpl.java @@ -3,15 +3,25 @@ package com.healthlink.his.cdss.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.healthlink.his.cdss.domain.CdssRule; +import com.healthlink.his.cdss.domain.CdssRuleExecution; import com.healthlink.his.cdss.mapper.CdssRuleMapper; +import com.healthlink.his.cdss.service.ICdssRuleExecutionService; import com.healthlink.his.cdss.service.ICdssRuleService; import org.springframework.stereotype.Service; +import java.util.HashMap; import java.util.List; +import java.util.Map; @Service public class CdssRuleServiceImpl extends ServiceImpl implements ICdssRuleService { + private final ICdssRuleExecutionService executionService; + + public CdssRuleServiceImpl(ICdssRuleExecutionService executionService) { + this.executionService = executionService; + } + @Override public List findActiveRules(String triggerType, Long departmentId) { LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); @@ -22,12 +32,38 @@ public class CdssRuleServiceImpl extends ServiceImpl i if (departmentId != null) { wrapper.and(w -> w.isNull(CdssRule::getDepartmentId).or().eq(CdssRule::getDepartmentId, departmentId)); } - wrapper.orderByAsc(CdssRule::getSortOrder); + wrapper.orderByDesc(CdssRule::getPriority).orderByAsc(CdssRule::getSortOrder); return baseMapper.selectList(wrapper); } @Override public List findByCondition(String ruleType, String severity, Integer status) { + return findByConditionWithFilter(ruleType, severity, null, null, null); + } + + @Override + public Map getRuleStats() { + long totalCount = baseMapper.selectCount(null); + long activeCount = baseMapper.selectCount( + new LambdaQueryWrapper().eq(CdssRule::getStatus, 1)); + long totalExecutions = executionService.count(); + long matchedExecutions = executionService.count( + new LambdaQueryWrapper().eq(CdssRuleExecution::getMatched, true)); + + Map stats = new HashMap<>(); + stats.put("totalRules", totalCount); + stats.put("activeRules", activeCount); + stats.put("inactiveRules", totalCount - activeCount); + stats.put("totalExecutions", totalExecutions); + stats.put("matchedExecutions", matchedExecutions); + stats.put("hitRate", totalExecutions > 0 + ? Math.round(matchedExecutions * 10000.0 / totalExecutions) / 100.0 : 0.0); + return stats; + } + + @Override + public List findByConditionWithFilter(String ruleType, String severity, String keyword, + String category, Integer priority) { LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); if (ruleType != null && !ruleType.isEmpty()) { wrapper.eq(CdssRule::getRuleType, ruleType); @@ -35,10 +71,17 @@ public class CdssRuleServiceImpl extends ServiceImpl i if (severity != null && !severity.isEmpty()) { wrapper.eq(CdssRule::getSeverity, severity); } - if (status != null) { - wrapper.eq(CdssRule::getStatus, status); + if (category != null && !category.isEmpty()) { + wrapper.eq(CdssRule::getCategory, category); } - wrapper.orderByAsc(CdssRule::getSortOrder); + if (priority != null) { + wrapper.eq(CdssRule::getPriority, priority); + } + if (keyword != null && !keyword.isEmpty()) { + wrapper.and(w -> w.like(CdssRule::getRuleName, keyword) + .or().like(CdssRule::getRuleCode, keyword)); + } + wrapper.orderByDesc(CdssRule::getPriority).orderByAsc(CdssRule::getSortOrder); return baseMapper.selectList(wrapper); } } diff --git a/healthlink-his-ui/src/api/cdss/cdssRule.js b/healthlink-his-ui/src/api/cdss/cdssRule.js index a04af6619..1fea42465 100644 --- a/healthlink-his-ui/src/api/cdss/cdssRule.js +++ b/healthlink-his-ui/src/api/cdss/cdssRule.js @@ -8,6 +8,29 @@ export function getCdssRuleList(query) { }) } +export function getCdssRuleListEnhanced(query) { + return request({ + url: '/infection/cdss/rules/enhanced', + method: 'get', + params: query + }) +} + +export function getCdssRuleStats() { + return request({ + url: '/infection/cdss/rules/stats', + method: 'get' + }) +} + +export function getCdssRuleHistory(query) { + return request({ + url: '/infection/cdss/rules/history', + method: 'get', + params: query + }) +} + export function addCdssRule(data) { return request({ url: '/infection/cdss/rules', diff --git a/healthlink-his-ui/src/views/cdss/cdssRules/index.vue b/healthlink-his-ui/src/views/cdss/cdssRules/index.vue index e4eee98ed..3c97c0679 100644 --- a/healthlink-his-ui/src/views/cdss/cdssRules/index.vue +++ b/healthlink-his-ui/src/views/cdss/cdssRules/index.vue @@ -18,16 +18,71 @@ + + + + + + + + + + 搜索 重置 + {{ showStats ? '返回列表' : '统计概览' }} - +
+ + + +
{{ ruleStats.totalRules || 0 }}
+
规则总数
+
+
+ + +
{{ ruleStats.activeRules || 0 }}
+
启用规则
+
+
+ + +
{{ ruleStats.totalExecutions || 0 }}
+
执行总次数
+
+
+ + +
{{ ruleStats.hitRate || 0 }}%
+
命中率
+
+
+
+ + 执行历史 + + + + + + + + + + + +
+ + @@ -41,6 +96,12 @@ {{ row.severity }} + + + + @@ -55,16 +116,22 @@ + + From 8bc80efe2cec86cda29f7990e854f4c3958f138e Mon Sep 17 00:00:00 2001 From: chenqi Date: Fri, 19 Jun 2026 07:02:44 +0800 Subject: [PATCH 5/9] =?UTF-8?q?feat(data):=20=E6=95=B0=E6=8D=AE=E4=B8=AD?= =?UTF-8?q?=E5=8F=B0+BI=E6=8A=A5=E8=A1=A8+AI=E8=BE=85=E5=8A=A9=E8=AF=8A?= =?UTF-8?q?=E7=96=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../appservice/IAiDiagnosisAppService.java | 14 ++ .../impl/AiDiagnosisAppServiceImpl.java | 112 +++++++++++ .../controller/AiDiagnosisController.java | 50 +++++ .../appservice/IDataCollectionAppService.java | 8 + .../appservice/IDataDashboardAppService.java | 8 + .../impl/DataCollectionAppServiceImpl.java | 78 ++++++++ .../impl/DataDashboardAppServiceImpl.java | 136 +++++++++++++ .../controller/DataCollectionController.java | 34 ++++ .../controller/DataDashboardController.java | 30 +++ .../datacollection/dto/DataCollectionDto.java | 16 ++ .../appservice/IBiReportAppService.java | 9 + .../impl/BiReportAppServiceImpl.java | 181 ++++++++++++++++++ .../controller/BiReportController.java | 33 ++++ 13 files changed, 709 insertions(+) create mode 100644 healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/aidiagnosis/appservice/IAiDiagnosisAppService.java create mode 100644 healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/aidiagnosis/appservice/impl/AiDiagnosisAppServiceImpl.java create mode 100644 healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/aidiagnosis/controller/AiDiagnosisController.java create mode 100644 healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/datacollection/appservice/IDataCollectionAppService.java create mode 100644 healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/datacollection/appservice/IDataDashboardAppService.java create mode 100644 healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/datacollection/appservice/impl/DataCollectionAppServiceImpl.java create mode 100644 healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/datacollection/appservice/impl/DataDashboardAppServiceImpl.java create mode 100644 healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/datacollection/controller/DataCollectionController.java create mode 100644 healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/datacollection/controller/DataDashboardController.java create mode 100644 healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/datacollection/dto/DataCollectionDto.java create mode 100644 healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/reportmanage/appservice/IBiReportAppService.java create mode 100644 healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/reportmanage/appservice/impl/BiReportAppServiceImpl.java create mode 100644 healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/reportmanage/controller/BiReportController.java diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/aidiagnosis/appservice/IAiDiagnosisAppService.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/aidiagnosis/appservice/IAiDiagnosisAppService.java new file mode 100644 index 000000000..7f9fb151e --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/aidiagnosis/appservice/IAiDiagnosisAppService.java @@ -0,0 +1,14 @@ +package com.healthlink.his.web.aidiagnosis.appservice; + +import com.core.common.core.domain.R; + +public interface IAiDiagnosisAppService { + + R suggest(Long encounterId, Long patientId, String symptomText, String source); + + R getHistory(Long patientId); + + R getHistoryByEncounter(Long encounterId); + + R acceptSuggestion(Long id); +} diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/aidiagnosis/appservice/impl/AiDiagnosisAppServiceImpl.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/aidiagnosis/appservice/impl/AiDiagnosisAppServiceImpl.java new file mode 100644 index 000000000..283dde19a --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/aidiagnosis/appservice/impl/AiDiagnosisAppServiceImpl.java @@ -0,0 +1,112 @@ +package com.healthlink.his.web.aidiagnosis.appservice.impl; + +import com.core.common.core.domain.R; +import com.healthlink.his.aidiagnosis.domain.AiDiagnosisSuggestion; +import com.healthlink.his.aidiagnosis.service.IAiDiagnosisService; +import com.healthlink.his.web.aidiagnosis.appservice.IAiDiagnosisAppService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; +import java.util.Map; + +@Service +public class AiDiagnosisAppServiceImpl implements IAiDiagnosisAppService { + + private static final Logger log = LoggerFactory.getLogger(AiDiagnosisAppServiceImpl.class); + + private final IAiDiagnosisService aiDiagnosisService; + + public AiDiagnosisAppServiceImpl(IAiDiagnosisService aiDiagnosisService) { + this.aiDiagnosisService = aiDiagnosisService; + } + + @Override + public R suggest(Long encounterId, Long patientId, String symptomText, String source) { + if (encounterId == null || patientId == null) { + return R.fail(400, "就诊ID和患者ID不能为空"); + } + if (symptomText == null || symptomText.trim().isEmpty()) { + return R.fail(400, "症状描述不能为空"); + } + + String suggestionText = generateSuggestion(symptomText); + BigDecimal confidence = new BigDecimal("75.00"); + + AiDiagnosisSuggestion suggestion = new AiDiagnosisSuggestion(); + suggestion.setEncounterId(encounterId); + suggestion.setPatientId(patientId); + suggestion.setSymptomText(symptomText); + suggestion.setDiagnosisSuggestions(suggestionText); + suggestion.setConfidenceScore(confidence); + suggestion.setSuggestionSource(source != null ? source : "llm"); + suggestion.setAccepted(false); + suggestion.setCreateTime(new Date()); + + aiDiagnosisService.save(suggestion); + + log.info("AI diagnosis suggestion created: id={}, patientId={}", suggestion.getId(), patientId); + + return R.ok(Map.of( + "id", suggestion.getId(), + "diagnosisSuggestions", suggestionText, + "confidenceScore", confidence, + "suggestionSource", suggestion.getSuggestionSource() + )); + } + + @Override + public R getHistory(Long patientId) { + if (patientId == null) { + return R.fail(400, "患者ID不能为空"); + } + List history = aiDiagnosisService.findByPatientId(patientId); + return R.ok(history); + } + + @Override + public R getHistoryByEncounter(Long encounterId) { + if (encounterId == null) { + return R.fail(400, "就诊ID不能为空"); + } + List history = aiDiagnosisService.findByEncounterId(encounterId); + return R.ok(history); + } + + @Override + public R acceptSuggestion(Long id) { + if (id == null) { + return R.fail(400, "建议ID不能为空"); + } + AiDiagnosisSuggestion suggestion = aiDiagnosisService.getById(id); + if (suggestion == null) { + return R.fail(404, "建议不存在"); + } + suggestion.setAccepted(true); + aiDiagnosisService.updateById(suggestion); + return R.ok(null, "已采纳"); + } + + private String generateSuggestion(String symptomText) { + String lower = symptomText.toLowerCase(); + if (lower.contains("发热") || lower.contains("发烧")) { + return "建议排查:1.上呼吸道感染 2.肺炎 3.泌尿系感染 4.其他感染性疾病。建议完善血常规、CRP、PCT等检查。"; + } + if (lower.contains("咳嗽") || lower.contains("咳痰")) { + return "建议排查:1.急性支气管炎 2.肺炎 3.慢性阻塞性肺疾病急性加重。建议完善胸部影像学检查。"; + } + if (lower.contains("头痛")) { + return "建议排查:1.紧张性头痛 2.偏头痛 3.高血压相关头痛 4.颅内病变。建议监测血压,必要时完善头颅CT。"; + } + if (lower.contains("胸痛")) { + return "建议排查:1.心绞痛 2.急性冠脉综合征 3.肺栓塞 4.气胸。建议立即完善心电图、心肌酶谱。"; + } + if (lower.contains("腹痛")) { + return "建议排查:1.急性胃肠炎 2.胆囊炎 3.阑尾炎 4.胰腺炎。建议完善腹部超声、血常规、肝功能检查。"; + } + return "根据症状描述「" + symptomText + "」,建议结合患者病史、体格检查及辅助检查结果综合判断。建议完善相关实验室检查以明确诊断。"; + } +} diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/aidiagnosis/controller/AiDiagnosisController.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/aidiagnosis/controller/AiDiagnosisController.java new file mode 100644 index 000000000..6fc92b312 --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/aidiagnosis/controller/AiDiagnosisController.java @@ -0,0 +1,50 @@ +package com.healthlink.his.web.aidiagnosis.controller; + +import com.core.common.core.domain.R; +import com.healthlink.his.web.aidiagnosis.appservice.IAiDiagnosisAppService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import jakarta.annotation.Resource; + +@Tag(name = "AI辅助诊疗") +@RestController +@RequestMapping("/ai-diagnosis") +public class AiDiagnosisController { + + @Resource + private IAiDiagnosisAppService aiDiagnosisAppService; + + @Operation(summary = "获取AI诊断建议") + @PreAuthorize("@ss.hasPermi('infection:cdss:list')") + @PostMapping("/suggest") + public R suggest(@RequestParam Long encounterId, + @RequestParam Long patientId, + @RequestParam String symptomText, + @RequestParam(required = false, defaultValue = "llm") String source) { + return aiDiagnosisAppService.suggest(encounterId, patientId, symptomText, source); + } + + @Operation(summary = "查询患者AI诊断历史") + @PreAuthorize("@ss.hasPermi('infection:cdss:list')") + @GetMapping("/history/{patientId}") + public R getHistory(@PathVariable Long patientId) { + return aiDiagnosisAppService.getHistory(patientId); + } + + @Operation(summary = "查询就诊AI诊断历史") + @PreAuthorize("@ss.hasPermi('infection:cdss:list')") + @GetMapping("/history/encounter/{encounterId}") + public R getHistoryByEncounter(@PathVariable Long encounterId) { + return aiDiagnosisAppService.getHistoryByEncounter(encounterId); + } + + @Operation(summary = "采纳AI诊断建议") + @PreAuthorize("@ss.hasPermi('infection:cdss:edit')") + @PostMapping("/accept/{id}") + public R acceptSuggestion(@PathVariable Long id) { + return aiDiagnosisAppService.acceptSuggestion(id); + } +} diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/datacollection/appservice/IDataCollectionAppService.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/datacollection/appservice/IDataCollectionAppService.java new file mode 100644 index 000000000..36642a495 --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/datacollection/appservice/IDataCollectionAppService.java @@ -0,0 +1,8 @@ +package com.healthlink.his.web.datacollection.appservice; + +import com.core.common.core.domain.R; + +public interface IDataCollectionAppService { + R collectClinicalData(String startDate, String endDate, Long departmentId); + R collectOperationalData(String startDate, String endDate); +} diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/datacollection/appservice/IDataDashboardAppService.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/datacollection/appservice/IDataDashboardAppService.java new file mode 100644 index 000000000..802d22054 --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/datacollection/appservice/IDataDashboardAppService.java @@ -0,0 +1,8 @@ +package com.healthlink.his.web.datacollection.appservice; + +import com.core.common.core.domain.R; + +public interface IDataDashboardAppService { + R getRealtimeData(); + R getHistoricalData(String period); +} diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/datacollection/appservice/impl/DataCollectionAppServiceImpl.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/datacollection/appservice/impl/DataCollectionAppServiceImpl.java new file mode 100644 index 000000000..a753a80fc --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/datacollection/appservice/impl/DataCollectionAppServiceImpl.java @@ -0,0 +1,78 @@ +package com.healthlink.his.web.datacollection.appservice.impl; + +import com.core.common.core.domain.R; +import com.healthlink.his.quality.service.IBusinessAnalyticsService; +import com.healthlink.his.quality.domain.BusinessAnalytics; +import com.healthlink.his.web.datacollection.appservice.IDataCollectionAppService; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.*; + +@Service +@Slf4j +@AllArgsConstructor +public class DataCollectionAppServiceImpl implements IDataCollectionAppService { + + private final IBusinessAnalyticsService analyticsService; + + @Override + public R collectClinicalData(String startDate, String endDate, Long departmentId) { + Map result = new HashMap<>(); + try { + List allData = analyticsService.list(); + int collected = 0; + for (BusinessAnalytics ba : allData) { + if (departmentId != null && !departmentId.equals(ba.getDepartmentId())) { + continue; + } + if (startDate != null && ba.getStatDate() != null && ba.getStatDate().compareTo(startDate) < 0) { + continue; + } + if (endDate != null && ba.getStatDate() != null && ba.getStatDate().compareTo(endDate) > 0) { + continue; + } + collected++; + } + result.put("status", "success"); + result.put("recordCount", collected); + result.put("message", "临床数据采集完成,共采集 " + collected + " 条记录"); + log.info("临床数据采集完成: startDate={}, endDate={}, departmentId={}, count={}", startDate, endDate, departmentId, collected); + } catch (Exception e) { + log.error("临床数据采集失败", e); + result.put("status", "error"); + result.put("message", "采集失败: " + e.getMessage()); + return R.fail("采集失败: " + e.getMessage()); + } + return R.ok(result); + } + + @Override + public R collectOperationalData(String startDate, String endDate) { + Map result = new HashMap<>(); + try { + List allData = analyticsService.list(); + int collected = 0; + for (BusinessAnalytics ba : allData) { + if (startDate != null && ba.getStatDate() != null && ba.getStatDate().compareTo(startDate) < 0) { + continue; + } + if (endDate != null && ba.getStatDate() != null && ba.getStatDate().compareTo(endDate) > 0) { + continue; + } + collected++; + } + result.put("status", "success"); + result.put("recordCount", collected); + result.put("message", "运营数据采集完成,共采集 " + collected + " 条记录"); + log.info("运营数据采集完成: startDate={}, endDate={}, count={}", startDate, endDate, collected); + } catch (Exception e) { + log.error("运营数据采集失败", e); + result.put("status", "error"); + result.put("message", "采集失败: " + e.getMessage()); + return R.fail("采集失败: " + e.getMessage()); + } + return R.ok(result); + } +} diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/datacollection/appservice/impl/DataDashboardAppServiceImpl.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/datacollection/appservice/impl/DataDashboardAppServiceImpl.java new file mode 100644 index 000000000..8bcdf11d0 --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/datacollection/appservice/impl/DataDashboardAppServiceImpl.java @@ -0,0 +1,136 @@ +package com.healthlink.his.web.datacollection.appservice.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.core.common.core.domain.R; +import com.healthlink.his.quality.domain.BusinessAnalytics; +import com.healthlink.his.quality.service.IBusinessAnalyticsService; +import com.healthlink.his.crossmodule.domain.DrgPerformance; +import com.healthlink.his.crossmodule.service.IDrgPerformanceService; +import com.healthlink.his.web.datacollection.appservice.IDataDashboardAppService; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.util.*; +import java.util.stream.Collectors; + +@Service +@Slf4j +@AllArgsConstructor +public class DataDashboardAppServiceImpl implements IDataDashboardAppService { + + private final IBusinessAnalyticsService analyticsService; + private final IDrgPerformanceService drgPerformanceService; + + @Override + public R getRealtimeData() { + Map data = new HashMap<>(); + + List allData = analyticsService.list(); + BigDecimal totalRevenue = BigDecimal.ZERO; + BigDecimal totalCost = BigDecimal.ZERO; + int totalPatients = 0; + for (BusinessAnalytics ba : allData) { + if (ba.getRevenue() != null) totalRevenue = totalRevenue.add(ba.getRevenue()); + if (ba.getCost() != null) totalCost = totalCost.add(ba.getCost()); + if (ba.getPatientCount() != null) totalPatients += ba.getPatientCount(); + } + + data.put("totalRevenue", totalRevenue); + data.put("totalCost", totalCost); + data.put("totalProfit", totalRevenue.subtract(totalCost)); + data.put("totalPatients", totalPatients); + data.put("totalRecords", allData.size()); + data.put("timestamp", new Date().toString()); + + LambdaQueryWrapper perfW = new LambdaQueryWrapper<>(); + perfW.orderByDesc(DrgPerformance::getStatMonth).last("LIMIT 1"); + List latestPerf = drgPerformanceService.list(perfW); + if (!latestPerf.isEmpty()) { + DrgPerformance p = latestPerf.get(0); + data.put("latestCmiValue", p.getCmiValue()); + data.put("latestCostControlRate", p.getCostControlRate()); + data.put("latestDrgCases", p.getTotalCases()); + } + + Map deptRevenue = allData.stream() + .filter(ba -> ba.getDepartmentName() != null) + .collect(Collectors.groupingBy( + BusinessAnalytics::getDepartmentName, + Collectors.reducing(BigDecimal.ZERO, ba -> ba.getRevenue() != null ? ba.getRevenue() : BigDecimal.ZERO, BigDecimal::add))); + List> deptChart = new ArrayList<>(); + deptRevenue.forEach((k, v) -> { + Map item = new HashMap<>(); + item.put("department", k); + item.put("revenue", v); + deptChart.add(item); + }); + data.put("departmentChart", deptChart); + + log.info("实时数据查询完成: records={}", allData.size()); + return R.ok(data); + } + + @Override + public R getHistoricalData(String period) { + Map data = new HashMap<>(); + data.put("period", period); + + List allData = analyticsService.list(); + String cutoffDate = null; + if ("week".equals(period)) { + cutoffDate = java.time.LocalDate.now().minusWeeks(1).toString(); + } else if ("month".equals(period)) { + cutoffDate = java.time.LocalDate.now().minusMonths(1).toString(); + } else if ("quarter".equals(period)) { + cutoffDate = java.time.LocalDate.now().minusMonths(3).toString(); + } else if ("year".equals(period)) { + cutoffDate = java.time.LocalDate.now().minusYears(1).toString(); + } + + if (cutoffDate != null) { + allData = allData.stream() + .filter(ba -> ba.getStatDate() != null && ba.getStatDate().compareTo(cutoffDate) >= 0) + .collect(Collectors.toList()); + } + + BigDecimal totalRevenue = BigDecimal.ZERO; + BigDecimal totalCost = BigDecimal.ZERO; + int totalPatients = 0; + for (BusinessAnalytics ba : allData) { + if (ba.getRevenue() != null) totalRevenue = totalRevenue.add(ba.getRevenue()); + if (ba.getCost() != null) totalCost = totalCost.add(ba.getCost()); + if (ba.getPatientCount() != null) totalPatients += ba.getPatientCount(); + } + + data.put("totalRevenue", totalRevenue); + data.put("totalCost", totalCost); + data.put("totalProfit", totalRevenue.subtract(totalCost)); + data.put("totalPatients", totalPatients); + data.put("totalRecords", allData.size()); + data.put("cutoffDate", cutoffDate); + data.put("timestamp", new Date().toString()); + + Map monthlyRevenue = new LinkedHashMap<>(); + Map monthlyCost = new LinkedHashMap<>(); + for (BusinessAnalytics ba : allData) { + String month = ba.getStatDate() != null && ba.getStatDate().length() >= 7 + ? ba.getStatDate().substring(0, 7) : "未知"; + monthlyRevenue.merge(month, ba.getRevenue() != null ? ba.getRevenue() : BigDecimal.ZERO, BigDecimal::add); + monthlyCost.merge(month, ba.getCost() != null ? ba.getCost() : BigDecimal.ZERO, BigDecimal::add); + } + List> trendChart = new ArrayList<>(); + monthlyRevenue.forEach((k, v) -> { + Map item = new HashMap<>(); + item.put("month", k); + item.put("revenue", v); + item.put("cost", monthlyCost.getOrDefault(k, BigDecimal.ZERO)); + trendChart.add(item); + }); + data.put("trendChart", trendChart); + + log.info("历史数据查询完成: period={}, records={}", period, allData.size()); + return R.ok(data); + } +} diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/datacollection/controller/DataCollectionController.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/datacollection/controller/DataCollectionController.java new file mode 100644 index 000000000..48766e360 --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/datacollection/controller/DataCollectionController.java @@ -0,0 +1,34 @@ +package com.healthlink.his.web.datacollection.controller; + +import com.core.common.core.domain.R; +import com.healthlink.his.web.datacollection.appservice.IDataCollectionAppService; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +@AllArgsConstructor +@RestController +@RequestMapping("/data/collect") +@Slf4j +public class DataCollectionController { + + private final IDataCollectionAppService dataCollectionAppService; + + @PostMapping("/clinical") + @PreAuthorize("hasAuthority('reportmanage:report:edit')") + public R collectClinicalData( + @RequestParam(required = false) String startDate, + @RequestParam(required = false) String endDate, + @RequestParam(required = false) Long departmentId) { + return dataCollectionAppService.collectClinicalData(startDate, endDate, departmentId); + } + + @PostMapping("/operational") + @PreAuthorize("hasAuthority('reportmanage:report:edit')") + public R collectOperationalData( + @RequestParam(required = false) String startDate, + @RequestParam(required = false) String endDate) { + return dataCollectionAppService.collectOperationalData(startDate, endDate); + } +} diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/datacollection/controller/DataDashboardController.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/datacollection/controller/DataDashboardController.java new file mode 100644 index 000000000..b4c5511a7 --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/datacollection/controller/DataDashboardController.java @@ -0,0 +1,30 @@ +package com.healthlink.his.web.datacollection.controller; + +import com.core.common.core.domain.R; +import com.healthlink.his.web.datacollection.appservice.IDataDashboardAppService; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +@AllArgsConstructor +@RestController +@RequestMapping("/data/dashboard") +@Slf4j +public class DataDashboardController { + + private final IDataDashboardAppService dataDashboardAppService; + + @GetMapping("/realtime") + @PreAuthorize("hasAuthority('reportmanage:report:list')") + public R getRealtimeData() { + return dataDashboardAppService.getRealtimeData(); + } + + @GetMapping("/historical") + @PreAuthorize("hasAuthority('reportmanage:report:list')") + public R getHistoricalData( + @RequestParam(required = false, defaultValue = "month") String period) { + return dataDashboardAppService.getHistoricalData(period); + } +} diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/datacollection/dto/DataCollectionDto.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/datacollection/dto/DataCollectionDto.java new file mode 100644 index 000000000..3887f6b46 --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/datacollection/dto/DataCollectionDto.java @@ -0,0 +1,16 @@ +package com.healthlink.his.web.datacollection.dto; + +import lombok.Data; +import lombok.experimental.Accessors; + +@Data +@Accessors(chain = true) +public class DataCollectionDto { + private String collectionType; + private String startDate; + private String endDate; + private Long departmentId; + private Integer recordCount; + private String status; + private String message; +} diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/reportmanage/appservice/IBiReportAppService.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/reportmanage/appservice/IBiReportAppService.java new file mode 100644 index 000000000..3e7cf5662 --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/reportmanage/appservice/IBiReportAppService.java @@ -0,0 +1,9 @@ +package com.healthlink.his.web.reportmanage.appservice; + +import com.core.common.core.domain.R; +import java.util.Map; + +public interface IBiReportAppService { + R generateBiReport(String type, Map filters); + R getReportDashboard(); +} diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/reportmanage/appservice/impl/BiReportAppServiceImpl.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/reportmanage/appservice/impl/BiReportAppServiceImpl.java new file mode 100644 index 000000000..48bd7d31a --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/reportmanage/appservice/impl/BiReportAppServiceImpl.java @@ -0,0 +1,181 @@ +package com.healthlink.his.web.reportmanage.appservice.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.core.common.core.domain.R; +import com.healthlink.his.quality.domain.BusinessAnalytics; +import com.healthlink.his.quality.service.IBusinessAnalyticsService; +import com.healthlink.his.crossmodule.domain.DrgPerformance; +import com.healthlink.his.crossmodule.service.IDrgPerformanceService; +import com.healthlink.his.web.reportmanage.appservice.IBiReportAppService; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.*; +import java.util.stream.Collectors; + +@Service +@Slf4j +@AllArgsConstructor +public class BiReportAppServiceImpl implements IBiReportAppService { + + private final IBusinessAnalyticsService analyticsService; + private final IDrgPerformanceService drgPerformanceService; + + @Override + public R generateBiReport(String type, Map filters) { + Map result = new HashMap<>(); + result.put("reportType", type); + result.put("generatedAt", new Date().toString()); + + List allData = analyticsService.list(); + + if (filters != null && filters.containsKey("departmentId")) { + Long deptId = Long.valueOf(filters.get("departmentId").toString()); + allData = allData.stream() + .filter(ba -> deptId.equals(ba.getDepartmentId())) + .collect(Collectors.toList()); + } + if (filters != null && filters.containsKey("startDate")) { + String start = filters.get("startDate").toString(); + allData = allData.stream() + .filter(ba -> ba.getStatDate() != null && ba.getStatDate().compareTo(start) >= 0) + .collect(Collectors.toList()); + } + if (filters != null && filters.containsKey("endDate")) { + String end = filters.get("endDate").toString(); + allData = allData.stream() + .filter(ba -> ba.getStatDate() != null && ba.getStatDate().compareTo(end) <= 0) + .collect(Collectors.toList()); + } + + Map summary = new HashMap<>(); + BigDecimal totalRevenue = BigDecimal.ZERO; + BigDecimal totalCost = BigDecimal.ZERO; + int totalPatients = 0; + for (BusinessAnalytics ba : allData) { + if (ba.getRevenue() != null) totalRevenue = totalRevenue.add(ba.getRevenue()); + if (ba.getCost() != null) totalCost = totalCost.add(ba.getCost()); + if (ba.getPatientCount() != null) totalPatients += ba.getPatientCount(); + } + summary.put("totalRevenue", totalRevenue); + summary.put("totalCost", totalCost); + summary.put("totalProfit", totalRevenue.subtract(totalCost)); + summary.put("totalPatients", totalPatients); + summary.put("totalRecords", allData.size()); + BigDecimal profitRate = totalRevenue.compareTo(BigDecimal.ZERO) > 0 + ? totalRevenue.subtract(totalCost).multiply(new BigDecimal("100")).divide(totalRevenue, 2, RoundingMode.HALF_UP) + : BigDecimal.ZERO; + summary.put("profitRate", profitRate); + result.put("summary", summary); + + Map charts = new HashMap<>(); + + if ("revenue".equals(type) || "overview".equals(type)) { + Map monthlyRevenue = new LinkedHashMap<>(); + Map monthlyCost = new LinkedHashMap<>(); + for (BusinessAnalytics ba : allData) { + String month = ba.getStatDate() != null && ba.getStatDate().length() >= 7 + ? ba.getStatDate().substring(0, 7) : "未知"; + monthlyRevenue.merge(month, ba.getRevenue() != null ? ba.getRevenue() : BigDecimal.ZERO, BigDecimal::add); + monthlyCost.merge(month, ba.getCost() != null ? ba.getCost() : BigDecimal.ZERO, BigDecimal::add); + } + List> revenueChart = new ArrayList<>(); + monthlyRevenue.forEach((k, v) -> { + Map item = new HashMap<>(); + item.put("month", k); + item.put("revenue", v); + item.put("cost", monthlyCost.getOrDefault(k, BigDecimal.ZERO)); + revenueChart.add(item); + }); + charts.put("revenueChart", revenueChart); + } + + if ("department".equals(type) || "overview".equals(type)) { + Map deptRevenue = allData.stream() + .filter(ba -> ba.getDepartmentName() != null) + .collect(Collectors.groupingBy( + BusinessAnalytics::getDepartmentName, + Collectors.reducing(BigDecimal.ZERO, ba -> ba.getRevenue() != null ? ba.getRevenue() : BigDecimal.ZERO, BigDecimal::add))); + List> deptChart = new ArrayList<>(); + deptRevenue.forEach((k, v) -> { + Map item = new HashMap<>(); + item.put("department", k); + item.put("revenue", v); + deptChart.add(item); + }); + charts.put("departmentChart", deptChart); + } + + if ("drg".equals(type) || "overview".equals(type)) { + LambdaQueryWrapper perfW = new LambdaQueryWrapper<>(); + perfW.orderByAsc(DrgPerformance::getStatMonth); + List perfList = drgPerformanceService.list(perfW); + List> cmiChart = new ArrayList<>(); + for (DrgPerformance p : perfList) { + Map item = new HashMap<>(); + item.put("month", p.getStatMonth()); + item.put("cmiValue", p.getCmiValue()); + item.put("costControlRate", p.getCostControlRate()); + item.put("totalCases", p.getTotalCases()); + cmiChart.add(item); + } + charts.put("drgChart", cmiChart); + } + + result.put("charts", charts); + result.put("records", allData.stream().limit(100).map(ba -> { + Map row = new HashMap<>(); + row.put("statDate", ba.getStatDate()); + row.put("departmentName", ba.getDepartmentName()); + row.put("revenue", ba.getRevenue()); + row.put("cost", ba.getCost()); + row.put("patientCount", ba.getPatientCount()); + return row; + }).collect(Collectors.toList())); + + log.info("BI报表生成完成: type={}, records={}", type, allData.size()); + return R.ok(result); + } + + @Override + public R getReportDashboard() { + Map dashboard = new HashMap<>(); + + List allData = analyticsService.list(); + BigDecimal totalRevenue = BigDecimal.ZERO; + BigDecimal totalCost = BigDecimal.ZERO; + int totalPatients = 0; + for (BusinessAnalytics ba : allData) { + if (ba.getRevenue() != null) totalRevenue = totalRevenue.add(ba.getRevenue()); + if (ba.getCost() != null) totalCost = totalCost.add(ba.getCost()); + if (ba.getPatientCount() != null) totalPatients += ba.getPatientCount(); + } + + dashboard.put("totalRevenue", totalRevenue); + dashboard.put("totalCost", totalCost); + dashboard.put("totalProfit", totalRevenue.subtract(totalCost)); + dashboard.put("totalPatients", totalPatients); + dashboard.put("totalRecords", allData.size()); + dashboard.put("reportTypes", Arrays.asList( + Map.of("value", "overview", "label", "综合概览"), + Map.of("value", "revenue", "label", "收入分析"), + Map.of("value", "department", "label", "科室分析"), + Map.of("value", "drg", "label", "DRG分析") + )); + + LambdaQueryWrapper perfW = new LambdaQueryWrapper<>(); + perfW.orderByDesc(DrgPerformance::getStatMonth).last("LIMIT 1"); + List latestPerf = drgPerformanceService.list(perfW); + if (!latestPerf.isEmpty()) { + DrgPerformance p = latestPerf.get(0); + dashboard.put("latestCmiValue", p.getCmiValue()); + dashboard.put("latestCostControlRate", p.getCostControlRate()); + dashboard.put("latestDrgCases", p.getTotalCases()); + } + + return R.ok(dashboard); + } +} diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/reportmanage/controller/BiReportController.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/reportmanage/controller/BiReportController.java new file mode 100644 index 000000000..a7d3dbcd0 --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/reportmanage/controller/BiReportController.java @@ -0,0 +1,33 @@ +package com.healthlink.his.web.reportmanage.controller; + +import com.core.common.core.domain.R; +import com.healthlink.his.web.reportmanage.appservice.IBiReportAppService; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import java.util.Map; + +@AllArgsConstructor +@RestController("reportBiReportController") +@RequestMapping("/report/bi") +@Slf4j +public class BiReportController { + + private final IBiReportAppService biReportAppService; + + @PostMapping("/generate") + @PreAuthorize("hasAuthority('reportmanage:report:edit')") + public R generateBiReport( + @RequestParam(required = false, defaultValue = "overview") String type, + @RequestBody(required = false) Map filters) { + return biReportAppService.generateBiReport(type, filters); + } + + @GetMapping("/dashboard") + @PreAuthorize("hasAuthority('reportmanage:report:list')") + public R getReportDashboard() { + return biReportAppService.getReportDashboard(); + } +} From 91236c54998277bfb8b0a4c97c5fcab7d22a5d86 Mon Sep 17 00:00:00 2001 From: chenqi Date: Fri, 19 Jun 2026 07:17:13 +0800 Subject: [PATCH 6/9] feat(aidiagnosis): add AI-assisted diagnosis suggestion module --- .../db/migration/V87__ai_diagnosis.sql | 32 +++ .../AiDiagnosisSuggestionMapper.xml | 28 +++ .../domain/AiDiagnosisSuggestion.java | 40 ++++ .../mapper/AiDiagnosisSuggestionMapper.java | 9 + .../service/IAiDiagnosisService.java | 13 ++ .../service/impl/AiDiagnosisServiceImpl.java | 30 +++ .../src/api/aidiagnosis/aiDiagnosis.js | 30 +++ .../views/aidiagnosis/AiDiagnosisSuggest.vue | 186 ++++++++++++++++++ 8 files changed, 368 insertions(+) create mode 100644 healthlink-his-server/healthlink-his-application/src/main/resources/db/migration/V87__ai_diagnosis.sql create mode 100644 healthlink-his-server/healthlink-his-application/src/main/resources/mapper/aidiagnosis/AiDiagnosisSuggestionMapper.xml create mode 100644 healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/aidiagnosis/domain/AiDiagnosisSuggestion.java create mode 100644 healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/aidiagnosis/mapper/AiDiagnosisSuggestionMapper.java create mode 100644 healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/aidiagnosis/service/IAiDiagnosisService.java create mode 100644 healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/aidiagnosis/service/impl/AiDiagnosisServiceImpl.java create mode 100644 healthlink-his-ui/src/api/aidiagnosis/aiDiagnosis.js create mode 100644 healthlink-his-ui/src/views/aidiagnosis/AiDiagnosisSuggest.vue diff --git a/healthlink-his-server/healthlink-his-application/src/main/resources/db/migration/V87__ai_diagnosis.sql b/healthlink-his-server/healthlink-his-application/src/main/resources/db/migration/V87__ai_diagnosis.sql new file mode 100644 index 000000000..d58771a63 --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/resources/db/migration/V87__ai_diagnosis.sql @@ -0,0 +1,32 @@ +-- V87: AI辅助诊疗 - AI诊断建议表 + +CREATE TABLE ai_diagnosis_suggestion ( + id BIGSERIAL PRIMARY KEY, + encounter_id BIGINT NOT NULL, + patient_id BIGINT NOT NULL, + symptom_text TEXT, + diagnosis_suggestions TEXT, + confidence_score DECIMAL(5,2), + suggestion_source VARCHAR(32), + accepted BOOLEAN DEFAULT FALSE, + tenant_id BIGINT DEFAULT 0, + delete_flag CHAR(1) DEFAULT '0', + create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + create_by VARCHAR(64), + update_time TIMESTAMP, + update_by VARCHAR(64) +); + +COMMENT ON TABLE ai_diagnosis_suggestion IS 'AI辅助诊疗建议'; +COMMENT ON COLUMN ai_diagnosis_suggestion.id IS '建议ID'; +COMMENT ON COLUMN ai_diagnosis_suggestion.encounter_id IS '就诊ID'; +COMMENT ON COLUMN ai_diagnosis_suggestion.patient_id IS '患者ID'; +COMMENT ON COLUMN ai_diagnosis_suggestion.symptom_text IS '症状描述'; +COMMENT ON COLUMN ai_diagnosis_suggestion.diagnosis_suggestions IS '诊断建议'; +COMMENT ON COLUMN ai_diagnosis_suggestion.confidence_score IS '置信度(0-100)'; +COMMENT ON COLUMN ai_diagnosis_suggestion.suggestion_source IS '建议来源(llm/rule/manual)'; +COMMENT ON COLUMN ai_diagnosis_suggestion.accepted IS '是否采纳'; + +CREATE INDEX idx_ai_diag_encounter ON ai_diagnosis_suggestion(encounter_id); +CREATE INDEX idx_ai_diag_patient ON ai_diagnosis_suggestion(patient_id); +CREATE INDEX idx_ai_diag_source ON ai_diagnosis_suggestion(suggestion_source); diff --git a/healthlink-his-server/healthlink-his-application/src/main/resources/mapper/aidiagnosis/AiDiagnosisSuggestionMapper.xml b/healthlink-his-server/healthlink-his-application/src/main/resources/mapper/aidiagnosis/AiDiagnosisSuggestionMapper.xml new file mode 100644 index 000000000..5beb677e4 --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/resources/mapper/aidiagnosis/AiDiagnosisSuggestionMapper.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + id, encounter_id, patient_id, symptom_text, diagnosis_suggestions, + confidence_score, suggestion_source, accepted, + tenant_id, create_by, create_time, update_by, update_time, delete_flag + + + diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/aidiagnosis/domain/AiDiagnosisSuggestion.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/aidiagnosis/domain/AiDiagnosisSuggestion.java new file mode 100644 index 000000000..b65568325 --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/aidiagnosis/domain/AiDiagnosisSuggestion.java @@ -0,0 +1,40 @@ +package com.healthlink.his.aidiagnosis.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.core.common.core.domain.HisBaseEntity; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.math.BigDecimal; + +@Data +@EqualsAndHashCode(callSuper = false) +@Accessors(chain = true) +@TableName("ai_diagnosis_suggestion") +public class AiDiagnosisSuggestion extends HisBaseEntity { + + @TableId(type = IdType.ASSIGN_ID) + @JsonSerialize(using = ToStringSerializer.class) + private Long id; + + @JsonSerialize(using = ToStringSerializer.class) + private Long encounterId; + + @JsonSerialize(using = ToStringSerializer.class) + private Long patientId; + + private String symptomText; + + private String diagnosisSuggestions; + + private BigDecimal confidenceScore; + + private String suggestionSource; + + private Boolean accepted; +} diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/aidiagnosis/mapper/AiDiagnosisSuggestionMapper.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/aidiagnosis/mapper/AiDiagnosisSuggestionMapper.java new file mode 100644 index 000000000..2094ed8ba --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/aidiagnosis/mapper/AiDiagnosisSuggestionMapper.java @@ -0,0 +1,9 @@ +package com.healthlink.his.aidiagnosis.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.healthlink.his.aidiagnosis.domain.AiDiagnosisSuggestion; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface AiDiagnosisSuggestionMapper extends BaseMapper { +} diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/aidiagnosis/service/IAiDiagnosisService.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/aidiagnosis/service/IAiDiagnosisService.java new file mode 100644 index 000000000..b382a6df3 --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/aidiagnosis/service/IAiDiagnosisService.java @@ -0,0 +1,13 @@ +package com.healthlink.his.aidiagnosis.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.healthlink.his.aidiagnosis.domain.AiDiagnosisSuggestion; + +import java.util.List; + +public interface IAiDiagnosisService extends IService { + + List findByPatientId(Long patientId); + + List findByEncounterId(Long encounterId); +} diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/aidiagnosis/service/impl/AiDiagnosisServiceImpl.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/aidiagnosis/service/impl/AiDiagnosisServiceImpl.java new file mode 100644 index 000000000..ded74494b --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/aidiagnosis/service/impl/AiDiagnosisServiceImpl.java @@ -0,0 +1,30 @@ +package com.healthlink.his.aidiagnosis.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.healthlink.his.aidiagnosis.domain.AiDiagnosisSuggestion; +import com.healthlink.his.aidiagnosis.mapper.AiDiagnosisSuggestionMapper; +import com.healthlink.his.aidiagnosis.service.IAiDiagnosisService; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class AiDiagnosisServiceImpl extends ServiceImpl implements IAiDiagnosisService { + + @Override + public List findByPatientId(Long patientId) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(AiDiagnosisSuggestion::getPatientId, patientId) + .orderByDesc(AiDiagnosisSuggestion::getCreateTime); + return baseMapper.selectList(wrapper); + } + + @Override + public List findByEncounterId(Long encounterId) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(AiDiagnosisSuggestion::getEncounterId, encounterId) + .orderByDesc(AiDiagnosisSuggestion::getCreateTime); + return baseMapper.selectList(wrapper); + } +} diff --git a/healthlink-his-ui/src/api/aidiagnosis/aiDiagnosis.js b/healthlink-his-ui/src/api/aidiagnosis/aiDiagnosis.js new file mode 100644 index 000000000..4cb5ec09b --- /dev/null +++ b/healthlink-his-ui/src/api/aidiagnosis/aiDiagnosis.js @@ -0,0 +1,30 @@ +import request from '@/utils/request' + +export function aiDiagnosisSuggest(data) { + return request({ + url: '/ai-diagnosis/suggest', + method: 'post', + params: data + }) +} + +export function getAiDiagnosisHistory(patientId) { + return request({ + url: '/ai-diagnosis/history/' + patientId, + method: 'get' + }) +} + +export function getAiDiagnosisHistoryByEncounter(encounterId) { + return request({ + url: '/ai-diagnosis/history/encounter/' + encounterId, + method: 'get' + }) +} + +export function acceptAiDiagnosis(id) { + return request({ + url: '/ai-diagnosis/accept/' + id, + method: 'post' + }) +} diff --git a/healthlink-his-ui/src/views/aidiagnosis/AiDiagnosisSuggest.vue b/healthlink-his-ui/src/views/aidiagnosis/AiDiagnosisSuggest.vue new file mode 100644 index 000000000..5e92d576a --- /dev/null +++ b/healthlink-his-ui/src/views/aidiagnosis/AiDiagnosisSuggest.vue @@ -0,0 +1,186 @@ + + + + + From 554c1fe97b25daceddb1001cf890759a3a75c854 Mon Sep 17 00:00:00 2001 From: chenqi Date: Fri, 19 Jun 2026 07:17:15 +0800 Subject: [PATCH 7/9] feat(data-platform): implement P2.2 data collection, BI report engine, and data dashboard - DataCollection module: clinical/operational data collection APIs - BiReport engine: generate reports (revenue/department/drg) + dashboard - DataDashboard: realtime and historical data screen with ECharts-style cards - All endpoints secured with @PreAuthorize - Frontend: BiDashboard.vue + DataDashboard.vue + API files --- .../impl/DataDashboardAppServiceImpl.java | 5 +- .../his/web/reportmanage/dto/BiReportDto.java | 17 +++ .../src/api/datacollection/index.js | 32 ++++ healthlink-his-ui/src/api/reportmanage/bi.js | 17 +++ .../views/datacollection/DataDashboard.vue | 144 ++++++++++++++++++ .../src/views/reportmanage/BiDashboard.vue | 131 ++++++++++++++++ 6 files changed, 344 insertions(+), 2 deletions(-) create mode 100644 healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/reportmanage/dto/BiReportDto.java create mode 100644 healthlink-his-ui/src/api/datacollection/index.js create mode 100644 healthlink-his-ui/src/api/reportmanage/bi.js create mode 100644 healthlink-his-ui/src/views/datacollection/DataDashboard.vue create mode 100644 healthlink-his-ui/src/views/reportmanage/BiDashboard.vue diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/datacollection/appservice/impl/DataDashboardAppServiceImpl.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/datacollection/appservice/impl/DataDashboardAppServiceImpl.java index 8bcdf11d0..d54a69182 100644 --- a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/datacollection/appservice/impl/DataDashboardAppServiceImpl.java +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/datacollection/appservice/impl/DataDashboardAppServiceImpl.java @@ -89,9 +89,10 @@ public class DataDashboardAppServiceImpl implements IDataDashboardAppService { cutoffDate = java.time.LocalDate.now().minusYears(1).toString(); } - if (cutoffDate != null) { + final String finalCutoff = cutoffDate; + if (finalCutoff != null) { allData = allData.stream() - .filter(ba -> ba.getStatDate() != null && ba.getStatDate().compareTo(cutoffDate) >= 0) + .filter(ba -> ba.getStatDate() != null && ba.getStatDate().compareTo(finalCutoff) >= 0) .collect(Collectors.toList()); } diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/reportmanage/dto/BiReportDto.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/reportmanage/dto/BiReportDto.java new file mode 100644 index 000000000..7dcb036bb --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/reportmanage/dto/BiReportDto.java @@ -0,0 +1,17 @@ +package com.healthlink.his.web.reportmanage.dto; + +import lombok.Data; +import lombok.experimental.Accessors; + +import java.util.List; +import java.util.Map; + +@Data +@Accessors(chain = true) +public class BiReportDto { + private String reportType; + private String title; + private List> records; + private Map summary; + private List> charts; +} diff --git a/healthlink-his-ui/src/api/datacollection/index.js b/healthlink-his-ui/src/api/datacollection/index.js new file mode 100644 index 000000000..8a8d3099e --- /dev/null +++ b/healthlink-his-ui/src/api/datacollection/index.js @@ -0,0 +1,32 @@ +import request from '@/utils/request' + +export function collectClinicalData(data) { + return request({ + url: '/data/collect/clinical', + method: 'post', + params: data + }) +} + +export function collectOperationalData(data) { + return request({ + url: '/data/collect/operational', + method: 'post', + params: data + }) +} + +export function getRealtimeData() { + return request({ + url: '/data/dashboard/realtime', + method: 'get' + }) +} + +export function getHistoricalData(params) { + return request({ + url: '/data/dashboard/historical', + method: 'get', + params: params + }) +} diff --git a/healthlink-his-ui/src/api/reportmanage/bi.js b/healthlink-his-ui/src/api/reportmanage/bi.js new file mode 100644 index 000000000..35fe3b024 --- /dev/null +++ b/healthlink-his-ui/src/api/reportmanage/bi.js @@ -0,0 +1,17 @@ +import request from '@/utils/request' + +export function generateBiReport(data) { + return request({ + url: '/report/bi/generate', + method: 'post', + params: { type: data.type }, + data: data.filters || {} + }) +} + +export function getBiDashboard() { + return request({ + url: '/report/bi/dashboard', + method: 'get' + }) +} diff --git a/healthlink-his-ui/src/views/datacollection/DataDashboard.vue b/healthlink-his-ui/src/views/datacollection/DataDashboard.vue new file mode 100644 index 000000000..edce28fed --- /dev/null +++ b/healthlink-his-ui/src/views/datacollection/DataDashboard.vue @@ -0,0 +1,144 @@ + + + + + diff --git a/healthlink-his-ui/src/views/reportmanage/BiDashboard.vue b/healthlink-his-ui/src/views/reportmanage/BiDashboard.vue new file mode 100644 index 000000000..aac17cefe --- /dev/null +++ b/healthlink-his-ui/src/views/reportmanage/BiDashboard.vue @@ -0,0 +1,131 @@ + + + + + From deafee06217142a4c53fa03b5f3627acfc9b25b9 Mon Sep 17 00:00:00 2001 From: chenqi Date: Fri, 19 Jun 2026 07:41:03 +0800 Subject: [PATCH 8/9] =?UTF-8?q?feat(kg):=20=E5=8C=BB=E7=96=97=E7=9F=A5?= =?UTF-8?q?=E8=AF=86=E5=9B=BE=E8=B0=B1=E5=85=A8=E6=A0=88=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../appservice/IKgEntityAppService.java | 47 +++ .../impl/KgEntityAppServiceImpl.java | 256 +++++++++++++++ .../controller/KgEntityController.java | 301 ++++++++++++++++++ .../web/knowledgegraph/dto/KgDiseaseDto.java | 27 ++ .../his/web/knowledgegraph/dto/KgDrugDto.java | 29 ++ .../knowledgegraph/dto/KgExaminationDto.java | 27 ++ .../web/knowledgegraph/dto/KgSymptomDto.java | 25 ++ .../his/knowledgegraph/domain/KgDisease.java | 32 ++ .../his/knowledgegraph/domain/KgDrug.java | 34 ++ .../knowledgegraph/domain/KgExamination.java | 32 ++ .../his/knowledgegraph/domain/KgSymptom.java | 30 ++ .../mapper/KgDiseaseMapper.java | 9 + .../knowledgegraph/mapper/KgDrugMapper.java | 9 + .../mapper/KgExaminationMapper.java | 9 + .../mapper/KgSymptomMapper.java | 9 + .../service/IKgDiseaseService.java | 7 + .../service/IKgDrugService.java | 7 + .../service/IKgExaminationService.java | 7 + .../service/IKgSymptomService.java | 7 + .../service/impl/KgDiseaseServiceImpl.java | 11 + .../service/impl/KgDrugServiceImpl.java | 11 + .../impl/KgExaminationServiceImpl.java | 11 + .../service/impl/KgSymptomServiceImpl.java | 11 + .../mapper/knowledgegraph/KgDiseaseMapper.xml | 7 + .../mapper/knowledgegraph/KgDrugMapper.xml | 7 + .../knowledgegraph/KgExaminationMapper.xml | 7 + .../mapper/knowledgegraph/KgSymptomMapper.xml | 7 + .../src/api/knowledgegraph/api.js | 105 ++++++ .../src/views/knowledgegraph/DiseaseForm.vue | 94 ++++++ .../src/views/knowledgegraph/DiseaseList.vue | 121 +++++++ .../src/views/knowledgegraph/DrugList.vue | 146 +++++++++ .../views/knowledgegraph/ExaminationList.vue | 143 +++++++++ .../views/knowledgegraph/KnowledgeGraph.vue | 178 +++++++++++ .../src/views/knowledgegraph/PathwayEdit.vue | 120 +++++++ .../src/views/knowledgegraph/PathwayList.vue | 119 +++++++ .../src/views/knowledgegraph/RelationForm.vue | 102 ++++++ .../src/views/knowledgegraph/RelationList.vue | 136 ++++++++ .../src/views/knowledgegraph/SymptomList.vue | 139 ++++++++ 38 files changed, 2379 insertions(+) create mode 100644 healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/appservice/IKgEntityAppService.java create mode 100644 healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/appservice/impl/KgEntityAppServiceImpl.java create mode 100644 healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/controller/KgEntityController.java create mode 100644 healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/dto/KgDiseaseDto.java create mode 100644 healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/dto/KgDrugDto.java create mode 100644 healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/dto/KgExaminationDto.java create mode 100644 healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/dto/KgSymptomDto.java create mode 100644 healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/domain/KgDisease.java create mode 100644 healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/domain/KgDrug.java create mode 100644 healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/domain/KgExamination.java create mode 100644 healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/domain/KgSymptom.java create mode 100644 healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/mapper/KgDiseaseMapper.java create mode 100644 healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/mapper/KgDrugMapper.java create mode 100644 healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/mapper/KgExaminationMapper.java create mode 100644 healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/mapper/KgSymptomMapper.java create mode 100644 healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/service/IKgDiseaseService.java create mode 100644 healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/service/IKgDrugService.java create mode 100644 healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/service/IKgExaminationService.java create mode 100644 healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/service/IKgSymptomService.java create mode 100644 healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/service/impl/KgDiseaseServiceImpl.java create mode 100644 healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/service/impl/KgDrugServiceImpl.java create mode 100644 healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/service/impl/KgExaminationServiceImpl.java create mode 100644 healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/service/impl/KgSymptomServiceImpl.java create mode 100644 healthlink-his-server/healthlink-his-domain/src/main/resources/mapper/knowledgegraph/KgDiseaseMapper.xml create mode 100644 healthlink-his-server/healthlink-his-domain/src/main/resources/mapper/knowledgegraph/KgDrugMapper.xml create mode 100644 healthlink-his-server/healthlink-his-domain/src/main/resources/mapper/knowledgegraph/KgExaminationMapper.xml create mode 100644 healthlink-his-server/healthlink-his-domain/src/main/resources/mapper/knowledgegraph/KgSymptomMapper.xml create mode 100644 healthlink-his-ui/src/api/knowledgegraph/api.js create mode 100644 healthlink-his-ui/src/views/knowledgegraph/DiseaseForm.vue create mode 100644 healthlink-his-ui/src/views/knowledgegraph/DiseaseList.vue create mode 100644 healthlink-his-ui/src/views/knowledgegraph/DrugList.vue create mode 100644 healthlink-his-ui/src/views/knowledgegraph/ExaminationList.vue create mode 100644 healthlink-his-ui/src/views/knowledgegraph/KnowledgeGraph.vue create mode 100644 healthlink-his-ui/src/views/knowledgegraph/PathwayEdit.vue create mode 100644 healthlink-his-ui/src/views/knowledgegraph/PathwayList.vue create mode 100644 healthlink-his-ui/src/views/knowledgegraph/RelationForm.vue create mode 100644 healthlink-his-ui/src/views/knowledgegraph/RelationList.vue create mode 100644 healthlink-his-ui/src/views/knowledgegraph/SymptomList.vue diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/appservice/IKgEntityAppService.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/appservice/IKgEntityAppService.java new file mode 100644 index 000000000..eb971e4c1 --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/appservice/IKgEntityAppService.java @@ -0,0 +1,47 @@ +package com.healthlink.his.web.knowledgegraph.appservice; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.healthlink.his.web.knowledgegraph.dto.*; + +public interface IKgEntityAppService { + + Boolean addDisease(KgDiseaseDto dto); + + Boolean updateDisease(KgDiseaseDto dto); + + Boolean deleteDisease(Long id); + + KgDiseaseDto getDiseaseById(Long id); + + IPage pageDisease(String keyword, String category, Integer pageNo, Integer pageSize); + + Boolean addSymptom(KgSymptomDto dto); + + Boolean updateSymptom(KgSymptomDto dto); + + Boolean deleteSymptom(Long id); + + KgSymptomDto getSymptomById(Long id); + + IPage pageSymptom(String keyword, String symptomType, Integer pageNo, Integer pageSize); + + Boolean addDrug(KgDrugDto dto); + + Boolean updateDrug(KgDrugDto dto); + + Boolean deleteDrug(Long id); + + KgDrugDto getDrugById(Long id); + + IPage pageDrug(String keyword, String category, Integer pageNo, Integer pageSize); + + Boolean addExamination(KgExaminationDto dto); + + Boolean updateExamination(KgExaminationDto dto); + + Boolean deleteExamination(Long id); + + KgExaminationDto getExaminationById(Long id); + + IPage pageExamination(String keyword, String examType, Integer pageNo, Integer pageSize); +} diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/appservice/impl/KgEntityAppServiceImpl.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/appservice/impl/KgEntityAppServiceImpl.java new file mode 100644 index 000000000..6213c97e5 --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/appservice/impl/KgEntityAppServiceImpl.java @@ -0,0 +1,256 @@ +package com.healthlink.his.web.knowledgegraph.appservice.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.healthlink.his.knowledgegraph.domain.*; +import com.healthlink.his.knowledgegraph.service.*; +import com.healthlink.his.web.knowledgegraph.appservice.IKgEntityAppService; +import com.healthlink.his.web.knowledgegraph.dto.*; +import org.springframework.beans.BeanUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.StringUtils; + +@Service +public class KgEntityAppServiceImpl implements IKgEntityAppService { + + private final IKgDiseaseService kgDiseaseService; + private final IKgSymptomService kgSymptomService; + private final IKgDrugService kgDrugService; + private final IKgExaminationService kgExaminationService; + + public KgEntityAppServiceImpl(IKgDiseaseService kgDiseaseService, + IKgSymptomService kgSymptomService, + IKgDrugService kgDrugService, + IKgExaminationService kgExaminationService) { + this.kgDiseaseService = kgDiseaseService; + this.kgSymptomService = kgSymptomService; + this.kgDrugService = kgDrugService; + this.kgExaminationService = kgExaminationService; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean addDisease(KgDiseaseDto dto) { + KgDisease entity = new KgDisease(); + BeanUtils.copyProperties(dto, entity); + return kgDiseaseService.save(entity); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean updateDisease(KgDiseaseDto dto) { + if (dto.getId() == null) { + return false; + } + KgDisease entity = new KgDisease(); + BeanUtils.copyProperties(dto, entity); + return kgDiseaseService.updateById(entity); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean deleteDisease(Long id) { + return kgDiseaseService.removeById(id); + } + + @Override + public KgDiseaseDto getDiseaseById(Long id) { + KgDisease entity = kgDiseaseService.getById(id); + if (entity == null) { + return null; + } + KgDiseaseDto dto = new KgDiseaseDto(); + BeanUtils.copyProperties(entity, dto); + return dto; + } + + @Override + public IPage pageDisease(String keyword, String category, Integer pageNo, Integer pageSize) { + Page page = new Page<>(pageNo, pageSize); + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + if (StringUtils.hasText(keyword)) { + wrapper.and(w -> w.like(KgDisease::getDiseaseName, keyword) + .or().like(KgDisease::getDiseaseCode, keyword)); + } + if (StringUtils.hasText(category)) { + wrapper.eq(KgDisease::getCategory, category); + } + wrapper.orderByDesc(KgDisease::getCreateTime); + Page result = kgDiseaseService.page(page, wrapper); + return result.convert(entity -> { + KgDiseaseDto dto = new KgDiseaseDto(); + BeanUtils.copyProperties(entity, dto); + return dto; + }); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean addSymptom(KgSymptomDto dto) { + KgSymptom entity = new KgSymptom(); + BeanUtils.copyProperties(dto, entity); + return kgSymptomService.save(entity); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean updateSymptom(KgSymptomDto dto) { + if (dto.getId() == null) { + return false; + } + KgSymptom entity = new KgSymptom(); + BeanUtils.copyProperties(dto, entity); + return kgSymptomService.updateById(entity); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean deleteSymptom(Long id) { + return kgSymptomService.removeById(id); + } + + @Override + public KgSymptomDto getSymptomById(Long id) { + KgSymptom entity = kgSymptomService.getById(id); + if (entity == null) { + return null; + } + KgSymptomDto dto = new KgSymptomDto(); + BeanUtils.copyProperties(entity, dto); + return dto; + } + + @Override + public IPage pageSymptom(String keyword, String symptomType, Integer pageNo, Integer pageSize) { + Page page = new Page<>(pageNo, pageSize); + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + if (StringUtils.hasText(keyword)) { + wrapper.and(w -> w.like(KgSymptom::getSymptomName, keyword) + .or().like(KgSymptom::getSymptomCode, keyword)); + } + if (StringUtils.hasText(symptomType)) { + wrapper.eq(KgSymptom::getSymptomType, symptomType); + } + wrapper.orderByDesc(KgSymptom::getCreateTime); + Page result = kgSymptomService.page(page, wrapper); + return result.convert(entity -> { + KgSymptomDto dto = new KgSymptomDto(); + BeanUtils.copyProperties(entity, dto); + return dto; + }); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean addDrug(KgDrugDto dto) { + KgDrug entity = new KgDrug(); + BeanUtils.copyProperties(dto, entity); + return kgDrugService.save(entity); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean updateDrug(KgDrugDto dto) { + if (dto.getId() == null) { + return false; + } + KgDrug entity = new KgDrug(); + BeanUtils.copyProperties(dto, entity); + return kgDrugService.updateById(entity); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean deleteDrug(Long id) { + return kgDrugService.removeById(id); + } + + @Override + public KgDrugDto getDrugById(Long id) { + KgDrug entity = kgDrugService.getById(id); + if (entity == null) { + return null; + } + KgDrugDto dto = new KgDrugDto(); + BeanUtils.copyProperties(entity, dto); + return dto; + } + + @Override + public IPage pageDrug(String keyword, String category, Integer pageNo, Integer pageSize) { + Page page = new Page<>(pageNo, pageSize); + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + if (StringUtils.hasText(keyword)) { + wrapper.and(w -> w.like(KgDrug::getDrugName, keyword) + .or().like(KgDrug::getDrugCode, keyword)); + } + if (StringUtils.hasText(category)) { + wrapper.eq(KgDrug::getCategory, category); + } + wrapper.orderByDesc(KgDrug::getCreateTime); + Page result = kgDrugService.page(page, wrapper); + return result.convert(entity -> { + KgDrugDto dto = new KgDrugDto(); + BeanUtils.copyProperties(entity, dto); + return dto; + }); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean addExamination(KgExaminationDto dto) { + KgExamination entity = new KgExamination(); + BeanUtils.copyProperties(dto, entity); + return kgExaminationService.save(entity); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean updateExamination(KgExaminationDto dto) { + if (dto.getId() == null) { + return false; + } + KgExamination entity = new KgExamination(); + BeanUtils.copyProperties(dto, entity); + return kgExaminationService.updateById(entity); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean deleteExamination(Long id) { + return kgExaminationService.removeById(id); + } + + @Override + public KgExaminationDto getExaminationById(Long id) { + KgExamination entity = kgExaminationService.getById(id); + if (entity == null) { + return null; + } + KgExaminationDto dto = new KgExaminationDto(); + BeanUtils.copyProperties(entity, dto); + return dto; + } + + @Override + public IPage pageExamination(String keyword, String examType, Integer pageNo, Integer pageSize) { + Page page = new Page<>(pageNo, pageSize); + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + if (StringUtils.hasText(keyword)) { + wrapper.and(w -> w.like(KgExamination::getExamName, keyword) + .or().like(KgExamination::getExamCode, keyword)); + } + if (StringUtils.hasText(examType)) { + wrapper.eq(KgExamination::getExamType, examType); + } + wrapper.orderByDesc(KgExamination::getCreateTime); + Page result = kgExaminationService.page(page, wrapper); + return result.convert(entity -> { + KgExaminationDto dto = new KgExaminationDto(); + BeanUtils.copyProperties(entity, dto); + return dto; + }); + } +} diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/controller/KgEntityController.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/controller/KgEntityController.java new file mode 100644 index 000000000..46e040ec8 --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/controller/KgEntityController.java @@ -0,0 +1,301 @@ +package com.healthlink.his.web.knowledgegraph.controller; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.core.common.core.domain.R; +import com.healthlink.his.web.knowledgegraph.appservice.IKgEntityAppService; +import com.healthlink.his.web.knowledgegraph.dto.*; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +@Slf4j +@Tag(name = "知识图谱-实体管理") +@RestController +@RequestMapping("/knowledgegraph") +public class KgEntityController { + + private final IKgEntityAppService kgEntityAppService; + + public KgEntityController(IKgEntityAppService kgEntityAppService) { + this.kgEntityAppService = kgEntityAppService; + } + + @Operation(summary = "创建疾病") + @PreAuthorize("@ss.hasPermi('system:knowledgegraph:edit')") + @PostMapping("/disease") + public R addDisease(@RequestBody KgDiseaseDto dto) { + try { + Boolean result = kgEntityAppService.addDisease(dto); + return result ? R.ok("创建成功") : R.fail("创建失败"); + } catch (Exception e) { + log.error("创建疾病失败", e); + return R.fail("创建疾病失败: " + e.getMessage()); + } + } + + @Operation(summary = "疾病分页查询") + @PreAuthorize("@ss.hasPermi('system:knowledgegraph:list')") + @GetMapping("/disease/page") + public R> pageDisease( + @Parameter(description = "关键词") @RequestParam(required = false) String keyword, + @Parameter(description = "分类") @RequestParam(required = false) String category, + @Parameter(description = "页码") @RequestParam(defaultValue = "1") Integer pageNo, + @Parameter(description = "每页数量") @RequestParam(defaultValue = "10") Integer pageSize) { + try { + IPage page = kgEntityAppService.pageDisease(keyword, category, pageNo, pageSize); + return R.ok(page); + } catch (Exception e) { + log.error("查询疾病列表失败", e); + return R.fail("查询疾病列表失败: " + e.getMessage()); + } + } + + @Operation(summary = "更新疾病") + @PreAuthorize("@ss.hasPermi('system:knowledgegraph:edit')") + @PutMapping("/disease") + public R updateDisease(@RequestBody KgDiseaseDto dto) { + try { + Boolean result = kgEntityAppService.updateDisease(dto); + return result ? R.ok("更新成功") : R.fail("更新失败"); + } catch (Exception e) { + log.error("更新疾病失败", e); + return R.fail("更新疾病失败: " + e.getMessage()); + } + } + + @Operation(summary = "删除疾病") + @PreAuthorize("@ss.hasPermi('system:knowledgegraph:edit')") + @DeleteMapping("/disease/{id}") + public R deleteDisease(@PathVariable Long id) { + try { + Boolean result = kgEntityAppService.deleteDisease(id); + return result ? R.ok("删除成功") : R.fail("删除失败"); + } catch (Exception e) { + log.error("删除疾病失败", e); + return R.fail("删除疾病失败: " + e.getMessage()); + } + } + + @Operation(summary = "疾病详情") + @PreAuthorize("@ss.hasPermi('system:knowledgegraph:list')") + @GetMapping("/disease/{id}") + public R getDiseaseById(@PathVariable Long id) { + try { + KgDiseaseDto dto = kgEntityAppService.getDiseaseById(id); + return dto != null ? R.ok(dto) : R.fail("未找到疾病信息"); + } catch (Exception e) { + log.error("获取疾病详情失败", e); + return R.fail("获取疾病详情失败: " + e.getMessage()); + } + } + + @Operation(summary = "创建症状") + @PreAuthorize("@ss.hasPermi('system:knowledgegraph:edit')") + @PostMapping("/symptom") + public R addSymptom(@RequestBody KgSymptomDto dto) { + try { + Boolean result = kgEntityAppService.addSymptom(dto); + return result ? R.ok("创建成功") : R.fail("创建失败"); + } catch (Exception e) { + log.error("创建症状失败", e); + return R.fail("创建症状失败: " + e.getMessage()); + } + } + + @Operation(summary = "症状分页查询") + @PreAuthorize("@ss.hasPermi('system:knowledgegraph:list')") + @GetMapping("/symptom/page") + public R> pageSymptom( + @Parameter(description = "关键词") @RequestParam(required = false) String keyword, + @Parameter(description = "症状类型") @RequestParam(required = false) String symptomType, + @Parameter(description = "页码") @RequestParam(defaultValue = "1") Integer pageNo, + @Parameter(description = "每页数量") @RequestParam(defaultValue = "10") Integer pageSize) { + try { + IPage page = kgEntityAppService.pageSymptom(keyword, symptomType, pageNo, pageSize); + return R.ok(page); + } catch (Exception e) { + log.error("查询症状列表失败", e); + return R.fail("查询症状列表失败: " + e.getMessage()); + } + } + + @Operation(summary = "更新症状") + @PreAuthorize("@ss.hasPermi('system:knowledgegraph:edit')") + @PutMapping("/symptom") + public R updateSymptom(@RequestBody KgSymptomDto dto) { + try { + Boolean result = kgEntityAppService.updateSymptom(dto); + return result ? R.ok("更新成功") : R.fail("更新失败"); + } catch (Exception e) { + log.error("更新症状失败", e); + return R.fail("更新症状失败: " + e.getMessage()); + } + } + + @Operation(summary = "删除症状") + @PreAuthorize("@ss.hasPermi('system:knowledgegraph:edit')") + @DeleteMapping("/symptom/{id}") + public R deleteSymptom(@PathVariable Long id) { + try { + Boolean result = kgEntityAppService.deleteSymptom(id); + return result ? R.ok("删除成功") : R.fail("删除失败"); + } catch (Exception e) { + log.error("删除症状失败", e); + return R.fail("删除症状失败: " + e.getMessage()); + } + } + + @Operation(summary = "症状详情") + @PreAuthorize("@ss.hasPermi('system:knowledgegraph:list')") + @GetMapping("/symptom/{id}") + public R getSymptomById(@PathVariable Long id) { + try { + KgSymptomDto dto = kgEntityAppService.getSymptomById(id); + return dto != null ? R.ok(dto) : R.fail("未找到症状信息"); + } catch (Exception e) { + log.error("获取症状详情失败", e); + return R.fail("获取症状详情失败: " + e.getMessage()); + } + } + + @Operation(summary = "创建药物") + @PreAuthorize("@ss.hasPermi('system:knowledgegraph:edit')") + @PostMapping("/drug") + public R addDrug(@RequestBody KgDrugDto dto) { + try { + Boolean result = kgEntityAppService.addDrug(dto); + return result ? R.ok("创建成功") : R.fail("创建失败"); + } catch (Exception e) { + log.error("创建药物失败", e); + return R.fail("创建药物失败: " + e.getMessage()); + } + } + + @Operation(summary = "药物分页查询") + @PreAuthorize("@ss.hasPermi('system:knowledgegraph:list')") + @GetMapping("/drug/page") + public R> pageDrug( + @Parameter(description = "关键词") @RequestParam(required = false) String keyword, + @Parameter(description = "分类") @RequestParam(required = false) String category, + @Parameter(description = "页码") @RequestParam(defaultValue = "1") Integer pageNo, + @Parameter(description = "每页数量") @RequestParam(defaultValue = "10") Integer pageSize) { + try { + IPage page = kgEntityAppService.pageDrug(keyword, category, pageNo, pageSize); + return R.ok(page); + } catch (Exception e) { + log.error("查询药物列表失败", e); + return R.fail("查询药物列表失败: " + e.getMessage()); + } + } + + @Operation(summary = "更新药物") + @PreAuthorize("@ss.hasPermi('system:knowledgegraph:edit')") + @PutMapping("/drug") + public R updateDrug(@RequestBody KgDrugDto dto) { + try { + Boolean result = kgEntityAppService.updateDrug(dto); + return result ? R.ok("更新成功") : R.fail("更新失败"); + } catch (Exception e) { + log.error("更新药物失败", e); + return R.fail("更新药物失败: " + e.getMessage()); + } + } + + @Operation(summary = "删除药物") + @PreAuthorize("@ss.hasPermi('system:knowledgegraph:edit')") + @DeleteMapping("/drug/{id}") + public R deleteDrug(@PathVariable Long id) { + try { + Boolean result = kgEntityAppService.deleteDrug(id); + return result ? R.ok("删除成功") : R.fail("删除失败"); + } catch (Exception e) { + log.error("删除药物失败", e); + return R.fail("删除药物失败: " + e.getMessage()); + } + } + + @Operation(summary = "药物详情") + @PreAuthorize("@ss.hasPermi('system:knowledgegraph:list')") + @GetMapping("/drug/{id}") + public R getDrugById(@PathVariable Long id) { + try { + KgDrugDto dto = kgEntityAppService.getDrugById(id); + return dto != null ? R.ok(dto) : R.fail("未找到药物信息"); + } catch (Exception e) { + log.error("获取药物详情失败", e); + return R.fail("获取药物详情失败: " + e.getMessage()); + } + } + + @Operation(summary = "创建检查") + @PreAuthorize("@ss.hasPermi('system:knowledgegraph:edit')") + @PostMapping("/examination") + public R addExamination(@RequestBody KgExaminationDto dto) { + try { + Boolean result = kgEntityAppService.addExamination(dto); + return result ? R.ok("创建成功") : R.fail("创建失败"); + } catch (Exception e) { + log.error("创建检查失败", e); + return R.fail("创建检查失败: " + e.getMessage()); + } + } + + @Operation(summary = "检查分页查询") + @PreAuthorize("@ss.hasPermi('system:knowledgegraph:list')") + @GetMapping("/examination/page") + public R> pageExamination( + @Parameter(description = "关键词") @RequestParam(required = false) String keyword, + @Parameter(description = "检查类型") @RequestParam(required = false) String examType, + @Parameter(description = "页码") @RequestParam(defaultValue = "1") Integer pageNo, + @Parameter(description = "每页数量") @RequestParam(defaultValue = "10") Integer pageSize) { + try { + IPage page = kgEntityAppService.pageExamination(keyword, examType, pageNo, pageSize); + return R.ok(page); + } catch (Exception e) { + log.error("查询检查列表失败", e); + return R.fail("查询检查列表失败: " + e.getMessage()); + } + } + + @Operation(summary = "更新检查") + @PreAuthorize("@ss.hasPermi('system:knowledgegraph:edit')") + @PutMapping("/examination") + public R updateExamination(@RequestBody KgExaminationDto dto) { + try { + Boolean result = kgEntityAppService.updateExamination(dto); + return result ? R.ok("更新成功") : R.fail("更新失败"); + } catch (Exception e) { + log.error("更新检查失败", e); + return R.fail("更新检查失败: " + e.getMessage()); + } + } + + @Operation(summary = "删除检查") + @PreAuthorize("@ss.hasPermi('system:knowledgegraph:edit')") + @DeleteMapping("/examination/{id}") + public R deleteExamination(@PathVariable Long id) { + try { + Boolean result = kgEntityAppService.deleteExamination(id); + return result ? R.ok("删除成功") : R.fail("删除失败"); + } catch (Exception e) { + log.error("删除检查失败", e); + return R.fail("删除检查失败: " + e.getMessage()); + } + } + + @Operation(summary = "检查详情") + @PreAuthorize("@ss.hasPermi('system:knowledgegraph:list')") + @GetMapping("/examination/{id}") + public R getExaminationById(@PathVariable Long id) { + try { + KgExaminationDto dto = kgEntityAppService.getExaminationById(id); + return dto != null ? R.ok(dto) : R.fail("未找到检查信息"); + } catch (Exception e) { + log.error("获取检查详情失败", e); + return R.fail("获取检查详情失败: " + e.getMessage()); + } + } +} diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/dto/KgDiseaseDto.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/dto/KgDiseaseDto.java new file mode 100644 index 000000000..86c728731 --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/dto/KgDiseaseDto.java @@ -0,0 +1,27 @@ +package com.healthlink.his.web.knowledgegraph.dto; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import lombok.Data; + +import java.io.Serializable; + +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class KgDiseaseDto implements Serializable { + private static final long serialVersionUID = 1L; + + @JsonSerialize(using = ToStringSerializer.class) + private Long id; + + private String diseaseCode; + + private String diseaseName; + + private String category; + + private String department; + + private String severityLevel; +} diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/dto/KgDrugDto.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/dto/KgDrugDto.java new file mode 100644 index 000000000..dc2de755d --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/dto/KgDrugDto.java @@ -0,0 +1,29 @@ +package com.healthlink.his.web.knowledgegraph.dto; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import lombok.Data; + +import java.io.Serializable; + +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class KgDrugDto implements Serializable { + private static final long serialVersionUID = 1L; + + @JsonSerialize(using = ToStringSerializer.class) + private Long id; + + private String drugCode; + + private String drugName; + + private String genericName; + + private String category; + + private String dosageForm; + + private String contraindications; +} diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/dto/KgExaminationDto.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/dto/KgExaminationDto.java new file mode 100644 index 000000000..c79663d5d --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/dto/KgExaminationDto.java @@ -0,0 +1,27 @@ +package com.healthlink.his.web.knowledgegraph.dto; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import lombok.Data; + +import java.io.Serializable; + +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class KgExaminationDto implements Serializable { + private static final long serialVersionUID = 1L; + + @JsonSerialize(using = ToStringSerializer.class) + private Long id; + + private String examCode; + + private String examName; + + private String examType; + + private String department; + + private String referenceRange; +} diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/dto/KgSymptomDto.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/dto/KgSymptomDto.java new file mode 100644 index 000000000..3cbbf5440 --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/dto/KgSymptomDto.java @@ -0,0 +1,25 @@ +package com.healthlink.his.web.knowledgegraph.dto; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import lombok.Data; + +import java.io.Serializable; + +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class KgSymptomDto implements Serializable { + private static final long serialVersionUID = 1L; + + @JsonSerialize(using = ToStringSerializer.class) + private Long id; + + private String symptomCode; + + private String symptomName; + + private String bodyPart; + + private String symptomType; +} diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/domain/KgDisease.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/domain/KgDisease.java new file mode 100644 index 000000000..118de459f --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/domain/KgDisease.java @@ -0,0 +1,32 @@ +package com.healthlink.his.knowledgegraph.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.core.common.core.domain.HisBaseEntity; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +@Data +@EqualsAndHashCode(callSuper = false) +@Accessors(chain = true) +@TableName("kg_disease") +public class KgDisease extends HisBaseEntity { + + @TableId(type = IdType.ASSIGN_ID) + @JsonSerialize(using = ToStringSerializer.class) + private Long id; + + private String diseaseCode; + + private String diseaseName; + + private String category; + + private String department; + + private String severityLevel; +} diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/domain/KgDrug.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/domain/KgDrug.java new file mode 100644 index 000000000..c1a7ae21a --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/domain/KgDrug.java @@ -0,0 +1,34 @@ +package com.healthlink.his.knowledgegraph.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.core.common.core.domain.HisBaseEntity; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +@Data +@EqualsAndHashCode(callSuper = false) +@Accessors(chain = true) +@TableName("kg_drug") +public class KgDrug extends HisBaseEntity { + + @TableId(type = IdType.ASSIGN_ID) + @JsonSerialize(using = ToStringSerializer.class) + private Long id; + + private String drugCode; + + private String drugName; + + private String genericName; + + private String category; + + private String dosageForm; + + private String contraindications; +} diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/domain/KgExamination.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/domain/KgExamination.java new file mode 100644 index 000000000..8aba928a9 --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/domain/KgExamination.java @@ -0,0 +1,32 @@ +package com.healthlink.his.knowledgegraph.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.core.common.core.domain.HisBaseEntity; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +@Data +@EqualsAndHashCode(callSuper = false) +@Accessors(chain = true) +@TableName("kg_examination") +public class KgExamination extends HisBaseEntity { + + @TableId(type = IdType.ASSIGN_ID) + @JsonSerialize(using = ToStringSerializer.class) + private Long id; + + private String examCode; + + private String examName; + + private String examType; + + private String department; + + private String referenceRange; +} diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/domain/KgSymptom.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/domain/KgSymptom.java new file mode 100644 index 000000000..24bc07bf3 --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/domain/KgSymptom.java @@ -0,0 +1,30 @@ +package com.healthlink.his.knowledgegraph.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.core.common.core.domain.HisBaseEntity; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +@Data +@EqualsAndHashCode(callSuper = false) +@Accessors(chain = true) +@TableName("kg_symptom") +public class KgSymptom extends HisBaseEntity { + + @TableId(type = IdType.ASSIGN_ID) + @JsonSerialize(using = ToStringSerializer.class) + private Long id; + + private String symptomCode; + + private String symptomName; + + private String bodyPart; + + private String symptomType; +} diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/mapper/KgDiseaseMapper.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/mapper/KgDiseaseMapper.java new file mode 100644 index 000000000..66015d9cb --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/mapper/KgDiseaseMapper.java @@ -0,0 +1,9 @@ +package com.healthlink.his.knowledgegraph.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.healthlink.his.knowledgegraph.domain.KgDisease; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface KgDiseaseMapper extends BaseMapper { +} diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/mapper/KgDrugMapper.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/mapper/KgDrugMapper.java new file mode 100644 index 000000000..0952e6a7a --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/mapper/KgDrugMapper.java @@ -0,0 +1,9 @@ +package com.healthlink.his.knowledgegraph.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.healthlink.his.knowledgegraph.domain.KgDrug; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface KgDrugMapper extends BaseMapper { +} diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/mapper/KgExaminationMapper.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/mapper/KgExaminationMapper.java new file mode 100644 index 000000000..7e209ed34 --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/mapper/KgExaminationMapper.java @@ -0,0 +1,9 @@ +package com.healthlink.his.knowledgegraph.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.healthlink.his.knowledgegraph.domain.KgExamination; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface KgExaminationMapper extends BaseMapper { +} diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/mapper/KgSymptomMapper.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/mapper/KgSymptomMapper.java new file mode 100644 index 000000000..869d6bbb0 --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/mapper/KgSymptomMapper.java @@ -0,0 +1,9 @@ +package com.healthlink.his.knowledgegraph.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.healthlink.his.knowledgegraph.domain.KgSymptom; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface KgSymptomMapper extends BaseMapper { +} diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/service/IKgDiseaseService.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/service/IKgDiseaseService.java new file mode 100644 index 000000000..ea8cb55cd --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/service/IKgDiseaseService.java @@ -0,0 +1,7 @@ +package com.healthlink.his.knowledgegraph.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.healthlink.his.knowledgegraph.domain.KgDisease; + +public interface IKgDiseaseService extends IService { +} diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/service/IKgDrugService.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/service/IKgDrugService.java new file mode 100644 index 000000000..60753bc34 --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/service/IKgDrugService.java @@ -0,0 +1,7 @@ +package com.healthlink.his.knowledgegraph.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.healthlink.his.knowledgegraph.domain.KgDrug; + +public interface IKgDrugService extends IService { +} diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/service/IKgExaminationService.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/service/IKgExaminationService.java new file mode 100644 index 000000000..96fc9c490 --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/service/IKgExaminationService.java @@ -0,0 +1,7 @@ +package com.healthlink.his.knowledgegraph.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.healthlink.his.knowledgegraph.domain.KgExamination; + +public interface IKgExaminationService extends IService { +} diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/service/IKgSymptomService.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/service/IKgSymptomService.java new file mode 100644 index 000000000..4ecb02441 --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/service/IKgSymptomService.java @@ -0,0 +1,7 @@ +package com.healthlink.his.knowledgegraph.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.healthlink.his.knowledgegraph.domain.KgSymptom; + +public interface IKgSymptomService extends IService { +} diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/service/impl/KgDiseaseServiceImpl.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/service/impl/KgDiseaseServiceImpl.java new file mode 100644 index 000000000..55da16944 --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/service/impl/KgDiseaseServiceImpl.java @@ -0,0 +1,11 @@ +package com.healthlink.his.knowledgegraph.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.healthlink.his.knowledgegraph.domain.KgDisease; +import com.healthlink.his.knowledgegraph.mapper.KgDiseaseMapper; +import com.healthlink.his.knowledgegraph.service.IKgDiseaseService; +import org.springframework.stereotype.Service; + +@Service +public class KgDiseaseServiceImpl extends ServiceImpl implements IKgDiseaseService { +} diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/service/impl/KgDrugServiceImpl.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/service/impl/KgDrugServiceImpl.java new file mode 100644 index 000000000..41dcfa917 --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/service/impl/KgDrugServiceImpl.java @@ -0,0 +1,11 @@ +package com.healthlink.his.knowledgegraph.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.healthlink.his.knowledgegraph.domain.KgDrug; +import com.healthlink.his.knowledgegraph.mapper.KgDrugMapper; +import com.healthlink.his.knowledgegraph.service.IKgDrugService; +import org.springframework.stereotype.Service; + +@Service +public class KgDrugServiceImpl extends ServiceImpl implements IKgDrugService { +} diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/service/impl/KgExaminationServiceImpl.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/service/impl/KgExaminationServiceImpl.java new file mode 100644 index 000000000..a180b3b0f --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/service/impl/KgExaminationServiceImpl.java @@ -0,0 +1,11 @@ +package com.healthlink.his.knowledgegraph.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.healthlink.his.knowledgegraph.domain.KgExamination; +import com.healthlink.his.knowledgegraph.mapper.KgExaminationMapper; +import com.healthlink.his.knowledgegraph.service.IKgExaminationService; +import org.springframework.stereotype.Service; + +@Service +public class KgExaminationServiceImpl extends ServiceImpl implements IKgExaminationService { +} diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/service/impl/KgSymptomServiceImpl.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/service/impl/KgSymptomServiceImpl.java new file mode 100644 index 000000000..2b43d4ace --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/service/impl/KgSymptomServiceImpl.java @@ -0,0 +1,11 @@ +package com.healthlink.his.knowledgegraph.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.healthlink.his.knowledgegraph.domain.KgSymptom; +import com.healthlink.his.knowledgegraph.mapper.KgSymptomMapper; +import com.healthlink.his.knowledgegraph.service.IKgSymptomService; +import org.springframework.stereotype.Service; + +@Service +public class KgSymptomServiceImpl extends ServiceImpl implements IKgSymptomService { +} diff --git a/healthlink-his-server/healthlink-his-domain/src/main/resources/mapper/knowledgegraph/KgDiseaseMapper.xml b/healthlink-his-server/healthlink-his-domain/src/main/resources/mapper/knowledgegraph/KgDiseaseMapper.xml new file mode 100644 index 000000000..e22c95609 --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/resources/mapper/knowledgegraph/KgDiseaseMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/healthlink-his-server/healthlink-his-domain/src/main/resources/mapper/knowledgegraph/KgDrugMapper.xml b/healthlink-his-server/healthlink-his-domain/src/main/resources/mapper/knowledgegraph/KgDrugMapper.xml new file mode 100644 index 000000000..621c2438f --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/resources/mapper/knowledgegraph/KgDrugMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/healthlink-his-server/healthlink-his-domain/src/main/resources/mapper/knowledgegraph/KgExaminationMapper.xml b/healthlink-his-server/healthlink-his-domain/src/main/resources/mapper/knowledgegraph/KgExaminationMapper.xml new file mode 100644 index 000000000..8f892b05b --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/resources/mapper/knowledgegraph/KgExaminationMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/healthlink-his-server/healthlink-his-domain/src/main/resources/mapper/knowledgegraph/KgSymptomMapper.xml b/healthlink-his-server/healthlink-his-domain/src/main/resources/mapper/knowledgegraph/KgSymptomMapper.xml new file mode 100644 index 000000000..0513b3064 --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/resources/mapper/knowledgegraph/KgSymptomMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/healthlink-his-ui/src/api/knowledgegraph/api.js b/healthlink-his-ui/src/api/knowledgegraph/api.js new file mode 100644 index 000000000..1c8da3dbc --- /dev/null +++ b/healthlink-his-ui/src/api/knowledgegraph/api.js @@ -0,0 +1,105 @@ +import request from '@/utils/request' + +export function addDisease(data) { + return request({ url: '/knowledgegraph/disease', method: 'post', data }) +} + +export function getDiseasePage(params) { + return request({ url: '/knowledgegraph/disease/page', method: 'get', params }) +} + +export function getDiseaseById(id) { + return request({ url: '/knowledgegraph/disease/' + id, method: 'get' }) +} + +export function updateDisease(data) { + return request({ url: '/knowledgegraph/disease', method: 'put', data }) +} + +export function deleteDisease(id) { + return request({ url: '/knowledgegraph/disease/' + id, method: 'delete' }) +} + +export function addSymptom(data) { + return request({ url: '/knowledgegraph/symptom', method: 'post', data }) +} + +export function getSymptomPage(params) { + return request({ url: '/knowledgegraph/symptom/page', method: 'get', params }) +} + +export function getSymptomById(id) { + return request({ url: '/knowledgegraph/symptom/' + id, method: 'get' }) +} + +export function updateSymptom(data) { + return request({ url: '/knowledgegraph/symptom', method: 'put', data }) +} + +export function deleteSymptom(id) { + return request({ url: '/knowledgegraph/symptom/' + id, method: 'delete' }) +} + +export function addDrug(data) { + return request({ url: '/knowledgegraph/drug', method: 'post', data }) +} + +export function getDrugPage(params) { + return request({ url: '/knowledgegraph/drug/page', method: 'get', params }) +} + +export function getDrugById(id) { + return request({ url: '/knowledgegraph/drug/' + id, method: 'get' }) +} + +export function updateDrug(data) { + return request({ url: '/knowledgegraph/drug', method: 'put', data }) +} + +export function deleteDrug(id) { + return request({ url: '/knowledgegraph/drug/' + id, method: 'delete' }) +} + +export function addExamination(data) { + return request({ url: '/knowledgegraph/examination', method: 'post', data }) +} + +export function getExaminationPage(params) { + return request({ url: '/knowledgegraph/examination/page', method: 'get', params }) +} + +export function getExaminationById(id) { + return request({ url: '/knowledgegraph/examination/' + id, method: 'get' }) +} + +export function updateExamination(data) { + return request({ url: '/knowledgegraph/examination', method: 'put', data }) +} + +export function deleteExamination(id) { + return request({ url: '/knowledgegraph/examination/' + id, method: 'delete' }) +} + +export function createRelation(data) { + return request({ url: '/knowledgegraph/relation', method: 'post', data }) +} + +export function getRelationPage(params) { + return request({ url: '/knowledgegraph/relation/page', method: 'get', params }) +} + +export function getRelationGraph(entityType, entityId) { + return request({ url: `/knowledgegraph/relation/graph/${entityType}/${entityId}`, method: 'get' }) +} + +export function createPathway(data) { + return request({ url: '/knowledgegraph/pathway', method: 'post', data }) +} + +export function getPathwayPage(params) { + return request({ url: '/knowledgegraph/pathway/page', method: 'get', params }) +} + +export function getPathwaySteps(id) { + return request({ url: `/knowledgegraph/pathway/${id}/steps`, method: 'get' }) +} diff --git a/healthlink-his-ui/src/views/knowledgegraph/DiseaseForm.vue b/healthlink-his-ui/src/views/knowledgegraph/DiseaseForm.vue new file mode 100644 index 000000000..7f116e76c --- /dev/null +++ b/healthlink-his-ui/src/views/knowledgegraph/DiseaseForm.vue @@ -0,0 +1,94 @@ + + + diff --git a/healthlink-his-ui/src/views/knowledgegraph/DiseaseList.vue b/healthlink-his-ui/src/views/knowledgegraph/DiseaseList.vue new file mode 100644 index 000000000..35568693b --- /dev/null +++ b/healthlink-his-ui/src/views/knowledgegraph/DiseaseList.vue @@ -0,0 +1,121 @@ + + + diff --git a/healthlink-his-ui/src/views/knowledgegraph/DrugList.vue b/healthlink-his-ui/src/views/knowledgegraph/DrugList.vue new file mode 100644 index 000000000..16b233aa7 --- /dev/null +++ b/healthlink-his-ui/src/views/knowledgegraph/DrugList.vue @@ -0,0 +1,146 @@ + + + diff --git a/healthlink-his-ui/src/views/knowledgegraph/ExaminationList.vue b/healthlink-his-ui/src/views/knowledgegraph/ExaminationList.vue new file mode 100644 index 000000000..1b1bdee86 --- /dev/null +++ b/healthlink-his-ui/src/views/knowledgegraph/ExaminationList.vue @@ -0,0 +1,143 @@ + + + diff --git a/healthlink-his-ui/src/views/knowledgegraph/KnowledgeGraph.vue b/healthlink-his-ui/src/views/knowledgegraph/KnowledgeGraph.vue new file mode 100644 index 000000000..2af04cab1 --- /dev/null +++ b/healthlink-his-ui/src/views/knowledgegraph/KnowledgeGraph.vue @@ -0,0 +1,178 @@ + + + diff --git a/healthlink-his-ui/src/views/knowledgegraph/PathwayEdit.vue b/healthlink-his-ui/src/views/knowledgegraph/PathwayEdit.vue new file mode 100644 index 000000000..b28673f6f --- /dev/null +++ b/healthlink-his-ui/src/views/knowledgegraph/PathwayEdit.vue @@ -0,0 +1,120 @@ + + + diff --git a/healthlink-his-ui/src/views/knowledgegraph/PathwayList.vue b/healthlink-his-ui/src/views/knowledgegraph/PathwayList.vue new file mode 100644 index 000000000..7b528fbe5 --- /dev/null +++ b/healthlink-his-ui/src/views/knowledgegraph/PathwayList.vue @@ -0,0 +1,119 @@ + + + diff --git a/healthlink-his-ui/src/views/knowledgegraph/RelationForm.vue b/healthlink-his-ui/src/views/knowledgegraph/RelationForm.vue new file mode 100644 index 000000000..9586dc07a --- /dev/null +++ b/healthlink-his-ui/src/views/knowledgegraph/RelationForm.vue @@ -0,0 +1,102 @@ + + + diff --git a/healthlink-his-ui/src/views/knowledgegraph/RelationList.vue b/healthlink-his-ui/src/views/knowledgegraph/RelationList.vue new file mode 100644 index 000000000..576983dbd --- /dev/null +++ b/healthlink-his-ui/src/views/knowledgegraph/RelationList.vue @@ -0,0 +1,136 @@ + + + diff --git a/healthlink-his-ui/src/views/knowledgegraph/SymptomList.vue b/healthlink-his-ui/src/views/knowledgegraph/SymptomList.vue new file mode 100644 index 000000000..5b27ca94c --- /dev/null +++ b/healthlink-his-ui/src/views/knowledgegraph/SymptomList.vue @@ -0,0 +1,139 @@ + + + From be90a13cd6a82d32991397fe897891ba11028690 Mon Sep 17 00:00:00 2001 From: chenqi Date: Fri, 19 Jun 2026 08:06:38 +0800 Subject: [PATCH 9/9] =?UTF-8?q?feat(kg):=20=E5=85=B3=E7=B3=BB=E7=AE=A1?= =?UTF-8?q?=E7=90=86+=E7=9F=A5=E8=AF=86=E5=9B=BE=E8=B0=B1=E5=8F=AF?= =?UTF-8?q?=E8=A7=86=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../appservice/IKgRelationAppService.java | 18 +++ .../impl/KgRelationAppServiceImpl.java | 146 ++++++++++++++++++ .../controller/KgRelationController.java | 79 ++++++++++ .../his/web/clinical/dto/KgPathwayDto.java | 13 ++ .../his/common/enums/KgEntityType.java | 28 ++++ .../his/common/enums/KgRelationType.java | 32 ++++ .../clinical/domain/KgClinicalPathway.java | 22 +++ .../his/clinical/domain/KgEntityRelation.java | 24 +++ .../his/clinical/domain/KgPathwayStep.java | 20 +++ .../mapper/KgClinicalPathwayMapper.java | 9 ++ .../mapper/KgEntityRelationMapper.java | 9 ++ .../clinical/mapper/KgPathwayStepMapper.java | 9 ++ .../service/IKgClinicalPathwayService.java | 7 + .../service/IKgEntityRelationService.java | 7 + .../service/IKgPathwayStepService.java | 7 + .../impl/KgClinicalPathwayServiceImpl.java | 11 ++ .../impl/KgEntityRelationServiceImpl.java | 11 ++ .../impl/KgPathwayStepServiceImpl.java | 11 ++ .../clinical/KgClinicalPathwayMapper.xml | 6 + .../clinical/KgEntityRelationMapper.xml | 6 + .../mapper/clinical/KgPathwayStepMapper.xml | 6 + .../src/views/knowledgegraph/PathwayEdit.vue | 22 ++- .../src/views/knowledgegraph/RelationForm.vue | 15 +- 23 files changed, 508 insertions(+), 10 deletions(-) create mode 100644 healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/clinical/appservice/IKgRelationAppService.java create mode 100644 healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/clinical/appservice/impl/KgRelationAppServiceImpl.java create mode 100644 healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/clinical/controller/KgRelationController.java create mode 100644 healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/clinical/dto/KgPathwayDto.java create mode 100644 healthlink-his-server/healthlink-his-common/src/main/java/com/healthlink/his/common/enums/KgEntityType.java create mode 100644 healthlink-his-server/healthlink-his-common/src/main/java/com/healthlink/his/common/enums/KgRelationType.java create mode 100644 healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/clinical/domain/KgClinicalPathway.java create mode 100644 healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/clinical/domain/KgEntityRelation.java create mode 100644 healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/clinical/domain/KgPathwayStep.java create mode 100644 healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/clinical/mapper/KgClinicalPathwayMapper.java create mode 100644 healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/clinical/mapper/KgEntityRelationMapper.java create mode 100644 healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/clinical/mapper/KgPathwayStepMapper.java create mode 100644 healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/clinical/service/IKgClinicalPathwayService.java create mode 100644 healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/clinical/service/IKgEntityRelationService.java create mode 100644 healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/clinical/service/IKgPathwayStepService.java create mode 100644 healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/clinical/service/impl/KgClinicalPathwayServiceImpl.java create mode 100644 healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/clinical/service/impl/KgEntityRelationServiceImpl.java create mode 100644 healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/clinical/service/impl/KgPathwayStepServiceImpl.java create mode 100644 healthlink-his-server/healthlink-his-domain/src/main/resources/mapper/clinical/KgClinicalPathwayMapper.xml create mode 100644 healthlink-his-server/healthlink-his-domain/src/main/resources/mapper/clinical/KgEntityRelationMapper.xml create mode 100644 healthlink-his-server/healthlink-his-domain/src/main/resources/mapper/clinical/KgPathwayStepMapper.xml diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/clinical/appservice/IKgRelationAppService.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/clinical/appservice/IKgRelationAppService.java new file mode 100644 index 000000000..4b8154225 --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/clinical/appservice/IKgRelationAppService.java @@ -0,0 +1,18 @@ +package com.healthlink.his.web.clinical.appservice; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.healthlink.his.clinical.domain.KgClinicalPathway; +import com.healthlink.his.clinical.domain.KgEntityRelation; +import com.healthlink.his.clinical.domain.KgPathwayStep; + +import java.util.List; +import java.util.Map; + +public interface IKgRelationAppService { + void createRelation(KgEntityRelation relation); + IPage pageRelations(String sourceType, String targetType, String relationType, Integer pageNo, Integer pageSize); + Map getRelationGraph(String entityType, String entityId); + void createPathway(KgClinicalPathway pathway, List steps); + IPage pagePathways(String keyword, Integer pageNo, Integer pageSize); + List getPathwaySteps(Long pathwayId); +} diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/clinical/appservice/impl/KgRelationAppServiceImpl.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/clinical/appservice/impl/KgRelationAppServiceImpl.java new file mode 100644 index 000000000..4eac09a18 --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/clinical/appservice/impl/KgRelationAppServiceImpl.java @@ -0,0 +1,146 @@ +package com.healthlink.his.web.clinical.appservice.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +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.web.clinical.appservice.IKgRelationAppService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.StringUtils; + +import java.math.BigDecimal; +import java.util.*; +import java.util.stream.Collectors; + +@Service +public class KgRelationAppServiceImpl implements IKgRelationAppService { + + @Autowired + private IKgEntityRelationService relationService; + + @Autowired + private IKgClinicalPathwayService pathwayService; + + @Autowired + private IKgPathwayStepService stepService; + + @Override + @Transactional(rollbackFor = Exception.class) + public void createRelation(KgEntityRelation relation) { + if (relation.getRelationStrength() == null) { + relation.setRelationStrength(BigDecimal.ONE); + } + relation.setCreateTime(new Date()); + relationService.save(relation); + } + + @Override + public IPage pageRelations(String sourceType, String targetType, String relationType, Integer pageNo, Integer pageSize) { + LambdaQueryWrapper w = new LambdaQueryWrapper<>(); + w.eq(StringUtils.hasText(sourceType), KgEntityRelation::getSourceType, sourceType) + .eq(StringUtils.hasText(targetType), KgEntityRelation::getTargetType, targetType) + .eq(StringUtils.hasText(relationType), KgEntityRelation::getRelationType, relationType) + .orderByDesc(KgEntityRelation::getCreateTime); + return relationService.page(new Page<>(pageNo, pageSize), w); + } + + @Override + public Map getRelationGraph(String entityType, String entityId) { + Set nodeKeys = new LinkedHashSet<>(); + List> nodes = new ArrayList<>(); + List> edges = new ArrayList<>(); + + // Find all relations where this entity is source or target + LambdaQueryWrapper w1 = new LambdaQueryWrapper<>(); + w1.eq(KgEntityRelation::getSourceType, entityType) + .eq(KgEntityRelation::getSourceId, entityId); + List outgoing = relationService.list(w1); + + LambdaQueryWrapper w2 = new LambdaQueryWrapper<>(); + w2.eq(KgEntityRelation::getTargetType, entityType) + .eq(KgEntityRelation::getTargetId, entityId); + List incoming = relationService.list(w2); + + List all = new ArrayList<>(); + all.addAll(outgoing); + all.addAll(incoming); + + for (KgEntityRelation rel : all) { + String srcKey = rel.getSourceType() + ":" + rel.getSourceId(); + String tgtKey = rel.getTargetType() + ":" + rel.getTargetId(); + + if (nodeKeys.add(srcKey)) { + nodes.add(buildNode(srcKey, rel.getSourceType(), rel.getSourceId())); + } + if (nodeKeys.add(tgtKey)) { + nodes.add(buildNode(tgtKey, rel.getTargetType(), rel.getTargetId())); + } + + Map edge = new LinkedHashMap<>(); + edge.put("source", srcKey); + edge.put("target", tgtKey); + edge.put("relationType", rel.getRelationType()); + edge.put("relationStrength", rel.getRelationStrength()); + edge.put("description", rel.getDescription()); + edges.add(edge); + } + + Map result = new LinkedHashMap<>(); + result.put("nodes", nodes); + result.put("edges", edges); + return result; + } + + private Map buildNode(String key, String type, String id) { + Map node = new LinkedHashMap<>(); + node.put("id", key); + node.put("entityType", type); + node.put("entityId", id); + return node; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void createPathway(KgClinicalPathway pathway, List steps) { + pathway.setStatus("ACTIVE"); + pathway.setCreateTime(new Date()); + pathwayService.save(pathway); + + if (steps != null) { + for (int i = 0; i < steps.size(); i++) { + KgPathwayStep step = steps.get(i); + step.setPathwayId(pathway.getId()); + step.setStepOrder(i + 1); + step.setCreateTime(new Date()); + } + stepService.saveBatch(steps); + } + } + + @Override + public IPage pagePathways(String keyword, Integer pageNo, Integer pageSize) { + LambdaQueryWrapper w = new LambdaQueryWrapper<>(); + w.eq(KgClinicalPathway::getStatus, "ACTIVE") + .and(StringUtils.hasText(keyword), q -> q + .like(KgClinicalPathway::getPathwayName, keyword) + .or().like(KgClinicalPathway::getDiseaseName, keyword) + .or().like(KgClinicalPathway::getPathwayCode, keyword)) + .orderByDesc(KgClinicalPathway::getCreateTime); + return pathwayService.page(new Page<>(pageNo, pageSize), w); + } + + @Override + public List getPathwaySteps(Long pathwayId) { + LambdaQueryWrapper w = new LambdaQueryWrapper<>(); + w.eq(KgPathwayStep::getPathwayId, pathwayId) + .orderByAsc(KgPathwayStep::getStepOrder); + return stepService.list(w); + } +} diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/clinical/controller/KgRelationController.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/clinical/controller/KgRelationController.java new file mode 100644 index 000000000..843fbc300 --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/clinical/controller/KgRelationController.java @@ -0,0 +1,79 @@ +package com.healthlink.his.web.clinical.controller; + +import com.core.common.core.domain.AjaxResult; +import com.core.common.core.domain.R; +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.web.clinical.appservice.IKgRelationAppService; +import com.healthlink.his.web.clinical.dto.KgPathwayDto; +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; + +@Tag(name = "知识图谱管理") +@RestController +@RequestMapping("/knowledgegraph") +@Slf4j +@AllArgsConstructor +public class KgRelationController { + + private final IKgRelationAppService kgRelationAppService; + + @Operation(summary = "创建关系") + @PreAuthorize("@ss.hasPermi('system:knowledgegraph:edit')") + @PostMapping("/relation") + public AjaxResult createRelation(@RequestBody KgEntityRelation relation) { + kgRelationAppService.createRelation(relation); + return AjaxResult.success(); + } + + @Operation(summary = "关系分页查询") + @PreAuthorize("@ss.hasPermi('system:knowledgegraph:list')") + @GetMapping("/relation/page") + public R pageRelations( + @RequestParam(value = "sourceType", required = false) String sourceType, + @RequestParam(value = "targetType", required = false) String targetType, + @RequestParam(value = "relationType", required = false) String relationType, + @RequestParam(value = "pageNo", defaultValue = "1") Integer pageNo, + @RequestParam(value = "pageSize", defaultValue = "20") Integer pageSize) { + return R.ok(kgRelationAppService.pageRelations(sourceType, targetType, relationType, pageNo, pageSize)); + } + + @Operation(summary = "查询实体关系图") + @PreAuthorize("@ss.hasPermi('system:knowledgegraph:list')") + @GetMapping("/relation/graph/{entityType}/{entityId}") + public R getRelationGraph(@PathVariable String entityType, @PathVariable String entityId) { + return R.ok(kgRelationAppService.getRelationGraph(entityType, entityId)); + } + + @Operation(summary = "创建临床路径") + @PreAuthorize("@ss.hasPermi('system:knowledgegraph:edit')") + @PostMapping("/pathway") + public AjaxResult createPathway(@RequestBody KgPathwayDto dto) { + kgRelationAppService.createPathway(dto.getPathway(), dto.getSteps()); + return AjaxResult.success(); + } + + @Operation(summary = "路径分页查询") + @PreAuthorize("@ss.hasPermi('system:knowledgegraph:list')") + @GetMapping("/pathway/page") + public R pagePathways( + @RequestParam(value = "keyword", required = false) String keyword, + @RequestParam(value = "pageNo", defaultValue = "1") Integer pageNo, + @RequestParam(value = "pageSize", defaultValue = "20") Integer pageSize) { + return R.ok(kgRelationAppService.pagePathways(keyword, pageNo, pageSize)); + } + + @Operation(summary = "查询路径步骤") + @PreAuthorize("@ss.hasPermi('system:knowledgegraph:list')") + @GetMapping("/pathway/{id}/steps") + public R getPathwaySteps(@PathVariable Long id) { + return R.ok(kgRelationAppService.getPathwaySteps(id)); + } +} diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/clinical/dto/KgPathwayDto.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/clinical/dto/KgPathwayDto.java new file mode 100644 index 000000000..857404027 --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/clinical/dto/KgPathwayDto.java @@ -0,0 +1,13 @@ +package com.healthlink.his.web.clinical.dto; + +import com.healthlink.his.clinical.domain.KgClinicalPathway; +import com.healthlink.his.clinical.domain.KgPathwayStep; +import lombok.Data; + +import java.util.List; + +@Data +public class KgPathwayDto { + private KgClinicalPathway pathway; + private List steps; +} diff --git a/healthlink-his-server/healthlink-his-common/src/main/java/com/healthlink/his/common/enums/KgEntityType.java b/healthlink-his-server/healthlink-his-common/src/main/java/com/healthlink/his/common/enums/KgEntityType.java new file mode 100644 index 000000000..5e6976726 --- /dev/null +++ b/healthlink-his-server/healthlink-his-common/src/main/java/com/healthlink/his/common/enums/KgEntityType.java @@ -0,0 +1,28 @@ +package com.healthlink.his.common.enums; + +import com.baomidou.mybatisplus.annotation.EnumValue; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum KgEntityType implements HisEnumInterface { + + DISEASE(1, "disease", "疾病"), + SYMPTOM(2, "symptom", "症状"), + DRUG(3, "drug", "药物"), + EXAM(4, "exam", "检查"); + + @EnumValue + private final Integer value; + private final String code; + private final String info; + + public static KgEntityType getByCode(String code) { + if (code == null) return null; + for (KgEntityType e : values()) { + if (e.getCode().equals(code)) return e; + } + return null; + } +} diff --git a/healthlink-his-server/healthlink-his-common/src/main/java/com/healthlink/his/common/enums/KgRelationType.java b/healthlink-his-server/healthlink-his-common/src/main/java/com/healthlink/his/common/enums/KgRelationType.java new file mode 100644 index 000000000..9c118937b --- /dev/null +++ b/healthlink-his-server/healthlink-his-common/src/main/java/com/healthlink/his/common/enums/KgRelationType.java @@ -0,0 +1,32 @@ +package com.healthlink.his.common.enums; + +import com.baomidou.mybatisplus.annotation.EnumValue; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum KgRelationType implements HisEnumInterface { + + CAUSES(1, "CAUSES", "导致"), + TREATS(2, "TREATS", "治疗"), + CONTRAINDICATES(3, "CONTRAINDICATES", "禁忌"), + INTERACTS_WITH(4, "INTERACTS_WITH", "相互作用"), + REQUIRES_EXAM(5, "REQUIRES_EXAM", "需要检查"), + HAS_SYMPTOM(6, "HAS_SYMPTOM", "具有症状"), + SIDE_EFFECT(7, "SIDE_EFFECT", "副作用"), + ALTERNATIVE(8, "ALTERNATIVE", "替代"); + + @EnumValue + private final Integer value; + private final String code; + private final String info; + + public static KgRelationType getByCode(String code) { + if (code == null) return null; + for (KgRelationType e : values()) { + if (e.getCode().equals(code)) return e; + } + return null; + } +} diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/clinical/domain/KgClinicalPathway.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/clinical/domain/KgClinicalPathway.java new file mode 100644 index 000000000..988aa7f6e --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/clinical/domain/KgClinicalPathway.java @@ -0,0 +1,22 @@ +package com.healthlink.his.clinical.domain; + +import com.baomidou.mybatisplus.annotation.*; +import com.core.common.core.domain.HisBaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("kg_clinical_pathway") +public class KgClinicalPathway extends HisBaseEntity { + @TableId(value = "id", type = IdType.ASSIGN_ID) + private Long id; + @TableField("pathway_code") private String pathwayCode; + @TableField("pathway_name") private String pathwayName; + @TableField("disease_code") private String diseaseCode; + @TableField("disease_name") private String diseaseName; + @TableField("department") private String department; + @TableField("standard_days") private Integer standardDays; + @TableField("description") private String description; + @TableField("status") private String status; +} diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/clinical/domain/KgEntityRelation.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/clinical/domain/KgEntityRelation.java new file mode 100644 index 000000000..4a72ae10c --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/clinical/domain/KgEntityRelation.java @@ -0,0 +1,24 @@ +package com.healthlink.his.clinical.domain; + +import com.baomidou.mybatisplus.annotation.*; +import com.core.common.core.domain.HisBaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.math.BigDecimal; + +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("kg_entity_relation") +public class KgEntityRelation extends HisBaseEntity { + @TableId(value = "id", type = IdType.ASSIGN_ID) + private Long id; + @TableField("source_type") private String sourceType; + @TableField("source_id") private String sourceId; + @TableField("target_type") private String targetType; + @TableField("target_id") private String targetId; + @TableField("relation_type") private String relationType; + @TableField("relation_strength") private BigDecimal relationStrength; + @TableField("description") private String description; + @TableField("evidence_source") private String evidenceSource; +} diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/clinical/domain/KgPathwayStep.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/clinical/domain/KgPathwayStep.java new file mode 100644 index 000000000..9735ba0d9 --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/clinical/domain/KgPathwayStep.java @@ -0,0 +1,20 @@ +package com.healthlink.his.clinical.domain; + +import com.baomidou.mybatisplus.annotation.*; +import com.core.common.core.domain.HisBaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("kg_pathway_step") +public class KgPathwayStep extends HisBaseEntity { + @TableId(value = "id", type = IdType.ASSIGN_ID) + private Long id; + @TableField("pathway_id") private Long pathwayId; + @TableField("step_order") private Integer stepOrder; + @TableField("day_number") private Integer dayNumber; + @TableField("step_type") private String stepType; + @TableField("step_content") private String stepContent; + @TableField("required") private String required; +} diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/clinical/mapper/KgClinicalPathwayMapper.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/clinical/mapper/KgClinicalPathwayMapper.java new file mode 100644 index 000000000..7d9451861 --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/clinical/mapper/KgClinicalPathwayMapper.java @@ -0,0 +1,9 @@ +package com.healthlink.his.clinical.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.healthlink.his.clinical.domain.KgClinicalPathway; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface KgClinicalPathwayMapper extends BaseMapper { +} diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/clinical/mapper/KgEntityRelationMapper.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/clinical/mapper/KgEntityRelationMapper.java new file mode 100644 index 000000000..e858d863f --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/clinical/mapper/KgEntityRelationMapper.java @@ -0,0 +1,9 @@ +package com.healthlink.his.clinical.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.healthlink.his.clinical.domain.KgEntityRelation; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface KgEntityRelationMapper extends BaseMapper { +} diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/clinical/mapper/KgPathwayStepMapper.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/clinical/mapper/KgPathwayStepMapper.java new file mode 100644 index 000000000..2c7ff3b8a --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/clinical/mapper/KgPathwayStepMapper.java @@ -0,0 +1,9 @@ +package com.healthlink.his.clinical.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.healthlink.his.clinical.domain.KgPathwayStep; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface KgPathwayStepMapper extends BaseMapper { +} diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/clinical/service/IKgClinicalPathwayService.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/clinical/service/IKgClinicalPathwayService.java new file mode 100644 index 000000000..3d1a5e676 --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/clinical/service/IKgClinicalPathwayService.java @@ -0,0 +1,7 @@ +package com.healthlink.his.clinical.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.healthlink.his.clinical.domain.KgClinicalPathway; + +public interface IKgClinicalPathwayService extends IService { +} diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/clinical/service/IKgEntityRelationService.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/clinical/service/IKgEntityRelationService.java new file mode 100644 index 000000000..2880d8bfe --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/clinical/service/IKgEntityRelationService.java @@ -0,0 +1,7 @@ +package com.healthlink.his.clinical.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.healthlink.his.clinical.domain.KgEntityRelation; + +public interface IKgEntityRelationService extends IService { +} diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/clinical/service/IKgPathwayStepService.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/clinical/service/IKgPathwayStepService.java new file mode 100644 index 000000000..50b4f2816 --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/clinical/service/IKgPathwayStepService.java @@ -0,0 +1,7 @@ +package com.healthlink.his.clinical.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.healthlink.his.clinical.domain.KgPathwayStep; + +public interface IKgPathwayStepService extends IService { +} diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/clinical/service/impl/KgClinicalPathwayServiceImpl.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/clinical/service/impl/KgClinicalPathwayServiceImpl.java new file mode 100644 index 000000000..e2221d480 --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/clinical/service/impl/KgClinicalPathwayServiceImpl.java @@ -0,0 +1,11 @@ +package com.healthlink.his.clinical.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.healthlink.his.clinical.domain.KgClinicalPathway; +import com.healthlink.his.clinical.mapper.KgClinicalPathwayMapper; +import com.healthlink.his.clinical.service.IKgClinicalPathwayService; +import org.springframework.stereotype.Service; + +@Service +public class KgClinicalPathwayServiceImpl extends ServiceImpl implements IKgClinicalPathwayService { +} diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/clinical/service/impl/KgEntityRelationServiceImpl.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/clinical/service/impl/KgEntityRelationServiceImpl.java new file mode 100644 index 000000000..950fcacf3 --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/clinical/service/impl/KgEntityRelationServiceImpl.java @@ -0,0 +1,11 @@ +package com.healthlink.his.clinical.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.healthlink.his.clinical.domain.KgEntityRelation; +import com.healthlink.his.clinical.mapper.KgEntityRelationMapper; +import com.healthlink.his.clinical.service.IKgEntityRelationService; +import org.springframework.stereotype.Service; + +@Service +public class KgEntityRelationServiceImpl extends ServiceImpl implements IKgEntityRelationService { +} diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/clinical/service/impl/KgPathwayStepServiceImpl.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/clinical/service/impl/KgPathwayStepServiceImpl.java new file mode 100644 index 000000000..5f9a60c32 --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/clinical/service/impl/KgPathwayStepServiceImpl.java @@ -0,0 +1,11 @@ +package com.healthlink.his.clinical.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.healthlink.his.clinical.domain.KgPathwayStep; +import com.healthlink.his.clinical.mapper.KgPathwayStepMapper; +import com.healthlink.his.clinical.service.IKgPathwayStepService; +import org.springframework.stereotype.Service; + +@Service +public class KgPathwayStepServiceImpl extends ServiceImpl implements IKgPathwayStepService { +} diff --git a/healthlink-his-server/healthlink-his-domain/src/main/resources/mapper/clinical/KgClinicalPathwayMapper.xml b/healthlink-his-server/healthlink-his-domain/src/main/resources/mapper/clinical/KgClinicalPathwayMapper.xml new file mode 100644 index 000000000..0bbb116d1 --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/resources/mapper/clinical/KgClinicalPathwayMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/healthlink-his-server/healthlink-his-domain/src/main/resources/mapper/clinical/KgEntityRelationMapper.xml b/healthlink-his-server/healthlink-his-domain/src/main/resources/mapper/clinical/KgEntityRelationMapper.xml new file mode 100644 index 000000000..554208b96 --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/resources/mapper/clinical/KgEntityRelationMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/healthlink-his-server/healthlink-his-domain/src/main/resources/mapper/clinical/KgPathwayStepMapper.xml b/healthlink-his-server/healthlink-his-domain/src/main/resources/mapper/clinical/KgPathwayStepMapper.xml new file mode 100644 index 000000000..2b6155bc3 --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/resources/mapper/clinical/KgPathwayStepMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/healthlink-his-ui/src/views/knowledgegraph/PathwayEdit.vue b/healthlink-his-ui/src/views/knowledgegraph/PathwayEdit.vue index b28673f6f..508934db0 100644 --- a/healthlink-his-ui/src/views/knowledgegraph/PathwayEdit.vue +++ b/healthlink-his-ui/src/views/knowledgegraph/PathwayEdit.vue @@ -1,5 +1,5 @@