Files
kitchen/docs/api/doc/APP_GUIDE.md
2026-04-11 07:07:13 +08:00

26 KiB
Raw Blame History

App 接入指南

版本: v2.0.0
更新日期: 2026-04-10
基础地址: http://eat.wktyl.com/api/


一、接口文件说明

文件 说明 主要功能
api.php 主接口 列表、详情、搜索、统计、统一输出
api_action.php 动态接口 点赞、推荐、浏览量
api_what_to_eat.php 智能选择 随机推荐、动态筛选
api_feed.php 信息流 推荐、热门、个性化
stats_full.php 全面统计 热门、在线、请求统计
api_preference.php 用户偏好 标签、分类、过敏原设置
api_check_duplicate.php 查重接口 菜品、食材、营养成分、内容查重

二、响应格式

支持格式

格式 参数 体积优化 解析速度 推荐场景
JSON _format=json 基准 基准 调试开发
Gzip _format=gzip 75%+ 移动网络
MessagePack _format=msgpack 35% 更快 高速网络
CBOR _format=cbor 32% 更快 高速网络

使用方式

# JSON格式默认
GET api.php?act=list

# Gzip压缩JSON推荐移动端使用
GET api.php?act=list&_format=gzip

# MessagePack格式
GET api.php?act=list&_format=msgpack

# 格式化JSON调试用
GET api.php?act=list&_pretty=1

响应结构

{
  "code": 200,
  "message": "success",
  "data": { ... },
  "_cached": false,
  "_query_time": "15.23ms"
}

三、缓存策略

缓存控制参数

参数 说明 使用场景
_refresh=1 强制刷新缓存 用户下拉刷新时
_stale=1 允许返回过期缓存 网络异常时保护用户体验

缓存TTL

接口 TTL 说明
feed 3分钟 信息流
hot 5分钟 热门排行
list 3分钟 列表页
detail 5分钟 详情页
full 10分钟 完整信息
stats 1分钟 统计数据
categories 10分钟 分类列表
tags 10分钟 标签列表

客户端缓存建议

1. 列表页本地缓存3分钟
2. 详情页本地缓存5分钟
3. 用户下拉刷新:添加 _refresh=1
4. 网络异常:添加 _stale=1 获取过期缓存

四、数据资源文件

本章节介绍API提供的静态数据资源文件这些文件包含菜谱系统的核心基础数据可配合API接口实现更丰富的功能。

📦 数据文件列表

文件 地址 说明 数据量
用餐时段数据 http://eat.wktyl.com/api/assets/eating_times.json 包含标准时段、组合时段、食用频率等 34种
营养成分数据 http://eat.wktyl.com/api/assets/nutrition_types.json 包含维生素、矿物质、宏量营养素等 31种
过敏原数据 http://eat.wktyl.com/api/assets/gmy.json 包含21大类食材过敏原信息 585种

🍽️ 用餐时段数据

数据结构:

{
  "standard_times": [
    {"id": 1, "name": "中餐", "count": 2485},
    {"id": 2, "name": "晚餐", "count": 2422},
    {"id": 3, "name": "早餐", "count": 1408},
    {"id": 4, "name": "零食", "count": 244}
  ],
  "combined_times": [...],
  "frequency_times": [...],
  "method_times": [...],
  "other_times": [...]
}

应用场景:

  • 🕐 智能时段推荐:根据当前时间自动推荐适合的菜谱
  • 📅 每日菜单规划:生成早中晚餐完整菜单
  • 🏥 药膳管理:根据食用频率规划药膳食谱
  • 📊 时段统计分析:分析不同时段的菜谱分布

使用示例:

// Flutter 示例:加载用餐时段数据
Future<Map<String, dynamic>> loadEatingTimes() async {
  final response = await http.get(
    Uri.parse('http://eat.wktyl.com/api/assets/eating_times.json')
  );
  return json.decode(response.body);
}

