Django项目实战(一):图书管理系统---第五阶段---基于Forms组件(注册功能)+注销功能+事务

下面是小凰凰的简介,看下吧!
💗人生态度:珍惜时间,渴望学习,热爱音乐,把握命运,享受生活
💗学习技能:网络 -> 云计算运维 -> python全栈( 当前正在学习中)
💗您的点赞、收藏、关注是对博主创作的最大鼓励,在此谢过!
有相关技能问题可以写在下方评论区,我们一起学习,一起进步。
后期会不断更新python全栈学习笔记,秉着质量博文为原则,写好每一篇博文。

一、效果展示

1、注册功能

注册界面:
在这里插入图片描述输入信息,前端会进行基本的格式检查:
在这里插入图片描述
Forms组件的后端数据校验:
在这里插入图片描述
后端不仅会校验数据格式,还会校验数据在后端数据库的重复性!

注册成功效果:

在这里插入图片描述
然后你就可以用自己注册的账号登录图书管理系统了!
在这里插入图片描述

2、注销功能

在这里插入图片描述

注销可不是单纯的跳转,注销之后就无法进入首页了!需要重新登录!

相比第四阶段改进点添加注册功能、注销功能、实现了数据库的事务
项目不足之处:
作者信息、书籍信息、出版社信息没有实现分页浏览

二、项目目录展示

在这里插入图片描述还需要把注册页面的URL,以及发送验证码的请求的URL放入自定义中间件的白名单!

中间那个static里面放的是整个项目所需要的一些静态文件!

三、项目源码

下面我只写变动的文件!

1、views.py
from django.shortcuts import render
from app01.models import Book, Publisher, Authors, AuthorDetail
from django.shortcuts import redirect, reverse
from django.http import HttpResponse
from django.contrib import auth
from django import forms
from app01 import common
from django.contrib.auth.models import User
from django.core.exceptions import ValidationError  # 错误管理
from django.db import transaction
import json

code = {}


class UserForm(forms.Form): # 规则校验类
    username = forms.CharField(min_length=2, max_length=6)
    password = forms.CharField(min_length=6, max_length=18)
    email = forms.EmailField(error_messages={"invalid": "邮箱格式错误"})

    # 用户名不能重复
    def clean_username(self):
        val = self.cleaned_data.get('username')  # 先取值
        ret = User.objects.filter(username=val)  # 查看数据库中是否有这个值
        if not ret:  # 如果在数据库中没有,就通过匹配
            return val
        else:  # 否则,说明在数据库中有该用户名
            raise ValidationError("用户名已存在")

    # 密码不能是纯数字
    def clean_password(self):
        val = self.cleaned_data.get('password')  # 先取值
        if val.isdigit():  # 判断是否是数字
            raise ValidationError("密码不能是纯数字")
        else:
            return val

    def clean_email(self):
        val = self.cleaned_data.get('email')
        ret = User.objects.filter(email=val)
        if not ret:
            return val
        else:
            raise ValidationError("该邮箱已注册")

@transaction.atomic
def registry(request):
    if request.method == 'GET':
        return render(request, 'register.html')
    else:
        res_dic = {}
        user_data_dic = json.loads(request.body.decode('utf8'))
        emailcode = user_data_dic['emailcode']
        email = user_data_dic['email']
        if emailcode == '' or code.get(email).lower() != emailcode.lower():
            res_dic['emailcode'] = '验证码错误!'
            res_json = json.dumps(res_dic).encode('utf8')
            return HttpResponse(res_json)
        fm = UserForm(user_data_dic)
        if fm.is_valid():
            User.objects.create_user(**fm.cleaned_data)
            res_dic['req_status'] = 'ok'
            res_json = json.dumps(res_dic).encode('utf8')
            return HttpResponse(res_json)
        else:
            error = fm.errors
            for i in error:
                res_dic[i] = error[i][0]
            res_json = json.dumps(res_dic).encode('utf8')
            return HttpResponse(res_json)


@transaction.atomic
def emailcode(request):
    email = request.POST.get('email')
    code[email] = common.make_code()
    ver_code = code[email]
    try:
        common.send_mail(email, ver_code)
    except Exception:
        return HttpResponse('no')
    else:
        return HttpResponse('ok')


