
学生做毕设最容易拿到的一种项目就是“医院网站平台”。你去搜Java Web相关资源十个里面起码有两三个是这个题目而它们大多长这样SpringBoot做后端Vue做前端附一份SQL脚本和接口文档标题写着“完整源码可直接运行”。我这两年带毕设经手改过的同类项目不在少数这个题目的框架本身不复杂但它覆盖的知识点确实很全——前后端分离、数据库建模、登录鉴权、接口对接、打包部署基本把Java Web该有的东西都串起来了。这篇文章我就拿这个“SpringBootVue中小型医院网站平台”作为主线把项目边界、环境版本、数据库脚本、后端接口、前端页面、交付答辩这些环节彻底拆开讲一遍。不管你手里是刚拿到的源码还是打算自己从零复刻照着这个思路走都能少踩一半的坑。一个医院网站听起来好像很大但题目里加了个关键定语——“中小型”。这意味着你不用去做挂号平台那种级别的线上支付、号源池管理、多院区调度你只需要把患者从“浏览科室”到“完成预约挂号”这条核心动线走通再给管理员留一套维护数据的后台项目就已经站得住了。患者侧流程访客打开网站看医院介绍和科室列表注册登录后浏览医生信息查看医生最近几天的排班号源选一个时间段提交挂号之后可以在“我的挂号”里看到记录和状态。管理侧流程管理员登录后台维护科室信息维护每个科室下的医生给医生配置每周的排班和号源数量同时还能管理公告资讯、查看用户挂号列表。系统非功能需求中小型医院不需要微服务一个单体SpringBoot应用加一个MySQL库就足够挂号这种操作需要保证并发安全但也不需要引入Redis分布式锁数据库层面控制就行。这套业务边界之所以适合毕设是因为每个模块都可以单独拎出来写进论文数据库设计有得写接口设计有得写前端交互有得写甚至如果你愿意还能往里面加数据可视化的统计页面。但它又不至于大到让你在答辩前一个月还在赶工是一个典型的“跳一跳就够得着”的选题。再聊聊技术选型。SpringBoot为什么是Java Web毕设的默认选择因为它把Spring家族的配置工作量消掉了很大一部分——内嵌Tomcat、自动配置、Maven一键管理依赖最终打出来一个jar包就能跑。Vue这边则是当前前端生态最成熟的方向之一组件化开发让页面维护变得清晰而且前后端分离的模式本身就是现在企业里最常见的协作方式。你把这个组合写完并讲清楚面试官或答辩老师也没什么好挑的。拿到一个项目第一件事永远是跑起来而不是看代码。但很多同学卡就卡在环境上——版本对不上一启动就是一片红。JDK、Maven、Node.js的版本牵连我先给一套我自己用着最顺手的版本组合这套组合匹配度极高网上遇到问题也最容易搜到答案组件推荐版本说明JDK1.8Java Web毕设绝大多数代码基于JDK8兼容性最好Maven3.6.3配合JDK8稳定换新版Maven有时候会有奇怪问题SpringBoot2.7.x2.7是最后一个支持JDK8的大版本非常稳MySQL5.7 或 8.0两个版本都行注意驱动和时区配置的区别Node.js14.x 或 16.x老项目Vue2配Node14Vue3Vite建议Node16以上Vue2.x Element UI中小型医院管理后台生态最成熟的组合这里有个很常见的坑SpringBoot版本选太高。很多人拉源码的时候不去看pom.xml直接下了个3.x的SpringBoot然后用JDK8去编译启动直接报错。SpringBoot 3.0开始强制要求JDK17不是换成SpringBoot2.7.18就是老老实实装JDK17。但坦白讲毕设项目用SpringBoot 2.7.x就够了网上资料多遇到问题好查老师的电脑环境也大概率是JDK8。Maven还有一个细节国内拉依赖建议配阿里云镜像否则第一次加载SpringBoot依赖能等到你怀疑人生。在Maven的settings.xml里加上阿里云的mirror十分钟拉完不配置的话可能半小时起步。后端工程骨架和前端目录约定后端解压后你会看到典型的Maven结构src/main/java下面分controller、service、mapper、entity、config、common这些包src/main/resources下面放application.yml、mapper的XML文件。启动入口是带SpringBootApplication注解的类运行main方法就能起服务。这个是SpringBoot的基本盘按着这个结构往下走就不会乱。前端如果是Vue2项目核心在src目录下api目录集中放所有请求接口router目录管理路由store或vuex管理登录状态views目录按业务放页面组件——home、dept、doctor、user、admin这些文件夹一摆出来整个项目的页面规模就清楚了。拿到源码先别急着看每个页面怎么写的先把目录结构和路由配置读一遍你就能知道这个项目有哪些页面、分别对应哪些接口。数据库脚本是整个项目里最“承重”的文件。很多同学拿到SQL就无脑执行执行完才发现表对不上、数据不全、代码跑不通。一个合格的医院网站SQL脚本至少要让这个系统的数据闭环能转起来。表结构清单与关系梳理以这套项目为例核心表大概有这六张sys_user用户表字段包含id、username、password、nickname、phone、roleROLE_USER或ROLE_ADMIN、create_time。管理员和普通患者都存在这张表里用角色字段区分。department科室表字段包含id、name、description、create_time。医院网站的门面科室入口全靠它。doctor医生表字段包含id、user_id关联登录账号、department_id关联科室、title主治/副主任/主任医师、intro、avatar。schedule排班表字段包含id、doctor_id、work_date、time_slot上午/下午、total_count总号源、remain_count剩余号源。registration_record挂号记录表字段包含id、user_id、doctor_id、department_id、schedule_id、register_date、status待就诊/已完成/已取消。notice公告表字段包含id、title、content、create_time用来做首页的公告资讯。这些表的关系非常直白科室与医生是一对多医生与排班是一对多排班与挂号记录是一对多用户与挂号记录是一对多。有这些关系你就能在论文里画出一张标准的ER图评审老师一看就知道你数据库设计的基本功是有的。初始化数据里藏着的门道SQL脚本除了建表还必须要带上初始化数据这是一个很多源码包做得差劲、但你绝不能学的地方。初始化数据至少要有一个管理员账号比如admin/123456。密码不能是明文老手都会用BCrypt加密后的密文直接写入SQL。五到八个科室内科、外科、儿科、妇产科、口腔科、眼科每个科室配上一两句简介首页展示才好看。每个科室配一到两名医生医生的avatar可以放一个本地默认头像不要依赖外链图片否则离线跑项目头像全挂。未来七到十四天的排班数据。这一步特别关键——如果排班表是空的你注册完账号想演示挂号流程就会发现根本没有号源可选整个演示直接尬住。手动往schedule表插数据是很麻烦的事好的SQL脚本会把最近一周每天每个医生上下各几个号源一次性初始化好。建表时的常见坑这里说几个我改代码时经常遇见的低级错误你们对照检查自己的SQL脚本。一是字段命名。Java实体里习惯用驼峰数据库里习惯用下划线department_id映射到departmentId这是MyBatis-Plus默认帮我们处理的事情但前提是配置文件里要开启map-underscore-to-camel-case或者使用MyBatis-Plus时保持默认开启。如果关了你的查询结果就会一堆字段为null程序跑起来全是空的。二是物理外键。很多同学为了让ER图好看在表上加一堆FOREIGN KEY结果删个科室就得先删医生、删排班后端代码里维护数据顺序一变就开始报错。数据库表用逻辑外键完全足够关联关系写到SQL查询的JOIN条件里维护起来比物理外键舒服太多。三是时间字段。日期统一用datetime不要为了省事用int存时间戳也不要用timestamp——不是不能用配合MySQL8和不同时区设置容易出幺蛾子。时间字段的默认值能写CURRENT_TIMESTAMP就一定要写这样插入数据不用手动set创建时间。后端代码看着文件很多但真正核心的只有三块统一返回体、登录鉴权、挂号落库。把这三条链路看明白整个后端你就通了。统一返回体与全局异常处理前后端分离的项目接口返回必须有一个约定俗成的格式。这套项目的common包下一般会有一个Result类长这样public class ResultT { private Integer code; private String message; private T data; public static T ResultT success(T data) { ResultT result new Result(); result.setCode(200); result.setMessage(操作成功); result.setData(data); return result; } public static T ResultT error(Integer code, String message) { ResultT result new Result(); result.setCode(code); result.setMessage(message); return result; } }前端axios拦截器只看code200就进then回调并把data解出来401就清除登录状态跳回登录页其他code统一弹出message。这个约定一旦建立前后端联调效率会高很多。配合RestControllerAdvice做全局异常处理业务代码里不需要到处try-catch抛出去的业务异常会自动被转成Result.error返回代码干净得多。JWT登录流程与请求拦截登录模块是医院网站的第一道门。密码存的是BCrypt密文用户登录时后端用BCrypt的matches方法校验明文和密文是否匹配校验通过后生成一个JWT token串返回给前端。JWT的好处是服务器不用保存会话token里就带着用户id和角色信息前端请求时在请求头里带上Authorization: Bearer xxx后端拦截器统一解析。拦截器设计上要有一份白名单像登录注册接口、首页科室列表、医生列表、排班列表这些游客也能访问的接口放行其余的请求在拦截器里校验token。代码实现不复杂但你要注意token过期时间的设置一般给24小时对演示型项目来说完全够用。挂号链路的核心实现思路挂号这个动作涉及三张表排班表、挂号记录表、用户表。一次挂号要做的事在service层的可能是这样Transactional public RegistrationRecord register(RegisterRequest request) { Schedule schedule scheduleMapper.selectById(request.getScheduleId()); if (schedule.getRemainCount() 0) { throw new BusinessException(500, 当前排班已约满); } int updated scheduleMapper.decreaseRemainCount(request.getScheduleId()); // where remain_count 0 if (updated 0) { throw new BusinessException(500, 当前排班已约满); } RegistrationRecord record new RegistrationRecord(); // 组装用户id、医生id、科室id、排班id、挂号日期和状态 registrationRecordMapper.insert(record); return record; }这里最关键的一步是扣减号源SQL语句一定是update schedule set remain_count remain_count - 1 where id ? and remain_count 0然后用返回的影响行数判断是否成功。如果你先select查一下剩余数量、再update直接减一两个人同时挂号就会超卖虽然毕设答辩不会真的压并发但面试官问起来你马上露馅。取消挂号则是逆向操作把挂号记录状态改为已取消同时把排班表对应的remain_count加一。这块逻辑里有两个细节第一是判断挂号记录是否属于当前登录用户防止越权操作别人的挂号第二是已经“已完成”的挂号不允许取消这些都属于业务边界条件在代码里都要判断到。前端是整个项目里最容易让人半途放弃的部分尤其是没怎么写过Vue的同学。其实医院网站这种页面80%都是列表加表单套路非常固定。路由设计、导航守卫与Axios封装Vue项目的router目录里一般会配置一张路由表。常用的做法是分成两类路由不需要登录的如首页、科室列表、医生详情、医院公告需要登录的如个人中心、我的挂号还有管理员专属的如后台管理相关页面。导航守卫设置在router.beforeEach里判断目标路由的meta.requiresAuth字段如果没登录就调this.$router.push去登录页这个套路写上去答辩问“你的路由守卫怎么实现的”你就可以答得头头是道。Axios封装也是必做的一步。在utils目录下的request.js里创建axios实例然后设置baseURL、超时时间、请求拦截器加token、响应拦截器处理code。好处是页面里的请求代码变得很短比如这样export function getDoctorList(deptId) { return request({ url: /doctor/list, method: get, params: { deptId } }) }页面里调用的时候只需要this.$api.getDoctorList(1).then(res { ... })就行所有的通用逻辑都已经收口到封装层了。核心页面医生列表、排班和挂号弹窗首页没什么好说的就是医院简介、科室轮播图加公告列表。科室页面调一个接口拿到全部分科渲染成卡片。医生列表页面要支持按科室筛选所以参数里带着deptId后端返回这个科室下的医生数组。真正有交互复杂度的页面是医生详情页。这个页面一般展示医生信息卡下面是一张未来几天的排班表排班表的单元格里显示上午/下午的剩余号源有号源显示“可预约”满了显示“已满”点击可预约的格子弹出一个确认对话框显示医生、日期、时间段、挂号费确认后就调用提交挂号接口。这一套交互逻辑做完你基本上就把Vue的组件事件、动态样式、弹窗交互全用了一遍比网上那些只写CRUD的模板项目有内容得多。开发期跨域、生产期打包放进SpringBoot开发的时候前后端分离跑前端端口是8081后端是8080跨域问题逃不掉。最省心的办法是在vue.config.js里配proxy代理module.exports { devServer: { port: 8081, proxy: { /api: { target: http://localhost:8080, changeOrigin: true, pathRewrite: { ^/api: } } } } }这样前端代码里请求都写/api开头开发期由Node代理转发到后端浏览器层面没有跨域就不需要后端额外开启CORS。但到了演示和交付阶段绝大多数同学会选择把前端打进SpringBoot里做成一个独立jar跑起来。做法是前端执行npm run build生成dist目录然后把dist里的所有文件复制到SpringBoot项目的src/main/resources/static目录下重新打包。启动后直接访问http://localhost:8080就是完整网站不用再单独启动前端演示环境清爽很多。这里有一个很经典的坑如果你用了Vue Router的history模式打包部署后直接访问某些子路由会出现404因为刷新时是由SpringBoot返回页面的而SpringBoot没有对应的路由映射。两个解决办法要么后端加一个forward把非接口路径都转发到index.html要么干脆用默认的hash模式路径里带#号虽然不太美观但对毕设来说省事可靠我一般都建议学生用hash模式保平安。接口文档是这个项目交付里最容易被低估的部分但恰恰是答辩老师翻开率最高的东西。你源码能跑、演示流畅可如果接口文档一塌糊涂老师会立刻怀疑整个项目是不是网上找的。所以这一步必须认真对待。接口文档到底该写哪些内容一份合格的接口文档不是把后端代码抄一遍。它应该有三部分全局说明接口的基础地址、请求头里token怎么带、统一的返回码什么意思。接口明细每个接口的请求方式、路径、请求参数名称、类型、是否必填、说明、返回结果示例。特殊说明比如挂号接口的并发控制逻辑取消挂号的状态限制条件。以挂号接口举例文档里应该写着POST /registration请求体包含scheduleId响应体返回一个挂号记录对象。然后再补一句备注说明如果号源已满返回code500以及提示文案。这些信息是给前后端联调用的也是给答辩老师看的。Knife4j自动生成与手动文档的取舍如果你的项目里整合了SpringFox或Knife4j那启动项目后访问/doc.html就能看到一个自动生成的接口调试页面既能看文档又能直接调试答辩演示效果相当加分。引入方式也不复杂pom.xml里加一个knife4j的依赖然后在配置类里加EnableKnife4j和相关配置类注解Controller里再用Api和ApiOperation写一下说明接口页面就出来了。但自动生成文档有一个问题它只能反映接口签名没法把你的业务约束写清楚。所以我通常建议学生做两手准备——后端用Knife4j跑起来给答辩现场看同时另外整理一份简明的Markdown接口文档放进交付目录里标明接口列表、核心模块的调用流程。这样无论是老师要看代码还是面试官想了解你的项目你都有拿得出手的东西。演示环境准备与答辩常见问题离答辩还有三天的时候就别再往项目里加功能了老老实实做演示的彩排。具体操作是在一台干净的电脑上装好JDK8、MySQL5.7或8.0、Maven、Node14然后把SQL脚本导入数据库后端用IDEA或命令行启动前端进入项目目录跑npm run dev。最好把命令都写成一篇启动文档包括数据库账号密码、配置文件里的MySQL连接信息、启动顺序。因为你答辩当天很可能是拿老师的电脑或者自己的电脑现场演示环境稍微不一样就启动失败场面会非常尴尬。答辩现场的高频问题我也提前帮你们列一下数据库表为什么不用外键——用逻辑外键控制方便维护和测试数据。JWT和传统Session有什么区别——JWT无状态适合前后端分离和分布式部署。挂号并发怎么处理——更新时判断remain_count大于0利用数据库行锁保证不超卖。密码为什么不存明文——BCrypt加盐哈希数据库泄露也不会直接暴露密码。前端路由守卫有什么用——控制未登录用户不能访问个人中心管理员页面还要加角色判断。这些问题都是围绕项目里的实际代码展开的你只要真正跑通过、理解过都能答上来。怕的就是拿了个源码但完全没看过被一问就卡壳。我个人的习惯是拿到这类项目源码第一件事不是急着改代码而是先整理出一张模块清单和一个启动手册把能跑通这件事放在最前面。跑通之后再去读最核心的认证和挂号代码读懂了再花时间对着自己的理解把接口文档补全。这一套流程走下来你收获的绝不是一个可以交差的毕设而是一个你能讲清每一行代码的真实项目经验。最后再分享一个实用小技巧如果答辩演示时担心网络环境不好、外部CDN加载慢导致前端样式错乱直接把node_modules里用到的Element UI相关资源在build时打进dist或者关闭开发环境的CDN引用改成本地依赖。demo之前自己先跑一遍把出现过的每一个报错都记录下来这比什么都管用。祝你们都能顺顺利利跑起来漂漂亮亮答完辩。