# 当前系统盘点 本文档用于盘点当前 branch 上的 Go 实现,并将其作为后续 Python 重构的唯一事实基线。 ## 范围与基线 - 当前事实基线:`legacy/go-backend/src/` 下的 Go 代码 - 不纳入当前基线:更早的 Python 版本 - 主入口:[`legacy/go-backend/src/cmd/serve.go`](/home/tianyu/workspace/home-automation/legacy/go-backend/src/cmd/serve.go:62) ## 系统概览 当前应用是一个单进程 Go HTTP 服务,具备以下特征: - 暴露少量 REST API - 使用本地 SQLite 做持久化 - 调用 Home Assistant API 和 webhook - 通过 OAuth 和 REST API 集成 TickTick - 当前仍依赖 Notion 做 poo 记录同步 - 内置一个每日执行的定时同步任务 进程启动后会先读取 YAML 配置文件,再初始化 Notion 与 TickTick 工具层、初始化各业务组件自己的 SQLite 数据库、注册路由、启动调度器,最后在配置的端口上提供 HTTP 服务。可参考 [`src/cmd/serve.go`](/home/tianyu/workspace/home-automation/legacy/go-backend/src/cmd/serve.go:65) 和 [`src/cmd/serve.go`](/home/tianyu/workspace/home-automation/legacy/go-backend/src/cmd/serve.go:104)。 ## API 盘点 ### `GET /status` - 路由定义:[`src/cmd/serve.go`](/home/tianyu/workspace/home-automation/legacy/go-backend/src/cmd/serve.go:106) - 用途:基础存活检查 - 请求参数:无 - 请求体:无 - 响应:纯文本 `OK` - 鉴权:当前代码中无鉴权 - 调用方类型:通用健康检查,可能用于本地监控或 supervisor 级别的探活 ### `GET /poo/latest` - 路由定义:[`src/cmd/serve.go`](/home/tianyu/workspace/home-automation/legacy/go-backend/src/cmd/serve.go:110) - 处理函数:[`pooRecorder.HandleNotifyLatestPoo`](/home/tianyu/workspace/home-automation/legacy/go-backend/src/components/pooRecorder/pooRecorder.go:87) - 用途:将最新一条 poo 状态重新发布到 Home Assistant 的 sensor state - 请求参数:无 - 请求体:无 - 响应: - 成功:空响应体,默认 HTTP 200 - 失败:通过 `http.Error(...)` 返回文本错误信息 - 鉴权:当前代码中无鉴权 - 外部调用方: - 会被 `POST /homeassistant/publish` 间接触发,当 `target=poo_recorder` 且 `action=get_latest` 时,代码会通过内部 HTTP 请求访问 `http://localhost:{port}/poo/latest`,见 [`src/components/homeassistant/homeassistant.go`](/home/tianyu/workspace/home-automation/legacy/go-backend/src/components/homeassistant/homeassistant.go:110) - 副作用: - 从 `poo_records` 读取最新一条记录 - 调用 Home Assistant `/api/states/{entity_id}` 更新 sensor 状态,见 [`src/util/homeassistantutil/homeassistantutil.go`](/home/tianyu/workspace/home-automation/legacy/go-backend/src/util/homeassistantutil/homeassistantutil.go:65) ### `POST /poo/record` - 路由定义:[`src/cmd/serve.go`](/home/tianyu/workspace/home-automation/legacy/go-backend/src/cmd/serve.go:111) - 处理函数:[`pooRecorder.HandleRecordPoo`](/home/tianyu/workspace/home-automation/legacy/go-backend/src/components/pooRecorder/pooRecorder.go:57) - 用途:记录一条 poo 事件,同时镜像到 Notion、刷新 Home Assistant sensor,并可选触发一个 Home Assistant webhook - 请求体 JSON: - `status: string` - `latitude: string` - `longitude: string` - 请求校验: - JSON decoder 开启了 `DisallowUnknownFields` - 如果配置里缺少 `pooRecorder.tableId`,请求会直接失败,虽然从纯本地 DB 角度看本来仍有可能写入成功 - 响应: - 成功:空响应体,默认 HTTP 200 - 请求错误:返回 decoder 错误文本,HTTP 400 - 服务端错误:返回错误文本,HTTP 500 - 鉴权:当前代码中无鉴权 - 外部调用方:大概率是 Home Assistant、移动端 shortcut、或手工调用;代码中没有明确写死调用方 - 副作用: - 向 SQLite `poo_records` 插入一条记录 - 异步向 Notion 追加一行 - 同步发布最新 Home Assistant sensor 状态 - 如果存在 `pooRecorder.webhookId`,异步触发一个 Home Assistant webhook ### `POST /homeassistant/publish` - 路由定义:[`src/cmd/serve.go`](/home/tianyu/workspace/home-automation/legacy/go-backend/src/cmd/serve.go:112) - 处理函数:[`HomeAssistant.HandleHaMessage`](/home/tianyu/workspace/home-automation/legacy/go-backend/src/components/homeassistant/homeassistant.go:36) - 用途:接收自动化消息,根据 `target` 和 `action` 做分发 - 请求体 JSON: - `target: string` - `action: string` - `content: string` - 请求校验: - JSON decoder 开启了 `DisallowUnknownFields` - 响应: - 成功路径:空响应体,默认 HTTP 200 - 失败路径:通常为空响应体并返回 HTTP 500;TickTick auth 回调是单独的接口,不在这里 - 鉴权:当前代码中无鉴权 - 外部调用方:设计意图上是给 Home Assistant automation 消息调用 当前代码支持的消息契约如下: - `target=poo_recorder`, `action=get_latest` - 转发到本地 `GET /poo/latest` - 见 [`src/components/homeassistant/homeassistant.go`](/home/tianyu/workspace/home-automation/legacy/go-backend/src/components/homeassistant/homeassistant.go:72) - `target=location_recorder`, `action=record` - `content` 预期是一个 JSON 风格字符串,实际很可能使用单引号 - 当前代码会用 `strings.ReplaceAll(message.Content, "'", "\"")` 做归一化 - 然后转发到本地 `POST /location/record` - 见 [`src/components/homeassistant/homeassistant.go`](/home/tianyu/workspace/home-automation/legacy/go-backend/src/components/homeassistant/homeassistant.go:82) - `target=ticktick`, `action=create_action_task` - `content` 预期可解析为: - `action: string` - `due_hour: int` - 当前代码会忽略调用方传来的 `title` 字段,而是把 `action` 映射为 TickTick task title - 到期时间的计算方式是:取 `now + due_hour` 后所在日期的“次日零点”,再转成 TickTick 使用的时间格式 - 最终在配置指定的 TickTick project 中创建任务 - 见 [`src/components/homeassistant/homeassistant.go`](/home/tianyu/workspace/home-automation/legacy/go-backend/src/components/homeassistant/homeassistant.go:124) 不支持的 `target` 或 `action` 会返回 HTTP 500,并打 warning 日志。相关测试在 [`src/components/homeassistant/homeassistant_test.go`](/home/tianyu/workspace/home-automation/legacy/go-backend/src/components/homeassistant/homeassistant_test.go:68)。 ### `POST /location/record` - 路由定义:[`src/cmd/serve.go`](/home/tianyu/workspace/home-automation/legacy/go-backend/src/cmd/serve.go:114) - 处理函数:[`locationRecorder.HandleRecordLocation`](/home/tianyu/workspace/home-automation/legacy/go-backend/src/components/locationRecorder/locationRecorder.go:43) - 用途:记录人的位置点,用于人生轨迹 / movement history - 请求体 JSON: - `person: string` - `latitude: string` - `longitude: string` - `altitude: string`,从请求结构上看是可选,但代码里即使为空也会被解析成 `0` - 请求校验: - JSON decoder 开启了 `DisallowUnknownFields` - 数值解析错误会被忽略;如果 `latitude` / `longitude` / `altitude` 不是合法数字,当前实现会静默落成 `0` - 响应: - 成功:空响应体,默认 HTTP 200 - 请求错误:返回 decoder 错误文本,HTTP 400 - 鉴权:当前代码中无鉴权 - 外部调用方: - 可被任意客户端直接调用 - 也会被 `POST /homeassistant/publish` 间接触发 - 副作用: - 向 SQLite `location` 表插入一条记录 ### `GET /ticktick/auth/code` - 路由定义:[`src/cmd/serve.go`](/home/tianyu/workspace/home-automation/legacy/go-backend/src/cmd/serve.go:116) - 处理函数:[`TicktickUtilImpl.HandleAuthCode`](/home/tianyu/workspace/home-automation/legacy/go-backend/src/util/ticktickutil/ticktickutil.go:103) - 用途:TickTick OAuth redirect callback - Query 参数: - `state` - `code` - 响应: - 成功:纯文本 `Authorization successful` - 失败:纯文本错误信息,HTTP 400 或 500 - 鉴权: - 通过 OAuth `state` 与进程内保存的 `authState` 做校验 - 没有额外的 session 或用户级鉴权 - 外部调用方:TickTick OAuth redirect - 副作用: - 用 authorization code 换取 access token - 通过 `viper.WriteConfig()` 把 `ticktick.token` 写回 YAML 配置文件 ## 外部集成盘点 ### TickTick - 在 Python 重构中的状态:应保留 - 主要文件: - [`src/util/ticktickutil/ticktickutil.go`](/home/tianyu/workspace/home-automation/legacy/go-backend/src/util/ticktickutil/ticktickutil.go:1) - [`src/components/homeassistant/homeassistant.go`](/home/tianyu/workspace/home-automation/legacy/go-backend/src/components/homeassistant/homeassistant.go:100) - 当前职责: - 初始化 TickTick 鉴权状态 - 当 token 缺失时启动 OAuth 授权 - 接收 OAuth callback 并持久化 token - 读取 project 下的 tasks - 若不存在同名任务,则创建新任务 - 连接方式: - OAuth authorization code flow - 调用 `https://ticktick.com/oauth/token` - 调用 `https://api.ticktick.com/open/v1/...` - 依赖的配置项: - `ticktick.clientId` - `ticktick.clientSecret` - `ticktick.redirectUri` - `ticktick.token` - 关键实现依赖: - 原生 `net/http` - `viper`,同时承担配置读取和配置回写 - 迁移高风险点: - OAuth callback 的 `state` 只保存在进程内;如果服务在授权开始和回调完成之间重启,流程会断 - token 直接写回 YAML 配置文件,虽然简单,但运维上比较脆弱 - 去重逻辑只按 task title 精确匹配 - due date 的计算语义是隐含在代码里的,重构前应先冻结 - `Init()` 会在启动时积极检查配置,且在 token 缺失时打印手动授权 URL;Python 版需要明确是否仍要在启动阶段卡住这一流程 ### Home Assistant - 在 Python 重构中的状态:应保留 - 主要文件: - [`src/components/homeassistant/homeassistant.go`](/home/tianyu/workspace/home-automation/legacy/go-backend/src/components/homeassistant/homeassistant.go:1) - [`src/util/homeassistantutil/homeassistantutil.go`](/home/tianyu/workspace/home-automation/legacy/go-backend/src/util/homeassistantutil/homeassistantutil.go:1) - 当前职责: - 接收来自 Home Assistant automations 的命令 envelope - 将命令转发给本地模块 - 把 sensor state 发布回 Home Assistant - 在 poo 记录后触发 Home Assistant webhook - 连接方式: - 入站 webhook 风格 JSON 接口:`POST /homeassistant/publish` - 出站 REST 调用:Home Assistant `/api/states/{entity_id}` - 出站 webhook 调用:`/api/webhook/{webhook_id}` - 出站调用使用 bearer token - 依赖的配置项: - `homeassistant.ip` - `homeassistant.port` - `homeassistant.authToken` - `homeassistant.actionTaskProjectId` - `pooRecorder.webhookId` - `pooRecorder.sensorEntityName` - `pooRecorder.sensorFriendlyName` - 关键实现依赖: - 原生 `net/http` - 通过 `localhost:{port}` 发起自调用,而不是直接走函数调用 - 迁移高风险点: - 入站 `/homeassistant/publish` 当前没有鉴权 - 当前命令 envelope 里的 `content` 是字符串,且常带单引号,现有客户端可能依赖这种非标准格式 - 模块间当前是通过自调用 HTTP 和 1 秒 timeout 编排的 - sensor 发布和 webhook 触发都属于强副作用行为,需要在兼容性测试里单独覆盖 ### Notion - 在 Python 重构中的状态:当前存在,但按已知目标不计划默认保留 - 主要文件: - [`src/util/notion/notion.go`](/home/tianyu/workspace/home-automation/legacy/go-backend/src/util/notion/notion.go:1) - [`src/components/pooRecorder/pooRecorder.go`](/home/tianyu/workspace/home-automation/legacy/go-backend/src/components/pooRecorder/pooRecorder.go:191) - helper CLI:[`src/helper/poo_recorder_helper/cmd/reverse.go`](/home/tianyu/workspace/home-automation/legacy/go-backend/src/helper/poo_recorder_helper/cmd/reverse.go:21) - 当前职责: - 使用 config token 初始化 Notion client - 读取 / 写入 poo 记录对应的表格行 - 每日做 SQLite 和 Notion 的双向同步 - 提供一个反转 Notion 表顺序的辅助 CLI - 连接方式: - 通过 `github.com/jomei/notionapi` 调用 Notion API - token 鉴权 - 依赖的配置项: - `notion.token` - `pooRecorder.tableId` - 关键实现依赖: - `github.com/jomei/notionapi` - 迁移高风险点: - 当前服务启动时如果缺少 `notion.token` 会直接退出,即便 Notion 并不是系统所有功能都需要的基础能力 - `POST /poo/record` 当前要求 `pooRecorder.tableId` 存在,并会异步镜像到 Notion - 每日定时同步会同时改写 Notion 和 SQLite,若 Python 版移除这一行为,数据一致性预期会发生变化 ## 数据库与 Schema 盘点 ### 数据库类型 - 当前使用 SQLite 做组件级持久化 - poo recorder 中显式导入了 SQLite driver,见 [`src/components/pooRecorder/pooRecorder.go`](/home/tianyu/workspace/home-automation/legacy/go-backend/src/components/pooRecorder/pooRecorder.go:20) - location recorder 也通过 driver 名 `sqlite` 打开 SQLite,见 [`src/components/locationRecorder/locationRecorder.go`](/home/tianyu/workspace/home-automation/legacy/go-backend/src/components/locationRecorder/locationRecorder.go:80) ### Poo recorder 数据库 - 配置项:`pooRecorder.dbPath` - 默认路径:`pooRecorder.db` - migration 机制: - 手写 `PRAGMA user_version` - 当前有效版本可以认为是 `1` - 目前只实现了 `0 -> 1` - 表: - `poo_records` - schema 定义见 [`src/components/pooRecorder/pooRecorder.go`](/home/tianyu/workspace/home-automation/legacy/go-backend/src/components/pooRecorder/pooRecorder.go:162) - 字段: - `timestamp TEXT PRIMARY KEY` - `status TEXT NOT NULL` - `latitude REAL NOT NULL` - `longitude REAL NOT NULL` - 核心用途: - 用于查询最新 poo 状态并发布到 Home Assistant sensor - 作为本地 poo 历史的持久化来源 - 作为 Notion 双向同步的本地数据源和数据汇 - 明显核心字段: - `timestamp` - `status` - `latitude` - `longitude` - 可能属于历史包袱 / 后续需要再判断的点: - 当前实现与 Notion 表行结构高度耦合 - 时间戳是字符串,格式为 `2006-01-02T15:04Z07:00`,不是带秒的完整 RFC3339 - API 请求模型接受的经纬度是字符串,因此 Python 版的类型规范化要小心兼容 ### Location recorder 数据库 - 配置项:`locationRecorder.dbPath` - 默认路径:`location_recorder.db` - migration 机制: - 手写 `PRAGMA user_version` - 当前版本 `2` - 已实现 migration: - `0 -> 1`:建表 - `1 -> 2`:把旧 datetime 字符串改写成 RFC3339 UTC - 表: - `location` - schema 定义见 [`src/components/locationRecorder/locationRecorder.go`](/home/tianyu/workspace/home-automation/legacy/go-backend/src/components/locationRecorder/locationRecorder.go:115) - 字段: - `person TEXT NOT NULL` - `datetime TEXT NOT NULL` - `latitude REAL NOT NULL` - `longitude REAL NOT NULL` - `altitude REAL` - 主键 `(person, datetime)` - 核心用途: - 持久化人生轨迹 / 位置点记录 - 明显核心字段: - `person` - `datetime` - `latitude` - `longitude` - 可能属于历史包袱 / 后续需要再判断的点: - `altitude` 在语义上是可选,但当前入站解析会把缺失和非法值一起压成 `0` - 当前没有查询 API,这张表目前主要承担“只写不读”的存储角色 ### 跨模块数据库观察 - 当前没有统一的共享 schema;每个组件各自打开自己的 SQLite 文件 - 没有使用 ORM - 没有统一 migration 框架 - 除主键外,没有看到额外索引 - `poo` 的常规写入和异步 Notion 镜像之间没有事务保证,一致性更接近 best-effort ## 业务模块拆分 ### 1. HTTP 外壳 / 应用启动层 - 职责: - 读取配置 - 设置日志级别 - 管理 scheduler 生命周期 - 注册路由 - 处理优雅退出 - 主要文件:[`src/cmd/serve.go`](/home/tianyu/workspace/home-automation/legacy/go-backend/src/cmd/serve.go:62) - 依赖: - 所有业务模块 - 迁移判断: - 这部分后续应成为 FastAPI 的 app 装配层 - 可以在早期先迁为“薄壳” ### 2. Poo recorder - 职责: - 接收 poo 记录 - 持久化本地 poo 历史 - 向 Home Assistant 发布最新状态 sensor - 触发可选 Home Assistant webhook - 与 Notion 做同步 - 主要文件:[`src/components/pooRecorder/pooRecorder.go`](/home/tianyu/workspace/home-automation/legacy/go-backend/src/components/pooRecorder/pooRecorder.go:50) - 依赖: - SQLite - Home Assistant util - Notion util - scheduler - 迁移判断: - 这是功能上重要、但耦合也最重的模块 - 适合在设计上先拆成: - poo API / service - Home Assistant 发布适配层 - legacy Notion sync adapter ### 3. Location recorder - 职责: - 接收位置更新 - 持久化人生轨迹点 - 主要文件:[`src/components/locationRecorder/locationRecorder.go`](/home/tianyu/workspace/home-automation/legacy/go-backend/src/components/locationRecorder/locationRecorder.go:39) - 依赖: - SQLite - 迁移判断: - 相对独立 - 很适合作为优先迁移对象 - 但需要先明确数值校验规则,因为当前实现会把非法数字静默压成 `0` ### 4. Home Assistant 命令路由层 - 职责: - 接收命令 envelope - 根据 target/action 调度 poo、location、ticktick 行为 - 主要文件:[`src/components/homeassistant/homeassistant.go`](/home/tianyu/workspace/home-automation/legacy/go-backend/src/components/homeassistant/homeassistant.go:36) - 依赖: - 本地服务端口 - TickTick util - 迁移判断: - 它是外部 automations 的关键契约 - 应在迁移早中期就被冻结和复刻 ### 5. TickTick adapter - 职责: - OAuth callback - project / task REST 操作 - 任务去重 - 主要文件:[`src/util/ticktickutil/ticktickutil.go`](/home/tianyu/workspace/home-automation/legacy/go-backend/src/util/ticktickutil/ticktickutil.go:81) - 依赖: - TickTick API - 可写配置文件 - 迁移判断: - 作为内部 adapter 相对独立 - 复杂度中等,主要难点是 OAuth 和 token 持久化 ### 6. Home Assistant 出站 client - 职责: - 发布 sensor state - 触发 webhook - 主要文件:[`src/util/homeassistantutil/homeassistantutil.go`](/home/tianyu/workspace/home-automation/legacy/go-backend/src/util/homeassistantutil/homeassistantutil.go:30) - 依赖: - Home Assistant API/token - 迁移判断: - 小而独立 - 很适合作为较早迁移的适配层 ### 7. Notion adapter 与辅助 CLI - 职责: - 读写 Notion table rows - 维护 poo 相关表格的辅助操作 - 主要文件: - [`src/util/notion/notion.go`](/home/tianyu/workspace/home-automation/legacy/go-backend/src/util/notion/notion.go:14) - [`src/helper/poo_recorder_helper/cmd/reverse.go`](/home/tianyu/workspace/home-automation/legacy/go-backend/src/helper/poo_recorder_helper/cmd/reverse.go:21) - 迁移判断: - 当前存在,但按已知方向应视为 planned non-migration - 更适合被标记为 legacy 模块,而不是直接带进 Python 主体 ### 8. 辅助 helper CLI - `src/helper/poo_recorder_helper` - 用于 Notion 表反转的运维辅助工具 - `src/helper/location_recorder` - 当前基本还是脚手架,没有实质业务逻辑 - 迁移判断: - 二者都不是后端重构的核心目标 - `poo_recorder_helper` 应随着 Notion 一并视作 legacy ## 运行方式与部署形态 ### 配置方式 - 配置文件名:`config.yaml` - 搜索路径: - 当前工作目录 - `$HOME/.config/home-automation` - 配置加载代码:[`src/cmd/serve.go`](/home/tianyu/workspace/home-automation/legacy/go-backend/src/cmd/serve.go:65) - 开启了 `viper.WatchConfig()` 代码中实际出现的配置项包括: - `port` - `logLevel` - `notion.token` - `ticktick.clientId` - `ticktick.clientSecret` - `ticktick.redirectUri` - `ticktick.token` - `homeassistant.ip` - `homeassistant.port` - `homeassistant.authToken` - `homeassistant.actionTaskProjectId` - `pooRecorder.tableId` - `pooRecorder.webhookId` - `pooRecorder.sensorEntityName` - `pooRecorder.sensorFriendlyName` - `pooRecorder.dbPath` - `locationRecorder.dbPath` ### 进程模型 - 单个二进制,通过 Cobra 子命令 `serve` 启动 - 监听 `SIGINT` / `SIGTERM` 做优雅退出 - scheduler 与 HTTP server 在同一进程内运行 - 路由层使用 `gorilla/mux` ### 定时任务 - 每天 `0 5 * * *` 执行一次 poo records 与 Notion 的同步 - 定义在 [`src/components/pooRecorder/pooRecorder.go`](/home/tianyu/workspace/home-automation/legacy/go-backend/src/components/pooRecorder/pooRecorder.go:180) ### 被动接收式接口 - 上面列出的入站 REST API - TickTick OAuth callback endpoint ### 运行时依赖的外部服务 - Home Assistant HTTP API 与 webhook - TickTick OAuth 与 REST API - 当前 poo 流程仍依赖 Notion API - 本地可写文件系统,用于配置文件和 SQLite DB ### 当前本地 / 服务部署形态 - 安装脚本会构建 Go 二进制,并安装到 `$HOME/.local/home-automation-backend` - 使用 Supervisor 管理进程 - 生成的 supervisor 配置最终执行 `{binary} serve` - 参考: - [`helper/install.sh`](/home/tianyu/workspace/home-automation/legacy/go-backend/helper/install.sh:45) - [`helper/home_automation_backend_template.conf`](/home/tianyu/workspace/home-automation/legacy/go-backend/helper/home_automation_backend_template.conf:1) ### 容器化情况 - 这一轮代码扫描中没有发现 `Dockerfile` 或 compose 文件 - 当前部署形态是 supervisor-based,而不是 container-based ## 测试与文档现状 ### 测试现状 - 只发现一个测试文件:[`src/components/homeassistant/homeassistant_test.go`](/home/tianyu/workspace/home-automation/legacy/go-backend/src/components/homeassistant/homeassistant_test.go:1) - 当前测试覆盖: - 入站命令 JSON 解码 - target/action 路由分发 - 转发到 poo/location handler 的行为 - TickTick task 创建委托 - 错误日志和失败路径 - 当前没有覆盖: - poo recorder 的 DB 行为 - location recorder 的 DB 行为 - TickTick OAuth 流程 - Home Assistant 出站发布 - Notion sync 逻辑 - 启动与配置加载 - scheduler 行为 ### 本轮测试执行情况 - 我尝试在 `legacy/go-backend/src/` 下执行 `go test ./...` - 但当前会话环境里没有安装 `go` 命令,因此无法实际运行测试 - 所以这轮关于测试的判断,基于静态阅读,而不是实际执行结果 ### 文档现状 - 仓库里的 `README.md` 基本只有标题和 badge,内容非常少 - 没有用户可读的 API 文档 - 没有 schema 文档 - 没有 Home Assistant / TickTick 的契约说明文档 - 没有关于配置项、OAuth 初始化、数据库文件位置的运维文档 ### 后续最需要补齐的文档 - Home Assistant 命令 envelope 与支持的 action - 各 API 的 request / response 契约 - TickTick OAuth 初始化与 token 持久化方式 - 数据库归属、用途与保留策略 - Notion 下线 / 不迁移说明 ## 对 Python 重构特别重要的事实 - 当前 API 行为整体比较“轻响应、重副作用”,很多成功请求返回的都是空响应体 - 当前所有入站 API 都没有看到鉴权 - 当前系统本地真相来源是 SQLite,但 poo 数据还同时与 Notion 同步 - `notion.token` 现在不是可选项,缺失时服务会在 `initUtil()` 阶段直接退出 - Home Assistant 命令路由当前是通过本地 HTTP 自调用实现的,而不是直接服务层调用 - TickTick callback 会改写应用本身使用的 YAML 配置文件