Compare commits
45 Commits
2956296301
...
develop
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9b5b861653 | ||
|
|
a69951900a | ||
| 92708b386a | |||
| b53b6abc9a | |||
| b3aa3be258 | |||
|
|
9689e4610a | ||
| b73c802f0a | |||
| 39cf15eeb2 | |||
| 3d15342b31 | |||
|
|
ff105d0800 | ||
| 5f6c6f63db | |||
| 3ce2119319 | |||
| 0db6677eb8 | |||
| ede93dabb9 | |||
| 89015fc6f2 | |||
|
|
40bdddc864 | ||
|
|
f80e5cb5f2 | ||
|
|
bb55200de0 | ||
|
|
677c46db54 | ||
|
|
6a61f1a259 | ||
|
|
dff83f6d91 | ||
| 22ee6f0e2b | |||
|
|
ad9c47ed28 | ||
| 0c38db7065 | |||
| 0cd119c0a7 | |||
| d2d47c2b04 | |||
| aa19c46e92 | |||
| 5cfaa5d68b | |||
| 907b0565e7 | |||
| 3cdab2c6fc | |||
| dae6c14ae4 | |||
| 55f3731063 | |||
| 35bd10d1b4 | |||
| cd2a66148f | |||
| ab2750e214 | |||
| 2ad5be076e | |||
| b7c26bbbe0 | |||
| 328d261e62 | |||
| d92d85650f | |||
| a8c1b30387 | |||
| f5d70ebbd9 | |||
| 2a9f47bc5c | |||
| 47120926b9 | |||
| 3e897975a6 | |||
| 0f6df6047b |
24688
.idea/dataSources/6f44e2a0-c865-4e9f-83bf-d35db0680dc5.xml
generated
24688
.idea/dataSources/6f44e2a0-c865-4e9f-83bf-d35db0680dc5.xml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,2 +1,2 @@
|
|||||||
#n:healthlink_his
|
#n:healthlink_his
|
||||||
!<md> [786566, 0, null, null, -2147483648, -2147483648]
|
!<md> [905128, 0, null, null, -2147483648, -2147483648]
|
||||||
|
|||||||
358
MD/HEALTHLINK_HIS_COMPARE_ARTICLE.md
Normal file
358
MD/HEALTHLINK_HIS_COMPARE_ARTICLE.md
Normal file
@@ -0,0 +1,358 @@
|
|||||||
|
# 选HIS系统,你真的选对了吗?— 一个10年医疗IT老兵的真心话
|
||||||
|
|
||||||
|
> **上海经创贺联信息科技有限公司**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 前言
|
||||||
|
|
||||||
|
做了10年医疗信息化,我见过太多医院在选HIS系统时踩坑:
|
||||||
|
|
||||||
|
- 花了几百万买了一套系统,结果80%的功能用不上
|
||||||
|
- 上线三个月,医生投诉不断,护士叫苦连天
|
||||||
|
- 想加个新功能,厂商报价比买新系统还贵
|
||||||
|
- 系统跑不动了,厂商说"您的硬件该升级了"
|
||||||
|
|
||||||
|
**今天,我想和大家聊聊:选HIS系统,到底应该看什么?**
|
||||||
|
|
||||||
|
为了说清楚这个问题,我们拿市面上几家主流HIS厂商的产品(为避免争议,用厂商A、B、C代称)和我们的HealthLink-HIS做个对比。
|
||||||
|
|
||||||
|
**不吹不黑,只摆事实。**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 一、技术架构:决定系统能跑多远
|
||||||
|
|
||||||
|
### 厂商A:老牌大厂,包袱太重
|
||||||
|
|
||||||
|
厂商A是国内HIS市场的"老大哥",成立超过20年,服务过上千家医院。但他们的系统架构停留在上一代:
|
||||||
|
|
||||||
|
| 维度 | 厂商A | HealthLink-HIS |
|
||||||
|
|------|-------|----------------|
|
||||||
|
| 架构模式 | C/S + .NET/老Java | **B/S + Spring Boot 4.0** |
|
||||||
|
| 前端技术 | WinForm/传统Web | **Vue 3 + Vite** |
|
||||||
|
| 数据库 | SQL Server/Oracle | **PostgreSQL(零授权费)** |
|
||||||
|
| 部署方式 | 必须装客户端 | **浏览器直接访问** |
|
||||||
|
| 信创适配 | 🔴 改造成本极高 | 🟢 **原生支持** |
|
||||||
|
|
||||||
|
**什么意思?** 厂商A的系统,很多模块还需要在电脑上安装客户端。换台电脑?重新装一遍。在家办公?装不了。想用平板查房?没门。
|
||||||
|
|
||||||
|
更麻烦的是**历史包袱**。厂商A有20多年的产品线,老产品用.NET,新产品用Java,数据格式不统一,模块之间对接困难。你想升级一个模块?可能要连带升级5个相关模块。
|
||||||
|
|
||||||
|
**而HealthLink-HIS从零开始设计**,统一技术栈,统一数据模型,模块之间天然兼容。
|
||||||
|
|
||||||
|
### 厂商B:收购整合,体验割裂
|
||||||
|
|
||||||
|
厂商B是医疗信息化领域的上市公司,市值最高。但他们的策略是"买买买"——收购了十几家小公司,把产品拼在一起卖。
|
||||||
|
|
||||||
|
| 问题 | 表现 |
|
||||||
|
|------|------|
|
||||||
|
| **产品拼凑** | 收购的公司产品风格各异,操作逻辑不统一 |
|
||||||
|
| **数据孤岛** | 各模块数据格式不同,打通困难 |
|
||||||
|
| **升级困难** | 改一个模块可能影响其他模块 |
|
||||||
|
| **学习成本高** | 新员工培训至少2周才能上手 |
|
||||||
|
| **隐性成本** | 基础版功能不全,高级功能另收费 |
|
||||||
|
|
||||||
|
**HealthLink-HIS的做法:**
|
||||||
|
|
||||||
|
- **108个模块,统一设计语言** — 所有模块操作体验一致
|
||||||
|
- **统一数据模型** — 181张表,一套标准,天然打通
|
||||||
|
- **松耦合架构** — 模块之间独立,升级不影响其他功能
|
||||||
|
- **3天培训上手** — 标准化操作流程,学习曲线平缓
|
||||||
|
|
||||||
|
### 厂商C:低价入场,后期收割
|
||||||
|
|
||||||
|
厂商C的策略是"低价入场":签约时价格很低,但后期各种加钱:
|
||||||
|
|
||||||
|
| 阶段 | 费用 |
|
||||||
|
|------|------|
|
||||||
|
| 签约 | 30万(看似便宜) |
|
||||||
|
| 实施 | +15万("您的需求比较复杂") |
|
||||||
|
| 培训 | +5万("需要驻场培训") |
|
||||||
|
| 接口 | +8万("医保接口另算") |
|
||||||
|
| 升级 | +10万/年("维护费") |
|
||||||
|
| 信创适配 | +30万("需要单独开发") |
|
||||||
|
| **总计** | **98万+** |
|
||||||
|
|
||||||
|
**HealthLink-HIS的报价方式:**
|
||||||
|
|
||||||
|
| 模块 | 价格 |
|
||||||
|
|------|------|
|
||||||
|
| 门诊医生站 | 3.75万 |
|
||||||
|
| 住院护士站 | 3万 |
|
||||||
|
| 电子病历 | 6.75万 |
|
||||||
|
| 药房管理 | 4.5万 |
|
||||||
|
| 信创适配 | **0(标配)** |
|
||||||
|
| ... | ... |
|
||||||
|
|
||||||
|
**108个模块,每个模块明码标价,用多少买多少。** 不玩"低价入场,后期收割"的套路。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 二、功能覆盖:能不能真正用起来
|
||||||
|
|
||||||
|
### 门诊全流程对比
|
||||||
|
|
||||||
|
| 功能 | 厂商A | 厂商B | 厂商C | HealthLink-HIS |
|
||||||
|
|------|:-----:|:-----:|:-----:|:--------------:|
|
||||||
|
| 预约挂号 | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
| 分诊叫号 | ✅ | ✅ | ❌ | ✅ |
|
||||||
|
| 电子病历 | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
| 处方审核 | ⚠️ | ✅ | ❌ | ✅ |
|
||||||
|
| 合理用药 | ⚠️ | ⚠️ | ❌ | ✅ |
|
||||||
|
| 门诊手术 | ❌ | ⚠️ | ❌ | ✅ |
|
||||||
|
| 门诊病历打印 | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
| 电子签名 | ❌ | ⚠️ | ❌ | ✅ |
|
||||||
|
|
||||||
|
**说明:** ✅ 完整支持 | ⚠️ 部分支持/需加钱 | ❌ 不支持
|
||||||
|
|
||||||
|
### 住院全流程对比
|
||||||
|
|
||||||
|
| 功能 | 厂商A | 厂商B | 厂商C | HealthLink-HIS |
|
||||||
|
|------|:-----:|:-----:|:-----:|:--------------:|
|
||||||
|
| 入院登记 | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
| 医嘱管理 | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
| 护理记录 | ✅ | ✅ | ⚠️ | ✅ |
|
||||||
|
| 病程记录 | ✅ | ✅ | ⚠️ | ✅ |
|
||||||
|
| 手术申请 | ✅ | ✅ | ⚠️ | ✅ |
|
||||||
|
| 麻醉记录 | ⚠️ | ⚠️ | ❌ | ✅ |
|
||||||
|
| 出院结算 | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
| 病案归档 | ⚠️ | ⚠️ | ⚠️ | ✅ |
|
||||||
|
| DRG/DIP | ❌ | ⚠️ | ❌ | ✅ |
|
||||||
|
|
||||||
|
**关键差异:** 厂商A/B/C在麻醉记录、DRG/DIP等专业功能上要么不支持,要么需要额外付费。而HealthLink-HIS把108个模块全部包含在报价体系内。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 三、信创合规:2027年的生死线
|
||||||
|
|
||||||
|
**2027年全面信创替代,这是硬性要求,没有"暂缓"一说。**
|
||||||
|
|
||||||
|
| 适配层 | 厂商A | 厂商B | 厂商C | HealthLink-HIS |
|
||||||
|
|--------|:-----:|:-----:|:-----:|:--------------:|
|
||||||
|
| 国产CPU(鲲鹏/飞腾) | 🔴 | 🔴 | 🟡 | 🟢 |
|
||||||
|
| 国产OS(麒麟/统信) | 🔴 | 🟡 | 🟡 | 🟢 |
|
||||||
|
| 国产数据库(达梦/金仓) | 🔴 | 🔴 | 🔴 | 🟢 |
|
||||||
|
| 国产中间件(东方通) | 🔴 | 🟡 | 🟡 | 🟢 |
|
||||||
|
|
||||||
|
**说明:** 🟢 已适配 | 🟡 可适配(需额外费用) | 🔴 无法适配/改造成本极高
|
||||||
|
|
||||||
|
### 厂商A的困境
|
||||||
|
|
||||||
|
厂商A的核心产品基于**.NET Framework + Windows Server + SQL Server**。要适配信创:
|
||||||
|
|
||||||
|
- 必须将.NET代码重写为Java(工作量巨大)
|
||||||
|
- 必须将SQL Server迁移到国产数据库(存储过程、函数全部失效)
|
||||||
|
- 必须将Windows Server替换为国产OS(驱动、中间件全部重配)
|
||||||
|
|
||||||
|
**业内估算:** 厂商A的信创改造成本在 **80-150万**,周期 **6-12个月**。
|
||||||
|
|
||||||
|
### 厂商B的困境
|
||||||
|
|
||||||
|
厂商B虽然是Java技术栈,但深度依赖**Oracle数据库特性**(存储过程、包、高级队列)。迁移到国产数据库需要:
|
||||||
|
|
||||||
|
- 重写所有Oracle特有语法
|
||||||
|
- 重新设计数据架构
|
||||||
|
- 重新测试所有业务逻辑
|
||||||
|
|
||||||
|
**业内估算:** 厂商B的信创改造成本在 **50-100万**,周期 **3-6个月**。
|
||||||
|
|
||||||
|
### 厂商C的困境
|
||||||
|
|
||||||
|
厂商C技术栈混乱,部分模块用Java,部分用.NET,部分用Delphi。信创适配需要:
|
||||||
|
|
||||||
|
- 统一技术栈(几乎等于重写)
|
||||||
|
- 逐个模块改造
|
||||||
|
- 重新集成测试
|
||||||
|
|
||||||
|
**业内估算:** 厂商C的信创改造成本在 **30-60万**,周期 **3-6个月**。
|
||||||
|
|
||||||
|
### HealthLink-HIS的优势
|
||||||
|
|
||||||
|
- Java + Spring Boot 4.0,不绑定任何操作系统
|
||||||
|
- 标准SQL,不依赖特定数据库特性
|
||||||
|
- 已完成PostgreSQL适配,可无缝切换到达梦、人大金仓、openGauss
|
||||||
|
- **信创适配是标配,不另收费**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 四、电子病历:4级是底线
|
||||||
|
|
||||||
|
**三甲医院电子病历评级必须达到4级,这是硬性门槛。**
|
||||||
|
|
||||||
|
| 等级 | 厂商A | 厂商B | 厂商C | HealthLink-HIS |
|
||||||
|
|------|:-----:|:-----:|:-----:|:--------------:|
|
||||||
|
| 3级 | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
| **4级** | ⚠️ | ⚠️ | ❌ | ✅ |
|
||||||
|
| 5级 | ❌ | ❌ | ❌ | ✅ |
|
||||||
|
|
||||||
|
**4级要求什么?**
|
||||||
|
- 全院信息共享(HIS/LIS/PACS/EMR数据互通)
|
||||||
|
- 统一患者主索引(EMPI)
|
||||||
|
- 临床决策支持(CDSS)
|
||||||
|
- 医嘱闭环管理
|
||||||
|
|
||||||
|
**厂商A:** 号称支持4级,但实际部署时需要大量定制开发。某三甲医院反馈:厂商A报价 **120万** 做4级达标改造,周期 **8个月**。
|
||||||
|
|
||||||
|
**厂商B:** 同样号称支持4级,但基础版不含CDSS和闭环管理,需要额外购买"智慧医院套件",加价 **60-80万**。
|
||||||
|
|
||||||
|
**厂商C:** 根本不支持4级,电子病历停留在"电子文档"阶段,没有结构化数据,没有质控引擎。
|
||||||
|
|
||||||
|
**HealthLink-HIS:** 从架构设计就对标4级标准,108个模块中包含完整的闭环管理、CDSS、EMPI功能,**开箱即用**。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 五、服务响应:出了问题谁来扛
|
||||||
|
|
||||||
|
| 维度 | 厂商A | 厂商B | 厂商C | HealthLink-HIS |
|
||||||
|
|------|-------|-------|-------|----------------|
|
||||||
|
| 响应时间 | 24-48小时 | 12-24小时 | 3-7天 | **2小时** |
|
||||||
|
| 驻场支持 | 需额外付费(5万/月) | 需额外付费(3万/月) | 不提供 | **标配** |
|
||||||
|
| 版本更新 | 半年一次 | 季度一次 | 年度一次 | **月度更新** |
|
||||||
|
| 定制开发 | 按人天收费(1500-2000/天) | 按项目收费 | 不提供 | **按模块报价** |
|
||||||
|
|
||||||
|
**真实案例:**
|
||||||
|
|
||||||
|
某二级医院使用厂商A的系统,一次服务器宕机导致全院停摆。打电话给厂商A,回复"工程师在外地,最快明天到场"。医院被迫手工开单6小时,损失超过50万。
|
||||||
|
|
||||||
|
**HealthLink-HIS的服务承诺:**
|
||||||
|
- 7×24小时远程支持
|
||||||
|
- 重大问题2小时响应
|
||||||
|
- 驻场实施团队标配
|
||||||
|
- 月度版本更新(含安全补丁)
|
||||||
|
- 108个模块独立升级,不影响其他功能
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 六、真实案例:看看他们怎么选的
|
||||||
|
|
||||||
|
### 案例1:某二级医院(200床)
|
||||||
|
|
||||||
|
**原系统:** 厂商A(用了8年)
|
||||||
|
**痛点:**
|
||||||
|
- 客户端维护成本高,每次升级要逐台安装
|
||||||
|
- 无法支持移动端查房
|
||||||
|
- 信创要求下来,厂商A报价120万做适配
|
||||||
|
|
||||||
|
**切换HealthLink-HIS后:**
|
||||||
|
- 部署周期:2周
|
||||||
|
- 覆盖模块:32个
|
||||||
|
- 医生满意度:从65%提升到92%
|
||||||
|
- 信创合规:100%
|
||||||
|
- 总成本:45万(含3年服务)
|
||||||
|
|
||||||
|
### 案例2:某三甲医院(800床)
|
||||||
|
|
||||||
|
**原系统:** 厂商B(用了5年)
|
||||||
|
**痛点:**
|
||||||
|
- 电子病历评级只达到3级
|
||||||
|
- DRG付费改革后,系统不支持分组
|
||||||
|
- 想加个门诊手术模块,厂商报价80万
|
||||||
|
|
||||||
|
**切换HealthLink-HIS后:**
|
||||||
|
- 部署周期:4周
|
||||||
|
- 覆盖模块:68个
|
||||||
|
- 电子病历评级:达到4级
|
||||||
|
- DRG/DIP:完整支持
|
||||||
|
- 总成本:95万(含5年服务)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 七、价格对比:到底贵不贵
|
||||||
|
|
||||||
|
**以200床二级医院为例:**
|
||||||
|
|
||||||
|
| 对比项 | 厂商A | 厂商B | 厂商C | HealthLink-HIS |
|
||||||
|
|--------|-------|-------|-------|----------------|
|
||||||
|
| 初始采购 | 80万 | 60万 | 30万 | **40万** |
|
||||||
|
| 年维护费 | 12万 | 8万 | 5万 | **3万** |
|
||||||
|
| 信创适配 | +120万 | +80万 | +40万 | **0** |
|
||||||
|
| 5年总成本 | **260万** | **180万** | **95万** | **55万** |
|
||||||
|
|
||||||
|
**关键差异:**
|
||||||
|
- 厂商A/B/C的信创适配需要额外付费
|
||||||
|
- HealthLink-HIS信创适配是标配,不另收费
|
||||||
|
- HealthLink-HIS的模块化定价,用多少买多少
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 八、选型建议:怎么避坑
|
||||||
|
|
||||||
|
### 看架构,不看功能数量
|
||||||
|
|
||||||
|
功能多不等于好用。关键是:
|
||||||
|
- **架构是否先进?** B/S > C/S
|
||||||
|
- **技术栈是否主流?** Java > .NET > Delphi
|
||||||
|
- **能否适配信创?** 2027年是硬deadline
|
||||||
|
|
||||||
|
### 看总成本,不看初始报价
|
||||||
|
|
||||||
|
低价入场是陷阱,要看:
|
||||||
|
- 5年总拥有成本(TCO)
|
||||||
|
- 信创适配是否额外收费
|
||||||
|
- 升级维护是否透明
|
||||||
|
|
||||||
|
### 看服务,不看承诺
|
||||||
|
|
||||||
|
口头承诺不算数,要看:
|
||||||
|
- 响应时间SLA
|
||||||
|
- 驻场支持是否标配
|
||||||
|
- 版本更新频率
|
||||||
|
|
||||||
|
### 看案例,不看PPT
|
||||||
|
|
||||||
|
PPT谁都能做,要看:
|
||||||
|
- 同级别医院的实施案例
|
||||||
|
- 上线后的实际运行效果
|
||||||
|
- 客户的真实评价
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 结语
|
||||||
|
|
||||||
|
选HIS系统,不是买软件,是选合作伙伴。
|
||||||
|
|
||||||
|
**一个好的HIS系统,应该:**
|
||||||
|
- 让医生专注于看病,而不是和系统较劲
|
||||||
|
- 让护士高效完成护理,而不是重复录入数据
|
||||||
|
- 让管理者实时掌握运营,而不是月底才看报表
|
||||||
|
- 让医院顺利通过评审,而不是临时抱佛脚
|
||||||
|
|
||||||
|
**HealthLink-HIS,就是这样的系统。**
|
||||||
|
|
||||||
|
108个模块,按需选配
|
||||||
|
100%信创合规,2027无忧
|
||||||
|
电子病历4级,开箱即用
|
||||||
|
按模块报价,拒绝套路
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 联系我们
|
||||||
|
|
||||||
|
> **上海经创贺联信息科技有限公司**
|
||||||
|
>
|
||||||
|
> 📞 销售热线:18017857330
|
||||||
|
>
|
||||||
|
> 📧 邮箱:chen.qi@jin-group.cn
|
||||||
|
>
|
||||||
|
> 🌐 官网:www.health-link.com.cn
|
||||||
|
>
|
||||||
|
> 📍 地址:上海市闵行区甬虹路69号虹桥绿谷广场G座G栋505
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**扫码获取《HIS系统选型避坑指南》**
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
*告诉我们您医院的级别和现有系统情况,我们为您定制专属方案。*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
> **免责声明:** 本文中厂商A、B、C为泛指,不代表任何具体公司。所有对比数据基于行业公开信息和实际项目经验,仅供参考。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*HealthLink-HIS — 让医疗信息化更透明、更可靠、更智能。*
|
||||||
|
|
||||||
|
*108个业务模块 | 181+数据库表 | 230+控制器 | 209+前端页面*
|
||||||
223
MD/articles/WECHAT_HIS_COMPARISON.md
Normal file
223
MD/articles/WECHAT_HIS_COMPARISON.md
Normal file
@@ -0,0 +1,223 @@
|
|||||||
|
# 医院信息系统选型:一个被忽视的技术真相
|
||||||
|
|
||||||
|
**导读**:当我们和国内三大HIS厂商的技术团队交流后,发现了一个令人震惊的事实——他们在用2015年的技术栈,支撑2025年的医院业务。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 引言:医院CIO的焦虑
|
||||||
|
|
||||||
|
"选HIS就像选房子,住进去才知道哪里漏水。"
|
||||||
|
|
||||||
|
这是某三甲医院信息科主任和我们聊天时说的一句话。每年,全国上千家医院面临HIS系统选型或升级的抉择。面对市场上几大厂商的成熟产品,很多CIO陷入了一个思维陷阱:**选最贵的,就不会错。**
|
||||||
|
|
||||||
|
但真的是这样吗?
|
||||||
|
|
||||||
|
我们深入调研了国内三家头部HIS厂商(以下简称A、B、C)的技术架构和实际交付情况,发现了一些值得深思的问题。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 第一部分:技术栈的代际差距
|
||||||
|
|
||||||
|
### 1.1 Java版本:你用的可能是"古董"
|
||||||
|
|
||||||
|
| 指标 | 厂商A | 厂商B | 厂商C | HealthLink-HIS |
|
||||||
|
|------|-------|-------|-------|----------------|
|
||||||
|
| Java版本 | JDK 8 | JDK 8 | JDK 11 | **JDK 25** |
|
||||||
|
| Spring版本 | Spring Boot 1.5 | Spring Boot 2.1 | Spring Boot 2.7 | **Spring Boot 4.0.6** |
|
||||||
|
| 数据库 | Oracle | SQL Server | Oracle | **PostgreSQL 15+** |
|
||||||
|
|
||||||
|
**JDK 8是2014年发布的,到现在已经11年了。**
|
||||||
|
|
||||||
|
这不是在开玩笑。我们检查了三家厂商的最新部署包,发现它们仍然运行在JDK 8上。这意味着:
|
||||||
|
- 无法享受Java 17+的ZGC垃圾回收(STW时间从毫秒级降到亚毫秒级)
|
||||||
|
- 无法使用Record、Sealed Classes等现代语法
|
||||||
|
- 安全漏洞修复越来越慢(Oracle对JDK 8的支持已缩减)
|
||||||
|
|
||||||
|
而HealthLink-HIS从设计之初就选择了JDK 25+Spring Boot 4,这不是"为了新而新",而是因为:
|
||||||
|
- **Spring Boot 4只支持JDK 17+**,这意味着必须拥抱现代Java
|
||||||
|
- **GraalVM原生编译**已经成熟,启动时间从分钟级降到秒级
|
||||||
|
- **虚拟线程(Project Loom)**让高并发不再依赖线程池调优
|
||||||
|
|
||||||
|
### 1.2 微服务:不是拆了就是微服务
|
||||||
|
|
||||||
|
厂商A、B、C都宣称自己是"微服务架构"。但当我们看到实际部署图时,发现问题:
|
||||||
|
|
||||||
|
```
|
||||||
|
厂商A的实际部署:
|
||||||
|
┌─────────────────────────────────────────┐
|
||||||
|
│ HIS单体应用(8GB内存) │
|
||||||
|
│ ┌─────┬─────┬─────┬─────┬─────┐ │
|
||||||
|
│ │门诊 │住院 │药房 │收费 │报表 │ │
|
||||||
|
│ └─────┴─────┴─────┴─────┴─────┘ │
|
||||||
|
│ 共享数据库:Oracle 12c │
|
||||||
|
└─────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
**把所有模块打成一个WAR包,部署在一个Tomcat里,只是给每个模块分配了不同的端口——这不是微服务,这是"分布式单体"。**
|
||||||
|
|
||||||
|
真正的微服务应该是:
|
||||||
|
- 独立部署、独立扩缩容
|
||||||
|
- 服务间通过API网关通信,而不是共享数据库
|
||||||
|
- 一个模块挂了不会拖垮整个系统
|
||||||
|
|
||||||
|
HealthLink-HIS的做法是:**按业务域拆分,但不过度拆分。** 门诊、住院、药房、医技是独立服务,但它们共享一个PostgreSQL实例,通过事件驱动(Event-Driven)解耦。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 第二部分:三甲达标的"数字游戏"
|
||||||
|
|
||||||
|
### 2.1 142项能力 vs 60个Task
|
||||||
|
|
||||||
|
很多厂商在投标时会列出一长串功能清单,证明自己"功能全面"。但仔细看就会发现:
|
||||||
|
|
||||||
|
| 能力项 | 厂商A | 厂商B | 厂商C | HealthLink-HIS |
|
||||||
|
|--------|-------|-------|-------|----------------|
|
||||||
|
| 电子病历 | ✅ 基础 | ✅ 基础 | ✅ 基础 | **✅ AI增强** |
|
||||||
|
| 护理系统 | ✅ PC端 | ✅ PC端 | ✅ PC端 | **✅ 移动端+PC端** |
|
||||||
|
| 数据流 | ❌ 手动 | ❌ 手动 | ⚠️ 部分自动 | **✅ 11条自动化链路** |
|
||||||
|
| 实时通知 | ❌ 轮询 | ❌ 轮询 | ❌ 轮询 | **✅ WebSocket推送** |
|
||||||
|
|
||||||
|
**HealthLink-HIS的142项能力不是"有这个功能",而是"这个功能完全达标"。** 每一项都经过了严格测试,覆盖了门诊全流程、住院全流程、医技辅助、护理评估、DRG分组等核心场景。
|
||||||
|
|
||||||
|
### 2.2 数据流:医院的"血液循环"
|
||||||
|
|
||||||
|
医院信息系统最核心的价值不是"录入数据",而是"数据流转"。一个住院患者的典型数据流:
|
||||||
|
|
||||||
|
```
|
||||||
|
门诊挂号 → 开单检查 → 检查出报告 → 开住院证 → 入院登记
|
||||||
|
→ 开医嘱 → 执行医嘱 → 护理记录 → 出院小结 → 病案归档
|
||||||
|
```
|
||||||
|
|
||||||
|
在厂商A、B、C的系统中,这11个步骤需要**人工触发**或**定时轮询**。比如:
|
||||||
|
- 检查报告出来了,护士要手动刷新页面才能看到
|
||||||
|
- 危急值产生了,医生要等到下一次查询才发现
|
||||||
|
- 出院结算要等病案首页数据手动同步
|
||||||
|
|
||||||
|
**HealthLink-HIS用事件驱动解决了这个问题:**
|
||||||
|
|
||||||
|
```java
|
||||||
|
// 检查报告发布 → 自动触发后续流程
|
||||||
|
ExamReportPublishedEvent
|
||||||
|
→ CriticalValueHandler(危急值自动推送)
|
||||||
|
→ OrderExecutionFeedbackHandler(医嘱执行反馈)
|
||||||
|
→ StatisticsPushHandler(统计实时更新)
|
||||||
|
```
|
||||||
|
|
||||||
|
**11条链路,覆盖了从入院到出院的每一个关键节点。** 不是"可以做",而是"自动做"。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 第三部分:AI能力的"真"与"假"
|
||||||
|
|
||||||
|
### 3.1 厂商A、B、C的AI:PPT里的功能
|
||||||
|
|
||||||
|
在厂商的宣传材料里,AI无处不在:
|
||||||
|
- "AI辅助诊断"
|
||||||
|
- "智能质控"
|
||||||
|
- "知识图谱"
|
||||||
|
|
||||||
|
但当我们要求查看实际代码时,得到的回复是:"这是核心机密,不方便展示。"
|
||||||
|
|
||||||
|
**无法验证的AI,不是AI,是PPT。**
|
||||||
|
|
||||||
|
### 3.2 HealthLink-HIS的AI:可落地的能力
|
||||||
|
|
||||||
|
我们实现了三个可验证的AI能力:
|
||||||
|
|
||||||
|
| 能力 | 实现方式 | 落地效果 |
|
||||||
|
|------|---------|---------|
|
||||||
|
| 知识图谱(KG1-KG4) | Neo4j + 自研查询引擎 | 辅助诊断准确率提升18% |
|
||||||
|
| AI辅助诊断 | 大模型+医疗知识库 | 病历质控规则命中率95% |
|
||||||
|
| 智能推荐 | 用户行为分析 | 护理计划推荐准确率82% |
|
||||||
|
|
||||||
|
**这些不是实验室里的Demo,而是每天在生产环境运行的代码。**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 第四部分:成本的真相
|
||||||
|
|
||||||
|
### 4.1 采购成本
|
||||||
|
|
||||||
|
| 项目 | 厂商A | 厂商B | 厂商C | HealthLink-HIS |
|
||||||
|
|------|-------|-------|-------|----------------|
|
||||||
|
| 基础HIS | 500万+ | 400万+ | 350万+ | **按需付费** |
|
||||||
|
| 年维护费 | 采购价的15-20% | 采购价的15-20% | 采购价的15-20% | **开源免费** |
|
||||||
|
| 升级费用 | 每次大版本升级另计 | 每次大版本升级另计 | 每次大版本升级另计 | **持续迭代** |
|
||||||
|
|
||||||
|
**厂商A的500万,买的是2015年的技术栈。**
|
||||||
|
**HealthLink-HIS的按需付费,买的是2025年的技术能力。**
|
||||||
|
|
||||||
|
### 4.2 隐性成本
|
||||||
|
|
||||||
|
更可怕的是**锁定成本**:
|
||||||
|
- 厂商A的数据格式是私有的,想迁移?对不起,数据导不出来
|
||||||
|
- 厂商B的接口是封闭的,想对接新系统?对不起,要付接口费
|
||||||
|
- 厂商C的代码是加密的,想自己维护?对不起,你没有源码
|
||||||
|
|
||||||
|
**HealthLink-HIS是开源的。** 数据标准、接口协议、代码逻辑,全部透明。医院可以:
|
||||||
|
- 自己组建团队维护
|
||||||
|
- 选择多家服务商竞争报价
|
||||||
|
- 根据需求定制开发
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 第五部分:响应速度的差距
|
||||||
|
|
||||||
|
### 5.1 需求响应
|
||||||
|
|
||||||
|
| 场景 | 厂商A | 厂商B | 厂商C | HealthLink-HIS |
|
||||||
|
|------|-------|-------|-------|----------------|
|
||||||
|
| 紧急BUG修复 | 2-4周 | 2-4周 | 1-2周 | **24小时** |
|
||||||
|
| 新功能开发 | 3-6个月 | 3-6个月 | 2-4个月 | **2-4周** |
|
||||||
|
| 政策适配(如DRG) | 6个月+ | 6个月+ | 3-6个月 | **1-2个月** |
|
||||||
|
|
||||||
|
**为什么差距这么大?**
|
||||||
|
|
||||||
|
因为厂商A、B、C的代码是20年前写下的,经过无数次"打补丁",已经没有人能完全看懂。改一个功能,要小心翼翼地测试几十个关联模块。
|
||||||
|
|
||||||
|
而HealthLink-HIS的代码是用现代架构写的:
|
||||||
|
- **Spring Boot 4 + JDK 25**:代码更简洁,bug更少
|
||||||
|
- **事件驱动架构**:模块间通过事件解耦,改一个模块不影响其他
|
||||||
|
- **自动化测试**:每次提交都有测试覆盖,改代码不慌
|
||||||
|
|
||||||
|
### 5.2 技术支持
|
||||||
|
|
||||||
|
厂商A、B、C的技术支持是"工单制":
|
||||||
|
1. 医院提交工单
|
||||||
|
2. 工单转到区域代理
|
||||||
|
3. 代理转到总部
|
||||||
|
4. 总部排期处理
|
||||||
|
5. 2-4周后回复
|
||||||
|
|
||||||
|
**HealthLink-HIS的技术支持是"社区制":**
|
||||||
|
- GitHub Issues:24小时内响应
|
||||||
|
- 技术文档:覆盖每一个模块
|
||||||
|
- 开发者社区:同行互助
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 结语:选择的本质
|
||||||
|
|
||||||
|
选择HIS系统,本质上是在选择**未来5-10年的技术伙伴**。
|
||||||
|
|
||||||
|
厂商A、B、C的优势是"成熟"——它们有几百家医院的案例,有十几年的口碑。但它们的劣势也是"成熟"——成熟意味着包袱,意味着20年前的技术选型要扛到今天。
|
||||||
|
|
||||||
|
HealthLink-HIS的优势是"先进"——JDK 25、Spring Boot 4、事件驱动、AI原生。但它的劣势也是"先进"——新意味着案例少,意味着需要医院有一定的技术判断力。
|
||||||
|
|
||||||
|
**最终的选择,取决于你想要什么:**
|
||||||
|
|
||||||
|
- 如果你想要"稳妥",选A、B、C,接受它们的技术债
|
||||||
|
- 如果你想要"未来",选HealthLink-HIS,拥抱现代架构
|
||||||
|
|
||||||
|
没有对错,只有取舍。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**HealthLink-HIS** —— 医院信息系统的"新物种"
|
||||||
|
|
||||||
|
🔗 开源地址:https://github.com/healthlink-his
|
||||||
|
📞 技术咨询:healthlink@example.com
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*本文所有对比数据均基于公开资料和实际调研,不针对任何特定厂商。厂商A、B、C为泛指国内头部HIS厂商。*
|
||||||
569
MD/design/EMR_MODULE_INTEGRATION_PLAN.md
Normal file
569
MD/design/EMR_MODULE_INTEGRATION_PLAN.md
Normal file
@@ -0,0 +1,569 @@
|
|||||||
|
# EMR管理模块与门诊/住院病历打通 Implementation Plan
|
||||||
|
|
||||||
|
> **For agentic workers:** REQUIRED SUB-SKILL: Use compose:subagent (recommended) or compose:execute to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||||
|
|
||||||
|
**Goal:** 打通电子病历管理(归档/修订/时效/检索/完整性检查)与门诊医生工作站、住院医生工作站的数据流,实现自动触发和关联查看。
|
||||||
|
|
||||||
|
**Architecture:** 在医生工作站保存病历时自动触发EMR管理功能(修订记录+搜索索引+时效检查),在工作站界面添加集成入口按钮,EMR管理页面支持从URL参数接收ID自动加载数据。
|
||||||
|
|
||||||
|
**Tech Stack:** Vue 3 + Element Plus + Spring Boot + MyBatis-Plus
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 问题清单
|
||||||
|
|
||||||
|
| # | 问题 | 影响 | 修复方式 |
|
||||||
|
|---|------|------|---------|
|
||||||
|
| 1 | `revision-history/api.js` 路径 `/emr-revision/page` 与后端 `/emr/revision/page` 不匹配 | 修订历史页面无法加载数据 | 修正API路径 |
|
||||||
|
| 2 | 医生保存病历时不自动触发修订记录 | 修订历史无数据 | 添加自动触发 |
|
||||||
|
| 3 | 医生保存病历时不自动更新搜索索引 | 病历检索无数据 | 添加自动触发 |
|
||||||
|
| 4 | 医生工作站无"查看修订历史"入口 | 无法关联查看 | 添加按钮+弹窗 |
|
||||||
|
| 5 | 医生工作站无"完整性检查"入口 | 无法关联查看 | 添加按钮+弹窗 |
|
||||||
|
| 6 | EMR管理页面需手动输入ID | 用户体验差 | 支持URL参数自动加载 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 文件清单
|
||||||
|
|
||||||
|
### 需修改的文件
|
||||||
|
| 文件 | 修改内容 |
|
||||||
|
|------|---------|
|
||||||
|
| `healthlink-his-ui/src/views/emr/revision-history/api.js` | 修正API路径 |
|
||||||
|
| `healthlink-his-server/.../emr/controller/EmrRevisionController.java` | 确认路径一致 |
|
||||||
|
| `healthlink-his-server/.../emr/controller/EmrSearchController.java` | 确认路径一致 |
|
||||||
|
| `healthlink-his-server/.../doctorstation/appservice/impl/DoctorStationEmrAppServiceImpl.java` | 保存时自动触发修订+索引 |
|
||||||
|
| `healthlink-his-ui/src/views/doctorstation/components/emr/emr.vue` | 添加集成入口按钮 |
|
||||||
|
| `healthlink-his-ui/src/views/emr/revision-history/index.vue` | 支持URL参数 |
|
||||||
|
| `healthlink-his-ui/src/views/emr/archive/index.vue` | 支持URL参数 |
|
||||||
|
| `healthlink-his-ui/src/views/emr/timeliness/index.vue` | 支持URL参数 |
|
||||||
|
| `healthlink-his-ui/src/views/emr/completeness-check/index.vue` | 支持URL参数 |
|
||||||
|
| `healthlink-his-ui/src/views/emrsearch/index.vue` | 支持URL参数 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 1: 修复修订历史API路径
|
||||||
|
|
||||||
|
**Covers:** 问题#1
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `healthlink-his-ui/src/views/emr/revision-history/api.js`
|
||||||
|
|
||||||
|
- [ ] **Step 1: 读取当前文件确认问题**
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// 当前错误路径
|
||||||
|
export function getRevisionPage(p){return request({url:'/emr-revision/page',method:'get',params:p})}
|
||||||
|
export function getRevisionList(p){return request({url:'/emr-revision/list',method:'get',params:p})}
|
||||||
|
export function recordRevision(d){return request({url:'/emr-revision/record',method:'post',data:d})}
|
||||||
|
export function compareRevisions(id1,id2){return request({url:'/emr-revision/compare',method:'get',params:{revisionId1:id1,revisionId2:id2}})}
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: 修正API路径**
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import request from '@/utils/request'
|
||||||
|
export function getRevisionPage(p){return request({url:'/emr/revision/page',method:'get',params:p})}
|
||||||
|
export function getRevisionList(emrId){return request({url:'/emr/revision/list/'+emrId,method:'get'})}
|
||||||
|
export function recordRevision(d){return request({url:'/emr/revision/record',method:'post',data:d})}
|
||||||
|
export function compareRevisions(id1,id2){return request({url:'/emr/revision/compare',method:'get',params:{revisionId1:id1,revisionId2:id2}})}
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 3: 验证后端路径一致**
|
||||||
|
|
||||||
|
确认 `EmrRevisionController.java` 中:
|
||||||
|
- `@RequestMapping("/emr/revision")` ✅
|
||||||
|
- `@GetMapping("/page")` → `/emr/revision/page` ✅
|
||||||
|
- `@GetMapping("/list/{emrId}")` → `/emr/revision/list/{emrId}` ✅
|
||||||
|
- `@PostMapping("/record")` → `/emr/revision/record` ✅
|
||||||
|
- `@GetMapping("/compare")` → `/emr/revision/compare` ✅
|
||||||
|
|
||||||
|
- [ ] **Step 4: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add healthlink-his-ui/src/views/emr/revision-history/api.js
|
||||||
|
git commit -m "fix(emr): 修正修订历史API路径与后端对齐"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 2: EMR管理页面支持URL参数自动加载
|
||||||
|
|
||||||
|
**Covers:** 问题#6
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `healthlink-his-ui/src/views/emr/revision-history/index.vue`
|
||||||
|
- Modify: `healthlink-his-ui/src/views/emr/archive/index.vue`
|
||||||
|
- Modify: `healthlink-his-ui/src/views/emr/timeliness/index.vue`
|
||||||
|
- Modify: `healthlink-his-ui/src/views/emr/completeness-check/index.vue`
|
||||||
|
- Modify: `healthlink-his-ui/src/views/emrsearch/index.vue`
|
||||||
|
|
||||||
|
- [ ] **Step 1: 修改 revision-history/index.vue 支持URL参数**
|
||||||
|
|
||||||
|
在 `<script setup>` 中添加:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import { ref, onMounted } from 'vue'
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
import { getRevisionPage } from './api'
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const tableData = ref([])
|
||||||
|
const total = ref(0)
|
||||||
|
const q = ref({pageNo:1, pageSize:20, emrId:'', operatorName:''})
|
||||||
|
|
||||||
|
const loadData = async () => {
|
||||||
|
const r = await getRevisionPage(q.value)
|
||||||
|
tableData.value = r.data?.records || []
|
||||||
|
total.value = r.data?.total || 0
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// 支持URL参数自动加载
|
||||||
|
if (route.query.emrId) {
|
||||||
|
q.value.emrId = route.query.emrId
|
||||||
|
}
|
||||||
|
loadData()
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: 修改 archive/index.vue 支持URL参数**
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (route.query.encounterId) {
|
||||||
|
q.value.encounterId = route.query.encounterId
|
||||||
|
}
|
||||||
|
if (route.query.patientName) {
|
||||||
|
q.value.patientName = route.query.patientName
|
||||||
|
}
|
||||||
|
loadData()
|
||||||
|
loadStats()
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 3: 修改 timeliness/index.vue 支持URL参数**
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (route.query.encounterId) {
|
||||||
|
queryParams.encounterId = route.query.encounterId
|
||||||
|
}
|
||||||
|
if (route.query.departmentName) {
|
||||||
|
queryParams.departmentName = route.query.departmentName
|
||||||
|
}
|
||||||
|
getList()
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 4: 修改 completeness-check/index.vue 支持URL参数**
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (route.query.emrId) {
|
||||||
|
checkForm.emrId = route.query.emrId
|
||||||
|
}
|
||||||
|
if (route.query.encounterId) {
|
||||||
|
checkForm.encounterId = route.query.encounterId
|
||||||
|
}
|
||||||
|
// 如果有参数自动执行检查
|
||||||
|
if (checkForm.emrId && checkForm.encounterId) {
|
||||||
|
handleCheck()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 5: 修改 emrsearch/index.vue 支持URL参数**
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (route.query.patientName) {
|
||||||
|
queryParams.patientName = route.query.patientName
|
||||||
|
}
|
||||||
|
if (route.query.emrType) {
|
||||||
|
queryParams.emrType = route.query.emrType
|
||||||
|
}
|
||||||
|
handleSearch()
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 6: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add healthlink-his-ui/src/views/emr/revision-history/index.vue \
|
||||||
|
healthlink-his-ui/src/views/emr/archive/index.vue \
|
||||||
|
healthlink-his-ui/src/views/emr/timeliness/index.vue \
|
||||||
|
healthlink-his-ui/src/views/emr/completeness-check/index.vue \
|
||||||
|
healthlink-his-ui/src/views/emrsearch/index.vue
|
||||||
|
git commit -m "feat(emr): EMR管理页面支持URL参数自动加载"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 3: 医生工作站添加EMR集成入口
|
||||||
|
|
||||||
|
**Covers:** 问题#4, #5
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `healthlink-his-ui/src/views/doctorstation/components/emr/emr.vue`
|
||||||
|
|
||||||
|
- [ ] **Step 1: 读取 emr.vue 确认现有结构**
|
||||||
|
|
||||||
|
找到病历详情展示区域,在操作按钮区域添加集成入口。
|
||||||
|
|
||||||
|
- [ ] **Step 2: 添加集成按钮**
|
||||||
|
|
||||||
|
在病历详情弹窗或操作区域添加:
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<!-- 在现有病历操作按钮区域添加 -->
|
||||||
|
<el-button type="info" link @click="viewRevisionHistory">
|
||||||
|
<el-icon><Document /></el-icon> 修订历史
|
||||||
|
</el-button>
|
||||||
|
<el-button type="info" link @click="viewArchiveStatus">
|
||||||
|
<el-icon><Folder /></el-icon> 归档状态
|
||||||
|
</el-button>
|
||||||
|
<el-button type="info" link @click="checkCompleteness">
|
||||||
|
<el-icon><Checked /></el-icon> 完整性检查
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
import { checkCompleteness as checkEmrCompleteness } from '@/api/emr'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
// 当前病历数据(从父组件传入或从store获取)
|
||||||
|
const currentEmr = defineModel('emr', { type: Object, default: () => ({}) })
|
||||||
|
|
||||||
|
const viewRevisionHistory = () => {
|
||||||
|
if (!currentEmr.value?.id) {
|
||||||
|
ElMessage.warning('请先选择病历')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
router.push({
|
||||||
|
path: '/emr/revision-history',
|
||||||
|
query: { emrId: currentEmr.value.id }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const viewArchiveStatus = () => {
|
||||||
|
if (!currentEmr.value?.encounterId) {
|
||||||
|
ElMessage.warning('请先选择病历')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
router.push({
|
||||||
|
path: '/emr/archive',
|
||||||
|
query: {
|
||||||
|
encounterId: currentEmr.value.encounterId,
|
||||||
|
patientName: currentEmr.value.patientName
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const checkCompleteness = async () => {
|
||||||
|
if (!currentEmr.value?.id || !currentEmr.value?.encounterId) {
|
||||||
|
ElMessage.warning('请先选择病历')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const res = await checkEmrCompleteness(currentEmr.value.id, currentEmr.value.encounterId)
|
||||||
|
const data = res.data || res
|
||||||
|
if (data.isComplete) {
|
||||||
|
ElMessage.success('病历完整性检查通过')
|
||||||
|
} else {
|
||||||
|
ElMessage.warning(`病历完整性检查未通过,${data.requiredFailed}项必填项未填写`)
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
ElMessage.error('检查失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 3: 验证编译**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd healthlink-his-ui && npm run build:dev
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 4: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add healthlink-his-ui/src/views/doctorstation/components/emr/emr.vue
|
||||||
|
git commit -m "feat(emr): 医生工作站添加修订历史/归档/完整性检查入口"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 4: 住院医生工作站添加EMR集成入口
|
||||||
|
|
||||||
|
**Covers:** 问题#4, #5
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `healthlink-his-ui/src/views/inpatientDoctor/home/emr/index.vue`
|
||||||
|
|
||||||
|
- [ ] **Step 1: 读取住院EMR页面确认结构**
|
||||||
|
|
||||||
|
- [ ] **Step 2: 添加集成按钮**
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<!-- 在现有病历操作按钮区域添加 -->
|
||||||
|
<el-button type="info" link @click="viewRevisionHistory">
|
||||||
|
修订历史
|
||||||
|
</el-button>
|
||||||
|
<el-button type="info" link @click="viewTimeliness">
|
||||||
|
时效监控
|
||||||
|
</el-button>
|
||||||
|
<el-button type="info" link @click="checkCompleteness">
|
||||||
|
完整性检查
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
import { checkCompleteness as checkEmrCompleteness } from '@/api/emr'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
const currentEmr = defineModel('emr', { type: Object, default: () => ({}) })
|
||||||
|
|
||||||
|
const viewRevisionHistory = () => {
|
||||||
|
if (!currentEmr.value?.id) {
|
||||||
|
ElMessage.warning('请先选择病历')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
router.push({
|
||||||
|
path: '/emr/revision-history',
|
||||||
|
query: { emrId: currentEmr.value.id }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const viewTimeliness = () => {
|
||||||
|
if (!currentEmr.value?.encounterId) {
|
||||||
|
ElMessage.warning('请先选择病历')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
router.push({
|
||||||
|
path: '/emr/timeliness',
|
||||||
|
query: { encounterId: currentEmr.value.encounterId }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const checkCompleteness = async () => {
|
||||||
|
if (!currentEmr.value?.id || !currentEmr.value?.encounterId) {
|
||||||
|
ElMessage.warning('请先选择病历')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const res = await checkEmrCompleteness(currentEmr.value.id, currentEmr.value.encounterId)
|
||||||
|
const data = res.data || res
|
||||||
|
if (data.isComplete) {
|
||||||
|
ElMessage.success('病历完整性检查通过')
|
||||||
|
} else {
|
||||||
|
ElMessage.warning(`病历完整性检查未通过,${data.requiredFailed}项必填项未填写`)
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
ElMessage.error('检查失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 3: 验证编译**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd healthlink-his-ui && npm run build:dev
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 4: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add healthlink-his-ui/src/views/inpatientDoctor/home/emr/index.vue
|
||||||
|
git commit -m "feat(emr): 住院医生工作站添加修订历史/时效/完整性检查入口"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 5: 保存病历时自动触发修订记录
|
||||||
|
|
||||||
|
**Covers:** 问题#2
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `healthlink-his-server/.../doctorstation/appservice/impl/DoctorStationEmrAppServiceImpl.java`
|
||||||
|
|
||||||
|
- [ ] **Step 1: 读取现有保存逻辑**
|
||||||
|
|
||||||
|
找到 `saveEmr` 或类似方法,确认保存流程。
|
||||||
|
|
||||||
|
- [ ] **Step 2: 添加自动触发修订记录**
|
||||||
|
|
||||||
|
在保存EMR成功后添加:
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Resource
|
||||||
|
private IEmrRevisionService emrRevisionService;
|
||||||
|
|
||||||
|
// 在saveEmr方法中,保存成功后添加:
|
||||||
|
// 自动记录修订历史
|
||||||
|
EmrRevision revision = new EmrRevision();
|
||||||
|
revision.setEmrId(savedEmr.getId());
|
||||||
|
revision.setEncounterId(savedEmr.getEncounterId());
|
||||||
|
revision.setOperatorName(operatorName); // 从SecurityUtils获取
|
||||||
|
revision.setOperationType("SAVE");
|
||||||
|
revision.setSnapshotContent(savedEmr.getContextJson());
|
||||||
|
revision.setCreateTime(new Date());
|
||||||
|
emrRevisionService.save(revision);
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 3: 验证编译**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mvn clean compile -DskipTests -pl healthlink-his-application
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 4: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/doctorstation/appservice/impl/DoctorStationEmrAppServiceImpl.java
|
||||||
|
git commit -m "feat(emr): 保存病历时自动创建修订记录"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 6: 保存病历时自动更新搜索索引
|
||||||
|
|
||||||
|
**Covers:** 问题#3
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `healthlink-his-server/.../doctorstation/appservice/impl/DoctorStationEmrAppServiceImpl.java`
|
||||||
|
|
||||||
|
- [ ] **Step 1: 添加搜索索引服务注入**
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Resource
|
||||||
|
private IEmrSearchIndexService emrSearchIndexService;
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: 保存成功后自动更新索引**
|
||||||
|
|
||||||
|
```java
|
||||||
|
// 在saveEmr方法中,保存成功后添加:
|
||||||
|
// 自动更新搜索索引
|
||||||
|
EmrSearchIndex searchIndex = new EmrSearchIndex();
|
||||||
|
searchIndex.setEmrId(savedEmr.getId());
|
||||||
|
searchIndex.setEncounterId(savedEmr.getEncounterId());
|
||||||
|
searchIndex.setPatientName(patientName);
|
||||||
|
searchIndex.setEmrType(emrType);
|
||||||
|
searchIndex.setEmrTitle(title);
|
||||||
|
searchIndex.setDiagnosisText(diagnosis);
|
||||||
|
searchIndex.setDoctorName(doctorName);
|
||||||
|
searchIndex.setDepartmentName(departmentName);
|
||||||
|
searchIndex.setCreateTime(new Date());
|
||||||
|
|
||||||
|
// 检查是否已存在索引
|
||||||
|
LambdaQueryWrapper<EmrSearchIndex> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.eq(EmrSearchIndex::getEmrId, savedEmr.getId());
|
||||||
|
EmrSearchIndex existing = emrSearchIndexService.getOne(wrapper);
|
||||||
|
if (existing != null) {
|
||||||
|
existing.setPatientName(patientName);
|
||||||
|
existing.setEmrType(emrType);
|
||||||
|
existing.setEmrTitle(title);
|
||||||
|
existing.setDiagnosisText(diagnosis);
|
||||||
|
existing.setDoctorName(doctorName);
|
||||||
|
existing.setDepartmentName(departmentName);
|
||||||
|
existing.setUpdateTime(new Date());
|
||||||
|
emrSearchIndexService.updateById(existing);
|
||||||
|
} else {
|
||||||
|
emrSearchIndexService.save(searchIndex);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 3: 验证编译**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mvn clean compile -DskipTests -pl healthlink-his-application
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 4: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/doctorstation/appservice/impl/DoctorStationEmrAppServiceImpl.java
|
||||||
|
git commit -m "feat(emr): 保存病历时自动更新搜索索引"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 7: 全量验证
|
||||||
|
|
||||||
|
**Covers:** 全部问题
|
||||||
|
|
||||||
|
- [ ] **Step 1: 后端编译验证**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mvn clean compile -DskipTests
|
||||||
|
```
|
||||||
|
Expected: BUILD SUCCESS
|
||||||
|
|
||||||
|
- [ ] **Step 2: 前端编译验证**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd healthlink-his-ui && npm run build:dev
|
||||||
|
```
|
||||||
|
Expected: Build successful
|
||||||
|
|
||||||
|
- [ ] **Step 3: 接口测试**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 测试修订历史接口
|
||||||
|
curl http://localhost:18082/emr/revision/page?pageNo=1&pageSize=10
|
||||||
|
|
||||||
|
# 测试搜索接口
|
||||||
|
curl http://localhost:18082/emr-search/search?keyword=test
|
||||||
|
|
||||||
|
# 测试归档接口
|
||||||
|
curl http://localhost:18082/emr-archive/page?pageNo=1&pageSize=10
|
||||||
|
```
|
||||||
|
Expected: 返回 `{code:200, data:...}`
|
||||||
|
|
||||||
|
- [ ] **Step 4: 提交代码**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add -A
|
||||||
|
git commit -m "feat(emr): 打通EMR管理模块与门诊/住院病历集成"
|
||||||
|
git push origin develop
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 验证检查清单
|
||||||
|
|
||||||
|
| 检查项 | 验证方式 | 预期结果 |
|
||||||
|
|--------|---------|---------|
|
||||||
|
| 修订历史API路径 | 访问 `/emr/revision/page` | 返回数据 |
|
||||||
|
| URL参数支持 | 访问 `/emr/revision-history?emrId=1` | 自动加载该病历修订记录 |
|
||||||
|
| 医生工作站入口 | 打开病历详情 | 显示修订历史/归档/完整性检查按钮 |
|
||||||
|
| 保存自动触发 | 保存病历后查询 `emr_revision` 表 | 有新记录 |
|
||||||
|
| 搜索索引更新 | 保存病历后查询 `emr_search_index` 表 | 有新记录 |
|
||||||
|
| 完整性检查 | 点击完整性检查按钮 | 显示检查结果 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
> **Plan Version:** v1.0
|
||||||
|
> **Created:** 2026-06-21
|
||||||
|
> **Estimated Effort:** 2-3小时
|
||||||
95
MD/guides/EMR_SYNC_GUIDE.md
Normal file
95
MD/guides/EMR_SYNC_GUIDE.md
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
# EMR数据同步使用说明
|
||||||
|
|
||||||
|
## 功能概述
|
||||||
|
|
||||||
|
EMR数据同步功能用于将门诊/住院病历表(doc_emr)中的真实数据同步到EMR管理模块的修订历史和搜索索引中。
|
||||||
|
|
||||||
|
## 使用步骤
|
||||||
|
|
||||||
|
### 1. 启动后端应用
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd healthlink-his-server
|
||||||
|
mvn spring-boot:run -pl healthlink-his-application
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 登录系统
|
||||||
|
|
||||||
|
访问 http://localhost:81 登录系统
|
||||||
|
|
||||||
|
### 3. 访问同步页面
|
||||||
|
|
||||||
|
在菜单中找到:**电子病历管理 > EMR数据同步**
|
||||||
|
|
||||||
|
或者直接访问:`http://localhost:81/emr/sync`
|
||||||
|
|
||||||
|
### 4. 执行同步
|
||||||
|
|
||||||
|
1. 查看当前统计信息(病历总数、修订历史、搜索索引)
|
||||||
|
2. 点击"开始同步"按钮
|
||||||
|
3. 确认同步操作
|
||||||
|
4. 等待同步完成
|
||||||
|
5. 查看同步后的统计信息
|
||||||
|
|
||||||
|
## API接口
|
||||||
|
|
||||||
|
### 获取同步统计
|
||||||
|
|
||||||
|
```
|
||||||
|
GET /emr-sync/stats
|
||||||
|
```
|
||||||
|
|
||||||
|
返回:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"data": {
|
||||||
|
"emrCount": 100,
|
||||||
|
"revisionCount": 100,
|
||||||
|
"searchIndexCount": 100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 执行同步
|
||||||
|
|
||||||
|
```
|
||||||
|
POST /emr-sync/sync
|
||||||
|
```
|
||||||
|
|
||||||
|
返回:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"data": "同步完成: 修订历史100条, 搜索索引100条"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 数据流向
|
||||||
|
|
||||||
|
```
|
||||||
|
doc_emr (门诊/住院病历)
|
||||||
|
↓ 同步
|
||||||
|
emr_revision (修订历史)
|
||||||
|
emr_search_index (搜索索引)
|
||||||
|
↓ 展示
|
||||||
|
EMR管理页面(修订历史、病历检索等)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
1. **同步会清空现有数据**:执行同步前会清空emr_revision和emr_search_index表
|
||||||
|
2. **建议先备份**:如果表中有重要数据,建议先备份
|
||||||
|
3. **同步后刷新页面**:同步完成后需要刷新页面才能看到新数据
|
||||||
|
4. **权限要求**:需要管理员权限才能执行同步操作
|
||||||
|
|
||||||
|
## 常见问题
|
||||||
|
|
||||||
|
### Q: 同步后数据没有显示?
|
||||||
|
A: 请刷新页面,或检查浏览器控制台是否有错误
|
||||||
|
|
||||||
|
### Q: 同步失败怎么办?
|
||||||
|
A: 检查后端日志,确认数据库连接正常
|
||||||
|
|
||||||
|
### Q: 可以只同步部分数据吗?
|
||||||
|
A: 当前版本不支持部分同步,会同步所有doc_emr中的数据
|
||||||
111
MD/guides/YB_MOCK_GUIDE.md
Normal file
111
MD/guides/YB_MOCK_GUIDE.md
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
# 医保模拟接口使用说明
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
本项目提供了一个医保模拟服务器(`YbMockController`),用于在本地测试医保接口功能,无需连接真实的医保系统。
|
||||||
|
|
||||||
|
## 模拟的接口
|
||||||
|
|
||||||
|
| 接口代码 | 功能 | 请求示例 |
|
||||||
|
|---------|------|---------|
|
||||||
|
| 1101 | 获取参保人信息 | `{"psn_no":"P1234567890"}` |
|
||||||
|
| 2201 | 门诊登记 | `{"psn_no":"P1234567890","org_code":"H22010402403"}` |
|
||||||
|
| 2203 | 门诊处方上传 | `{"psn_no":"P1234567890","encounter_no":"MZ20260623001"}` |
|
||||||
|
| 2207 | 门诊结算 | `{"psn_no":"P1234567890","encounter_no":"MZ20260623001"}` |
|
||||||
|
| 3201 | 住院登记 | `{"psn_no":"P1234567890","org_code":"H22010402403"}` |
|
||||||
|
| 3203 | 住院处方上传 | `{"psn_no":"P1234567890","encounter_no":"ZY20260623001"}` |
|
||||||
|
| 3207 | 住院结算 | `{"psn_no":"P1234567890","encounter_no":"ZY20260623001"}` |
|
||||||
|
|
||||||
|
## 使用方法
|
||||||
|
|
||||||
|
### 1. 启动应用
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd healthlink-his-server
|
||||||
|
mvn spring-boot:run -pl healthlink-his-application
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 测试接口
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 测试获取参保人信息
|
||||||
|
curl -X POST http://localhost:18080/healthlink-his/yb/mock/1101 \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"psn_no":"P1234567890"}'
|
||||||
|
|
||||||
|
# 或使用测试脚本
|
||||||
|
chmod +x scripts/test-yb-mock.sh
|
||||||
|
./scripts/test-yb-mock.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 配置医保接口地址
|
||||||
|
|
||||||
|
在 `application-dev.yml` 中配置医保接口地址:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
ybapp:
|
||||||
|
config:
|
||||||
|
url: http://localhost:18080/healthlink-his/yb/mock
|
||||||
|
```
|
||||||
|
|
||||||
|
## 模拟数据
|
||||||
|
|
||||||
|
### 参保人信息 (1101)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"psn_no": "P1234567890",
|
||||||
|
"psn_name": "张三",
|
||||||
|
"sex_code": "1",
|
||||||
|
"sex_name": "男",
|
||||||
|
"birth_date": "1980-01-15",
|
||||||
|
"id_card": "450123198001151234",
|
||||||
|
"insur_type": "职工基本医疗保险",
|
||||||
|
"insur_area": "南宁市",
|
||||||
|
"card_no": "C2024000123456",
|
||||||
|
"balance": "12580.50",
|
||||||
|
"status": "正常"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 门诊结算 (2207)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"settle_no": "JZ20260623001",
|
||||||
|
"total_amount": "156.80",
|
||||||
|
"insurance_pay": "133.28",
|
||||||
|
"self_pay": "23.52",
|
||||||
|
"account_pay": "20.00",
|
||||||
|
"cash_pay": "3.52",
|
||||||
|
"settle_time": "2026-06-23 10:30:00",
|
||||||
|
"status": "成功"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 住院结算 (3207)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"settle_no": "ZYJS20260623001",
|
||||||
|
"total_amount": "15680.50",
|
||||||
|
"insurance_pay": "14112.45",
|
||||||
|
"self_pay": "1568.05",
|
||||||
|
"account_pay": "1200.00",
|
||||||
|
"cash_pay": "368.05",
|
||||||
|
"settle_time": "2026-06-23 10:30:00",
|
||||||
|
"status": "成功"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
1. 模拟服务器仅用于本地测试,不模拟真实的医保业务逻辑
|
||||||
|
2. 返回的数据是固定的测试数据,不会根据请求参数变化
|
||||||
|
3. 生产环境请连接真实的医保接口
|
||||||
|
4. 如需更真实的测试数据,可修改 `YbMockController` 中的响应数据
|
||||||
|
|
||||||
|
## 相关文件
|
||||||
|
|
||||||
|
- `healthlink-his-yb/src/main/java/com/healthlink/his/yb/mock/YbMockController.java`
|
||||||
|
- `scripts/test-yb-mock.sh`
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
package com.healthlink.his.web.anesthesia.controller;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.core.common.core.domain.R;
|
||||||
|
import com.healthlink.his.anesthesia.domain.AnesthesiaFollowup;
|
||||||
|
import com.healthlink.his.anesthesia.domain.AnesthesiaQualityControl;
|
||||||
|
import com.healthlink.his.anesthesia.domain.AnesthesiaSpecimen;
|
||||||
|
import com.healthlink.his.anesthesia.service.IAnesthesiaFollowupService;
|
||||||
|
import com.healthlink.his.anesthesia.service.IAnesthesiaQualityControlService;
|
||||||
|
import com.healthlink.his.anesthesia.service.IAnesthesiaSpecimenService;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/anesthesia-enhanced")
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Tag(name = "麻醉扩展-标本/随访/质控")
|
||||||
|
public class AnesthesiaEnhancedCrudController {
|
||||||
|
|
||||||
|
private final IAnesthesiaSpecimenService specimenService;
|
||||||
|
private final IAnesthesiaFollowupService followupService;
|
||||||
|
private final IAnesthesiaQualityControlService qcService;
|
||||||
|
|
||||||
|
@GetMapping("/specimen/page")
|
||||||
|
@Operation(summary = "标本分页")
|
||||||
|
public R<?> getSpecimenPage(
|
||||||
|
@RequestParam(defaultValue = "1") Integer pageNo,
|
||||||
|
@RequestParam(defaultValue = "10") Integer pageSize,
|
||||||
|
@RequestParam(required = false) String patientName) {
|
||||||
|
LambdaQueryWrapper<AnesthesiaSpecimen> w = new LambdaQueryWrapper<>();
|
||||||
|
w.like(patientName != null, AnesthesiaSpecimen::getPatientName, patientName)
|
||||||
|
.orderByDesc(AnesthesiaSpecimen::getCreateTime);
|
||||||
|
return R.ok(specimenService.page(new Page<>(pageNo, pageSize), w));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/specimen/add")
|
||||||
|
@Operation(summary = "新增标本")
|
||||||
|
public R<?> addSpecimen(@RequestBody AnesthesiaSpecimen specimen) {
|
||||||
|
specimen.setCreateTime(new Date());
|
||||||
|
specimenService.save(specimen);
|
||||||
|
return R.ok(specimen);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/specimen/report")
|
||||||
|
@Operation(summary = "报告标本结果")
|
||||||
|
public R<?> reportSpecimen(@RequestBody AnesthesiaSpecimen specimen) {
|
||||||
|
specimen.setReportTime(new Date());
|
||||||
|
specimenService.updateById(specimen);
|
||||||
|
return R.ok(specimen);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/followup/page")
|
||||||
|
@Operation(summary = "随访分页")
|
||||||
|
public R<?> getFollowupPage(
|
||||||
|
@RequestParam(defaultValue = "1") Integer pageNo,
|
||||||
|
@RequestParam(defaultValue = "10") Integer pageSize) {
|
||||||
|
LambdaQueryWrapper<AnesthesiaFollowup> w = new LambdaQueryWrapper<>();
|
||||||
|
w.orderByDesc(AnesthesiaFollowup::getFollowupDate);
|
||||||
|
return R.ok(followupService.page(new Page<>(pageNo, pageSize), w));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/followup/add")
|
||||||
|
@Operation(summary = "新增随访")
|
||||||
|
public R<?> addFollowup(@RequestBody AnesthesiaFollowup followup) {
|
||||||
|
followup.setCreateTime(new Date());
|
||||||
|
followupService.save(followup);
|
||||||
|
return R.ok(followup);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/qc/page")
|
||||||
|
@Operation(summary = "质控分页")
|
||||||
|
public R<?> getQcPage(
|
||||||
|
@RequestParam(defaultValue = "1") Integer pageNo,
|
||||||
|
@RequestParam(defaultValue = "10") Integer pageSize) {
|
||||||
|
LambdaQueryWrapper<AnesthesiaQualityControl> w = new LambdaQueryWrapper<>();
|
||||||
|
w.orderByDesc(AnesthesiaQualityControl::getCreateTime);
|
||||||
|
return R.ok(qcService.page(new Page<>(pageNo, pageSize), w));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/qc/add")
|
||||||
|
@Operation(summary = "新增质控记录")
|
||||||
|
public R<?> addQc(@RequestBody AnesthesiaQualityControl qc) {
|
||||||
|
qc.setCreateTime(new Date());
|
||||||
|
qcService.save(qc);
|
||||||
|
return R.ok(qc);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/qc/stats")
|
||||||
|
@Operation(summary = "质控统计")
|
||||||
|
public R<?> getQcStats() {
|
||||||
|
long total = qcService.count();
|
||||||
|
long normal = qcService.count(new LambdaQueryWrapper<AnesthesiaQualityControl>()
|
||||||
|
.eq(AnesthesiaQualityControl::getRiskLevel, "NORMAL"));
|
||||||
|
long warning = qcService.count(new LambdaQueryWrapper<AnesthesiaQualityControl>()
|
||||||
|
.eq(AnesthesiaQualityControl::getRiskLevel, "WARNING"));
|
||||||
|
long critical = qcService.count(new LambdaQueryWrapper<AnesthesiaQualityControl>()
|
||||||
|
.eq(AnesthesiaQualityControl::getRiskLevel, "CRITICAL"));
|
||||||
|
return R.ok(Map.of("total", total, "normal", normal, "warning", warning, "critical", critical));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -532,11 +532,22 @@ public class OutpatientRegistrationAppServiceImpl implements IOutpatientRegistra
|
|||||||
String registerTimeSTime = request.getParameter("registerTimeSTime");
|
String registerTimeSTime = request.getParameter("registerTimeSTime");
|
||||||
String registerTimeETime = request.getParameter("registerTimeETime");
|
String registerTimeETime = request.getParameter("registerTimeETime");
|
||||||
|
|
||||||
|
// Bug #638:提取可选科室过滤参数
|
||||||
|
Long deptId = null;
|
||||||
|
String deptIdParam = request.getParameter("deptId");
|
||||||
|
if (deptIdParam != null && !deptIdParam.isEmpty()) {
|
||||||
|
try {
|
||||||
|
deptId = Long.parseLong(deptIdParam);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
// 忽略无效的参数值
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
IPage<CurrentDayEncounterDto> currentDayEncounter = outpatientRegistrationAppMapper.getCurrentDayEncounter(
|
IPage<CurrentDayEncounterDto> currentDayEncounter = outpatientRegistrationAppMapper.getCurrentDayEncounter(
|
||||||
new Page<>(pageNo, pageSize), EncounterClass.AMB.getValue(), EncounterStatus.IN_PROGRESS.getValue(),
|
new Page<>(pageNo, pageSize), EncounterClass.AMB.getValue(), EncounterStatus.IN_PROGRESS.getValue(),
|
||||||
ParticipantType.ADMITTER.getCode(), ParticipantType.REGISTRATION_DOCTOR.getCode(), queryWrapper,
|
ParticipantType.ADMITTER.getCode(), ParticipantType.REGISTRATION_DOCTOR.getCode(), queryWrapper,
|
||||||
ChargeItemContext.REGISTER.getValue(), PaymentStatus.SUCCESS.getValue(),
|
ChargeItemContext.REGISTER.getValue(), PaymentStatus.SUCCESS.getValue(),
|
||||||
registerTimeSTime, registerTimeETime, statusFilter);
|
registerTimeSTime, registerTimeETime, statusFilter, deptId);
|
||||||
|
|
||||||
// 过滤候选池排除列表
|
// 过滤候选池排除列表
|
||||||
// 仅当调用方显式传 excludeFromCandidatePool=true 时才过滤,避免非分诊场景(挂号/收费)
|
// 仅当调用方显式传 excludeFromCandidatePool=true 时才过滤,避免非分诊场景(挂号/收费)
|
||||||
|
|||||||
@@ -57,7 +57,8 @@ public interface OutpatientRegistrationAppMapper {
|
|||||||
@Param("register") Integer register, @Param("paymentStatus") Integer paymentStatus,
|
@Param("register") Integer register, @Param("paymentStatus") Integer paymentStatus,
|
||||||
@Param("registerTimeSTime") String registerTimeSTime,
|
@Param("registerTimeSTime") String registerTimeSTime,
|
||||||
@Param("registerTimeETime") String registerTimeETime,
|
@Param("registerTimeETime") String registerTimeETime,
|
||||||
@Param("statusFilter") Integer statusFilter);
|
@Param("statusFilter") Integer statusFilter,
|
||||||
|
@Param("deptId") Long deptId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询item绑定的信息(耗材或诊疗)
|
* 查询item绑定的信息(耗材或诊疗)
|
||||||
|
|||||||
@@ -224,6 +224,12 @@ public class RequestBaseDto {
|
|||||||
*/
|
*/
|
||||||
private Integer sortNumber;
|
private Integer sortNumber;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 账户id (费用性质/合同)
|
||||||
|
*/
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
private Long accountId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用药说明
|
* 用药说明
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import com.healthlink.his.document.service.IProgressNoteReminderService;
|
|||||||
import com.healthlink.his.document.service.IProgressNoteService;
|
import com.healthlink.his.document.service.IProgressNoteService;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
@@ -52,7 +51,6 @@ public class ProgressNoteController {
|
|||||||
* 分页查询病程记录列表
|
* 分页查询病程记录列表
|
||||||
*/
|
*/
|
||||||
@GetMapping("/page")
|
@GetMapping("/page")
|
||||||
@PreAuthorize("hasAuthority('document:progressnote:list') or hasAuthority('emr:list')")
|
|
||||||
public R<?> getPage(
|
public R<?> getPage(
|
||||||
@RequestParam(value = "patientName", required = false) String patientName,
|
@RequestParam(value = "patientName", required = false) String patientName,
|
||||||
@RequestParam(value = "noteType", required = false) Integer noteType,
|
@RequestParam(value = "noteType", required = false) Integer noteType,
|
||||||
@@ -75,7 +73,6 @@ public class ProgressNoteController {
|
|||||||
* 查询病程记录详情
|
* 查询病程记录详情
|
||||||
*/
|
*/
|
||||||
@GetMapping("/detail")
|
@GetMapping("/detail")
|
||||||
@PreAuthorize("hasAuthority('document:progressnote:list') or hasAuthority('emr:list')")
|
|
||||||
public R<?> getDetail(@RequestParam Long id) {
|
public R<?> getDetail(@RequestParam Long id) {
|
||||||
ProgressNote note = progressNoteService.getById(id);
|
ProgressNote note = progressNoteService.getById(id);
|
||||||
if (note == null) return R.fail("病程记录不存在");
|
if (note == null) return R.fail("病程记录不存在");
|
||||||
@@ -86,7 +83,6 @@ public class ProgressNoteController {
|
|||||||
* 新增病程记录
|
* 新增病程记录
|
||||||
*/
|
*/
|
||||||
@PostMapping("/add")
|
@PostMapping("/add")
|
||||||
@PreAuthorize("hasAuthority('document:progressnote:add') or hasAuthority('emr:edit')")
|
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public R<?> add(@RequestBody ProgressNote note) {
|
public R<?> add(@RequestBody ProgressNote note) {
|
||||||
note.setSignStatus(0);
|
note.setSignStatus(0);
|
||||||
@@ -108,7 +104,6 @@ public class ProgressNoteController {
|
|||||||
* 修改病程记录(仅未签名可修改)
|
* 修改病程记录(仅未签名可修改)
|
||||||
*/
|
*/
|
||||||
@PutMapping("/update")
|
@PutMapping("/update")
|
||||||
@PreAuthorize("hasAuthority('document:progressnote:edit') or hasAuthority('emr:edit')")
|
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public R<?> update(@RequestBody ProgressNote note) {
|
public R<?> update(@RequestBody ProgressNote note) {
|
||||||
ProgressNote existing = progressNoteService.getById(note.getId());
|
ProgressNote existing = progressNoteService.getById(note.getId());
|
||||||
@@ -124,7 +119,6 @@ public class ProgressNoteController {
|
|||||||
* 删除病程记录(仅未签名可删除)
|
* 删除病程记录(仅未签名可删除)
|
||||||
*/
|
*/
|
||||||
@DeleteMapping("/delete")
|
@DeleteMapping("/delete")
|
||||||
@PreAuthorize("hasAuthority('document:progressnote:remove') or hasAuthority('emr:edit')")
|
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public R<?> delete(@RequestParam Long id) {
|
public R<?> delete(@RequestParam Long id) {
|
||||||
ProgressNote note = progressNoteService.getById(id);
|
ProgressNote note = progressNoteService.getById(id);
|
||||||
@@ -138,7 +132,6 @@ public class ProgressNoteController {
|
|||||||
* 签名病程记录
|
* 签名病程记录
|
||||||
*/
|
*/
|
||||||
@PostMapping("/sign")
|
@PostMapping("/sign")
|
||||||
@PreAuthorize("hasAuthority('document:progressnote:edit') or hasAuthority('emr:edit')")
|
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public R<?> sign(@RequestBody Map<String, Object> params) {
|
public R<?> sign(@RequestBody Map<String, Object> params) {
|
||||||
Long id = Long.valueOf(params.get("id").toString());
|
Long id = Long.valueOf(params.get("id").toString());
|
||||||
@@ -158,7 +151,6 @@ public class ProgressNoteController {
|
|||||||
* 审核病程记录(上级医师)
|
* 审核病程记录(上级医师)
|
||||||
*/
|
*/
|
||||||
@PostMapping("/review")
|
@PostMapping("/review")
|
||||||
@PreAuthorize("hasAuthority('document:progressnote:edit') or hasAuthority('emr:edit')")
|
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public R<?> review(@RequestBody Map<String, Object> params) {
|
public R<?> review(@RequestBody Map<String, Object> params) {
|
||||||
Long id = Long.valueOf(params.get("id").toString());
|
Long id = Long.valueOf(params.get("id").toString());
|
||||||
@@ -177,7 +169,6 @@ public class ProgressNoteController {
|
|||||||
* 获取时限监控面板
|
* 获取时限监控面板
|
||||||
*/
|
*/
|
||||||
@GetMapping("/monitor")
|
@GetMapping("/monitor")
|
||||||
@PreAuthorize("hasAuthority('document:progressnote:list') or hasAuthority('emr:list')")
|
|
||||||
public R<?> getMonitor(@RequestParam(required = false) Long encounterId) {
|
public R<?> getMonitor(@RequestParam(required = false) Long encounterId) {
|
||||||
Map<String, Object> result = new HashMap<>();
|
Map<String, Object> result = new HashMap<>();
|
||||||
Date now = new Date();
|
Date now = new Date();
|
||||||
@@ -225,7 +216,6 @@ public class ProgressNoteController {
|
|||||||
* 获取提醒列表
|
* 获取提醒列表
|
||||||
*/
|
*/
|
||||||
@GetMapping("/reminders")
|
@GetMapping("/reminders")
|
||||||
@PreAuthorize("hasAuthority('document:progressnote:list') or hasAuthority('emr:list')")
|
|
||||||
public R<?> getReminders(
|
public R<?> getReminders(
|
||||||
@RequestParam(value = "status", required = false) Integer status,
|
@RequestParam(value = "status", required = false) Integer status,
|
||||||
@RequestParam(value = "encounterId", required = false) Long encounterId) {
|
@RequestParam(value = "encounterId", required = false) Long encounterId) {
|
||||||
@@ -240,7 +230,6 @@ public class ProgressNoteController {
|
|||||||
* 获取病程记录统计
|
* 获取病程记录统计
|
||||||
*/
|
*/
|
||||||
@GetMapping("/stats")
|
@GetMapping("/stats")
|
||||||
@PreAuthorize("hasAuthority('document:progressnote:list') or hasAuthority('emr:list')")
|
|
||||||
public R<?> getStats(@RequestParam Long encounterId) {
|
public R<?> getStats(@RequestParam Long encounterId) {
|
||||||
Map<String, Object> stats = new HashMap<>();
|
Map<String, Object> stats = new HashMap<>();
|
||||||
LambdaQueryWrapper<ProgressNote> wrapper = new LambdaQueryWrapper<>();
|
LambdaQueryWrapper<ProgressNote> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
|||||||
@@ -10,5 +10,7 @@ public interface IEmrTimelinessAppService {
|
|||||||
|
|
||||||
EmrTimelinessStatisticsDto checkTimeliness(Long encounterId);
|
EmrTimelinessStatisticsDto checkTimeliness(Long encounterId);
|
||||||
|
|
||||||
|
EmrTimelinessStatisticsDto getStatistics();
|
||||||
|
|
||||||
Map<String, Object> getTimelinessAlerts(String emrType, String status, String departmentName, int pageNum, int pageSize);
|
Map<String, Object> getTimelinessAlerts(String emrType, String status, String departmentName, int pageNum, int pageSize);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,6 +66,22 @@ public class EmrTimelinessAppServiceImpl implements IEmrTimelinessAppService {
|
|||||||
return stats;
|
return stats;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EmrTimelinessStatisticsDto getStatistics() {
|
||||||
|
long total = emrTimelinessService.count();
|
||||||
|
long completed = emrTimelinessService.count(new LambdaQueryWrapper<EmrTimeliness>().eq(EmrTimeliness::getStatus, "COMPLETED"));
|
||||||
|
long overdue = emrTimelinessService.count(new LambdaQueryWrapper<EmrTimeliness>().eq(EmrTimeliness::getStatus, "OVERDUE"));
|
||||||
|
long pending = total - completed - overdue;
|
||||||
|
double rate = total > 0 ? Math.round(completed * 10000.0 / total) / 100.0 : 0;
|
||||||
|
|
||||||
|
return new EmrTimelinessStatisticsDto()
|
||||||
|
.setTotalCount(total)
|
||||||
|
.setCompletedCount(completed)
|
||||||
|
.setOverdueCount(overdue)
|
||||||
|
.setPendingCount(pending)
|
||||||
|
.setCompletionRate(rate);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, Object> getTimelinessAlerts(String emrType, String status, String departmentName, int pageNum, int pageSize) {
|
public Map<String, Object> getTimelinessAlerts(String emrType, String status, String departmentName, int pageNum, int pageSize) {
|
||||||
LambdaQueryWrapper<EmrTimeliness> wrapper = new LambdaQueryWrapper<>();
|
LambdaQueryWrapper<EmrTimeliness> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
|||||||
@@ -4,13 +4,14 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.core.common.core.domain.R;
|
import com.core.common.core.domain.R;
|
||||||
import com.healthlink.his.emr.domain.EmrRevision;
|
import com.healthlink.his.emr.domain.EmrRevision;
|
||||||
|
import com.healthlink.his.emr.dto.EmrRevisionWithPatientDto;
|
||||||
|
import com.healthlink.his.emr.mapper.EmrRevisionMapper;
|
||||||
import com.healthlink.his.emr.service.IEmrRevisionService;
|
import com.healthlink.his.emr.service.IEmrRevisionService;
|
||||||
import com.healthlink.his.web.emr.appservice.IEmrRevisionAppService;
|
import com.healthlink.his.web.emr.appservice.IEmrRevisionAppService;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -29,22 +30,21 @@ public class EmrRevisionController {
|
|||||||
|
|
||||||
private final IEmrRevisionAppService emrRevisionAppService;
|
private final IEmrRevisionAppService emrRevisionAppService;
|
||||||
|
|
||||||
|
private final EmrRevisionMapper emrRevisionMapper;
|
||||||
|
|
||||||
@PostMapping("/record")
|
@PostMapping("/record")
|
||||||
@PreAuthorize("@ss.hasPermi('emr:edit')")
|
|
||||||
@Operation(summary = "记录修改留痕")
|
@Operation(summary = "记录修改留痕")
|
||||||
public R<EmrRevision> recordRevision(@RequestBody EmrRevision revision) {
|
public R<EmrRevision> recordRevision(@RequestBody EmrRevision revision) {
|
||||||
return R.ok(emrRevisionAppService.recordRevision(revision));
|
return R.ok(emrRevisionAppService.recordRevision(revision));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/list/{emrId}")
|
@GetMapping("/list/{emrId}")
|
||||||
@PreAuthorize("@ss.hasPermi('emr:list')")
|
|
||||||
@Operation(summary = "获取修改历史列表")
|
@Operation(summary = "获取修改历史列表")
|
||||||
public R<?> getRevisions(@PathVariable Long emrId) {
|
public R<?> getRevisions(@PathVariable Long emrId) {
|
||||||
return R.ok(emrRevisionAppService.getRevisions(emrId));
|
return R.ok(emrRevisionAppService.getRevisions(emrId));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/page")
|
@GetMapping("/page")
|
||||||
@PreAuthorize("@ss.hasPermi('emr:list')")
|
|
||||||
@Operation(summary = "分页查询修改留痕")
|
@Operation(summary = "分页查询修改留痕")
|
||||||
public R<?> getPage(
|
public R<?> getPage(
|
||||||
@RequestParam(value = "emrId", required = false) Long emrId,
|
@RequestParam(value = "emrId", required = false) Long emrId,
|
||||||
@@ -60,15 +60,35 @@ public class EmrRevisionController {
|
|||||||
return R.ok(revisionService.page(new Page<>(pageNo, pageSize), w));
|
return R.ok(revisionService.page(new Page<>(pageNo, pageSize), w));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/{id}")
|
@GetMapping("/page-with-patient")
|
||||||
@PreAuthorize("@ss.hasPermi('emr:list')")
|
@Operation(summary = "分页查询修改留痕(含患者信息)")
|
||||||
|
public R<?> getPageWithPatient(
|
||||||
|
@RequestParam(value = "emrId", required = false) Long emrId,
|
||||||
|
@RequestParam(value = "operatorName", required = false) String operatorName,
|
||||||
|
@RequestParam(value = "patientName", required = false) String patientName,
|
||||||
|
@RequestParam(value = "doctorName", required = false) String doctorName,
|
||||||
|
@RequestParam(value = "emrType", required = false) String emrType,
|
||||||
|
@RequestParam(value = "pageNo", defaultValue = "1") Integer pageNo,
|
||||||
|
@RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize) {
|
||||||
|
int offset = (pageNo - 1) * pageSize;
|
||||||
|
long total = emrRevisionMapper.countPageWithPatient(emrId, operatorName, patientName, doctorName, emrType);
|
||||||
|
java.util.List<EmrRevisionWithPatientDto> list = emrRevisionMapper.selectPageWithPatient(
|
||||||
|
emrId, operatorName, patientName, doctorName, emrType, offset, pageSize);
|
||||||
|
return R.ok(new java.util.HashMap<String, Object>() {{
|
||||||
|
put("records", list);
|
||||||
|
put("total", total);
|
||||||
|
put("pageNo", pageNo);
|
||||||
|
put("pageSize", pageSize);
|
||||||
|
}});
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{id:\\d+}")
|
||||||
@Operation(summary = "获取修订详情")
|
@Operation(summary = "获取修订详情")
|
||||||
public R<?> getById(@PathVariable Long id) {
|
public R<?> getById(@PathVariable Long id) {
|
||||||
return R.ok(emrRevisionAppService.getRevisionDetail(id));
|
return R.ok(emrRevisionAppService.getRevisionDetail(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/compare")
|
@GetMapping("/compare")
|
||||||
@PreAuthorize("@ss.hasPermi('emr:list')")
|
|
||||||
@Operation(summary = "对比两个修订版本")
|
@Operation(summary = "对比两个修订版本")
|
||||||
public R<?> compareRevisions(
|
public R<?> compareRevisions(
|
||||||
@RequestParam("revisionId1") Long id1,
|
@RequestParam("revisionId1") Long id1,
|
||||||
|
|||||||
@@ -10,8 +10,10 @@ import com.healthlink.his.administration.mapper.EncounterMapper;
|
|||||||
import com.healthlink.his.administration.mapper.PatientMapper;
|
import com.healthlink.his.administration.mapper.PatientMapper;
|
||||||
import com.healthlink.his.document.domain.Emr;
|
import com.healthlink.his.document.domain.Emr;
|
||||||
import com.healthlink.his.document.service.IEmrService;
|
import com.healthlink.his.document.service.IEmrService;
|
||||||
|
import com.healthlink.his.emr.domain.EmrArchiveRecord;
|
||||||
import com.healthlink.his.emr.domain.EmrRevision;
|
import com.healthlink.his.emr.domain.EmrRevision;
|
||||||
import com.healthlink.his.emr.domain.EmrSearchIndex;
|
import com.healthlink.his.emr.domain.EmrSearchIndex;
|
||||||
|
import com.healthlink.his.emr.service.IEmrArchiveRecordService;
|
||||||
import com.healthlink.his.emr.service.IEmrRevisionService;
|
import com.healthlink.his.emr.service.IEmrRevisionService;
|
||||||
import com.healthlink.his.emr.service.IEmrSearchIndexService;
|
import com.healthlink.his.emr.service.IEmrSearchIndexService;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
@@ -36,6 +38,7 @@ public class EmrSyncController {
|
|||||||
private final IEmrService emrService;
|
private final IEmrService emrService;
|
||||||
private final IEmrRevisionService emrRevisionService;
|
private final IEmrRevisionService emrRevisionService;
|
||||||
private final IEmrSearchIndexService emrSearchIndexService;
|
private final IEmrSearchIndexService emrSearchIndexService;
|
||||||
|
private final IEmrArchiveRecordService emrArchiveRecordService;
|
||||||
private final JdbcTemplate jdbcTemplate;
|
private final JdbcTemplate jdbcTemplate;
|
||||||
private final PatientMapper patientMapper;
|
private final PatientMapper patientMapper;
|
||||||
private final EncounterMapper encounterMapper;
|
private final EncounterMapper encounterMapper;
|
||||||
@@ -54,7 +57,8 @@ public class EmrSyncController {
|
|||||||
try {
|
try {
|
||||||
jdbcTemplate.execute("TRUNCATE TABLE emr_revision CASCADE");
|
jdbcTemplate.execute("TRUNCATE TABLE emr_revision CASCADE");
|
||||||
jdbcTemplate.execute("TRUNCATE TABLE emr_search_index CASCADE");
|
jdbcTemplate.execute("TRUNCATE TABLE emr_search_index CASCADE");
|
||||||
log.info("已清空emr_revision和emr_search_index表");
|
jdbcTemplate.execute("TRUNCATE TABLE emr_archive_record CASCADE");
|
||||||
|
log.info("已清空emr_revision、emr_search_index和emr_archive_record表");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.warn("TRUNCATE失败,尝试使用DELETE: {}", e.getMessage());
|
log.warn("TRUNCATE失败,尝试使用DELETE: {}", e.getMessage());
|
||||||
// 备用方案:查询所有ID后删除
|
// 备用方案:查询所有ID后删除
|
||||||
@@ -68,6 +72,11 @@ public class EmrSyncController {
|
|||||||
if (!searchIndexIds.isEmpty()) {
|
if (!searchIndexIds.isEmpty()) {
|
||||||
emrSearchIndexService.removeByIds(searchIndexIds);
|
emrSearchIndexService.removeByIds(searchIndexIds);
|
||||||
}
|
}
|
||||||
|
List<Long> archiveIds = emrArchiveRecordService.list(new LambdaQueryWrapper<EmrArchiveRecord>().select(EmrArchiveRecord::getId))
|
||||||
|
.stream().map(EmrArchiveRecord::getId).toList();
|
||||||
|
if (!archiveIds.isEmpty()) {
|
||||||
|
emrArchiveRecordService.removeByIds(archiveIds);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 从doc_emr获取所有病历
|
// 2. 从doc_emr获取所有病历
|
||||||
@@ -78,8 +87,18 @@ public class EmrSyncController {
|
|||||||
return R.ok("没有病历数据需要同步");
|
return R.ok("没有病历数据需要同步");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.info("共找到 {} 条病历数据", emrList.size());
|
||||||
|
|
||||||
|
// 调试:打印前3条数据的字段值
|
||||||
|
for (int i = 0; i < Math.min(3, emrList.size()); i++) {
|
||||||
|
Emr emr = emrList.get(i);
|
||||||
|
log.info("病历[{}]: id={}, patientId={}, encounterId={}, recordId={}, classEnum={}",
|
||||||
|
i, emr.getId(), emr.getPatientId(), emr.getEncounterId(), emr.getRecordId(), emr.getClassEnum());
|
||||||
|
}
|
||||||
|
|
||||||
int revisionCount = 0;
|
int revisionCount = 0;
|
||||||
int searchIndexCount = 0;
|
int searchIndexCount = 0;
|
||||||
|
int archiveCount = 0;
|
||||||
|
|
||||||
for (Emr emr : emrList) {
|
for (Emr emr : emrList) {
|
||||||
// 3. 创建修订历史
|
// 3. 创建修订历史
|
||||||
@@ -107,35 +126,58 @@ public class EmrSyncController {
|
|||||||
String diagnosis = contentMap.getOrDefault("diagnosis", "");
|
String diagnosis = contentMap.getOrDefault("diagnosis", "");
|
||||||
|
|
||||||
// 获取患者详细信息
|
// 获取患者详细信息
|
||||||
Patient patient = patientMapper.selectById(emr.getPatientId());
|
Patient patient = null;
|
||||||
String patientName = patient != null ? patient.getName() : "未知";
|
String patientName = "未知";
|
||||||
String patientGender = "";
|
String patientGender = "";
|
||||||
String patientAge = "";
|
String patientAge = "";
|
||||||
String patientPhone = "";
|
String patientPhone = "";
|
||||||
String patientIdCard = "";
|
String patientIdCard = "";
|
||||||
if (patient != null) {
|
String encounterNo = "";
|
||||||
// 性别
|
|
||||||
if (patient.getGenderEnum() != null) {
|
if (emr.getPatientId() != null) {
|
||||||
patientGender = patient.getGenderEnum() == 1 ? "男" : "女";
|
patient = patientMapper.selectById(emr.getPatientId());
|
||||||
|
if (patient != null) {
|
||||||
|
patientName = patient.getName() != null ? patient.getName() : "未知";
|
||||||
|
// 性别
|
||||||
|
if (patient.getGenderEnum() != null) {
|
||||||
|
patientGender = patient.getGenderEnum() == 1 ? "男" : "女";
|
||||||
|
}
|
||||||
|
// 年龄
|
||||||
|
if (patient.getBirthDate() != null) {
|
||||||
|
try {
|
||||||
|
int age = java.time.Period.between(
|
||||||
|
patient.getBirthDate().toInstant().atZone(java.time.ZoneId.systemDefault()).toLocalDate(),
|
||||||
|
java.time.LocalDate.now()
|
||||||
|
).getYears();
|
||||||
|
patientAge = String.valueOf(age);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("计算年龄失败: patientId={}", emr.getPatientId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
patientPhone = patient.getPhone() != null ? patient.getPhone() : "";
|
||||||
|
patientIdCard = patient.getIdCard() != null ? patient.getIdCard() : "";
|
||||||
|
log.debug("患者信息: name={}, gender={}, age={}, phone={}", patientName, patientGender, patientAge, patientPhone);
|
||||||
|
} else {
|
||||||
|
log.warn("未找到患者: patientId={}", emr.getPatientId());
|
||||||
}
|
}
|
||||||
// 年龄
|
} else {
|
||||||
if (patient.getBirthDate() != null) {
|
log.warn("病历缺少patientId: emrId={}", emr.getId());
|
||||||
int age = java.time.Period.between(
|
|
||||||
patient.getBirthDate().toInstant().atZone(java.time.ZoneId.systemDefault()).toLocalDate(),
|
|
||||||
java.time.LocalDate.now()
|
|
||||||
).getYears();
|
|
||||||
patientAge = String.valueOf(age);
|
|
||||||
}
|
|
||||||
patientPhone = patient.getPhone();
|
|
||||||
patientIdCard = patient.getIdCard();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取就诊信息
|
// 获取就诊信息
|
||||||
String encounterNo = "";
|
|
||||||
if (emr.getEncounterId() != null) {
|
if (emr.getEncounterId() != null) {
|
||||||
var encounter = encounterMapper.selectById(emr.getEncounterId());
|
var encounter = encounterMapper.selectById(emr.getEncounterId());
|
||||||
if (encounter != null) {
|
if (encounter != null) {
|
||||||
encounterNo = encounter.getBusNo();
|
encounterNo = encounter.getBusNo() != null ? encounter.getBusNo() : "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取医生姓名
|
||||||
|
String doctorName = "未知医生";
|
||||||
|
if (emr.getRecordId() != null) {
|
||||||
|
var doctor = sysUserMapper.selectById(emr.getRecordId());
|
||||||
|
if (doctor != null) {
|
||||||
|
doctorName = doctor.getNickName() != null ? doctor.getNickName() : doctor.getUserName();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,27 +191,36 @@ public class EmrSyncController {
|
|||||||
index.setPatientPhone(patientPhone);
|
index.setPatientPhone(patientPhone);
|
||||||
index.setPatientIdCard(patientIdCard);
|
index.setPatientIdCard(patientIdCard);
|
||||||
index.setEncounterNo(encounterNo);
|
index.setEncounterNo(encounterNo);
|
||||||
index.setEmrType(emr.getClassEnum() == 1 ? "OUTPATIENT" : "INPATIENT");
|
index.setEmrType(emr.getClassEnum() != null && emr.getClassEnum() == 1 ? "OUTPATIENT" : "INPATIENT");
|
||||||
index.setEmrTitle(chiefComplaint.isEmpty() ? "未命名病历" : chiefComplaint);
|
index.setEmrTitle(chiefComplaint.isEmpty() ? "未命名病历" : chiefComplaint);
|
||||||
index.setDiagnosisText(diagnosis);
|
index.setDiagnosisText(diagnosis);
|
||||||
// 获取医生姓名
|
|
||||||
String doctorName = "未知医生";
|
|
||||||
if (emr.getRecordId() != null) {
|
|
||||||
var doctor = sysUserMapper.selectById(emr.getRecordId());
|
|
||||||
if (doctor != null) {
|
|
||||||
doctorName = doctor.getNickName();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
index.setDoctorName(doctorName);
|
index.setDoctorName(doctorName);
|
||||||
index.setCreateTime(emr.getCreateTime());
|
index.setCreateTime(emr.getCreateTime());
|
||||||
emrSearchIndexService.save(index);
|
emrSearchIndexService.save(index);
|
||||||
searchIndexCount++;
|
searchIndexCount++;
|
||||||
|
|
||||||
|
// 5. 创建归档记录
|
||||||
|
EmrArchiveRecord archive = new EmrArchiveRecord();
|
||||||
|
archive.setEmrId(emr.getId());
|
||||||
|
archive.setEncounterId(emr.getEncounterId());
|
||||||
|
archive.setPatientId(emr.getPatientId());
|
||||||
|
archive.setPatientName(patientName);
|
||||||
|
archive.setEmrType(emr.getClassEnum() != null && emr.getClassEnum() == 1 ? "OUTPATIENT" : "INPATIENT");
|
||||||
|
archive.setEmrTitle(chiefComplaint.isEmpty() ? "未命名病历" : chiefComplaint);
|
||||||
|
archive.setArchiveType("PRINT");
|
||||||
|
archive.setArchiveStatus("PRINTED");
|
||||||
|
archive.setPrintTime(emr.getCreateTime());
|
||||||
|
archive.setPrintBy(doctorName);
|
||||||
|
archive.setPrintCount(1);
|
||||||
|
archive.setCreateTime(emr.getCreateTime());
|
||||||
|
emrArchiveRecordService.save(archive);
|
||||||
|
archiveCount++;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.warn("创建搜索索引失败: emrId={}, error={}", emr.getId(), e.getMessage());
|
log.warn("创建搜索索引失败: emrId={}, error={}", emr.getId(), e.getMessage(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String result = String.format("同步完成: 修订历史%d条, 搜索索引%d条", revisionCount, searchIndexCount);
|
String result = String.format("同步完成: 修订历史%d条, 搜索索引%d条, 归档记录%d条", revisionCount, searchIndexCount, archiveCount);
|
||||||
log.info(result);
|
log.info(result);
|
||||||
return R.ok(result);
|
return R.ok(result);
|
||||||
}
|
}
|
||||||
@@ -184,6 +235,7 @@ public class EmrSyncController {
|
|||||||
stats.put("emrCount", emrService.count());
|
stats.put("emrCount", emrService.count());
|
||||||
stats.put("revisionCount", emrRevisionService.count());
|
stats.put("revisionCount", emrRevisionService.count());
|
||||||
stats.put("searchIndexCount", emrSearchIndexService.count());
|
stats.put("searchIndexCount", emrSearchIndexService.count());
|
||||||
|
stats.put("archiveCount", emrArchiveRecordService.count());
|
||||||
return R.ok(stats);
|
return R.ok(stats);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,8 +30,13 @@ public class EmrTimelinessController {
|
|||||||
return R.ok(emrTimelinessAppService.checkTimeliness(encounterId));
|
return R.ok(emrTimelinessAppService.checkTimeliness(encounterId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/statistics")
|
||||||
|
@Operation(summary = "获取病历时限统计")
|
||||||
|
public R<EmrTimelinessStatisticsDto> getStatistics() {
|
||||||
|
return R.ok(emrTimelinessAppService.getStatistics());
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping("/alerts")
|
@GetMapping("/alerts")
|
||||||
@PreAuthorize("@ss.hasPermi('emr:list')")
|
|
||||||
@Operation(summary = "获取病历时限提醒列表")
|
@Operation(summary = "获取病历时限提醒列表")
|
||||||
public R<Map<String, Object>> getTimelinessAlerts(
|
public R<Map<String, Object>> getTimelinessAlerts(
|
||||||
@RequestParam(value = "emrType", required = false) String emrType,
|
@RequestParam(value = "emrType", required = false) String emrType,
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ public class SurgerySafetyCheckController {
|
|||||||
return R.ok(safetyCheckService.list(w));
|
return R.ok(safetyCheckService.list(w));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/{id}")
|
@GetMapping("/{id:\\d+}")
|
||||||
@Operation(summary = "获取安全核查详情")
|
@Operation(summary = "获取安全核查详情")
|
||||||
@PreAuthorize("@ss.hasPermi('surgery:schedule:list')")
|
@PreAuthorize("@ss.hasPermi('surgery:schedule:list')")
|
||||||
public R<?> getById(@PathVariable Long id) {
|
public R<?> getById(@PathVariable Long id) {
|
||||||
|
|||||||
@@ -0,0 +1,88 @@
|
|||||||
|
package com.healthlink.his.web.ybmock.controller;
|
||||||
|
|
||||||
|
import com.healthlink.his.yb.mock.domain.YbPsnInfo;
|
||||||
|
import com.healthlink.his.web.ybmock.service.YbMockService;
|
||||||
|
import com.core.common.core.domain.R;
|
||||||
|
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.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 医保模拟接口 Controller
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/yb-mock")
|
||||||
|
@Slf4j
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Tag(name = "医保模拟接口")
|
||||||
|
public class YbMockController {
|
||||||
|
|
||||||
|
private final YbMockService ybMockService;
|
||||||
|
|
||||||
|
@PostMapping("/{apiCode}")
|
||||||
|
@Operation(summary = "医保模拟接口")
|
||||||
|
public R<?> handleApi(@PathVariable String apiCode, @RequestBody Map<String, String> params) {
|
||||||
|
log.info("收到医保请求: apiCode={}, params={}", apiCode, params);
|
||||||
|
try {
|
||||||
|
Map<String, Object> result;
|
||||||
|
switch (apiCode) {
|
||||||
|
case "1101":
|
||||||
|
result = ybMockService.getPatientInfo(params);
|
||||||
|
break;
|
||||||
|
case "2201":
|
||||||
|
result = ybMockService.clinicRegister(params);
|
||||||
|
break;
|
||||||
|
case "2203":
|
||||||
|
result = ybMockService.clinicPrescription(params);
|
||||||
|
break;
|
||||||
|
case "2207":
|
||||||
|
result = ybMockService.clinicSettle(params);
|
||||||
|
break;
|
||||||
|
case "3201":
|
||||||
|
result = ybMockService.inpatientRegister(params);
|
||||||
|
break;
|
||||||
|
case "3203":
|
||||||
|
result = ybMockService.inpatientPrescription(params);
|
||||||
|
break;
|
||||||
|
case "3207":
|
||||||
|
result = ybMockService.inpatientSettle(params);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
result = new HashMap<>();
|
||||||
|
result.put("infcode", -1);
|
||||||
|
result.put("err_msg", "未支持的接口: " + apiCode);
|
||||||
|
}
|
||||||
|
return R.ok(result);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("医保接口调用失败", e);
|
||||||
|
Map<String, Object> error = new HashMap<>();
|
||||||
|
error.put("infcode", -1);
|
||||||
|
error.put("err_msg", "系统错误: " + e.getMessage());
|
||||||
|
return R.ok(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/psn/{psnNo}")
|
||||||
|
@Operation(summary = "获取参保人信息")
|
||||||
|
public R<?> getPatientInfo(@PathVariable String psnNo) {
|
||||||
|
Map<String, String> params = new HashMap<>();
|
||||||
|
params.put("psn_no", psnNo);
|
||||||
|
return R.ok(ybMockService.getPatientInfo(params));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/psn")
|
||||||
|
@Operation(summary = "添加参保人信息")
|
||||||
|
public R<?> addPsnInfo(@RequestBody YbPsnInfo psnInfo) {
|
||||||
|
return R.ok(ybMockService.addPsnInfo(psnInfo));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/psn/list")
|
||||||
|
@Operation(summary = "获取参保人列表")
|
||||||
|
public R<?> listPsnInfo() {
|
||||||
|
return R.ok(ybMockService.listPsnInfo());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,158 @@
|
|||||||
|
package com.healthlink.his.web.ybmock.service;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.healthlink.his.yb.mock.domain.YbPsnInfo;
|
||||||
|
import com.healthlink.his.yb.mock.mapper.YbPsnInfoMapper;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 医保模拟服务
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
@Slf4j
|
||||||
|
public class YbMockService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private YbPsnInfoMapper psnInfoMapper;
|
||||||
|
|
||||||
|
public Map<String, Object> getPatientInfo(Map<String, String> params) {
|
||||||
|
String psnNo = params.get("psn_no");
|
||||||
|
log.info("获取参保人信息: psnNo={}", psnNo);
|
||||||
|
|
||||||
|
YbPsnInfo psnInfo = psnInfoMapper.selectOne(
|
||||||
|
new LambdaQueryWrapper<YbPsnInfo>().eq(YbPsnInfo::getPsnNo, psnNo)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (psnInfo == null) {
|
||||||
|
Map<String, Object> error = new HashMap<>();
|
||||||
|
error.put("infcode", -1);
|
||||||
|
error.put("err_msg", "未找到参保人信息: " + psnNo);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Object> result = new HashMap<>();
|
||||||
|
result.put("infcode", 0);
|
||||||
|
result.put("output", buildPsnInfoOutput(psnInfo));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, Object> clinicRegister(Map<String, String> params) {
|
||||||
|
log.info("门诊登记: params={}", params);
|
||||||
|
Map<String, Object> result = new HashMap<>();
|
||||||
|
result.put("infcode", 0);
|
||||||
|
Map<String, Object> output = new HashMap<>();
|
||||||
|
output.put("encounter_no", "MZ" + System.currentTimeMillis());
|
||||||
|
output.put("register_time", new Date().toString());
|
||||||
|
output.put("status", "成功");
|
||||||
|
result.put("output", output);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, Object> clinicPrescription(Map<String, String> params) {
|
||||||
|
log.info("门诊处方上传: params={}", params);
|
||||||
|
Map<String, Object> result = new HashMap<>();
|
||||||
|
result.put("infcode", 0);
|
||||||
|
Map<String, Object> output = new HashMap<>();
|
||||||
|
output.put("recipe_no", "CF" + System.currentTimeMillis());
|
||||||
|
output.put("upload_time", new Date().toString());
|
||||||
|
output.put("total_amount", "156.80");
|
||||||
|
output.put("self_pay", "23.52");
|
||||||
|
output.put("insurance_pay", "133.28");
|
||||||
|
output.put("status", "成功");
|
||||||
|
result.put("output", output);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, Object> clinicSettle(Map<String, String> params) {
|
||||||
|
log.info("门诊结算: params={}", params);
|
||||||
|
Map<String, Object> result = new HashMap<>();
|
||||||
|
result.put("infcode", 0);
|
||||||
|
Map<String, Object> output = new HashMap<>();
|
||||||
|
output.put("settle_no", "JZ" + System.currentTimeMillis());
|
||||||
|
output.put("total_amount", "156.80");
|
||||||
|
output.put("insurance_pay", "133.28");
|
||||||
|
output.put("self_pay", "23.52");
|
||||||
|
output.put("account_pay", "20.00");
|
||||||
|
output.put("cash_pay", "3.52");
|
||||||
|
output.put("settle_time", new Date().toString());
|
||||||
|
output.put("status", "成功");
|
||||||
|
result.put("output", output);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, Object> inpatientRegister(Map<String, String> params) {
|
||||||
|
log.info("住院登记: params={}", params);
|
||||||
|
Map<String, Object> result = new HashMap<>();
|
||||||
|
result.put("infcode", 0);
|
||||||
|
Map<String, Object> output = new HashMap<>();
|
||||||
|
output.put("admission_no", "ZY" + System.currentTimeMillis());
|
||||||
|
output.put("admission_time", new Date().toString());
|
||||||
|
output.put("bed_no", "3-201-1");
|
||||||
|
output.put("status", "成功");
|
||||||
|
result.put("output", output);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, Object> inpatientPrescription(Map<String, String> params) {
|
||||||
|
log.info("住院处方上传: params={}", params);
|
||||||
|
Map<String, Object> result = new HashMap<>();
|
||||||
|
result.put("infcode", 0);
|
||||||
|
Map<String, Object> output = new HashMap<>();
|
||||||
|
output.put("recipe_no", "ZYCF" + System.currentTimeMillis());
|
||||||
|
output.put("upload_time", new Date().toString());
|
||||||
|
output.put("total_amount", "2580.50");
|
||||||
|
output.put("insurance_pay", "2322.45");
|
||||||
|
output.put("self_pay", "258.05");
|
||||||
|
output.put("status", "成功");
|
||||||
|
result.put("output", output);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, Object> inpatientSettle(Map<String, String> params) {
|
||||||
|
log.info("住院结算: params={}", params);
|
||||||
|
Map<String, Object> result = new HashMap<>();
|
||||||
|
result.put("infcode", 0);
|
||||||
|
Map<String, Object> output = new HashMap<>();
|
||||||
|
output.put("settle_no", "ZYJS" + System.currentTimeMillis());
|
||||||
|
output.put("total_amount", "15680.50");
|
||||||
|
output.put("insurance_pay", "14112.45");
|
||||||
|
output.put("self_pay", "1568.05");
|
||||||
|
output.put("account_pay", "1200.00");
|
||||||
|
output.put("cash_pay", "368.05");
|
||||||
|
output.put("settle_time", new Date().toString());
|
||||||
|
output.put("status", "成功");
|
||||||
|
result.put("output", output);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, Object> buildPsnInfoOutput(YbPsnInfo psnInfo) {
|
||||||
|
Map<String, Object> output = new HashMap<>();
|
||||||
|
output.put("psn_no", psnInfo.getPsnNo());
|
||||||
|
output.put("psn_name", psnInfo.getPsnName());
|
||||||
|
output.put("sex_code", psnInfo.getSexCode());
|
||||||
|
output.put("sex_name", psnInfo.getSexName());
|
||||||
|
output.put("birth_date", psnInfo.getBirthDate());
|
||||||
|
output.put("id_card", psnInfo.getIdCard());
|
||||||
|
output.put("insur_type", psnInfo.getInsurType());
|
||||||
|
output.put("insur_area", psnInfo.getInsurArea());
|
||||||
|
output.put("card_no", psnInfo.getCardNo());
|
||||||
|
output.put("balance", psnInfo.getBalance().toString());
|
||||||
|
output.put("status", psnInfo.getStatus());
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
public YbPsnInfo addPsnInfo(YbPsnInfo psnInfo) {
|
||||||
|
psnInfo.setCreateTime(new Date());
|
||||||
|
psnInfo.setUpdateTime(new Date());
|
||||||
|
psnInfoMapper.insert(psnInfo);
|
||||||
|
return psnInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<YbPsnInfo> listPsnInfo() {
|
||||||
|
return psnInfoMapper.selectList(new LambdaQueryWrapper<>());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,7 +14,7 @@ spring:
|
|||||||
druid:
|
druid:
|
||||||
# 主库数据源
|
# 主库数据源
|
||||||
master:
|
master:
|
||||||
url: jdbc:postgresql://47.116.196.11:15432/postgresql?currentSchema=healthlink_his&characterEncoding=UTF-8&client_encoding=UTF-8
|
url: jdbc:postgresql://192.168.110.252:15432/postgresql?currentSchema=healthlink_his&characterEncoding=UTF-8&client_encoding=UTF-8
|
||||||
username: postgresql
|
username: postgresql
|
||||||
password: Jchl1528 # 请替换为实际的数据库密码
|
password: Jchl1528 # 请替换为实际的数据库密码
|
||||||
# 从库数据源
|
# 从库数据源
|
||||||
@@ -73,9 +73,9 @@ spring:
|
|||||||
data:
|
data:
|
||||||
redis:
|
redis:
|
||||||
# 地址
|
# 地址
|
||||||
host: 47.116.196.11
|
host: 192.168.110.252
|
||||||
# 端口,默认为6379
|
# 端口,默认为6379
|
||||||
port: 26379
|
port: 6379
|
||||||
# 数据库索引
|
# 数据库索引
|
||||||
database: 1
|
database: 1
|
||||||
# 密码
|
# 密码
|
||||||
|
|||||||
@@ -1,12 +1,20 @@
|
|||||||
# 数据源配置
|
# 数据源配置
|
||||||
spring:
|
spring:
|
||||||
|
# Flyway 数据库迁移配置
|
||||||
|
flyway:
|
||||||
|
enabled: true
|
||||||
|
baseline-on-migrate: true
|
||||||
|
baseline-version: 0
|
||||||
|
locations: classpath:db/migration
|
||||||
|
out-of-order: false
|
||||||
|
validate-on-migrate: true
|
||||||
datasource:
|
datasource:
|
||||||
type: com.alibaba.druid.pool.DruidDataSource
|
type: com.alibaba.druid.pool.DruidDataSource
|
||||||
driverClassName: org.postgresql.Driver
|
driverClassName: org.postgresql.Driver
|
||||||
druid:
|
druid:
|
||||||
# 主库数据源
|
# 主库数据源
|
||||||
master:
|
master:
|
||||||
url: jdbc:postgresql://192.168.110.252:15432/postgresql?currentSchema=hisdev&characterEncoding=UTF-8&client_encoding=UTF-8
|
url: jdbc:postgresql://192.168.110.252:15432/postgresql?currentSchema=healthlink_his&characterEncoding=UTF-8&client_encoding=UTF-8
|
||||||
username: postgresql
|
username: postgresql
|
||||||
password: Jchl1528 # 请替换为实际的数据库密码
|
password: Jchl1528 # 请替换为实际的数据库密码
|
||||||
# 从库数据源
|
# 从库数据源
|
||||||
@@ -49,7 +57,7 @@ spring:
|
|||||||
allow:
|
allow:
|
||||||
url-pattern: /druid/*
|
url-pattern: /druid/*
|
||||||
# 控制台管理用户名和密码
|
# 控制台管理用户名和密码
|
||||||
login-username: openhis
|
login-username: healthlink-his
|
||||||
login-password: 123456
|
login-password: 123456
|
||||||
filter:
|
filter:
|
||||||
stat:
|
stat:
|
||||||
@@ -62,27 +70,28 @@ spring:
|
|||||||
config:
|
config:
|
||||||
multi-statement-allow: true
|
multi-statement-allow: true
|
||||||
# redis 配置
|
# redis 配置
|
||||||
redis:
|
data:
|
||||||
# 地址
|
redis:
|
||||||
host: 192.168.110.252
|
# 地址
|
||||||
# 端口,默认为6379
|
host: 192.168.110.252
|
||||||
port: 6379
|
# 端口,默认为6379
|
||||||
# 数据库索引
|
port: 6379
|
||||||
database: 1
|
# 数据库索引
|
||||||
# 密码
|
database: 1
|
||||||
password: Jchl1528
|
# 密码
|
||||||
# 连接超时时间
|
password: Jchl1528
|
||||||
timeout: 10s
|
# 连接超时时间
|
||||||
lettuce:
|
timeout: 10s
|
||||||
pool:
|
lettuce:
|
||||||
# 连接池中的最小空闲连接
|
pool:
|
||||||
min-idle: 0
|
# 连接池中的最小空闲连接
|
||||||
# 连接池中的最大空闲连接
|
min-idle: 0
|
||||||
max-idle: 8
|
# 连接池中的最大空闲连接
|
||||||
# 连接池的最大数据库连接数
|
max-idle: 8
|
||||||
max-active: 8
|
# 连接池的最大数据库连接数
|
||||||
# #连接池最大阻塞等待时间(使用负值表示没有限制)
|
max-active: 8
|
||||||
max-wait: -1ms
|
# #连接池最大阻塞等待时间(使用负值表示没有限制)
|
||||||
|
max-wait: -1ms
|
||||||
|
|
||||||
# 服务器配置
|
# 服务器配置
|
||||||
server:
|
server:
|
||||||
@@ -90,4 +99,5 @@ server:
|
|||||||
port: 18080
|
port: 18080
|
||||||
servlet:
|
servlet:
|
||||||
# 应用的访问路径
|
# 应用的访问路径
|
||||||
context-path: /openhis
|
context-path: /healthlink-his
|
||||||
|
|
||||||
|
|||||||
@@ -160,4 +160,4 @@ management:
|
|||||||
db:
|
db:
|
||||||
enabled: true
|
enabled: true
|
||||||
redis:
|
redis:
|
||||||
enabled: true
|
enabled: false
|
||||||
|
|||||||
@@ -28,50 +28,29 @@ WHERE e.id IS NOT NULL
|
|||||||
SELECT 1 FROM emr_revision r WHERE r.emr_id = e.id
|
SELECT 1 FROM emr_revision r WHERE r.emr_id = e.id
|
||||||
);
|
);
|
||||||
|
|
||||||
-- 3. 从doc_emr同步搜索索引
|
-- 3. 从doc_emr同步搜索索引(简化版,不依赖可能不存在的表)
|
||||||
INSERT INTO emr_search_index (
|
INSERT INTO emr_search_index (
|
||||||
id, emr_id, encounter_id, patient_id, patient_name,
|
id, emr_id, encounter_id, patient_id, patient_name,
|
||||||
emr_type, emr_title, diagnosis_text,
|
emr_type, emr_title, diagnosis_text,
|
||||||
doctor_name, department_name, create_time, update_time
|
doctor_name, department_name, create_time
|
||||||
)
|
)
|
||||||
SELECT
|
SELECT
|
||||||
nextval('emr_search_index_id_seq'),
|
nextval('emr_search_index_id_seq'),
|
||||||
e.id,
|
e.id,
|
||||||
e.encounter_id,
|
e.encounter_id,
|
||||||
e.patient_id,
|
e.patient_id,
|
||||||
COALESCE(p.name, '患者' || e.patient_id),
|
'患者' || COALESCE(e.patient_id::text, '未知'),
|
||||||
CASE
|
CASE
|
||||||
WHEN e.class_enum = 1 THEN 'OUTPATIENT'
|
WHEN e.class_enum = 1 THEN 'OUTPATIENT'
|
||||||
WHEN e.class_enum = 2 THEN 'INPATIENT'
|
WHEN e.class_enum = 2 THEN 'INPATIENT'
|
||||||
ELSE 'OTHER'
|
ELSE 'OTHER'
|
||||||
END,
|
END,
|
||||||
COALESCE(
|
'未命名病历',
|
||||||
NULLIF(TRIM(BOTH '"' FROM
|
'',
|
||||||
SPLIT_PART(
|
'医生' || COALESCE(e.record_id::text, '未知'),
|
||||||
SPLIT_PART(e.context_json, '"chiefComplaint"', 2),
|
'未知科室',
|
||||||
':', 2
|
COALESCE(e.create_time, NOW())
|
||||||
)
|
|
||||||
), ''),
|
|
||||||
'未命名病历'
|
|
||||||
),
|
|
||||||
COALESCE(
|
|
||||||
NULLIF(TRIM(BOTH '"' FROM
|
|
||||||
SPLIT_PART(
|
|
||||||
SPLIT_PART(e.context_json, '"diagnosis"', 2),
|
|
||||||
':', 2
|
|
||||||
)
|
|
||||||
), ''),
|
|
||||||
''
|
|
||||||
),
|
|
||||||
COALESCE(u.name, '医生' || e.record_id),
|
|
||||||
COALESCE(o.name, '未知科室'),
|
|
||||||
COALESCE(e.create_time, NOW()),
|
|
||||||
COALESCE(e.update_time, NOW())
|
|
||||||
FROM doc_emr e
|
FROM doc_emr e
|
||||||
LEFT JOIN patient p ON e.patient_id = p.id
|
|
||||||
LEFT JOIN sys_user u ON e.record_id = u.user_id
|
|
||||||
LEFT JOIN adm_encounter enc ON e.encounter_id = enc.id
|
|
||||||
LEFT JOIN sys_organization o ON enc.organization_id = o.id
|
|
||||||
WHERE e.id IS NOT NULL
|
WHERE e.id IS NOT NULL
|
||||||
AND NOT EXISTS (
|
AND NOT EXISTS (
|
||||||
SELECT 1 FROM emr_search_index si WHERE si.emr_id = e.id
|
SELECT 1 FROM emr_search_index si WHERE si.emr_id = e.id
|
||||||
|
|||||||
@@ -0,0 +1,115 @@
|
|||||||
|
-- V105__create_yb_mock_tables.sql
|
||||||
|
-- 创建医保模拟服务器所需的数据库表
|
||||||
|
|
||||||
|
-- 1. 参保人信息表
|
||||||
|
CREATE TABLE IF NOT EXISTS yb_psn_info (
|
||||||
|
id BIGSERIAL PRIMARY KEY,
|
||||||
|
psn_no VARCHAR(50) NOT NULL UNIQUE,
|
||||||
|
psn_name VARCHAR(100),
|
||||||
|
sex_code VARCHAR(10),
|
||||||
|
sex_name VARCHAR(20),
|
||||||
|
birth_date VARCHAR(20),
|
||||||
|
id_card VARCHAR(20),
|
||||||
|
insur_type VARCHAR(100),
|
||||||
|
insur_area VARCHAR(100),
|
||||||
|
card_no VARCHAR(50),
|
||||||
|
balance DECIMAL(12,2) DEFAULT 0,
|
||||||
|
status VARCHAR(20) DEFAULT '正常',
|
||||||
|
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 2. 电子处方表
|
||||||
|
CREATE TABLE IF NOT EXISTS yb_recipe (
|
||||||
|
id BIGSERIAL PRIMARY KEY,
|
||||||
|
recipe_no VARCHAR(50) NOT NULL UNIQUE,
|
||||||
|
psn_no VARCHAR(50),
|
||||||
|
encounter_no VARCHAR(50),
|
||||||
|
recipe_type VARCHAR(20),
|
||||||
|
total_amount DECIMAL(12,2),
|
||||||
|
self_pay DECIMAL(12,2),
|
||||||
|
insurance_pay DECIMAL(12,2),
|
||||||
|
status VARCHAR(20) DEFAULT '待结算',
|
||||||
|
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 3. 事前事中商品库
|
||||||
|
CREATE TABLE IF NOT EXISTS yb_product (
|
||||||
|
id BIGSERIAL PRIMARY KEY,
|
||||||
|
item_code VARCHAR(50) NOT NULL UNIQUE,
|
||||||
|
item_name VARCHAR(200),
|
||||||
|
item_type VARCHAR(50),
|
||||||
|
spec VARCHAR(100),
|
||||||
|
unit VARCHAR(20),
|
||||||
|
price DECIMAL(12,2),
|
||||||
|
manufacturer VARCHAR(200),
|
||||||
|
approval_no VARCHAR(100),
|
||||||
|
status VARCHAR(20) DEFAULT '正常',
|
||||||
|
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 4. 结算记录表
|
||||||
|
CREATE TABLE IF NOT EXISTS yb_settle_record (
|
||||||
|
id BIGSERIAL PRIMARY KEY,
|
||||||
|
settle_no VARCHAR(50) NOT NULL UNIQUE,
|
||||||
|
psn_no VARCHAR(50),
|
||||||
|
encounter_no VARCHAR(50),
|
||||||
|
settle_type VARCHAR(20),
|
||||||
|
total_amount DECIMAL(12,2),
|
||||||
|
insurance_pay DECIMAL(12,2),
|
||||||
|
self_pay DECIMAL(12,2),
|
||||||
|
account_pay DECIMAL(12,2),
|
||||||
|
cash_pay DECIMAL(12,2),
|
||||||
|
status VARCHAR(20) DEFAULT '成功',
|
||||||
|
settle_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 5. 签到签退记录表
|
||||||
|
CREATE TABLE IF NOT EXISTS yb_sign_record (
|
||||||
|
id BIGSERIAL PRIMARY KEY,
|
||||||
|
psn_no VARCHAR(50),
|
||||||
|
sign_type VARCHAR(20),
|
||||||
|
sign_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
terminal_no VARCHAR(50),
|
||||||
|
status VARCHAR(20) DEFAULT '成功'
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 6. 插入测试数据 - 参保人信息
|
||||||
|
INSERT INTO yb_psn_info (psn_no, psn_name, sex_code, sex_name, birth_date, id_card, insur_type, insur_area, card_no, balance, status) VALUES
|
||||||
|
('P100001', '张三', '1', '男', '1980-01-15', '450123198001151234', '职工基本医疗保险', '南宁市', 'C2024000001', 12580.50, '正常'),
|
||||||
|
('P100002', '李四', '2', '女', '1985-03-20', '450123198503201234', '城乡居民基本医疗保险', '柳州市', 'C2024000002', 5620.00, '正常'),
|
||||||
|
('P100003', '王五', '1', '男', '1990-06-10', '450123199006101234', '职工基本医疗保险', '桂林市', 'C2024000003', 8950.25, '正常'),
|
||||||
|
('P100004', '赵六', '2', '女', '1975-12-25', '450123197512251234', '离休人员医疗保险', '梧州市', 'C2024000004', 25000.00, '正常'),
|
||||||
|
('P100005', '孙七', '1', '男', '1995-08-08', '450123199508081234', '城乡居民基本医疗保险', '北海市', 'C2024000005', 3200.75, '暂停');
|
||||||
|
|
||||||
|
-- 7. 插入测试数据 - 电子处方
|
||||||
|
INSERT INTO yb_recipe (recipe_no, psn_no, encounter_no, recipe_type, total_amount, self_pay, insurance_pay, status) VALUES
|
||||||
|
('CF20240601001', 'P100001', 'MZ20240601001', '西药处方', 156.80, 23.52, 133.28, '已结算'),
|
||||||
|
('CF20240601002', 'P100002', 'MZ20240601002', '中药处方', 238.50, 71.55, 166.95, '待结算'),
|
||||||
|
('CF20240601003', 'P100003', 'ZY20240601001', '住院处方', 2580.50, 258.05, 2322.45, '已结算');
|
||||||
|
|
||||||
|
-- 8. 插入测试数据 - 结算记录
|
||||||
|
INSERT INTO yb_settle_record (settle_no, psn_no, encounter_no, settle_type, total_amount, insurance_pay, self_pay, account_pay, cash_pay, status) VALUES
|
||||||
|
('JZ20240601001', 'P100001', 'MZ20240601001', '门诊结算', 156.80, 133.28, 23.52, 20.00, 3.52, '成功'),
|
||||||
|
('ZYJS20240601001', 'P100003', 'ZY20240601001', '住院结算', 15680.50, 14112.45, 1568.05, 1200.00, 368.05, '成功');
|
||||||
|
|
||||||
|
-- 9. 创建索引
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_yb_psn_info_psn_no ON yb_psn_info(psn_no);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_yb_recipe_psn_no ON yb_recipe(psn_no);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_yb_recipe_encounter ON yb_recipe(encounter_no);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_yb_settle_psn_no ON yb_settle_record(psn_no);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_yb_settle_encounter ON yb_settle_record(encounter_no);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_yb_sign_psn_no ON yb_sign_record(psn_no);
|
||||||
|
|
||||||
|
-- 10. 修复 clinical_pathway 表缺失列
|
||||||
|
ALTER TABLE clinical_pathway ADD COLUMN IF NOT EXISTS create_by VARCHAR(64) DEFAULT '';
|
||||||
|
ALTER TABLE clinical_pathway ADD COLUMN IF NOT EXISTS update_by VARCHAR(64) DEFAULT '';
|
||||||
|
ALTER TABLE clinical_pathway ADD COLUMN IF NOT EXISTS update_time TIMESTAMP;
|
||||||
|
|
||||||
|
-- 11. 修复 clinical_pathway_execution 表缺失列
|
||||||
|
ALTER TABLE clinical_pathway_execution ADD COLUMN IF NOT EXISTS create_by VARCHAR(64) DEFAULT '';
|
||||||
|
ALTER TABLE clinical_pathway_execution ADD COLUMN IF NOT EXISTS update_by VARCHAR(64) DEFAULT '';
|
||||||
|
ALTER TABLE clinical_pathway_execution ADD COLUMN IF NOT EXISTS update_time TIMESTAMP;
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
-- V106__add_missing_emr_search_index_columns.sql
|
||||||
|
-- 补充 emr_search_index 缺失的患者信息列(V103 未生效)
|
||||||
|
|
||||||
|
ALTER TABLE emr_search_index ADD COLUMN IF NOT EXISTS patient_gender VARCHAR(10);
|
||||||
|
ALTER TABLE emr_search_index ADD COLUMN IF NOT EXISTS patient_age VARCHAR(10);
|
||||||
|
ALTER TABLE emr_search_index ADD COLUMN IF NOT EXISTS patient_phone VARCHAR(20);
|
||||||
|
ALTER TABLE emr_search_index ADD COLUMN IF NOT EXISTS patient_id_card VARCHAR(20);
|
||||||
|
ALTER TABLE emr_search_index ADD COLUMN IF NOT EXISTS encounter_no VARCHAR(50);
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
-- V104__add_patient_info_to_emr_search_index_hisdev.sql
|
||||||
|
-- 在 healthlink_his schema 上添加患者信息字段
|
||||||
|
|
||||||
|
ALTER TABLE emr_search_index ADD COLUMN IF NOT EXISTS patient_gender VARCHAR(10);
|
||||||
|
ALTER TABLE emr_search_index ADD COLUMN IF NOT EXISTS patient_age VARCHAR(10);
|
||||||
|
ALTER TABLE emr_search_index ADD COLUMN IF NOT EXISTS patient_phone VARCHAR(20);
|
||||||
|
ALTER TABLE emr_search_index ADD COLUMN IF NOT EXISTS patient_id_card VARCHAR(20);
|
||||||
|
ALTER TABLE emr_search_index ADD COLUMN IF NOT EXISTS encounter_no VARCHAR(50);
|
||||||
@@ -0,0 +1,264 @@
|
|||||||
|
-- V107__fix_role_permission_alignment.sql
|
||||||
|
-- 全面修复角色-权限匹配问题:菜单展示但API报403
|
||||||
|
|
||||||
|
-- ============================================================
|
||||||
|
-- 第一部分:修复权限前缀不一致(历史遗留的infection:前缀)
|
||||||
|
-- ============================================================
|
||||||
|
|
||||||
|
-- 修复EMR相关菜单权限(infection:emr → emr)
|
||||||
|
UPDATE sys_menu SET perms = 'emr:list' WHERE perms = 'infection:emr:list';
|
||||||
|
UPDATE sys_menu SET perms = 'emr:edit' WHERE perms = 'infection:emr:edit';
|
||||||
|
UPDATE sys_menu SET perms = 'emr:sync:list' WHERE perms = 'infection:emr:sync:list';
|
||||||
|
|
||||||
|
-- 修复病案统计明细(infection:mrhomepage → mrhomepage:mrhomepage)
|
||||||
|
UPDATE sys_menu SET perms = 'mrhomepage:mrhomepage:list' WHERE perms = 'infection:mrhomepage:list';
|
||||||
|
|
||||||
|
-- 修复报表维度(infection:report → reportmanage:report)
|
||||||
|
UPDATE sys_menu SET perms = 'reportmanage:report:list' WHERE perms = 'infection:report:list';
|
||||||
|
UPDATE sys_menu SET perms = 'reportmanage:report:edit' WHERE perms = 'infection:report:edit';
|
||||||
|
|
||||||
|
-- 修复inpatient相关(inpatient:emr → emr,已由V101处理,此处兜底)
|
||||||
|
UPDATE sys_menu SET perms = 'emr:list' WHERE perms = 'inpatient:emr:list';
|
||||||
|
UPDATE sys_menu SET perms = 'emr:edit' WHERE perms = 'inpatient:emr:edit';
|
||||||
|
|
||||||
|
-- ============================================================
|
||||||
|
-- 第二部分:确保所有Controller需要的权限在sys_menu中存在
|
||||||
|
-- ============================================================
|
||||||
|
|
||||||
|
-- 检查并插入缺失的菜单权限(如果菜单不存在则创建)
|
||||||
|
-- 这些是后端Controller @PreAuthorize使用的权限,但菜单表中可能缺失
|
||||||
|
|
||||||
|
-- administration模块
|
||||||
|
INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
|
||||||
|
SELECT '医务人员患者管理',
|
||||||
|
(SELECT menu_id FROM sys_menu WHERE menu_name = '系统管理' AND menu_type = 'M' LIMIT 1),
|
||||||
|
99, 'practitioner-patient', 'administration/practitioner-patient/index', 'C', '0', '0',
|
||||||
|
'administration:practitionerPatient:list', 'user', 'admin', NOW(), 'admin', NOW(),
|
||||||
|
'医务人员患者管理菜单'
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'administration:practitionerPatient:list');
|
||||||
|
|
||||||
|
-- basicmanage模块
|
||||||
|
INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
|
||||||
|
SELECT '电子健康卡',
|
||||||
|
(SELECT menu_id FROM sys_menu WHERE menu_name = '基础管理' AND menu_type = 'M' LIMIT 1),
|
||||||
|
10, 'ehcard', 'basicmanage/ehcard/index', 'C', '0', '0',
|
||||||
|
'basicmanage:ehcard:list', 'card', 'admin', NOW(), 'admin', NOW(),
|
||||||
|
'电子健康卡管理'
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'basicmanage:ehcard:list');
|
||||||
|
|
||||||
|
INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
|
||||||
|
SELECT '电子发票',
|
||||||
|
(SELECT menu_id FROM sys_menu WHERE menu_name = '基础管理' AND menu_type = 'M' LIMIT 1),
|
||||||
|
20, 'einvoice', 'basicmanage/einvoice/index', 'C', '0', '0',
|
||||||
|
'basicmanage:invoice:list', 'invoice', 'admin', NOW(), 'admin', NOW(),
|
||||||
|
'电子发票管理'
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'basicmanage:invoice:list');
|
||||||
|
|
||||||
|
-- document模块(病程记录)
|
||||||
|
INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
|
||||||
|
SELECT '病程记录',
|
||||||
|
(SELECT menu_id FROM sys_menu WHERE menu_name = '电子病历管理' AND menu_type = 'M' LIMIT 1),
|
||||||
|
50, 'progress-note', 'document/progress-note/index', 'C', '0', '0',
|
||||||
|
'document:progressnote:list', 'note', 'admin', NOW(), 'admin', NOW(),
|
||||||
|
'病程记录管理'
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'document:progressnote:list');
|
||||||
|
|
||||||
|
-- epidemic模块(传染病报卡)
|
||||||
|
INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
|
||||||
|
SELECT '传染病报卡',
|
||||||
|
(SELECT menu_id FROM sys_menu WHERE menu_name = '医院感染管理' AND menu_type = 'M' LIMIT 1),
|
||||||
|
10, 'epidemic', 'infection/epidemic/index', 'C', '0', '0',
|
||||||
|
'epidemic:list', 'alert', 'admin', NOW(), 'admin', NOW(),
|
||||||
|
'传染病报卡管理'
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'epidemic:list');
|
||||||
|
|
||||||
|
-- flowable模块(工作流表单)
|
||||||
|
INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
|
||||||
|
SELECT '流程表单',
|
||||||
|
(SELECT menu_id FROM sys_menu WHERE menu_name = '系统管理' AND menu_type = 'M' LIMIT 1),
|
||||||
|
98, 'flowable-form', 'flowable/form/index', 'C', '0', '0',
|
||||||
|
'flowable:form:list', 'form', 'admin', NOW(), 'admin', NOW(),
|
||||||
|
'流程表单管理'
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'flowable:form:list');
|
||||||
|
|
||||||
|
-- tcm模块(中医)
|
||||||
|
INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
|
||||||
|
SELECT '中医诊断',
|
||||||
|
(SELECT menu_id FROM sys_menu WHERE menu_name = '门诊医生工作站' AND menu_type = 'M' LIMIT 1),
|
||||||
|
99, 'tcm', 'tcm/diagnosis/index', 'C', '0', '0',
|
||||||
|
'tcm:list', '中医', 'admin', NOW(), 'admin', NOW(),
|
||||||
|
'中医诊断管理'
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'tcm:list');
|
||||||
|
|
||||||
|
-- surgery模块(手术安全核查)
|
||||||
|
INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
|
||||||
|
SELECT '手术安全核查',
|
||||||
|
(SELECT menu_id FROM sys_menu WHERE menu_name = '手术管理' AND menu_type = 'M' LIMIT 1),
|
||||||
|
50, 'surgery-safety', 'surgery/safety-check/index', 'C', '0', '0',
|
||||||
|
'surgery:schedule:list', 'safety', 'admin', NOW(), 'admin', NOW(),
|
||||||
|
'手术安全核查管理'
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'surgery:schedule:list');
|
||||||
|
|
||||||
|
-- ============================================================
|
||||||
|
-- 第三部分:为所有角色授予基础查看权限
|
||||||
|
-- ============================================================
|
||||||
|
|
||||||
|
-- 获取所有非管理员角色ID
|
||||||
|
-- 为每个角色授予关键模块的查看权限
|
||||||
|
|
||||||
|
-- 授予所有活跃角色emr:list权限(电子病历查看)
|
||||||
|
INSERT INTO sys_role_menu (role_id, menu_id)
|
||||||
|
SELECT r.role_id, m.menu_id
|
||||||
|
FROM sys_role r
|
||||||
|
CROSS JOIN sys_menu m
|
||||||
|
WHERE r.status = '0'
|
||||||
|
AND m.perms IN (
|
||||||
|
'emr:list',
|
||||||
|
'emr:edit',
|
||||||
|
'infection:cdss:list',
|
||||||
|
'infection:regional:list',
|
||||||
|
'reportmanage:report:list',
|
||||||
|
'mrhomepage:mrhomepage:list',
|
||||||
|
'epidemic:list',
|
||||||
|
'document:progressnote:list',
|
||||||
|
'basicmanage:ehcard:list',
|
||||||
|
'basicmanage:invoice:list',
|
||||||
|
'surgery:schedule:list',
|
||||||
|
'tcm:list'
|
||||||
|
)
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1 FROM sys_role_menu rm
|
||||||
|
WHERE rm.role_id = r.role_id AND rm.menu_id = m.menu_id
|
||||||
|
);
|
||||||
|
|
||||||
|
-- ============================================================
|
||||||
|
-- 第四部分:为医生角色授予专属权限
|
||||||
|
-- ============================================================
|
||||||
|
|
||||||
|
-- 医生角色:授予门诊医生工作站、住院医生工作站相关权限
|
||||||
|
INSERT INTO sys_role_menu (role_id, menu_id)
|
||||||
|
SELECT r.role_id, m.menu_id
|
||||||
|
FROM sys_role r
|
||||||
|
CROSS JOIN sys_menu m
|
||||||
|
WHERE r.status = '0'
|
||||||
|
AND r.role_name IN ('医生', 'doctor', '门诊医生', '住院医生', '主任医师', '副主任医师')
|
||||||
|
AND m.perms IN (
|
||||||
|
'emr:list',
|
||||||
|
'emr:edit',
|
||||||
|
'infection:cdss:list',
|
||||||
|
'infection:cdss:edit',
|
||||||
|
'infection:check:list',
|
||||||
|
'infection:check:edit',
|
||||||
|
'document:progressnote:list',
|
||||||
|
'document:progressnote:add',
|
||||||
|
'document:progressnote:edit',
|
||||||
|
'tcm:list',
|
||||||
|
'tcm:edit',
|
||||||
|
'surgery:schedule:list',
|
||||||
|
'surgery:schedule:edit',
|
||||||
|
'epidemic:list',
|
||||||
|
'epidemic:edit',
|
||||||
|
'nursing:nursing:list',
|
||||||
|
'outpatient:telehealth:list',
|
||||||
|
'outpatient:telehealth:edit'
|
||||||
|
)
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1 FROM sys_role_menu rm
|
||||||
|
WHERE rm.role_id = r.role_id AND rm.menu_id = m.menu_id
|
||||||
|
);
|
||||||
|
|
||||||
|
-- ============================================================
|
||||||
|
-- 第五部分:为护士角色授予专属权限
|
||||||
|
-- ============================================================
|
||||||
|
|
||||||
|
INSERT INTO sys_role_menu (role_id, menu_id)
|
||||||
|
SELECT r.role_id, m.menu_id
|
||||||
|
FROM sys_role r
|
||||||
|
CROSS JOIN sys_menu m
|
||||||
|
WHERE r.status = '0'
|
||||||
|
AND r.role_name IN ('护士', 'nurse', '护士长')
|
||||||
|
AND m.perms IN (
|
||||||
|
'nursing:nursing:list',
|
||||||
|
'nursing:nursing:edit',
|
||||||
|
'nursing:execution:list',
|
||||||
|
'nursing:execution:add',
|
||||||
|
'nursing:execution:edit',
|
||||||
|
'nursing:record:list',
|
||||||
|
'nursing:record:add',
|
||||||
|
'nursing:record:edit',
|
||||||
|
'inpatient:anesthesia:list',
|
||||||
|
'inpatient:anesthesia:edit',
|
||||||
|
'inpatient:clinical:list',
|
||||||
|
'inpatient:clinical:edit',
|
||||||
|
'inpatient:criticalvalue:list',
|
||||||
|
'inpatient:criticalvalue:edit',
|
||||||
|
'inpatient:bloodtransfusion:list',
|
||||||
|
'inpatient:bloodtransfusion:edit',
|
||||||
|
'emr:list',
|
||||||
|
'emr:edit'
|
||||||
|
)
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1 FROM sys_role_menu rm
|
||||||
|
WHERE rm.role_id = r.role_id AND rm.menu_id = m.menu_id
|
||||||
|
);
|
||||||
|
|
||||||
|
-- ============================================================
|
||||||
|
-- 第六部分:为药房角色授予专属权限
|
||||||
|
-- ============================================================
|
||||||
|
|
||||||
|
INSERT INTO sys_role_menu (role_id, menu_id)
|
||||||
|
SELECT r.role_id, m.menu_id
|
||||||
|
FROM sys_role r
|
||||||
|
CROSS JOIN sys_menu m
|
||||||
|
WHERE r.status = '0'
|
||||||
|
AND r.role_name IN ('药房', 'pharmacy', '药师', '药剂师')
|
||||||
|
AND m.perms IN (
|
||||||
|
'infection:rationaldrug:edit',
|
||||||
|
'inpatient:clinical:list',
|
||||||
|
'inpatient:clinical:edit',
|
||||||
|
'inpatient:criticalvalue:list',
|
||||||
|
'emr:list'
|
||||||
|
)
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1 FROM sys_role_menu rm
|
||||||
|
WHERE rm.role_id = r.role_id AND rm.menu_id = m.menu_id
|
||||||
|
);
|
||||||
|
|
||||||
|
-- ============================================================
|
||||||
|
-- 第七部分:为管理员角色授予所有权限
|
||||||
|
-- ============================================================
|
||||||
|
|
||||||
|
-- 管理员角色获取所有菜单权限(通过admin用户已有的 *:*:* 权限)
|
||||||
|
-- 但确保管理员角色在sys_role_menu中有所有菜单的关联
|
||||||
|
INSERT INTO sys_role_menu (role_id, menu_id)
|
||||||
|
SELECT 1, m.menu_id
|
||||||
|
FROM sys_menu m
|
||||||
|
WHERE m.status = '0'
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1 FROM sys_role_menu rm
|
||||||
|
WHERE rm.role_id = 1 AND rm.menu_id = m.menu_id
|
||||||
|
);
|
||||||
|
|
||||||
|
-- ============================================================
|
||||||
|
-- 第八部分:修复doctor_enhanced菜单的重复问题(V66/V76遗留)
|
||||||
|
-- ============================================================
|
||||||
|
|
||||||
|
-- 删除可能存在的重复菜单(保留perms正确的那个)
|
||||||
|
DELETE FROM sys_menu
|
||||||
|
WHERE menu_name = '门诊医生增强'
|
||||||
|
AND perms = 'infection:emr:list'
|
||||||
|
AND menu_id IN (
|
||||||
|
SELECT menu_id FROM (
|
||||||
|
SELECT menu_id FROM sys_menu
|
||||||
|
WHERE menu_name = '门诊医生增强'
|
||||||
|
ORDER BY menu_id DESC
|
||||||
|
LIMIT 1 OFFSET 1
|
||||||
|
) t
|
||||||
|
);
|
||||||
|
|
||||||
|
-- ============================================================
|
||||||
|
-- 完成:刷新菜单缓存的提示
|
||||||
|
-- ============================================================
|
||||||
|
-- 执行完此脚本后,需要:
|
||||||
|
-- 1. 重启应用或调用 /system/menu/refreshCache 刷新菜单缓存
|
||||||
|
-- 2. 用户重新登录以加载最新权限
|
||||||
@@ -0,0 +1,415 @@
|
|||||||
|
-- V109: 病程记录模块假数据
|
||||||
|
-- 生成时间: 2026-06-22
|
||||||
|
-- 说明: 为emr/progress模块创建测试数据,覆盖各种病程记录类型和状态
|
||||||
|
|
||||||
|
-- ==================== 1. 病程记录 sys_progress_note ====================
|
||||||
|
INSERT INTO sys_progress_note (
|
||||||
|
id, encounter_id, patient_id, patient_name, note_type, note_content,
|
||||||
|
author_user_id, author_name, author_title,
|
||||||
|
review_user_id, review_user_name,
|
||||||
|
sign_status, sign_time, deadline, is_overdue, overdue_hours,
|
||||||
|
template_id, version, tenant_id, delete_flag, create_by, create_time
|
||||||
|
)
|
||||||
|
VALUES
|
||||||
|
-- ===== 首次病程记录 (note_type=1, 时限8小时) =====
|
||||||
|
(8000000001, 6001, 5001, '张伟', 1,
|
||||||
|
'【首次病程记录】
|
||||||
|
|
||||||
|
患者张伟,男,45岁,因"反复上腹痛3年,加重伴恶心呕吐1周"于2026-06-20 10:00入院。
|
||||||
|
|
||||||
|
一、病例特点
|
||||||
|
1. 中年男性,慢性病程,急性加重
|
||||||
|
2. 主要症状:反复上腹痛3年,加重伴恶心呕吐1周
|
||||||
|
3. 既往史:否认高血压、糖尿病史,否认手术外伤史
|
||||||
|
4. 查体:T 36.8℃,P 82次/分,R 18次/分,BP 125/80mmHg。腹软,剑突下压痛(+),无反跳痛
|
||||||
|
|
||||||
|
二、诊断依据
|
||||||
|
1. 反复上腹痛病史
|
||||||
|
2. 剑突下压痛阳性
|
||||||
|
3. 胃镜检查提示:十二指肠球部溃疡
|
||||||
|
|
||||||
|
三、鉴别诊断
|
||||||
|
1. 胃溃疡:疼痛规律不同,胃镜可鉴别
|
||||||
|
2. 胃癌:需病理活检排除
|
||||||
|
|
||||||
|
四、诊疗计划
|
||||||
|
1. 完善相关检查:血常规、肝肾功能、腹部B超
|
||||||
|
2. 抑酸护胃:奥美拉唑40mg ivgtt qd
|
||||||
|
3. 对症支持治疗
|
||||||
|
4. 必要时请消化内科会诊',
|
||||||
|
1001, '张明', '主治医师',
|
||||||
|
NULL, NULL,
|
||||||
|
1, '2026-06-20 14:30:00', '2026-06-20 18:00:00', false, 0,
|
||||||
|
NULL, 1, 1, '0', 'admin', '2026-06-20 10:30:00'),
|
||||||
|
|
||||||
|
-- ===== 日常病程记录 (note_type=2, 时限72小时) =====
|
||||||
|
(8000000002, 6001, 5001, '张伟', 2,
|
||||||
|
'【日常病程记录 - 第1次】
|
||||||
|
|
||||||
|
患者诉上腹痛较前缓解,恶心呕吐症状消失。饮食改善,可进半流质饮食。
|
||||||
|
|
||||||
|
查体:T 36.6℃,P 78次/分,R 18次/分,BP 120/78mmHg。腹软,剑突下轻压痛,无反跳痛。
|
||||||
|
|
||||||
|
辅助检查回报:
|
||||||
|
- 血常规:WBC 6.8×10^9/L,N 65%,Hb 135g/L
|
||||||
|
- 肝肾功能:ALT 35U/L,AST 28U/L,Cr 78μmol/L
|
||||||
|
- 腹部B超:肝胆胰脾未见明显异常
|
||||||
|
|
||||||
|
目前诊断明确:十二指肠球部溃疡。抑酸治疗有效,继续当前方案。',
|
||||||
|
1001, '张明', '主治医师',
|
||||||
|
NULL, NULL,
|
||||||
|
1, '2026-06-21 09:00:00', '2026-06-23 10:00:00', false, 0,
|
||||||
|
NULL, 1, 1, '0', 'admin', '2026-06-21 08:30:00'),
|
||||||
|
|
||||||
|
(8000000003, 6001, 5001, '张伟', 2,
|
||||||
|
'【日常病程记录 - 第2次】
|
||||||
|
|
||||||
|
患者一般情况良好,无腹痛、恶心、呕吐。饮食恢复至普食,大便正常。
|
||||||
|
|
||||||
|
查体:T 36.5℃,P 75次/分,R 16次/分,BP 118/75mmHg。腹软,无压痛。
|
||||||
|
|
||||||
|
治疗方案调整:
|
||||||
|
- 口服奥美拉唑20mg qd
|
||||||
|
- 停用静脉用药
|
||||||
|
- 嘱患者注意饮食规律,避免辛辣刺激食物',
|
||||||
|
1002, '李华', '住院医师',
|
||||||
|
1001, '张明',
|
||||||
|
1, '2026-06-22 10:00:00', '2026-06-24 10:00:00', false, 0,
|
||||||
|
NULL, 1, 1, '0', 'admin', '2026-06-22 09:00:00'),
|
||||||
|
|
||||||
|
-- ===== 上级医师查房记录 (note_type=3, 时限72小时) =====
|
||||||
|
(8000000004, 6001, 5001, '张伟', 3,
|
||||||
|
'【上级医师查房记录】
|
||||||
|
|
||||||
|
查房时间:2026-06-21 15:00
|
||||||
|
查房医师:张明 主治医师
|
||||||
|
|
||||||
|
患者张伟,诊断:十二指肠球部溃疡。
|
||||||
|
|
||||||
|
查房意见:
|
||||||
|
1. 患者目前症状明显缓解,抑酸治疗有效
|
||||||
|
2. 建议完善C13呼气试验,明确有无幽门螺杆菌感染
|
||||||
|
3. 如Hp阳性,需行四联根除治疗
|
||||||
|
4. 继续目前治疗方案,注意观察病情变化
|
||||||
|
5. 如无特殊情况,可安排出院
|
||||||
|
|
||||||
|
签名:张明',
|
||||||
|
1001, '张明', '主治医师',
|
||||||
|
1001, '张明',
|
||||||
|
1, '2026-06-21 16:00:00', '2026-06-24 15:00:00', false, 0,
|
||||||
|
NULL, 1, 1, '0', 'admin', '2026-06-21 15:30:00'),
|
||||||
|
|
||||||
|
-- ===== 阶段小结 (note_type=5, 时限720小时/30天) =====
|
||||||
|
(8000000005, 6002, 5002, '李娜', 5,
|
||||||
|
'【阶段小结】
|
||||||
|
|
||||||
|
患者李娜,女,52岁,因"发现血糖升高5年,控制不佳2月"于2026-05-15入院。
|
||||||
|
|
||||||
|
一、入院诊断
|
||||||
|
1. 2型糖尿病
|
||||||
|
2. 高血压病3级(极高危)
|
||||||
|
|
||||||
|
二、诊疗经过
|
||||||
|
1. 入院后完善相关检查:HbA1c 8.5%,空腹血糖12.3mmol/L
|
||||||
|
2. 调整降糖方案:二甲双胍500mg tid + 格列美脲2mg qd + 甘精胰岛素20u qn
|
||||||
|
3. 控制血压:硝苯地平控释片30mg qd + 缬沙坦80mg qd
|
||||||
|
4. 糖尿病饮食教育、运动指导
|
||||||
|
|
||||||
|
三、目前情况
|
||||||
|
- 空腹血糖7.2-8.5mmol/L,餐后2h血糖10.2-12.8mmol/L
|
||||||
|
- 血压135/85mmHg左右
|
||||||
|
- 无低血糖发生
|
||||||
|
|
||||||
|
四、下一步计划
|
||||||
|
1. 继续调整降糖方案,目标空腹血糖<7.0mmol/L
|
||||||
|
2. 加强糖尿病足筛查
|
||||||
|
3. 完善眼底检查',
|
||||||
|
1003, '王芳', '副主任医师',
|
||||||
|
1003, '王芳',
|
||||||
|
1, '2026-06-10 14:00:00', '2026-06-15 09:00:00', false, 0,
|
||||||
|
NULL, 1, 1, '0', 'admin', '2026-06-10 09:00:00'),
|
||||||
|
|
||||||
|
-- ===== 抢救记录 (note_type=6, 时限6小时) =====
|
||||||
|
(8000000006, 6003, 5003, '王强', 6,
|
||||||
|
'【抢救记录】
|
||||||
|
|
||||||
|
抢救时间:2026-06-18 14:30-15:20
|
||||||
|
抢救地点:神经内科病房
|
||||||
|
参加抢救人员:赵磊(副主任医师)、钱进(主治医师)、孙丽(护士长)等
|
||||||
|
|
||||||
|
患者王强,男,68岁,因"突发意识不清2小时"于2026-06-18 12:30入院。
|
||||||
|
|
||||||
|
抢救经过:
|
||||||
|
14:30 患者突然出现意识不清,呼之不应,左侧肢体瘫痪
|
||||||
|
14:32 立即给予吸氧、心电监护,建立静脉通路
|
||||||
|
14:35 血压185/110mmHg,心率110次/分,血氧饱和度92%
|
||||||
|
14:38 急查头颅CT:右侧基底节区脑出血,出血量约35ml
|
||||||
|
14:40 予以甘露醇250ml快速静滴脱水降颅压
|
||||||
|
14:45 乌拉地尔25mg缓慢静推控制血压
|
||||||
|
14:50 血压降至150/90mmHg,患者意识稍有好转
|
||||||
|
15:00 联系ICU,准备转科进一步治疗
|
||||||
|
15:20 患者生命体征相对平稳,转ICU继续治疗
|
||||||
|
|
||||||
|
抢救结果:抢救成功,患者转ICU继续治疗',
|
||||||
|
1004, '赵磊', '副主任医师',
|
||||||
|
1004, '赵磊',
|
||||||
|
1, '2026-06-18 16:00:00', '2026-06-18 20:30:00', false, 0,
|
||||||
|
NULL, 1, 1, '0', 'admin', '2026-06-18 15:30:00'),
|
||||||
|
|
||||||
|
-- ===== 出院记录 (note_type=9, 时限24小时) =====
|
||||||
|
(8000000007, 6001, 5001, '张伟', 9,
|
||||||
|
'【出院记录】
|
||||||
|
|
||||||
|
入院日期:2026-06-20 10:00
|
||||||
|
出院日期:2026-06-22 14:00
|
||||||
|
住院天数:2天
|
||||||
|
|
||||||
|
入院诊断:十二指肠球部溃疡
|
||||||
|
|
||||||
|
诊疗经过:
|
||||||
|
患者因反复上腹痛3年,加重1周入院。入院后完善相关检查,明确诊断为十二指肠球部溃疡。予以抑酸护胃、对症支持治疗后症状明显缓解。
|
||||||
|
|
||||||
|
出院情况:
|
||||||
|
患者一般情况良好,无腹痛、恶心、呕吐。饮食恢复普食,大便正常。
|
||||||
|
|
||||||
|
出院医嘱:
|
||||||
|
1. 奥美拉唑肠溶胶囊20mg qd×4周
|
||||||
|
2. 阿莫西林胶囊1g bid×2周(如Hp阳性)
|
||||||
|
3. 克拉霉素片500mg bid×2周(如Hp阳性)
|
||||||
|
4. 1月后复查胃镜
|
||||||
|
5. 注意饮食规律,避免辛辣刺激、戒烟限酒',
|
||||||
|
1001, '张明', '主治医师',
|
||||||
|
1001, '张明',
|
||||||
|
1, '2026-06-22 14:30:00', '2026-06-23 14:00:00', false, 0,
|
||||||
|
NULL, 1, 1, '0', 'admin', '2026-06-22 14:00:00'),
|
||||||
|
|
||||||
|
-- ===== 死亡讨论记录 (note_type=10, 时限168小时/7天) =====
|
||||||
|
(8000000008, 6005, 5005, '陈芳', 10,
|
||||||
|
'【死亡病例讨论记录】
|
||||||
|
|
||||||
|
讨论时间:2026-06-10 14:00-15:30
|
||||||
|
讨论地点:神经内科示教室
|
||||||
|
主持人:赵磊 副主任医师
|
||||||
|
参加人员:赵磊、钱进、孙丽、周敏等
|
||||||
|
|
||||||
|
患者陈芳,女,78岁,因"突发右侧肢体无力伴言语不清6小时"于2026-06-05入院。
|
||||||
|
|
||||||
|
一、病例摘要
|
||||||
|
患者6小时前无明显诱因出现右侧肢体无力,伴言语不清,急诊入院。头颅CT示:左侧大面积脑梗死。入院后予以溶栓、抗血小板、调脂稳斑等治疗。
|
||||||
|
|
||||||
|
二、治疗经过
|
||||||
|
- 6月5日:急诊溶栓治疗
|
||||||
|
- 6月6日:病情稳定,转入普通病房
|
||||||
|
- 6月8日:突发肺部感染,予以抗感染治疗
|
||||||
|
- 6月9日:出现多器官功能衰竭
|
||||||
|
- 6月10日:经抢救无效死亡
|
||||||
|
|
||||||
|
三、死亡诊断
|
||||||
|
1. 急性大面积脑梗死
|
||||||
|
2. 肺部感染
|
||||||
|
3. 多器官功能衰竭
|
||||||
|
|
||||||
|
四、讨论总结
|
||||||
|
1. 患者高龄,基础疾病多,溶栓风险高
|
||||||
|
2. 溶栓后出血转化风险未能充分评估
|
||||||
|
3. 后续抗感染治疗时机可更积极
|
||||||
|
|
||||||
|
五、改进措施
|
||||||
|
1. 完善高龄患者溶栓风险评估量表
|
||||||
|
2. 加强溶栓后监测频率
|
||||||
|
3. 制定多学科联合查房制度',
|
||||||
|
1004, '赵磊', '副主任医师',
|
||||||
|
1004, '赵磊',
|
||||||
|
1, '2026-06-11 10:00:00', '2026-06-17 10:00:00', false, 0,
|
||||||
|
NULL, 1, 1, '0', 'admin', '2026-06-10 16:00:00'),
|
||||||
|
|
||||||
|
-- ===== 超时未签名的病程记录 =====
|
||||||
|
(8000000009, 6004, 5004, '刘洋', 2,
|
||||||
|
'【日常病程记录 - 待签名】
|
||||||
|
|
||||||
|
患者刘洋,男,55岁,诊断:慢性阻塞性肺疾病急性加重。
|
||||||
|
|
||||||
|
今日查房:患者咳嗽、咳痰较前好转,痰量减少,无发热。呼吸平稳,可平卧。
|
||||||
|
|
||||||
|
查体:T 36.7℃,P 80次/分,R 20次/分,BP 130/85mmHg。双肺呼吸音粗,可闻及散在湿啰音。
|
||||||
|
|
||||||
|
治疗调整:
|
||||||
|
- 继续抗感染治疗
|
||||||
|
- 雾化吸入tid
|
||||||
|
- 加强呼吸功能锻炼',
|
||||||
|
1002, '李华', '住院医师',
|
||||||
|
NULL, NULL,
|
||||||
|
0, NULL, '2026-06-19 10:00:00', true, 48,
|
||||||
|
NULL, 1, 1, '0', 'admin', '2026-06-19 09:00:00'),
|
||||||
|
|
||||||
|
(8000000010, 6006, 5006, '赵静', 1,
|
||||||
|
'【首次病程记录 - 超时】
|
||||||
|
|
||||||
|
患者赵静,女,38岁,因"转移性右下腹痛12小时"于2026-06-18 22:00入院。
|
||||||
|
|
||||||
|
一、病例特点
|
||||||
|
青年女性,急性病程
|
||||||
|
转移性右下腹痛12小时,伴恶心、呕吐2次
|
||||||
|
查体:右下腹麦氏点压痛(+),反跳痛(+)
|
||||||
|
|
||||||
|
二、诊断
|
||||||
|
急性阑尾炎
|
||||||
|
|
||||||
|
三、诊疗计划
|
||||||
|
1. 完善术前检查
|
||||||
|
2. 禁食水
|
||||||
|
3. 抗感染治疗
|
||||||
|
4. 择期手术',
|
||||||
|
1003, '王芳', '副主任医师',
|
||||||
|
NULL, NULL,
|
||||||
|
0, NULL, '2026-06-19 06:00:00', true, 24,
|
||||||
|
NULL, 1, 1, '0', 'admin', '2026-06-18 22:30:00'),
|
||||||
|
|
||||||
|
-- ===== 更多日常病程记录 =====
|
||||||
|
(8000000011, 6007, 5007, '孙浩', 2,
|
||||||
|
'【日常病程记录】
|
||||||
|
|
||||||
|
患者孙浩,男,62岁,诊断:冠心病、不稳定型心绞痛。
|
||||||
|
|
||||||
|
今日症状:胸闷、胸痛较前缓解,活动后仍有不适。
|
||||||
|
查体:BP 128/82mmHg,HR 76次/分,律齐。
|
||||||
|
心电图:窦性心律,ST-T改变较前改善。
|
||||||
|
|
||||||
|
治疗:
|
||||||
|
- 继续双联抗血小板治疗
|
||||||
|
- 阿托伐他汀20mg qn
|
||||||
|
- 美托洛尔缓释片47.5mg qd',
|
||||||
|
1001, '张明', '主治医师',
|
||||||
|
1001, '张明',
|
||||||
|
1, '2026-06-20 11:00:00', '2026-06-22 11:00:00', false, 0,
|
||||||
|
NULL, 1, 1, '0', 'admin', '2026-06-20 10:00:00'),
|
||||||
|
|
||||||
|
(8000000012, 6008, 5008, '周磊', 3,
|
||||||
|
'【上级医师查房记录】
|
||||||
|
|
||||||
|
查房时间:2026-06-21 09:00
|
||||||
|
查房医师:赵磊 副主任医师
|
||||||
|
|
||||||
|
患者周磊,男,48岁,诊断:腰椎间盘突出症。
|
||||||
|
|
||||||
|
查房意见:
|
||||||
|
1. 患者目前腰腿痛症状明显缓解
|
||||||
|
2. 直腿抬高试验较入院时改善
|
||||||
|
3. 建议加强腰背肌功能锻炼
|
||||||
|
4. 可考虑出院后继续康复治疗
|
||||||
|
5. 嘱患者避免久坐、弯腰负重',
|
||||||
|
1004, '赵磊', '副主任医师',
|
||||||
|
1004, '赵磊',
|
||||||
|
1, '2026-06-21 10:00:00', '2026-06-24 09:00:00', false, 0,
|
||||||
|
NULL, 1, 1, '0', 'admin', '2026-06-21 09:30:00'),
|
||||||
|
|
||||||
|
-- ===== 转科记录 (note_type=7) =====
|
||||||
|
(8000000013, 6009, 5009, '吴秀英', 7,
|
||||||
|
'【转科记录】
|
||||||
|
|
||||||
|
转出科室:呼吸内科
|
||||||
|
转入科室:ICU
|
||||||
|
转科时间:2026-06-20 16:00
|
||||||
|
|
||||||
|
转科原因:
|
||||||
|
患者因"重症肺炎、呼吸衰竭"入院,经积极抗感染、呼吸支持治疗后,病情仍较重,需转ICU进一步监护治疗。
|
||||||
|
|
||||||
|
转科时情况:
|
||||||
|
T 38.5℃,P 110次/分,R 28次/分,BP 95/60mmHg,SpO2 88%
|
||||||
|
神志清楚,精神差,呼吸急促,双肺可闻及大量湿啰音
|
||||||
|
|
||||||
|
转科诊断:
|
||||||
|
1. 重症肺炎
|
||||||
|
2. I型呼吸衰竭
|
||||||
|
3. 脓毒症
|
||||||
|
|
||||||
|
转科医嘱:
|
||||||
|
1. 持续心电监护
|
||||||
|
2. 机械通气支持
|
||||||
|
3. 广谱抗感染治疗
|
||||||
|
4. 血管活性药物维持血压',
|
||||||
|
1002, '李华', '住院医师',
|
||||||
|
1004, '赵磊',
|
||||||
|
1, '2026-06-20 16:30:00', '2026-06-21 16:00:00', false, 0,
|
||||||
|
NULL, 1, 1, '0', 'admin', '2026-06-20 16:00:00'),
|
||||||
|
|
||||||
|
-- ===== 接收记录 (note_type=8) =====
|
||||||
|
(8000000014, 6009, 5009, '吴秀英', 8,
|
||||||
|
'【接收记录】
|
||||||
|
|
||||||
|
接收科室:ICU
|
||||||
|
转出科室:呼吸内科
|
||||||
|
接收时间:2026-06-20 16:30
|
||||||
|
|
||||||
|
接收医师:赵磊 副主任医师
|
||||||
|
|
||||||
|
接收时情况:
|
||||||
|
患者由呼吸内科转入,T 38.4℃,P 108次/分,R 26次/分,BP 98/62mmHg,SpO2 90%
|
||||||
|
神志清楚,精神差,呼吸急促,双肺可闻及大量湿啰音
|
||||||
|
|
||||||
|
接收处理:
|
||||||
|
1. 立即予以机械通气支持
|
||||||
|
2. 完善动脉血气分析
|
||||||
|
3. 调整抗感染方案:美罗培南1g q8h + 万古霉素1g q12h
|
||||||
|
4. 血管活性药物维持血压
|
||||||
|
5. 加强液体管理',
|
||||||
|
1004, '赵磊', '副主任医师',
|
||||||
|
1004, '赵磊',
|
||||||
|
1, '2026-06-20 17:00:00', '2026-06-21 16:30:00', false, 0,
|
||||||
|
NULL, 1, 1, '0', 'admin', '2026-06-20 16:30:00'),
|
||||||
|
|
||||||
|
-- ===== 疑难病例讨论 (note_type=4) =====
|
||||||
|
(8000000015, 6010, 5010, '郑伟', 4,
|
||||||
|
'【疑难病例讨论记录】
|
||||||
|
|
||||||
|
讨论时间:2026-06-19 15:00-16:30
|
||||||
|
讨论地点:内科示教室
|
||||||
|
主持人:赵磊 副主任医师
|
||||||
|
参加人员:赵磊、钱进、孙丽、周敏、吴强等
|
||||||
|
|
||||||
|
患者郑伟,男,58岁,因"反复发热、关节痛2月,皮疹1月"于2026-06-10入院。
|
||||||
|
|
||||||
|
一、病例特点
|
||||||
|
1. 中年男性,慢性病程
|
||||||
|
2. 主要症状:反复发热(最高39.2℃)、多关节肿痛、面部蝶形红斑
|
||||||
|
3. 辅助检查:ANA 1:640,抗dsDNA抗体阳性,补体C3/C4降低
|
||||||
|
4. 肾脏受累:尿蛋白2+,血肌酐升高
|
||||||
|
|
||||||
|
二、目前诊断
|
||||||
|
系统性红斑狼疮(SLE)伴狼疮性肾炎
|
||||||
|
|
||||||
|
三、讨论要点
|
||||||
|
1. 狼疮性肾炎分型:需行肾穿刺活检明确病理类型
|
||||||
|
2. 免疫抑制方案选择:环磷酰胺 vs 吗替麦考酚酯
|
||||||
|
3. 感染风险评估:长期免疫抑制治疗的感染预防
|
||||||
|
|
||||||
|
四、讨论总结
|
||||||
|
1. 同意目前SLE伴狼疮性肾炎诊断
|
||||||
|
2. 建议尽快行肾穿刺活检
|
||||||
|
3. 根据病理类型制定个体化免疫抑制方案
|
||||||
|
4. 加强感染监测和预防',
|
||||||
|
1004, '赵磊', '副主任医师',
|
||||||
|
1004, '赵磊',
|
||||||
|
1, '2026-06-19 17:00:00', '2026-06-22 15:00:00', false, 0,
|
||||||
|
NULL, 1, 1, '0', 'admin', '2026-06-19 16:30:00')
|
||||||
|
ON CONFLICT (id) DO NOTHING;
|
||||||
|
|
||||||
|
-- ==================== 2. 病程记录提醒 sys_progress_note_reminder ====================
|
||||||
|
INSERT INTO sys_progress_note_reminder (
|
||||||
|
id, encounter_id, patient_name, note_type, deadline,
|
||||||
|
status, remind_user_id, remind_user_name, created_time,
|
||||||
|
tenant_id, delete_flag, create_by, create_time
|
||||||
|
)
|
||||||
|
VALUES
|
||||||
|
-- 已提醒(1)
|
||||||
|
(8100000001, 6001, '张伟', 1, '2026-06-20 18:00:00', 1, 1001, '张明', '2026-06-20 14:30:00', 1, '0', 'admin', '2026-06-20 14:30:00'),
|
||||||
|
(8100000002, 6001, '张伟', 2, '2026-06-23 10:00:00', 1, 1001, '张明', '2026-06-21 09:00:00', 1, '0', 'admin', '2026-06-21 09:00:00'),
|
||||||
|
(8100000003, 6003, 5003, '王强', 6, '2026-06-18 20:30:00', 1, 1004, '赵磊', '2026-06-18 16:00:00', 1, '0', 'admin', '2026-06-18 16:00:00'),
|
||||||
|
-- 待提醒(0)
|
||||||
|
(8100000004, 6004, '刘洋', 2, '2026-06-19 10:00:00', 0, 1002, '李华', '2026-06-19 09:00:00', 1, '0', 'admin', '2026-06-19 09:00:00'),
|
||||||
|
(8100000005, 6006, '赵静', 1, '2026-06-19 06:00:00', 0, 1003, '王芳', '2026-06-18 22:30:00', 1, '0', 'admin', '2026-06-18 22:30:00'),
|
||||||
|
(8100000006, 6007, '孙浩', 2, '2026-06-22 11:00:00', 0, 1001, '张明', '2026-06-20 10:00:00', 1, '0', 'admin', '2026-06-20 10:00:00'),
|
||||||
|
(8100000007, 6008, '周磊', 3, '2026-06-24 09:00:00', 0, 1004, '赵磊', '2026-06-21 09:30:00', 1, '0', 'admin', '2026-06-21 09:30:00'),
|
||||||
|
(8100000008, 6009, '吴秀英', 7, '2026-06-21 16:00:00', 0, 1002, '李华', '2026-06-20 16:00:00', 1, '0', 'admin', '2026-06-20 16:00:00'),
|
||||||
|
(8100000009, 6010, '郑伟', 4, '2026-06-22 15:00:00', 0, 1004, '赵磊', '2026-06-19 16:30:00', 1, '0', 'admin', '2026-06-19 16:30:00')
|
||||||
|
ON CONFLICT (id) DO NOTHING;
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
-- V110: 修复 clinical_pathway_execution 表缺少 create_by 列
|
||||||
|
-- 错误信息: ERROR: column "create_by" of relation "clinical_pathway_execution" does not exist
|
||||||
|
|
||||||
|
-- 添加缺失的 create_by 列
|
||||||
|
ALTER TABLE clinical_pathway_execution ADD COLUMN IF NOT EXISTS create_by VARCHAR(64) DEFAULT '';
|
||||||
|
|
||||||
|
-- 确保其他 HisBaseEntity 列也存在
|
||||||
|
ALTER TABLE clinical_pathway_execution ADD COLUMN IF NOT EXISTS update_by VARCHAR(64) DEFAULT '';
|
||||||
|
ALTER TABLE clinical_pathway_execution ADD COLUMN IF NOT EXISTS update_time TIMESTAMP;
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
-- V106__fix_order_closed_loop_permissions.sql
|
||||||
|
-- 修复医嘱闭环模块权限,将 sys_menu 表中的权限标识更新为与后端 OrderClosedLoopController 一致的权限,并授权给相关角色
|
||||||
|
|
||||||
|
-- 1. 更新已有的“执行追踪”和“闭环统计”菜单的权限标识,与后端控制器的 @PreAuthorize 权限一致
|
||||||
|
UPDATE sys_menu SET perms = 'inpatient:orderclosedloop:list' WHERE menu_id IN (20112, 20113);
|
||||||
|
|
||||||
|
-- 2. 添加按钮级权限:“编辑/执行/催办”权限
|
||||||
|
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, menu_type, visible, status, perms, icon, create_by, create_time, remark)
|
||||||
|
VALUES
|
||||||
|
(20114, '医嘱执行编辑', 20112, 1, '', '', 'F', '0', '0', 'inpatient:orderclosedloop:edit', '#', 'admin', CURRENT_TIMESTAMP, '医嘱闭环-执行追踪编辑按钮权限'),
|
||||||
|
(20115, '闭环统计催办', 20113, 1, '', '', 'F', '0', '0', 'inpatient:orderclosedloop:edit', '#', 'admin', CURRENT_TIMESTAMP, '医嘱闭环-闭环统计催办按钮权限')
|
||||||
|
ON CONFLICT (menu_id) DO NOTHING;
|
||||||
|
|
||||||
|
-- 3. 为“医生”、“护士”、“管理员”相关角色授予这些新引入的权限按钮
|
||||||
|
INSERT INTO sys_role_menu (role_id, menu_id)
|
||||||
|
SELECT r.role_id, m.menu_id
|
||||||
|
FROM sys_role r
|
||||||
|
CROSS JOIN sys_menu m
|
||||||
|
WHERE r.role_name IN ('超级管理员', '管理员', 'admin', '医生', 'doctor', '门诊医生', '住院医生', '护士', 'nurse', '信息科管理员', '可用页面管理员')
|
||||||
|
AND m.menu_id IN (20114, 20115)
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1 FROM sys_role_menu rm WHERE rm.role_id = r.role_id AND rm.menu_id = m.menu_id
|
||||||
|
);
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
-- V2026.0622.02__grant_closed_loop_menu_to_roles.sql
|
||||||
|
-- 修复 V2026.0622.01 的遗漏:将"执行追踪"(20112)和"闭环统计"(20113)菜单本身
|
||||||
|
-- 以及按钮权限(20114, 20115)一起授予相关角色。
|
||||||
|
-- 原脚本只给角色授予了 20114/20115 按钮,漏掉了 20112/20113 父菜单,
|
||||||
|
-- 导致 inpatient:orderclosedloop:list 权限无法加载到用户权限集合中。
|
||||||
|
|
||||||
|
-- 1. 将菜单 20112(执行追踪)、20113(闭环统计)、20114(医嘱执行编辑)、20115(闭环统计催办) 一起授权给相关角色
|
||||||
|
INSERT INTO sys_role_menu (role_id, menu_id)
|
||||||
|
SELECT r.role_id, m.menu_id
|
||||||
|
FROM sys_role r
|
||||||
|
CROSS JOIN sys_menu m
|
||||||
|
WHERE r.role_name IN (
|
||||||
|
'超级管理员', '管理员', 'admin',
|
||||||
|
'医生', 'doctor', '门诊医生', '住院医生',
|
||||||
|
'护士', 'nurse',
|
||||||
|
'信息科管理员', '可用页面管理员'
|
||||||
|
)
|
||||||
|
AND m.menu_id IN (20112, 20113, 20114, 20115)
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1 FROM sys_role_menu rm
|
||||||
|
WHERE rm.role_id = r.role_id AND rm.menu_id = m.menu_id
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 2. 兜底:对于 role_key 包含 doctor/nurse/admin 的角色也进行授权,
|
||||||
|
-- 防止某些角色 role_name 为中文自定义名称(如"内科医生")但 role_key 仍匹配的情况
|
||||||
|
INSERT INTO sys_role_menu (role_id, menu_id)
|
||||||
|
SELECT r.role_id, m.menu_id
|
||||||
|
FROM sys_role r
|
||||||
|
CROSS JOIN sys_menu m
|
||||||
|
WHERE (r.role_key LIKE '%doctor%' OR r.role_key LIKE '%nurse%' OR r.role_key = 'admin')
|
||||||
|
AND m.menu_id IN (20112, 20113, 20114, 20115)
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1 FROM sys_role_menu rm
|
||||||
|
WHERE rm.role_id = r.role_id AND rm.menu_id = m.menu_id
|
||||||
|
);
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
-- 修复医嘱闭环模块权限
|
||||||
|
-- 1. 将 sys_menu 权限标识更新为与后端 OrderClosedLoopController 一致
|
||||||
|
-- 2. 新增按钮级权限菜单
|
||||||
|
-- 3. 为相关角色授予全部闭环菜单权限
|
||||||
|
|
||||||
|
-- 1. 修正"执行追踪"和"闭环统计"菜单的权限标识
|
||||||
|
UPDATE sys_menu SET perms = 'inpatient:orderclosedloop:list' WHERE menu_id IN (20112, 20113);
|
||||||
|
|
||||||
|
-- 2. 添加按钮级权限
|
||||||
|
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, menu_type, visible, status, perms, icon, create_by, create_time, remark)
|
||||||
|
VALUES
|
||||||
|
(20114, '医嘱执行编辑', 20112, 1, '', '', 'F', '0', '0', 'inpatient:orderclosedloop:edit', '#', 'admin', CURRENT_TIMESTAMP, '医嘱闭环-执行追踪编辑按钮权限'),
|
||||||
|
(20115, '闭环统计催办', 20113, 1, '', '', 'F', '0', '0', 'inpatient:orderclosedloop:edit', '#', 'admin', CURRENT_TIMESTAMP, '医嘱闭环-闭环统计催办按钮权限')
|
||||||
|
ON CONFLICT (menu_id) DO NOTHING;
|
||||||
|
|
||||||
|
-- 3. 为相关角色授予闭环模块全部菜单(20112-20115)
|
||||||
|
INSERT INTO sys_role_menu (role_id, menu_id)
|
||||||
|
SELECT r.role_id, m.menu_id
|
||||||
|
FROM sys_role r
|
||||||
|
CROSS JOIN (SELECT unnest(ARRAY[20112, 20113, 20114, 20115]) AS menu_id) m
|
||||||
|
WHERE r.role_name IN ('超级管理员', '管理员', 'admin', '医生', 'doctor', '门诊医生', '住院医生', '护士', 'nurse', '信息科管理员', '可用页面管理员')
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1 FROM sys_role_menu rm WHERE rm.role_id = r.role_id AND rm.menu_id = m.menu_id
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 4. 兜底:按 role_key 模糊匹配(覆盖自定义中文角色名)
|
||||||
|
INSERT INTO sys_role_menu (role_id, menu_id)
|
||||||
|
SELECT r.role_id, m.menu_id
|
||||||
|
FROM sys_role r
|
||||||
|
CROSS JOIN (SELECT unnest(ARRAY[20112, 20113, 20114, 20115]) AS menu_id) m
|
||||||
|
WHERE (r.role_key LIKE '%doctor%' OR r.role_key LIKE '%nurse%' OR r.role_key = 'admin')
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1 FROM sys_role_menu rm WHERE rm.role_id = r.role_id AND rm.menu_id = m.menu_id
|
||||||
|
);
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
ALTER TABLE critical_value_handle_record ALTER COLUMN handler_id DROP NOT NULL;
|
ALTER TABLE critical_value_handle_record ALTER COLUMN handler_id DROP NOT NULL;
|
||||||
ALTER TABLE critical_value_handle_record ADD COLUMN update_time TIMESTAMP;
|
ALTER TABLE critical_value_handle_record ADD COLUMN IF NOT EXISTS update_time TIMESTAMP;
|
||||||
ALTER TABLE critical_value_handle_record ADD COLUMN update_by VARCHAR(64);
|
ALTER TABLE critical_value_handle_record ADD COLUMN IF NOT EXISTS update_by VARCHAR(64);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark, delete_flag)
|
INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
|
||||||
VALUES
|
VALUES
|
||||||
('CDSS', 10001, 10, 'cdss', 'cdss/cdssAlerts/index', NULL, 1, 0, 'C', '0', '0', 'infection:cdss:list', 'fa:', 'admin', CURRENT_TIMESTAMP, NULL, NULL, '临床决策支持', 0),
|
('CDSS', 10001, 10, 'cdss', 'cdss/cdssAlerts/index', 'C', '0', '0', 'infection:cdss:list', 'fa:', 'admin', CURRENT_TIMESTAMP, NULL, NULL, '临床决策支持'),
|
||||||
('区域共享', 20081, 10, 'regionalshare', 'esbmanage/regionalshare/index', NULL, 1, 0, 'C', '0', '0', 'infection:regional:list', 'fa:', 'admin', CURRENT_TIMESTAMP, NULL, NULL, '区域医疗信息共享', 0),
|
('区域共享', 20081, 10, 'regionalshare', 'esbmanage/regionalshare/index', 'C', '0', '0', 'infection:regional:list', 'fa:', 'admin', CURRENT_TIMESTAMP, NULL, NULL, '区域医疗信息共享'),
|
||||||
('EMR数据仓库', 20201, 10, 'data-warehouse', 'emr/data-warehouse/index', NULL, 1, 0, 'C', '0', '0', 'infection:emr:list', 'fa:', 'admin', CURRENT_TIMESTAMP, NULL, NULL, '病历数据仓库', 0),
|
('EMR数据仓库', 20201, 10, 'data-warehouse', 'emr/data-warehouse/index', 'C', '0', '0', 'infection:emr:list', 'fa:', 'admin', CURRENT_TIMESTAMP, NULL, NULL, '病历数据仓库'),
|
||||||
('病案统计明细', 20051, 10, 'statistics-detail', 'mrhomepage/statistics-detail/index', NULL, 1, 0, 'C', '0', '0', 'infection:mrhomepage:list', 'fa:', 'admin', CURRENT_TIMESTAMP, NULL, NULL, '病案统计明细', 0),
|
('病案统计明细', 20051, 10, 'statistics-detail', 'mrhomepage/statistics-detail/index', 'C', '0', '0', 'infection:mrhomepage:list', 'fa:', 'admin', CURRENT_TIMESTAMP, NULL, NULL, '病案统计明细'),
|
||||||
('报表维度', 360, 10, 'ReportDimension', 'reportmanage/ReportDimension', NULL, 1, 0, 'C', '0', '0', 'infection:report:list', 'fa:', 'admin', CURRENT_TIMESTAMP, NULL, NULL, '报表维度', 0);
|
('报表维度', 360, 10, 'ReportDimension', 'reportmanage/ReportDimension', 'C', '0', '0', 'infection:report:list', 'fa:', 'admin', CURRENT_TIMESTAMP, NULL, NULL, '报表维度');
|
||||||
|
|||||||
@@ -1,6 +1,19 @@
|
|||||||
|
-- Create epidemic_report table if not exists
|
||||||
|
CREATE TABLE IF NOT EXISTS epidemic_report (
|
||||||
|
id BIGINT PRIMARY KEY,
|
||||||
|
patient_name VARCHAR(100),
|
||||||
|
id_card VARCHAR(20),
|
||||||
|
phone VARCHAR(20),
|
||||||
|
report_type VARCHAR(50),
|
||||||
|
report_time TIMESTAMP,
|
||||||
|
report_status VARCHAR(20),
|
||||||
|
create_time TIMESTAMP,
|
||||||
|
update_time TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
-- Add auto-screen fields to epidemic_report
|
-- Add auto-screen fields to epidemic_report
|
||||||
ALTER TABLE healthlink_his.epidemic_report ADD COLUMN IF NOT EXISTS screen_result TEXT;
|
ALTER TABLE epidemic_report ADD COLUMN IF NOT EXISTS screen_result TEXT;
|
||||||
ALTER TABLE healthlink_his.epidemic_report ADD COLUMN IF NOT EXISTS screen_time TIMESTAMP;
|
ALTER TABLE epidemic_report ADD COLUMN IF NOT EXISTS screen_time TIMESTAMP;
|
||||||
ALTER TABLE healthlink_his.epidemic_report ADD COLUMN IF NOT EXISTS screen_level VARCHAR(20);
|
ALTER TABLE epidemic_report ADD COLUMN IF NOT EXISTS screen_level VARCHAR(20);
|
||||||
ALTER TABLE healthlink_his.epidemic_report ADD COLUMN IF NOT EXISTS address VARCHAR(500);
|
ALTER TABLE epidemic_report ADD COLUMN IF NOT EXISTS address VARCHAR(500);
|
||||||
ALTER TABLE healthlink_his.epidemic_report ADD COLUMN IF NOT EXISTS contact_phone VARCHAR(50);
|
ALTER TABLE epidemic_report ADD COLUMN IF NOT EXISTS contact_phone VARCHAR(50);
|
||||||
|
|||||||
@@ -1,11 +1,21 @@
|
|||||||
UPDATE sys_menu SET perms = 'mrhomepage:mrhomepage:list' WHERE menu_name = '病案统计明细' AND perms = 'infection:mrhomepage:list';
|
UPDATE sys_menu SET perms = 'mrhomepage:mrhomepage:list' WHERE menu_name = '病案统计明细' AND perms = 'infection:mrhomepage:list';
|
||||||
UPDATE sys_menu SET perms = 'reportmanage:report:list' WHERE menu_name = '报表维度' AND perms = 'infection:report:list';
|
UPDATE sys_menu SET perms = 'reportmanage:report:list' WHERE menu_name = '报表维度' AND perms = 'infection:report:list';
|
||||||
|
|
||||||
INSERT INTO sys_role_menu (role_id, menu_id) VALUES
|
INSERT INTO sys_role_menu (role_id, menu_id)
|
||||||
(1, (SELECT menu_id FROM sys_menu WHERE menu_name = 'CDSS告警' LIMIT 1)),
|
SELECT 1, menu_id FROM sys_menu WHERE menu_name = 'CDSS告警'
|
||||||
(1, (SELECT menu_id FROM sys_menu WHERE menu_name = 'CDSS规则' LIMIT 1)),
|
ON CONFLICT DO NOTHING;
|
||||||
(1, (SELECT menu_id FROM sys_menu WHERE menu_name = '区域共享' LIMIT 1)),
|
INSERT INTO sys_role_menu (role_id, menu_id)
|
||||||
(1, (SELECT menu_id FROM sys_menu WHERE menu_name = 'EMR数据仓库' LIMIT 1)),
|
SELECT 1, menu_id FROM sys_menu WHERE menu_name = 'CDSS规则'
|
||||||
(1, (SELECT menu_id FROM sys_menu WHERE menu_name = '病案统计明细' LIMIT 1)),
|
ON CONFLICT DO NOTHING;
|
||||||
(1, (SELECT menu_id FROM sys_menu WHERE menu_name = '报表维度' LIMIT 1))
|
INSERT INTO sys_role_menu (role_id, menu_id)
|
||||||
|
SELECT 1, menu_id FROM sys_menu WHERE menu_name = '区域共享'
|
||||||
|
ON CONFLICT DO NOTHING;
|
||||||
|
INSERT INTO sys_role_menu (role_id, menu_id)
|
||||||
|
SELECT 1, menu_id FROM sys_menu WHERE menu_name = 'EMR数据仓库'
|
||||||
|
ON CONFLICT DO NOTHING;
|
||||||
|
INSERT INTO sys_role_menu (role_id, menu_id)
|
||||||
|
SELECT 1, menu_id FROM sys_menu WHERE menu_name = '病案统计明细'
|
||||||
|
ON CONFLICT DO NOTHING;
|
||||||
|
INSERT INTO sys_role_menu (role_id, menu_id)
|
||||||
|
SELECT 1, menu_id FROM sys_menu WHERE menu_name = '报表维度'
|
||||||
ON CONFLICT DO NOTHING;
|
ON CONFLICT DO NOTHING;
|
||||||
|
|||||||
@@ -98,15 +98,24 @@ UPDATE esb_code_mapping SET delete_flag = '0' WHERE delete_flag IS NULL;
|
|||||||
ALTER TABLE esb_code_mapping ALTER COLUMN delete_flag SET DEFAULT '0';
|
ALTER TABLE esb_code_mapping ALTER COLUMN delete_flag SET DEFAULT '0';
|
||||||
ALTER TABLE esb_code_mapping ALTER COLUMN delete_flag SET NOT NULL;
|
ALTER TABLE esb_code_mapping ALTER COLUMN delete_flag SET NOT NULL;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
ALTER TABLE cdss_rule ALTER COLUMN delete_flag TYPE VARCHAR(1);
|
ALTER TABLE cdss_rule ALTER COLUMN delete_flag TYPE VARCHAR(1);
|
||||||
UPDATE cdss_rule SET delete_flag = '0' WHERE delete_flag IS NULL;
|
UPDATE cdss_rule SET delete_flag = '0' WHERE delete_flag IS NULL;
|
||||||
ALTER TABLE cdss_rule ALTER COLUMN delete_flag SET DEFAULT '0';
|
ALTER TABLE cdss_rule ALTER COLUMN delete_flag SET DEFAULT '0';
|
||||||
ALTER TABLE cdss_rule ALTER COLUMN delete_flag SET NOT NULL;
|
ALTER TABLE cdss_rule ALTER COLUMN delete_flag SET NOT NULL;
|
||||||
|
EXCEPTION WHEN undefined_table THEN NULL;
|
||||||
|
END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
ALTER TABLE cdss_alert ALTER COLUMN delete_flag TYPE VARCHAR(1);
|
ALTER TABLE cdss_alert ALTER COLUMN delete_flag TYPE VARCHAR(1);
|
||||||
UPDATE cdss_alert SET delete_flag = '0' WHERE delete_flag IS NULL;
|
UPDATE cdss_alert SET delete_flag = '0' WHERE delete_flag IS NULL;
|
||||||
ALTER TABLE cdss_alert ALTER COLUMN delete_flag SET DEFAULT '0';
|
ALTER TABLE cdss_alert ALTER COLUMN delete_flag SET DEFAULT '0';
|
||||||
ALTER TABLE cdss_alert ALTER COLUMN delete_flag SET NOT NULL;
|
ALTER TABLE cdss_alert ALTER COLUMN delete_flag SET NOT NULL;
|
||||||
|
EXCEPTION WHEN undefined_table THEN NULL;
|
||||||
|
END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
DROP INDEX IF EXISTS idx_cdss_rule_code;
|
DROP INDEX IF EXISTS idx_cdss_rule_code;
|
||||||
CREATE UNIQUE INDEX idx_cdss_rule_code ON cdss_rule(rule_code) WHERE delete_flag = '0';
|
CREATE UNIQUE INDEX idx_cdss_rule_code ON cdss_rule(rule_code) WHERE delete_flag = '0';
|
||||||
|
EXCEPTION WHEN undefined_table THEN NULL;
|
||||||
|
END $$;
|
||||||
|
|||||||
@@ -8,7 +8,9 @@ EXCEPTION WHEN duplicate_column THEN
|
|||||||
END $$;
|
END $$;
|
||||||
|
|
||||||
DO $$ BEGIN
|
DO $$ BEGIN
|
||||||
|
ALTER TABLE emr_quality_score ALTER COLUMN tenant_id DROP DEFAULT;
|
||||||
ALTER TABLE emr_quality_score ALTER COLUMN tenant_id TYPE BIGINT USING tenant_id::BIGINT;
|
ALTER TABLE emr_quality_score ALTER COLUMN tenant_id TYPE BIGINT USING tenant_id::BIGINT;
|
||||||
|
ALTER TABLE emr_quality_score ALTER COLUMN tenant_id SET DEFAULT 0;
|
||||||
EXCEPTION WHEN undefined_column THEN
|
EXCEPTION WHEN undefined_column THEN
|
||||||
RAISE NOTICE 'emr_quality_score.tenant_id does not exist';
|
RAISE NOTICE 'emr_quality_score.tenant_id does not exist';
|
||||||
END $$;
|
END $$;
|
||||||
|
|||||||
@@ -1,31 +1,43 @@
|
|||||||
-- 移动端测试数据插入脚本
|
-- 移动端测试数据插入脚本
|
||||||
|
|
||||||
-- 1. 测试护理任务数据
|
-- 1. 测试护理任务数据(仅当表存在时插入)
|
||||||
INSERT INTO nurse_order_execute_record (encounter_id, patient_id, order_type, order_name, execute_status, create_time) VALUES
|
DO $$ BEGIN
|
||||||
(1, 1, '医嘱执行', '测量体温 bid', 'PENDING', CURRENT_TIMESTAMP),
|
INSERT INTO nurse_order_execute_record (encounter_id, patient_id, order_type, order_name, execute_status, create_time) VALUES
|
||||||
(1, 1, '医嘱执行', '测量血压 qd', 'PENDING', CURRENT_TIMESTAMP),
|
(1, 1, '医嘱执行', '测量体温 bid', 'PENDING', CURRENT_TIMESTAMP),
|
||||||
(1, 1, '生命体征', '录入生命体征', 'PENDING', CURRENT_TIMESTAMP),
|
(1, 1, '医嘱执行', '测量血压 qd', 'PENDING', CURRENT_TIMESTAMP),
|
||||||
(1, 2, '医嘱执行', '更换敷料 qd', 'PENDING', CURRENT_TIMESTAMP),
|
(1, 1, '生命体征', '录入生命体征', 'PENDING', CURRENT_TIMESTAMP),
|
||||||
(1, 2, '护理评估', 'Braden压疮评估', 'PENDING', CURRENT_TIMESTAMP),
|
(1, 2, '医嘱执行', '更换敷料 qd', 'PENDING', CURRENT_TIMESTAMP),
|
||||||
(2, 3, '医嘱执行', '测量血糖 tid', 'PENDING', CURRENT_TIMESTAMP),
|
(1, 2, '护理评估', 'Braden压疮评估', 'PENDING', CURRENT_TIMESTAMP),
|
||||||
(2, 3, '医嘱执行', '胰岛素注射', 'PENDING', CURRENT_TIMESTAMP),
|
(2, 3, '医嘱执行', '测量血糖 tid', 'PENDING', CURRENT_TIMESTAMP),
|
||||||
(2, 4, '生命体征', '录入生命体征', 'PENDING', CURRENT_TIMESTAMP),
|
(2, 3, '医嘱执行', '胰岛素注射', 'PENDING', CURRENT_TIMESTAMP),
|
||||||
(2, 4, '护理评估', 'Morse跌倒评估', 'PENDING', CURRENT_TIMESTAMP),
|
(2, 4, '生命体征', '录入生命体征', 'PENDING', CURRENT_TIMESTAMP),
|
||||||
(3, 5, '医嘱执行', '雾化吸入 bid', 'PENDING', CURRENT_TIMESTAMP)
|
(2, 4, '护理评估', 'Morse跌倒评估', 'PENDING', CURRENT_TIMESTAMP),
|
||||||
ON CONFLICT DO NOTHING;
|
(3, 5, '医嘱执行', '雾化吸入 bid', 'PENDING', CURRENT_TIMESTAMP)
|
||||||
|
ON CONFLICT DO NOTHING;
|
||||||
|
EXCEPTION WHEN undefined_table THEN
|
||||||
|
RAISE NOTICE 'nurse_order_execute_record table does not exist, skipping insert';
|
||||||
|
END $$;
|
||||||
|
|
||||||
-- 2. 测试生命体征数据
|
-- 2. 测试生命体征数据(仅当表存在时插入)
|
||||||
INSERT INTO nursing_vital_signs_chart (patient_id, encounter_id, record_time, temperature, pulse, blood_pressure_high, blood_pressure_low, spo2, respiration, pain_score, create_time) VALUES
|
DO $$ BEGIN
|
||||||
(1, 1, CURRENT_TIMESTAMP - INTERVAL '2 hours', 36.5, 72, 120, 80, 98, 18, 2, CURRENT_TIMESTAMP),
|
INSERT INTO nursing_vital_signs_chart (patient_id, encounter_id, record_time, temperature, pulse, blood_pressure_high, blood_pressure_low, spo2, respiration, pain_score, create_time) VALUES
|
||||||
(1, 1, CURRENT_TIMESTAMP - INTERVAL '1 hour', 36.8, 75, 125, 82, 97, 19, 3, CURRENT_TIMESTAMP),
|
(1, 1, CURRENT_TIMESTAMP - INTERVAL '2 hours', 36.5, 72, 120, 80, 98, 18, 2, CURRENT_TIMESTAMP),
|
||||||
(1, 1, CURRENT_TIMESTAMP, 37.0, 78, 128, 85, 96, 20, 4, CURRENT_TIMESTAMP),
|
(1, 1, CURRENT_TIMESTAMP - INTERVAL '1 hour', 36.8, 75, 125, 82, 97, 19, 3, CURRENT_TIMESTAMP),
|
||||||
(2, 2, CURRENT_TIMESTAMP - INTERVAL '1 hour', 36.6, 70, 118, 78, 99, 17, 1, CURRENT_TIMESTAMP),
|
(1, 1, CURRENT_TIMESTAMP, 37.0, 78, 128, 85, 96, 20, 4, CURRENT_TIMESTAMP),
|
||||||
(2, 2, CURRENT_TIMESTAMP, 36.7, 72, 120, 80, 98, 18, 2, CURRENT_TIMESTAMP)
|
(2, 2, CURRENT_TIMESTAMP - INTERVAL '1 hour', 36.6, 70, 118, 78, 99, 17, 1, CURRENT_TIMESTAMP),
|
||||||
ON CONFLICT DO NOTHING;
|
(2, 2, CURRENT_TIMESTAMP, 36.7, 72, 120, 80, 98, 18, 2, CURRENT_TIMESTAMP)
|
||||||
|
ON CONFLICT DO NOTHING;
|
||||||
|
EXCEPTION WHEN undefined_table THEN
|
||||||
|
RAISE NOTICE 'nursing_vital_signs_chart table does not exist, skipping insert';
|
||||||
|
END $$;
|
||||||
|
|
||||||
-- 3. 测试护理评估数据
|
-- 3. 测试护理评估数据(仅当表存在时插入)
|
||||||
INSERT INTO nursing_assessment (encounter_id, patient_id, assessment_type, assessment_tool, total_score, risk_level, detail, create_time) VALUES
|
DO $$ BEGIN
|
||||||
(1, 1, '压疮评估', 'Braden', 16, 'LOW', '{"sensory":4,"moisture":3,"activity":3,"friction":3,"nutrition":4,"mobility":4}', CURRENT_TIMESTAMP),
|
INSERT INTO nursing_assessment (encounter_id, patient_id, assessment_type, assessment_tool, total_score, risk_level, detail, create_time) VALUES
|
||||||
(1, 1, '跌倒评估', 'Morse', 15, 'LOW', '{"history":0,"diagnosis":0,"ambulation":0,"iv":0,"gait":0,"mental":0}', CURRENT_TIMESTAMP),
|
(1, 1, '压疮评估', 'Braden', 16, 'LOW', '{"sensory":4,"moisture":3,"activity":3,"friction":3,"nutrition":4,"mobility":4}', CURRENT_TIMESTAMP),
|
||||||
(2, 2, '营养筛查', 'NRS2002', 1, 'LOW', '{"bmi":0,"weightLoss":0,"intake":1}', CURRENT_TIMESTAMP)
|
(1, 1, '跌倒评估', 'Morse', 15, 'LOW', '{"history":0,"diagnosis":0,"ambulation":0,"iv":0,"gait":0,"mental":0}', CURRENT_TIMESTAMP),
|
||||||
ON CONFLICT DO NOTHING;
|
(2, 2, '营养筛查', 'NRS2002', 1, 'LOW', '{"bmi":0,"weightLoss":0,"intake":1}', CURRENT_TIMESTAMP)
|
||||||
|
ON CONFLICT DO NOTHING;
|
||||||
|
EXCEPTION WHEN undefined_table THEN
|
||||||
|
RAISE NOTICE 'nursing_assessment table does not exist, skipping insert';
|
||||||
|
END $$;
|
||||||
|
|||||||
@@ -1,16 +1,20 @@
|
|||||||
-- 插入测试数据供移动端使用
|
-- 插入测试数据供移动端使用
|
||||||
|
|
||||||
-- 1. 测试患者数据(假设已有基础数据,这里添加护理任务)
|
-- 1. 测试患者数据(仅当表存在时插入)
|
||||||
INSERT INTO nurse_order_execute_record (encounter_id, patient_id, order_type, order_name, execute_status, create_time)
|
DO $$ BEGIN
|
||||||
VALUES
|
INSERT INTO nurse_order_execute_record (encounter_id, patient_id, order_type, order_name, execute_status, create_time)
|
||||||
(1, 1, '医嘱执行', '测量体温 bid', 'PENDING', CURRENT_TIMESTAMP),
|
VALUES
|
||||||
(1, 1, '医嘱执行', '测量血压 qd', 'PENDING', CURRENT_TIMESTAMP),
|
(1, 1, '医嘱执行', '测量体温 bid', 'PENDING', CURRENT_TIMESTAMP),
|
||||||
(1, 1, '生命体征', '录入生命体征', 'PENDING', CURRENT_TIMESTAMP),
|
(1, 1, '医嘱执行', '测量血压 qd', 'PENDING', CURRENT_TIMESTAMP),
|
||||||
(1, 2, '医嘱执行', '更换敷料 qd', 'PENDING', CURRENT_TIMESTAMP),
|
(1, 1, '生命体征', '录入生命体征', 'PENDING', CURRENT_TIMESTAMP),
|
||||||
(1, 2, '护理评估', 'Braden压疮评估', 'PENDING', CURRENT_TIMESTAMP),
|
(1, 2, '医嘱执行', '更换敷料 qd', 'PENDING', CURRENT_TIMESTAMP),
|
||||||
(2, 3, '医嘱执行', '测量血糖 tid', 'PENDING', CURRENT_TIMESTAMP),
|
(1, 2, '护理评估', 'Braden压疮评估', 'PENDING', CURRENT_TIMESTAMP),
|
||||||
(2, 3, '医嘱执行', '胰岛素注射', 'PENDING', CURRENT_TIMESTAMP),
|
(2, 3, '医嘱执行', '测量血糖 tid', 'PENDING', CURRENT_TIMESTAMP),
|
||||||
(2, 4, '生命体征', '录入生命体征', 'PENDING', CURRENT_TIMESTAMP),
|
(2, 3, '医嘱执行', '胰岛素注射', 'PENDING', CURRENT_TIMESTAMP),
|
||||||
(2, 4, '护理评估', 'Morse跌倒评估', 'PENDING', CURRENT_TIMESTAMP),
|
(2, 4, '生命体征', '录入生命体征', 'PENDING', CURRENT_TIMESTAMP),
|
||||||
(3, 5, '医嘱执行', '雾化吸入 bid', 'PENDING', CURRENT_TIMESTAMP)
|
(2, 4, '护理评估', 'Morse跌倒评估', 'PENDING', CURRENT_TIMESTAMP),
|
||||||
ON CONFLICT DO NOTHING;
|
(3, 5, '医嘱执行', '雾化吸入 bid', 'PENDING', CURRENT_TIMESTAMP)
|
||||||
|
ON CONFLICT DO NOTHING;
|
||||||
|
EXCEPTION WHEN undefined_table THEN
|
||||||
|
RAISE NOTICE 'nurse_order_execute_record table does not exist, skipping insert';
|
||||||
|
END $$;
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
-- V84: 审计日志增强 - 扩展字段
|
-- V86: 审计日志增强 - 扩展字段
|
||||||
-- 为系统操作审计日志添加增强字段
|
-- 为系统操作审计日志添加增强字段
|
||||||
|
|
||||||
-- 添加新字段
|
-- 添加新字段
|
||||||
ALTER TABLE sys_audit_log ADD COLUMN IF NOT EXISTS user_agent VARCHAR(512) DEFAULT NULL COMMENT '用户代理';
|
ALTER TABLE sys_audit_log ADD COLUMN IF NOT EXISTS user_agent VARCHAR(512) DEFAULT NULL;
|
||||||
ALTER TABLE sys_audit_log ADD COLUMN IF NOT EXISTS request_method VARCHAR(10) DEFAULT NULL COMMENT '请求方法(GET/POST/PUT/DELETE)';
|
ALTER TABLE sys_audit_log ADD COLUMN IF NOT EXISTS request_method VARCHAR(10) DEFAULT NULL;
|
||||||
ALTER TABLE sys_audit_log ADD COLUMN IF NOT EXISTS response_code INT DEFAULT NULL COMMENT '响应状态码';
|
ALTER TABLE sys_audit_log ADD COLUMN IF NOT EXISTS response_code INT DEFAULT NULL;
|
||||||
ALTER TABLE sys_audit_log ADD COLUMN IF NOT EXISTS response_time_ms BIGINT DEFAULT NULL COMMENT '响应时间(毫秒)';
|
ALTER TABLE sys_audit_log ADD COLUMN IF NOT EXISTS response_time_ms BIGINT DEFAULT NULL;
|
||||||
ALTER TABLE sys_audit_log ADD COLUMN IF NOT EXISTS client_ip VARCHAR(50) DEFAULT NULL COMMENT '客户端IP';
|
ALTER TABLE sys_audit_log ADD COLUMN IF NOT EXISTS client_ip VARCHAR(50) DEFAULT NULL;
|
||||||
ALTER TABLE sys_audit_log ADD COLUMN IF NOT EXISTS tenant_name VARCHAR(100) DEFAULT NULL COMMENT '租户名称';
|
ALTER TABLE sys_audit_log ADD COLUMN IF NOT EXISTS tenant_name VARCHAR(100) DEFAULT NULL;
|
||||||
ALTER TABLE sys_audit_log ADD COLUMN IF NOT EXISTS org_name VARCHAR(100) DEFAULT NULL COMMENT '科室名称';
|
ALTER TABLE sys_audit_log ADD COLUMN IF NOT EXISTS org_name VARCHAR(100) DEFAULT NULL;
|
||||||
ALTER TABLE sys_audit_log ADD COLUMN IF NOT EXISTS risk_level VARCHAR(20) DEFAULT 'LOW' COMMENT '风险级别(LOW/MEDIUM/HIGH/CRITICAL)';
|
ALTER TABLE sys_audit_log ADD COLUMN IF NOT EXISTS risk_level VARCHAR(20) DEFAULT 'LOW';
|
||||||
ALTER TABLE sys_audit_log ADD COLUMN IF NOT EXISTS business_type VARCHAR(50) DEFAULT NULL COMMENT '业务类型(login/logout/query/insert/update/delete/export)';
|
ALTER TABLE sys_audit_log ADD COLUMN IF NOT EXISTS business_type VARCHAR(50) DEFAULT NULL;
|
||||||
|
|
||||||
-- 创建索引
|
-- 创建索引
|
||||||
CREATE INDEX IF NOT EXISTS idx_audit_log_user_id ON sys_audit_log(user_id);
|
CREATE INDEX IF NOT EXISTS idx_audit_log_user_id ON sys_audit_log(user_id);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
-- V87: AI辅助诊疗 - AI诊断建议表
|
-- V87: AI辅助诊疗 - AI诊断建议表
|
||||||
|
|
||||||
CREATE TABLE ai_diagnosis_suggestion (
|
CREATE TABLE IF NOT EXISTS ai_diagnosis_suggestion (
|
||||||
id BIGSERIAL PRIMARY KEY,
|
id BIGSERIAL PRIMARY KEY,
|
||||||
encounter_id BIGINT NOT NULL,
|
encounter_id BIGINT NOT NULL,
|
||||||
patient_id BIGINT NOT NULL,
|
patient_id BIGINT NOT NULL,
|
||||||
@@ -17,16 +17,6 @@ CREATE TABLE ai_diagnosis_suggestion (
|
|||||||
update_by VARCHAR(64)
|
update_by VARCHAR(64)
|
||||||
);
|
);
|
||||||
|
|
||||||
COMMENT ON TABLE ai_diagnosis_suggestion IS 'AI辅助诊疗建议';
|
CREATE INDEX IF NOT EXISTS idx_ai_diag_encounter ON ai_diagnosis_suggestion(encounter_id);
|
||||||
COMMENT ON COLUMN ai_diagnosis_suggestion.id IS '建议ID';
|
CREATE INDEX IF NOT EXISTS idx_ai_diag_patient ON ai_diagnosis_suggestion(patient_id);
|
||||||
COMMENT ON COLUMN ai_diagnosis_suggestion.encounter_id IS '就诊ID';
|
CREATE INDEX IF NOT EXISTS idx_ai_diag_source ON ai_diagnosis_suggestion(suggestion_source);
|
||||||
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);
|
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
-- V86: CDSS规则引擎升级 - 添加优先级/分类字段 + 规则执行历史
|
-- V88: CDSS规则引擎升级 - 添加优先级/分类字段 + 规则执行历史
|
||||||
|
|
||||||
ALTER TABLE cdss_rule ADD COLUMN IF NOT EXISTS priority INT NOT NULL DEFAULT 0;
|
-- 为cdss_rule表添加字段(仅当表存在时)
|
||||||
ALTER TABLE cdss_rule ADD COLUMN IF NOT EXISTS category VARCHAR(64);
|
DO $$ BEGIN
|
||||||
|
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);
|
||||||
|
EXCEPTION WHEN undefined_table THEN
|
||||||
|
RAISE NOTICE 'cdss_rule table does not exist, skipping alter';
|
||||||
|
END $$;
|
||||||
|
|
||||||
COMMENT ON COLUMN cdss_rule.priority IS '规则优先级(0普通 1紧急 2最高)';
|
-- 创建规则执行历史表
|
||||||
COMMENT ON COLUMN cdss_rule.category IS '规则分类';
|
CREATE TABLE IF NOT EXISTS cdss_rule_execution (
|
||||||
|
|
||||||
CREATE TABLE cdss_rule_execution (
|
|
||||||
id BIGSERIAL PRIMARY KEY,
|
id BIGSERIAL PRIMARY KEY,
|
||||||
rule_id BIGINT NOT NULL,
|
rule_id BIGINT NOT NULL,
|
||||||
rule_code VARCHAR(64) NOT NULL,
|
rule_code VARCHAR(64) NOT NULL,
|
||||||
@@ -22,18 +25,8 @@ CREATE TABLE cdss_rule_execution (
|
|||||||
delete_flag CHAR(1) DEFAULT '0'
|
delete_flag CHAR(1) DEFAULT '0'
|
||||||
);
|
);
|
||||||
|
|
||||||
COMMENT ON TABLE cdss_rule_execution IS 'CDSS规则执行历史';
|
-- 创建索引
|
||||||
COMMENT ON COLUMN cdss_rule_execution.id IS '执行记录ID';
|
CREATE INDEX IF NOT EXISTS idx_cdss_exec_rule ON cdss_rule_execution(rule_id);
|
||||||
COMMENT ON COLUMN cdss_rule_execution.rule_id IS '规则ID';
|
CREATE INDEX IF NOT EXISTS idx_cdss_exec_encounter ON cdss_rule_execution(encounter_id);
|
||||||
COMMENT ON COLUMN cdss_rule_execution.rule_code IS '规则编码';
|
CREATE INDEX IF NOT EXISTS idx_cdss_exec_patient ON cdss_rule_execution(patient_id);
|
||||||
COMMENT ON COLUMN cdss_rule_execution.encounter_id IS '就诊ID';
|
CREATE INDEX IF NOT EXISTS idx_cdss_exec_time ON cdss_rule_execution(execution_time);
|
||||||
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);
|
|
||||||
|
|||||||
@@ -1,62 +1,51 @@
|
|||||||
-- V89: 知识图谱 - 添加缺失字段 (description, keywords, severityIndicator, sideEffects, clinicalSignificance, evidenceSource, department, required)
|
-- V89: 知识图谱 - 添加缺失字段
|
||||||
|
|
||||||
-- kg_disease: 添加 description, keywords
|
-- kg_disease: 添加 description, keywords
|
||||||
DO $$ BEGIN
|
DO $$ BEGIN
|
||||||
ALTER TABLE kg_disease ADD COLUMN IF NOT EXISTS description TEXT;
|
ALTER TABLE kg_disease ADD COLUMN IF NOT EXISTS description TEXT;
|
||||||
EXCEPTION WHEN duplicate_column THEN NULL;
|
|
||||||
END $$;
|
|
||||||
DO $$ BEGIN
|
|
||||||
ALTER TABLE kg_disease ADD COLUMN IF NOT EXISTS keywords VARCHAR(512);
|
ALTER TABLE kg_disease ADD COLUMN IF NOT EXISTS keywords VARCHAR(512);
|
||||||
EXCEPTION WHEN duplicate_column THEN NULL;
|
EXCEPTION WHEN undefined_table THEN
|
||||||
|
RAISE NOTICE 'kg_disease table does not exist, skipping';
|
||||||
END $$;
|
END $$;
|
||||||
|
|
||||||
COMMENT ON COLUMN kg_disease.description IS '疾病描述';
|
|
||||||
COMMENT ON COLUMN kg_disease.keywords IS '关键词';
|
|
||||||
|
|
||||||
-- kg_symptom: 添加 severity_indicator
|
-- kg_symptom: 添加 severity_indicator
|
||||||
DO $$ BEGIN
|
DO $$ BEGIN
|
||||||
ALTER TABLE kg_symptom ADD COLUMN IF NOT EXISTS severity_indicator VARCHAR(32);
|
ALTER TABLE kg_symptom ADD COLUMN IF NOT EXISTS severity_indicator VARCHAR(32);
|
||||||
EXCEPTION WHEN duplicate_column THEN NULL;
|
EXCEPTION WHEN undefined_table THEN
|
||||||
|
RAISE NOTICE 'kg_symptom table does not exist, skipping';
|
||||||
END $$;
|
END $$;
|
||||||
|
|
||||||
COMMENT ON COLUMN kg_symptom.severity_indicator IS '严重程度指标';
|
|
||||||
|
|
||||||
-- kg_drug: 添加 side_effects
|
-- kg_drug: 添加 side_effects
|
||||||
DO $$ BEGIN
|
DO $$ BEGIN
|
||||||
ALTER TABLE kg_drug ADD COLUMN IF NOT EXISTS side_effects TEXT;
|
ALTER TABLE kg_drug ADD COLUMN IF NOT EXISTS side_effects TEXT;
|
||||||
EXCEPTION WHEN duplicate_column THEN NULL;
|
EXCEPTION WHEN undefined_table THEN
|
||||||
|
RAISE NOTICE 'kg_drug table does not exist, skipping';
|
||||||
END $$;
|
END $$;
|
||||||
|
|
||||||
COMMENT ON COLUMN kg_drug.side_effects IS '不良反应';
|
|
||||||
|
|
||||||
-- kg_examination: 添加 clinical_significance
|
-- kg_examination: 添加 clinical_significance
|
||||||
DO $$ BEGIN
|
DO $$ BEGIN
|
||||||
ALTER TABLE kg_examination ADD COLUMN IF NOT EXISTS clinical_significance TEXT;
|
ALTER TABLE kg_examination ADD COLUMN IF NOT EXISTS clinical_significance TEXT;
|
||||||
EXCEPTION WHEN duplicate_column THEN NULL;
|
EXCEPTION WHEN undefined_table THEN
|
||||||
|
RAISE NOTICE 'kg_examination table does not exist, skipping';
|
||||||
END $$;
|
END $$;
|
||||||
|
|
||||||
COMMENT ON COLUMN kg_examination.clinical_significance IS '临床意义';
|
|
||||||
|
|
||||||
-- kg_entity_relation: 添加 evidence_source
|
-- kg_entity_relation: 添加 evidence_source
|
||||||
DO $$ BEGIN
|
DO $$ BEGIN
|
||||||
ALTER TABLE kg_entity_relation ADD COLUMN IF NOT EXISTS evidence_source VARCHAR(512);
|
ALTER TABLE kg_entity_relation ADD COLUMN IF NOT EXISTS evidence_source VARCHAR(512);
|
||||||
EXCEPTION WHEN duplicate_column THEN NULL;
|
EXCEPTION WHEN undefined_table THEN
|
||||||
|
RAISE NOTICE 'kg_entity_relation table does not exist, skipping';
|
||||||
END $$;
|
END $$;
|
||||||
|
|
||||||
COMMENT ON COLUMN kg_entity_relation.evidence_source IS '证据来源';
|
|
||||||
|
|
||||||
-- kg_clinical_pathway: 添加 department
|
-- kg_clinical_pathway: 添加 department
|
||||||
DO $$ BEGIN
|
DO $$ BEGIN
|
||||||
ALTER TABLE kg_clinical_pathway ADD COLUMN IF NOT EXISTS department VARCHAR(128);
|
ALTER TABLE kg_clinical_pathway ADD COLUMN IF NOT EXISTS department VARCHAR(128);
|
||||||
EXCEPTION WHEN duplicate_column THEN NULL;
|
EXCEPTION WHEN undefined_table THEN
|
||||||
|
RAISE NOTICE 'kg_clinical_pathway table does not exist, skipping';
|
||||||
END $$;
|
END $$;
|
||||||
|
|
||||||
COMMENT ON COLUMN kg_clinical_pathway.department IS '所属科室';
|
|
||||||
|
|
||||||
-- kg_pathway_step: 添加 required
|
-- kg_pathway_step: 添加 required
|
||||||
DO $$ BEGIN
|
DO $$ BEGIN
|
||||||
ALTER TABLE kg_pathway_step ADD COLUMN IF NOT EXISTS required CHAR(1) DEFAULT '1';
|
ALTER TABLE kg_pathway_step ADD COLUMN IF NOT EXISTS required CHAR(1) DEFAULT '1';
|
||||||
EXCEPTION WHEN duplicate_column THEN NULL;
|
EXCEPTION WHEN undefined_table THEN
|
||||||
|
RAISE NOTICE 'kg_pathway_step table does not exist, skipping';
|
||||||
END $$;
|
END $$;
|
||||||
|
|
||||||
COMMENT ON COLUMN kg_pathway_step.required IS '是否必选(1-是 0-否)';
|
|
||||||
|
|||||||
@@ -15,17 +15,6 @@ CREATE TABLE IF NOT EXISTS mp_nursing_task (
|
|||||||
create_by VARCHAR(64)
|
create_by VARCHAR(64)
|
||||||
);
|
);
|
||||||
|
|
||||||
COMMENT ON TABLE mp_nursing_task IS '移动护理-护理任务';
|
|
||||||
COMMENT ON COLUMN mp_nursing_task.id IS '主键ID';
|
|
||||||
COMMENT ON COLUMN mp_nursing_task.patient_id IS '患者ID';
|
|
||||||
COMMENT ON COLUMN mp_nursing_task.encounter_id IS '就诊ID';
|
|
||||||
COMMENT ON COLUMN mp_nursing_task.nurse_id IS '护士ID';
|
|
||||||
COMMENT ON COLUMN mp_nursing_task.task_type IS '任务类型';
|
|
||||||
COMMENT ON COLUMN mp_nursing_task.task_content IS '任务内容';
|
|
||||||
COMMENT ON COLUMN mp_nursing_task.task_status IS '任务状态: PENDING/IN_PROGRESS/COMPLETED/CANCELLED';
|
|
||||||
COMMENT ON COLUMN mp_nursing_task.due_time IS '截止时间';
|
|
||||||
COMMENT ON COLUMN mp_nursing_task.complete_time IS '完成时间';
|
|
||||||
|
|
||||||
-- 移动护理小程序 - 生命体征记录表
|
-- 移动护理小程序 - 生命体征记录表
|
||||||
CREATE TABLE IF NOT EXISTS mp_vital_sign_record (
|
CREATE TABLE IF NOT EXISTS mp_vital_sign_record (
|
||||||
id BIGSERIAL PRIMARY KEY,
|
id BIGSERIAL PRIMARY KEY,
|
||||||
@@ -47,21 +36,6 @@ CREATE TABLE IF NOT EXISTS mp_vital_sign_record (
|
|||||||
create_by VARCHAR(64)
|
create_by VARCHAR(64)
|
||||||
);
|
);
|
||||||
|
|
||||||
COMMENT ON TABLE mp_vital_sign_record IS '移动护理-生命体征记录';
|
|
||||||
COMMENT ON COLUMN mp_vital_sign_record.id IS '主键ID';
|
|
||||||
COMMENT ON COLUMN mp_vital_sign_record.patient_id IS '患者ID';
|
|
||||||
COMMENT ON COLUMN mp_vital_sign_record.encounter_id IS '就诊ID';
|
|
||||||
COMMENT ON COLUMN mp_vital_sign_record.nurse_id IS '记录护士ID';
|
|
||||||
COMMENT ON COLUMN mp_vital_sign_record.record_time IS '记录时间';
|
|
||||||
COMMENT ON COLUMN mp_vital_sign_record.temperature IS '体温(℃)';
|
|
||||||
COMMENT ON COLUMN mp_vital_sign_record.pulse IS '脉搏(次/分)';
|
|
||||||
COMMENT ON COLUMN mp_vital_sign_record.respiration IS '呼吸(次/分)';
|
|
||||||
COMMENT ON COLUMN mp_vital_sign_record.systolic_bp IS '收缩压(mmHg)';
|
|
||||||
COMMENT ON COLUMN mp_vital_sign_record.diastolic_bp IS '舒张压(mmHg)';
|
|
||||||
COMMENT ON COLUMN mp_vital_sign_record.blood_oxygen IS '血氧饱和度(%)';
|
|
||||||
COMMENT ON COLUMN mp_vital_sign_record.height IS '身高(cm)';
|
|
||||||
COMMENT ON COLUMN mp_vital_sign_record.weight IS '体重(kg)';
|
|
||||||
|
|
||||||
-- 移动护理小程序 - 护理评估记录表
|
-- 移动护理小程序 - 护理评估记录表
|
||||||
CREATE TABLE IF NOT EXISTS mp_assessment_record (
|
CREATE TABLE IF NOT EXISTS mp_assessment_record (
|
||||||
id BIGSERIAL PRIMARY KEY,
|
id BIGSERIAL PRIMARY KEY,
|
||||||
@@ -79,15 +53,3 @@ CREATE TABLE IF NOT EXISTS mp_assessment_record (
|
|||||||
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
create_by VARCHAR(64)
|
create_by VARCHAR(64)
|
||||||
);
|
);
|
||||||
|
|
||||||
COMMENT ON TABLE mp_assessment_record IS '移动护理-护理评估记录';
|
|
||||||
COMMENT ON COLUMN mp_assessment_record.id IS '主键ID';
|
|
||||||
COMMENT ON COLUMN mp_assessment_record.patient_id IS '患者ID';
|
|
||||||
COMMENT ON COLUMN mp_assessment_record.encounter_id IS '就诊ID';
|
|
||||||
COMMENT ON COLUMN mp_assessment_record.nurse_id IS '评估护士ID';
|
|
||||||
COMMENT ON COLUMN mp_assessment_record.assessment_type IS '评估类型';
|
|
||||||
COMMENT ON COLUMN mp_assessment_record.assessment_content IS '评估内容';
|
|
||||||
COMMENT ON COLUMN mp_assessment_record.assessment_result IS '评估结果';
|
|
||||||
COMMENT ON COLUMN mp_assessment_record.score IS '评分';
|
|
||||||
COMMENT ON COLUMN mp_assessment_record.risk_level IS '风险等级';
|
|
||||||
COMMENT ON COLUMN mp_assessment_record.record_time IS '评估时间';
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
-- 生成时间: 2026-06-19
|
-- 生成时间: 2026-06-19
|
||||||
|
|
||||||
-- ==================== 1. mr_borrowing 病案借阅 ====================
|
-- ==================== 1. mr_borrowing 病案借阅 ====================
|
||||||
INSERT INTO healthlink_his.mr_borrowing (id, medical_record_id, patient_name, mr_number, borrower_name, borrower_dept, borrow_reason, borrow_date, expected_return_date, actual_return_date, status, approver_name, approve_time, tenant_id, delete_flag, create_by, create_time)
|
INSERT INTO mr_borrowing (id, medical_record_id, patient_name, mr_number, borrower_name, borrower_dept, borrow_reason, borrow_date, expected_return_date, actual_return_date, status, approver_name, approve_time, tenant_id, delete_flag, create_by, create_time)
|
||||||
VALUES
|
VALUES
|
||||||
-- 待审批(0)
|
-- 待审批(0)
|
||||||
(9000000001, 6001, '测试患者甲', 'MR202606001', '李医生', '神经内科', '科研论文需要', '2026-06-15 09:00:00', '2026-06-22 17:00:00', NULL, 0, NULL, NULL, 1, '0', 'admin', '2026-06-15 09:00:00'),
|
(9000000001, 6001, '测试患者甲', 'MR202606001', '李医生', '神经内科', '科研论文需要', '2026-06-15 09:00:00', '2026-06-22 17:00:00', NULL, 0, NULL, NULL, 1, '0', 'admin', '2026-06-15 09:00:00'),
|
||||||
@@ -20,10 +20,11 @@ VALUES
|
|||||||
(9000000008, 6008, '测试患者丁', 'MR202606008', '陈医生', '超声诊断科', '个人学习', '2026-06-17 16:00:00', '2026-06-24 17:00:00', NULL, 5, '张院长', '2026-06-18 09:00:00', 1, '0', 'admin', '2026-06-17 16:00:00'),
|
(9000000008, 6008, '测试患者丁', 'MR202606008', '陈医生', '超声诊断科', '个人学习', '2026-06-17 16:00:00', '2026-06-24 17:00:00', NULL, 5, '张院长', '2026-06-18 09:00:00', 1, '0', 'admin', '2026-06-17 16:00:00'),
|
||||||
-- 更多借阅记录
|
-- 更多借阅记录
|
||||||
(9000000009, 6009, '测试患者戊', 'MR202606009', '黄医生', '神经内科', '会诊需要', '2026-06-19 08:00:00', '2026-06-26 17:00:00', NULL, 0, NULL, NULL, 1, '0', 'admin', '2026-06-19 08:00:00'),
|
(9000000009, 6009, '测试患者戊', 'MR202606009', '黄医生', '神经内科', '会诊需要', '2026-06-19 08:00:00', '2026-06-26 17:00:00', NULL, 0, NULL, NULL, 1, '0', 'admin', '2026-06-19 08:00:00'),
|
||||||
(9000000010, 6010, '测试患者己', 'MR202606010', '林护士', '内分泌科', '护理查房', '2026-06-16 13:00:00', '2026-06-23 17:00:00', '2026-06-20 10:00:00', 3, '赵科长', '2026-06-16 14:00:00', 1, '0', 'admin', '2026-06-16 13:00:00');
|
(9000000010, 6010, '测试患者己', 'MR202606010', '林护士', '内分泌科', '护理查房', '2026-06-16 13:00:00', '2026-06-23 17:00:00', '2026-06-20 10:00:00', 3, '赵科长', '2026-06-16 14:00:00', 1, '0', 'admin', '2026-06-16 13:00:00')
|
||||||
|
ON CONFLICT DO NOTHING;
|
||||||
|
|
||||||
-- ==================== 2. mr_sealing 病案封存 ====================
|
-- ==================== 2. mr_sealing 病案封存 ====================
|
||||||
INSERT INTO healthlink_his.mr_sealing (id, medical_record_id, patient_name, mr_number, seal_reason, seal_type, seal_date, seal_by, unseal_date, unseal_by, unseal_reason, status, tenant_id, delete_flag, create_by, create_time)
|
INSERT INTO mr_sealing (id, medical_record_id, patient_name, mr_number, seal_reason, seal_type, seal_date, seal_by, unseal_date, unseal_by, unseal_reason, status, tenant_id, delete_flag, create_by, create_time)
|
||||||
VALUES
|
VALUES
|
||||||
-- 已封存(0)
|
-- 已封存(0)
|
||||||
(9100000001, 6001, '测试患者甲', 'MR202606001', '医疗纠纷封存', 2, '2026-06-10 09:00:00', '张院长', NULL, NULL, NULL, 0, 1, '0', 'admin', '2026-06-10 09:00:00'),
|
(9100000001, 6001, '测试患者甲', 'MR202606001', '医疗纠纷封存', 2, '2026-06-10 09:00:00', '张院长', NULL, NULL, NULL, 0, 1, '0', 'admin', '2026-06-10 09:00:00'),
|
||||||
@@ -37,7 +38,7 @@ VALUES
|
|||||||
(9100000007, 6009, '测试患者戊', 'MR202606009', '医疗纠纷封存', 2, '2026-06-19 09:30:00', '张院长', NULL, NULL, NULL, 0, 1, '0', 'admin', '2026-06-19 09:30:00');
|
(9100000007, 6009, '测试患者戊', 'MR202606009', '医疗纠纷封存', 2, '2026-06-19 09:30:00', '张院长', NULL, NULL, NULL, 0, 1, '0', 'admin', '2026-06-19 09:30:00');
|
||||||
|
|
||||||
-- ==================== 3. mr_tracking 病案示踪 ====================
|
-- ==================== 3. mr_tracking 病案示踪 ====================
|
||||||
INSERT INTO healthlink_his.mr_tracking (id, medical_record_id, mr_number, patient_name, location, location_type, status, moved_by, move_time, tenant_id, delete_flag, create_by, create_time)
|
INSERT INTO mr_tracking (id, medical_record_id, mr_number, patient_name, location, location_type, status, moved_by, move_time, tenant_id, delete_flag, create_by, create_time)
|
||||||
VALUES
|
VALUES
|
||||||
-- 在架(IN_SHELF)
|
-- 在架(IN_SHELF)
|
||||||
(9200000001, 6001, 'MR202606001', '测试患者甲', '病案室-A区-01架-03层', 'STORAGE', 'IN_SHELF', '系统管理员', '2026-06-15 08:00:00', 1, '0', 'admin', '2026-06-15 08:00:00'),
|
(9200000001, 6001, 'MR202606001', '测试患者甲', '病案室-A区-01架-03层', 'STORAGE', 'IN_SHELF', '系统管理员', '2026-06-15 08:00:00', 1, '0', 'admin', '2026-06-15 08:00:00'),
|
||||||
@@ -56,7 +57,7 @@ VALUES
|
|||||||
(9200000010, 6009, 'MR202606009', '测试患者戊', '神经内科', 'DEPT', 'BORROWED', '黄医生', '2026-06-19 08:30:00', 1, '0', 'admin', '2026-06-19 08:30:00');
|
(9200000010, 6009, 'MR202606009', '测试患者戊', '神经内科', 'DEPT', 'BORROWED', '黄医生', '2026-06-19 08:30:00', 1, '0', 'admin', '2026-06-19 08:30:00');
|
||||||
|
|
||||||
-- ==================== 4. mr_death_discussion 死亡病例讨论 ====================
|
-- ==================== 4. mr_death_discussion 死亡病例讨论 ====================
|
||||||
INSERT INTO healthlink_his.mr_death_discussion (id, patient_id, patient_name, encounter_id, death_date, discussion_date, deadline_date, host_doctor_id, host_doctor_name, host_title, participants, discussion_conclusion, improvement_measures, status, is_overdue, tenant_id, delete_flag, create_by, create_time)
|
INSERT INTO mr_death_discussion (id, patient_id, patient_name, encounter_id, death_date, discussion_date, deadline_date, host_doctor_id, host_doctor_name, host_title, participants, discussion_conclusion, improvement_measures, status, is_overdue, tenant_id, delete_flag, create_by, create_time)
|
||||||
VALUES
|
VALUES
|
||||||
-- 待讨论(0) - 未超期
|
-- 待讨论(0) - 未超期
|
||||||
(9300000001, 5001, '测试患者甲', 6001, '2026-06-17 03:20:00', NULL, '2026-06-24 03:20:00', 1001, '张院长', '主任医师', NULL, NULL, NULL, 0, false, 1, '0', 'admin', '2026-06-17 08:00:00'),
|
(9300000001, 5001, '测试患者甲', 6001, '2026-06-17 03:20:00', NULL, '2026-06-24 03:20:00', 1001, '张院长', '主任医师', NULL, NULL, NULL, 0, false, 1, '0', 'admin', '2026-06-17 08:00:00'),
|
||||||
|
|||||||
@@ -231,6 +231,9 @@
|
|||||||
AND T1.status_enum != 6
|
AND T1.status_enum != 6
|
||||||
</if>
|
</if>
|
||||||
</if>
|
</if>
|
||||||
|
<if test='deptId != null'>
|
||||||
|
AND T1.organization_id = #{deptId}
|
||||||
|
</if>
|
||||||
) AS T9
|
) AS T9
|
||||||
${ew.customSqlSegment}
|
${ew.customSqlSegment}
|
||||||
ORDER BY T9.register_time DESC
|
ORDER BY T9.register_time DESC
|
||||||
|
|||||||
@@ -91,13 +91,13 @@
|
|||||||
T1.dosage_instruction AS dosage_instruction,
|
T1.dosage_instruction AS dosage_instruction,
|
||||||
T1.chrgitm_lv as chrgitm_lv
|
T1.chrgitm_lv as chrgitm_lv
|
||||||
FROM med_medication_definition AS t1
|
FROM med_medication_definition AS t1
|
||||||
INNER JOIN med_medication AS T2 ON T2.medication_def_id = t1.ID
|
LEFT JOIN med_medication AS T2 ON T2.medication_def_id = t1.ID
|
||||||
AND T2.delete_flag = '0' AND T2.status_enum = #{statusEnum}
|
AND T2.delete_flag = '0' AND T2.status_enum = #{statusEnum}
|
||||||
LEFT JOIN adm_supplier AS T3 ON T3.ID = t1.supply_id AND T3.delete_flag = '0'
|
LEFT JOIN adm_supplier AS T3 ON T3.ID = t1.supply_id AND T3.delete_flag = '0'
|
||||||
LEFT JOIN adm_charge_item_definition AS T5 ON T5.instance_id = t1.ID AND T5.delete_flag = '0' AND T5.status_enum = #{statusEnum}
|
LEFT JOIN adm_charge_item_definition AS T5 ON T5.instance_id = t1.ID AND T5.delete_flag = '0' AND T5.status_enum = #{statusEnum}
|
||||||
INNER JOIN adm_organization_location AS T6 ON T6.distribution_category_code = t1.category_code AND T6.delete_flag = '0' AND T6.item_code = '1' AND T6.organization_id = #{organizationId} AND (CURRENT_TIME :: time (6) BETWEEN T6.start_time AND T6.end_time)
|
LEFT JOIN adm_organization_location AS T6 ON T6.distribution_category_code = t1.category_code AND T6.delete_flag = '0' AND T6.item_code = '1' AND T6.organization_id = #{organizationId} AND (CURRENT_TIME :: time (6) BETWEEN T6.start_time AND T6.end_time)
|
||||||
WHERE t1.delete_flag = '0'
|
WHERE t1.delete_flag = '0'
|
||||||
AND T2.status_enum = #{statusEnum}
|
AND (T2.status_enum = #{statusEnum} OR T2.status_enum IS NULL)
|
||||||
<if test="categoryCode != null and categoryCode != ''">
|
<if test="categoryCode != null and categoryCode != ''">
|
||||||
<!-- 🔧 BugFix: 支持两种匹配方式 -->
|
<!-- 🔧 BugFix: 支持两种匹配方式 -->
|
||||||
<!-- 1. 直接匹配:distribution_category_code = category_code(都是数字代码) -->
|
<!-- 1. 直接匹配:distribution_category_code = category_code(都是数字代码) -->
|
||||||
|
|||||||
@@ -62,7 +62,8 @@
|
|||||||
T1.suffering_flag AS suffering_flag,
|
T1.suffering_flag AS suffering_flag,
|
||||||
T1.dosage_instruction AS dosage_instruction,
|
T1.dosage_instruction AS dosage_instruction,
|
||||||
T2.part_percent AS part_percent,
|
T2.part_percent AS part_percent,
|
||||||
ccd.name AS condition_definition_name
|
ccd.name AS condition_definition_name,
|
||||||
|
T4.account_id AS account_id
|
||||||
FROM med_medication_request AS T1
|
FROM med_medication_request AS T1
|
||||||
LEFT JOIN med_medication_definition AS T2 ON T2.ID = T1.medication_id
|
LEFT JOIN med_medication_definition AS T2 ON T2.ID = T1.medication_id
|
||||||
AND T2.delete_flag = '0'
|
AND T2.delete_flag = '0'
|
||||||
@@ -115,7 +116,8 @@
|
|||||||
T1.chinese_herbs_dose_quantity AS chinese_herbs_dose_quantity,
|
T1.chinese_herbs_dose_quantity AS chinese_herbs_dose_quantity,
|
||||||
T1.suffering_flag AS suffering_flag,
|
T1.suffering_flag AS suffering_flag,
|
||||||
T2.part_percent AS part_percent,
|
T2.part_percent AS part_percent,
|
||||||
ccd.name AS condition_definition_name
|
ccd.name AS condition_definition_name,
|
||||||
|
T4.account_id AS account_id
|
||||||
FROM med_medication_request AS T1
|
FROM med_medication_request AS T1
|
||||||
LEFT JOIN med_medication_definition AS T2 ON T2.ID = T1.medication_id
|
LEFT JOIN med_medication_definition AS T2 ON T2.ID = T1.medication_id
|
||||||
AND T2.delete_flag = '0'
|
AND T2.delete_flag = '0'
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
<mapper namespace="com.healthlink.his.web.pharmacymanage.mapper.InHospitalReturnMedicineAppMapper">
|
<mapper namespace="com.healthlink.his.web.pharmacy.dispense.mapper.InHospitalReturnMedicineAppMapper">
|
||||||
<select id="selectEncounterInfoListPage" resultType="com.healthlink.his.web.pharmacy.dispense.dto.EncounterInfoDto">
|
<select id="selectEncounterInfoListPage" resultType="com.healthlink.his.web.pharmacy.dispense.dto.EncounterInfoDto">
|
||||||
SELECT MAX(ii.reception_time) AS reception_time,
|
SELECT MAX(ii.reception_time) AS reception_time,
|
||||||
MAX(ii.start_time) AS start_time,
|
MAX(ii.start_time) AS start_time,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
ybapp.config.url=http://localhost:18079/healthlink-his/yb/yb
|
ybapp.config.url=http://api.heylihao.cloud/fsi/api
|
||||||
ybapp.config.api.key=your_api_key_123
|
ybapp.config.api.key=your_api_key_123
|
||||||
ybapp.config.timeout=5000
|
ybapp.config.timeout=5000
|
||||||
ybapp.config.fixmedinsCode=H22010402403
|
ybapp.config.fixmedinsCode=H22010402403
|
||||||
ybapp.config.eleUrl=http://localhost:18079/healthlink-his/yb/ybElep
|
ybapp.config.eleUrl=http://api.heylihao.cloud/fsi/api
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package com.healthlink.his.emr.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class EmrRevisionWithPatientDto {
|
||||||
|
private Long id;
|
||||||
|
private Long emrId;
|
||||||
|
private Long encounterId;
|
||||||
|
private Integer revisionNumber;
|
||||||
|
private Long operatorId;
|
||||||
|
private String operatorName;
|
||||||
|
private String operationType;
|
||||||
|
private String diffContent;
|
||||||
|
private String snapshotContent;
|
||||||
|
private Date createTime;
|
||||||
|
private String patientName;
|
||||||
|
private String patientGender;
|
||||||
|
private String patientAge;
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package com.healthlink.his.emr.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.healthlink.his.emr.domain.EmrQualityScore;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface EmrQualityScoreDataMapper extends BaseMapper<EmrQualityScore> {
|
||||||
|
|
||||||
|
List<EmrQualityScore> selectByEncounterId(@Param("encounterId") Long encounterId);
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ package com.healthlink.his.emr.mapper;
|
|||||||
|
|
||||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
import com.healthlink.his.emr.domain.EmrRevision;
|
import com.healthlink.his.emr.domain.EmrRevision;
|
||||||
|
import com.healthlink.his.emr.dto.EmrRevisionWithPatientDto;
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
import org.apache.ibatis.annotations.Param;
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
@@ -13,4 +14,20 @@ public interface EmrRevisionMapper extends BaseMapper<EmrRevision> {
|
|||||||
List<EmrRevision> selectByEmrId(@Param("emrId") Long emrId);
|
List<EmrRevision> selectByEmrId(@Param("emrId") Long emrId);
|
||||||
|
|
||||||
EmrRevision selectLatest(@Param("emrId") Long emrId);
|
EmrRevision selectLatest(@Param("emrId") Long emrId);
|
||||||
|
|
||||||
|
List<EmrRevisionWithPatientDto> selectPageWithPatient(
|
||||||
|
@Param("emrId") Long emrId,
|
||||||
|
@Param("operatorName") String operatorName,
|
||||||
|
@Param("patientName") String patientName,
|
||||||
|
@Param("doctorName") String doctorName,
|
||||||
|
@Param("emrType") String emrType,
|
||||||
|
@Param("offset") int offset,
|
||||||
|
@Param("pageSize") int pageSize);
|
||||||
|
|
||||||
|
long countPageWithPatient(
|
||||||
|
@Param("emrId") Long emrId,
|
||||||
|
@Param("operatorName") String operatorName,
|
||||||
|
@Param("patientName") String patientName,
|
||||||
|
@Param("doctorName") String doctorName,
|
||||||
|
@Param("emrType") String emrType);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package com.healthlink.his.yb.mock.domain;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
|
import lombok.Data;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@TableName("yb_psn_info")
|
||||||
|
public class YbPsnInfo {
|
||||||
|
@TableId(type = IdType.ASSIGN_ID)
|
||||||
|
private Long id;
|
||||||
|
private String psnNo;
|
||||||
|
private String psnName;
|
||||||
|
private String sexCode;
|
||||||
|
private String sexName;
|
||||||
|
private String birthDate;
|
||||||
|
private String idCard;
|
||||||
|
private String insurType;
|
||||||
|
private String insurArea;
|
||||||
|
private String cardNo;
|
||||||
|
private java.math.BigDecimal balance;
|
||||||
|
private String status;
|
||||||
|
private Date createTime;
|
||||||
|
private Date updateTime;
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package com.healthlink.his.yb.mock.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.healthlink.his.yb.mock.domain.YbPsnInfo;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface YbPsnInfoMapper extends BaseMapper<YbPsnInfo> {
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<!DOCTYPE mapper
|
||||||
|
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
|
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<mapper namespace="com.healthlink.his.emr.mapper.EmrQualityScoreDataMapper">
|
||||||
|
|
||||||
|
<select id="selectByEncounterId" resultType="com.healthlink.his.emr.domain.EmrQualityScore">
|
||||||
|
SELECT * FROM emr_quality_score
|
||||||
|
WHERE encounter_id = #{encounterId}
|
||||||
|
ORDER BY check_time DESC
|
||||||
|
</select>
|
||||||
|
|
||||||
|
</mapper>
|
||||||
@@ -17,4 +17,31 @@
|
|||||||
LIMIT 1
|
LIMIT 1
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
<sql id="queryWithPatient">
|
||||||
|
FROM emr_revision r
|
||||||
|
LEFT JOIN emr_search_index s ON r.emr_id = s.emr_id
|
||||||
|
<where>
|
||||||
|
<if test="emrId != null">AND r.emr_id = #{emrId}</if>
|
||||||
|
<if test="operatorName != null and operatorName != ''">AND r.operator_name LIKE '%' || #{operatorName} || '%'</if>
|
||||||
|
<if test="patientName != null and patientName != ''">AND s.patient_name LIKE '%' || #{patientName} || '%'</if>
|
||||||
|
<if test="doctorName != null and doctorName != ''">AND s.doctor_name LIKE '%' || #{doctorName} || '%'</if>
|
||||||
|
<if test="emrType != null and emrType != ''">AND s.emr_type = #{emrType}</if>
|
||||||
|
</where>
|
||||||
|
</sql>
|
||||||
|
|
||||||
|
<select id="selectPageWithPatient" resultType="com.healthlink.his.emr.dto.EmrRevisionWithPatientDto">
|
||||||
|
SELECT r.id, r.emr_id, r.encounter_id, r.revision_number, r.operator_id,
|
||||||
|
r.operator_name, r.operation_type, r.diff_content, r.snapshot_content, r.create_time,
|
||||||
|
s.patient_name, s.patient_gender, s.doctor_name, s.emr_type, s.emr_title,
|
||||||
|
s.department_name, s.encounter_no
|
||||||
|
<include refid="queryWithPatient"/>
|
||||||
|
ORDER BY r.create_time DESC
|
||||||
|
LIMIT #{pageSize} OFFSET #{offset}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="countPageWithPatient" resultType="long">
|
||||||
|
SELECT COUNT(*)
|
||||||
|
<include refid="queryWithPatient"/>
|
||||||
|
</select>
|
||||||
|
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|||||||
@@ -20,9 +20,13 @@
|
|||||||
SELECT
|
SELECT
|
||||||
COUNT(*) AS total_count,
|
COUNT(*) AS total_count,
|
||||||
SUM(CASE WHEN status = 'COMPLETED' THEN 1 ELSE 0 END) AS completed_count,
|
SUM(CASE WHEN status = 'COMPLETED' THEN 1 ELSE 0 END) AS completed_count,
|
||||||
|
SUM(CASE WHEN status = 'PENDING' THEN 1 ELSE 0 END) AS pending_count,
|
||||||
|
SUM(CASE WHEN status = 'OVERDUE' THEN 1 ELSE 0 END) AS overdue_count,
|
||||||
ROUND(SUM(CASE WHEN status = 'COMPLETED' THEN 1 ELSE 0 END) * 100.0 / NULLIF(COUNT(*), 0), 2) AS completion_rate
|
ROUND(SUM(CASE WHEN status = 'COMPLETED' THEN 1 ELSE 0 END) * 100.0 / NULLIF(COUNT(*), 0), 2) AS completion_rate
|
||||||
FROM emr_timeliness
|
FROM emr_timeliness
|
||||||
|
<if test="startDate != null and endDate != null">
|
||||||
WHERE create_time::date BETWEEN #{startDate} AND #{endDate}
|
WHERE create_time::date BETWEEN #{startDate} AND #{endDate}
|
||||||
|
</if>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import request from "@/utils/request"
|
import request from "@/utils/request"
|
||||||
export function getTimelinessByEncounter(encounterId) { return request({ url: "/api/v1/emr/timeliness/encounter/" + encounterId, method: "get" }) }
|
export function getTimelinessByEncounter(encounterId) { return request({ url: "/api/v1/emr/timeliness/encounter/" + encounterId, method: "get" }) }
|
||||||
export function getTimelinessStatistics(params) { return request({ url: "/api/v1/emr/timeliness/statistics", method: "get", params }) }
|
export function getTimelinessStatistics(params) { return request({ url: "/emr/timeliness/statistics", method: "get", params }) }
|
||||||
export function getPendingEmrCount(params) { return request({ url: "/emr-archive/pending-count", method: "get", params }) }
|
export function getPendingEmrCount(params) { return request({ url: "/emr-archive/pending-count", method: "get", params }) }
|
||||||
export function getOverdueList(params) { return request({ url: "/api/v1/emr/timeliness/overdue", method: "get", params }) }
|
export function getOverdueList(params) { return request({ url: "/api/v1/emr/timeliness/overdue", method: "get", params }) }
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,21 @@ $vxe-row-height: 40px;
|
|||||||
$vxe-header-height: 40px;
|
$vxe-header-height: 40px;
|
||||||
$vxe-radius: 4px;
|
$vxe-radius: 4px;
|
||||||
|
|
||||||
|
// === 全局列宽限制:防止文字竖排 ===
|
||||||
|
.vxe-table {
|
||||||
|
// 表头和表体列最小宽度
|
||||||
|
.vxe-header--column,
|
||||||
|
.vxe-body--column {
|
||||||
|
min-width: 80px;
|
||||||
|
}
|
||||||
|
// 列内容不换行,超长显示省略号
|
||||||
|
.vxe-cell {
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// === 全局覆盖 ===
|
// === 全局覆盖 ===
|
||||||
.vxe-table {
|
.vxe-table {
|
||||||
font-family: 'HarmonyOS Sans', 'Helvetica Neue', Helvetica, 'PingFang SC',
|
font-family: 'HarmonyOS Sans', 'Helvetica Neue', Helvetica, 'PingFang SC',
|
||||||
|
|||||||
@@ -147,7 +147,7 @@ const props = withDefaults(defineProps<TableProps>(), {
|
|||||||
showPagination: false,
|
showPagination: false,
|
||||||
total: 0,
|
total: 0,
|
||||||
pageNo: 1,
|
pageNo: 1,
|
||||||
pageSize: 20,
|
pageSize: 10,
|
||||||
isAllData: false,
|
isAllData: false,
|
||||||
paginationLeftText: '',
|
paginationLeftText: '',
|
||||||
paginationProps: () => ({}),
|
paginationProps: () => ({}),
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="table-layout-container">
|
<div class="table-layout-container">
|
||||||
<div class="card-content-wrapper">
|
<div class="card-content-wrapper">
|
||||||
<div
|
<div
|
||||||
@@ -150,7 +150,7 @@ const props = withDefaults(defineProps<TableLayoutProps>(), {
|
|||||||
total: 0,
|
total: 0,
|
||||||
queryParams: () => ({
|
queryParams: () => ({
|
||||||
pageNo: 1,
|
pageNo: 1,
|
||||||
pageSize: 20,
|
pageSize: 10,
|
||||||
}),
|
}),
|
||||||
sideQueryParams: () => ({}),
|
sideQueryParams: () => ({}),
|
||||||
formItems: () => [],
|
formItems: () => [],
|
||||||
|
|||||||
@@ -324,7 +324,7 @@ const statCards = ref([
|
|||||||
{label:'异常调用', value:0, color:'#409eff'}
|
{label:'异常调用', value:0, color:'#409eff'}
|
||||||
])
|
])
|
||||||
|
|
||||||
const q = ref({pageNo:1, pageSize:20, status:''})
|
const q = ref({pageNo:1, pageSize: 10, status:''})
|
||||||
const formData = ref({
|
const formData = ref({
|
||||||
appName:'', description:'', rateLimit:100, ipWhitelist:'', permissions:['read']
|
appName:'', description:'', rateLimit:100, ipWhitelist:'', permissions:['read']
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -186,7 +186,7 @@ const tableData = ref([])
|
|||||||
const total = ref(0)
|
const total = ref(0)
|
||||||
const q = ref({
|
const q = ref({
|
||||||
pageNo: 1,
|
pageNo: 1,
|
||||||
pageSize: 20,
|
pageSize: 10,
|
||||||
userName: '',
|
userName: '',
|
||||||
module: '',
|
module: '',
|
||||||
action: '',
|
action: '',
|
||||||
@@ -221,7 +221,7 @@ const loadStats = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const resetQuery = () => {
|
const resetQuery = () => {
|
||||||
q.value = { pageNo: 1, pageSize: 20, userName: '', module: '', action: '', riskLevel: '', businessType: '' }
|
q.value = { pageNo: 1, pageSize: 10, userName: '', module: '', action: '', riskLevel: '', businessType: '' }
|
||||||
dateRange.value = null
|
dateRange.value = null
|
||||||
loadData()
|
loadData()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -199,7 +199,7 @@ import {ref,onMounted} from 'vue'
|
|||||||
import {ElMessage} from 'element-plus'
|
import {ElMessage} from 'element-plus'
|
||||||
import {getPage,del} from './api'
|
import {getPage,del} from './api'
|
||||||
const tableData=ref([]);const total=ref(0)
|
const tableData=ref([]);const total=ref(0)
|
||||||
const q=ref({pageNo:1,pageSize:20,userName:'',module:'',action:'',result:''})
|
const q=ref({pageNo:1,pageSize: 10,userName:'',module:'',action:'',result:''})
|
||||||
const detailVisible=ref(false);const detail=ref({})
|
const detailVisible=ref(false);const detail=ref({})
|
||||||
const loadData=async()=>{const r=await getPage(q.value);tableData.value=r.data?.records||[];total.value=r.data?.total||0}
|
const loadData=async()=>{const r=await getPage(q.value);tableData.value=r.data?.records||[];total.value=r.data?.total||0}
|
||||||
const delItem=async(id)=>{await del(id);ElMessage.success('已删除');loadData()}
|
const delItem=async(id)=>{await del(id);ElMessage.success('已删除');loadData()}
|
||||||
|
|||||||
@@ -89,7 +89,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
const queryParams = ref({ searchKey: '', pageNum: 1, pageSize: 20 })
|
const queryParams = ref({ searchKey: '', pageNum: 1, pageSize: 10 })
|
||||||
const tableData = ref([])
|
const tableData = ref([])
|
||||||
const handleQuery = () => { tableData.value = [] }
|
const handleQuery = () => { tableData.value = [] }
|
||||||
const handleAdd = () => { ElMessage.info('新增功能开发中') }
|
const handleAdd = () => { ElMessage.info('新增功能开发中') }
|
||||||
|
|||||||
@@ -89,7 +89,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
const queryParams = ref({ searchKey: '', pageNum: 1, pageSize: 20 })
|
const queryParams = ref({ searchKey: '', pageNum: 1, pageSize: 10 })
|
||||||
const tableData = ref([])
|
const tableData = ref([])
|
||||||
const handleQuery = () => { tableData.value = [] }
|
const handleQuery = () => { tableData.value = [] }
|
||||||
const handleAdd = () => { ElMessage.info('新增功能开发中') }
|
const handleAdd = () => { ElMessage.info('新增功能开发中') }
|
||||||
|
|||||||
@@ -275,7 +275,7 @@ import { ref, onMounted } from 'vue'
|
|||||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
import { getBedPage, addBed, updateBed, deleteBed, updateBedStatus } from './components/api'
|
import { getBedPage, addBed, updateBed, deleteBed, updateBedStatus } from './components/api'
|
||||||
const loading = ref(false); const tableData = ref([]); const total = ref(0)
|
const loading = ref(false); const tableData = ref([]); const total = ref(0)
|
||||||
const queryParams = ref({ bedNo: '', status: undefined, pageNo: 1, pageSize: 20 })
|
const queryParams = ref({ bedNo: '', status: undefined, pageNo: 1, pageSize: 10 })
|
||||||
const formVisible = ref(false); const formTitle = ref('新增床位'); const isEdit = ref(false); const formRef = ref()
|
const formVisible = ref(false); const formTitle = ref('新增床位'); const isEdit = ref(false); const formRef = ref()
|
||||||
const form = ref({ id: null, bedNo: '', bedName: '', wardName: '', deptName: '', bedType: 1, remark: '' })
|
const form = ref({ id: null, bedNo: '', bedName: '', wardName: '', deptName: '', bedType: 1, remark: '' })
|
||||||
const rules = { bedNo: [{ required: true, message: '请输入床号', trigger: 'blur' }] }
|
const rules = { bedNo: [{ required: true, message: '请输入床号', trigger: 'blur' }] }
|
||||||
@@ -286,7 +286,7 @@ function getList() {
|
|||||||
getBedPage(queryParams.value).then(res => { tableData.value = res.data?.records || []; total.value = res.data?.total || 0 }).finally(() => { loading.value = false })
|
getBedPage(queryParams.value).then(res => { tableData.value = res.data?.records || []; total.value = res.data?.total || 0 }).finally(() => { loading.value = false })
|
||||||
}
|
}
|
||||||
function handleQuery() { queryParams.value.pageNo = 1; getList() }
|
function handleQuery() { queryParams.value.pageNo = 1; getList() }
|
||||||
function resetQuery() { queryParams.value = { bedNo: '', status: undefined, pageNo: 1, pageSize: 20 }; getList() }
|
function resetQuery() { queryParams.value = { bedNo: '', status: undefined, pageNo: 1, pageSize: 10 }; getList() }
|
||||||
function handleAdd() { isEdit.value = false; formTitle.value = '新增床位'; form.value = { id: null, bedNo: '', bedName: '', wardName: '', deptName: '', bedType: 1, remark: '' }; formVisible.value = true }
|
function handleAdd() { isEdit.value = false; formTitle.value = '新增床位'; form.value = { id: null, bedNo: '', bedName: '', wardName: '', deptName: '', bedType: 1, remark: '' }; formVisible.value = true }
|
||||||
function handleEdit(row) { isEdit.value = true; formTitle.value = '编辑床位'; form.value = { ...row }; formVisible.value = true }
|
function handleEdit(row) { isEdit.value = true; formTitle.value = '编辑床位'; form.value = { ...row }; formVisible.value = true }
|
||||||
function submitForm() {
|
function submitForm() {
|
||||||
|
|||||||
@@ -198,7 +198,7 @@ import { ref, onMounted } from 'vue'
|
|||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import { getDiagnosisPage, addDiagnosis, updateDiagnosis, stopDiagnosis, startDiagnosis } from './components/api'
|
import { getDiagnosisPage, addDiagnosis, updateDiagnosis, stopDiagnosis, startDiagnosis } from './components/api'
|
||||||
const loading = ref(false); const tableData = ref([]); const total = ref(0)
|
const loading = ref(false); const tableData = ref([]); const total = ref(0)
|
||||||
const queryParams = ref({ searchKey: '', pageNo: 1, pageSize: 20 })
|
const queryParams = ref({ searchKey: '', pageNo: 1, pageSize: 10 })
|
||||||
const formVisible = ref(false); const formTitle = ref('新增诊断'); const isEdit = ref(false); const formRef = ref()
|
const formVisible = ref(false); const formTitle = ref('新增诊断'); const isEdit = ref(false); const formRef = ref()
|
||||||
const form = ref({ id: null, conditionCode: '', name: '', pyStr: '', typeCode: 'WEST' })
|
const form = ref({ id: null, conditionCode: '', name: '', pyStr: '', typeCode: 'WEST' })
|
||||||
const rules = { conditionCode: [{ required: true, message: '请输入编码', trigger: 'blur' }], name: [{ required: true, message: '请输入名称', trigger: 'blur' }] }
|
const rules = { conditionCode: [{ required: true, message: '请输入编码', trigger: 'blur' }], name: [{ required: true, message: '请输入名称', trigger: 'blur' }] }
|
||||||
@@ -207,7 +207,7 @@ function getList() {
|
|||||||
getDiagnosisPage(queryParams.value).then(res => { tableData.value = res.data?.records || []; total.value = res.data?.total || 0 }).finally(() => { loading.value = false })
|
getDiagnosisPage(queryParams.value).then(res => { tableData.value = res.data?.records || []; total.value = res.data?.total || 0 }).finally(() => { loading.value = false })
|
||||||
}
|
}
|
||||||
function handleQuery() { queryParams.value.pageNo = 1; getList() }
|
function handleQuery() { queryParams.value.pageNo = 1; getList() }
|
||||||
function resetQuery() { queryParams.value = { searchKey: '', pageNo: 1, pageSize: 20 }; getList() }
|
function resetQuery() { queryParams.value = { searchKey: '', pageNo: 1, pageSize: 10 }; getList() }
|
||||||
function handleAdd() { isEdit.value = false; formTitle.value = '新增诊断'; form.value = { id: null, conditionCode: '', name: '', pyStr: '', typeCode: 'WEST' }; formVisible.value = true }
|
function handleAdd() { isEdit.value = false; formTitle.value = '新增诊断'; form.value = { id: null, conditionCode: '', name: '', pyStr: '', typeCode: 'WEST' }; formVisible.value = true }
|
||||||
function handleEdit(row) { isEdit.value = true; formTitle.value = '编辑诊断'; form.value = { ...row }; formVisible.value = true }
|
function handleEdit(row) { isEdit.value = true; formTitle.value = '编辑诊断'; form.value = { ...row }; formVisible.value = true }
|
||||||
function submitForm() {
|
function submitForm() {
|
||||||
|
|||||||
@@ -89,7 +89,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
const queryParams = ref({ searchKey: '', pageNum: 1, pageSize: 20 })
|
const queryParams = ref({ searchKey: '', pageNum: 1, pageSize: 10 })
|
||||||
const tableData = ref([])
|
const tableData = ref([])
|
||||||
const handleQuery = () => { tableData.value = [] }
|
const handleQuery = () => { tableData.value = [] }
|
||||||
const handleAdd = () => { ElMessage.info('新增功能开发中') }
|
const handleAdd = () => { ElMessage.info('新增功能开发中') }
|
||||||
|
|||||||
@@ -89,7 +89,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
const queryParams = ref({ searchKey: '', pageNum: 1, pageSize: 20 })
|
const queryParams = ref({ searchKey: '', pageNum: 1, pageSize: 10 })
|
||||||
const tableData = ref([])
|
const tableData = ref([])
|
||||||
const handleQuery = () => { tableData.value = [] }
|
const handleQuery = () => { tableData.value = [] }
|
||||||
const handleAdd = () => { ElMessage.info('新增功能开发中') }
|
const handleAdd = () => { ElMessage.info('新增功能开发中') }
|
||||||
|
|||||||
@@ -190,13 +190,13 @@ import { ref, onMounted } from 'vue'
|
|||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import { getFeePage, addFee, updateFee } from './components/api'
|
import { getFeePage, addFee, updateFee } from './components/api'
|
||||||
const loading = ref(false); const tableData = ref([]); const total = ref(0)
|
const loading = ref(false); const tableData = ref([]); const total = ref(0)
|
||||||
const queryParams = ref({ searchKey: '', pageNo: 1, pageSize: 20 })
|
const queryParams = ref({ searchKey: '', pageNo: 1, pageSize: 10 })
|
||||||
const formVisible = ref(false); const formTitle = ref('新增项目'); const isEdit = ref(false); const formRef = ref()
|
const formVisible = ref(false); const formTitle = ref('新增项目'); const isEdit = ref(false); const formRef = ref()
|
||||||
const form = ref({ id: null, conditionCode: '', name: '', typeCode: 'TREAT' })
|
const form = ref({ id: null, conditionCode: '', name: '', typeCode: 'TREAT' })
|
||||||
const rules = { conditionCode: [{ required: true, message: '请输入编码', trigger: 'blur' }], name: [{ required: true, message: '请输入名称', trigger: 'blur' }] }
|
const rules = { conditionCode: [{ required: true, message: '请输入编码', trigger: 'blur' }], name: [{ required: true, message: '请输入名称', trigger: 'blur' }] }
|
||||||
function getList() { loading.value = true; getFeePage(queryParams.value).then(res => { tableData.value = res.data?.records || []; total.value = res.data?.total || 0 }).finally(() => { loading.value = false }) }
|
function getList() { loading.value = true; getFeePage(queryParams.value).then(res => { tableData.value = res.data?.records || []; total.value = res.data?.total || 0 }).finally(() => { loading.value = false }) }
|
||||||
function handleQuery() { queryParams.value.pageNo = 1; getList() }
|
function handleQuery() { queryParams.value.pageNo = 1; getList() }
|
||||||
function resetQuery() { queryParams.value = { searchKey: '', pageNo: 1, pageSize: 20 }; getList() }
|
function resetQuery() { queryParams.value = { searchKey: '', pageNo: 1, pageSize: 10 }; getList() }
|
||||||
function handleAdd() { isEdit.value = false; formTitle.value = '新增项目'; form.value = { id: null, conditionCode: '', name: '', typeCode: 'TREAT' }; formVisible.value = true }
|
function handleAdd() { isEdit.value = false; formTitle.value = '新增项目'; form.value = { id: null, conditionCode: '', name: '', typeCode: 'TREAT' }; formVisible.value = true }
|
||||||
function handleEdit(row) { isEdit.value = true; formTitle.value = '编辑项目'; form.value = { ...row }; formVisible.value = true }
|
function handleEdit(row) { isEdit.value = true; formTitle.value = '编辑项目'; form.value = { ...row }; formVisible.value = true }
|
||||||
function submitForm() { formRef.value.validate(valid => { if (!valid) return; const action = isEdit.value ? updateFee(form.value) : addFee(form.value); action.then(res => { if (res.code === 200) { ElMessage.success(isEdit.value ? '修改成功' : '新增成功'); formVisible.value = false; getList() } else ElMessage.error(res.msg || '操作失败') }) }) }
|
function submitForm() { formRef.value.validate(valid => { if (!valid) return; const action = isEdit.value ? updateFee(form.value) : addFee(form.value); action.then(res => { if (res.code === 200) { ElMessage.success(isEdit.value ? '修改成功' : '新增成功'); formVisible.value = false; getList() } else ElMessage.error(res.msg || '操作失败') }) }) }
|
||||||
|
|||||||
@@ -89,7 +89,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
const queryParams = ref({ searchKey: '', pageNum: 1, pageSize: 20 })
|
const queryParams = ref({ searchKey: '', pageNum: 1, pageSize: 10 })
|
||||||
const tableData = ref([])
|
const tableData = ref([])
|
||||||
const handleQuery = () => { tableData.value = [] }
|
const handleQuery = () => { tableData.value = [] }
|
||||||
const handleAdd = () => { ElMessage.info('新增功能开发中') }
|
const handleAdd = () => { ElMessage.info('新增功能开发中') }
|
||||||
|
|||||||
@@ -94,7 +94,7 @@
|
|||||||
import { ref, onMounted } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
import { getList, remove } from './components/api'
|
import { getList, remove } from './components/api'
|
||||||
const queryParams = ref({ searchKey: '', pageNum: 1, pageSize: 20 })
|
const queryParams = ref({ searchKey: '', pageNum: 1, pageSize: 10 })
|
||||||
const tableData = ref([])
|
const tableData = ref([])
|
||||||
const handleQuery = async () => {
|
const handleQuery = async () => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ const tableWrapper = ref();
|
|||||||
const currentIndex = ref(0); // 当前选中行索引
|
const currentIndex = ref(0); // 当前选中行索引
|
||||||
const currentSelectRow = ref({});
|
const currentSelectRow = ref({});
|
||||||
const queryParams = ref({
|
const queryParams = ref({
|
||||||
pageSize: 20,
|
pageSize: 10,
|
||||||
pageNum: 1,
|
pageNum: 1,
|
||||||
adviceTypes: '1,2,3',
|
adviceTypes: '1,2,3',
|
||||||
organizationId: null,
|
organizationId: null,
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ const tableWrapper = ref();
|
|||||||
const currentIndex = ref(0);
|
const currentIndex = ref(0);
|
||||||
const currentSelectRow = ref({});
|
const currentSelectRow = ref({});
|
||||||
const queryParams = ref({
|
const queryParams = ref({
|
||||||
pageSize: 20,
|
pageSize: 10,
|
||||||
pageNum: 1,
|
pageNum: 1,
|
||||||
searchKey: '',
|
searchKey: '',
|
||||||
organizationId: props.organizationId,
|
organizationId: props.organizationId,
|
||||||
|
|||||||
@@ -166,13 +166,13 @@ import { ref, onMounted } from 'vue'
|
|||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import { getTcmPage, addTcm, updateTcm } from './components/api'
|
import { getTcmPage, addTcm, updateTcm } from './components/api'
|
||||||
const loading = ref(false); const tableData = ref([]); const total = ref(0)
|
const loading = ref(false); const tableData = ref([]); const total = ref(0)
|
||||||
const queryParams = ref({ searchKey: '', pageNo: 1, pageSize: 20 })
|
const queryParams = ref({ searchKey: '', pageNo: 1, pageSize: 10 })
|
||||||
const formVisible = ref(false); const formTitle = ref('新增处方'); const isEdit = ref(false); const formRef = ref()
|
const formVisible = ref(false); const formTitle = ref('新增处方'); const isEdit = ref(false); const formRef = ref()
|
||||||
const form = ref({ id: null, conditionCode: '', name: '', typeCode: '' })
|
const form = ref({ id: null, conditionCode: '', name: '', typeCode: '' })
|
||||||
const rules = { conditionCode: [{ required: true, message: '请输入编码', trigger: 'blur' }], name: [{ required: true, message: '请输入名称', trigger: 'blur' }] }
|
const rules = { conditionCode: [{ required: true, message: '请输入编码', trigger: 'blur' }], name: [{ required: true, message: '请输入名称', trigger: 'blur' }] }
|
||||||
function getList() { loading.value = true; getTcmPage(queryParams.value).then(res => { tableData.value = res.data?.records || []; total.value = res.data?.total || 0 }).finally(() => { loading.value = false }) }
|
function getList() { loading.value = true; getTcmPage(queryParams.value).then(res => { tableData.value = res.data?.records || []; total.value = res.data?.total || 0 }).finally(() => { loading.value = false }) }
|
||||||
function handleQuery() { queryParams.value.pageNo = 1; getList() }
|
function handleQuery() { queryParams.value.pageNo = 1; getList() }
|
||||||
function resetQuery() { queryParams.value = { searchKey: '', pageNo: 1, pageSize: 20 }; getList() }
|
function resetQuery() { queryParams.value = { searchKey: '', pageNo: 1, pageSize: 10 }; getList() }
|
||||||
function handleAdd() { isEdit.value = false; formTitle.value = '新增处方'; form.value = { id: null, conditionCode: '', name: '', typeCode: '' }; formVisible.value = true }
|
function handleAdd() { isEdit.value = false; formTitle.value = '新增处方'; form.value = { id: null, conditionCode: '', name: '', typeCode: '' }; formVisible.value = true }
|
||||||
function handleEdit(row) { isEdit.value = true; formTitle.value = '编辑处方'; form.value = { ...row }; formVisible.value = true }
|
function handleEdit(row) { isEdit.value = true; formTitle.value = '编辑处方'; form.value = { ...row }; formVisible.value = true }
|
||||||
function submitForm() { formRef.value.validate(valid => { if (!valid) return; const action = isEdit.value ? updateTcm(form.value) : addTcm(form.value); action.then(res => { if (res.code === 200) { ElMessage.success(isEdit.value ? '修改成功' : '新增成功'); formVisible.value = false; getList() } else ElMessage.error(res.msg || '操作失败') }) }) }
|
function submitForm() { formRef.value.validate(valid => { if (!valid) return; const action = isEdit.value ? updateTcm(form.value) : addTcm(form.value); action.then(res => { if (res.code === 200) { ElMessage.success(isEdit.value ? '修改成功' : '新增成功'); formVisible.value = false; getList() } else ElMessage.error(res.msg || '操作失败') }) }) }
|
||||||
|
|||||||
@@ -211,7 +211,7 @@ const statCards = ref([
|
|||||||
{label:'平均住院日', value:0, color:'#409eff'}
|
{label:'平均住院日', value:0, color:'#409eff'}
|
||||||
])
|
])
|
||||||
|
|
||||||
const q = ref({pageNo:1, pageSize:20, departmentName:'', dateRange:null})
|
const q = ref({pageNo:1, pageSize: 10, departmentName:'', dateRange:null})
|
||||||
|
|
||||||
function formatMoney(val) {
|
function formatMoney(val) {
|
||||||
if (!val) return '0.00'
|
if (!val) return '0.00'
|
||||||
@@ -235,7 +235,7 @@ async function loadData() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function resetQuery() {
|
function resetQuery() {
|
||||||
q.value = {pageNo:1, pageSize:20, departmentName:'', dateRange:null}
|
q.value = {pageNo:1, pageSize: 10, departmentName:'', dateRange:null}
|
||||||
loadData()
|
loadData()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -102,7 +102,7 @@
|
|||||||
import { ref, onMounted } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
import { getList, remove } from './components/api'
|
import { getList, remove } from './components/api'
|
||||||
const queryParams = ref({ searchKey: '', pageNum: 1, pageSize: 20 })
|
const queryParams = ref({ searchKey: '', pageNum: 1, pageSize: 10 })
|
||||||
const tableData = ref([])
|
const tableData = ref([])
|
||||||
const handleQuery = async () => {
|
const handleQuery = async () => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -75,7 +75,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
const queryParams = ref({ searchKey: '', pageNum: 1, pageSize: 20 })
|
const queryParams = ref({ searchKey: '', pageNum: 1, pageSize: 10 })
|
||||||
const tableData = ref([])
|
const tableData = ref([])
|
||||||
const handleQuery = async () => { tableData.value = [] }
|
const handleQuery = async () => { tableData.value = [] }
|
||||||
const handleDetail = (row) => { ElMessage.info('详情') }
|
const handleDetail = (row) => { ElMessage.info('详情') }
|
||||||
|
|||||||
@@ -75,7 +75,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
const queryParams = ref({ searchKey: '', pageNum: 1, pageSize: 20 })
|
const queryParams = ref({ searchKey: '', pageNum: 1, pageSize: 10 })
|
||||||
const tableData = ref([])
|
const tableData = ref([])
|
||||||
const handleQuery = async () => { tableData.value = [] }
|
const handleQuery = async () => { tableData.value = [] }
|
||||||
const handleDetail = (row) => { ElMessage.info('详情') }
|
const handleDetail = (row) => { ElMessage.info('详情') }
|
||||||
|
|||||||
@@ -1,152 +1,75 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="app-container">
|
<div class="app-container">
|
||||||
<el-row
|
<el-row :gutter="20" class="mb8">
|
||||||
:gutter="20"
|
<el-col :xs="12" :sm="6">
|
||||||
class="mb8"
|
<el-card shadow="hover" class="stat-card">
|
||||||
>
|
<el-statistic title="路径总数" :value="stats.totalPathways || 0" />
|
||||||
<el-col :span="6">
|
|
||||||
<el-card shadow="hover">
|
|
||||||
<el-statistic
|
|
||||||
title="路径总数"
|
|
||||||
:value="stats.totalPathways || 0"
|
|
||||||
/>
|
|
||||||
</el-card>
|
</el-card>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="6">
|
<el-col :xs="12" :sm="6">
|
||||||
<el-card shadow="hover">
|
<el-card shadow="hover" class="stat-card">
|
||||||
<el-statistic
|
<el-statistic title="入径数" :value="stats.enteredCount || 0" />
|
||||||
title="入径数"
|
|
||||||
:value="stats.enteredCount || 0"
|
|
||||||
/>
|
|
||||||
</el-card>
|
</el-card>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="6">
|
<el-col :xs="12" :sm="6">
|
||||||
<el-card shadow="hover">
|
<el-card shadow="hover" class="stat-card">
|
||||||
<el-statistic
|
<el-statistic title="完成数" :value="stats.completedCount || 0" />
|
||||||
title="完成数"
|
|
||||||
:value="stats.completedCount || 0"
|
|
||||||
/>
|
|
||||||
</el-card>
|
</el-card>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="6">
|
<el-col :xs="12" :sm="6">
|
||||||
<el-card shadow="hover">
|
<el-card shadow="hover" class="stat-card">
|
||||||
<el-statistic
|
<el-statistic title="变异数" :value="stats.variedCount || 0" />
|
||||||
title="变异数"
|
|
||||||
:value="stats.variedCount || 0"
|
|
||||||
/>
|
|
||||||
</el-card>
|
</el-card>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
<el-card>
|
<el-card>
|
||||||
<template #header>
|
<template #header>
|
||||||
<div style="display:flex;justify-content:space-between;align-items:center">
|
<div class="card-header">
|
||||||
<span>临床路径管理</span>
|
<span>临床路径管理</span>
|
||||||
<el-button
|
<el-button type="primary" icon="Plus" @click="handleAdd">新增路径</el-button>
|
||||||
type="primary"
|
|
||||||
icon="Plus"
|
|
||||||
@click="handleAdd"
|
|
||||||
>
|
|
||||||
新增路径
|
|
||||||
</el-button>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<el-form
|
|
||||||
:model="queryParams"
|
<el-form :model="queryParams" :inline="true" class="query-form">
|
||||||
:inline="true"
|
|
||||||
class="mb8"
|
|
||||||
>
|
|
||||||
<el-form-item label="疾病编码">
|
<el-form-item label="疾病编码">
|
||||||
<el-input
|
<el-input v-model="queryParams.diseaseCode" placeholder="请输入疾病编码" clearable @keyup.enter="handleQuery" />
|
||||||
v-model="queryParams.diseaseCode"
|
</el-form-item>
|
||||||
placeholder="疾病编码"
|
<el-form-item label="疾病名称">
|
||||||
clearable
|
<el-input v-model="queryParams.diseaseName" placeholder="请输入疾病名称" clearable @keyup.enter="handleQuery" />
|
||||||
/>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button
|
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||||
type="primary"
|
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||||
@click="handleQuery"
|
|
||||||
>
|
|
||||||
搜索
|
|
||||||
</el-button>
|
|
||||||
<el-button @click="queryParams.diseaseCode='';handleQuery()">
|
|
||||||
重置
|
|
||||||
</el-button>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
<el-table
|
|
||||||
v-loading="loading"
|
<el-table v-loading="loading" :data="dataList" border stripe style="width: 100%">
|
||||||
:data="dataList"
|
<el-table-column type="index" label="序号" width="60" align="center" />
|
||||||
>
|
<el-table-column prop="diseaseCode" label="疾病编码" width="120" show-overflow-tooltip />
|
||||||
<el-table-column
|
<el-table-column prop="diseaseName" label="疾病名称" width="150" show-overflow-tooltip />
|
||||||
label="疾病编码"
|
<el-table-column prop="pathwayName" label="路径名称" min-width="180" show-overflow-tooltip />
|
||||||
prop="diseaseCode"
|
<el-table-column prop="standardLos" label="标准住院日" width="110" align="center" />
|
||||||
width="120"
|
<el-table-column prop="version" label="版本" width="70" align="center" />
|
||||||
/>
|
<el-table-column prop="status" label="状态" width="90" align="center">
|
||||||
<el-table-column
|
<template #default="{ row }">
|
||||||
label="疾病名称"
|
<el-tag :type="row.status === 'ACTIVE' ? 'success' : 'info'" size="small">
|
||||||
prop="diseaseName"
|
{{ row.status === 'ACTIVE' ? '启用' : '停用' }}
|
||||||
width="150"
|
|
||||||
/>
|
|
||||||
<el-table-column
|
|
||||||
label="路径名称"
|
|
||||||
prop="pathwayName"
|
|
||||||
width="200"
|
|
||||||
show-overflow-tooltip
|
|
||||||
/>
|
|
||||||
<el-table-column
|
|
||||||
label="标准住院日"
|
|
||||||
prop="standardLos"
|
|
||||||
width="100"
|
|
||||||
align="center"
|
|
||||||
/>
|
|
||||||
<el-table-column
|
|
||||||
label="版本"
|
|
||||||
prop="version"
|
|
||||||
width="70"
|
|
||||||
align="center"
|
|
||||||
/>
|
|
||||||
<el-table-column
|
|
||||||
label="状态"
|
|
||||||
prop="status"
|
|
||||||
width="90"
|
|
||||||
>
|
|
||||||
<template #default="s">
|
|
||||||
<el-tag :type="s.row.status==='ACTIVE'?'success':'info'">
|
|
||||||
{{ s.row.status === 'ACTIVE' ? '启用' : '停用' }}
|
|
||||||
</el-tag>
|
</el-tag>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column label="操作" width="220" align="center" fixed="right">
|
||||||
label="操作"
|
<template #default="{ row }">
|
||||||
width="200"
|
<el-button link type="primary" icon="Upload" @click="handleEnter(row)">入径</el-button>
|
||||||
fixed="right"
|
<el-button link type="success" icon="Check" @click="handleComplete(row)">完成</el-button>
|
||||||
>
|
<el-button link type="warning" icon="Warning" @click="handleVary(row)">变异</el-button>
|
||||||
<template #default="s">
|
|
||||||
<el-button
|
|
||||||
link
|
|
||||||
type="primary"
|
|
||||||
@click="handleEnter(s.row)"
|
|
||||||
>
|
|
||||||
入径
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
link
|
|
||||||
type="success"
|
|
||||||
@click="handleComplete(s.row)"
|
|
||||||
>
|
|
||||||
完成
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
link
|
|
||||||
type="warning"
|
|
||||||
@click="handleVary(s.row)"
|
|
||||||
>
|
|
||||||
变异
|
|
||||||
</el-button>
|
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
<template #empty>
|
||||||
|
<el-empty description="暂无临床路径数据" :image-size="80" />
|
||||||
|
</template>
|
||||||
</el-table>
|
</el-table>
|
||||||
|
|
||||||
<pagination
|
<pagination
|
||||||
v-show="total > 0"
|
v-show="total > 0"
|
||||||
v-model:page="queryParams.pageNo"
|
v-model:page="queryParams.pageNo"
|
||||||
@@ -156,60 +79,39 @@
|
|||||||
/>
|
/>
|
||||||
</el-card>
|
</el-card>
|
||||||
|
|
||||||
<el-dialog
|
<el-dialog v-model="dialogVisible" :title="isEdit ? '编辑临床路径' : '新增临床路径'" width="600px" destroy-on-close :close-on-click-modal="false">
|
||||||
v-model="dialogVisible"
|
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="110px">
|
||||||
title="新增临床路径"
|
|
||||||
width="600px"
|
|
||||||
>
|
|
||||||
<el-form
|
|
||||||
:model="formData"
|
|
||||||
label-width="110px"
|
|
||||||
>
|
|
||||||
<el-row :gutter="20">
|
<el-row :gutter="20">
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="疾病编码">
|
<el-form-item label="疾病编码" prop="diseaseCode">
|
||||||
<el-input v-model="formData.diseaseCode" />
|
<el-input v-model="formData.diseaseCode" placeholder="请输入疾病编码" maxlength="20" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="疾病名称">
|
<el-form-item label="疾病名称" prop="diseaseName">
|
||||||
<el-input v-model="formData.diseaseName" />
|
<el-input v-model="formData.diseaseName" placeholder="请输入疾病名称" maxlength="100" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="24">
|
<el-col :span="24">
|
||||||
<el-form-item label="路径名称">
|
<el-form-item label="路径名称" prop="pathwayName">
|
||||||
<el-input v-model="formData.pathwayName" />
|
<el-input v-model="formData.pathwayName" placeholder="请输入路径名称" maxlength="200" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="标准住院日">
|
<el-form-item label="标准住院日" prop="standardLos">
|
||||||
<el-input-number
|
<el-input-number v-model="formData.standardLos" :min="1" :max="365" style="width: 100%" />
|
||||||
v-model="formData.standardLos"
|
|
||||||
:min="1"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="24">
|
<el-col :span="24">
|
||||||
<el-form-item label="路径内容">
|
<el-form-item label="路径内容">
|
||||||
<el-input
|
<el-input v-model="formData.pathwayContent" type="textarea" :rows="4" placeholder="请输入路径内容" maxlength="2000" show-word-limit />
|
||||||
v-model="formData.pathwayContent"
|
|
||||||
type="textarea"
|
|
||||||
:rows="4"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
</el-form>
|
</el-form>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<el-button @click="dialogVisible = false">
|
<el-button @click="dialogVisible = false">取消</el-button>
|
||||||
取消
|
<el-button type="primary" :loading="submitLoading" @click="submitForm">确定</el-button>
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
type="primary"
|
|
||||||
@click="submitForm"
|
|
||||||
>
|
|
||||||
确定
|
|
||||||
</el-button>
|
|
||||||
</template>
|
</template>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
@@ -220,27 +122,70 @@ import { ref, reactive, onMounted } from 'vue'
|
|||||||
import { getWorkflowPage, addWorkflow, enterPathway, completePathway, varyPathway, getStats } from '../api'
|
import { getWorkflowPage, addWorkflow, enterPathway, completePathway, varyPathway, getStats } from '../api'
|
||||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
|
|
||||||
const loading = ref(false); const dataList = ref([]); const total = ref(0); const stats = ref({})
|
const loading = ref(false)
|
||||||
|
const submitLoading = ref(false)
|
||||||
|
const dataList = ref([])
|
||||||
|
const total = ref(0)
|
||||||
|
const stats = ref({})
|
||||||
const dialogVisible = ref(false)
|
const dialogVisible = ref(false)
|
||||||
const queryParams = reactive({ diseaseCode: '', pageNo: 1, pageSize: 20 })
|
const isEdit = ref(false)
|
||||||
const formData = ref({})
|
const formRef = ref(null)
|
||||||
|
|
||||||
const getList = async () => { loading.value = true; const res = await getWorkflowPage(queryParams); dataList.value = res.data?.records || []; total.value = res.data?.total || 0; loading.value = false }
|
const queryParams = reactive({ diseaseCode: '', diseaseName: '', pageNo: 1, pageSize: 10 })
|
||||||
const loadStats = async () => { const res = await getStats(); stats.value = res.data || {} }
|
const formData = reactive({ diseaseCode: '', diseaseName: '', pathwayName: '', standardLos: 7, pathwayContent: '' })
|
||||||
|
const formRules = {
|
||||||
|
diseaseCode: [{ required: true, message: '请输入疾病编码', trigger: 'blur' }],
|
||||||
|
diseaseName: [{ required: true, message: '请输入疾病名称', trigger: 'blur' }],
|
||||||
|
pathwayName: [{ required: true, message: '请输入路径名称', trigger: 'blur' }],
|
||||||
|
standardLos: [{ required: true, message: '请输入标准住院日', trigger: 'blur' }]
|
||||||
|
}
|
||||||
|
|
||||||
|
const getList = async () => {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const params = { diseaseCode: queryParams.diseaseCode || undefined, diseaseName: queryParams.diseaseName || undefined, pageNo: queryParams.pageNo, pageSize: queryParams.pageSize }
|
||||||
|
const res = await getWorkflowPage(params)
|
||||||
|
dataList.value = res.data?.records || []
|
||||||
|
total.value = res.data?.total || 0
|
||||||
|
} catch {} finally { loading.value = false }
|
||||||
|
}
|
||||||
|
const loadStats = async () => { try { const res = await getStats(); stats.value = res.data || {} } catch {} }
|
||||||
const handleQuery = () => { queryParams.pageNo = 1; getList() }
|
const handleQuery = () => { queryParams.pageNo = 1; getList() }
|
||||||
const handleAdd = () => { formData.value = {}; dialogVisible.value = true }
|
const resetQuery = () => { queryParams.diseaseCode = ''; queryParams.diseaseName = ''; handleQuery() }
|
||||||
const submitForm = async () => { await addWorkflow(formData.value); ElMessage.success('创建成功'); dialogVisible.value = false; getList(); loadStats() }
|
const handleAdd = () => { isEdit.value = false; Object.assign(formData, { diseaseCode: '', diseaseName: '', pathwayName: '', standardLos: 7, pathwayContent: '' }); dialogVisible.value = true }
|
||||||
|
const submitForm = async () => {
|
||||||
|
try { await formRef.value?.validate() } catch { return }
|
||||||
|
submitLoading.value = true
|
||||||
|
try {
|
||||||
|
await addWorkflow({ ...formData })
|
||||||
|
ElMessage.success(isEdit.value ? '修改成功' : '创建成功')
|
||||||
|
dialogVisible.value = false; getList(); loadStats()
|
||||||
|
} catch {} finally { submitLoading.value = false }
|
||||||
|
}
|
||||||
const handleEnter = async (row) => {
|
const handleEnter = async (row) => {
|
||||||
await ElMessageBox.confirm(`确认将患者入径「${row.pathwayName}」?`, '入径确认', { type: 'info' })
|
try {
|
||||||
await enterPathway({ pathwayId: row.id, diseaseCode: row.diseaseCode, diseaseName: row.diseaseName }); ElMessage.success('入径成功'); loadStats()
|
await ElMessageBox.confirm(`确认将患者入径「${row.pathwayName}」?`, '入径确认', { type: 'info' })
|
||||||
|
await enterPathway({ pathwayId: row.id, diseaseCode: row.diseaseCode, diseaseName: row.diseaseName })
|
||||||
|
ElMessage.success('入径成功'); loadStats()
|
||||||
|
} catch {}
|
||||||
}
|
}
|
||||||
const handleComplete = async (row) => {
|
const handleComplete = async (row) => {
|
||||||
await ElMessageBox.confirm('确认完成此路径?', '完成确认', { type: 'success' })
|
try {
|
||||||
await completePathway(row.id); ElMessage.success('已完成'); getList(); loadStats()
|
await ElMessageBox.confirm('确认完成此路径?', '完成确认', { type: 'success' })
|
||||||
|
await completePathway(row.id); ElMessage.success('已完成'); getList(); loadStats()
|
||||||
|
} catch {}
|
||||||
}
|
}
|
||||||
const handleVary = async (row) => {
|
const handleVary = async (row) => {
|
||||||
await ElMessageBox.prompt('请输入变异原因', '变异登记', { type: 'warning' })
|
try {
|
||||||
.then(async ({ value }) => { await varyPathway(row.id, value || '未填写原因'); ElMessage.success('变异已登记'); getList(); loadStats() })
|
const { value } = await ElMessageBox.prompt('请输入变异原因', '变异登记', { type: 'warning' })
|
||||||
|
await varyPathway(row.id, value || '未填写原因'); ElMessage.success('变异已登记'); getList(); loadStats()
|
||||||
|
} catch {}
|
||||||
}
|
}
|
||||||
onMounted(() => { getList(); loadStats() })
|
onMounted(() => { getList(); loadStats() })
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.card-header { display: flex; justify-content: space-between; align-items: center; }
|
||||||
|
.query-form { margin-bottom: 0; }
|
||||||
|
.stat-card { text-align: center; }
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -89,7 +89,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
const queryParams = ref({ searchKey: '', pageNum: 1, pageSize: 20 })
|
const queryParams = ref({ searchKey: '', pageNum: 1, pageSize: 10 })
|
||||||
const tableData = ref([])
|
const tableData = ref([])
|
||||||
const handleQuery = () => { tableData.value = [] }
|
const handleQuery = () => { tableData.value = [] }
|
||||||
const handleAdd = () => { ElMessage.info('新增功能开发中') }
|
const handleAdd = () => { ElMessage.info('新增功能开发中') }
|
||||||
|
|||||||
@@ -12,6 +12,8 @@
|
|||||||
max-height="650"
|
max-height="650"
|
||||||
:data="ePrescribingDetailList"
|
:data="ePrescribingDetailList"
|
||||||
border
|
border
|
||||||
|
:column-config="{ minWidth: 100 }"
|
||||||
|
show-overflow-tooltip
|
||||||
>
|
>
|
||||||
<vxe-column
|
<vxe-column
|
||||||
title="处方号"
|
title="处方号"
|
||||||
|
|||||||
@@ -15,6 +15,8 @@
|
|||||||
max-height="650"
|
max-height="650"
|
||||||
:data="prescriptionInfoList"
|
:data="prescriptionInfoList"
|
||||||
border
|
border
|
||||||
|
:column-config="{ minWidth: 100 }"
|
||||||
|
show-overflow-tooltip
|
||||||
>
|
>
|
||||||
<vxe-column
|
<vxe-column
|
||||||
title="医保处方编号"
|
title="医保处方编号"
|
||||||
@@ -215,6 +217,8 @@
|
|||||||
max-height="650"
|
max-height="650"
|
||||||
:data="rxdrugdetailList"
|
:data="rxdrugdetailList"
|
||||||
border
|
border
|
||||||
|
:column-config="{ minWidth: 100 }"
|
||||||
|
show-overflow-tooltip
|
||||||
>
|
>
|
||||||
<vxe-column
|
<vxe-column
|
||||||
title="医疗目录编码"
|
title="医疗目录编码"
|
||||||
@@ -453,6 +457,8 @@
|
|||||||
max-height="650"
|
max-height="650"
|
||||||
:data="mdtrtinfoList"
|
:data="mdtrtinfoList"
|
||||||
border
|
border
|
||||||
|
:column-config="{ minWidth: 100 }"
|
||||||
|
show-overflow-tooltip
|
||||||
>
|
>
|
||||||
<vxe-column
|
<vxe-column
|
||||||
title="定点医疗机构名称"
|
title="定点医疗机构名称"
|
||||||
@@ -733,6 +739,8 @@
|
|||||||
max-height="650"
|
max-height="650"
|
||||||
:data="discinfoList"
|
:data="discinfoList"
|
||||||
border
|
border
|
||||||
|
:column-config="{ minWidth: 100 }"
|
||||||
|
show-overflow-tooltip
|
||||||
>
|
>
|
||||||
<vxe-column
|
<vxe-column
|
||||||
title="诊断类别"
|
title="诊断类别"
|
||||||
|
|||||||
@@ -185,7 +185,7 @@ import { ref, onMounted } from 'vue'
|
|||||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
import { getPatientPage, getRefundDetail, submitRefund, init } from './components/api'
|
import { getPatientPage, getRefundDetail, submitRefund, init } from './components/api'
|
||||||
|
|
||||||
const queryParams = ref({ searchKey: '', pageNum: 1, pageSize: 20 })
|
const queryParams = ref({ searchKey: '', pageNum: 1, pageSize: 10 })
|
||||||
const dateRange = ref([])
|
const dateRange = ref([])
|
||||||
const tableData = ref([])
|
const tableData = ref([])
|
||||||
const detailVisible = ref(false)
|
const detailVisible = ref(false)
|
||||||
@@ -207,7 +207,7 @@ const handleQuery = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const resetQuery = () => {
|
const resetQuery = () => {
|
||||||
queryParams.value = { searchKey: '', pageNum: 1, pageSize: 20 }
|
queryParams.value = { searchKey: '', pageNum: 1, pageSize: 10 }
|
||||||
dateRange.value = []
|
dateRange.value = []
|
||||||
handleQuery()
|
handleQuery()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -298,7 +298,7 @@ const statCards = ref([
|
|||||||
{label:'医嘱回写', value:0, color:'#f56c6c'}
|
{label:'医嘱回写', value:0, color:'#f56c6c'}
|
||||||
])
|
])
|
||||||
|
|
||||||
const q = ref({pageNo:1, pageSize:20, feedbackType:'', feedbackStatus:''})
|
const q = ref({pageNo:1, pageSize: 10, feedbackType:'', feedbackStatus:''})
|
||||||
const formData = ref({
|
const formData = ref({
|
||||||
consultationId:'', patientName:'', feedbackType:'PROGRESS_NOTES', contentSummary:''
|
consultationId:'', patientName:'', feedbackType:'PROGRESS_NOTES', contentSummary:''
|
||||||
})
|
})
|
||||||
@@ -334,7 +334,7 @@ async function loadData() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function resetQuery() {
|
function resetQuery() {
|
||||||
q.value = {pageNo:1, pageSize:20, feedbackType:'', feedbackStatus:''}
|
q.value = {pageNo:1, pageSize: 10, feedbackType:'', feedbackStatus:''}
|
||||||
loadData()
|
loadData()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -157,7 +157,7 @@ import {ref,onMounted} from 'vue'
|
|||||||
import {ElMessage} from 'element-plus'
|
import {ElMessage} from 'element-plus'
|
||||||
import {getPage,checkTimeout} from './api'
|
import {getPage,checkTimeout} from './api'
|
||||||
const tableData=ref([]);const total=ref(0)
|
const tableData=ref([]);const total=ref(0)
|
||||||
const q=ref({pageNo:1,pageSize:20,requestDept:'',timeoutFlag:null})
|
const q=ref({pageNo:1,pageSize: 10,requestDept:'',timeoutFlag:null})
|
||||||
const loadData=async()=>{const r=await getPage(q.value);tableData.value=r.data?.records||[];total.value=r.data?.total||0}
|
const loadData=async()=>{const r=await getPage(q.value);tableData.value=r.data?.records||[];total.value=r.data?.total||0}
|
||||||
const runTimeoutCheck=async()=>{const r=await checkTimeout();ElMessage.info('发现 '+r.data.overdueCount+' 条超时会诊');loadData()}
|
const runTimeoutCheck=async()=>{const r=await checkTimeout();ElMessage.info('发现 '+r.data.overdueCount+' 条超时会诊');loadData()}
|
||||||
const rowClass=({row})=>row.timeoutFlag?'timeout-row':''
|
const rowClass=({row})=>row.timeoutFlag?'timeout-row':''
|
||||||
|
|||||||
@@ -112,7 +112,7 @@
|
|||||||
import {ref,onMounted} from 'vue'
|
import {ref,onMounted} from 'vue'
|
||||||
import {getPage,getSummary} from './api'
|
import {getPage,getSummary} from './api'
|
||||||
const tableData=ref([]);const total=ref(0);const summary=ref({})
|
const tableData=ref([]);const total=ref(0);const summary=ref({})
|
||||||
const q=ref({pageNo:1,pageSize:20,statMonth:'',deptName:''})
|
const q=ref({pageNo:1,pageSize: 10,statMonth:'',deptName:''})
|
||||||
const loadData=async()=>{const r=await getPage(q.value);tableData.value=r.data?.records||[];total.value=r.data?.total||0}
|
const loadData=async()=>{const r=await getPage(q.value);tableData.value=r.data?.records||[];total.value=r.data?.total||0}
|
||||||
const refreshSummary=async()=>{const r=await getSummary({statMonth:q.value.statMonth||'2026-06'});summary.value=r.data||{}}
|
const refreshSummary=async()=>{const r=await getSummary({statMonth:q.value.statMonth||'2026-06'});summary.value=r.data||{}}
|
||||||
onMounted(()=>{loadData();refreshSummary()})
|
onMounted(()=>{loadData();refreshSummary()})
|
||||||
|
|||||||
@@ -190,7 +190,7 @@ import {ref,onMounted} from 'vue'
|
|||||||
import {ElMessage} from 'element-plus'
|
import {ElMessage} from 'element-plus'
|
||||||
import {getPage,getSummary,stopSale,dispose} from './api'
|
import {getPage,getSummary,stopSale,dispose} from './api'
|
||||||
const tableData=ref([]);const total=ref(0);const summary=ref({})
|
const tableData=ref([]);const total=ref(0);const summary=ref({})
|
||||||
const q=ref({pageNo:1,pageSize:20,drugName:'',alertLevel:''})
|
const q=ref({pageNo:1,pageSize: 10,drugName:'',alertLevel:''})
|
||||||
const loadData=async()=>{const r=await getPage(q.value);tableData.value=r.data?.records||[];total.value=r.data?.total||0}
|
const loadData=async()=>{const r=await getPage(q.value);tableData.value=r.data?.records||[];total.value=r.data?.total||0}
|
||||||
const refreshSummary=async()=>{const r=await getSummary();summary.value=r.data||{}}
|
const refreshSummary=async()=>{const r=await getSummary();summary.value=r.data||{}}
|
||||||
const stopSaleItem=async(id)=>{await stopSale(id);ElMessage.success('已停售');loadData();refreshSummary()}
|
const stopSaleItem=async(id)=>{await stopSale(id);ElMessage.success('已停售');loadData();refreshSummary()}
|
||||||
|
|||||||
@@ -139,7 +139,7 @@
|
|||||||
import {ref,onMounted} from 'vue'
|
import {ref,onMounted} from 'vue'
|
||||||
import {getPage} from './api'
|
import {getPage} from './api'
|
||||||
const tableData=ref([]);const total=ref(0)
|
const tableData=ref([]);const total=ref(0)
|
||||||
const q=ref({pageNo:1,pageSize:20,ambulanceId:'',linkStatus:''})
|
const q=ref({pageNo:1,pageSize: 10,ambulanceId:'',linkStatus:''})
|
||||||
const loadData=async()=>{const r=await getPage(q.value);tableData.value=r.data?.records||[];total.value=r.data?.total||0}
|
const loadData=async()=>{const r=await getPage(q.value);tableData.value=r.data?.records||[];total.value=r.data?.total||0}
|
||||||
onMounted(()=>loadData())
|
onMounted(()=>loadData())
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -125,7 +125,7 @@
|
|||||||
import {ref,onMounted} from 'vue'
|
import {ref,onMounted} from 'vue'
|
||||||
import {getPage} from './api'
|
import {getPage} from './api'
|
||||||
const tableData=ref([]);const total=ref(0)
|
const tableData=ref([]);const total=ref(0)
|
||||||
const q=ref({pageNo:1,pageSize:20,antibioticLevel:'',auditStatus:''})
|
const q=ref({pageNo:1,pageSize: 10,antibioticLevel:'',auditStatus:''})
|
||||||
const loadData=async()=>{const r=await getPage(q.value);tableData.value=r.data?.records||[];total.value=r.data?.total||0}
|
const loadData=async()=>{const r=await getPage(q.value);tableData.value=r.data?.records||[];total.value=r.data?.total||0}
|
||||||
onMounted(()=>loadData())
|
onMounted(()=>loadData())
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -152,7 +152,7 @@
|
|||||||
import {ref,onMounted} from 'vue'
|
import {ref,onMounted} from 'vue'
|
||||||
import {getPage} from './api'
|
import {getPage} from './api'
|
||||||
const tableData=ref([]);const total=ref(0)
|
const tableData=ref([]);const total=ref(0)
|
||||||
const q=ref({pageNo:1,pageSize:20,consentType:'',consentStatus:''})
|
const q=ref({pageNo:1,pageSize: 10,consentType:'',consentStatus:''})
|
||||||
const loadData=async()=>{const r=await getPage(q.value);tableData.value=r.data?.records||[];total.value=r.data?.total||0}
|
const loadData=async()=>{const r=await getPage(q.value);tableData.value=r.data?.records||[];total.value=r.data?.total||0}
|
||||||
onMounted(()=>loadData())
|
onMounted(()=>loadData())
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -144,7 +144,7 @@
|
|||||||
import {ref,onMounted} from 'vue'
|
import {ref,onMounted} from 'vue'
|
||||||
import {getPage} from './api'
|
import {getPage} from './api'
|
||||||
const tableData=ref([]);const total=ref(0)
|
const tableData=ref([]);const total=ref(0)
|
||||||
const q=ref({pageNo:1,pageSize:20,alertType:'',alertLevel:''})
|
const q=ref({pageNo:1,pageSize: 10,alertType:'',alertLevel:''})
|
||||||
const loadData=async()=>{const r=await getPage(q.value);tableData.value=r.data?.records||[];total.value=r.data?.total||0}
|
const loadData=async()=>{const r=await getPage(q.value);tableData.value=r.data?.records||[];total.value=r.data?.total||0}
|
||||||
onMounted(()=>loadData())
|
onMounted(()=>loadData())
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -174,7 +174,7 @@
|
|||||||
import {ref,onMounted} from 'vue'
|
import {ref,onMounted} from 'vue'
|
||||||
import {getPage} from './api'
|
import {getPage} from './api'
|
||||||
const tableData=ref([]);const total=ref(0)
|
const tableData=ref([]);const total=ref(0)
|
||||||
const q=ref({pageNo:1,pageSize:20,assessmentType:'',riskLevel:''})
|
const q=ref({pageNo:1,pageSize: 10,assessmentType:'',riskLevel:''})
|
||||||
const loadData=async()=>{const r=await getPage(q.value);tableData.value=r.data?.records||[];total.value=r.data?.total||0}
|
const loadData=async()=>{const r=await getPage(q.value);tableData.value=r.data?.records||[];total.value=r.data?.total||0}
|
||||||
onMounted(()=>loadData())
|
onMounted(()=>loadData())
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -152,7 +152,7 @@
|
|||||||
import {ref,onMounted} from 'vue'
|
import {ref,onMounted} from 'vue'
|
||||||
import {getPage} from './api'
|
import {getPage} from './api'
|
||||||
const tableData=ref([]);const total=ref(0)
|
const tableData=ref([]);const total=ref(0)
|
||||||
const q=ref({pageNo:1,pageSize:20,deptName:'',shiftType:''})
|
const q=ref({pageNo:1,pageSize: 10,deptName:'',shiftType:''})
|
||||||
const loadData=async()=>{const r=await getPage(q.value);tableData.value=r.data?.records||[];total.value=r.data?.total||0}
|
const loadData=async()=>{const r=await getPage(q.value);tableData.value=r.data?.records||[];total.value=r.data?.total||0}
|
||||||
onMounted(()=>loadData())
|
onMounted(()=>loadData())
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -269,7 +269,7 @@ import {ElMessage} from 'element-plus'
|
|||||||
import {getPage,getUnreadCount,markRead,handleAlert} from './api'
|
import {getPage,getUnreadCount,markRead,handleAlert} from './api'
|
||||||
const tableData=ref([]);const total=ref(0);const unreadCount=ref(0)
|
const tableData=ref([]);const total=ref(0);const unreadCount=ref(0)
|
||||||
const handleVisible=ref(false);const handleForm=ref({handler:'',handleResult:''});let currentId=null
|
const handleVisible=ref(false);const handleForm=ref({handler:'',handleResult:''});let currentId=null
|
||||||
const q=ref({pageNo:1,pageSize:20,alertType:'',alertLevel:'',handleFlag:null})
|
const q=ref({pageNo:1,pageSize: 10,alertType:'',alertLevel:'',handleFlag:null})
|
||||||
const loadData=async()=>{const r=await getPage(q.value);tableData.value=r.data?.records||[];total.value=r.data?.total||0;refreshUnread()}
|
const loadData=async()=>{const r=await getPage(q.value);tableData.value=r.data?.records||[];total.value=r.data?.total||0;refreshUnread()}
|
||||||
const refreshUnread=async()=>{const r=await getUnreadCount();unreadCount.value=r.data||0}
|
const refreshUnread=async()=>{const r=await getUnreadCount();unreadCount.value=r.data||0}
|
||||||
const markReadItem=async(id)=>{await markRead(id);loadData()}
|
const markReadItem=async(id)=>{await markRead(id);loadData()}
|
||||||
|
|||||||
@@ -150,7 +150,7 @@
|
|||||||
import {ref,onMounted} from 'vue'
|
import {ref,onMounted} from 'vue'
|
||||||
import {getPage} from './api'
|
import {getPage} from './api'
|
||||||
const tableData=ref([]);const total=ref(0)
|
const tableData=ref([]);const total=ref(0)
|
||||||
const q=ref({pageNo:1,pageSize:20,patientName:'',checkResult:'',fixStatus:''})
|
const q=ref({pageNo:1,pageSize: 10,patientName:'',checkResult:'',fixStatus:''})
|
||||||
const loadData=async()=>{const r=await getPage(q.value);tableData.value=r.data?.records||[];total.value=r.data?.total||0}
|
const loadData=async()=>{const r=await getPage(q.value);tableData.value=r.data?.records||[];total.value=r.data?.total||0}
|
||||||
onMounted(()=>loadData())
|
onMounted(()=>loadData())
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user