@transaction.atomic
def login_out(request):
    auth.logout(request)
    return redirect(reverse('login_view'))
2、common.py
import smtplib
import random
from email.mime.text import MIMEText
from email.header import Header
from email.utils import formataddr


# 生成随机验证码
def make_code(n=4):
    res = ''
    for i in range(n):
        num = str(random.randint(1, 9))  # 生成随机1-9,并强转成字符串格式
        num2 = str(random.randint(1, 9))
        big_char = chr(random.randint(65, 90))  # 生成随机A-Z字母
        small_char = chr(random.randint(97, 122))  # 随机生成a-z字母
        get_str = random.choice([num, num2, big_char, small_char])  # 从生成的数字和字母选择一个进行字符串拼接
        res += get_str
    return res


# 传入接收邮件账号和随机验证码,发送邮件
def send_mail(username_recv, code):
    sender = 'zabbixmails@163.com'
    receivers = '{}'.format(username_recv)
    # 三个参数:第一个为邮件正文文本内容,第二个 plain 设置文本格式,第三个 utf-8 设置编码
    message = MIMEText('【 竟成图书管理系统 】你正在注册竟成图书管理系统,验证码{}。转发可能导致账号被盗。'.format(code), 'plain', 'utf-8')
    message['From'] = formataddr(["admin", sender])  # 发送者
    message['To'] = formataddr(["竟成图书管理系统", receivers])  # 接收者

    subject = '来自竟成图书管理系统的消息'
    message['Subject'] = Header(subject, 'utf-8')  # 邮件的主题

    smtpObj = smtplib.SMTP('smtp.163.com', port=25)
    smtpObj.login(user=sender, password='HYDSGRIJQXNMUGWB')  # password并不是邮箱的密码,而是开启邮箱的授权码
    smtpObj.sendmail(sender, receivers, message.as_string())  # 发送邮件
3、middleware.py

注册页面的URL,以及发送验证码的请求的URL放入自定义中间件的白名单!

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import redirect, reverse


class AuthMiddleware(MiddlewareMixin):

    def process_request(self, request):
        white_menu = ['/login/', '/registry/', '/emailcode/']
        if request.path in white_menu:
            return None
        else:
            is_auth = request.user.is_authenticated
            if not is_auth:
                return redirect(reverse('login_view'))
4、urls.py
# 添加了这三条路由!
path('registry/', views.registry, name='registry_view'),
path('emailcode/', views.emailcode, name='emailcode'),
path('login_out', views.login_out, name='login_out'),
5、模板
(1)index.html

加了个注销的a标签,让a标签进行浮动定位到它该去的地方,添加了hover变色!

<a class="login_out" href="{% url 'login_out' %}">注销</a>
<span class="user-name">{{ username }}</span>
<div class="head-img">
    <img src="http://127.0.0.1:8000/static/img/head-img.jpg" alt="">
</div>
(2)login.html

登录界面添加了个注册的href,跳转到注册界面!

<div class="reg-area">还没有账号? <a href="{% url 'registry_view' %}" class="reg-btn">立即注册</a></div>
    <div class='login_title'>
        <span>管理员登录</span>
    </div>
