基于.NET平台的分层架构实战

摘自:http://www.tzwhx.com/newOperate/html/1/11/113/13404.html 比较不错的文章

 

基于.NET平台的分层架构实战(一)——综述

     通过浏览博客园的文章发现,很多朋友对分层架构特别感兴趣,刚好我刚做完的毕业设计就是专门研究.NET平台上分层架构的(题目叫“基于.NET平台的分层架构与设计模式应用研究”)。通过做这篇论文,我对分层架构有了一定的了解,所以,就萌发了想写一个文章系列,详述一下分层架构。然而,论文的理论性太强,不适合在网上发布,尤其不适合初学者理解,所以,我想在这个文章系列中,少讲理论,而是通过做一个完整的案例来讨论分层架构的基本方法,这样会直观很多。希望在这个文章系列的写作过程中,能和朋友们一起学习,一起进步。

      为了让朋友们把主要精力放在理解分层架构而不是案例本身,我准备选择一个相对简单的留言本系统作为Demo,这个系统的名字就叫做NGuestBook。

      初步计划将这个文章系列分为以下几篇:
      1.综述
      2.系统需求分析及数据库设计
      3.架构概要设计
      4.实体类的实现
      5.接口的设计与实现
      6.依赖注入及IoC的设计与实现
      7.数据访问层的第一种实现——Access+动态生成SQL语言
      8.数据访问层的第二种实现——SQLServer+存储过程
      9.数据访问层的第三种实现——基于NBear框架的ORM实现
      10.业务逻辑层的实现
      11.表示层的实现
      12.使用ASP.NET AJAX框架对表示层进行改进
      13.总结

      当然,以上只是初步计划,在写文章的过程中可能会根据具体情况适当调整,但是内容大体就是这些。

      这个文章系列不会对所用到的技术进行详细讲解,具体请参考相关文献,阅读文章前最好能对以下技术有一个了解:
      1.C#语言
      2.ASP.NET
      3.设计模式
      4.关系数据库基础知识
      5.软件架构基本原则与软件工程基础知识
      6.基于NBear框架的ORM技术
      7.JavaScript,Ajax
      8.ASP.NET AJAX框架(特别是客户端编程)
      9.HTML,CSS,标准化布局

      另外,本文章系列是基于.NET framework2.0框架平台进行讨论,3.5平台的新特性(如LINQ、ASP.NET MVC等)不会讨论,IDE使用Visual Studio 2005,数据库会用到SQLServer2005 Express和Access2003。

 

基于.NET平台的分层架构实战(二)——需求分析与数据库设计

     在实际的项目中,需求分析和数据库的设计是很重要的一个环节,这个环节会直接影响项目的开发过程和质量。实际中,这个环节不但需要系统分析师、软件工程师等计算机方面的专家,还需要相关领域的领域专家参与才能完成。

      但是,在这个文章系列中,所要使用的Demo仅仅是一个例子,而且其业务极为简单,因此,这里并不是真正的需求分析和数据库设计,而是将Demo的需求和数据库罗列至此,使朋友们对Demo有一个大体的了解,方便后续文章中开发过程的理解。

需求分析:
      这个项目是一个留言本,其业务极为简单,现将其描述如下。
      1.任何访问者可以进行留言,留言完成后,不会立即显示正文,而是要经过管理员验证后才可显示。
      2.任何访问者可以对留言发表评论,未通过验证的留言不可以评论。
      3.管理员可以对留言进行回复(这个回复不同于评论,是直接显示在正文下面,而且是一个留言只能有一个回复),并可对留言与评论实行删除,以及对留言进行通过验证操作。
      4.管理员分为超级管理员和普通管理员。超级管理员只有一个,负责对普通管理员实行添加、删除操作。普通管理员可偶多个,负责对留言的管理,并可以修改自己的登录密码。

      这个项目的用例图如下:
 

