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.

659 lines
16 KiB
Go

package cmm
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/finder"
"joybuywork/internal/repository/model"
"joybuywork/internal/repository/mtype"
"joybuywork/internal/repository/withdefault"
pr "joybuywork/internal/service/provider"
"joybuywork/internal/utils"
"joybuywork/internal/utils/blink"
"joybuywork/internal/utils/lockdo"
"joybuywork/internal/utils/merr"
"joybuywork/internal/utils/verify"
"joybuywork/proto/go_proto/workerr"
"strings"
"time"
deventpb "gits.branchcn.com/backend/deeplink/proto/go_proto/devent"
bi_analyzepb "gits.branchcn.com/backend/fissionbi/proto/go_proto/bi_analyze"
"gits.branchcn.com/backend/microcode/plugins/derivative/bucket"
linkpb "gits.branchcn.com/backend/shortlink/proto/go_proto/link"
"gorm.io/gen"
"gorm.io/gen/field"
)
type CMM struct {
ctx context.Context
be *merr.BusinessError
dao *dao.Query
}
func NewCMM(ctx context.Context, dao *dao.Query) *CMM {
return &CMM{
ctx: ctx,
be: merr.NewBE(ctx),
dao: dao,
}
}
// GetDomain 获取项目域名
func (s *CMM) GetDomain() (domain string) {
domain = app.NewLastingBucket(s.ctx, ckey.ProjectDomainBucket(), func(ctx context.Context) string {
return utils.GetRandEnStr(6)
}, bucket.UseCheckValue(func(ctx context.Context, val string) bool {
m := dao.Project
exist, err := m.WithContext(ctx).Where(m.Domain.Eq(val)).Take()
if err = dbcheck.DbError(err); err != nil || exist != nil {
return false
}
return true
})).BucketOnly().GetValue()
return
}
// GetFissionConfig 获取玩法配置
func (s *CMM) GetFissionConfig(projectId int64) (conf *model.FissionConfig, err error) {
m := s.dao.FissionConfig
conf, err = m.WithContext(s.ctx).Where(m.ProjectID.Eq(projectId)).Take()
if err = dbcheck.DbError(err); err != nil {
err = s.be.MysqlError(err)
return
}
if conf != nil {
return
}
lc := app.NewRedisLock(s.ctx, ckey.FissionConfigOnce(projectId)).Lock()
defer lc.Release()
if lc.Error() != nil {
err = workerr.BusyError
return
}
conf, err = m.WithContext(s.ctx).Where(m.ProjectID.Eq(projectId)).Take()
if err = dbcheck.DbError(err); err != nil {
err = s.be.MysqlError(err)
return
}
if conf != nil {
return
}
conf = withdefault.FissionConfig(&model.FissionConfig{
ProjectID: projectId,
DynamicCampaign: 0,
})
if err = m.WithContext(s.ctx).Create(conf); err != nil {
err = s.be.MysqlError(err)
return
}
return
}
// CreateContentLink 创建带内容的链接
func (s *CMM) CreateContentLink(user *model.FissionUser, markId int64, mark string) (ct *model.FissionUserCtlink, err error) {
lc := app.NewRedisLock(s.ctx, ckey.FissionUserContentLinkStoreOnce(user.ID, markId, mark))
defer lc.Release()
if !lc.SoftLock() {
err = workerr.OnceLockError
return
}
m := s.dao.FissionUserCtlink
ct, err = m.WithContext(s.ctx).Where(m.UserID.Eq(user.ID), m.MarkID.Eq(markId), m.Mark.Eq(mark)).Take()
if err = dbcheck.DbError(err); err != nil {
err = s.be.MysqlError(err)
return
}
if ct != nil {
return
}
cpn, err := finder.Campaign(s.ctx, user.CampaignID)
if err != nil {
err = s.be.CacheError(err)
return
}
project, err := finder.Project(s.ctx, cpn.ProjectID)
if err != nil {
err = s.be.CacheError(err)
return
}
channelM := s.dao.FissionCampaignChannel
channel, err := channelM.WithContext(s.ctx).Where(channelM.ID.Eq(user.ChannelID)).Take()
if err = dbcheck.DbError(err); err != nil {
err = dbcheck.DbError(err)
return
}
ct = withdefault.FissionUserCtlink(&model.FissionUserCtlink{
CampaignID: user.CampaignID,
UserID: user.ID,
Mark: mark,
MarkID: markId,
})
link := blink.NewCampaignLink(project.Domain, cpn.HashID, channel.Mark, blink.SetSubUserId(user.SubUserID), blink.SetCtLinkId(ct.HashID)).CampaignContentLink()
data, _ := app.NewShortLinkNameLink().CreateShortLink(s.ctx, &linkpb.CreateShortLinkReq{
LongLink: link,
Domain: project.Domain,
})
if data.GetShortLink() == "" {
return
}
ct.LinkURL = data.GetShortLink()
err = m.WithContext(s.ctx).Create(ct)
if err != nil {
err = s.be.MysqlError(err)
return
}
return
}
// UserCampaignType 获取活动对应的用户所属类型数据
func (s *CMM) UserCampaignType(cpn *model.FissionCampaign, conf *model.FissionCampaignConfig, langMark, lvMark, countryMark string) (uct consts.CampaignUserType, err error) {
var userLang, userLevelMark, userCountry string
var userLevel int64
defer func() {
if userLevel > 0 && userLevelMark == "" {
level, _ := cache.NewFissionUserLevel(s.ctx).FindById(userLevel)
userLevelMark = level.Mark
}
uct.LangMark, uct.LevelId, uct.LevelMark, uct.CountryMark = userLang, userLevel, userLevelMark, userCountry
}()
ful := finder.NewFuserLayer(conf)
switch conf.RewardGroup {
case mtype.FissionRewardGroupUserLevel:
var dfId int64
lvMarks := strings.Split(lvMark, ",")
for _, mark := range lvMarks {
var lvId int64
lvId, err = cache.NewFissionUserLevel(s.ctx).FindByMarkId(cpn.ProjectID, mark)
if err != nil {
err = s.be.CacheError(err)
return
}
if lvId == 0 {
continue
}
if dfId == 0 {
dfId = lvId
}
_, df := ful.Level().Selected(lvId)
if !df {
userLevel = lvId
userLevelMark = mark
break
}
}
if userLevel == 0 {
userLevel, _ = ful.Level().Selected(0)
}
case mtype.FissionRewardGroupLang:
userLang, _ = ful.Lang().Selected(langMark)
case mtype.FissionRewardGroupLangAndUserLevel:
userLang, _ = ful.LangLevel().GetLang(langMark)
var dfId int64
lvMarks := strings.Split(lvMark, ",")
for _, mark := range lvMarks {
var lvId int64
lvId, err = cache.NewFissionUserLevel(s.ctx).FindByMarkId(cpn.ProjectID, mark)
if err != nil {
err = s.be.CacheError(err)
return
}
if lvId == 0 {
continue
}
if dfId == 0 {
dfId = lvId
}
_, df := ful.LangLevel().GetLevel(userLang, lvId)
if !df {
userLevel = lvId
userLevelMark = mark
}
}
if userLevel == 0 {
userLevel, _ = ful.LangLevel().GetLevel(userLang, 0)
}
case mtype.FissionRewardGroupCountry:
userCountry, _ = ful.Country().Selected(countryMark)
}
return
}
// UserBindChannel 获取用户要绑定的站内渠道
func (s *CMM) UserBindChannel(cpnId int64, cmark string) (channel *model.FissionCampaignChannel, err error) {
clM := s.dao.FissionCampaignChannel
if cmark != "" {
channel, err = cache.NewCampaignChannel(s.ctx).FindByMark(cpnId, cmark)
if err != nil {
err = s.be.CacheError(err)
return
}
if channel != nil {
return
}
}
data, err := lockdo.Bytes(s.ctx, ckey.FissionDefaultAppChannel(cpnId), func() (num []byte, err error) {
//找不到 渠道就用默认渠道
channel, err = clM.WithContext(s.ctx).Where(
clM.CampaignID.Eq(cpnId),
clM.IsDefault.Eq(mtype.Yes),
clM.IsDel.Eq(mtype.No),
clM.CType.Eq(mtype.FissionCampaignChannelTypeApp),
).Take()
if err = dbcheck.DbError(err); err != nil {
return
}
return jsonm.MarshalCkZero(channel)
})
if err != nil {
err = s.be.CacheError(err)
return
}
channel = &model.FissionCampaignChannel{}
_ = jsonm.Unmarshal(data, channel)
return
}
// UserAppReward 获取用户在app内的活动奖品
func (s *CMM) UserAppReward(user *model.FissionUser, orders ...field.Expr) (list []*model.FissionCampaignReward, err error) {
conf, err := cache.NewCampaignConfig(s.ctx).FindByCampaignId(user.CampaignID)
if err != nil {
err = s.be.CacheError(err)
return
}
userLang, userLevel, userCountry := user.UserLang, user.UserLevel, user.UserCountry
m := s.dao.FissionCampaignReward
var conds = []gen.Condition{
m.CampaignID.Eq(user.CampaignID),
m.RewardType.Eq(mtype.FissionCampaignRewardTypeApp),
m.IsDel.Eq(mtype.No),
}
switch conf.RewardGroup {
case mtype.FissionRewardGroupLang:
conds = append(conds, m.LangMark.Eq(userLang))
case mtype.FissionRewardGroupUserLevel:
conds = append(conds, m.LevelID.Eq(userLevel))
case mtype.FissionRewardGroupLangAndUserLevel:
conds = append(conds, m.LangMark.Eq(userLang), m.LevelID.Eq(userLevel))
case mtype.FissionRewardGroupCountry:
conds = append(conds, m.CountryMark.Eq(userCountry))
default:
}
query := m.WithContext(s.ctx).Where(conds...)
if len(orders) > 0 {
query = query.Order(orders...)
} else {
query = query.Order(m.Sort, m.ID)
}
list, err = query.Find()
if err != nil {
err = s.be.MysqlError(err)
return
}
return
}
type UserAppRewardInfo struct {
RewardId int64
Title string
ImgUrl string
}
// UserAppRewardInfo 获取用户在app内的活动奖品信息
func (s *CMM) UserAppRewardInfo(cpn *model.FissionCampaign, user *model.FissionUser) (list map[int64]*UserAppRewardInfo, err error) {
rewards, err := s.UserAppReward(user)
if err != nil {
return
}
var brIds []int64
var dm = make(map[int64][]int64)
for _, reward := range rewards {
if _, ok := dm[reward.RewardID]; !ok {
brIds = append(brIds, reward.RewardID)
}
dm[reward.RewardID] = append(dm[reward.RewardID], reward.ID)
}
preward, err := pr.NewRewards(s.ctx, cpn.ProjectID, brIds...)
if err != nil {
return
}
list = make(map[int64]*UserAppRewardInfo)
for brid, rewardIds := range dm {
info := preward.AutoLang(brid, user.LangMark)
for _, v := range rewardIds {
list[v] = &UserAppRewardInfo{
RewardId: v,
Title: info.Title,
ImgUrl: info.ImgUrl,
}
}
}
return
}
// CampaignCancelTask 活动下线要处理一些任务
func (s *CMM) CampaignCancelTask(cpn *model.FissionCampaign, archiveTask bool) (err error) {
cpnId, projectId := cpn.ID, cpn.ProjectID
// 需要更新一下统计任务的归档时间
if archiveTask {
archiveTaskM := s.dao.FissionArchiveTask
_, err = archiveTaskM.WithContext(s.ctx).
Where(archiveTaskM.CampaignID.Eq(cpnId)).
UpdateSimple(archiveTaskM.EndTime.Value(utils.CampaignArchiveEnd(time.Now().Unix()).Unix()))
if err != nil {
return s.be.MysqlError(err)
}
if cpn.ReportType == mtype.FissionCampaignReportTypeNew {
// 同步到统计服务去
err = s.AnalyzeCampaign(projectId, cpnId, cpn.StartTime, time.Now().Unix(), 0)
if err != nil {
return
}
}
}
condM := s.dao.FissionCampaignCond
_, err = condM.WithContext(s.ctx).Where(
condM.CampaignID.Eq(cpnId),
condM.IsDel.Eq(mtype.No),
).UpdateSimple(condM.DelUse.Value(mtype.Yes), condM.IsDel.Value(mtype.Yes))
if err != nil {
return s.be.MysqlError(err)
}
// 把关联的奖品数据取消关联
relatedM := s.dao.RewardRelated
_, err = relatedM.WithContext(s.ctx).Where(
relatedM.RelateID.Eq(cpnId),
relatedM.RelateTable.Eq(dao.FissionCampaign.TableName()),
).UpdateSimple(relatedM.CancelTime.Value(time.Now().Unix()))
if err != nil {
return s.be.MysqlError(err)
}
// 把关联的用户登记数据取消关联
lvRelatedM := s.dao.FissionUserLevelRelated
_, err = lvRelatedM.WithContext(s.ctx).Where(
lvRelatedM.RelateID.Eq(cpnId),
lvRelatedM.RelateTable.Eq(dao.FissionCampaign.TableName()),
).UpdateSimple(lvRelatedM.CancelTime.Value(time.Now().Unix()))
if err != nil {
return s.be.MysqlError(err)
}
rewardAddM := s.dao.FissionCampaignRewardAdd
_, err = rewardAddM.WithContext(s.ctx).Where(
rewardAddM.CampaignID.Eq(cpnId),
rewardAddM.IsDel.Eq(mtype.No),
).UpdateSimple(rewardAddM.FinishAt.Value(time.Now().Unix()))
if err != nil {
return s.be.MysqlError(err)
}
err = s.SaveEventCampaignTrigger(projectId)
if err != nil {
return
}
return
}
// SaveEventCampaignTrigger 同步事件触发
func (s *CMM) SaveEventCampaignTrigger(projectId int64) (err error) {
m := s.dao.FissionCampaignCond
list, err := m.WithContext(s.ctx).Where(m.ProjectID.Eq(projectId),
m.EventType.In(mtype.FissionCampaignCondEventTypeCustom),
m.IsDel.Eq(mtype.No),
).Find()
if err != nil {
err = s.be.MysqlError(err)
return
}
var eventMap = make(map[string]bool)
for _, v := range list {
isGlobal, ok := eventMap[v.Event]
if !ok || !isGlobal {
eventMap[v.Event] = verify.InArray[int8](v.TriggetType, mtype.FissionCampaignCondTriggerTypeGlobal, mtype.FissionCampaignCondTriggerTypePage)
}
}
var eventInfo = make([]*deventpb.SaveEventCampaignTriggerReq_EventInfo, 0, len(eventMap))
for event, isGlobal := range eventMap {
eventInfo = append(eventInfo, &deventpb.SaveEventCampaignTriggerReq_EventInfo{
Event: event,
HaveGlobal: utils.Ternary[int32](isGlobal, 1, 0),
})
}
_, err = app.NewDeepLinkEvent().SaveEventCampaignTrigger(s.ctx, &deventpb.SaveEventCampaignTriggerReq{
ProjectId: projectId,
EventInfo: eventInfo,
})
return
}
// EventCanTotal 事件是否可以被计算
func (s *CMM) EventCanTotal(cond *model.FissionCampaignCond, subUserId string, adsCondId, joinAt, actionTime int64) (can bool) {
if cond.TriggetType == mtype.FissionCampaignCondTriggerTypeGlobal {
return true
}
if cond.TriggetType == mtype.FissionCampaignCondTriggerTypePage {
return joinAt > 0 && joinAt <= actionTime
}
if cond.EventType == mtype.FissionCampaignCondEventTypeAds {
return cond.ID == adsCondId
}
_, err := app.NewDeepLinkEvent().FindCampaignTaskClick(s.ctx, &deventpb.FindCampaignTaskClickReq{
ProjectId: cond.ProjectID,
UserId: subUserId,
EventName: cond.Event,
CondId: cond.ID,
})
return err == nil
}
// AnalyzeCampaign 分析活动报表
func (s *CMM) AnalyzeCampaign(projectId, cpnId, startTime, endTime, idxId int64) (err error) {
_, err = app.NewFissionBiAnalyze().Campaign(s.ctx, &bi_analyzepb.CampaignReq{
ProjectId: projectId,
CampaignId: cpnId,
StartTime: startTime,
EndTime: endTime,
IdxId: idxId,
})
return
}
// GetUserMap 获取用户地图
func (s *CMM) GetUserMap(user *model.FissionUser) (run *model.FissionDiceRun, err error) {
m := s.dao.FissionDiceMapGroup
dmg, err := m.WithContext(s.ctx).Where(
m.CampaignID.Eq(user.CampaignID),
m.LangMark.Eq(user.UserLang),
m.LevelID.Eq(user.UserLevel),
m.CountryMark.Eq(user.UserCountry),
).Take()
if err = dbcheck.DbError(err); err != nil {
err = s.be.MysqlError(err)
return
}
if dmg == nil {
err = workerr.NotFound("not found dice map")
return
}
lc := app.NewFollower(s.ctx, ckey.FissionUserRunCreateOnce(user.ID)).Lock()
defer lc.Release()
if lc.Error() != nil {
err = workerr.BusyError
return
}
runM := s.dao.FissionDiceRun
run, err = runM.WithContext(s.ctx).Where(runM.UserID.Eq(user.ID), runM.DmgID.Eq(dmg.ID)).Take()
if err = dbcheck.DbError(err); err != nil {
err = s.be.MysqlError(err)
return
}
if run != nil {
return
}
run = withdefault.FissionDiceRun(&model.FissionDiceRun{
CampaignID: user.CampaignID,
UserID: user.ID,
DmgID: dmg.ID,
})
if err = runM.WithContext(s.ctx).Create(run); err != nil {
err = s.be.MysqlError(err)
return
}
return
}
// LifeStatus 生命体状态修改
func (s *CMM) LifeStatus(life *model.FissionGrowLife, die bool, nextDie int64) (err error) {
var up []field.AssignExpr
var isDie int8 = mtype.No
var mark = mtype.FissionGrowRecordGetRevive
if die {
isDie = mtype.Yes
mark = mtype.FissionGrowRecordGetDie
}
lifeM := s.dao.FissionGrowLife
up = append(up, lifeM.IsDie.Value(isDie))
if nextDie > 0 {
up = append(up, lifeM.DieTime.Value(nextDie))
}
_, err = lifeM.WithContext(s.ctx).Where(lifeM.ID.Eq(life.ID)).UpdateSimple(up...)
if err != nil {
err = s.be.MysqlError(err)
return
}
recordM := s.dao.FissionGrowRecord
err = recordM.WithContext(s.ctx).Create(withdefault.FissionGrowRecord(&model.FissionGrowRecord{
UserID: life.UserID,
CampaignID: life.CampaignID,
Mark: mark,
VarData: "",
}))
if err != nil {
err = s.be.MysqlError(err)
return
}
return
}
func (s *CMM) GetCoinInfo(projectId int64, langMark string) (title, imgUrl, rewardTitle string, err error) {
coin, err := cache.NewTCoin(s.ctx).FindByProjectId(projectId)
if err != nil {
err = s.be.CacheError(err)
return
}
if coin == nil {
return
}
m := s.dao.TCoinLang
lang, err := m.WithContext(s.ctx).Where(m.TCoinID.Eq(coin.ID), m.LangMark.Eq(langMark)).Take()
if err = dbcheck.DbError(err); err != nil {
err = s.be.MysqlError(err)
return
}
if lang == nil {
lang, err = m.WithContext(s.ctx).Where(m.TCoinID.Eq(coin.ID), m.IsDefault.Eq(mtype.Yes)).Take()
if err = dbcheck.DbError(err); err != nil {
err = s.be.MysqlError(err)
return
}
if lang == nil {
return
}
}
return lang.Title, lang.ImgURL, coin.Title, nil
}