疯狂Activiti6连载(4)流程引擎数据源配置

  本文节选自《疯狂工作流讲义(第2版)》

京东购买地址:https://item.jd.com/12246565.html

工作流Activiti6电子书http://blog.csdn.net/boxiong86/article/details/78488562

工作流Activiti6教学视频http://blog.csdn.net/boxiong86/article/details/78608585

配置文件读取与数据配置

要点

    掌握Activiti的配置文件读取方式

    掌握Activiti的数据源配置

流程引擎配置对象

ProcessEngineConfiguration对象代表一个Activiti流程引擎的全部配置,该类提供一系列创建ProcessEngineConfiguration实例的静态方法,这些方法用于读取和解析相应的配置文件,并返回ProcessEngineConfiguration的实例。除这些静态方法外,该类其他可配置的引擎属性提供相应的settergetter方法。本小节主要讲解如何使用这些静态方法创建ProcessEngineConfiguration实例。

读取默认的配置文件

ProcessEngineConfigurationcreateProcessEngineConfigurationFromResourceDefault方法,使用Activiti默认的方式创建ProcessEngineConfiguration的实例。这里所说的默认方式,是指由Activiti决定读取配置文件的位置、文件的名称和配置bean的名称这些信息。Activiti默认到ClassPath下读取名为activiti.cfg.xmlActiviti配置文件,启动并获取名称为processEngineConfigurationbean的实例。解析XML与创建该bean实例的过程,由Spring代为完成。

使用过Spring的朋友可以知道,只需要指定SpringXML配置文件,创建相应的BeanFactory实例,再通过getBean(bean名称)方法即可获取相应对象的实例,ProcessEngineConfiguration使用Spring框架的DefaultListableBeanFactory作为BeanFactory

代码清单4-1使用createProcessEngineConfigurationFromResourceDefault方法创建ProcessEngineConfiguration实例。

代码清单4-1codes\04\4.1\create-default\src\org\crazyit\activiti\CreateDefault.java

//使用Activiti默认的方式创建ProcessEngineConfiguration

ProcessEngineConfiguration config = ProcessEngineConfiguration.createProcessEngineConfigurationFromResourceDefault();

代码清单4-1中,Activiti默认到ClassPath下读取activiti.cfg.xml文件,如果找不到该配置文件则抛出FileNotFoundException,如果找不到名称为processEngineConfigurationbean,则抛出org.springframework.beans.factory.NoSuchBeanDefinitionException,本例中的activiti.cfg.xml内容如代码清单4-2所示。

代码清单4-2codes\04\4.1\create-default\resource\activiti.cfg.xml

<!--只配置相应的数据库属性-->

<bean id="processEngineConfiguration"

class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">

<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/act" />

<property name="jdbcDriver" value="com.mysql.jdbc.Driver" />

<property name="jdbcUsername" value="root" />

<property name="jdbcPassword" value="123456" />

</bean>

此处需要注意的是,代码清单4-2中所使用的ProcessEngineConfigurationStandaloneProcessEngineConfiguration类,ProcessEngineConfiguration为抽象类,不能直接作为beanclass进行配置ProcessEngineConfiguration的子类将在下面章节进行描述

读取自定义的配置文件

4.1.1章节中可知,默认情况下Activiti将到ClassPath下读取activiti.cfg.xml文件,如果希望Activiti读取另外名称的配置文件,可以使用createProcessEngineConfigurationFromResource方法创建ProcessEngineConfiguration,该方法参数为一个字符串对象,调用该方法时,需要告诉Activiti配置文件位置。代码清单4-3createProcessEngineConfigurationFromResource(String resource)方法。

代码清单4-3codes\04\4.1\create-resource\src\org\crazyit\activiti\CreateFromResource_1.java

//指定配置文件创建ProcessEngineConfiguration

ProcessEngineConfiguration config = ProcessEngineConfiguration

.createProcessEngineConfigurationFromResource("my-activiti1.xml");

