拓冰建站拓冰建站
首页 / 资讯中心 / 正文

校园美食推荐系统毕设全攻略:SpringBoot+Vue+推荐算法与论文设计

如果你正在准备毕业设计并且已经确定要做“校园周边美食推荐系统”这类题目那我建议你先想清楚一个问题你的系统凭什么比一个普通的信息管理系统更有竞争力每年毕业季校园美食推荐系统的数量非常多选题本身并不稀缺。SpringBoot、Vue、MySQL、推荐算法这些都已经是成熟技术难的不是“把这些技术拼起来”而是如何在论文和答辩中讲清楚两件事第一你的系统解决了什么真实问题第二你的推荐逻辑到底是怎么设计的。这篇文章会围绕一个毕设级别的校园周边美食推荐系统完整拆解后端SpringBoot、前端Vue、数据库设计、推荐算法实现、论文写作和答辩准备。你可以把它当作一套可落地的项目框架也可以挑其中一部分移植到自己的项目里。我要给的判断是这类系统真正的分数不在CRUD而在推荐逻辑的设计与讲解。1. 这类毕业设计真正难在哪先说一个容易被忽略的事实校园周边美食推荐系统的业务功能本质上还是用户、商家、美食、收藏、评分、评论这些模块的增删改查。如果只做到这一步项目能运行但没有任何记忆点。真正拉开差距的是推荐模块。为什么这么说因为推荐系统要处理的问题不是“把数据存进数据库然后查出来”而是“在用户没有明确搜索关键词时如何把合适的内容主动推送给他”。这涉及到用户行为数据的采集、用户偏好的分析、推荐结果的排序以及冷启动场景的处理。整个链路比普通的管理系统多了一层数据分析和算法设计的逻辑。另一个难点是数据从哪来。很多同学做这个题目时手边没有真实数据于是随便造几条测试数据结果推荐效果非常假。比较稳妥的做法是构建一个可复用的模拟数据生成逻辑比如随机生成100个用户、50家商家、200条美食记录再随机生成一批收藏和评分记录。这样既能验证推荐效果也能在论文里写清楚测试数据的来源和构造方式。同时推荐效果的评估也是答辩时容易被追问的地方。你不能只说自己“推荐了相似美食”还要能说出用什么指标衡量推荐效果。毕业设计一般不需要做完整的离线实验但至少要能说明你是用什么规则排序的准确率和覆盖率在测试集上大概是什么水平或者至少说清楚你的推荐结果有可解释性。所以这篇文章要解决的问题是怎么在毕业设计的时间范围内做一个“能跑、能讲、能过答辩”的校园周边美食推荐系统。它的核心不是堆技术栈而是把推荐链路设计清楚并且能用代码和论文把整个过程表达出来。2. 核心技术栈与推荐算法选型2.1 后端技术选型系统后端选择SpringBoot这是目前Java毕设使用最广的框架理由也很直接SpringBoot通过自动配置和Starter机制把SSM时代的繁琐配置大幅简化。开发者只需要引入相关依赖编写少量配置就能启动一个独立运行的Web服务内嵌Tomcat让部署也变得很轻量。持久层框架推荐使用MyBatis-Plus。如果你用过原生MyBatis一定体验过自己写大量Mapper XML的繁琐过程。MyBatis-Plus在MyBatis基础上提供了通用CRUD接口单表操作基本不用手写SQL分页插件也内置好了。这对于毕设项目来说省时间而且代码量减少后论文里贴代码也不会占太多篇幅。需要注意MyBatis-Plus不是银弹复杂的多表关联查询还是要自己写注解SQL或XML。数据库方面MySQL是首选成本低、资料多、环境搭建简单。表结构设计按第三章的规划来就行。2.2 前端技术选型前端选择Vue。Vue的学习曲线比React更平滑对于不常写前端的大四学生来说更加友好。这里建议使用Vue 3配合Element Plus组件库页面风格接近管理后台适合快速搭建。当然如果你对Vue 2和Element UI更熟使用Vue 2也完全没有问题本文讲到的是通用思路。前后端分离是毕设中最常见的架构。后端只提供JSON接口前端通过axios调用。项目结构上前端项目和后端项目是两个独立目录这也方便在论文中分别描述。2.3 推荐算法选型对比推荐算法是核心但不要一上来就追求复杂的深度学习模型。毕设的时间有限需要的是一个能讲清楚、能跑出效果、出现Bug时能自己排查的算法。下面是常见的推荐方案对比。方案原理优点缺点毕设适配度基于内容推荐根据用户喜欢过的食物的分类、标签推荐同类食物可解释性强、没有冷启动依赖推荐结果单一缺乏新颖性高基于用户的协同过滤找到口味相似的用户推荐他们喜欢的食物能发现不同分类的新食物用户行为少时效果差中高基于物品的协同过滤找到相似食物推荐和用户喜欢过的食物相似的推荐结果稳定计算食物相似度需要一定数据量中基于规则推荐按热度、评分、距离等规则排序实现简单、稳定个性化程度低中混合推荐多种策略结合效果均衡实现复杂度略高高对于这个毕设推荐采用“基于内容推荐 热度兜底”作为第一版实现如果学有余力再加入基础的基于用户的协同过滤。这样既能保证冷启动用户有推荐结果又有个性化逻辑论文里也有两个可对比的策略内容不会显得单薄。3. 系统功能模块与整体架构设计3.1 用户角色设计校园美食推荐系统的用户角色分为三类普通学生用户、商家、系统管理员。普通用户是系统的主要服务对象能完成注册登录、浏览美食、搜索美食、查看美食详情、收藏、评分、评论以及查看个性化推荐列表。商家的功能则会少一些主要维护自己店铺和菜品的信息查看用户对自己菜品的评价。管理员负责整体平台的数据维护包括用户管理、商家审核、分类管理、数据统计等。在毕设开发中商家和管理员模块可以精简但普通用户的主流程必须完整。因为论文中的用例分析、功能测试主要围绕主流程展开主流程越完整测试章节越好写。3.2 功能模块划分系统按功能可以拆成以下模块用户认证模块注册、登录、退出、密码加密存储。美食浏览模块美食列表、分类筛选、关键字搜索、美食详情。用户行为模块收藏、评分1到5分、评论。个性化推荐模块根据用户行为生成推荐列表、热门美食推荐。商家管理模块美食上下架、信息维护。管理员模块用户管理、分类管理、数据统计。这里面推荐模块和用户行为模块是核心也是论文创新点所在。实际开发时建议先完成美食浏览和改进再补行为采集最后做推荐列表顺序不能反。3.3 系统架构分层系统逻辑上采用三层架构Controller层接收前端请求参数校验返回统一响应体。Service层业务逻辑处理推荐算法的核心代码也放在这一层。Mapper层数据库操作基于MyBatis-Plus完成。数据流向为前端Vue发起请求 → 后端Controller接收 → Service处理业务与推荐逻辑 → Mapper访问MySQL → 数据逐层返回前端展示。这种分层方式清晰答辩时画架构图也会很方便。4. 数据库表结构设计数据库设计的目标是支撑上面的功能模块。推荐系统核心表有这些。4.1 数据表划分表名说明主要字段t_user用户表id, username, password, nickname, avatar, create_timet_category美食分类表id, name, sortt_merchant商家表id, name, address, phone, business_hourst_food美食表id, category_id, merchant_id, name, price, image, tags, rating, descriptiont_rating评分表id, user_id, food_id, score, create_timet_collection收藏表id, user_id, food_id, create_timet_comment评论表id, user_id, food_id, content, create_time其中t_rating和t_collection是推荐算法的数据来源。t_food中的tags字段用于存储“川菜”“辣”“高性价比”这类标签推荐时会用到。4.2 建表SQL示例这里给出核心表的建表SQLMySQL 5.7及以上版本都可以使用。CREATE TABLE t_user ( id bigint(20) NOT NULL AUTO_INCREMENT, username varchar(50) NOT NULL UNIQUE COMMENT 用户名, password varchar(100) NOT NULL COMMENT 密码加密存储, nickname varchar(50) DEFAULT NULL COMMENT 昵称, avatar varchar(255) DEFAULT NULL COMMENT 头像, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表; CREATE TABLE t_category ( id bigint(20) NOT NULL AUTO_INCREMENT, name varchar(50) NOT NULL COMMENT 分类名称, sort int(11) DEFAULT 0 COMMENT 排序号, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT美食分类表; CREATE TABLE t_merchant ( id bigint(20) NOT NULL AUTO_INCREMENT, name varchar(100) NOT NULL COMMENT 商家名称, address varchar(255) DEFAULT NULL COMMENT 地址, phone varchar(20) DEFAULT NULL COMMENT 联系电话, business_hours varchar(100) DEFAULT NULL COMMENT 营业时间, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT商家表; CREATE TABLE t_food ( id bigint(20) NOT NULL AUTO_INCREMENT, category_id bigint(20) DEFAULT NULL COMMENT 分类ID, merchant_id bigint(20) DEFAULT NULL COMMENT 商家ID, name varchar(100) NOT NULL COMMENT 美食名称, price decimal(10,2) DEFAULT 0.00 COMMENT 价格, image varchar(255) DEFAULT NULL COMMENT 图片地址, tags varchar(255) DEFAULT NULL COMMENT 标签多个用逗号分隔, rating decimal(3,1) DEFAULT 5.0 COMMENT 综合评分, description varchar(500) DEFAULT NULL COMMENT 描述, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT美食表; CREATE TABLE t_rating ( id bigint(20) NOT NULL AUTO_INCREMENT, user_id bigint(20) NOT NULL, food_id bigint(20) NOT NULL, score int(11) NOT NULL COMMENT 评分1-5, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_user_id (user_id), KEY idx_food_id (food_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户评分表; CREATE TABLE t_collection ( id bigint(20) NOT NULL AUTO_INCREMENT, user_id bigint(20) NOT NULL, food_id bigint(20) NOT NULL, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_user_id (user_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户收藏表;用户表里要注意password字段的长度。很多同学使用varchar(20)然后直接明文存储这样做既不合规答辩也会被老师质疑。这里建议使用BCrypt加密字段长度留到100。评分表和收藏表一定要加索引因为推荐算法的核心查询都是按user_id去查行为记录不加索引时数据量稍微变大就会出现明显的性能问题。5. 后端SpringBoot接口实现5.1 项目初始化与依赖配置使用Spring Initializr创建项目或直接在IDE中新建Spring Boot工程。项目结构如下food-recommend-backend ├── src/main/java/com/example/food │ ├── controller │ ├── entity │ ├── mapper │ ├── service │ └── FoodApplication.java └── src/main/resources └── application.ymlpom.xml核心依赖如下。版本号以你实际初始化的项目为准不建议直接照抄。dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId scoperuntime/scope /dependency dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.5/version /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency /dependencies使用MyBatis-Plus时有一个比较容易踩的坑版本兼容。MyBatis-Plus 3.5.x和不同版本的SpringBoot组合时偶尔会出现Invalid value type for attribute factoryBeanObjectType这类错误。遇到这种情况优先检查MyBatis-Plus版本是否和你项目的SpringBoot版本匹配。5.2 application.yml配置server: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/food_recommend?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 123456 mybatis-plus: configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImpl global-config: db-config: id-type: automap-underscore-to-camel-case这个配置很关键开启后数据库里的create_time会自动映射到Java实体里的createTime字段省去大量手写TableField注解的麻烦。5.3 实体类与Mapper实体类以Food为例。package com.example.food.entity; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import java.math.BigDecimal; Data TableName(t_food) public class Food { TableId(type IdType.AUTO) private Long id; private Long categoryId; private Long merchantId; private String name; private BigDecimal price; private String image; private String tags; private BigDecimal rating; private String description; }Mapper接口继承了BaseMapper之后就自动拥有了按主键查、按条件查、分页查等能力。package com.example.food.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.example.food.entity.Food; public interface FoodMapper extends BaseMapperFood { }5.4 统一响应体封装推荐一个简单的统一返回结构这样前端处理时只需要判断code字段。package com.example.food.common; import lombok.Data; 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(success); result.setData(data); return result; } public static T ResultT error(String message) { ResultT result new Result(); result.setCode(500); result.setMessage(message); return result; } }这样做的好处是后面加拦截器做登录鉴权时返回格式不会乱。5.5 美食浏览Controller美食浏览是用户进入系统后的第一个页面接口一般包含分类查询、按条件搜索等。package com.example.food.controller; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.example.food.common.Result; import com.example.food.entity.Food; import com.example.food.mapper.FoodMapper; import org.springframework.web.bind.annotation.*; import java.util.List; RestController RequestMapping(/api/food) public class FoodController { private final FoodMapper foodMapper; public FoodController(FoodMapper foodMapper) { this.foodMapper foodMapper; } GetMapping(/list) public ResultListFood list(RequestParam(required false) Long categoryId, RequestParam(required false) String keyword) { LambdaQueryWrapperFood wrapper new LambdaQueryWrapper(); if (categoryId ! null) { wrapper.eq(Food::getCategoryId, categoryId); } if (keyword ! null !keyword.isEmpty()) { wrapper.like(Food::getName, keyword).or().like(Food::getTags, keyword); } return Result.success(foodMapper.selectList(wrapper)); } GetMapping(/detail) public ResultFood detail(RequestParam Long id) { return Result.success(foodMapper.selectById(id)); } }注意wrapper.like(Food::getName, keyword).or().like(Food::getTags, keyword)这种写法在和其他条件组合时很容易出现SQL拼接问题。如果你同时在前面加了categoryId条件这里会变成WHERE category_id ? OR name LIKE ? OR tags LIKE ?逻辑不对。更好的是用and方法包一层。遇到这种情况建议在本地日志中查看SQL并调整这也是排查Bug时要养成的习惯。6. 前端Vue页面与接口对接6.1 创建Vue项目使用Vite创建Vue 3项目。npm create vitelatest food-frontend -- --template vue cd food-frontend npm install npm install vue-router4 axios element-plus npm run dev如果你的网络环境安装依赖比较慢可以配置镜像源。项目创建成功后前端目录结构大概是food-frontend ├── src │ ├── api │ ├── components │ ├── router │ ├── views │ └── App.vue6.2 路由配置// 文件路径src/router/index.js import { createRouter, createWebHistory } from vue-router const routes [ { path: /, redirect: /home }, { path: /home, name: Home, component: () import(../views/Home.vue) }, { path: /recommend, name: Recommend, component: () import(../views/Recommend.vue) }, { path: /food/:id, name: FoodDetail, component: () import(../views/FoodDetail.vue) } ] const router createRouter({ history: createWebHistory(), routes }) export default router6.3 axios请求封装// 文件路径src/api/request.js import axios from axios import { ElMessage } from element-plus const request axios.create({ baseURL: http://localhost:8080/api, timeout: 10000 }) request.interceptors.response.use( response { // 假设后端返回结构是 { code, message, data } if (response.data.code 200) { return response.data.data } ElMessage.error(response.data.message || 请求失败) return Promise.reject(new Error(response.data.message)) }, error { ElMessage.error(网络异常请检查后端服务是否启动) return Promise.reject(error) } ) export default request6.4 调用推荐接口的API// 文件路径src/api/food.js import request from ./request export function getRecommendList(userId) { return request.get(/recommend/list, { params: { userId } }) } export function getFoodList(params) { return request.get(/food/list, { params }) } export function addRating(userId, foodId, score) { return request.post(/rating/add, { userId, foodId, score }) }前端代码不用写得特别复杂推荐系统前端页面的核心价值在于把后端返回的推荐结果可视化比如使用图片卡片列表配上价格和评分再提供一个“换一批”按钮重新请求推荐接口。7. 推荐算法核心逻辑实现这一章是整个项目的加分项值得认真写。7.1 推荐流程设计后端推荐接口的处理流程可以分为以下步骤获取当前用户ID。查询用户的历史行为数据评分记录、收藏记录。如果用户没有任何行为返回热门美食列表。如果用户有行为提取用户偏好的分类和标签。在偏好分类下查找候选美食排除已经消费或已经评分的食物。综合计算排序分偏好相似度、食物评分、热度。这里的核心思想是先缩小候选集再排序。这样做的好处是效率高逻辑也容易讲清楚。7.2 推荐服务核心代码package com.example.food.service; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.example.food.entity.Food; import com.example.food.entity.Rating; import com.example.food.mapper.FoodMapper; import com.example.food.mapper.RatingMapper; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; Service public class RecommendService { private final FoodMapper foodMapper; private final RatingMapper ratingMapper; public RecommendService(FoodMapper foodMapper, RatingMapper ratingMapper) { this.foodMapper foodMapper; this.ratingMapper ratingMapper; } public ListFood recommend(Long userId, int limit) { // 1. 查询当前用户的评分记录 ListRating ratings ratingMapper.selectList( new LambdaQueryWrapperRating() .eq(Rating::getUserId, userId)); // 2. 冷启动处理没有评分记录时返回热门食物 if (ratings.isEmpty()) { return getHotFood(limit); } // 3. 统计用户偏好的分类ID集合评分4的食物分类出现次数多的优先 MapLong, Integer categoryCount new HashMap(); for (Rating rating : ratings) { if (rating.getScore() 4) { Food food foodMapper.selectById(rating.getFoodId()); if (food ! null food.getCategoryId() ! null) { categoryCount.put(food.getCategoryId(), categoryCount.getOrDefault(food.getCategoryId(), 0) 1); } } } // 4. 如果用户没有高分记录退回到按平均评分推荐 if (categoryCount.isEmpty()) { return getHotFood(limit); } // 5. 按偏好分类出现次数排序取top分类 ListLong categoryIds categoryCount.entrySet().stream() .sorted((e1, e2) - e2.getValue().compareTo(e1.getValue())) .map(Map.Entry::getKey) .limit(2) .collect(Collectors.toList()); // 6. 在偏好分类下查询候选美食排除已评分的 ListLong ratedFoodIds ratings.stream() .map(Rating::getFoodId) .collect(Collectors.toList()); ListFood candidates foodMapper.selectList( new LambdaQueryWrapperFood() .in(Food::getCategoryId, categoryIds) .last(limit 50)); ListFood result candidates.stream() .filter(food - !ratedFoodIds.contains(food.getId())) .sorted((f1, f2) - { // 排序综合评分高的优先评分相同看价格低的优先 int compare f2.getRating().compareTo(f1.getRating()); if (compare ! 0) { return compare; } return f1.getPrice().compareTo(f2.getPrice()); }) .limit(limit) .collect(Collectors.toList()); // 7. 如果候选不足用热门食物补足 if (result.size() limit) { ListFood hotFood getHotFood(limit * 2); for (Food food : hotFood) { if (result.size() limit) { break; } if (!result.contains(food) !ratedFoodIds.contains(food.getId())) { result.add(food); } } } return result; } private ListFood getHotFood(int limit) { return foodMapper.selectList( new LambdaQueryWrapperFood() .orderByDesc(Food::getRating) .last(limit limit)); } }这段代码有两个值得在论文里展开的细节第一为什么用分类出现次数作为用户偏好因为评分数据比较稀疏用户可能只评过三五次直接计算标签权重容易过拟合。按分类聚合更稳。第二为什么排序时综合评分优先因为推荐系统的第一阶段目标不是“猜得最准”而是“不出错”。把热门高分食物推荐出去用户就算不惊喜至少不会反感。7.3 协同过滤的思路进阶如果基础版推荐完成后还有时间可以在论文里补充一个基于用户协同过滤的改良。思路是找出和当前用户口味最相似的其他用户将这些用户评分高的食物推荐给当前用户。相似度可以使用余弦相似度把每个用户对食物的评分看作一个向量向量维度是食物ID值是对应的评分。毕设实现时可以简化只取评分4的成交记录用Jaccard系数计算相似度也就是两个用户共同高分美食数量除以两个用户高分美食集合的并集数量。这么做代码量小论文里也能说清楚。7.4 冷启动与兜底策略冷启动是推荐系统必问的问题。这个系统的方案是如果用户没有评分记录就返回综合评分最高的一批食物并保证来自不同分类以避免结果过于单一。另一个兜底策略是推荐结果不足时用热门食物补齐。这个策略在代码里已经体现答辩时可以直接从代码里指给评委看很有说服力。8. 系统运行与功能验证8.1 启动后端在项目根目录执行。mvn spring-boot:run看到类似下面的日志就表示启动成功。Started FoodApplication in 2.3 seconds8.2 启动前端在food-frontend目录下执行。npm run dev浏览器访问Vite输出的本地地址默认是http://localhost:5173。8.3 功能验证流程推荐以下测试路径验证系统完整性。注册新用户并登录。浏览美食列表打开几个美食详情页。对至少5种食物进行评分其中3种打4分以上。打开推荐页面观察结果是否包含你打高分的食物所属分类。换一个新注册的空用户查看推荐结果是否为热门食物列表。如果步骤4推荐结果里出现了你从未浏览过但分类相似的食材说明推荐逻辑生效。如果步骤5推荐结果全是评分很高的食物说明冷启动兜底生效。8.4 通过接口验证除了页面验证也可以直接调用接口测试。curl http://localhost:8080/api/recommend/list?userId1返回结果中应该是一个JSON数组数组元素即推荐的美食列表。9. 常见问题与排查方法问题现象可能原因排查方式解决方案后端启动报数据库连接失败MySQL未启动或账号密码错误检查application.yml配置命令行连接测试确认MySQL已启动修正url/username/password前端页面请求接口404后端接口路径不匹配打开浏览器F12控制台查看请求URL核对Controller的RequestMapping与实际请求路径注意baseURL使用MyBatis-Plus报Invalid value type错误依赖版本与SpringBoot不兼容查看启动日志中的Caused by降低或升级MyBatis-Plus版本统一使用Spring Boot BOM管理推荐结果总是热门食物当前用户没有评分记录推荐代码里categoryCount为空查t_rating表确认数据在后端打印日志给测试用户补充评分数据调整冷启动判断逻辑前端跨域报错后端未配置CORS查看浏览器控制台CORS错误在Controller添加CrossOrigin或配置全局CorsFilter分页查询返回总数为0MyBatis-Plus分页插件未配置检查是否有MybatisPlusInterceptor的Bean添加分页插件配置类中文乱码数据库连接未指定utf8检查url中characterEncoding参数使用characterEncodingutf8并保证表为utf8mb4这里有一个容易忽略的问题如果前后端在不同端口运行比如前端5173、后端8080跨域问题是无法避免的。最快的方式是在后端写一个全局CORS配置或者直接在Controller上加CrossOrigin。如果要写进论文使用CorsFilter配置类会显得更规范。package com.example.food.config; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/**) .allowedOrigins(http://localhost:5173) .allowedMethods(GET, POST, PUT, DELETE) .allowedHeaders(*) .maxAge(3600); } }10. 论文写作与答辩准备建议10.1 论文目录结构毕业论文一般包含六到七章。这个系统的论文章节可以这样安排。第一章 绪论选题背景、研究意义、国内外研究现状、论文结构。第二章 相关技术介绍SpringBoot、Vue、MyBatis-Plus、推荐算法。第三章 系统需求分析功能需求、非功能需求、用例分析。第四章 系统设计总体架构、功能模块设计、数据库设计、推荐算法设计。第五章 系统实现各模块的核心代码与界面截图。第六章 系统测试功能测试用例、测试结果、推荐效果分析。第七章 总结与展望项目总结、不足、改进方向。10.2 开题报告的写作侧重点开题报告一般不需要写代码重点是研究内容和方法。针对这个题目研究内容可以写以下四点用户行为数据采集与存储设计、基于分类标签的用户偏好建模、冷启动推荐策略、前后端分离架构下的系统集成。研究方法可以写文献研究法、原型设计法、对比实验法。最好在题目中明确指出“使用基于内容的推荐算法”或“融合多种推荐策略”把算法写进标题会让研究内容更聚焦。10.3 答辩高频问题根据往年经验评委老师最爱问以下几个角度的问题。你的推荐算法为什么选择基于内容的推荐冷启动问题怎么解决用户没有评分数据时系统怎么办推荐效果用什么指标衡量数据量增大时系统的性能瓶颈在哪里回答这些问题时要直接引用自己的设计和代码。比如问“为什么不用协同过滤”可以回答“协同过滤依赖足够的用户评分数据而校园场景下的新用户评分非常稀疏所以我先使用基于内容推荐作为主策略将协同过滤作为后续扩展方向。”这个回答既合理又诚实。11. 总结与后续学习方向这套校园周边美食推荐系统从功能角度看它覆盖了用户、商家、管理员三类角色完成了美食浏览、评分、收藏、评论和推荐的主要链路。从毕设角度看它的架构清晰、算法有讲解空间、测试路径完整能在开题报告和论文中找到对应的内容支撑。如果你打算继续深入推荐的改进方向有三个第一是把推荐策略从“基于内容推荐”升级为“基于内容协同过滤的混合推荐”让个性化效果更好第二是加入Redis缓存把热门美食列表和推荐结果缓存起来提升接口响应速度第三是增加用户行为埋点把浏览时长、点击次数也纳入偏好计算让推荐结果更准确。实际做项目时不要一上来就追求代码量。先把用户行为数据这个基础建好再围绕数据设计推荐逻辑最后用页面展示效果。这个开发顺序比什么都重要。建议收藏这篇文章动手开发时对照着搭项目能省下不少排查问题的时间。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门