laravel电商系统核心在于数据一致性、状态流转与边界隔离。商品需拆分products、product_variants、inventory三表;购物车登录合并须事务处理并清空session;订单创建应封装为原子化job;支付回调必须独立验签并异步更新。

直接说结论:Laravel电商系统的核心模块不是堆功能,而是围绕数据一致性、状态流转和边界隔离来设计。商品、购物车、订单这三块一旦耦合过紧,后续加促销、多仓库、预售就会反复推翻重写。
商品模型必须分离规格与库存
常见错误是把 color、size、sku 全塞进 products 表,导致一个商品变体就要新增一条主记录。实际应拆成 products(基础信息)、product_variants(规格组合)、inventory(库存快照)三张表。
这样做的好处是:促销可按 variant_id 精准投放;库存扣减走 inventory 表的行级锁,避免超卖;后台批量导入时,只需维护 product_variants 关联关系,不用重复填商品标题、描述。
示例迁移命令:php artisan make:migration create_product_variants_table,注意在 up() 中给 product_id 和 sku 加联合唯一索引。
购物车不能只依赖 session 或数据库单表
用户未登录时用 session 存临时购物车,登录后必须合并到用户专属购物车表(如 carts),且要处理冲突:同一 variant_id 在两边都存在时,数量取最大值而非覆盖。
容易踩的坑:
- 没做合并去重,导致用户登录后商品重复出现
- 合并时没校验库存,把已售罄的临时项也迁过去了
- 没清空 session 购物车,下次访客模式又加载出旧数据
关键逻辑在 CartController@loginMerge() 里,建议用事务包裹整个合并流程,并在最后调用 session()->forget('guest_cart')。
订单创建必须原子化,且禁止在控制器里写业务逻辑
订单创建失败最常发生在「扣库存 + 写订单 + 发通知」这个链路中某一步出错,比如库存扣成功了但订单写入失败,结果用户收不到下单成功页,后台却少了一件库存。
正确做法是把整个流程封装进一个 Job 类,例如 ProcessOrderJob,并在 handle() 中用 DB 事务包住所有操作。失败时自动重试最多 3 次,超过则标记为 failed 状态并触发告警。
别在 OrderController@store() 里直接调用 DB::transaction() —— 那会让控制器承担太多职责,也难以测试和复用。
支付回调必须独立验证,不能信前端传来的任何参数
PayPal 或微信支付回调里,order_id、amount、status 这些字段全不可信。必须用支付平台提供的签名机制(如 paypal/verify-webhook-signature 或微信的 calculateHmac)验证原始请求来源,再查本地订单是否存在、金额是否匹配、状态是否为 pending。
漏掉任意一环,就可能被恶意构造回调篡改订单状态。更稳妥的做法是:回调只更新 payment_logs 表,再由队列 Job 异步核对并更新订单状态,避免阻塞 HTTP 响应。
真正难的不是写完这些模块,而是让它们之间的交接点足够薄、足够可测——比如购物车合并后怎么通知订单服务重新计算总价,这种胶水逻辑最容易随需求变更而腐化。