// 根据时段推荐菜谱
Future<List<Recipe>> recommendByTime(String mealTime) async {
  final response = await http.get(
    Uri.parse('$baseUrl/api.php?act=search&keyword=$mealTime&limit=10')
  );
  return parseRecipes(response.body);
}

🥗 营养成分数据

数据结构:

[
  {"id": 1, "name": "叶酸", "unit": "微克"},
  {"id": 2, "name": "核黄素", "unit": "毫克"},
  {"id": 3, "name": "蛋白质", "unit": "克"},
  {"id": 4, "name": "维生素C", "unit": "毫克"}
]

应用场景:

  • 📊 营养分析:计算菜谱营养成分及占比
  • 🎯 营养目标:追踪每日营养摄入目标
  • 🏋️ 健身餐规划:高蛋白、低碳水化合物菜谱推荐
  • 🏥 健康管理:糖尿病、高血压等特殊饮食规划
  • 👶 孕期营养:叶酸、铁、钙等关键营养素追踪

使用示例:

// Flutter 示例:计算菜谱营养贡献
Future<Map<String, dynamic>> calculateNutrition(int recipeId) async {
  // 获取菜谱详情
  final recipeResponse = await http.get(
    Uri.parse('$baseUrl/api.php?act=full&id=$recipeId')
  );
  final recipe = json.decode(recipeResponse.body)['data'];
  
  // 获取营养成分类型
  final typesResponse = await http.get(
    Uri.parse('http://eat.wktyl.com/api/assets/nutrition_types.json')
  );
  final types = json.decode(typesResponse.body);
  
  // 匹配营养成分单位
  final nutrition = recipe['nutrition'].map((n) {
    final type = types.firstWhere((t) => t['name'] == n['name']);
    return {
      'name': n['name'],
      'value': n['value'],
      'unit': type['unit']
    };
  }).toList();
  
  return nutrition;
}

⚠️ 过敏原数据

数据结构:

[
  {
    "name": "蔬菜类及制品",
    "items": [
      {
        "name": "姜",
        "allergens": ["姜"],
        "allergen_type": ["蔬菜类"]
      },
      {
        "name": "洋葱(白皮)",
        "allergens": ["葱", "洋葱"],
        "allergen_type": ["蔬菜类"]
      }
    ]
  }
]

应用场景:

  • ⚠️ 过敏原警示:用户查看菜谱时显示过敏原提醒
  • 🚫 智能过滤:自动过滤含用户过敏原的菜谱
  • 🔄 食材替代:推荐不含过敏原的替代食材
  • 📋 过敏原报告:生成菜谱过敏原分析报告
  • 👶 儿童饮食:儿童常见过敏原特殊处理
  • 🏥 医疗饮食:为特殊人群定制安全食谱

使用示例:

// Flutter 示例:检查菜谱过敏原
Future<Map<String, dynamic>> checkAllergens(int recipeId, List<String> userAllergens) async {
  // 获取菜谱详情
  final recipeResponse = await http.get(
    Uri.parse('$baseUrl/api.php?act=full&id=$recipeId')
  );
  final recipe = json.decode(recipeResponse.body)['data'];
  
  // 获取过敏原数据
  final allergenResponse = await http.get(
    Uri.parse('http://eat.wktyl.com/api/assets/gmy.json')
  );
  final allergenData = json.decode(allergenResponse.body);
  
  final warnings = [];
  
  // 检查所有食材
  for (var ingredient in recipe['ingredients']) {
    for (var category in allergenData) {
      final item = category['items'].firstWhere(
        (i) => i['name'] == ingredient['name'],
        orElse: () => null
      );
      
      if (item != null && item['allergens'] != null) {
        final hasAllergen = item['allergens'].any(
          (a) => userAllergens.contains(a)
        );
        
        if (hasAllergen) {
          warnings.add({
            'ingredient': ingredient['name'],
            'allergens': item['allergens'],
            'types': item['allergen_type']
          });
        }
      }
    }
  }
  
  return {
    'safe': warnings.isEmpty,
    'warnings': warnings
  };
}

🔗 数据文件与API协同使用

