Skip to content

rainsdm/HospitalInformationSystem

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

37 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

医院信息系统(HIS)

面向患者的医院门户网站,当前服务于眼科门诊。


系统架构

┌──────────────────┐     HTTPS/fetch      ┌─────────────────────┐    MyBatis    ┌───────────┐
│   Vue 3 SPA      │ ──────────────────▶  │   Spring Boot API   │ ──────────▶  │   MySQL   │
│  (Vite :5173)    │                      │    (HTTPS :8080)     │              │  his_db   │
└──────────────────┘                      └─────────────────────┘              └───────────┘
       │                                              │                        (port 3306)
       │ Vite 代理 /auth /patients                   │ 依赖
       │ /appointments → :8080            ┌──────────┴──────────┐
       │                                  │  UidProvider (1.3)  │
       │ Mock 模式可用:                   └─────────────────────┘
       │ VITE_DATA_SOURCE=mock              发布至本地 Maven 仓库
       │ 完全绕过后端                     (G:/CustomMavenRepo)

Mock 模式npm run dev:mock 使用内存 Mock 数据,完全脱离后端。启动后自动注入硬编码用户(赵六,uid: P7V455QZ2EC6S772621)和生成的 JWT。


技术栈

层级 技术 版本
前端框架 Vue 3 + Composition API 3.5
前端语言 TypeScript 6.0
构建工具 Vite 8.0
UI 组件库 Naive UI 2.44
CSS 框架 Tailwind CSS 4.3
状态管理 Pinia 3.0
路由 Vue Router 5.1
后端框架 Spring Boot 4.0.5
后端语言 Java 25
ORM MyBatis(纯注解,无 XML) 4.0
认证 自研 JWT Filter + @CurrentUserUid jose4j 0.9.6
密码校验 Passay 1.6
工具库 Lombok、Hutool、dayjs
数据库 MySQL 8.0+

快速开始

前置条件

  • JDK 25、Maven 3.9+、Node.js 18+、MySQL 8.0+
  • 在系统 hosts 文件中添加 127.0.0.1 dev.test
  • 复制 Server/src/main/resources/application.yaml.templateapplication.yaml,填入数据库用户名密码

1. 初始化数据库

mysql -u root -p < Db/CreateDb.sql      # 创建数据库与表
mysql -u root -p his_db < Db/DbViews.sql # 创建视图
mysql -u root -p his_db < Db/DataGen.sql # 导入种子数据(5名医生、5个诊室)

注意Db/DataGen.sql 当前使用旧版表结构(consulting_room_infodoctor_profile),在新版 CreateDb.sqlconsulting_roomsdoctors)下可能报错,需要手动适配或使用新版种子数据。

2. 构建共享库

cd UidProvider && mvn clean install    # UID 生成器
cd mqc_provider && mvn clean install   # 医师执业证号生成器(如果存在)

3. 启动后端

cd Server
mvn clean package                      # 构建 Fat JAR(版本 0.1.6)
mvn spring-boot:run                    # 开发服务器(热重载,端口 8080)

后端测试:

mvn test                              # 运行全部测试
mvn test -Dtest=LoginControllerTest   # 运行单个测试类

4. 启动前端

cd Web/frontend
npm install
npm run dev                  # 连接真实后端(HTTPS,端口 5173)
#
npm run dev:mock             # Mock 模式,无需后端即可完整开发

浏览器访问 https://dev.test:5173


构建与运行命令速查

命令 说明
cd Server && mvn clean package 构建后端 fat JAR
cd Server && mvn spring-boot:run 启动后端开发服务器(热重载)
cd Server && mvn test 运行全部后端测试
cd Server && mvn test -Dtest=ClassName 运行指定测试类
cd Web/frontend && npm run dev 启动前端(连接真实后端)
cd Web/frontend && npm run dev:mock 启动前端(Mock 数据,无需后端)
cd Web/frontend && npm run build 生产构建(类型检查 + 代码压缩)
cd Web/frontend && npm run build:local 开发构建(不压缩,开启 sourcemap)
cd Web/frontend && npm run preview 预览生产构建
cd UidProvider && mvn clean install 构建共享库并发布到本地 Maven 仓库

