assets/2026-07-01-医数智联系列开篇-医院数字化工作者的工具箱_2026-07-01_18-28-11.jpg

医数智联·第 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
);

-- DRG 入组结果
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
# -*- coding: utf-8 -*-
"""
首页质控仪表盘 - 每日数据流水线
"""
import pandas as pd
import pymysql
import json
from pathlib import Path
from datetime import datetime, timedelta
import logging

logging.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'
)

# 1. 总体数据
总数据 = 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()}')

# 2. 科室 TOP 10
科室数据 = 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)

# 3. Top 错误医生
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)

# 4. 月度趋势(近 12 个月)
月度趋势 = 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)

# 5. 输出 JSON(给 Obsidian Dataview 用)
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] 操作建议

  1. 每日查看核心指标(2 分钟)
  2. 每周看 Top 科室和 Top 医生(5 分钟)
  3. 每月看趋势变化(10 分钟)
  4. 重大问题 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」一脸懵
解决:指标要通俗,加注释:

1
总错误率(%):本月主诊错误的病案占比

[!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 流水线,我已经整理成「首页质控仪表盘工具包」。点击「阅读原文」可直接下载

我是白衣狼,一个用数字化工具把医院管理做「轻」的实战派。咱们下期见。