持续集成(施工完善中)


一.配置管理

配置管理是持续集成、发布管理以及部署流水线的基础,其对交付团队内部的协作起到巨大的促进作用。

制定策略保存基线和控制变更:

  • 应用程序的源代码、构建脚本、测试、文档、需求、数据库脚本、代码库以及配置文件
  • 用于开发、测试和运维的工具集
  • 用于开发、测试和生产运行的所有环境
  • 与应用程序相关的整个软件栈,包括二进制代码及相关配置
  • 在应用程序的整个生产周期(包括构建、部署、测试以及运维)的任意一种环境上,与该应用程序相关联的配置

目标:

  • 仅依靠保存于版本控制系统中的数据(除了生产数据),可以从无到有重建生产系统
  • 可以随时将应用程序回滚到以前某个正常正确的状态版本中
  • 确保在测试、试运行和正式上线时以同样的方式创建部署环境

二.持续集成
持续集成是让正在开发的软件一直处于可工作状态。
2.1 准备工作
2.1.1 版本控制
包括与项目相关的所有内容都必须提交到一个版本控制库中,包括产品代码、测试代码、数据库脚本、构建与部署脚本、以及所有用于创建、安装、运行和测试该应用程序的 东西。(git、svn等)
2.1.2 自动化构建
人和计算机都能通过命令行字洞执行应用的构建、测试以及部署过程。
原因:
  • 要能在持续集成环境中以自动化的方式来执行整个构建过程,以便出现问题时能够审计
  • 应将构建脚本与代码库同等对待,对它进行测试,并不断重构,以使其保持整洁且容易理解
  • 使理解、维护和调试构建过程更容易,并有利于和运维人员更好的协作
2.1.3 团队共识
持续集成不是一种工具,而是一种实践。需要开发团队给予一定的投入病遵守一些准则。
2.2 一个基本的持续集成系统
使用持续集成服务:
  1. 查看是否有构建正在运行,如果有,请等它运行完,如果它失败了,你要与团队中的其他人一起将其修复,然后再提交自己的代码
  2. 一旦构建完成且测试全部通过,就从版本控制库中将该版本的代码更新到自己的开发环境中
  3. 在自己的开发机上执行构建脚本,运行测试,以确保在你机器上的所有代码都工作正常。当然你也可以利用持续集成中的个人构建完成这一步骤
  4. 如果本地构建成功,就将代码提交到版本控制库中
  5. 等待包含你的这次提交的构建结果
  6. 如果构建失败,在自己的开发机上立即修复这个问题,然后转到步骤3
  7. 如果构建成功,cheers!开始下一项任务
2.3 持续集成前提条件
2.3.1频繁提交
很多项目使用版本控制中的分支技术来进行大型团队的管理,然而当使用分支时,你的代码就没有和其他开发人员的代码进行即时集成。
2.3.2 创建全面的自动化测试套件
有三类测试我们会在持续集成构建中使用,它们分别是单元测试、组件测试和验收测试
  • 单元测试用于单独测试应用程序中某些小单元的行为,运行时间快
  • 组件测试用于测试应用程序中几个组件的行为,运行时间较长
  • 验收测试用于验证应用程序是否满足业务需求所定义的验收条件,包括应用程序提供的功能和其他特定需求,比如容量、有效性、安全性等,验收测试最好采用将整个应用程序运行于类生产环境的运作方式
2.3.3 保持较短的构建和测试过程
2.3.4 管理开发工作区
在本地开发环境运行时,应确保使用的自动化过程于持续集成环境一致,与测试环境一致,与生产环境一致。
  • 细心的配置管理,不仅时管理代码,以及测试数据、数据库脚本、构建脚本和部署脚本
  • 对第三方以来的配置管理
  • 确保自动化测试都能够再开发机上运行
2.4 使用持续集成软件
2.5 总结
  • 好的持续集成时基石,在此之上你可以构建更多的基础设施:
  • 一个巨大的可视化指示器,用于显示构建系统所收集到的信息,以提供高质量的反馈
  • 结果报告系统,以及针对自己测试团队的安装包
  • 为项目经理提供关于应用程序质量的数据的提供程序
  • 使用部署流水线,可以将其延展到生产环境,为测试人员和运维团队提供一键式部署系统

