package handler import ( "fmt" "kefu-sys/server/internal/model" "kefu-sys/server/internal/ws" ) const offlineLeavePrompt = "当前无客服在线,请留言并留下联系方式,我们上线后会尽快回复您。" // pickLeastLoadedAgent 在在线一线客服中选择当前进行中会话最少的一位。 func pickLeastLoadedAgent(tenantID uint) (*model.User, error) { var agents []model.User if err := model.DB.Where("tenant_id = ? AND role = ? AND status = ?", tenantID, "agent", "online"). Order("id asc").Find(&agents).Error; err != nil { return nil, err } if len(agents) == 0 { return nil, nil } best := &agents[0] bestCount := int64(-1) for i := range agents { var count int64 if err := model.DB.Model(&model.Session{}). Where("tenant_id = ? AND agent_id = ? AND status = ?", tenantID, agents[i].ID, "active"). Count(&count).Error; err != nil { return nil, err } if bestCount < 0 || count < bestCount { bestCount = count best = &agents[i] } } return best, nil } // countOnlineAgents 统计租户当前可接待的在线客服数。 func countOnlineAgents(tenantID uint) (int64, error) { var count int64 err := model.DB.Model(&model.User{}). Where("tenant_id = ? AND role = ? AND status = ?", tenantID, "agent", "online"). Count(&count).Error return count, err } // tryAutoAssign 将会话自动分配给负载最低的在线客服;无可分配客服时返回 nil。 func tryAutoAssign(session *model.Session) (*model.User, error) { if session == nil || session.ID == 0 { return nil, nil } if session.Status != "waiting" || session.AgentID != nil { return nil, nil } agent, err := pickLeastLoadedAgent(session.TenantID) if err != nil || agent == nil { return nil, err } result := model.DB.Model(&model.Session{}). Where("id = ? AND tenant_id = ? AND status = ? AND agent_id IS NULL", session.ID, session.TenantID, "waiting"). Updates(map[string]interface{}{ "agent_id": agent.ID, "status": "active", "last_read_seq": 0, }) if result.Error != nil { return nil, result.Error } if result.RowsAffected == 0 { // 并发下可能已被他人领取 if err := model.DB.First(session, session.ID).Error; err != nil { return nil, err } return nil, nil } session.AgentID = &agent.ID session.Status = "active" detail := fmt.Sprintf("系统自动分配给 %s", agent.Nickname) model.DB.Create(&model.SessionEvent{ SessionID: session.ID, OperatorID: 0, Action: "auto_assign", Detail: detail, }) if payload, err := ws.NewEvent("session_updated", session.ID, session); err == nil { ws.DefaultHub.BroadcastToTenantStaff(session.TenantID, payload) ws.DefaultHub.BroadcastToVisitor(session.TenantID, session.ID, payload) } return agent, nil }