package main import ( "fmt" "log" "net/http" "time" "xk-websocket-v2/internal/api" "xk-websocket-v2/internal/manager" "xk-websocket-v2/internal/model" "xk-websocket-v2/internal/service" "xk-websocket-v2/internal/turnserver" "xk-websocket-v2/internal/ws" "github.com/gin-gonic/gin" "github.com/go-redis/redis/v8" "github.com/gorilla/websocket" "github.com/spf13/viper" "gorm.io/driver/mysql" "gorm.io/gorm" ) // ... (initConfig, initDB, initRedis 保持不变) ... func initConfig() { viper.SetConfigName("config") viper.SetConfigType("yaml") viper.AddConfigPath("configs") viper.AddConfigPath(".") if err := viper.ReadInConfig(); err != nil { log.Fatalf("❌ 无法读取配置文件: %v", err) } } func initDB() *gorm.DB { dsn := viper.GetString("database.dsn") db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{}) if err != nil { log.Fatalf("❌ 数据库连接失败: %v", err) } db.AutoMigrate(&model.ChatMessage{}) return db } func initRedis() *redis.Client { rdb := redis.NewClient(&redis.Options{ Addr: viper.GetString("redis.addr"), Password: viper.GetString("redis.password"), DB: viper.GetInt("redis.db"), }) if _, err := rdb.Ping(rdb.Context()).Result(); err != nil { log.Fatalf("❌ Redis 连接失败: %v", err) } return rdb } func main() { initConfig() db := initDB() rdb := initRedis() ws.StartWorkerPool() defer ws.StopWorkerPool() service.InitChatService(db, rdb) go turnserver.Start() r := gin.Default() // 允许跨域 (重要:为了前端本地开发) r.Use(func(c *gin.Context) { c.Writer.Header().Set("Access-Control-Allow-Origin", "*") c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE") c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, X-User-ID") if c.Request.Method == "OPTIONS" { c.AbortWithStatus(204) return } c.Next() }) r.GET("/ws", func(c *gin.Context) { upgrader := websocket.Upgrader{CheckOrigin: func(r *http.Request) bool { return true }} conn, err := upgrader.Upgrade(c.Writer, c.Request, nil) if err != nil { return } userID := c.Query("user_id") clientID := fmt.Sprintf("%s-%d", viper.GetString("app.node_id"), time.Now().UnixNano()) client := &manager.Client{ID: clientID, Conn: conn, SendQueue: make(chan []byte, 256)} manager.Manager.Register(client) if userID != "" { service.ChatSvc.BindUser(client, userID) } client.SendQueue <- []byte(fmt.Sprintf(`{"clientId": "%s"}`, clientID)) for { _, message, err := conn.ReadMessage() if err != nil { manager.Manager.Unregister(client) break } // WebSocket 仅处理信令和心跳,不再处理 send_message (改走API) // 但为了兼容,仍保留 PushTask ws.PushTask(client, message) } }) apiGroup := r.Group("/api") { apiGroup.POST("/send", api.SendHandler) apiGroup.POST("/send-to-user", api.SendToUserHandler) apiGroup.POST("/bind", api.BindHandler) apiGroup.GET("/check-user-online", api.CheckUserOnlineHandler) apiGroup.GET("/messages", api.HistoryHandler) apiGroup.GET("/messages/sync", api.SyncMessagesHandler) apiGroup.GET("/health", api.HealthHandler) apiGroup.GET("/ice-servers", api.ICEHandler) // 新增:联系人列表 apiGroup.GET("/contacts", api.ContactListHandler) } port := viper.GetString("app.port") log.Printf("🚀 服务启动在端口: %s", port) r.Run(":" + port) }