Files
kitchen/docs/api/api_action.php
Developer 8d27c67d3a api实现
2026-04-09 08:54:36 +08:00

588 lines
19 KiB
PHP
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.
<?php
/**
* 动态接口(写操作)
* 包含:点赞、推荐、浏览量等写操作
* IP限制每个IP每天可推荐30次
*/
$startTime = microtime(true);
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: *');
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
$act = strtolower(trim($_GET['act'] ?? 'index'));
$result = array();
switch ($act) {
case 'like':
$result = toggle_like();
break;
case 'recommend':
$result = toggle_recommend();
break;
case 'view':
$result = increase_view();
break;
case 'ip_status':
$result = get_ip_status();
break;
case 'index':
default:
$result = array(
'code' => 200,
'message' => '动态接口',
'data' => array(
'version' => '1.7.0',
'description' => '包含点赞、推荐、浏览量等写操作',
'ip_limit' => '每个IP每天可推荐30次',
'endpoints' => array(
'like' => '?act=like&type=recipe&id=1&action=like',
'recommend' => '?act=recommend&type=recipe&id=1&action=recommend&score=5',
'view' => '?act=view&type=recipe&id=1&count=1',
'ip_status' => '?act=ip_status'
)
)
);
break;
}
if ($result['code'] === 200) {
$type = strtolower(trim($_GET['type'] ?? ''));
if ($type === 'recipe') {
ApiCache::clearByAct('list');
ApiCache::clearByAct('stats');
$id = (int) ($_GET['id'] ?? 0);
if ($id > 0) {
ApiCache::clear('detail', array('id' => $id));
}
} elseif ($type === 'ingredient') {
ApiCache::clearByAct('ingredients');
ApiCache::clearByAct('stats');
$id = (int) ($_GET['id'] ?? 0);
if ($id > 0) {
ApiCache::clear('ingredient_detail', array('id' => $id));
}
}
}
$result['_query_time'] = round((microtime(true) - $startTime) * 1000, 2) . 'ms';
echo json_encode($result, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
exit;
/**
* 获取客户端IP
*/
function get_client_ip() {
$ip = '';
if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
} elseif (isset($_SERVER['HTTP_CLIENT_IP'])) {
$ip = $_SERVER['HTTP_CLIENT_IP'];
} elseif (isset($_SERVER['REMOTE_ADDR'])) {
$ip = $_SERVER['REMOTE_ADDR'];
}
$ip = trim(explode(',', $ip)[0]);
return filter_var($ip, FILTER_VALIDATE_IP) ? $ip : '0.0.0.0';
}
/**
* 记录统计日志
* @param string $type 类型: recipe 或 ingredient
* @param int $id ID
* @param string $field 字段: view, like, recommend
* @param int $count 变化数量(正数增加,负数减少)
*/
function log_stat($type, $id, $field, $count = 1) {
global $zbp;
$today = date('Y-m-d');
if ($type === 'recipe') {
$table = $zbp->db->dbpre . 'recipe_stat_log';
$idField = 'log_id';
} else {
$table = $zbp->db->dbpre . 'ingredient_stat_log';
$idField = 'ingredient_id';
}
$checkSql = "SELECT id, view_count, like_count, recommend_count FROM $table WHERE $idField = $id AND stat_date = '$today'";
$result = $zbp->db->Query($checkSql);
if (empty($result)) {
$insertSql = "INSERT INTO $table ($idField, stat_date, view_count, like_count, recommend_count) VALUES ($id, '$today', 0, 0, 0)";
$zbp->db->Query($insertSql);
$currentView = 0;
$currentLike = 0;
$currentRecommend = 0;
} else {
$currentView = (int) ($result[0]['view_count'] ?? 0);
$currentLike = (int) ($result[0]['like_count'] ?? 0);
$currentRecommend = (int) ($result[0]['recommend_count'] ?? 0);
}
switch ($field) {
case 'view':
$newView = max(0, $currentView + $count);
$updateSql = "UPDATE $table SET view_count = $newView WHERE $idField = $id AND stat_date = '$today'";
break;
case 'like':
$newLike = max(0, $currentLike + $count);
$updateSql = "UPDATE $table SET like_count = $newLike WHERE $idField = $id AND stat_date = '$today'";
break;
case 'recommend':
$newRecommend = max(0, $currentRecommend + $count);
$updateSql = "UPDATE $table SET recommend_count = $newRecommend WHERE $idField = $id AND stat_date = '$today'";
break;
default:
return;
}
$zbp->db->Query($updateSql);
}
/**
* 获取IP缓存文件路径
*/
function get_ip_cache_file() {
$cacheDir = dirname(__FILE__) . '/cache/ip/';
if (!is_dir($cacheDir)) {
@mkdir($cacheDir, 0755, true);
}
return $cacheDir . 'recommend_' . date('Y-m-d') . '.json';
}
/**
* 加载IP推荐缓存
*/
function load_ip_recommend_cache() {
$file = get_ip_cache_file();
if (file_exists($file)) {
$content = file_get_contents($file);
$data = json_decode($content, true);
return is_array($data) ? $data : array();
}
return array();
}
/**
* 保存IP推荐缓存
*/
function save_ip_recommend_cache($data) {
$file = get_ip_cache_file();
file_put_contents($file, json_encode($data, JSON_UNESCAPED_UNICODE));
}
/**
* 清理过期的IP缓存文件
*/
function clean_expired_ip_cache() {
$cacheDir = dirname(__FILE__) . '/cache/ip/';
if (!is_dir($cacheDir)) {
return;
}
$today = date('Y-m-d');
$files = glob($cacheDir . 'recommend_*.json');
foreach ($files as $file) {
$basename = basename($file, '.json');
$date = str_replace('recommend_', '', $basename);
if ($date < $today) {
@unlink($file);
}
}
}
/**
* 检查IP推荐限制
*/
function check_ip_recommend_limit() {
$ip = get_client_ip();
$maxDaily = 30;
if (rand(1, 50) === 1) {
clean_expired_ip_cache();
}
$cache = load_ip_recommend_cache();
$current = isset($cache[$ip]) ? (int) $cache[$ip] : 0;
return array(
'allowed' => $current < $maxDaily,
'current' => $current,
'remaining' => max(0, $maxDaily - $current)
);
}
/**
* 增加IP推荐次数
*/
function increment_ip_recommend() {
$ip = get_client_ip();
$cache = load_ip_recommend_cache();
$current = isset($cache[$ip]) ? (int) $cache[$ip] : 0;
$cache[$ip] = $current + 1;
save_ip_recommend_cache($cache);
}
/**
* 获取IP状态
*/
function get_ip_status() {
$ipStatus = check_ip_recommend_limit();
return array(
'code' => 200,
'message' => 'success',
'data' => array(
'ip' => get_client_ip(),
'today_recommend_count' => $ipStatus['current'],
'remaining_recommend' => $ipStatus['remaining'],
'daily_limit' => 30,
'date' => date('Y-m-d')
)
);
}
/**
* 点赞/取消点赞
*/
function toggle_like() {
global $zbp;
$type = strtolower(trim($_GET['type'] ?? 'recipe'));
$id = (int) ($_GET['id'] ?? 0);
$action = strtolower(trim($_GET['action'] ?? 'like'));
if (!in_array($type, ['recipe', 'ingredient'])) {
return array('code' => 400, 'message' => 'type参数必须是 recipe 或 ingredient');
}
if ($id <= 0) {
return array('code' => 400, 'message' => 'id参数无效');
}
if (!in_array($action, ['like', 'unlike'])) {
return array('code' => 400, 'message' => 'action参数必须是 like 或 unlike');
}
if ($type === 'recipe') {
$tablePost = $zbp->db->dbpre . 'post';
$tableStat = $zbp->db->dbpre . 'post_stat';
$checkSql = "SELECT log_ID FROM $tablePost WHERE log_ID = $id AND log_Type = 0 AND log_Status = 0";
$checkResult = $zbp->db->Query($checkSql);
if (empty($checkResult)) {
return array('code' => 404, 'message' => '菜谱不存在');
}
$statSql = "SELECT * FROM $tableStat WHERE log_id = $id";
$statResult = $zbp->db->Query($statSql);
if (empty($statResult)) {
$insertSql = "INSERT INTO $tableStat (log_id, like_nums, recommend_nums, recommend_score) VALUES ($id, 0, 0, 0.00)";
$zbp->db->Query($insertSql);
$currentLikes = 0;
} else {
$currentLikes = (int) ($statResult[0]['like_nums'] ?? 0);
}
if ($action === 'like') {
$newLikes = $currentLikes + 1;
} else {
$newLikes = max(0, $currentLikes - 1);
}
$updateSql = "UPDATE $tableStat SET like_nums = $newLikes WHERE log_id = $id";
$zbp->db->Query($updateSql);
log_stat('recipe', $id, 'like', $action === 'like' ? 1 : -1);
return array(
'code' => 200,
'message' => $action === 'like' ? '点赞成功' : '取消点赞成功',
'data' => array(
'type' => 'recipe',
'id' => $id,
'action' => $action,
'like_count' => $newLikes
)
);
} else {
$tableIngredient = $zbp->db->dbpre . 'ingredient_detail';
$tableStat = $zbp->db->dbpre . 'ingredient_stat';
$checkSql = "SELECT ingredient_id FROM $tableIngredient WHERE ingredient_id = $id";
$checkResult = $zbp->db->Query($checkSql);
if (empty($checkResult)) {
return array('code' => 404, 'message' => '食材不存在');
}
$statSql = "SELECT * FROM $tableStat WHERE ingredient_id = $id";
$statResult = $zbp->db->Query($statSql);
if (empty($statResult)) {
$insertSql = "INSERT INTO $tableStat (ingredient_id, like_nums, recommend_nums, recommend_score) VALUES ($id, 0, 0, 0.00)";
$zbp->db->Query($insertSql);
$currentLikes = 0;
} else {
$currentLikes = (int) ($statResult[0]['like_nums'] ?? 0);
}
if ($action === 'like') {
$newLikes = $currentLikes + 1;
} else {
$newLikes = max(0, $currentLikes - 1);
}
$updateSql = "UPDATE $tableStat SET like_nums = $newLikes WHERE ingredient_id = $id";
$zbp->db->Query($updateSql);
log_stat('ingredient', $id, 'like', $action === 'like' ? 1 : -1);
return array(
'code' => 200,
'message' => $action === 'like' ? '点赞成功' : '取消点赞成功',
'data' => array(
'type' => 'ingredient',
'id' => $id,
'action' => $action,
'like_count' => $newLikes
)
);
}
}
/**
* 推荐/取消推荐
* IP限制每个IP每天可推荐30次
*/
function toggle_recommend() {
global $zbp;
$type = strtolower(trim($_GET['type'] ?? 'recipe'));
$id = (int) ($_GET['id'] ?? 0);
$action = strtolower(trim($_GET['action'] ?? 'recommend'));
$score = (float) ($_GET['score'] ?? 1.00);
if (!in_array($type, ['recipe', 'ingredient'])) {
return array('code' => 400, 'message' => 'type参数必须是 recipe 或 ingredient');
}
if ($id <= 0) {
return array('code' => 400, 'message' => 'id参数无效');
}
if (!in_array($action, ['recommend', 'unrecommend'])) {
return array('code' => 400, 'message' => 'action参数必须是 recommend 或 unrecommend');
}
$score = max(0.00, min(5.00, $score));
if ($action === 'recommend') {
$ipStatus = check_ip_recommend_limit();
if (!$ipStatus['allowed']) {
return array(
'code' => 429,
'message' => '今日推荐次数已达上限(30次)',
'data' => array(
'ip' => get_client_ip(),
'today_recommend_count' => $ipStatus['current'],
'remaining' => 0,
'daily_limit' => 30
)
);
}
}
if ($type === 'recipe') {
$tablePost = $zbp->db->dbpre . 'post';
$tableStat = $zbp->db->dbpre . 'post_stat';
$checkSql = "SELECT log_ID FROM $tablePost WHERE log_ID = $id AND log_Type = 0 AND log_Status = 0";
$checkResult = $zbp->db->Query($checkSql);
if (empty($checkResult)) {
return array('code' => 404, 'message' => '菜谱不存在');
}
$statSql = "SELECT * FROM $tableStat WHERE log_id = $id";
$statResult = $zbp->db->Query($statSql);
if (empty($statResult)) {
$insertSql = "INSERT INTO $tableStat (log_id, like_nums, recommend_nums, recommend_score) VALUES ($id, 0, 0, 0.00)";
$zbp->db->Query($insertSql);
$currentNums = 0;
$currentScore = 0.00;
} else {
$currentNums = (int) ($statResult[0]['recommend_nums'] ?? 0);
$currentScore = (float) ($statResult[0]['recommend_score'] ?? 0.00);
}
if ($action === 'recommend') {
$newNums = $currentNums + 1;
$newScore = ($currentScore * $currentNums + $score) / $newNums;
increment_ip_recommend();
} else {
$newNums = max(0, $currentNums - 1);
$newScore = $newNums > 0 ? $currentScore : 0.00;
}
$updateSql = "UPDATE $tableStat SET recommend_nums = $newNums, recommend_score = $newScore WHERE log_id = $id";
$zbp->db->Query($updateSql);
log_stat('recipe', $id, 'recommend', $action === 'recommend' ? 1 : -1);
$ipStatus = check_ip_recommend_limit();
return array(
'code' => 200,
'message' => $action === 'recommend' ? '推荐成功' : '取消推荐成功',
'data' => array(
'type' => 'recipe',
'id' => $id,
'action' => $action,
'recommend_nums' => $newNums,
'recommend_score' => round($newScore, 2),
'ip_remaining' => $ipStatus['remaining']
)
);
} else {
$tableIngredient = $zbp->db->dbpre . 'ingredient_detail';
$tableStat = $zbp->db->dbpre . 'ingredient_stat';
$checkSql = "SELECT ingredient_id FROM $tableIngredient WHERE ingredient_id = $id";
$checkResult = $zbp->db->Query($checkSql);
if (empty($checkResult)) {
return array('code' => 404, 'message' => '食材不存在');
}
$statSql = "SELECT * FROM $tableStat WHERE ingredient_id = $id";
$statResult = $zbp->db->Query($statSql);
if (empty($statResult)) {
$insertSql = "INSERT INTO $tableStat (ingredient_id, like_nums, recommend_nums, recommend_score) VALUES ($id, 0, 0, 0.00)";
$zbp->db->Query($insertSql);
$currentNums = 0;
$currentScore = 0.00;
} else {
$currentNums = (int) ($statResult[0]['recommend_nums'] ?? 0);
$currentScore = (float) ($statResult[0]['recommend_score'] ?? 0.00);
}
if ($action === 'recommend') {
$newNums = $currentNums + 1;
$newScore = ($currentScore * $currentNums + $score) / $newNums;
increment_ip_recommend();
} else {
$newNums = max(0, $currentNums - 1);
$newScore = $newNums > 0 ? $currentScore : 0.00;
}
$updateSql = "UPDATE $tableStat SET recommend_nums = $newNums, recommend_score = $newScore WHERE ingredient_id = $id";
$zbp->db->Query($updateSql);
log_stat('ingredient', $id, 'recommend', $action === 'recommend' ? 1 : -1);
$ipStatus = check_ip_recommend_limit();
return array(
'code' => 200,
'message' => $action === 'recommend' ? '推荐成功' : '取消推荐成功',
'data' => array(
'type' => 'ingredient',
'id' => $id,
'action' => $action,
'recommend_nums' => $newNums,
'recommend_score' => round($newScore, 2),
'ip_remaining' => $ipStatus['remaining']
)
);
}
}
/**
* 增加浏览量
*/
function increase_view() {
global $zbp;
$type = strtolower(trim($_GET['type'] ?? 'recipe'));
$id = (int) ($_GET['id'] ?? 0);
$count = (int) ($_GET['count'] ?? 1);
if (!in_array($type, ['recipe', 'ingredient'])) {
return array('code' => 400, 'message' => 'type参数必须是 recipe 或 ingredient');
}
if ($id <= 0) {
return array('code' => 400, 'message' => 'id参数无效');
}
$count = max(1, min(100, $count));
if ($type === 'recipe') {
$tablePost = $zbp->db->dbpre . 'post';
$checkSql = "SELECT log_ID, log_ViewNums FROM $tablePost WHERE log_ID = $id AND log_Type = 0 AND log_Status = 0";
$checkResult = $zbp->db->Query($checkSql);
if (empty($checkResult)) {
return array('code' => 404, 'message' => '菜谱不存在');
}
$currentViews = (int) ($checkResult[0]['log_ViewNums'] ?? 0);
$newViews = $currentViews + $count;
$updateSql = "UPDATE $tablePost SET log_ViewNums = $newViews WHERE log_ID = $id";
$zbp->db->Query($updateSql);
log_stat('recipe', $id, 'view', $count);
return array(
'code' => 200,
'message' => '浏览量已更新',
'data' => array(
'type' => 'recipe',
'id' => $id,
'view_count' => $newViews,
'increment' => $count
)
);
} else {
$tableIngredient = $zbp->db->dbpre . 'ingredient_detail';
$checkSql = "SELECT ingredient_id, view_count FROM $tableIngredient WHERE ingredient_id = $id";
$checkResult = $zbp->db->Query($checkSql);
if (empty($checkResult)) {
return array('code' => 404, 'message' => '食材不存在');
}
$currentViews = (int) ($checkResult[0]['view_count'] ?? 0);
$newViews = $currentViews + $count;
$updateSql = "UPDATE $tableIngredient SET view_count = $newViews WHERE ingredient_id = $id";
$zbp->db->Query($updateSql);
log_stat('ingredient', $id, 'view', $count);
return array(
'code' => 200,
'message' => '浏览量已更新',
'data' => array(
'type' => 'ingredient',
'id' => $id,
'view_count' => $newViews,
'increment' => $count
)
);
}
}