package containers import ( "joybuywork/internal/app" "joybuywork/internal/repository/cache" "joybuywork/internal/repository/dao" "joybuywork/internal/repository/dbcheck" "joybuywork/internal/repository/finder" "joybuywork/internal/repository/model" "joybuywork/internal/repository/mtype" "joybuywork/internal/repository/withdefault" "joybuywork/internal/service/provider" "joybuywork/internal/service/provider/stocks" "joybuywork/internal/service/queue/producer" "joybuywork/internal/service/queue/types" "joybuywork/internal/utils" "joybuywork/internal/utils/logic" "fmt" "sync" "time" "github.com/panjf2000/ants/v2" "gorm.io/gen" "gorm.io/gorm/clause" ) type userTypeWinner struct { userIds []int64 thank, reward *model.FissionCampaignReward thankInfo, rewardInfo provider.RewardAutoLang } type CampaignQuizOpen struct { logic.Logic ntf chan []*model.FissionQuizReward cpnId int64 } func (s *CampaignQuizOpen) Run() error { return logic.Run(s) } func (s *CampaignQuizOpen) Exec() (err error) { m := s.GetDao().FissionCampaignQuiz var cpnIds []int64 err = m.WithContext(s.Ctx).Where(m.OpenPrize.Lte(time.Now().Unix()), m.OpenType.Eq(mtype.FissionCampaignQuizOpenTypeWait)).Pluck(m.CampaignID, &cpnIds) if err != nil { return } if len(cpnIds) == 0 { return } wg := sync.WaitGroup{} defer wg.Wait() gop, _ := ants.NewPool(50) defer gop.Release() for _, v := range cpnIds { wg.Add(1) cpnId := v _ = gop.Submit(func() { defer wg.Done() defer app.TryCatch(s.Ctx, fmt.Sprintf("竞猜开奖,活动id[%d]", cpnId)) err = s.open(cpnId) if err != nil { return } }) } return } func (s *CampaignQuizOpen) open(cpnId int64) (err error) { //活动数据 cpn, err := finder.Campaign(s.Ctx, cpnId) if err != nil { return } //活动配置数据 fcqM := dao.FissionCampaignQuiz fcq, err := fcqM.WithContext(s.Ctx).Where(fcqM.CampaignID.Eq(cpnId)).Take() if err = dbcheck.DbError(err); err != nil { return } //活动奖品数据 rewardM := dao.FissionCampaignReward rewards, err := rewardM.WithContext(s.Ctx).Where(rewardM.CampaignID.Eq(cpnId), rewardM.IsDel.Eq(mtype.No)).Find() if err != nil { return } if len(rewards) == 0 { //设置成已开奖 app.FeishuNormalSend("活动[%d],没有奖品开奖", cpnId) err = s.setOpen(fcq) return } var utRewardMap = make(map[string][]*model.FissionCampaignReward) var brIds []int64 for _, v := range rewards { brIds = append(brIds, v.RewardID) key := fmt.Sprintf("%s_%d", v.LangMark, v.LevelID) utRewardMap[key] = append(utRewardMap[key], v) } prewards, err := provider.NewRewards(s.Ctx, cpn.ProjectID, brIds...) if err != nil { return } // 赢家的数据,是否可以开奖 winMap, open, err := s.winMap(cpnId, fcq) if err != nil || !open { return } //活动配置数据 conf, err := cache.NewCampaignConfig(s.Ctx).FindByCampaignId(cpnId) if err != nil { return } var userRewardMap = make(map[string]*userTypeWinner) var list []*model.FissionUser m, quizM := s.GetDao().FissionUser.SubTable(cpn.ID), s.GetDao().FissionQuiz _ = m.WithContext(s.Ctx). Join(quizM, quizM.UserID.EqCol(m.ID)). Where(m.CampaignID.Eq(cpn.ID), quizM.OptionID.Gt(0)). FindInBatches(&list, 200, func(tx gen.Dao, batch int) error { for _, v := range list { wnk := fmt.Sprintf("%s_%d", v.UserLang, v.UserLevel) winner, ok := userRewardMap[wnk] if !ok { winner = &userTypeWinner{ userIds: []int64{v.ID}, thank: nil, reward: nil, } var setRewards []*model.FissionCampaignReward switch conf.RewardGroup { case mtype.FissionRewardGroupNormal: setRewards = utRewardMap["_0"] case mtype.FissionRewardGroupUserLevel: setRewards = utRewardMap[fmt.Sprintf("_%d", v.UserLevel)] case mtype.FissionRewardGroupLang: setRewards = utRewardMap[fmt.Sprintf("%s_0", v.UserLang)] case mtype.FissionRewardGroupLangAndUserLevel: setRewards = utRewardMap[fmt.Sprintf("%s_%d", v.UserLang, v.UserLevel)] } for _, reward := range setRewards { info, _ := prewards.InfoAutoLang(reward.RewardID, v.LangMark) if reward.IsThank == mtype.Yes { winner.thank = reward winner.thankInfo = info } else { winner.reward = reward winner.rewardInfo = info } } userRewardMap[wnk] = winner continue } userRewardMap[wnk].userIds = append(userRewardMap[wnk].userIds, v.ID) } return nil }) for _, winner := range userRewardMap { err = s.winner(cpnId, winner, winMap) if err != nil { return } } //设置成已开奖 err = s.setOpen(fcq) return } func (s *CampaignQuizOpen) setOpen(fcq *model.FissionCampaignQuiz) (err error) { //设置成已开奖 fcqM := dao.FissionCampaignQuiz _, err = fcqM.WithContext(s.Ctx).Where(fcqM.ID.Eq(fcq.ID)).UpdateSimple(fcqM.OpenType.Value(mtype.FissionCampaignQuizOpenTypeOpen)) if err != nil { return s.BE().MysqlError(err) } return } func (s *CampaignQuizOpen) winMap(cpnId int64, fcq *model.FissionCampaignQuiz) (winMap map[int64]struct{}, open bool, err error) { //选项数据 fcqM := s.GetDao().FissionCampaignQuiz optionM := dao.FissionCampaignQuizOption winMap = make(map[int64]struct{}) if fcq.RewardRule == mtype.FissionCampaignQuizRewardRuleVote { //对选项排序,并且要找有投票的数据 options, frr := optionM.WithContext(s.Ctx). Where( optionM.CampaignID.Eq(cpnId), optionM.IsDel.Eq(mtype.No), optionM.VoteNum.Gt(0), ). Order(optionM.VoteNum.Desc(), optionM.ID). Find() if frr != nil { err = s.BE().MysqlError(frr) return } //没有投票选项就不会发奖 if len(options) == 0 { //设置成已开奖 _, err = fcqM.WithContext(s.Ctx).Where(fcqM.ID.Eq(fcq.ID)).UpdateSimple(fcqM.OpenType.Value(mtype.FissionCampaignQuizOpenTypeNone)) if err != nil { err = s.BE().MysqlError(err) return } return } l := len(options) //因为排序了,所以第一个肯定是赢的 winMap[options[0].ID] = struct{}{} //看看后面的有没有和赢的相同票数,就一齐赢 for i := 1; i < l; i++ { if options[0].VoteNum != options[i].VoteNum { break } winMap[options[i].ID] = struct{}{} } } else { option, frr := optionM.WithContext(s.Ctx). Where( optionM.CampaignID.Eq(cpnId), optionM.IsDel.Eq(mtype.No), optionM.IsRight.Eq(mtype.Yes), ). Take() if err = dbcheck.DbError(frr); err != nil { err = s.BE().MysqlError(err) return } //还没设置正确答案就退出,等下一次开奖 if option == nil { return } winMap[option.ID] = struct{}{} } return winMap, true, nil } func (s *CampaignQuizOpen) winner(cpnId int64, winner *userTypeWinner, winMap map[int64]struct{}) (err error) { reward, thank := winner.reward, winner.thank rewardInfo, thankInfo := winner.rewardInfo, winner.thankInfo idGroups := utils.Groups(winner.userIds, 200) m := s.GetDao().FissionQuiz for _, ids := range idGroups { var list []*model.FissionQuiz list, err = m.WithContext(s.Ctx).Where(m.UserID.In(ids...)).Find() if err != nil { err = s.BE().MysqlError(err) return } if len(list) == 0 { continue } var add []*model.FissionRewardRecord var winnerIds []int64 var winnerMap = make(map[int64]struct{}) for _, player := range list { if _, ok := winMap[player.OptionID]; ok { winnerMap[player.UserID] = struct{}{} winnerIds = append(winnerIds, player.ID) add = append(add, withdefault.FissionRewardRecord(&model.FissionRewardRecord{ CampaignID: player.CampaignID, UserID: player.UserID, RewardID: reward.ID, Title: rewardInfo.Title, ImgURL: rewardInfo.ImgUrl, RewardType: reward.RewardType, RewardNum: reward.RewardNum, Step: reward.Step, IsThank: reward.IsThank, Status: mtype.FissionRewardRecordStatusWait, FailType: "", FailMsg: "", BaseRewardID: reward.RewardID, })) continue } add = append(add, withdefault.FissionRewardRecord(&model.FissionRewardRecord{ CampaignID: player.CampaignID, UserID: player.UserID, RewardID: thank.ID, Title: thankInfo.Title, ImgURL: thankInfo.ImgUrl, RewardType: thank.RewardType, RewardNum: thank.RewardNum, Step: thank.Step, IsThank: thank.IsThank, Status: mtype.FissionRewardRecordStatusWait, FailType: "", FailMsg: "", BaseRewardID: thank.RewardID, })) } if len(add) == 0 { return } //改成赢的状态 if len(winnerIds) > 0 { _, err = m.WithContext(s.Ctx).Where(m.ID.In(winnerIds...)).UpdateSimple(m.IsWin.Value(mtype.Yes)) if err != nil { err = s.BE().MysqlError(err) return } } fqrM := dao.FissionRewardRecord _ = fqrM.WithContext(s.Ctx).Clauses(clause.Insert{ Modifier: "IGNORE", }).CreateInBatches(add, 200) fqrMs := dao.FissionRewardRecord.SubTable(cpnId) _ = fqrMs.WithContext(s.Ctx).Clauses(clause.Insert{ Modifier: "IGNORE", }).CreateInBatches(add, 200) var useStock = make(map[int64]int64) for _, record := range add { if record.ID == 0 { continue } producer.NewCampaignNotice(s.Ctx).CampaignRewardNotify(types.CampaignRewardNotify{ CampaignId: record.CampaignID, UserId: record.UserID, UserRewardId: record.ID, }) useStock[record.RewardID]++ } for rewardId, num := range useStock { stock, _ := stocks.NewCampaignReward(s.Ctx, rewardId) _ = stock.AddUseStock(num) } } return }