ai-engineering-init 1.3.4 → 1.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (227) hide show
  1. package/.claude/hooks/skill-forced-eval.js +2 -0
  2. package/.claude/settings.json +3 -3
  3. package/.claude/skills/add-skill/SKILL.md +79 -32
  4. package/.claude/skills/api-development/SKILL.md +83 -377
  5. package/.claude/skills/architecture-design/SKILL.md +138 -632
  6. package/.claude/skills/backend-annotations/SKILL.md +134 -506
  7. package/.claude/skills/banana-image/SKILL.md +10 -3
  8. package/.claude/skills/brainstorm/SKILL.md +103 -535
  9. package/.claude/skills/bug-detective/SKILL.md +147 -1097
  10. package/.claude/skills/bug-detective/references/error-patterns.md +242 -0
  11. package/.claude/skills/code-patterns/SKILL.md +116 -426
  12. package/.claude/skills/code-patterns/references/leniu-code-patterns.md +87 -0
  13. package/.claude/skills/crud-development/SKILL.md +64 -304
  14. package/.claude/skills/data-permission/SKILL.md +105 -412
  15. package/.claude/skills/data-permission/references/custom-data-scope.md +90 -0
  16. package/.claude/skills/file-oss-management/SKILL.md +106 -714
  17. package/.claude/skills/file-oss-management/references/entities.md +105 -0
  18. package/.claude/skills/file-oss-management/references/service-impl.md +104 -0
  19. package/.claude/skills/leniu-api-development/SKILL.md +142 -626
  20. package/.claude/skills/leniu-api-development/references/real-examples.md +273 -0
  21. package/.claude/skills/leniu-architecture-design/SKILL.md +176 -391
  22. package/.claude/skills/leniu-backend-annotations/SKILL.md +132 -519
  23. package/.claude/skills/leniu-brainstorm/SKILL.md +132 -541
  24. package/.claude/skills/leniu-brainstorm/references/business-scenarios.md +162 -0
  25. package/.claude/skills/leniu-crud-development/SKILL.md +232 -938
  26. package/.claude/skills/leniu-crud-development/references/templates.md +597 -0
  27. package/.claude/skills/leniu-customization-location/SKILL.md +410 -0
  28. package/.claude/skills/leniu-data-permission/SKILL.md +70 -0
  29. package/.claude/skills/leniu-java-entity/SKILL.md +76 -590
  30. package/.claude/skills/leniu-java-entity/references/templates.md +237 -0
  31. package/.claude/skills/leniu-java-export/SKILL.md +94 -379
  32. package/.claude/skills/leniu-java-logging/SKILL.md +106 -709
  33. package/.claude/skills/leniu-java-logging/references/data-mask.md +46 -0
  34. package/.claude/skills/leniu-java-logging/references/logging-scenarios.md +113 -0
  35. package/.claude/skills/leniu-java-mybatis/SKILL.md +73 -446
  36. package/.claude/skills/leniu-java-mybatis/references/report-mapper.md +88 -0
  37. package/.claude/skills/leniu-report-customization/SKILL.md +111 -365
  38. package/.claude/skills/leniu-report-customization/references/table-fields.md +93 -0
  39. package/.claude/skills/leniu-report-standard-customization/SKILL.md +111 -334
  40. package/.claude/skills/leniu-report-standard-customization/references/analysis-module.md +64 -0
  41. package/.claude/skills/leniu-report-standard-customization/references/table-fields.md +113 -0
  42. package/.claude/skills/leniu-security-guard/SKILL.md +133 -347
  43. package/.claude/skills/mysql-debug/SKILL.md +364 -0
  44. package/.claude/skills/openspec-apply-change/SKILL.md +10 -1
  45. package/.claude/skills/openspec-archive-change/SKILL.md +9 -1
  46. package/.claude/skills/openspec-bulk-archive-change/SKILL.md +9 -1
  47. package/.claude/skills/openspec-continue-change/SKILL.md +9 -1
  48. package/.claude/skills/openspec-explore/SKILL.md +10 -1
  49. package/.claude/skills/openspec-ff-change/SKILL.md +9 -1
  50. package/.claude/skills/openspec-new-change/SKILL.md +9 -1
  51. package/.claude/skills/openspec-onboard/SKILL.md +15 -130
  52. package/.claude/skills/openspec-sync-specs/SKILL.md +9 -1
  53. package/.claude/skills/openspec-verify-change/SKILL.md +9 -1
  54. package/.claude/skills/performance-doctor/SKILL.md +110 -434
  55. package/.claude/skills/redis-cache/SKILL.md +89 -595
  56. package/.claude/skills/redis-cache/references/listeners.md +23 -0
  57. package/.claude/skills/scheduled-jobs/SKILL.md +88 -407
  58. package/.claude/skills/security-guard/SKILL.md +137 -532
  59. package/.claude/skills/security-guard/references/encrypt-config.md +103 -0
  60. package/.claude/skills/security-guard/references/sensitive-strategies.md +42 -0
  61. package/.claude/skills/sms-mail/SKILL.md +116 -574
  62. package/.claude/skills/sms-mail/references/mail-config.md +88 -0
  63. package/.claude/skills/sms-mail/references/sms-config.md +74 -0
  64. package/.claude/skills/social-login/SKILL.md +112 -514
  65. package/.claude/skills/social-login/references/provider-configs.md +118 -0
  66. package/.claude/skills/tenant-management/SKILL.md +129 -444
  67. package/.claude/skills/tenant-management/references/tenant-scenarios.md +91 -0
  68. package/.claude/skills/test-development/SKILL.md +86 -540
  69. package/.claude/skills/test-development/references/parameterized-examples.md +119 -0
  70. package/.claude/skills/utils-toolkit/SKILL.md +52 -305
  71. package/.claude/skills/utils-toolkit/references/redis-utils-api.md +56 -0
  72. package/.claude/skills/websocket-sse/SKILL.md +105 -550
  73. package/.claude/skills/workflow-engine/SKILL.md +147 -502
  74. package/.codex/skills/add-skill/SKILL.md +79 -32
  75. package/.codex/skills/api-development/SKILL.md +172 -599
  76. package/.codex/skills/architecture-design/SKILL.md +138 -504
  77. package/.codex/skills/backend-annotations/SKILL.md +134 -496
  78. package/.codex/skills/banana-image/SKILL.md +10 -3
  79. package/.codex/skills/brainstorm/SKILL.md +103 -535
  80. package/.codex/skills/bug-detective/SKILL.md +147 -1097
  81. package/.codex/skills/bug-detective/references/error-patterns.md +242 -0
  82. package/.codex/skills/code-patterns/SKILL.md +120 -282
  83. package/.codex/skills/code-patterns/references/leniu-code-patterns.md +87 -0
  84. package/.codex/skills/crud-development/SKILL.md +64 -292
  85. package/.codex/skills/data-permission/SKILL.md +108 -407
  86. package/.codex/skills/data-permission/references/custom-data-scope.md +90 -0
  87. package/.codex/skills/database-ops/SKILL.md +8 -154
  88. package/.codex/skills/error-handler/SKILL.md +10 -0
  89. package/.codex/skills/file-oss-management/SKILL.md +106 -714
  90. package/.codex/skills/file-oss-management/references/entities.md +105 -0
  91. package/.codex/skills/file-oss-management/references/service-impl.md +104 -0
  92. package/.codex/skills/git-workflow/SKILL.md +27 -5
  93. package/.codex/skills/leniu-api-development/SKILL.md +142 -626
  94. package/.codex/skills/leniu-api-development/references/real-examples.md +273 -0
  95. package/.codex/skills/leniu-architecture-design/SKILL.md +176 -391
  96. package/.codex/skills/leniu-backend-annotations/SKILL.md +132 -519
  97. package/.codex/skills/leniu-brainstorm/SKILL.md +132 -541
  98. package/.codex/skills/leniu-brainstorm/references/business-scenarios.md +162 -0
  99. package/.codex/skills/leniu-crud-development/SKILL.md +232 -938
  100. package/.codex/skills/leniu-crud-development/references/templates.md +597 -0
  101. package/.codex/skills/leniu-customization-location/SKILL.md +410 -0
  102. package/.codex/skills/leniu-data-permission/SKILL.md +70 -0
  103. package/.codex/skills/leniu-java-code-style/SKILL.md +510 -0
  104. package/.codex/skills/leniu-java-entity/SKILL.md +76 -590
  105. package/.codex/skills/leniu-java-entity/references/templates.md +237 -0
  106. package/.codex/skills/leniu-java-export/SKILL.md +94 -379
  107. package/.codex/skills/leniu-java-logging/SKILL.md +106 -709
  108. package/.codex/skills/leniu-java-logging/references/data-mask.md +46 -0
  109. package/.codex/skills/leniu-java-logging/references/logging-scenarios.md +113 -0
  110. package/.codex/skills/leniu-java-mybatis/SKILL.md +73 -446
  111. package/.codex/skills/leniu-java-mybatis/references/report-mapper.md +88 -0
  112. package/.codex/skills/leniu-report-customization/SKILL.md +111 -365
  113. package/.codex/skills/leniu-report-customization/references/table-fields.md +93 -0
  114. package/.codex/skills/leniu-report-standard-customization/SKILL.md +111 -334
  115. package/.codex/skills/leniu-report-standard-customization/references/analysis-module.md +64 -0
  116. package/.codex/skills/leniu-report-standard-customization/references/table-fields.md +113 -0
  117. package/.codex/skills/leniu-security-guard/SKILL.md +133 -347
  118. package/.codex/skills/mysql-debug/SKILL.md +364 -0
  119. package/.codex/skills/openspec-apply-change/SKILL.md +10 -1
  120. package/.codex/skills/openspec-archive-change/SKILL.md +9 -1
  121. package/.codex/skills/openspec-bulk-archive-change/SKILL.md +9 -1
  122. package/.codex/skills/openspec-continue-change/SKILL.md +9 -1
  123. package/.codex/skills/openspec-explore/SKILL.md +10 -1
  124. package/.codex/skills/openspec-ff-change/SKILL.md +9 -1
  125. package/.codex/skills/openspec-new-change/SKILL.md +9 -1
  126. package/.codex/skills/openspec-onboard/SKILL.md +15 -130
  127. package/.codex/skills/openspec-sync-specs/SKILL.md +9 -1
  128. package/.codex/skills/openspec-verify-change/SKILL.md +9 -1
  129. package/.codex/skills/performance-doctor/SKILL.md +110 -434
  130. package/.codex/skills/project-navigator/SKILL.md +20 -1
  131. package/.codex/skills/redis-cache/SKILL.md +93 -589
  132. package/.codex/skills/redis-cache/references/listeners.md +23 -0
  133. package/.codex/skills/scheduled-jobs/SKILL.md +88 -407
  134. package/.codex/skills/security-guard/SKILL.md +141 -527
  135. package/.codex/skills/security-guard/references/encrypt-config.md +103 -0
  136. package/.codex/skills/security-guard/references/sensitive-strategies.md +42 -0
  137. package/.codex/skills/sms-mail/SKILL.md +116 -574
  138. package/.codex/skills/sms-mail/references/mail-config.md +88 -0
  139. package/.codex/skills/sms-mail/references/sms-config.md +74 -0
  140. package/.codex/skills/social-login/SKILL.md +112 -514
  141. package/.codex/skills/social-login/references/provider-configs.md +118 -0
  142. package/.codex/skills/store-pc/SKILL.md +258 -383
  143. package/.codex/skills/tenant-management/SKILL.md +129 -444
  144. package/.codex/skills/tenant-management/references/tenant-scenarios.md +91 -0
  145. package/.codex/skills/test-development/SKILL.md +86 -540
  146. package/.codex/skills/test-development/references/parameterized-examples.md +119 -0
  147. package/.codex/skills/ui-pc/SKILL.md +350 -387
  148. package/.codex/skills/utils-toolkit/SKILL.md +52 -283
  149. package/.codex/skills/utils-toolkit/references/redis-utils-api.md +56 -0
  150. package/.codex/skills/websocket-sse/SKILL.md +105 -550
  151. package/.codex/skills/workflow-engine/SKILL.md +147 -502
  152. package/.cursor/hooks/cursor-skill-eval.js +53 -1
  153. package/.cursor/hooks.json +3 -3
  154. package/.cursor/skills/add-skill/SKILL.md +79 -32
  155. package/.cursor/skills/api-development/SKILL.md +83 -377
  156. package/.cursor/skills/architecture-design/SKILL.md +138 -632
  157. package/.cursor/skills/backend-annotations/SKILL.md +134 -506
  158. package/.cursor/skills/banana-image/SKILL.md +10 -3
  159. package/.cursor/skills/brainstorm/SKILL.md +103 -535
  160. package/.cursor/skills/bug-detective/SKILL.md +147 -1097
  161. package/.cursor/skills/bug-detective/references/error-patterns.md +242 -0
  162. package/.cursor/skills/code-patterns/SKILL.md +116 -426
  163. package/.cursor/skills/code-patterns/references/leniu-code-patterns.md +87 -0
  164. package/.cursor/skills/crud-development/SKILL.md +64 -304
  165. package/.cursor/skills/data-permission/SKILL.md +105 -412
  166. package/.cursor/skills/data-permission/references/custom-data-scope.md +90 -0
  167. package/.cursor/skills/file-oss-management/SKILL.md +106 -714
  168. package/.cursor/skills/file-oss-management/references/entities.md +105 -0
  169. package/.cursor/skills/file-oss-management/references/service-impl.md +104 -0
  170. package/.cursor/skills/git-workflow/SKILL.md +27 -5
  171. package/.cursor/skills/leniu-api-development/SKILL.md +142 -626
  172. package/.cursor/skills/leniu-api-development/references/real-examples.md +273 -0
  173. package/.cursor/skills/leniu-architecture-design/SKILL.md +176 -391
  174. package/.cursor/skills/leniu-backend-annotations/SKILL.md +132 -519
  175. package/.cursor/skills/leniu-brainstorm/SKILL.md +132 -541
  176. package/.cursor/skills/leniu-brainstorm/references/business-scenarios.md +162 -0
  177. package/.cursor/skills/leniu-crud-development/SKILL.md +232 -938
  178. package/.cursor/skills/leniu-crud-development/references/templates.md +597 -0
  179. package/.cursor/skills/leniu-customization-location/SKILL.md +410 -0
  180. package/.cursor/skills/leniu-data-permission/SKILL.md +70 -0
  181. package/.cursor/skills/leniu-java-code-style/SKILL.md +510 -0
  182. package/.cursor/skills/leniu-java-entity/SKILL.md +76 -590
  183. package/.cursor/skills/leniu-java-entity/references/templates.md +237 -0
  184. package/.cursor/skills/leniu-java-export/SKILL.md +94 -379
  185. package/.cursor/skills/leniu-java-logging/SKILL.md +106 -709
  186. package/.cursor/skills/leniu-java-logging/references/data-mask.md +46 -0
  187. package/.cursor/skills/leniu-java-logging/references/logging-scenarios.md +113 -0
  188. package/.cursor/skills/leniu-java-mybatis/SKILL.md +73 -446
  189. package/.cursor/skills/leniu-java-mybatis/references/report-mapper.md +88 -0
  190. package/.cursor/skills/leniu-report-customization/SKILL.md +111 -365
  191. package/.cursor/skills/leniu-report-customization/references/table-fields.md +93 -0
  192. package/.cursor/skills/leniu-report-standard-customization/SKILL.md +111 -334
  193. package/.cursor/skills/leniu-report-standard-customization/references/analysis-module.md +64 -0
  194. package/.cursor/skills/leniu-report-standard-customization/references/table-fields.md +113 -0
  195. package/.cursor/skills/leniu-security-guard/SKILL.md +133 -347
  196. package/.cursor/skills/mysql-debug/SKILL.md +364 -0
  197. package/.cursor/skills/openspec-apply-change/SKILL.md +10 -1
  198. package/.cursor/skills/openspec-archive-change/SKILL.md +9 -1
  199. package/.cursor/skills/openspec-bulk-archive-change/SKILL.md +9 -1
  200. package/.cursor/skills/openspec-continue-change/SKILL.md +9 -1
  201. package/.cursor/skills/openspec-explore/SKILL.md +10 -1
  202. package/.cursor/skills/openspec-ff-change/SKILL.md +9 -1
  203. package/.cursor/skills/openspec-new-change/SKILL.md +9 -1
  204. package/.cursor/skills/openspec-onboard/SKILL.md +15 -130
  205. package/.cursor/skills/openspec-sync-specs/SKILL.md +9 -1
  206. package/.cursor/skills/openspec-verify-change/SKILL.md +9 -1
  207. package/.cursor/skills/performance-doctor/SKILL.md +110 -434
  208. package/.cursor/skills/redis-cache/SKILL.md +89 -595
  209. package/.cursor/skills/redis-cache/references/listeners.md +23 -0
  210. package/.cursor/skills/scheduled-jobs/SKILL.md +88 -407
  211. package/.cursor/skills/security-guard/SKILL.md +137 -532
  212. package/.cursor/skills/security-guard/references/encrypt-config.md +103 -0
  213. package/.cursor/skills/security-guard/references/sensitive-strategies.md +42 -0
  214. package/.cursor/skills/sms-mail/SKILL.md +116 -574
  215. package/.cursor/skills/sms-mail/references/mail-config.md +88 -0
  216. package/.cursor/skills/sms-mail/references/sms-config.md +74 -0
  217. package/.cursor/skills/social-login/SKILL.md +112 -514
  218. package/.cursor/skills/social-login/references/provider-configs.md +118 -0
  219. package/.cursor/skills/tenant-management/SKILL.md +129 -444
  220. package/.cursor/skills/tenant-management/references/tenant-scenarios.md +91 -0
  221. package/.cursor/skills/test-development/SKILL.md +86 -540
  222. package/.cursor/skills/test-development/references/parameterized-examples.md +119 -0
  223. package/.cursor/skills/utils-toolkit/SKILL.md +52 -305
  224. package/.cursor/skills/utils-toolkit/references/redis-utils-api.md +56 -0
  225. package/.cursor/skills/websocket-sse/SKILL.md +105 -550
  226. package/.cursor/skills/workflow-engine/SKILL.md +147 -502
  227. package/package.json +1 -1
