Skip to content

八、项目结构 (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 文件,返回 shelfRouter,在 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(访客/本地用户的写操作不入队,由 TodoRepositoryuserId != 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 支撑运维后台的设备管理(见 第十三章)。