Files
nl-pms-api/internal/service/avatar_history.go
2026-08-15 17:04:47 +08:00

108 lines
3.4 KiB
Go

package service
import (
"strings"
"gorm.io/gorm"
"nl-pms-api/internal/commonservice"
"nl-pms-api/internal/model"
)
const avatarHistoryLimit = 12
// AvatarHistoryService 用户头像历史(与账号关联,多端可回选)。
type AvatarHistoryService struct {
DB *gorm.DB
}
// AvatarHistoryItem 对外一条历史。
type AvatarHistoryItem struct {
ID int64 `json:"id"`
Mode string `json:"mode"`
Value string `json:"value"`
CreatedAt string `json:"createdAt"`
}
// List 按时间倒序返回本人历史(最多 avatarHistoryLimit 条)。
func (s *AvatarHistoryService) List(userID int64) ([]AvatarHistoryItem, error) {
var rows []model.UserAvatarHistory
if err := s.DB.Where("user_id = ?", userID).Order("id DESC").Limit(avatarHistoryLimit).Find(&rows).Error; err != nil {
return nil, commonservice.Internal("QUERY_FAILED")
}
out := make([]AvatarHistoryItem, 0, len(rows))
for _, r := range rows {
out = append(out, AvatarHistoryItem{ID: r.ID, Mode: r.Mode, Value: r.Value, CreatedAt: r.CreatedAt})
}
return out, nil
}
// Push 追加一条;同 value 去重后置顶;超出上限删最旧。
func (s *AvatarHistoryService) Push(userID int64, mode, value string) ([]AvatarHistoryItem, error) {
value = strings.TrimSpace(value)
if value == "" {
return nil, commonservice.BadRequest("AVATAR_VALUE_REQUIRED")
}
mode = strings.TrimSpace(strings.ToLower(mode))
if mode != "url" && mode != "base64" && mode != "path" {
if strings.HasPrefix(value, "http://") || strings.HasPrefix(value, "https://") {
mode = "url"
} else {
mode = "base64"
}
}
// path 仅本机有效,不入库线上历史
if mode == "path" {
return s.List(userID)
}
// 防爆:单条过大拒绝(约 1.5MB 文本)
if len(value) > 1_500_000 {
return nil, commonservice.BadRequest("AVATAR_VALUE_TOO_LARGE")
}
now := commonservice.NowRFC()
tx := s.DB.Begin()
if tx.Error != nil {
return nil, commonservice.Internal("SAVE_FAILED")
}
defer tx.Rollback()
if err := tx.Where("user_id = ? AND value = ?", userID, value).Delete(&model.UserAvatarHistory{}).Error; err != nil {
return nil, commonservice.Internal("SAVE_FAILED")
}
row := model.UserAvatarHistory{UserID: userID, Mode: mode, Value: value, CreatedAt: now}
if err := tx.Create(&row).Error; err != nil {
return nil, commonservice.Internal("SAVE_FAILED")
}
// 超出上限:删掉最旧的多余行
var ids []int64
if err := tx.Model(&model.UserAvatarHistory{}).Where("user_id = ?", userID).
Order("id DESC").Limit(500).Pluck("id", &ids).Error; err != nil {
return nil, commonservice.Internal("SAVE_FAILED")
}
if len(ids) > avatarHistoryLimit {
drop := ids[avatarHistoryLimit:]
if err := tx.Where("user_id = ? AND id IN ?", userID, drop).Delete(&model.UserAvatarHistory{}).Error; err != nil {
return nil, commonservice.Internal("SAVE_FAILED")
}
}
if err := tx.Commit().Error; err != nil {
return nil, commonservice.Internal("SAVE_FAILED")
}
return s.List(userID)
}
// Delete 删除本人一条历史。
func (s *AvatarHistoryService) Delete(userID, id int64) error {
if id <= 0 {
return commonservice.BadRequest("ID_REQUIRED")
}
res := s.DB.Where("user_id = ? AND id = ?", userID, id).Delete(&model.UserAvatarHistory{})
if res.Error != nil {
return commonservice.Internal("DELETE_FAILED")
}
if res.RowsAffected == 0 {
return commonservice.NotFound("NOT_FOUND")
}
return nil
}