Files
gochat/backend/internal/model/working_hour.go
T
rogee aeddedf2a3 Reorganize repo: backend/, deploy/, docs/ layout + AGENTS.md
Restructure the monorepo into clear top-level directories:
- backend/: Go module root (cmd, internal, pkg, configs, migrations,
  docs/swagger, scripts, tests, go.mod, Makefile, .air.toml)
- deploy/: Docker (Dockerfile, docker-compose*), quickstart, fluentd
- docs/: project documentation + reports/ (moved from repo root)
- AGENTS.md: new AI coding-agent guide at repo root

Update all references to the new layout:
- Dockerfile: COPY backend/go.mod, COPY backend/ (context = repo root)
- docker-compose files: context ../.., dockerfile deploy/docker/Dockerfile,
  env_file ../../.env, volume mounts ../../backend:/app
- deploy/quickstart/compose.yaml: dockerfile deploy/docker/Dockerfile
- CI: working-directory: backend for go commands, file deploy/docker/Dockerfile,
  coverage path backend/coverage.out, health_check backend/scripts/
- backend/Makefile: docker target uses -f ../deploy/docker/Dockerfile ../
- README: architecture tree, quickstart, config paths updated

Move root stray scripts (rename_models.*, run_m11_tests.sh, verify_build.sh,
gorm_bool_main.go) to backend/scripts/legacy/. All moves via git mv to
preserve history. Build, vet, SQLite tests, and docker compose config verified.
2026-07-07 14:44:12 +08:00

103 lines
3.8 KiB
Go

package model
import "time"
// WorkingHour represents a single day's working hours configuration for an inbox.
// Reference: Chatwoot app/models/working_hour.rb
//
// Each inbox has 7 WorkingHour records (day_of_week 0-6), one per day.
// Default: weekdays 9:00-17:00, weekends closed_all_day.
//
// Table: working_hours
type WorkingHour struct {
ID uint `gorm:"primaryKey" json:"id"`
InboxID uint `gorm:"not null;index" json:"inbox_id"`
AccountID uint `gorm:"index" json:"account_id"`
DayOfWeek int `gorm:"not null" json:"day_of_week"` // 0=Sunday, 1=Monday, ..., 6=Saturday
ClosedAllDay bool `gorm:"default:false" json:"closed_all_day"` // true → inbox closed entire day
OpenAllDay bool `gorm:"default:false" json:"open_all_day"` // true → inbox open 24h
OpenHour *int `gorm:"default:null" json:"open_hour"` // 0-23, nil when closed_all_day or open_all_day
OpenMinutes *int `gorm:"default:null" json:"open_minutes"` // 0-59
CloseHour *int `gorm:"default:null" json:"close_hour"` // 0-23
CloseMinutes *int `gorm:"default:null" json:"close_minutes"` // 0-59
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
// TableName returns the GORM table name.
func (WorkingHour) TableName() string { return "working_hours" }
// Validate performs Chatwoot-equivalent validations:
// - open_hour/close_hour must be 0-23 (unless closed_all_day)
// - open_minutes/close_minutes must be 0-59 (unless closed_all_day)
// - close time must be after open time (unless closed_all_day)
// - open_all_day and closed_all_day cannot both be true
func (wh *WorkingHour) Validate() map[string]string {
errors := make(map[string]string)
// Chatwoot: open_all_day_and_closed_all_day validation
if wh.OpenAllDay && wh.ClosedAllDay {
errors["open_all_day"] = "cannot be true when closed_all_day is also true"
}
if wh.ClosedAllDay {
return errors // no further validation needed
}
// Chatwoot: inclusion validations (0-23 for hours, 0-59 for minutes)
if wh.OpenHour != nil {
if *wh.OpenHour < 0 || *wh.OpenHour > 23 {
errors["open_hour"] = "must be between 0 and 23"
}
} else if !wh.OpenAllDay {
errors["open_hour"] = "is required when not closed_all_day or open_all_day"
}
if wh.CloseHour != nil {
if *wh.CloseHour < 0 || *wh.CloseHour > 23 {
errors["close_hour"] = "must be between 0 and 23"
}
} else if !wh.OpenAllDay {
errors["close_hour"] = "is required when not closed_all_day or open_all_day"
}
if wh.OpenMinutes != nil {
if *wh.OpenMinutes < 0 || *wh.OpenMinutes > 59 {
errors["open_minutes"] = "must be between 0 and 59"
}
}
if wh.CloseMinutes != nil {
if *wh.CloseMinutes < 0 || *wh.CloseMinutes > 59 {
errors["close_minutes"] = "must be between 0 and 59"
}
}
// Chatwoot: close_after_open validation
// close time must be strictly after open time
if wh.OpenHour != nil && wh.CloseHour != nil && wh.OpenMinutes != nil && wh.CloseMinutes != nil {
openTotal := *wh.OpenHour * 60 + *wh.OpenMinutes
closeTotal := *wh.CloseHour * 60 + *wh.CloseMinutes
if closeTotal <= openTotal {
errors["close_hour"] = "close time must be after open time"
}
}
return errors
}
// EnsureOpenAllDayHours fills open/close fields when open_all_day is true.
// Chatwoot: before_validation :ensure_open_all_day_hours
// - When open_all_day: set open_hour=0, open_minutes=0, close_hour=23, close_minutes=59
func (wh *WorkingHour) EnsureOpenAllDayHours() {
if wh.OpenAllDay {
zero := 0
fiftyNine := 59
twentyThree := 23
wh.OpenHour = &zero
wh.OpenMinutes = &zero
wh.CloseHour = &twentyThree
wh.CloseMinutes = &fiftyNine
wh.ClosedAllDay = false
}
}