1. 程式人生 > >X-Admin&ABP框架開發-版本管理

X-Admin&ABP框架開發-版本管理

  多租戶系統中,針對於不同租戶開放不同功能,或是按照不同功能進行收費管理,需要從宿主本身去管理租戶的版本資訊,如同酒店人員對不同房間收取不同費用,依據房間內部設施,房間大小等設定不同收費標準。Abp系統中預設是多租戶的,並且在Zero模組中實現了版本管理功能。 

   演示地址:http://119.3.138.127/,更改Account/HostLogin進入宿主管理

 

一、設計前提

  基於Abp進行了相關限制,我將多租戶變成了單租戶,不允許新增新的租戶,由於日常接觸中,發現除了雲平臺這種SaaS需要多租戶,對於企業客戶來講,自備物理伺服器或自購雲伺服器是常有的事情,因此對於該部分客戶而言,多租戶也就沒有太多意義,但是從軟體公司本身考慮,一套軟體能夠銷售多家客戶,能夠通過簡單配置,開放關閉某些功能,以此來適應客戶功能需求,是最佳選項了。因此對於這兩種情況考慮後,對於本系統而言,採用的是單租戶+宿主形式的,企業客戶使用單租戶,宿主形式留給軟體公司方便配置單租戶實際需要的功能。

  在Zero中,已經預設實現了版本管理,但是對於非收費版本的頁面管理,應用服務等沒有具體程式碼實現。

 

二、版本管理

1、應用層增加版本應用服務,對於版本需要進行的使用者操作歸納為三個。

  • 可檢視現有版本列表;
  • 可對現有版本資訊及版本擁有的功能項進行編輯更改;
  • 可增加或是刪除版本;
/// <summary>
/// 版本管理應用服務介面
/// </summary>
public interface IEditionAppService : IApplicationService
{
    /// <summary>
    /// 獲取全部版本列表
    /// </summary>
    /// <returns></returns>
    Task<ListResultDto<EditionListDto>> GetEditionsList();

    /// <summary>
    /// 獲取版本用於編輯
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    Task<GetEditionForEditOutput> GetEditionForEdit(NullableIdDto input);

    /// <summary>
    /// 建立或更新版本
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    Task CreateOrUpdateEdition(CreateOrUpdateEditionInput input);

    /// <summary>
    /// 刪除版本
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    Task DeleteEdition(EntityDto input);

    /// <summary>
    /// 租戶更換版本
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    Task MoveTenantsToAnotherEdition(MoveTenantsToAnotherEditionDto input);
}

2、實現應用服務

實現版本應用服務介面,以建立版本為例,頂部許可權驗證,增加版本資訊,並且設定該版本所擁有的功能項,對於版本領域服務,Zero模組提供了完整實現AbpEditionManager,只需呼叫即可。

[AbpAuthorize(PermissionNames.Pages_Frame_Editions_Create)]
private async Task CreateEdition(CreateOrUpdateEditionInput input)
{
    var edition = ObjectMapper.Map<Edition>(input.Edition);

    await _editionManager.CreateAsync(edition);
    await CurrentUnitOfWork.SaveChangesAsync();

    await SetFeatureValues(edition, input.FeatureValues);
}

private Task SetFeatureValues(Edition edition, List<NameValueDto> featureValues)
{
    return _editionManager.SetFeatureValuesAsync(edition.Id, featureValues.Select(fv => new NameValue(fv.Name, fv.Value)).ToArray());
}

3、控制器中增加版本管理控制器並設計相應的方法(逐漸認識到,能夠使用Dto的儘量使用Dto),控制器部分主要承擔路由功能,將前端請求轉發到應用服務、領域服務中。

 4、頁面層實現,列表展示+彈框新增/編輯,彈框內展示出功能項樹結構,供版本配置需要的功能項。

 5、頁面功能展示,列表展示現有版本,因考慮到版本數量不會很多,無需分頁也無需條件查詢。該部分選單僅對宿主提供

 

三、版本功能管理

   對於功能項,ABP框架中採用宣告式,現在領域層中宣告具體的功能項,在程式碼中,依照當前租戶是否存在宣告的某個功能項去決定是否執行某個功能,

1、功能項宣告,在Core層->Features資料夾中宣告該系統擁有的功能,需要對租戶進行控制劃分的。如客戶服務模組,對於小部分企業客戶而言,可能不需要該模組,則可通過對客戶服務模組進行功能控制,頁面上,程式碼中該部分功能都會繞過去。

/// <summary>
/// 功能管理
/// </summary>
public static class AppFeatures
{
    public const string HostSettings = "App.HostSettings";

    public const string CustomerService = "App.CustomerService";
}

2、功能繫結到系統中,在Features資料夾中,AppFeatureProvider負責將宣告的功能繫結到系統中,可以對功能項進行預設設定,如對於客戶服務要預設為都具有,可以更改defaultValue設定為true,具體更豐富的設計檢視Abp提供的過載方法。可對功能項進行樹結構設計。

