爱租房/所有api接口的实现

#主项目urls.py中
from django.conf import settings
from django.contrib import admin
from django.urls import path, include
from rest_framework_swagger.views import get_swagger_view

schema_view = get_swagger_view(title='项目接口文档')

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/', include('api.urls')),
    path('api/docs/', schema_view),
]

# urlpatterns += static(settings.MEDIA_URL,
#                       document_root=settings.MEDIA_ROOT)
#
if settings.DEBUG:

    import debug_toolbar

    urlpatterns.insert(0, path('__debug__/', include(debug_toolbar.urls)))





#settings.py中修改
	
	# djangorestframework的配置
REST_FRAMEWORK = {
    'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema',
    # # 配置默认页面大小
    'PAGE_SIZE': 5,
    # 配置默认的分页类
    'DEFAULT_PAGINATION_CLASS': 'api.helpers.CustomPagePagination',}

INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'debug_toolbar',
'rest_framework',
'rest_framework_swagger',
'django_filters',
'common',
'api',
]

#api接口下

#新建consts.py,代码如下
MAX_PHOTO_SIZE = int(2.5 * 1024 * 1024)

MOBILE_CODE_SUCCESS = (100001, '短信验证码发送成功')
CODE_TOO_FREQUENCY = (100002, '请不要在120秒以内重复发送手机验证码')
INVALID_TEL_NUM = (100003, '请提供有效的手机号')

USER_LOGIN_SUCCESS = (200001, '用户登录成功')
USER_LOGIN_FAILED = (200002, '用户名或密码错误')
INVALID_LOGIN_INFO = (200003, '请输入有效的登录信息')

FILE_UPLOAD_SUCCESS = (300001, '文件上传成功')
FILE_SIZE_EXCEEDED = (300002, f'未指定上传文件或文件大小超过{MAX_PHOTO_SIZE}字节')


#serialiezers.py中
import ujson
from django.core.cache import caches
from django.db.models import Q
from django.db.transaction import atomic

from django_redis import get_redis_connection
from rest_framework import serializers
from rest_framework.exceptions import ValidationError

from common.models import District, Agent, Estate, HouseType, Tag, \
    HouseInfo, HousePhoto, User, Role, UserRole
from common.utils import to_md5_hex
from common.validators import USERNAME_PATTERN, TEL_PATTERN, EMAIL_PATTERN


class DistrictSimpleSerializer(serializers.ModelSerializer):
    """地区简单序列化器"""

    class Meta:
        model = District
        fields = ('distid', 'name')


class DistrictDetailSerializer(serializers.ModelSerializer):
    """地区详情序列化器"""
    cities = serializers.SerializerMethodField()

    @staticmethod
    def get_cities(district):
        redis_cli = get_redis_connection()
        data = redis_cli.get(f'izufang:district:{district.distid}:cities')
        if data:
            data = ujson.loads(data)
        else:
            queryset = District.objects.filter(parent=district).only('name')
            data = DistrictSimpleSerializer(queryset, many=True).data
            redis_cli.set(f'izufang:district:{district.distid}:cities',
                          ujson.dumps(data), ex=900)
        return data

    class Meta:
        model = District
        exclude = ('parent', )


class AgentSimpleSerializer(serializers.ModelSerializer):
    """经理人简单序列化器"""

    class Meta:
        model = Agent
        fields = ('agentid', 'name', 'tel', 'servstar')


class AgentCreateSerializer(serializers.ModelSerializer):
    """创建经理人序列化器"""

    class Meta:
        model = Agent
        exclude = ('estates', )


class AgentDetailSerializer(serializers.ModelSerializer):
    """经理人详情序列化器"""
    estates = serializers.SerializerMethodField()

    @staticmethod
    def get_estates(agent):
        queryset = agent.estates.all().order_by('-hot')[:5]
        return EstateSimpleSerializer(queryset, many=True).data

    class Meta:
        model = Agent
        fields = '__all__'


class EstateSimpleSerializer(serializers.ModelSerializer):
    """楼盘简单序列化器"""

    class Meta:
        model = Estate
        fields = ('estateid', 'name', 'hot')


