struts2(1)

struts2(1)

整理了从开始学习Java以来在纸质本上做的一些笔记,如有错误,恳请批评指正。


建立自己的一套MVC框架有助于更好的理解框架的运行原理。

MVC是一种架构模式,目的是将模型(业务逻辑)和视图(表示层)分离,使模型和视图可以独立修改。

Model:模型
View:视图
Controller:控制器

步骤:
1. 创建Action接口,定义execute方法,返回的是String页面路径,传入的是request,response对象。

public interface Action {
    String execute(HttpServletRequest request,HttpServletResponse response);
}

2. 对不同的操作实现action,定义具体的操作。

public class AddAction implements Action {
    private Calculator calculator = new Calculator();

    public String execute(HttpServletRequest request,
            HttpServletResponse response) {
        double num1 = Double.parseDouble(request.getParameter("num1"));
        double num2 = Double.parseDouble(request.getParameter("num2"));
        double result = calculator.add(num1, num2);

        request.setAttribute("result", result);
        return "/WEB-INF/jsp/add_result.jsp";
    }
}

//calculator
public class Calculator {
    public double add(double a,double b){
        return a+b;
    }

    public double subtract(double a,double b){
        return a-b;
    }
}

3. 创建基于Servlet实现的控制器,控制器中维护了所有的action对象。

/*
 * 自动以MVC框架,基于servlet实现的控制器
 */
public class Controller extends HttpServlet {
    private HashMap actionMap;


    /*
     *Serlet初始化方法
     * @see javax.servlet.GenericServlet#init()
     */
    @Override
    public void init() throws ServletException {
        //初始化actionMap
        actionMap = new HashMap();
        //将AddAction对象放入到HashMap中
        actionMap.put("add", new AddAction());
    }

    private Action determinActionByPath(String path){
        String actionName = path.substring(path.lastIndexOf("/")+1,path.length()-7);

        return (Action) actionMap.get(actionName);
    }
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        String path = req.getServletPath();
        Action action = this.determinActionByPath(path);
        String resultView = action.execute(req, resp);
        System.out.println(resultView);
        if(null!=resultView){
            req.getRequestDispatcher(resultView).forward(req, resp);
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        this.doGet(req, resp);
    }
}

4. 定义相应的jsp页面。并且设置web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" 
    xmlns="http://java.sun.com/xml/ns/javaee" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
    http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
  <display-name></display-name>
  <servlet>
    <servlet-name>Controller</servlet-name>
    <servlet-class>framework.Controller</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>Controller</servlet-name>
    <url-pattern>*.action</url-pattern>
  </servlet-mapping>    
</web-app>

JSP页面:

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML>
<html>
  <head>
     <title>add.jsp</title>
  </head>
  <body>
  <form action="add.action" method="post" id="calcForm">
    第1个数:<input type="text" name="num1"/>
    第2个数:<input type="text" name="num2"/>
    <input type="submit" value="plus"/>
  </form>
  </body>
</html>

<%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%>
<!DOCTYPE HTML>
<html>
  <head>
     <title>add_result.jsp</title>
  </head>

  <body>
    ${param.num1 }+${param.num2 }=${requestScope.result }<!-- EL表达式取参数 -->
  </body>
</html>

Hibernate

开源的、轻量级的ORM(Object Relation Mapping,对象关系映射)持久化框架,它允许应用程序以面向对象的的方式来操作关系型数据库,负责将对象数据保存到关系型数据库中和从关系型数据库中读取数据并封装成对象的工作。通过简单的配置即可替代JDBC繁琐的程序代码。

Spring

Spring作为一个一站式的JavaEE解决方案,渗透了JavaEE技术的方方面面,它主要用来实现依赖注入面向切面的编程、声明式事务以及对持久层的支持和简化功能。

Struts2 框架结构

S2中大量使用了拦截器来处理用户请求,核心控制器就是StrutsPrepareAndExecutorFilter拦截器,根据用户请求响应相应的execute()方法,并根据处理结果显示相应的JSP页面。

