苍穹外卖-day10

YangeIT大约 21 分钟苍穹外卖SpringTaskWebSocket催单

苍穹外卖-day10

课程内容

  • Spring Task
  • 订单状态定时处理
  • WebSocket
  • 来单提醒
  • 客户催单

经验储备

  1. 你曾经有过设置备忘录,每天(起床闹钟),每周(跑步),每月(房贷或者信用卡),每年(生日)定时执行的经历!
  2. 你曾经有过买东西没有及时支付,过了30分钟后自动取消订单的经历!
  3. 你曾经有过在电商平台购买东西后,因为不及时发货,你点击了催单的经历!

功能实现:订单状态定时处理来单提醒客户催单 🎯

订单状态定时处理:

image-20221218204021760

1. Spring Task 🍐 ✏️

介绍和应用场景以及入门案例

1.1 介绍

Spring Task 是Spring框架提供的任务调度工具,可以按照约定的时间自动执行某个代码逻辑。

定位: 定时任务框架

作用: 定时自动执行某段Java代码

Task使用步骤

1). 导入maven坐标 spring-context(已存在) image-20221218193251182

2). 启动类添加注解 @EnableScheduling 开启任务调度

3). 自定义定时任务类

代码开发

编写定时任务类:

进入sky-server模块中

package com.sky.task;
/**
 * 自定义定时任务类
 */
@Component
@Slf4j
public class MyTask {

    /**
     * 定时任务 每隔5秒触发一次
     */
    @Scheduled(cron = "0/5 * * * * ?")
    public void executeTask(){
        log.info("定时任务开始执行:{}",new Date());
    }
}

课堂练习

🎻 练习1:结合对SpringTask的理解,参考上述代码,完成:每15秒钟在控制台输出:

我爱java 1 次
我爱java 2 次
我爱java 3...
我爱java n 次

如果前置代码没有完成,别慌!!可以使用已经准备好的代码,练习当前知识

  1. 创建一个非中文的文件夹,右击git bash here 执行下述命令,将代码clone到本地
 git clone --branch V1.10.0 https://gitee.com/huhu520/sky-take-out79.git
  1. 使用idea导入上述代码,并且检查并调整:Setting中Maven设置open in new windowComplieropen in new window中JDK版本,以及Project Structure中的JDK版本open in new window
  2. 点击Idea中右边聚合项目的clean指令,观察是否执行成功,如果成功,进行下一步
  3. 找到sky-server的resource下的application-dev.yml文件,修改数据库名字、数据库用户名、数据库密码、微信appid 微信secret、redis密码等信息
  4. 完成上面的步鄹后,启动SkyApplication.java类,运行项目(记得运行redis和微信小程序工具,以及Nginx哦)

如果完成上面的步鄹后,项目能正常运行,恭喜你部署checkout代码成功!🎉🎉,现在可以大展拳脚完成SpringTask案例了

2.订单状态定时处理 ✏️❤️

订单状态定时处理

2.1 需求分析

用户下单后可能存在的情况:

  • 下单后未支付,订单一直处于待支付状态
  • 用户收货后管理端未点击完成按钮,订单一直处于派送中状态

image-20221218194939516 image-20221218194951963

支付超时的订单如何处理? 派送中的订单一直不点击完成如何处理?

对于上面两种情况需要通过定时任务来修改订单状态,具体逻辑为:

  • 通过定时任务每分钟 检查一次是否存在支付超时订单 下单后超过15分钟仍未支付则判定为支付超时订单),如果存在则修改订单状态为已取消
  • 通过定时任务每天凌晨1点 检查一次是否存在派送中的订单,如果存在则修改订单状态为已完成

2.2 代码开发

1). 自定义定时任务类OrderTask(待完善):

package com.sky.task;

/**
 * 自定义定时任务,实现订单状态定时处理
 */
@Component
@Slf4j
public class OrderTask {

    @Autowired
    private OrderMapper orderMapper;

    /**
     * 处理支付超时订单
     */
    @Scheduled(cron = "0 * * * * ?")
    public void processTimeoutOrder(){
        log.info("处理支付超时订单:{}", new Date());
    }

    /**
     * 处理“派送中”状态的订单
     */
    @Scheduled(cron = "0 0 1 * * ?")
    public void processDeliveryOrder(){
        log.info("处理派送中订单:{}", new Date());
    }

}

课堂练习

🎻 练习2:如果你完成了练习,说明你体验到了定时运行的效果,那么结合Task技术,完成订单状态定时处理的业务吧!!

