library-picturebook-activity/backend/docs/TENANT_GUIDE.md
aid 418aa57ea8 Day4: 超管端设计优化 + UGC绘本创作社区P0实现
一、超管端设计优化
- 文档管理SOP体系建立,docs目录重组
- 统一用户管理:跨租户全局视角,合并用户管理+公众用户
- 活动监管全模块重构:全部活动(统计卡片+阶段筛选+SuperDetail详情页)、报名数据/作品数据/评审进度(两层合一扁平列表)、成果发布(去Tab+统计+隐藏写操作)
- 菜单精简:移除评委管理/评审规则/通知管理
- Bug修复:租户编辑丢失隐藏菜单、pageSize限制、主色统一

二、UGC绘本创作社区P0
- 数据库:10张新表(user_works/user_work_pages/work_tags等)
- 子女账号独立化:Child升级为独立User,家长切换+独立登录
- 用户作品库:CRUD+发布审核,8个API
- AI创作流程:提交→生成→保存到作品库,4个API
- 作品广场:首页改造为推荐流,标签+搜索+排序
- 内容审核(超管端):作品审核+作品管理+标签管理
- 活动联动:WorkSelector作品选择器
- 布局改造:底部5Tab(发现/创作/活动/作品库/我的)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 22:20:25 +08:00

