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.
468 lines
11 KiB
Go
468 lines
11 KiB
Go
package statcampaign
|
|
|
|
import (
|
|
"joybuywork/internal/app"
|
|
"joybuywork/internal/consts"
|
|
"joybuywork/internal/consts/ckey"
|
|
"joybuywork/internal/repository/cache"
|
|
"joybuywork/internal/repository/dbcheck"
|
|
"joybuywork/internal/repository/finder"
|
|
"joybuywork/internal/repository/model"
|
|
"joybuywork/internal/repository/mtype"
|
|
"joybuywork/internal/service/cmm"
|
|
"joybuywork/internal/service/provider"
|
|
"joybuywork/internal/service/queue/producer"
|
|
"joybuywork/internal/service/queue/types"
|
|
"joybuywork/internal/utils"
|
|
"joybuywork/internal/utils/logic"
|
|
"joybuywork/internal/utils/verify"
|
|
"joybuywork/proto/go_proto/workerr"
|
|
"fmt"
|
|
"slices"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type Logic struct {
|
|
logic.Logic
|
|
|
|
Req *types.StatCampaignValue
|
|
|
|
userMark string //用户标识
|
|
expTime time.Duration //基于活动结束时间的缓存过期时间
|
|
|
|
lang string
|
|
|
|
channelId, webChannelId int64
|
|
}
|
|
|
|
func (s *Logic) Exec() (err error) {
|
|
cpn, err := s.campaign()
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
err = s.statCampaign(cpn)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
err = s.channelStat(cpn)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
err = s.campaignNotice()
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
err = s.other(cpn)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (s *Logic) campaign() (cpn *model.FissionCampaign, err error) {
|
|
cpn, err = finder.Campaign(s.Ctx, s.Req.CampaignID, s.Req.Hid)
|
|
if err != nil {
|
|
err = s.BE().CacheError(err)
|
|
return
|
|
}
|
|
|
|
if cpn == nil || cpn.IsDel == mtype.Yes {
|
|
err = workerr.NotFound("cpn not found")
|
|
return
|
|
}
|
|
|
|
// 让后面都可以用活动主键
|
|
s.Req.CampaignID = cpn.ID
|
|
s.lang = s.Req.Language
|
|
|
|
// 一些项目用户id特别处理
|
|
if s.Req.BehaviorType == mtype.FissionBehaviorTypeCustom && verify.InArray(cpn.ProjectID, 374, 143) {
|
|
lg, _, _ := strings.Cut(s.Req.UserID, "_")
|
|
if !verify.InArray(lg, cpn.GetLangMarks()...) {
|
|
err = workerr.NotFound("not at rule")
|
|
return
|
|
}
|
|
|
|
if s.lang == "" {
|
|
s.lang = lg
|
|
}
|
|
}
|
|
|
|
//构建标识
|
|
if s.Req.FromSrv {
|
|
s.userMark = fmt.Sprintf("srv:%s", s.Req.UserID)
|
|
} else {
|
|
s.userMark = utils.Md5String(fmt.Sprintf("%v_%v_%v_%v", s.Req.IpAddress, s.Req.Os, s.Req.OsVersion, s.Req.DeviceModel))
|
|
}
|
|
|
|
// 广告活动
|
|
if cpn.Target == mtype.FissionCampaignConfigTargetAdsGame {
|
|
s.expTime = 7 * 24 * time.Hour
|
|
return
|
|
}
|
|
|
|
tz := provider.NewCampaignTZ(s.Ctx, cpn, s.lang)
|
|
if tz.IsEnd() {
|
|
err = workerr.VerifyError("cpn stat end")
|
|
return
|
|
}
|
|
|
|
if tz.IsWait() {
|
|
err = workerr.VerifyError("cpn not start")
|
|
return
|
|
}
|
|
|
|
s.expTime = time.Until(tz.EndTime())
|
|
|
|
// 不是予发布状态下或者没有用户id的都不用验证白名单
|
|
if !tz.IsPre() || s.Req.UserID == "" {
|
|
return
|
|
}
|
|
|
|
// 验证白名单
|
|
conf, err := cache.NewCampaignConfig(s.Ctx).FindByCampaignId(cpn.ID)
|
|
if err != nil {
|
|
err = s.BE().CacheError(err)
|
|
return
|
|
}
|
|
|
|
if slices.Contains(conf.GetWhileList(), s.Req.UserID) {
|
|
return
|
|
}
|
|
|
|
return nil, workerr.CNotRunError
|
|
}
|
|
|
|
func (s *Logic) statCampaign(cpn *model.FissionCampaign) (err error) {
|
|
//行为队列
|
|
|
|
if s.Req.BehaviorType == mtype.FissionBehaviorTypeCustom || cpn.Target != consts.FissionTargetAds {
|
|
return
|
|
}
|
|
|
|
b := types.CampaignBehavior{
|
|
CampaignId: cpn.ID,
|
|
BehaviorType: s.Req.BehaviorType,
|
|
BehaviorTime: s.Req.BehaviorTime,
|
|
UserMark: s.userMark,
|
|
OtherData: nil,
|
|
MarkId: "",
|
|
}
|
|
|
|
producer.NewCampaignNotice(s.Ctx).CampaignBehavior(b)
|
|
|
|
return
|
|
}
|
|
|
|
func (s *Logic) channelStat(cpn *model.FissionCampaign) (err error) {
|
|
if !verify.InArray(s.Req.BehaviorType, mtype.FissionBehaviorTypeAppOpen, mtype.FissionBehaviorTypeAppInstall) || cpn.Target != mtype.FissionCampaignConfigTargetAdsGame {
|
|
return
|
|
}
|
|
|
|
var chId int64
|
|
defer func() {
|
|
if chId == 0 {
|
|
return
|
|
}
|
|
|
|
var bt int32
|
|
switch s.Req.BehaviorType {
|
|
case mtype.FissionBehaviorTypeAppInstall:
|
|
bt = mtype.FissionBehaviorTypeChannelInstall
|
|
case mtype.FissionBehaviorTypeAppOpen:
|
|
bt = mtype.FissionBehaviorTypeChannelOpen
|
|
}
|
|
|
|
producer.NewCampaignNotice(s.Ctx).CampaignBehavior(types.CampaignBehavior{
|
|
CampaignId: cpn.ID,
|
|
BehaviorType: bt,
|
|
BehaviorTime: s.Req.BehaviorTime,
|
|
UserMark: s.userMark,
|
|
MarkId: fmt.Sprint(chId),
|
|
})
|
|
}()
|
|
|
|
// 带渠道标识
|
|
if s.Req.Channel != "" {
|
|
m := s.GetDao().FissionCampaignChannel
|
|
|
|
var ch *model.FissionCampaignChannel
|
|
ch, err = m.WithContext(s.Ctx).Where(m.CampaignID.Eq(cpn.ID), m.Mark.Eq(s.Req.Channel)).Take()
|
|
if err = dbcheck.DbError(err); err != nil {
|
|
return s.BE().MysqlError(err)
|
|
}
|
|
|
|
if ch != nil {
|
|
chId = ch.ID
|
|
|
|
if ch.CType == mtype.FissionCampaignChannelTypeApp {
|
|
s.channelId = ch.ID
|
|
} else {
|
|
s.webChannelId = ch.ID
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// 有上级用户,通过上级用户的绑定信息来统计
|
|
var parent *model.FissionUser
|
|
if s.Req.OwnerID != "" {
|
|
parent, err = cache.NewFissionUser(s.Ctx, cpn.ID).FindBySubUserId(s.Req.OwnerID)
|
|
if err != nil {
|
|
return s.BE().CacheError(err)
|
|
}
|
|
}
|
|
|
|
// 有web_id就是站外渠道来源
|
|
if s.Req.WebId != "" {
|
|
defer func() {
|
|
s.webChannelId = chId
|
|
}()
|
|
|
|
// 拿上级用户的站外信息来统计站外渠道,因为产品定义,带来的下级全都归总到上级的渠道上
|
|
wbuM := s.GetDao().FissionWebUser
|
|
var wbu *model.FissionWebUser
|
|
if parent != nil {
|
|
wbu, err = wbuM.WithContext(s.Ctx).Where(wbuM.CampaignID.Eq(cpn.ID), wbuM.UserID.Eq(parent.ID)).Take()
|
|
if err = dbcheck.DbError(err); err != nil {
|
|
return s.BE().MysqlError(err)
|
|
}
|
|
|
|
if wbu != nil {
|
|
chId = wbu.ChannelID
|
|
}
|
|
}
|
|
|
|
// 如果在上级的关系链赋予了数据,就直接返回统计
|
|
if chId > 0 {
|
|
return
|
|
}
|
|
|
|
// 取当前web_id最后访问的渠道id
|
|
chId, err = app.Redis.Cache.Get(s.Ctx, ckey.FissionWebLastChannel(cpn.ID, s.Req.WebId)).Int64()
|
|
if err = dbcheck.RdError(err); err != nil {
|
|
return s.BE().CacheError(err)
|
|
}
|
|
|
|
if chId > 0 {
|
|
return
|
|
}
|
|
|
|
// webid对应的的站外记录
|
|
wbu, err = wbuM.WithContext(s.Ctx).Where(wbuM.CampaignID.Eq(cpn.ID), wbuM.WebID.Eq(s.Req.WebId)).Take()
|
|
if err = dbcheck.DbError(err); err != nil {
|
|
return s.BE().MysqlError(err)
|
|
}
|
|
|
|
if wbu != nil {
|
|
chId = wbu.ChannelID
|
|
_ = app.Redis.Cache.SetNX(s.Ctx, ckey.FissionWebLastChannel(cpn.ID, wbu.WebID), fmt.Sprint(chId), 2*time.Hour)
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// 站内的数据
|
|
if parent != nil && parent.ChannelID > 0 {
|
|
chId = parent.ChannelID
|
|
s.channelId = parent.ChannelID
|
|
return
|
|
}
|
|
|
|
if s.Req.UserID != "" {
|
|
var user *model.FissionUser
|
|
|
|
user, err = cache.NewFissionUser(s.Ctx, cpn.ID).FindBySubUserId(s.Req.UserID)
|
|
if err != nil {
|
|
return s.BE().CacheError(err)
|
|
}
|
|
if user != nil && user.ChannelID > 0 {
|
|
chId = user.ChannelID
|
|
}
|
|
}
|
|
|
|
// 归到默认渠道
|
|
m := s.GetDao().FissionCampaignChannel
|
|
ch, err := m.WithContext(s.Ctx).Where(
|
|
m.CampaignID.Eq(cpn.ID),
|
|
m.CType.Eq(mtype.FissionCampaignChannelTypeApp),
|
|
m.IsDefault.Eq(mtype.Yes),
|
|
m.IsDel.Eq(mtype.No),
|
|
).Take()
|
|
if err = dbcheck.DbError(err); err != nil {
|
|
return s.BE().MysqlError(err)
|
|
}
|
|
|
|
if ch == nil {
|
|
return
|
|
}
|
|
|
|
chId = ch.ID
|
|
|
|
return
|
|
}
|
|
|
|
// 活动通知
|
|
func (s *Logic) campaignNotice() (err error) {
|
|
// 不是需要通知的事件不通知
|
|
if !verify.InArray(s.Req.BehaviorType,
|
|
mtype.FissionBehaviorTypeAppInstall,
|
|
mtype.FissionBehaviorTypeAppOpen,
|
|
mtype.FissionBehaviorTypeAppRegister,
|
|
mtype.FissionBehaviorTypeCustom,
|
|
mtype.FissionBehaviorTypeWebFirstOpen,
|
|
mtype.FissionBehaviorTypePurchase,
|
|
) {
|
|
return
|
|
}
|
|
|
|
var notice = types.CampaignNotice{
|
|
CampaignId: s.Req.CampaignID,
|
|
DeviceId: s.Req.DeviceID,
|
|
UserId: s.Req.UserID,
|
|
ParentId: s.Req.OwnerID,
|
|
IsInstall: false,
|
|
BehaviorTime: s.Req.BehaviorTime,
|
|
NoticeType: 0,
|
|
NoticeTag: "",
|
|
Event: s.Req.Event,
|
|
EventData: s.Req.EventData,
|
|
ProjectId: s.Req.ProjectID,
|
|
Ip: s.Req.IpAddress,
|
|
Os: s.Req.Os,
|
|
OsVersion: s.Req.OsVersion,
|
|
DeviceModel: s.Req.DeviceModel,
|
|
Lang: s.lang,
|
|
ChannelId: s.channelId,
|
|
WebChannelId: s.webChannelId,
|
|
UserMark: s.userMark,
|
|
PurchasesData: s.Req.PurchasesData,
|
|
UserLv: s.Req.Tags,
|
|
}
|
|
|
|
switch s.Req.BehaviorType {
|
|
case mtype.FissionBehaviorTypeAppInstall:
|
|
notice.NoticeType = types.NoticeTypeInstall
|
|
notice.IsInstall = true
|
|
case mtype.FissionBehaviorTypeAppOpen:
|
|
notice.NoticeType = types.NoticeTypeOpen
|
|
case mtype.FissionBehaviorTypeAppRegister:
|
|
notice.NoticeType = types.NoticeTypeRegister
|
|
case mtype.FissionBehaviorTypeCustom:
|
|
notice.NoticeType = types.NoticeTypeCustom
|
|
case mtype.FissionBehaviorTypeWebFirstOpen:
|
|
notice.NoticeType = types.NoticeTypeInstall
|
|
notice.IsInstall = true
|
|
notice.NoticeTag = types.NoticeTagWeb
|
|
case mtype.FissionBehaviorTypePurchase:
|
|
notice.NoticeType = types.NoticeTypePay
|
|
}
|
|
|
|
// 安装,打开,注册,是新用户才通知
|
|
if verify.InArray(s.Req.BehaviorType, mtype.FissionBehaviorTypeAppInstall, mtype.FissionBehaviorTypeAppOpen, mtype.FissionBehaviorTypeAppRegister, mtype.FissionBehaviorTypeWebFirstOpen) {
|
|
producer.NewCampaignNotice(s.Ctx).CampaignNotice(notice)
|
|
return
|
|
}
|
|
|
|
// 自定义事件
|
|
if s.Req.UserID == "" && s.Req.DeviceID == "" {
|
|
return
|
|
}
|
|
|
|
var keys []string
|
|
for _, v := range s.Req.EventData {
|
|
keys = append(keys, v.EventKey)
|
|
}
|
|
|
|
m := s.GetDao().FissionCampaignCond
|
|
list, err := m.WithContext(s.Ctx).Where(
|
|
m.CampaignID.Eq(s.Req.CampaignID),
|
|
m.Event.Eq(s.Req.Event),
|
|
m.EventKey.In(keys...),
|
|
m.IsDel.Eq(mtype.No)).Find()
|
|
if err != nil {
|
|
return s.BE().MysqlError(err)
|
|
}
|
|
|
|
var hasCond bool
|
|
for _, v := range list {
|
|
|
|
switch v.TriggetType {
|
|
case mtype.FissionCampaignCondTriggerTypeClick:
|
|
if cmm.NewCMM(s.Ctx, s.GetDao()).EventCanTotal(v, s.Req.UserID, 0, 0, s.Req.BehaviorTime) {
|
|
hasCond = true
|
|
}
|
|
case mtype.FissionCampaignCondTriggerTypePage:
|
|
user, _ := cache.NewFissionUser(s.Ctx, s.Req.CampaignID).FindBySubUserId(s.Req.UserID)
|
|
|
|
if user != nil && user.JoinAt > 0 && user.JoinAt <= s.Req.BehaviorTime {
|
|
hasCond = true
|
|
}
|
|
|
|
case mtype.FissionCampaignCondTriggerTypeGlobal:
|
|
hasCond = true
|
|
}
|
|
}
|
|
|
|
if !hasCond {
|
|
return
|
|
}
|
|
|
|
if verify.InArray[int32](s.Req.BehaviorType, mtype.FissionBehaviorTypeCustom, mtype.FissionBehaviorTypePurchase) {
|
|
producer.NewCampaignNotice(s.Ctx).CampaignNotice(notice)
|
|
return
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (s *Logic) other(cpn *model.FissionCampaign) (err error) {
|
|
switch cpn.FissionID {
|
|
case consts.IdChop:
|
|
return s._chopHelp(cpn)
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (s *Logic) _chopHelp(cpn *model.FissionCampaign) (err error) {
|
|
if s.Req.BehaviorType != mtype.FissionBehaviorTypeViewDownload || s.Req.OwnerID == "" {
|
|
return
|
|
}
|
|
|
|
user, err := cache.NewFissionUser(s.Ctx, cpn.ID).FindBySubUserId(s.Req.OwnerID)
|
|
if err != nil {
|
|
err = s.BE().CacheError(err)
|
|
return
|
|
}
|
|
|
|
if user == nil {
|
|
return
|
|
}
|
|
|
|
producer.NewCampaignNotice(s.Ctx).ValidEvents(types.ValidEvents{
|
|
BehaviorTime: s.Req.BehaviorTime,
|
|
NoticeType: types.NoticeTypeClick,
|
|
CampaignId: cpn.ID,
|
|
ParentId: user.ID,
|
|
ClickInfo: &types.ValidEventsClickInfo{
|
|
UserMark: s.userMark,
|
|
IpAddress: s.Req.IpAddress,
|
|
Os: s.Req.Os,
|
|
OsVersion: s.Req.OsVersion,
|
|
DeviceModel: s.Req.DeviceModel,
|
|
DeviceID: s.Req.DeviceID,
|
|
WebId: s.Req.WebId,
|
|
},
|
|
})
|
|
return
|
|
}
|