怎样优雅地增删查改(二):扩展身份管理模块

身份管理模块(Identity模块)为通用查询接口的按组织架构查询和按户关系查询提供查询依据。

身份管理模块的领域层依赖Volo.Abp.Identity.Domain

在这里插入图片描述

Abp为我们实现了一套身份管理模块,此模块包含用户管理、角色管理、组织管理、权限管理等功能。详细请参考身份管理模块

我们将基于Volo.Abp.Identity模块按需求扩展。将为其扩展组织管理功能的接口,以及人员关系(Relation)功能。

用户关系管理

Relation是人员之间的关系,比如:签约、关注,或者朋友关系等。人员之间的关系是单项的,也就是说可以A是B的好友,但B不一定是A的好友。

关系类型由Type来定义

正向关系:User -> RelatedUser,由查询GetRelatedToUsersAsync实现;

反向关系:RelatedUser -> User,由查询GetRelatedFromUsersAsync实现。

添加Relation实体:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Relation : FullAuditedAggregateRoot<long>
{
public Guid? TenantId { get; set; }

[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public override long Id { get; protected set; }

public Guid UserId { get; set; }

[ForeignKey("UserId")]
public IdentityUser User { get; set; }

public Guid RelatedUserId { get; set; }

[ForeignKey("RelatedUserId")]
public IdentityUser RelatedUser { get; set; }

public string Type { get; set; }

}

在模块配置中添加

1
2
3
4
5
6
7
8
9
10
11
12
public class IdentityEntityFrameworkCoreModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddAbpDbContext<IdentityDbContext>(options =>
{
options.AddRepository<IdentityUserOrganizationUnit, EfCoreRepository<IdentityDbContext, IdentityUserOrganizationUnit>>();
options.AddRepository<Relation.Relation, EfCoreRepository<IdentityDbContext, Relation.Relation>>();
});
}
}

创建RelationManager,实现人员关系的正向和反向查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public async Task<List<Relation>> GetRelatedToUsersAsync(Guid userId, string type)
{
var query = (await Repository.GetQueryableAsync())
.WhereIf(userId != null, c => userId == c.UserId)
.WhereIf(!string.IsNullOrEmpty(type), c => c.Type == type);
var items = query.ToList();
return items;

}

public async Task<List<Relation>> GetRelatedFromUsersAsync(Guid userId, string type)
{
var query = (await Repository.GetQueryableAsync())
.Where(c => userId == c.RelatedUserId)
.WhereIf(!string.IsNullOrEmpty(type), c => c.Type == type);
var items = query.ToList();
return items;
}

扩展组织管理功能

组织(OrganizationUnit)是身份管理模块的核心概念,组织是树形结构,组织之间存在父子关系。

我们对功能模块的接口进行扩展:

  1. 增加OrganizationUnit的增删查改接口;

  2. 增加OrganizationUnit的移动接口;

  3. 增加人员与组织架构管理接口,如添加/删除人员到组织架构,查询组织架构下的人员,查询未分配组织的人员等;

  4. 增加查询根组织(GetRootOrganizationUnit)接口。

完整的应用层接口如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public interface IOrganizationUnitAppService : IBasicCurdAppService<OrganizationUnitDto, Guid, CreateOrganizationUnitInput, UpdateOrganizationUnitInput>, IApplicationService
{
Task AddToOrganizationUnitAsync(UserToOrganizationUnitInput input);
Task<List<OrganizationUnitDto>> GetCurrentOrganizationUnitsAsync();
Task<PagedResultDto<IdentityUserDto>> GetOrganizationUnitUsersByPageAsync(GetOrganizationUnitUsersInput input);
Task<List<IdentityUserDto>> GetOrganizationUnitUsersAsync(GetOrganizationUnitUsersInput input);
Task<OrganizationUnitDto> GetRootOrganizationUnitAsync(Guid id);
Task<List<OrganizationUnitDto>> GetRootOrganizationUnitsAsync(IEnumerable<Guid> ids);
Task<OrganizationUnitDto> GetRootOrganizationUnitByDisplayNameAsync(GetRootOrganizationUnitByDisplayName input);
Task<List<OrganizationUnitDto>> GetRootOrganizationUnitsByParentAsync(GetRootOrganizationUnitsByParentInput input);
Task<bool> IsInOrganizationUnitAsync(UserToOrganizationUnitInput input);
Task MoveOrganizationUnitAsync(MoveOrganizationUnitInput input);
Task RemoveUserFromOrganizationUnitAsync(UserToOrganizationUnitInput input);
Task<List<IdentityUserDto>> GetUsersWithoutOrganizationAsync(GetUserWithoutOrganizationInput input);
Task<PagedResultDto<IdentityUserDto>> GetUsersWithoutOrganizationByPageAsync(GetUserWithoutOrganizationInput input);
}

