又到了一年一度的毕业设计选题季,很多同学都在纠结怎么把AI大模型和SpringBoot结合起来,既要有技术含量,又要能落地实现。影视评论舆情分析一直是计算机毕业设计的热门方向,但大多数传统方案只是简单的爬虫加词云,缺少智能分析和可视化深度。本文将从零到一搭建一套基于SpringBoot与AI大模型的影视评论舆情数据可视化分析平台,涵盖数据采集、大模型情感分析、可视化大屏、前后端分离部署全流程,提供完整源码思路和核心代码示例,适合计算机专业毕业设计、课程设计和AI应用开发学习。
这套平台的核心价值在于:使用AI大模型替代传统词典库做评论情感分类和关键词抽取,准确率和泛化能力都更好;结合ECharts实现舆情大屏,让分析结果直观可读;后端采用SpringBoot统一提供接口,结构清晰,答辩时也容易讲出工程化思维。
1. 项目背景与核心概念
1.1 什么是影视评论舆情分析
影视评论舆情分析,通俗地说,就是从电影、电视剧的观众评论中提取有价值的信息,比如大家对这部电影的总体评价是正面还是负面、观众最关注的点是演员演技、剧情还是特效、评价热度在什么时间段达到峰值。这些信息对制片方、宣发团队和视频平台都有很强的参考价值。
传统做法往往是人工阅读评论,或者用简单的情感词典匹配。人工方式效率太低,词典方式对“演技炸裂”“剧情太拖了”这类网络化表达识别不准。AI大模型的出现改变了这个局面。
1.2 AI大模型在项目中的定位
本项目中,AI大模型承担两个核心任务:
- 情感分析:判断一条评论属于正向、中性还是负向。
- 关键词抽取:从评论中提取观众关注的维度,如剧情、演技、特效、音乐、剪辑等。
使用大模型的优势在于不需要维护庞大的情感词典,不需要人工标注训练数据,通过提示词工程就能完成分析任务,而且对中文口语化表达的理解能力远强于传统方案。
1.3 数据可视化分析平台的整体价值
数据可视化在这个项目中不只是“画图表”,它帮助用户从整体上把握舆情分布。
例如电影《流浪地球2》上映期间,舆情分析平台可以看到以下信息:
- 评论数量随时间的变化趋势;
- 情感倾向的占比;
- 不同维度的关键词热度排名;
- 热门评论的内容分布。
这些图表组合起来,就是一套完整的影视舆情驾驶舱。SpringBoot后端负责提供数据接口,前端通过ECharts渲染图表,AI大模型服务负责智能分析,三层结构清晰,非常符合毕业设计评分标准中“系统完整、技术新颖、实现合理”的要求。
2. 系统架构与技术选型
2.1 总体架构设计
本系统的架构采用前后端分离模式,整体分为五层:
[数据采集层] -> [数据存储层] -> [AI分析层] -> [后端服务层] -> [前端展示层] 爬虫 MySQL/Redis 大模型API SpringBoot Vue+ECharts从用户视角出发,系统工作流程如下:
- 管理员在后台触发采集任务;
- 爬虫模块从目标网站获取影视评论原始数据;
- 原始数据存入MySQL数据库;
- AI分析模块定时扫描未分析的评论,调用大模型接口完成情感分类和关键词抽取;
- SpringBoot后端通过RESTful接口将分析结果暴露给前端;
- 前端大屏使用ECharts渲染舆情仪表盘。
2.2 技术栈说明
后端核心框架:
| 组件 | 选型 | 说明 |
|---|---|---|
| 开发语言 | Java 8+ | 稳定兼容,适配学校机房环境 |
| 后端框架 | SpringBoot 2.x/3.x | 按实际环境选择,建议2.7.x |
| 持久层 | MyBatis-Plus | 减少SQL编写量,内置分页 |
| 数据库 | MySQL 5.7+ | 存储评论与任务数据 |
| AI能力 | 大模型API或本地部署 | 可选通义千问、文心一言等 |
| 可视化 | ECharts 5.x | 图表渲染 |
| 前端框架 | Vue 2.x/3.x | 配合ECharts搭建管理后台 |
需要说明,SpringBoot版本选择非常关键。如果你的JDK是1.8,建议使用SpringBoot 2.7.x,不要直接上SpringBoot 3.x,因为3.x要求JDK17+。如果你的机器已经装了JDK17,那么选择SpringBoot 3.x也可以。版本太高并不一定适合毕业设计场景,稳定能跑才是第一优先级。
2.3 为什么选择SpringBoot
SpringBoot在这类毕业设计项目中有三个明显优势:
- 自动装配:大量配置开箱即用,比如引入spring-boot-starter-web就自动完成内嵌Tomcat配置,不用像传统Spring那样写一堆XML;
- 生态成熟:MyBatis-Plus、Redis、JWT、Swagger都有成熟的starter依赖;
- 答辩友好:面试官或答辩老师询问项目时,SpringBoot的自动配置、依赖管理原理本身就属于高频考点,你实际使用过自然更有话说。
2.4 AI大模型的接入方式
本项目对接大模型有两种方式可供选择,建议根据预算和技术难度权衡:
| 接入方式 | 优点 | 缺点 | 适合场景 |
|---|---|---|---|
| 云端API调用 | 接入简单,效果稳定 | 需要申请API Key,可能有费用 | 演示、快速开发 |
| 本地部署开源模型 | 无外部依赖,隐私性好 | 对显卡有要求,配置复杂 | 高水平毕设、离线展示 |
在实际答辩演示时,如果现场网络不稳定,云端API容易翻车。建议代码中预留一个本地规则引擎降级方案,当大模型接口不可用时自动切回词典法分析,保证演示不会中断。
3. 环境准备与项目结构
3.1 开发环境要求
这里列出比较稳妥的环境组合,如果你本机环境不同,版本需要根据实际调整,重点是配置思路。
操作系统:Windows 10/11 或 macOS JDK版本:JDK 1.8 或 JDK 17 Maven版本:3.6.3+ IDE工具:IntelliJ IDEA Community/Ultimate 数据库:MySQL 5.7+ Redis(可选):3.2+ Node.js(前端用):14+3.2 创建SpringBoot项目
如果你是新手,推荐直接在IDEA的Spring Initializr中创建工程。关键依赖选择如下:
<!-- 文件路径:pom.xml --> <dependencies> <!-- Web依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- MyBatis-Plus --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.3.1</version> </dependency> <!-- MySQL驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.33</version> </dependency> <!-- Lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <!-- HTTP客户端,用于调用AI大模型 --> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.14</version> </dependency> </dependencies>这里需要注意依赖版本一致性。最常遇到的坑是mybatis-plus和springboot版本不兼容,建议优先使用SpringBoot 2.7.x搭配MyBatis-Plus 3.5.x,这是目前社区验证最多的组合。
3.3 项目包结构
一个好的包结构能让答辩时讲解代码更加清晰。推荐如下目录划分:
src/main/java/com/example/moviecomment/ ├── config/ // 配置类,如跨域、MyBatis-Plus分页 ├── controller/ // 控制层,接收前端请求 ├── service/ // 业务层,处理核心逻辑 ├── mapper/ // MyBatis-Plus接口层 ├── entity/ // 实体类 ├── dto/ // 数据传输对象 ├── utils/ // 工具类 └── job/ // 定时任务前端项目建议单独用Vue CLI创建,目录结构与后端分离管理,便于打包和部署。如果对前端不熟,也可以直接使用SpringBoot的static目录存放静态页面,但这样前后端分离的加分项就体现不出来了。
4. 数据库设计
4.1 核心表结构
本系统的数据库表不宜太多,建议控制在5张左右,既满足功能需要,又不至于工作量过重。核心表设计如下。
评论表t_comment:
CREATE TABLE t_comment ( id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键ID', movie_name VARCHAR(255) NOT NULL COMMENT '影视名称', comment_content TEXT NOT NULL COMMENT '评论原文', comment_time DATETIME DEFAULT NULL COMMENT '评论时间', sentiment VARCHAR(20) DEFAULT 'unknown' COMMENT '情感分类: positive/neutral/negative', sentiment_score DOUBLE DEFAULT 0 COMMENT '情感得分', keywords VARCHAR(500) DEFAULT '' COMMENT '抽取的关键词,逗号分隔', analyze_status TINYINT DEFAULT 0 COMMENT '分析状态: 0未分析, 1已分析, 2失败', create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='影视评论表';分析任务表t_analyze_task:
CREATE TABLE t_analyze_task ( id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键ID', movie_name VARCHAR(255) NOT NULL COMMENT '影视名称', total_count INT DEFAULT 0 COMMENT '待分析数量', success_count INT DEFAULT 0 COMMENT '成功数量', fail_count INT DEFAULT 0 COMMENT '失败数量', status TINYINT DEFAULT 0 COMMENT '任务状态: 0待执行, 1执行中, 2完成', create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='分析任务表';统计视图不做物理表,直接通过SQL聚合查询或者在后端用Java代码聚合,减少维护成本。
4.2 索引设计注意事项
评论表的设计中有两个高频查询场景需要建索引:
- 按影视名称查询评论列表;
- 按分析状态查询待处理数据。
因此建议在movie_name和analyze_status上建立联合索引。
ALTER TABLE t_comment ADD INDEX idx_movie_status (movie_name, analyze_status);需要特别注意的是comment_content字段使用TEXT类型,不能直接建普通索引,但在实际查询中一般也不需要对内容做模糊查询,因为搜索功能的实现可以交给ES或者大模型接口,这里不做复杂处理。
5. 后端核心模块实现
5.1 配置application.yml
配置文件是SpringBoot项目的关键,下面给出核心配置项。
# 文件路径:src/main/resources/application.yml server: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/movie_comment?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai username: root password: yourpassword jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT+8 mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl global-config: db-config: id-type: auto # 自定义AI模型配置 ai: model: api-url: https://your-llm-api.example.com/v1/chat/completions api-key: your-api-key model-name: qwen-plus注意,ai.model这个配置是自定义的,你需要根据自己的大模型服务来调整。使用@ConfigurationProperties可以将配置绑定到Java对象。
5.2 实体类编写
// 文件路径:src/main/java/com/example/moviecomment/entity/Comment.java package com.example.moviecomment.entity; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import java.time.LocalDateTime; @Data @TableName("t_comment") public class Comment { @TableId(type = IdType.AUTO) private Long id; private String movieName; private String commentContent; private LocalDateTime commentTime; private String sentiment; private Double sentimentScore; private String keywords; private Integer analyzeStatus; private LocalDateTime createTime; }注意@TableName注解将实体与数据库表对应起来,Lombok的@Data自动生成getter/setter方法,省去大量样板代码。
5.3 控制层接口实现
控制层负责暴露RESTful接口,下面给出评论分析结果统计接口的示例。
// 文件路径:src/main/java/com/example/moviecomment/controller/AnalysisController.java package com.example.moviecomment.controller; import com.example.moviecomment.common.Result; import com.example.moviecomment.service.AnalysisService; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import java.util.Map; @RestController @RequestMapping("/api/analysis") public class AnalysisController { @Resource private AnalysisService analysisService; /** * 获取评论统计概览 */ @GetMapping("/overview") public Result<Map<String, Object>> getOverview(@RequestParam String movieName) { return Result.success(analysisService.getOverview(movieName)); } /** * 获取情感趋势数据 */ @GetMapping("/trend") public Result<Map<String, Object>> getTrend(@RequestParam String movieName) { return Result.success(analysisService.getTrend(movieName)); } }控制层保持轻量,逻辑尽量封装在Service层,这不仅是SpringBoot的推荐做法,也是答辩时容易被提问的点——“你的控制器为什么这么薄”。
5.4 定时任务实现
定时任务负责自动扫描并分析未处理的评论。使用SpringBoot自带的@Scheduled注解即可。
// 文件路径:src/main/java/com/example/moviecomment/job/CommentAnalyzeJob.java package com.example.moviecomment.job; import com.example.moviecomment.service.AnalysisService; import lombok.extern.slf4j.Slf4j; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import javax.annotation.Resource; @Slf4j @Component public class CommentAnalyzeJob { @Resource private AnalysisService analysisService; /** * 每5分钟执行一次分析任务 */ @Scheduled(fixedDelay = 300000) public void analyzePendingComments() { log.info("开始扫描待分析的评论数据"); analysisService.analyzePending(); } }在启动类上需要添加@EnableScheduling注解开启定时任务功能。
// 文件路径:src/main/java/com/example/moviecomment/MovieCommentApplication.java package com.example.moviecomment; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.annotation.EnableScheduling; @EnableScheduling @SpringBootApplication public class MovieCommentApplication { public static void main(String[] args) { SpringApplication.run(MovieCommentApplication.class, args); } }关于定时任务,有一个容易被忽略的细节:fixedDelay和cron的区别。fixedDelay表示上一次任务执行完成后间隔多少毫秒再执行下一次,cron则按照固定的时间表达式执行。如果评论量不大,使用fixedDelay更安全,可以避免上一次任务还没执行完下一次又启动的问题。
6. AI大模型分析模块实现
6.1 提示词设计
AI大模型的核心技术点在于提示词工程。同样的模型,不同的提示词输出效果差距很大。
下面给出项目中使用的情感分析提示词:
你是一个影视评论舆情分析专家。请对下面这条影视评论进行情感分析。 评论内容:{评论内容} 请严格按照JSON格式输出结果,不要输出其他内容。 { "sentiment": "positive/neutral/negative", "sentiment_score": 0.8, "keywords": ["演技", "剧情"] } 说明: 1. sentiment表示整体情感倾向,positive为正向,neutral为中性,negative为负向。 2. sentiment_score表示情感得分,范围0到1,越接近1表示正向情绪越强。 3. keywords表示评论中提到的关注维度,抽取最多3个。这个提示词的优点在于:
- 明确输出格式为JSON,便于程序解析;
- 定义了枚举值的取值范围;
- 限制关键词数量为3,避免输出噪音信息。
6.2 大模型调用封装类
// 文件路径:src/main/java/com/example/moviecomment/utils/LlmClient.java package com.example.moviecomment.utils; import com.alibaba.fastjson.JSONObject; import com.example.moviecomment.config.AiModelProperties; import lombok.extern.slf4j.Slf4j; import org.springframework.http.*; import org.springframework.stereotype.Component; import org.springframework.web.client.RestTemplate; import javax.annotation.Resource; import java.util.*; @Slf4j @Component public class LlmClient { @Resource private AiModelProperties aiModelProperties; @Resource private RestTemplate restTemplate; /** * 调用大模型接口 */ public String chat(String prompt) { String url = aiModelProperties.getApiUrl(); String apiKey = aiModelProperties.getApiKey(); HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); headers.setBearerAuth(apiKey); Map<String, Object> message = new HashMap<>(); message.put("role", "user"); message.put("content", prompt); Map<String, Object> body = new HashMap<>(); body.put("model", aiModelProperties.getModelName()); body.put("messages", Collections.singletonList(message)); body.put("temperature", 0.2); HttpEntity<Map<String, Object>> request = new HttpEntity<>(body, headers); ResponseEntity<String> response = restTemplate.postForEntity(url, request, String.class); return response.getBody(); } /** * 解析大模型返回结果 */ public LLMResult parseResult(String responseBody) { JSONObject json = JSONObject.parseObject(responseBody); String content = json.getJSONArray("choices") .getJSONObject(0) .getJSONObject("message") .getString("content"); JSONObject result = JSONObject.parseObject(content); LLMResult llmResult = new LLMResult(); llmResult.setSentiment(result.getString("sentiment")); llmResult.setScore(result.getDouble("sentiment_score")); List<String> keywords = result.getJSONArray("keywords").toJavaList(String.class); llmResult.setKeywords(keywords); return llmResult; } }这里使用了RestTemplate调用HTTP接口,temperature参数设置为0.2,目的是让模型输出更加确定,减少随机性。在毕业设计答辩中,你可以解释这个参数的实际意义——温度越低,输出越保守稳定,适合舆情分类场景。
6.3 降级方案设计
由于大模型API可能存在超时、限流、余额不足等问题,建议实现一个简单的降级策略。
当大模型调用失败时,系统自动使用基于词典的简单情感分析作为兜底方案。
// 文件路径:src/main/java/com/example/moviecomment/service/impl/SentimentFallback.java package com.example.moviecomment.service.impl; import com.example.moviecomment.dto.LLMResult; import org.springframework.stereotype.Component; import java.util.Arrays; import java.util.List; @Component public class SentimentFallback { private static final List<String> POSITIVE_WORDS = Arrays.asList("好看", "精彩", "演技好", "震撼", "推荐"); private static final List<String> NEGATIVE_WORDS = Arrays.asList("难看", "烂", "失望", "无聊", "差评"); public LLMResult analyzeByDict(String content) { int positiveCount = countHits(content, POSITIVE_WORDS); int negativeCount = countHits(content, NEGATIVE_WORDS); LLMResult result = new LLMResult(); if (positiveCount > negativeCount) { result.setSentiment("positive"); result.setScore(0.6 + 0.1 * positiveCount); } else if (negativeCount > positiveCount) { result.setSentiment("negative"); result.setScore(0.3 - 0.1 * negativeCount); } else { result.setSentiment("neutral"); result.setScore(0.5); } result.setKeywords(Arrays.asList("剧情", "演员")); return result; } private int countHits(String content, List<String> words) { int count = 0; for (String word : words) { if (content.contains(word)) { count++; } } return count; } }降级方案虽然准确率不如大模型,但保证了系统基本可用。在答辩演示时,你可以主动提到这一点,说明自己考虑了系统的健壮性,这也是加分项。
7. 数据可视化实现
7.1 后端统计接口设计
数据可视化需要后端提供聚合统计接口。下面给出情感分布统计和评论趋势统计的核心代码。
// 文件路径:src/main/java/com/example/moviecomment/service/impl/AnalysisServiceImpl.java package com.example.moviecomment.service.impl; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.example.moviecomment.entity.Comment; import com.example.moviecomment.mapper.CommentMapper; import com.example.moviecomment.service.AnalysisService; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.HashMap; import java.util.List; import java.util.Map; @Service public class AnalysisServiceImpl implements AnalysisService { @Resource private CommentMapper commentMapper; @Override public Map<String, Object> getOverview(String movieName) { QueryWrapper<Comment> wrapper = new QueryWrapper<>(); wrapper.eq("movie_name", movieName); List<Comment> comments = commentMapper.selectList(wrapper); int total = comments.size(); Map<String, Object> result = new HashMap<>(); result.put("total", total); long positive = comments.stream().filter(c -> "positive".equals(c.getSentiment())).count(); long neutral = comments.stream().filter(c -> "neutral".equals(c.getSentiment())).count(); long negative = comments.stream().filter(c -> "negative".equals(c.getSentiment())).count(); result.put("positive", positive); result.put("neutral", neutral); result.put("negative", negative); result.put("positiveRate", total == 0 ? 0 : Math.round(positive * 100.0 / total)); return result; } @Override public Map<String, Object> getTrend(String movieName) { // 按日期统计评论数量和正负面数量 List<Map<String, Object>> trendData = commentMapper.selectTrend(movieName); Map<String, Object> result = new HashMap<>(); result.put("trend", trendData); return result; } }需要特别注意的是,这里用了comments.stream().filter()做内存统计,适合数据量不大的毕业设计场景。如果评论数据达到百万级别,就需要改用SQL的GROUP BY聚合查询,否则内存溢出风险很大。在答辩时,你可以主动区分这两种方式的适用场景,展示你的工程思维。
7.2 MyBatis-Plus分页配置
如果评论列表需要分页展示,需要配置MyBatis-Plus的分页插件。
// 文件路径:src/main/java/com/example/moviecomment/config/MybatisPlusConfig.java package com.example.moviecomment.config; import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); PaginationInnerInterceptor paginationInterceptor = new PaginationInnerInterceptor(DbType.MYSQL); paginationInterceptor.setMaxLimit(500L); interceptor.addInnerInterceptor(paginationInterceptor); return interceptor; } }分页插件配置了最大每页查询数量500条,这样可以避免前端某个恶意请求一次性查出全表数据,造成接口压力过大。
7.3 跨域配置
前后端分离模式下,前端通常运行在http://localhost:5173(Vite默认)或http://localhost:8081,后端运行在http://localhost:8080,浏览器会阻止跨域请求。因此需要配置跨域规则。
// 文件路径:src/main/java/com/example/moviecomment/config/CorsConfig.java package com.example.moviecomment.config; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOriginPatterns("*") .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") .allowedHeaders("*") .allowCredentials(true) .maxAge(3600); } }这里使用allowedOriginPatterns而不是allowedOrigins,是因为在SpringBoot 2.4+版本中,当allowCredentials(true)时,allowedOrigins("*")会受到限制。这是一个非常经典的版本兼容坑,很多同学在部署时遇到“跨域配置了但请求还是失败”就是因为这个原因。
7.4 前端ECharts可视化
前端核心是Vue组件加ECharts图表。下面给出情感分布饼图的示例。
<!-- 文件路径:frontend/src/components/SentimentPie.vue --> <template> <div ref="chartRef" style="width: 100%; height: 400px;"></div> </template> <script> import * as echarts from 'echarts'; export default { name: 'SentimentPie', data() { return { chart: null, }; }, mounted() { this.chart = echarts.init(this.$refs.chartRef); this.loadData(); }, methods: { async loadData() { const response = await fetch('/api/analysis/overview?movieName=流浪地球2'); const data = await response.json(); this.renderChart(data.data); }, renderChart(data) { const option = { title: { text: '情感倾向分布', left: 'center', }, tooltip: { trigger: 'item', }, legend: { orient: 'vertical', left: 'left', }, series: [ { name: '情感分布', type: 'pie', radius: '60%', data: [ { value: data.positive, name: '正向' }, { value: data.neutral, name: '中性' }, { value: data.negative, name: '负向' }, ], emphasis: { itemStyle: { shadowBlur: 10, shadowOffsetX: 0, shadowColor: 'rgba(0, 0, 0, 0.5)', }, }, }, ], }; this.chart.setOption(option); }, }, beforeDestroy() { if (this.chart) { this.chart.dispose(); } }, }; </script>ECharts组件的关键在于mounted生命周期中初始化图表,并在beforeDestroy中释放实例,避免页面切换导致的内存泄漏。另外,window.addEventListener('resize', this.chart.resize)也是常见优化点,可以让图表在浏览器窗口大小变化时自适应。
8. 运行时序流程
为了让答辩时讲解更清晰,这里给出系统的核心运行时序流程。
1. 管理员通过前端页面提交“开启分析”请求 2. SpringBoot Controller接收请求,创建分析任务记录 3. 定时任务检测到待分析评论,逐条调用AI大模型接口 4. 大模型返回情感分类结果,SpringBoot解析结果并更新数据库 5. 前端大屏每隔10秒轮询后端最新统计结果 6. ECharts根据接口返回数据重新渲染图表这个流程中有一个值得关注的工程问题:AI大模型接口调用是同步阻塞的,如果评论数量很大,逐条同步调用会非常慢。优化方案是引入线程池异步调用,或者用消息队列排队处理。毕业设计阶段建议多线程加线程池即可,不需要引入复杂的消息队列。
9. 常见问题与排查思路
在开发和运行过程中,以下问题是同学们最常遇到的,这里给出排查思路。
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
| SpringBoot启动失败 | JDK版本与SpringBoot版本不匹配 | 检查JDK版本,切换SpringBoot版本 |
| 数据库连接失败 | MySQL字符集或时区配置错误 | 在URL中加上characterEncoding=utf8和serverTimezone=Asia/Shanghai |
| 大模型接口超时 | 网络不通或API Key无效 | 先curl测试接口,再检查Key是否有效 |
| 返回的JSON中内容为空 | 模型返回格式与解析逻辑不一致 | 打开日志,打印原始响应,调整解析代码 |
| 前端请求502 | 后端端口未开放或接口路径错误 | 浏览器F12查看请求地址,确认后端端口 |
| 定时任务不执行 | 启动类缺少@EnableScheduling | 检查启动类注解 |
| 中文乱码 | 数据库连接未指定utf8 | 修改URL增加useUnicode=true&characterEncoding=UTF-8 |
其中关于大模型返回内容解析失败是最常见的。因为不同模型的返回结构有所不同,有的模型除了choices字段还带有content_filter_results等字段,有的模型content字段是列表而不是字符串。建议先在Postman中真实调用一次接口,把返回报文完整打印出来,再写解析代码。不要凭想象写解析逻辑。
10. 最佳实践与答辩建议
10.1 安全边界与合规提醒
在毕业设计中,很多同学会选择爬取豆瓣、知乎等平台的评论数据。这里必须强调:
- 爬虫要遵守目标网站的
robots.txt协议; - 控制请求频率,避免对目标服务器造成压力;
- 只采集公开数据,不使用账号篡改、恶意破解等非法手段;
- 涉及用户个人信息的评论要做好脱敏,展示时隐藏用户名和头像。
如果论文中涉及数据采集,建议在“合规性分析”章节中明确说明数据来源的合法性和采集频率的控制策略,这也是近年来论文盲审和答辩比较关注的方面。
10.2 大模型API Key安全管理
不要把API Key直接写在代码中提交到Git仓库。推荐做法是:
- 将API Key配置在
application-local.yml中; - 本地启动时通过
--spring.profiles.active=local指定; .gitignore中排除配置文件。
如果代码需要交到学校服务器演示,可以使用环境变量注入。
ai: model: api-key: ${AI_API_KEY}这样即使代码被别人看到,也不会泄露密钥。
10.3 性能优化建议
对于毕业设计项目,性能不是最核心的指标,但基本的优化意识要体现出来:
- 数据量大的查询使用分页,不要一次性全查出来;
- AI大模型调用使用线程池控制并发数;
- 前端图表获取数据使用轮询间隔至少10秒,避免频繁请求;
- 热门电影的数据统计结果可以加Redis缓存,减少数据库压力。
10.4 答辩时的高频问题参考
根据经验,答辩老师通常会对以下问题感兴趣:
- 为什么选择大模型而不是传统情感词典?
- 大模型分析的平均延迟是多少?如果评论量很大如何优化?
- 你的项目有哪些创新点?
- 爬虫接口如果被封了怎么办?
- 系统的准确率做评估了吗?
针对“准确率评估”这个问题,建议在项目中准备一个200条评论的人工标注测试集,计算出大模型分析准确率。比如准备200条评论,人工标注50条正向、50条中性、100条负向,然后和大模型的分析结果对比,计算准确率。这部分数据可以放入项目报告中,会显得非常严谨。
11. 总结与下一步扩展
本文从项目背景、系统架构、数据库设计、SpringBoot后端实现、AI大模型接入、数据可视化、部署运行到常见问题排查,完整梳理了基于SpringBoot与AI大模型的影视评论舆情数据可视化分析平台的开发流程。核心要点可以概括为:用SpringBoot构建分层后端,用AI大模型做智能情感分析和关键词抽取,用ECharts实现舆情大屏展示,通过合理的降级方案保证系统稳定性。
如果后续想继续提升这个项目的含金量,可以考虑以下方向:
- 引入Elasticsearch实现评论搜索和聚合分析,应对更大数据量;
- 接入消息队列实现评论采集与AI分析的异步解耦;
- 增加热点识别算法,自动发现爆发性舆论事件;
- 使用WebSocket替代轮询机制,实现舆情数据实时推送。
如果你正在准备毕业设计,建议先快速跑通一条数据链路——采集一批评论、用AI分析、在ECharts中展示,再逐步完善权限管理、任务调度、大屏美化等细节。完成一个能运行、能演示、能讲清原理的项目,比追求功能数量更重要。希望这篇文章能帮你少走弯路,顺利过关。