samba/util/model/userResource.go

257 lines
7.2 KiB
Go
Raw Permalink Normal View History

2025-06-04 09:51:39 +08:00
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)
}