feat(inboxes): persist chatwoot working hours

This commit is contained in:
2026-06-05 05:19:36 +08:00
parent ff98202c38
commit 945782eb03
8 changed files with 347 additions and 75 deletions
+13 -4
View File
@@ -27,7 +27,7 @@ func (r *InboxRepo) DB() *gorm.DB { return r.db }
// FindByID retrieves an inbox by primary key.
func (r *InboxRepo) FindByID(ctx context.Context, id uint) (*model.Inbox, error) {
var inbox model.Inbox
err := r.db.WithContext(ctx).First(&inbox, id).Error
err := r.withWorkingHours(r.db.WithContext(ctx)).First(&inbox, id).Error
if err != nil {
return nil, err
}
@@ -44,7 +44,7 @@ func (r *InboxRepo) FindByAccount(ctx context.Context, accountID uint, offset, l
return nil, 0, err
}
err := r.db.WithContext(ctx).Where("account_id = ?", accountID).
err := r.withWorkingHours(r.db.WithContext(ctx)).Where("account_id = ?", accountID).
Offset(offset).Limit(limit).Order("id DESC").
Find(&inboxes).Error
return inboxes, total, err
@@ -57,7 +57,7 @@ func (r *InboxRepo) Create(ctx context.Context, inbox *model.Inbox) error {
// Update modifies an existing inbox.
func (r *InboxRepo) Update(ctx context.Context, inbox *model.Inbox) error {
return r.db.WithContext(ctx).Save(inbox).Error
return r.db.WithContext(ctx).Omit("WorkingHours").Save(inbox).Error
}
// Delete soft-deletes an inbox.
@@ -68,13 +68,22 @@ func (r *InboxRepo) Delete(ctx context.Context, id uint) error {
// FindByAccountAndID retrieves an inbox scoped to an account.
func (r *InboxRepo) FindByAccountAndID(ctx context.Context, accountID, id uint) (*model.Inbox, error) {
var inbox model.Inbox
err := r.db.WithContext(ctx).Where("account_id = ? AND id = ?", accountID, id).First(&inbox).Error
err := r.withWorkingHours(r.db.WithContext(ctx)).Where("account_id = ? AND id = ?", accountID, id).First(&inbox).Error
if err != nil {
return nil, err
}
return &inbox, nil
}
func (r *InboxRepo) withWorkingHours(db *gorm.DB) *gorm.DB {
if r == nil || r.db == nil || !r.db.Migrator().HasTable(&model.WorkingHour{}) {
return db
}
return db.Preload("WorkingHours", func(tx *gorm.DB) *gorm.DB {
return tx.Order("day_of_week ASC")
})
}
// CountByAccount returns total number of inboxes in an account.
func (r *InboxRepo) CountByAccount(ctx context.Context, accountID uint) (int64, error) {
var total int64
+66 -2
View File
@@ -2,6 +2,8 @@ package repository
import (
"context"
"encoding/json"
"strconv"
"github.com/gochat/gochat/internal/model"
"gorm.io/gorm"
@@ -52,7 +54,12 @@ func (r *WorkingHourRepo) CreateDefaultWorkingHours(ctx context.Context, inboxID
{InboxID: inboxID, AccountID: accountID, DayOfWeek: 5, OpenHour: &nine, OpenMinutes: &zero, CloseHour: &seventeen, CloseMinutes: &zero, OpenAllDay: false},
{InboxID: inboxID, AccountID: accountID, DayOfWeek: 6, ClosedAllDay: true, OpenAllDay: false},
}
return r.db.WithContext(ctx).Create(&defaults).Error
for i := range defaults {
if err := r.db.WithContext(ctx).Create(&defaults[i]).Error; err != nil {
return err
}
}
return nil
}
// UpdateWorkingHours bulk-updates the weekly schedule for an inbox.
@@ -93,4 +100,61 @@ type WorkingHourUpdateParam struct {
OpenMinutes *int `json:"open_minutes"`
CloseHour *int `json:"close_hour"`
CloseMinutes *int `json:"close_minutes"`
}
}
func (p *WorkingHourUpdateParam) UnmarshalJSON(data []byte) error {
var raw map[string]interface{}
if err := json.Unmarshal(data, &raw); err != nil {
return err
}
p.DayOfWeek = intFromAny(raw["day_of_week"])
p.ClosedAllDay = boolFromAny(raw["closed_all_day"])
p.OpenAllDay = boolFromAny(raw["open_all_day"])
p.OpenHour = intPtrFromAny(raw["open_hour"])
p.OpenMinutes = intPtrFromAny(raw["open_minutes"])
p.CloseHour = intPtrFromAny(raw["close_hour"])
p.CloseMinutes = intPtrFromAny(raw["close_minutes"])
return nil
}
func intFromAny(value interface{}) int {
if ptr := intPtrFromAny(value); ptr != nil {
return *ptr
}
return 0
}
func intPtrFromAny(value interface{}) *int {
switch typed := value.(type) {
case nil:
return nil
case float64:
v := int(typed)
return &v
case int:
return &typed
case string:
if typed == "" || typed == "null" {
return nil
}
parsed, err := strconv.Atoi(typed)
if err != nil {
return nil
}
return &parsed
default:
return nil
}
}
func boolFromAny(value interface{}) bool {
switch typed := value.(type) {
case bool:
return typed
case string:
parsed, _ := strconv.ParseBool(typed)
return parsed
default:
return false
}
}