API 端点

方法 路径 认证 说明
POST /auth/patients 注册患者,自动登录 → JWT
POST /auth/tokens 邮箱 + 密码登录 → JWT
GET /patients/profile JWT 患者档案(姓名、脱敏身份证、邮箱)
PUT /patients/profile JWT 更新患者档案(邮箱)
GET /patients/doctors 可挂号医师列表(公开)
GET /patients/appointments?type=active|history JWT 预约列表
GET /patients/appointments/:ticketNo JWT 预约详情
POST /patients/appointments JWT 创建预约(锁定号源)

标注「JWT」的端点需在请求头携带 Authorization: Bearer <token>。部分端点(挂号、预约)后端尚未完整实现,当前在前端 Mock 模式下可使用。

请求/响应示例

登录:

POST /auth/tokens
{ "email": "zhaoliu@test.cn", "password": "Test123456,." }

→ {
  "code": 200,
  "message": "登录成功",
  "data": {
    "uid": "P7V455QZ2EC6S772621",
    "role": 0,
    "token": "eyJhbGciOiJIUzI1NiJ9...",
    "expiresAt": "2026-05-19T14:30:00",
    "name": "赵六"
  }
}

注册:

POST /auth/patients
{ "name": "张三", "identityCard": "110101199001011234",
  "email": "zhangsan@test.cn", "password": "Test123456,.", "role": 0 }

→ {
  "code": 200,
  "message": "注册成功",
  "data": {
    "uid": "PAAAAAAAABBBBCCCC",
    "role": 0,
    "token": "eyJhbGciOiJIUzI1NiJ9...",
    "expiresAt": "2026-05-19T15:00:00",
    "name": "张三"
  }
}

患者档案:

GET /patients/profile
Authorization: Bearer <token>

→ {
  "code": 200,
  "data": {
    "name": "赵六",
    "maskedIdentityCard": "450802********7217",
    "email": "zhaoliu@test.cn"
  }
}

项目结构

HospitalInformationSystem/
├── Server/                         ← [子模块] Spring Boot 后端
│   └── src/main/java/cn/snowdreamsblog/server/
│       ├── ServerApplication.java
│       ├── auth/                   ← 认证模块(注册 + 登录)
│       │   ├── controller/AuthController.java
│       │   ├── service/AuthService.java
│       │   ├── mapper/AuthMapper.java
│       │   └── dto/                ← AuthCredentialData、RegisterRequest
│       ├── common/
│       │   ├── annotation/CurrentUserUid.java      ← 自定义参数注解
│       │   ├── resolver/CurrentUserUidResolver.java ← 参数解析器(从 Request 提取 uid)
│       │   ├── exception/          ← ApiException + GlobalExceptionHandler
│       │   └── result/             ← CommonResult<T> + ResultCode
│       ├── config/WebMvcConfig.java ← 统一 CORS + 参数解析器注册
│       ├── entity/                 ← Users、Patients、RegistrationRecord
│       ├── filter/JwtAuthenticationFilter.java ← JWT 认证过滤器(手动白名单)
│       └── utils/jwt/              ← JWT 生成/解析(jose4j)+ 密码校验(Passay)
│
├── Web/                            ← [子模块] 前端项目
│   └── frontend/
│       ├── src/
│       │   ├── main.ts             ← 入口:Pinia + Router beforeEach 守卫
│       │   ├── App.vue             ← 根组件(NConfigProvider + 暖棕色主题)
│       │   ├── api/                ← fetch 封装(JWT 注入、401 登出、Mock 切换)
│       │   │   ├── index.ts        ←   request<T>() 核心请求函数
│       │   │   ├── auth.ts         ←   注册/登录
│       │   │   ├── patient.ts      ←   档案/预约列表/预约详情
│       │   │   ├── appointment.ts  ←   医生列表/创建预约
│       │   │   └── _mock/          ←   Mock 实现(mock 模式下使用)
│       │   ├── types/api.ts        ← TypeScript 接口定义
│       │   ├── router/index.ts     ← 7 条嵌套路由 + beforeEnter 预约守卫
│       │   ├── stores/             ← Pinia 状态管理
│       │   │   ├── user.ts         ←   认证状态 + 档案 + Mock 会话注入
│       │   │   └── appointment.ts  ←   预约流程 selectedDoctor
│       │   ├── views/              ← 7 个页面组件
│       │   ├── components/         ← Header/Navbar/Title/Footer
│       │   └── utils/              ← isTokenExpired、naiveDiscreteApi
│       ├── env/                    ← 环境变量(.development / .mock / .production)
│       └── vite.config.ts          ← HTTPS + 代理 + 自动导入 + Tailwind v4
│
├── Db/                             ← [子模块] 数据库脚本
│   ├── CreateDb.sql                ← 建库建表(新版)
│   ├── DbViews.sql                 ← 视图(需适配新版表结构)
│   └── DataGen.sql                 ← 种子数据(使用旧版表结构)
│
├── UidProvider/                    ← UID 生成共享库
├── mqc_provider/                   ← 医师资格证编码生成库
└── Db_data/                        ← CSV 格式种子数据

