作为一个长期从事互联网应用开发的工程师,我最近完成了一个基于SpringBoot和Vue的美食推荐系统。这个项目源于一个常见的痛点:当我们在陌生城市寻找餐厅时,往往面临信息过载却难以找到真正符合口味的选择。传统的美食平台虽然提供了海量数据,但缺乏个性化的推荐能力。
这个系统通过整合用户行为数据、餐厅评价及菜品特征,实现了智能推荐功能。后端采用SpringBoot框架构建RESTful API,前端使用Vue.js实现响应式界面,数据库选用MySQL存储结构化数据。系统最核心的价值在于其推荐算法——基于用户历史行为和相似用户偏好的协同过滤算法,能够为每个用户生成个性化的美食推荐列表。
从技术架构来看,这是一个典型的前后端分离应用。后端负责业务逻辑处理和数据持久化,前端专注于用户交互体验,两者通过HTTP接口进行通信。这种架构不仅提高了开发效率,也使得系统更易于维护和扩展。
在项目初期,技术选型是至关重要的决策。经过对各种技术栈的评估,我们最终确定了以下技术组合:
后端选择SpringBoot主要基于以下几个考虑:
前端选择Vue.js则是因为:
数据库选用MySQL的原因是:
系统主要分为以下几个功能模块:
这种模块化设计遵循了单一职责原则,每个模块都有明确的边界和职责,便于团队协作和后期维护。
系统的数据模型设计直接影响了业务逻辑的实现和系统性能。经过多次迭代,我们确定了以下核心表结构:
**用户表(user_profile)**的设计考虑了信息安全和个人化需求:
**餐厅表(restaurant_info)**包含的关键字段:
**菜品评价表(dish_review)**的设计特点:
为了提高查询性能,我们实施了以下优化措施:
sql复制CREATE INDEX idx_restaurant_cuisine ON restaurant_info(cuisine_type);
CREATE INDEX idx_review_user ON dish_review(user_id);
sql复制ALTER TABLE dish_review MODIFY comment TEXT;
实现读写分离,将报表类查询路由到只读副本
使用连接池管理数据库连接,避免频繁创建销毁连接的开销
后端代码采用典型的分层架构:
这种分层设计使得各层职责清晰,便于单元测试和维护。以下是一个典型的控制器示例:
java复制@RestController
@RequestMapping("/api/restaurants")
public class RestaurantController {
@Autowired
private RestaurantService restaurantService;
@GetMapping
public ResponseEntity<List<Restaurant>> searchRestaurants(
@RequestParam(required = false) String cuisineType,
@RequestParam(required = false) Double minRating) {
List<Restaurant> restaurants = restaurantService.search(cuisineType, minRating);
return ResponseEntity.ok(restaurants);
}
@PostMapping
public ResponseEntity<Restaurant> createRestaurant(
@Valid @RequestBody Restaurant restaurant) {
Restaurant saved = restaurantService.create(restaurant);
return ResponseEntity.status(HttpStatus.CREATED).body(saved);
}
}
系统采用JWT(JSON Web Token)进行身份认证,主要流程如下:
安全配置类示例:
java复制@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/api/auth/**").permitAll()
.anyRequest().authenticated()
.and()
.addFilter(new JwtAuthenticationFilter(authenticationManager()))
.addFilter(new JwtAuthorizationFilter(authenticationManager()))
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
前端采用组件化开发模式,主要组件包括:
组件之间通过Props向下传递数据,通过Events向上传递用户交互。对于全局状态(如用户登录状态),使用Vuex进行管理。
餐厅搜索功能的实现展示了前后端协作的典型模式:
vue复制<template>
<div>
<search-filters @search="handleSearch"/>
<restaurant-list :restaurants="filteredRestaurants"/>
</div>
</template>
<script>
export default {
data() {
return {
filteredRestaurants: []
}
},
methods: {
async handleSearch(filters) {
try {
const response = await axios.get('/api/restaurants', {
params: filters
});
this.filteredRestaurants = response.data;
} catch (error) {
this.$notify.error('获取餐厅列表失败');
}
}
}
}
</script>
系统采用基于用户的协同过滤算法,主要步骤包括:
java复制public Map<Long, Map<Long, Double>> calculateUserSimilarities() {
// 获取所有用户评价数据
List<Review> reviews = reviewRepository.findAll();
// 构建用户-物品评分矩阵
Map<Long, Map<Long, Integer>> userItemMatrix = buildUserItemMatrix(reviews);
// 计算用户间余弦相似度
return computeCosineSimilarities(userItemMatrix);
}
java复制public List<Recommendation> generateRecommendations(Long userId) {
// 获取目标用户的相似用户
Map<Long, Double> similarUsers = findSimilarUsers(userId);
// 预测目标用户对未评分菜品的评分
Map<Long, Double> predictedRatings = predictRatings(userId, similarUsers);
// 按预测评分排序,返回TopN推荐
return sortAndFilter(predictedRatings, 10);
}
在实践中,我们发现并解决了几个性能问题:
系统采用Docker容器化部署,主要组件包括:
docker-compose.yml配置示例:
yaml复制version: '3'
services:
frontend:
image: nginx:alpine
ports:
- "8080:80"
volumes:
- ./dist:/usr/share/nginx/html
backend:
image: openjdk:11-jre
ports:
- "8081:8080"
environment:
- SPRING_DATASOURCE_URL=jdbc:mysql://mysql:3306/foodie
depends_on:
- mysql
mysql:
image: mysql:8.0
environment:
- MYSQL_ROOT_PASSWORD=secret
- MYSQL_DATABASE=foodie
volumes:
- mysql_data:/var/lib/mysql
volumes:
mysql_data:
为了确保系统稳定运行,我们配置了以下监控措施:
在实际开发和部署过程中,我们遇到了几个典型问题:
跨域问题:开发阶段前端访问后端API时出现CORS错误
java复制@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET", "POST", "PUT", "DELETE");
}
};
}
N+1查询问题:获取餐厅列表时产生过多SQL查询
java复制@EntityGraph(attributePaths = {"dishes"})
List<Restaurant> findByCuisineType(String cuisineType);
前端内存泄漏:切换路由时组件未正确销毁
javascript复制beforeDestroy() {
window.removeEventListener('resize', this.handleResize);
}
基于现有系统,可以考虑以下几个扩展方向:
这个项目从技术选型到最终实现,完整展示了如何构建一个现代化的Web应用。采用SpringBoot和Vue的组合,既能保证后端服务的稳定性,又能提供流畅的前端用户体验。特别是推荐算法的实现,为系统增加了真正的商业价值。