ThinkPHP购物车核心是数据存储、并发控制、登录态绑定与库存校验;未登录用带IP前缀的session,登录后立即合并至user_cart表并清空session,且须在(user_id,sku_id)建唯一索引防重复。

ThinkPHP 实现购物车接口,核心不是“写个 API 就完事”,而是得先想清楚:数据存在哪、并发怎么扛、登录态怎么绑定、库存怎么校验。直接用 session 存简单购物车可以,但一上生产、加优惠券、做结算、接订单,session 就会出问题——比如用户换设备就丢购物车,或者多开浏览器导致数据不一致。
购物车数据该存 Session 还是数据库?
未登录用户用 session 存最轻量,但必须加前缀隔离(避免和其他模块冲突),且要设好过期时间;一旦用户登录,必须立刻把 session 里的商品合并进用户专属的购物车表,并清空 session。别想着“等用户下单再同步”,中间任何一步刷新或跳转都可能丢数据。
- 未登录场景:用
$this->request->session()->set('cart_'.$ip, $data),其中$ip防止多人共用同一 session_id - 已登录场景:必须走
user_cart表,字段至少含user_id、goods_id、sku_id、quantity、updated_at - 别用
cookie存购物车 —— 容易被篡改、有大小限制、不安全
添加商品时如何防止重复插入或数量错乱?
常见错误是“查一遍、没就插、有就更新”,但并发下两条请求同时查到“不存在”,结果插入两条重复记录。正确做法是用唯一索引 + INSERT ... ON DUPLICATE KEY UPDATE,或事务中先 SELECT ... FOR UPDATE 锁行。
- 数据库表必须在
(user_id, sku_id)上建唯一联合索引 - ThinkPHP 6+ 推荐用
Db::table('user_cart')->insert([...])配合ignore(true)或原生 SQL 的ON DUPLICATE KEY UPDATE - 如果用模型操作,
save()前务必调用where(['user_id' => $uid, 'sku_id' => $sid])->find()判断是否存在,再决定是save()还是update()
为什么 getCartList 接口总返回旧数据?缓存和事务没对齐
添加/修改后立即查列表却看不到最新结果,大概率是数据库事务还没提交,而查询走了缓存(比如 Redis 缓存了整个购物车列表),或者用了 read_master 但读库延迟。更隐蔽的是:你在事务里 insert,但查询没在同一个事务里,读不到未提交数据。
立即学习“PHP免费学习笔记(深入)”;
- 购物车列表接口禁用全局查询缓存,尤其不能缓存到 Redis 里——它变太频繁
- 所有写操作(add/update/del)结束后,主动清除对应用户的缓存键,例如
cache('cart_'.$uid, null) - 如果用了主从分离,确保读写都在主库,或明确加
->master()强制走主库查询
真正难的不是写几个接口,而是当用户一边加购一边切微信小程序、一边在 H5 页面删商品、一边客服后台强制改库存时,购物车状态还能对得上。这时候 session 和数据库的边界、事务隔离级别、乐观锁字段(比如加个 version)、以及前端防重复提交,一个都不能少。



