后端架构详解

包结构(v2 重构后)

基础包:cn.snowdreamsblog.server

auth/                              ← 认证模块(注册 + 登录合一)
  controller/AuthController.java   ← POST /auth/patients、POST /auth/tokens
  service/AuthService.java         ← 合并注册与登录逻辑
  mapper/AuthMapper.java           ← users + patients 表 CRUD(纯注解 SQL)
  dto/                             ← AuthCredentialData、RegisterRequest

common/
  annotation/CurrentUserUid.java   ← @CurrentUserUid 自定义参数注解
  resolver/CurrentUserUidResolver  ← HandlerMethodArgumentResolver,从 Request 属性提取 uid
  exception/                       ← ApiException(code + message)、GlobalExceptionHandler
  result/                          ← CommonResult<T> 统一响应 + ResultCode 状态码枚举

config/WebMvcConfig.java           ← 统一 CORS 配置 + CurrentUserUidResolver 注册

entity/
  Users.java                       ← 核心用户表(id BIGINT 内部主键、uid 业务 ID、email、password、role)
  Patients.java                    ← 患者扩展表(user_id → users.id,CASCADE 删除)
  RegistrationRecord.java          ← 挂号记录表(含 uid、ticket_no、快照字段)

filter/JwtAuthenticationFilter     ← 手动 JWT 过滤器(实现 Filter 接口,白名单放行 /auth/** 和 /patients/doctors)

utils/jwt/                         ← JWT 签发/校验(jose4j HMAC-SHA256)、JwtPayloadSettings
utils/pwd/                         ← Passay 密码强度规则(8-16 字符,大写+小写+数字+特殊字符)

核心设计模式

1. 统一响应

所有 API 返回 CommonResult<T>,结构为 {code, message, data}GlobalExceptionHandler 统一捕获 ApiException 和通用 Exception

2. JWT 认证流程(非 Spring Security)

项目已从 Spring Security 重构为手动 Filter 方案:

  • JwtAuthenticationFilter 实现 Filter 接口,在 doFilter 中手动校验白名单(/auth/**/error/patients/doctors 直接放行)
  • 有效 Token 的 uid 和 role 通过 request.setAttribute() 存入 Request
  • Controller 通过 @CurrentUserUid String uid 注解(CurrentUserUidResolver 自动注入)获取当前用户
  • JWT 有效期 3600 秒(60 分钟),通过 jose4j HMAC-SHA256 签名

3. UID 生成策略

内部 UidGenerator 库生成 32 字符唯一 ID:先插入临时 UID 获取自增主键序号 → 用序号 + 时间戳 + 密钥生成最终 UID → 回写。

4. 按业务域分包

认证模块(auth/)内聚 Controller、Service、Mapper、DTO,避免按技术层横向拆分。

