tenant.mdc 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. ---
  2. description: 多租户(Tenant)
  3. globs:
  4. ---
  5. # 多租户(v8.0新增)
  6. 多租户(Multi-tenancy)是一种软件架构模式,允许单个应用实例服务多个租户(客户组织)。每个租户的数据是相互隔离的,但共享同一个应用程序代码和基础设施。
  7. ## 主要特点
  8. - **数据隔离**: 确保不同租户之间的数据严格分离,互不可见
  9. - **资源共享**: 多个租户共享同一套应用程序代码和基础设施
  10. - **独立配置**: 每个租户可以有自己的个性化配置和定制化需求
  11. - **成本优化**: 通过资源共享降低运营和维护成本
  12. ## 实现
  13. ### 1、数据隔离
  14. 多租户的数据隔离有许多种方案,但最为常见的是以列进行隔离的方式。Cool Admin 通过在`BaseEntity`中加入指定的列(租户ID `tenantId`)对数据进行隔离。
  15. ::: tip 小贴士
  16. v8.0之后,`BaseEntity`已经从`cool-midway/core`中移动至`src/modules/base/entity/base.ts`,方便开发者扩展定制
  17. :::
  18. `src/modules/base/entity/base.ts`
  19. ```ts
  20. import {
  21. Index,
  22. UpdateDateColumn,
  23. CreateDateColumn,
  24. PrimaryGeneratedColumn,
  25. Column,
  26. } from 'typeorm';
  27. import { CoolBaseEntity } from '@cool-midway/core';
  28. /**
  29. * 实体基类
  30. */
  31. export abstract class BaseEntity extends CoolBaseEntity {
  32. // 默认自增
  33. @PrimaryGeneratedColumn('increment', {
  34. comment: 'ID',
  35. })
  36. id: number;
  37. @Index()
  38. @CreateDateColumn({ comment: '创建时间' })
  39. createTime: Date;
  40. @Index()
  41. @UpdateDateColumn({ comment: '更新时间' })
  42. updateTime: Date;
  43. @Index()
  44. @Column({ comment: '租户ID', nullable: true })
  45. tenantId: number;
  46. }
  47. ```
  48. ### 2、条件注入
  49. Cool 改造了 `typeorm`的 `Subscriber`,新增了以下四种监听:
  50. ```ts
  51. /**
  52. * 当进行select的QueryBuilder构建之后触发
  53. */
  54. afterSelectQueryBuilder?(queryBuilder: SelectQueryBuilder<any>): void;
  55. /**
  56. * 当进行insert的QueryBuilder构建之后触发
  57. */
  58. afterInsertQueryBuilder?(queryBuilder: InsertQueryBuilder<any>): void;
  59. /**
  60. * 当进行update的QueryBuilder构建之后触发
  61. */
  62. afterUpdateQueryBuilder?(queryBuilder: UpdateQueryBuilder<any>): void;
  63. /**
  64. * 当进行delete的QueryBuilder构建之后触发
  65. */
  66. afterDeleteQueryBuilder?(queryBuilder: DeleteQueryBuilder<any>): void;
  67. ```
  68. 在`src/modules/base/db/tenant.ts`中,通过`tenantId`进行条件注入,从而实现数据隔离。
  69. ## 使用
  70. ### 1、开启多租户
  71. 框架默认关闭多租户,需要手动开启,在`src/config/config.default.ts`中开启多租户
  72. ```ts
  73. cool: {
  74. // 是否开启多租户
  75. tenant: {
  76. // 是否开启多租户
  77. enable: true,
  78. // 需要过滤多租户的url, 支持通配符,如/admin/**/* 表示admin模块下的所有接口都进行多租户过滤
  79. urls: [],
  80. },
  81. }
  82. ```
  83. tenant
  84. ### 2、代码中使用
  85. 只要开启了多租户,并配置了`urls`,那么框架会自动注入`tenantId`,开发者原本的代码不需要做任何修改,框架会自动进行数据隔离。
  86. #### Controller
  87. @CoolController的`add`、`delete`、`update`、`info`、`list`、`page`方法都支持过滤多租户。
  88. #### Service
  89. `Service`中使用多租户,以下是一个完整的示例,包含有效和无效的情况,开发者需要结合实际业务进行选择。
  90. ```ts
  91. import { Inject, Provide } from '@midwayjs/core';
  92. import { BaseService } from '@cool-midway/core';
  93. import { InjectEntityModel } from '@midwayjs/typeorm';
  94. import { Repository } from 'typeorm';
  95. import { DemoGoodsEntity } from '../entity/goods';
  96. import { UserInfoEntity } from '../../user/entity/info';
  97. import { noTenant } from '../../base/db/tenant';
  98. /**
  99. * 商品服务
  100. */
  101. @Provide()
  102. export class DemoTenantService extends BaseService {
  103. @InjectEntityModel(DemoGoodsEntity)
  104. demoGoodsEntity: Repository<DemoGoodsEntity>;
  105. @Inject()
  106. ctx;
  107. /**
  108. * 使用多租户
  109. */
  110. async use() {
  111. await this.demoGoodsEntity.createQueryBuilder().getMany();
  112. await this.demoGoodsEntity.find();
  113. }
  114. /**
  115. * 不使用多租户(局部不使用)
  116. */
  117. async noUse() {
  118. // 过滤多租户
  119. await this.demoGoodsEntity.createQueryBuilder().getMany();
  120. // 被noTenant包裹,不会过滤多租户
  121. await noTenant(this.ctx, async () => {
  122. return await this.demoGoodsEntity.createQueryBuilder().getMany();
  123. });
  124. // 过滤多租户
  125. await this.demoGoodsEntity.find();
  126. }
  127. /**
  128. * 无效多租户
  129. */
  130. async invalid() {
  131. // 自定义sql,不进行多租户过滤
  132. await this.nativeQuery('select * from demo_goods');
  133. // 自定义分页sql,进行多租户过滤
  134. await this.sqlRenderPage('select * from demo_goods', {});
  135. }
  136. }
  137. ```