Files
ttstd-infinite-canvas/backend/internal/repository/user_avatar.go
2026-10-13 05:31:30 +08:00

54 lines
1.7 KiB
Go

package repository
import (
"errors"
"time"
"yingce/backend/internal/model"
"gorm.io/gorm"
"gorm.io/gorm/clause"
)
var ErrProfileAvatarUserUnavailable = errors.New("avatar owner is not active")
func (r *Repository) UserAvatar(userID string) (*model.UserAvatar, error) {
var avatar model.UserAvatar
err := r.db.First(&avatar, "user_id = ?", userID).Error
return &avatar, err
}
// UpdateUserAvatar serializes avatar writes on the user row so the payload and
// its cache version always belong to the same committed selection.
func (r *Repository) UpdateUserAvatar(userID string, data []byte, version string, preset string, now time.Time) (*model.User, error) {
var user model.User
err := r.db.Transaction(func(tx *gorm.DB) error {
mode := "default"
if len(data) > 0 {
mode = "custom"
}
updates := map[string]any{"avatar_mode": mode, "avatar_version": version, "updated_at": now}
if mode == "default" {
updates["avatar_preset"] = preset
}
result := tx.Model(&model.User{}).Where("id = ? AND status = ?", userID, model.UserStatusActive).
Updates(updates)
if result.Error != nil {
return result.Error
}
if result.RowsAffected != 1 {
return ErrProfileAvatarUserUnavailable
}
if mode == "custom" {
avatar := model.UserAvatar{UserID: userID, Data: data, UpdatedAt: now}
if err := tx.Clauses(clause.OnConflict{Columns: []clause.Column{{Name: "user_id"}}, DoUpdates: clause.AssignmentColumns([]string{"data", "updated_at"})}).Create(&avatar).Error; err != nil {
return err
}
} else if err := tx.Delete(&model.UserAvatar{}, "user_id = ?", userID).Error; err != nil {
return err
}
return tx.First(&user, "id = ?", userID).Error
})
return &user, err
}