基于springboot+redis+国际化+定时任务的疫情项目已上线

Posted 步尔斯特

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于springboot+redis+国际化+定时任务的疫情项目已上线相关的知识,希望对你有一定的参考价值。

这是我自己做的一套疫情实时数据项目,有地图、折线图、表格

该项目涵盖了大部分springboot项目实际开发所必需的技术

目前已经上线,🔍公号【步尔斯特】获取源码,可直接拿走学习或复用,不谢。

文章目录

一、效果图

二、技术栈

  1. 基础框架(springboot + mybatis + mybatis-plus)
  2. 缓存数据(redis)
  3. 国际化(一键切换不同语种)
  4. 定时任务(定时更新数据)
  5. 爬虫
  6. 加密
  7. 感知数据变更,进行通知推送
  8. 日志监控
  9. 数据计算和二次存储(为图表服务 - 比如趋势图)
  10. 数据展示和渲染 Thymeleaf Echarts
  11. 模拟http请求

三、项目背景

模拟疫情数据展示网站,做出一个完整的数据采集、数据存储、数据计算、数据展示的疫情数据系统。

四、搜索引擎原理

链接人和内容

网页爬取 -》 网页去重 -》 网页分析 -》 内容保存(倒排索引)

关键字查询 -》 关键字分析 -》 去匹配内容 -》 筛选出比如100条数据 -》 数据排序 (可口可乐的秘方)

分类:
通用型爬虫 和 垂直型爬虫

疫情数据系统:
数据采集 -》 数据存储 -》 数据计算 -》 数据展示

五、数据分析

5.1 分析数据源

确认能够通过代码 获取到数据 (定位到具体的http请求)

5.2 获取疫情数据

  1. 国内各省份表格数据-对应请求

  2. 对应的数据格式:json

  3. 国外表格数据-对应请求

  4. 国内趋势数据

六、数据处理

6.1 初识json

json = javascript object notation (js对象表示法)
独立于语言,具有自我描述性

Gson -> From Google

new Gson().toJson(Object obj) 将对象转化为json字符串
new Gson().fromJson(String jsonStr, T.class) 将json字符串转化为对象

6.2 数据展示

controller - service - handler

数据加载到model中 返回给html渲染

@Controller
public class DataController 

    @Autowired
    private DataService dataService;

    @GetMapping("/")
    public String list(Model model) 
        List<DataBean> beanList = dataService.list();
        model.addAttribute("beanList", beanList);
        return "list";
    

public interface DataService 
    
    List<DataBean> list();



@Service
public class DataServiceImpl implements DataService 

    @Override
    public List<DataBean> list() 
        return MyDataHandler.getData();
    


<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>


<h2> 国内疫情情况如下 </h2>
<br>

<table>
    <thead>
    <tr>
        <th>地区</th>
        <th>现有</th>
        <th>累计</th>
        <th>治愈</th>
        <th>死亡</th>
    </tr>
    </thead>

    <tbody>
    <tr th:each="bean:$beanList">
        <td th:text="$bean.area">name</td>
        <td th:text="$bean.nowConfirm">nowConfirm</td>
        <td th:text="$bean.confirm">confirm</td>
        <td th:text="$bean.heal">heal</td>
        <td th:text="$bean.dead">dead</td>
    </tr>
    </tbody>
</table>

</body>
</html>

七、网络请求

HTTP

  1. “应用层协议”
  2. 了解HTTP不同版本的演进
  3. 了解GET和POST请求的区别
    // 需要的参数url
    // 创建一个远程的连接对象  设置方法类型 GET
    // 设置相关参数    发送请求
    // 通过io接收数据后返回

    public static String doGet(String urlStr) 
        HttpURLConnection conn = null;
        InputStream is = null;
        BufferedReader br = null;
        StringBuilder result = new StringBuilder();

        try 
            // 创建远程url连接对象
            URL url = new URL(urlStr);
            // 打开一个连接
            conn = (HttpURLConnection) url.openConnection();
            // 设置为GET请求
            conn.setRequestMethod("GET");
            // 设置重要的参数  连接超时时间 和 读取超时时间
            // 超时时间  更多被距离影响   读取时间  更多被数据量影响
            conn.setConnectTimeout(15000);
            conn.setReadTimeout(60000);

            // header参数设置  可以不设置
            conn.setRequestProperty("Accept", "application/json");

            // 发送请求
            conn.connect();

            // 状态码  200  302  404  500 ?
            // 如果比较时  可能出现空指针  把确定的值放在前面  可以避免
            if (200 == conn.getResponseCode()) 
                is = conn.getInputStream();
                br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
                String line;
                while ((line = br.readLine()) != null) 
                    result.append(line);
                
             else 
                System.out.println("error responseCode :"
                        + conn.getResponseCode());
            


         catch (Exception e) 
            e.printStackTrace();
         finally 
            try 
                if (br != null) 
                    br.close();
                

                if (is != null) 
                    is.close();
                

             catch (Exception e) 
                e.printStackTrace();
            

            conn.disconnect();
        

        return result.toString();
    

