获取本人租户列表与切换租户
更新日期:2026-10-26。API 根地址:https://work.zingyang.com/api。
本文中的“组织”和“租户”指同一业务上下文。两个接口都从平台登录 token 的认证身份确定当前用户,不需要传 userId,也不能指定其他用户。当前控制器没有额外的 @RequiresPermissions 要求,仍必须通过平台认证。
1. 公共请求头
X-Access-Token: <平台登录token>
X-Tenant-Id: <当前已确认的租户ID>
沿用登录初始化确认的租户上下文。不要使用 Apple identity token,不要自行伪造租户 ID。首次还没有可用租户时按登录响应和实际业务权限处理,不要强行填入某个其他租户。
获取列表、记录当前租户本身不消耗 AI 额度;切换后调用 AI、上传等能力仍须分别满足目标租户的权限、有效权益和余量要求。
2. 获取本人所属租户
GET /sys/tenant/getTenantListByUserId?userTenantStatus=1
| 参数 | 位置/类型 | 必填 | 说明 |
|---|---|---|---|
| userTenantStatus | query / string | 否 | 成员关系状态过滤;推荐 1,表示正常成员关系;支持逗号分隔多个状态 |
不传该参数时不按成员关系状态过滤。它过滤的是用户与租户的关系状态,不是租户启用状态或套餐状态。接口查询未删除租户,并根据当前用户 ID 返回列表;不接受客户端 userId 作为查询目标。
curl 'https://work.zingyang.com/api/sys/tenant/getTenantListByUserId?userTenantStatus=1' \
--header 'X-Access-Token: <平台token>' \
--header 'X-Tenant-Id: <当前租户ID>'
成功响应示意(省略其他可选字段):
{
"success": true,
"code": 200,
"message": "",
"result": [
{
"id": "<当前用户ID>",
"tenantUserId": "1234",
"name": "示例组织",
"status": "1",
"userTenantStatus": "1",
"tenantAdmin": false
}
],
"timestamp": 1792972800000
}
| 字段 | 含义 |
|---|---|
| result | 数组,不是分页 records;无匹配项时为空数组 |
| tenantUserId | 租户 ID,当前 VO 为 string;验证为有效正整数后用于切换参数和租户头 |
| id | 用户 ID,不能当作租户 ID |
| name | 租户名称 |
| status | 租户状态;正常启用值为 1 |
| userTenantStatus | 成员关系状态;正常值为 1 |
| tenantAdmin | 当前用户是否具有该租户管理角色;不等于所有接口都有权限 |
客户端兼容状态值的字符串/数字表示,只把租户启用且成员关系正常的记录作为可选目标。列表不包含完整的有效期、套餐可用性判断,不能把“出现在列表中”当作所有业务调用均获授权。
登录响应中的 tenantList 是另一种数据模型,其中 id 才是租户 ID;不要与本接口共用一个字段含义不明确的 DTO。
3. 切换当前登录租户
PUT /sys/user/changeLoginTenantId
Content-Type: application/json
X-Access-Token: <当前平台token>
X-Tenant-Id: <切换前已确认的租户ID>
请求体:
{"loginTenantId": 1234}
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| loginTenantId | integer | 是 | 从本人租户列表取得的目标租户 ID;不能传列表中的用户 id |
只传目标租户 ID,不传用户 id、用户名、角色或权限。接口以认证用户身份查询目标租户成员关系,然后更新该用户的 loginTenantId。它不是加入租户接口。
curl --request PUT 'https://work.zingyang.com/api/sys/user/changeLoginTenantId' \
--header 'X-Access-Token: <平台token>' \
--header 'X-Tenant-Id: <切换前租户ID>' \
--header 'Content-Type: application/json' \
--data '{"loginTenantId":1234}'
成功响应示意:
{
"success": true,
"message": "",
"code": 200,
"result": null,
"timestamp": 1792972800000
}
时间戳仅为示例,客户端应兼容无有效 result 的成功响应。不会签发新 token,不返回新用户对象或新权限列表。 不要从 result 中尝试读取 token。
如果由 AI 工具代用户切换,这是业务写操作:先取得用户明确授权,再携带 X-Zing-Ai-Context: 1 和 X-Open-Confirm: confirmed;普通页面请求无需额外标记为 AI 调用。
4. 客户端完整切换流程
- 获取本人租户列表,选择正常成员关系下已启用的目标租户。
- 进入“切换中”,暂停新的业务请求;取消或隔离旧租户在途响应,暂保留旧会话快照。
- 使用旧上下文调用切换接口,检查 HTTP 状态、
success与code。 - 成功后保留同一个 token,把后续请求的
X-Tenant-Id更新为目标租户 ID。 - 用目标租户头重新读取权限:
GET /sys/permission/getUserPermissionByToken,并加载目标租户首屏数据。权限响应中的codeList是用户授权编码;allAuth是全量定义,不代表当前用户全部拥有。 - 缓存按
API环境 + 用户ID + 租户ID分区;切换前发出的请求即使迟到返回,也不能覆盖新租户页面。
GET /sys/user/getUserInfo 可用于读取会话资料,但其中 loginTenantId 可能受缓存影响,不能只靠立即回读该字段判断整个切换流程完成。必须核实目标上下文的实际授权读取;失败时展示初始化失败,而不是拿旧租户缓存充当新数据。
5. 当前服务端边界
切换控制器目前检查目标租户是否有该用户的成员关系记录,没有在这个方法里完整校验成员状态、租户启停/有效期、套餐状态,也没有刷新全部用户缓存或编排权限重载;更新操作的布尔结果也没有单独作为失败分支处理。文档不会把这些尚未实现的检查写成已有保障。
因此,客户端应限制候选项并完成切换后的真实读取校验;客户端过滤不能代替业务后端鉴权。AI 等业务接口仍独立检查用户、租户、成员关系及权益,切换接口成功不代表这些能力一定可用。
6. 错误与恢复
| 场景 | 处理 |
|---|---|
| token 缺失、无效或过期 | 按实际认证失败响应重新登录 |
| 用户不属于目标租户 | 当前业务码 500,message 为「非租户下的用户,不允许修改!」;不更新本地目标上下文 |
| 成员列表为空 | 展示暂无可用租户;不自行猜 ID 或借用其他用户租户 |
| 参数格式错误、缺少目标 ID | 按实际失败响应处理;客户端应提前校验正整数,不依赖缺参时的偶然行为 |
| 目标业务读取返回 403/510 | 提示租户或权限问题,不能通过更换接口绕过 |
| 目标 AI 调用返回 402 | 目标租户权益未满足,进入现有套餐查看/续费/升级流程;不是重新登录就能解决 |
| 切换请求网络超时 | 结果未知,不假定成功或失败;暂停目标业务、重新核对上下文,避免无限重复写请求 |
业务错误可能使用 HTTP200 + success=false,不能只看 HTTP 状态。重新选择其他租户也必须重新走完整切换流程。
7. 相关文档与验证范围
核查依据:SysTenantController#getTenantListByUserId、SysTenantUserServiceImpl#getTenantListByUserId、SysTenantUserMapper.xml 的字段映射,以及 SysUserController#changeLoginTenantId。列表实际调用方为 Admin 的 TenantSetting.vue;切换接口未在当前已跟踪的客户端 TS/Vue/Swift 源码中检索到直接调用,不据此宣称客户端已有完整切换功能。
本次仅编写和发布文档,没有替生产用户切换租户,也没有新增成员、调整权限或套餐。示例值不是生产账号资料。