From 8c8e712cbbb377594a85d43f93cf92f7312e4c1d Mon Sep 17 00:00:00 2001 From: Rogee Date: Thu, 8 Jan 2026 11:05:37 +0800 Subject: [PATCH] feat: map super content list --- backend/app/services/super.go | 154 ++++++++++++++++++++++++++++++++-- 1 file changed, 148 insertions(+), 6 deletions(-) diff --git a/backend/app/services/super.go b/backend/app/services/super.go index cccf8f3..dad4528 100644 --- a/backend/app/services/super.go +++ b/backend/app/services/super.go @@ -7,7 +7,7 @@ import ( "quyun/v2/app/errorx" super_dto "quyun/v2/app/http/super/v1/dto" - creator_dto "quyun/v2/app/http/v1/dto" + v1_dto "quyun/v2/app/http/v1/dto" "quyun/v2/app/requests" "quyun/v2/database/models" "quyun/v2/pkg/consts" @@ -279,14 +279,24 @@ func (s *super) ListContents(ctx context.Context, filter *super_dto.SuperContent if err != nil { return nil, errorx.ErrDatabaseError.WithCause(err) } - list, err := q.Offset(int(filter.Pagination.Offset())).Limit(int(filter.Pagination.Limit)).Order(tbl.ID.Desc()).Find() + var list []*models.Content + err = q.Offset(int(filter.Pagination.Offset())).Limit(int(filter.Pagination.Limit)).Order(tbl.ID.Desc()). + UnderlyingDB(). + Preload("Author"). + Preload("ContentAssets.Asset"). + Find(&list).Error if err != nil { return nil, errorx.ErrDatabaseError.WithCause(err) } - // Simplified DTO for list - var data []any + + priceMap, err := s.contentPriceMap(ctx, list) + if err != nil { + return nil, err + } + + data := make([]super_dto.AdminContentItem, 0, len(list)) for _, c := range list { - data = append(data, c) // TODO: Map to DTO + data = append(data, s.toSuperContentItem(c, priceMap[c.ID])) } return &requests.Pager{ Pagination: filter.Pagination, @@ -382,7 +392,7 @@ func (s *super) RefundOrder(ctx context.Context, id int64, form *super_dto.Super return errorx.ErrRecordNotFound.WithMsg("租户不存在") } - return Creator.ProcessRefund(ctx, t.UserID, id, &creator_dto.RefundForm{ + return Creator.ProcessRefund(ctx, t.UserID, id, &v1_dto.RefundForm{ Action: "accept", Reason: form.Reason, }) @@ -580,6 +590,138 @@ func (s *super) toSuperOrderItem(o *models.Order, tenant *models.Tenant, buyer * return item } +func (s *super) contentPriceMap(ctx context.Context, list []*models.Content) (map[int64]*models.ContentPrice, error) { + if len(list) == 0 { + return map[int64]*models.ContentPrice{}, nil + } + + ids := make([]int64, 0, len(list)) + for _, item := range list { + ids = append(ids, item.ID) + } + + tbl, q := models.ContentPriceQuery.QueryContext(ctx) + prices, err := q.Where(tbl.ContentID.In(ids...)).Find() + if err != nil { + return nil, errorx.ErrDatabaseError.WithCause(err) + } + + priceMap := make(map[int64]*models.ContentPrice, len(prices)) + for _, price := range prices { + priceMap[price.ContentID] = price + } + return priceMap, nil +} + +func (s *super) toSuperContentItem(item *models.Content, price *models.ContentPrice) super_dto.AdminContentItem { + return super_dto.AdminContentItem{ + Content: s.toSuperContentDTO(item, price), + Owner: s.toSuperContentOwner(item.Author), + Price: s.toSuperContentPrice(price), + StatusDescription: item.Status.Description(), + VisibilityDescription: item.Visibility.Description(), + } +} + +func (s *super) toSuperContentOwner(author *models.User) *super_dto.AdminContentOwnerLite { + if author == nil { + return nil + } + return &super_dto.AdminContentOwnerLite{ + ID: author.ID, + Username: author.Username, + Roles: author.Roles, + Status: author.Status, + } +} + +func (s *super) toSuperContentDTO(item *models.Content, price *models.ContentPrice) *v1_dto.ContentItem { + dto := &v1_dto.ContentItem{ + ID: item.ID, + Title: item.Title, + Genre: item.Genre, + AuthorID: item.UserID, + Views: int(item.Views), + Likes: int(item.Likes), + CreatedAt: item.CreatedAt.Format("2006-01-02"), + IsPurchased: false, + } + if price != nil { + dto.Price = float64(price.PriceAmount) / 100.0 + } + if item.Author != nil { + dto.AuthorName = item.Author.Nickname + if dto.AuthorName == "" { + dto.AuthorName = item.Author.Username + } + dto.AuthorAvatar = item.Author.Avatar + } + + var hasVideo, hasAudio bool + for _, asset := range item.ContentAssets { + if asset.Asset == nil { + continue + } + if asset.Role == consts.ContentAssetRoleCover { + dto.Cover = Common.GetAssetURL(asset.Asset.ObjectKey) + } + switch asset.Asset.Type { + case consts.MediaAssetTypeVideo: + hasVideo = true + case consts.MediaAssetTypeAudio: + hasAudio = true + } + } + + if dto.Cover == "" && len(item.ContentAssets) > 0 { + for _, asset := range item.ContentAssets { + if asset.Asset != nil && asset.Asset.Type == consts.MediaAssetTypeImage { + dto.Cover = Common.GetAssetURL(asset.Asset.ObjectKey) + break + } + } + } + + if hasVideo { + dto.Type = "video" + } else if hasAudio { + dto.Type = "audio" + } else { + dto.Type = "article" + } + + return dto +} + +func (s *super) toSuperContentPrice(price *models.ContentPrice) *v1_dto.ContentPrice { + if price == nil { + return nil + } + dto := &v1_dto.ContentPrice{ + Currency: string(price.Currency), + PriceAmount: float64(price.PriceAmount) / 100.0, + DiscountType: string(price.DiscountType), + DiscountValue: s.toSuperDiscountValue(price), + } + if !price.DiscountStartAt.IsZero() { + dto.DiscountStartAt = price.DiscountStartAt.Format(time.RFC3339) + } + if !price.DiscountEndAt.IsZero() { + dto.DiscountEndAt = price.DiscountEndAt.Format(time.RFC3339) + } + return dto +} + +func (s *super) toSuperDiscountValue(price *models.ContentPrice) float64 { + if price == nil { + return 0 + } + if price.DiscountType == consts.DiscountTypeAmount { + return float64(price.DiscountValue) / 100.0 + } + return float64(price.DiscountValue) +} + func (s *super) ListWithdrawals(ctx context.Context, filter *super_dto.SuperOrderListFilter) (*requests.Pager, error) { tbl, q := models.OrderQuery.QueryContext(ctx) q = q.Where(tbl.Type.Eq(consts.OrderTypeWithdrawal))