☰
Java后端+ECharts:疫情数据可视化大屏系统实战全解析
2026/10/4 15:01:05 网站建设 项目流程

简介:这是一套基于Java与ECharts构建的疫情数据可视化分析系统源码,主要面向具有一定编程基础、希望深入掌握数据采集与可视化展示流程的开发者。项目采用Spring Boot框架搭建后端服务,配合MyBatis访问MySQL数据库,同时利用爬虫定时获取公开疫情数据,再通过ECharts以折线图、柱状图、地图等多种形式直观呈现累计确诊、治愈、死亡等核心指标。整个资源包共包含一百零六个文件,包括二十八个Java后端类、十三个JavaScript交互脚本、十三份XML配置及MyBatis映射文件、十份CSS样式表、八份properties系统配置,以及建库SQL脚本、项目说明文档和启动命令等,压缩后整体大小约十兆字节。此外包内还附带授权文件、图标资源和若干示例图片,方便开发者快速还原运行环境并理解项目结构。这套系统已有1275人学习浏览,无论是用于课程设计、毕业设计还是个人技能提升,都可以提供完整的可运行参考,帮助读者从数据抓取、持久化存储到前端图表渲染一脉相承地完成整套系统。

1. Java 疫情分析系统到底在做什么:ECharts 这个库才是灵魂

如果你在求职简历上写“基于 Java 的疫情数据可视化系统”,面试官大概率会追问一句:“图表是你自己画的,还是用 ECharts 套的?”诚实答案是:Java 只负责当数据搬运工,真正让看板亮眼的是百度开源的 ECharts 这个 JavaScript 图表库。这个压缩包标题里把“Java”“ECharts”“数据可视化”三个词焊在一起,本质上就是一个经典的全栈演示项目:后端用 Java 和数据库管理疫情数据,前端用 ECharts 渲染折线图、柱状图、饼图和地图,最后拼成一个能交互的疫情监控大屏。

它能解决什么问题?对正在学 Java 后端的人来说,这是少有的能把 SSM/Spring Boot、MySQL、JSON、Ajax、前端图表一条线串起来的课题;对需要交课程设计、毕设或企业内训 demo 的人来说,这是一份能直接跑起来、能截图写进文档的完整源码。它适合的人群很明确:Java 基础语法过关、但没做过前后端联通项目的人,以及被导师要求“做个可视化”却不知道从哪下手的应届生。整个系统不复杂,但技术链完整,正好用来建立“后端怎么把数据送到前端”的完整心智。源码包不管来自哪个作者,这类项目的骨架高度一致,下面的展开就是按这份骨架来的。

2. 拆开源码包:从目录结构看懂这套系统的数据流

拿到“Java基于ECharts的数据可视化疫情分析系统源码.zip”,先别急着点 IDE 运行。第一件事是把压缩包解开,对着目录结构把数据流画出来。绝大多数同类项目都是标准 Maven 工程,目录长得差不多,但每个文件夹承担什么职责,决定了你后面改功能时该动哪。

2.1 目录结构里藏着三层架构:Controller 只管转 JSON,ECharts 只管画图

常见做法是把项目分为三块:src/main/java放后端代码,src/main/resources放配置文件和静态资源,src/main/webapp放前端页面。Java 代码里一般能看到controller、service、mapper、entity四个包,对应经典三层架构。举个例子,一个典型的包名是com.epidemic.controller,里面是EpidemicDataController之类的类;service里写业务逻辑,mapper(如果用 MyBatis)或dao(如果用 JdbcTemplate)写数据库访问。

前端呢?webapp/static下通常放着js/echarts.min.js、js/jquery.min.js、css/style.css和几个 HTML 页面,比如index.html、province.html、trend.html。注意,ECharts 是纯前端库,它不需要 Java 参与——Java 后端只负责把数据库里的疫情数据查出来,转成 JSON 字符串,通过 HTTP 接口返回,HTML 页面里的 JavaScript 拿到 JSON 后用 ECharts 渲染。这套结构最值得学习的就是“后端与前端完全解耦”:Controller 里返回@ResponseBody或@RestController的 JSON 结果,不掺任何图表代码;ECharts 的 option 全部写在 HTML 或 JS 文件里。这就是为什么项目标题敢把“Java”“ECharts”并列——两者通过 JSON 对话。

