diff --git a/api/consts/option.go b/api/consts/option.go index 78ab15a9..94fe6dbd 100644 --- a/api/consts/option.go +++ b/api/consts/option.go @@ -100,3 +100,5 @@ const ( ) const ZoneAdmissionPrice_NoZone = int64(-999999) + +const ActivityHot_QueryLimit = 20 diff --git a/api/consts/status.go b/api/consts/status.go index ce9a131d..d2e10dfa 100644 --- a/api/consts/status.go +++ b/api/consts/status.go @@ -340,3 +340,15 @@ const ( RavenIQTest_VType_Visit = 0 // 访问 RavenIQTest_VType_Paid = 1 // 付款 ) + +// 热榜状态 +const ( + ActivityHotStatus_Normal = 0 // 已生效 + ActivityHotStatus_Paused = 1 // 已暂停 +) + +// 活动Banner状态 +const ( + ActivityBannerStatus_Normal = 0 // 已生效 + ActivityBannerStatus_Paused = 1 // 已暂停 +) diff --git a/api/proto/activity_banner/proto/activity_banner_api_vo.go b/api/proto/activity_banner/proto/activity_banner_api_vo.go new file mode 100644 index 00000000..d205902e --- /dev/null +++ b/api/proto/activity_banner/proto/activity_banner_api_vo.go @@ -0,0 +1,23 @@ +package proto + +import "service/dbstruct" + +type ActivityBannerApiVO struct { + Image *dbstruct.MediaComponent `json:"image" bson:"image"` // 主图 + Title string `json:"title" bson:"title"` // 标题 + Hyperlinks []*dbstruct.Hyperlink `json:"hyperlinks" bson:"hyperlinks"` // 跳转链接 +} + +func NewActivityBannerApiVO() *ActivityBannerApiVO { + return &ActivityBannerApiVO{} +} + +func (vo *ActivityBannerApiVO) CopyActivityBanner(activityBanner *dbstruct.ActivityBanner) *ActivityBannerApiVO { + if activityBanner == nil { + return vo + } + vo.Image = activityBanner.Image + vo.Title = activityBanner.GetTitle() + vo.Hyperlinks = activityBanner.Hyperlinks + return vo +} diff --git a/api/proto/activity_hot/proto/activity_hot_api_vo.go b/api/proto/activity_hot/proto/activity_hot_api_vo.go new file mode 100644 index 00000000..71b1ab7d --- /dev/null +++ b/api/proto/activity_hot/proto/activity_hot_api_vo.go @@ -0,0 +1,54 @@ +package proto + +import ( + "service/dbstruct" +) + +type ActivityHotApiVO struct { + Mid int64 `json:"mid" bson:"mid"` // 主播mid + Image *dbstruct.MediaComponent `json:"image" bson:"image"` // 主图 + Title string `json:"title" bson:"title"` // 标题 + Text string `json:"text" bson:"text"` // 文字内容 + Hyperlinks []*dbstruct.Hyperlink `json:"hyperlinks" bson:"hyperlinks"` // 跳转链接 +} + +func NewActivityHotApiVO() *ActivityHotApiVO { + return &ActivityHotApiVO{} +} + +func (vo *ActivityHotApiVO) CopyActivityHot(activityHot *dbstruct.ActivityHot) *ActivityHotApiVO { + if activityHot == nil { + return vo + } + vo.Mid = activityHot.GetMid() + vo.Image = activityHot.Image + vo.Title = activityHot.GetTitle() + vo.Text = activityHot.GetText() + vo.Hyperlinks = activityHot.Hyperlinks + return vo +} + +func (vo *ActivityHotApiVO) CopyZone(zone *dbstruct.Zone) *ActivityHotApiVO { + if zone == nil { + return vo + } + vo.Text = zone.GetProfile() + return vo +} + +func (vo *ActivityHotApiVO) CopyAccount(account *dbstruct.Account) *ActivityHotApiVO { + if account == nil { + return vo + } + vo.Image = account.Avatar + vo.Title = account.GetName() + return vo +} + +func (vo *ActivityHotApiVO) CopyHyperlinks(getFunc func(mid int64) []*dbstruct.Hyperlink) *ActivityHotApiVO { + if getFunc == nil { + return vo + } + vo.Hyperlinks = getFunc(vo.Mid) + return vo +} diff --git a/app/mix/dao/mongo.go b/app/mix/dao/mongo.go index d93bab37..de5a39f2 100644 --- a/app/mix/dao/mongo.go +++ b/app/mix/dao/mongo.go @@ -4818,6 +4818,20 @@ func (m *Mongo) GetMidsByZids(ctx *gin.Context, zids []int64) ([]int64, error) { return mids, err } +func (m *Mongo) GetMids(ctx *gin.Context) ([]int64, error) { + col := m.getColZone() + query := qmgo.M{ + "del_flag": 0, + } + mids := make([]int64, 0) + err := col.Find(ctx, query).Distinct("mid", &mids) + if err == qmgo.ErrNoSuchDocuments { + err = nil + return make([]int64, 0), err + } + return mids, err +} + func (m *Mongo) RecordZoneStatisticsById(ctx *gin.Context, id int64, zoneMomentCount int64, imageCount int64, videoCount int64) error { col := m.getColZone() up := qmgo.M{ @@ -6574,7 +6588,7 @@ func (m *Mongo) GetActivityHotList(ctx *gin.Context, req *activity_hot_proto.OpL } } - err := col.Find(ctx, query).Sort("-ct").Skip(int64(req.Offset)).Limit(int64(req.Limit)).All(&list) + err := col.Find(ctx, query).Sort("-priority").Skip(int64(req.Offset)).Limit(int64(req.Limit)).All(&list) if err == qmgo.ErrNoSuchDocuments { err = nil return list, err diff --git a/app/mix/service/apiservice.go b/app/mix/service/apiservice.go index ac422f85..f550775b 100644 --- a/app/mix/service/apiservice.go +++ b/app/mix/service/apiservice.go @@ -4465,23 +4465,63 @@ func (s *Service) ApiGetRavenIQTestVisitCount(ctx *gin.Context, req *Raven_IQ_te return } -func (s *Service) ApiGetActivityHotList(ctx *gin.Context, req *activity_hot_proto.ApiListReq) (list []*dbstruct.ActivityHot, ec errcode.ErrCode) { +func (s *Service) ApiGetActivityHotList(ctx *gin.Context, req *activity_hot_proto.ApiListReq) (volist []*activity_hot_proto.ActivityHotApiVO, ec errcode.ErrCode) { ec = errcode.ErrCodeActivityHotSrvOk + + volist = make([]*activity_hot_proto.ActivityHotApiVO, 0) + + // 先尝试从编推里拿出指定数量的主播 list, err := _DefaultActivityHot.OpList(ctx, &activity_hot_proto.OpListReq{ BaseRequest: req.BaseRequest, - Offset: req.Offset, - Limit: req.Limit, + Status: goproto.Int64(consts.ActivityHotStatus_Normal), + Offset: 0, + Limit: consts.ActivityHot_QueryLimit, }) if err != nil { logger.Error("OpGetActivityHotList fail, req: %v, err: %v", util.ToJson(req), err) ec = errcode.ErrCodeActivityHotSrvFail return } + + // 做map和mids + activityHotMp := make(map[int64]*dbstruct.ActivityHot) + mids := make([]int64, 0) + for _, v := range list { + activityHotMp[v.GetMid()] = v + mids = append(mids, v.GetMid()) + } + + // 填充信息 + if len(list) > 0 { + vl, err := s.utilFillActivityHotVO(ctx, activityHotMp, mids) + if err != nil { + logger.Error("utilFillActivityHotVO fail, req: %v, err: %v", util.ToJson(req), err) + ec = errcode.ErrCodeActivityHotSrvFail + return + } + volist = append(volist, vl...) + } + + // 再补足剩余的主播 + remLength := consts.ActivityHot_QueryLimit - len(list) + if remLength > 0 { + vl, err := s.utilCompleteActivityHotList(ctx, activityHotMp, remLength) + if err != nil { + logger.Error("utilCompleteActivityHotList fail, req: %v, err: %v", util.ToJson(req), err) + ec = errcode.ErrCodeActivityHotSrvFail + return + } + volist = append(volist, vl...) + } + return } -func (s *Service) ApiGetActivityBannerList(ctx *gin.Context, req *activity_banner_proto.ApiListReq) (list []*dbstruct.ActivityBanner, ec errcode.ErrCode) { +func (s *Service) ApiGetActivityBannerList(ctx *gin.Context, req *activity_banner_proto.ApiListReq) (volist []*activity_banner_proto.ActivityBannerApiVO, ec errcode.ErrCode) { ec = errcode.ErrCodeActivityBannerSrvOk + + volist = make([]*activity_banner_proto.ActivityBannerApiVO, 0) + list, err := _DefaultActivityBanner.OpList(ctx, &activity_banner_proto.OpListReq{ BaseRequest: req.BaseRequest, DeviceType: req.DeviceType, @@ -4493,5 +4533,10 @@ func (s *Service) ApiGetActivityBannerList(ctx *gin.Context, req *activity_banne ec = errcode.ErrCodeActivityBannerSrvFail return } + + for _, activityBanner := range list { + volist = append(volist, activity_banner_proto.NewActivityBannerApiVO().CopyActivityBanner(activityBanner)) + } + return } diff --git a/app/mix/service/service.go b/app/mix/service/service.go index 1948c8c2..727f91d3 100644 --- a/app/mix/service/service.go +++ b/app/mix/service/service.go @@ -5061,6 +5061,8 @@ func (s *Service) OpCreateActivityHot(ctx *gin.Context, req *activity_hot_proto. return } + // 新创建的活动默认直接生效 + req.ActivityHot.Status = goproto.Int64(consts.ActivityHotStatus_Normal) err := _DefaultActivityHot.OpCreate(ctx, req) if err != nil { logger.Error("OpCreate fail, req: %v, err: %v", util.ToJson(req), err) @@ -5111,6 +5113,9 @@ func (s *Service) OpGetActivityHotList(ctx *gin.Context, req *activity_hot_proto // ActivityBanner func (s *Service) OpCreateActivityBanner(ctx *gin.Context, req *activity_banner_proto.OpCreateReq) (ec errcode.ErrCode) { ec = errcode.ErrCodeActivityBannerSrvOk + + // 新创建的活动默认直接生效 + req.ActivityBanner.Status = goproto.Int64(consts.ActivityBannerStatus_Normal) err := _DefaultActivityBanner.OpCreate(ctx, req) if err != nil { logger.Error("OpCreate fail, req: %v, err: %v", util.ToJson(req), err) diff --git a/app/mix/service/utilservice.go b/app/mix/service/utilservice.go index 0c80440b..9ef017b7 100644 --- a/app/mix/service/utilservice.go +++ b/app/mix/service/utilservice.go @@ -10,6 +10,7 @@ import ( "service/api/message/request" accountproto "service/api/proto/account/proto" accountrelationproto "service/api/proto/accountrelation/proto" + activity_hot_proto "service/api/proto/activity_hot/proto" contact_customer_service_sessionproto "service/api/proto/contact_customer_service_session/proto" imageauditproto "service/api/proto/imageaudit/proto" imageaudittaskproto "service/api/proto/imageaudittask/proto" @@ -2208,3 +2209,111 @@ func (s *Service) UtilEncryptVideosForZoneMomentVOs(ctx *gin.Context, list []*zo } } } + +func (s *Service) utilFillActivityHotVO(ctx *gin.Context, activityHotMp map[int64]*dbstruct.ActivityHot, mids []int64) ([]*activity_hot_proto.ActivityHotApiVO, error) { + volist := make([]*activity_hot_proto.ActivityHotApiVO, 0) + + acctMp, err := _DefaultAccount.GetAccountMapByMids(ctx, mids) + if err != nil { + logger.Error("GetAccountMapByMids failed, err: %v", err) + return make([]*activity_hot_proto.ActivityHotApiVO, 0), err + } + + zoneMp, err := _DefaultZone.GetZoneMapByMids(ctx, mids) + if err != nil { + logger.Error("GetZoneMapByMids failed, err: %v", err) + return make([]*activity_hot_proto.ActivityHotApiVO, 0), err + } + + for mid, activityHot := range activityHotMp { + vo := activity_hot_proto.NewActivityHotApiVO().CopyActivityHot(activityHot) + if activityHot.Image == nil { // 主图为空,填充头像 + vo.Image = acctMp[mid].Avatar + } + if activityHot.Title == nil { // 标题为空,填充昵称 + vo.Title = acctMp[mid].GetName() + } + if activityHot.Text == nil { // 文字内容为空,填充空间简介 + vo.Text = zoneMp[mid][0].GetProfile() + } + if len(activityHot.Hyperlinks) == 0 { // 超链接为空,填充空间链接 + vo.CopyHyperlinks(s.utilGetZoneHyperlinks) + } + volist = append(volist, vo) + } + return volist, nil +} + +func (s *Service) utilCompleteActivityHotList(ctx *gin.Context, existedMp map[int64]*dbstruct.ActivityHot, remLength int) ([]*activity_hot_proto.ActivityHotApiVO, error) { + + volist := make([]*activity_hot_proto.ActivityHotApiVO, 0) + voMap := make(map[int64]*activity_hot_proto.ActivityHotApiVO) + selectedMids := make([]int64, 0) + + // 从redis中获取主播推荐列表 + recommlist := make([]int64, 0) + err := redis.GetRedisClient().GetObject(consts.RedisStreamerPrefix+"recomm_list", &recommlist) + if err != nil { + logger.Error("Redis read failed : %v", err) + return make([]*activity_hot_proto.ActivityHotApiVO, 0), err + } + + // 存量主播基本一定有空间,且排位越靠前的主播拥有空间的概率越大,一次性查询100个出来基本能保证命中,未全命中则继续查询 + for offset, count := 0, 0; offset < len(recommlist) && count < remLength; offset += 100 { + // 取切片 + end := offset + 100 + if end > len(recommlist) { + end = len(recommlist) + } + mids := recommlist[offset:end] + + // 取空间 + zoneMp, err := _DefaultZone.GetZoneMapByMids(ctx, mids) + if err != nil { + logger.Error("GetZoneMapByMids failed, err: %v", err) + return make([]*activity_hot_proto.ActivityHotApiVO, 0), err + } + + for _, mid := range mids { + if _, ok := existedMp[mid]; ok { // 已经存在的编推不再补足进去 + continue + } + if len(zoneMp[mid]) > 0 { // 该mid有空间,进行补足 + vo := activity_hot_proto.NewActivityHotApiVO().CopyZone(zoneMp[mid][0]) + volist = append(volist, vo) + voMap[mid] = vo + selectedMids = append(selectedMids, mid) + count++ + } + if count == remLength { + break + } + } + } + + // 补足account表信息 + acctMp, err := _DefaultAccount.GetAccountMapByMids(ctx, selectedMids) + if err != nil { + logger.Error("GetAccountMapByMids failed, err: %v", err) + return make([]*activity_hot_proto.ActivityHotApiVO, 0), err + } + + for mid, vo := range voMap { + vo.CopyAccount(acctMp[mid]).CopyHyperlinks(s.utilGetZoneHyperlinks) + } + + return volist, nil +} + +func (s *Service) utilGetZoneHyperlinks(mid int64) []*dbstruct.Hyperlink { + list := make([]*dbstruct.Hyperlink, 0) + list = append(list, &dbstruct.Hyperlink{ + Action: "app", + Url: fmt.Sprintf("StreamerSpace?mid=%d", mid), + }) + list = append(list, &dbstruct.Hyperlink{ + Action: "h5", + Url: fmt.Sprintf("space/%v", mid), + }) + return list +} diff --git a/dbstruct/activity_banner.go b/dbstruct/activity_banner.go index 36341e00..d8d715a9 100644 --- a/dbstruct/activity_banner.go +++ b/dbstruct/activity_banner.go @@ -12,7 +12,13 @@ type ActivityBanner struct { Ct *int64 `json:"ct" bson:"ct"` // 创建时间 Ut *int64 `json:"ut" bson:"ut"` // 更新时间 DelFlag *int64 `json:"del_flag" bson:"del_flag"` // 删除标记 +} +func (p *ActivityBanner) GetTitle() string { + if p == nil || p.Title == nil { + return "" + } + return *p.Title } func (p *ActivityBanner) GetSt() int64 { diff --git a/dbstruct/activity_hot.go b/dbstruct/activity_hot.go index 3a07382b..892cce33 100644 --- a/dbstruct/activity_hot.go +++ b/dbstruct/activity_hot.go @@ -23,6 +23,20 @@ func (p *ActivityHot) GetMid() int64 { return *p.Mid } +func (p *ActivityHot) GetTitle() string { + if p == nil || p.Title == nil { + return "" + } + return *p.Title +} + +func (p *ActivityHot) GetText() string { + if p == nil || p.Text == nil { + return "" + } + return *p.Text +} + func (p *ActivityHot) GetSt() int64 { if p == nil || p.St == nil { return -1