Appearance
八、项目结构 (Project Structure)
本项目分为两个独立仓库:前端(Flutter 客户端)和后端(Dart Shelf 服务)。
8.1 前端目录结构
todo_app/
├── android/ ← Android 平台配置
│ └── app/src/main/
│ └── AndroidManifest.xml ← 权限声明(INTERNET · 通知 · 振动)
├── windows/ ← Windows 平台配置
├── lib/
│ ├── main.dart ← 应用入口,初始化各服务,按平台路由
│ ├── models/
│ │ └── todo.dart ← Todo 模型,Priority / TodoType / RecurrenceKind 枚举
│ ├── database/
│ │ └── database.dart ← Drift 数据库定义,表结构,迁移脚本
│ ├── repositories/
│ │ └── todo_repository.dart ← 数据访问层,CRUD + 离线队列入队
│ ├── services/
│ │ ├── api_service.dart ← REST 请求封装,Token 刷新,E2EE 加解密,Correlation ID
│ │ ├── auth_service.dart ← JWT Token 本地持久化,deviceId 管理,密钥决策
│ │ ├── crypto_service.dart ← E2EE:PBKDF2 密钥派生 + AES-256-GCM 加解密
│ │ ├── device_service.dart ← 采集设备名(型号 / 计算机名),用于设备管理
│ │ ├── sse_service.dart ← SSE 长连接,断线重连,事件分发,定时兜底
│ │ ├── sync_service.dart ← 全量 / 增量同步,智能同步,离线队列重放
│ │ ├── reminder_service.dart ← 提醒与 routine 调度,按平台分发通知
│ │ ├── routine_scheduler.dart ← routine 纯计算:展开触发时刻、频率文案
│ │ ├── theme_service.dart ← 主题(明暗)管理
│ │ ├── tray_service.dart ← Windows 系统托盘管理
│ │ └── notifications/
│ │ ├── notification_service.dart ← 通知抽象接口
│ │ ├── notification_service_android.dart ← Android 实现(awesome_notifications,channel 矩阵)
│ │ └── notification_service_desktop.dart ← Windows 实现(local_notifier)
│ ├── ui/
│ │ ├── screens/
│ │ │ ├── home_screen.dart ← Win11 主界面(侧边栏布局)
│ │ │ ├── home_screen_mobile.dart ← Android 主界面(底部导航栏)
│ │ │ ├── login_screen.dart ← 登录页
│ │ │ ├── register_screen.dart ← 注册页,含访客数据迁移
│ │ │ ├── forgot_password_screen.dart ← 忘记密码,发送验证码
│ │ │ └── reset_password_screen.dart ← 重置密码,验证码 + 新密码
│ │ ├── theme/
│ │ │ └── app_palette.dart ← 全局配色板
│ │ └── widgets/
│ │ ├── sidebar.dart ← Win11 侧边栏,含日志查看入口
│ │ ├── todo_list_panel.dart ← 待办列表,搜索 + 排序,普通/日常分区
│ │ ├── todo_item_card.dart ← 单条待办卡片,移动端支持滑动手势
│ │ ├── routine_item_card.dart ← routine 卡片,频率文案 + 每日打勾
│ │ ├── todo_form_dialog.dart ← 新建 / 编辑弹窗,响应式布局
│ │ ├── todo_detail_dialog.dart ← 详情弹窗,含编辑 / 删除入口
│ │ ├── e2ee_consent_dialog.dart← E2EE 知情同意弹窗(每账号一次)
│ │ └── permission_guide.dart ← 通知/权限体检引导(Android)
│ └── utils/
│ ├── app_logger.dart ← 前端日志工具(talker 封装,文件输出)
│ ├── platform_util.dart ← 平台判断工具(isMobile / isDesktop)
│ ├── app_paths.dart ← Windows portable 路径解析(exe 同目录)
│ ├── app_prefs.dart ← 跨平台 prefs(Windows 用 prefs.json,其他用 SharedPreferences)
│ └── android_permissions.dart ← Android 通知/精确闹钟权限申请
├── pubspec.yaml ← 依赖声明
└── .gitignore前端各层说明
| 目录 | 层级 | 说明 |
|---|---|---|
models/ | 数据模型层 | 定义 Todo 类和 Priority 枚举,前后端共享相同的字段约定 |
database/ | 数据库层 | Drift ORM 表定义、schema 版本管理、迁移脚本,只负责本地 SQLite 读写 |
repositories/ | 数据访问层 | 统一的数据操作入口,联网时写操作同时写入 sync_queue |
services/ | 业务服务层 | 封装所有副作用:网络、推送连接、同步调度、通知、托盘 |
ui/screens/ | 页面层 | 各页面的整体布局与状态管理,调用 Service 层,不直接访问数据库 |
ui/widgets/ | 组件层 | 可复用的 UI 组件,尽量无状态,接收数据和回调作为参数 |
utils/ | 工具层 | 日志、平台判断等横切关注点,无业务逻辑 |
8.2 后端目录结构
todo_backend/
├── bin/
│ └── server.dart ← 应用入口,启动 HttpServer,注册所有路由
├── lib/
│ ├── config.dart ← 环境变量读取,统一配置入口
│ ├── database/
│ │ └── database.dart ← PostgreSQL 操作封装,建表,CRUD 方法
│ ├── handlers/
│ │ ├── auth_handler.dart ← 注册 / 登录 / Token 刷新 / 登出
│ │ ├── todo_handler.dart ← 待办 CRUD,写操作后触发 SSE 广播
│ │ ├── reset_handler.dart ← 忘记密码 / 发送验证码 / 重置密码
│ │ ├── event_handler.dart ← SSE 路由定义(实际处理已移至 server.dart)
│ │ ├── log_handler.dart ← 日志查看接口,Basic Auth + Cookie + 限速
│ │ └── dashboard_handler.dart ← 运维监控后台,Basic Auth + Cookie + 限速
│ ├── middleware/
│ │ └── auth_middleware.dart ← JWT 验证,解析 userId 注入请求上下文
│ ├── services/
│ │ └── event_broadcaster.dart ← SSE 连接池,Map<userId, List<Controller>>
│ └── utils/
│ ├── jwt_util.dart ← Token 签发(access + refresh)与验证
│ ├── password_util.dart ← bcrypt 哈希与校验
│ ├── response_util.dart ← 统一 JSON 响应格式封装
│ ├── email_util.dart ← QQ 邮箱 SMTP 邮件发送(验证码邮件,mailer 包)
│ └── server_logger.dart ← 结构化日志,文件输出,日志读取接口
├── Dockerfile ← 多阶段构建,编译为原生二进制
├── docker-compose.yml ← api + db 两个容器的编排配置
├── pubspec.yaml ← 依赖声明
├── .env ← 敏感配置(不进仓库)
├── .env.example ← 配置模板(进仓库)
├── logs/ ← 日志文件目录(不进仓库)
└── .gitignore后端各模块说明
| 目录 | 说明 |
|---|---|
bin/server.dart | 整个后端的启动入口。初始化 ServerLogger、数据库,注册路由,启动 HttpServer。SSE 请求在此处单独处理,绕过 shelf 直接使用原生 HttpResponse,以便设置 bufferOutput = false |
lib/config.dart | 所有环境变量的唯一读取入口,其他模块通过 Config.xxx 访问配置,不直接读 Platform.environment |
lib/database/ | PostgreSQL 的所有 SQL 操作封装为静态方法,Handler 层不直接拼 SQL |
lib/handlers/ | 每个业务域一个 Handler 文件,返回 shelf 的 Router,在 server.dart 中 mount 挂载 |
lib/middleware/ | authMiddleware 验证 JWT,将解析出的 userId 注入请求上下文,Handler 通过 getUserId(req) 取用 |
lib/services/ | EventBroadcaster 单例维护所有 SSE 连接,Handler 调用 broadcast() 推送事件 |
lib/utils/ | 无状态的工具函数,可被任意模块调用 |
8.3 数据库表结构
前端 SQLite(Drift,当前 schemaVersion = 6)
| 表名 | 用途 | 核心字段 |
|---|---|---|
todos | 待办本地副本 | 基础:id · user_id · title · description · due_date · priority · is_completed · created_at · updated_at 提醒:sound_key · vibrate routine:todo_type · recurrence_kind · daily_times · interval_minutes · window_start · window_end · days_of_week · routine_checkable · completed_for_date |
sync_queue | 离线操作队列(Outbox) | id · operation · todo_id · payload · created_at(访客/本地用户的写操作不入队,由 TodoRepository 按 userId != kLocalUserId 判断) |
kv_store | 键值元数据存储 | key · value(存储 last_sync_time 等) |
title/description在本地存明文(本地是绝对信任区);提醒与 routine 字段均为明文结构化元数据。
后端 PostgreSQL
| 表名 | 用途 | 核心字段 |
|---|---|---|
users | 用户账号 | id · email · password(bcrypt 哈希)· nickname · key_version · last_todo_modified_at · created_at |
todos | 待办云端数据 | 字段与前端 todos 对齐(title / description 存 E2EE 密文,其余明文),外加 user_id 外键 |
refresh_tokens | 刷新令牌 | token · user_id · expires_at · created_at |
reset_codes | 密码重置验证码 | email · code · expires_at · used · created_at |
device_logins | 设备登录历史(运维后台用) | device_id · user_id · device_name · last_login_at · last_city(主键 = device_id + user_id) |
key_version支撑 E2EE 密钥轮换(见 第十章);last_todo_modified_at支撑智能同步(见 第十一章);device_logins支撑运维后台的设备管理(见 第十三章)。