thread-master/docs/product/demand-radar/tasks/T510-service-profile-model-...

72 lines
3.0 KiB
Markdown
Raw Normal View History

2026-08-03 05:52:02 +00:00
# T510 — service-profile-model-repository
> Status: `done`2026-07-31
> Milestone: `M1`
> Kind: `feat`
> Est. change: `~170 lines`
## Goal
完成後系統應:`radar` module 有 ServiceProfile domain 與 repositorymongomemory每會員一份可讀寫。
## 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 | GetUpsert usecase |
### 行為變更
- `Upsert` 為整份覆寫(每會員一份),`updated_at` 為 ns UTC。
- `Get` 未建立時回「不存在」語意,供上層判斷 SP-01 前置條件。
- 驗證:價格區間下限 ≤ 上限;`service_areas[]` 為縣市代碼白名單;`forbidden[]` 去空白去重。
## Out of scope
- HTTP logicT511
- 判定與回覆如何使用本檔T529T549
## Acceptance
- [x] unit testupsert → 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` | addGetHasUpsert |
| `internal/module/radar/usecase/service_profile_test.go` | add9 則) |
### 幾個刻意的決定
- **`Get` 未建檔回 `ErrNotFound`,不回零值檔案。** 「沒建檔」與「建了一份空的」必須分得出來SP-01 的訂閱閘門就是靠這個差別HTTP 層才把它翻成 `exists=false` 的 200。
- **價格只在兩端都填時比大小。** 只填 `price_min` 是「起價」、只填 `price_max` 是「上限」,都是真實的報價方式,硬要成對會逼使用者亂填。填了價格但沒填幣別時補 `TWD`
- **`service_areas` 空且 `remote_ok=false` 直接拒絕。** 兩者皆空時,五問判定的地區這一問沒有任何依據可用。
- **`Upsert` 是整份覆寫,但保留 `created_at`。** 不做欄位合併,否則使用者永遠刪不掉一個服務項目或禁語。