(3)registry.html
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>注册界面</title>
    <meta name="description" content="">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <meta name="robots" content="all,follow">
    <!-- Bootstrap CSS-->
    <link rel="stylesheet" href="../static/css/bootstrap.min.css">
    <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Poppins:300,400,700">
    <link rel="stylesheet" href="../static/css/style.default.css" id="theme-stylesheet">
    <style>
        .bg-white {
            opacity: 0.9;
        }

        #register-emailcode {
            display: inline-block;
            width: 320px;
            margin-right: 20px;
        }

        #regbtn {
            color: white;
        }

        #register-username, #register-email, #register-passwords, #register-password, #register-emailcode {
            font-family: "Microsoft YaHei";
        }

        #send_email_code {
            font-family: "Kaiti SC";
        }

        #send_email_code:hover {
            border: 2px solid skyblue;
            background-color: white;
        }

        #regbtn {
            background: #1AAB8A;
            color: #fff;
            border: none;
            position: relative;
            height: 40px;
            font-size: 1em;
            padding: 0 2em;
            cursor: pointer;
            transition: 800ms ease all;
            outline: none;
        }

        #regbtn:hover {
            background: #fff;
            color: #1AAB8A;
        }

        #regbtn:before, #regbtn:after {
            content: '';
            position: absolute;
            top: 0;
            right: 0;
            height: 2px;
            width: 0;
            background: #1AAB8A;
            transition: 400ms ease all;
        }

        #regbtn:after {
            right: inherit;
            top: inherit;
            left: 0;
            bottom: 0;
        }

        #regbtn:hover:before, #regbtn:hover:after {
            width: 100%;
            transition: 800ms ease all;
        }

        .signup:hover {
            text-decoration: none;
        }

        .reg-modal {
            width: 96px;
            height: 40px;
            position: absolute;
            top: 0px;
            background-color: white;
            opacity: 0.7;
        }

        .after_reg_error {
            color: red;
            font-family: "Songti SC";
            font-size: small;
        }
    </style>
</head>
<body>
{% csrf_token %}
<div class="page login-page">
    <div class="container d-flex align-items-center">
        <div class="form-holder has-shadow">
            <div class="row">
                <!-- Logo & Information Panel-->
                <div class="col-lg-6">
                    <div class="info d-flex align-items-center">
                        <div class="content">
                            <div class="logo">
                                <h1>欢迎注册</h1>
                            </div>
                            <p>图书管理系统</p>
                        </div>
                    </div>
                </div>
                <!-- Form Panel    -->
                <div class="col-lg-6 bg-white">
                    <div class="form d-flex align-items-center">
                        <div class="content">
                            <div class="form-group">
                                <input id="register-username" class="input-material" type="text" name="registerUsername"
                                       placeholder="请输入用户名/姓名">
                                <div class="invalid-feedback">
                                    用户名必须在2~6位之间
                                </div>
                            </div>
                            <div class="form-group">
                                <input id="register-email" class="input-material" type="text"
                                       name="registerEmail" placeholder="请输入邮箱">
                                <div class="invalid-feedback">
                                    邮箱格式错误
                                </div>
                            </div>
                            <div class="form-group">
                                <input id="register-password" class="input-material" type="password"
                                       name="registerPassword" placeholder="请输入密码">
                                <div class="invalid-feedback">
                                    密码必须在6~18位之间,不能是全数字
                                </div>
                            </div>
                            <div class="form-group">
                                <input id="register-passwords" class="input-material" type="password"
                                       name="registerPasswords" placeholder="确认密码">
                                <div class="invalid-feedback">
                                    两次密码必须相同 且在6~18位之间,不能是全数字
                                </div>
                            </div>
                            <div class="form-group">
                                <input id="register-emailcode" class="input-material" type="text"
                                       name="registeremailcode" placeholder="请输入邮箱验证码">
                                <a id="send_email_code" class="btn btn-default btn-default">发送验证码</a>
                                <div class="invalid-feedback">
                                    验证码由4个数字或字母组成
                                </div>
                            </div>
                            <div class="form-group">
                                <button id="regbtn" type="button" name="registerSubmit">注册</button>
                                <div id="reg-shadow" class="reg-modal"></div>
                            </div>
                            <small>已有账号?</small><a href="{% url 'login_view' %}" class="signup">&nbsp;登录</a>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>
