简介:一套基于SpringBoot与ECharts的新闻可视化分析平台完整源码及数据库,适合正在学习Spring Boot后端开发、ECharts数据可视化,或需要完成课程设计、毕业设计的开发者。项目将新闻数据的采集、存储、查询与可视化展示串联起来,通过柱状图、折线图等图表直观呈现新闻趋势、热门话题热度变化与类别分布,有助于理解前后端接口设计及数据联动流程。压缩包共69个文件、约1.68MB,主要包含Java后端源码、前端页面与js/css文件、SQL与CSV新闻数据、Maven构建脚本和Markdown说明文档,同时附有界面截图与效果预览图,目录结构清晰,便于按模块对照学习。目前已有37人下载学习。借助源码中的新闻实体类、Service业务层、Controller请求处理以及ECharts图表配置,可快速掌握从数据库设计到可视化展示的完整链路;压缩包内的附赠内容与备份文件,也为二次开发和调试提供了有价值的参考。
1. 这个 SpringBoot+Echarts 平台到底解决了什么问题
每天几万条新闻涌进来,光看标题列表你根本看不出哪个领域在升温、哪个来源在刷屏、热度曲线是往上走还是往下走。这个基于 SpringBoot+Echart 的新闻可视化分析平台,就是把「数据落库 → 聚合统计 → 接口输出 → 图表展示」整条链路做通的一个完整工程:MySQL 存新闻数据,SpringBoot 负责把查询结果组装成图表要的 JSON,前端 Echarts 负责渲染成趋势折线、分类占比和来源 Top 榜。项目自带数据库建表脚本和业务源码,跑起来就是一套能直接演示的新闻数据分析环境。适合三类人:拿 SpringBoot 做课设或毕设、想要一个不是玩具项目的完整参照的学生;想在公司内部搭一套资讯/新闻数据展示看板的小团队;以及想彻底搞懂 SpringBoot 后端如何对齐 Echarts 数据格式的前后端开发。下面从架构到部署,把每个环节和踩过的坑一次讲透。
2. 技术选型与数据链路:为什么是这个组合,数据怎么流到图表
2.1 选型理由:SpringBoot 管接口,Echarts 管渲染,MySQL 管聚合
先说 SpringBoot。它在这类项目里几乎是默认选项,不是因为玄学,而是因为它把过去 Spring 项目里最繁琐的配置全收了:内嵌 Tomcat、自动配置数据源、starter 一键引入 MyBatis 或 JPA,你写一个@RestController加一个@Mapper就能把数据库表暴露成 HTTP 接口。对于新闻可视化这个场景,后端要做的事情其实很薄——接收请求、查库、聚合、返回 JSON,SpringBoot 在这条链路里的心智负担是最低的。
再说 Echarts。它之所以被选中而不是 Highcharts 或 D3,核心原因是 Echarts 是「JSON 配置驱动」的:你给一个option对象,里面写上xAxis、series、tooltip,它就画图。图表类型是line、bar还是pie,只是series.type一个字段的事。这意味着后端的聚合结果基本可以 1:1 映射到图表的data字段上,省掉了 D3 那种手写 SVG 的复杂度。
最后是 MySQL。新闻数据本质上是结构化数据,标题、来源、分类、发布时间、热度值,天然适合关系型存储。更重要的是,图表要的「分类数量趋势」「来源 Top 10」都可以用 SQL 的GROUP BY直接算出来,不需要把全表数据拉到内存里再算。这几层选型合起来就是一句话:让数据库做聚合,让 SpringBoot 做搬运,让 Echarts 做展示。
2.2 完整数据链路:从 MySQL 表到浏览器图表的七个环节
我一般会把这个项目的数据流拆成这样,排查问题的时候按这个链路逐层查:
| 层级 | 对应文件/组件 | 职责 |
|---|---|---|
| 数据存储 | sql/news_info.sql | 原始新闻表 |
| 聚合统计 | news_category_daily表 | 按日期+分类预聚合 |
| ORM 映射 | NewsStatsMapper.java/Mapper.xml | 执行聚合 SQL |
| 业务组装 | NewsStatsService.java | 把查询结果拼成图表 JSON |
| 接口暴露 | NewsStatsController.java | @RestController输出 |
| 前端请求 | static/index.html中的fetch | 拉取接口数据 |
| 图表渲染 | EchartssetOption() | 画图 |
这里有一个很多新手会犯的错:试图把全量新闻数据返回给前端,让前端 JS 去算分组和总和。千万别这么干。图表接口返回的应该已经是「图表可以直接用的数据」,而不是原始数据。前端只负责把data[0]塞进series[0].data,不该做任何聚合计算。
2.3 SpringBoot 基础配置:application.yml 里的关键参数
拿到源码包后第一步不是急着跑,而是先把application.yml里的数据源改成本地环境。下面是这份资源里典型的配置,我加了逐项说明:
server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/news_analysis?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8&allowPublicKeyRetrieval=true username: root password: root driver-class-name: com.mysql.cj.jdbc.Driver mybatis: mapper-locations: classpath:mapper/*.xml configuration: map-underscore-to-camel-case: true这里几个参数是血泪经验换来的。serverTimezone=Asia/Shanghai不写的话,MySQL 8 和 JDBC 驱动之间会出现 8 小时时差,查询出来的publish_time会比你数据库里实际值早 8 个小时,图表上的时间轴会整体偏移。characterEncoding=utf8不写,中文新闻标题在返回 JSON 时会出现乱码。allowPublicKeyRetrieval=true是 MySQL 8.x 连接时的常见报错点,后面避坑章节专门讲。
还有一个注意点:如果源码包里的spring-boot-starter-parent是 3.x 版本,它要求 JDK 17+,本地 JDK 8 会直接启动失败。我看到很多人在这个位置翻车,解决方案两个:要么把 JDK 换到 17,要么把pom.xml里 SpringBoot 版本降到 2.7.x。后面避坑章节细说。
3. 数据库设计与数据准备:报表结构要提前想好
3.1 新闻表和聚合统计表怎么建
这份资源里通常会准备两张核心表:一张存原始新闻数据,一张存按日期和分类聚合后的统计结果。原始表负责「存事实」,统计表负责「给图表提速」。
news_info表结构如下:
CREATE TABLE news_info ( id INT AUTO_INCREMENT PRIMARY KEY, title VARCHAR(255) NOT NULL COMMENT '新闻标题', source VARCHAR(100) COMMENT '来源站点', category VARCHAR(50) COMMENT '新闻分类:科技/财经/体育/娱乐等', publish_time DATETIME COMMENT '发布时间', heat INT DEFAULT 0 COMMENT '热度值,用于排序和均值计算', content TEXT COMMENT '正文摘要', KEY idx_category_time (category, publish_time), KEY idx_source (source) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;注意两个细节:一是category和publish_time建了联合索引,因为后面所有报表查询基本都带WHERE category = ? AND publish_time BETWEEN ? AND ?,没有这个索引数据量大了之后查询会非常慢;二是字符集用utf8mb4而不是utf8,因为新闻标题里可能出现 emoji 或特殊符号,utf8存不了四个字节的字符。
统计表news_category_daily的设计是这份资源里比较关键的地方:
CREATE TABLE news_category_daily ( id INT AUTO_INCREMENT PRIMARY KEY, stat_date DATE NOT NULL COMMENT '统计日期', category VARCHAR(50) NOT NULL COMMENT '新闻分类', news_count INT DEFAULT 0 COMMENT '当日该分类新闻数', avg_heat INT DEFAULT 0 COMMENT '当日该分类平均热度', UNIQUE KEY uk_date_cat (stat_date, category) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;这个表解决什么问题?假设你想看「最近 30 天科技类新闻的数量趋势」,没有这张表就要对news_info做GROUP BY DATE(publish_time), category,数据量到了几十万级别时接口响应会明显变慢。有了预聚合表,接口只需要SELECT * FROM news_category_daily WHERE category='科技' ORDER BY stat_date,毫秒级返回。这是数据可视化项目里非常标准的「空间换时间」做法。
3.2 数据导入:源码包里的 SQL 与模拟数据脚本
源码包里的sql/目录一般包含完整的建表和预置数据脚本。导入方式很简单:
mysql -uroot -proot news_analysis < sql/news_info.sql mysql -uroot -proot news_analysis < sql/news_category_daily.sql如果你拿到的是带数据的版本,运行完这两条命令后库里就有现成的可查询数据,直接启动 SpringBoot 就能看到图表。
但如果没有预置数据,或者你想自己生成一批规模更大的数据来压测,我一般会写一个 Python 脚本造模拟数据,因为手写 SQL 插入几百条太累,而且造数据要模拟出「分布特征」才有图表效果:
import random import pymysql from datetime import datetime, timedelta conn = pymysql.connect(host='127.0.0.1', user='root', password='root', database='news_analysis', charset='utf8mb4') cursor = conn.cursor() categories = ['科技', '财经', '体育', '娱乐', '国际'] sources = ['新浪新闻', '腾讯新闻', '网易新闻', '今日头条', '澎湃新闻'] start_date = datetime(2024, 1, 1) for i in range(5000): publish_time = start_date + timedelta( days=random.randint(0, 90), hours=random.randint(0, 23), minutes=random.randint(0, 59) ) category = random.choice(categories) source = random.choice(sources) heat = random.randint(0, 10000) title = f'[{category}] 模拟新闻标题 {i}' cursor.execute( 'INSERT INTO news_info (title, source, category, publish_time, heat) VALUES (%s, %s, %s, %s, %s)', (title, source, category, publish_time, heat) ) conn.commit() cursor.close() conn.close()脚本里的random.randint分布决定图表形态:热度完全均匀随机时折线图是平的,没有趋势感。想要好看的趋势,可以在heat生成逻辑里加一个基于publish_time日期的正弦波动,模拟工作日高峰、周末低谷,这样画出来的折线才有真实项目的感觉。数据量 5000 条足够演示,想压测可以改成 50000。
3.3 聚合查询:GROUP BY 是图表的燃料
统计表不会自己填数据,要么用定时任务,要么手动跑聚合 SQL。把news_info聚合成news_category_daily的核心 SQL 是这样的:
INSERT INTO news_category_daily (stat_date, category, news_count, avg_heat) SELECT DATE(publish_time) AS stat_date, category, COUNT(*) AS news_count, ROUND(AVG(heat)) AS avg_heat FROM news_info WHERE publish_time >= DATE_SUB(CURDATE(), INTERVAL 90 DAY) GROUP BY DATE(publish_time), category ON DUPLICATE KEY UPDATE news_count = VALUES(news_count), avg_heat = VALUES(avg_heat);注意末尾的ON DUPLICATE KEY UPDATE很重要:因为uk_date_cat唯一键的存在,重复跑这条 SQL 不会插入重复数据,而是更新已有统计值。这样不管是每天定时跑还是手动重复执行,结果都是稳定的。实际资源里这段逻辑可能会放在 SpringBoot 的启动ApplicationRunner里,或者用@Scheduled定时触发,后面章节会展示代码。
4. SpringBoot 接口层:把查询结果组装成 Echarts 要的 JSON
4.1 接口返回结构:先约定后编码
做可视化项目的第一条铁律就是:先定 JSON 结构,再写后端代码,最后才碰前端。如果前后端各写各的,字段名对不上是最常见的翻车现场。这份资源里接口返回结构一般是下面这样的规范格式,按data字段传对象、code和message走统一响应:
{ "code": 0, "message": "success", "data": { "dates": ["2024-01-01", "2024-01-02", "2024-01-03"], "series": [ { "name": "科技", "data": [12, 18, 25] }, { "name": "财经", "data": [8, 10, 15] } ] } }为什么dates和series分开而不是塞成对象数组?因为 Echarts 的折线图xAxis.data需要的是日期数组,series需要的是并列的多条线数据。后端按这个结构返回,前端只需要chart.setOption({ xAxis: { data: res.data.dates }, series: res.data.series }),一行代码都不用加工。如果返回成[{date:'2024-01-01', category:'科技', count:12}]这种行式结构,前端就要自己遍历重组,既慢又容易错。
4.2 Controller 到 Mapper 的完整实现
后端代码一般拆成三层。Controller 层只做请求接收和响应包装,不写业务逻辑。下面是核心代码,我按这份资源的常见实现方式给出:
@RestController @RequestMapping("/api/news") public class NewsStatsController { @Autowired private NewsStatsService newsStatsService; // 分类数量趋势:/api/news/trend?category=科技&days=30 @GetMapping("/trend") public Map<String, Object> trend(@RequestParam(required = false) String category, @RequestParam(defaultValue = "30") Integer days) { List<Map<String, Object>> rows = newsStatsService.getDailyStats(category, days); return buildTrendResponse(rows); } // 来源 Top 榜:/api/news/topSources?limit=10 @GetMapping("/topSources") public Map<String, Object> topSources(@RequestParam(defaultValue = "10") Integer limit) { List<Map<String, Object>> rows = newsStatsService.getTopSources(limit); return buildPieResponse(rows); } private Map<String, Object> buildTrendResponse(List<Map<String, Object>> rows) { List<String> dates = new ArrayList<>(); Map<String, List<Integer>> seriesMap = new LinkedHashMap<>(); for (Map<String, Object> row : rows) { String date = String.valueOf(row.get("stat_date")); String cat = String.valueOf(row.get("category")); Integer count = ((Number) row.get("news_count")).intValue(); dates.add(date); seriesMap.computeIfAbsent(cat, k -> new ArrayList<>()).add(count); } List<Map<String, Object>> series = new ArrayList<>(); seriesMap.forEach((k, v) -> { Map<String, Object> s = new HashMap<>(); s.put("name", k); s.put("data", v); series.add(s); }); Map<String, Object> result = new HashMap<>(); result.put("dates", dates); result.put("series", series); return result; } }这里有两个细节值得注意。第一,seriesMap用LinkedHashMap而不是HashMap,是为了保证分类顺序稳定,否则前端图例顺序每次请求都会变。第二,stat_date从 MyBatis 查出来可能是java.sql.Date,直接String.valueOf会得到2024-01-01这种标准格式,但如果数据库驱动返回的是Timestamp,就会带00:00:00后缀,所以稳妥的做法是在 SQL 里用DATE_FORMAT(stat_date, '%Y-%m-%d')转好字符串再返回。
Service 层和 Mapper 层对应如下:
@Service public class NewsStatsService { @Autowired private NewsStatsMapper newsStatsMapper; public List<Map<String, Object>> getDailyStats(String category, Integer days) { return newsStatsMapper.selectDailyStats(category, days); } public List<Map<String, Object>> getTopSources(Integer limit) { return newsStatsMapper.selectTopSources(limit); } }<!-- NewsStatsMapper.xml --> <select id="selectDailyStats" resultType="map"> SELECT DATE_FORMAT(stat_date, '%Y-%m-%d') AS stat_date, category, news_count, avg_heat FROM news_category_daily <where> <if test="category != null and category != ''"> AND category = #{category} </if> </where> AND stat_date >= DATE_SUB(CURDATE(), INTERVAL #{days} DAY) ORDER BY stat_date </select> <select id="selectTopSources" resultType="map"> SELECT source, COUNT(*) AS news_count FROM news_info GROUP BY source ORDER BY news_count DESC LIMIT #{limit} </select>Mapper 接口里对应写两个方法声明,@Mapper注解标上,MyBatis 会自动扫描到NewsStatsMapper.xml里的同名 SQL。<where>标签是动态 SQL 的关键,category参数为空时自动去掉该条件,实现「不传 category 就查全部分类趋势」的效果。
4.3 定时任务:统计表自动更新
聚合统计表不会自己长数据,这份资源里一般会在 SpringBoot 启动时自动跑一次初始化,同时注册一个每日定时任务。代码通常在启动类或独立组件里:
@Component public class DailyStatsTask { @Autowired private JdbcTemplate jdbcTemplate; // 每天凌晨 1 点执行一次统计聚合 @Scheduled(cron = "0 0 1 * * ?") public void aggregateDailyStats() { String sql = "INSERT INTO news_category_daily (stat_date, category, news_count, avg_heat) " + "SELECT DATE(publish_time), category, COUNT(*), ROUND(AVG(heat)) " + "FROM news_info WHERE publish_time >= DATE_SUB(CURDATE(), INTERVAL 1 DAY) " + "GROUP BY DATE(publish_time), category " + "ON DUPLICATE KEY UPDATE news_count = VALUES(news_count), avg_heat = VALUES(avg_heat)"; jdbcTemplate.execute(sql); } }注意@Scheduled需要主启动类上加了@EnableScheduling才会生效,这是新手最容易漏的。cron 表达式0 0 1 * * ?的意思是「每天 1 点 0 分 0 秒执行」,?是秒位占位符,表示不指定。如果不加@EnableScheduling,这个定时任务会静默失效,统计表永远停留在初始数据,图表时间轴不再增长。
5. 前端 Echarts 可视化:从接口 JSON 到图表渲染的完整实践
5.1 页面初始化与 Ajax 拉取
前端部分在这份资源里一般就是一个static/index.html,引入 Echarts 的 JS 文件后开始渲染。用 CDN 还是本地文件看你的环境,内网部署建议把echarts.min.js下载到static/js/目录下,避免外网依赖。
页面中画趋势折线图的核心逻辑如下:
const chart = echarts.init(document.getElementById('trendChart')); fetch('/api/news/trend?days=30') .then(res => res.json()) .then(data => { if (data.code !== 0) { console.error('接口异常:', data.message); return; } const { dates, series } = data.data; chart.setOption({ tooltip: { trigger: 'axis' }, legend: { data: series.map(s => s.name) }, grid: { left: '10%', right: '5%', top: '15%', bottom: '15%' }, xAxis: { type: 'category', data: dates }, yAxis: { type: 'value', name: '新闻数量' }, series: series.map(s => ({ name: s.name, type: 'line', smooth: true, data: s.data })) }); }) .catch(err => console.error('请求失败:', err));这里要注意一个常见的白屏原因:echarts.init的容器trendChart在 JS 执行时必须已经存在于 DOM 中。如果你把<script>标签写在了<div id="trendChart">前面,document.getElementById返回null,Echarts 会在控制台报Can't get dom width or height之类的错。正确做法是把脚本放在页面末尾,或者用window.onload或DOMContentLoaded包裹。
5.2 饼图、柱状图与可视化大屏适配
除了趋势折线,这份资源里典型还有「来源占比饼图」和「分类数量柱状图」。饼图的 option 片段:
const pieChart = echarts.init(document.getElementById('sourcePie')); fetch('/api/news/topSources?limit=10') .then(res => res.json()) .then(data => { if (data.code !== 0) return; const { names, values } = data.data; pieChart.setOption({ tooltip: { trigger: 'item', formatter: '{b}: {c} ({d}%)' }, legend: { orient: 'vertical', left: 'right' }, series: [{ type: 'pie', radius: ['30%', '65%'], data: names.map((name, i) => ({ name, value: values[i] })) }] }); });radius: ['30%', '65%']表示这是一个环形图而不是实心饼图,内径 30%、外径 65%,视觉效果更现代。formatter: '{b}: {c} ({d}%)'中{d}是 Echarts 内置的占比变量,会在 tooltip 里自动显示百分比。
如果你要做可视化大屏,所有图表容器都要考虑自适应。我的习惯是加一个窗口监听,让所有图表实例统一 resize:
window.addEventListener('resize', () => { chart.resize(); pieChart.resize(); });大屏套件的常见布局是用grid或flex把页面切成 3 列,上面放趋势图、左下放词云、右下放 Top 榜。字体大小和图表高度用vw/vh单位而不是px,避免在不同分辨率下布局错乱。
5.3 echart axis.type 与参数调校:category 还是 time
Echarts 折线图xAxis.type有两个选择:category和time。这个选择决定了日期数据的解析方式,也是很多人被坑的地方。
如果xAxis.type: 'category',xAxis.data里的每个值都会被当作「类目」,原样显示在横轴上,不解析、不排序。如果xAxis.type: 'time',Echarts 会把data里的值当作时间戳或日期字符串来解析,自动排序并计算坐标刻度。两种方式的适用场景完全不同:
| 场景 | 推荐 type | 原因 |
|---|---|---|
日期是字符串且格式统一(2024-01-01) | category | 原样展示,显示规则可控 |
| 日期是时间戳或 ISO 字符串 | time | 自动解析,适合非均匀时间间隔 |
| 想控制横轴显示密度(每 3 天标一个刻度) | category+axisLabel.interval | time轴刻度不可控 |
我在实际项目里更倾向于用category,因为后端 SQL 已经在DATE_FORMAT里统一了日期格式,前端拿到的不存在解析歧义。用time轴反而容易出现「日期字符串被解析成 UTC 时间导致横轴偏移 8 小时」的莫名其妙的显示问题,那是我踩过的最莫名其妙的一个坑。
5.4 柱状图与折线图组合:双 Y 轴场景
新闻数据里经常要同时对比「数量」和「平均热度」,两者量级不同,数量一般是几十到几百,热度可能是几千到几万,放在同一个 Y 轴里数量那根线会被压扁成一条直线。解决方案是用双 Y 轴:
series: [ { name: '新闻数量', type: 'bar', yAxisIndex: 0, data: countData }, { name: '平均热度', type: 'line', yAxisIndex: 1, data: heatData } ]对应的yAxis要写成数组两个对象,分别设置name: '数量'和name: '热度'。这个配置在 Echarts 里叫「多轴联动」,不算难,但新手经常忘了加yAxisIndex,导致两根线挤在一个轴上,图表形态完全失真。
6. 避坑与常见问题排查:本地跑通这个平台的五处关键细节
6.1 现象:SpringBoot 启动直接失败,报UnsupportedClassVersionError
原因:源码包的pom.xml里 SpringBoot 版本是 3.x,它编译时目标 JDK 是 17,而你本地 JDK 是 8 或 11,字节码版本不兼容,JVM 在加载主类时直接抛异常。
解决:两个方案选一个。方案一:把本地 JDK 换到 17,JAVA_HOME指过去后重新mvn clean package。方案二:把pom.xml里的spring-boot-starter-parent版本降到2.7.18,同时确认java.version标签改成1.8,然后mvn clean再启动。我一般推荐方案一,因为 SpringBoot 3.x 的jakarta.*命名空间和 2.x 的javax.*差异较大,降版本可能伴随其他依赖兼容问题。
6.2 现象:接口查询报Public Key Retrieval is not allowed
原因:MySQL 8.x 默认使用caching_sha2_password认证插件,JDBC 驱动第一次连接时需要向服务器请求公钥来加密密码传输,但连接串里没有允许这个行为,于是驱动抛异常拒绝连接。
解决:在application.yml的 JDBC URL 末尾加allowPublicKeyRetrieval=true,完整的连接串是:
url: jdbc:mysql://localhost:3306/news_analysis?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai&characterEncoding=utf8useSSL=false也要保留,本地开发环境没有配置 SSL 证书,驱动默认尝试加密连接会额外报 SSL 警告。
6.3 现象:前端图表白屏,F12 控制台报echarts is not defined
原因:echarts.min.js没引入成功。最常见的两种:一是<script src="js/echarts.min.js">路径写错,浏览器 404;二是脚本放在了 DOM 之前导致echarts.init传入的容器不存在。
解决:先用浏览器 F12 的 Network 面板确认echarts.min.js请求状态是 200。然后确认init的执行时机——把页面脚本统一放到</body>前面,或者用window.onload包裹。这里我用一个习惯杜绝此坑:所有 Echarts 初始化代码都包进一个initCharts()函数,在window.onload里统一调用,既保证 DOM 就绪,还能在函数里统一加console.log验证。
6.4 现象:折线图横轴日期全部挤成一团,且显示的是数字而不是日期
原因:xAxis.type被设成了time,而后端返回的日期字符串2024-01-01中带横杠,Echarts 把它解析成时间戳后,由于默认刻度算法不合适,标签挤在一起;有时候还会显示成毫秒时间戳数字。
解决:优先用category轴并显式设置xAxis.data,这样日期是什么就显示什么。如果一定要用time轴,给xAxis.axisLabel加formatter: (value) => echarts.format.formatTime(value, '{yyyy}-{MM}-{dd}'),强制把时间戳格式化成日期字符串。
6.5 现象:图表存在闪烁或动画卡顿,页面滚动时明显
原因:这是两个问题叠加。一是setOption被频繁调用(比如页面里有刷新定时器),Echarts 每次都做全量 diff,开销大;二是图表容器在滚动或尺寸变化时被不断重绘。前端圈里管这叫「图表翻车现场」,不致命但很影响观感。
解决:setOption时传入第二个参数true,强制覆盖而不是合并:
chart.setOption(option, true);用于刷新场景时,在 fetch 新的数据前先chart.clear()再setOption,避免旧图形残留。定时刷新加一个防抖,比如每 5 秒拉一次数据时,用setInterval但只在数据有变化时才setOption,不然每次刷新都是全量重绘。
这五条是本地跑这个平台最容易卡的五个位置,前两条属于环境类,后三条属于代码类。按「配置 → 依赖 → 数据 → 渲染」的顺序排查,绝大多数问题能在 10 分钟内定位。
7. 部署与进阶用法:打包、启动和日常维护技巧
7.1 打 jar 包与启动
后端打标准 SpringBoot jar 包,执行:
mvn clean package -DskipTests java -jar target/news-visualization-0.0.1-SNAPSHOT.jar如果想换端口而不改配置,启动时加参数:
java -jar target/news-visualization-0.0.1-SNAPSHOT.jar --server.port=8081这个参数优先级高于application.yml,线上临时改端口时不用重新打包,也算是有后悔药可吃。前端index.html里的接口请求路径如果写的是绝对路径带端口,改完要同步;如果写的是相对路径/api/news/trend,那后端换端口完全不影响页面访问。
7.2 进阶:前端工程化整合与词云扩展
如果你的前端不是单个 HTML,而是 Vue 或 React 工程,最省事的做法是执行npm run build后把dist目录里的index.html、js、css全部拷贝到 SpringBoot 的static/目录下,然后后端接口保持@RestController不动。这是「Vue 打包放进 SpringBoot」的标准姿势,注意dist/index.html里的资源引用如果是绝对路径/js/xxx.js,要改成相对路径,否则后端根路径改变时资源会 404。
功能扩展方向上,除了折线、柱状、饼图,这份资源还可以加两个高价值图表:词云和地图。词云用 Echarts 扩展插件echarts-wordcloud,只需要后端多提供一个「高频关键词」接口,前端把它渲染成词云图,视觉冲击力比普通柱状图强得多,适合大屏展示。
7.3 数据维护的日常工作
这个平台跑起来之后,我一般的日常巡检顺序是:先看news_category_daily表最新日期是不是今天,再看定时任务日志有没有执行成功,最后打开页面确认图表时间轴在增长。定时任务失败最常见的迹象就是图表停在昨天或更早的日期上,这个时候查日志比调前端代码有效得多——先确认数据端没问题,再考虑是不是接口或渲染层的问题。
从那以后,我每次在本地跑这类 SpringBoot+Echarts 项目,都强制先做一遍三步检查:SQL 字段名和后端返回的 key 有没有对齐、Echarts 的xAxis.data和series.data长度是否一致、连接串的时区和编码参数有没有漏。这三处全对了再打开浏览器,能省掉至少一半的图表白屏和乱码时间。希望帮到你。
本文还有配套的精品资源,点击获取