From 9b830999aab977f5d02654964d743d1033a0bb71 Mon Sep 17 00:00:00 2001 From: Leufolium Date: Thu, 7 Nov 2024 17:05:13 +0800 Subject: [PATCH] by Robin at 20241107 --- api/consts/consts.go | 1 + api/consts/notif_template.go | 2 +- api/errcode/errcode.go | 2 + api/interfaces/nid_accessible.go | 5 ++ apollostruct/acct_punishment_real_endtime.go | 26 +++++++++ app/mix/service/logic/accountpunishment.go | 2 - app/mix/service/notif_builder_handler.go | 23 +++++++- app/mix/service/service.go | 27 ++++++++- app/mix/service/utilservice.go | 58 ++++++++++++++------ dbstruct/accountpunishment.go | 8 +++ dbstruct/notification.go | 24 +++++--- library/middleware/notif_sender.go | 22 ++++++++ 12 files changed, 169 insertions(+), 31 deletions(-) create mode 100644 api/interfaces/nid_accessible.go create mode 100644 apollostruct/acct_punishment_real_endtime.go diff --git a/api/consts/consts.go b/api/consts/consts.go index 3e670a0e..98174dd1 100644 --- a/api/consts/consts.go +++ b/api/consts/consts.go @@ -66,6 +66,7 @@ const ( DefaultZoneTextKey = "default_zone_text" AuditTaskCollectionReflectKey = "audit_task_collection_reflect" NotifBannerInfoKey = "notif_banner_info" + AcctPunishmentRealEndTimeKey = "acct_punishment_real_endtime" ) // del_flag diff --git a/api/consts/notif_template.go b/api/consts/notif_template.go index 14dfad4f..eb244df3 100644 --- a/api/consts/notif_template.go +++ b/api/consts/notif_template.go @@ -7,8 +7,8 @@ const ( Notif_Audit = 1 // 审核消息 Notif_Vas = 2 // 付费消息 ) - const ( + SysNotifTemp_CancelNotif = -1 // 取消推送通知 SysNotifTemp_FirstLogin = 1 // 首次登录 SysNotifTemp_StreamerPunished = 2 // 主播封禁 SysNotifTemp_StreamerPunishmentEnds = 3 // 主播封禁结束 diff --git a/api/errcode/errcode.go b/api/errcode/errcode.go index c80b21ca..44c86a4b 100644 --- a/api/errcode/errcode.go +++ b/api/errcode/errcode.go @@ -185,6 +185,7 @@ var ErrCodeMsgMap = map[ErrCode]string{ ErrCodeAccountPunishmentHasFinished: "账号处罚已正常结束,无法执行该操作!", ErrCodeAccountPunishmentHasBeenInterrupted: "账号处罚已提前中止,无法执行该操作!", ErrCodeAccountPunishmentStreamerOnly: "该账号处罚行为仅能对主播进行!", + ErrCodeAccountPunishmentEndTimeCalcFail: "账号处罚结束时间计算错误", ErrCodeAccountCancellationSrvFail: "账户注销服务错误", ErrCodeAccountCancellationNotExist: "账户注销不存在", @@ -518,6 +519,7 @@ const ( ErrCodeAccountPunishmentHasFinished ErrCode = -32005 // 账号处罚已正常结束 ErrCodeAccountPunishmentHasBeenInterrupted ErrCode = -32006 // 账号处罚已提前中止 ErrCodeAccountPunishmentStreamerOnly ErrCode = -32007 // 该账号处罚仅能对主播进行 + ErrCodeAccountPunishmentEndTimeCalcFail ErrCode = -32008 // 账号处罚结束时间计算错误 // Zone: 33xxx ErrCodeZoneSrvOk ErrCode = ErrCodeOk diff --git a/api/interfaces/nid_accessible.go b/api/interfaces/nid_accessible.go new file mode 100644 index 00000000..107cd521 --- /dev/null +++ b/api/interfaces/nid_accessible.go @@ -0,0 +1,5 @@ +package interfaces + +type NidAccessible interface { + GetNid() int64 +} diff --git a/apollostruct/acct_punishment_real_endtime.go b/apollostruct/acct_punishment_real_endtime.go new file mode 100644 index 00000000..c12eedb6 --- /dev/null +++ b/apollostruct/acct_punishment_real_endtime.go @@ -0,0 +1,26 @@ +package apollostruct + +// 账号处罚结束实际生效时间配置,用于通知推送服务 + +/* 在账号处罚创建时,除了立即给用户推送一条封禁通知,还会写一条解禁通知,推送时间为处罚的结束时间 + 正常来说,账号处罚结束时间 = 账号处罚创建时间 + 触发时长 + 但由于具体场景存在的时延,在上述计算得到的账号处罚的结束时间,处罚可能还未结束 + 如主播推荐列表每1个小时推送一次,故它实际生效时间应该是结束时间顺延的下一个小时整点 + 动态推荐列表每5分钟推送一次,故它实际生效时间应该是结束时间顺延的下一个5分整点*/ + +/* 我们认为用户实际感知到的处罚结束应该在他收到通知的时候,所以解禁的通知时间需要根据具体场景延后一些 + interval_map表示具体场景的定时任务每隔多少时间执行一次,单位为秒 + 比如对于主播推荐列表,时间间隔就是3600秒,解禁通知的时间戳需要补足至3600秒的倍数 +*/ + +/* + default_interval表示推送“定时通知”的定时任务的时间间隔 + 如果这条封禁的场景没有定时推送的需求,则解禁通知的时间戳补足至default_interval的倍数即可 + 用以满足“用户实际感知到的处罚结束应该在他收到通知的时候” + 需要注意,interval_map每个键值对的值必须是default_interval的倍数,否则推送通知会产生异常 +*/ + +type AcctPunishmentRealEndtimeCfg struct { + IntervalMap map[string]int64 `json:"interval_map"` // 时间间隔map + DefaultInterval int64 `json:"default_interval"` // 默认通知时间间隔 +} diff --git a/app/mix/service/logic/accountpunishment.go b/app/mix/service/logic/accountpunishment.go index 25d0ed18..07a148ec 100644 --- a/app/mix/service/logic/accountpunishment.go +++ b/app/mix/service/logic/accountpunishment.go @@ -4,7 +4,6 @@ import ( "service/api/consts" accountpunishmentproto "service/api/proto/accountpunishment/proto" "service/app/mix/dao" - "service/bizcommon/util" "service/dbstruct" "service/library/logger" "time" @@ -36,7 +35,6 @@ func (p *AccountPunishment) OpCreate(ctx *gin.Context, req *accountpunishmentpro nowTime := time.Now().Unix() req.AccountPunishment.Id = goproto.Int64(accountpunishmentIdSeq.Seq) req.AccountPunishment.Status = goproto.Int64(consts.AccountPunishment_Punishing) - req.AccountPunishment.EndTime = goproto.Int64(nowTime + util.DerefInt64(req.Duration)) req.AccountPunishment.Ct = goproto.Int64(nowTime) req.AccountPunishment.Ut = goproto.Int64(nowTime) req.AccountPunishment.DelFlag = goproto.Int64(consts.Exist) diff --git a/app/mix/service/notif_builder_handler.go b/app/mix/service/notif_builder_handler.go index 83cc1249..ed777d66 100644 --- a/app/mix/service/notif_builder_handler.go +++ b/app/mix/service/notif_builder_handler.go @@ -2,12 +2,15 @@ package service import ( "service/api/consts" + "service/api/interfaces" + accountpunishmentproto "service/api/proto/accountpunishment/proto" zoneproto "service/api/proto/zone/proto" "service/bizcommon/util" "service/dbstruct" "service/library/logger" "github.com/gin-gonic/gin" + goproto "google.golang.org/protobuf/proto" ) var DefaultNotifBuilderHandler *NotifBuilderHandler @@ -28,6 +31,7 @@ func NewNotifBuilderHandler() *NotifBuilderHandler { } func (handler *NotifBuilderHandler) init() { + handler.handleSysNotifCancelNotif() handler.handleSysFirstLogin() handler.handleSysStreamerPunished() handler.handleSysPswdChanged() @@ -38,6 +42,13 @@ func (handler *NotifBuilderHandler) init() { handler.handleSysZoneThirdPartnerCreated() } +func (handler *NotifBuilderHandler) handleSysNotifCancelNotif() { + handler.handlerMap[consts.SysNotifTemp_CancelNotif] = func(ctx *gin.Context, args ...any) { + nidAccessor := args[0].(interfaces.NidAccessible) + DefaultService.utilWriteNotifCancel(ctx, nidAccessor) + } +} + func (handler *NotifBuilderHandler) handleSysFirstLogin() { handler.handlerMap[consts.SysNotifTemp_FirstLogin] = func(ctx *gin.Context, args ...any) { account := args[0].(*dbstruct.Account) @@ -53,7 +64,17 @@ func (handler *NotifBuilderHandler) handleSysStreamerPunished() { consts.AccountPunishmentMap[acctpunishment.GetType()], util.FormatTsAsNotifT(acctpunishment.GetEndTime())) // 解禁通知 if !acctpunishment.IsPermanent() { - DefaultService.utilWriteCrontabNotifInfo(ctx, consts.SysNotifTemp_StreamerPunishmentEnds, acctpunishment.GetMid(), acctpunishment.GetEndTime()) + argsMap := make(map[string]any) + argsMap["push_time"] = acctpunishment.GetEndTime() + argsMap["nid_save_func"] = func(ctx *gin.Context, nid int64) error { + return _DefaultAccountPunishment.OpUpdate(ctx, &accountpunishmentproto.OpUpdateReq{ + AccountPunishment: &dbstruct.AccountPunishment{ + Id: acctpunishment.Id, + Nid: goproto.Int64(nid), + }, + }) + } + DefaultService.utilWriteNotifInfoByMap(ctx, consts.SysNotifTemp_StreamerPunishmentEnds, acctpunishment.GetMid(), argsMap) } } } diff --git a/app/mix/service/service.go b/app/mix/service/service.go index 5f4cd583..fbf34b6d 100644 --- a/app/mix/service/service.go +++ b/app/mix/service/service.go @@ -3754,7 +3754,17 @@ func (s *Service) OpCreateAccountPunishment(ctx *gin.Context, req *accountpunish return } - err := _DefaultAccountPunishment.OpCreate(ctx, req) + // 计算处罚结束的通知时间 + req.AccountPunishment.EndTime = goproto.Int64(time.Now().Unix() + util.DerefInt64(req.Duration)) + realEndTime, err := s.utilCalcAccountPunishmentEndNotifTime(req.AccountPunishment) + if err != nil { + logger.Error("utilCalcAccountPunishmentEndNotifTime fail, req: %v, err: %v", util.ToJson(req), err) + ec = errcode.ErrCodeAccountPunishmentSrvFail + return + } + req.AccountPunishment.EndTime = goproto.Int64(realEndTime) + + err = _DefaultAccountPunishment.OpCreate(ctx, req) if err != nil { logger.Error("OpCreate fail, req: %v, err: %v", util.ToJson(req), err) ec = errcode.ErrCodeAccountPunishmentSrvFail @@ -3790,6 +3800,21 @@ func (s *Service) OpUnblockAccountPunishment(ctx *gin.Context, req *accountpunis ec = errcode.ErrCodeAccountPunishmentSrvFail return } + + // 取消该条推送 + acctpunishment, err := _DefaultAccountPunishment.OpListById(ctx, util.DerefInt64(req.Id)) + if err == qmgo.ErrNoSuchDocuments { + ec = errcode.ErrCodeAccountPunishmentNotExist + err = nil + return + } + if err != nil { + logger.Error("OpListById fail, req: %v, err: %v", util.ToJson(req), err) + ec = errcode.ErrCodeAccountPunishmentSrvFail + return + } + DefaultNotifBuilderHandler.Handle(ctx, consts.SysNotifTemp_CancelNotif, acctpunishment) + return } diff --git a/app/mix/service/utilservice.go b/app/mix/service/utilservice.go index 884c1c34..5b265826 100644 --- a/app/mix/service/utilservice.go +++ b/app/mix/service/utilservice.go @@ -2116,6 +2116,21 @@ func (s *Service) UtilEncryptVideosForZoneMomentVOs(ctx *gin.Context, list []*zo } } +func (s *Service) utilWriteNotifCancel(ctx *gin.Context, nidAccessor interfaces.NidAccessible) { + // 获取通知builder + notifBuilders := make([]*dbstruct.NotifBuilder, 0) + notifBuildersObj, ok := ctx.Get("notif_builders") + if ok { + notifBuilders = notifBuildersObj.([]*dbstruct.NotifBuilder) + } + + notifBuilders = append(notifBuilders, &dbstruct.NotifBuilder{ + TemplateId: consts.SysNotifTemp_CancelNotif, + NidGetter: nidAccessor.GetNid, + }) + ctx.Set("notif_builders", notifBuilders) +} + func (s *Service) utilWriteNotifInfo(ctx *gin.Context, notifTempId int64, objMid int64, notifTempParams ...any) { // 获取通知builder notifBuilders := make([]*dbstruct.NotifBuilder, 0) @@ -2132,23 +2147,6 @@ func (s *Service) utilWriteNotifInfo(ctx *gin.Context, notifTempId int64, objMid ctx.Set("notif_builders", notifBuilders) } -func (s *Service) utilWriteCrontabNotifInfo(ctx *gin.Context, notifTempId int64, objMid int64, pushTime int64, notifTempParams ...any) { - // 获取通知builder - notifBuilders := make([]*dbstruct.NotifBuilder, 0) - notifBuildersObj, ok := ctx.Get("notif_builders") - if ok { - notifBuilders = notifBuildersObj.([]*dbstruct.NotifBuilder) - } - - notifBuilders = append(notifBuilders, &dbstruct.NotifBuilder{ - TemplateId: notifTempId, - ObjMids: []int64{objMid}, - PushTime: pushTime, - TemplateParams: notifTempParams, - }) - ctx.Set("notif_builders", notifBuilders) -} - func (s *Service) utilWriteNotifInfoByMap(ctx *gin.Context, notifTempId int64, objMid int64, mp map[string]any) { // 获取通知builder notifBuilders := make([]*dbstruct.NotifBuilder, 0) @@ -2191,6 +2189,32 @@ func (s *Service) utilWriteNotifInfoByMap(ctx *gin.Context, notifTempId int64, o notifBuilder.PushTime = pushTimeObj.(int64) } + nidSaveFuncObj, ok := mp["nid_save_func"] + if ok { + notifBuilder.NidSaveFunc = nidSaveFuncObj.(func(ctx *gin.Context, nid int64) error) + } + notifBuilders = append(notifBuilders, notifBuilder) ctx.Set("notif_builders", notifBuilders) } + +func (s *Service) utilCalcAccountPunishmentEndNotifTime(acctpunishment *dbstruct.AccountPunishment) (int64, error) { + + notifTime := acctpunishment.GetEndTime() + + cfg := apollostruct.AcctPunishmentRealEndtimeCfg{} + err := apollo.GetJson(consts.AcctPunishmentRealEndTimeKey, &cfg, apollo.ApolloOpts().SetNamespace("application")) + if err != nil { + logger.Error("Apollo read failed : %v", err) + return 0, err + } + + interval, ok := cfg.IntervalMap[fmt.Sprint(acctpunishment.GetType())] + if !ok { + interval = cfg.DefaultInterval + } + if notifTime%interval != 0 { // 已经是倍数则不再顺延 + notifTime = (notifTime/interval + 1) * interval + } + return notifTime, nil +} diff --git a/dbstruct/accountpunishment.go b/dbstruct/accountpunishment.go index f3a8b8ff..2b6e30c9 100644 --- a/dbstruct/accountpunishment.go +++ b/dbstruct/accountpunishment.go @@ -14,6 +14,7 @@ type AccountPunishment struct { Duration *int64 `json:"duration" bson:"duration"` // 处罚时长 EndTime *int64 `json:"end_time" bson:"end_time"` // 处罚结束时间 Status *int64 `json:"status" bson:"status"` // 处罚结果 + Nid *int64 `json:"nid" bson:"nid"` // 通知表id,用于控制推送 Ct *int64 `json:"ct" bson:"ct"` // 创建时间 Ut *int64 `json:"ut" bson:"ut"` // 更新时间 DelFlag *int64 `json:"del_flag" bson:"del_flag"` // 删除标记 @@ -60,3 +61,10 @@ func (p *AccountPunishment) GetType() int64 { } return *p.Type } + +func (p *AccountPunishment) GetNid() int64 { + if p == nil || p.Nid == nil { + return 0 + } + return *p.Nid +} diff --git a/dbstruct/notification.go b/dbstruct/notification.go index 15f1d620..971a2516 100644 --- a/dbstruct/notification.go +++ b/dbstruct/notification.go @@ -1,5 +1,9 @@ package dbstruct +import ( + "github.com/gin-gonic/gin" +) + type Notification struct { Id *int64 `json:"id" bson:"_id"` // 系统通知表id SubMid *int64 `json:"sub_mid" bson:"sub_mid"` // 通知发送人mid @@ -95,13 +99,15 @@ type NotifReceivePull struct { } type NotifBuilder struct { - TemplateId int64 // 模板id - TemplateParams []any // 模板参数 - ObjMids []int64 // 目标Mids - ObjType int64 // 目标类型 - LinkTextTemplateParams []any // 链接模板参数 - Thumbnail *MediaComponent // 缩略图 - Action string // 行为 - Params string // 行为参数 - PushTime int64 // 推送时间 + TemplateId int64 // 模板id + TemplateParams []any // 模板参数 + ObjMids []int64 // 目标Mids + ObjType int64 // 目标类型 + LinkTextTemplateParams []any // 链接模板参数 + Thumbnail *MediaComponent // 缩略图 + Action string // 行为 + Params string // 行为参数 + PushTime int64 // 推送时间 + NidSaveFunc func(ctx *gin.Context, nid int64) error // nid存储函数,一般用于取消推送 + NidGetter func() int64 // nid访问器 } diff --git a/library/middleware/notif_sender.go b/library/middleware/notif_sender.go index 641d4025..888d476f 100644 --- a/library/middleware/notif_sender.go +++ b/library/middleware/notif_sender.go @@ -32,6 +32,21 @@ func InitNotifSender(_DefaultNotification *logic.Notification, _DefaultNotifBcst notifBuilders := notifBuildersObj.([]*dbstruct.NotifBuilder) for _, notifBuilder := range notifBuilders { + + // 如果是取消推送,则更新推送状态为已取消 + if notifBuilder.TemplateId == consts.SysNotifTemp_CancelNotif { + err := _DefaultNotification.OpUpdate(ctx, ¬ificationproto.OpUpdateReq{ + Notification: &dbstruct.Notification{ + Id: goproto.Int64(notifBuilder.NidGetter()), + Status: goproto.Int64(consts.Notification_Cancelled), + }, + }) + if err != nil { + logger.Error("_DefaultNotification OpUpdate failed : %v", err) + } + continue + } + notification := &dbstruct.Notification{} notification.SubMid = goproto.Int64(0) @@ -80,6 +95,13 @@ func InitNotifSender(_DefaultNotification *logic.Notification, _DefaultNotifBcst ctx.Next() } + // 将通知id暂存,若暂存失败,则不广播该条通知,因业务无法再控制其停止 + err = notifBuilder.NidSaveFunc(ctx, notification.GetId()) + if err != nil { + logger.Error("通知id暂存失败:%v", err) + continue + } + nids := make([]int64, 0) nids = append(nids, notification.GetId())