forked from liuyon7/ruoyi-vue-pro-doc
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsaas-tenant.html
More file actions
106 lines (102 loc) · 91.1 KB
/
saas-tenant.html
File metadata and controls
106 lines (102 loc) · 91.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>SaaS 多租户【字段隔离】 | ruoyi-vue-pro 开发指南</title>
<meta name="generator" content="VuePress 1.9.2">
<link rel="icon" href="img_favicon.ico.html">
<script ></script>
<script ></script>
<script ></script>
<link rel="stylesheet" href="https://static.iocoder.cn/answer.css">
<meta name="description" content="RuoYi-Vue 全新 Pro 版本,优化重构所有功能。基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 微信小程序,支持 RBAC 动态权限、数据权限、SaaS 多租户、Activiti + Flowable 工作流、三方登录、支付、短信、商城等功能。">
<meta name="keywords" content="ruoyi-vue,权限,数据权限,SaaS,多租户,Activiti,Flowable,工作流,商城">
<meta name="theme-color" content="#11a8cd">
<link rel="preload" >
<link rel="stylesheet" href="https://doc.iocoder.cn/assets/css/0.styles.7c54dcda.css">
</head>
<body class="theme-mode-light">
<div id="app" data-server-rendered="true"><div class="theme-container sidebar-open have-rightmenu"><header class="navbar blur"><div title="目录" class="sidebar-button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512" class="icon"><path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"></path></svg></div> <a href="intro.html" class="home-link router-link-active"><img src="https://static.iocoder.cn/ruoyi-vue-pro-logo.png" alt="ruoyi-vue-pro 开发指南" class="logo"> <span class="site-name can-hide">ruoyi-vue-pro 开发指南(更新时间:2025-2-26)</span></a> <div class="links"><div class="search-box"><input aria-label="Search" autocomplete="off" spellcheck="false" value=""> <!----></div> <nav class="nav-links can-hide"><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="开发指南" class="dropdown-title"><a href="intro.html" class="link-title">开发指南</a> <span class="title" style="display:none;">开发指南</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="intro.html" class="nav-link">萌新必读</a></li><li class="dropdown-item"><!----> <a href="module-new.html" class="nav-link">后端手册</a></li><li class="dropdown-item"><!----> <a href="message-queue_event.html" class="nav-link">中间件手册</a></li><li class="dropdown-item"><!----> <a href="bpm-preview.html" class="nav-link">工作流手册</a></li><li class="dropdown-item"><!----> <a href="report.html" class="nav-link">大屏手册</a></li><li class="dropdown-item"><!----> <a href="pay_build.html" class="nav-link">支付手册</a></li><li class="dropdown-item"><!----> <a href="member_build.html" class="nav-link">会员手册</a></li><li class="dropdown-item"><!----> <a href="mall_build.html" class="nav-link">商城手册</a></li><li class="dropdown-item"><!----> <a href="erp_build.html" class="nav-link">ERP 手册</a></li><li class="dropdown-item"><!----> <a href="crm_build.html" class="nav-link">CRM 手册</a></li><li class="dropdown-item"><!----> <a href="ai-preview.html" class="nav-link">AI 大模型手册</a></li><li class="dropdown-item"><!----> <a href="mp_build.html" class="nav-link">公众号手册</a></li><li class="dropdown-item"><!----> <a href="sms.html" class="nav-link">系统手册</a></li><li class="dropdown-item"><!----> <a href="dev-env.html" class="nav-link">运维手册</a></li><li class="dropdown-item"><!----> <a href="vue2_dev-spec.html" class="nav-link">前端手册 Vue 2.x</a></li><li class="dropdown-item"><!----> <a href="vue3_dev-spec.html" class="nav-link">前端手册 Vue 3.x</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="项目实战" class="dropdown-title"><a href="bpm.html" class="link-title">项目实战</a> <span class="title" style="display:none;">项目实战</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="bpm-preview.html" class="nav-link">工作流手册</a></li><li class="dropdown-item"><!----> <a href="report.html" class="nav-link">大屏手册</a></li><li class="dropdown-item"><!----> <a href="pay_build.html" class="nav-link">支付手册</a></li><li class="dropdown-item"><!----> <a href="member_build.html" class="nav-link">会员手册</a></li><li class="dropdown-item"><!----> <a href="mall_build.html" class="nav-link">商城手册</a></li><li class="dropdown-item"><!----> <a href="erp_build.html" class="nav-link">ERP 手册</a></li><li class="dropdown-item"><!----> <a href="crm_build.html" class="nav-link">CRM 手册</a></li><li class="dropdown-item"><!----> <a href="ai-preview.html" class="nav-link">AI 大模型手册</a></li><li class="dropdown-item"><!----> <a href="mp_build.html" class="nav-link">公众号手册</a></li><li class="dropdown-item"><!----> <a href="sms.html" class="nav-link">系统手册</a></li></ul></div></div><div class="nav-item"><a href="video.html" class="nav-link">视频教程</a></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="在线体验" class="dropdown-title"><!----> <span class="title" style="display:;">在线体验</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="http://dashboard-vue3.yudao.iocoder.cn/" target="_blank" rel="noopener noreferrer" class="nav-link external">
Vue3 + element-plus
<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li><li class="dropdown-item"><!----> <a href="http://dashboard-vben.yudao.iocoder.cn/" target="_blank" rel="noopener noreferrer" class="nav-link external">
Vue3 + vben(ant-design-vue)
<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li><li class="dropdown-item"><!----> <a href="http://dashboard.yudao.iocoder.cn/" target="_blank" rel="noopener noreferrer" class="nav-link external">
Vue2 + element-ui
<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li></ul></div></div><div class="nav-item"><a href="https://github.com/YunaiV/yudao-cloud" target="_blank" rel="noopener noreferrer" class="nav-link external">
微服务版
<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></div><div class="nav-item"><a href="https://www.iocoder.cn/" target="_blank" rel="noopener noreferrer" class="nav-link external">
作者博客
<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></div> <a href="https://github.com/YunaiV/ruoyi-vue-pro" target="_blank" rel="noopener noreferrer" class="repo-link">
GitHub
<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></nav></div></header> <div class="sidebar-mask"></div> <div class="sidebar-hover-trigger"></div> <aside class="sidebar"><!----> <nav class="nav-links"><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="开发指南" class="dropdown-title"><a href="intro.html" class="link-title">开发指南</a> <span class="title" style="display:none;">开发指南</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="intro.html" class="nav-link">萌新必读</a></li><li class="dropdown-item"><!----> <a href="module-new.html" class="nav-link">后端手册</a></li><li class="dropdown-item"><!----> <a href="message-queue_event.html" class="nav-link">中间件手册</a></li><li class="dropdown-item"><!----> <a href="bpm-preview.html" class="nav-link">工作流手册</a></li><li class="dropdown-item"><!----> <a href="report.html" class="nav-link">大屏手册</a></li><li class="dropdown-item"><!----> <a href="pay_build.html" class="nav-link">支付手册</a></li><li class="dropdown-item"><!----> <a href="member_build.html" class="nav-link">会员手册</a></li><li class="dropdown-item"><!----> <a href="mall_build.html" class="nav-link">商城手册</a></li><li class="dropdown-item"><!----> <a href="erp_build.html" class="nav-link">ERP 手册</a></li><li class="dropdown-item"><!----> <a href="crm_build.html" class="nav-link">CRM 手册</a></li><li class="dropdown-item"><!----> <a href="ai-preview.html" class="nav-link">AI 大模型手册</a></li><li class="dropdown-item"><!----> <a href="mp_build.html" class="nav-link">公众号手册</a></li><li class="dropdown-item"><!----> <a href="sms.html" class="nav-link">系统手册</a></li><li class="dropdown-item"><!----> <a href="dev-env.html" class="nav-link">运维手册</a></li><li class="dropdown-item"><!----> <a href="vue2_dev-spec.html" class="nav-link">前端手册 Vue 2.x</a></li><li class="dropdown-item"><!----> <a href="vue3_dev-spec.html" class="nav-link">前端手册 Vue 3.x</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="项目实战" class="dropdown-title"><a href="bpm.html" class="link-title">项目实战</a> <span class="title" style="display:none;">项目实战</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="bpm-preview.html" class="nav-link">工作流手册</a></li><li class="dropdown-item"><!----> <a href="report.html" class="nav-link">大屏手册</a></li><li class="dropdown-item"><!----> <a href="pay_build.html" class="nav-link">支付手册</a></li><li class="dropdown-item"><!----> <a href="member_build.html" class="nav-link">会员手册</a></li><li class="dropdown-item"><!----> <a href="mall_build.html" class="nav-link">商城手册</a></li><li class="dropdown-item"><!----> <a href="erp_build.html" class="nav-link">ERP 手册</a></li><li class="dropdown-item"><!----> <a href="crm_build.html" class="nav-link">CRM 手册</a></li><li class="dropdown-item"><!----> <a href="ai-preview.html" class="nav-link">AI 大模型手册</a></li><li class="dropdown-item"><!----> <a href="mp_build.html" class="nav-link">公众号手册</a></li><li class="dropdown-item"><!----> <a href="sms.html" class="nav-link">系统手册</a></li></ul></div></div><div class="nav-item"><a href="video.html" class="nav-link">视频教程</a></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="在线体验" class="dropdown-title"><!----> <span class="title" style="display:;">在线体验</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="http://dashboard-vue3.yudao.iocoder.cn/" target="_blank" rel="noopener noreferrer" class="nav-link external">
Vue3 + element-plus
<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li><li class="dropdown-item"><!----> <a href="http://dashboard-vben.yudao.iocoder.cn/" target="_blank" rel="noopener noreferrer" class="nav-link external">
Vue3 + vben(ant-design-vue)
<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li><li class="dropdown-item"><!----> <a href="http://dashboard.yudao.iocoder.cn/" target="_blank" rel="noopener noreferrer" class="nav-link external">
Vue2 + element-ui
<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li></ul></div></div><div class="nav-item"><a href="https://github.com/YunaiV/yudao-cloud" target="_blank" rel="noopener noreferrer" class="nav-link external">
微服务版
<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></div><div class="nav-item"><a href="https://www.iocoder.cn/" target="_blank" rel="noopener noreferrer" class="nav-link external">
作者博客
<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></div> <a href="https://github.com/YunaiV/ruoyi-vue-pro" target="_blank" rel="noopener noreferrer" class="repo-link">
GitHub
<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></nav> <ul class="sidebar-links"><li><section class="sidebar-group depth-0"><p class="sidebar-heading"><span>萌新必读</span> <!----></p> <ul class="sidebar-links sidebar-group-items"><li><a href="intro.html" class="sidebar-link">简介</a></li><li><a href="qun.html" class="sidebar-link">交流群</a></li><li><a href="video.html" class="sidebar-link">视频教程</a></li><li><a href="feature.html" class="sidebar-link">功能列表</a></li><li><a href="quick-start.html" class="sidebar-link">快速启动(后端项目)</a></li><li><a href="quick-start-front.html" class="sidebar-link">快速启动(前端项目)</a></li><li><a href="api-doc.html" class="sidebar-link">接口文档</a></li><li><a href="technology.html" class="sidebar-link">技术选型</a></li><li><a href="project-intro.html" class="sidebar-link">项目结构</a></li><li><a href="dev-hot-swap.html" class="sidebar-link">代码热加载</a></li><li><a href="project-rename.html" class="sidebar-link">一键改包</a></li><li><a href="migrate-module.html" class="sidebar-link">迁移模块(适合新项目)</a></li><li><a href="delete-code.html" class="sidebar-link">删除功能(以租户为例)</a></li><li><a href="sql-update.html" class="sidebar-link">表结构变更(版本升级)</a></li><li><a href="xinchuang-db.html" class="sidebar-link">国产信创数据库(DM 达梦、大金、OpenGauss)</a></li><li><a href="remove-redis.html" class="sidebar-link">如何去除 Redis 缓存</a></li><li><a href="natapp.html" class="sidebar-link">内网穿透</a></li><li><a href="interview.html" class="sidebar-link">面试题、简历模版、简历优化</a></li></ul></section></li><li><section class="sidebar-group depth-0"><p class="sidebar-heading open"><span>后端手册</span> <!----></p> <ul class="sidebar-links sidebar-group-items"><li><a href="module-new.html" class="sidebar-link">新建模块</a></li><li><a href="new-feature.html" class="sidebar-link">代码生成【单表】(新增功能)</a></li><li><a href="new-feature_master-sub.html" class="sidebar-link">代码生成【主子表】</a></li><li><a href="new-feature_tree.html" class="sidebar-link">代码生成(树表)</a></li><li><a href="resource-permission.html" class="sidebar-link">功能权限</a></li><li><a href="data-permission.html" class="sidebar-link">数据权限</a></li><li><a href="user-center.html" class="sidebar-link">用户体系</a></li><li><a href="social-user.html" class="sidebar-link">三方登录</a></li><li><a href="oauth2.html" class="sidebar-link">OAuth 2.0(SSO 单点登录)</a></li><li><a href="saas-tenant.html" aria-current="page" class="active sidebar-link">SaaS 多租户【字段隔离】</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header level2"><a href="saas-tenant_#_1-多租户是什么.html" class="sidebar-link">1. 多租户是什么?</a></li><li class="sidebar-sub-header level2"><a href="saas-tenant_#_2-数据隔离方案.html" class="sidebar-link">2. 数据隔离方案</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header level3"><a href="saas-tenant_#_2-1-datasource-模式.html" class="sidebar-link">2.1 DATASOURCE 模式</a></li><li class="sidebar-sub-header level3"><a href="saas-tenant_#_2-2-schema-模式.html" class="sidebar-link">2.2 SCHEMA 模式</a></li><li class="sidebar-sub-header level3"><a href="saas-tenant_#_2-3-column-模式.html" class="sidebar-link">2.3 COLUMN 模式</a></li><li class="sidebar-sub-header level3"><a href="saas-tenant_#_2-4-方案选择.html" class="sidebar-link">2.4 方案选择</a></li></ul></li><li class="sidebar-sub-header level2"><a href="saas-tenant_#_3-多租户的开关.html" class="sidebar-link">3. 多租户的开关</a></li><li class="sidebar-sub-header level2"><a href="saas-tenant_#_4-多租户的业务功能.html" class="sidebar-link">4. 多租户的业务功能</a></li><li class="sidebar-sub-header level2"><a href="saas-tenant_#_5-多租户的技术组件.html" class="sidebar-link">5. 多租户的技术组件</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header level3"><a href="saas-tenant_#_5-1-租户上下文.html" class="sidebar-link">5.1 租户上下文</a></li><li class="sidebar-sub-header level3"><a href="saas-tenant_#_5-2-web-层【重要】.html" class="sidebar-link">5.2 Web 层【重要】</a></li><li class="sidebar-sub-header level3"><a href="saas-tenant_#_5-3-security-层.html" class="sidebar-link">5.3 Security 层</a></li><li class="sidebar-sub-header level3"><a href="saas-tenant_#_5-4-db-层【重要】.html" class="sidebar-link">5.4 DB 层【重要】</a></li><li class="sidebar-sub-header level3"><a href="saas-tenant_#_5-5-redis-层【重要】.html" class="sidebar-link">5.5 Redis 层【重要】</a></li><li class="sidebar-sub-header level3"><a href="saas-tenant_#_5-6-aop【重要】.html" class="sidebar-link">5.6 AOP【重要】</a></li><li class="sidebar-sub-header level3"><a href="saas-tenant_#_5-7-job【重要】.html" class="sidebar-link">5.7 Job【重要】</a></li><li class="sidebar-sub-header level3"><a href="saas-tenant_#_5-8-mq.html" class="sidebar-link">5.8 MQ</a></li><li class="sidebar-sub-header level3"><a href="saas-tenant_#_5-9-async.html" class="sidebar-link">5.9 Async</a></li></ul></li><li class="sidebar-sub-header level2"><a href="saas-tenant_#_6-租户独立域名.html" class="sidebar-link">6. 租户独立域名</a></li></ul></li><li><a href="saas-tenant_dynamic.html" class="sidebar-link">SaaS 多租户【数据库隔离】</a></li><li><a href="websocket.html" class="sidebar-link">WebSocket 实时通信</a></li><li><a href="exception.html" class="sidebar-link">异常处理(错误码)</a></li><li><a href="validator.html" class="sidebar-link">参数校验、时间传参</a></li><li><a href="page-feature.html" class="sidebar-link">分页实现</a></li><li><a href="vo.html" class="sidebar-link">VO 对象转换、数据翻译</a></li><li><a href="file.html" class="sidebar-link">文件存储(上传下载)</a></li><li><a href="excel-import-and-export.html" class="sidebar-link">Excel 导入导出</a></li><li><a href="system-log.html" class="sidebar-link">操作日志、访问日志、异常日志</a></li><li><a href="mybatis.html" class="sidebar-link">MyBatis 数据库</a></li><li><a href="mybatis-pro.html" class="sidebar-link">MyBatis 联表&分页查询</a></li><li><a href="dynamic-datasource.html" class="sidebar-link">多数据源(读写分离)、事务</a></li><li><a href="redis-cache.html" class="sidebar-link">Redis 缓存</a></li><li><a href="local-cache.html" class="sidebar-link">本地缓存</a></li><li><a href="async-task.html" class="sidebar-link">异步任务</a></li><li><a href="distributed-lock.html" class="sidebar-link">分布式锁</a></li><li><a href="idempotent.html" class="sidebar-link">幂等性(防重复提交)</a></li><li><a href="rate-limiter.html" class="sidebar-link">请求限流(RateLimiter)</a></li><li><a href="http-sign.html" class="sidebar-link">HTTP 接口签名(防篡改)</a></li><li><a href="unit-test.html" class="sidebar-link">单元测试</a></li><li><a href="captcha.html" class="sidebar-link">验证码</a></li><li><a href="util.html" class="sidebar-link">工具类 Util</a></li><li><a href="config-center.html" class="sidebar-link">配置管理</a></li><li><a href="db-doc.html" class="sidebar-link">数据库文档</a></li></ul></section></li><li><section class="sidebar-group depth-0"><p class="sidebar-heading"><span>中间件手册</span> <!----></p> <ul class="sidebar-links sidebar-group-items"><li><a href="job.html" class="sidebar-link">定时任务</a></li><li><a href="message-queue_event.html" class="sidebar-link">消息队列(内存)</a></li><li><a href="message-queue_redis.html" class="sidebar-link">消息队列(Redis)</a></li><li><a href="message-queue_rocketmq.html" class="sidebar-link">消息队列(RocketMQ)</a></li><li><a href="message-queue_rabbitmq.html" class="sidebar-link">消息队列(RabbitMQ)</a></li><li><a href="message-queue_kafka.html" class="sidebar-link">消息队列(Kafka)</a></li><li><a href="server-protection.html" class="sidebar-link">限流熔断</a></li></ul></section></li><li><section class="sidebar-group depth-0"><p class="sidebar-heading"><span>工作流手册</span> <!----></p> <ul class="sidebar-links sidebar-group-items"><li><a href="bpm-preview.html" class="sidebar-link">工作流演示</a></li><li><a href="bpm.html" class="sidebar-link">功能开启</a></li><li><a href="bpm_dameng.html" class="sidebar-link">工作流(达梦适配)</a></li><li><a href="bpm_use-bpm-form.html" class="sidebar-link">审批接入(流程表单)</a></li><li><a href="bpm_use-business-form.html" class="sidebar-link">审批接入(业务表单)</a></li><li><a href="bpm_model-designer-bpmn.html" class="sidebar-link">流程设计器(BPMN)</a></li><li><a href="bpm_model-designer-dingding.html" class="sidebar-link">流程设计器(钉钉、飞书)</a></li><li><a href="bpm_assignee.html" class="sidebar-link">选择审批人、发起人自选</a></li><li><a href="bpm_multi-instance.html" class="sidebar-link">会签、或签、依次审批</a></li><li><a href="bpm_process-instance.html" class="sidebar-link">流程发起、取消、重新发起</a></li><li><a href="bpm_task-todo-done.html" class="sidebar-link">审批通过、不通过、驳回</a></li><li><a href="bpm_sign.html" class="sidebar-link">审批加签、减签</a></li><li><a href="bpm_task-delegation-and-cc.html" class="sidebar-link">审批转办、委派、抄送</a></li><li><a href="bpm_listener.html" class="sidebar-link">执行监听器、任务监听器</a></li><li><a href="bpm_expression.html" class="sidebar-link">流程表达式</a></li><li><a href="bpm_message.html" class="sidebar-link">流程审批通知</a></li></ul></section></li><li><section class="sidebar-group depth-0"><p class="sidebar-heading"><span>大屏手册</span> <!----></p> <ul class="sidebar-links sidebar-group-items"><li><a href="report.html" class="sidebar-link">报表设计器</a></li><li><a href="report_screen.html" class="sidebar-link">大屏设计器</a></li></ul></section></li><li><section class="sidebar-group depth-0"><p class="sidebar-heading"><span>支付手册</span> <!----></p> <ul class="sidebar-links sidebar-group-items"><li><a href="pay_build.html" class="sidebar-link">功能开启</a></li><li><a href="pay_alipay-pay-demo.html" class="sidebar-link">支付宝支付接入</a></li><li><a href="pay_wx-pub-pay-demo.html" class="sidebar-link">微信公众号支付接入</a></li><li><a href="pay_wx-lite-pay-demo.html" class="sidebar-link">微信小程序支付接入</a></li><li><a href="pay_refund-demo.html" class="sidebar-link">支付宝、微信退款接入</a></li><li><a href="pay_wallet.html" class="sidebar-link">钱包充值、支付、退款</a></li><li><a href="pay_mock.html" class="sidebar-link">模拟支付、退款</a></li></ul></section></li><li><section class="sidebar-group depth-0"><p class="sidebar-heading"><span>会员手册</span> <!----></p> <ul class="sidebar-links sidebar-group-items"><li><a href="member_build.html" class="sidebar-link">功能开启</a></li><li><a href="member_weixin-mp-login.html" class="sidebar-link">微信公众号登录</a></li><li><a href="member_weixin-lite-login.html" class="sidebar-link">微信小程序登录</a></li><li><a href="member_weixin-lite-subscribe-message.html" class="sidebar-link">微信小程序订阅消息</a></li><li><a href="member_weixin-lite-qrcode.html" class="sidebar-link">微信小程序码</a></li><li><a href="member_user.html" class="sidebar-link">会员用户、标签、分组</a></li><li><a href="member_level.html" class="sidebar-link">会员等级、积分、签到</a></li></ul></section></li><li><section class="sidebar-group depth-0"><p class="sidebar-heading"><span>商城手册</span> <!----></p> <ul class="sidebar-links sidebar-group-items"><li><a href="mall-preview.html" class="sidebar-link">商城演示</a></li><li><a href="mall_build.html" class="sidebar-link">功能开启</a></li><li><a href="mall_diy.html" class="sidebar-link">商城装修</a></li><li><a href="mall_kefu.html" class="sidebar-link">在线客服</a></li><li><a href="mall_product-category.html" class="sidebar-link">【商品】商品分类</a></li><li><a href="mall_product-property.html" class="sidebar-link">【商品】商品属性</a></li><li><a href="mall_product-spu-sku.html" class="sidebar-link">【商品】商品 SPU 与 SKU</a></li><li><a href="mall_product-comment.html" class="sidebar-link">【商品】商品评价</a></li><li><a href="mall_trade-cart.html" class="sidebar-link">【交易】购物车</a></li><li><a href="mall_trade-order.html" class="sidebar-link">【交易】交易订单</a></li><li><a href="mall_trade-aftersale.html" class="sidebar-link">【交易】售后退款</a></li><li><a href="mall_trade-delivery-express.html" class="sidebar-link">【交易】快递发货</a></li><li><a href="mall_trade-delivery-pickup.html" class="sidebar-link">【交易】门店自提</a></li><li><a href="mall_trade-brokerage.html" class="sidebar-link">【交易】分销返佣</a></li><li><a href="mall_promotion-coupon.html" class="sidebar-link">【营销】优惠劵</a></li><li><a href="mall_point-activity.html" class="sidebar-link">【营销】积分商城</a></li><li><a href="mall_promotion-combination.html" class="sidebar-link">【营销】拼团活动</a></li><li><a href="mall_promotion-seckill.html" class="sidebar-link">【营销】秒杀活动</a></li><li><a href="mall_promotion-bargain.html" class="sidebar-link">【营销】砍价活动</a></li><li><a href="mall_promotion-record.html" class="sidebar-link">【营销】满减送活动</a></li><li><a href="mall_promotion-discount.html" class="sidebar-link">【营销】限时折扣</a></li><li><a href="mall_promotion-content.html" class="sidebar-link">【营销】内容管理</a></li><li><a href="mall_statistics.html" class="sidebar-link">【统计】会员、商品、交易统计</a></li></ul></section></li><li><section class="sidebar-group depth-0"><p class="sidebar-heading"><span>ERP手册</span> <!----></p> <ul class="sidebar-links sidebar-group-items"><li><a href="erp-preview.html" class="sidebar-link">ERP 演示</a></li><li><a href="erp_build.html" class="sidebar-link">功能开启</a></li><li><a href="erp_product.html" class="sidebar-link">【产品】产品信息、分类、单位</a></li><li><a href="erp_stock.html" class="sidebar-link">【库存】产品库存、库存明细</a></li><li><a href="erp_stock-in-out.html" class="sidebar-link">【库存】其它入库、其它出库</a></li><li><a href="erp_stock-move-check.html" class="sidebar-link">【库存】库存调拨、库存盘点</a></li><li><a href="erp_purchase.html" class="sidebar-link">【采购】采购订单、入库、退货</a></li><li><a href="erp_sale.html" class="sidebar-link">【销售】销售订单、出库、退货</a></li><li><a href="sale_finance-payment-receipt.html" class="sidebar-link">【财务】采购付款、销售收款</a></li></ul></section></li><li><section class="sidebar-group depth-0"><p class="sidebar-heading"><span>CRM手册</span> <!----></p> <ul class="sidebar-links sidebar-group-items"><li><a href="crm-preview.html" class="sidebar-link">CRM 演示</a></li><li><a href="crm_build.html" class="sidebar-link">功能开启</a></li><li><a href="crm_clue.html" class="sidebar-link">【线索】线索管理</a></li><li><a href="crm_customer.html" class="sidebar-link">【客户】客户管理、公海客户</a></li><li><a href="crm_business.html" class="sidebar-link">【商机】商机管理、商机状态</a></li><li><a href="crm_contract.html" class="sidebar-link">【合同】合同管理、合同提醒</a></li><li><a href="crm_receivable.html" class="sidebar-link">【回款】回款管理、回款计划</a></li><li><a href="crm_product.html" class="sidebar-link">【产品】产品管理、产品分类</a></li><li><a href="crm_permission.html" class="sidebar-link">【通用】数据权限</a></li><li><a href="crm_follow-up.html" class="sidebar-link">【通用】跟进记录、待办事项</a></li></ul></section></li><li><section class="sidebar-group depth-0"><p class="sidebar-heading"><span>AI大模型手册</span> <!----></p> <ul class="sidebar-links sidebar-group-items"><li><a href="ai-preview.html" class="sidebar-link">AI 大模型演示</a></li><li><a href="ai_build.html" class="sidebar-link">功能开启</a></li><li><a href="ai_chat.html" class="sidebar-link">AI 聊天对话</a></li><li><a href="ai_image.html" class="sidebar-link">AI 绘画创作</a></li><li><a href="ai_music.html" class="sidebar-link">AI 音乐创作</a></li><li><a href="ai_write.html" class="sidebar-link">AI 写作助手</a></li><li><a href="ai_mindmap.html" class="sidebar-link">AI 思维导图</a></li><li><a href="ai_openai.html" class="sidebar-link">【模型接入】OpenAI</a></li><li><a href="ai_tongyi.html" class="sidebar-link">【模型接入】通义千问</a></li><li><a href="ai_deep-seek.html" class="sidebar-link">【模型接入】DeepSeek</a></li><li><a href="ai_doubao.html" class="sidebar-link">【模型接入】字节豆包</a></li><li><a href="ai_hunyuan.html" class="sidebar-link">【模型接入】腾讯混元</a></li><li><a href="ai_siliconflow.html" class="sidebar-link">【模型接入】硅基流动</a></li><li><a href="ai_yiyan.html" class="sidebar-link">【模型接入】文心一言</a></li><li><a href="ai_llama.html" class="sidebar-link">【模型接入】LLAMA</a></li><li><a href="ai_glm.html" class="sidebar-link">【模型接入】智谱 GLM</a></li><li><a href="ai_xinghuo.html" class="sidebar-link">【模型接入】讯飞星火</a></li><li><a href="ai_azure-openai.html" class="sidebar-link">【模型接入】微软 OpenAI</a></li><li><a href="ai_gemini.html" class="sidebar-link">【模型接入】谷歌 Gemini</a></li><li><a href="ai_stable-diffusion.html" class="sidebar-link">【模型接入】Stable Diffusion</a></li><li><a href="ai_midjourney.html" class="sidebar-link">【模型接入】Midjourney</a></li><li><a href="ai_suno.html" class="sidebar-link">【模型接入】Suno</a></li></ul></section></li><li><section class="sidebar-group depth-0"><p class="sidebar-heading"><span>公众号手册</span> <!----></p> <ul class="sidebar-links sidebar-group-items"><li><a href="mp_build.html" class="sidebar-link">功能开启</a></li><li><a href="mp_account.html" class="sidebar-link">公众号接入</a></li><li><a href="mp_user.html" class="sidebar-link">公众号粉丝</a></li><li><a href="mp_tag.html" class="sidebar-link">公众号标签</a></li><li><a href="mp_message.html" class="sidebar-link">公众号消息</a></li><li><a href="mp_auto-reply.html" class="sidebar-link">自动回复</a></li><li><a href="mp_menu.html" class="sidebar-link">公众号菜单</a></li><li><a href="mp_material.html" class="sidebar-link">公众号素材</a></li><li><a href="mp_article.html" class="sidebar-link">公众号图文</a></li><li><a href="mp_statistics.html" class="sidebar-link">公众号统计</a></li></ul></section></li><li><section class="sidebar-group depth-0"><p class="sidebar-heading"><span>系统手册</span> <!----></p> <ul class="sidebar-links sidebar-group-items"><li><a href="sms.html" class="sidebar-link">短信配置</a></li><li><a href="mail.html" class="sidebar-link">邮件配置</a></li><li><a href="notify.html" class="sidebar-link">站内信配置</a></li><li><a href="desensitize.html" class="sidebar-link">数据脱敏、字段权限</a></li><li><a href="sensitive-word.html" class="sidebar-link">敏感词</a></li><li><a href="area-and-ip.html" class="sidebar-link">地区 & IP 库</a></li></ul></section></li><li><section class="sidebar-group depth-0"><p class="sidebar-heading"><span>运维手册</span> <!----></p> <ul class="sidebar-links sidebar-group-items"><li><a href="dev-env.html" class="sidebar-link">开发环境</a></li><li><a href="deployment-linux.html" class="sidebar-link">Linux 部署</a></li><li><a href="deployment-docker.html" class="sidebar-link">Docker 部署</a></li><li><a href="deployment-jenkins.html" class="sidebar-link">Jenkins 部署</a></li><li><a href="deployment-baota.html" class="sidebar-link">宝塔部署</a></li><li><a href="https.html" class="sidebar-link">HTTPS 证书</a></li><li><a href="server-monitor.html" class="sidebar-link">服务监控</a></li></ul></section></li><li><section class="sidebar-group depth-0"><p class="sidebar-heading"><span>前端手册 Vue 3.x</span> <!----></p> <ul class="sidebar-links sidebar-group-items"><li><a href="vue3_dev-spec.html" class="sidebar-link">开发规范</a></li><li><a href="vue3_route.html" class="sidebar-link">菜单路由</a></li><li><a href="vue3_icon.html" class="sidebar-link">Icon 图标</a></li><li><a href="vue3_dict.html" class="sidebar-link">字典数据</a></li><li><a href="vue3_components.html" class="sidebar-link">系统组件</a></li><li><a href="vue3_util.html" class="sidebar-link">通用方法</a></li><li><a href="vue3_config-center.html" class="sidebar-link">配置读取</a></li><li><a href="vue3_crud-schema.html" class="sidebar-link">CRUD 组件</a></li><li><a href="vue3_i18n.html" class="sidebar-link">国际化</a></li><li><a href="vue3_debugger.html" class="sidebar-link">IDE 调试</a></li><li><a href="vue3_format.html" class="sidebar-link">代码格式化</a></li></ul></section></li><li><section class="sidebar-group depth-0"><p class="sidebar-heading"><span>前端手册 Vue 2.x</span> <!----></p> <ul class="sidebar-links sidebar-group-items"><li><a href="vue2_dev-spec.html" class="sidebar-link">开发规范</a></li><li><a href="vue2_route.html" class="sidebar-link">菜单路由</a></li><li><a href="vue2_icon.html" class="sidebar-link">Icon 图标</a></li><li><a href="vue2_dict.html" class="sidebar-link">字典数据</a></li><li><a href="vue2_components.html" class="sidebar-link">系统组件</a></li><li><a href="vue2_util.html" class="sidebar-link">通用方法</a></li><li><a href="vue2_config-center.html" class="sidebar-link">配置读取</a></li></ul></section></li><li><section class="sidebar-group depth-0"><p class="sidebar-heading"><span>更新日志</span> <!----></p> <ul class="sidebar-links sidebar-group-items"><li><a href="changelog_2.4.2.html" class="sidebar-link">【v2.4.2】开发中</a></li><li><a href="changelog_2.4.1.html" class="sidebar-link">【v2.4.1】2025-02-09</a></li><li><a href="changelog_2.4.0.html" class="sidebar-link">【v2.4.0】2024-12-31</a></li><li><a href="changelog_2.3.0.html" class="sidebar-link">【v2.3.0】2024-10-07</a></li><li><a href="changelog_2.2.0.html" class="sidebar-link">【v2.2.0】2024-08-02</a></li><li><a href="changelog_2.1.0.html" class="sidebar-link">【v2.1.0】2024-05-05</a></li><li><a href="changelog_2.0.1.html" class="sidebar-link">【v2.0.1】2024-03-01</a></li><li><a href="changelog_2.0.0.html" class="sidebar-link">【v2.0.0】2024-01-26</a></li></ul></section></li></ul> </aside> <div><main class="page"><div class="theme-vdoing-wrapper "><div class="articleInfo-wrap" data-v-06225672><div class="articleInfo" data-v-06225672><ul class="breadcrumbs" data-v-06225672><li data-v-06225672><a href="intro.html" title="首页" class="iconfont icon-home router-link-active" data-v-06225672></a></li> <li data-v-06225672><span data-v-06225672>开发指南</span></li><li data-v-06225672><span data-v-06225672>后端手册</span></li></ul> <div class="info" data-v-06225672><div title="作者" class="author iconfont icon-touxiang" data-v-06225672><a href="https://www.iocoder.cn" target="_blank" title="作者" class="beLink" data-v-06225672>芋道源码</a></div> <div title="创建时间" class="date iconfont icon-riqi" data-v-06225672><a href="javascript:;" data-v-06225672>2022-03-07</a></div> <!----></div></div></div> <!----> <div class="content-wrapper"><div class="right-menu-wrapper"><div class="right-menu-margin"><div class="right-menu-title">目录</div> <div class="right-menu-content"></div></div></div> <h1><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAYAAAA7MK6iAAAAAXNSR0IArs4c6QAABKFJREFUSA3tVl1oFVcQnrMbrak3QUgkya1akpJYcrUtIqW1JvFBE9LiQ5v6JmJpolbMg32rVrhgoYK0QiMY6i9Y6EMaW5D+xFJaTYItIuK2Kr3+BJNwkxBj05sQY3b3nM6cs2dv9t7NT/vQJw/sndk5M/PNzJkzewGerP+pAmy+ON8lLzUJgA8ZYxYIYZmGYRnctDaWvJJAmTtfP1pvXsBCCPP8QFcCaRkZYACgDZFO4stNIcBCajEOlmmC9XpJ9bAGCaPaPmzPl32dvLSVu3BWCTQs0XQQ6g0DYgwLIoAZbBCdW/i+781o1VVlm/410mw4h06Y7bIPHNyWDyL4FHkX03Q8SrzNhZTZriieckWt7cL6MM85YcLpsi/7O9/iXFT6MswI0DmmpkSaJ0qLxFIm3+i1THHB3zmBH3PYx9CcykcLOeQVVa7QtdxTgQgEleX2AjHYfwA+2ddV77ruGoJUbhGDI09YSNXyMpUt5ylOzxgbUmtOp7NmbNt8v3arjTBfYELmLUV+M+nSawNNAUqpT3ClJWg5I3BLT+cGW/DXNGCa6tx1aakCGEigArTn4TDIPdrXXYKCZNrHLMCOEPvHBlLQ99s9eHB7EB6NTki73CVPQ2F5MSx/uRQixfmq7rK0wYD8w8E905bnPDfwoWs/rfv93NWN/ZfvwsLIU7A09gxECyISeGJkHAau98L97tuw7NXnoPyNF8FcYGLGKsOs0mN3OEyec9esGW/ZEl945dTP34wlR2FZVQWU1q0Cw8Tr7p+hgLLNL0FPxx/Q35mA8aEUrH6nCgwEl0tn7wUiZYJnNRh6DK4UH/k0lfyrsBKdPVv/AriGIQcEDQZ65LBAGe2Rzui9Ybjz7XUppz1/uKBbyVPGkN3ZAeC6hr0x7Nr38N5+EqkoOm17xpoqR9ohQF55ERSvr4Dkr3chNfC3DMzGJlNBElW8w9nsGQvhNGIzDkXzCg8cLK951xHsFBlTJspJNi3ZFIMF2AeDV3q8DNOB+YHi6QTrChDIWDBRi5U5f+ZMfJLu3ccrqxtdxk4SKH336LFxSmkqefwU5T8fhdSdQf9IVKD6aNiwI/hnmcAZ91isYMJIaCUCx9W098+LgruikeTqzqqxKPUwqJyCPJiyemVVZBOijDGjD38Os0jOiSPL1z3SPjXNANbiNPXAdzTfukjjuknNBbyz3nwgTd3AVFqUJ5hpHlq9MveLnWwttUfoygBmvVjuikxND3znrhsELnZk7k+OjIGxeNEkomyLVta0xxn+HZhjBc4YZ/AFjHjz9u3xRZl2BN4aq9nFwWh16IrQ1aHHEd3j1+4/dB9OtH4e29A2H1DyHQRmOSfQZ1Fy7MHBTGB6J/Djq6p3OxyO2cB+4Car7v/o3GXgfAkj23+x9ID1Teoamo/SXcbvSf2PX7Vc8DdCmE1vN9di+32P9/5YR3vLnhCVGUWBjEkr3yh4H8v9CzmsbdhzOKzsJKM90iFdaTMjRPhGVsakRvOaRidljo6H6G7j+ctrJpsP+4COhDIl0La2+FS4+5mlocBaXY5QnGZysIBYoeSsl5qQzrSj/cgNrfuEzlWBfwA+EjrZyWUvpAAAAABJRU5ErkJggg==">SaaS 多租户【字段隔离】<!----></h1> <div class="theme-vdoing-content content__default"><p>本章节,将介绍多租户的基础知识、以及怎样使用多租户的功能。</p> <p>相关的视频教程:</p> <ul><li><a href="https://t.zsxq.com/06ufyFAeM" target="_blank" rel="noopener noreferrer">01、如何实现多租户的 DB 封装?<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li> <li><a href="https://t.zsxq.com/067eQfAQN" target="_blank" rel="noopener noreferrer">02、如何实现多租户的 Redis 封装?<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li> <li><a href="https://t.zsxq.com/06Nnm6QBE" target="_blank" rel="noopener noreferrer">03、如何实现多租户的 Web 与 Security 封装?<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li> <li><a href="https://t.zsxq.com/06AYJUR3V" target="_blank" rel="noopener noreferrer">04、如何实现多租户的 Job 封装?<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li> <li><a href="https://t.zsxq.com/06aq3nuNF" target="_blank" rel="noopener noreferrer">05、如何实现多租户的 MQ 与 Async 封装?<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li> <li><a href="https://t.zsxq.com/06vFQVJIe" target="_blank" rel="noopener noreferrer">06、如何实现多租户的 AOP 与 Util 封装?<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li> <li><a href="https://t.zsxq.com/063bqRrNZ" target="_blank" rel="noopener noreferrer">07、如何实现多租户的管理?<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li> <li><a href="https://t.zsxq.com/06rBI66yV" target="_blank" rel="noopener noreferrer">08、如何实现多租户的套餐?<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li></ul> <h2 id="_1-多租户是什么"><a href="#_1-多租户是什么" class="header-anchor">#</a> 1. 多租户是什么?</h2> <p>多租户,简单来说是指<strong>一个</strong>业务系统,可以为<strong>多个</strong>组织服务,并且组织之间的数据是<strong>隔离</strong>的。</p> <p>例如说,在服务上部署了一个 <a href="https://github.com/YunaiV/ruoyi-vue-pro" target="_blank" rel="noopener noreferrer"><code>ruoyi-vue-pro</code><span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a> 系统,可以支持多个不同的公司使用。这里的<strong>一个公司就是一个租户</strong>,每个用户必然属于某个租户。因此,用户也只能看见自己租户下面的内容,其它租户的内容对他是不可见的。</p> <h2 id="_2-数据隔离方案"><a href="#_2-数据隔离方案" class="header-anchor">#</a> 2. 数据隔离方案</h2> <p>多租户的数据隔离方案,可以分成分成三种:</p> <ol><li>DATASOURCE 模式:独立数据库</li> <li>SCHEMA 模式:共享数据库,独立 Schema</li> <li>COLUMN 模式:共享数据库,共享 Schema,共享数据表</li></ol> <h3 id="_2-1-datasource-模式"><a href="#_2-1-datasource-模式" class="header-anchor">#</a> 2.1 DATASOURCE 模式</h3> <p>一个租户一个数据库,这种方案的用户数据隔离级别最高,安全性最好,但成本也高。</p> <p><img src="https://doc.iocoder.cn/img/Saas%E5%A4%9A%E7%A7%9F%E6%88%B7/DATASOURCE%E6%A8%A1%E5%BC%8F.png" alt="DATASOURCE 模式"></p> <ul><li>优点:为不同的租户提供独立的数据库,有助于简化数据模型的扩展设计,满足不同租户的独特需求;如果出现故障,恢复数据比较简单。</li> <li>缺点:增大了数据库的安装数量,随之带来维护成本和购置成本的增加。</li></ul> <h3 id="_2-2-schema-模式"><a href="#_2-2-schema-模式" class="header-anchor">#</a> 2.2 SCHEMA 模式</h3> <p>多个或所有租户共享数据库,但一个租户一个表。</p> <p><img src="https://doc.iocoder.cn/img/Saas%E5%A4%9A%E7%A7%9F%E6%88%B7/SCHEMA%E6%A8%A1%E5%BC%8F.png" alt="SCHEMA 模式"></p> <ul><li>优点:为安全性要求较高的租户提供了一定程度的逻辑数据隔离,并不是完全隔离;每个数据库可以支持更多的租户数量。</li> <li>缺点:如果出现故障,数据恢复比较困难,因为恢复数据库将牵扯到其他租户的数据; 如果需要跨租户统计数据,存在一定困难。</li></ul> <h3 id="_2-3-column-模式"><a href="#_2-3-column-模式" class="header-anchor">#</a> 2.3 COLUMN 模式</h3> <p>共享数据库,共享数据架构。租户共享同一个数据库、同一个表,但在表中通过 <code>tenant_id</code> 字段区分租户的数据。这是共享程度最高、隔离级别最低的模式。</p> <p><img src="https://doc.iocoder.cn/img/Saas%E5%A4%9A%E7%A7%9F%E6%88%B7/COLUMN%E6%A8%A1%E5%BC%8F.png" alt="COLUMN 模式"></p> <ul><li>优点:维护和购置成本最低,允许每个数据库支持的租户数量最多。</li> <li>缺点:隔离级别最低,安全性最低,需要在设计开发时加大对安全的开发量;数据备份和恢复最困难,需要逐表逐条备份和还原。</li></ul> <h3 id="_2-4-方案选择"><a href="#_2-4-方案选择" class="header-anchor">#</a> 2.4 方案选择</h3> <p><img src="https://doc.iocoder.cn/img/Saas%E5%A4%9A%E7%A7%9F%E6%88%B7/%E6%A8%A1%E5%BC%8F%E9%80%89%E6%8B%A9.png" alt="模式选择"></p> <ul><li>一般情况下,可以考虑采用 COLUMN 模式,开发、运维简单,以最少的服务器为最多的租户提供服务。</li> <li>租户规模比较大,或者一些租户对安全性要求较高,可以考虑采用 DATASOURCE 模式,当然它也相对复杂的多。</li> <li>不推荐采用 SCHEMA 模式,因为它的优点并不明显,而且它的缺点也很明显,同时对复杂 SQL 支持一般。</li></ul> <div class="custom-block tip"><p class="custom-block-title">提问:项目支持哪些模式?</p> <p>目前支持最主流的 DATASOURCE 和 COLUMN 两种模式。而 SCHEMA 模式不推荐使用,所以暂时不考虑实现。</p></div> <p>考虑到让大家更好的理解 DATASOURCE 和 COLUMN 模式,拆成了两篇文章:</p> <ul><li><a href="saas-tenant.html">《SaaS 多租户【字段隔离】》</a>:讲解 COLUMN 模式</li> <li><a href="saas-tenant_dynamic.html">《SaaS 多租户【数据库隔离】》</a>:讲解 DATASOURCE 模式</li></ul> <h2 id="_3-多租户的开关"><a href="#_3-多租户的开关" class="header-anchor">#</a> 3. 多租户的开关</h2> <p>系统有两个配置项,设置为 <code>true</code> 时开启多租户,设置为 <code>false</code> 时关闭多租户。</p> <p>注意,两者需要保持一致,否则会报错!</p> <table><thead><tr><th>配置项</th> <th>说明</th> <th>配置文件</th></tr></thead> <tbody><tr><td><code>yudao.server.tenant</code></td> <td>后端开关</td> <td><img src="https://doc.iocoder.cn/img/Saas%E5%A4%9A%E7%A7%9F%E6%88%B7/01.png" alt="示例"></td></tr> <tr><td><code>VUE_APP_TENANT_ENABLE</code></td> <td>前端开关</td> <td><img src="https://doc.iocoder.cn/img/Saas%E5%A4%9A%E7%A7%9F%E6%88%B7/02.png" alt="示例"></td></tr></tbody></table> <div class="custom-block tip"><p class="custom-block-title">疑问:为什么要设置两个配置项?</p> <p>前端登录界面需要使用到多租户的配置项,从后端加载配置项的话,体验会比较差。</p></div> <h2 id="_4-多租户的业务功能"><a href="#_4-多租户的业务功能" class="header-anchor">#</a> 4. 多租户的业务功能</h2> <p>多租户主要有两个业务功能:</p> <table><thead><tr><th>业务功能</th> <th>说明</th> <th>界面</th> <th>代码</th></tr></thead> <tbody><tr><td>租户管理</td> <td>配置系统租户,创建对应的租户管理员</td> <td><img src="https://doc.iocoder.cn/img/Saas%E5%A4%9A%E7%A7%9F%E6%88%B7/03.png" alt="租户管理"></td> <td><a href="https://github.com/YunaiV/ruoyi-vue-pro/blob/master/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantServiceImpl.java" target="_blank" rel="noopener noreferrer">后端<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a> <a href="https://github.com/yudaocode/yudao-ui-admin-vue2/blob/master/src/views/system/tenant/index.vue" target="_blank" rel="noopener noreferrer">前端<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></td></tr> <tr><td>租户套餐</td> <td>配置租户套餐,自定每个租户的菜单、操作、按钮的权限</td> <td><img src="https://doc.iocoder.cn/img/Saas%E5%A4%9A%E7%A7%9F%E6%88%B7/04.png" alt="租户套餐"></td> <td><a href="https://github.com/YunaiV/ruoyi-vue-pro/blob/master/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantPackageServiceImpl.java" target="_blank" rel="noopener noreferrer">后端<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a> <a href="https://github.com/yudaocode/yudao-ui-admin-vue2/blob/master/src/views/system/tenantPackage/index.vue" target="_blank" rel="noopener noreferrer">前端<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></td></tr></tbody></table> <p><strong>下面,我们来新增一个租户,它使用 COLUMN 模式。</strong></p> <p>① 点击 [租户管理] 菜单,点击 [新增] 按钮,填写租户的信息。</p> <p><img src="https://doc.iocoder.cn/img/Saas%E5%A4%9A%E7%A7%9F%E6%88%B7/05.png" alt="新增租户"></p> <p>② 点击 [确认] 按钮,完成租户的创建,它会自动创建对应的租户管理员、角色等信息。</p> <p><img src="https://doc.iocoder.cn/img/Saas%E5%A4%9A%E7%A7%9F%E6%88%B7/06.png" alt="租户的管理员、角色"></p> <p>③ 退出系统,登录刚创建的租户。</p> <p><img src="https://doc.iocoder.cn/img/Saas%E5%A4%9A%E7%A7%9F%E6%88%B7/07.png" alt="登录界面"></p> <p>至此,我们已经完成了租户的创建。</p> <h2 id="_5-多租户的技术组件"><a href="#_5-多租户的技术组件" class="header-anchor">#</a> 5. 多租户的技术组件</h2> <p>技术组件 <a href="https://github.com/YunaiV/ruoyi-vue-pro/tree/master/yudao-framework/yudao-spring-boot-starter-biz-tenant/" target="_blank" rel="noopener noreferrer"><code>yudao-spring-boot-starter-biz-tenant</code><span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a>,实现透明化的多租户能力,针对 Web、Security、DB、Redis、AOP、Job、MQ、Async 等多个层面进行封装。</p> <h3 id="_5-1-租户上下文"><a href="#_5-1-租户上下文" class="header-anchor">#</a> 5.1 租户上下文</h3> <p><a href="https://github.com/YunaiV/ruoyi-vue-pro/blob/master/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/context/TenantContextHolder.java" target="_blank" rel="noopener noreferrer">TenantContextHolder<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a> 是租户上下文,通过 ThreadLocal 实现租户编号的共享与传递。</p> <p>通过调用 TenantContextHolder 的 <code>#getTenantId()</code> <strong>静态</strong>方法,获得当前的租户编号。绝绝绝大多数情况下,并不需要。</p> <h3 id="_5-2-web-层【重要】"><a href="#_5-2-web-层【重要】" class="header-anchor">#</a> 5.2 Web 层【重要】</h3> <blockquote><p>实现可见 <a href="https://github.com/YunaiV/ruoyi-vue-pro/tree/master/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/web" target="_blank" rel="noopener noreferrer"><code>web</code><span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a> 包。</p></blockquote> <p>默认情况下,前端的每个请求 Header <strong>必须</strong>带上 <code>tenant-id</code>,值为租户编号,即 <code>system_tenant</code> 表的主键编号。</p> <p><img src="https://doc.iocoder.cn/img/Saas%E5%A4%9A%E7%A7%9F%E6%88%B7/08.png" alt="请求示例"></p> <p>如果不带该请求头,会报“租户的请求未传递,请进行排查”错误提示。</p> <p>😜 通过 <code>yudao.tenant.ignore-urls</code> 配置项,可以设置哪些 URL 无需带该请求头。例如说:</p> <p><img src="https://doc.iocoder.cn/img/Saas%E5%A4%9A%E7%A7%9F%E6%88%B7/09.png" alt=" 配置项"></p> <h3 id="_5-3-security-层"><a href="#_5-3-security-层" class="header-anchor">#</a> 5.3 Security 层</h3> <blockquote><p>实现可见 <a href="https://github.com/YunaiV/ruoyi-vue-pro/tree/master/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/security" target="_blank" rel="noopener noreferrer"><code>security</code><span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a> 包。</p></blockquote> <p>主要是校验登录的用户,校验是否有权限访问该租户,避免越权问题。</p> <h3 id="_5-4-db-层【重要】"><a href="#_5-4-db-层【重要】" class="header-anchor">#</a> 5.4 DB 层【重要】</h3> <blockquote><p>实现可见 <a href="https://github.com/YunaiV/ruoyi-vue-pro/tree/master/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/db" target="_blank" rel="noopener noreferrer"><code>db</code><span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a> 包。</p></blockquote> <p>COLUMN 模式,基于 MyBatis Plus 自带的<a href="https://baomidou.com/pages/aef2f2/" target="_blank" rel="noopener noreferrer">多租户<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a>功能实现。</p> <p>核心:每次对数据库操作时,它会<strong>自动</strong>拼接 <code>WHERE tenant_id = ?</code> 条件来进行租户的过滤,并且基本支持所有的 SQL 场景。</p> <p>如下是具体方式:</p> <p>① <strong>需要</strong>开启多租户的表,必须添加 <code>tenant_id</code> 字段。例如说 <code>system_users</code>、<code>system_role</code> 等表。</p> <div class="language-SQL extra-class"><pre class="language-sql"><code><span class="token keyword">CREATE</span> <span class="token keyword">TABLE</span> <span class="token identifier"><span class="token punctuation">`</span>system_role<span class="token punctuation">`</span></span> <span class="token punctuation">(</span>
<span class="token identifier"><span class="token punctuation">`</span>id<span class="token punctuation">`</span></span> <span class="token keyword">bigint</span> <span class="token operator">NOT</span> <span class="token boolean">NULL</span> <span class="token keyword">AUTO_INCREMENT</span> <span class="token keyword">COMMENT</span> <span class="token string">'角色ID'</span><span class="token punctuation">,</span>
<span class="token identifier"><span class="token punctuation">`</span>name<span class="token punctuation">`</span></span> <span class="token keyword">varchar</span><span class="token punctuation">(</span><span class="token number">30</span><span class="token punctuation">)</span> <span class="token keyword">CHARACTER</span> <span class="token operator">NOT</span> <span class="token boolean">NULL</span> <span class="token keyword">COMMENT</span> <span class="token string">'角色名称'</span><span class="token punctuation">,</span>
<span class="token identifier"><span class="token punctuation">`</span>tenant_id<span class="token punctuation">`</span></span> <span class="token keyword">bigint</span> <span class="token operator">NOT</span> <span class="token boolean">NULL</span> <span class="token keyword">DEFAULT</span> <span class="token string">'0'</span> <span class="token keyword">COMMENT</span> <span class="token string">'租户编号'</span><span class="token punctuation">,</span>
<span class="token keyword">PRIMARY</span> <span class="token keyword">KEY</span> <span class="token punctuation">(</span><span class="token identifier"><span class="token punctuation">`</span>id<span class="token punctuation">`</span></span><span class="token punctuation">)</span> <span class="token keyword">USING</span> <span class="token keyword">BTREE</span>
<span class="token punctuation">)</span> <span class="token keyword">ENGINE</span><span class="token operator">=</span><span class="token keyword">InnoDB</span> <span class="token keyword">AUTO_INCREMENT</span><span class="token operator">=</span><span class="token number">1</span> <span class="token keyword">COMMENT</span><span class="token operator">=</span><span class="token string">'角色信息表'</span><span class="token punctuation">;</span>
</code></pre></div><p>并且该表对应的 DO 需要使用到 <code>tenantId</code> 属性时,建议继承 <a href="https://github.com/YunaiV/ruoyi-vue-pro/blob/master/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/db/TenantBaseDO.java" target="_blank" rel="noopener noreferrer">TenantBaseDO<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a> 类。</p> <p>② <strong>无需</strong>开启多租户的表,需要添加表名到 <code>yudao.tenant.ignore-tables</code> 配置项目。例如说:</p> <p><img src="https://doc.iocoder.cn/img/Saas%E5%A4%9A%E7%A7%9F%E6%88%B7/10.png" alt=" 配置项"></p> <p>如果不配置的话,MyBatis Plus 会自动拼接 <code>WHERE tenant_id = ?</code> 条件,导致报 <code>tenant_id</code> 字段不存在的错误。</p> <div class="custom-block tip"><p class="custom-block-title">友情提示:MyBatis Plus 的多租户方案,在我们在 MyBatis XML 手写 SQL 时,是不生效的,即不会拼接 `tenant_id` 字段!!!</p> <p>解决方案:需要手动自己拼接,可见 <code>ErpPurchaseStatisticsMapper.xml</code> 案例,如下所示:</p> <div class="language-SQL extra-class"><pre class="language-sql"><code>tenant_id <span class="token operator">=</span> ${<span class="token variable">@cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder</span><span class="token variable">@getRequiredTenantId</span><span class="token punctuation">(</span><span class="token punctuation">)</span>}
</code></pre></div><ul><li>其中,后面 <code>${@...}</code> 一串,是 MyBatis 调用静态方法的方式,即使用 TenantContextHolder 的 <code>#getRequiredTenantId()</code> 方法,获得当前的租户编号。</li></ul> <p>补充说明:后续和球友沟通下来,部分简单 SQL 情况下,MyBatis Plus 还是会拼接。可见 <a href="https://t.zsxq.com/O8ys4" target="_blank" rel="noopener noreferrer">https://t.zsxq.com/O8ys4<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a> 帖子,欢迎讨论!</p></div> <h3 id="_5-5-redis-层【重要】"><a href="#_5-5-redis-层【重要】" class="header-anchor">#</a> 5.5 Redis 层【重要】</h3> <blockquote><p>实现可见 <a href="https://github.com/YunaiV/ruoyi-vue-pro/tree/master/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/redis" target="_blank" rel="noopener noreferrer"><code>redis</code><span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a> 包。</p></blockquote> <p>由于 Redis 不同于 DB 有 <code>tenant_id</code> 字段,无法通过类似 <code>WHERE tenant_id</code> = ? 的方式过滤,所以需要通过在 Redis Key 上增加 <code>:t{tenantId}</code> 后缀的方式,进行租户之间的隔离。</p> <p>例如说,假设 Redis Key 是 <code>user:%d</code>,示例是 <code>user:1024</code>;对应到多租户 1 的 Redis Key 是 <code>user:t1:1024</code>。</p> <div class="custom-block tip"><p class="custom-block-title">为什么 Redis Key 要多租户隔离呢?</p> <ul><li>① 在使用 DATASOURCE 模式时,不同库的相同表的 id 可能相同,例如说 A 库的用户,和 B 库的用户都是 1024,直接缓存会存在 Redis Key 的冲突。</li> <li>② 在所有模式下,跨租户可能存在相同的需要唯一的数据,例如说用户的手机号,直接缓存会存在 Redis Key 的冲突。</li></ul></div> <h4 id="使用方式一-基于-spring-cache-redis【推荐】"><a href="#使用方式一-基于-spring-cache-redis【推荐】" class="header-anchor">#</a> 使用方式一:基于 Spring Cache + Redis【推荐】</h4> <p>只需要一步,在方法上添加 Spring Cache 注解,例如说 <code>@Cachable</code>、<code>@CachePut</code>、<code>@CacheEvict</code>。</p> <p>具体的实现原理,可见 <a href="https://github.com/YunaiV/ruoyi-vue-pro/blob/master/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/redis/TenantRedisCacheManager.java" target="_blank" rel="noopener noreferrer">TenantRedisCacheManager<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a> 的源码。</p> <p>注意!!!默认配置下,Spring Cache 都开启 Redis Key 的多租户隔离。如果不需要,可以将 Key 添加到 <code>yudao.tenant.ignore-caches</code> 配置项中。如下图所示:</p> <p><img src="https://doc.iocoder.cn/img/Saas%E5%A4%9A%E7%A7%9F%E6%88%B7/%E5%BF%BD%E7%95%A5%E5%A4%9A%E7%A7%9F%E6%88%B7RedisKey.png" alt=" 配置项"></p> <h4 id="使用方式二-基于-redistemplate-tenantrediskeydefine"><a href="#使用方式二-基于-redistemplate-tenantrediskeydefine" class="header-anchor">#</a> 使用方式二:基于 RedisTemplate + TenantRedisKeyDefine</h4> <p>暂时没有合适的封装,需要在自己 format Redis Key 的时候,手动将 <code>:t{tenantId}</code> 后缀拼接上。</p> <p>这也是为什么,我推荐你使用 Spring Cache + Redis 的原因!</p> <h3 id="_5-6-aop【重要】"><a href="#_5-6-aop【重要】" class="header-anchor">#</a> 5.6 AOP【重要】</h3> <blockquote><p>实现可见 <a href="https://github.com/YunaiV/ruoyi-vue-pro/tree/master/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/aop" target="_blank" rel="noopener noreferrer"><code>aop</code><span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a> 包。</p></blockquote> <p>① 声明 <a href="https://github.com/YunaiV/ruoyi-vue-pro/blob/master/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/aop/TenantIgnore.java" target="_blank" rel="noopener noreferrer"><code>@TenantIgnore</code><span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a> 注解在方法上,标记指定方法不进行租户的自动过滤,避免<strong>自动</strong>拼接 <code>WHERE tenant_id = ?</code> 条件等等。</p> <p>例如说:<a href="https://github.com/YunaiV/ruoyi-vue-pro/blob/master/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImpl.java" target="_blank" rel="noopener noreferrer">RoleServiceImpl<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a> 的 <a href="https://github.com/YunaiV/ruoyi-vue-pro/blob/master/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImpl.java#L83-L100" target="_blank" rel="noopener noreferrer"><code>#initLocalCache()</code><span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a> 方法,加载<strong>所有</strong>租户的角色到内存进行缓存,如果不声明 <code>@TenantIgnore</code> 注解,会导致租户的自动过滤,只加载了某个租户的角色。</p> <div class="language-Java extra-class"><pre class="language-java"><code><span class="token comment">// RoleServiceImpl.java</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">RoleServiceImpl</span> <span class="token keyword">implements</span> <span class="token class-name">RoleService</span> <span class="token punctuation">{</span>
<span class="token annotation punctuation">@Resource</span>
<span class="token annotation punctuation">@Lazy</span> <span class="token comment">// 注入自己,所以延迟加载</span>
<span class="token keyword">private</span> <span class="token class-name">RoleService</span> self<span class="token punctuation">;</span>
<span class="token annotation punctuation">@Override</span>
<span class="token annotation punctuation">@PostConstruct</span>
<span class="token annotation punctuation">@TenantIgnore</span> <span class="token comment">// 忽略自动多租户,全局初始化缓存</span>
<span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">initLocalCache</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token comment">// ... 从数据库中,加载角色</span>
<span class="token punctuation">}</span>
<span class="token annotation punctuation">@Scheduled</span><span class="token punctuation">(</span>fixedDelay <span class="token operator">=</span> <span class="token constant">SCHEDULER_PERIOD</span><span class="token punctuation">,</span> initialDelay <span class="token operator">=</span> <span class="token constant">SCHEDULER_PERIOD</span><span class="token punctuation">)</span>
<span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">schedulePeriodicRefresh</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
self<span class="token punctuation">.</span><span class="token function">initLocalCache</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// <x> 通过 self 引用到 Spring 代理对象</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre></div><p>有一点要格外注意,由于 <code>@TenantIgnore</code> 注解是基于 Spring AOP 实现,如果是<strong>方法内部的调用</strong>,避免使用 <code>this</code> 导致不生效,可以采用上述示例的 <code><x></code> 处的 <code>self</code> 方式。</p> <p>② 使用 <a href="https://github.com/YunaiV/ruoyi-vue-pro/blob/master/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/util/TenantUtils.java" target="_blank" rel="noopener noreferrer">TenantUtils<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a> 的 <code>#execute(Long tenantId, Runnable runnable)</code> 方法,模拟指定租户( <code>tenantId</code> ),执行某段业务逻辑( <code>runnable</code> )。</p> <p>例如说:在 <a href="https://github.com/YunaiV/ruoyi-vue-pro/blob/master/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantServiceImpl.java" target="_blank" rel="noopener noreferrer">TenantServiceImpl<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a> 的 <code>#createTenant(...)</code> 方法,在创建完租户时,需要模拟该租户,进行用户和角色的创建。如下图所示:</p> <p><img src="https://doc.iocoder.cn/img/Saas%E5%A4%9A%E7%A7%9F%E6%88%B7/11.png" alt="TenantUtils 模拟租户"></p> <h3 id="_5-7-job【重要】"><a href="#_5-7-job【重要】" class="header-anchor">#</a> 5.7 Job【重要】</h3> <blockquote><p>实现可见 <a href="https://github.com/YunaiV/ruoyi-vue-pro/tree/master/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/job" target="_blank" rel="noopener noreferrer"><code>job</code><span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a> 包。</p></blockquote> <p>声明 <a href="https://github.com/YunaiV/ruoyi-vue-pro/blob/master/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/job/TenantJob.java" target="_blank" rel="noopener noreferrer"><code>@TenantJob</code><span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a> 注解在 Job 方法上,实现<strong>并行</strong>遍历每个租户,执行定时任务的逻辑。</p> <h3 id="_5-8-mq"><a href="#_5-8-mq" class="header-anchor">#</a> 5.8 MQ</h3> <blockquote><p>实现可见 <a href="https://github.com/YunaiV/ruoyi-vue-pro/tree/master/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq" target="_blank" rel="noopener noreferrer"><code>mq</code><span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a> 包。</p></blockquote> <p>通过租户对 MQ 层面的封装,实现租户上下文,可以继续传递到 MQ 消费的逻辑中,避免丢失的问题。实现原理是:</p> <ul><li>发送消息时,MQ 会将租户上下文的租户编号,记录到 Message 消息头 <code>tenant-id</code> 上。</li> <li>消费消息时,MQ 会将 Message 消息头 <code>tenant-id</code>,设置到租户上下文的租户编号。</li></ul> <h3 id="_5-9-async"><a href="#_5-9-async" class="header-anchor">#</a> 5.9 Async</h3> <blockquote><p>实现可见 <a href="https://github.com/YunaiV/ruoyi-vue-pro/blob/master/yudao-framework/yudao-spring-boot-starter-job/src/main/java/cn/iocoder/yudao/framework/quartz/config/YudaoAsyncAutoConfiguration.java" target="_blank" rel="noopener noreferrer"><code>YudaoAsyncAutoConfiguration</code><span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a> 类。</p></blockquote> <p>通过使用阿里开源的 <a href="https://github.com/alibaba/transmittable-thread-local" target="_blank" rel="noopener noreferrer">TransmittableThreadLocal<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a> 组件,实现 Spring Async 执行异步逻辑时,租户上下文可以继续传递,避免丢失的问题。</p> <h2 id="_6-租户独立域名"><a href="#_6-租户独立域名" class="header-anchor">#</a> 6. 租户独立域名</h2> <p>在我们使用 SaaS 云产品的时候,每个租户会拥有 <strong>独立的子域名</strong>,例如说:租户 A 对应 <code>a.iocoder.cn</code>,租户 B 对应 <code>b.iocoder.cn</code>。</p> <p>目前管理后台已经提供类似的能力,更多大家可以基于它去拓展。实现方式:</p> <ol><li>在 <code>system_tenant</code> 表里,有个 <code>website</code> 字段为该租户的独立域名,你可以填写你希望分配给它的子域名。</li> <li>在 Nginx 上做 <strong>泛域名解析</strong> 到你的前端项目,例如说 Nginx 的 <code>server_name</code> <code>*.iocoder.cn</code> 解析到 Vue3 管理后台。</li></ol> <p>这样用户在访问管理后台的登录界面,会自动根据当前访问域名的 <code>host</code>,向后端获得对应的 <code>tenant-id</code> 编号,后续请求都带上它!</p> <p>ps:商城 uniapp 暂时还没做,感兴趣可以 pull request 贡献下噢!</p></div></div> <div class="page-slot page-slot-bottom">
<div class="wwads-cn wwads-horizontal pageB" data-id="136" style="width:100%;max-height:80px;min-height:auto;"></div>
<style>
.pageB img{width:80px!important;}
.wwads-horizontal .wwads-text, .wwads-content .wwads-text{line-height:1;}
</style>
</div> <div class="page-edit"><!----> <!----> <!----></div> <div class="page-nav-wapper"><div class="page-nav-centre-wrap"><a href="oauth2.html" class="page-nav-centre page-nav-centre-prev"><div class="tooltip">OAuth 2.0(SSO 单点登录)</div></a> <a href="saas-tenant_dynamic.html" class="page-nav-centre page-nav-centre-next"><div class="tooltip">SaaS 多租户【数据库隔离】</div></a></div> <div class="page-nav"><p class="inner"><span class="prev">
←
<a href="oauth2.html" class="prev">OAuth 2.0(SSO 单点登录)</a></span> <span class="next"><a href="saas-tenant_dynamic.html">SaaS 多租户【数据库隔离】</a>→
</span></p></div></div></div> <!----></main></div> <div class="footer"><div class="icons"><a href="mailto:7685413@qq.com" title="发邮件" target="_blank" class="iconfont icon-youjian"></a><a href="https://github.com/YunaiV" title="GitHub" target="_blank" class="iconfont icon-github"></a><a href="https://www.iocoder.cn/?yudao" title="博客" target="_blank" class="iconfont icon-erji"></a></div>
Theme by
<a href="https://github.com/xugaoyi/vuepress-theme-vdoing" target="_blank" title="本站主题">Vdoing</a>
| Copyright © 2019-2025
<span>芋道源码 | MIT License</span></div> <div class="buttons"><div title="返回顶部" class="button blur go-to-top iconfont icon-fanhuidingbu" style="display:none;"></div> <div title="去评论" class="button blur go-to-comment iconfont icon-pinglun" style="display:none;"></div> <div title="主题模式" class="button blur theme-mode-but iconfont icon-zhuti"><ul class="select-box" style="display:none;"><li class="iconfont icon-zidong">
跟随系统
</li><li class="iconfont icon-rijianmoshi">
浅色模式
</li><li class="iconfont icon-yejianmoshi">
深色模式
</li><li class="iconfont icon-yuedu">
阅读模式
</li></ul></div></div> <!----> <!----> <div class="custom-html-window custom-html-window-rb" style="display:;"><div class="custom-wrapper"><span class="close-but">×</span> <div>
<div class="wwads-cn wwads-vertical windowRB" data-id="136" style="max-width:160px;
min-width: auto;min-height:auto;"></div>
<style>
.windowRB{ padding: 0;}
.windowRB .wwads-img{margin-top: 10px;}
.windowRB .wwads-content{margin: 0 10px 10px 10px;}
.custom-html-window-rb .close-but{
display: none;
}
</style>
</div></div></div></div><div class="global-ui"></div></div>
<script defer></script>
</body>
</html>