八、数据存储

8.1 Mybatis整合

SSM的整合 -> 入门课的整合 -> mybatis-plus的整合

明确数据结构 -> 创建mapper文件夹 -> 调用getData方法 将数据据存储到数据库中 -> 查询时从数据库中读取

a) 引入依赖

        <!-- mybatis整合
             mybatis-spring-boot-starter
             mybatis-plus -boot-starter
             mysql驱动-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.1</version>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.2.0</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

b) 配置数据库连接参数

spring.datasource.url=jdbc:mysql://localhost:3306/illness?characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=123456

c) 创建数据库

CREATE DATABASE /*!32312 IF NOT EXISTS*/`illness` /*!40100 DEFAULT CHARACTER SET utf8 */;

USE `illness`;

/*Table structure for table `epidemic` */

DROP TABLE IF EXISTS `epidemic`;

CREATE TABLE `epidemic` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `area` varchar(100) DEFAULT NULL,
  `confirm` int(11) DEFAULT NULL,
  `now_confirm` int(11) DEFAULT NULL,
  `dead` int(11) DEFAULT NULL,
  `heal` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=308 DEFAULT CHARSET=utf8;

d) 编写类及文件夹

将实体类对应到数据库的表中 属性和字段也需对应

需注意 属性的命名为驼峰命名法

如 nowConfirm 字段的命名是下划线分隔 如 now_confirm

可以使用@TableName和@TableField等注解指定对应关系

@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("epidemic")
public class DataBean implements Serializable 

    // Alt + Insert 调用   安装插件
    private static final long serialVersionUID
            = 4938260405189292371L;

    // Alt + 7 查看类的方法

    // 地区  累计确诊人数  现有确诊人数   死亡人数  治愈人数
    private long id;
    private String area;
//    @TableField("all_confirm")
    // 如果属性名和表中字段名  不一致  可以通过 TableField注解指定
    private int confirm;
    private int nowConfirm;
    private int dead;
    private int heal;

在主程序类上 增加@MapperScan注解 指定mapper类所在文件夹

在此文件夹下 创建mapper类 实战中 如表名叫user mapper会命名为UserMapper

@SpringBootApplication
@MapperScan("com.duing.mapper")
public class DataHandlerApplication 

    public static void main(String[] args) 
        SpringApplication.run(DataHandlerApplication.class, args);
    




public interface DataMapper extends BaseMapper<DataBean> 


创建service接口及其实现类

整合mybatis-plus,用其提供的IService父接口 和 ServiceImpl实现父类

public interface DataService extends IService<DataBean> 




@Service
public class DataServiceImpl
        extends ServiceImpl<DataMapper,DataBean>
        implements DataService 


在controller中调用mybatis-plus实现的CRUD方法

@Controller
public class DataController 

    @Autowired
    private DataService dataService;

    @GetMapping("/")
    public String list(Model model) 
        List<DataBean> beanList = dataService.list();
        model.addAttribute("beanList", beanList);
        return "list";
    


8.2 数据初始化及定时更新

在数据查询前,先将数据存储到数据库中,我们称之为数据初始化

可以在项目启动时,先采集一次数据存储到数据库中,然后再进行定期更新

而项目启动时执行且只执行一次的逻辑,可以使用注解 @PostConstruct

//  先将DataHandler托管到spring容器中  使用@Component
//  以便于获取到 dataService对象
@Component
public class DataHandler 

    @Autowired
    private DataService dataService;

    // 数据初始化
    //   在服务器加载Servlet时运行  且 只运行一次
    @PostConstruct
    public void saveData() 
        List<DataBean> dataBeans = getData();
        // mybatis-plus提供了可用的方法
        // 删除全部数据  批量新增数据
        dataService.remove(null);
        dataService.saveBatch(dataBeans);
    
...

定时更新其实也是定时任务

可以通过注解@Scheduled + cron表达式来实现

