# 代码闪退风险分析报告 > 分析日期: 2026-04-11 > 修复日期: 2026-04-11 > 分析范围: 全项目代码审查 > 严重程度: 🔴 高危 🟡 中危 🟢 低危 --- ## 修复进度 ### ✅ 已修复 (P0) - [x] WeeklyMenuController 全局注册 - [x] BedtimeReminderController 全局注册 - [x] 移除页面中的重复 Get.put 调用 - [x] HiveService 添加 box 缓存机制 - [x] BedtimeReminderController 添加时间有效性检查 ### ⏸️ 待修复 (P2) - [ ] 本地通知功能实现(需添加 flutter_local_notifications 依赖) --- ## 一、Controller 重复注册风险 🔴 高危 ### 问题描述 多个页面使用 `Get.put()` 直接注册控制器,可能导致重复注册或生命周期混乱。 ### 风险代码位置 #### 1. WeeklyMenuController 重复注册 **文件**: `lib/src/pages/tools/weekly_menu_planner_page.dart:26` ```dart final WeeklyMenuController _controller = Get.put(WeeklyMenuController()); ``` **风险**: - 该控制器未在 `AppBinding` 中全局注册 - 每次进入页面都会重新创建实例 - 如果页面被多次推入,可能导致多个实例共存 - Hive 数据可能被不同实例覆盖 **建议修复**: ```dart // 方案1: 在 AppBinding 中全局注册 // lib/src/app_binding.dart Get.put(WeeklyMenuController(), permanent: true); // 方案2: 使用 Get.lazyPut + fenix class WeeklyMenuBinding extends Bindings { @override void dependencies() { Get.lazyPut(() => WeeklyMenuController(), fenix: true); } } ``` #### 2. BedtimeReminderController 重复注册 **文件**: `lib/src/pages/profile/bedtime_reminder_page.dart:19` ```dart final controller = Get.put(BedtimeReminderController()); ``` **风险**: 同上 **建议修复**: ```dart // 在 AppBinding 中全局注册 // lib/src/app_binding.dart Get.put(BedtimeReminderController(), permanent: true); ``` #### 3. MealRecordController 重复注册 **文件**: `lib/src/pages/home/home_page.dart:53` ```dart Get.put(MealRecordController(), permanent: true); ``` **风险**: - 该控制器已在 `NutritionBinding` 中注册(带检查) - 这里再次注册可能导致重复实例 **建议修复**: 移除 home_page.dart 中的注册,依赖 NutritionBinding 的注册逻辑 --- ## 二、HiveService 通用方法风险 🔴 高危 ### 问题描述 新添加的通用 `get()` 和 `put()` 方法直接使用 `Hive.box(boxName)` 打开 box,缺少安全检查。 ### 风险代码位置 **文件**: `lib/src/services/data/hive_service.dart:449-473` ```dart dynamic get(String boxName, String key) { if (!_initialized) return null; try { final box = Hive.box(boxName); // ⚠️ box 可能不存在 return box.get(key); } catch (e) { LoggerService().error('HiveService get error ($boxName/$key): $e'); return null; } } ``` **风险**: - 如果 box 不存在,`Hive.box(boxName)` 会抛出异常 - 虽然有 try-catch,但每次访问都会尝试打开 box,性能差 - 多个并发访问可能导致 box 重复打开 **建议修复**: ```dart // 添加 box 缓存机制 final Map> _dynamicBoxCache = {}; Box _getOrOpenBox(String boxName) { if (_dynamicBoxCache.containsKey(boxName)) { return _dynamicBoxCache[boxName]!; } try { final box = Hive.box(boxName); _dynamicBoxCache[boxName] = box; return box; } catch (e) { LoggerService().error('Failed to open box $boxName: $e'); rethrow; } } dynamic get(String boxName, String key) { if (!_initialized) return null; try { final box = _getOrOpenBox(boxName); return box.get(key); } catch (e) { LoggerService().error('HiveService get error ($boxName/$key): $e'); return null; } } // 在 close() 方法中清理缓存 Future close() async { // ... 现有代码 ... for (final box in _dynamicBoxCache.values) { await box.close(); } _dynamicBoxCache.clear(); _initialized = false; } ``` --- ## 三、空指针风险 🟡 中危 ### 问题描述 多处代码缺少空值检查,可能导致空指针异常。 ### 风险代码位置 #### 1. WeeklyMenuController 数据访问 **文件**: `lib/src/controllers/weekly_menu_controller.dart:111-114` ```dart void addMealToDay(String dateKey, String mealType, RecipeModel recipe) { if (currentMenu.value == null) return; final dayMenu = currentMenu.value!.dailyMenus[dateKey]; if (dayMenu == null) return; // ✅ 有检查 // ... } ``` **评价**: 此处有检查,但其他类似方法可能遗漏 #### 2. BedtimeReminderController 时间计算 **文件**: `lib/src/controllers/bedtime_reminder_controller.dart:169-187` ```dart bool shouldShowBeforeSleepEatingWarning() { if (!beforeSleepEatingReminder.value) return false; final now = DateTime.now(); final dinnerTime = DateTime( now.year, now.month, now.day, dinnerHour.value, dinnerMinute.value, ); // ⚠️ 如果 dinnerHour/dinnerMinute 超出有效范围(如 25:70),DateTime 构造会抛异常 // ... } ``` **建议修复**: ```dart bool shouldShowBeforeSleepEatingWarning() { if (!beforeSleepEatingReminder.value) return false; // 添加时间有效性检查 if (dinnerHour.value < 0 || dinnerHour.value > 23 || dinnerMinute.value < 0 || dinnerMinute.value > 59) { debugPrint('Invalid dinner time: ${dinnerHour.value}:${dinnerMinute.value}'); return false; } final now = DateTime.now(); final dinnerTime = DateTime( now.year, now.month, now.day, dinnerHour.value, dinnerMinute.value, ); // ... } ``` --- ## 四、TODO 未完成功能 🟡 中危 ### 问题描述 代码中存在 TODO 标记的功能未实现,可能导致功能不完整。 ### 未完成功能 #### 1. 本地通知功能 **文件**: `lib/src/controllers/bedtime_reminder_controller.dart:126-129` ```dart if (enabled) { ToastService.show(message: '已启用就寝提醒 🔔'); // TODO: 实际设置本地通知 } else { ToastService.show(message: '已关闭就寝提醒 🔕'); // TODO: 取消本地通知 } ``` **影响**: - 用户启用提醒后不会收到实际通知 - 用户体验不完整 **建议实现**: ```dart import 'package:flutter_local_notifications/flutter_local_notifications.dart'; class BedtimeReminderController extends BaseController { final FlutterLocalNotificationsPlugin _notifications = FlutterLocalNotificationsPlugin(); @override void onInit() { super.onInit(); _initNotifications(); _loadSettings(); _calculateRecommendedBedtime(); } Future _initNotifications() async { const androidSettings = AndroidInitializationSettings('@mipmap/ic_launcher'); const iosSettings = DarwinInitializationSettings(); const settings = InitializationSettings( android: androidSettings, iOS: iosSettings, ); await _notifications.initialize(settings); } Future _scheduleNotification() async { final now = DateTime.now(); final bedtime = DateTime( now.year, now.month, now.day, recommendedBedtimeHour.value, recommendedBedtimeMinute.value, ); if (bedtime.isBefore(now)) { bedtime = bedtime.add(Duration(days: 1)); } await _notifications.zonedSchedule( 'bedtime_reminder', '就寝提醒', controller.bedtimeRecommendation, tz.TZDateTime.from(bedtime, tz.local), const NotificationDetails( android: AndroidNotificationDetails( 'bedtime_reminder_channel', '就寝提醒', importance: Importance.high, ), iOS: DarwinNotificationDetails(), ), uiLocalNotificationDateInterpretation: UILocalNotificationDateInterpretation.absoluteTime, ); } void toggleReminder(bool enabled) { reminderEnabled.value = enabled; _saveSettings(); if (enabled) { ToastService.show(message: '已启用就寝提醒 🔔'); _scheduleNotification(); } else { ToastService.show(message: '已关闭就寝提醒 🔕'); _notifications.cancel('bedtime_reminder'); } } } ``` --- ## 五、代码不足与改进建议 🟢 低危 ### 1. 缺少输入验证 **问题**: 用户输入的时间、数值等缺少范围验证 **建议**: - 在 Controller 中添加数据验证方法 - 在 UI 层添加输入限制(如 CupertinoPicker 的范围) - 添加错误提示 ### 2. 缺少日志记录 **问题**: 关键操作缺少日志记录,不利于问题排查 **建议**: - 在数据保存/加载时添加日志 - 在异常处理时记录详细错误信息 - 使用 LoggerService 替代 debugPrint ### 3. 缺少数据迁移机制 **问题**: Hive 数据结构变更时缺少迁移机制 **建议**: - 为每个 box 添加 schema 版本号 - 实现数据迁移逻辑 - 参考现有的 `_runMigrations()` 方法扩展 ### 4. 缺少单元测试 **问题**: 关键业务逻辑缺少单元测试 **建议**: - 为 Controller 添加单元测试 - 为 Service 添加单元测试 - 为工具类添加单元测试 --- ## 六、优先级修复建议 ### 🔴 立即修复(P0) 1. **Controller 重复注册问题** - 可能导致数据丢失和内存泄漏 2. **HiveService 通用方法风险** - 可能导致数据读写失败 ### 🟡 尽快修复(P1) 3. **空指针风险** - 添加时间有效性检查 4. **本地通知功能实现** - 完善用户体验 ### 🟢 计划修复(P2) 5. **输入验证** - 提升代码健壮性 6. **日志记录** - 便于问题排查 7. **单元测试** - 保证代码质量 --- ## 七、总结 本次代码审查发现的主要问题: 1. **Controller 管理混乱** - 多处重复注册,需要统一管理 2. **HiveService 安全性不足** - 通用方法缺少 box 缓存和错误处理 3. **功能不完整** - 本地通知等 TODO 未实现 4. **缺少防御性编程** - 输入验证、空值检查不足 建议优先修复 P0 和 P1 级别问题,然后逐步完善 P2 级别改进。