《苍穹外卖》知识梳理P9-定时任务、来单提醒与用户催单

一.定时任务

实现定时任务可以使用spring家族中的sprinig-task;

1.1 spring-task

spring-task是Spring框架的任务调度工具,可以按照约定的时间自动执行某个代码逻辑;

应用场景

  • 信用卡每月归还贷款提醒,定时任务检查,每天查看有没有提醒的人,有的话自动提醒;
  • 车票处理未支付订单(xx分钟之内付款,超时自动取消);
  • 入职纪念日为用户发送通知;

cron表达式的使用

  • 本质上是一个字符串,可以用来定义任务触发的时间;
  • 构成规则:分为6/7个域,由空格隔开,每个域代表一个含义,每个域可以分为:秒,分钟,小时,日,月,周,年(可选),
  • 日和周通常只写一个,另一个写?例如:2023年10月12日上午9点整对应的cron表达式为:0 0 9 12 10 ? 2023
  • cron表达式在线生成器:https://cron.qqe2.com/

入门案例

  • 导入坐标:实际上是spring-context依赖,但是由于spring-boot-starter已经包含了spring-context依赖,因此直接导入springboot的依赖即可,会将context传递过来;
		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
  • 在启动类上加入@EnableScheduling注解开启任务调度
  • 自定义定时任务类(写业务逻辑)
	@Component
	@Slf4j
	/**
	 * 自定义定时任务类
	 */
	public class MyTask {
		//从第0秒开始,每隔5秒触发一次;
	    @Scheduled(cron = "0/5 * * * * ?")
	    public void executeTask(){
	        log.info("定时任务开始执行......"+ LocalDateTime.now());
	    }
	}

用户下单之后可能存在

  • 下单之后未支付,订单一直处于“待支付”状态;超时后订单需要自动取消;
  • 用户收获之后未点击“完成”按钮,订单一直处于“派送中”状态;超时后定时任务应该自动完成;

1.2 超时订单的处理

  • 通过定时任务设置每分钟检查一次是否存在支付超时订单(下单完成后超过15分钟仍未支付则判定为超时订单),如果存在则修改订单为“已取消”;
	/**
     * 处理超时订单,每分钟触发一次
     */
//    @Scheduled(cron = "0 * * * * ?")
    @Scheduled(cron = "0 0/10 * * * ?")  //每10分钟触发一次
    public void processTimeoutOrder() {
        log.info("定时处理超市订单...{}", LocalDateTime.now());
        //查询订单表,时候有处于待付款的订单;
        LocalDateTime time = LocalDateTime.now().plusMinutes(-15);
        List<Orders> ordersList = orderMapper.getByStatusAndOrderTime(Orders.PENDING_PAYMENT, time);
        if (ordersList != null && ordersList.size() != 0) {
            for (Orders order : ordersList) {
                order.setStatus(Orders.CANCELLED);
                order.setCancelReason("订单超时,自动取消");
                order.setCancelTime(LocalDateTime.now());
                orderMapper.update(order);
            }
        }
    }

1.3 用户已收到货物但未点击“完成”的仍处于派送中的订单处理

  • 对于派送中的订单,一天只需要检查一次即可(可以设置为每天凌晨4点检查一次),是否存在“派送中”的订单,如果存在,则修改为“已完成”;否则如果一分钟检查一次,可能出现用户还未收到货,但是已经完成了。
	/**
     * 处理派送中订单,每天触发一次
     */
    @Scheduled(cron = " 0 0 4 * * ? ") //每月每天凌晨四点
    public void processDeliveryOrder() {
        log.info("定时处理派送中订单...{}", LocalDateTime.now());
        //每天凌晨四点清算上一天的派送中订单(小于上一天24:00的订单都完成)
        LocalDateTime time = LocalDateTime.now().plusMinutes(-240);
        List<Orders> ordersList = orderMapper.getByStatusAndOrderTime(Orders.DELIVERY_IN_PROGRESS, time);
        if (ordersList != null && ordersList.size() != 0) {
            for (Orders order : ordersList) {
                order.setStatus(Orders.COMPLETED);
                orderMapper.update(order);
            }
        }
    }

