购物车与结算接口
购物车中心
用户点击价格策略加入购物车,个人中心可以查看自己所有购物车中数据~~
在购物车中可以删除课程,还可以更新购物车中课程的价格策略~~~
所以接口应该有四种请求方式, get,post,patch,delete~~
我们在做这个功能之前,首先要讨论我们购物车中数据存在哪~~~为什么要这么存~~
因为购物车是属于中间状态数据~~而且很多时候需要过期时间~~所以我们选择redis~~
读取速度快~并且redis可以做持久化~~支持的数据类型也比较多~~
然后我们需要讨论~我们存redis~~存什么样的数据结构~对于我们实现需求来说更加方便~
下面看代码中存入redis中的数据结构是什么样的~~
from rest_framework.views import APIView from rest_framework.response import Response from utils.authentication import MyAuth from utils.redis_pool import POOL from utils.base_response import BaseResponse import redis from couse import models import json, datetime from utils.exceptions import CommonException from django.core.exceptions import ObjectDoesNotExist SHOPPING_CAR_KEY = "shopping_car_%s_%s" # 购物车在redis的key ACCOUNT_KEY = "account_%s_%s" # 支付时候用的key REDIS_CONN = redis.Redis(connection_pool=POOL) SHOPPING_CAR_KEY = "shopping_car_%s_%s" # 购物车在redis的key ACCOUNT_KEY = "account_%s_%s" # 支付时候用的key REDIS_CONN = redis.Redis(connection_pool=POOL) # shopping_car_ %s_ %s: { # id: 1, # title: CMDB, # course_img: xxxxx, # price_policy_dict: { # 1: {"valid_period_text":有效期1个月,"price": 99} # }, # default_price_policy_id: 3 # # } redis库中对应的数据解构(根据查看luffy官网购物车获得) class ShoppingCar(APIView): # 加入购物车必学确认登录(根据token) # 添加登录的认证类 authentication_classes = [MyAuth, ] def post(self, request): res = BaseResponse() try: # 1 获取前端传过来的course_id 以及price_policy_id user_id course_id = request.data.get("course_id", "") price_policy_id = request.data.get("price_policy_id", "") user_id = request.user.id # 2 验证数据的合法性 # 2.1 验证course_id是否合法 course_obj = models.Course.objects.filter(id=course_id).first() if not course_obj: res.code = 1031 res.error = "课程不存在" return Response(res.dict) # 2.2 验证价格策略是否合法 # 该课程的所有价格策略对象 price_policy_queryset = course_obj.price_policy.all() # 循环获得每个价格策略的详细信息 price_policy_dict = {} for price_policy_obj in price_policy_queryset: price_policy_dict[price_policy_obj.id] = { "valid_period_text": price_policy_obj.get_valid_period_display(), "price": price_policy_obj.price } # 判断价格策略是否在价格策略的字典里 if price_policy_id not in price_policy_dict: res.code = 1032 res.error = "价格策略不存在" return Response(res.dict) # 3 构建我们想要的数据结构 course_info = { "id": course_id, "title": course_obj.title, "course_img": str(course_obj.course_img), "price_policy_dict": json.dumps(price_policy_dict, ensure_ascii=False), "default_policy_id": price_policy_id } # 4 写入redis # 4.1 先拼接购物车的key shopping_car_key = SHOPPING_CAR_KEY % (user_id, course_id) # 4.2 写入redis print(shopping_car_key, course_info) REDIS_CONN.hmset(shopping_car_key, course_info) res.data = "加入购物车成功" except Exception as e: print(str(e)) # raise res.code = 1030 res.error = "加入购物车失败" return Response(res.dict) def get(self, request): res = BaseResponse() try: # 1 取到user_id user_id = request.user.id # 2 拼接购物车的key shopping_car_key = SHOPPING_CAR_KEY % (user_id, "*") # shopping_car_1_* # shopping_car_1_asdgnlaksdj # 3 去redis读取该用户的所有加入购物车的课程 # 3.1 先去模糊匹配出所有符合要求的key all_key = REDIS_CONN.scan_iter(shopping_car_key) # 3.2 循环所有的keys 得到每个key shopping_car_lsit = [] for key in all_key: course_info = REDIS_CONN.hgetall(key) course_info["price_policy_dict"] = json.loads(course_info["price_policy_dict"]) shopping_car_lsit.append(course_info) res.data = shopping_car_lsit except Exception as e: res.code = 1033 res.error = "加载购物车失败" return Response(res.dict) def put(self, request): res = BaseResponse() try: # 1 获取前端传过来的course_id 以及price_policy_id course_id = request.data.get("course_id", "") price_policy_id = request.data.get("price_policy_id", "") user_id = request.user.id # 校验数据的合法性 # 2.1 校验course_id是否合法 shoping_car_key = SHOPPING_CAR_KEY % (user_id, course_id) if not REDIS_CONN.exists(shoping_car_key): # 如果不存在 res.code = 1035 res.error = "课程不存在" return Response(res.dict) # 2.2 判断价格策略是否合法 course_info = REDIS_CONN.hgetall(shoping_car_key) price_policy_dict = json.loads(course_info["price_policy_dict"]) if str(price_policy_id) not in price_policy_dict: res.code = 1036 res.error = "所选的价格策略不存在" return Response(res.dict) # 修改信息写入redis REDIS_CONN.hmset(shoping_car_key, course_info) res.data = "更新成功" except Exception as e: # raise res.code = 1034 res.error = "更新价格策略失败" return Response(res.dict) def delete(self, request): res = BaseResponse() try: # 获取前端传过来的course_id course_id = request.data.get("course_id", "") user_id = request.user.id # 判断课程id是否合法 shopping_car_key = SHOPPING_CAR_KEY % (user_id, course_id) if not REDIS_CONN.exists(shopping_car_key): res.code = 1039 res.error = "删除的课程不存在" return Response(res.dict) # 删除redis中的数据 REDIS_CONN.delete(shopping_car_key) except Exception as e: print(str(e)) res.code = 1037 res.error = "删除失败" return Response(res.dict) class AccountView(APIView): """ 结算接口 shopping_car_ 1_ 1: { id: 1, title: CMDB, course_img: xxxxx, price_policy_dict: { 1: {有效期1个月, 99} }, default_price_policy_id: 3 } account_%s_%s:{ "course_info":{ id: 1, title: CMDB, course_img: xxxxx, price_policy_dict: { 1: {有效期1个月, 99} }, default_price_policy_id: 3 }, "coupons":{ 1:{}, 3:{}, } } global_coupon_1:{} """ authentication_classes = [MyAuth, ] def post(self, request, *args, **kwargs): # 获取数据 user = request.user course_id_list = request.data.get("course_id_list", "") print(course_id_list) response = BaseResponse() try: # 清空操作 # 找到当前用户所有以account_userid_ * 全部清空 del_list = REDIS_CONN.keys(ACCOUNT_KEY % (user.pk, "*")) print(del_list) REDIS_CONN.delete(*del_list) # 2 创建数据结构 for course_id in course_id_list: account_key = ACCOUNT_KEY % (user.pk, course_id) account_dict = {} shopping_car_key = SHOPPING_CAR_KEY % (user.pk, course_id) # 判断课程是否存在在购物车中 if not REDIS_CONN.exists(shopping_car_key): raise CommonException("购物车不存在该课程", 1040) # 将课程信息加入到没有一个课程结算字典中 course_info = REDIS_CONN.hgetall(shopping_car_key) # 从redis中获取课程信息 account_dict["course_info"] = course_info # 将课程优惠券加入结算每一个课程结算字典中 # 查询当前用户拥有未使用的,在有效期的企鹅与当前课程相关的优惠券 account_dict["course_coupons"] = self.get_coupon_dict(request, course_id) # 将课程优惠券加入结算每一个课程结算字典中get_coupon_dict # 存储结算信息 REDIS_CONN.set(account_key, json.dumps(account_dict)) # 获取通用优惠券,加入redis中 REDIS_CONN.set("global_coupon_%s" % user.pk, json.dumps(self.get_coupon_dict(request))) except CommonException as e: response.code = e.code response.error = e.msg except Exception as e: response.code = 500 response.error = str(e) return Response(response.dict) def get_coupon_dict(self, request, course_id=None): now = datetime.datetime.utcnow() coupon_record_list = models.CouponRecord.objects.filter( user=request.user, status=0, coupon__valid_begin_date__lte=now, coupon__valid_end_date__gt=now, coupon__content_type_id=10, coupon__object_id=course_id ) coupon_dict = {} for coupon_record in coupon_record_list: coupon_dict[coupon_record.pk] = { "name": coupon_record.coupon.name, # 名字 "coupon_type": coupon_record.coupon.get_coupon_type_display(), # 券类型 "money_equivalent_value": coupon_record.coupon.money_equivalent_value, # 等值货币 "off_percent": coupon_record.coupon.off_percent, # 折扣百分比 "minimum_consume": coupon_record.coupon.minimum_consume, # 最低消费 "valid_begin_date": coupon_record.coupon.valid_begin_date.strftime("%Y-%m-%d"), # 有效期开始时间 "valid_end_date": coupon_record.coupon.valid_end_date.strftime("%Y-%m-%d"), # 有效结束时间 } print(coupon_dict) return coupon_dict def get(self, request): pass