番外篇--Moddule Zero 版本管理與組織單位管理


返回總目錄:ABP+AdminLTE+Bootstrap Table權限管理系統一期

Moddule Zero 版本管理

2.2.1 簡介

大多數SaaS(多租戶)應用都會有多個版本(包),這些版本的功能點也會各不相同。因此,他們能夠為他們的租戶(客戶)提供不同的價格和功能點選項。

關於功能點

請查閱功能點管理以理解功能點的概念

2.2.2 版本實體

版本是一個簡單的實體,代表了一個應用的版本(包)。它有Name(名稱)和DisplayName(顯示名稱)屬性。

2.2.3 版本管理器

版本管理器是一個用來管理版本的領域服務:

public class EditionManager : AbpEditionManager
{
}

它繼承自AbpEditionManager類。你可以注入並用版本管理器來創建、刪除、更新版本。而且,版本管理器也被用於管理版本的功能點。它的內部緩存了版本的功能點以實現更好的性能。

Moddule Zero 版本管理

2.4.1 簡介

組織單位(OU)用於給組用戶和實體分層

2.4.2 組織單位實體

一個OU由組織單位實體來展現。該實體基本的屬性如下:

  • TenantId:OU的租戶Id,host的組織單位的TenantId可以為空。
  • ParentId:父OU的Id,根OU的ParentId可以為空。
  • Code:租戶下唯一的層級字符串。
  • DisplayName:OU的顯示名稱。

組織單位實體的主鍵(Id)是長整型,該實體從FullAuditedEntity繼承,FullAuditedEntity提供了審計信息並實現了軟刪除接口。

1. 組織單位樹

由於一個OU可以有父級OU,一個租戶下所有的OU是樹形結構,該樹有如下規則:

  • 可以有多個根節點(根節點的ParentId為空)
  • 樹的最大深度被定義為一個常量OrganizationUnit.MaxDepth,它的值是16。
  • 第一層級子OU數有限制(由於固定的OU編碼單位長度的原因,下面有相關解釋)

2. OU編碼

OU編碼有組織單位管理器自動創建和維護,OU編碼是類似下面的字符串:
"00001.00042.00005"

該編碼可以輕易實現遞歸查詢數據庫中所有的子OU,該編碼有如下規則:

  • 對於某個租戶來說,它是唯一的。
  • 一個父OU的所有子OU的編碼都以父OU的編碼開頭。
  • 它的固定長度基於樹中的OU層級決定,參見示例代碼。
  • 盡管OU編碼是唯一的,但是在你移動一個OU的情況下它能被改變,因此我們應該通過OU的Id來引用OU,而不是OU的編碼。

2.4.3 組織單位管理器

組織單位管理器類可以被注入並用於管理OU,常見用例如下:

  • 創建、更新、刪除一個OU。
  • 在OU樹中移動一個OU。
  • 獲取OU樹及節點的信息。

1. 多租戶

組織單位管理器被設計用來為單租戶使用,它默認為當前租戶使用。

2.4.4 常見用例

以下是OU的常見用例,相關示例的源代碼請點這里

1. 創建屬於某個組織單位的實體

OU最明顯的用例是將實體分配給某個OU,例如:

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

我們簡單地創建了OrganizationUnitId這個屬性用於將實體分配給某個OU。IMustHaveOrganizationUnit接口定義了OrganizationUnitId屬性。
我們不是必須要實現這個接口,但是它被建議實現來作為一個標准。另外還有一個IMayHaveOrganizationId接口,這個接口定義了可為空的OrganizationUnitId屬性。

現在我們就可以把一個產品關聯到某個OU並且查詢某個指定OU的相關產品了。

注意:在多租戶應用中,產品實體擁有一個TenantId屬性(它是IMustHaveTenant接口的一個屬性)來區分不同租戶的產品。如果你的應用不是多租戶的應用,你就不需要這個接口及其相關的屬性。

2. 獲取某個組織單位內的實體

獲取某個組織單位內的實體很簡單,請看如下領域服務中的示例代碼:

public class ProductManager : IDomainService
{
    private readonly IRepository<Product> _productRepository;

    public ProductManager(IRepository<Product> productRepository)
    {
        _productRepository = productRepository;
    }

    public List<Product> GetProductsInOu(long organizationUnitId)
    {
        return _productRepository.GetAllList(p => p.OrganizationUnitId == organizationUnitId);
    }
}

像上面代碼里那樣,我們只需要簡單地加一個謂詞Product.OrganizationUnitId。

3. 獲取某個組織單位及其子組織單位內的實體

當我們想獲取某個組織單位及其子組織單位內的實體的時候,OU編碼就可以幫我們實現這個功能了。

public class ProductManager : IDomainService
{
    private readonly IRepository<Product> _productRepository;
    private readonly IRepository<OrganizationUnit, long> _organizationUnitRepository;

    public ProductManager(
        IRepository<Product> productRepository, 
        IRepository<OrganizationUnit, long> organizationUnitRepository)
    {
        _productRepository = productRepository;
        _organizationUnitRepository = organizationUnitRepository;
    }

    [UnitOfWork]
    public virtual List<Product> GetProductsInOuIncludingChildren(long organizationUnitId)
    {
        var code = _organizationUnitRepository.Get(organizationUnitId).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();
    }
}

首先,我們獲取指定的OU的編碼,然后我們創建一個帶join和StartsWith(code)條件的LINQ查詢(StartsWith創建了sql里的like查詢)。因此,我們能夠向下按層級獲取一個OU及其子OU所屬的產品。

4. 過濾某個用戶的實體

我們可能希望獲取某個用戶所屬的OU(一個或多個)內的所有產品,代碼如下:

public class ProductManager : IDomainService
{
    private readonly IRepository<Product> _productRepository;
    private readonly UserManager _userManager;

    public ProductManager(
        IRepository<Product> productRepository, 
        UserManager userManager)
    {
        _productRepository = productRepository;
        _organizationUnitRepository = organizationUnitRepository;
        _userManager = userManager;
    }

    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));
    }
}

我們通過簡單地查詢獲取了用戶所屬OU的Id列表,然后我們使用Contains條件來查詢產品。當然,我們可以創建LINQ關聯查詢來獲取相同的列表。

我們也可能會希望獲取某個用戶所屬的OU(一個或多個)內及其子OU內的所有產品

public class ProductManager : IDomainService
{
    private readonly IRepository<Product> _productRepository;
    private readonly IRepository<OrganizationUnit, long> _organizationUnitRepository;
    private readonly UserManager _userManager;

    public ProductManager(
        IRepository<Product> productRepository, 
        IRepository<OrganizationUnit, long> organizationUnitRepository, 
        UserManager userManager)
    {
        _productRepository = productRepository;
        _organizationUnitRepository = organizationUnitRepository;
        _userManager = userManager;
    }

    [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();
    }
}

我們在LINQ關聯查詢聲明中組合使用了Any和StartsWith條件。
當然,可能會有更復雜的需求,但是都能用LINQ和SQL來實現。

2.4.5 設置

我們可以注入並使用IOrganizationUnitSettings接口來獲取組織單位設置值。當前,只有一個設置能被按需改變:

  • MaxUserMembershipCount:一個用戶所屬組織單位的數量的最大值。
  • 默認值是int.MaxValue,即無限制。
  • 設置的名稱是一個常量,被定義在AbpZeroSettingNames.OrganizationUnits.MaxUserMembershipCount中。

你可以通過設置管理改變設定值


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM