订单超时

方案一: 定时检测(数据库)

demo

java 复制代码
package com.jysemel.kcnf.todo.job;

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;

@Slf4j
@Component
public class TodoDayWarnJob {


    private static final Map<String, String> accessTokenCache = new HashMap<>();

    // 使用 ReentrantLock
    private final ReentrantLock lock = new ReentrantLock();

    @SneakyThrows
    @Scheduled(cron = "0 0/1 * * * ?")
    public void run() {
        // 获取锁,避免重复执行
        if (lock.tryLock()) {
            log.info("每日待办事项提醒.........");
            try {

            } finally {
                // 释放锁
                lock.unlock();
            }
        } else {
            log.info("任务正在执行中,跳过本次执行");
        }
    }
}

优点和缺点

复制代码
小项目首选、快速
缺点: 耗时、并发问题

方案二: 延迟消息队列

优点和缺点

复制代码
小项目首选、快速
缺点: 耗时、并发问题

方案三: 基于redis过期事件

arduino 复制代码
基于redis过期key
开启 Redis 的过期事件通知(默认关闭)
redis.config.notify-keyspace-events "Ex"

demo

kotlin 复制代码
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.listener.PatternTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;

@Configuration
public class RedisConfig {

    /**
     * 配置 Redis 消息监听容器
     */
    @Bean
    public RedisMessageListenerContainer redisMessageListenerContainer(
            RedisConnectionFactory connectionFactory,
            MessageListenerAdapter listenerAdapter) {

        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);

        // 订阅所有数据库的过期事件
        container.addMessageListener(listenerAdapter, new PatternTopic("__keyevent@*__:expired"));

        return container;
    }

    /**
     * 绑定自定义的监听器
     */
    @Bean
    public MessageListenerAdapter listenerAdapter(OrderExpiredListener listener) {
        return new MessageListenerAdapter(listener);
    }
}



import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.stereotype.Component;

@Component
public class OrderExpiredListener implements MessageListener {

    private static final Logger log = LoggerFactory.getLogger(OrderExpiredListener.class);

    @Override
    public void onMessage(Message message, byte[] pattern) {
        // 获取过期的 key
        String expiredKey = message.toString();
        log.info("收到 Redis 过期事件,key: {}", expiredKey);

        // 判断是否是订单 key(格式 order:{id})
        if (expiredKey != null && expiredKey.startsWith("order:")) {
            String orderId = expiredKey.substring(6); // 去掉 "order:"
            // 执行订单取消逻辑(幂等处理)
            cancelOrder(orderId);
        }
    }

    /**
     * 模拟取消订单:更新订单状态为已取消,打印日志
     */
    private void cancelOrder(String orderId) {
        // 实际项目中可注入 Service 操作数据库
        log.info("订单 {} 超时,已自动取消", orderId);
        // 这里可以调用数据库更新语句,例如 update orders set status = 'CANCELLED' where id = ? and status = 'NEW'
    }
}


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.TimeUnit;

@RestController
@RequestMapping("/order")
public class OrderController {

    @Autowired
    private StringRedisTemplate redisTemplate;

    /**
     * 创建订单,设置过期时间(秒)
     * 例如:http://localhost:8080/order/create?orderId=1001&ttl=10
     */
    @GetMapping("/create")
    public String createOrder(@RequestParam String orderId,
                              @RequestParam(defaultValue = "30") long ttl) {
        // 模拟将订单信息存入数据库,状态为 NEW
        // ...

        // 将订单 ID 存入 Redis,设置过期时间
        String key = "order:" + orderId;
        redisTemplate.opsForValue().set(key, "待支付", ttl, TimeUnit.SECONDS);

        return "订单 " + orderId + " 已创建," + ttl + " 秒后过期自动取消";
    }
}

方案四: 兜底策略

复制代码
幂等性:所有取消操作必须幂等,防止重复取消导致数据错误。
状态检查:执行取消前应检查订单当前状态(如仅当状态为"待支付"时才取消)。
重试机制:对于执行失败的取消操作,应有重试机制(如结合消息队列重试)。
监控告警:对长时间未处理的订单进行监控,及时发现策略失效。
相关推荐
树獭叔叔2 小时前
06-大模型如何"学习":从梯度下降到AdamW优化器
后端·aigc·openai
得鹿2 小时前
MySQL基础架构与存储引擎、索引、事务、锁、日志
后端
程序员飞哥2 小时前
Block科技公司裁员四千人,竟然是因为 AI ?
人工智能·后端·程序员
JavaEdge在掘金2 小时前
Claude Code 直连 Ollama / LM Studio:本地、云端开源模型都能跑
后端
LSTM972 小时前
使用 Python 将 TXT 转换为 PDF (自动分页)
后端
于眠牧北2 小时前
Java开发学习提高效率的辅助软件和插件:一键生成接口文档,AI制作原型等
后端
JordanHaidee3 小时前
Python 中 `if x:` 到底在判断什么?
后端·python
开心就好20253 小时前
不越狱能抓到 HTTPS 吗?在未越狱 iPhone 上抓取 HTTPS
后端·ios
用户908324602733 小时前
Spring Boot + MyBatis-Plus 多租户实战:从数据隔离到权限控制的完整方案
java·后端