二.WebSocket

  • 基于TCP新的网络协议;实现了浏览器与服务器的全双工通信;浏览器可以同浏览器进行双向数据传输,浏览器与服务器只需要完车一次握手,二者之间就会创建持久性连接,并进行双向数据传输;类似打电话。
  • HTTP协议是一定是客户端先请求服务器,HTTP是短连接单向通信,请求-响应模式,
  • WebSocket与HTTP底层都是基于TCP协议;

应用场景

适用于页面并没有刷新,但是数据在实时更新的场景,即用户;

  • 视频弹幕;
  • 网页聊天;
  • 体育实况更新;
  • 股票基金实时报价;

websocket使用流程

  • 导入maven坐标
		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>
  • 导入websocket配置类
	/**
	 * WebSocket配置类,用于注册WebSocket的Bean
	 */
	@Configuration
	public class WebSocketConfiguration {
	    @Bean
	    public ServerEndpointExporter serverEndpointExporter() {
	        return new ServerEndpointExporter();
	    }
	}
  • 导入websocket服务端代码
	/**
	* WebSocket服务
	*/
	@Component
	@ServerEndpoint("/ws/{sid}") //websocket客户端请求路径;
	public class WebSocketServer {
	
	   //存放会话对象
	   private static Map<String, Session> sessionMap = new HashMap();
	
	   /**
	    * 连接建立成功调用的方法
	    */
	   @OnOpen
	   public void onOpen(Session session, @PathParam("sid") String sid) {
	       System.out.println("客户端:" + sid + "建立连接");
	       sessionMap.put(sid, session);
	   }
	
	   /**
	    * 收到客户端消息后调用的方法
	    *
	    * @param message 客户端发送过来的消息
	    */
	   @OnMessage
	   public void onMessage(String message, @PathParam("sid") String sid) {
	       System.out.println("收到来自客户端:" + sid + "的信息:" + message);
	   }
	
	   /**
	    * 连接关闭调用的方法
	    *
	    * @param sid
	    */
	   @OnClose
	   public void onClose(@PathParam("sid") String sid) {
	       System.out.println("连接断开:" + sid);
	       sessionMap.remove(sid);
	   }
	
	   /**
	    * 群发消息
	    *
	    * @param message
	    */
	   public void sendToAllClient(String message) {
	       Collection<Session> sessions = sessionMap.values();
	       for (Session session : sessions) {
	           try {
	               //服务器向客户端发送消息
	               session.getBasicRemote().sendText(message);
	           } catch (Exception e) {
	               e.printStackTrace();
	           }
	       }
	   }
	
	}

可能出现的问题

  • 1.导入代码之后,如果出现了websocket客户端连接失败的问题,可能原因是代码导入之后未重新编译,手动点击“重新构建代码”即可
    在这里插入图片描述
  • 2.由于前边在配置nginx反向代理时,可能会由于IIS占用80端口,导致Nginx无法监听在80端口,因此有2种解决方案:1.关闭IIS服务,2.将监听端口修改为其他端口比如8888端口;由于第一种方案可能带来一些影响并且比较麻烦,因此通常使用第2种方案,但是如果使用第2种方案,现在就会引入这一新的问题:由于NGINX反向代理的存在,请求会被先转发到nginx服务器,再由nginx服务器转发到我的服务器,然而最开始由客户端发送的请求ws://localhost/ws/k3ql4hupss默认是80端口,而我的nginx服务器运行在8888端口,所以就连nginx服务器都无法收到这个请求,更别说我的服务器了,因此会造成服务器无法接收到建立websocket连接的请求;
    在这里插入图片描述
  • 此时如果最开始配置nginx时你使用了第一种方案,那么恭喜你,你不用修改任何东西,即可继续正常运行;如果你使用了第二种方案(我把nginx监听端口设置为了8888端口),此时要么改回80端口,但是需要关闭IIS服务,要么修改前端代码,我直接修改前端代码:找到nginx目录下的html目录,里边部署了sky-take-out的前端工程;
    在这里插入图片描述
  • 进入sky目录下:
    在这里插入图片描述
  • 进入js目录:
    在这里插入图片描述
  • 使用vscode或其他编辑器打开app.d0aa4eb3.js文件,ctrl+F查找"ws://localhost",如图所示,原本为"ws://localhost/ws/“改为"ws://localhost:你的nginx监听端口/ws/”
    在这里插入图片描述
  • 修改之后重启nginx;并记得清除浏览器缓存(我因为没请缓存所以改过之后还是旧的请求想了半天不知道哪出问题…)修改成功之后的效果:
    在这里插入图片描述

