add location

This commit is contained in:
2026-09-18 16:01:02 +07:00
parent fc171dace5
commit 32191529a5
15 changed files with 106 additions and 21 deletions
+3
View File
@@ -1354,6 +1354,7 @@ message AddGameReq {
google.protobuf.Timestamp start_at = 3;
int32 scenario_id = 4;
string format = 5;
string location = 6;
}
message AddGameRsp {
@@ -1379,6 +1380,7 @@ message Game {
optional google.protobuf.Timestamp started_at = 8;
optional google.protobuf.Timestamp ended_at = 9;
string format = 10;
string location = 11;
}
enum ScreenMode {
@@ -1695,6 +1697,7 @@ message UpdateGameReq {
google.protobuf.Timestamp start_at = 4;
int32 scenario_id = 5;
string format = 6;
string location = 7;
}
message UpdateGameRsp {
Binary file not shown.
@@ -3082,6 +3082,9 @@
},
"format": {
"type": "string"
},
"location": {
"type": "string"
}
}
},
@@ -3205,6 +3208,9 @@
},
"format": {
"type": "string"
},
"location": {
"type": "string"
}
}
},
@@ -3560,6 +3566,9 @@
},
"format": {
"type": "string"
},
"location": {
"type": "string"
}
}
},
+5
View File
@@ -10,6 +10,10 @@ import (
func mapGame(
o *game_model.Game,
) *proto.Game {
location := ""
if o.Location != nil {
location = *o.Location
}
var startedAt *timestamppb.Timestamp
if o.StartedAt != nil {
startedAt = timestamppb.New(*o.StartedAt)
@@ -27,6 +31,7 @@ func mapGame(
Id: int32(o.ID),
Name: o.Name,
Description: o.Description,
Location: location,
StartAt: timestamppb.New(o.StartAt),
Status: o.Status,
Format: o.Format,
+9
View File
@@ -11,6 +11,13 @@ import (
"google.golang.org/grpc/status"
)
func nilIfEmpty(value string) *string {
if value == "" {
return nil
}
return &value
}
func (s *server) AddGame(ctx context.Context, req *proto.AddGameReq) (*proto.AddGameRsp, error) {
claims := ctx.Value("claims").(*processor_jwt.JWTClaims)
if !roles.HasRole(claims, roles.Organizer) && !roles.HasRole(claims, roles.Author) {
@@ -25,6 +32,7 @@ func (s *server) AddGame(ctx context.Context, req *proto.AddGameReq) (*proto.Add
ctx,
req.Name,
req.Description,
nilIfEmpty(req.Location),
req.StartAt.AsTime(),
req.ScenarioId,
req.Format,
@@ -148,6 +156,7 @@ func (s *server) UpdateGame(ctx context.Context, req *proto.UpdateGameReq) (*pro
int(req.Id),
req.Name,
req.Description,
nilIfEmpty(req.Location),
req.StartAt.AsTime(),
req.ScenarioId,
req.Format,
+1
View File
@@ -10,6 +10,7 @@ type Game struct {
ID int
Name string
Description string
Location *string
StartAt time.Time
Status string
Scenario *scenarios_service.Scenario
@@ -13,6 +13,7 @@ func MapGame(
ID: o.ID,
Name: o.Name,
Description: o.Description,
Location: o.Location,
StartAt: o.StartAt,
Status: o.Status,
Format: o.Format,
+1
View File
@@ -9,6 +9,7 @@ type Game struct {
ID int
Name string
Description string
Location *string
StartAt time.Time
Status string
ScenarioId int
+15 -6
View File
@@ -28,6 +28,7 @@ func (r *GamesRepo) AddGame(
ctx context.Context,
name string,
description string,
location *string,
startAt time.Time,
scenarioId int32,
format string,
@@ -35,11 +36,12 @@ func (r *GamesRepo) AddGame(
id := 0
err := r.pool.QueryRow(
ctx,
`INSERT INTO games (name, description, start_at, scenario_id, format)
VALUES ($1, $2, $3, $4, $5)
`INSERT INTO games (name, description, location, start_at, scenario_id, format)
VALUES ($1, $2, $3, $4, $5, $6)
RETURNING id`,
name,
description,
location,
startAt,
scenarioId,
format,
@@ -62,6 +64,7 @@ func (r *GamesRepo) GetGameByID(
games.id,
games.name,
games.description,
games.location,
games.start_at,
games.format,
games.status,
@@ -76,6 +79,7 @@ func (r *GamesRepo) GetGameByID(
&game.ID,
&game.Name,
&game.Description,
&game.Location,
&game.StartAt,
&game.Format,
&game.Status,
@@ -121,6 +125,7 @@ func (r *GamesRepo) GetGames(
games.id,
games.name,
games.description,
games.location,
games.start_at,
games.format,
games.status
@@ -139,6 +144,7 @@ func (r *GamesRepo) GetGames(
&game.ID,
&game.Name,
&game.Description,
&game.Location,
&game.StartAt,
&game.Format,
&game.Status,
@@ -160,6 +166,7 @@ func (r *GamesRepo) UpdateGame(
id int,
name string,
description string,
location *string,
startAt time.Time,
scenarioId int32,
format string,
@@ -170,13 +177,15 @@ func (r *GamesRepo) UpdateGame(
SET
name = $1,
description = $2,
start_at = $3,
scenario_id = $4,
format = $5,
location = $3,
start_at = $4,
scenario_id = $5,
format = $6,
updated_at = NOW()
WHERE id = $6`,
WHERE id = $7`,
name,
description,
location,
startAt,
scenarioId,
format,
@@ -28,11 +28,11 @@ func TestAnswerAccessPostgres(t *testing.T) {
}
defer pool.Close()
_, err = pool.Exec(ctx, `
CREATE TEMP TABLE games (id INT, name TEXT, description TEXT, start_at TIMESTAMP,
CREATE TEMP TABLE games (id INT, name TEXT, description TEXT, location TEXT, start_at TIMESTAMP,
format TEXT, status TEXT, scenario_id INT, started_at TIMESTAMP, ended_at TIMESTAMP);
CREATE TEMP TABLE teams (id INT, name TEXT, captain_email TEXT, participants_count INT,
status TEXT, password TEXT, game_id INT);
INSERT INTO games VALUES (1, 'Game', '', NOW(), 'online', 'finish', 1, NULL, NULL);
INSERT INTO games VALUES (1, 'Game', '', NULL, NOW(), 'online', 'finish', 1, NULL, NULL);
INSERT INTO teams VALUES (1, 'Team', '', 1, 'register', 'secret', 1);
ALTER TABLE teams ADD COLUMN participation_status TEXT DEFAULT 'pending', ADD COLUMN participation_source TEXT DEFAULT '',
ADD COLUMN participation_email_error TEXT DEFAULT '', ADD COLUMN participation_sent_at TIMESTAMPTZ;`)
@@ -48,7 +48,7 @@ func TestServicesLifecyclePostgres(t *testing.T) {
CREATE TEMP TABLE users(id INT, username TEXT);
CREATE TEMP TABLE scenarios(id INT PRIMARY KEY, name TEXT, description TEXT, image TEXT, scenario TEXT,
author_id INT, status TEXT DEFAULT 'draft', updated_at TIMESTAMP DEFAULT NOW(), created_at TIMESTAMP DEFAULT NOW(), published_at TIMESTAMP, is_deleted BOOLEAN DEFAULT FALSE);
CREATE TEMP TABLE games(id SERIAL PRIMARY KEY, name TEXT, description TEXT, start_at TIMESTAMP, scenario_id INT,
CREATE TEMP TABLE games(id SERIAL PRIMARY KEY, name TEXT, description TEXT, location TEXT, start_at TIMESTAMP, scenario_id INT,
format TEXT, status TEXT DEFAULT 'draft', started_at TIMESTAMP, ended_at TIMESTAMP, updated_at TIMESTAMP DEFAULT NOW(), created_at TIMESTAMP DEFAULT NOW());
CREATE TEMP TABLE teams(id SERIAL PRIMARY KEY, name TEXT, creator_id INT, game_id INT, password TEXT, status TEXT DEFAULT 'register', created_at TIMESTAMP DEFAULT NOW());
CREATE TEMP TABLE actions(id SERIAL PRIMARY KEY, team_id INT, code TEXT, via_mcp BOOLEAN, created_at TIMESTAMP DEFAULT NOW());
@@ -73,9 +73,9 @@ func TestServicesLifecyclePostgres(t *testing.T) {
answers := answer_service.NewAnswerService(games, teams, scenarioService, team_answers_repo.NewTeamAnswersRepo(pool), actions)
participation := participation_service.NewParticipationService(teams, "https://app.test")
start := time.Now().Add(48 * time.Hour)
id, err := game.AddGame(ctx, "Game", "", start, 1, "online")
id, err := game.AddGame(ctx, "Game", "", nil, start, 1, "online")
check(err)
check(game.UpdateGame(ctx, id, "Game", "Updated", start, 1, ""))
check(game.UpdateGame(ctx, id, "Game", "Updated", nil, start, 1, ""))
registration := team_service.Registration{Name: "Team", Email: "captain@example.org", ParticipantsCount: 3}
teamID, err := team.RegisterTeam(ctx, id, "delivery", registration)
check(err)
+4 -2
View File
@@ -47,6 +47,7 @@ func (s *GameService) AddGame(
ctx context.Context,
name string,
description string,
location *string,
startAt time.Time,
scenarioId int32,
format string,
@@ -57,7 +58,7 @@ func (s *GameService) AddGame(
if format != "online" && format != "offline" {
return 0, errors.New("Формат игры должен быть online или offline")
}
id, err := s.gamesRepo.AddGame(ctx, name, description, startAt, scenarioId, format)
id, err := s.gamesRepo.AddGame(ctx, name, description, location, startAt, scenarioId, format)
if err != nil {
return 0, err
}
@@ -152,6 +153,7 @@ func (s *GameService) UpdateGame(
id int,
name string,
description string,
location *string,
startAt time.Time,
scenarioId int32,
format string,
@@ -166,7 +168,7 @@ func (s *GameService) UpdateGame(
if format != "online" && format != "offline" {
return errors.New("Формат игры должен быть online или offline")
}
return s.gamesRepo.UpdateGame(ctx, id, name, description, startAt, scenarioId, format)
return s.gamesRepo.UpdateGame(ctx, id, name, description, location, startAt, scenarioId, format)
}
func (s *GameService) StartGame(ctx context.Context, id int) error {
@@ -30,11 +30,11 @@ func TestFinishedTeamStoryPostgres(t *testing.T) {
}
defer pool.Close()
_, err = pool.Exec(ctx, `
CREATE TEMP TABLE games (id INT, name TEXT, description TEXT, start_at TIMESTAMP,
CREATE TEMP TABLE games (id INT, name TEXT, description TEXT, location TEXT, start_at TIMESTAMP,
format TEXT, status TEXT, scenario_id INT, started_at TIMESTAMP, ended_at TIMESTAMP);
CREATE TEMP TABLE teams (id INT, name TEXT, captain_email TEXT, participants_count INT,
status TEXT, password TEXT, game_id INT);
INSERT INTO games VALUES (1, 'Game', '', NOW(), 'online', 'finish', 1, NULL, NULL);
INSERT INTO games VALUES (1, 'Game', '', NULL, NOW(), 'online', 'finish', 1, NULL, NULL);
INSERT INTO teams VALUES (1, 'Team', '', 1, 'register', 'secret', 1);
ALTER TABLE teams ADD COLUMN participation_status TEXT DEFAULT 'pending', ADD COLUMN participation_source TEXT DEFAULT '',
ADD COLUMN participation_email_error TEXT DEFAULT '', ADD COLUMN participation_sent_at TIMESTAMPTZ;`)
@@ -0,0 +1,18 @@
-- +goose Up
ALTER TABLE games ADD COLUMN location TEXT;
CREATE TABLE tickets (
id SERIAL PRIMARY KEY,
game_id INTEGER NOT NULL REFERENCES games (id) ON DELETE CASCADE,
token_hash BYTEA NOT NULL UNIQUE,
status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'used')),
used_at TIMESTAMPTZ,
used_by INTEGER REFERENCES users (id) ON DELETE SET NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX tickets_game_id ON tickets (game_id);
-- +goose Down
DROP TABLE IF EXISTS tickets;
ALTER TABLE games DROP COLUMN location;
+33 -6
View File
@@ -4404,6 +4404,7 @@ type AddGameReq struct {
StartAt *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=start_at,json=startAt,proto3" json:"start_at,omitempty"`
ScenarioId int32 `protobuf:"varint,4,opt,name=scenario_id,json=scenarioId,proto3" json:"scenario_id,omitempty"`
Format string `protobuf:"bytes,5,opt,name=format,proto3" json:"format,omitempty"`
Location string `protobuf:"bytes,6,opt,name=location,proto3" json:"location,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
@@ -4473,6 +4474,13 @@ func (x *AddGameReq) GetFormat() string {
return ""
}
func (x *AddGameReq) GetLocation() string {
if x != nil {
return x.Location
}
return ""
}
type AddGameRsp struct {
state protoimpl.MessageState `protogen:"open.v1"`
Error string `protobuf:"bytes,1,opt,name=error,proto3" json:"error,omitempty"`
@@ -4625,6 +4633,7 @@ type Game struct {
StartedAt *timestamppb.Timestamp `protobuf:"bytes,8,opt,name=started_at,json=startedAt,proto3,oneof" json:"started_at,omitempty"`
EndedAt *timestamppb.Timestamp `protobuf:"bytes,9,opt,name=ended_at,json=endedAt,proto3,oneof" json:"ended_at,omitempty"`
Format string `protobuf:"bytes,10,opt,name=format,proto3" json:"format,omitempty"`
Location string `protobuf:"bytes,11,opt,name=location,proto3" json:"location,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
@@ -4729,6 +4738,13 @@ func (x *Game) GetFormat() string {
return ""
}
func (x *Game) GetLocation() string {
if x != nil {
return x.Location
}
return ""
}
type ScreenState struct {
state protoimpl.MessageState `protogen:"open.v1"`
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
@@ -7436,6 +7452,7 @@ type UpdateGameReq struct {
StartAt *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=start_at,json=startAt,proto3" json:"start_at,omitempty"`
ScenarioId int32 `protobuf:"varint,5,opt,name=scenario_id,json=scenarioId,proto3" json:"scenario_id,omitempty"`
Format string `protobuf:"bytes,6,opt,name=format,proto3" json:"format,omitempty"`
Location string `protobuf:"bytes,7,opt,name=location,proto3" json:"location,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
@@ -7512,6 +7529,13 @@ func (x *UpdateGameReq) GetFormat() string {
return ""
}
func (x *UpdateGameReq) GetLocation() string {
if x != nil {
return x.Location
}
return ""
}
type UpdateGameRsp struct {
state protoimpl.MessageState `protogen:"open.v1"`
Error string `protobuf:"bytes,1,opt,name=error,proto3" json:"error,omitempty"`
@@ -9513,7 +9537,7 @@ const file_main_proto_rawDesc = "" +
"\x02id\x18\x01 \x01(\x05R\x02id\"@\n" +
"\x18UploadScenarioArchiveRsp\x12\x14\n" +
"\x05error\x18\x01 \x01(\tR\x05error\x12\x0e\n" +
"\x02id\x18\x02 \x01(\x05R\x02id\"\xb2\x01\n" +
"\x02id\x18\x02 \x01(\x05R\x02id\"\xce\x01\n" +
"\n" +
"AddGameReq\x12\x12\n" +
"\x04name\x18\x01 \x01(\tR\x04name\x12 \n" +
@@ -9521,7 +9545,8 @@ const file_main_proto_rawDesc = "" +
"\bstart_at\x18\x03 \x01(\v2\x1a.google.protobuf.TimestampR\astartAt\x12\x1f\n" +
"\vscenario_id\x18\x04 \x01(\x05R\n" +
"scenarioId\x12\x16\n" +
"\x06format\x18\x05 \x01(\tR\x06format\"2\n" +
"\x06format\x18\x05 \x01(\tR\x06format\x12\x1a\n" +
"\blocation\x18\x06 \x01(\tR\blocation\"2\n" +
"\n" +
"AddGameRsp\x12\x14\n" +
"\x05error\x18\x01 \x01(\tR\x05error\x12\x0e\n" +
@@ -9529,7 +9554,7 @@ const file_main_proto_rawDesc = "" +
"\vGetGamesReq\"_\n" +
"\vGetGamesRsp\x12\x14\n" +
"\x05error\x18\x01 \x01(\tR\x05error\x12:\n" +
"\x05games\x18\x02 \x03(\v2$.crabs.evening_detective_server.GameR\x05games\"\xcd\x03\n" +
"\x05games\x18\x02 \x03(\v2$.crabs.evening_detective_server.GameR\x05games\"\xe9\x03\n" +
"\x04Game\x12\x0e\n" +
"\x02id\x18\x01 \x01(\x05R\x02id\x12\x12\n" +
"\x04name\x18\x02 \x01(\tR\x04name\x12 \n" +
@@ -9542,7 +9567,8 @@ const file_main_proto_rawDesc = "" +
"started_at\x18\b \x01(\v2\x1a.google.protobuf.TimestampH\x00R\tstartedAt\x88\x01\x01\x12:\n" +
"\bended_at\x18\t \x01(\v2\x1a.google.protobuf.TimestampH\x01R\aendedAt\x88\x01\x01\x12\x16\n" +
"\x06format\x18\n" +
" \x01(\tR\x06formatB\r\n" +
" \x01(\tR\x06format\x12\x1a\n" +
"\blocation\x18\v \x01(\tR\blocationB\r\n" +
"\v_started_atB\v\n" +
"\t_ended_at\"\xb6\x04\n" +
"\vScreenState\x12\x0e\n" +
@@ -9737,7 +9763,7 @@ const file_main_proto_rawDesc = "" +
"\x06status\x18\x02 \x01(\tR\x06status\"q\n" +
"\x15ModerateGameReviewRsp\x12\x14\n" +
"\x05error\x18\x01 \x01(\tR\x05error\x12B\n" +
"\x06review\x18\x02 \x01(\v2*.crabs.evening_detective_server.GameReviewR\x06review\"\xc5\x01\n" +
"\x06review\x18\x02 \x01(\v2*.crabs.evening_detective_server.GameReviewR\x06review\"\xe1\x01\n" +
"\rUpdateGameReq\x12\x0e\n" +
"\x02id\x18\x01 \x01(\x05R\x02id\x12\x12\n" +
"\x04name\x18\x02 \x01(\tR\x04name\x12 \n" +
@@ -9745,7 +9771,8 @@ const file_main_proto_rawDesc = "" +
"\bstart_at\x18\x04 \x01(\v2\x1a.google.protobuf.TimestampR\astartAt\x12\x1f\n" +
"\vscenario_id\x18\x05 \x01(\x05R\n" +
"scenarioId\x12\x16\n" +
"\x06format\x18\x06 \x01(\tR\x06format\"%\n" +
"\x06format\x18\x06 \x01(\tR\x06format\x12\x1a\n" +
"\blocation\x18\a \x01(\tR\blocation\"%\n" +
"\rUpdateGameRsp\x12\x14\n" +
"\x05error\x18\x01 \x01(\tR\x05error\"\x1e\n" +
"\fStartGameReq\x12\x0e\n" +