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 }