如果前置代码没有完成,别慌!!可以使用已经准备好的代码,练习当前知识

  1. 创建一个非中文的文件夹,右击git bash here执行下述命令,将代码clone到本地
 git clone --branch V1.10.0 https://gitee.com/huhu520/sky-take-out79.git
  1. 使用idea导入上述代码,并且检查并调整:Setting中Maven设置open in new windowComplieropen in new window中JDK版本,以及Project Structure中的JDK版本open in new window
  2. 点击Idea中右边聚合项目的clean指令,观察是否执行成功,如果成功,进行下一步
  3. 找到sky-server的resource下的application-dev.yml文件,修改数据库名字、数据库用户名、数据库密码、微信appid 微信secret、redis密码等信息
  4. 完成上面的步鄹后,启动SkyApplication.java类,运行项目(记得运行redis和微信小程序工具,以及Nginx哦)

如果完成上面的步鄹后,项目能正常运行,恭喜你部署checkout代码成功!🎉🎉,现在可以大展拳脚完成SpringTask案例了

关于时间增加和减少的知识点: 👇

public static void main(String[] args) {
    // Java中LocalDateTime类中的plusMinutes()方法来获取LocalDateTime对象的不可变副本,在其中增加了几分钟。
    // 此方法需要一个参数,即要添加的分钟数,它返回带有添加的分钟数的LocalDateTime对象。
      LocalDateTime ldt = LocalDateTime.now();
      System.out.println("当前的LocalDateTime是: " + ldt);
      System.out.println("加上15分钟的LocalDateTime是: " + ldt.plusMinutes(15));
   }

3. WebSocket

WebSocket

WebSocket 是基于 TCP 的一种新的网络协议。它实现了浏览器与服务器全双工通信——浏览器和服务器只需要完成一次握手,两者之间就可以创建持久性的连接, 并进行双向数据传输。

HTTP协议和WebSocket协议对比:

  • HTTP是短连接
  • WebSocket是长连接
  • HTTP通信是单向的,基于请求响应模式
  • WebSocket支持双向通信
  • HTTP和WebSocket底层都是TCP连接

image-20221222184340172 image-20221222184352573

入门案例代码实现

需求

需求: 实现浏览器与服务器全双工通信。浏览器既可以向服务器发送消息,服务器也可主动向浏览器推送消息。

效果展示:

image-20221222190401414

实现步骤:

  1. 直接使用websocket.html页面作为WebSocket客户端
  2. 导入WebSocket的maven坐标
  3. 导入WebSocket服务端组件WebSocketServer,用于和客户端通信
  4. 导入配置类WebSocketConfiguration,注册WebSocket的服务端组件
  5. 导入定时任务类WebSocketTask,定时向客户端推送数据 ::

1). 定义websocket.html页面(资料中已提供)

<!DOCTYPE HTML>
<html>
<head>
    <meta charset="UTF-8">
    <title>WebSocket Demo</title>
</head>
<body>
    <input id="text" type="text" />
    <button onclick="send()">发送消息</button>
    <button onclick="closeWebSocket()">关闭连接</button>
    <div id="message">
    </div>
</body>
<script type="text/javascript">
    var websocket = null;
    var clientId = Math.random().toString(36).substr(2);

    //判断当前浏览器是否支持WebSocket
    if('WebSocket' in window){
        //连接WebSocket节点
        websocket = new WebSocket("ws://localhost:8080/ws/"+clientId);
    }
    else{
        alert('Not support websocket')
    }

    //连接发生错误的回调方法
    websocket.onerror = function(){
        setMessageInnerHTML("error");
    };

    //连接成功建立的回调方法
    websocket.onopen = function(){
        setMessageInnerHTML("连接成功");
    }

    //接收到消息的回调方法
    websocket.onmessage = function(event){
        setMessageInnerHTML(event.data);
    }

    //连接关闭的回调方法
    websocket.onclose = function(){
        setMessageInnerHTML("close");
    }

    //监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
    window.onbeforeunload = function(){
        websocket.close();
    }

    //将消息显示在网页上
    function setMessageInnerHTML(innerHTML){
        document.getElementById('message').innerHTML += innerHTML + '<br/>';
    }

    //发送消息
    function send(){
        var message = document.getElementById('text').value;
        websocket.send(message);
    }
	
	//关闭连接
    function closeWebSocket() {
        websocket.close();
    }
</script>
</html>




















 















































总结

课堂作业

  1. WebSocket是什么?有什么应用场景(O_O)?
  2. 既然WebSocket支持双向通信,功能看似比HTTP强大,那么我们是不是可以基于WebSocket开发所有的业务功能?

