一个覆盖 15 个业务模块、23 张数据表、68 个 API 端点的完整医院信息系统,从门诊挂号到住院结算,从药房库存到 AI 智能辅助,从医护工作台到患者自助门户。
一、项目概述
医院信息系统(Hospital Information System,HIS) 是医院运营的数字中枢。本项目从零搭建了一套 Web 架构的 HIS,后端采用 Python FastAPI + SQLAlchemy 2.0(异步)+ MySQL,前端采用 Vue 3 + Vite + Element Plus,并集成了 LangChain / LangGraph 多智能体 AI 引擎,支持自然语言问诊、处方审核和智能问数。
核心数字
| 指标 |
数值 |
| 业务模块 |
15 个 |
| 数据库表 |
23 张 |
| API 端点 |
68 个 |
| 用户角色 |
6 种(admin / doctor / nurse / cashier / pharmacist / patient) |
| 前端页面 |
20+ 个 SFC 组件 |
二、系统架构总览
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| ┌─────────────────────────────────────────────────────┐ │ 前端层 (Vue 3) │ │ ┌──────────────┐ ┌──────────────────────────────┐ │ │ │ 医护端 /index │ │ 患者端 /patient │ │ │ │ 14个功能页面 │ │ 5个自助页面 │ │ │ └──────────────┘ └──────────────────────────────┘ │ ├─────────────────────────────────────────────────────┤ │ API 网关 (FastAPI) │ │ ┌──────┐┌──────┐┌──────┐┌──────┐┌──────┐┌──────┐ │ │ │挂号 ││处方 ││药房 ││住院 ││收费 ││AI │ │ │ │路由 ││路由 ││路由 ││路由 ││路由 ││路由 │ │ │ └──────┘└──────┘└──────┘└──────┘└──────┘└──────┘ │ ├─────────────────────────────────────────────────────┤ │ 数据层 │ │ ┌────────────────┐ ┌────────────────────────────┐ │ │ │ MySQL (aiomysql)│ │ Qdrant (向量数据库) │ │ │ │ 23张业务表 │ │ RAG 知识库检索 │ │ │ └────────────────┘ └────────────────────────────┘ │ │ ┌────────────────┐ ┌────────────────────────────┐ │ │ │ Redis (缓存) │ │ LangGraph (多智能体引擎) │ │ │ └────────────────┘ └────────────────────────────┘ │ └─────────────────────────────────────────────────────┘
|
技术选型理由
- FastAPI + 异步 SQLAlchemy:原生 async/await 支持,与 aiomysql 配合实现非阻塞数据库操作,单进程即可承载高并发
- 单体路由文件:15 个 APIRouter 集中注册在
routers.py,依赖注入(Depends)模式保证鉴权和数据库会话的一致性
- Vue 3 + Element Plus:成熟的医疗后台 UI 体系,表格/表单/弹窗等组件开箱即用
- LangGraph:多智能体协作框架,Router Agent 自动分类用户意图后分发给 Consultant / Medical / Science / Tool 四个专业 Agent
三、15 个业务模块全景
| # |
模块 |
路由前缀 |
核心表 |
角色 |
| 1 |
认证与权限 |
/api/auth |
users |
全部 |
| 2 |
患者管理 |
/api/patients |
patients |
admin/doctor/nurse/cashier |
| 3 |
门诊挂号 |
/api/registrations |
registrations |
admin/doctor/cashier |
| 4 |
处方管理 |
/api/prescriptions |
prescriptions, prescription_items |
admin/doctor |
| 5 |
药品主档 |
/api/drugs |
drugs |
admin/pharmacist |
| 6 |
药房管理 |
/api/pharmacy |
pharmacy_inventory |
admin/pharmacist |
| 7 |
药库管理 |
/api/warehouse |
warehouse_inventory, drug_transactions |
admin/pharmacist |
| 8 |
住院管理 |
/api/admissions |
admissions, admission_fee_items, beds |
admin/nurse/cashier |
| 9 |
护士工作站 |
/api/nurse |
beds, medical_orders, vital_signs |
admin/nurse |
| 10 |
医嘱管理 |
/api/orders |
medical_orders |
admin/doctor |
| 11 |
特殊收费 |
/api/charges |
special_charges, charge_items |
admin/cashier |
| 12 |
院长查询 |
/api/director |
聚合查询 |
admin |
| 13 |
统一收费 |
/api/billing |
billing_records |
admin/cashier |
| 14 |
AI 助手 |
/api/ai |
ai_sessions |
全部 |
| 15 |
患者自助 |
/api/patient-self |
归属查询 |
patient |
四、核心业务流程详解
4.1 门诊就诊全链路
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ ① 挂号 │───→│ ② 就诊 │───→│ ③ 开方 │───→│ ④ 收费 │───→│ ⑤ 发药 │ │ 收费处 │ │ 医生站 │ │ 医生站 │ │ 收费处 │ │ 药房 │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ │ │ ¥5 (普通) / ¥20 (专家) │ 处方费 │ ¥15 (急诊) / ¥10 (专科) │ 药品单价 × 数量 │ │ ▼ ▼ ┌─────────────────────────────────────────────────────────────┐ │ billing_records (统一收费主表) │ │ ┌─────────────────┐ ┌─────────────────┐ │ │ │ 挂号收费 ¥5.00 │ │ 门诊处方 ¥112.50 │ │ │ └─────────────────┘ └─────────────────┘ │ │ 今日收入: ¥117.50 (自动汇总) │ └─────────────────────────────────────────────────────────────┘
|
技术实现要点:
- 挂号:
POST /api/registrations → 根据 reg_type 自动匹配挂号费(FEE_MAP),初始状态 payment_status=PENDING
- 缴费:
PATCH /api/registrations/{id}/pay → 状态更新为 PAID + 写入 paid_at 时间戳 + 事务性写入统一收费主表 billing_records
- 开方:
POST /api/prescriptions → 自动查询药品单价(drug.retail_price),计算 total_amount = Σ(unit_price × quantity)
- 处方缴费:
PATCH /api/prescriptions/{id}/pay → 状态更新 + 写入 billing_records(通过 pres.registration.patient_id 获取患者 ID)
- 发药:
PATCH /api/prescriptions/{id}/dispense → 校验药房库存 → 扣减 pharmacy_inventory.stock_qty → 生成 drug_transactions 流水
防重复收费机制:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| if reg.payment_status == PaymentStatus.PAID: raise HTTPException(409, "已收费,不可重复")
existing = await db.execute( select(BillingRecord).where( BillingRecord.charge_type == "挂号收费", BillingRecord.source_id == reg_id, BillingRecord.status == "已收", ) ) if existing.scalar_one_or_none(): raise HTTPException(409, "已生成收费记录")
|
4.2 药品供应链:药库 → 药房 → 患者
1 2 3 4 5 6 7 8 9 10 11 12
| ┌──────────────┐ 调拨 ┌──────────────┐ 发药 ┌──────────────┐ │ 药库 │ ─────────→ │ 药房 │ ─────────→ │ 患者 │ │ warehouse │ │ pharmacy │ │ prescription │ │ _inventory │ │ _inventory │ │ .dispensed │ └──────────────┘ └──────────────┘ └──────────────┘ │ │ │ 入库 (stock-in) │ 盘点 / 退药 ▼ ▼ ┌─────────────────────────────────────────────────────────┐ │ drug_transactions (出入库流水) │ │ 入库 +100 │ 调拨出库 -50 │ 调拨入库 +50 │ 发药出库 -9 │ └─────────────────────────────────────────────────────────┘
|
4.3 住院全流程
1 2 3 4 5 6 7 8 9
| 入院登记 → 押金缴纳 → 医嘱执行 → 每日清单 → 出院结算 → 释放床位 │ │ │ │ beds.status=OCCUPIED │ medical_orders │ beds.status=AVAILABLE │ │ vital_signs │ admission.settled=True ▼ ▼ ▼ ┌──────────────────────────────────────────────────────────┐ │ billing_records │ │ 住院结算 (total_fee) │ └──────────────────────────────────────────────────────────┘
|
4.4 AI 智能辅助链路
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 用户消息 → Router Agent (意图分类) │ ┌─────────┼─────────┐ ▼ ▼ ▼ ▼ Consultant Medical Science Tool (闲聊) (诊疗) (科普) (数据查询) │ │ │ ▼ ▼ ▼ search_kb search_kb 4个工具函数 drug_interaction (查数据/统计/报表) summarize_patient │ ▼ Qdrant RAG (向量检索知识库) │ ▼ LLM 生成回复 → 返回用户
|
5 个 LangGraph Agent:
| Agent |
触发条件 |
能力 |
| Router |
所有请求 |
关键词 + LLM 双重意图分类 |
| Consultant |
日常咨询 |
普通对话,不走 RAG |
| Medical |
症状/处方 |
诊疗分析 + 可调工具 |
| Science |
健康科普 |
自动判断是否需检索 |
| Tool |
数据查询 |
自然语言 → 工具匹配 → 执行 → 回复 |
五、统一收费主表设计(核心重构)
问题背景
旧系统存在两个关键缺陷:
- 「今日收入」仅统计挂号费(如 ¥5 的挂号费),处方收费(¥112.50)未被计入
- 「收费历史」缺失挂号费记录,患者就诊-收费全链路断裂
解决方案:BillingRecord 统一收费主表
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| CREATE TABLE billing_records ( id INT PRIMARY KEY AUTO_INCREMENT, bill_no VARCHAR(30) UNIQUE NOT NULL, charge_type VARCHAR(20) NOT NULL, source_id INT NOT NULL, patient_id INT NOT NULL, total_amount DECIMAL(10,2) DEFAULT 0, paid_amount DECIMAL(10,2) DEFAULT 0, operator_id INT, charge_time DATETIME NOT NULL, status VARCHAR(20) DEFAULT '已收', remark VARCHAR(200), created_at DATETIME DEFAULT NOW() );
|
写穿透模式 — 所有付费操作在修改源表状态的同时,事务性地写入 billing_records:
1 2 3 4 5 6 7 8
| PATCH /registrations/{id}/pay ──→ reg.payment_status = PAID ──→ INSERT billing_records (挂号收费)
PATCH /prescriptions/{id}/pay ──→ pres.payment_status = PAID ──→ INSERT billing_records (门诊处方)
PATCH /admissions/{id}/discharge → adm.settled = True ──→ INSERT billing_records (住院结算)
|
改造收益:
| 场景 |
旧逻辑 |
新逻辑 |
| 今日收入统计 |
SUM(registrations.reg_fee) |
SUM(billing_records.paid_amount) |
| 收入拆分 |
不支持 |
GROUP BY charge_type 三项拆分 |
| 收费历史 |
前端合并 2 个 API |
单一 /api/billing/history |
| 对账校验 |
无 |
/api/billing/reconciliation 自动比对 |
六、双入口门户设计(医护端 + 患者端)
传统 HIS 仅面向医护人员。本次升级将系统定位为 医护 + 患者共用平台:
1 2 3 4
| 登录页 ──→ [🩺 医护登录] ──→ /index (14 个功能页面) │ └──→ [👤 患者入口] ──→ /patient (5 个自助页面) 数据隔离:只能查本人 patient_id 关联的数据
|
角色隔离机制:
- 路由守卫:
patient 角色访问 /index/* → 自动重定向 /patient
- API 层:所有
/api/patient-self/* 端点强制 require_role("patient") + 数据归属校验
- 账户接管防护:自助注册始终创建新 Patient 记录,不关联已有档案
七、智能特性汇总
| 特性 |
端点 |
说明 |
| 智能对账 |
/api/billing/reconciliation |
收费主表 vs 各源表金额自动比对 |
| 异常检测 |
/api/billing/anomalies |
大额收费(≥¥5000)、实付与总额不符 |
| 患者追溯 |
/api/billing/timeline/{id} |
挂号+处方+收费全链路时间线 |
| 收入拆分 |
DashboardStats 扩展 |
挂号/处方/住院三项分开展示 |
| AI 分诊 |
/api/ai/triage |
症状 → 推荐科室/号别/医生 |
| AI 审方 |
/api/ai/check-prescription |
相互作用/剂量/重复用药审核 |
| 语音输入 |
全局空格键 |
WebM → 阿里云 Paraformer 识别 |
八、踩坑记录
SQLAlchemy 子查询陷阱:用 .subquery() 包裹 JOIN 后的查询做聚合会导致行膨胀翻倍。正确做法是直接在目标表上聚合,只在需要时才 JOIN。
1 2 3 4 5 6 7 8
| sum_q = select(...).join(Patient, ...).subquery() result = select(func.sum(...)).select_from(sum_q)
result = select(func.sum(BillingRecord.paid_amount)) if patient_name: result = result.join(Patient, ...).where(...)
|
TestClient 与异步 DB:FastAPI 的 TestClient 无法直接测试异步 SQLAlchemy 端点(MissingGreenlet)。推荐用 httpx.AsyncClient 做集成测试,或直接用 sessionmaker 验证 SQL。
九、启动命令
1 2 3 4 5 6
| cd his-system uv sync docker-compose up -d python seed.py python main.py cd his-frontend && npm run dev
|
| 角色 |
用户名 |
密码 |
| 管理员 |
admin |
Admin@123 |
| 医生 |
doctor1 |
Doctor@123 |
| 收费员 |
cashier1 |
Cashier@123 |
| 药师 |
pharmacist1 |
Pharma@123 |
| 患者 |
自助注册 |
手机号+密码 |
十、后续方向
- WebSocket 实时推送处方至药房发药窗口
- Redis 缓存层 加速高频查询
- Docker 容器化部署 一键上线
- 微信小程序 患者移动端
- 医保接口 实时结算
本项目从零搭建了一套功能完整的 HIS 系统,涵盖门诊→住院→药品→收费→AI 辅助→患者自助的全链路。代码开源于 GitHub,欢迎 Star 和 PR。
🤖 本文由 Claude Code 辅助撰写,项目由南昌师范学院团队开发。