Files
wushu/ht/p1/API文档.md
2026-03-30 02:35:31 +08:00

504 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 古诗文答题系统 API 文档
## 文件信息
- **文件路径**: `p1/api.php`
- **功能描述**: 古诗文题目获取与答题系统
- **编码格式**: UTF-8
- **响应格式**: JSON
## 安全认证
### Key 认证机制
API 采用动态 Key 认证,防止接口被滥用。
#### 认证原理
1. **客户端生成 Key**: 使用 `SHA256(时间戳 + 密钥)` 生成
2. **服务端验证**: 验证 Key 是否在有效期内(默认 300 秒)
3. **Key 更新**: 每次成功请求后,服务端返回新的 `new_key`
#### 密钥配置
- **前端**: `api.js` 中的 `SECRET_KEY` 变量
- **后端**: `api.php` 中的 `SECRET_KEY` 常量
- **默认密钥**: `tzgsc_2026_secret_key`
#### Key 生成算法
##### PHP
```php
<?php
define('SECRET_KEY', 'tzgsc_2026_secret_key');
function generate_key() {
$timestamp = time();
$data = $timestamp . SECRET_KEY;
return hash('sha256', $data);
}
// 使用示例
$key = generate_key();
$url = "api.php?id=1&key=" . urlencode($key);
```
##### JavaScript (Node.js)
```javascript
const crypto = require('crypto');
const SECRET_KEY = 'tzgsc_2026_secret_key';
function generateKey() {
const timestamp = Math.floor(Date.now() / 1000);
const data = timestamp + SECRET_KEY;
return crypto.createHash('sha256').update(data).digest('hex');
}
// 使用示例
const key = generateKey();
const url = `api.php?id=1&key=${encodeURIComponent(key)}`;
```
##### Python
```python
import time
import hashlib
import urllib.parse
SECRET_KEY = 'tzgsc_2026_secret_key'
def generate_key():
timestamp = int(time.time())
data = str(timestamp) + SECRET_KEY
return hashlib.sha256(data.encode()).hexdigest()
# 使用示例
key = generate_key()
url = f"api.php?id=1&key={urllib.parse.quote(key)}"
```
##### Java
```java
import java.security.MessageDigest;
import java.time.Instant;
public class KeyGenerator {
private static final String SECRET_KEY = "tzgsc_2026_secret_key";
public static String generateKey() throws Exception {
long timestamp = Instant.now().getEpochSecond();
String data = timestamp + SECRET_KEY;
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(data.getBytes("UTF-8"));
StringBuilder hexString = new StringBuilder();
for (byte b : hash) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) hexString.append('0');
hexString.append(hex);
}
return hexString.toString();
}
// 使用示例
public static void main(String[] args) throws Exception {
String key = generateKey();
String url = "api.php?id=1&key=" + java.net.URLEncoder.encode(key, "UTF-8");
System.out.println(url);
}
}
```
##### Go
```go
package main
import (
"crypto/sha256"
"encoding/hex"
"fmt"
"net/url"
"time"
)
const SECRET_KEY = "tzgsc_2026_secret_key"
func generateKey() string {
timestamp := time.Now().Unix()
data := fmt.Sprintf("%d%s", timestamp, SECRET_KEY)
hash := sha256.Sum256([]byte(data))
return hex.EncodeToString(hash[:])
}
// 使用示例
func main() {
key := generateKey()
encodedKey := url.QueryEscape(key)
apiUrl := fmt.Sprintf("api.php?id=1&key=%s", encodedKey)
fmt.Println(apiUrl)
}
```
##### C#
```csharp
using System;
using System.Security.Cryptography;
using System.Text;
using System.Web;
public class KeyGenerator
{
private const string SECRET_KEY = "tzgsc_2026_secret_key";
public static string GenerateKey()
{
long timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
string data = timestamp + SECRET_KEY;
using (SHA256 sha256 = SHA256.Create())
{
byte[] hash = sha256.ComputeHash(Encoding.UTF8.GetBytes(data));
StringBuilder builder = new StringBuilder();
foreach (byte b in hash)
{
builder.Append(b.ToString("x2"));
}
return builder.ToString();
}
}
// 使用示例
public static void Main()
{
string key = GenerateKey();
string url = $"api.php?id=1&key={HttpUtility.UrlEncode(key)}";
Console.WriteLine(url);
}
}
```
##### Ruby
```ruby
require 'digest'
require 'cgi'
SECRET_KEY = 'tzgsc_2026_secret_key'
def generate_key
timestamp = Time.now.to_i
data = "#{timestamp}#{SECRET_KEY}"
Digest::SHA256.hexdigest(data)
end
# 使用示例
key = generate_key
url = "api.php?id=1&key=#{CGI.escape(key)}"
puts url
```
##### Rust
```rust
use sha2::{Sha256, Digest};
use std::time::{SystemTime, UNIX_EPOCH};
const SECRET_KEY: &str = "tzgsc_2026_secret_key";
fn generate_key() -> String {
let timestamp = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs();
let data = format!("{}{}", timestamp, SECRET_KEY);
let mut hasher = Sha256::new();
hasher.update(data.as_bytes());
let result = hasher.finalize();
hex::encode(result)
}
// 使用示例
fn main() {
let key = generate_key();
let encoded_key = urlencoding::encode(&key);
let url = format!("api.php?id=1&key={}", encoded_key);
println!("{}", url);
}
```
### 请求限制
| 类型 | 限制 | 说明 |
|------|------|------|
| 无 Key 请求 | 10 次/分钟 | 基于 IP 地址限制 |
| 有 Key 请求 | 无限制 | 验证通过后无频率限制 |
#### 限制配置
```php
define('KEY_EXPIRE_TIME', 300); // Key 有效期(秒)
define('MAX_REQUESTS_NO_KEY', 10); // 无 Key 最大请求次数
define('RATE_LIMIT_WINDOW', 60); // 限流时间窗口(秒)
```
## 接口信息
### 接口概述
该接口用于获取古诗文题目、验证答案、获取提示信息,支持 GET 和 POST 请求方式。
### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| id | string/int | 是 | 题目唯一标识符 |
| msg | string | 否 | 操作类型:答案序号/提示 |
| key | string | 否 | 认证密钥(推荐携带) |
### 请求示例
#### 1. 获取题目
```
GET p1/api.php?id=1&key=xxx
```
POST 请求:
```
POST p1/api.php
Content-Type: application/json
{
"id": "1",
"key": "xxx"
}
```
#### 2. 提交答案
```
GET p1/api.php?id=1&msg=1&key=xxx
```
POST 请求:
```
POST p1/api.php
Content-Type: application/json
{
"id": "1",
"msg": "1",
"key": "xxx"
}
```
#### 3. 获取提示
```
GET p1/api.php?id=1&msg=提示&key=xxx
```
POST 请求:
```
POST p1/api.php
Content-Type: application/json
{
"id": "1",
"msg": "提示",
"key": "xxx"
}
```
### 响应信息
#### 获取题目成功响应
```json
{
"success": true,
"new_key": "新的认证密钥",
"data": {
"id": "1",
"question": "题目内容",
"options": [
{
"num": 1,
"text": "选项1"
},
{
"num": 2,
"text": "选项2"
},
{
"num": 3,
"text": "选项3"
},
{
"num": 4,
"text": "选项4"
}
],
"author": "作者",
"type": "描写类型",
"grade": "学习阶段",
"dynasty": "年代"
}
}
```
#### 答案正确响应
```json
{
"success": true,
"type": "correct",
"message": "恭喜你,回答正确。请继续下一题",
"next_question": {
"id": "2",
"question": "下一题题目",
"options": [
...
],
"author": "作者",
"type": "描写类型",
"grade": "学习阶段",
"dynasty": "年代"
}
}
```
#### 答案错误响应
```json
{
"success": true,
"type": "wrong",
"message": "抱歉,答案不对哦。你可以回复提示获取该题的部分信息哦。"
}
```
#### 提示响应
```json
{
"success": true,
"type": "hint",
"message": "这是首描写[描写类型]的诗,你在[学习阶段]学过它。"
}
```
#### 获取失败响应
```json
{
"success": false,
"message": "抱歉,获取出现错误。"
}
```
#### 错误参数响应
```json
{
"success": false,
"message": "缺少必要参数: id"
}
```
#### 不支持的请求方法
```json
{
"success": false,
"message": "不支持的请求方法"
}
```
#### 请求频率限制
```json
{
"success": false,
"message": "请求过于频繁,请稍后再试"
}
```
HTTP 状态码: 429
## 核心功能说明
### 1. 题目数据来源
- 从百度汉语 API 获取题目数据
- API 地址: `https://hanyu.baidu.com/hanyu/ajax/pingce_data`
### 2. 数据存储
- 题目数据存储在 `data/tzgsc/` 目录
- 文件格式: `.json`
- 文件命名: `{id}.json`
- JSON 缓存: `data/tzgsc.json`
### 3. 数据格式
题目数据存储格式 (JSON):
```json
{
"id": "1",
"question": "题目内容",
"options": [
{"num": 1, "text": "选项1"},
{"num": 2, "text": "选项2"},
{"num": 3, "text": "选项3"},
{"num": 4, "text": "选项4"}
],
"author": "作者",
"type": "描写类型",
"grade": "学习阶段",
"dynasty": "年代",
"correct_answer": 1
}
```
### 4. 核心函数
#### get_question($id)
获取并返回题目
- **参数**: $id - 题目 ID
- **返回**: 包含题目信息的数组
#### submit_answer($id, $msg)
提交答案并返回结果
- **参数**:
- $id - 题目 ID
- $msg - 答案序号或"提示"
- **返回**: 包含答题结果的数组
#### cj($data)
采集并缓存题目数据
- **参数**: $data - API 返回的原始数据
- **功能**: 解析题目数据,去重后保存到 JSON 文件
#### get_curl($url, $post, $referer, $cookie, $header, $ua, $nobaody)
发送 HTTP 请求
- **参数**:
- $url - 请求地址
- $post - POST 数据
- $referer - 来源地址
- $cookie - Cookie
- $header - 是否返回头部
- $ua - User-Agent
- $nobaody - 是否不返回内容
- **功能**: 使用 cURL 发送 HTTP 请求,支持 SSL 和 GZIP 压缩
#### replace_unicode_escape_sequence($match)
Unicode 转义序列转换
- **参数**: $match - 匹配到的 Unicode 序列
- **返回**: UTF-8 编码的字符串
- **功能**: 将 Unicode 转义序列转换为 UTF-8 字符
## 目录结构
```
p1/
├── api.php # API 接口文件
├── index.php # 前端页面(服务端生成初始 Key
├── api.js # 前端交互脚本
└── data/
├── tzgsc.json # 题目缓存文件
├── rate_limit/ # 请求限制记录目录
└── tzgsc/
└── [id].json # 题目数据文件
```
## 注意事项
1. 确保 `data/tzgsc/` 目录具有写入权限
2. API 依赖百度汉语接口,需确保网络连接正常
3. 题目数据会缓存到本地,避免重复请求
4. 答案验证时会读取本地保存的题目数据文件
5. 支持 Unicode 编码处理和 GZIP 压缩响应
6. 支持 GET 和 POST 两种请求方式
7. 所有响应均为 JSON 格式