企业级 Java 后端实战:Spring Boot + MyBatis 实现幂等下单接口

举报
yd_216727992 发表于 2026/11/10 09:46:26 2026/11/10
【摘要】 本文介绍企业级 Java 下单接口的幂等落地方案:前端防抖、幂等 Token 与数据库唯一索引分层设防,含 Spring Boot + MyBatis 核心代码、建表要点与验收用例。

本文以一个企业级下单接口为例,介绍如何用 Spring Boot + MyBatis 落地接口幂等。文中出现的金额、并发数、超时时间等均为示例数值,请按实际业务调整,切勿直接照抄到生产配置。

一、为什么下单接口需要幂等

用户点击提交后网络抖动、前端重复点击、网关自动重试,都可能让同一笔订单被创建两次。幂等的含义是:同一个请求被执行多次,与执行一次的效果相同。对下单、支付、退款这类涉及资金的接口,幂等不是可选项,而是上线前必须通过的验收项。

二、三种常见方案对比

方案 适用场景 主要缺点
前端防抖 + 提交锁 防用户手抖重复点击 防不住网络重试和网关重发
幂等 Token 下单、支付等写操作 需要额外存储 Token,流程稍复杂
数据库唯一索引 所有写操作 只能兜底,报错后需要业务层再做友好处理

生产上通常把三者组合使用:前端防抖挡住大部分重复,Token 做主防线,唯一索引做最后一道兜底。

三、落地方案:Token + 唯一索引双保险

流程如下:

  1. 用户进入下单页,后端发放一个一次性幂等 Token,示例 TTL 为 15 分钟(示例数值)。
  2. 提交订单时前端把 Token 放在请求头 Idempotency-Key 中一起发送。
  3. 服务端先校验并删除 Token:Token 不存在或已使用,直接返回首次执行的结果。
  4. 写入订单时,订单号字段建唯一索引,即使 Token 校验被绕过也不会产生重复订单。

四、核心代码

1. 校验 Token 的拦截器

@Component
public class IdempotencyInterceptor implements HandlerInterceptor {

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response,
                             Object handler) {
        String key = request.getHeader("Idempotency-Key");
        if (key == null || key.isBlank()) {
            throw new IllegalArgumentException("缺少幂等键 Idempotency-Key");
        }
        // 原子删除:删成功说明是首次请求,删失败说明已处理过
        Boolean first = redisTemplate.delete("idem:" + key);
        if (Boolean.FALSE.equals(first)) {
            throw new DuplicateRequestException("请求已处理,请勿重复提交");
        }
        return true;
    }
}

2. 下单服务:唯一索引兜底

@Service
public class OrderService {

    @Autowired
    private OrderMapper orderMapper;

    @Transactional(rollbackFor = Exception.class)
    public Order createOrder(OrderCreateRequest req) {
        Order order = new Order();
        // 订单号由业务规则生成,例如日期 + 序列,库表上建唯一索引
        order.setOrderNo(generateOrderNo());
        // 示例:订单金额 99.00 元为示例数值,实际金额以后端按商品与优惠规则计算为准
        order.setAmount(req.getAmount());
        order.setStatus(OrderStatus.CREATED);
        orderMapper.insert(order);
        return order;
    }
}

对应的建表语句要点:

CREATE TABLE orders (
  id BIGINT PRIMARY KEY AUTO_INCREMENT,
  order_no VARCHAR(32) NOT NULL,
  amount DECIMAL(10, 2) NOT NULL,
  status VARCHAR(16) NOT NULL,
  UNIQUE KEY uk_order_no (order_no)
);

五、测试与验收

上线前建议至少覆盖这三个用例(括号内为示例数值):

  1. 同一个 Token 连续提交 5 次,只生成 1 笔订单,其余请求返回首次执行的结果。
  2. 并发测试:示例并发 200(示例数值)下压测下单接口,订单数与请求去重后一致,无重复订单。
  3. Token 过期测试:Token 超过示例 TTL 15 分钟(示例数值)后提交,接口应明确拒绝,而不是静默成功。

六、容易踩的坑

  1. 只做前端防抖:网络层和网关的重试前端看不到,服务端必须有自己的防线。
  2. Token 校验与写库不在同一事务边界内考虑:校验通过后进程崩溃,Token 已删、订单未落库,重试会被误判为重复,关键业务要做好对账补偿。
  3. 忘记建唯一索引:Token 机制一旦出问题,没有任何兜底。
  4. 幂等键粒度不当:按“用户 + 商品”做键会误拦截用户正常买两件同款;按“一次提交”生成唯一键才合理。

七、总结

企业级后端做幂等,核心思路是分层设防:前端防抖减少重复,幂等 Token 做主防线,数据库唯一索引做最终兜底,再配上过期测试和对账补偿。把这些做到位,下单接口才能在网络抖动和重试面前保持“只建一单”的承诺。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。