如何设计购物车页面:一次系统设计模拟面试
购物车是电商网站的关键一环,用户在这里核对和调整选购内容再去结账。一次模拟面试的整理:功能需求、数据结构与 API 设计,以及为什么购物车的计算逻辑应该放在后端。
English version: How to Design a Shopping Cart Page: A System Design Mock Interview
购物车页面是任何电商网站的关键组成部分,用户在这里核对和修改自己选购的商品,然后再去付款。设计一个好用的购物车页面,功能需求和非功能需求都要仔细考虑。
功能需求
购物车页面必须有这几项功能:
- 展示购物车:包括总价、折扣和最终价格。
- 加入购物车:把商品加进购物车。
- 从购物车移除:删掉不想要的商品。
- 修改数量:调整每件商品的数量。
- 勾选和取消勾选:选择哪些商品参与结账。
- 结账:发起结账流程,跳转到支付页面。
数据结构与 API 设计
以下数据结构和接口可以实现上面的功能。
展示购物车
请求:
GET /api/cart
响应:
{
"totalSelectItems": 2,
"cartItems": [
{
"skuId": "123",
"cover": "https://example.com/cover.jpg",
"name": "Product A",
"description": "Product A description",
"quantity": 2,
"price": 100,
"discountPrice": 90,
"finalPrice": 180,
"isSelect": true
},
{
"skuId": "124",
"cover": "https://example.com/cover.jpg",
"name": "Product B",
"description": "Product B description",
"quantity": 1,
"price": 200,
"discountPrice": 180,
"finalPrice": 180,
"isSelect": true
}
],
"promotions": [
{
"promotionId": "123",
"type": "discount",
"description": "Buy 2 get 1 free",
"isSelect": true
}
],
"currency": "USD",
"totalPrice": 360,
"discountPrice": 330,
"finalPrice": 330
}
加入购物车
请求:
POST /api/cart/add
{
"skuId": "123",
"quantity": 1
}
响应:
{
"code": 200,
"message": "Success"
}
从购物车移除
请求:
POST /api/cart/remove
{
"skuId": "123"
}
修改数量
请求:
POST /api/cart/modify
{
"skuId": "123",
"quantity": 2
}
勾选和取消勾选
请求:
POST /api/cart/select
{
"skuId": "123",
"isSelect": true
}
结账
请求:
POST /api/cart/checkout
响应:
{
"orderId": "123",
"redirectUrl": "https://example.com/pay?orderId=123"
}
以上移除、修改、勾选三个接口的响应和“加入购物车”相同,返回状态码和消息即可。
非功能需求
信息正确性
为了保证数据一致,购物车的计算逻辑应该放在后端。好处有两点:
- 数据一致:把计算集中在后端,Android、iOS、Web 各端拿到的结果完全一样。对促销规则复杂的电商平台来说这一点很关键,避免各端团队各自实现造成的不一致。
- 前端更快更简单:计算下沉到后端,前端架构变简单,性能也更好。
但这个做法也有代价:
- 后端负载增加:计算集中意味着后端算力需求上升。
- 网络请求变多:每次改动数量或勾选都要请求一次后端,延迟增加可能影响前端体验。可以用本地乐观更新加后端最终校验来缓解。