4. 来单提醒

来单提醒

用户下单并且支付成功后,需要第一时间通知外卖商家。通知的形式有如下两种:

  1. 语音播报 image-20221222194413901
  2. 弹出提示框
image-20221222194450142
  1. 通过WebSocket实现管理端页面和服务端保持长连接状态
  2. 当客户支付后,调用WebSocket的相关API实现服务端向客户端推送消息
  3. 客户端浏览器解析服务端推送的消息,判断是来单提醒还是客户催单,进行相应的消息提示和语音播报
  4. 约定服务端发送给客户端浏览器的数据格式为JSON,字段包括:type,orderId,content
    • type 为消息类型,1为来单提醒 2为客户催单
    • orderId 为订单id
    • content 为消息内容

4.2 代码开发

在OrderServiceImpl中注入WebSocketServer对象,修改paySuccess方法,加入如下代码:

	@Autowired
    private WebSocketServer webSocketServer;
	/**
     * 支付成功,修改订单状态
     *
     * @param outTradeNo
     */
    public void paySuccess(String outTradeNo) {
        // 当前登录用户id
        Long userId = BaseContext.getCurrentId();

        // 根据订单号查询当前用户的订单
        Orders ordersDB = orderMapper.getByNumberAndUserId(outTradeNo, userId);

        // 根据订单id更新订单的状态、支付方式、支付状态、结账时间
        Orders orders = Orders.builder()
                .id(ordersDB.getId())
                .status(Orders.TO_BE_CONFIRMED)
                .payStatus(Orders.PAID)
                .checkoutTime(LocalDateTime.now())
                .build();

        orderMapper.update(orders);
		//////////////////////////////////////////////
        Map map = new HashMap();
        map.put("type", 1);//消息类型,1表示来单提醒
        map.put("orderId", orders.getId());
        map.put("content", "订单号:" + outTradeNo);

        //通过WebSocket实现来单提醒,向客户端浏览器推送消息
        webSocketServer.sendToAllClient(JSON.toJSONString(map));
        ///////////////////////////////////////////////////
    }
























 
 
 
 
 
 
 


总结

课堂作业

  1. 来单提醒的作用是什么?怎么实现来单提醒,说说思路?🎤
  2. 绘制来单提醒的流程图?理解小程序、tomcat服务器、后台管理端的交互关系

5. 客户催单

客户催单

用户在小程序中点击催单按钮后,需要第一时间通知外卖商家。通知的形式有如下两种:

  • 语音播报 image-20221222203301218
  • 弹出提示框
image-20221222203345829

设计思路:

  1. 通过WebSocket实现管理端页面和服务端保持长连接状态
  2. 当用户点击催单按钮后,调用WebSocket的相关API实现服务端向客户端推送消息
  3. 客户端浏览器解析服务端推送的消息,判断是来单提醒还是客户催单,进行相应的消息提示和语音播报 约定服务端发送给客户端浏览器的数据格式为JSON,字段包括:type,orderId,content
    • type 为消息类型,1为来单提醒 2为客户催单
    • orderId 为订单id
    • content 为消息内容

当用户点击催单按钮时,向服务端发送请求。

5.2 代码开发

思路

  1. 定义Controller接收小程序催单请求
  2. 在业务层中查询订单是否存在,如果存在,通过websocket向管理端发送消息
  3. 测试(小程序发消息,观察管理端是否有提示)

Controller层

根据用户催单的接口定义,在user/OrderController中创建催单方法:

	/**
     * 用户催单
     *
     * @param id
     * @return
     */
    @GetMapping("/reminder/{id}")
    @ApiOperation("用户催单")
    public Result reminder(@PathVariable("id") Long id) {
        orderService.reminder(id);
        return Result.success();
    }

课后作业

🚩 1. 重点完成上述的课堂作业

  1. 晚自习第一节课的前30分钟,总结完毕之后,每个同学先必须梳理今日知识点 (记得写不知道的,以及感恩三件事);整理好的笔记可以发给组长,组长交给班长,意在培养大家总结的能力)

  2. 晚自习第一节课的后30分钟开始练习(记住:程序员是代码堆起来的):

  3. 剩余的时间:预习第二天的知识,预习的时候一定要注意:

  • 预习不是学习,不要死看第二天的视频(很容易出现看了白看,为了看视频而看视频)
  • 预习看第二天的笔记,把笔记中标注重要的知识,可以找到预习视频,先看一遍,如果不懂的 ,记住做好标注。