package svc import ( "applet/app/md" "applet/app/utils/cache" db "code.fnuoos.com/zhimeng/model.git/src" "code.fnuoos.com/zhimeng/model.git/src/super/implement" "code.fnuoos.com/zhimeng/model.git/src/super/model" zhios_order_relate_utils "code.fnuoos.com/zhimeng/model.git/utils" zhios_order_relate_logx "code.fnuoos.com/zhimeng/model.git/utils/logx" "errors" "fmt" "github.com/shopspring/decimal" "strconv" "time" "xorm.io/xorm" ) // DealMediumAmount 处理给媒体余额 func DealMediumAmount(session *xorm.Session, req md.DealMediumAmount) (err error) { if req.Amount < 0 { req.Amount = 0 } //1、分布式锁阻拦 requestIdPrefix := fmt.Sprintf(md.DealMediumAmountRequestIdPrefix, req.Mid, req.MediumId) cb, err := HandleBalanceDistributedLockForMedium(req.Mid, strconv.Itoa(req.MediumId), requestIdPrefix) if err != nil { return err } if cb != nil { defer cb() // 释放锁 } //2、计算&&组装数据 now := time.Now() userAmount, err := GetMediumAmount(session, req.Mid, req.MediumId, true) if err != nil { return err } userAmountValue := decimal.NewFromFloat(zhios_order_relate_utils.StrToFloat64(userAmount)) amountValue := decimal.NewFromFloat(req.Amount).RoundFloor(4) var finUserFlow = model.FinMediumFlow{ MediumId: req.MediumId, Type: req.Type, Amount: amountValue.String(), BeforeAmount: userAmount, OrdId: req.OrdId, Kind: req.Kind, Memo: req.Memo, CreateAt: now.Format("2006-01-02 15:04:05"), UpdateAt: now.Format("2006-01-02 15:04:05"), } if req.Type == md.FinMediumFlowDirectionIncome { finUserFlow.AfterAmount = userAmountValue.Add(amountValue).RoundFloor(4).String() } else if req.Type == md.FinMediumFlowDirectionExpenditure { finUserFlow.AfterAmount = userAmountValue.Sub(amountValue).RoundFloor(4).String() } else { err = errors.New("错误的kind类型") return err } if zhios_order_relate_utils.StrToFloat64(finUserFlow.AfterAmount) < 0 { zhios_order_relate_utils.FilePutContents("medium_amount_not", zhios_order_relate_utils.SerializeStr(map[string]interface{}{ "medium_id": finUserFlow.MediumId, "amount": finUserFlow.Amount, "before_amount": finUserFlow.BeforeAmount, "memo": finUserFlow.Memo, "mid": req.Mid, })) return errors.New("媒体余额不足") } //3、插入 `fin_user_flow` 记录 affected, err := session.Insert(&finUserFlow) if affected == 0 || err != nil { _ = zhios_order_relate_logx.Warn(err) return err } //4、修改 `user_profile`的fin_valid值 && 及缓存 err = SetCacheMediumAmount(session, req.Mid, finUserFlow.AfterAmount, req.MediumId, true) if err != nil { return err } return nil } // GetMediumAmount 获取媒体余额 func GetMediumAmount(session *xorm.Session, masterId string, mediumId int, isForceUpdate bool) (amount string, err error) { if isForceUpdate { mediumListDb := implement.NewMediumListDb(db.Db) medium, err1 := mediumListDb.GetMediumListBySession(session, mediumId) if err1 != nil { return amount, err1 } if medium == nil { amount = "0" } else { amount = medium.Amount } //将获取到的余额值缓存至redis _ = SetCacheMediumAmount(session, masterId, amount, mediumId, false) } else { redisKey := fmt.Sprintf(md.MediumAmountRedisKey, masterId, mediumId) amount, err = cache.GetString(redisKey) if err != nil && err.Error() != "redigo: nil returned" { return } mediumListDb := implement.NewMediumListDb(db.Db) medium, err1 := mediumListDb.GetMediumListBySession(session, mediumId) if err1 != nil { return amount, err1 } if medium == nil { amount = "0" } else { amount = medium.Amount } //将获取到的余额值缓存至redis _ = SetCacheMediumAmount(session, masterId, amount, mediumId, false) } return amount, nil } // SetCacheMediumAmount 设置缓存的用户余额 func SetCacheMediumAmount(session *xorm.Session, masterId, amount string, mediumId int, isUpdateDb bool) error { redisKey := fmt.Sprintf(md.MediumAmountRedisKey, masterId, mediumId) if isUpdateDb { _, err := session.Where("medium_id=?", mediumId).Update(model.MediumList{ MediumId: mediumId, Amount: amount, }) if err != nil { return err } } //TODO::默认缓存1小时 (先调整为 2 min) _, err := cache.SetEx(redisKey, zhios_order_relate_utils.StrToFloat64(amount), 60*0.5) if err != nil { return err } return nil }