ARTICLE DETAIL

资讯详情

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

Java Spring Boot + ECharts 疫情数据可视化系统设计与实现

Java Spring Boot + ECharts 疫情数据可视化系统设计与实现 简介这是一份基于Java与ECharts构建的疫情数据可视化分析系统完整源码适合具备Java基础、希望学习Spring BootMyBatisMySQL全栈开发与前端图表展示的开发者。项目通过爬虫抓取疫情数据结合ECharts以折线图、柱状图等形式动态呈现覆盖数据采集、存储、接口服务与可视化展示全流程。压缩包共106个文件约10MB核心文件类型包括28个java源码、13个js脚本、13个xml配置、10个css样式以及sql、properties等辅助资源目录结构清晰便于快速定位与二次开发。资源已有1275人学习附带了完整的项目代码、配置说明与数据库脚本可帮助读者理解疫情分析系统从后端接口到前端图表的实现思路适合课程设计、毕业设计或技术演练场景。1. 用Java和ECharts做疫情可视化这门课设为什么值得你抄下来随手打开一个Java课程设计的答辩现场十个组里有八个交的是控制台输出的“学生管理系统”剩下两个做网页的图表还停留在用JFreeChart画饼图。但如果你看过真正用ECharts做的数据可视化项目会明显感觉到一个差距页面是活的——鼠标划过去有tooltip柱状图能渐变色地图按省份着色还有涟漪效果。标题里的这个“Java基于ECharts的数据可视化疫情分析系统源码”本质上就是把Java后端的数据处理能力和ECharts前端的渲染能力拼在一起做一个能答辩、能写进简历、还能顺手学到前后端联调流程的完整项目。适合谁正在找Java课程设计案例源码的在校生想练Spring Boot接口开发的新手以及需要给老板交一个“企业级数据可视化”看板样子的初级工程师。这篇我直接把实现路径拆开讲从数据怎么洗、接口怎么写到图表怎么配、坑在哪全部按可复现的标准来。2. 从CSV到接口疫情数据清洗与后端聚合的真实写法2.1 数据从哪来不连数据库也能跑通的静态数据方案多数课程设计场景下你拿不到真实的疫情数据库也不该把时间耗在爬虫上。常见做法是准备一份CSV或JSON静态数据放在src/main/resources下让Spring Boot在启动时读取并缓存到内存里。这样有几个好处第一项目在别人的电脑上clone下来直接能跑不用配数据库第二答辩时你能解释清楚“为什么用内存存储”——因为数据量只有几万行内存聚合比数据库查询更快也更直观。我一般用OpenCSV或者直接BufferedReader手写解析看个人习惯。下面这段代码是把CSV读成对象的典型写法Component public class CovidDataLoader implements ApplicationRunner { private final ListCovidRecord records new ArrayList(); Override public void run(ApplicationArguments args) throws Exception { try (BufferedReader reader new BufferedReader( new InputStreamReader( new ClassPathResource(covid_data.csv).getInputStream(), StandardCharsets.UTF_8))) { String line; reader.readLine(); // 跳过表头 while ((line reader.readLine()) ! null) { String[] cols line.split(,); records.add(new CovidRecord( cols[0], // 省份 cols[1], // 日期 Integer.parseInt(cols[2]), // 新增确诊 Integer.parseInt(cols[3]), // 新增治愈 Integer.parseInt(cols[4]) // 新增死亡 )); } } } }这里需要注意几点ClassPathResource的路径不能带/前缀直接用相对resources的路径reader.readLine()跳过表头很重要否则第一行数据会被当成字段名解析StandardCharsets.UTF_8必须显式指定避免在Windows中文环境下用默认GBK编码读UTF-8文件导致的乱码。文件的列之间不要有空格最好统一用,分隔否则split(,)会把北京 这种带尾随空格的字符串也带进去后面聚合时会产生两个不同的分组键。2.2 按省份和日期聚合用Stream写内存聚合比SQL更顺手数据加载进来了接下来要解决的核心问题是前端要什么形状的数据后端就聚合出什么形状。比如ECharts柱状图需要{ date: 2020-02-01, value: 123 }这种结构饼图需要{ name: 湖北, value: 67802 }这种结构。用Java 8的Stream做内存分组聚合比写SQL更直观也方便在聚合过程中做条件过滤。Service public class CovidStatService { private final CovidDataLoader loader; public ListMapString, Object sumByProvince() { MapString, Integer grouped loader.getRecords().stream() .filter(r - r.getDate().startsWith(2020-02)) // 只看2月数据 .collect(Collectors.groupingBy( CovidRecord::getProvince, Collectors.summingInt(CovidRecord::getConfirmed) )); return grouped.entrySet().stream() .sorted(Map.Entry.String, IntegercomparingByValue().reversed()) .limit(10) .map(e - { MapString, Object item new HashMap(); item.put(name, e.getKey()); item.put(value, e.getValue()); return item; }) .collect(Collectors.toList()); } }这段代码的关键点在于groupingBy的分类函数和下游收集器。Collectors.summingInt会生成一个int累加器不需要手工维护Map里的旧值sorted配合comparingByValue().reversed()得到的是按累计值降序的流limit(10)截取前10个省份正好对应柱状图的“Top10”展示。而输出的MapString, Object是最通用的JSON友好结构Spring Boot的Jackson会自动把它序列化成[{ name: 湖北, value: 67802 }, ...]。顺带说一句如果前端要用ECharts折线图展示全国每日新增趋势就改成按日期分组groupingBy(CovidRecord::getDate)即可其他逻辑完全复用。这就是“数据结构决定聚合逻辑”的含义——先确认图表需要什么再去写聚合。2.3 接口返回的JSON结构字段命名规范与时间格式的坑后端接口写好之后暴露给前端的字段命名要注意两点一是ECharts的series.data里name和value这两个字段名是约定俗成的尽量不要改成cityName和totalCount这类自定义名除非你在前端做一层映射二是时间字段如果直接用java.util.Date序列化Jackson默认输出的是时间戳数字或带时区的ISO字符串不是前端期望的2020-02-01格式。常见做法是在application.yml里固定全局的日期格式spring: jackson: date-format: yyyy-MM-dd time-zone: GMT8更保险的方式是让VO里的日期字段直接声明为String类型在Service层聚合时就格式化成2020-02-01这种字符串。这样彻底绕开序列化规则不一致的问题也算是一个不算优雅但绝对稳定的做法。另外接口统一返回MapString, Object并包一层code/msg/data结构前端fetch的时候先判断code再取data这对后续加权限、加异常处理都留了余地。3. ECharts在前端如何吃下后端数据柱状图、饼图与地图的配置细节3.1 从fetch到setOption前后端数据对接的最小闭环后端接口跑通了前端这一步就是把接口返回的JSON塞进ECharts的option里。很多新手卡在这一步就是因为不清楚fetch返回的是Response对象需要先调res.json()才能拿到数据再调setOption。下面是一个完整的最小页面直接复制到static/index.html就能用!DOCTYPE html html langzh-CN head meta charsetUTF-8 title疫情数据可视化/title !-- 使用国内CDN本地部署时建议下载到static目录 -- script srchttps://cdn.jsdelivr.net/npm/echarts5.4.3/dist/echarts.min.js/script /head body div idbarChart stylewidth: 800px; height: 500px;/div script const chart echarts.init(document.getElementById(barChart)); fetch(/api/covid/province/top10) .then(res res.json()) .then(data { // 这里data已经是解析后的JavaScript对象 chart.setOption({ title: { text: 各省累计确诊Top10 }, tooltip: { trigger: axis }, xAxis: { type: category, data: data.map(item item.name) }, yAxis: { type: value }, series: [{ type: bar, data: data.map(item item.value), // 柱状图渐变色属于ECharts配置里出效果最快的一招 itemStyle: { color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ { offset: 0, color: #83bff6 }, { offset: 1, color: #2f7ed8 } ]) } }] }); }) .catch(err console.error(加载数据失败:, err)); /script /body /html注意echarts.init必须在DOM元素渲染完成后调用所以script要放在div之后或者包在window.onload里。fetch的路径是相对路径开发时Spring Boot的静态资源默认映射/static/**所以index.html直接放static目录下访问http://localhost:8080/就会加载它然后页面里fetch(/api/covid/province/top10)会自动指向本机后端接口不存在跨域问题。这套配置下来一个完整的“后端起数据、前端画图表”的最小闭环就通了。3.2 柱状图与折线图参数哪些配置是菜鸟教程不会强调的很多人照着ECharts菜鸟教程抄配置能画出来但总感觉“没有项目感”。差别通常出在几个细节参数上barMaxWidth控制柱宽不设的话在数据只有5条时会挤成几条粗柱子boundaryGap决定柱状图第一个柱子离y轴的距离柱状图设true、折线图设false这是两种图表的典型差异axisLabel.rotate在分类轴文字过长时设为30或45度能避免标签重叠。折线图如果展示30天内的每日新增需要额外注意x轴刻度的密度。ECharts默认会自动抽稀刻度但抽稀后可能正好把峰值日期藏掉了。解决方式是显式设置xAxis.minInterval: 1强制每天一个刻度再配合axisLabel.interval: 0让所有标签都显示。代价是标签会密这时要么旋转45度要么把formatter改成只显示日期中的MM-dd。xAxis: { type: category, data: dates, minInterval: 1, axisLabel: { interval: 0, rotate: 45, formatter: value value.substring(5) // 只显示02-01这种格式 } }formatter里的substring(5)会把2020-02-01截成02-01既缓解了横向空间压力又能保留时间序列的连续性。折线图的areaStyle如果要渐变和柱状图的配置方式完全一致normal里的color改成LinearGradient即可这就是热词里提到的echarts areastyle 渐变色的落点。3.3 饼图与地图玫瑰图让占比更直观地图本质是GeoJSON注册饼图本身不难真正让答辩加分的是把普通饼图改成南丁格尔玫瑰图也就是roseType: angle。这样半径可以设为数组[20%, 80%]每一项的面积按数值等比放大视觉冲击力比普通饼图强很多。图例如果省份太多设orient: vertical和right: 0让它竖排靠右避免挤压画布。中国地图要额外引入地图GeoJSON数据。ECharts 5不再内置地图需要从https://geo.datav.aliyun.com/areas_v3/bound/100000_full.json加载省份边界数据然后调用echarts.registerMap(china, geoJson)注册再在series里指定map: china。fetch(https://geo.datav.aliyun.com/areas_v3/bound/100000_full.json) .then(res res.json()) .then(geoJson { echarts.registerMap(china, geoJson); chart.setOption({ series: [{ type: map, map: china, roam: true, visualMap: { min: 0, max: 5000, text: [高, 低], calculable: true, inRange: { color: [#e0f3f8, #abd9e9, #2f7ed8] } }, data: provinceData // [{name: 湖北, value: 67802}, ...] }] }); });visualMap是地图上色的核心min和max要结合业务数据动态设定写死的话在数据量级变化后颜色会失真。一个技巧是先用后端聚合算出最大值再动态传给visualMap.max或者在前端拿到数据后自行计算Math.max(...values)。另外roam: true支持缩放拖拽演示时能让评委看到地图交互效果属于性价比极高的一个参数。4. 前后端联调与打包部署本地跑通是底线能部署到服务器才是加分项4.1 Spring Boot默认静态资源与接口路径规划开发环境下Spring Boot会自动把classpath:/static/目录下的文件映射为根路径资源。也就是你只要把index.html放进src/main/resources/static/启动应用后浏览器访问http://localhost:8080/就能直接看到页面。这套默认行为对单人开发的课程设计项目非常友好不需要任何Nginx或Tomcat独立配置。但路径规划上有一个常见坑如果index.html里引用的JS/CSS用了绝对路径/js/echarts.min.js而项目的context-path被改过比如设成了/covid那所有静态资源都要加上这个前缀。我一般建议你别动context-path保持默认/接口路径统一以/api/covid/开头这样静态页和接口的路径边界清晰前端fetch里的相对路径也不会因为context-path问题而404。如果你想在本地下载ECharts文件而不是走CDN就把echarts.min.js放进static/js/目录然后引用script src/js/echarts.min.js/script。CDN模式有一个隐患如果答辩现场没有外网页面会因为加载不到ECharts而白屏。所以“本地化JS”是答辩前必须做的一步属于可预见的设备环境风险。4.2 打成Jar包表单提交与数据加载都正常才算真正跑通部署不一定要上云服务器。本地打包成可执行Jar然后在任意一台有JDK的机器上运行是验证项目完整性的最低标准。Maven打包命令mvn clean package -DskipTests生成的Jar在target/目录下运行java -jar covid-analysis.jar即可。如果启动后访问页面能正常显示图表数据说明项目是自洽的。这里有几个需要提前确认的点第一src/main/resources/static/下的页面和JS是否都被打进了Jar你可以用jar tf covid-analysis.jar | grep static查看第二数据文件covid_data.csv有没有被打进BOOT-INF/classes/第三端口是否被占用Spring Boot默认8080如果你本地的其他服务占了8080启动日志里会出现Port 8080 was already in use解决办法是启动时加--server.port9000。4.3 外部Tomcat部署与接口跨域的区别如果用独立Tomcat部署War包或者前后端分离部署在不同端口就一定会遇到跨域问题。Spring Boot解决跨域最简单的是加一个配置类Configuration public class CorsConfig { Bean public WebMvcConfigurer corsConfigurer() { return new WebMvcConfigurer() { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/api/**) .allowedOrigins(http://localhost:5173) // Vite默认端口 .allowedMethods(GET, POST, PUT, DELETE) .allowedHeaders(*); } }; } }这里有一个细节allowedOrigins不要写成*因为如果后续要带Cookie*和allowCredentialstrue是冲突的。课程设计不涉及登录态写*可以但如果项目扩展了登录功能必须显式指定来源域名否则前端请求会自动失败。5. 避坑与常见问题排查五个让新手卡到凌晨的典型故障5.1 中文标签变成方块或问号现象ECharts图表里的省份名、图例名显示为小方块。 原因通常是HTML文件本身没有声明meta charsetUTF-8导致浏览器按系统默认编码解析页面中文全部乱码另一个原因是CSV文件编码是GBK而Java读取时用了UTF-8数据在加载阶段就已经乱掉了。 解决HTML的head最上面加meta charsetUTF-8Java读取CSV时用InputStreamReader指定StandardCharsets.UTF_8如果原文件是Excel另存的CSV先用Notepad或VS Code将编码转为UTF-8再放进resources目录。5.2 地图渲染出来是一张空白网格现象使用ECharts地图时画布上有坐标轴或碎块但没有中国地图轮廓。 原因ECharts 5确实不再内置世界地图和中国地图的GeoJSON数据必须手动registerMap。如果没有注册图表就不知道该绘制什么形状。 解决从DataV GeoAtlas接口拉取GeoJSON后先console.log确认数据已经加载到前端然后执行echarts.registerMap(china, geoJson)再setOption。注意拉取接口的域名可能生产环境不可用本地演示时最好把GeoJSON下载到static/geo/china.json用fetch(/geo/china.json)加载。5.3 Java时间序列化后变成一串数字现象后端返回的时间字段在浏览器里显示为1580486400000ECharts折线图的x轴变成了一堆大数字。 原因LocalDate或Date类型被Jackson序列化时默认调用了时间戳格式加上前端没有做格式化直接把数字当类别轴显示。 解决两种方式任选——后端把日期转成yyyy-MM-dd字符串再返回或者前端在xAxis.axisLabel.formatter里用echarts.format.formatTime做转换。我更推荐前者因为聚合逻辑里本来就生成了字符串完全没有必要让日期以对象形态跨网络。5.4 接口慢得离谱刷新一次要等好几秒现象页面加载后接口一会儿才返回F12里看耗时全花在query阶段。 原因如果用了数据库且每次请求都新建连接连接池没启用如果没连数据库那就是每次请求都重新读CSV文件而且没有做缓存。 解决用Service单例持有数据列表启动时加载一次后续请求全走内存如果确实连了MySQL加上HikariCP连接池并配置最大连接数同时把聚合查询改成在Service层用Map做缓存因为疫情数据的聚合结果在一天内不会变化。5.5 fetch请求成功但页面白屏不动现象接口在浏览器地址栏直接访问能返回JSON但页面里通过fetch拿不到数据控制台报CORS错误或404。 原因如果前后端分离部署比如前端用Vite/nginx后端8080浏览器会拦截跨域请求如果是同一个Spring Boot应用但路径写错比如fetch(/covid/api/province/top10)而接口实际是/api/province/top10会返回404但页面仍然白屏。 解决先用curl http://localhost:8080/api/covid/province/top10验证后端接口是否可用再用F12查看请求的完整URL确认是否附带多余的context-path前缀最后检查是否缺少CORS配置类。这三步做完90%的联调问题都能定位。6. 从复现到进阶把系统改造成“不像课程设计”的完整项目图表能跑通只是起点答辩时真正拉开差距的是“你这个系统有没有别人没想到的能力”。我给三个成本不高但收益明显的改造方向。第一个是数据筛选联动。当前端有多个图表共用同一个数据集时加一个下拉框或日期区间选择器通过重新请求接口实现联动刷新。比如选中“2月”柱状图、折线图、地图全部展示2月数据切换成“3月”整体变化。这个功能只需要给接口加一个RequestParam(required false) String month参数前端fetch时拼上?month2020-02即可逻辑简单但答辩效果非常好。第二个是用WebSocket推送“实时”数据变化。疫情数据的实时性本身是个敏感话题但在演示环境里你可以写一个定时任务每5秒从数据文件里随机取一条新增记录推给前端更新ECharts的series.appendData。这个能力让页面看起来像“活系统”比静态图表能获取更多关注。第三个是加一个数据导出的按钮用response.setContentType(application/vnd.ms-excel)把聚合结果输出为CSV用户点了就能下载。这属于加分项但能体现你对“数据可视化不只是看还要能取”的理解。验证方法上我习惯在改造后做三件事第一看接口响应时间是否稳定在100ms以内第二把浏览器缩放窗口确认图表调用resize()方法后不会变形第三断网刷新页面确认静态资源全部本地化不依赖外部CDN。这三件事都过了这个项目就达到了可以被写进简历、拿出来讲的程度。我个人做这类项目有一个习惯永远保留一份“最小可运行版本”在Git里每次加新功能都在分支上改遇到改坏的情况就回退重来。这门Java基于ECharts的疫情分析系统看似是课程设计本质上是让你用最低的成本摸清“前后端分离、数据接口设计、可视化配置”这一整条链路。照着上面的步骤走一遍你收获的不只是一个源码包而是遇到图表项目时不再心虚的底气。希望帮到你。本文还有配套的精品资源点击获取
返回列表