当前位置: 首页 > news >正文

苍穹外卖Day10 | 订单状态定时处理、来单提醒、客户催单、SpringTask、WebSocket、cron表达式

目录

SpringTask

1. 介绍

2. cron表达式

3. 入门案例

订单状态定时处理

1. 需求分析和设计

2. 代码开发

3. 功能测试 

WebSocket

1. 介绍

2. HTTP协议和WebSocket协议对比

1. 通信模式:“单向请求 - 响应” vs “双向全双工”

2. 连接性质:“短连接(可复用)” vs “长连接(持续保持)”

3. 头部开销:“每次请求大开销” vs “仅握手一次开销”

4. 状态维护:“无状态” vs “有状态”

HTTP 适用场景:非实时、单向请求 - 响应

WebSocket 适用场景:实时、双向交互

3. 入门案例

​编辑

​编辑

来单提醒

1. 需求分析和设计

2. 代码开发

3. 功能测试

客户催单

1. 需求分析和设计

2.  代码开发

3. 功能测试


SpringTask

1. 介绍

定时自动执行某段java代码

2. cron表达式

cron表达式是一个字符串,可以定义任务触发的时间

日和周通常只能定义一个,另一个写成?

3. 入门案例

server下面新建一个task包,新建MyTask类

package com.sky.task;import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;import java.util.Date;@Component
@Slf4j
public class MyTask {/*** 定时任务,每隔五秒触发一次*/@Scheduled(cron = "0/5 * * * * ?")public void executeTask(){log.info("定时任务开始执行:{}", new Date());}
}

订单状态定时处理

1. 需求分析和设计

不需要接口,不需要前端发送什么请求

2. 代码开发

OrderTask

package com.sky.task;import com.sky.entity.Orders;
import com.sky.mapper.OrderMapper;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.weaver.ast.Or;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;import java.time.LocalDateTime;
import java.util.List;/*** 定时任务类,定时处理订单状态*/
@Component
@Slf4j
public class OrderTask {@Autowiredprivate OrderMapper orderMapper;/*** 处理超时订单的方法*/@Scheduled(cron = "0 * * * * ? ")//每分钟触发一次public void processTimeoutOrder(){log.info("定时处理超时订单:{}", LocalDateTime.now());// 获取减去15min之后的时间LocalDateTime time = LocalDateTime.now().plusMinutes(-15);// 查询超时订单--当前处于待付款状态 且 下单时间已经超过15分钟// select * from orders where status = ? and order_time < (当前时间 - 15分钟)List<Orders> ordersList = orderMapper.getByStatusAndOrderTimeLT(Orders.PENDING_PAYMENT, time);if (ordersList != null && ordersList.size() > 0){// 遍历处理,都设置为已取消for (Orders orders : ordersList){orders.setStatus(Orders.CANCELLED);orders.setCancelReason("订单超时,自动取消");orders.setCancelTime(LocalDateTime.now());orderMapper.update(orders);}}}/*** 处理一直处于派送中状态的订单*/@Scheduled(cron = "0 0 1 * * ?")//每天凌晨1点触发一次public void processDeliveryOrder(){log.info("定时处理处于派送中的订单:{}", LocalDateTime.now());LocalDateTime time = LocalDateTime.now().plusMinutes(-60);List<Orders> ordersList = orderMapper.getByStatusAndOrderTimeLT(Orders.DELIVERY_IN_PROGRESS, time);if (ordersList != null && ordersList.size() > 0){// 遍历处理,都设置为已取消for (Orders orders : ordersList){orders.setStatus(Orders.COMPLETED);orderMapper.update(orders);}}}
}

3. 功能测试 

测试这种和其他功能不同,其他功能可以通过前后端联调或者接口文档测试,测试订单状态定时处理可以先临时改掉cron表达式,测试无误后再改回去

两个都修改成每5秒触发一次

WebSocket

1. 介绍

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

