一、功能
下单前,暂存用户选购的商品。
主要包括四个功能:
- 把商品加入购物车
- 购物车列表展示
- 结算下单
- 其他页面展示购物车小图标
购物车一般展示SKUID、数量、加入时间、勾选图标
二、展示原则
- 如果用户没有登录,需要临时找个地方存储购物车的内容;
- 当用户登录时,把临时购物车的内容合并到用户的购物车中,并且清除临时购物车;
- 用户登录后,需要保证用户各端展示的购物车商品都是一致的;
综上,购物车系统有两类购物车:未登录时的“临时购物车”和登录后的“用户购物车”
三、临时购物车设计
设计方案:
- 保存在客户端:session、cookie和LocalStorage
- 保存在服务端
如果保存在服务端,需要一个全局唯一的标识ID,这个ID的生成比较麻烦,而且浪费服务端资源;所以保存在客户端比较合适,既没有生成ID的问题,也没有使用服务器的存储资源,每个客户端保存自己的购物车信息就可以了。对于客服端的三种方案,session的保留时间短,且仍然存储在服务端,浪费资源;cookie存储实现简单,客户端和服务端每次交互都会带着cookie交互,这样服务端可以使用客户端cookie的数据,但是缺点是只有4K的容量上限,适合小型电商;对于LocalStorage,只能由客户端访问,存储容量大,不用每次和服务端交互都带着,节省带宽,缺点是实现复杂,客户端和服务端都要实现一些业务逻辑,比较适合那种会加购大量商品的批发的行业用户。
不管选用哪种存储方式,直接用JSON字符串保存即可,形如:
{
"tmpCart": [
{
"skuid": 111,
"createdTime": 1589792247,
"count": 10,
"selected": false
},
{
"skuid": 222,
"createdTime": 1589792247,
"count": 20,
"selected": false
}
]
}
四、用户购物车设计
用户购物车可以选择数据库来实现:
CREATE TABLE `cart` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增ID',
`user_id` bigint(20) NOT NULL DEFAULT 0 COMMENT '用户ID',
`sku_id` bigint(20) NOT NULL DEFAULT 0 COMMENT 'SKUID',
`count` int NOT NULL DEFAULT 0 COMMENT '数量',
`selected` tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否选中',
`created_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间'
PRIMARY KEY (`id`),
KEY `ix_user_id` (`user_id`),
KEY `ix_created_time` (`created_time`),
) ENGINE = InnoDB DEFAULT CHARSET = utf8 COMMENT = '购物车表'
如果用Redis存储的话:
{
"KEY": 10001,
"VALUE": [
{
"FIELD": 90001,
"FIELD_VALUE": {
"createdTime": 1589792247,
"count": 20,
"selected": false
}
},
{
"FIELD": 90002,
"FIELD_VALUE": {
"createdTime": 1589792248,
"count": 20,
"selected": false
}
}
]
}
其中,KEY的值10001是用户ID,FIELD的值90001和90002存放的是商品skuid,FIELD_VALUE是商品的信息。
对比:
- Redis的读写性能比数据库至少高出一个量级,响应更快;
- 数据库的可靠性要高于Redis,因为Redis是异步刷盘,如果出现服务器突然宕机,可能会丢失数据,但是购物车对可靠性要求没那么高;
- 数据库支持多种查询方式和事务机制,例如统计一下今天加够的商品总数,这个适合用数据库比较容易实现。
五、注意点
- 购物车数据最好放在数据库中,正常购物车的商品是不占库存的,但是某些特卖电商购物车的商品是占库存的,数据是不允许丢失的,不如客户体验会非常差;
- 假如用数据库实现,没必要添加一层缓存,因为每个用户只会访问自己的购物车,每次访问网站也不会多次打开购物车。“读多写少用缓存,写多读少用MQ”,购物车不符合这个场景,造成缓存的命中率太低,意义不大。