5. MyBatis 纯注解 SQL

无 XML Mapper 文件,所有 SQL 通过 @Select@Insert@Update 注解直接写在 Mapper 接口中。

6. DTO 使用 POJO

AuthCredentialDataRegisterRequest 使用 Lombok @Data 注解的普通 POJO(非 Java record),请求体通过 @Validated 校验。


前端架构详解

环境模式

模式 VITE_DATA_SOURCE 行为
dev(默认) api Vite 代理 /auth/patients 到后端 :8080
mock mock 使用内存 Mock 数据,__USE_MOCK_API__ 编译时常量切换
production api 直接请求 VITE_API_BASE_URL(无代理)

API 层设计

request<T>() 核心函数:

  • 自动注入 Authorization: Bearer <token> 请求头
  • 解析 CommonResult<T> → 自动解包 data 字段
  • 401 → 自动登出;403 → 区分代理层/应用层错误
  • 编译时常量 __USE_MOCK_API__ 控制 mock/api 切换(零运行时开销)

路由设计

嵌套路由:根路径 / 使用 home/index.vue 作为布局外壳(Header + <RouterView/> + Footer),子路由渲染在 <RouterView/> 中。

路径 组件 需登录 说明
/home MainPage.vue 仪表盘:公告、快捷链接、排班预览
/register Register.vue 建档(含密码强度指示器)
/login Login.vue 登录
/appointment Appointment.vue 选择医生 + 号源
/appointment/confirm AppointmentConfirm.vue 确认预约(beforeEnter 守卫)
/appointments/:ticketNo AppointmentDetail.vue 预约详情(以 ticketNo 为参数)
/profile Profile.vue 电子档案

路由守卫:

  • beforeEach:token 过期检查、登录后自动加载档案、requiresAuth 鉴权
  • beforeEnter(confirm):检查 selectedDoctor(Pinia)或 sessionStorage(刷新恢复)

状态管理

  • user Store:token/uid/name 持久化于 localStorage、登录/注册/登出、档案加载与刷新、Mock 会话注入
  • appointment Store:多步骤预约流程的 selectedDoctor 状态、doctorAvatarInitial 计算属性

核心依赖

  • Naive UI:通过 unplugin-vue-components 自动按需导入,unplugin-auto-import 自动导入 Vue API
  • Tailwind CSS v4:通过 @tailwindcss/vite 插件集成,无 tailwind.config 文件
  • dayjs:日期格式化与计算
  • password-validator:前端密码强度实时校验

数据库架构

数据库:his_db,字符集 utf8mb4,引擎 InnoDB。

表结构(v2 重构后)

表名 主键 说明
users id BIGINT 自增 核心用户认证:uid(业务 ID,UNIQUE)、uid_saltemailpasswordrole(0=患者,1=医生,2=管理员)、created_atupdated_at
patients user_id BIGINT 患者档案扩展:关联 users.id(CASCADE 删除)、namegenderidentity_card(UNIQUE)、birthdaylast_login_at
doctors user_id BIGINT 医生档案扩展:关联 users.id(CASCADE 删除)、doctor_code(执业证号,UNIQUE)、nametitleconsulting_room_idreception_limitoutcall_start_timeoutcall_end_timeentry_date
consulting_rooms id BIGINT 自增 诊室信息:uid(业务 ID)、code(字母编号,如 "A")、nameposition
doctor_daily_sequences (doctor_id, visit_date) 联合主键 医生每日排队序号计数器:current_seq
registration_records id BIGINT 自增 挂号记录:uid(业务 ID)、ticket_no(业务单号)、patient_iddoctor_idvisit_datesequence_num、快照字段(doctor/room name/title/position)、fee_amountvisit_status(0-3)、arrive_time

关键设计变更

  • 内部 ID 与业务 ID 分离:所有表使用 BIGINT id 作为内部主键,uid(VARCHAR(32))作为对外业务标识
  • users 统一用户表:替代旧 auth 表,通过 role 区分患者/医生/管理员
  • 快照冗余:挂号记录保留预约时刻的医生姓名、职称、诊室名称等快照,防止后续关联表变更导致历史数据失真
  • 号源计数器doctor_daily_sequences 按(医生, 日期)维度独立计数,替代旧的单行 cumulative_sequence_number_controller

