library-picturebook-activity/docs/design/super-admin/tenant-auto-create-admin.md
aid 3c4100c231 feat: 创建租户时自动生成管理员账号、角色和权限
创建租户改为事务化一站式操作:自动复制 gdlib 权限模板 + 补充基础管理权限,
创建 tenant_admin 角色和管理员用户,支持自定义账号密码。
前端表单增加管理员输入区块,成功弹窗展示凭据并支持一键复制。
同步实现 menuIds 菜单分配(消除原 TODO)。

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 18:48:34 +08:00

161 lines
5.6 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.

# 租户创建自动生成管理员账号 — 设计方案
> 所属端:超管端
> 状态:已完成
> 创建日期2026-04-02
> 最后更新2026-04-02
---
## 1. 背景与问题
超管端创建新租户(机构)后,需要**手动**到用户管理页面另行创建该租户的管理员账号、角色、权限,流程割裂且容易遗漏,导致新建的租户无法登录使用。
核心问题:
- 创建租户与创建管理员是分离的两步操作,缺乏原子性
- 新租户没有默认角色和权限,管理员账号无法正常使用
- 菜单分配menuIds也未实现后端标记为 TODO
## 2. 现状分析
### 2.1 创建租户接口
- `POST /api/tenants``SysTenantServiceImpl.createTenant()`
- 仅创建 `t_sys_tenant` 记录,不创建用户、角色、权限、菜单关联
- menuIds 参数存在但未处理TODO 注释)
### 2.2 初始化脚本的做法
`backend/scripts/init-dev-tenants.ts`Node 版,已移除)中,创建租户时会一并完成:
1. 创建租户 → 2. 创建权限 → 3. 创建角色 → 4. 角色绑定权限 → 5. 创建 admin 用户 → 6. 用户绑定角色 → 7. 分配菜单
`init.sql` 中每个租户也都有对应的 admin 用户和角色。
### 2.3 涉及的数据表
| 表名 | 说明 |
|------|------|
| `t_sys_tenant` | 租户 |
| `t_sys_user` | 用户 |
| `t_sys_role` | 角色 |
| `t_sys_permission` | 权限(按租户隔离) |
| `t_sys_user_role` | 用户-角色关联 |
| `t_sys_role_permission` | 角色-权限关联 |
| `t_sys_tenant_menu` | 租户-菜单关联 |
## 3. 设计方案
### 3.1 整体思路
创建租户时在同一事务中自动完成:创建租户 → 复制权限模板 → 创建管理员角色 → 创建管理员用户 → 绑定关联关系 → 分配菜单。接口返回租户信息 + 管理员凭据。
### 3.2 权限模板策略
- **模板来源**运行时从广东省图租户code=`gdlib`tenant_id=9复制权限该租户拥有 26 项业务权限,覆盖活动、报名、评审、作品、公告等核心模块
- **补充基础管理权限**:模板中缺少的 7 项基础权限自动补充:
- `user:update`、`user:delete` — 用户管理
- `role:create`、`role:update`、`role:delete`、`role:assign` — 角色管理
- `permission:read` — 权限查看
- 新租户最终获得 **33 项权限**26 模板 + 7 补充)
### 3.3 前端页面设计
**创建表单改动**
- 基本信息 Tab 增加「管理员账号」区块(用分割线分隔)
- 新增两个输入框:管理员账号(默认 admin、管理员密码默认 admin@{编码}
- 仅新建时显示,编辑时隐藏
**成功弹窗改动**
- 原先:提示"创建成功"+ 跳转用户管理页按钮
- 现在:展示管理员账号、初始密码、登录地址
- 新增「复制账号信息」按钮,一键复制全部信息到剪贴板
### 3.4 后端改动
**`CreateTenantDto`** — 新增字段:
- `adminUsername`String可选默认 "admin"
- `adminPassword`String可选默认 "admin@{tenantCode}"
**`ISysTenantService.createTenant()`** — 返回值改为 `Map<String, Object>`
**`SysTenantServiceImpl.createTenant()`** — 核心改动,事务内完成 7 步:
1. 创建租户记录
2. `copyTemplatePermissions()` — 复制 gdlib 权限 + 补充基础权限
3. 创建 `tenant_admin` 角色
4. 批量绑定角色-权限
5. 创建管理员用户(密码 BCrypt 加密)
6. 绑定用户-角色
7. 处理 menuIds 菜单分配
新增私有方法 `copyTemplatePermissions(Long newTenantId)`
- 查询 gdlib 租户的全部权限,逐条复制(替换 tenantId
- 对比已有权限编码,补充缺失的基础管理权限
**`SysTenantServiceImpl.updateTenant()`** — 同步实现 menuIds 更新(先删后增)
**`SysTenantController.create()`** — 返回类型从 `Result<SysTenant>` 改为 `Result<Map<String, Object>>`
### 3.5 接口响应格式
```json
{
"code": 200,
"data": {
"tenant": {
"id": 13,
"name": "测试图书馆",
"code": "test-lib",
"tenantType": "library",
...
},
"admin": {
"username": "libadmin",
"password": "Lib@2026",
"userId": 32,
"nickname": "测试图书馆管理员"
}
}
}
```
### 3.6 前端改动
**`src/api/tenants.ts`**
- `CreateTenantForm` 新增 `adminUsername`、`adminPassword`
- 新增 `CreateTenantResult` 接口
- `createTenant()` 返回类型改为 `CreateTenantResult`
**`src/views/system/tenants/Index.vue`**
- 表单 reactive 新增 `adminUsername`、`adminPassword`
- 模板增加管理员输入区块
- 成功弹窗展示管理员信息卡片 + 复制按钮
- 移除 `useRouter`、`useAuthStore` 无用依赖
## 4. 改动范围
### 后端Java
| 操作 | 文件 |
|------|------|
| 修改 | `modules/sys/dto/CreateTenantDto.java` |
| 修改 | `modules/sys/service/ISysTenantService.java` |
| 修改 | `modules/sys/service/impl/SysTenantServiceImpl.java` |
| 修改 | `modules/sys/controller/SysTenantController.java` |
### 前端
| 操作 | 文件 |
|------|------|
| 修改 | `src/api/tenants.ts` |
| 修改 | `src/views/system/tenants/Index.vue` |
## 5. 实施记录
### 2026-04-02 — 初版实现
- 完成后端事务化创建租户 + 管理员 + 角色 + 权限 + 菜单分配
- 权限模板使用 gdlib 租户26项+ 7项基础管理权限补充
- 前端表单增加管理员账号/密码输入,成功弹窗展示凭据信息
- 测试验证:创建租户 → 管理员登录成功 → 获得 97 项权限(含 gdlib 完整业务权限 + 补充权限)
- 同步实现了 `updateTenant` 的 menuIds 更新(消除 TODO