项目仓库
https://github.com/EthanYan6/E-commerce-sites.git
结合代码查看笔记,效果更佳。笔记只是记录重点或者难点。
每日分享
It is our attitude at the beginning of a difficult task which, more than anything else, will affect its successful outcome.
我们在开始做一件富有挑战性的任务时,对于成功的影响,没有什么比我们的态度更为关键的了。
小闫语录:
在大一的时候,英语老师曾经送给我们一句话『Attitude is everything.』态度决定一切。其实一件事成功与否,在这份工作上是否有建树,从一开始就决定了,那便是我们对其的态度。客观的困难不足以阻挡前进的脚步,畏惧的态度才是失败的根本原因,其他的事情就算是直接原因,那也只是『压死骆驼身上的最后一根稻草』。有着必胜的决心,不畏难的坚定,端正的态度,前方路上的困难都将是小的磕磕绊绊,你的成功从开始的这一刻便已注定,加油!
美多商城项目(八)
1.项目每次启动前开启的任务
1.前端服务器live-server开启(在静态文件目录下执行)
live-server
2.开启celery任务(在celery_task上一级目录终端进行输入)
celery -A celery_task.main worker -l info
3.开启定时任务
# 添加定时任务到系统中 python manage.py crontab add # 显示已经激活的定时任务 python manage.py crontab show # 移除定时任务 python manage.py crontab remove
4.开启tracker服务器
docker container start tracker
5.开启storage服务器
docker container start storage
6.开启elasticsearch服务器
docker container start elasticsearch
7.进入redis查看
# 进入redis redis-cli # 选择数据库 select <数据库序号>
8.进入mysql数据库
mysql -h<主机地址> -P<端口号> -u<用户名> -p<密码>
2.购物车记录添加
API: POST /cart/ 参数: 通过请求头传递jwt token { "sku_id":"添加商品id", "count":"数量", "selected":"勾选状态", # 可以不传,默认是勾选状态 } 响应: { "sku_id":"添加商品id", "count":"数量", "selected":"勾选状态" }
2.1业务逻辑
1.获取参数并进行校验(参数完整,sku_id商品是否存在,商品库存是否足够[可有可无,添加购物车可要可不要,下单时必须有])。
2.获取校验之后的数据。
3.获取user。
4.保存用户的购物车记录。
4.1如果用户已经登录,在redis中保存用户的购物车记录。
4.1.1获取redis链接。
hash:在redis hash中存储用户购物车添加的商品id和数量count。
如果购物车已经添加过该商品,数量需要进行累加,如果未添加,直接添加一个新元素。
hincrby 1. hincrby <key> <field> <increment>
给redis hash中指定field属性的值累加increment,如果field属性不存在,直接添加一个新的属性和值。
1. set: 在redis set中存储用户购物车勾选的商品id 2. 集合中的元素是唯一的 sadd 1. sadd <key> <member> ...
向redis set中添加元素,集合中元素是唯一的。
查看redis中是否存在。
# hash中查看所有的域field和他的值value hgetall xxx # set中获取key域集合的所有元素 smembers <key>
4.2如果用户未登录,在cookie中保存用户的购物车记录。
4.2.1获取原始cookie的购物车数据。
4.2.2解析cookie中购物车数据。
cart_dict = pickle.loads(base64.b64decode(cookie_cart))
4.2.3如果购物车已经添加过该商品,数量需要进行累加。
通过看商品的id是不是在购物车数据字典中,可以得知是否添加过。
4.2.3如果购物车没有添加过该商品,直接添加一个新元素。
5.返回应答,购物车记录添加成功。
5.1设置cookie中购物车数据
cart_data = base64.b64encode(pickle.dumps(cart_dict)).decode()
如过出现认证未通过,可以重写父类APIView中的perform_authentication方法,直接pass跳过认证。我们在获取用户的时候使用request.user也会触发认证机制,我们可以手动对错误进行捕获而不让其报错停止。如果进行访问的时候没有传递jwt用户,是不会进行jwt验证的,会将其作为匿名用户处理。
3.购物车记录获取
API: GET /cart/ 参数: 通过请求头传递jwt token 响应: [ { "id":"商品id", "name":"商品名称", "price":"商品价格", "default_image_url":"默认图片", "count":"数量", "selected":"勾选状态" }, ... ]
3.1业务逻辑
1.获取用户的购物车记录
1.1如果用户已登录,从redis中获取用户的购物车记录。
1.1.1获取redis链接
redis_conn = get_redis_connection('cart')
1.1.2从redis hash中获取用户购物车中添加的商品id和对应的数量count
hgetall 1. hgetall <key>
获取redis hash中所有的属性和值
1.1.3从redis set中获取用户购物车中勾选的商品id
smembers 1. smembers <key>
获取redis set中所有的元素
1.2如果用户未登录,从cookie中获取用户的购物车记录。
1.2.1获取cookie中购物车的数据。如果没有数据是None。
1.2.2解析cookie中购物车数据
cart_dict = pickle.loads(base64.b64decode(cookie_cart))
2.根据用户购物车中商品id获取对应商品的数据。
2.1给sku对象增加属性count和selected,分别保存该商品在购物车中添加数据和勾选状态。
3.将购物车商品数据序列化并返回。
3.1添加购物车商品序列化器类。
3.2将数据序列化并返回。
4.购物车记录修改
API: PUT /cart/ 参数: 通过请求头传递jwt token { "sku_id":"商品id", "count":"修改数量", "selected":"修改勾选状态" } 响应: { "sku_id":"商品id", "count":"修改数量", "selected":"修改勾选状态" }
4.1业务逻辑
1.获取参数并进行校验(参数完整性,sku_id商品是否存在,商品的库存)。
1.1获取校验之后的数据。
1.2获取user。
调用request.user会触发DRF框架认证过程。
2.修改用户的购物车记录。
2.1如果用户已经登录,修改redis中对应的购物车记录。
2.1.1获取redis链接。
2.1.2修改redis hash中商品id对应数量count。
hset
hset <key><field><value>
将redis hash中指定的field属性值设置为value。
2.1.3修改redis set中勾选的商品id。
2.1.3.1勾选,那么添加新元素。
sadd
sadd <key><member>...
向redis set中添加元素,集合中元素是唯一的。
2.1.3.2取消勾选,将其移除。
srem
移除
srem <key><members>...
从redis set中移除指定的元素,有则移除,无则忽略。
2.1如果用户没有登录,修改cookie中对应的购物车记录。
2.1.1获取cookie中的购物车数据。
2.1.2如果购物车没有数据。
2.1.3如果购物车中有数据,解析cookie中的购物车数据。
cart_dict = pickle.loads(base64.b64decode(cookie_cart))
如果获取的字典为空,表示购物车无数据。
2.1.4修改购物车数据。
3.返回应答,购物车记录修改成功。
3.1设置cookie中购物车数据。
cart_data = base64.b64encode(pickle.dumps(cart_dict)).decode()
3.2返回应答,购物车记录修改成功。
总结
1.购物车记录增加
hincrby sadd
2.购物车记录获取
hgetall smembers
3.购物车记录修改
hset sadd srem
对于上面的三个接口,都需要考虑用户是登录还是未登录。
让视图跳过DRF框架认证过程。
5.购物车记录删除
API: DELETE /cart/ 参数: 通过请求头传递jwt token { "sku_id":"商品id" } 响应: { "sku_id":"商品id" }
5.1业务逻辑
1.获取商品skuid并进行校验(skuid必传,sku_id对应商品是否存在)。
1.1获取校验之后的数据。
1.2获取用户,防止触发DRF框架的认证机制,对错误进行捕获。
2.删除用户的购物车记录。
2.1如果用户已登录,删除redis中对应的购物车记录。
2.1.1获取redis链接
2.1.2从redis hash中删除对应商品的id和数量count
hdel
hdel <key><field>...
删除redis hash中指定的field属性和值。
2.1.3从redis set中删除对应商品的id
srem
移除
srem <key><members>...
从redis set中移除指定的元素,有则移除,无则忽略。
2.2如果用户未登录,删除cookie中对应的购物车记录。
2.2.1获取cookie中的购物车记录。
2.2.2如果无数据,直接返回204;如果有数据需要解析cookie中购物车数据。
cart_dict = pickle.loads(base64.b64decode(cookie_cart))
2.2.3删除对应的购物车记录。
2.2.4重新设置cookie购物车数据。
cart_data = base64.b64encode(pickle.dumps(cart_dict)).decode()
3.返回应答,购物车记录删除成功。
6.全选和取消全选
API: PUT /cart/selection/ 参数: 通过请求头传递jwt token { "selected": "勾选状态", # True:全选, False:取消全选 } 响应: { "message": "OK" }
6.1业务逻辑
1.获取参数selected并进行校验(selected必传)。
1.1获取校验之后的selected。
1.2获取用户。
2.设置用户购物车记录勾选状态。
2.1如果用户已登录,设置redis中用户购物车记录勾选状态。
2.1.1获取redis链接。
2.1.2从redis hash中获取用户购物车中所有商品的id。
2.1.3全选:将用户购物车所有商品的id添加到redis set中。
2.1.4全不选:将用户购物车所有商品的id从redis set中移除。
2.2如果用户未登录,设置cookie中用户购物车记录勾选状态。
2.2.1获取cookie的购物车记录。
2.2.2解析cookie中的购物车数据
cart_dict = pickle.loads(base64.b64decode(cookie_cart))
2.2.3设置cookie购物车记录勾选状态。
3.返回应答,设置成功。
3.1设置cookie中的购物车数据
cart_data = base64.b64encode(pickle.dumps(cart_dict)).decode()
7.购物车记录合并
7.1需求
用户登录时,将cookie中的购物车数据合并到登录用户redis购物车记录中。
7.2实现
只需将购物车记录合并封装成函数,然后在登录过程进行调用。
登录过程调用合并购物车函数:
1.普通账户密码登录。
2.QQ登录。
7.2.1购物车记录合并方案
# 假如id为2的用户登录之前 # cookie中的购物车记录如下 { 11:{ 'count': 1, 'selected': False }, 15: { 'count': 2, 'selected': True } } cart_dict = { 11: 1, 15: 2 } cart_selected_add = [15] cart_selecte_remove = [11] # redis中的购物车记录如下 cart_2: { '11': '2', '16': '3', '9': '1' } cart_selected_2:('11', '9')
如果id为2的用户进行了登录,需要将cookie中的购物车数据合并到用户redis购物车记录中,如何进行合并?
答:当进行购物车记录合并时,如果cookie中的购物车数据和redis中的购物车数据发生了冲突,直接以cookie中的购物车数据为准,用cookie中的购物车数据去覆盖redis中对应的数据。如果没有冲突,都进行保留。
cart_2: { '11': '1', '15': '2', '16': '3', '9': '1' } cart_selected_2('15','9')
11的数据因为发生了冲突,覆盖后,取消了勾选。
7.2.2举例
假如id为2的用户登录之前,cookie中的购物车记录如下:
1. { 2. 1: { 3. 'count': 3, 4. 'selected': False 5. }, 6. 3: { 7. 'count': 2, 8. 'selected': True 9. }, 10. 5: { 11. 'count': 2, 12. 'selected': True 13. } 14. } redis中的购物车记录如下: 1. cart_2: { 2. '1': '2', 3. '5': '1', 4. '7': '3' 5. } 6. cart_selected_2: ('1', '7') 合并之后的数据如下: 1. cart_2: { 2. '1': '3', 3. '3': '2', 4. '5': '2', 5. '7': '3' 6. } 7. cart_selected_2: ('3', '5', '7')
7.3封装合并购物车记录函数
7.3.1目的
将cookie中的购物车记录合并到登录用户的redis记录中。
7.3.2业务逻辑
1.获取cookie中购物车记录。
2.如果cookie购物车中无数据,就不需要合并了。
3.如果cookie购物车中有数据,需要解析cookie中的购物车数据。
cart_dict = pickle.loads(base64.b64decode(cookie_cart))
4.如果解析出来的字典为空,表明cookie购物车中无数据,也不需要合并。
5.将cookie中购物车记录合并到登录用户的redis记录中
6.组织一个字典,存储cookie购物车记录中添加的商品id和对应数量count。此字典中的数据在进行购物车记录合并时需要设置到redis hash中。
7.组织一个列表,存储cookie购物车记录中被勾选的商品的id。此列表中的商品的id在进行购物车记录合并时,需要添加到redis set中。
8.组织一个列表,存储cookie购物车记录中未被勾选商品的id。此列表中商品id在进行购物车记录合并时需要从redis set中移除。
9.遍历解析之后的字典,将勾选与未勾选的数据分别加入对应的列表中。
10.合并:将组织的字典中key和value作为属性和值设置到redis对应的hash元素中。
11.将需要添加到redis的列表中商品id添加到redis对应的set元素中。
12.将需要从redis中移除的列表中商品的id从redis对应的set元素中移除。
13.删除cookie中购物车数据。
1. response.delete_cookie('cart') hmset 1. hmset <key> <field> <value> [<field> <value>....]
同时将多个field属性和值设置到哈希表key中。此命令会覆盖以存在的属性。
7.4修改登录视图
rest_framework_jwt提供的obtain_jwt_token视图, 实际从rest_framework_jwt.views.ObtainJSONWebToken类视图而来, 我们可以重写此类视图里的post方法来添加合并逻辑。
登录视图
from rest_framework_jwt.views import ObtainJSONWebToken class UserAuthorizeView(ObtainJSONWebToken): """ 用户认证 """ def post(self, request, *args, **kwargs): # 调用父类的方法,获取drf jwt扩展默认的认证用户处理结果 response = super().post(request, *args, **kwargs) # 仿照drf jwt扩展对于用户登录的认证方式,判断用户是否认证登录成功 # 如果用户登录认证成功,则合并购物车 serializer = self.get_serializer(data=request.data) if serializer.is_valid(): user = serializer.validated_data.get('user') response = merge_cart_cookie_to_redis(request, user, response) return response 修改路径users/urls.py . urlpatterns = [ . ... . # url(r'^authorizations/$', obtain_jwt_token), . url(r'^authorizations/$', views.UserAuthorizeView.as_view()), . ... . ]
QQ登录
修改oauth/serializers.py中的序列化器
class OAuthQQUserSerializer(serializers.ModelSerializer): ... def create(self, validated_data): ... # 向视图对象中补充user对象属性,以便在视图中使用user self.context['view'].user = user return user
修改oauth/views.py中的视图
class OAuthQQUserView(CreateAPIView): """ 获取QQ用户对应的美多商城用户 """ serializer_class = OAuthQQUserSerializer def get(self, request): ... else: reponse = Response({ 'token': token, 'username': user.username, 'user_id': user.id }) # 合并购物车 response = merge_cart_cookie_to_redis(request, user, response) return response def post(self, request, *args, **kwargs): response = super().post(request, *args, **kwargs) # 合并购物车 response = merge_cart_cookie_to_redis(request, self.user, response) return response