This commit is contained in:
2026-10-03 13:07:50 +07:00
parent 7a7808b4d1
commit 3aeaa09ea8
14 changed files with 98 additions and 28 deletions
@@ -7,6 +7,7 @@ import (
"evening_detective_server/internal/repos/game_reviews_repo"
)
// Review describes a game review exposed by the service layer.
type Review struct {
ID int
GameID int
@@ -18,15 +18,23 @@ import (
)
var (
ErrTeamNotFound = errors.New("Команда не найдена")
ErrGameNotFound = errors.New("Игра не найдена")
ErrReviewNotFound = errors.New("Отзыв не найден")
ErrReviewExists = errors.New("Отзыв от команды уже оставлен")
ErrInvalidStatus = errors.New("Недопустимый статус модерации")
// ErrTeamNotFound indicates that a team was not found.
ErrTeamNotFound = errors.New("Команда не найдена")
// ErrGameNotFound indicates that a game was not found.
ErrGameNotFound = errors.New("Игра не найдена")
// ErrReviewNotFound indicates that a review was not found.
ErrReviewNotFound = errors.New("Отзыв не найден")
// ErrReviewExists indicates that a team already submitted a review.
ErrReviewExists = errors.New("Отзыв от команды уже оставлен")
// ErrInvalidStatus indicates that a moderation status is invalid.
ErrInvalidStatus = errors.New("Недопустимый статус модерации")
// ErrGameNotFinished indicates that the game is not finished.
ErrGameNotFinished = errors.New("Оценить можно только завершённую игру")
ErrInvalidReview = errors.New("Оценка должна быть от 1 до 5, текст обязателен")
// ErrInvalidReview indicates that review data is invalid.
ErrInvalidReview = errors.New("Оценка должна быть от 1 до 5, текст обязателен")
)
// GameReviewService handles game reviews.
type GameReviewService struct {
reviews *game_reviews_repo.Repo
games *games_repo.GamesRepo
@@ -34,11 +42,13 @@ type GameReviewService struct {
appBaseURL string
}
// NewGameReviewService creates a game review service.
// NewGameReviewService creates a game review service.
func NewGameReviewService(reviews *game_reviews_repo.Repo, games *games_repo.GamesRepo, teams *teams_repo.TeamsRepo, appBaseURL string) *GameReviewService {
return &GameReviewService{reviews: reviews, games: games, teams: teams, appBaseURL: appBaseURL}
}
// Create stores a game review.
func (s *GameReviewService) Create(ctx context.Context, gameID, teamID int, password string, rating int32, text string) (*Review, error) {
if rating < 1 || rating > 5 || strings.TrimSpace(text) == "" {
return nil, ErrInvalidReview
@@ -73,6 +83,7 @@ func (s *GameReviewService) Create(ctx context.Context, gameID, teamID int, pass
return mapReview(review), nil
}
// List returns reviews for a game.
func (s *GameReviewService) List(ctx context.Context, gameID int, all bool) ([]*Review, float64, int32, error) {
if _, err := s.games.GetGameByID(ctx, gameID); err != nil {
if errors.Is(err, games_repo.ErrGameNotFound) {
@@ -84,11 +95,13 @@ func (s *GameReviewService) List(ctx context.Context, gameID int, all bool) ([]*
return mapReviews(reviews), average, count, err
}
// ListForModeration returns reviews awaiting moderation.
func (s *GameReviewService) ListForModeration(ctx context.Context) ([]*Review, error) {
reviews, err := s.reviews.ListForModeration(ctx)
return mapReviews(reviews), err
}
// Moderate updates a review moderation status.
func (s *GameReviewService) Moderate(ctx context.Context, id int, status string) (*Review, error) {
review, err := s.reviews.Moderate(ctx, id, status)
if err != nil {
@@ -103,10 +116,12 @@ func (s *GameReviewService) Moderate(ctx context.Context, id int, status string)
return mapReview(review), nil
}
// ScenarioRating returns aggregate ratings for a scenario.
func (s *GameReviewService) ScenarioRating(ctx context.Context, scenarioID int) (float64, int32, error) {
return s.reviews.ScenarioRating(ctx, scenarioID)
}
// ListByScenario returns reviews for a scenario.
func (s *GameReviewService) ListByScenario(ctx context.Context, scenarioID int) ([]*Review, float64, int32, error) {
reviews, average, count, err := s.reviews.ListByScenario(ctx, scenarioID)
return mapReviews(reviews), average, count, err
@@ -121,6 +136,7 @@ func reviewReminderEmail(job *teams_repo.ReviewReminder, baseURL string) email_s
return email_sender.Message{To: job.Email, Subject: "Поделитесь впечатлениями об игре", Body: body, HTML: email_sender.WrapHTML(content, strings.TrimRight(baseURL, "/"))}
}
// RunReviewReminderEmails sends pending review reminder emails.
func (s *GameReviewService) RunReviewReminderEmails(ctx context.Context, sender email_sender.IEmailSender) {
base, err := url.Parse(s.appBaseURL)
if err != nil || base.Host == "" || (base.Scheme != "https" && base.Scheme != "http") || base.User != nil || base.RawQuery != "" || base.Fragment != "" {