This commit is contained in:
2026-09-18 03:18:48 +07:00
parent 1304bbacff
commit f8a4ca0f55
10 changed files with 515 additions and 290 deletions
+13
View File
@@ -705,6 +705,16 @@ service EveningDetectiveServer {
};
}
rpc GetReviewsForModeration(GetReviewsForModerationReq) returns (GetGameReviewsRsp) {
option (google.api.http) = {
get: "/api/admin/game-reviews"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Отзывы"
summary: "Получить общую очередь отзывов"
};
}
rpc ModerateGameReview(ModerateGameReviewReq) returns (ModerateGameReviewRsp) {
option (google.api.http) = {
put : "/api/admin/game-reviews/{id}"
@@ -1624,6 +1634,7 @@ message GameReview {
string text = 6;
string status = 7;
google.protobuf.Timestamp created_at = 8;
string game_name = 9;
}
message CreateGameReviewReq {
@@ -1643,6 +1654,8 @@ message GetGameReviewsReq {
int32 gameId = 1;
}
message GetReviewsForModerationReq {}
message GetGameReviewsRsp {
string error = 1;
double average_rating = 2;
Binary file not shown.
@@ -16,6 +16,29 @@
"application/json"
],
"paths": {
"/api/admin/game-reviews": {
"get": {
"summary": "Получить общую очередь отзывов",
"operationId": "EveningDetectiveServer_GetReviewsForModeration",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/evening_detective_serverGetGameReviewsRsp"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/rpcStatus"
}
}
},
"tags": [
"Отзывы"
]
}
},
"/api/admin/game-reviews/{id}": {
"put": {
"summary": "Модерировать отзыв",
@@ -3534,6 +3557,9 @@
"createdAt": {
"type": "string",
"format": "date-time"
},
"gameName": {
"type": "string"
}
}
},
+13 -1
View File
@@ -13,7 +13,7 @@ import (
)
func mapGameReview(r *game_reviews_repo.Review) *proto.GameReview {
return &proto.GameReview{Id: int32(r.ID), GameId: int32(r.GameID), TeamId: int32(r.TeamID), TeamName: r.TeamName, Rating: r.Rating, Text: r.Text, Status: r.Status, CreatedAt: timestamppb.New(r.CreatedAt)}
return &proto.GameReview{Id: int32(r.ID), GameId: int32(r.GameID), TeamId: int32(r.TeamID), TeamName: r.TeamName, Rating: r.Rating, Text: r.Text, Status: r.Status, CreatedAt: timestamppb.New(r.CreatedAt), GameName: r.GameName}
}
func mapGameReviews(reviews []*game_reviews_repo.Review) []*proto.GameReview {
@@ -52,6 +52,18 @@ func (s *server) GetGameReviewsForModeration(ctx context.Context, req *proto.Get
return &proto.GetGameReviewsRsp{AverageRating: average, ReviewsCount: count, Reviews: mapGameReviews(reviews)}, nil
}
func (s *server) GetReviewsForModeration(ctx context.Context, req *proto.GetReviewsForModerationReq) (*proto.GetGameReviewsRsp, error) {
claims := ctx.Value("claims").(*processor_jwt.JWTClaims)
if !roles.HasRole(claims, roles.Admin) {
return nil, status.Error(codes.PermissionDenied, "permission denied")
}
reviews, err := s.reviewService.ListForModeration(ctx)
if err != nil {
return &proto.GetGameReviewsRsp{Error: err.Error()}, nil
}
return &proto.GetGameReviewsRsp{Reviews: mapGameReviews(reviews)}, nil
}
func (s *server) ModerateGameReview(ctx context.Context, req *proto.ModerateGameReviewReq) (*proto.ModerateGameReviewRsp, error) {
claims := ctx.Value("claims").(*processor_jwt.JWTClaims)
if !roles.HasRole(claims, roles.Admin) {
+26 -6
View File
@@ -17,6 +17,7 @@ var (
type Review struct {
ID int
GameID int
GameName string
TeamID int
TeamName string
Rating int32
@@ -59,8 +60,8 @@ func (r *Repo) List(ctx context.Context, gameID int, all bool) ([]*Review, float
if all {
where = "r.game_id=$1"
}
rows, err := r.pool.Query(ctx, `SELECT r.id,r.game_id,r.team_id,t.name,r.rating,r.text,r.status,r.created_at
FROM game_reviews r JOIN teams t ON t.id=r.team_id WHERE `+where+` ORDER BY r.created_at DESC`, gameID)
rows, err := r.pool.Query(ctx, `SELECT r.id,r.game_id,r.team_id,t.name,g.name,r.rating,r.text,r.status,r.created_at
FROM game_reviews r JOIN teams t ON t.id=r.team_id JOIN games g ON g.id=r.game_id WHERE `+where+` ORDER BY r.created_at DESC`, gameID)
if err != nil {
return nil, 0, 0, err
}
@@ -68,7 +69,7 @@ func (r *Repo) List(ctx context.Context, gameID int, all bool) ([]*Review, float
var reviews []*Review
for rows.Next() {
review := &Review{}
if err := rows.Scan(&review.ID, &review.GameID, &review.TeamID, &review.TeamName, &review.Rating, &review.Text, &review.Status, &review.CreatedAt); err != nil {
if err := rows.Scan(&review.ID, &review.GameID, &review.TeamID, &review.TeamName, &review.GameName, &review.Rating, &review.Text, &review.Status, &review.CreatedAt); err != nil {
return nil, 0, 0, err
}
reviews = append(reviews, review)
@@ -84,6 +85,25 @@ func (r *Repo) List(ctx context.Context, gameID int, all bool) ([]*Review, float
return reviews, average, int32(count), nil
}
func (r *Repo) ListForModeration(ctx context.Context) ([]*Review, error) {
rows, err := r.pool.Query(ctx, `SELECT r.id,r.game_id,r.team_id,t.name,g.name,r.rating,r.text,r.status,r.created_at
FROM game_reviews r JOIN teams t ON t.id=r.team_id JOIN games g ON g.id=r.game_id
ORDER BY CASE WHEN r.status='pending' THEN 0 ELSE 1 END, r.created_at ASC`)
if err != nil {
return nil, err
}
defer rows.Close()
var reviews []*Review
for rows.Next() {
review := &Review{}
if err := rows.Scan(&review.ID, &review.GameID, &review.TeamID, &review.TeamName, &review.GameName, &review.Rating, &review.Text, &review.Status, &review.CreatedAt); err != nil {
return nil, err
}
reviews = append(reviews, review)
}
return reviews, rows.Err()
}
func (r *Repo) Moderate(ctx context.Context, id int, status string) (*Review, error) {
review, err := r.queryOne(ctx, `WHERE r.id=$1`, id)
if err != nil {
@@ -110,9 +130,9 @@ func (r *Repo) ScenarioRating(ctx context.Context, scenarioID int) (float64, int
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
FROM game_reviews r JOIN teams t ON t.id=r.team_id `+condition, id).Scan(
&review.ID, &review.GameID, &review.TeamID, &review.TeamName, &review.Rating, &review.Text, &review.Status, &review.CreatedAt)
err := r.pool.QueryRow(ctx, `SELECT r.id,r.game_id,r.team_id,t.name,g.name,r.rating,r.text,r.status,r.created_at
FROM game_reviews r JOIN teams t ON t.id=r.team_id JOIN games g ON g.id=r.game_id `+condition, id).Scan(
&review.ID, &review.GameID, &review.TeamID, &review.TeamName, &review.GameName, &review.Rating, &review.Text, &review.Status, &review.CreatedAt)
if errors.Is(err, pgx.ErrNoRows) {
return nil, ErrReviewNotFound
}
@@ -58,6 +58,10 @@ func (s *Service) List(ctx context.Context, gameID int, all bool) ([]*game_revie
return s.reviews.List(ctx, gameID, all)
}
func (s *Service) ListForModeration(ctx context.Context) ([]*game_reviews_repo.Review, error) {
return s.reviews.ListForModeration(ctx)
}
func (s *Service) Moderate(ctx context.Context, id int, status string) (*game_reviews_repo.Review, error) {
return s.reviews.Moderate(ctx, id, status)
}
+1
View File
@@ -9,6 +9,7 @@ var (
permissions = map[string][]string{
rolesModule.Admin: {
"users_page",
"reviews_page",
},
rolesModule.Author: {
"scenarios_page",
+334 -283
View File
File diff suppressed because it is too large Load Diff
+60
View File
@@ -2273,6 +2273,27 @@ func local_request_EveningDetectiveServer_GetGameReviewsForModeration_0(ctx cont
return msg, metadata, err
}
func request_EveningDetectiveServer_GetReviewsForModeration_0(ctx context.Context, marshaler runtime.Marshaler, client EveningDetectiveServerClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq GetReviewsForModerationReq
metadata runtime.ServerMetadata
)
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
msg, err := client.GetReviewsForModeration(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_EveningDetectiveServer_GetReviewsForModeration_0(ctx context.Context, marshaler runtime.Marshaler, server EveningDetectiveServerServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq GetReviewsForModerationReq
metadata runtime.ServerMetadata
)
msg, err := server.GetReviewsForModeration(ctx, &protoReq)
return msg, metadata, err
}
func request_EveningDetectiveServer_ModerateGameReview_0(ctx context.Context, marshaler runtime.Marshaler, client EveningDetectiveServerClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq ModerateGameReviewReq
@@ -4305,6 +4326,26 @@ func RegisterEveningDetectiveServerHandlerServer(ctx context.Context, mux *runti
}
forward_EveningDetectiveServer_GetGameReviewsForModeration_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodGet, pattern_EveningDetectiveServer_GetReviewsForModeration_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/crabs.evening_detective_server.EveningDetectiveServer/GetReviewsForModeration", runtime.WithHTTPPathPattern("/api/admin/game-reviews"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_EveningDetectiveServer_GetReviewsForModeration_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_EveningDetectiveServer_GetReviewsForModeration_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPut, pattern_EveningDetectiveServer_ModerateGameReview_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
@@ -5742,6 +5783,23 @@ func RegisterEveningDetectiveServerHandlerClient(ctx context.Context, mux *runti
}
forward_EveningDetectiveServer_GetGameReviewsForModeration_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodGet, pattern_EveningDetectiveServer_GetReviewsForModeration_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/crabs.evening_detective_server.EveningDetectiveServer/GetReviewsForModeration", runtime.WithHTTPPathPattern("/api/admin/game-reviews"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_EveningDetectiveServer_GetReviewsForModeration_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_EveningDetectiveServer_GetReviewsForModeration_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPut, pattern_EveningDetectiveServer_ModerateGameReview_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
@@ -6113,6 +6171,7 @@ var (
pattern_EveningDetectiveServer_CreateGameReview_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 2, 3}, []string{"api", "games", "gameId", "reviews"}, ""))
pattern_EveningDetectiveServer_GetGameReviews_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 2, 3}, []string{"api", "games", "gameId", "reviews"}, ""))
pattern_EveningDetectiveServer_GetGameReviewsForModeration_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3, 2, 4}, []string{"api", "admin", "games", "gameId", "reviews"}, ""))
pattern_EveningDetectiveServer_GetReviewsForModeration_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "admin", "game-reviews"}, ""))
pattern_EveningDetectiveServer_ModerateGameReview_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"api", "admin", "game-reviews", "id"}, ""))
pattern_EveningDetectiveServer_UpdateGame_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"api", "games", "id"}, ""))
pattern_EveningDetectiveServer_StartGame_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 2, 3}, []string{"api", "games", "id", "start"}, ""))
@@ -6195,6 +6254,7 @@ var (
forward_EveningDetectiveServer_CreateGameReview_0 = runtime.ForwardResponseMessage
forward_EveningDetectiveServer_GetGameReviews_0 = runtime.ForwardResponseMessage
forward_EveningDetectiveServer_GetGameReviewsForModeration_0 = runtime.ForwardResponseMessage
forward_EveningDetectiveServer_GetReviewsForModeration_0 = runtime.ForwardResponseMessage
forward_EveningDetectiveServer_ModerateGameReview_0 = runtime.ForwardResponseMessage
forward_EveningDetectiveServer_UpdateGame_0 = runtime.ForwardResponseMessage
forward_EveningDetectiveServer_StartGame_0 = runtime.ForwardResponseMessage
+38
View File
@@ -81,6 +81,7 @@ const (
EveningDetectiveServer_CreateGameReview_FullMethodName = "/crabs.evening_detective_server.EveningDetectiveServer/CreateGameReview"
EveningDetectiveServer_GetGameReviews_FullMethodName = "/crabs.evening_detective_server.EveningDetectiveServer/GetGameReviews"
EveningDetectiveServer_GetGameReviewsForModeration_FullMethodName = "/crabs.evening_detective_server.EveningDetectiveServer/GetGameReviewsForModeration"
EveningDetectiveServer_GetReviewsForModeration_FullMethodName = "/crabs.evening_detective_server.EveningDetectiveServer/GetReviewsForModeration"
EveningDetectiveServer_ModerateGameReview_FullMethodName = "/crabs.evening_detective_server.EveningDetectiveServer/ModerateGameReview"
EveningDetectiveServer_UpdateGame_FullMethodName = "/crabs.evening_detective_server.EveningDetectiveServer/UpdateGame"
EveningDetectiveServer_StartGame_FullMethodName = "/crabs.evening_detective_server.EveningDetectiveServer/StartGame"
@@ -176,6 +177,7 @@ type EveningDetectiveServerClient interface {
CreateGameReview(ctx context.Context, in *CreateGameReviewReq, opts ...grpc.CallOption) (*CreateGameReviewRsp, error)
GetGameReviews(ctx context.Context, in *GetGameReviewsReq, opts ...grpc.CallOption) (*GetGameReviewsRsp, error)
GetGameReviewsForModeration(ctx context.Context, in *GetGameReviewsReq, opts ...grpc.CallOption) (*GetGameReviewsRsp, error)
GetReviewsForModeration(ctx context.Context, in *GetReviewsForModerationReq, opts ...grpc.CallOption) (*GetGameReviewsRsp, error)
ModerateGameReview(ctx context.Context, in *ModerateGameReviewReq, opts ...grpc.CallOption) (*ModerateGameReviewRsp, error)
UpdateGame(ctx context.Context, in *UpdateGameReq, opts ...grpc.CallOption) (*UpdateGameRsp, error)
StartGame(ctx context.Context, in *StartGameReq, opts ...grpc.CallOption) (*StartGameRsp, error)
@@ -814,6 +816,16 @@ func (c *eveningDetectiveServerClient) GetGameReviewsForModeration(ctx context.C
return out, nil
}
func (c *eveningDetectiveServerClient) GetReviewsForModeration(ctx context.Context, in *GetReviewsForModerationReq, opts ...grpc.CallOption) (*GetGameReviewsRsp, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(GetGameReviewsRsp)
err := c.cc.Invoke(ctx, EveningDetectiveServer_GetReviewsForModeration_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *eveningDetectiveServerClient) ModerateGameReview(ctx context.Context, in *ModerateGameReviewReq, opts ...grpc.CallOption) (*ModerateGameReviewRsp, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ModerateGameReviewRsp)
@@ -1069,6 +1081,7 @@ type EveningDetectiveServerServer interface {
CreateGameReview(context.Context, *CreateGameReviewReq) (*CreateGameReviewRsp, error)
GetGameReviews(context.Context, *GetGameReviewsReq) (*GetGameReviewsRsp, error)
GetGameReviewsForModeration(context.Context, *GetGameReviewsReq) (*GetGameReviewsRsp, error)
GetReviewsForModeration(context.Context, *GetReviewsForModerationReq) (*GetGameReviewsRsp, error)
ModerateGameReview(context.Context, *ModerateGameReviewReq) (*ModerateGameReviewRsp, error)
UpdateGame(context.Context, *UpdateGameReq) (*UpdateGameRsp, error)
StartGame(context.Context, *StartGameReq) (*StartGameRsp, error)
@@ -1280,6 +1293,9 @@ func (UnimplementedEveningDetectiveServerServer) GetGameReviews(context.Context,
func (UnimplementedEveningDetectiveServerServer) GetGameReviewsForModeration(context.Context, *GetGameReviewsReq) (*GetGameReviewsRsp, error) {
return nil, status.Error(codes.Unimplemented, "method GetGameReviewsForModeration not implemented")
}
func (UnimplementedEveningDetectiveServerServer) GetReviewsForModeration(context.Context, *GetReviewsForModerationReq) (*GetGameReviewsRsp, error) {
return nil, status.Error(codes.Unimplemented, "method GetReviewsForModeration not implemented")
}
func (UnimplementedEveningDetectiveServerServer) ModerateGameReview(context.Context, *ModerateGameReviewReq) (*ModerateGameReviewRsp, error) {
return nil, status.Error(codes.Unimplemented, "method ModerateGameReview not implemented")
}
@@ -2454,6 +2470,24 @@ func _EveningDetectiveServer_GetGameReviewsForModeration_Handler(srv interface{}
return interceptor(ctx, in, info, handler)
}
func _EveningDetectiveServer_GetReviewsForModeration_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetReviewsForModerationReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(EveningDetectiveServerServer).GetReviewsForModeration(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: EveningDetectiveServer_GetReviewsForModeration_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(EveningDetectiveServerServer).GetReviewsForModeration(ctx, req.(*GetReviewsForModerationReq))
}
return interceptor(ctx, in, info, handler)
}
func _EveningDetectiveServer_ModerateGameReview_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ModerateGameReviewReq)
if err := dec(in); err != nil {
@@ -3029,6 +3063,10 @@ var EveningDetectiveServer_ServiceDesc = grpc.ServiceDesc{
MethodName: "GetGameReviewsForModeration",
Handler: _EveningDetectiveServer_GetGameReviewsForModeration_Handler,
},
{
MethodName: "GetReviewsForModeration",
Handler: _EveningDetectiveServer_GetReviewsForModeration_Handler,
},
{
MethodName: "ModerateGameReview",
Handler: _EveningDetectiveServer_ModerateGameReview_Handler,