医数智联·第 30 期:实战案例 —— 用 4 工具搭建「首页质控仪表盘」,让院长随时看数据
[!ABSTRACT] 核心摘要
本期主题 :把 29 期所有技能串成「首页质控仪表盘」——让院长随时看实时数据
核心结论 :Obsidian(展示)+ MySQL(数据)+ Python(自动化)+ Claude Code(智能),4 工具联手,就是 2026 年医院数字化的最佳实践
完整架构 :数据层 (MySQL)→ 加工层 (Python 流水线)→ 展示层 (Obsidian)→ 智能层 (Claude Code)
典型效果 :院长手机随时打开 Obsidian 看实时数据,重大问题 24 小时内发现
阅读时长 :约 10 分钟
一、场景引入:为什么院长需要「首页质控仪表盘」?
[!example] 一个真实的周二上午 院长办公室:
院长:「上月主诊错误率多少?」 → 质管办小李:「我查一下 」(半天) 院长:「骨科和心内科哪个错误率高?」 → 小李:「我再查一下 」(1 天) 院长:「DRG 入组异常有多少?」 → 小李:「我去找信息科 」(3 天)
院长想要的不是「一个数字」,是「随时能看的实时数据」 。
现状:
数据在 HIS(查不了)
报表在 Excel(不及时)
分析在 SQL(不会写)
图表在 PPT(过时了)
院长的「数据胃口」永远喂不饱。
这是医院数据管理的终极困境——院长想要「实时仪表盘」,但传统工具做不到 。
现状
痛点
每月一次 Excel 报表
不及时
找信息科跑数据
慢(3 天)
PPT 汇报
静态、过期
院长临时要数据
手忙脚乱
[!quote] 狼叔的判断“院长不需要’报表’,院长需要’仪表盘’——实时、可视、可钻取。” 这一期,带你用 4 工具搭建一个完整的「首页质控仪表盘」,让院长随时看数据 。
二、4 工具联动架构 2.1 整体架构图 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ┌────────────────────────────────────────────┐ │ 智能层(Claude Code) │ │ 自然语言问数据 / 智能分析 / 自动报告 │ └─────────────┬───────────────────────────────┘ │ ┌─────────────▼───────────────────────────────┐ │ 展示层(Obsidian) │ │ MOC 主页 + Dataview 查询 + 图表 + 实时更新 │ └─────────────┬───────────────────────────────┘ │ ┌─────────────▼───────────────────────────────┐ │ 加工层(Python) │ │ 自动校验 + DRG 筛查 + 图表生成 + PDF 报告 │ └─────────────┬───────────────────────────────┘ │ ┌─────────────▼───────────────────────────────┐ │ 数据层(MySQL) │ │ 首页主表 / 诊断表 / 手术表 / DRG 表 │ └────────────────────────────────────────────┘
2.2 4 工具的核心职责
工具
职责
替代什么
MySQL
数据存储 + 查询
手工 Excel 表
Python
自动化加工 + 校验
手工操作
Obsidian
可视化展示 + 知识沉淀
PPT 报表
Claude Code
智能问询 + 报告生成
人工分析
三、医院实战:5 步搭建仪表盘 Step 1:数据层(MySQL 建表)
[!example] 实战 ①:数据库表结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 CREATE TABLE inpatient_main ( record_id VARCHAR (20 ) PRIMARY KEY , discharge_date DATE , dept_name VARCHAR (50 ), doctor_id VARCHAR (20 ), error_flag INT , error_code VARCHAR (20 ), length_of_stay INT , total_cost DECIMAL (10 ,2 ) ); CREATE TABLE diagnosis ( id INT AUTO_INCREMENT PRIMARY KEY , record_id VARCHAR (20 ), icd_code VARCHAR (20 ), diagnosis_type VARCHAR (10 ), is_mcc BOOLEAN ); CREATE TABLE doctor ( doctor_id VARCHAR (20 ) PRIMARY KEY , doctor_name VARCHAR (50 ), title_id INT , dept_id INT ); CREATE TABLE drg_result ( record_id VARCHAR (20 ) PRIMARY KEY , adrg_code VARCHAR (20 ), drg_weight DECIMAL (8 ,4 ), mdc_group VARCHAR (10 ) );
[!TIP] 索引优化
1 2 3 CREATE INDEX idx_discharge ON inpatient_main (discharge_date);CREATE INDEX idx_dept ON inpatient_main (dept_name);CREATE INDEX idx_record ON diagnosis (record_id);
Step 2:加工层(Python 流水线)
[!example] 实战 ②:Python 自动跑数据 → 输出 JSON
daily_pipeline.py :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 """ 首页质控仪表盘 - 每日数据流水线 """ import pandas as pdimport pymysqlimport jsonfrom pathlib import Pathfrom datetime import datetime, timedeltaimport logginglogging.basicConfig(level=logging.INFO) OUTPUT_DIR = Path('D:/dashboard/data' ) OUTPUT_DIR.mkdir(parents=True , exist_ok=True ) def main (): """每日跑一次,生成仪表盘数据""" conn = pymysql.connect( host='192.168.1.100' , user='quality_user' , password='xxx' , database='his_db' , charset='utf8mb4' ) 总数据 = pd.read_sql(''' SELECT COUNT(*) AS 总病案数, SUM(error_flag) AS 总错误数, ROUND(SUM(error_flag) * 100.0 / COUNT(*), 2) AS 总错误率 FROM inpatient_main WHERE discharge_date >= DATE_SUB(CURDATE(), INTERVAL 30 DAY) ''' , conn) logging.info(f'总数据:{总数据.iloc[0 ].to_dict()} ' ) 科室数据 = pd.read_sql(''' SELECT dept_name AS 科室, COUNT(*) AS 总数, SUM(error_flag) AS 错误数, ROUND(SUM(error_flag) * 100.0 / COUNT(*), 2) AS 错误率 FROM inpatient_main WHERE discharge_date >= DATE_SUB(CURDATE(), INTERVAL 30 DAY) GROUP BY dept_name ORDER BY 错误率 DESC LIMIT 10 ''' , conn) Top医生 = pd.read_sql(''' SELECT d.doctor_name AS 医生, dept.dept_name AS 科室, t.title_name AS 职称, COUNT(*) AS 错误数 FROM inpatient_main im INNER JOIN doctor d ON im.doctor_id = d.doctor_id LEFT JOIN dept ON d.dept_id = dept.dept_id LEFT JOIN title t ON d.title_id = t.title_id WHERE im.error_flag = 1 AND im.discharge_date >= DATE_SUB(CURDATE(), INTERVAL 30 DAY) GROUP BY d.doctor_id, d.doctor_name, dept.dept_name, t.title_name ORDER BY 错误数 DESC LIMIT 10 ''' , conn) 月度趋势 = pd.read_sql(''' SELECT DATE_FORMAT(discharge_date, '%Y-%m') AS 月份, COUNT(*) AS 总数, SUM(error_flag) AS 错误数, ROUND(SUM(error_flag) * 100.0 / COUNT(*), 2) AS 错误率 FROM inpatient_main WHERE discharge_date >= DATE_SUB(CURDATE(), INTERVAL 12 MONTH) GROUP BY 月份 ORDER BY 月份 ''' , conn) dashboard_data = { '更新时间' : datetime.now().strftime('%Y-%m-%d %H:%M:%S' ), '总体' : 总数据.iloc[0 ].to_dict(), '科室Top10' : 科室数据.to_dict('records' ), 'Top错误医生' : Top医生.to_dict('records' ), '月度趋势' : 月度趋势.to_dict('records' ) } with open (OUTPUT_DIR / 'dashboard.json' , 'w' , encoding='utf-8' ) as f: json.dump(dashboard_data, f, ensure_ascii=False , indent=2 ) logging.info(f'✓ 仪表盘数据已生成:{OUTPUT_DIR / "dashboard.json" } ' ) conn.close() if __name__ == '__main__' : main()
定时任务 (Windows 任务计划程序):
每天 02:00 自动跑 python daily_pipeline.py
Step 3:展示层(Obsidian MOC 主页)
[!example] 实战 ③:Obsidian MOC 仪表盘主页
D:\仪表盘\00-仪表盘主页.md :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 --- title: 首页质控仪表盘 tags: - 仪表盘 - 质控 - MOC --- # 🏥 首页质控仪表盘 > [!info] 数据更新 > 最后更新:{{更新时间}} > 数据来源:MySQL inpatient_main > 自动刷新:每日 02:00 --- ## 📊 核心指标(实时) ```dataview TABLE 总病案数 AS "总病案数", 总错误数 AS "总错误数", 总错误率 AS "错误率(%)" FROM "D:/仪表盘/data/dashboard.json"
🏆 Top 10 错误科室(30 天) 1 2 3 4 5 6 7 8 9 TABLE 科室, 总数, 错误数, 错误率 FROM "D:/仪表盘/data/dashboard.json" WHERE 包含 "科室Top10" SORT 错误率 DESC LIMIT 10
👨⚕️ Top 10 错误医生(30 天) 1 2 3 4 5 6 7 8 TABLE 医生, 科室, 职称, 错误数 FROM "D:/仪表盘/data/dashboard.json" SORT 错误数 DESC LIMIT 10
📈 月度趋势(12 个月) 1 2 3 4 5 6 7 TABLE 月份, 总数, 错误数, 错误率 FROM "D:/仪表盘/data/dashboard.json" SORT 月份 ASC
[!TIP] 操作建议
每日查看核心指标 (2 分钟)
每周看 Top 科室和 Top 医生 (5 分钟)
每月看趋势变化 (10 分钟)
重大问题 24 小时内开会整改
1 2 3 4 5 ### Step 4:智能层(Claude Code 接入) > [!example] 实战 ④:用 Claude Code 智能问数据
👤 院长:@Claude Code 上月骨科出院了多少人?错误率多少?
Claude Code: 好的,我从 dashboard.json 查:
📊 骨科数据(2026-06):
出院人数:285 人
主诊错误数:28 例
主诊错误率:9.82%
比上月 +1.2pp,需要关注
(数据来源:D:\仪表盘\data\dashboard.json)
1 2 3 4 5 6 7 8 👤 院长:@Claude Code 把本月错误率最高 5 个医生列出来,自动发邮件给科主任 Claude Code: 好的,我从 dashboard.json 提取 Top 5 医生,自动生成邮件: (自动跑 Python) ✓ Top 5 医生已提取 ✓ 邮件已生成 ✓ 已发送至骨科主任、心内科主任、神经科主任
Step 5:院长的「手机仪表盘」
[!example] 实战 ⑤:院长手机随时看
方案 1:Obsidian 移动端
院长手机装 Obsidian(免费)
把 D:\仪表盘\ 同步到云盘(iCloud/坚果云)
随时打开看实时数据
方案 2:Obsidian Publish(发布到 Web)
Obsidian 官方提供「Publish」服务(每月 $4)
仪表盘发布为网页,任何设备浏览器可访问
无需安装 App
方案 3:每天自动发邮件
每天 02:00 跑流水线
02:30 自动发邮件给院长
院长每天 08:00 醒来看到
四、避坑指南:5 个让仪表盘「失灵」的常见错误
[!warning] 雷区 ①:数据更新不及时症状 :仪表盘显示 1 个月前的数据,院长打开发现「过期」 。解决 :配置定时任务 ,每天自动跑流水线。
[!warning] 雷区 ②:JSON 路径错误,Dataview 查不到症状 :Obsidian 仪表盘显示「无数据」。解决 :路径用绝对路径 + 正斜杠 :"D:/仪表盘/data/dashboard.json"。
[!warning] 雷区 ③:院长看不懂指标症状 :院长看到「MCC 占比」「ADRG」一脸懵 。解决 :指标要通俗 ,加注释:
[!warning> 雷区 ④:权限失控,敏感数据泄露症状 :院长仪表盘含医生姓名,医生抗议 。解决 :权限分级 :
院长版:含医生姓名
医生版:仅自己科室,且无医生姓名
公开版:仅总体数据
[!warning> 雷区 ⑤:数据孤岛,仪表盘和报告脱节症状 :仪表盘数据和月度报告数据对不上。解决 :统一数据源 :仪表盘数据 = 报告数据 = 实际数据。
五、仪表盘实施 5 阶段
[!TIP] 5 阶段实施
阶段 1(1 周) :MySQL 表结构 + Python 读数据 → JSON阶段 2(1 周) :Obsidian MOC 主页 + Dataview 查询阶段 3(1 周) :定时任务 + 自动更新阶段 4(1 周) :院长手机/邮箱接入阶段 5(持续) :数据脱敏 + 权限管理 + 持续优化
总计 1 个月 ,仪表盘上线。
六、下期预告
[!quote] 第 31 期(系列收官)从「工具使用者」到「工具创造者」——医数智联 30 期总结与思维升级
这一期,我们用 1 篇文章回顾整个 30 期历程,并展望未来:医院人的下一站,是「创造工具」,不是「使用工具」 。
[!TIP] 互动 如果你觉得这篇「医数智联·第 30 期」对你有用,请点赞、在看、转发 给科里的同事。
文中完整仪表盘代码、Obsidian 模板、Python 流水线,我已经整理成「首页质控仪表盘工具包」。点击「阅读原文」可直接下载 。
我是白衣狼,一个用数字化工具把医院管理做「轻」的实战派。咱们下期见。