class EstateCreateSerializer(serializers.ModelSerializer):
    """创建楼盘序列化器"""

    class Meta:
        model = Estate
        fields = '__all__'


class EstateDetailSerializer(serializers.ModelSerializer):
    """楼盘详情序列化器"""
    district = DistrictSimpleSerializer()

    class Meta:
        model = Estate
        fields = '__all__'


class HouseTypeSerializer(serializers.ModelSerializer):
    """户型序列化器"""

    class Meta:
        model = HouseType
        fields = '__all__'


class TagSerializer(serializers.ModelSerializer):
    """房源标签序列化器"""

    class Meta:
        model = Tag
        fields = '__all__'


class HouseInfoSimpleSerializer(serializers.ModelSerializer):
    """房源基本信息序列化器"""
    mainphoto = serializers.SerializerMethodField()
    district = serializers.SerializerMethodField()
    type = HouseTypeSerializer()
    tags = TagSerializer(many=True)

    @staticmethod
    def get_mainphoto(houseinfo):
        return '/media/images/' + houseinfo.mainphoto

    @staticmethod
    def get_district(houseinfo):
        return DistrictSimpleSerializer(houseinfo.district_level3).data

    class Meta:
        model = HouseInfo
        fields = ('houseid', 'title', 'area', 'floor', 'totalfloor',
                  'price', 'priceunit', 'mainphoto', 'street',
                  'district', 'type', 'tags')


class HouseInfoCreateSerializer(serializers.ModelSerializer):
    """创建房源序列化器"""

    class Meta:
        model = HouseInfo
        fields = '__all__'


class HouseInfoDetailSerializer(serializers.ModelSerializer):
    """房源详情序列化器"""
    photos = serializers.SerializerMethodField()
    district = serializers.SerializerMethodField()
    type = HouseTypeSerializer()
    agent = AgentSimpleSerializer()
    estate = EstateSimpleSerializer()
    tags = TagSerializer(many=True)

    @staticmethod
    def get_photos(houseinfo):
        queryset = HousePhoto.objects.filter(house=houseinfo)
        return HousePhotoSerializer(queryset, many=True).data

    @staticmethod
    def get_district(houseinfo):
        return DistrictSimpleSerializer(houseinfo.district_level3).data

    class Meta:
        model = HouseInfo
        exclude = ('district_level2', 'district_level3', 'user')


class HousePhotoSerializer(serializers.ModelSerializer):
    """房源照片序列化器"""

    class Meta:
        model = HousePhoto
        fields = ('photoid', 'path')


class UserSimpleSerializer(serializers.ModelSerializer):
    """用户简单序列化器"""

    class Meta:
        model = User
        exclude = ('password', 'roles')


class UserUpdateSerializer(serializers.ModelSerializer):
    """更新用户序列化器"""

    class Meta:
        model = User
        fields = ('realname', 'tel', 'email', 'sex')


class UserCreateSerializer(serializers.ModelSerializer):
    """创建用户序列化器"""
    username = serializers.RegexField(regex=USERNAME_PATTERN)
    password = serializers.CharField(min_length=6)
    realname = serializers.RegexField(regex=r'[\u4e00-\u9fa5]{2,20}')
    tel = serializers.RegexField(regex=TEL_PATTERN)
    email = serializers.RegexField(regex=EMAIL_PATTERN)
    code = serializers.CharField(write_only=True, min_length=6, max_length=6)

    def validate(self, attrs):
        code_from_user = attrs['code']
        code_from_redis = caches['default'].get(f'{attrs["tel"]}:valid')
        if code_from_redis != code_from_user:
            raise ValidationError('请输入有效的手机验证码', 'invalid')
        user = User.objects.filter(Q(username=attrs['username']) |
                                   Q(tel=attrs['tel']) |
                                   Q(email=attrs['email']))
        if user:
            raise ValidationError('用户名、手机或邮箱已被注册', 'invalid')
        return attrs

    def create(self, validated_data):
        del validated_data['code']
        caches['default'].delete(f'{validated_data["tel"]}:valid')
        validated_data['password'] = to_md5_hex(validated_data['password'])
        with atomic():
            user = User.objects.create(**validated_data)
            role = Role.objects.get(roleid=1)
            UserRole.objects.create(user=user, role=role)
        return user

    class Meta:
        model = User
        exclude = ('userid', 'regdate', 'point', 'lastvisit', 'roles')


