JavaEE第二次实验

实验二 Struts基础应用——基于Struts2框架的用户登录模块

完整源代码链接入口

一、基础实验——Struts2框架搭建

(一)实验目的

  1. 掌握Struts2应用的基本开发步骤和常规配置;
  2. 观察表单参数与Action属性的赋值关系,观察Action的execute()方法及其返回值,并能正确应用;
  3. 观察配置文件struts.xml中的主要元素及属性,并能够正确应用;
  4. 理解Stuts2框架中MVC设计模式的体现,理解Action,FilterDispatcher,struts.xml的主要作用,并能够正确应用。

(二)基础知识与原理

  1. Struts2 是从 WebWork 框架上发展起来的 MVC 框架;

  2. FilterDispatcher 是 Struts2 中的核心控制器,客户端对服务器端的请求将被 FilterDispatcher 过滤;若请求需要调用某个 Action,则框架将根据配置文件 struts.xml,找到需要调用的 Action 类;

  3. Action 类是一个符合一定命名规范的 JavaSE 类,作为业务控制器使用;Action 中的 execute()方法用于调用 Model 层的业务逻辑类,并根据返回结果决定页面导航;

  4. 若 Action 类中需要使用表单提交的请求参数,那么必须在 Action 类中声明与 表单域的名字对应的变量,并为变量提供 getters/setters 方法;

  5. Action 类需要在 struts.xml 中进行配置才能使用;

  6. 编译运行基于 Struts2 框架的 Web 工程,需要导入 struts2 的 8 个核心 jar 包:

    文件名说明
    struts2-core-2.3.15.1.jarStruts 2 框架的核心类库
    xwork-core-2.3.15.1.jarXWork 类库,Struts 2 的构建基础
    Ognl-3.0.6.jarStruts 2 使用的一种表达式语言类库
    freemarker-2.3.19.jarStruts 2的标签模板使用类库
    javassist-3.11.0.GA.jar代码生成工具包
    commons-lang3-3.1.jarApache 语言包,是 java.lang 包的扩展
    commons-io-2.0.1.jarApache IO 包
    commons-fileupload-1.3.jarStruts 2 文件上传依赖包

