
简介本资源是一套完整的基于Python的医院预约挂号系统实战项目源码与演示视频面向Web全栈初学者及课程设计、毕业设计学习者解决医疗场景下患者在线挂号、医生排班管理、预约信息协同等核心业务需求。压缩包共604个文件含118个Vue组件文件实现响应式前端界面、46个Python后端脚本Flask路由与数据库交互逻辑、69个JPG/PNG图片资源UI素材、63个JS文件前端交互与状态管理、36个pyc编译文件可直接运行验证以及SQL初始化脚本、多套批处理运行脚本如init_sql.bat、run.bat等和1个MP4演示视频整体大小67.54MB。已有207人学习下载提供开箱即用的完整工程结构、MySQL 5.7兼容数据库脚本、PyCharmNavicat开发环境适配方案以及前后端分离部署说明便于快速复现、调试与二次开发。 说实话这几年前后端分离的实战项目特别多但一提到“医院预约挂号”很多人的第一反应还是停留在那种老旧的单体架构里。直到我动手做了一个基于Flask和Vue的医院预约挂号系统才对这类业务系统有了全新的理解。它不只是一个简单的CRUD演示而是一整套完整的预约流程、用户权限控制、医生排班管理、消息通知机制甚至还要考虑并发预约的冲突处理。这个标题里的项目核心就是帮你在一个真实场景中把Python后端和Vue前端的知识全部串起来。这个系统能做什么简单来说它模拟了从患者注册登录、浏览科室医生、选择号源时间、提交预约到后台管理员维护科室/医生/排班再到医生查看预约记录、变更排班状态的完整闭环。对于正在准备毕业设计、个人作品集或者想系统学习FlaskVue前后端分离开发的同学来说这是一个非常合适的实战练手项目可以让你在写代码的同时也搞清楚就诊流程里那些“为什么”。1. 项目整体设计与思路拆解1.1 为什么选择Flask Vue这个组合很多人在技术选型上会纠结Spring Boot、Django、甚至什么都不用纯PHP一把梭。我自己实际做下来FlaskVue的好处在于两个字轻和灵活。Flask本身是一个微框架它的核心非常小没有Django那样默认绑定ORM对象关系映射、Admin后台、模板引擎等一堆东西。这反而给了你极大的掌控力——你需要什么就装什么。比如连接MySQL我用了flask-sqlalchemy比Django默认的ORM在学习成本和理解难度上都要低一截尤其是调试的时候你很容易看到底层发生了什么。对于预约挂号这种业务本质上就是处理用户请求、操作数据库、返回JSON数据Flask应付起来绰绰有余。Vue这边我用的是Vue 2 Element UI的组合。Element UI的组件库非常成熟表单、表格、日期选择器、弹窗提示开箱即用。对于预约挂号系统的后台管理和用户端页面来说几乎不需要写复杂的CSS层叠样式表就可以拼出一个干净整洁的界面。Vue的双向数据绑定也让页面状态的管理变得非常直观比如用户选择了一个科室医生列表就会自动刷新这种联动效果如果用原生DOM文档对象模型操作来实现代码量和心智负担会重很多。1.2 系统角色和核心流程设计这个系统我把它分成了三种角色分别是患者、医生和系统管理员。理解这三种角色是理解整个系统的钥匙。设计角色时不能简单粗暴地给每个角色建一张表而是用一个用户表加一个角色字段来控制权限配合JWTJSON Web Token里存储的角色信息前端再根据角色动态渲染路由和菜单。核心流程我简化成了一条主线而所有模块的开发都是围绕这条路展开的患者注册登录完善个人信息如姓名、身份证号、联系方式。患者浏览科室列表选择某个科室后查看该科室下的医生排班。患者选择一个具体的日期和号源时段点击确认预约。系统检查该时段剩余号源若未被预约则生成一条预约记录。患者可以在“我的预约”中查看、取消预约。医生登录后查看自己的排班表和预约患者列表。这条主线看起来简单但里面包含了大量的细节用户注册时手机号格式要验证患者点击预约时要同时判断时间和号源医生登录时要能区分自己属于哪个科室。这些细节恰恰是评价一个系统“真不真”“能不能用”的关键。1.3 项目目录规划与工程化思考拿到源码的第一步我建议大家先看目录结构而不是急着跑起来。一个合理的Flask项目不是把所有代码塞进一个app.py里。我自己习惯将这个项目的后端按模块划分大概长这样backend/ ├── app.py # Flask应用入口 ├── config.py # 配置文件数据库、JWT密钥等 ├── models/ # SQLAlchemy数据模型 │ ├── user.py │ ├── department.py │ ├── doctor.py │ ├── schedule.py │ └── appointment.py ├── resources/ # Flask-RESTful资源类相当于接口层 │ ├── auth.py │ ├── department.py │ ├── doctor.py │ ├── schedule.py │ └── appointment.py ├── utils/ # 工具函数JWT解析、统一返回格式等 └── requirements.txt前端Vue项目的目录则是标准的Vue CLI命令行工具脚手架结构按views和components组织页面。这种前后端分离的写法好处很明显开发时我可以开两个端口后端跑5000前端跑8080用axios调接口部署时前端打包成静态文件交给Nginx托管后端用Gunicorn一个Python的Web服务器跑多个worker两边互不干扰。这也是现在互联网公司比较主流的开发模式。2. 核心模块解析与数据库设计2.1 数据库需求分析和表结构设计数据库设计是整个项目的根。我见过太多新手一上来就建表结果字段设计得一塌糊涂后面写接口的时候疯狂打补丁。预约挂号系统无论如何都离不开这几个核心实体用户、科室、医生、排班、预约。它们之间的关系也很清晰一个科室下有多个医生一个医生有多个排班一个排班可以被多次预约而一个用户可以预约多个排班。关键的表结构设计如下表名核心字段说明usersid, username, password_hash, real_name, id_card, phone, role统一用户表role区分患者/医生/管理员departmentsid, name, intro科室基础信息表doctorsid, user_id, department_id, title, introduction医生信息表与用户表一对一关联科室schedulesid, doctor_id, date, time_slot, total, remaining排班表记录某医生某天某时段的号源总数和剩余量appointmentsid, user_id, schedule_id, status, created_at预约表status记录预约状态已预约/已取消/已完成这里有一个容易被忽略的点不要把医生的职称、介绍等信息直接塞进users表。用户表只存账号、密码和最基本的身份信息医生的专业信息通过外键关联到doctors表。这样设计的好处是将来如果系统需要扩展医生的其它属性比如擅长领域、出诊费用只需要去改doctors表而不需要动通用的用户表。2.2 密码安全与用户权限控制密码处理上绝对不要明文保存。我使用的是werkzeug.security库它提供了generate_password_hash和check_password_hash方法内部采用的是加盐哈希算法这样可以防彩虹表攻击。在代码里用户的密码转换成password_hash字段后端返回用户信息时一定要把密码字段过滤掉这个坑我早期踩过很多次所以在序列化用户信息时我会单独写一个方法只返回需要的字段。权限控制我用的是JWT前端登录成功后后端返回一个包含用户id、角色、过期时间的token串。前端把这个token存到localStorage里每次调接口时在axios拦截器中都会加上Authorization: Bearer ${token}后端用Flask的装饰器来校验权限。这里有个小技巧我会把“校验是否登录”和“校验是否为医生”分开做成两个装饰器比如获取预约列表需要登录但只有医生角色才能看患者详情。这样代码逻辑清晰也方便以后加管理员角色。2.3 医生排班模块的最优解排班是预约系统的核心也是很多新手最容易做简单的部分。有些人会做成一个“排班表”里直接存固定的日期和时段但现实场景是医生可能每周的周一周三出诊每周的展示规律是周期性循环的。所以我在排班模块设计时采用了一个更聪明的办法医生或管理员选择一周中的某几天、某个时间段以及每个时间段的号源总数。系统自动根据这些配置去生成未来一段时间比如两周内的所有排班记录。这样做的好处是当患者在前端选择日期时后端可以根据日期和医生id直接查到排班记录并显示每个时段的剩余号源实现真正的“按需生成”。如果你只是手动创建排班那么每来一个患者你都要去创建一个排班操作繁琐而且很容易在并发环境下出现数据不一致的问题。3. 实操过程与核心环节实现3.1 后端URL设计让接口一目了然一个清晰的后端URL设计能让前端开发效率翻倍。在写代码前我先花点时间把接口约束好。下面是我在这个项目里实际用的接口清单方法接口地址功能说明权限POST/api/auth/register用户注册公开POST/api/auth/login用户登录返回JWT公开GET/api/departments获取科室列表登录可访问GET/api/departments/ /doctors获取科室下的医生列表登录可访问GET/api/doctors/ /schedules获取某医生的排班信息登录可访问POST/api/appointments发起预约传入排班id患者GET/api/appointments/my获取当前登录用户的预约记录登录可访问DELETE/api/appointments/取消预约患者本人接口设计有一个原则我特别想强调接口的路径尽量用名词复数操作用HTTP动词表示。比如删除一个预约不是/deleteAppointment而是DELETE /api/appointments/5。这一套RESTful风格表述性状态传递约定在使用Flask-RESTful时特别好用因为每个Resource类对应一种资源方法直接用get、post、delete命名代码结构干净利落。3.2 预约挂号的并发处理如何避免号源超挂这是整个系统最有技术含量、也最容易被面试官追问的地方。在患者提交预约时我们不能只在前端做一次“剩余号源大于0”的判断因为当多个用户同时发起请求时后端可能会同时读到同一个剩余号源的数值从而导致两个人成功预约同一个号。这是一个典型的并发超卖问题。后端代码我这样处理通过事务和行锁的方式保证同一时间只有一个请求能成功扣减号源。核心逻辑如下from flask import request, jsonify from flask_restful import Resource from models import db, Schedule, Appointment from utils.decorators import login_required, patient_required class AppointmentResource(Resource): login_required patient_required def post(self): data request.get_json() schedule_id data.get(schedule_id) # 锁定该排班记录带行级锁 schedule Schedule.query.filter_by(idschedule_id).with_for_update().first() if not schedule: return {msg: 排班不存在}, 404 if schedule.remaining 0: return {msg: 该号源已被预约完}, 400 # 创建预约记录并扣减剩余号源 appointment Appointment( user_idrequest.user_id, schedule_idschedule.id, statusbooked ) schedule.remaining - 1 db.session.add(appointment) db.session.commit() return {msg: 预约成功, appointment_id: appointment.id}, 201这里的关键是with_for_update()它会锁住这一行数据直到事务提交。如果一个请求正在操作这个排班另一个请求就会在这里等待这就天然解决了并发超卖的问题。当然实际生产环境下会有更复杂的分布式锁方案但在项目演示和单体架构阶段用数据库行锁已经是一个非常成熟且足够稳妥的解决方案。3.3 前端核心页面实现科室、医生、号源三级联动前端这边我印象最深的是预约页面的三级联动交互。用户先选择科室然后选择医生最后选择日期和时段。这个页面看着不多但用Vue做起来逻辑非常顺。我的核心思路是在data里定义三个关键变量data() { return { selectedDepartment: null, selectedDoctor: null, selectedSchedule: null, departments: [], doctors: [], schedules: [] } }当用户点击某个科室时触发handleDepartmentClick方法向后台请求/api/departments/{id}/doctors。拿到医生的数据后把doctors数组清空重设同时把selectedSchedule置空防止上一次选中的排班残留。再往后点击医生就去请求/api/doctors/{id}/schedules拿到未来两周的排班列表包括日期、时段和剩余号源。还有一个细节值得提所有请求都需要带token。在Vue的main.js里我通过axios的拦截器统一处理如果响应状态码是401token过期就清除localStorage并跳转到登录页这样用户就不会在使用过程中频繁遇到“登录已失效”的报错体验会好很多。3.4 开发环境联调时的跨域问题处理前后端分离开发的痛点十个有九个是跨域。前端运行在localhost:8080后端运行在localhost:5000浏览器会拦截后端的响应。处理这个问题我采用的方法是使用Flask-CORS插件它比手动设置响应头要方便得多from flask_cors import CORS from app import app CORS(app, supports_credentialsTrue)这里有一个要注意的点supports_credentialsTrue是为了允许前端axios请求带上cookie或其他凭证信息。默认情况下跨域请求的withCredentials字段是false如果你在登录接口中有使用session或者cookie登录态的这个参数必须打开。但对于JWT方案token放在自定义Authorization头里cookie不是必需的其实不开也能跑通。不过建议还是开着以防以后要用。4. 常见问题与排查技巧实录4.1 数据库表结构改动后如何快速同步在开发过程中我最常遇到的问题就是表结构改了但数据库里的老表没有同步。比如有个老版本的表没有remaining字段我想给它加直接用SQL语句改很容易出错。我现在的标准做法是在写项目之前先花时间把模型定义好然后直接用db.create_all()建表。如果后续真的有模型变动我会优先保证模型文件是对的然后使用flask db migrate配合Flask-Migrate插件来生成迁移脚本这比手动改数据库表强多了。4.2 Vue打包后访问页面出现404或静态资源加载失败这是一个很容易踩的坑。Vue前端项目默认是路由的history模式打包后在服务器上刷新某个子路由页面就会出现404。因为这个路径在服务器上是不存在的需要后端配合做路径重写把所有路由都指向index.html。解决方式有两种第一种是前端改用hash模式在vue-router里加上mode: hashURL里会多出一个#刷新不会404但不太美观。第二种是后端配置一个catch-all路由把非接口的请求都返回到前端的index.html。在Flask里我推荐两种都尝试一下理解为什么history模式需要后端配合这对你以后面试也是加分项。4.3 前端上传视频无法播放m3u8格式与兼容性这个项目里有一个科室介绍的视频展示功能我用的是标准HTML5的video标签但后来测试发现直接放MP4有些浏览器能播有些浏览器不能播尤其是Chrome对格式有一套自己的要求。后来我换成了HLSHTTP Live Streaming协议支持把视频切成m3u8的格式用Vue集成了一个hls.js库来播放。这一步折腾了我不少时间但效果确实明显移动端和桌面端都能流畅播放。这里建议如果只是本地项目不必把所有视频都转成m3u8直接用MP4并确保编码是H.264的大多数浏览器都能正常播放。但如果是要做“大视频在线观看”类的功能就需要深入了解m3u8的原理和前端播放方案。4.4 关于系统扩展和性能优化的一点心得你别看这只是一个预约挂号系统但麻雀虽小五脏俱全。把基础功能做完后还有很多可以优化的方向。比如把预约信息同步到患者手机号短信提醒用Celery一个Python分布式任务队列做异步任务比如给高频访问的科室列表接口加上Redis缓存再比如把数据库从SQLite换成MySQL以支持真正的多用户并发场景。我在实际扩展时遇到最多的坑是“数据库连接池不够用”。如果前端页面刷新得太频繁Flask默认的SQLAlchemy在一段时间后可能报OperationalError: (MySQLdb.exceptions.OperationalError) (1040, Too many connections)。解决办法是修改数据库连接配置的pool_size和max_overflow或者在每个请求结束后主动关闭会话。我在utils里封装了一个统一管理数据库会话的模块确保每次请求结束后连接都会放回池里。4.5 常见问题速查表方便你直接对照我把调试过程中遇到的高频问题整理成了一个表格这个表格也是我在开发后期排查问题时最常用到的工具。希望你拿到源码后如果遇到类似问题能快速定位。问题现象最可能的原因解决方法前端注册成功但无法登录密码哈希生成和校验方法不匹配确认使用同一个哈希函数和校验函数接口返回401token过期、未携带、或角色校验失败检查前端axios是否在headers中添加Authorization预约时提示号源已满排班表中remaining为0或并发扣减失败用with_for_update()行锁控制并发表格数据显示为null关联表数据未正确关联外键字段为空检查数据库中的外键关联以及查询时的join条件Vue组件里拿不到this数据在异步回调中用了this指向错误在回调前用const self this或使用箭头函数打包后访问页面空白前端静态资源路径配置错误检查Vue的publicPath是相对路径还是绝对路径m3u8视频无法播放浏览器不支持HLS或后端未返回正确的MIME类型使用hls.js插件并设置正确的响应头application/vnd.apple.mpegurl5. 源码结构与演示视频的配合使用建议很多同学拿到素材包之后喜欢直接双击演示视频看一遍厉害效果然后就把源码扔一边了。我建议反过来先卡住目录结构和视频里演示的页面然后逐个模块去对照代码这个习惯会让你受益很多。这个项目里源码的结构清晰后端按照models和resources分模块前端按照views和views中的子页面分模块非常适合大家去整理总结自己后续做项目时的模板。我在交付这个项目时习惯在视频里重点走三个场景患者预约挂号的完整流程、医生查看预约列表的操作流程、管理员维护基础数据的流程。看视频的同时用编辑器打开对应模块的代码去思考“为什么点击那个按钮会触发放那一段逻辑”这才是演示视频存在的最大价值。另外拿到源码后的第一件事我建议你先修改配置文件里的数据库连接串和JWT密钥不要用默认值。这个坑也提醒一下很多新手直接跑通项目以后邮件或文档里看到什么密码都默认就在生产环境里部署这很危险。至少在本地你要习惯把所有敏感配置都写在config.py里并加入.gitignore不要把带密钥的配置文件提交到公开仓库。6. 我做完这个项目后的一些体会抛开技术细节我需要说这个项目写下来最大的收获不是学会了Flask的某个插件而是培养了一套完整的“拆解业务—设计数据—实现接口—化前端—联调测试”的思路。医院预约挂号不是简单的“增删改查”它涉及到真实的业务规则科室与医生的对应关系、排班的周期性规则、预约时的并发处理、用户状态的跟踪。这些规则靠脑子记很容易乱但如果你能从表结构设计出发把所有关系用外键和字段表达出来后面写代码就会很丝滑。最后再分享一个我在预约模块里的小技巧。很多人在做“取消预约”时只把用户可以看到的预约状态改掉就完事了但你会被业务方继续追问“那这个号源是不是要释放回去”答案是肯定的。所以我在取消预约的接口里除了更新appointments表的status字段还做了一个加回去的操作appointment Appointment.query.filter_by(idappointment_id, user_iduser_id).first() if appointment and appointment.status booked: schedule Schedule.query.filter_by(idappointment.schedule_id).with_for_update().first() schedule.remaining 1 appointment.status cancelled db.session.commit()这一来一回的加减虽然只是一行代码但正是从“会写接口”到“懂业务”的分水岭。希望这个项目能成为你进入后端开发大门的一把钥匙。本文还有配套的精品资源点击获取