前言
代码链接:
Echo0701/take-out (github.com)
1 Spring Task
1.1 介绍
Spring Task 是 Spring 框架提供的任务调度工具,可以按照约定的时间自动执行某个代码逻辑
应用场景:
- 信用卡每月还款提醒
- 银行贷款每月还款提醒
- 火车票售票系统处理未支付订单
- 入职纪念日为用户发送通知
- .......
1.2 cron 表达式
一个字符串,通过 cron 表达式可以定义任务触发的时间
构成规则:6或7个域,由空格分开
每个域代表一个含义:秒、分钟、小时、日、月、周、年(可选)
cron 表达式在线生成器: https://cron.qqe2.com/
【注】周和日指定其一,剩下一个用 ?代替
1.3 入门案例
使用步骤:
MyTask.java
@Component
@Slf4j
public class MyTask {
/**
* 定时任务,每隔五秒触发一次
*/
@Scheduled(cron = "0/5 * * * * ?")
public void executeTask() {
log.info("定时任务开始执行:{}", new Date());
}
}
2 订单状态定时处理
2.1 需求分析
用户下单以后可能存在的问题:
- 下单后未支付,订单一直处于“待支付”状态
- 用户收货后管理端未点击完成按钮,订单一直处于“派送中”状态
解决方案:
- 通过定时任务每分钟检查一次是否存在支付超时订单(下单超过15分钟未支付),如果存在则修改订单状态未“已取消”
- 通过定时任务每天凌晨1点检查一次是否存在“派送中”订单,如果存在则修改订单状态为“已完成”
2.2 代码开发
OrderTask.java
/**
* 定时任务类,定时处理订单状态
*/
@Component
@Slf4j
public class OrderTask {
@Autowired
private OrderMapper orderMapper;
/**
* 订单超时未支付
*/
@Scheduled(cron = "0 * * * * ? ") //每分钟触发一次
public void processTimeoutOrder() {
log.info("定时处理超时订单:{}", LocalDateTime.now());
LocalDateTime time = LocalDateTime.now().plusMinutes(-15);
//查询超时订单 select * from orders where status = ? and order_time < LocalDateTime.now() - 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());
//这里仍然调用orderMapper.getByStatusAndOrderTimeLT(Orders.PENDING_PAYMENT, time);不过这里的time需要减去一个小时,这样的话就是处理上一天的订单
LocalDateTime time = LocalDateTime.now().plusHours(-1);
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 WebSocket
基于 TCP 的一种新的网络协议,它实现了浏览器与服务器的全双工通信——浏览器和服务器只需要完成一次握手,两者之间就可以创建持久性的连接,并进行双向数据传输。
应用场景:
- 视频弹幕
- 网页聊天
- 体育实况更新
- 股票基金报价实时更新
【注】不需要我们主动发请求获取数据,服务器会主动将数据推送出来
4 来单提醒
用户下单并且支付成功后,需要第一时间通知外卖商家,通知的形式有如下两种:
- 语音播报
- 弹出提示框
设计:
代码开发
OrderServiceImpl.java
/**
* 订单支付
*
* @param ordersPaymentDTO
* @return
*/
public OrderPaymentVO payment(OrdersPaymentDTO ordersPaymentDTO) throws Exception {
// 当前登录用户id
Long userId = BaseContext.getCurrentId();
User user = userMapper.getById(userId);
// //调用微信支付接口,生成预支付交易单
// JSONObject jsonObject = weChatPayUtil.pay(
// ordersPaymentDTO.getOrderNumber(), //商户订单号
// new BigDecimal(0.01), //支付金额,单位 元
// "苍穹外卖订单", //商品描述
// user.getOpenid() //微信用户的openid
// );
//
// if (jsonObject.getString("code") != null && jsonObject.getString("code").equals("ORDERPAID")) {
// throw new OrderBusinessException("该订单已支付");
// }
// OrderPaymentVO vo = jsonObject.toJavaObject(OrderPaymentVO.class);
// vo.setPackageStr(jsonObject.getString("package"));
JSONObject jsonObject = new JSONObject();
jsonObject.put("code", "ORDERPAID");
OrderPaymentVO vo = jsonObject.toJavaObject(OrderPaymentVO.class);
vo.setPackageStr(jsonObject.getString("package"));
Integer OrderPaidStatus = Orders.PAID; //支付状态,已支付
Integer OrderStatus = Orders.TO_BE_CONFIRMED; //订单状态,待接单
//发现没有将支付时间 check_out属性赋值,所以在这里更新
LocalDateTime check_out_time = LocalDateTime.now();
orderMapper.updateStatus(OrderStatus, OrderPaidStatus, check_out_time, orderid);
//通过 websocket 向客户端浏览器推送消息, type orderId content
Map map = new HashMap();
map.put("type", 1); // 1 表示来单提醒;2 表示客户催单
map.put("orderId", orderid);
map.put("content", "订单号:"+ orderid);
//把 map 转换成 json 字符串
String json = JSON.toJSONString(map);
webSocketServer.sendToAllClient(json);
return vo;
}
【注】直接把这个来单提醒的功能放到订单成功支付这,因为之前跳过微信支付,直接在订单支付出完成,并没有执行paysucess方法
5 客户催单
用户在小程序中点击催单按钮以后,需要第一时间通知外卖商家,形式如下:
- 语音播报
- 弹出提示框
接口设计
代码开发
OrderServiceImpl.java
/**
* 客户催单
* @param id
*/
public void reminder(Long id) {
//根据 id 查询订单
Orders ordersDB = orderMapper.getById(id);
//校验订单是否村咋子,并且状态为4
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));
}