路飞学城购买流程API

路飞学城购买流程API

购物车

复制代码
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import json
from django.core.exceptions import ObjectDoesNotExist
from django.conf import settings

from rest_framework.views import APIView
from rest_framework.viewsets import ViewSetMixin
from rest_framework.viewsets import ModelViewSet
from rest_framework.response import Response

from repository import models

from api.serializer.payment import ShoppingCarSerializer
from api.utils.auth.token_auth import LuffyTokenAuthentication
from api.utils.auth.token_permission import LuffyPermission
from api.utils import redis_pool
from api.utils.exception import PricePolicyDoesNotExist


class ShoppingCarView(ViewSetMixin, APIView):
    """
    购物车接口
    """
    authentication_classes = [LuffyTokenAuthentication, ]
    permission_classes = [LuffyPermission, ]

    def get(self, request, *args, **kwargs):
        """
        根据用户ID获取购物车所有东西
        :param request: 
        :param args: 
        :param kwargs: 
        :return: 
        """
        response = {'code': 1000, 'data': None}
        try:
            product_dict = redis_pool.conn.hget(settings.REDIS_SHOPPING_CAR_KEY, request.user.id)
            if product_dict:
                product_dict = json.loads(product_dict.decode('utf-8'))
                response['data'] = product_dict
        except Exception as e:
            response['code'] = 1001
            response['msg'] = "获取购物车列表失败"

        return Response(response)

    def post(self, request, *args, **kwargs):
        """
        # 根据课程ID获取课程信息以及相关所有价格策略
        chopping_car = {
            request.user.id:{
                course.id:{
                        title:'xx',
                        img:'xx',
                        choice_policy_id:1,
                        price_policy_dict:{
                            {id:1,price:'9.9', period:'1个月'},
                            {id:2,price:'19.9',period:'3个月'},
                            {id:3,price:'59.9',period:'8个月'},
                        },
                    }
                },
                course.id:[
                        title:'xx',
                        img:'xx',
                        choice_policy_id:1,
                        price_policy_dict:{
                            {id:1,price:'9.9', period:'1个月'},
                            {id:2,price:'19.9',period:'3个月'},
                            {id:3,price:'59.9',period:'8个月'},
                        },
                    ]
                }
            }
        }
        :param request: 
        :param args: 
        :param kwargs: 
        :return: 
        """

        response = {'code': 1000, 'msg': None}
        try:
            course_id = int(request.data.get('course_id'))
            policy_id = int(request.data.get('policy_id'))

            # 获取课程信息
            course = models.Course.objects.exclude(course_type=2).filter(status=0).get(id=course_id)

            # 序列化课程信息,并获取其关联的所有价格策略
            ser = ShoppingCarSerializer(instance=course, many=False)
            product = ser.data

            # 判断价格策略是否存在
            policy_exist = False
            for policy in product['price_policy_list']:
                if policy['id'] == policy_id:
                    policy_exist = True
                    break
            if not policy_exist:
                raise PricePolicyDoesNotExist()

            # 设置默认选中的价格策略
            product.setdefault('choice_policy_id', policy_id)
            # 获取当前用户在购物车中已存在的课程,如果存在则更新,否则添加新课程
            product_dict = redis_pool.conn.hget(settings.REDIS_SHOPPING_CAR_KEY, request.user.id)
            if not product_dict:
                product_dict = {course_id: product}
            else:
                product_dict = json.loads(product_dict.decode('utf-8'))
                product_dict[course_id] = product
            # 将新课程写入到购物车
            redis_pool.conn.hset(settings.REDIS_SHOPPING_CAR_KEY, request.user.id, json.dumps(product_dict))

        except ObjectDoesNotExist as e:
            response['code'] = 1001
            response['msg'] = '视频不存在'
        except PricePolicyDoesNotExist as e:
            response['code'] = 1002
            response['msg'] = '价格策略不存在'
        except Exception as e:
            print(e)
            response['code'] = 1003
            response['msg'] = '添加购物车失败'

        return Response(response)

    def delete(self, request, *args, **kwargs):
        """
        删除购物车中的课程
        :param request: 
        :param args: 
        :param kwargs: 
        :return: 
        """
        response = {'code': 1000}
        try:
            course_id = kwargs.get('pk')
            product_dict = redis_pool.conn.hget(settings.REDIS_SHOPPING_CAR_KEY, request.user.id)
            if not product_dict:
                raise Exception('购物车中无课程')
            product_dict = json.loads(product_dict.decode('utf-8'))
            if course_id not in product_dict:
                raise Exception('购物车中无该商品')
            del product_dict[course_id]
            redis_pool.conn.hset(settings.REDIS_SHOPPING_CAR_KEY, request.user.id, json.dumps(product_dict))
        except Exception as e:
            response['code'] = 1001
            response['msg'] = str(e)

        return Response(response)

    def put(self, request, *args, **kwargs):
        """
        更新购物车中的课程的默认的价格策略
        :param request: 
        :param args: 
        :param kwargs: 
        :return: 
        """
        response = {'code': 1000}
        try:
            course_id = kwargs.get('pk')
            policy_id = request.data.get('policy_id')
            product_dict = redis_pool.conn.hget(settings.REDIS_SHOPPING_CAR_KEY, request.user.id)
            if not product_dict:
                raise Exception('购物车清单不存在')
            product_dict = json.loads(product_dict.decode('utf-8'))
            if course_id not in product_dict:
                raise Exception('购物车清单中商品不存在')

            policy_exist = False
            for policy in product_dict[course_id]['price_policy_list']:
                if policy['id'] == policy_id:
                    policy_exist = True
                    break
            if not policy_exist:
                raise PricePolicyDoesNotExist()

            product_dict[course_id]['choice_policy_id'] = policy_id
            redis_pool.conn.hset(settings.REDIS_SHOPPING_CAR_KEY, request.user.id, json.dumps(product_dict))
        except PricePolicyDoesNotExist as e:
            response['code'] = 1001
            response['msg'] = '价格策略不存在'
        except Exception as e:
            response['code'] = 1002
            response['msg'] = str(e)

        return Response(response)
