苍穹外卖|项目日记(day10)
前言: 第10天的任务相对接单, 主要是熟悉一下SpringTask和WebSocket, 不过由于WebSocket代码比较固定, 直接导入即可
吐槽下, md文件导入到csdn上, 本地上传的图片老是丢失, 需要重新导入, 这是我比较讨厌的一点, 害的我再做笔记时都尽量不使用图片.
今日收获:
1.熟悉SpingTask
2.了解WebSocket
3. 来单提醒和客户催单业务实现
一.SpringTask
1.Spring Task 是 Spring 框架提供的任务调度模块,用于实现定时任务功能。
2.核心特性
- 轻量级:作为 Spring 框架的一部分,无需额外依赖
- 注解驱动:通过
@Scheduled
注解即可定义定时任务 - 灵活配置:支持 cron 表达式、固定延迟、固定速率等多种调度方式
- 线程池支持:可以配置任务执行的线程池
3.基本使用
a.在项目启动类上加上开启任务调度注解
@SpringBootApplication
@EnableTransactionManagement //开启注解方式的事务管理
@Slf4j
@EnableCaching // 增加缓存注解
@EnableScheduling // 开启任务调度
public class SkyApplication {
public static void main(String[] args) {
SpringApplication.run(SkyApplication.class, args);
log.info("server started");
}
}
b.创建任务调度, 加上Scheduled注解,并设置cron
Component
@Slf4j
public class OrderTask {
@Autowired
private OrderMapper orderMapper;
/**
* 处理超时订单
*/
@Scheduled(cron = "0 * * * * ?") // 每分钟触发一次
// @Scheduled(cron = "1/5 * * * * ?")
public void processTimeoutOrder(){
log.info("定时处理超时订单: {}", LocalDateTime.now());
// 待付款订单超时15分钟取消
LocalDateTime time = LocalDateTime.now().plusMinutes(-15);
Integer status = Orders.PENDING_PAYMENT;
List<Orders> list = orderMapper.getByStatusandOrderTime(status, time);
if(list != null && list.size() > 0){
for (Orders orders : list) {
// 取消订单, 设置修改时间
orders.setStatus(Orders.CANCELLED);
orders.setCancelTime(LocalDateTime.now());
orders.setCancelReason("订单超时, 自动取消");
orderMapper.update(orders);
}
}
}
}
[!tip]
cron表达式可以通过网页在线生成, 并不需要耗费太长时间去记
二. 了解websocket
WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议,它使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。
1. WebSocket 基础
1.1 WebSocket 特点
- 全双工通信:客户端和服务器可以同时发送和接收数据
- 低延迟:相比 HTTP 轮询,WebSocket 通信更高效
- 持久连接:建立连接后保持打开状态
- 基于事件:通过事件监听处理消息
Java WebSocket 详解
WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议,它使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。
1. WebSocket 基础
1.1 WebSocket 特点
- 全双工通信:客户端和服务器可以同时发送和接收数据
- 低延迟:相比 HTTP 轮询,WebSocket 通信更高效
- 持久连接:建立连接后保持打开状态
- 基于事件:通过事件监听处理消息
1.2 WebSocket 与 HTTP 对比
特性 | HTTP | WebSocket |
---|---|---|
通信方式 | 请求-响应 | 全双工 |
连接 | 短连接 | 长连接 |
服务器推送 | 不支持 | 支持 |
协议 | 无状态 | 有状态 |
2 核心注解
@ServerEndpoint
: 声明一个 WebSocket 服务端点@OnOpen
: 连接建立时触发@OnClose
: 连接关闭时触发@OnMessage
: 收到消息时触发@OnError
: 发生错误时触发
3. Spring WebSocket 实现
Spring 框架提供了对 WebSocket 的高级支持,包括:
- STOMP 协议支持
- 消息代理集成
- 更简单的编程模型
3.1基本配置
/**
* WebSocket配置类,用于注册WebSocket的Bean
*/
@Configuration
public class WebSocketConfiguration {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
3.2自定义处理
/**
* WebSocket服务
*/
@Component
@ServerEndpoint("/ws/{sid}")
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.来单提醒
由于我是跳过微信支付的, 只是简单的模拟来电提醒, 并不是真的支付,.
逻辑: 在用户支付成功后, 让服务端向商家管理端发送来电提醒, 所以只需在paysuccess中使用websocket方法就行
如图:
代码:
/**
* 支付成功,修改订单状态
*
* @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向客户端浏览器推送消息 type orderId content
Map 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);
}
2.客户催单
此接口也相对简单, 没有过多的业务逻辑, 只需服务端向商家发送催单信息
代码:
/**
* 用户催单
* @param id
*/
@Override
public void reminder(Long id) {
// type orderid content
// 根据id查询订单
Orders ordersDB = orderMapper.getById(id);
// 校验订单是否存在
if (ordersDB == null) {
throw new OrderBusinessException(MessageConstant.ORDER_NOT_FOUND);
}
Map map = new HashMap();
map.put("orderid", ordersDB.getId());
map.put("type", 2);
map.put("content", "订单号: " + ordersDB.getNumber());
String json = JSON.toJSONString(map);
webSocketServer.sendToAllClient(json);
}