class RoleSimpleSerializer(serializers.ModelSerializer):

    class Meta:
        model = Role
        fields = ('roleid', )


#新建helpers.py中

from functools import lru_cache

import jwt
from django.db.models import Q, Prefetch
from django_filters import filterset
from jwt import InvalidTokenError
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from rest_framework.pagination import PageNumberPagination, CursorPagination
from rest_framework.response import Response

from common.models import Estate, HouseInfo, User, Role
from izufang.settings import SECRET_KEY


class DefaultResponse(Response):
    """定义返回JSON数据的响应类"""

    def __init__(self, code=100000, message='操作成功',
                 data=None, status=None, template_name=None,
                 headers=None, exception=False, content_type=None):
        _data = {'code': code, 'message': message}
        if data:
            _data.update(data)
        super().__init__(_data, status, template_name,
                         headers, exception, content_type)


class LoginRequiredAuthentication(BaseAuthentication):
    """登录认证"""

    # 如果用户身份验证成功需要返回一个二元组(user, token)
    def authenticate(self, request):
        token = request.META.get('HTTP_TOKEN')
        if token:
            try:
                payload = jwt.decode(token, SECRET_KEY)
                user = User()
                user.userid = payload['data']['userid']
                user.is_authenticated = True
                return user, token
            except InvalidTokenError:
                raise AuthenticationFailed('无效的令牌或令牌已过期')
        raise AuthenticationFailed('请提供用户身份令牌')


# class RbacPermission(BasePermission):
#     """RBAC授权"""
#
#     # 返回True表示有操作权限,返回False表示没有操作权限
#     def has_permission(self, request, view):
#         privs = get_privs_by_userid(request.user.userid)
#         for priv in privs:
#             if request.path.startswith(priv.url) and \
#                     request.method == priv.method:
#                 return True
#         return False


@lru_cache(maxsize=256)
def get_privs_by_userid(userid):
    user = User.objects.filter(userid=userid)\
        .prefetch_related(
            Prefetch(
                'roles',
                queryset=Role.objects.all().prefetch_related('privs'))
        ).first()
    return [priv for role in user.roles.all()
            for priv in role.privs.all()]


class CustomPagePagination(PageNumberPagination):
    """自定义页码分页类"""
    page_size_query_param = 'size'
    max_page_size = 50


class AgentCursorPagination(CursorPagination):
    """经理人游标分页类"""
    page_size_query_param = 'size'
    max_page_size = 50
    ordering = '-agentid'


class EstateFilterSet(filterset.FilterSet):
    """自定义楼盘筛选器"""
    # filter(name__contains=name)
    name = filterset.CharFilter(lookup_expr='contains')
    # filter(hot__gte=minhot, hot__lte=maxhot)
    minhot = filterset.NumberFilter(field_name='hot', lookup_expr='gte')
    maxhot = filterset.NumberFilter(field_name='hot', lookup_expr='lte')
    dist = filterset.NumberFilter(field_name='district')

    class Meta:
        model = Estate
        fields = ('name', 'minhot', 'maxhot', 'dist')


class HouseInfoFilterSet(filterset.FilterSet):
    """自定义房源筛选器"""
    title = filterset.CharFilter(lookup_expr='contains')
    minprice = filterset.NumberFilter(field_name='price', lookup_expr='gte')
    maxprice = filterset.NumberFilter(field_name='price', lookup_expr='lte')
    minarea = filterset.NumberFilter(field_name='area', lookup_expr='gte')
    maxarea = filterset.NumberFilter(field_name='area', lookup_expr='lte')
    district = filterset.NumberFilter(method='filter_by_district')

    @staticmethod
    def filter_by_district(queryset, name, value):
        return queryset.filter(Q(district_level2=value) |
                               Q(district_level3=value))

    class Meta:
        model = HouseInfo
        fields = ('title', 'minprice', 'maxprice', 'minarea', 'maxarea', 'type', 'district')




