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

更新日期：2026-10-26。API 根地址：`https://work.zingyang.com/api`。

本文中的“组织”和“租户”指同一业务上下文。两个接口都从平台登录 token 的认证身份确定当前用户，**不需要传 userId，也不能指定其他用户**。当前控制器没有额外的 `@RequiresPermissions` 要求，仍必须通过平台认证。

## 1. 公共请求头

```http
X-Access-Token: <平台登录token>
X-Tenant-Id: <当前已确认的租户ID>
```

沿用登录初始化确认的租户上下文。不要使用 Apple identity token，不要自行伪造租户 ID。首次还没有可用租户时按登录响应和实际业务权限处理，不要强行填入某个其他租户。

获取列表、记录当前租户本身不消耗 AI 额度；切换后调用 AI、上传等能力仍须分别满足目标租户的权限、有效权益和余量要求。

## 2. 获取本人所属租户

```http
GET /sys/tenant/getTenantListByUserId?userTenantStatus=1
```

| 参数 | 位置／类型 | 必填 | 说明 |
|---|---|---|---|
| userTenantStatus | query / string | 否 | 成员关系状态过滤；推荐 `1`，表示正常成员关系；支持逗号分隔多个状态 |

不传该参数时不按成员关系状态过滤。它过滤的是用户与租户的关系状态，**不是租户启用状态或套餐状态**。接口查询未删除租户，并根据当前用户 ID 返回列表；不接受客户端 userId 作为查询目标。

```bash
curl 'https://work.zingyang.com/api/sys/tenant/getTenantListByUserId?userTenantStatus=1' \
  --header 'X-Access-Token: <平台token>' \
  --header 'X-Tenant-Id: <当前租户ID>'
```

成功响应示意（省略其他可选字段）：

```json
{
  "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. 切换当前登录租户

```http
PUT /sys/user/changeLoginTenantId
Content-Type: application/json
X-Access-Token: <当前平台token>
X-Tenant-Id: <切换前已确认的租户ID>
```

请求体：

```json
{"loginTenantId": 1234}
```

| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| loginTenantId | integer | 是 | 从本人租户列表取得的目标租户 ID；不能传列表中的用户 id |

只传目标租户 ID，不传用户 id、用户名、角色或权限。接口以认证用户身份查询目标租户成员关系，然后更新该用户的 `loginTenantId`。它不是加入租户接口。

```bash
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}'
```

成功响应示意：

```json
{
  "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. 相关文档与验证范围

- [登录与会话初始化](/apple-sign-in-ios/)
- [修改本人资料](/user-profile/)
- [AI 大模型调用](/ai-chat/)

核查依据：SysTenantController#getTenantListByUserId、SysTenantUserServiceImpl#getTenantListByUserId、SysTenantUserMapper.xml 的字段映射，以及 SysUserController#changeLoginTenantId。列表实际调用方为 Admin 的 TenantSetting.vue；切换接口未在当前已跟踪的客户端 TS/Vue/Swift 源码中检索到直接调用，不据此宣称客户端已有完整切换功能。

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