• 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,望技术有成后能回来看见自己的脚步
  • 相关阅读:
    EntityFramework中的线程安全,又是Dictionary
    记一次w3wp占用CPU过高的解决过程(Dictionary和线程安全)
    这一个月
    使用Nginx解决IIS绑定域名导致应用程序重启的问题
    Bootstrap for MVC:Html.Bootstrap().TextBoxFor(model=>model.Name)
    Orchard 刨析:Logging
    Orchard 刨析:Caching
    Orchard 刨析:前奏曲
    Orchard 刨析:导航篇
    数据集
  • 原文地址:https://www.cnblogs.com/CKExp/p/12682687.html
Copyright © 2020-2023  润新知