conversation_repository.go 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. package repository
  2. import (
  3. "errors"
  4. "time"
  5. "github.com/2930134478/AI-CS/backend/models"
  6. "gorm.io/gorm"
  7. )
  8. // ConversationRepository 封装与会话相关的数据库操作。
  9. type ConversationRepository struct {
  10. db *gorm.DB
  11. }
  12. // NewConversationRepository 创建会话仓库实例。
  13. func NewConversationRepository(db *gorm.DB) *ConversationRepository {
  14. return &ConversationRepository{db: db}
  15. }
  16. // FindOpenByVisitorID 查询访客当前未关闭的会话(仅 visitor 类型)。
  17. func (r *ConversationRepository) FindOpenByVisitorID(visitorID uint) (*models.Conversation, error) {
  18. var conv models.Conversation
  19. err := r.db.Where("conversation_type = ? AND visitor_id = ? AND status != ?", "visitor", visitorID, "closed").
  20. Order("created_at desc").
  21. First(&conv).Error
  22. if err != nil {
  23. return nil, err
  24. }
  25. return &conv, nil
  26. }
  27. // ListActiveInternalByAgentID 返回某客服的全部未关闭内部对话(知识库测试用)。
  28. func (r *ConversationRepository) ListActiveInternalByAgentID(agentID uint) ([]models.Conversation, error) {
  29. var list []models.Conversation
  30. err := r.db.Where("conversation_type = ? AND agent_id = ? AND status != ?", "internal", agentID, "closed").
  31. Order("updated_at desc").
  32. Find(&list).Error
  33. if err != nil {
  34. return nil, err
  35. }
  36. return list, nil
  37. }
  38. // Create 创建新的会话记录。
  39. func (r *ConversationRepository) Create(conv *models.Conversation) error {
  40. return r.db.Create(conv).Error
  41. }
  42. // UpdateFields 更新会话的指定字段。
  43. func (r *ConversationRepository) UpdateFields(id uint, values map[string]interface{}) error {
  44. if len(values) == 0 {
  45. return nil
  46. }
  47. return r.db.Model(&models.Conversation{}).Where("id = ?", id).Updates(values).Error
  48. }
  49. // GetByID 根据主键查询会话。
  50. func (r *ConversationRepository) GetByID(id uint) (*models.Conversation, error) {
  51. var conv models.Conversation
  52. if err := r.db.First(&conv, id).Error; err != nil {
  53. return nil, err
  54. }
  55. return &conv, nil
  56. }
  57. // ListActive 返回所有未关闭的访客会话(不含 internal)。
  58. func (r *ConversationRepository) ListActive() ([]models.Conversation, error) {
  59. var conversations []models.Conversation
  60. if err := r.db.Where("conversation_type = ? AND status != ?", "visitor", "closed").
  61. Order("updated_at desc").
  62. Find(&conversations).Error; err != nil {
  63. return nil, err
  64. }
  65. return conversations, nil
  66. }
  67. // ListByTypeAndStatus 返回指定类型的会话列表(支持 open/closed/all)。
  68. func (r *ConversationRepository) ListByTypeAndStatus(conversationType string, status string) ([]models.Conversation, error) {
  69. var conversations []models.Conversation
  70. q := r.db.Where("conversation_type = ?", conversationType)
  71. switch status {
  72. case "open":
  73. q = q.Where("status = ?", "open")
  74. case "closed":
  75. q = q.Where("status = ?", "closed")
  76. case "", "all":
  77. // no-op
  78. default:
  79. return nil, errors.New("invalid status")
  80. }
  81. if err := q.Order("updated_at desc").Find(&conversations).Error; err != nil {
  82. return nil, err
  83. }
  84. return conversations, nil
  85. }
  86. // ListInternalByAgentIDAndStatus 返回某客服的内部对话(支持 open/closed/all)。
  87. func (r *ConversationRepository) ListInternalByAgentIDAndStatus(agentID uint, status string) ([]models.Conversation, error) {
  88. var conversations []models.Conversation
  89. q := r.db.Where("conversation_type = ? AND agent_id = ?", "internal", agentID)
  90. switch status {
  91. case "open":
  92. q = q.Where("status = ?", "open")
  93. case "closed":
  94. q = q.Where("status = ?", "closed")
  95. case "", "all":
  96. // no-op
  97. default:
  98. return nil, errors.New("invalid status")
  99. }
  100. if err := q.Order("updated_at desc").Find(&conversations).Error; err != nil {
  101. return nil, err
  102. }
  103. return conversations, nil
  104. }
  105. // ListByIDs 根据多个 ID 批量查询会话。
  106. func (r *ConversationRepository) ListByIDs(ids []uint) ([]models.Conversation, error) {
  107. if len(ids) == 0 {
  108. return []models.Conversation{}, nil
  109. }
  110. var conversations []models.Conversation
  111. if err := r.db.Where("id IN ? AND status != ?", ids, "closed").
  112. Order("updated_at desc").
  113. Find(&conversations).Error; err != nil {
  114. return nil, err
  115. }
  116. return conversations, nil
  117. }
  118. // SearchByIDOrVisitorLike 根据会话 ID 或访客 ID 进行模糊搜索。
  119. func (r *ConversationRepository) SearchByIDOrVisitorLike(pattern string) ([]models.Conversation, error) {
  120. var conversations []models.Conversation
  121. if err := r.db.Where("CAST(id AS CHAR) LIKE ? OR CAST(visitor_id AS CHAR) LIKE ?", pattern, pattern).
  122. Find(&conversations).Error; err != nil {
  123. return nil, err
  124. }
  125. return conversations, nil
  126. }
  127. // CloseStaleOpenVisitorConversations 关闭长期未更新的 open 访客会话。
  128. func (r *ConversationRepository) CloseStaleOpenVisitorConversations(olderThan time.Time) (int64, error) {
  129. result := r.db.Model(&models.Conversation{}).
  130. Where("conversation_type = ? AND status = ? AND updated_at < ?", "visitor", "open", olderThan).
  131. Update("status", "closed")
  132. return result.RowsAffected, result.Error
  133. }
  134. // ListVisitorForAgentList 分页查询客服列表可见的访客会话(排除 AI 模式,且需有访客侧消息)。
  135. func (r *ConversationRepository) ListVisitorForAgentList(status string, offset, limit int) ([]models.Conversation, int64, error) {
  136. q := r.db.Model(&models.Conversation{}).
  137. Where("conversation_type = ? AND chat_mode != ?", "visitor", "ai").
  138. Where("EXISTS (SELECT 1 FROM messages WHERE messages.conversation_id = conversations.id AND messages.sender_is_agent = ?)", false)
  139. switch status {
  140. case "open":
  141. q = q.Where("status = ?", "open")
  142. case "closed":
  143. q = q.Where("status = ?", "closed")
  144. case "", "all":
  145. default:
  146. return nil, 0, errors.New("invalid status")
  147. }
  148. var total int64
  149. if err := q.Count(&total).Error; err != nil {
  150. return nil, 0, err
  151. }
  152. var conversations []models.Conversation
  153. if err := q.Order("updated_at desc").Offset(offset).Limit(limit).Find(&conversations).Error; err != nil {
  154. return nil, 0, err
  155. }
  156. return conversations, total, nil
  157. }
  158. func (r *ConversationRepository) AssignAgent(conversationID uint, agentID uint) error {
  159. result := r.db.Model(&models.Conversation{}).
  160. Where("id = ?", conversationID).
  161. Updates(map[string]interface{}{
  162. "agent_id": agentID,
  163. })
  164. if result.Error != nil {
  165. return result.Error
  166. }
  167. if result.RowsAffected == 0 {
  168. return gorm.ErrRecordNotFound
  169. }
  170. return nil
  171. }
  172. // UpdateStatus 更新会话状态。
  173. func (r *ConversationRepository) UpdateStatus(conversationID uint, status string) error {
  174. if status == "" {
  175. return errors.New("status cannot be empty")
  176. }
  177. return r.db.Model(&models.Conversation{}).
  178. Where("id = ?", conversationID).
  179. Update("status", status).Error
  180. }