数据库设计:
      设计数据表之前,首先进行实体和关系的识别与确定。
      通过需求分析,可以观察得出,本项目的实体有:管理员(不包括超级管理员),留言,评论。本项目的关系有:留言与评论间的一对多关系。

      进一步,数据库各表的设计如下:

      管理员表(TAdmin)
      ID    int    管理员ID    NotNull    主键,自增
      Name    varchar(20)    登录名    NotNull
      Password    varchar(50)    登录密码    NotNull    使用MD5加密

      留言表(TMessage)
      ID    int    留言ID    NotNull    主键,自增
      GuestName    varchar(20)    留言者用户名    NotNull
      GuestEmail    varchar(100)    留言者E-mail    Null
      Content    text    留言内容    NotNull
      Time    datetime    发表留言时间    NotNull    
      Reply    text    回复    Null
      IsPass    varchar(10)    是否通过验证    NotNull

      评论表(T Comment
      Content    text    评论内容    NotNull
      Time    datetime    发表评论时间    NotNull
      MessageID    int    所属留言的ID    外键
 
基于.NET平台的分层架构实战(三)—架构概要设计
     本文主要是对将要实现的架构进行一个总体的描述,使朋友们对这个架构有个宏观上的认识。这篇文章理论性的东西会偏多一点,从下篇开始,将进行实际项目的开发。这篇文章的许多内容摘自我的毕业论文。

架构基本原则:
      这里,将描述一些在这个架构设计中的基本原则,其中很多都是经典的设计原则,不过针对分层架构的特点,用我自己的语言进行了描述。其中也有我自己提出的原则。

 

      逐层调用原则及单向调用原则
      现在约定将N层架构的各层依次编号为1、2、…、K、…、N-1、N,其中层的编号越大,则越处在上层。那么,我们设计的架构应该满足以下两个原则:
      1.第K(1       2.如果P层依赖Q层,则P的编号一定大于Q。
      其中第一个原则,保证了依赖的逐层性,及整个架构的依赖是逐层向下的,而不能跨层依赖。第二个原则,则保证了依赖的单向性,及只能上层依赖底层,而不能底层反过来依赖上层。

      针对接口编程,而不是针对实现编程
      这里所指的接口,不是特指编程语言中的具体语言元素(如C#中由Interface定义的语言接口),而是指一种抽象的,在语义层面上起着接合作用语义体。它的具体实现,可能是接口,可能是抽象类,甚至可能是具体类。
      我认为,从不同的视角,接口可以有以下两种定义:
      1.接口是一组规则的集合,它规定了实现本接口的类或接口必须拥有的一组规则。体现了自然界“如果你是……则必须能……”的理念。
      2.接口是在一定粒度视图上同类事物的抽象表示。注意这里我强调了在一定粒度视图上,因为“同类事物”这个概念是相对的,它因为粒度视图不同而不同。
      具体到N层架构中,针对接口编程的意义在部分上是这样的:
      现仍约定将N层架构的各层依次编号为1、2、…、K、…、N-1、N,其中层的编号越大,则越处在上层,那么第K层不应该依赖具体一个K-1层,而应该依赖一个K-1层的接口,即在第K层中不应该有K-1层中的某个具体类。

      依赖倒置原则
      在软件设计原则中,有一种重要的思想叫做依赖倒置。它的核心思想是:不能让高层组件依赖底层组件,而且,不管高层组件和底层组件,两者都应依赖于抽象。
      那么,这个原则和我们上面的原则是否矛盾呢?其实并不矛盾。
      因为这个原则定义中的“依赖”是指“具体依赖”,而上面定义中的依赖全部指“抽象依赖”。我对这两种依赖的定义如下:
      具体依赖——如果P层中有一个或一个以上的地方实例化了Q层中某个具体类,则说P层具体依赖于Q层。
      抽象依赖——如果P层没有实例化Q层中的具体类,而是在一个或一个以上的地方实例化了Q层中某个接口,则说P层抽象依赖于Q层,也叫接口依赖于Q层。
      从这两个定义可以看到,所谓的依赖倒置原则,正是上面提到针对接口编程,而不是针对实现编程,两者在本质上是统一的。
      综上所述,可以看出,本课题设计的分层架构,应该是这样一种架构:
      1.N层架构的各层依次编号为1、2、…、K、…、N-1、N,其中层的编号越大,则越处在上层。
      2.架构中仅存在一种依赖,即第K层接口依赖第K-1层,其中1

      封装变化原则
      封装变化的原则定义为:找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混杂在一起。

      开放-关闭原则
      开发-关闭原则定义为:对扩展开放,对修改关闭。
      具体到N层架构中,可以描述为:当某一层有了一个新的具体实现时,它应该可以在不修改其他层的情况下,与此新实现无缝连接,顺利交互。

      单一归属原则
      在这个架构中,任何一个操作类都应该有单一的职责,属于单独的一层,而不能同时担负两种职责或属于多个层次(实体类及辅助类可以被多个层使用,但它们不属于任何一个层,而是独立存在)。

层次划分:
      目前,典型的分层架构是三层架构,即自底向上依次是数据访问层、业务逻辑层和表示层。
      这种经典架构经历了时间的考验和实践的多次检验,被认为是合理、有效的分层设计,所以,在本文中,将沿袭这种经典架构,使用数据访问层、业务逻辑层和表示层的三层架构体系。

职责划分:
      目前,在典型的三层架构中,对层次各自的职责划分并没有一个统一的规范,综合现有的成功实践和.NET平台的特殊性,在本文中将三层架构的职责划分如下:
      数据访问层——负责与数据源的交互,即数据的插入、删除、修改以及从数据库中读出数据等操作。对数据的正确性和有效性不负责,对数据的用途不了解,不负担任何业务逻辑。
      业务逻辑层——负责系统领域业务的处理,负责逻辑性数据的生成、处理及转换。对流入的逻辑性数据的正确性及有效性负责,对流出的逻辑性数据及用户性数据不负责,对数据的呈现样式不负责。
      表示层——负责接收用户的输入、将输出呈现给用户以及访问安全性验证。对流入的数据的正确性和有效性负责,对呈现样式负责,对流出的数据正确性不负责,但负责在数据不正确时给出相应的异常信息。

模块划分及交互设计:
      综合以上分析,可在宏观上将整个系统分为一下几个模块:
      实体类模块——一组实体类的集合,负责整个系统中数据的封装及传递。
      数据访问层接口族——一组接口的集合,表示数据访问层的接口。
      业务逻辑层接口族——一组接口的集合,表示业务逻辑层的接口。
      数据访问层模块——一组类的集合,完成数据访问层的具体功能,实现数据访问层接口族。
      业务逻辑层模块——一组类的集合,完成业务逻辑层的具体功能,实现业务逻辑层接口族。
      表示层模块——程序及可视元素的集合,负责完成表示层的具体功能。
      IoC容器模块——负责依赖注入的实现。
      辅助类模块——完成全局辅助性功能。

      各模块见交互关系如下:
 

 
      这篇中理论比较多,但是它是整个架构的基础,可以帮助朋友们对将要实现的项目架构及要遵循的原则有一个整体的了解。当然,在后续文章中,将主要讨论Demo项目的实际开发过程,那时,这些思想和理论性的东西将得到体现。

 

 

基于.NET平台的分层架构实战(四)——实体类的设计与实现

    实体类是现实实体在计算机中的表示。它贯穿于整个架构,负担着在各层次及模块间传递数据的职责。一般来说,实体类可以分为“贫血实体类”和“充血实体类”,前者仅仅保存实体的属性,而后者还包含一些实体间的关系与逻辑。我们在这个Demo中用的实体类将是“贫血实体类”。

      大多情况下,实体类和数据库中的表(这里指实体表,不包括表示多对多对应的关系表)是一一对应的,但这并不是一个限制,在复杂的数据库设计中,有可能出现一个实体类对应多个表,或者交叉对应的情况。在本文的Demo中,实体类和表是一一对应的,并且实体类中的属性和表中的字段也是对应的。

      在看实体类的代码前,先看一下系统的工程结构。
 


 

      如上图所示,在初始阶段,整个系统包括6个工程,它们的职责是这样的:
      Web——表示层
      Entity——存放实体类
      Factory——存放和依赖注入及IoC相关的类
      IBLL——存放业务逻辑层接口族
      IDAL——存放数据访问层接口族
      Utility——存放各种工具类及辅助类

      这只是一个初期架构,主要是将整个系统搭一个框架,在后续开发中,将会有其他工程被陆陆续续添加进来。

      我们的实体类将放在Entity工程下,这里包括三个文件:AdminInfo.cs,MessageInfo.cs,CommentInfo.cs,分别是管理员实体类、留言实体类和评论实体类。具体代码如下:

AdminInfo.cs:
 

AdminInfo


MessageInfo.cs:
 

MessageInfo


CommentInfo.cs:
 

CommentInfo


      大家可以看出,实体类的代码很简单,仅仅是负责实体的表示和数据的传递,不包含任何逻辑性内容。下篇将介绍接口的设计

基于.NET平台的分层架构实战(五)——接口的设计与实现

      接下来,将进行接口的设计。这里包括数据访问层接口和业务逻辑层接口。在分层架构中,接口扮演着非常重要的角色,它不但直接决定了各层中的各个操作类需要实现何种操作,而且它明确了各个层次的职责。接口也是系统实现依赖注入机制不可缺少的部分。

      本项目的接口设计将按如下顺序进行:
      1.首先由前文的需求分析,列出主要的UI部分。
      2.分析各个UI需要什么业务逻辑支持,从而确定业务逻辑层接口。
      3.分析业务逻辑层接口需要何种数据访问操作,从而确定数据访问层接口。

      另外,为保证完全的面向对象特性,接口之间的数据传递主要靠实体类或实体类集合,禁止使用DataTable等对象传递数据。

      由需求分析,列出主要UI
      需求分析部分,请参看基于.NET平台的分层架构实战(二)——需求分析与数据库设计 。有需求分析,可以列出系统中主要应包括以下UI:
      UI01——主页面,列出全部的留言及相应评论,支持分页显示。留言按发表时间逆序显示,评论紧跟在相应留言下。管理员可以通过相应链接对留言执行通过验证、删除、回复以及对评论进行删除操作。游客可通过相应连接进入发表留言评论页面。
      UI02——发表留言页面,供游客发表新留言。
      UI03——发表评论页面,供游客发表评论。
      UI04——回复留言页面,供管理员回复留言。
      UI05——管理员登录页面。
      UI06——管理员修改个人密码的页面。
      UI07——超级管理员登录后的页面,主要提供管理员列表。可以通过相应链接将指定管理员删除。
      UI08——添加新管理员的页面。
      UI09——操作成功完成后的跳转提示页面。
      UI10——系统出现异常时显示友好出错信息的页面。

      由UI识别业务逻辑操作
      UI01:按分页取得留言,按指定留言取得全部评论,将指定留言通过验证,将指定留言删除,将指定评论删除
      UI02:添加新留言
      UI03:添加新评论
      UI04:回复留言
      UI05:管理员登录
      UI06:修改管理员密码
      UI07:取得全部管理员信息,删除管理员
      UI08:添加新管理员

      经过整理,可得以下接口操作:
      IAdminBLL:Add(添加管理员),Remove(删除管理员),ChangePassword(修改管理员密码),Login(管理员登录),GetAll(取得全部管理员)
      IMessageBLL:Add(添加留言),Remove(删除留言),Revert(回复留言),Pass(将留言通过验证),GetByPage(按分页取得留言)
      ICommentBLL:Add(添加评论),Remove(删除评论),GetByMessage(按留言取得全部评论)

      这三个接口文件都放在IBLL工程下,具体代码如下:

IAdminBLL.cs:
 

IAdminBLL


IMessageBLL.cs:
 

IMessageBLL


ICommentBLL.cs
 

ICommentBLL


 
      由业务逻辑确定数据访问操作
      IAdminBLL需要的数据访问操作:插入管理员,删除管理员,更新管理员信息,按ID取得管理员信息,按登录名与密码取得管理员,取得全部管理员
      IMessageBLL需要的数据访问操作:插入留言,删除留言,更新留言信息,按ID取得留言信息,按分页取得留言
      ICommentBLL需要的数据访问操作:插入评论,删除评论,按留言取得全部评论
      另外,添加管理员时需要验证是否存在同名管理员,所以需要添加一个“按登录名取得管理员”。

      对以上操作进行整理,的如下接口操作:
      IAdminDAL:Insert,Delete,Update,GetByID,GetByNameAndPassword,GetAll
      IMessageDAL:Insert,Delete,Update,GetByID,GetByPage
      ICommentDAL:Insert,Delete,GetByMessage

      这三个接口文件放在IDAL工程下,具体代码如下:

IAdminDAL.cs:
 

IAdminDAL


IMessageDAL.cs:
 

IMessageDAL


ICommentDAL.cs:
 

ICommentDAL

基于.NET平台的分层架构实战(六)——依赖注入机制及IoC的设计与实现 

    我们设计的分层架构,层与层之间应该是松散耦合的。因为是单向单一调用,所以,这里的“松散耦合”实际是指上层类不能具体依赖于下层类,而应该依赖于下层提供的一个接口。这样,上层类不能直接实例化下层中的类,而只持有接口,至于接口所指变量最终究竟是哪一个类,则由依赖注入机制决定。

      之所以这样做,是为了实现层与层之间的“可替换”式设计,例如,现在需要换一种方式实现数据访问层,只要这个实现遵循了前面定义的数据访问层接口,业务逻辑层和表示层不需要做任何改动,只需要改一下配置文件系统即可正常运行。另外,基于这种结构的系统,还可以实现并行开发。即不同开发人员可以专注于自己的层次,只有接口被定义好了,开发出来的东西就可以无缝连接。

      在J2EE平台上,主要使用Spring框架实现依赖注入。这里,我们将自己做一个依赖注入容器。

      依赖注入的理论基础是Abstract Factory设计模式,这里结合具体实例简单介绍一下。

 

      上图以数据访问层为例,展示了Abstract Factory模式的应用。如图,现假设有针对Access和SQLServer两种数据库的数据访问层,它们都实现了数据访问层接口。每个数据访问层有自己的工厂,所有工厂都实现自IDALFactory接口。而客户类(这里就是业务逻辑层类)仅与工厂接口、数据访问层接口耦合,而与具体类无关,这样,只要通过配置文件确定实例化哪个工厂,就可以得到不同的数据访问层。
 
      然而,这种设计虽然可行,但是代码比较冗余,因为这样需要为数据访问层的每一个实现编写一个工厂,业务逻辑层也一样。在以前,我们毫无办法,但是,.NET平台引入的反射机制,给我们提供了一种解决方案。使用反射,每个层只需要一个工厂,然后通过从配置文件中读出程序集的名称,动态加载相应类。另外,为了提高依赖注入机制的效率,这里引入缓存机制。下面来看具体实现。

      配置
      首先,需要在Web工程的Web.config文件的 节点下添加如下两个项:
     
     
      这两个配置选项分别存储要应用的数据访问和也业务逻辑层的程序集名称。value目前是空,是因为目前还没有各个层次的具体实现。

      实现缓存操作辅助类
      为实现缓存操作,我们将缓存操作封装成一个辅助类,放在Utility工程下,具体代码如下:

CacheAccess.cs:

      封装依赖注入代码
      因为很多依赖注入代码非常相似,为了减少重复性代码,我们将可复用的代码先封装在一个类中。具体代码如下(这个类放在Factory工程下):

DependencyInjector.cs:

CacheAccess

 

DependencyInjector


      实现工厂
      下面使用两个辅助类,实现数据访问层工厂和业务逻辑层工厂。

DALFactory.cs
 

DALFactory


BLLFactory.cs
 

BLLFactory
 
基于.NET平台的分层架构实战(七)—数据访问层的第一种实现:Access+SQL     经过上面篇文章的介绍,整个系统的框架算是基本搭建完了,下面,我们要具体实现各个层次。关于数据访问层的实现,我准备讨论三种实现方式,这一篇文章讨论第一种:Access+动态生成SQL。
      顾名思义,这种实现将使用Access作为后台数据库,而操作方式也是最基本的使用SQL命令。
      在具体编写实现代码之前,我们需要做一些准备工作:

      第一步,我们要将Access数据库搭建完成,具体做法如下。
      在Web工程下新建一个文件夹,命名为AccessData,并在其中新建一个mdb文件(即Access数据库文件),按照前面介绍过的数据库设计构架,将数据表及表间关系建好,这里不再赘述。

      第二步,我们要进行一些配置。
      打开Web工程下的Web.config文件,在其中的appSettings节点下,添加如下键值:
     
     
      第一条为Access的连接字符串,第二条为Access数据库文件的路径,其中“~”表示网站根目录。

      第三步,新建一个工程。
      我们要新建一个工程AccessDAL,用来存放Access数据访问层的代码。

      准备工作做完了,现在来实现具体的代码。

      1.编写数据访问助手类
      因为很多数据访问操作流程很相似,所以,这里将一些可复用的代码抽取出来,编写成助手类,以此减少代码量,提高代码复用性。
      这个助手类放在AccessDAL下,叫AccessDALHelper,主要负责Access数据库的访问。它包括三个方法:
      GetConnectionString:从配置文件中读取配置项,组合成连接字符串。
      ExecuteSQLNonQuery:执行指定SQL语句,不返回任何值,一般用于Insert,Delete,Update命令。
      ExecuteSQLDataReader:执行SQL语句返回查询结果,一般用于Select命令。
      具体代码如下:

AccessDALHelper.cs:

      2.实现具体的数据访问操作类
      因为前面已经定义了数据访问层接口,所以实现数据访问操作类就是很机械的工作了。下面仅以Admin的数据访问操作类为例:

AdminDAL:
AccessDALHelper

 

AdminDAL


      可以看到,这里主要包括三种类型的操作,一种是修改型,如Insert;一种是返回单个实体类型,如GetByID;还有一种是返回实体类集合型,如GetAll。
      MessageDAL和CommentDAL的实现非常相似,在这里不再赘述。

基于.NET平台的分层架构实战(八)—数据访问层的第二种实现:SQLServer+存储过程

     在上一篇中,讨论了使用SQL构建数据访问层的方法,并且针对的是Access数据库。而这一篇中,将要创建一个针对SQLServer数据库的数据访问层,并且配合存储过程实现。

      曾经有朋友问我使用SQL和存储过程在效率上的差别,惭愧的是我对这方面没有研究,也没有实际做过测试。通过查阅资料,发现在一般情况下,存储过程的效率由于使用SQL,但是也不绝对,也发现有的朋友测试时发现在特定情况下SQL的效率优于存储过程,所以这个问题不能一概而论。

      好,废话不多说,这里先列出使用存储过程构建数据访问层的一般步骤:
      1.创建新工程
      2.创建数据库
      3.编写相应存储过程
      4.编写数据库辅助类
      5.实现数据访问层

创建新工程
      在开始所有开发工作前,我们需要在解决方案下新建一个工程,叫SQLServerDAL,用于存放所有SQLServer数据访问层的代码。

创建数据库
      首先,我们要根据前文设计的数据库,在SQLServer中创建相应的数据库及数据表。我使用的是SQLServer2005,使用企业管理器创建,创建方法不再赘述。

编写存储过程
      数据库创建完成后,我们就要编写存储过程了。由于数据访问层接口已经确定,所以需要哪些存储过程也很好确定。例如数据访问层接口中有一个添加管理员方法,那么就一定有一个存储过程实现这个功能。
      还是以管理员模块为例,经过简单分析,需要一下存储过程:

      插入管理员记录
      删除管理员记录
      更新管理员信息
      按ID取得管理员记录
      按用户名及密码取得管理员记录
      按用户名取得管理员记录
      取得全部管理员记录

      创建这些存储过程的SQL代码如下:
 

插入管理员记录


 

删除管理员记录


 

修改管理员信息


 

按ID取得管理员


 

按用户名和密码取得管理员


 

按用户名取得管理员


 

取得全部管理员信息


编写数据库辅助类
      由于访问数据库的代码很相似,这里我们仍需要编写一个数据库辅助类,来将常用代码封装起来,方便复用。虽然在这里只使用到了存储过程,但是为了扩展性考虑,这个数据库辅助类仍然包含了通过SQL访问数据库的方法。具体实现如下:

SQLServerDALHelper.cs:
 

SQLServerDALHelper


实现数据访问层
      最后仍以管理员模块为例,看一下具体数据访问层的实现。

AdminDAL.cs:
 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值