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(
|
||||
|
||||
@@ -1,11 +1,27 @@
|
||||
# 菜谱 API 接口文档
|
||||
|
||||
> **版本**: v3.2.0
|
||||
> **更新日期**: 2026-04-12
|
||||
> **版本**: v3.2.1
|
||||
> **更新日期**: 2026-04-13
|
||||
> **基础地址**: `http://eat.wktyl.com/api/`
|
||||
|
||||
---
|
||||
|
||||
## 📝 更新日志
|
||||
|
||||
### v3.2.1 (2026-04-13)
|
||||
- **文档同步更新**:完善发现页接口字段说明
|
||||
- **分类字段补充**:添加 `id` 字段到分类返回结构
|
||||
|
||||
### v3.2.0 (2026-04-12)
|
||||
- **发现页接口优化**:`api_discover.php` 多项改进
|
||||
- 去重机制:同一客户端多次请求返回不重复数据
|
||||
- 自动重置:30分钟后自动重置
|
||||
- 混合分类:菜谱分类60% + 食材分类40%
|
||||
- 新增字段:`recipe_count`、`ingredient_count`、`parent_name`
|
||||
- 标签 `count` 字段修复
|
||||
|
||||
---
|
||||
|
||||
|
||||
## 📁 接口文件说明
|
||||
|
||||
@@ -1588,10 +1604,17 @@ Content-Type: application/json
|
||||
```
|
||||
GET api_discover.php?total=30
|
||||
GET api_discover.php?recipe=8&ingredient=5&category=4&tag=6&nutrition=4&meal_time=3
|
||||
GET api_discover.php?total=30&_refresh=1
|
||||
```
|
||||
|
||||
**功能**: 获取随机数据用于首页/发现页瀑布流展示
|
||||
|
||||
**核心特性**:
|
||||
- ✅ **去重机制**:同一客户端多次请求返回不重复数据(基于IP记录已返回ID)
|
||||
- ✅ **自动重置**:30分钟后自动重置,重新开始展示
|
||||
- ✅ **混合分类**:分类返回混合类型(菜谱分类60% + 食材分类40%)
|
||||
- ✅ **完整信息**:分类包含父分类名称,标签包含实际使用次数
|
||||
|
||||
**请求参数**:
|
||||
|
||||
| 参数 | 类型 | 默认值 | 说明 |
|
||||
@@ -1603,7 +1626,7 @@ GET api_discover.php?recipe=8&ingredient=5&category=4&tag=6&nutrition=4&meal_tim
|
||||
| tag | int | - | 标签数量 |
|
||||
| nutrition | int | - | 营养成分数量 |
|
||||
| meal_time | int | - | 时段数量 |
|
||||
| _refresh | int | 0 | 强制刷新缓存 |
|
||||
| _refresh | int | 0 | 强制刷新(清除已返回ID记录) |
|
||||
|
||||
**数量分配比例**(使用 total 参数时):
|
||||
|
||||
@@ -1622,12 +1645,12 @@ GET api_discover.php?recipe=8&ingredient=5&category=4&tag=6&nutrition=4&meal_tim
|
||||
|------|------|
|
||||
| `recipes` | 随机菜品列表 |
|
||||
| `ingredients` | 随机食材列表 |
|
||||
| `categories` | 随机分类列表 |
|
||||
| `categories` | 随机分类列表(混合类型) |
|
||||
| `tags` | 随机标签列表 |
|
||||
| `nutrition_types` | 营养成分类型列表 |
|
||||
| `meal_times` | 用餐时段列表 |
|
||||
| `meta.request_count` | 当前IP在5秒内的请求次数 |
|
||||
| `meta.cache_status` | 缓存状态:fresh/cached |
|
||||
| `meta.cache_status` | 缓存状态:fresh |
|
||||
| `meta.counts` | 各类型实际返回数量 |
|
||||
|
||||
**菜品字段**:
|
||||
|
||||
@@ -1652,12 +1675,27 @@ GET api_discover.php?recipe=8&ingredient=5&category=4&tag=6&nutrition=4&meal_tim
|
||||
| `allergen` | 过敏原数组 |
|
||||
| `intro` | 简介(截取100字) |
|
||||
|
||||
**缓存策略**:
|
||||
**分类字段**(新增):
|
||||
|
||||
| 请求次数 | 返回数据 | 说明 |
|
||||
|----------|----------|------|
|
||||
| 第1-2次 | 新鲜数据 | 查询数据库 |
|
||||
| 第3次及以后 | 缓存数据 | 返回缓存(5秒TTL) |
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `id` | int | 分类ID |
|
||||
| `name` | string | 分类名称 |
|
||||
| `type` | string | 类型:recipe(菜谱分类) / ingredient(食材分类) / recipe_main(食谱大类) |
|
||||
| `recipe_count` | int | 该分类下的菜品数量 |
|
||||
| `ingredient_count` | int | 该分类下的食材数量 |
|
||||
| `count` | int | 根据类型自动选择:ingredient显示食材数,recipe显示菜品数 |
|
||||
| `parent_id` | int | 父分类ID |
|
||||
| `parent_name` | string | 父分类名称 |
|
||||
|
||||
**标签字段**:
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `id` | int | 标签ID |
|
||||
| `name` | string | 标签名称 |
|
||||
| `type` | string | 类型:taste(口味) / cooking(工艺) |
|
||||
| `count` | int | 使用次数(已修复) |
|
||||
|
||||
**返回示例**:
|
||||
```json
|
||||
@@ -1669,10 +1707,10 @@ GET api_discover.php?recipe=8&ingredient=5&category=4&tag=6&nutrition=4&meal_tim
|
||||
{
|
||||
"id": 26940,
|
||||
"title": "酸菜蛇段汤",
|
||||
"cover": "http://eat.wktyl.com/zb_users/upload/1605a.jpg",
|
||||
"cover": "http://eat.wktyl.com/api/assets/pic/1030a.jpg",
|
||||
"category": {"id": 175, "name": "汤类"},
|
||||
"rating": {"score": 4.5, "nums": 128, "display": "4.5分 (128人评分)", "status": "sufficient", "level": "推荐", "star": 5},
|
||||
"views": 1024
|
||||
"rating": {"score": 0, "nums": 0, "display": "暂无评分", "star": 0},
|
||||
"views": 0
|
||||
}
|
||||
],
|
||||
"ingredients": [
|
||||
@@ -1685,20 +1723,39 @@ GET api_discover.php?recipe=8&ingredient=5&category=4&tag=6&nutrition=4&meal_tim
|
||||
}
|
||||
],
|
||||
"categories": [
|
||||
{"id": 12, "name": "中国菜", "type": "recipe_main", "count": 1234, "parent_id": 11}
|
||||
{
|
||||
"id": 42,
|
||||
"name": "家常菜",
|
||||
"type": "recipe",
|
||||
"recipe_count": 4581,
|
||||
"ingredient_count": 0,
|
||||
"count": 4581,
|
||||
"parent_id": 12,
|
||||
"parent_name": "中国菜"
|
||||
},
|
||||
{
|
||||
"id": 1150,
|
||||
"name": "杭椒",
|
||||
"type": "ingredient",
|
||||
"recipe_count": 0,
|
||||
"ingredient_count": 1,
|
||||
"count": 1,
|
||||
"parent_id": 1001,
|
||||
"parent_name": "蔬菜类"
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
{"id": 1, "name": "麻辣", "type": "taste", "count": 567}
|
||||
{"id": 1, "name": "咸鲜味", "type": "taste", "count": 12973},
|
||||
{"id": 50, "name": "炒", "type": "cooking", "count": 2053}
|
||||
],
|
||||
"nutrition_types": [
|
||||
{"name": "蛋白质", "unit": "克"}
|
||||
],
|
||||
"meal_times": [
|
||||
{"id": 1, "name": "早餐", "count": 1234}
|
||||
{"id": 1, "name": "中餐", "count": 481}
|
||||
]
|
||||
},
|
||||
"meta": {
|
||||
"request_count": 1,
|
||||
"cache_status": "fresh",
|
||||
"counts": {"recipe": 8, "ingredient": 5, "category": 4, "tag": 6, "nutrition": 4, "meal_time": 3}
|
||||
},
|
||||
@@ -1711,6 +1768,28 @@ GET api_discover.php?recipe=8&ingredient=5&category=4&tag=6&nutrition=4&meal_tim
|
||||
- 📱 响应式布局展示
|
||||
- 🎨 动态卡片生成
|
||||
- 🔄 下拉刷新加载
|
||||
- 📂 分类导航(显示父分类名称)
|
||||
|
||||
**客户端实现**:
|
||||
```dart
|
||||
// Flutter 示例
|
||||
final response = await http.get(
|
||||
Uri.parse('$baseUrl/api_discover.php?total=30')
|
||||
);
|
||||
final data = json.decode(response.body)['data'];
|
||||
|
||||
// 显示分类卡片
|
||||
Widget buildCategoryCard(Map<String, dynamic> category) {
|
||||
return Card(
|
||||
child: Column(
|
||||
children: [
|
||||
Text('${category['parent_name']} > ${category['name']}'),
|
||||
Text('${category['count']} 个${category['type'] == 'ingredient' ? '食材' : '菜谱'}'),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,81 +1,27 @@
|
||||
# App 接入指南
|
||||
|
||||
> **版本**: v2.9.0
|
||||
> **更新日期**: 2026-04-12
|
||||
> **版本**: v2.10.1
|
||||
> **更新日期**: 2026-04-13
|
||||
> **基础地址**: `http://eat.wktyl.com/api/`
|
||||
|
||||
---
|
||||
|
||||
## 📝 更新日志
|
||||
|
||||
### v2.9.0 (2026-04-12)
|
||||
- **新增发现页接口**:`api_discover.php` 随机数据接口
|
||||
- 支持返回多种类型随机数据:菜品、食材、分类、标签、营养成分、时段
|
||||
- 支持分类数量参数和总数量自动分配
|
||||
- 基于IP的请求频率控制:5秒内前2次返回新鲜数据,第3次起返回缓存
|
||||
- 适用于首页/发现页瀑布流、响应式布局
|
||||
### v2.10.1 (2026-04-13)
|
||||
- **文档同步更新**:与 API_DOC.md 保持一致
|
||||
- **分类字段完善**:补充 `id` 字段说明
|
||||
|
||||
### v2.8.0 (2026-04-12)
|
||||
- **评分显示优化**:所有菜谱接口返回数据中新增 `rating` 字段
|
||||
- 自动处理边缘情况:暂无评分、评分数量少、评分异常
|
||||
- 提供格式化显示文本,可直接用于UI展示
|
||||
- 提供评分状态和等级,便于客户端差异化展示
|
||||
- **热门排行接口更新**:`sort` 参数从 `recommend` 改为 `rate`
|
||||
### v2.10.0 (2026-04-12)
|
||||
- **发现页接口优化**:`api_discover.php` 多项改进
|
||||
- 同一客户端多次请求返回不重复数据(基于IP记录已返回ID)
|
||||
- 30分钟后自动重置,重新开始展示
|
||||
- 分类返回混合类型:菜谱分类60% + 食材分类40%
|
||||
- 分类新增字段:`recipe_count`(菜品数)、`ingredient_count`(食材数)、`parent_name`(父分类名称)
|
||||
- 标签 `count` 字段已修复,显示实际使用次数
|
||||
- 数据库统计字段已更新:`cate_Count`、`tag_Count`
|
||||
|
||||
### v2.7.0 (2026-04-12)
|
||||
- **接口变更**:`recommend` 接口改为 `rate` 评分接口
|
||||
- 评分范围:1-5分
|
||||
- 每日限制:每个IP每天最多30次
|
||||
- 不可取消:评分后无法撤销
|
||||
- 自动计算平均分
|
||||
- 数据库字段更新:`recommend_nums`/`recommend_score` 改为 `rate_nums`/`rate_score`
|
||||
- 新增本地评分日志:记录IP、时间、分数到本地文件
|
||||
|
||||
### v2.6.0 (2026-04-12)
|
||||
- 新增接口:`api_hot.php` 热门排行接口
|
||||
|
||||
### v2.5.0 (2026-04-12)
|
||||
- 文档更新:简化客户端代码示例为1-2行
|
||||
- 新增接口描述:`api_action.php?act=ip_status` IP状态查询
|
||||
- 新增接口描述:`api_feed.php?act=feed` 信息流接口
|
||||
- 新增接口描述:`cache_manage.php` 缓存管理接口
|
||||
- 修正接口描述:随机推荐使用 `filter_apply&count=1` 而非 `random`
|
||||
- 修正接口描述:热门排行使用 `api_hot.php` 替代 `stats_full.php?act=hot`
|
||||
- 删除不存在的接口描述
|
||||
|
||||
### v2.4.0 (2026-04-12)
|
||||
- 筛选接口新增排除筛选参数:
|
||||
- `exclude_category`/`exclude_category_name` 排除分类
|
||||
- `exclude_taste`/`exclude_taste_name` 排除口味
|
||||
- `exclude_cooking`/`exclude_cooking_name` 排除工艺
|
||||
- `exclude_ingredient` 排除食材
|
||||
- `exclude_allergen` 排除过敏原
|
||||
- `exclude_author` 排除作者
|
||||
- `exclude_meal_time` 排除用餐时段
|
||||
|
||||
### v2.3.0 (2026-04-12)
|
||||
- 新增迷你版接口 (api.php?act=mini),适用于列表页快速加载
|
||||
- 筛选接口新增高级筛选参数:
|
||||
- `nutrition_name`/`nutrition_min`/`nutrition_max` 按营养成分筛选
|
||||
- `allergen` 按过敏原筛选(排除含该过敏原的菜品)
|
||||
- `ingredient` 按食材名称筛选
|
||||
- `author_id` 按作者筛选
|
||||
- `category_name` 按分类名称筛选
|
||||
- `taste_name`/`cooking_name` 按口味/工艺名称筛选
|
||||
|
||||
### v2.2.0 (2026-04-12)
|
||||
- 新增全局搜索接口 (api_filter.php?act=global_search)
|
||||
- 支持搜索食谱、食材、口味标签、工艺标签
|
||||
- 搜索结果包含所属分类、关联菜品数量等信息
|
||||
- 支持模糊搜索和关键词高亮
|
||||
|
||||
### v2.1.0 (2026-04-12)
|
||||
- 删除个性化推荐相关接口 (api_preference.php)
|
||||
- 删除 api_feed.php 中的 personal 接口
|
||||
- 删除 api.php 中的 use_preference 参数
|
||||
- 更新推荐算法说明
|
||||
|
||||
---
|
||||
|
||||
## 一、接口文件说明
|
||||
|
||||
@@ -223,6 +169,11 @@ Widget buildRating(Map<String, dynamic> rating) {
|
||||
|
||||
用于首页/发现页瀑布流展示,返回多种类型的随机数据。
|
||||
|
||||
**特性**:
|
||||
- 同一客户端多次请求返回不重复数据(基于IP记录已返回ID)
|
||||
- 30分钟后自动重置,重新开始展示
|
||||
- 分类返回混合类型:菜谱分类60% + 食材分类40%
|
||||
|
||||
**请求示例**:
|
||||
```dart
|
||||
// 获取30条随机数据(自动分配)
|
||||
@@ -234,8 +185,74 @@ final response = await http.get(
|
||||
final response = await http.get(
|
||||
Uri.parse('$baseUrl/api_discover.php?recipe=8&ingredient=5&category=4&tag=6&nutrition=4&meal_time=3')
|
||||
);
|
||||
|
||||
// 强制刷新(清除已返回ID记录)
|
||||
final response = await http.get(
|
||||
Uri.parse('$baseUrl/api_discover.php?total=30&_refresh=1')
|
||||
);
|
||||
```
|
||||
|
||||
**返回数据结构**:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "success",
|
||||
"data": {
|
||||
"recipes": [...],
|
||||
"ingredients": [...],
|
||||
"categories": [
|
||||
{
|
||||
"id": 42,
|
||||
"name": "家常菜",
|
||||
"type": "recipe",
|
||||
"recipe_count": 4581,
|
||||
"ingredient_count": 0,
|
||||
"count": 4581,
|
||||
"parent_id": 12,
|
||||
"parent_name": "中国菜"
|
||||
},
|
||||
{
|
||||
"id": 1150,
|
||||
"name": "杭椒",
|
||||
"type": "ingredient",
|
||||
"recipe_count": 0,
|
||||
"ingredient_count": 1,
|
||||
"count": 1,
|
||||
"parent_id": 1001,
|
||||
"parent_name": "蔬菜类"
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "咸鲜味",
|
||||
"type": "taste",
|
||||
"count": 12973
|
||||
},
|
||||
{
|
||||
"id": 50,
|
||||
"name": "炒",
|
||||
"type": "cooking",
|
||||
"count": 2053
|
||||
}
|
||||
],
|
||||
"nutrition_types": [...],
|
||||
"meal_times": [...]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**分类字段说明**:
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `type` | string | 分类类型:recipe(菜谱分类) / ingredient(食材分类) / recipe_main(食谱大类) |
|
||||
| `recipe_count` | int | 该分类下的菜品数量 |
|
||||
| `ingredient_count` | int | 该分类下的食材数量 |
|
||||
| `count` | int | 根据类型自动选择:ingredient显示食材数,recipe显示菜品数 |
|
||||
| `parent_name` | string | 父分类名称,可直接显示为"父分类 > 子分类" |
|
||||
|
||||
**Flutter 瀑布流示例**:
|
||||
```dart
|
||||
Widget buildDiscoverPage(Map<String, dynamic> data) {
|
||||
@@ -251,9 +268,13 @@ Widget buildDiscoverPage(Map<String, dynamic> data) {
|
||||
items.add(IngredientCard(ingredient: ingredient));
|
||||
}
|
||||
|
||||
// 分类卡片
|
||||
// 分类卡片(显示父分类名称)
|
||||
for (final category in data['categories']) {
|
||||
items.add(CategoryCard(category: category));
|
||||
items.add(CategoryCard(
|
||||
category: category,
|
||||
subtitle: '${category['parent_name']} > ${category['name']}',
|
||||
count: category['count'],
|
||||
));
|
||||
}
|
||||
|
||||
// 标签卡片
|
||||
|
||||
322
docs/api/doc/api_discover
Normal file
322
docs/api/doc/api_discover
Normal file
@@ -0,0 +1,322 @@
|
||||
https://eat.wktyl.com/api/api_discover.php?recipe=8&ingredient=5&category=4&tag=6&nutrition=4&meal_time=3
|
||||
|
||||
{
|
||||
"code": 200,
|
||||
"message": "success",
|
||||
"data": {
|
||||
"recipes": [
|
||||
{
|
||||
"id": 40420,
|
||||
"title": "旱蒸酸菜鸭",
|
||||
"cover": "http://eat.wktyl.com/api/assets/pic/15303a.jpg",
|
||||
"category": {
|
||||
"id": 43,
|
||||
"name": "私家菜"
|
||||
},
|
||||
"rating": {
|
||||
"score": 0,
|
||||
"nums": 0,
|
||||
"display": "暂无评分",
|
||||
"star": 0
|
||||
},
|
||||
"views": 0
|
||||
},
|
||||
{
|
||||
"id": 56308,
|
||||
"title": "炸山鸡球",
|
||||
"cover": "",
|
||||
"category": {
|
||||
"id": 21,
|
||||
"name": "京菜"
|
||||
},
|
||||
"rating": {
|
||||
"score": 0,
|
||||
"nums": 0,
|
||||
"display": "暂无评分",
|
||||
"star": 0
|
||||
},
|
||||
"views": 0
|
||||
},
|
||||
{
|
||||
"id": 44289,
|
||||
"title": "八宝鸡汤",
|
||||
"cover": "http://eat.wktyl.com/api/assets/pic/19248a.jpg",
|
||||
"category": {
|
||||
"id": 78,
|
||||
"name": "气血双补食谱"
|
||||
},
|
||||
"rating": {
|
||||
"score": 0,
|
||||
"nums": 0,
|
||||
"display": "暂无评分",
|
||||
"star": 0
|
||||
},
|
||||
"views": 0
|
||||
},
|
||||
{
|
||||
"id": 56918,
|
||||
"title": "金枪鱼芸豆色拉",
|
||||
"cover": "http://eat.wktyl.com/api/assets/pic/26164a.jpg",
|
||||
"category": {
|
||||
"id": 242,
|
||||
"name": "西餐其他"
|
||||
},
|
||||
"rating": {
|
||||
"score": 0,
|
||||
"nums": 0,
|
||||
"display": "暂无评分",
|
||||
"star": 0
|
||||
},
|
||||
"views": 0
|
||||
},
|
||||
{
|
||||
"id": 50247,
|
||||
"title": "荷兰豆饺",
|
||||
"cover": "http://eat.wktyl.com/api/assets/pic/25317a.jpg",
|
||||
"category": {
|
||||
"id": 241,
|
||||
"name": "快餐/主食"
|
||||
},
|
||||
"rating": {
|
||||
"score": 0,
|
||||
"nums": 0,
|
||||
"display": "暂无评分",
|
||||
"star": 0
|
||||
},
|
||||
"views": 0
|
||||
},
|
||||
{
|
||||
"id": 37723,
|
||||
"title": "椒盐拌花生米",
|
||||
"cover": "http://eat.wktyl.com/api/assets/pic/12514a.jpg",
|
||||
"category": {
|
||||
"id": 43,
|
||||
"name": "私家菜"
|
||||
},
|
||||
"rating": {
|
||||
"score": 0,
|
||||
"nums": 0,
|
||||
"display": "暂无评分",
|
||||
"star": 0
|
||||
},
|
||||
"views": 0
|
||||
},
|
||||
{
|
||||
"id": 49117,
|
||||
"title": "糟香汁卤肥鸡",
|
||||
"cover": "http://eat.wktyl.com/api/assets/pic/24016a.jpg",
|
||||
"category": {
|
||||
"id": 238,
|
||||
"name": "卤酱菜"
|
||||
},
|
||||
"rating": {
|
||||
"score": 0,
|
||||
"nums": 0,
|
||||
"display": "暂无评分",
|
||||
"star": 0
|
||||
},
|
||||
"views": 0
|
||||
},
|
||||
{
|
||||
"id": 48252,
|
||||
"title": "琉璃山药",
|
||||
"cover": "http://eat.wktyl.com/api/assets/pic/23228a.jpg",
|
||||
"category": {
|
||||
"id": 239,
|
||||
"name": "甜品/点心"
|
||||
},
|
||||
"rating": {
|
||||
"score": 0,
|
||||
"nums": 0,
|
||||
"display": "暂无评分",
|
||||
"star": 0
|
||||
},
|
||||
"views": 0
|
||||
}
|
||||
],
|
||||
"ingredients": [
|
||||
{
|
||||
"id": 56,
|
||||
"name": "黄豆芽",
|
||||
"category": {
|
||||
"id": 1057,
|
||||
"name": "黄豆芽"
|
||||
},
|
||||
"allergen": [
|
||||
"黄豆",
|
||||
"豆"
|
||||
],
|
||||
"intro": "豆芽是大豆经加工处理发出的嫩芽,是一种营养丰富的蔬菜。当今世界“天然”、“健康”食物引领风骚者之一,乃是源自我国的豆芽菜,特别是金灿灿的“如意菜”—黄豆芽。明人陈嶷曾有过赞美黄豆芽的诗句:“有彼物兮,..."
|
||||
},
|
||||
{
|
||||
"id": 490,
|
||||
"name": "鱼丸",
|
||||
"category": {
|
||||
"id": 1498,
|
||||
"name": "鱼丸"
|
||||
},
|
||||
"allergen": [
|
||||
"鱼"
|
||||
],
|
||||
"intro": "鱼丸是闽南、福州、广州一带经常烹制的传统食品。因为它味道鲜美,多吃不腻,可作点心配料,又可作汤,是沿海人们不可少的海味佳肴。"
|
||||
},
|
||||
{
|
||||
"id": 1262,
|
||||
"name": "酸黄瓜",
|
||||
"category": {
|
||||
"id": 2282,
|
||||
"name": "酸黄瓜"
|
||||
},
|
||||
"allergen": [],
|
||||
"intro": ""
|
||||
},
|
||||
{
|
||||
"id": 214,
|
||||
"name": "黑枣(无核)",
|
||||
"category": {
|
||||
"id": 1216,
|
||||
"name": "黑枣(无核)"
|
||||
},
|
||||
"allergen": [],
|
||||
"intro": "黑枣产各地山区,野生于山坡、谷地或栽培;分布于辽宁、河北、山东、陕西、中南及西南各地。 材质优良,可作一般用材;果实去涩生食或酿酒、制醋,含维生素丙,可提取供医用;种子入药,能消渴去热。"
|
||||
},
|
||||
{
|
||||
"id": 172,
|
||||
"name": "枣(干)",
|
||||
"category": {
|
||||
"id": 1174,
|
||||
"name": "枣(干)"
|
||||
},
|
||||
"allergen": [],
|
||||
"intro": "枣为鼠李科落叶灌木或小乔木植物枣树的成熟果实。我国栽培枣树范围极广,北边达到辽宁的锦州、北镇一带,以山东、河北、山西、陕西、甘肃、安徽、浙江产量最多。著名品种有金丝小枣,果实小,含糖量多,生产山东乐陵..."
|
||||
}
|
||||
],
|
||||
"categories": [
|
||||
{
|
||||
"id": 1219,
|
||||
"name": "椰蓉",
|
||||
"type": "ingredient",
|
||||
"recipe_count": 0,
|
||||
"ingredient_count": 1,
|
||||
"count": 1,
|
||||
"parent_id": 1173,
|
||||
"parent_name": "水果类及制品"
|
||||
},
|
||||
{
|
||||
"id": 101,
|
||||
"name": "婴儿食谱",
|
||||
"type": "recipe",
|
||||
"recipe_count": 64,
|
||||
"ingredient_count": 0,
|
||||
"count": 64,
|
||||
"parent_id": 98,
|
||||
"parent_name": "人群营养膳食"
|
||||
},
|
||||
{
|
||||
"id": 212,
|
||||
"name": "咽炎食谱",
|
||||
"type": "recipe",
|
||||
"recipe_count": 2,
|
||||
"ingredient_count": 0,
|
||||
"count": 2,
|
||||
"parent_id": 172,
|
||||
"parent_name": "疾病调理"
|
||||
},
|
||||
{
|
||||
"id": 38,
|
||||
"name": "滇黔菜",
|
||||
"type": "recipe",
|
||||
"recipe_count": 158,
|
||||
"ingredient_count": 0,
|
||||
"count": 158,
|
||||
"parent_id": 12,
|
||||
"parent_name": "中国菜"
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
{
|
||||
"id": 57,
|
||||
"name": "炸",
|
||||
"type": "cooking",
|
||||
"count": 624
|
||||
},
|
||||
{
|
||||
"id": 110,
|
||||
"name": "焗",
|
||||
"type": "cooking",
|
||||
"count": 70
|
||||
},
|
||||
{
|
||||
"id": 18,
|
||||
"name": "酸咸味",
|
||||
"type": "taste",
|
||||
"count": 212
|
||||
},
|
||||
{
|
||||
"id": 35,
|
||||
"name": "怪味",
|
||||
"type": "taste",
|
||||
"count": 67
|
||||
},
|
||||
{
|
||||
"id": 133,
|
||||
"name": "煎烹",
|
||||
"type": "cooking",
|
||||
"count": 17
|
||||
},
|
||||
{
|
||||
"id": 144,
|
||||
"name": "红外线烤",
|
||||
"type": "cooking",
|
||||
"count": 1
|
||||
}
|
||||
],
|
||||
"nutrition_types": [
|
||||
{
|
||||
"name": "维生素B",
|
||||
"unit": "微克"
|
||||
},
|
||||
{
|
||||
"name": "锌",
|
||||
"unit": "毫克"
|
||||
},
|
||||
{
|
||||
"name": "泛酸",
|
||||
"unit": "毫克"
|
||||
},
|
||||
{
|
||||
"name": "蛋白质",
|
||||
"unit": "克"
|
||||
}
|
||||
],
|
||||
"meal_times": [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "中餐",
|
||||
"count": 481
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "晚餐",
|
||||
"count": 475
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"name": "早餐",
|
||||
"count": 325
|
||||
}
|
||||
]
|
||||
},
|
||||
"meta": {
|
||||
"cache_status": "fresh",
|
||||
"counts": {
|
||||
"recipe": 8,
|
||||
"ingredient": 5,
|
||||
"category": 4,
|
||||
"tag": 6,
|
||||
"nutrition": 4,
|
||||
"meal_time": 3
|
||||
}
|
||||
},
|
||||
"_query_time": "2129.4ms"
|
||||
}
|
||||
@@ -375,24 +375,23 @@
|
||||
│ └───────────────────────┘ │
|
||||
│ │
|
||||
│ ── 🍳 烹饪助手 ── │
|
||||
│ ┌──┐ ┌──┐ ┌──┐ │
|
||||
│ │⏱️│ │📝│ │🧮│ │
|
||||
│ │计时│ │笔记│ │换算│ │
|
||||
│ │ ↓│ │ ↓│ │ ↓│ │
|
||||
│ └──┘ └──┘ └──┘ │
|
||||
│ ┌──┐ ┌──┐ │ ← GridView 2列布局
|
||||
│ │⏱️│ │📝│ │
|
||||
│ │计时│ │笔记│ │
|
||||
│ │ ↓│ │ ↓│ │
|
||||
│ │使用│ │使用│ ← 新增"使用工具"按钮
|
||||
│ └──┘ └──┘ │
|
||||
│ │
|
||||
│ ── 📋 规划管理 ── │
|
||||
│ ┌──┐ ┌──┐ ┌──┐ │
|
||||
│ │📅│ │🛒│ │⚖️│ │
|
||||
│ │菜单│ │购物│ │缩放│ │
|
||||
│ │ ↓│ │ ↓│ │ ↓│ │
|
||||
│ └──┘ └──┘ └──┘ │
|
||||
│ ┌──┐ ┌──┐ │
|
||||
│ │📅│ │🛒│ │
|
||||
│ │菜单│ │购物│ │
|
||||
│ └──┘ └──┘ │
|
||||
│ │
|
||||
│ ── 🏥 健康工具 ── │
|
||||
│ ┌──┐ ┌──┐ │
|
||||
│ │BMI│ │🌙│ │
|
||||
│ │计算│ │就寝│ │
|
||||
│ │ ↓│ │ ↓│ │
|
||||
│ └──┘ └──┘ │
|
||||
│ │
|
||||
│ ── 🏷️ 热门标签 ── │
|
||||
@@ -419,6 +418,7 @@
|
||||
| BMI计算 | `Get.toNamed` | BMI计算器 | `/bmi-calculator` | - |
|
||||
| 🌙 就寝提醒 | `Get.toNamed` | 就寝提醒页 | `/profile/bedtime-reminder` | - |
|
||||
| 热门标签 | `Get.toNamed` | 标签菜谱列表 | `/tag-recipe-list` | tagName, tagId, tagType |
|
||||
| 🎮 使用工具按钮 | `Get.toNamed` | 工具功能页 | 工具对应路由 | - |
|
||||
|
||||
### 🎨 美观问题
|
||||
| # | 问题 | 严重度 | 建议 |
|
||||
@@ -515,6 +515,112 @@
|
||||
|
||||
---
|
||||
|
||||
## ❤️ 收藏页 (FavoritesPage)
|
||||
|
||||
```
|
||||
┌─────────────────────────────┐
|
||||
│ ← [↑返回] 我的收藏 │
|
||||
├─────────────────────────────┤
|
||||
│ │
|
||||
│ ┌──┐ ┌──┐ │ ← GridView 2列布局
|
||||
│ │🖼️│ │🖼️│ │
|
||||
│ │菜│ │菜│ │
|
||||
│ │品│ │品│ │
|
||||
│ │1│ │2│ │
|
||||
│ └──┘ └──┘ │
|
||||
│ ┌──┐ ┌──┐ │
|
||||
│ │🖼️│ │🖼️│ │
|
||||
│ │菜│ │菜│ │
|
||||
│ │品│ │品│ │
|
||||
│ │3│ │4│ │
|
||||
│ └──┘ └──┘ │
|
||||
│ │
|
||||
└─────────────────────────────┘
|
||||
```
|
||||
|
||||
### 📄 页面视图文件
|
||||
- `lib/src/pages/profile/social/favorites_page.dart`(主页面)
|
||||
|
||||
### 🔗 跳转关系
|
||||
| 触发元素 | 跳转方式 | 目标页面 | 路由 | 传参 |
|
||||
|---------|---------|---------|------|------|
|
||||
| ← 返回 | `Get.back` | 上一个页面 | - | - |
|
||||
| 菜品卡片 | `Get.toNamed` | 菜品详情页 | `/recipe-detail` | id |
|
||||
|
||||
### 🎨 美观问题
|
||||
| # | 问题 | 严重度 | 建议 |
|
||||
|---|------|--------|------|
|
||||
| 1 | 空收藏状态缺少引导 | 🟡中 | 添加"去发现美食"按钮引导用户 |
|
||||
|
||||
### ⚡ 功能缺失
|
||||
| # | 功能 | 优先级 | 说明 |
|
||||
|---|------|--------|------|
|
||||
| 1 | 收藏分组 | P3 | 按分类/标签分组管理收藏 |
|
||||
| 2 | 收藏排序 | P3 | 按收藏时间/评分排序 |
|
||||
|
||||
---
|
||||
|
||||
## 📋 关于页面 (AboutPage)
|
||||
|
||||
```
|
||||
┌─────────────────────────────┐
|
||||
│ ← [↑返回] 关于 │
|
||||
├─────────────────────────────┤
|
||||
│ │
|
||||
│ ┌───────────────────────┐ │
|
||||
│ │ 🍳 妈妈厨房 │ │
|
||||
│ │ Version 0.92.4 │ │
|
||||
│ └───────────────────────┘ │
|
||||
│ │
|
||||
│ ── 应用信息 ── │
|
||||
│ ┌───────────────────────┐ │
|
||||
│ │ 📱 应用版本 0.92.4 │ │
|
||||
│ │ 📅 更新日期 2026-04 │ │
|
||||
│ │ 🏷️ 构建版本 92 │ │
|
||||
│ └───────────────────────┘ │
|
||||
│ │
|
||||
│ ── 联系我们 ── │
|
||||
│ ┌───────────────────────┐ │
|
||||
│ │ 💬 用户反馈 [→] │ │ ← [→意见反馈页]
|
||||
│ │ ⭐ 评价应用 [→] │ │
|
||||
│ │ 📧 联系邮箱 [→] │ │
|
||||
│ └───────────────────────┘ │
|
||||
│ │
|
||||
│ ── 法律信息 ── │
|
||||
│ ┌───────────────────────┐ │
|
||||
│ │ 📜 用户协议 [→] │ │
|
||||
│ │ 🔒 隐私政策 [→] │ │
|
||||
│ └───────────────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────┘
|
||||
```
|
||||
|
||||
### 📄 页面视图文件
|
||||
- `lib/src/pages/profile/about_page.dart`(主页面)
|
||||
|
||||
### 🔗 跳转关系
|
||||
| 触发元素 | 跳转方式 | 目标页面 | 路由 | 传参 |
|
||||
|---------|---------|---------|------|------|
|
||||
| ← 返回 | `Get.back` | 上一个页面 | - | - |
|
||||
| 💬 用户反馈 | `Get.toNamed` | 意见反馈页 | `/chat` | - |
|
||||
| ⭐ 评价应用 | 打开应用商店 | App Store | - | - |
|
||||
| 📧 联系邮箱 | 打开邮件客户端 | Mail App | - | - |
|
||||
| 📜 用户协议 | `Get.toNamed` | 用户协议页 | `/user-agreement` | - |
|
||||
| 🔒 隐私政策 | `Get.toNamed` | 隐私政策页 | `/privacy-policy` | - |
|
||||
|
||||
### 🎨 美观问题
|
||||
| # | 问题 | 严重度 | 建议 |
|
||||
|---|------|--------|------|
|
||||
| 1 | 应用图标区域可增加动画效果 | 🟢低 | 添加呼吸动画或渐变效果 |
|
||||
|
||||
### ⚡ 功能缺失
|
||||
| # | 功能 | 优先级 | 说明 |
|
||||
|---|------|--------|------|
|
||||
| 1 | 检查更新 | P2 | 检测新版本并提示更新 |
|
||||
| 2 | 更新日志 | P3 | 展示版本更新内容 |
|
||||
|
||||
---
|
||||
|
||||
## 🥬 食材详情页 (IngredientDetailPage)
|
||||
|
||||
```
|
||||
@@ -737,10 +843,10 @@
|
||||
| P1 | 下拉刷新 | 首页/发现 | 缺少下拉手势刷新 | 🔴待开发 |
|
||||
| P2 | 搜索历史 | 搜索页 | 无本地搜索记录 | 🔴待开发 |
|
||||
| P2 | 分页加载 | 多个列表页 | 分类浏览/标签列表无分页 | 🔴待开发 |
|
||||
| P2 | 相关推荐 | 详情页 | 缺少相关菜谱推荐 | 🔴待开发 |
|
||||
| P2 | 相关推荐 | 详情页 | 缺少相关菜谱推荐 | ✅v0.92.0 |
|
||||
| P2 | 烹饪模式 | 详情页 | 全屏步骤+计时器 | 🔴待开发 |
|
||||
| P2 | 过敏原警示 | 详情页 | 食材含过敏原时警告 | 🔴待开发 |
|
||||
| P2 | 营养可视化 | 详情页 | 环形图/进度条展示营养占比 | 🔴待开发 |
|
||||
| P2 | 过敏原警示 | 详情页 | 食材含过敏原时警告 | ✅v0.92.0 |
|
||||
| P2 | 营养可视化 | 详情页 | 环形图/进度条展示营养占比 | ✅v0.92.0 |
|
||||
| P3 | 排序筛选 | 列表页 | 按评分/浏览量/最新排序 | 🔴待开发 |
|
||||
| P3 | 评论系统 | 详情页 | 需后端支持 | 🔴需后端 |
|
||||
| P3 | 用户等级 | 个人中心 | 经验值+等级+徽章 | 🔴需后端 |
|
||||
@@ -755,72 +861,62 @@
|
||||
|
||||
| 接口文件 | Repository | 已用act | 状态 |
|
||||
|---------|-----------|---------|------|
|
||||
| `api.php` | RecipeRepository | list/detail/full/ingredients/ingredient_detail/search/categories/tags/stats/unified_list/unified_detail/unified_search/unified_hot/query | ✅ |
|
||||
| `api.php` | RecipeRepository | list/detail/full/ingredients/ingredient_detail/search/categories/tags/stats/unified_list/unified_detail/unified_search/unified_hot/query/mini | ✅ |
|
||||
| `api_action.php` | ActionRepository | like/rate/view/ip_status | ✅ |
|
||||
| `api_feed.php` | FeedRepository | recommend/latest/hot/prefetch | ✅ |
|
||||
| `api_filter.php` | RecipeRepository+SearchController | recipe_main_categories/taste_tags/cooking_tags/filter_recipes/global_search | ✅ |
|
||||
| `api_hot.php` | HotRepository(→stats_full.php?act=hot) | hot | ✅ |
|
||||
| `api_what_to_eat.php` | WhatToEatRepository | filter_apply/detail | ✅ |
|
||||
| `api_filter.php` | RecipeRepository+SearchController | recipe_main_categories/taste_tags/cooking_tags/filter_recipes/global_search/meal_times/recipe_sub_categories/ingredient_main_categories/ingredient_sub_categories/category_tags/filter_ingredients/ingredient_recipes | ✅ |
|
||||
| `api_hot.php` | HotRepository(→stats_full.php?act=hot) | hot(sort=view/like/rate) | ✅ |
|
||||
| `api_what_to_eat.php` | WhatToEatRepository | filter_apply/detail/filter_steps | ✅ |
|
||||
| `api_discover.php` | DiscoverRepository | 随机数据 | ✅ |
|
||||
| `stats_full.php` | StatsRepository | online/request/hot | ✅ |
|
||||
| 静态数据 | 未使用 | eating_times.json/nutrition_types.json/gmy.json | ❌未使用 |
|
||||
| `api_check_duplicate.php` | RecipeRepository | check_title/check_ingredient/check_step/check_content/check_all | ✅v0.92.0 |
|
||||
| `stats_full.php` | StatsRepository | online/request/hot/stats | ✅ |
|
||||
| 静态数据 | 部分使用 | eating_times.json(✅)/nutrition_types.json(✅)/gmy.json(✅) | ✅v0.92.0 |
|
||||
|
||||
### 📊 未使用API接口一览
|
||||
|
||||
> 注:以下接口在v0.92.0版本已大部分实现,仅保留少量待开发接口
|
||||
|
||||
| 接口 | act/参数 | 文档描述 | 当前状态 |
|
||||
|------|---------|---------|---------|
|
||||
| `api.php?act=mini` | mini | 迷你版菜谱信息(~1KB),适用于列表页快速加载 | ❌未调用 |
|
||||
| `api_filter.php?act=meal_times` | meal_times | 用餐时段列表(早餐/中餐/晚餐等) | ❌未调用 |
|
||||
| `api_filter.php?act=recipe_sub_categories` | recipe_sub_categories&parent_id= | 食谱子分类列表 | ❌未调用 |
|
||||
| `api_filter.php?act=ingredient_main_categories` | ingredient_main_categories | 食材大类列表 | ❌未调用 |
|
||||
| `api_filter.php?act=ingredient_sub_categories` | ingredient_sub_categories&parent_id= | 食材子分类列表 | ❌未调用 |
|
||||
| `api_filter.php?act=category_tags` | category_tags&category_id= | 指定分类下的口味+工艺标签 | ❌未调用 |
|
||||
| `api_filter.php` exclude_*参数 | exclude_category/taste/cooking/allergen等 | 排除筛选(排除分类/口味/工艺/过敏原) | ❌未调用 |
|
||||
| `api_filter.php` 高级筛选 | nutrition_name/min/max, allergen, ingredient, author_id | 营养范围/过敏原/食材/作者筛选 | ❌未调用 |
|
||||
| `api_what_to_eat.php?act=filter_steps` | filter_steps&category= | 获取筛选步骤和菜谱数量 | ❌未调用 |
|
||||
| `api_what_to_eat.php?act=detail&code=` | detail&code=CP032892 | 编码查询菜谱详情 | ❌未调用 |
|
||||
| `api_what_to_eat.php?act=detail&title=&fuzzy=1` | detail&title=&fuzzy=1 | 模糊标题搜索 | ❌未调用 |
|
||||
| `api_hot.php?sort=rate` | sort=rate | 按评分排序的热门排行 | ❌未调用 |
|
||||
| `api.php?act=unified_list&type=ingredient` | unified_list&type=ingredient | 统一格式食材列表 | ❌未调用 |
|
||||
| `api.php?act=unified_detail&type=ingredient` | unified_detail&type=ingredient | 统一格式食材详情 | ❌未调用 |
|
||||
| `api.php?act=unified_hot&type=ingredient` | unified_hot&type=ingredient | 统一格式食材热门 | ❌未调用 |
|
||||
| `eating_times.json` | 静态资源 | 34种用餐时段数据(标准/组合/频率/方法) | ❌未使用 |
|
||||
| `nutrition_types.json` | 静态资源 | 31种营养成分数据(含单位) | ❌未使用 |
|
||||
| `gmy.json` | 静态资源 | 585种过敏原数据(21大类) | ❌未使用 |
|
||||
| `api.php?act=unified_list&type=ingredient` | unified_list&type=ingredient | 统一格式食材列表 | 🔴待开发 |
|
||||
| `api.php?act=unified_detail&type=ingredient` | unified_detail&type=ingredient | 统一格式食材详情 | 🔴待开发 |
|
||||
| `api.php?act=unified_hot&type=ingredient` | unified_hot&type=ingredient | 统一格式食材热门 | 🔴待开发 |
|
||||
|
||||
---
|
||||
|
||||
### 🟢 已有API可直接开发(P2优先级)
|
||||
|
||||
| # | 功能 | API接口 | 数据源 | 页面位置 | 开发复杂度 | 说明 |
|
||||
> 注:以下功能在v0.92.0版本已全部实现
|
||||
|
||||
| # | 功能 | API接口 | 数据源 | 页面位置 | 开发复杂度 | 状态 |
|
||||
|---|------|---------|--------|---------|-----------|------|
|
||||
| 1 | 🕐 用餐时段推荐 | `api_filter.php?act=meal_times` | eating_times.json(34种) | 首页/工具中心 | ⭐⭐ | 根据当前时间智能推荐早/午/晚餐,首页瀑布流已有时段卡片但跳转搜索页,应改为时段专属推荐页 |
|
||||
| 2 | ⚠️ 过敏原警示 | `api.php?act=full` allergens字段 | gmy.json(585种) | 菜品详情页 | ⭐⭐ | 详情页显示过敏原警告,结合用户过敏原设置自动过滤,已有allergen字段但未展示 |
|
||||
| 3 | 📊 营养可视化 | `api.php?act=full` nutrition字段 | nutrition_types.json(31种) | 菜品详情页/营养中心 | ⭐⭐⭐ | 环形图/进度条展示营养占比,nutrition字段已有数据,需前端可视化组件 |
|
||||
| 4 | 🏆 评分排行榜 | `api_hot.php?type=recipe&sort=rate` | api_hot.php | 热门排行页 | ⭐ | 热门页已有Tab,增加"评分榜"排序选项,API已支持sort=rate |
|
||||
| 5 | 📱 迷你信息加载 | `api.php?act=mini&id=` | api.php | 列表页/卡片 | ⭐⭐ | 列表页使用mini接口(~1KB)替代detail(~10KB),10倍性能提升,需改造列表加载逻辑 |
|
||||
| 6 | 🔍 排除筛选 | `api_filter.php?act=filter_recipes` exclude_*参数 | api_filter.php | 高级搜索页 | ⭐ | 高级搜索页增加"排除"选项,如排除辣味/油炸等,API已支持7个exclude参数 |
|
||||
| 7 | 🌐 IP状态显示 | `api_action.php?act=ip_status` | api_action.php | 菜品详情页 | ⭐ | 评分前显示今日剩余评分次数,ActionRepository已封装fetchIpStatus()但UI未使用 |
|
||||
| 8 | 🏷️ 分类标签联动 | `api_filter.php?act=category_tags&category_id=` | api_filter.php | 分类浏览页/高级搜索 | ⭐⭐ | 选择分类后自动加载该分类下的口味+工艺标签,提升筛选体验 |
|
||||
| 9 | 🥗 食材分类浏览 | `api_filter.php?act=ingredient_main_categories/sub_categories` | api_filter.php | 发现页/工具中心 | ⭐⭐ | 食材大类→子类→食材列表三级浏览,当前发现页食材分类跳转分类浏览页但无食材专用分类 |
|
||||
| 10 | 📋 食谱子分类 | `api_filter.php?act=recipe_sub_categories&parent_id=` | api_filter.php | 分类浏览页 | ⭐ | 当前分类浏览使用api.php?act=categories,改用filter接口可获取更丰富的子分类数据 |
|
||||
| 11 | 🎲 筛选步骤引导 | `api_what_to_eat.php?act=filter_steps` | api_what_to_eat.php | 今天吃什么 | ⭐⭐ | 逐步筛选:先选分类→再选标签→显示匹配数量,比当前直接filter_apply体验更好 |
|
||||
| 12 | 🔢 编码/模糊查询 | `api_what_to_eat.php?act=detail&code=/title=` | api_what_to_eat.php | 搜索页 | ⭐ | 支持菜谱编码(CP032892)查询和标题模糊搜索,扩展搜索能力 |
|
||||
| 1 | 🕐 用餐时段推荐 | `api_filter.php?act=meal_times` | eating_times.json(34种) | 首页/工具中心 | ⭐⭐ | ✅v0.92.0 |
|
||||
| 2 | ⚠️ 过敏原警示 | `api.php?act=full` allergens字段 | gmy.json(585种) | 菜品详情页 | ⭐⭐ | ✅v0.92.0 |
|
||||
| 3 | 📊 营养可视化 | `api.php?act=full` nutrition字段 | nutrition_types.json(31种) | 菜品详情页/营养中心 | ⭐⭐⭐ | ✅v0.92.0 |
|
||||
| 4 | 🏆 评分排行榜 | `api_hot.php?type=recipe&sort=rate` | api_hot.php | 热门排行页 | ⭐ | ✅v0.92.0 |
|
||||
| 5 | 📱 迷你信息加载 | `api.php?act=mini&id=` | api.php | 列表页/卡片 | ⭐⭐ | ✅v0.92.0 |
|
||||
| 6 | 🔍 排除筛选 | `api_filter.php?act=filter_recipes` exclude_*参数 | api_filter.php | 高级搜索页 | ⭐ | ✅v0.92.0 |
|
||||
| 7 | 🌐 IP状态显示 | `api_action.php?act=ip_status` | api_action.php | 菜品详情页 | ⭐ | ✅v0.92.0 |
|
||||
| 8 | 🏷️ 分类标签联动 | `api_filter.php?act=category_tags&category_id=` | api_filter.php | 分类浏览页/高级搜索 | ⭐⭐ | ✅v0.92.0 |
|
||||
| 9 | 🥗 食材分类浏览 | `api_filter.php?act=ingredient_main_categories/sub_categories` | api_filter.php | 发现页/工具中心 | ⭐⭐ | ✅v0.92.0 |
|
||||
| 10 | 📋 食谱子分类 | `api_filter.php?act=recipe_sub_categories&parent_id=` | api_filter.php | 分类浏览页 | ⭐ | ✅v0.92.0 |
|
||||
| 11 | 🎲 筛选步骤引导 | `api_what_to_eat.php?act=filter_steps` | api_what_to_eat.php | 今天吃什么 | ⭐⭐ | ✅v0.92.0 |
|
||||
| 12 | 🔢 编码/模糊查询 | `api_what_to_eat.php?act=detail&code=/title=` | api_what_to_eat.php | 搜索页 | ⭐ | ✅v0.92.0 |
|
||||
|
||||
### 🟡 需组合API开发(P3优先级)
|
||||
|
||||
| # | 功能 | 所需API | 页面位置 | 开发复杂度 | 说明 |
|
||||
| # | 功能 | 所需API | 页面位置 | 开发复杂度 | 状态 |
|
||||
|---|------|---------|---------|-----------|------|
|
||||
| 1 | 🧠 智能推荐 | `api_feed.php?act=recommend` + `api_filter.php?act=filter_recipes` + `gmy.json` + 用户偏好设置 | 首页 | ⭐⭐⭐⭐ | 结合时段+营养+过敏原+用户偏好的智能推荐,需设计推荐算法权重 |
|
||||
| 2 | 📅 每日菜单规划 | `api_what_to_eat.php?act=filter_apply` × 3次 + `eating_times.json` | 工具中心 | ⭐⭐⭐ | 一次性生成早中晚餐完整菜单,需新建菜单规划页面 |
|
||||
| 3 | 📱 二维码海报 | `api.php?act=detail` code字段 + qr_flutter库 | 菜品详情页 | ⭐⭐ | 生成菜谱二维码分享图,code字段已有(如CP032892) |
|
||||
| 4 | 🔗 社交分享增强 | `api.php?act=detail` code字段 + `api_hot.php` statistics | 菜品详情页 | ⭐⭐ | 分享链接含菜谱编码+热度标签,当前分享功能已有但内容简单 |
|
||||
| 5 | 🏋️ 健身餐推荐 | `api_filter.php?act=filter_recipes` nutrition_min/max + nutrition_types.json | 工具中心/发现页 | ⭐⭐⭐ | 高蛋白(>30g)/低脂(<10g)/低碳水菜谱筛选,需营养目标设置UI |
|
||||
| 6 | 📋 过敏原报告 | `api.php?act=full` allergens + `gmy.json` | 菜品详情页 | ⭐⭐⭐ | 菜谱过敏原完整分析报告,交叉比对585种过敏原数据 |
|
||||
| 7 | 🥗 食材营养详情 | `api.php?act=ingredient_detail` + `nutrition_types.json` | 食材详情页 | ⭐⭐ | 食材详情页增加营养成分表格+单位匹配,nutrition_types.json提供31种营养单位 |
|
||||
| 8 | 🔄 食材替代建议 | `api_filter.php?act=filter_recipes` ingredient参数 + `gmy.json` | 食材详情页 | ⭐⭐⭐ | 缺少某食材时推荐替代品,需建立食材替代关系映射 |
|
||||
| 9 | 📈 营养目标追踪 | `api.php?act=full` nutrition × 多菜谱 + `nutrition_types.json` | 营养中心 | ⭐⭐⭐⭐ | 每日营养摄入统计+目标追踪,需记录用户每日饮食 |
|
||||
| 10 | 🏷️ 统一格式输出 | `api.php?act=unified_list/detail/search/hot` type=ingredient | 食材相关页面 | ⭐⭐ | 统一格式简化食材数据处理,当前食材页面使用不同接口格式 |
|
||||
| 1 | 🧠 智能推荐 | `api_feed.php?act=recommend` + `api_filter.php?act=filter_recipes` + `gmy.json` + 用户偏好设置 | 首页 | ⭐⭐⭐⭐ | 🔴待开发 |
|
||||
| 2 | 📅 每日菜单规划 | `api_what_to_eat.php?act=filter_apply` × 3次 + `eating_times.json` | 工具中心 | ⭐⭐⭐ | 🔴待开发 |
|
||||
| 3 | 📱 二维码海报 | `api.php?act=detail` code字段 + qr_flutter库 | 菜品详情页 | ⭐⭐ | 🔴待开发 |
|
||||
| 4 | 🔗 社交分享增强 | `api.php?act=detail` code字段 + `api_hot.php` statistics | 菜品详情页 | ⭐⭐ | 🔴待开发 |
|
||||
| 5 | 🏋️ 健身餐推荐 | `api_filter.php?act=filter_recipes` nutrition_min/max + nutrition_types.json | 工具中心/发现页 | ⭐⭐⭐ | ✅v0.92.0 |
|
||||
| 6 | 📋 过敏原报告 | `api.php?act=full` allergens + `gmy.json` | 菜品详情页 | ⭐⭐⭐ | ✅v0.92.0 |
|
||||
| 7 | 🥗 食材营养详情 | `api.php?act=ingredient_detail` + `nutrition_types.json` | 食材详情页 | ⭐⭐ | ✅v0.92.0 |
|
||||
| 8 | 🔄 食材替代建议 | `api_filter.php?act=filter_recipes` ingredient参数 + `gmy.json` | 食材详情页 | ⭐⭐⭐ | ✅v0.92.0 |
|
||||
| 9 | 📈 营养目标追踪 | `api.php?act=full` nutrition × 多菜谱 + `nutrition_types.json` | 营养中心 | ⭐⭐⭐⭐ | ✅v0.92.0 |
|
||||
| 10 | 🏷️ 统一格式输出 | `api.php?act=unified_list/detail/search/hot` type=ingredient | 食材相关页面 | ⭐⭐ | 🔴待开发 |
|
||||
|
||||
### 🔴 需后端新开发API
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 📋 未完成功能清单
|
||||
|
||||
> 创建: 2026-04-09 | 更新: 2026-04-13 v0.92.0 | 优先级: P1=核心 P2=重要 P3=增强 | 优先级值1-5(5=最高)
|
||||
> 创建: 2026-04-09 | 更新: 2026-04-13 v0.92.4 | 优先级: P1=核心 P2=重要 P3=增强 | 优先级值1-5(5=最高)
|
||||
|
||||
---
|
||||
|
||||
@@ -33,7 +33,9 @@
|
||||
| 三十四 | 食材详情本地缓存+缓存管理 | 2 | 2 | 100% | ✅ |
|
||||
| 三十五 | 食材详情页闪退修复 | 1 | 1 | 100% | ✅ |
|
||||
| 三十六 | 21项功能批量实现 | 21 | 21 | 100% | ✅ |
|
||||
| **合计** | **220** | **210** | **95%** | |
|
||||
| 三十七 | 目录结构整理+导入路径修复 | 8 | 8 | 100% | ✅ |
|
||||
| 三十八 | UI布局优化+缓存修复 | 4 | 4 | 100% | ✅ |
|
||||
| **合计** | **232** | **222** | **96%** | |
|
||||
|
||||
---
|
||||
|
||||
@@ -217,6 +219,22 @@
|
||||
- 📈 数据管理中心增强:DataCenterPage新增运营大屏入口
|
||||
- 🔧 代码质量:flutter analyze零错误
|
||||
|
||||
### 阶段三十七:目录结构整理+导入路径修复 ✅
|
||||
- 📁 lib/src目录重组:每个文件夹≤8文件,按功能分子目录
|
||||
- 🔧 导入路径更新:批量更新import路径适配新目录结构
|
||||
- 🧹 BOM字符清理:72个Dart文件移除UTF-8 BOM (U+FEFF)
|
||||
- 🐛 类型错误修复:ShoppingItemModel/MealRecordModel类型匹配
|
||||
- 🧹 未使用代码清理:移除未使用的导入和字段
|
||||
- 📊 flutter analyze:解决所有critical错误
|
||||
- 🔄 路由参数修复:CategoryModel类型转换问题修复
|
||||
- 📝 文档更新:同步目录结构变更
|
||||
|
||||
### 阶段三十八:UI布局优化+缓存修复 ✅
|
||||
- 📱 收藏页面网格布局:GridView 2列卡片展示
|
||||
- 🛠️ 工具中心增强:新增"使用工具"按钮直接打开工具功能
|
||||
- 💾 食材缓存修复:CacheService键名匹配问题修复
|
||||
- 🔗 缓存管理页面跳转修复:正确跳转到食材详情页
|
||||
|
||||
### 阶段三十:发现页口味/工艺筛选 ✅
|
||||
- ✅ 口味标签筛选 / ✅ 工艺标签筛选
|
||||
- ✅ 相关菜谱推荐(详情页底部)— v0.92.0实现
|
||||
@@ -245,46 +263,25 @@
|
||||
|
||||
## 📊 API接口使用状态一览
|
||||
|
||||
> 基于 API_DOC.md v3.2.0 + APP_GUIDE.md v2.9.0
|
||||
> 基于 API_DOC.md v3.2.0 + APP_GUIDE.md v2.9.0,更新于 v0.92.0
|
||||
|
||||
### ✅ 已使用接口
|
||||
|
||||
| 接口文件 | Repository | 已用act |
|
||||
|---------|-----------|---------|
|
||||
| `api.php` | RecipeRepository | list/detail/full/ingredients/ingredient_detail/search/categories/tags/stats/unified_list/unified_detail/unified_search/unified_hot/query |
|
||||
| `api.php` | RecipeRepository | list/detail/full/ingredients/ingredient_detail/search/categories/tags/stats/unified_list/unified_detail/unified_search/unified_hot/query/mini |
|
||||
| `api_action.php` | ActionRepository | like/rate/view/ip_status |
|
||||
| `api_feed.php` | FeedRepository | recommend/latest/hot/prefetch |
|
||||
| `api_filter.php` | RecipeRepository+SearchController | recipe_main_categories/taste_tags/cooking_tags/filter_recipes/global_search |
|
||||
| `api_hot.php` | HotRepository | hot(today/month/total) |
|
||||
| `api_what_to_eat.php` | WhatToEatRepository | filter_apply/detail |
|
||||
| `api_filter.php` | RecipeRepository+SearchController | recipe_main_categories/taste_tags/cooking_tags/filter_recipes/global_search/meal_times/recipe_sub_categories/ingredient_main_categories/ingredient_sub_categories/category_tags/filter_ingredients/ingredient_recipes |
|
||||
| `api_hot.php` | HotRepository | hot(today/month/total) sort=view/like/rate |
|
||||
| `api_what_to_eat.php` | WhatToEatRepository | filter_apply/detail/filter_steps |
|
||||
| `api_discover.php` | DiscoverRepository | 随机数据 |
|
||||
| `stats_full.php` | StatsRepository+OnlineRepository | online/request/hot/heartbeat |
|
||||
| `api_check_duplicate.php` | RecipeRepository | check_title/check_ingredient/check_step/check_content/check_all |
|
||||
| `stats_full.php` | StatsRepository+OnlineRepository | online/request/hot/stats/heartbeat |
|
||||
| 静态数据 | 各页面 | eating_times.json(✅)/nutrition_types.json(✅)/gmy.json(✅) |
|
||||
|
||||
### ❌ 未使用接口(可直接调用)
|
||||
### 🔴 未使用接口(待开发)
|
||||
|
||||
| 接口 | act | 功能 | 可开发功能 |
|
||||
|------|-----|------|-----------|
|
||||
| `api.php` | mini | 迷你版菜谱(~1KB) | 列表页性能优化 |
|
||||
| `api_filter.php` | meal_times | 用餐时段列表 | 时段推荐 |
|
||||
| `api_filter.php` | recipe_sub_categories | 食谱子分类 | 分类浏览增强 |
|
||||
| `api_filter.php` | ingredient_main_categories | 食材大类 | 食材分类浏览 |
|
||||
| `api_filter.php` | ingredient_sub_categories | 食材子分类 | 食材三级浏览 |
|
||||
| `api_filter.php` | category_tags | 分类下标签 | 标签联动筛选 |
|
||||
| `api_filter.php` | filter_ingredients | 食材筛选 | 高级搜索食材 |
|
||||
| `api_filter.php` | ingredient_recipes | 食材对应菜品 | 食材详情页菜品列表 |
|
||||
| `api_filter.php` | index | 接口索引 | 调试/接口文档 |
|
||||
| `api_what_to_eat.php` | filter_steps | 筛选步骤引导 | "吃什么"步骤UI |
|
||||
| `api_hot.php` | sort=rate | 评分排行 | 评分排行榜 |
|
||||
| `api_filter.php` | exclude_*参数(7个) | 排除筛选 | 高级搜索排除选项 |
|
||||
| `api_filter.php` | nutrition_min/max | 营养范围筛选 | 健身餐推荐 |
|
||||
| `api_check_duplicate.php` | 5种act | 查重检测 | 菜谱上传查重 |
|
||||
| `api.php` | unified_* type=ingredient | 统一格式食材 | 食材数据标准化 |
|
||||
| `stats_full.php` | stats layer=detail/full | 详细/完整统计 | 运营数据大屏 |
|
||||
|
||||
### ❌ 未使用静态资源
|
||||
|
||||
| 文件 | 数据量 | 当前使用情况 | 可开发功能 |
|
||||
|------|--------|-------------|-----------|
|
||||
| `eating_times.json` | 34种时段 | 仅meal_time_recommend_page直接HTTP请求 | 用餐时段推荐 |
|
||||
| `nutrition_types.json` | 31种营养(含单位) | ❌完全未使用 | 营养可视化+目标追踪 |
|
||||
| `gmy.json` | 585种过敏原(21大类) | 仅allergen_checker_page直接HTTP请求 | 过敏原警示+报告 |
|
||||
|
||||
Reference in New Issue
Block a user