Files
kitchen/docs/audit/crash_risk_analysis.md
2026-04-11 22:55:27 +08:00

382 lines
9.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 代码闪退风险分析报告
> 分析日期: 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<dynamic>(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<dynamic>(boxName); // ⚠️ box 可能不存在
return box.get(key);
} catch (e) {
LoggerService().error('HiveService get error ($boxName/$key): $e');
return null;
}
}
```
**风险**:
- 如果 box 不存在,`Hive.box<dynamic>(boxName)` 会抛出异常
- 虽然有 try-catch但每次访问都会尝试打开 box性能差
- 多个并发访问可能导致 box 重复打开
**建议修复**:
```dart
// 添加 box 缓存机制
final Map<String, Box<dynamic>> _dynamicBoxCache = {};
Box<dynamic> _getOrOpenBox(String boxName) {
if (_dynamicBoxCache.containsKey(boxName)) {
return _dynamicBoxCache[boxName]!;
}
try {
final box = Hive.box<dynamic>(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<void> 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:70DateTime 构造会抛异常
// ...
}
```
**建议修复**:
```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<void> _initNotifications() async {
const androidSettings = AndroidInitializationSettings('@mipmap/ic_launcher');
const iosSettings = DarwinInitializationSettings();
const settings = InitializationSettings(
android: androidSettings,
iOS: iosSettings,
);
await _notifications.initialize(settings);
}
Future<void> _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 级别改进。