
毕业设计选“SpringBoot AI大模型影视评论舆情数据可视化分析平台”这类题目的同学通常第一反应是先把页面搭出来再想怎么接入大模型。实际上这个题目真正值钱的部分在于数据从哪来、舆情指标怎么算、大模型在分析链路里到底承担什么角色、可视化页面如何把这些结果串起来。本文按“概念 - 数据库 - 后端 - 可视化 - 运行验证 - 排错 - 最佳实践”这条线完整拆解一个可复现的影视评论舆情分析平台适合 SpringBoot 有一定基础、想在毕设里接入 AI 大模型并补齐数据可视化能力的同学。1. 先理解这个毕设题目的技术主线1.1 这个项目到底在解决什么问题影视评论数据天然适合做舆情分析。用户在豆瓣、猫眼、微博等平台留下的评论包含情感倾向、评分、时间分布、影片类型偏好等信息。一个平台如果能把多部影片的评论聚合起来实时展示“正面评论占比、负面评论集中在哪些时间段、哪个演员或导演的热度更高”对制片方、宣发团队和视频平台都有参考价值。这类系统放进毕业设计里通常需要完成四件事评论数据采集与存储有数据库表能存电影、评论、用户、评分等信息。舆情指标计算按情感、时间、影片、评分区间做聚合统计。AI 大模型分析让大模型对评论情感、核心观点、舆情摘要进行推理。数据可视化用图表把统计结果呈现出来形成平台页面。核心难点不是某个单一技术而是把 SpringBoot MySQL AI 大模型接口 ECharts 这四层串起来。任何一个环节断掉整个系统都跑不通。1.2 AI 大模型在舆情分析里承担什么角色很多同学会误以为“AI 大模型舆情分析平台”就是把所有评论丢给大模型让大模型返回结论。实际工程中这样做既贵又慢。合理的分工是传统统计负责确定性计算评论数量、情感词频、评分均值、时间趋势用 SQL 和聚合逻辑完成。大模型负责语义理解单条评论的隐含情感、多条评论的舆情摘要、宣发建议、影片口碑总结。可视化负责结果呈现统计结果用图表展示大模型生成的分析报告单独展示。换句话说AI 大模型不是替代统计模块而是给舆情分析增加一层“人话总结”能力。这个定位在论文写作和项目答辩时也更容易讲清楚。1.3 技术选型与模块划分推荐采用以下技术组合既能满足毕设工作量又不会因为过重依赖导致环境难配模块技术选型用途后端框架SpringBoot 2.7.x / 3.x提供 HTTP 接口和业务逻辑数据库MySQL 5.7 / 8.0存储电影、评论、舆情统计结果数据访问Spring Data JPA 或 MyBatis-Plus操作数据库AI 大模型国内主流大模型 HTTP 接口情感分析、评论摘要、舆情报告可视化ECharts Thymeleaf页面图表展示数据来源模拟数据 / 公开数据集 / 合规采集初始化评论数据模块上划分为四层数据层实体类、Mapper 或 Repository、数据库初始化脚本。服务层评论服务、统计服务、AI 大模型服务。接口层RESTful Controller返回 JSON 给前端。展示层Thymeleaf 页面 ECharts 图表。这个结构的好处是每一层都能单独测试。AI 大模型接口挂了统计模块仍然可用页面还能展示历史分析结果。2. 环境准备与数据库设计2.1 开发环境清单开始编码前先把环境对齐。这个项目最容易在第一周卡住的问题就是版本不一致尤其是 SpringBoot 版本和 JDK 版本。工具建议版本说明JDK8 或 17SpringBoot 2.7.x 用 JDK8SpringBoot 3.x 必须 JDK17Maven3.6项目构建和依赖管理MySQL5.7 或 8.0数据库实例IDEIntelliJ IDEA开发调试浏览器Chrome/Edge页面预览和接口调试这里要注意热词里常见“springboot版本太高”的问题本质是 SpringBoot 3.x 要求 JDK 17 和jakarta.*包如果本地环境是 JDK 8导入 3.x 项目后会出现编译错误。落地方案是JDK 8 配 SpringBoot 2.7.xJDK 17 配 SpringBoot 3.x。毕业设计如果不想折腾环境SpringBoot 2.7.x JDK 8 更稳妥。2.2 数据库表结构设计设计表结构时不用铺太多表但要覆盖完整分析链路。核心是 movie 和 comment 两张表再加一张可选的 ai_analysis_record 表用于缓存大模型分析结果。CREATE DATABASE IF NOT EXISTS movie_comment DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE movie_comment; CREATE TABLE movie ( id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT 影片ID, title VARCHAR(255) NOT NULL COMMENT 影片名称, category VARCHAR(100) COMMENT 类型如 动作/爱情/科幻, release_year INT COMMENT 上映年份, created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间 ) ENGINEInnoDB COMMENT 影片表; CREATE TABLE comment ( id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT 评论ID, movie_id BIGINT NOT NULL COMMENT 关联影片ID, content TEXT NOT NULL COMMENT 评论内容, user_name VARCHAR(100) COMMENT 评论用户, sentiment INT DEFAULT 0 COMMENT 情感倾向0中性 1正向 2负向, score DECIMAL(3,1) COMMENT 用户评分范围 1.0-10.0, comment_time DATETIME COMMENT 评论时间, created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, INDEX idx_movie_id (movie_id), INDEX idx_comment_time (comment_time) ) ENGINEInnoDB COMMENT 影视评论表; CREATE TABLE ai_analysis_record ( id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT 记录ID, movie_id BIGINT NOT NULL COMMENT 影片ID, analysis_type VARCHAR(50) NOT NULL COMMENT 分析类型EMOTION/SUMMARY, result_json TEXT COMMENT 大模型返回的JSON结果, created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间 ) ENGINEInnoDB COMMENT AI分析记录表;设计说明sentiment字段用 0/1/2 而不是直接存字符串是为了后续 SQL 聚合更简单统计时再映射成“中性/正向/负向”。score使用 DECIMAL避免浮点数精度问题。ai_analysis_record表用于缓存避免每次刷新页面都重复调用大模型接口。2.3 Maven 依赖配置用 Spring Initializr 创建工程后pom.xml 至少需要以下依赖dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-thymeleaf/artifactId /dependency dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3.1/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId scoperuntime/scope /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency /dependencies如果使用的 SpringBoot 版本是 2.7.xmysql-connector-java会自动选择 8.0.x 版本。如果是 SpringBoot 3.x建议换成com.mysql:mysql-connector-j。3. 构建后端核心功能3.1 项目目录结构后端目录按功能分包避免把所有类堆在 controller 里src/main/java/com/example/moviecomment/ ├── controller/ │ ├── AnalysisController.java │ └── CommentController.java ├── entity/ │ ├── Movie.java │ ├── Comment.java │ └── AiAnalysisRecord.java ├── mapper/ │ ├── MovieMapper.java │ └── CommentMapper.java ├── service/ │ ├── CommentService.java │ ├── AnalysisService.java │ └── ai/ │ ├── AIModelService.java │ └── DefaultAIModelServiceImpl.java ├── config/ │ ├── RestTemplateConfig.java │ └── AiModelProperties.java └── common/ └── Result.java这种分包不是固定模板但能让答辩时讲清楚“控制层、业务层、数据访问层、外部服务层”的职责边界。3.2 实体与数据访问层以 MyBatis-Plus 为例Comment 实体可以这样写Data TableName(comment) public class Comment { TableId(type IdType.AUTO) private Long id; private Long movieId; private String content; private String userName; private Integer sentiment; private BigDecimal score; private LocalDateTime commentTime; }Mapper 接口Mapper public interface CommentMapper extends BaseMapperComment { }通过 MyBatis-Plus 自带的selectList、selectCount能完成大部分查询。涉及多表分组统计时可以写自定义 SQL。下面这条查询统计每个电影的情感分布Mapper public interface CommentMapper extends BaseMapperComment { Select(SELECT sentiment, COUNT(*) AS cnt FROM comment WHERE movie_id #{movieId} GROUP BY sentiment) ListMapString, Object countSentimentByMovie(Long movieId); }3.3 舆情统计服务统计模块是大模型分析的输入前提也是可视化页面的数据来源。核心方法包括统计某部电影的情感分布。统计每日评论数量趋势。统计评分分布。找出评论数最多的 TOP 电影。“每日评论数量趋势”可以用 SQL 按日期分组SELECT DATE(comment_time) AS date, COUNT(*) AS cnt FROM comment WHERE movie_id #{movieId} GROUP BY DATE(comment_time) ORDER BY dateService 层只需要把 Mapper 返回的结果映射成统一的返回结构。示例Service public class AnalysisService { private final CommentMapper commentMapper; public AnalysisService(CommentMapper commentMapper) { this.commentMapper commentMapper; } public ListMapString, Object sentimentDistribution(Long movieId) { ListMapString, Object rows commentMapper.countSentimentByMovie(movieId); for (MapString, Object row : rows) { Integer sentiment Integer.valueOf(row.get(sentiment).toString()); String name switch (sentiment) { case 0 - 中性; case 1 - 正向; case 2 - 负向; default - 未知; }; row.put(name, name); } return rows; } }这里要注意 SQL 查询返回的字段名可能包含大小写问题在 Java 中读取时统一做类型转换避免因为数据库字段别名不一致报错。3.4 接入 AI 大模型统一接口与实现AI 大模型接入是整个系统的亮点也是最容易写乱的地方。推荐定义一个统一接口后续无论对接哪家大模型业务层都不需要改。public interface AIModelService { String analyzeComment(String content); String generateSummary(Long movieId); }实现类通过 RestTemplate 调用 HTTP 接口。国内主流大模型平台一般提供 OpenAI 兼容的 HTTP 接口请求结构比较简单{ model: qwen-plus, messages: [ { role: system, content: 你是影视评论情感分析助手。只输出 JSON不要输出多余文字。 }, { role: user, content: 请分析这条评论的情感倾向剧情拖沓节奏太慢失望。 } ], temperature: 0.7, max_tokens: 512 }对应 Java 代码Service public class DefaultAIModelServiceImpl implements AIModelService { private final RestTemplate restTemplate; private final AiModelProperties properties; public DefaultAIModelServiceImpl(RestTemplate restTemplate, AiModelProperties properties) { this.restTemplate restTemplate; this.properties properties; } Override public String analyzeComment(String content) { MapString, Object payload new HashMap(); payload.put(model, properties.getModel()); ListMapString, String messages new ArrayList(); messages.add(Map.of(role, system, content, 你是影视评论情感分析助手。只输出JSON格式为 {\sentiment\:\POSITIVE|NEGATIVE|NEUTRAL\,\summary\:\短评\})); messages.add(Map.of(role, user, content, content)); payload.put(messages, messages); payload.put(temperature, properties.getTemperature()); payload.put(max_tokens, properties.getMaxToken()); HttpHeaders headers new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); headers.setBearerAuth(properties.getApiKey()); HttpEntityMapString, Object entity new HttpEntity(payload, headers); ResponseEntityString response restTemplate.postForEntity(properties.getUrl(), entity, String.class); return extractContent(response.getBody()); } }extractContent方法负责从大模型返回 JSON 中取出choices[0].message.content。返回内容可能带 Markdown 标记或额外说明文字最好做一次清洗private String extractContent(String responseBody) { try { JsonNode root new ObjectMapper().readTree(responseBody); return root.path(choices).path(0).path(message).path(content).asText(); } catch (JsonProcessingException e) { throw new RuntimeException(解析大模型返回结果失败, e); } }配置类统一管理 API 地址、密钥、模型名、超时时间ConfigurationProperties(prefix ai.model) Data Component public class AiModelProperties { private String apiKey; private String url; private String model; private Integer maxToken 512; private Double temperature 0.7; private Integer connectTimeout 5000; private Integer readTimeout 30000; }application.ymlai: model: api-key: ${AI_API_KEY:你的密钥} url: ${AI_API_URL:https://api.example.com/v1/chat/completions} model: qwen-plus max-token: 512 temperature: 0.7密钥不要硬编码到 Java 代码里通过环境变量注入项目提交到 GitHub 时也安全。3.5 控制器接口设计控制器返回统一 Result 结构便于前端处理。Result 可以设计成Data public class ResultT { private Integer code; private String message; private T data; public static T ResultT ok(T data) { ResultT result new Result(); result.setCode(200); result.setMessage(success); result.setData(data); return result; } }AnalysisController 提供以下接口接口方法说明/api/analysis/sentiment-distributionGET某个电影的评论情感分布/api/analysis/trendGET评论数量时间趋势/api/analysis/score-distributionGET评分分布/api/analysis/ai/summaryGET大模型生成的舆情摘要示例RestController RequestMapping(/api/analysis) public class AnalysisController { private final AnalysisService analysisService; private final AIModelService aiModelService; public AnalysisController(AnalysisService analysisService, AIModelService aiModelService) { this.analysisService analysisService; this.aiModelService aiModelService; } GetMapping(/sentiment-distribution) public ResultListMapString, Object sentimentDistribution(RequestParam Long movieId) { return Result.ok(analysisService.sentimentDistribution(movieId)); } GetMapping(/ai/summary) public ResultString aiSummary(RequestParam Long movieId) { return Result.ok(aiModelService.generateSummary(movieId)); } }接口层不写复杂逻辑只负责参数校验和调用服务。答辩时老师问“接口怎么设计的”可以按这个职责讲。4. 数据可视化页面与接口联调4.1 集成 ECharts可视化页面不引入 Vite、Webpack 等构建工具直接用 Thymeleaf 模板加 ECharts 静态文件降低毕设复杂度。下载echarts.min.js放到src/main/resources/static/js/目录。在src/main/resources/templates/下创建index.html。页面中引入 jQuery可选和 ECharts。!DOCTYPE html html langzh head meta charsetUTF-8 title影视评论舆情可视化分析平台/title script src/js/echarts.min.js/script style .chart { width: 100%; height: 400px; margin-bottom: 30px; } /style /head body div label选择电影/label select idmovieSelect/select /div div idsentimentChart classchart/div div idtrendChart classchart/div div idaiSummary stylepadding: 20px; border: 1px solid #ddd;/div script // 初始化图表 const sentimentChart echarts.init(document.getElementById(sentimentChart)); const trendChart echarts.init(document.getElementById(trendChart)); async function loadCharts(movieId) { loadSentimentChart(movieId); loadTrendChart(movieId); loadAISummary(movieId); } async function loadSentimentChart(movieId) { const response await fetch(/api/analysis/sentiment-distribution?movieId movieId); const result await response.json(); sentimentChart.setOption({ title: { text: 评论情感分布 }, tooltip: { trigger: item }, series: [{ type: pie, radius: 60%, data: result.data.map(item { return { name: item.name, value: item.value }; }) }] }); } async function loadTrendChart(movieId) { const response await fetch(/api/analysis/trend?movieId movieId); const result await response.json(); trendChart.setOption({ title: { text: 评论数量时间趋势 }, tooltip: { trigger: axis }, xAxis: { type: category, data: result.data.map(item item.date) }, yAxis: { type: value }, series: [{ type: line, data: result.data.map(item item.cnt) }] }); } async function loadAISummary(movieId) { const response await fetch(/api/analysis/ai/summary?movieId movieId); const result await response.json(); document.getElementById(aiSummary).innerText result.data || 暂无分析结果; } /script /body /html代码中的 fetch 请求路径是相对的SpringBoot 默认会把static目录下的静态资源映射到根路径/js/echarts.min.js能直接访问。4.2 图表数据格式统一前后端联调最容易出错的是数据格式。ECharts 的 Pi e 图需要{ name, value }结构折线图需要两个数组分别对应 X 轴和 Y 轴。建议在 Controller 接口层就返回符合前端预期的结构而不是把 Mapper 查询结果直接抛给前端。情感分布接口返回格式{ code: 200, message: success, data: [ { name: 正向, value: 120 }, { name: 中性, value: 45 }, { name: 负向, value: 32 } ] }趋势接口返回格式{ code: 200, message: success, data: [ { date: 2025-01-01, cnt: 18 }, { date: 2025-01-02, cnt: 25 } ] }前端只需做一次map转换即可渲染不需要在前端写复杂数据处理逻辑。4.3 页面上如何展示大模型分析结果大模型返回内容通常是一段文字也可能带结构化 JSON。推荐把 AI 舆情摘要单独放在页面顶部或侧边不混入普通图表统计中。例如生成一段“综合舆情报告”{ movieTitle: 流浪地球2, overall: 正面评价占比较高观众普遍认可视觉效果和叙事节奏。, hotTopics: [特效, 科幻设定, 演员表演], riskPoints: [部分观众认为前段文戏较长] }页面展示时把hotTopics和riskPoints渲染成标签列表比单纯展示一段长文本更直观。后端可以对大模型返回结果做强约束要求必须输出 JSON 格式再在 Java 中解析后传给前端。5. 运行验证与预期结果5.1 启动项目项目启动前先确认 MySQL 已启动并执行建库建表脚本。然后配置 application.ymlserver: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/movie_comment?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: your_password driver-class-name: com.mysql.cj.jdbc.Driver启动命令mvn clean package mvn spring-boot:run看到日志中出现Started Application in x.x seconds说明启动成功。5.2 验证接口用 curl 验证情感分布接口curl http://localhost:8080/api/analysis/sentiment-distribution?movieId1预期返回包含 code、message、data 的 JSON。如果返回的 code 是 500优先检查 SQL 和数据库连接。验证大模型接口curl http://localhost:8080/api/analysis/ai/summary?movieId1如果返回AI service unavailable或超时先看 application.yml 中 url 和 api-key 是否配置正确。5.3 页面效果浏览器访问http://localhost:8080选择电影后应看到评论情感分布饼图。评论数量时间趋势折线图。评分分布柱状图如果实现了。AI 舆情摘要区域展示大模型生成的文字或标签。页面图表不显示时按下面顺序排查打开浏览器开发者工具 Network 面板看接口是否返回 200看 Console 是否有 JavaScript 报错看echarts.init是否在容器可见后调用。6. 常见问题排查从现象找根因6.1 SpringBoot 版本太高导致启动失败现象项目导入后大量 Java 类找不到比如javax.servlet.*报错。原因SpringBoot 3.x 使用jakarta.servlet.*同时要求 JDK 17。如果本地是 JDK 8或者项目是旧版javax.*写法就会报编译错误。解决查看当前 JDK 版本java -version。JDK 8 环境就把 pom.xml 中 SpringBoot 版本降到 2.7.x。JDK 17 环境就保持 3.x并把javax引用改成jakarta。环境推荐 SpringBoot 版本JDK 82.7.xJDK 173.0.x 以上但要注意依赖兼容性毕设快速跑通2.7.x JDK 86.2 大模型接口调用超时现象调用/api/analysis/ai/summary时前端一直转圈后端日志出现Read timed out。可能原因大模型本身生成耗时较长。RestTemplate 默认超时时间太短甚至无超时。本地网络访问外部 API 不稳定。解决在 RestTemplateConfig 中配置明确的连接和读取超时Bean public RestTemplate restTemplate() { SimpleClientHttpRequestFactory factory new SimpleClientHttpRequestFactory(); factory.setConnectTimeout(5000); factory.setReadTimeout(30000); return new RestTemplate(factory); }同时把大模型调用放到异步线程或加缓存避免前端每次都等待生成。6.3 大模型返回内容不是合法 JSON现象解析choices[0].message.content时抛JsonProcessingException。原因大模型可能返回 json ... 包裹的内容或者在 JSON 前后加了说明文字。解决提取 content 后先去掉 Markdown 代码块标记再提取 JSON 子串private String cleanJson(String content) { String cleaned content.replace(json, ).replace(, ).trim(); int start cleaned.indexOf({); int end cleaned.lastIndexOf(}); return cleaned.substring(start, end 1); }建议在提示词中反复强调“只输出 JSON不要输出多余文字”并增加异常兜底。6.4 图表空白或数据错位现象页面打开后图表区域空白Console 报Cannot read properties of undefined。可能原因接口返回的 data 为空。图表容器高度为 0。数据字段名和前端预期不一致。解决在 Network 面板确认接口真实返回。给图表容器设置明确高度例如height: 400px。先用console.log(result)打印返回结果确认字段名。6.5 中文乱码现象数据库能显示中文接口返回乱码或者前端展示乱码。检查顺序数据库连接 URL 是否带characterEncodingutf8。数据库表和字段是否使用utf8mb4。IDEA 中文件编码是否设置为 UTF-8。大模型返回内容乱码时确认返回响应体读取时是否指定 UTF-8。统一约定所有表、字段、连接串、页面文件都使用 UTF-8。7. 毕业设计落地的最佳实践7.1 学习环境与生产环境的差异学习环境跑通只需要一个本地 MySQL 和一个大模型 API Key。如果这个项目要部署到服务器展示需要考虑项目学习环境生产部署数据库本机 MySQL云数据库或服务器 MySQL开启备份API Key配置文件直接填环境变量或密钥管理服务大模型调用一次性调用加缓存、限流、失败重试日志控制台输出文件日志 按天滚动监控无基础健康检查接口推荐在项目里预留一个健康检查接口/api/health部署后发现服务异常时可以快速定位是数据库问题还是大模型接口问题。7.2 论文和答辩可以围绕哪些点展开这个题目的论文不是只写“我实现了一个系统”而是要有工程增量。可以按以下方向展开情感分析方案对比传统词典匹配、TF-IDF 机器学习、大模型提示词分析三者对比准确率和实现成本。大数据量下的聚合优化百万级评论按时间分组统计时如何用索引和定时汇总表优化。AI 大模型提示词工程如何设计 system prompt 约束输出格式如何解析不稳定输出。可视化选型对比ECharts 与 Highcharts、AntV G2 的区别为什么用 ECharts。答辩时重点讲“大模型在这里不负责数据统计只负责语义理解”这句话能防止老师误以为你把所有逻辑都依赖外部接口。7.3 功能扩展方向这个项目天然可以扩展不需要换架构接入实时评论流用定时任务每 5 分钟拉取一次新评论更新统计结果。热词分析用 HanLP 或大模型抽取评论关键词生成词云。多模型对比同时接入两个大模型对比不同模型的情感分析结果一致性。用户端增加评论发布功能用户提交评论后自动触发大模型情感分析并更新统计。权限控制Spring Security 区分管理员和普通用户管理员能看到全部数据普通用户只能看公开统计。7.4 对新手最重要的建议做这个项目顺序非常重要。不要一上来就写大模型调用代码先做三件事初始化评论数据把数据库表填满保证统计接口能返回数据。用 ECharts 显示普通统计图表先跑通前后端链路。再接入大模型先写一个最简单的analyzeComment接口能返回文字即可再逐步增加 JSON 解析和缓存。AI 大模型是项目的亮点但不是项目的地基。统计、可视化、前后端联调这些基础能力先跑通再往上面加大模型能力整个毕业设计的节奏会稳很多。