每年到了毕设选题季,总有人捧着各种“XX管理系统”的题目来问我要不要做。看到“基于Spring Boot+小程序的掌上智慧医疗系统医院预约挂号系统”这个选题,我第一反应是:这个方向挑得挺聪明。它不像单一的管理系统那样单薄,也不至于做到高并发秒杀那种规模,属于大多数计算机专业学生能啃下来、答辩时又能拿得出手的难度区间。这篇就把这套系统从选题逻辑、数据库设计、核心接口、小程序端联调,再到答辩准备和文档整理的完整思路展开写一遍,希望帮你把项目做实,而不是只停留在“能跑通demo”的层面。如果你正打算拿这个题目做毕业设计,或者已经拿了源码和资料但不知道从哪下手,这篇文章应该能给你一个比较完整的项目地图:哪些地方是核心得分点,哪些地方最容易被问倒,哪些地方需要提前埋好“防御工事”。废话不多说,直接开始。1. 这个选题为什么在毕设里吃香:难度和答辩点的匹配1.1 一套系统覆盖了三个维度的考核点毕业设计最怕什么?最怕题目做出来了,但答辩时老师问不出东西,或者问出来全是“复制粘贴”的痕迹。预约挂号系统的价值在于,它天然包含一个完整的业务闭环:用户注册登录、查科室、选医生、看排班、提交预约、取消预约、查看记录,再加上后端的科室管理、医生管理、排班维护。这些模块每一个都能单独展开讲,串起来又是一条完整的业务链路。从考核维度上看,第一层是需求分析能力。你有没有把“预约挂号”这件事拆清楚?挂号是不是只要插入一条记录就行?重复挂号怎么处理?号源不够了怎么反馈?这些细节都是老师在论文和代码里要看的。第二层是工程实现能力。前端小程序、后端接口、数据库表结构、联调测试,一套走下来,基本的全栈能力就展示完整了。第三层是业务理解能力。医疗系统的预约场景和普通商品购买场景不一样,号源、排班、停诊、退号这些概念本身就比“增删改查”高一个段位。1.2 Spring Boot 加小程序为什么是“黄金组合”这个组合在毕设里非常常见,原因其实很实在。Spring Boot的生态成熟,网上资料多,遇到报错基本能在社区里找到答案;语法对大多数学生来说比Python后端更熟悉,毕竟大学课程里Java的比例一直很高。MyBatis-Plus这类持久层插件也让数据库操作变得简单,不用手写太多XML映射。小程序端则解决了“App不好装、网页演示不够炫”的问题。答辩的时候拿手机扫个码或者直接在开发者工具里打开,评委就能看到真实的界面交互,比投影PPT里贴截图有说服力得多。而且小程序开发是轻量级的,页面不会太多,单人维护完全来得及。前后端通过HTTP接口通信,数据链路短,调试起来也不容易绕晕。1.3 号源、排班、订单这些概念本身就有“深度”很多毕设项目之所以拿不到高分,不是因为代码写得差,而是因为业务太浅。比如图书管理系统,核心就是“加书、借书、还书”,很难说出花来。但预约挂号系统不同,它天然带有几个值得深挖的点:排班是号源的基础,号源扣减要考虑并发,用户不能重复挂同一个排班,取消预约之后号源要释放,医生停诊之后已挂号用户如何处理。这些点随便拎出来一个,都够在论文里写一节“系统难点与解决方案”。如果你拿到的项目资料里含源码、MySQL脚本、文档和调试指导,那么你要做的不是背下来,而是把每一个“设计决策”都想明白,因为答辩老师最爱问的就是“这里为什么这么设计”。2. 先别急着写代码:业务流程和角色边界想清楚2.1 患者、医生、管理方的功能边界很多学生拿到项目第一件事就打开IDE开始改代码,这是最容易走偏的。建议先把角色和模块梳理清楚。这类系统至少包含三类角色:角色核心功能通常所处端患者/普通用户注册登录、科室查询、医生查询、排班查看、预约挂号、取消预约、我的挂号记录小程序端医生查看自己的排班、查看某排班下已预约患者、更新出诊状态小程序端或后台端管理员科室管理、医生管理、排班生成、号源调整、停诊处理、基础统计后台管理端如果时间有限,优先把患者端做到完整,因为预约流程的核心闭环在患者侧。医生端和管理员端可以做成简单列表加状态修改,甚至用网页后台代替小程序,这样也能保证业务完整,但不要三个端都做得半吊子。2.2 一次预约挂号从开始到结束的完整链路我们得先定义清楚一次“预约行为”到底要经过哪些步骤。最基础的链路是这样的:用户登录,拿到身份凭证。进入首页,按科室浏览,或者直接搜索医生姓名。进入医生详情页,选择某一天、某一个时段排班。系统展示当前剩余号源数,以及挂号费等信息。用户确认预约,后端校验合法性。后端扣减号源,生成一条挂号单记录。用户在“我的预约”里看到记录,状态为“待就诊”。就诊后,状态变为“已完成”;如果用户取消,状态变为“已取消”,号源归还。这个流程听起来简单,但每一步都有可以出错的细节。第5步的“校验合法性”就不是一个if能解决的:排班是否存在、剩余号源是否大于0、当前时间是否已经过了该排班的预约截止时间、用户是不是已经在同一时段预约过其他医生,这些都要想清楚。2.3 容易被忽略的隐性需求除了主线流程,这类系统还有几个很容易被忽略的需求。比如停诊和退号。医生临时停诊,那么该排班下的已挂号用户应该被通知或自动取消;用户取消预约后,号源不是简单改个状态,而是要回到排班的剩余号数里。再比如过号处理:医生叫号之后,用户没到,这个号是保留还是作废?毕设阶段不需要做得太复杂,但至少要在数据库里留一个状态位,别把“待就诊”和“已完成”当成唯一的状态。另外,隐私保护也是一个可以加分的点。查询医生列表时,展示的应该是姓名、职称、科室,而不是身份证号这类敏感信息。接口返回字段越少,越显得你考虑过数据安全。3. 数据库设计定生死:这些表字段最好一开始就规划好3.1 五张核心表的字段清单数据库是整个项目的地基。表结构设计不好,后面写代码会非常痛苦。以常见的实现方案为例,核心表可以分成下面这几张:表名关键字段作用sys_userid, openid, username, phone, avatar, role, status用户与角色departmentid, name, intro, status科室信息doctorid, dept_id, name, title, intro, avatar, status医生信息scheduleid, doctor_id, work_date, time_slot, total, remain, status医生排班与号源reg_orderid, order_no, user_id, schedule_id, doctor_id, visit_time, fee, status, create_time挂号订单这里要特别提醒:表名不要用order,因为order是SQL里的排序关键字,容易给自己埋坑。用reg_order或者appointment_record都行。字段名也一样,避免使用desc、date这种单词作为列名,以免写SQL的时候还得加反引号。下面是一个简化的建表示例,实际项目里可以根据需要加备注字段:CREATE TABLE department ( id BIGINT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL, intro VARCHAR(500), status TINYINT DEFAULT 1 COMMENT '1启用 0停用', create_time DATETIME DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE schedule ( id BIGINT PRIMARY KEY AUTO_INCREMENT, doctor_id BIGINT NOT NULL, work_date VARCHAR(20) NOT NULL, time_slot VARCHAR(20) NOT NULL COMMENT '上午/下午/晚间', total INT DEFAULT 20, remain INT DEFAULT 20, status TINYINT DEFAULT 1 COMMENT '1可预约 0停诊', create_time DATETIME DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE reg_order ( id BIGINT PRIMARY KEY AUTO_INCREMENT, order_no VARCHAR(32) NOT NULL UNIQUE, user_id BIGINT NOT NULL, schedule_id BIGINT NOT NULL, doctor_id BIGINT NOT NULL, dept_name VARCHAR(50), doctor_name VARCHAR(50), visit_time VARCHAR(50), fee DECIMAL(10,2), status TINYINT DEFAULT 0 COMMENT '0待就诊 1已完成 2已取消', create_time DATETIME DEFAULT CURRENT_TIMESTAMP );3.2 给状态字段“加戏”:状态机设计状态字段是这类系统最容易出错的地方。挂号单的status千万别只设计成“有效/无效”两个值。建议至少定义三到四个状态:0待就诊、1已完成、2已取消。如果还要做退号、过期未就诊,就再加:3已退号、4已过期。有了状态机之后,业务逻辑会更清晰:只有“待就诊”状态的订单才能取消;只有“待就诊”状态的记录在完成后才能变为“已完成”;取消后号源要归还;已完成和已取消的订单不允许再被系统修改。这些规则看起来是代码逻辑,本质上是你对状态转移的定义是否严密。同样的道理也适用于schedule表。排班状态应该区分“可预约”“约满”“停诊”。约满可以由remain字段推导出来,但停诊必须是一个独立状态,不然医生临时停诊你没法处理已经预约的用户。3.3 时间、索引、唯一约束的细节时间字段建议使用VARCHAR存日期,比如work_date存“2026-05-10”,time_slot存“上午”,这样处理起来最直接。不要用时间戳去表示一个“上午时段”,到后面写查询条件会把自己绕晕。同理visit_time也建议存一个便于展示的字符串,比如“2026-05-10 上午”,因为业务上不需要对它做时间计算。索引设计上,reg_order的user_id加普通索引,方便用户查“我的预约”;schedule的doctor_id和work_date加联合索引,方便查医生某一天的排班;最关键的是reg_order加上(user_id, schedule_id)的唯一约束,这是防止重复挂号的数据库底线。后面在代码里做校验只是第一道防线,唯一的索引是兜底。4. 后端核心逻辑:预约不退、不超、不重复是怎么做到的4.1 排班查询接口的返回结构设计后端是这套系统的发动机。建议所有接口统一返回格式,比如code、message、data。前端只需要判断code是否为0,然后取data数据即可。下面是一个查询医生排班的返回结构示例:{ "code": 0, "message": "ok", "data": { "doctorId": 12, "doctorName": "王医生", "deptName": "心内科", "scheduleList": [ { "scheduleId": 101, "workDate": "2026-05-10", "timeSlot": "上午", "total": 20, "remain": 3, "fee": "15.00", "canBook": true } ] } }这里有个细节:不要把remain直接暴露给前端后让前端判断是否有号。更稳妥的做法是后端根据“当前时间是否超过预约截止时间、排班是否停诊、剩余号源是否大于0”算出canBook字段,前端拿到canBook直接决定按钮是否可用。把业务判断收敛到后端,可以减少前端写重复逻辑的机会。4.2 创建预约的事务与原子扣减创建预约是整个系统最核心的接口,需要满足三个要求:不重复、不超卖、扣减和下单保持一致。代码层面,先做基础校验,再扣减号源,最后插入挂号单,整个过程要放在一个事务里。下面是一个典型的Service方法结构:@Transactional public String createOrder(CreateOrderRequest req) { Schedule schedule = scheduleMapper.selectById(req.getScheduleId()); if (schedule == null || schedule.getStatus() != 1) { throw new BusinessException("排班不存在或已停诊"); } if (schedule.getRemain() <= 0) { throw new BusinessException("号源不足"); } int count = regOrderMapper.countByUserAndSchedule(req.getUserId(), req.getScheduleId()); if (count > 0) { throw new BusinessException("您已预约过该排班,请勿重复挂号"); } int rows = scheduleMapper.decreaseRemain(req.getScheduleId()); if (rows == 0) { throw new BusinessException("号源刚被抢完,请重新选择"); } RegOrder order = new RegOrder(); order.setOrderNo(generateOrderNo()); order.setUserId(req.getUserId()); order.setScheduleId(req.getScheduleId()); order.setStatus(0); regOrderMapper.insert(order); return order.getOrderNo(); }关键在decreaseRemain这条SQL:UPDATE schedule SET remain = remain - 1 WHERE id = #{id} AND remain > 0这条语句是原子操作,不会把remain扣成负数。即使两个请求同时进来,数据库也会串行处理。另一个方案是selectByIdForUpdate加锁,再判断再更新,但相对复杂,对毕设来说原子更新已经足够体现并发意识。4.3 防重复预约和防重复提交的两道防线重复预约的代码校验上面提到了,但代码校验存在时间窗口。假设用户快速点了两次提交,两个请求同时通过了“count == 0”的判断,就会插入两条记录。这时候数据库唯一约束就派上用场了。我给reg_order表加一个(user_id, schedule_id)的唯一索引,第二个插入直接报错,事务回滚后号源会自动恢复。防重复提交还有一层,是幂等思路。可以在插入前根据order_no判断是否已存在,但更简单的做法是让前端在用户点击提交后立即把按钮置灰,加上loading状态。这个是体验问题,也是防重复的手段,成本最低。取消预约的接口也同样需要事务:先根据订单号查出记录,校验状态必须是“待就诊”,然后把状态改成“已取消”,再把排班表对应schedule_id的remain加1。注意顺序:改状态和加号源只要有一个失败,整个操作就不能成功,必须放在同一个事务里。4.4 登录与权限校验小程序端通常用用户在小程序平台上的唯一标识作为身份,首次登录时把用户信息同步到sys_user表,后端生成一个token返回给小程序。后续请求都带上token,后端通过拦截器校验。不需要引入特别重的安全框架,拦截器加注解就能解决,但要注意排除登录和科室列表这类公开接口。权限控制也别做得太复杂。患者和医生可以共用一个用户表,通过role字段区分。医生接口校验role必须等于医生,管理员接口校验role必须等于管理员。这样既清晰又足够应对答辩提问。5. 小程序端开发:页面、交互和接口对接怎么落地5.1 页面分解:至少这几页不能缺小程序端的页面不需要多,但主流程必须完整。我习惯把页面拆成七个左右:首页:科室分类入口,搜索框。科室详情页:该科室下的医生列表。医生详情页:医生介绍,排班列表。排班确认页:选择具体时段,确认预约。预约成功页:展示订单号,跳转我的预约。我的预约页:列表展示订单,支持取消操作。我的页面:登录状态、个人信息。页面太多会做不完,太少又显得功能单薄。上面的结构刚好覆盖了“找医生、看排班、下单、管订单”的完整链路,每一页都有明确职责。5.2 接口对接的正确姿势和联调经验小程序端最常见的坑有三个。第一个是本地调试访问不到后端。解决办法是在本地启一个统一的request工具,把基础地址集中管理。开发时指向本机局域网IP,部署时指向线上接口地址。注意手机真机预览时不能用localhost,要改成电脑的局域网IP。第二个是请求拦截和错误提示。封装的request工具应该统一在响应里判断业务状态码,非0状态统一弹出提示,不让每个页面都写一遍错误处理。token失效时统一跳转登录页。第三个是数据为空和加载中的处理。列表页要有加载动画,空数据要有占位文案,接口报错要有重试按钮。这些细节在答辩演示时特别加分,评委一眼就能看出你有没有真正考虑用户体验。5.3 表单校验和交互细节预约确认页是最容易出交互问题的地方。排班日期选择器要控制只能选今天之后的日期;号源为0的时段直接置灰;点击预约后按钮立刻loading,防止重复提交。取消预约时要有二次确认弹窗,不要一点就取消。医生搜索功能如果可以做成模糊搜索,按姓名或科室名匹配,演示效果会好很多。比如输入“心”能查到“心内科”下的医生,这个功能写起来不难,但在答辩时很有记忆点。6. 从部署到答辩:文档、演示数据、代码讲解一个都不能少6.1 环境配置与调试顺序拿到一套完整资料后,建议按这个顺序部署:先创建MySQL数据库,执行SQL脚本,确认表都建好;然后修改后端配置文件里的数据库地址、用户名、密码;启动Spring Boot服务,用接口测试工具验证登录和科室列表接口;最后再打开小程序项目,修改接口地址,运行查看效果。我遇到过不少学生在环境上卡住的情况,最常见的两个原因是:MySQL版本语法不兼容,比如用了新版MySQL但脚本里写了老版的字段类型;后端配置里端口被占用。建议先看日志,日志里一般都会明确告诉你哪里错了。调试的时候不要东改一下西改一下,每次只改一个变量,改完重启一次。6.2 造一套“有说服力”的演示数据演示数据的重要性被严重低估。很多学生的系统里只有一两个医生,几行测试数据,演示起来干巴巴的。如果想让答辩效果好,建议准备一套完整的“有故事”的数据:科室至少四五个,比如内科、外科、儿科、骨科。每个科室有两三个医生,职称从主治到主任都有。排班覆盖未来一周,每天分上午、下午两个时段。每个时段的号源数量不要全满,留几个约满的,几个有余号的。造两个测试用户,一个演示预约,一个演示取消。演示路径也要提前走一遍:登录、选科室、选医生、选时段、确认预约、查看订单、取消预约。每一步鼠标点在哪、手机屏显示什么,心里要有数。不要现场去边翻边找,那会给评委留下“不熟悉项目”的印象。6.3 源码、文档和代码讲解怎么准备如果你手里有源码、SQL脚本、开发文档和调试指导这套完整资料,那么你的重点不是“把代码看完”,而是“把核心逻辑讲明白”。建议准备一个三分钟的讲解思路:一,讲业务背景,为什么需要预约挂号,解决了什么线下痛点。二,讲技术架构,前端小程序、后端Spring Boot、数据库MySQL,各自职责是什么。三,讲核心难点,号源不超卖怎么做的,重复挂号怎么防的,状态怎么流转的。四,讲个人工作量,哪些页面是你调的,哪些接口是你写的,哪些测试是你跑的。不要说自己把所有代码都从零写的,那很容易被追问细节。更稳妥的说法是:在整体框架基础上完成了核心模块的实现和优化,然后准确说出核心模块的代码位置和逻辑。论文里的数据库设计部分,建议把ER图、表结构、字段说明列清楚,这部分是所有计算机专业论文都会重点看的内容。最后再分享一点我个人带过多个毕设项目的体会。预约挂号这个题目能拿多少分,往往不取决于系统功能有多炫,而是取决于你对自己代码和业务逻辑的理解有多深。源码可以给你一个高起点,但你要做的不是把别人的成果背下来,而是把“为什么这样设计”想通:为什么用事务,为什么用唯一索引,为什么状态字段要留多个值,为什么取消要恢复号源。把这些问题的答案装进脑子里,答辩时就算老师换个角度问,你也接得住。如果你还想要一点进阶发挥,可以在基础功能上加一个“医生停诊后批量通知已预约用户”的后台操作,或者做一个“个人中心就诊记录”的时间线展示。这些附加功能都不会占用太多时间,但会让你的项目在同类题目中更有辨识度。希望这套思路能帮你的毕设少走点弯路,祝顺利。
每年到了毕设选题季,总有人捧着各种“XX管理系统”的题目来问我要不要做。看到“基于Spring Boot+小程序的掌上智慧医疗系统医院预约挂号系统”这个选题,我第一反应是:这个方向挑得挺聪明。它不像单一的管理系统那样单薄,也不至于做到高并发秒杀那种规模,属于大多数计算机专业学生能啃下来、答辩时又能拿得出手的难度区间。这篇就把这套系统从选题逻辑、数据库设计、核心接口、小程序端联调,再到答辩准备和文档整理的完整思路展开写一遍,希望帮你把项目做实,而不是只停留在“能跑通demo”的层面。如果你正打算拿这个题目做毕业设计,或者已经拿了源码和资料但不知道从哪下手,这篇文章应该能给你一个比较完整的项目地图:哪些地方是核心得分点,哪些地方最容易被问倒,哪些地方需要提前埋好“防御工事”。废话不多说,直接开始。1. 这个选题为什么在毕设里吃香:难度和答辩点的匹配1.1 一套系统覆盖了三个维度的考核点毕业设计最怕什么?最怕题目做出来了,但答辩时老师问不出东西,或者问出来全是“复制粘贴”的痕迹。预约挂号系统的价值在于,它天然包含一个完整的业务闭环:用户注册登录、查科室、选医生、看排班、提交预约、取消预约、查看记录,再加上后端的科室管理、医生管理、排班维护。这些模块每一个都能单独展开讲,串起来又是一条完整的业务链路。从考核维度上看,第一层是需求分析能力。你有没有把“预约挂号”这件事拆清楚?挂号是不是只要插入一条记录就行?重复挂号怎么处理?号源不够了怎么反馈?这些细节都是老师在论文和代码里要看的。第二层是工程实现能力。前端小程序、后端接口、数据库表结构、联调测试,一套走下来,基本的全栈能力就展示完整了。第三层是业务理解能力。医疗系统的预约场景和普通商品购买场景不一样,号源、排班、停诊、退号这些概念本身就比“增删改查”高一个段位。1.2 Spring Boot 加小程序为什么是“黄金组合”这个组合在毕设里非常常见,原因其实很实在。Spring Boot的生态成熟,网上资料多,遇到报错基本能在社区里找到答案;语法对大多数学生来说比Python后端更熟悉,毕竟大学课程里Java的比例一直很高。MyBatis-Plus这类持久层插件也让数据库操作变得简单,不用手写太多XML映射。小程序端则解决了“App不好装、网页演示不够炫”的问题。答辩的时候拿手机扫个码或者直接在开发者工具里打开,评委就能看到真实的界面交互,比投影PPT里贴截图有说服力得多。而且小程序开发是轻量级的,页面不会太多,单人维护完全来得及。前后端通过HTTP接口通信,数据链路短,调试起来也不容易绕晕。1.3 号源、排班、订单这些概念本身就有“深度”很多毕设项目之所以拿不到高分,不是因为代码写得差,而是因为业务太浅。比如图书管理系统,核心就是“加书、借书、还书”,很难说出花来。但预约挂号系统不同,它天然带有几个值得深挖的点:排班是号源的基础,号源扣减要考虑并发,用户不能重复挂同一个排班,取消预约之后号源要释放,医生停诊之后已挂号用户如何处理。这些点随便拎出来一个,都够在论文里写一节“系统难点与解决方案”。如果你拿到的项目资料里含源码、MySQL脚本、文档和调试指导,那么你要做的不是背下来,而是把每一个“设计决策”都想明白,因为答辩老师最爱问的就是“这里为什么这么设计”。2. 先别急着写代码:业务流程和角色边界想清楚2.1 患者、医生、管理方的功能边界很多学生拿到项目第一件事就打开IDE开始改代码,这是最容易走偏的。建议先把角色和模块梳理清楚。这类系统至少包含三类角色:角色核心功能通常所处端患者/普通用户注册登录、科室查询、医生查询、排班查看、预约挂号、取消预约、我的挂号记录小程序端医生查看自己的排班、查看某排班下已预约患者、更新出诊状态小程序端或后台端管理员科室管理、医生管理、排班生成、号源调整、停诊处理、基础统计后台管理端如果时间有限,优先把患者端做到完整,因为预约流程的核心闭环在患者侧。医生端和管理员端可以做成简单列表加状态修改,甚至用网页后台代替小程序,这样也能保证业务完整,但不要三个端都做得半吊子。2.2 一次预约挂号从开始到结束的完整链路我们得先定义清楚一次“预约行为”到底要经过哪些步骤。最基础的链路是这样的:用户登录,拿到身份凭证。进入首页,按科室浏览,或者直接搜索医生姓名。进入医生详情页,选择某一天、某一个时段排班。系统展示当前剩余号源数,以及挂号费等信息。用户确认预约,后端校验合法性。后端扣减号源,生成一条挂号单记录。用户在“我的预约”里看到记录,状态为“待就诊”。就诊后,状态变为“已完成”;如果用户取消,状态变为“已取消”,号源归还。这个流程听起来简单,但每一步都有可以出错的细节。第5步的“校验合法性”就不是一个if能解决的:排班是否存在、剩余号源是否大于0、当前时间是否已经过了该排班的预约截止时间、用户是不是已经在同一时段预约过其他医生,这些都要想清楚。2.3 容易被忽略的隐性需求除了主线流程,这类系统还有几个很容易被忽略的需求。比如停诊和退号。医生临时停诊,那么该排班下的已挂号用户应该被通知或自动取消;用户取消预约后,号源不是简单改个状态,而是要回到排班的剩余号数里。再比如过号处理:医生叫号之后,用户没到,这个号是保留还是作废?毕设阶段不需要做得太复杂,但至少要在数据库里留一个状态位,别把“待就诊”和“已完成”当成唯一的状态。另外,隐私保护也是一个可以加分的点。查询医生列表时,展示的应该是姓名、职称、科室,而不是身份证号这类敏感信息。接口返回字段越少,越显得你考虑过数据安全。3. 数据库设计定生死:这些表字段最好一开始就规划好3.1 五张核心表的字段清单数据库是整个项目的地基。表结构设计不好,后面写代码会非常痛苦。以常见的实现方案为例,核心表可以分成下面这几张:表名关键字段作用sys_userid, openid, username, phone, avatar, role, status用户与角色departmentid, name, intro, status科室信息doctorid, dept_id, name, title, intro, avatar, status医生信息scheduleid, doctor_id, work_date, time_slot, total, remain, status医生排班与号源reg_orderid, order_no, user_id, schedule_id, doctor_id, visit_time, fee, status, create_time挂号订单这里要特别提醒:表名不要用order,因为order是SQL里的排序关键字,容易给自己埋坑。用reg_order或者appointment_record都行。字段名也一样,避免使用desc、date这种单词作为列名,以免写SQL的时候还得加反引号。下面是一个简化的建表示例,实际项目里可以根据需要加备注字段:CREATE TABLE department ( id BIGINT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL, intro VARCHAR(500), status TINYINT DEFAULT 1 COMMENT '1启用 0停用', create_time DATETIME DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE schedule ( id BIGINT PRIMARY KEY AUTO_INCREMENT, doctor_id BIGINT NOT NULL, work_date VARCHAR(20) NOT NULL, time_slot VARCHAR(20) NOT NULL COMMENT '上午/下午/晚间', total INT DEFAULT 20, remain INT DEFAULT 20, status TINYINT DEFAULT 1 COMMENT '1可预约 0停诊', create_time DATETIME DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE reg_order ( id BIGINT PRIMARY KEY AUTO_INCREMENT, order_no VARCHAR(32) NOT NULL UNIQUE, user_id BIGINT NOT NULL, schedule_id BIGINT NOT NULL, doctor_id BIGINT NOT NULL, dept_name VARCHAR(50), doctor_name VARCHAR(50), visit_time VARCHAR(50), fee DECIMAL(10,2), status TINYINT DEFAULT 0 COMMENT '0待就诊 1已完成 2已取消', create_time DATETIME DEFAULT CURRENT_TIMESTAMP );3.2 给状态字段“加戏”:状态机设计状态字段是这类系统最容易出错的地方。挂号单的status千万别只设计成“有效/无效”两个值。建议至少定义三到四个状态:0待就诊、1已完成、2已取消。如果还要做退号、过期未就诊,就再加:3已退号、4已过期。有了状态机之后,业务逻辑会更清晰:只有“待就诊”状态的订单才能取消;只有“待就诊”状态的记录在完成后才能变为“已完成”;取消后号源要归还;已完成和已取消的订单不允许再被系统修改。这些规则看起来是代码逻辑,本质上是你对状态转移的定义是否严密。同样的道理也适用于schedule表。排班状态应该区分“可预约”“约满”“停诊”。约满可以由remain字段推导出来,但停诊必须是一个独立状态,不然医生临时停诊你没法处理已经预约的用户。3.3 时间、索引、唯一约束的细节时间字段建议使用VARCHAR存日期,比如work_date存“2026-05-10”,time_slot存“上午”,这样处理起来最直接。不要用时间戳去表示一个“上午时段”,到后面写查询条件会把自己绕晕。同理visit_time也建议存一个便于展示的字符串,比如“2026-05-10 上午”,因为业务上不需要对它做时间计算。索引设计上,reg_order的user_id加普通索引,方便用户查“我的预约”;schedule的doctor_id和work_date加联合索引,方便查医生某一天的排班;最关键的是reg_order加上(user_id, schedule_id)的唯一约束,这是防止重复挂号的数据库底线。后面在代码里做校验只是第一道防线,唯一的索引是兜底。4. 后端核心逻辑:预约不退、不超、不重复是怎么做到的4.1 排班查询接口的返回结构设计后端是这套系统的发动机。建议所有接口统一返回格式,比如code、message、data。前端只需要判断code是否为0,然后取data数据即可。下面是一个查询医生排班的返回结构示例:{ "code": 0, "message": "ok", "data": { "doctorId": 12, "doctorName": "王医生", "deptName": "心内科", "scheduleList": [ { "scheduleId": 101, "workDate": "2026-05-10", "timeSlot": "上午", "total": 20, "remain": 3, "fee": "15.00", "canBook": true } ] } }这里有个细节:不要把remain直接暴露给前端后让前端判断是否有号。更稳妥的做法是后端根据“当前时间是否超过预约截止时间、排班是否停诊、剩余号源是否大于0”算出canBook字段,前端拿到canBook直接决定按钮是否可用。把业务判断收敛到后端,可以减少前端写重复逻辑的机会。4.2 创建预约的事务与原子扣减创建预约是整个系统最核心的接口,需要满足三个要求:不重复、不超卖、扣减和下单保持一致。代码层面,先做基础校验,再扣减号源,最后插入挂号单,整个过程要放在一个事务里。下面是一个典型的Service方法结构:@Transactional public String createOrder(CreateOrderRequest req) { Schedule schedule = scheduleMapper.selectById(req.getScheduleId()); if (schedule == null || schedule.getStatus() != 1) { throw new BusinessException("排班不存在或已停诊"); } if (schedule.getRemain() <= 0) { throw new BusinessException("号源不足"); } int count = regOrderMapper.countByUserAndSchedule(req.getUserId(), req.getScheduleId()); if (count > 0) { throw new BusinessException("您已预约过该排班,请勿重复挂号"); } int rows = scheduleMapper.decreaseRemain(req.getScheduleId()); if (rows == 0) { throw new BusinessException("号源刚被抢完,请重新选择"); } RegOrder order = new RegOrder(); order.setOrderNo(generateOrderNo()); order.setUserId(req.getUserId()); order.setScheduleId(req.getScheduleId()); order.setStatus(0); regOrderMapper.insert(order); return order.getOrderNo(); }关键在decreaseRemain这条SQL:UPDATE schedule SET remain = remain - 1 WHERE id = #{id} AND remain > 0这条语句是原子操作,不会把remain扣成负数。即使两个请求同时进来,数据库也会串行处理。另一个方案是selectByIdForUpdate加锁,再判断再更新,但相对复杂,对毕设来说原子更新已经足够体现并发意识。4.3 防重复预约和防重复提交的两道防线重复预约的代码校验上面提到了,但代码校验存在时间窗口。假设用户快速点了两次提交,两个请求同时通过了“count == 0”的判断,就会插入两条记录。这时候数据库唯一约束就派上用场了。我给reg_order表加一个(user_id, schedule_id)的唯一索引,第二个插入直接报错,事务回滚后号源会自动恢复。防重复提交还有一层,是幂等思路。可以在插入前根据order_no判断是否已存在,但更简单的做法是让前端在用户点击提交后立即把按钮置灰,加上loading状态。这个是体验问题,也是防重复的手段,成本最低。取消预约的接口也同样需要事务:先根据订单号查出记录,校验状态必须是“待就诊”,然后把状态改成“已取消”,再把排班表对应schedule_id的remain加1。注意顺序:改状态和加号源只要有一个失败,整个操作就不能成功,必须放在同一个事务里。4.4 登录与权限校验小程序端通常用用户在小程序平台上的唯一标识作为身份,首次登录时把用户信息同步到sys_user表,后端生成一个token返回给小程序。后续请求都带上token,后端通过拦截器校验。不需要引入特别重的安全框架,拦截器加注解就能解决,但要注意排除登录和科室列表这类公开接口。权限控制也别做得太复杂。患者和医生可以共用一个用户表,通过role字段区分。医生接口校验role必须等于医生,管理员接口校验role必须等于管理员。这样既清晰又足够应对答辩提问。5. 小程序端开发:页面、交互和接口对接怎么落地5.1 页面分解:至少这几页不能缺小程序端的页面不需要多,但主流程必须完整。我习惯把页面拆成七个左右:首页:科室分类入口,搜索框。科室详情页:该科室下的医生列表。医生详情页:医生介绍,排班列表。排班确认页:选择具体时段,确认预约。预约成功页:展示订单号,跳转我的预约。我的预约页:列表展示订单,支持取消操作。我的页面:登录状态、个人信息。页面太多会做不完,太少又显得功能单薄。上面的结构刚好覆盖了“找医生、看排班、下单、管订单”的完整链路,每一页都有明确职责。5.2 接口对接的正确姿势和联调经验小程序端最常见的坑有三个。第一个是本地调试访问不到后端。解决办法是在本地启一个统一的request工具,把基础地址集中管理。开发时指向本机局域网IP,部署时指向线上接口地址。注意手机真机预览时不能用localhost,要改成电脑的局域网IP。第二个是请求拦截和错误提示。封装的request工具应该统一在响应里判断业务状态码,非0状态统一弹出提示,不让每个页面都写一遍错误处理。token失效时统一跳转登录页。第三个是数据为空和加载中的处理。列表页要有加载动画,空数据要有占位文案,接口报错要有重试按钮。这些细节在答辩演示时特别加分,评委一眼就能看出你有没有真正考虑用户体验。5.3 表单校验和交互细节预约确认页是最容易出交互问题的地方。排班日期选择器要控制只能选今天之后的日期;号源为0的时段直接置灰;点击预约后按钮立刻loading,防止重复提交。取消预约时要有二次确认弹窗,不要一点就取消。医生搜索功能如果可以做成模糊搜索,按姓名或科室名匹配,演示效果会好很多。比如输入“心”能查到“心内科”下的医生,这个功能写起来不难,但在答辩时很有记忆点。6. 从部署到答辩:文档、演示数据、代码讲解一个都不能少6.1 环境配置与调试顺序拿到一套完整资料后,建议按这个顺序部署:先创建MySQL数据库,执行SQL脚本,确认表都建好;然后修改后端配置文件里的数据库地址、用户名、密码;启动Spring Boot服务,用接口测试工具验证登录和科室列表接口;最后再打开小程序项目,修改接口地址,运行查看效果。我遇到过不少学生在环境上卡住的情况,最常见的两个原因是:MySQL版本语法不兼容,比如用了新版MySQL但脚本里写了老版的字段类型;后端配置里端口被占用。建议先看日志,日志里一般都会明确告诉你哪里错了。调试的时候不要东改一下西改一下,每次只改一个变量,改完重启一次。6.2 造一套“有说服力”的演示数据演示数据的重要性被严重低估。很多学生的系统里只有一两个医生,几行测试数据,演示起来干巴巴的。如果想让答辩效果好,建议准备一套完整的“有故事”的数据:科室至少四五个,比如内科、外科、儿科、骨科。每个科室有两三个医生,职称从主治到主任都有。排班覆盖未来一周,每天分上午、下午两个时段。每个时段的号源数量不要全满,留几个约满的,几个有余号的。造两个测试用户,一个演示预约,一个演示取消。演示路径也要提前走一遍:登录、选科室、选医生、选时段、确认预约、查看订单、取消预约。每一步鼠标点在哪、手机屏显示什么,心里要有数。不要现场去边翻边找,那会给评委留下“不熟悉项目”的印象。6.3 源码、文档和代码讲解怎么准备如果你手里有源码、SQL脚本、开发文档和调试指导这套完整资料,那么你的重点不是“把代码看完”,而是“把核心逻辑讲明白”。建议准备一个三分钟的讲解思路:一,讲业务背景,为什么需要预约挂号,解决了什么线下痛点。二,讲技术架构,前端小程序、后端Spring Boot、数据库MySQL,各自职责是什么。三,讲核心难点,号源不超卖怎么做的,重复挂号怎么防的,状态怎么流转的。四,讲个人工作量,哪些页面是你调的,哪些接口是你写的,哪些测试是你跑的。不要说自己把所有代码都从零写的,那很容易被追问细节。更稳妥的说法是:在整体框架基础上完成了核心模块的实现和优化,然后准确说出核心模块的代码位置和逻辑。论文里的数据库设计部分,建议把ER图、表结构、字段说明列清楚,这部分是所有计算机专业论文都会重点看的内容。最后再分享一点我个人带过多个毕设项目的体会。预约挂号这个题目能拿多少分,往往不取决于系统功能有多炫,而是取决于你对自己代码和业务逻辑的理解有多深。源码可以给你一个高起点,但你要做的不是把别人的成果背下来,而是把“为什么这样设计”想通:为什么用事务,为什么用唯一索引,为什么状态字段要留多个值,为什么取消要恢复号源。把这些问题的答案装进脑子里,答辩时就算老师换个角度问,你也接得住。如果你还想要一点进阶发挥,可以在基础功能上加一个“医生停诊后批量通知已预约用户”的后台操作,或者做一个“个人中心就诊记录”的时间线展示。这些附加功能都不会占用太多时间,但会让你的项目在同类题目中更有辨识度。希望这套思路能帮你的毕设少走点弯路,祝顺利。