146 lines
4.2 KiB
Go
146 lines
4.2 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"time"
|
|
|
|
"fashionapi/internal/dto"
|
|
"fashionapi/internal/model"
|
|
"fashionapi/internal/repository"
|
|
)
|
|
|
|
// FavoriteService 收藏业务接口。所有方法均要求调用方已通过中间件解析出 userID。
|
|
type FavoriteService interface {
|
|
// ListPaged 分页取当前用户收藏(服务端分页,与收藏总量解耦)。
|
|
// kind 传空表示不限粒度;传 gallery / image 只取对应粒度。
|
|
ListPaged(ctx context.Context, userID uint32, page, perPage int, kind string) ([]dto.FavoriteItem, int64, error)
|
|
// Add 新增收藏(幂等)。
|
|
Add(ctx context.Context, userID uint32, req dto.FavoriteAdd) (*dto.FavoriteItem, error)
|
|
// Remove 删除收藏。
|
|
Remove(ctx context.Context, userID uint32, targetUID string) error
|
|
// Check 返回给定 ids 中已收藏的子集(列表页打勾用)。
|
|
Check(ctx context.Context, userID uint32, ids []string) ([]string, error)
|
|
}
|
|
|
|
type favoriteService struct {
|
|
favs repository.FavoriteRepository
|
|
}
|
|
|
|
// NewFavoriteService 创建收藏服务。
|
|
func NewFavoriteService(favs repository.FavoriteRepository) FavoriteService {
|
|
return &favoriteService{favs: favs}
|
|
}
|
|
|
|
func (s *favoriteService) ListPaged(ctx context.Context, userID uint32, page, perPage int, kind string) ([]dto.FavoriteItem, int64, error) {
|
|
rows, err := s.favs.ListPaged(ctx, userID, page, perPage, kind)
|
|
if err != nil {
|
|
return nil, 0, internalErr(err.Error())
|
|
}
|
|
total, err := s.favs.CountByUser(ctx, userID, kind)
|
|
if err != nil {
|
|
return nil, 0, internalErr(err.Error())
|
|
}
|
|
out := make([]dto.FavoriteItem, 0, len(rows))
|
|
for _, r := range rows {
|
|
out = append(out, toFavoriteItem(r))
|
|
}
|
|
return out, total, nil
|
|
}
|
|
|
|
func (s *favoriteService) Add(ctx context.Context, userID uint32, req dto.FavoriteAdd) (*dto.FavoriteItem, error) {
|
|
if !validTargetType(req.TargetType) {
|
|
return nil, badRequest("invalid target_type")
|
|
}
|
|
if req.TargetUID == "" {
|
|
return nil, badRequest("target_uid required")
|
|
}
|
|
|
|
// 粒度:请求未显式指定时按 target_type 推断(*_image 即图片收藏)。
|
|
kind := req.Kind
|
|
if kind == "" {
|
|
kind = inferKind(req.TargetType)
|
|
}
|
|
if kind != dto.FavoriteKindGallery && kind != dto.FavoriteKindImage {
|
|
return nil, badRequest("invalid kind")
|
|
}
|
|
// 图片收藏必须带上图片地址,否则个人中心无从渲染。
|
|
if kind == dto.FavoriteKindImage && req.ImageURL == "" {
|
|
return nil, badRequest("image_url required for image favorite")
|
|
}
|
|
|
|
row := &model.Favorite{
|
|
UserID: userID,
|
|
TargetType: req.TargetType,
|
|
TargetUID: req.TargetUID,
|
|
Title: req.Title,
|
|
Cover: req.Cover,
|
|
Brand: req.Brand,
|
|
Kind: kind,
|
|
ImageURL: req.ImageURL,
|
|
ParentUID: req.ParentUID,
|
|
CreatedAt: uint32(time.Now().Unix()),
|
|
}
|
|
if err := s.favs.Add(ctx, row); err != nil {
|
|
return nil, internalErr(err.Error())
|
|
}
|
|
item := toFavoriteItem(*row)
|
|
return &item, nil
|
|
}
|
|
|
|
func (s *favoriteService) Remove(ctx context.Context, userID uint32, targetUID string) error {
|
|
if targetUID == "" {
|
|
return badRequest("target_uid required")
|
|
}
|
|
if err := s.favs.Remove(ctx, userID, targetUID); err != nil {
|
|
return internalErr(err.Error())
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *favoriteService) Check(ctx context.Context, userID uint32, ids []string) ([]string, error) {
|
|
if len(ids) == 0 {
|
|
return []string{}, nil
|
|
}
|
|
got, err := s.favs.FilterExisting(ctx, userID, ids)
|
|
if err != nil {
|
|
return nil, internalErr(err.Error())
|
|
}
|
|
return got, nil
|
|
}
|
|
|
|
// validTargetType 允许的收藏目标类型:图集两种 + 单图两种。
|
|
func validTargetType(t string) bool {
|
|
switch t {
|
|
case "runway", "street", "runway_image", "snap_image":
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// inferKind 由 target_type 推断收藏粒度。
|
|
func inferKind(targetType string) string {
|
|
if strings.HasSuffix(targetType, "_image") {
|
|
return dto.FavoriteKindImage
|
|
}
|
|
return dto.FavoriteKindGallery
|
|
}
|
|
|
|
func toFavoriteItem(r model.Favorite) dto.FavoriteItem {
|
|
kind := r.Kind
|
|
if kind == "" {
|
|
kind = dto.FavoriteKindGallery // 历史数据无 kind 列值,一律视为图集
|
|
}
|
|
return dto.FavoriteItem{
|
|
ID: r.TargetUID,
|
|
Type: r.TargetType,
|
|
Title: r.Title,
|
|
Cover: r.Cover,
|
|
Brand: r.Brand,
|
|
AddedAt: r.CreatedAt,
|
|
Kind: kind,
|
|
ImageURL: r.ImageURL,
|
|
ParentUID: r.ParentUID,
|
|
}
|
|
}
|