Artizan-MsOnAbp:一个基于 ABP 的微服架构,开箱即用

artizan-abp 系列目录

Artizan-Abp: Abp 扩展库
Artizan-MsOnAbp:一个基于 ABP 的微服架构,开箱即用
Artizan-MsOnAbp:将其自定义成 .Net 项目模板

视频:https://www.bilibili.com/video/BV13D421s7p1

售价
¥198,包含源码:

  • Artizan-Abp: Abp 扩展库
  • Artizan-MsOnAbp: 一个基于 ABP 的微服架构,开箱即用

需要者可私信,并说明来意。

简介

一个基于 ABP 的微服架构,开箱即用。

1688055634191

运行条件

运行环境

运行前先安装:

开发工具

可以选择自己喜欢的IDE:

  • Visual Studio
  • Visual Code

依赖类库 artizan-abp

解决方案 artizan-abp-microservice 引用了 artizan-abp 类库。

引用依赖类库 artizan-abp的方式有:

  • 通过项目源码引用
  • 通过 Nuget (官方或私有)引用

解决方案 artizan-abp-microservice 与依赖类库 artizan-abp 源码的目录层级关系如下所示:

artizan-abp
├── framework     # 框架
├── modules       # 主目录
│   ├── account   # account 模块
│   ├── identity  # identity 模块
│   ├── ...       # 其它 module
│  
├── build # 所有项目编译、发布脚本
│   │   ├── common.ps1 # 配置脚本,主要配置将要编译、发布的项目目录
│   │   ├── build-all.ps1 # 项目编译脚本
│   │   └── build-all-release.ps1 # 项目发布脚本
│   │   
├── nupkg # nuget 相关文件
│   │   ├── common.ps1 # 配置脚本,主要配置将要打包的项目(根)目录
│   │   ├── pack.ps1 # 脚本:批量打包
│   │   └── push_packages.ps1 # 脚本:批量上传包
│   └── ... 
│   
artizan-abp-projects 
│   ├── microservice # 微服务架构
│   │   ├── apps     # 应用程序
│   │   ├── gateways # 网关
│   │   ├── services # 微服务
│   │   ├── tye.yaml # tye 配置,用于 tye run
│   │   ├── ...
│   │   └── MsOnAbp.sln # 解决方案
│   │
│   ├── n-layered # 分层架构
│   │   ├── ...
│   │
│   ├── single # 单体架构
│   │   ├── ...

引用 artizan-abp 源码

如果解决方案 artizan-abp-microservice 直接引用类库 artizan-abp 源码,得先编译 artizan-abp 源码,

在类库 artizan-abp的目录: artizan-abp\build 中打开 PowerShell,执行如下命令,对 artizan-abp 所有项目进行编译:

artizan-abp\build> .\build-all.ps1

可选参数:-f, (即:full, 编译全部的意思) ,添加该参数后,在编译时将包含一些非必须项目。

更多说明,参见该目录下的 README.md 文件

编译成功后,解决方案 artizan-abp-microservice 有可能还是无法识别类库 artizan-abp 中的项目,出现这种情况,解决办法如下:

MsOnAbp.sln 解决方案所在目录中打开 PowerShell,然后执行如下命令:

> dotnet restore

命令执行完成后,如果还是有些项目无法识别,请到具体出现问题的项目的根目录中执行: dotnet restore 命令。

引用 artizan-abp 的Nuget包

可以将类库 artizan-abp 中的项目发布到官方Nuget或者私有的 Nuget源,然后再通过 Nuget引用。

如何发布 artizan-abpNuget包 ,请参见类库 artizan-abp 的如下文档:

artizan-abp\nupkg\README.md

拓展:
发布到私有 Nuget源示例,参见:BaGet:搭建 Nuget 私服
发布到官方 Nuget 示例,参见:将自己开发的类库上传到 NuGet