@@ -1,140 +1,50 @@
1
1
  ---
2
2
  name: tenant-management
3
3
  description: |
4
- 当需要实现多租户数据隔离、租户管理、跨租户查询时自动使用此 Skill。
4
+ 多租户数据隔离开发指南。基于 MyBatis-Plus 租户插件,自动 SQL/Redis/Cache 隔离。
5
5
 
6
6
  触发场景:
7
- - 需要为业务表添加租户隔离
8
- - 需要临时忽略租户过滤查询全量数据
9
- - 需要切换到其他租户执行操作
10
- - 需要配置排除租户过滤的表
11
- - 需要理解多租户数据隔离原理
7
+ - 为业务表添加租户隔离
8
+ - 临时忽略租户过滤查询全量数据
9
+ - 切换到其他租户执行操作
10
+ - 配置排除租户过滤的表
11
+ - 定时任务遍历所有租户
12
12
 
13
- 触发词:多租户、租户隔离、TenantEntity、TenantHelper、租户ID、tenantId、跨租户、ignore租户、动态租户、租户切换、SaaS、数据隔离
13
+ 触发词:多租户、租户隔离、TenantEntity、TenantHelper、tenantId、跨租户、ignore租户、动态租户、租户切换、SaaS、数据隔离
14
14
  ---