最佳实践:

  1. 数据缓存策略
// 本地缓存基础数据,减少网络请求
class DataManager {
  static const CACHE_KEY = 'recipe_base_data';
  static const CACHE_DURATION = 24 * 60 * 60 * 1000; // 24小时
  
  Future<Map<String, dynamic>> getBaseData() async {
    final prefs = await SharedPreferences.getInstance();
    final cached = prefs.getString(CACHE_KEY);
    
    if (cached != null) {
      final data = json.decode(cached);
      if (DateTime.now().millisecondsSinceEpoch - data['timestamp'] < CACHE_DURATION) {
        return data['data'];
      }
    }
    
    // 并行加载所有数据文件
    final results = await Future.wait([
      http.get(Uri.parse('http://eat.wktyl.com/api/assets/eating_times.json')),
      http.get(Uri.parse('http://eat.wktyl.com/api/assets/nutrition_types.json')),
      http.get(Uri.parse('http://eat.wktyl.com/api/assets/gmy.json'))
    ]);
    
    final data = {
      'eatingTimes': json.decode(results[0].body),
      'nutritionTypes': json.decode(results[1].body),
      'allergenData': json.decode(results[2].body),
      'timestamp': DateTime.now().millisecondsSinceEpoch
    };
    
    await prefs.setString(CACHE_KEY, json.encode(data));
    return data;
  }
}
  1. 智能推荐示例
// 结合时段、营养、过敏原的智能推荐
Future<List<Recipe>> smartRecommend(UserProfile userProfile) async {
  final dataManager = DataManager();
  final baseData = await dataManager.getBaseData();
  
  // 根据时段推荐
  final currentMeal = getCurrentMealType(baseData['eatingTimes']);
  
  // 获取推荐菜谱
  final response = await http.get(
    Uri.parse('$baseUrl/api.php?act=search&keyword=$currentMeal&limit=20')
  );
  final recipes = parseRecipes(response.body);
  
  // 过滤过敏原
  final safeRecipes = recipes.where((recipe) {
    final safety = checkRecipeSafety(recipe, userProfile.allergens, baseData['allergenData']);
    return safety['safe'];
  }).toList();
  
  // 计算营养匹配度并排序
  safeRecipes.sort((a, b) {
    final scoreA = calculateNutritionScore(a, userProfile.goals, baseData['nutritionTypes']);
    final scoreB = calculateNutritionScore(b, userProfile.goals, baseData['nutritionTypes']);
    return scoreB.compareTo(scoreA);
  });
  
  return safeRecipes.take(10).toList();
}

五、核心功能实现指南

🍽️ 功能一:用餐时段推荐

应用场景

  • 早餐推荐早上7-10点推荐早餐菜谱
  • 午餐推荐中午11-14点推荐午餐菜谱
  • 晚餐推荐傍晚17-20点推荐晚餐菜谱
  • 每日菜单:生成一日三餐完整菜单

实现方式

intro 字段包含用餐时段信息:

{
  "intro": "早餐、中餐、晚餐"
}

接口调用

GET api.php?act=search&keyword=早餐&limit=10
GET api.php?act=search&keyword=晚餐&limit=10

客户端实现

// Flutter 示例
Future<List<Recipe>> getMealByTime() async {
  final hour = DateTime.now().hour;
  String mealType = '中餐';
  
  if (hour < 10) mealType = '早餐';
  else if (hour > 17) mealType = '晚餐';
  
  final response = await http.get(
    Uri.parse('$baseUrl/api.php?act=search&keyword=$mealType')
  );
  
  return parseRecipes(response.body);
}

🥗 功能二:健康饮食管理

应用场景

  • 过敏原警示:用户查看菜谱时提醒过敏风险
  • 过敏原过滤:自动排除含过敏原的菜谱
  • 热量计算:统计每日摄入热量
  • 健身餐推荐:推荐高蛋白低脂菜谱
  • 特殊饮食:糖尿病/高血压饮食推荐

实现方式

allergensnutrition 字段:

{
  "allergens": ["海鲜", "花生"],
  "nutrition": [
    {"name": "热量", "value": 350, "unit": "kcal"},
    {"name": "蛋白质", "value": 25, "unit": "g"}
  ]
}

接口调用

# 获取完整信息(含过敏原和营养)
GET api.php?act=full&id=32892

# 智能推荐(排除过敏原)
GET api_what_to_eat.php?act=smart&exclude_allergens=seafood,nuts

# 设置用户过敏原
POST api_preference.php?act=save
{
  "user_id": "xxx",
  "blocked_allergens": ["seafood", "nuts"]
}

客户端实现

// 过敏原检查
bool checkAllergens(Recipe recipe, List<String> userAllergens) {
  final recipeAllergens = recipe.allergens ?? [];
  return userAllergens.any((a) => recipeAllergens.contains(a));
}

// 显示过敏警示
Widget buildAllergenWarning(Recipe recipe, List<String> userAllergens) {
  final warnings = userAllergens
    .where((a) => recipe.allergens?.contains(a) ?? false)
    .toList();
    
  if (warnings.isEmpty) return SizedBox.shrink();
  
  return Container(
    color: Colors.orange.shade100,
    child: Text('⚠️ 含有您的过敏原:${warnings.join('、')}'),
  );
}

📱 功能三:社交分享

应用场景

  • 分享链接:微信/微博分享菜谱
  • 二维码:生成菜谱二维码海报
  • 热度展示:显示浏览量/点赞数
  • 排行榜:热门菜谱排行

实现方式

codestatistics 字段:

{
  "id": 32892,
  "code": "CP032892",
  "title": "宫保鸡丁",
  "statistics": {
    "view_count": 1000,
    "like_count": 50
  }
}

接口调用

# 通过编码查询菜谱
GET api_what_to_eat.php?act=detail&code=CP032892

# 点赞
GET api_action.php?act=like&type=recipe&id=32892&action=like

# 获取热门排行
GET stats_full.php?act=hot&period=today&limit=20

客户端实现

// 生成分享链接
String generateShareLink(Recipe recipe) {
  return 'https://eat.wktyl.com/recipe/${recipe.code}';
}

// 显示热度标签
String getHotnessTag(Statistics stats) {
  if (stats.viewCount > 10000) return '🔥 爆款';
  if (stats.viewCount > 1000) return '📈 热门';
  if (stats.likeCount > 100) return '❤️ 受欢迎';
  return '';
}

// 分享到微信
void shareToWechat(Recipe recipe) {
  Wechat.shareWebpage(
    title: recipe.title,
    description: recipe.intro,
    url: generateShareLink(recipe),
  );
}

🛒 功能四:购物清单

应用场景

  • 一键生成:根据菜谱生成购物清单
  • 合并清单:多个菜谱合并采购清单
  • 分类展示:按主料/辅料/调料分类
  • 超市导航:按超市区域分类

实现方式

ingredients 字段:

{
  "ingredients": {
    "main": [{"name": "鸡肉", "amount": "500", "unit": "克"}],
    "auxiliary": [{"name": "青椒", "amount": "2", "unit": "个"}],
    "seasoning": [{"name": "生抽", "amount": "2", "unit": "勺"}]
  }
}

接口调用

# 获取完整食材信息
GET api.php?act=full&id=32892

# 获取食材选购指南
GET api.php?act=ingredient_detail&id=1

客户端实现

// 生成购物清单
List<ShoppingItem> generateShoppingList(Recipe recipe) {
  final items = <ShoppingItem>[];
  
  recipe.ingredients?.main?.forEach((item) {
    items.add(ShoppingItem(
      name: item.name,
      amount: item.amount,
      unit: item.unit,
      category: '主料',
    ));
  });
  
  // ... 辅料、调料同理
  
  return items;
}

