135 lines
3.9 KiB
Go
135 lines
3.9 KiB
Go
package service
|
||
|
||
import (
|
||
"context"
|
||
"errors"
|
||
"net/http"
|
||
|
||
"fashionapi/internal/dto"
|
||
"fashionapi/internal/model"
|
||
"fashionapi/internal/pkg/hashid"
|
||
"fashionapi/internal/repository"
|
||
)
|
||
|
||
// StreetSnapService 街拍业务接口。
|
||
type StreetSnapService interface {
|
||
List(ctx context.Context, q dto.StreetSnapQuery) ([]dto.PublicStreetSnap, int64, error)
|
||
Detail(ctx context.Context, id string) (*dto.PublicStreetSnapDetail, error)
|
||
// Popular 返回热度前 N 个街拍(按图片数降序),供 SSG 首页热门街拍展示。
|
||
Popular(ctx context.Context, limit int) ([]dto.PublicStreetSnap, error)
|
||
}
|
||
|
||
type streetSnapService struct {
|
||
repo repository.StreetSnapRepository
|
||
}
|
||
|
||
// NewStreetSnapService 创建街拍服务。
|
||
func NewStreetSnapService(repo repository.StreetSnapRepository) StreetSnapService {
|
||
return &streetSnapService{repo: repo}
|
||
}
|
||
|
||
// ErrStreetSnapNotFound 街拍不存在。
|
||
var ErrStreetSnapNotFound = NewError(http.StatusNotFound, "street snap not found")
|
||
|
||
// List 查询街拍列表,并按需附带每条的前 N 张图。
|
||
func (s *streetSnapService) List(ctx context.Context, q dto.StreetSnapQuery) ([]dto.PublicStreetSnap, int64, error) {
|
||
q.Normalize()
|
||
|
||
rows, total, err := s.repo.List(ctx, q)
|
||
if err != nil {
|
||
return nil, 0, internalErr(err.Error())
|
||
}
|
||
|
||
list := make([]dto.PublicStreetSnap, 0, len(rows))
|
||
ids := make([]uint32, 0, len(rows))
|
||
for _, r := range rows {
|
||
ids = append(ids, r.ID)
|
||
list = append(list, dto.PublicStreetSnap{
|
||
UID: hashid.Encode(r.ID),
|
||
Title: r.Title,
|
||
Cover: r.Cover,
|
||
ImageCount: r.ImageCount,
|
||
})
|
||
}
|
||
|
||
if q.Images > 0 && len(list) > 0 {
|
||
if err := s.attachImages(ctx, list, ids, q.Images); err != nil {
|
||
return nil, 0, internalErr(err.Error())
|
||
}
|
||
}
|
||
return list, total, nil
|
||
}
|
||
|
||
// attachImages 用单次 IN 查询为列表批量补齐图片,避免前端逐条请求详情。
|
||
func (s *streetSnapService) attachImages(ctx context.Context, list []dto.PublicStreetSnap, ids []uint32, limit int) error {
|
||
grouped, err := s.repo.ImagesBySnapIDs(ctx, ids)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
for i := range list {
|
||
imgs, ok := grouped[ids[i]]
|
||
if !ok {
|
||
continue
|
||
}
|
||
if len(imgs) > limit {
|
||
imgs = imgs[:limit]
|
||
}
|
||
list[i].Images = toPublicSnapImages(imgs)
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// Detail 查询街拍详情,含完整图片集。
|
||
func (s *streetSnapService) Detail(ctx context.Context, id string) (*dto.PublicStreetSnapDetail, error) {
|
||
snap, err := s.repo.FindByID(ctx, id)
|
||
if err != nil {
|
||
if errors.Is(err, repository.ErrNotFound) {
|
||
return nil, ErrStreetSnapNotFound
|
||
}
|
||
return nil, internalErr(err.Error())
|
||
}
|
||
|
||
imgs, err := s.repo.ListImages(ctx, id)
|
||
if err != nil {
|
||
return nil, internalErr(err.Error())
|
||
}
|
||
|
||
return &dto.PublicStreetSnapDetail{
|
||
UID: hashid.Encode(snap.ID),
|
||
Title: snap.Title,
|
||
Cover: snap.Cover,
|
||
Images: toPublicSnapImages(imgs),
|
||
}, nil
|
||
}
|
||
|
||
// IDs 返回全部街拍的编码 uid(供 SSG 枚举详情页路径使用,对外暴露的即编码串)。
|
||
// Popular 返回热度前 N 个街拍(按图片数降序),供 SSG 首页热门街拍展示。
|
||
func (s *streetSnapService) Popular(ctx context.Context, limit int) ([]dto.PublicStreetSnap, error) {
|
||
rows, err := s.repo.Popular(ctx, limit)
|
||
if err != nil {
|
||
return nil, internalErr(err.Error())
|
||
}
|
||
list := make([]dto.PublicStreetSnap, 0, len(rows))
|
||
for _, r := range rows {
|
||
list = append(list, dto.PublicStreetSnap{
|
||
UID: hashid.Encode(r.ID),
|
||
Title: r.Title,
|
||
Cover: r.Cover,
|
||
ImageCount: r.ImageCount,
|
||
})
|
||
}
|
||
return list, nil
|
||
}
|
||
|
||
// toPublicSnapImages 把图片实体映射为对外结构(只保留 url 与名称)。
|
||
func toPublicSnapImages(imgs []model.StreetSnapImage) []dto.PublicArticleImage {
|
||
if len(imgs) == 0 {
|
||
return nil
|
||
}
|
||
out := make([]dto.PublicArticleImage, 0, len(imgs))
|
||
for _, im := range imgs {
|
||
out = append(out, dto.PublicArticleImage{Image: im.Image, Name: im.Name})
|
||
}
|
||
return out
|
||
}
|