<!--  配置启动strut2的全局过滤器 -->
    <filter>
        <filter-name>struts2</filter-name>
        <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>struts2</filter-name>
            <!-- 过滤所有请求 -->
        <url-pattern>/*</url-pattern>
    </filter-mapping>
Struts2的配置文件

struts.xml:配置Action,struts2创建系统的Action代理时需要用到。
struts.properties:struts2的全局属性

Struts2的执行过程

项目启动:
1. 创建核心过滤器StrutsPrepareAndExcuteFilter对象。
2. 执行核心过滤器的init方法
依次读取下列的配置:
struts-default.xml ————struts2框架的默认配置文件。
struts-plugins.xml————struts2的插件配置文件。
struts.xml————用户自定义的业务配置文件(包含配置的Action)
struts.properties————struts2的全部属性。如web应用默认的编码集等。

struts2读取struts.xml内容之后,将内容封装进javabean对象然后存放在内存中,不再需要读取struts.xml文件。

访问资源:
3. 在内存中查询对应的Action配置,得到Class内容,反射创建Action对象。
4. 读取Action配置的method内容,执行Action对象对应的方法。

Struts-default.xml详解:

<struts>
    <bean class="com.opensymphony.xwork2.ObjectFactory" name="xwork" />
    <bean type="com.opensymphony.xwork2.ObjectFactory" name="struts" class="org.apache.struts2.impl.StrutsObjectFactory" />

    ...

    <package name="struts-default" abstract="true">
        <result-types>
            <result-type name="chain" class="com.opensymphony.xwork2.ActionChainResult"/>
            <result-type name="dispatcher" class="org.apache.struts2.dispatcher.ServletDispatcherResult" default="true"/>
           ...

        <interceptors>
            <interceptor name="alias" class="com.opensymphony.xwork2.interceptor.AliasInterceptor"/>
            <interceptor name="autowiring" class="com.opensymphony.xwork2.spring.interceptor.ActionAutowiringInterceptor"/>
           ...

            <!-- Basic stack -->
            <interceptor-stack name="basicStack">
                <interceptor-ref name="exception"/>
                <interceptor-ref name="servletConfig"/>
              ...

       </interceptors>

        <default-interceptor-ref name="defaultStack"/>

        <default-class-ref class="com.opensymphony.xwork2.ActionSupport" />
    </package>

</struts>

bean:声明struts框架运行中使用到的一些对象
package:默认包struts-default(自定义的pcakage需要extends=struts-default“”)
interceptor:拦截器(默认32个,完成Struts的核心功能)过滤actions,执行action时加入通用代码


这样记录效率太慢了,并且是复习,以后就捡重点来总结吧。

struts.xml配置详解

<struts>
    <package name="p2" extends="struts-default" namespace="/">
        <action name="add" class="strutsplus.AddAction">
            <result name="success">/WEB-INF/jsp/add_result.jsp</result>
        </action>
    </package>
</struts>

—:package:代表一个包,包内不准同名action
|—name:包名
|—extends:继承拦截器,默认继承18个。
namespace:名称空间。

abstruct:表示当前包是否抽象,若是,则不能含有action,抽象包一般来定义拦截器,公共视图,不做具体业务。

过滤器和拦截器的区别?

都是对用户的请求起到拦截作用,只不过Filter是javaEE规范的概念,拦截器Interceptors设计struts2中提出的概念,struts2中过滤器负责调用拦截器。

1 struts2的Action三种使用方式

Action是自己创建还是继承别的类呢?或者实现别的接口?这是一个问题。
1.1 第一种方式,不实现Action接口

/**
 * 第一种方式:不需要实现或继承任何接口或类
 * @author APPle
 *
 */
public class UserAction2 {

    public String login()throws Exception{
        System.out.println("UserAction2.login()");
        return "success";
    }

}

1.2第二种方式,实现Action接口

