---
name: uap-admin-api
description: 在业务系统中使用 X-Admin-Api-Key（管理员级永久 API Key）调用统一认证平台（UAP / Unified Authentication Platform）的 /admin-api/* 管理接口：含 UAP_API_BASE 配置、Header 鉴权、scopes 权限模型（user:read|user:write|org:read|org:write）、用户与组织的查询/创建/更新、白名单字段约束（禁止改 mobile/role/status、不支持删除）、操作审计与常见错误码。当业务系统拿到一把 ak_live_… 形式的永久 Key、需要服务端调用 UAP 后台维护用户和组织时使用。详细字段与示例以同目录 LLM_ADMIN_API_INTEGRATION.md 为准。
---

# UAP 管理 API 对接（X-Admin-Api-Key）

## 何时使用

业务系统已从 UAP 后台拿到一把 **管理员级永久 API Key**（明文形如 `ak_live_xxxxxxxx…`），需在**服务端**直接调用 UAP 的 `/admin-api/*` 接口管理用户与组织（替代人工到后台点击）。

> 与 `uap-api` Skill 的区别：
>
> - `uap-api`：业务应用对接 UAP 的「登录 / SSO / 用户映射 / 消息」，使用三套凭据（APP_ID + APP_SECRET 做 HMAC 签名 / APP_ACCESS_TOKEN）。
> - `uap-admin-api`（本 Skill）：**只**使用一把 `X-Admin-Api-Key` Header，调用 `/admin-api/users` 与 `/admin-api/organizations`，做平台级用户/组织维护；**不**做签名、**不**做登录票据、**不**发消息。
>
> 两套接口的鉴权模型互不兼容，**不要混用**。

## 向用户询问（必须）

下文示例中的 Key、API 根地址均为占位，**不得**臆造或把示例当真实值。在编写或修改对接代码、配置 `.env` 前：

1. **若仓库内尚无有效配置**：在对话中**向用户询问**，请其从 **UAP 管理后台 → API Key 管理**（仅超级管理员可见）提供：
   - `UAP_API_BASE`（必须含 `/api/v1`，如 `https://api.hnyunzhu.com/api/v1`）。
   - `ADMIN_API_KEY`：完整明文 Key（`ak_live_…`）。**仅服务端**使用，提醒用户不要贴到公开渠道、不要写入前端代码或 git。
   - 该 Key 已授予的 **`scopes`**（逗号分隔，子集来自 `user:read,user:write,org:read,org:write`）。Key 实际拥有什么权限只能由发放方告知，平台在缺 scope 时直接返回 403。
2. **明文 Key 的获取方式**：平台**只在创建 Key 那一刻**返回一次明文（`POST /admin-api-keys/` 的 `key_once` 字段），之后只能在后台看到**前缀**与统计信息。如果用户说"Key 丢了"，引导其到后台**吊销旧 Key 并新建一把**，而不是尝试找回。
3. **Key 状态不对时的应对**：禁用（`is_active=false`）→ 让用户在后台启用；吊销（`is_revoked=true`）→ 不可恢复，必须重发新 Key。
4. 用户已在本机 `.env` 填好且仅调试时，可读 `.env`（勿把内容写回仓库）；仍缺项时继续询问。

## 权威文档（必须先读）

**完整接口、请求/响应字段、错误处理以下文件为准，不要凭直觉补字段：**

| 路径 | 说明 |
|------|------|
| `./LLM_ADMIN_API_INTEGRATION.md`（与本 `SKILL.md` 同目录） | 独立成篇的接口手册，面向自动化阅读 |

## 鉴权与权限模型

### Header（所有 `/admin-api/*` 请求都要带）

```
X-Admin-Api-Key: ak_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx…
```

- **不要**额外带 `X-App-Id`、`X-App-Access-Token`、`X-Sign`、`X-Timestamp` —— 这些属于 `uap-api` 的应用签名/M2M 体系，与本 Key 无关。
- 平台对 Key 做：前缀粗筛（前 12 字节） → bcrypt 校验完整 Key → 检查 `is_active=true` 且 `is_revoked=false` → 命中后递增 `use_count`，刷新 `last_used_at` / `last_used_ip`（每次调用都会留痕）。

### Scopes（写到 Key 的元数据里，由超管创建时分配）

| Scope | 控制的接口 |
|-------|------------|
| `user:read` | `GET /admin-api/users/`、`GET /admin-api/users/search`、`GET /admin-api/users/{id}` |
| `user:write` | `POST /admin-api/users/`、`PUT /admin-api/users/{id}` |
| `org:read` | `GET /admin-api/organizations/` |
| `org:write` | `POST /admin-api/organizations/`、`PUT /admin-api/organizations/{id}` |

缺对应 scope 时平台返回 **`403 API Key 缺少 xxx 权限`**，不会偷偷放行。

## 端点速查表

| 方法 | 路径 | Scope | 用途 |
|------|------|-------|------|
| GET | `/admin-api/users/` | `user:read` | 用户分页列表，支持 `skip / limit / status / role / mobile / name / english_name / keyword / organization_id` 多条件过滤 |
| GET | `/admin-api/users/search?q=&limit=` | `user:read` | 按关键词模糊搜索激活用户 |
| GET | `/admin-api/users/{user_id}` | `user:read` | 用户详情 |
| POST | `/admin-api/users/` | `user:write` | 创建普通用户（强制 `role=ORDINARY_USER`、`status=ACTIVE`） |
| PUT | `/admin-api/users/{user_id}` | `user:write` | 更新用户**白名单字段**（仅 `name / english_name / password / organization_id`） |
| GET | `/admin-api/organizations/` | `org:read` | 组织全量列表（按 `sort_order, id` 升序） |
| POST | `/admin-api/organizations/` | `org:write` | 创建组织 |
| PUT | `/admin-api/organizations/{organization_id}` | `org:write` | 更新组织（白名单：`name / description / sort_order`） |

> 接口完整字段、请求/响应 JSON、过滤参数细节请打开 `LLM_ADMIN_API_INTEGRATION.md` 对应章节，再写代码。

## 白名单与禁止项（最重要的"易错点"）

平台在 schema 层用 `extra="forbid"` 做了硬约束。误传字段不会被默默忽略，会直接 **422**：

### 创建用户（`POST /admin-api/users/`）

- **允许**：`mobile`（中国大陆手机号正则 `^1[3-9]\d{9}$`，必填）、`password`（必填，6–64 位）、`name`、`english_name`、`organization_id`。
- **平台强制**：新建用户始终为 `role=ORDINARY_USER`、`status=ACTIVE`。请求体里**不要**传 `role` / `status`。
- 不传 `name` 时平台自动生成 `用户XXXXXX`；不传 `english_name` 时按姓名规则生成拼音。

### 更新用户（`PUT /admin-api/users/{user_id}`）

- **仅允许**：`name`、`english_name`、`password`、`organization_id`。
- **明确禁止**（传了直接 422）：`mobile`、`role`、`status`、`is_deleted`。
  - 改手机号 → 必须到 UAP 后台人工处理；
  - 提升超管 / 改角色 → 不在此接口职责内；
  - 删除用户 / 软删除 → 此接口**不暴露**，由 UAP 后台超管完成。
- `name` 变更后若未显式传 `english_name`，平台会自动重生成拼音并去重。

### 组织接口

- 创建：`name`（1–100，必填，唯一）、`description`、`sort_order`（默认 0）。`name` 重复返回 400。
- 更新：白名单 `name / description / sort_order`；禁止其他字段（`extra="forbid"` → 422）。无任何字段变更时返回 400。

## 错误码速查

| HTTP | 含义 |
|------|------|
| 401 | 缺少 `X-Admin-Api-Key` Header |
| 403 | Key 不存在 / 已禁用 / 已吊销 / 缺对应 scope（`API Key 缺少 xxx 权限`） |
| 404 | 用户或组织不存在 |
| 422 | 请求体含未在白名单内的字段（`extra="forbid"`），或字段类型/正则不匹配（如手机号） |
| 400 | 业务校验失败：手机号已注册、密码为空、组织名重复、未检测到变更等 |

## 操作审计（自动）

每次 `POST` / `PUT` 写操作，平台都会写一条 `operation_log`，`details` 至少含：

```json
{
  "via": "admin_api_key",
  "key_id": <int>,
  "key_prefix": "ak_live_xxx"
}
```

`operator_id` 取 Key 创建者（`AdminApiKey.created_by`）。这意味着：**Key 出问题就能追到发放它的超管**——侧面要求 Key 不要在团队间随意共享。

## 工作流程建议

1. **识别意图**：用户说"列用户 / 拉一份组织清单 / 同步外部账号到 UAP"→ 走读接口；说"批量创建账号 / 改部门"→ 写接口。
2. **凭据与地址**：按上文「向用户询问」收集；缺什么问什么，不要臆造。
3. **查对应章节**：把要调用的接口在 `LLM_ADMIN_API_INTEGRATION.md` §4（用户）/ §5（组织）找到，按字段表写代码。
4. **写代码前自检**：是否仅在服务端调用？Key 是否只从环境变量读取？是否有任何敏感字段（mobile/role/status）出现在 PUT 请求体里？

## 与 `uap-api` Skill 的边界（避免误用）

| 场景 | 用哪个 |
|------|-------|
| 业务应用要让用户 SSO 进自己 / 推消息 / 同步映射 | `uap-api`（APP_ID + APP_SECRET / APP_ACCESS_TOKEN，做签名） |
| 平台运维 / 总部系统要替超管管理 UAP 上的用户与组织 | `uap-admin-api`（X-Admin-Api-Key，本 Skill） |
| 同时要做以上两件事 | 两把凭据**分别**保管，分别走各自的接口；不要试图用 `X-Admin-Api-Key` 调 `/messages/`、也不要用 APP_SECRET 调 `/admin-api/*` —— 平台会直接 401/403 |

## 自检清单

- [ ] 已识别本次任务确实属于 `/admin-api/*` 范围（用户与组织的常规读写），不是 SSO / 消息 / 应用映射。
- [ ] 已向用户确认 `UAP_API_BASE`（含 `/api/v1`）、`ADMIN_API_KEY` 明文与 Key 已授予的 scopes。
- [ ] `ADMIN_API_KEY` 仅出现在服务端（`.env` / 服务进程环境变量），未写入前端、未提交仓库。
- [ ] 所有请求都带 `X-Admin-Api-Key` Header，且**没有**多带 `X-App-Id` / `X-App-Access-Token` / `X-Sign` / `X-Timestamp` 等 `uap-api` 体系的字段。
- [ ] 创建用户：未传 `role` / `status`；密码非空；手机号匹配 `^1[3-9]\d{9}$`。
- [ ] 更新用户：请求体只含 `name / english_name / password / organization_id` 中的若干项；**未传** `mobile` / `role` / `status` / `is_deleted`。
- [ ] 已对 401 / 403 / 404 / 422 / 400 设计了对应的错误处理与日志（含输出当前 Key 前缀，便于排查）。
- [ ] 涉及批量操作时，已与用户确认是否真的需要——本接口**不**支持批量删除 / 批量改角色 / 批量软删，需到后台处理。
- [ ] 若 Key 调用频繁，已与发放方约定**轮换流程**：先创建新 Key → 在调用方切换 → 再吊销旧 Key（吊销不可恢复）。
