【苍穹外卖|Day10】Spring Task、订单状态定时处理、WebSocket、来单提醒、客户催单

D10

本文记录「苍穹外卖」项目开发中的关键技术实践与踩坑思考,包含个人在实际开发中的具体过程遇到的问题 以及知识点总结
希望可以给一起学习的大家带来帮助

完整项目在github上rannn-1127/sky-take-out: 苍穹外卖 (还没完结)

文章目录

1.Spring Task

1.1介绍

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

定位:定时任务框架

作用:定时自动执行某段java代码

1.2cron表达式

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

构成规则:分为6或7个域,由空格分隔开,每个域代表一个含义

每个域的含义分别为:秒、分钟、小时、日、月份、星期

在线Cron表达式生成器

常用的符号

  • \* (星号) : 表示任何值。例如,* 在分钟字段中表示"每一分钟"。
  • , (逗号) : 用来指定多个值。例如,1,2,3 表示"1、2、3分钟"。
  • - (连字符) : 用来指定一个范围。例如,1-5 表示"1 到 5"。
  • / (斜杠) : 用于指定步进。例如,*/5 在分钟字段中表示"每 5 分钟执行一次"。
  • ? (问号) : 表示不指定值,通常用于"日"和"星期几"字段中。当两个字段互相冲突时,使用 ? 来避免冲突。比如,如果你已经指定了日期字段为 1,那么"星期几"字段就可以使用 ?,表示不关心星期几。
  • L (最后) : 用于"日"和"星期几"字段,表示"最后一天"或"最后一个星期几"。例如,L 在日字段中表示"每月最后一天"。
  • W (工作日) : 用于"日"字段,表示离指定日期最近的工作日。例如,15W 表示"每月 15 日附近的工作日"。
  • # (星期几的第 N 个) : 用于"星期几"字段,表示一个月中某个星期几的第 N 个。例如,6#3 表示"每个月的第三个星期五"
1.3入门案例

Spring Task使用步骤:

  1. 导入maven坐标spring-context
  2. 启动类添加注解@EnableScheduling开启任务调度
  3. 自定义定时服务类
java 复制代码
/**
 *
 * 自定义任务类
 */
@Component
@Slf4j
public class MyTask {
    /**
     * 定时任务,每五秒触发一次
     */
    @Scheduled(cron = "0/5 * * * * ?")
    public void executeTask(){
        log.info("定时任务开始执行:{}",new Date());
    }

}

2.订单状态定时处理

2.1需求分析
2.2代码实现

在Task包下创建一个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);

        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 processDeliverOrder(){
        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);
            }
        }
    }
}

在OrderMapper类下新增

java 复制代码
/**
     * 根据订单状态和下单时间查询订单
     * @param status
     * @param orderTime
     * @return
     */
    @Select("select * from orders where status = #{status} and order_time < #{orderTime}")
    List<Orders> getByStatusAndOrderTimeLT(Integer status, LocalDateTime orderTime);

3.WebSocket

3.1介绍

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

应用场景

  • 视频弹幕
  • 网页聊天
  • 体育实况更新
  • 股票基金报价实时更新
3.2入门案例

步骤

  1. 直接使用websocket.html页面作为WebSocket客户端
  2. 导入WebSocket的Maven坐标
  3. 导入webSocket服务器组件WebSocketServer,用于和客户端通信
  4. 导入配置类WebSocketConfiguration,注册WebSocket的服务器组件
  5. 导入定时任务类WebSocketTask,定时向客户端推送数据
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();
            }
        }
    }

}
java 复制代码
/**
 * WebSocket配置类,用于注册WebSocket的Bean
 */
@Configuration
public class WebSocketConfiguration {

    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }

}
java 复制代码
@Component
public class WebSocketTask {
    @Autowired
    private WebSocketServer webSocketServer;

    /**
     * 通过WebSocket每隔5秒向客户端发送消息
     */
    @Scheduled(cron = "0/5 * * * * ?")
    public void sendMessageToClient() {
        webSocketServer.sendToAllClient("这是来自服务端的消息:" + DateTimeFormatter.ofPattern("HH:mm:ss").format(LocalDateTime.now()));
    }
}

4.来单提醒

4.1需求分析

用户下单且支付成功后,需要第一时间通知外卖商家:

  • 语音播报
  • 弹出提示框
4.2代码实现

修改OrderServiceImpl中的paySuccess方法

java 复制代码
/**
     * 支付成功,修改订单状态
     * @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向客户端推送消息
        Map map = new HashMap();
        map.put("type",1);//1来单提醒
        map.put("orderId",ordersDB.getId());
        map.put("content","订单号:" + outTradeNo);

        String json = JSON.toJSONString(map);
        webSocketServer.sendToAllClient(json);

    }

支付成功后就通过WebSocket向客户端推送消息

5.客户催单

5.1需求分析

用户在小程序中点击催单按钮后,需要第一时间通知外卖商家:

  • 语音播报
  • 弹出提示框

用户催单接口设计

5.2代码开发

在User包下的OrderController类中增加接口

java 复制代码
/**
     * 客户催单
     * @param id
     * @return
     */
    @GetMapping("/reminder/{id}")
    public Result reminder(@PathVariable Long id){
        log.info("客户催单:{}",id);
        orderService.reminder(id);
        return Result.success();
    } 

在OrderServiceImp中增加客户催单方法

java 复制代码
/**
     * 客户催单
     * @param id
     */
    @Override
    public void reminder(Long id) {
        // 根据id查询订单
        Orders ordersDB = orderMapper.getById(id);

        // 校验订单是否存在,并且状态为4
        if (ordersDB == null) {
            throw new OrderBusinessException(MessageConstant.ORDER_STATUS_ERROR);
        }

        //通过WebSocket向客户端浏览器推送消息
        Map map = new HashMap();
        map.put("type",2);//2表示客户催单
        map.put("orderId",id);
        map.put("content","订单号:" + ordersDB.getId());
        String json = JSON.toJSONString(map);

        webSocketServer.sendToAllClient(json);
    }
相关推荐
cqbzcsq2 小时前
MC Forge 1.20.1 mod开发学习笔记(战利品、标签、配方)
java·笔记·学习·mod·mc
追随者永远是胜利者2 小时前
(LeetCode-Hot100)461. 汉明距离
java·算法·leetcode·职场和发展·go
人道领域2 小时前
SpringBoot多环境配置实战指南
java·开发语言·spring boot·github
捷利迅分享2 小时前
Android TV 4分屏独立播放电视应用完整开发方案
java
马猴烧酒.2 小时前
【JAVA算法|hot100】栈类型题目详解笔记
java·笔记
Dragon Wu2 小时前
SpringCloud 多模块下引入独立bom模块的正确架构方案
java·spring boot·后端·spring cloud·架构·springboot
_BugMan2 小时前
Spring核心知识清单
java·后端·spring
yaoxin5211232 小时前
329. Java Stream API - 打开 Optional 的正确方式:如何安全提取值?
java·安全·rpc
山北雨夜漫步2 小时前
点评day04 Redisson
java·jvm