generated from VLADIMIR/template
add qr-s
This commit is contained in:
@@ -6,6 +6,7 @@ import (
|
||||
"evening_detective_server/internal/modules/cleaner"
|
||||
"evening_detective_server/internal/modules/password_generator"
|
||||
"evening_detective_server/internal/modules/storytelling"
|
||||
"evening_detective_server/internal/modules/team_qr_pdf"
|
||||
"evening_detective_server/internal/repos"
|
||||
"evening_detective_server/internal/repos/actions_repo"
|
||||
"evening_detective_server/internal/repos/applications_repo"
|
||||
@@ -29,6 +30,8 @@ type GameService struct {
|
||||
applicationsRepo *applications_repo.ApplicationsRepo
|
||||
answersRepo *team_answers_repo.TeamAnswersRepo
|
||||
cleaner cleaner.ICleaner
|
||||
teamQRGenerator team_qr_pdf.IGenerator
|
||||
appBaseURL string
|
||||
}
|
||||
|
||||
func NewGameService(
|
||||
@@ -43,6 +46,8 @@ func NewGameService(
|
||||
applicationsRepo *applications_repo.ApplicationsRepo,
|
||||
answersRepo *team_answers_repo.TeamAnswersRepo,
|
||||
cleaner cleaner.ICleaner,
|
||||
teamQRGenerator team_qr_pdf.IGenerator,
|
||||
appBaseURL string,
|
||||
) *GameService {
|
||||
return &GameService{
|
||||
gamesRepo: gamesRepo,
|
||||
@@ -56,6 +61,8 @@ func NewGameService(
|
||||
applicationsRepo: applicationsRepo,
|
||||
answersRepo: answersRepo,
|
||||
cleaner: cleaner,
|
||||
teamQRGenerator: teamQRGenerator,
|
||||
appBaseURL: appBaseURL,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
package game_service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"evening_detective_server/internal/modules/team_qr_pdf"
|
||||
"evening_detective_server/internal/repos"
|
||||
)
|
||||
|
||||
func (s *GameService) DownloadTeamQR(ctx context.Context, id int) ([]byte, string, error) {
|
||||
game, err := s.gamesRepo.GetGameByID(ctx, id)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
teams, err := s.teamsRepo.GetTeamsByGameID(ctx, id)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
if len(teams) == 0 {
|
||||
return nil, "", team_qr_pdf.ErrNoTeams
|
||||
}
|
||||
cards, filename, err := teamQRData(s.appBaseURL, game, teams)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
data, err := s.teamQRGenerator.Generate(cards)
|
||||
return data, filename, err
|
||||
}
|
||||
|
||||
func teamQRData(baseURL string, game *repos.Game, teams []*repos.Team) ([]team_qr_pdf.Card, string, error) {
|
||||
base, err := url.Parse(strings.TrimSpace(baseURL))
|
||||
if err != nil || (base.Scheme != "http" && base.Scheme != "https") || base.Host == "" || base.User != nil || base.RawQuery != "" || base.Fragment != "" {
|
||||
return nil, "", errors.New("для QR-кодов необходимо настроить APP_BASE_URL — публичный адрес приложения")
|
||||
}
|
||||
cards := make([]team_qr_pdf.Card, 0, len(teams))
|
||||
for _, team := range teams {
|
||||
if team.ID <= 0 || team.Password == "" {
|
||||
return nil, "", errors.New("у команды отсутствуют данные для входа")
|
||||
}
|
||||
link := *base
|
||||
link.Path = strings.TrimRight(base.Path, "/") + fmt.Sprintf("/team-story/%d", team.ID)
|
||||
link.RawPath = ""
|
||||
link.RawQuery = url.Values{"password": {team.Password}}.Encode()
|
||||
cards = append(cards, team_qr_pdf.Card{Name: team.Name, URL: link.String()})
|
||||
}
|
||||
name := strings.Map(func(r rune) rune {
|
||||
if unicode.IsControl(r) || strings.ContainsRune(`\/:*?"<>|`, r) {
|
||||
return '_'
|
||||
}
|
||||
return r
|
||||
}, game.Name)
|
||||
return cards, game.StartAt.Format("2006-01-02") + "_" + strings.TrimSpace(name) + ".pdf", nil
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package game_service
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"evening_detective_server/internal/repos"
|
||||
)
|
||||
|
||||
func TestTeamQRData(t *testing.T) {
|
||||
game := &repos.Game{Name: "Дело / №1\r\n", StartAt: time.Date(2026, 9, 8, 14, 0, 0, 0, time.UTC)}
|
||||
teams := []*repos.Team{{ID: 7, Name: "Знатоки", Password: "a+b&c?"}}
|
||||
cards, filename, err := teamQRData("https://example.org/app/", game, teams)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(cards) != 1 || cards[0].Name != "Знатоки" {
|
||||
t.Fatalf("cards: %+v", cards)
|
||||
}
|
||||
u, err := url.Parse(cards[0].URL)
|
||||
if err != nil || u.Path != "/app/team-story/7" || u.Query().Get("password") != teams[0].Password {
|
||||
t.Fatalf("wrong team link: %s", cards[0].URL)
|
||||
}
|
||||
if filename != "2026-09-08_Дело _ №1__.pdf" {
|
||||
t.Fatalf("filename: %s", filename)
|
||||
}
|
||||
for _, base := range []string{"", "ftp://example.org", "https://user:pass@example.org", "https://example.org?q=1", "https://example.org/#hash"} {
|
||||
if _, _, err := teamQRData(base, game, teams); err == nil {
|
||||
t.Errorf("accepted invalid base URL %q", base)
|
||||
}
|
||||
}
|
||||
teams[0].Password = ""
|
||||
if _, _, err := teamQRData("https://example.org", game, teams); err == nil {
|
||||
t.Fatal("accepted missing password")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user