下单不付款库存被占?Flask定时任务自动取消超时订单+回滚库存

发布时间:2026/11/5 6:48:03

下单不付款库存被占?Flask定时任务自动取消超时订单+回滚库存 ⚠️免责声明本文项目仅供技术学习参考严禁直接作为毕业设计或课程作业提交。请结合自身需求进行二次开发与创新学术诚信从我做起 这事服务端不干库存就锁死了先还原一个事故现场。你的商城里有一件爆款卫衣库存30件。有人一口气点了3件下单然后……不付款了。订单挂在待支付库存照05篇讲的逻辑已经扣掉了。真正想买的人进来一看库存不足。那超时没人付的单谁来取消答案在后台。被取消的订单在管理后台长这样图后台订单管理里可以直接筛出「已取消」的订单图订单被取消后用户在订单详情看到的最终状态半小时后他还没付。一小时后还没付。3件货被一个永远不会付的订单锁死了。这篇解决的就是这件事下单超过一定时间没付款系统自动取消订单把库存还回去。核心动作就两个定时扫描 取消回滚。取消回滚05篇写过这篇的重点是谁来定时、怎么定时、定时任务怎么不出事。一、为什么不能靠前端倒计时很多人第一反应在订单页放个30分钟倒计时时间到了发个请求取消订单。能跑。但只在用户还开着这个页面的时候能跑。用户关了浏览器、断了网、切了App、直接杀进程倒计时就没了取消请求永远不会发。库存照样锁着。**前端倒计时只能用来显示不能用来执行。**真正的超时判断必须发生在服务端而且不能依赖任何用户在线。这是这一篇最先要想明白的事。二、三种服务端方案毕设选哪个方案原理优点缺点定时扫表每隔几分钟查一次超时订单简单、好理解、好答辩有几分钟误差Redis过期监听下单时写一个30分钟过期的key过期触发取消准时、性能好要装Redis通知可能丢配置麻烦延迟队列死信队列消息进队列延迟30分钟再消费工业级方案对毕设太重毕设选型定时扫表 APScheduler。理由很实在不增加新组件、代码量小、逻辑一眼能看懂、答辩好讲。几分钟的误差对毕设完全无所谓真要纠结把扫描间隔设短一点就行。Redis方案我会在最后提一嘴作为我了解更优解的答辩储备。三、先给订单表加一个字段判断超时需要知道这单是什么时候下的。01篇建表时本来就有created_at可以直接拿它算。但更规范的做法是单独留一个取消时间方便以后查这单是用户取消的还是系统取消的ALTERTABLEorder_infoADDCOLUMNcancel_timedatetimeDEFAULTNULLCOMMENT取消时间,ADDCOLUMNcancel_typetinyintDEFAULTNULLCOMMENT取消类型1用户取消 2系统超时;超时线就用created_at算当前时间 - 下单时间 30分钟且状态还是待支付status0就是超时单。四、写定时任务扫描 批量回滚先装库pipinstallapscheduler新建一个scheduler.py专门放定时任务fromdatetimeimportdatetime,timedeltafromapscheduler.schedulers.backgroundimportBackgroundScheduler# 后台调度器不阻塞 Flask 主线程schedulerBackgroundScheduler(timezoneAsia/Shanghai)# 超时时间分钟ORDER_TIMEOUT_MINUTES30# 每次最多处理多少单防止一次捞出几万条把库打挂BATCH_SIZE100defcancel_timeout_orders(app):取消超时未支付订单并回滚库存# 定时任务不在请求上下文里要手动推入 app 上下文才能用 dbwithapp.app_context():fromapp.modelsimportOrderInfo,OrderItem,Goods,db deadlinedatetime.now()-timedelta(minutesORDER_TIMEOUT_MINUTES)# 关键条件状态待支付 且 下单时间早于截止线timeout_ordersOrderInfo.query.filter(OrderInfo.status0,OrderInfo.created_atdeadline).order_by(OrderInfo.created_at.asc()).limit(BATCH_SIZE).all()ifnottimeout_orders:returntry:fororderintimeout_orders:# 再判一次状态防止扫描到处理的间隙用户刚好付了款幂等iforder.status!0:continue# 1. 回滚库存加回库存减回销量itemsOrderItem.query.filter_by(order_idorder.id).all()foriteminitems:Goods.query.filter_by(iditem.goods_id).update({stock:Goods.stockitem.quantity,sales:Goods.sales-item.quantity})# 2. 订单置为已取消标记是系统取消的order.status4order.cancel_timedatetime.now()order.cancel_type2db.session.commit()app.logger.info(f定时任务取消超时订单{len(timeout_orders)}单)exceptExceptionase:db.session.rollback()app.logger.error(f超时订单处理失败{e})definit_scheduler(app):注册任务并启动# interval固定间隔。每5分钟跑一次scheduler.add_job(funclambda:cancel_timeout_orders(app),triggerinterval,minutes5,idcancel_timeout_orders,replace_existingTrue)scheduler.start()库存回滚那段和05篇用户手动取消订单是同一套逻辑stock加回去sales减回来。区别只是触发方从用户点击变成了定时任务以及多了一个cancel_type2。五、在应用启动时把任务挂上去在 Flask 应用工厂一般是app/__init__.py里启动调度器from.schedulerimportinit_schedulerdefcreate_app():appFlask(__name__)# ... 各种初始化、注册蓝图 ...# 生产/正常启动时才开调度器init_scheduler(app)returnapp然后正常flask run或python run.py任务就活了每5分钟醒一次找超时单处理睡回去。六、三个一定会踩的坑坑1debug模式下任务被执行两次这是APScheduler配Flask最经典的坑。Flask的debug模式默认会启动一个reloader 监视进程 一个真正的服务进程等于应用被加载了两次调度器也跟着启动了两个同一批订单可能被处理两遍。两种解决办法任选# 办法一启动时关掉 reloader毕设调试期够用app.run(debugTrue,use_reloaderFalse)# 办法二只在 reloader 的子进程里启动调度器推荐importosdefcreate_app():appFlask(__name__)# ...# 环境变量 WERKZEUG_RUN_MAIN 为 true 说明是真正干活的子进程ifos.environ.get(WERKZEUG_RUN_MAIN)true:init_scheduler(app)returnapp记住一句话调度器在整个系统里只能有一个实例。多实例不光重复跑还可能重复回滚库存。坑2任务里报Working outside of application context定时任务不是HTTP请求没有请求上下文直接用db.session会报错。所以任务函数里必须套一层with app.app_context():上面代码里已经写了。把app传进去就是为了这个。坑3时间别用数据库的NOW()拍脑袋判断超时时当前时间以应用服务器为准datetime.now()下单时间以created_at为准。如果以后上云应用和数据库分属不同机器要确认两边时区一致统一用Asia/Shanghai否则可能出现订单刚下就被判超时或者永远不超时的灵异现象。调度器初始化时传timezoneAsia/Shanghai也是这个原因。七、答辩被追问高并发怎么办定时扫表毕设够用但如果老师追问订单量大了这个方案有什么问题你可以这样答扫表有两个局限一是有轮询间隔取消不够实时二是订单量大了每5分钟全表扫一次会有压力需要加索引、分批处理。更大的系统会用Redis下单时往Redis写一个30分钟过期的key或用ZSetscore存过期时间戳过期/轮询到就发取消消息由消费者去取消订单并回滚库存。这样准时且不给主库添负担。这段一说老师就知道你不是只会写个定时器而是清楚这个方案的边界在哪。另外补一个工程细节取消订单和回滚库存必须在同一个事务里05篇的begin_nested否则可能出现订单取消了、库存没还回去的半吊子状态。上面代码整体commit/rollback就是在保证这一点。定时任务答辩常问这几问1. 为什么不在前端用倒计时自动取消答前端倒计时依赖页面开着用户关浏览器、断网、杀进程就失效了。超时判断必须放在服务端不依赖用户是否在线。前端倒计时只负责给用户看服务端定时任务才负责真正执行。2. 定时任务和用户刚好付款冲突了怎么办答任务在真正改状态前会再查一次订单状态只有仍然是待支付才取消。支付回调那边同样会先判状态。两边都做状态校验保证一笔订单不会被重复处理这就是幂等。3. 取消订单时为什么要回滚库存还要减销量答下单瞬间已经按05篇的逻辑扣了库存、加了销量。订单取消意味着这笔交易没发生库存要加回来销量也要减回去两个字段一起还原数据才一致。而且这两步和订单状态更新在同一个事务里。4. APScheduler 定时任务在debug下为什么会跑两次答Flask debug的reloader会加载两次应用一次监视进程、一次服务进程调度器被启动了两个实例。用use_reloaderFalse或者判断WERKZEUG_RUN_MAIN只在子进程启动都能解决。一句话记牢调度器全局只能有一个。5. 一次查出几十万条超时订单怎么办答用limit分批处理每批比如100条配合created_at索引任务间隔内消化不完下一轮接着处理。同时给status和created_at建联合索引避免全表扫描。超时取消这个功能代码量不大但它考的是一种思维系统得能在什么都没发生的时候自己把残局收拾干净。用户不付款、关掉页面、人间蒸发——这些才是真实世界的常态。好的系统不能假设每一步都有人盯着点。把这一篇和05篇连起来看下单扣库存、付款流转、超时自动释放订单从生到死这一整条就接上了。梅雅达编程笔记原创 · 首发 CSDN · 转载请注明出处
延伸阅读