// 合并多个菜谱的购物清单
List<ShoppingItem> mergeShoppingLists(List<Recipe> recipes) {
  final merged = <String, ShoppingItem>{};
  
  for (final recipe in recipes) {
    final allItems = [
      ...?recipe.ingredients?.main,
      ...?recipe.ingredients?.auxiliary,
      ...?recipe.ingredients?.seasoning,
    ];
    
    for (final item in allItems) {
      if (merged.containsKey(item.name)) {
        // 合并数量
        merged[item.name]!.amount += item.amount;
      } else {
        merged[item.name] = item;
      }
    }
  }
  
  return merged.values.toList();
}

🎯 功能五:智能推荐

应用场景

  • 随机推荐:"今天吃什么"随机推荐
  • 标签推荐:根据口味标签推荐
  • 分类推荐:根据菜系分类推荐
  • 快手菜推荐30分钟内完成的菜谱
  • 个性化:基于用户偏好推荐

实现方式

接口调用

# 随机推荐
GET api_what_to_eat.php?act=random

# 智能推荐(带条件)
GET api_what_to_eat.php?act=smart&include_categories=12,13&exclude_allergens=seafood

# 信息流推荐
GET api_feed.php?act=recommend&page=1&limit=20

# 个性化推荐
GET api_feed.php?act=personal&user_id=xxx

# 热门推荐
GET api_feed.php?act=hot&page=1&limit=20

客户端实现

// "今天吃什么"功能
Future<Recipe> getRandomRecipe() async {
  final response = await http.get(
    Uri.parse('$baseUrl/api_what_to_eat.php?act=random')
  );
  return Recipe.fromJson(json.decode(response.body)['data']);
}

// 智能推荐
Future<List<Recipe>> smartRecommend({
  List<int>? categories,
  List<String>? excludeAllergens,
}) async {
  final params = <String, String>{'act': 'smart'};
  
  if (categories != null) {
    params['include_categories'] = categories.join(',');
  }
  if (excludeAllergens != null) {
    params['exclude_allergens'] = excludeAllergens.join(',');
  }
  
  final response = await http.get(
    Uri.parse('$baseUrl/api_what_to_eat.php').replace(queryParameters: params)
  );
  
  return parseRecipes(response.body);
}

📊 功能六:数据统计

应用场景

  • 运营大屏:实时数据展示
  • 在线人数:显示当前在线用户
  • 热门趋势:分析热门菜谱趋势
  • 平台分析:分析各平台使用情况

实现方式

接口调用

# 请求统计
GET stats_full.php?act=request

# 热门统计
GET stats_full.php?act=hot&period=today

# 在线统计
GET stats_full.php?act=online

# 心跳更新
GET stats_full.php?act=heartbeat&platform=ios&page=home

客户端实现

// 构建数据大屏
Future<DashboardData> buildDashboard() async {
  final responses = await Future.wait([
    http.get(Uri.parse('$baseUrl/stats_full.php?act=request')),
    http.get(Uri.parse('$baseUrl/stats_full.php?act=hot&period=today')),
    http.get(Uri.parse('$baseUrl/stats_full.php?act=online')),
  ]);
  
  return DashboardData(
    totalRecipes: json.decode(responses[0].body)['data']['total'],
    todayRequests: json.decode(responses[0].body)['data']['today'],
    onlineUsers: json.decode(responses[2].body)['data']['online_total'],
    topRecipes: json.decode(responses[1].body)['data']['recipe_view'],
  );
}

// 心跳更新每30秒调用
void startHeartbeat() {
  Timer.periodic(Duration(seconds: 30), (timer) {
    http.get(Uri.parse(
      '$baseUrl/stats_full.php?act=heartbeat&platform=ios&page=${currentPage}'
    ));
  });
}

🔍 功能七:内容查重

应用场景

  • 投稿查重:用户投稿前检查重复率
  • 内容审核:防止重复内容录入
  • 质量把控:确保内容原创性
  • 版权保护:防止抄袭内容

实现方式

接口调用

# 菜品标题查重
GET api_check_duplicate.php?act=recipe_title&title=宫保鸡丁

# 食材名称查重
GET api_check_duplicate.php?act=ingredient_name&name=鸡蛋

