文档 / 接入指南
原始 Markdown ↗

获取本人租户列表与切换租户

更新日期: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. 客户端完整切换流程

  1. 获取本人租户列表,选择正常成员关系下已启用的目标租户。
  2. 进入“切换中”,暂停新的业务请求;取消或隔离旧租户在途响应,暂保留旧会话快照。
  3. 使用旧上下文调用切换接口,检查 HTTP 状态、success 与 code。
  4. 成功后保留同一个 token,把后续请求的 X-Tenant-Id 更新为目标租户 ID。
  5. 用目标租户头重新读取权限:GET /sys/permission/getUserPermissionByToken,并加载目标租户首屏数据。权限响应中的 codeList 是用户授权编码;allAuth 是全量定义,不代表当前用户全部拥有。
  6. 缓存按 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 源码中检索到直接调用,不据此宣称客户端已有完整切换功能。

本次仅编写和发布文档,没有替生产用户切换租户,也没有新增成员、调整权限或套餐。示例值不是生产账号资料。