更多 页面 重构

This commit is contained in:
Developer
2026-04-12 01:07:47 +08:00
parent 442f648128
commit 7b90983bb9
16 changed files with 2246 additions and 988 deletions

View File

@@ -1,304 +0,0 @@
<?php
/**
* 🚀 API缓存系统
*
* 支持文件缓存,自动清理过期缓存
* 优化多人多次请求性能
*/
class ApiCache {
private static $cacheDir = null;
private static $defaultTTL = 300;
private static $ttlConfig = array(
'list' => 300,
'detail' => 600,
'full' => 600,
'ingredients' => 600,
'ingredient_detail' => 1200,
'search' => 180,
'categories' => 1800,
'tags' => 1800,
'stats' => 120,
'stats_full' => 120,
'hot' => 300,
'query' => 180,
'filter' => 180,
'like' => 0,
'recommend' => 0,
'view' => 0
);
public static function init() {
if (self::$cacheDir === null) {
self::$cacheDir = dirname(__FILE__) . '/cache/';
if (!is_dir(self::$cacheDir)) {
@mkdir(self::$cacheDir, 0755, true);
}
}
}
public static function getTTL($act) {
return isset(self::$ttlConfig[$act]) ? self::$ttlConfig[$act] : self::$defaultTTL;
}
public static function getCacheKey($act, $params = array()) {
ksort($params);
$paramStr = http_build_query($params);
return md5($act . '_' . $paramStr);
}
public static function get($act, $params = array()) {
self::init();
$ttl = isset(self::$ttlConfig[$act]) ? self::$ttlConfig[$act] : self::$defaultTTL;
if ($ttl <= 0) {
return null;
}
$key = self::getCacheKey($act, $params);
$file = self::$cacheDir . $key . '.json';
if (!file_exists($file)) {
return null;
}
$content = file_get_contents($file);
$data = json_decode($content, true);
if (!$data || !isset($data['expire']) || !isset($data['data'])) {
@unlink($file);
return null;
}
if (time() > $data['expire']) {
@unlink($file);
return null;
}
return $data['data'];
}
public static function getStale($act, $params = array()) {
self::init();
$key = self::getCacheKey($act, $params);
$file = self::$cacheDir . $key . '.json';
if (!file_exists($file)) {
return null;
}
$content = @file_get_contents($file);
if ($content === false) {
return null;
}
$data = json_decode($content, true);
if (!$data || !isset($data['data'])) {
return null;
}
return $data['data'];
}
public static function isExpired($act, $params = array()) {
self::init();
$key = self::getCacheKey($act, $params);
$file = self::$cacheDir . $key . '.json';
if (!file_exists($file)) {
return true;
}
$content = @file_get_contents($file);
if ($content === false) {
return true;
}
$data = json_decode($content, true);
if (!$data || !isset($data['expire'])) {
return true;
}
return time() > $data['expire'];
}
public static function getCacheAge($act, $params = array()) {
self::init();
$key = self::getCacheKey($act, $params);
$file = self::$cacheDir . $key . '.json';
if (!file_exists($file)) {
return -1;
}
$content = @file_get_contents($file);
if ($content === false) {
return -1;
}
$data = json_decode($content, true);
if (!$data || !isset($data['created'])) {
return -1;
}
return time() - $data['created'];
}
public static function set($act, $params, $data) {
self::init();
$ttl = isset(self::$ttlConfig[$act]) ? self::$ttlConfig[$act] : self::$defaultTTL;
if ($ttl <= 0) {
return false;
}
$key = self::getCacheKey($act, $params);
$file = self::$cacheDir . $key . '.json';
$cacheData = array(
'act' => $act,
'params' => $params,
'data' => $data,
'expire' => time() + $ttl,
'created' => time()
);
return file_put_contents($file, json_encode($cacheData, JSON_UNESCAPED_UNICODE)) !== false;
}
public static function clear($act = null, $params = array()) {
self::init();
if ($act === null) {
$files = glob(self::$cacheDir . '*.json');
foreach ($files as $file) {
@unlink($file);
}
return true;
}
$key = self::getCacheKey($act, $params);
$file = self::$cacheDir . $key . '.json';
if (file_exists($file)) {
@unlink($file);
}
return true;
}
public static function clearByAct($act) {
self::init();
$files = glob(self::$cacheDir . '*.json');
$count = 0;
foreach ($files as $file) {
$content = file_get_contents($file);
$data = json_decode($content, true);
if ($data && isset($data['act']) && $data['act'] === $act) {
@unlink($file);
$count++;
}
}
return $count;
}
public static function cleanExpired() {
self::init();
$files = glob(self::$cacheDir . '*.json');
$count = 0;
$now = time();
foreach ($files as $file) {
$content = @file_get_contents($file);
if ($content === false) {
@unlink($file);
$count++;
continue;
}
$data = json_decode($content, true);
if (!$data || !isset($data['expire']) || $now > $data['expire']) {
@unlink($file);
$count++;
}
}
return $count;
}
public static function getStats() {
self::init();
$files = glob(self::$cacheDir . '*.json');
$totalSize = 0;
$count = 0;
$oldest = time();
$newest = 0;
foreach ($files as $file) {
$count++;
$totalSize += filesize($file);
$content = file_get_contents($file);
$data = json_decode($content, true);
if ($data && isset($data['created'])) {
$oldest = min($oldest, $data['created']);
$newest = max($newest, $data['created']);
}
}
return array(
'count' => $count,
'total_size' => $totalSize,
'total_size_readable' => self::formatBytes($totalSize),
'oldest' => $oldest > 0 ? date('Y-m-d H:i:s', $oldest) : '-',
'newest' => $newest > 0 ? date('Y-m-d H:i:s', $newest) : '-'
);
}
private static function formatBytes($bytes) {
if ($bytes >= 1073741824) {
return number_format($bytes / 1073741824, 2) . ' GB';
} elseif ($bytes >= 1048576) {
return number_format($bytes / 1048576, 2) . ' MB';
} elseif ($bytes >= 1024) {
return number_format($bytes / 1024, 2) . ' KB';
} else {
return $bytes . ' bytes';
}
}
}
if (php_sapi_name() === 'cli' && isset($argv[0]) && basename($argv[0]) === 'cache.php') {
$action = $argv[1] ?? 'stats';
switch ($action) {
case 'clean':
$count = ApiCache::cleanExpired();
echo "Cleaned {$count} expired cache files.\n";
break;
case 'clear':
ApiCache::clear();
echo "All cache cleared.\n";
break;
case 'stats':
default:
$stats = ApiCache::getStats();
echo "Cache Statistics:\n";
echo " Files: {$stats['count']}\n";
echo " Size: {$stats['total_size_readable']}\n";
echo " Oldest: {$stats['oldest']}\n";
echo " Newest: {$stats['newest']}\n";
break;
}
}

