路飞学城项目-支付相关-购物车接口

 ###############     支付相关的需求  ################

"""
支付相关的需求:
购物车需求:
1,在课程详情页面,点击加入购物车可以把课程加入到购物车
2,点击导航条的购物车按钮可以进入购物车页面
3,在购物车页面点击删除可以把购物车删除,
4,点击购物车的有效期,可以更改价格策略,然后去购物车的数据进行修改,
5,点击购物车底部左侧的全选,可以全选,然后全选按钮旁边有删除,可以批量删除,
6,购物车底部右侧的有全部的金额合计,右侧有一个去结算按钮,点击可以到结算页面
实现购物车的新增,删除,修改,查询,


结算需求:
1,结算页面的价格策略是不能修改的,但是可以选择优惠券,使用贝里,
2,优惠券分为两种一种是绑定课程的和课程在一栏,没有绑定课程的是通用优惠券,在底部可以选择,
3,结算页面右下角显示使用优惠券之后的价格,显示使用贝里之后的价格,
4,最下面显示支付方式,应付金额,和立即支付按钮,点击按钮可以进入支付流程,
实现结算信息的新增,查看,修改,没有删除,你回去购物车之后就把结算信息情况了,可以再次新增,


支付需求:
1,点击立即支付进入支付流程,需要生成订单,然后调支付宝接口,
2,支付成功之后调回调接口,修改订单的状态,但是没有这么简单,
2.1修改订单的状态
2.2有优惠券修改优惠券的状态
2.2使用了贝里需要修改贝里的金额,
2.3你买的如果是普通课程,你就可以看了,如果是学位课,你要开通模块,配备老师,


技术使用redis数据库,
原因有两个
1,购物车和结算是临时状态,
2,需要频繁的修改购物车信息,



"""

 ###############     购物车接口实现思路   ################

"""

购物车的设计思路:
一,需要保持什么字段

1,用户id
2,课程名称,图片,
3,价格策略:你需要这个课程的价格策略都取出来,
    3.1 id
    3.2 有效期,
    3.3 价格
4,默认选中的价格策略,

二,数据结构
注意,redis只允许有一层字典结构,所以多余一层的,需要进行序列化,变成字符串放进去,然后反序列化拿出来,
第一种数据结构:
shapping_car : {
    user_id : {
        course_id : {
          title:XX
          course_img:XX
          price_policy_dict: {
              1:{'name':有效期1个月,price:799} # 价格策略会有多个
              }
          default_price_policy_dict: 1
        }
    }
}

第二种数据结构
shapping_car_userid_courseid : {
      id:XX
      title:XX
      course_img:XX
      price_policy_dict: {
          1:{name:有效期1个月,price:99 }
      }
      default_price_policy_dict:XX
 }

三,开始实现加入购物车:
前端:
传过来course_id , price_policy_id ,不需要传递user_id因为已经登录了,
后端:
1,获取前端传过来的值,如何取到user_id ? 直接request里面就会有,
2,对存过来的数据进行校验,一定要校验,把这个校验深深刻进脑子
      1,验证course_id 验证是否合法
      2,price_policy_id 验证是否合法
3,构建我们想要的数据结构
4,写入redis
5,返回数据,

四,开始实现查看购物车
前端:进入购物车页面,发送get请求,
后端逻辑:
1,获取到userid,
2,根据userid拼接购物车的key,
3,去redis里面把这个人所有的购物车信息都拿出来,
4,然后返回给前端,
注意,redis的操作是重中之重

五,更新购物车逻辑
前端发过来字段:{
  course_id:1,
  default_policy_id:2
}
后端逻辑:
1,获取前端传过来的课程id,策略id
2,校验数据,
3,更新数据,
4,返回数据,

六,删除购物车信息
前端传过来字典:{
  course_id:[1,2,3]  # 支持删除单个,和多个,
   }
后端逻辑:
1,获取前端传过来的课程id
2,拼接购物车的key,
3,验证key是否存在,
4,如果存在,就删除,
5,返回成功,



"""

 ###############     购物车接口实现思路   ################

1:

from rest_framework.views import APIView
from rest_framework.viewsets import GenericViewSet, ViewSetMixin
from rest_framework.response import Response
from django_redis import get_redis_connection
from utils.response import BaseResponse
from utils.auth import LuffyAuth
from api import models
from django.core.exceptions import ObjectDoesNotExist
from utils.exception import PricePolicyInvalid
from django.conf import settings
import json

