huangjiajun 1 anno fa
parent
commit
6e9db42130
21 ha cambiato i file con 855 aggiunte e 37 eliminazioni
  1. +0
    -15
      db/db_day_luck_draw_order_jackpot.go
  2. +2
    -1
      db/db_day_luck_draw_period.go
  3. +3
    -3
      db/db_day_luck_draw_period_reward.go
  4. +19
    -0
      db/db_day_luck_draw_setting.go
  5. +38
    -0
      db/db_day_luck_draw_settle_order.go
  6. +9
    -0
      db/db_user.go
  7. +0
    -3
      db/model/day_luck_draw_order_jackpot.go
  8. +1
    -0
      db/model/day_luck_draw_period.go
  9. +16
    -0
      db/model/day_luck_draw_settle_order.go
  10. +29
    -0
      db/model/fin_user_flow.go
  11. +91
    -0
      db/model/user_profile.go
  12. +1
    -0
      go.mod
  13. +20
    -0
      md/app_redis_key.go
  14. +1
    -0
      md/day_luck_draw_setting.go
  15. +45
    -0
      md/fin_user_flow.go
  16. +2
    -11
      svc/svc_day_luck_draw_order_jackpot.go
  17. +325
    -4
      svc/svc_day_luck_draw_reward.go
  18. +19
    -0
      svc/svc_day_luck_draw_settle_order.go
  19. +126
    -0
      svc/svc_deal_user_amount.go
  20. +84
    -0
      svc/svc_redis_mutex_lock.go
  21. +24
    -0
      svc/svc_redis_mutex_lock_for_amount.go

+ 0
- 15
db/db_day_luck_draw_order_jackpot.go Vedi File

