2026-04-19 21:39:23 +02:00
|
|
|
# Location Recorder
|
|
|
|
|
|
2026-04-19 21:57:31 +02:00
|
|
|
本文档说明 `location recorder` 在 Python 项目中的当前数据库接管策略,以及 legacy SQLite 接管 runbook。
|
2026-04-19 21:39:23 +02:00
|
|
|
|
2026-04-19 23:18:20 +02:00
|
|
|
当前 Python 版本的 `POST /location/record` 请求校验策略是:
|
|
|
|
|
|
|
|
|
|
- `latitude` 和 `longitude` 为必填,缺失或无法解析成合法数值时返回 `400 bad request`
|
|
|
|
|
- `altitude` 为可选,缺失或非法时按 `0` 处理
|
|
|
|
|
- unknown field 仍返回 `400 bad request`
|
|
|
|
|
- 对 caller 的错误响应保持简洁,不直接暴露底层校验细节;详细原因只写日志
|
|
|
|
|
|
2026-04-19 21:39:23 +02:00
|
|
|
## Legacy 事实基线
|
|
|
|
|
|
|
|
|
|
当前 legacy SQLite 中 `location` 表的真实 schema 为:
|
|
|
|
|
|
|
|
|
|
```sql
|
|
|
|
|
CREATE TABLE location (
|
|
|
|
|
person TEXT NOT NULL,
|
|
|
|
|
datetime TEXT NOT NULL,
|
|
|
|
|
latitude REAL NOT NULL,
|
|
|
|
|
longitude REAL NOT NULL,
|
|
|
|
|
altitude REAL,
|
|
|
|
|
PRIMARY KEY (person, datetime)
|
|
|
|
|
);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
历史上 legacy Go 实现使用:
|
|
|
|
|
|
|
|
|
|
```sql
|
|
|
|
|
PRAGMA user_version = 2;
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
这代表旧系统曾依赖 `user_version` 管理 location 数据库版本,但这不再是 Python 项目的长期 migration 机制。
|
|
|
|
|
|
|
|
|
|
## 当前策略
|
|
|
|
|
|
|
|
|
|
当前采用的最小必要接管方案是:
|
|
|
|
|
|
|
|
|
|
1. 把上述 `location` schema 视为 Alembic baseline
|
|
|
|
|
2. 新数据库通过 Alembic `upgrade head` 初始化
|
2026-04-19 23:02:43 +02:00
|
|
|
3. 已有 legacy SQLite 数据库,只要确认 schema 与 baseline 一致,再通过 `alembic stamp` 接管
|
|
|
|
|
4. 如果数据库已经存在 `alembic_version`,则必须先确认当前 revision 与项目预期 baseline 一致
|
|
|
|
|
5. 只有 revision 一致时,才视为该库已经被正确接管
|
|
|
|
|
6. 未来不再以 `PRAGMA user_version` 作为主 migration 机制
|
2026-04-19 21:39:23 +02:00
|
|
|
|
|
|
|
|
当前 baseline revision 是:
|
|
|
|
|
|
|
|
|
|
- `20260419_01_location_baseline`
|
|
|
|
|
|
2026-04-19 21:57:31 +02:00
|
|
|
当前提供的最小脚本入口是:
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
python scripts/location_db_adopt.py
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
如果你更喜欢模块方式运行,也可以用:
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
python -m scripts.location_db_adopt
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
它只针对 `LOCATION_DATABASE_URL` 工作,并且遵守保守接管原则:
|
|
|
|
|
|
|
|
|
|
- 本地已有 DB 文件:先校验,再接管
|
|
|
|
|
- 本地没有 DB 文件:按新库初始化
|
|
|
|
|
- 任一校验不通过:立即报错并停止
|
|
|
|
|
|
2026-04-19 23:02:43 +02:00
|
|
|
应用本身在启动时不会自动替你初始化 `location` 数据库。
|
|
|
|
|
应用启动时会对 `LOCATION_DATABASE_URL` 做只读校验:
|
|
|
|
|
|
|
|
|
|
- 文件不存在:直接报错,并提示先运行接管脚本
|
|
|
|
|
- 文件存在但还没有 `alembic_version`:直接报错,要求先完成 legacy 接管
|
|
|
|
|
- 文件已被 Alembic 管理但 revision 不匹配:直接报错并拒绝启动
|
|
|
|
|
|
|
|
|
|
这是有意为之,用来避免应用在错误路径上静默创建新库,或带着错误数据库版本继续跑业务。
|
|
|
|
|
|
2026-04-19 21:39:23 +02:00
|
|
|
## 新数据库初始化
|
|
|
|
|
|
2026-04-19 21:57:31 +02:00
|
|
|
如果本地不存在 `LOCATION_DATABASE_URL` 指向的 DB 文件:
|
|
|
|
|
|
|
|
|
|
- 脚本会先创建父目录
|
|
|
|
|
- 然后执行 Alembic `upgrade head`
|
|
|
|
|
- 最终建立 `location` 表与 `alembic_version` 表
|
|
|
|
|
|
|
|
|
|
手工执行时也等价于:
|
2026-04-19 21:39:23 +02:00
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
alembic upgrade head
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
这会创建与 legacy 相同的 `location` 表结构,并在库中建立 Alembic revision 记录。
|
|
|
|
|
|
|
|
|
|
## 旧数据库接管
|
|
|
|
|
|
|
|
|
|
对于已经存在的 legacy SQLite 数据库:
|
|
|
|
|
|
2026-04-19 21:57:31 +02:00
|
|
|
1. 先确认 DB 文件存在
|
2026-04-19 23:02:43 +02:00
|
|
|
2. 如果已经存在 `alembic_version` 表,则先读取当前 revision
|
|
|
|
|
3. 如果 revision 等于 `20260419_01_location_baseline`,则视为该库已经被 Alembic 正确接管
|
|
|
|
|
4. 如果 revision 不匹配,立即报错并停止,不做任何自动修复
|
|
|
|
|
5. 如果还没有 `alembic_version` 表,则读取当前 DB 中 `location` 表的实际 schema
|
|
|
|
|
6. 与 baseline schema 做严格比对
|
|
|
|
|
7. 再检查 `PRAGMA user_version`
|
|
|
|
|
8. 只有 schema 匹配且 `user_version = 2` 时,才执行 Alembic `stamp`
|
|
|
|
|
9. 接管完成后,后续 migration 才交给 Alembic 管理
|
2026-04-19 21:39:23 +02:00
|
|
|
|
|
|
|
|
示例:
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
LOCATION_DATABASE_URL=sqlite:///./data/locationRecorder.db alembic stamp 20260419_01_location_baseline
|
|
|
|
|
```
|
|
|
|
|
|
2026-04-19 21:57:31 +02:00
|
|
|
或直接执行脚本:
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
LOCATION_DATABASE_URL=sqlite:///./data/locationRecorder.db python scripts/location_db_adopt.py
|
|
|
|
|
```
|
|
|
|
|
|
2026-04-19 21:39:23 +02:00
|
|
|
这样做的含义是:
|
|
|
|
|
|
|
|
|
|
- 告诉 Alembic:这个数据库已经处于 baseline 结构
|
|
|
|
|
- 不修改已有 `location` 表数据
|
|
|
|
|
- 后续 migration 由 Alembic 接管
|
|
|
|
|
|
2026-04-19 21:57:31 +02:00
|
|
|
## Fail Closed 原则
|
|
|
|
|
|
|
|
|
|
当前策略是保守接管,不做未知 legacy 状态的自动修复。
|
|
|
|
|
|
|
|
|
|
如果出现以下任一情况,脚本会直接报错并停止:
|
|
|
|
|
|
|
|
|
|
- 找不到 `location` 表
|
|
|
|
|
- `location` 表 schema 与 baseline 不一致
|
|
|
|
|
- `PRAGMA user_version` 不等于 `2`
|
2026-04-19 23:02:43 +02:00
|
|
|
- 已有 `alembic_version`,但 revision 与预期 baseline 不一致
|
2026-04-19 21:57:31 +02:00
|
|
|
- 目标 DB 不是 SQLite URL
|
|
|
|
|
|
|
|
|
|
当前不会尝试:
|
|
|
|
|
|
|
|
|
|
- 自动修表
|
|
|
|
|
- 自动调整 `user_version`
|
|
|
|
|
- 自动推断未知 legacy 状态
|
|
|
|
|
|
|
|
|
|
如果发生这些情况,应先人工确认数据库状态,再决定是否需要单独迁移或修复。
|
|
|
|
|
|
2026-04-19 21:39:23 +02:00
|
|
|
## 关于 `data/locationRecorder.db`
|
|
|
|
|
|
|
|
|
|
你本地放在 `data/locationRecorder.db` 的 legacy 样本库,可以用于:
|
|
|
|
|
|
|
|
|
|
- 人工核对 schema
|
|
|
|
|
- 手动验证 `stamp` 接管流程
|
|
|
|
|
- 做开发时的兼容性确认
|
|
|
|
|
|
|
|
|
|
但当前代码不应硬依赖这个文件存在。
|
|
|
|
|
|
|
|
|
|
## 测试样本的安全使用方式
|
|
|
|
|
|
|
|
|
|
如果要用 legacy SQLite 样本做测试或验证,应遵守:
|
|
|
|
|
|
|
|
|
|
1. 不直接在原始样本文件上跑测试
|
|
|
|
|
2. 先复制到临时路径
|
|
|
|
|
3. 所有 `stamp`、写入、实验性 migration 都只针对副本执行
|
|
|
|
|
|
|
|
|
|
自动化测试里当前采用的方式是:
|
|
|
|
|
|
|
|
|
|
- 构造一个“legacy 风格”的临时 SQLite 文件
|
|
|
|
|
- 建出同样的 `location` 表
|
|
|
|
|
- 设置 `PRAGMA user_version = 2`
|
2026-04-19 21:57:31 +02:00
|
|
|
- 再执行接管脚本中的 adopt 逻辑
|
|
|
|
|
|
|
|
|
|
同时也覆盖:
|
|
|
|
|
|
|
|
|
|
- DB 文件不存在时的新库初始化路径
|
|
|
|
|
- schema 不匹配时的失败路径
|
|
|
|
|
- `user_version` 不匹配时的失败路径
|
2026-04-19 21:39:23 +02:00
|
|
|
|
|
|
|
|
这样可以验证接管路径,同时不污染真实样本库。
|