@@ -0,0 +1 @@ | |||||
package enum |
@@ -7,7 +7,7 @@ const ( | |||||
EggEnergyNowPriceUpdateLock = "egg_energy_core_data_update_lock" // 当前价格(能拿到锁才能更新价格) | EggEnergyNowPriceUpdateLock = "egg_energy_core_data_update_lock" // 当前价格(能拿到锁才能更新价格) | ||||
DealEggEnergyNowPriceRequestIdPrefix = "egg_energy_core_data:%d" | 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" // 用户虚拟币更新锁(能拿到锁才能更新余额) | UserVirtualAmountUpdateLock = "%s:user_virtual_amount_update_lock:%s" // 用户虚拟币更新锁(能拿到锁才能更新余额) | ||||
) | ) |
@@ -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"` | |||||
} |
@@ -142,8 +142,8 @@ func SettlementGiveActivityCoin(engine *xorm.Engine, uid int64, ch *rabbit.Chann | |||||
Uid: uid, | Uid: uid, | ||||
NextWatchAdDate: now.Add(time.Hour * time.Duration(egg_system_rules.StrToInt(videoRewardSystem.EachRoundHour))), | NextWatchAdDate: now.Add(time.Hour * time.Duration(egg_system_rules.StrToInt(videoRewardSystem.EachRoundHour))), | ||||
ResidueWatchAdNum: egg_system_rules.StrToInt(videoRewardSystem.RewardTotalNum) - 1, | 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 { | if err2 != nil { | ||||
return err2 | return err2 | ||||
@@ -23,7 +23,7 @@ func DealUserVirtualCoin(session *xorm.Session, req md.DealUserVirtualCoinReq) ( | |||||
} | } | ||||
//1、分布式锁阻拦 | //1、分布式锁阻拦 | ||||
requestIdPrefix := fmt.Sprintf(md2.DealUserCoinRequestIdPrefix, req.CoinId, req.Uid) | 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 { | if err != nil { | ||||
return err | return err | ||||
} | } | ||||
@@ -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 | |||||
} |
@@ -66,9 +66,9 @@ func GetDistributedLockRequestId(prefix string) string { | |||||
return prefix + zhios_order_relate_utils.IntToStr(rand.Intn(100000000)) | 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) { | func HandleDistributedLock(uid, coinId, requestIdPrefix string) (cb func(), err error) { | ||||
// 获取余额更新锁 | |||||
// 获取虚拟币更新锁 | |||||
balanceLockKey := fmt.Sprintf(md.UserVirtualAmountUpdateLock, uid, coinId) | balanceLockKey := fmt.Sprintf(md.UserVirtualAmountUpdateLock, uid, coinId) | ||||
requestId := GetDistributedLockRequestId(requestIdPrefix) | requestId := GetDistributedLockRequestId(requestIdPrefix) | ||||
balanceLockOk := TryGetDistributedLock(balanceLockKey, requestId, true) | balanceLockOk := TryGetDistributedLock(balanceLockKey, requestId, true) | ||||
@@ -82,3 +82,20 @@ func HandleDistributedLock(uid, coinId, requestIdPrefix string) (cb func(), err | |||||
return cb, nil | 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 | |||||
} |