创建可查询仓储

通用查询接口过滤条件需要对IQueryable进行拼接,由于Volo.Abp.Identity.IIdentityUserRepository继承自IBasicRepository,我们需要重新编写一个IdentityUser的可查询仓储:QueryableIdentityUserRepository

其实现接口IQueryableIdentityUserRepository的定义如下:

1
2
3
4
5
6
7
8
public interface IQueryableIdentityUserRepository : IIdentityUserRepository
{
Task<IQueryable<OrganizationUnit>> GetOrganizationUnitsQueryableAsync(Guid id, bool includeDetails = false);
Task<IQueryable<IdentityUser>> GetOrganizationUnitUsersAsync(
Guid id, string keyword, string[] type,
bool includeDetails = false);
Task<IQueryable<IdentityUser>> GetUsersWithoutOrganizationAsync(string keyword, string[] type);
}

实现控制器

为OrganizationUnitAppService 以及 RelationAppService 创建MVC控制器

完整的 OrganizationUnitController 代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
namespace Matoapp.Identity.OrganizationUnit
{
[Area(IdentityRemoteServiceConsts.ModuleName)]
[RemoteService(Name = IdentityRemoteServiceConsts.RemoteServiceName)]
[Route("api/identity/organizationUnit")]
public class OrganizationUnitController : IdentityController, IOrganizationUnitAppService
{
private readonly IOrganizationUnitAppService _organizationUnitAppService;

public OrganizationUnitController(IOrganizationUnitAppService organizationUnitAppService)
{
_organizationUnitAppService = organizationUnitAppService;
}

[HttpPost]
[Route("AddToOrganizationUnit")]

public async Task AddToOrganizationUnitAsync(UserToOrganizationUnitInput input)
{
await _organizationUnitAppService.AddToOrganizationUnitAsync(input);
}

[HttpPost]
[Route("Create")]

public async Task<OrganizationUnitDto> CreateAsync(CreateOrganizationUnitInput input)
{
return await _organizationUnitAppService.CreateAsync(input);
}

[HttpDelete]
[Route("Delete")]

public async Task DeleteAsync(Guid id)
{
await _organizationUnitAppService.DeleteAsync(id);
}


[HttpGet]
[Route("Get")]

public async Task<OrganizationUnitDto> GetAsync(Guid id)
{
return await _organizationUnitAppService.GetAsync(id);

}

[HttpGet]
[Route("GetCurrentOrganizationUnits")]


public async Task<List<OrganizationUnitDto>> GetCurrentOrganizationUnitsAsync()
{
return await _organizationUnitAppService.GetCurrentOrganizationUnitsAsync();
}


[HttpGet]
[Route("GetOrganizationUnitUsers")]

public async Task<List<IdentityUserDto>> GetOrganizationUnitUsersAsync(GetOrganizationUnitUsersInput input)
{
return await _organizationUnitAppService.GetOrganizationUnitUsersAsync(input);
}

[HttpGet]
[Route("GetOrganizationUnitUsersByPage")]

public async Task<PagedResultDto<IdentityUserDto>> GetOrganizationUnitUsersByPageAsync(GetOrganizationUnitUsersInput input)
{
return await _organizationUnitAppService.GetOrganizationUnitUsersByPageAsync(input);
}

[HttpGet]
[Route("GetRootOrganizationUnit")]

public async Task<OrganizationUnitDto> GetRootOrganizationUnitAsync(Guid id)
{
return await _organizationUnitAppService.GetRootOrganizationUnitAsync(id);
}

[HttpGet]
[Route("GetRootOrganizationUnits")]

public async Task<List<OrganizationUnitDto>> GetRootOrganizationUnitsAsync(IEnumerable<Guid> ids)
{
return await _organizationUnitAppService.GetRootOrganizationUnitsAsync(ids);
}

[HttpGet]
[Route("GetRootOrganizationUnitByDisplayName")]

public async Task<OrganizationUnitDto> GetRootOrganizationUnitByDisplayNameAsync(GetRootOrganizationUnitByDisplayName input)
{
return await _organizationUnitAppService.GetRootOrganizationUnitByDisplayNameAsync(input);
}

[HttpGet]
[Route("GetRootOrganizationUnitsByParent")]

public async Task<List<OrganizationUnitDto>> GetRootOrganizationUnitsByParentAsync(GetRootOrganizationUnitsByParentInput input)
{
return await _organizationUnitAppService.GetRootOrganizationUnitsByParentAsync(input);
}

[HttpGet]
[Route("GetUsersWithoutOrganization")]

public async Task<List<IdentityUserDto>> GetUsersWithoutOrganizationAsync(GetUserWithoutOrganizationInput input)
{
return await _organizationUnitAppService.GetUsersWithoutOrganizationAsync(input);
}

[HttpGet]
[Route("GetUsersWithoutOrganizationByPage")]

public async Task<PagedResultDto<IdentityUserDto>> GetUsersWithoutOrganizationByPageAsync(GetUserWithoutOrganizationInput input)
{
return await _organizationUnitAppService.GetUsersWithoutOrganizationByPageAsync(input);
}

[HttpGet]
[Route("IsInOrganizationUnit")]

public async Task<bool> IsInOrganizationUnitAsync(UserToOrganizationUnitInput input)
{
return await _organizationUnitAppService.IsInOrganizationUnitAsync(input);
}

[HttpPost]
[Route("MoveOrganizationUnit")]

public async Task MoveOrganizationUnitAsync(MoveOrganizationUnitInput input)
{
await _organizationUnitAppService.MoveOrganizationUnitAsync(input);
}

[HttpPost]
[Route("RemoveUserFromOrganizationUnit")]

public async Task RemoveUserFromOrganizationUnitAsync(UserToOrganizationUnitInput input)
{
await _organizationUnitAppService.RemoveUserFromOrganizationUnitAsync(input);
}

[HttpPut]
[Route("Update")]

public async Task<OrganizationUnitDto> UpdateAsync(UpdateOrganizationUnitInput input)
{
return await _organizationUnitAppService.UpdateAsync(input);
}

}

完整的 RelationController 代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
[Area(IdentityRemoteServiceConsts.ModuleName)]
[RemoteService(Name = IdentityRemoteServiceConsts.RemoteServiceName)]
[Route("api/identity/relation")]
public class RelationController : IdentityController, IRelationAppService
{
private readonly IRelationAppService _relationAppService;

public RelationController(IRelationAppService relationAppService)
{
_relationAppService = relationAppService;
}

[HttpDelete]
[Route("ClearAllRelatedFromUsers")]

public async Task ClearAllRelatedFromUsersAsync(GetRelatedUsersInput input)
{
await _relationAppService.ClearAllRelatedFromUsersAsync(input);
}

[HttpDelete]
[Route("ClearAllRelatedToUsers")]

public async Task ClearAllRelatedToUsersAsync(GetRelatedUsersInput input)
{
await _relationAppService.ClearAllRelatedToUsersAsync(input);
}

[HttpPost]
[Route("Create")]

public async Task<RelationDto> CreateAsync(ModifyRelationInput input)
{
return await _relationAppService.CreateAsync(input);
}

[HttpDelete]
[Route("Delete")]

public async Task DeleteAsync(EntityDto<long> input)
{
await _relationAppService.DeleteAsync(input);
}

[HttpDelete]
[Route("DeleteByUserId")]

public async Task DeleteByUserIdAsync(ModifyRelationInput input)
{
await _relationAppService.DeleteByUserIdAsync(input);
}

[HttpGet]
[Route("GetRelatedFromUsers")]

public async Task<List<IdentityUserDto>> GetRelatedFromUsersAsync(GetRelatedUsersInput input)
{
return await _relationAppService.GetRelatedFromUsersAsync(input);
}

[HttpGet]
[Route("GetRelatedToUsers")]

public async Task<List<IdentityUserDto>> GetRelatedToUsersAsync(GetRelatedUsersInput input)
{
return await _relationAppService.GetRelatedToUsersAsync(input);
}

[HttpGet]
[Route("GetRelatedToUserIds")]
public async Task<List<Guid>> GetRelatedToUserIdsAsync(GetRelatedUsersInput input)
{
return await _relationAppService.GetRelatedToUserIdsAsync(input);
}


[HttpGet]
[Route("GetRelatedFromUserIds")]
public async Task<List<Guid>> GetRelatedFromUserIdsAsync(GetRelatedUsersInput input)
{
return await _relationAppService.GetRelatedFromUserIdsAsync(input);
}


}

测试接口

上一章节我们已经将三个模组的依赖添加到MatoappHttpApiModule中,直接启动HttpApi.Host就可以访问接口了。

1
2
3
4
5
6
7
[DependsOn(
...
typeof(CommonHttpApiModule),
typeof(HealthHttpApiModule),
typeof(IdentityHttpApiModule)
)]
public class MatoappHttpApiModule : AbpModule

Relation相关接口:

在这里插入图片描述

OrganizationUnit相关接口:

在这里插入图片描述

下一章节将介绍如何利用Identity模块为用户的查询提供组织架构和用户关系的条件过滤。

怎样优雅地增删查改(二):扩展身份管理模块

https://blog.matoapp.net/posts/bb037ed1/

作者

林晓lx

发布于

2023-07-08

更新于

2024-09-11

许可协议

评论