pet_home_server/src/server/db/Mysql.go
2026-04-03 16:25:17 +08:00

483 lines
14 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package db
import (
"database/sql"
"fmt"
"reflect"
"server/MergeConst"
"server/conf"
GoUtil "server/game_util"
"strings"
"sync"
"time"
"gitea.bywaystudios.com/pet_home/leaf/log"
// "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`, `interval` 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
}