# License 授权系统 API 文档 ## 1. 概述 License 系统用于管理超级管理员级别的授权许可。 **层级关系**:平台 → 超级管理员 (License) → 租户 → 用户 **基础 URL**:`http://:`(开发环境默认 `http://localhost:8000`) --- ## 2. License 管理接口 ### 2.1 获取超级管理员列表 ``` GET /api/license/super-admins ``` 下拉选项接口,获取所有超级管理员,用于创建 License 时选择关联对象。 **响应**: ```json [ { "id": 1, "username": "admin1", "nickname": "管理员A", "remark": "客户A" } ] ``` --- ### 2.2 创建/更新 License ``` POST /api/license/ Content-Type: application/json ``` **请求体**: | 字段 | 类型 | 必填 | 说明 | |------|------|------|------| | `super_admin_id` | integer | 是 | 超级管理员 ID | | `license_key` | string | 是 | License 密钥(1-200 字符) | | `expires_at` | string | 是 | 过期时间(ISO 8601 格式) | | `max_tenants` | integer | 否 | 可管理租户上限 | | `max_users_per_tenant` | integer | 否 | 每租户用户上限 | | `remark` | string | 否 | 备注 | **示例**: ```bash curl -X POST "http://localhost:8000/api/license/" \ -H "Content-Type: application/json" \ -d '{ "super_admin_id": 1, "license_key": "LICENSE-2026-ABCDEF", "expires_at": "2027-12-31T23:59:59", "max_tenants": 10, "remark": "年度授权" }' ``` **响应**: ```json { "message": "License已创建", "license_id": 1 } ``` > 同一 `super_admin_id` 只允许一个 `active` 状态的 License。重复创建会更新已有记录。 --- ### 2.3 获取 License 列表 ``` GET /api/license/list?super_admin_id=1&status=active&page=1&size=20 ``` **查询参数**: | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | `super_admin_id` | integer | 否 | 按超管筛选 | | `status` | string | 否 | `active` / `expired` / `revoked` | | `page` | integer | 否 | 页码,默认 1 | | `size` | integer | 否 | 每页条数,默认 20,最大 100 | **响应**: ```json { "total": 1, "items": [ { "id": 1, "super_admin_id": 1, "super_admin_name": "客户A", "license_key": "LICENSE-2026-ABCDEF", "expires_at": "2027-12-31T23:59:59", "status": "active", "max_tenants": 10, "max_users_per_tenant": null, "remark": "年度授权", "created_at": "2026-05-13T10:00:00", "updated_at": null } ] } ``` --- ### 2.4 获取 License 详情 ``` GET /api/license/{license_id} ``` **响应**: ```json { "id": 1, "super_admin_id": 1, "super_admin_name": "客户A", "license_key": "LICENSE-2026-ABCDEF", "expires_at": "2027-12-31T23:59:59", "status": "active", "days_left": 232, "max_tenants": 10, "max_users_per_tenant": null, "remark": "年度授权" } ``` > 查询时如果 `days_left <= 0`,会自动将 License 状态更新为 `expired`。 --- ### 2.5 吊销 License ``` POST /api/license/{license_id}/revoke ``` **响应**: ```json { "message": "License已吊销" } ``` --- ### 2.6 删除 License ``` DELETE /api/license/{license_id} ``` **响应**: ```json { "message": "License已删除" } ``` --- ## 3. 公开 License 校验接口(第三方系统集成) ### 3.1 接口说明 提供给第三方系统调用的公开接口,**无需认证**。通过请求的 `Referer` 头自动匹配对应的监控域名,并返回该域名关联超管的 License 状态。 ### 3.2 域名匹配流程 ``` 请求携带 Referer: https://example.com/page → 提取域名 example.com → 在 monitored_domains 表中匹配 domain = 'example.com'(需 is_active=true) → 获取该记录关联的 super_admin_id → 查询该超管当前 active 的 License → 返回 License 状态 ``` ### 3.3 请求 ``` GET /api/public/license/check ``` **请求头**: | 头 | 类型 | 必填 | 说明 | |------|------|------|------| | `Referer` | string | 是 | 请求来源页面的完整 URL,用于提取域名匹配 | **curl 示例**: ```bash curl -H "Referer: https://example.com/dashboard" \ "http://localhost:8000/api/public/license/check" ``` ### 3.4 响应 #### License 有效 ```json { "valid": true, "status": "active", "super_admin_name": "客户A", "license_key": "LICENSE-2026-ABCDEF", "expires_at": "2027-12-31T23:59:59", "days_left": 232, "max_tenants": 10, "max_users_per_tenant": null, "remark": "年度授权" } ``` #### 域名未注册或无关联超管 ```json { "valid": false, "status": "unknown", "message": "域名未注册或无关联超管" } ``` #### 未找到有效 License ```json { "valid": false, "status": "not_found", "message": "未找到有效 License" } ``` #### 缺少 Referer 头 ```json { "valid": false, "status": "unknown", "message": "缺少 Referer 头" } ``` ### 3.5 响应字段说明 | 字段 | 类型 | 出现条件 | 说明 | |------|------|----------|------| | `valid` | boolean | 总是 | License 是否有效 | | `status` | string | 总是 | `active`(有效)、`not_found`(未找到)、`unknown`(未知/错误) | | `super_admin_name` | string | `valid=true` | 超管显示名称(优先取 remark,否则取 username) | | `license_key` | string | `valid=true` | License 密钥 | | `expires_at` | string | `valid=true` | 过期时间(ISO 8601) | | `days_left` | integer | `valid=true` | 剩余天数,负数表示已过期 | | `max_tenants` | integer | `valid=true` | 可管理租户上限 | | `max_users_per_tenant` | integer | `valid=true` | 每租户用户上限 | | `remark` | string | `valid=true` | License 备注 | | `message` | string | `valid=false` | 失败原因说明 | ### 3.6 调用示例 **前端(浏览器自动携带 Referer)**: ```javascript const res = await fetch('/api/public/license/check'); const data = await res.json(); if (data.valid) { console.log(`License 有效,剩余 ${data.days_left} 天`); } else { console.warn(`License 校验失败:${data.message}`); } ``` **Python 后端**: ```python import requests resp = requests.get( "https://license-server.example.com/api/public/license/check", headers={"Referer": "https://example.com/dashboard"}, ) print(resp.json()) ``` **Node.js**: ```javascript const resp = await fetch("https://license-server.example.com/api/public/license/check", { headers: { "Referer": "https://example.com/dashboard" }, }); console.log(await resp.json()); ``` **curl**: ```bash curl -H "Referer: https://example.com/dashboard" \ "https://license-server.example.com/api/public/license/check" ``` --- ## 4. 数据表 ### super_admin_license | 字段 | 类型 | 说明 | |------|------|------| | `id` | INTEGER PK | 主键 | | `super_admin_id` | INTEGER | 关联的超管 ID | | `license_key` | VARCHAR(200) | License 密钥 | | `expires_at` | TIMESTAMPTZ | 过期时间 | | `status` | VARCHAR(20) | `active` / `expired` / `revoked` | | `max_tenants` | INTEGER | 租户上限 | | `max_users_per_tenant` | INTEGER | 每租户用户上限 | | `remark` | TEXT | 备注 | | `created_at` | TIMESTAMPTZ | 创建时间 | | `updated_at` | TIMESTAMPTZ | 更新时间 | 索引:`super_admin_id`, `status`, `expires_at` ### monitored_domains(公开校验接口关联用) | 字段 | 类型 | 说明 | |------|------|------| | `id` | INTEGER PK | 主键 | | `domain` | VARCHAR (UNIQUE) | 域名 | | `super_admin_id` | INTEGER | 关联的超管 ID | | `is_active` | BOOLEAN | 是否启用 | --- ## 5. 常见问题 **Q: 同一超管可以创建多个 License 吗?** A: 同一 `super_admin_id` 只允许一个 `active` 状态的 License。重复创建会更新已有记录。 **Q: License 过期后会自动变为 expired 吗?** A: 会。每次查询 License 详情时会自动检查 `days_left`,如果 `<= 0` 则自动更新状态为 `expired`。 **Q: 公开校验接口需要认证吗?** A: 不需要。`GET /api/public/license/check` 是公开接口,第三方系统可直接调用。 **Q: 公开接口如果域名没有关联超管怎么办?** A: 返回 `"valid": false, "status": "unknown", "message": "域名未注册或无关联超管"`。