Abp 微服务
总结
Abp 隐式保存更改
在 ABP 框架中,调用 _repository.GetListAsync() 方法获取的是一个实体的列表,但是这些实体对象是通过 Entity Framework Core 进行跟踪的。当你在 foreach 循环中修改了这些实体对象的属性值时,EF Core 会自动将这些更改跟踪下来,然后在事务提交时将这些更改保存到数据库中。
因此,即使你没有调用 _repository.UpdateAsync() 方法,EF Core 仍然会将更改保存到数据库中。这种行为称为“隐式保存更改”(Implicit Save Changes)。
如果不需要,应进行 map
AutoMap
1、ApplicationAutoMapperProfile 中 MemberList.Source 要删除
2、ApplicationModule 的 Configure<AbpAutoMapperOptions>配置中 validate: true 要删除
3、AutoMapper IEnumerable 集合属性映射
新增 WaterSupervision 模块
1、在 Gateways 的
1)InternalGateway,无需增加?
2)WebAppGateway 中 appsetting.json 里
{
"DownstreamPathTemplate": "/api/watersupervision/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": \[
{
"Host": "120.79.206.42",
"Port": 8007
}
\],
"UpstreamPathTemplate": "/api/watersupervision/{everything}",
"UpstreamHttpMethod": \[ "Put", "Delete", "Get", "Post" \]
}6、
Redis
CommonService 中的表头数据,字典数据,菜单数据
WaterSupervision 中的项目列表,标段列表,检查列表,
//表头数据
/CommonService/ModelProp/ModelTableHeadersByName
//数据字典
/CommonService/DataDictionary/ByModuleName
//菜单权限
/CommonService/menu/RoleMenus
数据字典前后端调用
1、导入数据字典及字典细项。
2、调用,级连选择应可通过 id 变为 pid 解决。输入字典时,要人为改 id,将其关联。
/api/base/dictDetails/list?name=abc
/api/base/dictDetails/all?Pid=3fa85f64-5717-4562-b3fc-2c963f66afa6
api/CommonService/DataDictionaryDetail/GetAllByName(name,moduleName)
api/CommonService/DataDictionaryDetail/GetAllByPid(pid)3、前端,增加函数,getDictDetailsByName(name)直接引用调取。返回:
{
"items": [
{
"pid": "c6513ff9-90db-e9aa-5553-3a0b0c6388e7",
"label": "a",
"value": "工",
"sort": 1,
"id": "6f2c4247-86fb-20bb-4bbd-3a0b0c63f642"
},
{
"pid": "c6513ff9-90db-e9aa-5553-3a0b0c6388e7",
"label": "b",
"value": "要",
"sort": 2,
"id": "86a8bcc9-d4d7-63cb-b2ca-3a0b0c6433fe"
},
{
"pid": "c6513ff9-90db-e9aa-5553-3a0b0c6388e7",
"label": "c",
"value": "以",
"sort": 3,
"id": "14aba5c7-a6ce-b2c2-4f76-3a0b0c64d8a3"
}
]
}getDictDetailsByName
CommonService.js
:items="options.SelectSupervisingAuthorityInformation"
item-text="label"
?item-title="label"
item-value="value"模块
新建模块增至相应解决方案,此方案存在 abphelper 无法自动生成代码 abp add-module <module-name> [options] abp add-module ShuzeSoft.ProjectModule --new --add-to-solution-file abp add-module ShuzeSoft.EngineeringModule --new --add-to-solution-file abp add-module ShuzeSoft.CommonModule --new --add-to-solution-file
sqlite locked 问题,
在 ShuzeSoftEntityFrameworkCoreModule ConfigureServices 中,完全禁用数据库事务,Add them to ef core module is no problem. the web module is ok too.
Configure<AbpUnitOfWorkDefaultOptions>(options =>
{
options.TransactionBehavior = UnitOfWorkTransactionBehavior.Disabled;
});或
Configure<AbpSecurityLogOptions>(x =>
{
x.IsEnabled = false;
});自动 api
在 host 中,
Configure<AbpAspNetCoreMvcOptions>(options =>
{
options.ConventionalControllers.Create(typeof(ShuzeSoftApplicationModule).Assembly);
});命名约定:
- Get: 如果方法名称以
GetList,GetAll或Get开头. - Put: 如果方法名称以
Put或Update开头. - Delete: 如果方法名称以
Delete或Remove开头. - Post: 如果方法名称以
Create,Add,Insert或Post开头. - Patch: 如果方法名称以
Patch开头. - 其他情况, Post 为 默认方式.
add-migration init -c ShuzeSoftDbContext -Project ShuzeSoft.EntityFrameworkCore add-migration init -c ShuzeSoftCommonDbContext -Project ShuzeSoft.EntityFrameworkCore add-migration init -c ShuzeSoftProjectDbContext -Project ShuzeSoft.EntityFrameworkCore add-migration init -c ShuzeSoftEngineeringDbContext -Project ShuzeSoft.EntityFrameworkCore
Update-Database -Context ShuzeSoftDbContext -Project ShuzeSoft.EntityFrameworkCore Update-Database -Context ShuzeSoftCommonDbContext -Project ShuzeSoft.EntityFrameworkCore Update-Database -Context ShuzeSoftProjectDbContext -Project ShuzeSoft.EntityFrameworkCore Update-Database -Context ShuzeSoftEngineeringDbContext -Project ShuzeSoft.EntityFrameworkCore
https://www.cnblogs.com/xhznl/p/13898971.html 添加 HttpApi.Client 引用 添加 RemoteServices 地址配置 注入服务接口进行使用
elsa
docker pull elsaworkflows/elsa-server-and-studio-v3-3-0-preview:latest
docker run --name elsa -p 8038:8080 -d elsaworkflows/elsa-server-and-studio-v3
abp tools
ABP.cli
安装
#8.0版本还可以免费生成微服务,推荐采用
dotnet tool install -g Volo.Abp.Cli --version 8.0.0新建模块
#新建auth
abp new Shuze -t module --no-ui --database-provider ef -dbms PostgreSQL -csf --connection-string Server=shuze.net;Port=8206;Database=ShuzeAuth;userid=postgres;Password=password; --version 8.0.0
# 新建微服务-模块,不带 ui-推荐
abp new Shuze.DemoService -t module --no-ui --database-provider ef -dbms PostgreSQL -csf --connection-string Server=shuze.net;Port=8206;Database=ShuzeDemo;Username=postgres;Password=password; --version 8.0.0easyabp
easyapi abphelper cli
dotnet tool install EasyAbp.AbpHelper -g
dotnet tool update EasyAbp.AbpHelper -g对应解决方案目录下(自动生成 application 等文件,需有 Shuze.***Service.Installer 目录)
# 生成类对应文件
abphelper generate crud Project --skip-ui
abphelper generate crud --skip-ui.net 改为 abp
1、拆分类,所以单独的类单独为一个文件。
2、类名首字大写修改。
3、在类后面加增加基类: FullAuditedAggregateRoot<Guid>, IMultiTenant, IPermission
4、执行类生成操作,生成模型 abphelper
5、解决 new()不可访问问题,类名加 Dto 问题,只读属性赋值 ,ID 删除问题等。一个文件中只能一个类!
6、map 中增加.ReverseMap()。升级至高版本
在negut中升级abp 增加新模型
1、用 abphelper 根据模型文件生成其它文件,对应解决方案目录下(自动生成 application 等文件),注意模型文件 namespace 命名。
#在domain中,需增加
public class Company : FullAuditedAggregateRoot<Guid>, IMultiTenant, ICascadedId
{}
#对应解决方案目录下,运行以下命令
abphelper generate crud Company --skip-ui2、生成解决方案,修改生成文件的错误。
3、add-migration update-database,生成数据库表
4、增加 table、select Dto 文件,在 I*AppService 中增加方法,修改 PagedAndSortedResultRequestDto 为 GetInputDto。
5、ApplicationAutoMapperProfile 文件中,增加 table select 的 map,删除 MemberList.Source。
6、WaterSupervisionApplicationModule : AbpModule 中将validate: true 删除
options.AddMaps<WaterSupervisionApplicationModule>();//validate: true
增加DependsOn( typeof(CommonServiceEntityFrameworkCoreModule),//ActivatorChain
7、在*AppService.cs 中,修改参数,实现接口。
Details
#region common api
#endregion模型权限
模型权限注册
方法一:不用再在本地的原配套的 authserver 跑一下,才能注册到
方法二:auth 已把 scope 加入,新建 module 后,发布 nuget,在 shuzeservice 中集成,然后到 admin 模块中增加该模块权限,即可权限注册。
方法三(推荐):在 httpapi.host 中,dependson,在前端设置模块权限解决(api/permission-management/permissions),auth 可为 keycloak。
typeof(AbpPermissionManagementDomainIdentityModule),//---
typeof(AbpPermissionManagementApplicationModule),//---
typeof(AbpPermissionManagementHttpApiModule),//---
typeof(AbpIdentityEntityFrameworkCoreModule),//---
typeof(AbpIdentityApplicationModule),//---动态权限
using Volo.Abp.PermissionManagement;//动态权限
Configure<PermissionManagementOptions>(options =>
{
options.IsDynamicPermissionStoreEnabled = true;//动态权限
});api/permission-management/permissions?providerName=R,可查全部权限,动态新增权限亦可。根据 providerName 来区分,"R"是角色权限,"U"是用户权限
再设置权限,应可访问。
权限前端设置方法
1、系统默认提供三类:Client,Role,User,分别用于基于客户端、基于角色、基于用户授权,对应 providerName=C,R,U
providerKey=admin,为空时全查
权限设置
# 在AppService类上方增加一行,可设置所有api的默认权限
[Authorize(WaterSupervisionPermissions.Supervision.Default)]
#新api的函数上方增加一行,设置具体权限名称
[Authorize(WaterSupervisionPermissions.Supervision.Create)]模块引用
CommonService 模块引入 FileStorage 模块示例
1、在 CommonServiceApplicationModule 中引入 Shuze.FileStorage.EntityFrameworkCore、Shuze.FileStorage.Application 的 nuget 包。
2、需加载application及entityframework,否则调用时会现错。
DependsOn
typeof(FileStorageApplicationModule),
typeof(FileStorageEntityFrameworkCoreModule),3、Shuze.CommonService.HttpApi.Host 的 appsettings.json 中增加 FileStorage连接字符串。
4、在 CommonServiceHttpApiHostModule 的 Configure<AbpAspNetCoreMvcOptions>中增加配置,以暴露api
options.ConventionalControllers.Create(typeof(FileStorageApplicationModule).Assembly, opts =>
{
opts.RootPath = "CommonService";
opts.UseV3UrlStyle = true;
});ShuzeService
可将所有模块加载至 shuzeservice 中,则可不用运行各个模块的微服务,可节省内存,也可不用 gate。也可在 nginx 中进行负载均衡配置。
调用 AppService 服务
如 BusinessApplicationModule 要调用 BaseServiceApplicationModule 中的服务,把项目引用至解决方案,在 BusinessApplicationModule 中要加依赖,注意要在 appsetting 中增加对应的ConnectionStrings。
private readonly IRepository<Organization, Guid> _orgRepository;
typeof(BaseServiceEntityFrameworkCoreModule),//IRepository时要加这个,还要ConnectionStrings private readonly IUserAppService _userAppService;
typeof(BaseServiceApplicationModule),// AppService时要加这个未引用,可能出现 ActivatorChain 错误。
RabbitMQ
落地到 ABP
1、配置
在 appsettings.json 中,配置 AbpRabbitMqOptions 即原始 RabbitMq 针对.NET 的连接配置类,配置 AbpRabbitMqEventBusOptions 连接名、队列名即要监听的队列名、交换机名。
"RabbitMQ": {
"Connections": {
"Default": {
"HostName": "shuze.net",//用容器名走内网
"UserName": "admin",
"Password": "password",
"VirtualHost": "vhost",
"Port": 8023
}
},
"EventBus": {
"ClientName": "Shuze-DemoService-Queue",
"ExchangeName": "ShuzeMessagesExchange"
}
},2、安装依赖
在 httpapi.host 中安装依赖 Volo.Abp.EventBus.RabbitMQ。
DependsOn(typeof(AbpEventBusRabbitMqModule))
//以下无需配置,自动
Configure<AbpRabbitMqOptions>(configuration.GetSection("RabbitMQ"));
Configure<AbpRabbitMqEventBusOptions>(configuration.GetSection("RabbitMQ:EventBus"));3、定义生产者,发布
可在.Domain.Shared 层建立 Eto,在 application 层注入 IDistributedEventBus,发布 await _distributedEventBus.PublishAsync(new Eto{});生产者把消息发布到交换机中,消息携带一个 routingKey 属性(默认为空间名称+类名,如 Shuze.DemoService.Books.AddBookEto),交换机会根据 routingKey 的值把消息发送到一个或者多个队列。如可在 update 中加入函数。
public class AddBookEto:EtoBase
{
public string Name { get; set; }
public int Price { get; set; }
}注入 IDistributedEventBus,
private readonly IDistributedEventBus _distributedEventBus;
IDistributedEventBus distributedEventBus,
_distributedEventBus = distributedEventBus;
public async Task<bool> RabbitMqTest()
{
for (int i = 0; i < 1000; i++)
{
await _distributedEventBus.PublishAsync(new AddBookEto { Name = i.ToString(), Price = i, });
}
return true;
}MyHandler 由 ABP 框架自动发现,并在发生 StockCountChangedEto 事件时调用 HandleEventAsync. 如果你使用的是分布式消息代理,比如 RabbitMQ,ABP 会自动订阅消息代理上的事件,获取消息执行处理程序. 如果事件处理程序成功执行(没有抛出任何异常),它将向消息代理发送确认(ACK). 你可以在处理程序注入任何服务来执行所需的逻辑. 一个事件处理程序可以订阅多个事件,但是需要为每个事件实现 IDistributedEventHandler<TEvent> 接口.
4、定义消费者
消费者会从队列中获取消息;订阅:在 applicationn 层建立**EventHandler.cs,Handler 类,注意:消费者的 TitleChangedEto 的 routingKey(类全名)应与生产者类完全一致!如两个服务消费者最好可引用该生产者.Domain.Shared 类,否则连不上。**如调用其它服务***AppService,则需要注入 IPermissionManager 。
public class AddBookHandler : IDistributedEventHandler<AddBookEto>, ITransientDependency
{
private readonly IBookAppService _bookAppService;
public AddBookHandler(IBookAppService bookAppService)
{
_bookAppService = bookAppService;
}
public async Task HandleEventAsync(AddBookEto eventData)
{
var dto = new CreateUpdateBookDto();
dto.Name = eventData.Name;
dto.Price = eventData.Price;
await _bookAppService.CreateAsync(dto);
}
}5、预定义事件
实体自动发布创建,更新和删除分布式事件,EntityCreatedEto<T> 是实体 T 创建后发布,EntityUpdatedEto<T> 是实体 T 更新后发布,EntityDeletedEto<T> 是实体 T 删除后发布.这些都是泛型的, T 实际上是 Event Transfer Object (ETO)的类型,而不是实体的类型,因为实体对象不能做为事件数据传输,所以通常会为实体类定义一个 ETO 类,如为 Product 实体定义 ProductEto.
A:需在模块的 ConfigureServices 中配置 AbpDistributedEntityEventOptions 添加选择器.为 Product 声明使用 ProductEto
Configure<AbpDistributedEntityEventOptions>(options =>
{
//Enable for all entities
options.AutoEventSelectors.AddAll();
//Enable for a single entity
options.AutoEventSelectors.Add<Product>();
options.EtoMappings.Add<Product, ProductEto>();
//Enable for all entities in a namespace (and child namespaces)
options.AutoEventSelectors.AddNamespace("Volo.Abp.Identity");
//Custom predicate expression that should return true to select a type
options.AutoEventSelectors.Add(
type => type.Namespace.StartsWith("MyProject.")
);
});B:配置映射
[AutoMap(typeof(Product))]
public class ProductEto : EntityEto
{
public Guid Id { get; set; }
public string Name { get; set; }
}6、订阅服务,权限问题,
函数前加[AllowAnonymous], [NonAction]
消费端的类和方法不能有任何授权拦截,比如[AbpAuthorize]等。
HandleEventAsync 中调用其它模块时,如有权限会出错,注入后仍无法获取 ICurrentUser,估计是无状态性。调用 Repository 则可避免。
7、各个模型新建或修改时,根据相关规则生成 cascaded 等字段,
问题
通过 docker 安装 rabbitmq 后无法通过 15672 端口登陆管理页面
undefined: There is no template at js/tmpl/login.ejs undefined 这是因为没有开通管理插件,输入以下指令开通,如果是通过 docker 安装的 rabbitmq,要进入到 rabbitmq 容器中执行 rabbitmq-plugins enable rabbitmq_management
基本概念
ABP 框架中的事件总线分为 本地事件总线 和 分布式事件总线 两种,两种使用的方式基本类似,只是分布式事件总线需要借助 RabbitMQ、Kafaka 等第三方消息队列中间件。事件可用于我们发生一个动作时,触发多种处理的操作,以往,我们都是在发生动作的业务逻辑里面写触发事件的调用函数。如果多个地方要触发,就需要多处写,不利于代码的解耦。用了事件,就完全解耦并且是异步。
读写分离,服务之间只读,写由消息总线发布。
访问: http://120.46.156.96:15672/,注意要把端口打开,15672,5672
- A 服务=》B 服务=》C 服务,传输对象 1 与传输对象 2 不能是同一个命名空间下的同一个类名,这是由于 ABP vNext EventBus 底层将完整类名作为路由键进行转发,如果两个对象一致,那么 B 将进入死循环,附带 C 也进入异常
- A、B、C 之间应该共用同一个交换机,即配置的 ExChangeName 一致,而队列名不一致
- 不存在自己指定自己作为生产者的队列的名字,所以必须先启动队列消费者创建了相关队列,队列生产者发出 Message 才有效果,即启动顺序:C=>B=>A
生产者:也可以说是发布者,主要是发布消息,发送给交换器;消费者:也可以说是订阅者,从队列中订阅消息进行处理并返回应答;交换器:可能连接多个队列,将生产者发布的消息发送到队列中;队列:存放生产者产生的消息,供消费者进行订阅处理;
1)信道(Channel):信道是消息的生产者、消费者和服务器进行通信的虚拟连接。TCP 连接的建立是非常消耗资源的,所以 RabbitMQ 在 TCP 连接的基础上构建了虚拟的信道。我们尽量重复使用 TCP 连接,而信道则是可以用完了就关闭。
2)队列(Queue):用来进行消息收发的地方,生产者把消息放到队列中,消费者从队列中获取数据。
3)交换机(exchange):把消息路由到一个或者多个队列中。
4、定义生产者,定义消费者。交换机和队列都位于 RabbitMQ 服务器内部。优点:即使消费者不在线,消费者相关的消息也会被保存到队列中,当消费者上线之后,消费者就可以获取到离线期间错过的消息。
为什么需要消息队列机制? 发布-订阅模式,解耦业务 非必须强一致性的业务场景,借助消息队列剥离到离线处理 各种通知,站内通知、邮件通知、手机短信、微信推送
Swagger
Using Swagger with OAUTH
1、在 authserver 中,注意要增加其它 scope,不然会出错。
"DemoService_Swagger": {
"ClientId": "DemoService_Swagger",
"RootUrl": "https://localhost:8012"
}
EngineeringServiceScopes.AddRange(commonScopes);2、在 demoservice 中,在 appsetting 中,修改 auth 服务器地址,注意 https,无需密码,id 应在 auth 服务器中已预先设定好。因为是采用 auth code 模式,故不能有"SwaggerClientSecret": "1q2w3e*",注意要把该行注释掉。
"AuthServer": {
"Authority": "https://localhost:8001/",
"RequireHttpsMetadata": "false",
"SwaggerClientId": "DemoService_Swagger"
}
app.UseAbpSwaggerUI(options =>
{
options.SwaggerEndpoint("/swagger/v1/swagger.json", "Support APP API");
var configuration = context.GetConfiguration();
options.OAuthClientId(configuration["AuthServer:SwaggerClientId"]);
//options.OAuthClientSecret(configuration["AuthServer:SwaggerClientSecret"]);
options.OAuthScopes("DemoService");
});3、authserver 地址,http,有无“s”要注意。错误:以一种访问权限不允许的方式做了一个访问套接字的尝试,是端口被占了,可能在 iis 或 hyper-v 中该端口被用了。
WaterSupervision
用户设计
一般角色可以按照管理和业务两种维度进行分离,管理角色:一般是企业的运维人员,可以登录系统,进行维护和授权操作。一般通过自定义的方式,可以支持页面上新建。一般一个账号可以同时有多个自定义角色。业务角色:按照职责进行确认,比如财务,保洁员,总经理等。
当新建一个角色时,需要为这个角色进行授权。permission 是一个操作,定义了能够读取哪些资源,可以认为授权的对象是由角色+资源(resource)+ 操作组成。资源是指菜单,功能,操作是通常含义的 CRUD 操作,用 user story 来描述是,这个角色能够对哪些资源做哪些具体的操作。角色与资源之间是多对多的关系。
一个授权的例子:角色是仓库保管员,可以访问仓库页面,并对仓库里的货物进行出入和入库操作。系统管理员为小明创建了一个 xiaoming 的帐号,并给 xiaoming 绑定了仓库保管员的角色。结果就是:小明登录系统后,能够对仓库的货物进行操作了。
RBAC1 模型增加了子角色,引入了继承概念,即子角色可以继承父角色的所有权限。举个例子:所属同一个部门的员工并不应该人人都能查看全部门数据。所以这时我们引入「等级」的概念,部门负责人可查看全部门数据,部门中各小组组长只可查看自己小组数据,而组员,只能看自己的数据。
RBAC 权限模型由三大部分构成,即用户管理、角色管理、权限管理。用户管理按照企业架构或业务线架构来划分,这些结构本身比较清晰,扩展性和可读性都非常好。角色管理一定要在深入理解业务逻辑后再来设计,一般使用各部门真实的角色作为基础,再根据业务逻辑进行扩展。权限管理是前两种管理的再加固,做太细容易太碎片,做太粗又不够安全,这里我们需要根据经验和实际情况来设计。
putlist-----hydrograph 中的 waterlevel 中
部门--对应资源
职位--对应查看范围,
员工--项目组,项目组成员可以查看自己参加的项目情况信息,(只能改自己,可看项目信息)
部门--部门负责,
WaterSupervision 用户体系
1、项目及标段信息生成 UserAndOrgDto 信息,生成用户名、密码、用户类型,角色类型、岗位类型、权限类型。从标段中查项目,再查找建设单位及项目本身,如不在库中,则新增。
2、组织层级最多为 5 层:市水务局--市(区)管项目--下属单位(区水务局)--项目--标段,区级项目上级到区水务局,如果市级项目,则到各管理处及前海或水务集团。组织类型:监管单位、监督单位、建设单位、监理单位、施工单位、标段用户、项目用户。
3、角色类型:建管-汇总用户,建管-项目用户,建管-标段用户(标段用户、项目用户、区级用户、市级用户)。岗位:(汇总用户,项目用户)。数据权限:上级可看下级。权限:建管处、质监站、建设单位、监理单位、施工单位(不同资源及范围所对应的增删查改)
4、userorg 既是组织也是用户,组织名采用项目及标段序号,根据 userandorg 分别生成 org 及 user 数据。用户有 CascadeId 为组织的 CascadeId。
5、标段及项目用户,其中项目及标段用户格式为 XM+年的后两位+3 位序号;用户名为对应标段编号或项目编号,密码为:用户名 md5hash 码(首位大写)+@2022(对应年);项目组用户,用户名为姓全拼名为首字母,密码为首字母大写的用户名+@2022。
事件总线
1、WaterSupervision 中 Project、Section 模块新建数据后,在 Project 及 Section 中增加推送标记(未推送前,不能新建操作),发布 AddProjectUserOrgEto、AddSectionUserOrgEto 事件。AddProjectUserOrgEto 发送项目主管部门 WaterManagementInformationShort 和项目建设单位 OwnerCompanyInformation 确定组织上级 UpperOrganizationNameShort 及其 Id。注意市管简称与 UserAndOrg 中要统一!!!AddSectionUserOrgEto 发送项目编号 ProjectNumber 确定组织上级 UpperOrganizationNameShort 及其 Id。
2、在 CommonService 的 UserAndOrgHandler 中订阅 AddProjectUserOrgEto、AddSectionUserOrgEto 事件,生成 UserAndOrg。
3、订阅 EntityCreatedEto<UserAndOrgEto>事件,UpperOrgNewUserAndOrgHandler 自动找到上级 upperorgid 赋值,OrgNewUserAndOrgHandler 生成 organization,UserNewUserAndOrgHandler 生成 user。其中对已在 UserAndOrg 数据库中的采用在 UserAndOrgAppService 中单独 api 生成,发布 PublishAsync(UserAndOrgEto);OldOrgHandler 及 OldUserHandler 订阅 UserAndOrgEto,生成 org 及 user。
4、在生成 org 及 user 的 Handler 中,创建完后后,再 PublishAsync(OrganizationCreateEto,UserCreateEto),把 userid、orgid 及 CascadeId 传回给 UserAndOrg。订阅 EntityCreatedEto<OrganizationCreateEto、UserCreateEto>事件,NewOrgHandler<OrganizationCreateEto>及 NewUserHandler<UserCreateEto>,UserAndOrg 相应修改 UserId 及 OrganizationId,CascadedId,并增加用户和单位关联关系。
5、在 UserAndOrgHandler 同时发布 SetProjectCascadedIdEto、SetSectionCascadedIdEto。
6、在 WaterSupervision 中 Project、Section 模块中订阅 SetProjectCascadedIdEto、SetSectionCascadedIdEto 事件,将 cascadeid 写回数据库并发布 ChangeSectionCascadedIdEto 事件;其它模块新建数据条目时根据 Project 或 Section 中的 cascadedid,将 cascadedid 同步写入至数据库中。
7、在 Section 模块发布 ChangeSectionCascadedIdEto 事件,隐患、危大、检查等模块订阅 ChangeSectionCascadedIdEto 事件,将 cascadedid 修改。
删除,AbpUsers,AbpUserRoles,Organizations,UserOrganizations,UserAndOrgs。
流程:
在 ProjectAppService 及 SectionAppService,执行 AddProjectHandlerAsync()、AddSectionHandlerAsync(),即可让事件总线跑起来。
userorg
创建项目=》创建用户
创建标段=》创建用户
修改项目所属单位=>修改用户 cascadedid
修改标段所属单位=》修改用户 cascadedid
修改用户 cascadedid=>修改各模块已有 cascadedid
项目及标段生成后
watersupervision 模块发送新建用户消息 mq,commonservice 模块接收消息,并新建一条 UserAndOrg 记录,根据用户名分别生成组织及用户,生成用户时关联用户与组织关系。