zoukankan      html  css  js  c++  java
  • 基于rest_framework和redis实现购物车的操作,结算,支付

    前奏:

      首先,要在主机中安装redis,windows中安装,下载一个镜像,直接进行下一步的安装,安装成功后,在cmd中输入redis-cli

      安装python的依赖库: redis     和   django-redis

      redis是一个python的库,用来操作redis。

      django默认支持的缓存是memcache,使用redis作为django的缓存,就需要django-redis了,django-redis是一个开源的库。

    只需要在全局settings中配置即可。

    django-redis的配置使用

    在settings.py中的配置:

    CACHES = {
        "default": {
            "BACKEND": "django_redis.cache.RedisCache",
            "LOCATION": "redis://127.0.0.1:6379",
            "OPTIONS": {
                "CLIENT_CLASS": "django_redis.client.DefaultClient",
                "CONNECTION_POOL_KWARGS": {"max_connections": 100}
                # "PASSWORD": "密码",
            }
        }
      "可自定义配置":{...} }
    在视图中的使用:
    from django_redis import get_redis_connection
    conn = get_redis_connection("default")

      安装配置后就可以使用了,但是据说性能不够高,官方文档见https://niwinz.github.io/django-redis/latest/

     异常错误信息的自定义

    # 自定义一个类名,这个类继承Exception即可
    class PriceNoExistsError(Exception):
    
        def __init__(self,msg):
            self.msg = msg

    模型表数据存储的校验

    models.py中模型表可以在数据保存前做数据校验,重写save方法即可

    比如:校验优惠券存储的开始和结束时间

    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 = models.IntegerField(verbose_name="等值货币",blank=True,null=True)
        off_percent = models.PositiveSmallIntegerField("折扣百分比", help_text="只针对折扣券,例7.9折,写79", blank=True, null=True)
        minimum_consume = models.PositiveIntegerField("最低消费", default=0, help_text="仅在满减券时填写此字段",blank=True,null=True)
    
        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)
    
        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)

    对于购物车的操作,结算,支付等部分视图函数做登录认证的处理

    借助rest_framework的局部认证功能

    from api import models
    from rest_framework.authentication import BaseAuthentication
    from rest_framework.exceptions import AuthenticationFailed
    
    
    class LoginAuth(BaseAuthentication):
    
        def authenticate(self, request):
            token = request.query_params.get('token')
            token_obj = models.Token.objects.filter(name=token).first()
            if token_obj:
                return token_obj.user,token_obj
            else:
                raise AuthenticationFailed('认证失败')

    封装响应数据的结构

    可以将所有视图函数返回给前端的响应信息封装到一个类中,什么时候需要直接实例化对象使用。

    class StatusRet(object):
        '''构建返回响应的结构''
    
        def __init__(self):
            self.code = 1000
            self.msg = ''
            self.data = None
    
        @property
        def dict(self):
            return self.__dict__

    借助admin快速添加数据

    from django.contrib import admin
    
    # Register your models here.
    
    from django.apps import apps
    
    all_model_gene =apps.get_app_config('api').get_models()   # 将所有的模型类放在生成器中
    for model_cls in all_model_gene:
        admin.site.register(model_cls)
    from django.contrib import admin

    from
    django.apps import apps app_models =apps.get_app_config("api").get_models() from django.contrib.admin.sites import AlreadyRegistered for i in app_models: try: admin.site.register(i) except AlreadyRegistered: pass

    购物车的增删改查:

    1.url的设计

      url(r'^shopping_car/',views.ShoppingCar.as_view()),2.视图类的处理

    redis中数据的存储格式:  通过这种name的形式来存储 哪个用户的哪门课程信息

    SHOP_CAR_COURSE_KEY = 'shop_car_%s_%s'     # 第一个%s 代表当前的用户的主键id   第二个%s 代表某个课程的主键id

    具体的视图函数逻辑:

    class ShoppingCar(APIView):
        '''购物车的增删改查'''
    
        #  购物车的登录认证
        authentication_classes = [LoginAuth]
       conn
    = get_redis_connection('default') def get(self,request): '''获取当前用户在购物车中的所有记录''' # 获取返回值对象 ret = StatusRet() # 从redis中获取当前用户所有的key shop_car_key = settings.SHOP_CAR_COURSE_KEY % (request.user.pk, '*') # 利用scan_iter 将当前用户所有的keys生成一个生成器 price_policy_gene = self.conn.scan_iter(shop_car_key) # 构建接口数据 ''' [ { key1:{ title:...,price:... }}, { key2:{... }},... ] ''' price_policy_list = [] for keys in price_policy_gene: data_dict = { 'title': self.conn.hget(keys,'title').decode(), 'price': self.conn.hget(keys,'price').decode(), 'course_img': self.conn.hget(keys,'course_img').decode(), 'price_policy': json.loads(self.conn.hget(keys,'price_policy').decode()), # 直接json反序列化 'checked_price_policy_id': self.conn.hget(keys,'checked_price_policy_id').decode() } price_policy_list.append(data_dict) # 将查询的所有数据返回 ret.data = price_policy_list return Response(ret.dict) def post(self,request): '''添加购物车记录''' ret = StatusRet() # 获取从前端发来的数据 course_id : 课程的pk值 price_policy_id : 价格策略的pk值 course_id = request.data.get('course_id') price_policy_id = request.data.get('price_policy_id') # 校验前端传来数据的合法性 try: course_obj = models.Courses.objects.get(pk=course_id) price_policy_list = course_obj.price_policy.all() price_policy_dict = {} for item in price_policy_list: price_policy_dict[item.pk] = {"name":item.name,"price":float(item.price)} if int(price_policy_id) not in price_policy_dict: raise PriceNoExistsError('价格策略不存在') shop_car_key = settings.SHOP_CAR_COURSE_KEY%(request.user.pk,course_id) # 构建数据 val_dict = { "title":course_obj.title, "price":course_obj.price, "course_img":course_obj.course_img, "price_policy":json.dumps(price_policy_dict,ensure_ascii=False), #提前将数据json序列化,便于后期的取值 "checked_price_policy_id":price_policy_id } # 将数据添加到redis中 self.conn.hmset(shop_car_key,val_dict) ret.data = val_dict except PriceNoExistsError as e: ret.code = 2000 ret.msg = str(e) except ObjectDoesNotExist as e: ret.code = 2000 ret.msg = '课程不存在' except Exception as e: ret.code = 2000 ret.msg = '出错了' return Response(ret.dict) def put(self,request): '''修改购物车记录''' ret = StatusRet() # 获取前端传来的数据 course_id = request.data.get('course_id') price_policy_id = request.data.get('price_policy_id') # 校验数据的合法性 try: shop_car_key = settings.SHOP_CAR_COURSE_KEY % (request.user.pk, course_id) if not self.conn.exists(shop_car_key): raise CourseNoExistsErrot('修改课程不存在') course_data = self.conn.hgetall(shop_car_key) course_policy = json.loads(self.conn.hget(shop_car_key,"price_policy").decode()) if str(price_policy_id) not in course_policy: raise PriceNoExistsError('价格策略不存在') # 将redis中的值进行修改 self.conn.hset(shop_car_key,'checked_price_policy_id',price_policy_id) # 构建返回给前端的数据 return_data = {} for k,v in course_data.items(): if k == b"price_policy": val = json.loads(v.decode()) else: val = v.decode() return_data[k.decode()] = val ret.data = return_data except CourseNoExistsErrot as e: ret.code = 2000 ret.msg = str(e) except PriceNoExistsError as e: ret.code = 2000 ret.msg = str(e) except Exception as e: ret.code = 2000 ret.msg = '更新失败' return Response(ret.dict) def delete(self,request): '''删除购物车记录''' ret = StatusRet() # 校验前端传来数据的合法性 try: course_id_list = request.data.get('course_id') shop_key_list =[] for course_id in course_id_list: shop_car_key = settings.SHOP_CAR_COURSE_KEY % (request.user.pk, course_id) if not self.conn.exists(shop_car_key): raise CourseNoExistsErrot('删除课程不存在') else: # 将所有校验合法的key存在列表中 shop_key_list.append(shop_car_key) # 从redis中删除记录 self.conn.delete(*shop_key_list) ret.data = '' except CourseNoExistsErrot as e: ret.code = 2000 ret.msg = str(e) return Response(ret.dict)

     备注:使用json进行序列化时,如果字段类型为decimal时,要先将数据转为float类型,否则json无法序列化,json不支持decimal的数据类型。

    结算页面

    购物车中增删改查实现后,就是结算页面了,结算包含两种情况,一种是购物车中选中商品后的直接结算,是post请求,将该商品的所有信息和所有可用的优惠券(包含专用的和通用的),以及其他,比如散币(腾讯的Q币,淘宝的积分,等等)查询出来后存入redis中,并将数据构建后返回。另一种是用户选择结算后的延迟,在此使用时,可以通过未支付页面的get请求,直接从redis中取数据,并返回给前端的展示。

    1.url的构建

        url(r'^settle_center/',views.SettlementView.as_view()),

    2.redis中的存储方式:

    PAYMENT_COURSE_KEY = 'payment_%s_%s'  #课程专用优惠券
    PAYMENT_COMMON_KEY = 'pay_common_%s'    #通用优惠券
    USER_BALANCE_COUNT = 'balance_count_%s'   #用户贝里数

    3.视图逻辑

    class SettlementView(APIView):
        '''结算的逻辑操作'''
        authentication_classes = [LoginAuth]  #登录认证
        conn = get_redis_connection('default')   #redis的连接
    
        def get_return_data(self,request):
            '''构建返回给前端的数据'''
    
            #获取用户pk值
            user_id = request.user.pk
            #获取redis中该用户的贝里数
            user_balance_key = settings.USER_BALANCE_COUNT
            user_balance_count = user_balance_key % user_id
            balance_data = self.conn.hgetall(user_balance_count)
            # 获取redis中该用户的所有课程专用优惠券
            payment_key = settings.PAYMENT_COURSE_KEY % (user_id, '*')
            keys = self.conn.scan_iter(payment_key)
            #获取redis中该用户的通用优惠券
            common_key = settings.PAYMENT_COMMON_KEY % user_id
            common_data = self.conn.hgetall(common_key)
    
            return_data = []
            # 构建贝里数数据
            balance_dict = {}
            for k,v in balance_data.items():
                balance_dict[k.decode()] = v.decode()
            # 构建通用优惠券的信息
            common_show_dict = {}
            for com_k, com_v in common_data.items():
                common_show_dict[com_k.decode()] = json.loads(com_v.decode())
    
            # 处理普通优惠券的数据
            coupon_dict = {}
            for key in keys:
                course_id = key.decode().split('_')[-1]
                coupon_dict[course_id] = {}
                course_coupon_data = self.conn.hgetall(key)
                for cou_k, cou_v in course_coupon_data.items():
                    if cou_k == b'coupons':
                        coupons_data = {}
                        for coup_k, coup_v in json.loads(cou_v.decode()).items():
                            coupons_data[coup_k] = json.loads(coup_v)
                        coupon_dict[course_id][cou_k.decode()] = coupons_data
                    else:
                        coupon_dict[course_id][cou_k.decode()] = json.loads(cou_v.decode())
    
            return_data.append(balance_dict)
            return_data.append(common_show_dict)
            return_data.append(coupon_dict)
    
            return return_data
    
        def get(self,request):
            '''获取redis中当前登录用户的所有的待支付信息'''
            ret = StatusRet()
            return_data = self.get_return_data(request)
    
            ret.data = return_data
    
            return Response(ret.dict)
    
        def post(self,request):
            '''将用户选择的商品信息存储到redis中'''
            ret = StatusRet()
    
            # 获取前端发送的所有选中的数据
            course_id_list = request.data.get('course_id')
            for course_id in course_id_list:
                shop_car_key = settings.SHOP_CAR_COURSE_KEY%(request.user.pk,course_id)
                # 校验数据是不是在购物车中
                if not self.conn.exists(shop_car_key):
                    ret.code = 2000
                    ret.msg = '课程不存在'
                    return Response(ret.dict)
                else:
                    # 校验通过后  获取当前的时间
                    now = datetime.datetime.now()
                    # 获取选中课程在购物车中的详细信息
                    course_price_data = self.conn.hgetall(shop_car_key)
                    # 将取出的数据进行重新构建
                    course_detail = {}
                    for key,val in course_price_data.items():
                        if key == b'price_policy':
                            course_detail[key.decode()] = json.loads(val.decode())
                        else:
                            course_detail[key.decode()]=val.decode()
                    # 构建存进redis中的数据
                    coupon_dict = {}
                    common_dict = {}
                    # 设置全局的存储name的格式
                    pay_course_settings = settings.PAYMENT_COURSE_KEY
                    payment_key =pay_course_settings%(request.user.pk,course_id)
                    # 获取当前用户在优惠券领取记录表中所有的未使用,并且未过期的优惠券
                    couponrecord_list = models.CouponRecord.objects.filter(user=request.user,status=0,coupon__valid_begin_date__lte=now,coupon__valid_end_date__gte=now,number__gte=1,coupon__object_id=course_id)
    
                    for coupon_record_obj in couponrecord_list:
                        coupon_obj = coupon_record_obj.coupon
                        temp = {
                            'name':coupon_obj.name,
                            'coupon_type':coupon_obj.coupon_type,
                            'money_equivalent_value':coupon_obj.money_equivalent_value,
                            'off_percent':coupon_obj.off_percent,
                            'minimum_consume':coupon_obj.minimum_consume,
                            'object_id':coupon_obj.object_id
                        }
                        # 判断是通用优惠券还是某个课程的专用优惠券
                        if not coupon_obj.object_id:
                            common_dict[coupon_record_obj.pk] = json.dumps(temp,ensure_ascii=False)
                        else:
                            coupon_dict[coupon_record_obj.pk] = json.dumps(temp,ensure_ascii=False)
                    val_dict = {}
                    val_dict['course_detail'] = json.dumps(course_detail,ensure_ascii=False)
    
                    val_dict['coupons'] = json.dumps(coupon_dict,ensure_ascii=False)
                    self.conn.hmset(payment_key,val_dict)
                    common_settint_key = settings.PAYMENT_COMMON_KEY
                    common_key = common_settint_key%(request.user.pk)
                    # 如果没有通用优惠券,在存储redis时会报错,因为存储的value不能为一个空字典
                    if common_dict:
                        self.conn.hmset(common_key,common_dict)
            #
            # 获取当前用户的贝里数
            balance = request.user.beli_count
            balance_dict = {'balance': balance}
            user_balance_key = settings.USER_BALANCE_COUNT
            user_balance_count = user_balance_key%request.user.pk
            self.conn.hmset(user_balance_count,balance_dict)
    
            # 获取返回前端的数据
            return_data = self.get_return_data(request)
            ret.data = return_data
    
            return Response(ret.dict)

    支付页面

    结算完毕就该支付页面了,用户点击支付后,会将所有要支付的数据发回来,进行数据的校验,校验通过后,生成order订单表(或订单详情表),然后就可以调用支付的接口

    1.url的构建

    url(r'^payment/',views.PaymentViewSet.as_view()),

    2.逻辑的实现

      数据取出可以是从redis中取,也可以从数据库中取,进行校验,具体的实现粒度视情况

    class PaymentViewSet(APIView):
        authentication_classes = [LoginAuth]
        conn = get_redis_connection('default')
        now = datetime.datetime.now()
    
        def post(self,request):
            '''
            前端发送的数据结构:
            {
                "money":87.12,  # 总价格
                "balance":0,    # 贝里数
                "course_detail":  #课程详细   包含该课程选中的优惠券pk值,价格id,价格
                    {
                        "1":{"coupon_id":1,"policy_id":1,"price":99},
                        "2":{"coupon_id":5,"policy_id":4,"price":0}
                    },
                "common_coupon_id":4   # 通用优惠券的pk值
            }
            
            '''
            #获取数据
            ret = StatusRet()
    
            user_id = request.user.pk
            _balance = models.UserInfo.objects.filter(pk=user_id).first().beli_count
            course_detail = request.data.get("course_detail")
            money = request.data.get('money')
            currency_coupon_id = request.data.get('common_coupon_id')
            balance = request.data.get('balance')
            # 数据处理
            try:
                if balance>_balance:
                    raise OrderError('贝利数异常,支付失败')
                course_price_list = []
                for course_id,course_info in course_detail.items():
                    # 从redis中购物车中取数据校验
                    # pay_key = settings.PAYMENT_COURSE_KEY%(user_id,course_id)
                    # pay_ret = self.conn.hgetall(pay_key)
                    # print(pay_ret,'pay_key')
                    # for key,value in pay_ret.items():
                    #     # key = key.decode()
                    #     value = json.loads(value.decode())
                    #     print(key,value)
                    #     if key == b'coupons':
                    #         if course_info['coupon_id'] not in value:
                    #             raise OrderError('优惠券不存在')
                    #     elif key == b'course_detail':
                    #         for key_course,value_course in value.items():
                    #             if key_course == 'title':
                    #                 pass
                    #             elif key_course == 'checked_price_policy_id':
                    #                 if value_course != str(course_info['policy_id']):
                    #                     raise OrderError('选中的价格策略异常')
                    #             elif key_course == 'price_policy':
                    #                 # print(value_course,type(value_course))
                    #                 course_price = value_course[str(course_info['policy_id'])]['price']
                    #                 if course_price != course_info['price']:
                    #                     raise OrderError('课程价格异常')
                    #                 course_price_list.append(course_price)
    
                                    
                    # 数据库中取数据校验
                    course_obj = models.Courses.objects.filter(pk=course_id).first()
                    if not course_obj:
                        raise OrderError('课程不存在')
                    if course_obj.status !=0:
                        raise OrderError('课程已下架')
                    policy_list = list(course_obj.price_policy.all().values('pk','price'))
                    # print(type(course_info['policy_id']))
                    if course_info['policy_id'] not in [i['pk'] for i in policy_list]:
                        raise OrderError('价格策略不存在')
                    print(policy_list, '价格策略列表')
                    print([float(i['price']) for i in policy_list if i['pk'] ==course_info['policy_id'] ])
                    policy_price = [float(i['price']) for i in policy_list if i['pk'] ==course_info['policy_id']][0]
                    if policy_price != course_info['price']:
                        raise OrderError('价格异常')
    
                    # 更细粒度的校验实现
                    # xxx = models.CouponRecord.objects.filter(pk=course_info['coupon_id'],user_id=user_id).first()
                    # if not xxx:
                    #     raise OrderError('优惠券不存在')
                    # if xxx.status !=0:
                    #     raise OrderError('优惠券未上架')
                    # if int(xxx.number) <1:
                    #     raise OrderError('优惠券数量不够')
                    #  ...
    
                    if course_info['coupon_id']:
                        coupon_obj = models.CouponRecord.objects.filter(pk=course_info['coupon_id'],user_id=user_id,status=0,number__gte =1,coupon__valid_begin_date__lte=self.now,coupon__valid_end_date__gte=self.now,coupon__object_id=course_id).first()
                        if not coupon_obj:
                            raise OrderError('优惠券异常,支付失败')
                    # 获取每个课程的价格
                        price_course = self.account_price(coupon_obj,policy_price)
                    else:
                        price_course = policy_price
                
                    course_price_list.append(price_course)
               
                # 获取价格总和
                course_price_total = sum(course_price_list)
                # 通用优惠券的校验和使用
                if currency_coupon_id:
                    currency_coupon_obj = models.CouponRecord.objects.filter(pk=currency_coupon_id,user_id=user_id,status=0,number__gte =1,coupon__valid_begin_date__lte=self.now,coupon__valid_end_date__gte=self.now).first()
                    if not currency_coupon_obj:
                        raise OrderError('通用优惠卷异常')
                    course_total = self.account_price(currency_coupon_obj,course_price_total)
                else:
                    course_total = course_price_total
                # 贝里数的扣减
                if balance>0:
                    _finally_price = course_total- balance/100
                else:
                    _finally_price = course_total
                finally_price = round(_finally_price,2)
                # 总价格的校验
                if finally_price != money:
                    raise OrderError('总价格异常')
    
                # 生成订单信息
    
                order_obj = models.Order.objects.create(payment_type=1,order_number=self.get_random_order(),actual_amount=finally_price,user_id=user_id,status=1)
                # models.OrderDetail.objects.create(order=order_obj,original_price=course_price_total,price=finally_price,valid_period_display='课程有效期',valid_period=100,content_type=)
    
                # 调用支付接口
                # xxxxxxxxxxxxxxxxxxxxx
    
            except OrderError as e:
                ret.code = 2000
                ret.msg = str(e)
    
            return Response(ret.dict)
    
        def account_price(self, coupon_record_obj, price):
            """
            根据优惠券记录对象,以及优惠前的价格 计算出使用优惠券的价格
            """
            coupon_type = coupon_record_obj.coupon.coupon_type
            if coupon_type == 0:
                money_equivalent_value = coupon_record_obj.coupon.money_equivalent_value
                # --立减
                # 0 = 获取原价 - 优惠券金额   或
                # 折后价格 = 获取原价 - 优惠券金额
                if price - money_equivalent_value >= 0:
                    rebate_price = price - money_equivalent_value
                else:
                    rebate_price = 0
            elif coupon_type == 1:
                # -- 满减 是否满足限制
                minimum_consume = coupon_record_obj.coupon.minimum_consume
                if price >= minimum_consume:
                    # 折后价格 = 获取原价 - 优惠券金额
                    money_equivalent_value = coupon_record_obj.coupon.money_equivalent_value
                    rebate_price = price - money_equivalent_value
                else:
                    return price
            elif coupon_type == 2:
                # -- 折扣
                off_percent = coupon_record_obj.coupon.off_percent
                # 折后价格 = 获取原价* 80/100
                rebate_price = price * (off_percent / 100)
            else:
                rebate_price = price
            return rebate_price
    
        def get_random_order(self):
            '''生成随机订单号'''
            import random
            time_current = self.now.strftime('%Y%m%d%H%M%S')
            random_str = ''.join([str(random.randint(0,9)) for i in range(4)])
            return f'{time_current}{random_str}'

    附表信息:

    from datetime import datetime
    from django.db import models
    
    from django.contrib.contenttypes.models import ContentType
    from django.contrib.contenttypes.fields import GenericRelation,GenericForeignKey
    
    
    # Create your models here.
    
    
    
    class Courses(models.Model):
        title = models.CharField(max_length=64)
        price = models.DecimalField(max_digits=8,decimal_places=2)
        classes = ((1,'初级'),(2,'中级'),(3,'高级'))
        course_class = models.SmallIntegerField(choices=classes)
        course_img = models.CharField(max_length=128)
        status_choices = ((0, '上线'), (1, '下线'), (2, '预上线'))
        status = models.SmallIntegerField(choices=status_choices, default=0,help_text='课程的状态')
    
        price_policy = GenericRelation(to='PricePolicy')
    
        questions = GenericRelation(to='OftenQuestion')
    
        coupons = GenericRelation(to='Coupon')
    
        def __str__(self):
            return self.title
    
    
    class CourseDetail(models.Model):
        course = models.OneToOneField(to='Courses')
        slogen = models.CharField(max_length=128)
        desc = models.CharField(max_length=512)
        hours = models.IntegerField('课时',default=10)
        recommend_courses = models.ManyToManyField(to='Courses',related_name="recourse")
    
        def __str__(self):
            return self.course.title
    
    
    class ChapterList(models.Model):
        '''课程章节'''
        num = models.SmallIntegerField()
        name = models.CharField(max_length=64)
        course = models.ForeignKey(to='Courses',on_delete=models.CASCADE)
    
        def __str__(self):
            return "%s:(第%s章)%s"%(self.course,self.num,self.name)
    
        class Meta:
            unique_together = ('num','course')
    
    
    class CourseSection(models.Model):
        '''课时目录'''
        name = models.CharField(max_length=64)
        charpter = models.ForeignKey(to='ChapterList')
    
        def __str__(self):
            return self.name
    
    
    class UserInfo(models.Model):
        name = models.CharField(max_length=16)
        password = models.CharField(max_length=32)
    
        beli_count = models.IntegerField(default=0)
    
    
        def __str__(self):
            return self.name
    
    
    class Token(models.Model):
        name = models.CharField(max_length=128)
        user = models.OneToOneField(to='UserInfo')
    
        def __str__(self):
            return self.name
    
    
    class PricePolicy(models.Model):
        '''价格策略表'''
    
        name = models.CharField(max_length=128)
        price = models.DecimalField(max_digits=6,decimal_places=2,default=199)
    
        content_type = models.ForeignKey(to=ContentType)
        object_id = models.PositiveIntegerField()
        content_obj = GenericForeignKey('content_type','object_id')
    
        def __str__(self):
            return self.name
    
        class Meta:
            unique_together = ('content_type','object_id','name')
    
    
    class OftenQuestion(models.Model):
        question = models.CharField(max_length=128)
        answer = models.TextField()
    
        content_type = models.ForeignKey(to=ContentType)
        object_id = models.PositiveIntegerField()
        content_obj = GenericForeignKey('content_type','object_id')
    
        def __str__(self):
            return self.question
    
        class Meta:
            unique_together = ('content_type','object_id','question')
    
    
    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 = models.IntegerField(verbose_name="等值货币",blank=True,null=True)
        off_percent = models.PositiveSmallIntegerField("折扣百分比", help_text="只针对折扣券,例7.9折,写79", blank=True, null=True)
        minimum_consume = models.PositiveIntegerField("最低消费", default=0, help_text="仅在满减券时填写此字段",blank=True,null=True)
    
        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)
    
        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)
        user = 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.user, self.number, self.status)
    
    
    class Order(models.Model):
        """订单"""
        payment_type_choices = ((0, '微信'), (1, '支付宝'), (2, '优惠码'), (3, '贝里'))
        payment_type = models.SmallIntegerField(choices=payment_type_choices)
        payment_number = models.CharField(max_length=128, verbose_name="支付第3方订单号", null=True, blank=True)
        order_number = models.CharField(max_length=128, verbose_name="订单号", unique=True)  # 考虑到订单合并支付的问题
        user = models.ForeignKey("UserInfo")
        actual_amount = models.FloatField(verbose_name="实付金额")
    
        status_choices = ((0, '交易成功'), (1, '待支付'), (2, '退费申请中'), (3, '已退费'), (4, '主动取消'), (5, '超时取消'))
        status = models.SmallIntegerField(choices=status_choices, verbose_name="状态")
        date = models.DateTimeField(auto_now_add=True, verbose_name="订单生成时间")
        pay_time = models.DateTimeField(blank=True, null=True, verbose_name="付款时间")
        cancel_time = models.DateTimeField(blank=True, null=True, verbose_name="订单取消时间")
    
        class Meta:
            verbose_name_plural = "37. 订单表"
    
        def __str__(self):
            return "%s" % self.order_number
    
    
    class OrderDetail(models.Model):
        """订单详情"""
        order = models.ForeignKey("Order")
    
    
        content_type = models.ForeignKey(ContentType)  # 可关联普通课程或学位
        object_id = models.PositiveIntegerField()
        content_object = GenericForeignKey('content_type', 'object_id')
    
        original_price = models.FloatField("课程原价")
        price = models.FloatField("折后价格")
        content = models.CharField(max_length=255, blank=True, null=True)  #
        valid_period_display = models.CharField("有效期显示", max_length=32)  # 在订单页显示
        valid_period = models.PositiveIntegerField("有效期(days)")  # 课程有效期
        memo = models.CharField(max_length=255, blank=True, null=True)
    
        def __str__(self):
            return "%s - %s - %s" % (self.order, self.content_type, self.price)
    
        class Meta:
            verbose_name_plural = "38. 订单详细"
            unique_together = ("order", 'content_type', 'object_id')
    models.py
  • 相关阅读:
    centos 安装 TortoiseSVN svn 客户端
    linux 定时任务 日志记录
    centos6.5 安装PHP7.0支持nginx
    linux root 用户 定时任务添加
    composer 一些使用说明
    laravel cookie写入
    laravel composer 安装指定版本以及基本的配置
    mysql 删除重复记录语句
    linux php redis 扩展安装
    linux php 安装 memcache 扩展
  • 原文地址:https://www.cnblogs.com/zhaopanpan/p/9295835.html
Copyright © 2011-2022 走看看