三.来单提醒与用户催单

3.1 来单提醒

  • 商家管理端页面与服务端建立长连接;
  • 当支付成功之后,调用websocket的相关API实现服务端向客户端推送消息;
  • 客户端浏览器解析服务端推送的消息,判断是来单提醒还是客户催单,进行相应的消息提示和语音播报;
  • 约定服务端发送给客户端浏览器数据的数据格式为JSON,字段包括:type,orderId,content;
    • type 消息类型1来单提醒,2客户催单;
    • orderId 订单ID;
    • content消息内容;

内网穿透工具cpolar

  • 访问网址cpoloar.com
  • 注册账号并登陆
  • 下载windows版
  • 进行傻瓜式安装
  • 回到cpolar页面,点击“验证”
    在这里插入图片描述

 会得到你的令牌,复制之
 进入到带有cpolar.exe的目录,打开命令行
在这里插入图片描述
 windows下使用命令:

cpolar.exe authtoken 你的authtoken 

 会生成一个内网穿透的配置文件,文件的地址会给出:
在这里插入图片描述
 然后可以临时启动一个服务指定一个IP地址:

cpolar.exe http 8080 

在这里插入图片描述
  此时生成了一个域名并被映射到localhost:8080,使用临时域名访问swagger接口文档(注意复制时如果cmd窗口无法复制,可以右键点击“标记”,在此之后便可以复制)
在这里插入图片描述

修改配置文件

  由于前边使用的模拟微信支付,直接调用了支付成功的接口,所以并没有微信支付的相关配置;

