# SpringBootExperiment03-COVID-19 **Repository Path**: bothin/SpringBootExperiment03-COVID-19 ## Basic Information - **Project Name**: SpringBootExperiment03-COVID-19 - **Description**: 【Spring Boot实验】实验三 爬取全球冠状病毒实时统计数据,分析统计显示。 - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 1 - **Created**: 2020-05-07 - **Last Updated**: 2020-12-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README #

东莞理工学院网络空间安全学院

####

课程名称:企业级开发框架专题                  学期:2020春季



实验名称全球新型冠状病毒实时数据统计应用程序的设计与实现实验序号
姓 名李剑波学 号201741404110班 级17软卓1班
实验地点在家实验日期2020/05/07指导老师黎志雄
教师评语实验成绩 评阅教师
百分制
同组同学
### 一、 实验目的 1. 掌握使用Spring框架自带的RestTemplate工具类爬取网络数据; 2. 掌握使用Spring框架自带的计划任务功能; 3. 掌握使用Apache Commons CSV组件解释CSV文件; 4. 掌握Java 8的Stream API处理集合类型数据; 5. 了解使用模板引擎或前端框架展示数据。 ### 二、 实验环境 1. JDK 1.8或更高版本 2. Maven 3.6+ 3. IntelliJ IDEA 4. commons-csv 1.8+ ### 三、 实验任务 #### 1. 后端实现 > 此项目采用前后端分离架构实现, 核心思想是前端HTML页面通过axios调用后端的RESTFUL API接口并使用JSON数据进行交互。 ##### 依赖导入 ![pom文件](https://images.gitee.com/uploads/images/2020/0508/095945_e04cf19f_4846724.png "屏幕截图.png") ##### 分析csv文件的数据结构,定义model类 ```java package org.example.springbootexperiment03.pojo; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import java.util.Collections; import java.util.List; import java.util.Map; /** * Created by bothin * At 2020/5/7 14:09 * Description: 地区 用于全球冠状病毒实时统计的一个实体 */ @Data @AllArgsConstructor @NoArgsConstructor @Builder public class Region { /** * 地区编号 */ private Integer id; /** * 州/省 */ private String provinceName = ""; /** * 国家/地区 */ private String countryName = ""; /** * 病例数据 时间为倒序排序 */ private Map data = Collections.EMPTY_MAP; } ``` ##### 三层结构dao service controller ###### dao层 ```java package org.example.springbootexperiment03.dao; import org.apache.commons.csv.CSVFormat; import org.apache.commons.csv.CSVRecord; import org.example.springbootexperiment03.config.RegionPropertiesConfig; import org.example.springbootexperiment03.pojo.Region; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.Resource; import org.springframework.http.RequestEntity; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Repository; import org.springframework.web.client.RestTemplate; import java.io.IOException; import java.io.InputStreamReader; import java.io.Reader; import java.net.URI; import java.util.*; /** * Created by bothin * At 2020/5/7 14:18 * Description: dao层 */ @Repository public class RegionDao { /** * 保存数据 */ private List dataList = new ArrayList<>(); @Autowired private RestTemplate restTemplate; @Autowired private RegionPropertiesConfig regionProperties; public List getALLRegions(){ return dataList; } /** * 从url上爬取数据 * @throws IOException */ public void pullData() throws IOException { RequestEntity requestEntity = RequestEntity.get(URI.create(regionProperties.getUrl())) .headers(httpHeaders -> httpHeaders.add("User-Agent", "Mozilla/5.0")) .build(); ResponseEntity responseEntity = restTemplate.exchange(requestEntity, Resource.class); Resource resource = responseEntity.getBody(); Reader in = new InputStreamReader(resource.getInputStream(), "UTF-8"); Iterable records = CSVFormat.RFC4180.parse(in); //Iterable records = CSVFormat.RFC4180.withFirstRecordAsHeader().parse(in); boolean isHeader = true; List datelist = new ArrayList<>(); //获取表头(每天的日期) int id = 0; for (CSVRecord record : records) { //获取表头 if (isHeader) { isHeader = false; //从第5列开始是日期/病例 for (int i = record.size() - 1; i >= 4; i--) { String[] split = record.get(i).split("/"); datelist.add("20"+split[2]+"年"+split[0]+"月"+split[1]+"日"); } } else { //获取数据 Region region = new Region(); Map map = new LinkedHashMap<>(); //从第5列开始是日期/病例 for (int i = record.size() - 1, j = 0; i >= 4; i--, j++) { map.put(datelist.get(j), Long.valueOf(record.get(i))); } region.setData(map); region.setId(id++); region.setProvinceName(record.get(0)); region.setCountryName(record.get(1)); dataList.add(region); } } } } ``` ###### service 层 ```java package org.example.springbootexperiment03.service; import org.example.springbootexperiment03.pojo.Region; import org.example.springbootexperiment03.vo.LatestRegionVO; import java.util.List; import java.util.Map; /** * Created by bothin * At 2020/5/7 14:19 * Description: */ public interface IRegionService { /** * 获取全部地区数据 * @return List */ public List getALLRegions(); /** * 通过国家名查询记录 * @param countryName 国家名 * @return List */ public List getByCountryName(String countryName); /** * 通过id查询记录 * @param id 地区编号 * @return Map */ public Map getById(Integer id); /** * 查询今日的最新情况,返回全球的数据 * @return Map */ public Map getLatestRegions(); } ``` ###### controller层 ```java package org.example.springbootexperiment03.controller; import org.example.springbootexperiment03.pojo.Region; import org.example.springbootexperiment03.service.IRegionService; import org.example.springbootexperiment03.vo.LatestRegionVO; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.*; import java.util.List; import java.util.Map; /** * Created by bothin * At 2020/5/7 14:22 * Description: */ @RestController @RequestMapping("/data/region") public class RegionController { @Autowired private IRegionService regionService; @GetMapping("all") public List getALLRegions(){ return this.regionService.getALLRegions(); } @GetMapping("find/country") public List getByCountryName(@RequestParam(value = "name") String countryName, @RequestParam(value = "page", defaultValue = "1") Integer page, @RequestParam(value = "size", defaultValue = "10") Integer size){ //若是countryName为空,则返回全部数据 if (StringUtils.isEmpty(countryName)){ return (List) this.regionService.getLatestRegions().get("globalData"); } return this.regionService.getByCountryName(countryName); } @GetMapping("find/id/{id}") public Map getById(@PathVariable("id") Integer id){ return this.regionService.getById(id); } @GetMapping("/latest") public Map getLatestRegions(@RequestParam(value = "page", defaultValue = "1") Integer page, @RequestParam(value = "size", defaultValue = "10") Integer size){ return this.regionService.getLatestRegions(); } } ``` ##### 单元测试 ![输入图片说明](https://images.gitee.com/uploads/images/2020/0508/164048_fefaa5e5_4846724.png "屏幕截图.png") ![输入图片说明](https://images.gitee.com/uploads/images/2020/0508/164116_cc4c4ccb_4846724.png "屏幕截图.png") ##### 开启定时任务服务 ![输入图片说明](https://images.gitee.com/uploads/images/2020/0508/101056_f1bf0626_4846724.png "屏幕截图.png") ##### 抽取核心的常量配置 application.yml ![application.yml](https://images.gitee.com/uploads/images/2020/0508/100955_0748f62a_4846724.png "屏幕截图.png") ![输入图片说明](https://images.gitee.com/uploads/images/2020/0508/101021_dc46a155_4846724.png "屏幕截图.png") #### 2. 前端实现 > 前端主要采用Vue + Element-ui + V-charts实现,采用前后端分离架构实现, 核心思想是前端HTML页面通过axios调用后端的RESTFUL API接口并使用JSON数据进行交互。 ##### 变量定义 ![输入图片说明](https://images.gitee.com/uploads/images/2020/0508/102220_20d75ea3_4846724.png "屏幕截图.png") ##### 请求后端数据 ![输入图片说明](https://images.gitee.com/uploads/images/2020/0508/101454_d9c5cba3_4846724.png "屏幕截图.png") ##### 获取初始化数据 ![输入图片说明](https://images.gitee.com/uploads/images/2020/0508/101542_5480ab55_4846724.png "屏幕截图.png") #### 3. 界面展示 ###### 运行项目 > 通过运行SpringBoot启动类启动项目,访问 http://localhost:8080/ ###### 应用界面 - 通过拆线图,显示每天的病例人数以及新增病例,反映出疫情的趋势走向 - 通过表格,展示每个地区的相应数据 ![应用界面](https://images.gitee.com/uploads/images/2020/0508/004421_ab4f360a_4846724.png "屏幕截图.png") ###### 详情页面 通过点击详情按钮,可以查看该地区冠状病毒的详细统计数据 ![详情按钮](https://images.gitee.com/uploads/images/2020/0508/005058_6e04bfd0_4846724.png "屏幕截图.png") ![地区详情](https://images.gitee.com/uploads/images/2020/0508/005031_c07db4f2_4846724.png "屏幕截图.png") ###### 地区查询 查询某地区/国家 ![输入图片说明](https://images.gitee.com/uploads/images/2020/0508/005521_32b53386_4846724.png "屏幕截图.png") ### 四、附录 1. 爬取全球冠状病毒实时统计数据。 在Github上,有一个由约翰·霍普金斯大学系统科学与工程中心(JHU CSSE)运营的2020年新型冠状病毒可视化仪表板的数据仓库,大家可以从该仓库中爬取全球新型冠状病毒最新的统计数据。 [Github仓库地址](https://github.com/CSSEGISandData/COVID-19) 该仓库会把全球新型冠状病毒最新的统计数据汇总到一个csv文件上,大家在爬取数据时,获取这个csv文件即可。 [csv文件地址](https://github.com/CSSEGISandData/COVID-19/blob/master/csse_covid_19_data/csse_covid_19_time_series/time_series_covid19_confirmed_global.csv) 由于国内网络访问限制的原因,我们不能正常访问Github。老师已经克隆整个仓库,并上传到Gitee,方便大家进行实验。 [克隆的Gitee仓库地址](https://gitee.com/dgut-sai/COVID-19) [克隆的Gitee仓库的csv文件地址](https://gitee.com/dgut-sai/COVID-19/blob/master/csse_covid_19_data/csse_covid_19_time_series/time_series_covid19_confirmed_global.csv) 2. 使用Spring框架自带的RestTemplate工具类爬取数据。 RestTemplate采用同步方式执行 HTTP 请求的类,底层使用 JDK 原生 HttpURLConnection API ,或者 HttpComponents等其他 HTTP 客户端请求类库。 RestTemplate 工具类提供模板化的方法让开发者能更简单地发送 HTTP 请求。 RestTemplate 就是 Spring框架 封装的处理同步 HTTP 请求的工具类。 [RestTemplate 官方使用指南](https://docs.spring.io/spring/docs/current/spring-framework-reference/integration.html#rest-client-access)