多租户系统中,针对于不同租户开放不同功能,或是按照不同功能进行收费管理,需要从宿主本身去管理租户的版本信息,如同酒店人员对不同房间收取不同费用,依据房间内部设施,房间大小等设置不同收费标准。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,望技术有成后能回来看见自己的脚步

X-Admin&ABP框架开发-版本管理的更多相关文章

  1. X-Admin&ABP框架开发-消息通知

    业务型网站使用过程中,消息通知是一个不可或缺的功能,采用站内通知.短信通知.邮件通知.微信通知等等各种方式都有,ABP框架对这部分工作已经封装的很好了,站在巨人的肩膀上,一览全貌,带来的就是心情舒畅. ...

  2. X-Admin&ABP框架开发-系统日志

    网站正常运行中有时出现异常在所难免,查看系统运行日志分析问题并能够根据错误信息快速解决问题尤为重要,ABP对于系统运行日志这块已经做了很好的处理,默认采用的Log4Net已经足够满足开发过程中的需要了 ...

  3. X-Admin&ABP框架开发-代码生成器

    在日常开发中,有时会遇到一些相似的代码,甚至是只要CV一次,改几个名称,就可以实现功能了,而且总归起来,都可以由一些公用的页面更改而来,因此,结合我日常开发中使用到的页面,封装一个适合自己的代码生成器 ...

  4. X-Admin&ABP框架开发-设置管理

    在网站开发中,设置是不可缺少的一环,如用户设置.系统设置.甚至是租户设置等.ABP对于设置的管理已经做了很好的处理,我们可以借助巨人的力量来完成我们的冒险. ABP官网地址:https://aspne ...

  5. X-Admin&ABP框架开发-RBAC

    在业务系统需求规划过程中,通常对于诸如组织机构.用户和角色等这种基础功能,通常是将这部分功能规划到通用子域中,这也说明了,对于这部分功能来讲,是系统的基石,整个业务体系是建立于这部分基石之上的,当然, ...

  6. X-Admin&ABP框架开发-数据字典

    在业务型的系统开发中,我们需要维护各种个样的类型,比如客户类型.客户行业.商品类型等等,这些类型往往信息量不多,并且相似度极高,如果采用一类型一表去设计,将会造成极大的工作量,通过将这部分类型的信息进 ...

  7. X-Admin&ABP框架开发-租户管理

    软件即服务概念的推动,定制化到通用化的发展,用一套代码完成适应不同企业的需求,利用多租户技术可以去做到这一点.ABP里提供了多租户这一概念并且也在Zero模块中实现了这一概念. 一.多租户的概念 单部 ...

  8. 高薪诚聘熟悉ABP框架的.NET高级开发工程师(2016年7月28日重发)

    招聘单位是ABP架构设计交流群(134710707)群主阳铭所在的公司-上海运图贸易有限公司 招聘岗位:.NET高级开发工程师工作地点:上海-普陀区 [公司情况]上海运图贸易有限公司,是由易迅网的创始 ...

  9. ABP框架实践基础篇之开发UI层

    返回总目录<一步一步使用ABP框架搭建正式项目系列教程> 说明 其实最开始写的,就是这个ABP框架实践基础篇.在写这篇博客之前,又回头复习了一下ABP框架的理论,如果你还没学习,请查看AB ...

随机推荐

  1. swoole模块的编译安装:php编译安装swoole模块的代码

    本篇文章给大家带来的内容是关于swoole模块的编译安装:php编译安装swoole模块的代码,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助. 1.下载swoole 1 wget ht ...

  2. 简单配置Vue路由

    简单配置Vue路由 1.  创建一个单文件组件Test.vue <template> <div>Test</div> </template> <s ...

  3. JavaScript的自调用函数

    函数表达式可以 "自调用". 自调用表达式会自动调用. 如果表达式后面紧跟 () ,则会自动调用. 不能自调用声明的函数. 通过添加括号,来说明它是一个函数表达式: <scr ...

  4. Spring MVC系列-(2) Bean的装配

    2. Bean的装配 Spring容器负责创建应用程序中的bean,并通过DI来协调对象之间的关系.Spring提供了三种主要的装配机制: XML显式配置: Java配置类进行显式配置: 隐式的bea ...

  5. css报模块没找到 分析思路 从后往前找,先定位最后blue.less 解决:iview升级4.0 css没改导致编译不过去

    E:\xxx\xxx\xxx\../../../../../../../E:/xxx/xxx/xxx/node_modules/_iview@3.5.4@iview/src/styles/common ...

  6. react build本地相对目录 "homepage": ".", package.json

    react build相对目录 "homepage": ".", package.json

  7. vmware导入ova文件踩坑记

    问题来源 众所周知,所有的网络行为都会产生相应的网络流量,那么所有的网络攻击行为也有其对应的流量特点,那么是否能根据流量特点进而分析出其对应的是什么攻击行为呢? 我在虚拟机上使用vulnhub的靶场环 ...

  8. Development of a High Coverage Pseudotargeted Lipidomics Method Based on Ultra-High Performance Liquid Chromatography−Mass Spectrometry(基于超高效液相色谱-质谱法的高覆盖拟靶向脂质组学方法的开发)

    文献名:Development of a High Coverage Pseudotargeted Lipidomics Method Based on Ultra-High Performance ...

  9. MySQL笔记(8)-- 索引类型

    一.背景 前面我们讲了SQL分析和索引优化都涉及到了索引,那么什么是索引,它的模型有什么,实现的机制是什么,今天我们来好好讨论下. 二.索引的介绍 索引就相当书的目录,比如一本500页的书,如果你想快 ...

  10. BIT-Reverse Pairs

    2019-12-17 11:07:02 问题描述: 问题求解: 本题可以看作是逆序数问题的强化版本,需要注意的是num[i] > 2 * num[j],这里有0和负数的情况. public in ...