/**
 * 第二种方式:实现Action接口
 *  1)定义了默认的execute方法的标准
 *  2)提供了项目中常用的视图标记
 * @author APPle
 *
 */
public class UserAction implements Action {

    public String login() throws Exception {
        System.out.println("执行了UserAction的login方法");
        return SUCCESS;
    }

    public String execute() throws Exception {
        return null;
    }

}

1.3 第三种方式, 继承ActionSupport类(推荐)

/**
 * 第三种方式: 继承ActionSupport类(推荐使用)
 *      好处: 
 *          1)提供了常用的视图标记
 *          2)提供了数据校验功能
 * 
 * @author APPle
 *
 */
public class UserAction3 extends ActionSupport{

    public String login()throws Exception{
        System.out.println("UserAction3.login()");
        return SUCCESS;
    }
}
2 路径通配符

struts.xml配置action时可以使用路径通配符呀。

<action name="*_*" class="gz.ustb.b_path.{1}Action" method="{2}">
        <result name="{2}">/{1}/{2}.jsp</result>
</action>
3 strus2的常量配置

struts2的常量就是用于在strut2的程序运行过程中使用的一些常量参数。在struts.xml中进行配置。

   指定默认编码集,作用于HttpServletRequest的setCharacterEncoding方法 和freemarker 、velocity的输出 
   <constant name="struts.i18n.encoding" value="UTF-8"/>

   自定义后缀修改常量 
   <constant name="struts.action.extension" value="do"/>

   设置浏览器是否缓存静态内容,默认值为true(生产环境下使用),开发阶段最好关闭 
   <constant name="struts.serve.static.browserCache" value="false"/>

   当struts的配置文件修改后,系统是否自动重新加载该文件,默认值为false(生产环境下使用),开发阶段最好打开 
   <constant name="struts.configuration.xml.reload" value="true"/>

   开发模式下使用,这样可以打印出更详细的错误信息 
   <constant name="struts.devMode" value="true" />

   默认的视图主题 
   <constant name="struts.ui.theme" value="simple" />

   与spring集成时,指定由spring负责action对象的创建 
   <constant name="struts.objectFactory" value="spring" />

   该属性设置Struts 2是否支持动态方法调用,该属性的默认值是true。如果需要关闭动态方法调用,则可设置该属性
   为 false
   <constant name="struts.enable.DynamicMethodInvocation" value="false"/>

   上传文件的大小限制 
   <constant name="struts.multipart.maxSize" value=“10701096"/>

注意:
通过struts.xml文件声明<constant name="struts.action.extension" value="action,do,,"></constant>修改常量配置。

4 struts2的全局视图配置和默认配置

局视图配置在struts.xml中配置,配置所有action都是用到的一些jsp对应关系。

4.1 全局视图作用: 当该包下的所有action都使用到的一些视图就是可以放到全局视图配置中
注意:
当action中也有相同名称的视图,那么action的局部视图会覆盖全局视图。

<!-- 全局视图配置: 把该包下的所有action共用的视图都机集中在这里写 -->
            <global-results>
                <result name="success">/login.jsp</result>
            </global-results>

4.2 action的默认配置

<!-- 默认配置 
name: 必填项
class: 可选项 。默认配置:  ActionSupport类   该类继承自struts-default (<default-class-ref class="com.opensymphony.xwork2.ActionSupport" />)
 method: 可选。默认配置execute()
    result:
        name: 可选。默认配置: success
        type: 可选。默认配置: dispatcher
 -->
 <!-- 全部使用默认配置的action的作用 :专门用于转发到WEB-INF下的页面 -->
 <action name="book">
    <result>/WEB-INF/jsp/login.jsp</result>
 </action>
5 Action的属性注入