三.测试策略的实现
3.1 引言
测试策略的设计主要是识别和评估项目风险的优先级,以及决定采用哪些行动来缓解风险的过程
3.2 测试的分类
   业务导向的
自动的手动的
功能验收测试
演示
易用性测试
探索性测试
自动的手动的/自动的
单元测试
集成测试
系统测试
非功能验收测试
包括容量测试,
安全测试等
技术导向的

3.2.1 业务导向且支持开发过程的测试
3.2.2 技术导向且支持开发过程的测试
单元测试、组件测试和部署测试
3.2.3 业务导向且评价项目的测试
演示、探索性测试、易用性测试和beta测试
3.2.4技术导向且评价项目的测试
验收测试:功能测试、非功能测试
3.2.5 测试替身
哑对象、假对象、桩(stub)、spy、模拟对象(mock)
3.3 现实中的情况与应对策略
3.4 流程
测试应成为与软件交付相关的每一个人的责任,并从项目一开始就被引入并持续进行时,才能产生高质量的软件。测试主要是建立反馈环,而这个反馈环会驱动开发、设计和发布等工作。

四.部署流水线解析
4.1 什么是部署流水线
部署流水线是指软件从版本控制库到用户手中这一过程的自动化表现形式
部署流水线是由提交阶段、自动化验收测试阶段、手工测试阶段、发布阶段以及为软件交付流程建模所需的其他阶段组成,其本质是一个自动化的软件交付流程
4.2 部署流水线的相关实践
a.只生成一次二进制包
二进制包:所有可执行代码的集合,例如jar包、.NET程序集和.so文件
b.对不同环境采用统一部署方式
环境不同不应该为每个环境都建立一个独立的部署脚本,只要把与特定环境相关的特定配置分开放置就行了。一种方法是使用属性文件保存配置信息,比如为每个环境保存一个属性文件,并将其放在版本控制库中;另外也可以提供部署时的配置信息,比如将其放在一个目录服务中,也可以将其放在数据库中。
如果使用同一个脚本在所有的环境上进行部署,那么当在某个环境上部署失败时,就可以确定其原因一定来自以下三个方面:
  • 与该环境相关的配置文件中,某项配置有问题
  • 基础设施或应用程序所依赖的某个服务有问题
  • 环境本身的配置有问题
c.对部署进行冒烟测试
d.向生产环境的副本中部署
保障良好的配置管理实践:
  • 基础设施是相同的,比如网络拓扑和防火墙的配置等
  • 操作系统的配置(包括补丁版本)都是相同的
  • 应用程序所用的软件栈是相同的
  • 应用程序的数据处于一个已知且有效的状态
e.只要环节失败,就停止整个流水线
3.提交阶段
部署流水线中第一个阶段的目标就是消除那些不适合生产环境的构建,尽早给团队一个程序出错的信号
提交阶段包含步骤:
  • 编译代码(如果所用开发语言需要的话)
  • 运行一套提交测试
  • 为后续阶段创建二进制包
  • 执行代码分析来检查代码的健康状况
  • 为后续阶段做准备工作,比如准备一下后续测试所用的数据库
4.自动化验收测试之门
整个团队都是验收测试的所有者,如果验收测试失败了,整个团队就要停下来,马上修复它。
5.后续的测试阶段
验收测试阶段时整个遴选候选发布版本过程中一个重要里程碑,一旦这个阶段结束了,这个候选版本就会受到开发人员之外更多人的广泛关注。
a.手工测试
b.非功能测试
6.发布准备
  • 让参与项目交付过程的人共同创建并维护一个发布计划(包括开发人员和测试人员,以及运维人员,基础设施和支持人员)
  • 通过尽可能多的自动化过程最小化人为错误发生的可能性,并从最容易出错的环节开始实现自动化
  • 在类生产环境中经常做发布流程演练,这样就可以对这个流程及其所使用的技术进行调试
  • 如果事情并没有按计划执行,要有撤销某次发布的能力
  • 作为升级和撤销过程的一部分,制定配置迁移和数据迁移的策略