<!-- JavaScript files-->
<script src="../static/js/jquery-2.1.1.min.js"></script>
<script src="../static/js/bootstrap.min.js"></script>
<script src="../static/js/jquery.cookie.js"></script>
<script>
    $(function () {
        /*错误class  form-control is-invalid
        正确class  form-control is-valid*/
        var flag_username = false;
        var flag_password = false;
        var flag_passwords = false;
        var flag_email = false;
        var flag_emailcode = false;
        /*验证用户名*/
        var name, passWord, passWords;
        $("#register-username").blur(function () {
            name = $("#register-username").val();
            if (name.length < 2 || name.length > 6) {
                $("#register-username").removeClass("form-control is-valid");
                $("#register-username").addClass("form-control is-invalid");
                flag_username = false;
            } else {
                $("#register-username").removeClass("form-control is-invalid");
                $("#register-username").addClass("form-control is-valid");
                flag_username = true;
            }
            check_isreg()
        })

        /*验证密码*/
        $("#register-password").blur(function () {
            var reg = /^[0-9]+$/
            passWord = $("#register-password").val();
            if (passWord.length < 6 || passWord.length > 18 || reg.test(passWord)) {
                $("#register-password").removeClass("form-control is-valid")
                $("#register-password").addClass("form-control is-invalid");
                flag_password = false;
            } else {
                $("#register-password").removeClass("form-control is-invalid")
                $("#register-password").addClass("form-control is-valid");
                flag_password = true;
            }
            check_isreg()
        })

        /*验证确认密码*/
        $("#register-passwords").blur(function () {
            passWords = $("#register-passwords").val();
            if ((passWord != passWords) || (passWords.length < 6 || passWords.length > 18)) {
                $("#register-passwords").removeClass("form-control is-valid");
                $("#register-passwords").addClass("form-control is-invalid");
                flag_passwords = false;
            } else {
                $("#register-passwords").removeClass("form-control is-invalid");
                $("#register-passwords").addClass("form-control is-valid");
                flag_passwords = true;
            }
            check_isreg()
        })

        /*验证确认邮箱*/
        $("#register-email").blur(function () {
            email = $("#register-email").val();
            email_list = email.split('@')
            if ((email_list.length != 2) || (email_list[0] == '') || (email_list[1] == '' > 18)) {
                $("#register-email").removeClass("form-control is-valid");
                $("#register-email").addClass("form-control is-invalid");
                flag_email = false;
            } else {
                $("#register-email").removeClass("form-control is-invalid");
                $("#register-email").addClass("form-control is-valid");
                flag_email = true;
            }
            check_isreg()
        })

        /*验证码格式检查*/
        $("#register-emailcode").change(function () {
            var emailcode = $("#register-emailcode").val();
            var reg = /^[0-9a-zA-Z]+$/
            if (emailcode.length != 4 || !reg.test(emailcode)) {
                $("#register-emailcode").removeClass("form-control is-valid");
                $("#register-emailcode").addClass("form-control is-invalid");
                flag_emailcode = false;
            } else {
                $("#register-emailcode").removeClass("form-control is-invalid");
                $("#register-emailcode").addClass("form-control is-valid");
                flag_emailcode = true;
            }
            check_isreg()
        })

        $("#regbtn").click(function () {
            var username = $('#register-username').val()
            var email = $('#register-email').val()
            var password = $('#register-password').val()
            var emailcode = $('#register-emailcode').val()
            var Json_data = {
                username: username,
                email: email,
                password: password,
                emailcode: emailcode
            }
            $.ajax({
                url: "{% url 'registry_view' %}",
                type: "post",
                contentType: "json",
                headers: {"X-CSRFToken": $.cookie('csrftoken')},
                data: JSON.stringify(Json_data),
                success: function (response) {
                    res_dic = JSON.parse(response)
                    if (res_dic.emailcode == '验证码错误!') {
                        alert(res_dic.emailcode)
                        $('#register-EmailCode').val('')
                    } else if (res_dic.req_status == 'ok') {
                        alert('注册用户成功!正在跳转到登录界面...')
                        location.href = "{% url 'login_view' %}"
                    } else {
                        for (obj in res_dic) {
                            let ele = `#register-${obj}`
                            $(ele).val('')
                            eval(`flag_${obj}=false`)
                            $("<div>").insertAfter($(ele))
                            $(ele).next().addClass('after_reg_error').text(res_dic[obj])
                            check_isreg()
                            setTimeout(function () {
                                $(ele).next().remove()
                            }, 3000)
                            setTimeout(function () {
                                $(ele).blur()
                            },3000)
                        }
                    }
                }
            })
        })

        // 发送验证码点击事件
        $('#send_email_code').click(function () {
            var email = $('#register-email').val();
            $ele = $(this)
            $ele.addClass('disabled');
            nums = 60;
            $.ajax({
                url: "{% url 'emailcode' %}",
                type: "post",
                data: {
                    email: email,
                    csrfmiddlewaretoken: $("input[name='csrfmiddlewaretoken']").val()
                },
                success: function (response) {
                    if (response == 'ok') {
                        $ele.text(`重新发送(${nums}s)`);
                        clock = setInterval(doLoop, 1000);
                    } else {
                        alert('发送验证码失败,请认真核对邮箱信息!')
                        $ele.removeClass('disabled');
                    }
                }
            })
        })

        function doLoop() {
            nums--;
            if (nums > 0) {
                $ele.text(`重新发送(${nums}s)`)
            } else {
                clearInterval(clock); //清除js定时器
                $ele.removeClass('disabled');
                $ele.text('发送验证码');
                nums = 60; //重置时间
            }
        }

        //如果所有input框都是正确的,才会把注册框的禁用取消
        function check_isreg() {
            if (flag_username && flag_password && flag_passwords && flag_email && flag_emailcode) {
                $('#reg-shadow').removeClass('reg-modal')
            } else {
                $('#reg-shadow').addClass('reg-modal')
            }
        }
    })