复制代码

结算

复制代码
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import json
import datetime
from django.conf import settings

from rest_framework.views import APIView
from rest_framework.response import Response

from api.utils.auth.token_auth import LuffyTokenAuthentication
from api.utils.auth.token_permission import LuffyPermission
from api.utils import redis_pool
from repository import models


class PaymentView(APIView):
    """
    去结算接口
    """
    authentication_classes = [LuffyTokenAuthentication, ]
    permission_classes = [LuffyPermission, ]

    def get(self, request, *args, **kwargs):
        """
        获取结算列表
        :param request: 
        :param args: 
        :param kwargs: 
        :return: 
        """
        response = {'code': 1000}
        try:
            # 结算商品列表
            payment_list = redis_pool.conn.hget(settings.REDIS_PAYMENT_KEY, request.user.id)
            if not payment_list:
                raise Exception()

            response['data'] = {
                'payment_list': json.loads(payment_list.decode('utf-8')),  # 结算信息(课程、价格和优惠券)
                "balance": request.user.balance  # 个人贝里账户,可使用贝里金额
            }
        except Exception as e:
            response['code'] = 1001
            response['msg'] = "结算列表为空"

        return Response(response)

    def post(self, request, *args, **kwargs):
        """
        去结算
            方案一(示例):用户提交课程id,去redis购物车中获取其选好的价格策略,再次检测课程和价格策略的合法性。
                   PS: 直接购买时,需要先加入购物车,再立即去结算

            方案二:用户提交课程id和价格策略id,去数据库验证其合法性。
                   PS: 直接购买时,直接去结算
            
            user.id: {
                policy_course_dict:{
                    课程ID:{
                        'course_id': course_id,
                        'course_name': product['name'],
                        'course_img': product['course_img'],
                        'policy_id': product['choice_policy_id'],
                        'policy_price': policy_price,
                        'policy_': policy_period,
                        'coupon_record_list': [
                            {'id': 0, 'text': '请选择优惠券'},
                            {'id': 1, 'type':1, 'text': '优惠券1', ..},
                            {'id': 2, 'type':2, 'text': '优惠券1', ..},
                            {'id': 3, 'type':3, 'text': '优惠券1', ..},
                        ],
                    },
                    课程ID:{
                        'course_id': course_id,
                        'course_name': product['name'],
                        'course_img': product['course_img'],
                        'policy_id': product['choice_policy_id'],
                        'policy_price': policy_price,
                        'policy_': policy_period,
                        'coupon_record_list': [
                            {'id': 0, 'text': '请选择优惠券'},
                            {'id': 1, 'type':1, 'text': '优惠券1', ..},
                            {'id': 2, 'type':2, 'text': '优惠券1', ..},
                            {'id': 3, 'type':3, 'text': '优惠券1', ..},
                        ],
                    }
                },
                global_coupon_dict:{
                    1:{'type': 0, 'text': "通用优惠券", 'id': 1, ..},
                    2:{'type': 0, 'text': "通用优惠券", 'id': 2, ..},
                    3:{'type': 0, 'text': "通用优惠券", 'id': 3, ...},
                    4:{'type': 0, 'text': "通用优惠券", 'id': 4, ...},
                }
            }  
                   
            
        :param request: 
        :param args: 
        :param kwargs: 
        :return: 
        """
        response = {'code': 1001}
        try:

            """
            1. 获取要支付的课程ID
            2. 检查购物车中是否存在,不存在则报错
                循环用户提交的课程ID,去购物车中获取,如果不存在,就报错。
                
            """
            # 获取用户提交的课程id
            course_id_list = request.data.get('course_list')
            if not course_id_list or not isinstance(course_id_list, list):
                raise Exception('请选择要结算的课程')

            # 购物车中检查是否已经有课程(应该有课程的)
            product_dict = redis_pool.conn.hget(settings.REDIS_SHOPPING_CAR_KEY, request.user.id)
            if not product_dict:
                raise Exception('购物车无课程')

            # 购物车中是否有用户要购买的课程
            product_dict = json.loads(product_dict.decode('utf-8'))

            # ###### 课程、价格和优惠券 #######
            policy_course_dict = {}

            for course_id in course_id_list:
                course_id = str(course_id)
                product = product_dict.get(course_id)
                if not product:
                    raise Exception('购买的课程必须先加入购物车')

                policy_exist = False
                for policy in product['price_policy_list']:
                    if policy['id'] == product['choice_policy_id']:
                        policy_price = policy['price']
                        policy_period = policy['period']
                        policy_valid_period = policy['valid_period']
                        policy_exist = True
                        break
                if not policy_exist:
                    raise Exception('购物车中的课程无此价格')

                policy_course = {
                    'course_id': course_id,
                    'course_name': product['name'],
                    'course_img': product['course_img'],
                    'policy_id': product['choice_policy_id'],
                    'policy_price': policy_price,
                    'policy_period': policy_period,
                    'policy_valid_period': policy_valid_period,
                    'coupon_record_list': [
                        {'id': 0, 'text': '请选择优惠券'},
                    ],
                }
                policy_course_dict[course_id] = policy_course

            # 获取当前所有优惠券
            user_coupon_list = models.CouponRecord.objects.filter(account=request.user,
                                                                  status=0)
            # ###### 全局优惠券 #######
            global_coupon_record_dict = {}

            # 课程优惠券添加到课程中;全局优惠券添加到全局
            current_date = datetime.datetime.now().date()
            for record in user_coupon_list:
                # 检查优惠券是否已经过期
                begin_date = record.coupon.valid_begin_date
                end_date = record.coupon.valid_end_date
                if begin_date:
                    if current_date < begin_date:
                        continue
                if end_date:
                    if current_date > end_date:
                        continue
                # 全局优惠券
                if not record.coupon.content_type:
                    if record.coupon.coupon_type == 0:
                        temp = {'type': 0, 'text': "通用优惠券", 'id': record.id,
                                'begin_date': begin_date, 'end_date': end_date,
                                'money_equivalent_value': record.coupon.money_equivalent_value}
                    elif record.coupon.coupon_type == 1:
                        temp = {'type': 1, 'text': "满减券", 'id': record.id,
                                'begin_date': begin_date, 'end_date': end_date,
                                'minimum_consume': record.coupon.minimum_consume,
                                'money_equivalent_value': record.coupon.money_equivalent_value}
                    elif record.coupon.coupon_type == 2:
                        temp = {'type': 2, 'text': "折扣券", 'id': record.id,
                                'begin_date': begin_date, 'end_date': end_date,
                                'off_percent': record.coupon.off_percent}
                    else:
                        continue

                    global_coupon_record_dict[record.id] = temp
                # 课程优惠券
                else:
                    cid = record.coupon.object_id
                    if record.coupon.content_type.model == 'course' and cid in policy_course_dict:
                        # 课程价格:满减,打折,通用
                        if record.coupon.coupon_type == 0:
                            temp = {'type': 0, 'text': "通用优惠券", 'id': record.id,
                                    'begin_date': begin_date, 'end_date': end_date,
                                    'money_equivalent_value': record.coupon.money_equivalent_value}
                        elif record.coupon.coupon_type == 1 and policy_course_dict[cid][
                            'policy_price'] >= record.coupon.minimum_consume:
                            temp = {'type': 1, 'text': "满减券", 'id': record.id,
                                    'begin_date': begin_date, 'end_date': end_date,
                                    'minimum_consume': record.coupon.minimum_consume,
                                    'money_equivalent_value': record.coupon.money_equivalent_value}
                        elif record.coupon.coupon_type == 2:
                            temp = {'type': 2, 'text': "折扣券", 'id': record.id,
                                    'begin_date': begin_date, 'end_date': end_date,
                                    'off_percent': record.coupon.off_percent}
                        else:
                            continue
                        policy_course_dict[cid]['coupon_record_list'].append(temp)

            user_pay = {
                'policy_course_dict': policy_course_dict,
                'global_coupon_record_dict': global_coupon_record_dict
            }
            redis_pool.conn.hset(settings.REDIS_PAYMENT_KEY, request.user.id, json.dumps(user_pay))

        except Exception as e:
            response['code'] = 1002
            response['msg'] = str(e)

        return Response(response)