代码清单4-3中,Activiti会到ClassPath下查找my-activiti1.xml配置文件,并创建名称为“processEngineConfiguration”的bean,此处创建bean的过程与4.1.1中描述一致,my-activiti1.xml文件的配置内容与代码清单4-2一致。

ProcessEngineConfiguration中还有一个createProcessEngineConfigurationFromResource的重载方法,该方法需要提供两个参数来创建ProcessEngineConfiguration第一个参数为Activiti配置文件的位置,第二个参数为创建bean的名称。代码清单4-4调用createProcessEngineConfigurationFromResource(String resource, String beanName)的方法。

代码清单4-4codes\04\4.1\create-resource\src\org\crazyit\activiti\CreateFromResource_2.java

//指定配置文件创建ProcessEngineConfiguration

ProcessEngineConfiguration config = ProcessEngineConfiguration

.createProcessEngineConfigurationFromResource(

"my-activiti2.xml", "test");

System.out.println(config.getProcessEngineName());

代码清单4-4中,告诉Activiti需要到ClassPath下查找my-activiti2.xml文件,并且创建名字为“test”的bean如果找不到名称为“test”的bean,则抛出NoSuchBeanDefinitionException,以下的代码会抛该异常,因为找不到名称为test2bean

ProcessEngineConfiguration config = ProcessEngineConfiguration

.createProcessEngineConfigurationFromResource(

"my-activiti2.xml", "test2");

读取输入流配置

ProcessEngineConfiguration中提供了一个createProcessEngineConfigurationFromInputStream方法,该方法使得Activiti配置文件的加载不再局限于项目的ClassPath,只要得到配置文件的输入流,即可创建ProcessEngineConfiguration

同样的,createProcessEngineConfigurationFromInputStream方法提供了两个重载的方法,可以指定在解析XMLbean的名称。代码清单4-5使用createProcessEngineConfigurationFromInputStream方法(没有指定bean名称)

代码清单4-5codes\04\4.1\create-stream\src\org\crazyit\activiti\CreateInputStream.java

File file = new File("resource/input-stream.xml");

//得到文件输入流

InputStream fis = new FileInputStream(file);

//使用createProcessEngineConfigurationFromInputStream方法创建ProcessEngineConfiguration

ProcessEngineConfiguration config = ProcessEngineConfiguration

.createProcessEngineConfigurationFromInputStream(fis);

使用createStandaloneInMemProcessEngineConfiguration方法

使用该方法创建ProcessEngineConfiguration,并不需要指定任何参数,该方法直接返回一StandaloneInMemProcessEngineConfiguration实例,该类为ProcessEngineConfiguration的子类。使用该方法创建ProcessEngineConfiguration,并不会读取任何的Activiti配置文件,这样意味着流程引擎配置的全部属性,都会使用默认值,与其他子类不一样的是,创建的StandaloneInMemProcessEngineConfiguration实例,只特别指定了databaseSchemaUpdate属性和jdbcUrl属性,详细请见代码清单4-6

代码清单4-6

codes\04\4.1\create-standalone-inmem\src\org\crazyit\activiti\CreateStandaloneInMem.java

ProcessEngineConfiguration config = ProcessEngineConfiguration

.createStandaloneInMemProcessEngineConfiguration();

//值为create-drop

System.out.println(config.getDatabaseSchemaUpdate());

//值为jdbc:h2:mem:activiti

System.out.println(config.getJdbcUrl());

该方法不需要读取任何的配置文件,ClassPath下也没有任何的Activiti配置文件,如果需要改变相关的配置,可以调用ProcessEngineConfiguration中相应的setter方法进行修改。

方法createStandaloneInMemProcessEngineConfiguration返回的是一个org.activiti.engine.impl.cfg.StandaloneInMemProcessEngineConfiguration实例,如果使用配置文件的方式创建ProcessEngineConfiguration,可以将该类配置为beanclass,但使用时需要注意该类中属性的默认值。

注:ProcessEngineConfiguration属性及其作用,将在下面章节中逐描述。

使用createStandaloneProcessEngineConfiguration方法

