166 lines
5.8 KiB
Markdown
166 lines
5.8 KiB
Markdown
|
|
# 前後端改進建議
|
|||
|
|
|
|||
|
|
依「影響使用者 / 維護成本 / 風險」排序。細節背景見先前健康檢查;**架構導覽**見 `docs/architecture.md`。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 後端:立刻值得修的問題
|
|||
|
|
|
|||
|
|
### 1. Job dedupe:改寫草稿全域互擋
|
|||
|
|
|
|||
|
|
`generate-rewrite-draft` 的 `DedupeKeys` 為空,但 `buildDedupeKey` 仍用 `type|scope` 產 key → 所有 `scope=persona` 的改寫 job 共用一把 lease。
|
|||
|
|
|
|||
|
|
- 檔案:`internal/model/job/usecase/usecase.go`、`helpers.go`
|
|||
|
|
- 建議:`DedupeKeys: []string{"scope_id"}`,或空 keys 時回傳 `""` 不啟用 lease
|
|||
|
|
- 補 test
|
|||
|
|
|
|||
|
|
### 2. CreateRun 失敗路徑用裸 `Update`
|
|||
|
|
|
|||
|
|
lease 失敗時 `runs.Update` 直接改 status,違反 guarded 規則。
|
|||
|
|
|
|||
|
|
- 改 `UpdateIfStatus(pending → cancelled)`
|
|||
|
|
|
|||
|
|
### 3. ClaimNext lock 失敗立刻 re-enqueue
|
|||
|
|
|
|||
|
|
可能 tight loop 打 Redis。應 delay / 檢查狀態後再入列,FindByID 失敗不要 silent drop。
|
|||
|
|
|
|||
|
|
### 4. Worker 忽略 Heartbeat 錯誤
|
|||
|
|
|
|||
|
|
`_ = step.Heartbeat(ctx)` 在 lock 過期後仍繼續跑。失敗應中止 step。
|
|||
|
|
|
|||
|
|
### 5. Refresh token 並發
|
|||
|
|
|
|||
|
|
先發新 token 再 blacklist 舊的 → 並發 refresh 可能雙活。應用原子輪替(Lua / 先 blacklist 再 issue,或 refresh family)。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 後端:結構與可維護性
|
|||
|
|
|
|||
|
|
| 議題 | 建議 |
|
|||
|
|
|------|------|
|
|||
|
|
| ServiceContext 過胖 | 依域拆 bootstrap(auth/job/content/publish),減少單檔 400+ 行 DI |
|
|||
|
|
| 每個 worker process 註冊全部 handler | 只 register 該 `workerType` 需要的 step;啟動時 assert template↔handler |
|
|||
|
|
| ghost worker 在 compose | 已清理一輪;新增 template 時同步 `dev-all` / `docker-compose` |
|
|||
|
|
| 列表 API 用 `limit` | 新 API 一律 `page/pageSize`;舊的雙讀後 deprecate |
|
|||
|
|
| `time.Now().UnixNano()` 散落 | 統一 `clock.NowUnixNano()` 利於測試 |
|
|||
|
|
| Secrets key 可空 | prod config validate:`HAIXUN_SECRETS_KEY` 必填 |
|
|||
|
|
| copy_mission 半殘 | 見 `post-project-plan`:過渡期只留 inspire;完整 pipeline 勿再接,走 Post Project |
|
|||
|
|
| 測試 | Job 較完整;auth refresh、ClaimNext race、logic 層幾乎無測 — 優先補 P0 |
|
|||
|
|
|
|||
|
|
### 錯誤處理慣例
|
|||
|
|
|
|||
|
|
- 好:`errs.For(code.X).…` + `response.Write`
|
|||
|
|
- 差:`_, _ = FailRun` / `UpdateProgress` 吞錯誤 → 可觀測性差,長任務尤其危險
|
|||
|
|
|
|||
|
|
### 不建議做的事
|
|||
|
|
|
|||
|
|
- 為了 template-monorepo 相容引入 OAuth/OTP/Zitadel 重依賴
|
|||
|
|
- 在 provider adapter 裡讀 Mongo / setting
|
|||
|
|
- 裸 `Update` 蓋 job / publish_queue 狀態
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 前端:可明顯變好的地方
|
|||
|
|
|
|||
|
|
### 1. 登入態對 5xx 過於暴力
|
|||
|
|
|
|||
|
|
`AuthContext.reloadMember` 任何錯誤都 `clearTokens()`。gateway 短暫 502 會踢登出。
|
|||
|
|
|
|||
|
|
建議:僅 401 / refresh 失敗清 token;5xx 顯示「後端暫時無法連線」。
|
|||
|
|
|
|||
|
|
### 2. 巨型頁面
|
|||
|
|
|
|||
|
|
| 檔案 | 約略行數 | 建議 |
|
|||
|
|
|------|----------|------|
|
|||
|
|
| `PatrolPage.tsx` | ~1900 | 拆 list / graph / scan / outreach 子元件 + hooks |
|
|||
|
|
| `MissionsPage.tsx` | ~1200 | 若走 Post Project 直接換 wizard,勿再膨脹 |
|
|||
|
|
| `OwnPostsHub.tsx` | ~750 | 拆 formula / reply / list |
|
|||
|
|
|
|||
|
|
好處:可測、可讀、島民/自動化也較好掛 `data-islander-*`。
|
|||
|
|
|
|||
|
|
### 3. Hash 路由的限制
|
|||
|
|
|
|||
|
|
- 無巢狀 path、難做 deep link(例如 `#patrol/topic/xyz`)
|
|||
|
|
- 中長期可考慮 React Router,或至少支援 `page + query` 狀態
|
|||
|
|
|
|||
|
|
### 4. 過時 UI / 死 client
|
|||
|
|
|
|||
|
|
| 項目 | 建議 |
|
|||
|
|
|------|------|
|
|||
|
|
| `GapsPage` | 對齊 `threads-automation-gap-plan` 真實進度,或改「已完成 checklist」 |
|
|||
|
|
| `haixun.ts` 未呼叫方法 | 定期刪(inventory API client 等) |
|
|||
|
|
| `extensionSync` 固定 reject | 文件與 UI 標「已停用」;重啟時勿再 postMessage JWT |
|
|||
|
|
| jobLabels 幽靈 type | 隨 ghost template 刪 |
|
|||
|
|
|
|||
|
|
### 5. 設計系統(已做得不錯)
|
|||
|
|
|
|||
|
|
- 繼續只用 `--hx-*` / semantic Tailwind;勿引入 MUI/Ant
|
|||
|
|
- 共用 `ui.tsx`(Button 必須有 children)
|
|||
|
|
- 大頁面新功能優先組 Card / Field / Badge,不要另寫色票
|
|||
|
|
|
|||
|
|
### 6. API 層
|
|||
|
|
|
|||
|
|
`client.ts` 的 refresh single-flight、502 retry 是好的。可再:
|
|||
|
|
|
|||
|
|
- 對 `ApiError` 做 `isUnauthorized` / `isUnavailable` helper,頁面統一處理
|
|||
|
|
- 長輪詢(jobs 3s)考慮 visibility API:分頁隱藏時降頻
|
|||
|
|
|
|||
|
|
### 7. 測試
|
|||
|
|
|
|||
|
|
目前幾乎無前端 test。最低投資:
|
|||
|
|
|
|||
|
|
- `client.ts` refresh + 502 retry
|
|||
|
|
- `AuthContext` 5xx 不登出
|
|||
|
|
- 純函式:`jobStatus`、`format` nano 轉換
|
|||
|
|
|
|||
|
|
### 8. AGENTS 寫了但未存在的功能
|
|||
|
|
|
|||
|
|
- **Islander agent runtime**(`web/src/lib/islander`)文件超前,程式沒有
|
|||
|
|
→ 實作或從 AGENTS 刪除描述,避免後續 agent 按空文件改碼
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 產品債(架構外但影響體感)
|
|||
|
|
|
|||
|
|
1. **Flow A 過渡態**:Missions 用 inspire + content_ops + drafts,舊 copy_mission CRUD 已移除/不可用;應盡快 Post Project 或明確標「精簡模式」。
|
|||
|
|
2. **Extension 同步關閉**:海巡 dev crawl 若依賴 browser session,需新安全協議(short-lived code,勿 JWT postMessage)。
|
|||
|
|
3. **Publish inventory refill job** 無 handler:policy API 可留,refill job 勿再 enqueue(應已清)。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 建議迭代順序(若只做改進、不做大功能)
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
Sprint A 正確性
|
|||
|
|
rewrite dedupe、CreateRun guarded cancel、ClaimNext、Heartbeat、AuthContext 5xx
|
|||
|
|
|
|||
|
|
Sprint B 可理解性
|
|||
|
|
拆 PatrolPage / Missions 邊界、GapsPage 對齊、AGENTS 修路徑與 auth matrix
|
|||
|
|
|
|||
|
|
Sprint C 可觀測
|
|||
|
|
worker 錯誤上報、job stranded reaper、prod secrets validate
|
|||
|
|
|
|||
|
|
Sprint D 產品
|
|||
|
|
Post Project(取代 Flow A)或凍結 Missions 只修 bug
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 自我檢查清單(PR 前)
|
|||
|
|
|
|||
|
|
後端:
|
|||
|
|
|
|||
|
|
- [ ] 列表是否 `page/pageSize` + pagination
|
|||
|
|
- [ ] Job 狀態是否 guarded
|
|||
|
|
- [ ] 長任務是否處理 Heartbeat 錯誤
|
|||
|
|
- [ ] 錯誤是否 `errs.For(code.X)`
|
|||
|
|
- [ ] 時間是否 unix nano UTC
|
|||
|
|
|
|||
|
|
前端:
|
|||
|
|
|
|||
|
|
- [ ] 是否只用 semantic token
|
|||
|
|
- [ ] Button 是否有中文動詞 children
|
|||
|
|
- [ ] 是否經 `api/client` envelope
|
|||
|
|
- [ ] 錯誤是否區分 401 vs 5xx
|
|||
|
|
- [ ] `make web-build` 通過
|