外卖开发(八)—— SpringTask 和 WebSocket
一、利用SpringTask完成定时任务
Spring Task是Spring框架提供的任务调度工具,可以按照约定的时间自动执行某个代码逻辑。
定位: 定时任务框架
作用: 定时自动执行某段Java代码
应用场景:
信用卡每月还款提醒
银行贷款每月还款提醒
火车票售票系统处理未支付订单。入职纪念日为用户发送通知
只要是需要定时处理的场景都可以使用Spring Task
1、cron表达式
@Scheduled(cron = "0 0 1 * * ? ")
注解
cron表达式其实就是一个字符串,通过cron表达式可以定义任务触发的时间
构成规则:分为6或7个域,由空格分隔开,每个域代表一个含义
每个域的含义分别为:秒、分钟、小时、日、月、周、年(可选)
cron表达式在线生成: https://cron.qqe2.com/
2、springtask实现
使用springtask
解决外卖订单已经派送成功,用户迟迟不点击已送达,需要我们在一个特点的时间点(凌晨一点)检查一天前的所有 “正在派送” 的订单,并修改状态为已完成。
需求分析:
用户收货后管理端未点击完成按钮,订单一直处于“派送中”
状态
通过定时任务每天凌晨1点检查一次是否存在“派送中”的订单,如果存在则修改订单状态为“已完成”
1、自定义定时任务类
OrderTask.java
@Component
@Slf4j
public class OrderTask {
@Autowired
private OrderMapper orderMapper;
/**
* 每天夜里一点检查是否存在已派送未完成的订单
*/
@Scheduled(cron = "0 0 1 * * ? ")
public void processDeliveryOrder(){
log.info("系统处理未支付订单:{}",LocalDateTime.now());
LocalDateTime localDateTime = LocalDateTime.now().plusHours(-1); //检查时间小于0:00的订单
List<Orders> orders = orderMapper.checkDeliveryUnsuccess(Orders.DELIVERY_IN_PROGRESS,localDateTime);
if(orders != null && orders.size() > 0){
for (Orders order : orders){
order.setPayStatus(Orders.COMPLETED);
order.setDeliveryTime(LocalDateTime.now());
orderMapper.update(order);
}
}
}
}
2、mapper接口
/**
* 每天一点检查前一日正在派送的订单,改为已完成
* @param status
* @param deliveryTime
* @return
*/
@Select("select * from orders where status = #{status} and delivery_time < #{deliveryTime}")
List<Orders> checkDeliveryUnsuccess(Integer status,LocalDateTime deliveryTime);
通过上述springtask类,实现了每天凌晨1点,定时去数据库中搜索前一天的派送中的订单,并统一完成订单。
二、使用webSocket实现接单、催单提醒
WebSocket
是基于 TCP
的一种新的网络协议。它实现了浏览器与服务器全双工通信——浏览器和服务器只需要完成—次握手,两者之间就可以创建持久性的连接,并进行双向数据传输。
1、代码分析
websocket配置类
WebSocketConfiguration.java
/**
* WebSocket配置类,用于注册WebSocket的Bean
*/
@Configuration
public class WebSocketConfiguration {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
websocket服务
WebSocketServer.java
/**
* 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();
}
}
}
}
2、催单提醒
用户在小程序中点击催单按钮后,需要第一时间通知外卖商家。
设计:
通过WebSocket实现管理端页面和服务端保持长连接状态
当用户点击催单按钮后,调用WebSocket的相关API实现服务端向客户端推送消息
客户端浏览器解析服务端推送的消息,判断是来单提醒还是客户催单,进行相应的消息提示。约定服务端发送给客户端浏览器的数据格式为JSON,字段包括: type,orderld, content
- type为消息类型,1为来单提醒2为客户催单
- orderld为订单id
- content为消息内容
OrderController.java
/**
* 用户催单
* @param id
* @return
*/
@GetMapping("/reminder/{id}")
@ApiOperation("用户催单")
public Result userReminder(@PathVariable Long id){
orderService.userReminder(id);
return Result.success();
}
OrderService.java
/**
* 用户催单
* @param id
*/
@Override
public void userReminder(Long id) {
Orders orders = orderMapper.queryOrderById(id); //根据id查询订单详情
String number = orders.getNumber();
Map map = new HashMap();
map.put("type",2);
map.put("orderId",id);
map.put("content","订单号:" + number);
String json = JSON.toJSONString(map);
webSocketServer.sendToAllClient(json);
}