a.自动部署与发布
通过自动化的环境准备和管理,最佳的配置管理实践以及虚拟化技术,环境准备和维护的成本会显著降低
b.变更的撤销
传统人们对新版本的发布常常存在恐惧心理,原因有二:
  1. 害怕引入问题,因为手工的软件发布过程很可能引入难以发现的认为错误,或者部署手册本身就隐藏着某个错误
  2. 担心由于发布过程中的一个问题或新版本的某个缺陷,使你的原来承诺的发布失败
撤销策略:
  1. 在发布新版本时,让旧版本仍旧处于可用状态,并在发布后保持一段时间
  2. 从头开始重新部署旧版本
c.在成功的基础上构建
当一个候选发布版本能够部署到生产环境时,我们就确信:
  • 代码可以编译
  • 代码能够按开发人员的预期运行,因为它通过了单元测试
  • 系统能够满足分析人员或用户预期,因为它通过了所有的验收测试
  • 基础设施的配置和基线环境被恰当地管理了,因为应用程序在模拟的生产环境上通过了测试
  • 系统所有的正确组件都就绪了,因为它是可以部署的
  • 部署脚本也是可以工作的,因为在该版本到这一阶段之前,部署脚本至少在开发环境中用过一次,在验收测试阶段用过一次,在测试环境中用过一次
  • 我们需要部署的所有内容都在版本控制库中,而且不需要手工干预,因为我们已经部署这个系统好几次了
7.实现一个部署流水线
步骤:
  • 对价值流建模,并创建一个可工作的简单框架
  • 将构建和部署流程自动化
  • 将单元测试和代码分析自动化
  • 将验收测试自动化
  • 将发布自动化
a.对价值流进行建模并创建简单的可工作框架
最简单的价值流:
  1. 提交阶段
  2. 验收测试
  3. 向类生产环境部署应用
b.构建和部署过程的自动化
部署活动可能包含:
  1. 为应用程序打包,如果应用程序的不同组件需要部署在不同的机器上,就要分别打包
  2. 安装和配置过程应该实现自动化
  3. 写自动化部署测试脚本来验证部署是否成功了
c.自动化单元测试和代码分析
d.自动化验收测试
e.部署流水线的演进
常见外延:组件、分支
  1. 并不需要一次实现整个流水线,而应该是增量式实现
  2. 部署流水线是构建、部署、测试和发布应用程序整个流程中有效的,也是最重要的统计数据来源
  3. 部署流水线是一个有生命的系统
8.度量
反馈是所有软件交付流程的核心,改善反馈的最佳方法是缩短反馈周期,并让结果可视化
对于软件交付过程来说,最重要的全局度量指标就是周期时间,优化周期时间:
  1. 识别系统中的约束,也就是构建、测试、部署和发布这个流程中的瓶颈
  2. 确保供应,即确保最大限度地提高流程中这部分的产出
  3. 根据这一约束调整其他环节的产出,即其他资源都不会100%满负荷工作
  4. 为约束环节扩容
  5. 理顺约束环节并重复上述步骤,即在系统中找到下个约束,重复1
其余度量项:
  • 自动化测试覆盖率
  • 代码库的某些特征,比如重复代码量、圈复杂度、输入耦合度、输出耦合度、代码风格问题等
  • 缺陷的数量
  • 交付速度,即团队交付可工作、已测试过并可以使用的代码速率
  • 每天提交到版本控制库的次数
  • 每天构建的次数
  • 每天构建失败的次数
  • 每次构建所花的时间,包括自动化测试的时间
9.小结
部署流水线的目的是,让软件交付过程中的每个人都能够看到每个构建版本从提交到发布的整个过程

五.构建与部署的自动化
一般的自动化部署步骤:配置应用程序、初始化数据、配置基础设施、操作系统和中间件、以及安装所需的模拟外部系统等
应该由开发人员和运维人员共同决定怎么做自动化部署
构建和部署系统必须一直保持活力,即这个系统不仅要从项目刚开始就开发,而且一直要持续到软件在生产环境中的维护阶段
2.构建工具
构建工具的不同点在于它是任务导向的还是产品导向的
假想的构建工具可能会调用“设置测试数据”、“初始化”、“编译源代码”、“初始化”、“编译测试”、“运行测试”任务
  • 任务导向:每个任务都会知道它自己在构建过程中是否被运行过,在构建之间并不保存状态,适合c#
  • 产品导向:它们是用一系列的文件建模的,将状态以时间戳的形式保存在每个任务执行后生成的文件中,适合c/c++