15
15
 
16
16
  # 多租户开发指南
17
17
 
18
- > **适用模块**:`ruoyi-common-tenant`
18
+ > **模块**:`ruoyi-common-tenant` | **核心类**:`TenantHelper`、`TenantEntity`
19
19
 
20
- ## 概述
20
+ ## 一、Entity 规范
21
21
 
22
- 本框架基于 **MyBatis-Plus 租户插件** 实现多租户数据隔离,支持 SaaS 多租户架构。
23
-
24
- **核心特性**:
25
- - ✅ 自动 SQL 过滤(无需手动添加 tenant_id 条件)
26
- - ✅ Redis 缓存自动隔离(Key 前缀自动添加租户)
27
- - ✅ Spring Cache 租户隔离
28
- - ✅ Sa-Token 会话租户隔离
29
- - ✅ 支持临时忽略租户、动态切换租户
30
-
31
- ---
32
-
33
- ## 一、多租户架构原理
34
-
35
- ### 1.1 数据隔离流程
36
-
37
- ```
38
- ┌─────────────┐ 请求头携带租户ID ┌─────────────┐
39
- │ 前端请求 │ ───────────────────────→ │ 后端服务 │
40
- └─────────────┘ └─────────────┘
41
-
42
- ┌────────────┴────────────┐
43
- │ TenantHelper │
44
- │ 获取当前租户ID │
45
- └────────────┬────────────┘
46
-
47
- ┌──────────────────────────────────┼──────────────────────────────────┐
48
- │ │ │
49
- ▼ ▼ ▼
50
- ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
51
- │ MyBatis-Plus │ │ Redis 缓存 │ │ Spring Cache │
52
- │ 自动追加条件 │ │ Key 前缀隔离 │ │ 缓存名隔离 │
53
- │ WHERE tenant_id │ │ {tenantId}:key │ │ {tenantId}::xxx │
54
- └─────────────────┘ └─────────────────┘ └─────────────────┘
55
- ```
56
-
57
- ### 1.2 自动 SQL 改写
58
-
59
- 原始 SQL:
60
- ```sql
61
- SELECT * FROM sys_user WHERE status = '1'
62
- ```
63
-
64
- 自动改写后:
65
- ```sql
66
- SELECT * FROM sys_user WHERE status = '1' AND tenant_id = '000000'
67
- ```
68
-
69
- ---
70
-
71
- ## 二、Entity 继承规范
72
-
73
- ### 2.1 TenantEntity 基类
74
-
75
- **位置**:`org.dromara.common.tenant.core.TenantEntity`
22
+ ### 继承 TenantEntity
76
23
 