#views.py中
import datetime
import os
import random

import jwt
import ujson
from django.core.cache import caches
from django.db.models import Prefetch, Q
from django.db.transaction import atomic
from django.utils import timezone
from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_page
from django_filters.rest_framework import DjangoFilterBackend
from django_redis import get_redis_connection
from rest_framework.decorators import api_view, action
from rest_framework.filters import OrderingFilter
from rest_framework.generics import ListAPIView
from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet

from api.consts import MAX_PHOTO_SIZE, FILE_UPLOAD_SUCCESS, FILE_SIZE_EXCEEDED, CODE_TOO_FREQUENCY, MOBILE_CODE_SUCCESS, \
    INVALID_TEL_NUM, USER_LOGIN_SUCCESS, USER_LOGIN_FAILED, INVALID_LOGIN_INFO
from api.helpers import EstateFilterSet, HouseInfoFilterSet, \
    LoginRequiredAuthentication, DefaultResponse
from api.serializers import DistrictSimpleSerializer, DistrictDetailSerializer, \
    AgentCreateSerializer, AgentDetailSerializer, AgentSimpleSerializer, \
    HouseTypeSerializer, TagSerializer, EstateCreateSerializer, \
    EstateDetailSerializer, EstateSimpleSerializer, HouseInfoDetailSerializer, \
    HousePhotoSerializer, HouseInfoCreateSerializer, HouseInfoSimpleSerializer, \
    UserCreateSerializer, UserUpdateSerializer, UserSimpleSerializer
from common.models import District, Agent, HouseType, Tag, User, LoginLog, \
    HousePhoto, Estate, HouseInfo
from common.utils import gen_mobile_code, send_sms_by_luosimao, to_md5_hex, \
    get_ip_address, upload_stream_to_qiniu
from common.validators import check_tel, check_username, check_email
from izufang.settings import SECRET_KEY


@api_view(('POST', ))
def upload_house_photo(request):
    file_obj = request.FILES.get('mainphoto')
    if file_obj and len(file_obj) < MAX_PHOTO_SIZE:
        prefix = to_md5_hex(file_obj.file)
        filename = f'{prefix}{os.path.splitext(file_obj.name)[1]}'
        upload_stream_to_qiniu.delay(file_obj, filename, len(file_obj))
        photo = HousePhoto()
        photo.path = f'http://qea6re99o.bkt.clouddn.com/{filename}'
        photo.ismain = True
        photo.save()
        resp = DefaultResponse(*FILE_UPLOAD_SUCCESS, data={
            'photoid': photo.photoid,
            'url': photo.path
        })
    else:
        resp = DefaultResponse(*FILE_SIZE_EXCEEDED)
    return resp


@api_view(('GET', ))
def get_code_by_sms(request, tel):
    """获取短信验证码"""
    if check_tel(tel):
        if caches['default'].get(f'{tel}:block'):
            resp = DefaultResponse(*CODE_TOO_FREQUENCY)
        else:
            code = gen_mobile_code()
            message = f'您的短信验证码是{code},打死也不能告诉别人哟!【Python小课】'
            send_sms_by_luosimao.apply_async((tel, message),
                                             countdown=random.random() * 5)
            caches['default'].set(f'{tel}:block', code, timeout=120)
            caches['default'].set(f'{tel}:valid', code, timeout=1800)
            resp = DefaultResponse(*MOBILE_CODE_SUCCESS)
    else:
        resp = DefaultResponse(*INVALID_TEL_NUM)
    return resp


