visionA/docs/autoflow/04-architecture/db-integration-plan.md
jim800121chen d41a57097f docs(architecture): DB 接入工時規劃 — man-hours/man-day 估算文件
visionA backend 從 in-memory 接資料庫的規劃與工時估算(規劃,未實作)。

範圍與工時(三種):
- 最小可行(只 model): 7.8–13.5 人天
- 持久資料(model+device+pairing/token): 16–27.4 人天
- 完整(+ session→Redis + 韌性): 22–37.7 人天

關鍵結論:
- DB 由他人在 stage docker host(192.168.0.130)開好並提供連線,visionA 端不 provision
- ~80% Go 端工作(repository/migration/測試 via testcontainers)拿連線前就能開工,
  等 DB 只卡最後 1.5–4 天 stage 收尾
- 測試占比 ~45%(依需求刻意拉高、業界常態 25–35%)

DB 選型: Postgres(model/device/pairing/session_token)+ Redis(userSession;
tunnel session 因 yamux Handle 不可序列化維持 in-memory)。

含 Executive Summary(主管)/ 子任務 man-hours 明細(工程師)/ man-day 表(PM)三層視角。
過程草案保留於 .autoflow/04-architecture/(個人層)。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 20:01:21 +08:00

385 lines
28 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# visionA Backend 接資料庫 — 工時規劃文件Man-hours / Man-day 估算)
| 項目 | 內容 |
|------|------|
| **文件目的** | 提供 visionA-backend 各 in-memory store 接資料庫持久化的工時估算供主管對齊資源man-day、PM 排程、工程師排工使用。 |
| **適用範圍** | visionA-backend 6 個現有 in-memory store 的持久化規劃。不含 cluster / converter_job 兩張未 wire 的表、不含 prod 環境另建 DB見 §8 範圍外)。 |
| **狀態** | **規劃,未實作**。本文件為工時估算與技術規劃,非實作紀錄,無任何 production code 變更。 |
| **產出者** | Architect Agent |
| **最後更新** | 2026-06-16 |
| **數字基準** | 子任務級 man-hours1 人天 = 6.5 有效 hrs見 §2 估算假設)。 |
---
## Executive Summary給主管
**一句話結論**:把 visionA-backend 接 DB 的工程已被拆到子任務級並完成保守工時估算,**整體區間 2237.7 人天(視範圍)**;其中約 **80% 的 Go 端工作不需要等 DB 開好就能開工**,等 DB 連線資訊只卡最後 1.54 天的 stage 收尾驗證。
### 三種範圍的 man-day 區間
| 範圍 | 包含內容 | **Man-day** | 累計 Man-hours | 適用情境 |
|------|---------|-------------|---------------|---------|
| **最小可行(只 model** | DB 基礎建設 + 模型庫接 Postgres + 精簡驗證 | **7.8 13.5** | 51 88 | 使用者最關心:模型庫能持久化、重啟資料還在 |
| **持久資料** | 上述 + device + pairing/session token | **16 27.4** | 104 178 | 所有業務持久資料上 Postgressession 暫留 in-memory |
| **完整** | 上述 + session 接 Redis + 交易/韌性 | **22 37.7** | 143 245 | 全部持久化 + Redis session + 交易/韌性 |
> 區間下限 = 一切順利;上限 = 含 schema 細節踩坑與測試補齊。三種範圍皆已含「DB 基礎建設」與「一次 stage 驗證」。
### 測試占比
本估算依使用者要求**刻意拉高測試覆蓋**:測試占整體工時 **約 45%**41%48%),高於業界常態 2535%。測試含三層unit / integration via testcontainers / 邊界),詳見 §5。
### 兩個關鍵結論
1. **DB 由他人開好、Go 端不受影響**DB 機器與 visionA 專用 database 由他人在 stage host130開好並提供連線資訊。visionA 端**不負責 provision DB**,但 Go 端所有工作連線池、config、migration 撰寫、repository 實作、testcontainers 整合測試一小時都沒省——這些跟「DB 誰開」無關。整合測試改用 testcontainers本機/CI 一次性 DB不依賴 130更可靠。
2. **「等 DB」只卡最後收尾**:拿到 DB 連線資訊**前**最小範圍可做到「testcontainers 全綠」的程度(約 6.511 個工作天的活),只差最後 stage 接上驗證1.52.5 天)。換句話說,**只要範圍拍板,工程師可立刻開工,不會被「等 DB」block 大部分工作。**
---
## 1. 背景與前提
### 1.1 為什麼這次接 DB 估得動
6 個 store 全部已有 `Repository` / `Store` interface + in-memory 實作,`main.go` 裡是乾淨的 6 個 `NewInMemory*` 呼叫點。接 DB = 加 Postgres/Redis 實作 + 換 1 行 wiring**interface 與所有 handler 不用動**。這是估算偏可控的根本原因。
### 1.2 從零起手的部分(誠實交代)
`go.mod` 目前**沒有**任何 DB 依賴pgx / redis / golang-migrate 皆無),`migrations/` 目錄不存在,`config.go` 無 DB 設定區塊。因此「DB 基礎建設」(塊 0是真的從零建連線池、config、migration 工具、CI Postgres。
此外pairing / session-token 兩個 store 目前用 **plaintext 當 map key**,接 Postgres 要改成 **token_hash 當 PK**`database.md` §2.4 已是此意圖code 已有 `HashToken()`)。這是邏輯改動、不是純 swap塊 3 須特別小心。
### 1.3 ⚡ 關鍵前提DB 由他人在 130 另開visionA 不負責 provision
> **此前提很重要避免讀者誤解「DB 現成可省一大塊」。**
- stage host 192.168.0.130 是整個 stage 的 docker hostvisionA / MC / FAA / converter / minio 都在上面)。
- 130 上已存在的 DB container`fanfan-mysql` / `fanfan-redis` / `kneron_model_converter-redis`**都是別人的**visionA 一個都不該共用。
- 130:5432 port 雖 OPEN 但**不對應任何已知 container**(來源不明),**不採信為 visionA 可用**。
-**已確認**:會有人在 130 上幫 visionA **另外開好專用 DB**Postgres + 視需要 Redis開好後提供連線資訊。
**對工時的影響**
| 項目 | visionA 端是否要做 | 說明 |
|------|-------------------|------|
| Provision DB 機器 / 起 container | **不用**(他人做) | 省約 612 hrs |
| 建 visionA database / 角色權限 | **不用**(他人做,含在「給連線資訊」裡) | 省約 24 hrs |
| 連線池 / config / migration 撰寫 | **照樣要做** | 跟 DB 誰開無關 |
| Repository 實作 + 整合測試 | **照樣要做** | 改用 testcontainers不依賴 130 |
→ 一句話:**省下的是「DB provisioning + database 建置 + 130 維運邊界」(約 1020 hrs / 1.53 人天Go 端全部照做。** 整合測試改用 testcontainers本機/CI 一次性 DB這比連 130 測試更可靠CI 能跑、隔離乾淨),但 testcontainers 設置成本(約 917 hrs如實算進塊 0。淨效果最小範圍總工時主要被「測試多估」推高不是被「DB 誰開」推高。
---
## 2. 估算單位與假設(讓數字可信)
| 項目 | 設定 | 理由 |
|------|------|------|
| **基本單位** | man-hours人時子任務加總 → 塊小計 → 範圍累計 | 細到子任務才能排工 |
| **Man-day 換算** | **1 人天 = 6.5 有效 hrs**(非 8 | 扣掉 context switch、PR 溝通、等 CI、開會、被打斷的真實 overhead |
| **人力假設** | 1 位熟 Go、熟本 codebase 的 backend 工程師 | interface 已在、有現成 in-memory 測試可對照 |
| **hours 已含** | 實作 + 三層測試 + 一輪 code review 修正 | 每塊獨立列「self-review + 過 Reviewer」子任務 |
| **hours 不含** | 跨團隊等待(拿連線資訊、他人開 DB、需求變更、prod 另建 DB、CI 大改造意外 | 這些不可控、不放進工程估算 |
| **區間語意** | 下限 = 一切順利;上限 = 含 schema 細節array、unique×soft-delete、hash key 切換)踩坑與測試補齊 | |
| **測試刻意多估** | 測試占比拉到約 45%(業界常態 2535% | **使用者明確要求充足覆蓋**,詳見 §5 |
> **塊 0 第一次做最貴**連線池、migration 機制、testcontainers、CI Postgres 都從零。完成後塊 13 每塊攤平、可偏區間低值。
---
## 3. DB 選型分工
對齊 `database.md` §2.7「Session 不落 DB、Phase 1 考慮 Redis」的既有立場。
### 3.1 放 PostgreSQL持久業務資料
| Store | package | 理由 |
|-------|---------|------|
| `modelRepo` | `internal/model` | 模型庫要長期保存、要查詢List by owner/chip/source、跨重啟不能掉。**使用者最關心。** |
| `deviceRepo` | `internal/device` | 裝置綁定身分長期保存、有 owner/serial unique 約束、有關聯查詢需求。 |
| `pairingStore` | `internal/auth` | 配對 token 是「可撤銷的長期憑證」要稽核used_at/revoked_at重啟不能掉。 |
| `sessionTokenStore` | `internal/auth` | session token 90 天長效、可撤銷、要查 parent token 稽核鏈。 |
### 3.2 放 Redis揮發 session有 TTL、高頻讀寫
| Store | package | 理由 |
|-------|---------|------|
| `userSessionStore` | `internal/usersession` | 瀏覽器 cookie session。高頻讀、有 idle/absolute TTL、掉了重登即可。Redis TTL 原生支援。 |
| remote-proxy `session` | `internal/session` | tunnel session。**特例handle 是 process-local 活連線物件yamux/WS不能序列化進 Redis。** 單節點維持 in-memory多節點才需 Redis 存 Summaryhandle 仍留本地)。 |
### 3.3 為什麼不全放 Postgres / 不全放 Redis
- **不全放 Postgres**cookie session 高頻讀寫 + 自然過期Postgres 會變熱點、還要自己寫 cleanup jobRedis TTL 天生適配。
- **不全放 Redis**model/device/token 要關聯查詢、unique 約束、稽核、長期保存Redis 做這些彆扭。
- **結論**:持久業務資料 → Postgres揮發 session → Redis`database.md` 既有立場一致。
---
## 4. 各功能塊子任務拆解 + man-hours
> 標記:🟢 = 拿到 DB 連線資訊前就能做testcontainers 不依賴 130🔵 = 必須等 DB 連線資訊 / 真 DB 才能驗。
### 塊 0DB 基礎建設(所有 Postgres 塊的硬前置)
| # | 子任務 | hrs順利卡關 | 依賴 | 標記 |
|---|--------|----------------|------|------|
| 0.1 | 加依賴 `pgx/v5`pgxpool+ `golang-migrate/v4`go.mod/go.sum 整理 | 12 | — | 🟢 |
| 0.2 | `config.DatabaseConfig`DSN/host/port/user/password/dbname/sslmode/pool size+ env 解析 + `Enabled()` 模式 + `.env.example` | 23 | 0.1 | 🟢 |
| 0.3 | 連線池 `internal/db/pool.go`pgxpool 建池 + 啟動 ping + graceful shutdown | 24 | 0.1,0.2 | 🟢/🔵 |
| 0.4 | migration runner`migrations/` + 啟動自動 migrate up 或獨立 `cmd/migrate`+ 第一份骨架 migration | 35 | 0.1 | 🟢 |
| 0.5 | main.go wire 連線池(依 config 決定是否建池,本塊只接骨架) | 12 | 0.3 | 🟢 |
| 0.6(測試) | **整合測試基礎建設**testcontainers-go 設置 + 測試 helper`setupTestDB(t)` 自動 migrate + truncate+ fixture/factory builder | 47 | 0.1,0.4 | 🟢 |
| 0.7(測試) | **CI 接 Postgres**CI workflow 讓 testcontainers 在 CI 跑Docker-in-CI+ migration 跑通 + cache 調校 | 36 | 0.6 | 🟢 |
| 0.8(測試) | 連線池/migration 本身測試ping、migrate up/down 冪等、池耗盡、連線失敗 fail-fast | 24 | 0.6 | 🔵 |
| 0.9 | 整塊 self-review + 過 Reviewer + 修正 | 23 | 全部 | 🟢 |
| | **塊 0 小計** | **2036** | | |
> 一次性最貴投資。「DB 由他人開好」讓 0.3/0.8 不用煩惱在 130 上裝/起 DB但 testcontainers0.6)成本如實算入。
### 塊 1model 接 Postgres ← 使用者最關心
| # | 子任務 | hrs順利卡關 | 依賴 | 標記 |
|---|--------|----------------|------|------|
| 1.1 | `PostgresModelRepository`Get/List(by owner/chip/source)/Save(upsert ON CONFLICT)/Delete(soft) | 46 | 塊0 | 🟢 |
| 1.2 | `input_shape INT[]` / `classes TEXT[]` pgx array 映射 + `Source` enum + upsert 保留 CreatedAt 語意 | 24 | 1.1 | 🟢 |
| 1.3 | migration `create_models`(含 `faa_object_key TEXT` nullable — `database.md` §4 漏此欄indexowner/chip/source、`deleted_at IS NULL` partial | 23 | 塊0 | 🟢 |
| 1.4 | main.go wiring 切換config 決定 Postgres/in-memory保留 in-memory 給 local dev+ `seedDemoData` 改寫進 DB | 23 | 1.1,塊0 | 🟢 |
| 1.5(測試) | **unit/邏輯**:對齊既有 `inmemory_repository_test.go`SaveAndGet、NotFound、List 三 filter、soft delete、Save 需 ID→ Postgres 版重打 | 35 | 1.1 | 🟢 |
| 1.6(測試) | **integration/真 DB**array round-trip、upsert 保留 CreatedAt、soft-delete 後 List 不含、List filter SQL 正確性、faa_object_key nullable round-trip | 47 | 1.1,0.6 | 🟢 |
| 1.7(測試) | **邊界**:空 List、重複 Save、併發 Save 同 ID、連線中斷 Get 行為、context cancel | 35 | 1.1,0.6 | 🟢 |
| 1.8 | 整塊 self-review + 過 Reviewer + 修正 | 23 | 全部 | 🟢 |
| | **塊 1 小計** | **2236** | | |
> model 欄位最多array + FAA 欄位 + 3 維 filter測試子任務1.51.7)合計 1017 hrs偏重。
### 塊 2device 接 Postgres
| # | 子任務 | hrs順利卡關 | 依賴 | 標記 |
|---|--------|----------------|------|------|
| 2.1 | `PostgresDeviceRepository`Get/GetBySerial/List(by owner)/Save(upsert)/Delete(soft) | 35 | 塊0 | 🟢 |
| 2.2 | migration `create_devices`(雙狀態欄位 remote_status/last_seen_at/last_connected_at + `UNIQUE(owner_user_id, serial_number)` | 23 | 塊0 | 🟢 |
| 2.3 | `UNIQUE(owner,serial)` × soft-delete 語意(已刪 serial 能否重註冊 → partial unique index `WHERE deleted_at IS NULL`+ 決策註記 | 24 | 2.2 | 🟢 |
| 2.4 | main.go wiring 切換 + `seedDemoData` device | 12 | 2.1,塊0 | 🟢 |
| 2.5(測試) | **unit/邏輯**:對齊既有 device testSaveAndGet、GetBySerial 跨 owner 不串、List by owner、soft delete、再刪回 NotFound、保留 CreatedAt | 35 | 2.1 | 🟢 |
| 2.6(測試) | **integration/真 DB**unique 衝突、partial unique 讓已刪 serial 可重註冊、雙狀態欄位 round-trip、upsert 保留 CreatedAt | 46 | 2.1,0.6 | 🟢 |
| 2.7(測試) | **邊界**:空 List、併發註冊同 serial、連線中斷、context cancel | 24 | 2.1,0.6 | 🟢 |
| 2.8 | 整塊 self-review + 過 Reviewer + 修正 | 23 | 全部 | 🟢 |
| | **塊 2 小計** | **1932** | | |
### 塊 3pairing_token + session_token 接 Postgres
| # | 子任務 | hrs順利卡關 | 依賴 | 標記 |
|---|--------|----------------|------|------|
| 3.1 | `PostgresPairingStore`Create/Validate/MarkUsed/Revoke/List/CleanupExpired | 46 | 塊0 | 🟢 |
| 3.2 | `PostgresSessionTokenStore`Create/Get/Revoke/CleanupExpired含 parent_token_hash 稽核鏈) | 35 | 塊0 | 🟢 |
| 3.3 | **關鍵改動**plaintext→token_hash 當 PK。Validate/Get 先 `HashToken()` 再查;確保所有呼叫端傳 plaintext 進、內部一致 hash | 35 | 3.1,3.2 | 🟢 |
| 3.4 | migration `create_pairing_tokens`pairing + session token 是否共表 by `kind` — 需決策,傾向共表;含 used_at/revoked_at/expires_at/parent_token_hash + index | 24 | 塊0 | 🟢 |
| 3.5 | main.go wiring 切換(兩個 store+ `seedDemoData` pairing token | 23 | 3.1,3.2,塊0 | 🟢 |
| 3.6(測試) | **unit/邏輯pairing**:對齊既有 testCreateAndValidate、unknown token、MarkUsed 一次性+冪等、Revoke、CleanupExpired、List by user、Validate expired | 35 | 3.1 | 🟢 |
| 3.7(測試) | **unit/邏輯session token**:對齊既有 testCreateAndGet、NotFound、expired、Revoke 冪等、Revoke NotFound、CleanupExpired、NeverExpires ttl=0 | 35 | 3.2 | 🟢 |
| 3.8(測試) | **integration/真 DB**hash 當 PK 查詢正確性、TTL 過期、一次性 used 的 DB 層 race兩併發 MarkUsed 只一成功)、撤銷稽核欄位、共表 kind 隔離 | 58 | 3.1,3.2,0.6 | 🟢 |
| 3.9(測試) | **邊界**:併發 Validate 同 token、CleanupExpired 大量資料、連線中斷、context cancel | 24 | 0.6 | 🟢 |
| 3.10 | 整塊 self-review + 過 Reviewer + 修正 | 34 | 全部 | 🟢 |
| | **塊 3 小計** | **3049** | | |
> Postgres 三塊裡最重:兩個 store + plaintext→hash 邏輯切換(漏一個呼叫端就驗不過)+ 一次性語意的 DB 層併發正確性測試。測試子任務3.63.9)合計 1322 hrs反映「token 語意錯會出安全問題」的保守估法。
>
> **加註(不在本塊預設範圍)**remote-proxy 目前只驗 token 格式不查 store。若 Phase 1 要新增 `GET /internal/session-token/:token` 給 remote-proxy 拉驗證 → 另 **+47 hrs**,不含在塊 3 小計。
### 塊 4session 接 RedisuserSessiontunnel session 維持 in-memory
> ⚠️ 此塊需 Redis。若本期不做塊 4可叫他人**先不用開 Redis**,只開 Postgres。測試用 miniredis純 Go in-process或 testcontainers Redis不依賴 130。
| # | 子任務 | hrs順利卡關 | 依賴 | 標記 |
|---|--------|----------------|------|------|
| 4.1 | 加依賴 `redis/go-redis/v9` + `config.RedisConfig`host/port/password/db index+ env + `.env.example` + `Enabled()` | 23 | — | 🟢 |
| 4.2 | `internal/db/redis.go` 連線 + 啟動 ping + graceful close | 13 | 4.1 | 🟢/🔵 |
| 4.3 | `RedisUserSessionStore`Create/Get/Update/Delete/CleanupExpired用 Redis TTLidle + absolute 雙 TTL取代手動 cleanup goroutineExtra map JSON 序列化 | 46 | 4.1,4.2 | 🟢 |
| 4.4 | main.go wiring 切換 + 移除/停用 `runUserSessionCleanup` goroutine改靠 Redis TTL | 12 | 4.3 | 🟢 |
| 4.5(測試) | **unit/邏輯**:對齊既有 usersession testCreateAndGet、NotFound/EmptyID、Get 回副本、Update 移 LastSeenAt 不動 CreatedAt、Update NotFound/Nil、Delete 冪等、Extra round-trip、context cancel | 46 | 4.3 | 🟢 |
| 4.6(測試) | **integration/真 Redis**TTL 實際過期、idle vs absolute 雙 timeout、序列化 round-trip、key 命名/隔離 | 35 | 4.3 | 🟢 |
| 4.7(測試) | **邊界 + 併發**race detector、Redis 連線中斷 store 行為、TTL 邊界 | 24 | 4.3 | 🟢 |
| 4.8 | 整塊 self-review + 過 Reviewer + 修正 | 23 | 全部 | 🟢 |
| | **塊 4 小計** | **1932** | | |
> tunnel session`internal/session`value 是活的 yamux Handle 不可序列化 → 維持 in-memory單節點本塊不動。跨節點「Summary 放 Redis、handle 留本地」列範圍外(要的話另估 +1326 hrs ≈ 24 人天)。
### 塊 5一致性 / 交易 / 連線韌性 / 健康檢查
| # | 子任務 | hrs順利卡關 | 依賴 | 標記 |
|---|--------|----------------|------|------|
| 5.1 | `internal/db/tx.go` 交易 helperpgx tx 包裝 + rollback on error + context | 24 | 塊0 | 🟢 |
| 5.2 | 跨 store 交易:建 Device + PairingToken 同 tx、刪 Device cascade 撤銷 token、converter job 完成→建 model transactional upsert | 47 | 5.1,塊1-3 | 🟢 |
| 5.3 | 連線韌性pool retry / context timeout / 斷線重連 / fail-fast vs degrade 策略(策略需使用者裁決) | 36 | 塊0 | 🟢 |
| 5.4 | `/healthz` 擴充 DB/Redis ping + 統一 pgx error → `internal/api/errors.go` 映射 | 24 | 塊0 | 🟢 |
| 5.5(測試) | **交易測試**rollback中途失敗整筆回滾、cascade 撤銷 token 交易邊界、併發交易 | 46 | 5.2,0.6 | 🟢 |
| 5.6(測試) | **韌性測試**連線中斷模擬、timeout、健康檢查回應DB down 時 /healthz 行為) | 35 | 5.3,5.4,0.6 | 🟢 |
| 5.7 | 整塊 self-review + 過 Reviewer + 修正 | 23 | 全部 | 🟢 |
| | **塊 5 小計** | **2035** | | |
> 區間寬反映「做到多嚴謹」的彈性:最小只做 /healthz + 基本 tx取下限完整韌性策略retry/degrade/cascade 全做)取上限。
### 塊 6stage 部署接 130 + e2e 回歸驗證
| # | 子任務 | hrs順利卡關 | 依賴 | 標記 |
|---|--------|----------------|------|------|
| 6.1 | 拿到他人開好的 DB 連線資訊後:`.env.stage` 注入 DSN/憑證(走既有 secrets 機制,不進 repo | 12 | DB 連線資訊 | 🔵 |
| 6.2 | 在 130 的 visionA database 跑 migration + 確認 PG 版本支援 `gen_random_uuid()`/CITEXT不支援則調 migration | 24 | 6.1 | 🔵 |
| 6.3 | stage 部署設定 / compose / CI 部署步驟接上 DB env | 23 | 6.1 | 🔵 |
| 6.4(測試) | **e2e 持久化驗證**:登入(OIDC)→配對→上傳 model→列 model→**重啟 backend→資料還在**(核心驗收)+ 空庫/seed 啟動正常 | 35 | 6.2,6.3 | 🔵 |
| 6.5(測試/回歸) | **回歸測試**:接 DB 後既有 e2e/integration約 6+ 檔)重跑,修因持久化行為改變而壞的測試 | 48 | 6.2 | 🔵/🟢 |
| 6.6 | 部署驗證彙報 + 修 stage 特有問題 | 13 | 全部 | 🔵 |
| | **塊 6 小計** | **1325** | | |
> 6.5 回歸測試刻意估寬48 hrs既有 6+ 個 e2e/integration 測試檔,接 DB 後「重啟資料還在」「seed 行為」「token hash 切換」都可能讓 in-memory 假設失效,要逐一確認。**這是「測試多估」的具體體現之一。**
---
## 5. 工時總表與測試占比分析
### 5.1 各塊 man-hours 總表
| 塊 | 功能 | hrs順利卡關 | 換算 Man-day÷6.5 | DB | 主要依賴 |
|----|------|----------------|---------------------|-----|---------|
| 0 | DB 基礎建設(連線池/config/migration/testcontainers/CI | 2036 | 3.15.5 | PG | — |
| 1 | **model 接 Postgres含 FAAObjectKey** | 2236 | 3.45.5 | PG | 塊0 |
| 2 | device 接 Postgres | 1932 | 2.94.9 | PG | 塊0 |
| 3 | pairing + session token 接 Postgres | 3049 | 4.67.5 | PG | 塊0 |
| 4 | session 接 RedisuserSession | 1932 | 2.94.9 | Redis | —與塊0平行 |
| 5 | 一致性/交易/韌性/健康檢查 | 2035 | 3.15.4 | PG | 塊13 |
| 6 | stage 部署接 130 + e2e 回歸驗證 | 1325 | 2.03.8 | PG+Redis | 想驗的塊 |
| | **全部加總** | **143245** | **2237.7** | | |
### 5.2 三種範圍累計
| 範圍 | 包含塊 | 累計 hrs | Man-day | 說明 |
|------|--------|---------|---------|------|
| **最小可行(只 model** | 0 + 1 + 6 精簡(只跑 6.16.4 約 916 hrs | **5188** | **7.813.5** | 模型庫持久化 + 驗證重啟資料還在。塊 0 是硬前置。 |
| **持久資料** | 0 + 1 + 2 + 3 + 6 完整 | **104178** | **1627.4** | 業務持久資料上 Postgres。session 仍 in-memory內測可接受。 |
| **完整** | 0+1+2+3+4+5+6 | **143245** | **2237.7** | 全部持久化 + Redis session + 交易/韌性。tunnel 多節點 summary 範圍外(另 +1326 hrs。 |
> 三種範圍皆已含塊 0 與一次塊 6 驗證;中間範圍不重複計塊 6。最小範圍的塊 6 只跑 6.16.4(不含完整回歸 6.5),故取精簡值。
### 5.3 測試占比分析(「測試多估」的結果)
| 塊 | 測試子任務 | 測試 hrs | 該塊總 hrs | 測試占比(中位) |
|----|-----------|---------|-----------|----------------|
| 0 | 0.6+0.7+0.8 | 917 | 2036 | ~46% |
| 1 | 1.5+1.6+1.7 | 1017 | 2236 | ~47% |
| 2 | 2.5+2.6+2.7 | 915 | 1932 | ~47% |
| 3 | 3.6+3.7+3.8+3.9 | 1322 | 3049 | ~44% |
| 4 | 4.5+4.6+4.7 | 915 | 1932 | ~47% |
| 5 | 5.5+5.6 | 711 | 2035 | ~33% |
| 6 | 6.4+6.5 | 713 | 1325 | ~53% |
- **純測試子任務加總**64110 hrs。
- **總工時**143245 hrs。
- **測試占比 ≈ 45%(中位)**,範圍 41%48%。
> 一般專案「測試占 2535%」是常態;本估**刻意拉到 ~45%**反映使用者「測試多估、充足覆蓋」的明確要求。testcontainers/CI 一次性基礎建設(塊 0 的 917 hrs是大頭——做一次、後續所有塊共用。若日後評估「測試估太保守」最先可砍各塊「邊界」子任務的卡關上限但**不建議砍 integration 與回歸**(那是「重啟資料還在」的核心保證)。
---
## 6. 可先做(不卡 DBvs 等 DB 的劃分
### 6.1 拿到 DB 連線資訊前「就能先做」(🟢,約 80% 工作)
- **塊 0**加依賴、config、連線池建池邏輯、migration runner+骨架、main.go 接骨架、testcontainers 設置、CI Postgres。
- **塊 1/2/3****全部 repository 實作 + migration 撰寫 + 全部測試unit + integration via testcontainers + 邊界)**。testcontainers 自己起一次性 DB完全不依賴 130。
- **塊 4**全部miniredis / testcontainers Redis
- **塊 5**tx helper、韌性邏輯、health 擴充、交易測試testcontainers
**拿到連線資訊前,最小範圍(塊 0+1可做到「testcontainers 全綠」(約 6.511 個工作天的活),只差最後 stage 接上驗證。**
### 6.2 必須等 DB 連線資訊 / 真 DB🔵
- **塊 0**0.3 連真 DB ping 確認、0.8 對真 DB 測試(可先用 testcontainers 跑、再對 stage DB 確認一次)。
- **塊 6****整塊**(注入憑證、在 130 跑 migration、部署設定、e2e、stage 回歸、彙報)—— 這是唯一硬卡 DB 連線資訊的塊。
---
## 7. 排程建議(假設 1 個 backend 全職)
### 7.1 依賴關係(哪些可平行)
```
塊0基礎建設──┬──► 塊1model──┐
├──► 塊2device─┤
├──► 塊3token──┼──► 塊5交易/韌性需塊1-3
塊4Redis─────與塊0平行獨立─┘
塊6stage 驗證)◄── 拿到 DB 連線資訊 + 想驗的塊完成
```
- **塊 0 是所有 Postgres 塊的硬前置**,必須先做。
- **塊 4Redis可與塊 0 平行**(依賴獨立)—— 單人全職時意義不大,列為「若有第二人可平行」。
- **塊 1/2/3 在塊 0 完成後可任意順序**,使用者最關心 model → 建議塊 1 優先。
- **塊 5 需塊 13 的 store 都在**才能做跨 store 交易。
- **塊 6 卡 DB 連線資訊**(唯一硬卡外部)。
### 7.2 最小範圍(塊 0+1單人全職時程
| 階段 | 子任務 | 工作天×6.5hr/day |
|------|--------|---------------------|
| 第 1 階段(拿連線資訊前可做) | 塊0 全部 🟢2036 hrs+ 塊1 全部 🟢2236 hrstestcontainers 全綠) | **6.511 天** |
| 第 2 階段(拿到連線資訊後) | 塊6 精簡6.16.4916 hrs+ 塊0 真 DB 確認 | **1.52.5 天** |
| **最小範圍合計** | | **約 813.5 個工作天** |
> 第 1 階段6.511 天的活)**完全不卡 DB**——只要拍板做最小範圍backend 可立刻開工做到 testcontainers 全綠,等他人把 130 的 DB 開好、給連線資訊,再花 1.52.5 天接上 stage 驗證收尾。**「等 DB」不會 block 大部分工作。**
### 7.3 持久資料範圍單人全職時程
- 第 1 階段(拿連線資訊前):塊 0+1+2+3 全部 🟢 ≈ 91153 hrs ≈ **1424 天**
- 第 2 階段(拿到後):塊 6 完整(含回歸 6.5)≈ 1325 hrs ≈ **24 天**
- 合計 **約 1628 個工作天**
---
## 8. 範圍外(本估算未計入)
要做再另加,不含在上述任何範圍:
| 項目 | 估算 | 說明 |
|------|------|------|
| cluster / converter_job 兩張表 | 未估 | 目前 `main.go` 無對應 in-memory store 被 wireconverter 是 stub、cluster 只有 types |
| remote-proxy `/internal/session-token` 驗證 endpoint | +47 hrs | 目前 remote-proxy 只驗 token 格式不查 store |
| tunnel session 多節點 Redis summary | +1326 hrs24 人天) | handle 不可序列化需「Summary 放 Redis、handle 留本地」混合實作 |
| prod 環境另建 DB | 未估 | prod 是否用 130 或另一套RDS/Cloud SQL/自建)未定,影響韌性/HA/成本 |
---
## 9. 待提供 / 待決策清單
### 9.1 待他人提供的 DB 連線資訊(開好後請給)
| 項目 | 為什麼需要 | 阻塞 |
|------|-----------|------|
| Postgreshost / port / user / password / dbname | 塊 6 連線config 可先用 placeholder 寫好) | 塊 6 |
| **PG 版本** | 影響 `gen_random_uuid()`(需 PG13+ 或 pgcrypto、CITEXT extension → 影響 migration 寫法 | 塊 6.2 |
| sslmodedisable/require/verify-full | DSN 組裝 | 塊 6.1 |
| visionA database 是否已建好、app role 權限 | 確認 migration 能跑、有無建 extension 權限 | 塊 6.2 |
| **Redis有無開、host/port、有無密碼** | 決定塊 4 是否本期做;若不做可叫他人先不開 Redis | 塊 4、塊 6 |
### 9.2 待使用者決策(影響估算/設計)
| # | 項目 | 影響 |
|---|------|------|
| 1 | **範圍**:最小 / 持久資料 / 完整session→Redis 本期做嗎? | 決定總工時與要不要請他人開 Redis |
| 2 | session token 與 pairing token 是否共表by `kind` | 塊 3.4 schema |
| 3 | DB 掛掉降級策略fail-fast(503) 還是 degrade | 塊 5.3 韌性 |
| 4 | 已 soft-delete 的 device serial 能否重註冊? | 塊 2.3 partial unique index |
| 5 | remote-proxy 是否本期要 `/internal/session-token` 驗證 endpoint | 塊 3 加註(+47 hrs |
---
## 10. 給 Orchestrator 的後續建議database.md 更新,本文件未動)
> 以下為 `database.md`(共享文件)應同步更新的缺口,建議另派 Architect 處理,不在本工時估算內。
1. §2.3/§4 models table 補 `faa_object_key TEXT`nullable—— code 已有ADR-017schema 漏。
2. §4 補 DatabaseConfig / RedisConfig env 規格DSN/pool/sslmode/redis password
3. §5 補 migration 第一份清單(哪些 table 在第一個 migration
4. 可選§2.7 補 tunnel session「handle 不可序列化、單節點維持 in-memory」結論。
5.`database.md` 提及「DB 在 130 現成可用」字眼,需更正為「**DB 由他人在 130 另開 visionA 專用實例並提供連線資訊visionA 端不負責 provision只負責跑 migration 與接上**」(與 §1.3 前提一致)。