/// <summary>
/// 功能設定提供器
/// </summary>
public class AppFeatureProvider : FeatureProvider
{
    public override void SetFeatures(IFeatureDefinitionContext context)
    {
        var hostSettings = context.Create(
            AppFeatures.HostSettings,
            defaultValue: "false",
            displayName: L("HostSettings"),
            inputType: new CheckboxInputType()
        );

        var customerService = context.Create(
            AppFeatures.CustomerService,
            defaultValue: "false",
            displayName: L("CustomerService"),
            inputType: new CheckboxInputType()
        );

        var customerServiceMaps = customerService.CreateChildFeature(
            AppFeatures.CustomerService_Maps,
            defaultValue: "false",
            displayName: L("CustomerServiceMaps"),
            inputType: new CheckboxInputType()
        );
    }

    private ILocalizableString L(string name)
    {
        return new LocalizableString(name, SurroundConsts.LocalizationSourceName);
    }
}

3、版本管理中關聯功能項,在版本管理頁面,編輯版本資訊彈框內右側tab頁功能項樹結構,可檢視系統已有功能,並通過勾選形式確定該版本需要的功能。

 

四、租戶版本管理

  軟體公司可以在發售給客戶的軟體中預先配置好幾個版本,方便部署實施時,更改租戶使用的版本即可完成功能劃分。預設使用的是單個租戶,因此,對於租戶的增刪操作直接pass掉了。對於Abp提供的多租戶的管理部分程式碼進行相關更改,適應單租戶的一些操作。

  • 可檢視當前租戶資訊;
  • 可切換租戶使用版本;

1、在已有租戶應用服務中更改已有程式碼,取消原有繼承的CRUD服務,實現獲取租戶列表,更改版本等幾個操作。

/// <summary>
/// 租戶應用服務
/// </summary>
public interface ITenantAppService : IApplicationService
{
    /// <summary>
    /// 獲取單個租戶
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    Task<TenantListDto> GetTenant(EntityDto<int> input);

    /// <summary>
    /// 獲取全部租戶列表
    /// </summary>
    /// <returns></returns>
    Task<ListResultDto<TenantListDto>> GetTenantsList();

    /// <summary>
    /// 移動當前租戶版本到其它版本
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    Task MoveTenantToAnotherEdition(MoveTenantToAnotherEditionInput input);
}

2、實現替換的幾個租戶應用服務介面方法,此處僅展示切換租戶版本。完成頂部許可權驗證,版本引數驗證,租戶版本修改。

[AbpAuthorize(PermissionNames.Pages_Frame_Tenants_MoveTenantToAnotherEdition)]
public async Task MoveTenantToAnotherEdition(MoveTenantToAnotherEditionInput input)
{
    if (input.SourceEditionId == input.TargetEditionId)
    {
        throw new UserFriendlyException("原版本與目標版本一致,無需切換");
    }

    var tenant = await _tenantManager.GetByIdAsync(input.TenantId);
    tenant.EditionId = input.TargetEditionId;
    await _tenantManager.UpdateAsync(tenant);
}

 3、租戶控制器層面已經存在了相關程式碼,改造部分程式碼並完成頁面實現,頁面實現中主要是彈框內列舉出當前系統已有的版本列表資訊,方便切換版本。

 4、頁面效果實現,版本切換操作實現。

 

五、選單許可權控制

  對於諸如版本管理、租戶管理這部分選單僅能夠對宿主進行開放,因此在許可權列表中對該部分許可權進行控制,在導航選單中會依據是否有許可權進行過濾選單。許可權中使用命名引數multiTenancySides設定為僅宿主使用。

#region 版本管理
var editions = frame.CreateChildPermission(PermissionNames.Pages_Frame_Editions, L("Editions"), multiTenancySides: MultiTenancySides.Host);
editions.CreateChildPermission(PermissionNames.Pages_Frame_Editions_Create, L("CreateEdition"), multiTenancySides: MultiTenancySides.Host);
editions.CreateChildPermission(PermissionNames.Pages_Frame_Editions_Update, L("UpdateEdition"), multiTenancySides: MultiTenancySides.Host);
editions.CreateChildPermission(PermissionNames.Pages_Frame_Editions_Delete, L("DeleteEdition"), multiTenancySides: MultiTenancySides.Host);
editions.CreateChildPermission(PermissionNames.Pages_Frame_Editions_MoveTenantsToAnotherEdition, L("MoveTenantsToAnotherEdition"), multiTenancySides: MultiTenancySides.Host);
#endregion

#region 租戶管理
var tenants = frame.CreateChildPermission(PermissionNames.Pages_Frame_Tenants, L("Tenants"), multiTenancySides: MultiTenancySides.Host);
tenants.CreateChildPermission(PermissionNames.Pages_Frame_Tenants_MoveTenantToAnotherEdition, L("MoveTenantsToAnotherEdition"), multiTenancySides: MultiTenancySides.Host);
#endregion

  對於宿主登入,在主頁面設定了兩個入口,如登入時使用Account/HostLogin則為宿主登入,否則為租戶登入。

 

 倉庫地址:https://gitee.com/530521314/Partner.Surround.git 

2020-04-12,望技術有成後能回來看見自己的腳步