package db import ( "database/sql" "fmt" "reflect" "server/MergeConst" "server/conf" GoUtil "server/game_util" "server/pkg/github.com/name5566/leaf/log" "strings" "sync" "time" // "server/game" _ "github.com/go-sql-driver/mysql" "github.com/jmoiron/sqlx" ) var SqlDb *sqlx.DB var sqlDbMu sync.RWMutex // GetDB 线程安全地获取数据库连接 func GetDB() *sqlx.DB { sqlDbMu.RLock() defer sqlDbMu.RUnlock() return SqlDb } // GetDBOrPanic 获取数据库连接,如果为 nil 则记录错误 func GetDBOrPanic() *sqlx.DB { db := GetDB() if db == nil { log.Error("Database connection is nil, please check database initialization") } return db } // EnsureDB 确保数据库连接可用,如果不可用则返回错误 func EnsureDB() (*sqlx.DB, error) { db := GetDB() if db == nil { return nil, fmt.Errorf("database connection is nil") } if err := db.Ping(); err != nil { return nil, fmt.Errorf("database ping failed: %w", err) } return db, nil } // 封装创建连接 func connectMySQL() (*sqlx.DB, error) { MysqlPwd, _ := GoUtil.Decrypt(conf.Server.MySqlPwd) // 减少超时时间,避免长时间阻塞 connect := fmt.Sprintf("%s:%s@(%s:%s)/%s?timeout=10s&readTimeout=15s&writeTimeout=15s&parseTime=true", conf.Server.MySqlUsr, MysqlPwd, conf.Server.MySqlAddr, conf.Server.MySqlPort, conf.Server.DbName) db, err := sqlx.Connect("mysql", connect) if err != nil { return nil, err } // 增加最大连接数,减少连接等待时间 db.SetMaxOpenConns(100) db.SetMaxIdleConns(20) db.SetConnMaxLifetime(30 * time.Minute) // 减少连接生命周期 db.SetConnMaxIdleTime(5 * time.Minute) // 减少空闲时间 return db, nil } func InitDB() { //"用户名:密码@[连接方式](主机名:端口号)/数据库名" db, err := connectMySQL() if err != nil { log.Debug("connect mysql failed: %v", err) return } sqlDbMu.Lock() SqlDb = db sqlDbMu.Unlock() log.Debug("connect mysql success") // 定时检测与重连 go func() { ticker := time.NewTicker(5 * time.Second) // 改为5秒检测一次,降低频率 defer ticker.Stop() for range ticker.C { sqlDbMu.RLock() cur := SqlDb sqlDbMu.RUnlock() if cur == nil { log.Debug("mysql connection is nil, start reconnect") ReconnectDB() continue } // Ping 操作不持有锁,避免阻塞其他操作 if err := cur.Ping(); err != nil { log.Debug("mysql ping failed: %v, start reconnect", err) ReconnectDB() } } }() } // 自动重连 func ReconnectDB() { sqlDbMu.Lock() defer sqlDbMu.Unlock() newDb, err := connectMySQL() if err != nil { log.Debug("mysql reconnect failed: %v", err) return } if SqlDb != nil { _ = SqlDb.Close() } SqlDb = newDb log.Debug("mysql reconnect success") } func SeriesTransaction(sqlstrs []string, params [][]any) (err error) { sqlDb := GetDB() if sqlDb == nil { return fmt.Errorf("database connection is nil") } tx, err := sqlDb.Begin() if err != nil { log.Debug("Transaction failed, err:%v\n", err) return err } defer func() { if err != nil { log.Debug("Transaction failed, err:%v\n", err) _ = tx.Rollback() } else { err = tx.Commit() return } }() for i := 0; i < len(sqlstrs); i++ { _, err := tx.Exec(sqlstrs[i], params[i]...) if err != nil { log.Debug("Transaction failed, err:%v\n", err) return err } } return } // "UPDATE user SET age = ?, degree = ? WHERE id = ?" func FormatAllMemUpdateDb(u interface{}, tableName string, Exclude string) (err error) { t := reflect.TypeOf(u) len1 := t.Elem().NumField() Fields := make([]string, len1) Values := make([]interface{}, len1) pp := reflect.ValueOf(u) origin := "UPDATE " + tableName + " SET " index := 0 var keyValue interface{} for i := 0; i < len1; i++ { field := t.Elem().Field(i) if field.Tag.Get("db") != Exclude { Fields[index] = "`" + field.Tag.Get("db") + "` = ?" ufield := pp.Elem().FieldByName(field.Name) k := ufield.Kind() if k == reflect.String { Values[index] = ufield.String() } if k == reflect.Int32 || k == reflect.Int || k == reflect.Int64 { Values[index] = ufield.Int() } index++ } else { ufield := pp.Elem().FieldByName(field.Name) k := ufield.Kind() if k == reflect.String { keyValue = ufield.String() } if k == reflect.Int32 || k == reflect.Int || k == reflect.Int64 { keyValue = ufield.Int() } } } Values[index] = keyValue // Values = append(Values, keyValue) origin += strings.Join(Fields, ",") strLen := len(origin) origin = origin[:strLen-1] origin = origin + " WHERE `" + Exclude + "` = ?" sqlStr := origin _, err = SqlDb.Exec(sqlStr, Values...) if err != nil { log.Debug("table: %s, sql :%s, exec failed, err:%v\n", tableName, sqlStr, err) return } return } func FormatAllMemInsertDb(u interface{}, tableName string) (insertID int64, err error) { t := reflect.TypeOf(u) len := t.Elem().NumField() Fields := make([]string, len) Fields1 := make([]string, len) FieldNames := make([]string, len) Values := make([]interface{}, len) pp := reflect.ValueOf(u) origin := "INSERT INTO " + tableName + "(" for i := 0; i < len; i++ { field := t.Elem().Field(i) Fields[i] = field.Tag.Get("db") Fields1[i] = "?" FieldNames[i] = field.Name ufield := pp.Elem().FieldByName(field.Name) k := ufield.Kind() if k == reflect.String { Values[i] = ufield.String() } if k == reflect.Int32 { Values[i] = ufield.Int() } if k == reflect.Int64 { Values[i] = ufield.Int() } } for i := range Fields { Fields[i] = "`" + Fields[i] + "`" } origin += strings.Join(Fields, ",") origin += ") VALUES(" origin += strings.Join(Fields1, ",") origin += ")" sqlStr := origin result, err := SqlDb.Exec(sqlStr, Values...) if err != nil { log.Debug("table: %s, sql :%s, exec failed, err:%v\n", tableName, sqlStr, err) return } insertID, err = result.LastInsertId() if err != nil { log.Debug("table: %s, sql :%s, exec failed, err:%v\n", tableName, sqlStr, err) return } return } func GetPlayerBaseInfoFromDbByName(name string) *ResPlayerBaseInfo { sqlStr := "SELECT * FROM t_player_baseinfo WHERE user_name = ?" var res ResPlayerBaseInfo if err := SqlDb.Get(&res, sqlStr, name); err != nil { log.Debug("table: %s, sql :%s, exec failed, err:%v\n", "PlayerBaseInfo", sqlStr, err) return nil } return &res } func GetPlayerBan(name string) int64 { sqlStr := "SELECT ban FROM t_player_baseinfo WHERE user_name = ?" var ban int64 if err := SqlDb.Get(&ban, sqlStr, name); err != nil { log.Debug("table: %s, sql :%s, exec failed, err:%v\n", "PlayerBaseInfo", sqlStr, err) return 0 } return ban } func UpdatePlayerBan(uid int64, ban int64) error { sqlStr := "UPDATE t_player_baseinfo SET ban = ? WHERE dwUin = ?" _, err := SqlDb.Exec(sqlStr, ban, uid) return err } func UpdatePlayerBaseInfoName(oldName, newName string) error { sqlStr := "UPDATE t_player_baseinfo SET user_name = ? WHERE user_name = ?" _, err := SqlDb.Exec(sqlStr, newName, oldName) return err } func GetPlayerBaseInfoFromDbById(id int32) *ResPlayerBaseInfo { sqlStr := "SELECT * FROM t_player_baseinfo WHERE dwUin = ?" var res ResPlayerBaseInfo if err := SqlDb.Get(&res, sqlStr, id); err != nil { log.Debug("table: %s, sql :%s, exec failed, err:%v\n", "PlayerBaseInfo", sqlStr, err) return nil } return &res } func GetAccountInfoFromDb(name string) *Db_Account { sqlStr := "SELECT * FROM t_account WHERE user_name = ?" var res Db_Account if err := SqlDb.Get(&res, sqlStr, name); err != nil { log.Debug("登录的账号不存在:%s", name) return nil } return &res } func ResetAccountData(oldName, newName string) error { sqlStr := "UPDATE t_account SET user_name = ? WHERE user_name = ?" _, err := SqlDb.Exec(sqlStr, newName, oldName) if err != nil { return err } sqlStr = "UPDATE t_player_baseinfo SET user_name = ? WHERE user_name = ?" _, err = SqlDb.Exec(sqlStr, newName, oldName) return err } func MappingAccountData(oldName, newName string) error { sqlStr := "UPDATE t_account SET user_name = ? WHERE user_name = ?" _, err := SqlDb.Exec(sqlStr, "", oldName) if err != nil { return err } sqlStr = "UPDATE t_player_baseinfo SET user_name = ? WHERE user_name = ?" _, err = SqlDb.Exec(sqlStr, "", oldName) if err != nil { return err } sqlStr = "UPDATE t_account SET user_name = ? WHERE user_name = ?" _, err = SqlDb.Exec(sqlStr, oldName, newName) if err != nil { return err } sqlStr = "UPDATE t_player_baseinfo SET user_name = ? WHERE user_name = ?" _, err = SqlDb.Exec(sqlStr, oldName, newName) return err } func UpdateAccountInfoToDb(account *Db_Account) (err error) { _, err = SqlDb.Exec("UPDATE t_account SET user_password = ? WHERE user_name = ?", account.UserPassword, account.UserName) return } func UpdateAccountInfoName(account *Db_Account, newName string) (err error) { _, err = SqlDb.Exec("UPDATE t_account SET user_name = ? WHERE user_name = ?", newName, account.UserName) return } func UpdateAccountInfoDeviceToDb(account *Db_Account) (err error) { _, err = SqlDb.Exec("UPDATE t_account SET device_id = ? WHERE user_name = ?", account.DeviceId, account.UserName) return } func GetServerData(d interface{}, Key string) (err error) { sqlDb := GetDB() if sqlDb == nil { return fmt.Errorf("database connection is nil") } sql := "select * from t_server_mod where `key` = ?" err = sqlDb.Get(d, sql, Key) return } func SaveServerData(data *SqlServerModStruct) error { sqlDb := GetDB() if sqlDb == nil { return fmt.Errorf("database connection is nil") } sql := "update t_server_mod set `mData` = ? , `updateTime` = ? where `key` = ?" _, err := sqlDb.Exec(sql, data.ModData, data.UpdataTime, data.Key) return err } func SaveServerDataWithTx(tx *sql.Tx, data *SqlServerModStruct) error { sql := "update t_server_mod set `mData` = ? , `updateTime` = ? where `key` = ?" _, err := tx.Exec(sql, data.ModData, data.UpdataTime, data.Key) return err } func InsertServerData(data *SqlServerModStruct) error { sqlDb := GetDB() if sqlDb == nil { return fmt.Errorf("database connection is nil") } sql := "insert into t_server_mod (`mData` , `updateTime` ,`key`) Values (?,?,?)" _, err := sqlDb.Exec(sql, data.ModData, data.UpdataTime, data.Key) return err } func SavePlayerModData(data *SqlModStruct) error { sql := "INSERT INTO `t_player_mod` (`mData` , `updateTime` ,`dwUin`) Values (?,?,?) ON DUPLICATE KEY UPDATE `mData` = ? , `updateTime` = ? " _, err := SqlDb.Exec(sql, data.ModData, data.UpdataTime, data.DwUin, data.ModData, data.UpdataTime) return err } func InsertPlayerModData(data *SqlModStruct) error { sql := "insert into t_player_mod (`mData` , `updateTime` ,`dwUin`) Values (?,?,?)" _, err := SqlDb.Exec(sql, data.ModData, data.UpdataTime, data.DwUin) return err } func SavePlayerClientData(data *SqlModStruct) error { sql := "INSERT INTO `t_player_client_data` (`mData` , `updateTime` ,`dwUin`) Values (?,?,?) ON DUPLICATE KEY UPDATE `mData` = ? , `updateTime` = ? " _, err := SqlDb.Exec(sql, data.ModData, data.UpdataTime, data.DwUin, data.ModData, data.UpdataTime) return err } func GetPlayerClientData(d interface{}, Key string) (err error) { sql := "select * from t_server_mod where `dwUin` = ?" err = SqlDb.Get(d, sql, Key) return } func GetServerMailData(data *[]*SqlServerMailStruct) error { sql := "select * from system_mail_info" err := SqlDb.Select(data, sql) return err } func GetActivityData(data *[]*SqlActivityCfgStruct) error { sql := "select `id`, `type`, `title`, `mail_title`, `mail_content`, `level_limit`, `start_time`, `end_time`, `cfg_buf`, `extra` from t_activity_mod" err := SqlDb.Select(data, sql) return err } func CreateOrderSn(Uid, ChargeId int, OrderSn, Platform, Channel string, Price float64, Currency, Extra string) error { sql := "insert into t_player_charge (`Uid`,`OrderId`, `ProductId`, `Price`,`Currency`, `CreateTime`, `PayPlatform`, `PayChannel`, `PayChannelExtra`) Values (?,?,?,?,?,?,?,?,?)" Now := GoUtil.Now() _, err := SqlDb.Exec(sql, Uid, OrderSn, ChargeId, Price, Currency, Now, Platform, Channel, Extra) return err } func GetPlayerChargeData(OrderSn string) (*SqlChargeOrderStruct, error) { sql := "select * from t_player_charge where OrderId = ?" data := &SqlChargeOrderStruct{} err := SqlDb.Get(data, sql, OrderSn) return data, err } func GetPlayerChargeDataList(Uid int) ([]*SqlChargeOrderStruct, error) { sql := "select * from t_player_charge where Uid = ? and PayStatus = ?" data := &[]*SqlChargeOrderStruct{} err := SqlDb.Select(data, sql, Uid, MergeConst.ORDER_STATUS_PAY) return *data, err } func GetPlayerPayChannelOrderId(OrderSn string) (*SqlChargeOrderStruct, error) { sql := "select * from t_player_charge where PayChannelOrderId = ?" data := &SqlChargeOrderStruct{} err := SqlDb.Get(data, sql, OrderSn) return data, err } func UpdatePlayerChargeData(data *SqlChargeOrderStruct) error { sql := "update t_player_charge set PayTime = ?, PayStatus = ?, PayChannelOrderId = ? where OrderId = ?" _, err := SqlDb.Exec(sql, data.PayTime, data.PayStatus, data.PayChannelOrderId, data.OrderId) return err } func SearchPlayer(key string) ([]*ResPlayerBaseInfo, error) { sql := "select * from t_player_baseinfo where nick_name like ? limit 10" data := &[]*ResPlayerBaseInfo{} err := SqlDb.Select(data, sql, "%"+key+"%") return *data, err } func GetCommendPlayerFromDb(uid, login int64, level int) ([]int, error) { sqlStr := "SELECT dwUin FROM t_player_baseinfo WHERE dwUin != ? AND logout_time > ? AND level >= ? ORDER BY logout_time DESC LIMIT 1000" var res []int if err := SqlDb.Select(&res, sqlStr, uid, login, level); err != nil { log.Debug("table: %s, sql :%s, exec failed, err:%v\n", "PlayerBaseInfo", sqlStr, err) return nil, err } return res, nil } func GetDebugPlayer(uid int) ([]int, error) { sqlStr := "SELECT dwUin FROM t_player_baseinfo WHERE dwUin != ? ORDER BY login_time DESC LIMIT 1000" var res []int if err := SqlDb.Select(&res, sqlStr, uid); err != nil { log.Debug("table: %s, sql :%s, exec failed, err:%v\n", "PlayerBaseInfo", sqlStr, err) return nil, err } return res, nil }