<template>
  <div class="help-content">
    <div class="content-header">
      <h2>UAP 管理 API（X-Admin-Api-Key）</h2>
      <el-button
        type="primary"
        size="small"
        plain
        @click="downloadDoc('/skills/uap-admin-api/LLM_ADMIN_API_INTEGRATION.md', 'UAP_Admin_API_Integration.md')"
      >
        <el-icon style="margin-right: 5px"><Download /></el-icon>
        下载 完整接口手册
      </el-button>
    </div>
    <p class="intro">
      本文说明如何在<strong>业务系统服务端</strong>使用 UAP 管理后台发放的<strong>管理员级永久 API Key</strong>，通过 Header
      <code>X-Admin-Api-Key</code> 调用 <code>/admin-api/*</code> 接口维护平台用户与组织。下文按接口列出<strong>发送（Header、Query、JSON 体）</strong>与<strong>接收（响应体）</strong>及示例；亦可下载 Markdown 获取相同内容的离线版。
    </p>

    <div class="section">
      <h3>1. 适用场景与边界</h3>
      <ul>
        <li>
          <strong>适用</strong>：总部或运维系统在服务端批量查询/创建/更新 UAP 用户与组织，替代人工登录后台操作。
        </li>
        <li>
          <strong>与业务应用对接（uap-api）的区别</strong>：应用侧 SSO、用户映射、消息等使用
          <code>APP_ID</code>、<code>APP_SECRET</code>、HMAC 签名或 <code>APP_ACCESS_TOKEN</code>；管理 API <strong>仅</strong>使用
          <code>X-Admin-Api-Key</code>，<strong>不要混用</strong>两套 Header。
        </li>
      </ul>
    </div>

    <div class="section">
      <h3>2. 配置与 Key 生命周期</h3>
      <table class="param-table">
        <thead>
          <tr>
            <th>项</th>
            <th>说明</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td><code>UAP_API_BASE</code></td>
            <td>
              API 根路径，<strong>必须</strong>包含版本前缀 <code>/api/v1</code>（例如 <code>https://example.com/api/v1</code>）。下文路径均拼接在其后。
            </td>
          </tr>
          <tr>
            <td><code>ADMIN_API_KEY</code></td>
            <td>明文 Key，形如 <code>ak_live_…</code>。<strong>仅服务端</strong>使用，勿写入前端或公开仓库。</td>
          </tr>
        </tbody>
      </table>
      <ul>
        <li>Key 由 UAP 后台「API Key 管理」（超级管理员）创建；<strong>明文仅在创建成功时返回一次</strong>，丢失需吊销后新建。</li>
        <li>禁用可在后台恢复；吊销不可恢复，须换新 Key。</li>
      </ul>
    </div>

    <div class="section">
      <h3>3. 鉴权（所有请求）</h3>
      <p>每个请求必须在 Header 中携带（不要用应用侧的 <code>X-App-Id</code> / <code>X-Sign</code> 等）：</p>
      <div class="code-block">
        <pre>X-Admin-Api-Key: ak_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx</pre>
      </div>
      <ul>
        <li>校验通过后平台会更新 Key 的使用次数与最近使用时间；不要额外携带 <code>X-App-Id</code>、<code>X-App-Access-Token</code>、<code>X-Sign</code>、<code>X-Timestamp</code>。</li>
      </ul>
    </div>

    <div class="section">
      <h3>4. Scopes 与接口映射</h3>
      <p>Key 创建时由超管分配 scope；缺权限时返回 <strong>403</strong>，响应体形如 <code>{"detail":"API Key 缺少 user:read 权限"}</code>。</p>
      <table class="param-table">
        <thead>
          <tr>
            <th>Scope</th>
            <th>可调用的接口</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td><code>user:read</code></td>
            <td>
              <code>GET /admin-api/users/</code>、<code>GET /admin-api/users/search</code>、<code>GET /admin-api/users/{id}</code>
            </td>
          </tr>
          <tr>
            <td><code>user:write</code></td>
            <td><code>POST /admin-api/users/</code>、<code>PUT /admin-api/users/{id}</code></td>
          </tr>
          <tr>
            <td><code>org:read</code></td>
            <td><code>GET /admin-api/organizations/</code></td>
          </tr>
          <tr>
            <td><code>org:write</code></td>
            <td><code>POST /admin-api/organizations/</code>、<code>PUT /admin-api/organizations/{id}</code></td>
          </tr>
        </tbody>
      </table>
    </div>

    <div class="section">
      <h3>5. 公共约定</h3>
      <ul>
        <li><strong>完整 URL</strong>：<code>{UAP_API_BASE}</code> + 下文路径（注意尾部斜杠与列表接口保持一致）。</li>
        <li><strong>JSON 接口</strong>：<code>POST</code> / <code>PUT</code> 需带 <code>Content-Type: application/json</code>。</li>
        <li><strong>用户对象字段含义</strong>：<code>status</code> 取值 <code>PENDING</code> / <code>ACTIVE</code> / <code>DISABLED</code>；<code>role</code> 取值 <code>SUPER_ADMIN</code> / <code>DEVELOPER</code> / <code>ORDINARY_USER</code>；<code>is_deleted</code> 为 <code>0</code> 或 <code>1</code>；时间为 ISO8601 字符串。</li>
      </ul>
    </div>

    <div class="section">
      <h3>6. 用户接口详述</h3>

      <h4>6.1 GET /admin-api/users/（分页列表）</h4>
      <p><strong>权限</strong>：<code>user:read</code>。<strong>说明</strong>：仅列出未删除用户（<code>is_deleted = 0</code>），按 <code>id</code> 降序。</p>
      <p><strong>发送 — Query 参数</strong></p>
      <table class="param-table">
        <thead>
          <tr>
            <th>参数</th>
            <th>类型</th>
            <th>默认</th>
            <th>说明</th>
          </tr>
        </thead>
        <tbody>
          <tr><td><code>skip</code></td><td>整数</td><td>0</td><td>跳过条数（分页偏移）</td></tr>
          <tr><td><code>limit</code></td><td>整数</td><td>50</td><td>每页条数</td></tr>
          <tr><td><code>status</code></td><td>字符串</td><td>—</td><td>精确匹配：<code>PENDING</code> / <code>ACTIVE</code> / <code>DISABLED</code></td></tr>
          <tr><td><code>role</code></td><td>字符串</td><td>—</td><td>精确匹配：<code>SUPER_ADMIN</code> / <code>DEVELOPER</code> / <code>ORDINARY_USER</code></td></tr>
          <tr><td><code>mobile</code></td><td>字符串</td><td>—</td><td>手机号模糊匹配（包含即可）</td></tr>
          <tr><td><code>name</code></td><td>字符串</td><td>—</td><td>中文名模糊匹配</td></tr>
          <tr><td><code>english_name</code></td><td>字符串</td><td>—</td><td>英文名模糊匹配</td></tr>
          <tr><td><code>keyword</code></td><td>字符串</td><td>—</td><td>同时对手机、中文名、英文名做模糊匹配（任一条命中即可）</td></tr>
          <tr>
            <td><code>organization_id</code></td>
            <td>整数</td>
            <td>—</td>
            <td>精确匹配组织 ID；传 <code>0</code> 表示筛选「未归属任何组织」的用户</td>
          </tr>
        </tbody>
      </table>
      <p><strong>接收 — 响应体（HTTP 200）</strong>：<code>total</code> 为符合条件的总条数；<code>items</code> 为用户对象数组。</p>
      <div class="code-block">
        <pre>{
  "total": 128,
  "items": [
    {
      "id": 1001,
      "mobile": "13800138000",
      "name": "张三",
      "english_name": "zhangsan",
      "organization_id": 7,
      "organization_name": "研发中心",
      "status": "ACTIVE",
      "role": "ORDINARY_USER",
      "is_deleted": 0,
      "created_at": "2026-01-10T08:00:00",
      "updated_at": "2026-04-01T12:00:00"
    }
  ]
}</pre>
      </div>
      <p class="note">示例请求：<code>GET …/admin-api/users/?skip=0&amp;limit=20&amp;status=ACTIVE&amp;keyword=zhang</code></p>

      <h4>6.2 GET /admin-api/users/search（关键词搜索）</h4>
      <p><strong>权限</strong>：<code>user:read</code>。<strong>说明</strong>：只返回<strong>已激活且未删除</strong>的用户；不传 <code>q</code> 时按 <code>limit</code> 截取一批激活用户。</p>
      <p><strong>发送 — Query 参数</strong></p>
      <table class="param-table">
        <thead>
          <tr>
            <th>参数</th>
            <th>类型</th>
            <th>默认</th>
            <th>说明</th>
          </tr>
        </thead>
        <tbody>
          <tr><td><code>q</code></td><td>字符串</td><td>—</td><td>关键词；模糊匹配手机、中文名、英文名</td></tr>
          <tr><td><code>limit</code></td><td>整数</td><td>20</td><td>最多返回条数</td></tr>
        </tbody>
      </table>
      <p><strong>接收 — 响应体（HTTP 200）</strong>：<strong>JSON 数组</strong>（不是分页对象），元素字段与用户列表中单条一致。</p>
      <div class="code-block">
        <pre>[
  {
    "id": 1001,
    "mobile": "13800138000",
    "name": "张三",
    "english_name": "zhangsan",
    "organization_id": 7,
    "organization_name": "研发中心",
    "status": "ACTIVE",
    "role": "ORDINARY_USER",
    "is_deleted": 0,
    "created_at": "2026-01-10T08:00:00",
    "updated_at": "2026-04-01T12:00:00"
  }
]</pre>
      </div>

      <h4>6.3 GET /admin-api/users/{user_id}（用户详情）</h4>
      <p><strong>权限</strong>：<code>user:read</code>。<strong>发送 — 路径参数</strong>：<code>user_id</code> 为平台用户主键（整数）。</p>
      <p><strong>接收 — 响应体（HTTP 200）</strong>：单个用户对象，字段同列表项。若用户不存在或已软删，返回 <strong>404</strong>，<code>detail</code> 为「用户不存在」。</p>
      <div class="code-block">
        <pre>{
  "id": 1001,
  "mobile": "13800138000",
  "name": "张三",
  "english_name": "zhangsan",
  "organization_id": 7,
  "organization_name": "研发中心",
  "status": "ACTIVE",
  "role": "ORDINARY_USER",
  "is_deleted": 0,
  "created_at": "2026-01-10T08:00:00",
  "updated_at": "2026-04-01T12:00:00"
}</pre>
      </div>

      <h4>6.4 POST /admin-api/users/（创建用户）</h4>
      <p><strong>权限</strong>：<code>user:write</code>。<strong>说明</strong>：平台<strong>强制</strong>新用户为 <code>role = ORDINARY_USER</code>（普通用户）、<code>status = ACTIVE</code>；请求体<strong>不要</strong>包含 <code>role</code>、<code>status</code>。</p>
      <p><strong>发送 — JSON 体</strong></p>
      <table class="param-table">
        <thead>
          <tr>
            <th>字段</th>
            <th>类型</th>
            <th>必填</th>
            <th>说明</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td><code>mobile</code></td>
            <td>字符串</td>
            <td>是</td>
            <td>中国大陆手机号，正则 <code>^1[3-9]\d{9}$</code></td>
          </tr>
          <tr>
            <td><code>password</code></td>
            <td>字符串</td>
            <td>是</td>
            <td>6–64 位；空或仅空格会返回 400「密码不能为空」</td>
          </tr>
          <tr>
            <td><code>name</code></td>
            <td>字符串或 null</td>
            <td>否</td>
            <td>中文名；不传或去空格后为空时，平台生成「用户」+ 6 位随机字符并保证不重名</td>
          </tr>
          <tr>
            <td><code>english_name</code></td>
            <td>字符串或 null</td>
            <td>否</td>
            <td>英文名；不传时由平台按姓名生成拼音并去重</td>
          </tr>
          <tr>
            <td><code>organization_id</code></td>
            <td>整数或 null</td>
            <td>否</td>
            <td>归属组织；若传非 null，该组织必须存在，否则 400「组织不存在」</td>
          </tr>
        </tbody>
      </table>
      <p><strong>接收 — 响应体（HTTP 200）</strong>：新建用户的完整对象（含 <code>id</code>、<code>organization_name</code> 等）。</p>
      <p><strong>常见错误</strong>：400「该手机号已在系统中注册」；422 手机号格式或密码长度不符。</p>
      <div class="code-block">
        <pre>POST …/admin-api/users/
Content-Type: application/json
X-Admin-Api-Key: ak_live_…

{
  "mobile": "13900139000",
  "password": "Init@2026",
  "name": "李四",
  "organization_id": 7
}</pre>
      </div>
      <div class="code-block">
        <pre>{
  "id": 1042,
  "mobile": "13900139000",
  "name": "李四",
  "english_name": "lisi",
  "organization_id": 7,
  "organization_name": "研发中心",
  "status": "ACTIVE",
  "role": "ORDINARY_USER",
  "is_deleted": 0,
  "created_at": "2026-04-28T10:00:00",
  "updated_at": "2026-04-28T10:00:00"
}</pre>
      </div>

      <h4>6.5 PUT /admin-api/users/{user_id}（更新用户）</h4>
      <p><strong>权限</strong>：<code>user:write</code>。<strong>说明</strong>：请求体使用白名单字段且 <code>extra=forbid</code>：<strong>禁止</strong>出现 <code>mobile</code>、<code>role</code>、<code>status</code>、<code>is_deleted</code>，否则 <strong>422</strong>。</p>
      <p><strong>发送 — 路径参数</strong>：<code>user_id</code>。<strong>发送 — JSON 体（至少一项；仅提交需要变更的字段）</strong></p>
      <table class="param-table">
        <thead>
          <tr>
            <th>字段</th>
            <th>类型</th>
            <th>说明</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td><code>name</code></td>
            <td>字符串或 null</td>
            <td>改名；若本次未同时传 <code>english_name</code>，平台会按新姓名自动重生成英文名并去重</td>
          </tr>
          <tr>
            <td><code>english_name</code></td>
            <td>字符串或 null</td>
            <td>显式设置英文名（会去重）；空字符串若语义允许则按后端逻辑处理</td>
          </tr>
          <tr>
            <td><code>password</code></td>
            <td>字符串</td>
            <td>新密码 6–64 位；不能为空字符串（否则 400「新密码不能为空」）</td>
          </tr>
          <tr>
            <td><code>organization_id</code></td>
            <td>整数或 null</td>
            <td><code>null</code> 表示取消归属；非 null 时组织须存在</td>
          </tr>
        </tbody>
      </table>
      <p><strong>接收 — 响应体（HTTP 200）</strong>：更新后的完整用户对象。</p>
      <p><strong>常见错误</strong>：404「用户不存在」；400「未提供需更新的字段」「未检测到变更」「姓名不能为空」「组织不存在」；422 含禁止字段。</p>
      <div class="code-block">
        <pre>PUT …/admin-api/users/1042
Content-Type: application/json

{ "organization_id": 12 }</pre>
      </div>
      <div class="code-block">
        <pre>{
  "detail": [
    {
      "loc": ["body", "mobile"],
      "msg": "Extra inputs are not permitted",
      "type": "extra_forbidden"
    }
  ]
}</pre>
      </div>
      <p class="note">反例：请求体中含 <code>mobile</code> 或 <code>role</code> 会触发上述 422 结构。</p>
    </div>

    <div class="section">
      <h3>7. 组织接口详述</h3>

      <h4>7.1 GET /admin-api/organizations/（组织全量列表）</h4>
      <p><strong>权限</strong>：<code>org:read</code>。<strong>说明</strong>：返回<strong>全部</strong>组织（不分页），按 <code>sort_order</code> 升序、再按 <code>id</code> 升序。</p>
      <p><strong>发送</strong>：无 Query 参数。</p>
      <p><strong>接收 — 响应体（HTTP 200）</strong>：JSON 数组，元素字段如下。</p>
      <table class="param-table">
        <thead>
          <tr>
            <th>字段</th>
            <th>类型</th>
            <th>说明</th>
          </tr>
        </thead>
        <tbody>
          <tr><td><code>id</code></td><td>整数</td><td>组织主键</td></tr>
          <tr><td><code>name</code></td><td>字符串</td><td>组织名称（唯一）</td></tr>
          <tr><td><code>description</code></td><td>字符串或 null</td><td>描述</td></tr>
          <tr><td><code>sort_order</code></td><td>整数</td><td>排序序号</td></tr>
          <tr><td><code>created_at</code> / <code>updated_at</code></td><td>字符串</td><td>时间（ISO8601）</td></tr>
        </tbody>
      </table>
      <div class="code-block">
        <pre>[
  {
    "id": 7,
    "name": "研发中心",
    "description": "技术研发部门",
    "sort_order": 0,
    "created_at": "2025-12-01T08:00:00",
    "updated_at": "2026-01-15T10:30:00"
  }
]</pre>
      </div>

      <h4>7.2 POST /admin-api/organizations/（创建组织）</h4>
      <p><strong>权限</strong>：<code>org:write</code>。</p>
      <p><strong>发送 — JSON 体</strong></p>
      <table class="param-table">
        <thead>
          <tr>
            <th>字段</th>
            <th>类型</th>
            <th>必填</th>
            <th>说明</th>
          </tr>
        </thead>
        <tbody>
          <tr><td><code>name</code></td><td>字符串</td><td>是</td><td>长度 1–100，全局唯一</td></tr>
          <tr><td><code>description</code></td><td>字符串或 null</td><td>否</td><td>描述</td></tr>
          <tr><td><code>sort_order</code></td><td>整数</td><td>否</td><td>默认 <code>0</code></td></tr>
        </tbody>
      </table>
      <p><strong>接收 — 响应体（HTTP 200）</strong>：新建的组织对象。名称冲突时 <strong>400</strong>「组织名称已存在」。</p>
      <div class="code-block">
        <pre>{
  "name": "市场部",
  "description": "对外市场与品牌",
  "sort_order": 5
}</pre>
      </div>

      <h4>7.3 PUT /admin-api/organizations/{organization_id}（更新组织）</h4>
      <p><strong>权限</strong>：<code>org:write</code>。<strong>说明</strong>：仅允许 <code>name</code>、<code>description</code>、<code>sort_order</code>；多传其他字段会 422。至少有一项实际变更，否则可能 400。</p>
      <p><strong>发送 — JSON 体（任选若干字段）</strong></p>
      <table class="param-table">
        <thead>
          <tr>
            <th>字段</th>
            <th>类型</th>
            <th>说明</th>
          </tr>
        </thead>
        <tbody>
          <tr><td><code>name</code></td><td>字符串或 null</td><td>1–100</td></tr>
          <tr><td><code>description</code></td><td>字符串或 null</td><td>描述</td></tr>
          <tr><td><code>sort_order</code></td><td>整数或 null</td><td>排序</td></tr>
        </tbody>
      </table>
      <p><strong>接收 — 响应体（HTTP 200）</strong>：更新后的组织对象。</p>
      <div class="code-block">
        <pre>{
  "description": "市场部 + 品牌运营",
  "sort_order": 3
}</pre>
      </div>
    </div>

    <div class="section">
      <h3>8. 易错点（白名单与 422）</h3>
      <p>更新类接口的请求体在 schema 层使用 <code>extra="forbid"</code>：多传未允许字段会直接 <strong>422</strong>，不会被静默忽略。</p>
      <ul>
        <li>
          <strong>创建用户</strong>：允许 <code>mobile</code>、<code>password</code>、<code>name</code>、<code>english_name</code>、<code>organization_id</code>；不要传 <code>role</code>、<code>status</code>。
        </li>
        <li>
          <strong>更新用户</strong>：仅允许 <code>name</code>、<code>english_name</code>、<code>password</code>、<code>organization_id</code>；禁止 <code>mobile</code>、<code>role</code>、<code>status</code>、<code>is_deleted</code>。改手机号、角色、删用户需在 UAP 后台处理。
        </li>
        <li><strong>组织</strong>：创建/更新仅允许上文列出的字段；名称重复等返回 400。</li>
        <li>本组接口<strong>不提供</strong>用户/组织的删除能力。</li>
      </ul>
    </div>

    <div class="section">
      <h3>9. 操作审计（写入接口）</h3>
      <p>每次 <code>POST</code> / <code>PUT</code> 成功写入用户或组织时，平台会记录操作日志，<code>details</code> 中至少包含：</p>
      <div class="code-block">
        <pre>{
  "via": "admin_api_key",
  "key_id": 12,
  "key_prefix": "ak_live_xxx"
}</pre>
      </div>
      <p>便于根据 Key 追溯到发放者（超管）。请勿在团队间随意共享同一把 Key。</p>
    </div>

    <div class="section">
      <h3>10. 常见 HTTP 状态</h3>
      <table class="param-table">
        <thead>
          <tr>
            <th>HTTP</th>
            <th>含义</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td>401</td>
            <td>缺少 <code>X-Admin-Api-Key</code></td>
          </tr>
          <tr>
            <td>403</td>
            <td>Key 无效、已禁用、已吊销或缺少对应 scope</td>
          </tr>
          <tr>
            <td>404</td>
            <td>用户或组织不存在</td>
          </tr>
          <tr>
            <td>422</td>
            <td>请求体含禁止字段或字段校验失败（如手机号格式）</td>
          </tr>
          <tr>
            <td>400</td>
            <td>业务校验失败（如手机号已注册、无变更、组织名重复等）</td>
          </tr>
        </tbody>
      </table>
    </div>

    <div class="section">
      <h3>11. Swagger 与离线手册</h3>
      <p>
        帮助页顶部的「API 文档 (Swagger)」中可查看 <strong>管理 API - 用户</strong>、<strong>管理 API - 组织</strong> 等标签；Authorize 中可填写
        <code>X-Admin-Api-Key</code> 在线调试。亦可点击页面顶部按钮下载 Markdown 完整手册。
      </p>
    </div>
  </div>
</template>

<script setup lang="ts">
import { Download } from '@element-plus/icons-vue'
import { useHelpDocs } from '../../composables/useHelpDocs'

const { downloadDoc } = useHelpDocs()
</script>

<style scoped>
@import './help.css';
</style>