在开发阶段,可以先发布到私有Nuget源进行测试,版本稳定后再发布到官方Nuget,这时也伴随一个新的问题出现,即:
同一个 package 存在于多个源中,则会遇到一个 NU1507 的 warning,可以通过 NuGet.Config 文件配置 Package Source Mapping 来指定具体的 package 使用特定的 NuGet 源。
具体的资料参见:https://learn.microsoft.com/zh-cn/nuget/consume-packages/package-source-mapping

特别注意:
当存在多个Nuget源,会根据优先级依次从某个Nuget源下载某个包到全局包目录:
C:\Users\<用户名>\.nuget\packages 中进行缓存,
而Nuget默认规则是:
只要包的版本号相同,不管从哪个Nuget源下载到全局包目录的包,其内容视为一致,即使实际上有可能并不一致,故:
当全局包目录已经存在某个版本号的包,该版本号的包就不会再次下载,即不会被更新替换。
在开发阶段,在修复bug后重新发布包,但不想修改包的版本号,而是以相同的版本号覆盖旧版本,
这时就破坏了Nuget的上述的默认规则,而官方Nuget不支持这种破坏默认规则的做法,就只能发布到私有Nuget源。

综上,当多个Nuget源中某个版本的包的内容不一致时,在切换Nuget源后,全局目录中该版本的包是不会自动更新的,必须手动删除,方法有>>二:
1). 打开全局包目录 C:\Users<用户名>.nuget\packages,手动删除要切换包对应的文件夹;
2). 或者运行 dotnet nuget locals global-packages -c 以清除全局包目录。

接着编译项目(或 dotnet restore),
最后查看目标包文件夹下的【.nupkg.metadata】文件中的source 字段,该字段标明包的Nuget源(下载)来源,以此来判断切换Nuget源是否成功。

在整个解决方案的根目录下创建一个名为 NuGet.Config 的文件(如果已存在可以直接打开),然后添加以下内容:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
	<!--
	package-source-mapping 参见:https://learn.microsoft.com/zh-cn/nuget/consume-packages/package-source-mapping 

	特别注意:
	当多个Nuget源中某个版本的包的内容不一致时,在切换Nuget源后,全局目录中的包是不会自动更新的,必须手动删除,方法有二
		1. 打开全局包目录 C:\Users\<用户名>\.nuget\packages,手动删除要切换包对应的文件夹;
		2. 或者运行 dotnet nuget locals global-packages -c 以清除全局包目录。
	
	接着编译项目,
    最后查看目标包文件夹下的【.nupkg.metadata】文件中的source 字段,该字段标明包的Nuget源(下载)来源,以此来判断切换Nuget源是否成功。
	-->
	
	<packageSources>
	    <clear />
		<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
		<add key="PrivateNuget" value="http://localhost:5656/v3/index.json" />
	</packageSources>
	<packageSourceMapping>
	    <packageSource key="nuget.org">
      		<package pattern="*" />
    	</packageSource>
		<packageSource key="PrivateNuget">
			<package pattern="Artizan.Abp.*" />
		</packageSource>
	</packageSourceMapping>
</configuration>

配置解析:

  • packageSources 部分定义了两个 NuGet 源:
    名为 “nuget.org” 的官方 Nuget 源https://api.nuget.org/v3/index.json
    名为 “PrivateNuget” 的私有 NuGet 源:http://localhost:5656/v3/index.json

  • packageSourceMapping 部分定义了如何根据包的名称模式来指定其应该从哪个源获取:
    对于名为 “PrivateNuget” 的源:只有符合模式 “Artizan.Abp.*” 的包会从私有 NuGet 源 http://localhost:5656/v3/index.json 中获取。

  • 其他未明确指定的包仍会按照 packageSources 中定义的顺序从源中获取,可能是 NuGet 官方源或其他定义的源。

