# Habo 架构设计文档

> 基于 REQUIREMENTS.md 中的需求，说明架构决策及其理由

---

## 1. 架构决策总览

### 1.1 为什么选择这个架构

| 决策 | 理由 |
|------|------|
| **纯客户端架构** | 需求：零网络依赖、数据不离开设备（NFR-安全）。没有服务端意味着无运维成本、无数据泄露风险 |
| **SQLite 本地存储** | 需求：持久化存储、离线可用。SQLite 是最成熟的嵌入式数据库，无需额外进程 |
| **Flutter 跨平台** | 需求：支持 Android/iOS/Linux/macOS 四个平台。一套代码覆盖所有目标平台 |
| **Provider + ChangeNotifier** | 需求：响应式 UI 更新。Flutter 官方推荐方案，学习曲线低，适合中等复杂度应用 |
| **Repository Pattern** | 需求：业务逻辑与数据访问解耦。便于替换数据源和编写单元测试 |
| **Navigation 2.0** | 需求：深度链接支持（habo://settings）。声明式路由更易管理页面栈 |

### 1.2 架构约束

- **无网络** — 所有功能离线可用，备份通过文件系统完成
- **单用户** — 无需多用户系统，简化数据模型
- **单数据库** — 一个 SQLite 文件 `habo_db0.db`，数据库版本 9
- **无实时同步** — 数据只存在本地，跨设备迁移依赖手动备份/恢复

---

## 2. 分层架构

```
┌─────────────────────────────────────────────────────────────────┐
│  Presentation Layer (展示层)                                      │
│  ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌────────────┐          │
│  │ Screens  │ │ Widgets  │ │ Onboarding│ │ Dialogs    │          │
│  └────┬─────┘ └────┬─────┘ └────┬─────┘ └─────┬──────┘          │
│       │             │            │              │                 │
├───────┴─────────────┴────────────┴──────────────┴─────────────────┤
│  Business Logic Layer (业务逻辑层)                                │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────────┐        │
│  │ HabitsManager│  │SettingsManager│  │  Statistics      │        │
│  │ (习惯 CRUD)  │  │ (设置管理)    │  │  (统计计算)      │        │
│  └──────┬───────┘  └──────┬───────┘  └────────┬─────────┘        │
│         │                  │                    │                  │
├─────────┴──────────────────┴────────────────────┴─────────────────┤
│  Service Layer (服务层)                                           │
│  ┌────────────┐ ┌────────────┐ ┌──────────┐ ┌───────────────┐    │
│  │ Notification│ │  Backup    │ │UIFeedback│ │ BiometricAuth │    │
│  │  Service   │ │  Service   │ │ Service  │ │   Service     │    │
│  └─────┬──────┘ └─────┬──────┘ └────┬─────┘ └──────┬────────┘    │
│        │               │             │               │            │
├────────┴───────────────┴─────────────┴───────────────┴────────────┤
│  Repository Layer (数据访问层)                                     │
│  ┌─────────────┐ ┌─────────────┐ ┌──────────────┐                 │
│  │  HabitRepo  │ │  EventRepo  │ │ CategoryRepo │                 │
│  │  (接口)     │ │  (接口)     │ │   (接口)     │                 │
│  └──────┬──────┘ └──────┬──────┘ └──────┬───────┘                 │
│         │               │               │                         │
├─────────┴───────────────┴───────────────┴─────────────────────────┤
│  Data Layer (数据层)                                              │
│  ┌──────────────────────────────────────────┐                     │
│  │  HaboModel → SQLite (sqflite / ffi)      │                     │
│  │  数据库: habo_db0.db (版本 9)              │                     │
│  └──────────────────────────────────────────┘                     │
└───────────────────────────────────────────────────────────────────┘
```

---

## 3. 模块职责划分

### 3.1 Presentation Layer

| 模块 | 文件 | 职责 |
|------|------|------|
| **HabitsScreen** | `habits/habits_screen.dart` | 主屏幕入口，承载习惯列表、导航栏、FAB |
| **CalendarColumn** | `habits/calendar_column.dart` | 可拖拽排序的习惯列表容器，含分类筛选 |
| **Habit Card** | `habits/habit.dart` | 单个习惯卡片组件（含日历、连续天数、事件标记） |
| **EditHabit** | `habits/edit_habit.dart` | 创建/编辑习惯的表单页面 |
| **StatisticsScreen** | `statistics/statistics_screen.dart` | 统计总览页面 |
| **SettingsScreen** | `settings/settings_screen.dart` | 设置页面 |
| **Onboarding** | `onboarding/onboarding.dart` | 三步引导流程 |

