# spring boot 实验3
**Repository Path**: superlgc/spring_boot_experiment_3
## Basic Information
- **Project Name**: spring boot 实验3
- **Description**: No description available
- **Primary Language**: Java
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2020-05-20
- **Last Updated**: 2020-12-19
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
东莞理工学院网络空间安全学院
实验报告模板
| 课程名称:企业级开发框架专题 |
学期:2019年秋季 |
| 实验名称 |
全球新型冠状病毒实时数据统计应用程序的设计与实现
|
实验序号 |
三 |
| 姓 名 |
李贵超 |
学 号 |
201741412214 |
班 级 |
17软卓2班 |
| 实验地点 |
无 |
实验日期 |
2020/5/9 |
指导老师 |
黎志雄 |
| 教师评语 |
|
实验成绩 |
评阅教师 |
| 百分制 |
|
| 同组同学 |
|
### 一、实验目的
**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**
### 三、 实验任务
**1. 通过IntelliJ IDEA的Spring Initializr向导创建Spring Boot项目。**

**2. 添加功能模块:spring MVC、lombok、commons-csv等。**
```xml
org.springframework.boot
spring-boot-starter-web
org.projectlombok
lombok
true
org.apache.commons
commons-csv
1.8
```
**3. 爬取全球冠状病毒实时统计数据。**
```
地址:https://gitee.com/dgut-sai/COVID-19/blob/master/csse_covid_19_data/csse_covid_19_time_series/time_series_covid19_confirmed_global.csv
```
**4. 使用Spring框架自带的RestTemplate工具类爬取数据。**
```java
String uri = "https://gitee.com/dgut-sai/COVID-19/blob/master/csse_covid_19_data/csse_covid_19_time_series/time_series_covid19_confirmed_global.csv";
RequestEntity requestEntity = RequestEntity.get(URI.create(uri))
.header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36")
.header("Cookie", "")
.build();
ResponseEntity response = new RestTemplate().exchange(requestEntity, String.class);
return response.getBody();
```
**5. 分析csv文件的数据结构,定义model类。**
```java
@Data
@NoArgsConstructor
@AllArgsConstructor
public class EpidemicSituation {
private String provinceOrState;
private String countryOrRegion;
private int totalPat;
private int currAdd;
private HashMap dayDetail;
}
```
**6. 使用Apache Commons CSV组件解释CSV文件。**
```java
Iterable records = CSVFormat
.EXCEL
.withHeader("Province/State", "Country/Region", "Lat", "Long")
.parse(new InputStreamReader(new ByteArrayInputStream(data.getBytes()), StandardCharsets.UTF_8));
for (CSVRecord record : records) {
if ("Province/State".equals(record.get("Province/State"))) continue;
EpidemicSituation es = new EpidemicSituation();
es.setProvinceOrState(record.get("Province/State"));
es.setCountryOrRegion(record.get("Country/Region"));
es.setTotalPat(Integer.parseInt(record.get(record.size() - 1)));
es.setCurrAdd(Integer.parseInt(record.get(record.size() - 1)) - Integer.parseInt(record.get(record.size() - 2)));
DateRecord dateRecord = new DateRecord(2020, 1, 22);
HashMap map = new HashMap<>();
for (int i = 4; i < record.size(); i++) {
map.put(dateRecord.toString(), Integer.parseInt(record.get(i)));
dateRecord.add(1);
}
es.setDayDetail(map);
}
```
自定义DateRecord类处理时间。
```java
public class DateRecord {
private int year;
private int month;
private int day;
private final List leapDys = Arrays.asList(31,29,31,30,31,30,31,31,30,31,30,31);
private final List ordinaryDys = Arrays.asList(31,29,31,30,31,30,31,31,30,31,30,31);
public DateRecord(int year,int month,int day) {
this.year = year;
this.month = month;
this.day = day;
}
public Boolean isLeapYear() {
return this.year%4==0||this.year%100==0&&this.year%400!=0;
}
public void addOneDay() {
List days = new ArrayList<>(isLeapYear()?leapDys:ordinaryDys);
this.day += 1;
if (this.day > days.get(this.month - 1)) {
this.month += 1;
this.day = 1;
}
if (this.month > 12) {
this.year += 1;
this.month = 1;
}
}
public void add(int days) {
while (days>0){
this.addOneDay();
days -= 1;
}
}
public String toString() {
return this.year + "-" + (this.month<10?"0"+this.month:this.month) + "-" + (this.day<10?"0"+this.day:this.day);
}
```
**7. 使用Spring框架自带的计划任务功能定时更新统计数据。**
```java
@EnableScheduling
public class AnalysisService {
@Scheduled(cron = "0 0 1 * * *")
public void upData() throws IOException {}
}
```
设置任务线程池为20
```properties
spring.task.scheduling.pool.size=20
```
**8. 要确保应用程序启动时,获取一次统计数据。**
```java
@PostConstruct
@Scheduled(cron = "0 0 1 * * *")
public void upData() throws IOException {}
```
**9. 单元测试。**
contraller
```java
@SpringBootTest
@AutoConfigureMockMvc
class DisplayControllerTest {
@Autowired
private MockMvc mvc;
@Test
void returnData() throws Exception {
mvc.perform(MockMvcRequestBuilders.get("/data/0/0")
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andDo(MockMvcResultHandlers.print());
}
@Test
void accumulate() throws Exception {
mvc.perform(MockMvcRequestBuilders.get("/accumulate")
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.columns").isArray())
.andDo(MockMvcResultHandlers.print());
}
@Test
void dayAdd() throws Exception {
mvc.perform(MockMvcRequestBuilders.get("/dayAdd")
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.rows").isArray())
.andDo(MockMvcResultHandlers.print());
}
@Test
void countryTotal() throws Exception {
mvc.perform(MockMvcRequestBuilders.get("/countryTotal")
.accept(MediaType.ALL))
.andExpect(status().isOk())
.andDo(MockMvcResultHandlers.print());
}
@Test
void total() throws Exception {
mvc.perform(MockMvcRequestBuilders.get("/total")
.accept(MediaType.ALL))
.andExpect(status().isOk())
.andExpect(jsonPath("$.total").isNumber())
.andExpect(jsonPath("$.added").isNumber())
.andExpect(jsonPath("$.lastTime").isString())
.andDo(MockMvcResultHandlers.print());
}
}
```
service
```java
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE,classes = {AnalysisService.class})
class AnalysisServiceTest {
@Autowired
AnalysisService analysisService;
@Test
public void testGetData() {
Assert.assertNotNull(analysisService.getData());
Assert.assertEquals(96541,analysisService.getData().length());
}
@Test
public void testTotalData() {
String data = analysisService.totalData();
Assert.assertNotNull(data);
}
}
```
**10. 定义Cotroller控制器。**
```java
@Controller
public class DisplayController {
@Autowired
private AnalysisService analysis;
@RequestMapping("/")
public String index() {
return "index";
}
@ResponseBody
@RequestMapping("/data/{PORS}/{CORR}")
public List ReturnData(@PathVariable("PORS") String PORS,@PathVariable("CORR") String CORR) {
System.out.println((PORS + " " + CORR));
if (PORS==null||CORR==null) return new ArrayList<>();
return analysis
.getList()
.stream()
.filter(e -> PORS.equals("0") || e.getProvinceOrState().contains(PORS))
.filter(e -> CORR.equals("0") || e.getCountryOrRegion().contains(CORR))
.collect(Collectors.toList());
}
@ResponseBody
@RequestMapping("/accumulate")
public String accumulate(){
return analysis.accumulateJson();
}
@ResponseBody
@RequestMapping("/dayAdd")
public String dayAdd(){
return analysis.dayAddData();
}
@ResponseBody
@RequestMapping("/countryTotal")
public String countryTotal(){
return analysis.countryOrRegionTotalNumber();
}
@ResponseBody
@RequestMapping("/total")
public String total(){
return analysis.totalData();
}
}
```
使用Stream API对数据进行处理
```java
public Stream> accumulateData() {
return list.stream()
.map(EpidemicSituation::getDayDetail)
.reduce((m1, m2) -> {
HashMap temp = new HashMap<>();
for (Map.Entry entry : m1.entrySet()) {
if (m2.containsKey(entry.getKey())) {
temp.put(entry.getKey(), entry.getValue() + m2.get(entry.getKey()));
}
}
return temp;
})
.map(HashMap::entrySet)
.stream()
.flatMap(Collection::stream)
.sorted(Map.Entry.comparingByKey());
}
```
**9. 定义前端数据展示页面。**
使用技术:Vue,axios,element
功能和效果如下图所示:

### 四、实验总结
通过这次实验,在实操的过程中我明白了一些课堂上还没有完全理解的问题,收获满满,再次感谢老师!