
SpringBoot3Vue3 多租户隔离SQL 改写、套餐初始化与跨环境搬家文档地址https://ruoyioffice.com源码1·GitHubhttps://github.com/yuqing2026/ruoyi-office源码2·GitCodehttps://gitcode.com/zhouzhongyan/ruoyi-office源码3·Giteehttps://gitee.com/yqzy1688/ruoyi-office微信17156169080备注「RuoYi Office」一套代码卖给 N 家企业最怕的不是功能少而是 A 公司能看见 B 公司的合同。多租户要同时解决四件事请求里如何带上租户、SQL 如何自动隔离、新租户如何按套餐铺权限以及开发库怎么搬到客户环境。本文按 RuoYi Office 的实现把这四段拆开讲。▲ Header 写入 TTL 上下文 → SQL 自动拼 tenant_id → 套餐异步初始化 → L0–L5 数据包搬家引言加一列 company_id 为什么不够内部系统给自己用写WHERE company_id ?就能混过去。SaaS 上线后会出现这些事故表象根因后果异步任务、定时任务查到别家数据线程池复用上下文没传/没清跨租户泄露字典、序列表报 tenant_id 不存在全局表被误拼条件登录和编号全面失败新租户菜单是空的只建了租户行没按套餐铺权限客户以为系统坏了导入后待办全空、在途单走不动把流程实例当普通表搬实施现场事故RuoYi Office 选择共享库 tenant_id字段隔离作为默认方案成本最低能覆盖绝大多数中小客户。独立库/独立 Schema 可以后加但本套代码的主路径是「业务无感知自动过滤」。一、上下文请求进来必须有租户出去必须清干净过滤器从请求头读取租户号写入TenantContextHolder。Holder 用的是TransmittableThreadLocal这样Async、线程池提交的任务还能拿到同一个租户。protectedvoiddoFilterInternal(HttpServletRequestrequest,HttpServletResponseresponse,FilterChainchain)throwsServletException,IOException{LongtenantIdWebFrameworkUtils.getTenantId(request);if(tenantId!null){TenantContextHolder.setTenantId(tenantId);}try{chain.doFilter(request,response);}finally{TenantContextHolder.clear();}}finally里的clear()不是礼貌是安全。Servlet 线程会被复用上一次请求的租户号若残留下一次未带 Header 的回调就可能写进错误租户。短信、支付这类 Open API 可以配忽略 URL不强制 Header。忽略不是“不要租户”而是由业务自己决定写哪一家。定时任务要扫所有租户时用TenantUtils.execute(tenantId, runnable)进去强制ignorefalse出来恢复旧值。TenantJob则按租户列表并行执行要求 Job 幂等——某一家失败重试时已经成功的租户也会再跑一遍。二、SQL 改写默认隔离三种出口MyBatis-Plus 的TenantLineHandler在解析 SQL 时插入tenant_id ?。取值来自getRequiredTenantId()上下文为空直接抛避免“没租户就查全库”。是否忽略某张表按这个顺序判断当前线程是否全局ignore配置里的忽略表大小写各存一份Oracle 的DUAL必须忽略实体找不到外部表→ 忽略避免误伤继承TenantBaseDO→不忽略类上有TenantIgnore→ 忽略。publicbooleanignoreTable(StringtableName){if(TenantContextHolder.isIgnore()){returntrue;}tableNameSqlParserUtils.removeWrapperSymbol(tableName);BooleanignoreignoreTables.get(tableName.toLowerCase());if(ignorenull){ignorecomputeIgnoreTable(tableName);synchronized(ignoreTables){addIgnoreTable(tableName,ignore);}}returnignore;}业务表继承TenantBaseDO即可开发者不用手写tenant_id。这是“无感知”的那一半另一半是全局表必须显式退出不能靠运气。缓存同样要切片。TenantRedisCacheManager在非忽略且租户号存在时把 cache name 拼成name:tenantId。配置缓存若不分租户A 改字典 B 立刻中招。三、套餐新租户不是空壳创建租户时要填套餐、账号额度、过期时间、品牌和初始管理员。packageId 0是系统租户菜单全量且锁死演示环境禁止改名、禁止停用。▲ 效果新建租户必须选套餐并指定初始管理员列表行上还有「补齐套餐」「导出数据」说明租户不是一行档案后台会丢一个异步 Job按套餐铺菜单、默认角色和组织骨架。进度弹窗盯到 100% 再让客户登录。套餐表单把菜单做成树全功能套餐能勾两千多项另有「流程模型 / 默认角色 / 组织骨架」分页说明套餐不只是菜单开关。▲ 效果全功能套餐勾选 2132 项菜单套餐是权限蓝图不是营销文案「补齐套餐」和「初始化」不同补齐只新增缺失项不覆盖租户已经改过的角色和菜单。这是实施现场最容易踩的坑——客户改了角色之后你再点一次初始化等于把定制冲掉。平台管理员还可以带visit-tenant-id访问别家数据。拦截器要求权限system:tenant:visit请求期间把上下文切过去afterCompletion再切回本人租户。个人信息接口要配忽略访问 URL否则切过去拿不到自己的账号。四、跨环境搬家分层导出明确不搬待办开发库调通后要把租户搬到客户机或演示站。这里没有“整库 dump 换个名字”而是按层打包、导入时重映射主键。层内容导入后L0租户信息、套餐、品牌 Logo新建租户行L1部门、岗位、角色、用户账号在此阶段写入L2流程分类与可移植流程定义自动发布LBiz业务主数据 过程单据流程实例字段清空L5附件元数据可选文件本体businessId 重映射待办、已办、审批轨迹不同步。工作流运行态绑在引擎的运行时表和任务身份上换了主键就对不上。导入向导把这条写成红色风险并警告L1 写账号期间提前登录会密码错误。▲ 效果导入始终新建租户并自动重映射在途审批必须先办结否则导入后只能看到业务数据、走不下去导出同样走异步 Job100% 后再下 ZIP。这不是体验细节大租户的 LBiz 可能跑几分钟提前下载会得到半包。五、和数据权限的边界租户隔离是横向切片你属于哪家企业。数据权限是纵向切片你在这家企业能看哪个部门。搬家、补齐、创建租户这些管理接口会关掉数据权限DataPermission(enable false)否则平台管理员自己的部门范围会把套餐初始化滤空。两层拦截器顺序必须稳定先定租户再套部门范围。反过来会在错误的租户世界里算权限。六、技术亮点设计要点实现方式价值上下文可传递TTL Filter finally 清理异步不串租户SQL 无感知TenantBaseDO LineHandler业务少写条件三种出口配置表 / 注解 / 全局 ignore全局表能活缓存隔离name 拼 tenantId配置不串味访问切换visit 权限 请求级切回平台可运维套餐蓝图菜单/模型/角色/组织新租户可开箱补齐不覆盖只新增缺失项保护现场定制搬家分层L0–L5 不同步轨迹跨环境可预期七、快速体验在线演示https://ruoyioffice.com/web/账号 admin / admin123推荐路径系统管理 → 租户管理 → 租户列表打开新增看套餐、额度、品牌进入租户套餐打开全功能套餐的菜单树和另外三个分页点「导入租户数据」读风险说明和分层清单对非系统租户看「补齐套餐」「导出数据」。源码仓库GitHubhttps://github.com/yuqing2026/ruoyi-officeGitCodehttps://gitcode.com/zhouzhongyan/ruoyi-officeGiteehttps://gitee.com/yqzy1688/ruoyi-office常见问题FAQ为什么不一人一套独立数据库独立库隔离最硬运维成本和备份成本也最高。默认共享表能覆盖绝大多数客户强监管客户可以再拆库不必让所有租户为少数人买单。忽略租户会不会变成后门会所以忽略必须白名单化配置表、注解或明确的 Open API。业务 CRUD 默认不能 ignore。搬家后为什么待办是空的运行中的任务和审批轨迹没有进数据包。请先办结在途单再导出导入后只保证业务数据和已发布的流程模型。补齐套餐会不会冲掉客户改过的角色不会。补齐只新增缺失项。若你要“恢复出厂”应走单独的初始化而不是补齐。Redis 为什么也要拼租户Cacheable的 key 往往只有业务 ID。不拼租户两家的“角色 1”会互相覆盖。结语多租户的目标不是“能同时登录两家公司”而是默认隔离、出口显式、开户可初始化、搬家可验收。把 Header、SQL、缓存、套餐和数据包当成同一条治理链SaaS 才敢把一套代码卖出去。想要体验 RuoYi Office 的强大功能在线演示https://ruoyioffice.com/web/账号 admin / admin123源码仓库GitHubhttps://github.com/yuqing2026/ruoyi-office GitCodehttps://gitcode.com/zhouzhongyan/ruoyi-office Giteehttps://gitee.com/yqzy1688/ruoyi-office技术咨询添加微信17156169080备注「RuoYi Office」⭐如果觉得不错请给个 Star 支持一下