package main import ( "flag" "log" "os" "runtime/debug" "time" "xk-websocket/controller" "xk-websocket/route" "xk-websocket/utils" "gorm.io/driver/mysql" "gorm.io/gorm" "github.com/gin-gonic/gin" ) /** * ensureLogDir * 功能:检查指定的日志目录是否存在,如果不存在则尝试创建该目录。 * @param logDir string 日志目录路径 */ func ensureLogDir(logDir string) { if _, err := os.Stat(logDir); os.IsNotExist(err) { log.Printf("📂 日志目录不存在,正在创建: %s", logDir) if err := os.MkdirAll(logDir, 0755); err != nil { log.Fatalf("❌ 无法创建日志目录: %v", err) } log.Printf("✅ 日志目录创建成功: %s", logDir) } } /** * setupLogger * 功能:配置全局日志系统。 * 逻辑: * 1. 读取环境变量配置日志目录和轮转频率。 * 2. 创建自定义的 DailyFileWriter 以支持按天/按小时分割日志。 * 3. 将标准 log 的输出重定向到文件系统。 */ func setupLogger() { // 设置日志目录(优先级:环境变量 > 默认值) logDir := os.Getenv("LOG_DIR") if logDir == "" { logDir = "logs" // 默认日志目录 } // 确保日志目录存在 ensureLogDir(logDir) // 设置日志轮转频率 logFreq := os.Getenv("LOG_ROTATE_FREQ") if logFreq == "" { logFreq = "daily" // 默认每天轮转 } // 创建文件日志写入器 logger := log.Default() fileWriter := utils.NewDailyFileWriter(logDir, logFreq, logger, "xk-websocket") // 同时输出到控制台和文件 log.SetOutput(fileWriter) } /** * initDB * 功能:初始化 MySQL 数据库连接。 * 返回:*gorm.DB 数据库连接实例 */ func initDB() *gorm.DB { dsn := utils.GetEnv("DB_DSN", "root:password@tcp(localhost:3306)/xk_chat?charset=utf8mb4&parseTime=True&loc=Local") db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{}) if err != nil { log.Fatalf("❌ 数据库连接失败: %v", err) } //// 自动迁移表结构 //if err := db.AutoMigrate(&models.XkChatMessage{}); err != nil { // log.Fatalf("❌ 数据库迁移失败: %v", err) //} log.Println("✅ 数据库连接成功") return db } /** * main * 功能:程序主入口。 * 流程: * 1. 初始化日志。 * 2. 解析命令行参数(端口、节点ID)。 * 3. 初始化数据库和WebSocket控制器。 * 4. 启动 Redis 订阅(用于集群消息同步)。 * 5. 注册 HTTP/WebSocket 路由。 * 6. 启动 Web Server。 */ func main() { // 初始化日志系统(必须放在最先) setupLogger() defer func() { if r := recover(); r != nil { log.Printf("❌ 程序崩溃: %v", r) debug.PrintStack() log.Println("程序将在10秒后退出...") time.Sleep(10 * time.Second) } }() log.Println("🚀 启动 WebSocket 服务...") // 添加命令行参数解析 var port, nodeID string flag.StringVar(&port, "port", "", "运行端口号") flag.StringVar(&nodeID, "nodeId", "", "节点标识符") flag.Parse() // 初始化数据库 db := initDB() // 初始化控制器 wsCtrl := controller.NewWebSocketController(db) // 设置参数优先级 if nodeID != "" { wsCtrl.NodeID = nodeID } if port != "" { wsCtrl.Port = port } // 配置系统 wsCtrl.ConfigureSystem() wsCtrl.InitRedisClient() wsCtrl.PrintStartupInfo() // 创建Gin路由器 router := gin.Default() // 设置路由 route.SetupRoutes(router, wsCtrl) // 启动Redis消息订阅 go wsCtrl.SubscribeToRedis() // 启动HTTP服务 addr := "0.0.0.0:" + wsCtrl.Port log.Printf("🌐 监听地址: %s", addr) log.Printf("🔌 WebSocket连接地址: ws://%s/ws", addr) if err := router.Run(addr); err != nil { log.Fatalf("❌ 服务启动失败: %v", err) } }