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 1489找到最小生成树李关键边和伪关键边
    Leetcode 113 路径总和 II
    hdu 1223 还是畅通工程
    hdu 1087 Super Jumping! Jumping! Jumping!
    hdu 1008 Elevator
    hdu 1037 Keep on Truckin'
    湖工oj 1241 畅通工程
    湖工oj 1162 大武汉局域网
    hdu 2057 A + B Again
    poj 2236 Wireless Network
  • 原文地址:https://www.cnblogs.com/andy0816/p/12302075.html
Copyright © 2011-2022 走看看