路飛學城購買流程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)
複製代碼
相關文章
相關標籤/搜索