更多相关文章

2026/11/5 8:48:17

【Java基础知识】字面量与变量

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/11/5 8:48:17

kafka--基础知识点--0

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/11/5 8:48:17

快速上手playwight

在自动化测试和网页爬虫的领域,Selenium 曾是无可争议的霸主。然而,随着现代 Web 应用越来越复杂(大量使用 React、Vue、单页应用等),传统的测试工具开始显得有些力不从心。今天,我们要聊的是由微软开源的现…

2026/11/4 12:38:23

面向复杂光照与角度的宠物面部识别鲁棒性增强策略

在智慧养宠时代,基于视觉的宠物身份识别技术正逐步从实验室走向家庭应用。然而,与配合度高的人类不同,宠物是典型的“非合作生物识别对象”——它们不会主动配合摄像头调整姿态,更无法控制光照环境。实际应用中,我们常…

2026/11/4 21:04:25

SSM电子书店源码全解析:结构、原理、部署与排错实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/11/5 0:02:07

多智能体协作系统架构设计与实践优化

1. 多智能体协作系统的时代背景2017年Transformer架构的诞生彻底改变了人工智能的发展轨迹。当行业还沉浸在单一大模型带来的性能突破时,Anthropic的研究团队已经将目光投向了更前沿的领域——如何让多个智能体通过协作产生超越单体能力的系统智能。这就像交响乐团中…

