generated from VLADIMIR/template
fix
This commit is contained in:
@@ -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 != "" {
|
||||
|
||||
@@ -18,18 +18,25 @@ import (
|
||||
const maxTicketsPerRequest = 100
|
||||
|
||||
var (
|
||||
// ErrInvalidTicketCount indicates that the requested ticket count is invalid.
|
||||
ErrInvalidTicketCount = errors.New("количество билетов должно быть от 1 до 100")
|
||||
ErrInvalidBaseURL = errors.New("необходимо настроить APP_BASE_URL — публичный адрес приложения")
|
||||
ErrTicketNotFound = errors.New("Билет не найден")
|
||||
ErrTicketUsed = errors.New("Билет уже использован")
|
||||
ErrGameFinished = errors.New("Игра уже завершена")
|
||||
// ErrInvalidBaseURL indicates that the public application URL is missing.
|
||||
ErrInvalidBaseURL = errors.New("необходимо настроить APP_BASE_URL — публичный адрес приложения")
|
||||
// ErrTicketNotFound indicates that a ticket was not found.
|
||||
ErrTicketNotFound = errors.New("Билет не найден")
|
||||
// ErrTicketUsed indicates that a ticket was already used.
|
||||
ErrTicketUsed = errors.New("Билет уже использован")
|
||||
// ErrGameFinished indicates that the game has finished.
|
||||
ErrGameFinished = errors.New("Игра уже завершена")
|
||||
)
|
||||
|
||||
// Ticket describes an issued support ticket.
|
||||
type Ticket struct {
|
||||
ID int
|
||||
URL string
|
||||
}
|
||||
|
||||
// Page contains tickets returned for a user.
|
||||
type Page struct {
|
||||
ID int
|
||||
Status string
|
||||
@@ -41,15 +48,18 @@ type Page struct {
|
||||
UsedBy *string
|
||||
}
|
||||
|
||||
// TicketService handles support tickets.
|
||||
type TicketService struct {
|
||||
repo *tickets_repo.Repo
|
||||
appBaseURL string
|
||||
}
|
||||
|
||||
// NewTicketService creates a ticket service.
|
||||
func NewTicketService(repo *tickets_repo.Repo, appBaseURL string) *TicketService {
|
||||
return &TicketService{repo: repo, appBaseURL: appBaseURL}
|
||||
}
|
||||
|
||||
// Issue creates tickets for a game.
|
||||
func (s *TicketService) Issue(ctx context.Context, gameID, count int) ([]*Ticket, error) {
|
||||
if count < 1 || count > maxTicketsPerRequest {
|
||||
return nil, ErrInvalidTicketCount
|
||||
@@ -87,6 +97,7 @@ func (s *TicketService) Issue(ctx context.Context, gameID, count int) ([]*Ticket
|
||||
return tickets, nil
|
||||
}
|
||||
|
||||
// Get returns tickets associated with a token.
|
||||
func (s *TicketService) Get(ctx context.Context, token string) (*Page, error) {
|
||||
hash, err := tokenHash(token)
|
||||
if err != nil {
|
||||
@@ -108,6 +119,7 @@ func (s *TicketService) Get(ctx context.Context, token string) (*Page, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Accept assigns a ticket to a user.
|
||||
func (s *TicketService) Accept(ctx context.Context, token string, userID int) (*Page, error) {
|
||||
hash, err := tokenHash(token)
|
||||
if err != nil {
|
||||
|
||||
@@ -28,13 +28,16 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
type UiService struct{}
|
||||
// UIService provides user interface permissions.
|
||||
type UIService struct{}
|
||||
|
||||
func NewUiService() *UiService {
|
||||
return &UiService{}
|
||||
// NewUIService creates a UI service.
|
||||
func NewUIService() *UIService {
|
||||
return &UIService{}
|
||||
}
|
||||
|
||||
func (s *UiService) GetPermissions(roles []string) []string {
|
||||
// GetPermissions returns permissions for the supplied roles.
|
||||
func (s *UIService) GetPermissions(roles []string) []string {
|
||||
resMap := s.getPermissionsMap(roles)
|
||||
res := make([]string, 0, len(resMap))
|
||||
for permission := range resMap {
|
||||
@@ -43,7 +46,7 @@ func (s *UiService) GetPermissions(roles []string) []string {
|
||||
return res
|
||||
}
|
||||
|
||||
func (s *UiService) getPermissionsMap(roles []string) map[string]any {
|
||||
func (s *UIService) getPermissionsMap(roles []string) map[string]any {
|
||||
resMap := make(map[string]any, 100)
|
||||
|
||||
if slices.Contains(roles, rolesModule.Admin) {
|
||||
|
||||
@@ -53,6 +53,7 @@ type usersRepository interface {
|
||||
DeleteUser(ctx context.Context, userID int) error
|
||||
}
|
||||
|
||||
// UsersService handles user accounts and authentication.
|
||||
type UsersService struct {
|
||||
usersRepo usersRepository
|
||||
passwordGenerator password_generator.IPasswordGenerator
|
||||
@@ -64,6 +65,7 @@ type UsersService struct {
|
||||
appBaseURL string
|
||||
}
|
||||
|
||||
// NewUsersService creates a users service.
|
||||
func NewUsersService(
|
||||
usersRepo usersRepository,
|
||||
passwordGenerator password_generator.IPasswordGenerator,
|
||||
@@ -84,14 +86,20 @@ func NewUsersService(
|
||||
|
||||
var (
|
||||
// ErrTermsNotAccepted — регистрация невозможна без акцепта соглашений.
|
||||
ErrTermsNotAccepted = errors.New("Необходимо принять Пользовательское соглашение и Политику конфиденциальности")
|
||||
ErrInvalidEmail = errors.New("Некорректный email")
|
||||
ErrInvalidCredentials = errors.New("Не верен email или пароль")
|
||||
ErrTermsNotAccepted = errors.New("Необходимо принять Пользовательское соглашение и Политику конфиденциальности")
|
||||
// ErrInvalidEmail indicates that an email address is invalid.
|
||||
ErrInvalidEmail = errors.New("Некорректный email")
|
||||
// ErrInvalidCredentials indicates that the supplied credentials are invalid.
|
||||
ErrInvalidCredentials = errors.New("Не верен email или пароль")
|
||||
// ErrInvalidAccountPassword indicates that the account password is invalid.
|
||||
ErrInvalidAccountPassword = errors.New("Неверный пароль")
|
||||
ErrInvalidRefreshToken = errors.New("Пользователь не найден")
|
||||
ErrInvalidRole = errors.New("Недопустимая роль")
|
||||
// ErrInvalidRefreshToken indicates that a refresh token is invalid.
|
||||
ErrInvalidRefreshToken = errors.New("Пользователь не найден")
|
||||
// ErrInvalidRole indicates that a role is invalid.
|
||||
ErrInvalidRole = errors.New("Недопустимая роль")
|
||||
)
|
||||
|
||||
// AddUser creates a user account.
|
||||
func (s *UsersService) AddUser(
|
||||
ctx context.Context,
|
||||
username string,
|
||||
@@ -192,6 +200,7 @@ func (s *UsersService) DeleteAccount(
|
||||
return err
|
||||
}
|
||||
|
||||
// RefreshPassword generates and sends a new password.
|
||||
func (s *UsersService) RefreshPassword(
|
||||
ctx context.Context,
|
||||
email string,
|
||||
@@ -252,6 +261,7 @@ func validateEmail(email string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Login authenticates a user and returns tokens.
|
||||
func (s *UsersService) Login(
|
||||
ctx context.Context,
|
||||
email string,
|
||||
@@ -294,6 +304,7 @@ func (s *UsersService) Login(
|
||||
return accessToken, refreshToken, nil
|
||||
}
|
||||
|
||||
// Refresh exchanges a refresh token for new tokens.
|
||||
func (s *UsersService) Refresh(
|
||||
ctx context.Context,
|
||||
token string,
|
||||
@@ -344,6 +355,7 @@ func (s *UsersService) Refresh(
|
||||
return accessToken, refreshToken, nil
|
||||
}
|
||||
|
||||
// GetUsers returns users.
|
||||
func (s *UsersService) GetUsers(
|
||||
ctx context.Context,
|
||||
) ([]*User, error) {
|
||||
@@ -354,6 +366,7 @@ func (s *UsersService) GetUsers(
|
||||
return mapUsers(users), nil
|
||||
}
|
||||
|
||||
// GetUserByID returns a user by ID.
|
||||
func (s *UsersService) GetUserByID(
|
||||
ctx context.Context,
|
||||
userID int,
|
||||
@@ -368,6 +381,7 @@ func (s *UsersService) GetUserByID(
|
||||
return mapUser(user), nil
|
||||
}
|
||||
|
||||
// AddUserRole assigns a role to a user.
|
||||
func (s *UsersService) AddUserRole(
|
||||
ctx context.Context,
|
||||
userID int,
|
||||
@@ -383,6 +397,7 @@ func (s *UsersService) AddUserRole(
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteUserRole removes a role from a user.
|
||||
func (s *UsersService) DeleteUserRole(
|
||||
ctx context.Context,
|
||||
userID int,
|
||||
@@ -399,13 +414,16 @@ func (s *UsersService) DeleteUserRole(
|
||||
}
|
||||
|
||||
var (
|
||||
// ErrPasswordChangeForbidden indicates that the password cannot be changed.
|
||||
ErrPasswordChangeForbidden = errors.New("Изменять чужой пароль может только администратор")
|
||||
ErrInvalidPasswordChange = errors.New("Укажите чужой положительный ID и пароль от 8 до 16 символов")
|
||||
// ErrInvalidPasswordChange indicates that the password change request is invalid.
|
||||
ErrInvalidPasswordChange = errors.New("Укажите чужой положительный ID и пароль от 8 до 16 символов")
|
||||
// ErrUserNotFound — целевой пользователь не найден.
|
||||
ErrUserNotFound = errors.New("Пользователь не найден")
|
||||
)
|
||||
|
||||
// ChangeUserPassword сохраняет только хеш; действующие сеансы не изменяются.
|
||||
// ChangeUserPassword changes a user's password subject to authorization checks.
|
||||
func (s *UsersService) ChangeUserPassword(ctx context.Context, actorID, userID int, password string) error {
|
||||
actor, err := s.usersRepo.GetUserByID(ctx, actorID)
|
||||
if errors.Is(err, users_repo.ErrUserNotFound) {
|
||||
|
||||
@@ -3,6 +3,7 @@ package users_service
|
||||
import "time"
|
||||
|
||||
// User — пользователь, доступный прикладным сценариям и выдаче наружу.
|
||||
// User describes a user exposed by the service layer.
|
||||
type User struct {
|
||||
ID int
|
||||
Username string
|
||||
|
||||
Reference in New Issue
Block a user