init: web_hgdjc_server
This commit is contained in:
@@ -0,0 +1,371 @@
|
||||
package srv_playlet
|
||||
|
||||
import (
|
||||
"context"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
"videoweb/pub/common/srvdef"
|
||||
"videoweb/pub/common/utils"
|
||||
"videoweb/pub/models/playletchapterunlockmod"
|
||||
"videoweb/pub/models/playletcollectmod"
|
||||
"videoweb/pub/models/playletlikemod"
|
||||
"videoweb/pub/models/playletmod"
|
||||
"videoweb/pub/pubdef"
|
||||
"videoweb/web/api/proto"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
const playletSalesLimit = 50
|
||||
|
||||
type playletSalesAggRow struct {
|
||||
PlayletId int64 `bson:"_id"`
|
||||
Count int64 `bson:"count"`
|
||||
Coins int64 `bson:"coins"`
|
||||
}
|
||||
|
||||
type playletSalesRow struct {
|
||||
Id int64
|
||||
UnlockCount int64
|
||||
UnlockCoins int64
|
||||
LikeCount int64
|
||||
CollectCount int64
|
||||
}
|
||||
|
||||
// ListPlayletSales 按时间段汇总短剧金币解锁、点赞、收藏,最多返回 50 条。
|
||||
func ListPlayletSales(ctx context.Context, req *proto.PlayletSalesReq) (*proto.PlayletSalesResp, *srvdef.SrvErr) {
|
||||
if req == nil {
|
||||
return nil, srvdef.New(srvdef.ErrParamError, "", "")
|
||||
}
|
||||
start, end, serr := resolvePlayletSalesTimeRange(req.StartTime, req.EndTime)
|
||||
if serr != nil {
|
||||
return nil, serr
|
||||
}
|
||||
resp := &proto.PlayletSalesResp{
|
||||
StartTime: start.In(utils.LocBJ).Format(pubdef.TimeISO),
|
||||
EndTime: end.In(utils.LocBJ).Format(pubdef.TimeISO),
|
||||
List: make([]*proto.PlayletSalesItem, 0),
|
||||
}
|
||||
filterIDs, playlets, serr := loadPlayletSalesFilter(ctx, req)
|
||||
if serr != nil {
|
||||
return nil, serr
|
||||
}
|
||||
if filterIDs != nil && len(filterIDs) == 0 {
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
unlocks, serr := aggregatePlayletUnlocks(ctx, start, end, filterIDs)
|
||||
if serr != nil {
|
||||
return nil, serr
|
||||
}
|
||||
likes, serr := aggregatePlayletMetric(ctx, start, end, filterIDs, playletlikemod.FN.PlayletLike.CreatedAt, playletlikemod.FN.PlayletLike.PlayletId, playletlikemod.AggregatePlayletLike)
|
||||
if serr != nil {
|
||||
return nil, serr
|
||||
}
|
||||
collects, serr := aggregatePlayletMetric(ctx, start, end, filterIDs, playletcollectmod.FN.PlayletCollect.CreatedAt, playletcollectmod.FN.PlayletCollect.PlayletId, playletcollectmod.AggregatePlayletCollect)
|
||||
if serr != nil {
|
||||
return nil, serr
|
||||
}
|
||||
|
||||
rows := mergePlayletSalesRows(filterIDs, unlocks, likes, collects)
|
||||
sortPlayletSalesRows(rows, req.SortType)
|
||||
rows = takePlayletSalesRows(rows, playletSalesLimit)
|
||||
if len(rows) == 0 {
|
||||
return resp, nil
|
||||
}
|
||||
if playlets == nil {
|
||||
playlets, serr = loadPlayletsByIDs(ctx, playletSalesIDs(rows))
|
||||
if serr != nil {
|
||||
return nil, serr
|
||||
}
|
||||
}
|
||||
for _, row := range rows {
|
||||
item := &proto.PlayletSalesItem{
|
||||
Id: row.Id,
|
||||
UnlockCount: row.UnlockCount,
|
||||
UnlockCoins: row.UnlockCoins,
|
||||
LikeCount: row.LikeCount,
|
||||
CollectCount: row.CollectCount,
|
||||
}
|
||||
if playlet := playlets[row.Id]; playlet != nil {
|
||||
item.Title = playlet.Title
|
||||
item.PlayName = playlet.PlayName
|
||||
item.Cover = playlet.Cover
|
||||
item.Slug = playlet.Slug
|
||||
item.Status = playlet.Status
|
||||
}
|
||||
resp.List = append(resp.List, item)
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func resolvePlayletSalesTimeRange(startRaw, endRaw string) (time.Time, time.Time, *srvdef.SrvErr) {
|
||||
startRaw = strings.TrimSpace(startRaw)
|
||||
endRaw = strings.TrimSpace(endRaw)
|
||||
if startRaw == "" && endRaw == "" {
|
||||
day := time.Now().In(utils.LocBJ).AddDate(0, 0, -1)
|
||||
return utils.BeginOfTime(day), utils.EndOfTime(day), nil
|
||||
}
|
||||
if startRaw == "" || endRaw == "" {
|
||||
return time.Time{}, time.Time{}, srvdef.New(srvdef.ErrParamError, "开始时间和结束时间需同时传入", "开始时间和结束时间需同时传入")
|
||||
}
|
||||
return parsePlayletSalesTimeRange(startRaw, endRaw)
|
||||
}
|
||||
|
||||
func parsePlayletSalesTimeRange(startRaw, endRaw string) (time.Time, time.Time, *srvdef.SrvErr) {
|
||||
start, ok := parsePlayletSalesTime(startRaw, true)
|
||||
if !ok {
|
||||
return time.Time{}, time.Time{}, srvdef.New(srvdef.ErrParamError, "开始时间无效", "开始时间无效")
|
||||
}
|
||||
end, ok := parsePlayletSalesTime(endRaw, false)
|
||||
if !ok {
|
||||
return time.Time{}, time.Time{}, srvdef.New(srvdef.ErrParamError, "结束时间无效", "结束时间无效")
|
||||
}
|
||||
if end.Before(start) {
|
||||
return time.Time{}, time.Time{}, srvdef.New(srvdef.ErrParamError, "结束时间不能早于开始时间", "结束时间不能早于开始时间")
|
||||
}
|
||||
return start, end, nil
|
||||
}
|
||||
|
||||
func parsePlayletSalesTime(raw string, isStart bool) (time.Time, bool) {
|
||||
raw = strings.TrimSpace(raw)
|
||||
if raw == "" {
|
||||
return time.Time{}, false
|
||||
}
|
||||
layouts := []string{
|
||||
pubdef.TimeISO,
|
||||
time.RFC3339,
|
||||
time.RFC3339Nano,
|
||||
"2006-01-02 15:04:05 -0700 MST",
|
||||
"2006-01-02 15:04:05.999999999 -0700 MST",
|
||||
}
|
||||
for _, layout := range layouts {
|
||||
if t, err := time.ParseInLocation(layout, raw, utils.LocBJ); err == nil {
|
||||
return t, true
|
||||
}
|
||||
}
|
||||
if t, err := time.ParseInLocation(pubdef.DateISO, raw, utils.LocBJ); err == nil {
|
||||
if isStart {
|
||||
return utils.BeginOfTime(t), true
|
||||
}
|
||||
return utils.EndOfTime(t), true
|
||||
}
|
||||
return time.Time{}, false
|
||||
}
|
||||
|
||||
func loadPlayletSalesFilter(ctx context.Context, req *proto.PlayletSalesReq) ([]int64, map[int64]*playletmod.Playlet, *srvdef.SrvErr) {
|
||||
id := int64(0)
|
||||
if req.Id != nil {
|
||||
id = *req.Id
|
||||
}
|
||||
title := strings.TrimSpace(req.Title)
|
||||
if id <= 0 && title == "" {
|
||||
return nil, nil, nil
|
||||
}
|
||||
filter := bson.M{}
|
||||
if id > 0 {
|
||||
filter[playletmod.FN.Playlet.Id] = id
|
||||
}
|
||||
if title != "" {
|
||||
pattern := regexp.QuoteMeta(title)
|
||||
filter["$or"] = []bson.M{
|
||||
{playletmod.FN.Playlet.Title: bson.M{"$regex": pattern}},
|
||||
{playletmod.FN.Playlet.PlayName: bson.M{"$regex": pattern}},
|
||||
}
|
||||
}
|
||||
list, serr := playletmod.FindManyPlaylet(ctx, filter, options.Find().SetProjection(playletSalesProjection()))
|
||||
if serr != nil {
|
||||
return nil, nil, serr
|
||||
}
|
||||
ids := make([]int64, 0, len(list))
|
||||
playlets := make(map[int64]*playletmod.Playlet, len(list))
|
||||
for _, item := range list {
|
||||
if item == nil || item.Id <= 0 {
|
||||
continue
|
||||
}
|
||||
ids = append(ids, item.Id)
|
||||
playlets[item.Id] = item
|
||||
}
|
||||
return ids, playlets, nil
|
||||
}
|
||||
|
||||
func playletSalesProjection() bson.D {
|
||||
return bson.D{
|
||||
{Key: playletmod.FN.Playlet.Id, Value: 1},
|
||||
{Key: playletmod.FN.Playlet.Title, Value: 1},
|
||||
{Key: playletmod.FN.Playlet.PlayName, Value: 1},
|
||||
{Key: playletmod.FN.Playlet.Cover, Value: 1},
|
||||
{Key: playletmod.FN.Playlet.Slug, Value: 1},
|
||||
{Key: playletmod.FN.Playlet.Status, Value: 1},
|
||||
}
|
||||
}
|
||||
|
||||
func loadPlayletsByIDs(ctx context.Context, ids []int64) (map[int64]*playletmod.Playlet, *srvdef.SrvErr) {
|
||||
out := make(map[int64]*playletmod.Playlet, len(ids))
|
||||
if len(ids) == 0 {
|
||||
return out, nil
|
||||
}
|
||||
list, serr := playletmod.FindManyPlaylet(ctx, bson.M{
|
||||
playletmod.FN.Playlet.Id: bson.M{"$in": ids},
|
||||
}, options.Find().SetProjection(playletSalesProjection()))
|
||||
if serr != nil {
|
||||
return nil, serr
|
||||
}
|
||||
for _, item := range list {
|
||||
if item == nil || item.Id <= 0 {
|
||||
continue
|
||||
}
|
||||
out[item.Id] = item
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func aggregatePlayletUnlocks(ctx context.Context, start, end time.Time, playletIDs []int64) (map[int64]playletSalesAggRow, *srvdef.SrvErr) {
|
||||
match := bson.M{
|
||||
playletchapterunlockmod.FN.PlayletChapterUnlock.CreatedAt: bson.M{
|
||||
"$gte": start,
|
||||
"$lte": end,
|
||||
},
|
||||
}
|
||||
if len(playletIDs) > 0 {
|
||||
match[playletchapterunlockmod.FN.PlayletChapterUnlock.PlayletId] = bson.M{"$in": playletIDs}
|
||||
}
|
||||
pipeline := []bson.M{
|
||||
{"$match": match},
|
||||
{"$group": bson.M{
|
||||
"_id": "$" + playletchapterunlockmod.FN.PlayletChapterUnlock.PlayletId,
|
||||
"count": bson.M{"$sum": 1},
|
||||
"coins": bson.M{"$sum": "$" + playletchapterunlockmod.FN.PlayletChapterUnlock.Coins},
|
||||
}},
|
||||
}
|
||||
rows := make([]playletSalesAggRow, 0)
|
||||
if serr := playletchapterunlockmod.AggregatePlayletChapterUnlock(ctx, &rows, pipeline); serr != nil {
|
||||
return nil, serr
|
||||
}
|
||||
return playletSalesAggMap(rows), nil
|
||||
}
|
||||
|
||||
func aggregatePlayletMetric(ctx context.Context, start, end time.Time, playletIDs []int64, createdAtField, playletIDField string, agg func(context.Context, interface{}, []bson.M) *srvdef.SrvErr) (map[int64]playletSalesAggRow, *srvdef.SrvErr) {
|
||||
match := bson.M{
|
||||
createdAtField: bson.M{
|
||||
"$gte": start,
|
||||
"$lte": end,
|
||||
},
|
||||
}
|
||||
if len(playletIDs) > 0 {
|
||||
match[playletIDField] = bson.M{"$in": playletIDs}
|
||||
}
|
||||
pipeline := []bson.M{
|
||||
{"$match": match},
|
||||
{"$group": bson.M{
|
||||
"_id": "$" + playletIDField,
|
||||
"count": bson.M{"$sum": 1},
|
||||
}},
|
||||
}
|
||||
rows := make([]playletSalesAggRow, 0)
|
||||
if serr := agg(ctx, &rows, pipeline); serr != nil {
|
||||
return nil, serr
|
||||
}
|
||||
return playletSalesAggMap(rows), nil
|
||||
}
|
||||
|
||||
func playletSalesAggMap(rows []playletSalesAggRow) map[int64]playletSalesAggRow {
|
||||
out := make(map[int64]playletSalesAggRow, len(rows))
|
||||
for _, row := range rows {
|
||||
if row.PlayletId <= 0 {
|
||||
continue
|
||||
}
|
||||
out[row.PlayletId] = row
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func mergePlayletSalesRows(filterIDs []int64, unlocks, likes, collects map[int64]playletSalesAggRow) []playletSalesRow {
|
||||
seen := make(map[int64]struct{})
|
||||
if filterIDs != nil {
|
||||
for _, id := range filterIDs {
|
||||
if id > 0 {
|
||||
seen[id] = struct{}{}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for id := range unlocks {
|
||||
seen[id] = struct{}{}
|
||||
}
|
||||
for id := range likes {
|
||||
seen[id] = struct{}{}
|
||||
}
|
||||
for id := range collects {
|
||||
seen[id] = struct{}{}
|
||||
}
|
||||
}
|
||||
rows := make([]playletSalesRow, 0, len(seen))
|
||||
for id := range seen {
|
||||
row := playletSalesRow{Id: id}
|
||||
if item, ok := unlocks[id]; ok {
|
||||
row.UnlockCount = item.Count
|
||||
row.UnlockCoins = item.Coins
|
||||
}
|
||||
if item, ok := likes[id]; ok {
|
||||
row.LikeCount = item.Count
|
||||
}
|
||||
if item, ok := collects[id]; ok {
|
||||
row.CollectCount = item.Count
|
||||
}
|
||||
rows = append(rows, row)
|
||||
}
|
||||
return rows
|
||||
}
|
||||
|
||||
func sortPlayletSalesRows(rows []playletSalesRow, sortType proto.PlayletSalesSortType) {
|
||||
sort.SliceStable(rows, func(i, j int) bool {
|
||||
left, right := rows[i], rows[j]
|
||||
switch sortType {
|
||||
case proto.PlayletSalesSortLike:
|
||||
if left.LikeCount != right.LikeCount {
|
||||
return left.LikeCount > right.LikeCount
|
||||
}
|
||||
case proto.PlayletSalesSortCollect:
|
||||
if left.CollectCount != right.CollectCount {
|
||||
return left.CollectCount > right.CollectCount
|
||||
}
|
||||
default:
|
||||
if left.UnlockCount != right.UnlockCount {
|
||||
return left.UnlockCount > right.UnlockCount
|
||||
}
|
||||
if left.UnlockCoins != right.UnlockCoins {
|
||||
return left.UnlockCoins > right.UnlockCoins
|
||||
}
|
||||
}
|
||||
if left.UnlockCount != right.UnlockCount {
|
||||
return left.UnlockCount > right.UnlockCount
|
||||
}
|
||||
if left.LikeCount != right.LikeCount {
|
||||
return left.LikeCount > right.LikeCount
|
||||
}
|
||||
if left.CollectCount != right.CollectCount {
|
||||
return left.CollectCount > right.CollectCount
|
||||
}
|
||||
return left.Id > right.Id
|
||||
})
|
||||
}
|
||||
|
||||
func takePlayletSalesRows(rows []playletSalesRow, limit int) []playletSalesRow {
|
||||
if limit <= 0 || len(rows) <= limit {
|
||||
return rows
|
||||
}
|
||||
return rows[:limit]
|
||||
}
|
||||
|
||||
func playletSalesIDs(rows []playletSalesRow) []int64 {
|
||||
ids := make([]int64, 0, len(rows))
|
||||
for _, row := range rows {
|
||||
if row.Id > 0 {
|
||||
ids = append(ids, row.Id)
|
||||
}
|
||||
}
|
||||
return ids
|
||||
}
|
||||
Reference in New Issue
Block a user