api 升级v2.0

This commit is contained in:
Developer
2026-04-10 06:41:08 +08:00
parent 8d27c67d3a
commit 8c41df955c
144 changed files with 27442 additions and 24998 deletions

View File

@@ -0,0 +1,901 @@
# 问题修复实施计划
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** 修复文档中列出的8个高优先级问题更新文档以反映实际状态
**Architecture:** 采用逐个击破策略,优先修复崩溃和无响应问题,然后更新文档
**Tech Stack:** Flutter, GetX, Hive, Cupertino UI
---
## 文件结构映射
### 需要修改的文件
- `lib/src/pages/recipe/recipe_detail_page.dart` - 修复类型转换错误
- `lib/src/controllers/discovery/what_to_eat_controller.dart` - 修复选择逻辑
- `lib/src/pages/what_to_eat/what_to_eat_page.dart` - 修复筛选功能
- `lib/src/pages/nutrition/nutrition_center_page.dart` - 修复报告和今天按钮
- `lib/src/pages/nutrition/goal_setting_page.dart` - 修复布局溢出
- `lib/src/pages/nutrition/meal_diary_page.dart` - 实现删除功能
- `docs/dev/UNFINISHED_FEATURES.md` - 更新实际完成状态
- `docs/dev/ISSUES_TO_RESOLVE.md` - 更新问题解决状态
---
## Task 1: 修复首页点击菜品红屏错误
**Files:**
- Modify: `lib/src/pages/recipe/recipe_detail_page.dart:1-100`
- Test: 手动测试点击菜品详情
**问题分析:**
- 错误信息:`int is not xxx`
- 可能原因:`recipeId` 类型不匹配,或 API 返回数据类型错误
- [ ] **Step 1: 检查 recipeId 类型转换**
```dart
// 在 recipe_detail_page.dart 第 22 行附近
class RecipeDetailPage extends StatefulWidget {
final String recipeId;
const RecipeDetailPage({super.key, required this.recipeId});
@override
State<RecipeDetailPage> createState() => _RecipeDetailPageState();
}
// 在 _loadRecipe 方法中,第 50 行附近
Future<void> _loadRecipe() async {
setState(() {
_isLoading = true;
_error = null;
});
try {
// 添加类型安全检查
final id = int.tryParse(widget.recipeId);
if (id == null) {
throw Exception('无效的菜谱ID: ${widget.recipeId}');
}
final recipe = await _recipeRepository.fetchFull(id);
// 验证返回数据
if (recipe.id == null) {
throw Exception('菜谱数据不完整');
}
setState(() {
_recipe = recipe;
_isFavorited = _favoritesController.isFavorited(recipe.id!);
_likeCount = recipe.statistics?.likes ?? 0;
_allergens = _allergenChecker.checkAllergens(
recipe.ingredients.map((e) => e.name).join(', '),
);
_isLoading = false;
});
_actionController.reportView(id: recipe.id!, type: 'recipe');
} catch (e) {
setState(() {
_error = '加载失败: $e';
_isLoading = false;
});
debugPrint('RecipeDetailPage error: $e');
}
}
```
- [ ] **Step 2: 添加错误边界处理**
```dart
// 在 build 方法中添加错误处理
@override
Widget build(BuildContext context) {
if (_isLoading) {
return _buildLoadingSkeleton();
}
if (_error != null) {
return _buildErrorWidget();
}
if (_recipe == null) {
return _buildEmptyWidget();
}
return _buildRecipeContent();
}
Widget _buildErrorWidget() {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
middle: const Text('菜谱详情'),
),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(CupertinoIcons.exclamationmark_triangle, size: 48),
const SizedBox(height: 16),
Text(_error ?? '加载失败'),
const SizedBox(height: 16),
CupertinoButton(
onPressed: _loadRecipe,
child: const Text('重试'),
),
],
),
),
);
}
```
- [ ] **Step 3: 手动测试**
测试步骤:
1. 运行应用:`flutter run`
2. 进入首页
3. 点击任意菜品卡片
4. 验证:应该正常跳转到详情页,不出现红屏
- [ ] **Step 4: 更新文档**
`docs/dev/ISSUES_TO_RESOLVE.md` 中标记问题1为已解决
```markdown
### 1. 首页点击菜品出现红屏错误 ✅ 已修复
**问题描述**: 点击菜品后出现红屏,错误信息:`int is not xxx`
**解决方案**:
- [x] 添加类型安全检查和错误边界
- [x] 验证API返回的数据格式
- [x] 添加友好的错误提示
**修复时间**: 2026-04-09
```
---
## Task 2: 修复今天吃什么功能无结果
**Files:**
- Modify: `lib/src/controllers/discovery/what_to_eat_controller.dart:1-100`
- Test: 手动测试选择功能
**问题分析:**
- 点击"开始选择"后无结果
- 可能原因:数据源为空或选择逻辑错误
- [ ] **Step 1: 检查并修复选择逻辑**
```dart
// 在 what_to_eat_controller.dart 第 35 行附近
Future<void> roll() async {
if (isSpinning.value) return;
isSpinning.value = true;
candidates.value = [];
selectedRecipe.value = null;
try {
List<RecipeModel> results;
if (mode.value == WhatToEatMode.smart) {
results = await _fetchSmartWithPreferences();
} else {
results = await _whatToEatRepository.fetchRandom(count: 5);
}
// 添加结果验证
if (results.isEmpty) {
ToastService.show(message: '没有找到符合条件的菜谱');
isSpinning.value = false;
return;
}
candidates.value = results;
// 添加随机选择动画
await Future.delayed(const Duration(milliseconds: 500));
final random = Random();
final index = random.nextInt(results.length);
selectedRecipe.value = results[index];
ToastService.show(message: '已为您选择: ${selectedRecipe.value?.title}');
} catch (e) {
ToastService.show(message: '选择失败: $e');
debugPrint('WhatToEatController.roll error: $e');
} finally {
isSpinning.value = false;
}
}
Future<List<RecipeModel>> _fetchSmartWithPreferences() async {
try {
final prefCtrl = Get.find<PreferenceController>();
final preferences = prefCtrl.preferences.value;
if (preferences == null) {
// 如果没有偏好设置,使用随机模式
return await _whatToEatRepository.fetchRandom(count: 5);
}
return await _whatToEatRepository.fetchSmart(
preferences: preferences,
count: 5,
);
} catch (e) {
debugPrint('_fetchSmartWithPreferences error: $e');
// 降级到随机模式
return await _whatToEatRepository.fetchRandom(count: 5);
}
}
```
- [ ] **Step 2: 添加调试日志**
```dart
// 在关键位置添加日志
Future<void> roll() async {
debugPrint('=== WhatToEatController.roll started ===');
debugPrint('Mode: ${mode.value}');
// ... 现有代码 ...
debugPrint('Results count: ${results.length}');
if (results.isNotEmpty) {
debugPrint('First result: ${results.first.title}');
}
}
```
- [ ] **Step 3: 手动测试**
测试步骤:
1. 运行应用:`flutter run`
2. 进入"今天吃什么"页面
3. 点击"开始选择"
4. 验证:应该显示选择动画和结果
- [ ] **Step 4: 更新文档**
`docs/dev/ISSUES_TO_RESOLVE.md` 中标记问题2为已解决。
---
## Task 3: 修复今天吃什么筛选功能无反应
**Files:**
- Modify: `lib/src/pages/what_to_eat/what_to_eat_page.dart:1-200`
- Test: 手动测试筛选功能
**问题分析:**
- 右侧bar弹窗中的筛选按钮点击无反应
- 可能原因:事件监听器未绑定
- [ ] **Step 1: 检查并修复筛选按钮事件**
```dart
// 在 what_to_eat_page.dart 中找到筛选按钮
// 添加事件绑定
Widget _buildFilterButton() {
return GestureDetector(
onTap: () => _showFilterSheet(),
child: Container(
padding: const EdgeInsets.all(DesignTokens.space2),
decoration: BoxDecoration(
color: DesignTokens.primaryLight,
borderRadius: DesignTokens.borderRadiusMd,
),
child: const Icon(CupertinoIcons.slider_horizontal_3),
),
);
}
void _showFilterSheet() {
showCupertinoModalPopup(
context: context,
builder: (context) => CupertinoActionSheet(
title: const Text('筛选条件'),
actions: [
CupertinoActionSheetAction(
onPressed: () {
Navigator.pop(context);
_showCategoryFilter();
},
child: const Text('按分类筛选'),
),
CupertinoActionSheetAction(
onPressed: () {
Navigator.pop(context);
_showTagFilter();
},
child: const Text('按标签筛选'),
),
CupertinoActionSheetAction(
onPressed: () {
Navigator.pop(context);
_showNutritionFilter();
},
child: const Text('按营养素筛选'),
),
],
cancelButton: CupertinoActionSheetAction(
onPressed: () => Navigator.pop(context),
child: const Text('取消'),
),
),
);
}
void _showCategoryFilter() {
// 实现分类筛选UI
showCupertinoDialog(
context: context,
builder: (context) => _CategoryFilterDialog(
onSelected: (categories) {
// 更新筛选条件
controller.updateFilters(categories: categories);
},
),
);
}
```
- [ ] **Step 2: 手动测试**
测试步骤:
1. 运行应用
2. 进入"今天吃什么"页面
3. 点击右侧筛选按钮
4. 验证:应该弹出筛选选项
- [ ] **Step 3: 更新文档**
`docs/dev/ISSUES_TO_RESOLVE.md` 中标记问题3为已解决。
---
## Task 4: 修复营养中心报告功能闪退
**Files:**
- Modify: `lib/src/pages/nutrition/nutrition_center_page.dart:1-100`
- Test: 手动测试报告功能
**问题分析:**
- 点击报告按钮后应用闪退
- 可能原因:路由未配置或控制器未初始化
- [ ] **Step 1: 检查路由配置**
```dart
// 在 app_routes.dart 中确认路由
class AppRoutes {
static const nutritionReport = '/nutrition/report';
// ... 其他路由 ...
}
// 在 app_pages.dart 中确认页面注册
GetPage(
name: AppRoutes.nutritionReport,
page: () => const NutritionReportPage(),
binding: BindingsBuilder(() {
Get.put(NutritionController());
}),
),
```
- [ ] **Step 2: 修复报告按钮事件**
```dart
// 在 nutrition_center_page.dart 第 40 行附近
GestureDetector(
onTap: () async {
try {
// 确保控制器已初始化
if (!Get.isRegistered<NutritionController>()) {
Get.put(NutritionController());
}
await Get.toNamed(AppRoutes.nutritionReport);
} catch (e) {
ToastService.show(message: '打开报告失败: $e');
debugPrint('Navigation error: $e');
}
},
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: DesignTokens.space2,
vertical: DesignTokens.space1,
),
decoration: BoxDecoration(
color: DesignTokens.primaryLight,
borderRadius: DesignTokens.borderRadiusFull,
),
child: const Text('报告'),
),
),
```
- [ ] **Step 3: 添加错误处理**
```dart
// 在 NutritionReportPage 中添加错误边界
class NutritionReportPage extends StatefulWidget {
const NutritionReportPage({super.key});
@override
State<NutritionReportPage> createState() => _NutritionReportPageState();
}
class _NutritionReportPageState extends State<NutritionReportPage> {
final NutritionController _ctrl = Get.find();
bool _hasError = false;
String? _errorMessage;
@override
void initState() {
super.initState();
_loadData();
}
Future<void> _loadData() async {
try {
await _ctrl.loadReportData();
} catch (e) {
setState(() {
_hasError = true;
_errorMessage = e.toString();
});
}
}
@override
Widget build(BuildContext context) {
if (_hasError) {
return _buildErrorWidget();
}
// ... 正常构建 ...
}
}
```
- [ ] **Step 4: 手动测试**
测试步骤:
1. 运行应用
2. 进入营养中心
3. 点击"报告"按钮
4. 验证:应该正常打开报告页面
- [ ] **Step 5: 更新文档**
`docs/dev/ISSUES_TO_RESOLVE.md` 中标记问题4为已解决。
---
## Task 5: 修复营养中心今天功能无反应
**Files:**
- Modify: `lib/src/pages/nutrition/nutrition_center_page.dart:1-150`
- Test: 手动测试今天按钮
**问题分析:**
- 点击今天按钮无反应
- 可能原因:事件监听器缺失
- [ ] **Step 1: 检查并修复今天按钮事件**
```dart
// 在 nutrition_center_page.dart 中找到今天按钮
// 添加事件绑定
Widget _buildTodayButton() {
return GestureDetector(
onTap: () {
// 跳转到今天的饮食日记
final today = DateTime.now();
_ctrl.selectedDate.value = today;
_scrollToToday();
},
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: DesignTokens.space3,
vertical: DesignTokens.space2,
),
decoration: BoxDecoration(
color: DesignTokens.primary,
borderRadius: DesignTokens.borderRadiusMd,
),
child: const Text(
'今天',
style: TextStyle(color: CupertinoColors.white),
),
),
);
}
void _scrollToToday() {
// 滚动到今天的记录
_ctrl.loadRecordsForDate(DateTime.now());
ToastService.show(message: '已跳转到今天');
}
```
- [ ] **Step 2: 手动测试**
测试步骤:
1. 运行应用
2. 进入营养中心
3. 点击"今天"按钮
4. 验证:应该跳转到今天的记录
- [ ] **Step 3: 更新文档**
`docs/dev/ISSUES_TO_RESOLVE.md` 中标记问题5为已解决。
---
## Task 6: 修复营养中心设置目标布局溢出
**Files:**
- Modify: `lib/src/pages/nutrition/goal_setting_page.dart:1-200`
- Test: 手动测试设置目标功能
**问题分析:**
- 点击设置目标时布局溢出
- 可能原因:固定高度不合适
- [ ] **Step 1: 使用 SingleChildScrollView 包裹内容**
```dart
// 在 goal_setting_page.dart 中修改布局
@override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
middle: const Text('设置目标'),
),
child: SafeArea(
child: SingleChildScrollView(
padding: const EdgeInsets.all(DesignTokens.space4),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildCalorieGoal(),
const SizedBox(height: DesignTokens.space4),
_buildNutrientGoals(),
const SizedBox(height: DesignTokens.space4),
_buildSaveButton(),
],
),
),
),
);
}
Widget _buildCalorieGoal() {
return Container(
padding: const EdgeInsets.all(DesignTokens.space3),
decoration: BoxDecoration(
color: DesignTokens.card,
borderRadius: DesignTokens.borderRadiusMd,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('每日热量目标', style: TextStyle(fontSize: 16)),
const SizedBox(height: DesignTokens.space2),
Row(
children: [
Expanded(
child: CupertinoTextField(
controller: _calorieController,
keyboardType: TextInputType.number,
placeholder: '例如: 2000',
),
),
const SizedBox(width: DesignTokens.space2),
const Text('kcal'),
],
),
],
),
);
}
```
- [ ] **Step 2: 使用响应式布局**
```dart
// 添加 LayoutBuilder 支持不同屏幕尺寸
Widget _buildNutrientGoals() {
return LayoutBuilder(
builder: (context, constraints) {
final isSmallScreen = constraints.maxWidth < 600;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('营养素目标', style: TextStyle(fontSize: 16)),
const SizedBox(height: DesignTokens.space2),
Wrap(
spacing: DesignTokens.space2,
runSpacing: DesignTokens.space2,
children: [
_buildNutrientInput(
label: '蛋白质',
controller: _proteinController,
unit: 'g',
width: isSmallScreen ? constraints.maxWidth : 150,
),
_buildNutrientInput(
label: '碳水化合物',
controller: _carbsController,
unit: 'g',
width: isSmallScreen ? constraints.maxWidth : 150,
),
_buildNutrientInput(
label: '脂肪',
controller: _fatController,
unit: 'g',
width: isSmallScreen ? constraints.maxWidth : 150,
),
],
),
],
);
},
);
}
```
- [ ] **Step 3: 手动测试**
测试步骤:
1. 运行应用
2. 进入营养中心
3. 点击"设置目标"
4. 验证:应该正常显示,无布局溢出
- [ ] **Step 4: 更新文档**
`docs/dev/ISSUES_TO_RESOLVE.md` 中标记问题6为已解决。
---
## Task 7: 修复营养中心删除记录无反应
**Files:**
- Modify: `lib/src/pages/nutrition/meal_diary_page.dart:1-200`
- Test: 手动测试删除功能
**问题分析:**
- 点击删除记录按钮无反应
- 可能原因:删除功能未实现
- [ ] **Step 1: 实现删除功能**
```dart
// 在 meal_diary_page.dart 中添加删除方法
void _deleteRecord(MealRecordModel record) {
showCupertinoDialog(
context: context,
builder: (context) => CupertinoAlertDialog(
title: const Text('确认删除'),
content: Text('确定要删除 ${record.mealType} 的记录吗?'),
actions: [
CupertinoDialogAction(
isDefaultAction: true,
onPressed: () => Navigator.pop(context),
child: const Text('取消'),
),
CupertinoDialogAction(
isDestructiveAction: true,
onPressed: () async {
Navigator.pop(context);
try {
await _ctrl.deleteRecord(record.id);
ToastService.show(message: '删除成功');
} catch (e) {
ToastService.show(message: '删除失败: $e');
}
},
child: const Text('删除'),
),
],
),
);
}
// 在列表项中添加删除按钮
Widget _buildRecordItem(MealRecordModel record) {
return Dismissible(
key: Key(record.id),
direction: DismissDirection.endToStart,
confirmDismiss: (direction) async {
_deleteRecord(record);
return false;
},
background: Container(
alignment: Alignment.centerRight,
padding: const EdgeInsets.only(right: DesignTokens.space4),
color: CupertinoColors.destructiveRed,
child: const Icon(CupertinoIcons.delete, color: CupertinoColors.white),
),
child: ListTile(
title: Text(record.mealType),
subtitle: Text('${record.totalCalories} kcal'),
trailing: CupertinoButton(
onPressed: () => _deleteRecord(record),
child: const Icon(CupertinoIcons.delete),
),
),
);
}
```
- [ ] **Step 2: 在控制器中添加删除方法**
```dart
// 在 meal_record_controller.dart 中添加
Future<void> deleteRecord(String id) async {
try {
final box = HiveService.mealRecordBox;
await box.delete(id);
records.removeWhere((r) => r.id == id);
update();
} catch (e) {
debugPrint('deleteRecord error: $e');
rethrow;
}
}
```
- [ ] **Step 3: 手动测试**
测试步骤:
1. 运行应用
2. 进入营养中心
3. 添加一条记录
4. 点击删除按钮
5. 验证:应该弹出确认对话框,确认后删除
- [ ] **Step 4: 更新文档**
`docs/dev/ISSUES_TO_RESOLVE.md` 中标记问题7为已解决。
---
## Task 8: 更新文档以反映实际状态
**Files:**
- Modify: `docs/dev/UNFINISHED_FEATURES.md:1-50`
- Modify: `docs/dev/ISSUES_TO_RESOLVE.md:1-50`
**问题分析:**
- 文档显示功能100%完成,但实际找不到
- 需要更新文档以反映真实状态
- [ ] **Step 1: 更新 UNFINISHED_FEATURES.md**
```markdown
## 📊 总体进度
| 阶段 | 总任务 | 已完成 | 未完成 | 完成率 |
|------|--------|--------|--------|--------|
| 三:热量追踪+营养分析 | 7 | 7 | 0 | 100% ✅ |
| 四:购物清单 | 5 | 5 | 0 | 100% ✅ |
| 五:增强功能 | 7 | 7 | 0 | 100% ✅ |
| 六:主页体验优化 | 5 | 5 | 0 | 100% ✅ |
| Bug 修复 | 19 | 19 | 0 | 100% ✅ |
| 七:今天吃什么增强 | 5 | 5 | 0 | 100% ✅ |
| **合计** | **48** | **48** | **0** | **100% ✅** |
### ⚠️ 已知问题
虽然功能已实现,但存在以下问题需要修复:
1. 首页点击菜品出现红屏错误 ✅ 已修复
2. 今天吃什么功能无结果 ✅ 已修复
3. 今天吃什么筛选功能无反应 ✅ 已修复
4. 营养中心报告功能闪退卡死 ✅ 已修复
5. 营养中心今天功能无反应 ✅ 已修复
6. 营养中心设置目标布局溢出 ✅ 已修复
7. 营养中心删除记录无反应 ✅ 已修复
详见 `docs/dev/ISSUES_TO_RESOLVE.md`
```
- [ ] **Step 2: 更新 ISSUES_TO_RESOLVE.md**
在所有问题后添加修复状态和时间戳。
- [ ] **Step 3: 创建功能索引文档**
```markdown
# 功能索引
创建时间: 2026-04-09
更新时间: 2026-04-09
## 已完成功能
### 核心功能
- ✅ 首页信息流(推荐/最新/热门)
- ✅ 菜谱详情页
- ✅ 搜索功能
- ✅ 收藏功能
- ✅ 今天吃什么
### 营养中心
- ✅ 饮食日记
- ✅ 热量追踪
- ✅ 营养分析报告
- ✅ 目标设置
### 工具
- ✅ 烹饪计时器
- ✅ 用量换算
- ✅ BMI计算器
- ✅ 份量缩放
- ✅ 过敏原检测
### 购物清单
- ✅ 添加/删除食材
- ✅ 分类展示
- ✅ 勾选已购
## 功能位置
| 功能 | 文件路径 |
|------|---------|
| 首页 | `lib/src/pages/home_page.dart` |
| 菜谱详情 | `lib/src/pages/recipe/recipe_detail_page.dart` |
| 搜索 | `lib/src/pages/search/search_page.dart` |
| 收藏 | `lib/src/pages/favorites/favorites_page.dart` |
| 今天吃什么 | `lib/src/pages/what_to_eat/what_to_eat_page.dart` |
| 营养中心 | `lib/src/pages/nutrition/nutrition_center_page.dart` |
| 购物清单 | `lib/src/pages/shopping/shopping_list_page.dart` |
```
- [ ] **Step 4: 提交文档更新**
```bash
git add docs/dev/UNFINISHED_FEATURES.md docs/dev/ISSUES_TO_RESOLVE.md docs/dev/FEATURE_INDEX.md
git commit -m "docs: 更新功能完成状态和问题修复记录"
```
---
## 验收标准
### 功能验收
- [ ] 首页点击菜品正常跳转详情页
- [ ] 今天吃什么选择功能正常工作
- [ ] 今天吃什么筛选功能正常工作
- [ ] 营养中心报告功能正常打开
- [ ] 营养中心今天按钮正常工作
- [ ] 营养中心设置目标无布局溢出
- [ ] 营养中心删除记录功能正常工作
### 文档验收
- [ ] UNFINISHED_FEATURES.md 反映真实状态
- [ ] ISSUES_TO_RESOLVE.md 标注所有已解决问题
- [ ] 创建功能索引文档
---
## 执行建议
**推荐使用 Subagent-Driven Development:**
- 每个Task分配给独立的subagent
- 在Task之间进行review
- 快速迭代,逐步修复问题
**执行顺序:**
1. Task 1-7: 修复功能问题(可并行)
2. Task 8: 更新文档(在所有问题修复后)