add scenarios methods

This commit is contained in:
2026-07-13 23:48:20 +07:00
parent 487b1aa436
commit db746f2123
17 changed files with 3620 additions and 118 deletions
+131 -6
View File
@@ -6,6 +6,7 @@ import (
"evening_detective_server/internal/modules/processor_jwt"
"evening_detective_server/internal/modules/roles"
"evening_detective_server/internal/services/file_service"
"evening_detective_server/internal/services/scenarios_service"
"evening_detective_server/internal/services/ui_service"
"evening_detective_server/internal/services/users_service"
proto "evening_detective_server/proto"
@@ -18,20 +19,23 @@ import (
type server struct {
proto.UnsafeEveningDetectiveServerServer
usersService *users_service.UsersService
uiService *ui_service.UiService
fileService *file_service.FileService
usersService *users_service.UsersService
uiService *ui_service.UiService
fileService *file_service.FileService
scenarioService *scenarios_service.ScenarioService
}
func NewServer(
usersService *users_service.UsersService,
uiService *ui_service.UiService,
fileService *file_service.FileService,
scenarioService *scenarios_service.ScenarioService,
) proto.EveningDetectiveServerServer {
return &server{
usersService: usersService,
uiService: uiService,
fileService: fileService,
usersService: usersService,
uiService: uiService,
fileService: fileService,
scenarioService: scenarioService,
}
}
@@ -202,3 +206,124 @@ func (s *server) DownloadFile(ctx context.Context, req *proto.DownloadFileReq) (
ContentType: file.Mime,
}, nil
}
func (s *server) AddScenario(ctx context.Context, req *proto.AddScenarioReq) (*proto.AddScenarioRsp, error) {
claims := ctx.Value("claims").(*processor_jwt.JWTClaims)
if !roles.HasRole(claims, roles.Author) {
return nil, status.Errorf(codes.PermissionDenied, "permission denied")
}
id, err := s.scenarioService.AddScenario(ctx, req.Name, claims.UserID)
if err != nil {
return &proto.AddScenarioRsp{
Error: err.Error(),
}, nil
}
return &proto.AddScenarioRsp{
Id: int32(id),
}, nil
}
func (s *server) GetMyScenarios(ctx context.Context, req *proto.GetMyScenariosReq) (*proto.GetMyScenariosRsp, error) {
claims := ctx.Value("claims").(*processor_jwt.JWTClaims)
if !roles.HasRole(claims, roles.Author) {
return nil, status.Errorf(codes.PermissionDenied, "permission denied")
}
scenarios, err := s.scenarioService.GetScenariosByAuthorID(ctx, claims.UserID)
if err != nil {
return &proto.GetMyScenariosRsp{
Error: err.Error(),
}, nil
}
return &proto.GetMyScenariosRsp{
Scenarios: mapScenarios(scenarios),
}, nil
}
func (s *server) GetScenario(ctx context.Context, req *proto.GetScenarioReq) (*proto.GetScenarioRsp, error) {
claims := ctx.Value("claims").(*processor_jwt.JWTClaims)
if !roles.HasRole(claims, roles.Author) {
return nil, status.Errorf(codes.PermissionDenied, "permission denied")
}
scenario, err := s.scenarioService.GetScenarioByID(ctx, int(req.Id))
if err != nil {
return &proto.GetScenarioRsp{
Error: err.Error(),
}, nil
}
return &proto.GetScenarioRsp{
Scenario: mapScenario(scenario),
}, nil
}
func (s *server) UpdateScenario(ctx context.Context, req *proto.UpdateScenarioReq) (*proto.UpdateScenarioRsp, error) {
claims := ctx.Value("claims").(*processor_jwt.JWTClaims)
if !roles.HasRole(claims, roles.Author) {
return nil, status.Errorf(codes.PermissionDenied, "permission denied")
}
err := s.scenarioService.UpdateScenarioByID(
ctx,
int(req.Id),
req.Name,
req.Description,
req.Image,
)
if err != nil {
return &proto.UpdateScenarioRsp{
Error: err.Error(),
}, nil
}
return &proto.UpdateScenarioRsp{}, nil
}
func (s *server) DeleteScenario(ctx context.Context, req *proto.DeleteScenarioReq) (*proto.DeleteScenarioRsp, error) {
claims := ctx.Value("claims").(*processor_jwt.JWTClaims)
if !roles.HasRole(claims, roles.Author) {
return nil, status.Errorf(codes.PermissionDenied, "permission denied")
}
err := s.scenarioService.DeleteScenarioByID(
ctx,
int(req.Id),
)
if err != nil {
return &proto.DeleteScenarioRsp{
Error: err.Error(),
}, nil
}
return &proto.DeleteScenarioRsp{}, nil
}
func (s *server) AddScenarioPlace(ctx context.Context, req *proto.AddScenarioPlaceReq) (*proto.AddScenarioPlaceRsp, error) {
claims := ctx.Value("claims").(*processor_jwt.JWTClaims)
if !roles.HasRole(claims, roles.Author) {
return nil, status.Errorf(codes.PermissionDenied, "permission denied")
}
panic("unimplemented")
}
func (s *server) UpdateScenarioPlace(ctx context.Context, req *proto.UpdateScenarioPlaceReq) (*proto.UpdateScenarioPlaceRsp, error) {
claims := ctx.Value("claims").(*processor_jwt.JWTClaims)
if !roles.HasRole(claims, roles.Author) {
return nil, status.Errorf(codes.PermissionDenied, "permission denied")
}
panic("unimplemented")
}
func (s *server) DeleteScenarioPlace(ctx context.Context, req *proto.DeleteScenarioPlaceReq) (*proto.DeleteScenarioPlaceRsp, error) {
claims := ctx.Value("claims").(*processor_jwt.JWTClaims)
if !roles.HasRole(claims, roles.Author) {
return nil, status.Errorf(codes.PermissionDenied, "permission denied")
}
panic("unimplemented")
}
+107
View File
@@ -0,0 +1,107 @@
package app
import (
"evening_detective_server/internal/modules/storytelling"
"evening_detective_server/internal/services/scenarios_service"
proto "evening_detective_server/proto"
"google.golang.org/protobuf/types/known/timestamppb"
)
func mapScenarios(o []*scenarios_service.Scenario) []*proto.Scenario {
res := make([]*proto.Scenario, 0, len(o))
for _, item := range o {
res = append(res, mapScenario(item))
}
return res
}
func mapScenario(o *scenarios_service.Scenario) *proto.Scenario {
var publishedAt *timestamppb.Timestamp
if o.PublishedAt != nil {
publishedAt = timestamppb.New(*o.PublishedAt)
}
return &proto.Scenario{
Id: int32(o.ID),
Name: o.Name,
Description: o.Description,
Image: o.Image,
Story: mapStory(o.Story),
Author: mapUser(o.Author),
UpdatedAt: timestamppb.New(o.UpdatedAt),
CreatedAt: timestamppb.New(o.CreatedAt),
PublishedAt: publishedAt,
}
}
func mapStory(o *storytelling.Story) *proto.Story {
return &proto.Story{
Places: mapPlaces(o.Places),
}
}
func mapPlaces(o []*storytelling.Place) []*proto.Place {
res := make([]*proto.Place, 0, len(o))
for _, item := range o {
res = append(res, mapPlace(item))
}
return res
}
func mapPlace(o *storytelling.Place) *proto.Place {
return &proto.Place{
Code: o.Code,
Name: o.Name,
Text: o.Text,
Image: o.Image,
Hidden: o.Hidden,
Applications: mapApplications(o.Applications),
Doors: mapDoors(o.Doors),
Keys: mapKeys(o.Keys),
}
}
func mapApplications(o []*storytelling.Application) []*proto.Application {
res := make([]*proto.Application, 0, len(o))
for _, item := range o {
res = append(res, mapApplication(item))
}
return res
}
func mapDoors(o []*storytelling.Door) []*proto.Door {
res := make([]*proto.Door, 0, len(o))
for _, item := range o {
res = append(res, mapDoor(item))
}
return res
}
func mapKeys(o []*storytelling.Key) []*proto.Key {
res := make([]*proto.Key, 0, len(o))
for _, item := range o {
res = append(res, mapKey(item))
}
return res
}
func mapApplication(o *storytelling.Application) *proto.Application {
return &proto.Application{
Name: o.Name,
Image: o.Image,
}
}
func mapDoor(o *storytelling.Door) *proto.Door {
return &proto.Door{
Code: o.Code,
Name: o.Name,
Keys: mapKeys(o.Keys),
}
}
func mapKey(o *storytelling.Key) *proto.Key {
return &proto.Key{
Name: o.Name,
}
}
+17
View File
@@ -0,0 +1,17 @@
package repos
import "time"
type Scenario struct {
ID int
Name string
Description *string
Image *string
Scenario string
Author *User
Status string
UpdatedAt time.Time
CreatedAt time.Time
PublishedAt *time.Time
IsDeleted bool
}
+206
View File
@@ -0,0 +1,206 @@
package scenarios_repo
import (
"context"
"errors"
"evening_detective_server/internal/repos"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgxpool"
)
var (
ErrScenarioNotFound = errors.New("Сценарий не найден")
)
type ScenariosRepo struct {
pool *pgxpool.Pool
}
func NewScenariosRepo(pool *pgxpool.Pool) *ScenariosRepo {
return &ScenariosRepo{
pool: pool,
}
}
func (s *ScenariosRepo) AddScenario(
ctx context.Context,
name string,
authorId int,
) (int, error) {
id := 0
err := s.pool.QueryRow(
ctx,
`INSERT INTO scenarios (name, author_id)
VALUES ($1, $2)
RETURNING id`,
name,
authorId,
).Scan(&id)
if err != nil {
return 0, err
}
return id, nil
}
func (s *ScenariosRepo) GetScenariosByAuthorID(
ctx context.Context,
authorId int,
) ([]*repos.Scenario, error) {
rows, err := s.pool.Query(
ctx,
`SELECT
scenarios.id,
scenarios.name,
scenarios.description,
scenarios.image,
scenarios.scenario,
users.id,
users.username,
scenarios.status,
scenarios.updated_at,
scenarios.created_at,
scenarios.published_at
FROM scenarios
LEFT JOIN users on scenarios.author_id = users.id
WHERE scenarios.author_id = $1 and is_deleted = FALSE
ORDER BY created_at DESC`,
authorId,
)
if err != nil {
return nil, err
}
defer rows.Close()
var scenarios []*repos.Scenario
for rows.Next() {
scenario := &repos.Scenario{
Author: &repos.User{},
}
err := rows.Scan(
&scenario.ID,
&scenario.Name,
&scenario.Description,
&scenario.Image,
&scenario.Scenario,
&scenario.Author.ID,
&scenario.Author.Username,
&scenario.Status,
&scenario.UpdatedAt,
&scenario.CreatedAt,
&scenario.PublishedAt,
)
if err != nil {
return nil, err
}
scenarios = append(scenarios, scenario)
}
if err := rows.Err(); err != nil {
return nil, err
}
return scenarios, nil
}
func (s *ScenariosRepo) GetScenarioByID(
ctx context.Context,
id int,
) (*repos.Scenario, error) {
scenario := &repos.Scenario{
Author: &repos.User{},
}
row := s.pool.QueryRow(
ctx,
`SELECT
scenarios.id,
scenarios.name,
scenarios.description,
scenarios.image,
scenarios.scenario,
users.id,
users.username,
scenarios.status,
scenarios.updated_at,
scenarios.created_at,
scenarios.published_at,
scenarios.is_deleted
FROM scenarios
LEFT JOIN users on scenarios.author_id = users.id
WHERE scenarios.id = $1`,
id,
)
err := row.Scan(
&scenario.ID,
&scenario.Name,
&scenario.Description,
&scenario.Image,
&scenario.Scenario,
&scenario.Author.ID,
&scenario.Author.Username,
&scenario.Status,
&scenario.UpdatedAt,
&scenario.CreatedAt,
&scenario.PublishedAt,
&scenario.IsDeleted,
)
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return nil, ErrScenarioNotFound
}
return nil, err
}
return scenario, nil
}
func (s *ScenariosRepo) UpdateScenarioByID(
ctx context.Context,
id int,
name string,
description string,
image string,
) error {
tag, err := s.pool.Exec(
ctx,
`UPDATE scenarios
SET
name = $1,
description = $2,
image = $3,
updated_at = NOW()
WHERE id = $4`,
name,
description,
image,
id,
)
if err != nil {
return err
}
if tag.RowsAffected() == 0 {
return ErrScenarioNotFound
}
return nil
}
func (s *ScenariosRepo) DeleteScenarioByID(
ctx context.Context,
id int,
) error {
tag, err := s.pool.Exec(
ctx,
`UPDATE scenarios
SET
is_deleted = TRUE
WHERE id = $1`,
id,
)
if err != nil {
return err
}
if tag.RowsAffected() == 0 {
return ErrScenarioNotFound
}
return nil
}
-1
View File
@@ -169,7 +169,6 @@ func (s *UsersRepo) GetUsers(
}
users = append(users, user)
}
if err := rows.Err(); err != nil {
return nil, err
}
@@ -0,0 +1,21 @@
package scenarios_service
import (
"evening_detective_server/internal/modules/storytelling"
"evening_detective_server/internal/repos"
"time"
)
type Scenario struct {
ID int
Name string
Description *string
Image *string
Story *storytelling.Story
Author *repos.User
Status string
UpdatedAt time.Time
CreatedAt time.Time
PublishedAt *time.Time
IsDeleted bool
}
@@ -0,0 +1,39 @@
package scenarios_service
import (
"encoding/json"
"evening_detective_server/internal/modules/storytelling"
"evening_detective_server/internal/repos"
)
func mapScenarios(o []*repos.Scenario) ([]*Scenario, error) {
res := make([]*Scenario, 0, len(o))
for _, item := range o {
scenario, err := mapScenario(item)
if err != nil {
return nil, err
}
res = append(res, scenario)
}
return res, nil
}
func mapScenario(o *repos.Scenario) (*Scenario, error) {
story := &storytelling.Story{}
if err := json.Unmarshal([]byte(o.Scenario), story); err != nil {
return nil, err
}
return &Scenario{
ID: o.ID,
Name: o.Name,
Description: o.Description,
Image: o.Image,
Story: story,
Author: o.Author,
Status: o.Status,
UpdatedAt: o.UpdatedAt,
CreatedAt: o.CreatedAt,
PublishedAt: o.PublishedAt,
IsDeleted: o.IsDeleted,
}, nil
}
@@ -0,0 +1,69 @@
package scenarios_service
import (
"context"
"evening_detective_server/internal/repos/scenarios_repo"
)
type ScenarioService struct {
scenariosRepo *scenarios_repo.ScenariosRepo
}
func NewScenarioService(
scenariosRepo *scenarios_repo.ScenariosRepo,
) *ScenarioService {
return &ScenarioService{
scenariosRepo: scenariosRepo,
}
}
func (s *ScenarioService) AddScenario(
ctx context.Context,
name string,
authorId int,
) (int, error) {
id, err := s.scenariosRepo.AddScenario(ctx, name, authorId)
if err != nil {
return 0, err
}
return id, nil
}
func (s *ScenarioService) GetScenariosByAuthorID(
ctx context.Context,
authorId int,
) ([]*Scenario, error) {
scenarios, err := s.scenariosRepo.GetScenariosByAuthorID(ctx, authorId)
if err != nil {
return nil, err
}
return mapScenarios(scenarios)
}
func (s *ScenarioService) GetScenarioByID(
ctx context.Context,
id int,
) (*Scenario, error) {
scenario, err := s.scenariosRepo.GetScenarioByID(ctx, id)
if err != nil {
return nil, err
}
return mapScenario(scenario)
}
func (s *ScenarioService) UpdateScenarioByID(
ctx context.Context,
id int,
name string,
description string,
image string,
) error {
return s.scenariosRepo.UpdateScenarioByID(ctx, id, name, description, image)
}
func (s *ScenarioService) DeleteScenarioByID(
ctx context.Context,
id int,
) error {
return s.scenariosRepo.DeleteScenarioByID(ctx, id)
}