# 营养成分查重
GET api_check_duplicate.php?act=nutrition_name&name=维生素C

# 菜品内容查重
POST api_check_duplicate.php?act=recipe_content
{"content": "1. 将鸡肉切成丁状..."}

# 食材内容查重
POST api_check_duplicate.php?act=ingredient_content
{"content": "鸡蛋含有丰富的蛋白质..."}

客户端实现

// Flutter 示例:投稿前查重
Future<bool> checkDuplicateBeforeSubmit(String title, String content) async {
  // 检查标题重复率
  final titleResponse = await http.get(
    Uri.parse('$baseUrl/api_check_duplicate.php?act=recipe_title&title=${Uri.encodeComponent(title)}')
  );
  final titleRate = json.decode(titleResponse.body)['data']['duplicate_rate'];
  
  // 检查内容重复率
  final contentResponse = await http.post(
    Uri.parse('$baseUrl/api_check_duplicate.php?act=recipe_content'),
    body: json.encode({'content': content}),
    headers: {'Content-Type': 'application/json'}
  );
  final contentRate = json.decode(contentResponse.body)['data']['duplicate_rate'];
  
  // 判断是否可以投稿(重复率低于阈值)
  const threshold = 80.0; // 重复率阈值
  if (titleRate >= threshold) {
    showDialog('标题重复率过高:${titleRate}%,请修改标题');
    return false;
  }
  
  if (contentRate >= threshold) {
    showDialog('内容重复率过高:${contentRate}%,请修改内容');
    return false;
  }
  
  return true;
}

// 批量查重
Future<Map<String, double>> batchCheckDuplicate({
  String? title,
  String? ingredientName,
  String? content,
}) async {
  final results = <String, double>{};
  
  if (title != null) {
    final response = await http.get(
      Uri.parse('$baseUrl/api_check_duplicate.php?act=recipe_title&title=${Uri.encodeComponent(title)}')
    );
    results['title'] = json.decode(response.body)['data']['duplicate_rate'];
  }
  
  if (ingredientName != null) {
    final response = await http.get(
      Uri.parse('$baseUrl/api_check_duplicate.php?act=ingredient_name&name=${Uri.encodeComponent(ingredientName)}')
    );
    results['ingredient'] = json.decode(response.body)['data']['duplicate_rate'];
  }
  
  if (content != null) {
    final response = await http.post(
      Uri.parse('$baseUrl/api_check_duplicate.php?act=recipe_content'),
      body: json.encode({'content': content}),
      headers: {'Content-Type': 'application/json'}
    );
    results['content'] = json.decode(response.body)['data']['duplicate_rate'];
  }
  
  return results;
}

六、推荐算法 (MDHW)

算法简介

MDHW多维度混合权重推荐算法综合用户偏好、行为历史、内容热度、管理员配置四大维度。

评分规则满分150+分)

管理员权重:
├── 置顶分类 +50分
└── 推荐分类 +30分

用户偏好:
├── 偏好分类 +25分
├── 偏好标签 +30分/个
└── 浏览历史 +5分/次上限20分

热门数据:
├── 浏览量 +1分/100次上限20分
├── 点赞数 +2分/个上限15分
└── 推荐数 +3分/个上限15分

时间衰减:
├── 1天内 +15分
├── 7天内 +10分
└── 30天内 +5分

信息流接口

类型 接口 说明
推荐 api_feed.php?act=recommend 热门40% + 最新40% + 随机20%
热门 api_feed.php?act=hot 按浏览量排序
个性化 api_feed.php?act=personal&user_id=xxx 基于用户偏好
预加载 api_feed.php?act=prefetch&pages=3 一次加载多页

七、性能优化

响应时间参考

场景 服务器处理时间 说明
缓存命中 ~5ms 直接返回文件缓存
缓存未命中 ~200-1000ms 查询数据库+生成缓存
Stale模式 ~5ms 返回过期缓存

并发能力

场景 QPS 说明
缓存命中 100+ 高并发支持
缓存未命中 10-20 数据库查询

