# T500 — api-shells-radar-crm-goctl > Status: `done`(2026-07-31) > Milestone: `M0` > Kind: `feat` > Est. change: `~180 lines` ## Goal 完成後系統應:`radar` 與 `crm` 兩個 group 的路由由 goctl 產出並可打通,所有尚未實作的能力回**明確未就緒錯誤**(禁止 `102000` 空成功)。 ## Depends on - — ## Inputs - Spec:`../spec.md` §5.1、§5.2、§5.6 - 既有契約檔範例:`apps/backend/generate/api/growth.api`、`scout` 相關段落 - `AGENTS.md`:後端一律 goctl,禁止手寫 handler/routes ## Outputs ### 程式變更(預期路徑) | 路徑 | 動作 | 說明 | |------|------|------| | `apps/backend/generate/api/radar.api` | add | service-profile/watches/today/opportunities/sweeps 全部 route 與 type | | `apps/backend/generate/api/crm.api` | add | contacts/followups/stats 全部 route 與 type | | `apps/backend/generate/api/gateway.api` | edit | import 兩個新契約檔 | | `apps/backend/internal/handler/**`、`routes.go` | generated | `make gen-api` 產出,**不手寫** | | `apps/backend/internal/logic/radar/**` | add | logic 殼,一律回未就緒錯誤 | | `apps/backend/internal/logic/crm/**` | add | logic 殼,一律回未就緒錯誤 | ### 行為變更 - 新路由存在且需 Bearer JWT,`owner_uid` 取登入 uid。 - 未實作能力回明確 error code/message(例:`radar capability not ready`),**不是** `102000` + 空 `data`。 ### API / 契約 - 依 spec §5.2 全表建立 route;`P1` 的 `/api/v1/radar/import` 先不建。 - 列表型 request 一律 `page`/`pageSize`(`pageSize ≤ 50`),response `pagination` + `list`。 - 時間欄位一律 `int64` unix nanoseconds。 ## Out of scope - 任何業務邏輯與 Mongo 讀寫(T510 起) - Mongo 索引(T501) - 前端接線(T502) ## Acceptance - [x] `make gen-api` 後 `git status` 顯示 handler/routes 為產生物,非手改 - [x] 每個新 route 回未就緒錯誤且**非** `102000`(以 34 個 capability 測試取代 curl,斷言 HTTP 501+`code≠102000`+`data=nil`) - [x] 指令: ```bash cd apps/backend && make gen-api && make build && go test ./... -count=1 ``` ## Notes route 命名避免與既有 `/api/v1/scout` 衝突;`promote` 掛在既有 scout group(T600)。 ### 實作結果(2026-07-31) - 新增 34 個 route:`radar` 20、`crm` 14;`internal/handler/{radar,crm}` 與 `routes.go` 全為 goctl 產出。 - 未就緒語意:`radarDomain.ErrNotReady`/`crmDomain.ErrNotReady` → `internal/response` 映射 **HTTP 501 / code 501010**;各 logic 以 `notReady("")` 標明是哪個能力。 - 一併定義兩個 module 的 `ErrNotFound`/`ErrForbidden`/`ErrValidation`(`400100`)sentinel 與映射,供 M1 起直接使用,避免每則 task 重複改 `response.go`。 - **超出 spec §5.2 表格的兩個 route**(刻意加入,非漏審): - `GET /api/v1/radar/opportunities/:id/replies` — RP-01「查 replies 只有 public_comment」需要讀取端點。 - `POST /api/v1/crm/contacts/:id/unmerge` — spec §4.6.3 與 T571 都要求 unmerge。 - 送出回覆端點**未**建立,依原計畫留給 T550。 - `routes.go` 另有一處無害變動:兩個同前綴 `/api/v1/media` 區塊的輸出順序被 goctl 互換,`rest.WithJwt` 仍附在 `generate-image` 區塊,全檔 19 處計數不變。