zoukankan      html  css  js  c++  java
  • 路飞学城项目-支付相关-购物车接口

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

    """
    支付相关的需求:
    购物车需求:
    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__

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

  • 相关阅读:
    力扣(LeetCode) 14. 最长公共前缀
    力扣(LeetCode)965. 单值二叉树
    力扣(LeetCode)258. 各位相加
    力扣(LeetCode)389. 找不同
    PTA 阶乘之和取模
    A. Sea Battle
    PTA 二叉树路径
    PTA 重构二叉树
    PTA 笛卡尔树
    绿豆蛙的归宿
  • 原文地址:https://www.cnblogs.com/andy0816/p/12302075.html
Copyright © 2011-2022 走看看