Django REST Framework实战:构建高性能API的架构设计与最佳实践

Django REST Framework实战:构建高性能API的架构设计与最佳实践

【免费下载链接】Python-100-Days Python - 100天从新手到大师 【免费下载链接】Python-100-Days 项目地址: https://gitcode.com/GitHub_Trending/py/Python-100-Days

Django REST Framework(DRF)作为Python生态中最强大的REST API开发框架,为现代Web应用提供了完整的API构建解决方案。本文将通过架构设计、性能优化和实战案例,深入解析如何基于DRF构建企业级RESTful API服务,涵盖从基础概念到高级特性的全流程开发。

核心概念解析:RESTful架构的"邮局模型"

RESTful架构可以类比为一个现代化的邮局系统:资源如同包裹,URI是包裹的地址标签,HTTP动词是邮局的各类服务(寄送、查询、修改、退回),而状态码则是邮局的业务回执。在这个模型中,每个资源都有唯一的地址标识,客户端通过标准的操作指令与服务器进行无状态通信。

DRF的核心价值在于将Django的ORM模型无缝转换为RESTful API,正如邮局系统将实体包裹转换为可追踪的物流信息。序列化器(Serializer)充当了"包装工"的角色,负责将Python对象打包为JSON格式,同时验证输入数据的完整性。视图集(ViewSet)则像邮局的分拣中心,根据请求类型自动路由到相应的处理逻辑。

实战架构设计:分层解耦的企业级API架构

1. 四层架构模型

基于DRF的企业级API应采用清晰的四层架构:

# 项目结构示例
api_project/
├── apps/
│   ├── users/           # 用户管理模块
│   │   ├── serializers.py
│   │   ├── views.py
│   │   ├── permissions.py
│   │   └── models.py
│   ├── products/        # 产品管理模块  
│   └── orders/          # 订单管理模块
├── config/
│   ├── settings/
│   │   ├── base.py      # 基础配置
│   │   ├── dev.py       # 开发环境
│   │   └── prod.py      # 生产环境
│   └── urls.py          # 路由配置
├── core/
│   ├── authentication.py # 自定义认证
│   ├── pagination.py     # 分页策略
│   └── throttling.py     # 限流控制
└── utils/
    └── exceptions.py     # 异常处理

2. 认证授权体系设计

安全是API设计的首要考虑。DRF提供了灵活的认证授权机制,推荐采用JWT + 权限控制的组合方案:

# core/authentication.py
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
import jwt
from datetime import datetime, timedelta

class JWTAuthentication(BaseAuthentication):
    """JWT认证后端"""
    
    def authenticate(self, request):
        token = request.META.get('HTTP_AUTHORIZATION', '').replace('Bearer ', '')
        
        if not token:
            return None
            
        try:
            payload = jwt.decode(
                token, 
                settings.SECRET_KEY, 
                algorithms=['HS256']
            )
            
            # 检查令牌是否过期
            if datetime.fromtimestamp(payload['exp']) < datetime.now():
                raise AuthenticationFailed('令牌已过期')
                
            user = User.objects.get(id=payload['user_id'])
            return (user, token)
            
        except jwt.InvalidTokenError:
            raise AuthenticationFailed('无效的令牌')
        except User.DoesNotExist:
            raise AuthenticationFailed('用户不存在')

JWT令牌认证流程示意图

图1:JWT令牌认证流程示意图 - 展示JWT令牌的三段式结构(Header、Payload、Signature)及其在API认证中的应用流程

3. 数据序列化策略

序列化器是DRF的核心组件,负责数据转换和验证。针对复杂业务场景,需要设计多级序列化器:

# apps/products/serializers.py
from rest_framework import serializers
from .models import Product, Category, ProductImage

class ProductImageSerializer(serializers.ModelSerializer):
    """产品图片序列化器"""
    class Meta:
        model = ProductImage
        fields = ['id', 'image_url', 'display_order']
        
