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

      ###############    结算接口分析   ################

    """
    结算
    一,基于购物车的结算
    现在购物车里面有什么内容了,
    每一个用户的课程信息,包括标题,图片,所有的价格策略,选中的价格策略,
    注意:
    1,在结算页面不允许再修改价格策略,
    2,结算信息只需要选中的价格策略,不需要其他的价格策略了。
    3,点击购物车页面的去结算,是做了两个事情,1是加入结算信息,2是跳转结算页面,,
    4,这个地方的难点在于优惠券,可以选择优惠券,
    
    二,优惠券的分析
    优惠券有三种
    1,满减优惠券
    2,通用优惠券,
    3,折扣优惠券,
    优惠券需要两张表,
    1,一个优惠券生成规则表,通用类型需要一个字段等值货币,折扣类型需要折扣比例字段,满减需要最低消费字段,
    contenttype可以绑定专题课和学位课,
    2,一个发放和消费记录表,需要有拥有者和状态,优惠券需要和订单关联起来,就是哪一个订单用的,
    
    三,存储结算信息的数据结构
    
    第一种数据结构:
    account_userid_courseid{
        "course_id":{
             "title":
              "img":
              "policy_id":default_policy,
              period : 60
              period_display: 2个月
              price : 99
              coupon: {
                1:{
                    'coupon_type':
                    'coupon_display':
                    'money_equivalent_value'
                    'minimum_consume'
                    'off_percent'
                    }
              }
               default_coupon":0
           }
      payment_global_coupon_用户ID: {
      'coupon': {   # 这个字典存储用户所有可用的通用优惠券
          2: {'money_equivalent_value': 200},  # 优惠券 ID :{优惠券信息}
      },
      'default_coupon': 0
     }
    
    }
    
    第二种数据结构,
    account_userid_courseid:{
        course_info: {  # 这个字典存储用户所有课程信息
          id:
          title:
          img:
          price:{  # 实际这个价格策略不需要这么多,只需要选中的价格策略就可以了,因为结算页面不允许修改价格策略,全都放进来是好放,直接redis获取到然后直接放进来
              1:
              2:
          }
          default_price:
            }
        coupon:{  # 这个字典存储用户所有可用的课程优惠券
            1:{}
            2:{}
               default_coupon_id :1  # 初始进来这个默认是空的,选择了一个前端发put请求,然后后端直接改掉,这就是修改优惠券信息,
        }
      payment_global_coupon_用户ID: {
          'coupon': {   # 这个字典存储用户所有可用的通用优惠券
              2: {'money_equivalent_value': 200},  # 优惠券 ID :{优惠券信息}
          },
          'default_coupon': 0
     }
    }
    
    结算接口的业务逻辑
    ################################################
    post请求:
    前端:
    1,操作的地方:在购物车页面,选中购物车中的商品,点击去结算,
    2,点击需要传的参数:只课程id,这是一个列表的形式,
    请求体:{
        courseids :[1,2]
    }
    不需要再传策略id了,因为购物车里面有一个default_policy_id,这样后端也剩下了一步校验,
    不需要发价格,价格购物车也有,
    
    后端:
    1,获取前端传过来的课程id,
      每次进来之后,都要清空这个客户的所有的结算信息,
    2,获取所有的课程信息放到结算key中,可能会有多个课程
      2.1 拼接shopping_car_key,
      2.2 判断这个 shopping_car_key 是否存在,要学习怎么写异常,
      2.3 获取到redis中的课程信息,
      2.4 放入account_key中,
    3,获取到这个客户这个课程的所有的优惠券信息,然后放入account_key中
      3.1 根据课程id,userid,状态,开始时间,结束时间,object_id,content_type_id,查询符合条件的优惠券信息
      3.2 查到之后这是一个集合,需要遍历然后放入account_key中的coupon中,可能会有多个,
    4,获取通用的优惠券信息,
      4.1 然后放入一个新的通用券key中,
    5,返回数据
    
    ##################################################
    get请求
    需要传给前端,
    1,课程信息
    2,课程优惠券信息
    3,全局优惠券信息,
    
    #####################################################
    put请求,选择优惠券
    前端请求参数:
    {
    course_id : 1 # 如果是通用优惠券这个值就是空,
    coupon_id : 1
    }
    后端,
    1,对于传过来的数据,需要课程校验是否存在,优惠券校验是否存在,
    2,然后修改redis数据,本质就是redis的操作问题了,
    
    注意:
    1,优惠券的状态
    2,优惠券的起始时间,
    
    
    
    """

      

      ###############    结算接口相关的表   ################

    class Coupon(models.Model):
        """优惠券生成规则"""
        name = models.CharField(max_length=64, verbose_name="活动名称")
        brief = models.TextField(blank=True, null=True, verbose_name="优惠券介绍")
        coupon_type_choices = ((0, '通用券'), (1, '满减券'), (2, '折扣券'))
        coupon_type = models.SmallIntegerField(choices=coupon_type_choices, default=0, verbose_name="券类型")
    
        """
        通用:
            money_equivalent_value=100
            off_percent=null
            minimum_consume=0
        满减:
            money_equivalent_value=100
            off_percent=null
            minimum_consume=1000
        折扣:
            money_equivalent_value=0
            off_percent=79
            minimum_consume=0
        """
        money_equivalent_value = models.IntegerField(verbose_name="等值货币")
        off_percent = models.PositiveSmallIntegerField(verbose_name="折扣百分比", help_text="只针对折扣券,例7.9折,写79", blank=True,
                                                       null=True)
        minimum_consume = models.PositiveIntegerField(verbose_name="最低消费", default=0, help_text="仅在满减券时填写此字段")
    
        content_type = models.ForeignKey(ContentType, blank=True, null=True)
        object_id = models.PositiveIntegerField("绑定课程", blank=True, null=True, help_text="可以把优惠券跟课程绑定")
        content_object = GenericForeignKey('content_type', 'object_id')
    
        quantity = models.PositiveIntegerField("数量(张)", default=1)
        open_date = models.DateField("优惠券领取开始时间")
        close_date = models.DateField("优惠券领取结束时间")
        valid_begin_date = models.DateField(verbose_name="有效期开始时间", blank=True, null=True)
        valid_end_date = models.DateField(verbose_name="有效结束时间", blank=True, null=True)
        coupon_valid_days = models.PositiveIntegerField(verbose_name="优惠券有效期(天)", blank=True, null=True,
                                                        help_text="自券被领时开始算起")
        date = models.DateTimeField(auto_now_add=True, verbose_name="创建优惠券的日期")
    
        class Meta:
            verbose_name_plural = "31. 优惠券生成记录"
    
        def __str__(self):
            return "%s(%s)" % (self.get_coupon_type_display(), self.name)
    
        def save(self, *args, **kwargs):
            if not self.coupon_valid_days or (self.valid_begin_date and self.valid_end_date):
                if self.valid_begin_date and self.valid_end_date:
                    if self.valid_end_date <= self.valid_begin_date:
                        raise ValueError("valid_end_date 有效期结束日期必须晚于 valid_begin_date ")
                if self.coupon_valid_days == 0:
                    raise ValueError("coupon_valid_days 有效期不能为0")
            if self.close_date < self.open_date:
                raise ValueError("close_date 优惠券领取结束时间必须晚于 open_date优惠券领取开始时间 ")
    
            super(Coupon, self).save(*args, **kwargs)
    
    
    
    class CouponRecord(models.Model):
        """优惠券发放、消费纪录"""
        coupon = models.ForeignKey("Coupon")
        number = models.CharField(max_length=64, unique=True, verbose_name="优惠券的编号")
        account = models.ForeignKey("UserInfo", verbose_name="拥有者")
        status_choices = ((0, '未使用'), (1, '已使用'), (2, '已过期'))
        status = models.SmallIntegerField(choices=status_choices, default=0)
        get_time = models.DateTimeField(verbose_name="领取时间", help_text="用户领取时间")
        used_time = models.DateTimeField(blank=True, null=True, verbose_name="使用时间")
    
        # order = models.ForeignKey("Order", blank=True, null=True, verbose_name="关联订单")  # 一个订单可以有多个优惠券
    
        class Meta:
            verbose_name_plural = "32. 用户优惠券"
    
        def __str__(self):
            return '%s-%s-%s' % (self.account, self.number, self.status)

       ###############    结算接口第一种结构   ################

    from rest_framework.views import APIView
    from rest_framework.response import Response
    from utils.auth import LuffyAuth
    from django.conf import settings
    from django_redis import get_redis_connection
    import json
    from utils.response import BaseResponse
    from api import models
    import datetime
    
    class PaymentViewSet(APIView):
    
        authentication_classes =  [LuffyAuth,]
        conn = get_redis_connection("default")
    
        def post(self,request,*args,**kwargs):
            ret = BaseResponse()
            try:
                # 清空当前用户结算中心的数据
                # luffy_payment_1_*
                # luffy_payment_coupon_1
                key_list = self.conn.keys( settings.PAYMENT_KEY %(request.auth.user_id,"*",) )
                key_list.append(settings.PAYMENT_COUPON_KEY %(request.auth.user_id,))
                self.conn.delete(*key_list)
    
                payment_dict = {}  # 这里保存课程信息,和课程优惠券,
    
                global_coupon_dict = {
                    "coupon":{},
                    "default_coupon":0
                }
    
                # 1. 获取用户要结算课程ID
                course_id_list = request.data.get('courseids')
                for course_id in course_id_list:
                    car_key = settings.SHOPPING_CAR_KEY %(request.auth.user_id,course_id,)
    
                    # 1.1 检测用户要结算的课程是否已经加入购物车
                    if not self.conn.exists(car_key):
                        ret.code = 1001
                        ret.error = "课程需要先加入购物车才能结算"
                    # 1.2 从购物车中获取信息,放入到结算中心。
                    # 获取标题和图片
                    policy = json.loads(self.conn.hget(car_key, 'policy').decode('utf-8'))
                    default_policy = self.conn.hget(car_key, 'default_policy').decode('utf-8')
                    policy_info = policy[default_policy]  # 这也是一个字典,里面有期限,期限display,价格,
    
                    payment_course_dict = {
                        "course_id":str(course_id),
                        "title":self.conn.hget(car_key, 'title').decode('utf-8'),
                        "img":self.conn.hget(car_key, 'img').decode('utf-8'),
                        "policy_id":default_policy,
                        "coupon":{},
                        "default_coupon":0
                    }
                    payment_course_dict.update(policy_info)  # 把字典policy_info中的字段更新到payment_course_dict这个字典中,
                    payment_dict[str(course_id)] = payment_course_dict
    
    
                # 2. 获取优惠券
                ctime = datetime.date.today()
    
                coupon_list = models.CouponRecord.objects.filter(
                    account=request.auth.user,
                    status=0,
                    coupon__valid_begin_date__lte=ctime,
                    coupon__valid_end_date__gte=ctime,
                )
    
    
                for item in coupon_list:  # item是获取到的每一个优惠券,
    
    
                    # 只处理绑定课程的优惠券
                    if not item.coupon.object_id:  # 处理通用优惠券,
                        # 优惠券ID
                        coupon_id = item.id
    
                        # 优惠券类型:满减、折扣、立减
                        coupon_type = item.coupon.coupon_type
    
                        info = {}
                        info['coupon_type'] = coupon_type
                        info['coupon_display'] = item.coupon.get_coupon_type_display()
                        if coupon_type == 0:  # 立减
                            info['money_equivalent_value'] = item.coupon.money_equivalent_value
                        elif coupon_type == 1:  # 满减券
                            info['money_equivalent_value'] = item.coupon.money_equivalent_value
                            info['minimum_consume'] = item.coupon.minimum_consume
                        else:  # 折扣
                            info['off_percent'] = item.coupon.off_percent
    
                        global_coupon_dict['coupon'][coupon_id] = info
    
                        continue
                    # 优惠券绑定课程的ID
                    coupon_course_id = str(item.coupon.object_id)
    
                    # 优惠券ID
                    coupon_id = item.id
    
                    # 优惠券类型:满减、折扣、立减
                    coupon_type = item.coupon.coupon_type
    
                    info = {}
                    info['coupon_type'] = coupon_type
                    info['coupon_display'] = item.coupon.get_coupon_type_display()
                    if coupon_type == 0: # 立减
                        info['money_equivalent_value'] = item.coupon.money_equivalent_value
                    elif coupon_type == 1: # 满减券
                        info['money_equivalent_value'] = item.coupon.money_equivalent_value
                        info['minimum_consume'] = item.coupon.minimum_consume
                    else: # 折扣
                        info['off_percent'] = item.coupon.off_percent
    
                    if coupon_course_id not in payment_dict:
                        # 获取到优惠券,但是没有购买此课程
                        continue
                    # 将优惠券设置到指定的课程字典中
                    payment_dict[coupon_course_id]['coupon'][coupon_id] = info
    
                # 可以获取绑定的优惠券
    
                # 3. 将绑定优惠券课程+全站优惠券 写入到redis中(结算中心)。
                # 3.1 绑定优惠券课程放入redis
                for cid,cinfo in payment_dict.items():
                    pay_key = settings.PAYMENT_KEY %(request.auth.user_id,cid,)
                    cinfo['coupon'] = json.dumps(cinfo['coupon'])
                    self.conn.hmset(pay_key,cinfo)
                # 3.2 将全站优惠券写入redis
                gcoupon_key = settings.PAYMENT_COUPON_KEY %(request.auth.user_id,)
                global_coupon_dict['coupon'] = json.dumps(global_coupon_dict['coupon'])
                self.conn.hmset(gcoupon_key,global_coupon_dict)
    
            except Exception as e:
                pass
    
            return Response(ret.dict)
    
        def patch(self,request,*args,**kwargs):
    
            ret = BaseResponse()
            try:
                # 1. 用户提交要修改的优惠券
                course = request.data.get('courseid')
                course_id = str(course) if course else course
    
                coupon_id = str(request.data.get('couponid'))
    
                # payment_global_coupon_1
                redis_global_coupon_key = settings.PAYMENT_COUPON_KEY %(request.auth.user_id,)
    
                # 修改全站优惠券
                if not course_id:
                    if coupon_id == "0":
                        # 不使用优惠券,请求数据:{"couponid":0}
                        self.conn.hset(redis_global_coupon_key,'default_coupon',coupon_id)
                        ret.data = "修改成功"
                        return Response(ret.dict)
                    # 使用优惠券,请求数据:{"couponid":2}
                    coupon_dict = json.loads(self.conn.hget(redis_global_coupon_key,'coupon').decode('utf-8'))
    
                    # 判断用户选择得优惠券是否合法
                    if coupon_id not in coupon_dict:
                        ret.code = 1001
                        ret.error = "全站优惠券不存在"
                        return Response(ret.dict)
    
                    # 选择的优惠券合法
                    self.conn.hset(redis_global_coupon_key, 'default_coupon', coupon_id)
                    ret.data = "修改成功"
                    return Response(ret.dict)
    
                # 修改课程优惠券
                # luffy_payment_1_1
                redis_payment_key = settings.PAYMENT_KEY % (request.auth.user_id, course_id,)
                # 不使用优惠券
                if coupon_id == "0":
                    self.conn.hset(redis_payment_key,'default_coupon',coupon_id)
                    ret.data = "修改成功"
                    return Response(ret.dict)
    
                # 使用优惠券
                coupon_dict = json.loads(self.conn.hget(redis_payment_key,'coupon').decode('utf-8'))
                if coupon_id not in coupon_dict:
                    ret.code = 1010
                    ret.error = "课程优惠券不存在"
                    return Response(ret.dict)
    
                self.conn.hset(redis_payment_key,'default_coupon',coupon_id)
    
            except Exception as e:
                ret.code = 1111
                ret.error = "修改失败"
    
            return Response(ret.dict)
    
        def get(self,request,*args,**kwargs):
    
            ret = BaseResponse()
    
            try:
                # luffy_payment_1_*
                redis_payment_key = settings.PAYMENT_KEY %(request.auth.user_id,"*",)
    
                # luffy_payment_coupon_1
                redis_global_coupon_key = settings.PAYMENT_COUPON_KEY %(request.auth.user_id,)
    
                # 1. 获取绑定课程信息
                course_list = []
                for key in self.conn.scan_iter(redis_payment_key):
                    info = {}
                    data = self.conn.hgetall(key)
                    for k,v in data.items():
                        kk = k.decode('utf-8')
                        if kk == "coupon":
                            info[kk] = json.loads(v.decode('utf-8'))
                        else:
                            info[kk] = v.decode('utf-8')
                    course_list.append(info)
    
                # 2.全站优惠券
                global_coupon_dict = {
                    'coupon':json.loads(self.conn.hget(redis_global_coupon_key,'coupon').decode('utf-8')),
                    'default_coupon':self.conn.hget(redis_global_coupon_key,'default_coupon').decode('utf-8')
                }
    
                ret.data = {
                    "course_list":course_list,
                    "global_coupon_dict":global_coupon_dict
                }
            except Exception as e:
                ret.code = 1001
                ret.error = "获取失败"
    
            return Response(ret.dict)

       ###############    结算接口第二种结构  ################

    from api.exceptions import CommentException
    import datetime
    class AccountView(APIView):
        authentication_classes = [TokenAuth, ]
        conn = get_redis_connection('default')
    
        def post(self,request):
            res = BaseException()
            account_dict = {}
            try:
                # 1,获取前端的课程id
                course_list = request.data.get('course_id')
                user_id = request.user.id
                print(course_list,user_id)
    
                # 清空redis操作,把所有的结算数据全部清除
                del_list = self.conn.keys(settings.PAYMENT_COURSE_KEY.format(user_id, '*'))
                print(del_list)
                for key in del_list:
                    self.conn.delete(key)  # 删除该用户的课程、优惠券信息的key
                print('--------')
                for course_id in course_list:
                    # 2,判断购物车中是否有这个信息
                    shopping_car_id = settings.SHOPPING_CART_KEY.format(user_id,course_id)
                    print(shopping_car_id)
                    if not self.conn.exists(shopping_car_id):
                        raise CommentException(10033,'购物车不存在这个课程')
                    # 2.1 存在这个key,就把内容取出来然后放入 account_dict
                    account_dict['course_info'] = str(self.conn.hgetall(shopping_car_id))
                    account_dict['course_coupon'] = str(self.get_coupon_dict(request,course_id))
    
                    # 把数据存入redis
                    account_key = settings.PAYMENT_COURSE_KEY.format(user_id,course_id)
                    self.conn.set(account_key,json.dumps(account_dict))
    
                # 构建通用券
                global_key = settings.PAYMENT_GLOBAL_COUPON_KEY.format(user_id)
                self.conn.set(global_key,json.dumps(self.get_coupon_dict(request)))
                res.data='加入成功'
            except CommentException as e:
                print(e)
                res.code= e.code
                res.error = e.msg
            except Exception as e:
                print(e)
                res.code = 500
                res.error = '添加结算信息失败'
            return Response(res.dict)
    
        def get_coupon_dict(self,request,course_id=None):
            now = datetime.datetime.utcnow()
            # 3,把这个课程的优惠券拿出来,放进去account_dict
            coupon_list = models.CouponRecord.objects.filter(
                status=0,
                account=request.user,
                coupon__valid_begin_date__lte=now,
                coupon__valid_end_date__gte=now,
                # coupon__content_type_id= 这个值理论上需要加,因为会有多个,
                coupon__object_id=course_id
            )
            # 3.1 把优惠券的内容怎么放?放什么字段?这个地方就是对业务的理解问题了,
            coupon_dict = {}
            for coupon_item in coupon_list:
                coupon_dict[coupon_item.id] = {
                    "name": coupon_item.coupon.name,
                    "money_equivalent_value": coupon_item.coupon.money_equivalent_value,
                    "off_percent": coupon_item.coupon.off_percent,
                    "minimum_consume": coupon_item.coupon.minimum_consume,
                    "coupon_type": coupon_item.coupon.get_coupon_type_display(),
                    "valid_begin_date": coupon_item.coupon.valid_begin_date.strftime('%Y-%m-%d'),
                    "valid_end_date": coupon_item.coupon.valid_end_date.strftime('%Y-%m-%d'),
                }
            return coupon_dict

    ###############    结算接口   ################

    ###############    结算接口   ################

  • 相关阅读:
    JavaScript操作DOM对象
    QTP(13)
    QTP(12)
    QTP(11)
    QTP(10)
    QTP(9)
    QTP(8)
    QTP(7)
    QTP(6)
    QTP(5)
  • 原文地址:https://www.cnblogs.com/andy0816/p/12302095.html
Copyright © 2011-2022 走看看