package model import ( "context" "errors" "fmt" "github.com/go-redis/redis/v8" "gorm.io/gorm" "samba/pkg/log" "samba/stub" "samba/util/rdbkey" "samba/util/util" "strconv" ) const ( ResCoins = "coins" // 玩家金币 ResDiamond = "diamond" ResTakeCoins = "takecoins" ResClubUserScore = "score" // 俱乐部玩家身上的积分 t_club_member_info ResClubCoins = "club_coins" // 玩家身上的俱乐部币(仅部长身上有值) t_user_rmoney ResClubUserFreeScore = "free_score" // 官方赠送免费积分 t_club_member_info ResClubUserInOutFreeScore = "inout_free_score" // 可提现的免费积分 t_club_member_info ) var resourceMap = map[string]int{ ResCoins: 1000, ResDiamond: 1001, ResClubCoins: 1003, ResTakeCoins: 1004, ResClubUserScore: 1005, ResClubUserFreeScore: 1006, ResClubUserInOutFreeScore: 1007, } func ItemIdToRes(id stub.ItemId) string { switch id { case stub.Coin: return ResCoins case stub.ClubCoins: return ResClubCoins case stub.Diamonds: return ResDiamond } return "" } func ResToInt(res string) (int, error) { resInt, ok := resourceMap[res] if !ok { return -1, fmt.Errorf("has not resource type:%v", res) } return resInt, nil } func IntToRes(res int) (string, error) { for k, v := range resourceMap { if v == res { return k, nil } } return "", fmt.Errorf("has not resource id:%v", res) } const ( ReasonEmote = "emote" ReasonGame = "game" ReasonEnter = "enter_room" ReasonLeave = "leave_room" ReasonSubsidy = "subsidy" // 破产补贴 //ReasonRobot = "robot" ) type UserResource struct { UID int64 `gorm:"column:user_id" json:"user_id"` Coins int64 `gorm:"column:coins" json:"coins"` // 通用金币,通过商城购买得到 PayCoins int64 `gorm:"column:pay_coins" json:"pay_coins"` // 当前付费筹码数 已废弃 FreeCoins int64 `gorm:"column:free_coins" json:"free_coins"` // 当前免费筹码数 已废弃 PayTotal int64 `gorm:"column:pay_total" json:"pay_total"` // 累计付费筹码数 已废弃 BankCoins int64 `gorm:"column:bank_coins" json:"bank_coins"` // Diamond int64 `gorm:"column:diamond" json:"diamond"` // 钻石 ClubCoins int64 `gorm:"column:club_coins" json:"club_coins"` // 俱乐部币 } func (t *UserResource) TableName() string { return "t_user_rmoney" } type UserResourceOp struct { rdb *redis.Client db *gorm.DB } func NewUserResourceOp() *UserResourceOp { return &UserResourceOp{rdb: rdbMoney, db: userDB} } func (op *UserResourceOp) Load(uid int64) *UserResource { res, err := util.Redis2Struct[UserResource](op.rdb, rdbkey.UserResourceKey(uid)) if err == nil { return res } result := op.db.Where("user_id = ?", uid).First(&res) if result.Error != nil { // 没有该数据 if errors.Is(result.Error, gorm.ErrRecordNotFound) { res.UID = uid result = op.db.Create(res) if result.Error != nil { log.Error(result.Error.Error()) return nil } } else { log.Error(fmt.Sprintf("read db error:%v user:%v ", result.Error, uid)) return nil } } // 回写redis err = util.Struct2Redis(op.rdb, rdbkey.UserResourceKey(uid), res) if err != nil { log.Error(fmt.Sprintf("serialize user:%v resource:%v error: %v", uid, res, err)) return nil } _ = rdbBaseInfo.Expire(context.Background(), rdbkey.UserResourceKey(uid), redis30day).Err() return res } func (op *UserResourceOp) Get(uid int64, resType string) (int64, error) { if resType == "" { resType = ResCoins } res := op.Load(uid) if res == nil { return 0, fmt.Errorf("Load user:%v resource:%v fail", uid, resType) } switch resType { case ResCoins: return res.Coins, nil case ResDiamond: return res.Diamond, nil case ResClubCoins: return res.ClubCoins, nil default: return 0, fmt.Errorf("not add resource:%v user:%v resource:%v fail", resType, uid, resType) } } func (op *UserResourceOp) GetTakeCoin(uid int64) (int64, error) { val, err := op.rdb.HGet(context.Background(), rdbkey.UserResourceKey(uid), ResTakeCoins).Result() if err == nil { res, err := strconv.ParseInt(val, 10, 64) return res, err } if errors.Is(err, redis.Nil) { _ = op.Load(uid) _, _ = op.AddTakeCoin(uid, 0) return 0, nil } return 0, err } func (op *UserResourceOp) AddTakeCoin(uid int64, takeCoin int64) (int64, bool) { res, err := op.rdb.HIncrBy(context.Background(), rdbkey.UserResourceKey(uid), ResTakeCoins, takeCoin).Result() if err != nil { log.Error(err.Error()) return 0, false } return res, true } func (op *UserResourceOp) Add(clubId int, uid, add int64, resType string) (int64, error) { if resType == "" { resType = ResCoins } res, err := op.Get(uid, resType) if err != nil { err = fmt.Errorf("user:%v add res:%v value:%v fail.err:%v", uid, resType, add, err.Error()) log.Error(err.Error()) return 0, err } //if res+add < 0 { // err = fmt.Errorf("user:%v add res:%v add value:%v + current value:%v<0", uid, resType, add, res) // log.Error(err.Error()) // return 0, err //} res, err = op.rdb.HIncrBy(context.Background(), rdbkey.UserResourceKey(uid), resType, add).Result() if err != nil { err = fmt.Errorf("user:%v add res:%v value:%v fail.err:%v", uid, resType, add, err.Error()) log.Error(err.Error()) return 0, err } _ = rdbBaseInfo.Expire(context.Background(), rdbkey.UserResourceKey(uid), redis30day).Err() expr := fmt.Sprintf("%v + ?", resType) err = op.db.Model(&UserResource{}).Where("user_id = ?", uid).Update(resType, gorm.Expr(expr, add)).Error if err != nil { err = fmt.Errorf("user:%v add res:%v value:%v fail.err:%v", uid, resType, add, err.Error()) log.Error(err.Error()) } // 消耗俱乐部币添加等级分 if resType == ResClubCoins && add < 0 { expType := "exp" _, err = op.rdb.HIncrBy(context.Background(), rdbkey.ClubKey(clubId), expType, -add).Result() if err != nil { err = fmt.Errorf("key:%v add res:%v value:%v fail.err:%v", rdbkey.ClubKey(clubId), resType, -add, err.Error()) log.Error(err.Error()) return 0, err } expr := fmt.Sprintf("%v + ?", expType) err = op.db.Model(&ClubInfo{}).Where("id = ?", clubId).Update(expType, gorm.Expr(expr, -add)).Error if err != nil { err = fmt.Errorf("club:%v add res:%v value:%v fail.err:%v", clubId, expType, -add, err.Error()) log.Error(err.Error()) } } return res, err } func CleanTakeCoin() { cursor := uint64(0) var keys []string var err error num := 0 for { keys, cursor, err = rdbBaseInfo.Scan(context.Background(), cursor, "user|resource|*", 100).Result() if err != nil { log.Error(err.Error()) break } if cursor == 0 { break } for _, key := range keys { _, _ = rdbBaseInfo.HSet(context.Background(), key, ResTakeCoins, 0).Result() log.Debug(fmt.Sprintf("clean key:%v takecoin", key)) num++ } } log.Debug(fmt.Sprintf("over.num:%v", num)) } // UserIsBankrupt 用户是否达到破产条件 // offset: 可选参数,用户当前的金币+-某个值 func UserIsBankrupt(uid int64, offset ...int64) bool { op := NewUserResourceOp() coins, err := op.Get(uid, ResCoins) if err != nil { return false } if len(offset) > 0 { coins += offset[0] } return stub.IsBankrupt(coins) }