a.Make
产品
b.Ant
任务
c.NAnt MSBuild
类似Ant
d.Maven
“管理胜于配置”,自动管理Java库和项目间的依赖
e.Rake
f.Build
g.Psake
3.构建部署脚本化的原则与实践
a.为部署流水线的每个阶段构建脚本
DDD(Domain-Driven Design,领域驱动设计)
b.使用恰当的技术部署应用程序
在做自动化部署工作时,应该使用恰当的工具,而不是通用脚本语言(除非部署流程十分简单)
最重要的是,开发人员(至少可以在他们自己的开发机器上)、测试人员和运维人员都要做应用程序的部署工作,因此他们要共同判定如何部署应用程序,这件事也要在项目一开始就做
部署脚本应该能够完成应用程序的安装和升级任务。在部署之前,它要能够关闭当前运行的版本,而且既支持在当前的数据库上升级,又能够从头创建数据库
c.使用同样的脚本向所有的环境部署
d.使用操作系统自带的包管理工具
e.确保部署流程是幂等的(Idempotent)
无论开始部署时目标环境处于何种状态,部署流程应该总是令目标环境达到同样(正确)的状态,并以之为结束点
例外情况:
对于集群系统来说,总是将整个集群系统同时重新部署就不可取
如果应用程序是由多个组件构成的,而这些组件来源于不同的源代码库,那么二进制包就由这些源代码库中的一系列修正版本来定义
使用效果幂等的工具进行部署。Rsync,Puppet
f.部署系统的增量式演进
4.面向JVM的应用程序项目结构
项目结构
a.源代码管理
b.测试管理
c.构建输出的管理
d.库文件管理
5.部署脚本化
环境管理的核心原则之一就是,对测试和生产环境的修改只能由自动化过程执行
6.小贴士
a.总是使用相对路径
b.消除手工部署
c.从二进制包到版本控制库的内建可追溯性
d.不要把二进制包作为构建的一部分放到版本控制库中
e.“test”不应该让构建失败
f.用集成冒烟测试来限制应用程序

七.提交阶段
7.1 提交阶段
提交阶段是部署流水线的入口,它不但是候选发布版本的诞生地,也是很多团队实现部署流水线的起点


提交阶段工作流程:
  1. 编译(如果需要的话),并在集成后的源代码上运行提交测试;
  2. 创建能部署在所有环境中的二进制包(如果使用需要编译的语言,则包括编译和组装);
  3. 执行必要的分析,检查代码库的健康状况;
  4. 创建部署流水线的后续阶段需要使用的其他产物(比如数据库迁移或测试数据)。

7.2 提交阶段的原则和实践
提交阶段的首要目标是要么创建可部署的产物,要么快速失败并将失败原因通知给团队
7.2.1 提交快速有用的反馈

提交测试的失败原因:
  • 由于语法错误导致编译失败;
  • 由于语义错误导致一个或多个测试失败;
  • 由于应用程序的配置或环境方面(包括操作系统本身)的问题引起。

7.2.2 何时令提交阶段失败

7.2.3 精心对待提交阶段
随着项目的进行,要不断努力地改进提交阶段脚本的质量、设计和性能

7.2.4 让开发人员也拥有所有权

7.2.5 在超大项目团队中指定一个构建负责人

7.3 提交阶段的结果

制品库:
  • 制品库算是一个不同寻常的版本控制系统,他仅保存某些版本,而不是全部;
  • 能够追溯已发布的软件究竟是由版本控制库中的那个版本产生的;
  • 二进制文件的创建过程应该是可重复的。

