Files
quyun/backend_v1/app/services/posts.go
Rogee ef0bb03ea0
Some checks failed
build quyun / Build (push) Failing after 1m21s
fix: cors
2025-12-22 11:20:30 +08:00

235 lines
5.9 KiB
Go

package services
import (
"context"
"time"
"quyun/v2/app/http/dto"
"quyun/v2/app/requests"
"quyun/v2/database"
"quyun/v2/database/models"
"github.com/pkg/errors"
"github.com/samber/lo"
"go.ipao.vip/gen"
)
// @provider
type posts struct{}
// IncrViewCount
func (m *posts) IncrViewCount(ctx context.Context, postID int64) error {
tbl, query := models.PostQuery.QueryContext(ctx)
_, err := query.Where(tbl.ID.Eq(postID)).Inc(tbl.Views, 1)
if err != nil {
return errors.Wrapf(err, "failed to increment view count for post %d", postID)
}
return nil
}
// List
func (m *posts) List(ctx context.Context, filter *dto.PostListQuery) (*requests.Pager, error) {
filter.Pagination.Format()
// conds := []gen.Condition{}
tbl, query := models.PostQuery.QueryContext(ctx)
query = query.Order(tbl.ID.Desc())
if filter.Keyword != nil && *filter.Keyword != "" {
keyword := database.WrapLike(*filter.Keyword)
query = query.Where(tbl.Title.Like(keyword))
}
// filter status
if filter.Status != nil {
query = query.Where(tbl.Status.Eq(*filter.Status))
}
items, cnt, err := query.FindByPage(int(filter.Offset()), int(filter.Limit))
if err != nil {
return nil, errors.Wrap(err, "list post failed")
}
return &requests.Pager{
Items: items,
Total: cnt,
Pagination: *filter.Pagination,
}, nil
}
// SendTo
func (m *posts) SendTo(ctx context.Context, postID, userID int64) error {
model := &models.UserPost{
UserID: userID,
PostID: postID,
Price: -1,
}
return model.Create(ctx)
}
// PostBoughtStatistics 获取指定文件 ID 的购买次数
func (m *posts) BoughtStatistics(ctx context.Context, postIds []int64) (map[int64]int64, error) {
tbl, query := models.UserPostQuery.QueryContext(ctx)
var items []struct {
Count int64
PostID int64
}
err := query.Select(
tbl.UserID.Count().As("count"),
tbl.PostID,
).
Where(tbl.PostID.In(postIds...)).
Group(tbl.PostID).Scan(&items)
if err != nil {
return nil, err
}
result := make(map[int64]int64)
for _, item := range items {
result[item.PostID] = item.Count
}
return result, nil
}
func (m *posts) Bought(ctx context.Context, userId int64, pagination *requests.Pagination) (*requests.Pager, error) {
pagination.Format()
tblUserPost, queryUserPost := models.UserPostQuery.QueryContext(ctx)
cnt, err := queryUserPost.Where(tblUserPost.UserID.Eq(userId)).Count()
if err != nil {
return nil, err
}
tbl, query := models.PostQuery.QueryContext(ctx)
var retItems []struct {
models.Post
Price int64 `json:"price"`
BoughtAt time.Time `json:"bought_at"`
}
err = query.
Select(
tbl.ALL,
tblUserPost.Price.As("price"),
tblUserPost.CreatedAt.As("bought_at"),
).
RightJoin(tblUserPost, tbl.ID.EqCol(tblUserPost.PostID)).
Where(tblUserPost.UserID.Eq(userId)).
Order(tblUserPost.CreatedAt.Desc()).
Limit(int(pagination.Limit)).
Offset(int(pagination.Offset())).
Scan(&retItems)
if err != nil {
return nil, err
}
return &requests.Pager{
Items: retItems,
Total: cnt,
Pagination: *pagination,
}, nil
}
// Buyers 获取某个作品的购买人列表(管理端使用)
func (m *posts) Buyers(ctx context.Context, postID int64, pagination *requests.Pagination) (*requests.Pager, error) {
pagination.Format()
// 先分页查询购买记录,避免一次性拉取全量 user_posts 造成内存/延迟抖动
tblUserPost, queryUserPost := models.UserPostQuery.QueryContext(ctx)
queryUserPost = queryUserPost.
Where(tblUserPost.PostID.Eq(postID)).
Order(tblUserPost.CreatedAt.Desc())
userPosts, cnt, err := queryUserPost.FindByPage(int(pagination.Offset()), int(pagination.Limit))
if err != nil {
return nil, err
}
if len(userPosts) == 0 {
return &requests.Pager{
Items: []dto.PostBuyerItem{},
Total: cnt,
Pagination: *pagination,
}, nil
}
// 批量回表查询用户信息,避免 N+1
userIDs := lo.Uniq(lo.Map(userPosts, func(item *models.UserPost, _ int) int64 {
return item.UserID
}))
tblUser, queryUser := models.UserQuery.QueryContext(ctx)
users, err := queryUser.Where(tblUser.ID.In(userIDs...)).Find()
if err != nil {
return nil, err
}
userMap := lo.KeyBy(users, func(item *models.User) int64 { return item.ID })
items := make([]dto.PostBuyerItem, 0, len(userPosts))
for _, item := range userPosts {
user, ok := userMap[item.UserID]
if !ok {
continue
}
items = append(items, dto.PostBuyerItem{
UserID: user.ID,
Username: user.Username,
Avatar: user.Avatar,
Phone: user.Phone,
BoughtAt: item.CreatedAt,
Price: item.Price,
})
}
return &requests.Pager{
Items: items,
Total: cnt,
Pagination: *pagination,
}, nil
}
// GetPostsMapByIDs
func (m *posts) GetPostsMapByIDs(ctx context.Context, ids []int64) (map[int64]*models.Post, error) {
tbl, query := models.PostQuery.QueryContext(ctx)
posts, err := query.Where(tbl.ID.In(ids...)).Find()
if err != nil {
return nil, err
}
return lo.KeyBy(posts, func(item *models.Post) int64 { return item.ID }), nil
}
// GetMediaByIds
func (m *posts) GetMediasByIds(ctx context.Context, ids []int64) ([]*models.Medium, error) {
if len(ids) == 0 {
return nil, nil
}
tbl, query := models.MediumQuery.QueryContext(ctx)
return query.Where(tbl.ID.In(ids...)).Find()
}
// FindByID
func (m *posts) FindByID(ctx context.Context, id int64, conds ...gen.Condition) (*models.Post, error) {
tbl, query := models.PostQuery.QueryContext(ctx)
if len(conds) > 0 {
query = query.Where(conds...)
}
return query.Where(tbl.ID.Eq(id)).First()
}
// Count
func (m *posts) Count(ctx context.Context, conds ...gen.Condition) (int64, error) {
_, query := models.PostQuery.QueryContext(ctx)
if len(conds) > 0 {
query = query.Where(conds...)
}
return query.Count()
}
// FilterIdsByKeyword filter posts ids by title keywords
func (m *posts) FilterIdsByKeyword(ctx context.Context, keyword string) ([]int64, error) {
tbl, query := models.PostQuery.QueryContext(ctx)
return query.Select(tbl.ID).Where(tbl.Title.Like(database.WrapLike(keyword))).PluckIDs()
}