4.1.4类似的是createStandaloneProcessEngineConfiguration方法返回的是一个StandaloneProcessEngineConfiguration实例,并且需要注意的是,4.1.4中的StandaloneInMemProcessEngineConfiguration本小节StandaloneProcessEngineConfiguration的子类。代码清单4-7输出了StandaloneProcessEngineConfiguration类的databaseSchemaUpdatejdbcUrl的值

代码清单4-7codes\04\4.1\create-standalone\src\org\crazyit\activiti\CreateStandalone.java

ProcessEngineConfiguration config = ProcessEngineConfiguration

.createStandaloneProcessEngineConfiguration();

//默认值为false

System.out.println(config.getDatabaseSchemaUpdate());

//默认值为jdbc:h2:tcp://localhost/~/activiti

System.out.println(config.getJdbcUrl());

从代码清单4-7中可以明显看出,父类StandaloneProcessEngineConfigurationdatabaseSchemaUpdatejdbcUrl属性值分别为falsejdbc:h2:tcp://localhost/~/activiti,而其子类StandaloneInMemProcessEngineConfiguration4.1.4章节),这两个属性值分别为create-drop”和“jdbc:h2:mem:activiti

数据源配置

Activiti在启动时,会读取数据源配置,用于对数据库进行相应的操作。在前面章节中得知,Activiti会先读取配置文件,然后取得配置的bean,并对其进行初始化,本小节将讲解配置bean的一系列参数,并了解其作用。

Activiti支持的数据库

Activiti默认H2数据库,H2是一个开源的嵌入式数据库,使用Java语言编写。使用H2数据库并不需要另外安装服务器或者客户端,只需要提供一个jar包即可使用。实际的企业应用中,很少会使用这种轻量级的嵌入式数据库,因此H2数据更适合使用于单元测试。除H2数据库,Activiti还为以下的数据库提供支持:

     MySQL主流数据库之一,它是一个开源的小型关系型数据库,由它体积小、速度快,得到相当多开发者的青睐,并且最重要的是,它是免费的。

     Oracle目前世界上最流行的商业数据库,价格昂贵,但是它高效的性能、可靠的数据管理,仍令不少企业心甘情愿为其掏钱。

     PostgresPostgreSQL是另外一款开源的数据库

     DB2IBM公司研发的一款关系型数据库,其良好的伸缩性、数据库的高效性,让它成为继Oracle之后又一应用广泛的商业数据库。

     MSSQL微软研发的一款数据库产品,目前也支持在Linux下使用。

ActivitiSpring

Spring是目前非常流行的一个轻量级J2EE框架,它提供了一套轻量级的企业应用解决方案,它包括IoC容器、AOP面向切面技术以及Web MVC框架等。

使用Activiti的项目,并不意味着一定要使用SpringActiviti可以在没有Spring的环境中使用。虽然Activiti并不需要使用Spring环境,但是Activiti在创建流程引擎时,使用SpringXML解析与依赖注功能ProcessEngineConfiguration对应的配置,即为Spring中的一个bean

使用过Spring的读者,看到ProcessEngineConfiguration对应的配置,会感到非常熟悉,没有使用过的读者也不必感到气馁,因为Activiti可以在一个完全没有Spring的环境中运行。

JDBC配置

JDBC连接数据库,需要使用jdbc urljdbc驱动、数据库用户名和密码,以下代码为连接MySQL的配置:

<bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">

<!-- JDBC url -->

<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/act" />

<!-- JDBC驱动-->

<property name="jdbcDriver" value="com.mysql.jdbc.Driver" />

<!--数据库用户名-->

<property name="jdbcUsername" value="root" />

<!--数据库密码-->

<property name="jdbcPassword" value="123456" />

</bean>

以上代码配置一个bean,表示一个ProcessEngineConfiguration,并且使用设值注入”的方式将四个数据库属性设置到该bean中,换言之,该ProcessEngineConfiguration类中,肯定有相应属性的setter方法。该bean的实现类以及这些属性,将在下面章节中作详细讲解。

DBCP数据源配置

