商城系统中30分钟未付款自动取消订单怎么实现(简单几种方法)

实现以上功能

方法1:定时任务批量执行

写一个定时任务,每隔 30分钟执行一次,列出所有超出时间范围得订单id的列表

@Async@Scheduled(cron = "20 20 1 * * ?")public void cancelOrder(){log.info("【取消订单任务开始】");QueryWrapper<Order> qw = new QueryWrapper<>();qw.eq("status", Constants.OrderStatus.NOTPAID);qw.eq("aftersale_status", 1);List<Order> orderList = orderMapper.selectList(qw);List<Long> idList = orderList.stream().filter(order -> LocalDateTimeUtil.between(order.getCreateTime(), LocalDateTime.now()).toMinutes() >= 15).map(Order::getId).collect(Collectors.toList());CancelOrderRequest request = new CancelOrderRequest();request.setIdList(idList);h5OrderService.orderBatchCancel(request, null);log.info("【取消订单任务结束】");}

批量执行取消订单操作

@Transactionalpublic String orderBatchCancel(CancelOrderRequest request, Long userId) {LocalDateTime optDate = LocalDateTime.now();if (CollectionUtil.isEmpty(request.getIdList())) {throw new RuntimeException("未指定需要取消的订单号");}QueryWrapper<Order> orderQw = new QueryWrapper<>();orderQw.in("id", request.getIdList());List<Order> orderList = orderMapper.selectList(orderQw);if (orderList.size() < request.getIdList().size()) {throw new RuntimeException("未查询到订单信息");}Order order = orderList.get(0);//查orderItemQueryWrapper<OrderItem> qw = new QueryWrapper<>();qw.in("order_id", request.getIdList());List<OrderItem> orderItems = orderItemMapper.selectList(qw);if (CollectionUtil.isEmpty(orderItems)) {throw new RuntimeException("未查询到订单信息");}long count = orderList.stream().filter(it -> !Constants.H5OrderStatus.UN_PAY.equals(it.getStatus())).count();if (count > 0) {throw new RuntimeException("订单状态已更新,请刷新页面");}List<OrderOperateHistory> addHistoryList = new ArrayList<>();orderList.forEach(item -> {item.setStatus(Constants.H5OrderStatus.CLOSED);item.setUpdateTime(optDate);item.setUpdateBy(userId);OrderOperateHistory history = new OrderOperateHistory();history.setOrderId(item.getId());history.setOrderSn(item.getOrderSn());history.setOperateMan(userId == null ? "后台管理员" : "" + item.getMemberId());history.setOrderStatus(Constants.H5OrderStatus.CLOSED);history.setCreateTime(optDate);history.setCreateBy(userId);history.setUpdateBy(userId);history.setUpdateTime(optDate);addHistoryList.add(history);});//取消订单int rows = orderMapper.cancelBatch(orderList);if (rows < 1) {throw new RuntimeException("更改订单状态失败");}orderItems.stream().collect(Collectors.groupingBy(it -> it.getSkuId())).forEach((k, v) -> {AtomicReference<Integer> totalCount = new AtomicReference<>(0);v.forEach(it -> totalCount.updateAndGet(v1 -> v1 + it.getQuantity()));skuMapper.updateStockById(k, optDate, -1 * totalCount.get());});//创建订单操作记录boolean flag = orderOperateHistoryService.saveBatch(addHistoryList);if (!flag) {throw new RuntimeException("创建订单操作记录失败");}// 根据order 退还积分orderUsePointsService.refundOrderUsePoints(order.getId());return "取消订单成功";}

方法2:使用jdk自带的阻塞队列

实现一个简单的队列,每隔一定时间执行队列。

/*** (30分钟扫描三十分钟内需要发送的订单)*/@Scheduled(cron = "0 0/30 * * * ?")public void checkOrderStatus() {DelayQueue<ItemVo<Order>> queue = new DelayQueue<ItemVo<Order>>();try {// 插入订单new Thread(new PutOrder(queue)).start();} catch (Exception e) {e.printStackTrace();}}

这里使用队列的优势可以跟前端时间匹配上,前端读秒几秒这里就什么时候取消 

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.toolkit.CollectionUtils;
import com.kxmall.market.biz.BeanContext;
import com.kxmall.market.data.dto.PrivatePlanAndDetailDO;
import com.kxmall.market.data.mapper.PrivatePlanMapper;
import com.kxmall.market.data.util.DateUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;import java.util.Date;
import java.util.List;
import java.util.concurrent.DelayQueue;/*** 模拟订单插入的功能*/public class PutOrder implements Runnable {private static final Logger logger = LoggerFactory.getLogger(PutOrder.class);//    public static PutOrder putOrder;@Autowiredprivate PrivatePlanMapper privatePlanMapper;// 使用DelayQueue:一个使用优先级队列实现的无界阻塞队列。private DelayQueue<ItemVo<Order>> queue;public PutOrder(DelayQueue<ItemVo<Order>> queue) {super();this.queue = queue;}@Overridepublic void run() {Date startTime = new Date();privatePlanMapper = BeanContext.getApplicationContext().getBean(PrivatePlanMapper.class);// 每隔半小时获取半小时内需要取消的List<PrivatePlanAndDetailDO> privatePlanDOS = privatePlanMapper.getPrivatePlanDetailList();logger.info("待取消清单->{}", JSON.toJSONString(privatePlanDOS));if (CollectionUtils.isNotEmpty(privatePlanDOS)) {privatePlanDOS.forEach(s -> {long count = DateUtil.calLastedTime(startTime,s.getTodoTime() )*1000;Order tbOrder = new Order(s.getId().toString(), 0.0);ItemVo<Order> itemVoTb = new ItemVo<Order>(count, tbOrder);queue.offer(itemVoTb);logger.info("订单{}将在->{}秒后取消", s.getId().toString(),count/1000);});// 取出过期订单的线程new Thread(new FetchOrder(queue)).start();}else {logger.info("没有待发送订单->");}}
}
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
/*** 存到队列里的元素* 支持延时获取的元素的阻塞队列,元素必须要实现Delayed接口。* 根据订单有效时间作为队列的优先级* @param <T>*/
public class ItemVo<T> implements Delayed{// 到期时间 单位:msprivate long activeTime;// 订单实体(使用泛型是因为后续扩展其他业务共用此业务类)private T data;public ItemVo(long activeTime, T data) {super();// 将传入的时间转换为超时的时刻this.activeTime = TimeUnit.NANOSECONDS.convert(activeTime, TimeUnit.MILLISECONDS)+ System.nanoTime();this.data = data;}public long getActiveTime() {return activeTime;}public T getData() {return data;}// 按照剩余时间进行排序@Overridepublic int compareTo(Delayed o) {// 订单剩余时间-当前传入的时间= 实际剩余时间(单位纳秒)long d = getDelay(TimeUnit.NANOSECONDS) - o.getDelay(TimeUnit.NANOSECONDS);// 根据剩余时间判断等于0 返回1 不等于0// 有可能大于0 有可能小于0  大于0返回1  小于返回-1return (d == 0) ? 0 : ((d > 0) ? 1 : -1);}// 获取剩余时间@Overridepublic long getDelay(TimeUnit unit) {// 剩余时间= 到期时间-当前系统时间,系统一般是纳秒级的,所以这里做一次转换long d = unit.convert(activeTime-System.nanoTime(), TimeUnit.NANOSECONDS);return d;}}

方法3:分布式场景(mq队列)

使用mq队列,消费消息。如果消息到达30分钟没有付款,那么就取消

方法4:分布式场景(redis)

使用redis商品下单,设置过期时间 30分钟,并且写一个redis监听器,监听过期需要操作的key,然后判单是否过期

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/643013.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

HTTP/1.1、HTTP/2、HTTP/3 基本概述

参考链接 https://xiaolincoding.com/network/2_http/http_interview.html#http-1-1-%E7%9B%B8%E6%AF%94-http-1-0-%E6%8F%90%E9%AB%98%E4%BA%86%E4%BB%80%E4%B9%88%E6%80%A7%E8%83%BD HTTP/1.1 HTTP/1.1 相比 HTTP/1.0 性能上的改进&#xff1a; 使用长连接的方式改善了 HTT…

Leetcode—25.K 个一组翻转链表【困难】

2023每日刷题&#xff08;八十二&#xff09; Leetcode—25.K 个一组翻转链表 算法思想 实现代码 /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode() : val(0), next(nullptr) {}* ListNode(int x) : val…

Conda python管理packages一 从入门到精通

Conda系列&#xff1a; 翻译: Anaconda 与 miniconda的区别Miniconda介绍以及安装Conda python运行的包和环境管理 入门Conda python管理环境environments 一 从入门到精通Conda python管理环境environments 二 从入门到精通Conda python管理环境environments 三 从入门到精通…

力扣每日一题 ---- 1970. 你能穿过矩阵的最后一天

这题对于没怎么做到过这题的朋友来说比较陌生&#xff0c;他给出的条件是两个都是动态的条件&#xff0c;一个条件随着另一个条件走&#xff0c;这个时候是很熟悉这题的朋友就会感觉比较麻烦&#xff0c;比较难&#xff0c;就不想写了&#xff0c;两个强相关条件的第一个是水每…

IT大侦“碳”:Concept Luna向循环设计持续演进

今天聊点轻松的话题。上个月&#xff0c;小编用来吃饭的家伙开始闹罢工&#xff0c;笔记本的触控和键盘突然没了反应&#xff0c;电脑虽然还能打开&#xff0c;但嗡嗡直叫的风扇让我意识到这件事并不简单。 你问我为什么电脑会出问题&#xff1f;好吧&#xff0c;那我得先搞清楚…

java web mvc-08-Grails 入门介绍

拓展阅读 Spring Web MVC-00-重学 mvc mvc-01-Model-View-Controller 概览 web mvc-03-JFinal web mvc-04-Apache Wicket web mvc-05-JSF JavaServer Faces web mvc-06-play framework intro web mvc-07-Vaadin web mvc-08-Grails 开源 The jdbc pool for java.(java …

Unity通用渲染管线升级URP、HDRP

Unity通用渲染管线升级URP、HDRP 一、Build-in Pipline升级到 URP 一、Build-in Pipline升级到 URP 安装URP包 升级所有材质&#xff08;升级完成后材质会变成紫红色&#xff0c;Shader丢失&#xff0c;此为正常现象&#xff09; 创建 UniversalRenderPipelineAsset 配置文…

深入Kafka broker

一、协议设计 颗粒度, PRODUCE和FETCH中支持topic,partion等层级的颗粒度;测试友好, 基于session_id和epoch确定一条拉取链路的fetch session;全量增量结合, FetchRequest中的全量拉取和增量拉取;基本结构: headerbody。 常见header: api_key, api_version, corelation_id, cl…

8.前端--CSS-显示模式

元素的显示模式 元素显示模式就是元素&#xff08;标签&#xff09;以什么方式进行显示&#xff0c;比如<div>自己占一行&#xff0c;比如一行可以放多个<span>。 1.块元素 常见的块元素 常见的块元素&#xff1a;<h1>~<h6>、<p>、<div>、…

如何预防服务器IP被劫持,危害有什么?

服务器IP被劫持是一种严重的网络安全问题&#xff0c;攻击者通过篡改服务器的IP地址&#xff0c;将网络流量重定向到恶意服务器或网站&#xff0c;导致用户无法正常访问目标服务器&#xff0c;并可能面临数据泄露、恶意软件感染等安全风险。了解服务器IP被劫持的危害和预防措施…

Jumpserver中安装httpx

查看已安装的包 pip freeze # python -V # Python 2.7.5安装anyio 刚开始直接pip install httpx&#xff0c;未设置版本号&#xff0c;因已有idna2.7&#xff0c;所以出现版本不兼容的现象 anyio 3.0.0 requires idna>2.8, but you have idna 2.7 which is incompatible.…

船的最小载重量-算法

说明&#xff1a;题解完全是从leetCode上拉下来的&#xff0c;在这里只是作为一个备份&#xff0c;怕之后找不着了。同时也分享给大家&#xff0c;这个题目用了一个我之前从未遇到的思路。 原题&#xff1a;船的最小载重量-leetCode1101 题目&#xff08;看懂题目了吗&#xff…

街机模拟游戏逆向工程(HACKROM)教程:[18]分析的思路

在之前的文章,我们已经提及,得到玩家的血量,可以用这个分析出哪些想要的东西: 1、被敌人攻击 - 得到敌人某个技能的攻击力,自身的防御力。 2、被队友攻击 - 得到队友的攻击力。 3、被道具击中 - 得到道具的攻击力。 4、使用扣血技能 - 得到使用技能扣除的血量值。 5、…

pikachu_csrf通关攻略

csrf&#xff08;get&#xff09; 打开pikachu靶场&#xff1a; 1. 根据提示给的账户密码进行登录 2. 打开代理拦截数据包将拦截数据发送到已打开的burp中&#xff1a; 修改数据进行发包&#xff1a; 从上面的url可见&#xff0c;修改用户信息的时候&#xff0c;是不带任何不…

网易云音乐JS逆向分析

文章目录 页面分析抓包分析JS逆向分析代码编写 页面分析 先来分析一下页面 当我们点击播放按钮的时候&#xff0c;音乐开始播放。实际上这个逻辑背后的原理是这个按钮后面对应的是一个url&#xff0c;这个地址是通过ajax来进行局部刷新的。 所以我们可以通过抓包工具&#…

软件开发:大厂中的灰度发布到底是什么样的一个流程

前言 灰度发布是一种软件发布的策略&#xff0c;也被称为渐进式发布或部分用户发布。在灰度发布中&#xff0c;新版本的软件不会立即对所有用户进行全面发布&#xff0c;而是先选择一小部分用户进行测试和试用。这样可以在生产环境中逐步引入新功能或修复bug&#xff0c;以降低…

K8S搭建(centos)四、安装K8S

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

linux环境开发工具---yum与vim

1.Linux软件包管理器yum 1.1什么是软件包 在学习linux过程中&#xff0c;我们常常会遇到某些指令用不了的时候&#xff0c;原因除了权限问题外&#xff0c;还有可能是你当前的linux环境并没有安装相应的软件包。而在Linux下载安装软件的办法有两个&#xff0c;一个是先下载所需…

Aspx漏洞总结

第一部分&#xff0c;.NET项目当中的dll都可以进行反编译&#xff1a; 在java中有很多jar包&#xff0c;而在.NET框架中的bin中对应有很多DLL文件&#xff0c;bin下面都是可执行文件&#xff0c;这些文件都是很多代码封装的&#xff0c;想要查看源码&#xff0c;都需要通过反编…

Microsoft Remote Desktop for Mac(远程桌面连接)激活版

Microsoft Remote Desktop是一款由微软开发的远程桌面连接工具&#xff0c;它允许用户从另一台计算机或移动设备远程连接到Windows桌面或服务器。 以下是该软件的一些主要特点和功能&#xff1a; 跨平台支持&#xff1a;Microsoft Remote Desktop支持Windows、macOS、iOS和Andr…