HTTP做不到这个效果,HTTP是请求-响应模式

 

2. HTTP协议和WebSocket协议对比

HTTP 协议和 WebSocket 协议都是基于 TCP 传输层的应用层协议,但其设计目标、通信模式、适用场景存在本质差异 ——HTTP 是 “单向请求 - 响应” 的短连接协议,WebSocket 是 “双向实时通信” 的长连接协议

对比维度HTTP 协议(超文本传输协议)WebSocket 协议(WebSocket 协议)
1. 通信模式单向请求 - 响应(客户端发请求,服务端回响应)双向全双工(客户端 / 服务端可随时主动发消息)
2. 连接性质短连接(请求完成后断开,需复用则靠 Keep-Alive)长连接(建立后持续保持,直到主动关闭)
3. 发起方仅客户端可发起请求(服务端不能主动发消息给客户端)客户端发起握手建立连接,之后双方均可主动发消息
4. 头部开销每次请求 / 响应头部大(如 HTTP/1.1 头部通常几百字节)仅握手阶段用 HTTP 头部,后续通信头部极小(仅 2-10 字节)
5. 状态维护无状态(服务端不保存客户端上下文,需靠 Cookie/Session 维持状态)有状态(连接建立后,服务端可识别客户端身份,无需重复验证)
6. 实时性低(需客户端轮询 / 长轮询获取更新,有延迟)高(消息实时推送,延迟毫秒级)
7. 适用场景静态资源获取、普通接口请求(如网页加载、数据查询)实时交互场景(如聊天、直播、实时通知)
8. 协议标识URL 以 http:///https:// 开头URL 以 ws:///wss://(加密)开头
1. 通信模式:“单向请求 - 响应” vs “双向全双工”

这是两者最核心的差异,直接决定了 “能否实时通信”:

  • HTTP 单向请求 - 响应
    通信必须由客户端先发起 “请求”(如 GET/POST 请求),服务端才能返回 “响应”;服务端无法主动向客户端推送消息—— 若客户端需获取实时更新(如实时聊天消息),只能通过 “轮询”(每隔几秒发一次请求)或 “长轮询”(客户端发请求后,服务端 hold 住连接直到有更新)实现,本质仍是 “客户端主动问、服务端被动答”。
    例:打开网页时,浏览器(客户端)发 HTTP GET 请求获取 HTML/CSS/JS,服务器返回资源后,HTTP 连接通常断开;若要获取网页的实时数据(如股票行情),需浏览器每隔 10 秒发一次 GET 请求查询最新行情。

  • WebSocket 双向全双工
    连接建立后,客户端和服务端处于 “平等地位”,双方均可随时主动向对方发送消息,无需等待对方先请求 —— 类似 “打电话”,接通后双方可随时说话,无需一方 “先提问”。
    例:微信网页版的聊天功能,通过 WebSocket 连接,当好友发消息时,微信服务器可直接将消息 “推” 给你的浏览器(无需你的浏览器主动查询),实现实时聊天。

2. 连接性质:“短连接(可复用)” vs “长连接(持续保持)”
  • HTTP 短连接与 Keep-Alive
    标准 HTTP 是 “短连接”—— 每次请求完成后,TCP 连接会断开;为减少连接建立 / 断开的开销,HTTP/1.1 引入 Connection: Keep-Alive 机制,让 TCP 连接在一定时间内(如 30 秒)复用(后续请求可复用同一连接),但本质仍是 “请求触发式” 连接,无请求时连接可能被回收,且服务端仍不能主动发消息。
    例:浏览一个包含 10 张图片的网页,浏览器会用 1-6 个复用的 TCP 连接(HTTP 连接池)依次请求图片,所有图片加载完成后,连接会在闲置一段时间后断开。

  • WebSocket 长连接
    连接通过 “HTTP 握手” 建立后,TCP 连接会持续保持(直到客户端 / 服务端主动调用 close() 关闭),期间即使没有消息传输,连接也不会被随意断开(可通过 “心跳包” 维持连接,避免被防火墙 / 路由器判定为闲置连接而断开)。
    例:直播平台的 “实时弹幕” 功能,用户打开直播间后,浏览器与直播服务器建立 WebSocket 长连接,后续所有弹幕消息(用户发送、服务器推送)都通过这个连接实时传输,连接会持续到用户关闭直播间。

