首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >游客和登录态的购物车怎么合并不丢不重:合并算法与冲突处理

游客和登录态的购物车怎么合并不丢不重:合并算法与冲突处理

原创
作者头像
用户5598620
发布2026-09-13 11:39:59
发布2026-09-13 11:39:59
410
举报

大部分电商都允许"先逛后登录":用户没登录时把商品加进本地购物车,登录或注册后要把这辆"游客购物车"和账号里已有的"登录购物车"合成一辆。听起来简单,做起来全是边界:同一件商品两边都有怎么办、规格不同算不算同一件、游客选中的状态要不要保留、合并失败如何回滚。本文讲清一套可直接落地的购物车合并方案:先定数据模型,再定合并算法,最后处理冲突和一致性。

一、先想清楚:购物车里到底装了什么

合并出错,很多是因为购物车条目模型太简陋,只存了"商品 id + 数量"。要支持精确合并,一条购物车项至少要包含这些字段:

字段

作用

合并时的意义

sku_id

最小规格单元

判断"是不是同一件"的唯一依据

quantity

数量

冲突时需要决定相加还是覆盖

selected

是否勾选

影响登录后全选/结算状态

added_at

加入时间

排序、以及冲突时的取舍依据

source

来源端

多端合并时追溯来源

最关键的一条:判重必须用 SKU 而不是 SPU。同一款 T 恤(SPU)选了不同颜色尺码(SKU)就是两条独立记录,不能合并成一条;如果用商品 id 判重,会把不同规格错误折叠,数量和规格全乱。

二、游客车与登录车分别存在哪

两辆车的存储位置不同,决定了合并的触发时机:

游客车:存在浏览器本地(localStorage / 本地数据库),按设备隔离,不上账号。 登录车:存在服务端,按用户 id 隔离,跨设备同步。

合并发生在三个时刻:登录成功的瞬间、注册成功的瞬间、以及已登录用户在新设备拉取到本地旧车时。原则是以服务端登录车为基准、把游客车作为增量并进去,合并完成后用结果回写服务端、再清空本地游客车。不要反过来以本地车覆盖服务端,否则用户在另一台设备上已加的商品会丢。

三、核心合并算法:按 SKU 对齐,分三类处理

把两辆车都按 sku_id 建索引,然后遍历并集,每一条只会落入下面三种情况之一:

代码语言:javascript
复制
function mergeCarts(guestItems, serverItems) {
  const map = new Map();
  // 先放服务端车作为基准
  for (const it of serverItems) map.set(it.sku_id, { ...it });
  // 再并入游客车
  for (const g of guestItems) {
    const exist = map.get(g.sku_id);
    if (!exist) {
      map.set(g.sku_id, { ...g });            // 情况A:服务端没有,直接新增
    } else {
      exist.quantity = mergeQuantity(exist, g); // 情况B:同一SKU,数量合并
      exist.selected = exist.selected && g.selected;
    }
  }
  // 情况C:只在服务端、游客车没有的,原样保留(map 基准已含)
  return [...map.values()];
}

三种情况的处理规则:

情况

含义

处理

A 仅游客车有

登录车没这件

直接新增,保留游客的数量与勾选态

B 两边都有同一 SKU

重复加了同规格

数量按策略合并,勾选态取交集

C 仅登录车有

游客没动这件

原样保留,不许丢

情况 B 的"数量怎么合"是产品决策而非纯技术问题,常见两种策略:累加(两边数量相加,符合"我之前加过、刚才又加了一次"的直觉,多数电商采用)和覆盖(以最近一次操作为准)。选定后要全端一致,不能 iOS 累加、安卓覆盖。

四、数量合并的边界:库存、上下架、失效商品

直接把数量相加会撞上现实约束,合并时必须逐 SKU 过一遍校验:

  1. 库存上限:相加后的数量不能超过可售库存和单品限购数,超出则截断到上限,并给用户一条"部分商品已达可购上限"的提示。
  2. 已下架/失效 SKU:游客车里某件商品登录时已经下架,不能直接并进有效列表,应放进"失效商品"分组展示,让用户自己决定移除还是找相似。
  3. 售价变动:购物车一般不锁售价,合并后以服务端最新价为准,不要沿用游客本地缓存的旧价,避免结算价差。
  4. 数量非法:本地数据可能被改过,数量要重新夹取到合法区间(最小 1、最大按库存)。