复制代码

去支付

复制代码
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import json
import time
import random
import datetime
from django.conf import settings
from django.db import transaction
from django.db.models import F

from rest_framework.views import APIView
from rest_framework.response import Response

from api.utils.auth.token_auth import LuffyTokenAuthentication
from api.utils.auth.token_permission import LuffyPermission
from api.utils import redis_pool
from api.utils.alipay import AliPay

from repository import models


def generate_order_num():
    """
    生成订单编号, 且必须唯一
    :return:
    """
    while True:
        order_num = time.strftime('%Y%m%d%H%M%S', time.localtime()) + str(random.randint(111, 999))
        if not models.Order.objects.filter(order_number=order_num).exists():
            break
    return order_num


def generate_transaction_num():
    """
    生成流水编号, 且必须唯一
    :return:
    """
    while True:
        transaction_number = time.strftime('%Y%m%d%H%M%S', time.localtime()) + str(random.randint(111, 999))
        if not models.TransactionRecord.objects.filter(transaction_number=transaction_number).exists():
            break
    return transaction_number


class PayOrderView(APIView):
    authentication_classes = [LuffyTokenAuthentication, ]
    permission_classes = [LuffyPermission, ]

    def post(self, request, *args, **kwargs):
        """
        去支付,生成订单。
        获取前端提交的购买信息
            {
                course_price_list:[
                    {'policy_id':1, '':'course_id':1, 'coupon_record_id':1},
                    {'policy_id':2, '':'course_id':2, 'coupon_record_id':2},
                ],
                coupon_record_id:1,
                alipay: 99,
                balance: 1
            }

        1. 用户提交
            - balance
            - alipay
        2. 获取去结算列表

        课程
        3. 循环所有课程
            - 获取原价
            - 抵扣的钱




        :param request: 
        :param args: 
        :param kwargs: 
        :return: 
        """
        response = {'code': 1000}
        try:
            # 用户请求验证
            policy_course_list = request.data.get('course_price_list')
            coupon_record_id = request.data.get('coupon_record_id')
            alipay = request.data.get('alipay')  # >= 0
            balance = request.data.get('balance')  # >= 0

            if balance > request.user.balance:
                raise Exception('账户中贝里余额不足')

            # 检查用户提交的信息在 redis结算列表 中是否存在,如果不存在,则需要用户从购物车中再次去结算
            payment_dict_bytes = redis_pool.conn.hget(settings.REDIS_PAYMENT_KEY, request.user.id)
            payment_dict = json.loads(payment_dict_bytes.decode('utf-8'))

            policy_course_dict = payment_dict['policy_course_dict']
            global_coupon_record_dict = payment_dict['global_coupon_record_dict']

            global_coupon_record = {}
            # 全局优惠券
            if coupon_record_id:
                if  coupon_record_id not in global_coupon_record_dict:
                    raise Exception('全局优惠券在缓存中不存在')
                global_coupon_record = global_coupon_record_dict[coupon_record_id]

            # 当前时间
            current_date = datetime.datetime.now().date()
            current_datetime = datetime.datetime.now()

            # 原价
            total_price = 0
            # 总抵扣的钱
            discount = 0
            # 使用优惠券ID列表
            if coupon_record_id:
                use_coupon_record_id_list = [coupon_record_id, ]
            else:
                use_coupon_record_id_list=[]
            # 课程和优惠券
            buy_course_record = []

            for cp in policy_course_list:
                _policy_id = cp['policy_id']
                _course_id = cp['course_id']
                _coupon_record_id = cp['coupon_record_id']

                temp = {
                    'course_id': _course_id,
                    'course_name': "course",
                    'valid_period': 0,  # 有效期:30
                    'period': 0,  # 有效期:一个月
                    'original_price': 0,
                    'price': 0,
                }


                if str(_course_id) not in policy_course_dict:
                    raise Exception('课程在缓存中不存在')

                redis_course = policy_course_dict[str(_course_id)]

                if str(_policy_id) != str(redis_course['policy_id']):
                    raise Exception('价格策略在缓存中不存在')

                # 课程是否已经下线或价格策略被修改
                policy_object = models.PricePolicy.objects.get(id=_policy_id)  # 价格策略对象
                course_object = policy_object.content_object  # 课程对象

                if course_object.id != _course_id:
                    raise Exception('课程和价格策略对应失败')
                if course_object.status != 0:
                    raise Exception('课程已下线,无法购买')

                # 选择的优惠券是否在缓存中
                redis_coupon_list = redis_course['coupon_record_list']
                redis_coupon_record = None
                for item in redis_coupon_list:
                    if item['id'] == _coupon_record_id:
                        redis_coupon_record = item
                        break
                if not redis_coupon_record:
                    raise Exception('单课程优惠券在缓存中不存在')

                # 计算购买原总价
                total_price += policy_object.price

                # 未使用单课程优惠券
                if redis_coupon_record['id'] == 0:
                    temp['price'] = policy_object.price
                    buy_course_record.append(temp)
                    continue

                temp['original_price'] = policy_object.price
                temp['valid_period'] = redis_coupon_record['policy_valid_period']
                temp['period'] = redis_coupon_record['policy_period']

                # 缓存中的优惠券是否已经过期
                begin_date = redis_coupon_record.get('begin_date')
                end_date = redis_coupon_record.get('end_date')
                if begin_date:
                    if current_date < begin_date:
                        raise Exception('优惠券使用还未到时间')
                if end_date:
                    if current_date > end_date:
                        raise Exception('优惠券已过期')

                # 使用的是单课程优惠券抵扣了多少钱;使用的 个人优惠券ID
                if redis_coupon_record['type'] == 0:
                    # 通用优惠券
                    money = redis_coupon_record['money_equivalent_value']
                    discount += money
                elif redis_coupon_record['type'] == 1:
                    # 满减券
                    money = redis_coupon_record['money_equivalent_value']
                    minimum_consume = redis_coupon_record['minimum_consume']
                    if policy_object.price >= minimum_consume:
                        discount += money
                elif redis_coupon_record['type'] == 2:
                    # 打折券
                    money = policy_object.price * redis_coupon_record['off_percent']
                    discount += money

                temp['price'] = policy_object.price - money
                buy_course_record.append(temp)
                use_coupon_record_id_list.append(redis_coupon_record['id'])

            # 全局优惠券
            print(global_coupon_record)
            begin_date = global_coupon_record.get('begin_date')
            end_date = global_coupon_record.get('end_date')
            if begin_date:
                if current_date < begin_date:
                    raise Exception('优惠券使用还未到时间')
            if end_date:
                if current_date > end_date:
                    raise Exception('优惠券已过期')

            # 使用全局优惠券抵扣了多少钱
            if global_coupon_record.get('type') == 0:
                # 通用优惠券
                money = global_coupon_record['money_equivalent_value']
                discount += money
            elif global_coupon_record.get('type') == 1:
                # 满减券
                money = global_coupon_record['money_equivalent_value']
                minimum_consume = global_coupon_record['minimum_consume']
                if (total_price - discount) >= minimum_consume:
                    discount += money
            elif global_coupon_record.get('type') == 2:
                # 打折券
                money = (total_price - discount) * global_coupon_record['off_percent']
                discount += money

            # 贝里抵扣的钱
            if balance:
                discount += balance

            if (alipay + discount) != total_price:
                raise Exception('总价、优惠券抵扣、贝里抵扣和实际支付的金额不符')

            # 创建订单 + 支付宝支付
            # 创建订单详细
            # 贝里抵扣 + 贝里记录
            # 优惠券状态更新
            actual_amount = 0
            if alipay:
                payment_type = 1  # 支付宝
                actual_amount = alipay
            elif balance:
                payment_type = 3  # 贝里
            else:
                payment_type = 2  # 优惠码

            with transaction.atomic():
                order_num = generate_order_num()
                if payment_type == 1:
                    order_object = models.Order.objects.create(
                        payment_type=payment_type,
                        order_number=order_num,
                        account=request.user,
                        actual_amount=actual_amount,
                        status=1,  # 待支付
                    )
                else:
                    order_object = models.Order.objects.create(
                        payment_type=payment_type,
                        order_number=order_num,
                        account=request.user,
                        actual_amount=actual_amount,
                        status=0,  # 支付成功,优惠券和贝里已够支付
                        pay_time=current_datetime
                    )

                for item in buy_course_record:

                    detail = models.OrderDetail.objects.create(
                        order=order_object,
                        content_object=models.Course.objects.get(id=item['course_id']),
                        original_price=item['original_price'],
                        price=item['price'],
                        valid_period_display=item['period'],
                        valid_period=item['valid_period']
                    )
                models.Account.objects.filter(id=request.user.id).update(balance=F('balance') - balance)
                models.TransactionRecord.objects.create(
                    account=request.user,
                    amount=request.user.balance,
                    balance=request.user.balance - balance,
                    transaction_type=1,
                    content_object=order_object,
                    transaction_number=generate_transaction_num()
                )
                effect_row = models.CouponRecord.objects.filter(id__in=use_coupon_record_id_list).update(
                    order=order_object,
                    used_time=current_datetime)


                if effect_row != len(use_coupon_record_id_list):
                    raise Exception('优惠券使用失败')

                response['payment_type'] = payment_type
                # 生成支付宝URL地址
                if payment_type == 1:
                    pay = AliPay(debug=True)
                    query_params = pay.direct_pay(
                        subject="路飞学城",  # 商品简单描述
                        out_trade_no=order_num,  # 商户订单号
                        total_amount=actual_amount,  # 交易金额(单位: 元 保留俩位小数)
                    )
                    pay_url = "https://openapi.alipaydev.com/gateway.do?{}".format(query_params)

                    response['pay_url'] = pay_url

        except IndentationError as e:
            response['code'] = 1001
            response['msg'] = str(e)

        return Response(response)
复制代码
原文地址:https://www.cnblogs.com/xyhh/p/10861262.html