**设计原则**：
- 展示层不直接操作数据库，只通过 Manager 类
- 每个 Screen 是独立的 StatefulWidget
- 可复用的 UI 片段提取到 `widgets/` 目录

### 3.2 Business Logic Layer

#### HabitsManager（核心管理器）

**为什么需要 Manager 而非直接操作 Repository？**

```
┌─────────────────────────────────────────────┐
│                HabitsManager                  │
│  ┌─────────────────────────────────────┐     │
│  │ 1. 内存状态管理 (allHabits 列表)     │     │
│  │ 2. 业务规则执行 (排序、归档、undo)    │     │
│  │ 3. 服务协调 (通知、备份、小组件)      │     │
│  │ 4. 变更通知 (notifyListeners)        │     │
│  └─────────────────────────────────────┘     │
│          │              │              │      │
│     HabitRepo     EventRepo     CategoryRepo  │
│          │              │              │      │
│     NotificationService  BackupService  ...   │
└─────────────────────────────────────────────┘
```

**职责清单**：
- 维护 `allHabits` 内存列表（活跃 + 归档）
- 协调 Repository 的 CRUD 操作
- 调用通知服务更新提醒
- 调用小组件服务更新桌面小组件
- 调用 UI 反馈服务展示消息
- 处理 undo（删除后可撤销）
- 处理拖拽排序的位置更新

#### SettingsManager

**职责**：
- 管理所有用户偏好设置（主题、音效、通知等）
- 使用 SharedPreferences 持久化
- 管理 SoLoud 音效引擎的初始化和播放
- 通知 UI 主题变化

#### Statistics

**为什么统计是纯函数而非 Manager？**

统计计算是无状态的——每次从当前习惯数据实时计算，不需要维护额外状态。因此设计为纯静态方法 `Statistics.calculateStatistics()`。

### 3.3 Service Layer

**为什么需要 Service 层？**

跨领域的关注点（通知、备份、UI反馈、认证）不属于任何单一 Manager 的职责，提取为独立服务便于复用和测试。

| 服务 | 依赖 | 被谁调用 |
|------|------|----------|
| NotificationService | awesome_notifications | HabitsManager |
| BackupService | RepositoryFactory, 文件系统 | HabitsManager, SettingsScreen |
| UIFeedbackService | ScaffoldMessenger | HabitsManager |
| BiometricAuthService | local_auth | BiometricAuthWrapper |
| HomeWidgetService | home_widget | HabitsManager |
| ServiceLocator | 所有服务 | main.dart (初始化) |

### 3.4 Repository Layer

**为什么用 Repository Pattern 而非直接用 HaboModel？**

```
// 不好的方式 — UI 直接依赖数据层
HaboModel().insertHabit(habit);

// 好的方式 — 通过抽象接口解耦
HabitRepository habitRepo = RepositoryFactory.createHabitRepository(model);
habitRepo.createHabit(habit);
```

好处：
1. **可测试** — 可以用 MockRepository 替换真实数据库
2. **可替换** — 未来换数据库引擎只需改 Repository 实现
3. **关注点分离** — UI/Manager 不知道也不关心数据如何存储

### 3.5 Data Layer (HaboModel)

**定位**：直接操作 SQLite 的底层类，是整个数据层的基石。

**特殊处理**：
- 移动端使用 `sqflite`
- 桌面端（Linux/macOS）使用 `sqflite_common_ffi`
- 启用 `PRAGMA foreign_keys = ON` 确保级联删除
- 管理数据库版本升级迁移（版本 1→9）

---

## 4. 数据流设计

### 4.1 用户打卡事件流

```
用户点击日历日期
  │
  ├─── 一键打卡模式 ON
  │     └── 直接切换 check/clear
  │
  └─── 一键打卡模式 OFF
        └── 弹出选择菜单
              ├── Check (完成)
              ├── Progress (进度，数值型)
              ├── Fail (失败)
              ├── Skip (跳过)
              ├── Note (备注)
              └── Date (修改日期)
                    │
                    ▼
          HabitsManager.addEvent(id, date, eventData)
                    │
          ┌─────────┴─────────┐
          │                    │
    内存状态更新          Repository 写入
    habitData.events    EventRepository.add()
          │                    │
          │              SQLite INSERT/REPLACE
          │                    │
          ▼                    ▼
    notifyListeners()    NotificationService
          │              (奖励/惩罚通知)
          ▼
    Provider → Widget 重建
    (日历标记更新、连续天数更新)
```