(三)实验内容及步骤

  1. 登录 http://struts.apache.org/download.cgi 站点,下载 Struts2 的最新版(Full Distribution);

  2. IDEA中新建工程

  3. 加入Struts2相关依赖或导入jar包

    	<dependency>
            <groupId>org.apache.struts</groupId>
            <artifactId>struts2-core</artifactId>
            <version>2.3.37</version>
        </dependency>
    

    Snipaste_2021-09-27_13-26-58

  4. 新建login.jsp页面,作为用户登录的视图。

    <form method="post" action="login">
        <table>
            <tr>
                <td>用户名:</td>
                <td> <input name="loginUser.account" type="text"> </td>
            </tr>
            <tr>
                <td>密  码:</td>
                <td><input type="password" name="loginUser.password"></td>
            </tr>
        </table>
        <input type="submit" value="提交" name="提交">
    </form>
    
  5. 新建loginSuccess.jsp和loginFail.jsp页面分别作为登录成功或登录失败的视图。

  6. 新建bean包,创建UserBean.java,用于记录登录用户信息:

    package com.example.struts_prij1.bean;
    
    public class UserBean {
        private String account;
        private String password;
    
        public UserBean() {
        }
    
        public UserBean(String account, String password) {
            this.account = account;
            this.password = password;
        }
    
        public String getAccount() {
            return account;
        }
    
        public void setAccount(String account) {
            this.account = account;
        }
    
        public String getPassword() {
            return password;
        }
    
        public void setPassword(String password) {
            this.password = password;
        }
    }
    
    
  7. 新建service包,并创建UserService.java,用于实现登录逻辑

    package com.example.struts_prij1.service;
    
    import com.example.struts_prij1.bean.UserBean;
    
    public class UserService {
        public boolean login(UserBean loginUser){
            if(loginUser.getAccount().equals(loginUser.getPassword())){
                return true;
            }
            return false;
        }
    }
    
    
  8. 创建action包,并创建UserAction.java,调用登录逻辑,并根据登录结果不同而返回不同内容

    package com.example.struts_prij1.action;
    
    import com.example.struts_prij1.bean.UserBean;
    import com.example.struts_prij1.service.UserService;
    
    public class UserAction {
        private UserBean loginUser;
        public UserBean getLoginUser(){
            return loginUser;
        }
        public void setLoginUser(UserBean loginUser){
            this.loginUser = loginUser;
        }
        public String execute(){
            UserService userService = new UserService();
            if(userService.login(loginUser)){
                return "success";
            }
            return "fail";
        }
    }
    
  9. 在src目录中创建struts.xml文件,用于配置Action并设置页面导航

    <?xml version="1.0" encoding="UTF-8"?>
    
    <!DOCTYPE struts PUBLIC
            "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
            "http://struts.apache.org/dtds/struts-2.0.dtd">
    
    <struts>
        <package name="strutsBean" extends="struts-default">
            <action name="login" class="com.example.action.UserAction">
                <result name="success">/loginSuccess.jsp</result>
                <result name="fail">/loginFail.jsp</result>
            </action>
        </package>
    </struts>
    
  10. 编辑Web应用的web.xml文件,增加Struts2核心Filter配置

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <filter>
        <filter-name>struts2</filter-name>
        <filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>struts2</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>
  1. 部署到Tomcat服务器

  2. 修改login.jsp代码

    <%--
      Created by IntelliJ IDEA.
      User: YIYI
      Date: 2021/9/27
      Time: 10:22
      To change this template use File | Settings | File Templates.
    --%>
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <%@ taglib prefix="s" uri="/struts-tags"%>
    <html>
    <head>
        <title>登录</title>
    </head>
    <body>
    <s:form method="post" action="login.action">
        <s:textfield name="loginUser.account" label="请您输入账号"/>
        <s:password name="loginUser.password" label="请您输入密码"/>
        <s:submit value="登录"/>
    </s:form>
    </body>
    </html>
    
    
  3. 运行结果
    Snipaste_2021-09-27_13-32-38

    Snipaste_2021-09-27_13-32-45

(四)实验总结

  1. 在修改代码之前,login.jsp的代码很显然是不正确的,需要将html标签改为Struts2的标签

    <%@ taglib prefix="s" uri="/struts-tags"%>
    
  2. 由于struts 2.3版本和2.5版本发生了较大变化,一开始安装了2.5版本,struts.xml文件配置错误,出现了404错误;

  3. Action属性赋值关系:

    jsp中的name属性的值对应Action类中数据对象名,若数据对象的数据成员为对象则使用如下格式 例子:loginUser.name.first

    Action类中execute方法的返回值为字符串,在struts.xml中为该返回值配置映射;

  4. 根据容器配置,服务器将请求转到FilterDispatcher进行处理,进入Struts2流程;

  5. struts框架会在struts.xml配置文件中查找对应的action类,初始化该action,执行set方法,再执行execute方法;

  6. 框架根据execute方法返回的字符串和配置文件信息进行匹配,进而返回结果页面。

  7. Action具有很好的代码复用性,无需与Servlet API耦合,同时返回的字符串作为处理结果,使得结果处理更加灵活。

    <struts>
    <!--    <package> 元素用于进行包配置,在 Struts2 框架中,包用于组织 Action 和拦截器等信息,每个包都是由零个或多个拦截器以及 Action 所组成的集合。-->
    <!--    name:必填属性,用于指定该包的名称,此名称是该包被其他包引用的Key-->
    <!--    namespace:可选属性,用于定义包的命名空间-->
    <!--    extends:可选属性,用于指定该包继承自其他包,其属性值必须是另一个包的name属性值,但该属性值通常设置为struts-default,这样该包中的Action就具有了Struts框架默认的拦截器等功能-->
        <package name="strutsBean" extends="struts-default">
    <!--        action配置指定了需要初始化哪个action类进行处理-->
    <!--        result则指定了对应字符串的处理结果-->
            <action name="login" class="com.example.action.UserAction">
                <result name="success">/loginSuccess.jsp</result>
                <result name="fail">/loginFail.jsp</result>
            </action>
        </package>
    </struts>
    

