首页 > 其他分享 >怎样优雅地增删查改(八):按用户关系查询

怎样优雅地增删查改(八):按用户关系查询

时间:2023-07-19 09:23:24浏览次数:44  
标签:string userId 用户 优雅 var 查改 filteredInput 增删 Guid

@

目录
用户关系(Relation)是描述业务系统中人员与人员之间的关系,如:签约、关注,或者朋友关系。

之前我们在扩展身份管理模块的时候,已经实现了用户关系管理,可以查看本系列博文之前的内容。怎样优雅地增删查改(二):扩展身份管理模块

原理

查询依据

用户之间的关系通过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; }

}

  • EntityUserIdIdiom:语义上的UserId,用于指定业务实体中用于描述“用户Id”字段的名称,若不指定,则默认为“UserId”;
  • RelationToUserId:正向关系用户Id,若为Guid.Empty,则使用当前登录用户的Id;
  • RelationType:关系类型,如:“attach”为签约,“follow”为关注,可自定义。

对于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; }

}

  • EntityUserIdIdiom:语义上的UserId,用于指定业务实体中用于描述“用户Id”字段的名称,若不指定,则默认为“UserId”;
  • RelationFromUserId:反向关系用户Id,若为Guid.Empty,则使用当前登录用户的Id;
  • RelationType:关系类型,如:“attach”为签约,“follow”为关注,可自定义。

对于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接口实现上并非互斥。

请注意,可应用过滤的条件为:

  1. input需实现IRelationToOrientedFilter接口;
  2. 实体必须关联用户。

否则将原封不动返回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如下图:

在这里插入图片描述

标签:string,userId,用户,优雅,var,查改,filteredInput,增删,Guid
From: https://www.cnblogs.com/jevonsflash/p/17564676.html

相关文章

  • 【测试联调】如何在前后端测试联调时优雅的构造异常场景
    背景当前的应用都使用了前后端分离的架构,前后端系统需要协同以实现各种功能。后端系统通常负责处理业务逻辑、数据存储和与其他服务的交互,而前端则负责用户界面和用户交互。而在前后端数据交互的过程中,各种异常和错误都有可能发生,确认异常发生时前后端系统的处理是否合理是测试验......
  • jfinal 框架学习笔记-第三天 Model相关学习--record+Model增删改查的用法(震惊之今日刷
    1.了解了数据库连接池。其中使用最多也是最广泛的是druid数据库连接池也就是阿里云研发的数据库连接池2.ActiveRecord(jFinal的核心技术)+DruidPlugin(数据库连接词,如何与数据库打交道)ActiveRecord:1.Record(记录,相当于一个通用的Model),2.Model(提供日常CRUD的封装)Model示例......
  • 怎样优雅地增删查改(七):按用户查询
    @目录实现使用测试实现定义按用户查询(IUserOrientedFilter)接口publicinterfaceIUserOrientedFilter{publicstringEntityUserIdIdiom{get;}Guid?UserId{get;set;}}EntityUserIdIdiom:语义上的UserId,用于指定业务实体中用于描述“用户Id”字段的名称,......
  • 数据核对之找出增删数据
    1、查询表结构,获取主键信息SHOWCREATETABLE表名;2、创建临时表并写入备份数据createtable临时表asselect*from主表whereexpdate>='开始时间'andexpdate<'结束时间';3、重新从源系统采集数据至主表中4、临时表中有的数据,但在主表中没有,即为源端删除的......
  • ElasticSearch-Mapping类型映射-增删改查
    https://www.elastic.co/guide/en/elasticsearch/reference/6.8/mapping.html7.x版本后默认都是_doc类型增加Mapping映射先说一个特殊的字段_all:https://www.elastic.co/guide/en/elasticsearch/reference/6.8/mapping-all-field.html#mapping-all-field_all字段是一个特......
  • 后台管理系统基础功能(增删改查)用例大全
    为方便平时写测试用例,整理如下:转自:https://www.cnblogs.com/wysk/archive/2018/01/05/8193091.html功能条件测试步骤测试数据预期结果备注搜索或查询 单独遍历各查询条件,测试按各查询条件是否都能够查询出相应的值. 查询出符合条件的记录  设置......
  • 洛谷 P6667 [清华集训2016] 如何优雅地求和
    洛谷传送门点值不好搞。考虑把它搞成系数一类的东西。由二项式反演,\(f(x)=\sum\limits_{i=0}^x\binom{x}{i}b_i\Leftrightarrowb_i=\sum\limits_{j=0}^i\binom{i}{j}(-1)^{i-j}f(j)\)。然后我们要求:\[\sum\limits_{k=0}^n\sum\limits_{i=0}^ms_i\bino......
  • 怎样优雅地增删查改(五):按组织架构查询
    @目录原理实现应用测试之前我们实现了Employee,Alarm管理模块以及通用查询应用层。Employee的集合查询业务,是通过重写CreateFilteredQueryAsync方法,来实现按组织架构查询的过滤条件。我们将这段逻辑代码提取到通用查询应用层中,便可实现在任何业务的按组织架构查询。原理Employ......
  • Java空指针异常优雅处理的方式
    1原因如下:由于Java开发过程中一不注意就会造成空指针异常,但是如果要避免这些空指针异常我们就可能需要写如下啰嗦有无聊的语句:if(test!=null&&test.size()>0){..............}为了避免写这些无聊的语句和避免NPE错误,我们可以用如下用法进行替代。......
  • 怎样优雅地增删查改(四):创建通用查询基类
    @目录创建通用查询抽象层创建通用查询应用层基类创建通用查询控制器基类[可选]替换RESTfulApi扩展泛型参数服务的“渐进式”使用上一章我们实现了Employee管理模块,Employee的增删改查是通过其应用服务类,继承自Abp.Application.Services.CrudAppService实现的。我们将封装通用的......