diff --git a/api/proto/game/v1/game.pb.go b/api/proto/game/v1/game.pb.go index bcc336c3..ea239e7b 100644 --- a/api/proto/game/v1/game.pb.go +++ b/api/proto/game/v1/game.pb.go @@ -1669,6 +1669,102 @@ func (x *SetGameStatusRequest) GetStatus() string { return "" } +type DeleteCatalogRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Meta *RequestMeta `protobuf:"bytes,1,opt,name=meta,proto3" json:"meta,omitempty"` + GameId string `protobuf:"bytes,2,opt,name=game_id,json=gameId,proto3" json:"game_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DeleteCatalogRequest) Reset() { + *x = DeleteCatalogRequest{} + mi := &file_proto_game_v1_game_proto_msgTypes[21] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DeleteCatalogRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteCatalogRequest) ProtoMessage() {} + +func (x *DeleteCatalogRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_game_v1_game_proto_msgTypes[21] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeleteCatalogRequest.ProtoReflect.Descriptor instead. +func (*DeleteCatalogRequest) Descriptor() ([]byte, []int) { + return file_proto_game_v1_game_proto_rawDescGZIP(), []int{21} +} + +func (x *DeleteCatalogRequest) GetMeta() *RequestMeta { + if x != nil { + return x.Meta + } + return nil +} + +func (x *DeleteCatalogRequest) GetGameId() string { + if x != nil { + return x.GameId + } + return "" +} + +type DeleteCatalogResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + ServerTimeMs int64 `protobuf:"varint,1,opt,name=server_time_ms,json=serverTimeMs,proto3" json:"server_time_ms,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DeleteCatalogResponse) Reset() { + *x = DeleteCatalogResponse{} + mi := &file_proto_game_v1_game_proto_msgTypes[22] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DeleteCatalogResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteCatalogResponse) ProtoMessage() {} + +func (x *DeleteCatalogResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_game_v1_game_proto_msgTypes[22] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeleteCatalogResponse.ProtoReflect.Descriptor instead. +func (*DeleteCatalogResponse) Descriptor() ([]byte, []int) { + return file_proto_game_v1_game_proto_rawDescGZIP(), []int{22} +} + +func (x *DeleteCatalogResponse) GetServerTimeMs() int64 { + if x != nil { + return x.ServerTimeMs + } + return 0 +} + var File_proto_game_v1_game_proto protoreflect.FileDescriptor const file_proto_game_v1_game_proto_rawDesc = "" + @@ -1828,7 +1924,12 @@ const file_proto_game_v1_game_proto_rawDesc = "" + "\x14SetGameStatusRequest\x12.\n" + "\x04meta\x18\x01 \x01(\v2\x1a.hyapp.game.v1.RequestMetaR\x04meta\x12\x17\n" + "\agame_id\x18\x02 \x01(\tR\x06gameId\x12\x16\n" + - "\x06status\x18\x03 \x01(\tR\x06status2\xb3\x01\n" + + "\x06status\x18\x03 \x01(\tR\x06status\"_\n" + + "\x14DeleteCatalogRequest\x12.\n" + + "\x04meta\x18\x01 \x01(\v2\x1a.hyapp.game.v1.RequestMetaR\x04meta\x12\x17\n" + + "\agame_id\x18\x02 \x01(\tR\x06gameId\"=\n" + + "\x15DeleteCatalogResponse\x12$\n" + + "\x0eserver_time_ms\x18\x01 \x01(\x03R\fserverTimeMs2\xb3\x01\n" + "\x0eGameAppService\x12N\n" + "\tListGames\x12\x1f.hyapp.game.v1.ListGamesRequest\x1a .hyapp.game.v1.ListGamesResponse\x12Q\n" + "\n" + @@ -1836,13 +1937,14 @@ const file_proto_game_v1_game_proto_rawDesc = "" + "\x13GameCallbackService\x12Q\n" + "\x0eHandleCallback\x12\x1e.hyapp.game.v1.CallbackRequest\x1a\x1f.hyapp.game.v1.CallbackResponse2t\n" + "\x0fGameCronService\x12a\n" + - "\x1cProcessLevelEventOutboxBatch\x12\x1f.hyapp.game.v1.CronBatchRequest\x1a .hyapp.game.v1.CronBatchResponse2\xc9\x03\n" + + "\x1cProcessLevelEventOutboxBatch\x12\x1f.hyapp.game.v1.CronBatchRequest\x1a .hyapp.game.v1.CronBatchResponse2\xa5\x04\n" + "\x10GameAdminService\x12Z\n" + "\rListPlatforms\x12#.hyapp.game.v1.ListPlatformsRequest\x1a$.hyapp.game.v1.ListPlatformsResponse\x12W\n" + "\x0eUpsertPlatform\x12$.hyapp.game.v1.UpsertPlatformRequest\x1a\x1f.hyapp.game.v1.PlatformResponse\x12T\n" + "\vListCatalog\x12!.hyapp.game.v1.ListCatalogRequest\x1a\".hyapp.game.v1.ListCatalogResponse\x12T\n" + "\rUpsertCatalog\x12#.hyapp.game.v1.UpsertCatalogRequest\x1a\x1e.hyapp.game.v1.CatalogResponse\x12T\n" + - "\rSetGameStatus\x12#.hyapp.game.v1.SetGameStatusRequest\x1a\x1e.hyapp.game.v1.CatalogResponseB&Z$hyapp.local/api/proto/game/v1;gamev1b\x06proto3" + "\rSetGameStatus\x12#.hyapp.game.v1.SetGameStatusRequest\x1a\x1e.hyapp.game.v1.CatalogResponse\x12Z\n" + + "\rDeleteCatalog\x12#.hyapp.game.v1.DeleteCatalogRequest\x1a$.hyapp.game.v1.DeleteCatalogResponseB&Z$hyapp.local/api/proto/game/v1;gamev1b\x06proto3" var ( file_proto_game_v1_game_proto_rawDescOnce sync.Once @@ -1856,7 +1958,7 @@ func file_proto_game_v1_game_proto_rawDescGZIP() []byte { return file_proto_game_v1_game_proto_rawDescData } -var file_proto_game_v1_game_proto_msgTypes = make([]protoimpl.MessageInfo, 23) +var file_proto_game_v1_game_proto_msgTypes = make([]protoimpl.MessageInfo, 25) var file_proto_game_v1_game_proto_goTypes = []any{ (*RequestMeta)(nil), // 0: hyapp.game.v1.RequestMeta (*GamePlatform)(nil), // 1: hyapp.game.v1.GamePlatform @@ -1879,16 +1981,18 @@ var file_proto_game_v1_game_proto_goTypes = []any{ (*UpsertCatalogRequest)(nil), // 18: hyapp.game.v1.UpsertCatalogRequest (*CatalogResponse)(nil), // 19: hyapp.game.v1.CatalogResponse (*SetGameStatusRequest)(nil), // 20: hyapp.game.v1.SetGameStatusRequest - nil, // 21: hyapp.game.v1.CallbackRequest.HeadersEntry - nil, // 22: hyapp.game.v1.CallbackRequest.QueryEntry + (*DeleteCatalogRequest)(nil), // 21: hyapp.game.v1.DeleteCatalogRequest + (*DeleteCatalogResponse)(nil), // 22: hyapp.game.v1.DeleteCatalogResponse + nil, // 23: hyapp.game.v1.CallbackRequest.HeadersEntry + nil, // 24: hyapp.game.v1.CallbackRequest.QueryEntry } var file_proto_game_v1_game_proto_depIdxs = []int32{ 0, // 0: hyapp.game.v1.ListGamesRequest.meta:type_name -> hyapp.game.v1.RequestMeta 3, // 1: hyapp.game.v1.ListGamesResponse.games:type_name -> hyapp.game.v1.AppGame 0, // 2: hyapp.game.v1.LaunchGameRequest.meta:type_name -> hyapp.game.v1.RequestMeta 0, // 3: hyapp.game.v1.CallbackRequest.meta:type_name -> hyapp.game.v1.RequestMeta - 21, // 4: hyapp.game.v1.CallbackRequest.headers:type_name -> hyapp.game.v1.CallbackRequest.HeadersEntry - 22, // 5: hyapp.game.v1.CallbackRequest.query:type_name -> hyapp.game.v1.CallbackRequest.QueryEntry + 23, // 4: hyapp.game.v1.CallbackRequest.headers:type_name -> hyapp.game.v1.CallbackRequest.HeadersEntry + 24, // 5: hyapp.game.v1.CallbackRequest.query:type_name -> hyapp.game.v1.CallbackRequest.QueryEntry 0, // 6: hyapp.game.v1.CronBatchRequest.meta:type_name -> hyapp.game.v1.RequestMeta 0, // 7: hyapp.game.v1.ListPlatformsRequest.meta:type_name -> hyapp.game.v1.RequestMeta 1, // 8: hyapp.game.v1.ListPlatformsResponse.platforms:type_name -> hyapp.game.v1.GamePlatform @@ -1901,29 +2005,32 @@ var file_proto_game_v1_game_proto_depIdxs = []int32{ 2, // 15: hyapp.game.v1.UpsertCatalogRequest.game:type_name -> hyapp.game.v1.GameCatalogItem 2, // 16: hyapp.game.v1.CatalogResponse.game:type_name -> hyapp.game.v1.GameCatalogItem 0, // 17: hyapp.game.v1.SetGameStatusRequest.meta:type_name -> hyapp.game.v1.RequestMeta - 4, // 18: hyapp.game.v1.GameAppService.ListGames:input_type -> hyapp.game.v1.ListGamesRequest - 6, // 19: hyapp.game.v1.GameAppService.LaunchGame:input_type -> hyapp.game.v1.LaunchGameRequest - 8, // 20: hyapp.game.v1.GameCallbackService.HandleCallback:input_type -> hyapp.game.v1.CallbackRequest - 10, // 21: hyapp.game.v1.GameCronService.ProcessLevelEventOutboxBatch:input_type -> hyapp.game.v1.CronBatchRequest - 12, // 22: hyapp.game.v1.GameAdminService.ListPlatforms:input_type -> hyapp.game.v1.ListPlatformsRequest - 14, // 23: hyapp.game.v1.GameAdminService.UpsertPlatform:input_type -> hyapp.game.v1.UpsertPlatformRequest - 16, // 24: hyapp.game.v1.GameAdminService.ListCatalog:input_type -> hyapp.game.v1.ListCatalogRequest - 18, // 25: hyapp.game.v1.GameAdminService.UpsertCatalog:input_type -> hyapp.game.v1.UpsertCatalogRequest - 20, // 26: hyapp.game.v1.GameAdminService.SetGameStatus:input_type -> hyapp.game.v1.SetGameStatusRequest - 5, // 27: hyapp.game.v1.GameAppService.ListGames:output_type -> hyapp.game.v1.ListGamesResponse - 7, // 28: hyapp.game.v1.GameAppService.LaunchGame:output_type -> hyapp.game.v1.LaunchGameResponse - 9, // 29: hyapp.game.v1.GameCallbackService.HandleCallback:output_type -> hyapp.game.v1.CallbackResponse - 11, // 30: hyapp.game.v1.GameCronService.ProcessLevelEventOutboxBatch:output_type -> hyapp.game.v1.CronBatchResponse - 13, // 31: hyapp.game.v1.GameAdminService.ListPlatforms:output_type -> hyapp.game.v1.ListPlatformsResponse - 15, // 32: hyapp.game.v1.GameAdminService.UpsertPlatform:output_type -> hyapp.game.v1.PlatformResponse - 17, // 33: hyapp.game.v1.GameAdminService.ListCatalog:output_type -> hyapp.game.v1.ListCatalogResponse - 19, // 34: hyapp.game.v1.GameAdminService.UpsertCatalog:output_type -> hyapp.game.v1.CatalogResponse - 19, // 35: hyapp.game.v1.GameAdminService.SetGameStatus:output_type -> hyapp.game.v1.CatalogResponse - 27, // [27:36] is the sub-list for method output_type - 18, // [18:27] is the sub-list for method input_type - 18, // [18:18] is the sub-list for extension type_name - 18, // [18:18] is the sub-list for extension extendee - 0, // [0:18] is the sub-list for field type_name + 0, // 18: hyapp.game.v1.DeleteCatalogRequest.meta:type_name -> hyapp.game.v1.RequestMeta + 4, // 19: hyapp.game.v1.GameAppService.ListGames:input_type -> hyapp.game.v1.ListGamesRequest + 6, // 20: hyapp.game.v1.GameAppService.LaunchGame:input_type -> hyapp.game.v1.LaunchGameRequest + 8, // 21: hyapp.game.v1.GameCallbackService.HandleCallback:input_type -> hyapp.game.v1.CallbackRequest + 10, // 22: hyapp.game.v1.GameCronService.ProcessLevelEventOutboxBatch:input_type -> hyapp.game.v1.CronBatchRequest + 12, // 23: hyapp.game.v1.GameAdminService.ListPlatforms:input_type -> hyapp.game.v1.ListPlatformsRequest + 14, // 24: hyapp.game.v1.GameAdminService.UpsertPlatform:input_type -> hyapp.game.v1.UpsertPlatformRequest + 16, // 25: hyapp.game.v1.GameAdminService.ListCatalog:input_type -> hyapp.game.v1.ListCatalogRequest + 18, // 26: hyapp.game.v1.GameAdminService.UpsertCatalog:input_type -> hyapp.game.v1.UpsertCatalogRequest + 20, // 27: hyapp.game.v1.GameAdminService.SetGameStatus:input_type -> hyapp.game.v1.SetGameStatusRequest + 21, // 28: hyapp.game.v1.GameAdminService.DeleteCatalog:input_type -> hyapp.game.v1.DeleteCatalogRequest + 5, // 29: hyapp.game.v1.GameAppService.ListGames:output_type -> hyapp.game.v1.ListGamesResponse + 7, // 30: hyapp.game.v1.GameAppService.LaunchGame:output_type -> hyapp.game.v1.LaunchGameResponse + 9, // 31: hyapp.game.v1.GameCallbackService.HandleCallback:output_type -> hyapp.game.v1.CallbackResponse + 11, // 32: hyapp.game.v1.GameCronService.ProcessLevelEventOutboxBatch:output_type -> hyapp.game.v1.CronBatchResponse + 13, // 33: hyapp.game.v1.GameAdminService.ListPlatforms:output_type -> hyapp.game.v1.ListPlatformsResponse + 15, // 34: hyapp.game.v1.GameAdminService.UpsertPlatform:output_type -> hyapp.game.v1.PlatformResponse + 17, // 35: hyapp.game.v1.GameAdminService.ListCatalog:output_type -> hyapp.game.v1.ListCatalogResponse + 19, // 36: hyapp.game.v1.GameAdminService.UpsertCatalog:output_type -> hyapp.game.v1.CatalogResponse + 19, // 37: hyapp.game.v1.GameAdminService.SetGameStatus:output_type -> hyapp.game.v1.CatalogResponse + 22, // 38: hyapp.game.v1.GameAdminService.DeleteCatalog:output_type -> hyapp.game.v1.DeleteCatalogResponse + 29, // [29:39] is the sub-list for method output_type + 19, // [19:29] is the sub-list for method input_type + 19, // [19:19] is the sub-list for extension type_name + 19, // [19:19] is the sub-list for extension extendee + 0, // [0:19] is the sub-list for field type_name } func init() { file_proto_game_v1_game_proto_init() } @@ -1937,7 +2044,7 @@ func file_proto_game_v1_game_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_proto_game_v1_game_proto_rawDesc), len(file_proto_game_v1_game_proto_rawDesc)), NumEnums: 0, - NumMessages: 23, + NumMessages: 25, NumExtensions: 0, NumServices: 4, }, diff --git a/api/proto/game/v1/game.proto b/api/proto/game/v1/game.proto index 963b2034..1e566ec7 100644 --- a/api/proto/game/v1/game.proto +++ b/api/proto/game/v1/game.proto @@ -182,6 +182,15 @@ message SetGameStatusRequest { string status = 3; } +message DeleteCatalogRequest { + RequestMeta meta = 1; + string game_id = 2; +} + +message DeleteCatalogResponse { + int64 server_time_ms = 1; +} + service GameAppService { rpc ListGames(ListGamesRequest) returns (ListGamesResponse); rpc LaunchGame(LaunchGameRequest) returns (LaunchGameResponse); @@ -202,4 +211,5 @@ service GameAdminService { rpc ListCatalog(ListCatalogRequest) returns (ListCatalogResponse); rpc UpsertCatalog(UpsertCatalogRequest) returns (CatalogResponse); rpc SetGameStatus(SetGameStatusRequest) returns (CatalogResponse); + rpc DeleteCatalog(DeleteCatalogRequest) returns (DeleteCatalogResponse); } diff --git a/api/proto/game/v1/game_grpc.pb.go b/api/proto/game/v1/game_grpc.pb.go index 901754fe..1176d5d3 100644 --- a/api/proto/game/v1/game_grpc.pb.go +++ b/api/proto/game/v1/game_grpc.pb.go @@ -372,6 +372,7 @@ const ( GameAdminService_ListCatalog_FullMethodName = "/hyapp.game.v1.GameAdminService/ListCatalog" GameAdminService_UpsertCatalog_FullMethodName = "/hyapp.game.v1.GameAdminService/UpsertCatalog" GameAdminService_SetGameStatus_FullMethodName = "/hyapp.game.v1.GameAdminService/SetGameStatus" + GameAdminService_DeleteCatalog_FullMethodName = "/hyapp.game.v1.GameAdminService/DeleteCatalog" ) // GameAdminServiceClient is the client API for GameAdminService service. @@ -383,6 +384,7 @@ type GameAdminServiceClient interface { ListCatalog(ctx context.Context, in *ListCatalogRequest, opts ...grpc.CallOption) (*ListCatalogResponse, error) UpsertCatalog(ctx context.Context, in *UpsertCatalogRequest, opts ...grpc.CallOption) (*CatalogResponse, error) SetGameStatus(ctx context.Context, in *SetGameStatusRequest, opts ...grpc.CallOption) (*CatalogResponse, error) + DeleteCatalog(ctx context.Context, in *DeleteCatalogRequest, opts ...grpc.CallOption) (*DeleteCatalogResponse, error) } type gameAdminServiceClient struct { @@ -443,6 +445,16 @@ func (c *gameAdminServiceClient) SetGameStatus(ctx context.Context, in *SetGameS return out, nil } +func (c *gameAdminServiceClient) DeleteCatalog(ctx context.Context, in *DeleteCatalogRequest, opts ...grpc.CallOption) (*DeleteCatalogResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(DeleteCatalogResponse) + err := c.cc.Invoke(ctx, GameAdminService_DeleteCatalog_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + // GameAdminServiceServer is the server API for GameAdminService service. // All implementations must embed UnimplementedGameAdminServiceServer // for forward compatibility. @@ -452,6 +464,7 @@ type GameAdminServiceServer interface { ListCatalog(context.Context, *ListCatalogRequest) (*ListCatalogResponse, error) UpsertCatalog(context.Context, *UpsertCatalogRequest) (*CatalogResponse, error) SetGameStatus(context.Context, *SetGameStatusRequest) (*CatalogResponse, error) + DeleteCatalog(context.Context, *DeleteCatalogRequest) (*DeleteCatalogResponse, error) mustEmbedUnimplementedGameAdminServiceServer() } @@ -477,6 +490,9 @@ func (UnimplementedGameAdminServiceServer) UpsertCatalog(context.Context, *Upser func (UnimplementedGameAdminServiceServer) SetGameStatus(context.Context, *SetGameStatusRequest) (*CatalogResponse, error) { return nil, status.Error(codes.Unimplemented, "method SetGameStatus not implemented") } +func (UnimplementedGameAdminServiceServer) DeleteCatalog(context.Context, *DeleteCatalogRequest) (*DeleteCatalogResponse, error) { + return nil, status.Error(codes.Unimplemented, "method DeleteCatalog not implemented") +} func (UnimplementedGameAdminServiceServer) mustEmbedUnimplementedGameAdminServiceServer() {} func (UnimplementedGameAdminServiceServer) testEmbeddedByValue() {} @@ -588,6 +604,24 @@ func _GameAdminService_SetGameStatus_Handler(srv interface{}, ctx context.Contex return interceptor(ctx, in, info, handler) } +func _GameAdminService_DeleteCatalog_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeleteCatalogRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GameAdminServiceServer).DeleteCatalog(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: GameAdminService_DeleteCatalog_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GameAdminServiceServer).DeleteCatalog(ctx, req.(*DeleteCatalogRequest)) + } + return interceptor(ctx, in, info, handler) +} + // GameAdminService_ServiceDesc is the grpc.ServiceDesc for GameAdminService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -615,6 +649,10 @@ var GameAdminService_ServiceDesc = grpc.ServiceDesc{ MethodName: "SetGameStatus", Handler: _GameAdminService_SetGameStatus_Handler, }, + { + MethodName: "DeleteCatalog", + Handler: _GameAdminService_DeleteCatalog_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "proto/game/v1/game.proto", diff --git a/server/admin/internal/integration/gameclient/client.go b/server/admin/internal/integration/gameclient/client.go index b51d7d58..9bdabcc4 100644 --- a/server/admin/internal/integration/gameclient/client.go +++ b/server/admin/internal/integration/gameclient/client.go @@ -15,6 +15,7 @@ type Client interface { ListCatalog(ctx context.Context, req *gamev1.ListCatalogRequest) (*gamev1.ListCatalogResponse, error) UpsertCatalog(ctx context.Context, req *gamev1.UpsertCatalogRequest) (*gamev1.CatalogResponse, error) SetGameStatus(ctx context.Context, req *gamev1.SetGameStatusRequest) (*gamev1.CatalogResponse, error) + DeleteCatalog(ctx context.Context, req *gamev1.DeleteCatalogRequest) (*gamev1.DeleteCatalogResponse, error) } type GRPCClient struct { @@ -44,3 +45,7 @@ func (c *GRPCClient) UpsertCatalog(ctx context.Context, req *gamev1.UpsertCatalo func (c *GRPCClient) SetGameStatus(ctx context.Context, req *gamev1.SetGameStatusRequest) (*gamev1.CatalogResponse, error) { return c.client.SetGameStatus(ctx, req) } + +func (c *GRPCClient) DeleteCatalog(ctx context.Context, req *gamev1.DeleteCatalogRequest) (*gamev1.DeleteCatalogResponse, error) { + return c.client.DeleteCatalog(ctx, req) +} diff --git a/server/admin/internal/modules/gamemanagement/handler.go b/server/admin/internal/modules/gamemanagement/handler.go index d27e682c..d49658af 100644 --- a/server/admin/internal/modules/gamemanagement/handler.go +++ b/server/admin/internal/modules/gamemanagement/handler.go @@ -146,6 +146,24 @@ func (h *Handler) SetGameStatus(c *gin.Context) { response.OK(c, item) } +// DeleteCatalog 删除后台游戏目录记录,同时由 game-service 清理展示规则;历史订单和启动会话保留用于审计。 +func (h *Handler) DeleteCatalog(c *gin.Context) { + gameID := strings.TrimSpace(c.Param("game_id")) + if gameID == "" { + response.BadRequest(c, "游戏 ID 参数不正确") + return + } + if _, err := h.game.DeleteCatalog(c.Request.Context(), &gamev1.DeleteCatalogRequest{ + Meta: requestMeta(c), + GameId: gameID, + }); err != nil { + response.BadRequest(c, err.Error()) + return + } + h.auditLog(c, "delete-game-catalog", "game_catalog", gameID, "deleted") + response.OK(c, gin.H{"gameId": gameID}) +} + func (h *Handler) auditLog(c *gin.Context, action string, resource string, resourceID string, detail string) { shared.OperationLogWithResourceID(c, h.audit, action, resource, resourceID, "success", detail) } diff --git a/server/admin/internal/modules/gamemanagement/routes.go b/server/admin/internal/modules/gamemanagement/routes.go index ca5ae57d..d9ab9fe5 100644 --- a/server/admin/internal/modules/gamemanagement/routes.go +++ b/server/admin/internal/modules/gamemanagement/routes.go @@ -19,4 +19,5 @@ func RegisterRoutes(protected *gin.RouterGroup, h *Handler) { protected.POST("/admin/game/games", middleware.RequirePermission("game:create"), h.CreateCatalog) protected.PATCH("/admin/game/games/:game_id", middleware.RequirePermission("game:update"), h.UpdateCatalog) protected.PATCH("/admin/game/games/:game_id/status", middleware.RequirePermission("game:status"), h.SetGameStatus) + protected.DELETE("/admin/game/games/:game_id", middleware.RequireAnyPermission("game:delete", "game:update"), h.DeleteCatalog) } diff --git a/server/admin/internal/repository/seed.go b/server/admin/internal/repository/seed.go index 2cedbfab..7fdd73fd 100644 --- a/server/admin/internal/repository/seed.go +++ b/server/admin/internal/repository/seed.go @@ -83,6 +83,7 @@ var defaultPermissions = []model.Permission{ {Name: "游戏创建", Code: "game:create", Kind: "button"}, {Name: "游戏更新", Code: "game:update", Kind: "button"}, {Name: "游戏状态", Code: "game:status", Kind: "button"}, + {Name: "游戏删除", Code: "game:delete", Kind: "button"}, {Name: "每日任务查看", Code: "daily-task:view", Kind: "menu"}, {Name: "每日任务创建", Code: "daily-task:create", Kind: "button"}, {Name: "每日任务更新", Code: "daily-task:update", Kind: "button"}, @@ -478,7 +479,7 @@ func defaultRolePermissionCodes(code string) []string { "coin-seller:view", "coin-seller:create", "coin-seller:update", "coin-seller:stock-credit", "coin-ledger:view", "coin-adjustment:view", "coin-adjustment:create", "payment-bill:view", "payment-product:view", "payment-product:create", "payment-product:update", "payment-product:delete", "lucky-gift:view", "lucky-gift:update", - "game:view", "game:create", "game:update", "game:status", + "game:view", "game:create", "game:update", "game:status", "game:delete", "daily-task:view", "daily-task:create", "daily-task:update", "daily-task:status", "achievement:view", "achievement:create", "achievement:update", "seven-day-checkin:view", "seven-day-checkin:update", @@ -558,7 +559,7 @@ func defaultRolePermissionMigrationCodes(code string) []string { "coin-seller:view", "coin-seller:create", "coin-seller:update", "coin-seller:stock-credit", "coin-ledger:view", "coin-adjustment:view", "coin-adjustment:create", "payment-bill:view", "payment-product:view", "payment-product:create", "payment-product:update", "payment-product:delete", "lucky-gift:view", "lucky-gift:update", - "game:view", "game:create", "game:update", "game:status", + "game:view", "game:create", "game:update", "game:status", "game:delete", "daily-task:view", "daily-task:create", "daily-task:update", "daily-task:status", "achievement:view", "achievement:create", "achievement:update", "seven-day-checkin:view", "seven-day-checkin:update", diff --git a/server/admin/migrations/009_admin_navigation_seed.sql b/server/admin/migrations/009_admin_navigation_seed.sql index 20477da6..8e93c67e 100644 --- a/server/admin/migrations/009_admin_navigation_seed.sql +++ b/server/admin/migrations/009_admin_navigation_seed.sql @@ -17,6 +17,7 @@ INSERT INTO admin_permissions (name, code, kind, description, created_at_ms, upd ('游戏创建', 'game:create', 'button', '', @now_ms, @now_ms), ('游戏更新', 'game:update', 'button', '', @now_ms, @now_ms), ('游戏状态', 'game:status', 'button', '', @now_ms, @now_ms), + ('游戏删除', 'game:delete', 'button', '', @now_ms, @now_ms), ('每日任务查看', 'daily-task:view', 'menu', '', @now_ms, @now_ms), ('每日任务创建', 'daily-task:create', 'button', '', @now_ms, @now_ms), ('每日任务更新', 'daily-task:update', 'button', '', @now_ms, @now_ms), diff --git a/services/game-service/internal/service/game/service.go b/services/game-service/internal/service/game/service.go index c659eea4..dba4712a 100644 --- a/services/game-service/internal/service/game/service.go +++ b/services/game-service/internal/service/game/service.go @@ -46,6 +46,7 @@ type Repository interface { ListCatalog(ctx context.Context, query ListCatalogQuery) ([]gamedomain.CatalogItem, string, error) UpsertCatalog(ctx context.Context, item gamedomain.CatalogItem) (gamedomain.CatalogItem, error) SetGameStatus(ctx context.Context, appCode string, gameID string, status string, nowMs int64) (gamedomain.CatalogItem, error) + DeleteCatalog(ctx context.Context, appCode string, gameID string) error } // WalletClient 是 game-service 调 wallet-service 的最小改账依赖。 diff --git a/services/game-service/internal/service/game/service_test.go b/services/game-service/internal/service/game/service_test.go index 9ca2ce62..31f474c4 100644 --- a/services/game-service/internal/service/game/service_test.go +++ b/services/game-service/internal/service/game/service_test.go @@ -1113,6 +1113,9 @@ func (f *fakeRepository) UpsertCatalog(context.Context, gamedomain.CatalogItem) func (f *fakeRepository) SetGameStatus(context.Context, string, string, string, int64) (gamedomain.CatalogItem, error) { return gamedomain.CatalogItem{}, nil } +func (f *fakeRepository) DeleteCatalog(context.Context, string, string) error { + return nil +} type fakeWallet struct { lastApply *walletv1.ApplyGameCoinChangeRequest diff --git a/services/game-service/internal/storage/mysql/repository.go b/services/game-service/internal/storage/mysql/repository.go index 362dec33..f49100a9 100644 --- a/services/game-service/internal/storage/mysql/repository.go +++ b/services/game-service/internal/storage/mysql/repository.go @@ -818,6 +818,34 @@ func (r *Repository) SetGameStatus(ctx context.Context, appCode string, gameID s return r.getCatalog(ctx, appCode, gameID) } +func (r *Repository) DeleteCatalog(ctx context.Context, appCode string, gameID string) error { + app := appcode.Normalize(appCode) + id := strings.TrimSpace(gameID) + if id == "" { + return xerr.New(xerr.InvalidArgument, "game_id is required") + } + tx, err := r.db.BeginTx(ctx, nil) + if err != nil { + return err + } + defer tx.Rollback() + result, err := tx.ExecContext(ctx, `DELETE FROM game_catalog WHERE app_code = ? AND game_id = ?`, app, id) + if err != nil { + return err + } + affected, err := result.RowsAffected() + if err != nil { + return err + } + if affected == 0 { + return xerr.New(xerr.NotFound, "game not found") + } + if _, err := tx.ExecContext(ctx, `DELETE FROM game_display_rules WHERE app_code = ? AND game_id = ?`, app, id); err != nil { + return err + } + return tx.Commit() +} + func (r *Repository) getCatalog(ctx context.Context, appCode string, gameID string) (gamedomain.CatalogItem, error) { row := r.db.QueryRowContext(ctx, `SELECT app_code, game_id, platform_code, provider_game_id, game_name, category, icon_url, cover_url, diff --git a/services/game-service/internal/storage/mysql/repository_test.go b/services/game-service/internal/storage/mysql/repository_test.go index c2509c66..c8923379 100644 --- a/services/game-service/internal/storage/mysql/repository_test.go +++ b/services/game-service/internal/storage/mysql/repository_test.go @@ -108,3 +108,59 @@ func TestUpsertCatalogCreatesDefaultVoiceRoomDisplayRule(t *testing.T) { t.Fatalf("display rule mismatch: scene=%s visible=%v enabled=%v sort=%d", scene, visible, enabled, sortOrder) } } + +func TestDeleteCatalogRemovesGameAndDisplayRule(t *testing.T) { + caller := mysqlschema.CallerFile(t, 1) + schema := mysqlschema.New(t, mysqlschema.Config{ + InitDBPath: mysqlschema.InitDBPath(t, caller, "../../../deploy/mysql/initdb/001_game_service.sql"), + DatabasePrefix: "hy_game_test", + }) + + ctx := appcode.WithContext(context.Background(), "lalu") + repo, err := Open(ctx, schema.DSN) + if err != nil { + t.Fatalf("open repository failed: %v", err) + } + t.Cleanup(func() { _ = repo.Close() }) + + item := gamedomain.CatalogItem{ + AppCode: "lalu", + GameID: "delete_game_001", + PlatformCode: "demo", + ProviderGameID: "provider_delete_001", + GameName: "Delete Game", + Category: "casino", + LaunchMode: gamedomain.LaunchModeH5Popup, + Orientation: "portrait", + Status: gamedomain.StatusActive, + SortOrder: 80, + } + if _, err := repo.UpsertCatalog(ctx, item); err != nil { + t.Fatalf("upsert catalog failed: %v", err) + } + if err := repo.DeleteCatalog(ctx, "lalu", item.GameID); err != nil { + t.Fatalf("delete catalog failed: %v", err) + } + + var gameCount int + if err := repo.db.QueryRowContext(ctx, + `SELECT COUNT(*) FROM game_catalog WHERE app_code = ? AND game_id = ?`, + "lalu", item.GameID, + ).Scan(&gameCount); err != nil { + t.Fatalf("query catalog count failed: %v", err) + } + if gameCount != 0 { + t.Fatalf("game_catalog count = %d, want 0", gameCount) + } + + var ruleCount int + if err := repo.db.QueryRowContext(ctx, + `SELECT COUNT(*) FROM game_display_rules WHERE app_code = ? AND game_id = ?`, + "lalu", item.GameID, + ).Scan(&ruleCount); err != nil { + t.Fatalf("query display rule count failed: %v", err) + } + if ruleCount != 0 { + t.Fatalf("game_display_rules count = %d, want 0", ruleCount) + } +} diff --git a/services/game-service/internal/transport/grpc/server.go b/services/game-service/internal/transport/grpc/server.go index 9c911281..e5bac842 100644 --- a/services/game-service/internal/transport/grpc/server.go +++ b/services/game-service/internal/transport/grpc/server.go @@ -171,6 +171,14 @@ func (s *Server) SetGameStatus(ctx context.Context, req *gamev1.SetGameStatusReq return &gamev1.CatalogResponse{Game: catalogToProto(item), ServerTimeMs: time.Now().UnixMilli()}, nil } +func (s *Server) DeleteCatalog(ctx context.Context, req *gamev1.DeleteCatalogRequest) (*gamev1.DeleteCatalogResponse, error) { + ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode()) + if err := s.svcRepository().DeleteCatalog(ctx, req.GetMeta().GetAppCode(), req.GetGameId()); err != nil { + return nil, xerr.ToGRPCError(err) + } + return &gamev1.DeleteCatalogResponse{ServerTimeMs: time.Now().UnixMilli()}, nil +} + func (s *Server) svcRepository() gameservice.Repository { if s == nil || s.svc == nil || s.svc.Repository() == nil { return unavailableRepository{} @@ -237,6 +245,9 @@ func (unavailableRepository) UpsertCatalog(context.Context, gamedomain.CatalogIt func (unavailableRepository) SetGameStatus(context.Context, string, string, string, int64) (gamedomain.CatalogItem, error) { return gamedomain.CatalogItem{}, xerr.New(xerr.Unavailable, "game repository is not configured") } +func (unavailableRepository) DeleteCatalog(context.Context, string, string) error { + return xerr.New(xerr.Unavailable, "game repository is not configured") +} func appGameToProto(item gamedomain.AppGame) *gamev1.AppGame { return &gamev1.AppGame{ diff --git a/tools/baishun-game-test-server.mjs b/tools/baishun-game-test-server.mjs index 5db3e6be..17111427 100644 --- a/tools/baishun-game-test-server.mjs +++ b/tools/baishun-game-test-server.mjs @@ -22,9 +22,10 @@ function copyProxyHeaders(req) { for (const [key, value] of Object.entries(req.headers)) { if (!value) continue; const lower = key.toLowerCase(); - if (["host", "origin", "referer", "connection", "content-length"].includes(lower)) continue; + if (["host", "origin", "referer", "connection", "content-length", "accept-encoding"].includes(lower)) continue; headers.set(key, Array.isArray(value) ? value.join(", ") : value); } + headers.set("Accept-Encoding", "identity"); return headers; } diff --git a/tools/baishun-game-test.html b/tools/baishun-game-test.html index d5295855..f86e03cd 100644 --- a/tools/baishun-game-test.html +++ b/tools/baishun-game-test.html @@ -203,12 +203,16 @@ } catch { payload = { message: text }; } - if (!response.ok || payload.code !== 0) { + if (!response.ok || !isSuccessCode(payload.code)) { throw new Error(payload.message || response.statusText || `HTTP ${response.status}`); } return payload.data; } + function isSuccessCode(code) { + return code === undefined || code === null || code === 0 || code === "0" || code === 200 || code === "200" || code === "OK"; + } + function saveToken(data) { const accessToken = data?.access_token || data?.token?.access_token || ""; if (!accessToken) {