You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
376 lines
8.7 KiB
Go
376 lines
8.7 KiB
Go
package pusher
|
|
|
|
import (
|
|
"context"
|
|
"joybuywork/internal/app"
|
|
"joybuywork/internal/config"
|
|
"joybuywork/internal/consts"
|
|
"joybuywork/internal/consts/ckey"
|
|
"joybuywork/internal/repository/cache"
|
|
"joybuywork/internal/repository/dao"
|
|
"joybuywork/internal/repository/model"
|
|
"joybuywork/internal/repository/mtype"
|
|
"joybuywork/internal/service/provider"
|
|
"joybuywork/internal/service/queue/producer"
|
|
"joybuywork/internal/service/queue/types"
|
|
"joybuywork/internal/utils"
|
|
"joybuywork/internal/utils/blink"
|
|
"joybuywork/internal/utils/logic"
|
|
"joybuywork/internal/utils/merr"
|
|
"fmt"
|
|
"time"
|
|
|
|
"gits.branchcn.com/backend/microcode/plugins/derivative/lock"
|
|
)
|
|
|
|
type UserLine struct {
|
|
Id int64
|
|
DoneAt int64
|
|
}
|
|
|
|
type Pusher struct {
|
|
logic.Context
|
|
|
|
cpn *model.FissionCampaign
|
|
project *model.Project
|
|
|
|
channelLink map[int64]string // 渠道链接
|
|
|
|
pg map[string]*pushGroup // 防止重复推送分组
|
|
|
|
langTitle map[string]string // 语言页面标题
|
|
|
|
targetNum int64 // 需要邀请的目标数
|
|
|
|
icon string // 玩法icon
|
|
}
|
|
|
|
func NewPusher(ctx context.Context, cpn *model.FissionCampaign) (*Pusher, error) {
|
|
pusher := &Pusher{
|
|
cpn: cpn,
|
|
pg: make(map[string]*pushGroup),
|
|
langTitle: make(map[string]string),
|
|
}
|
|
|
|
pusher.Init(ctx, dao.Q, merr.NewBE(ctx))
|
|
|
|
err := pusher.set()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return pusher, nil
|
|
}
|
|
|
|
// 设置必定需要的参数
|
|
func (s *Pusher) set() (err error) {
|
|
channelM := dao.FissionCampaignChannel
|
|
channelList, err := channelM.WithContext(s.Ctx).Where(
|
|
channelM.CampaignID.Eq(s.cpn.ID),
|
|
channelM.CType.Eq(mtype.FissionCampaignChannelTypeApp),
|
|
).Find()
|
|
if err != nil {
|
|
err = s.BE().MysqlError(err)
|
|
return
|
|
}
|
|
|
|
s.project, err = cache.NewProject(s.Ctx).FindById(s.cpn.ProjectID)
|
|
if err != nil {
|
|
err = s.BE().CacheError(err)
|
|
return
|
|
}
|
|
|
|
s.channelLink = make(map[int64]string, len(channelList))
|
|
for _, v := range channelList {
|
|
s.channelLink[v.ID] = blink.NewCampaignLink(s.project.Domain, s.cpn.HashID, v.Mark).CampaignShareLink()
|
|
if v.IsDefault == mtype.Yes && v.IsDel == mtype.No {
|
|
s.channelLink[0] = s.channelLink[v.ID]
|
|
}
|
|
}
|
|
|
|
switch s.cpn.FissionID {
|
|
case consts.IdCash:
|
|
taskM := dao.FissionCampaignTask
|
|
task, err1 := taskM.WithContext(s.Ctx).Where(
|
|
taskM.CampaignID.Eq(s.cpn.ID),
|
|
taskM.TaskType.In(mtype.FissionCampaignTaskTypeInstall, mtype.FissionCampaignTaskTypeRegister),
|
|
taskM.IsDel.Eq(mtype.No),
|
|
).Take()
|
|
if err1 != nil {
|
|
err = s.BE().MysqlError(err1)
|
|
return
|
|
}
|
|
|
|
if task == nil {
|
|
return
|
|
}
|
|
|
|
s.targetNum = int64(1 / task.Percent)
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (s *Pusher) CompleteTask(user *model.FissionUser) (err error) {
|
|
if s.beLimit(s.cpn.ProjectID, mtype.FissionCampaignPushMarkCompleteTask, user.SubUserID) {
|
|
return
|
|
}
|
|
|
|
inviteNum, err1 := s._inviteNum([]int64{user.ID})
|
|
if err1 != nil {
|
|
return err1
|
|
}
|
|
|
|
userMap, err := s._userChannel(s.cpn.ID, []int64{user.ID})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
s._push(
|
|
mtype.FissionCampaignPushMarkCompleteTask,
|
|
"",
|
|
"",
|
|
app.TalkMap.CompeteTask(user.LangMark),
|
|
userMap[user.ID],
|
|
0,
|
|
inviteNum[user.ID],
|
|
)
|
|
|
|
return
|
|
}
|
|
|
|
// Active 日活推送
|
|
func (s *Pusher) Active(t time.Time, markCode, codeVal string, userLines []*UserLine) (err error) {
|
|
return s.gpPush(mtype.FissionCampaignPushMarkActive, markCode, codeVal, userLines, s._getPg(mtype.FissionCampaignPushMarkActive, t))
|
|
}
|
|
|
|
// TaskRest 任务重制推送
|
|
func (s *Pusher) TaskRest(t time.Time, markCode, codeVal string, userLines []*UserLine) (err error) {
|
|
return s.gpPush(mtype.FissionCampaignPushMarkTaskReset, markCode, codeVal, userLines, s._getPg(mtype.FissionCampaignPushMarkTaskReset, t))
|
|
}
|
|
|
|
// Offline 离线推送
|
|
func (s *Pusher) Offline(t time.Time, markCode, codeVal string, userLines []*UserLine) (err error) {
|
|
return s.gpPush(mtype.FissionCampaignPushMarkOffline, markCode, codeVal, userLines, s._getPg(mtype.FissionCampaignPushMarkOffline, t))
|
|
}
|
|
|
|
// Visit 访问推送
|
|
func (s *Pusher) Visit(user *model.FissionUser, t time.Time) (err error) {
|
|
userMap, err := s._userChannel(s.cpn.ID, []int64{user.ID})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
s._push(
|
|
mtype.FissionCampaignPushMarkVisit,
|
|
"",
|
|
"",
|
|
"",
|
|
userMap[user.ID],
|
|
t.Unix(),
|
|
0,
|
|
)
|
|
|
|
return
|
|
}
|
|
|
|
// 记录组推送,一些需要防止二次推送的处理的
|
|
func (s *Pusher) gpPush(mark, markCode, codeVal string, userLines []*UserLine, pg *pushGroup) (err error) {
|
|
var userIds []int64
|
|
for _, v := range userLines {
|
|
id := fmt.Sprintf("%d", v.Id)
|
|
if pg.insideOne(v.DoneAt) && pg.one.Exist(id) {
|
|
continue
|
|
}
|
|
|
|
if pg.insideTwo(v.DoneAt) && pg.two.Exist(id) {
|
|
continue
|
|
}
|
|
|
|
if pg.insideThree(v.DoneAt) && pg.three.Exist(id) {
|
|
continue
|
|
}
|
|
|
|
userIds = append(userIds, v.Id)
|
|
}
|
|
|
|
inviteNum, err1 := s._inviteNum(userIds)
|
|
if err1 != nil {
|
|
return err1
|
|
}
|
|
|
|
cpn := s.cpn
|
|
|
|
userMap, err1 := s._userChannel(cpn.ID, userIds)
|
|
if err1 != nil {
|
|
return err1
|
|
}
|
|
|
|
var oneOk, twoOk, threeOk []string
|
|
for _, v := range userLines {
|
|
user, ok := userMap[v.Id]
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
langMark := utils.NotExistGetFirst[string](user.LangMark, s.cpn.GetLangMarks())
|
|
|
|
var desc string
|
|
switch mark {
|
|
case mtype.FissionCampaignPushMarkOffline:
|
|
switch s.cpn.FissionID {
|
|
case consts.IdKeep:
|
|
desc = app.TalkMap.DogOffline(langMark)
|
|
}
|
|
case mtype.FissionCampaignPushMarkTaskReset:
|
|
desc = app.TalkMap.TaskReset(langMark)
|
|
case mtype.FissionCampaignPushMarkActive:
|
|
desc = app.TalkMap.Active(langMark)
|
|
}
|
|
|
|
id := fmt.Sprintf("%d", v.Id)
|
|
if pg.insideThree(v.DoneAt) {
|
|
threeOk = append(threeOk, id, "1")
|
|
} else if pg.insideTwo(v.DoneAt) {
|
|
twoOk = append(twoOk, id, "1")
|
|
} else if pg.insideOne(v.DoneAt) {
|
|
oneOk = append(oneOk, id, "1")
|
|
}
|
|
|
|
s._push(mark, markCode, codeVal, desc, user, v.DoneAt, inviteNum[v.Id])
|
|
}
|
|
|
|
if len(threeOk) > 0 {
|
|
_ = pg.three.Set(threeOk...)
|
|
}
|
|
|
|
if len(twoOk) > 0 {
|
|
_ = pg.two.Set(twoOk...)
|
|
}
|
|
|
|
if len(oneOk) > 0 {
|
|
_ = pg.one.Set(oneOk...)
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// 推送到开发平台
|
|
func (s *Pusher) _push(mark, markCode, codeVal, desc string, user userChannel, doneAt, inviteNum int64) {
|
|
producer.NewOplatformOther(s.Ctx).OpoCampaignPush(types.OpoCampaignPush{
|
|
ProjectId: s.cpn.ProjectID,
|
|
FissionId: s.cpn.FissionID,
|
|
CampaignHashId: s.cpn.HashID,
|
|
CampaignTitle: s.cpn.Title,
|
|
CampaignUrl: user.ChannelUrl,
|
|
PageTitle: s._getLangTitle(user.LangMark),
|
|
FissionIcon: fmt.Sprintf("%s/%s", config.Config.Upload.Host, consts.GetPushLogo(s.cpn.FissionID, int32(s.cpn.Target))),
|
|
UserId: user.UserId,
|
|
Mark: mark,
|
|
MarkCode: markCode,
|
|
CodeVal: codeVal,
|
|
MarkDesc: desc,
|
|
PushTime: time.Now().Unix(),
|
|
TaskExpireTime: doneAt,
|
|
InviteNum: inviteNum,
|
|
TargetNum: s.targetNum,
|
|
}, producer.SetHeaderLang(user.LangMark))
|
|
}
|
|
|
|
// 获取页面标题
|
|
func (s *Pusher) _getLangTitle(userLang string) (langTitle string) {
|
|
langTitle, ok := s.langTitle[userLang]
|
|
if ok {
|
|
return langTitle
|
|
}
|
|
|
|
lt, err := provider.NewLangTitle(s.Ctx, s.cpn, userLang)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
langTitle, _ = lt.GetPageTitle()
|
|
|
|
s.langTitle[userLang] = langTitle
|
|
return
|
|
}
|
|
|
|
// 获取push记录组
|
|
func (s *Pusher) _getPg(mark string, t time.Time) *pushGroup {
|
|
pg, ok := s.pg[mark]
|
|
if !ok {
|
|
pg = newPushGroup(s.Ctx, t, s.cpn.ID, mark)
|
|
s.pg[mark] = pg
|
|
return pg
|
|
}
|
|
|
|
return pg
|
|
}
|
|
|
|
// 邀请人数
|
|
func (s *Pusher) _inviteNum(ids []int64) (data map[int64]int64, err error) {
|
|
data = make(map[int64]int64, len(ids))
|
|
|
|
m := dao.FissionUserStat
|
|
list, err := m.WithContext(s.Ctx).Where(m.UserID.In(ids...)).Find()
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
for _, v := range list {
|
|
data[v.UserID] = v.EffectiveNum
|
|
}
|
|
return
|
|
}
|
|
|
|
type userChannel struct {
|
|
UserId string `json:"user_id"`
|
|
ChannelUrl string `json:"channel"`
|
|
LangMark string `json:"lang_mark"`
|
|
}
|
|
|
|
// 用户渠道
|
|
func (s *Pusher) _userChannel(cpnId int64, ids []int64) (data map[int64]userChannel, err error) {
|
|
m := dao.FissionUser.SubTable(cpnId)
|
|
list, err := m.WithContext(s.Ctx).Where(m.ID.In(ids...)).Find()
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
var channelIds []int64
|
|
var channelMap = make(map[int64]string)
|
|
for _, v := range list {
|
|
if _, ok := channelMap[v.ChannelID]; !ok {
|
|
channelMap[v.ChannelID] = ""
|
|
channelIds = append(channelIds, v.ChannelID)
|
|
}
|
|
}
|
|
|
|
data = make(map[int64]userChannel, len(ids))
|
|
for _, user := range list {
|
|
if user.IsView == mtype.Yes {
|
|
continue
|
|
}
|
|
|
|
data[user.ID] = userChannel{
|
|
UserId: user.SubUserID,
|
|
ChannelUrl: s.channelLink[user.ChannelID],
|
|
LangMark: user.LangMark,
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (s *Pusher) beLimit(projectId int64, mark, subUserId string) bool {
|
|
return !app.NewRedisLock(s.Ctx, ckey.FissionPushLimit(projectId, mark, subUserId), lock.SetExpire(2*time.Hour)).SoftLock()
|
|
}
|
|
|
|
func (s *Pusher) GetChannelUrl(channelId int64) string {
|
|
return s.channelLink[channelId]
|
|
}
|
|
|
|
func (s *Pusher) GetProject() *model.Project {
|
|
return s.project
|
|
}
|