# 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数据进行交互。
##### 依赖导入

##### 分析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();
}
}
```
##### 单元测试


##### 开启定时任务服务

##### 抽取核心的常量配置 application.yml


#### 2. 前端实现
> 前端主要采用Vue + Element-ui + V-charts实现,采用前后端分离架构实现, 核心思想是前端HTML页面通过axios调用后端的RESTFUL API接口并使用JSON数据进行交互。
##### 变量定义

##### 请求后端数据

##### 获取初始化数据

#### 3. 界面展示
###### 运行项目
> 通过运行SpringBoot启动类启动项目,访问 http://localhost:8080/
###### 应用界面
- 通过拆线图,显示每天的病例人数以及新增病例,反映出疫情的趋势走向
- 通过表格,展示每个地区的相应数据

###### 详情页面
通过点击详情按钮,可以查看该地区冠状病毒的详细统计数据


###### 地区查询
查询某地区/国家

### 四、附录
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)