DBCPApache提供的一个数据库连接池ProcessEngineConfiguration中提供了一个dataSource属性,如果用户不希望将JDBC的相关连接属性交给Activiti,可以自己创建数据库连接,然后通过这个dataSource属性设置到ProcessEngineConfigurationActivitiProcessEngineConfiguration设置dataSource,可以采用配置或者编写代码的方式代码清单4-8为使用配置方式使用DBCP数据源。

代码清单4-8codes\04\4.2\ds-dbcp\resource\dbcp-config.xml

<!--使用DBCP数据源-->

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">

<property name="driverClassName" value="com.mysql.jdbc.Driver" />

<property name="url" value="jdbc:mysql://localhost:3306/act" />

<property name="username" value="root" />

<property name="password" value="123456" />

</bean>

<bean id="processEngineConfiguration"

class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">

<property name="dataSource" ref="dataSource" />

</bean>

代码清单4-8中的体部分,配置了一个DBCPdataSource bean,然后在processEngineConfigurationbean中注入该dataSource在初始化流程引擎配置时,只需根据情况调用ProcessEngineConfigurationcreateXXX方法即可,如以下代码所示:

//读取dbcp-config.xml配置

ProcessEngineConfiguration config = ProcessEngineConfiguration

.createProcessEngineConfigurationFromResource("dbcp-config.xml");

//能正常输出,即完成配置

DataSource ds = config.getDataSource();

//查询数据库元信息,如果能查询则表示连接成功

ds.getConnection().getMetaData();

//结果为org.apache.commons.dbcp.BasicDataSource

System.out.println(ds.getClass().getName());

本例使用了createProcessEngineConfigurationFromResource方法读取Activiti的配置文件。除使用配置外,也可以通过编码方式设置相应的dataSource,只需要先创建一个DataSource对象,然后设置到ProcessEngineConfiguration中即可,代码清单4-9为通过编码方式设置DBCP数据源。

代码清单4-9codes\04\4.2\ds-dbcp\src\org\crazyit\activiti\DBCPConfig.java

//创建DBCP数据源

BasicDataSource ds = new BasicDataSource();

//设置JDBC连接的各个属性

ds.setUsername("root");

ds.setPassword("123456");

ds.setUrl("jdbc:mysql://localhost:3306/act");

ds.setDriverClassName("com.mysql.jdbc.Driver");

//验证是否连接成功

ds.getConnection().getMetaData();

//读取Activiti配置文件

ProcessEngineConfiguration config = ProcessEngineConfiguration

.createProcessEngineConfigurationFromResource("dbcp-coding.xml");

//ProcessEngineConfiguration设置dataSource属性

config.setDataSource(ds);

System.out.println(config.getDataSource());

代码清单4-9中,先创建DataSource对象,然后为该对象设置相应的数据库连接属性,然后读取Activiti配置文件,得到ProcessEngineConfiguration对象,并将DataSource设置到该对象中。ProcessEngineConfigurationbean配置不需要设置任何属性

<!--不初始化任何属性-->

<bean id="processEngineConfiguration"

class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">

</bean>

在笔者成书时,DBCP项目已经发展到2.1版本,本小节的案例就是基于该版本。

C3P0数据源配置

DBCP类似,C3P0也是一个开源的数据库连接池,它们都被广泛的应用到开源项目以及企业用中同。与DBCP类似,可以在Activiti中使用C3P0数据源,配置方式大致相同,代码清单4-10C3P0 bean的配置。

代码清单4-10codes\04\4.2\ds-c3p0\resource\config\c3p0-config.xml

<!--使用C3P0数据源-->

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">

<property name="driverClass" value="com.mysql.jdbc.Driver" />

<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/act" />

<property name="user" value="root" />

<property name="password" value="123456" />

</bean>

 

<bean id="processEngineConfiguration"

class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">

<property name="dataSource" ref="dataSource" />

</bean>

此处需要注意的是,DBCPC3P0的属性名称不一样,可以到两个数据源的官方文档查看更详细的配置。

