# 多项目综合管理平台 — API 集成文档

> Base URL：`https://www.wzl123.com/api`

本文档面向希望接入本平台能力的第三方开发者，涵盖 **激活码验证** 与 **软件自动更新** 两大模块。

---

## 目录

1. [基础信息](#1-基础信息)
2. [认证方式](#2-认证方式)
  - 2.1 [查询功能开关](#21-查询功能开关)
3. [激活码验证接口](#3-激活码验证接口)
   - 3.1 [验证单个激活码](#31-验证单个激活码)
   - 3.2 [批量验证激活码](#32-批量验证激活码)
   - 3.3 [查询激活码详情](#33-查询激活码详情)
4. [软件自动更新接口](#4-软件自动更新接口)
   - 4.1 [检查是否有新版本](#41-检查是否有新版本)
   - 4.2 [获取软件项目列表](#42-获取软件项目列表)
   - 4.3 [下载安装包](#43-下载安装包)
5. [错误码说明](#5-错误码说明)

---

## 1. 基础信息

| 字段 | 说明 |
|------|------|
| Base URL | `https://www.wzl123.com/api` |
| 数据格式 | 请求体与响应体均为 `application/json`（文件上传除外） |
| 字符编码 | UTF-8 |
| HTTPS | 生产环境强制使用 HTTPS，请勿在明文 HTTP 下传输 API Key |

所有接口均返回统一结构：

```json
{
  "success": true,
  "message": "操作说明",
  "data": {}
}
```

当 `success` 为 `false` 时，`message` 说明错误原因，`data` 可能为 `null`。

---

## 2. 认证方式

### API Key 认证（激活码验证类接口）

通过 HTTP 请求头传递：

```
X-API-Key: ak_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```

也可通过 URL 参数传递（不推荐，存在泄露风险）：

```
GET /api/verify/info?code=xxx&api_key=ak_xxx
```

**Key 类型说明：**

| 类型 | 获取方式 | 权限范围 |
|------|----------|----------|
| 用户 API Key | 登录后台 → 个人信息页面复制 | 验证该账号名下所有项目的激活码 |
| 项目 API Key | 项目管理 → 操作 → 查看/重置项目密钥 | 仅能验证该项目的激活码（推荐用于单产品集成） |

### JWT Bearer Token（管理类接口）

通过 `POST /api/auth/login` 登录后获得 JWT Token（有效期 7 天）：

```
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
```

### 无需认证（软件更新类接口）

`/api/update/*` 系列接口完全公开，客户端无需任何 Key 即可调用。

### 2.1 查询功能开关

```http
GET /api/projects/features
```

**认证：** API Key（请求头 `X-API-Key`）

**说明：** 查询项目是否开启激活码和软件更新功能。接口只返回两个开关状态，外部应用根据结果自行决定后续行为。

使用项目 API Key 时，无需传递其他参数：

```bash
curl "https://www.wzl123.com/api/projects/features" \
  -H "X-API-Key: pk_xxxxxxxxxxxxxxxx"
```

使用用户 API Key 时，必须通过查询参数指定项目：

```bash
curl "https://www.wzl123.com/api/projects/features?project_key=my-app" \
  -H "X-API-Key: ak_xxxxxxxxxxxxxxxx"
```

**响应示例：**

```json
{
  "success": true,
  "message": "操作成功",
  "data": {
    "activation_enabled": true,
    "update_enabled": false
  }
}
```

| 字段 | 类型 | 说明 |
|------|------|------|
| `activation_enabled` | boolean | 是否开启激活码功能 |
| `update_enabled` | boolean | 是否开启软件更新功能 |

---

## 3. 激活码验证接口

### 3.1 验证单个激活码

```
POST /api/verify
```

**认证：** API Key（请求头 `X-API-Key`）

**说明：** 验证一个激活码是否有效，成功后自动记录使用次数与日志。

> ⚠️ 每次调用都会消耗一次使用次数。建议本地缓存验证结果，仅在首次激活或定期检查时调用。

**请求参数（JSON Body）：**

| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `code` | string | ✅ 必填 | 待验证的激活码，如 `ABCD-1234-EFGH-5678` |
| `project_name` | string | 选填 | 调用方项目名称，记录到验证日志中 |
| `project_version` | string | 选填 | 调用方软件版本，记录到验证日志中 |
| `extra_data` | object | 选填 | 自定义扩展数据，原样记录到日志 |

**响应示例（验证成功）：**

```json
{
  "success": true,
  "message": "激活码有效",
  "data": {
    "valid": true,
    "code": "ABCD-1234-EFGH-5678",
    "current_uses": 1,
    "total_uses": 10,
    "remaining_uses": 9,
    "valid_until": "2026-12-31 23:59:59",
    "metadata": null
  }
}
```

> `total_uses` 和 `remaining_uses` 为 `-1` 表示不限次数；`valid_until` 为 `null` 表示永久有效。

**响应示例（验证失败）：**

```json
{
  "success": false,
  "message": "激活码已过期",
  "data": null
}
```

**cURL 示例：**

```bash
curl -X POST https://www.wzl123.com/api/verify \
  -H "Content-Type: application/json" \
  -H "X-API-Key: ak_xxxxxxxxxxxxxxxx" \
  -d '{
    "code": "ABCD-1234-EFGH-5678",
    "project_name": "MyApp",
    "project_version": "2.1.0"
  }'
```

**Python 示例：**

```python
import requests

API_KEY  = "ak_xxxxxxxxxxxxxxxx"
BASE_URL = "https://www.wzl123.com/api"

def verify_code(code: str, version: str = "") -> dict:
    resp = requests.post(
        f"{BASE_URL}/verify",
        headers={"X-API-Key": API_KEY, "Content-Type": "application/json"},
        json={"code": code, "project_name": "MyApp", "project_version": version},
        timeout=10,
    )
    resp.raise_for_status()
    return resp.json()

result = verify_code("ABCD-1234-EFGH-5678", "2.1.0")
if result["success"] and result["data"]["valid"]:
    print("激活成功！剩余次数:", result["data"]["remaining_uses"])
else:
    print("激活失败:", result["message"])
```

**PHP 示例：**

```php
<?php
$apiKey = 'ak_xxxxxxxxxxxxxxxx';
$code   = 'ABCD-1234-EFGH-5678';

$ch = curl_init('https://www.wzl123.com/api/verify');
curl_setopt_array($ch, [
    CURLOPT_POST           => true,
    CURLOPT_POSTFIELDS     => json_encode([
        'code'            => $code,
        'project_name'    => 'MyApp',
        'project_version' => '2.1.0',
    ]),
    CURLOPT_HTTPHEADER     => [
        'Content-Type: application/json',
        'X-API-Key: ' . $apiKey,
    ],
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT        => 10,
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);

if ($result['success'] && $result['data']['valid']) {
    echo '激活码有效，到期时间：' . ($result['data']['valid_until'] ?? '永久');
} else {
    echo '验证失败：' . $result['message'];
}
```

**C# 示例：**

```csharp
using System.Net.Http.Json;

var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-API-Key", "ak_xxxxxxxxxxxxxxxx");

var payload = new {
    code            = "ABCD-1234-EFGH-5678",
    project_name    = "MyApp",
    project_version = "2.1.0"
};
var resp   = await client.PostAsJsonAsync("https://www.wzl123.com/api/verify", payload);
var result = await resp.Content.ReadFromJsonAsync<JsonElement>();

if (result.GetProperty("success").GetBoolean()) {
    Console.WriteLine("激活成功！");
} else {
    Console.WriteLine("失败：" + result.GetProperty("message").GetString());
}
```

---

### 3.2 批量验证激活码

```
POST /api/verify/batch
```

**认证：** API Key（请求头 `X-API-Key`）

**说明：** 一次性查询多个激活码的有效性，**仅做状态查询，不消耗使用次数、不记录日志**，适合后台批量核查。

**请求参数（JSON Body）：**

| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `codes` | string[] | ✅ 必填 | 激活码数组，最多 100 个 |

**响应示例：**

```json
{
  "success": true,
  "data": [
    { "code": "AAAA-1111-BBBB-2222", "valid": true,  "status": "active",  "valid_until": "2026-12-31 23:59:59" },
    { "code": "CCCC-3333-DDDD-4444", "valid": false, "error": "激活码已过期" },
    { "code": "EEEE-5555-FFFF-6666", "valid": false, "error": "激活码不存在或不属于该项目" }
  ]
}
```

**cURL 示例：**

```bash
curl -X POST https://www.wzl123.com/api/verify/batch \
  -H "Content-Type: application/json" \
  -H "X-API-Key: ak_xxxxxxxxxxxxxxxx" \
  -d '{"codes": ["AAAA-1111-BBBB-2222", "CCCC-3333-DDDD-4444"]}'
```

**Python 示例：**

```python
codes  = ["AAAA-1111-BBBB-2222", "CCCC-3333-DDDD-4444"]
result = requests.post(
    f"{BASE_URL}/verify/batch",
    headers={"X-API-Key": API_KEY},
    json={"codes": codes},
).json()

for item in result["data"]:
    status = "有效" if item["valid"] else f"无效（{item.get('error','')}）"
    print(f"  {item['code']}: {status}")
```

---

### 3.3 查询激活码详情

```
GET /api/verify/info?code={code}
```

**认证：** API Key（请求头 `X-API-Key`）

**说明：** 查询单个激活码的详细状态，不影响使用次数。

**URL 查询参数：**

| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `code` | string | ✅ 必填 | 要查询的激活码 |

**响应示例：**

```json
{
  "success": true,
  "data": {
    "code": "ABCD-1234-EFGH-5678",
    "status": "active",
    "current_uses": 3,
    "max_uses": 10,
    "valid_from": "2025-01-01 00:00:00",
    "valid_until": "2026-12-31 23:59:59",
    "last_used_at": "2026-05-20 14:32:01",
    "metadata": null
  }
}
```

> `status` 取值：`active`（有效）、`used`（已用完）、`expired`（已过期）、`disabled`（已禁用）  
> `max_uses` 为 `0` 表示不限次数

**cURL 示例：**

```bash
curl "https://www.wzl123.com/api/verify/info?code=ABCD-1234-EFGH-5678" \
  -H "X-API-Key: ak_xxxxxxxxxxxxxxxx"
```

---

## 4. 软件自动更新接口

> 以下接口**无需任何认证**，直接集成到客户端程序中即可。  
> `project_key` 在后台「软件更新」页面可查看；若只有一个软件项目，可省略（自动使用默认项目）。

### 4.1 检查是否有新版本

```
GET /api/update/check?project_key={key}&current_version={version}
```

**认证：** 无需认证

**说明：** 客户端启动时调用，传入当前版本号，服务端返回是否有新版本及下载地址。

**URL 查询参数：**

| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `current_version` | string | ✅ 必填 | 当前安装的版本号，格式 `x.y.z`，如 `1.2.0` |
| `project_key` | string | 选填 | 软件项目标识；省略时使用后台设置的默认项目 |

**响应示例（有新版本）：**

```json
{
  "success": true,
  "data": {
    "has_update": true,
    "current_version": "1.2.0",
    "latest_version": "1.3.0",
    "is_force_update": false,
    "changelog": "修复了若干 Bug，新增批量导出功能",
    "file_size": 15728640,
    "md5": "d41d8cd98f00b204e9800998ecf8427e",
    "download_url": "/api/update/download?project_key=my-app&version=1.3.0"
  }
}
```

> `is_force_update` 为 `true` 时，客户端应强制升级后才能继续使用。  
> `file_size` 单位为字节，`0` 表示外部链接类型。  
> `download_url` 为相对路径时需拼接域名 `https://www.wzl123.com`。

**响应示例（已是最新版）：**

```json
{
  "success": true,
  "data": {
    "has_update": false,
    "current_version": "1.3.0",
    "latest_version": "1.3.0"
  }
}
```

**cURL 示例：**

```bash
curl "https://www.wzl123.com/api/update/check?project_key=my-app&current_version=1.2.0"
```

**Python 示例：**

```python
import requests, webbrowser

CURRENT_VERSION = "1.2.0"
PROJECT_KEY     = "my-app"

resp = requests.get(
    "https://www.wzl123.com/api/update/check",
    params={"project_key": PROJECT_KEY, "current_version": CURRENT_VERSION},
    timeout=8,
).json()

if resp["success"] and resp["data"]["has_update"]:
    d = resp["data"]
    print(f"发现新版本 {d['latest_version']}！")
    print(f"更新日志：{d['changelog']}")
    if d["is_force_update"]:
        print("强制更新，必须升级后才能继续使用")
    download_url = "https://www.wzl123.com" + d["download_url"]
    webbrowser.open(download_url)
else:
    print("已是最新版本")
```

**C# 示例：**

```csharp
using System.Net.Http.Json;
using System.Diagnostics;

const string CURRENT_VERSION = "1.2.0";
const string PROJECT_KEY     = "my-app";

var client = new HttpClient();
var url    = $"https://www.wzl123.com/api/update/check?project_key={PROJECT_KEY}&current_version={CURRENT_VERSION}";
var result = await client.GetFromJsonAsync<JsonElement>(url);

var data = result.GetProperty("data");
if (data.GetProperty("has_update").GetBoolean()) {
    string latest    = data.GetProperty("latest_version").GetString()!;
    string changelog = data.GetProperty("changelog").GetString()!;
    string dlUrl     = "https://www.wzl123.com" + data.GetProperty("download_url").GetString()!;
    bool   force     = data.GetProperty("is_force_update").GetBoolean();

    Console.WriteLine($"发现新版本 {latest}，更新日志：{changelog}");
    Process.Start(new ProcessStartInfo(dlUrl) { UseShellExecute = true });
}
```

---

### 4.2 获取软件项目列表

```
GET /api/update/projects
```

**认证：** 无需认证

**说明：** 返回平台上所有公开软件项目的基本信息，适用于多产品场景下的项目选择。

**响应示例：**

```json
{
  "success": true,
  "data": [
    {
      "project_key": "my-app",
      "name": "我的应用",
      "description": "一款好用的工具",
      "current_version": "1.3.0",
      "total_downloads": 1024,
      "version_count": 5
    }
  ]
}
```

**cURL 示例：**

```bash
curl "https://www.wzl123.com/api/update/projects"
```

---

### 4.3 下载安装包

```
GET /api/update/download?project_key={key}&version={version}
```

**认证：** 无需认证

**说明：**
- 本地文件类型：直接流式输出文件内容，响应头包含 `Content-Disposition`
- 外部链接类型：返回 HTTP 302 重定向到外部 URL
- 每次成功请求自动累计下载计数

> 通常直接将 `/api/update/check` 返回的 `download_url` 用浏览器打开或交给下载器即可，无需手动拼接此接口。

**URL 查询参数：**

| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `project_key` | string | ✅ 必填 | 项目标识 |
| `version` | string | ✅ 必填 | 版本号，如 `1.3.0` |

**Python 下载示例（带 MD5 校验）：**

```python
import requests, hashlib, os

def download_update(project_key, version, save_path, expected_md5=None):
    url = "https://www.wzl123.com/api/update/download"
    with requests.get(url, params={"project_key": project_key, "version": version},
                      stream=True, timeout=60) as r:
        r.raise_for_status()
        with open(save_path, "wb") as f:
            for chunk in r.iter_content(chunk_size=8192):
                f.write(chunk)

    if expected_md5:
        md5 = hashlib.md5(open(save_path, "rb").read()).hexdigest()
        if md5 != expected_md5:
            os.remove(save_path)
            raise ValueError(f"MD5 校验失败：期望 {expected_md5}，实际 {md5}")
    return save_path

download_update("my-app", "1.3.0", "setup_1.3.0.exe",
                expected_md5="d41d8cd98f00b204e9800998ecf8427e")
```

**C# 下载示例（带 MD5 校验）：**

```csharp
using System.Security.Cryptography;

async Task DownloadUpdate(string projectKey, string version,
                          string savePath, string? expectedMd5 = null)
{
    var client = new HttpClient();
    var url    = $"https://www.wzl123.com/api/update/download?project_key={projectKey}&version={version}";
    var bytes  = await client.GetByteArrayAsync(url);
    await File.WriteAllBytesAsync(savePath, bytes);

    if (expectedMd5 != null) {
        var md5 = Convert.ToHexString(MD5.HashData(bytes)).ToLower();
        if (md5 != expectedMd5)
            throw new Exception($"MD5 校验失败：期望 {expectedMd5}，实际 {md5}");
    }
}

await DownloadUpdate("my-app", "1.3.0", "setup_1.3.0.exe",
                     "d41d8cd98f00b204e9800998ecf8427e");
```

---

## 5. 错误码说明

| HTTP 状态码 | success | 常见场景 |
|-------------|---------|----------|
| `200` | `true` | 请求成功 |
| `200` | `false` | 业务逻辑失败（如激活码无效、已过期），`message` 含错误描述 |
| `400` | `false` | 参数错误（缺少必填字段、格式不正确） |
| `401` | `false` | 未提供认证信息或 API Key / Token 无效 |
| `404` | `false` | 资源不存在（激活码、项目、版本不存在） |
| `405` | `false` | HTTP 方法不匹配（如用 GET 调用 POST 接口） |
| `500` | `false` | 服务器内部错误，请联系管理员 |

> **安全建议**：请勿将 API Key 硬编码在前端 JS 或公开仓库中；建议通过后端服务中转验证请求，或使用「项目 API Key」限制权限范围。

---

*文档由 多项目综合管理平台 自动生成 · https://www.wzl123.com*
