Files
cursor/plans/tenant_code-9333b12a.plan.md
ray zhou 2dd9f17da9 ok
2026-06-29 14:51:55 +08:00

8.8 KiB
Raw Permalink Blame History


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/.envVITE_APP_MODE 区分两种模式:

模式 环境值 识别方式 入口示例
app_id appid URL ?app_id=1 可猜测自增 id
domain domain location.host 管理端配置域名

核心链路:

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

关键文件:

目标架构

appid 模式替换为 code 模式domain 模式不动):

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 已支持 hash 路由),登录页无需新增输入框(避免与验证码字段 code 混淆)。


1. 数据库变更

sm_system_organization 增加字段:

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 建表语句与 seed 数据。

存量机构回填:新增独立迁移脚本(如 db/migrate_tenant_code.sql 或由一次性 PHP 命令生成),为已有记录生成不重复 6 位码;规则与新建一致。


2. Server 后端

2.1 Model

SystemOrganization.php 补充类 PHPDoc表说明 + @property string $tenant_code 租户访问码)。

可选:新增 findEnabledByTenantCode(string $tenantCode) 查询方法。

2.2 Logic — 核心改动

SystemOrganizationLogic.php

  • generateUniqueTenantCode()private
    • 字符集:ABCDEFGHJKLMNPQRSTUVWXYZ2345678932 字符)
    • 循环生成 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()

  • 读取 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

# 模式1: code  通过 URL ?tenant_code= 区分租户
# 模式2: domain 通过域名区分
VITE_APP_MODE = code

3.2 siteStore 重构

tenant-vue/src/store/modules/site.ts

  • 常量 TENANT_CODE_QUERY_KEY = 'tenant_code'
  • SiteInfoParams 改为 { tenantCode: string; mode: string }(替代 appid
  • loadSiteInfo/saimulti/appInfotenant_code + mode
  • 解析成功后:info.id 用于 App-Id 头;tenant_code 持久化到 localStorage刷新后无需 URL 参数也能识别租户)
  • domain 模式分支逻辑保持不变

3.3 路由监听

tenant-vue/src/App.vue

  • route.query.app_idroute.query.tenant_code
  • 相应函数重命名(如 getRouteTenantCode

3.4 HTTP 拦截器

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.tssiteInfoResponse 增加 tenant_code?: string


4. admin-vue 总后台

4.1 机构列表

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

  • 新增时:不展示 tenant_code后端自动生成
  • 编辑时:只读展示 tenant_code(不可修改,保证链接稳定)
  • 不在表单提交中包含 tenant_code防篡改

5. 文档

更新 README.md 租户前端配置章节:

  • 模式 1code + ?tenant_code=A3K9M2
  • 模式 2domain(不变)
  • 删除 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 保持不变)。