package api import ( "fmt" "io" "log" // "log" "net/http" _ "net/http/pprof" "os" "path/filepath" "sync" "time" "xiawan/wx/api/middleware" "xiawan/wx/api/req" "xiawan/wx/api/router" "xiawan/wx/api/service" "xiawan/wx/clientsdk/baseinfo" "xiawan/wx/db" "xiawan/wx/db/table" "xiawan/wx/srv/srvconfig" "github.com/gin-gonic/gin" ) func TLog() { dir, _ := filepath.Abs(filepath.Dir("")) logFileName := time.Now().Format("20060102") + ".log" logFileAllPath := dir + "/log/" + logFileName _, err := os.Stat(logFileAllPath) var f *os.File if err != nil { f, _ = os.Create(logFileAllPath) } else { f, _ = os.OpenFile(logFileAllPath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666) } gin.DefaultWriter = io.MultiWriter(f, os.Stdout) gin.DefaultErrorWriter = io.MultiWriter(f, os.Stderr) log.SetOutput(gin.DefaultWriter) if srvconfig.GlobalSetting.Debug { gin.SetMode(gin.DebugMode) } else { gin.SetMode(gin.ReleaseMode) } } func WXServerGinHttpApiStart() error { TLog() // 初始化日志 service.InitWXServerRouter() // 初始化路由 go InitAnewLogin() // 初始化重新上线 // 设置中间件 app := router.SetUpRouter(func(engine *gin.Engine) { engine.Use(middleware.Cors) engine.Use(gin.Recovery()) }, false) // 启动 http 服务 go func() { _ = http.ListenAndServe(":"+srvconfig.GlobalSetting.Port, nil) }() // 启动 gin 服务 err := app.Run(srvconfig.GlobalSetting.Host + ":" + srvconfig.GlobalSetting.Port) if err != nil { return err } return nil } const concurrencyLimit = 100 // 并发限制 // InitAnewLogin 初始化重新上线,后期考虑用分页存redis多线程实现 func InitAnewLogin() { // 清理缓存 db.DelObj("wx_sync_msg_topic") db.ClearSyncMsgCache("_syncMsg") db.ClearSyncMsgCache("_syncHttp") db.ClearSyncMsgCache("_wx_sync_msg_topic") db.DelObj("wx_sync_msg_topic") list := db.QueryListUserInfo() sem := make(chan struct{}, concurrencyLimit) // 创建带缓冲通道,用于限制并发数 var wg sync.WaitGroup for _, v := range list { // 只为已登录的用户初始化连接,避免未登录用户的重连尝试 if v.State == 0 || v.State == 4 { // MMLoginStateNoLogin 或 MMLoginStateLogout fmt.Printf("跳过未登录用户的连接初始化: %s (状态: %d)\n", v.UUID, v.State) continue } // 初始化代理 key := fmt.Sprintf("%s%s", "wechat:Proxy:", v.UUID) exists, _ := db.Exists(key) newModel := &req.GetLoginQrCodeModel{ Proxy: "", Check: false, } if exists { //获取代理 db.GETObj(key, &newModel) } wg.Add(1) go func(_v table.UserInfoEntity) { defer wg.Done() // 获取信号量,确保并发数不超过限制 sem <- struct{}{} defer func() { <-sem }() // 执行完毕后释放信号量 // 捕获 panic,防止程序崩溃 defer func() { if r := recover(); r != nil { fmt.Printf("初始化上线-Recovered from panic: %v\n", r) // 这里可以记录日志或者执行其他的恢复操作 } }() service.InitLoginStatusService(_v.UUID, false, true, *newModel) }(v) } // 等待所有协程完成 wg.Wait() fmt.Println("初始化上线完成") // 启动定期清理未登录连接的协程 go func() { ticker := time.NewTicker(5 * time.Minute) // 每5分钟清理一次 defer ticker.Stop() for range ticker.C { cleanupUnloggedConnections() } }() } // cleanupUnloggedConnections 清理未登录状态的连接 func cleanupUnloggedConnections() { defer func() { if r := recover(); r != nil { fmt.Printf("cleanupUnloggedConnections发生panic: %v\n", r) } }() // fmt.Println("开始清理未登录连接...") connectMgr := service.WXServer.GetWXConnectMgr() connectInfo := connectMgr.GetConnectInfo() // fmt.Printf("连接信息类型: %T\n", connectInfo) // 安全获取connections connectionsRaw, exists := connectInfo["connections"] if !exists { fmt.Println("警告:没有找到connections字段") return } // fmt.Printf("connections类型: %T\n", connectionsRaw) connections, ok := connectionsRaw.([]map[string]interface{}) if !ok { fmt.Printf("错误:connections类型断言失败,实际类型: %T\n", connectionsRaw) return } // fmt.Printf("找到 %d 个连接\n", len(connections)) for _, conn := range connections { // fmt.Printf("处理连接 %d\n", i) // 修复类型断言问题:loginState 实际是 uint32 类型 loginStateRaw := conn["loginState"] // fmt.Printf("loginState原始值: %v, 类型: %T\n", loginStateRaw, loginStateRaw) var loginState int // 安全的类型转换 switch v := loginStateRaw.(type) { case uint32: loginState = int(v) case int: loginState = v default: // fmt.Printf("警告:未知的 loginState 类型: %T, 跳过此连接\n", v) continue } // 修复userInfo类型断言问题 userInfoRaw := conn["userInfo"] // fmt.Printf("userInfo原始值类型: %T\n", userInfoRaw) var uuid string // 安全处理不同类型的userInfo switch v := userInfoRaw.(type) { case *baseinfo.UserInfo: uuid = v.UUID // fmt.Printf("从*baseinfo.UserInfo获取UUID: %s\n", uuid) case map[string]interface{}: if uuidRaw, exists := v["UUID"]; exists { if uuidStr, ok := uuidRaw.(string); ok { uuid = uuidStr // fmt.Printf("从map[string]interface{}获取UUID: %s\n", uuid) } else { // fmt.Printf("警告:UUID类型不是string,类型: %T, 跳过此连接\n", uuidRaw) continue } } else { // fmt.Println("警告:userInfo中没有找到UUID字段,跳过此连接") continue } default: // fmt.Printf("警告:未知的 userInfo 类型: %T, 跳过此连接\n", v) continue } // fmt.Printf("连接 %d: UUID=%s, loginState=%d\n", i, uuid, loginState) // 如果是未登录或已登出状态,清理连接 if loginState == 0 || loginState == 4 { // MMLoginStateNoLogin 或 MMLoginStateLogout wxConn := connectMgr.GetWXConnectByUserInfoUUID(uuid) if wxConn != nil { // fmt.Printf("清理未登录用户的连接: %s (状态: %d)\n", uuid, loginState) wxConn.Stop() connectMgr.Remove(wxConn) } else { // fmt.Printf("未找到用户连接对象: %s\n", uuid) } } } // fmt.Println("清理未登录连接完成") } // socks5://ztb44:1454@27.128.157.125:4015