zl程序教程

您现在的位置是:首页 >  其他

当前栏目

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

接口项目 相关 支付 购物车
2023-09-14 09:00:33 时间

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

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

 

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