通过这样的配置,可以灵活地指定特定模式的包应该从哪个 NuGet 源获取,同时保留默认的包源获取行为。这种配置适用于需要定制化地管理不同包的来源,并且需要准确定义获取逻辑的情况。

当所依赖的特定版本的 Artizan.Abp 类库已发布到官方 Nuget 源 并决定使用官方 Nuget 源 源时,通过注释掉记<packageSource key="PrivateNuget> 节点来切换包源,如下配置所示:

    ......
	<packageSourceMapping>
	    <packageSource key="nuget.org">
      		<package pattern="*" />
    	</packageSource>
		<!-- <packageSource key="PrivateNuget">
			<package pattern="Artizan.Abp.*" />
		</packageSource> -->
	</packageSourceMapping>

按照上述换了 Nuget 源,有可能会因为缓存,实际引用的包不会改变,这时需要确定包的来源,右键要更换Nuget包,选择【属性】

在属性栏中,找到包的存放地址:

C:\Users\wei\.nuget\packages\artizan.abp.openiddict.application.contracts\8.1.0

不管是从官方Nuget 或者私有Nuget 都被复制和缓存到这个全局包的存储位置,查看文件【.nupkg.metadata】的内容:

{
  "version": 2,
  "contentHash": "hjgilM0rlH2dTuC/eONfcRNo5bKSG21Kzc7Q7Oev0+w0k6mm2O0kRJYViob71Spn4sAJd8Y7u+/XW5hwKeb3Qw==",
  "source": "http://localhost:5656/v3/index.json"
}

source 字段表示该包的Nuget源来源,以此来判断切换Nuget源是否成功,如果切换Nuget源不成功,请删除该包对应的文件夹:

或者执行如下命令,清除全局包:

 dotnet nuget locals global-packages -c
正在清除 NuGet 全局包文件夹: C:\Users\wei\.nuget\packages\
本地资源已部分清除。

这时,该文件夹位置下的所有全局包会被删除,如下图所示:

如何运行?

编译项目

在运行解决方案 MsOnAbp.sln 前,必须先编译其所依赖的项目。为此可以通过执行脚本来完成这项任务。

在文件夹 build-dev 中打开 PowerShell, 然后执行如下命令,编译所有项目:

build-dev>  .\build-all.ps1

更多说明,参见文档: build-dev/README.md

后台项目

后台项目包括:

  • 认证授权服务: auth-server

  • 内部网关:web-gateway

  • 外部网关: web-public-gateway

  • 微服务: administration-service、identity-service 等

要运行所有后台项目,有如下几种方式:

  • Visual Studio
  • Tye

Visual Studio

使用 Visual Studio 运行 MsOnAbp的方法如下:

使用 Visual Studio 打开 MsOnAbp.sln 解决方案 ,鼠标右键单击 MsOnAbp.sln解决方案,选择【属性】菜单项,在弹出的属性页弹框中,选择【多个启动项目(M)】,然后选择要启动的项目,注意调整项目启动的顺序,如下图所示:

设置完成后,点击【确定】按钮。

然后在工具栏中,点击【启动】按钮,即可同时启动多个项目。

Tye

使用 Tye 运行 MsOnAbp 的方法如下:

打开 PowerShell, 切换到解决方案 MsOnAbp.sln 所在目录,然后执行如下命令运行脚本:

> .\run-tye.ps1

注意:

1.你需要在你的电脑中安装 Tye ,可以通过如下执行命令安装 Tye:

dotnet tool install -g Microsoft.Tye

如果安装失败,要求指定版本,请打开如下网站:

https://www.nuget.org/packages/Microsoft.Tye

查看最新版本,然后在安装时指定版本:

dotnet tool install --global Microsoft.Tye --version 0.11.0-alpha.22111.1

2.Tye 的配置文件名为:tye.yaml, 位于解决方案 MsOnAbp.sln 所在目录,如果有新的项目要加入启动列表,记得在该文件中添加相关配置。

