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,望技術有成後能回來看見自己的腳步