支持可配置的会话自动分配策略

- 租户可设置负载最低或轮询,以及每位坐席最大进行中会话数
- 自动分配按策略过滤在线一线客服,满并发则保持排队
- 系统设置「客服分配规则」页可保存上述配置
This commit is contained in:
yml2213
2026-07-15 15:07:41 +08:00
parent 9251815695
commit 216ab2c85a
6 changed files with 323 additions and 59 deletions
+37 -11
View File
@@ -60,6 +60,8 @@ func getOrCreateTenantSettings(tenantID uint) (*model.TenantSetting, error) {
NotifyNewSession: true,
NotifyOfflineLeave: true,
NotifyDailyReport: false,
AssignStrategy: assignStrategyLeastLoad,
MaxActivePerAgent: 0,
}
if err := model.DB.Create(&setting).Error; err != nil {
return nil, err
@@ -135,18 +137,25 @@ func isWithinWorkHours(setting *model.TenantSetting, now time.Time) bool {
}
func settingsResponse(setting *model.TenantSetting) gin.H {
strategy := normalizeAssignStrategy(setting.AssignStrategy)
maxActive := setting.MaxActivePerAgent
if maxActive < 0 {
maxActive = 0
}
return gin.H{
"tenant_id": setting.TenantID,
"display_name": setting.DisplayName,
"agent_nickname": setting.AgentNickname,
"timezone": setting.Timezone,
"welcome_message": setting.WelcomeMessage,
"offline_prompt": setting.OfflinePrompt,
"work_hours": parseWorkHours(setting.WorkHoursJSON),
"worktime_prompt": setting.WorktimePrompt,
"notify_new_session": setting.NotifyNewSession,
"notify_offline_leave": setting.NotifyOfflineLeave,
"notify_daily_report": setting.NotifyDailyReport,
"tenant_id": setting.TenantID,
"display_name": setting.DisplayName,
"agent_nickname": setting.AgentNickname,
"timezone": setting.Timezone,
"welcome_message": setting.WelcomeMessage,
"offline_prompt": setting.OfflinePrompt,
"work_hours": parseWorkHours(setting.WorkHoursJSON),
"worktime_prompt": setting.WorktimePrompt,
"notify_new_session": setting.NotifyNewSession,
"notify_offline_leave": setting.NotifyOfflineLeave,
"notify_daily_report": setting.NotifyDailyReport,
"assign_strategy": strategy,
"max_active_per_agent": maxActive,
}
}
@@ -175,6 +184,8 @@ type updateSettingsReq struct {
NotifyNewSession *bool `json:"notify_new_session"`
NotifyOfflineLeave *bool `json:"notify_offline_leave"`
NotifyDailyReport *bool `json:"notify_daily_report"`
AssignStrategy *string `json:"assign_strategy"`
MaxActivePerAgent *int `json:"max_active_per_agent"`
}
func (h *SettingsHandler) Update(c *gin.Context) {
@@ -284,6 +295,21 @@ func (h *SettingsHandler) Update(c *gin.Context) {
if req.NotifyDailyReport != nil {
updates["notify_daily_report"] = *req.NotifyDailyReport
}
if req.AssignStrategy != nil {
raw := strings.TrimSpace(*req.AssignStrategy)
if raw != assignStrategyLeastLoad && raw != assignStrategyRoundRobin {
c.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": "分配策略无效,可选 least_load / round_robin"})
return
}
updates["assign_strategy"] = raw
}
if req.MaxActivePerAgent != nil {
if *req.MaxActivePerAgent < 0 || *req.MaxActivePerAgent > 200 {
c.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": "最大并发需为 02000 表示不限制)"})
return
}
updates["max_active_per_agent"] = *req.MaxActivePerAgent
}
if len(updates) == 0 {
c.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": "没有可更新字段"})
return