成功通向生产环境的步骤:
交付团队的某个人提交了一次修改;
持续集成服务器运行提交阶段;
  1. 成功结束后,二进制包和所有报告和元数据都被保存到制品库中;
  2. 持续集成服务器从制品库中获取提交阶段生成的二进制包,并将其部署到一个类生产测试环境中;
  3. 持续集成服务器使用提交阶段生成的二进制包执行验收测试;
  4. 成功完成后,该候选发布版本被标记为"已成功通过验收测试";
  5. 测试人员拿到已通过验收测试的所有构建列表,并通过单击一个按钮将其部署到手工测试环境中;
  6. 测试人员执行手工测试;
  7. 一旦手工测试也通过了,测试人员会更新这个候选发布版本的状态,指示它已经通过手工测试了;
  8. 持续集成服务器从制品库中拿到通过验收测试(根据部署流水线的配置,也可能是手工测试)的最新候选发布版本,将其部署到生产测试环境;
  9. 对这个候选发布版本进行容量测试;
  10. 如果成功了,将这个候选版本的状态更新为"已通过容量测试";
  11. 如果部署流水线中还有后续阶段的话,一直重复这个过程;
  12. 一旦这个候选发布版本通过了所有相关阶段,把它标记为"可以发布",并且任何被授权的人都能将其发布,通常是由质量保证人员和运维人员共同批准;
  13. 一旦发布以后,将其标记为"已发布"。

7.4 提交测试套件的原则和实践
7.4.1 避免用户界面

7.4.2 使用依赖注入

7.4.3 避免使用数据库

7.4.4 在单元测试中避免异步

7.4.5 使用测试替身

打桩是指利用模拟代码来代替原系统中的某个部分,并提供已封装好的响应。

7.4.6 最少化测试中的状态

7.4.7 时间的伪装

7.4.8 蛮力

7.5 小结

提交测试聚焦于尽快地捕获那些因修改向系统中引入的最常见错误,并通知开发人员,以便他们能够快速修复它们
提交阶段提供反馈的价值在于,对它的投入可以让系统高效且快速地工作

八.自动化验收测试
8.1 引言

对于一个单独的验收测试,其目的是验证一个用户故事或需求的验收条件是否被满足

验收条件:
功能性验收条件
非功能性验收条件:容量、性能、可修改性、可用性、安全性、易用性等等

验收测试是针对业务的,单元测试的目的是证明应用程序的某个单一部分的确是按开发人员的思路运行的,但这并不能断言它也就是用户想要的功能

8.2 为什么验收测试是至关重要的
8.2.1 如何创建可维护的验收测试套件

8.2.2 GUI上的测试

8.3 创建验收测试
8.3.1 分析人员和测试人员的角色

8.3.2 迭代开发项目中的分析工作

8.3.3 将验收条件变成可执行的规格说明书

行为驱动开发核心理念之一就是验收测试应该以客户期望的应用程序行为的方式来书写

8.4 应用程序驱动层
应用程序驱动层是一个知道如何与应用程序(即被测试的系统)打交道的层次

8.4.1 如何表述验收条件

8.4.2 窗口驱动器模式:让测试与GUI解耦
验收测试分为三层:可执行的验收条件、测试实现和应用程序驱动器层
应用程序驱动器与窗口驱动器的区别在于:窗口驱动器指导如何与GUI打交道

8.5 实现验收测试
8.5.1 验收测试中的状态

8.5.2 过程边界、封装和测试

最直截了当的测试是那些不需要权限就能验证需求的测试

8.5.3 管理异步与超时问题

8.5.4 使用测试替身对象

8.6 验收测试阶段
8.6.1 确保验收测试一直处于通过状态

8.6.2 部署测试

8.7 验收测试的性能
8.7.1 重构通用任务

8.7.2 共享昂贵资源

8.7.3 并行测试

8.7.4 使用计算网络

8.8 小结

验收测试对提高开发流程效率非常重要,它使交付团队的所有成员都关注于真正的工作:用户想从应用程序中得到什么

采用验收测试条件驱动的测试代表了更先进的理念,因为它:
  • 为"软件是否满足业务目标"提供了更高的信心;
  • 为系统进行大范围修改提供了一个保护网;
  • 通过全面的自动回归测试极大地提高了质量;
  • 无论什么时候出现缺陷,都能提供快速、可靠的反馈,以便可以立即修复;
  • 让测试人员有更多的时间和精力去思考测试策略、开发可执行的规格说明,以及执行探索性测试和易用性测试;
  • 缩短周期时间,使持续部署成为可能。

九.非功能需求的测试
9.1 引言

