Files
backend_v2/internal/repository/history_repository.go
toom1996 1fd9c48f58 update
2026-09-02 21:51:35 +08:00

98 lines
3.4 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
}