苍穹外卖资源点整理+个人错误解析-Day10-订单状态定时处理(Spring Task)、来单提醒和客户催单

订单状态定时处理

分析

在支付订单界面,如果超时不支付就会取消订单,取消是通过程序而不是人工。

还有派送订单,在派送成功以及用户收到后我们就会点击完成,而如果没点的话我们也需要通过程序来处理。

用户下单后可能存在的情况:

处理逻辑:

代码

逻辑:

先搭好框架:

复制代码
@Scheduled(cron ="0 * * * * ?  ")
public void processTimeoutOrder(){
log.info("定时处理超时订单:{}", LocalDateTime.now());

引入ordersmapper以此来进行操作:

根据逻辑,如何判断每分钟触发一次的前提是超时的订单,一共有两点要求:

1.状态处于待付款

2.处于这个状态超过15分钟

sql语句大概这样写:

复制代码
 selecet*from orders where status=?(待付款状态) and order_time<?(小于当前时间-15min)

放到ordesmapper中:查询出来的很有可能是一个集合:

复制代码
 /*
 判断超时订单
  */
    @Select("select * from orders where status=#{status} and order_time<#{orderTime}")
    List<Orders> getByStatusAndOrderTimeLT(Integer status, LocalDateTime orderTime);
}

再回到代码,如何实现超过15分钟这个逻辑,放在代码里大概是这样:现在的时间减去15分钟得到的时间,比得到的时间小的就是超时,使用代码:

复制代码
LocalDateTime time = LocalDateTime.now().plusMinutes(-15);
复制代码
List<Orders> orders= orderMapper.getByStatusAndOrderTimeLT(Orders.PENDING_PAYMENT, time);

接下来就要判断是否查询到数据:

复制代码
if (orders!=null && orders.size()>0){
    for (Orders orders1:orders){
        orders1.setStatus(Orders.CANCELLED);
        orders1.setCancelReason("订单超时");
        orders1.setCancelTime(LocalDateTime.now());
        orderMapper.update(orders1);
    }

完整代码:

复制代码
@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);
        //查询超时的订单:
        // selecet*from orders where status=?(待付款状态) and order_time<?(小于当前时间-15min)
        List<Orders> orders= orderMapper.getByStatusAndOrderTimeLT(Orders.PENDING_PAYMENT, time);
    if (orders!=null && orders.size()>0){
        for (Orders orders1:orders){
            orders1.setStatus(Orders.CANCELLED);
            orders1.setCancelReason("订单超时");
            orders1.setCancelTime(LocalDateTime.now());
            orderMapper.update(orders1);
        }
    }
    }
}

以上是处理超时订单,以下是处理派送中订单:

复制代码
/*
处理一直派送中订单
 */