77
24
  ```java
78
- /**
79
- * 租户基类
80
- */
81
- @Data
82
- @EqualsAndHashCode(callSuper = true)
83
- public class TenantEntity extends BaseEntity {
84
-
85
- /**
86
- * 租户编号
87
- */
88
- private String tenantId;
89
-
90
- }
91
- ```
92
-
93
- ### 2.2 继承关系
94
-
95
- ```
96
- TenantEntity
97
- └── BaseEntity
98
- ├── createDept // 创建部门
99
- ├── createBy // 创建者
100
- ├── createTime // 创建时间
101
- ├── updateBy // 更新者
102
- ├── updateTime // 更新时间
103
- └── params // 请求参数
104
- ```
25
+ import org.dromara.common.tenant.core.TenantEntity;
105
26
 
106
- ### 2.3 Entity 示例
27
+ // TenantEntity 继承关系:TenantEntity → BaseEntity
28
+ // TenantEntity 额外字段:tenantId
29
+ // BaseEntity 字段:createDept, createBy, createTime, updateBy, updateTime, params
107
30
 
108
- ```java
109
- import org.dromara.common.tenant.core.TenantEntity;
110
- import com.baomidou.mybatisplus.annotation.TableId;
111
- import com.baomidou.mybatisplus.annotation.TableName;
112
- import lombok.Data;
113
- import lombok.EqualsAndHashCode;
114
-
115
- /**
116
- * 业务实体(需要租户隔离)
117
- */
118
31
  @Data
119
32
  @EqualsAndHashCode(callSuper = true)
120
33
  @TableName("biz_order")
121
34
  public class BizOrder extends TenantEntity {
122
-
123
35
  @TableId(value = "id")
124
36
  private Long id;
125
-
126
37
  private String orderNo;
127
-
128
38
  private String status;
129
39
  }
130
40
  ```
131
41
 
132
- ### 2.4 数据库表设计
42
+ ### 数据库表必须含 tenant_id
133
43
 
134
44
  ```sql
135
45
  CREATE TABLE biz_order (
136
46
  id BIGINT(20) NOT NULL COMMENT '主键ID',
137
- tenant_id VARCHAR(20) DEFAULT '000000' COMMENT '租户ID', -- 必须有此字段
47
+ tenant_id VARCHAR(20) DEFAULT '000000' COMMENT '租户ID', -- 必须有
138
48
  order_no VARCHAR(64) NOT NULL COMMENT '订单号',
139
49
  status CHAR(1) DEFAULT '0' COMMENT '状态',
140
50
  create_dept BIGINT(20) DEFAULT NULL COMMENT '创建部门',
@@ -144,263 +54,137 @@ CREATE TABLE biz_order (
144
54
  update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
145
55
  del_flag CHAR(1) DEFAULT '0' COMMENT '删除标志',
146
56
  PRIMARY KEY (id),
147
- KEY idx_tenant_id (tenant_id) -- 建议加索引
148
- ) ENGINE=InnoDB COMMENT='业务订单表';
57
+ KEY idx_tenant_id (tenant_id)
58
+ );
149
59
  ```
