管理员可以看到所有会话了

This commit is contained in:
yml
2026-06-05 19:43:55 +08:00
parent 59093f8fd5
commit 92c603794a
5 changed files with 236 additions and 31 deletions
+156 -22
View File
@@ -149,6 +149,38 @@ func (r *Repository) ListConversations(principal Principal, page, pageSize int)
}
func (r *Repository) FindConversation(principal Principal, id uint64) (*ConversationDTO, error) {
// 管理员可以查看任意会话,无需是 participant
if principal.Type == "admin" {
var conversation model.ChatConversation
if err := r.db.First(&conversation, id).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, ErrConversationNotFound
}
return nil, err
}
participants, err := r.participants(conversation.ID)
if err != nil {
return nil, err
}
dto := ConversationDTO{
ID: conversation.ID,
OrderID: conversation.OrderID,
Type: conversation.Type,
Title: conversation.Title,
Status: conversation.Status,
Role: "admin", // 管理员角色
Participants: participants,
LastMessageID: conversation.LastMessageID,
LastMessagePreview: conversation.LastMessagePreview,
LastMessageAt: conversation.LastMessageAt,
UnreadCount: 0, // 管理员不计未读
CreatedAt: conversation.CreatedAt,
UpdatedAt: conversation.UpdatedAt,
}
return &dto, nil
}
// 普通用户需要是 participant
var row conversationRow
err := r.conversationQuery(principal).Where("c.id = ?", id).First(&row).Error
if err != nil {
@@ -269,9 +301,23 @@ func (r *Repository) EnsureSupportConversation(userID uint64) (*ConversationDTO,
func (r *Repository) Messages(principal Principal, conversationID uint64, page, pageSize int) (*PaginatedResult, error) {
page, pageSize = normalizePagination(page, pageSize)
if _, err := r.findParticipant(r.db, principal, conversationID, false); err != nil {
return nil, err
// 管理员可以查看任意会话的消息,普通用户需要是 participant
if principal.Type != "admin" {
if _, err := r.findParticipant(r.db, principal, conversationID, false); err != nil {
return nil, err
}
} else {
// 管理员需要验证会话存在
var count int64
if err := r.db.Model(&model.ChatConversation{}).Where("id = ?", conversationID).Count(&count).Error; err != nil {
return nil, err
}
if count == 0 {
return nil, ErrConversationNotFound
}
}
var total int64
if err := r.db.Model(&model.ChatMessage{}).Where("conversation_id = ?", conversationID).Count(&total).Error; err != nil {
return nil, err
@@ -295,10 +341,6 @@ func (r *Repository) Messages(principal Principal, conversationID uint64, page,
func (r *Repository) SendMessage(principal Principal, conversationID uint64, req SendMessageRequest) (*MessageDTO, error) {
var messageID uint64
err := r.db.Transaction(func(tx *gorm.DB) error {
participant, err := r.findParticipant(tx, principal, conversationID, true)
if err != nil {
return err
}
var conversation model.ChatConversation
if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).First(&conversation, conversationID).Error; err != nil {
return err
@@ -306,11 +348,41 @@ func (r *Repository) SendMessage(principal Principal, conversationID uint64, req
if conversation.Status != "active" {
return ErrPermissionDenied
}
var senderRole string
var participant *model.ChatParticipant
// 管理员可以在任意会话发送消息,无需是 participant
if principal.Type == "admin" {
// 尝试查找管理员的 participant 记录
var p model.ChatParticipant
err := tx.Where("conversation_id = ? AND participant_type = ? AND participant_id = ?",
conversationID, principal.Type, principal.ID).First(&p).Error
if err == nil {
// 管理员是 participant,使用其角色
participant = &p
senderRole = p.Role
} else if errors.Is(err, gorm.ErrRecordNotFound) {
// 管理员不是 participant,使用特殊角色 "admin"
senderRole = "admin"
} else {
return err
}
} else {
// 普通用户必须是 participant
p, err := r.findParticipant(tx, principal, conversationID, true)
if err != nil {
return err
}
participant = p
senderRole = p.Role
}
message := model.ChatMessage{
ConversationID: conversation.ID,
SenderType: principal.Type,
SenderID: principal.ID,
SenderRole: participant.Role,
SenderRole: senderRole,
ContentType: "text",
Content: req.Content,
AttachmentURLS: encodeStringList(req.AttachmentURLS),
@@ -324,10 +396,15 @@ func (r *Repository) SendMessage(principal Principal, conversationID uint64, req
if err := tx.Save(&conversation).Error; err != nil {
return err
}
now := time.Now()
if err := tx.Model(participant).Update("last_read_at", now).Error; err != nil {
return err
// 更新 participant 的已读时间(仅当是 participant 时)
if participant != nil {
now := time.Now()
if err := tx.Model(participant).Update("last_read_at", now).Error; err != nil {
return err
}
}
messageID = message.ID
return nil
})
@@ -370,6 +447,24 @@ func (r *Repository) SendMessage(principal Principal, conversationID uint64, req
func (r *Repository) MarkRead(principal Principal, conversationID uint64) error {
return r.db.Transaction(func(tx *gorm.DB) error {
// 管理员可以不是 participant,直接返回成功
if principal.Type == "admin" {
// 尝试查找 participant 记录,如果有就更新
var participant model.ChatParticipant
err := tx.Where("conversation_id = ? AND participant_type = ? AND participant_id = ?",
conversationID, principal.Type, principal.ID).First(&participant).Error
if err == nil {
// 有 participant 记录,更新已读时间
now := time.Now()
return tx.Model(&participant).Update("last_read_at", now).Error
} else if errors.Is(err, gorm.ErrRecordNotFound) {
// 没有 participant 记录,直接返回成功(管理员无需记录已读)
return nil
}
return err
}
// 普通用户必须是 participant
participant, err := r.findParticipant(tx, principal, conversationID, true)
if err != nil {
return err
@@ -475,6 +570,10 @@ func (r *Repository) toMessageDTOs(principal Principal, rows []model.ChatMessage
}
if row.SenderType == "admin" {
name = adminNames[row.SenderID]
// 如果角色是 "admin"(不是 participant 的管理员),在名字后添加标识
if row.SenderRole == "admin" {
name = name + " (管理员)"
}
}
items = append(items, MessageDTO{
ID: row.ID,
@@ -830,8 +929,50 @@ func (r *Repository) GetAvailableSupportAdmins() ([]SupportAdminDTO, error) {
// ListConversationsWithFilter 支持筛选的会话列表
func (r *Repository) ListConversationsWithFilter(principal Principal, page, pageSize int, filter string) (*PaginatedResult, error) {
page, pageSize = normalizePagination(page, pageSize)
var total int64
// 管理员在"全部"模式下直接查询所有会话
if principal.Type == "admin" && filter == "all" {
var total int64
if err := r.db.Model(&model.ChatConversation{}).Count(&total).Error; err != nil {
return nil, err
}
var conversations []model.ChatConversation
offset := (page - 1) * pageSize
if err := r.db.Order("COALESCE(last_message_at, created_at) DESC, id DESC").
Offset(offset).
Limit(pageSize).
Find(&conversations).Error; err != nil {
return nil, err
}
items := make([]ConversationDTO, 0, len(conversations))
for _, conv := range conversations {
participants, err := r.participants(conv.ID)
if err != nil {
return nil, err
}
items = append(items, ConversationDTO{
ID: conv.ID,
OrderID: conv.OrderID,
Type: conv.Type,
Title: conv.Title,
Status: conv.Status,
Role: "admin", // 管理员角色
Participants: participants,
LastMessageID: conv.LastMessageID,
LastMessagePreview: conv.LastMessagePreview,
LastMessageAt: conv.LastMessageAt,
UnreadCount: 0, // 管理员不计未读
CreatedAt: conv.CreatedAt,
UpdatedAt: conv.UpdatedAt,
})
}
return &PaginatedResult{Items: items, Total: total, Page: page, PageSize: pageSize}, nil
}
// 其他情况使用原有逻辑
var total int64
countDB := r.db.Table("chat_conversations AS c").
Joins("JOIN chat_participants AS cp ON cp.conversation_id = c.id")
@@ -844,12 +985,8 @@ func (r *Repository) ListConversationsWithFilter(principal Principal, page, page
countDB = countDB.Where("c.id NOT IN (?)",
r.db.Table("chat_participants").Select("conversation_id").Where("participant_type = ? AND role = ?", "admin", "support"))
default:
// 全部会话admin 可以看所有)
if principal.Type == "admin" {
// 管理员看所有会话
} else {
countDB = countDB.Where("cp.participant_type = ? AND cp.participant_id = ?", principal.Type, principal.ID)
}
// 普通用户的全部会话
countDB = countDB.Where("cp.participant_type = ? AND cp.participant_id = ?", principal.Type, principal.ID)
}
if err := countDB.Count(&total).Error; err != nil {
@@ -867,11 +1004,8 @@ func (r *Repository) ListConversationsWithFilter(principal Principal, page, page
queryDB = queryDB.Where("c.id NOT IN (?)",
r.db.Table("chat_participants").Select("conversation_id").Where("participant_type = ? AND role = ?", "admin", "support"))
default:
if principal.Type == "admin" {
// 管理员看所有会话
} else {
queryDB = queryDB.Where("cp.participant_type = ? AND cp.participant_id = ?", principal.Type, principal.ID)
}
// 普通用户的全部会话
queryDB = queryDB.Where("cp.participant_type = ? AND cp.participant_id = ?", principal.Type, principal.ID)
}
err := queryDB.