license_api.md 8.0 KB

License 授权系统 API 文档

1. 概述

License 系统用于管理超级管理员级别的授权许可。

层级关系:平台 → 超级管理员 (License) → 租户 → 用户

基础 URLhttp://<host>:<port>(开发环境默认 http://localhost:8000


2. License 管理接口

2.1 获取超级管理员列表

GET /api/license/super-admins

下拉选项接口,获取所有超级管理员,用于创建 License 时选择关联对象。

响应

[
  {
    "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 备注

示例

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": "年度授权"
  }'

响应

{
  "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

响应

{
  "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}

响应

{
  "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

响应

{
  "message": "License已吊销"
}

2.6 删除 License

DELETE /api/license/{license_id}

响应

{
  "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 示例

curl -H "Referer: https://example.com/dashboard" \
  "http://localhost:8000/api/public/license/check"

3.4 响应

License 有效

{
  "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": "年度授权"
}

域名未注册或无关联超管

{
  "valid": false,
  "status": "unknown",
  "message": "域名未注册或无关联超管"
}

未找到有效 License

{
  "valid": false,
  "status": "not_found",
  "message": "未找到有效 License"
}

缺少 Referer 头

{
  "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)

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 后端

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

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

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": "域名未注册或无关联超管"