ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

SpringBoot+Vue老年人膳食营养系统毕设:数据库设计、前后端联调与部署全解析

SpringBoot+Vue老年人膳食营养系统毕设:数据库设计、前后端联调与部署全解析 手上正拿着一张“基于SpringBootVue的老年人膳食营养服务网站管理系统”毕设题目的同学应该不在少数。这个题目听起来不复杂但真正动手之后才会发现从数据库怎么建表、后端接口怎么分层到Vue页面怎么把数据渲染出来再到最后答辩怎么把整个业务闭环讲清楚每一步都有不少细节。我这两年帮人调试过不少同类项目见过一上来就把环境搞到崩溃的也见过答辩前一周推荐功能还跑不通的。这篇就把我拆解这个系统的完整思路、核心实现和踩坑记录整理出来按实际开发的顺序讲希望能让做这个题目的同学少走点弯路。它适合正在为这个毕设发愁的人也适合想借着这个项目把SpringBootVue前后端分离开发完整过一遍的初学者。1. 项目背后的真实需求与整体设计拆解1.1 为什么“老年人膳食营养”是一个好选题很多人选毕设题目的时候第一反应是做个“XX管理系统”比如图书馆管理系统、学生管理系统。这类题目不是不能做但答辩时很难讲出亮点因为业务逻辑太单薄无非是增删改查换了一层皮。而“老年人膳食营养服务网站管理系统”这个选题天然具备几个优势对毕设来说非常重要。第一业务场景好理解。营养膳食、健康管理这些事情不需要给答辩老师科普三分钟业务背景一眼就能看懂系统在做什么。老师听得懂自然就容易认可。第二业务闭环完整。系统涵盖了用户注册登录、健康档案维护、食谱推荐、一周膳食计划、饮食打卡记录、后台数据管理这一整条链路从用户进来到产生数据再到管理员管理数据逻辑是连贯的。论文写起来也顺手因为系统设计、数据库设计、功能实现、系统测试这些章节都有现成的素材可以组织。第三容易包装创新点。同样是增删改查如果加上“根据老人慢性病标签过滤不适合的食谱”这样的推荐逻辑即使实现很简单答辩时也可以理直气壮地说这是一个“带有健康约束条件的膳食推荐系统”。这个点非常讨巧技术上不复杂但业务价值讲得出来。第四契合社会热点。老龄化背景下老年人的健康服务是一个比较受关注的方向选题的意义部分写起来不空洞导师也不会觉得你做的东西没有应用场景。1.2 系统角色与功能全景这个系统的主体结构可以分成前台展示和后台管理两块。前台面向普通用户也就是老年人本人或者帮忙操作的家属后台面向管理员可以理解成营养师或平台运营人员。角色权限设计越清晰后面前后端联调和答辩演示就越省事。先看普通用户侧的功能。登录注册是基础登录之后用户可以维护自己的健康档案包括年龄、身高体重、是否有高血压糖尿病这类慢性病、食物过敏史等。首页展示膳食指南文章和公告用户可以浏览食材库查看每种食材的热量、蛋白质、脂肪含量以及适宜人群。食谱模块是整个系统的核心用户可以根据分类、热量区间、慢病标签来筛选食谱点进详情页能看到配料表和具体步骤。用户还能创建自己的膳食计划按早中晚餐搭配一周的食谱并且每天打卡记录吃了什么。再看管理员后台。管理员可以管理用户列表查看用户健康档案对食材和食谱进行增删改查比如新增一道适合高血压老人的食谱需要同时维护它的食材配料和步骤管理前台展示的膳食资讯文章最后还有一个统计模块展示用户年龄段分布、热门食谱Top10、近7日打卡趋势这类可视化图表。我把主要模块整理成了一张表做数据库设计和论文功能描述时可以直接对着看模块面向角色核心功能用户认证所有用户注册、登录、退出健康档案普通用户维护年龄、慢病、过敏史、身体指标膳食资讯普通用户浏览文章、查看公告食材库普通用户按分类查看食材营养信息食谱推荐普通用户按慢病/热量/分类筛选食谱查看详情膳食计划普通用户按周生成三餐计划、每日打卡后台用户管理管理员查看用户列表、查看档案后台内容管理管理员食材/食谱/文章增删改查数据统计管理员用户分布、食谱热度、打卡趋势1.3 技术栈选型的底层逻辑技术栈方面SpringBootVue这套组合放在今天依然是毕设项目里最稳的选择没有之一。SpringBoot把过去Spring MVC时代繁琐的XML配置全砍掉了借助自动配置和starter机制你只需要一个加了相关依赖的工程写上数据源配置就能启动。这对时间紧张的毕业生来说太重要了因为环境搭建越简单留给业务开发的时间越多。Vue这边的优势是组件化和双向数据绑定。同一个后台管理页面如果用JSP做页面里混着Java代码改个样式都可能把逻辑弄坏换成Vue之后每一块界面都是一个组件页面数据和DOM自动同步渲染列表时一个v-for就搞定开发效率完全不在一个量级。有一个建议必须说在前面不要因为看到网上有人用Spring Cloud、Redis、消息队列就眼红非要往毕设里塞。这个项目的业务规模单体应用绰绰有余引入分布式组件除了让答辩老师连环追问只会给自己增加无谓的负担。毕设的核心目标是“功能完整、逻辑自洽、能讲清楚”不是技术选型越重越好。我见过好几个选了SpringCloud全家桶、最后把自己坑到延期答辩的例子属实没必要。数据库层推荐MySQL加MyBatis-Plus。MyBatis-Plus对单表CRUD几乎是零成本内置分页插件不用写XML就能完成大部分操作比原生MyBatis省不少事。前端UI组件库用Element UIVue2或Element PlusVue3后端接口用JWT做登录鉴权图表用ECharts这套组合的技术资料非常多遇到问题基本一搜就有答案。2. 核心设计细节从数据库到前后端分工2.1 数据库设计核心表结构与关联关系数据库设计是整个系统的基础也是答辩时最容易被追问的部分。这套系统的数据量不大核心表控制在八张左右比较合适表太多会让论文和答辩变得复杂表太少又显得功能单薄。八张表刚好把业务闭环覆盖完整。先看用户与健康档案。users表保存账号密码和基本信息role字段区分管理员和普通用户。health_record表与用户表一对一关联保存身高体重、慢性病、过敏史等健康数据。这里要注意健康档案不能直接在users表里加字段因为档案是后续维护的且更新频繁单独拆表会让逻辑更清晰论文的数据库设计小节也好展开。食材和食谱的关系是典型的多对多一道食谱由多种食材组成一种食材也会出现在多道食谱里。所以除了ingredient和recipe两张表还需要一张recipe_ingredient关联表把食谱和食材关联起来并记录用量。我见过有人图省事直接在recipe表里存一个食材ID的逗号拼接字符串这种做法初看简单但一旦要做“根据健康档案过滤含糖量过高的食谱”这种查询就会非常痛苦。老老实实建关联表后面推荐逻辑会好写很多。膳食计划和饮食打卡是支撑业务闭环的两张表。meal_plan表用plan_date加meal_type的组合来定位每一天的某一餐meal_type可以约定为breakfast、lunch、dinner三个枚举值diet_log表记录用户每天实际的饮食内容。后端通过这两张表可以算出用户的执行率比如某位老人这周三餐计划里有21餐实际打卡了18餐打卡率就出来了这个数据可以统计模块展示。我把核心表整理了一下建表时可以直接参考表名说明关键字段users用户与管理员id, username, password, nickname, role, phonehealth_record健康档案id, user_id, chronic_disease, allergies, height, weightingredient食材库id, name, category, calorie, protein, fat, carbohydraterecipe食谱id, name, cover, difficulty, steps, suitable_for, statusrecipe_ingredient食谱与食材关联id, recipe_id, ingredient_id, amountmeal_plan膳食计划id, user_id, plan_date, meal_type, recipe_iddiet_log饮食打卡id, user_id, log_date, meal_type, contentarticle膳食资讯id, title, cover, summary, content, publish_time2.2 后端分层Controller、Service、Mapper到底怎么组织后端代码的组织方式直接决定了答辩时导师对你工程能力的判断。最忌讳的做法是所有的逻辑都堆在Controller里一个方法几十行既要查库、又要拼数据、还要处理异常。短期看代码能跑但论文里的“系统设计”部分没法写答辩时也经不起追问。标准的做法是三层结构。Controller层只负责接收请求参数、调用Service、返回统一结构体Service层写业务逻辑比如登录时校验密码、推荐食谱时过滤不适合的食材Mapper层通过MyBatis-Plus的BaseMapper继承获得单表CRUD能力复杂查询再用注解或XML补充。我见过不少同学在Service层里越写越乱其实只要记住一条原则一个方法只做一件完整的事比如generateWeeklyPlan这个方法就只负责生成一周计划不要顺手把打卡统计也写进去。这里需要重点说一个所有毕设都躲不开的问题Controller返回的数据结构。我建议全项目统一使用一个Result类结构包含code、message、data三个字段成功时code为200失败时code为500未登录时code为401。这样做的好处是前端axios拦截器可以根据code统一处理错误不用每个接口单独判断。代码很短放出来给大家参考Data 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(String message) { ResultT result new Result(); result.setCode(500); result.setMessage(message); return result; } }登录鉴权方面推荐用JWT而不是传统的Session。前后端分离项目里前端和后端往往不在同一个端口Session跨域处理起来比较麻烦而JWT是后端签发一个Token字符串前端存到localStorage里每次请求带着这个Token后端通过拦截器校验。具体流程是用户登录成功后后端用用户ID和角色生成Token返回前端把Token放在请求头的Authorization字段里后端写一个拦截器拦截需要登录的接口解析Token并放行。核心生成Token的方法就几行public String generateToken(Long userId, String role) { return Jwts.builder() .setSubject(String.valueOf(userId)) .claim(role, role) .setExpiration(new Date(System.currentTimeMillis() 24 * 3600 * 1000)) .signWith(SignatureAlgorithm.HS512, secretKey) .compact(); }2.3 前端工程化路由、状态管理与接口对接前端部分很多人以为写几个Vue组件就完事了但一个像样的项目必须有清晰的路由设计和统一的请求封装。Vue Router要按模块划分登录页、首页、食谱列表、食谱详情、膳食计划、健康档案、后台管理各占一个目录。核心的路由配置不需要太复杂用meta字段标记哪些页面需要登录就行这里直接看路由守卫的写法router.beforeEach((to, from, next) { const token localStorage.getItem(token) if (to.meta.requiresAuth !token) { next(/login) } else if (to.path /login token) { next(/) } else { next() } })状态管理方面毕设项目里用Vuex或Pinia存一个用户基本信息就够了不需要搞动态路由、多角色权限菜单这类复杂设计。如果导师问起来就老老实实说“项目规模不大管理员和普通用户的路由是通过请求拦截器控制的”这是一个合理的设计取舍不是技术缺陷。axios请求封装是前端最重要的工程化手段。创建一个request.js文件设置baseURL为/api在请求拦截器里从localStorage取出Token并放入请求头在响应拦截器里统一处理code这样可以避免每个页面的请求都重复写错误处理代码service.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers[Authorization] token } return config }) service.interceptors.response.use(response { const res response.data if (res.code ! 200) { this.$message.error(res.message || 请求失败) return Promise.reject(new Error(res.message)) } return res.data })3. 实操过程从零搭出一个可运行的膳食系统3.1 环境与版本选择别在第一步翻车这几年我调试项目时发现大量问题不是出在业务代码上而是出在环境版本不匹配上。有很多同学直接下载最新版JDK和SpringBoot然后发现各种依赖不兼容最后跑到论坛问“为什么我的项目一启动就报错”。这里直接给一套经过验证的版本搭配照着选不会出大问题组件推荐版本说明JDK1.8 或 111.8最稳不少学校机房还是这个版本SpringBoot2.7.x不要用3.x3.x要求JDK17且部分starter不兼容Maven3.8.x配置国内镜像避免下载依赖卡死Node.js14 到 16过高的Node版本可能导致Vue CLI项目构建报错Vue CLI4.5 或 5.x配合Node版本选择MySQL5.7 或 8.08.0需要指定时区参数这里要专门说一句SpringBoot版本的问题。很多人在热词里搜“springboot版本太高”就是因为中招了。SpringBoot 3.x虽然已经发布很久但很多旧版MyBatis Plus插件、代码生成器都没有跟上报错信息往往不是一两句话能解决的。毕设用SpringBoot 2.7.x完全够用不要去追新版本。Maven配置国内镜像这一步很多人忽略了结果就是创建一个SpringBoot工程后依赖下载慢到怀疑人生。打开maven安装目录下的conf/settings.xml找到mirrors标签加上阿里云镜像就行mirror idaliyunmaven/id mirrorOfcentral/mirrorOf name阿里云公共仓库/name urlhttps://maven.aliyun.com/repository/public/url /mirror3.2 后端初始化与核心代码实现后端工程推荐用Spring Initializr创建在start.spring.io上选择Java版本、SpringBoot版本然后勾选Spring Web、MySQL Driver、MyBatis相关依赖。创建完成后第一步是配置application.yml。数据源配置是重中之重尤其要注意8.0以上MySQL必须带时区参数否则启动时很容易报时区错误server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/diet_system?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 你的密码 driver-class-name: com.mysql.cj.jdbc.Driver mybatis-plus: configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImpl配置文件的注释里我特意标了密码位置实际使用时按你自己数据库的密码来填。map-underscore-to-camel-case这个配置建议开启它能让数据库里health_record这种下划线字段名自动映射成Java实体里的healthRecord驼峰属性省掉一大堆手动映射的麻烦。登录功能是后端的第一个核心闭环也是后面所有需要登录接口的基础。UserService里的login方法逻辑并不复杂流程是用username查用户比对数据库中加密后的密码生成Token返回。这里提一个很多学生会忽略的点数据库里存的密码一定要加密不要用明文。Spring Security里的BCryptPasswordEncoder可以单独拿来用不要因为觉得引入框架麻烦就省掉这一步答辩时这也是一个亮点。推荐食谱是整个系统里最有业务价值的接口。我的实现思路是这样的先查用户的健康档案根据慢性病信息找到用户需要避开的食材集合比如糖尿病患者要少摄入高糖分食材然后查出所有状态正常的食谱排除掉包含禁忌食材的食谱最后按某种规则排序返回。核心代码可以参考这个写法public ListRecipeVO recommendForUser(Long userId) { HealthRecord record healthRecordMapper.selectOne( new LambdaQueryWrapperHealthRecord() .eq(HealthRecord::getUserId, userId)); SetLong forbiddenIngredientIds new HashSet(); if (record ! null) { forbiddenIngredientIds ingredientMapper .selectForbiddenIds(record.getChronicDisease()); } ListRecipe allRecipes recipeMapper.selectList( new LambdaQueryWrapperRecipe().eq(Recipe::getStatus, 1)); return allRecipes.stream() .filter(recipe - !containsForbiddenIngredient(recipe, forbiddenIngredientIds)) .sorted(Comparator.comparingInt(this::scoreRecipe)) .limit(10) .map(recipe - convertToVO(recipe)) .collect(Collectors.toList()); }这段代码里containsForbiddenIngredient方法内部会通过recipe_ingredient关联表查出该食谱包含的食材与禁忌食材集合取交集判断scoreRecipe方法是根据食谱的标签匹配度打分。逻辑简单但可讲性很强答辩时可以围绕这套代码讲清楚推荐算法的基本思想。3.3 前端初始化与页面联调前端用Vue CLI创建工程后记得在vue.config.js里配置开发服务器代理。本地开发时Vue跑在8081端口SpringBoot跑在8080端口如果不做代理浏览器会直接跨域拦掉所有请求。配置方式是把所有以/api开头的请求转发到后端地址module.exports { devServer: { port: 8081, proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } }登录页的写法比最基础的示例稍微多一点东西表单校验、请求封装、登录成功后的跳转。模板部分就是一个普通的表单组件脚本部分调用封装的request方法这里贴一下登录成功后的处理逻辑async handleLogin() { this.$refs.loginForm.validate(async valid { if (!valid) return const data await request.post(/auth/login, this.loginForm) localStorage.setItem(token, data.token) localStorage.setItem(userInfo, JSON.stringify(data.user)) this.$router.push(/) }) }登录联调通了之后就进入业务功能开发。“一键生成一周计划”是完善业务闭环最关键的功能它的交互流程可以这样设计用户在膳食计划页面选择本周起始日期点击生成按钮前端向后端发起请求后端根据用户的健康档案按周一至周日每天早中晚三个餐次分别推荐一道适合的食谱写入meal_plan表然后前端把这一周7天的三餐列表渲染出来。用户可以在生成的计划上手动调整替换食谱每天打卡时从计划列表里选当天实际吃的餐次。这里有一个很实际的开发建议前端的页面开发顺序应该是先做依赖核心业务的页面比如食谱列表和食谱详情再做个人中心这类简单的信息展示页。因为核心业务页面的接口一旦跑通整个系统的数据链路就通了剩下的页面都是套模板而已。3.4 前后端打包部署把Vue放进SpringBoot有不少同学在热词里搜“vue打包放进springboot中”说明这一步确实困扰了很多人。其实操作方式非常简单。前端执行npm run build会在项目根目录生成一个dist文件夹里面是打包后的静态资源文件。把这个文件夹里的所有内容复制到SpringBoot项目的src/main/resources/static目录下然后执行mvn package打包就能生成一个包含前端页面的可运行jar包。这里有一个必须处理的坑前端打包时axios请求的baseURL不能指向localhost:8080因为部署后前后端在同一个端口下必须改成相对路径/api。否则页面能被访问到但所有接口请求都会404。如果你的开发环境在src/request.js里硬编码了baseURL部署前记得改成不带域名的相对路径。打包完成后通过java -jar target/diet-system.jar启动项目浏览器访问http://localhost:8080就能看到完整的系统前端页面和后端接口都跑在同一个端口。这一步很关键因为答辩时只需要启动一个jar包就能演示整个系统不会出现开两个终端的尴尬场景。4. 常见问题与排查技巧实录4.1 Maven依赖下载慢与依赖冲突Maven依赖下载慢这个问题出现的频率极高尤其是刚创建SpringBoot工程那会儿一个starter就能让IDE转半天圈。解决方式就是前面提到的阿里云镜像。已经创建好的项目也可以在项目pom.xml中加一个repository指向镜像地址效果相同。我见过有人因为等不及反复取消重新创建工程结果依赖缓存损坏越弄越乱。遇到这种情况直接删除本地仓库中对应的损坏目录重新下载就行不要反复折腾。依赖冲突的典型场景是引入某个第三方工具包之后项目启动报NoSuchMethodError或者ClassNotFoundException究其原因多半是传递依赖的版本和SpringBoot自带的版本冲突。排查方式是在IDEA的Maven面板里点一下依赖分析看哪些库存在冲突解决方式是用exclusion把多余的传递依赖排除掉或者显式声明一个统一版本。这类问题对毕设来说不算高频但一旦遇到就很容易让人心态崩溃。4.2 SpringBoot版本选择与启动失败版本相关的问题在毕设项目里尤为突出。如果你用了SpringBoot 3.x会遇到两个最典型的问题一是javax包全部变成了jakarta老的代码示例几乎全部失效二是一些MyBatis Plus的旧版本不支持新规范启动时直接报错。所以还是推荐用2.7.x。启动失败还有一个很常见的原因是端口被占用很多时候是之前启动的项目没有关掉或者两个进程同时跑在8080端口。Windows上用netstat -ano | findstr 8080找到占用端口的进程PID然后在任务管理器里结束它macOS或Linux上用lsof -i:8080。这个操作很容易排查不要一上来就怀疑代码写错了。4.3 数据库连接与中文乱码数据库连接出问题大多是两类。一类是Access denied for user密码错了或者用户权限不足检查一下数据库配置文件的用户名密码是否和本地一致另一类是时区报错MySQL 8.0默认时区配置和国内环境不匹配解决方式就是在jdbc url后面加serverTimezoneAsia/Shanghai。中文乱码的问题也常见前端页面提交中文到后端后存进数据库变成乱码通常是三个环节里某一环的编码不一致导致的。解决方案是数据库连接串加上useUnicodetruecharacterEncodingutf8MySQL建库时指定utf8mb4字符集前端页面head标签设置charset为utf-8。记住这三步基本可以杜绝乱码问题。4.4 前端联调与常见报错速查前后端联调阶段遇到的问题几乎每个星期都会有人问。这里整理了一个速查表按报错现象、可能原因、解决方式三列列出实际操作时直接对着排查报错现象常见原因解决方式接口返回404前端代理未生效或后端没有对应路由检查vue.config.js代理配置检查Controller的RequestMapper路径跨域被浏览器拦截前端8081调后端8080优先用Vue代理后端可加CORS配置兜底后端接收不到前端参数Content-Type不匹配POST请求带JSON用RequestBody表单用RequestParamVue打包后图片404静态资源路径配置不对vue.config.js里设置publicPath为./npm run build内存溢出Node默认内存不足package.json里把build命令改成node --max-old-space-size4096 node_modules/.bin/vue-cli-service buildECharts图表不显示容器高度为0外层div显式设置高度比如height: 400px这个表里的每一条都是我实际遇到过的尤其是ECharts图表不显示这个问题看起来是代码问题其实只是容器高度问题遇到过好几次学生因为一个图不显示怀疑了整个数据链路有问题。排查的时候先看容器样式再去看数据。关于后端接口调试我还有一个习惯会在开发阶段把MyBatis Plus的SQL日志打开也就是application.yml里那个log-impl配置。这样每次请求接口时控制台会打印实际执行的SQL语句排查数据对不上的问题非常高效。等到部署生产时再关掉答辩的时候开着问题也不大反而显得项目真实。另外如果你的项目后续还要扩展功能比如增加视频教程模块前端用原生的video标签就能满足基本需求不需要为了这个去引入额外的播放器依赖。真要加复杂的播放功能再考虑专门的播放器组件也不迟。做毕设要把精力集中在核心业务上不要被边缘功能拖住。说实话这种管理类系统的技术难度并不算高真正拉开差距的是你有没有把业务闭环做完整、把坑都踩平。我自己在带这类项目时最看重三件事数据库表之间的关系能不能讲清楚角色权限流程能不能讲清楚前端页面是不是真的和后端接口对上了。源码和文档只是一个起点调试的过程才是真正值钱的部分。每年都有人拿到附带的源码就以为万事大吉结果答辩时连项目怎么启动都说不明白这其实很可惜。照着这篇的思路把数据库建好、接口调通、页面渲染出来整个过程走一遍你会发现这个系统并没有想象中那么复杂反而会让你在答辩的时候特别有底气。
返回列表