150
60
 
151
61
  ---
152
62
 
153
- ## 三、TenantHelper 工具类
63
+ ## 二、TenantHelper 工具类
154
64
 
155
65
  **位置**:`org.dromara.common.tenant.helper.TenantHelper`
156
66
 
157
- ### 3.1 核心方法
67
+ ### API 速查
158
68
 
159
- ```java
160
- import org.dromara.common.tenant.helper.TenantHelper;
161
-
162
- // ========== 基础方法 ==========
163
-
164
- // 1. 检查租户功能是否启用
165
- boolean enabled = TenantHelper.isEnable();
166
-
167
- // 2. 获取当前租户ID(优先返回动态租户,其次返回登录用户租户)
168
- String tenantId = TenantHelper.getTenantId();
69
+ | 方法 | 说明 | 返回值 |
70
+ |------|------|--------|
71
+ | `isEnable()` | 租户功能是否启用 | boolean |
72
+ | `getTenantId()` | 获取当前租户ID(优先动态租户) | String |
73
+ | `ignore(Runnable)` | 忽略租户执行(无返回值) | void |
74
+ | `ignore(Supplier<T>)` | 忽略租户执行(有返回值) | T |
75
+ | `dynamic(tenantId, Runnable)` | 切换租户执行(无返回值) | void |
76
+ | `dynamic(tenantId, Supplier<T>)` | 切换租户执行(有返回值) | T |
77
+ | `setDynamic(tenantId)` | 手动设置动态租户(需配合 clearDynamic) | void |
78
+ | `setDynamic(tenantId, true)` | 设置全局动态租户(跨请求,存 Redis) | void |
79
+ | `clearDynamic()` | 清除动态租户 | void |
169
80
 
170
- // ========== 忽略租户(查询全量数据) ==========
81
+ ### 核心用法
171
82
 
172
- // 3. 在忽略租户中执行(无返回值)
173
- TenantHelper.ignore(() -> {
174
- // 此代码块内的 SQL 不会追加 tenant_id 条件
175
- List<SysUser> allUsers = userMapper.selectList(null);
176
- });
83
+ ```java
84
+ import org.dromara.common.tenant.helper.TenantHelper;
177
85
 
178
- // 4. 在忽略租户中执行(有返回值)
86
+ // 1. 忽略租户(查全量数据)
179
87
  List<SysUser> allUsers = TenantHelper.ignore(() -> {
180
- return userMapper.selectList(null);
88
+ return userMapper.selectList(null); // 不追加 tenant_id 条件
181
89
  });
182
90
 
183
- // ========== 动态租户(切换到其他租户) ==========
184
-
185
- // 5. 在指定租户中执行(无返回值)
91
+ // 2. 切换到指定租户(推荐用法)
186
92
  TenantHelper.dynamic("000001", () -> {
187
- // 此代码块内使用租户 000001 的数据
188
- userMapper.insert(user);
93
+ userMapper.insert(user); // 使用租户 000001
189
94
  });
190
95
 
191
- // 6. 在指定租户中执行(有返回值)
192
- List<SysUser> tenant001Users = TenantHelper.dynamic("000001", () -> {
193
- return userMapper.selectList(null);
194
- });
195
-
196
- // ========== 手动管理动态租户 ==========
197
-
198
- // 7. 设置动态租户(需手动清理)
96
+ // 3. 手动管理(复杂场景)
199
97
  TenantHelper.setDynamic("000001");
200
- // ... 执行操作 ...
201
- TenantHelper.clearDynamic(); // 必须清理
202
-
203
- // 8. 设置全局动态租户(跨请求生效,存入 Redis)
204
- TenantHelper.setDynamic("000001", true);
205
- // ... 多次请求都使用该租户 ...
206
- TenantHelper.clearDynamic();
98
+ try {
99
+ userMapper.insert(user);
100
+ } finally {
101
+ TenantHelper.clearDynamic(); // 必须清理!
102
+ }
207
103
  ```
208
104
 
209
- ### 3.2 方法速查表
210
-
211
- | 方法 | 说明 | 使用场景 |
212
- |------|------|---------|
213
- | `isEnable()` | 检查租户是否启用 | 条件判断 |
214
- | `getTenantId()` | 获取当前租户ID | 业务逻辑 |
215
- | `ignore(Runnable)` | 忽略租户执行(无返回值) | 查询全量数据 |
216
- | `ignore(Supplier<T>)` | 忽略租户执行(有返回值) | 查询全量数据 |
217
- | `dynamic(tenantId, Runnable)` | 切换租户执行(无返回值) | 跨租户操作 |
218
- | `dynamic(tenantId, Supplier<T>)` | 切换租户执行(有返回值) | 跨租户查询 |
219
- | `setDynamic(tenantId)` | 手动设置动态租户 | 复杂场景 |
220
- | `setDynamic(tenantId, true)` | 设置全局动态租户 | 跨请求场景 |
221
- | `getDynamic()` | 获取动态租户 | 调试/检查 |
222
- | `clearDynamic()` | 清除动态租户 | 配合 setDynamic |
223
-
224
105
  ---
225
106
 
226
- ## 四、配置说明
227
-
228
- ### 4.1 启用多租户
107
+ ## 三、配置
229
108
 
230
109
  ```yaml
231
110
  # application.yml
232
111
  tenant:
233
- # 是否开启多租户
234
112
  enable: true
