實現(xiàn)以上功能
方法1:定時任務(wù)批量執(zhí)行
寫一個定時任務(wù),每隔 30分鐘執(zhí)行一次,列出所有超出時間范圍得訂單id的列表
@Async
@Scheduled(cron = "20 20 1 * * ?")
public void cancelOrder(){
log.info("【取消訂單任務(wù)開始】");
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("【取消訂單任務(wù)結(jié)束】");
}
批量執(zhí)行取消訂單操作
@Transactional
public 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);
//查orderItem
QueryWrapper<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("訂單狀態(tài)已更新,請刷新頁面");
}
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("更改訂單狀態(tài)失敗");
}
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());
});
//創(chuàng)建訂單操作記錄
boolean flag = orderOperateHistoryService.saveBatch(addHistoryList);
if (!flag) {
throw new RuntimeException("創(chuàng)建訂單操作記錄失敗");
}
// 根據(jù)order 退還積分
orderUsePointsService.refundOrderUsePoints(order.getId());
return "取消訂單成功";
}
方法2:使用jdk自帶的阻塞隊列
實現(xiàn)一個簡單的隊列,每隔一定時間執(zhí)行隊列。
/**
* (30分鐘掃描三十分鐘內(nèi)需要發(fā)送的訂單)
*/
@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();
}
}
這里使用隊列的優(yōu)勢可以跟前端時間匹配上,前端讀秒幾秒這里就什么時候取消?
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;
@Autowired
private PrivatePlanMapper privatePlanMapper;
// 使用DelayQueue:一個使用優(yōu)先級隊列實現(xiàn)的無界阻塞隊列。
private DelayQueue<ItemVo<Order>> queue;
public PutOrder(DelayQueue<ItemVo<Order>> queue) {
super();
this.queue = queue;
}
@Override
public void run() {
Date startTime = new Date();
privatePlanMapper = BeanContext.getApplicationContext().getBean(PrivatePlanMapper.class);
// 每隔半小時獲取半小時內(nèi)需要取消的
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("沒有待發(fā)送訂單->");
}
}
}
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
/**
* 存到隊列里的元素
* 支持延時獲取的元素的阻塞隊列,元素必須要實現(xiàn)Delayed接口。
* 根據(jù)訂單有效時間作為隊列的優(yōu)先級
* @param <T>
*/
public class ItemVo<T> implements Delayed{
// 到期時間 單位:ms
private long activeTime;
// 訂單實體(使用泛型是因為后續(xù)擴(kuò)展其他業(yè)務(wù)共用此業(yè)務(wù)類)
private T data;
public ItemVo(long activeTime, T data) {
super();
// 將傳入的時間轉(zhuǎn)換為超時的時刻
this.activeTime = TimeUnit.NANOSECONDS.convert(activeTime, TimeUnit.MILLISECONDS)
+ System.nanoTime();
this.data = data;
}
public long getActiveTime() {
return activeTime;
}
public T getData() {
return data;
}
// 按照剩余時間進(jìn)行排序
@Override
public int compareTo(Delayed o) {
// 訂單剩余時間-當(dāng)前傳入的時間= 實際剩余時間(單位納秒)
long d = getDelay(TimeUnit.NANOSECONDS) - o.getDelay(TimeUnit.NANOSECONDS);
// 根據(jù)剩余時間判斷等于0 返回1 不等于0
// 有可能大于0 有可能小于0 大于0返回1 小于返回-1
return (d == 0) ? 0 : ((d > 0) ? 1 : -1);
}
// 獲取剩余時間
@Override
public long getDelay(TimeUnit unit) {
// 剩余時間= 到期時間-當(dāng)前系統(tǒng)時間,系統(tǒng)一般是納秒級的,所以這里做一次轉(zhuǎn)換
long d = unit.convert(activeTime-System.nanoTime(), TimeUnit.NANOSECONDS);
return d;
}
}
方法3:分布式場景(mq隊列)
使用mq隊列,消費消息。如果消息到達(dá)30分鐘沒有付款,那么就取消文章來源:http://www.zghlxwxcb.cn/news/detail-820794.html
方法4:分布式場景(redis)
使用redis商品下單,設(shè)置過期時間 30分鐘,并且寫一個redis監(jiān)聽器,監(jiān)聽過期需要操作的key,然后判單是否過期文章來源地址http://www.zghlxwxcb.cn/news/detail-820794.html
到了這里,關(guān)于商城系統(tǒng)中30分鐘未付款自動取消訂單怎么實現(xiàn)(簡單幾種方法)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!