1. 项目概述:一个适合教学实践的影视评论平台
去年指导本科生毕业设计时,发现很多同学对前后端分离开发缺乏系统认知。于是基于SpringBoot+Vue技术栈设计了这个电影评论平台,它完美复现了企业级应用的核心功能模块,包含用户认证、影片管理、评论互动等完整流程。采用Java 11+MySQL 8.0技术组合,前后端通过RESTful API交互,特别适合作为计算机专业学生的实践项目。
这个项目最突出的特点是"教学友好性":每个功能模块都预留了标准接口文档,数据库设计遵循三范式但保留了优化空间,关键业务逻辑添加了扩展点注释。我曾用这个框架带出3个校级优秀毕业设计,接下来详细拆解技术实现要点。
2. 技术架构解析
2.1 后端SpringBoot设计
采用经典的三层架构:
- Controller层:处理HTTP请求,返回统一JSON格式
java复制@RestController
@RequestMapping("/api/movies")
public class MovieController {
@Autowired
private MovieService movieService;
@GetMapping
public Result<List<Movie>> listMovies(
@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "10") Integer size) {
return Result.success(movieService.getMovies(page, size));
}
}
- Service层:业务逻辑实现,包含事务管理
java复制@Service
@Transactional
public class CommentServiceImpl implements CommentService {
@Override
public void addComment(CommentDTO dto) {
// 验证用户权限
// 校验电影是否存在
// 保存评论
// 更新电影评分
}
}
- DAO层:MyBatis-Plus实现数据持久化
xml复制<select id="selectCommentsWithUser" resultType="CommentVO">
SELECT c.*, u.username, u.avatar
FROM t_comment c
LEFT JOIN t_user u ON c.user_id = u.id
WHERE c.movie_id = #{movieId}
ORDER BY c.create_time DESC
</select>
关键设计:使用Hibernate Validator进行参数校验,全局异常处理器统一捕获ConstraintViolationException,返回400错误明细。
2.2 前端Vue工程化实践
基于Vue CLI 4.x搭建的工程包含:
src/api/目录存放所有接口请求封装src/views/使用路由懒加载提升性能src/components/复用组件采用props+emit通信- Vuex管理登录状态和用户信息
典型页面组件结构:
javascript复制<template>
<div class="movie-detail">
<MovieHeader :movie="movieData"/>
<CommentEditor @submit="handleSubmit"/>
<CommentList :comments="comments"/>
</div>
</template>
<script>
import { getMovieDetail } from '@/api/movie'
export default {
data() {
return {
movieData: null,
comments: []
}
},
async created() {
const res = await getMovieDetail(this.$route.params.id)
this.movieData = res.data
}
}
</script>
3. 数据库设计与优化
3.1 核心表结构
sql复制CREATE TABLE `t_movie` (
`id` int NOT NULL AUTO_INCREMENT,
`title` varchar(100) NOT NULL,
`cover_url` varchar(255) DEFAULT NULL,
`release_year` int DEFAULT NULL,
`avg_rating` decimal(3,1) DEFAULT '0.0',
PRIMARY KEY (`id`),
FULLTEXT KEY `idx_title` (`title`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `t_comment` (
`id` int NOT NULL AUTO_INCREMENT,
`content` text NOT NULL,
`rating` tinyint DEFAULT NULL,
`user_id` int NOT NULL,
`movie_id` int NOT NULL,
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_movie` (`movie_id`),
KEY `idx_user` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
3.2 性能优化实践
- 评论分页查询优化:
java复制public PageInfo<CommentVO> getCommentsByMovie(Integer movieId, Integer page, Integer size) {
PageHelper.startPage(page, size);
List<CommentVO> list = commentMapper.selectCommentsWithUser(movieId);
return new PageInfo<>(list);
}
- 电影评分动态计算:
java复制@Scheduled(cron = "0 0 3 * * ?") // 每天凌晨3点执行
public void updateMovieRatings() {
movieMapper.batchUpdateRating();
}
- 使用Redis缓存热门电影数据:
java复制@Cacheable(value = "movies", key = "#page+'-'+#size")
public List<Movie> getHotMovies(Integer page, Integer size) {
return movieMapper.selectByRatingDesc(page, size);
}
4. 关键功能实现细节
4.1 JWT认证流程
- 登录成功后生成token:
java复制public String generateToken(User user) {
return Jwts.builder()
.setSubject(user.getUsername())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.signWith(SignatureAlgorithm.HS512, SECRET)
.compact();
}
- Vue前端处理token:
javascript复制// 请求拦截器
service.interceptors.request.use(config => {
const token = localStorage.getItem('token')
if (token) {
config.headers['Authorization'] = `Bearer ${token}`
}
return config
})
// 响应拦截器
service.interceptors.response.use(
response => response,
error => {
if (error.response.status === 401) {
store.dispatch('user/logout')
router.push('/login')
}
return Promise.reject(error)
}
)
4.2 电影搜索功能
- 后端实现模糊搜索:
java复制public List<Movie> searchMovies(String keyword) {
QueryWrapper<Movie> wrapper = new QueryWrapper<>();
wrapper.like("title", keyword)
.or()
.like("director", keyword);
return movieMapper.selectList(wrapper);
}
- 前端防抖处理:
javascript复制let timer = null
export default {
methods: {
handleSearch() {
clearTimeout(timer)
timer = setTimeout(() => {
this.fetchResults(this.keyword)
}, 500)
}
}
}
5. 项目部署指南
5.1 后端部署
- 打包SpringBoot应用:
bash复制mvn clean package -DskipTests
- 数据库初始化:
bash复制mysql -u root -p < schema.sql
- 使用Docker运行:
dockerfile复制FROM openjdk:11-jre
COPY target/movie-comment-*.jar /app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
5.2 前端部署
- 生产环境构建:
bash复制npm run build
- Nginx配置示例:
nginx复制server {
listen 80;
server_name yourdomain.com;
location / {
root /path/to/dist;
index index.html;
try_files $uri $uri/ /index.html;
}
location /api {
proxy_pass http://backend:8080;
}
}
6. 教学实践建议
6.1 适合扩展的功能
- 社交功能扩展:
- 用户关注系统
- 私信通知模块
- 热门评论排行榜
- 数据分析方向:
- 用户行为日志收集
- 电影推荐算法
- 评论情感分析
6.2 常见问题解决方案
- 跨域问题:
java复制@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("*")
.maxAge(3600);
}
}
- 日期格式化问题:
javascript复制// main.js
import moment from 'moment'
Vue.filter('formatDate', value => {
return moment(value).format('YYYY-MM-DD HH:mm')
})
- 大文件上传优化:
java复制@PostMapping("/upload")
public Result<String> upload(@RequestParam("file") MultipartFile file) {
if (file.isEmpty()) {
return Result.error("请选择文件");
}
String fileName = UUID.randomUUID() + file.getOriginalFilename();
Path path = Paths.get(UPLOAD_DIR, fileName);
Files.copy(file.getInputStream(), path, StandardCopyOption.REPLACE_EXISTING);
return Result.success("/uploads/" + fileName);
}
这个项目经过多次教学实践迭代,建议学生在理解基础架构后,可以从性能优化、功能扩展、UI改进等不同方向进行深化。我在代码关键位置都预留了TODO注释,方便学生找到切入点。比如电影推荐模块可以尝试基于内容的推荐算法,评论系统可以引入Markdown支持,这些都能显著提升项目竞争力。