建议大家研究一下ABP预构建模块源代,了解它们是如何在应用中构建和使用的,因为这样你可以看到模块化开发的实现细节。 本节探讨的支付模块,是一个简单而又真实的案例,特别是当你的应用想要升级到高级帐户时,您可以使用它来接收付款。 这里不会展示该模块的开发步骤,只研究基本要点,以便您了解模块结构并构建自己的模块。 ## 创建新应用模块 我们使用 ABP CLI 命令行工具创建新解决方案: ``` abp new Payment -t module ``` `-t module` 指定创建的是模块,模块名是`Payment`。打开解决方案,如下图所示: ![](https://img.kancloud.cn/be/65/be65ed86c0ed55f2dc22f268553aff0a_634x624.png) 启动模板有太多的项目,因为它支持多个UI和数据库选项,并且包含一些测试/演示项目,我们需要做筛选,删除不必要的项目: 我们把`src`文件夹中需要的项目留下来,然后删除以下项目: * 删除了Blazor.\*的项目,因为采用的是MVC/Razor页面。 * 删除了与MongoDB相关的项目,因为这里使用的是EF Core模块。 * 最后,删除了angular文件夹,因为不希望这个模块有angular UI。 清理之后,解决方案中有12个项目。其中4个用于单元和集成测试,剩下8个主模块。 ![](https://img.kancloud.cn/88/c9/88c97b0e0fe6f8c5902a8ab508a4dace_634x599.png) 这8个项目是类库项目,因此不能单独运行。它们需要由可执行应用程序使用,例如EventHub。 重新整理后的支付模块的结构和层次已在第9章DDD部分介绍过了,不再重复。 下面我们将进一步演化这种结构,因为我们希望为支付模块提供多个应用层。 ## 重组支付模块解决方案 我们将把裁剪过的支付模块安装到EventHub解决方案中。我们回顾第4章,知道EventHub解决方案有两个UI应用程序: ![](https://img.kancloud.cn/0a/5f/0a5fcaea1df884c36359975db63e96ec_302x470.png) * 主站:最终用户用于创建和参加活动的公共网站,这个应用程序有一个MVC/Razor页面UI。 * 后台:系统管理员使用的后台程序。是由Blazor WebAssembly构建。 为了支持相同的架构,我们将为支付模块提供两个UI应用层: * 带有MVC/Razor Pages UI的应用层,由EventHub主站使用。最终用户将使用该UI付款。 * 带有Blazor WebAssembly UI的应用层,由EventHub管理后台使用。管理用户将通过该UI查看付款报告。 下图展示重组后的最终支付解决方案: ![](https://img.kancloud.cn/fe/6e/fe6ea2d4153276dfcd86d4eea70c5e24_426x473.png) admin端,我添加了`Payment.Admin.Application`, `Payment.Admin.Application.Contracts`, `Payment.Admin.Blazor`, `Payment.Admin.HttpApi`, 和`Payment.Admin.HttpApi.Client` 我还加了`Payment.BackgroundServices` 项目定期进行一些后台定时作用。 总体结构:后台应用(Blazor UI)和公共主站应用(MVC/Razor UI)。这两个应用共享相同的领域层和数据库集成代码。 我们了解了支付解决方案的总体结构,下面将介绍付款流程的细节。 ## 支付流程 支付模块的职责是向用户提供支付服务,它在内部使用PayPal作为支付网关。支付模块是通用的,可以被任何类型的应用程序使用。支付模块应该包括一些启动支付流程和处理支付结果的集成逻辑。 EventHub使用付款模块从用户处获得付款,付款后的用户将升级为高级用户。所以高级用户在应用中会拥有更多权限。 如果您是组织的所有者,并访问了组织详细信息页面,您将在页面上看到升级到高级按钮,如下图所示 ![](https://img.kancloud.cn/41/be/41beee922630ae7ed2835331c3746c0e_620x468.png) 单击“升级到高级”按钮时,您将被重定向到定价页面: ![](https://img.kancloud.cn/67/e3/67e344727c9bbd7a374a257194e56301_619x462.png) 在这里,我们可以看到账户类型及其差异。单击此处的“升级到高级”按钮时,我们将被重定向到预结账页面,该页面由支付模块定义: ![](https://img.kancloud.cn/b8/11/b811e05164ab5dd72bc452dd605ac8c6_650x539.png) 预结账页面通常位于支付模块内部,并且开发为独立于应用的页面。我们可以使用`/Payment/PreCheckout?paymentRequestId=3a002186-cb04-eb46-7310-251e45fc6aed`URL将用户重定向到预签出页面 但是,我们应该首先使用IPaymentRequestAppService服务的CreateAsync方法获取支付请求ID。这在`EventHub.Web`项目的`Pages/Pricing.cshtml.cs`页面中完成。 EventHub应用覆写了视图(UI)部分,使其更好地适应EventHub的UI设计。 这是一个在最终应用中自定义模块的示例。EventHub应用在`Pages/Payment`文件夹下定义了`PreCheckout.cshtml` 和`PostCheckout.cshtml`,如下图所示: ![](https://img.kancloud.cn/46/ab/46ab625a289da922adf153f5f3d34182_384x523.png) 它们会自动覆盖相应的支付页面(因为它们完全位于支付模块定义的同一路径中)。这些页面没有`.cshtml.cs`文件,因为我们不想改变页面的逻辑行为,我们只是想改变一下界面展示。 下图显示了支付的主要组件和流程: ![](https://img.kancloud.cn/f7/88/f788a3b8e31ae495fa3125034dff9cc8_624x342.png) * 一旦在付款界面我们点击升级按钮,它会跳转到支付模块的确认页面(Checkout),当我们点击确认按钮,我们将跳转到PayPal。 * 支付模块整合了支付系统,一旦我们在PayPal上完成付款,我们将被重定向回应用的结账后页面,该页面将向用户显示感谢信息。 * 当支付过程成功时,支付模块将发布一个分布式事件`PaymentRequestCompletedEto` (定义 在`Payment.Domain.Shared` 项目中)。 EventHub应用通过`PaymentRequestEventHandler` 类订阅上面的事件(定义在`EventHub.Domain`项目中),它会找到已完成付款的用户和组织,升级组织,并发送电子邮件感谢用户升级帐户。 * 在一些罕见的情况下,从PayPal返回时可能会出现错误,我们无法知道支付过程是否成功。对于这种情况,支付模块提供了一个回调,PayPal会调用该回调来通知我们支付操作的状态。回调请求由`PaymentRequestController`(在`Payment.HttpApi`项目中)处理。如果操作成功,将发布相同的`PaymentRequestCompletedEto`事件,以便EventHub应用可以异步升级组织帐户。 接下来,我们将介绍支付模块如何提供配置选项。 ## 提供配置选项 支付模块使用PayPal,因此必须配置的PayPal帐户信息。它遵循选项模式(详情参阅第5章的实现选项模式),并提供`PayPalOptions`类,如以下示例所示: ``` Configure(options => { options.ClientId = "...";      options.Secret = "..."; }); ``` 我们通常从配置文件(`appsettings.json`)中获取值。付款模块可以由`Payment:PayPal`键获取选项值,如下例所示: ``` "Payment": { "PayPal": { "ClientId": "...", "Secret": "...", "Environment": "Sandbox" } } ``` 在`Payment.Domain`项目的`PaymentDomainModule`类中配置如下: ``` Configure<PayPalOptions>(configuration.GetSection("Payment:PayPal")); ``` 默认,从配置中获取值是一种良好的做法。 我已经介绍了支付模块结构的要点。它的代码与典型的ABP应用没有太大区别。您可以下载源码,了解它的内部工作原理。