作用: 如果Action对象中需要把一些经常改变的参数提取到配置文件中,那么就可以使用属性注入的方法。
Action属性注入的步骤
1)在Action类中声明一个成员变量,用于接收xml配置文件传入内容
2)在Action类提供一个该变量的setter方法,该方法接收了xml配置的内容
3)在对应的struts.xml文件中,找到对应的action对象的配置,然后在action中使用<param name=""></param> 这个标签来向Action对象的属性注入内容

    //1)在action中提供一个属性
    private String savePath;
    //2)提供属性的setter方法,用于外部的action的参数进行注入
    public void setSavePath(String savePath) {
        this.savePath = savePath;
    }

<action name="upload" class="gz.ustb.d_ioc.UploadAction" method="upload">
    <!-- 3)使用该配置可以往Action对象的属性注入内容(只要有setter都可以使用param进行注入)
        param:
            name: setter方法名。setSavePath -> savePath
     -->
    <param name="savePath">e:/images/</param>
    <result>/login.jsp</result>
</action>
6 struts2的数据共享的三种方式

上面说过,Action类可以有三种使用方式,但是在Action中如何使用Servlet?如何获取Request?

在web项目中都是使用域对象来共享数据。struts2提供给开发者使用域对象来共享数据的方法一共有三种

方式一:
利用ServletActionContext类的静态方法:

HttpServletRequest request = ServletActionContext.getRequest();//EL中是requestScope
HttpServletResponse response = ServletActionContext.getResponse();//EL:sessionScope
ServletContext servletContext = ServletActionContext.getServletContext();//EL:applicationScope

特点:
1. 依赖Servlet的api,耦合比较高。
2. 若要通过域对象来获取域对象的相关信息(存取数据之外的操作),必须使用此方式。

方式二

ActionContext类
ActionContext ac = ActionContext.getContext();
ac.getContextMap() : 获取操作request域对象数据的map集合
ac.getSession() :       获取操作session域对象数据的map集合  
ac.getApplication()  获取操作context域对象数据的map集合

注意:
1)不依赖servlet的api,耦合性低
2)只能用在Action对象的一个方法中。不能在所有方法中都是用同一个ActionContext

ActionContext对象的构建是通过拦截器创建的,拦截器的执行是创建Action对象之后,所以不能在Action的方法之外维护ActionContext对象。

public class UserAction extends ActionSupport {
    //这种用法有问题!!因为ActionContext对象的构造是通过拦截器创建的,而拦截器的执行是在创建UserAction之后
    /*ActionContext ac = null;

    public UserAction(){
        ac = ActionContext.getContext();
    }*/

    //业务方法是在拦截器之后被执行的,所有ActionContext被拦截器成功的创建。
    public String list()throws Exception{

        //1)从数据库得到数据
        List<String> list = new ArrayList<String>();
        list.add("eric");
        list.add("jacky");
        list.add("rose");

        //2) 用request,session,context域对象来共享数据
        /**
         * 1)strus2提供的第一种使用域对象的方法(如果单纯的使用域对象来存取数据 ,不推荐使用这种方式)
         *       ServletActionContext对象:可以在struts2的action方法中使用域对象
         *    特点: 依赖servlet原生的api
         */
        /*//获取request域对象
        HttpServletRequest request = ServletActionContext.getRequest();
        request.setAttribute("request_list", list);
        //获取session域对象
        HttpSession session = ServletActionContext.getRequest().getSession(true);
        session.setAttribute("session_list", list);
        //获取ServletContext域对象
        ServletContext context = ServletActionContext.getServletContext();
        context.setAttribute("context_list", list);*/

        //得到客户的请求的相关数据
        /**
         * 注意: 如果用到了request/session/servletcontext对象中的除存取数据以外的其他方法,就必须得使用ServletActionContext
         *     来获取数据。
         */
        ServletActionContext.getRequest().getMethod();



        /*ActionContext ac = ActionContext.getContext();
        *//**
         * 2)strus2提供的第二种使用域对象的方法(Action对象方法少的时候,可以使用这种方式)
         *     ActionContext对象: action的上下文对象,在这个ActionContext对象中提供操作不同域对象数据的Map集合
         *     
         *     特点:
         *     1) 不依赖servlet原生的api,方便测试
         *     2)只能在action的某个业务方法中使用    
         *//*


        //得到操作request域的map集合(操作这个Map集合就等同于操作了request域的数据)
        Map<String,Object> requestMap = ac.getContextMap();
        requestMap.put("request_list", list); //存放到request域中
        //得到操作session域的map集合
        Map<String,Object> sessionMap = ac.getSession();
        sessionMap.put("session_list", list);
        //得到操作context域的map集合
        Map<String, Object> contextMap = ac.getApplication();
        contextMap.put("context_list", list);*/


        return "success";

    }

}

