多租户下的系统业务开发过程探讨

发布时间:2026/5/19 20:58:26

多租户下的系统业务开发过程探讨 设计数据库万层高楼从底起开发应用项目数据库的设计很重要它可能是业务对象业务流程的综合设计好的数据库设计可以减少后期的重复返工提高开发效率。表名前缀区分一般表名称根据不同的业务关系我们可以使用不同的前缀进行区分使用前缀可以非常方便区分不同的业务表如我们可以一般基础表使用 “TB_” 定义前缀基础设施的系统表使用SYS_定义前缀等。按照业务区分比较可以在很多表的时候让我们快速查找到对应的表名。数据库的模型设计我们建议在第三方的数据库设计工具上进行设计如PowerDesigner这样的设计工具使用工具设计数据库有很多好处一个是可以高效率进行调整二是根据需要生成不同的数据库类型Sql语句三是可以全局了解各个表之间的关系等等。使用PowerDesigner这样的数据库设计工具能够在很大程度上提高我们数据库的设计效率。多租户系统设计原则只要是业务表一律带 tenant_id 字段并且查询时需要根据租户过滤。什么时候可以不加 tenant_id? 只有一种情况全局表,这种是 平台共享数据不属于某个租户吗如租户表字典表、参数表、系统配置表、菜单表等。系统推荐ID方案大多数 ERP 系统推荐主键IDBIGINT 业务编码VARCHAR主键ID使用分布式IDSnowflake Leaf Sonyflake生成 64bit BIGINT178923741239123特点全局唯一有时间顺序仍然是 BIGINT因此使用BIGINT Snowflake 方案。结构id BIGINT PRIMARY KEY生成Snowflake ID有了这些基础的规则我们就可以开始使用PowerDesigner建模工具设计数据库了如下所示。根据我们前面介绍的基础表设计整理了相关的设计模型如下所示。ERP仓库管理的业务表比较多先做好基础内容后续再继续分析介绍。这次我设计的数据库是基于MySQL来设计的方便以后迁移到PythonFastAPISqlAlchemy上更方便。由于PowerDesigner可以生成相关的表SQL我们一次性设计好后导出相关的SQL表为后续创建表准备。然后在数据库管理工具 Navicat Premium 17或更高版本均可上进行数据库表的生成和管理即可。前期我们现在粗略准备好相关的表后续可以对表增加更多的约束如唯一性索引等要求进一步提高安全性和性能即可。2、项目框架的生成设计好数据库后我们通过代码生成工具进行整个项目框架的生成这样对于我们在开发新项目上有很好的好处里面的项目层级、类之间的引用关系已经处理好了这样对我们处理起来就非常方便。要了解项目的快速生成需要从官网下载《代码生成工具Database2Sharp》工具后进行使用该工具支持多种框架的项目开发我们这里以《SqlSugar开发框架》的项目生成为例介绍。《SqlSugar开发框架》项目框架后端为.netcore的WebAPI服务端同时可以支持多终端的项目框架我们先利用工具来生成后端所需要的基础和BS管理前端后续需要再进行扩展即可基于Web API可以对接多个终端。我们利用代码生成工具来进行后端代码的生成代码生成工具支持把表和字段的备注信息生成到类和属性的注释代码表和字段小写自动转换为对应的Pascal命名模式。选择所有的表生成设置代码生成的表过滤前缀名称和项目主命名空间最终生成的项目文件如下所示包括接口层、实体模型层、服务层。其中的基类通过泛型的方式封装抽象了大多数常规的增删改查接口在代码生成工具的智能处理下会结合数据库的字段和备注信息生成相关的函数内容我们生成后进行一定的微调即可。位置Core/Interface/IOperationLogService.csnamespace WHC.SugarProject { /// summary /// 操作日志表应用层服务接口定义 /// /summary public interface IOperationLogService : IMyCrudServiceOperationLogInfo, long, OperationLogPagedDto, ITransientDependency { } }位置Core/Modal/OperationLogInfo.csnamespace WHC.SugarProject { /// summary /// 操作日志表 /// /summary [SugarTable(sys_operation_log)] public class OperationLogInfo : Entitylong { /// summary /// 默认构造函数需要初始化属性的在此处理 /// /summary public OperationLogInfo() { this.Id Yitter.IdGenerator.YitIdHelper.NextId(); //雪花ID生成 this.CreatedAt DateTime.Now; } #region Property Members /// summary /// 主键ID /// /summary [SugarColumn(IsPrimaryKey true)] public override long Id { get; set; } /// summary /// 租户ID /// /summary //[Required] [SqlSugar.SugarColumn(ColumnName tenant_id)] public virtual long? TenantId { get; set; } /// summary /// 用户ID /// /summary //[Required] [SqlSugar.SugarColumn(ColumnName user_id)] public virtual long? UserId { get; set; } 0; /// summary /// 操作用户名 /// /summary //[Required] public virtual string Username { get; set; } ....位置Core/Service/OperationLogService.csnamespace WHC.SugarProject { /// summary /// 操作日志表 应用层服务接口实现,。 /// 注意由于MyCrudService需要引用IOperationLogService来记录操作日志因此这里本尊的基类只能是BaseCrudService否则会导致循环引用。 /// /summary public class OperationLogService : BaseCrudServiceOperationLogInfo,long, OperationLogPagedDto, IOperationLogService { /// summary /// 构造函数 /// /summary public OperationLogService(IApiUserSession currentApiUser) { this.CurrentApiUser currentApiUser; } ......而一般Service层我们生成代码的时候会提供几个重写的函数处理默认增加了一些对应的内容如下所示。在我们提供的Service层的重写函数中对于基础条件过滤是一个很重要的规则重写如对于多租户来说我们请求的数据仅限于当前租户的数据范围因此需要进行数据范围的过滤如下函数所示。/// summary /// 子类重写最高一级的Query封装增加一些共性的过滤如is_deleted, tenent_id的处理。 /// /summary /// returns/returns protected override ISugarQueryableUserInfo BaseCreateFilteredQuery() { var query base.BaseCreateFilteredQuery(); query query.Where(t t.IsDeleted 0); //不需要删除记录 if (!isPlatformAdmin) { query query.Where(t t.TenantId CurrentApiUser.TenantId);//非管理员需要带上租户ID用来隔离数据 } return query; }通过最顶级逻辑条件的过滤任何数据请求都会以当前租户的范围进行条件过滤了。而基类封装常规CRUD接口如下所示。有了基础Core核心模块的支持我们就可以再WebAPI项目中整合相关的接口在控制器层了在SqlSugar框架的代码生成的时候也有对应WebAPI的控制器类的生成我们整合到项目后如下所示。测试项目的Swagger启动后的接口文档展示如下所示。

相关新闻