class ShoppingCarViewSet(APIView):
    authentication_classes = [LuffyAuth,]
    conn = get_redis_connection("default") # 在这个地方穿件连接池,下面在函数中连接redis的时候,需要self.conn

    def post(self, request, *args, **kwargs):
        """
        将课程添加到购物车
        :param request:
        :param args:
        :param kwargs:
        :return:
        """
        ret = BaseResponse()
        try:
            # 1. 获取用户提交的课程ID和价格策略ID
            course_id = int(request.data.get('courseid'))
            policy_id = int(request.data.get('policyid'))

            # 2. 通过课程id获取专题课信息,如果找不多会报异常,需要处理ObjectDoesNotExist
            course = models.Course.objects.get(id=course_id)

            # 3. 获取该课程相关的所有价格策略
            price_policy_list = course.price_policy.all()  # all返回queryset对象,可以遍历
            price_policy_dict = {}
            for item in price_policy_list:
                price_policy_dict[item.id] = {
                    "period":item.valid_period,
                    "period_display":item.get_valid_period_display(),
                    "price":item.price,
                }

            # 4. 判断用户提交的价格策略是否合法
            if policy_id not in price_policy_dict:
                # 价格策略不合法
                raise PricePolicyInvalid('价格策略不合法')

            # 5. 将购物信息添加到redis中
            # self.conn
            # car_key = "luffy_shopping_car_%s_%s"
            car_key = settings.SHOPPING_CAR_KEY %(request.auth.user_id,course_id,)
            car_dict = {
                'title':course.name,
                'img':course.course_img,
                'default_policy':policy_id,
                'policy':json.dumps(price_policy_dict)
            }
            # conn = get_redis_connection("default")
            self.conn.hmset(car_key,car_dict)
            ret.data = '添加成功'

        except PricePolicyInvalid as e:
            ret.code = 2001
            ret.error = e.msg
        except ObjectDoesNotExist as e:
            ret.code = 2001
            ret.error = '课程不存在'
        except Exception as e:
            ret.code = 1001
            ret.error = '获取购物车失败'
        return Response(ret.dict)

    def delete(self, request, *args, **kwargs):
        """
        购物车中删除课程
        :param request:
        :param args:
        :param kwargs:
        :return:
        """
        ret = BaseResponse()
        try:
            course_id_list = request.data.get('courseids')
            key_list = [ settings.SHOPPING_CAR_KEY %(request.auth.user_id,course_id,) for course_id in course_id_list]
            self.conn.delete(*key_list)
        except Exception as e:
            ret.code = 1002
            ret.error = "删除失败"

        return Response(ret.dict)

    def patch(self, request, *args, **kwargs):
        """
        修改课程的价格策略
        :param request:
        :param args:
        :param kwargs:
        :return:
        """
        ret = BaseResponse()
        try:
            # 1. 获取价格策略ID和课程ID
            course_id = int(request.data.get('courseid'))
            policy_id = str(request.data.get('policyid'))  # 因为你load回来之后,是一个字符串,

            # 2. 拼接课程的key
            key = settings.SHOPPING_CAR_KEY %(request.auth.user_id,course_id,)
            if not self.conn.exists(key):
                ret.code = 1002
                ret.error = "购物车中不存在此课程"
                return Response(ret.dict)
            # 3. redis中获取所有的价格策略
            policy_dict = json.loads(str(self.conn.hget(key,'policy'),encoding='utf-8'))
            if policy_id not in policy_dict:
                ret.code = 1003
                ret.error = "价格策略不合法"
                return Response(ret.dict)

            # 4. 在购物车中修改该课程的默认价格策略
            self.conn.hset(key,'default_policy',policy_id)

            ret.data = "修改成功"

        except Exception as e:
            ret.code = 1004
            ret.error = "修改失败"

        return Response(ret.dict)

    def get(self,request, *args, **kwargs):
        """
        查看购物车中所有的商品
        :param request:
        :param args:
        :param kwargs:
        :return:
        """
        ret = BaseResponse()
        try:
            key_match = settings.SHOPPING_CAR_KEY %(request.auth.user_id,"*")

            course_list = []

            for key in self.conn.scan_iter(key_match,count=10):
                info = {
                    "title":self.conn.hget(key,'title').decode('utf-8'),
                    "img":self.conn.hget(key,'img').decode('utf-8'),
                    "policy":json.loads(self.conn.hget(key,'policy').decode('utf-8')),
                    "default_policy":self.conn.hget(key,'default_policy').decode('utf-8')
                }
                course_list.append(info)
            ret.data = course_list
        except Exception as e:
            ret.code = 1002
            ret.error = "获取失败"
        return Response(ret.dict)

2:

class ShoppingCar(APIView):
    conn = get_redis_connection('default')
    authentication_classes = [TokenAuth]

    def post(self,request):
        res = BaseException()
        # 1,获取前端传过来的数据
        course_id = int(request.data.get('course_id','')) price_policy_id = int(request.data.get('price_policy_id','')) user_id = request.user.id print(course_id,price_policy_id,user_id) # 2,获取课程信息 course_obj = Course.objects.filter(id=course_id).first() if not course_obj: res.code=1021 res.error= '课程不存在' return Response(res.dict) # 3,获取课程所有的价格策略 price_policy_list = course_obj.price_policy.all() price_dict = {} for i in price_policy_list: price_dict[i.id]= { "text":i.get_valid_period_display(), "price":i.price } print(price_dict) print(price_dict.keys()) print(price_policy_id) # 判断策略是否存在 if price_policy_id not in price_dict: res.code=1022 res.error='策略不存在' return Response(res.dict) # 存入redis redis_car_key = settings.SHOPPING_CART_KEY.format(user_id,course_id) self.conn.hmset(redis_car_key,{ "title": course_obj.name, "course_img": course_obj.course_img, "price": json.dumps(price_dict), "default_price_id": price_policy_id }) res.data='加入成功' return Response(res.dict) def get(self,request): res = BaseException() try: # 1,获取userid user_id = request.user.id # 2,拼接购物车的key, shopping_car_key = settings.SHOPPING_CART_KEY.format(user_id,"*") print(shopping_car_key) # 3,根据key,获取所有的购物车列表 all_key = self.conn.scan_iter(shopping_car_key) print('all_key',all_key) # 4,构造返回数据 shopping_car_list = [] for key in all_key: course_id = str(key, encoding='utf-8').rsplit("_", maxsplit=1)[1] # str 类型 # print(course_id, type(course_id)) course_info = { course_id: { "title": self.conn.hget(key, "title").decode('utf-8'), "img": self.conn.hget(key, "course_img").decode('utf-8'), "default_policy": self.conn.hget(key, "default_price_id").decode('utf-8'), "policy": json.loads(self.conn.hget(key, "price").decode('utf-8')), # 转换成 字符串  } } shopping_car_list.append(course_info) res.data=shopping_car_list except Exception as e: print(e) res.error=10031 res.error='获取失败' return Response(res.dict) def put(self,request): res = BaseException() try: # 1,获取前端传过来的数据,课程id,策略id。 course_id = request.data.get('course_id') price_id = request.data.get('default_price_id') user_id = request.user.id # 2,判断课程是否存在 # 逻辑,拼接shoppingkey,然后判断是否存在 shopping_car_key = settings.SHOPPING_CART_KEY.format(user_id,course_id) if not self.conn.exists(shopping_car_key): res.code=1033 res.error="课程不存在" return Response(res.data) # 3,判断策略是否存在 # 先获取到这个课程下面的价格策略 # course_info = self.conn.hgetall(shopping_car_key) price_dict= json.loads(str(self.conn.hget(shopping_car_key,'price'),encoding='utf-8')) print(price_dict) if price_id not in price_dict: res.code=1034 res.error='策略不存在' return Response(res.dict) self.conn.hset(shopping_car_key,'default_price_id',price_id) res.data='修改成功' except Exception as e: print(e) res.code=1034 res.error='更新失败' return Response(res.dict) def delete(self,request): res = BaseException() try: # 1,获取前端的 courseid course_id = request.data.get('course_id') user_id = request.user.id # 2,判断courseid是否存在 shopping_car_key = settings.SHOPPING_CART_KEY.format(user_id,course_id) if not self.conn.exists(shopping_car_key): res.code=1035 res.error='课程不存在' # 3,删除记录  self.conn.delete(shopping_car_key) res.data='删除成功' except Exception as e: res.code=1036 res.error='删除失败' return Response(res.dict)

###############    购物车认证类    ################

from rest_framework.authentication import BaseAuthentication
from api import models
from rest_framework.exceptions import AuthenticationFailed

class LuffyAuth(BaseAuthentication):

    def authenticate(self, request):
        """
        用户请求进行认证
        :param request:
        :return:
        """
        # http://wwwww...c0ovmadfasd/?token=adfasdfasdf
        token = request.query_params.get('token')
        obj = models.UserAuthToken.objects.filter(token=token).first()
        if not obj:
            raise AuthenticationFailed({'code':1001,'error':'认证失败'})

        return (obj.user.username,obj)

###############    购物车异常类    ################

class PricePolicyInvalid(Exception):
    def __init__(self,msg):
        self.msg = msg

###############    购物车redis连接和key配置    ################

# django-redis 配置
CACHES = {
    "default": {  # 一个redis连接
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://192.168.100.128:6379",  # redis的IP和端口
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
            "CONNECTION_POOL_KWARGS": {"max_connections": 1000},  # 连接池最大连接数
            "PASSWORD": "ji10201749"  # 密码
        },
    },
}

# 购物车的 redis 中的key
SHOPPING_CART_KEY = "shopping_car_{0}_{1}"
PAYMENT_COURSE_KEY = "payment_{0}_{1}"  # redis 中 结算中心关于课程信息+优惠券信息
PAYMENT_GLOBAL_COUPON_KEY = "payment_global_coupon_{}"  # 通用优惠券信息

###############    基础的返回类    ################

class BaseResponse(object):
    def __init__(self):
        self.data = None
        self.code = 1000
        self.error=None

    @property
    def dict(self):
        return self.__dict__

###############    结束线    ################

猜你喜欢

转载自www.cnblogs.com/andy0816/p/12302075.html