SpringBoot+Vue智能推荐卫生健康系统:毕设全栈实战解析
发布时间:2026/10/22 8:44:23 锦皓数字建站

每年毕业季我都能看到大量Java Web选题十个里有八九个是图书管理系统、学生选课系统。但像“SpringBootVue 智能推荐卫生健康系统平台”这种题才是真正能拉开分数差距的。它同时踩中了前后端分离、推荐算法、数据库设计、接口文档这几个核心考核点又披着“卫生健康”这个热门场景做出来放进简历里也不掉价。适合正在发愁毕设选题或者想练手完整全栈项目、又不想做烂大街管理系统的同学。下面这篇就按我实际操盘过的项目经验把这个系统的整体思路、技术选型、数据库设计、代码落地细节和踩坑记录完整说一遍。1. 项目整体定位与技术选型为什么这套组合能打1.1 这个项目到底在解决什么问题很多同学看到“智能推荐”就开始慌以为要上机器学习模型。其实把这个题目拆开看需求非常清晰一方面用户打开健康资讯网站内容太多太杂不知道看什么另一方面系统里积累了用户健康档案比如过敏史、慢性病、运动习惯、饮食偏好这些信息天然可以用来做个性化推荐。所以项目的核心不是训练模型而是把“用户画像 内容标签 推荐规则”这三层东西做扎实。用户角色也要分清。平台不只有一个面向用户的健康资讯门户还必须有后台管理端否则管理员没有地方去维护资讯、审核用户档案。有的题目还会带医护端医生或健康管理师可以给用户推送建议、制定计划。三端放到一起才算是一个完整的业务闭环。拿这个项目做毕设前期最重要的就是把这些角色和权限分清楚把数据流捋明白代码反而是后面的事。1.2 技术选型的四个关键理由后端我推荐SpringBoot不是因为它最新而是因为它在校招和毕设场景里最稳妥。相比SSM那一套繁琐的XML配置SpringBoot的自动配置和起步依赖能让你把精力放在业务逻辑上而不是整天改配置文件相比SpringCloud那套微服务全家桶它又足够轻量一台电脑就能跑得起。SpringBoot本身自带Tomcat、内置事务管理配合MyBatis-Plus操作数据库写增删改查可以节省大量时间。前端选Vue是因为社区生态成熟、中文资料多。Vue3加Element Plus做后台管理页面几乎可以“抄着写”。前后端分离之后前端负责页面渲染和交互后端只暴露JSON接口两边可以并行开发项目的工程化程度看起来也更高答辩老师一眼就知道你掌握的不只是“往JSP里塞Java代码”。数据库用MySQL缓存加Redis。Redis在毕设里不是为了炫技而是实打实有需求首页推荐结果要缓存不让每次请求都去跑一遍推荐计算热门资讯和用户token也适合放Redis。用Redis还有一个好处可以在技术亮点里写“热点数据缓存”和“单点登录状态管理”这也是加分项。和传统JSP项目相比这套技术栈真正拉开了差距的地方在于它把“数据”和“展示”解耦了。前端一个Vue项目后端一个SpringBoot项目中间用HTTP接口通信代码结构更清晰出错也好定位。对于一个以“工程能力”为考核重点的毕设来说这才是最被看重的东西。1.3 推荐引擎的算法定位别自己吓自己“智能推荐”这四个字是很多人选题时犹豫的原因。但真实项目里如果用户行为数据只有几千条强行上一个深度推荐模型效果不一定比简单规则好训练和调参反而会花掉大量时间。这个项目里我采用的思路是标签匹配为基座协同过滤做补充规则过滤来兜底。第一步给每篇健康资讯打标签比如高血压、糖尿病、运动康复、营养膳食、心理健康。用户注册和填写健康档案时也让他们勾选关注领域。推荐时先做标签召回选出用户感兴趣的类目。第二步如果用户收藏过文章、浏览过文章就把他看过的文章相似文章找出来再结合用户之间的相似行为做协同过滤。第三步过滤掉不适宜的内容比如过敏、禁忌、低龄不宜等。这套组合方式不需要GPU、不需要Pandas训练模型用纯Java和SQL就能跑足够应付卫生健康场景的推荐需求。2. 功能模块拆解与数据库设计从业务到SQL脚本2.1 三端核心业务线怎么划分我一般会把功能拆成“用户端、管理端、医护端”三条线。用户端是前台门户包含注册登录、完善健康档案、浏览健康资讯、查看智能推荐、收藏文章、接收健康建议。管理端负责系统基础配置包括用户管理、资讯发布与审核、标签分类管理、推荐参数设置、数据统计。医护端面向健康管理师或者医生功能包括查询用户健康档案、进行档案审核、为用户制定运动或饮食计划、推送健康提醒。这样划分有一个好处权限清楚。普通用户只能访问用户端接口管理员能进管理后台医护端需要单独的权限标识。实现起来可以用Spring Security加JWT也可以自己写一个拦截器加角色判断。毕设阶段没必要把权限做得太复杂但一定要让别人一眼看出三端的边界。2.2 核心表结构设计六张表撑起整个系统表的设计直接决定你后面写代码的舒服程度。我常用的核心表大概有六到八张这里挑几张开出来说users用户表主要字段包括user_id、username、password、nickname、phone、avatar、role、status、create_time。password字段需要加密存储不要存明文role就是user、admin、doctor这三种角色。health_profile健康档案表是推荐引擎的数据基础字段有profile_id、user_id、height、weight、age、gender、allergy_info、chronic_disease、exercise_habit、dietary_preference、interested_tags、update_time。interested_tags可以用逗号分隔的字符串存简单直接。article_category资讯分类表和health_article健康资讯表是内容模型。health_article里应该有title、summary、content、cover_image、category_id、tags、view_count、favorite_count、status、publish_time。status字段用来控制是草稿、发布中还是已下线。user_favorite收藏表记录user_id和article_id、create_time再加一个唯一索引防止重复收藏。recommend_record推荐记录表用于保存推荐结果字段有record_id、user_id、article_id、recommend_type、score、create_time。这个表在答辩时很管用直接展示“用户点开首页后系统生成了哪几条推荐、推荐依据是什么”比嘴上说算法效果强太多。doctor_advice健康建议表包含advice_id、user_id、doctor_id、content、type、create_time。医护端写给用户的建议和计划都放这里。针对SQL脚本需要注意三个点。第一字符集统一用utf8mb4不然中文和emoji都可能乱码。第二表与表之间尽量不要用物理外键约束用Java代码维护逻辑关系否则以后初始化数据、删除数据时会被外键折腾得很痛苦。第三时间字段建议用datetime类型默认值写成CURRENT_TIMESTAMP这样插入数据时不用手动填创建时间。2.3 SQL脚本编写要点与初始化数据SQL脚本不只是把表建出来它还要让项目一跑起来就能看效果。最忌的就是把表建完里面一条数据都没有前端页面打开全是空白。我的习惯是准备一套初始化数据1个管理员账号、2个医护账号、5个测试用户、5个资讯分类、每个分类下至少10篇文章、每个测试用户都有一条健康档案和几条收藏记录。这套数据量不大但已经足够把推荐流程跑通。脚本顺序也很重要先建库再建表再插入基础数据。如果你贴出来的源码只有一个建表脚本没有初始化数据答辩演示时临时造数据会非常尴尬。还有一个小技巧在脚本顶部加入DROP TABLE IF EXISTS方便重复执行。提交源码包时SQL脚本要同时带创建脚本和初始化数据脚本并在接口文档里写清楚MySQL版本和导入方式。3. 前端后端实操落地代码层面的关键细节3.1 环境搭配和项目目录结构这个项目的最佳上手配置是JDK 1.8或JDK 17SpringBoot 2.7.xMySQL 8.0Redis 6.xNode 16以上Maven 3.6以上。SpringBoot选2.7而不是3.x是因为很多网上的现成工具类和MyBatis-Plus整合方案都是基于2.7的踩坑成本低。JDK1.8配SpringBoot2.7这一套几乎兼容所有毕设场景。后端项目我习惯这样分包config放配置类包括跨域配置、Redis配置、拦截器注册controller接口层service / service.impl业务逻辑mapperMyBatis-Plus的Mapper接口entity数据库表对应的实体类common统一返回结果、异常处理、常量类utilsJWT工具、加密工具等recommend推荐算法相关逻辑单独抽出来别混在业务代码里前端Vue3项目用Vite构建工具建项目src目录下分api、router、store、views、components这几个目录。views里再按用户端、管理端、医护端建子目录。这样评阅老师看代码时能快速找到对应模块。3.2 推荐算法集成的两种主流写法这里给出一个可以直接落地的推荐逻辑伪代码思路。第一种是基于标签召回的推荐这也是最简单、最容易讲明白的public ListArticleVO recommendByTags(Long userId) { // 1. 从health_profile里拿到用户关注的tags String interestedTags healthProfileMapper.selectByUserId(userId).getInterestedTags(); ListString tagList Arrays.asList(interestedTags.split(,)); // 2. 查询包含任一标签的文章按浏览量排序 QueryWrapperHealthArticle wrapper new QueryWrapper(); wrapper.inSql(tags, String.join(,, tagList)) .eq(status, 1) .orderByDesc(view_count); // 3. 限制返回20条 wrapper.last(limit 20); return articleService.list(wrapper); }注意如果只是用inSql拼接标签容易造成索引失效稳妥做法是提前按标签表关联。但毕设演示这样写也能跑通重点是逻辑能自洽。第二种是基于用户协同过滤的推荐稍微进阶一点。做法是找到当前用户浏览或收藏过的文章ID再找也浏览过这些文章的其他用户把他们收藏过但当前用户没看过的高分文章推荐出来。用SQL表示会更直观SELECT DISTINCT a.id FROM user_favorite uf1 JOIN user_favorite uf2 ON uf1.article_id uf2.article_id JOIN health_article a ON a.id uf2.article_id WHERE uf1.user_id #{currentUserId} AND uf2.user_id ! #{currentUserId} AND a.id NOT IN ( SELECT article_id FROM user_favorite WHERE user_id #{currentUserId} ) ORDER BY a.favorite_count DESC LIMIT 10;这种写法展示起来很有说服力。答辩的时候老师问“你的协同过滤怎么做的”你就可以回答通过用户对文章的收藏行为计算物品之间的相似度从而推荐给有相似行为的用户。还要提醒一句推荐模块一定要做缓存。推荐结果加上Redis缓存key可以设计成recommend:user:{userId}TTL设置为30分钟。用户可以手动点击“刷新推荐”后台清空对应缓存重新生成。这样既照顾了性能又能体现你对缓存的理解。3.3 接口文档设计与权限保护接口文档不能等到写完代码再补而是应该在设计数据库后、写Controller之前就把接口清单列出来。这个项目的核心接口大致有模块请求方式路径说明用户POST/api/user/login登录并返回token用户POST/api/user/register注册账号档案GET/api/profile/get获取当前用户健康档案档案POST/api/profile/update新增或修改健康档案资讯GET/api/article/page分页查询资讯推荐GET/api/recommend/list获取智能推荐列表收藏POST/api/favorite/add收藏文章收藏DELETE/api/favorite/remove取消收藏管理POST/api/admin/article/publish发布资讯医护POST/api/doctor/advice/add给用户写健康建议统计GET/api/admin/stats/overview首页数据统计权限保护用JWT最简单。用户登录成功之后后端生成一个token返回给前端前端存在localStorage里。每次请求时在请求头带上Authorization字段。后端写一个拦截器校验token是否存在以及是否过期然后把用户ID存到ThreadLocal里供Controller使用。以下是一个简化版拦截器逻辑public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (OPTIONS.equals(request.getMethod())) { return true; } String auth request.getHeader(Authorization); if (StringUtils.isEmpty(auth) || !auth.startsWith(Bearer )) { throw new ApiException(未登录或token已失效); } String token auth.substring(7); Long userId JwtUtils.parseToken(token); UserContext.set(userId); return true; }接口文档我建议直接用Swagger注解同时导出一份Markdown或PDF版本放在项目doc目录下。在接口文档里要写清参数类型、是否必填和返回示例。不要只贴一个在线Swagger地址因为答辩现场可能没有网络。3.4 Vue3前端页面与接口对接管理的后台页面尽量少写原生CSS直接依赖Element Plus组件库四五个页面能很快搭完。前端的核心不只是“把界面画出来”更重要的是封装请求和路由权限。所有请求都走一个二次封装的Axios实例。baseURL设置为/api请求拦截器统一把token加到请求头响应拦截器判断HTTP状态码和业务状态码。遇到401就跳到登录页并清空本地缓存。这样每个页面写请求时不用反复处理异常和token。Vue Router里可以加一个全局前置守卫没有token就跳到登录页有token才能进入详细页面。三端的路由分别放在不同目录下管理端路由和用户端路由分开避免代码混在一起。这里有一个特别容易踩的坑前端启动时和后端联调跨域报错。开发环境最简单的方式不是在后端写一堆CORS配置而是在Vite的vite.config.js里配置代理server: { port: 5173, proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } }这样前端页面访问的地址是localhost:5173/api/xxxVite在本地将请求转发给localhost:8080完全避开跨域问题。很多同学一上来就在后端写CrossOrigin结果又碰到预请求、token失效一堆问题换成代理之后清净很多。4. 一套完整的踩坑实录从开发到部署会遇到什么4.1 数据库时区、中文编码和连接参数MySQL8和MySQL5的驱动写法不一样。用MySQL8时JDBC连接串里建议写成spring.datasource.urljdbc:mysql://localhost:3306/health_db?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrueserverTimezone这行很关键。不写的话有些机器会报“The server time zone value”的错。allowPublicKeyRetrieval也需要加上不然MySQL8的驱动会默认拒绝连接。建库时也要检查数据库字符集如果建库时选错了字符集后面代码里怎么设置编码都白搭。4.2 跨域、会话管理与刷新404项目做到一半最烦的问题就是“明明登录成功了刷新页面又回到登录页”。很多同学把user信息放在sessionStorage但是刷新后前端路由重新加载sessionStorage确实还在问题往往出在后端接口返回的token没有存好。我的建议是token放在localStorage刷新不会丢。还有一类问题是部署时前端路由刷新404。这是因为后端服务器没有默认到前端路由。可以用Nginx配置解决location / { root /usr/share/nginx/html; index index.html; try_files $uri $uri/ /index.html; }try_files这条语句的作用是如果找不到对应文件就回退到index.html由前端路由自己处理。没有这一句刷新一个二级页面就会直接白屏。4.3 推荐冷启动与热门效应推荐系统最常见的问题是冷启动。新用户没有收藏行为、没有浏览记录协同过滤根本没法算。我的处理方案是对未登录或者行为数据少于3条的用户直接推荐“健康科普”和“营养膳食”分类下浏览量最高的文章同时在首页单独放一个“热门推荐”栏目。等用户有了几条浏览/收藏记录后再切换成个性化推荐。另一个问题是热门效应。如果推荐列表里永远都是那几篇高浏览文章个性化就名存实亡了。我一般会在推荐结果里加一个干扰因子比如评分计算公式里加上随机权重double score viewCount * 0.6 favoriteCount * 1.2 new Random().nextInt(10) * 0.1;这样排名不会完全被热门文章卡死新文章也有机会被推荐出去。4.4 前后端打包与部署上线开发完以后部署环节也是加分项。后端在项目根目录执行mvn clean package -DskipTests生成jar包后用java -jar运行。服务器有内存限制的话可以把JVM参数调小一点java -Xms256m -Xmx512m -jar health-recommend.jar前端打包npm run build打包完成后会在dist目录生成静态文件。把dist目录里的内容放到Nginx的html目录下再把后端jar跑起来用Nginx代理转发/api请求到SpringBoot的8080端口。整个部署过程大概半小时就能搞定。如果在答辩时需要现场演示直接跑在本机也行把两个终端窗口打开一个跑后端一个跑前端提前把服务和页面都启动好。4.5 答辩前的功能自查清单离答辩还剩下两三天的时候按这个清单过一遍用户注册、登录、退出是否正常密码是否加密存储健康档案能否正常保存并回显管理员能否发布资讯、下架资讯推荐列表在实际登录后是否发生变化收藏、取消收藏之后前端列表是否同步医护端能否给指定用户写入建议刷新页面是否出现404或登录态丢失数据库中是否有足够多的初始化数据支撑演示接口文档和项目说明文档是否放在doc目录把项目跑起来的步骤是否写在README里其中第八条特别重要。我见过太多项目演示到一半因为数据库里只有三条测试数据点开页面空荡荡最后只能当场口述“功能已经写完”。与其那样不如花一下午把模拟数据造得丰富一点。系统里如果有几十篇文章、十来个测试账号演示效果会完全不同。这个项目真正难的地方不是技术而是把“推荐”这个亮点讲清楚。我在实际操盘过程中最大的体会是别把推荐算法想得有多高大上把用户标签、行为记录、内容分类这些基础数据整理好用最简单的规则也能做出像样的“智能推荐”。经历过一次完整的前后端分离开发你再去写其他Java Web项目很多坑就都能提前避开了。最后再分享一个小技巧如果你打算把项目源码写到简历里不要只写“完成健康资讯管理”要写“基于SpringBootVue构建健康内容平台结合用户健康档案标签召回与协同过滤实现个性化推荐并通过Redis缓存推荐结果”。一句话就把技术栈、业务场景、算法思路和性能优化全带出来了。这套项目的价值也就在这里。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。