更多 页面 重构
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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>
|
||||
Reference in New Issue
Block a user