72 lines
3.0 KiB
Markdown
72 lines
3.0 KiB
Markdown
|
|
# T510 — service-profile-model-repository
|
|||
|
|
|
|||
|
|
> Status: `done`(2026-07-31)
|
|||
|
|
> Milestone: `M1`
|
|||
|
|
> Kind: `feat`
|
|||
|
|
> Est. change: `~170 lines`
|
|||
|
|
|
|||
|
|
## Goal
|
|||
|
|
|
|||
|
|
完成後系統應:`radar` module 有 ServiceProfile domain 與 repository(mongo/memory),每會員一份可讀寫。
|
|||
|
|
|
|||
|
|
## Depends on
|
|||
|
|
|
|||
|
|
- T501
|
|||
|
|
|
|||
|
|
## Inputs
|
|||
|
|
|
|||
|
|
- Spec:`../spec.md` §2、§4.1、§8
|
|||
|
|
- 既有 module 模式:`apps/backend/internal/module/scout/{domain,repository,usecase}`
|
|||
|
|
|
|||
|
|
## Outputs
|
|||
|
|
|
|||
|
|
### 程式變更(預期路徑)
|
|||
|
|
|
|||
|
|
| 路徑 | 動作 | 說明 |
|
|||
|
|
|------|------|------|
|
|||
|
|
| `apps/backend/internal/module/radar/domain/service_profile.go` | add | struct+驗證:`services[]`(項目+價格區間)、`cases[]`、`forbidden[]`、`faq[]`、`service_areas[]`、`remote_ok`、`availability`、`tone_note` |
|
|||
|
|
| `apps/backend/internal/module/radar/repository/service_profile_mongo.go` | add | `radar_service_profiles`,`_id = owner_uid` upsert |
|
|||
|
|
| `apps/backend/internal/module/radar/repository/service_profile_memory.go` | add | 測試用 |
|
|||
|
|
| `apps/backend/internal/module/radar/usecase/service_profile.go` | add | Get/Upsert usecase |
|
|||
|
|
|
|||
|
|
### 行為變更
|
|||
|
|
|
|||
|
|
- `Upsert` 為整份覆寫(每會員一份),`updated_at` 為 ns UTC。
|
|||
|
|
- `Get` 未建立時回「不存在」語意,供上層判斷 SP-01 前置條件。
|
|||
|
|
- 驗證:價格區間下限 ≤ 上限;`service_areas[]` 為縣市代碼白名單;`forbidden[]` 去空白去重。
|
|||
|
|
|
|||
|
|
## Out of scope
|
|||
|
|
|
|||
|
|
- HTTP logic(T511)
|
|||
|
|
- 判定與回覆如何使用本檔(T529/T549)
|
|||
|
|
|
|||
|
|
## Acceptance
|
|||
|
|
|
|||
|
|
- [x] unit test:upsert → get 欄位完整、`forbidden[]` 可讀、價格區間反向被拒
|
|||
|
|
- [x] 指令:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
cd apps/backend && go test ./internal/module/radar/... -count=1
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## Notes
|
|||
|
|
|
|||
|
|
縣市代碼白名單放 `domain` 常數,不猜測、不做模糊比對(OP-04 行為靠這個約束)。
|
|||
|
|
|
|||
|
|
### 實作結果(2026-07-31)
|
|||
|
|
|
|||
|
|
| 路徑 | 動作 |
|
|||
|
|
|------|------|
|
|||
|
|
| `internal/module/radar/domain/service_profile.go` | add(含 22 個縣市代碼白名單、`Normalize`、`MatchesRegion`) |
|
|||
|
|
| `internal/module/radar/domain/repository.go` | add(`Repository` 介面) |
|
|||
|
|
| `internal/module/radar/repository/service_profile_{mongo,memory}.go` | add |
|
|||
|
|
| `internal/module/radar/usecase/service_profile.go` | add(Get/Has/Upsert) |
|
|||
|
|
| `internal/module/radar/usecase/service_profile_test.go` | add(9 則) |
|
|||
|
|
|
|||
|
|
### 幾個刻意的決定
|
|||
|
|
|
|||
|
|
- **`Get` 未建檔回 `ErrNotFound`,不回零值檔案。** 「沒建檔」與「建了一份空的」必須分得出來,SP-01 的訂閱閘門就是靠這個差別;HTTP 層才把它翻成 `exists=false` 的 200。
|
|||
|
|
- **價格只在兩端都填時比大小。** 只填 `price_min` 是「起價」、只填 `price_max` 是「上限」,都是真實的報價方式,硬要成對會逼使用者亂填。填了價格但沒填幣別時補 `TWD`。
|
|||
|
|
- **`service_areas` 空且 `remote_ok=false` 直接拒絕。** 兩者皆空時,五問判定的地區這一問沒有任何依據可用。
|
|||
|
|
- **`Upsert` 是整份覆寫,但保留 `created_at`。** 不做欄位合併,否則使用者永遠刪不掉一個服務項目或禁語。
|