release
This commit is contained in:
@@ -5,9 +5,10 @@
|
||||
* @file api_discover.php
|
||||
* @author AI Assistant
|
||||
* @date 2026-04-12
|
||||
* @version 1.0.0
|
||||
* @version 1.1.0
|
||||
* @desc 提供随机数据用于首页/发现页瀑布流,支持响应式布局
|
||||
* @lastUpdate 2026-04-12 初始版本
|
||||
* 同一客户端多次请求返回不重复数据,30分钟后重置
|
||||
* @lastUpdate 2026-04-12 添加已返回ID排除机制,避免重复数据
|
||||
*/
|
||||
|
||||
$startTime = microtime(true);
|
||||
@@ -47,47 +48,52 @@ $format = ApiResponse::getFormat();
|
||||
$forceRefresh = isset($params['_refresh']) && $params['_refresh'] === '1';
|
||||
|
||||
$clientIP = getClientIP();
|
||||
$requestInfo = ApiCache::getDiscoverRequestCount($clientIP);
|
||||
|
||||
$shouldUseCache = !$forceRefresh && $requestInfo['count'] >= 2 && $requestInfo['data'] !== null;
|
||||
|
||||
if ($shouldUseCache) {
|
||||
header('X-Cache: HIT');
|
||||
header('X-Request-Count: ' . $requestInfo['count']);
|
||||
$result = $requestInfo['data'];
|
||||
$result['meta']['cache_status'] = 'cached';
|
||||
$result['meta']['request_count'] = $requestInfo['count'];
|
||||
$result['_query_time'] = round((microtime(true) - $startTime) * 1000, 2) . 'ms';
|
||||
ApiResponse::output($result, $format);
|
||||
exit;
|
||||
}
|
||||
|
||||
header('X-Cache: MISS');
|
||||
|
||||
$counts = calculateCounts($params);
|
||||
|
||||
$data = array(
|
||||
'recipes' => getRandomRecipes($counts['recipe']),
|
||||
'ingredients' => getRandomIngredients($counts['ingredient']),
|
||||
'categories' => getRandomCategories($counts['category']),
|
||||
'tags' => getRandomTags($counts['tag']),
|
||||
'nutrition_types' => getNutritionTypes($counts['nutrition']),
|
||||
'recipes' => getRandomRecipes($counts['recipe'], $clientIP),
|
||||
'ingredients' => getRandomIngredients($counts['ingredient'], $clientIP),
|
||||
'categories' => getRandomCategories($counts['category'], $clientIP),
|
||||
'tags' => getRandomTags($counts['tag'], $clientIP),
|
||||
'nutrition_types' => getNutritionTypes($counts['nutrition'], $clientIP),
|
||||
'meal_times' => getMealTimes($counts['meal_time'])
|
||||
);
|
||||
|
||||
$recipeIds = array_column($data['recipes'], 'id');
|
||||
$ingredientIds = array_column($data['ingredients'], 'id');
|
||||
$categoryIds = array_column($data['categories'], 'id');
|
||||
$tagIds = array_column($data['tags'], 'id');
|
||||
$nutritionNames = array_column($data['nutrition_types'], 'name');
|
||||
|
||||
if (!empty($recipeIds)) {
|
||||
ApiCache::addDiscoverReturnedIds($clientIP, 'recipe', $recipeIds);
|
||||
}
|
||||
if (!empty($ingredientIds)) {
|
||||
ApiCache::addDiscoverReturnedIds($clientIP, 'ingredient', $ingredientIds);
|
||||
}
|
||||
if (!empty($categoryIds)) {
|
||||
ApiCache::addDiscoverReturnedIds($clientIP, 'category', $categoryIds);
|
||||
}
|
||||
if (!empty($tagIds)) {
|
||||
ApiCache::addDiscoverReturnedIds($clientIP, 'tag', $tagIds);
|
||||
}
|
||||
if (!empty($nutritionNames)) {
|
||||
ApiCache::addDiscoverReturnedIds($clientIP, 'nutrition', $nutritionNames);
|
||||
}
|
||||
|
||||
$result = array(
|
||||
'code' => 200,
|
||||
'message' => 'success',
|
||||
'data' => $data,
|
||||
'meta' => array(
|
||||
'request_count' => $requestInfo['count'] + 1,
|
||||
'cache_status' => 'fresh',
|
||||
'counts' => $counts
|
||||
)
|
||||
);
|
||||
|
||||
ApiCache::incrementDiscoverRequestCount($clientIP, $result);
|
||||
|
||||
$result['_query_time'] = round((microtime(true) - $startTime) * 1000, 2) . 'ms';
|
||||
|
||||
ApiResponse::output($result, $format);
|
||||
@@ -112,15 +118,15 @@ function getClientIP() {
|
||||
* 计算各类型数量
|
||||
*/
|
||||
function calculateCounts($params) {
|
||||
$total = isset($params['total']) ? min((int) $params['total'], 100) : 30;
|
||||
$total = isset($params['total']) ? min((int) $params['total'], 100) : 50;
|
||||
|
||||
$ratios = array(
|
||||
'recipe' => 0.25,
|
||||
'ingredient' => 0.15,
|
||||
'category' => 0.15,
|
||||
'tag' => 0.20,
|
||||
'nutrition' => 0.15,
|
||||
'meal_time' => 0.10
|
||||
'recipe' => 0.75,
|
||||
'ingredient' => 0.03,
|
||||
'category' => 0.10,
|
||||
'tag' => 0.03,
|
||||
'nutrition' => 0.02,
|
||||
'meal_time' => 0.02
|
||||
);
|
||||
|
||||
$counts = array();
|
||||
@@ -130,7 +136,7 @@ function calculateCounts($params) {
|
||||
foreach ($ratios as $type => $ratio) {
|
||||
$paramName = $type === 'nutrition' ? 'nutrition' : $type;
|
||||
if (isset($params[$paramName]) && (int) $params[$paramName] > 0) {
|
||||
$counts[$type] = min((int) $params[$paramName], 20);
|
||||
$counts[$type] = min((int) $params[$paramName], 80);
|
||||
$specifiedTotal += $counts[$type];
|
||||
$specifiedTypes[] = $type;
|
||||
}
|
||||
@@ -159,7 +165,7 @@ function calculateCounts($params) {
|
||||
/**
|
||||
* 获取随机菜品
|
||||
*/
|
||||
function getRandomRecipes($limit) {
|
||||
function getRandomRecipes($limit, $clientIP = '') {
|
||||
global $zbp;
|
||||
|
||||
if ($limit <= 0) return array();
|
||||
@@ -169,6 +175,17 @@ function getRandomRecipes($limit) {
|
||||
$tablePostStat = $zbp->db->dbpre . 'post_stat';
|
||||
$tableIdMap = $zbp->db->dbpre . 'recipe_id_map';
|
||||
|
||||
$excludeIds = array();
|
||||
if (!empty($clientIP)) {
|
||||
$excludeIds = ApiCache::getDiscoverReturnedIds($clientIP, 'recipe');
|
||||
}
|
||||
|
||||
$whereClause = "p.log_Type = 0 AND p.log_Status = 0";
|
||||
if (!empty($excludeIds)) {
|
||||
$excludeIdsStr = implode(',', array_map('intval', $excludeIds));
|
||||
$whereClause .= " AND p.log_ID NOT IN ($excludeIdsStr)";
|
||||
}
|
||||
|
||||
$sql = "SELECT p.log_ID, p.log_Title, p.log_CateID, p.log_ViewNums,
|
||||
c.cate_Name,
|
||||
s.rate_nums, s.rate_score,
|
||||
@@ -177,12 +194,17 @@ function getRandomRecipes($limit) {
|
||||
LEFT JOIN $tableCategory c ON p.log_CateID = c.cate_ID
|
||||
LEFT JOIN $tablePostStat s ON p.log_ID = s.log_id
|
||||
LEFT JOIN $tableIdMap m ON p.log_ID = m.new_log_id
|
||||
WHERE p.log_Type = 0 AND p.log_Status = 0
|
||||
WHERE $whereClause
|
||||
ORDER BY RAND()
|
||||
LIMIT $limit";
|
||||
|
||||
$results = $zbp->db->Query($sql);
|
||||
|
||||
if (empty($results) && !empty($excludeIds)) {
|
||||
ApiCache::clearDiscoverReturnedIds($clientIP, 'recipe');
|
||||
return getRandomRecipes($limit, $clientIP);
|
||||
}
|
||||
|
||||
$list = array();
|
||||
foreach ($results as $row) {
|
||||
$rateNums = (int) ($row['rate_nums'] ?? 0);
|
||||
@@ -213,7 +235,7 @@ function getRandomRecipes($limit) {
|
||||
/**
|
||||
* 获取随机食材
|
||||
*/
|
||||
function getRandomIngredients($limit) {
|
||||
function getRandomIngredients($limit, $clientIP = '') {
|
||||
global $zbp;
|
||||
|
||||
if ($limit <= 0) return array();
|
||||
@@ -221,15 +243,32 @@ function getRandomIngredients($limit) {
|
||||
$tableIngredient = $zbp->db->dbpre . 'ingredient_detail';
|
||||
$tableCategory = $zbp->db->dbpre . 'category';
|
||||
|
||||
$excludeIds = array();
|
||||
if (!empty($clientIP)) {
|
||||
$excludeIds = ApiCache::getDiscoverReturnedIds($clientIP, 'ingredient');
|
||||
}
|
||||
|
||||
$whereClause = "1=1";
|
||||
if (!empty($excludeIds)) {
|
||||
$excludeIdsStr = implode(',', array_map('intval', $excludeIds));
|
||||
$whereClause .= " AND i.ingredient_id NOT IN ($excludeIdsStr)";
|
||||
}
|
||||
|
||||
$sql = "SELECT i.ingredient_id, i.name, i.cate_ID, i.allergen, i.introduction,
|
||||
c.cate_Name
|
||||
FROM $tableIngredient i
|
||||
LEFT JOIN $tableCategory c ON i.cate_ID = c.cate_ID
|
||||
WHERE $whereClause
|
||||
ORDER BY RAND()
|
||||
LIMIT $limit";
|
||||
|
||||
$results = $zbp->db->Query($sql);
|
||||
|
||||
if (empty($results) && !empty($excludeIds)) {
|
||||
ApiCache::clearDiscoverReturnedIds($clientIP, 'ingredient');
|
||||
return getRandomIngredients($limit, $clientIP);
|
||||
}
|
||||
|
||||
$list = array();
|
||||
foreach ($results as $row) {
|
||||
$allergen = array();
|
||||
@@ -261,20 +300,72 @@ function getRandomIngredients($limit) {
|
||||
/**
|
||||
* 获取随机分类
|
||||
*/
|
||||
function getRandomCategories($limit) {
|
||||
function getRandomCategories($limit, $clientIP = '') {
|
||||
global $zbp;
|
||||
|
||||
if ($limit <= 0) return array();
|
||||
|
||||
$tableCategory = $zbp->db->dbpre . 'category';
|
||||
$tableIngredient = $zbp->db->dbpre . 'ingredient_detail';
|
||||
|
||||
$sql = "SELECT cate_ID, cate_Name, cate_Count, cate_ParentID
|
||||
FROM $tableCategory
|
||||
WHERE cate_ID > 10
|
||||
ORDER BY RAND()
|
||||
LIMIT $limit";
|
||||
$excludeIds = array();
|
||||
if (!empty($clientIP)) {
|
||||
$excludeIds = ApiCache::getDiscoverReturnedIds($clientIP, 'category');
|
||||
}
|
||||
|
||||
$results = $zbp->db->Query($sql);
|
||||
$excludeClause = '';
|
||||
if (!empty($excludeIds)) {
|
||||
$excludeIdsStr = implode(',', array_map('intval', $excludeIds));
|
||||
$excludeClause = " AND cate_ID NOT IN ($excludeIdsStr)";
|
||||
}
|
||||
|
||||
$recipeLimit = max(1, ceil($limit * 0.6));
|
||||
$ingredientLimit = $limit - $recipeLimit;
|
||||
|
||||
$results = array();
|
||||
|
||||
$sqlRecipe = "SELECT cate_ID, cate_Name, cate_Count, cate_ParentID
|
||||
FROM $tableCategory
|
||||
WHERE cate_ID > 10 AND cate_ID < 1000 AND cate_ParentID != 11 $excludeClause
|
||||
ORDER BY RAND()
|
||||
LIMIT $recipeLimit";
|
||||
$recipeResults = $zbp->db->Query($sqlRecipe);
|
||||
$results = array_merge($results, $recipeResults);
|
||||
|
||||
$sqlIngredient = "SELECT cate_ID, cate_Name, cate_Count, cate_ParentID
|
||||
FROM $tableCategory
|
||||
WHERE cate_ID >= 1000 $excludeClause
|
||||
ORDER BY RAND()
|
||||
LIMIT $ingredientLimit";
|
||||
$ingredientResults = $zbp->db->Query($sqlIngredient);
|
||||
$results = array_merge($results, $ingredientResults);
|
||||
|
||||
if (empty($results) && !empty($excludeIds)) {
|
||||
ApiCache::clearDiscoverReturnedIds($clientIP, 'category');
|
||||
return getRandomCategories($limit, $clientIP);
|
||||
}
|
||||
|
||||
$ingredientCounts = array();
|
||||
$cateIds = array_column($results, 'cate_ID');
|
||||
if (!empty($cateIds)) {
|
||||
$cateIdsStr = implode(',', array_map('intval', $cateIds));
|
||||
$sqlIngredientCount = "SELECT cate_ID, COUNT(*) as cnt FROM $tableIngredient WHERE cate_ID IN ($cateIdsStr) GROUP BY cate_ID";
|
||||
$ingredientCountResults = $zbp->db->Query($sqlIngredientCount);
|
||||
foreach ($ingredientCountResults as $row) {
|
||||
$ingredientCounts[$row['cate_ID']] = (int) $row['cnt'];
|
||||
}
|
||||
}
|
||||
|
||||
$parentIds = array_unique(array_filter(array_column($results, 'cate_ParentID')));
|
||||
$parentNames = array();
|
||||
if (!empty($parentIds)) {
|
||||
$parentIdsStr = implode(',', array_map('intval', $parentIds));
|
||||
$sqlParent = "SELECT cate_ID, cate_Name FROM $tableCategory WHERE cate_ID IN ($parentIdsStr)";
|
||||
$parentResults = $zbp->db->Query($sqlParent);
|
||||
foreach ($parentResults as $row) {
|
||||
$parentNames[$row['cate_ID']] = $row['cate_Name'];
|
||||
}
|
||||
}
|
||||
|
||||
$list = array();
|
||||
foreach ($results as $row) {
|
||||
@@ -285,36 +376,62 @@ function getRandomCategories($limit) {
|
||||
$type = 'recipe_main';
|
||||
}
|
||||
|
||||
$recipeCount = (int) ($row['cate_Count'] ?? 0);
|
||||
$ingredientCount = isset($ingredientCounts[$row['cate_ID']]) ? $ingredientCounts[$row['cate_ID']] : 0;
|
||||
$parentId = (int) $row['cate_ParentID'];
|
||||
$parentName = isset($parentNames[$parentId]) ? $parentNames[$parentId] : '';
|
||||
|
||||
$list[] = array(
|
||||
'id' => (int) $row['cate_ID'],
|
||||
'name' => $row['cate_Name'],
|
||||
'type' => $type,
|
||||
'count' => (int) ($row['cate_Count'] ?? 0),
|
||||
'parent_id' => (int) $row['cate_ParentID']
|
||||
'recipe_count' => $recipeCount,
|
||||
'ingredient_count' => $ingredientCount,
|
||||
'count' => $type === 'ingredient' ? $ingredientCount : $recipeCount,
|
||||
'parent_id' => $parentId,
|
||||
'parent_name' => $parentName
|
||||
);
|
||||
}
|
||||
|
||||
shuffle($list);
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取随机标签
|
||||
*/
|
||||
function getRandomTags($limit) {
|
||||
function getRandomTags($limit, $clientIP = '') {
|
||||
global $zbp;
|
||||
|
||||
if ($limit <= 0) return array();
|
||||
|
||||
$tableTag = $zbp->db->dbpre . 'tag';
|
||||
|
||||
$excludeIds = array();
|
||||
if (!empty($clientIP)) {
|
||||
$excludeIds = ApiCache::getDiscoverReturnedIds($clientIP, 'tag');
|
||||
}
|
||||
|
||||
$whereClause = "tag_Alias IN ('口味', '做法')";
|
||||
if (!empty($excludeIds)) {
|
||||
$excludeIdsStr = implode(',', array_map('intval', $excludeIds));
|
||||
$whereClause .= " AND tag_ID NOT IN ($excludeIdsStr)";
|
||||
}
|
||||
|
||||
$sql = "SELECT tag_ID, tag_Name, tag_Alias, tag_Count
|
||||
FROM $tableTag
|
||||
WHERE tag_Alias IN ('口味', '做法')
|
||||
WHERE $whereClause
|
||||
ORDER BY RAND()
|
||||
LIMIT $limit";
|
||||
|
||||
$results = $zbp->db->Query($sql);
|
||||
|
||||
if (empty($results) && !empty($excludeIds)) {
|
||||
ApiCache::clearDiscoverReturnedIds($clientIP, 'tag');
|
||||
return getRandomTags($limit, $clientIP);
|
||||
}
|
||||
|
||||
$list = array();
|
||||
foreach ($results as $row) {
|
||||
$type = 'taste';
|
||||
@@ -336,21 +453,40 @@ function getRandomTags($limit) {
|
||||
/**
|
||||
* 获取营养成分类型
|
||||
*/
|
||||
function getNutritionTypes($limit) {
|
||||
function getNutritionTypes($limit, $clientIP = '') {
|
||||
global $zbp;
|
||||
|
||||
if ($limit <= 0) return array();
|
||||
|
||||
$tableNutrition = $zbp->db->dbpre . 'recipe_nutrition';
|
||||
|
||||
$excludeNames = array();
|
||||
if (!empty($clientIP)) {
|
||||
$excludeNames = ApiCache::getDiscoverReturnedIds($clientIP, 'nutrition');
|
||||
}
|
||||
|
||||
$whereClause = "name IS NOT NULL AND name != ''";
|
||||
if (!empty($excludeNames)) {
|
||||
$escapedNames = array_map(function($name) use ($zbp) {
|
||||
return $zbp->db->EscapeString($name);
|
||||
}, $excludeNames);
|
||||
$excludeNamesStr = "'" . implode("','", $escapedNames) . "'";
|
||||
$whereClause .= " AND name NOT IN ($excludeNamesStr)";
|
||||
}
|
||||
|
||||
$sql = "SELECT DISTINCT name, unit
|
||||
FROM $tableNutrition
|
||||
WHERE name IS NOT NULL AND name != ''
|
||||
WHERE $whereClause
|
||||
ORDER BY RAND()
|
||||
LIMIT $limit";
|
||||
|
||||
$results = $zbp->db->Query($sql);
|
||||
|
||||
if (empty($results) && !empty($excludeNames)) {
|
||||
ApiCache::clearDiscoverReturnedIds($clientIP, 'nutrition');
|
||||
return getNutritionTypes($limit, $clientIP);
|
||||
}
|
||||
|
||||
$list = array();
|
||||
foreach ($results as $row) {
|
||||
$list[] = array(
|
||||
|
||||
Reference in New Issue
Block a user