大部分电商都允许"先逛后登录":用户没登录时把商品加进本地购物车,登录或注册后要把这辆"游客购物车"和账号里已有的"登录购物车"合成一辆。听起来简单,做起来全是边界:同一件商品两边都有怎么办、规格不同算不算同一件、游客选中的状态要不要保留、合并失败如何回滚。本文讲清一套可直接落地的购物车合并方案:先定数据模型,再定合并算法,最后处理冲突和一致性。
合并出错,很多是因为购物车条目模型太简陋,只存了"商品 id + 数量"。要支持精确合并,一条购物车项至少要包含这些字段:
字段 | 作用 | 合并时的意义 |
|---|---|---|
sku_id | 最小规格单元 | 判断"是不是同一件"的唯一依据 |
quantity | 数量 | 冲突时需要决定相加还是覆盖 |
selected | 是否勾选 | 影响登录后全选/结算状态 |
added_at | 加入时间 | 排序、以及冲突时的取舍依据 |
source | 来源端 | 多端合并时追溯来源 |
最关键的一条:判重必须用 SKU 而不是 SPU。同一款 T 恤(SPU)选了不同颜色尺码(SKU)就是两条独立记录,不能合并成一条;如果用商品 id 判重,会把不同规格错误折叠,数量和规格全乱。
两辆车的存储位置不同,决定了合并的触发时机:
游客车:存在浏览器本地(localStorage / 本地数据库),按设备隔离,不上账号。 登录车:存在服务端,按用户 id 隔离,跨设备同步。
合并发生在三个时刻:登录成功的瞬间、注册成功的瞬间、以及已登录用户在新设备拉取到本地旧车时。原则是以服务端登录车为基准、把游客车作为增量并进去,合并完成后用结果回写服务端、再清空本地游客车。不要反过来以本地车覆盖服务端,否则用户在另一台设备上已加的商品会丢。
把两辆车都按 sku_id 建索引,然后遍历并集,每一条只会落入下面三种情况之一:
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 过一遍校验:
校验逻辑放在服务端做最终裁决,前端的校验只用于即时提示,防止被篡改的本地数据直接写进账号购物车。
合并涉及"读服务端车→并本地车→写回服务端→清本地",中途失败会留下半合并状态。几个关键设计:
一个隐蔽 bug 是重复合并:用户在设备 A 登录合并过一次,没退出,又在设备 B 以游客身份加了商品再登录同一账号。如果每次都无脑把"本地全部条目"累加进服务端,而这些条目其实是上次合并后从服务端同步下来的,就会二次叠加。解法是给每条购物车项标记来源和"是否已上云"状态:只有本地新增、尚未同步过的条目才参与本次合并,已经是从服务端同步下来的条目不再作为"游客增量"重复提交。
坑 | 后果 | 解法 |
|---|---|---|
用 SPU/商品 id 判重 | 不同规格被错误折叠 | 一律按 sku_id 判重 |
以本地车覆盖服务端车 | 其他设备加的商品丢失 | 服务端车为基准、本地为增量 |
数量无脑相加 | 超库存、超限购 | 相加后过库存与限购校验 |
下架商品直接并入 | 结算报错 | 归入失效分组 |
先清本地再等响应 | 回写失败即丢车 | 成功后才清本地 |
无版本号并发回写 | 多端互相覆盖 | 乐观锁版本号 |
同步过的条目重复合并 | 数量越加越多 | 标记已上云、只并新增 |
建议分三步落地:先实现"按 SKU 对齐、三类处理、数量累加"的最小闭环,覆盖 90% 的正常路径;再补库存/下架/售价这些现实校验,让合并结果可直接结算;最后加版本号、幂等和"已上云"标记,解决多端并发与重复登录的边界。测试时务必覆盖这几组用例:游客车为空、登录车为空、两车有相同 SKU、相同 SPU 不同 SKU、合并时商品下架、合并请求超时重试、两台设备同时登录。这些用例跑通,购物车合并才算稳。
购物车合并的难点不在"把两个列表拼起来",而在承认两辆车来源不同、时序交错、还会和库存现实碰撞。把判重粒度落到 SKU、把合并基准放到服务端、把冲突和失败路径逐条设计好,游客无论在哪台设备先逛多久,登录后那辆车都能既不丢一件、也不重一件。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。