@Scheduled(cron = "0 0 1 * * ?")//每天凌晨1点触发
public void processDeliveryOrder(){
    log.info("定时处理处于派送中的订单{}",LocalDateTime.now());
    LocalDateTime localDateTime = LocalDateTime.now().plusMinutes(-60);
    List<Orders> orders = orderMapper.getByStatusAndOrderTimeLT(Orders.DELIVERY_IN_PROGRESS, localDateTime);
    if (orders!=null && orders.size()>0){
        for (Orders orders1:orders){
            orders1.setStatus(Orders.COMPLETED);
            orderMapper.update(orders1);
        }
}

Spring Task

介绍

是一个定时任务框架。作用就是定时自动执行某段java代码。

应用场景如:

cron表达式

本质是一个字符串,用于定义任务触发的时间。

构成规则:

其中这个周代表的是星期几。

一般来说,日和周是不能同时出现的,因为并不能确定这个日就是正确的周

有时候会用特殊字符,比如2月的最后一天究竟是28还是29,表达式通过在线cron表达式生成器网址生成即可:在线Cron表达式生成器

案例

使用:

添加启动:

入门:

测试

只需要运行项目,查看控制台输出是否有输出。

来单提醒

用户支付完订单后商家会跳出语言提醒。

分析

设计:

type是用来标注订单的类型,不然不知道究竟是来单提醒还是客户催单。

orderid是订单id,因为无论是什么提醒都是订单。

content是指具体的提醒内容,指的是弹出来的提醒小框。

代码

通过websocket实现长连接是通过webSocketSever实现的,至于如何和前端绑定,是由前端代码写好了代码。

请求的地址没有写8080,为什么也能请求到呢?

这是因为我们是用了nignx反向代理,是进行了一次转发。但是这样做的前提是在nignx进行了路径配置。

如图所示

在微信的支付订单代码(controller/notify)这个方法中调用的这个方法。

所以在这个方法通过ws给客户端推送消息。

WebSocket

即浏览器可以向服务器传输数据,服务器也可以主动向浏览器传输数据。

区别:

常见的应用场景:

视频弹幕

网页聊天

实况更新

入门案例

实现步骤:

使用websocket.html页面作为socket客户端。

导入maven坐标:

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-websocket</artifactId>

</dependency>

导入服务端组件WebSocketServer,用于和客户端通信。

导入定时任务类定时推送数据。

客户端服务端建立连接,本质上就是一个会话,这个map是用来存储会话对象的:

握手成功后服务端会自动掉用下面这个方法。路径参数加sid对应的是上文中的{sid}

配置:创建一个websocket包:

复制代码
/**
 * 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();
            }
        }
    }

}

在配置类加入一个类:

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

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

}

方便测试效果,加入一个定时测试类:

复制代码
@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()));
    }
}

启动程序之后查看效果:

先打开html文件,显示建立连接:

每五秒出现一个消息。

此外客户端也可以给服务端发消息:

客户催单

分析

是由客户催发的,用户点击催单按钮之后它会发起一次请求来请求服务端,所以需要设计一个新接口。

代码

controller:

复制代码
/*
用户催单提醒
 */
@GetMapping("/reminder/{id}")
public Result reminder(@PathVariable("id") Long id){
    orderService.reminder(id);
    return Result.success();
}

serviceimpl:

复制代码
/*
用户催单提醒
 */
    @Override
    public void reminder(Long id) {
//确认有没有订单
        // 根据id查询订单
        Orders ordersDB = orderMapper.getById(id);
        // 校验订单是否存在,并且状态为4
        if (ordersDB == null) {
            throw new OrderBusinessException(MessageConstant.ORDER_STATUS_ERROR);
        }
        //由于方法要的是json,先转换
        //通过ws向客户端浏览器推送消息 type orderid content
        Map map = new HashMap<>();
       //1为来单提醒,2为客户催单
        map.put("type",2);
        map.put("orderId",ordersDB.getId());
        map.put("content","订单号:"+ordersDB.getNumber());
        //map转json
        String json = JSON.toJSONString(map);
        webSocketServer.sendToAllClient(json);
        //存在就推消息
        webSocketServer.sendToAllClient(json);
    }
相关推荐
Rust语言中文社区39 分钟前
【Rust日报】 丰田“先锋”选择了 Rust
开发语言·后端·rust
努力的小雨42 分钟前
从零跑起 RokidDemo:开发小白也能搞定的入门实践
后端
Y***h1871 小时前
eclipse配置Spring
java·spring·eclipse
東雪木1 小时前
变量与数据类型
java·开发语言
p***62991 小时前
CVE-2024-38819:Spring 框架路径遍历 PoC 漏洞复现
java·后端·spring
Lisonseekpan1 小时前
Java分词器深度评测与实战指南
java·开发语言·后端
c***87191 小时前
Flask:后端框架使用
后端·python·flask
aiopencode1 小时前
iOS 应用性能测试的系统化实践,构建从底层分析到真机回归的多工具协同体系
后端
饕餮争锋1 小时前
Kotlin: [Internal Error] java.lang.NoSuchFieldError: FILE_HASHING_STRATEGY
java·kotlin