Loading

AspnetBoilerplate (ABP) Organization Units 组织结构管理

ABP是一个成熟的.NET框架,功能完善。目前由于项目需要正在自学中。

ABP对于组织节点管理这一基本上每个项目都要反复重复开发的内容,进行了自己的实现。

主要包括这些常用功能:

  • 多租户
  • 树结构管理的实体
  • 与用户系统集成的查询

下面我们根据官方文档以及DEMO去学习这个功能。

不过需要注意的是,ABP默认没有提供展示层的实现,这一块就需要自己实现了。

官方文档理解

 OrganizationUnit 实体定义

  • TenantId: 租户ID,如果为null则是host的组织节点。(具体概念参阅多租户)
  • ParentId: 父节点Id,如果为null则是根节点。
  • Code: 一个拼接的虚拟路径字符串代码,在租户内唯一。
  • DisplayName: 显示名称

Organization Tree

模型定义中的ParentId使得这个数据结构定义了一个典型的父子树。

  • 这个树允许有多个根节点
  • 树的最大深度是OrganizationUnit.MaxDepth,值为16
  • 每一级子节点的数目也有限制,主要是由于后面要提到的OU Code定义决定的。

OU Code

OU Code由OrganizationUnit Manager自动维护,它是类似于"00001.00042.00005"的字符串。它可以用于递归查询。

这种字段在树结构中是很必须的,如果没有它,树查询会变成效率的杀手。有了这类虚拟路径,可以通过分隔符分解后批量查询。

Abp对OU Code有以下规则:

  •  在一个租户中唯一
  • 子节点的Code需要以父节点的Code开头
  • Code的长度,由层级深度决定
  • OU Code可以被改变,例如移动节点
  • 我们需要使用Id作为OU引用的字段,而不是Code

OrganizationUnit Manager

  • OrganizationUnitManager 通过依赖注入引入,一般用于:
  • 增、删、改OU
  • 移动OU
  • 读取OU信息,以及OU的items

Multi-Tenancy

OrganizationUnitManager 一次只能操作一个租户,默认租户为当前租户。

样例代码分析

首先创建一个实体,派生自IMustHaveTenant , IMustHaveOrganizationUnit

 

public class Product : Entity, IMustHaveTenant, IMustHaveOrganizationUnit
{
    public virtual int TenantId { get; set; }

    public virtual long OrganizationUnitId { get; set; }
    
    public virtual string Name { get; set; }

    public virtual float Price { get; set; }
}

  

实现Service:

public class ProductManager : IDomainService
{
	//实体仓储,实体继承自IMustHaveOrganizationUnit
	private readonly IRepository<Product> _productRepository;
	//OU仓储,通过此仓储读取OU
	private readonly IRepository<OrganizationUnit, long> _organizationUnitRepository;
	//用户数据Manager
	private readonly UserManager _userManager;

	//构造函数,DI注入
	public ProductManager(
		IRepository<Product> productRepository, 
		IRepository<OrganizationUnit, long> organizationUnitRepository, 
		UserManager userManager)
	{
		_productRepository = productRepository;
		_organizationUnitRepository = organizationUnitRepository;
		_userManager = userManager;
	}

	//根据组织节点,获取关联的Product
	public List<Product> GetProductsInOu(long organizationUnitId)
	{
		return _productRepository.GetAllList(p => p.OrganizationUnitId == organizationUnitId);
	}
	
	//根据组织节点Id查询所有的Products,包含子Product
	[UnitOfWork]//UnitOfWork支持事务
	public virtual List<Product> GetProductsInOuIncludingChildren(long organizationUnitId)
	{
		//根据组织节点id,获取code
		var code = _organizationUnitRepository.Get(organizationUnitId).Code;

		//查询组织节点开头的所有节点,这样避免了递归查询,提升了效率,也是Code定义的目的所在
		var query =
			from product in _productRepository.GetAll()
			join organizationUnit in _organizationUnitRepository.GetAll() on product.OrganizationUnitId equals organizationUnit.Id
			where organizationUnit.Code.StartsWith(code)
			select product;

		return query.ToList();
	}

	//根据用户查询Product
	//查询用户关联的组织节点,再根据组织节点,查询关联的Product
	public async Task<List<Product>> GetProductsForUserAsync(long userId)
	{
		var user = await _userManager.GetUserByIdAsync(userId);
		var organizationUnits = await _userManager.GetOrganizationUnitsAsync(user);
		var organizationUnitIds = organizationUnits.Select(ou => ou.Id);

		return await _productRepository.GetAllListAsync(p => organizationUnitIds.Contains(p.OrganizationUnitId));
	}

	//同上个函数类似,查询中加入了子节点
	[UnitOfWork]
	public virtual async Task<List<Product>> GetProductsForUserIncludingChildOusAsync(long userId)
	{
		var user = await _userManager.GetUserByIdAsync(userId);
		var organizationUnits = await _userManager.GetOrganizationUnitsAsync(user);
		var organizationUnitCodes = organizationUnits.Select(ou => ou.Code);

		var query =
			from product in _productRepository.GetAll()
			join organizationUnit in _organizationUnitRepository.GetAll() on product.OrganizationUnitId equals organizationUnit.Id
			where organizationUnitCodes.Any(code => organizationUnit.Code.StartsWith(code))
			select product;

		return query.ToList();
	}
}

  

  

通过这段源码我们发现,其实在Abp模板中Zero模块已经默认添加了用户与组织节点的关联,如下图:

 

OrganizationUnits表是一个父子树结构,表达了我们系统中所有需要以父子树表达的逻辑结构。

实体表User,Product通过一张关联表与组织节点关联,关联关系如E-R图所示。

在数据库中,abp并没有创建外键联系,这应该是为了高复用OU表。

其他设置

你可以通过 AbpZeroSettingNames.OrganizationUnits.MaxUserMembershipCount 来设置一个用户的最大OU关联数。

posted @ 2018-12-25 20:12  wswind  阅读(2920)  评论(0编辑  收藏  举报