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
+16
View File
@@ -7,6 +7,7 @@ import (
"strings"
"github.com/gin-gonic/gin"
"github.com/gochat/gochat/internal/repository"
"github.com/gochat/gochat/internal/service"
"github.com/gochat/gochat/pkg/response"
)
@@ -317,6 +318,8 @@ func applyCreateInboxForm(req *service.CreateInboxRequest, values map[string][]s
req.BusinessName = inboxStringPtr(value)
case "csat_config":
req.CsatConfig = inboxJSONObject(value)
case "working_hours":
req.WorkingHours = inboxWorkingHours(value)
}
}
}
@@ -369,6 +372,8 @@ func applyUpdateInboxForm(req *service.UpdateInboxRequest, values map[string][]s
req.BusinessName = inboxStringPtr(value)
case "csat_config":
req.CsatConfig = inboxJSONObject(value)
case "working_hours":
req.WorkingHours = inboxWorkingHours(value)
}
}
}
@@ -443,6 +448,17 @@ func inboxJSONObject(value string) map[string]any {
return data
}
func inboxWorkingHours(value string) []repository.WorkingHourUpdateParam {
if value == "" {
return nil
}
var data []repository.WorkingHourUpdateParam
if err := json.Unmarshal([]byte(value), &data); err != nil {
return nil
}
return data
}
// ========================================
// Member-action handlers (Chatwoot InboxesController member routes)
// ========================================
@@ -33,7 +33,7 @@ func TestInboxHandler_ChatwootSerializerParity(t *testing.T) {
_ = sqlDB.Close()
}
})
require.NoError(t, db.AutoMigrate(&model.Account{}, &model.Inbox{}))
require.NoError(t, db.AutoMigrate(&model.Account{}, &model.Inbox{}, &model.WorkingHour{}))
account := &model.Account{Name: "Inbox Parity", Locale: "en", Active: true}
require.NoError(t, db.Create(account).Error)
@@ -123,7 +123,7 @@ func TestInboxHandler_ChatwootCreateUpdateRequestBinding(t *testing.T) {
_ = sqlDB.Close()
}
})
require.NoError(t, db.AutoMigrate(&model.Account{}, &model.Inbox{}))
require.NoError(t, db.AutoMigrate(&model.Account{}, &model.Inbox{}, &model.WorkingHour{}))
account := &model.Account{Name: "Inbox Binding", Locale: "en", Active: true}
require.NoError(t, db.Create(account).Error)
@@ -206,6 +206,41 @@ func TestInboxHandler_ChatwootCreateUpdateRequestBinding(t *testing.T) {
require.Equal(t, "Rate this conversation", csatConfig["message"])
require.Equal(t, "Please rate us", csatConfig["button_text"])
require.ElementsMatch(t, []any{"vip", "priority"}, csatConfig["survey_rules"].(map[string]any)["values"].([]any))
workingHoursUpdate := inboxParityRequest(t, router, http.MethodPut, fmt.Sprintf("/api/v1/accounts/%d/inboxes/%d", account.ID, websiteID), map[string]any{
"working_hours_enabled": true,
"out_of_office_message": "We are away",
"timezone": "Asia/Shanghai",
"working_hours": []map[string]any{
{"day_of_week": 0, "closed_all_day": true, "open_hour": "", "open_minutes": "", "close_hour": "", "close_minutes": "", "open_all_day": false},
{"day_of_week": 1, "closed_all_day": false, "open_hour": "9", "open_minutes": "30", "close_hour": "18", "close_minutes": "0", "open_all_day": false},
{"day_of_week": 2, "closed_all_day": false, "open_hour": "", "open_minutes": "", "close_hour": "", "close_minutes": "", "open_all_day": true},
{"day_of_week": 3, "closed_all_day": true, "open_hour": "", "open_minutes": "", "close_hour": "", "close_minutes": "", "open_all_day": false},
{"day_of_week": 4, "closed_all_day": true, "open_hour": "", "open_minutes": "", "close_hour": "", "close_minutes": "", "open_all_day": false},
{"day_of_week": 5, "closed_all_day": true, "open_hour": "", "open_minutes": "", "close_hour": "", "close_minutes": "", "open_all_day": false},
{"day_of_week": 6, "closed_all_day": true, "open_hour": "", "open_minutes": "", "close_hour": "", "close_minutes": "", "open_all_day": false},
},
"channel": map[string]any{},
})
require.Equal(t, http.StatusOK, workingHoursUpdate.Code, workingHoursUpdate.Body.String())
workingHoursData := inboxParityObject(t, workingHoursUpdate)
require.Equal(t, true, workingHoursData["working_hours_enabled"])
require.Equal(t, "We are away", workingHoursData["out_of_office_message"])
require.Equal(t, "Asia/Shanghai", workingHoursData["timezone"])
workingHours := workingHoursData["working_hours"].([]any)
require.Len(t, workingHours, 7)
monday := workingHours[1].(map[string]any)
require.Equal(t, float64(1), monday["day_of_week"])
require.Equal(t, float64(9), monday["open_hour"])
require.Equal(t, float64(30), monday["open_minutes"])
tuesday := workingHours[2].(map[string]any)
require.Equal(t, true, tuesday["open_all_day"])
require.Equal(t, float64(0), tuesday["open_hour"])
require.Equal(t, float64(23), tuesday["close_hour"])
showUpdated := inboxParityRequest(t, router, http.MethodGet, fmt.Sprintf("/api/v1/accounts/%d/inboxes/%d", account.ID, websiteID), nil)
require.Equal(t, http.StatusOK, showUpdated.Code, showUpdated.Body.String())
require.Len(t, inboxParityObject(t, showUpdated)["working_hours"].([]any), 7)
}
func setupInboxParityRouter(db *gorm.DB) *gin.Engine {
+20 -1
View File
@@ -32,7 +32,7 @@ func serializeInbox(inbox *model.Inbox) map[string]any {
"enable_auto_assignment": inbox.EnableAutoAssignment,
"auto_assignment_config": configValue(config, "auto_assignment_config"),
"out_of_office_message": inbox.OutOfOfficeMessage,
"working_hours": configArray(config, "working_hours"),
"working_hours": serializeInboxWorkingHours(inbox, config),
"timezone": inbox.Timezone,
"callback_webhook_url": configValue(config, "callback_webhook_url"),
"allow_messages_after_resolved": inbox.AllowMessagesAfterResolved,
@@ -96,6 +96,25 @@ func serializeInbox(inbox *model.Inbox) map[string]any {
return payload
}
func serializeInboxWorkingHours(inbox *model.Inbox, config map[string]any) any {
if len(inbox.WorkingHours) == 0 {
return configArray(config, "working_hours")
}
payload := make([]any, 0, len(inbox.WorkingHours))
for _, wh := range inbox.WorkingHours {
payload = append(payload, map[string]any{
"day_of_week": wh.DayOfWeek,
"closed_all_day": wh.ClosedAllDay,
"open_hour": wh.OpenHour,
"open_minutes": wh.OpenMinutes,
"close_hour": wh.CloseHour,
"close_minutes": wh.CloseMinutes,
"open_all_day": wh.OpenAllDay,
})
}
return payload
}
func chatwootChannelType(channelType string) string {
if strings.HasPrefix(channelType, "Channel::") {
return channelType