详细页优化

This commit is contained in:
Developer
2026-04-11 22:55:27 +08:00
parent 2d7484fd29
commit 442f648128
213 changed files with 6170 additions and 29298 deletions

View File

@@ -0,0 +1,381 @@
# 代码闪退风险分析报告
> 分析日期: 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 级别改进。