看源码时,按这个顺序读最省力:先看pom.xml确认用了哪些依赖(Spring Boot 还是 SSM、MySQL 驱动还是 MyBatis),再看application.properties或db.properties里数据库连接配置,然后打开 Controller,看它暴露了哪些 REST 接口,最后打开前端 HTML,找到$.ajax或fetch请求的 URL,跟 Controller 里的@RequestMapping对上。这一圈走完,你就能说清楚用户打开页面后发生了什么:页面加载 → JS 发异步请求 → Java 查库 → 返回 JSON → ECharts 根据 JSON 画图。

2.2 ECharts 的引入方式:压缩包里的 echarts.min.js 决定你能否离线运行

这类源码包一般会把 echarts.min.js 直接放在js目录下,而不是让你从 CDN 引用。原因很简单:课程设计要离线演示,答辩现场的教室网络经常连不上外网。所以拿到包后先检查static/js/下有没有这个文件,如果没有,去官网下载对应版本放进去。为什么强调版本?ECharts 5 和 ECharts 4 在 API 上有细微差别,比如 5 系列对地图的引入方式变了、主题注册方式不同,如果源码是 4 写的,你强行换 5 可能会报Cannot read property 'registerMap' of undefined之类的错。

常见做法是压缩包里自带哪个版本就用哪个版本。如果你需要手动引入,用 unpkg 的 esm 版或直接下载完整版:

<!-- 放在 HTML 的 head 或 body 末尾,必须优先于你自己的 echarts 初始化脚本 --> <script src="js/echarts.min.js"></script>

这行代码的讲究在于:ECharts 会挂载一个全局echarts对象,你的业务脚本必须在它之后加载,否则调用echarts.init时会报echarts is not defined。另外,如果页面里同时用了 jQuery 和 ECharts,两者互不依赖,各自引入即可,不需要考虑冲突。源码包的 HTML 里通常会同时出现这两个<script>标签,先用 jQuery 的$.getJSON请求数据,再用 ECharts 渲染。

2.3 数据库表设计是这套系统的地基:日期、省份、确诊、疑似、治愈、死亡

疫情分析系统既然叫“分析”,就离不开时间维度和地域维度。标准表结构是一张主表epidemic_daily,字段大致为:id主键、province省份、date日期、confirmed累计确诊、suspected疑似、cured治愈、dead死亡,再加一个createtime用于记录入库时间。部分项目会把全球数据、国内数据拆成两张表,或者把“新增确诊”单独做一列,方便画每日新增曲线。源码包里一般会附sql目录或db目录,里面有建表语句和导入数据,这是整个系统唯一的“数据源”。

我用 MySQL 5.7 时的一般建表语句长这样:

