Files
yovision/Bell/server/app/bell/duty_schedule/validation.go
T

91 lines
2.9 KiB
Go

package duty_schedule
import (
"errors"
"sort"
"strings"
"time"
)
var (
ErrInvalid = errors.New("值班排班信息不符合要求")
ErrNotFound = errors.New("值班组或排班不存在")
ErrConflict = errors.New("数据已被其他人员更新,请刷新后重试")
ErrCoverage = errors.New("周排班存在空档或重叠")
)
type GroupInput struct {
Name string `json:"name"`
Timezone string `json:"timezone"`
ExpectedVersion int `json:"expectedVersion"`
}
type MemberInput struct {
ContactID string `json:"contactId"`
Role string `json:"role"`
}
type SlotInput struct {
Weekday int `json:"weekday"`
StartMinute int `json:"startMinute"`
EndMinute int `json:"endMinute"`
PrimaryContactID string `json:"primaryContactId"`
BackupContactID string `json:"backupContactId"`
}
type ScheduleInput struct {
EffectiveFrom time.Time `json:"effectiveFrom"`
EffectiveTo *time.Time `json:"effectiveTo"`
Slots []SlotInput `json:"slots"`
}
type OverrideInput struct {
OriginalContactID string `json:"originalContactId"`
ReplacementContactID string `json:"replacementContactId"`
StartsAt time.Time `json:"startsAt"`
EndsAt time.Time `json:"endsAt"`
Reason string `json:"reason"`
}
func normalizeGroup(input GroupInput) (GroupInput, error) {
input.Name = strings.TrimSpace(input.Name)
input.Timezone = strings.TrimSpace(input.Timezone)
if input.Name == "" || len([]rune(input.Name)) > 128 {
return GroupInput{}, ErrInvalid
}
if _, err := time.LoadLocation(input.Timezone); err != nil {
return GroupInput{}, ErrInvalid
}
return input, nil
}
func validateSlots(slots []SlotInput) error {
if len(slots) == 0 {
return ErrCoverage
}
byDay := map[int][]SlotInput{}
for _, slot := range slots {
if slot.Weekday < 0 || slot.Weekday > 6 || slot.StartMinute < 0 || slot.EndMinute > 1440 || slot.StartMinute >= slot.EndMinute || slot.PrimaryContactID == "" || slot.BackupContactID == "" || slot.PrimaryContactID == slot.BackupContactID {
return ErrInvalid
}
byDay[slot.Weekday] = append(byDay[slot.Weekday], slot)
}
for day := 0; day < 7; day++ {
daySlots := byDay[day]
sort.Slice(daySlots, func(i, j int) bool { return daySlots[i].StartMinute < daySlots[j].StartMinute })
cursor := 0
for _, slot := range daySlots {
if slot.StartMinute != cursor {
return ErrCoverage
}
cursor = slot.EndMinute
}
if cursor != 1440 {
return ErrCoverage
}
}
return nil
}
func normalizeOverride(input OverrideInput) (OverrideInput, error) {
input.Reason = strings.TrimSpace(input.Reason)
if input.OriginalContactID == "" || input.ReplacementContactID == "" || input.OriginalContactID == input.ReplacementContactID || input.StartsAt.IsZero() || !input.EndsAt.After(input.StartsAt) || input.Reason == "" || len([]rune(input.Reason)) > 256 {
return OverrideInput{}, ErrInvalid
}
return input, nil
}