271 lines
7.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 多租户系统实现指南
## 概述
本系统实现了完整的多租户架构,支持:
- 每个租户独立的数据隔离(用户、角色、权限、菜单等)
- 每个租户独立的访问链接(通过租户编码或域名)
- 超级租户可以创建和管理其他租户
- 超级租户可以为租户分配菜单
## 数据库设计
### 核心表结构
1. **Tenant租户表**
- `id`: 租户ID
- `name`: 租户名称
- `code`: 租户编码(唯一,用于访问链接)
- `domain`: 租户域名(可选,用于子域名访问)
- `isSuper`: 是否为超级租户0-否1-是)
- `validState`: 有效状态1-有效2-失效)
2. **TenantMenu租户菜单关联表**
- `tenantId`: 租户ID
- `menuId`: 菜单ID
- 用于关联租户和菜单,实现菜单分配
3. **其他表添加租户字段**
- `User`: 添加 `tenantId` 字段
- `Role`: 添加 `tenantId` 字段
- `Permission`: 添加 `tenantId` 字段
- `Dict`: 添加 `tenantId` 字段
- `Config`: 添加 `tenantId` 字段
### 唯一性约束调整
- `User.username`: 从全局唯一改为 `(tenantId, username)` 唯一
- `User.email`: 从全局唯一改为 `(tenantId, email)` 唯一
- `Role.name/code`: 从全局唯一改为 `(tenantId, name/code)` 唯一
- `Permission.code`: 从全局唯一改为 `(tenantId, code)` 唯一
- 其他类似字段也做了相应调整
## 租户识别机制
系统支持多种方式识别租户:
1. **请求头方式**(推荐)
- `X-Tenant-Code`: 租户编码
- `X-Tenant-Id`: 租户ID
2. **子域名方式**
-`Host` 请求头提取子域名
- 匹配租户的 `code``domain` 字段
3. **JWT Token方式**
- Token中包含 `tenantId` 字段
- 登录时自动关联租户
4. **登录参数方式**
- 登录接口支持 `tenantCode` 参数
## 使用流程
### 1. 数据库迁移
首先需要生成并执行数据库迁移:
```bash
# 生成迁移文件
npm run prisma:migrate:dev -- --name add_tenant_support
# 执行迁移
npm run prisma:migrate
```
### 2. 初始化超级租户
运行初始化脚本创建超级租户:
```bash
npm run init:super-tenant
```
这将创建:
- 超级租户code: `super`
- 超级管理员用户username: `admin`, password: `admin123`
- 超级管理员角色
- 基础权限
### 3. 创建普通租户
使用超级租户的管理员账号登录后,通过租户管理接口创建新租户:
```bash
POST /api/tenants
Headers:
Authorization: Bearer <token>
X-Tenant-Code: super
Body:
{
"name": "租户A",
"code": "tenant-a",
"domain": "tenant-a.example.com",
"description": "租户A的描述",
"menuIds": [1, 2, 3] // 分配的菜单ID列表
}
```
### 4. 为租户分配菜单
超级租户可以为租户分配菜单:
```bash
PATCH /api/tenants/:id
Headers:
Authorization: Bearer <token>
X-Tenant-Code: super
Body:
{
"menuIds": [1, 2, 3, 4, 5]
}
```
### 5. 租户用户登录
租户用户登录时需要指定租户:
```bash
POST /api/auth/login
Body:
{
"username": "user1",
"password": "password123",
"tenantCode": "tenant-a" // 可选,也可以从请求头获取
}
```
或者在请求头中指定:
```bash
POST /api/auth/login
Headers:
X-Tenant-Code: tenant-a
Body:
{
"username": "user1",
"password": "password123"
}
```
### 6. 访问租户数据
所有API请求都会自动根据租户ID过滤数据
```bash
GET /api/users
Headers:
Authorization: Bearer <token>
X-Tenant-Code: tenant-a
```
返回的数据只会包含该租户的用户。
## API接口
### 租户管理接口
- `POST /api/tenants` - 创建租户(需要 `tenant:create` 权限)
- `GET /api/tenants` - 获取租户列表(需要 `tenant:read` 权限)
- `GET /api/tenants/:id` - 获取租户详情(需要 `tenant:read` 权限)
- `PATCH /api/tenants/:id` - 更新租户(需要 `tenant:update` 权限)
- `DELETE /api/tenants/:id` - 删除租户(需要 `tenant:delete` 权限)
- `GET /api/tenants/:id/menus` - 获取租户的菜单树(需要 `tenant:read` 权限)
### 其他接口
所有其他接口(用户、角色、权限等)都支持租户隔离,会自动根据请求中的租户信息过滤数据。
## 前端集成
### 1. 请求拦截器
在前端请求拦截器中添加租户信息:
```typescript
// utils/request.ts
service.interceptors.request.use(
(config: InternalAxiosRequestConfig) => {
const token = getToken();
const tenantCode = getTenantCode(); // 从localStorage或store获取
if (token && config.headers) {
config.headers.Authorization = `Bearer ${token}`;
}
if (tenantCode && config.headers) {
config.headers['X-Tenant-Code'] = tenantCode;
}
return config;
},
(error) => {
return Promise.reject(error);
}
);
```
### 2. 登录时保存租户信息
```typescript
// 登录成功后
localStorage.setItem('tenantCode', response.data.user.tenantCode);
localStorage.setItem('tenantId', response.data.user.tenantId);
```
### 3. 租户切换
如果需要支持租户切换可以在前端实现租户选择器切换时更新localStorage中的租户信息并重新加载数据。
## 权限控制
### 超级租户权限
超级租户的用户拥有所有权限,包括:
- 创建、查看、更新、删除租户
- 为租户分配菜单
- 管理所有租户的数据(如果需要在超级租户中查看所有租户数据)
### 普通租户权限
普通租户的用户只能:
- 管理自己租户内的数据
- 查看分配给租户的菜单
- 无法访问其他租户的数据
## 注意事项
1. **数据隔离**: 所有查询都会自动添加租户过滤条件,确保数据隔离
2. **唯一性**: 用户名、邮箱等在租户内唯一,不同租户可以有相同的用户名
3. **菜单管理**: 菜单是全局的(由超级租户管理),但通过 `TenantMenu` 表分配给各个租户
4. **超级租户**: 超级租户不能被删除,且拥有所有权限
5. **迁移数据**: 如果现有系统已有数据,需要编写迁移脚本将现有数据关联到超级租户
## 迁移现有数据
如果系统已有数据,需要将现有数据迁移到超级租户:
```sql
-- 假设超级租户ID为1
UPDATE users SET tenant_id = 1 WHERE tenant_id IS NULL;
UPDATE roles SET tenant_id = 1 WHERE tenant_id IS NULL;
UPDATE permissions SET tenant_id = 1 WHERE tenant_id IS NULL;
-- 其他表类似
```
## 故障排查
1. **租户识别失败**: 检查请求头是否正确设置或检查JWT token中是否包含tenantId
2. **数据查询为空**: 确认租户ID正确且数据确实属于该租户
3. **权限不足**: 确认用户角色有相应权限,且角色属于正确的租户
## 扩展功能
未来可以考虑的扩展:
1. 租户级别的配置(每个租户可以有自己的系统配置)
2. 租户级别的主题和品牌定制
3. 租户级别的功能开关
4. 租户使用统计和监控
5. 租户数据导出和备份