# Python 重构方案 本文档基于当前 Go 实现,给出迁移到 Python + FastAPI 的设计输入与建议顺序。本文档只讨论迁移方案,不代表已经开始实现。 ## 重构原则 - 以当前 Go 实现为唯一事实来源 - 先保持对外行为兼容,再考虑内部清理和优化 - 明确区分“行为兼容”和“内部实现升级” - 默认不把 Notion 纳入新的 Python 主版本目标,除非后续重新决策 - 尽量按模块迁移,并在模块之间建立清晰契约 ## 目标形态 建议的 Python 目标架构: - 用 FastAPI 提供 HTTP 路由,并自然生成 OpenAPI - 业务逻辑按模块拆分到 service layer - 外部系统对接放到 adapter layer - SQLite 访问放到 repository layer - 配置采用显式 settings model - scheduler 是否内嵌在应用进程内,需要在启动语义明确后再决定 - 仅保留极轻量的服务端页面,用于 OAuth 跳转或简单配置 ## 建议的 Python 模块边界 ### 应用外壳层 - 职责: - 读取 settings - 依赖注入与对象装配 - 路由注册 - lifespan hooks - scheduler 启停 - 在 FastAPI 中可对应: - `main.py` 或 app factory - settings 类 ### Poo 领域模块 - 职责: - 校验 poo record 输入 - 持久化 poo record - 查询 latest poo - 通过接口触发外部副作用 - 建议把副作用依赖抽象为端口: - `PooRepository` - `HomeAssistantPublisher` - `HomeAssistantWebhookClient` - 如有需要,可保留临时 `LegacyPooMirror` 作为 Notion 过渡适配器 ### Location 领域模块 - 职责: - 校验并持久化位置点 - 可保持简洁: - `LocationRepository` - `LocationService` ### Home Assistant 命令网关 - 职责: - 暴露 `/homeassistant/publish` - 解析 `target/action/content` - 将命令分发到内部服务 - 兼容性注意: - 第一阶段应保留当前 `content` 的处理习惯,包括对字符串 payload 的兼容解析 ### TickTick 集成模块 - 职责: - OAuth start / callback - token 存储 - task 查询 - 去重 - task 创建 - 建议: - 把 token persistence 抽象成独立能力,而不是把“改写配置文件”直接塞进业务逻辑 ### Home Assistant 出站适配层 - 职责: - 发布 sensor state - 触发 webhook - 该层小而独立,适合较早迁移 ### Legacy Notion 适配层 - 职责: - 只在分析或过渡阶段表示当前行为 - 默认建议: - 不放入 Python 第一版正式目标 - 如 cutover 期间确有需要,可以 feature flag 或独立迁移工具的方式暂存 ## 实现前需要冻结的兼容契约 在正式编码前,建议先把以下当前行为写成明确契约: ### API 契约 - `POST /poo/record` 的请求字段与当前“成功时空响应体”的行为 - `POST /location/record` 的请求字段与数值解析行为 - `POST /homeassistant/publish` 的 envelope 格式与支持的 `target/action` - `GET /ticktick/auth/code` 的成功 / 失败语义 ### 副作用契约 - `POST /poo/record` 在什么时机会发布 Home Assistant sensor - `POST /poo/record` 在什么条件下会触发 Home Assistant webhook - Home Assistant 消息如何映射为 TickTick task title 与 due date - Home Assistant sensor payload 的结构 ### 持久化契约 - 当前 SQLite 表名与主键 - 当前磁盘上的时间戳格式 - Python 第一阶段是否直接复用现有 DB 文件,还是做显式迁移 ## 建议的迁移决策 ### 决策 1:第一阶段保持对外 API 形状不变 原因: - 当前 API 面很小 - 保持兼容能显著降低切换风险 - 即使保持兼容,FastAPI 仍然可以生成 OpenAPI 文档 ### 决策 2:把内部 self-HTTP 改成直接服务调用 原因: - 当前 Go 代码中的 `localhost` 自调用,本质上是内部编排手段 - 这不是一个必须暴露给外部的契约 - Python 版改为直接函数 / service 调用,可以提升清晰度和可测试性 ### 决策 3:先继续使用 SQLite 原因: - 当前系统已经使用 SQLite - 数据模型规模很小 - PostgreSQL 更适合作为 parity 之后的下一阶段演进 ### 决策 4:默认不迁 Notion,但要明确记录影响 原因: - 你已经明确表示 Notion 很可能不继续保留 - 当前 Notion 不是“代码里有但没在用”,而是真正参与运行逻辑 - 所以不能静默删除,而要在方案中写清楚删掉后有什么影响 ### 决策 5:把 token / auth persistence 做成显式设计 原因: - 当前 TickTick token 处理虽然可用,但运维上比较脆弱 - Python 重构是一个把这件事规范化的机会 ## 建议迁移顺序 ### Phase 0:盘点与契约确认 - 完成当前系统 inventory - 确认哪些当前行为是“必须兼容的契约”,哪些只是历史偶然实现 - 明确把 Notion 标为 non-migration scope,除非后续重新决定 ### Phase 1:Python 骨架与通用基础设施 - 建立 FastAPI app shell - 定义 settings / config model - 定义日志方案 - 定义 SQLite 访问方式 - 定义测试框架与 fixture 策略 - 定义 OpenAPI 生成与导出方式 这一阶段不需要大量迁移业务逻辑,只要搭好后续模块可持续迁入的基础即可。 ### Phase 2:先迁最独立、最稳定的业务模块 推荐优先迁移:`location recorder` 原因: - 独立 SQLite 表 - 没有复杂外部副作用 - 没有 OAuth - 没有 scheduler 这一阶段的交付物可以包括: - `POST /location/record` - 与现有 SQLite 兼容的写入逻辑 - 校验与 repository 的单元测试 - 基于临时 SQLite 的 integration test ### Phase 3:迁移 Home Assistant 出站适配层 原因: - 功能面小 - 能为后面的 poo 迁移做铺垫 这一阶段的交付物可以包括: - sensor publish client - webhook trigger client - 针对请求格式与错误处理的 mock tests ### Phase 4:迁移 TickTick adapter 原因: - 相对自洽 - 在完成 Home Assistant 命令网关前就需要它 这一阶段的交付物可以包括: - OAuth callback endpoint - token persistence abstraction - task 创建与去重行为 - 基于 mock HTTP 的集成式测试 ### Phase 5:迁移 Home Assistant 命令网关 原因: - 这是外部 automations 的核心编排入口 - 在 location 与 TickTick adapter 准备好后,网关迁移会顺很多 这一阶段的交付物可以包括: - `/homeassistant/publish` - 兼容当前 `target/action` 的分发逻辑 - 用进程内 service 调用替代 self-HTTP - 把现有 Go 测试场景迁成 Python contract tests ### Phase 6:迁移 poo recorder 核心,但默认不带 Notion 原因: - 这是最复杂的模块 - 它既有本地 DB,又有 Home Assistant 副作用,当前还耦合 Notion 建议拆成两个子阶段: - phase 6a: - 本地 poo DB - latest poo 查询 - sensor publish - 可选 webhook trigger - `/poo/record` - `/poo/latest` - phase 6b: - 如果 cutover 期间必须保留旧逻辑,再做一个临时 legacy Notion 兼容层 ### Phase 7:运维加固与切换验证 - 做 Go / Python 路由级契约比对 - 用现有 SQLite 文件或其副本做兼容验证 - 在 staging 环境手动验证 TickTick OAuth - 用真实 Home Assistant automation payload 做验证 - 导出 OpenAPI YAML - 再补容器化与部署方案 ## 哪些模块适合先迁,哪些适合后迁 ### 适合优先迁移 - location recorder - Home Assistant 出站 client - TickTick adapter ### 更适合后迁 - Home Assistant 命令网关 - poo recorder 核心 ### 现状存在,但建议不迁 - Notion sync adapter - `poo_recorder_helper` 的 Notion 表反转 CLI - `location_recorder` helper CLI 脚手架 ## 建议的验证策略 ### Contract tests - 基于当前 Go 行为建立 request / response fixtures - 先把现有 `homeassistant` 测试案例迁成 Python - 补上 `poo` 与 `location` API 的契约测试 ### Integration tests - 每个模块使用临时 SQLite DB - Home Assistant 与 TickTick 出站流量通过 mock HTTP 替代 - 若仍保留 scheduler,则为其补定时行为测试 ### 手工 staging 验证 - TickTick OAuth callback - Home Assistant sensor 更新 - Home Assistant webhook 触发 - 当前真实自动化 payload 样例 ## 开始实现前仍需明确的问题 - Python 第一阶段是否还要保留“缺少 `notion.token` 就启动失败”的行为,还是直接把 Notion 变成可关闭能力? - `POST /location/record` 是否要继续保留“非法数字静默变成 0”的兼容行为? - TickTick token 在第一阶段是否继续写回 YAML,还是立即切到独立 token store? - 当前 Home Assistant automations 是否真实依赖 `content` 中的单引号 pseudo-JSON? 这些问题不影响当前 inventory,但会影响第一阶段“兼容到什么程度”的具体定义。