This commit is contained in:
ray zhou
2026-06-29 14:51:55 +08:00
parent 225fb2bd28
commit 2dd9f17da9
319 changed files with 29461 additions and 9412 deletions

View File

@@ -0,0 +1,230 @@
<!-- 9333b12a-cba7-4c83-9a56-b9ca81467dd0 -->
---
todos:
- id: "db-tenant-code"
content: "新增 tenant_code 字段、唯一索引,更新 saimulti.sql 与存量回填脚本"
status: pending
- id: "server-generate-resolve"
content: "SystemOrganizationLogic 实现码生成、add 自动写入、appInfo code 分支LoginController 接收 tenant_code"
status: pending
- id: "tenant-vue-code-mode"
content: "tenant-vue siteStore/App.vue/http/.env/types 从 app_id 模式切换为 tenant_code 模式"
status: pending
- id: "admin-vue-display"
content: "admin-vue 机构列表与编辑弹窗展示 tenant_code只读及可选登录链接"
status: pending
- id: "docs-verify"
content: "更新 README跑 verify 脚本与手动登录联调"
status: pending
isProject: false
---
# tenant_code 租户登录改造方案
## 现状
当前租户端通过 [`tenant-vue/.env`](tenant-vue/.env) 的 `VITE_APP_MODE` 区分两种模式:
| 模式 | 环境值 | 识别方式 | 入口示例 |
|------|--------|----------|----------|
| app_id | `appid` | URL `?app_id=1` | 可猜测自增 id |
| domain | `domain` | `location.host` | 管理端配置域名 |
核心链路:
```mermaid
sequenceDiagram
participant Browser
participant TenantVue
participant Server
participant DB
Browser->>TenantVue: ?app_id=1 或域名访问
TenantVue->>Server: GET /saimulti/appInfo
Server->>DB: 按 id 或 domain 查 sm_system_organization
Server-->>TenantVue: 返回 id/title/logo...
TenantVue->>Server: 后续请求 Header App-Id=机构id
```
关键文件:
- 前端站点识别:[`tenant-vue/src/store/modules/site.ts`](tenant-vue/src/store/modules/site.ts)、[`tenant-vue/src/App.vue`](tenant-vue/src/App.vue)、[`tenant-vue/src/utils/http/index.ts`](tenant-vue/src/utils/http/index.ts)
- 后端入口:[`server/plugin/saimulti/app/controller/LoginController.php`](server/plugin/saimulti/app/controller/LoginController.php) → [`SystemOrganizationLogic::appInfo()`](server/plugin/saimulti/app/logic/system/SystemOrganizationLogic.php)
- 租户隔离:全站仍依赖 `App-Id` 请求头([`TenantController`](server/plugin/saimulti/basic/TenantController.php)、[`TenantModel`](server/plugin/saimulti/basic/TenantModel.php)**内部继续使用数字机构 id不改为 tenant_code**
## 目标架构
`appid` 模式替换为 `code` 模式domain 模式不动):
```mermaid
sequenceDiagram
participant Browser
participant TenantVue
participant Server
participant DB
Browser->>TenantVue: /auth/login?tenant_code=A3K9M2
TenantVue->>Server: GET /saimulti/appInfo?tenant_code=A3K9M2&mode=code
Server->>DB: WHERE tenant_code=A3K9M2
Server-->>TenantVue: id, title, logo, tenant_code...
Note over TenantVue: 持久化 tenant_code + 解析后的 org id
TenantVue->>Server: POST /tenant/login, Header App-Id=1
```
**安全收益**:对外 URL 只暴露 6 位随机码(大写+数字,排除 0/O、1/I/L不再暴露可枚举的自增 id。
**tenant_code 获取方式**:与现有 app_id 一致,从浏览器 URL 查询参数自动读取([`getUrlQueryValue`](tenant-vue/src/store/modules/site.ts) 已支持 hash 路由),**登录页无需新增输入框**(避免与验证码字段 `code` 混淆)。
---
## 1. 数据库变更
在 [`sm_system_organization`](db/saimulti.sql) 增加字段:
```sql
ALTER TABLE `sm_system_organization`
ADD COLUMN `tenant_code` varchar(6) NOT NULL COMMENT '租户访问码6位随机' AFTER `domain`,
ADD UNIQUE INDEX `uk_tenant_code` (`tenant_code`);
```
同步更新 [`db/saimulti.sql`](db/saimulti.sql) 建表语句与 seed 数据。
**存量机构回填**:新增独立迁移脚本(如 `db/migrate_tenant_code.sql` 或由一次性 PHP 命令生成),为已有记录生成不重复 6 位码;规则与新建一致。
---
## 2. Server 后端
### 2.1 Model
[`SystemOrganization.php`](server/plugin/saimulti/app/model/system/SystemOrganization.php) 补充类 PHPDoc表说明 + `@property string $tenant_code 租户访问码`)。
可选:新增 `findEnabledByTenantCode(string $tenantCode)` 查询方法。
### 2.2 Logic — 核心改动
[`SystemOrganizationLogic.php`](server/plugin/saimulti/app/logic/system/SystemOrganizationLogic.php)
- **`generateUniqueTenantCode()`**private
- 字符集:`ABCDEFGHJKLMNPQRSTUVWXYZ23456789`32 字符)
- 循环生成 6 位 + 查重,直到唯一
- **`add($data)`** 重写/扩展:保存前若未传 `tenant_code`,自动调用生成器写入
- **`appInfo($identifier, $mode)`** 扩展分支:
- `mode === 'domain'` → 现有逻辑不变
- `mode === 'code'``where('tenant_code', $identifier)` 查询
- 移除或废弃 `mode !== 'domain'` 时按自增 id 查询的路径(原 appid 模式)
- **`appInfo` 返回字段**增加 `tenant_code`(供前端展示/持久化)
### 2.3 Controller
[`LoginController::appInfo()`](server/plugin/saimulti/app/controller/LoginController.php)
- 读取 `tenant_code` 查询参数code 模式)
- `mode=code` 时传入 `appInfo($tenantCode, 'code')`
- domain 模式保持 `appid=host` + `mode=domain`
**不改动** `App-Id` 头解析逻辑 — 租户 API 仍接收机构数字 id。
---
## 3. tenant-vue 租户端
### 3.1 环境配置
[`tenant-vue/.env`](tenant-vue/.env)
```env
# 模式1: code 通过 URL ?tenant_code= 区分租户
# 模式2: domain 通过域名区分
VITE_APP_MODE = code
```
### 3.2 siteStore 重构
[`tenant-vue/src/store/modules/site.ts`](tenant-vue/src/store/modules/site.ts)
- 常量 `TENANT_CODE_QUERY_KEY = 'tenant_code'`
- `SiteInfoParams` 改为 `{ tenantCode: string; mode: string }`(替代 `appid`
- `loadSiteInfo``/saimulti/appInfo``tenant_code` + `mode`
- 解析成功后:`info.id` 用于 `App-Id` 头;`tenant_code` 持久化到 localStorage刷新后无需 URL 参数也能识别租户)
- domain 模式分支逻辑保持不变
### 3.3 路由监听
[`tenant-vue/src/App.vue`](tenant-vue/src/App.vue)
- `route.query.app_id``route.query.tenant_code`
- 相应函数重命名(如 `getRouteTenantCode`
### 3.4 HTTP 拦截器
[`tenant-vue/src/utils/http/index.ts`](tenant-vue/src/utils/http/index.ts)
- `appMode === 'code'`:优先 `siteStore.info.id` 作为 `App-Id`;未加载时用已持久化的 org id
- 移除 `appid` 模式分支
### 3.5 类型定义
[`tenant-vue/src/types/api/api.d.ts`](tenant-vue/src/types/api/api.d.ts)`siteInfoResponse` 增加 `tenant_code?: string`
---
## 4. admin-vue 总后台
### 4.1 机构列表
[`admin-vue/src/views/admin/panel/organization/index.vue`](admin-vue/src/views/admin/panel/organization/index.vue) 增加列:
- `tenant_code` — 标签「租户码」
- 可选:展示租户登录链接模板 `租户端地址?tenant_code=xxx`(方便复制发给客户)
### 4.2 编辑弹窗
[`admin-vue/src/views/admin/panel/organization/modules/edit-dialog.vue`](admin-vue/src/views/admin/panel/organization/modules/edit-dialog.vue)
- **新增**时:不展示 tenant_code后端自动生成
- **编辑**时:只读展示 `tenant_code`(不可修改,保证链接稳定)
- 不在表单提交中包含 tenant_code防篡改
---
## 5. 文档
更新 [`README.md`](README.md) 租户前端配置章节:
- 模式 1`code` + `?tenant_code=A3K9M2`
- 模式 2`domain`(不变)
- 删除 app_id 相关说明
---
## 6. 兼容与测试要点
| 场景 | 预期 |
|------|------|
| 新机构创建 | 自动生成唯一 6 位 tenant_code |
| `?tenant_code=xxx` 访问 | 加载站点信息,登录成功 |
| 刷新页面(无 URL 参数) | localStorage 中 tenant_code + org id 仍可识别 |
| 错误/不存在 tenant_code | appInfo 报错「未找到该应用」 |
| domain 模式部署 | 行为与改造前一致 |
| 旧 `?app_id=1` 链接 | **不再支持**(按你的替换需求) |
手动验证路径:
1. 总后台新建机构 → 列表出现 tenant_code
2. 租户端 `http://localhost:16888/?tenant_code=XXXXXX` → Logo/标题正确
3. admin/admin 登录 → 租户 API 正常(菜单、用户信息)
4. domain 模式 `.env` 切换后仍可用
---
## 改动范围摘要
| 层 | 文件 | 动作 |
|----|------|------|
| DB | `db/saimulti.sql` + 迁移脚本 | 加字段、唯一索引、存量回填 |
| Server | `SystemOrganizationLogic.php` | 生成码、appInfo code 分支、add 钩子 |
| Server | `LoginController.php` | 接收 tenant_code 参数 |
| Server | `SystemOrganization.php` | PHPDoc |
| tenant-vue | `site.ts`, `App.vue`, `http/index.ts`, `.env`, `api.d.ts` | appid → code |
| admin-vue | `organization/index.vue`, `edit-dialog.vue` | 展示 tenant_code |
| Docs | `README.md` | 更新模式说明 |
**刻意不改**`App-Id` 请求头机制、TenantModel 全局 scope、登录接口参数验证码 `code` 保持不变)。