package award import ( "context" "joybuywork/internal/repository/dao" "joybuywork/internal/repository/model" "joybuywork/internal/repository/mtype" "joybuywork/internal/repository/withdefault" "joybuywork/internal/service/cmm" "joybuywork/internal/service/provider" "joybuywork/internal/service/provider/fissionaction" "joybuywork/internal/service/provider/projectuser" "joybuywork/internal/service/queue/producer" "joybuywork/internal/service/queue/types" "joybuywork/internal/utils/logic" "joybuywork/internal/utils/merr" "joybuywork/internal/utils/verify" "fmt" ) type RecordOption func(record *model.FissionRewardRecord) // RewardRecordStatus 设置状态 func RewardRecordStatus(status int8, failType, failMsg string) RecordOption { return func(record *model.FissionRewardRecord) { record.Status = status record.FailType = failType record.FailMsg = failMsg } } // RewardRecordAnswer 设置题答题目 func RewardRecordAnswer(answer string, score int32) RecordOption { return func(record *model.FissionRewardRecord) { record.Answer = answer record.Score = score } } // RewardRecordChildId 设置子活动 func RewardRecordChildId(childId int64) RecordOption { return func(record *model.FissionRewardRecord) { record.ChildID = childId } } type Reward struct { logic.Context user *model.FissionUser projectId int64 coinInfo struct { Title string ImgURL string RewardTitle string } async []func() } // NewAwardReward 奖品发放验证 func NewAwardReward(ctx context.Context, dao *dao.Query, projectId int64, user *model.FissionUser) *Reward { award := &Reward{ user: user, projectId: projectId, } award.Init(ctx, dao, merr.NewBE(ctx)) return award } // RewardIdsBatch 通过奖品id扣除奖品 func (s *Reward) RewardIdsBatch(rewardIds []int64, sync bool) (res []*RewardRes, async []func(), err error) { m := s.GetDao().FissionCampaignReward rewards, err := m.WithContext(s.Ctx).Where(m.ID.In(rewardIds...)).Find() if err != nil { err = s.BE().MysqlError(err) return } if len(rewards) == 0 { return } var rewardMap = make(map[int64]*model.FissionCampaignReward) for _, v := range rewards { rewardMap[v.ID] = v } var sendRewards = make([]*model.FissionCampaignReward, 0, len(rewardIds)) for _, v := range rewardIds { reward, has := rewardMap[v] if !has { continue } sendRewards = append(sendRewards, reward) } if len(sendRewards) == 0 { return } return s.DeductStockBatch(sendRewards, sync) } // DeductStockOne 扣除单个缓存 func (s *Reward) DeductStockOne(reward *model.FissionCampaignReward, sync bool) (record *model.FissionRewardRecord, info provider.RewardAutoLang, async []func(), err error) { res, async, err := s.DeductStockBatch([]*model.FissionCampaignReward{reward}, sync) if err != nil { return } return res[0].Record, res[0].Info, async, nil } // DeductStockBatch 扣除缓存的批量处理 func (s *Reward) DeductStockBatch(rewards []*model.FissionCampaignReward, sync bool) (res []*RewardRes, async []func(), err error) { var addRewards = make([]*RewardOne, 0, len(rewards)) for _, reward := range rewards { ok, err1 := NewAwardIssVerify(s.Ctx, s.user, reward).DeductStock().IsOK() if err1 != nil { err = err1 return } var opts []RecordOption if !ok { opts = append(opts, RewardRecordStatus( mtype.FissionRewardRecordStatusFail, mtype.FissionRewardRecordFailTypeCli, mtype.FissionRewardRecordFailTypeCliLackMsg, )) } addRewards = append(addRewards, &RewardOne{ Reward: reward, Opts: opts, }) } return s.Batch(addRewards, sync) } // One 创建用户奖品 func (s *Reward) One(reward *model.FissionCampaignReward, sync bool, opts ...RecordOption) (record *model.FissionRewardRecord, info provider.RewardAutoLang, async []func(), err error) { var one = &RewardOne{ Reward: reward, Opts: opts, } res, async, err := s.Batch([]*RewardOne{one}, sync) if err != nil { return } return res[0].Record, res[0].Info, async, nil } type RewardOne struct { Reward *model.FissionCampaignReward Opts []RecordOption } type RewardRes struct { Record *model.FissionRewardRecord Info provider.RewardAutoLang Reward *model.FissionCampaignReward } // Batch 批量创建用户奖品 func (s *Reward) Batch(rewards []*RewardOne, sync bool) (res []*RewardRes, async []func(), err error) { if len(rewards) == 0 { return } user := s.user var brIds []int64 for _, reward := range rewards { if reward.Reward.RewardID == 0 { continue } brIds = append(brIds, reward.Reward.RewardID) } var preward *provider.Rewards if len(brIds) > 0 { preward, err = provider.NewRewards(s.Ctx, s.projectId, brIds...) if err != nil { return } } res = make([]*RewardRes, 0, len(rewards)) var records = make([]*model.FissionRewardRecord, 0, len(rewards)) for _, one := range rewards { reward := one.Reward record := withdefault.FissionRewardRecord(&model.FissionRewardRecord{ CampaignID: user.CampaignID, UserID: user.ID, RewardID: reward.ID, RewardType: reward.RewardType, RewardNum: reward.RewardNum, Step: reward.Step, IsThank: reward.IsThank, BaseRewardID: reward.RewardID, PropMark: reward.PropMark, }) if verify.InArray[string](reward.PropMark, mtype.FissionCampaignRewardPropMarkChance, mtype.FissionCampaignRewardPropMarkPoint) || reward.RewardID < 0 { record.Status = mtype.FissionRewardRecordStatusSuccess } if reward.RewardType == mtype.FissionCampaignRewardTypeChild { record.ChildID = reward.CampaignID } for _, opt := range one.Opts { opt(record) } var info provider.RewardAutoLang if reward.PropMark == mtype.FissionCampaignRewardPropMarkPoint { info.Title, info.ImgUrl, info.RewardTitle, _ = s.getCoinInfo(user.LangMark) } else if reward.RewardID != 0 && preward != nil { info, _ = preward.InfoAutoLang(reward.RewardID, user.LangMark) } record.Title, record.ImgURL = info.Title, info.ImgUrl records = append(records, record) res = append(res, &RewardRes{ Record: nil, Info: info, Reward: reward, }) } m := s.GetDao().FissionRewardRecord err = m.WithContext(s.Ctx).Create(records...) if err != nil { err = s.BE().MysqlError(err) return } sm := s.GetDao().FissionRewardRecord.SubTable(user.CampaignID) err = sm.WithContext(s.Ctx).Create(records...) if err != nil { err = s.BE().MysqlError(err) return } var earnCoin []*projectuser.EarnCoinParam for k, record := range records { res[k].Record = record switch record.PropMark { case mtype.FissionCampaignRewardPropMarkPoint: var operate = mtype.ProjectUserCoinAddReward switch res[k].Reward.RewardType { case mtype.FissionCampaignRewardTypeTask: operate = mtype.ProjectUserCoinAddTask } earnCoin = append(earnCoin, &projectuser.EarnCoinParam{ CoinNum: record.RewardNum, IsExpired: res[k].Reward.IsExpired(), ExpiredAt: res[k].Reward.ExpiredAt(), Source: mtype.ProjectUserCoinSourceCampaign, SourceID: record.CampaignID, SourceData: map[string]string{ "reward_id": fmt.Sprintf("%d", record.RewardID), "record_id": fmt.Sprintf("%d", record.ID), }, Operate: operate, }) default: s.notify(record, res[k].Info) } s.markPoint(record) } if len(earnCoin) > 0 { var pu *projectuser.ProjectUser pu, err = projectuser.NewProjectUser(s.Ctx, projectuser.WithInit(s.projectId, s.user.SubUserID)) if err != nil { return } err = pu.EarnCoin(earnCoin...) if err != nil { return } } if sync { for _, do := range s.async { do() } return } async = s.async return } // 回调 func (s *Reward) notify(record *model.FissionRewardRecord, info provider.RewardAutoLang) { if record.Status != mtype.FissionRewardRecordStatusWait || record.PropMark == mtype.FissionCampaignRewardPropMarkChance { return } if info.ReceiveType == mtype.RewardReceiveTypeClient { _ = s.GetDao().FissionClientReward.WithContext(s.Ctx).Create(withdefault.FissionClientReward(&model.FissionClientReward{ UserID: s.user.SubUserID, ProjectID: s.projectId, CampaignID: s.user.CampaignID, RecordID: record.ID, CreatedAt: record.CreatedAt, })) return } s.async = append(s.async, func() { producer.NewCampaignNotice(s.Ctx).CampaignRewardNotify(types.CampaignRewardNotify{ CampaignId: s.user.CampaignID, UserId: s.user.ID, UserRewardId: record.ID, }) producer.CheckRewardPush(s.Ctx, 60, types.DelayCommonCheckRewardPush{ CampaignId: s.user.CampaignID, UserId: s.user.ID, UserRewardId: record.ID, BaseRewardId: record.BaseRewardID, }) }) } // 埋点 func (s *Reward) markPoint(record *model.FissionRewardRecord) { s.async = append(s.async, func() { fissionaction.NewMarkPoint(s.Ctx).InApp(s.user).Reward(record.CreatedAt, record.RewardID, record.ID, record.ChildID, record.RewardType, record.RewardNum) }) } func (s *Reward) getCoinInfo(langMark string) (title, imgUrl, rewardTitle string, err error) { if s.coinInfo.Title != "" { return s.coinInfo.Title, s.coinInfo.ImgURL, s.coinInfo.RewardTitle, nil } s.coinInfo.Title, s.coinInfo.ImgURL, s.coinInfo.RewardTitle, err = cmm.NewCMM(s.Ctx, s.GetDao()).GetCoinInfo(s.projectId, langMark) if err != nil { return } return s.coinInfo.Title, s.coinInfo.ImgURL, s.coinInfo.RewardTitle, nil }