视图

视图名 说明
slot 医师 + 诊室联查(⚠ 引用旧版表名 doctor_profileconsulting_room_info,需适配)

实体关系

users.id ──(CASCADE)──▶ patients.user_id
users.id ──(CASCADE)──▶ doctors.user_id
users.id ─────────────▶ registration_records.patient_id
users.id ─────────────▶ registration_records.doctor_id
consulting_rooms.id ──▶ doctors.consulting_room_id(SET NULL)
consulting_rooms.id ──▶ registration_records.room_id_snapshot
doctors.user_id ──────▶ doctor_daily_sequences.doctor_id(CASCADE)

种子数据

  • 医生(5名):李伟(A 诊室,08:00-12:00,限 15 号)、王芳(B 诊室,14:00-18:00,限 20 号)、刘强(C 诊室,09:00-17:00,限 25 号)、陈静(D 诊室,08:30-12:30,限 18 号)、赵敏(E 诊室,不限时间,限 30 号)
  • 患者(6名):测试密码均为 Test123456,.,邮箱:zhangsan@test.cnlisi@test.cnwangwu@test.cnzhaoliu@test.cnzhaoqi@test.cnzhaoba@test.cn
  • Db_data/ 目录中另有 CSV 格式种子数据

DataGen.sql 仍使用旧版表结构,暂未适配新版 users/doctors/consulting_rooms 表。


子模块操作

三个核心目录均为 Git 子模块(Server/Web/Db/)。对子模块的更改必须先在子模块内提交,然后更新父仓库指向新的提交。

git submodule update --init --recursive   # 拉取所有子模块
cd Server && git pull origin main         # 更新单个子模块

开发状态

功能 状态
患者注册/建档 ✅ 已完成
患者登录 ✅ 已完成
患者档案查询 + 更新 ✅ 已完成
医师列表查询 ✅ 已完成
预约挂号(创建预约、锁定号源) 🔄 后端部分完成
预约列表 + 预约详情 🔄 后端部分完成
Mock 开发模式 ✅ 已完成
门诊缴费 ⏳ 开发中
报告查询 ⏳ 开发中
密码加密哈希 ❌ 明文存储(已列入 TODO)
slot 视图适配新版表 ⚠ 仍引用旧版表名
DataGen.sql 适配新版表 ⚠ 仍使用旧版表结构
电子档案页 🔄 使用 Mock 数据,未完全接入后端
首页排班速览 🔄 硬编码数据

注意事项

  • 密码哈希为 TODO:注册服务以明文存储密码,users 表无 salt 字段用于密码加密(uid_salt 仅用于 UID 生成)
  • SSL 当前关闭application.yamlserver.ssl.enabled: false;模板中为 true,生产环境需启用
  • 共享库UidProvider 必须先 mvn clean installG:/CustomMavenRepo,Server 才能编译
  • 非子模块目录passay-demo/totpGenerator/zxcvbn-demo/Prototypes/Gemini对话记录/Web_Gemini/ 为探索/演示项目,不属于核心应用
  • Server 使用 distributionManagement 发布到 file://G:/CustomMavenRepo/HIS-System

重构变化记录

本节记录了项目从 v1 到 v2 的主要架构变更,供了解演进历史。

数据库:从 VARCHAR 主键到 BIGINT 内部 ID 体系

维度 v1(旧) v2(新)
用户表 auth(uid VARCHAR 主键) users(id BIGINT 自增主键,uid 为 UNIQUE 业务列)
患者表 patients(uid VARCHAR 主键,外键→auth.uid) patients(user_id BIGINT 主键,外键→users.id)
医生表 doctor_profile(uid 主键,doctorId 业务列) doctors(user_id 主键,doctor_code 业务列,新增 title/entry_date)
诊室表 consulting_room_info(id VARCHAR(9) 主键) consulting_rooms(id BIGINT 自增 + uid VARCHAR(32) 业务列 + code 字母编号)
挂号表 registration_record(orderId 订单号) registration_records(uid 业务 ID + ticket_no 单号 + 快照字段 + fee_amount)
序号器 cumulative_sequence_number_controller(单行全局) doctor_daily_sequences(按医生+日期独立计数)

