558 lines
24 KiB
Markdown
558 lines
24 KiB
Markdown
|
|
# 当前系统盘点
|
|||
|
|
|
|||
|
|
本文档用于盘点当前 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 配置文件
|