修正默认客服分配逻辑
This commit is contained in:
@@ -11,7 +11,6 @@ import (
|
||||
"hfb_sys/backend/internal/model"
|
||||
"hfb_sys/backend/internal/modules/chathub"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"gorm.io/datatypes"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/clause"
|
||||
@@ -23,9 +22,7 @@ type Repository struct {
|
||||
}
|
||||
|
||||
const (
|
||||
defaultSupportUsername = "admin"
|
||||
defaultSupportPassword = "admin123456"
|
||||
defaultSupportNickname = "超级管理员"
|
||||
defaultSupportRoleCode = "cs"
|
||||
)
|
||||
|
||||
func NewRepository(db *gorm.DB, hub *chathub.Hub) *Repository {
|
||||
@@ -689,21 +686,16 @@ func (row conversationRow) toDTO(participants []ParticipantDTO) ConversationDTO
|
||||
}
|
||||
|
||||
func defaultSupportAdminID(tx *gorm.DB) uint64 {
|
||||
// 查询所有有 chat:view 权限且状态为 active 的管理员
|
||||
var adminIDs []uint64
|
||||
err := tx.Table("admin_users AS au").
|
||||
Joins("JOIN admin_user_roles AS aur ON aur.admin_user_id = au.id").
|
||||
Joins("JOIN role_permissions AS rp ON rp.role_id = aur.role_id").
|
||||
Joins("JOIN permissions AS p ON p.id = rp.permission_id").
|
||||
Where("au.status = ? AND p.code = ?", "active", "chat:view").
|
||||
Distinct("au.id").
|
||||
Pluck("au.id", &adminIDs).Error
|
||||
if err != nil || len(adminIDs) == 0 {
|
||||
// 回退到原有逻辑
|
||||
return fallbackSupportAdminID(tx)
|
||||
if supportID := configuredDefaultSupportAdminID(tx); supportID > 0 {
|
||||
return supportID
|
||||
}
|
||||
|
||||
// 统计每个客服当前负责的会话数,选择负载最少的
|
||||
adminIDs, err := supportAdminIDs(tx)
|
||||
if err != nil || len(adminIDs) == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// 未配置默认客服时,按当前会话负载选择客服角色中最空闲的一位。
|
||||
type adminLoad struct {
|
||||
AdminID uint64
|
||||
Count int64
|
||||
@@ -733,48 +725,38 @@ func defaultSupportAdminID(tx *gorm.DB) uint64 {
|
||||
if selectedID > 0 {
|
||||
return selectedID
|
||||
}
|
||||
return fallbackSupportAdminID(tx)
|
||||
return 0
|
||||
}
|
||||
|
||||
func fallbackSupportAdminID(tx *gorm.DB) uint64 {
|
||||
func configuredDefaultSupportAdminID(tx *gorm.DB) uint64 {
|
||||
var cfg model.SystemConfig
|
||||
if err := tx.Where("`key` = ?", "chat.default_support_admin_id").First(&cfg).Error; err == nil {
|
||||
id, parseErr := strconv.ParseUint(cfg.Value, 10, 64)
|
||||
if parseErr == nil && id > 0 && adminActive(tx, id) {
|
||||
if parseErr == nil && id > 0 && adminIsSupport(tx, id) {
|
||||
return id
|
||||
}
|
||||
}
|
||||
var admin model.AdminUser
|
||||
if err := tx.Where("status = ?", "active").Order("id ASC").First(&admin).Error; err != nil {
|
||||
return ensureDefaultSupportAdmin(tx)
|
||||
}
|
||||
return admin.ID
|
||||
return 0
|
||||
}
|
||||
|
||||
func ensureDefaultSupportAdmin(tx *gorm.DB) uint64 {
|
||||
var count int64
|
||||
if err := tx.Model(&model.AdminUser{}).Count(&count).Error; err != nil || count > 0 {
|
||||
return 0
|
||||
}
|
||||
hash, err := bcrypt.GenerateFromPassword([]byte(defaultSupportPassword), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
admin := model.AdminUser{
|
||||
Username: defaultSupportUsername,
|
||||
PasswordHash: string(hash),
|
||||
Nickname: defaultSupportNickname,
|
||||
Status: "active",
|
||||
}
|
||||
if err := tx.Create(&admin).Error; err != nil {
|
||||
return 0
|
||||
}
|
||||
return admin.ID
|
||||
func supportAdminIDs(tx *gorm.DB) ([]uint64, error) {
|
||||
var adminIDs []uint64
|
||||
err := tx.Table("admin_users AS au").
|
||||
Joins("JOIN admin_user_roles AS aur ON aur.admin_user_id = au.id").
|
||||
Joins("JOIN roles AS r ON r.id = aur.role_id").
|
||||
Where("au.status = ? AND r.code = ?", "active", defaultSupportRoleCode).
|
||||
Order("CASE au.support_status WHEN 'online' THEN 0 WHEN 'busy' THEN 1 ELSE 2 END, au.id ASC").
|
||||
Pluck("au.id", &adminIDs).Error
|
||||
return adminIDs, err
|
||||
}
|
||||
|
||||
func adminActive(tx *gorm.DB, id uint64) bool {
|
||||
func adminIsSupport(tx *gorm.DB, id uint64) bool {
|
||||
var count int64
|
||||
if err := tx.Model(&model.AdminUser{}).Where("id = ? AND status = ?", id, "active").Count(&count).Error; err != nil {
|
||||
if err := tx.Table("admin_users AS au").
|
||||
Joins("JOIN admin_user_roles AS aur ON aur.admin_user_id = au.id").
|
||||
Joins("JOIN roles AS r ON r.id = aur.role_id").
|
||||
Where("au.id = ? AND au.status = ? AND r.code = ?", id, "active", defaultSupportRoleCode).
|
||||
Count(&count).Error; err != nil {
|
||||
return false
|
||||
}
|
||||
return count > 0
|
||||
@@ -826,9 +808,9 @@ func (r *Repository) TransferConversation(principal Principal, conversationID ui
|
||||
if _, err := r.findParticipant(tx, principal, conversationID, false); err != nil {
|
||||
return err
|
||||
}
|
||||
// 验证目标客服存在且活跃
|
||||
if !adminActive(tx, toAdminID) {
|
||||
return fmt.Errorf("目标客服不存在或已禁用")
|
||||
// 验证目标客服存在、活跃且拥有客服角色,避免转接给超级管理员。
|
||||
if !adminIsSupport(tx, toAdminID) {
|
||||
return fmt.Errorf("目标客服不存在、已禁用或不是客服角色")
|
||||
}
|
||||
// 检查目标客服是否已有该会话
|
||||
var count int64
|
||||
@@ -874,7 +856,7 @@ func (r *Repository) TransferConversation(principal Principal, conversationID ui
|
||||
|
||||
// GetAvailableSupportAdmins 获取可用客服列表及其会话数
|
||||
func (r *Repository) GetAvailableSupportAdmins() ([]SupportAdminDTO, error) {
|
||||
// 查询所有有 chat:view 权限且状态为 active 的管理员
|
||||
// 仅展示客服角色管理员,超级管理员即使有 chat:view 权限也不作为客服候选。
|
||||
type adminRow struct {
|
||||
ID uint64
|
||||
Nickname string
|
||||
@@ -882,11 +864,11 @@ func (r *Repository) GetAvailableSupportAdmins() ([]SupportAdminDTO, error) {
|
||||
}
|
||||
var admins []adminRow
|
||||
err := r.db.Table("admin_users AS au").
|
||||
Select("DISTINCT au.id, COALESCE(NULLIF(au.nickname, ''), au.username) AS nickname, au.support_status").
|
||||
Select("au.id, COALESCE(NULLIF(au.nickname, ''), au.username) AS nickname, au.support_status").
|
||||
Joins("JOIN admin_user_roles AS aur ON aur.admin_user_id = au.id").
|
||||
Joins("JOIN role_permissions AS rp ON rp.role_id = aur.role_id").
|
||||
Joins("JOIN permissions AS p ON p.id = rp.permission_id").
|
||||
Where("au.status = ? AND p.code = ?", "active", "chat:view").
|
||||
Joins("JOIN roles AS r ON r.id = aur.role_id").
|
||||
Where("au.status = ? AND r.code = ?", "active", defaultSupportRoleCode).
|
||||
Order("CASE au.support_status WHEN 'online' THEN 0 WHEN 'busy' THEN 1 ELSE 2 END, au.id ASC").
|
||||
Scan(&admins).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
Reference in New Issue
Block a user