2026/11/5 0:02:07

大模型系统性入门实战指南:从原理到部署微调的全链路路线

提到大模型的系统性入门,我先说个结论:这事真不难,难的是资料太散、路线太多,今天这个框架明天那个工具,新人很容易在“收藏夹吃灰”和“教程看到一半放弃”之间反复横跳。市面上关于大模型的内容少说有几十万篇&#…

2026/11/5 0:02:07

鸿蒙文件系统沙箱机制解析与开发实践

1. 鸿蒙文件系统沙箱机制深度解析作为一名从Android转向鸿蒙开发的工程师,我深刻体会到文件系统访问是迁移过程中最先遇到的"拦路虎"。在Android上运行良好的文件读写代码,在鸿蒙真机环境却频频出现文件写入后神秘消失或直接抛出权限拒绝错误的…

2026/11/4 12:38:23

面向复杂光照与角度的宠物面部识别鲁棒性增强策略

在智慧养宠时代,基于视觉的宠物身份识别技术正逐步从实验室走向家庭应用。然而,与配合度高的人类不同,宠物是典型的“非合作生物识别对象”——它们不会主动配合摄像头调整姿态,更无法控制光照环境。实际应用中,我们常…

2026/11/4 21:04:25

SSM电子书店源码全解析:结构、原理、部署与排错实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