471 lines
12 KiB
Go
471 lines
12 KiB
Go
package repository
|
||
|
||
import (
|
||
"errors"
|
||
"strings"
|
||
|
||
"github.com/topfans/backend/pkg/database"
|
||
appErrors "github.com/topfans/backend/pkg/errors"
|
||
"github.com/topfans/backend/pkg/models"
|
||
"gorm.io/gorm"
|
||
)
|
||
|
||
// contains 检查字符串是否包含子串(不区分大小写)
|
||
func contains(s, substr string) bool {
|
||
return strings.Contains(strings.ToLower(s), strings.ToLower(substr))
|
||
}
|
||
|
||
// FanProfileRepository 粉丝档案Repository接口
|
||
type FanProfileRepository interface {
|
||
// Create 创建粉丝档案
|
||
Create(profile *models.FanProfile) error
|
||
|
||
// GetByUserAndStar 根据user_id + star_id查询
|
||
GetByUserAndStar(userID, starID int64) (*models.FanProfile, error)
|
||
|
||
// GetByUserID 查询用户的所有粉丝身份
|
||
GetByUserID(userID int64) ([]*models.FanProfile, error)
|
||
|
||
// ExistsByNickname 检查昵称是否已存在
|
||
ExistsByNickname(nickname string) (bool, error)
|
||
|
||
// CountByUserID 统计用户粉丝身份数量
|
||
CountByUserID(userID int64) (int64, error)
|
||
|
||
// Update 更新粉丝档案
|
||
Update(profile *models.FanProfile) error
|
||
|
||
// UpdateNickname 更新昵称
|
||
UpdateNickname(userID, starID int64, nickname string) error
|
||
|
||
// IncrementAssetsCount 增加资产计数
|
||
IncrementAssetsCount(userID, starID int64, delta int32) error
|
||
|
||
// DecrementAssetsCount 减少资产计数
|
||
DecrementAssetsCount(userID, starID int64, delta int32) error
|
||
|
||
// UpdateChainAddress 更新链地址
|
||
UpdateChainAddress(userID, starID int64, address string) error
|
||
|
||
// UpdateSocial 更新好友数量(social字段)
|
||
UpdateSocial(userID, starID int64, delta int32) (int32, error)
|
||
|
||
// UpdateCrystalBalance 更新水晶余额
|
||
UpdateCrystalBalance(userID, starID int64, delta int64) (int64, error)
|
||
|
||
// UpdateExperience 更新经验值
|
||
UpdateExperience(userID, starID int64, delta int64) (int64, error)
|
||
|
||
// UpdateAvatar 更新头像
|
||
UpdateAvatar(userID, starID int64, avatarURL string) error
|
||
}
|
||
|
||
// fanProfileRepository 粉丝档案Repository实现
|
||
type fanProfileRepository struct {
|
||
db *gorm.DB
|
||
}
|
||
|
||
// NewFanProfileRepository 创建粉丝档案Repository实例
|
||
func NewFanProfileRepository() FanProfileRepository {
|
||
return &fanProfileRepository{
|
||
db: database.GetDB(),
|
||
}
|
||
}
|
||
|
||
// Create 创建粉丝档案
|
||
func (r *fanProfileRepository) Create(profile *models.FanProfile) error {
|
||
if profile == nil {
|
||
return errors.New("profile cannot be nil")
|
||
}
|
||
|
||
if profile.UserID <= 0 {
|
||
return errors.New("user_id must be greater than 0")
|
||
}
|
||
|
||
if profile.StarID <= 0 {
|
||
return errors.New("star_id must be greater than 0")
|
||
}
|
||
|
||
if err := r.db.Create(profile).Error; err != nil {
|
||
// 检查是否是唯一索引冲突
|
||
errStr := err.Error()
|
||
if contains(errStr, "duplicate") || contains(errStr, "unique") || contains(errStr, "violates unique constraint") {
|
||
// 区分不同的唯一约束冲突
|
||
if contains(errStr, "uk_fan_profiles_star_nickname") {
|
||
// star_id + nickname 唯一约束冲突
|
||
return appErrors.ErrNicknameAlreadyExists
|
||
} else if contains(errStr, "uk_fan_profiles_user_star") {
|
||
// user_id + star_id 唯一约束冲突
|
||
return appErrors.ErrFanProfileAlreadyExists
|
||
}
|
||
// 其他唯一约束冲突
|
||
return appErrors.ErrFanProfileAlreadyExists
|
||
}
|
||
return err
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// GetByUserAndStar 根据user_id + star_id查询
|
||
func (r *fanProfileRepository) GetByUserAndStar(userID, starID int64) (*models.FanProfile, error) {
|
||
if userID <= 0 {
|
||
return nil, errors.New("user_id must be greater than 0")
|
||
}
|
||
|
||
if starID <= 0 {
|
||
return nil, errors.New("star_id must be greater than 0")
|
||
}
|
||
|
||
var profile models.FanProfile
|
||
if err := r.db.Where("user_id = ? AND star_id = ? AND is_active = ?", userID, starID, true).
|
||
First(&profile).Error; err != nil {
|
||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||
return nil, appErrors.ErrFanProfileNotFound
|
||
}
|
||
return nil, err
|
||
}
|
||
|
||
return &profile, nil
|
||
}
|
||
|
||
// GetByUserID 查询用户的所有粉丝身份
|
||
func (r *fanProfileRepository) GetByUserID(userID int64) ([]*models.FanProfile, error) {
|
||
if userID <= 0 {
|
||
return nil, errors.New("user_id must be greater than 0")
|
||
}
|
||
|
||
var profiles []*models.FanProfile
|
||
if err := r.db.Where("user_id = ? AND is_active = ?", userID, true).
|
||
Order("created_at ASC").
|
||
Find(&profiles).Error; err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
return profiles, nil
|
||
}
|
||
|
||
// CountByUserID 统计用户粉丝身份数量
|
||
func (r *fanProfileRepository) CountByUserID(userID int64) (int64, error) {
|
||
if userID <= 0 {
|
||
return 0, errors.New("user_id must be greater than 0")
|
||
}
|
||
|
||
var count int64
|
||
if err := r.db.Model(&models.FanProfile{}).
|
||
Where("user_id = ? AND is_active = ?", userID, true).
|
||
Count(&count).Error; err != nil {
|
||
return 0, err
|
||
}
|
||
|
||
return count, nil
|
||
}
|
||
|
||
// ExistsByNickname 检查昵称是否已存在
|
||
func (r *fanProfileRepository) ExistsByNickname(nickname string) (bool, error) {
|
||
if nickname == "" {
|
||
return false, errors.New("nickname cannot be empty")
|
||
}
|
||
|
||
var count int64
|
||
if err := r.db.Model(&models.FanProfile{}).
|
||
Where("nickname = ? AND is_active = ?", nickname, true).
|
||
Count(&count).Error; err != nil {
|
||
return false, err
|
||
}
|
||
|
||
return count > 0, nil
|
||
}
|
||
|
||
// Update 更新粉丝档案
|
||
func (r *fanProfileRepository) Update(profile *models.FanProfile) error {
|
||
if profile == nil {
|
||
return errors.New("profile cannot be nil")
|
||
}
|
||
|
||
if profile.ID == 0 {
|
||
return errors.New("profile id cannot be zero")
|
||
}
|
||
|
||
if err := r.db.Model(profile).Updates(profile).Error; err != nil {
|
||
return err
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// UpdateNickname 更新昵称
|
||
func (r *fanProfileRepository) UpdateNickname(userID, starID int64, nickname string) error {
|
||
if userID <= 0 {
|
||
return errors.New("user_id must be greater than 0")
|
||
}
|
||
|
||
if starID <= 0 {
|
||
return errors.New("star_id must be greater than 0")
|
||
}
|
||
|
||
if nickname == "" {
|
||
return errors.New("nickname cannot be empty")
|
||
}
|
||
|
||
result := r.db.Model(&models.FanProfile{}).
|
||
Where("user_id = ? AND star_id = ?", userID, starID).
|
||
Update("nickname", nickname)
|
||
|
||
if result.Error != nil {
|
||
return result.Error
|
||
}
|
||
|
||
if result.RowsAffected == 0 {
|
||
return errors.New("fan profile not found")
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// IncrementAssetsCount 增加资产计数
|
||
func (r *fanProfileRepository) IncrementAssetsCount(userID, starID int64, delta int32) error {
|
||
if userID <= 0 {
|
||
return errors.New("user_id must be greater than 0")
|
||
}
|
||
|
||
if starID <= 0 {
|
||
return errors.New("star_id must be greater than 0")
|
||
}
|
||
|
||
if delta < 0 {
|
||
return errors.New("delta must be greater than or equal to 0")
|
||
}
|
||
|
||
return r.db.Model(&models.FanProfile{}).
|
||
Where("user_id = ? AND star_id = ?", userID, starID).
|
||
UpdateColumn("assets_count", gorm.Expr("assets_count + ?", delta)).Error
|
||
}
|
||
|
||
// DecrementAssetsCount 减少资产计数
|
||
func (r *fanProfileRepository) DecrementAssetsCount(userID, starID int64, delta int32) error {
|
||
if userID <= 0 {
|
||
return errors.New("user_id must be greater than 0")
|
||
}
|
||
|
||
if starID <= 0 {
|
||
return errors.New("star_id must be greater than 0")
|
||
}
|
||
|
||
if delta < 0 {
|
||
return errors.New("delta must be greater than or equal to 0")
|
||
}
|
||
|
||
return r.db.Model(&models.FanProfile{}).
|
||
Where("user_id = ? AND star_id = ? AND assets_count >= ?", userID, starID, delta).
|
||
UpdateColumn("assets_count", gorm.Expr("assets_count - ?", delta)).Error
|
||
}
|
||
|
||
// UpdateChainAddress 更新链地址
|
||
func (r *fanProfileRepository) UpdateChainAddress(userID, starID int64, address string) error {
|
||
if userID <= 0 {
|
||
return errors.New("user_id must be greater than 0")
|
||
}
|
||
|
||
if starID <= 0 {
|
||
return errors.New("star_id must be greater than 0")
|
||
}
|
||
|
||
if address == "" {
|
||
return errors.New("chain address cannot be empty")
|
||
}
|
||
|
||
result := r.db.Model(&models.FanProfile{}).
|
||
Where("user_id = ? AND star_id = ?", userID, starID).
|
||
Update("chain_address", address)
|
||
|
||
if result.Error != nil {
|
||
return result.Error
|
||
}
|
||
|
||
if result.RowsAffected == 0 {
|
||
return errors.New("fan profile not found")
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// UpdateSocial 更新好友数量(social字段)
|
||
// delta: 变化量,正数表示增加,负数表示减少
|
||
// 返回: 更新后的好友数量
|
||
func (r *fanProfileRepository) UpdateSocial(userID, starID int64, delta int32) (int32, error) {
|
||
if userID <= 0 {
|
||
return 0, errors.New("user_id must be greater than 0")
|
||
}
|
||
|
||
if starID <= 0 {
|
||
return 0, errors.New("star_id must be greater than 0")
|
||
}
|
||
|
||
// 使用事务确保原子性
|
||
var newSocial int32
|
||
err := r.db.Transaction(func(tx *gorm.DB) error {
|
||
// 先查询当前的 social 值
|
||
var profile models.FanProfile
|
||
if err := tx.Where("user_id = ? AND star_id = ?", userID, starID).
|
||
First(&profile).Error; err != nil {
|
||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||
return appErrors.ErrFanProfileNotFound
|
||
}
|
||
return err
|
||
}
|
||
|
||
// 计算新值
|
||
newSocial = profile.Social + delta
|
||
|
||
// 确保不会小于 0
|
||
if newSocial < 0 {
|
||
newSocial = 0
|
||
}
|
||
|
||
// 更新 social 字段
|
||
if err := tx.Model(&models.FanProfile{}).
|
||
Where("user_id = ? AND star_id = ?", userID, starID).
|
||
Update("social", newSocial).Error; err != nil {
|
||
return err
|
||
}
|
||
|
||
return nil
|
||
})
|
||
|
||
if err != nil {
|
||
return 0, err
|
||
}
|
||
|
||
return newSocial, nil
|
||
}
|
||
|
||
// UpdateCrystalBalance 更新水晶余额
|
||
// delta: 变化量,正数表示增加,负数表示减少
|
||
// 返回: 更新后的水晶余额
|
||
func (r *fanProfileRepository) UpdateCrystalBalance(userID, starID int64, delta int64) (int64, error) {
|
||
if userID <= 0 {
|
||
return 0, errors.New("user_id must be greater than 0")
|
||
}
|
||
|
||
if starID <= 0 {
|
||
return 0, errors.New("star_id must be greater than 0")
|
||
}
|
||
|
||
// 使用事务确保原子性
|
||
var newBalance int64
|
||
err := r.db.Transaction(func(tx *gorm.DB) error {
|
||
// 先查询当前的 crystal_balance 值
|
||
var profile models.FanProfile
|
||
if err := tx.Where("user_id = ? AND star_id = ?", userID, starID).
|
||
First(&profile).Error; err != nil {
|
||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||
return appErrors.ErrFanProfileNotFound
|
||
}
|
||
return err
|
||
}
|
||
|
||
// 计算新值
|
||
newBalance = profile.CrystalBalance + delta
|
||
|
||
// 确保不会小于 0
|
||
if newBalance < 0 {
|
||
newBalance = 0
|
||
}
|
||
|
||
// 更新 crystal_balance 字段
|
||
if err := tx.Model(&models.FanProfile{}).
|
||
Where("user_id = ? AND star_id = ?", userID, starID).
|
||
Update("crystal_balance", newBalance).Error; err != nil {
|
||
return err
|
||
}
|
||
|
||
return nil
|
||
})
|
||
|
||
if err != nil {
|
||
return 0, err
|
||
}
|
||
|
||
return newBalance, nil
|
||
}
|
||
|
||
// UpdateExperience 更新经验值
|
||
// delta: 变化量,正数表示增加,负数表示减少
|
||
// 返回: 更新后的经验值
|
||
func (r *fanProfileRepository) UpdateExperience(userID, starID int64, delta int64) (int64, error) {
|
||
if userID <= 0 {
|
||
return 0, errors.New("user_id must be greater than 0")
|
||
}
|
||
|
||
if starID <= 0 {
|
||
return 0, errors.New("star_id must be greater than 0")
|
||
}
|
||
|
||
// 使用事务确保原子性
|
||
var newExperience int64
|
||
err := r.db.Transaction(func(tx *gorm.DB) error {
|
||
// 先查询当前的 experience 值
|
||
var profile models.FanProfile
|
||
if err := tx.Where("user_id = ? AND star_id = ?", userID, starID).
|
||
First(&profile).Error; err != nil {
|
||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||
return appErrors.ErrFanProfileNotFound
|
||
}
|
||
return err
|
||
}
|
||
|
||
// 计算新值
|
||
newExperience = profile.Experience + delta
|
||
|
||
// 确保不会小于 0
|
||
if newExperience < 0 {
|
||
newExperience = 0
|
||
}
|
||
|
||
// 更新 experience 字段
|
||
if err := tx.Model(&models.FanProfile{}).
|
||
Where("user_id = ? AND star_id = ?", userID, starID).
|
||
Update("experience", newExperience).Error; err != nil {
|
||
return err
|
||
}
|
||
|
||
return nil
|
||
})
|
||
|
||
if err != nil {
|
||
return 0, err
|
||
}
|
||
|
||
return newExperience, nil
|
||
}
|
||
|
||
// UpdateAvatar 更新头像
|
||
func (r *fanProfileRepository) UpdateAvatar(userID, starID int64, avatarURL string) error {
|
||
if userID <= 0 {
|
||
return errors.New("user_id must be greater than 0")
|
||
}
|
||
|
||
if starID <= 0 {
|
||
return errors.New("star_id must be greater than 0")
|
||
}
|
||
|
||
if avatarURL == "" {
|
||
return errors.New("avatar_url cannot be empty")
|
||
}
|
||
|
||
result := r.db.Model(&models.FanProfile{}).
|
||
Where("user_id = ? AND star_id = ?", userID, starID).
|
||
Update("avatar_url", avatarURL)
|
||
|
||
if result.Error != nil {
|
||
return result.Error
|
||
}
|
||
|
||
if result.RowsAffected == 0 {
|
||
return appErrors.ErrFanProfileNotFound
|
||
}
|
||
|
||
return nil
|
||
}
|