Easy Admin SaaS 是一个共享数据库的多租户业务底座。它不是单独的管理后台,而是由平台端、租户端、用户端和统一服务端组成,并在同一套基础能力上装载测算、心理测评、短剧和吃瓜热点等业务应用。
物理项目与交付物
| 目录 | 技术与形态 | 主要职责 | 交付方式 |
|---|---|---|---|
platform |
Vue 3、Vite、Element Plus、Pinia | 平台运营、租户开通、平台管理员、全局配置、定时任务、升级和代码生成 | 独立静态管理站 |
tenant |
Vue 3、Vite、Element Plus、Pinia | 租户员工、组织权限、用户、内容、渠道、装修、财务和业务应用管理 | 独立静态管理站 |
pc |
Nuxt 3、Element Plus、Pinia | 登录用户的桌面 Web 入口,承载账户和各业务应用页面 | SSR 服务或静态产物 |
uniapp |
Vue 3、uni-app、Pinia | H5、小程序和 App 用户入口 | 按渠道构建的多端产物 |
server |
PHP 8、ThinkPHP 8、多应用模式 | 三类 API、公共模型、支付、短信、存储、微信和业务应用服务 | PHP-FPM API 与任务进程 |
五个目录可以独立开发和发布,但服务端数据模型与接口契约是共同依赖。前端页面隐藏只能改善体验,真正的权限、租户隔离和业务校验必须由 server 完成。
总体拓扑
flowchart TB Platform[platform
平台运营后台] --> PA[platformapi] Tenant[tenant
租户管理后台] --> TA[tenantapi] PC[pc
Nuxt 用户端] --> UA[api] Uni[uniapp
H5 / 小程序 / App] --> UA PA --> Common[common
身份 / 支付 / 存储 / 渠道] TA --> Common UA --> Common TA --> SM[测算
suanming] UA --> SM TA --> TR[心理测评
testreport] UA --> TR TA --> VE[短剧
ve] UA --> VE TA --> CG[吃瓜热点
chigua] UA --> CG SM --> Common TR --> Common VE --> Common CG --> Common Common --> DB[(MySQL
业务事实)] Common --> Cache[(Redis
会话 / 缓存 / 锁)] Common --> Storage[本地 / OSS / COS / 七牛] Common --> Channels[微信 / 短信 / 支付]
服务端应用边界
ThinkPHP 的多应用入口对应三类不同信任级别,不能只把它们理解为 URL 前缀。
| 应用 | 调用方 | 已有模块 | 数据权限 |
|---|---|---|---|
platformapi |
平台运营人员 | 租户、平台管理员、角色菜单、部门岗位、全局配置、计划任务、升级、生成器 | 可以治理多个租户,但每项跨租户操作都应审计 |
tenantapi |
某一租户的管理员和员工 | 组织权限、用户、文章、微信渠道、页面装修、财务、充值和业务应用 | 只能访问当前租户及授权部门范围 |
api |
最终用户 | 登录、账户、文章、搜索、支付、充值及各业务应用 | 只能访问自己的数据和当前租户公开内容 |
index |
安装程序、健康检查或公开入口 | 环境初始化和无需业务身份的接口 | 不应暴露管理操作 |
common |
仅供服务端内部复用 | 模型、枚举、列表、服务和基础能力 | 不直接作为公网应用入口 |
平台账号、租户员工和最终用户分别使用 Admin、TenantAdmin、User 及各自的 Session/Auth 模型。三套身份不能只靠一个角色字段混用,否则平台权限容易意外下放到租户或用户接口。
一次请求怎样流动
- 客户端向对应 API 发送 Token、渠道信息和业务参数;
- 入口中间件解析会话,并从可信会话确定
admin_id、user_id与tenant_id; - 控制器接收参数,Validate 完成格式与边界校验;
- Logic 处理用例,Lists 负责有界分页查询,Service 处理支付、存储或跨模块协作;
- Model 在租户数据范围内读写 MySQL,需要时同步 Redis 缓存;
- API 返回统一响应,关键管理操作和资金变化写入日志。
客户端提交的 tenant_id 只能帮助定位请求,最终租户身份必须由域名、应用配置或登录会话推导,不能直接信任表单值。
平台治理与租户运营
平台层
平台端负责 SaaS 的控制面,包括租户生命周期、租户管理员、平台组织与 RBAC、全局存储和交易配置、定时任务、版本升级和开发工具。创建租户时,应在一个事务或可恢复流程中完成租户记录、初始管理员、默认角色菜单、基础配置和必要业务数据。
平台层可以查看租户状态和汇总指标,但不应默认进入租户业务数据。需要代运营或排障时,应记录操作人、目标租户、原因和前后值。
租户层
租户端负责组织、角色、菜单、员工、最终用户、内容、渠道、装修、财务以及已安装业务应用。租户角色的功能权限与部门数据范围需要同时生效:拥有菜单不代表可以读取全租户数据,拥有部门范围也不代表可以执行敏感按钮操作。
文件、短信记录、通知、支付方式等对象在源码中同时存在平台版和租户版模型。维护时要明确哪些是平台模板,哪些是租户覆盖值,避免修改平台默认值后覆盖租户的既有配置。
公共能力与业务应用
| 类型 | 主要对象或服务 | 架构边界 |
|---|---|---|
| 身份与组织 | 管理员、租户员工、用户、角色、菜单、部门、岗位、会话 | 三类身份分开鉴权,部门范围进入每个列表查询 |
| 内容与装修 | 文章、分类、页面、Tabbar、热搜 | 内容发布与页面装修可以复用,但数据必须带租户范围 |
| 渠道能力 | 公众号、小程序、网页配置、微信请求 | 渠道凭据仅在服务端保存,并按租户读取 |
| 交易能力 | 支付配置、支付方式、充值、退款、账户流水 | 金额变化使用事务、幂等业务号和不可变流水 |
| 基础设施 | 文件、短信、对象存储、代码生成、定时任务 | 通过 Driver 或 Service 收口供应商差异 |
| 业务应用 | 测算、心理测评、短剧、吃瓜热点 | 自己维护订单和领域数据,通过公共支付、用户和存储能力接入 |
业务应用不是简单页面集合。例如测算包含产品、订单、分销和提现,心理测评包含项目、订单、评分引擎和支付,短剧包含分类、剧集、观看记录、订单与 VOD。它们可以复用平台能力,但不能把领域状态全部塞进通用订单或用户表。
四个业务应用在代码中都有独立的控制器、Logic、模型和安装命令,并同时接入租户后台与用户 API。详细设计分别见测算应用、心理测评、短剧应用和吃瓜热点。
多租户数据隔离
共享数据库模式的关键不是“表里有 tenant_id”,而是所有访问路径都能保持租户上下文。
| 访问位置 | 必须检查的内容 |
|---|---|
| 新增与修改 | 服务端写入当前租户,不接受前端任意指定;更新条件同时包含主键和租户 |
| 列表与详情 | 列表、详情、导出和统计都加入租户及部门范围 |
| 唯一约束 | 租户内唯一的数据使用 tenant_id + 业务字段 组合索引 |
| 缓存 | Key 至少包含租户和业务对象标识,平台模板与租户配置分开 |
| 文件 | 对象存储路径带租户命名空间,下载时再次鉴权 |
| 定时任务 | 任务负载显式携带租户,执行前验证租户仍有效 |
| 操作日志 | 记录操作者身份类型、租户、业务对象和请求编号 |
跨租户汇总属于平台治理用例,应进入单独查询路径,不能通过“临时去掉 tenant_id 条件”实现。
支付、存储与渠道
支付服务已经按微信、支付宝和基础支付接口分层。推荐以业务订单号作为幂等键:创建支付单、接收回调、确认业务订单和写账户流水分别记录状态,重复回调只返回已处理结果。退款同样需要退款业务号和原支付单关系。
存储 Driver 支持本地、阿里云、腾讯云、七牛和服务器存储。数据库只保存文件元数据与对象 Key,业务表引用文件 ID;切换供应商时由驱动生成访问地址,不在历史业务数据中批量替换完整 URL。
微信、短信和支付凭据按租户配置存放,运行时通过当前租户加载。日志必须过滤 Access Token、签名密钥、手机号验证码和支付原始报文中的敏感字段。
部署与故障域
| 运行单元 | 扩缩容与发布 | 主要故障影响 |
|---|---|---|
platform / tenant 静态站 |
CDN 或静态 Web 服务独立发布 | 对应后台不可用,不应中断用户端业务 |
pc |
根据 Nuxt 模式部署 SSR 或静态产物 | 桌面用户端不可用,移动端可继续工作 |
uniapp |
各渠道独立发版 | 单一渠道故障不影响其他端 |
| PHP API | 尽量无状态扩容,会话放共享缓存 | 登录和业务操作受影响 |
| 定时任务与升级任务 | 单实例或分布式锁 | 通知、清理或升级延迟,不应阻塞普通请求 |
| MySQL / Redis | 分别保存事实与临时状态 | MySQL 故障禁止资金写入;Redis 故障需明确降级策略 |
| 对象存储与第三方渠道 | 超时、重试、熔断和回调校验 | 文件、短信或支付延迟,业务订单需保持可恢复状态 |
当前架构债务与改进顺序
- 统一租户上下文:在三个 API 入口形成唯一的上下文对象,避免每个 Logic 自行读取
tenant_id; - 自动化隔离测试:为详情、列表、导出、统计、缓存和任务补充跨租户访问测试;
- 插件边界:给业务应用定义安装、配置、菜单、数据和卸载契约,减少对
common的反向依赖; - 交易一致性:统一支付、充值、退款和分销的幂等键、状态机与对账规则;
- 异步任务:把短信、媒体处理、批量安装和通知移出同步请求,并保存任务状态;
- 配置治理:所有域名、密钥和供应商配置进入环境变量或密钥服务,轮换源码中曾出现的固定凭据;
- 可观测性:用请求 ID、租户 ID、用户 ID 和业务单号串联 API、任务与第三方回调。
具体怎样判断一个能力应进入公共层还是独立业务应用,可继续阅读模块边界。