兼容百顺游戏

This commit is contained in:
zhx 2026-05-23 15:12:17 +08:00
parent d1a7facf10
commit 17d777610c
15 changed files with 321 additions and 36 deletions

View File

@ -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,
},

View File

@ -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);
}

View File

@ -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",

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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",

View File

@ -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),

View File

@ -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 的最小改账依赖。

View File

@ -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

View File

@ -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,

View File

@ -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)
}
}

View File

@ -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{

View File

@ -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;
}

View File

@ -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) {