Scheduled 英文原意是调度的意思 意思是我们将某段逻辑 按照指定的时间间隔 进行调度 即为定时处理

使用方式如下:

首先在入口类上 打开定时任务开关 使用注解 @EnableScheduling

@SpringBootApplication
@MapperScan("com.duing.mapper")
@EnableScheduling
public class DataHandlerApplication 

    public static void main(String[] args) 
        SpringApplication.run(DataHandlerApplication.class, args);
    


然后在方法上 使用 @Scheduled 搭配cron表达式 决定方法多久执行一次

    // 定时更新
    @Scheduled(cron = "0 0 0/1 * * ? ")
    public void updateData()
        System.out.println("要更新数据啦");
        System.out.println("当前时间:" + dateFormat.format(new Date()));

        List<DataBean> dataBeans = getData();
        dataService.remove(null);
        dataService.saveBatch(dataBeans);
    

其中cron表达式,是由6个表达不同时间单位的字段拼接而成

可以通过在线cron表达式生成器来生成

cron表达式几乎可以满足所有定时执行的需求

可以生成各类需求对应的表达式熟悉一下

除此之外,@Scheduled还支持固定时间间隔的参数设置

分别为 fixedRate 和 fixedDelay

// 固定频率任务   以ms为单位  10000代表每10s执行一次
//   使用时要注意间隔时间  和  任务消耗时间的  大小关系      
//   如设置间隔10s  而方法需执行20s   那么方法会等待上一次执行完成才会执行  
//   唤起方法真正的时间间隔为20s
@Scheduled(fixedRate = 10000)
public void updateData1()

// 固定间隔任务   上一次执行结束后   再隔10s进行下一次执行
// 如设置间隔10s  而方法需执行20s   那么会从上一次执行完成后开始计算  10s后开始下一次执行
// 唤起方法真正的时间间隔为30s
@Scheduled(fixedDelay = 10000)
public void updateData2()

九、图形化数据处理

9.1 折线图

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script type="text/javascript" src="js/echarts.min.js"></script>
</head>
<body>

<!-- 为ECharts准备一个具备大小(宽高)的Dom -->
<div id="main" style="width: 600px;height:400px;"></div>
<script th:inline="javascript">

    var dateStr = [[$dateList]];
    var confirmStr = [[$confirmList]];
    var suspectStr = [[$suspectList]];

    // 基于准备好的dom  初始化实例
    var mychart = echarts.init(document.getElementById("main"));
    var option = 
        // 标题
        title:
            text: '全国疫情新增趋势'
        ,
        legend:
            data:['新增确诊','新增疑似']
        ,
        // x轴的数据
        xAxis:
            data: dateStr
        ,
        // y轴的数据类型
        yAxis:
            type:'value'
        ,
        series:[
            
                name: '新增确诊',
                type: 'line',
                data: confirmStr
            ,
            
                name: '新增疑似',
                type: 'line',
                data: suspectStr
            
        ]
    ;
    // 将参数设置进去
    mychart.setOption(option);

</script>

</body>
</html>

9.2 地图

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script type="text/javascript" src="js/echarts.min.js"></script>
    <script type="text/javascript" src="js/china.js"></script>
</head>
<body>


<!-- 为ECharts准备一个具备大小(宽高)的Dom -->
<div id="main" style="width: 1000px;height:700px;"></div>
<script th:inline="javascript">

    var dataStr1 = [[$mapData1]];
    var dataStr2 = [[$mapData2]];
    // 基于准备好的dom  初始化实例
    var mychart = echarts.init(document.getElementById("main"));
    var option = 
        title: 
            text: '疫情地图',
            subtext: '仅供参考',
            x: 'center'
        ,
        tooltip: 
            trigger: 'item'
        ,
        legend: 
            orient: 'vertical',
            left: 'left',
            data: ['现有确诊', '累计确诊']
        ,
        visualMap: 
            type: 'piecewise',
            pieces以上是关于基于springboot+redis+国际化+定时任务的疫情项目已上线的主要内容,如果未能解决你的问题,请参考以下文章

SpringBoot使用SchedulingConfigurer实现多个定时任务多机器部署问题

SpringBoot + Redis 实现点赞功能的缓存和定时持久化(附源码)

Redis实战(12)-基于Redis的Key失效和定时任务调度实现订单超时未支付自动失效(延时队列)

10. Springboot整合Redis事件监听实现定时功能

springboot定时任务重启生效

SpringBoot中使用redis实现相对可靠的分布式定时任务,适用于订单场景