package players import ( "context" "joybuywork/internal/app" "joybuywork/internal/consts" "joybuywork/internal/consts/ckey" "joybuywork/internal/plugins/jsonm" "joybuywork/internal/repository/cache" "joybuywork/internal/repository/dao" "joybuywork/internal/repository/dbcheck" "joybuywork/internal/repository/model" "joybuywork/internal/repository/mtype" "joybuywork/internal/repository/withdefault" "joybuywork/internal/service/cmm" "joybuywork/internal/service/provider" "joybuywork/internal/service/provider/award" "joybuywork/internal/service/provider/fissionaction" "joybuywork/internal/utils" "fmt" "time" "gits.branchcn.com/backend/joybuywork/proto/go_proto/workerr" ) // Qa 现金转盘交互逻辑 type Qa struct { Player player *model.FissionQa conf *model.FissionCampaignQa fissionPlayerSetInitOnceKey string } func NewQa(ctx context.Context, user *model.FissionUser, fissionId int64, opts ...Option) (player *Qa, err error) { player = &Qa{} err = player.Init(ctx, user, fissionId, opts...) if err != nil { return } err = player.initPlayer() if err != nil { return } return } // ReceiveReward 手动领取奖励 func (s *Qa) ReceiveReward(taskRewards ...*model.FissionUserTaskReward) (chance int32, reward *award.RewardRes, err error) { err = s._receiveReward(taskRewards, func(tasks []*model.FissionCampaignTask, records []*award.RewardRes) error { chance, err = s.taskFinish(tasks, false) if err != nil { return err } if len(records) == 0 { return nil } reward = records[0] return nil }) if err != nil { return } return } // FinishTask 完成任务 func (s *Qa) FinishTask(tasks ...*model.FissionCampaignTask) (err error) { _, err = s.taskFinish(tasks, true) if err != nil { return } return } // 完成任务 func (s *Qa) taskFinish(tasks []*model.FissionCampaignTask, checkManual bool) (chance int32, err error) { player := s.GetPlayer() var logs []*model.FissionQaLog for _, task := range tasks { if checkManual && task.AwardMode == mtype.FissionCampaignTaskAwardModeManual { continue } switch task.PropMark { case mtype.FissionCampaignTaskPropMarkChance: chance += task.Chance } val, _ := jsonm.MarshalToString(mtype.AnswerLogVal{ TaskId: task.ID, Chance: task.Chance, }) log := withdefault.FissionQaLog(&model.FissionQaLog{ UserID: player.UserID, CampaignID: player.CampaignID, QaID: player.ID, Type: mtype.FissionQaLogTypeAdd, Val: val, }) switch task.TaskType { case mtype.FissionCampaignTaskTypeRegister: log.Source = mtype.FissionLogSourceRegister case mtype.FissionCampaignTaskTypeInstall: log.Source = mtype.FissionLogSourceInstall case mtype.FissionCampaignTaskTypeCondAll: log.Source = utils.Ternary[string](task.AwardMode == mtype.FissionCampaignTaskAwardModeManual, mtype.FissionLogSourceManualCondAll, mtype.FissionLogSourceCondAll) case mtype.FissionCampaignTaskTypeCondOne: log.Source = utils.Ternary[string](task.AwardMode == mtype.FissionCampaignTaskAwardModeManual, mtype.FissionLogSourceManualCondOne, mtype.FissionLogSourceCondOne) } logs = append(logs, log) } if chance == 0 { return } m := s.GetDao().FissionQa _, err = m.WithContext(s.Ctx).Where(m.ID.Eq(player.ID)).UpdateSimple(m.TaskChance.Add(chance), m.Chance.Add(chance)) if err != nil { return } err = s._addLog(logs...) if err != nil { return } return } type QaAnswer struct { QID int64 `json:"qid"` AID []int64 `json:"aid"` } func (s *Qa) Answer(submits []*QaAnswer) (score, qaRightNum int32, win bool, reward *model.FissionCampaignReward, record *model.FissionRewardRecord, info provider.RewardAutoLang, err error) { player := s.GetPlayer() if player.Chance <= 0 { err = workerr.CChanceNoneError return } //答卷 var qaIds []int64 answer := make(map[int64]map[int64]struct{}) for _, v := range submits { qaIds = append(qaIds, v.QID) answer[v.QID] = map[int64]struct{}{} for _, v1 := range v.AID { answer[v.QID][v1] = struct{}{} } } var qaNum = int32(len(qaIds)) var qaGroupId int64 switch s.cpn.FissionID { case consts.IdPrizeAnswer: var checkGroup bool temp, _ := cache.NewFissionTemp(s.Ctx).FindById(s.cpn.TemplateID) if temp != nil { group, _ := cache.NewFissionTempGroup(s.Ctx).FindById(temp.GroupID) checkGroup = group.Mark == "answer_tmb" } if !checkGroup { score, err = s._prizeAnswerScore(qaIds, answer) if err != nil { return } } else { qaNum, qaGroupId, err = s._getQaGroup() if err != nil { return } score, qaRightNum, err = s._qaScore(qaNum, qaGroupId, qaIds, answer) if err != nil { return } } case consts.IdQa, consts.IdQaVideo, consts.IdQaAudio: qaNum, qaGroupId, err = s._getQaGroup() if err != nil { return } score, qaRightNum, err = s._qaScore(qaNum, qaGroupId, qaIds, answer) if err != nil { return } default: return } answerSb, _ := jsonm.MarshalToString(submits) win, reward, record, info, err = s._getReward(answerSb, score) if err != nil { return } playerM := s.GetDao().FissionQa _, err = playerM.WithContext(s.Ctx).Where(playerM.ID.Eq(player.ID)).UpdateSimple(playerM.Chance.Sub(1)) if err != nil { err = s.BE().MysqlError(err) return } val, _ := jsonm.MarshalToString(mtype.AnswerLogVal{ Chance: 1, RewardId: record.RewardID, RecordId: record.ID, Score: score, }) log := withdefault.FissionQaLog(&model.FissionQaLog{ UserID: player.UserID, CampaignID: player.CampaignID, QaID: player.ID, Type: mtype.FissionQaLogTypeDecr, Source: mtype.FissionLogSourceAnswer, Val: val, }) err = s._addLog(log) if err != nil { return } fissionaction.NewMarkPoint(s.Ctx).InApp(s.user).SubmitQa(time.Now().Unix(), qaGroupId, qaNum, fmt.Sprint(score)) return } // Sign 签到 func (s *Qa) Sign() (err error) { player, conf := s.GetPlayer(), s.GetConf() if conf.DayChance == 0 { return } signOk, lastSign := s._lastSign(player.LastSign) if !signOk { return } playerM := s.GetDao().FissionQa _, err = playerM.WithContext(s.Ctx).Where(playerM.ID.Eq(player.ID)).UpdateSimple( playerM.Chance.Add(conf.DayChance), playerM.DayChance.Add(conf.DayChance), playerM.LastSign.Value(lastSign), ) if err != nil { return s.BE().MysqlError(err) } val, _ := jsonm.MarshalToString(mtype.AnswerLogVal{ Chance: conf.DayChance, }) log := withdefault.FissionQaLog(&model.FissionQaLog{ UserID: player.UserID, CampaignID: player.CampaignID, QaID: player.ID, Type: mtype.FissionQaLogTypeAdd, Source: mtype.FissionLogSourceSign, Val: val, }) err = s._addLog(log) if err != nil { return } s.player.Chance = player.Chance + conf.DayChance return } // SetInit 获得初始次数 func (s *Qa) SetInit() (err error) { player, conf := s.GetPlayer(), s.GetConf() if player.SetInit == mtype.Yes || conf.InitChance == 0 { return } key := s.fissionPlayerSetInitOnceKey if !app.NewRedisLock(s.Ctx, key).SoftLock() { return } m := s.GetDao().FissionQa _, err = m.WithContext(s.Ctx).Where(m.ID.Eq(player.ID)).UpdateSimple(m.SetInit.Value(mtype.Yes), m.Chance.Add(conf.InitChance)) if err != nil { err = s.BE().MysqlError(err) return } val, _ := jsonm.MarshalToString(mtype.AnswerLogVal{ Chance: conf.InitChance, }) log := withdefault.FissionQaLog(&model.FissionQaLog{ UserID: player.UserID, CampaignID: player.CampaignID, QaID: player.ID, Type: mtype.FissionQaLogTypeAdd, Source: mtype.FissionLogSourceInit, Val: val, }) err = s._addLog(log) if err != nil { return } s.player.Chance += conf.InitChance return } // GetConf 获取玩法配置 func (s *Qa) GetConf() *model.FissionCampaignQa { if s.conf == nil { s.conf, _ = cache.NewCampaignQa(s.Ctx).FindByCampaignId(s.user.CampaignID) } return s.conf } // GetPlayer 获取玩家基础数据 func (s *Qa) GetPlayer() *model.FissionQa { return s.player } // 玩家 func (s *Qa) initPlayer() (err error) { player, err := s._dbFirst() if err != nil { return } defer func() { if err != nil { return } s.player = player s.fissionPlayerSetInitOnceKey = ckey.FissionPlayerSetInitOnce(player.UserID) }() if player != nil { return } player, err = s.firstOrCreate() if err != nil { return } return } // 创建玩家基础数据 func (s *Qa) firstOrCreate() (player *model.FissionQa, err error) { lc := app.NewFollower(s.Ctx, ckey.FissionUserCreatePlayerOnce(s.user.ID)).Lock() defer lc.Release() if lc.Error() != nil { err = lc.Error() return } player, err = s._dbFirst() if err != nil { return } if player != nil { return } return s.createPlayer() } // 数据库查询 func (s *Qa) _dbFirst() (*model.FissionQa, error) { playerM := dao.FissionQa player, err := playerM.WithContext(s.Ctx).Where(playerM.UserID.Eq(s.user.ID)).Take() if err = dbcheck.DbError(err); err != nil { return nil, s.BE().MysqlError(err) } return player, nil } // 创建一个玩家 func (s *Qa) createPlayer() (player *model.FissionQa, err error) { playerM := dao.FissionQa player = withdefault.FissionQa(&model.FissionQa{ CampaignID: s.user.CampaignID, UserID: s.user.ID, }) err = playerM.WithContext(s.Ctx).Create(player) if err != nil { err = s.BE().MysqlError(err) return } return } func (s *Qa) _addLog(logs ...*model.FissionQaLog) (err error) { err = s.GetDao().FissionQaLog.WithContext(s.Ctx).Create(logs...) if err != nil { err = s.BE().MysqlError(err) return } return } func (s *Qa) _prizeAnswerScore(qaIds []int64, answer map[int64]map[int64]struct{}) (score int32, err error) { conf := s.GetConf() qm := s.GetDao().FissionCampaignQaQuestion ql, err := qm.WithContext(s.Ctx).Where(qm.ID.In(qaIds...), qm.QaID.Eq(conf.ID), qm.IsDel.Eq(mtype.No)).Find() if err != nil { err = s.BE().MysqlError(err) return } var qids []int64 var scoreMap = make(map[int64]int32) for _, v := range ql { qids = append(qids, v.ID) scoreMap[v.ID] = v.Score } opm := s.GetDao().FissionCampaignQaOption opl, err := opm.WithContext(s.Ctx).Where(opm.QuestionID.In(qids...), opm.IsRight.Eq(mtype.Yes), opm.IsDel.Eq(mtype.No)).Find() if err != nil { err = s.BE().MysqlError(err) return } //问题 question := map[int64][]int64{} for _, v := range opl { question[v.QuestionID] = append(question[v.QuestionID], v.ID) } for qid, aid := range question { sub, ok := answer[qid] if !ok { continue } var allRight = true for _, v := range aid { _, ok1 := sub[v] if !ok1 { allRight = false break } } if allRight { score += scoreMap[qid] } } return } func (s *Qa) _getReward(answer string, score int32) (win bool, reward *model.FissionCampaignReward, record *model.FissionRewardRecord, info provider.RewardAutoLang, err error) { m := s.GetDao().FissionCampaignReward rl, err := cmm.NewCMM(s.Ctx, s.GetDao()).UserAppReward(s.user, m.Score, m.ID) if err != nil { return } var thinkMap = make(map[int64]*model.FissionCampaignReward) var rewardMap = make(map[int32][]*model.FissionCampaignReward) var minScore int32 = -1 for _, v := range rl { if v.IsThank == mtype.Yes { thinkMap[v.ID] = v } else { rewardMap[v.Score] = append(rewardMap[v.Score], v) if minScore == -1 { minScore = v.Score } } } var stepScore int32 var scoreRewards []*model.FissionCampaignReward for sc, rewards := range rewardMap { if sc > score || sc <= stepScore { continue } stepScore = sc scoreRewards = rewards } if len(scoreRewards) == 1 { reward = scoreRewards[0] var deOk bool deOk, err = award.NewAwardIssVerify(s.Ctx, s.user, reward).DeductStock().RuleFilter().IsOK() if deOk { win = score >= minScore record, info, err = s._answerReward(reward, answer, score) return } } else { var scoreRewardsMap = make(map[int64]*model.FissionCampaignReward) for _, v := range scoreRewards { scoreRewardsMap[v.ID] = v } key := ckey.FissionUserRewards(s.user.ID) var ids []int64 idStr := app.Redis.Cache.Get(s.Ctx, key).Val() if idStr != "" { ids = utils.SplitToInt64(idStr, consts.JoinComma) } if len(ids) >= len(scoreRewards) { ids = []int64{} } var next = make(map[int64]struct{}) for _, v := range ids { next[v] = struct{}{} } for _, v := range scoreRewardsMap { reward = v if _, ok := next[reward.ID]; ok { continue } var deOk bool deOk, err = award.NewAwardIssVerify(s.Ctx, s.user, reward).DeductStock().RuleFilter().IsOK() if deOk { win = score >= minScore record, info, err = s._answerReward(reward, answer, score) break } reward = nil } if reward != nil { ids = append(ids, reward.ID) idStr = utils.Join(ids, consts.JoinComma) app.Redis.Cache.SetEX(s.Ctx, key, idStr, consts.TimeDay*2) return } } for _, v := range thinkMap { reward = v win = score >= minScore record, info, err = s._answerReward(v, answer, score) return } return } // 答题奖品 func (s *Qa) _answerReward(reward *model.FissionCampaignReward, answer string, score int32) (record *model.FissionRewardRecord, info provider.RewardAutoLang, err error) { record, info, _, err = award.NewAwardReward(s.Ctx, s.GetDao(), s.cpn.ProjectID, s.user).One( reward, true, award.RewardRecordAnswer(answer, score), ) return } func (s *Qa) _qaScore(qal int32, qaGroupId int64, qaIds []int64, answer map[int64]map[int64]struct{}) (score, qaRightNum int32, err error) { qam := s.GetDao().LibraryQa ql, err := qam.WithContext(s.Ctx).Where(qam.GroupID.Eq(qaGroupId), qam.ID.In(qaIds...), qam.IsDel.Eq(mtype.No)).Find() if err != nil { err = s.BE().MysqlError(err) return } l := int32(len(ql)) if l == 0 { return } if l < qal { qal = l } qaScore := 100 / qal var qids []int64 var scoreMap = make(map[int64]int32) for _, v := range ql { qids = append(qids, v.ID) scoreMap[v.ID] = qaScore } opm := s.GetDao().LibraryQaOption opl, err := opm.WithContext(s.Ctx).Where(opm.QaID.In(qids...), opm.IsRight.Eq(mtype.Yes), opm.IsDel.Eq(mtype.No)).Find() if err != nil { err = s.BE().MysqlError(err) return } //问题 question := map[int64][]int64{} for _, v := range opl { question[v.QaID] = append(question[v.QaID], v.ID) } for qid, aid := range question { sub, ok := answer[qid] if !ok { continue } var allRight = true for _, v := range aid { _, ok1 := sub[v] if !ok1 { allRight = false break } } if allRight { score += scoreMap[qid] qaRightNum++ } } if qaRightNum >= qal { score = 100 } return } func (s *Qa) _getQaGroup() (qaNum int32, qaGroupId int64, err error) { cpn, qa := s.cpn, s.GetConf() qaNum, qaGroupId = qa.QaNum, qa.QaGroupID spLang := cpn.GetLangMarks() if len(spLang) == 1 { return } langMark := utils.NotExistGetFirst(s.user.LangMark, spLang) if langMark == spLang[0] { return } lang, err := cache.NewCampaignLang(s.Ctx).FindByCampaignId(cpn.ID, langMark) if err != nil { err = s.BE().CacheError(err) return } if lang == nil { return } qaGroup := lang.GetQaGroup() if qaGroup == nil { return } group, err := cache.NewLibraryQaGroup(s.Ctx).FindByHashId(qaGroup.QaGroupId) if err != nil { err = s.BE().CacheError(err) return } if group == nil { return } return qaGroup.QaNum, group.ID, nil }