结算中心模块

结算中心要开始选择优惠券了~~

表结构如下~~

from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import GenericForeignKey
from course.models import Account

# Create your models here.
__all__ = ["Coupon", "CouponRecord", "Order", "OrderDetail", "TransactionRecord"]


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

    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 = "13. 优惠券生成规则记录"
        db_table = verbose_name_plural
        verbose_name = verbose_name_plural

    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(to=Account, 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 = "14. 用户优惠券领取使用记录表"
        db_table = verbose_name_plural
        verbose_name = verbose_name_plural

    def __str__(self):
        return '%s-%s-%s' % (self.account, 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)  # 考虑到订单合并支付的问题
    account = models.ForeignKey(to=Account)
    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 = "15. 订单表"
        db_table = verbose_name_plural
        verbose_name = verbose_name_plural

    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("折后价格")
    valid_period_display = models.CharField("有效期显示", max_length=32)  # 在订单页显示
    valid_period = models.PositiveIntegerField("有效期(days)")  # 课程有效期
    memo = models.CharField(max_length=255, blank=True, null=True, verbose_name="备忘录")

    def __str__(self):
        return "%s - %s - %s" % (self.order, self.content_type, self.price)

    class Meta:
        verbose_name_plural = "16. 订单详细"
        db_table = verbose_name_plural
        verbose_name = verbose_name_plural
        unique_together = ("order", 'content_type', 'object_id')


class TransactionRecord(models.Model):
    """贝里交易纪录"""
    account = models.ForeignKey(to=Account)
    amount = models.IntegerField("金额")
    balance = models.IntegerField("账户余额")
    transaction_type_choices = ((0, '收入'), (1, '支出'), (2, '退款'), (3, "提现"))  # 2 为了处理 订单过期未支付时,锁定期贝里的回退
    transaction_type = models.SmallIntegerField(choices=transaction_type_choices)
    transaction_number = models.CharField(unique=True, verbose_name="流水号", max_length=128)
    date = models.DateTimeField(auto_now_add=True)
    memo = models.CharField(max_length=128, blank=True, null=True, verbose_name="备忘录")

    class Meta:
        verbose_name_plural = "17. 贝里交易记录"
        db_table = verbose_name_plural
        verbose_name = verbose_name_plural

    def __str__(self):
        return "%s" % self.transaction_number
models.py

视图代码如下~~

# by gaoxin
from rest_framework.views import APIView
from rest_framework.response import Response
from utils.base_response import BaseResponse
from utils.redis_pool import pool
from django.utils.timezone import now
import redis
from utils.authentication import MyAuth
from .shop import SHOPPING_CAR_KEY
from pay.models import CouponRecord
from django.contrib.contenttypes.models import ContentType
import json
"""
结算中心
在购物车里选择了商品以及价格策略点击结算 才进入结算中心
在结算中心用户可以选择优惠券
传过来的数据 [course_id, course_id]
根据用户id 以及商品id 构建一个存入redis的key
SETTLEMENT_KEY = "settlement_%s_%s"
接下来构建结算中心数据结构
redis = {
    settlement_用户id_课程id: {
        "id": course.id, 带用户id是为了用户查看购物车的时候需要
        "title": course.name,
        "img": str(course.course_img),
        "period_display": item.get_valid_period_display(), 价格策略中文
        "price":  item.price, 价格
        "course_coupons": {
            course_coupon_id: {...}
        } 
        用户选中的优惠券id 在更新请求 也就是用户选了优惠券的时候更改
        前端默认优惠券可以是不选的 根据业务决定
        default_coupon_id: 1
    }
    global_coupon_用户id: {
         global_coupon_id: {....},
         # 在用户进入结算中心选择优惠券的时候 也就是更新请求的时候更改 
         default_global_coupon_id: 1   
    }
}

"""

SETTLEMENT_KEY = "settlement_%s_%s"
GLOBAL_COUPON_KEY = "global_coupon_%s"
REDIS_CONN = redis.Redis(connection_pool=pool)


class SettlementView(APIView):
    """
    code为1020开始的都是结算中心的错误
    1021 获取课程id失败
    """
    authentication_classes = [MyAuth, ]

    # 进入结算中心 也就是用户查看自己的未支付订单
    def get(self, request, *args, **kwargs):
        res = BaseResponse()
        try:
            # 1 从redis结算中心获取数据并展示
            # 根据用户id  得到 settlement_id_userid_*
            user_id = request.user.pk
            settlement_key = SETTLEMENT_KEY % (user_id, "*")
            # scan_iter 得到所有的key
            all_settlement_keys = REDIS_CONN.scan_iter(settlement_key)
            global_coupon_key = GLOBAL_COUPON_KEY % user_id
            # 2 循环遍历得到所有信息
            settlement_list = []
            for key in all_settlement_keys:
                settlement_info = REDIS_CONN.hgetall(key)
                settlement_list.append(settlement_info)
            global_coupon_info = REDIS_CONN.hgetall(global_coupon_key)
            res.data = {"settlement_list": settlement_list, "global_coupon_info": global_coupon_info}
        except Exception as e:
            res.code = 1024
            res.error = "获取结算中心失败"
        return Response(res.dict)

    # 把商品添加到结算中心 用户传过来的数据是 【course_id,course_id】
    def post(self, request, *args, **kwargs):
        res = BaseResponse()
        # 1 获取前端传过来的数据以及用户id
        try:
            course_list = request.data.get("course_list", "")
            if not course_list:
                res.code = 1021
                res.error = "获取课程id失败"
                return Response(res.dict)
            user_id = request.user.pk
            # 2 检查数据的合法性 查看课程id 是否在购物车中
            for course_id in course_list:
                # course_id = int(course_id)
                shopping_car_key = SHOPPING_CAR_KEY % (user_id, course_id)
                if not REDIS_CONN.exists(shopping_car_key):
                    res.code = 1022
                    res.error = "结算的课程id不合法"
                    return Response(res.dict)
                # 3 从购物车中获取课程的信息 并构建成结算中心的数据格式放入redis
                # 3.1 先从redis中拿购物车中的数据
                course_info = REDIS_CONN.hgetall(shopping_car_key)
                # 3.2 获取用户所有符合条件优惠券
                # 注意优惠券的使用期限以及状态
                now_time = now()
                # course_table_id = ContentType.objects.filter(app_label="course", model="course").first().pk
                user_all_coupons = CouponRecord.objects.filter(
                    account_id=user_id,
                    status=0,
                    coupon__valid_begin_date__lte=now_time,
                    coupon__valid_end_date__gte=now_time,
                    # content_type_id=course_table_id,
                    # object_id=course_id
                ).all()
                # 3.2 构建数据结构
                course_coupons = {}
                global_coupons = {}
                for coupon_record in user_all_coupons:
                    # 证明这个优惠券是绑定课程的优惠券
                    coupon = coupon_record.coupon
                    if coupon.object_id == course_id:
                        course_coupons[coupon.id] = {
                            "name": coupon.name,
                            "coupon_type": coupon.get_coupon_type_display(),
                            "money_equivalent_value": coupon.money_equivalent_value,
                            "off_percent": coupon.off_percent,
                            "minimum_consume": coupon.minimum_consume,
                            "object_id": coupon.object_id,
                        }
                    elseif coupon.object_id == "":
                        global_coupons[coupon.id] = {
                            "name": coupon.name,
                            "coupon_type": coupon.get_coupon_type_display(),
                            "money_equivalent_value": coupon.money_equivalent_value,
                            "off_percent": coupon.off_percent,
                            "minimum_consume": coupon.minimum_consume,
                            "object_id": coupon.object_id,
                        }
                # print(json.loads(course_info["price_policy_dict"]))
                price_policy_dict = json.loads(course_info["price_policy_dict"])
                default_policy_id = course_info["default_policy"]
                price = price_policy_dict[default_policy_id]["price"]
                print(price)
                period_display = price_policy_dict[default_policy_id]["valid_period_text"]
                print(period_display)
                settlement_info = {
                    "id": course_info["id"],
                    "title": course_info["title"],
                    "img_src": course_info["img_src"],
                    "price": price,
                    "period_display": period_display,
                    "course_coupons": json.dumps(course_coupons, ensure_ascii=False),
                }
                # 3.3 准备构建结算中心的key
                settlement_key = SETTLEMENT_KEY % (user_id, course_id)
                global_coupon_key = GLOBAL_COUPON_KEY % user_id
                # # 3.4 写入redis
                REDIS_CONN.hmset(settlement_key, settlement_info)
                print(global_coupons)
                if global_coupons:
                    REDIS_CONN.hmset(global_coupon_key, global_coupons)
                # # 4 加入结算中心成功 清除用户购物车中的相应课程的数据
                REDIS_CONN.delete(shopping_car_key)
                res.data = "加入结算中心成功"
        except Exception as e:
            print(e)
            res.code = 1020
            res.error = "结算失败"
        return Response(res.dict)

    # 用户在结算中心的时候更改优惠券
    # 用户在选择优惠券的时候前端传过来course_id 以及coupon_id
    # 如果选的是全局优惠券 就传 global_coupon_id
    def patch(self, request, *args, **kwargs):
        res = BaseResponse()
        try:
            # 1 获取前端传过来的数据
            course_id = request.data.get("course_id", "")
            coupon_id = request.data.get("coupon_id", "")
            global_coupon_id = request.data.get("global_coupon_id", "")
            user_id = request.user.pk
            # 2 校验数据的合法性
            # 2.2 校验course_id是否合法
            settlement_key = SETTLEMENT_KEY % (user_id, course_id)
            if not REDIS_CONN.exists(settlement_key):
                res.code = 1027
                res.error = "课程id不合法"
                return Response(res.dict)
            # 2.3 校验coupon_id 是否合法
            course_info = REDIS_CONN.hgetall(settlement_key)
            course_coupon_dict = json.loads(course_info["course_coupons"])
            if coupon_id not in course_coupon_dict:
                res.code = 1028
                res.error = "课程的优惠券不合法"
                return Response(res.dict)
            # 2.4 校验global_coupon_id是否合法 如果合法把默认的全局优惠信息放入redis
            if global_coupon_id:
                global_coupon_key = GLOBAL_COUPON_KEY % user_id
                if not REDIS_CONN.exists(global_coupon_key):
                    res.code = 1029
                    res.error = "全局优惠券不合法"
                    return Response(res.dict)
                REDIS_CONN.hset(global_coupon_key, "default_coupon_id", global_coupon_id)
            # 3 给数据里加入默认选择的课程优惠券
            REDIS_CONN.hset(settlement_key, "default_course_coupon_id", coupon_id)
            res.data = "修改优惠券成功"
        except Exception as e:
            res.code = 1026
            res.error = "更改优惠券失败"
        return Response(res.dict)

 

posted @ 2018-12-11 16:43  GGG_XXX  阅读(443)  评论(0编辑  收藏  举报