1. 项目概述
这个基于Java SpringBoot+Vue3+MyBatis的高校学生饮食推荐系统,是一个典型的前后端分离架构应用。系统通过分析学生的饮食偏好、消费习惯和营养需求,为高校学生提供个性化的餐饮推荐服务。我在开发过程中发现,这种系统不仅能帮助学生解决"今天吃什么"的难题,还能为食堂管理者提供数据支持,优化菜品供应结构。
系统采用MySQL作为数据库,充分发挥了SpringBoot的快速开发特性和Vue3的响应式优势。前后端分离的设计让开发和部署更加灵活,MyBatis作为ORM框架则简化了数据库操作。下面我将从技术选型、系统设计和实现细节三个方面,详细解析这个项目的开发过程。
2. 技术栈选型分析
2.1 后端技术选择
SpringBoot作为后端框架的选择主要基于以下几个考虑:
- 自动配置特性大幅减少了XML配置
- 内嵌Tomcat服务器简化了部署流程
- 丰富的starter依赖可以快速集成各种功能模块
- 完善的生态和社区支持
在实际开发中,我们特别使用了Spring Security进行权限控制,Spring Cache管理缓存,以及Spring Data JPA的部分特性辅助MyBatis。
2.2 前端框架考量
Vue3相比Vue2有几个显著优势:
- Composition API提供了更好的代码组织方式
- 更小的打包体积和更快的渲染速度
- 更好的TypeScript支持
- 更灵活的逻辑复用方式
我们特别利用了Vue3的setup语法糖、ref和reactive响应式API,以及Pinia状态管理库。Element Plus作为UI组件库,提供了丰富的预制组件。
2.3 数据库选型
MySQL 8.0作为关系型数据库的选择理由:
- 高校饮食数据具有明确的结构化特征
- 事务支持确保数据一致性
- 成熟的索引机制优化查询性能
- 丰富的社区资源和工具支持
我们在表设计时特别注意了范式化,同时针对热点查询做了适当的反范式优化。
3. 系统架构设计
3.1 整体架构
系统采用经典的三层架构:
- 表现层:Vue3前端应用
- 业务逻辑层:SpringBoot后端服务
- 数据访问层:MyBatis+MySQL
前后端通过RESTful API交互,使用JWT进行身份认证。特别设计了API网关统一处理跨域、鉴权和限流。
3.2 数据库设计
核心表包括:
- 用户表(student_info)
- 菜品表(dish_info)
- 食堂表(canteen_info)
- 消费记录表(consumption_record)
- 评价表(rating)
- 推荐记录表(recommendation)
我们建立了适当的索引和外键约束,同时使用触发器维护部分统计字段。
3.3 推荐算法设计
系统采用混合推荐策略:
- 基于内容的推荐:分析菜品特征相似度
- 协同过滤:根据相似用户的口味推荐
- 热门推荐:近期受欢迎的菜品
- 随机推荐:增加多样性
算法实现上使用了Java的Mahout库,并针对高校场景做了定制优化。
4. 关键功能实现
4.1 用户认证模块
采用JWT实现无状态认证:
// 生成Token示例 public String generateToken(UserDetails userDetails) { Map<String, Object> claims = new HashMap<>(); return Jwts.builder() .setClaims(claims) .setSubject(userDetails.getUsername()) .setIssuedAt(new Date(System.currentTimeMillis())) .setExpiration(new Date(System.currentTimeMillis() + JWT_TOKEN_VALIDITY * 1000)) .signWith(SignatureAlgorithm.HS512, secret) .compact(); }前端使用axios拦截器自动携带Token:
axios.interceptors.request.use(config => { const token = localStorage.getItem('token') if (token) { config.headers.Authorization = `Bearer ${token}` } return config })4.2 推荐引擎实现
核心推荐逻辑:
public List<Dish> recommendDishes(Long studentId) { // 获取用户特征 StudentPreference preference = preferenceMapper.selectByStudentId(studentId); // 基于内容的推荐 List<Dish> contentBased = contentBasedRecommender.recommend(preference); // 协同过滤推荐 List<Dish> cfBased = cfRecommender.recommend(studentId); // 合并结果并去重 return mergeAndDeduplicate(contentBased, cfBased); }4.3 前后端数据交互
典型API设计示例:
@RestController @RequestMapping("/api/dishes") public class DishController { @Autowired private DishService dishService; @GetMapping("/recommend") public ResponseEntity<List<DishDTO>> getRecommendations( @RequestParam Long studentId, @RequestParam(defaultValue = "10") int limit) { return ResponseEntity.ok(dishService.getRecommendations(studentId, limit)); } }前端调用示例:
const fetchRecommendations = async (studentId) => { try { const res = await axios.get('/api/dishes/recommend', { params: { studentId } }); return res.data; } catch (error) { console.error('获取推荐失败:', error); return []; } }5. 性能优化实践
5.1 数据库优化
- 索引优化:为常用查询字段建立复合索引
CREATE INDEX idx_dish_canteen ON dish_info(canteen_id, category);- 查询优化:避免SELECT *,使用分页
@Select("SELECT id, name, price FROM dish_info WHERE canteen_id = #{canteenId} LIMIT #{offset}, #{limit}") List<Dish> selectByCanteen(@Param("canteenId") Long canteenId, @Param("offset") int offset, @Param("limit") int limit);- 缓存策略:使用Redis缓存热点数据
5.2 前端性能优化
- 组件懒加载:
const RecommendList = defineAsyncComponent(() => import('./components/RecommendList.vue') )- 图片懒加载:
<img v-lazy="dish.imageUrl" alt="菜品图片">- API请求合并与防抖
5.3 推荐算法优化
- 离线计算:定时任务预先计算推荐结果
- 增量更新:用户行为实时更新推荐模型
- 结果缓存:缓存推荐结果减少重复计算
6. 安全防护措施
6.1 SQL注入防护
MyBatis严格使用#{}参数绑定:
<select id="selectByConditions" resultType="Dish"> SELECT * FROM dish_info WHERE name LIKE CONCAT('%', #{name}, '%') AND price BETWEEN #{minPrice} AND #{maxPrice} </select>6.2 XSS防护
前端使用DOMPurify净化输入:
import DOMPurify from 'dompurify'; const clean = DOMPurify.sanitize(userInput);后端统一过滤敏感字符:
public String filterXSS(String value) { if (value == null) return null; return value.replaceAll("<", "<") .replaceAll(">", ">"); }6.3 CSRF防护
虽然使用JWT后CSRF风险降低,但仍采取了以下措施:
- 关键操作需要二次确认
- 敏感接口使用POST而非GET
- 设置SameSite cookie属性
7. 部署与监控
7.1 多环境配置
SpringBoot profiles配置不同环境:
# application-dev.yml server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/campus_food_dev7.2 容器化部署
使用Docker编排:
# 后端Dockerfile示例 FROM openjdk:11 COPY target/campus-food.jar app.jar ENTRYPOINT ["java","-jar","/app.jar"]7.3 监控方案
- Spring Boot Actuator提供健康检查
- Prometheus + Grafana监控系统指标
- ELK日志收集分析
8. 开发中的经验总结
8.1 前后端协作经验
- 使用Swagger维护API文档
- 定义统一的响应格式
{ "code": 200, "message": "success", "data": {} }- 约定错误代码规范
8.2 性能调优心得
- 数据库查询N+1问题解决:
// 使用MyBatis的@SelectProvider实现联合查询 @SelectProvider(type = DishSqlProvider.class, method = "selectWithCanteen") List<DishWithCanteen> selectWithCanteen();- Vue组件性能优化:
- 合理使用v-if和v-show
- 避免不必要的响应式数据
- 使用computed缓存计算结果
8.3 推荐算法改进方向
- 引入时间因素:考虑早中晚餐不同需求
- 天气影响:不同天气推荐不同菜品
- 社交因素:好友喜欢的菜品加权推荐
9. 常见问题解决方案
9.1 跨域问题
SpringBoot配置CORS:
@Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("*") .allowedMethods("*") .allowedHeaders("*"); } }9.2 页面刷新404
Vue Router配置:
const router = createRouter({ history: createWebHistory(), routes, }) // 后端需要配置所有路由指向index.html9.3 MyBatis动态SQL
使用