|
前言
我们平时使用的abp框架默认是没有提供组织机构管理的相关页面和功能的。
但是组织机构管理这个功能在某些情况下是刚需,我们需要自定义它。
abp默认的包里面是包含了一部分关于组织机构管理的基础设施,包含:
Domain:OrganizationUnit相关实体定义、领域服务、仓储接口等内容。
Domain.Shared:OrgnaizationUnit相关的枚举常量等内容
EntityFrameworkCore:OrganizationUnit相关的仓储实现等。
由于Abp官方的开源库已经为我们实现了上述的一些基础功能,所以在我们实现自定义的OrganizationUnit的相关页面和功能时,实际上只需要考虑如下几方面的事情就可以了:
1、Application.Contracts,在这里需要定义OrganizationUnit的应用服务接口,各个DTO等。
2、Application,在这里需要实现在Application.Contracts层定义的接口。
3、HttpApi,在这里需要实现OrganizationUnit的Controller(如果需要的话)。
4、Web,在这里需要实现OrganizationUnit的UI设计。(当然如果你是前后端分离的话这块就不用弄了)
5、和OrganizationUnit相关的权限设置。由于OrganizationUnit在Abp里面属于Identity的范畴,所以围绕OrganizationUnit的权限需要处理好。
下面我们针对上面提出的四个方面逐一进行改造。
改造
一、Application.Contracts
首先是IOrganizationUnitAppService接口的定义:
public interface IOrganizationUnitAppService :
ICrudAppService<OrganizationUnitDto,
Guid,
OrganizationUnitGetListInput,
OrganizationUnitCreateDto,
OrganizationUnitUpdateDto>
{
Task<ListResultDto<OrganizationUnitDto>> GetAllListAsync();
Task<OrganizationUnitDto> GetLastChildOrNullAsync(Guid? parentId);
Task MoveAsync(Guid id, OrganizationUnitMoveDto input);
Task<ListResultDto<OrganizationUnitDto>> GetRootAsync();
Task<ListResultDto<OrganizationUnitDto>> FindChildrenAsync(OrganizationUnitGetChildrenDto input);
Task<ListResultDto<string>> GetRoleNamesAsync(Guid id);
Task<PagedResultDto<IdentityRoleDto>> GetUnaddedRolesAsync(Guid id, OrganizationUnitGetUnaddedRoleListInput input);
Task<PagedResultDto<IdentityRoleDto>> GetRolesAsync(Guid id, PagedAndSortedResultRequestDto input);
Task AddRolesAsync(Guid id, OrganizationUnitAddRoleDto input);
Task<PagedResultDto<IdentityUserDto>> GetUnaddedUsersAsync(Guid id, OrganizationUnitGetUnaddedUserListInput input);
Task<PagedResultDto<IdentityUserDto>> GetUsersAsync(Guid id, GetIdentityUsersInput input);
Task AddUsersAsync(Guid id, OrganizationUnitAddUserDto input);
Task DeleteRoleAsync(Guid organizationUnitId, Guid roleId);
Task DeleteUserAsync(Guid organizationUnitId, Guid userId);
}
然后是针对上述接口的各个DTO的定义:
// OrganizationUnitAddRoleDto
public class OrganizationUnitAddRoleDto
{
[Required]
public List<Guid> RoleIds { get; set; }
}
//OrganizationUnitAddUserDto
public class OrganizationUnitAddUserDto
{
[Required]
public List<Guid> UserIds { get; set; }
}
//OrganizationUnitCreateDto
public class OrganizationUnitCreateDto : ExtensibleObject
{
[Required]
[DynamicStringLength(typeof(OrganizationUnitConsts), nameof(OrganizationUnitConsts.MaxDisplayNameLength))]
public string DisplayName { get; set; }
public Guid? ParentId { get; set; }
}
//OrganizationUnitDto
public class OrganizationUnitDto : ExtensibleAuditedEntityDto<Guid>
{
public Guid? ParentId { get; set; }
public string Code { get; set; }
public string DisplayName { get; set; }
}
//OrganizationUnitGetChildrenDto
public class OrganizationUnitGetChildrenDto : IEntityDto<Guid>
{
[Required]
public Guid Id { get; set; }
public bool Recursive { get; set; }
}
//OrganizationUnitGetListInput
public class OrganizationUnitGetListInput : FilterBase, IPagedAndSortedResultRequest
{
[CompareTo(nameof(OrganizationUnitDto.DisplayName))]
[StringFilterOptions(AutoFilterer.Enums.StringFilterOption.Contains)]
public string Filter { get; set; }
public int SkipCount { get; set; }
public int MaxResultCount { get; set; }
public string Sorting { get; set; }
}
//OrganizationUnitGetUnaddedRoleListInput
public class OrganizationUnitGetUnaddedRoleListInput : FilterBase, IPagedAndSortedResultRequest
{
public string Filter { get; set; }
public int SkipCount { get; set; }
public int MaxResultCount { get; set; }
public string Sorting { get; set; }
}
//OrganizationUnitGetUnaddedUserListInput
public class OrganizationUnitGetUnaddedUserListInput : FilterBase, IPagedAndSortedResultRequest
{
public string Filter { get; set; }
public int SkipCount { get ; set ; }
public int MaxResultCount { get ; set ; }
public string Sorting { get ; set ; }
}
//OrganizationUnitMoveDto
public class OrganizationUnitMoveDto
{
public Guid? ParentId { get; set; }
}
//OrganizationUnitUpdateDto
public class OrganizationUnitUpdateDto : ExtensibleObject
{
public string DisplayName { get; set; }
}
二、Application
这一层主要定义OrganizationUnitAppService,属于实现接口。
public class OrganizationUnitAppService : IdentityAppServiceBase, IOrganizationUnitAppService
{
protected OrganizationUnitManager OrganizationUnitManager { get; }
protected IOrganizationUnitRepository OrganizationUnitRepository { get; }
protected IdentityUserManager UserManager { get; }
protected IIdentityRoleRepository RoleRepository { get; }
protected IIdentityUserRepository UserRepository { get; }
public OrganizationUnitAppService(
IdentityUserManager userManager,
IIdentityRoleRepository roleRepository,
IIdentityUserRepository userRepository,
OrganizationUnitManager organizationUnitManager,
IOrganizationUnitRepository organizationUnitRepository)
{
UserManager = userManager;
RoleRepository = roleRepository;
UserRepository = userRepository;
OrganizationUnitManager = organizationUnitManager;
OrganizationUnitRepository = organizationUnitRepository;
ObjectMapperContext = typeof(AbpIdentityApplicationModule);
}
public async virtual Task<OrganizationUnitDto> CreateAsync(OrganizationUnitCreateDto input)
{
var origanizationUnit = new OrganizationUnit(
GuidGenerator.Create(), input.DisplayName, input.ParentId, CurrentTenant.Id)
{
CreationTime = Clock.Now
};
input.MapExtraPropertiesTo(origanizationUnit);
await OrganizationUnitManager.CreateAsync(origanizationUnit);
await CurrentUnitOfWork.SaveChangesAsync();
return ObjectMapper.Map<OrganizationUnit, OrganizationUnitDto>(origanizationUnit);
}
public async virtual Task DeleteAsync(Guid id)
{
var origanizationUnit = await OrganizationUnitRepository.FindAsync(id);
if (origanizationUnit == null)
{
return;
}
await OrganizationUnitManager.DeleteAsync(id);
}
public async virtual Task<ListResultDto<OrganizationUnitDto>> GetRootAsync()
{
var rootOriganizationUnits = await OrganizationUnitManager.FindChildrenAsync(null, recursive: false);
return new ListResultDto<OrganizationUnitDto>(
ObjectMapper.Map<List<OrganizationUnit>, List<OrganizationUnitDto>>(rootOriganizationUnits));
}
public async virtual Task<ListResultDto<OrganizationUnitDto>> FindChildrenAsync(OrganizationUnitGetChildrenDto input)
{
var origanizationUnitChildren = await OrganizationUnitManager.FindChildrenAsync(input.Id, input.Recursive);
return new ListResultDto<OrganizationUnitDto>(
ObjectMapper.Map<List<OrganizationUnit>, List<OrganizationUnitDto>>(origanizationUnitChildren));
}
public async virtual Task<OrganizationUnitDto> GetAsync(Guid id)
{
var origanizationUnit = await OrganizationUnitRepository.FindAsync(id);
return ObjectMapper.Map<OrganizationUnit, OrganizationUnitDto>(origanizationUnit);
}
public async virtual Task<OrganizationUnitDto> GetLastChildOrNullAsync(Guid? parentId)
{
var origanizationUnitLastChildren = await OrganizationUnitManager.GetLastChildOrNullAsync(parentId);
return ObjectMapper.Map<OrganizationUnit, OrganizationUnitDto>(origanizationUnitLastChildren);
}
public async virtual Task<ListResultDto<OrganizationUnitDto>> GetAllListAsync()
{
var origanizationUnits = await OrganizationUnitRepository.GetListAsync(false);
return new ListResultDto<OrganizationUnitDto>(
ObjectMapper.Map<List<OrganizationUnit>, List<OrganizationUnitDto>>(origanizationUnits));
}
public async virtual Task<PagedResultDto<OrganizationUnitDto>> GetListAsync(OrganizationUnitGetListInput input)
{
var origanizationUnitCount = await OrganizationUnitRepository.GetCountAsync();
var origanizationUnits = await OrganizationUnitRepository
.GetListAsync(input.Sorting, input.MaxResultCount, input.SkipCount, false);
return new PagedResultDto<OrganizationUnitDto>(origanizationUnitCount,
ObjectMapper.Map<List<OrganizationUnit>, List<OrganizationUnitDto>>(origanizationUnits));
}
public async virtual Task<ListResultDto<string>> GetRoleNamesAsync(Guid id)
{
var inOrignizationUnitRoleNames = await UserRepository.GetRoleNamesInOrganizationUnitAsync(id);
return new ListResultDto<string>(inOrignizationUnitRoleNames);
}
public async virtual Task<PagedResultDto<IdentityRoleDto>> GetUnaddedRolesAsync(Guid id, OrganizationUnitGetUnaddedRoleListInput input)
{
var origanizationUnit = await OrganizationUnitRepository.GetAsync(id);
var origanizationUnitRoleCount = await OrganizationUnitRepository
.GetUnaddedRolesCountAsync(origanizationUnit, input.Filter);
var origanizationUnitRoles = await OrganizationUnitRepository
.GetUnaddedRolesAsync(origanizationUnit,
input.Sorting, input.MaxResultCount,
input.SkipCount, input.Filter);
return new PagedResultDto<IdentityRoleDto>(origanizationUnitRoleCount,
ObjectMapper.Map<List<IdentityRole>, List<IdentityRoleDto>>(origanizationUnitRoles));
}
public async virtual Task<PagedResultDto<IdentityRoleDto>> GetRolesAsync(Guid id, PagedAndSortedResultRequestDto input)
{
var origanizationUnit = await OrganizationUnitRepository.GetAsync(id);
var origanizationUnitRoleCount = await OrganizationUnitRepository
.GetRolesCountAsync(origanizationUnit);
var origanizationUnitRoles = await OrganizationUnitRepository
.GetRolesAsync(origanizationUnit,
input.Sorting, input.MaxResultCount,
input.SkipCount);
return new PagedResultDto<IdentityRoleDto>(origanizationUnitRoleCount,
ObjectMapper.Map<List<IdentityRole>, List<IdentityRoleDto>>(origanizationUnitRoles));
}
public async virtual Task<PagedResultDto<IdentityUserDto>> GetUnaddedUsersAsync(Guid id, OrganizationUnitGetUnaddedUserListInput input)
{
var origanizationUnit = await OrganizationUnitRepository.GetAsync(id);
var origanizationUnitUserCount = await OrganizationUnitRepository
.GetUnaddedUsersCountAsync(origanizationUnit, input.Filter);
var origanizationUnitUsers = await OrganizationUnitRepository
.GetUnaddedUsersAsync(origanizationUnit,
input.Sorting, input.MaxResultCount,
input.SkipCount, input.Filter);
return new PagedResultDto<IdentityUserDto>(origanizationUnitUserCount,
ObjectMapper.Map<List<IdentityUser>, List<IdentityUserDto>>(origanizationUnitUsers));
}
public async virtual Task<PagedResultDto<IdentityUserDto>> GetUsersAsync(Guid id, GetIdentityUsersInput input)
{
var origanizationUnit = await OrganizationUnitRepository.GetAsync(id);
var origanizationUnitUserCount = await OrganizationUnitRepository
.GetMembersCountAsync(origanizationUnit, input.Filter);
var origanizationUnitUsers = await OrganizationUnitRepository
.GetMembersAsync(origanizationUnit,
input.Sorting, input.MaxResultCount,
input.SkipCount, input.Filter);
return new PagedResultDto<IdentityUserDto>(origanizationUnitUserCount,
ObjectMapper.Map<List<IdentityUser>, List<IdentityUserDto>>(origanizationUnitUsers));
}
public async virtual Task MoveAsync(Guid id, OrganizationUnitMoveDto input)
{
await OrganizationUnitManager.MoveAsync(id, input.ParentId);
}
public async virtual Task<OrganizationUnitDto> UpdateAsync(Guid id, OrganizationUnitUpdateDto input)
{
var origanizationUnit = await OrganizationUnitRepository.GetAsync(id);
origanizationUnit.DisplayName = input.DisplayName;
input.MapExtraPropertiesTo(origanizationUnit);
await OrganizationUnitManager.UpdateAsync(origanizationUnit);
await CurrentUnitOfWork.SaveChangesAsync();
return ObjectMapper.Map<OrganizationUnit, OrganizationUnitDto>(origanizationUnit);
}
public async virtual Task AddUsersAsync(Guid id, OrganizationUnitAddUserDto input)
{
var origanizationUnit = await OrganizationUnitRepository.GetAsync(id);
foreach (var item in input.UserIds)
{
var user = await UserRepository.GetAsync(item);
await UserManager.AddToOrganizationUnitAsync(user, origanizationUnit);
}
await CurrentUnitOfWork.SaveChangesAsync();
}
public async virtual Task AddRolesAsync(Guid id, OrganizationUnitAddRoleDto input)
{
var origanizationUnit = await OrganizationUnitRepository.GetAsync(id);
foreach (var item in input.RoleIds)
{
var role = await RoleRepository.GetAsync(item);
await OrganizationUnitManager.AddRoleToOrganizationUnitAsync(role, origanizationUnit);
}
await CurrentUnitOfWork.SaveChangesAsync();
}
public async virtual Task DeleteRoleAsync(Guid organizationUnitId, Guid roleId)
{
var origanizationUnit = await OrganizationUnitRepository.GetAsync(organizationUnitId);
var role = await RoleRepository.GetAsync(roleId);
await OrganizationUnitManager.RemoveRoleFromOrganizationUnitAsync(role, origanizationUnit);
}
public async virtual Task DeleteUserAsync(Guid organizationUnitId, Guid userId)
{
await UserManager.RemoveFromOrganizationUnitAsync(userId, organizationUnitId);
}
}
3、HttpApi
这一层主要定义OrganizationUnit的Controller,很简单,就需要它实现IOrganizationUnitAppService然后在方法内部继续调用IOrganizationUnitAppService的同名方法就可以了。
需要注意的是如果你的Abp开启了AutoController或者你是前后端分离,都不需要继续在这一层进行Controller的定义。
定义的过程很简单,就不放代码了。
4、Web
如果你的应用是Razor Page,那么需要在Web层定义Organization Unit的CRUD UI。
在Pages下面新建Identity/OrganizationUnits,分别定义如下razor pages:
Index:
//C#
namespace Wallee.FileManagement.Web.Pages.Identity.OrganizationUnits
{
public class IndexModel : FileManagementPageModel
{
public void OnGet()
{
}
}
}
//razor page
@page
@using Microsoft.AspNetCore.Authorization;
@using Microsoft.AspNetCore.Mvc.Localization;
@using Volo.Abp.AspNetCore.Mvc.UI.Layout;
@using Wallee.FileManagement.Localization;
@using Wallee.FileManagement.OrganizationUnits.Dtos;
@using Wallee.FileManagement.Permissions;
@using Wallee.FileManagement.Web.Pages.Identity.OrganizationUnits;
@model IndexModel
@inject IPageLayout PageLayout
@inject IAuthorizationService Authorization
@inject IHtmlLocalizer<FileManagementResource> L
@section scripts{
<abp-script src=&#34;/Pages/Identity/OrganizationUnits/Index.js&#34;></abp-script>
}
@section styles{
<abp-style src=&#34;/Pages/Identity/OrganizationUnits/Index.css&#34;></abp-style>
}
<abp-card>
<abp-card-header>
<abp-row>
<abp-column size-md=&#34;_6&#34;>
<abp-card-title>@L[&#34;OrganizationUnit&#34;]</abp-card-title>
</abp-column>
<abp-column size-md=&#34;_6&#34; class=&#34;text-end&#34;>
</abp-column>
</abp-row>
</abp-card-header>
<abp-card-body>
<abp-row>
<abp-column size-lg=&#34;_4&#34;>
<abp-card>
<abp-card-header background=&#34;Light&#34;>
<div class=&#34;d-flex justify-content-between align-items-center&#34;>
<span>机构目录树</span>
@if (await Authorization.IsGrantedAsync(FileManagementPermissions.OrganizationUnits.Create))
{
<abp-button size=&#34;Small&#34; button-type=&#34;Primary&#34; icon=&#34;fas fa-plus&#34; text=&#34;新增根机构&#34; id=&#34;CreateNewOrganizationUnit&#34;></abp-button>
}
</div>
</abp-card-header>
<abp-card-body>
<abp-table id=&#34;ousTable&#34; class=&#34;nowrap&#34; />
</abp-card-body>
</abp-card>
</abp-column>
<abp-column size-lg=&#34;_8&#34;>
<abp-tabs tab-style=&#34;Tab&#34;>
<abp-tab title=&#34;用户&#34;>
<abp-card id=&#34;userInfoEmpty&#34; class=&#34;d-flex flex-column justify-content-center align-items-center&#34;>
<abp-card-body abp-rounded=&#34;Top&#34; style=&#34;width:100%;&#34; background=&#34;Light&#34;>
<abp-card-text>请点击左侧菜单选择机构进行用户相关操作</abp-card-text>
</abp-card-body>
</abp-card>
<abp-card id=&#34;userInfo&#34;>
<abp-card-header style=&#34;padding:0 0 10px 0;&#34;>
<div class=&#34;d-flex justify-content-between align-items-center&#34;>
<abp-card-title class=&#34;text-primary&#34; id=&#34;currentOuTitle_u&#34;></abp-card-title>
<abp-button button-type=&#34;Outline_Primary&#34; icon=&#34;plus&#34; id=&#34;addUserToOuBtn&#34; text=&#34;新增用户&#34;></abp-button>
</div>
</abp-card-header>
<abp-card-body style=&#34;padding:0 0 10px 0;&#34;>
<abp-table striped-rows=&#34;true&#34; id=&#34;ouUsersTable&#34; class=&#34;nowrap&#34; />
</abp-card-body>
</abp-card>
</abp-tab>
<abp-tab title=&#34;角色&#34;>
<abp-card id=&#34;roleInfoEmpty&#34; class=&#34;d-flex flex-column justify-content-center align-items-center&#34;>
<abp-card-body abp-rounded=&#34;Top&#34; style=&#34;width:100%;&#34; background=&#34;Light&#34;>
<abp-card-text>请点击左侧菜单选择机构进行角色相关操作</abp-card-text>
</abp-card-body>
</abp-card>
<abp-card id=&#34;roleInfo&#34;>
<abp-card-header style=&#34;padding:0 0 10px 0;&#34;>
<div class=&#34;d-flex justify-content-between align-items-center&#34;>
<abp-card-title class=&#34;text-primary&#34; id=&#34;currentOuTitle_r&#34;></abp-card-title>
<abp-button button-type=&#34;Outline_Primary&#34; icon=&#34;plus&#34; id=&#34;addRoleToOuBtn&#34; text=&#34;新增角色&#34;></abp-button>
</div>
</abp-card-header>
<abp-card-body style=&#34;padding:0 0 10px 0;&#34;>
<abp-table striped-rows=&#34;true&#34; id=&#34;ouRolesTable&#34; class=&#34;nowrap&#34; />
</abp-card-body>
</abp-card>
</abp-tab>
</abp-tabs>
</abp-column>
</abp-row>
</abp-card-body>
</abp-card>
//javascript
$(function () {
var l = abp.localization.getResource(&#39;FileManagement&#39;);
var ouService = wallee.fileManagement.organizationUnits.organizationUnit;
var createModal = new abp.ModalManager(abp.appPath + &#39;Identity/OrganizationUnits/CreateModal&#39;);
var addRoleToOuModal = new abp.ModalManager(abp.appPath + &#34;Identity/OrganizationUnits/AddRoleToOuModal&#34;);
var addUserToOuModal = new abp.ModalManager(abp.appPath + &#34;Identity/OrganizationUnits/AddUserToOuModal&#34;);
var currentOu = null;
var ouDataTable = null;
var ouUserDataTable = null;
var ouRoleDataTable = null;
function initOuUserTable() {
var inputAction = function (requestData, dataTableSettings) {
return {
filter: &#34;&#34;,
};
};
ouUserDataTable = $(&#39;#ouUsersTable&#39;).DataTable(abp.libs.datatables.normalizeConfiguration({
processing: true,
serverSide: true,
paging: true,
searching: false,
autoWidth: false,
scrollCollapse: true,
order: [[0, &#34;asc&#34;]],
ajax: abp.libs.datatables.createAjax(function () {
return ouService.getUsers(currentOu, inputAction);
}),
columnDefs: [
{
title: &#34;登录名&#34;,
data: &#34;userName&#34;,
}, {
title: &#34;用户名称&#34;,
data: &#34;name&#34;,
}, {
rowAction: {
items:
[
{
text: &#34;删除&#34;,
visible: abp.auth.isGranted(&#39;FileManagement.OrganizationUnit.Delete&#39;),
confirmMessage: function (data) {
return `确认删除当前用户吗?(${data.record.userName})`;
},
action: function (data) {
ouService.deleteUser(currentOu, data.record.id)
.then(function () {
abp.notify.info(l(&#39;SuccessfullyDeleted&#39;));
ouUserDataTable.ajax.reload();
});
}
}
]
}
},
]
}));
}
function initOuRoleTable() {
var inputAction = function (requestData, dataTableSettings) {
return {
filter: &#34;&#34;,
};
};
ouRoleDataTable = $(&#39;#ouRolesTable&#39;).DataTable(abp.libs.datatables.normalizeConfiguration({
processing: true,
serverSide: true,
paging: true,
searching: false,
autoWidth: false,
scrollCollapse: true,
order: [[0, &#34;asc&#34;]],
ajax: abp.libs.datatables.createAjax(function () {
return ouService.getRoles(currentOu, inputAction);
}),
columnDefs: [
{
title: &#34;角色名&#34;,
data: &#34;name&#34;,
}, {
rowAction: {
items:
[
{
text: &#34;删除&#34;,
visible: abp.auth.isGranted(&#39;FileManagement.OrganizationUnit.Delete&#39;),
confirmMessage: function (data) {
return `确认删除当前角色吗?(${data.record.name})`;
},
action: function (data) {
ouService.deleteRole(currentOu, data.record.id)
.then(function () {
abp.notify.info(l(&#39;SuccessfullyDeleted&#39;));
ouRoleDataTable.ajax.reload();
});
}
}
]
}
},
]
}));
}
function checkUserAndRoleInfoPanel() {
if (!currentOu) {
$(&#34;#userInfo&#34;).hide();
$(&#34;#roleInfo&#34;).hide();
$(&#34;#userInfoEmpty&#34;).show();
$(&#34;#roleInfoEmpty&#34;).show();
} else {
$(&#34;#userInfoEmpty&#34;).remove();
$(&#34;#roleInfoEmpty&#34;).remove();
$(&#34;#userInfo&#34;).show();
$(&#34;#roleInfo&#34;).show();
}
}
function initOuDataTable() {
ouDataTable = $(&#39;#ousTable&#39;).DataTable(abp.libs.datatables.normalizeConfiguration({
processing: true,
serverSide: true,
paging: false,
info: false,
searching: false,//disable default searchbox
autoWidth: false,
scrollCollapse: true,
order: [[0, &#34;asc&#34;]],
ajax: abp.libs.datatables.createAjax(ouService.getAllList),
columnDefs: [
{
title: &#34;机构名称&#34;,
data: &#34;displayName&#34;,
}, {
title: &#34;操作&#34;,
rowAction: {
items:
[
{
text: &#34;查看&#34;,
iconClass: &#34;fas fa-search&#34;,
displayNameHtml: false,
visible: abp.auth.isGranted(&#39;FileManagement.OrganizationUnit&#39;),
action: function (data) {
currentOu = data.record.id;
$(&#34;#currentOuTitle_u&#34;).html(data.record.displayName);
$(&#34;#currentOuTitle_r&#34;).html(data.record.displayName);
checkUserAndRoleInfoPanel();
if (!ouUserDataTable) {
initOuUserTable();
}
else {
ouUserDataTable.ajax.reload();
}
if (!ouRoleDataTable) {
initOuRoleTable();
}
else {
ouRoleDataTable.ajax.reload();
}
}
}, {
text: l(&#39;Delete&#39;),
iconClass: &#34;fas fa-trash&#34;,
visible: abp.auth.isGranted(&#39;FileManagement.OrganizationUnit.Delete&#39;),
confirmMessage: function (data) {
return l(&#39;FileDeletionConfirmationMessage&#39;, data.record.id);
},
action: function (data) {
ouService.delete(data.record.id)
.then(function () {
abp.notify.info(l(&#39;SuccessfullyDeleted&#39;));
ouDataTable.ajax.reload();
});
}
}
]
}
},
]
}));
}
initOuDataTable();
checkUserAndRoleInfoPanel();
$(&#34;#addUserToOuBtn&#34;).click(function (e) {
addUserToOuModal.open({ organizationUnitId: currentOu });
});
$(&#34;#addRoleToOuBtn&#34;).click(function (e) {
addRoleToOuModal.open({ organizationUnitId: currentOu });
});
$(&#34;#CreateNewOrganizationUnit&#34;).click(function () {
createModal.open();
});
createModal.onResult(function (e, result) {
ouDataTable.ajax.reload();
abp.notify.success(&#34;操作成功&#34;);
});
addRoleToOuModal.onResult(function (e, result) {
ouRoleDataTable.ajax.reload();
abp.notify.success(&#34;操作成功&#34;);
});
addUserToOuModal.onResult(function (e, result) {
ouUserDataTable.ajax.reload();
abp.notify.success(&#34;操作成功&#34;);
})
});
EditModal:
//C#
using Microsoft.AspNetCore.Mvc;
using System;
using System.ComponentModel.DataAnnotations;
using System.Threading.Tasks;
using Volo.Abp.Identity;
using Volo.Abp.Validation;
using Wallee.FileManagement.OrganizationUnits;
using Wallee.FileManagement.OrganizationUnits.Dtos;
namespace Wallee.FileManagement.Web.Pages.Identity.OrganizationUnits
{
public class EditModalModel : FileManagementPageModel
{
private readonly IOrganizationUnitAppService _organizationUnitAppService;
[BindProperty(SupportsGet = true)]
[HiddenInput]
public Guid OrganizationUnitId { get; set; }
[BindProperty]
public OrganizationUnitUpdateViewModel ViewModel { get; set; }
public EditModalModel(
IOrganizationUnitAppService organizationUnitAppService)
{
_organizationUnitAppService = organizationUnitAppService;
}
public async Task OnGetAsync()
{
var dto = await _organizationUnitAppService.GetAsync(OrganizationUnitId);
ViewModel = new OrganizationUnitUpdateViewModel { DisplayName = dto.DisplayName };
}
public async Task OnPostAsync()
{
var dto = ObjectMapper.Map<OrganizationUnitUpdateViewModel, OrganizationUnitUpdateDto>(ViewModel);
await _organizationUnitAppService.UpdateAsync(OrganizationUnitId, dto);
}
}
public class OrganizationUnitUpdateViewModel
{
[Display(Name = &#34;机构名称&#34;)]
[Required]
[DynamicStringLength(typeof(OrganizationUnitConsts), nameof(OrganizationUnitConsts.MaxDisplayNameLength))]
public string DisplayName { get; set; }
}
}
//razor page
@page
@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal;
@model Wallee.FileManagement.Web.Pages.Identity.OrganizationUnits.EditModalModel
@{
Layout = null;
}
<abp-dynamic-form abp-model=&#34;ViewModel&#34; data-ajaxForm=&#34;true&#34; asp-page=&#34;EditModal&#34;>
<abp-modal>
<abp-modal-header title=&#34;新增机构&#34;></abp-modal-header>
<abp-modal-body>
<abp-input asp-for=&#34;@Model.OrganizationUnitId&#34;></abp-input>s
<abp-form-content />
</abp-modal-body>
<abp-modal-footer buttons=&#34;@(AbpModalButtons.Cancel|AbpModalButtons.Save)&#34;></abp-modal-footer>
</abp-modal>
</abp-dynamic-form>
CreateModal:
//C#
using System.ComponentModel.DataAnnotations;
using System;
using Volo.Abp.Identity;
using Volo.Abp.Validation;
using Wallee.FileManagement.OrganizationUnits.Dtos;
using Microsoft.AspNetCore.Mvc;
using Wallee.FileManagement.OrganizationUnits;
using System.Threading.Tasks;
namespace Wallee.FileManagement.Web.Pages.Identity.OrganizationUnits
{
public class CreateModalModel : FileManagementPageModel
{
private readonly IOrganizationUnitAppService _organizationUnitAppService;
[BindProperty]
public OrganizationUnitCreateViewModel ViewModel { get; set; }
[BindProperty(SupportsGet = true)]
public Guid? ParentId { get; set; }
public CreateModalModel(IOrganizationUnitAppService organizationUnitAppService)
{
_organizationUnitAppService = organizationUnitAppService;
}
public void OnGet()
{
ViewModel = new OrganizationUnitCreateViewModel
{
ParentId = ParentId
};
}
public async Task OnPostAsync()
{
var dto = ObjectMapper.Map<OrganizationUnitCreateViewModel, OrganizationUnitCreateDto>(ViewModel);
await _organizationUnitAppService.CreateAsync(dto);
}
}
public class OrganizationUnitCreateViewModel
{
[Required]
[Display(Name = &#34;机构名称&#34;)]
[DynamicStringLength(typeof(OrganizationUnitConsts), nameof(OrganizationUnitConsts.MaxDisplayNameLength))]
public string DisplayName { get; set; }
[HiddenInput]
public Guid? ParentId { get; set; }
}
}
//razor page
@page
@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal;
@model Wallee.FileManagement.Web.Pages.Identity.OrganizationUnits.CreateModalModel
@{
Layout = null;
}
<abp-dynamic-form abp-model=&#34;ViewModel&#34; data-ajaxForm=&#34;true&#34; asp-page=&#34;CreateModal&#34;>
<abp-modal>
<abp-modal-header title=&#34;新增机构&#34;></abp-modal-header>
<abp-modal-body>
<abp-form-content />
</abp-modal-body>
<abp-modal-footer buttons=&#34;@(AbpModalButtons.Cancel|AbpModalButtons.Save)&#34;></abp-modal-footer>
</abp-modal>
</abp-dynamic-form>
AddUserToOuModal:
//C#
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using System;
using System.Threading.Tasks;
using Wallee.FileManagement.OrganizationUnits;
using Wallee.FileManagement.OrganizationUnits.Dtos;
namespace Wallee.FileManagement.Web.Pages.Identity.OrganizationUnits
{
public class AddUserToOuModalModel : FileManagementPageModel
{
private readonly IOrganizationUnitAppService _organizationUnitAppService;
[BindProperty(SupportsGet = true)]
public Guid OrganizationUnitId { get; set; }
[BindProperty]
public OrganizationUnitDto OrganizationUnit { get; set; }
public AddUserToOuModalModel(IOrganizationUnitAppService organizationUnitAppService)
{
_organizationUnitAppService = organizationUnitAppService;
}
public async Task OnGetAsync()
{
OrganizationUnit = await _organizationUnitAppService.GetAsync(OrganizationUnitId);
}
}
}
//razor page
@page
@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal;
@model Wallee.FileManagement.Web.Pages.Identity.OrganizationUnits.AddUserToOuModalModel
@{
Layout = null;
}
<abp-script src=&#34;/Pages/Identity/OrganizationUnits/AddUserToOuModal.js&#34;></abp-script>
<script type=&#34;text/javascript&#34;>
var organizationUnitId = &#34;@Model.OrganizationUnitId&#34;;
</script>
<abp-modal size=&#34;Large&#34;>
<abp-modal-header title=&#34;新增用户-@(Model.OrganizationUnit.DisplayName)&#34;></abp-modal-header>
<abp-modal-body>
<abp-table id=&#34;unAddedUsersTable&#34;></abp-table>
</abp-modal-body>
<form>
<abp-modal-footer buttons=&#34;@(AbpModalButtons.Cancel|AbpModalButtons.Save)&#34;></abp-modal-footer>
</form>
</abp-modal>
//javascript
$(function () {
var ouService = wallee.fileManagement.organizationUnits.organizationUnit;
var inputAction = function (requestData, dataTableSettings) {
console.log(requestData);
console.log(dataTableSettings);
return {
filter: &#34;&#34;,
};
};
function initOuUserTable() {
ouUserDataTable = $(&#39;#unAddedUsersTable&#39;).DataTable(abp.libs.datatables.normalizeConfiguration({
processing: true,
serverSide: true,
paging: true,
searching: true,
autoWidth: false,
scrollCollapse: true,
order: [[0, &#34;asc&#34;]],
ajax: abp.libs.datatables.createAjax(function () {
return ouService.getUnaddedUsers(organizationUnitId, inputAction);
}),
columnDefs: [
{
title: &#34;登录名&#34;,
data: &#34;userName&#34;,
}, {
title: &#34;用户名&#34;,
data: &#34;name&#34;,
}, {
rowAction: {
items:
[
{
text: &#34;添加&#34;,
visible: abp.auth.isGranted(&#39;FileManagement.OrganizationUnit.ManageUsers&#39;),
confirmMessage: function (data) {
return `确认要添加该角色?(${data.record.name})`;
},
action: function (data) {
ouService.addUsers(organizationUnitId, { UserIds: [data.record.id] })
}
}
]
}
},
]
}));
}
initOuUserTable();
})
AddRoleToOuModal:
//C#
using Microsoft.AspNetCore.Mvc;
using System;
using System.Threading.Tasks;
using Wallee.FileManagement.OrganizationUnits;
using Wallee.FileManagement.OrganizationUnits.Dtos;
namespace Wallee.FileManagement.Web.Pages.Identity.OrganizationUnits
{
public class AddRoleToOuModalModel : FileManagementPageModel
{
private readonly IOrganizationUnitAppService _organizationUnitAppService;
[BindProperty(SupportsGet = true)]
public Guid OrganizationUnitId { get; set; }
[BindProperty]
public OrganizationUnitDto OrganizationUnit { get; set; }
public AddRoleToOuModalModel(IOrganizationUnitAppService organizationUnitAppService)
{
_organizationUnitAppService = organizationUnitAppService;
}
public async Task OnGetAsync()
{
OrganizationUnit = await _organizationUnitAppService.GetAsync(OrganizationUnitId);
}
}
}
//razor page
@page
@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal;
@using Wallee.FileManagement.Web.Pages.Identity.OrganizationUnits;
@model AddRoleToOuModalModel
@{
Layout = null;
}
<abp-script src=&#34;/Pages/Identity/OrganizationUnits/AddRoleToOuModal.js&#34;></abp-script>
<script type=&#34;text/javascript&#34;>
var organizationUnitId = &#34;@Model.OrganizationUnitId&#34;;
</script>
<abp-modal size=&#34;Large&#34;>
<abp-modal-header title=&#34;新增角色-@(Model.OrganizationUnit.DisplayName)&#34;></abp-modal-header>
<abp-modal-body>
<abp-table id=&#34;unAddedRoleTable&#34;></abp-table>
</abp-modal-body>
<form>
<abp-modal-footer buttons=&#34;@(AbpModalButtons.Cancel|AbpModalButtons.Save)&#34;></abp-modal-footer>
</form>
</abp-modal>
//javascript
$(function () {
var ouService = wallee.fileManagement.organizationUnits.organizationUnit;
var inputAction = function (requestData, dataTableSettings) {
console.log(requestData);
console.log(dataTableSettings);
return {
filter: &#34;&#34;,
};
};
function initOuRoleTable() {
ouRoleDataTable = $(&#39;#unAddedRoleTable&#39;).DataTable(abp.libs.datatables.normalizeConfiguration({
processing: true,
serverSide: true,
paging: true,
searching: false,
autoWidth: false,
scrollCollapse: true,
order: [[0, &#34;asc&#34;]],
ajax: abp.libs.datatables.createAjax(function () {
return ouService.getUnaddedRoles(organizationUnitId, inputAction);
}),
columnDefs: [
{
title: &#34;角色名&#34;,
data: &#34;name&#34;,
}, {
rowAction: {
items:
[
{
text: &#34;添加&#34;,
visible: abp.auth.isGranted(&#39;FileManagement.OrganizationUnit.ManageRoles&#39;),
confirmMessage: function (data) {
return `确认要添加该角色?(${data.record.name})`;
},
action: function (data) {
ouService.addRoles(organizationUnitId, { RoleIds: [data.record.id] })
}
}
]
}
},
]
}));
}
initOuRoleTable();
})5、权限
OrganizationUnit在Abp中属于Identity的范畴。Identity本身依赖于Abp的PermissionManagement模块,用于构建用户、角色以及机构和权限之间的关系。所以,在围绕OrganizationUnit构建它的权限之前,有必要对Abp的权限进行大概的说明。
Abp的权限基于http://asp.net core的授权流程,通过一系列的转换最终会转化为http://asp.net core中的以policy为基础进行授权的一种模式(详情:https://learn.microsoft.com/en-us/aspnet/core/security/authorization/policies?view=aspnetcore-7.0),每一个你在Abp中定义的PermissionDefinition都是一个asp.net core上的policy。
在Abp的Identity下面,我们可以通过以下途径为用户分配权限:
①、可以为用户定义专属于用户自身的权限;
②、可以为用户归属的角色定义该角色拥有的权限;
③、可以为用户归属的机构定义该机构拥有的角色,继而定义这个角色所拥有的权限
④、可以为用户归属的结构定义该机构拥有的权限;
那么,衡量一个用户到底拥有多少权限,就要从三个方面进行合并:①用户自身拥有的权限;②用户归属的角色所拥有的权限;③用户归属的组织机构所拥有的角色/权限。 拥有了上述的基础知识后,我们就可以进行下一步了。
首先我们了解一下Abp下面的权限的数据结构:
Abp关于权限的配置主要集中在两个命名空间下面:
①、Volo.Abp.Authorization
PermissionDefinitionProvider是用于定义你系统权限的一个基类,在你的Abp的启动项目下面的Application.Contracts中已经给你定义了一个默认的,比如XXXXPermissionDefinitionProvider,你在这个类中定义你的权限就可以了。
PermissionValueProvider是用于校验你是否能够访问一个API。Abp在Volo.Abp.Authorization里面内置了三个PermissionValueProvider:RolePermissionValueProvider、UserPermissionValueProvider以及ClientPermissionValueProvider。他们的逻辑基本都一样:在CheckAsync这个方法里面考量CurrentUser的相关的Claim的值,看看符不符合要求。
首先我们需要在Application.Contracts层定义我们的OrganizationUnit相关的权限:
//PermissionDefinitionProvider
var organizationUnitsPermission = myGroup.AddPermission(FileManagementPermissions.OrganizationUnits.Default, L(&#34;Permission:Organization&#34;));
organizationUnitsPermission.AddChild(FileManagementPermissions.OrganizationUnits.Create, L(&#34;Permission:Create&#34;));
organizationUnitsPermission.AddChild(FileManagementPermissions.OrganizationUnits.Update, L(&#34;Permission:Update&#34;));
organizationUnitsPermission.AddChild(FileManagementPermissions.OrganizationUnits.Delete, L(&#34;Permission:Delete&#34;));
organizationUnitsPermission.AddChild(FileManagementPermissions.OrganizationUnits.ManageRoles, L(&#34;Permission:OrganizationUnits:ManageRoles&#34;));
organizationUnitsPermission.AddChild(FileManagementPermissions.OrganizationUnits.ManageUsers, L(&#34;Permission:OrganizationUnits:ManageUsers&#34;));
然后我们新增一个OrganizationUnitPermissionVaueProvider:
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Authorization.Permissions;
using Wallee.FileManagement.Identity;
namespace Wallee.FileManagement.Authorization.Permissions;
public class OrganizationUnitPermissionValueProvider : PermissionValueProvider
{
public const string ProviderName = &#34;O&#34;;
public override string Name => ProviderName;
public OrganizationUnitPermissionValueProvider(
IPermissionStore permissionStore)
: base(permissionStore)
{
}
public async override Task<PermissionGrantResult> CheckAsync(PermissionValueCheckContext context)
{
var organizationUnits = context.Principal?.FindAll(AbpOrganizationUnitClaimTypes.OrganizationUnit).Select(c => c.Value).ToArray();
if (organizationUnits.IsNullOrEmpty())
{
return PermissionGrantResult.Undefined;
}
foreach (var organizationUnit in organizationUnits.Distinct())
{
if (await PermissionStore.IsGrantedAsync(context.Permission.Name, Name, organizationUnit))
{
return PermissionGrantResult.Granted;
}
}
return PermissionGrantResult.Undefined;
}
public async override Task<MultiplePermissionGrantResult> CheckAsync(PermissionValuesCheckContext context)
{
var permissionNames = context.Permissions.Select(x => x.Name).Distinct().ToList();
Check.NotNullOrEmpty(permissionNames, nameof(permissionNames));
var result = new MultiplePermissionGrantResult(permissionNames.ToArray());
var organizationUnits = context.Principal?.FindAll(AbpOrganizationUnitClaimTypes.OrganizationUnit).Select(c => c.Value).ToArray();
if (organizationUnits.IsNullOrEmpty())
{
return result;
}
foreach (var organizationUnit in organizationUnits.Distinct())
{
var multipleResult = await PermissionStore.IsGrantedAsync(permissionNames.ToArray(), Name, organizationUnit);
foreach (var grantResult in multipleResult.Result.Where(grantResult =>
result.Result.ContainsKey(grantResult.Key) &&
result.Result[grantResult.Key] == PermissionGrantResult.Undefined &&
grantResult.Value != PermissionGrantResult.Undefined))
{
result.Result[grantResult.Key] = grantResult.Value;
permissionNames.RemoveAll(x => x == grantResult.Key);
}
if (result.AllGranted || result.AllProhibited)
{
break;
}
if (permissionNames.IsNullOrEmpty())
{
break;
}
}
return result;
}
}
②、Volo.Abp.PermissionManagement
这个模块属于Abp的应用模块,用于持久化Abp的权限数据。Abp在这里定义了User和Role相关的Provider用于持久化,我们需要在这里定义OrganizationUnitPermissionManagementProvider:
using Volo.Abp.Guids;
using Volo.Abp.MultiTenancy;
using Volo.Abp.PermissionManagement;
using Wallee.FileManagement.Authorization.Permissions;
namespace Wallee.FileManagement.PermissionManagement
{
public class OrganizationUnitPermissionManagementProvider : PermissionManagementProvider
{
public override string Name => OrganizationUnitPermissionValueProvider.ProviderName;
public OrganizationUnitPermissionManagementProvider(
IPermissionGrantRepository permissionGrantRepository,
IGuidGenerator guidGenerator,
ICurrentTenant currentTenant)
: base(
permissionGrantRepository,
guidGenerator,
currentTenant)
{
}
}
}
基于此,我们大概就完成了针对OrganizationUnit的自定义了。
需要注意的是我上面的代码没有完成针对OrganizationUnit的权限设置的改造,只是完成针对OrganizationUnit的角色绑定功能,有空/有需要的话我会对此进行升级。 |
|