235
- # 排除表(这些表不追加 tenant_id 条件)
236
- excludes:
237
- - sys_menu # 菜单表(所有租户共享)
238
- - sys_dict_type # 字典类型(所有租户共享)
239
- - sys_dict_data # 字典数据(所有租户共享)
113
+ excludes: # 不追加 tenant_id 条件的表
114
+ - sys_menu
115
+ - sys_dict_type
116
+ - sys_dict_data
117
+ - sys_oss_config
118
+ - sys_config
240
119
  ```
241
120
 
242
- ### 4.2 排除表说明
243
-
244
- 以下类型的表通常需要排除:
245
- - **系统配置表**:所有租户共享的配置
246
- - **字典表**:公共字典数据
247
- - **地区表**:省市区数据
248
- - **代码生成表**:gen_table、gen_table_column(框架自动排除)
249
-
250
- ### 4.3 自动配置的组件
121
+ **自动配置组件**(`tenant.enable=true` 时生效):
251
122
 
252
- `tenant.enable=true` 时,框架自动配置:
253
-
254
- | 组件 | 类名 | 功能 |
255
- |------|------|------|
256
- | MyBatis 租户插件 | `TenantLineInnerInterceptor` | SQL 自动追加租户条件 |
257
- | Redis Key 前缀 | `TenantKeyPrefixHandler` | Redis Key 自动添加租户前缀 |
258
- | 缓存管理器 | `TenantSpringCacheManager` | Spring Cache 租户隔离 |
259
- | Sa-Token DAO | `TenantSaTokenDao` | 会话数据租户隔离 |
123
+ | 组件 | 功能 |
124
+ |------|------|
125
+ | `TenantLineInnerInterceptor` | SQL 自动追加租户条件 |
126
+ | `TenantKeyPrefixHandler` | Redis Key 自动添加租户前缀 |
127
+ | `TenantSpringCacheManager` | Spring Cache 租户隔离 |
128
+ | `TenantSaTokenDao` | Sa-Token 会话租户隔离 |
260
129
 
261
130
  ---
262
131
 
263
- ## 五、常见场景
132
+ ## 四、常见场景
264
133
 
265
- ### 5.1 场景:管理员查看所有租户数据
134
+ ### 场景 1:管理员查看所有租户数据
266
135
 
267
136
  ```java