除了配置方式外,也可以像DBCP一样使用编码方式创建数据源,设置方式基本与DBCP一致,只是创建DataSource实例的方式不一样而已。代码清单4-11展示如何创建C3P0数据源。

代码清单4-11codes\04\4.2\ds-c3p0\src\org\crazyit\activiti\C3P0Coding.java

//创建C3P0数据源

ComboPooledDataSource ds = new ComboPooledDataSource();

//设置JDBC连接的各个属性

ds.setUser("root");

ds.setPassword("123456");

ds.setJdbcUrl("jdbc:mysql://localhost:3306/act");

ds.setDriverClass("com.mysql.jdbc.Driver");

//验证是否连接成功

ds.getConnection().getMetaData();

//读取Activiti配置文件

ProcessEngineConfiguration config = ProcessEngineConfiguration

.createProcessEngineConfigurationFromResource("config/c3p0-coding.xml");

//ProcessEngineConfiguration设置dataSource属性

config.setDataSource(ds);

System.out.println(config.getDataSource());

Activiti其他数据源配置

如果不使用第三方数据源,直接使用Activiti提供的数据源,那么还可以指定其他一些数据库属性Activiti默认使用的是myBatis的数据连接池,因此ProcessEngineConfiguration中也提供了MyBatis的配置:

     jdbcMaxActiveConnections在数据库连接池内最大的活跃连接数,默认值为10

     jdbcMaxIdleConnections连接池最大的空闲连接数。

     jdbcMaxCheckoutTime当连接池内的连接耗尽,外界向连接池请求连接时,创建连接的等待时间,单位为毫秒,默认值为20000,即20秒。

     jdbcMaxWaitTime整个连接池需要重新获取连接的时候,设置等待时间,单位为毫秒,默认值为20000,即20秒。

数据库策略配置

ProcessEngineConfiguration提供了databaseSchemaUpdate属性,该项可以设置流程引擎启动和关闭时数据库执行的策略Activiti的官方文档中,databaseSchemaUpdate有以下三个

     falsefalse为默认值,设置为该值后,Activiti在启动时,会对比数据库表中保存的版本,如果没有表或者版本不匹配时,将在启动时抛出异常。

     true设置为该值后,Activiti会对数据库中所有的表进行更新,如果表不存在,则Activiti会自动创建。

     create-dropActiviti启动时,会执行数据库表的创建操作,在Activiti关闭时,执行数据库表的删除操作。

代码清单4-12databaseSchemaUpdate配置为false

代码清单4-12codes\04\4.2\schema-update\resource\schemaUpdate-false.xml

<!--databaseSchemaUpdate设置为false -->

<bean id="processEngineConfiguration"

class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">

<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/act" />

<property name="jdbcDriver" value="com.mysql.jdbc.Driver" />

<property name="jdbcUsername" value="root" />

<property name="jdbcPassword" value="123456" />

<property name="databaseSchemaUpdate" value="false"/>

</bean>

使用以下代码启动Activiti流程引擎:

//读取Activiti配置

ProcessEngineConfiguration config = ProcessEngineConfiguration

.createProcessEngineConfigurationFromResource("schemaUpdate-false.xml");

//启动Activiti

config.buildProcessEngine();

以上代码的粗体部分,如果没有数据表则会抛出异常。这里需要注意的是,如果想看到抛出异常的效果,需要将相应数据库里面的表全部删除。如果想执行数据库表结构更新,可以将该配置设置为true,将全部数据库表删除后,再启动Activiti,即可看到Activiti已经建好全部的表,值为true的配置与代码在此不再赘述,读者可看以下配置文件与Java类:

     配置文件:codes\04\4.2\schema-update\resource\schemaUpdate-true.xml

     Java类:codes\04\4.2\schema-update\src\org\crazyit\activiti\DatabaseSchemaUpdateTrue.java

databaseSchemaUpdate设置为create-drop后,Activiti会先检查数据表是否存在,如果表已经存在,则抛出异常并停止创建流程引擎。代码清单4-13使用create-drop属性启动Activiti

