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

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
}