@api_view(('POST', ))
def login(request):
    """登录(获取用户身份令牌)"""
    username = request.data.get('username')
    password = request.data.get('password')
    if (check_username(username) or check_tel(username) or
            check_email(username)) and len(password) >= 6:
        password = to_md5_hex(password)
        q = Q(username=username, password=password) | \
            Q(tel=username, password=password) | \
            Q(email=username, password=password)
        user = User.objects.filter(q)\
            .only('username', 'realname').first()
        if user:
            # 用户登录成功通过JWT生成用户身份令牌
            payload = {
                'exp': datetime.datetime.utcnow() + datetime.timedelta(days=1),
                'data': {
                    'userid': user.userid,
                }
            }
            token = jwt.encode(payload, SECRET_KEY, algorithm='HS256').decode()
            with atomic():
                current_time = timezone.now()
                if not user.lastvisit or \
                        (current_time - user.lastvisit).days >= 1:
                    user.point += 2
                    user.lastvisit = current_time
                    user.save()
                loginlog = LoginLog()
                loginlog.user = user
                loginlog.logdate = current_time
                loginlog.ipaddr = get_ip_address(request)
                loginlog.save()
            resp = DefaultResponse(*USER_LOGIN_SUCCESS, data={
                'token': token, 'username': user.username, 'realname': user.realname
            })
        else:
            resp = DefaultResponse(*USER_LOGIN_FAILED)
    else:
        resp = DefaultResponse(*INVALID_LOGIN_INFO)
    return resp


@api_view(('GET', ))
@cache_page(timeout=365 * 86400)
def get_provinces(request):
    """获取省级行政单位"""
    queryset = District.objects.filter(parent__isnull=True).only('name')
    serializer = DistrictSimpleSerializer(queryset, many=True)
    return Response({'results': serializer.data})


@api_view(('GET', ))
def get_district(request, distid):
    """获取地区详情"""
    redis_cli = get_redis_connection()
    data = redis_cli.get(f'izufang:district:{distid}')
    if data:
        data = ujson.loads(data)
    else:
        district = District.objects.filter(distid=distid).defer('parent').first()
        data = DistrictDetailSerializer(district).data
        redis_cli.set(f'izufang:district:{distid}', ujson.dumps(data), ex=900)
    return Response(data)


@method_decorator(decorator=cache_page(timeout=86400), name='get')
class HotCityView(ListAPIView):
    """热门城市视图
    get:
        获取热门城市
    """
    queryset = District.objects.filter(ishot=True).only('name')
    serializer_class = DistrictSimpleSerializer
    pagination_class = None


@method_decorator(decorator=cache_page(timeout=120), name='list')
@method_decorator(decorator=cache_page(timeout=300), name='retrieve')
class AgentViewSet(ModelViewSet):
    """经理人视图
    list:
        获取经理人列表
    retrieve:
        获取经理人详情
    create:
        创建经理人
    update:
        更新经理人信息
    partial_update:
        更新经理人信息
    delete:
        删除经理人
    """
    queryset = Agent.objects.all()

    def get_queryset(self):
        name = self.request.GET.get('name')
        if name:
            self.queryset = self.queryset.filter(name__contains=name)
        servstar = self.request.GET.get('servstar')
        if servstar:
            self.queryset = self.queryset.filter(servstar__gte=servstar)
        if self.action == 'list':
            self.queryset = self.queryset.only('name', 'tel', 'servstar')
        else:
            self.queryset = self.queryset.prefetch_related(
                Prefetch('estates',
                         queryset=Estate.objects.all().only('name').order_by('-hot'))
            )
        return self.queryset.order_by('-servstar')

    def get_serializer_class(self):
        if self.action in ('create', 'update'):
            return AgentCreateSerializer
        return AgentDetailSerializer if self.action == 'retrieve' \
            else AgentSimpleSerializer


@method_decorator(decorator=cache_page(timeout=86400), name='list')
@method_decorator(decorator=cache_page(timeout=86400), name='retrieve')
class HouseTypeViewSet(ModelViewSet):
    """户型视图集"""
    queryset = HouseType.objects.all()
    serializer_class = HouseTypeSerializer
    pagination_class = None


@method_decorator(decorator=cache_page(timeout=3600), name='list')
class TagViewSet(ModelViewSet):
    """房源标签视图集"""
    queryset = Tag.objects.all()
    serializer_class = TagSerializer