@@ -17,9 +17,6 @@ func AddOrderJackpot(eg *xorm.Engine, req md.DayLuckDrawOrderJackpotReq) error {
var data = &model.DayLuckDrawOrderJackpot{
Oid: req.Oid,
Pvd: req.Pvd,
Amount: req.Amount,
Commission: req.Commission,
Reward: req.Reward,
Uid: zhios_day_luck_draw_utils.StrToInt(req.Uid),
CreateTime: time.Now(),
}
@@ -33,18 +30,6 @@ func AddOrderJackpot(eg *xorm.Engine, req md.DayLuckDrawOrderJackpotReq) error {
return nil
}

/***
1.创建期数
2.获取还没分配的金额 就是 期数为0的
3.把期数设置到订单
4.查出所有订单号
5.随机出订单号(每个名额随机一次 如果重复再次循环) 先按一等奖分
*/

func GetAllOrderSum(sess *xorm.Session) float64 {
sum, _ := sess.Where("period=0").Sum(&model.DayLuckDrawOrderJackpot{}, "reward")
return sum
}
func GetAllOrderOid(sess *xorm.Session) []string {
sql := `SELECT oid,uid FROM day_luck_draw_order_jackpot where period=0;`
data, _ := QueryNativeStringSess(sess, sql)


+ 2
- 1
db/db_day_luck_draw_period.go Vedi File

@@ -8,11 +8,12 @@ import (
"xorm.io/xorm"
)

func AddPeriod(sess *xorm.Session, req md.DayLuckDrawPeriodReq) bool {
func AddPeriod(sess *xorm.Session, req md.DayLuckDrawPeriodReq, base string) bool {
var data = &model.DayLuckDrawPeriod{
Reward: req.Reward,
CreateTime: time.Now(),
Period: zhios_day_luck_draw_utils.StrToInt(req.Period),
Base: base,
}
insert, err := sess.Insert(data)
if insert == 0 || err != nil {


+ 3
- 3
db/db_day_luck_draw_period_reward.go Vedi File

@@ -9,8 +9,8 @@ import (
"xorm.io/xorm"
)

func AddPeriodReward(eg *xorm.Engine, req md.DayLuckDrawRewardReq) error {
count, _ := eg.Where("uid=? and period=?", req.Uid, req.Period).Count(&model.DayLuckDrawPeriodReward{})
func AddPeriodReward(sess *xorm.Session, req md.DayLuckDrawRewardReq) error {
count, _ := sess.Where("uid=? and period=?", req.Uid, req.Period).Count(&model.DayLuckDrawPeriodReward{})
if count > 0 {
return nil
}
@@ -21,7 +21,7 @@ func AddPeriodReward(eg *xorm.Engine, req md.DayLuckDrawRewardReq) error {
Period: zhios_day_luck_draw_utils.StrToInt(req.Period),
Lv: zhios_day_luck_draw_utils.StrToInt(req.Lv),
}
one, err := eg.InsertOne(data)
one, err := sess.Insert(data)
if one == 0 {
return errors.New("失败")
}


+ 19
- 0
db/db_day_luck_draw_setting.go Vedi File

@@ -2,6 +2,8 @@ package db

import (
"code.fnuoos.com/go_rely_warehouse/zyos_go_day_luck_draw.git/db/model"
zhios_day_luck_draw_utils "code.fnuoos.com/go_rely_warehouse/zyos_go_day_luck_draw.git/utils"
"time"
"xorm.io/xorm"
)

@@ -19,6 +21,23 @@ func GetDayLuckDrawSetting(eg *xorm.Engine) *model.DayLuckDrawSetting {
}
return &data
}
func CheckSetting(eg *xorm.Engine, pvd string) bool {
setting := GetDayLuckDrawSetting(eg)
if setting == nil {
return false
}
now := time.Now().Unix()
if setting.IsUse == 0 || setting.StartTime.IsZero() || setting.EndTime.IsZero() {
return false
}
if now < setting.StartTime.Unix() || now > setting.EndTime.Unix() {
return false
}
if setting.OrderSoruce == "mall" && pvd != "" && zhios_day_luck_draw_utils.InArr(pvd, []string{"mall_goods", "mall_group_buy", "mall_goods_user_lv", "super_group_buy", "mall_supply", "mall_group_own_buy"}) == false {
return false
}
return true
}
func GetDayLuckDrawSettingSess(sess *xorm.Session) *model.DayLuckDrawSetting {
var data model.DayLuckDrawSetting
get, err := sess.Get(&data)


+ 38
- 0
db/db_day_luck_draw_settle_order.go Vedi File

@@ -0,0 +1,38 @@
package db

import (
"code.fnuoos.com/go_rely_warehouse/zyos_go_day_luck_draw.git/db/model"
"code.fnuoos.com/go_rely_warehouse/zyos_go_day_luck_draw.git/md"
zhios_day_luck_draw_utils "code.fnuoos.com/go_rely_warehouse/zyos_go_day_luck_draw.git/utils"
"errors"
"time"
"xorm.io/xorm"
)

func GetSettleAllOrderSum(sess *xorm.Session) float64 {
sum, _ := sess.Where("is_send=0").Sum(&model.DayLuckDrawSettleOrder{}, "commission")
return sum
}

func AddSettle(eg *xorm.Engine, req md.DayLuckDrawOrderJackpotReq) error {
count, _ := eg.Where("oid=? and uid=? and pvd=?", req.Oid, req.Uid, req.Pvd).Count(&model.DayLuckDrawSettleOrder{})
if count > 0 {
return nil
}
var data = &model.DayLuckDrawSettleOrder{
Oid: req.Oid,
Pvd: req.Pvd,
Amount: req.Amount,
Commission: req.Commission,
Uid: zhios_day_luck_draw_utils.StrToInt(req.Uid),
CreateTime: time.Now(),
}
one, err := eg.InsertOne(data)
if one == 0 {
return errors.New("失败")
}
if err != nil {
return err
}
return nil
}

+ 9
- 0
db/db_user.go Vedi File

@@ -14,3 +14,12 @@ func UserFindByMobile(Db *xorm.Engine, mobile string) (*model.User, error) {
}
return &m, nil
}

func UserProfileFindByIDWithSession(sess *xorm.Session, id interface{}) (*model.UserProfile, error) {
var m model.UserProfile
if has, err := sess.Where("uid = ?", id).
Get(&m); err != nil || has == false {
return nil, err
}
return &m, nil
}

+ 0
- 3
db/model/day_luck_draw_order_jackpot.go Vedi File

@@ -8,9 +8,6 @@ type DayLuckDrawOrderJackpot struct {
Id int `json:"id" xorm:"not null pk autoincr INT(11)"`
Oid string `json:"oid" xorm:"index unique(oid_pvd) VARCHAR(255)"`
Pvd string `json:"pvd" xorm:"unique(oid_pvd) index VARCHAR(255)"`
Amount string `json:"amount" xorm:"DECIMAL(11,6)"`
Commission string `json:"commission" xorm:"DECIMAL(11,6)"`
Reward string `json:"reward" xorm:"DECIMAL(11,6)"`
Uid int `json:"uid" xorm:"index INT(11)"`
CreateTime time.Time `json:"create_time" xorm:"DATETIME"`
Period int `json:"period" xorm:"default 0 INT(11)"`


+ 1
- 0
db/model/day_luck_draw_period.go Vedi File

@@ -7,6 +7,7 @@ import (
type DayLuckDrawPeriod struct {
Id int `json:"id" xorm:"not null pk autoincr INT(11)"`
Reward string `json:"reward" xorm:"VARCHAR(255)"`
Base string `json:"base" xorm:"TEXT"`
CreateTime time.Time `json:"create_time" xorm:"DATETIME"`
Period int `json:"period" xorm:"default 0 INT(11)"`
}

+ 16
- 0
db/model/day_luck_draw_settle_order.go Vedi File

@@ -0,0 +1,16 @@
package model

import (
"time"
)

type DayLuckDrawSettleOrder struct {
Id int `json:"id" xorm:"not null pk autoincr INT(11)"`
Oid string `json:"oid" xorm:"index unique(oid_pvd) VARCHAR(255)"`
Pvd string `json:"pvd" xorm:"unique(oid_pvd) index VARCHAR(255)"`
Amount string `json:"amount" xorm:"DECIMAL(11,6)"`
Commission string `json:"commission" xorm:"DECIMAL(11,6)"`
Uid int `json:"uid" xorm:"index INT(11)"`
CreateTime time.Time `json:"create_time" xorm:"DATETIME"`
IsSend int `json:"is_send" xorm:"default 0 INT(11)"`
}

+ 29
- 0
db/model/fin_user_flow.go Vedi File

@@ -0,0 +1,29 @@
package model

import (
"time"
)

type FinUserFlow struct {
Id int64 `json:"id" xorm:"pk autoincr comment('流水编号') BIGINT(20)"`
Uid int `json:"uid" xorm:"not null default 0 comment('用户id') INT(11)"`
Type int `json:"type" xorm:"not null default 0 comment('0收入,1支出') TINYINT(1)"`
Amount string `json:"amount" xorm:"not null default 0.0000 comment('变动金额') DECIMAL(11,4)"`
BeforeAmount string `json:"before_amount" xorm:"not null default 0.0000 comment('变动前金额') DECIMAL(11,4)"`
AfterAmount string `json:"after_amount" xorm:"not null default 0.0000 comment('变动后金额') DECIMAL(11,4)"`
SysFee string `json:"sys_fee" xorm:"not null default 0.0000 comment('手续费') DECIMAL(11,4)"`
PaymentType int `json:"payment_type" xorm:"not null default 1 comment('1支付宝,2微信.3手动转账') TINYINT(1)"`
OrdType string `json:"ord_type" xorm:"not null default '' comment('订单类型taobao,jd,pdd,vip,suning,kaola,own自营,withdraw提现') VARCHAR(20)"`
OrdId string `json:"ord_id" xorm:"not null default '' comment('对应订单编号') VARCHAR(50)"`
OrdTitle string `json:"ord_title" xorm:"not null default '' comment('订单标题') VARCHAR(50)"`
OrdAction int `json:"ord_action" xorm:"not null default 0 comment('10自购,11推广,12团队,20提现,21消费') TINYINT(2)"`
OrdTime int `json:"ord_time" xorm:"not null default 0 comment('下单时间or提现时间') INT(11)"`
OrdDetail string `json:"ord_detail" xorm:"not null default '' comment('记录商品ID或提现账号') VARCHAR(50)"`
ExpectedTime string `json:"expected_time" xorm:"not null default '0' comment('预期到账时间,字符串用于直接显示,结算后清除内容') VARCHAR(30)"`
State int `json:"state" xorm:"not null default 1 comment('1未到账,2已到账') TINYINT(1)"`
Memo string `json:"memo" xorm:"not null default '' comment('备注') VARCHAR(2000)"`
OtherId int64 `json:"other_id" xorm:"not null default 0 comment('其他关联订单,具体根据订单类型判断') BIGINT(20)"`
AliOrdId string `json:"ali_ord_id" xorm:"default '' comment('支付宝订单号') VARCHAR(128)"`
CreateAt time.Time `json:"create_at" xorm:"created not null default CURRENT_TIMESTAMP comment('创建时间') TIMESTAMP"`
UpdateAt time.Time `json:"update_at" xorm:"updated not null default CURRENT_TIMESTAMP comment('更新时间') TIMESTAMP"`
}

+ 91
- 0
db/model/user_profile.go Vedi File

@@ -0,0 +1,91 @@
package model

import (
"time"
)

type UserProfile struct {
Uid int `json:"uid" xorm:"not null pk comment('关联userID') INT(20)"`
ArkidUid int `json:"arkid_uid" xorm:"not null default 0 comment('Arkid 用户ID') INT(20)"`
ParentUid int `json:"parent_uid" xorm:"not null default 0 comment('上级ID') INT(20)"`
ArkidToken string `json:"arkid_token" xorm:"not null default '' comment('token') VARCHAR(2000)"`
AvatarUrl string `json:"avatar_url" xorm:"not null default '' comment('头像URL') VARCHAR(2000)"`
CustomInviteCode string `json:"custom_invite_code" xorm:"not null default '' comment('邀请码(自定义)') VARCHAR(16)"`
InviteCode string `json:"invite_code" xorm:"not null default '' comment('邀请码(系统)') VARCHAR(16)"`
Gender int `json:"gender" xorm:"not null default 2 comment('性别0女,1男,2未知') TINYINT(1)"`
Birthday int `json:"birthday" xorm:"not null default 0 comment('出生日期') INT(10)"`
AccWxId string `json:"acc_wx_id" xorm:"not null default '' comment('账户_微信id') VARCHAR(50)"`
AccWxOpenid string `json:"acc_wx_openid" xorm:"not null default '' comment('账户_微信openid') VARCHAR(80)"`
AccTaobaoNickname string `json:"acc_taobao_nickname" xorm:"not null default '' comment('淘宝昵称') VARCHAR(50)"`
AccTaobaoAuthTime int64 `json:"acc_taobao_auth_time" xorm:"not null default 0 comment('淘宝授权备案时间') BIGINT(11)"`
AccTaobaoShareId int64 `json:"acc_taobao_share_id" xorm:"not null default 0 comment('淘宝分享relationId,') index BIGINT(12)"`
AccTaobaoSelfId int64 `json:"acc_taobao_self_id" xorm:"not null default 0 comment('淘宝自购specialId') index BIGINT(12)"`
AccJdSelfId string `json:"acc_jd_self_id" xorm:"not null default '' comment('京东自购ID') index VARCHAR(50)"`
AccJdShareId string `json:"acc_jd_share_id" xorm:"not null default '' comment('京东分享ID') index VARCHAR(50)"`
AccJdFreeId string `json:"acc_jd_free_id" xorm:"not null default '' comment('京东新人免单ID') VARCHAR(50)"`
AccSuningSelfId string `json:"acc_suning_self_id" xorm:"not null default '' comment('苏宁自购ID') index VARCHAR(50)"`
AccSuningShareId string `json:"acc_suning_share_id" xorm:"not null default '' comment('苏宁分享ID') index VARCHAR(50)"`
AccSuningFreeId string `json:"acc_suning_free_id" xorm:"not null default '' comment('苏宁新人免单ID') VARCHAR(50)"`
AccPddSelfId string `json:"acc_pdd_self_id" xorm:"not null default '' comment('拼多多自购ID') index VARCHAR(50)"`
AccPddShareId string `json:"acc_pdd_share_id" xorm:"not null default '' comment('拼多多分享ID') index VARCHAR(50)"`
AccPddFreeId string `json:"acc_pdd_free_id" xorm:"not null default '' comment('拼多多新人免单ID') VARCHAR(50)"`
AccPddBind int `json:"acc_pdd_bind" xorm:"not null default 0 comment('拼多多是否授权绑定') TINYINT(1)"`
AccVipSelfId string `json:"acc_vip_self_id" xorm:"not null default '' comment('唯品会自购ID') index VARCHAR(50)"`
AccVipShareId string `json:"acc_vip_share_id" xorm:"not null default '' comment('唯品会分享ID') index VARCHAR(50)"`
AccVipFreeId string `json:"acc_vip_free_id" xorm:"not null default '' comment('唯品会新人免单ID') VARCHAR(50)"`
AccKaolaSelfId string `json:"acc_kaola_self_id" xorm:"not null default '' comment('考拉自购ID') index VARCHAR(50)"`
AccKaolaShareId string `json:"acc_kaola_share_id" xorm:"not null default '' comment('考拉分享ID') index VARCHAR(50)"`
AccKaolaFreeId string `json:"acc_kaola_free_id" xorm:"not null default '' comment('考拉新人免单ID') VARCHAR(50)"`
AccDuomaiShareId int64 `json:"acc_duomai_share_id" xorm:"not null pk default 0 comment('多麦联盟分享ID') BIGINT(12)"`
AccAlipay string `json:"acc_alipay" xorm:"not null default '' comment('支付宝账号') VARCHAR(50)"`
AccAlipayRealName string `json:"acc_alipay_real_name" xorm:"not null default '' comment('支付宝账号真实姓名') VARCHAR(50)"`
CertTime int `json:"cert_time" xorm:"not null default 0 comment('认证时间') INT(10)"`
CertName string `json:"cert_name" xorm:"not null default '' comment('证件上名字,也是真实姓名') VARCHAR(50)"`
CertNum string `json:"cert_num" xorm:"not null default '' comment('证件号码') VARCHAR(50)"`
CertState int `json:"cert_state" xorm:"not null default 0 comment('认证状态(0为未认证,1为认证中,2为已认证,3为认证失败)') TINYINT(1)"`
FinCommission string `json:"fin_commission" xorm:"not null default 0.0000 comment('累计佣金') DECIMAL(10,4)"`
FinValid string `json:"fin_valid" xorm:"not null default 0.0000 comment('可用余额,fin=>finance财务') DECIMAL(10,4)"`
FinInvalid string `json:"fin_invalid" xorm:"not null default 0.0000 comment('不可用余额,冻结余额') DECIMAL(10,4)"`
FinSelfOrderCount int `json:"fin_self_order_count" xorm:"not null default 0 comment('自购订单数,包括未完成') INT(11)"`
FinSelfOrderCountDone int `json:"fin_self_order_count_done" xorm:"not null default 0 comment('自购已完成订单') INT(11)"`
FinSelfRebate float32 `json:"fin_self_rebate" xorm:"not null default 0.000000 comment('累积自购获得返利金额') FLOAT(14,6)"`
FinTotal float32 `json:"fin_total" xorm:"not null default 0.000000 comment('累计总收益') FLOAT(14,6)"`
Lat float32 `json:"lat" xorm:"not null default 0.000000 comment('纬度') FLOAT(15,6)"`
Lng float32 `json:"lng" xorm:"not null default 0.000000 comment('经度') FLOAT(15,6)"`
Memo string `json:"memo" xorm:"not null default '' comment('用户简述备注') VARCHAR(2048)"`
IsNew int `json:"is_new" xorm:"not null default 1 comment('是否是新用户') TINYINT(1)"`
IsVerify int `json:"is_verify" xorm:"not null default 0 comment('是否有效会员') TINYINT(1)"`
IsOrdered int `json:"is_ordered" xorm:"not null default 0 comment('是否已完成首单(0否,1是)') TINYINT(1)"`
FromWay string `json:"from_way" xorm:"not null default '' comment('注册来源:
no_captcha_phone:免验证码手机号注册;
manual_phone:手动手机验证码注册;
wx:微信授权;
wx_mp:小程序授权;
wx_pub:公众号授权;
wx_bind_phone:微信注册绑定手机号;
admin:管理员添加;taobao_bind_phone:淘宝注册绑定手机号,apple_bind_phone:苹果注册绑定手机号') VARCHAR(16)"`
HidOrder int `json:"hid_order" xorm:"not null default 0 comment('隐藏订单') TINYINT(3)"`
HidContact int `json:"hid_contact" xorm:"not null default 0 comment('隐藏联系方式') TINYINT(4)"`
NewMsgNotice int `json:"new_msg_notice" xorm:"not null default 1 comment('新消息通知') TINYINT(1)"`
WxAccount string `json:"wx_account" xorm:"not null default '' comment('微信号') VARCHAR(100)"`
WxQrcode string `json:"wx_qrcode" xorm:"not null default '' comment('微信二维码') VARCHAR(100)"`
ThirdPartyTaobaoOid string `json:"third_party_taobao_oid" xorm:"not null default '' comment('淘宝第三方登录openID') VARCHAR(100)"`
ThirdPartyTaobaoSid string `json:"third_party_taobao_sid" xorm:"not null default '' comment('淘宝第三方登录sID') VARCHAR(255)"`
ThirdPartyTaobaoAcctoken string `json:"third_party_taobao_acctoken" xorm:"not null default '' comment('淘宝第三方登录topaccesstoken') VARCHAR(100)"`
ThirdPartyTaobaoAuthcode string `json:"third_party_taobao_authcode" xorm:"not null default '' comment('淘宝第三方登录topAuthCode') VARCHAR(100)"`
ThirdPartyAppleToken string `json:"third_party_apple_token" xorm:"not null default '' comment('苹果第三方登录token') VARCHAR(1024)"`
ThirdPartyQqAccessToken string `json:"third_party_qq_access_token" xorm:"not null default '' comment('QQ第三方登录access_token') VARCHAR(255)"`
ThirdPartyQqExpiresIn string `json:"third_party_qq_expires_in" xorm:"not null default '' comment('QQ第三方登录expires_in(剩余时长)') VARCHAR(255)"`
ThirdPartyQqOpenid string `json:"third_party_qq_openid" xorm:"not null default '' comment('QQ第三方登陆openid(不变,用于认证)') VARCHAR(255)"`
ThirdPartyQqUnionid string `json:"third_party_qq_unionid" xorm:"not null default '' comment('QQ第三方登陆unionid') VARCHAR(255)"`
ThirdPartyWechatExpiresIn string `json:"third_party_wechat_expires_in" xorm:"not null default '' comment('微信第三方登录expires_in(剩余时长)') VARCHAR(255)"`
ThirdPartyWechatOpenid string `json:"third_party_wechat_openid" xorm:"not null default '' comment('微信第三方登陆openid(不变,用于认证)') VARCHAR(255)"`
ThirdPartyWechatUnionid string `json:"third_party_wechat_unionid" xorm:"not null default '' comment('微信第三方登陆unionid') VARCHAR(255)"`
ThirdPartyWechatMiniOpenid string `json:"third_party_wechat_mini_openid" xorm:"not null default '' comment('微信小程序登录open_id') VARCHAR(255)"`
ThirdPartyWechatH5Openid string `json:"third_party_wechat_h5_openid" xorm:"not null default '' comment('微信H5登录open_id') VARCHAR(255)"`
FreeRemainTime int `json:"free_remain_time" xorm:"not null default 0 comment('免单剩余次数') INT(11)"`
FreeCumulativeTime int `json:"free_cumulative_time" xorm:"not null default 0 comment('免单累计次数') INT(11)"`
IsDelete int `json:"is_delete" xorm:"not null default 0 comment('是否已删除') TINYINT(1)"`
UpdateAt time.Time `json:"update_at" xorm:"updated not null default CURRENT_TIMESTAMP comment('更新时间') TIMESTAMP"`
SignDay int `json:"sign_day" xorm:"not null default 0 comment('签到天数') INT(11)"`
}

+ 1
- 0
go.mod Vedi File

@@ -9,4 +9,5 @@ require (
go.uber.org/zap v1.13.0
gopkg.in/natefinch/lumberjack.v2 v2.0.0
xorm.io/xorm v1.3.1
github.com/shopspring/decimal v1.3.1
)

+ 20
- 0
md/app_redis_key.go Vedi File

@@ -0,0 +1,20 @@
package md

// 缓存key统一管理, %s格式化为masterId
const (
AppCfgCacheKey = "%s:cfg_cache:%s" // 占位符: masterId, key的第一个字母
VirtualCoinCfgCacheKey = "%s:virtual_coin_cfg"
PlanRewardCfgCacheKey = "%s:plan_reward_cfg"
UnionSetCacheCfg = "%s:union_set_cfg:%s" // 联盟设置缓存key

UserFinValidUpdateLock = "%s:user_fin_valid_update_lock:%s" // 用户余额更新锁(能拿到锁才能更新余额)
UserVirtualAmountUpdateLock = "%s:user_virtual_amount_update_lock:%s" // 用户虚拟币更新锁(能拿到锁才能更新余额)

WithdrawApplyQueueListKey = "withdraw_apply_queue" // 提现队列

TplBottomNavRedisKey = "%s:tpl_nav_bottom_key:%s" // master_id platform

SysModByIdRedisKey = "%s:sys_mod_tpl_by_id:%s"

CfgCacheTime = 86400
)

+ 1
- 0
md/day_luck_draw_setting.go Vedi File

@@ -4,4 +4,5 @@ type RewardData struct {
Key string `json:"key"`
Bili string `json:"bili"`
Num string `json:"num"`
Probability string `json:"probability"`
}

+ 45
- 0
md/fin_user_flow.go Vedi File

@@ -0,0 +1,45 @@
package md

const (
FinUserFlowDirectionIncome = 1 //流水 - 收入
FinUserFlowDirectionExpenditure = 2 //流水 - 支出
)

const (
FinUserFlowRedisDataBase = 0
FinUserFlowRedisKey = "%s:fin_user_flow:%d:user:%d"
)

const (
IntegralReleaseServiceRevenueTitleForFinUserFlow = "积分释放-服务收益"
IntegralReleaseServiceRevenueRefundTitleForFinUserFlow = "订单退款-服务收益扣除"
)

const (
IntegralReleaseServiceRevenueOrderTypeForFinUserFlow = 50 // 积分释放-服务收益
IntegralReleaseServiceRevenueOrderRefundTypeForFinUserFlow = 51 // 积分释放-服务收益退款
)

const DealUserAmountRequestIdPrefix = "%s:deal_user_amount:%d"
const UserAmountRedisKey = "%s:user_amount:%d"

type DealIntegralReleaseInterpositionUserAmountReq struct {
Kind string `json:"kind"`
Mid string `json:"mid"`
OrdId string `json:"ord_id"`
CoinId int `json:"coin_id"`
Uid int `json:"uid"`
OriginalUid int `json:"original_uid"`
Amount float64 `json:"amount"`
}

type DealUserAmount struct {
Kind string `json:"kind"`
Mid string `json:"mid"`
Title string `json:"title"`
OrderType string `json:"order_type"`
OrdAction int `json:"ord_action"`
OrdId string `json:"ord_id"`
Uid int `json:"uid"`
Amount float64 `json:"amount"`
}

+ 2
- 11
svc/svc_day_luck_draw_order_jackpot.go Vedi File

@@ -3,21 +3,12 @@ package svc
import (
"code.fnuoos.com/go_rely_warehouse/zyos_go_day_luck_draw.git/db"
"code.fnuoos.com/go_rely_warehouse/zyos_go_day_luck_draw.git/md"
zhios_day_luck_draw_utils "code.fnuoos.com/go_rely_warehouse/zyos_go_day_luck_draw.git/utils"
"time"
"xorm.io/xorm"
)

func AddOrderJackpot(eg *xorm.Engine, req md.DayLuckDrawOrderJackpotReq) error {
setting := db.GetDayLuckDrawSetting(eg)
if setting == nil {
return nil
}
now := time.Now().Unix()
if setting.IsUse == 0 || setting.StartTime.IsZero() || setting.EndTime.IsZero() || (now > setting.StartTime.Unix() && now < setting.EndTime.Unix()) {
return nil
}
if setting.OrderSoruce == "mall" && zhios_day_luck_draw_utils.InArr(req.Pvd, []string{"mall_goods", "mall_group_buy", "mall_goods_user_lv", "super_group_buy", "mall_supply", "mall_group_own_buy"}) == false {
setting := db.CheckSetting(eg, req.Pvd)
if setting == false {
return nil
}
err := db.AddOrderJackpot(eg, req)


+ 325
- 4
svc/svc_day_luck_draw_reward.go Vedi File

@@ -2,14 +2,27 @@ package svc

import (
"code.fnuoos.com/go_rely_warehouse/zyos_go_day_luck_draw.git/db"
"code.fnuoos.com/go_rely_warehouse/zyos_go_day_luck_draw.git/db/model"
"code.fnuoos.com/go_rely_warehouse/zyos_go_day_luck_draw.git/md"
zhios_day_luck_draw_utils "code.fnuoos.com/go_rely_warehouse/zyos_go_day_luck_draw.git/utils"
"code.fnuoos.com/go_rely_warehouse/zyos_go_day_luck_draw.git/utils/cache"
"encoding/json"
"errors"
"fmt"
"github.com/shopspring/decimal"
"math/rand"
"time"
"xorm.io/xorm"
)

func InitForDayLuckDraw(redisAddr string) (err error) {
if redisAddr != "" {
cache.NewRedis(redisAddr)
}
_, err = cache.SelectDb(0)
return
}

//随机几个用户
func RandUser(sess *xorm.Session) []string {
var newOid = make([]string, 0)
@@ -19,7 +32,10 @@ func RandUser(sess *xorm.Session) []string {
return newOid
}
now := time.Now().Unix()
if setting.IsUse == 0 || setting.StartTime.IsZero() || setting.EndTime.IsZero() || (now > setting.StartTime.Unix() && now < setting.EndTime.Unix()) {
if setting.IsUse == 0 || setting.StartTime.IsZero() || setting.EndTime.IsZero() {
return newOid
}
if now < setting.StartTime.Unix() || now > setting.EndTime.Unix() {
return newOid
}
count := 0
@@ -38,6 +54,224 @@ func RandUser(sess *xorm.Session) []string {
return newOid

}

/***
1.创建期数
2.获取还没分配的金额 就是 期数为0的
3.把期数设置到订单
4.查出所有订单号
5.随机出订单号(每个名额随机一次 如果重复再次循环) 按概率分几等奖
*/

func GetUserReward(eg *xorm.Engine, mid string, isTask bool) error {
// 加锁 防止频繁操作
mutexKey := fmt.Sprintf("%s:day_lucky_raw_settle", mid)
operationSumAvailable, err := cache.Do("SET", mutexKey, 1, "EX", 3600, "NX")
if err != nil {
return err
}
defer cache.Del(mutexKey)
if operationSumAvailable != "OK" {
return errors.New("上一次未运行完成")
}
sess := eg.NewSession()
defer func() {
sess.Close()
if err := recover(); err != nil {
fmt.Println(err)
}
}()
sess.Begin()
key := fmt.Sprintf("%s:day_lucky_draw_bingo_rate_calculate", mid)
isDone, _ := cache.GetString(key)
if isDone == "" || isDone == "0" {
SetAllAwardsProbability(sess, mid)
}
probabilityMap := make([]map[int]int64, 0)
key = fmt.Sprintf("%s:day_lucky_draw_rotary_bingo_rate", mid)
err = cache.GetJson(key, &probabilityMap)
if err != nil {
sess.Rollback()
return err
}

setting := db.GetDayLuckDrawSettingSess(sess)
var rewardData = make([]md.RewardData, 0)
err = json.Unmarshal([]byte(setting.RewardData), &rewardData)
if err != nil {
sess.Rollback()
return err
}
now := time.Now().Unix()
if isTask {
if setting.IsUse == 0 || setting.StartTime.IsZero() || setting.EndTime.IsZero() {
return errors.New("未开始")
}
if now < setting.StartTime.Unix() || now > setting.EndTime.Unix() {
return errors.New("未开始")
}
if setting.OpenTime.IsZero() {
return errors.New("没抽奖时间")
}
if now < setting.OpenTime.Unix() {
return errors.New("没到时间")
}
}
if setting.Source == "" {
return errors.New("未选择奖励池数据类型")
}
//设置期数
max := db.GetPeriodMax(sess)
sum := db.GetSettleAllOrderSum(sess)
if setting.Source == "amount" {
sum = zhios_day_luck_draw_utils.StrToFloat64(setting.Amount)
} else {
sum = sum * zhios_day_luck_draw_utils.StrToFloat64(setting.CommissionBili) / 100
}
if sum == 0 { //没金额中断了
setting.OpenTime = time.Unix(now+int64(setting.CycleDay)*86400, 0)
update, err := sess.Where("id=?", setting.Id).Cols("open_time").Update(setting)
if err != nil || update == 0 {
sess.Rollback()
return errors.New("失败")
}
sess.Commit()
return errors.New("没金额")
}
period := 1
if max != nil {
period = max.Period + 1
}
req := md.DayLuckDrawPeriodReq{
Reward: zhios_day_luck_draw_utils.Float64ToStrByPrec(sum, 7),
Period: zhios_day_luck_draw_utils.IntToStr(period),
}
settingStr, _ := json.Marshal(setting)
bools := db.AddPeriod(sess, req, string(settingStr))
if bools == false {
return errors.New("期数写入错误")
}

rewardDataMap := make(map[int64]int, 0)
for _, v := range rewardData {
rewardDataMap[zhios_day_luck_draw_utils.StrToInt64(v.Key)] = zhios_day_luck_draw_utils.StrToInt(v.Num)
}
uids := RandUser(sess)
var award = make(map[string]int64)
var awardIdMap = make(map[int64]int)
for _, v := range uids {
for i := 0; i < 10; i++ {
key := AwardRandomNumberFor100000()
awardId := int64(0)
for _, awardProbabilityMap := range probabilityMap {
id, ok := awardProbabilityMap[key]
if ok {
awardId = id
} else {
continue
}
}
if awardId == 0 || award[v] > 0 {
continue
}
//查下已产生用户数量
awardCount, ok := awardIdMap[awardId]
//查下总数量
rewardDataCount, ok := rewardDataMap[awardId]
if ok == false {
continue
}
if ok {
if awardCount+1 > rewardDataCount {
continue
}
}
if rewardDataCount == 0 {
continue
}
awardIdMap[awardId] = awardCount + 1
award[v] = awardId
}
}

//处理奖励
var awardMap = make(map[int64][]string)
for k, v := range award {
awardMap[v] = append(awardMap[v], k)
}
for _, v := range rewardData {
uid, ok := awardMap[zhios_day_luck_draw_utils.StrToInt64(v.Key)]
if ok {
err := SendReward(sess, uid, mid, period, sum, v)
if err != nil {
sess.Rollback()
return err
}
}
}
setting.OpenTime = time.Unix(now+int64(setting.CycleDay)*86400, 0)
update, err := sess.Where("id=?", setting.Id).Cols("open_time").Update(setting)
if err != nil || update == 0 {
sess.Rollback()
return errors.New("失败")
}
update1, err1 := sess.Where("is_send=0").Cols("is_send").Update(&model.DayLuckDrawSettleOrder{IsSend: 1})
if err1 != nil || update1 == 0 {
sess.Rollback()
return errors.New("失败")
}
update2, err2 := sess.Where("period=0").Cols("period").Update(&model.DayLuckDrawOrderJackpot{Period: period})
if err2 != nil || update2 == 0 {
sess.Rollback()
return errors.New("失败")
}
sess.Commit()
return nil
}
func SendReward(sess *xorm.Session, uid []string, mid string, period int, sum float64, rewardData md.RewardData) error {
count := len(uid)
if count == 0 {
return nil
}
bili := zhios_day_luck_draw_utils.StrToFloat64(rewardData.Bili)
if bili == 0 {
return nil
}
amount := sum * bili / float64(count)
for _, v := range uid {
var req = md.DayLuckDrawRewardReq{
Uid: v,
Period: zhios_day_luck_draw_utils.IntToStr(period),
Reward: zhios_day_luck_draw_utils.Float64ToStrByPrec(amount, 7),
Lv: rewardData.Key,
}
err := db.AddPeriodReward(sess, req)
if err != nil {
return err
}
var dealUserAmount = md.DealUserAmount{
Kind: "add",
Mid: mid,
Title: "每日抽奖奖励",
OrderType: "",
OrdAction: 85,
OrdId: "",
Uid: zhios_day_luck_draw_utils.StrToInt(v),
Amount: amount,
}
err = DealUserAmount(sess, dealUserAmount)
if err != nil {
return err
}
}
return nil
}
func AwardRandomNumberFor100000() int {
rand.Seed(time.Now().UnixNano())
num := rand.Intn(100000)
return num
}

func MicsSlice(origin []string, count int) []string {
tmpOrigin := make([]string, len(origin))
copy(tmpOrigin, origin)
@@ -47,12 +281,99 @@ func MicsSlice(origin []string, count int) []string {
tmpOrigin[i], tmpOrigin[j] = tmpOrigin[j], tmpOrigin[i]
})

result := make([]string, 0, count)
for index, value := range tmpOrigin {
if index == count {
result := make([]string, 0)
for _, value := range tmpOrigin {
if len(result) == count {
break
}
if zhios_day_luck_draw_utils.InArr(value, result) {
continue
}
result = append(result, value)
}
return result
}

func SetAllAwardsProbability(sess *xorm.Session, mid string) error {
defer func() {
//处理异常错误
if r := recover(); r != nil {
fmt.Println(r)
}
}()
setting := db.GetDayLuckDrawSettingSess(sess)
var rewardData = make([]md.RewardData, 0)
err := json.Unmarshal([]byte(setting.RewardData), &rewardData)
if err != nil {
key := fmt.Sprintf("%s:day_lucky_draw_bingo_rate_calculate", mid)
_, err = cache.SetEx(key, "0", 39528000) // 半年
if err != nil {
return err
}
}
//建立协程池
workerCount := 0
ProbabilityKeyChan := make(chan int, 100000)
awardsMapChan := make(chan map[int]int64, len(rewardData))
doneChan := make(chan bool, len(rewardData))
for i := 0; i < 10; i++ {
go func(endNum int) {
for startNum := (endNum * 10000) + 1; startNum <= (endNum+1)*10000; startNum++ {
ProbabilityKeyChan <- startNum
}
if i == 9 {
close(ProbabilityKeyChan)
}
}(i)
}
for _, award := range rewardData {
probabilityD, err := decimal.NewFromString(award.Num)
if err != nil {
probabilityD = decimal.NewFromInt(0)
}
probabilityD = probabilityD.Div(decimal.NewFromInt(100))
probability, _ := probabilityD.Float64()
go CalculateAwardsProbability(zhios_day_luck_draw_utils.StrToInt64(award.Key), probability, ProbabilityKeyChan, awardsMapChan, doneChan)
workerCount++
}
probabilityMap := make([]map[int]int64, 0)
WaitChain(workerCount, awardsMapChan, doneChan)
for awards := range awardsMapChan {
probabilityMap = append(probabilityMap, awards)
}
key := fmt.Sprintf("%s:day_lucky_draw_rotary_bingo_rate", mid)
_, err = cache.SetEx(key, zhios_day_luck_draw_utils.SerializeStr(probabilityMap), 39528000) // 半年
if err != nil {
return err
}
key = fmt.Sprintf("%s:day_lucky_draw_bingo_rate_calculate", mid)
_, err = cache.SetEx(key, "1", 39528000) // 半年
if err != nil {
return err
}
return nil
}

func CalculateAwardsProbability(awardsId int64, probability float64, ProbabilityKeyChan chan int, awardsMapChan chan map[int]int64, doneChan chan bool) {
//计算要循环多少次
var awardsMap = make(map[int]int64)
for i := 0; i < int(100000*probability); i++ {
key := <-ProbabilityKeyChan
awardsMap[key] = awardsId
}
awardsMapChan <- awardsMap
doneChan <- true
}

func WaitChain(workerCount int, awardsMapChan chan map[int]int64, doneChan chan bool) {
for {
select {
case <-doneChan:
workerCount--
if workerCount == 0 {
close(awardsMapChan)
return
}
}
}
}

+ 19
- 0
svc/svc_day_luck_draw_settle_order.go Vedi File

@@ -0,0 +1,19 @@
package svc

import (
"code.fnuoos.com/go_rely_warehouse/zyos_go_day_luck_draw.git/db"
"code.fnuoos.com/go_rely_warehouse/zyos_go_day_luck_draw.git/md"
"xorm.io/xorm"
)

func AddSettleOrder(eg *xorm.Engine, req md.DayLuckDrawOrderJackpotReq) error {
setting := db.CheckSetting(eg, req.Pvd)
if setting == false {
return nil
}
err := db.AddSettle(eg, req)
if err != nil {
return err
}
return nil
}

+ 126
- 0
svc/svc_deal_user_amount.go Vedi File

@@ -0,0 +1,126 @@
package svc

import (
"code.fnuoos.com/go_rely_warehouse/zyos_go_day_luck_draw.git/db"
"code.fnuoos.com/go_rely_warehouse/zyos_go_day_luck_draw.git/db/model"
"code.fnuoos.com/go_rely_warehouse/zyos_go_day_luck_draw.git/md"
zhios_day_luck_draw_utils "code.fnuoos.com/go_rely_warehouse/zyos_go_day_luck_draw.git/utils"
"code.fnuoos.com/go_rely_warehouse/zyos_go_day_luck_draw.git/utils/cache"
"errors"
"fmt"
"github.com/shopspring/decimal"
"strconv"
"time"
"xorm.io/xorm"
)

// DealUserAmount 处理给用户余额
func DealUserAmount(session *xorm.Session, req md.DealUserAmount) (err error) {
if req.Amount < 0 {
req.Amount = 0
}
//1、分布式锁阻拦
requestIdPrefix := fmt.Sprintf(md.DealUserAmountRequestIdPrefix, req.Mid, req.Uid)
cb, err := HandleBalanceDistributedLockForAmount(req.Mid, strconv.Itoa(req.Uid), requestIdPrefix)
if err != nil {
return err
}
if cb != nil {
defer cb() // 释放锁
}

//2、计算&&组装数据
now := time.Now()
userAmount, err := GetUserAmount(session, req.Mid, req.Uid)
if err != nil {
return err
}
userAmountValue := decimal.NewFromFloat(zhios_day_luck_draw_utils.StrToFloat64(userAmount))
amountValue := decimal.NewFromFloat(req.Amount).RoundFloor(8)

var finUserFlow = model.FinUserFlow{
Uid: req.Uid,
Amount: amountValue.String(),
BeforeAmount: userAmount,
OrdType: req.OrderType,
OrdId: req.OrdId,
OrdTitle: req.Title,
OrdAction: req.OrdAction,
OrdTime: int(now.Unix()),
State: 1,
CreateAt: now,
UpdateAt: now,
}
if req.Kind == "add" {
finUserFlow.Type = 0
finUserFlow.AfterAmount = userAmountValue.Add(amountValue).RoundFloor(8).String()
} else if req.Kind == "sub" {
finUserFlow.Type = 1
finUserFlow.AfterAmount = userAmountValue.Sub(amountValue).RoundFloor(8).String()
} else {
err = errors.New("错误的kind类型")
return err
}
if zhios_day_luck_draw_utils.StrToFloat64(finUserFlow.AfterAmount) < 0 {
return errors.New("用户余额不足")
}

//3、插入 `user_virtual_coin_flow` 记录
affected, err := session.Insert(&finUserFlow)
if affected == 0 || err != nil {
return err
}

//4、修改 `user_profile`的fin_valid值 && 及缓存
err = SetCacheUserAmount(session, req.Mid, finUserFlow.AfterAmount, req.Uid, true)
if err != nil {
return err
}

return nil
}

// GetUserAmount 获取用户余额
func GetUserAmount(session *xorm.Session, masterId string, uid int) (amount string, err error) {
redisKey := fmt.Sprintf(md.UserAmountRedisKey, masterId, uid)
amount, err = cache.GetString(redisKey)
if err != nil {
if err.Error() == "redigo: nil returned" {
userAmount, err := db.UserProfileFindByIDWithSession(session, uid)
if err != nil {
return amount, err
}
if userAmount == nil {
amount = "0"
} else {
amount = userAmount.FinValid
}
//将获取到的余额值缓存至redis
_ = SetCacheUserAmount(session, masterId, amount, uid, false)
return amount, nil
}
return amount, err
}
return amount, nil
}

// SetCacheUserAmount 设置缓存的用户余额
func SetCacheUserAmount(session *xorm.Session, masterId, amount string, uid int, isUpdateDb bool) error {
redisKey := fmt.Sprintf(md.UserAmountRedisKey, masterId, uid)
if isUpdateDb {
_, err := session.Where("uid=?", uid).Update(model.UserProfile{
Uid: uid,
FinValid: amount,
})
if err != nil {
return err
}
}
//_, err := cache.Set(redisKey, int64(utils.StrToFloat64(amount)))
//TODO::默认缓存1小时 (先调整为 2 min)
_, err := cache.SetEx(redisKey, zhios_day_luck_draw_utils.StrToFloat64(amount), 60*2)
if err != nil {
return err
}
return nil
}

+ 84
- 0
svc/svc_redis_mutex_lock.go Vedi File

@@ -0,0 +1,84 @@
package svc

import (
"code.fnuoos.com/go_rely_warehouse/zyos_go_day_luck_draw.git/md"
zhios_day_luck_draw_utils "code.fnuoos.com/go_rely_warehouse/zyos_go_day_luck_draw.git/utils"
"code.fnuoos.com/go_rely_warehouse/zyos_go_day_luck_draw.git/utils/cache"
"errors"
"fmt"
"math/rand"
"reflect"
"time"
)

const redisMutexLockExpTime = 15

// TryGetDistributedLock 分布式锁获取
// requestId 用于标识请求客户端,可以是随机字符串,需确保唯一
func TryGetDistributedLock(lockKey, requestId string, isNegative bool) bool {
if isNegative { // 多次尝试获取
retry := 1
for {
ok, err := cache.Do("SET", lockKey, requestId, "EX", redisMutexLockExpTime, "NX")
// 获取锁成功
if err == nil && ok == "OK" {
return true
}
// 尝试多次没获取成功
if retry > 10 {
return false
}
time.Sleep(time.Millisecond * time.Duration(rand.Intn(1000)))
retry += 1
}
} else { // 只尝试一次
ok, err := cache.Do("SET", lockKey, requestId, "EX", redisMutexLockExpTime, "NX")
// 获取锁成功
if err == nil && ok == "OK" {
return true
}

return false
}
}

// ReleaseDistributedLock 释放锁,通过比较requestId,用于确保客户端只释放自己的锁,使用lua脚本保证操作的原子型
func ReleaseDistributedLock(lockKey, requestId string) (bool, error) {
luaScript := `
if redis.call("get",KEYS[1]) == ARGV[1]
then
return redis.call("del",KEYS[1])
else
return 0
end`

do, err := cache.Do("eval", luaScript, 1, lockKey, requestId)
fmt.Println(reflect.TypeOf(do))
fmt.Println(do)
if zhios_day_luck_draw_utils.AnyToInt64(do) == 1 {
return true, err
} else {
return false, err
}
}

func GetDistributedLockRequestId(prefix string) string {
return prefix + zhios_day_luck_draw_utils.IntToStr(rand.Intn(100000000))
}

// HandleDistributedLock 处理余额更新时获取锁和释放锁 如果加锁成功,使用语句 ` defer cb() ` 释放锁
func HandleDistributedLock(masterId, uid, requestIdPrefix string) (cb func(), err error) {
// 获取余额更新锁
balanceLockKey := fmt.Sprintf(md.UserVirtualAmountUpdateLock, masterId, uid)
requestId := GetDistributedLockRequestId(requestIdPrefix)
balanceLockOk := TryGetDistributedLock(balanceLockKey, requestId, true)
if !balanceLockOk {
return nil, errors.New("系统繁忙,请稍后再试")
}

cb = func() {
_, _ = ReleaseDistributedLock(balanceLockKey, requestId)
}

return cb, nil
}

+ 24
- 0
svc/svc_redis_mutex_lock_for_amount.go Vedi File

@@ -0,0 +1,24 @@
package svc

import (
"code.fnuoos.com/go_rely_warehouse/zyos_go_day_luck_draw.git/md"
"errors"
"fmt"
)

// HandleBalanceDistributedLockForAmount 处理余额更新时获取锁和释放锁 如果加锁成功,使用语句 ` defer cb() ` 释放锁
func HandleBalanceDistributedLockForAmount(masterId, uid, requestIdPrefix string) (cb func(), err error) {
// 获取余额更新锁
balanceLockKey := fmt.Sprintf(md.UserFinValidUpdateLock, masterId, uid)
requestId := GetDistributedLockRequestId(requestIdPrefix)
balanceLockOk := TryGetDistributedLock(balanceLockKey, requestId, true)
if !balanceLockOk {
return nil, errors.New("系统繁忙,请稍后再试")
}

cb = func() {
_, _ = ReleaseDistributedLock(balanceLockKey, requestId)
}

return cb, nil
}

Caricamento…
Annulla
Salva