158 lines
9.2 KiB
Markdown
158 lines
9.2 KiB
Markdown
# 实现与文档的偏差
|
||
|
||
开发中凡是实现和 [PRD.md](./PRD.md)、[DEVELOPMENT.md](./DEVELOPMENT.md) 不一致的地方,以及文档没写清、开发中自己拿主意的地方,都记在这里,交给负责人事后审阅。开发全程自动推进,记下来就继续,不等审阅。
|
||
|
||
每条写清:日期、原条款(文档和小节)、实际做法、原因、备选方案、影响。各线只写自己那一节,避免多条线同时改同一段。
|
||
|
||
## 总控 L
|
||
|
||
### T0.1 2026-09-30
|
||
|
||
1. **前端嵌入方式**
|
||
- 原条款:DEVELOPMENT 第 3 节 `web/embed.go` 用 `//go:embed all:dist`;TASKS T0.1;`.gitignore` 忽略 `web/dist`。
|
||
- 实际做法:默认构建用 `-tags` 以外的 `embed_stub.go` 嵌入 `web/stub/`;`task build` 加 `-tags embeddist` 嵌入真实 `web/dist`。
|
||
- 原因:`web/dist` 不进仓库,且要求没有前端产物时也能 `go build`。
|
||
- 备选方案:提交最小 `dist`;或构建前脚本生成占位目录。
|
||
- 影响:裸 `go build` 不含真实前端;正式产物必须走 `task build`。
|
||
|
||
2. **listen.addr 写入时机**
|
||
- 原条款:DEVELOPMENT 4.1「端口写 0 时」写 `<data_dir>/listen.addr`。
|
||
- 实际做法:只要 `serve` 启动成功就写入实际监听地址。
|
||
- 原因:测试启动器统一读取该文件更简单,固定端口场景也无害。
|
||
- 备选方案:仅当配置端口为 0 时写入。
|
||
- 影响:多一个小文件;行为超集,兼容文档要求。
|
||
|
||
3. **迁移占位**
|
||
- 原条款:T0.3 才写完整表与 `VACUUM INTO` 备份;T0.1 允许空执行器加空 0001。
|
||
- 实际做法:`internal/store.Migrate` 建 `schema_migrations` 并应用 `0001_init.sql`(内容为 `SELECT 1;`);不做迁移前备份。
|
||
- 原因:保证 serve 可跑通迁移路径,表结构留给 T0.3。
|
||
- 备选方案:完全空文件 + 只记版本。
|
||
- 影响:T0.3 需替换 0001 正文并补备份逻辑;已应用的占位版本号仍为 1。
|
||
|
||
4. **Taskfile 引入 taskfiles**
|
||
- 原条款:TASKS T0.1 / 4.2 引入 `taskfiles/*.yml`。
|
||
- 实际做法:`includes: '*': taskfile: taskfiles/*.yml, optional: true`,并放 `_init.yml` 占位。
|
||
- 原因:空目录 glob 可能失败;各线稍后加自己的 yml。
|
||
- 备选方案:主文件逐条 optional include 各线文件名。
|
||
- 影响:无。
|
||
|
||
5. **deploy/Dockerfile**
|
||
- 原条款:完整多架构镜像属 Q4;T0.1 需要 `task docker` 目标。
|
||
- 实际做法:提供单架构多阶段 Dockerfile 骨架,供 `task docker` 使用;T0.1 验证另用官方 `golang` 镜像跑 `task check`。
|
||
- 原因:让 docker 目标可执行,又不抢 Q4 范围。
|
||
- 备选方案:docker 目标仅 echo 提示。
|
||
- 影响:镜像发布流程仍由 Q4 定稿。
|
||
|
||
### T0.2 2026-09-30
|
||
|
||
1. **请求指纹规范化格式**
|
||
- 原条款:DEVELOPMENT 7.3「下列字段规范化后的 SHA-256」,未规定字节布局。
|
||
- 实际做法:对 `to.kind`、`to.id`、`body.enc`、有效 `content_type`、解码后正文、`meta` 规范 JSON、`send_at_ms`、`delay_ms`、有效 `offline.keep`/`ttl_seconds`、有效 `receipt` 做长度前缀(或有无标记)串联后算 SHA-256,输出小写十六进制;`meta` 用 `encoding/json` 对 `map` 键排序序列化;不含 `talk_password`、`rid`。
|
||
- 原因:文档未给规范格式,需固定、与键顺序无关、含可选字段区分。
|
||
- 备选方案:整段规范 JSON 对象再哈希。
|
||
- 影响:各语言 SDK / 服务端必须共用同一布局,否则防重失效。
|
||
|
||
2. **指纹使用文档默认值后的有效字段**
|
||
- 原条款:指纹字段列表含 `receipt`、`offline.*`,未说明缺省如何表示。
|
||
- 实际做法:`receipt` 缺省按 true;`offline.keep` 缺省 false;`keep` 为 true 且未给 `ttl_seconds` 时按 86400;`keep` 为 false 时 ttl 记 0;`content_type` 按 enc 补默认。
|
||
- 原因:重试时省略与显式默认应视为同一请求。
|
||
- 备选方案:按原始 JSON 有无字段区分,省略与显式默认算冲突。
|
||
- 影响:SDK 省略默认字段时防重仍命中。
|
||
|
||
3. **JSON Encoder 去掉尾部换行**
|
||
- 原条款:用 `json.Encoder` 且 `SetEscapeHTML(false)`。
|
||
- 实际做法:Encode 后去掉 `Encoder.Encode` 追加的 `\n`,整帧字节数不含该换行。
|
||
- 原因:MQTT 一发布一帧,示例 JSON 无尾换行;保留换行会抬高帧长并与本地 `frame_too_large` 判断不一致。
|
||
- 备选方案:保留换行并在 DEVELOPMENT 写明。
|
||
- 影响:线上帧比「裸 Encoder.Encode」少 1 字节。
|
||
|
||
4. **协议包校验范围**
|
||
- 原条款:T0.2 要求编号规则、正文/meta 大小、`send_at_ms`/`delay_ms` 互斥、登录密码不以 `nst_` 开头。
|
||
- 实际做法:上述必做之外,顺带校验各帧 `v`/`type`/`rid`、目标 kind、分页 limit、致命 reason 枚举等结构字段;业务错误(目标不存在、配额等)不在本包判定。
|
||
- 原因:无结构校验则编解码测试无法覆盖「合法帧」边界。
|
||
- 备选方案:协议包只做编解码,校验留给各 app 模块。
|
||
- 影响:服务端应复用本包 `Validate`,避免重复规则。
|
||
|
||
### T0.3 2026-09-30
|
||
|
||
1. **完整表放在 0002,不改已发布的 0001**
|
||
- 原条款:TASKS T0.3 / 4.2「`0001_init.sql` 包含第 7.7 节全部表」;T0.1 偏差曾写「T0.3 需替换 0001 正文」。
|
||
- 实际做法:保留 `0001_init.sql` 为 `SELECT 1;`;新增 `0002_schema.sql` 写入 DEVELOPMENT 7.7 全部业务表与索引(含 `api_tokens`、`settings`、`session_hash` 等)。`schema_migrations` 仍由迁移执行器 `CREATE TABLE IF NOT EXISTS` 维护,不放入 0002。
|
||
- 原因:T0.1 的 0001 可能已记入已有库的 `schema_migrations`;改写已发布迁移语义会导致「版本已应用但表不存在」。
|
||
- 备选方案:对未迁移库特殊检测并改写 0001(复杂且易错)。
|
||
- 影响:新库会有版本 1+2 两行;与 TASKS「表在 0001」字面不一致,与「不改已发布迁移」一致。
|
||
|
||
2. **写入队列先做一操作一事务**
|
||
- 原条款:DEVELOPMENT 7.2 合并提交(最多 256 或凑满 2ms,SAVEPOINT);TASKS T0.3 允许简单实现,P2 换合并。
|
||
- 实际做法:`store.Queue` 用互斥锁串行,每请求一个事务;注释与本条标明 P2 再改为写 goroutine 合并提交。
|
||
- 原因:本任务范围;合并留给平台 P2。
|
||
- 备选方案:T0.3 直接做合并(抢 P2 范围)。
|
||
- 影响:高并发写入落盘次数偏多,正式压测前需完成 P2。
|
||
|
||
3. **空库不备份;仅已有 db 文件且有未应用版本时 VACUUM INTO**
|
||
- 原条款:DEVELOPMENT 7.7「有未应用版本时先 VACUUM INTO」;未区分空库。
|
||
- 实际做法:`Open` 在打开前检查 `nixmsg.db` 是否已存在;不存在则跳过备份;存在且有 pending 则写入 `<data_dir>/backup/pre-migrate-<UTC时间>.db`。迁移失败返回错误,不自动从备份恢复。
|
||
- 原因:空库备份无意义;失败退出与文档一致,恢复交给运维。
|
||
- 备选方案:失败时自动还原备份再退出。
|
||
- 影响:与任务说明一致;运维需知备份路径。
|
||
|
||
### T0.5 2026-09-30
|
||
|
||
1. **admin init / 管理员密码**
|
||
- 原条款:TASKS T0.5「自动执行 `admin init` 拿到管理员密码」;命令行启动器 JSON 含管理员密码。
|
||
- 实际做法:定义 `AdminInitializer`(默认 `CLIAdminInit`);二进制尚无 `admin` 子命令时返回 `ErrAdminInitUnsupported`,启动器跳过并继续起 serve;`admin_password` 字段为空字符串。示例集成测试只验 `/healthz`。
|
||
- 原因:`admin init` 属 P1,当前 main 仅有 `version`/`serve`。
|
||
- 备选方案:harness 内嵌假密码写入库(无表结构可写);或阻塞等 P1。
|
||
- 影响:P1 合入后无需改调用方接口,密码解析约定见 `parseAdminPassword`;Q/SDK 集成测试在拿到非空密码前勿依赖管理登录。
|
||
|
||
2. **MQTT 测试客户端范围**
|
||
- 原条款:能收发 DEVELOPMENT 第 6 节应用帧。
|
||
- 实际做法:提供 TCP / WebSocket(`/mqtt`,子协议 `mqtt`)传输层 `MQTTClient`,收发原始 MQTT 控制包字节;不实现 CONNECT/hello/主题业务。
|
||
- 原因:内置 broker 与协议处理尚未合入(连接 N / 后续任务);T0.5 先给可连传输与占位 API。
|
||
- 备选方案:引入完整 MQTT 客户端库并编假 broker。
|
||
- 影响:业务级帧测试在 broker 可用后由各线基于 `Send`/`Recv` 或再包一层完成。
|
||
|
||
3. **进程停止方式**
|
||
- 原条款:优雅停机(DEVELOPMENT 7.8 / P1)。
|
||
- 实际做法:测试启动器对子进程使用 `Kill`(Windows 上 `Interrupt` 不可靠)。
|
||
- 原因:保证并行测试与清理在 Windows 上稳定。
|
||
- 备选方案:Unix 发 SIGTERM;Windows 用 Job Object / Ctrl+Break。
|
||
- 影响:不覆盖优雅停机验收;该验收仍归 P1/Q。
|
||
|
||
|
||
## 平台 P
|
||
|
||
暂无。
|
||
|
||
## 连接 N
|
||
|
||
暂无。
|
||
|
||
## 消息 M
|
||
|
||
暂无。
|
||
|
||
## 身份 I
|
||
|
||
暂无。
|
||
|
||
## 后台接口 A
|
||
|
||
暂无。
|
||
|
||
## 后台网页 W
|
||
|
||
暂无。
|
||
|
||
## SDK 一 S1
|
||
|
||
暂无。
|
||
|
||
## SDK 二 S2
|
||
|
||
暂无。
|
||
|
||
## 测试交付 Q
|
||
|
||
暂无。
|