class CategorySerializer(serializers.ModelSerializer):
    """分类序列化器"""
    product_count = serializers.SerializerMethodField()
    
    class Meta:
        model = Category
        fields = ['id', 'name', 'slug', 'product_count']
        
    def get_product_count(self, obj):
        return obj.products.count()

class ProductSerializer(serializers.ModelSerializer):
    """产品主序列化器"""
    category = CategorySerializer(read_only=True)
    images = ProductImageSerializer(many=True, read_only=True)
    is_in_stock = serializers.SerializerMethodField()
    
    class Meta:
        model = Product
        fields = [
            'id', 'name', 'sku', 'price', 
            'category', 'images', 'is_in_stock',
            'created_at', 'updated_at'
        ]
        read_only_fields = ['created_at', 'updated_at']
        
    def get_is_in_stock(self, obj):
        return obj.stock_quantity > 0
        
    def validate_price(self, value):
        """价格验证"""
        if value <= 0:
            raise serializers.ValidationError("价格必须大于0")
        return value

关键实现步骤:从零构建完整API服务

阶段一:基础配置与环境搭建

技术栈选择:Django 3.2+ + DRF 3.12+ + PostgreSQL 13+ + Redis 6+

# 创建虚拟环境并安装依赖
python -m venv venv
source venv/bin/activate  # Linux/Mac
# venv\Scripts\activate  # Windows

pip install django==3.2.16
pip install djangorestframework==3.12.4
pip install psycopg2-binary==2.9.3
pip install redis==4.3.4
pip install django-redis==5.2.0
pip install PyJWT==2.6.0

Django配置优化

# config/settings/base.py
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    
    # 第三方应用
    'rest_framework',
    'corsheaders',
    'django_filters',
    
    # 本地应用
    'apps.users',
    'apps.products',
    'apps.orders',
]

# DRF全局配置
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'core.authentication.JWTAuthentication',
        'rest_framework.authentication.SessionAuthentication',
    ],
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticatedOrReadOnly',
    ],
    'DEFAULT_PAGINATION_CLASS': 'core.pagination.CustomPageNumberPagination',
    'PAGE_SIZE': 20,
    'DEFAULT_FILTER_BACKENDS': [
        'django_filters.rest_framework.DjangoFilterBackend',
        'rest_framework.filters.OrderingFilter',
        'rest_framework.filters.SearchFilter',
    ],
    'DEFAULT_THROTTLE_CLASSES': [
        'rest_framework.throttling.AnonRateThrottle',
        'rest_framework.throttling.UserRateThrottle',
    ],
    'DEFAULT_THROTTLE_RATES': {
        'anon': '100/hour',
        'user': '1000/hour',
    },
    'EXCEPTION_HANDLER': 'utils.exceptions.custom_exception_handler',
}

阶段二:核心业务模块开发

视图集设计模式:采用ViewSet + Router实现CRUD操作

# apps/products/views.py
from rest_framework import viewsets, mixins, status
from rest_framework.decorators import action
from rest_framework.response import Response
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.filters import SearchFilter, OrderingFilter

from .models import Product, Category
from .serializers import ProductSerializer, CategorySerializer
from .filters import ProductFilter
from .permissions import IsProductOwnerOrReadOnly

