|
|
package notify
|
|
|
|
|
|
import (
|
|
|
"context"
|
|
|
"joybuywork/internal/app"
|
|
|
"joybuywork/internal/consts"
|
|
|
"joybuywork/internal/consts/ckey"
|
|
|
"joybuywork/internal/plugins/hashid"
|
|
|
"joybuywork/internal/plugins/jsonm"
|
|
|
"joybuywork/internal/repository/cache"
|
|
|
"joybuywork/internal/repository/dao"
|
|
|
"joybuywork/internal/repository/dbcheck"
|
|
|
"joybuywork/internal/repository/model"
|
|
|
"joybuywork/internal/repository/mtype"
|
|
|
"joybuywork/internal/repository/withdefault"
|
|
|
"joybuywork/internal/service/queue/producer"
|
|
|
"joybuywork/internal/service/queue/types"
|
|
|
"joybuywork/internal/utils"
|
|
|
"fmt"
|
|
|
"strings"
|
|
|
"time"
|
|
|
|
|
|
"gits.branchcn.com/backend/microcode/plugins/derivative/lock"
|
|
|
"gorm.io/gen/field"
|
|
|
)
|
|
|
|
|
|
var delaySecMap = map[int32]int{
|
|
|
0: 15, 1: 15,
|
|
|
2: 30,
|
|
|
3: 180,
|
|
|
4: 600,
|
|
|
5: 1200,
|
|
|
6: 1800, 7: 1800, 8: 1800,
|
|
|
9: 3600,
|
|
|
10: 3600 * 3, 11: 3600 * 3, 12: 3600 * 3,
|
|
|
13: 3600 * 6, 14: 3600 * 6,
|
|
|
}
|
|
|
|
|
|
type Reward struct {
|
|
|
//非传输数据
|
|
|
CampaignId int64 `json:"campaign_id"` //活动id
|
|
|
RewardId int64 `json:"reward_id"` //领取的用户奖品记录id(fission_reward_record表主键)
|
|
|
UserId int64 `json:"user_id"` //用户id
|
|
|
ParentId int64 `json:"parent_id"` //上级用户id
|
|
|
|
|
|
//传输数据
|
|
|
CampaignHashId string `json:"campaign_hash_id"` //活动hash_id(fission_campaign表)
|
|
|
RewardHashId string `json:"reward_hash_id"` //奖品hash_id(fission_reward_record表)
|
|
|
KvData []*mtype.KV `json:"kv_data"` //奖品设置的回调的键值数据
|
|
|
RewardType int8 `json:"reward_type"` //奖品类型
|
|
|
RewardTitle string `json:"reward_title"` //奖品名称
|
|
|
RewardNum int32 `json:"reward_num"` //奖品发放数量
|
|
|
SubUserId string `json:"sub_user_id"` //活动提供的用户id,该值为第三方提供的唯一标识
|
|
|
ParentSubUserId string `json:"parent_sub_user_id"` //活用户的上级用户id
|
|
|
Lang string `json:"lang"` //用户绑定的语种标识
|
|
|
Title string `json:"title"` //活动标题
|
|
|
MallRecordId int64 `json:"mall_record_id"` //关联的商城记录id(redeem_mall_record表主键)
|
|
|
}
|
|
|
|
|
|
func (s Reward) Send(ctx context.Context, notifyUrl, token string, pushLadder int8, headers []*mtype.Header) (err error) {
|
|
|
// 防止一些并发操作,导致同时推送到客户端
|
|
|
lc := app.NewRedisLock(ctx, ckey.FissionAcceptRewardOnce(s.CampaignId, s.RewardHashId), lock.SetExpire(timeOut))
|
|
|
defer lc.Release()
|
|
|
if !lc.SoftLock() {
|
|
|
return
|
|
|
}
|
|
|
|
|
|
//回调记录
|
|
|
taskId := hashid.Id()
|
|
|
m := dao.FissionNotify
|
|
|
fn, err := m.WithContext(ctx).Where(m.CampaignID.Eq(s.CampaignId), m.RecordID.Eq(s.RewardId)).Take()
|
|
|
if err = dbcheck.DbError(err); err != nil {
|
|
|
return
|
|
|
}
|
|
|
|
|
|
if fn != nil {
|
|
|
taskId = fn.HashID
|
|
|
}
|
|
|
|
|
|
// 回调成功不处理
|
|
|
if fn != nil && (fn.IsOk == mtype.Yes || s.hasTask(ctx, fn.ID)) {
|
|
|
return
|
|
|
}
|
|
|
|
|
|
body, _ := jsonm.Marshal(s.body(ctx, taskId))
|
|
|
app.Logger.Info(ctx, fmt.Sprintf("奖品推送: %s", body.ToString()))
|
|
|
|
|
|
reqStart := time.Now()
|
|
|
if fn == nil {
|
|
|
fn = withdefault.FissionNotify(&model.FissionNotify{
|
|
|
HashID: taskId,
|
|
|
CampaignID: s.CampaignId,
|
|
|
RecordID: s.RewardId,
|
|
|
NotifyURL: notifyUrl,
|
|
|
NotifyType: mtype.FissionNotifyTypeServer,
|
|
|
Token: token,
|
|
|
Body: body.ToString(),
|
|
|
PushLadder: pushLadder,
|
|
|
})
|
|
|
|
|
|
fn.Headers, _ = jsonm.ToSliceString(headers)
|
|
|
|
|
|
_ = m.WithContext(ctx).Create(fn)
|
|
|
}
|
|
|
|
|
|
var end bool
|
|
|
delaySec, hasDelay := delaySecMap[fn.PushNum]
|
|
|
if !hasDelay {
|
|
|
end = true
|
|
|
}
|
|
|
|
|
|
var respBody string
|
|
|
var code int
|
|
|
// 是否转发推送
|
|
|
if pushLadder == mtype.No {
|
|
|
respBody, code, err = normalReq(ctx, body, notifyUrl, token, s.Lang, headers)
|
|
|
} else {
|
|
|
respBody, code, err = proxyReq(ctx, body, notifyUrl, token, s.Lang, headers)
|
|
|
}
|
|
|
|
|
|
defer func() { // 只有在重推任务结束的时候,才返回错误信息出去
|
|
|
if err != nil && end {
|
|
|
return
|
|
|
}
|
|
|
|
|
|
err = nil
|
|
|
}()
|
|
|
|
|
|
//处理回调任务
|
|
|
var ok bool
|
|
|
if err == nil && code == 200 && strings.HasPrefix(respBody, "success") {
|
|
|
ok = true
|
|
|
}
|
|
|
|
|
|
var ntfUp = []field.AssignExpr{
|
|
|
m.PushNum.Add(1),
|
|
|
m.IsOk.Value(utils.Ternary[int8](ok, mtype.Yes, mtype.No)),
|
|
|
m.Body.Value(body.ToString()),
|
|
|
}
|
|
|
|
|
|
if fn.NotifyURL != notifyUrl {
|
|
|
ntfUp = append(ntfUp, m.NotifyURL.Value(notifyUrl))
|
|
|
}
|
|
|
|
|
|
// 更新状态
|
|
|
_, _ = m.WithContext(ctx).Where(m.ID.Eq(fn.ID)).UpdateSimple(ntfUp...)
|
|
|
|
|
|
var cliResp = respBody
|
|
|
if err != nil && cliResp == "" {
|
|
|
cliResp = fmt.Sprintf("系统错误:%v", err)
|
|
|
} else if cliResp == "" {
|
|
|
cliResp = "如果处理成功请返回字符串success,当前接收到的信息为空字符串,无法判断当前回调是否正确"
|
|
|
}
|
|
|
|
|
|
//回调结果
|
|
|
fnrM := dao.FissionNotifyResp
|
|
|
_ = fnrM.WithContext(ctx).Create(withdefault.FissionNotifyResp(&model.FissionNotifyResp{
|
|
|
CreatedAt: reqStart.Unix(),
|
|
|
NotifyID: fn.ID,
|
|
|
Resp: cliResp,
|
|
|
}))
|
|
|
|
|
|
//如果没有发送成功并且不是最后一次发送,就创建一个任务
|
|
|
if !ok && !end {
|
|
|
s.createTask(ctx, fn.ID, delaySec)
|
|
|
return
|
|
|
}
|
|
|
|
|
|
recordM := dao.FissionRewardRecord
|
|
|
recordSM := dao.FissionRewardRecord.SubTable(s.CampaignId)
|
|
|
mallRecordM := dao.RedeemMallRecord
|
|
|
if ok {
|
|
|
_, nd, _ := strings.Cut(respBody, ":")
|
|
|
_, _ = recordM.WithContext(ctx).Where(recordM.ID.Eq(s.RewardId)).UpdateSimple(
|
|
|
recordM.Status.Value(mtype.FissionRewardRecordStatusSuccess),
|
|
|
recordM.FailType.Value(""),
|
|
|
recordM.FailMsg.Value(""),
|
|
|
recordM.CustomRewardVal.Value(nd),
|
|
|
)
|
|
|
|
|
|
_, _ = recordSM.WithContext(ctx).Where(recordSM.ID.Eq(s.RewardId)).UpdateSimple(
|
|
|
recordSM.Status.Value(mtype.FissionRewardRecordStatusSuccess),
|
|
|
recordSM.FailType.Value(""),
|
|
|
recordSM.FailMsg.Value(""),
|
|
|
recordSM.CustomRewardVal.Value(nd),
|
|
|
)
|
|
|
|
|
|
if s.MallRecordId > 0 {
|
|
|
_, _ = mallRecordM.WithContext(ctx).Where(mallRecordM.ID.Eq(s.MallRecordId)).UpdateSimple(
|
|
|
mallRecordM.Status.Value(mtype.RedeemMallRecordStatusOk),
|
|
|
mallRecordM.FailType.Value(""),
|
|
|
mallRecordM.FailMsg.Value(""),
|
|
|
mallRecordM.CustomRewardVal.Value(nd),
|
|
|
)
|
|
|
}
|
|
|
|
|
|
return
|
|
|
}
|
|
|
|
|
|
var up = []field.AssignExpr{
|
|
|
recordM.Status.Value(mtype.FissionRewardRecordStatusFail),
|
|
|
}
|
|
|
|
|
|
var ups = []field.AssignExpr{
|
|
|
recordSM.Status.Value(mtype.FissionRewardRecordStatusFail),
|
|
|
}
|
|
|
|
|
|
var mup = []field.AssignExpr{
|
|
|
mallRecordM.Status.Value(mtype.RedeemMallRecordStatusFail),
|
|
|
}
|
|
|
|
|
|
if err != nil {
|
|
|
val := fmt.Sprintf("%v", err)
|
|
|
|
|
|
if strings.Contains(val, "missing port in address") {
|
|
|
up = append(up,
|
|
|
recordM.FailType.Value(mtype.FissionRewardRecordFailTypeCli),
|
|
|
recordM.FailMsg.Value("回调地址无法正常访问"),
|
|
|
)
|
|
|
|
|
|
ups = append(ups,
|
|
|
recordSM.FailType.Value(mtype.FissionRewardRecordFailTypeCli),
|
|
|
recordSM.FailMsg.Value("回调地址无法正常访问"),
|
|
|
)
|
|
|
|
|
|
mup = append(mup,
|
|
|
recordSM.FailType.Value(mtype.RewardPushFailTypeCli),
|
|
|
recordSM.FailMsg.Value("回调地址无法正常访问"),
|
|
|
)
|
|
|
} else {
|
|
|
up = append(up,
|
|
|
recordM.FailType.Value(mtype.FissionRewardRecordFailTypeSys),
|
|
|
recordM.FailMsg.Value(val),
|
|
|
)
|
|
|
ups = append(ups,
|
|
|
recordSM.FailType.Value(mtype.FissionRewardRecordFailTypeSys),
|
|
|
recordSM.FailMsg.Value(val),
|
|
|
)
|
|
|
|
|
|
mup = append(mup,
|
|
|
recordSM.FailType.Value(mtype.RewardPushFailTypeSys),
|
|
|
recordSM.FailMsg.Value(val),
|
|
|
)
|
|
|
}
|
|
|
|
|
|
} else if !strings.HasPrefix(respBody, "success") {
|
|
|
up = append(up,
|
|
|
recordM.FailType.Value(mtype.FissionRewardRecordFailTypeCli),
|
|
|
recordM.FailMsg.Value("返回无法识别值:"+cliResp),
|
|
|
)
|
|
|
ups = append(ups,
|
|
|
recordSM.FailType.Value(mtype.FissionRewardRecordFailTypeCli),
|
|
|
recordSM.FailMsg.Value("返回无法识别值:"+cliResp),
|
|
|
)
|
|
|
mup = append(mup,
|
|
|
recordSM.FailType.Value(mtype.RewardPushFailTypeCli),
|
|
|
recordSM.FailMsg.Value("返回无法识别值:"+cliResp),
|
|
|
)
|
|
|
} else {
|
|
|
up = append(up,
|
|
|
recordM.FailType.Value(mtype.FissionRewardRecordFailTypeUndefined),
|
|
|
)
|
|
|
ups = append(ups,
|
|
|
recordSM.FailType.Value(mtype.FissionRewardRecordFailTypeUndefined),
|
|
|
)
|
|
|
mup = append(mup,
|
|
|
recordSM.FailType.Value(mtype.RewardPushFailTypeUndefined),
|
|
|
)
|
|
|
}
|
|
|
|
|
|
_, _ = recordM.WithContext(ctx).
|
|
|
Where(recordM.ID.Eq(s.RewardId)).
|
|
|
UpdateSimple(up...)
|
|
|
|
|
|
_, _ = recordSM.WithContext(ctx).
|
|
|
Where(recordSM.ID.Eq(s.RewardId)).
|
|
|
UpdateSimple(ups...)
|
|
|
|
|
|
if s.MallRecordId > 0 {
|
|
|
_, _ = mallRecordM.WithContext(ctx).
|
|
|
Where(mallRecordM.ID.Eq(s.MallRecordId)).
|
|
|
UpdateSimple(mup...)
|
|
|
}
|
|
|
|
|
|
return
|
|
|
}
|
|
|
|
|
|
type InviteList struct {
|
|
|
UserId string `json:"user_id"` // 活动提供的用户id,该值为第三方提供的唯一用户标识
|
|
|
DeviceId string `json:"device_id"` // 活动提供的用户id,该值为第三方提供的唯一设备标识
|
|
|
InviteTime int64 `json:"invite_time"` // 邀请时间
|
|
|
Platform string `json:"platform"` // 平台(ios, android, web, other)
|
|
|
}
|
|
|
|
|
|
func (s Reward) body(ctx context.Context, taskId string) map[string]interface{} {
|
|
|
inviteM := dao.FissionUser
|
|
|
|
|
|
// 找下级用户数据
|
|
|
var list []*model.FissionUser
|
|
|
if s.UserId > 0 {
|
|
|
list, _ = inviteM.WithContext(ctx).Where(inviteM.CampaignID.Eq(s.CampaignId), inviteM.ParentID.Eq(s.UserId)).Find()
|
|
|
}
|
|
|
|
|
|
var ids []int64
|
|
|
for _, v := range list {
|
|
|
ids = append(ids, v.ID)
|
|
|
}
|
|
|
|
|
|
var attrMap = make(map[int64]string)
|
|
|
if len(ids) > 0 {
|
|
|
m := dao.FissionUserAttr
|
|
|
attrs, _ := m.WithContext(ctx).Where(m.UserID.In(ids...), m.Attr.Eq(mtype.FissionUserAttrMarkPlatform)).Find()
|
|
|
for _, v := range attrs {
|
|
|
attrMap[v.UserID] = v.Value
|
|
|
}
|
|
|
}
|
|
|
|
|
|
var il = make([]*InviteList, 0, len(list))
|
|
|
for _, v := range list {
|
|
|
il = append(il, &InviteList{
|
|
|
UserId: v.SubUserID,
|
|
|
DeviceId: v.DeviceID,
|
|
|
InviteTime: v.CreatedAt,
|
|
|
Platform: utils.Ternary[string](attrMap[v.ID] == "", consts.PlatformOther, attrMap[v.ID]),
|
|
|
})
|
|
|
}
|
|
|
|
|
|
var kvData = make(map[string]string, len(s.KvData))
|
|
|
for _, v := range s.KvData {
|
|
|
kvData[v.Key] = v.Value
|
|
|
}
|
|
|
|
|
|
return map[string]interface{}{
|
|
|
"task_id": taskId,
|
|
|
"source": "campaign",
|
|
|
"campaign_id": s.CampaignHashId,
|
|
|
"reward_id": s.RewardHashId,
|
|
|
"reward_type": s.RewardType,
|
|
|
"reward_title": s.RewardTitle,
|
|
|
"reward_num": s.RewardNum,
|
|
|
"user_id": s.SubUserId,
|
|
|
"user_platform": s._getPlatform(ctx, s.UserId),
|
|
|
"user_pid": s.ParentSubUserId,
|
|
|
"parent_platform": s._getPlatform(ctx, s.ParentId),
|
|
|
"kv_data": kvData,
|
|
|
"invite_list": il,
|
|
|
"lang": s.Lang,
|
|
|
"title": s.Title,
|
|
|
"custom_param": s._getCustomParam(ctx, s.UserId),
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 获取平台信息
|
|
|
func (s Reward) _getPlatform(ctx context.Context, userId int64) (platform string) {
|
|
|
if userId == 0 {
|
|
|
return
|
|
|
}
|
|
|
attr, err := cache.NewFissionUserAttr(ctx).FindByUniqIdx(userId, mtype.FissionUserAttrMarkPlatform)
|
|
|
if err != nil {
|
|
|
return
|
|
|
}
|
|
|
|
|
|
if attr != nil {
|
|
|
platform = attr.Value
|
|
|
}
|
|
|
|
|
|
if platform == "" {
|
|
|
platform = consts.PlatformOther
|
|
|
}
|
|
|
|
|
|
return
|
|
|
}
|
|
|
|
|
|
func (s Reward) createTask(ctx context.Context, fnId int64, sec int) {
|
|
|
exp := time.Duration(sec) * time.Second
|
|
|
|
|
|
// 防止一些并发操作,导致同时推送到客户端
|
|
|
lc := app.NewRedisLock(ctx, ckey.FissionRewardPushTaskOnce(fnId), lock.SetExpire(exp-time.Second))
|
|
|
if !lc.SoftLock() {
|
|
|
return
|
|
|
}
|
|
|
|
|
|
reward, _ := jsonm.MarshalToString(s)
|
|
|
fntM := dao.FissionNotifyTask
|
|
|
task := withdefault.FissionNotifyTask(&model.FissionNotifyTask{
|
|
|
DoTime: time.Now().Add(exp).Unix(),
|
|
|
NotifyID: fnId,
|
|
|
Reward: reward,
|
|
|
})
|
|
|
|
|
|
_ = fntM.WithContext(ctx).Create(task)
|
|
|
producer.RewardPushPub(ctx, sec, types.DelayCommonRewardPush{TaskId: task.ID})
|
|
|
}
|
|
|
|
|
|
func (s Reward) hasTask(ctx context.Context, fnId int64) bool {
|
|
|
return app.Redis.Cache.Exists(ctx, ckey.FissionRewardPushTaskOnce(fnId)).Val() > 0
|
|
|
}
|
|
|
|
|
|
func (s Reward) _getCustomParam(ctx context.Context, userId int64) (data []*mtype.KV) {
|
|
|
if userId == 0 {
|
|
|
return
|
|
|
}
|
|
|
attr, err := cache.NewFissionUserJattr(ctx).FindByUniqIdx(userId, mtype.FissionUserAttrMarkCustomParam)
|
|
|
if err != nil {
|
|
|
return
|
|
|
}
|
|
|
|
|
|
if attr == nil {
|
|
|
return make([]*mtype.KV, 0)
|
|
|
}
|
|
|
|
|
|
var params = strings.Split(attr.Value, ",")
|
|
|
data = make([]*mtype.KV, 0, len(params))
|
|
|
for _, v := range params {
|
|
|
key, val, _ := strings.Cut(v, "$")
|
|
|
|
|
|
data = append(data, &mtype.KV{Key: key, Value: val})
|
|
|
}
|
|
|
|
|
|
return
|
|
|
}
|