# T527 — job-radar-sweep-template-daily-schedule > Status: `done` > Milestone: `M2` > Kind: `feat` > Est. change: `~190 lines` ## Goal 完成後系統應:每日 **UTC 22:00** 為每個 active watch 建立一筆 `radar_sweep` Job,多台 worker 只有一台建立與執行。 ## Depends on - T512、T526 ## Inputs - Spec:`../spec.md` §4.2.3、§5.4 - 既有:`apps/backend/internal/module/job/domain/job.go`(template 常數)、`internal/module/job/usecase/service.go`、`cmd/worker/main.go`(maintenance tick+`redislock`,lock value = `workerID`) ## Outputs ### 程式變更(預期路徑) | 路徑 | 動作 | 說明 | |------|------|------| | `apps/backend/internal/module/job/domain/job.go` | edit | 新增 `TemplateRadarSweep = "radar_sweep"` | | `apps/backend/internal/module/job/usecase/service.go` | edit | `ScheduleRadarSweep(owner, watchID, runAt)`;guarded 狀態更新沿用 | | `apps/backend/internal/module/radar/usecase/sweep_schedule.go` | add | 每日排程 tick:判斷是否已過當日 UTC 22:00 且未建立過,逐一 active watch 建 Job | | `apps/backend/cmd/worker/main.go` | edit | `case jobDomain.TemplateRadarSweep` 分派;maintenance 內呼叫每日排程 tick | ### 行為變更 - 同一 UTC 日、同一 watch 只建立一筆排程 Job(以當日標記或 Job 唯一性防重)。 - 排程與執行皆在既有 Redis lock 保護下,lock value = `workerID`;未取得鎖者跳過不報錯。 - 手動觸發(T533)走**同一條執行路徑**,不另寫一份。 ## Out of scope - 抓取實作(T528)、判定(T529/T530) - 個人化時區(P2,不做) ## Acceptance - [x] SW-01:2 個 active watch → 排程後各一筆 `radar_sweep` Job - [x] 兩個 worker 實例同時跑,Job 不重複建立、不重複執行(RefID=`watchID:YYYY-MM-DD` 去重+maintenance Redis lock;並行 schedule 測試) - [x] 指令: ```bash cd apps/backend && go test ./internal/module/radar/... ./internal/module/job/... -count=1 && make build ``` ## Notes 排程時間為固定 UTC 22:00(台北 06:00),使用者早晨打開就有名單。 ### 實作落點 - `job/domain`:`TemplateRadarSweep` - `job/usecase`:`ScheduleRadarSweep`+`RadarSweepPayload`(同 watch+日去重) - `radar/usecase/sweep_schedule.go`:`ScheduleDailySweeps`(UTC 22:00 閘)、`BeginSweepRecord` - `radar` repo:`ListAllActiveWatches` - `cmd/worker`:template 分派+maintenance tick 呼叫排程;執行體暫明確 fail「fetch not wired (T528)」並寫 Sweep 紀錄