ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Spring Boot + ECharts 疫情数据可视化系统搭建与避坑指南

Spring Boot + ECharts 疫情数据可视化系统搭建与避坑指南 简介这套源码是一份基于Java技术栈与ECharts的数据可视化疫情分析系统完整实现面向具备一定编程基础的后端学习者、数据可视化开发人员及毕业设计参考人群。项目以Spring Boot为核心框架整合MyBatis持久层与MySQL数据库借助ECharts实现折线图、柱状图、饼图等动态图表完整呈现从数据爬取、存储到前端渲染的全链路流程。压缩包共一百零六个文件整体约十兆主要包含二十八份Java源码、十三份JS脚本、十三份XML配置、十份CSS样式另有SQL数据库脚本、属性配置、依赖包及工程文档等目录结构清晰便于导入开发环境直接调试、扩展或二次开发。项目文件名为Covid-19-Data-Showing-master内部按数据模型、控制器、服务、配置与前端视图等分层组织并含有爬虫相关工具类可用于自动抓取并更新疫情数据。目前已有1275人学习下载适合希望掌握Spring Boot、MyBatis、MySQL与ECharts整合方法、理解实战项目架构的开发者参考。1. 拿到这套 Java 疫情分析系统源码包先别急着双击一个压缩包丢到桌面文件名写着“Java基于ECharts的数据可视化疫情分析系统源码.zip”你大概率想知道三件事这东西能不能跑起来、代码值不值得读、我能不能在上面改出自己的作品。我的建议是先别急着解压双击先把它当作一个“前后端分离的可视化看板工程”来验收。这套系统的本质是用 Spring Boot 提供统计接口用 ECharts 把确诊、治愈、死亡这类公开统计数据画成折线图、饼图和中国地图适合正在做毕设、想补全项目经验的 Java 学习者也适合企业里要把报表做成可视化大屏的入门参考。能不能跑通九成取决于 JDK 版本、MySQL 配置和地图 JS 文件的引入方式而不是可视化代码本身——这个反直觉结论你往下看就会明白。2. Spring Boot MyBatis-Plus 搭后端先把三层结构和依赖选型理顺2.1 为什么选 Spring Boot MyBatis-Plus而不是 SSM 或 JPA疫情分析系统的数据量级通常很小一天几千条记录已经算多难点根本不在并发而在“快速把接口写出来、把数据结构交代清楚”。Spring Boot 自带内嵌 Tomcat能省掉一堆 XML 配置MyBatis-Plus 则是在 MyBatis 之上做了增强单表 CRUD 不用手写 SQL分页查询直接调selectPage对新手非常友好。如果你用原生 SSM光是 Spring、SpringMVC、MyBatis 三个配置文件的版本兼容就够你折腾两天。用 JPA 也可以但它的自动建表策略和复杂统计 SQL 的匹配度不如 MyBatis尤其在写GROUP BY聚合查询时XML 里的 SQL 一眼能看懂比 JPQL 好调试得多。2.2 最小工程结构与实体类设计建表脚本和 Java 字段要一一对应我一般会建一个标准的 Maven 多模块工程但既然是源码包单模块也能说清楚。关键是把controller、service、mapper、entity、vo分包放好。实体类对应一张epidemic_data表字段不外乎这些省份或地区、日期、确诊累计、治愈累计、死亡累计。别小看这张表的设计它直接决定后续饼图和地图能不能画出来——地图需要“省份名”趋势图需要“日期”占比图需要“数值”缺一个字段就得回炉。Data TableName(epidemic_data) public class EpidemicData { TableId(type IdType.AUTO) private Long id; private String province; private LocalDate statisticDate; private Integer confirmed; private Integer cured; private Integer dead; }这段代码里TableName指定了表名TableId(type IdType.AUTO)表示主键自增LocalDate用来存日期。注意字段名用了驼峰statisticDateMyBatis-Plus 默认开启驼峰映射所以数据库列名只要写成statistic_date就能自动对应。2.3 用 MyBatis-Plus 根据实体类生成建表 SQL一条命令省掉手工建表这里要提一个热门操作MyBatis-Plus 的代码生成器不仅能生成实体类还能配合MySqlGenerator反向生成建表语句。如果你的pom.xml里引了mybatis-plus-generator可以直接写一个生成器类来建表。但更常见的做法是让实体类本身变成设计文档再用工具导出 SQL。CREATE TABLE epidemic_data ( id bigint(20) NOT NULL AUTO_INCREMENT, province varchar(50) DEFAULT NULL COMMENT 省份, statistic_date date DEFAULT NULL COMMENT 统计日期, confirmed int(11) DEFAULT NULL COMMENT 确诊累计, cured int(11) DEFAULT NULL COMMENT 治愈累计, dead int(11) DEFAULT NULL COMMENT 死亡累计, PRIMARY KEY (id), KEY idx_province_date (province, statistic_date) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这里给province和statistic_date建了联合索引是因为最常见的查询就是“按省份查时间序列”这条索引能让折线图接口的响应快一个量级。如果你拿到手的源码包里没有建表脚本别慌按这个结构自己补一张即可。索引字段的顺序也有讲究等值条件province放前面范围条件statistic_date放后面才能充分利用联合索引的最左前缀原则。3. 疫情数据落库与统计口径从原始数据到聚合接口3.1 数据从哪来以及字段的口径怎么定疫情分析系统的数据通常来自公开的每日通报或爬虫采集拿到手是 CSV 或 Excel。入库之前必须统一“口径”否则前端画出来的图自己都解释不了。什么叫口径confirmed是“累计确诊”还是“新增确诊”必须写清楚日期粒度是“按天”还是“按周”也要定死。我一般会在数据库注释和实体类注释里同时说明避免半个月后自己翻代码时犯迷糊。表结构到位后导入数据有两种方式一是用LOAD DATA INFILE直接灌 CSV二是写一个简单的导入接口。对于源码包演示直接在 Navicat 里执行 SQL 导入是最快的。导入后先跑一条验证语句确认数据行数和日期范围符合预期。SELECT COUNT(*), MIN(statistic_date), MAX(statistic_date) FROM epidemic_data;这条 SQL 是任何导入操作后的“后悔药”——如果最大值日期比文件里的最后一天小说明有数据没导进去如果NULL大概率是日期格式解析失败。数据干净了后端才有底气往下走。3.2 趋势、占比、分布三类统计 SQLGROUP BY 是核心疫情分析系统最少要支撑三类图表折线图要“每日全国确诊趋势”饼图要“各省确诊占比”地图要“各省确诊分布”。它们对应三个统计接口但底层 SQL 逻辑是相通的都是GROUP BY加聚合函数。select idselectDailyTrend resultTypemap SELECT statistic_date AS date, SUM(confirmed) AS confirmed, SUM(cured) AS cured, SUM(dead) AS dead FROM epidemic_data GROUP BY statistic_date ORDER BY statistic_date ASC /select这段 SQL 放在EpidemicDataMapper.xml里返回结构是“日期 三个统计值”的 List。要注意SUM(confirmed)的前提是表里的confirmed是“当日累计数”如果表里存的是“当日新增”那这里要改成SUM(confirmed) OVER窗口函数做累加逻辑会复杂很多。select idselectProvincePie resultTypemap SELECT province AS name, SUM(confirmed) AS value FROM epidemic_data WHERE statistic_date (SELECT MAX(statistic_date) FROM epidemic_data) GROUP BY province ORDER BY value DESC /select饼图取的是“最新一天”的各省确诊数所以用子查询卡住MAX(statistic_date)。这里有个细节如果同一天某省有多条记录SUM会把它们加总而不会报错导致饼图占比虚高。我会在数据导入阶段按“省 日期”做去重或者在 SQL 里加DISTINCT否则前端怎么看都觉得不对劲。地图接口跟饼图接口几乎一样只是后端把它按省名返回前端交给 ECharts 的map系列去消费。3.3 聚合接口返回结构约定VO 统一字段名前端不扯皮很多源码包的坑不在后端也不在前端而在接口返回字段命名不一致。后端写statisticDate前端用date图表渲染出来全是undefined。我习惯在 controller 层做一次“翻译”用 VO 把后端字段转成前端真正要用的名字。GetMapping(/trend) public ResultListMapString, Object trend() { return Result.ok(epidemicDataService.selectDailyTrend()); } GetMapping(/pie) public ResultListMapString, Object pie() { return Result.ok(epidemicDataService.selectProvincePie()); }这里的Result是统一封装类结构是code message data。前端拿到后先判断code 200再取data。这套约定没什么新意但非常管用——企业里做数据可视化看板前后端扯皮最多的就是字段名。你在源码包里如果看到 controller 里直接return list而没有包装建议加上Result不然全局异常处理都没法接。4. ECharts 在 Spring Boot 里的落地从静态页面到企业级可视化看板4.1 引入 ECharts 的正确姿势静态资源还是 CDN这是所有 ECharts 项目里翻车频率最高的环节。很多源码包在static/js/下只放了一个echarts.min.js版本还是 4.x然后 HTML 里用script标签引相对路径这没问题。但如果你想用中国地图必须额外引入china.js或 GeoJSON 文件这一步漏掉地图区域就渲染成空白。还有一种常见做法是把 ECharts 丢在 HTML 里走 CDN 加载省去本地资源管理但内网环境或离线演示时会直接白屏。我是建议本地化echarts.min.js和地图 JSON 都下载到项目里用 Spring Boot 的static目录直接托管这样不受外网影响。页面结构上放一个div idchart再写一个initCharts()方法在document.ready里调用。!DOCTYPE html html langzh-CN head meta charsetUTF-8 title疫情数据可视化/title script src/js/echarts.min.js/script script src/js/china.js/script style#chart { width: 100%; height: 600px; }/style /head body div idchart/div script src/js/dashboard.js/script /body /htmlchina.js的作用是注册中国地图坐标系没有它echarts.registerMap(china, ...)就会报错。这也是标题里“数据可视化”的关键所在——地图不是自动画的必须注册地图数据。用 CDN 时china.js的版本要和echarts.min.js对齐否则会出现“地图轮廓没有但名字对不上”的怪问题。4.2 中国地图的配置细节visualMap 与 data 的映射地图系列的配置是源码包里的重头戏。初始化时先registerMap再把接口返回的数据setOption进去数据里的省份名必须与 GeoJSON 里的名称完全一致比如“内蒙古”不能写成“内蒙”。这个坑我踩过不止一次。var chart echarts.init(document.getElementById(chart)); fetch(/api/pie) .then(function (res) { return res.json(); }) .then(function (data) { chart.setOption({ tooltip: { trigger: item }, visualMap: { min: 0, max: 50000, left: left, text: [高, 低], inRange: { color: [#e0f3f8, #74add1, #4575b4] } }, series: [{ type: map, map: china, roam: true, label: { show: true }, data: data.data }] }); });这里visualMap的max是硬编码的 50000实战中应该从后端接口里把那天的最大值一起返回否则颜色映射失真。roam: true开启缩放拖动在做企业级数据可视化看板时几乎是标配但注意地图缩放后如果图例left和top固定会遮挡地图边缘建议给visualMap加calculable: true让用户能手动拖动图例范围。4.3 折线图与饼图的参数调试x 轴刻度、渐变色和 legend 换行折线图是疫情分析的门面也是体现“最热操作”的地方。ECharts 折线图的 x 轴默认自动计算刻度但日期类型的数据经常出现刻度挤在一起、标签重叠显示成省略号的问题。我一般在xAxis里显式设置type: category、boundaryGap: false并配合axisLabel控制间隔。xAxis: { type: category, boundaryGap: false, data: dates, axisLabel: { interval: Math.ceil(dates.length / 8), rotate: 30 } }interval的计算不是随便写的目的是让 x 轴最多显示 8 个日期标签太少看不出趋势太多必然重叠。rotate: 30是让标签斜着排挤出阅读空间。饼图那边如果你想让颜色更有质感可以给series加itemStyle.color用线性渐变而不是纯色itemStyle: { color: function (params) { return new echarts.graphic.LinearGradient(0, 0, 1, 0, [ { offset: 0, color: #ff7f50 }, { offset: 1, color: #ff5252 } ]); } }这里LinearGradient的四个参数分别是 x1, y1, x2, y2表示渐变方向。横向渐变用在柱状图、径向渐变用在饼图会更自然。这种视觉细节对看板整体观感影响很大能让你做的系统一眼看出“像回事”。4.4 从单图表到看板联动筛选与 resize 自适应演示系统只有一张图没意思要撑起“分析系统”这个名字至少得有三个图表上边折线图左下饼图右下地图再加一排筛选条件按省份、按日期范围。ECharts 的联动不需要额外组件只要在折线图上绑一个events事件点击省份后把地图和饼图一起setOption更新。关键点在于处理好异步时序用户快速点击时上一次请求可能还没回来导致图表显示旧数据和新数据混在一起。chart.on(click, function (params) { fetch(/api/province?name params.name) .then(function (res) { return res.json(); }) .then(function (data) { pieChart.setOption({ series: [{ data: data.data }] }); }); });这段代码在浏览器请求没有做竞态处理时快速切换省份会出现饼图闪烁旧比例。我一般会给请求加一个简单序号标记只让最后一个请求生效。另外布局上强烈建议用flex或grid管理图表尺寸给每个 div 一个明确的宽高并监听window.resize调用chart.resize()否则窗口一拉大图表就“黑匣子”了一样留在原地。5. 避坑与排查从环境到渲染的常见问题记录5.1 现象页面白屏控制台报Map china not exists这不是后端问题而是前端没有正确注册地图。原因有三一是china.js没有引入二是引入顺序不对china.js必须在echarts.min.js之后加载因为前者要往后者全局对象上挂载地图三是 GeoJSON 里的地图名是空字符串。解决办法是按顺序引入脚本打开浏览器 Network 面板确认静态资源状态码是 200 而不是 404。如果用了 CDN还要注意 ECharts 4.x 和 5.x 的地图注册 API 有差异5.x 里china.js的注册方式已经变了。5.2 现象后端启动报Table epidemic_data doesnt exist源码包里没带建表脚本是常态。原因是开发者默认你本地有数据库但没把schema.sql放进resources目录。解决方式就是回到第 2.3 节根据实体类补建表 SQL。如果工程里配置了ddl-auto之类的自动建表也可能因为字段类型不匹配而静默失败比如LocalDateTime对应不上 MySQL 的datetime。建议启动前手动执行建表脚本再用SELECT验证不要把黑匣子留给自动建表。5.3 现象饼图和地图数据全没显示接口返回正常但前端data.data是 undefined这是前后端字段约定没有对齐。接口结构是Result.ok()数据包在data字段但前端代码写的是res.data.data还是res.data要看具体情况。如果后端把ListMap直接塞进 data那前端拿res.data就是数组了长度正常但没有name和value字段。解决办法是打开浏览器控制台把返回 JSON 打出来看一眼字段名叫province就把它映射成namedata.map(item ({ name: item.province, value: item.confirmed }))。这是最琐碎也最让人上头的坑。5.4 现象日期折线图 X 轴全挤在一起像一根刺这不是数据错误而是axisLabel默认把每个日期都画出来了87 个日期当然重叠。解决方法是显式指定interval为函数或数字。我一般先算数组长度用Math.ceil(len / 8)让标签数量控制在 8 个左右再根据实际渲染效果调整。数据从LocalDate序列化成 JSON 后是2024-01-01这种字符串前端可以直接当category用但如果想显示“1月1日”这种短格式需要在前端做一次字符串裁剪不要指望后端帮忙。5.5 现象窗口放大后图表不跟随甚至出现滚动条原因是图表容器 div 的宽度单位是px固定值viewport 变化而图表不会跟着变。ECharts 的chart.resize()需要自己监听窗口事件而且必须在容器尺寸变化后调用。常见做法是window.addEventListener(resize, function () { chart.resize(); pieChart.resize(); mapChart.resize(); });这里要注意多个图表对象都要各自调一次resize只调其中一个会导致其他图表定格。更稳的做法是做一个图表实例管理器统一注册和销毁。如果页面里有 Tab 切换切换后处于隐藏状态的图表宽度可能为 0需要强制重新调用resize否则切回来就是一片空白。6. 进阶把分析系统做成大屏——缓存预热与渲染优化项目跑通之后下一步自然是想让它“像企业级数据可视化大屏”。这一步先别急着加炫酷特效先把性能边界摸清楚。疫情数据按天累计一年最多 300 多条记录SQL 查询毫秒级返回但前端反复请求接口也会把浏览器拖慢。我习惯用 Spring 的Scheduled做缓存预热每 10 分钟把三个核心聚合查询的结果放进内存缓存接口直接读缓存而不是每次去查 MySQL。Scheduled(fixedDelay 600000) public void preloadCache() { trendCache epidemicDataService.selectDailyTrend(); pieCache epidemicDataService.selectProvincePie(); }fixedDelay 600000表示上次任务完成后 10 分钟再执行下一次避免定时任务在接口高峰期抢数据库连接。要注意缓存里的数据是“快照”如果后台导入了新数据最多 10 分钟后才会体现在图上这在大屏场景是可接受的。如果你要严格实时那这条路就不合适回到直接查库即可。还有一个常被忽略的优化页面里多个图表不要同时setOption大数据量数组用chart.showLoading()先盖一层加载动画数据回来后再hideLoading()体验会好很多。最后一个技巧是调试时用浏览器开发者工具的 Performance 面板记录渲染时间——如果单次setOption超过 100ms优先检查地图 GeoJSON 是不是太大或者visualMap的splitNumber设得过密。这套系统做下来我最大的教训是可视化项目的坑不在 ECharts 本身而在数据从数据库到前端展示的一整条链路任何一个字段口径不一致、静态资源没引全都能让看似正确的代码白屏。先跑通最小闭环再考虑把每个环节做扎实是效率最高的路径。希望这些踩坑记录能帮你把这个源码包更快变成自己的作品。本文还有配套的精品资源点击获取
返回列表