3. 头部开销:“每次请求大开销” vs “仅握手一次开销”

HTTP 的性能瓶颈之一是 “头部开销”,而 WebSocket 完美解决了这一问题:

  • HTTP 头部开销大
    每次 HTTP 请求 / 响应都需要携带完整的头部(如请求行、Host、Cookie、User-Agent、Accept 等),头部大小通常几百字节,甚至超过实际传输的 “业务数据”(如一个查询用户信息的请求,数据仅 50 字节,头部却有 300 字节);即使复用连接,每次请求仍需携带头部。

  • WebSocket 头部开销极小
    仅在 “建立连接的握手阶段” 使用 HTTP 头部(格式类似 HTTP 请求,用于告诉服务器 “要升级为 WebSocket 连接”),握手成功后,后续传输的 “WebSocket 帧” 头部仅 2-10 字节(包含帧类型、数据长度等核心信息),业务数据占比极高,传输效率远高于 HTTP。
    例:实时推送温度数据(每秒 1 次,每次数据 20 字节),用 HTTP 每次需额外携带 300 字节头部,总开销 320 字节 / 次;用 WebSocket 仅需 2+20=22 字节 / 次,开销仅为 HTTP 的 1/14。

4. 状态维护:“无状态” vs “有状态”
  • HTTP 无状态
    服务端不保存客户端的 “连接状态”—— 每次 HTTP 请求都是独立的,服务端无法通过连接识别客户端身份,需通过 Cookie、Session ID、Token 等额外机制让服务端记住客户端(如登录状态),增加了开发复杂度和请求开销。

  • WebSocket 有状态
    连接建立后,服务端会为每个 WebSocket 连接分配唯一标识(如 sessionId),并保存连接上下文(如客户端用户 ID、连接状态);后续双方通信时,服务端可直接通过连接标识识别客户端,无需重复传递身份信息(如 Token),简化开发且减少开销

HTTP 适用场景:非实时、单向请求 - 响应
  • 静态资源获取:网页(HTML/CSS/JS)、图片、视频、文件下载;
  • 普通接口请求:数据查询(如用户信息、商品列表)、表单提交(如登录、注册)、数据上传(如上传图片);
  • 无实时需求的业务:博客浏览、电商商品详情页、新闻阅读。
WebSocket 适用场景:实时、双向交互
  • 实时聊天:网页版微信、企业 IM(如钉钉网页版)、在线客服;
  • 实时通知:订单状态更新(如 “订单已发货” 推送)、消息提醒(如 “收到新评论”);
  • 实时数据展示:股票行情、实时监控(如设备温度 / 湿度)、直播弹幕;
  • 实时协作:在线文档协作(如腾讯文档)、多人在线游戏(如网页版小游戏)。

3. 入门案例

来单提醒

1. 需求分析和设计

2. 代码开发

在notify/PayNotifyController的paySuccessNotify函数中,最后通过orderService.paySuccess(outTradeNo); 进行修改订单状态、来电提醒

所以通过OrderServiceImpl的paySuccess方法,通过websocket 向用户端推送消息

