医院预约管理系统

发布时间:2026/7/22 2:34:26

医院预约管理系统 医院预约管理系统 — 设计思路与实现笔记本文档记录从需求分析 → 架构设计 → 分层实现 → 边界处理的完整思路。一、需求分析与建模1.1 业务背景某社区医院需要一个预约管理系统管理科室、医生、患者及预约挂号。核心痛点科室与医生多对一关系医生排班信息需直观呈现患者管理身份证唯一标识联系方式一对一独立存储预约挂号需防止超额预约和重复预约数据统计管理者需快速了解每日运营概况1.2 实体关系分析科室 (departments) 1 ── N 医生 (doctors) 医生 (doctors) 1 ── N 预约 (appointments) 患者 (patients) 1 ── N 预约 (appointments) 患者 (patients) 1 ── 1 联系方式 (patient_contacts)1.3 关键设计决策Q: 联系方式为何独立成表而非放在 patients 表中A: 遵循数据库范式联系方式字段手机、微信、邮箱、地址、紧急联系人是可选信息且与患者核心标识姓名、身份证职责不同。独立存储便于按需加载列表页不需要联系方式减少数据传输独立维护联系方式更新不影响患者主体Q: 为何使用 Tortoise ORM 而非 SQLAlchemyA: Tortoise ORM 是 Python 异步生态的原生选择与 FastAPI 的 async/await 无缝配合无需额外线程池处理数据库操作。二、架构设计2.1 分层思想采用经典的8 层架构自上而下单向依赖┌──────────────────────────────────────────────┐ │ ⑦ api/ 路由层 HTTP 入口/出口 │ ← 薄控制器不做业务判断 │ ⑥ services/ 业务逻辑层 规则编排 │ ← 核心所有 if/else 在这里 │ ⑤ repositories/ 数据访问层 ORM 查询封装 │ ← 屏蔽数据库细节 │ ③ models/ ORM模型层 表映射 │ ← 纯声明 ├──────────────────────────────────────────────┤ │ ④ schemas/ Pydantic层 数据校验 │ ← 与路由层配合 │ ② core/ 基础设施层 DB/JWT/异常/依赖 │ ← 横向支撑 │ ① config/ 配置层 .env → Settings │ ← 环境隔离 │ ⑧ utils/ 工具层 响应封装 │ ← 无状态纯函数 └──────────────────────────────────────────────┘2.2 为什么需要 Repository Service 两层模式问题路由直接写 ORM路由层承担太多职责参数校验 业务判断 查询构建 响应组装难以测试和复用路由 → Service → ORMService 既要处理业务规则又要写 SQLService 变臃肿路由 → Service → Repository → ORM✅每层职责单一Service 只管能不能做Repository 只管怎么查具体收益切换数据库时只改 Repository 层Service 可以 mock Repository 做单元测试复杂查询如预约 5 维筛选封装在 Repository 中Service 只需传参2.3 统一响应格式设计{code:200,message:操作成功,data:null}设计考量code用于前端判断成功/失败不依赖 HTTP 状态码因为部分代理可能改写message可直接展示给用户Toast/提示框data承载实际业务数据失败时为null三、各层实现要点3.1 配置层 (config/settings.py).env 文件 → python-dotenv 加载 → os.getenv() 读取 → Settings 类属性 ↓ DB_URL 属性动态拼接连接串为什么用.env而非硬编码开发/测试/生产环境分离.env.dev/.env.prod各自维护敏感信息密码不入 Git3.2 基础设施层 (core/)模块核心实现设计要点database.pyTortoise.init()generate_schemas()开发自动建表生产用 Aerich 迁移security.pypython-jose的jwt.encode/decodeexp字段控制过期HS256 对称加密exceptions.py3 个异常处理器注册到 app将IntegrityError转为用户友好中文提示dependencies.pyHeader(authorization)提取 →verify_token()支持Bearer xxx和纯 token 两种格式3.3 ORM 模型层 (models/hospital.py)classDoctor(models.Model):departmentfields.ForeignKeyField(models.Department,related_namedoctors)设计要点related_name让反向查询更语义化dept.doctors而非dept.doctor_setauto_now_addTrue创建时自动填时间auto_nowTrue更新时自动刷新nullTrue允许字段为空与 MySQLDEFAULT NULL对应3.4 数据访问层 (repositories/)基类设计classBaseRepository:model:Type[Model]# 子类覆写asyncdefpaginate(self,query,page,page_size,order_by):通用分页返回 (items, total)所有 Repository 继承此基类获得get_by_id/create/update/delete/paginate五件套。批量加载避免 N1# ❌ 坏做法循环中逐条查N1 问题fordoctorindoctors:deptawaitDepartment.get(iddoctor.department_id)# 每轮 1 次 SQL# ✅ 好做法先收集 ID再批量查dept_idslist({d.department_idfordindoctors})departmentsawaitDepartment.filter(id__indept_ids).all()dept_map{d.id:d.namefordindepartments}3.5 业务逻辑层 (services/)核心模式三元组返回asyncdefcreate_appointment(self,data:dict)-tuple[bool,str,dict|None]: 返回 (是否成功, 提示消息, 数据或None) 四个校验按序执行任一失败立即返回 ① 患者存在 → 否则 患者不存在 ② 医生在岗 → 否则 该医生当前不在岗 ③ 当天已预约 → 否则 该患者当天已预约过该医生 ④ 号满 → 否则 该时段号已满 ⑤ 全部通过 → 创建 为什么用 fail-fast 而非收集所有错误社区医院场景用户一次只操作一条预约即时反馈比批量错误提示体验更好。3.6 路由层 (api/v1/)路由层只做三件事接收 HTTP 参数Query/Path/Body调用 Service 方法根据返回值组装 JSON 响应router.post(/appointments)asyncdefcreate_appointment(data:AppointmentCreate,...):ok,msg,resultawaitappointment_service.create_appointment(data.model_dump())ifnotok:returnerror(msg)# 业务失败 → 400returnsuccess(dataresult,messagemsg)# 成功 → 200四、边界情况处理清单边界场景处理方式层级科室名称重复新增/编辑时查重编辑时排除自身Service删除有医生的科室先查Doctor表有则返回错误Service删除有待就诊的医生查Appointment(status待就诊)Service身份证重复新增/编辑时查重编辑排除自身Service同一患者同天重复预约同一医生组合条件查重Service预约数超过日限额先统计同时段数≥限额则拒绝Service医生不在岗时预约查医生 status ! “在岗”Service取消/完成非待就诊预约查 status不符则拒绝Service联系方式不存在时查询返回空结构id0前端据此显示创建按钮Service联系方式已存在时再次创建查唯一约束提示请使用编辑Service编辑联系方式时不存在容错处理自动创建Service搜索无结果返回空数组 total0Repository分页超出范围Tortoise ORM 自动返回空数组RepositoryToken 过期/伪造verify_token()返回 None → 401Security数据库唯一键冲突全局异常处理器 → 400 友好提示Exception请求参数格式错误Pydantic 校验 → 422 字段级错误Exception五、代码风格约定注释规范# ── 区块分隔注释用 ── 区分不同逻辑块 ──# ① 有编号的步骤说明# → 缩进表示结果/后果# ✅ 成功路径 ❌ 失败路径命名约定Repository 类{Entity}Repo如DoctorRepoService 类{Entity}Service如DoctorServiceSchema 类{Entity}{Action}如DoctorCreate/DoctorUpdate路由文件实体名复数如doctors.py路由函数{action}_{entity}如list_doctors/create_doctor文件组织每层一个模块目录每个实体一个文件简单的实体如科室把相关逻辑集中在一个文件复杂的实体如患者 联系方式在 Repository 中合并为patient_repo.py类之间用空行分隔六、可扩展性预留扩展点当前实现扩展方向多角色认证固定 admin 账号core/security.py增加角色字段dependencies.py增加require_role()数据库迁移generate_schemas()改用 Aerichaerich init→aerich migrate→aerich upgrade日志系统无utils/增加logger.pycore/middleware.py增加请求日志中间件缓存无repositories/base.py增加 Redis 缓存层API 版本化api/v1/新增api/v2/路由注册时指定 prefix单元测试无tests/目录pytest httpx 异步测试客户端定时任务无增加 APScheduler定时将过期预约状态改为「已过期」七、总结本项目采用8 层分层架构核心思想是关注点分离路由层只管 HTTP 协议请求解析、响应序列化业务逻辑层只管业务规则能不能做、什么条件数据访问层只管数据查询怎么查、查什么模型层只管表结构字段类型、关系映射每层只做一件事每行代码都有注释降低了认知负担和维护成本。所有边界情况在 Service 层显式处理不依赖数据库异常作为业务逻辑控制流。

相关新闻