package main import ( "Tech/common" "Tech/controllers" _ "Tech/routers" "os" "os/signal" "syscall" "time" "github.com/beego/beego/v2/client/orm" "github.com/beego/beego/v2/core/logs" "github.com/beego/beego/v2/server/web" beego "github.com/beego/beego/v2/server/web" "github.com/beego/beego/v2/server/web/filter/cors" "github.com/sirupsen/logrus" ) func init() { // 注册驱动(可选的,mysql/sqlite3/postgres 默认已注册):cite[4] // orm.RegisterDriver("mysql", orm.DR_MySQL) // 从配置文件读取数据库配置 dbAdapter, _ := beego.AppConfig.String("DB.Adapter") dbURL, _ := beego.AppConfig.String("DB.URL") // 注册数据库 orm.RegisterDataBase("default", dbAdapter, dbURL) // 启动数据库连接监控协程 go monitorDatabaseConnection(dbAdapter, dbURL) // 注册模型(也可以在主程序入口或模型自身的 init 函数中) // orm.RegisterModel(new(User)) } // sendHeartbeatPeriodically 定时发送心跳到信息化服务器 func sendHeartbeatPeriodically() { ticker := time.NewTicker(30 * time.Second) // 每30秒发送一次心跳 defer ticker.Stop() common.LogInfo("心跳发送任务已启动,间隔30秒") for range ticker.C { err := func() error { defer func() { if r := recover(); r != nil { common.LogError("发送心跳时发生异常", nil, logrus.Fields{ "panic": r, }) } }() err := common.SendHeartbeat() if err != nil { common.LogError("发送心跳失败", err) return err } common.LogInfo("成功发送心跳到信息化服务器") return nil }() if err != nil { common.LogError("发送心跳任务出现错误", err) } } } // monitorDatabaseConnection 定时检查数据库连接并尝试重连 func monitorDatabaseConnection(dbAdapter, dbURL string) { ticker := time.NewTicker(10 * time.Second) // 每10秒检查一次 defer ticker.Stop() common.LogInfo("数据库连接监控任务已启动,间隔10秒") for range ticker.C { // 使用匿名函数包装可能产生异常的操作,并在每次循环都进行异常处理 func() { defer func() { if r := recover(); r != nil { // 即使NewOrm发生异常,也要尝试重新注册数据库连接 common.LogError("监控协程发生异常", nil, logrus.Fields{ "panic": r, }) err := orm.RegisterDataBase("default", dbAdapter, dbURL) if err != nil { common.LogError("数据库重连失败", err) } else { common.LogInfo("数据库重连成功") } } }() // 尝试执行一个简单的查询来测试连接 startTime := time.Now() o := orm.NewOrm() _, err := o.Raw("SELECT 1").Exec() duration := time.Since(startTime) if err != nil { common.LogError("数据库连接异常", err, logrus.Fields{ "adapter": dbAdapter, "db_check": "SELECT 1", }) // 尝试重新注册数据库连接 err = orm.RegisterDataBase("default", dbAdapter, dbURL) if err != nil { common.LogError("数据库重连失败", err) } else { common.LogInfo("数据库重连成功") } } else { // 每100次检查记录一次成功日志(避免日志过多) if time.Now().Unix()%100 == 0 { common.LogDebug("数据库连接检查正常", logrus.Fields{ "duration_ms": duration.Milliseconds(), }) } } }() } } func main() { // 初始化日志系统 logConfig := &common.LogConfig{ LogDir: "./log", LogLevel: "info", MaxSize: 100, // 100MB MaxBackups: 30, // 保留30个旧文件 MaxAge: 90, // 保留90天 Compress: true, EnableCaller: true, } if err := common.InitLogger(logConfig); err != nil { logs.Error("初始化日志系统失败: %v", err) // 仍然继续运行,但使用默认日志 } else { common.LogInfo("日志系统初始化成功") } // 设置优雅关闭 setupGracefulShutdown() // 自动建表。参数说明:数据库别名、是否强制重建(false 则只创建新表)、是否打印日志 // orm.RunSyncdb("default", false, true) // 可选:开启 ORM 调试模式,打印 SQL 日志 // 将 URL 路径 /static/upload 映射到本地目录 static/upload // CORS 过滤器 beego.InsertFilter("*", beego.BeforeRouter, cors.Allow(&cors.Options{ AllowAllOrigins: true, AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"}, AllowHeaders: []string{"Origin", "Authorization", "Access-Control-Allow-Origin", "Content-Type"}, ExposeHeaders: []string{"Content-Length", "Access-Control-Allow-Origin"}, AllowCredentials: true, })) web.SetStaticPath("/static/upload", "static/upload") orm.Debug = true // 初始化用户信息缓存 controllers.InitUserInfoCache() // 启动定时发送心跳任务 go sendHeartbeatPeriodically() // 初始化电调数据获取器 common.InitElectricalDataFetcher() common.LogInfo("SCADA培训系统服务启动中...") beego.Run() } // setupGracefulShutdown 设置优雅关闭 func setupGracefulShutdown() { c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt, syscall.SIGTERM) go func() { <-c common.LogInfo("接收到关闭信号,正在优雅关闭服务...") common.CloseLogger() os.Exit(0) }() }