feat: 新增公告功能及外卖备注工具
新增公告功能接口及页面,支持查看最新公告信息 添加外卖备注工具,可管理常用备注并一键生成 优化动态筛选接口,支持多分类和标签组合筛选 移除flutter_dotenv依赖,不再使用.env文件 修复布局溢出错误处理逻辑,避免生产环境报错 新增文件选择器插件,替换receive_sharing_intent实现文件导入
This commit is contained in:
156
scripts/test_filter_steps.dart
Normal file
156
scripts/test_filter_steps.dart
Normal file
@@ -0,0 +1,156 @@
|
||||
// 2026-04-20 | test_filter_steps.dart | 动态筛选接口测试 | 验证filter_steps动态筛选+api_filter分类
|
||||
// 运行: dart run scripts/test_filter_steps.dart
|
||||
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
const String baseUrl = 'https://eat.wktyl.com/api';
|
||||
|
||||
Future<void> main() async {
|
||||
print('=== 动态筛选接口测试 ===\n');
|
||||
|
||||
print('━━━ 1. api_filter: 获取菜谱大类 ━━━');
|
||||
final mainCats = await getJson('api_filter.php', {'act': 'recipe_main_categories'});
|
||||
if (mainCats != null) {
|
||||
final list = mainCats['data']?['list'] as List? ?? [];
|
||||
print(' 大类数量: ${list.length}');
|
||||
for (final c in list) {
|
||||
final m = c as Map;
|
||||
print(' 📂 ${m['name']} (ID:${m['id']}): ${m['recipe_count']}道');
|
||||
}
|
||||
}
|
||||
|
||||
print('\n━━━ 2. api_filter: 获取中国菜子类 ━━━');
|
||||
final subCats = await getJson('api_filter.php', {'act': 'recipe_sub_categories', 'parent_id': '12'});
|
||||
if (subCats != null) {
|
||||
final list = subCats['data']?['list'] as List? ?? [];
|
||||
print(' 子类数量: ${list.length}');
|
||||
for (final c in list.take(5)) {
|
||||
final m = c as Map;
|
||||
print(' 📂 ${m['name']} (ID:${m['id']}): ${m['recipe_count']}道');
|
||||
}
|
||||
}
|
||||
|
||||
print('\n━━━ 3. filter_steps: 无筛选条件 ━━━');
|
||||
final fs1 = await fetchFilterSteps();
|
||||
if (fs1 != null) {
|
||||
print(' 匹配菜谱数: ${fs1['matched_count']}');
|
||||
final opts = fs1['available_options'] as List? ?? [];
|
||||
print(' 可用分类数: ${opts.length}');
|
||||
for (final o in opts.take(3)) {
|
||||
final m = o as Map;
|
||||
print(' 📂 ${m['name']}: ${m['count']}道 (${(m['children'] as List?)?.length ?? 0}子类)');
|
||||
}
|
||||
final tags1 = fs1['available_tags'] as List? ?? [];
|
||||
print(' 可用标签数: ${tags1.length}');
|
||||
for (final t in tags1.take(5)) {
|
||||
final tm = t as Map;
|
||||
print(' 🏷️ ${tm['name']}: ${tm['count']}道');
|
||||
}
|
||||
}
|
||||
|
||||
print('\n━━━ 4. filter_steps: 选分类[12] ━━━');
|
||||
final fs2 = await fetchFilterSteps(categories: [12]);
|
||||
if (fs2 != null) {
|
||||
print(' 匹配菜谱数: ${fs2['matched_count']}');
|
||||
final tags2 = fs2['available_tags'] as List? ?? [];
|
||||
print(' 可用标签数: ${tags2.length}');
|
||||
for (final t in tags2.take(5)) {
|
||||
final tm = t as Map;
|
||||
print(' 🏷️ ${tm['name']}: ${tm['count']}道');
|
||||
}
|
||||
}
|
||||
|
||||
print('\n━━━ 5. filter_steps: 分类[12]+标签[1] ━━━');
|
||||
final fs3 = await fetchFilterSteps(categories: [12], tags: [1]);
|
||||
if (fs3 != null) {
|
||||
print(' 匹配菜谱数: ${fs3['matched_count']}');
|
||||
final tags3 = fs3['available_tags'] as List? ?? [];
|
||||
print(' 可用标签数: ${tags3.length}');
|
||||
for (final t in tags3.take(5)) {
|
||||
final tm = t as Map;
|
||||
print(' 🏷️ ${tm['name']}: ${tm['count']}道');
|
||||
}
|
||||
}
|
||||
|
||||
print('\n━━━ 6. 验证动态筛选效果 ━━━');
|
||||
final c1 = fs1?['matched_count'] ?? 0;
|
||||
final c2 = fs2?['matched_count'] ?? 0;
|
||||
final c3 = fs3?['matched_count'] ?? 0;
|
||||
print(' 无筛选: $c1 道');
|
||||
print(' 选分类: $c2 道');
|
||||
print(' 分类+标签: $c3 道');
|
||||
if (c2 < c1 && c3 < c2) {
|
||||
print(' ✅ 动态筛选正常:选项越多,匹配越少');
|
||||
} else if (c2 <= c1 && c3 <= c2) {
|
||||
print(' ⚠️ 动态筛选部分正常');
|
||||
} else {
|
||||
print(' ❌ 动态筛选异常');
|
||||
}
|
||||
|
||||
print('\n━━━ 7. filter_apply: 获取推荐菜谱 ━━━');
|
||||
final applyResult = await fetchFilterApply(categories: [12], tags: [1], count: 3);
|
||||
if (applyResult != null) {
|
||||
final recipes = applyResult['recipes'] as List? ?? [];
|
||||
print(' 返回菜谱数: ${recipes.length}');
|
||||
print(' 总匹配数: ${applyResult['total_matched']}');
|
||||
for (final r in recipes) {
|
||||
final rm = r as Map;
|
||||
print(' 🍳 ${rm['title']} (ID: ${rm['id']})');
|
||||
}
|
||||
}
|
||||
|
||||
print('\n=== 测试完成 ===');
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>?> getJson(String endpoint, Map<String, String> params) async {
|
||||
try {
|
||||
final uri = Uri.parse('$baseUrl/$endpoint').replace(queryParameters: params);
|
||||
final response = await HttpClient()
|
||||
.getUrl(uri)
|
||||
.then((r) => r.close())
|
||||
.timeout(const Duration(seconds: 15));
|
||||
final body = await response.transform(utf8.decoder).join();
|
||||
return json.decode(body) as Map<String, dynamic>;
|
||||
} catch (e) {
|
||||
print(' ❌ 请求失败: $e');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>?> fetchFilterSteps({
|
||||
List<int>? categories,
|
||||
List<int>? tags,
|
||||
}) async {
|
||||
final params = <String, String>{'act': 'filter_steps'};
|
||||
if (categories != null && categories.isNotEmpty) {
|
||||
params['category'] = categories.join(',');
|
||||
}
|
||||
if (tags != null && tags.isNotEmpty) {
|
||||
params['tag'] = tags.join(',');
|
||||
}
|
||||
final data = await getJson('api_what_to_eat.php', params);
|
||||
if (data != null && data['code'] == 200) {
|
||||
return data['data'] as Map<String, dynamic>?;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>?> fetchFilterApply({
|
||||
List<int>? categories,
|
||||
List<int>? tags,
|
||||
int count = 5,
|
||||
}) async {
|
||||
final params = <String, String>{'act': 'filter_apply', 'count': '$count'};
|
||||
if (categories != null && categories.isNotEmpty) {
|
||||
params['category'] = categories.join(',');
|
||||
}
|
||||
if (tags != null && tags.isNotEmpty) {
|
||||
params['tag'] = tags.join(',');
|
||||
}
|
||||
final data = await getJson('api_what_to_eat.php', params);
|
||||
if (data != null && data['code'] == 200) {
|
||||
return data['data'] as Map<String, dynamic>?;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
394
scripts/test_what_to_eat.dart
Normal file
394
scripts/test_what_to_eat.dart
Normal file
@@ -0,0 +1,394 @@
|
||||
// 2026-04-20 | test_what_to_eat.dart | 今天吃什么完整接口测试 | 验证动态筛选全流程
|
||||
// 运行: dart run scripts/test_what_to_eat.dart
|
||||
|
||||
import 'dart:convert';
|
||||
import 'dart:math';
|
||||
import 'dart:io';
|
||||
|
||||
const String baseUrl = 'https://eat.wktyl.com/api';
|
||||
|
||||
int passCount = 0;
|
||||
int failCount = 0;
|
||||
int warnCount = 0;
|
||||
|
||||
void pass(String msg) {
|
||||
passCount++;
|
||||
print(' ✅ $msg');
|
||||
}
|
||||
|
||||
void fail(String msg) {
|
||||
failCount++;
|
||||
print(' ❌ $msg');
|
||||
}
|
||||
|
||||
void warn(String msg) {
|
||||
warnCount++;
|
||||
print(' ⚠️ $msg');
|
||||
}
|
||||
|
||||
Future<void> main() async {
|
||||
print('╔══════════════════════════════════════════╗');
|
||||
print('║ 🍽️ 今天吃什么 - 动态筛选接口测试 ║');
|
||||
print('╚══════════════════════════════════════════╝\n');
|
||||
|
||||
// ─── 1. api_filter: 菜谱大类 ───
|
||||
print('━━━ 1. api_filter: 获取菜谱大类 ━━━');
|
||||
final mainCatsData = await getJson('api_filter.php', {'act': 'recipe_main_categories'});
|
||||
List<Map> mainCats = [];
|
||||
if (mainCatsData != null && mainCatsData['code'] == 200) {
|
||||
mainCats = (mainCatsData['data']?['list'] as List? ?? []).cast<Map>();
|
||||
print(' 大类数量: ${mainCats.length}');
|
||||
for (final c in mainCats) {
|
||||
print(' 📂 ${c['name']} (ID:${c['id']}): ${c['recipe_count']}道');
|
||||
}
|
||||
if (mainCats.isNotEmpty) {
|
||||
pass('菜谱大类加载成功 (${mainCats.length}个)');
|
||||
} else {
|
||||
fail('菜谱大类为空');
|
||||
}
|
||||
} else {
|
||||
fail('菜谱大类请求失败');
|
||||
}
|
||||
|
||||
// ─── 2. api_filter: 子类 ───
|
||||
print('\n━━━ 2. api_filter: 获取子分类 ━━━');
|
||||
int? testParentId;
|
||||
List<Map> subCats = [];
|
||||
if (mainCats.isNotEmpty) {
|
||||
testParentId = mainCats.first['id'] as int;
|
||||
final subData = await getJson('api_filter.php', {
|
||||
'act': 'recipe_sub_categories',
|
||||
'parent_id': '$testParentId',
|
||||
});
|
||||
if (subData != null && subData['code'] == 200) {
|
||||
subCats = (subData['data']?['list'] as List? ?? []).cast<Map>();
|
||||
print(' 父类ID=$testParentId 的子类数量: ${subCats.length}');
|
||||
for (final c in subCats.take(5)) {
|
||||
print(' 📂 ${c['name']} (ID:${c['id']}): ${c['recipe_count']}道');
|
||||
}
|
||||
if (subCats.isNotEmpty) {
|
||||
pass('子分类加载成功 (${subCats.length}个)');
|
||||
} else {
|
||||
warn('子分类为空,可能该大类无子分类');
|
||||
}
|
||||
} else {
|
||||
fail('子分类请求失败');
|
||||
}
|
||||
} else {
|
||||
warn('无大类数据,跳过子分类测试');
|
||||
}
|
||||
|
||||
// ─── 3. filter_steps: 无筛选条件 ───
|
||||
print('\n━━━ 3. filter_steps: 无筛选条件(基线) ━━━');
|
||||
final fs0 = await fetchFilterSteps();
|
||||
int baseMatched = 0;
|
||||
int baseTagCount = 0;
|
||||
if (fs0 != null) {
|
||||
baseMatched = fs0['matched_count'] ?? 0;
|
||||
final tags = (fs0['available_tags'] as List? ?? []);
|
||||
baseTagCount = tags.length;
|
||||
print(' 匹配菜谱数: $baseMatched');
|
||||
print(' 可用标签数: $baseTagCount');
|
||||
for (final t in tags.take(5)) {
|
||||
final tm = t as Map;
|
||||
print(' 🏷️ ${tm['name']}: ${tm['count']}道');
|
||||
}
|
||||
if (baseMatched > 0) {
|
||||
pass('无筛选基线: $baseMatched 道菜谱');
|
||||
} else {
|
||||
fail('无筛选基线: 匹配数为0');
|
||||
}
|
||||
if (baseTagCount > 0) {
|
||||
pass('无筛选基线: $baseTagCount 个标签');
|
||||
} else {
|
||||
warn('无筛选基线: 标签为空');
|
||||
}
|
||||
} else {
|
||||
fail('filter_steps 无筛选请求失败');
|
||||
}
|
||||
|
||||
// ─── 4. filter_steps: 选1个分类 ───
|
||||
print('\n━━━ 4. filter_steps: 选1个分类 ━━━');
|
||||
int cat1Matched = 0;
|
||||
int cat1TagCount = 0;
|
||||
List<Map> cat1Tags = [];
|
||||
if (testParentId != null) {
|
||||
final fs1 = await fetchFilterSteps(categories: [testParentId]);
|
||||
if (fs1 != null) {
|
||||
cat1Matched = fs1['matched_count'] ?? 0;
|
||||
cat1Tags = ((fs1['available_tags'] as List? ?? [])).cast<Map>();
|
||||
cat1TagCount = cat1Tags.length;
|
||||
print(' 选分类[$testParentId]: 匹配 $cat1Matched 道');
|
||||
print(' 可用标签数: $cat1TagCount');
|
||||
for (final t in cat1Tags.take(5)) {
|
||||
print(' 🏷️ ${t['name']}: ${t['count']}道');
|
||||
}
|
||||
if (cat1Matched <= baseMatched) {
|
||||
pass('选1分类后匹配数($cat1Matched) <= 基线($baseMatched)');
|
||||
} else {
|
||||
fail('选1分类后匹配数($cat1Matched) > 基线($baseMatched),逻辑异常');
|
||||
}
|
||||
if (cat1TagCount <= baseTagCount) {
|
||||
pass('选1分类后标签数($cat1TagCount) <= 基线($baseTagCount)');
|
||||
} else {
|
||||
warn('选1分类后标签数($cat1TagCount) > 基线($baseTagCount)');
|
||||
}
|
||||
} else {
|
||||
fail('filter_steps 选1分类请求失败');
|
||||
}
|
||||
} else {
|
||||
warn('无分类ID,跳过');
|
||||
}
|
||||
|
||||
// ─── 5. filter_steps: 选分类+标签 ───
|
||||
print('\n━━━ 5. filter_steps: 选分类+标签 ━━━');
|
||||
int catTagMatched = 0;
|
||||
if (testParentId != null && cat1Tags.isNotEmpty) {
|
||||
final firstTagId = cat1Tags.first['id'];
|
||||
final firstTagName = cat1Tags.first['name'];
|
||||
final fs2 = await fetchFilterSteps(categories: [testParentId], tags: [firstTagId]);
|
||||
if (fs2 != null) {
|
||||
catTagMatched = fs2['matched_count'] ?? 0;
|
||||
final tags2 = (fs2['available_tags'] as List? ?? []);
|
||||
print(' 选分类[$testParentId]+标签[$firstTagName($firstTagId)]: 匹配 $catTagMatched 道');
|
||||
print(' 可用标签数: ${tags2.length}');
|
||||
for (final t in tags2.take(5)) {
|
||||
final tm = t as Map;
|
||||
print(' 🏷️ ${tm['name']}: ${tm['count']}道');
|
||||
}
|
||||
if (catTagMatched <= cat1Matched) {
|
||||
pass('分类+标签匹配数($catTagMatched) <= 仅分类($cat1Matched)');
|
||||
} else {
|
||||
fail('分类+标签匹配数($catTagMatched) > 仅分类($cat1Matched),逻辑异常');
|
||||
}
|
||||
} else {
|
||||
fail('filter_steps 分类+标签请求失败');
|
||||
}
|
||||
} else {
|
||||
warn('无标签数据,跳过');
|
||||
}
|
||||
|
||||
// ─── 6. filter_steps: 多分类组合 ───
|
||||
print('\n━━━ 6. filter_steps: 多分类组合 ━━━');
|
||||
if (mainCats.length >= 2 && testParentId != null) {
|
||||
final secondCatId = mainCats[1]['id'] as int;
|
||||
final secondCatName = mainCats[1]['name'];
|
||||
final fs3 = await fetchFilterSteps(categories: [testParentId, secondCatId]);
|
||||
if (fs3 != null) {
|
||||
final multiMatched = fs3['matched_count'] ?? 0;
|
||||
print(' 选分类[$testParentId,$secondCatId($secondCatName)]: 匹配 $multiMatched 道');
|
||||
print(' 对比: 仅1分类=$cat1Matched, 2分类=$multiMatched');
|
||||
if (multiMatched >= cat1Matched) {
|
||||
pass('多分类匹配数($multiMatched) >= 单分类($cat1Matched) [OR逻辑正确]');
|
||||
} else {
|
||||
warn('多分类匹配数($multiMatched) < 单分类($cat1Matched),可能子分类重叠');
|
||||
}
|
||||
} else {
|
||||
fail('filter_steps 多分类请求失败');
|
||||
}
|
||||
} else {
|
||||
warn('大类不足2个,跳过多分类测试');
|
||||
}
|
||||
|
||||
// ─── 7. filter_steps: 子分类筛选 ───
|
||||
print('\n━━━ 7. filter_steps: 子分类筛选 ━━━');
|
||||
if (subCats.isNotEmpty) {
|
||||
final subCatId = subCats.first['id'] as int;
|
||||
final subCatName = subCats.first['name'];
|
||||
final fs4 = await fetchFilterSteps(categories: [subCatId]);
|
||||
if (fs4 != null) {
|
||||
final subMatched = fs4['matched_count'] ?? 0;
|
||||
print(' 选子分类[$subCatName($subCatId)]: 匹配 $subMatched 道');
|
||||
if (subMatched <= cat1Matched && subMatched > 0) {
|
||||
pass('子分类匹配数($subMatched) <= 父分类($cat1Matched) 且 > 0');
|
||||
} else if (subMatched == 0) {
|
||||
warn('子分类匹配数为0,可能无直接关联菜谱');
|
||||
} else {
|
||||
warn('子分类匹配数($subMatched) > 父分类($cat1Matched)');
|
||||
}
|
||||
} else {
|
||||
fail('filter_steps 子分类请求失败');
|
||||
}
|
||||
} else {
|
||||
warn('无子分类数据,跳过');
|
||||
}
|
||||
|
||||
// ─── 8. filter_apply: 获取推荐菜谱 ───
|
||||
print('\n━━━ 8. filter_apply: 获取推荐菜谱 ━━━');
|
||||
if (testParentId != null) {
|
||||
final applyResult = await fetchFilterApply(categories: [testParentId], count: 3);
|
||||
if (applyResult != null) {
|
||||
final recipes = (applyResult['recipes'] as List? ?? []);
|
||||
final total = applyResult['total_matched'] ?? 0;
|
||||
print(' 返回菜谱数: ${recipes.length}');
|
||||
print(' 总匹配数: $total');
|
||||
for (final r in recipes) {
|
||||
final rm = r as Map;
|
||||
print(' 🍳 ${rm['title']} (ID: ${rm['id']})');
|
||||
}
|
||||
if (recipes.isNotEmpty) {
|
||||
pass('filter_apply 返回 ${recipes.length} 道菜谱');
|
||||
} else {
|
||||
warn('filter_apply 返回空列表');
|
||||
}
|
||||
if (total == cat1Matched) {
|
||||
pass('filter_apply 总匹配数($total) = filter_steps 匹配数($cat1Matched)');
|
||||
} else {
|
||||
warn('filter_apply 总匹配数($total) != filter_steps 匹配数($cat1Matched)');
|
||||
}
|
||||
} else {
|
||||
fail('filter_apply 请求失败');
|
||||
}
|
||||
} else {
|
||||
warn('无分类ID,跳过');
|
||||
}
|
||||
|
||||
// ─── 9. filter_apply: 分类+标签组合 ───
|
||||
print('\n━━━ 9. filter_apply: 分类+标签组合 ━━━');
|
||||
if (testParentId != null && cat1Tags.isNotEmpty) {
|
||||
final tagId = cat1Tags.first['id'];
|
||||
final tagName = cat1Tags.first['name'];
|
||||
final applyResult2 = await fetchFilterApply(
|
||||
categories: [testParentId],
|
||||
tags: [tagId],
|
||||
count: 3,
|
||||
);
|
||||
if (applyResult2 != null) {
|
||||
final recipes = (applyResult2['recipes'] as List? ?? []);
|
||||
final total = applyResult2['total_matched'] ?? 0;
|
||||
print(' 分类[$testParentId]+标签[$tagName]: 返回 ${recipes.length} 道菜谱');
|
||||
print(' 总匹配数: $total');
|
||||
for (final r in recipes) {
|
||||
final rm = r as Map;
|
||||
print(' 🍳 ${rm['title']} (ID: ${rm['id']})');
|
||||
}
|
||||
if (total <= cat1Matched) {
|
||||
pass('分类+标签总匹配($total) <= 仅分类($cat1Matched)');
|
||||
} else {
|
||||
fail('分类+标签总匹配($total) > 仅分类($cat1Matched),逻辑异常');
|
||||
}
|
||||
} else {
|
||||
fail('filter_apply 分类+标签请求失败');
|
||||
}
|
||||
} else {
|
||||
warn('无标签数据,跳过');
|
||||
}
|
||||
|
||||
// ─── 10. 动态筛选核心验证 ───
|
||||
print('\n━━━ 10. 动态筛选核心验证 ━━━');
|
||||
print(' 基线(无筛选): $baseMatched 道');
|
||||
print(' 选1分类: $cat1Matched 道');
|
||||
print(' 分类+标签: $catTagMatched 道');
|
||||
|
||||
if (cat1Matched > 0 && cat1Matched < baseMatched && catTagMatched > 0 && catTagMatched < cat1Matched) {
|
||||
pass('🎯 动态筛选核心逻辑正确:选项越多,匹配越少');
|
||||
} else if (cat1Matched > 0 && cat1Matched <= baseMatched && catTagMatched >= 0 && catTagMatched <= cat1Matched) {
|
||||
warn('动态筛选部分正常(可能标签筛选后匹配数不变)');
|
||||
} else {
|
||||
fail('动态筛选逻辑异常');
|
||||
}
|
||||
|
||||
// ─── 11. 标签count准确性抽检 ───
|
||||
print('\n━━━ 11. 标签count准确性抽检 ━━━');
|
||||
if (cat1Tags.isNotEmpty && testParentId != null) {
|
||||
final pid = testParentId;
|
||||
final sampleTag = cat1Tags[Random().nextInt(cat1Tags.length.clamp(0, cat1Tags.length - 1))];
|
||||
final sampleTagId = sampleTag['id'] as int;
|
||||
final sampleTagName = sampleTag['name'];
|
||||
final sampleTagCount = sampleTag['count'] ?? 0;
|
||||
print(' 抽检标签: $sampleTagName (ID:$sampleTagId, 声称:$sampleTagCount道)');
|
||||
|
||||
final verifyResult = await fetchFilterSteps(categories: [pid], tags: [sampleTagId]);
|
||||
if (verifyResult != null) {
|
||||
final verifyMatched = verifyResult['matched_count'] ?? 0;
|
||||
print(' 实际匹配: $verifyMatched 道');
|
||||
if (verifyMatched == sampleTagCount) {
|
||||
pass('标签count准确: $sampleTagCount == $verifyMatched');
|
||||
} else if ((verifyMatched - sampleTagCount).abs() <= 2) {
|
||||
warn('标签count近似: 声称$sampleTagCount vs 实际$verifyMatched (差${(verifyMatched - sampleTagCount).abs()})');
|
||||
} else {
|
||||
fail('标签count偏差大: 声称$sampleTagCount vs 实际$verifyMatched (差${(verifyMatched - sampleTagCount).abs()})');
|
||||
}
|
||||
} else {
|
||||
fail('抽检验证请求失败');
|
||||
}
|
||||
} else {
|
||||
warn('无标签数据,跳过抽检');
|
||||
}
|
||||
|
||||
// ─── 汇总 ───
|
||||
print('\n╔══════════════════════════════════════════╗');
|
||||
print('║ 📊 测试结果汇总 ║');
|
||||
print('╠══════════════════════════════════════════╣');
|
||||
print('║ ✅ 通过: $passCount ║');
|
||||
print('║ ⚠️ 警告: $warnCount ║');
|
||||
print('║ ❌ 失败: $failCount ║');
|
||||
print('╚══════════════════════════════════════════╝');
|
||||
|
||||
if (failCount == 0) {
|
||||
print('\n🎉 所有核心测试通过!动态筛选功能正常。');
|
||||
} else {
|
||||
print('\n⚠️ 存在 $failCount 个失败项,请检查接口逻辑。');
|
||||
}
|
||||
|
||||
exit(failCount > 0 ? 1 : 0);
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>?> getJson(String endpoint, Map<String, String> params) async {
|
||||
try {
|
||||
final uri = Uri.parse('$baseUrl/$endpoint').replace(queryParameters: params);
|
||||
final client = HttpClient();
|
||||
client.connectionTimeout = const Duration(seconds: 15);
|
||||
final request = await client.getUrl(uri);
|
||||
final response = await request.close();
|
||||
final body = await response.transform(utf8.decoder).join();
|
||||
client.close();
|
||||
return json.decode(body) as Map<String, dynamic>;
|
||||
} catch (e) {
|
||||
print(' ❌ 请求失败: $e');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>?> fetchFilterSteps({
|
||||
List<int>? categories,
|
||||
List<int>? tags,
|
||||
}) async {
|
||||
final params = <String, String>{'act': 'filter_steps'};
|
||||
if (categories != null && categories.isNotEmpty) {
|
||||
params['category'] = categories.join(',');
|
||||
}
|
||||
if (tags != null && tags.isNotEmpty) {
|
||||
params['tag'] = tags.join(',');
|
||||
}
|
||||
final data = await getJson('api_what_to_eat.php', params);
|
||||
if (data != null && data['code'] == 200) {
|
||||
return data['data'] as Map<String, dynamic>?;
|
||||
}
|
||||
if (data != null && data['code'] != 200) {
|
||||
print(' ❌ 接口返回错误: code=${data['code']}, msg=${data['message']}');
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>?> fetchFilterApply({
|
||||
List<int>? categories,
|
||||
List<int>? tags,
|
||||
int count = 5,
|
||||
}) async {
|
||||
final params = <String, String>{'act': 'filter_apply', 'count': '$count'};
|
||||
if (categories != null && categories.isNotEmpty) {
|
||||
params['category'] = categories.join(',');
|
||||
}
|
||||
if (tags != null && tags.isNotEmpty) {
|
||||
params['tag'] = tags.join(',');
|
||||
}
|
||||
final data = await getJson('api_what_to_eat.php', params);
|
||||
if (data != null && data['code'] == 200) {
|
||||
return data['data'] as Map<String, dynamic>?;
|
||||
}
|
||||
if (data != null && data['code'] != 200) {
|
||||
print(' ❌ 接口返回错误: code=${data['code']}, msg=${data['message']}');
|
||||
}
|
||||
return null;
|
||||
}
|
||||
Reference in New Issue
Block a user