update
This commit is contained in:
84
internal/repository/favorite_repository.go
Normal file
84
internal/repository/favorite_repository.go
Normal file
@ -0,0 +1,84 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"fashionapi/internal/model"
|
||||
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/clause"
|
||||
)
|
||||
|
||||
// FavoriteRepository 收藏数据访问接口。
|
||||
type FavoriteRepository interface {
|
||||
// ListPaged 分页取某用户收藏,按收藏时间倒序。
|
||||
ListPaged(ctx context.Context, userID uint32, page, perPage int) ([]model.Favorite, error)
|
||||
// CountByUser 取某用户收藏总数(分页用)。
|
||||
CountByUser(ctx context.Context, userID uint32) (int64, error)
|
||||
// Add 新增收藏;同用户同 target 已存在时幂等忽略(ON DUPLICATE KEY DO NOTHING)。
|
||||
Add(ctx context.Context, f *model.Favorite) error
|
||||
// Remove 删除某用户对某个 target 的收藏。
|
||||
Remove(ctx context.Context, userID uint32, targetUID string) error
|
||||
// FilterExisting 返回在给定 ids 中、且属于该用户已收藏的 target_uid 子集(列表页打勾用)。
|
||||
FilterExisting(ctx context.Context, userID uint32, ids []string) ([]string, error)
|
||||
}
|
||||
|
||||
type favoriteRepository struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewFavoriteRepository 创建收藏仓储。
|
||||
func NewFavoriteRepository(db *gorm.DB) FavoriteRepository {
|
||||
return &favoriteRepository{db: db}
|
||||
}
|
||||
|
||||
func (r *favoriteRepository) ListPaged(ctx context.Context, userID uint32, page, perPage int) ([]model.Favorite, error) {
|
||||
var rows []model.Favorite
|
||||
offset := (page - 1) * perPage
|
||||
if offset < 0 {
|
||||
offset = 0
|
||||
}
|
||||
err := r.db.WithContext(ctx).
|
||||
Where("user_id = ?", userID).
|
||||
Order("created_at DESC").
|
||||
Offset(offset).
|
||||
Limit(perPage).
|
||||
Find(&rows).Error
|
||||
return rows, err
|
||||
}
|
||||
|
||||
func (r *favoriteRepository) CountByUser(ctx context.Context, userID uint32) (int64, error) {
|
||||
var n int64
|
||||
err := r.db.WithContext(ctx).
|
||||
Model(&model.Favorite{}).
|
||||
Where("user_id = ?", userID).
|
||||
Count(&n).Error
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (r *favoriteRepository) Add(ctx context.Context, f *model.Favorite) error {
|
||||
// 复合唯一索引 (user_id, target_uid) 保证不重复;冲突时忽略而非报错,使接口天然幂等。
|
||||
return r.db.WithContext(ctx).
|
||||
Clauses(clause.OnConflict{
|
||||
Columns: []clause.Column{{Name: "user_id"}, {Name: "target_uid"}},
|
||||
DoNothing: true,
|
||||
}).Create(f).Error
|
||||
}
|
||||
|
||||
func (r *favoriteRepository) Remove(ctx context.Context, userID uint32, targetUID string) error {
|
||||
return r.db.WithContext(ctx).
|
||||
Where("user_id = ? AND target_uid = ?", userID, targetUID).
|
||||
Delete(&model.Favorite{}).Error
|
||||
}
|
||||
|
||||
func (r *favoriteRepository) FilterExisting(ctx context.Context, userID uint32, ids []string) ([]string, error) {
|
||||
if len(ids) == 0 {
|
||||
return []string{}, nil
|
||||
}
|
||||
var uids []string
|
||||
err := r.db.WithContext(ctx).
|
||||
Model(&model.Favorite{}).
|
||||
Where("user_id = ? AND target_uid IN ?", userID, ids).
|
||||
Pluck("target_uid", &uids).Error
|
||||
return uids, err
|
||||
}
|
||||
97
internal/repository/history_repository.go
Normal file
97
internal/repository/history_repository.go
Normal file
@ -0,0 +1,97 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"fashionapi/internal/model"
|
||||
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/clause"
|
||||
)
|
||||
|
||||
// 每个用户浏览历史硬上限:超出后 FIFO 删除最旧条目,避免重度用户把表撑爆。
|
||||
const historyCap = 2000
|
||||
|
||||
// HistoryRepository 浏览历史数据访问接口。
|
||||
type HistoryRepository interface {
|
||||
// Upsert 记录一次浏览:同一 (user_id, target_uid)(一篇文章)已存在时只刷新 viewed_at,
|
||||
// 不新增行;插入/刷新后若超出用户上限,按 viewed_at 升序 FIFO 删最旧。
|
||||
Upsert(ctx context.Context, h *model.History) error
|
||||
// ListPaged 分页取某用户历史,按 viewed_at 倒序。
|
||||
ListPaged(ctx context.Context, userID uint32, page, perPage int) ([]model.History, error)
|
||||
// CountByUser 取某用户历史总数(分页用)。
|
||||
CountByUser(ctx context.Context, userID uint32) (int64, error)
|
||||
// Remove 删除某用户对某篇文章(target_uid)的历史。
|
||||
Remove(ctx context.Context, userID uint32, targetUID string) error
|
||||
// Clear 清空某用户全部历史。
|
||||
Clear(ctx context.Context, userID uint32) error
|
||||
}
|
||||
|
||||
type historyRepository struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewHistoryRepository 创建浏览历史仓储。
|
||||
func NewHistoryRepository(db *gorm.DB) HistoryRepository {
|
||||
return &historyRepository{db: db}
|
||||
}
|
||||
|
||||
func (r *historyRepository) Upsert(ctx context.Context, h *model.History) error {
|
||||
// 复合唯一索引 (user_id, target_uid) 保证同一篇文章不重复;冲突时刷新 viewed_at,
|
||||
// 反复打开同一篇文章只更新最后浏览时间,行数不增长。
|
||||
err := r.db.WithContext(ctx).
|
||||
Clauses(clause.OnConflict{
|
||||
Columns: []clause.Column{{Name: "user_id"}, {Name: "target_uid"}},
|
||||
DoUpdates: clause.AssignmentColumns([]string{"viewed_at"}),
|
||||
}).Create(h).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// 超出用户上限则 FIFO 裁剪最旧若干(子查询双嵌套规避 MySQL「同表不可直接子查删除」限制)。
|
||||
var n int64
|
||||
if err := r.db.WithContext(ctx).Model(&model.History{}).Where("user_id = ?", h.UserID).Count(&n).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if n > historyCap {
|
||||
excess := n - historyCap
|
||||
if err := r.db.WithContext(ctx).Exec(
|
||||
"DELETE FROM histories WHERE user_id = ? AND id IN (SELECT id FROM (SELECT id FROM histories WHERE user_id = ? ORDER BY viewed_at ASC LIMIT ?) AS t)",
|
||||
h.UserID, h.UserID, excess,
|
||||
).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *historyRepository) ListPaged(ctx context.Context, userID uint32, page, perPage int) ([]model.History, error) {
|
||||
var rows []model.History
|
||||
offset := (page - 1) * perPage
|
||||
if offset < 0 {
|
||||
offset = 0
|
||||
}
|
||||
err := r.db.WithContext(ctx).
|
||||
Where("user_id = ?", userID).
|
||||
Order("viewed_at DESC").
|
||||
Offset(offset).Limit(perPage).
|
||||
Find(&rows).Error
|
||||
return rows, err
|
||||
}
|
||||
|
||||
func (r *historyRepository) CountByUser(ctx context.Context, userID uint32) (int64, error) {
|
||||
var n int64
|
||||
err := r.db.WithContext(ctx).Model(&model.History{}).Where("user_id = ?", userID).Count(&n).Error
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (r *historyRepository) Remove(ctx context.Context, userID uint32, targetUID string) error {
|
||||
return r.db.WithContext(ctx).
|
||||
Where("user_id = ? AND target_uid = ?", userID, targetUID).
|
||||
Delete(&model.History{}).Error
|
||||
}
|
||||
|
||||
func (r *historyRepository) Clear(ctx context.Context, userID uint32) error {
|
||||
return r.db.WithContext(ctx).
|
||||
Where("user_id = ?", userID).
|
||||
Delete(&model.History{}).Error
|
||||
}
|
||||
Reference in New Issue
Block a user