From 16237dcefc328e16e9e03833fa5af4576bb5f698 Mon Sep 17 00:00:00 2001 From: yml2213 Date: Tue, 25 Aug 2026 19:32:04 +0800 Subject: [PATCH] =?UTF-8?q?fix(chat):=20=E4=BF=AE=E5=A4=8D=E4=B8=80?= =?UTF-8?q?=E9=94=AE=E6=B8=85=E7=90=86=E6=9C=AA=E8=AF=BB=E5=A4=B1=E8=B4=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../internal/modules/chat/handler_admin.go | 13 ++++++++ backend/internal/modules/chat/message.go | 28 +++++++++++++++++ backend/internal/modules/chat/service.go | 10 +++++++ backend/internal/router/router.go | 1 + .../features/admin/views/AdminChatsView.vue | 30 +++++++++++++++++-- frontend/src/features/chats/api/chats.ts | 5 ++++ 6 files changed, 85 insertions(+), 2 deletions(-) diff --git a/backend/internal/modules/chat/handler_admin.go b/backend/internal/modules/chat/handler_admin.go index 20407a9..ab3a6a2 100644 --- a/backend/internal/modules/chat/handler_admin.go +++ b/backend/internal/modules/chat/handler_admin.go @@ -82,6 +82,19 @@ func (h *Handler) AdminMarkRead(c *gin.Context) { h.markRead(c, Principal{Type: "admin", ID: adminID}) } +func (h *Handler) AdminMarkAllRead(c *gin.Context) { + adminID, ok := currentAdminID(c) + if !ok { + response.Unauthorized(c, "缺少管理员上下文") + return + } + if err := h.service.MarkAllAdminConversationsRead(c.Request.Context(), adminID); err != nil { + writeChatError(c, err) + return + } + response.OK(c, gin.H{"read": true}) +} + func (h *Handler) AdminTransfer(c *gin.Context) { adminID, ok := currentAdminID(c) if !ok { diff --git a/backend/internal/modules/chat/message.go b/backend/internal/modules/chat/message.go index 015b51a..9d79938 100644 --- a/backend/internal/modules/chat/message.go +++ b/backend/internal/modules/chat/message.go @@ -239,3 +239,31 @@ func (r *Repository) MarkRead(ctx context.Context, principal Principal, conversa } return nil } + +// MarkAllAdminConversationsRead advances one admin's read cursor for every conversation in one transaction. +func (r *Repository) MarkAllAdminConversationsRead(ctx context.Context, adminID uint64) error { + now := time.Now() + return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { + // 先补齐状态行,再更新游标。拆成两步可兼容本地 MySQL/MariaDB 对 + // INSERT ... SELECT 同表读取并 ON DUPLICATE KEY UPDATE 的限制。 + if err := tx.Exec(` + INSERT INTO chat_admin_conversation_states + (conversation_id, admin_user_id, remark, last_read_message_id, last_read_at) + SELECT c.id, ?, COALESCE(existing.remark, ''), COALESCE(c.last_message_id, 0), ? + FROM chat_conversations AS c + LEFT JOIN chat_admin_conversation_states AS existing + ON existing.conversation_id = c.id AND existing.admin_user_id = ? + WHERE existing.id IS NULL`, adminID, now, adminID).Error; err != nil { + return err + } + return tx.Exec(` + UPDATE chat_admin_conversation_states AS cas + JOIN chat_conversations AS c ON c.id = cas.conversation_id + SET cas.last_read_message_id = c.last_message_id, + cas.last_read_at = ?, + cas.updated_at = CURRENT_TIMESTAMP + WHERE cas.admin_user_id = ? + AND c.last_message_id IS NOT NULL + AND cas.last_read_message_id < c.last_message_id`, now, adminID).Error + }) +} diff --git a/backend/internal/modules/chat/service.go b/backend/internal/modules/chat/service.go index 43fca18..f60ea05 100644 --- a/backend/internal/modules/chat/service.go +++ b/backend/internal/modules/chat/service.go @@ -131,6 +131,16 @@ func (s *Service) MarkRead(ctx context.Context, principal Principal, conversatio return s.repo.MarkRead(ctx, principal, conversationID) } +func (s *Service) MarkAllAdminConversationsRead(ctx context.Context, adminID uint64) error { + if s.repo == nil { + return ErrDependencyUnavailable + } + if adminID == 0 { + return ErrPermissionDenied + } + return s.repo.MarkAllAdminConversationsRead(ctx, adminID) +} + func (s *Service) TransferConversation(ctx context.Context, principal Principal, conversationID uint64, req TransferRequest) error { if s.repo == nil { return ErrDependencyUnavailable diff --git a/backend/internal/router/router.go b/backend/internal/router/router.go index 38fd854..e2dd0c4 100644 --- a/backend/internal/router/router.go +++ b/backend/internal/router/router.go @@ -727,6 +727,7 @@ func New(cfg config.Config, deps Dependencies, logger *zap.Logger) *gin.Engine { } adminRoutes.GET("/chats", requirePerm("chat:view"), chatHandler.AdminList) adminRoutes.GET("/chats/counts", requirePerm("chat:view"), chatHandler.AdminCounts) + adminRoutes.POST("/chats/read-all", requirePerm("chat:view"), chatHandler.AdminMarkAllRead) adminRoutes.GET("/chats/:id", requirePerm("chat:view"), chatHandler.AdminDetail) adminRoutes.GET("/chats/:id/messages", requirePerm("chat:view"), chatHandler.AdminMessages) adminRoutes.POST("/chats/:id/messages", requirePerm("chat:send"), chatHandler.AdminSend) diff --git a/frontend/src/features/admin/views/AdminChatsView.vue b/frontend/src/features/admin/views/AdminChatsView.vue index c505570..6fd57ad 100644 --- a/frontend/src/features/admin/views/AdminChatsView.vue +++ b/frontend/src/features/admin/views/AdminChatsView.vue @@ -1,8 +1,8 @@