class ProductViewSet(viewsets.ModelViewSet):
    """产品视图集"""
    queryset = Product.objects.select_related('category').prefetch_related('images')
    serializer_class = ProductSerializer
    permission_classes = [IsProductOwnerOrReadOnly]
    filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
    filterset_class = ProductFilter
    search_fields = ['name', 'description', 'sku']
    ordering_fields = ['price', 'created_at', 'sales_count']
    
    def get_queryset(self):
        """优化查询集"""
        queryset = super().get_queryset()
        
        # 根据用户权限过滤数据
        if not self.request.user.is_staff:
            queryset = queryset.filter(is_active=True)
            
        return queryset
    
    @action(detail=True, methods=['post'])
    def add_to_cart(self, request, pk=None):
        """添加到购物车自定义动作"""
        product = self.get_object()
        quantity = request.data.get('quantity', 1)
        
        # 业务逻辑处理
        cart_item, created = CartItem.objects.get_or_create(
            user=request.user,
            product=product,
            defaults={'quantity': quantity}
        )
        
        if not created:
            cart_item.quantity += quantity
            cart_item.save()
            
        return Response({
            'message': '已添加到购物车',
            'cart_item_id': cart_item.id,
            'quantity': cart_item.quantity
        }, status=status.HTTP_200_OK)
    
    @action(detail=False, methods=['get'])
    def best_sellers(self, request):
        """热销产品接口"""
        products = self.get_queryset().order_by('-sales_count')[:10]
        serializer = self.get_serializer(products, many=True)
        return Response(serializer.data)

路由配置优化

# config/urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView

from apps.users.views import UserViewSet
from apps.products.views import ProductViewSet, CategoryViewSet
from apps.orders.views import OrderViewSet

router = DefaultRouter()
router.register(r'users', UserViewSet, basename='user')
router.register(r'products', ProductViewSet, basename='product')
router.register(r'categories', CategoryViewSet, basename='category')
router.register(r'orders', OrderViewSet, basename='order')

