23 KiB
23 KiB
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 |
用户偏好 | 标签、分类、过敏原设置 |
二、响应格式
支持格式
| 格式 | 参数 | 体积优化 | 解析速度 | 推荐场景 |
|---|---|---|---|---|
| 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协同使用
最佳实践:
- 数据缓存策略
// 本地缓存基础数据,减少网络请求
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;
}
}
- 智能推荐示例
// 结合时段、营养、过敏原的智能推荐
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);
}
🥗 功能二:健康饮食管理
应用场景
- 过敏原警示:用户查看菜谱时提醒过敏风险
- 过敏原过滤:自动排除含过敏原的菜谱
- 热量计算:统计每日摄入热量
- 健身餐推荐:推荐高蛋白低脂菜谱
- 特殊饮食:糖尿病/高血压饮食推荐
实现方式
allergens 和 nutrition 字段:
{
"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('、')}'),
);
}
📱 功能三:社交分享
应用场景
- 分享链接:微信/微博分享菜谱
- 二维码:生成菜谱二维码海报
- 热度展示:显示浏览量/点赞数
- 排行榜:热门菜谱排行
实现方式
code 和 statistics 字段:
{
"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}'
));
});
}
六、推荐算法 (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= |
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 |