capsule.go 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437
  1. package dao
  2. import (
  3. "context"
  4. "encoding/json"
  5. "fmt"
  6. "math/rand"
  7. "strconv"
  8. "sync"
  9. "time"
  10. v12 "go-common/app/service/live/rc/api/liverpc/v1"
  11. "github.com/pkg/errors"
  12. "go-common/app/service/live/xlottery/api/grpc/v1"
  13. "go-common/library/cache/redis"
  14. "go-common/library/database/sql"
  15. "go-common/library/log"
  16. "go-common/library/queue/databus/report"
  17. )
  18. const (
  19. _userCapsuleCoinMysql = "select normal_score, colorful_score from capsule_%d where uid = ?"
  20. _addCapsuleCoinMysql = "insert into capsule_%d(uid, normal_score, colorful_score) values(?, ?, ?)"
  21. _userInfoMysql = "select score from capsule_info_%d where uid = ? and type = ?"
  22. _addInfoMysql = "insert into capsule_info_%d(uid,type,score) values(?, ?, ?)"
  23. _updateUserCapsuleMysql = "update capsule_%d set %s_score = %s_score + ? where uid = ?"
  24. _updateUserInfoMysql = "update capsule_info_%d set score = score + ? where uid = ? and type = ?"
  25. _transUserCapsuleMysql = "update capsule_%d set colorful_score = 0, normal_score = normal_score + ? where uid = ?"
  26. _reportCapsuleChangeMysql = "insert into capsule_log_%s(uid, type, score, action, platform, pre_normal_score, pre_colorful_score, cur_normal_score, cur_colorful_score) values(?,?,?,?,?,?,?,?,?)"
  27. _userCapsuleCoinRedis = "hash:capsule:user:%d"
  28. _userInfoRedis = "capsule:user:info:%d:%d"
  29. _openHistoryRedis = "list:capsule:%s:list"
  30. _openLockRedis = "capsule-pay-%d"
  31. _capsuleConfRand = "capsule:rand"
  32. _historyOpenCount = "hash:capsule:count"
  33. _historyGiftCount = "capsule:gift:count:%d:%s"
  34. _capsuleNotice = "capsule:notice:%s:%d"
  35. _whiteUserPrizeRedis = "capsule:white:user:%d"
  36. )
  37. const (
  38. _ = iota
  39. // CapsulePrizeGift1Type 辣条
  40. CapsulePrizeGift1Type // 辣条
  41. // CapsulePrizeTitleType 头衔
  42. CapsulePrizeTitleType
  43. // CapsulePrizeStuff1Type 经验原石
  44. CapsulePrizeStuff1Type
  45. // CapsulePrizeStuff2Type 经验曜石
  46. CapsulePrizeStuff2Type
  47. // CapsulePrizeStuff3Type 贤者之石
  48. CapsulePrizeStuff3Type
  49. // CapsulePrizeSmallTvType 小电视
  50. CapsulePrizeSmallTvType
  51. // CapsulePrizeGuard3Type 舰长体验
  52. CapsulePrizeGuard3Type
  53. // CapsulePrizeGuard2Type 提督体验
  54. CapsulePrizeGuard2Type
  55. // CapsulePrizeGuard1Type 总督体验
  56. CapsulePrizeGuard1Type
  57. // CapsulePrizeScoreAdd 积分加成卡
  58. CapsulePrizeScoreAdd
  59. // CapsulePrizeSmallStar 小星星
  60. CapsulePrizeSmallStar
  61. // CapsulePrizeWeekScore 抽奖券
  62. CapsulePrizeWeekScore
  63. // CapsulePrizeDanmuColor 弹幕颜色
  64. CapsulePrizeDanmuColor
  65. // CapsulePrizeLplScore lpl抽奖券
  66. CapsulePrizeLplScore
  67. // CapsulePrizeLplProduct1 lpl实物奖励1
  68. CapsulePrizeLplProduct1
  69. // CapsulePrizeLplProduct2 lpl实物奖励2
  70. CapsulePrizeLplProduct2
  71. // CapsulePrizeLplProduct3 lpl实物奖励3
  72. CapsulePrizeLplProduct3
  73. )
  74. const (
  75. // CapsulePrizeProduct1 .
  76. CapsulePrizeProduct1 = 100000 // 实物奖励
  77. // CapsulePrizeProduct2 .
  78. CapsulePrizeProduct2 = 100001 // 实物奖励
  79. // CapsulePrizeProduct3 .
  80. CapsulePrizeProduct3 = 100002
  81. // CapsulePrizeProduct4 .
  82. CapsulePrizeProduct4 = 100003 // 实物奖励
  83. // CapsulePrizeProduct5 .
  84. CapsulePrizeProduct5 = 100004 // 实物奖励
  85. // CapsulePrizeProduct6 .
  86. CapsulePrizeProduct6 = 100005
  87. )
  88. const (
  89. // CapsulePrizeCoupon1 .
  90. CapsulePrizeCoupon1 = 200000
  91. // CapsulePrizeCoupon2 .
  92. CapsulePrizeCoupon2 = 200001
  93. // CapsulePrizeCoupon3 .
  94. CapsulePrizeCoupon3 = 200002
  95. )
  96. const (
  97. // CapsulePrizeExpire1Day 过期时间24小时
  98. CapsulePrizeExpire1Day = 1
  99. // CapsulePrizeExpire3Day 过期时间72小时
  100. CapsulePrizeExpire3Day = 10
  101. // CapsulePrizeExpire1Week 过期时间1周
  102. CapsulePrizeExpire1Week = 20
  103. // CapsulePrizeExpire3Month 过期时间3个月
  104. CapsulePrizeExpire3Month = 30
  105. // CapsulePrizeExpireForever 过期时间永久
  106. CapsulePrizeExpireForever = 100
  107. )
  108. const (
  109. _ = iota
  110. // ProTypeNormal 概率
  111. ProTypeNormal
  112. // ProTypeFixDay 每天固定数量
  113. ProTypeFixDay
  114. // ProTypeFixWeek 每周固定数量
  115. ProTypeFixWeek
  116. // ProTypeWhite 白名单
  117. ProTypeWhite
  118. )
  119. const (
  120. // CapsuleGiftTypeAll gift_type 为全部道具
  121. CapsuleGiftTypeAll = 1
  122. )
  123. const (
  124. // NormalCoinId 普通扭蛋id
  125. NormalCoinId = 1
  126. // ColorfulCoinId 梦幻扭蛋id
  127. ColorfulCoinId = 2
  128. // WeekCoinId 梦幻扭蛋id
  129. WeekCoinId = 3
  130. // LplCoinId 梦幻扭蛋id
  131. LplCoinId = 4
  132. // BlessCoinId 祈福券
  133. BlessCoinId = 5
  134. // OpenHistoryNum 开奖历史
  135. OpenHistoryNum = 30
  136. // NormalCoinString 普通扭蛋字符串标识,数据库和redis
  137. NormalCoinString = "normal"
  138. // ColorfulCoinString 梦幻扭蛋字符串标识,数据库和redis
  139. ColorfulCoinString = "colorful"
  140. // WeekCoinString 周星扭蛋字符串标识,数据库和redis
  141. WeekCoinString = "week"
  142. // LplCoinString lpl扭蛋字符串标识,数据库和redis
  143. LplCoinString = "lpl"
  144. // BlessCoinString 新年祈愿扭蛋字符串标识,数据库和redis
  145. BlessCoinString = "bless"
  146. // GetCapsuleDetailFromRoom 接口来源
  147. GetCapsuleDetailFromRoom = "room"
  148. // GetCapsuleDetailFromWeb 接口来源
  149. GetCapsuleDetailFromWeb = "web"
  150. // GetCapsuleDetailFromH5 接口来源
  151. GetCapsuleDetailFromH5 = "h5"
  152. )
  153. const (
  154. //IsBottomPool 是保底奖池
  155. IsBottomPool = 1
  156. //CapsuleActionTrans 转换扭蛋
  157. CapsuleActionTrans = "trans"
  158. )
  159. // CapsuleConf 扭蛋全局配置
  160. type CapsuleConf struct {
  161. CoinConfMap map[int64]*CapsuleCoinConf
  162. CacheTime int64
  163. ChangeFlag int64
  164. RwLock sync.RWMutex
  165. }
  166. // CapsuleCoinConf 扭蛋币配置
  167. type CapsuleCoinConf struct {
  168. Id int64
  169. Title string
  170. GiftType int64
  171. ChangeNum int64
  172. StartTime int64
  173. EndTime int64
  174. Status int64
  175. GiftMap map[int64]struct{}
  176. AreaMap map[int64]struct{}
  177. PoolConf *CapsulePoolConf
  178. AllPoolConf []*CapsulePoolConf
  179. }
  180. // CapsulePoolConf 奖池配置
  181. type CapsulePoolConf struct {
  182. Id int64
  183. CoinId int64
  184. Title string
  185. Rule string
  186. StartTime, EndTime int64
  187. Status int64
  188. IsBottom int64
  189. PoolPrize []*CapsulePoolPrize
  190. }
  191. // CapsulePoolPrize 奖池奖品
  192. type CapsulePoolPrize struct {
  193. Id, PoolId, Type, Num, ObjectId, Expire int64
  194. Name, WebImage, MobileImage, Description, JumpUrl string
  195. ProType int64
  196. Chance int64
  197. LoopNum, LimitNum, Weight int64
  198. WhiteUserMap map[int64]struct{}
  199. }
  200. // HistoryOpenInfo 开奖历史
  201. type HistoryOpenInfo struct {
  202. Uid int64 `json:"uid"`
  203. Name string `json:"name"`
  204. Date string `json:"date"`
  205. Num int64 `json:"num"`
  206. }
  207. var (
  208. // CoinIdIntMap map
  209. CoinIdIntMap map[int64]string
  210. // CoinIdStringMap map
  211. CoinIdStringMap map[string]int64
  212. // ReprotConfig map
  213. ReprotConfig map[int64]string
  214. // PrizeNameMap map
  215. PrizeNameMap map[int64]string
  216. // PrizeExpireMap map
  217. PrizeExpireMap map[int64]string
  218. // UnLockGetWrong flag
  219. UnLockGetWrong = "UnLockGetWrong"
  220. // ErrUnLockGet error
  221. ErrUnLockGet = errors.New(UnLockGetWrong)
  222. capsuleConf CapsuleConf
  223. whitePrizeMap sync.Map
  224. )
  225. func init() {
  226. CoinIdIntMap = make(map[int64]string)
  227. CoinIdIntMap[NormalCoinId] = NormalCoinString
  228. CoinIdIntMap[ColorfulCoinId] = ColorfulCoinString
  229. CoinIdIntMap[WeekCoinId] = WeekCoinString
  230. CoinIdIntMap[LplCoinId] = LplCoinString
  231. CoinIdIntMap[BlessCoinId] = BlessCoinString
  232. CoinIdStringMap = make(map[string]int64)
  233. CoinIdStringMap[NormalCoinString] = NormalCoinId
  234. CoinIdStringMap[ColorfulCoinString] = ColorfulCoinId
  235. CoinIdStringMap[WeekCoinString] = WeekCoinId
  236. CoinIdStringMap[LplCoinString] = LplCoinId
  237. CoinIdStringMap[BlessCoinString] = BlessCoinId
  238. ReprotConfig = make(map[int64]string)
  239. ReprotConfig[0] = "未知"
  240. ReprotConfig[1] = "增加普通扭蛋"
  241. ReprotConfig[2] = "增加梦幻扭蛋"
  242. ReprotConfig[3] = "减少普通扭蛋"
  243. ReprotConfig[4] = "减少梦幻扭蛋"
  244. ReprotConfig[5] = "梦幻转化普通"
  245. PrizeNameMap = make(map[int64]string)
  246. PrizeNameMap[CapsulePrizeGift1Type] = "辣条"
  247. PrizeNameMap[CapsulePrizeTitleType] = "头衔"
  248. PrizeNameMap[CapsulePrizeStuff1Type] = "经验原石"
  249. PrizeNameMap[CapsulePrizeStuff2Type] = "经验曜石"
  250. PrizeNameMap[CapsulePrizeStuff3Type] = "贤者之石"
  251. PrizeNameMap[CapsulePrizeSmallTvType] = "小电视抱枕"
  252. PrizeNameMap[CapsulePrizeGuard3Type] = "舰长体验"
  253. PrizeNameMap[CapsulePrizeGuard2Type] = "提督体验"
  254. PrizeNameMap[CapsulePrizeScoreAdd] = "积分加成卡"
  255. PrizeNameMap[CapsulePrizeSmallStar] = "小星星"
  256. PrizeNameMap[CapsulePrizeWeekScore] = "抽奖券"
  257. PrizeNameMap[CapsulePrizeDanmuColor] = "金色弹幕"
  258. PrizeNameMap[CapsulePrizeLplScore] = "LPL抽奖券"
  259. PrizeNameMap[CapsulePrizeLplProduct1] = "2019拜年祭小电视猪"
  260. PrizeNameMap[CapsulePrizeLplProduct2] = "小电视毛绒公仔"
  261. PrizeNameMap[CapsulePrizeLplProduct3] = "机械之心桌垫"
  262. PrizeNameMap[CapsulePrizeProduct1] = "迎新礼盒+新年台历"
  263. PrizeNameMap[CapsulePrizeProduct2] = "新年锦鲤围巾"
  264. PrizeNameMap[CapsulePrizeProduct3] = "拜年祭挂件+拜年祭立牌+年夜饭挂画"
  265. PrizeNameMap[CapsulePrizeProduct4] = "拜年祭耳罩"
  266. PrizeNameMap[CapsulePrizeProduct5] = "小电视猪挂件+小电视猪公仔"
  267. PrizeNameMap[CapsulePrizeProduct6] = "拜年祭徽章"
  268. PrizeNameMap[CapsulePrizeCoupon1] = "会员购20元优惠券"
  269. PrizeNameMap[CapsulePrizeCoupon2] = "会员购40元优惠券"
  270. PrizeNameMap[CapsulePrizeCoupon3] = "会员购60元优惠券"
  271. PrizeExpireMap = make(map[int64]string)
  272. PrizeExpireMap[CapsulePrizeExpire1Day] = "1天"
  273. PrizeExpireMap[CapsulePrizeExpire3Day] = "3天"
  274. PrizeExpireMap[CapsulePrizeExpire1Week] = "1周"
  275. PrizeExpireMap[CapsulePrizeExpire3Month] = "3个月"
  276. PrizeExpireMap[CapsulePrizeExpireForever] = "永久"
  277. }
  278. // GetCapsuleConf 获取扭蛋币配置
  279. func (d *Dao) GetCapsuleConf(ctx context.Context) (conf map[int64]*CapsuleCoinConf, err error) {
  280. capsuleConf.RwLock.RLock()
  281. tmpConf := capsuleConf.CoinConfMap
  282. capsuleConf.RwLock.RUnlock()
  283. if len(tmpConf) == 0 {
  284. redisChangeFlag, _ := d.GetCapsuleChangeFlag(ctx)
  285. tmpConf, err = d.RelaodCapsuleConfig(ctx, redisChangeFlag)
  286. if err != nil || tmpConf == nil || len(tmpConf) == 0 {
  287. log.Error("[dap.capsule | GetCapsuleConf] CapsuleCoinConf is empty")
  288. return nil, err
  289. }
  290. }
  291. now := time.Now().Unix()
  292. conf = make(map[int64]*CapsuleCoinConf)
  293. for coinId := range CoinIdIntMap {
  294. if _, ok := tmpConf[coinId]; ok {
  295. coinConf := tmpConf[coinId]
  296. if coinConf.AllPoolConf != nil && len(coinConf.AllPoolConf) > 0 {
  297. for _, poolConf := range coinConf.AllPoolConf {
  298. if poolConf.StartTime < now && poolConf.EndTime > now {
  299. if _, ok := conf[coinId]; !ok {
  300. conf[coinId] = &CapsuleCoinConf{
  301. Id: coinConf.Id,
  302. Title: coinConf.Title,
  303. GiftType: coinConf.GiftType,
  304. ChangeNum: coinConf.ChangeNum,
  305. StartTime: coinConf.StartTime,
  306. EndTime: coinConf.EndTime,
  307. Status: coinConf.Status,
  308. GiftMap: coinConf.GiftMap,
  309. AreaMap: coinConf.AreaMap,
  310. PoolConf: poolConf,
  311. AllPoolConf: coinConf.AllPoolConf,
  312. }
  313. } else {
  314. if poolConf.IsBottom != IsBottomPool {
  315. conf[coinId] = &CapsuleCoinConf{
  316. Id: coinConf.Id,
  317. Title: coinConf.Title,
  318. GiftType: coinConf.GiftType,
  319. ChangeNum: coinConf.ChangeNum,
  320. StartTime: coinConf.StartTime,
  321. EndTime: coinConf.EndTime,
  322. Status: coinConf.Status,
  323. GiftMap: coinConf.GiftMap,
  324. AreaMap: coinConf.AreaMap,
  325. PoolConf: poolConf,
  326. AllPoolConf: coinConf.AllPoolConf,
  327. }
  328. }
  329. }
  330. }
  331. }
  332. }
  333. }
  334. }
  335. return conf, nil
  336. }
  337. func getCapsuleTable(Uid int64) int64 {
  338. return Uid % 10
  339. }
  340. func userKey(uid int64) string {
  341. return fmt.Sprintf(_userCapsuleCoinRedis, uid)
  342. }
  343. func userInfoKey(uid int64, coinId int64) string {
  344. return fmt.Sprintf(_userInfoRedis, uid, coinId)
  345. }
  346. func openHistoryKey(coinType string) string {
  347. return fmt.Sprintf(_openHistoryRedis, coinType)
  348. }
  349. func openTotalCountKey() string {
  350. return _historyOpenCount
  351. }
  352. func openGiftCountKey(giftId int64, day string) string {
  353. return fmt.Sprintf(_historyGiftCount, giftId, day)
  354. }
  355. func whiteUserPrizeKey(uid int64) string {
  356. return fmt.Sprintf(_whiteUserPrizeRedis, uid)
  357. }
  358. // GetUserCapsuleInfo 获取扭蛋币积分
  359. func (d *Dao) GetUserCapsuleInfo(c context.Context, uid int64) (coinMap map[int64]int64, err error) {
  360. var (
  361. isEmpty bool
  362. uKey string
  363. normalScore int64
  364. colorfulScore int64
  365. )
  366. uKey = userKey(uid)
  367. conn := d.redis.Get(c)
  368. defer conn.Close()
  369. uInfo, err := redis.Int64Map(conn.Do("HGETALL", uKey))
  370. if err != nil {
  371. if err == redis.ErrNil {
  372. isEmpty = true
  373. err = nil
  374. } else {
  375. log.Error("[dao.redis_lottery|setUserCapsuleInfoCache] getUserCapsuleInfoCache conn.HMGET(%s) error(%v)", uKey, err)
  376. return
  377. }
  378. } else if len(uInfo) == 0 {
  379. isEmpty = true
  380. }
  381. coinMap = make(map[int64]int64)
  382. if isEmpty {
  383. sqlStr := fmt.Sprintf(_userCapsuleCoinMysql, getCapsuleTable(uid))
  384. row := d.db.QueryRow(c, sqlStr, uid)
  385. err = row.Scan(&normalScore, &colorfulScore)
  386. if err != nil && err != sql.ErrNoRows {
  387. return
  388. }
  389. if err == sql.ErrNoRows {
  390. sqlStr := fmt.Sprintf(_addCapsuleCoinMysql, getCapsuleTable(uid))
  391. _, err = d.db.Exec(c, sqlStr, uid, 0, 0)
  392. if err != nil {
  393. log.Error("[dao.redis_lottery|GetUserCapsuleInfo] init sql(%s) uid(%d) error(%v)", sqlStr, uid, err)
  394. return
  395. }
  396. normalScore, colorfulScore = 0, 0
  397. }
  398. _, err = conn.Do("HMSET", uKey, CoinIdIntMap[NormalCoinId], normalScore, CoinIdIntMap[ColorfulCoinId], colorfulScore)
  399. if err != nil {
  400. log.Error("[dao.redis_lottery|GetUserCapsuleInfo] setUserCapsuleInfoCache conn.HMSET(%s) error(%v)", uKey, err)
  401. }
  402. coinMap[NormalCoinId] = normalScore
  403. coinMap[ColorfulCoinId] = colorfulScore
  404. err = nil
  405. } else {
  406. if _, ok := uInfo[CoinIdIntMap[NormalCoinId]]; ok {
  407. coinMap[NormalCoinId] = uInfo[CoinIdIntMap[NormalCoinId]]
  408. }
  409. if _, ok := uInfo[CoinIdIntMap[ColorfulCoinId]]; ok {
  410. coinMap[ColorfulCoinId] = uInfo[CoinIdIntMap[ColorfulCoinId]]
  411. }
  412. }
  413. return
  414. }
  415. // GetUserInfo 获取扭蛋币详情
  416. func (d *Dao) GetUserInfo(c context.Context, uid, coinId int64) (coinMap map[int64]int64, err error) {
  417. if coinId <= ColorfulCoinId {
  418. coinMap, err = d.GetUserCapsuleInfo(c, uid)
  419. return
  420. }
  421. var (
  422. isEmpty bool
  423. uKey string
  424. score int64
  425. )
  426. uKey = userInfoKey(uid, coinId)
  427. conn := d.redis.Get(c)
  428. defer conn.Close()
  429. score, err = redis.Int64(conn.Do("GET", uKey))
  430. if err != nil {
  431. if err == redis.ErrNil {
  432. isEmpty = true
  433. err = nil
  434. } else {
  435. log.Error("[dao.redis_lottery|GetUserInfo] getUserInfoCache conn.HMGET(%s) error(%v)", uKey, err)
  436. return
  437. }
  438. }
  439. coinMap = make(map[int64]int64)
  440. if isEmpty {
  441. sqlStr := fmt.Sprintf(_userInfoMysql, getCapsuleTable(uid))
  442. row := d.db.QueryRow(c, sqlStr, uid, CoinIdIntMap[coinId])
  443. err = row.Scan(&score)
  444. if err != nil && err != sql.ErrNoRows {
  445. log.Error("[dao.redis_lottery|GetUserInfo] getUserInfoFromDB uid(%d) error(%v)", uid, err)
  446. return
  447. }
  448. if err == sql.ErrNoRows {
  449. sqlStr := fmt.Sprintf(_addInfoMysql, getCapsuleTable(uid))
  450. _, err = d.db.Exec(c, sqlStr, uid, CoinIdIntMap[coinId], 0)
  451. if err != nil {
  452. log.Error("[dao.redis_lottery|GetUserInfo] init sql(%s) uid(%d) error(%v)", sqlStr, uid, err)
  453. return nil, err
  454. }
  455. score = 0
  456. }
  457. _, err = conn.Do("SET", uKey, score)
  458. if err != nil {
  459. log.Error("[dao.redis_lottery|GetUserInfo] setUserCapsuleInfoCache conn.HMSET(%s) error(%v)", uKey, err)
  460. }
  461. coinMap[coinId] = score
  462. err = nil
  463. } else {
  464. coinMap[coinId] = score
  465. }
  466. return
  467. }
  468. // GetOpenHistory 获取扭蛋币历史
  469. func (d *Dao) GetOpenHistory(c context.Context, coinType int64) (ret []*HistoryOpenInfo, err error) {
  470. hkey := openHistoryKey(CoinIdIntMap[coinType])
  471. conn := d.redis.Get(c)
  472. defer conn.Close()
  473. jsons, err := redis.Strings(conn.Do("LRANGE", hkey, 0, OpenHistoryNum-1))
  474. if err != nil {
  475. return
  476. }
  477. length := len(jsons)
  478. if length == 0 {
  479. return
  480. }
  481. ret = make([]*HistoryOpenInfo, length)
  482. for ix, jsonStr := range jsons {
  483. var openInfo HistoryOpenInfo
  484. json.Unmarshal([]byte(jsonStr), &openInfo)
  485. ret[ix] = &openInfo
  486. }
  487. return
  488. }
  489. // GetCoin 获取扭蛋数量
  490. func (d *Dao) GetCoin(score int64, coinConf *CapsuleCoinConf) (coinNum int64) {
  491. if coinConf == nil || coinConf.ChangeNum == 0 {
  492. return 0
  493. }
  494. coinNum = score / coinConf.ChangeNum
  495. return
  496. }
  497. // GetProgress 获取扭蛋币进度
  498. func (d *Dao) GetProgress(score int64, coinConf *CapsuleCoinConf) (process *v1.Progress) {
  499. process = &v1.Progress{}
  500. if coinConf == nil || coinConf.ChangeNum == 0 {
  501. return
  502. }
  503. process.Max = coinConf.ChangeNum
  504. process.Now = score % coinConf.ChangeNum
  505. return
  506. }
  507. // UpdateScore 更新扭蛋币积分
  508. func (d *Dao) UpdateScore(ctx context.Context, uid, coinId, score int64, action, platform string, coinConf *CapsuleCoinConf) (affect int64, err error) {
  509. var (
  510. sqlStr, uKey, iKey string
  511. )
  512. if action == CapsuleActionTrans {
  513. sqlStr = fmt.Sprintf(_transUserCapsuleMysql, getCapsuleTable(uid))
  514. } else {
  515. sqlStr = fmt.Sprintf(_updateUserCapsuleMysql, getCapsuleTable(uid), CoinIdIntMap[coinId], CoinIdIntMap[coinId])
  516. }
  517. conn := d.redis.Get(ctx)
  518. defer conn.Close()
  519. uKey = userKey(uid)
  520. iKey = userInfoKey(uid, coinId)
  521. log.Info("trace UpdateScore start")
  522. affect, err = d.execSqlWithBindParams(ctx, &sqlStr, score, uid)
  523. log.Info("trace UpdateScore end")
  524. if err != nil {
  525. log.Error("[dao.mysql_lottery|updateScore] uid(%d) type(%d) score(%d) error(%v)", uid, coinId, score, err)
  526. _, e := conn.Do("DEL", uKey, iKey)
  527. if e != nil {
  528. log.Error("[dao.redis_lottery|updateScore] conn.DEL(%s, %s) error(%v)", uKey, iKey, e)
  529. }
  530. return
  531. }
  532. _, e := conn.Do("DEL", uKey, iKey)
  533. if e != nil {
  534. log.Error("[dao.redis_lottery|updateScore] conn.DEL(%s, %s) error(%v)", uKey, iKey, e)
  535. }
  536. return
  537. }
  538. // UpdateCapsule 更新扭蛋币积分
  539. func (d *Dao) UpdateCapsule(ctx context.Context, uid, coinId, score int64, action, platform string, coinConf *CapsuleCoinConf) (affect int64, err error) {
  540. var (
  541. sqlStr, uKey, iKey string
  542. )
  543. sqlStr = fmt.Sprintf(_updateUserInfoMysql, getCapsuleTable(uid))
  544. conn := d.redis.Get(ctx)
  545. defer conn.Close()
  546. uKey = userKey(uid)
  547. iKey = userInfoKey(uid, coinId)
  548. log.Info("trace UpdateCapsule start")
  549. affect, err = d.execSqlWithBindParams(ctx, &sqlStr, score, uid, CoinIdIntMap[coinId])
  550. log.Info("trace UpdateCapsule end")
  551. if err != nil {
  552. log.Error("[dao.mysql_lottery|UpdateCapsule] uid(%d) type(%d) score(%d) error(%v)", uid, coinId, score, err)
  553. _, e := conn.Do("DEL", uKey, iKey)
  554. if e != nil {
  555. log.Error("[dao.redis_lottery|UpdateCapsule] conn.DEL(%s, %s) error(%v)", uKey, iKey, e)
  556. }
  557. return
  558. }
  559. _, e := conn.Do("DEL", uKey, iKey)
  560. if e != nil {
  561. log.Error("[dao.redis_lottery|UpdateCapsule] conn.DEL(%s, %s) error(%v)", uKey, iKey, e)
  562. }
  563. return
  564. }
  565. // ReportCapsuleChange 扭蛋流水
  566. func (d *Dao) ReportCapsuleChange(ctx context.Context, coinId, uid, score int64, action, platform string, pInfo, cInfo map[int64]int64, coinConf *CapsuleCoinConf) bool {
  567. if _, ok := pInfo[coinId]; !ok {
  568. return false
  569. }
  570. if _, ok := cInfo[coinId]; !ok {
  571. return false
  572. }
  573. chnageType := coinId
  574. change := d.GetCoin(cInfo[coinId], coinConf) - d.GetCoin(pInfo[coinId], coinConf)
  575. if change > 0 {
  576. d.AddNotice(ctx, uid, coinId, change)
  577. }
  578. var normalPreScore, colorPreScore, normalNowScore, colorNowScore int64
  579. if coinId <= ColorfulCoinId {
  580. if action == CapsuleActionTrans {
  581. chnageType = 5
  582. } else {
  583. if score < 0 {
  584. chnageType += 2
  585. score = -score
  586. }
  587. }
  588. normalPreScore, colorPreScore, normalNowScore, colorNowScore = pInfo[NormalCoinId], pInfo[ColorfulCoinId], cInfo[NormalCoinId], cInfo[ColorfulCoinId]
  589. } else {
  590. chnageType = coinId * 10
  591. if score < 0 {
  592. chnageType += 1
  593. }
  594. normalPreScore, colorPreScore, normalNowScore, colorNowScore = 0, pInfo[coinId], 0, cInfo[coinId]
  595. }
  596. date := time.Now().Format("200601")
  597. sqlStr := fmt.Sprintf(_reportCapsuleChangeMysql, date)
  598. affect, _ := d.execSqlWithBindParams(ctx, &sqlStr, uid, chnageType, score, action, platform, normalPreScore, colorPreScore, normalNowScore, colorNowScore)
  599. var rcontent string
  600. if _, ok := ReprotConfig[chnageType]; ok {
  601. rcontent = ReprotConfig[chnageType]
  602. }
  603. if rcontent == "" {
  604. if chnageType%10 == 0 {
  605. rcontent = "减少" + coinConf.Title
  606. } else if chnageType%10 == 1 {
  607. rcontent = "增加" + coinConf.Title
  608. } else if chnageType%10 == 2 {
  609. rcontent = "转化" + coinConf.Title
  610. }
  611. }
  612. report.User(&report.UserInfo{
  613. Platform: platform,
  614. Business: 101, // 101 102 103 104 105 106
  615. Type: int(chnageType),
  616. Oid: uid,
  617. Action: "capsule_change",
  618. Ctime: time.Now(),
  619. Index: []interface{}{
  620. rcontent,
  621. score,
  622. normalNowScore,
  623. colorNowScore,
  624. action,
  625. },
  626. })
  627. return affect > 0
  628. }
  629. // PayCoin 支付扭蛋币
  630. func (d *Dao) PayCoin(ctx context.Context, uid int64, coinConf *CapsuleCoinConf, openCount int64, action, platform string) (status int64, pInfo map[int64]int64, err error) {
  631. lockKey := fmt.Sprintf(_openLockRedis, uid)
  632. isGetLock, lockString, err := d.Lock(ctx, lockKey, 10000, 0, 0)
  633. if err != nil || !isGetLock {
  634. return
  635. }
  636. conn := d.redis.Get(ctx)
  637. defer conn.Close()
  638. userData, err := d.GetUserInfo(ctx, uid, coinConf.Id)
  639. if err != nil {
  640. d.UnLock(ctx, lockKey, lockString)
  641. return
  642. }
  643. var score int64
  644. if _, ok := userData[coinConf.Id]; ok {
  645. score = userData[coinConf.Id]
  646. }
  647. coinNum := d.GetCoin(score, coinConf)
  648. if coinNum < openCount {
  649. d.UnLock(ctx, lockKey, lockString)
  650. return 1, userData, nil
  651. }
  652. value := openCount * coinConf.ChangeNum
  653. _, err = d.UpdateScore(ctx, uid, coinConf.Id, -value, action, platform, coinConf)
  654. if err != nil {
  655. d.UnLock(ctx, lockKey, lockString)
  656. return
  657. }
  658. d.UnLock(ctx, lockKey, lockString)
  659. return 0, userData, nil
  660. }
  661. // PayCapsule 支付扭蛋币
  662. func (d *Dao) PayCapsule(ctx context.Context, uid int64, coinConf *CapsuleCoinConf, openCount int64, action, platform string) (status int64, pInfo map[int64]int64, err error) {
  663. lockKey := fmt.Sprintf(_openLockRedis, uid)
  664. isGetLock, lockString, err := d.Lock(ctx, lockKey, 10000, 0, 0)
  665. if err != nil {
  666. return
  667. }
  668. if !isGetLock {
  669. return 1, nil, nil
  670. }
  671. conn := d.redis.Get(ctx)
  672. defer conn.Close()
  673. userData, err := d.GetUserInfo(ctx, uid, coinConf.Id)
  674. if err != nil {
  675. d.UnLock(ctx, lockKey, lockString)
  676. return
  677. }
  678. var score int64
  679. if _, ok := userData[coinConf.Id]; ok {
  680. score = userData[coinConf.Id]
  681. }
  682. coinNum := d.GetCoin(score, coinConf)
  683. if coinNum < openCount {
  684. d.UnLock(ctx, lockKey, lockString)
  685. return 2, userData, nil
  686. }
  687. value := openCount * coinConf.ChangeNum
  688. _, err = d.UpdateCapsule(ctx, uid, coinConf.Id, -value, action, platform, coinConf)
  689. if err != nil {
  690. d.UnLock(ctx, lockKey, lockString)
  691. return
  692. }
  693. d.UnLock(ctx, lockKey, lockString)
  694. return 0, userData, nil
  695. }
  696. // IsPoolOpen 判断扭蛋池是否开启
  697. func (d *Dao) IsPoolOpen(coinConf *CapsuleCoinConf, coinId int64) bool {
  698. if coinConf == nil {
  699. return false
  700. }
  701. if coinConf.PoolConf == nil || len(coinConf.PoolConf.PoolPrize) == 0 {
  702. return false
  703. }
  704. now := time.Now().Unix()
  705. if coinConf.PoolConf.StartTime < now && coinConf.PoolConf.EndTime > now {
  706. return true
  707. }
  708. return false
  709. }
  710. // GetGift 获取扭蛋奖池奖品
  711. func (d *Dao) GetGift(ctx context.Context, coinId int64) (gift []*CapsulePoolPrize, err error) {
  712. coinConfMap, err := d.GetCapsuleConf(ctx)
  713. if err != nil || len(coinConfMap) == 0 {
  714. return
  715. }
  716. if _, ok := coinConfMap[coinId]; !ok {
  717. return
  718. }
  719. conf := coinConfMap[coinId]
  720. if conf.PoolConf == nil || len(conf.PoolConf.PoolPrize) == 0 {
  721. return
  722. }
  723. gift = conf.PoolConf.PoolPrize
  724. return
  725. }
  726. // IncrOpenCount 增加开奖次数
  727. func (d *Dao) IncrOpenCount(ctx context.Context, coinId int64) (cnt int64) {
  728. hkey := openTotalCountKey()
  729. conn := d.redis.Get(ctx)
  730. defer conn.Close()
  731. cnt, err := redis.Int64(conn.Do("HINCRBY", hkey, CoinIdIntMap[coinId], 1))
  732. if err != nil {
  733. return
  734. }
  735. if cnt > 0 {
  736. return cnt
  737. }
  738. return 0
  739. }
  740. // GetOpenCount 获取开奖次数
  741. func (d *Dao) GetOpenCount(ctx context.Context, coinId int64) (cnt int64) {
  742. hkey := openTotalCountKey()
  743. conn := d.redis.Get(ctx)
  744. defer conn.Close()
  745. cnt, err := redis.Int64(conn.Do("HGET", hkey, CoinIdIntMap[coinId]))
  746. if err != nil {
  747. return 0
  748. }
  749. if cnt > 0 {
  750. return cnt
  751. }
  752. return 0
  753. }
  754. func getWhiteGift(coinConf *CapsuleCoinConf) (fixPrize []*CapsulePoolPrize) {
  755. if coinConf == nil || coinConf.PoolConf == nil || len(coinConf.PoolConf.PoolPrize) == 0 {
  756. return
  757. }
  758. fLen := 0
  759. for _, prize := range coinConf.PoolConf.PoolPrize {
  760. if prize.ProType == ProTypeWhite {
  761. fLen++
  762. }
  763. }
  764. if fLen <= 0 {
  765. return
  766. }
  767. fixPrize = make([]*CapsulePoolPrize, fLen)
  768. fLen = 0
  769. for _, prize := range coinConf.PoolConf.PoolPrize {
  770. if prize.ProType == ProTypeWhite {
  771. fixPrize[fLen] = prize
  772. fLen++
  773. }
  774. }
  775. return
  776. }
  777. func getFixGift(coinConf *CapsuleCoinConf) (fixPrize []*CapsulePoolPrize) {
  778. if coinConf == nil || coinConf.PoolConf == nil || len(coinConf.PoolConf.PoolPrize) == 0 {
  779. return
  780. }
  781. fLen := 0
  782. for _, prize := range coinConf.PoolConf.PoolPrize {
  783. if prize.ProType == ProTypeFixDay || prize.ProType == ProTypeFixWeek {
  784. fLen++
  785. }
  786. }
  787. if fLen <= 0 {
  788. return
  789. }
  790. fixPrize = make([]*CapsulePoolPrize, fLen)
  791. fLen = 0
  792. for _, prize := range coinConf.PoolConf.PoolPrize {
  793. if prize.ProType == ProTypeFixDay || prize.ProType == ProTypeFixWeek {
  794. fixPrize[fLen] = prize
  795. fLen++
  796. }
  797. }
  798. return
  799. }
  800. func getRandomGift(coinConf *CapsuleCoinConf) (randomPrize []*CapsulePoolPrize) {
  801. if coinConf == nil || coinConf.PoolConf == nil || len(coinConf.PoolConf.PoolPrize) == 0 {
  802. return
  803. }
  804. rLen := 0
  805. for _, prize := range coinConf.PoolConf.PoolPrize {
  806. if prize.ProType == ProTypeNormal {
  807. rLen++
  808. }
  809. }
  810. if rLen <= 0 {
  811. return
  812. }
  813. randomPrize = make([]*CapsulePoolPrize, rLen)
  814. rLen = 0
  815. for _, prize := range coinConf.PoolConf.PoolPrize {
  816. if prize.ProType == ProTypeNormal {
  817. randomPrize[rLen] = prize
  818. rLen++
  819. }
  820. }
  821. return
  822. }
  823. func (d *Dao) checkWhiteLimit(ctx context.Context, uid int64, prize *CapsulePoolPrize) bool {
  824. if prize == nil || prize.ProType != ProTypeWhite || len(prize.WhiteUserMap) == 0 {
  825. return false
  826. }
  827. if _, ok := prize.WhiteUserMap[uid]; !ok {
  828. return false
  829. }
  830. conn := d.redis.Get(ctx)
  831. defer conn.Close()
  832. dtime := time.Now()
  833. day := dtime.Format("2006-01-02")
  834. uKey := whiteUserPrizeKey(uid)
  835. lastTime, err := redis.Int64(conn.Do("GET", uKey))
  836. if err != nil {
  837. if err == redis.ErrNil {
  838. // 回源数据库
  839. prizeLog, err := d.GetUserPrizeLog(ctx, prize.Id, uid)
  840. if err != nil {
  841. return false
  842. }
  843. if prizeLog != nil {
  844. lastTime = prizeLog.Timestamp
  845. } else {
  846. lastTime = 0
  847. }
  848. conn.Do("SET", uKey, lastTime, "EX", 30*86400)
  849. } else {
  850. return false
  851. }
  852. }
  853. if dtime.Unix()-lastTime < 7*86400 {
  854. return false
  855. }
  856. gKey := openGiftCountKey(prize.Id, day)
  857. isGetLock, lockString, errLock := d.Lock(ctx, gKey, 1000000, 0, 0)
  858. if errLock != nil || !isGetLock {
  859. return false
  860. }
  861. mKey := day + strconv.FormatInt(prize.Id, 10)
  862. _, ok := whitePrizeMap.Load(mKey)
  863. if ok {
  864. d.UnLock(ctx, gKey, lockString)
  865. return false
  866. }
  867. // 回源数据库
  868. prizeLog, errDb := d.GetPrizeDayLog(ctx, prize.Id, day)
  869. if errDb != nil {
  870. d.UnLock(ctx, gKey, lockString)
  871. return false
  872. }
  873. if prizeLog != nil {
  874. whitePrizeMap.Store(mKey, prizeLog.Uid)
  875. d.UnLock(ctx, gKey, lockString)
  876. return false
  877. }
  878. stutus, errAdd := d.AddPrizeData(ctx, prize.Id, uid, day, dtime.Unix())
  879. if errAdd != nil || !stutus {
  880. d.UnLock(ctx, gKey, lockString)
  881. return false
  882. }
  883. whitePrizeMap.Store(mKey, uid)
  884. conn.Do("DEL", uKey)
  885. d.UnLock(ctx, gKey, lockString)
  886. return true
  887. }
  888. func (d *Dao) checkPrizeLimit(ctx context.Context, prize *CapsulePoolPrize) bool {
  889. if prize == nil {
  890. return false
  891. }
  892. if prize.ProType != ProTypeFixDay && prize.ProType != ProTypeFixWeek {
  893. return false
  894. }
  895. conn := d.redis.Get(ctx)
  896. defer conn.Close()
  897. var status bool
  898. switch prize.ProType {
  899. case ProTypeFixDay:
  900. day := time.Now().Format("2006-01-02")
  901. gKey := openGiftCountKey(prize.Id, day)
  902. cnt, err := redis.Int64(conn.Do("INCRBY", gKey, 1))
  903. if err != nil {
  904. status = false
  905. break
  906. }
  907. status = cnt <= prize.LimitNum
  908. case ProTypeFixWeek:
  909. wDay := time.Now().Weekday()
  910. if wDay == 0 {
  911. wDay = 7
  912. }
  913. diff := time.Duration(wDay)
  914. day := time.Now().Add(time.Second * 86400 * (diff - 1)).Format("2006-01-02")
  915. gKey := openGiftCountKey(prize.Id, day)
  916. cnt, err := redis.Int64(conn.Do("INCRBY", gKey, 1))
  917. if err != nil {
  918. status = false
  919. break
  920. }
  921. status = cnt <= prize.LimitNum
  922. default:
  923. status = false
  924. }
  925. return status
  926. }
  927. func (d *Dao) getWhiteAward(ctx context.Context, uid int64, coinConf *CapsuleCoinConf) (prize *CapsulePoolPrize) {
  928. whitePrize := getWhiteGift(coinConf)
  929. if len(whitePrize) == 0 {
  930. return
  931. }
  932. openCount := d.GetOpenCount(ctx, coinConf.Id)
  933. if openCount == 0 {
  934. return
  935. }
  936. for _, wprize := range whitePrize {
  937. if !d.checkWhiteLimit(ctx, uid, wprize) {
  938. continue
  939. }
  940. return &CapsulePoolPrize{
  941. Id: wprize.Id,
  942. PoolId: wprize.PoolId,
  943. Type: wprize.Type,
  944. Num: wprize.Num,
  945. Name: wprize.Name,
  946. WebImage: wprize.WebImage,
  947. MobileImage: wprize.MobileImage,
  948. Description: wprize.Description,
  949. ProType: wprize.ProType,
  950. JumpUrl: wprize.JumpUrl,
  951. ObjectId: wprize.ObjectId,
  952. Expire: wprize.Expire,
  953. Weight: wprize.Weight,
  954. }
  955. }
  956. return
  957. }
  958. func (d *Dao) getFixAward(ctx context.Context, uid, openCount int64, coinConf *CapsuleCoinConf) (prize *CapsulePoolPrize) {
  959. fixPrize := getFixGift(coinConf)
  960. if len(fixPrize) == 0 {
  961. return
  962. }
  963. if openCount == 0 {
  964. return
  965. }
  966. var loop int64
  967. for _, fprize := range fixPrize {
  968. loop = fprize.LoopNum
  969. if openCount%loop != 0 {
  970. continue
  971. }
  972. if d.checkPrizeLimit(ctx, fprize) {
  973. return &CapsulePoolPrize{
  974. Id: fprize.Id,
  975. PoolId: fprize.PoolId,
  976. Type: fprize.Type,
  977. Num: fprize.Num,
  978. Name: fprize.Name,
  979. WebImage: fprize.WebImage,
  980. MobileImage: fprize.MobileImage,
  981. Description: fprize.Description,
  982. ProType: fprize.ProType,
  983. JumpUrl: fprize.JumpUrl,
  984. ObjectId: fprize.ObjectId,
  985. Expire: fprize.Expire,
  986. Weight: fprize.Weight,
  987. }
  988. }
  989. }
  990. return
  991. }
  992. func (d *Dao) getRandomAward(ctx context.Context, uid int64, coinConf *CapsuleCoinConf) (prize *CapsulePoolPrize) {
  993. randomPrize := getRandomGift(coinConf)
  994. if len(randomPrize) == 0 {
  995. return
  996. }
  997. var start, random, total int64
  998. for _, prize := range randomPrize {
  999. if prize == nil {
  1000. rbyte, _ := json.Marshal(randomPrize)
  1001. log.Error("[dao.capsule | getRandomAward] randomPrize error : %s", string(rbyte))
  1002. continue
  1003. }
  1004. total += prize.Chance
  1005. }
  1006. r := rand.New(rand.NewSource(time.Now().UnixNano()))
  1007. random = r.Int63n(total - 1)
  1008. for _, prize := range randomPrize {
  1009. if random >= start && random < start+prize.Chance {
  1010. return &CapsulePoolPrize{
  1011. Id: prize.Id,
  1012. PoolId: prize.PoolId,
  1013. Type: prize.Type,
  1014. Num: prize.Num,
  1015. Name: prize.Name,
  1016. WebImage: prize.WebImage,
  1017. MobileImage: prize.MobileImage,
  1018. Description: prize.Description,
  1019. ProType: prize.ProType,
  1020. JumpUrl: prize.JumpUrl,
  1021. ObjectId: prize.ObjectId,
  1022. Expire: prize.Expire,
  1023. Weight: prize.Weight,
  1024. }
  1025. }
  1026. start += prize.Chance
  1027. }
  1028. return
  1029. }
  1030. // OpenCapsule 开启扭蛋
  1031. func (d *Dao) OpenCapsule(ctx context.Context, uid int64, coinConf *CapsuleCoinConf, iTime, openCount int64, isGetFixAward bool, entryMap map[int64]bool) (award *CapsulePoolPrize) {
  1032. if iTime == 0 {
  1033. award = d.getWhiteAward(ctx, uid, coinConf)
  1034. if award != nil {
  1035. return award
  1036. }
  1037. }
  1038. if isGetFixAward {
  1039. award = d.getFixAward(ctx, uid, openCount, coinConf)
  1040. if award != nil {
  1041. if _, ok := entryMap[award.Id]; !ok {
  1042. return
  1043. }
  1044. }
  1045. }
  1046. award = d.getRandomAward(ctx, uid, coinConf)
  1047. if award != nil {
  1048. return
  1049. }
  1050. return
  1051. }
  1052. // LogAward 记录抽奖奖励
  1053. func (d *Dao) LogAward(ctx context.Context, uid int64, coinId int64, awards []*CapsulePoolPrize) {
  1054. if len(awards) == 0 {
  1055. return
  1056. }
  1057. hkey := openHistoryKey(CoinIdIntMap[coinId])
  1058. day := time.Now().Format("2006-01-02")
  1059. logs := make([]interface{}, len(awards)+1)
  1060. logs[0] = hkey
  1061. ll := 1
  1062. for _, award := range awards {
  1063. if award.Type == CapsulePrizeGift1Type {
  1064. continue
  1065. }
  1066. info := HistoryOpenInfo{Uid: uid, Name: award.Name, Num: award.Num, Date: day}
  1067. b, err := json.Marshal(info)
  1068. if err == nil {
  1069. logs[ll] = string(b)
  1070. ll++
  1071. }
  1072. }
  1073. if ll == 1 {
  1074. return
  1075. }
  1076. logs = logs[0:ll]
  1077. conn := d.redis.Get(ctx)
  1078. defer conn.Close()
  1079. conn.Do("LPUSH", logs...)
  1080. }
  1081. // AddNotice 增加标记
  1082. func (d *Dao) AddNotice(ctx context.Context, uid, coinId, coinNum int64) {
  1083. nKey := fmt.Sprintf(_capsuleNotice, CoinIdIntMap[coinId], uid)
  1084. conn := d.redis.Get(ctx)
  1085. defer conn.Close()
  1086. conn.Do("SET", nKey, coinNum, 30*86400)
  1087. }
  1088. // ClearNotice 清除标记
  1089. func (d *Dao) ClearNotice(ctx context.Context, uid, coinId int64) {
  1090. nKey := fmt.Sprintf(_capsuleNotice, CoinIdIntMap[coinId], uid)
  1091. conn := d.redis.Get(ctx)
  1092. defer conn.Close()
  1093. conn.Do("DEL", nKey)
  1094. }
  1095. // ClearNoticeBoth 清除标记
  1096. func (d *Dao) ClearNoticeBoth(ctx context.Context, uid int64) {
  1097. keys := make([]interface{}, len(CoinIdIntMap))
  1098. var i = 0
  1099. for _, coinType := range CoinIdIntMap {
  1100. keys[i] = fmt.Sprintf(_capsuleNotice, coinType, uid)
  1101. }
  1102. conn := d.redis.Get(ctx)
  1103. defer conn.Close()
  1104. conn.Do("DEL", keys...)
  1105. }
  1106. // GetChangeNum 获取扭蛋变化数量
  1107. func (d *Dao) GetChangeNum(ctx context.Context, uid, coinId int64) int64 {
  1108. nKey := fmt.Sprintf(_capsuleNotice, CoinIdIntMap[coinId], uid)
  1109. conn := d.redis.Get(ctx)
  1110. defer conn.Close()
  1111. change, err := redis.Int64(conn.Do("GET", nKey))
  1112. if err != nil {
  1113. return 0
  1114. }
  1115. return change
  1116. }
  1117. // SetCapsuleChangeFlag 设置扭蛋配置变化标记
  1118. func (d *Dao) SetCapsuleChangeFlag(ctx context.Context) (status string, err error) {
  1119. conn := d.redis.Get(ctx)
  1120. defer conn.Close()
  1121. status, err = redis.String(conn.Do("SET", _capsuleConfRand, time.Now().Unix()))
  1122. if err != nil {
  1123. log.Error("[dao.capsule | SetCapsuleChangeFlag] redis set error : %v", err)
  1124. return
  1125. }
  1126. return
  1127. }
  1128. // GetCapsuleChangeFlag 获取扭蛋配置变化标记
  1129. func (d *Dao) GetCapsuleChangeFlag(ctx context.Context) (changeFlag int64, err error) {
  1130. conn := d.redis.Get(ctx)
  1131. defer conn.Close()
  1132. changeFlag, err = redis.Int64(conn.Do("GET", _capsuleConfRand))
  1133. if err != nil {
  1134. return
  1135. }
  1136. return
  1137. }
  1138. // GetCapsuleChangeInfo 获取扭蛋配置信息
  1139. func (d *Dao) GetCapsuleChangeInfo(ctx context.Context) (int64, int64) {
  1140. capsuleConf.RwLock.RLock()
  1141. CacheTime := capsuleConf.CacheTime
  1142. ChangeFlag := capsuleConf.ChangeFlag
  1143. capsuleConf.RwLock.RUnlock()
  1144. return CacheTime, ChangeFlag
  1145. }
  1146. // RelaodCapsuleConfig 重新加载扭蛋配置
  1147. func (d *Dao) RelaodCapsuleConfig(ctx context.Context, changeFlag int64) (conf map[int64]*CapsuleCoinConf, err error) {
  1148. coinMap, err := d.GetCoinMap(ctx)
  1149. if err != nil || len(coinMap) == 0 {
  1150. log.Error("[dao.capsule | RelaodCapsuleConfig] coinMap is empty")
  1151. return
  1152. }
  1153. coinIds := make([]int64, len(coinMap))
  1154. ix := 0
  1155. for _, coinInfo := range coinMap {
  1156. coinIds[ix] = coinInfo.Id
  1157. ix++
  1158. }
  1159. coinConfigMap, err := d.GetCoinConfigMap(ctx, coinIds)
  1160. if err != nil || len(coinConfigMap) == 0 {
  1161. log.Error("[dao.capsule | RelaodCapsuleConfig] CoinConfigMap is empty")
  1162. return
  1163. }
  1164. poolMap, err := d.GetPoolMap(ctx, coinIds)
  1165. if err != nil || len(poolMap) == 0 {
  1166. log.Error("[dao.capsule | RelaodCapsuleConfig] PoolMap is empty")
  1167. return
  1168. }
  1169. poolIds := make([]int64, 0)
  1170. for _, pools := range poolMap {
  1171. for _, pool := range pools {
  1172. poolIds = append(poolIds, pool.Id)
  1173. }
  1174. }
  1175. poolPrizeMap, err := d.GetPoolPrizeMap(ctx, poolIds)
  1176. if err != nil || len(poolPrizeMap) == 0 {
  1177. log.Error("[dao.capsule | RelaodCapsuleConfig] PoolPrizeMap is empty")
  1178. return
  1179. }
  1180. coinConfMap := make(map[int64]*CapsuleCoinConf)
  1181. ids := make([]int64, 0)
  1182. prizeIds := make([]int64, 0)
  1183. idMap := make(map[int64]struct{})
  1184. for _, prizeList := range poolPrizeMap {
  1185. for _, prize := range prizeList {
  1186. if prize.ObjectId != 0 && prize.Type == CapsulePrizeTitleType {
  1187. if _, ok := idMap[prize.ObjectId]; !ok {
  1188. ids = append(ids, prize.ObjectId)
  1189. idMap[prize.ObjectId] = struct{}{}
  1190. }
  1191. }
  1192. prizeIds = append(prizeIds, prize.Id)
  1193. }
  1194. }
  1195. prizeWhiteMap, err1 := d.GetWhiteUserMap(ctx, prizeIds)
  1196. if err1 != nil {
  1197. log.Error("[dao.capsule | RelaodCapsuleConfig] GetWhiteUserMap error")
  1198. }
  1199. titleMap := make(map[int64]string)
  1200. if len(ids) != 0 {
  1201. TitleData, err1 := RcApi.V1UserTitle.GetTitleByIds(ctx, &v12.UserTitleGetTitleByIdsReq{Ids: ids})
  1202. if err1 != nil {
  1203. log.Error("[dao.capsule | RelaodCapsuleConfig] GetTitleByIds error")
  1204. }
  1205. if TitleData != nil && TitleData.Data != nil {
  1206. titleMap = TitleData.Data
  1207. }
  1208. }
  1209. for coinId, coinConf := range coinMap {
  1210. conf := &CapsuleCoinConf{}
  1211. conf.Status = coinConf.Status
  1212. conf.GiftType = coinConf.GiftType
  1213. conf.Title = coinConf.Title
  1214. conf.EndTime = coinConf.EndTime
  1215. conf.StartTime = coinConf.StartTime
  1216. conf.Id = coinConf.Id
  1217. conf.ChangeNum = coinConf.ChangeNum
  1218. if _, ok := coinConfigMap[coinId]; ok {
  1219. coinConfig := coinConfigMap[coinId]
  1220. gifts := make(map[int64]struct{})
  1221. areas := make(map[int64]struct{})
  1222. for _, config := range coinConfig {
  1223. if config.GiftId > 0 {
  1224. gifts[config.GiftId] = struct{}{}
  1225. }
  1226. if config.AreaV2ParentId > 0 {
  1227. areas[config.AreaV2Id] = struct{}{}
  1228. }
  1229. }
  1230. conf.AreaMap = areas
  1231. conf.GiftMap = gifts
  1232. }
  1233. if _, ok := poolMap[coinId]; ok {
  1234. for _, poolConf := range poolMap[coinId] {
  1235. pool := &CapsulePoolConf{}
  1236. pool.Id = poolConf.Id
  1237. pool.StartTime = poolConf.StartTime
  1238. pool.EndTime = poolConf.EndTime
  1239. pool.Title = poolConf.Title
  1240. pool.Status = poolConf.Status
  1241. pool.Rule = poolConf.Description
  1242. pool.CoinId = poolConf.CoinId
  1243. pool.IsBottom = poolConf.IsBottom
  1244. if _, ok := poolPrizeMap[pool.Id]; ok {
  1245. prizeConfigs := poolPrizeMap[pool.Id]
  1246. pool.PoolPrize = make([]*CapsulePoolPrize, len(poolPrizeMap[pool.Id]))
  1247. for ix, prizeConfig := range prizeConfigs {
  1248. name := PrizeNameMap[prizeConfig.Type]
  1249. if prizeConfig.Type == CapsulePrizeTitleType && titleMap != nil {
  1250. if _, ok := titleMap[prizeConfig.ObjectId]; ok {
  1251. name = titleMap[prizeConfig.ObjectId]
  1252. }
  1253. }
  1254. prize := &CapsulePoolPrize{
  1255. Id: prizeConfig.Id,
  1256. PoolId: prizeConfig.PoolId,
  1257. Type: prizeConfig.Type,
  1258. Num: prizeConfig.Num,
  1259. ObjectId: prizeConfig.ObjectId,
  1260. Expire: prizeConfig.Expire,
  1261. Name: name,
  1262. WebImage: prizeConfig.WebUrl,
  1263. MobileImage: prizeConfig.MobileUrl,
  1264. Description: prizeConfig.Description,
  1265. JumpUrl: prizeConfig.JumpUrl,
  1266. ProType: prizeConfig.ProType,
  1267. Chance: prizeConfig.Chance,
  1268. LoopNum: prizeConfig.LoopNum,
  1269. LimitNum: prizeConfig.LimitNum,
  1270. Weight: prizeConfig.Weight,
  1271. }
  1272. prize.WhiteUserMap = make(map[int64]struct{})
  1273. if prize.ProType == ProTypeWhite {
  1274. if _, ok := prizeWhiteMap[prize.Id]; ok {
  1275. if len(prizeWhiteMap[prize.Id]) > 0 {
  1276. for _, wuid := range prizeWhiteMap[prize.Id] {
  1277. prize.WhiteUserMap[wuid] = struct{}{}
  1278. }
  1279. }
  1280. }
  1281. }
  1282. pool.PoolPrize[ix] = prize
  1283. }
  1284. }
  1285. if conf.AllPoolConf == nil {
  1286. conf.AllPoolConf = make([]*CapsulePoolConf, 0)
  1287. }
  1288. conf.AllPoolConf = append(conf.AllPoolConf, pool)
  1289. }
  1290. }
  1291. coinConfMap[coinId] = conf
  1292. }
  1293. cacheTime := time.Now().Unix()
  1294. capsuleConf.RwLock.Lock()
  1295. capsuleConf.CacheTime = cacheTime
  1296. capsuleConf.ChangeFlag = changeFlag
  1297. capsuleConf.CoinConfMap = coinConfMap
  1298. capsuleConf.RwLock.Unlock()
  1299. log.Info("[dao.capsule | RelaodCapsuleConfig] reload conf")
  1300. return coinConfMap, nil
  1301. }
  1302. // GetBottomPrize 获取保底奖品
  1303. func (d *Dao) GetBottomPrize(ctx context.Context, coinConf *CapsuleCoinConf) (bottomPrize *CapsulePoolPrize) {
  1304. if coinConf == nil || coinConf.PoolConf == nil || len(coinConf.PoolConf.PoolPrize) == 0 {
  1305. return nil
  1306. }
  1307. for _, prize := range coinConf.PoolConf.PoolPrize {
  1308. if bottomPrize == nil || bottomPrize.Weight > prize.Weight {
  1309. bottomPrize = &CapsulePoolPrize{
  1310. Id: prize.Id,
  1311. PoolId: prize.PoolId,
  1312. Type: prize.Type,
  1313. Name: prize.Name,
  1314. WebImage: prize.WebImage,
  1315. MobileImage: prize.MobileImage,
  1316. Description: prize.Description,
  1317. ProType: prize.ProType,
  1318. JumpUrl: prize.JumpUrl,
  1319. ObjectId: prize.ObjectId,
  1320. Expire: prize.Expire,
  1321. Weight: prize.Weight,
  1322. Num: prize.Num,
  1323. }
  1324. }
  1325. }
  1326. return bottomPrize
  1327. }
  1328. //GetExpireTime 获取过期时间
  1329. func (d *Dao) GetExpireTime(expire int64) time.Time {
  1330. var td time.Time
  1331. if expire == CapsulePrizeExpire1Day {
  1332. year, month, day := time.Now().Date()
  1333. td = time.Date(year, month, day, 0, 0, 0, 0, time.Now().Location()).Add(86400 * time.Second).Add(86400 * time.Second)
  1334. } else if expire == CapsulePrizeExpire3Day {
  1335. year, month, day := time.Now().Date()
  1336. td = time.Date(year, month, day, 0, 0, 0, 0, time.Now().Location()).Add(86400 * time.Second).Add(3 * 86400 * time.Second)
  1337. } else if expire == CapsulePrizeExpire1Week {
  1338. year, month, day := time.Now().Date()
  1339. td = time.Date(year, month, day, 0, 0, 0, 0, time.Now().Location()).Add(86400 * time.Second).Add(6 * 86400 * time.Second)
  1340. } else if expire == CapsulePrizeExpire3Month {
  1341. year, month, day := time.Now().Date()
  1342. td = time.Date(year, month, day, 0, 0, 0, 0, time.Now().Location()).Add(86400 * time.Second).Add(90 * 86400 * time.Second)
  1343. } else if expire == CapsulePrizeExpireForever {
  1344. td = time.Unix(0, 0)
  1345. } else {
  1346. td = time.Unix(0, 0)
  1347. }
  1348. return td
  1349. }
  1350. // IsAwardEntry 是否是实物奖励
  1351. func (d *Dao) IsAwardEntry(awardType int64) bool {
  1352. if awardType == CapsulePrizeSmallTvType || awardType == CapsulePrizeLplProduct1 || awardType == CapsulePrizeLplProduct2 || awardType == CapsulePrizeLplProduct3 {
  1353. return true
  1354. }
  1355. if awardType >= CapsulePrizeProduct1 && awardType < CapsulePrizeCoupon1 {
  1356. return true
  1357. }
  1358. return false
  1359. }
  1360. // IsAwardCoupon 是否是会员券
  1361. func (d *Dao) IsAwardCoupon(awardType int64) bool {
  1362. return awardType >= CapsulePrizeCoupon1
  1363. }