1. 项目概述
最近在GitHub上看到一个挺有意思的厨艺交流平台项目,采用SpringBoot+Vue3+MyBatis技术栈实现。作为一个经常下厨的程序员,我觉得这种将技术爱好和生活兴趣结合的项目特别实用。这个平台不仅实现了基本的菜谱分享功能,还包含了用户互动、收藏等社交元素,技术实现上也用到了当前主流的前后端分离架构。
这个项目最吸引我的是它的完整性和实用性。从数据库设计到前后端交互,再到权限控制和性能优化,整个系统考虑得比较全面。我在本地部署测试后发现,代码结构清晰,文档齐全,特别适合想要学习企业级应用开发的开发者参考。
2. 技术架构解析
2.1 前端技术选型
前端采用Vue3+TypeScript的组合,这是我个人非常推荐的技术方案。Vue3的Composition API相比Options API更加灵活,特别是在处理复杂业务逻辑时优势明显。项目中使用到的关键技术点包括:
- Vue Router实现前端路由管理
- Pinia作为状态管理工具
- Axios处理HTTP请求
- Element Plus组件库构建UI
- Vite作为构建工具
选择Vue3而不是React或Angular的主要考虑是学习曲线和开发效率。对于这类以展示为主的Web应用,Vue的模板语法更直观,开发速度更快。而且Vue3的性能优化做得很好,打包体积小,运行效率高。
2.2 后端技术栈
后端采用SpringBoot框架,这是Java生态中最流行的微服务框架。项目中的关键技术实现包括:
- Spring Security + JWT实现认证授权
- MyBatis-Plus增强数据库操作
- Lombok简化代码
- Swagger生成API文档
- Redis缓存热点数据
SpringBoot的自动配置和起步依赖大大简化了项目配置,配合MyBatis-Plus的代码生成器,可以快速实现CRUD操作。我特别喜欢项目中对于异常处理的统一封装,通过@ControllerAdvice实现了全局异常处理,代码非常优雅。
2.3 数据库设计
数据库使用MySQL 8.0,设计了以下几张核心表:
用户表(user)
sql复制CREATE TABLE `user` (
`user_id` bigint NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL,
`password_hash` varchar(100) NOT NULL,
`email` varchar(100) NOT NULL,
`avatar_url` varchar(255) DEFAULT NULL,
`register_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`last_login` datetime DEFAULT NULL,
PRIMARY KEY (`user_id`),
UNIQUE KEY `idx_username` (`username`),
UNIQUE KEY `idx_email` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
菜谱表(recipe)
sql复制CREATE TABLE `recipe` (
`recipe_id` bigint NOT NULL AUTO_INCREMENT,
`title` varchar(100) NOT NULL,
`description` text,
`ingredients` text NOT NULL,
`steps` text NOT NULL,
`cover_img` varchar(255) DEFAULT NULL,
`creator_id` bigint NOT NULL,
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`view_count` int DEFAULT '0',
PRIMARY KEY (`recipe_id`),
KEY `idx_creator` (`creator_id`),
CONSTRAINT `fk_recipe_user` FOREIGN KEY (`creator_id`) REFERENCES `user` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
评论表(comment)
sql复制CREATE TABLE `comment` (
`comment_id` bigint NOT NULL AUTO_INCREMENT,
`content` text NOT NULL,
`user_id` bigint NOT NULL,
`recipe_id` bigint NOT NULL,
`comment_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`like_count` int DEFAULT '0',
PRIMARY KEY (`comment_id`),
KEY `idx_recipe` (`recipe_id`),
KEY `idx_user` (`user_id`),
CONSTRAINT `fk_comment_recipe` FOREIGN KEY (`recipe_id`) REFERENCES `recipe` (`recipe_id`),
CONSTRAINT `fk_comment_user` FOREIGN KEY (`user_id`) REFERENCES `user` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
数据库设计有几个亮点:
- 使用utf8mb4字符集支持emoji表情
- 合理设置索引提高查询效率
- 外键约束保证数据完整性
- 自动时间戳记录创建时间
3. 核心功能实现
3.1 用户认证模块
认证采用JWT(JSON Web Token)方案,相比传统的Session认证更适合前后端分离架构。实现流程如下:
- 用户登录时,后端验证用户名密码
- 验证通过后生成JWT令牌返回给前端
- 前端将令牌存储在localStorage中
- 后续请求在Authorization头中携带令牌
- 后端通过过滤器验证令牌有效性
核心代码示例:
java复制// JWT工具类
public class JwtUtil {
private static final String SECRET_KEY = "your-secret-key";
private static final long EXPIRATION_TIME = 86400000; // 24小时
public static String generateToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>();
return Jwts.builder()
.setClaims(claims)
.setSubject(userDetails.getUsername())
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.signWith(SignatureAlgorithm.HS256, SECRET_KEY)
.compact();
}
public static Boolean validateToken(String token, UserDetails userDetails) {
final String username = extractUsername(token);
return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
}
}
3.2 菜谱管理模块
菜谱管理实现了CRUD操作和分页查询。特别值得一提的是图片上传功能,采用阿里云OSS存储图片,返回URL保存到数据库。
后端接口设计:
code复制POST /api/recipes - 创建菜谱
GET /api/recipes/{id} - 获取菜谱详情
PUT /api/recipes/{id} - 更新菜谱
DELETE /api/recipes/{id} - 删除菜谱
GET /api/recipes - 分页查询菜谱
前端使用Vue3的Composition API实现:
typescript复制// 使用Pinia管理菜谱状态
export const useRecipeStore = defineStore('recipe', {
state: () => ({
recipes: [] as Recipe[],
currentRecipe: null as Recipe | null,
total: 0
}),
actions: {
async fetchRecipes(page: number, size: number) {
const res = await api.get('/api/recipes', {
params: { page, size }
})
this.recipes = res.data.items
this.total = res.data.total
},
async createRecipe(recipe: RecipeForm) {
const formData = new FormData()
Object.keys(recipe).forEach(key => {
if (key === 'coverImg' && recipe.coverImg) {
formData.append('file', recipe.coverImg)
} else {
formData.append(key, recipe[key])
}
})
await api.post('/api/recipes', formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
})
}
}
})
3.3 评论互动模块
评论功能实现了增删改查和点赞功能。为了提高性能,点赞数使用Redis缓存,定期同步到数据库。
Redis缓存设计:
java复制// 评论点赞服务
@Service
public class CommentLikeService {
private final RedisTemplate<String, String> redisTemplate;
@Value("${redis.key.prefix.like}")
private String likeKeyPrefix;
// 点赞
public void likeComment(Long commentId, Long userId) {
String key = likeKeyPrefix + commentId;
redisTemplate.opsForSet().add(key, userId.toString());
}
// 取消点赞
public void unlikeComment(Long commentId, Long userId) {
String key = likeKeyPrefix + commentId;
redisTemplate.opsForSet().remove(key, userId.toString());
}
// 获取点赞数
public Long getLikeCount(Long commentId) {
String key = likeKeyPrefix + commentId;
return redisTemplate.opsForSet().size(key);
}
}
4. 项目部署与优化
4.1 本地开发环境搭建
- 安装JDK17、Node.js16+、MySQL8.0、Redis
- 克隆项目代码
- 后端配置:
- 修改application.yml中的数据库连接
- 配置Redis连接
- 设置JWT密钥
- 前端配置:
- 修改.env.development中的API基础URL
- 安装依赖:npm install
- 启动:
- 后端:mvn spring-boot:run
- 前端:npm run dev
4.2 生产环境部署
推荐使用Docker容器化部署:
后端Dockerfile示例:
dockerfile复制FROM openjdk:17-jdk-slim
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
前端Dockerfile示例:
dockerfile复制FROM node:16 as build
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
FROM nginx:alpine
COPY --from=build /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
使用docker-compose编排:
yaml复制version: '3'
services:
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: cookhub
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql
redis:
image: redis:alpine
ports:
- "6379:6379"
backend:
build: ./backend
ports:
- "8080:8080"
depends_on:
- mysql
- redis
frontend:
build: ./frontend
ports:
- "80:80"
volumes:
mysql_data:
4.3 性能优化建议
-
数据库优化:
- 为常用查询字段添加索引
- 使用EXPLAIN分析慢查询
- 考虑读写分离
-
缓存策略:
- 菜谱详情使用Redis缓存
- 实现本地缓存(Caffeine)作为二级缓存
- 设置合理的缓存过期时间
-
前端优化:
- 图片懒加载
- 路由懒加载
- 使用CDN加速静态资源
-
监控与日志:
- 集成Prometheus监控
- 使用ELK收集分析日志
- 设置关键指标告警
5. 常见问题与解决方案
5.1 跨域问题
开发环境下常见跨域问题,解决方案:
后端配置CORS:
java复制@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedHeaders("*")
.allowCredentials(true)
.maxAge(3600);
}
}
或者使用Nginx反向代理:
nginx复制server {
listen 80;
server_name api.cookhub.com;
location / {
proxy_pass http://backend:8080;
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE';
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization';
}
}
5.2 文件上传大小限制
SpringBoot默认文件上传大小为1MB,需要调整配置:
application.yml:
yaml复制spring:
servlet:
multipart:
max-file-size: 10MB
max-request-size: 10MB
5.3 前端路由刷新404
Vue单页应用在刷新时可能会404,需要在Nginx配置:
nginx复制location / {
try_files $uri $uri/ /index.html;
}
5.4 MyBatis查询性能问题
遇到复杂查询性能低下时,可以考虑:
- 使用MyBatis的二级缓存
- 优化SQL语句,避免SELECT *
- 使用
标签复用SQL片段 - 考虑使用MyBatis-Plus的QueryWrapper简化查询
6. 项目扩展方向
这个基础项目还有很多可以扩展的地方:
-
社交功能增强:
- 用户关注系统
- 私信功能
- 菜谱评分系统
-
内容推荐:
- 基于用户行为的个性化推荐
- 热门菜谱排行榜
- 时令食材推荐
-
移动端适配:
- 开发微信小程序版本
- 响应式设计优化移动端体验
- PWA支持离线访问
-
商业化功能:
- 食材电商对接
- 付费精品课程
- 广告系统集成
-
技术深度优化:
- 引入消息队列处理高并发
- 实现分布式架构
- 增加单元测试覆盖率
我在实际开发中还遇到过一些坑,比如Vue3的响应式代理和原始对象的问题,SpringBoot事务失效的场景等。这些经验教训让我深刻理解到,一个看似简单的项目背后其实有很多技术细节需要考虑。