View File

@@ -1,104 +0,0 @@
<?php
/**
* 🧹 缓存管理接口
*
* 访问方式: /api/cache_manage.php?action=xxx
*/
require '../zb_system/function/c_system_base.php';
$zbp->Load();
require_once 'cache.php';
header('Content-Type: application/json; charset=utf-8');
header('Access-Control-Allow-Origin: *');
$action = $_GET['action'] ?? 'stats';
$result = array();
switch ($action) {
case 'stats':
$result = array(
'code' => 200,
'message' => '📊 缓存统计',
'data' => ApiCache::getStats()
);
break;
case 'clean':
$count = ApiCache::cleanExpired();
$result = array(
'code' => 200,
'message' => "🧹 已清理 {$count} 个过期缓存",
'data' => array(
'cleaned' => $count,
'stats' => ApiCache::getStats()
)
);
break;
case 'clear':
$act = $_GET['act'] ?? null;
if ($act) {
$count = ApiCache::clearByAct($act);
$result = array(
'code' => 200,
'message' => "🗑️ 已清除 {$act} 相关的 {$count} 个缓存",
'data' => array(
'act' => $act,
'cleaned' => $count
)
);
} else {
ApiCache::clear();
$result = array(
'code' => 200,
'message' => '🗑️ 已清除所有缓存',
'data' => ApiCache::getStats()
);
}
break;
case 'config':
$result = array(
'code' => 200,
'message' => '⚙️ 缓存配置',
'data' => array(
'ttl_config' => array(
'list' => '180秒 (3分钟)',
'detail' => '300秒 (5分钟)',
'ingredients' => '300秒 (5分钟)',
'ingredient_detail' => '600秒 (10分钟)',
'search' => '120秒 (2分钟)',
'categories' => '600秒 (10分钟)',
'tags' => '600秒 (10分钟)',
'stats' => '60秒 (1分钟)',
'like' => '不缓存',
'recommend' => '不缓存',
'view' => '不缓存'
),
'auto_clean' => '1%概率自动清理过期缓存'
)
);
break;
default:
$result = array(
'code' => 200,
'message' => '🧹 缓存管理接口',
'data' => array(
'actions' => array(
'stats' => '查看缓存统计',
'clean' => '清理过期缓存',
'clear' => '清除所有缓存',
'clear&act=xxx' => '清除指定接口缓存',
'config' => '查看缓存配置'
)
)
);
break;
}
echo json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
exit;

