354 lines
9.4 KiB
PHP
354 lines
9.4 KiB
PHP
<?php
|
|
header("Content-Type:application/json;charset=UTF-8");
|
|
session_start();
|
|
|
|
define('CACHE_DIR', __DIR__ . '/data/');
|
|
define('CACHE_FILE', CACHE_DIR . 'questions.json');
|
|
define('API_LOCK_FILE', CACHE_DIR . 'api.lock');
|
|
define('API_INTERVAL', 5);
|
|
|
|
static $cacheData = null;
|
|
|
|
$result = ['code' => 0, 'msg' => '', 'data' => null];
|
|
|
|
$action = $_REQUEST['action'] ?? 'question';
|
|
$id = isset($_REQUEST['id']) ? intval($_REQUEST['id']) : null;
|
|
|
|
switch($action){
|
|
case 'question':
|
|
$result['data'] = getQuestion($id ?? getCurrentId());
|
|
break;
|
|
case 'next':
|
|
$result['data'] = getNextQuestionAuto();
|
|
break;
|
|
case 'fetch':
|
|
$result['data'] = fetchNewQuestion();
|
|
break;
|
|
case 'answer':
|
|
$result['data'] = checkAnswer($id, $_REQUEST['answer'] ?? '');
|
|
break;
|
|
case 'hint':
|
|
$result['data'] = getHint($id);
|
|
break;
|
|
case 'list':
|
|
$result['data'] = getQuestionList();
|
|
break;
|
|
case 'refresh':
|
|
$result['data'] = refreshCache();
|
|
break;
|
|
case 'stats':
|
|
$result['data'] = getStats();
|
|
break;
|
|
default:
|
|
$result['code'] = 400;
|
|
$result['msg'] = '未知操作';
|
|
}
|
|
|
|
echo json_encode($result, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
|
|
|
function getStats(){
|
|
global $cacheData;
|
|
$cache = loadCache();
|
|
return [
|
|
'total_questions' => count($cache),
|
|
'cache_file_size' => file_exists(CACHE_FILE) ? round(filesize(CACHE_FILE) / 1024, 2) . ' KB' : 0,
|
|
'last_updated' => file_exists(CACHE_FILE) ? date('Y-m-d H:i:s', filemtime(CACHE_FILE)) : 'N/A',
|
|
'memory_usage' => round(memory_get_peak_usage(true) / 1024, 2) . ' KB'
|
|
];
|
|
}
|
|
|
|
function getQuestion($id){
|
|
$cache = loadCache();
|
|
if(empty($cache)){
|
|
return ['error' => '没有题目数据'];
|
|
}
|
|
if($id < 0 || $id >= count($cache)){
|
|
return ['error' => '题目不存在', 'total' => count($cache)];
|
|
}
|
|
$item = $cache[$id];
|
|
return [
|
|
'id' => $id,
|
|
'total' => count($cache),
|
|
'question' => $item['question_content'],
|
|
'author' => $item['type']['person'] ?? '',
|
|
'type' => $item['type']['type'] ?? '',
|
|
'grade' => $item['type']['grade'] ?? '',
|
|
'dynasty' => $item['type']['dynasty'] ?? '',
|
|
'options' => formatOptions($item['option_answers'] ?? [])
|
|
];
|
|
}
|
|
|
|
function getNextQuestionAuto(){
|
|
$cache = loadCache();
|
|
if(empty($cache)){
|
|
return ['error' => '没有题目数据'];
|
|
}
|
|
$total = count($cache);
|
|
$currentId = getCurrentId();
|
|
$nextId = $currentId + 1;
|
|
if($nextId >= $total){
|
|
$nextId = 0;
|
|
}
|
|
setCurrentId($nextId);
|
|
$result = getQuestion($nextId);
|
|
$result['prev_id'] = $currentId;
|
|
return $result;
|
|
}
|
|
|
|
function getCurrentId(){
|
|
return isset($_SESSION['current_question_id']) ? intval($_SESSION['current_question_id']) : 0;
|
|
}
|
|
|
|
function setCurrentId($id){
|
|
$_SESSION['current_question_id'] = $id;
|
|
}
|
|
|
|
function fetchNewQuestion(){
|
|
$apiData = fetchFromBaiduApi();
|
|
|
|
if($apiData === null){
|
|
$cache = loadCache();
|
|
if(!empty($cache)){
|
|
$randomId = array_rand($cache);
|
|
$result = formatQuestionItem($randomId, $cache[$randomId], count($cache));
|
|
$result['from_cache'] = true;
|
|
$result['api_status'] = 'failed';
|
|
return $result;
|
|
}
|
|
return ['error' => 'API请求失败且无本地缓存'];
|
|
}
|
|
|
|
$cache = loadCache();
|
|
$newCount = mergeToCache($apiData, $cache);
|
|
|
|
$randomItem = $apiData[array_rand($apiData)];
|
|
$result = formatQuestionItem(null, $randomItem, count($cache));
|
|
$result['from_cache'] = false;
|
|
$result['new_questions'] = $newCount;
|
|
$result['api_status'] = 'success';
|
|
|
|
return $result;
|
|
}
|
|
|
|
function formatQuestionItem($id, $item, $total, $fromCache = false, $newCount = 0){
|
|
return [
|
|
'id' => $id,
|
|
'total' => $total,
|
|
'question' => $item['question_content'],
|
|
'author' => $item['type']['person'] ?? '',
|
|
'type' => $item['type']['type'] ?? '',
|
|
'grade' => $item['type']['grade'] ?? '',
|
|
'dynasty' => $item['type']['dynasty'] ?? '',
|
|
'options' => formatOptions($item['option_answers'] ?? []),
|
|
'from_cache' => $fromCache,
|
|
'new_questions' => $newCount
|
|
];
|
|
}
|
|
|
|
function checkAnswer($id, $answer){
|
|
$cache = loadCache();
|
|
if(empty($cache)){
|
|
return ['error' => '没有题目数据'];
|
|
}
|
|
if($id < 0 || $id >= count($cache)){
|
|
return ['error' => '题目不存在'];
|
|
}
|
|
$item = $cache[$id];
|
|
$correctAnswer = findAnswer($item['option_answers'] ?? []);
|
|
$isCorrect = ($answer === $correctAnswer);
|
|
|
|
return [
|
|
'id' => $id,
|
|
'correct' => $isCorrect,
|
|
'your_answer' => $answer,
|
|
'correct_answer' => $correctAnswer,
|
|
'next_id' => $id + 1,
|
|
'has_next' => $id + 1 < count($cache)
|
|
];
|
|
}
|
|
|
|
function getHint($id){
|
|
$cache = loadCache();
|
|
if(empty($cache)){
|
|
return ['error' => '没有题目数据'];
|
|
}
|
|
if($id < 0 || $id >= count($cache)){
|
|
return ['error' => '题目不存在'];
|
|
}
|
|
$item = $cache[$id];
|
|
return [
|
|
'id' => $id,
|
|
'hint' => '这是首描写' . ($item['type']['type'] ?? '') . '的诗,你在' . ($item['type']['grade'] ?? '') . '学过它。',
|
|
'author' => $item['type']['person'] ?? '',
|
|
'dynasty' => $item['type']['dynasty'] ?? ''
|
|
];
|
|
}
|
|
|
|
function getQuestionList(){
|
|
$cache = loadCache();
|
|
if(empty($cache)){
|
|
return ['total' => 0, 'list' => []];
|
|
}
|
|
$list = [];
|
|
foreach($cache as $i => $item){
|
|
$list[] = [
|
|
'id' => $i,
|
|
'question' => mb_substr($item['question_content'], 0, 30, 'UTF-8') . '...',
|
|
'author' => $item['type']['person'] ?? '',
|
|
'dynasty' => $item['type']['dynasty'] ?? ''
|
|
];
|
|
}
|
|
return ['total' => count($list), 'list' => $list];
|
|
}
|
|
|
|
function refreshCache(){
|
|
$apiData = fetchFromBaiduApi();
|
|
if($apiData === null){
|
|
return ['refreshed' => false, 'error' => 'API请求失败'];
|
|
}
|
|
$cache = loadCache();
|
|
$newCount = mergeToCache($apiData, $cache);
|
|
return ['refreshed' => true, 'total' => count($cache), 'new_questions' => $newCount];
|
|
}
|
|
|
|
function loadCache(){
|
|
global $cacheData;
|
|
|
|
if($cacheData !== null){
|
|
return $cacheData;
|
|
}
|
|
|
|
if(!file_exists(CACHE_FILE)){
|
|
$cacheData = [];
|
|
return [];
|
|
}
|
|
|
|
$content = file_get_contents(CACHE_FILE);
|
|
if($content === false){
|
|
$cacheData = [];
|
|
return [];
|
|
}
|
|
|
|
$data = json_decode($content, true);
|
|
if(!is_array($data) || !isset($data['questions'])){
|
|
$cacheData = [];
|
|
return [];
|
|
}
|
|
|
|
$cacheData = $data['questions'];
|
|
return $cacheData;
|
|
}
|
|
|
|
function saveCache($questions){
|
|
global $cacheData;
|
|
|
|
if(!is_dir(CACHE_DIR)){
|
|
mkdir(CACHE_DIR, 0755, true);
|
|
}
|
|
|
|
$data = [
|
|
'updated' => date('Y-m-d H:i:s'),
|
|
'count' => count($questions),
|
|
'questions' => $questions
|
|
];
|
|
|
|
$tempFile = CACHE_FILE . '.tmp';
|
|
$result = file_put_contents($tempFile, json_encode($data, JSON_UNESCAPED_UNICODE), LOCK_EX);
|
|
|
|
if($result !== false){
|
|
rename($tempFile, CACHE_FILE);
|
|
}
|
|
|
|
$cacheData = $questions;
|
|
}
|
|
|
|
function fetchFromBaiduApi(){
|
|
if(file_exists(API_LOCK_FILE)){
|
|
$lockTime = intval(file_get_contents(API_LOCK_FILE));
|
|
if(time() - $lockTime < API_INTERVAL){
|
|
return null;
|
|
}
|
|
}
|
|
|
|
file_put_contents(API_LOCK_FILE, time());
|
|
|
|
$url = 'https://hanyu.baidu.com/hanyu/ajax/pingce_data';
|
|
$ch = curl_init();
|
|
curl_setopt_array($ch, [
|
|
CURLOPT_URL => $url,
|
|
CURLOPT_RETURNTRANSFER => true,
|
|
CURLOPT_SSL_VERIFYPEER => false,
|
|
CURLOPT_SSL_VERIFYHOST => false,
|
|
CURLOPT_TIMEOUT => 5,
|
|
CURLOPT_CONNECTTIMEOUT => 3,
|
|
CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
|
|
CURLOPT_HTTPHEADER => [
|
|
'Accept: application/json',
|
|
'Accept-Language: zh-CN,zh;q=0.9'
|
|
]
|
|
]);
|
|
$response = curl_exec($ch);
|
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
curl_close($ch);
|
|
|
|
if($httpCode !== 200 || empty($response)){
|
|
return null;
|
|
}
|
|
|
|
$json = json_decode($response, true);
|
|
if(!isset($json['data']) || !is_array($json['data'])){
|
|
return null;
|
|
}
|
|
|
|
return $json['data'];
|
|
}
|
|
|
|
function mergeToCache($newData, &$existingCache){
|
|
if(empty($newData)){
|
|
return 0;
|
|
}
|
|
|
|
$existingKeys = [];
|
|
foreach($existingCache as $item){
|
|
$key = $item['question_content'] ?? '';
|
|
$existingKeys[$key] = true;
|
|
}
|
|
|
|
$newCount = 0;
|
|
foreach($newData as $item){
|
|
$key = $item['question_content'] ?? '';
|
|
if(!isset($existingKeys[$key])){
|
|
$existingCache[] = $item;
|
|
$existingKeys[$key] = true;
|
|
$newCount++;
|
|
}
|
|
}
|
|
|
|
if($newCount > 0){
|
|
saveCache($existingCache);
|
|
}
|
|
|
|
return $newCount;
|
|
}
|
|
|
|
function formatOptions($options){
|
|
$result = [];
|
|
foreach($options as $i => $opt){
|
|
$result[] = [
|
|
'index' => $i + 1,
|
|
'content' => $opt['answer_content'] ?? ''
|
|
];
|
|
}
|
|
return $result;
|
|
}
|
|
|
|
function findAnswer($options){
|
|
foreach($options as $i => $opt){
|
|
if(($opt['is_standard_answer'] ?? 0) === 1){
|
|
return (string)($i + 1);
|
|
}
|
|
}
|
|
return '';
|
|
}
|
|
?>
|