### 4.2 应用初始化流

```
main()
  │
  ├── 1. WidgetsFlutterBinding.ensureInitialized()
  ├── 2. SettingsManager.loadData()     ← SharedPreferences
  ├── 3. HaboModel.initDatabase()        ← SQLite 初始化
  ├── 4. ServiceLocator.init(model)      ← 注册所有服务
  ├── 5. HabitsManager(repos, services)  ← 注入依赖
  ├── 6. HabitsManager.loadHabits()      ← 从数据库加载所有习惯
  ├── 7. NotificationService 初始化
  ├── 8. AppRouter(stateManagers)        ← 创建路由
  ├── 9. 日变化定时器启动                ← 检测跨日刷新
  │
  └── runApp(MultiProvider → MaterialApp.router)
        ├── ChangeNotifierProvider<SettingsManager>
        ├── ChangeNotifierProvider<HabitsManager>
        └── ChangeNotifierProvider<AppStateManager>
```

### 4.3 跨日刷新流

```
DayChangeTimer (每小时检查)
  │
  ├── 检测到日期变化 (now.day != lastDay.day)
  │     │
  │     ├── HabitsManager.loadHabits()     ← 重新加载习惯数据
  │     ├── NotificationService.reset()    ← 重置通知调度
  │     └── HomeWidgetService.update()     ← 更新桌面小组件
  │
  └── 未变化 → 等待下次检查
```

---

## 5. 导航架构

### 5.1 为什么选择 Navigation 2.0

| Navigation 1.0 (Navigator) | Navigation 2.0 (RouterDelegate) |
|---------------------------|----------------------------------|
| 命令式 push/pop | 声明式页面栈 |
| 不支持深度链接 | 原生支持 URL 映射 |
| 难以管理复杂页面栈 | 通过状态管理器统一控制 |

### 5.2 页面栈结构

```
AppRouter (RouterDelegate)
  │
  ├── SplashScreen          ← 初始页（条件判断后自动跳转）
  │     │
  │     ├── 首次使用 → OnboardingScreen
  │     ├── 有新版本 → WhatsNewScreen
  │     └── 正常使用 → HabitsScreen (主页面)
  │
  ├── HabitsScreen          ← 主页面（始终在栈底）
  │     ├── → StatisticsScreen
  │     ├── → SettingsScreen
  │     ├── → CreateHabitScreen
  │     └── → EditHabitScreen
  │
  └── 深度链接: habo://settings → 直接打开 SettingsScreen
```

### 5.3 状态驱动导航

```
AppStateManager (ChangeNotifier)
  │
  ├── _statistics: bool    → 控制 StatisticsScreen 显示
  ├── _settings: bool      → 控制 SettingsScreen 显示
  ├── _onboarding: bool    → 控制 OnboardingScreen 显示
  ├── _whatsNew: bool      → 控制 WhatsNewScreen 显示
  ├── _createHabit: bool   → 控制 CreateHabitScreen 显示
  └── _editHabit: bool     → 控制 EditHabitScreen 显示

AppRouter 监听 AppStateManager 变更
  └── 根据 bool 标志位组合构建 pages 列表
```

**关键设计决策**：`AppRouter` 不监听 `HabitsManager`，因为习惯数据变化不应触发导航跳转，只应刷新当前页面内容。

---

## 6. 状态管理策略

### 6.1 Provider 分布

```
MultiProvider(
  providers: [
    ChangeNotifierProvider<SettingsManager>   ← 主题、音效、设置
    ChangeNotifierProvider<HabitsManager>     ← 习惯数据、分类
    ChangeNotifierProvider<AppStateManager>   ← 导航状态
  ]
)
```

### 6.2 读写模式

```dart
// 读取 — context.watch<T>() 或 Provider.of<T>(context)
// UI 组件监听变化并自动重建
final habits = context.watch<HabitsManager>().activeHabits;

// 写入 — context.read<T>() 或 Provider.of<T>(context, listen: false)
// 事件处理中调用方法，不触发当前 widget 重建
context.read<HabitsManager>().addEvent(id, date, event);
```

### 6.3 状态生命周期

| 状态 | 作用域 | 生命周期 |
|------|--------|----------|
| HabitsManager.allHabits | 全局 | 应用启动到关闭 |
| SettingsManager.* | 全局 | 应用启动到关闭 |
| AppStateManager.* | 全局 | 应用启动到关闭 |
| HabitData.events | 每个习惯 | 随 HabitsManager 加载 |
| 习惯卡片 UI 状态 (streak, calendar) | 单个 Widget | Widget 生命周期 |