3.更多详情, 参见:
https://docs.abp.io/en/commercial/latest/startup-templates/microservice/tye-integration

比如:如何结合Type + VS 进行 Debug

# You can debug my-service by attaching to a process
tye run --debug my-service

# If you want to attach a debugger to all of your services
tye run --debug *

In Visual Studio go to menu Debug -> Attach to Process then select the > service process you are debugging.

上述命令执行完成后,查看输出结果:

......
[00:30:17 INF] Dashboard running on http://127.0.0.1:8000
[00:30:17 INF] Build Watcher: Watching for builds...
[00:30:17 INF] Building projects
......

从上述输出结果可以看到,Tye 的仪表盘地址:http://localhost:8000 ,使用浏览器访问该地址,你会看到所有的项目都已经运行,如下图所示:

数据库迁移

数据库迁移有以下两种方式:

  • Auto Migration (On-the-fly Migration):微服务自己动态迁移(推荐)

  • Migration 控制台项目:统一执行所有微服务的数据库迁移(从8.0 开始,废弃并不再维护)

注意
以上两种运行方式都默认采用了 Auto Migration (On-the-fly Migration),即微服务自己动态迁移,所以不需要再运行Migration 控制台项目。

Auto Migration (On-the-fly Migration)

Auto Migration (On-the-fly Migration)是指:每个微服务(启动后)自己动态地执行数据迁移,包括:

  • 自动进行数据库架构迁移,例如:修改表结构等

  • 自动插入种子数据

下面以微服务 AdministrationService 为例,说明 Auto Migration (On-the-fly Migration) 实现原理:

在微服 Abp Moudle 类的 OnPostApplicationInitializationAsync() 方法调用数据迁移检查者(即:AdministrationServiceDatabaseMigrationChecker 类)的CheckAndApplyDatabaseMigrationsAsync()方法,如下代码所示:

public class AdministrationServiceHttpApiHostModule : AbpModule
{
    ...
    public override async Task OnPostApplicationInitializationAsync(ApplicationInitializationContext context)
    {
        var env = context.GetEnvironment();

        //if (!env.IsDevelopment())
        {
            using (var scope = context.ServiceProvider.CreateScope())
            {
                await scope.ServiceProvider
                    .GetRequiredService<AdministrationServiceDatabaseMigrationChecker>()
                    .CheckAndApplyDatabaseMigrationsAsync();
            }
        }
    }
}

而该方法内容如下:

public class AdministrationServiceDatabaseMigrationChecker 
    : PendingEfCoreMigrationsChecker<AdministrationServiceDbContext>
{ 
      private readonly AdministrationServiceDataSeeder _administrationServiceDataSeeder;    
public override async Task CheckAndApplyDatabaseMigrationsAsync()
    {
        await base.CheckAndApplyDatabaseMigrationsAsync(); 

        await TryAsync(async () => await SeedAsync());
    }

    private async Task SeedAsync()
    {
        await _administrationServiceDataSeeder.SeedAsync();
    }
  • 调用基类:await base.CheckAndApplyDatabaseMigrationsAsync();方法进行数据库(架构)的迁移。

  • 调用 await TryAsync(async () => await SeedAsync()); 插入种子数据。

就在这样,微服务启动成功后,将自动进行数据迁移, 即所谓的 Auto Migration (On-the-fly Migration)

Database Migration System for EF Core

Abp 7.4 版本提供了 Database Migration System for EF Core ,请参见:
https://github.com/abpframework/abp/blob/fd419a0477f3fb58f9e2ef586c0f7066c99266ab/docs/en/Blog-Posts/2023-08-15 v7_4_Preview/POST.md?plain=1#L97
【Database Migration System for EF Core】 一节。

In this version, ABP Framework provides base classes and events to migrate the database schema and seed the database on application startup. This system works compatibly with multi-tenancy and whenever a new > tenant is created or a tenant's database connection string has been updated, it checks and applies database migrations for the new tenant state.
This system is especially useful to migrate databases for microservices. In this way, when you deploy a new version of a microservice, you don't need to manually migrate its database.
You need to take the following actions to use the database migration system:

  • Create a class that derives from EfCoreRuntimeDatabaseMigratorBase class, override and implement its SeedAsync method. And lastly, execute the CheckAndApplyDatabaseMigrationsAsync method of your > > > class in the OnPostApplicationInitializationAsync method of your module class.
  • Create a class that derives from DatabaseMigrationEventHandlerBase class, override and implement its SeedAsync method. Then, whenever a new tenant is created or a tenant's connection string is changed > then the SeedAsync method will be executed.

Migration 控制台项目

打开 PowerShell,切换到 MsOnAbp.DbMigrator 项目根目录(即:.csproj 所在的目录),

执行如下命令运行控制台程序,

../MsOnAbp.DbMigrator> dotnet run

程序运行成功后,即完成所有微服务的数据库迁移。

两种迁移并存

以上两种数据库迁移方式可以选其一,也可以两者并存,如果要两者并存,需要进行一些代码同步,具体参见 Abp 商业版数据库迁移文档

NOTE

If you want to use only Auto Migration (On-the-fly-Migration), it is safe to delete this project completely.
如果您只想使用自动迁移(动态迁移),则完全删除项目(DbMigrator 控制台项目)是安全的。

if you decide to use both DbMigrator and Auto Migration approaches,
you need to keep duplicate dataseeder files;
one under your microservice DbMigrations folder for auto migration
and the other under the shared DbMigrator project. You need to keep both of your data seeder files synchronized.
如果您决定同时使用DbMigrator和自动迁移方法,您需要保留重复的dataseeder文件;

  • 一个在用于自动迁移的微服务DbMigrations文件夹下,
  • 另一个在共享的DbMigrator项目下。您需要保持两个数据种子文件同步。

注意:

从8.0 开始,打算不再维护【MsOnAbp.DbMigrator】控制台项目,因为在微服架构中,推荐微服自己在启动时进行数据迁移,有以下理由:

  • MsOnAbp.DbMigrator 控制台项目运行将为所有数据库进行迁移,有时我们只需要更新某个微服务,如果此时为所有的微服进行数据迁移,显然是不允许的,同时也违背了微服务自治原则

  • 在生产环境执行【MsOnAbp.DbMigrator】控制台程序,显然有点麻烦, 原因如下:
    生产环境的数据库(端口)往往并不对外网开放,要在自己本地电脑运行【MsOnAbp.DbMigrator】控制台程序链接生产环境数据库,需要开放特殊的权限,不方便。
    如果要在生产环境中运行该控制台程序,微服务要是很多,就特别繁琐。

应用程序

backend-admin

这是后台管理应用程序,基于 vue的纯前端项目,其技术栈主要有:

  • Vue3
  • TypeScript
  • Tailwind css
  • Vite

那如何运行项目呢?

在项目根目录(即:package.json 文件所在目录)中打开 Powershell ,然后执行如下命令:

# 安装依赖
> yarn

# 运行开发环境
> yarn dev

# 生产环境项目打包
> yarn build

注意

1.也可以使用 npm、pnpm 等;

2.如果无法运行,具体参看该项目的相关文档(如:README.md 等)。

其它

clean.bat

双击 clean.bat,可以自动清除所有项目的如下文件夹:

  • bin
  • obj
  • node_modules

开发进度

后台框架

完成进度如下:

  • [✔] 微服务:AdministrationService
  • [✔] 微服务:IdentityService
  • [✔] 内部网关
  • [✔] 外部网关
  • [✔] 认证服务:AuthServer (基于OpenIddict)

artizan-vue-admin

开发进度

完成进度如下:

  • [✔] 用户名/邮箱登录

  • [✔] Vue 的状态管理:基于pinia 的 abpStore

  • [✔] 本地存储:基于localStorage 的 abpStorage

  • [✔] 本地化:hook:useLocalization

  • [✔] 权限:hook:useAuthorization

  • [✔] 路由:登录身份认证守卫、权限守卫

  • [✔] 菜单:根据权限生成菜单

  • [✔] 角色管理:

    • [✔ ] CURD
    • [✔] 权限
  • [✔] 用户管理:

    • [✔] CURD
    • [✔] 权限
    • [✔] 密码重置
    • [✔] 锁定/解锁
  • [✔] OpenIddict:

    • 范围(Scope):
      • [✔] 查询页
      • [✘] URD
    • 应用程序(Application):
      • [✔] 查询页
      • [✘] URD
    • 授权(Authorization):
      • [✔] 查询页
      • [✘] URD
    • Token:
      • [✔] 查询页
      • [✘] URD
  • [✔] 个人设置:

    • [✔] 个人信息
    • [✔] 修改密码
  • [✔] 设置管理:

    • [✔] 账号设置
    • [✔] 邮箱设置
  • [✔] 账号管理:

    • [✔] 用户名/邮箱注册
    • [✔] 邮箱确认
    • [✘] 手机注册
    • [✘] 手机确认
    • [✔] 忘记密码
  • [✘] 第三方登录:

    • [✘] 微信公众号
    • [✘] 微信小程序
    • [✘] QQ登录

持续开发中....

预览

布局

登录

支持当 token过期后,使用 refresh_token 自动刷新 token, 如下代码片段所示:

export class AbpAxiosHelper {
    /**
     * 创建 Axios 请求拦截器
     * @param req
     * @param options
     * @returns
     */
    public static createRequestInterceptors = async(req: InternalAxiosRequestConfig<any>, options?: AbpAxiosRequestOptions) => {
        const headers = req.headers;

        //-------------ABP 本地化-------------
        const abpLocale = abpStorage.getItem(abpStorageKeys.ABP_Locale)??"";
        // 追加 ABP 本地化标识,以获取本地化资源
        headers["accept-language"] = `${req.headers["accept-language"]??""} ${abpLocale}`

        //-------------Token-------------
        const token = abpStorage.getItem(abpStorageKeys.ABP_Token) as AbpToken;
        if(options?.needAuth) {
            if (!token) { // 没有 token
                showGoToLoginToast(options);
            }

            if(token?.access_token) {
                const isTokenExpired = TokenHelper.isTokenExpired(token.expires_at)

                if(isTokenExpired) { // token 过期
                    if (options.refreshTokenCallback) {
                        const refresh_token_succeed = await options.refreshTokenCallback();
                        if (!refresh_token_succeed) {
                            showGoToLoginToast(options);
                        }
                    }
                }
            }
        }
        headers['Authorization'] = `${token?.token_type??"bearer"} ${token?.access_token??""}`;

       return req;
    }
}

响应式布局:

仪表盘

多语言

头像

角色管理

查询

操作:

添加

编辑

权限

响应式:

删除

用户管理

查询

操作:

添加

用户信息:

角色:

编辑

用户信息

角色

权限


响应式:

设置密码

锁定

锁定后,出现锁定小图标

删除

OpenIddict

范围(Scope)

查询页

应用程序(Application)

查询页

授权(Authorization):

查询页

Token

查询页

个人中心

个人信息

响应式布局

修改密码

双因素

设置

身份认证管理

账户

邮件

  • 设置

  • 测试邮件

注册

注册成功

不开放注册


跳转到注册页,显示不开放注册:

忘记密码

发送密码重置邮件

Electron 版本

使用 vite-plugin-electron , 可以将后台管理 BackenAdmin 改造为 Electron 版本。

1688055634191

posted @ 2023-06-30 00:28  easy5  阅读(166)  评论(0编辑  收藏  举报