@method_decorator(decorator=cache_page(timeout=300), name='list')
@method_decorator(decorator=cache_page(timeout=300), name='retrieve')
class EstateViewSet(ModelViewSet):
    """楼盘视图集"""
    queryset = Estate.objects.all()
    filter_backends = (DjangoFilterBackend, OrderingFilter)
    # 只能做精确查询,而且多个条件间只能是而且的关系
    # filter_fields = ('name', 'hot', 'district')
    filterset_class = EstateFilterSet
    ordering = '-hot'
    ordering_fields = ('district', 'hot', 'name')
    # authentication_classes = (LoginRequiredAuthentication, )

    def get_queryset(self):
        if self.action == 'list':
            queryset = self.queryset.only('name', 'hot')
        else:
            queryset = self.queryset\
                .defer('district__parent', 'district__ishot', 'district__intro')\
                .select_related('district')
        return queryset

    def get_serializer_class(self):
        if self.action in ('create', 'update'):
            return EstateCreateSerializer
        return EstateDetailSerializer if self.action == 'retrieve' \
            else EstateSimpleSerializer


@method_decorator(decorator=cache_page(timeout=120), name='list')
@method_decorator(decorator=cache_page(timeout=300), name='retrieve')
class HouseInfoViewSet(ModelViewSet):
    """房源视图集"""
    queryset = HouseInfo.objects.all()
    serializer_class = HouseInfoDetailSerializer
    filter_backends = (DjangoFilterBackend, OrderingFilter)
    filterset_class = HouseInfoFilterSet
    ordering = ('-pubdate', )
    ordering_fields = ('pubdate', 'price')

    @action(methods=('GET', ), detail=True)
    def photos(self, request, pk):
        queryset = HousePhoto.objects.filter(house=self.get_object())
        return Response(HousePhotoSerializer(queryset, many=True).data)

    def get_queryset(self):
        if self.action == 'list':
            return self.queryset\
                .only('houseid', 'title', 'area', 'floor', 'totalfloor', 'price',
                      'mainphoto', 'priceunit', 'street', 'type',
                      'district_level3__distid', 'district_level3__name')\
                .select_related('district_level3', 'type')\
                .prefetch_related('tags')
        return self.queryset\
            .defer('user', 'district_level2',
                   'district_level3__parent', 'district_level3__ishot', 'district_level3__intro',
                   'estate__district', 'estate__hot', 'estate__intro',
                   'agent__realstar', 'agent__profstar', 'agent__certificated')\
            .select_related('district_level3', 'type', 'estate', 'agent')\
            .prefetch_related('tags')

    def get_serializer_class(self):
        if self.action in ('create', 'update'):
            return HouseInfoCreateSerializer
        return HouseInfoDetailSerializer if self.action == 'retrieve' \
            else HouseInfoSimpleSerializer


class UserViewSet(ModelViewSet):
    """用户模型视图集"""
    queryset = User.objects.all()

    def get_serializer_class(self):
        if self.action == 'create':
            return UserCreateSerializer
        elif self.action == 'update':
            return UserUpdateSerializer
        return UserSimpleSerializer



#urls.py中
from django.urls import path
from rest_framework.routers import DefaultRouter

from api.views import get_provinces, get_district, HotCityView, \
    AgentViewSet, HouseTypeViewSet, EstateViewSet, TagViewSet, \
    HouseInfoViewSet, get_code_by_sms, login, UserViewSet, \
    upload_house_photo

urlpatterns = [
    path('photos/', upload_house_photo),
    path('tokens/', login),
    path('mobile/<str:tel>/', get_code_by_sms),
    path('districts/', get_provinces),
    path('districts/<int:distid>/', get_district),
    path('hotcities/', HotCityView.as_view()),
]

router = DefaultRouter()
router.register('housetypes', HouseTypeViewSet)
router.register('estates', EstateViewSet)
router.register('agents', AgentViewSet)
router.register('tags', TagViewSet)
router.register('houseinfos', HouseInfoViewSet)
router.register('users', UserViewSet)
urlpatterns += router.urls
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

像风一样的男人@

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

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

抵扣说明:

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

余额充值