Skip to content

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,将其关联。

bash
/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)直接引用调取。返回:

json
{
  "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&lt;AbpUnitOfWorkDefaultOptions&gt;(options =&gt;
{
    options.TransactionBehavior = UnitOfWorkTransactionBehavior.Disabled;
});

Configure&lt;AbpSecurityLogOptions&gt;(x =&gt;
{
	x.IsEnabled = false;
});

自动 api

在 host 中,

Configure&lt;AbpAspNetCoreMvcOptions&gt;(options =&gt;
{
   options.ConventionalControllers.Create(typeof(ShuzeSoftApplicationModule).Assembly);
});

命名约定:

  • Get: 如果方法名称以GetList,GetAllGet开头.
  • Put: 如果方法名称以PutUpdate开头.
  • Delete: 如果方法名称以DeleteRemove开头.
  • Post: 如果方法名称以Create,Add,InsertPost开头.
  • 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

安装

bash
#8.0版本还可以免费生成微服务,推荐采用
dotnet tool install -g Volo.Abp.Cli --version 8.0.0

新建模块

bash
#新建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.0

easyabp

easyapi abphelper cli
dotnet tool install EasyAbp.AbpHelper -g
dotnet tool update EasyAbp.AbpHelper -g

对应解决方案目录下(自动生成 application 等文件,需有 Shuze.***Service.Installer 目录)

bash
# 生成类对应文件
abphelper generate crud Project --skip-ui

abphelper generate crud  --skip-ui

.net 改为 abp

bash
1、拆分类,所以单独的类单独为一个文件。
2、类名首字大写修改。
3、在类后面加增加基类: FullAuditedAggregateRoot<Guid>, IMultiTenant, IPermission
4、执行类生成操作,生成模型 abphelper
5、解决 new()不可访问问题,类名加 Dto 问题,只读属性赋值 ,ID 删除问题等。一个文件中只能一个类!
6、map 中增加.ReverseMap()

升级至高版本

在negut中升级

abp 增加新模型

发表于
更新于
字数
阅读量

1、用 abphelper 根据模型文件生成其它文件,对应解决方案目录下(自动生成 application 等文件),注意模型文件 namespace 命名。

bash
#在domain中,需增加
public class Company : FullAuditedAggregateRoot<Guid>, IMultiTenant, ICascadedId
{}
#对应解决方案目录下,运行以下命令
abphelper generate crud Company --skip-ui

2、生成解决方案,修改生成文件的错误。

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
bash
#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&lt;PermissionManagementOptions&gt;(options =&gt;
            {
                options.IsDynamicPermissionStoreEnabled = true;//动态权限
            });

api/permission-management/permissions?providerName=R,可查全部权限,动态新增权限亦可。根据 providerName 来区分,"R"是角色权限,"U"是用户权限

再设置权限,应可访问。

权限前端设置方法

1、系统默认提供三类:Client,Role,User,分别用于基于客户端、基于角色、基于用户授权,对应 providerName=C,R,U

providerKey=admin,为空时全查

权限设置

bash
# 在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 =&gt;
            {
                opts.RootPath = "CommonService";
                opts.UseV3UrlStyle = true;
            });

ShuzeService

可将所有模块加载至 shuzeservice 中,则可不用运行各个模块的微服务,可节省内存,也可不用 gate。也可在 nginx 中进行负载均衡配置。

调用 AppService 服务

如 BusinessApplicationModule 要调用 BaseServiceApplicationModule 中的服务,把项目引用至解决方案,在 BusinessApplicationModule 中要加依赖,注意要在 appsetting 中增加对应的ConnectionStrings

private readonly IRepository&lt;Organization, Guid&gt; _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&lt;AbpRabbitMqOptions&gt;(configuration.GetSection("RabbitMQ"));
Configure&lt;AbpRabbitMqEventBusOptions&gt;(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&lt;Product&gt;();
    options.EtoMappings.Add&lt;Product, ProductEto&gt;();


    //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 =&gt;
        {
            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 记录,根据用户名分别生成组织及用户,生成用户时关联用户与组织关系。