From 6f0d7edcdb6442caedc23a56a03f3b27eceb752e Mon Sep 17 00:00:00 2001 From: dengbiao Date: Wed, 13 Nov 2024 19:46:09 +0800 Subject: [PATCH] add user_willet --- enum/sys_cfg.go | 1 + md/app_redis_key.go | 2 +- md/user_wallet.go | 13 ++ rule/egg_energy/give_activty_coin.go | 4 +- rule/user_virtual_coin.go | 2 +- rule/user_wallet.go | 121 ++++++++++++++++++ ...vc_user_virtual_amount_redis_mutex_lock.go | 21 ++- 7 files changed, 158 insertions(+), 6 deletions(-) create mode 100644 enum/sys_cfg.go create mode 100644 md/user_wallet.go create mode 100644 rule/user_wallet.go diff --git a/enum/sys_cfg.go b/enum/sys_cfg.go new file mode 100644 index 0000000..32558fb --- /dev/null +++ b/enum/sys_cfg.go @@ -0,0 +1 @@ +package enum diff --git a/md/app_redis_key.go b/md/app_redis_key.go index f870c6a..3d570a5 100644 --- a/md/app_redis_key.go +++ b/md/app_redis_key.go @@ -7,7 +7,7 @@ const ( EggEnergyNowPriceUpdateLock = "egg_energy_core_data_update_lock" // 当前价格(能拿到锁才能更新价格) DealEggEnergyNowPriceRequestIdPrefix = "egg_energy_core_data:%d" - UserFinValidUpdateLock = "%s:user_fin_valid_update_lock:%s" // 用户余额更新锁(能拿到锁才能更新余额) + UserFinValidUpdateLock = "user_fin_valid_update_lock:%s" // 用户余额更新锁(能拿到锁才能更新余额) UserVirtualAmountUpdateLock = "%s:user_virtual_amount_update_lock:%s" // 用户虚拟币更新锁(能拿到锁才能更新余额) ) diff --git a/md/user_wallet.go b/md/user_wallet.go new file mode 100644 index 0000000..53e9cac --- /dev/null +++ b/md/user_wallet.go @@ -0,0 +1,13 @@ +package md + +const ( + UserWalletRedisKey = "user_wallet_user:%d" +) + +type DealUserWalletReq struct { + Direction string `json:"direction"` + Kind int `json:"kind"` + Title string `json:"title"` + Uid int64 `json:"uid"` + Amount float64 `json:"amount"` +} diff --git a/rule/egg_energy/give_activty_coin.go b/rule/egg_energy/give_activty_coin.go index 14267ba..7597387 100644 --- a/rule/egg_energy/give_activty_coin.go +++ b/rule/egg_energy/give_activty_coin.go @@ -142,8 +142,8 @@ func SettlementGiveActivityCoin(engine *xorm.Engine, uid int64, ch *rabbit.Chann Uid: uid, NextWatchAdDate: now.Add(time.Hour * time.Duration(egg_system_rules.StrToInt(videoRewardSystem.EachRoundHour))), ResidueWatchAdNum: egg_system_rules.StrToInt(videoRewardSystem.RewardTotalNum) - 1, - CreateAt: now, - UpdateAt: now, + CreateAt: now.Format("2006-01-02 15:04:05"), + UpdateAt: now.Format("2006-01-02 15:04:05"), }) if err2 != nil { return err2 diff --git a/rule/user_virtual_coin.go b/rule/user_virtual_coin.go index 33be61e..c5e757e 100644 --- a/rule/user_virtual_coin.go +++ b/rule/user_virtual_coin.go @@ -23,7 +23,7 @@ func DealUserVirtualCoin(session *xorm.Session, req md.DealUserVirtualCoinReq) ( } //1、分布式锁阻拦 requestIdPrefix := fmt.Sprintf(md2.DealUserCoinRequestIdPrefix, req.CoinId, req.Uid) - cb, err := svc.HandleDistributedLock(zhios_order_relate_utils.Int64ToStr(req.Uid), requestIdPrefix, strconv.Itoa(req.CoinId)) + cb, err := svc.HandleDistributedLock(zhios_order_relate_utils.Int64ToStr(req.Uid), strconv.Itoa(req.CoinId), requestIdPrefix) if err != nil { return err } diff --git a/rule/user_wallet.go b/rule/user_wallet.go new file mode 100644 index 0000000..00179c9 --- /dev/null +++ b/rule/user_wallet.go @@ -0,0 +1,121 @@ +package rule + +import ( + "code.fnuoos.com/EggPlanet/egg_models.git/src/implement" + "code.fnuoos.com/EggPlanet/egg_models.git/src/model" + zhios_order_relate_utils "code.fnuoos.com/EggPlanet/egg_models.git/utils" + "code.fnuoos.com/EggPlanet/egg_system_rules.git/md" + "code.fnuoos.com/EggPlanet/egg_system_rules.git/svc" + "code.fnuoos.com/EggPlanet/egg_system_rules.git/utils/cache" + "errors" + "fmt" + "github.com/shopspring/decimal" + "time" + "xorm.io/xorm" +) + +// DealUserWallet 处理给用户金额 +func DealUserWallet(session *xorm.Session, req md.DealUserWalletReq) (err error) { + if req.Amount < 0 { + req.Amount = 0 + } + //1、分布式锁阻拦 + requestIdPrefix := fmt.Sprintf(md.UserWalletRedisKey, req.Uid) + cb, err := svc.HandleDistributedLockForUserWallet(zhios_order_relate_utils.Int64ToStr(req.Uid), requestIdPrefix) + if err != nil { + return err + } + if cb != nil { + defer cb() // 释放锁 + } + + //2、计算&&组装数据 + now := time.Now() + userAmount, err := GetUserWalletAmount(session, req.Uid) + if err != nil { + return err + } + amountValue := decimal.NewFromFloat(req.Amount).RoundFloor(4) + + var userWalletFlow model.UserWalletFlow + userWalletFlow.Uid = req.Uid + userWalletFlow.Title = req.Title + userWalletFlow.Kind = req.Kind + userWalletFlow.BeforeAmount = userAmount + userWalletFlow.Amount = amountValue.String() + userWalletFlow.CreateAt = now.Format("2006-01-02 15:04:05") + + if req.Direction == "add" { + userWalletFlow.Direction = 1 + userWalletFlow.AfterAmount = amountValue.Add(amountValue).RoundFloor(8).String() + } else if req.Direction == "sub" { + userWalletFlow.Direction = 2 + userWalletFlow.AfterAmount = amountValue.Sub(amountValue).RoundFloor(8).String() + if zhios_order_relate_utils.StrToFloat64(userWalletFlow.AfterAmount) < 0 { + return errors.New("用户钱包余额不足") + } + } else { + err = errors.New("错误的Direction类型") + return err + } + + //3、插入 `user_wallet_flow` 记录 + userWalletFlowDb := implement.NewUserWalletFlowDb(session.Engine()) + _, err = userWalletFlowDb.UserWalletFlowInsertBySession(session, &userWalletFlow) + if err != nil { + return err + } + + //4、修改 `user_wallet`的amount值 && 及缓存 + err = SetUserWalletAmount(session, userWalletFlow.AfterAmount, req.Uid, true) + if err != nil { + return err + } + + return nil +} + +// GetUserWalletAmount 获取用户钱包余额 +func GetUserWalletAmount(session *xorm.Session, uid int64) (amount string, err error) { + redisKey := fmt.Sprintf(md.UserWalletRedisKey, uid) + amount, err = cache.GetString(redisKey) + if err != nil { + if err.Error() == "redigo: nil returned" { + userWalletDb := implement.NewUserWalletDb(session.Engine()) + userVirtualAmount, err := userWalletDb.GetUserVirtualWallet(uid) + if err != nil { + return amount, err + } + if userVirtualAmount == nil { + amount = "0" + } else { + amount = userVirtualAmount.Amount + } + //将获取到的余额值缓存至redis + _ = SetUserWalletAmount(session, amount, uid, false) + return amount, nil + } + return amount, err + } + return amount, nil +} + +// SetUserWalletAmount 设置缓存的用户虚拟币积分余额 +func SetUserWalletAmount(session *xorm.Session, amount string, uid int64, isUpdateDb bool) error { + redisKey := fmt.Sprintf(md.UserWalletRedisKey, uid) + if isUpdateDb { + _, err := session.Where("uid=?", uid).Update(model.UserWallet{ + Uid: uid, + Amount: amount, + }) + if err != nil { + return err + } + } + //TODO::默认缓存1小时 (先调整为 20 min) + _, err := cache.SetEx(redisKey, zhios_order_relate_utils.StrToFloat64(amount), 60*20) + if err != nil { + return err + } + return nil +} diff --git a/svc/svc_user_virtual_amount_redis_mutex_lock.go b/svc/svc_user_virtual_amount_redis_mutex_lock.go index d70121b..80b9794 100644 --- a/svc/svc_user_virtual_amount_redis_mutex_lock.go +++ b/svc/svc_user_virtual_amount_redis_mutex_lock.go @@ -66,9 +66,9 @@ func GetDistributedLockRequestId(prefix string) string { return prefix + zhios_order_relate_utils.IntToStr(rand.Intn(100000000)) } -// HandleDistributedLock 处理余额更新时获取锁和释放锁 如果加锁成功,使用语句 ` defer cb() ` 释放锁 +// HandleDistributedLock 处理虚拟币更新时获取锁和释放锁 如果加锁成功,使用语句 ` defer cb() ` 释放锁 func HandleDistributedLock(uid, coinId, requestIdPrefix string) (cb func(), err error) { - // 获取余额更新锁 + // 获取虚拟币更新锁 balanceLockKey := fmt.Sprintf(md.UserVirtualAmountUpdateLock, uid, coinId) requestId := GetDistributedLockRequestId(requestIdPrefix) balanceLockOk := TryGetDistributedLock(balanceLockKey, requestId, true) @@ -82,3 +82,20 @@ func HandleDistributedLock(uid, coinId, requestIdPrefix string) (cb func(), err return cb, nil } + +// HandleDistributedLockForUserWallet 处理余额更新时获取锁和释放锁 如果加锁成功,使用语句 ` defer cb() ` 释放锁 +func HandleDistributedLockForUserWallet(uid, requestIdPrefix string) (cb func(), err error) { + // 获取虚拟币更新锁 + balanceLockKey := fmt.Sprintf(md.UserFinValidUpdateLock, uid) + requestId := GetDistributedLockRequestId(requestIdPrefix) + balanceLockOk := TryGetDistributedLock(balanceLockKey, requestId, true) + if !balanceLockOk { + return nil, errors.New("系统繁忙,请稍后再试") + } + + cb = func() { + _, _ = ReleaseDistributedLock(balanceLockKey, requestId) + } + + return cb, nil +}