98 lines
2.2 KiB
Go
98 lines
2.2 KiB
Go
package socialrating
|
|
|
|
import (
|
|
"errors"
|
|
"strings"
|
|
|
|
"gorm.io/gorm"
|
|
|
|
"social-raiting.nekiiinkognito.ru/internal/models"
|
|
)
|
|
|
|
type Service struct {
|
|
DB *gorm.DB
|
|
}
|
|
|
|
type ChangeInput struct {
|
|
TargetUserID uint
|
|
ActorUserID *uint
|
|
Delta int
|
|
OperationType string
|
|
Reason string
|
|
Source string
|
|
}
|
|
|
|
func NewService(db *gorm.DB) Service {
|
|
return Service{DB: db}
|
|
}
|
|
|
|
func (s Service) ApplyChange(input ChangeInput) (models.SocialRatingOperation, models.UserSocialRating, error) {
|
|
var operation models.SocialRatingOperation
|
|
var currentRating models.UserSocialRating
|
|
|
|
if err := validateChangeInput(input); err != nil {
|
|
return operation, currentRating, err
|
|
}
|
|
|
|
err := s.DB.Transaction(func(tx *gorm.DB) error {
|
|
if err := ensureUserExists(tx, input.TargetUserID); err != nil {
|
|
return err
|
|
}
|
|
|
|
if input.ActorUserID != nil {
|
|
if err := ensureUserExists(tx, *input.ActorUserID); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
currentRating = models.UserSocialRating{UserID: input.TargetUserID}
|
|
if err := tx.FirstOrCreate(¤tRating, models.UserSocialRating{
|
|
UserID: input.TargetUserID,
|
|
}).Error; err != nil {
|
|
return err
|
|
}
|
|
|
|
// Social rating is allowed to go below zero, so no lower bound is applied here.
|
|
currentRating.Score += input.Delta
|
|
|
|
operation = models.SocialRatingOperation{
|
|
TargetUserID: input.TargetUserID,
|
|
ActorUserID: input.ActorUserID,
|
|
Delta: input.Delta,
|
|
OperationType: strings.TrimSpace(input.OperationType),
|
|
Reason: strings.TrimSpace(input.Reason),
|
|
Source: strings.TrimSpace(input.Source),
|
|
BalanceAfter: currentRating.Score,
|
|
}
|
|
if err := tx.Create(&operation).Error; err != nil {
|
|
return err
|
|
}
|
|
|
|
currentRating.LastOperationID = &operation.ID
|
|
return tx.Save(¤tRating).Error
|
|
})
|
|
|
|
return operation, currentRating, err
|
|
}
|
|
|
|
func validateChangeInput(input ChangeInput) error {
|
|
if input.TargetUserID == 0 {
|
|
return errors.New("target user id is required")
|
|
}
|
|
|
|
if strings.TrimSpace(input.OperationType) == "" {
|
|
return errors.New("operation type is required")
|
|
}
|
|
|
|
if input.Delta == 0 {
|
|
return errors.New("delta must not be zero")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func ensureUserExists(tx *gorm.DB, userID uint) error {
|
|
var user models.User
|
|
return tx.First(&user, "id = ?", userID).Error
|
|
}
|