
简介这是一套面向高校计算机专业学生与Python初学者、用于毕业设计或课程设计场景的医院体检挂号系统完整源码采用前后台分离的管理模式前台支持用户注册、个人空间与挂号体检记录查询后台面向医院管理人员完成预约调度与数据审核并融入全民体检趋势预测分析算法帮助理解数据分析在医疗场景中的落地思路。资源包共268个文件约8.77MB以34个py源码、31个js脚本、23个css样式、10个html页面及75个gif素材为主另含sql建库脚本、csv数据、md说明与docx文档覆盖前端页面、后端逻辑与数据库结构。开发环境为Python 3.6.8、MySQL 5.7、Navicat11与PyCharm已有86人学习。读者可据此掌握完整项目结构、数据库设计与预测算法实现并借助部署说明文档快速完成环境配置与运行调试。1. 体检挂号系统这套源码拿到手先别急着跑体检挂号这件事看着比门诊挂号简单真做起来坑一点不少体检套餐有组合项、有加项、有职业禁忌号源按天按时间段放还要处理退号、改期、报告回传。很多同学做毕业设计选题写的是「基于 Python 的某医院体检挂号系统」真正卡住的地方不是业务而是拿到一份源码压缩包之后不知道从哪下手——环境跑不起来、数据库连不上、登录进去一片空白。这篇笔记就按一线做法把这类 Python 体检挂号系统从环境搭建、数据库设计、核心挂号逻辑到部署排错完整走一遍适合正在做计算机毕业设计、需要一套能跑通能答辩的 Python 源码的同学也适合想拿它当练手项目的新手。整套方案用 Flask 或 Django 都能落地下面以最常见的 Flask MySQL 组合来讲前端用模板渲染不引入太重的前端框架保证你在本地和服务器上都能复现。2. 先搞清楚这套体检挂号系统到底由哪些模块组成2.1 体检挂号跟普通门诊挂号的区别在哪普通门诊挂号的核心是「医生 时间段 号源」体检挂号的核心是「体检套餐 日期 时段 名额」。这个差别决定了数据库表结构完全不一样。门诊挂号你只要一张 doctor 表加一张 schedule 表就能撑起来体检挂号至少要拆成套餐表、套餐项目关联表、体检日期排期表、预约订单表、报告表五张核心表。很多网上的 Python 挂号系统源码其实是门诊挂号改个名字你打开一看发现没有套餐概念那就是套壳的答辩时老师一问「体检加项怎么处理」就露馅。体检业务还有几个门诊没有的特性一是套餐可组合基础套餐加自选加项二是同一时段有名额上限比如 8:00-8:30 只放 20 个号三是体检前有注意事项比如空腹、憋尿这些要跟着套餐走四是报告是异步出的预约完成不代表业务结束。你在读源码的时候先确认这几个点有没有对应的表和字段没有的话后面自己补别指望一份现成源码什么都给你写好。2.2 一份能跑的源码通常包含哪些目录拿到压缩包解压后正常的 Flask 项目结构大概是这样你可以对照检查health_checkup/ ├── app/ │ ├── __init__.py # 应用工厂注册蓝图和扩展 │ ├── models.py # 所有 ORM 模型核心表都在这 │ ├── views/ │ │ ├── auth.py # 登录注册、权限校验 │ │ ├── package.py # 套餐浏览、详情 │ │ ├── booking.py # 预约下单、退号改期 │ │ └── admin.py # 后台管理排期和报告 │ ├── templates/ # Jinja2 模板 │ └── static/ # css/js/图片 ├── config.py # 数据库、密钥等配置 ├── requirements.txt # 依赖清单 ├── manage.py # 启动和数据库迁移入口 └── init_data.sql # 初始化数据套餐和排期种子如果解压出来是app.py一个文件几百行全塞一起那也能跑但改起来痛苦。遇到这种结构我的建议是先别重构先让它跑起来跑通之后再按蓝图拆。毕业设计答辩看的是功能完整和逻辑清晰不是代码多优雅但你至少要知道每个文件负责什么被问到能说清楚。2.3 技术选型为什么是 Flask 而不是 Django这类系统用 Flask 和 Django 都能做选 Flask 的理由是轻、上手快、代码量少适合毕业设计这种功能边界清晰的项目。Django 自带 admin 和 ORM 迁移后台管理几乎白送但学习曲线陡配置项多新手容易在 settings 里迷路。如果你时间紧、只想快点跑通Flask SQLAlchemy Flask-Login 这套组合是最稳的。数据库选 MySQL 8.0别用 SQLite 交差。SQLite 本地跑没问题但答辩时老师常问「并发预约怎么保证不超卖」SQLite 的写锁机制你解释起来反而麻烦MySQL 的行锁和事务隔离级别是标准答案。缓存和名额扣减可以用 Redis但毕业设计阶段不是必须用数据库行锁就能满足别为了炫技引入一堆中间件最后自己调不通。3. 把环境搭起来并让系统在本地跑通3.1 Python 环境与依赖安装的具体命令先确认 Python 版本这类源码大多要求 3.8 以上3.10 和 3.11 一般也没问题但 3.12 有时会因为某些库没跟上而出问题稳妥起见用 3.10。# 查看当前版本 python --version # 建虚拟环境别装到全局否则依赖冲突很难查 python -m venv venv # 激活Windows venv\Scripts\activate # 激活macOS / Linux source venv/bin/activate # 安装依赖建议加国内镜像加速 pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simplerequirements.txt里通常会有 Flask、Flask-SQLAlchemy、Flask-Login、PyMySQL、WTForms 这几个。如果安装报错八成是某个包版本和 Python 版本不匹配把报错的那一行单独pip install 包名版本号试。虚拟环境这一步别省我见过太多人全局装了一堆包最后ImportError查半天血泪经验。3.2 数据库建库和初始化数据MySQL 里先建库字符集用 utf8mb4否则中文套餐名会乱码CREATE DATABASE health_checkup DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER hc_userlocalhost IDENTIFIED BY your_password; GRANT ALL PRIVILEGES ON health_checkup.* TO hc_userlocalhost; FLUSH PRIVILEGES;然后在config.py里改连接串格式是mysqlpymysql://用户:密码主机:端口/库名。改完执行初始化# 建表 python manage.py db upgrade # 或者源码用的是 create_all python manage.py init_db # 导入种子数据套餐和排期 mysql -u hc_user -p health_checkup init_data.sqlinit_data.sql里一般有管理员账号、几个体检套餐、未来几天的排期。如果导入后登录提示密码错误多半是密码字段存的是明文而代码里做了哈希校验或者反过来。打开models.py看 User 模型有没有set_password方法有的话种子数据里的密码得是哈希值最简单的办法是注册一个新账号把它的密码哈希复制到管理员记录上。3.3 启动服务并验证核心链路# 开发模式启动带热重载 export FLASK_APPmanage.py export FLASK_ENVdevelopment flask run --host0.0.0.0 --port5000浏览器打开http://127.0.0.1:5000按这条链路验证注册登录 → 浏览套餐 → 选日期时段 → 提交预约 → 我的预约里能看到 → 后台能看到这条订单。这条链路走通说明系统主干没问题。哪一步断了就看那一步对应的视图函数和模板Flask 的报错页面会给出具体行号比 Django 的报错还直观。提示如果页面样式全丢检查static目录路径和模板里url_for(static, ...)的写法Flask 对静态文件路径比较敏感。4. 体检挂号的核心逻辑排期、名额与并发扣减4.1 排期表怎么设计才够用排期表是整个系统的核心设计不好后面全是坑。我一般会这么建字段类型说明idint主键package_idint关联套餐check_datedate体检日期time_slotvarchar(20)时段如 08:00-08:30capacityint该时段总名额bookedint已预约数statustinyint1 开放 0 关闭关键点是capacity和booked分开存不要用「剩余名额」一个字段因为退号时要加回去分开存逻辑更清楚。time_slot用字符串而不是时间类型是因为体检时段是固定区间用字符串展示和比较都方便。索引建在(package_id, check_date)上查询某套餐某天的排期走这个索引。4.2 预约下单的完整代码与事务处理预约的核心是「查名额 → 扣名额 → 写订单」三步必须在一个事务里否则并发下会超卖。用 SQLAlchemy 的写法from flask import current_app from app.models import db, Schedule, Order from sqlalchemy.exc import IntegrityError def create_booking(user_id, schedule_id): try: # with_for_update 加行锁锁住这条排期记录 schedule Schedule.query.with_for_update().get(schedule_id) if not schedule or schedule.status ! 1: return False, 该时段未开放 # 判断是否还有名额 if schedule.booked schedule.capacity: return False, 该时段名额已满 # 同一用户同一套餐同一天不能重复预约 exist Order.query.filter_by( user_iduser_id, package_idschedule.package_id, check_dateschedule.check_date, status1 ).first() if exist: return False, 您已预约该套餐 # 扣名额 schedule.booked 1 # 写订单 order Order( user_iduser_id, schedule_idschedule_id, package_idschedule.package_id, check_dateschedule.check_date, time_slotschedule.time_slot, status1 ) db.session.add(order) db.session.commit() return True, order.id except IntegrityError: db.session.rollback() return False, 预约冲突请重试 except Exception as e: db.session.rollback() current_app.logger.error(fbooking error: {e}) return False, 系统繁忙with_for_update()是关键它会在数据库层面对这条排期记录加排他锁第二个并发请求会阻塞到第一个事务提交从而保证booked不会算错。参数上status1表示有效订单退号时改成 0 并把booked减一。IntegrityError捕获的是唯一约束冲突如果你在订单表上加了(user_id, package_id, check_date)的唯一索引重复提交会走到这里比先查后插更可靠。4.3 退号与改期怎么处理名额退号逻辑看着简单实际有两个坑。一是退号后名额要加回去但加回去的时机要在订单状态改成功之后顺序反了会出现名额加了订单没退的情况。二是改期本质是「退旧号 约新号」必须放在同一个事务里否则可能出现旧号退了新号没约上的尴尬。def cancel_booking(user_id, order_id): order Order.query.filter_by(idorder_id, user_iduser_id, status1).first() if not order: return False, 订单不存在或已取消 schedule Schedule.query.with_for_update().get(order.schedule_id) order.status 0 if schedule and schedule.booked 0: schedule.booked - 1 db.session.commit() return True, 退号成功改期就在这个基础上先调 cancel 再调 create两个操作包在一个外层事务里用db.session.begin_nested()做保存点任何一步失败整体回滚。这块是答辩高频提问点把事务边界讲清楚分数就稳了。5. 后台管理、报告回传与权限控制5.1 管理员排期和套餐维护后台是给医院工作人员用的核心功能是放号、改套餐、看订单。放号就是往 schedule 表插记录通常按周批量生成。写一个批量生成的接口from datetime import date, timedelta def batch_create_schedule(package_id, start_date, days7, slotsNone): slots slots or [08:00-08:30, 08:30-09:00, 09:00-09:30] created 0 for i in range(days): d start_date timedelta(daysi) for s in slots: # 已存在就跳过避免重复放号 if Schedule.query.filter_by(package_idpackage_id, check_dated, time_slots).first(): continue db.session.add(Schedule( package_idpackage_id, check_dated, time_slots, capacity20, booked0, status1 )) created 1 db.session.commit() return createdcapacity20是每个时段默认名额实际按医院体检中心吞吐量调一般一个时段 15 到 30 人。days7是放一周的号医院通常提前一周放号。这个函数要加管理员权限校验普通用户不能调。5.2 报告回传与状态流转体检报告是异步的预约订单的状态要能流转已预约 → 已体检 → 报告已出。报告表单独建存 PDF 路径或文本结果。后台提供一个上传入口上传后把订单状态改成「报告已出」用户端就能下载。这里注意文件上传要做类型和大小校验只允许 pdf大小限制 10M存到非 web 根目录下通过一个带权限校验的下载接口读取别直接把文件路径暴露出去。5.3 权限控制别只做前端隐藏新手最容易犯的错是只在模板里{% if user.is_admin %}隐藏按钮后端接口不校验。别人直接构造请求就能调管理员接口。正确做法是写一个装饰器from functools import wraps from flask import abort from flask_login import current_user def admin_required(f): wraps(f) def decorated(*args, **kwargs): if not current_user.is_authenticated or not current_user.is_admin: abort(403) return f(*args, **kwargs) return decorated所有/admin/开头的路由都挂上这个装饰器。用户查自己的订单也要校验order.user_id current_user.id不能只靠 URL 里的 order_id否则改个数字就能看别人的体检信息这是隐私事故。6. 部署上线与常见问题排查6.1 从开发模式切到生产模式开发用的flask run不能上生产性能和稳定性都不行。生产用 Gunicorn 加 Nginx# 安装 pip install gunicorn # 启动4 个 worker绑定本地端口 gunicorn -w 4 -b 127.0.0.1:8000 app:create_app() # 后台运行并记日志 nohup gunicorn -w 4 -b 127.0.0.1:8000 app:create_app() gunicorn.log 21 Nginx 反代到 8000 端口顺便处理静态文件。-w 4是 worker 数一般设成 CPU 核数乘 2 加 1小服务器 2 到 4 就够。注意生产环境要把DEBUG关掉SECRET_KEY换成随机长字符串别用源码里默认的否则 session 能被伪造。6.2 常见问题排查清单现象启动报ModuleNotFoundError: No module named app。原因工作目录不对或者FLASK_APP没设对。 解决确认在项目根目录执行export FLASK_APPmanage.py或app:create_app()路径要对得上。现象预约提交后名额没变或者超卖了。原因没用行锁或者事务没提交。 解决确认with_for_update()在事务内db.session.commit()被调到。用两个终端同时压测验证。现象中文套餐名显示成问号。原因数据库或连接字符集不是 utf8mb4。 解决建库时指定 utf8mb4连接串加?charsetutf8mb4模板文件存 UTF-8。现象登录后马上又跳回登录页。原因SECRET_KEY每次重启变化session 失效。 解决把SECRET_KEY写死在配置里用固定随机串。现象上传报告报 413。原因Nginx 默认请求体限制 1M。 解决Nginx 配置里加client_max_body_size 10m;并 reload。6.3 答辩前该重点准备什么老师最爱问三件事并发预约怎么防超卖、权限怎么控制、数据表怎么设计。把第 4 章的行锁逻辑、第 5 章的装饰器、第 2 章的表结构讲清楚基本就稳了。另外准备一个演示账号和一个管理员账号现场走一遍预约到报告下载的完整流程比 PPT 讲十页都管用。源码里如果有没做完的功能别硬撑如实说「这块预留了接口后续可扩展」比被问穿强。7. 把这套源码改成你自己的东西一份现成的 Python 体检挂号系统源码直接交上去风险很大查重和答辩都可能出问题。真正聪明的做法是拿它当骨架改出你自己的东西。我一般会从三个方向动手一是换业务细节把套餐改成你学校附近某体检中心的真实项目组合加项逻辑自己重写二是加一个源码里没有的功能比如体检报告的关键指标趋势图用 ECharts 画或者加一个短信提醒的模拟接口三是把前端模板重做一遍换个配色和布局哪怕只是改 CSS视觉上就是两个项目。改的时候有个习惯我一直保持每改一个功能就 git commit 一次写清楚改了什么。答辩前如果改崩了能回滚到上一个能跑的版本这就是后悔药。数据库改动一定写迁移脚本别手动改表否则换台机器部署就复现不出来。最后提醒一句源码里的默认密码、密钥、测试数据上线前全部换掉这是基本的安全习惯也是答辩时能加分的细节。希望帮到你。本文还有配套的精品资源点击获取