urlpatterns = [
    path('api/v1/', include(router.urls)),
    path('api/v1/auth/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
    path('api/v1/auth/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
    path('api/v1/auth/token/verify/', TokenVerifyView.as_view(), name='token_verify'),
    
    # API文档
    path('api/v1/docs/', include_docs_urls(title='API文档')),
    path('api/v1/schema/', get_schema_view(
        title="电商平台API",
        description="电商平台RESTful API接口文档",
        version="1.0.0"
    ), name='openapi-schema'),
]

DRF应用架构示意图

图2:DRF应用架构示意图 - 展示Django REST Framework在MVC架构中的位置及其与前端应用的交互关系

阶段三:高级特性集成

自定义分页器实现

# core/pagination.py
from rest_framework.pagination import PageNumberPagination
from rest_framework.response import Response

class CustomPageNumberPagination(PageNumberPagination):
    """自定义分页器"""
    page_size = 20
    page_size_query_param = 'page_size'
    max_page_size = 100
    
    def get_paginated_response(self, data):
        return Response({
            'code': 200,
            'message': 'success',
            'data': {
                'count': self.page.paginator.count,
                'total_pages': self.page.paginator.num_pages,
                'current_page': self.page.number,
                'results': data,
                'next': self.get_next_link(),
                'previous': self.get_previous_link(),
            }
        })

批量操作支持

# apps/products/views.py
class BulkProductViewSet(viewsets.GenericViewSet):
    """批量操作视图集"""
    serializer_class = ProductSerializer
    
    @action(detail=False, methods=['post'])
    def bulk_create(self, request):
        """批量创建产品"""
        serializer = self.get_serializer(data=request.data, many=True)
        serializer.is_valid(raise_exception=True)
        self.perform_bulk_create(serializer)
        return Response(serializer.data, status=status.HTTP_201_CREATED)
    
    @action(detail=False, methods=['put'])
    def bulk_update(self, request):
        """批量更新产品"""
        products_data = request.data
        products = []
        
        for product_data in products_data:
            try:
                product = Product.objects.get(id=product_data['id'])
                serializer = self.get_serializer(product, data=product_data, partial=True)
                serializer.is_valid(raise_exception=True)
                serializer.save()
                products.append(serializer.data)
            except Product.DoesNotExist:
                continue
                
        return Response(products, status=status.HTTP_200_OK)
    
    def perform_bulk_create(self, serializer):
        serializer.save()

性能优化策略:提升API响应速度的实战技巧

1. 数据库查询优化

N+1查询问题解决方案

# 优化前:产生N+1查询
products = Product.objects.all()
for product in products:
    print(product.category.name)  # 每次循环都查询数据库

# 优化后:使用select_related和prefetch_related
products = Product.objects.select_related('category').prefetch_related('images', 'tags').all()

查询集缓存策略

from django.core.cache import cache
from django.db.models import Prefetch

class ProductViewSet(viewsets.ModelViewSet):
    
    def get_queryset(self):
        cache_key = f"products_queryset_{self.request.user.id}"
        queryset = cache.get(cache_key)
        
        if queryset is None:
            queryset = Product.objects.select_related(
                'category'
            ).prefetch_related(
                Prefetch('images', queryset=ProductImage.objects.order_by('display_order')),
                'tags'
            ).filter(is_active=True)
            
            # 缓存1小时
            cache.set(cache_key, queryset, 3600)
            
        return queryset

2. 序列化器性能优化

延迟加载与预计算

class OptimizedProductSerializer(serializers.ModelSerializer):
    """优化性能的序列化器"""
    category_name = serializers.CharField(source='category.name', read_only=True)
    image_count = serializers.SerializerMethodField()
    average_rating = serializers.SerializerMethodField()
    
    class Meta:
        model = Product
        fields = [
            'id', 'name', 'price', 'category_name',
            'image_count', 'average_rating', 'stock_status'
        ]
    
    def get_image_count(self, obj):
        # 使用预取的数据,避免额外查询
        return obj.images.count() if hasattr(obj, 'images') else 0
    
    def get_average_rating(self, obj):
        # 预计算评分,避免实时计算
        if hasattr(obj, 'avg_rating'):
            return obj.avg_rating
        return obj.reviews.aggregate(avg=Avg('rating'))['avg']
    
    def to_representation(self, instance):
        """重写表示方法,控制输出字段"""
        data = super().to_representation(instance)
        
        # 根据请求上下文动态调整输出
        request = self.context.get('request')
        if request and not request.user.is_staff:
            # 普通用户隐藏敏感字段
            data.pop('cost_price', None)
            data.pop('supplier_info', None)
            
        return data

3. 缓存策略实施

视图级缓存

from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_page
from django.views.decorators.vary import vary_on_cookie

class CachedProductViewSet(viewsets.ReadOnlyModelViewSet):
    """带缓存的只读视图集"""
    
    @method_decorator(cache_page(60 * 15))  # 缓存15分钟
    @method_decorator(vary_on_cookie)
    def list(self, request, *args, **kwargs):
        return super().list(request, *args, **kwargs)
    
    @method_decorator(cache_page(60 * 30))  # 缓存30分钟
    def retrieve(self, request, *args, **kwargs):
        return super().retrieve(request, *args, **kwargs)

Redis缓存集成

# config/settings/prod.py
CACHES = {
    'default': {
        'BACKEND': 'django_redis.cache.RedisCache',
        'LOCATION': 'redis://127.0.0.1:6379/1',
        'OPTIONS': {
            'CLIENT_CLASS': 'django_redis.client.DefaultClient',
            'PARSER_CLASS': 'redis.connection.HiredisParser',
            'CONNECTION_POOL_CLASS': 'redis.BlockingConnectionPool',
            'CONNECTION_POOL_CLASS_KWARGS': {
                'max_connections': 50,
                'timeout': 20,
            },
            'MAX_CONNECTIONS': 1000,
            'PICKLE_VERSION': -1,
        },
        'KEY_PREFIX': 'api_cache',
        'TIMEOUT': 300,  # 默认5分钟
    }
}

# 缓存装饰器
from django.core.cache import cache
from functools import wraps

def cache_result(timeout=300):
    """缓存装饰器"""
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            cache_key = f"{func.__name__}_{args}_{kwargs}"
            result = cache.get(cache_key)
            
            if result is None:
                result = func(*args, **kwargs)
                cache.set(cache_key, result, timeout)
                
            return result
        return wrapper
    return decorator

Redis缓存服务架构图

图3:Redis缓存服务架构图 - 展示Redis在API性能优化中的缓存层作用和数据流处理机制

生态集成方案:构建完整的API开发生态

1. API文档自动化

使用drf-yasg生成OpenAPI文档

# 安装依赖
pip install drf-yasg

# 配置文档生成
from drf_yasg import openapi
from drf_yasg.views import get_schema_view
from drf_yasg import openapi

schema_view = get_schema_view(
    openapi.Info(
        title="电商平台API",
        default_version='v1',
        description="电商平台RESTful API接口文档",
        terms_of_service="https://www.example.com/terms/",
        contact=openapi.Contact(email="contact@example.com"),
        license=openapi.License(name="BSD License"),
    ),
    public=True,
    permission_classes=[permissions.AllowAny],
)

urlpatterns = [
    path('swagger/', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
    path('redoc/', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'),
]

2. 监控与日志系统

结构化日志配置

# config/logging.py
import json_log_formatter
import structlog

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'json': {
            '()': 'pythonjsonlogger.jsonlogger.JsonFormatter',
            'format': '%(asctime)s %(levelname)s %(name)s %(message)s'
        },
        'verbose': {
            'format': '{levelname} {asctime} {module} {process:d} {thread:d} {message}',
            'style': '{',
        },
    },
    'handlers': {
        'file': {
            'level': 'INFO',
            'class': 'logging.handlers.RotatingFileHandler',
            'filename': 'logs/api.log',
            'maxBytes': 1024 * 1024 * 100,  # 100MB
            'backupCount': 10,
            'formatter': 'json',
        },
        'console': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',
            'formatter': 'verbose',
        },
    },
    'loggers': {
        'django': {
            'handlers': ['file', 'console'],
            'level': 'INFO',
            'propagate': True,
        },
        'api': {
            'handlers': ['file', 'console'],
            'level': 'DEBUG',
            'propagate': False,
        },
    },
}

# 请求响应日志中间件
class RequestResponseLogMiddleware:
    """记录API请求响应日志"""
    
    def __init__(self, get_response):
        self.get_response = get_response
        self.logger = structlog.get_logger(__name__)
        
    def __call__(self, request):
        # 记录请求
        self.logger.info(
            'api_request',
            method=request.method,
            path=request.path,
            user_id=request.user.id if request.user.is_authenticated else None,
            ip_address=self.get_client_ip(request)
        )
        
        # 处理请求
        start_time = time.time()
        response = self.get_response(request)
        duration = time.time() - start_time
        
        # 记录响应
        self.logger.info(
            'api_response',
            method=request.method,
            path=request.path,
            status_code=response.status_code,
            duration=duration,
            user_id=request.user.id if request.user.is_authenticated else None
        )
        
        return response
    
    def get_client_ip(self, request):
        x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
        if x_forwarded_for:
            ip = x_forwarded_for.split(',')[0]
        else:
            ip = request.META.get('REMOTE_ADDR')
        return ip

3. 测试策略与持续集成

API测试套件

# tests/test_api.py
from django.urls import reverse
from rest_framework.test import APITestCase
from rest_framework import status
from django.contrib.auth import get_user_model
from apps.products.models import Product

User = get_user_model()

class ProductAPITestCase(APITestCase):
    """产品API测试用例"""
    
    def setUp(self):
        # 创建测试用户
        self.user = User.objects.create_user(
            username='testuser',
            email='test@example.com',
            password='testpass123'
        )
        
        # 创建测试产品
        self.product = Product.objects.create(
            name='测试产品',
            sku='TEST001',
            price=99.99,
            stock_quantity=100,
            created_by=self.user
        )
        
        # 获取认证令牌
        url = reverse('token_obtain_pair')
        response = self.client.post(url, {
            'username': 'testuser',
            'password': 'testpass123'
        })
        self.token = response.data['access']
        
    def test_product_list(self):
        """测试产品列表API"""
        url = reverse('product-list')
        self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {self.token}')
        response = self.client.get(url)
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(len(response.data['results']), 1)
        self.assertEqual(response.data['results'][0]['name'], '测试产品')
    
    def test_product_create(self):
        """测试产品创建API"""
        url = reverse('product-list')
        self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {self.token}')
        
        data = {
            'name': '新产品',
            'sku': 'NEW001',
            'price': 199.99,
            'stock_quantity': 50
        }
        
        response = self.client.post(url, data, format='json')
        
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertEqual(Product.objects.count(), 2)
        self.assertEqual(response.data['name'], '新产品')
    
    def test_rate_limiting(self):
        """测试API限流"""
        url = reverse('product-list')
        self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {self.token}')
        
        # 快速发起多个请求
        for _ in range(11):
            response = self.client.get(url)
            
        # 第11个请求应该被限流
        self.assertEqual(response.status_code, status.HTTP_429_TOO_MANY_REQUESTS)

4. 部署与性能监控

Docker容器化部署

# Dockerfile
FROM python:3.9-slim

# 设置工作目录
WORKDIR /app

# 设置环境变量
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
ENV DJANGO_SETTINGS_MODULE=config.settings.prod

# 安装系统依赖
RUN apt-get update && apt-get install -y \
    gcc \
    postgresql-client \
    && rm -rf /var/lib/apt/lists/*

# 安装Python依赖
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# 复制项目代码
COPY . .

# 收集静态文件
RUN python manage.py collectstatic --noinput

# 运行Gunicorn
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "--workers", "4", "config.wsgi:application"]

性能监控配置

# config/settings/prod.py
# Prometheus监控配置
INSTALLED_APPS += [
    'django_prometheus',
]

MIDDLEWARE = [
    'django_prometheus.middleware.PrometheusBeforeMiddleware',
    # ... 其他中间件
    'django_prometheus.middleware.PrometheusAfterMiddleware',
]

# 添加监控端点
urlpatterns += [
    path('metrics/', include('django_prometheus.urls')),
]

# Sentry错误监控
import sentry_sdk
from sentry_sdk.integrations.django import DjangoIntegration

sentry_sdk.init(
    dsn="https://your-sentry-dsn@sentry.io/your-project",
    integrations=[DjangoIntegration()],
    traces_sample_rate=1.0,
    send_default_pii=True
)

总结:构建可维护的API架构

通过本文的完整指南,我们系统性地探讨了使用Django REST Framework构建企业级API的最佳实践。从核心概念解析实战架构设计,再到性能优化策略生态集成方案,每个环节都体现了现代API开发的专业要求。

关键要点回顾

  1. 架构设计:采用四层分离架构,确保代码的可维护性和可扩展性
  2. 安全策略:实现JWT认证+RBAC权限控制,保障API安全性
  3. 性能优化:通过查询优化、缓存策略和异步处理提升响应速度
  4. 监控体系:建立完整的日志、监控和告警机制
  5. 开发流程:集成自动化测试、文档生成和持续部署

在实际开发中,建议根据业务规模选择合适的架构复杂度。小型项目可以从基础的ModelViewSet开始,逐步引入缓存、监控等高级特性。大型项目则应从一开始就建立完善的架构规范,确保系统的长期可维护性。

DRF的强大之处在于其灵活性可扩展性,开发者可以根据具体需求选择合适的组件和配置。通过本文提供的实战方案,您可以快速构建出高性能、高可用的RESTful API服务,为现代Web应用提供坚实的数据支撑。

【免费下载链接】Python-100-Days Python - 100天从新手到大师 【免费下载链接】Python-100-Days 项目地址: https://gitcode.com/GitHub_Trending/py/Python-100-Days

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值