优化建议

1. 使用 Gzip 格式减少流量节省75%+
2. 合理设置本地缓存时间
3. 预加载下一页数据
4. 列表页使用 _stale=1 保护
5. 批量请求使用预加载接口

八、统一输出接口

提供食谱和食材的一致性输出格式减少App端代码量。

统一字段结构

{
  "id": 123,
  "type": "recipe",
  "type_name": "食谱",
  "title": "菜谱名称",
  "intro": "简介...",
  "category": {"id": 11, "name": "家常菜"},
  "statistics": {
    "view_count": 1000,
    "like_count": 50,
    "recommend_count": 30
  },
  "publish_time": 1712500000
}

接口

GET api.php?act=unified_list&type=recipe&page=1
GET api.php?act=unified_detail&type=recipe&id=1
GET api.php?act=unified_search&type=recipe&keyword=鸡蛋
GET api.php?act=unified_hot&type=recipe&limit=20

统一格式优势

  • 🔄 食谱和食材使用相同结构减少App端代码量
  • 📱 适合移动端列表展示,字段精简
  • 🔗 统一的URL格式方便跳转
  • 📊 统一的统计字段,方便排序和展示

九、错误处理

错误码

错误码 说明 处理建议
200 成功 正常处理
301 重定向 跟随重定向
400 参数错误 检查请求参数
404 资源不存在 提示用户或返回列表
429 请求过于频繁 延迟重试
500 服务器错误 使用缓存或提示用户

错误响应

{
  "code": 404,
  "message": "菜谱不存在",
  "data": null
}

客户端处理建议

Future<T> safeRequest<T>(Future<T> Function() request) async {
  try {
    return await request();
  } on SocketException {
    // 网络异常,尝试使用缓存
    throw NetworkException('网络连接失败');
  } on HttpException catch (e) {
    if (e.statusCode == 429) {
      // 请求频繁,延迟重试
      await Future.delayed(Duration(seconds: 1));
      return safeRequest(request);
    }
    rethrow;
  }
}

十、过敏原类型

类型 说明 常见食材
seafood 海鲜 虾、蟹、贝类、鱼类
nuts 坚果 花生、核桃、杏仁
dairy 乳制品 牛奶、奶酪、黄油
egg 蛋类 鸡蛋、鸭蛋、鹌鹑蛋
gluten 麸质 小麦、大麦、黑麦
soy 大豆 豆腐、豆浆、酱油
peanut 花生 花生、花生油

十一、菜谱编码格式

格式:CP + 6位数字

示例:CP032892 对应 ID 32892

用途:

  • 唯一标识分享
  • 二维码生成
  • 语音搜索识别
  • 快速定位菜谱

接口调用

GET api_what_to_eat.php?act=detail&code=CP032892

十二、字段功能速查表

字段 可实现功能 推荐接口
id 详情查询、收藏、分享链接 api.php?act=detail
code 二维码、短链接、语音搜索 api_what_to_eat.php?act=detail&code=
pic_id 图片资源关联、新旧系统迁移 api.php?act=fullapi_what_to_eat.php?act=detailapi_feed.php
title 搜索、分享标题、列表展示 api.php?act=search
intro 用餐时段筛选、列表预览 客户端过滤
category 分类筛选、面包屑导航 api.php?act=list&cate_id=
tags 标签云、口味筛选、相关推荐 api.php?act=list&tag_id=
allergens 过敏警示、健康过滤 api.php?act=full
nutrition 营养计算、健康分析 api.php?act=full
ingredients 购物清单、营养计算 api.php?act=full
statistics 热门排序、热度展示 stats_full.php?act=hot
meta.process 工艺筛选(炒/蒸/煮) 客户端过滤
meta.taste 口味筛选(酸甜苦辣咸) 客户端过滤
meta.difficulty 难度筛选、新手推荐 客户端过滤
meta.time 时间筛选、快手菜推荐 客户端过滤
content 步骤解析、语音播报 api.php?act=detail