1. 项目概述
这个校园周边美食探索与分享平台是一个典型的Web应用系统,采用前后端分离架构。前端使用Vue.js框架实现响应式用户界面,后端基于SpringBoot构建RESTful API服务,数据存储采用MySQL关系型数据库。系统主要功能包括美食信息展示、用户点评、收藏分享、位置导航等模块,为校园师生提供周边餐饮信息的发现与交流渠道。
作为毕业设计项目,它完整覆盖了从需求分析、系统设计、编码实现到测试部署的全流程。项目源码结构清晰,包含详细注释,配套的数据库脚本、毕业论文和部署文档使其具有很好的教学参考价值。特别适合计算机相关专业学生学习现代Web开发技术栈的实际应用。
2. 技术架构解析
2.1 后端技术选型
SpringBoot 2.x作为后端框架,主要基于以下考虑:
- 自动配置特性大幅简化了Spring应用的初始搭建和开发过程
- 内嵌Tomcat服务器,无需额外部署WAR包
- 完善的starter依赖管理,轻松整合MyBatis、Redis等组件
- 提供actuator端点方便监控应用状态
数据库操作层采用MyBatis-Plus,相比原生MyBatis:
- 内置通用Mapper和Service,减少30%以上的样板代码
- 强大的条件构造器简化复杂查询编写
- 分页插件自动处理物理分页逻辑
- 支持Lambda表达式,避免硬编码字段名
2.2 前端技术方案
Vue 3.x作为前端框架的优势体现在:
- 组合式API使代码组织更灵活
- 基于Proxy的响应式系统性能更优
- 单文件组件(SFC)实现关注点分离
- 丰富的生态系统(Vuex、Vue Router等)
项目还使用了以下关键前端技术:
- Element Plus:提供丰富的UI组件
- Axios:处理HTTP请求
- ECharts:可视化评分数据
- 腾讯地图JavaScript API:实现位置展示与导航
2.3 数据库设计
MySQL 8.0作为关系型数据库,主要表结构包括:
sql复制CREATE TABLE `restaurant` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL,
`address` varchar(100) NOT NULL,
`latitude` decimal(10,7) NOT NULL,
`longitude` decimal(10,7) NOT NULL,
`avg_price` decimal(10,2) DEFAULT NULL,
`cover_url` varchar(255) DEFAULT NULL,
`open_hours` varchar(100) DEFAULT NULL,
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `review` (
`id` int NOT NULL AUTO_INCREMENT,
`user_id` int NOT NULL,
`restaurant_id` int NOT NULL,
`content` text NOT NULL,
`rating` tinyint NOT NULL,
`photo_urls` json DEFAULT NULL,
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_restaurant` (`restaurant_id`),
KEY `idx_user` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
3. 核心功能实现
3.1 美食发现模块
采用地图+列表双模式展示:
- 地图模式使用腾讯地图API实现标记点聚合
- 列表支持多种排序方式(距离、评分、价格)
- 智能推荐算法基于用户历史行为分析
关键实现代码片段:
java复制// 餐厅搜索服务层实现
@Service
@RequiredArgsConstructor
public class RestaurantSearchServiceImpl implements RestaurantSearchService {
private final RestaurantMapper restaurantMapper;
@Override
public Page<RestaurantVO> search(RestaurantSearchDTO dto) {
LambdaQueryWrapper<Restaurant> wrapper = Wrappers.lambdaQuery();
if (StringUtils.isNotBlank(dto.getKeyword())) {
wrapper.like(Restaurant::getName, dto.getKeyword())
.or().like(Restaurant::getAddress, dto.getKeyword());
}
if (dto.getCategoryId() != null) {
wrapper.eq(Restaurant::getCategoryId, dto.getCategoryId());
}
// 距离排序特殊处理
if ("distance".equals(dto.getSortBy()) && dto.getLat() != null && dto.getLng() != null) {
String distanceExpr = "ST_Distance_Sphere(point(longitude, latitude), point(%f, %f))";
wrapper.last(String.format("ORDER BY " + distanceExpr, dto.getLng(), dto.getLat()));
}
Page<Restaurant> page = restaurantMapper.selectPage(
new Page<>(dto.getPageNum(), dto.getPageSize()), wrapper);
return page.convert(this::convertToVO);
}
}
3.2 用户点评系统
实现要点:
- 富文本编辑支持图文混排
- 敏感词过滤机制
- 评分统计实时更新
- 照片上传压缩处理
前端关键代码:
vue复制<template>
<div class="review-editor">
<el-rate v-model="form.rating" />
<el-input
v-model="form.content"
type="textarea"
:rows="4"
placeholder="分享你的用餐体验..."
show-word-limit
maxlength="500"
/>
<image-uploader v-model="form.images" />
<div class="actions">
<el-button @click="cancel">取消</el-button>
<el-button type="primary" @click="submit">提交评价</el-button>
</div>
</div>
</template>
<script setup>
const emit = defineEmits(['submit', 'cancel']);
const form = reactive({
rating: 5,
content: '',
images: []
});
const submit = async () => {
if (!form.content.trim()) {
return ElMessage.error('请填写评价内容');
}
try {
await submitReview(form);
emit('submit');
ElMessage.success('评价提交成功');
} catch (err) {
ElMessage.error(err.message);
}
};
</script>
4. 项目部署实践
4.1 后端部署要点
- 生产环境配置:
yaml复制# application-prod.yml
server:
port: 8080
servlet:
context-path: /api
spring:
datasource:
url: jdbc:mysql://mysql-prod:3306/foodie?useSSL=false&characterEncoding=utf8
username: ${DB_USER}
password: ${DB_PASSWORD}
redis:
host: redis-prod
port: 6379
profiles:
active: prod
- Docker容器化部署:
dockerfile复制FROM openjdk:11-jre
WORKDIR /app
COPY target/foodie-backend-1.0.0.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java","-jar","app.jar"]
4.2 前端部署方案
- Nginx配置示例:
nginx复制server {
listen 80;
server_name foodie.example.com;
location / {
root /usr/share/nginx/html;
index index.html;
try_files $uri $uri/ /index.html;
}
location /api {
proxy_pass http://backend:8080/api;
proxy_set_header Host $host;
}
}
- 性能优化措施:
- 开启Gzip压缩
- 配置HTTP缓存头
- 使用CDN分发静态资源
- 启用Brotli压缩(需要Nginx 1.15+)
5. 开发经验总结
5.1 前后端协作实践
- API设计规范:
- 统一响应格式:
json复制{
"code": 200,
"message": "success",
"data": {...},
"timestamp": 1630000000000
}
- 使用Swagger UI维护接口文档
- 版本控制通过URL路径(/api/v1/)实现
- 跨域问题解决方案:
java复制@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedHeaders("*")
.maxAge(3600);
}
}
5.2 性能优化技巧
- 数据库层面:
- 为常用查询字段添加索引
- 使用连接池(HikariCP)并合理配置参数
- 批量操作代替循环单条处理
- 缓存策略:
java复制@Cacheable(value = "restaurants", key = "#id")
public RestaurantVO getById(Integer id) {
return restaurantMapper.selectById(id);
}
@CacheEvict(value = "restaurants", key = "#restaurant.id")
public void updateRestaurant(Restaurant restaurant) {
restaurantMapper.updateById(restaurant);
}
- 前端优化:
- 路由懒加载
- 组件按需引入
- 图片懒加载
- 虚拟滚动长列表
6. 常见问题排查
6.1 数据库连接问题
症状:应用启动时报"Communications link failure"
排查步骤:
- 检查MySQL服务是否运行
- 验证连接字符串中的主机名、端口
- 确认用户名密码正确
- 检查网络连通性(telnet mysql_host 3306)
- 查看MySQL错误日志
6.2 跨域请求失败
典型错误:No 'Access-Control-Allow-Origin' header
解决方案:
- 确保后端已配置CORS
- 检查前端请求是否携带必要头信息
- 复杂请求需处理OPTIONS预检请求
- 开发环境可配置代理:
javascript复制// vue.config.js
module.exports = {
devServer: {
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true
}
}
}
}
6.3 文件上传大小限制
SpringBoot默认限制文件上传大小为1MB,需要调整配置:
yaml复制spring:
servlet:
multipart:
max-file-size: 10MB
max-request-size: 10MB
同时Nginx也需要相应调整:
nginx复制client_max_body_size 10M;
7. 项目扩展方向
- 移动端适配:
- 开发微信小程序版本
- 使用Capacitor打包为原生App
- PWA支持离线访问
- 高级功能扩展:
- 实时聊天室
- 外卖订餐集成
- 会员积分系统
- 大数据分析看板
- 架构演进:
- 引入SpringCloud实现微服务化
- 使用Elasticsearch改进搜索体验
- 采用Redis集群提升缓存能力
- 实现分布式文件存储