wechat:
    appid: 你的appid
    secret: 你的appsecret
    notify-url: https://21e88914.r24.cpolar.top/notify/paySuccess
    refund-notify-url: https://21e88914.r24.cpolar.top/notify/refundSuccess

  修改支付成功业务层代码OrderServiceImpl.paySuccess,这里代码中所指出的客户端是商家管理端(由于商家可能打开了多个管理端页面,所以向所有与websocket-server建立了连接的管理端都推送了订单通知)

	/**
     * 支付成功,修改订单状态
     *
     * @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);

        //通过websocket向客户端浏览器推送消息(新增消息推送代码)
        Map map=new HashMap<>();
        map.put("type",1);
        map.put("orderId",ordersDB.getId());
        map.put("content","订单号"+outTradeNo);

        String json= JSON.toJSONString(map);

        //推送到所有用户;
        webSocketServer.sendToAllClient(json);

    }

3.2 客户催单

  • 商家管理端页面与服务端建立长连接;
  • 当用户催单之后,调用websocket的相关API实现服务端向客户端推送消息;
  • 客户端浏览器解析服务端推送的消息,判断是来单提醒还是客户催单,进行相应的消息提示和语音播报;
  • 约定服务端发送给客户端浏览器数据的数据格式为JSON,字段包括:type,orderId,content;
    • type 消息类型1来单提醒,2客户催单;
    • orderId 订单ID;
    • content消息内容;
      催单业务代码与来单提醒类似:
	/**
     * 用户催单
     * @param id
     */
    @Override
    public void reminder(Long id) {
        Orders orders=orderMapper.getById(id);
        if (orders==null){
            throw new OrderBusinessException(MessageConstant.ORDER_NOT_FOUND);
        }
        Map map=new HashMap<>();
        map.put("type",2);
        map.put("orderId",id);
        map.put("content","订单号"+orders.getNumber());

        String json= JSON.toJSONString(map);
        log.info("推送消息为:{}",json);

        //推送到所有的管理端
        webSocketServer.sendToAllClient(json);

    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/393780.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Jetpack Compose 第 2 课:布局

点击查看&#xff1a;Jetpack Compose 教程 点击查看&#xff1a;Composetutorial 代码 简介 Jetpack Compose 是用于构建原生 Android 界面的新工具包。它使用更少的代码、强大的工具和直观的 Kotlin API&#xff0c;可以帮助您简化并加快 Android 界面开发。 在本教程中&a…

【springboot+vue项目(十四)】基于Oauth2的SSO单点登录(一)整体流程介绍

场景&#xff1a;现在有一个前后端分离的系统&#xff0c;前端框架使用vue-element-template&#xff0c;后端框架使用springbootspringSecurityJWTRedis&#xff08;登录部分&#xff09;现在需要接入到已经存在的第三方基于oauth2.0的非标准接口统一认证系统。 温馨提示&…

html表格标签(下):lable标签,select标签和textara标签

html表格标签(下)&#xff1a;lable标签&#xff0c;select标签和textarea标签 lable标签 搭配 input 使用,点击 label 标签就能选中对应的单选/复选框, 能够提升用户体验。 for 属性: 指定当前 label 和哪个相同 id 的 input 标签对应 (此时点击才是有用的) 运行效果&#x…

php数组运算符 比较 isset、is_null、empty的用法和区别

php数组运算符 1. 数组运算符2. 判断两个数组是否相等3. isset、is_null、empty的用法和区别 1. 数组运算符 注意&#xff1a;只会保留第一个数组中的键值对&#xff0c;而忽略后面数组中相同键名的元素&#xff0c;如果想要合并两个数组并覆盖相同键名的元素&#xff0c;可以…

obsidian的Workbooks插件

学习目标&#xff1a; 学会使用obsidian 学习内容&#xff1a; obsidian咖啡豆教程 | Obsidian的Excel管理解密|Workbooks插件 直接在obsidian中插入表格编辑 但是在实际的使用过程中不好。虽然设置了自动保存&#xff0c;但是实际有时没有保存 读取外部excel文件进行修改 默…

【Jvm】性能调优(拓展)Jprofiler如何监控和解决死锁、内存泄露问题

文章目录 Jprofiler简介1.安装及IDEA集成Jprofiler2.如何监控并解决死锁3.如何监控及解决内存泄露(重点)4.总结5.后话 Jprofiler简介 Jprofilers是针对Java开发的性能分析工具(免费试用10天), 可以对Java程序的内存,CPU,线程,GC,锁等进行监控和分析, 1.安装及IDEA集成Jprofil…

VFH特征的使用(一)

一、SHOT特征描述符可视化 C #include <pcl/point_types.h> #include <pcl/point_cloud.h> #include <pcl/io/pcd_io.h> #include <pcl/features/normal_3d_omp.h> #include <pcl/registration/correspondence_estimation.h> #include <boo…

软件测试项目测试报告总结

测试计划概念&#xff1a;就在软件测试工作实施之前明确测试对象&#xff0c;并且通过资源、时间、风险、测试范围和预算等方面的综合分析和规划&#xff0c;保证有效的实施软件测试。 需求挖掘的6个方面&#xff1a; 1、输入方面 2、处理方面 3、结果输出方面 4、性能需求…

C语言学习day15:数组强化训练

题目一&#xff1a; 称体重&#xff1a;分别给10个值&#xff0c;来获得最大值 思路&#xff1a; 定义数组&#xff0c;给数组内赋10个值第一个下标的值与第二个下标的值进行比较定义max&#xff0c;将比较得来的较大的值赋值给max一直比较直到比较到最后一个下标&#xff0…

ubuntu22.04-磁盘管理-虚拟机动态扩容-系统monitor

文章目录 1.虚拟机2.ubuntu设置3.命令查看4.系统资源管理器1.虚拟机 关闭ubuntu22.04,然后修改虚拟机设置,如下图所示: 修改容量 2.ubuntu设置 搜索打开disks,如下图所示: 选择目标磁盘,选择调整大小到目标大小即可。

萝卜大杂烩 | 把微信接入ChatGPT,变成聊天机器人竟然这么简单!(一起来尝试吧~)

本文来源公众号“萝卜大杂烩”&#xff0c;仅用于学术分享&#xff0c;侵权删&#xff0c;干货满满。 原文链接&#xff1a;把微信接入ChatGPT&#xff0c;变成聊天机器人竟然这么简单&#xff01; 最近的 ChatGPT 又再次火热起来了&#xff0c;各种周边工具也是层出不穷&…

离谱!用ChatGPT进行审稿!

离谱&#xff01;用ChatGPT进行审稿&#xff01; 关注微信公众号: DeepGoAI 在这个信息爆炸的时代&#xff0c;AI已经跑到了学术会议的后台&#xff0c;偷偷摸摸地开始“帮忙”审稿了&#xff01;&#x1f916; 最近&#xff0c;一位教授的LinkedIn动态可谓是火了一把&#xf…

蜂蜜器实验-驱动代码测试

一. 简介 上一篇文章实现了蜂鸣器驱动代码&#xff0c;实现关闭蜂鸣器与打开功能。文章地址如下&#xff1a; 蜂鸣器驱动代码完善-CSDN博客 本文对所实现的蜂鸣器驱动代码进行测试。 二. 蜂鸣器驱动代码测试 1. 准备应用程序 这里应用程序还使用 前面实现所使用的Led应用…

2.18 day5 C++

以下是一个简单的比喻&#xff0c;将多态概念与生活中的实际情况相联系:比喻:动物园的讲解员和动物表演 想象一下你去了一家动物园&#xff0c;看到了许多不同种类的动物&#xff0c;如狮子、大象、猴子等。现在&#xff0c;动物园里有一位讲解员&#xff0c;他会为每种动物表演…

【C++初阶】值得一刷的字符串string相关oj题

&#x1f466;个人主页&#xff1a;Weraphael ✍&#x1f3fb;作者简介&#xff1a;目前学习C和算法 ✈️专栏&#xff1a;C航路 &#x1f40b; 希望大家多多支持&#xff0c;咱一起进步&#xff01;&#x1f601; 如果文章对你有帮助的话 欢迎 评论&#x1f4ac; 点赞&#x1…

Visual Studio+C#实现信道与信息率失真函数

1. 要求 设计一款信道与信息率失真函数计算系统&#xff0c;要求如下&#xff1a; 系统能够通过输入的转移概率矩阵计算对称以及非对称离散无记忆信道的信道容量系统能够通过输入的概率分布以及失真矩阵来计算与信息率失真函数有关的相关参数&#xff0c;例如Dmin&#xff0c…

【初始RabbitMQ】发布订阅的实现

发布确认原理 生产者将信道设置成 confirm 模式&#xff0c;一旦信道进入 confirm 模式&#xff0c;所有在该信道上面发布的消息都将会被指派一个唯一的 ID(从 1 开始)&#xff0c;一旦消息被投递到所有匹配的队列之后&#xff0c;broker 就会发送一个确认给生产者(包含消息的…

Java并发之死锁详解

(/≧▽≦)/~┴┴ 嗨~我叫小奥 ✨✨✨ &#x1f440;&#x1f440;&#x1f440; 个人博客&#xff1a;小奥的博客 &#x1f44d;&#x1f44d;&#x1f44d;&#xff1a;个人CSDN ⭐️⭐️⭐️&#xff1a;传送门 &#x1f379; 本人24应届生一枚&#xff0c;技术和水平有限&am…

拿捏c语言指针(中)

前言 书接上回 拿捏c语言指针&#xff08;上&#xff09; 此篇主要讲解的是指针与数组之间的爱恨情仇&#xff0c;跟着我的脚步一起来看看吧~ 创造不易&#xff0c;可以帮忙点点赞吗 如有差错&#xff0c;欢迎指出 理解数组名 数组名是首元素地址 例外 1.sizeof&#xff0…

【SQL注入】小白手把手入门SQL注入1-数据库基础

前言 本文以SQL注入为核心&#xff0c;讲解MySQL数据库的基本知识&#xff0c;和在SQL注入过程中可能会用到的部分重要语法。 什么是数据库 数据库是“按照数据结构来组织、存储和管理数据的仓库”。是一个长期存储在计算机内的、有组织的、可共享的、统一管理的大量数据的集…