方式三
实现RequestAware , SessionAware ApplicationAware 接口注入操作对应域对象数据的Map集合

注意:
1)不依赖servlet的api
2)可以在Action对象的所有方法中共享Map集合

最佳实践:

/*
 * 基础Action
 *  在这个基础Action当前注入了三个域对象的操作Map集合
 * @author APPle
 *
 */
public class BaseAction extends ActionSupport implements RequestAware,SessionAware,ApplicationAware{
    protected Map<String,Object> requestMap;
    protected Map<String,Object> sessionMap;
    protected Map<String,Object> contextMap;

    //struts2自动会把操作request域的map集合传入
    public void setRequest(Map<String, Object> request) {
        this.requestMap = request;
    }
    //struts2自动会把操作session域的map集合传入
    public void setSession(Map<String, Object> session) {
        this.sessionMap = session;
    }
    //struts2自动会把操作context域的map集合传入
    public void setApplication(Map<String, Object> application) {
        this.contextMap = application;
    }
}

public class BookAction extends BaseAction{

    public String list()throws Exception{

        //1)从数据库得到数据
        List<String> list = new ArrayList<String>();
        list.add("eric");
        list.add("jacky");
        list.add("rose");

        //往request域存放数据
        requestMap.put("request_list", list);
        //往session域存放数据
        sessionMap.put("session_list", list);
        //往context域存放数据
        contextMap.put("context_list", list);

        return "success";

    }
}
7 请求参数数据的封装

7.1 直接赋值给简单数据类型
在jsp中直接用name=变量名注入,多选是String[]数组。action中的参数需要设置setter。

