连着好几个晚上,都有同学在后台私我同一个关键词:springboot健康菜谱生成系统。起因是我们整理的那批2026年毕设项目合集里,这个项目编号14221,热度一直排在前排。今天我不再一个问题一个问题回复了,直接把这个项目从选题思路到代码落地,再到答辩前需要准备的东西,全部写出来。内容不吹不黑,就是我实际带学弟做这个项目时踩过和解决过的问题,希望能给打算用Spring Boot做毕设、或者单纯想找个完整练手项目的同学一点参考。
健康菜谱生成系统,本质上不是普通的管理系统。它表面上包含用户管理、食材管理、菜谱管理、健康档案管理这些常规模块,但核心亮点在于“生成”这两个字:系统能根据用户的个人健康数据,比如身高、体重、年龄、性别、饮食偏好,自动计算出每日热量需求,再从菜谱库里筛选搭配出符合营养比例的菜谱。对于毕设来说,这个点很有价值,因为老师一听就知道你不是只会增删改查,而是确实做了业务逻辑设计。下面我按实际做项目的顺序,从整体设计讲到问题排查,一步一步拆开说。
1. 项目整体设计与思路拆解
1.1 毕设选题为什么选健康菜谱生成系统
先说选题逻辑。很多同学选毕设题目的时候,习惯性奔着“管理管理系统”去,什么图书管理、宿舍管理、车辆管理。这类题目不是不行,但说实话,每年答辩现场全是这类系统,老师早就审美疲劳了。而且这类系统技术难度集中在CRUD,很难在答辩时展示出核心亮点。
健康菜谱生成系统不一样。它有一个所有评委老师都能听懂的“健康”主题,不需要你解释太多背景,大家都知道现在饮食健康有多重要。同时它又没有复杂到没法在几个月内做完的程度。用户输入自己的身体数据,系统给出合理的菜谱推荐,这个业务闭环非常清晰。从毕设角度讲,它兼备了常规业务功能和核心算法逻辑,既能展示基本功,又能展示设计能力,属于性价比很高的题目。
另外,健康菜谱这个方向天然适合Spring Boot这套技术栈。Spring Boot负责接口层和业务层,前端可以用Vue做页面,数据库存用户、食材、菜谱、健康档案这些结构化数据,Redis做缓存加速热点菜谱访问,JWT做登录认证。整个技术栈选型非常自然,不会出现为了用某个技术而强行加功能的情况,这对答辩很重要,因为老师问“你这个技术用在哪里”的时候,你能给出顺理成章的回答。
再说点实际的。这个项目的工作量比较可控。如果一个人全职做,两周到三周能完成核心功能,剩下时间用来写论文、做PPT、准备答辩。如果组队做,一个人负责后端Spring Boot,一个人负责前端Vue,一个人负责算法逻辑和测试,分工也很清晰。不像有些题目听起来高端,但做起来库表能建三四十张,代码量爆炸,最后忙不过来。
1.2 功能模块划分与数据库设计思路
整个系统我把它拆成六个核心模块:
- 用户模块:注册、登录、个人信息维护,使用JWT做无状态认证。
- 健康档案模块:身高、体重、年龄、性别、活动量、饮食目标(减肥、增肌、维持、控糖),以及过敏食材和忌口食材。
- 食材库模块:管理员维护食材分类、食材名称、每100克热量、蛋白质、脂肪、碳水化合物含量。
- 菜谱库模块:管理员维护菜谱名称、分类、烹饪方式、所需食材清单、制作步骤、封面图、标签。
- 菜谱生成模块:根据用户健康档案和饮食目标,自动计算每日热量和营养需求,从菜谱库中筛选并组合出合理的菜谱,支持按天生成和按周生成。
- 生成记录模块:保存用户每一次的生成记录,方便查看历史食谱。
数据库设计是整个项目的基石,我建议不要贪多,先保证核心表清晰。我实际用的是下面这几张表:
- user:用户表,字段包括id、username、password、nickname、gender、phone、create_time。
- health_profile:健康档案表,字段包括id、user_id、height、weight、age、activity_level、goal_type、allergen、avoid_food,和user是一对一关系。
- food:食材表,字段包括id、name、category、calorie、protein、fat、carbohydrate、unit。
- recipe:菜谱表,字段包括id、name、category、method、cover_image、steps、status。
- recipe_food:菜谱食材关联表,字段包括id、recipe_id、food_id、food_weight。为什么单独建这张表?因为一道菜里包含多种食材,每种食材用量不同,必须通过关联表来维护多对多关系,也方便后续根据用户忌口动态过滤菜谱。
- diet_record:生成记录表,字段包括id、user_id、date、meal_type(早/午/晚)、recipe_ids、total_calorie、total_protein、total_fat、total_carbohydrate、create_time。
这套表设计很干净,没有冗余。每一个模块都能找到对应的表支撑,在论文的数据设计章节也有东西可写。还有一个细节:健康档案表不建议把字段直接塞进user表。因为用户可能暂时没有填写健康信息,拆开之后,登录功能不受影响,只有在生成菜谱时才要求档案完整,这样逻辑更清晰。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术栈选型与项目初始化
2.1 Spring Boot版本和JDK版本怎么选
这个点我必须单独拿出来说,因为太多人在第一步就卡住了。这几年Spring Boot版本更新很快,网上很多教程还在用老版本,你照着做又报一堆错,很容易心态爆炸。关于版本选择,我的建议很简单:图省心就选Spring Boot 2.7.18配JDK 8,想尝尝新就选Spring Boot 3.2.x配JDK 17。
为什么我推荐Spring Boot 2.7.18?因为它是2.x系列的最后一个版本,稳定、资料多、兼容性好,绝大多数网上的代码片段拿到就能用,MyBatis-Plus、Swagger、JWT等主流依赖都能找到对应的适配写法。对于毕设项目来说,稳定压倒一切,你不想在答辩前一晚还在折腾包冲突。
如果你选Spring Boot 3.x,那就必须用JDK 17及以上版本,因为3.x从底层上就不再支持JDK 8。同时要注意一个最容易踩的坑:Spring Boot 3.x把javax开头的包换成了jakarta开头。比如原来写javax.servlet.http.HttpServletRequest,现在要写jakarta.servlet.http.HttpServletRequest。很多老代码复制过来不修改直接报红,就是这个原因。这部分内容我在后面常见问题里会细讲。
还有一点和“springboot版本太高”这个热搜词很贴合。Spring Boot 4.0已经在路上了,但我不建议毕设项目用它。版本越高,新特性越多,但坑也越深,很多第三方库的兼容没跟上,出了问题你连搜都搜不到解决方案。做毕设不是追新,能用稳定版本把业务做完才是真的。
2.2 项目骨架搭建与基础配置
项目创建我习惯用IDEA的Spring Initializr。选好Spring Boot版本、JDK版本,勾选需要用到的依赖,然后让IDEA自动生成骨架,这样比手动建Maven项目再去补依赖要快很多,也不容易漏掉关键包。
以Spring Boot 2.7.18版本为例,我实际项目里的核心依赖如下:
xml复制<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi2-spring-boot-starter</artifactId>
<version>4.4.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
MyBatis-Plus版本必须注意,3.5.3.1这个版本对Spring Boot 2.x和JDK 8都友好。MySQL驱动在Spring Boot 2.7.x里用mysql-connector-java,但在Spring Boot 3.x里坐标变成了com.mysql:mysql-connector-j,别搞混了。
application.yml里的核心配置,我贴一份实际封装过的模板:
yaml复制server:
port: 8080
servlet:
context-path: /api
spring:
datasource:
url: jdbc:mysql://localhost:3306/healthy_recipe?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
redis:
host: localhost
port: 6379
database: 0
servlet:
multipart:
max-file-size: 10MB
max-request-size: 50MB
mybatis-plus:
mapper-locations: classpath*:mapper/**/*.xml
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
map-underscore-to-camel-case: true
jwt:
secret: healthy-recipe-secret-key
expire: 604800
这里有几个细节值得说。数据库连接串里serverTimezone=Asia/Shanghai必须要加,不然会报时区错误,这属于新手必踩的坑。map-underscore-to-camel-case开启下划线转驼峰,这样数据库里的user_id能自动映射到Java实体里的userId,省掉一堆别名。JWT密钥和过期时间放配置文件而不是写死在代码里,方便后面改,也显得你代码规范。
2.3 自动装配原理能帮你解决什么问题
聊到Spring Boot,面试和答辩十有八九会被问到自动装配原理,我在这里先把这个核心机制讲透,后面排查问题也用得到。
Spring Boot的启动类上那个@SpringBootApplication,其实是一个组合注解,里面包含了@SpringBootConfiguration、@EnableAutoConfiguration和@ComponentScan。关键在@EnableAutoConfiguration,它通过AutoConfigurationImportSelector这个类,去加载META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件里声明的所有自动配置类,然后按条件注解(@ConditionalOnClass、@ConditionalOnMissingBean等)判断是否生效。
用大白话说,就是Spring Boot启动时会自动检查你的类路径下有没有某个库的类,如果有,就自动帮你创建一堆默认的Bean。比如你引入了Redis的starter,类路径里有RedisTemplate相关的类,Spring Boot就自动帮你配置一个RedisTemplate的Bean,不用你手动写配置类。
理解这个原理,最大的实际价值在于排查问题。很多时候你发现某个功能没生效,不是你的代码写错了,而是某个自动配置没有生效,或者被你的配置覆盖了。比如开发中遇到Redis连接报错,第一反应就应该是检查是不是没有引入对应的starter,或者自己手动创建的RedisTemplate覆盖了默认配置。这个思路在很多问题排查里都能复用。
3. 核心功能实现与健康菜谱生成逻辑
3.1 菜谱推荐算法:从需求到代码的落地思路
健康菜谱生成系统的核心,不在登录注册,而在于那个“怎么从菜谱库里选出一组合适菜谱”的算法逻辑。我实际用的是规则评分加动态筛选的方式,不涉及机器学习,但足够实用,而且答辩完全能讲清楚。
算法整体分三层:
第一层叫硬性过滤。根据用户填写的过敏食材和忌口食材,把包含这些食材的菜谱全部排除掉。比如用户填了“花生过敏”,那所有关联了花生的菜谱都不能出现在结果里。这一步通过SQL或内存过滤都能实现,我推荐在查询菜谱时用SQL关联recipe_food表和food表,一次查询就把不满足条件的菜谱剔除,性能更好。
第二层叫热量约束。根据健康档案算出用户每日总热量需求,然后按早中晚3比4比3的比例分配到三餐,每道候选菜谱根据食材用量算出热量,累加后落在对应热量范围内,才进入下一轮。
第三层叫营养均衡评分。每天的热量够了不代表营养均衡。我会对每道菜谱计算蛋白质、脂肪、碳水化合物的含量,然后按目标类型打分。减脂目标要求高蛋白、中低碳水、低脂肪;增肌目标要求高蛋白、中高碳水、中高脂肪;控糖目标重点关注低GI食材和低碳水。评分高的菜谱排前面,再结合一个随机因子防止用户每天看到相同推荐。
这样分层的好处是逻辑清晰,每一层都可以单独测试。答辩时老师问“怎么保证推荐结果的合理性”,你就把这三层一讲,他马上能理解,比那些黑盒调用大模型或者花里胡哨的推荐算法更可信。
3.2 健康档案与每日热量需求计算
每日热量需求是整个生成逻辑的源头,这里我采用的是Mifflin-St Jeor公式,它在营养学里认可度很高,算出来的基础代谢率比较接近真实值。公式分男女:
- 男性基础代谢率 = 10 × 体重(kg) + 6.25 × 身高(cm) - 5 × 年龄(岁) + 5
- 女性基础代谢率 = 10 × 体重(kg) + 6.25 × 身高(cm) - 5 × 年龄(岁) - 161
算出来的基础代谢率,再乘一个活动系数,就是每日维持当前体重的热量需求。活动系数一般取这些值:久坐不动1.2,轻度活动1.375,中度活动1.55,高度活动1.725。然后根据目标调整,减脂在总热量上减10%到20%,增肌则加10%到15%。
我举个例子,一个25岁、身高170cm、体重65kg、每周锻炼三四次的男生,想减脂。基础代谢率算出来是:10×65+6.25×170-5×25+5=650+1062.5-125+5=1592.5。活动系数取1.55,维持热量是1592.5×1.55=2468.4。减脂20%,每日热量目标大概在1974千卡。接着按早餐30%、午餐40%、晚餐30%拆分,早餐约592千卡,午餐约790千卡,晚餐约592千卡。
这些计算逻辑全部放在后端一个叫NutritionCalculator的类里,前端只需要传身高体重这些原始数据,不需要做任何计算。这个设计我强烈推荐,因为把核心算法放在后端,不仅方便测试,答辩时还能演示接口输入输出,比前端写死一套公式要正规得多。
3.3 菜谱生成的核心代码实现
下面我贴一段实际用的生成逻辑,去掉了一些细节,保留了主干结构。注意我用的是MyBatis-Plus的LambdaQueryWrapper,比写XML简单,可读性也更高。
java复制public DietRecommendResult generateDailyPlan(Long userId) {
// 1. 获取用户健康档案
HealthProfile profile = healthProfileMapper.selectOne(
new LambdaQueryWrapper<HealthProfile>()
.eq(HealthProfile::getUserId, userId)
);
if (profile == null) {
throw new BizException("请先完善健康档案");
}
// 2. 计算每日热量和三大营养素目标
NutritionGoal goal = nutritionCalculator.calculateDailyGoal(profile);
// 3. 查询用户忌口食材ID集合
List<Long> bannedFoodIds = getBannedFoodIds(profile);
// 4. 查询所有启用的菜谱,排除包含忌口食材的菜谱
List<Recipe> candidates = recipeMapper.selectEnabledRecipesExcludingFoods(bannedFoodIds);
// 5. 按早餐/午餐/晚餐分别生成
DailyMealPlan plan = new DailyMealPlan();
plan.setBreakfast(pickMeal(candidates, goal.getBreakfastCalorie(), MealType.BREAKFAST));
plan.setLunch(pickMeal(candidates, goal.getLunchCalorie(), MealType.LUNCH));
plan.setDinner(pickMeal(candidates, goal.getDinnerCalorie(), MealType.DINNER));
// 6. 计算总热量和营养素,保存在记录表里
return buildResult(userId, plan, goal);
}
pickMeal方法是核心,思路是遍历候选菜谱,对每道菜算热量,找到与目标热量差值最小的那道,作为主菜,再尝试搭配一两个小菜或主食,直到总热量接近目标值。实际项目里我预设了一些菜谱分类,比如“主食”“荤菜”“素菜”“汤羹”,搭配时优先保证一个荤菜一个素菜一个主食,这样结构更合理。
这个实现不是最优的算法,但它足够容易懂、容易改、容易扩展,毕设完全够用。如果你学有余力,还可以加一个约束:连续两天不要推荐完全相同的菜谱组合,这个用Redis记录最近几天的生成结果就能实现。
4. 实操过程:从零到一跑通项目
4.1 项目初始化实操
实操过程我按照实际做项目的顺序走一遍,方便你照着复现。第一步,打开IDEA,选择Spring Initializr,注意Server URL如果连不上,就切换成阿里云的镜像地址。Group填com.study,Artifact填healthy-recipe,Java版本选8,如果你前面定了Spring Boot 2.7.18,这里才能选8。
项目生成之后,第一步先改pom.xml,把依赖补全,然后修改application.yml。这里我建议你先跑一个最小的demo验证环境,在启动类里写一个HelloController,返回“hello”,启动Spring Boot,看能不能访问http://localhost:8080/api/hello。这一步能最快暴露出JDK环境、端口占用、依赖冲突这些问题。等基础环境通了,再开始写业务代码,否则后面一堆报错混在一起,你根本不知道是环境问题还是代码问题。
4.2 数据库建表与初始化数据
我实际使用的建表脚本如下,为了简洁我只放核心几张表的结构:
sql复制CREATE DATABASE IF NOT EXISTS healthy_recipe DEFAULT CHARACTER SET utf8mb4;
USE healthy_recipe;
CREATE TABLE user (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) UNIQUE NOT NULL,
password VARCHAR(100) NOT NULL,
nickname VARCHAR(50),
gender TINYINT DEFAULT 0,
phone VARCHAR(20),
create_time DATETIME DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE health_profile (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
user_id BIGINT NOT NULL,
height DECIMAL(5,1),
weight DECIMAL(5,1),
age INT,
activity_level TINYINT,
goal_type TINYINT,
allergen VARCHAR(255),
avoid_food VARCHAR(255),
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_user_id (user_id)
);
CREATE TABLE food (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50) NOT NULL,
category VARCHAR(20),
calorie DECIMAL(6,1) COMMENT '每100克热量(千卡)',
protein DECIMAL(5,1),
fat DECIMAL(5,1),
carbohydrate DECIMAL(5,1),
unit VARCHAR(20)
);
CREATE TABLE recipe (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100) NOT NULL,
category VARCHAR(20),
method VARCHAR(20),
cover_image VARCHAR(255),
steps TEXT,
status TINYINT DEFAULT 1
);
CREATE TABLE recipe_food (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
recipe_id BIGINT NOT NULL,
food_id BIGINT NOT NULL,
food_weight DECIMAL(6,1) COMMENT '食材用量(克)'
);
初始化数据也很关键。菜谱数据可以从网上搜集,不用太多,早餐、午餐、晚餐各二十到三十条就够用了。每条菜谱都需要关联食材和用量,这样系统才能准确计算热量和营养素。这块数据整理工作比较繁琐,我建议用Excel先整理好,再通过SQL批量导入。别指望全手工一条一条insert,那会把你心态搞没。
4.3 接口开发与自测
项目里最核心的接口是POST /api/diet/generate,请求参数只需要userId,后端自动读取健康档案并生成菜谱。响应结构大概是这样的:
json复制{
"code": 200,
"message": "success",
"data": {
"date": "2026-04-01",
"breakfast": [
{"recipeId": 1, "name": "燕麦牛奶粥", "calorie": 320, "protein": 15.2},
{"recipeId": 2, "name": "水煮蛋", "calorie": 72, "protein": 6.8}
],
"lunch": [
{"recipeId": 12, "name": "鸡胸肉沙拉", "calorie": 450, "protein": 38.4}
],
"dinner": [
{"recipeId": 20, "name": "清蒸鲈鱼", "calorie": 280, "protein": 33.1}
],
"totalCalorie": 1122,
"totalProtein": 93.5,
"totalFat": 35.2,
"totalCarbohydrate": 120.6
}
}
开发完接口自己一定要测。我一般先用Knife4j自带的接口文档页面测一遍,再用Postman跑一遍关键业务流程,包括注册、登录、完善健康档案、生成菜谱、查看历史记录。特别要注意,生成接口依赖登录状态,测试前先调用登录接口拿到JWT token,然后在请求头里加Authorization: Bearer <token>,不然会报401。很多同学第一次做JWT相关功能,全部测完才发现接口因为没带token全挂了,这种低级错误一定要避免。
5. 常见问题与排查技巧实录
5.1 Spring Boot版本过高导致的那一堆坑
我把这个问题放在第一位,因为它遇到的人实在太多了。这两年关键词“springboot版本太高”搜索量一直不低,什么意思呢?就是很多人明明照着低版本教程写的代码,结果项目创建时选了最新版Spring Boot,直接编译不通过,甚至项目都启动不了。
Spring Boot 3.x最明显的变化就是javax迁移到jakarta。我见过一个同学,代码里导入了javax.annotation.Resource,编译直接报错,他还在那找依赖冲突,实际就是版本问题。解决起来很简单,把javax.*改成jakarta.*,比如javax.servlet.http.HttpServletRequest变成jakarta.servlet.http.HttpServletRequest。另外,Spring Boot 3.x里Redis的配置前缀也变了,原来是spring.redis,现在换成了spring.data.redis,配置文件不跟着改,Redis根本连不上。
如果你反复折腾还是各种报错,我的终极建议是别死磕,直接退回Spring Boot 2.7.18加JDK 8,这个组合已经经过大量项目验证,网上的解决方案也最多。做毕设本来时间就紧张,没必要把大把时间花在版本兼容性这种破事上。
5.2 Spring Boot循环依赖与事务失效
循环依赖是个高频面试题,在项目中也会真实发生。最常见的情况就是两个Service互相调用,比如菜谱生成服务依赖营养计算服务,营养计算服务又依赖菜谱生成服务,然后启动时报错“The dependencies of some of the beans in the application context form a cycle”。
Spring Boot 2.6版本之后,循环依赖默认是禁止的,报错直接中断启动。解决循环依赖,优先别想着开开关绕过,而是重构代码,把互相调用的逻辑抽到第三个服务里。比如我之前就遇到推荐服务和记录服务互相调用的设计,后来把“记录保存”抽成一个独立方法,两个服务都通过一个DietRecordService来调用,问题自然消失。如果你的项目里确实有无法避免的循环依赖,可以用@Lazy注解延迟注入,实测有效,但这不是长远方案,能重构就重构。
事务失效问题也常见。我接手过一版代码,用户保存健康档案后要同步生成一条初始化记录,代码在Service里写了个私有方法,加了@Transactional,结果事务根本没生效。原因有两条:一是私有方法自调用,Spring事务是基于AOP代理的,自调用不会经过代理类,注解等于白写;二是异常被try-catch吞掉了,事务感知不到异常,自然不会回滚。
正确做法是:事务方法必须是public,并且通过Spring注入的代理对象来调用,不要把调用写在同类内部。同时,如果方法里出现了非RuntimeException,记得在注解上指定@Transactional(rollbackFor = Exception.class),不然默认只在RuntimeException时回滚,你会看到数据一半成功一半失败的诡异情况。
5.3 接口调试和部署相关问题
开发阶段还容易遇到几个杂症。第一个是端口被占用。启动Spring Boot时报Port 8080 was already in use,这时候按Win+R输入cmd,执行netstat -ano | findstr 8080,找到占用进程的PID,去任务管理器结束进程,或者直接改配置文件端口。
第二个是上传图片后访问不到。菜谱封面图上传到本地磁盘,但浏览器里打开的是404。这是因为Spring Boot默认不会把你本地的某个磁盘目录映射成静态资源路径。解决办法是写一个WebMvcConfigurer的配置类,把本地路径映射到/images/**:
java复制@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Value("${file.upload-path}")
private String uploadPath;
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/images/**")
.addResourceHandler("file:" + uploadPath);
}
}
第三个是打包部署相关。很多同学用JDK 8写完,最后想打包成Docker镜像,结果老是在镜像里跑不起来。这里我建议用多阶段构建,先在一个带JDK的镜像里执行maven打包,再把生成的jar复制到轻量运行时镜像里。如果项目用的是JDK 8和Spring Boot 2.x,基础镜像我常用eclipse-temurin:8-jre,比较稳。时区一定要在Dockerfile里设置为Asia/Shanghai,否则日志时间和本地时间对不上,排查问题能把你搞崩溃。
第四个是跨域问题。前后端分离部署时,前端在5173端口,后端在8080端口,浏览器默认会把跨域请求拦截。开发阶段最简单的方式是加一个全局CORS配置类,允许所有来源,开发调试方便,但上线时建议把allowedOrigins收窄到具体域名。
5.4 推荐一个方便调试的小工具
很多同学在控制台看到一堆日志,但没什么结构感。我这里推荐一个很实用的小技巧:Spring Boot支持自定义banner,就是你启动时那个大大的“Spring”字母画。网上有在线banner生成器,可以把你的项目名字做成ASCII艺术字,放到src/main/resources/banner.txt里。这样每次启动,团队成员都知道当前跑的是哪个模块。虽然对功能没什么提升,但在答辩演示时,启动画面一亮相,老师的印象分会好很多。这个小细节成本极低,我非常建议加上。
6. 项目扩展方向与答辩经验
6.1 答辩时最容易被问到的几个问题
带着这个项目去答辩,我预判老师大概率会问这几个问题,而且都跟Spring Boot有关,这里我提前给你捋一下怎么答。
老师问“为什么选Spring Boot做这个项目”,你不能只说“因为好用、生态好”。要从开发效率讲:Spring Boot自动配置大大减少了XML配置,内置Tomcat让项目可以一键启动,对快速实现业务很有帮助。配合Spring生态,整合数据库、Redis、安全认证都很快。这样的回答既有原理又有场景,比空吹强得多。
老师问“讲讲自动装配原理”,你把我上面2.3节讲的内容组织成两三句话,先说启动类是个组合注解,再说@EnableAutoConfiguration通过加载自动配置文件来注册Bean,再说条件注解按需生效,然后举个例子,比如引入了Redis starter后RedisTemplate自动可用了。这个回答基本满分。
老师问“健康菜谱生成算法怎么保证合理性”,你就把三层逻辑讲清楚:过滤忌口、热量约束、营养评分。如果老师追问公式,你就把Mifflin-St Jeor公式和三大营养素比例写出来,说明是按标准营养学公式做的。老师会认为你有据可依,不是瞎写。
6.2 后续还能怎么扩展
如果做完了基本功能时间还很充裕,我建议往这几个方向扩展。方向一是增加饮食偏好标签,比如“清淡”“麻辣”“快手菜”,让生成逻辑再增加一个偏好评分维度。方向二是接入一个食物识别接口,用户拍照识别食材,自动把食材加入冰箱库存,这个亮点很有冲击力,但实现成本不低。方向三是做一个小程序端,用Spring Boot做后端接口,小程序负责展示和交互,很多学校对移动端元素比较看中。方向四是在管理端加一个简单的数据统计图表,统计每个用户的热量摄入分布,用ECharts展示。
这些扩展不是必须的,但每做一个,系统的完整度和答辩的竞争力就高一个台阶。我之前带学弟那个版本,最后加了“一周菜谱导出PDF”的功能,答辩时老师还专门问了这个功能是怎么实现的,气氛一下子就不一样了。
最后说一句我个人实际体会最深的事。做这个项目,真正拉开差距的不是代码量,而是对核心业务逻辑的理解深度。把健康菜谱生成这条主流程讲透,让老师觉得你是“设计者”而不是“搬运工”,比堆砌几十张表有用得多。如果你准备拿这个项目当毕设,花点时间把热量计算、菜谱筛选这些环节自己从头推一遍,等答辩的时候,你会感谢自己当初没偷这点懒。
