feat: 添加客服在线状态和聊天粘贴图片功能
**客服在线状态:** - 后端:添加 admin_users.support_status 字段(online/offline/busy) - 接口:PUT /admin/me/support-status 更新状态 - 前端:管理员顶部导航显示状态切换器(仅客服权限可见) - 转接对话框显示客服在线状态和智能排序(在线优先) **聊天粘贴图片:** - 用户端、移动端、管理端聊天输入框支持 Ctrl+V/Cmd+V 直接粘贴图片 - 自动调用图片压缩和上传逻辑,与文件选择上传保持一致 - 最多支持粘贴9张图片 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -7,13 +7,14 @@ import (
|
||||
)
|
||||
|
||||
type AdminDTO struct {
|
||||
ID uint64 `json:"id"`
|
||||
Username string `json:"username"`
|
||||
Nickname string `json:"nickname"`
|
||||
Status string `json:"status"`
|
||||
Roles []RoleDTO `json:"roles"`
|
||||
Permissions []string `json:"permissions"`
|
||||
LastLoginAt *time.Time `json:"last_login_at"`
|
||||
ID uint64 `json:"id"`
|
||||
Username string `json:"username"`
|
||||
Nickname string `json:"nickname"`
|
||||
Status string `json:"status"`
|
||||
SupportStatus string `json:"support_status"`
|
||||
Roles []RoleDTO `json:"roles"`
|
||||
Permissions []string `json:"permissions"`
|
||||
LastLoginAt *time.Time `json:"last_login_at"`
|
||||
}
|
||||
|
||||
type RoleDTO struct {
|
||||
@@ -39,3 +40,7 @@ type CaptchaDTO struct {
|
||||
Image string `json:"image"`
|
||||
ExpiresIn int64 `json:"expires_in"`
|
||||
}
|
||||
|
||||
type UpdateSupportStatusRequest struct {
|
||||
Status string `json:"status" binding:"required,oneof=online offline busy"`
|
||||
}
|
||||
|
||||
@@ -65,6 +65,33 @@ func (h *Handler) Logout(c *gin.Context) {
|
||||
response.OK(c, gin.H{"logged_out": true})
|
||||
}
|
||||
|
||||
func (h *Handler) UpdateSupportStatus(c *gin.Context) {
|
||||
adminID, ok := currentAdminID(c)
|
||||
if !ok {
|
||||
response.Unauthorized(c, "缺少管理员上下文")
|
||||
return
|
||||
}
|
||||
var req UpdateSupportStatusRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.BadRequest(c, "状态值无效,必须是 online、offline 或 busy")
|
||||
return
|
||||
}
|
||||
if err := h.service.UpdateSupportStatus(adminID, req.Status); err != nil {
|
||||
writeAdminAuthError(c, err)
|
||||
return
|
||||
}
|
||||
response.OK(c, gin.H{"updated": true, "support_status": req.Status})
|
||||
}
|
||||
|
||||
func currentAdminID(c *gin.Context) (uint64, bool) {
|
||||
value, ok := c.Get(middleware.ContextAdminID)
|
||||
if !ok {
|
||||
return 0, false
|
||||
}
|
||||
adminID, ok := value.(uint64)
|
||||
return adminID, ok
|
||||
}
|
||||
|
||||
type AdminRefreshRequest struct {
|
||||
RefreshToken string `json:"refresh_token" binding:"required"`
|
||||
}
|
||||
|
||||
@@ -126,6 +126,18 @@ func (r *Repository) FindByID(id uint64) (*AdminDTO, error) {
|
||||
return &dto, nil
|
||||
}
|
||||
|
||||
func (r *Repository) UpdateSupportStatus(adminID uint64, status string) error {
|
||||
if r.db == nil {
|
||||
return ErrDependencyUnavailable
|
||||
}
|
||||
if status != "online" && status != "offline" && status != "busy" {
|
||||
return errors.New("invalid support status")
|
||||
}
|
||||
return r.db.Model(&model.AdminUser{}).
|
||||
Where("id = ?", adminID).
|
||||
Update("support_status", status).Error
|
||||
}
|
||||
|
||||
func (r *Repository) ensureDefaultAdmin() error {
|
||||
var count int64
|
||||
if err := r.db.Model(&model.AdminUser{}).Count(&count).Error; err != nil {
|
||||
@@ -162,11 +174,12 @@ func (r *Repository) ensureDefaultAdmin() error {
|
||||
|
||||
func toDTO(admin model.AdminUser) AdminDTO {
|
||||
return AdminDTO{
|
||||
ID: admin.ID,
|
||||
Username: admin.Username,
|
||||
Nickname: admin.Nickname,
|
||||
Status: admin.Status,
|
||||
LastLoginAt: admin.LastLoginAt,
|
||||
ID: admin.ID,
|
||||
Username: admin.Username,
|
||||
Nickname: admin.Nickname,
|
||||
Status: admin.Status,
|
||||
SupportStatus: admin.SupportStatus,
|
||||
LastLoginAt: admin.LastLoginAt,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -61,3 +61,10 @@ func (s *Service) Me(adminID uint64) (*AdminDTO, error) {
|
||||
}
|
||||
return s.repo.FindByID(adminID)
|
||||
}
|
||||
|
||||
func (s *Service) UpdateSupportStatus(adminID uint64, status string) error {
|
||||
if s.repo == nil {
|
||||
return ErrDependencyUnavailable
|
||||
}
|
||||
return s.repo.UpdateSupportStatus(adminID, status)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user