</script>
</body>
</html>

四、项目心得体会

1、注册按钮置灰,格式正确才能点击的实现

明晰了前端数据校验流程,注册按钮置灰的方法(为一个div添加一个class,实现:使其变成模态框,必须和按钮一样大小的模态框,采用绝对定位,用模态框覆盖按钮

.reg-modal {
    width: 96px;
    height: 40px;
    position: absolute;
    top: 0px;
    background-color: white;
    opacity: 0.7;
}

还有就是什么时候移除此class,使按钮能够被点击

var flag_username = false;
var flag_password = false;
var flag_passwords = false;
var flag_email = false;
var flag_emailcode = false;

只有当上面所有的flag标志变成true,即是所有框框都有绿色勾勾才移除.reg-modal

//如果所有input框都是正确的,才会把注册框的禁用取消
function check_isreg() {
    if (flag_username && flag_password && flag_passwords && flag_email && flag_emailcode) {
        $('#reg-shadow').removeClass('reg-modal')
    } else {
        $('#reg-shadow').addClass('reg-modal')
    }
}

然后在每一个格式验证最后面,加上上面这个函数!注意:每一个都要加
在这里插入图片描述
注意不能在if else里面加这个函数,因为我们要实现错了就置灰,全部对了就取消置灰!

2、发送验证码的倒计时的实现
// 发送验证码点击事件
$('#send_email_code').click(function () {
    var email = $('#register-email').val();
    $ele = $(this)
    $ele.addClass('disabled');  // 只要点击了发送验证码,马上将其禁用,避免用户下次点击!
    nums = 60;
    $.ajax({
        url: "{% url 'emailcode' %}",
        type: "post",
        data: {
            email: email,
            csrfmiddlewaretoken: $("input[name='csrfmiddlewaretoken']").val()
        },
        success: function (response) {
        	// 验证码发送成功,则执行下面代码出现倒计时
            if (response == 'ok') {
                $ele.text(`重新发送(${nums}s)`);
                clock = setInterval(doLoop, 1000); // 关键步骤
            
            // 验证码发送失败,则取消禁用
            } else {
                alert('发送验证码失败,请认真核对邮箱信息!')
                $ele.removeClass('disabled');
            }
        }
    })
})

function doLoop() {
    nums--;
    if (nums > 0) {
        $ele.text(`重新发送(${nums}s)`)
    } else {
        clearInterval(clock); //清除js定时器
        $ele.removeClass('disabled');
        $ele.text('发送验证码');
        nums = 60; //重置时间
    }
}
3、怎么实现两个视图函数之间的验证码的核对是否一致
# 验证码生成,及邮箱发送验证码的视图函数
code = {}
@transaction.atomic
def emailcode(request):
    email = request.POST.get('email')
    code[email] = common.make_code()
    ver_code = code[email]
    try:
        common.send_mail(email, ver_code)
    except Exception:
        return HttpResponse('no')
    else:
        return HttpResponse('ok')

采用定义一个全局的code字典,发送一个验证码,就以email为键,验证码为值,存入code字典,这样注册就能从code中根据email获取到验证码了!

@transaction.atomic
def registry(request):
    if request.method == 'GET':
        return render(request, 'registry.html')
    else:
        res_dic = {}
        user_data_dic = json.loads(request.body.decode('utf8'))
        
        # 验证码核对
        emailcode = user_data_dic['emailcode']
        email = user_data_dic['email']
        if emailcode == '' or code.get(email).lower() != emailcode.lower():
            res_dic['emailcode'] = '验证码错误!'
            res_json = json.dumps(res_dic).encode('utf8')
            return HttpResponse(res_json)
       	
       	# forms规则校验
        fm = UserForm(user_data_dic)
        if fm.is_valid():
            User.objects.create_user(**fm.cleaned_data)
            res_dic['req_status'] = 'ok'
            res_json = json.dumps(res_dic).encode('utf8')
            return HttpResponse(res_json)
        else:
            error = fm.errors
            for i in error:
                res_dic[i] = error[i][0]
            res_json = json.dumps(res_dic).encode('utf8')
            return HttpResponse(res_json)
4、后期优化:实现注册失败之后的注册按钮立马禁用

重难点问题:怎么为一个名为某字符串的变量赋值?

(1)优化效果对比

先看下优化效果对比帮助理解:

原来的后端校验之后:
注意按钮的状态、错误输入框的状态
在这里插入图片描述优化之后的效果:
在这里插入图片描述

(2)优化手段解析

这个系统中的注册按钮置灰是通过flag标志的状态实现的,具体是通过check_isreg()函数实现的!

由于我们前端校验正确之后按钮立马关闭禁用,但是如果后端用户名重复了,注册失败,flag标志也都还是true,因此这里我们需要对错误的字段,清空输入框之后,还需要将其flag标志置为false,并check_isreg()

# 重点方法:
	eval方法,将括号类的字符串当作一个js语句执行

# 源码
for (obj in res_dic) {
    let ele = `#register-${obj}`
    $(ele).val('') # 将错误输入的输入框的内容清空
    eval(`flag_${obj}=false`) # 将错误输入的输入框的flag标志置为false
    $("<div>").insertAfter($(ele))
    # 后端校验报错结果,前端显示
    $(ele).next().addClass('after_reg_error').text(res_dic[obj])
    check_isreg() # 上面flag置为了false,这里实现按钮禁用置灰

	# 3s后,取消后端校验报错结果的前端显示
    setTimeout(function () { # 一次性计时器是异步的
        $(ele).next().remove()
    }, 3000)
    
    '''
    当后端校验的告警信息取消,错误输入的输入框也被清空了
    这时不再应该显示绿色的勾勾,而应该显示错误的叉叉
    '''
    setTimeout(function () { 
        $(ele).blur() # 主动触发事件,触发错误输入框的失去焦点事件!
    },3000) 
    # 3s后,这是为了让后端错误显示和前端错误显示能够错开!后端报错完再报前端校验错误!
}

有人可能会认为,eval(flag_${obj}=false)check_isreg()没用,因为$(ele).blur()中就做了它们做的事情!

/*验证确认密码*/
        $("#register-passwords").blur(function () {
            passWords = $("#register-passwords").val();
            if ((passWord != passWords) || (passWords.length < 6 || passWords.length > 18)) {
                $("#register-passwords").removeClass("form-control is-valid");
                $("#register-passwords").addClass("form-control is-invalid");
                flag_passwords = false;
            } else {
                $("#register-passwords").removeClass("form-control is-invalid");
                $("#register-passwords").addClass("form-control is-valid");
                flag_passwords = true;
            }
            check_isreg()
        })

我们把输入框清空,上面确实做到了eval(flag_${obj}=false)check_isreg()它们做的事情。但是我们注册按钮置灰这件事刻不容缓,应该在注册失败的第一时间实现注册按钮置灰!上面有3s的定时器啊!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

凤求凰的博客

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

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

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

打赏作者

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

抵扣说明:

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

余额充值