public class UserAction extends ActionSupport{
    //参数赋值(注入方式)
    private String name;
    private String password;
    private String gender;
    private String[] hobit;
    //参数通过这个set方法注入到Action中
    public void setName(String name) {
        this.name = name;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public void setGender(String gender) {
        this.gender = gender;
    }
    public void setHobit(String[] hobit) {
        this.hobit = hobit;
    }

7.2 赋值给一个javabean对象
请求中参数的封装通过struts2的ParametersInterceptor拦截器进行赋值。

在JSP中,name=Object.param直接对对象进行赋值也是可以的。action中的对象需要设置setter、并且需要设置getter、并且需要设置getter、并且需要设置getter、并且需要设置getter、并且需要设置getter

<form action="${pageContext.request.contextPath }/data/user_register.action" method="post">
        用户名: <input type="text" name="user.name"/><br/>
        密码: <input type="password" name="user.password"/><br/>
        性别: <input type="radio" name="user.gender" value="男"/><input type="radio" name="user.gender" value="女"/><br/>
        爱好:
        <input type="checkbox" name="user.hobit" value="篮球"/>篮球
        <input type="checkbox" name="user.hobit" value="足球"/>足球
        <input type="checkbox" name="user.hobit" value="羽毛球"/>羽毛球<br/>
        <input type="submit" value="注册"/>

    </form>

public class UserAction2 extends ActionSupport{
    //使用一个javabean对象接收
    private User user;

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }
8 文件上传与下载

8.1 文件上传
1. 三个条件:
- 表单有file
- post提交
- enctype=”multipart/form-data”

  1. 在Action中接收文件内容
    • File attach; (attach是file表单的name属性)
    • String attachContentType; 文件类型
    • String attachFileName; 文件名称
<!-- 修改默认文件上传大小 -->
<constant name="struts.multipart.maxSize" value="100000000"></constant>

<action name="upload" class="gz.ustb.h_upload_down.UploadAction" >
    <!-- 往FileUploadInterceptor拦截器的属性注入值(调用setter方法) -->

    <interceptor-ref name="defaultStack">
        <!-- 改变当前文件上传拦截器的允许文件类型 -->
        <param name="fileUpload.allowedTypes">image/jpeg,image/jpg</param>
        <!-- 允许的文件后缀 -->
        <param name="fileUpload.allowedExtensions">jpg,jpeg,gif</param>
        <!-- 如果以上配置都写了,那么取他们的交集  -->
    </interceptor-ref>

    <param name="savePath">e:/images/</param>
    <result>/login.jsp</result>
    <result name="input">/error.jsp</result>
</action>

8.2 文件下载
视图类型一定是Stream

<action name="down_*" class="gz.ustb.h_upload_down.DownAction"
    method="{1}">
    <param name="serverPath">e:/images/</param>
    <result name="list">/listFile.jsp</result>
    <!-- 文件下载的关键: 视图类型一定是stream -->
    <result name="down" type="stream">
        <!-- 往StreamResult类中的属性注入内容 -->
        <!-- 返回给浏览器的文件类型。返回通用的二进制 -->
        <param name="contentType">application/octet-stream</param>
        <!-- 返回给浏览器的输入流 -->
        <param name="inputName">inputStream</param>
        <!-- 告诉浏览器的方式下载资源 ${name}: 获取Action中的getName()方法的数据 -->
        <param name="contentDisposition">attachment;filename=${name}</param>
        <!-- 缓存大小 -->
        <param name="bufferSize">1024</param>
    </result>
</action>


下载:
<action name="down" class="xxxxx">
    <result  type="stream">
        <param name="contentType">xxxxx</param>
        <param name="inputName">Action中定义获取输入流的方法(方法名)</param>
        <param name="contentDispostion">xxxxx</param>
        <param name="bufferedSize"></param>
    </result>
</action>

在Action对象中提供一个对应的获取输入流的方法

//需要提供给struts写出数据的输入流
public InputStream getInputStream(){
        try {
            FileInputStream fis = new FileInputStream(new File(serverPath+name));
            return fis;
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

public String getName() {
        return name;
}

}
“`

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用 JavaScript 编写的记忆游戏(附源代码)   项目:JavaScript 记忆游戏(附源代码) 记忆检查游戏是一个使用 HTML5、CSS 和 JavaScript 开发的简单项目。这个游戏是关于测试你的短期 记忆技能。玩这个游戏 时,一系列图像会出现在一个盒子形状的区域中 。玩家必须找到两个相同的图像并单击它们以使它们消失。 如何运行游戏? 记忆游戏项目仅包含 HTML、CSS 和 JavaScript。谈到此游戏的功能,用户必须单击两个相同的图像才能使它们消失。 点击卡片或按下键盘键,通过 2 乘 2 旋转来重建鸟儿对,并发现隐藏在下面的图像! 如果翻开的牌面相同(一对),您就赢了,并且该对牌将从游戏中消失! 否则,卡片会自动翻面朝下,您需要重新尝试! 该游戏包含大量的 javascript 以确保游戏正常运行。 如何运行该项目? 要运行此游戏,您不需要任何类型的本地服务器,但需要浏览器。我们建议您使用现代浏览器,如 Google Chrome 和 Mozilla Firefox, 以获得更好、更优化的游戏体验。要玩游戏,首先,通过单击 memorygame-index.html 文件在浏览器中打开游戏。 演示: 该项目为国外大神项目,可以作为毕业设计的项目,也可以作为大作业项目,不用担心代码重复,设计重复等,如果需要对项目进行修改,需要具备一定基础知识。 注意:如果装有360等杀毒软件,可能会出现误报的情况,源码本身并无病毒,使用源码时可以关闭360,或者添加信任。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值