动机:将数据库内部自增 ID 与对外暴露的业务 UID 彻底解耦,引入快照字段保证历史数据不受关联表变更影响。

后端:Spring Security → 手动 Filter + HandlerMethodArgumentResolver

维度 v1(旧) v2(新)
认证框架 Spring Security + OncePerRequestFilter 手动 Filter 接口实现 + 白名单
获取用户 SecurityContextHolder request.setAttribute("currentUserUid") + @CurrentUserUid 注解
参数注入 手动从 Request Header 提取 Token 并解析 CurrentUserUidResolver 自动注入
CORS 配置 独立 CorsConfig.java 统一 WebMvcConfig.java(CORS + 参数解析器)
异常处理 携带 ResultCode 枚举 简化为 code(int)+ message,401 单独处理

动机:降低 Spring Security 的配置复杂度,减少依赖,使认证流程更透明可控。

后端:认证模块合并

维度 v1(旧) v2(新)
包结构 patient/auth/login/ + patient/auth/register/ 独立分包 auth/ 统一模块
Controller LoginController + RegisterController 两个类 AuthController 单一类
Service LoginService + RegisterService 两个类 AuthService 单一类
响应 DTO LoginResponse + RegisterResponse(Java record) AuthCredentialData(Lombok POJO,含 role 字段)
异常处理 登录抛异常,注册返回 null 统一抛 ApiException

动机:消除重复代码,统一错误处理策略,简化包结构。

后端:实体变更

v1 v2
Auth.java(uid/password/email/salt/role) Users.java(id/uid/uidSalt/email/password/role/createdAt/updatedAt)
UserProfile.java 抽象基类(已移除)
Patients.java 继承 UserProfile,uid 主键 Patients.java 独立 POJO,userId 主键
DoctorProfile.javaConsultingRoomInfo.javaPatientsInsurance.java 暂未迁移至新版 entity

后端:JWT 配置变更

配置项 v1 v2
过期时间 1800 秒(30 分钟) 3600 秒(60 分钟)
SSL 强制启用 application.yaml 默认关闭(模板仍为启用)

前端:路由与 API 变更

维度 v1 v2
预约详情路由 /appointments/:orderId /appointments/:ticketNo(使用 ticketNo 作为标识)
类型系统 旧版接口(缺少 doctor title/roomInfo/visitDate 等) 新版 DoctorInfo(含 title/roomCode/period/remainingSlots/visitDate)
预约类型 appointment store 缺少 doctorAvatarInitial 新增 doctorAvatarInitial 计算属性
路由守卫 预约确认仅检查 Pinia 增加 sessionStorage 兜底(刷新页面后恢复)
新环境变量 VITE_ENABLE_ROUTE_GUARD 控制是否启用全局鉴权守卫
档案 API getProfile() 新增 updateProfile()getAppointments()getAppointmentDetail()

前端:依赖版本升级

依赖 v1 v2
TypeScript ~5.x 6.0.3
Vue Router 4.x 5.1.0
Pinia 2.x 3.0.4
Vite 5.x 8.0.14
Tailwind CSS 3.x 4.3(@tailwindcss/vite 插件)
新增依赖 dayjs、password-validator

构建:新增环境变量

变量名 默认值 说明
VITE_ENABLE_ROUTE_GUARD true 设为 false 可关闭路由鉴权(调试用)
__USE_MOCK_API__ 编译时常量 VITE_DATA_SOURCE 编译时注入,控制 Mock/API 模式

About

医院信息管理系统的父项目。由多个子模块组成。

Resources

Stars

Watchers

Forks

Releases

Packages

Used by

Contributors