312 lines
8.8 KiB
Markdown
312 lines
8.8 KiB
Markdown
|
|
# TODOS
|
|||
|
|
|
|||
|
|
重構 cursor-api-proxy → go-zero + DDD Architecture 的待辦事項。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Phase 1: API 定義與骨架生成
|
|||
|
|
|
|||
|
|
### DONE
|
|||
|
|
- [x] 建立 `api/chat.api` 定義檔
|
|||
|
|
- [x] 建立 `etc/chat.yaml` 配置檔
|
|||
|
|
- [x] 生成代碼骨架
|
|||
|
|
- [x] 移動 `chat.go` 到 `cmd/chat/`
|
|||
|
|
|
|||
|
|
### TODO
|
|||
|
|
|
|||
|
|
#### TODO-1: 全局變數遷移清單
|
|||
|
|
- **What**: 建立全局變數到 ServiceContext 的遷移清單
|
|||
|
|
- **Why**: 現有代碼有多個全局變數,遷移時容易遺漏
|
|||
|
|
- **Files**:
|
|||
|
|
- `internal/pool/pool.go:36-38` → `globalPool`, `globalMu` → ServiceContext
|
|||
|
|
- `internal/process/process.go:117` → `MaxModeFn` → ServiceContext
|
|||
|
|
- `internal/handlers/chat.go:28-29` → `rateLimitRe`, `retryAfterRe` → ServiceContext 或常數
|
|||
|
|
- `internal/models/cursormap.go:8,47,51` → 正則表達式常數化
|
|||
|
|
- **Decision**: ServiceContext 注入
|
|||
|
|
- **Effort**: human ~2h / CC ~30min
|
|||
|
|
- **Depends on**: Phase 2 (Domain 層建立)
|
|||
|
|
- **Status**: pending
|
|||
|
|
|
|||
|
|
#### TODO-2: go.mod 更新
|
|||
|
|
- **What**: 添加 go-zero 依賴到 go.mod
|
|||
|
|
- **Why**: 現有 go.mod 沒有 go-zero 依賴
|
|||
|
|
- **Command**: `go get github.com/zeromicro/go-zero@latest`
|
|||
|
|
- **Decision**: 使用最新穩定版
|
|||
|
|
- **Effort**: human ~5min / CC ~1min
|
|||
|
|
- **Depends on**: Phase 1 開始前
|
|||
|
|
- **Status**: pending
|
|||
|
|
|
|||
|
|
#### TODO-3: Makefile 更新
|
|||
|
|
- **What**: 更新 Makefile 以支援 go-zero 的建置流程
|
|||
|
|
- **Why**: 需要新增 goctl 命令和整合現有 env/run 命令
|
|||
|
|
- **Commands to add**:
|
|||
|
|
```makefile
|
|||
|
|
.PHONY: api
|
|||
|
|
api:
|
|||
|
|
goctl api go -api api/chat.api -dir . --style go_zero
|
|||
|
|
|
|||
|
|
.PHONY: api-doc
|
|||
|
|
api-doc:
|
|||
|
|
goctl api doc -api api/chat.api -dir docs/
|
|||
|
|
|
|||
|
|
.PHONY: gen
|
|||
|
|
gen: api
|
|||
|
|
go mod tidy
|
|||
|
|
```
|
|||
|
|
- **Decision**: 需要追蹤
|
|||
|
|
- **Effort**: human ~30min / CC ~10min
|
|||
|
|
- **Depends on**: Phase 1 (API 定義與骨架生成)
|
|||
|
|
- **Status**: pending
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Phase 2: Domain 層建立
|
|||
|
|
|
|||
|
|
### DONE
|
|||
|
|
- [ ] 建立 `pkg/domain/entity/`
|
|||
|
|
- [ ] 建立 `pkg/domain/repository/`
|
|||
|
|
- [ ] 建立 `pkg/domain/usecase/`
|
|||
|
|
- [ ] 建立 `pkg/domain/const/`
|
|||
|
|
|
|||
|
|
### TODO
|
|||
|
|
|
|||
|
|
#### TODO-4: import 循環依賴檢測
|
|||
|
|
- **What**: 在每個 Phase 完成後執行 `go build ./...` 檢測循環依賴
|
|||
|
|
- **Why**: DDD 架構分層容易產生循環依賴
|
|||
|
|
- **Potential cycles**:
|
|||
|
|
- `pkg/usecase` ↔ `pkg/domain/usecase`
|
|||
|
|
- `pkg/repository` ↔ `pkg/domain/repository`
|
|||
|
|
- **Command**: `go build ./... && go test ./... -run=none`
|
|||
|
|
- **Depends on**: 每個 Phase 完成後
|
|||
|
|
- **Status**: pending
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Phase 8: Internal 層重組
|
|||
|
|
|
|||
|
|
### DONE
|
|||
|
|
- [ ] 更新 `internal/config/config.go`
|
|||
|
|
- [ ] 建立 `internal/svc/servicecontext.go`
|
|||
|
|
- [ ] 建立 `internal/logic/`
|
|||
|
|
- [ ] 建立 `internal/handler/`
|
|||
|
|
- [ ] 建立 `internal/middleware/`
|
|||
|
|
|
|||
|
|
### TODO
|
|||
|
|
|
|||
|
|
#### TODO-5: SSE 整合測試
|
|||
|
|
- **What**: 增加 SSE streaming 的端對端測試
|
|||
|
|
- **Why**: SSE 是核心功能,自訂 handler 容易出錯,沒有測試覆蓋
|
|||
|
|
- **Test cases**:
|
|||
|
|
1. SSE streaming 請求正常返回
|
|||
|
|
2. SSE client disconnect 正確處理
|
|||
|
|
3. SSE timeout 正確處理
|
|||
|
|
4. 非串流請求轉 SSE 格式
|
|||
|
|
- **Implementation**:
|
|||
|
|
```go
|
|||
|
|
// tests/integration/sse_test.go
|
|||
|
|
func TestSSEStreaming(t *testing.T) {
|
|||
|
|
// 使用 httptest 模擬 SSE 客戶端
|
|||
|
|
// 驗證 data: [DONE] 正確返回
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
- **Decision**: 使用 `rest.WithCustom` 路由
|
|||
|
|
- **Effort**: human ~2h / CC ~30min
|
|||
|
|
- **Depends on**: Phase 8 完成(Internal 層重組)
|
|||
|
|
- **Status**: pending
|
|||
|
|
|
|||
|
|
#### TODO-6: SSE Handler 實作
|
|||
|
|
- **What**: 使用 `rest.WithCustom` 實作 SSE streaming handler
|
|||
|
|
- **Why**: go-zero 標準 handler 不支援 SSE,需要自訂
|
|||
|
|
- **Implementation**:
|
|||
|
|
```go
|
|||
|
|
// internal/handler/chat_handler.go
|
|||
|
|
func NewChatHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
|||
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|||
|
|
// SSE 設定
|
|||
|
|
w.Header().Set("Content-Type", "text/event-stream")
|
|||
|
|
w.Header().Set("Cache-Control", "no-cache")
|
|||
|
|
w.Header().Set("Connection", "keep-alive")
|
|||
|
|
|
|||
|
|
// 委託給 usecase
|
|||
|
|
svcCtx.ChatUsecase.Stream(r.Context(), input, callback)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
- **Decision**: 使用 `rest.WithCustom` 路由
|
|||
|
|
- **Effort**: human ~2h / CC ~30min
|
|||
|
|
- **Depends on**: Phase 6 (Usecase 層建立)
|
|||
|
|
- **Status**: pending
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Phase 10: 清理與測試
|
|||
|
|
|
|||
|
|
### DONE
|
|||
|
|
- [ ] 移除舊目錄
|
|||
|
|
- [ ] 更新 import 路徑
|
|||
|
|
- [ ] 執行測試
|
|||
|
|
|
|||
|
|
### TODO
|
|||
|
|
|
|||
|
|
#### TODO-7: 測試文件遷移
|
|||
|
|
- **What**: 測試文件跟隨源碼遷移到 pkg/
|
|||
|
|
- **Why**: 測試應該與源碼在同一目錄
|
|||
|
|
- **Files to migrate**:
|
|||
|
|
- `internal/httputil/httputil_test.go` → `pkg/infrastructure/httputil/`
|
|||
|
|
- `internal/config/config_test.go` → `internal/config/` (保留)
|
|||
|
|
- `internal/sanitize/sanitize_test.go` → `pkg/usecase/`
|
|||
|
|
- `internal/models/cursormap_test.go` → `pkg/domain/const/`
|
|||
|
|
- `internal/models/cursorcli_test.go` → `pkg/domain/const/`
|
|||
|
|
- `internal/parser/stream_test.go` → `pkg/infrastructure/parser/`
|
|||
|
|
- `internal/env/env_test.go` → `pkg/infrastructure/env/`
|
|||
|
|
- `internal/winlimit/winlimit_test.go` → `pkg/infrastructure/winlimit/`
|
|||
|
|
- `internal/anthropic/anthropic_test.go` → `pkg/adapter/anthropic/`
|
|||
|
|
- `internal/pool/pool_test.go` → `pkg/repository/`
|
|||
|
|
- `internal/openai/openai_test.go` → `pkg/adapter/openai/`
|
|||
|
|
- `internal/process/process_test.go` → `pkg/infrastructure/process/`
|
|||
|
|
- **Decision**: 測試遷移到 pkg/
|
|||
|
|
- **Effort**: human ~1h / CC ~10min
|
|||
|
|
- **Depends on**: Phase 3-7 完成
|
|||
|
|
- **Status**: pending
|
|||
|
|
|
|||
|
|
#### TODO-8: ServiceContext 單例 Pool
|
|||
|
|
- **What**: AccountPool 使用單例模式,透過 sync.Once 確保只初始化一次
|
|||
|
|
- **Why**: 避免每次請求創建新 Pool 的開銷
|
|||
|
|
- **Implementation**:
|
|||
|
|
```go
|
|||
|
|
// pkg/repository/account.go
|
|||
|
|
var (
|
|||
|
|
globalPool *AccountPool
|
|||
|
|
globalPoolOnce sync.Once
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
func GetAccountPool(configDirs []string) *AccountPool {
|
|||
|
|
globalPoolOnce.Do(func() {
|
|||
|
|
globalPool = NewAccountPool(configDirs)
|
|||
|
|
})
|
|||
|
|
return globalPool
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
- **Decision**: 使用單例 Pool
|
|||
|
|
- **Effort**: human ~30min / CC ~10min
|
|||
|
|
- **Depends on**: Phase 4 (Repository 層實作)
|
|||
|
|
- **Status**: pending
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Phase 獨立 TODO
|
|||
|
|
|
|||
|
|
### TODO-9: 回歸測試自動化
|
|||
|
|
- **What**: 建立自動化回歸測試腳本
|
|||
|
|
- **Why**: 確保每次遷移後功能正常
|
|||
|
|
- **Script**:
|
|||
|
|
```bash
|
|||
|
|
# scripts/regression-test.sh
|
|||
|
|
#!/bin/bash
|
|||
|
|
set -e
|
|||
|
|
|
|||
|
|
echo "=== Health check ==="
|
|||
|
|
curl -s http://localhost:8080/health | jq .
|
|||
|
|
|
|||
|
|
echo "=== Models list ==="
|
|||
|
|
curl -s http://localhost:8080/v1/models | jq .
|
|||
|
|
|
|||
|
|
echo "=== Chat completion (non-streaming) ==="
|
|||
|
|
curl -s -X POST http://localhost:8080/v1/chat/completions \
|
|||
|
|
-H "Content-Type: application/json" \
|
|||
|
|
-d '{"model":"test","messages":[{"role":"user","content":"hi"}],"stream":false}' | jq .
|
|||
|
|
|
|||
|
|
echo "=== Chat completion (streaming) ==="
|
|||
|
|
curl -s -X POST http://localhost:8080/v1/chat/completions \
|
|||
|
|
-H "Content-Type: application/json" \
|
|||
|
|
-d '{"model":"test","messages":[{"role":"user","content":"hi"}],"stream":true}'
|
|||
|
|
```
|
|||
|
|
- **Depends on**: Phase 10 完成
|
|||
|
|
- **Status**: pending
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Summary
|
|||
|
|
|
|||
|
|
| TODO | Phase | Effort | Status |
|
|||
|
|
|------|-------|--------|--------|
|
|||
|
|
| TODO-1: 全局變數遷移清單 | Phase 2 | 2h | pending |
|
|||
|
|
| TODO-2: go.mod 更新 | Phase 1 | 5min | pending |
|
|||
|
|
| TODO-3: Makefile 更新 | Phase 1 | 30min | pending |
|
|||
|
|
| TODO-4: import 循環依賴檢測 | Each Phase | 5min | pending |
|
|||
|
|
| TODO-5: SSE 整合測試 | Phase 8 | 2h | pending |
|
|||
|
|
| TODO-6: SSE Handler 實作 | Phase 8 | 2h | pending |
|
|||
|
|
| TODO-7: 測試文件遷移 | Phase 10 | 1h | pending |
|
|||
|
|
| TODO-8: ServiceContext 單例 Pool | Phase 4 | 30min | pending |
|
|||
|
|
| TODO-9: 回歸測試自動化 | Phase 10 | 30min | pending |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Dependencies Graph
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
Phase 1 (API 定義)
|
|||
|
|
│
|
|||
|
|
├── TODO-2: go.mod 更新 (必须在開始前完成)
|
|||
|
|
├── TODO-3: Makefile 更新
|
|||
|
|
│
|
|||
|
|
▼
|
|||
|
|
Phase 2 (Domain 層)
|
|||
|
|
│
|
|||
|
|
├── TODO-1: 全局變數遷移清單
|
|||
|
|
├── TODO-4: import 循環依賴檢測
|
|||
|
|
│
|
|||
|
|
▼
|
|||
|
|
Phase 3 (Infrastructure 層)
|
|||
|
|
│
|
|||
|
|
├── TODO-4: import 循環依賴檢測
|
|||
|
|
│
|
|||
|
|
▼
|
|||
|
|
Phase 4 (Repository 層)
|
|||
|
|
│
|
|||
|
|
├── TODO-8: ServiceContext 單例 Pool
|
|||
|
|
├── TODO-4: import 循環依賴檢測
|
|||
|
|
│
|
|||
|
|
▼
|
|||
|
|
Phase 5 (Provider 層)
|
|||
|
|
│
|
|||
|
|
├── TODO-4: import 循環依賴檢測
|
|||
|
|
│
|
|||
|
|
▼
|
|||
|
|
Phase 6 (Usecase 層)
|
|||
|
|
│
|
|||
|
|
├── TODO-4: import 循環依賴檢測
|
|||
|
|
│
|
|||
|
|
▼
|
|||
|
|
Phase 7 (Adapter 層)
|
|||
|
|
│
|
|||
|
|
├── TODO-4: import 循環依賴檢測
|
|||
|
|
│
|
|||
|
|
▼
|
|||
|
|
Phase 8 (Internal 層)
|
|||
|
|
│
|
|||
|
|
├── TODO-5: SSE 整合測試
|
|||
|
|
├── TODO-6: SSE Handler 實作
|
|||
|
|
├── TODO-4: import 循環依賴檢測
|
|||
|
|
│
|
|||
|
|
▼
|
|||
|
|
Phase 9 (CLI 工具)
|
|||
|
|
│
|
|||
|
|
├── TODO-4: import 循環依賴檢測
|
|||
|
|
│
|
|||
|
|
▼
|
|||
|
|
Phase 10 (清理與測試)
|
|||
|
|
│
|
|||
|
|
├── TODO-7: 測試文件遷移
|
|||
|
|
├── TODO-9: 回歸測試自動化
|
|||
|
|
├── TODO-4: import 循環依賴檢測
|
|||
|
|
│
|
|||
|
|
▼
|
|||
|
|
完成
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
**文件版本**: v1.0
|
|||
|
|
**建立日期**: 2026-04-03
|
|||
|
|
**最後更新**: 2026-04-03
|