瀑布流

This commit is contained in:
Developer
2026-04-13 03:39:29 +08:00
parent 7b90983bb9
commit 13fdbdc431
254 changed files with 49543 additions and 9085 deletions

View File

@@ -168,17 +168,21 @@ switch ($act) {
case 'unified_hot':
$result = get_unified_hot($type);
break;
case 'mini':
$result = recipe_mini();
break;
case 'index':
default:
$result = array(
'code' => 200,
'message' => '🍳 菜谱API服务正常运行',
'data' => array(
'version' => '1.26.0',
'version' => '1.29.0',
'endpoints' => array(
'list' => '?act=list',
'detail' => '?act=detail&id=1',
'full' => '?act=full&id=1',
'mini' => '?act=mini&id=1',
'ingredients' => '?act=ingredients',
'ingredient_detail' => '?act=ingredient_detail&id=1',
'search' => '?act=search&keyword=苹果',
@@ -225,52 +229,24 @@ function recipe_list() {
$cateId = (int) ($_GET['cate_id'] ?? 0);
$tagId = (int) ($_GET['tag_id'] ?? 0);
$search = trim($_GET['search'] ?? '');
$userId = trim($_GET['user_id'] ?? '');
$usePreference = isset($_GET['use_preference']) && $_GET['use_preference'] === 'true';
if ($limit > 100) $limit = 100;
if ($limit < 1) $limit = 20;
if ($page < 1) $page = 1;
$preferredTags = array();
$preferredCategories = array();
$blockedAllergens = array();
if ($usePreference && !empty($userId)) {
$preference = load_user_preference_data($userId);
$preferredTags = $preference['preferred_tags'] ?? array();
$preferredCategories = $preference['preferred_categories'] ?? array();
$blockedAllergens = $preference['blocked_allergens'] ?? array();
}
if (isset($_GET['preferred_tags'])) {
$preferredTags = array_map('intval', explode(',', $_GET['preferred_tags']));
}
if (isset($_GET['preferred_categories'])) {
$preferredCategories = array_map('intval', explode(',', $_GET['preferred_categories']));
}
$tablePost = $zbp->db->dbpre . 'post';
$tableCategory = $zbp->db->dbpre . 'category';
$tablePostStat = $zbp->db->dbpre . 'post_stat';
$offset = ($page - 1) * $limit;
$whereSql = "WHERE p.log_Type = 0 AND p.log_Status = 0";
if ($cateId > 0) {
$whereSql .= " AND p.log_CateID = $cateId";
} elseif (!empty($preferredCategories)) {
$cateList = implode(',', $preferredCategories);
$whereSql .= " AND p.log_CateID IN ($cateList)";
}
if ($tagId > 0) {
$whereSql .= " AND p.log_Tag LIKE '%{$tagId}%'";
} elseif (!empty($preferredTags)) {
$tagConditions = array();
foreach ($preferredTags as $pt) {
$tagConditions[] = "p.log_Tag LIKE '%{$pt}%'";
}
$whereSql .= " AND (" . implode(' OR ', $tagConditions) . ")";
}
if (!empty($search)) {
@@ -282,8 +258,8 @@ function recipe_list() {
$countResult = $zbp->db->Query($countSql);
$total = (int) ($countResult[0]['total'] ?? 0);
$selectFields = "p.log_ID, p.log_Title, p.log_Intro, p.log_CateID, p.log_Tag, p.log_PostTime, p.log_ViewNums, p.log_CommNums, p.log_Meta, p.log_Content, c.cate_Name";
$sql = "SELECT $selectFields FROM $tablePost p LEFT JOIN $tableCategory c ON p.log_CateID = c.cate_ID $whereSql ORDER BY p.log_PostTime DESC LIMIT $offset, $limit";
$selectFields = "p.log_ID, p.log_Title, p.log_Intro, p.log_CateID, p.log_Tag, p.log_PostTime, p.log_ViewNums, p.log_CommNums, p.log_Meta, p.log_Content, c.cate_Name, COALESCE(s.rate_nums, 0) as rate_nums, COALESCE(s.rate_score, 0) as rate_score";
$sql = "SELECT $selectFields FROM $tablePost p LEFT JOIN $tableCategory c ON p.log_CateID = c.cate_ID LEFT JOIN $tablePostStat s ON p.log_ID = s.log_id $whereSql ORDER BY p.log_PostTime DESC LIMIT $offset, $limit";
$results = $zbp->db->Query($sql);
$list = array();
@@ -302,7 +278,11 @@ function recipe_list() {
'comment_count' => (int) ($row['log_CommNums'] ?? 0),
'meta' => $meta,
'url' => '?id=' . $row['log_ID'],
'cover' => extract_cover_from_content($row['log_Content'] ?? '')
'cover' => extract_cover_from_content($row['log_Content'] ?? ''),
'rating' => ApiResponse::getRatingSummary(
(float) ($row['rate_score'] ?? 0),
(int) ($row['rate_nums'] ?? 0)
)
);
}
@@ -313,35 +293,11 @@ function recipe_list() {
'list' => $list,
'page' => $page,
'limit' => $limit,
'total' => $total,
'filter' => array(
'preferred_tags' => $preferredTags,
'preferred_categories' => $preferredCategories
)
'total' => $total
)
);
}
function load_user_preference_data($userId) {
$cacheDir = dirname(__FILE__) . '/cache/preference/';
$file = $cacheDir . 'pref_' . md5($userId) . '.json';
if (file_exists($file)) {
$content = file_get_contents($file);
$data = json_decode($content, true);
return is_array($data) ? $data : array(
'preferred_tags' => array(),
'preferred_categories' => array(),
'blocked_allergens' => array()
);
}
return array(
'preferred_tags' => array(),
'preferred_categories' => array(),
'blocked_allergens' => array()
);
}
/**
* 获取菜谱详情
*/
@@ -358,13 +314,27 @@ function recipe_detail() {
return array('code' => 404, 'message' => '❌ 菜谱不存在');
}
// 增加浏览量
if (isset($_GET['viewnums']) && $_GET['viewnums'] === 'true') {
$post->ViewNums += 1;
$sql = $zbp->db->sql->Update($zbp->table['Post'], array('log_ViewNums' => $post->ViewNums), array(array('=', 'log_ID', $post->ID)));
$zbp->db->Update($sql);
}
$tableRecipeIdMap = $zbp->db->dbpre . 'recipe_id_map';
$tablePostStat = $zbp->db->dbpre . 'post_stat';
$idMapSql = "SELECT old_id FROM $tableRecipeIdMap WHERE new_log_id = $id LIMIT 1";
$idMapResult = $zbp->db->Query($idMapSql);
$picId = !empty($idMapResult) ? (int) $idMapResult[0]['old_id'] : null;
$statSql = "SELECT rate_nums, rate_score FROM $tablePostStat WHERE log_id = $id LIMIT 1";
$statResult = $zbp->db->Query($statSql);
$rateNums = 0;
$rateScore = 0.00;
if (!empty($statResult)) {
$rateNums = (int) ($statResult[0]['rate_nums'] ?? 0);
$rateScore = (float) ($statResult[0]['rate_score'] ?? 0);
}
$meta = json_decode($post->Meta, true) ?: array();
$ingredients = get_recipe_ingredients($id);
@@ -373,6 +343,7 @@ function recipe_detail() {
'message' => 'success',
'data' => array(
'id' => $post->ID,
'pic_id' => $picId,
'title' => $post->Title,
'content' => $post->Content,
'intro' => $post->Intro,
@@ -391,7 +362,123 @@ function recipe_detail() {
'id' => $post->Author->ID,
'name' => $post->Author->Name,
'avatar' => $post->Author->Avatar
) : null
) : null,
'rating' => ApiResponse::formatRating($rateScore, $rateNums)
)
);
}
/**
* 获取菜谱迷你信息
* 返回简化的核心信息code, pic_id, title, intro, category, allergens, nutrition
* 适用于列表页、卡片展示等需要快速加载的场景
* @return array 迷你版菜谱信息
*/
function recipe_mini() {
global $zbp;
$id = (int) ($_GET['id'] ?? 0);
if ($id <= 0) {
return array('code' => 400, 'message' => '缺少菜谱ID参数');
}
$tablePost = $zbp->db->dbpre . 'post';
$tableCategory = $zbp->db->dbpre . 'category';
$tableRecipeIdMap = $zbp->db->dbpre . 'recipe_id_map';
$tableRecipeIngredient = $zbp->db->dbpre . 'recipe_ingredient';
$tableIngredientDetail = $zbp->db->dbpre . 'ingredient_detail';
$tableRecipeNutrition = $zbp->db->dbpre . 'recipe_nutrition';
$tablePostStat = $zbp->db->dbpre . 'post_stat';
$sql = "SELECT p.log_ID, p.log_Title, p.log_Intro, p.log_CateID,
c.cate_Name as cate_name, c.cate_ParentID as cate_parent_id,
COALESCE(s.rate_nums, 0) as rate_nums, COALESCE(s.rate_score, 0) as rate_score
FROM $tablePost p
LEFT JOIN $tableCategory c ON p.log_CateID = c.cate_ID
LEFT JOIN $tablePostStat s ON p.log_ID = s.log_id
WHERE p.log_ID = $id AND p.log_Type = 0 AND p.log_Status = 0";
$result = $zbp->db->Query($sql);
if (empty($result)) {
return array('code' => 404, 'message' => '菜谱不存在');
}
$row = $result[0];
$picId = null;
$idMapSql = "SELECT old_id FROM $tableRecipeIdMap WHERE new_log_id = $id LIMIT 1";
$idMapResult = $zbp->db->Query($idMapSql);
if (!empty($idMapResult)) {
$picId = (int) $idMapResult[0]['old_id'];
}
$allergenSql = "SELECT DISTINCT id.allergen
FROM $tableRecipeIngredient ri
INNER JOIN $tableIngredientDetail id ON ri.detail_id = id.ingredient_id
WHERE ri.log_id = $id AND id.allergen IS NOT NULL AND id.allergen != '' AND id.allergen != '[]'";
$allergenResults = $zbp->db->Query($allergenSql);
$allergens = array();
foreach ($allergenResults as $aRow) {
$aList = json_decode($aRow['allergen'] ?? '[]', true);
if (is_array($aList)) {
$allergens = array_merge($allergens, $aList);
}
}
$allergens = array_values(array_unique($allergens));
$nutritionSql = "SELECT name, value, unit FROM $tableRecipeNutrition WHERE log_id = $id";
$nutritionResults = $zbp->db->Query($nutritionSql);
$nutrition = array();
foreach ($nutritionResults as $nRow) {
$nutrition[] = array(
'name' => $nRow['name'],
'value' => (float) $nRow['value'],
'unit' => $nRow['unit']
);
}
$categoryHierarchy = array();
$currentCateId = (int) $row['cate_parent_id'];
while ($currentCateId > 0) {
$cateSql = "SELECT cate_ID, cate_Name, cate_ParentID FROM $tableCategory WHERE cate_ID = $currentCateId LIMIT 1";
$cateResult = $zbp->db->Query($cateSql);
if (!empty($cateResult)) {
$cateRow = $cateResult[0];
array_unshift($categoryHierarchy, array(
'id' => (int) $cateRow['cate_ID'],
'name' => $cateRow['cate_Name']
));
$currentCateId = (int) $cateRow['cate_ParentID'];
} else {
break;
}
}
$categoryHierarchy[] = array(
'id' => (int) $row['log_CateID'],
'name' => $row['cate_name']
);
return array(
'code' => 200,
'message' => 'success',
'data' => array(
'id' => (int) $row['log_ID'],
'code' => 'CP' . str_pad($row['log_ID'], 5, '0', STR_PAD_LEFT),
'pic_id' => $picId,
'title' => $row['log_Title'],
'intro' => $row['log_Intro'],
'category' => array(
'id' => (int) $row['log_CateID'],
'name' => $row['cate_name'],
'hierarchy' => $categoryHierarchy
),
'allergens' => $allergens,
'nutrition' => $nutrition,
'rating' => ApiResponse::getRatingSummary(
(float) ($row['rate_score'] ?? 0),
(int) ($row['rate_nums'] ?? 0)
)
)
);
}
@@ -428,8 +515,8 @@ function recipe_full() {
m.mem_ID as author_id, m.mem_Name as author_name, m.mem_Alias as author_alias,
m.mem_Email as author_email, m.mem_HomePage as author_homepage,
COALESCE(s.like_nums, 0) as like_nums,
COALESCE(s.recommend_nums, 0) as recommend_nums,
COALESCE(s.recommend_score, 0) as recommend_score
COALESCE(s.rate_nums, 0) as rate_nums,
COALESCE(s.rate_score, 0) as rate_score
FROM $tablePost p
LEFT JOIN $tableCategory c ON p.log_CateID = c.cate_ID
LEFT JOIN $tableMember m ON p.log_AuthorID = m.mem_ID
@@ -626,8 +713,12 @@ function recipe_full() {
'view_count' => (int) ($row['log_ViewNums'] ?? 0),
'comment_count' => (int) ($row['log_CommNums'] ?? 0),
'like_count' => (int) ($row['like_nums'] ?? 0),
'recommend_count' => (int) ($row['recommend_nums'] ?? 0),
'recommend_score' => (float) ($row['recommend_score'] ?? 0)
'rate_count' => (int) ($row['rate_nums'] ?? 0),
'rate_score' => (float) ($row['rate_score'] ?? 0)
),
'rating' => ApiResponse::formatRating(
(float) ($row['rate_score'] ?? 0),
(int) ($row['rate_nums'] ?? 0)
),
'meta' => array(
'indices' => $meta['indices'] ?? array(),
@@ -650,21 +741,11 @@ function ingredient_list() {
$cateId = (int) ($_GET['cate_id'] ?? 0);
$search = trim($_GET['search'] ?? '');
$author = trim($_GET['author'] ?? '');
$userId = trim($_GET['user_id'] ?? '');
$usePreference = isset($_GET['use_preference']) && $_GET['use_preference'] === 'true';
if ($limit > 100) $limit = 100;
if ($limit < 1) $limit = 20;
if ($page < 1) $page = 1;
$blockedAllergens = array();
if (isset($_GET['blocked_allergens'])) {
$blockedAllergens = array_filter(explode(',', $_GET['blocked_allergens']));
} elseif ($usePreference && !empty($userId)) {
$preference = load_user_preference_data($userId);
$blockedAllergens = $preference['blocked_allergens'] ?? array();
}
$table = $zbp->db->dbpre . 'ingredient_detail';
$offset = ($page - 1) * $limit;
@@ -691,24 +772,9 @@ function ingredient_list() {
$results = $zbp->db->Query($sql);
$list = array();
$filtered = 0;
foreach ($results as $row) {
$allergenType = json_decode($row['allergen_type'] ?? '[]', true);
if (!empty($blockedAllergens) && !empty($allergenType)) {
$hasBlocked = false;
foreach ($allergenType as $type) {
if (in_array($type, $blockedAllergens)) {
$hasBlocked = true;
$filtered++;
break;
}
}
if ($hasBlocked) {
continue;
}
}
$item = array(
'id' => (int) $row['ingredient_id'],
'name' => $row['name'],
@@ -732,9 +798,7 @@ function ingredient_list() {
'list' => $list,
'page' => $page,
'limit' => $limit,
'total' => $total,
'filtered' => $filtered,
'blocked_allergens' => $blockedAllergens
'total' => $total
)
);
}