每年毕业设计开题季,“基于Spring Boot的医院用户移动端”这类题目几乎每个实验室都会出现。很多同学第一反应是:这不就是一个带登录的查询页面吗?医院系统难道不就是后台管理加几个表格?等真拿到题目做两周就会发现,预约挂号、排班余号、报告查看、支付回调这些环节每一个都能拆出不少细节。这篇文章我打算把自己带毕设时对这类题目的完整拆解写出来,从需求规划到后端实现再到移动端联调,最后聊聊答辩怎么讲才不像在念PPT,希望对准备做Spring Boot医院移动端毕设的同学有帮助。
1. 从毕业设计题目里拆出真实业务需求
1.1 “用户移动端”不是App,而是移动医疗服务的入口
标题里的“某中心医院”是一个具体场景,但更准确地说,它是一个典型的三甲医院患者服务移动端。这类系统的核心用户是患者或患者家属,他们要解决的是“去医院办事排队太久”的问题。所以核心功能通常围绕就医流程展开:查医生排班、预约挂号、支付费用、看报告单、住院费用查询、院内导航或就诊记录等。
很多同学拿到题目后第一件事是打开编辑器建表,这其实顺序反了。用户移动端最重要的是把“患者角度”的就医动线抠出来。以一次普通的门诊就诊为例:患者注册登录后先按科室找医生,看医生什么时候出诊,选择一个时间预约,支付挂号费,就诊当天扫码签到或凭码取号,医生看诊开单,患者在线缴费,之后可以在报告出来后随时查看。这中间还穿插着退款、取消预约、历史记录等功能。
把这条路径一条条列出来,再对应到移动端的页面、后端接口和数据库表,项目范围一下就不模糊了。所以我在辅导的时候都会让同学先在纸上画一遍“让一个患者从登录到拿到报告”的完整过程,再把它拆成功能清单,而不是照着网上模板抄菜单。
1.2 前端选型:小程序、H5还是原生App
题目中写的是“移动端”,很多同学会默认必须用Android原生开发。其实移动端是一个产品概念,不是技术概念。Spring Boot只负责后端接口,移动端可以选择的承载方式非常多:微信小程序、微信公众号H5、Uniapp跨端项目、Android原生都可以。选型要看的不是哪个“高级”,而是你实际能维护、能演示、能在答辩时讲清楚。
下面是我的建议(也是常见实践):
| 承载方式 | 优点 | 缺点 | 适合场景 |
|---|---|---|---|
| 微信小程序 | 调用方便,审批/登录示范自然,免安装 | 需要注册小程序账号,个人主体审核有门槛 | 演示线上项目时观感好 |
| H5响应式网页 | 开发简单,浏览器直接演示,不依赖平台 | 更容易被质疑“这不是App” | 时间紧、重心想放在后端 |
| Uniapp | 一套代码编译到小程序和App | 有一定学习成本,调试链路长 | 想同时出两个端加分 |
| Android原生 | 系统API掌控强,适合做离线缓存等 | 开发量最大,机型适配繁琐 | 尽力展示终端能力 |
从答辩角度看,我比较推荐Spring Boot + Uniapp或Spring Boot + 小程序,因为能讲一套接口同时支撑多个端,能体现前后端分离的理念。如果题目本身强调“用户移动端”,那做成H5也算合理,但需要在摘要里写明“基于移动端适配的Web应用”,避免评委误判。
2. 业务流程与数据库设计:把状态流转想清楚再动工
2.1 从患者一次完整就诊路径推导功能清单
开发前需要列一个功能清单。注意不要只列“登录注册”“医生管理”“预约挂号”“报告查询”这种名词,要拆成“动词 + 对象”。比如:
- 登录注册(手机号密码)
- 浏览科室列表
- 查看某科室下的医生
- 根据日期查看医生排班
- 选择排班时段并提交预约
- 支付挂号费用(模拟支付即可)
- 取消未就诊的预约并退费
- 查看待就诊二维码/预约凭证
- 查询并下载检查报告
- 查看历史预约与缴费记录
- 后台维护:医生、科室、排班、放号数量
这些功能对应到移动端,就是一个多Tab结构:首页(推荐科室/公告)、科室医生、我的(订单、报告、个人信息)。不要一开始做一堆花哨的“健康资讯”“在线问诊”,那些既难实现又容易偏离毕设重点。医院的毕设重点一定是“业务状态正确”。
2.2 核心表结构与字段设计
基于以上功能,数据库至少要有这些核心表:
- 用户表
t_user:id、手机号、密码密文、姓名、身份证号(脱敏)、创建时间 - 科室表
t_department:科室名称、简介、位置 - 医生表
t_doctor:所属科室、姓名、职称、简介、排班状态 - 排班表
t_schedule:医生ID、日期、上午/下午/晚间时段、总号数、剩余号数 - 预约挂号表
t_appointment:用户ID、排班ID、就诊日期、时段、订单号、状态、支付金额 - 支付记录表
t_payment:预约ID、流水号、支付方式、金额、状态 - 报告表
t_report:用户ID、医生ID、报告标题、生成时间、报告文件路径 - 管理员/后台账号表(按需)
这里特别说几个容易忽略的字段:
- 排班表不要只放“日期”,要区分“号别”,比如上午号、下午号。每个时段一个schedule记录,余号单独维护。
- 预约表要有唯一业务订单号,不能只靠自增主键。给用户的回执、对接模拟支付都需要。
- 所有表都要有
create_time、update_time,很多答辩问题会围绕“这个字段为什么要冗余”展开。 - 用户身份证、手机号属于敏感信息,数据库里可以存明文,但接口返回必须做脱敏,比如
187****1234。
2.3 预约状态不是“有/无”,而是有限状态机
这是这个项目里最重要的设计点。很多同学把预约状态设计成布尔值:0表示未预约,1表示已预约。但真实业务里状态是流动的:
- 待支付:用户提交预约但还没付款
- 已支付:挂号成功,等待就诊
- 已完成:该号已使用,患者看完诊
- 已取消:用户主动取消/超时未支付
- 已退费:取消后原路退回
用数字status表示,比如0待支付、1已支付、2已完成、3已取消、4已退费。同时要规定允许的状态变化:待支付只能变成已支付或已取消;已支付只能变成已完成或已取消(就诊前允许退号);已取消不能再操作。你可以把这些规则写在Service层,而不是做成随便修改的字段。答辩时如果评委问“患者重复预约怎么办”,就是用状态机 + 业务校验来回答。
3. Spring Boot后端:认证、预约、报告这三个环节最值得写透
3.1 工程结构与接口分层
Spring Boot项目我建议用很标准的Controller / Service / Mapper三层,不要整复杂的微服务。移动端给后端带来的最大变化是“接口风格统一”,一般用RESTful风格,返回JSON格式的统一结果体,例如:
public class Result<T> { private Integer code; private String message; private T data; public static <T> Result<T> ok(T data) { Result<T> r = new Result<>(); r.code = 200; r.message = "success"; r.data = data; return r; } public static <T> Result<T> error(Integer code, String message) { Result<T> r = new Result<>(); r.code = code; r.message = message; return r; } }每层职责要清楚:Controller只做参数接收与结果包装,Service封装业务规则,Mapper负责数据库操作。这样答辩时能画出清晰的调用链路图。工程内部可以按业务模块分包:controller、service、mapper、entity、dto、vo、config、common等等。
3.2 登录认证:不用Session,用Token
移动端和浏览器不同,Session的Cookie机制在App里表现得并不好,因此这类项目普遍使用Token认证。一种经典组合是Spring Boot + Redis + JWT,或者使用轻量级认证框架如Sa-Token。
JWT的核心思路是:用户登录成功后,服务端生成一个带签名和过期时间的token,移动端每次请求把它放在请求头里,服务端通过拦截器验证签名和有效期,然后从token里读取用户ID。
一个参考的流程:
- 登录接口校验手机号+密码
- 校验通过后生成token,过期时间设置为24小时或7天
- 返回给移动端,移动端保存到本地storage
- 自定义拦截器或HandlerInterceptor,统一在请求头读取Authorization
- 拦截器解析token成功后,把userId放入ThreadLocal供后续使用
代码大概长这样(示意):
@Component public class AuthInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token = request.getHeader("Authorization"); // 校验token合法性,如果无效则返回401并中断请求 if (!tokenService.validate(token)) { response.setStatus(401); return false; } Long userId = tokenService.getUserId(token); UserContext.set(userId); return true; } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { UserContext.clear(); } }密码存储需要提到:不能明文存,使用BCrypt加密。答辩时如果被问到“用户密码如何保证安全”,这是标准答案。
3.3 预约挂号的并发控制
预约挂号是题目的核心,也是最容易暴雷的点。假设某个医生的上午号只剩1个,两个患者同时提交预约,如果不对排班余号做并发控制,就可能出现两个人都预约成功,而余号变成-1的情况。
正确做法有两个层级:
第一层是数据库层面的原子更新。不要在Service里“先查余号,再做判断,再更新”,要先更新判断影响行数。例如:
UPDATE schedule SET remain_number = remain_number - 1 WHERE id = ? AND remain_number > 0这条SQL在数据库层面保证了“扣减只在余号大于0时发生”。如果返回的影响行数为0,说明号已被约满,Service可以直接抛出业务异常。
第二层是事务上的配合。生成预约记录和扣减余号必须在同一个事务里,否则会出现订单建好了、号没扣,或者号扣了、订单没建成的中间状态。在Spring中只需要在Service方法上加@Transactional,注意是org.springframework.transaction.annotation.Transactional。
余号扣减后,建议再补一个唯一约束,例如同一用户同一天同一排班不允许重复预约,数据库层面加唯一索引,双保险。
提示:不要把“查询剩余号”和“扣减号源”拆成两个接口,让移动端先查再提交。中间隔一秒都可能出现超卖,必须由后端接口一次性完成校验和扣减。
3.4 报告查询的权限控制与文件存储
报告查询模块容易做成“所有报告列表都能看到”,这是移动端项目里很严重的越权问题。实现时要注意:
- 查询报告的接口必须从token里取当前用户ID,SQL查询带上
user_id = 当前用户 - 不能提供一个“传什么报告ID就返回什么报告”的下载接口而不校验归属
- 报告文件存储路径建议放在配置文件中,或者使用相对路径保存,不要硬编码成某个绝对路径,否则换一台电脑启动就404
- 如果直接用Spring Boot静态资源映射,要注意放外部目录,并设置合适的资源映射路径
例如在application.yml中配置:
file: upload-dir: ./upload然后写一个映射配置把虚拟路径/report/**映射到本地上传目录。这样报告文件是动态生成、动态读取的,毕设演示时也更方便。
4. 移动端的实现思路:从原型到联调
4.1 页面导航与关键交互
移动端核心页面不要贪多,我建议按这个结构来做:
- 首页:顶部医院Banner、搜索框、快捷入口(预约挂号、报告查询、门诊缴费)
- 科室列表页:按内科、外科等分类展示
- 医生列表页:某一科室下的医生卡片,展示姓名、职称、简介
- 排班页:选医生后,按日期区间展示出诊时段与余号
- 预约确认页:确认就诊人、就诊时间、金额,提交预约
- 我的预约页:展示待支付、待就诊、已完成、已取消等状态列表
- 报告列表页:展示报告标题、状态,点击可查看详情
页面与页面之间是明确的操作流,不要做“所有功能都堆在首页”。这样演示时,鼠标点几次就能讲完整条业务闭环。
4.2 请求封装与Token携带
无论使用Uniapp还是微信小程序,都要封装一个统一的请求对象,避免每个页面重复写token逻辑。以Uniapp为例,大致逻辑是:
const request = (url, method = 'GET', data = {}) => { return new Promise((resolve, reject) => { uni.request({ url: baseUrl + url, method, data, header: { 'Authorization': uni.getStorageSync('token') }, success: (res) => { if (res.data.code === 401) { uni.removeStorageSync('token') uni.navigateTo({ url: '/pages/login/login' }) return } resolve(res.data) }, fail: reject }) }) }这里还要处理一个细节:token过期。直接删掉本地token并跳回登录页,而不是停在当前页面弹一个报错。
4.3 联调中绕不开的跨域、时间格式与图片路径问题
移动端和Spring Boot联调时,最常见的问题有这么几个:
- 跨域:如果浏览器里调试,Spring Boot需要配置CORS。可以在
WebMvcConfigurer里加上addCorsMappings,允许来源、方法、请求头。 - 时间格式:Java返回的
LocalDateTime默认序列化格式可能不是移动端想显示的“yyyy-MM-dd HH:mm”,需要在配置文件或@JsonFormat注解里统一。建议在application.yml里配置jackson日期格式。 - 图片/文件访问:文件上传成功返回的路径要能拼成浏览器可访问的完整URL,比如
http://localhost:8080/report/xxx.pdf。如果只返回相对路径,移动端拼接时容易漏掉端口和前缀。
这些看起来是小问题,但联调时非常浪费时间。提前统一接口文档,规定好字段命名和格式,能省下大量来回沟通。
5. 这些坑我在辅导中反复见到
5.1 事务方法内调用导致锁失效
很多同学写Service时把扣库存写在A方法中,然后同一个类里B方法调用A,以为加了@Transactional就万事大吉。实际上同类内部调用时,事务注解可能不生效。一个明显的现象是“数据库里余号已经扣了,但预约记录没生成,回滚没有生效”。解决方法是把需要事务的调用拆到另一个类中,或通过动态代理自注入调用,最稳妥的其实是把业务流程写在一个公开的Service方法里,保证入口进入。
5.2 文件上传路径写死,换电脑就崩溃
毕设演示经常换电脑。如果配置文件里写的是C:/Users/xxx/upload,换一台笔记本就找不到文件。更可靠的做法是相对项目运行目录存放,并且放到gitignore,同时保证项目启动时会自动创建目录。演示用的报告PDF可以提前放到该目录,演示时不要现场上传模拟,容易浪费时间和出意外。
5.3 只在前端控制权限,后端不校验
有些同学为了省事,在移动端用“判断用户角色,显示隐藏按钮”来限制操作,但后端接口却允许任意用户查询。答辩现场评委可以当场发起一个越权请求,项目直接翻车。所有权限判断必须在后端做,前端隐藏只是体验优化。比如报告查询接口,要从token中解析用户ID,再传递给Mapper,加where user_id = #{userId},而不是把userId作为参数由前端传过来。
5.4 演示时数据库与上传目录没有准备
临近答辩,除了代码,还要准备一份演示环境清单:MySQL建库脚本、初始化数据(科室、医生、排班)、一个测试患者账号、一张待查看的报告PDF。很多同学在答辩现场才开服务器,结果连不上数据库,演示翻车。实际经验是提前录制一个正常流程的演示视频,即使现场出问题也有保底。
6. 答辩讲解策略与项目亮点提炼
6.1 用一条完整流程演示替代功能罗列
答辩时不要“从首页开始一个个点菜单”,评委最想看的是业务闭环。设计一条15分钟的演示路线:用户登录 → 找呼吸内科 → 查看某医生周三上午排班 → 预约并模拟支付 → 在我的预约中看到待就诊 → 进入后台将状态改为已完成并生成报告 → 回到移动端查看报告。整个流程可以把认证、预约、支付、报告四个核心模块全串起来。
6.2 主动讲异常和设计取舍
很多同学喜欢强调“功能全”,但评委更想听到“边界考虑”。主动讲几个异常分支:号源只剩1个时的并发控制、重复点击预约如何防重复、token过期如何重新登录、报告下载越权如何拒绝。这些点是从“能用”到“像认真做的”之间的关键。同样,技术选型也可以主动对比一下为什么用Spring Boot而不是SSM、为什么要令牌认证而不是Session,这比背书效果好很多。
6.3 常见追问与回答方向
整理几个高频问题:
- 为什么移动端要分开设计后端接口?
- 答案是前后端分离,一套接口供多个客户端使用,便于维护。
- 预约挂号时怎么避免超卖?
- 答数据库原子更新 + 事务 + 唯一索引。
- 密码明文保存被质疑怎么办?
- 答BCrypt加密,密码正文存的是不可逆密文。
- 报告文件如果越来越多怎么优化?
- 可以答服务器本地存储只适合开发环境,生产可用对象存储服务或按日期目录分桶,毕业论文里留了改进方向。
7. 做完这个项目后,我更看重的几件事
其实毕业设计写到一定程度,功能难度已经不是最大障碍,最大的障碍是“把业务规则表达清楚”。面对医院用户移动端这种题目,最重要的收获不是学会了Spring Boot注解,而是学会从用户视角拆流程、用状态机管数据、并考虑并发边界。我给准备做类似题目的同学的建议是:先画出完整业务链路,再动手写代码;把接口文档和数据库设计放到代码之前;答辩前反复跑三遍完整流程。这样做出来的Spring Boot医院用户移动端,不管最终功能多还是少,整体会显得非常完整。