
简介本资源为糖尿病患者饮食推荐系统毕业设计完整项目包编号25303面向计算机相关专业进行毕业设计或课程设计的学生以及需要前后端分离实战案例的开发者。项目采用Vue.js3构建管理后台与用户前台后端基于SpringBoot3数据库使用MySQL8实现患者信息与健康状况录入、个性化饮食建议制定、饮食计划查看、每日饮食记录及食谱评价反馈等功能贴合医疗健康与营养学场景。资源包共6个文件包含3个zip源码与返修压缩包、1个MySQL数据库sql脚本、1份需求文档pdf以及1个系统录屏mp4整体约39.22MB结构清晰便于按模块查阅。目前已有130人学习下载。读者可获取完整可运行源码、数据库脚本、需求文档与实操录屏并配合启动教程快速完成环境部署与功能验证适合作为毕业设计参考或二次开发基础。1. 糖尿病饮食推荐系统SpringBoot3 与 Vue.js3 的毕业设计落地拆解很多计算机毕业设计的选题卡在“业务逻辑太薄”和“技术栈太旧”这两个死穴上。糖尿病饮食推荐系统这个题目恰好踩在了一个很实用的交叉点上它既有真实的医学营养业务规则升糖指数、碳水交换份、热量缺口又能把 SpringBoot3 Vue.js3 这套 2025 年依然主流的全栈组合完整跑一遍。我拿到这份源码包的第一反应是——它不是一个只做增删改查的“伪系统”推荐逻辑里塞了可配置的规则引擎前端也做了营养数据的可视化。适合谁适合正在找毕业设计选题的软件工程、计算机专业学生也适合想拿一个完整前后端分离项目练手 SpringBoot3 新特性的开发者。下面我按“能跑起来、能改得动、能讲得清”三个层次把这份资源拆开讲。2. 环境搭建与项目结构把 SpringBoot3 和 Vue.js3 跑在同一台机器上2.1 技术栈版本锁定与依赖关系这份项目用的是 SpringBoot 3.x 搭配 Vue.js 3.x两个“3”不是巧合而是 2025 年毕业设计答辩时比较讨巧的组合。SpringBoot3 强制要求 JDK 17 起步底层从 Java EE 迁移到了 Jakarta EE所有javax.*包名都变成了jakarta.*。这一点在导入老教程代码时会直接翻车——你从网上抄一段javax.servlet.Filter进来编译立刻报错。Vue.js3 这边用的是组合式 APIComposition API配合 Vite 做构建启动速度比 Vue2 webpack 快一个量级。先把版本对齐这是整个项目能不能跑起来的前提组件版本要求说明JDK17 或 21SpringBoot3 最低 17推荐 17 LTSMaven3.8用于后端依赖管理Node.js18 LTS 或 20 LTSVite 5 要求 18MySQL8.0推荐 8.0.33 以上SpringBoot3.x项目核心框架Vue.js3.x前端框架我一般会先确认 JDK 版本因为这是最容易出玄学问题的地方。命令行敲java -version如果显示的是 1.8 或者 11后面 SpringBoot3 启动会直接抛UnsupportedClassVersionError。Node 版本同理低于 18 的话 Vite 会拒绝启动。2.2 后端启动从数据库脚本到接口自测后端跑起来分三步建库、改配置、启动。项目里一般会带一个sql目录里面是建表语句和初始数据。先执行它# 登录 MySQL 并创建数据库 mysql -u root -p -e CREATE DATABASE diabetes_diet DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; # 导入项目自带的初始化脚本 mysql -u root -p diabetes_diet ./sql/init.sql这里有个细节字符集必须用utf8mb4因为食物名称里可能出现生僻字或者特殊符号用utf8会插入失败。导入完成后去改application.ymlspring: datasource: url: jdbc:mysql://localhost:3306/diabetes_diet?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 你的密码 driver-class-name: com.mysql.cj.jdbc.Driver jpa: hibernate: ddl-auto: update # 首次启动可用 update生产环境改 none show-sql: true # 调试期打开能看到生成的 SQLserverTimezone这个参数别省MySQL 8 的时区问题会导致插入的时间字段差 8 小时答辩演示时数据对不上就很尴尬。配置改完在项目根目录执行mvn spring-boot:run看到控制台输出Started Application in x.x seconds就算成功。然后用 Postman 或者浏览器直接访问一个测试接口比如http://localhost:8080/api/food/list能返回 JSON 数据说明后端链路通了。2.3 前端启动与跨域联调前端这边进入frontend或vue目录先装依赖再启动# 安装依赖建议用 npm ci 而不是 npm install锁版本更稳 npm ci # 启动开发服务器 npm run devVite 默认跑在 5173 端口后端在 8080浏览器直接请求会触发跨域。项目里一般有两种处理方式一种是在vite.config.js里配代理另一种是后端加CrossOrigin注解。我倾向于用 Vite 代理因为前端代码里写请求路径时不用带完整域名// vite.config.js export default defineConfig({ server: { proxy: { /api: { target: http://localhost:8080, // 后端地址 changeOrigin: true, // 允许跨域 rewrite: (path) path.replace(/^\/api/, ) // 去掉前缀 } } } })changeOrigin设为 true 是为了让后端收到的请求头里 Host 字段正确有些安全拦截器会校验这个。rewrite那行看后端接口有没有统一前缀如果后端本身就是/api/xxx这行就去掉。联调时打开浏览器 F12看 Network 面板里请求是不是 200如果 401 就去检查登录 token 有没有带上如果 404 就核对路径重写规则。3. 饮食推荐核心逻辑规则引擎怎么算出一份食谱3.1 推荐算法的业务规则拆解这个系统最值钱的部分不是 CRUD而是推荐逻辑。糖尿病饮食推荐不是“随机抽几个菜”它要满足几个硬约束总热量在目标范围内、碳水化合物占比可控、升糖指数GI尽量低、三餐分配合理。项目里通常用一个基于规则的评分模型来实现核心思路是给每个食物打分再按分数排序组合。先看食物表的结构这是推荐的数据基础字段类型用途food_namevarchar食物名称caloriesdecimal每 100g 热量kcalcarbohydratedecimal碳水化合物含量ggi_indexint升糖指数categoryvarchar分类主食/蔬菜/肉类suitabletinyint是否适合糖尿病患者推荐时系统先根据用户的身体数据算出每日所需热量再按比例分配到三餐最后从候选食物里挑出组合。这个“挑”的过程就是算法核心。3.2 推荐服务的代码实现与参数说明下面这段是推荐服务的核心逻辑我按常见实现方式还原出来你可以对照源码里的RecommendService类/** * 根据用户信息生成一日三餐推荐 * param userId 用户ID * return 推荐结果包含三餐食物列表和营养汇总 */ public RecommendResult generateRecommend(Long userId) { // 1. 获取用户身体数据和饮食偏好 UserProfile profile userProfileMapper.selectByUserId(userId); // 2. 计算每日目标热量基础代谢 × 活动系数 // 基础代谢用 Mifflin-St Jeor 公式 double bmr; if (male.equals(profile.getGender())) { bmr 10 * profile.getWeight() 6.25 * profile.getHeight() - 5 * profile.getAge() 5; } else { bmr 10 * profile.getWeight() 6.25 * profile.getHeight() - 5 * profile.getAge() - 161; } // 活动系数久坐1.2轻度1.375中度1.55高强度1.725 double targetCalories bmr * profile.getActivityFactor(); // 3. 按 3:4:3 分配到早中晚三餐 double[] mealCalories { targetCalories * 0.3, targetCalories * 0.4, targetCalories * 0.3 }; // 4. 每餐从候选食物中按评分选组合 ListListFood meals new ArrayList(); for (double mealCal : mealCalories) { ListFood candidates foodMapper.selectSuitableFoods(); // 评分GI 越低分越高碳水占比越合理分越高 candidates.sort((a, b) - Double.compare(score(b), score(a))); meals.add(pickCombination(candidates, mealCal)); } return buildResult(meals, targetCalories); } /** * 食物评分函数GI 权重 0.6碳水合理性权重 0.4 */ private double score(Food food) { double giScore (100 - food.getGiIndex()) / 100.0; // GI 越低越好 double carbRatio food.getCarbohydrate() / food.getCalories(); double carbScore 1 - Math.abs(carbRatio - 0.5); // 碳水占比接近 0.5 最优 return giScore * 0.6 carbScore * 0.4; }这段代码有几个参数你可以按需调整。activityFactor活动系数直接决定总热量如果用户是办公室久坐人群用 1.2 算出来的热量偏低推荐的食物量会少演示时可能显得“不够吃”可以适当调到 1.375。score函数里的权重 0.6 和 0.4 也不是固定的如果答辩老师更看重血糖控制把 GI 权重提到 0.7 甚至 0.8 都合理。pickCombination方法负责在热量约束下做组合常见做法是贪心算法按评分从高到低取食物累加热量直到接近目标值就停。3.3 前端营养数据可视化推荐结果出来之后前端要把它展示清楚。Vue.js3 这边一般用 ECharts 画两个图一个是三餐热量占比的饼图一个是营养素碳水、蛋白质、脂肪的柱状图。ECharts 在 Vue3 里的用法和 Vue2 略有不同需要用ref获取 DOM 容器import * as echarts from echarts import { ref, onMounted } from vue const chartRef ref(null) let chartInstance null onMounted(() { chartInstance echarts.init(chartRef.value) chartInstance.setOption({ title: { text: 三餐热量分布 }, series: [{ type: pie, data: [ { value: 450, name: 早餐 }, { value: 600, name: 午餐 }, { value: 450, name: 晚餐 } ] }] }) })echarts.init必须在 DOM 挂载完成后调用所以放在onMounted里。如果图表容器一开始是隐藏的比如在 tab 页里初始化会拿不到宽高图表显示不出来。解决办法是等 tab 切换时再调chartInstance.resize()或者给容器设一个固定高度。4. 避坑与排查这套源码最容易翻车的五个地方4.1 启动报错jakarta.servlet找不到现象后端启动直接抛ClassNotFoundException: jakarta.servlet.http.HttpServletRequest或者编译期就报包不存在。原因SpringBoot3 把 Servlet API 的包名从javax.servlet换成了jakarta.servlet。如果你从网上抄了一段基于 SpringBoot2 的过滤器或者拦截器代码里面 import 的还是javax.*就会出这个问题。解决全局搜索项目里的javax.servlet全部替换成jakarta.servlet。同时检查pom.xml里有没有混入旧版本的javax.servlet-api依赖有的话删掉SpringBoot3 的 starter 已经内置了正确的版本。4.2 前端请求后端返回 401 但登录明明成功了现象登录接口返回了 token但后续请求全部 401浏览器控制台显示Authorization头没带上。原因前端封装 axios 时拦截器里取 token 的 key 和后端存 token 的 key 不一致。比如登录成功后存的是localStorage.setItem(token, res.data.token)但请求拦截器里读的是localStorage.getItem(access_token)。解决统一 token 的存取 key建议在src/utils/auth.js里封装getToken和setToken两个方法所有地方都调这两个方法不要直接操作 localStorage。另外检查请求拦截器有没有正确拼Bearer前缀后端 JWT 解析时对前缀是敏感的。4.3 推荐结果为空或只有一两个食物现象调用推荐接口返回的列表是空的或者每餐只推了一个食物明显不合理。原因候选食物池太小。selectSuitableFoods查询条件里可能加了suitable 1且gi_index 55这样的限制如果初始化数据里符合条件的数据只有几条组合自然出不来。解决先查数据库确认food表里有多少条suitable 1的记录。如果少于 30 条要么补充初始化数据要么放宽查询条件比如把 GI 阈值从 55 调到 70。另外检查pickCombination里的热量匹配逻辑如果要求“精确等于目标热量”而不是“接近”也会导致组合失败改成允许 ±10% 的误差。4.4 MySQL 8 连接报Public Key Retrieval is not allowed现象后端启动时数据库连接失败报错信息里有Public Key Retrieval is not allowed。原因MySQL 8 默认使用caching_sha2_password认证插件JDBC 连接时如果没有明确允许公钥检索就会拒绝连接。解决在 JDBC URL 后面加上allowPublicKeyRetrievaltrue完整写法是jdbc:mysql://localhost:3306/diabetes_diet?allowPublicKeyRetrievaltrueuseSSLfalseserverTimezoneAsia/Shanghai。注意useSSLfalse在本地开发时加上生产环境不要关。4.5 前端打包后刷新页面 404现象npm run build之后把dist目录部署到 Nginx首页能打开但刷新或者直接访问子路由就 404。原因Vue Router 默认用 history 模式URL 里没有#但 Nginx 不知道要把这些路径都转发到index.html它按静态文件路径去找找不到就 404。解决在 Nginx 配置里加一段try_fileslocation / { root /usr/share/nginx/html; index index.html; try_files $uri $uri/ /index.html; }这行的意思是先找对应的静态文件找不到就找目录再找不到就统一返回index.html把路由交给前端处理。改完nginx -s reload生效。5. 二次开发与答辩加分把推荐逻辑讲成自己的东西5.1 给推荐算法加一个可解释性输出答辩时老师最容易问的一句话是“你这个推荐结果是怎么来的”如果只回答“按规则算的”显得很虚。我建议在推荐结果里加一个reason字段把每个食物被选中的原因写出来。比如// 在 buildResult 里为每个食物附加推荐理由 food.setReason(String.format( GI值%d属于低升糖食物每100g热量%.0fkcal适合当前餐次热量预算, food.getGiIndex(), food.getCalories() ));前端展示时鼠标悬停在食物卡片上就弹出这个理由。这样老师一问你直接指着界面说“系统会解释每个推荐的原因”说服力完全不一样。这个改动工作量很小但答辩效果提升明显。5.2 用 AOP 记录推荐日志做效果验证推荐系统最怕“说不清效果”。你可以加一个切面把每次推荐的入参用户 ID、目标热量和出参推荐的食物 ID 列表、总热量写到日志表里。运行一段时间后用 SQL 统计一下推荐结果的平均热量偏差SELECT AVG(ABS(actual_calories - target_calories) / target_calories) AS avg_deviation FROM recommend_log WHERE create_time DATE_SUB(NOW(), INTERVAL 7 DAY);如果偏差在 10% 以内说明推荐逻辑是稳定的。这个数据可以直接放进答辩 PPT比空口说“推荐效果良好”有分量得多。我一般会把这个查询做成一个简单的统计接口前端加一个“推荐效果”页面用折线图展示最近七天的偏差趋势。5.3 从“能跑”到“能讲”的检查清单最后给你一个答辩前自检的清单按这个顺序过一遍基本不会在现场翻车检查项操作预期结果数据库连接重启后端无报错日志显示 HikariPool 启动登录流程用测试账号登录返回 token跳转到首页推荐接口调用/api/recommend/generate返回三餐食物列表热量在目标 ±10%图表渲染打开推荐结果页饼图和柱状图正常显示跨域前端直接请求后端无 CORS 报错打包部署npm run build后刷新子路由页面正常不 404这份源码包的价值不在于代码有多复杂而在于它把“业务规则 全栈实现 可视化”这条链路走通了。你拿到之后先按第 2 章把环境跑起来再按第 3 章理解推荐逻辑最后用第 5 章的方法加点自己的东西进去。从那以后我每次带毕业设计都要求学生至少加一个“可解释性”功能因为答辩场上能讲清楚“为什么”比“做了什么”更重要。希望帮到你。本文还有配套的精品资源点击获取