268
- @Service
269
- @RequiredArgsConstructor
270
- public class AdminUserServiceImpl implements IAdminUserService {
271
-
272
- private final SysUserMapper userMapper;
273
-
274
- /**
275
- * 管理员查看所有租户的用户(需要超级管理员权限)
276
- */
277
- @SaCheckRole("superadmin")
278
- public List<SysUserVo> listAllTenantUsers() {
279
- // 忽略租户过滤,查询所有数据
280
- return TenantHelper.ignore(() -> {
281
- return userMapper.selectVoList(null);
282
- });
283
- }
137
+ @SaCheckRole("superadmin")
138
+ public List<SysUserVo> listAllTenantUsers() {
139
+ return TenantHelper.ignore(() -> userMapper.selectVoList(null));
284
140
  }
285
141
  ```
286
142
 
287
- ### 5.2 场景:跨租户数据同步
143
+ ### 场景 2:跨租户数据同步
288
144
 
289
145
  ```java
290
- @Service
291
- @RequiredArgsConstructor
292
- public class DataSyncServiceImpl implements IDataSyncService {
293
-
294
- private final SysConfigMapper configMapper;
295
-
296
- /**
297
- * 将配置同步到所有租户
298
- */
299
- public void syncConfigToAllTenants(SysConfig config) {
300
- // 1. 获取所有租户ID
301
- List<String> tenantIds = TenantHelper.ignore(() -> {
302
- return tenantMapper.selectList(null)
303
- .stream()
304
- .map(SysTenant::getTenantId)
305
- .collect(Collectors.toList());
146
+ public void syncConfigToAllTenants(SysConfig config) {
147
+ List<String> tenantIds = TenantHelper.ignore(() ->
148
+ tenantMapper.selectList(null).stream()
149
+ .map(SysTenant::getTenantId).collect(Collectors.toList())
150
+ );
151
+ for (String tenantId : tenantIds) {
152
+ TenantHelper.dynamic(tenantId, () -> {
153
+ SysConfig existing = configMapper.selectByKey(config.getConfigKey());
154
+ if (existing == null) configMapper.insert(config);
155
+ else configMapper.updateById(config);
306
156
  });
307
-
308
- // 2. 逐个租户同步
309
- for (String tenantId : tenantIds) {
310
- TenantHelper.dynamic(tenantId, () -> {
311
- // 检查是否已存在
312
- SysConfig existing = configMapper.selectByKey(config.getConfigKey());
313
- if (existing == null) {
314
- configMapper.insert(config);
315
- } else {
316
- configMapper.updateById(config);
317
- }
318
- });
319
- }
320
157
  }
321
158
  }
322
159
  ```
323
160
 
324
- ### 5.3 场景:定时任务处理所有租户
161
+ ### 场景 3:定时任务处理所有租户
325
162
 
326
- ```java
327
- @Service
328
- @RequiredArgsConstructor
329
- public class OrderCleanupJob {
330
-
331
- private final OrderMapper orderMapper;
332
- private final SysTenantMapper tenantMapper;
333
-
334
- /**
335
- * 清理所有租户的过期订单
336
- */
337
- @Scheduled(cron = "0 0 2 * * ?") // 每天凌晨2点
338
- public void cleanupExpiredOrders() {
339
- // 1. 获取所有启用的租户
340
- List<SysTenant> tenants = TenantHelper.ignore(() -> {
341
- return tenantMapper.selectList(
342
- Wrappers.<SysTenant>lambdaQuery()
343
- .eq(SysTenant::getStatus, "0")
344
- );
345
- });
346
-
347
- // 2. 逐个租户处理
348
- for (SysTenant tenant : tenants) {
349
- try {
350
- TenantHelper.dynamic(tenant.getTenantId(), () -> {
351
- // 删除30天前的已取消订单
352
- orderMapper.delete(
353
- Wrappers.<Order>lambdaQuery()
354
- .eq(Order::getStatus, "CANCELLED")
355
- .lt(Order::getCreateTime, DateUtils.addDays(new Date(), -30))
356
- );
357
- });
358
- } catch (Exception e) {
359
- log.error("清理租户 {} 订单失败: {}", tenant.getTenantId(), e.getMessage());
360
- }
361
- }
362
- }
363
- }
364
- ```
365
-
366
- ### 5.4 场景:统计所有租户数据
163
+ > 详细示例见 `references/tenant-scenarios.md`
367
164
 
368
165
  ```java
369
- @Service
370
- @RequiredArgsConstructor
371
- public class StatisticsServiceImpl implements IStatisticsService {
372
-
373
- private final OrderMapper orderMapper;
374
-
375
- /**
376
- * 统计各租户订单数量
377
- */
378
- public List<TenantOrderStats> getTenantOrderStats() {
379
- // 忽略租户,使用 GROUP BY 统计
380
- return TenantHelper.ignore(() -> {
381
- return orderMapper.selectMaps(
382
- new QueryWrapper<Order>()
383
- .select("tenant_id", "COUNT(*) as order_count", "SUM(amount) as total_amount")
384
- .groupBy("tenant_id")
385
- ).stream().map(map -> {
386
- TenantOrderStats stats = new TenantOrderStats();
387
- stats.setTenantId((String) map.get("tenant_id"));
388
- stats.setOrderCount(((Number) map.get("order_count")).longValue());
389
- stats.setTotalAmount((BigDecimal) map.get("total_amount"));
390
- return stats;
391
- }).collect(Collectors.toList());
392
- });
166
+ @Scheduled(cron = "0 0 2 * * ?")
167
+ public void cleanupExpiredOrders() {
168
+ List<SysTenant> tenants = TenantHelper.ignore(() ->
169
+ tenantMapper.selectList(Wrappers.<SysTenant>lambdaQuery().eq(SysTenant::getStatus, "0"))
170
+ );
171
+ for (SysTenant tenant : tenants) {
172
+ try {
173
+ TenantHelper.dynamic(tenant.getTenantId(), () -> {
174
+ orderMapper.delete(Wrappers.<Order>lambdaQuery()
175
+ .eq(Order::getStatus, "CANCELLED")
176
+ .lt(Order::getCreateTime, DateUtils.addDays(new Date(), -30)));
177
+ });
178
+ } catch (Exception e) {
179
+ log.error("清理租户 {} 订单失败: {}", tenant.getTenantId(), e.getMessage());
180
+ }
393
181
  }
394
182
  }
395
183
  ```
396
184
 
397
185
  ---
398
186
 
399
- ## 六、Redis 缓存隔离
400
-
401
- ### 6.1 自动隔离原理
402
-
403
- 框架通过 `TenantKeyPrefixHandler` 自动为 Redis Key 添加租户前缀:
187
+ ## 五、Redis 缓存隔离
404
188
 
405
189
  ```
406
190
  原始 Key: user:info:1001
@@ -408,9 +192,7 @@ public class StatisticsServiceImpl implements IStatisticsService {
408
192
  实际 Key: 000001:user:info:1001 (租户 000001)
409
193
  ```
410
194
 
411
- ### 6.2 全局 Key(不隔离)
412
-
413
- 使用 `GlobalConstants.GLOBAL_REDIS_KEY` 前缀的 Key 不会添加租户前缀:
195
+ **全局 Key(不隔离)**:使用 `GlobalConstants.GLOBAL_REDIS_KEY` 前缀
414
196
 
415
197
  ```java
416
198
  import org.dromara.common.core.constant.GlobalConstants;
@@ -419,185 +201,88 @@ import org.dromara.common.core.constant.GlobalConstants;
419
201
  String globalKey = GlobalConstants.GLOBAL_REDIS_KEY + "config:site_name";
420
202
  RedisUtils.setCacheObject(globalKey, "网站名称");
421
203
 
422
- // 租户隔离缓存
423
- String tenantKey = "user:info:" + userId; // 自动添加租户前缀
424
- RedisUtils.setCacheObject(tenantKey, userInfo);
204
+ // 租户隔离缓存(自动添加前缀)
205
+ RedisUtils.setCacheObject("user:info:" + userId, userInfo);
425
206
  ```
426
207
 
427
- ### 6.3 忽略租户时的缓存
428
-
429
- 在 `TenantHelper.ignore()` 中操作的缓存不会添加租户前缀:
430
-
431
- ```java
432
- TenantHelper.ignore(() -> {
433
- // 此处的 Redis 操作不添加租户前缀
434
- RedisUtils.setCacheObject("global:data", data);
435
- });
436
- ```
208
+ > `TenantHelper.ignore()` 中的 Redis 操作不添加租户前缀。
437
209
 
438
210
  ---
439
211
 
440
- ## 七、常见错误与最佳实践
212
+ ## 六、常见错误
441
213
 
442
- ### ❌ 错误1:忘记继承 TenantEntity
214
+ ### 1. 忘记继承 TenantEntity
443
215
 
444
216
  ```java
445
- // ❌ 错误:继承 BaseEntity,没有 tenantId 字段
446
- @Data
447
- @EqualsAndHashCode(callSuper = true)
448
- public class BizOrder extends BaseEntity {
449
- // 没有 tenantId,数据无法隔离!
450
- }
217
+ // ❌ 继承 BaseEntity,没有 tenantId
218
+ public class BizOrder extends BaseEntity { }
451
219
 
452
- // ✅ 正确:继承 TenantEntity
453
- @Data
454
- @EqualsAndHashCode(callSuper = true)
455
- public class BizOrder extends TenantEntity {
456
- // 自动包含 tenantId 字段
457
- }
220
+ // ✅ 继承 TenantEntity
221
+ public class BizOrder extends TenantEntity { }
458
222
  ```
459
223
 
460
- ### ❌ 错误2:数据库表缺少 tenant_id 字段
224
+ ### 2. 数据库表缺少 tenant_id
461
225
 
462
226
  ```sql
463
- -- ❌ 错误:表没有 tenant_id 字段
464
- CREATE TABLE biz_order (
465
- id BIGINT(20) NOT NULL,
466
- order_no VARCHAR(64)
467
- );
227
+ -- ❌ 表没有 tenant_id
228
+ CREATE TABLE biz_order (id BIGINT NOT NULL, order_no VARCHAR(64));
468
229
 
469
- -- ✅ 正确:添加 tenant_id 字段
470
- CREATE TABLE biz_order (
471
- id BIGINT(20) NOT NULL,
472
- tenant_id VARCHAR(20) DEFAULT '000000', -- 必须有
473
- order_no VARCHAR(64)
474
- );
230
+ -- ✅ 添加 tenant_id
231
+ CREATE TABLE biz_order (id BIGINT NOT NULL, tenant_id VARCHAR(20) DEFAULT '000000', order_no VARCHAR(64));
475
232
  ```
476
233
 
477
- ### 错误3:setDynamic 后忘记 clearDynamic
234
+ ### 3. setDynamic 后忘记 clearDynamic
478
235
 
479
236
  ```java
480
- // ❌ 错误:设置后没有清理,后续请求使用错误的租户
237
+ // ❌ 租户ID泄漏到其他请求
481
238
  TenantHelper.setDynamic("000001");
482
239
  userMapper.insert(user);
483
- // 忘记 clearDynamic(),租户ID泄漏到其他请求!
240
+ // 忘记 clearDynamic()
484
241
 
485
- // ✅ 正确方式1:使用 try-finally
486
- TenantHelper.setDynamic("000001");
487
- try {
488
- userMapper.insert(user);
489
- } finally {
490
- TenantHelper.clearDynamic();
491
- }
492
-
493
- // ✅ 正确方式2:使用 dynamic() 方法(推荐)
494
- TenantHelper.dynamic("000001", () -> {
495
- userMapper.insert(user);
496
- });
242
+ // ✅ 推荐使用 dynamic() 方法(自动清理)
243
+ TenantHelper.dynamic("000001", () -> userMapper.insert(user));
497
244
  ```
498
245
 
499
- ### 错误4:在事务中切换租户
246
+ ### 4. 事务中切换租户
500
247
 
501
248
  ```java
502
- // ❌ 错误:事务内切换租户可能导致数据错乱
249
+ // ❌ 事务内切换租户导致数据错乱
503
250
  @Transactional
504
251
  public void wrongMethod() {
505
- orderMapper.insert(order); // 使用当前租户
506
- TenantHelper.dynamic("000001", () -> {
507
- logMapper.insert(log); // 使用租户 000001
508
- });
509
- // 事务提交时可能出现问题!
510
- }
511
-
512
- // ✅ 正确:避免在事务中切换租户,或使用独立事务
513
- public void correctMethod() {
514
- // 先处理当前租户
515
- saveOrder(order);
516
-
517
- // 再处理其他租户(独立事务)
518
- saveLogToOtherTenant("000001", log);
252
+ orderMapper.insert(order);
253
+ TenantHelper.dynamic("000001", () -> logMapper.insert(log));
519
254
  }
520
255
 
256
+ // ✅ 使用独立事务
521
257
  @Transactional(propagation = Propagation.REQUIRES_NEW)
522
258
  public void saveLogToOtherTenant(String tenantId, Log log) {
523
- TenantHelper.dynamic(tenantId, () -> {
524
- logMapper.insert(log);
525
- });
259
+ TenantHelper.dynamic(tenantId, () -> logMapper.insert(log));
526
260
  }
527
261
  ```
528
262
 
529
- ### 错误5:排除表配置不当
263
+ ### 5. 排除表配置不当
530
264
 
531
265
  ```yaml
532
- # ❌ 错误:把业务表加入排除列表
266
+ # ❌ 业务表不应排除
533
267
  tenant:
534
268
  excludes:
535
- - biz_order # 业务表不应该排除!
269
+ - biz_order
536
270
 
537
- # ✅ 正确:只排除真正需要共享的系统表
271
+ # ✅ 只排除共享的系统表
538
272
  tenant:
539
273
  excludes:
540
- - sys_menu # 菜单配置(共享)
541
- - sys_dict_type # 字典类型(共享)
542
- - sys_config # 系统配置(共享)
543
- ```
544
-
545
- ---
546
-
547
- ## 八、API 速查表
548
-
549
- ### TenantHelper 方法
550
-
551
- | 方法 | 说明 | 返回值 |
552
- |------|------|--------|
553
- | `isEnable()` | 租户功能是否启用 | boolean |
554
- | `getTenantId()` | 获取当前租户ID | String |
555
- | `ignore(Runnable)` | 忽略租户执行 | void |
556
- | `ignore(Supplier<T>)` | 忽略租户执行 | T |
557
- | `dynamic(tenantId, Runnable)` | 动态租户执行 | void |
558
- | `dynamic(tenantId, Supplier<T>)` | 动态租户执行 | T |
559
- | `setDynamic(tenantId)` | 设置动态租户 | void |
560
- | `setDynamic(tenantId, global)` | 设置全局动态租户 | void |
561
- | `getDynamic()` | 获取动态租户 | String |
562
- | `clearDynamic()` | 清除动态租户 | void |
563
-
564
- ### Entity 继承
565
-
566
- | 基类 | 说明 | 包含字段 |
567
- |------|------|---------|
568
- | `BaseEntity` | 基础实体 | createDept, createBy, createTime, updateBy, updateTime |
569
- | `TenantEntity` | 租户实体 | tenantId + BaseEntity 所有字段 |
570
-
571
- ---
572
-
573
- ## 九、配置参考
574
-
575
- ### 完整配置示例
576
-
577
- ```yaml
578
- # application.yml
579
- tenant:
580
- # 是否开启多租户
581
- enable: true
582
- # 排除表(不追加租户条件的表)
583
- excludes:
584
- - sys_menu # 菜单表
585
- - sys_dict_type # 字典类型
586
- - sys_dict_data # 字典数据
587
- - sys_oss_config # OSS 配置
588
- - sys_config # 系统配置
274
+ - sys_menu
275
+ - sys_dict_type
589
276
  ```
590
277
 
591
278
  ---
592
279
 
593
- ## 十、参考代码位置
280
+ ## 七、参考代码位置
594
281
 
595
282
  | 类型 | 位置 |
596
283
  |------|------|
597
- | 租户基类 | `ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/core/TenantEntity.java` |
598
- | 租户助手 | `ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/helper/TenantHelper.java` |
599
- | 租户处理器 | `ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/handle/PlusTenantLineHandler.java` |
600
- | Redis Key 处理 | `ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/handle/TenantKeyPrefixHandler.java` |
601
- | 租户配置类 | `ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/config/TenantConfig.java` |
602
- | 租户属性 | `ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/properties/TenantProperties.java` |
284
+ | 租户基类 | `ruoyi-common/ruoyi-common-tenant/.../core/TenantEntity.java` |
285
+ | 租户助手 | `ruoyi-common/ruoyi-common-tenant/.../helper/TenantHelper.java` |
286
+ | 租户处理器 | `ruoyi-common/ruoyi-common-tenant/.../handle/PlusTenantLineHandler.java` |
287
+ | Redis Key 处理 | `ruoyi-common/ruoyi-common-tenant/.../handle/TenantKeyPrefixHandler.java` |
603
288
  | 配置文件 | `ruoyi-admin/src/main/resources/application.yml` |