订单核心模块:下单流程、状态机、防重下单设计
订单核心模块下单流程、状态机、防重下单设计如果说购物车是开门快乐拿那订单就是关门老实付。订单模块承载着从结算到支付的完整链路也是整个系统中并发压力最大、数据一致性要求最高的模块。一、订单在无人售货柜中的核心地位整条业务链回顾一下扫码开门 → 拿商品 → 关门 → 生成订单 → 支付 → 完成 ↑ 订单中心订单是交易的载体它的职责包括记录谁在哪个柜子买了什么驱动库存真实扣减触发支付流程作为售后凭证退款、申诉、对账二、订单状态机设计一个健壮的状态机是订单模块的灵魂┌──────────────┐ │ PENDING │ ← 关门后等待设备上报最终清单 └──────┬───────┘ │ 设备上报完成 ▼ ┌──────────────┐ ┌────────│ UNPAID │ ← 已生成待支付 │ └──────┬───────┘ │ │ 用户发起支付 │ ▼ │ ┌──────────────┐ │ ┌────│ PAID │────┐ │ │ └──────────────┘ │ │ │ 超时/手动取消 │ 支付成功 │ ▼ ▼ │ ┌──────────┐ ┌──────────────┐ │ │ CANCELLED │ │ COMPLETED │ │ └──────────┘ └──────┬───────┘ │ │ 用户申请退款 │ ▼ │ ┌──────────────┐ └─────────────────────│ REFUNDED │ └──────────────┘6个状态详解状态含义触发事件允许的操作PENDING待结算关门事件等待设备清单UNPAID待支付设备清单确认发起支付 / 取消PAID已支付支付成功完成 / 退款COMPLETED已完成系统确认仅查看CANCELLED已取消超时/手动取消仅查看REFUNDED已退款退款成功仅查看publicenumOrderStatus{PENDING(0,待结算),UNPAID(1,待支付),PAID(2,已支付),COMPLETED(3,已完成),CANCELLED(4,已取消),REFUNDED(5,已退款);// 允许的状态流转白名单static{transitionsMap.of(PENDING,Set.of(UNPAID,CANCELLED),UNPAID,Set.of(PAID,CANCELLED),PAID,Set.of(COMPLETED,REFUNDED,CANCELLED),COMPLETED,Set.of(REFUNDED),CANCELLED,Set.of(),REFUNDED,Set.of());}}三、订单表结构CREATETABLEorder_info(idbigintNOTNULLCOMMENT订单ID(雪花算法),order_novarchar(32)NOTNULLCOMMENT订单号(展示用),user_idbigintNOTNULLCOMMENT用户ID,device_idbigintNOTNULLCOMMENT设备ID,cart_idbigintNOTNULLCOMMENT关联购物车ID,command_idvarchar(64)NOTNULLCOMMENT设备指令ID(防重),statustinyintNOTNULLDEFAULT0COMMENT状态0-待结算 1-待支付 2-已支付 3-已完成 4-已取消 5-已退款,total_amountdecimal(10,2)NOTNULLCOMMENT订单总金额,discount_amountdecimal(10,2)DEFAULT0.00COMMENT优惠金额,pay_amountdecimal(10,2)NOTNULLCOMMENT实付金额,pay_timedatetimeDEFAULTNULLCOMMENT支付时间,pay_channelvarchar(20)DEFAULTNULLCOMMENT支付渠道WECHAT/ALIPAY,transaction_idvarchar(64)DEFAULTNULLCOMMENT第三方交易流水号,cancel_timedatetimeDEFAULTNULLCOMMENT取消时间,cancel_reasonvarchar(100)DEFAULTNULLCOMMENT取消原因,expire_timedatetimeNOTNULLCOMMENT支付超时时间(创建后30分钟),create_timedatetimeNOTNULLDEFAULTCURRENT_TIMESTAMP,update_timedatetimeNOTNULLDEFAULTCURRENT_TIMESTAMPONUPDATECURRENT_TIMESTAMP,PRIMARYKEY(id),UNIQUEKEYuk_order_no(order_no),UNIQUEKEYuk_command_id(command_id),UNIQUEKEYuk_cart_id(cart_id),KEYidx_user_id_create_time(user_id,create_time),KEYidx_status_expire(status,expire_time))ENGINEInnoDBDEFAULTCHARSETutf8mb4COMMENT订单表;CREATETABLEorder_item(idbigintNOTNULL,order_idbigintNOTNULLCOMMENT订单ID,product_idbigintNOTNULLCOMMENT商品ID,product_namevarchar(100)NOTNULLCOMMENT商品名称(快照),product_imagevarchar(500)DEFAULTNULLCOMMENT商品图片(快照),pricedecimal(10,2)NOTNULLCOMMENT单价,quantityintNOTNULLCOMMENT数量,subtotaldecimal(10,2)NOTNULLCOMMENT小计,PRIMARYKEY(id),KEYidx_order_id(order_id))ENGINEInnoDBDEFAULTCHARSETutf8mb4COMMENT订单明细表;设计要点command_id 唯一约束防重下单的基石cart_id 唯一约束一个购物车只能生成一个订单order_item 中存商品快照订单生成后商品信息可能被修改快照保证历史数据准确expire_time支付超时时间定时任务扫描使用四、下单流程完整实现ServicepublicclassOrderService{AutowiredprivateOrderMapperorderMapper;AutowiredprivateOrderItemMapperorderItemMapper;AutowiredprivateStockFeignClientstockFeignClient;AutowiredprivateProductFeignClientproductFeignClient;/** * 从购物车生成订单 */GlobalTransactional// Seata 分布式事务publicOrderResultcreateOrderFromCart(Cartcart){StringcommandIdcart.getCommandId();// 设备关门指令ID// 防重检查 OrderexistingorderMapper.selectOne(newLambdaQueryWrapperOrder().eq(Order::getCommandId,commandId));if(existing!null){log.warn(重复下单commandId{}返回已有订单,commandId);returnbuildResult(existing);}// 生成订单号 StringorderNoSnowflakeUtil.nextStr();// 创建订单 OrderordernewOrder();order.setId(SnowflakeUtil.nextId());order.setOrderNo(orderNo);order.setUserId(cart.getUserId());order.setDeviceId(cart.getDeviceId());order.setCartId(cart.getId());order.setCommandId(commandId);order.setStatus(OrderStatus.PENDING.getCode());// 解析购物车中的商品ListCartItemcartItemsJSONUtil.toList(cart.getItems().toString(),CartItem.class);BigDecimaltotalAmountBigDecimal.ZERO;for(CartItemitem:cartItems){// 获取商品最新价格防止购物车开启期间价格变更ProductproductproductFeignClient.getById(item.getProductId()).getData();BigDecimalunitPriceproduct.getPrice();BigDecimalsubtotalunitPrice.multiply(BigDecimal.valueOf(item.getQuantity()));totalAmounttotalAmount.add(subtotal);// 写入订单明细OrderItemorderItemnewOrderItem();orderItem.setId(SnowflakeUtil.nextId());orderItem.setOrderId(order.getId());orderItem.setProductId(item.getProductId());orderItem.setProductName(product.getName());orderItem.setProductImage(product.getMainImage());orderItem.setPrice(unitPrice);orderItem.setQuantity(item.getQuantity());orderItem.setSubtotal(subtotal);orderItemMapper.insert(orderItem);}order.setTotalAmount(totalAmount);order.setPayAmount(totalAmount);order.setStatus(OrderStatus.UNPAID.getCode());order.setExpireTime(LocalDateTime.now().plusMinutes(30));// 30分钟支付窗口orderMapper.insert(order);// 扣减真实库存Feign调用库存服务DeductStockDTOdeductDTOnewDeductStockDTO();deductDTO.setDeviceId(cart.getDeviceId());deductDTO.setOrderId(order.getId());deductDTO.setItems(cartItems.stream().map(i-{DeductStockDTO.ItemDTOdtonewDeductStockDTO.ItemDTO();dto.setProductId(i.getProductId());dto.setQuantity(i.getQuantity());returndto;}).collect(Collectors.toList()));stockFeignClient.deduct(deductDTO);// 更新购物车状态 cart.setStatus(DONE);cart.setOrderId(order.getId());cartMapper.updateById(cart);log.info(订单创建成功: orderNo{}, amount{}, items{},orderNo,totalAmount,cartItems.size());returnbuildResult(order);}}五、防重下单设计三层防护第一层commandId 唯一约束设备关门指令commandId是全局唯一的。DB 层设置UNIQUE KEY uk_command_id(command_id)即使网络重试导致重复请求第二个 INSERT 直接报DuplicateKeyException返回已有订单。第二层cartId 唯一约束一个购物车只能对应一个订单。UNIQUE KEY uk_cart_id(cart_id)保证同一购物车不会被两次生成订单。第三层幂等检查publicOrderResultcreateOrGetOrder(StringcommandId){OrderexistingorderMapper.selectByCommandId(commandId);if(existing!null){returnbuildResult(existing);// 已存在直接返回}returnlockAndCreate(commandId);// 加分布式锁后创建}六、雪花算法生成订单号publicclassSnowflakeUtil{privatestaticfinalSnowflakeSNOWFLAKE;static{// workerId 从 Nacos 配置中心读取确保每个实例唯一longworkerIdLong.parseLong(System.getProperty(snowflake.workerId,1));longdatacenterId1L;SNOWFLAKEIdUtil.createSnowflake(workerId,datacenterId);}publicstaticlongnextId(){returnSNOWFLAKE.nextId();}publicstaticStringnextStr(){returnSNOWFLAKE.nextIdStr();}}雪花算法特点全局唯一分布式环境下不会有重复趋势递增64位中高位是时间戳天然有序高性能纯内存计算QPS可达百万级七、订单与库存的分布式事务GlobalTransactional(nameorder_create,timeoutMills300000)publicOrderResultcreateOrderFromCart(Cartcart){// Seata AT 模式自动管理// 1. 本地事务创建订单// 2. Feign 远程调用库存服务扣减库存// 3. 任一环节失败自动回滚所有数据源}Seata AT 模式下GlobalTransactional注解会协调两个本地事务订单库的INSERT和库存库的UPDATE。回滚时自动执行 undo log。八、订单超时关闭Scheduled(fixedDelay60000)// 每分钟执行publicvoidcloseTimeoutOrders(){ListOrdertimeoutOrdersorderMapper.selectList(newLambdaQueryWrapperOrder().eq(Order::getStatus,OrderStatus.UNPAID.getCode()).lt(Order::getExpireTime,LocalDateTime.now()));for(Orderorder:timeoutOrders){try{cancelOrder(order.getId(),支付超时系统自动取消);}catch(Exceptione){log.error(超时关闭订单失败: {},order.getId(),e);}}}TransactionalpublicvoidcancelOrder(LongorderId,Stringreason){OrderorderorderMapper.selectById(orderId);if(!OrderStatus.canTransition(order.getStatus(),OrderStatus.CANCELLED)){thrownewBusinessException(当前订单状态不允许取消);}order.setStatus(OrderStatus.CANCELLED.getCode());order.setCancelTime(LocalDateTime.now());order.setCancelReason(reason);orderMapper.updateById(order);// 释放库存stockFeignClient.release(order.getDeviceId(),order.getId());}九、支付回调幂等性PostMapping(/callback/wechat)publicStringwechatPayCallback(RequestBodyWechatPayNotifyDTOdto){StringtransactionIddto.getTransactionId();// 幂等性已处理过直接返回成功避免微信重复回调OrderorderorderMapper.selectByTransactionId(transactionId);if(order!nullorder.getStatus()OrderStatus.PAID.getCode()){returnsuccess();// 重复通知直接返回成功}// 验签 更新订单状态if(!wechatPayService.verify(dto)){returnfail(验签失败);}orderorderMapper.selectByOrderNo(dto.getOutTradeNo());order.setStatus(OrderStatus.PAID.getCode());order.setPayTime(LocalDateTime.now());order.setPayChannel(WECHAT);order.setTransactionId(transactionId);orderMapper.updateById(order);// 订单完成order.setStatus(OrderStatus.COMPLETED.getCode());orderMapper.updateById(order);returnsuccess();}十、订单幂等性总结环节幂等机制实现下单commandId 唯一约束DB UNIQUE KEY库存扣减订单已存在则跳过先查后扣支付回调transactionId 去重DB 状态判断退款refundId 唯一约束DB UNIQUE KEY订单模块是整个系统中最后一道门。状态机设计清楚了防重和幂等做扎实了后面就是关门→付钱→走人的愉快体验。反之如果这里出了问题——重复扣款、库存超卖、支付成功订单没更新——那就是线上事故级别的灾难。

相关新闻

存储芯片成半导体增长引擎,开发者如何应对内存与存储性能挑战?

存储芯片成半导体增长引擎,开发者如何应对内存与存储性能挑战?

大家好,我是专注于科技产业趋势分析的博主。最近,一份来自权威研究机构Omdia的半导体市场预测报告引发了业界广泛关注。报告指出,到2026年,全球半导体行业营收将迎来高达94.1%的惊人增长,而其中存储芯片(IC…

2026/8/3 3:20:35阅读更多 →
企业 Agent 死在试点:88% 背后的四大死因与 5 信号自查框架(技术视角)

企业 Agent 死在试点:88% 背后的四大死因与 5 信号自查框架(技术视角)

一、核心判断:试点 ≠ 产品 2026 年企业圈最热的关键词是 Agentic AI,但数据给了冷水: KPMG 2026-06-30 调研:88% 企业已启动 Agentic AI 试点,仅 24% 真正见回报;MIT:约 95% 已上线 GenAI 试点…

2026/8/3 3:18:35阅读更多 →
5分钟构建智能狗品种识别系统:从模型训练到多端部署实战

5分钟构建智能狗品种识别系统:从模型训练到多端部署实战

5分钟构建智能狗品种识别系统:从模型训练到多端部署实战 【免费下载链接】AIDog 一款从图片识别狗的类别的应用,包括Android版和微信小程序版。 项目地址: https://gitcode.com/gh_mirrors/ai/AIDog AIDog是一个基于TensorFlow的智能狗品种识别开…

2026/8/3 3:18:35阅读更多 →
SpringBoot定时任务详解:@Scheduled与@Schedules对比与实践

SpringBoot定时任务详解:@Scheduled与@Schedules对比与实践

1. SpringBoot定时任务基础解析在SpringBoot应用中实现定时任务功能时,Scheduled和Schedules这两个注解经常让开发者产生混淆。作为Spring框架定时任务模块的核心注解,它们虽然名称相似,但在使用场景和功能特性上存在本质区别。定时任务在现代…

2026/8/3 4:25:02阅读更多 →
$?,$(),[],[[]],=,==,exit

$?,$(),[],[[]],=,==,exit

文章目录 return:退出函数,回到调用函数的地方。exit:退出整个 Shell 脚本。$? 表示最近执行完成的那一条命令的退出状态/退出码(exit status 或 exit code函数退出码脚本退出码Shell 规定:如果赋值中包含命令替换 $(…

2026/8/3 4:25:02阅读更多 →
企业认知升维:余行补位方法论解析与实践

企业认知升维:余行补位方法论解析与实践

1. 项目概述:认知升维的时代需求中国企业正面临从"实力竞争"向"思想竞争"转型的关键节点。过去十年间,我们见证了太多依靠资本扩张、规模效应取胜的企业在遭遇行业变革时陷入困境。专知智库提出的"余行补位"方法论&#x…

2026/8/3 4:25:02阅读更多 →
Java游戏开发新选择:FXGL轻量级引擎入门与实践指南

Java游戏开发新选择:FXGL轻量级引擎入门与实践指南

1. 项目概述:为什么是FXGL?如果你是一名Java开发者,对游戏开发感兴趣,但又对Unity、Unreal Engine这类庞然大物感到望而却步,或者你只是想快速验证一个2D游戏的点子,那么FXGL很可能就是你一直在寻找的那个“…

2026/8/3 4:25:02阅读更多 →
2026,千问元宝静悄悄

2026,千问元宝静悄悄

作者 | 辰纹来源 | 洞见新研社2026年过半,AI赛道的喧嚣程度不减,但聚光灯下的面孔已经悄然换了一轮。就拿最近的行业热点来看,7月中旬,月之暗面发布Kimi K3,参数规模高达2.8万亿,成为全球参数最大的开源模型…

2026/8/3 4:25:02阅读更多 →
空间视频与屏幕色准分析:从技术原理到Python量化实践

空间视频与屏幕色准分析:从技术原理到Python量化实践

1. 项目背景与核心概念最近在科技数码圈,关于苹果Vision Pro录制空间视频的讨论热度不减,尤其是其与普通手机视频的差异,以及是否值得为这一功能购买二手设备,成为了许多开发者和科技爱好者关注的焦点。与此同时,三星G…

2026/8/3 4:23:02阅读更多 →
MATLAB xcorr函数详解:从互相关原理到四大实战应用

MATLAB xcorr函数详解:从互相关原理到四大实战应用

1. 从一次信号“找茬”说起:为什么我们需要互相关几年前,我在处理一组声学传感器数据时遇到了一个棘手的问题。我有两个麦克风记录了一段相同的音频信号,理论上它们接收到的声音波形应该非常相似,只是由于麦克风位置不同&#xff…

2026/8/3 0:29:53阅读更多 →
限时公开!某头部SaaS公司内部AI模板工厂架构文档(含5类行业模板源码+性能压测报告)

限时公开!某头部SaaS公司内部AI模板工厂架构文档(含5类行业模板源码+性能压测报告)

更多请点击: https://intelliparadigm.com 第一章:AI模板批量生成的核心价值与落地全景 AI模板批量生成正从实验性工具演进为现代软件工程的关键基础设施。它通过语义理解、上下文感知与结构化约束,将重复性高、模式明确的代码/文档/配置生成…

2026/8/3 0:33:53阅读更多 →
如何快速找回消失的网页:Web Archives浏览器扩展终极指南

如何快速找回消失的网页:Web Archives浏览器扩展终极指南

如何快速找回消失的网页:Web Archives浏览器扩展终极指南 【免费下载链接】web-archives Browser extension for viewing archived and cached versions of web pages, available for Chrome, Edge and Safari 项目地址: https://gitcode.com/gh_mirrors/we/web-a…

2026/8/3 0:20:37阅读更多 →
3个让你工作效率翻倍的Umi-OCR实战技巧:免费离线文字识别完全指南

3个让你工作效率翻倍的Umi-OCR实战技巧:免费离线文字识别完全指南

3个让你工作效率翻倍的Umi-OCR实战技巧:免费离线文字识别完全指南 【免费下载链接】Umi-OCR OCR software, free and offline. 开源、免费的离线OCR软件。支持截屏/批量导入图片,PDF文档识别,排除水印/页眉页脚,扫描/生成二维码。…

2026/8/3 0:00:32阅读更多 →
[具身智能-181]:PC+服务器+具身机器人:构建具身智能从仿真到量产的闭环迭代混合架构

[具身智能-181]:PC+服务器+具身机器人:构建具身智能从仿真到量产的闭环迭代混合架构

PC服务器具身机器人:构建具身智能从仿真到量产的闭环迭代混合架构一、前言:具身智能需要“混合算力闭环系统”传统人工智能依赖云端静态数据集训练,不具备物理交互能力,无法适应真实世界的不确定性。具身智能(Embodied…

2026/8/3 0:00:32阅读更多 →
[具身智能-181]:大分布式通信模型对比:看懂为什么 DDS 是 ROS2 底层通信最优解

[具身智能-181]:大分布式通信模型对比:看懂为什么 DDS 是 ROS2 底层通信最优解

前言构建机器人、具身智能这类分布式实时系统,通信底座直接决定整套系统的实时性、容错性、组网能力。分布式领域长期存在 4 类经典通信架构:点对点模式、Broker 中间代理模式、广播模式、以数据为中心(DDS)模式。很多开发者疑惑&…

2026/8/3 0:00:32阅读更多 →
无损视频剪辑终极指南:如何实现快速高效的多媒体处理

无损视频剪辑终极指南:如何实现快速高效的多媒体处理

无损视频剪辑终极指南:如何实现快速高效的多媒体处理 【免费下载链接】lossless-cut The swiss army knife of lossless video/audio editing 项目地址: https://gitcode.com/gh_mirrors/lo/lossless-cut 在数字媒体创作领域,视频编辑处理的质量损…

2026/8/3 2:32:59阅读更多 →
AI辅助本科论文写作:8大工具评测与高效使用指南

AI辅助本科论文写作:8大工具评测与高效使用指南

1. 本科生论文写作的AI辅助现状本科毕业论文是每个大学生必须跨越的一道坎。记得我当年写论文时,光是文献检索就花了整整两周时间,打印的参考文献堆满了半个书桌。如今AI技术的发展为学术写作带来了革命性变化,合理使用这些工具可以节省80%以…

2026/8/3 2:33:01阅读更多 →
如何快速配置大麦自动抢票系统:从零开始搭建Python抢票助手

如何快速配置大麦自动抢票系统:从零开始搭建Python抢票助手

如何快速配置大麦自动抢票系统:从零开始搭建Python抢票助手 【免费下载链接】ticket-purchase 大麦自动抢票,支持人员、城市、日期场次、价格选择 项目地址: https://gitcode.com/GitHub_Trending/ti/ticket-purchase 还在为抢不到热门演唱会门票…

2026/8/3 2:33:04阅读更多 →