laravel商城真实难点在于业务与框架默认行为的冲突:购物车需分阶段存储(游客用带设备指纹的session,登录后合并至数据库+redis缓存)、库存扣减须结合db行锁与redis校验防超卖、支付回调需幂等判断+唯一索引防重+异步通知,核心是保障高并发下状态一致性。

Laravel商城不是“照着教程敲完就能上线”的项目,它卡在真实业务逻辑和框架默认行为的缝隙里——比如购物车数据该存数据库还是 Session、库存扣减怎么防超卖、订单状态机怎么避免脏写。直接上手跑通 demo 很容易,但一加并发、一接支付、一做退款,就暴露底层设计缺陷。
购物车用 Session 还是数据库?
新手常把 Cart 模型直接关联用户后全走数据库,结果登录态失效或游客变会员时购物车丢了;也有人全靠 session()->put() 存数组,结果商品属性变更(如规格图、价格)不自动同步。
- 游客阶段必须用
session,且 key 要带设备指纹(如cart_'.md5($_SERVER['HTTP_USER_AGENT']. $_SERVER['REMOTE_ADDR'])),否则同一局域网多人购物会串 - 用户登录后,首次合并时要把 session 里的商品逐条插入
cart表,并清空 session;后续操作全部走数据库 + 缓存(如 Redis 的hgetall cart:{$userId}) - 别在控制器里反复调
Auth::user()->cart,Eloquent 关联查一次就查了整张表,用Cart::where('user_id', auth()->id())->with('goods')->get()更可控
订单创建时库存怎么扣才不超卖?
DB::transaction() 包不住所有情况:如果两个请求同时查到库存为 1,都进事务,最终还是写入两条订单。
- 必须用数据库行锁:
SELECT * FROM goods WHERE id = ? FOR UPDATE,Laravel 写法是Good::where('id', $goodsId)->lockForUpdate()->firstOrFail() - 扣减后立刻检查
$good->stock >= $buyNum,不满足就 throw 异常并 rollback,不能只靠事务自动回滚 - Redis 计数器(
DECRBY goods_stock:{$id} {$num})可作前置校验,但不能替代 DB 行锁——缓存可能滞后,最终以 DB 为准
支付回调里更新订单状态为什么总丢?
常见错误是收到微信/支付宝回调后,直接 Order::find($outTradeNo)->update(['status' => 'paid']),但没做幂等判断,重复通知导致状态被刷回或覆盖。
- 回调入口第一行必须查订单并判断当前
status,仅当为pending时才允许更新,其他状态直接返回 success - 用数据库唯一索引防重:给
order_payments表加联合索引(order_id, trade_no),插入支付记录时用upsert或捕获23000错误码 - 别在回调里触发发短信、发邮件等耗时操作,统一扔进
dispatch(new SendOrderPaidNotification($order))队列
真正难的不是写出来,是让每笔订单在高并发、网络抖动、支付方重推、用户狂点提交的混合压力下,依然保持状态一致。这些地方没有“标准答案”,只有根据你的服务器资源、DB 配置、支付渠道特性做的取舍。