/*** 支付成功回调** @param request*/@RequestMapping("/paySuccess")public void paySuccessNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {//读取数据String body = readData(request);log.info("支付成功回调:{}", body);//数据解密String plainText = decryptData(body);log.info("解密后的文本:{}", plainText);JSONObject jsonObject = JSON.parseObject(plainText);String outTradeNo = jsonObject.getString("out_trade_no");//商户平台订单号String transactionId = jsonObject.getString("transaction_id");//微信支付交易号log.info("商户平台订单号:{}", outTradeNo);log.info("微信支付交易号:{}", transactionId);//业务处理,修改订单状态、来单提醒orderService.paySuccess(outTradeNo);//给微信响应responseToWeixin(response);}

实际追加代码

    /*** 支付成功,修改订单状态** @param outTradeNo*/public void paySuccess(String outTradeNo) {// 根据订单号查询订单Orders ordersDB = orderMapper.getByNumber(outTradeNo);// 根据订单id更新订单的状态、支付方式、支付状态、结账时间Orders orders = Orders.builder().id(ordersDB.getId()).status(Orders.TO_BE_CONFIRMED).payStatus(Orders.PAID).checkoutTime(LocalDateTime.now()).build();orderMapper.update(orders);// 通过websocket向客户端浏览器推送消息 type orderId contentMap map = new HashMap();map.put("type", 1); // 1表示来单提醒 2表示用户催单map.put("orderId", ordersDB.getId());map.put("content", "订单号" + outTradeNo);String json = JSON.toJSONString(map);webSocketServer.sendToAllClient(json);}

导入代码:WebSocketServer

package com.sky.websocket;import org.springframework.stereotype.Component;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;/*** WebSocket服务*/
@Component
@ServerEndpoint("/ws/{sid}")// 根据路径进行匹配
public class WebSocketServer {//存放会话对象private static Map<String, Session> sessionMap = new HashMap();/*** 连接建立成功调用的方法*/@OnOpenpublic void onOpen(Session session, @PathParam("sid") String sid) {System.out.println("客户端:" + sid + "建立连接");sessionMap.put(sid, session);}/*** 收到客户端消息后调用的方法* 类似于controller* @param message 客户端发送过来的消息*/@OnMessagepublic void onMessage(String message, @PathParam("sid") String sid) {System.out.println("收到来自客户端:" + sid + "的信息:" + message);}/*** 连接关闭调用的方法** @param sid*/@OnClosepublic 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();}}}}

WebSocketConfiguration

package com.sky.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;/*** WebSocket配置类,用于注册WebSocket的Bean*/
@Configuration
public class WebSocketConfiguration {@Beanpublic ServerEndpointExporter serverEndpointExporter() {return new ServerEndpointExporter();}}

WebSocketTask

package com.sky.task;import com.sky.websocket.WebSocketServer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;@Component
public class WebSocketTask {@Autowiredprivate WebSocketServer webSocketServer;/*** 通过WebSocket每隔5秒向客户端发送消息*///@Scheduled(cron = "0/5 * * * * ?")public void sendMessageToClient() {webSocketServer.sendToAllClient("这是来自服务端的消息:" + DateTimeFormatter.ofPattern("HH:mm:ss").format(LocalDateTime.now()));}
}

使用cpolar进行内网穿透

使用穿透后的地址配置application-dev,将内网穿透后的地址配置到dev文件

 notifyUrl: http://1d4c81e9.r7.vip.cpolar.cn/notify/paySuccessrefundNotifyUrl:  http://1d4c81e9.r7.vip.cpolar.cn/notify/refundSuccess

3. 功能测试

前端页面先请求到nginx,由nginx反向代理请求、转发后端服务器,需要提前在nginx配置好相关路径

将schedual五秒定时提醒的注释给注释掉,重新登陆管理员身份,可以正常在用户下单后播报语音提醒

客户催单

1. 需求分析和设计

2.  代码开发

user/OrderController

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

OrderService

   /*** 客户催单* @param id*/void reminder(Long id);

OrderServiceImpl

    /*** 客户催单* @param id*/public void reminder(Long id) {// 根据id查询订单Orders ordersDB = orderMapper.getById(id);// 校验订单是否存在if (ordersDB== null){throw new OrderBusinessException(MessageConstant.ORDER_STATUS_ERROR);}Map map = new HashMap();map.put("type", 2); //1表示来单提醒 2表示客户催单map.put("orderId", id);map.put("content", "订单号:" + ordersDB.getNumber());//通过websocket向客户端浏览器推送消息webSocketServer.sendToAllClient(JSON.toJSONString(map));}

3. 功能测试

在用户端点击催单按钮可以收到语音播报催单提醒


文章转载自:

http://VsaoGYt0.dwkfx.cn
http://xe44l1Yd.dwkfx.cn
http://AW0oJjv6.dwkfx.cn
http://DSGMMaGd.dwkfx.cn
http://1oIrDrUN.dwkfx.cn
http://YBKSLxDk.dwkfx.cn
http://JCIqRXcW.dwkfx.cn
http://x4O0T7xE.dwkfx.cn
http://2KBxPbXA.dwkfx.cn
http://taYF3OSM.dwkfx.cn
http://p3Wc3zpV.dwkfx.cn
http://kG75sHqn.dwkfx.cn
http://xdnZbeb3.dwkfx.cn
http://SSJzyEHh.dwkfx.cn
http://JerJ1WVF.dwkfx.cn
http://Pz3EfP8t.dwkfx.cn
http://DqgcFa26.dwkfx.cn
http://Cgyaj4X4.dwkfx.cn
http://a2m1eO9y.dwkfx.cn
http://H7pWiMsI.dwkfx.cn
http://12TCyGty.dwkfx.cn
http://J2nM6ztO.dwkfx.cn
http://LwkM5wGA.dwkfx.cn
http://wo6vdiXB.dwkfx.cn
http://ynaAVmWX.dwkfx.cn
http://HKFo6ZWm.dwkfx.cn
http://WMtGoVDK.dwkfx.cn
http://jyNYv1qZ.dwkfx.cn
http://KltwaZYw.dwkfx.cn
http://gFRFhDle.dwkfx.cn
http://www.dtcms.com/a/368218.html

相关文章:

  • 01-Hadoop简介与生态系统
  • 如何利用静态代理IP优化爬虫策略?从基础到实战的完整指南
  • 信息安全工程师考点-网络信息安全概述
  • 功能强大的多线程端口扫描工具,支持批量 IP 扫描、多种端口格式输入、扫描结果美化导出,适用于网络安全检测与端口监控场景
  • 自定义格式化数据(BYOFD)(81)
  • 人工智能时代职能科室降本增效KPI设定全流程与思路考察
  • 使用 chromedp 高效爬取 Bing 搜索结果
  • Linux 命令速查宝典:从入门到高效操作
  • 【科研绘图系列】R语言绘制论文合集图
  • 分类、目标检测、实例分割的评估指标
  • 卷积神经网络进行图像分类
  • Java JVM核心原理与面试题解析
  • 【Flutter】RefreshIndicator 无法下拉刷新问题
  • 基于Django+Vue3+YOLO的智能气象检测系统
  • Flutter的三棵树
  • React 样式隔离核心方法和最佳实践
  • 踩坑实录:Django继承AbstractUser时遇到的related_name冲突及解决方案
  • 【Flutter】flutter_local_notifications并发下载任务通知实践
  • 覆盖Transformer、GAN:掩码重建正在重塑时间序列领域!
  • 数据结构基础之队列:数组/链表
  • 数据可视化工具推荐:5款让图表制作轻松上手的神器
  • 【网安基础】--ip地址与子网掩码
  • spring AI 的简单使用
  • 【yolo】YOLOv8 训练模型参数与多机环境差异总结
  • 算法(keep learning)
  • C/C++中的可变参数 (Variadic Arguments)函数机制
  • 深度学习:CNN 模型训练中的学习率调整(基于 PyTorch)
  • Mattermost教程:用Docker搭建自己的开源Slack替代品 (团队聊天)
  • Electron 性能优化:内存管理和渲染效率
  • 数字隔离器,新能源汽车PTC中的“电气安全卫士”