@
之前我們在擴充套件身份管理模組的時候,已經實現了使用者關係管理,可以檢視本系列博文之前的內容。怎樣優雅地增刪查改(二):擴充套件身份管理模組
查詢依據
使用者之間的關係通過Relation表來儲存。模型如下圖所示:
關係型別由Type來定義
關係指向由UserId與RelatedUserId來描述
人員之間的關係是單項的,也就是說可以A是B的好友,但B不一定是A的好友
正向關係:User -> RelatedUser
反向關係:RelatedUser -> User
查詢目標業務物件HealthAlarm關聯了業務使用者HealthClient,因業務使用者與鑑權使用者IdentityUser共用同一個Id,因此可以通過查詢使用者關係關聯的User,查詢到業務物件。
定義按正向使用者關係查詢(IRelationToOrientedFilter)介面
public interface IRelationToOrientedFilter
{
Guid? RelationToUserId { get; set; }
public string EntityUserIdIdiom { get; }
string RelationType { get; set; }
}
對於Relation服務,其依賴關係在應用層,查詢指定使用者的關係使用者將在CurdAppServiceBase的子類實現。建立一個抽象方法GetUserIdsByRelatedToAsync
protected abstruct Task<IEnumerable<Guid>> GetUserIdsByRelatedToAsync(Guid userId, string relationType);
建立應用過濾條件方法:ApplyRelationToOrientedFiltered,在此實現拼接LINQ表示式,
ICurrentUser是Abp的一個服務,用於獲取當前登入使用者的資訊
程式碼如下:
protected virtual async Task<IQueryable<TEntity>> ApplyRelationToOrientedFiltered(IQueryable<TEntity> query, TGetListInput input)
{
if (input is IRelationToOrientedFilter)
{
var filteredInput = input as IRelationToOrientedFilter;
var entityUserIdIdiom = filteredInput.EntityUserIdIdiom;
if (string.IsNullOrEmpty(entityUserIdIdiom))
{
entityUserIdIdiom = "UserId";
}
if (HasProperty<TEntity>(entityUserIdIdiom))
{
var property = typeof(TEntity).GetProperty(entityUserIdIdiom);
if (filteredInput != null && filteredInput.RelationToUserId.HasValue && !string.IsNullOrEmpty(filteredInput.RelationType))
{
Guid userId = default;
if (filteredInput.RelationToUserId.Value == Guid.Empty)
{
using (var scope = ServiceProvider.CreateScope())
{
var currentUser = scope.ServiceProvider.GetRequiredService<ICurrentUser>();
if (currentUser != null)
{
userId = currentUser.GetId();
}
}
}
else
{
userId = filteredInput.RelationToUserId.Value;
}
var ids = await GetUserIdsByRelatedToAsync(userId, filteredInput.RelationType);
Expression originalExpression = null;
var parameter = Expression.Parameter(typeof(TEntity), "p");
foreach (var id in ids)
{
var keyConstantExpression = Expression.Constant(id, typeof(Guid));
var propertyAccess = Expression.MakeMemberAccess(parameter, property);
var expressionSegment = Expression.Equal(propertyAccess, keyConstantExpression);
if (originalExpression == null)
{
originalExpression = expressionSegment;
}
else
{
originalExpression = Expression.Or(originalExpression, expressionSegment);
}
}
var equalExpression = originalExpression != null ?
Expression.Lambda<Func<TEntity, bool>>(originalExpression, parameter)
: p => false;
query = query.Where(equalExpression);
}
}
}
return query;
}
定義按反向使用者關係查詢(IRelationFromOrientedFilter)介面
public interface IRelationFromOrientedFilter
{
Guid? RelationFromUserId { get; set; }
public string EntityUserIdIdiom { get; }
string RelationType { get; set; }
}
對於Relation服務,其依賴關係在應用層,查詢指定使用者的關係使用者將在CurdAppServiceBase的子類實現。建立一個抽象方法GetUserIdsByRelatedFromAsync
protected abstruct Task<IEnumerable<Guid>> GetUserIdsByRelatedFromAsync(Guid userId, string relationType);
建立應用過濾條件方法:ApplyRelationFromOrientedFiltered,在此實現拼接LINQ表示式,
ICurrentUser是Abp的一個服務,用於獲取當前登入使用者的資訊
程式碼如下:
protected virtual async Task<IQueryable<TEntity>> ApplyRelationFromOrientedFiltered(IQueryable<TEntity> query, TGetListInput input)
{
if (input is IRelationFromOrientedFilter)
{
var filteredInput = input as IRelationFromOrientedFilter;
var entityUserIdIdiom = filteredInput.EntityUserIdIdiom;
if (string.IsNullOrEmpty(entityUserIdIdiom))
{
entityUserIdIdiom = "UserId";
}
if (HasProperty<TEntity>(entityUserIdIdiom))
{
var property = typeof(TEntity).GetProperty(entityUserIdIdiom);
if (filteredInput != null && filteredInput.RelationFromUserId.HasValue && !string.IsNullOrEmpty(filteredInput.RelationType))
{
Guid userId = default;
if (filteredInput.RelationFromUserId.Value == Guid.Empty)
{
using (var scope = ServiceProvider.CreateScope())
{
var currentUser = scope.ServiceProvider.GetRequiredService<ICurrentUser>();
if (currentUser != null)
{
userId = currentUser.GetId();
}
}
}
else
{
userId = filteredInput.RelationFromUserId.Value;
}
var ids = await GetUserIdsByRelatedFromAsync(userId, filteredInput.RelationType);
Expression originalExpression = null;
var parameter = Expression.Parameter(typeof(TEntity), "p");
foreach (var id in ids)
{
var keyConstantExpression = Expression.Constant(id, typeof(Guid));
var propertyAccess = Expression.MakeMemberAccess(parameter, property);
var expressionSegment = Expression.Equal(propertyAccess, keyConstantExpression);
if (originalExpression == null)
{
originalExpression = expressionSegment;
}
else
{
originalExpression = Expression.Or(originalExpression, expressionSegment);
}
}
var equalExpression = originalExpression != null ?
Expression.Lambda<Func<TEntity, bool>>(originalExpression, parameter)
: p => false;
query = query.Where(equalExpression);
}
}
}
return query;
}
IRelationToOrientedFilter 和 IRelationFromOrientedFilter介面實現上並非互斥。
請注意,可應用過濾的條件為:
否則將原封不動返回IQueryable物件。
在應用層中,實現GetUserIdsByRelatedToAsync
protected override async Task<IEnumerable<Guid>> GetUserIdsByRelatedToAsync(Guid userId, string relationType)
{
var ids = await relationAppService.GetRelatedToUserIdsAsync(new GetRelatedUsersInput()
{
UserId = userId,
Type = relationType
});
return ids;
}
或GetUserIdsByRelatedFromAsync
protected override async Task<IEnumerable<Guid>> GetUserIdsByRelatedFromAsync(Guid userId, string relationType)
{
var ids = await relationAppService.GetRelatedFromUserIdsAsync(new GetRelatedUsersInput()
{
UserId = userId,
Type = relationType
});
return ids;
}
在GetAllAlarmInput中實現IRelationToOrientedFilter或GetUserIdsByRelatedFromAsync介面,程式碼如下:
public class GetAllAlarmInput : PagedAndSortedResultRequestDto, IRelationToOrientedFilter
{
public Guid? RelationToUserId { get ; set ; }
public string RelationType { get; set; }
public string EntityUserIdIdiom { get; }
...
}
建立一些客戶(Client)
進入客戶管理,在右側客戶列表中點選「檢視詳情」
開啟客戶詳情頁面,點選管理 - 設定簽約員工
選擇一個使用者,此時該客戶會簽約至該使用者賬號下,這裡我們將客戶1和客戶3簽約至當前賬號admin下。
登入簽約使用者(admin)的賬號,點選「我的」 - 客戶 - 簽約客戶
在客戶列表中可見,客戶1和客戶3已簽約至當前賬號下。
組合查詢的報文Payload如下圖:
本文來自部落格園,作者:林曉lx,轉載請註明原文連結:https://www.cnblogs.com/jevonsflash/p/17564676.html