代码清单4-13

codes\04\4.2\schema-update\src\org\crazyit\activiti\DatabaseSchemaUpdateCreateDrop.java

//读取Activiti配置

ProcessEngineConfiguration config = ProcessEngineConfiguration

.createProcessEngineConfigurationFromResource("schemaUpdate-create-drop.xml");

//启动Activiti

ProcessEngine engine = config.buildProcessEngine();

//关闭流程引擎

engine.close();

注意代码清单4-13中的粗体部分,如果想要Activiti执行“drop”操作,必须要调用ProcessEngineclose方法,否则将不会删除表一般情况下,将databaseSchemaUpdate配置为create-drop,更适合在单元测试中使用。

除了falsetruecreate-drop三个值外,databaseSchemaUpdate还有一个drop-create值,跟create-drop类似,drop-create会在流程引擎启动时,先将原来全部的数据表删除,再进行创建,与create-drop不同的是,不管是否调用ProcessEngineclose方法,都会执行create操作。同样地,该值在单元测试中使用比较合适,在流程引擎初始化时将原有的数据删除,在实际应用中,此举会带来较大的风险Activiti的官方文档并没有提供该项配置,读者知道即可。

注:使用各种方法读取Activiti配置,均不会创建数据库表,Activiti的数据库表只会在流程引擎创建的时候,才会按照配置的策略进行创建(代码清单4-12的粗体部分)

databaseType配置

根据前一小节得知,将databaseSchemaUpdate设置为create-drop或者drop-create时,Activiti在启动和初始化时,会执行相应的创建表和删除表操作,Activiti支持多种数据库,每种数据库的创建表与删除表的语法有可能不一样,因此,需要指定databaseType属性,来告诉Activiti,目前使用了何种数据库(当然,如果设置true而数据库中没有表的话,也需要知道使用哪种数据库)databaseType属性支持这些值:h2, mysql, oracle, postgres, mssql, db2,没有指定值时,databaseTypenull指定databaseType属性,目的是为了确定执行创建(或删除)表的SQL脚本

代码清单4-14,将该属性设置为oracle

代码清单4-14codes\04\4.2\db-type\resource\database-type.xml

<!--databaseType设置为oracle -->

<bean id="processEngineConfiguration"

class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">

<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/act" />

<property name="jdbcDriver" value="com.mysql.jdbc.Driver" />

<property name="jdbcUsername" value="root" />

<property name="jdbcPassword" value="123456" />

<property name="databaseSchemaUpdate" value="create-drop"/>

<property name="databaseType" value="oracle"/>

</bean>

使用以上配置,然后启动和关闭Activiti,会抛出MySQL异常,因为Activiti会根据该值去使用Oracle创建表和删除表的脚本,OracleSQL脚本MySQL上面执行,肯定会出错。

实际上,可以根本不需要指定该属性,Activiti就可以知道使用的是种数据库,因为配置数据源时就提供了JDBC连接属性给Activiti,根据这些属性创建JDBC连接,得到Connection对象后,可以调用getMetaData方法获取当前数据库的元数据,完全可以判断出当前所使用的数据库。的确,Activiti也是这样做的,但是为什么另外提供一个databaseType属性如此多此一举呢?笔者认为,Activiti为防止适配数据类型出现异常,就提供多一个这样的值来给使用者选择,确保能适配到准确的数据库类型。

注:没有配置databaseType属性,Activiti会使用ConnectiongetMetaData方法获取数据库元数据,但是一旦配置了databaseType属性,将会以该值为准。

 本文节选自《疯狂工作流讲义(第2版)》

京东购买地址:https://item.jd.com/12246565.html

工作流Activiti6电子书http://blog.csdn.net/boxiong86/article/details/78488562

工作流Activiti6教学视频http://blog.csdn.net/boxiong86/article/details/78608585

本书代码共享地址:https://gitee.com/yangenxiong/CrazyActiviti

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

杨大仙的程序空间

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

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

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

打赏作者

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

抵扣说明:

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

余额充值