二、提高实验——Struts2标签

(一)实验目的

  1. 进一步熟悉Struts2 应用的基本开发步骤和常规配置;
  2. 进一步熟悉Action 及配置文件struts.xml 的应用方法;
  3. 掌握Struts2 标签的基本使用方法;
  4. 能熟练使用Struts2 的常用标签,能参考Struts2 标签的使用说明文档,对各类标签进行灵活应用;

(二)基本知识与原理

  1. 使用Struts2 标签的形式来表达页面逻辑,可以尽量避免在视图中使用Java代码,让逻辑与显示分离,提高视图的可维护性;
  2. Struts2 标签库的主要tld 文件为struts-tags.tld,在struts2-core-2.3.15.1.jar 包中,另一个与Ajax 相关的标签库tld 文件为struts-dojo-tags.tld,在struts2-dojo-plugin-2.3.15.1.jar 包中;
  3. Struts2 标签的使用步骤和使用JSTL 相同,只需在JSP 页面中使用taglib 指令引入标签库中tld 文件的uri,并指定前缀即可,例如:<%@ taglib prefix=“s” uri="/struts-tags"%>;
  4. 根据Struts2 标签的主要作用,可以将其分为:用于生成页面元素的UI 标签、用于实现流程控制的控制类标签、用于控制数据的数据标签和用于支持Ajax的标签。

