Initial commit: Flutter 无书应用项目

This commit is contained in:
Developer
2026-03-30 02:35:31 +08:00
commit 9175ff9905
566 changed files with 103261 additions and 0 deletions

503
ht/p1/API文档.md Normal file
View File

@@ -0,0 +1,503 @@
# 古诗文答题系统 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 格式