This commit is contained in:
2026-09-18 02:46:48 +07:00
parent e9e35c60d5
commit d255860b76
9 changed files with 97 additions and 33 deletions
+12 -10
View File
@@ -1110,16 +1110,18 @@ message GetScenarioRsp {
}
message Scenario {
int32 id = 1;
string name = 2;
optional string description = 3;
optional string image = 4;
Story story = 5;
string status = 6;
User author = 7;
google.protobuf.Timestamp updated_at = 8;
google.protobuf.Timestamp created_at = 9;
google.protobuf.Timestamp published_at = 10;
int32 id = 1;
string name = 2;
optional string description = 3;
optional string image = 4;
Story story = 5;
string status = 6;
User author = 7;
google.protobuf.Timestamp updated_at = 8;
google.protobuf.Timestamp created_at = 9;
google.protobuf.Timestamp published_at = 10;
double average_rating = 11;
int32 reviews_count = 12;
}
message Story {
Binary file not shown.
@@ -4195,6 +4195,14 @@
"publishedAt": {
"type": "string",
"format": "date-time"
},
"averageRating": {
"type": "number",
"format": "double"
},
"reviewsCount": {
"type": "integer",
"format": "int32"
}
}
},
+19
View File
@@ -5,6 +5,7 @@ import (
"evening_detective_server/internal/modules/processor_jwt"
"evening_detective_server/internal/modules/roles"
"evening_detective_server/internal/services/scenarios_service"
proto "evening_detective_server/proto"
"google.golang.org/grpc/codes"
@@ -53,6 +54,9 @@ func (s *server) GetScenariosCatalog(ctx context.Context, req *proto.GetScenario
Error: err.Error(),
}, nil
}
if err := s.addScenarioRatings(ctx, scenarios); err != nil {
return &proto.GetScenariosCatalogRsp{Error: err.Error()}, nil
}
return &proto.GetScenariosCatalogRsp{
Scenarios: mapScenarios(scenarios),
@@ -84,12 +88,27 @@ func (s *server) GetScenario(ctx context.Context, req *proto.GetScenarioReq) (*p
Error: err.Error(),
}, nil
}
if err := s.addScenarioRatings(ctx, []*scenarios_service.Scenario{scenario}); err != nil {
return &proto.GetScenarioRsp{Error: err.Error()}, nil
}
return &proto.GetScenarioRsp{
Scenario: mapScenario(scenario),
}, nil
}
func (s *server) addScenarioRatings(ctx context.Context, scenarios []*scenarios_service.Scenario) error {
for _, scenario := range scenarios {
average, count, err := s.reviewService.ScenarioRating(ctx, scenario.ID)
if err != nil {
return err
}
scenario.AverageRating = average
scenario.ReviewsCount = count
}
return 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) {
+12 -10
View File
@@ -26,16 +26,18 @@ func mapScenario(o *scenarios_service.Scenario) *proto.Scenario {
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),
Status: o.Status,
UpdatedAt: timestamppb.New(o.UpdatedAt),
CreatedAt: timestamppb.New(o.CreatedAt),
PublishedAt: publishedAt,
Id: int32(o.ID),
Name: o.Name,
Description: o.Description,
Image: o.Image,
Story: mapStory(o.Story),
Author: mapUser(o.Author),
Status: o.Status,
UpdatedAt: timestamppb.New(o.UpdatedAt),
CreatedAt: timestamppb.New(o.CreatedAt),
PublishedAt: publishedAt,
AverageRating: o.AverageRating,
ReviewsCount: o.ReviewsCount,
}
}
+9
View File
@@ -99,6 +99,15 @@ func (r *Repo) Moderate(ctx context.Context, id int, status string) (*Review, er
return review, errors.New("Недопустимый статус модерации")
}
func (r *Repo) ScenarioRating(ctx context.Context, scenarioID int) (float64, int32, error) {
var average float64
var count int64
err := r.pool.QueryRow(ctx, `SELECT COALESCE(AVG(r.rating),0), COUNT(*)
FROM game_reviews r JOIN games g ON g.id=r.game_id
WHERE g.scenario_id=$1 AND g.status='finish' AND r.status='approved'`, scenarioID).Scan(&average, &count)
return average, int32(count), err
}
func (r *Repo) queryOne(ctx context.Context, condition string, id int) (*Review, error) {
review := &Review{}
err := r.pool.QueryRow(ctx, `SELECT r.id,r.game_id,r.team_id,t.name,r.rating,r.text,r.status,r.created_at
@@ -62,6 +62,10 @@ func (s *Service) Moderate(ctx context.Context, id int, status string) (*game_re
return s.reviews.Moderate(ctx, id, status)
}
func (s *Service) ScenarioRating(ctx context.Context, scenarioID int) (float64, int32, error) {
return s.reviews.ScenarioRating(ctx, scenarioID)
}
func reviewReminderEmail(job *teams_repo.ReviewReminder, baseURL string) email_sender.Message {
link := strings.TrimRight(baseURL, "/") + "/team-story/" + fmt.Sprint(job.TeamID) + "?" + url.Values{"password": {job.Password}, "review": {"1"}}.Encode()
body := fmt.Sprintf("Команда «%s», поделитесь впечатлениями об игре «%s».\n\nОценка и отзыв займут пару минут: %s\n\nОтзыв можно оставить один раз от команды.", job.TeamName, job.GameName, link)
+13 -11
View File
@@ -7,15 +7,17 @@ import (
)
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
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
AverageRating float64
ReviewsCount int32
IsDeleted bool
}
+20 -2
View File
@@ -2338,6 +2338,8 @@ type Scenario struct {
UpdatedAt *timestamppb.Timestamp `protobuf:"bytes,8,opt,name=updated_at,json=updatedAt,proto3" json:"updated_at,omitempty"`
CreatedAt *timestamppb.Timestamp `protobuf:"bytes,9,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"`
PublishedAt *timestamppb.Timestamp `protobuf:"bytes,10,opt,name=published_at,json=publishedAt,proto3" json:"published_at,omitempty"`
AverageRating float64 `protobuf:"fixed64,11,opt,name=average_rating,json=averageRating,proto3" json:"average_rating,omitempty"`
ReviewsCount int32 `protobuf:"varint,12,opt,name=reviews_count,json=reviewsCount,proto3" json:"reviews_count,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
@@ -2442,6 +2444,20 @@ func (x *Scenario) GetPublishedAt() *timestamppb.Timestamp {
return nil
}
func (x *Scenario) GetAverageRating() float64 {
if x != nil {
return x.AverageRating
}
return 0
}
func (x *Scenario) GetReviewsCount() int32 {
if x != nil {
return x.ReviewsCount
}
return 0
}
type Story struct {
state protoimpl.MessageState `protogen:"open.v1"`
Conclusion *Conclusion `protobuf:"bytes,5,opt,name=conclusion,proto3" json:"conclusion,omitempty"` // Только в полном сценарии, игрокам не передаётся.
@@ -9272,7 +9288,7 @@ const file_main_proto_rawDesc = "" +
"\x02id\x18\x01 \x01(\x05R\x02id\"l\n" +
"\x0eGetScenarioRsp\x12\x14\n" +
"\x05error\x18\x01 \x01(\tR\x05error\x12D\n" +
"\bscenario\x18\x02 \x01(\v2(.crabs.evening_detective_server.ScenarioR\bscenario\"\xd2\x03\n" +
"\bscenario\x18\x02 \x01(\v2(.crabs.evening_detective_server.ScenarioR\bscenario\"\x9e\x04\n" +
"\bScenario\x12\x0e\n" +
"\x02id\x18\x01 \x01(\x05R\x02id\x12\x12\n" +
"\x04name\x18\x02 \x01(\tR\x04name\x12%\n" +
@@ -9286,7 +9302,9 @@ const file_main_proto_rawDesc = "" +
"\n" +
"created_at\x18\t \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAt\x12=\n" +
"\fpublished_at\x18\n" +
" \x01(\v2\x1a.google.protobuf.TimestampR\vpublishedAtB\x0e\n" +
" \x01(\v2\x1a.google.protobuf.TimestampR\vpublishedAt\x12%\n" +
"\x0eaverage_rating\x18\v \x01(\x01R\raverageRating\x12#\n" +
"\rreviews_count\x18\f \x01(\x05R\freviewsCountB\x0e\n" +
"\f_descriptionB\b\n" +
"\x06_image\"\xcc\x02\n" +
"\x05Story\x12J\n" +