(三)实验内容及步骤

  1. 导入struts2-dojo-plugin-2.3.37.jar包到lib目录下,并加入依赖

  2. 新建register.jsp页面作为用户注册的视图,页面使用Struts2的UI标签来生成表单元素,包括用户名、密码、确认密码、真实姓名、性别、生日、联系地址、联系电话和电子邮箱:

    <%--
      Created by IntelliJ IDEA.
      User: yiyi
      Date: 2021/9/27
      Time: 14:07
      To change this template use File | Settings | File Templates.
    --%>
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <%@ taglib prefix="s" uri="/struts-tags"%>
    <%@ taglib prefix="sx" uri="/struts-dojo-tags"%>
    <html>
    <head>
        <s:head theme="xhtml"/>
        <sx:head parseContent="true" extraLocales="UTF-8"/>
    </head>
    <body>
        <s:form action="register.action" method="post" onsubmit="return check()">
            <s:textfield name="loginUser.account" label="请输入用户名"/>
            <s:password name="loginUser.password" label="请输入密码"/>
            <s:password name="loginUser.repassword" label="请再次输入密码"/>
            <s:radio name="loginUser.sex" list="#{1:'', 0:''}" label="请输入性别"/>
            <sx:datetimepicker name="loginUser.birthday" displayFormat="yyyy-MM-dd" label="请输入生日"/>
            <s:textfield name="loginUser.address" label="请输入联系地址"/>
            <s:textfield name="loginUser.phone" label="请输入手机号"/>
            <s:textfield name="loginUser.email" label="请输入电子邮箱"/>
            <s:submit value="注册"/>
            <s:reset value="重置"/>
        </s:form>
    </body>
    </html>
    
    
  3. 新建regFail.jsp页面,作为注册失败的试图,显示“注册失败”

  4. 新建regSuccess.jsp页面,作为注册成功试图,使用Struts2的数据标签和控制标签来生成注册成功的信息,并将登录用户信息保存在会话范围内。

    <%--
      Created by IntelliJ IDEA.
      User: yiyi
      Date: 2021/9/27
      Time: 14:37
      To change this template use File | Settings | File Templates.
    --%>
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <%@ taglib prefix="s" uri="/struts-tags" %>
    <html>
    <head>
        <title>注册成功</title>
    </head>
    <body>
        <s:property value="loginUser.name"/>
        <s:if test="%{loginUser.sex==\"1\"}">
            <s:text name="先生, "/>
        </s:if>
        <s:else>
            <s:text name="女士, "/>
        </s:else>
    您注册成功了!
    <s:set name="user" value="loginUser" scope="session"/>
    </body>
    </html>
    
    
  5. 修改UserBean类

    package com.example.bean;
    
    public class UserBean {
        private String account="";
        private String password="";
        private String repassword="";
        private String name="";
        private String sex="";
        private String birthday="";
        private String address="";
        private String phone="";
        private String email="";
        public String getAccount() {
            return account;
        }
    
        public void setAccount(String account) {
            this.account = account;
        }
    
        public String getPassword() {
            return password;
        }
    
        public void setPassword(String password) {
            this.password = password;
        }
    
        public String getRepassword() {
            return repassword;
        }
    
        public void setRepassword(String repassword) {
            this.repassword = repassword;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getSex() {
            return sex;
        }
    
        public void setSex(String sex) {
            this.sex = sex;
        }
    
        public String getBirthday() {
            return birthday;
        }
    
        public void setBirthday(String birthday) {
            this.birthday = birthday;
        }
    
        public String getAddress() {
            return address;
        }
    
        public void setAddress(String address) {
            this.address = address;
        }
    
        public String getPhone() {
            return phone;
        }
    
        public void setPhone(String phone) {
            this.phone = phone;
        }
    
        public String getEmail() {
            return email;
        }
    
        public void setEmail(String email) {
            this.email = email;
        }
    }
    
  6. 修改UserService.java,增加用户注册逻辑,为简化注册逻辑,健注册成功的条件设置为:用户名、密码和确认密码相同,而且不为空字符串;

    public boolean register(UserBean registerUser){
            if(registerUser.getAccount().equals(registerUser.getPassword()) && registerUser.getPassword().equals(registerUser.getRepassword()) && !registerUser.getAccount().equals("")){
                return true;
            }
            return false;
        }
    
  7. 修改UserAction.java中的execute()方法,用于调用注册逻辑,并根据注册结果返回不同内容

    public String execute(){
        UserService userService = new UserService();
        if(userService.register(loginUser)){
            return "success";
        }
        return "fail";
    }
    
  8. 修改struts.xml文件,对用户注册进行配置并设置页面导航

            <action name="register" class="com.example.action.UserAction">
                <result name="success">/regSuccess.jsp</result>
                <result name="fail">/regFail.jsp</result>
            </action>
    
  9. 重新部署到Tomcat服务器,运行。

    Snipaste_2021-09-27_15-02-15

Snipaste_2021-09-27_15-03-07

Snipaste_2021-09-27_15-03-35

(四)实验总结

本次实验未出现BUG,但修改原execute的方法不是合乎常理的操作,应该要再创建一个Action来处理注册业务,而不能捡芝麻丢西瓜。

三、拓展实验——Struts2的国际化

(一)实验目的

  1. 进一步熟悉Struts2标签的基本使用方法;
  2. 能使用Struts2标签实现国际化;
  3. 了解配置文件struts.properties以及国际化资源文件的作用和基本使用方法。

(二)基本知识与原理

  1. 通过将不同语言版本的字符保存在属性文件中,Struts2的国际化机制能够在不需要修改程序主体的前提下,实现不同语言版本的应用;
  2. 在Web应用中选择需要进行国际化的内容,不在页面中直接输出该信息,而是通过Struts2标签输出一个键值,该键值在不同语言环境下对应不同的字符串;如::<s:textfield name=“loginUser.account” lable=“请输入用户名”/>代码中的“请输入用户名”是需要国际化的内容,将其以键值代替为<s:textfield name=“loginUser.account” key=“login.account.lable”/>;
  3. 需要进行国际化的内容以键值对(key=value)的形式写入Struts2的国际化资源文件中,如“login.account.lable=请输入用户名”;该资源文件名可以自定义,但后缀必须是properties;资源文件应放在Web应用的类加载路径下;每一个语言版本需要创建一个资源文件;
  4. 通过Struts2的配置文件struts.properties来配置资源文件的基础名,若资源文件的基础名为message,则message_zh_CN.properties是对应的中文资源文件,message_en_US.properties是对应的美国英语资源文件;
  5. 浏览器将根据其默认的语言版本,自动调用相应语言的资源文件,从而在页面中展示不同的语言效果。

(三)实验内容及步骤

  1. 在src目录下创建一个cn.edu.zjut.local包,把所有的资源文件放置其中,如:message_zh_CN.properties、message_en_US.properties等;

    Snipaste_2021-09-27_21-01-53

    #message_en_US.properties
    login.account.lable=Please input your account
    login.password.lable=Please input your password
    login.submit.button=submit
    
    #message_zh_CN.properties
    login.account.lable=请输入用户名
    login.password.lable=请输入密码
    login.submit.button=登录
    
  2. 对资源文件进行重新编码,避免乱码问题;

    Snipaste_2021-09-27_21-07-57

    #message_zh_CN.properties
    login.account.lable=请输入用户名
    login.password.lable=请输入密码
    login.submit.button=登录
    
  3. src目录中创建struts.properties文件,通过它加载资源文件:

    struts.custom.i18n.resources=cn.edu.zjut.local.message
    struts.i18n.encoding=GBK
    
  4. 修改login.jsp、loginSucess.jsp和loginFail.jsp页面,通过Struts2标签实现国际化;

    <!--login.jsp-->
        <s:textfield name="loginUser.account" key="login.account.lable"/>
        <s:password name="loginUser.password" key="login.password.lable"/>
        <s:submit name="submit" key="login.submit.button"/>
    
  5. 重新部署到Tomcat服务器

  6. 设置浏览器首选语言选项,访问login.jsp页面,观察并记录运行结果;

    中文时访问

    Snipaste_2021-09-27_21-25-01

    修改浏览器语言为英语

    Snipaste_2021-09-27_21-25-01

    英文时访问

    Snipaste_2021-09-27_21-25-49

  7. 修改用户注册模块

    #message_en_US.properties
    login.account.lable=Please input your account
    login.password.lable=Please input your password
    login.submit.button=submit
    
    register.account.lable=Please input your account
    register.password.lable=Please input your password
    register.repassword.lable=Please input your password again
    register.sex.lable=Please input your sex
    register.birthday.lable=Please input your birthday
    register.address.lable=Please input your address
    register.phone.lable=Please input your phone
    register.email.lable=Please input your email
    register.submit.button=Sign up
    register.reset.button=Reset
    
    #message_zh_CN.properties
    login.account.lable=请输入用户名
    login.password.lable=请输入密码
    login.submit.button=登录
    
    register.account.lable=请输入用户名
    register.password.lable=请输入密码
    register.repassword.lable=请再次输入密码
    register.sex.lable=请输入性别
    register.birthday.lable=请输入生日
    register.address.lable=请输入联系地址
    register.phone.lable=请输入手机号
    register.email.lable=请输入电子邮箱
    register.submit.button=注册
    register.reset.button=重置
    

    创建UserRegisterAction.java类处理注册业务

    package com.example.action;
    
    import com.example.bean.UserBean;
    import com.example.service.UserService;
    
    public class UserRegisterAction {
        private UserBean loginUser;
        public UserBean getLoginUser(){
            return loginUser;
        }
        public void setLoginUser(UserBean loginUser){
            this.loginUser = loginUser;
        }
        public String execute(){
            UserService userService = new UserService();
            if(userService.register(loginUser)){
                return "success";
            }
            return "fail";
        }
    }
    

    修改struts2.xml配置

    <struts>
    <!--    <package> 元素用于进行包配置,在 Struts2 框架中,包用于组织 Action 和拦截器等信息,每个包都是由零个或多个拦截器以及 Action 所组成的集合。-->
    <!--    name:必填属性,用于指定该包的名称,此名称是该包被其他包引用的Key-->
    <!--    namespace:可选属性,用于定义包的命名空间-->
    <!--    extends:可选属性,用于指定该包继承自其他包,其属性值必须是另一个包的name属性值,但该属性值通常设置为struts-default,这样该包中的Action就具有了Struts框架默认的拦截器等功能-->
        <package name="strutsBean" extends="struts-default">
    <!--        action配置指定了需要初始化哪个action类进行处理-->
    <!--        result则指定了对应字符串的处理结果-->
            <action name="login" class="com.example.action.UserAction">
                <result name="success">/loginSuccess.jsp</result>
                <result name="fail">/loginFail.jsp</result>
            </action>
            <action name="register" class="com.example.action.UserRegisterAction">
                <result name="success">/regSuccess.jsp</result>
                <result name="fail">/regFail.jsp</result>
            </action>
        </package>
    </struts>
    

    运行结果

    Snipaste_2021-09-27_21-45-34

(四)实验总结

通过struts2标签进行国际化能够使得一个网站多出一个properties资源文件即可完成不同语言的页面,而且各语言之间无耦合,修改非常方便。

通过本次实验,熟悉了struts2进行国际化的方式以及对struts2框架有了进一步的理解。

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
1. 创建UserRegisterBean类,包含三个属性:用户名、密码、年龄,以及对应的getter和setter方法。 2. 在register.html中创建一个表单,包含三个输入框:用户名、密码、年龄,以及一个提交按钮。表单的action属性设置为register.jsp。 3. 在register.jsp中获取用户提交的表单数据,创建UserRegisterBean对象,并将表单数据赋值给UserRegisterBean对象的属性。 4. 对UserRegisterBean对象进行校验,检查用户名和密码是否符合要求,年龄是否合法。若校验不通过,跳转回register.html页面,并给出相应的提示信息。 5. 若校验通过,将UserRegisterBean对象保存到数据库中,并跳转到注册成功页面register_success.jsp,显示注册成功的信息。 下面是register.html的代码示例: ``` <form action="register.jsp" method="post"> <label>用户名:</label> <input type="text" name="username"><br> <label>密码:</label> <input type="password" name="password"><br> <label>年龄:</label> <input type="text" name="age"><br> <input type="submit" value="提交"> </form> ``` 下面是register.jsp的代码示例: ``` <% // 获取用户提交的表单数据 String username = request.getParameter("username"); String password = request.getParameter("password"); int age = Integer.parseInt(request.getParameter("age")); // 创建UserRegisterBean对象并赋值 UserRegisterBean user = new UserRegisterBean(); user.setUsername(username); user.setPassword(password); user.setAge(age); // 对UserRegisterBean对象进行校验 String errMsg = user.validate(); if (errMsg != null) { // 若校验不通过,跳转回register.html并给出提示信息 out.println("<script>alert('" + errMsg + "');history.back();</script>"); } else { // 若校验通过,保存UserRegisterBean对象到数据库,并跳转到注册成功页面 // 此处省略保存到数据库的代码 response.sendRedirect("register_success.jsp"); } %> ``` 下面是UserRegisterBean类的代码示例: ``` public class UserRegisterBean { private String username; private String password; private int age; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } // 校验用户名和密码是否符合要求,年龄是否合法 public String validate() { if (username == null || username.trim().equals("")) { return "用户名不能为空"; } if (password == null || password.trim().equals("")) { return "密码不能为空"; } if (age <= 0 || age > 120) { return "年龄不合法"; } return null; } } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

yiyiqwq

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

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

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

打赏作者

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

抵扣说明:

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

余额充值