---

## 7. 主题系统设计

### 7.1 为什么支持 5 种主题模式

| 主题 | 目标用户 | 技术实现 |
|------|----------|----------|
| Device | 大多数用户 | 跟随系统 MediaQuery |
| Light | 强制浅色偏好 | 固定浅色 ColorScheme |
| Dark | 强制深色偏好 | 固定深色 ColorScheme |
| OLED | OLED 屏幕用户 | 纯黑 (#000000) 背景 |
| Material You | Android 12+ 用户 | dynamic_color 提取壁纸颜色 |

### 7.2 颜色体系

```
核心颜色常量:
  primary:    #09BF30  (完成/主色)
  red:        #F44336  (失败)
  skip:       #FBC02D  (跳过)
  orange:     #FF9800  (两天法则警告)
  progress:   #2196F3  (进度)
  progressBg: #E3F2FD  (进度背景)

用户可自定义:
  checkColor:    默认 primary
  failColor:     默认 red
  skipColor:     默认 skip
  progressColor: 默认 progress
```

---

## 8. 通知系统设计

### 8.1 通知类型

| 类型 | 触发条件 | 内容 |
|------|----------|------|
| **每日提醒** | 用户设定时间 | "Do not forget to check your habits." |
| **奖励通知** | 习惯标记完成 + showReward 开启 | "Congratulations! Your reward: {reward}" |
| **惩罚通知** | 习惯标记失败 + showSanction 开启 | "Oh no! Your sanction: {sanction}" |

### 8.2 通知调度策略

```
创建/编辑习惯
  └── NotificationService.resetNotifications()
        ├── 取消所有现有通知
        └── 为每个启用通知的习惯创建定时通知
              └── awesome_notifications.createNotification()
                    ├── channel: "habit_notifications"
                    ├── schedule: 每日重复 at notTime
                    └── payload: habitId

删除习惯
  └── NotificationService.removeNotifications(id)
```

---

## 9. 备份系统设计

### 9.1 为什么选择 JSON 文件而非二进制

- **用户可读** — 用户可以打开 JSON 查看自己的数据
- **调试友好** — 开发时可直接检查备份内容
- **版本控制** — 可以 git diff 对比变化
- **跨平台** — JSON 在所有平台上通用

### 9.2 备份文件格式

```json
{
  "version": 3,
  "habits": [...],
  "events": { "habitId": { "date": [dayType, ...] } },
  "categories": [...],
  "habit_categories": [{ "habit_id": 1, "category_id": 1 }],
  "metadata": {
    "imported_from": "legacy_list",
    "import_timestamp": "ISO8601"
  }
}
```

### 9.3 兼容性策略

- 新格式包含 `version` 字段用于版本识别
- 支持读取旧版数组格式（无 version 字段 = 旧版）
- 导入时自动转换为当前格式
- 文件大小限制 10MB

---

## 10. 依赖注入设计

### 10.1 ServiceLocator 模式

```
ServiceLocator (单例)
  │
  ├── 提供:
  │   ├── RepositoryFactory → 创建各 Repository
  │   ├── NotificationService
  │   ├── BackupService
  │   ├── UIFeedbackService
  │   ├── BiometricAuthService
  │   └── HomeWidgetService
  │
  ├── 初始化时接收:
  │   └── HaboModel (共享数据库连接)
  │
  └── 使用方:
      └── main.dart 中创建并注入到 HabitsManager
```

### 10.2 HabitsManager 的依赖注入

```dart
HabitsManager(
  habitRepository: repoFactory.habitRepository,
  eventRepository: repoFactory.eventRepository,
  categoryRepository: repoFactory.categoryRepository,
  backupService: serviceLocator.backupService,      // 可选
  notificationService: serviceLocator.notificationService, // 可选
  uiFeedbackService: serviceLocator.uiFeedbackService,     // 可选
)
```

可选服务的设计使得在测试时可以传入 null，方便隔离测试业务逻辑。

---

## 11. 错误处理策略

| 层级 | 策略 | 用户体验 |
|------|------|----------|
| Data Layer (HaboModel) | try-catch + debugPrint | 静默失败，不中断应用 |
| Repository Layer | 抛出异常 | 向上传播 |
| Service Layer | 返回结果对象 (BackupResult) | UI 反馈服务展示错误消息 |
| Manager Layer | catch + UIFeedbackService.showError() | 用户看到错误提示 |
| Presentation Layer | FutureBuilder 处理 loading/error | 加载指示器 + 错误状态 |