性能是对处理单一事务所花时间的一种度量,既可以单独衡量,也可以在一定的负载下衡量
吞吐量是系统在一定时间内处理事务的数量,通常它受限于系统中的某个瓶颈
在一定的工作负载下,当每个单独请求的响应时间维持在可接受的范围内时,该系统所能承担的最大吞吐量被称为他的容量
性能常被用来指这些术语的合集

9.2 非功能需求的管理

9.3 如何为容量编程

解决容量问题可采取的策略:
  • 为应用程序决定一种架构。通常要特别注意进程、网络边界和I/O;
  • 了解并使用正确的模式,避免使用那些影响系统容量和稳定性的反模式;
  • 除了采用适当模式以外,还要确保团队在已经明确的应用架构下进行开发,不要为容量做无谓的优化;
  • 注意在数据结构和算法方面的选择,确保它们的属性与应用程序相吻合;
  • 处理线程时要特别小心;
  • 创建一些自动化测试来断言所期望的容量级别;
  • 使用调测工具主要关注测试中发现的问题,并修复它,不要使用"让它越来越好"这类策略;
  • 只要有可能,就是用真是的容量数据来做度量

9.4 容量度量
容量度量要广泛研究应用程序的特征:
  • 扩展性测试;
  • 持久性测试;
  • 吞度量测试;
  • 负载测试

9.5 容量测试环境

9.6 自动化容量测试

容量测试应达到以下几个目标:
  • 测试具体的现实场景,这样就不会因为测试太抽象而错过真实应用场景中那些重要的bug;
  • 预先设定成功的门槛,这样就能判定容量测试是否通过了;
  • 尽可能让测试运行时间短一些,从而保证容量测试在适当时间内完成;
  • 在变更面前要更健壮一些,从而避免因对应用程序的频繁修改而不断返工;
  • 组合成大规模的复杂场景,这样就可以模拟显示世界中的用户使用模式;
  • 可重复的,并且既能申请执行,也能并行执行,以便这些测试既可以做负载测试,也可以做持久性测试

9.6.1 通过UI的容量测试

9.6.2 基于服务或公共API来录制交互操作

9.6.3 使用录制的交互模板

9.6.4 使用容量测试桩开发测试

9.7 将容量测试加入到部署流水线

9.8 容量测试系统的附加价值

9.9 小结


十.应用程序的部署与发布
10.1 引言

10.2 创建发布策略

在项目开始创建发布策略的第一个版本时,应该考虑下列内容:
  • 每个环境的部署和发布都是由谁负责的;
  • 创建一个资产和配置管理策略;
  • 部署时所用的技术的描述。运维团队和开发团队应该对其达成共识;
  • 实现部署流水线的计划;
  • 枚举所有的环境,包括用于验收测试、容量测试、集成测试、用户验收测试的环境,以及每个构建在这些环境中的移动过程;
  • 描述在测试和生产环境中部署时应该遵循的流程,比如提交一个变更申请,以及申请授权等;
  • 对应用程序的监控需求,包括用于通知运维团队关于应用程序相关状态的API或服务;
  • 讨论部署时和运行时的配置方法如何管理,以及它们与自动化部署流程是如何关联在一起的;
  • 描述应用程序如何与所有外部系统集成。比如,在那个阶段进行集成?作为发布过程里的一份子,如何对这种外部集成进行测试?一旦出现问题,运维人员如何与供应商进行沟通?
  • 如何记录日志详情,以便运维人员能够确定应用程序的状态,识别出错原因;
  • 制定灾难恢复计划,以便在灾难发生之后,可以恢复应用程序的状态;
  • 对软件的服务级别达成一致,比如,应用程序是否有像故障转移以及其他高可用性策略等方面的需求;
  • 生产环境的数量大小及容量计划:应用程序会创建多少数据?需要多少个日志文件或数据库?需要多少带宽或磁盘空间?客户对响应延迟的容忍度是什么?
  • 制定一个归档策略,以便不必为了审计或技术支持而保留生产数据;
  • 如何对生产环境进行首次部署;
  • 如何修复生产环境中出现的缺陷,并为其打补丁;
  • 如何升级生产环境中的应用程序以及迁移数据;
  • 如何做应用程序的生产服务和技术支持。

10.2.1 发布计划













  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值