View File

@@ -1,206 +0,0 @@
<?php
/**
* 🍳 数据库诊断脚本
* 访问: http://eat.wktyl.com/api/diagnose.php
*/
require '../zb_system/function/c_system_base.php';
$zbp->Load();
header('Content-Type: text/html; charset=utf-8');
function check($msg, $result, $detail = '') {
$icon = $result ? '✅' : '❌';
$color = $result ? 'green' : 'red';
echo "<div style='margin:5px 0;padding:8px;background:" . ($result ? '#e8f5e9' : '#ffebee') . ";border-radius:4px;'>";
echo "<span style='color:$color;font-size:18px;'>$icon</span> <strong>$msg</strong>";
if ($detail) echo "<br><small style='color:#666;margin-left:28px;'>$detail</small>";
echo "</div>";
}
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>🍳 数据库诊断</title>
<style>
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; max-width: 900px; margin: 40px auto; padding: 20px; background: #f5f5f7; }
.card { background: #fff; border-radius: 12px; padding: 24px; margin-bottom: 20px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); }
h1 { color: #1d1d1f; margin-bottom: 8px; }
h2 { color: #0071e3; border-bottom: 2px solid #0071e3; padding-bottom: 8px; margin-top: 0; }
.code { background: #1d1d1f; color: #f5f5f7; padding: 12px; border-radius: 8px; font-family: monospace; font-size: 13px; overflow-x: auto; margin: 8px 0; }
table { width: 100%; border-collapse: collapse; margin: 12px 0; }
th, td { padding: 10px; text-align: left; border-bottom: 1px solid #e5e5e7; }
th { background: #f5f5f7; font-weight: 600; }
.ok { color: #34c759; }
.err { color: #ff3b30; }
</style>
</head>
<body>
<div class="card">
<h1>🍳 数据库诊断报告</h1>
<p style="color:#666;">生成时间: <?php echo date('Y-m-d H:i:s'); ?></p>
</div>
<div class="card">
<h2>🔌 数据库连接</h2>
<?php
try {
$sql = "SELECT 1";
$result = $zbp->db->Query($sql);
check("数据库连接", true, "类型: " . get_class($zbp->db) . ", 前缀: " . $zbp->db->dbpre);
} catch (Exception $e) {
check("数据库连接", false, $e->getMessage());
}
?>
</div>
<div class="card">
<h2>📋 数据表检查</h2>
<?php
$tables = ['Post', 'Category', 'Tag', 'recipe_ingredient'];
foreach ($tables as $table) {
try {
$fullTable = $zbp->db->dbpre . $table;
$sql = "SELECT COUNT(*) FROM $fullTable";
$count = $zbp->db->Query($sql)[0]['COUNT(*)'] ?? 0;
check("$fullTable 表", true, "$count 条记录");
} catch (Exception $e) {
check("$fullTable 表", false, $e->getMessage());
}
}
?>
</div>
<div class="card">
<h2>🍳 菜谱数据检查</h2>
<?php
// 检查公开菜谱
$tablePost = $zbp->db->dbpre . 'Post';
$sql = "SELECT COUNT(*) as cnt FROM $tablePost WHERE log_Type = 0 AND log_Status = 0";
$publicCount = $zbp->db->Query($sql)[0]['cnt'] ?? 0;
check("公开菜谱 (log_Type=0, log_Status=0)", $publicCount > 0, "数量: $publicCount");
// 检查ID范围
$sql = "SELECT MIN(log_ID) as min_id, MAX(log_ID) as max_id FROM $tablePost WHERE log_Type = 0";
$range = $zbp->db->Query($sql)[0];
check("菜谱ID范围", true, "min={$range['min_id']}, max={$range['max_id']}");
// 检查示例菜谱
$sql = "SELECT log_ID, log_Title, log_CateID FROM $tablePost WHERE log_Type = 0 AND log_Status = 0 LIMIT 3";
$samples = $zbp->db->Query($sql);
?>
<h3 style="margin-top:16px;">示例菜谱数据:</h3>
<table>
<tr><th>ID</th><th>标题</th><th>分类ID</th></tr>
<?php foreach ($samples as $s): ?>
<tr>
<td><?php echo $s['log_ID']; ?></td>
<td><?php echo htmlspecialchars($s['log_Title']); ?></td>
<td><?php echo $s['log_CateID']; ?></td>
</tr>
<?php endforeach; ?>
</table>
<?php
// 检查ID=70是否存在
$sql = "SELECT log_ID, log_Title FROM $tablePost WHERE log_ID = 70 AND log_Type = 0";
$id70 = $zbp->db->Query($sql);
if ($id70) {
check("ID=70 菜谱", true, "标题: " . $id70[0]['log_Title']);
} else {
check("ID=70 菜谱", false, "不存在或不是文章类型");
}
?>
</div>
<div class="card">
<h2>🥬 食材数据检查</h2>
<?php
$tableIng = $zbp->db->dbpre . 'recipe_ingredient';
// 检查唯一食材数量
$sql = "SELECT COUNT(DISTINCT name) as cnt FROM $tableIng";
$uniqueCount = $zbp->db->Query($sql)[0]['cnt'] ?? 0;
check("食材种类数", $uniqueCount > 0, "$uniqueCount 种");
// 检查ID范围
$sql = "SELECT MIN(ingredient_id) as min_id, MAX(ingredient_id) as max_id FROM $tableIng";
$range = $zbp->db->Query($sql)[0];
check("ingredient_id范围", true, "min={$range['min_id']}, max={$range['max_id']}");
// 检查示例食材
$sql = "SELECT DISTINCT name, ingredient_id FROM $tableIng LIMIT 5";
$samples = $zbp->db->Query($sql);
?>
<h3 style="margin-top:16px;">示例食材数据:</h3>
<table>
<tr><th>ingredient_id</th><th>名称</th></tr>
<?php foreach ($samples as $s): ?>
<tr>
<td><?php echo $s['ingredient_id']; ?></td>
<td><?php echo htmlspecialchars($s['name']); ?></td>
</tr>
<?php endforeach; ?>
</table>
<?php
// 检查ID=85是否存在
$sql = "SELECT name FROM $tableIng WHERE ingredient_id = 85 LIMIT 1";
$id85 = $zbp->db->Query($sql);
if ($id85) {
check("ID=85 食材", true, "名称: " . $id85[0]['name']);
} else {
check("ID=85 食材", false, "不存在");
}
?>
</div>
<div class="card">
<h2>🧪 API实际查询测试</h2>
<?php
// 模拟list接口查询
$sql = "SELECT log_ID, log_Title FROM $tablePost WHERE log_Type = 0 AND log_Status = 0 ORDER BY log_PostTime DESC LIMIT 3";
$listResult = $zbp->db->Query($sql);
check("list接口查询", count($listResult) > 0, "返回 " . count($listResult) . " 条");
// 模拟ingredients接口查询
$sql = "SELECT DISTINCT name, ingredient_id FROM $tableIng LIMIT 3";
$ingResult = $zbp->db->Query($sql);
check("ingredients接口查询", count($ingResult) > 0, "返回 " . count($ingResult) . " 条");
?>
</div>
<div class="card">
<h2>💡 问题诊断建议</h2>
<?php if ($publicCount == 0): ?>
<div class="code">
⚠️ 公开菜谱数量为0可能原因:
1. log_Status 字段值不是0 (0=公开, 1=草稿, 2=审核中)
2. log_Type 字段值不是0 (0=文章, 1=页面)
修复SQL:
UPDATE zbp_Post SET log_Status = 0 WHERE log_Type = 0;
</div>
<?php endif; ?>
<?php if (!$id70): ?>
<div class="code">
⚠️ ID=70不存在可用ID范围: <?php echo $range['min_id']; ?> ~ <?php echo $range['max_id']; ?>
测试可用的ID:
?act=detail&id=<?php echo $range['min_id']; ?>
</div>
<?php endif; ?>
<?php if (!$id85): ?>
<div class="code">
⚠️ ingredient_id=85不存在可用范围: <?php echo $range['min_id'] ?? 'N/A'; ?> ~ <?php echo $range['max_id'] ?? 'N/A'; ?>
</div>
<?php endif; ?>
</div>
<div class="card" style="text-align:center;color:#86868b;">
<p>🍳 菜谱API诊断工具 | Powered by Z-Blog PHP</p>
</div>
</body>
</html>