CREATE TABLE epidemic_daily ( id INT PRIMARY KEY AUTO_INCREMENT, province VARCHAR(50) NOT NULL, stat_date DATE NOT NULL, confirmed INT DEFAULT 0, suspected INT DEFAULT 0, cured INT DEFAULT 0, dead INT DEFAULT 0, update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_province_date (province, stat_date) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

注意UNIQUE KEY uk_province_date (province, stat_date)这一行。它保证了同一省份同一天只能有一条记录,避免因为重复跑数据脚本导致图上的数字翻倍。这是很容易被忽略的细节:如果源数据是 CSV 导入,你可能重复执行了两次 INSERT,折线图上的累计确诊在某个日期突然跳变,不是数据错了,是行重复了。加上唯一索引后,配合INSERT ... ON DUPLICATE KEY UPDATE可以把幂等性补上:

INSERT INTO epidemic_daily (province, stat_date, confirmed, suspected, cured, dead) VALUES ('湖北', '2024-03-01', 68100, 0, 63600, 3200) ON DUPLICATE KEY UPDATE confirmed = VALUES(confirmed), cured = VALUES(cured);

除了主表,部分源码还会配一张province_info表,存省份代码、拼音、坐标范围,用于 ECharts 地图。如果后端返回的 JSON 里需要“省份名”和“数值”两个字段,Controller 大概率是SELECT province, confirmed FROM epidemic_daily WHERE stat_date='xxx'。所以读 SQL 时你会发现,这套系统的后端逻辑比想象中简单——本质就是几张表按日期、省份分组聚合。

2.4 pom.xml 里的依赖组合:Spring Boot 2 + MyBatis + MySQL 是主流答案

源码包用 Maven 管理依赖时,pom.xml里最显眼的三个坐标是:spring-boot-starter-web、mybatis-spring-boot-starter、mysql-connector-java。如果是 Spring Boot 2.x,还会带spring-boot-starter-jdbc或spring-boot-starter-data-jpa。MyBatis 在这类系统里很常见,因为疫情数据的查询多为固定的按日期/省份查询,写 SQL 比 JPA 的自动 CRUD 更直观。少数项目会用 JdbcTemplate,代码更少,适合表结构极简的情况。

一个典型的pom.xml片段:

<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.18</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.3.2</version> </dependency> <dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <scope>runtime</scope> </dependency> </dependencies>

这里要留意的版本坑:Spring Boot 3.x 要求 Java 17 起步,而很多课程设计的源码是基于 Java 8 写的。如果你本机是 JDK 8,硬跑 Spring Boot 3 的项目会在编译期报错。拿到源码先看pom.xml的 parent 版本,再对照自己java -version的结果。ECharts 版本和 Java 版本是两套独立的兼容体系,别混淆。如果源码是 Spring Boot 2.7,JDK 8 完全够用;如果是 Spring Boot 3,那必须用 JDK 17。

3. 把源码跑起来的完整步骤:从数据库初始化到浏览器出现图表

动手运行之前,你要意识到这套系统不是一个“一键启动”的玩具。它依赖 MySQL、依赖初始化数据、依赖后端端口和前端静态资源的路径。每一步的顺序错了,都会看到不同的报错。我一般按“导库 → 改配置 → 启动后端 → 开前端页面”的顺序走。

3.1 第一步:导入 SQL 并核对数据量,别让图表空白

源码包里通常有一个epidemic.sql或init.sql。打开它先扫一眼:有没有CREATE DATABASE,表名是什么。如果脚本里没有建库语句,你要先手动建库:

CREATE DATABASE epidemic CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE epidemic; SOURCE /your/path/epidemic.sql;

导完之后别急着关命令行。执行几条 SELECT 验证数据真实存在:

SELECT COUNT(*) FROM epidemic_daily; SELECT stat_date, COUNT(DISTINCT province) FROM epidemic_daily GROUP BY stat_date;

这两条查询是后续排查的照妖镜。如果COUNT(*)是 0,图表一定空白,问题在数据导入而非前端代码;如果按日期分组后每个日期的省份数量不同,那后端查“最新一天”数据时可能漏掉某些省份。很多源码里的 SQL 文件是从真实疫情接口抓的数据,日期范围集中在 2020 年 1 月至 6 月,这没关系,图表展示的是趋势,不是实时数据。不过如果你想让系统看起来更新,需要自己造一批 2024 或 2025 年的模拟数据,注意把所有涉及日期的查询条件一起改掉,否则图上会出现“最新日期”和“旧日期”断层的难堪局面。

3.2 第二步:修改 application.properties 的数据库连接四项

这一步是新手翻车率最高的地方。源码作者用的是自己的账号密码,你拿过来必须改成你的本地环境。改错任何一个字符,后端启动时不报错,但一请求接口就抛Access denied或者Unknown database。

spring.datasource.url=jdbc:mysql://localhost:3306/epidemic?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai spring.datasource.username=root spring.datasource.password=123456 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

解释一下:characterEncoding=utf8保证从数据库查出来的中文传到前端不乱码;serverTimezone=Asia/Shanghai是 MySQL 8 的强制要求,不写会报The server time zone value的错。如果你的 MySQL 是 5.7,可以把useSSL=false加上,但不是必须。username和password改成你本机 MySQL 的账号——如果你不记得密码,用mysql -u root -p试一下,能进就是对的。

还需要看mybatis.mapper-locations配置。如果源码把 Mapper XML 放在src/main/resources/mapper下,但配置里写的是classpath:mapper/*.xml,且路径对不上,启动时会报Invalid bound statement (not found)。这种玄学问题最磨人,先检查这个配置。

3.3 第三步:启动 Spring Boot 后,先测接口再开页面

在 IDE 里直接运行带@SpringBootApplication的主类,或者用 Maven 命令启动:

mvn spring-boot:run

启动成功的标志是Tomcat started on port(s): 8080。这时先别打开页面,先用浏览器或 curl 测一个接口:

curl http://localhost:8080/api/epidemic/trend

如果返回一段 JSON,里面有日期和数值字段,说明后端链路是通的。如果返回 404,检查 Controller 的@RequestMapping和前端 AJAX 的 URL 是否一致;如果返回 500,看后台日志——最常见的错误是 SQL 查到不存在的列,比如前端要confirmed,SQL 里写成了confirm。

确认接口正常后,打开http://localhost:8080/index.html。如果页面空白,按 F12 打开开发者工具看 Console:报404说明静态资源路径不对,报echarts is not defined说明 echarts.min.js 没加载成功。这两种情况,分别检查spring.web.resources.static-locations配置和 HTML 里<script>的 src 路径。

3.4 第四步:ECharts 图表只渲染半截或空白的定位方法

图表出来了但数据是空的,这是最常见也最让人抓狂的现象,因为页面不报错。定位路径只有一条:打开 F12 → Network → XHR,刷新页面,找一个名为trend或province的请求,看看 Response 是不是[]或{"code":500}。如果是[],说明 SQL 查询结果为空,问题在数据库或 SQL 条件;如果是正常数据,问题在 JS 代码对字段名的处理。

举个例子,后端返回的 JSON 字段叫stat_date,前端 JS 写的却是date:

// 错误写法:字段名对不上,图表拿不到数据 trendData.forEach(function(item) { dates.push(item.date); values.push(item.confirmed); }); // 正确写法:按后端返回的实际字段名取 dates.push(item.stat_date);

这种坑就是“后端数据没问题、前端也不报错,但图表就是空”的经典来源。特别是用 MyBatis 时,如果application.properties里没开启map-underscore-to-camel-case=true,数据库字段stat_date映射到实体类时可能是stat_date而不是statDate,你直接用item.statDate取就是 undefined。最稳妥的办法是先console.log(JSON.stringify(data))把后端返回的原始 JSON 打印出来,再照着字段名写 JS。这套血泪经验适用于所有 Java + ECharts 项目,不只是疫情系统。

4. 最快能落地的三个 ECharts 图表:拆解配置与 Java 数据交互

系统跑通之后,你要理解图表本身是怎么组织数据的。ECharts 的配置全在一个 JavaScript 对象option里,Java 后端只需要提供xAxis.data和series.data。三种图表覆盖了疫情分析最常见的需求:全国趋势看折线,各省对比看柱状,结构占比看饼图。

4.1 折线图:累计确诊与治愈的趋势对照

折线图放在首页最合适,因为它能一眼看出疫情的爆发期、平台期、消退期。ECharts 的折线图type: 'line'配上smooth: true就会变成光滑曲线,视觉上更专业。

var trendChart = echarts.init(document.getElementById('trendChart')); var option = { tooltip: { trigger: 'axis' }, legend: { data: ['累计确诊', '治愈'] }, xAxis: { type: 'category', data: dates }, yAxis: { type: 'value', name: '人数' }, series: [ { name: '累计确诊', type: 'line', smooth: true, data: confirmedData }, { name: '治愈', type: 'line', smooth: true, data: curedData } ] }; trendChart.setOption(option);

这段代码里,dates、confirmedData、curedData三个数组来自异步请求的回调。Java 后端对应接口的写法是:

@GetMapping("/api/epidemic/trend") public Map<String, Object> trend() { List<Map<String, Object>> list = epidemicService.selectTrend(); Map<String, Object> result = new HashMap<>(); // 把 list 里的日期、确诊、治愈分别抽出来放进数组 List<String> dates = new ArrayList<>(); List<Integer> confirmed = new ArrayList<>(); for (Map<String, Object> row : list) { dates.add(row.get("stat_date").toString()); confirmed.add((Integer) row.get("confirmed")); } result.put("dates", dates); result.put("confirmed", confirmed); return result; }

这里有个参数细节:如果stat_date是java.sql.Date,直接 toString 会得到2024-03-01,ECharts 的 category 轴正好接受字符串日期。但如果后面要展示“3月1日”这种短格式,需要在前端做一次格式化,别在后端改格式,否则排序会乱。后端返回 JSON 时,Map里有Integer和String混用,Jackson 序列化后是{dates: ["2024-03-01"], confirmed: [68100]},前端拿到的数据结构必须和你 JS 里写的循环对应上。

4.2 柱状图:各省份累计确诊 Top10 的排序逻辑

柱状图常用于横向对比,比如“哪些省份最严重”。这类图表要求 SQL 里有排序和分组:

SELECT province, confirmed FROM epidemic_daily WHERE stat_date = (SELECT MAX(stat_date) FROM epidemic_daily) ORDER BY confirmed DESC LIMIT 10;

这里嵌套子查询取“最新一天”的数据,避免把历史每一天的都查出来导致柱状图难以阅读。前端配置很简单:

barOption = { xAxis: { type: 'value' }, // 横向柱状图把 value 放在 xAxis yAxis: { type: 'category', data: provinces, // 省份名 inverse: true // 让最多的省份显示在最上面 }, series: [{ type: 'bar', data: numbers, label: { show: true, position: 'right' } }] }; barChart.setOption(barOption);

inverse: true是一个易被忽略的参数。SQL 里ORDER BY confirmed DESC查出来的第一条是最大值,但 ECharts 的 category 轴默认从下往上排列,第一个数据会出现在底部。如果不加inverse: true,你看到的图会变成“最大的在下面”,完全反直觉。想保持 SQL 不用改,就在前端加这一行;想用主动思维控制,就在 SQL 里ORDER BY confirmed ASC,让第一条是最小值,ECharts 从下往上画,视觉上同样是最大的在顶部。两种做法等价,但团队协作时要统一,否则换个人维护就乱了。

如果你想把柱状图顶部显示具体数值,就开label.show = true,位置选'top'或'right'。很多热搜词里提到的“y轴的标签如何在每个柱子上面显示”就是问这个——label.position设成'right'对应横向柱,设成'top'对应纵向柱,没有第三种玄学。

4.3 饼图与地图:占比展示和疫情热力分布的边界情况

饼图适合展示“累计确诊的省份占比”,比如前五名省份占全国多少。ECharts 饼图没有 x 轴和 y 轴,数据格式是{name: '湖北', value: 68100}的结构,后端返回的就是List<Map>,前端在 AJAX 回调里原样塞进series.data即可。

pieOption = { tooltip: { trigger: 'item', formatter: '{b}: {c} ({d}%)' }, series: [{ type: 'pie', radius: ['40%', '70%'], // 环形饼图 data: pieData }] }; pieChart.setOption(pieOption);

这里要提一句大数据量的坑:如果省份有 30 多个,饼图会变成“针尖图”,小省份的占比扇形几乎看不清。常见做法是只取 Top5,其余归为“其他”,这个逻辑可以放在 SQL 里用UNION写,也可以放在 Java 代码里算。更省事的做法是改成使用 ECharts 的地图——但地图需要额外的中国地图 GeoJSON 文件和echarts.registerMap注册,源码包不一定自带,需要联网下载。如果没有地图文件,就别硬做地图,用柱状图替代效果一样。

饼图中间加文字也是高频需求,很多热搜词提“echarts pie 中间的字”。这个不是饼图的 title,而是graphic元素或title组件定位在饼图中心:

title: { text: '累计确诊', subtext: String(total), left: 'center', top: '40%' }

注意top: '40%'是个经验值,因为环形图中心区域高度受radius影响,不同屏幕比例下文字位置会偏。最稳妥的方案是用graphic配合计算出的中心坐标,但新手阶段用 title 居中已经够看,不必过度优化。

4.4 时间轴联动:把日期滑块变成动态大屏的加分项

疫情分析有个特殊需求:随时间变化看趋势。折线图天然支持时间轴,但如果你想让首页的地图或柱状图跟随日期切换,需要在 ECharts 里加timeline组件,或者在页面放一个<input type="range">,每次滑动触发一次 AJAX 请求。我一般建议后端接口设计成带日期参数:

@GetMapping("/api/epidemic/province") public List<Map<String, Object>> province(@RequestParam("date") String date) { return epidemicService.selectByDate(date); }

前端用 jQuery 绑定滑块事件:

$('#dateSlider').on('input', function() { var date = $(this).val(); $.getJSON('/api/epidemic/province', {date: date}, function(res) { myChart.setOption({ series: [{ data: res }] }); }); });

这样做的好处是后端无状态,前端控制交互,逻辑简单。坏处是每次滑动都打一个 HTTP 请求,如果数据量大或网络慢,会有明显的延迟。课程设计演示时,可以预先把所有日期的数据一次性加载到前端,用timeline滑动切换,但那样代码复杂度会上升,容易在答辩现场翻车。优先采用滑块请求的方案,稳定优先,这就是实战和炫技的区别。

5. ECharts 与 Java 数据交互的五个避坑记录:现象、原因、解决

把上面的链路走通不难,难在遇到问题时能快速定位。这部分记录我实际调试同类项目时踩过的五个高频坑,每一条都可以在本地复现,希望你能直接记住,而不是到时候再去翻黄山文档。

坑一:图表显示“NAN”或控制台报ECharts is not defined,页面白屏。现象是打开 HTML 后没有任何图表,F12 报Uncaught ReferenceError: echarts is not defined。原因几乎都是echarts.min.js这个 script 标签放在了初始化脚本的后面,或者路径写错返回 404。解决方法是把<script src="js/echarts.min.js">放到所有业务 JS 之前,并确认浏览器 Network 里这个文件的状态码是 200。顺带检查一下,如果是用 Thymeleaf 模板引擎,src路径要写th:src="@{/js/echarts.min.js}",否则会多出一层项目名前缀,导致 404。

坑二:折线图数据点全部为 0,但数据库里有数。现象是图能渲染出来,曲线趴在 0 的位置。原因定位在后端 SQL 或 Java 类型转换:比如 MySQL 里confirmed字段是BIGINT,实体类却用Integer接收,超出范围后会被转成 0 或抛出异常;更常见的是 SQL 里查的是sum(confirmed),如果该日期没有数据,聚合函数返回null,Java 拿到null放进int数组变成 0。解决方法是先curl接口看 JSON 里对应字段是不是null;是 null 的话,在 SQL 中用IFNULL(SUM(confirmed), 0)兜底,或者在 Java 中统一用Integer并判空。

坑三:柱状图的数据顺序和图例对不上。现象是图里第一个 Bar 对应的是 SQL 里的最后一行。原因就是前面提过的 category 轴方向问题,解决方法是加inverse: true,或者在后端把 SQL 改成ORDER BY confirmed ASC。如果做到这里还是对不上,排查一下 SQL 里是否有GROUP BY province ORDER BY confirmed DESC LIMIT 10同时存在,LIMIT和ORDER BY的执行顺序跟你想的可能不一致。

坑四:饼图数据全部挤在一起,且每个饼块颜色都接近。现象是饼图只有两三种颜色,大户省份和小户省份难以分辨。原因是 ECharts 默认调色板只有 8 种颜色,超过 8 类数据会自动循环调色,如果数据有 30 类,颜色就重复了。解决方案有两种:一是用color属性传入一个更长的调色板数组;二是在数据层面做 Top10 + “其他”合并,后者对疫情大屏更有意义——没人关心第 20 名省份的精确占比。这里不需要写 Java 代码,但在service层做合并比在前端做更合理,因为后端返回的 JSON 就应该已经是“适合展示”的结构。

坑五:地图组件报registerMap is not defined。现象是用了type: 'map'的 series 后就报错。原因是你没有引入中国地图的 GeoJSON 数据,或者引用的版本与 ECharts 5 不再兼容。ECharts 5 之后,地图数据需要单独下载china.js,然后在 ECharts 之后引入:<script src="js/china.js"></script>,并在 JS 里执行echarts.registerMap('china', chinaJson)。如果源码包里只有一个 echarts.min.js、没有 china.js,那就不要用地图图表,换成柱状图或饼图,别在答辩前才补地图数据。

这些坑有一个共同的主线:前后端的数据契约——JSON 的字段名、类型、顺序、null 值——是整套系统最脆弱的连接点。Java 后端写的每一个Map.put,前端 JS 都会直接读取;后端多一个空格、少一个下划线,前端就是 undefined。所以调试时养成习惯:先看 Network 里原始响应,再写前端取值逻辑。

6. 进阶验证手段:把爬虫抓到的接口数据导入你的系统,验证整条链路

系统跑通、图表正常之后,你是不是觉得“这就是个静态数据的展示器”?下一个值得做的是把定时抓取的数据灌进去,让你的系统变成“有源活水”。这个操作不需要改前端,重点是写好 Java 的定时任务和 SQL 的幂等插入。

最常见的进阶做法是使用 Spring 的@Scheduled注解,每天凌晨自动调用外部数据接口(比如各省卫健委公开的 JSON API),解析后写入epidemic_daily表:

@Component public class EpidemicDataSyncTask { @Autowired private EpidemicMapper epidMapper; @Scheduled(cron = "0 0 1 * * ?") public void sync() { // 1. 调用外部接口拿到 JSON 字符串 String json = restTemplate.getForObject("https://example-api/province", String.class); // 2. 用 Jackson 解析成 List<ProvinceData> List<ProvinceData> list = objectMapper.readValue(json, new TypeReference<>() {}); // 3. 逐条 upsert 到数据库 for (ProvinceData d : list) { epidMapper.upsert(d); } } }

这段代码里的三个步骤,每个都有讲究。第一步的 URL 是假造的,真实场景要替换成目标接口的完整地址,同时考虑接口返回的字段名和你的实体类是否一致;第二步的TypeReference是为了把 JSON 数组反序列化成泛型 List;第三步的upsert对应我们在第 2 章写的ON DUPLICATE KEY UPDATE,保证同一天同一省份的数据只更新不新增。

验证这个功能是否正常,最直接的方法是改系统时间或手动触发一次 sync 方法,然后对比数据库里的update_time字段。如果更新的行数为 0,大概率是stat_date格式不一致,比如外部接口返回"2024/03/01"而你的实体类字段是Date类型,转换失败会抛异常并把整个任务拦停。所以建议在sync方法里逐条处理并捕获异常,打印出是哪条数据出了问题,不要因为一条脏数据让整个任务挂掉。

写到这里,我想起一个真实的教训:我做类似系统时,偷懒没加唯一索引,数据同步脚本跑了三遍,前端累计确诊曲线直接变成锯齿状,还以为是 ECharts 渲染的问题,最后查库才发现重复行占了三分之一。打那以后,我养成了一个习惯——任何数据导入型功能,第一件事就是给自然键(日期+省份)加唯一索引。这个习惯花不了几分钟,但能替你省下以后排查数据的无数个夜晚。如果你现在正打算照着这套思路做一个疫情分析大屏,先把数据幂等性想清楚,再动手写图表,顺序别反。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询