conversation_controller.go 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. package controller
  2. import (
  3. "net/http"
  4. "github.com/2930134478/AI-CS/backend/service"
  5. "github.com/2930134478/AI-CS/backend/utils"
  6. "github.com/gin-gonic/gin"
  7. )
  8. // ConversationController 负责处理会话相关的 HTTP 请求。
  9. type ConversationController struct {
  10. conversationService *service.ConversationService
  11. }
  12. // NewConversationController 创建 ConversationController 实例。
  13. func NewConversationController(conversationService *service.ConversationService) *ConversationController {
  14. return &ConversationController{conversationService: conversationService}
  15. }
  16. type initConversationRequest struct {
  17. VisitorID uint `json:"visitor_id"`
  18. Website string `json:"website"`
  19. Referrer string `json:"referrer"`
  20. Browser string `json:"browser"`
  21. OS string `json:"os"`
  22. Language string `json:"language"`
  23. }
  24. type updateContactRequest struct {
  25. Email *string `json:"email"`
  26. Phone *string `json:"phone"`
  27. Notes *string `json:"notes"`
  28. }
  29. // InitConversation 为访客初始化或恢复会话。
  30. func (cc *ConversationController) InitConversation(c *gin.Context) {
  31. var req initConversationRequest
  32. if err := c.ShouldBindJSON(&req); err != nil || req.VisitorID == 0 {
  33. c.JSON(http.StatusBadRequest, gin.H{"error": "请求参数错误"})
  34. return
  35. }
  36. browser := req.Browser
  37. os := req.OS
  38. if browser == "" || os == "" {
  39. parsedBrowser, parsedOS := utils.ParseUserAgent(c.GetHeader("User-Agent"))
  40. if browser == "" {
  41. browser = parsedBrowser
  42. }
  43. if os == "" {
  44. os = parsedOS
  45. }
  46. }
  47. result, err := cc.conversationService.InitConversation(service.InitConversationInput{
  48. VisitorID: req.VisitorID,
  49. Website: req.Website,
  50. Referrer: req.Referrer,
  51. Browser: browser,
  52. OS: os,
  53. Language: req.Language,
  54. IPAddress: utils.GetClientIP(c),
  55. })
  56. if err != nil {
  57. c.JSON(http.StatusInternalServerError, gin.H{"error": "创建对话失败"})
  58. return
  59. }
  60. c.JSON(http.StatusOK, gin.H{
  61. "conversation_id": result.ConversationID,
  62. "status": result.Status,
  63. })
  64. }
  65. // UpdateContactInfo 用于更新访客的联系信息。
  66. func (cc *ConversationController) UpdateContactInfo(c *gin.Context) {
  67. id, err := parseUintParam(c, "id")
  68. if err != nil {
  69. c.JSON(http.StatusBadRequest, gin.H{"error": "会话ID不合法"})
  70. return
  71. }
  72. var req updateContactRequest
  73. if err := c.ShouldBindJSON(&req); err != nil {
  74. c.JSON(http.StatusBadRequest, gin.H{"error": "请求参数错误"})
  75. return
  76. }
  77. if req.Email == nil && req.Phone == nil && req.Notes == nil {
  78. c.JSON(http.StatusBadRequest, gin.H{"error": "至少提供一个需要更新的字段"})
  79. return
  80. }
  81. result, err := cc.conversationService.UpdateConversationContact(service.UpdateConversationContactInput{
  82. ConversationID: uint(id),
  83. Email: req.Email,
  84. Phone: req.Phone,
  85. Notes: req.Notes,
  86. })
  87. if err != nil {
  88. if err == service.ErrConversationNotFound {
  89. c.JSON(http.StatusNotFound, gin.H{"error": "会话不存在"})
  90. } else {
  91. c.JSON(http.StatusInternalServerError, gin.H{"error": "更新失败"})
  92. }
  93. return
  94. }
  95. c.JSON(http.StatusOK, gin.H{
  96. "email": result.Email,
  97. "phone": result.Phone,
  98. "notes": result.Notes,
  99. })
  100. }
  101. // ListConversations 返回当前活跃会话的列表。
  102. func (cc *ConversationController) ListConversations(c *gin.Context) {
  103. conversations, err := cc.conversationService.ListConversations()
  104. if err != nil {
  105. c.JSON(http.StatusInternalServerError, gin.H{"error": "查询对话列表失败"})
  106. return
  107. }
  108. items := make([]gin.H, 0, len(conversations))
  109. for _, conv := range conversations {
  110. item := gin.H{
  111. "id": conv.ID,
  112. "visitor_id": conv.VisitorID,
  113. "agent_id": conv.AgentID,
  114. "status": conv.Status,
  115. "created_at": formatTimeValue(conv.CreatedAt),
  116. "updated_at": formatTimeValue(conv.UpdatedAt),
  117. "unread_count": conv.UnreadCount,
  118. }
  119. // 添加 last_seen_at 字段(用于判断在线状态)
  120. if lastSeen := formatTimePointer(conv.LastSeenAt); lastSeen != "" {
  121. item["last_seen_at"] = lastSeen
  122. }
  123. if conv.LastMessage != nil {
  124. item["last_message"] = gin.H{
  125. "id": conv.LastMessage.ID,
  126. "content": conv.LastMessage.Content,
  127. "sender_is_agent": conv.LastMessage.SenderIsAgent,
  128. "message_type": conv.LastMessage.MessageType,
  129. "is_read": conv.LastMessage.IsRead,
  130. "read_at": formatTimePointer(conv.LastMessage.ReadAt),
  131. "created_at": formatTimeValue(conv.LastMessage.CreatedAt),
  132. }
  133. }
  134. items = append(items, item)
  135. }
  136. c.JSON(http.StatusOK, items)
  137. }
  138. // GetConversationDetail 返回会话的详细信息。
  139. func (cc *ConversationController) GetConversationDetail(c *gin.Context) {
  140. id, err := parseUintParam(c, "id")
  141. if err != nil {
  142. c.JSON(http.StatusBadRequest, gin.H{"error": "会话ID不合法"})
  143. return
  144. }
  145. detail, err := cc.conversationService.GetConversationDetail(uint(id))
  146. if err != nil {
  147. if err == service.ErrConversationNotFound {
  148. c.JSON(http.StatusNotFound, gin.H{"error": "会话不存在"})
  149. } else {
  150. c.JSON(http.StatusInternalServerError, gin.H{"error": "查询失败"})
  151. }
  152. return
  153. }
  154. response := gin.H{
  155. "id": detail.ID,
  156. "visitor_id": detail.VisitorID,
  157. "agent_id": detail.AgentID,
  158. "status": detail.Status,
  159. "website": detail.Website,
  160. "referrer": detail.Referrer,
  161. "browser": detail.Browser,
  162. "os": detail.OS,
  163. "language": detail.Language,
  164. "ip_address": detail.IPAddress,
  165. "location": detail.Location,
  166. "email": detail.Email,
  167. "phone": detail.Phone,
  168. "notes": detail.Notes,
  169. "created_at": formatTimeValue(detail.CreatedAt),
  170. "updated_at": formatTimeValue(detail.UpdatedAt),
  171. "unread_count": detail.UnreadCount,
  172. }
  173. if lastSeen := formatTimePointer(detail.LastSeen); lastSeen != "" {
  174. response["last_seen_at"] = lastSeen
  175. }
  176. if detail.LastMessage != nil {
  177. response["last_message"] = gin.H{
  178. "id": detail.LastMessage.ID,
  179. "content": detail.LastMessage.Content,
  180. "sender_is_agent": detail.LastMessage.SenderIsAgent,
  181. "message_type": detail.LastMessage.MessageType,
  182. "is_read": detail.LastMessage.IsRead,
  183. "read_at": formatTimePointer(detail.LastMessage.ReadAt),
  184. "created_at": formatTimeValue(detail.LastMessage.CreatedAt),
  185. }
  186. }
  187. c.JSON(http.StatusOK, response)
  188. }
  189. // SearchConversations 根据关键字进行会话的模糊搜索。
  190. func (cc *ConversationController) SearchConversations(c *gin.Context) {
  191. query := c.Query("q")
  192. if query == "" {
  193. c.JSON(http.StatusBadRequest, gin.H{"error": "搜索关键词不能为空"})
  194. return
  195. }
  196. conversations, err := cc.conversationService.SearchConversations(query)
  197. if err != nil {
  198. c.JSON(http.StatusInternalServerError, gin.H{"error": "搜索失败"})
  199. return
  200. }
  201. items := make([]gin.H, 0, len(conversations))
  202. for _, conv := range conversations {
  203. item := gin.H{
  204. "id": conv.ID,
  205. "visitor_id": conv.VisitorID,
  206. "agent_id": conv.AgentID,
  207. "status": conv.Status,
  208. "created_at": formatTimeValue(conv.CreatedAt),
  209. "updated_at": formatTimeValue(conv.UpdatedAt),
  210. "unread_count": conv.UnreadCount,
  211. }
  212. // 添加 last_seen_at 字段(用于判断在线状态)
  213. if lastSeen := formatTimePointer(conv.LastSeenAt); lastSeen != "" {
  214. item["last_seen_at"] = lastSeen
  215. }
  216. if conv.LastMessage != nil {
  217. item["last_message"] = gin.H{
  218. "id": conv.LastMessage.ID,
  219. "content": conv.LastMessage.Content,
  220. "sender_is_agent": conv.LastMessage.SenderIsAgent,
  221. "message_type": conv.LastMessage.MessageType,
  222. "is_read": conv.LastMessage.IsRead,
  223. "read_at": formatTimePointer(conv.LastMessage.ReadAt),
  224. "created_at": formatTimeValue(conv.LastMessage.CreatedAt),
  225. }
  226. }
  227. items = append(items, item)
  228. }
  229. c.JSON(http.StatusOK, items)
  230. }