2.9 KiB
2.9 KiB
T511 — service-profile-api-get-put
Status:
done(2026-07-31)
Milestone:M1
Kind:feat
Est. change:~160 lines
Goal
完成後系統應:GET/PUT /api/v1/radar/service-profile 真實可用,欄位完整往返並以 owner_uid 隔離。
Depends on
- T500、T510
Inputs
- Spec:
../spec.md§4.1、§5.1、§5.6、§9.1(SP-02) - 既有 logic 模式:
apps/backend/internal/logic/scout/**
Outputs
程式變更(預期路徑)
| 路徑 | 動作 | 說明 |
|---|---|---|
apps/backend/generate/api/radar.api |
edit | service-profile req/resp type 定稿 |
apps/backend/internal/logic/radar/getServiceProfileLogic.go |
edit | 讀取;未建立回明確「尚未建立」狀態 |
apps/backend/internal/logic/radar/putServiceProfileLogic.go |
edit | 整份 upsert+驗證錯誤映射 |
apps/backend/internal/svc/servicecontext.go |
edit | 掛入 radar usecase |
行為變更
- 成功
102000;驗證失敗回明確 message(欄位名+原因),非空成功。 - 只能讀寫自己的檔案;
owner_uid一律取 JWT,request 帶的 uid 忽略。
API / 契約
GET回{ exists, services[], cases[], forbidden[], faq[], service_areas[], remote_ok, availability, tone_note, updated_at }。PUT回更新後全量。
Out of scope
- 前端畫面(T515)
- watch 前置條件檢查(T513)
Acceptance
- SP-02:填寫後
GET欄位完整、forbidden[]可讀 - 別人的 uid 無法讀寫(回越權錯誤)
- 指令:
cd apps/backend && make gen-api && go test ./internal/logic/radar/... -count=1 && make build
Notes(2026-07-31)
| 路徑 | 動作 |
|---|---|
internal/logic/radarmap/map.go |
add(domain ↔ types 映射,含 Pagination helper) |
internal/logic/radar/owner.go |
add(ownerUID:一律取 JWT,request 帶的 uid 忽略) |
internal/logic/radar/{get,upsert}_service_profile_logic.go |
edit |
internal/svc/service_context.go |
edit(掛 Radar *radarUC.Service) |
internal/middleware/auth.go |
edit(新增 WithUID,見下) |
internal/logic/radar/service_profile_logic_test.go |
add(5 則) |
generate/api/radar.api未改:T500 定的 req/resp 型別已足夠,不需要重跑契約。- 未建檔回
exists=false的 200,不是 404。 表單得先開得起來才有東西填;誠實之處在exists欄位,而不是用錯誤碼表達。空陣列而非null,前端不必每個欄位判空。 - 驗證錯誤映射成 400100,
cleanBizMessage會把radar validation:前綴切掉,所以前端拿到的是「services[0].name required」這種能對到欄位的訊息。 middleware.WithUID是新增的匯出入口。ctxUID是私有型別,沒有它就只能繞過UIDFrom來測,那測到的就不是真的授權路徑。測試證明別人的 uid 讀不到也寫不壞自己的檔案。