校验逻辑放在服务端做最终裁决,前端的校验只用于即时提示,防止被篡改的本地数据直接写进账号购物车。

五、一致性:合并、回写、清空要一气呵成

合并涉及"读服务端车→并本地车→写回服务端→清本地",中途失败会留下半合并状态。几个关键设计:

  • 服务端做合并而不是前端:前端把游客车上送,由服务端在一次请求里完成"取登录车、合并、落库、返回最终车",避免前端先改本地、回写失败导致两边不一致。
  • 用版本号防覆盖:给登录车加一个 version,回写时带上读取时的版本,服务端发现版本已变(用户在另一台设备同时操作)就拒绝覆盖、重新拉取再合并,这是乐观锁思路。
  • 本地车延后清除:只有收到服务端合并成功的响应后才清空游客车;失败则保留本地车,允许下次重试,避免"请求失败但本地已清空"造成的丢失。
  • 合并幂等:同一次登录合并可能因重试执行多次,合并接口要幂等——可以用"游客车一次性 token + 服务端记录已合并的游客批次",防止重复合并把数量越加越多。

六、多端与多次登录:别让合并反复叠加

一个隐蔽 bug 是重复合并:用户在设备 A 登录合并过一次,没退出,又在设备 B 以游客身份加了商品再登录同一账号。如果每次都无脑把"本地全部条目"累加进服务端,而这些条目其实是上次合并后从服务端同步下来的,就会二次叠加。解法是给每条购物车项标记来源和"是否已上云"状态:只有本地新增、尚未同步过的条目才参与本次合并,已经是从服务端同步下来的条目不再作为"游客增量"重复提交。

七、踩坑清单

后果

解法

用 SPU/商品 id 判重

不同规格被错误折叠

一律按 sku_id 判重

以本地车覆盖服务端车

其他设备加的商品丢失

服务端车为基准、本地为增量

数量无脑相加

超库存、超限购

相加后过库存与限购校验

下架商品直接并入

结算报错

归入失效分组

先清本地再等响应

回写失败即丢车

成功后才清本地

无版本号并发回写

多端互相覆盖

乐观锁版本号

同步过的条目重复合并

数量越加越多

标记已上云、只并新增

八、工程落地建议

建议分三步落地:先实现"按 SKU 对齐、三类处理、数量累加"的最小闭环,覆盖 90% 的正常路径;再补库存/下架/售价这些现实校验,让合并结果可直接结算;最后加版本号、幂等和"已上云"标记,解决多端并发与重复登录的边界。测试时务必覆盖这几组用例:游客车为空、登录车为空、两车有相同 SKU、相同 SPU 不同 SKU、合并时商品下架、合并请求超时重试、两台设备同时登录。这些用例跑通,购物车合并才算稳。

九、复盘清单

  • 判重是否严格用 sku_id
  • 是否以服务端车为基准、本地为增量
  • 同一 SKU 数量策略是否全端一致
  • 合并后是否过了库存、限购、上下架校验
  • 是否成功后才清本地车、接口是否幂等
  • 多端并发是否有版本号、同步过的条目不重复合并

结语

购物车合并的难点不在"把两个列表拼起来",而在承认两辆车来源不同、时序交错、还会和库存现实碰撞。把判重粒度落到 SKU、把合并基准放到服务端、把冲突和失败路径逐条设计好,游客无论在哪台设备先逛多久,登录后那辆车都能既不丢一件、也不重一件。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 一、先想清楚:购物车里到底装了什么
  • 二、游客车与登录车分别存在哪
  • 三、核心合并算法:按 SKU 对齐,分三类处理
  • 四、数量合并的边界:库存、上下架、失效商品
  • 五、一致性:合并、回写、清空要一气呵成
  • 六、多端与多次登录:别让合并反复叠加
  • 七、踩坑清单
  • 八、工程落地建议
  • 九、复盘清单
  • 结语
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档