thread-master/apps/backend/internal/module/radar/domain/schedule.go

93 lines
2.1 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package domain
import (
"fmt"
"sort"
"time"
)
const (
ScheduleTimezone = "Asia/Taipei"
DefaultSweepHour = 6
MaxSweepHours = 6
minSweepHour = 0
maxSweepHour = 23
)
// RadarSchedule is the owner's automatic patrol timetable (Taipei local hours).
type RadarSchedule struct {
OwnerUID int64 `bson:"_id" json:"owner_uid"`
Hours []int `bson:"hours" json:"hours"`
UpdatedAt int64 `bson:"updated_at" json:"updated_at"`
}
func TaipeiLocation() *time.Location {
loc, err := time.LoadLocation(ScheduleTimezone)
if err != nil {
return time.FixedZone(ScheduleTimezone, 8*3600)
}
return loc
}
func DefaultSweepHours() []int {
return []int{DefaultSweepHour}
}
func NormalizeSweepHours(hours []int) ([]int, error) {
if len(hours) == 0 {
return DefaultSweepHours(), nil
}
seen := map[int]bool{}
out := make([]int, 0, len(hours))
for _, h := range hours {
if h < minSweepHour || h > maxSweepHour {
return nil, fmt.Errorf("%w: hours must be 023 (got %d)", ErrValidation, h)
}
if seen[h] {
continue
}
seen[h] = true
out = append(out, h)
}
if len(out) == 0 {
return DefaultSweepHours(), nil
}
if len(out) > MaxSweepHours {
return nil, fmt.Errorf("%w: at most %d patrol hours", ErrValidation, MaxSweepHours)
}
sort.Ints(out)
return out, nil
}
// SweepSlot is one due automatic patrol (Taipei calendar day + hour).
type SweepSlot struct {
Date string
Hour int
RunAt int64
}
// DueSweepSlots returns selected hours that have already started today (Taipei)
// plus any earlier selected hours the same day, so a late worker still catches up.
func DueSweepSlots(now time.Time, hours []int) []SweepSlot {
hours, err := NormalizeSweepHours(hours)
if err != nil {
hours = DefaultSweepHours()
}
loc := TaipeiLocation()
local := now.In(loc)
day := time.Date(local.Year(), local.Month(), local.Day(), 0, 0, 0, 0, loc)
out := make([]SweepSlot, 0, len(hours))
for _, h := range hours {
slot := day.Add(time.Duration(h) * time.Hour)
if local.Before(slot) {
continue
}
out = append(out, SweepSlot{
Date: day.Format("2006-01-02"),
Hour: h,
RunAt: slot.UnixNano(),
})
}
return out
}