# C 语言接入指南

适用：ESP-IDF 5.x 与 Linux C / C++。接口与字段说明见 [API 参考](/developers/)。以下均为占位配置片段。

## 接入流程

1. 使用所属系统的 API Key、HTTPS 客户端和 JSON 解析库，联网后校准时间。
2. 将稳定设备码写入持久存储，登记设备并轮询绑定状态。
3. 获取 `data.access_token`，调用本周作业接口，按 `tracks[].url` 下载。
4. 播放后保存听音事件到本地队列，联网批量同步。

设备 Token 与网页账号会话独立；网页切换登录不要求固件重新获取 Token。

可先在后台 [在线测试](/admin/apipost-simulator) 点击“准备测试设备”，查询设备状态并获取作业，再接入固件。

## HTTPS 客户端

### ESP-IDF

使用 `esp_http_client`、`cJSON` 和可信根证书包；请求只指向受控配置的 API 基址。以下片段应放入已初始化网络的固件中，`api_key` 从设备安全存储读取。

```c
#include "esp_http_client.h"
#include "esp_crt_bundle.h"

esp_http_client_config_t config = {
    .url = "https://saas.example.com/api/terminal/listening-rules",
    .method = HTTP_METHOD_GET,
    .crt_bundle_attach = esp_crt_bundle_attach,
    .disable_auto_redirect = true,
    .timeout_ms = 20000,
};
esp_http_client_handle_t client = esp_http_client_init(&config);
if (client) {
    esp_http_client_set_header(client, "X-API-Key", api_key);
    esp_err_t result = esp_http_client_perform(client);
    int status = esp_http_client_get_status_code(client);
    // 先检查 result 与 status；按需添加事件处理器收集并解析 JSON。
    esp_http_client_cleanup(client);
}
```

使用 `.event_handler` 处理响应分片并限制缓冲区大小。解析 JSON 前检查响应完整性；网络错误不代表服务端未处理请求。

### Linux / libcurl

新建独立句柄，设置受控的 HTTPS API URL 和本次所需的单一鉴权头；保留证书校验并关闭自动重定向：

```c
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 0L);
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 8L);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 20L);
```

3xx 响应先核对基址，不把鉴权头转发到其他主机。参考 [ESP-IDF HTTP Client](https://docs.espressif.com/projects/esp-idf/en/v5.3/esp32/api-reference/protocols/esp_http_client.html) 与 [libcurl 重定向说明](https://curl.se/libcurl/c/CURLOPT_FOLLOWLOCATION.html)。

## 下载与同步

| 场景 | 实现要求 |
| --- | --- |
| 音频下载 | 使用独立 HTTPS 客户端，不携带 API Key、Bearer Token 或 Cookie；流式写入临时文件，校验后原子重命名 |
| 断点续传 | 发送 `Range: bytes={offset}-`；仅在 `206` 且 `Content-Range` 偏移匹配时追加，`200` 时从头覆盖 |
| URL 失效 | 音频返回 `403` 时重新获取作业中的 URL，不修改或拼接签名参数 |
| Token 失效 | Bearer 接口返回 `401` 时重新查询绑定状态，清除旧 Token |
| 离线补传 | 持久化事件和幂等键，确认成功或重复后再出队；重试不得换键 |
| 凭证与日志 | Key/Token 使用加密存储，解绑后清除；日志只记录状态码、错误码、Request ID，不记录凭证、个人数据或签名 URL |

上线前验证完整收听链路、断网恢复、重复上报不重复计数，以及下载文件校验。有效听音时长、作业完成度与资源权限均以服务端结果为准。

服务端的数据压缩与回执切换不改变 C 终端协议。客户端继续以原幂等键重试，并以接口响应判断是否成功或重复，不得假设服务端会永久保留原始上报明细。
