一个覆盖 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 (自动汇总) │
└─────────────────────────────────────────────────────────────┘

技术实现要点:

  1. 挂号POST /api/registrations → 根据 reg_type 自动匹配挂号费(FEE_MAP),初始状态 payment_status=PENDING
  2. 缴费PATCH /api/registrations/{id}/pay → 状态更新为 PAID + 写入 paid_at 时间戳 + 事务性写入统一收费主表 billing_records
  3. 开方POST /api/prescriptions → 自动查询药品单价(drug.retail_price),计算 total_amount = Σ(unit_price × quantity)
  4. 处方缴费PATCH /api/prescriptions/{id}/pay → 状态更新 + 写入 billing_records(通过 pres.registration.patient_id 获取患者 ID)
  5. 发药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
# 1. 状态层拦截
if reg.payment_status == PaymentStatus.PAID:
raise HTTPException(409, "已收费,不可重复")

# 2. 收费主表层拦截
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 数据查询 自然语言 → 工具匹配 → 执行 → 回复

五、统一收费主表设计(核心重构)

问题背景

旧系统存在两个关键缺陷:

  1. 「今日收入」仅统计挂号费(如 ¥5 的挂号费),处方收费(¥112.50)未被计入
  2. 「收费历史」缺失挂号费记录,患者就诊-收费全链路断裂

解决方案: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 # Qdrant + Redis
python seed.py # 初始化数据库
python main.py # 后端 http://localhost:8000
cd his-frontend && npm run dev # 前端 http://localhost:5173
角色 用户名 密码
管理员 admin Admin@123
医生 doctor1 Doctor@123
收费员 cashier1 Cashier@123
药师 pharmacist1 Pharma@123
患者 自助注册 手机号+密码

十、后续方向

  1. WebSocket 实时推送处方至药房发药窗口
  2. Redis 缓存层 加速高频查询
  3. Docker 容器化部署 一键上线
  4. 微信小程序 患者移动端
  5. 医保接口 实时结算

本项目从零搭建了一套功能完整的 HIS 系统,涵盖门诊→住院→药品→收费→AI 辅助→患者自助的全链路。代码开源于 GitHub,欢迎 Star 和 PR。

🤖 本文由 Claude Code 辅助撰写,项目由南昌师范学院团队开发。