handler.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. package websocket
  2. import (
  3. "errors"
  4. "log"
  5. "net/http"
  6. "strconv"
  7. "strings"
  8. "github.com/2930134478/AI-CS/backend/repository"
  9. "github.com/2930134478/AI-CS/backend/service"
  10. "github.com/2930134478/AI-CS/backend/utils"
  11. "github.com/gin-gonic/gin"
  12. "github.com/gorilla/websocket"
  13. "gorm.io/gorm"
  14. )
  15. var upgrader = websocket.Upgrader{
  16. ReadBufferSize: 1024,
  17. WriteBufferSize: 1024,
  18. // 允许跨域连接
  19. CheckOrigin: func(r *http.Request) bool {
  20. return true
  21. },
  22. }
  23. // HandleWebSocket 处理 WebSocket 连接
  24. func HandleWebSocket(hub *Hub, userRepo *repository.UserRepository, conversationService *service.ConversationService) gin.HandlerFunc {
  25. return func(c *gin.Context) {
  26. // 从查询参数获取对话ID
  27. conversationIDStr := c.Query("conversation_id")
  28. if conversationIDStr == "" {
  29. c.JSON(http.StatusBadRequest, gin.H{"error": "conversation_id 不能为空"})
  30. return
  31. }
  32. conversationID, err := strconv.ParseUint(conversationIDStr, 10, 32)
  33. if err != nil {
  34. c.JSON(http.StatusBadRequest, gin.H{"error": "无效的 conversation_id"})
  35. return
  36. }
  37. // 从查询参数获取是否是访客(默认为 true,因为默认是访客连接)
  38. isVisitorStr := c.DefaultQuery("is_visitor", "true")
  39. isVisitor := isVisitorStr == "true" || isVisitorStr == "1"
  40. // 从查询参数获取客服ID(如果是客服连接,需要传递 agent_id)
  41. var agentID uint
  42. if !isVisitor {
  43. agentIDStr := c.Query("agent_id")
  44. if agentIDStr == "" {
  45. c.JSON(http.StatusBadRequest, gin.H{"error": "agent_id 不能为空"})
  46. return
  47. }
  48. parsed, parseErr := strconv.ParseUint(agentIDStr, 10, 32)
  49. if parseErr != nil || parsed == 0 {
  50. c.JSON(http.StatusBadRequest, gin.H{"error": "无效的 agent_id"})
  51. return
  52. }
  53. agentID = uint(parsed)
  54. wsToken := c.Query("ws_token")
  55. if !utils.ValidateWSToken(wsToken, agentID) {
  56. c.JSON(http.StatusUnauthorized, gin.H{"error": "ws_token 无效或已过期"})
  57. return
  58. }
  59. if userRepo != nil {
  60. user, userErr := userRepo.GetByID(agentID)
  61. if userErr != nil || user == nil {
  62. c.JSON(http.StatusUnauthorized, gin.H{"error": "客服身份无效"})
  63. return
  64. }
  65. if user.Role != "admin" && user.Role != "agent" {
  66. c.JSON(http.StatusForbidden, gin.H{"error": "仅客服账号允许建立该连接"})
  67. return
  68. }
  69. }
  70. } else if conversationService != nil {
  71. accessToken := strings.TrimSpace(c.Query("access_token"))
  72. if accessToken == "" {
  73. accessToken = strings.TrimSpace(c.GetHeader("X-Conversation-Token"))
  74. }
  75. if err := conversationService.ValidateVisitorAccessToken(uint(conversationID), accessToken); err != nil {
  76. if errors.Is(err, gorm.ErrRecordNotFound) {
  77. c.JSON(http.StatusNotFound, gin.H{"error": "会话不存在"})
  78. } else {
  79. c.JSON(http.StatusUnauthorized, gin.H{"error": "access_token 无效或缺失"})
  80. }
  81. return
  82. }
  83. }
  84. // 升级 HTTP 连接为 WebSocket 连接
  85. conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
  86. if err != nil {
  87. log.Printf("WebSocket 升级失败: %v", err)
  88. return
  89. }
  90. // 创建客户端
  91. client := NewClient(hub, conn, uint(conversationID), isVisitor, agentID)
  92. // 注册客户端到 Hub
  93. client.hub.register <- client
  94. // 启动两个 goroutine:
  95. // 1. ReadPump:从客户端读取消息(主要是心跳包)
  96. // 2. WritePump:向客户端发送消息
  97. go client.WritePump()
  98. go client.ReadPump()
  99. log.Printf("✅ WebSocket 连接已建立: 对话ID=%d, 是访客=%v", conversationID, isVisitor)
  100. }
  101. }