游戏id修改

This commit is contained in:
zhx 2026-06-08 19:00:23 +08:00
parent dedf6e8382
commit d4eef547a8
86 changed files with 17164 additions and 19647 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1187,6 +1187,27 @@ message ConsumeLevelEventResponse {
int64 reward_job_count = 6;
}
message SetUserLevelRequest {
RequestMeta meta = 1;
string command_id = 2;
int64 user_id = 3;
string track = 4;
int32 level = 5;
int64 operator_user_id = 6;
string reason = 7;
}
message SetUserLevelResponse {
string event_id = 1;
string status = 2;
string track = 3;
int32 previous_level = 4;
int32 new_level = 5;
int64 total_value = 6;
int64 reward_job_count = 7;
int64 server_time_ms = 8;
}
message RegistrationLevelBadgeGrant {
string track = 1;
int32 level = 2;
@ -1844,6 +1865,7 @@ service GrowthLevelService {
rpc BatchGetUserLevelDisplayProfiles(BatchGetUserLevelDisplayProfilesRequest) returns (BatchGetUserLevelDisplayProfilesResponse);
rpc ListLevelRewards(ListLevelRewardsRequest) returns (ListLevelRewardsResponse);
rpc ConsumeLevelEvent(ConsumeLevelEventRequest) returns (ConsumeLevelEventResponse);
rpc SetUserLevel(SetUserLevelRequest) returns (SetUserLevelResponse);
rpc IssueRegistrationLevelBadges(IssueRegistrationLevelBadgesRequest) returns (IssueRegistrationLevelBadgesResponse);
}

View File

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.5.1
// - protoc-gen-go-grpc v1.6.2
// - protoc v7.35.0
// source: proto/activity/v1/activity.proto
@ -80,10 +80,10 @@ type ActivityServiceServer interface {
type UnimplementedActivityServiceServer struct{}
func (UnimplementedActivityServiceServer) PingActivity(context.Context, *PingActivityRequest) (*PingActivityResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method PingActivity not implemented")
return nil, status.Error(codes.Unimplemented, "method PingActivity not implemented")
}
func (UnimplementedActivityServiceServer) GetActivityStatus(context.Context, *GetActivityStatusRequest) (*GetActivityStatusResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetActivityStatus not implemented")
return nil, status.Error(codes.Unimplemented, "method GetActivityStatus not implemented")
}
func (UnimplementedActivityServiceServer) mustEmbedUnimplementedActivityServiceServer() {}
func (UnimplementedActivityServiceServer) testEmbeddedByValue() {}
@ -96,7 +96,7 @@ type UnsafeActivityServiceServer interface {
}
func RegisterActivityServiceServer(s grpc.ServiceRegistrar, srv ActivityServiceServer) {
// If the following call pancis, it indicates UnimplementedActivityServiceServer was
// If the following call panics, it indicates UnimplementedActivityServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
@ -289,25 +289,25 @@ type MessageInboxServiceServer interface {
type UnimplementedMessageInboxServiceServer struct{}
func (UnimplementedMessageInboxServiceServer) ListMessageTabs(context.Context, *ListMessageTabsRequest) (*ListMessageTabsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListMessageTabs not implemented")
return nil, status.Error(codes.Unimplemented, "method ListMessageTabs not implemented")
}
func (UnimplementedMessageInboxServiceServer) ListInboxMessages(context.Context, *ListInboxMessagesRequest) (*ListInboxMessagesResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListInboxMessages not implemented")
return nil, status.Error(codes.Unimplemented, "method ListInboxMessages not implemented")
}
func (UnimplementedMessageInboxServiceServer) MarkInboxMessageRead(context.Context, *MarkInboxMessageReadRequest) (*MarkInboxMessageReadResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method MarkInboxMessageRead not implemented")
return nil, status.Error(codes.Unimplemented, "method MarkInboxMessageRead not implemented")
}
func (UnimplementedMessageInboxServiceServer) MarkInboxSectionRead(context.Context, *MarkInboxSectionReadRequest) (*MarkInboxSectionReadResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method MarkInboxSectionRead not implemented")
return nil, status.Error(codes.Unimplemented, "method MarkInboxSectionRead not implemented")
}
func (UnimplementedMessageInboxServiceServer) DeleteInboxMessage(context.Context, *DeleteInboxMessageRequest) (*DeleteInboxMessageResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method DeleteInboxMessage not implemented")
return nil, status.Error(codes.Unimplemented, "method DeleteInboxMessage not implemented")
}
func (UnimplementedMessageInboxServiceServer) CreateInboxMessage(context.Context, *CreateInboxMessageRequest) (*CreateInboxMessageResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreateInboxMessage not implemented")
return nil, status.Error(codes.Unimplemented, "method CreateInboxMessage not implemented")
}
func (UnimplementedMessageInboxServiceServer) CreateFanoutJob(context.Context, *CreateFanoutJobRequest) (*CreateFanoutJobResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreateFanoutJob not implemented")
return nil, status.Error(codes.Unimplemented, "method CreateFanoutJob not implemented")
}
func (UnimplementedMessageInboxServiceServer) mustEmbedUnimplementedMessageInboxServiceServer() {}
func (UnimplementedMessageInboxServiceServer) testEmbeddedByValue() {}
@ -320,7 +320,7 @@ type UnsafeMessageInboxServiceServer interface {
}
func RegisterMessageInboxServiceServer(s grpc.ServiceRegistrar, srv MessageInboxServiceServer) {
// If the following call pancis, it indicates UnimplementedMessageInboxServiceServer was
// If the following call panics, it indicates UnimplementedMessageInboxServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
@ -597,19 +597,19 @@ type ActivityCronServiceServer interface {
type UnimplementedActivityCronServiceServer struct{}
func (UnimplementedActivityCronServiceServer) ProcessMessageFanoutBatch(context.Context, *CronBatchRequest) (*CronBatchResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ProcessMessageFanoutBatch not implemented")
return nil, status.Error(codes.Unimplemented, "method ProcessMessageFanoutBatch not implemented")
}
func (UnimplementedActivityCronServiceServer) ProcessLevelRewardBatch(context.Context, *CronBatchRequest) (*CronBatchResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ProcessLevelRewardBatch not implemented")
return nil, status.Error(codes.Unimplemented, "method ProcessLevelRewardBatch not implemented")
}
func (UnimplementedActivityCronServiceServer) ProcessAchievementRewardBatch(context.Context, *CronBatchRequest) (*CronBatchResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ProcessAchievementRewardBatch not implemented")
return nil, status.Error(codes.Unimplemented, "method ProcessAchievementRewardBatch not implemented")
}
func (UnimplementedActivityCronServiceServer) ProcessRoomTurnoverRewardSettlementBatch(context.Context, *CronBatchRequest) (*CronBatchResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ProcessRoomTurnoverRewardSettlementBatch not implemented")
return nil, status.Error(codes.Unimplemented, "method ProcessRoomTurnoverRewardSettlementBatch not implemented")
}
func (UnimplementedActivityCronServiceServer) ProcessWeeklyStarSettlementBatch(context.Context, *CronBatchRequest) (*CronBatchResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ProcessWeeklyStarSettlementBatch not implemented")
return nil, status.Error(codes.Unimplemented, "method ProcessWeeklyStarSettlementBatch not implemented")
}
func (UnimplementedActivityCronServiceServer) mustEmbedUnimplementedActivityCronServiceServer() {}
func (UnimplementedActivityCronServiceServer) testEmbeddedByValue() {}
@ -622,7 +622,7 @@ type UnsafeActivityCronServiceServer interface {
}
func RegisterActivityCronServiceServer(s grpc.ServiceRegistrar, srv ActivityCronServiceServer) {
// If the following call pancis, it indicates UnimplementedActivityCronServiceServer was
// If the following call panics, it indicates UnimplementedActivityCronServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
@ -829,13 +829,13 @@ type TaskServiceServer interface {
type UnimplementedTaskServiceServer struct{}
func (UnimplementedTaskServiceServer) ListUserTasks(context.Context, *ListUserTasksRequest) (*ListUserTasksResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListUserTasks not implemented")
return nil, status.Error(codes.Unimplemented, "method ListUserTasks not implemented")
}
func (UnimplementedTaskServiceServer) ClaimTaskReward(context.Context, *ClaimTaskRewardRequest) (*ClaimTaskRewardResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ClaimTaskReward not implemented")
return nil, status.Error(codes.Unimplemented, "method ClaimTaskReward not implemented")
}
func (UnimplementedTaskServiceServer) ConsumeTaskEvent(context.Context, *ConsumeTaskEventRequest) (*ConsumeTaskEventResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ConsumeTaskEvent not implemented")
return nil, status.Error(codes.Unimplemented, "method ConsumeTaskEvent not implemented")
}
func (UnimplementedTaskServiceServer) mustEmbedUnimplementedTaskServiceServer() {}
func (UnimplementedTaskServiceServer) testEmbeddedByValue() {}
@ -848,7 +848,7 @@ type UnsafeTaskServiceServer interface {
}
func RegisterTaskServiceServer(s grpc.ServiceRegistrar, srv TaskServiceServer) {
// If the following call pancis, it indicates UnimplementedTaskServiceServer was
// If the following call panics, it indicates UnimplementedTaskServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
@ -942,6 +942,7 @@ const (
GrowthLevelService_BatchGetUserLevelDisplayProfiles_FullMethodName = "/hyapp.activity.v1.GrowthLevelService/BatchGetUserLevelDisplayProfiles"
GrowthLevelService_ListLevelRewards_FullMethodName = "/hyapp.activity.v1.GrowthLevelService/ListLevelRewards"
GrowthLevelService_ConsumeLevelEvent_FullMethodName = "/hyapp.activity.v1.GrowthLevelService/ConsumeLevelEvent"
GrowthLevelService_SetUserLevel_FullMethodName = "/hyapp.activity.v1.GrowthLevelService/SetUserLevel"
GrowthLevelService_IssueRegistrationLevelBadges_FullMethodName = "/hyapp.activity.v1.GrowthLevelService/IssueRegistrationLevelBadges"
)
@ -956,6 +957,7 @@ type GrowthLevelServiceClient interface {
BatchGetUserLevelDisplayProfiles(ctx context.Context, in *BatchGetUserLevelDisplayProfilesRequest, opts ...grpc.CallOption) (*BatchGetUserLevelDisplayProfilesResponse, error)
ListLevelRewards(ctx context.Context, in *ListLevelRewardsRequest, opts ...grpc.CallOption) (*ListLevelRewardsResponse, error)
ConsumeLevelEvent(ctx context.Context, in *ConsumeLevelEventRequest, opts ...grpc.CallOption) (*ConsumeLevelEventResponse, error)
SetUserLevel(ctx context.Context, in *SetUserLevelRequest, opts ...grpc.CallOption) (*SetUserLevelResponse, error)
IssueRegistrationLevelBadges(ctx context.Context, in *IssueRegistrationLevelBadgesRequest, opts ...grpc.CallOption) (*IssueRegistrationLevelBadgesResponse, error)
}
@ -1017,6 +1019,16 @@ func (c *growthLevelServiceClient) ConsumeLevelEvent(ctx context.Context, in *Co
return out, nil
}
func (c *growthLevelServiceClient) SetUserLevel(ctx context.Context, in *SetUserLevelRequest, opts ...grpc.CallOption) (*SetUserLevelResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(SetUserLevelResponse)
err := c.cc.Invoke(ctx, GrowthLevelService_SetUserLevel_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *growthLevelServiceClient) IssueRegistrationLevelBadges(ctx context.Context, in *IssueRegistrationLevelBadgesRequest, opts ...grpc.CallOption) (*IssueRegistrationLevelBadgesResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(IssueRegistrationLevelBadgesResponse)
@ -1038,6 +1050,7 @@ type GrowthLevelServiceServer interface {
BatchGetUserLevelDisplayProfiles(context.Context, *BatchGetUserLevelDisplayProfilesRequest) (*BatchGetUserLevelDisplayProfilesResponse, error)
ListLevelRewards(context.Context, *ListLevelRewardsRequest) (*ListLevelRewardsResponse, error)
ConsumeLevelEvent(context.Context, *ConsumeLevelEventRequest) (*ConsumeLevelEventResponse, error)
SetUserLevel(context.Context, *SetUserLevelRequest) (*SetUserLevelResponse, error)
IssueRegistrationLevelBadges(context.Context, *IssueRegistrationLevelBadgesRequest) (*IssueRegistrationLevelBadgesResponse, error)
mustEmbedUnimplementedGrowthLevelServiceServer()
}
@ -1050,22 +1063,25 @@ type GrowthLevelServiceServer interface {
type UnimplementedGrowthLevelServiceServer struct{}
func (UnimplementedGrowthLevelServiceServer) GetMyLevelOverview(context.Context, *GetMyLevelOverviewRequest) (*GetMyLevelOverviewResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetMyLevelOverview not implemented")
return nil, status.Error(codes.Unimplemented, "method GetMyLevelOverview not implemented")
}
func (UnimplementedGrowthLevelServiceServer) GetLevelTrack(context.Context, *GetLevelTrackRequest) (*GetLevelTrackResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetLevelTrack not implemented")
return nil, status.Error(codes.Unimplemented, "method GetLevelTrack not implemented")
}
func (UnimplementedGrowthLevelServiceServer) BatchGetUserLevelDisplayProfiles(context.Context, *BatchGetUserLevelDisplayProfilesRequest) (*BatchGetUserLevelDisplayProfilesResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method BatchGetUserLevelDisplayProfiles not implemented")
return nil, status.Error(codes.Unimplemented, "method BatchGetUserLevelDisplayProfiles not implemented")
}
func (UnimplementedGrowthLevelServiceServer) ListLevelRewards(context.Context, *ListLevelRewardsRequest) (*ListLevelRewardsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListLevelRewards not implemented")
return nil, status.Error(codes.Unimplemented, "method ListLevelRewards not implemented")
}
func (UnimplementedGrowthLevelServiceServer) ConsumeLevelEvent(context.Context, *ConsumeLevelEventRequest) (*ConsumeLevelEventResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ConsumeLevelEvent not implemented")
return nil, status.Error(codes.Unimplemented, "method ConsumeLevelEvent not implemented")
}
func (UnimplementedGrowthLevelServiceServer) SetUserLevel(context.Context, *SetUserLevelRequest) (*SetUserLevelResponse, error) {
return nil, status.Error(codes.Unimplemented, "method SetUserLevel not implemented")
}
func (UnimplementedGrowthLevelServiceServer) IssueRegistrationLevelBadges(context.Context, *IssueRegistrationLevelBadgesRequest) (*IssueRegistrationLevelBadgesResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method IssueRegistrationLevelBadges not implemented")
return nil, status.Error(codes.Unimplemented, "method IssueRegistrationLevelBadges not implemented")
}
func (UnimplementedGrowthLevelServiceServer) mustEmbedUnimplementedGrowthLevelServiceServer() {}
func (UnimplementedGrowthLevelServiceServer) testEmbeddedByValue() {}
@ -1078,7 +1094,7 @@ type UnsafeGrowthLevelServiceServer interface {
}
func RegisterGrowthLevelServiceServer(s grpc.ServiceRegistrar, srv GrowthLevelServiceServer) {
// If the following call pancis, it indicates UnimplementedGrowthLevelServiceServer was
// If the following call panics, it indicates UnimplementedGrowthLevelServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
@ -1178,6 +1194,24 @@ func _GrowthLevelService_ConsumeLevelEvent_Handler(srv interface{}, ctx context.
return interceptor(ctx, in, info, handler)
}
func _GrowthLevelService_SetUserLevel_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(SetUserLevelRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(GrowthLevelServiceServer).SetUserLevel(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: GrowthLevelService_SetUserLevel_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(GrowthLevelServiceServer).SetUserLevel(ctx, req.(*SetUserLevelRequest))
}
return interceptor(ctx, in, info, handler)
}
func _GrowthLevelService_IssueRegistrationLevelBadges_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(IssueRegistrationLevelBadgesRequest)
if err := dec(in); err != nil {
@ -1223,6 +1257,10 @@ var GrowthLevelService_ServiceDesc = grpc.ServiceDesc{
MethodName: "ConsumeLevelEvent",
Handler: _GrowthLevelService_ConsumeLevelEvent_Handler,
},
{
MethodName: "SetUserLevel",
Handler: _GrowthLevelService_SetUserLevel_Handler,
},
{
MethodName: "IssueRegistrationLevelBadges",
Handler: _GrowthLevelService_IssueRegistrationLevelBadges_Handler,
@ -1320,16 +1358,16 @@ type AchievementServiceServer interface {
type UnimplementedAchievementServiceServer struct{}
func (UnimplementedAchievementServiceServer) ListAchievements(context.Context, *ListAchievementsRequest) (*ListAchievementsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListAchievements not implemented")
return nil, status.Error(codes.Unimplemented, "method ListAchievements not implemented")
}
func (UnimplementedAchievementServiceServer) ConsumeAchievementEvent(context.Context, *ConsumeAchievementEventRequest) (*ConsumeAchievementEventResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ConsumeAchievementEvent not implemented")
return nil, status.Error(codes.Unimplemented, "method ConsumeAchievementEvent not implemented")
}
func (UnimplementedAchievementServiceServer) ListMyBadges(context.Context, *ListMyBadgesRequest) (*ListMyBadgesResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListMyBadges not implemented")
return nil, status.Error(codes.Unimplemented, "method ListMyBadges not implemented")
}
func (UnimplementedAchievementServiceServer) SetBadgeDisplay(context.Context, *SetBadgeDisplayRequest) (*SetBadgeDisplayResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method SetBadgeDisplay not implemented")
return nil, status.Error(codes.Unimplemented, "method SetBadgeDisplay not implemented")
}
func (UnimplementedAchievementServiceServer) mustEmbedUnimplementedAchievementServiceServer() {}
func (UnimplementedAchievementServiceServer) testEmbeddedByValue() {}
@ -1342,7 +1380,7 @@ type UnsafeAchievementServiceServer interface {
}
func RegisterAchievementServiceServer(s grpc.ServiceRegistrar, srv AchievementServiceServer) {
// If the following call pancis, it indicates UnimplementedAchievementServiceServer was
// If the following call panics, it indicates UnimplementedAchievementServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
@ -1514,10 +1552,10 @@ type LuckyGiftServiceServer interface {
type UnimplementedLuckyGiftServiceServer struct{}
func (UnimplementedLuckyGiftServiceServer) CheckLuckyGift(context.Context, *CheckLuckyGiftRequest) (*CheckLuckyGiftResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method CheckLuckyGift not implemented")
return nil, status.Error(codes.Unimplemented, "method CheckLuckyGift not implemented")
}
func (UnimplementedLuckyGiftServiceServer) ExecuteLuckyGiftDraw(context.Context, *ExecuteLuckyGiftDrawRequest) (*ExecuteLuckyGiftDrawResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ExecuteLuckyGiftDraw not implemented")
return nil, status.Error(codes.Unimplemented, "method ExecuteLuckyGiftDraw not implemented")
}
func (UnimplementedLuckyGiftServiceServer) mustEmbedUnimplementedLuckyGiftServiceServer() {}
func (UnimplementedLuckyGiftServiceServer) testEmbeddedByValue() {}
@ -1530,7 +1568,7 @@ type UnsafeLuckyGiftServiceServer interface {
}
func RegisterLuckyGiftServiceServer(s grpc.ServiceRegistrar, srv LuckyGiftServiceServer) {
// If the following call pancis, it indicates UnimplementedLuckyGiftServiceServer was
// If the following call panics, it indicates UnimplementedLuckyGiftServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
@ -1645,7 +1683,7 @@ type RoomTurnoverRewardServiceServer interface {
type UnimplementedRoomTurnoverRewardServiceServer struct{}
func (UnimplementedRoomTurnoverRewardServiceServer) GetRoomTurnoverRewardStatus(context.Context, *GetRoomTurnoverRewardStatusRequest) (*GetRoomTurnoverRewardStatusResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetRoomTurnoverRewardStatus not implemented")
return nil, status.Error(codes.Unimplemented, "method GetRoomTurnoverRewardStatus not implemented")
}
func (UnimplementedRoomTurnoverRewardServiceServer) mustEmbedUnimplementedRoomTurnoverRewardServiceServer() {
}
@ -1659,7 +1697,7 @@ type UnsafeRoomTurnoverRewardServiceServer interface {
}
func RegisterRoomTurnoverRewardServiceServer(s grpc.ServiceRegistrar, srv RoomTurnoverRewardServiceServer) {
// If the following call pancis, it indicates UnimplementedRoomTurnoverRewardServiceServer was
// If the following call panics, it indicates UnimplementedRoomTurnoverRewardServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
@ -1778,13 +1816,13 @@ type WeeklyStarServiceServer interface {
type UnimplementedWeeklyStarServiceServer struct{}
func (UnimplementedWeeklyStarServiceServer) GetWeeklyStarCurrent(context.Context, *GetWeeklyStarCurrentRequest) (*GetWeeklyStarCurrentResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetWeeklyStarCurrent not implemented")
return nil, status.Error(codes.Unimplemented, "method GetWeeklyStarCurrent not implemented")
}
func (UnimplementedWeeklyStarServiceServer) ListWeeklyStarLeaderboard(context.Context, *ListWeeklyStarLeaderboardRequest) (*ListWeeklyStarLeaderboardResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListWeeklyStarLeaderboard not implemented")
return nil, status.Error(codes.Unimplemented, "method ListWeeklyStarLeaderboard not implemented")
}
func (UnimplementedWeeklyStarServiceServer) ListWeeklyStarHistory(context.Context, *ListWeeklyStarHistoryRequest) (*ListWeeklyStarHistoryResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListWeeklyStarHistory not implemented")
return nil, status.Error(codes.Unimplemented, "method ListWeeklyStarHistory not implemented")
}
func (UnimplementedWeeklyStarServiceServer) mustEmbedUnimplementedWeeklyStarServiceServer() {}
func (UnimplementedWeeklyStarServiceServer) testEmbeddedByValue() {}
@ -1797,7 +1835,7 @@ type UnsafeWeeklyStarServiceServer interface {
}
func RegisterWeeklyStarServiceServer(s grpc.ServiceRegistrar, srv WeeklyStarServiceServer) {
// If the following call pancis, it indicates UnimplementedWeeklyStarServiceServer was
// If the following call panics, it indicates UnimplementedWeeklyStarServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
@ -1986,19 +2024,19 @@ type BroadcastServiceServer interface {
type UnimplementedBroadcastServiceServer struct{}
func (UnimplementedBroadcastServiceServer) EnsureBroadcastGroups(context.Context, *EnsureBroadcastGroupsRequest) (*EnsureBroadcastGroupsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method EnsureBroadcastGroups not implemented")
return nil, status.Error(codes.Unimplemented, "method EnsureBroadcastGroups not implemented")
}
func (UnimplementedBroadcastServiceServer) PublishRegionBroadcast(context.Context, *PublishRegionBroadcastRequest) (*PublishBroadcastResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method PublishRegionBroadcast not implemented")
return nil, status.Error(codes.Unimplemented, "method PublishRegionBroadcast not implemented")
}
func (UnimplementedBroadcastServiceServer) PublishGlobalBroadcast(context.Context, *PublishGlobalBroadcastRequest) (*PublishBroadcastResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method PublishGlobalBroadcast not implemented")
return nil, status.Error(codes.Unimplemented, "method PublishGlobalBroadcast not implemented")
}
func (UnimplementedBroadcastServiceServer) RemoveRegionBroadcastMember(context.Context, *RemoveRegionBroadcastMemberRequest) (*RemoveRegionBroadcastMemberResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method RemoveRegionBroadcastMember not implemented")
return nil, status.Error(codes.Unimplemented, "method RemoveRegionBroadcastMember not implemented")
}
func (UnimplementedBroadcastServiceServer) ProcessBroadcastOutboxBatch(context.Context, *CronBatchRequest) (*ProcessBroadcastOutboxBatchResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ProcessBroadcastOutboxBatch not implemented")
return nil, status.Error(codes.Unimplemented, "method ProcessBroadcastOutboxBatch not implemented")
}
func (UnimplementedBroadcastServiceServer) mustEmbedUnimplementedBroadcastServiceServer() {}
func (UnimplementedBroadcastServiceServer) testEmbeddedByValue() {}
@ -2011,7 +2049,7 @@ type UnsafeBroadcastServiceServer interface {
}
func RegisterBroadcastServiceServer(s grpc.ServiceRegistrar, srv BroadcastServiceServer) {
// If the following call pancis, it indicates UnimplementedBroadcastServiceServer was
// If the following call panics, it indicates UnimplementedBroadcastServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
@ -2192,7 +2230,7 @@ type RoomEventConsumerServiceServer interface {
type UnimplementedRoomEventConsumerServiceServer struct{}
func (UnimplementedRoomEventConsumerServiceServer) ConsumeRoomEvent(context.Context, *ConsumeRoomEventRequest) (*ConsumeRoomEventResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ConsumeRoomEvent not implemented")
return nil, status.Error(codes.Unimplemented, "method ConsumeRoomEvent not implemented")
}
func (UnimplementedRoomEventConsumerServiceServer) mustEmbedUnimplementedRoomEventConsumerServiceServer() {
}
@ -2206,7 +2244,7 @@ type UnsafeRoomEventConsumerServiceServer interface {
}
func RegisterRoomEventConsumerServiceServer(s grpc.ServiceRegistrar, srv RoomEventConsumerServiceServer) {
// If the following call pancis, it indicates UnimplementedRoomEventConsumerServiceServer was
// If the following call panics, it indicates UnimplementedRoomEventConsumerServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
@ -2325,13 +2363,13 @@ type AdminTaskServiceServer interface {
type UnimplementedAdminTaskServiceServer struct{}
func (UnimplementedAdminTaskServiceServer) ListTaskDefinitions(context.Context, *ListTaskDefinitionsRequest) (*ListTaskDefinitionsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListTaskDefinitions not implemented")
return nil, status.Error(codes.Unimplemented, "method ListTaskDefinitions not implemented")
}
func (UnimplementedAdminTaskServiceServer) UpsertTaskDefinition(context.Context, *UpsertTaskDefinitionRequest) (*UpsertTaskDefinitionResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method UpsertTaskDefinition not implemented")
return nil, status.Error(codes.Unimplemented, "method UpsertTaskDefinition not implemented")
}
func (UnimplementedAdminTaskServiceServer) SetTaskDefinitionStatus(context.Context, *SetTaskDefinitionStatusRequest) (*SetTaskDefinitionStatusResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method SetTaskDefinitionStatus not implemented")
return nil, status.Error(codes.Unimplemented, "method SetTaskDefinitionStatus not implemented")
}
func (UnimplementedAdminTaskServiceServer) mustEmbedUnimplementedAdminTaskServiceServer() {}
func (UnimplementedAdminTaskServiceServer) testEmbeddedByValue() {}
@ -2344,7 +2382,7 @@ type UnsafeAdminTaskServiceServer interface {
}
func RegisterAdminTaskServiceServer(s grpc.ServiceRegistrar, srv AdminTaskServiceServer) {
// If the following call pancis, it indicates UnimplementedAdminTaskServiceServer was
// If the following call panics, it indicates UnimplementedAdminTaskServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
@ -2494,10 +2532,10 @@ type RegistrationRewardServiceServer interface {
type UnimplementedRegistrationRewardServiceServer struct{}
func (UnimplementedRegistrationRewardServiceServer) GetRegistrationRewardEligibility(context.Context, *GetRegistrationRewardEligibilityRequest) (*GetRegistrationRewardEligibilityResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetRegistrationRewardEligibility not implemented")
return nil, status.Error(codes.Unimplemented, "method GetRegistrationRewardEligibility not implemented")
}
func (UnimplementedRegistrationRewardServiceServer) IssueRegistrationReward(context.Context, *IssueRegistrationRewardRequest) (*IssueRegistrationRewardResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method IssueRegistrationReward not implemented")
return nil, status.Error(codes.Unimplemented, "method IssueRegistrationReward not implemented")
}
func (UnimplementedRegistrationRewardServiceServer) mustEmbedUnimplementedRegistrationRewardServiceServer() {
}
@ -2511,7 +2549,7 @@ type UnsafeRegistrationRewardServiceServer interface {
}
func RegisterRegistrationRewardServiceServer(s grpc.ServiceRegistrar, srv RegistrationRewardServiceServer) {
// If the following call pancis, it indicates UnimplementedRegistrationRewardServiceServer was
// If the following call panics, it indicates UnimplementedRegistrationRewardServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
@ -2652,13 +2690,13 @@ type AdminRegistrationRewardServiceServer interface {
type UnimplementedAdminRegistrationRewardServiceServer struct{}
func (UnimplementedAdminRegistrationRewardServiceServer) GetRegistrationRewardConfig(context.Context, *GetRegistrationRewardConfigRequest) (*GetRegistrationRewardConfigResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetRegistrationRewardConfig not implemented")
return nil, status.Error(codes.Unimplemented, "method GetRegistrationRewardConfig not implemented")
}
func (UnimplementedAdminRegistrationRewardServiceServer) UpdateRegistrationRewardConfig(context.Context, *UpdateRegistrationRewardConfigRequest) (*UpdateRegistrationRewardConfigResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method UpdateRegistrationRewardConfig not implemented")
return nil, status.Error(codes.Unimplemented, "method UpdateRegistrationRewardConfig not implemented")
}
func (UnimplementedAdminRegistrationRewardServiceServer) ListRegistrationRewardClaims(context.Context, *ListRegistrationRewardClaimsRequest) (*ListRegistrationRewardClaimsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListRegistrationRewardClaims not implemented")
return nil, status.Error(codes.Unimplemented, "method ListRegistrationRewardClaims not implemented")
}
func (UnimplementedAdminRegistrationRewardServiceServer) mustEmbedUnimplementedAdminRegistrationRewardServiceServer() {
}
@ -2672,7 +2710,7 @@ type UnsafeAdminRegistrationRewardServiceServer interface {
}
func RegisterAdminRegistrationRewardServiceServer(s grpc.ServiceRegistrar, srv AdminRegistrationRewardServiceServer) {
// If the following call pancis, it indicates UnimplementedAdminRegistrationRewardServiceServer was
// If the following call panics, it indicates UnimplementedAdminRegistrationRewardServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
@ -2822,10 +2860,10 @@ type FirstRechargeRewardServiceServer interface {
type UnimplementedFirstRechargeRewardServiceServer struct{}
func (UnimplementedFirstRechargeRewardServiceServer) GetFirstRechargeRewardStatus(context.Context, *GetFirstRechargeRewardStatusRequest) (*GetFirstRechargeRewardStatusResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetFirstRechargeRewardStatus not implemented")
return nil, status.Error(codes.Unimplemented, "method GetFirstRechargeRewardStatus not implemented")
}
func (UnimplementedFirstRechargeRewardServiceServer) ConsumeFirstRechargeReward(context.Context, *ConsumeFirstRechargeRewardRequest) (*ConsumeFirstRechargeRewardResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ConsumeFirstRechargeReward not implemented")
return nil, status.Error(codes.Unimplemented, "method ConsumeFirstRechargeReward not implemented")
}
func (UnimplementedFirstRechargeRewardServiceServer) mustEmbedUnimplementedFirstRechargeRewardServiceServer() {
}
@ -2839,7 +2877,7 @@ type UnsafeFirstRechargeRewardServiceServer interface {
}
func RegisterFirstRechargeRewardServiceServer(s grpc.ServiceRegistrar, srv FirstRechargeRewardServiceServer) {
// If the following call pancis, it indicates UnimplementedFirstRechargeRewardServiceServer was
// If the following call panics, it indicates UnimplementedFirstRechargeRewardServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
@ -2980,13 +3018,13 @@ type AdminFirstRechargeRewardServiceServer interface {
type UnimplementedAdminFirstRechargeRewardServiceServer struct{}
func (UnimplementedAdminFirstRechargeRewardServiceServer) GetFirstRechargeRewardConfig(context.Context, *GetFirstRechargeRewardConfigRequest) (*GetFirstRechargeRewardConfigResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetFirstRechargeRewardConfig not implemented")
return nil, status.Error(codes.Unimplemented, "method GetFirstRechargeRewardConfig not implemented")
}
func (UnimplementedAdminFirstRechargeRewardServiceServer) UpdateFirstRechargeRewardConfig(context.Context, *UpdateFirstRechargeRewardConfigRequest) (*UpdateFirstRechargeRewardConfigResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method UpdateFirstRechargeRewardConfig not implemented")
return nil, status.Error(codes.Unimplemented, "method UpdateFirstRechargeRewardConfig not implemented")
}
func (UnimplementedAdminFirstRechargeRewardServiceServer) ListFirstRechargeRewardClaims(context.Context, *ListFirstRechargeRewardClaimsRequest) (*ListFirstRechargeRewardClaimsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListFirstRechargeRewardClaims not implemented")
return nil, status.Error(codes.Unimplemented, "method ListFirstRechargeRewardClaims not implemented")
}
func (UnimplementedAdminFirstRechargeRewardServiceServer) mustEmbedUnimplementedAdminFirstRechargeRewardServiceServer() {
}
@ -3000,7 +3038,7 @@ type UnsafeAdminFirstRechargeRewardServiceServer interface {
}
func RegisterAdminFirstRechargeRewardServiceServer(s grpc.ServiceRegistrar, srv AdminFirstRechargeRewardServiceServer) {
// If the following call pancis, it indicates UnimplementedAdminFirstRechargeRewardServiceServer was
// If the following call panics, it indicates UnimplementedAdminFirstRechargeRewardServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
@ -3150,10 +3188,10 @@ type CumulativeRechargeRewardServiceServer interface {
type UnimplementedCumulativeRechargeRewardServiceServer struct{}
func (UnimplementedCumulativeRechargeRewardServiceServer) GetCumulativeRechargeRewardStatus(context.Context, *GetCumulativeRechargeRewardStatusRequest) (*GetCumulativeRechargeRewardStatusResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetCumulativeRechargeRewardStatus not implemented")
return nil, status.Error(codes.Unimplemented, "method GetCumulativeRechargeRewardStatus not implemented")
}
func (UnimplementedCumulativeRechargeRewardServiceServer) ConsumeCumulativeRechargeReward(context.Context, *ConsumeCumulativeRechargeRewardRequest) (*ConsumeCumulativeRechargeRewardResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ConsumeCumulativeRechargeReward not implemented")
return nil, status.Error(codes.Unimplemented, "method ConsumeCumulativeRechargeReward not implemented")
}
func (UnimplementedCumulativeRechargeRewardServiceServer) mustEmbedUnimplementedCumulativeRechargeRewardServiceServer() {
}
@ -3167,7 +3205,7 @@ type UnsafeCumulativeRechargeRewardServiceServer interface {
}
func RegisterCumulativeRechargeRewardServiceServer(s grpc.ServiceRegistrar, srv CumulativeRechargeRewardServiceServer) {
// If the following call pancis, it indicates UnimplementedCumulativeRechargeRewardServiceServer was
// If the following call panics, it indicates UnimplementedCumulativeRechargeRewardServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
@ -3308,13 +3346,13 @@ type AdminCumulativeRechargeRewardServiceServer interface {
type UnimplementedAdminCumulativeRechargeRewardServiceServer struct{}
func (UnimplementedAdminCumulativeRechargeRewardServiceServer) GetCumulativeRechargeRewardConfig(context.Context, *GetCumulativeRechargeRewardConfigRequest) (*GetCumulativeRechargeRewardConfigResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetCumulativeRechargeRewardConfig not implemented")
return nil, status.Error(codes.Unimplemented, "method GetCumulativeRechargeRewardConfig not implemented")
}
func (UnimplementedAdminCumulativeRechargeRewardServiceServer) UpdateCumulativeRechargeRewardConfig(context.Context, *UpdateCumulativeRechargeRewardConfigRequest) (*UpdateCumulativeRechargeRewardConfigResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method UpdateCumulativeRechargeRewardConfig not implemented")
return nil, status.Error(codes.Unimplemented, "method UpdateCumulativeRechargeRewardConfig not implemented")
}
func (UnimplementedAdminCumulativeRechargeRewardServiceServer) ListCumulativeRechargeRewardGrants(context.Context, *ListCumulativeRechargeRewardGrantsRequest) (*ListCumulativeRechargeRewardGrantsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListCumulativeRechargeRewardGrants not implemented")
return nil, status.Error(codes.Unimplemented, "method ListCumulativeRechargeRewardGrants not implemented")
}
func (UnimplementedAdminCumulativeRechargeRewardServiceServer) mustEmbedUnimplementedAdminCumulativeRechargeRewardServiceServer() {
}
@ -3328,7 +3366,7 @@ type UnsafeAdminCumulativeRechargeRewardServiceServer interface {
}
func RegisterAdminCumulativeRechargeRewardServiceServer(s grpc.ServiceRegistrar, srv AdminCumulativeRechargeRewardServiceServer) {
// If the following call pancis, it indicates UnimplementedAdminCumulativeRechargeRewardServiceServer was
// If the following call panics, it indicates UnimplementedAdminCumulativeRechargeRewardServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
@ -3504,16 +3542,16 @@ type AdminRoomTurnoverRewardServiceServer interface {
type UnimplementedAdminRoomTurnoverRewardServiceServer struct{}
func (UnimplementedAdminRoomTurnoverRewardServiceServer) GetRoomTurnoverRewardConfig(context.Context, *GetRoomTurnoverRewardConfigRequest) (*GetRoomTurnoverRewardConfigResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetRoomTurnoverRewardConfig not implemented")
return nil, status.Error(codes.Unimplemented, "method GetRoomTurnoverRewardConfig not implemented")
}
func (UnimplementedAdminRoomTurnoverRewardServiceServer) UpdateRoomTurnoverRewardConfig(context.Context, *UpdateRoomTurnoverRewardConfigRequest) (*UpdateRoomTurnoverRewardConfigResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method UpdateRoomTurnoverRewardConfig not implemented")
return nil, status.Error(codes.Unimplemented, "method UpdateRoomTurnoverRewardConfig not implemented")
}
func (UnimplementedAdminRoomTurnoverRewardServiceServer) ListRoomTurnoverRewardSettlements(context.Context, *ListRoomTurnoverRewardSettlementsRequest) (*ListRoomTurnoverRewardSettlementsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListRoomTurnoverRewardSettlements not implemented")
return nil, status.Error(codes.Unimplemented, "method ListRoomTurnoverRewardSettlements not implemented")
}
func (UnimplementedAdminRoomTurnoverRewardServiceServer) RetryRoomTurnoverRewardSettlement(context.Context, *RetryRoomTurnoverRewardSettlementRequest) (*RetryRoomTurnoverRewardSettlementResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method RetryRoomTurnoverRewardSettlement not implemented")
return nil, status.Error(codes.Unimplemented, "method RetryRoomTurnoverRewardSettlement not implemented")
}
func (UnimplementedAdminRoomTurnoverRewardServiceServer) mustEmbedUnimplementedAdminRoomTurnoverRewardServiceServer() {
}
@ -3527,7 +3565,7 @@ type UnsafeAdminRoomTurnoverRewardServiceServer interface {
}
func RegisterAdminRoomTurnoverRewardServiceServer(s grpc.ServiceRegistrar, srv AdminRoomTurnoverRewardServiceServer) {
// If the following call pancis, it indicates UnimplementedAdminRoomTurnoverRewardServiceServer was
// If the following call panics, it indicates UnimplementedAdminRoomTurnoverRewardServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
@ -3764,25 +3802,25 @@ type AdminWeeklyStarServiceServer interface {
type UnimplementedAdminWeeklyStarServiceServer struct{}
func (UnimplementedAdminWeeklyStarServiceServer) ListWeeklyStarCycles(context.Context, *ListWeeklyStarCyclesRequest) (*ListWeeklyStarCyclesResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListWeeklyStarCycles not implemented")
return nil, status.Error(codes.Unimplemented, "method ListWeeklyStarCycles not implemented")
}
func (UnimplementedAdminWeeklyStarServiceServer) CreateWeeklyStarCycle(context.Context, *UpsertWeeklyStarCycleRequest) (*UpsertWeeklyStarCycleResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreateWeeklyStarCycle not implemented")
return nil, status.Error(codes.Unimplemented, "method CreateWeeklyStarCycle not implemented")
}
func (UnimplementedAdminWeeklyStarServiceServer) GetWeeklyStarCycle(context.Context, *GetWeeklyStarCycleRequest) (*GetWeeklyStarCycleResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetWeeklyStarCycle not implemented")
return nil, status.Error(codes.Unimplemented, "method GetWeeklyStarCycle not implemented")
}
func (UnimplementedAdminWeeklyStarServiceServer) UpdateWeeklyStarCycle(context.Context, *UpsertWeeklyStarCycleRequest) (*UpsertWeeklyStarCycleResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method UpdateWeeklyStarCycle not implemented")
return nil, status.Error(codes.Unimplemented, "method UpdateWeeklyStarCycle not implemented")
}
func (UnimplementedAdminWeeklyStarServiceServer) SetWeeklyStarCycleStatus(context.Context, *SetWeeklyStarCycleStatusRequest) (*SetWeeklyStarCycleStatusResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method SetWeeklyStarCycleStatus not implemented")
return nil, status.Error(codes.Unimplemented, "method SetWeeklyStarCycleStatus not implemented")
}
func (UnimplementedAdminWeeklyStarServiceServer) ListWeeklyStarLeaderboard(context.Context, *ListWeeklyStarLeaderboardRequest) (*ListWeeklyStarLeaderboardResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListWeeklyStarLeaderboard not implemented")
return nil, status.Error(codes.Unimplemented, "method ListWeeklyStarLeaderboard not implemented")
}
func (UnimplementedAdminWeeklyStarServiceServer) ListWeeklyStarSettlements(context.Context, *ListWeeklyStarSettlementsRequest) (*ListWeeklyStarSettlementsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListWeeklyStarSettlements not implemented")
return nil, status.Error(codes.Unimplemented, "method ListWeeklyStarSettlements not implemented")
}
func (UnimplementedAdminWeeklyStarServiceServer) mustEmbedUnimplementedAdminWeeklyStarServiceServer() {
}
@ -3796,7 +3834,7 @@ type UnsafeAdminWeeklyStarServiceServer interface {
}
func RegisterAdminWeeklyStarServiceServer(s grpc.ServiceRegistrar, srv AdminWeeklyStarServiceServer) {
// If the following call pancis, it indicates UnimplementedAdminWeeklyStarServiceServer was
// If the following call panics, it indicates UnimplementedAdminWeeklyStarServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
@ -4034,10 +4072,10 @@ type SevenDayCheckInServiceServer interface {
type UnimplementedSevenDayCheckInServiceServer struct{}
func (UnimplementedSevenDayCheckInServiceServer) GetSevenDayCheckInStatus(context.Context, *GetSevenDayCheckInStatusRequest) (*GetSevenDayCheckInStatusResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetSevenDayCheckInStatus not implemented")
return nil, status.Error(codes.Unimplemented, "method GetSevenDayCheckInStatus not implemented")
}
func (UnimplementedSevenDayCheckInServiceServer) SignSevenDayCheckIn(context.Context, *SignSevenDayCheckInRequest) (*SignSevenDayCheckInResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method SignSevenDayCheckIn not implemented")
return nil, status.Error(codes.Unimplemented, "method SignSevenDayCheckIn not implemented")
}
func (UnimplementedSevenDayCheckInServiceServer) mustEmbedUnimplementedSevenDayCheckInServiceServer() {
}
@ -4051,7 +4089,7 @@ type UnsafeSevenDayCheckInServiceServer interface {
}
func RegisterSevenDayCheckInServiceServer(s grpc.ServiceRegistrar, srv SevenDayCheckInServiceServer) {
// If the following call pancis, it indicates UnimplementedSevenDayCheckInServiceServer was
// If the following call panics, it indicates UnimplementedSevenDayCheckInServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
@ -4192,13 +4230,13 @@ type AdminSevenDayCheckInServiceServer interface {
type UnimplementedAdminSevenDayCheckInServiceServer struct{}
func (UnimplementedAdminSevenDayCheckInServiceServer) GetSevenDayCheckInConfig(context.Context, *GetSevenDayCheckInConfigRequest) (*GetSevenDayCheckInConfigResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetSevenDayCheckInConfig not implemented")
return nil, status.Error(codes.Unimplemented, "method GetSevenDayCheckInConfig not implemented")
}
func (UnimplementedAdminSevenDayCheckInServiceServer) UpdateSevenDayCheckInConfig(context.Context, *UpdateSevenDayCheckInConfigRequest) (*UpdateSevenDayCheckInConfigResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method UpdateSevenDayCheckInConfig not implemented")
return nil, status.Error(codes.Unimplemented, "method UpdateSevenDayCheckInConfig not implemented")
}
func (UnimplementedAdminSevenDayCheckInServiceServer) ListSevenDayCheckInClaims(context.Context, *ListSevenDayCheckInClaimsRequest) (*ListSevenDayCheckInClaimsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListSevenDayCheckInClaims not implemented")
return nil, status.Error(codes.Unimplemented, "method ListSevenDayCheckInClaims not implemented")
}
func (UnimplementedAdminSevenDayCheckInServiceServer) mustEmbedUnimplementedAdminSevenDayCheckInServiceServer() {
}
@ -4212,7 +4250,7 @@ type UnsafeAdminSevenDayCheckInServiceServer interface {
}
func RegisterAdminSevenDayCheckInServiceServer(s grpc.ServiceRegistrar, srv AdminSevenDayCheckInServiceServer) {
// If the following call pancis, it indicates UnimplementedAdminSevenDayCheckInServiceServer was
// If the following call panics, it indicates UnimplementedAdminSevenDayCheckInServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
@ -4388,16 +4426,16 @@ type AdminGrowthLevelServiceServer interface {
type UnimplementedAdminGrowthLevelServiceServer struct{}
func (UnimplementedAdminGrowthLevelServiceServer) ListLevelConfig(context.Context, *ListLevelConfigRequest) (*ListLevelConfigResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListLevelConfig not implemented")
return nil, status.Error(codes.Unimplemented, "method ListLevelConfig not implemented")
}
func (UnimplementedAdminGrowthLevelServiceServer) UpsertLevelTrack(context.Context, *UpsertLevelTrackRequest) (*UpsertLevelTrackResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method UpsertLevelTrack not implemented")
return nil, status.Error(codes.Unimplemented, "method UpsertLevelTrack not implemented")
}
func (UnimplementedAdminGrowthLevelServiceServer) UpsertLevelRule(context.Context, *UpsertLevelRuleRequest) (*UpsertLevelRuleResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method UpsertLevelRule not implemented")
return nil, status.Error(codes.Unimplemented, "method UpsertLevelRule not implemented")
}
func (UnimplementedAdminGrowthLevelServiceServer) UpsertLevelTier(context.Context, *UpsertLevelTierRequest) (*UpsertLevelTierResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method UpsertLevelTier not implemented")
return nil, status.Error(codes.Unimplemented, "method UpsertLevelTier not implemented")
}
func (UnimplementedAdminGrowthLevelServiceServer) mustEmbedUnimplementedAdminGrowthLevelServiceServer() {
}
@ -4411,7 +4449,7 @@ type UnsafeAdminGrowthLevelServiceServer interface {
}
func RegisterAdminGrowthLevelServiceServer(s grpc.ServiceRegistrar, srv AdminGrowthLevelServiceServer) {
// If the following call pancis, it indicates UnimplementedAdminGrowthLevelServiceServer was
// If the following call panics, it indicates UnimplementedAdminGrowthLevelServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
@ -4583,10 +4621,10 @@ type AdminAchievementServiceServer interface {
type UnimplementedAdminAchievementServiceServer struct{}
func (UnimplementedAdminAchievementServiceServer) ListAchievementDefinitions(context.Context, *ListAchievementsRequest) (*ListAchievementsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListAchievementDefinitions not implemented")
return nil, status.Error(codes.Unimplemented, "method ListAchievementDefinitions not implemented")
}
func (UnimplementedAdminAchievementServiceServer) UpsertAchievementDefinition(context.Context, *UpsertAchievementDefinitionRequest) (*UpsertAchievementDefinitionResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method UpsertAchievementDefinition not implemented")
return nil, status.Error(codes.Unimplemented, "method UpsertAchievementDefinition not implemented")
}
func (UnimplementedAdminAchievementServiceServer) mustEmbedUnimplementedAdminAchievementServiceServer() {
}
@ -4600,7 +4638,7 @@ type UnsafeAdminAchievementServiceServer interface {
}
func RegisterAdminAchievementServiceServer(s grpc.ServiceRegistrar, srv AdminAchievementServiceServer) {
// If the following call pancis, it indicates UnimplementedAdminAchievementServiceServer was
// If the following call panics, it indicates UnimplementedAdminAchievementServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
@ -4767,19 +4805,19 @@ type AdminLuckyGiftServiceServer interface {
type UnimplementedAdminLuckyGiftServiceServer struct{}
func (UnimplementedAdminLuckyGiftServiceServer) GetLuckyGiftConfig(context.Context, *GetLuckyGiftConfigRequest) (*GetLuckyGiftConfigResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetLuckyGiftConfig not implemented")
return nil, status.Error(codes.Unimplemented, "method GetLuckyGiftConfig not implemented")
}
func (UnimplementedAdminLuckyGiftServiceServer) UpsertLuckyGiftConfig(context.Context, *UpsertLuckyGiftConfigRequest) (*UpsertLuckyGiftConfigResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method UpsertLuckyGiftConfig not implemented")
return nil, status.Error(codes.Unimplemented, "method UpsertLuckyGiftConfig not implemented")
}
func (UnimplementedAdminLuckyGiftServiceServer) ListLuckyGiftConfigs(context.Context, *ListLuckyGiftConfigsRequest) (*ListLuckyGiftConfigsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListLuckyGiftConfigs not implemented")
return nil, status.Error(codes.Unimplemented, "method ListLuckyGiftConfigs not implemented")
}
func (UnimplementedAdminLuckyGiftServiceServer) ListLuckyGiftDraws(context.Context, *ListLuckyGiftDrawsRequest) (*ListLuckyGiftDrawsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListLuckyGiftDraws not implemented")
return nil, status.Error(codes.Unimplemented, "method ListLuckyGiftDraws not implemented")
}
func (UnimplementedAdminLuckyGiftServiceServer) GetLuckyGiftDrawSummary(context.Context, *GetLuckyGiftDrawSummaryRequest) (*GetLuckyGiftDrawSummaryResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetLuckyGiftDrawSummary not implemented")
return nil, status.Error(codes.Unimplemented, "method GetLuckyGiftDrawSummary not implemented")
}
func (UnimplementedAdminLuckyGiftServiceServer) mustEmbedUnimplementedAdminLuckyGiftServiceServer() {}
func (UnimplementedAdminLuckyGiftServiceServer) testEmbeddedByValue() {}
@ -4792,7 +4830,7 @@ type UnsafeAdminLuckyGiftServiceServer interface {
}
func RegisterAdminLuckyGiftServiceServer(s grpc.ServiceRegistrar, srv AdminLuckyGiftServiceServer) {
// If the following call pancis, it indicates UnimplementedAdminLuckyGiftServiceServer was
// If the following call panics, it indicates UnimplementedAdminLuckyGiftServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -83,6 +83,32 @@ message ListGamesResponse {
int64 server_time_ms = 2;
}
message ListRecentGamesRequest {
RequestMeta meta = 1;
int64 user_id = 2;
string scene = 3;
string room_id = 4;
int64 region_id = 5;
string language = 6;
string client_platform = 7;
int32 page_size = 8;
}
message GetBridgeScriptRequest {
RequestMeta meta = 1;
}
message GameBridgeScriptConfig {
string bridge_script_url = 1;
string bridge_script_version = 2;
string bridge_script_sha256 = 3;
}
message GetBridgeScriptResponse {
GameBridgeScriptConfig config = 1;
int64 server_time_ms = 2;
}
message LaunchGameRequest {
RequestMeta meta = 1;
int64 user_id = 2;
@ -196,6 +222,8 @@ message DeleteCatalogResponse {
service GameAppService {
rpc ListGames(ListGamesRequest) returns (ListGamesResponse);
rpc ListRecentGames(ListRecentGamesRequest) returns (ListGamesResponse);
rpc GetBridgeScript(GetBridgeScriptRequest) returns (GetBridgeScriptResponse);
rpc LaunchGame(LaunchGameRequest) returns (LaunchGameResponse);
}

View File

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.5.1
// - protoc-gen-go-grpc v1.6.2
// - protoc v7.35.0
// source: proto/game/v1/game.proto
@ -19,8 +19,10 @@ import (
const _ = grpc.SupportPackageIsVersion9
const (
GameAppService_ListGames_FullMethodName = "/hyapp.game.v1.GameAppService/ListGames"
GameAppService_LaunchGame_FullMethodName = "/hyapp.game.v1.GameAppService/LaunchGame"
GameAppService_ListGames_FullMethodName = "/hyapp.game.v1.GameAppService/ListGames"
GameAppService_ListRecentGames_FullMethodName = "/hyapp.game.v1.GameAppService/ListRecentGames"
GameAppService_GetBridgeScript_FullMethodName = "/hyapp.game.v1.GameAppService/GetBridgeScript"
GameAppService_LaunchGame_FullMethodName = "/hyapp.game.v1.GameAppService/LaunchGame"
)
// GameAppServiceClient is the client API for GameAppService service.
@ -28,6 +30,8 @@ const (
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type GameAppServiceClient interface {
ListGames(ctx context.Context, in *ListGamesRequest, opts ...grpc.CallOption) (*ListGamesResponse, error)
ListRecentGames(ctx context.Context, in *ListRecentGamesRequest, opts ...grpc.CallOption) (*ListGamesResponse, error)
GetBridgeScript(ctx context.Context, in *GetBridgeScriptRequest, opts ...grpc.CallOption) (*GetBridgeScriptResponse, error)
LaunchGame(ctx context.Context, in *LaunchGameRequest, opts ...grpc.CallOption) (*LaunchGameResponse, error)
}
@ -49,6 +53,26 @@ func (c *gameAppServiceClient) ListGames(ctx context.Context, in *ListGamesReque
return out, nil
}
func (c *gameAppServiceClient) ListRecentGames(ctx context.Context, in *ListRecentGamesRequest, opts ...grpc.CallOption) (*ListGamesResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ListGamesResponse)
err := c.cc.Invoke(ctx, GameAppService_ListRecentGames_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *gameAppServiceClient) GetBridgeScript(ctx context.Context, in *GetBridgeScriptRequest, opts ...grpc.CallOption) (*GetBridgeScriptResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(GetBridgeScriptResponse)
err := c.cc.Invoke(ctx, GameAppService_GetBridgeScript_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *gameAppServiceClient) LaunchGame(ctx context.Context, in *LaunchGameRequest, opts ...grpc.CallOption) (*LaunchGameResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(LaunchGameResponse)
@ -64,6 +88,8 @@ func (c *gameAppServiceClient) LaunchGame(ctx context.Context, in *LaunchGameReq
// for forward compatibility.
type GameAppServiceServer interface {
ListGames(context.Context, *ListGamesRequest) (*ListGamesResponse, error)
ListRecentGames(context.Context, *ListRecentGamesRequest) (*ListGamesResponse, error)
GetBridgeScript(context.Context, *GetBridgeScriptRequest) (*GetBridgeScriptResponse, error)
LaunchGame(context.Context, *LaunchGameRequest) (*LaunchGameResponse, error)
mustEmbedUnimplementedGameAppServiceServer()
}
@ -76,10 +102,16 @@ type GameAppServiceServer interface {
type UnimplementedGameAppServiceServer struct{}
func (UnimplementedGameAppServiceServer) ListGames(context.Context, *ListGamesRequest) (*ListGamesResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListGames not implemented")
return nil, status.Error(codes.Unimplemented, "method ListGames not implemented")
}
func (UnimplementedGameAppServiceServer) ListRecentGames(context.Context, *ListRecentGamesRequest) (*ListGamesResponse, error) {
return nil, status.Error(codes.Unimplemented, "method ListRecentGames not implemented")
}
func (UnimplementedGameAppServiceServer) GetBridgeScript(context.Context, *GetBridgeScriptRequest) (*GetBridgeScriptResponse, error) {
return nil, status.Error(codes.Unimplemented, "method GetBridgeScript not implemented")
}
func (UnimplementedGameAppServiceServer) LaunchGame(context.Context, *LaunchGameRequest) (*LaunchGameResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method LaunchGame not implemented")
return nil, status.Error(codes.Unimplemented, "method LaunchGame not implemented")
}
func (UnimplementedGameAppServiceServer) mustEmbedUnimplementedGameAppServiceServer() {}
func (UnimplementedGameAppServiceServer) testEmbeddedByValue() {}
@ -92,7 +124,7 @@ type UnsafeGameAppServiceServer interface {
}
func RegisterGameAppServiceServer(s grpc.ServiceRegistrar, srv GameAppServiceServer) {
// If the following call pancis, it indicates UnimplementedGameAppServiceServer was
// If the following call panics, it indicates UnimplementedGameAppServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
@ -120,6 +152,42 @@ func _GameAppService_ListGames_Handler(srv interface{}, ctx context.Context, dec
return interceptor(ctx, in, info, handler)
}
func _GameAppService_ListRecentGames_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ListRecentGamesRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(GameAppServiceServer).ListRecentGames(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: GameAppService_ListRecentGames_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(GameAppServiceServer).ListRecentGames(ctx, req.(*ListRecentGamesRequest))
}
return interceptor(ctx, in, info, handler)
}
func _GameAppService_GetBridgeScript_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetBridgeScriptRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(GameAppServiceServer).GetBridgeScript(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: GameAppService_GetBridgeScript_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(GameAppServiceServer).GetBridgeScript(ctx, req.(*GetBridgeScriptRequest))
}
return interceptor(ctx, in, info, handler)
}
func _GameAppService_LaunchGame_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(LaunchGameRequest)
if err := dec(in); err != nil {
@ -149,6 +217,14 @@ var GameAppService_ServiceDesc = grpc.ServiceDesc{
MethodName: "ListGames",
Handler: _GameAppService_ListGames_Handler,
},
{
MethodName: "ListRecentGames",
Handler: _GameAppService_ListRecentGames_Handler,
},
{
MethodName: "GetBridgeScript",
Handler: _GameAppService_GetBridgeScript_Handler,
},
{
MethodName: "LaunchGame",
Handler: _GameAppService_LaunchGame_Handler,
@ -203,7 +279,7 @@ type GameCallbackServiceServer interface {
type UnimplementedGameCallbackServiceServer struct{}
func (UnimplementedGameCallbackServiceServer) HandleCallback(context.Context, *CallbackRequest) (*CallbackResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method HandleCallback not implemented")
return nil, status.Error(codes.Unimplemented, "method HandleCallback not implemented")
}
func (UnimplementedGameCallbackServiceServer) mustEmbedUnimplementedGameCallbackServiceServer() {}
func (UnimplementedGameCallbackServiceServer) testEmbeddedByValue() {}
@ -216,7 +292,7 @@ type UnsafeGameCallbackServiceServer interface {
}
func RegisterGameCallbackServiceServer(s grpc.ServiceRegistrar, srv GameCallbackServiceServer) {
// If the following call pancis, it indicates UnimplementedGameCallbackServiceServer was
// If the following call panics, it indicates UnimplementedGameCallbackServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
@ -309,7 +385,7 @@ type GameCronServiceServer interface {
type UnimplementedGameCronServiceServer struct{}
func (UnimplementedGameCronServiceServer) ProcessLevelEventOutboxBatch(context.Context, *CronBatchRequest) (*CronBatchResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ProcessLevelEventOutboxBatch not implemented")
return nil, status.Error(codes.Unimplemented, "method ProcessLevelEventOutboxBatch not implemented")
}
func (UnimplementedGameCronServiceServer) mustEmbedUnimplementedGameCronServiceServer() {}
func (UnimplementedGameCronServiceServer) testEmbeddedByValue() {}
@ -322,7 +398,7 @@ type UnsafeGameCronServiceServer interface {
}
func RegisterGameCronServiceServer(s grpc.ServiceRegistrar, srv GameCronServiceServer) {
// If the following call pancis, it indicates UnimplementedGameCronServiceServer was
// If the following call panics, it indicates UnimplementedGameCronServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
@ -476,22 +552,22 @@ type GameAdminServiceServer interface {
type UnimplementedGameAdminServiceServer struct{}
func (UnimplementedGameAdminServiceServer) ListPlatforms(context.Context, *ListPlatformsRequest) (*ListPlatformsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListPlatforms not implemented")
return nil, status.Error(codes.Unimplemented, "method ListPlatforms not implemented")
}
func (UnimplementedGameAdminServiceServer) UpsertPlatform(context.Context, *UpsertPlatformRequest) (*PlatformResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method UpsertPlatform not implemented")
return nil, status.Error(codes.Unimplemented, "method UpsertPlatform not implemented")
}
func (UnimplementedGameAdminServiceServer) ListCatalog(context.Context, *ListCatalogRequest) (*ListCatalogResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListCatalog not implemented")
return nil, status.Error(codes.Unimplemented, "method ListCatalog not implemented")
}
func (UnimplementedGameAdminServiceServer) UpsertCatalog(context.Context, *UpsertCatalogRequest) (*CatalogResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method UpsertCatalog not implemented")
return nil, status.Error(codes.Unimplemented, "method UpsertCatalog not implemented")
}
func (UnimplementedGameAdminServiceServer) SetGameStatus(context.Context, *SetGameStatusRequest) (*CatalogResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method SetGameStatus not implemented")
return nil, status.Error(codes.Unimplemented, "method SetGameStatus not implemented")
}
func (UnimplementedGameAdminServiceServer) DeleteCatalog(context.Context, *DeleteCatalogRequest) (*DeleteCatalogResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method DeleteCatalog not implemented")
return nil, status.Error(codes.Unimplemented, "method DeleteCatalog not implemented")
}
func (UnimplementedGameAdminServiceServer) mustEmbedUnimplementedGameAdminServiceServer() {}
func (UnimplementedGameAdminServiceServer) testEmbeddedByValue() {}
@ -504,7 +580,7 @@ type UnsafeGameAdminServiceServer interface {
}
func RegisterGameAdminServiceServer(s grpc.ServiceRegistrar, srv GameAdminServiceServer) {
// If the following call pancis, it indicates UnimplementedGameAdminServiceServer was
// If the following call panics, it indicates UnimplementedGameAdminServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.

File diff suppressed because it is too large Load Diff

View File

@ -425,6 +425,8 @@ message CreateRoomRequest {
string room_description = 7;
// room_short_id ID default/current display_user_id
string room_short_id = 8;
// owner_country_code gateway user-service
string owner_country_code = 9;
}
// CreateRoomResponse
@ -901,6 +903,8 @@ message ListRoomsRequest {
string cursor = 5;
int32 limit = 6;
string query = 7;
// country_code gateway
string country_code = 8;
}
// ListRoomFeedsRequest Mine visited/friend/following/followed
@ -947,6 +951,7 @@ message RoomListItem {
string app_code = 13;
string room_short_id = 14;
bool locked = 15;
string country_code = 16;
}
// ListRoomsResponse cursor

View File

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.5.1
// - protoc-gen-go-grpc v1.6.2
// - protoc v7.35.0
// source: proto/room/v1/room.proto
@ -470,100 +470,100 @@ type RoomCommandServiceServer interface {
type UnimplementedRoomCommandServiceServer struct{}
func (UnimplementedRoomCommandServiceServer) CreateRoom(context.Context, *CreateRoomRequest) (*CreateRoomResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreateRoom not implemented")
return nil, status.Error(codes.Unimplemented, "method CreateRoom not implemented")
}
func (UnimplementedRoomCommandServiceServer) UpdateRoomProfile(context.Context, *UpdateRoomProfileRequest) (*UpdateRoomProfileResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method UpdateRoomProfile not implemented")
return nil, status.Error(codes.Unimplemented, "method UpdateRoomProfile not implemented")
}
func (UnimplementedRoomCommandServiceServer) SaveRoomBackground(context.Context, *SaveRoomBackgroundRequest) (*SaveRoomBackgroundResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method SaveRoomBackground not implemented")
return nil, status.Error(codes.Unimplemented, "method SaveRoomBackground not implemented")
}
func (UnimplementedRoomCommandServiceServer) SetRoomBackground(context.Context, *SetRoomBackgroundRequest) (*SetRoomBackgroundResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method SetRoomBackground not implemented")
return nil, status.Error(codes.Unimplemented, "method SetRoomBackground not implemented")
}
func (UnimplementedRoomCommandServiceServer) JoinRoom(context.Context, *JoinRoomRequest) (*JoinRoomResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method JoinRoom not implemented")
return nil, status.Error(codes.Unimplemented, "method JoinRoom not implemented")
}
func (UnimplementedRoomCommandServiceServer) RoomHeartbeat(context.Context, *RoomHeartbeatRequest) (*RoomHeartbeatResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method RoomHeartbeat not implemented")
return nil, status.Error(codes.Unimplemented, "method RoomHeartbeat not implemented")
}
func (UnimplementedRoomCommandServiceServer) LeaveRoom(context.Context, *LeaveRoomRequest) (*LeaveRoomResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method LeaveRoom not implemented")
return nil, status.Error(codes.Unimplemented, "method LeaveRoom not implemented")
}
func (UnimplementedRoomCommandServiceServer) CloseRoom(context.Context, *CloseRoomRequest) (*CloseRoomResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method CloseRoom not implemented")
return nil, status.Error(codes.Unimplemented, "method CloseRoom not implemented")
}
func (UnimplementedRoomCommandServiceServer) AdminUpdateRoom(context.Context, *AdminUpdateRoomRequest) (*AdminUpdateRoomResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method AdminUpdateRoom not implemented")
return nil, status.Error(codes.Unimplemented, "method AdminUpdateRoom not implemented")
}
func (UnimplementedRoomCommandServiceServer) AdminDeleteRoom(context.Context, *AdminDeleteRoomRequest) (*AdminDeleteRoomResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method AdminDeleteRoom not implemented")
return nil, status.Error(codes.Unimplemented, "method AdminDeleteRoom not implemented")
}
func (UnimplementedRoomCommandServiceServer) AdminUpdateRoomRocketConfig(context.Context, *AdminUpdateRoomRocketConfigRequest) (*AdminUpdateRoomRocketConfigResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method AdminUpdateRoomRocketConfig not implemented")
return nil, status.Error(codes.Unimplemented, "method AdminUpdateRoomRocketConfig not implemented")
}
func (UnimplementedRoomCommandServiceServer) AdminUpdateRoomSeatConfig(context.Context, *AdminUpdateRoomSeatConfigRequest) (*AdminUpdateRoomSeatConfigResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method AdminUpdateRoomSeatConfig not implemented")
return nil, status.Error(codes.Unimplemented, "method AdminUpdateRoomSeatConfig not implemented")
}
func (UnimplementedRoomCommandServiceServer) AdminCreateRoomPin(context.Context, *AdminCreateRoomPinRequest) (*AdminCreateRoomPinResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method AdminCreateRoomPin not implemented")
return nil, status.Error(codes.Unimplemented, "method AdminCreateRoomPin not implemented")
}
func (UnimplementedRoomCommandServiceServer) AdminCancelRoomPin(context.Context, *AdminCancelRoomPinRequest) (*AdminCancelRoomPinResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method AdminCancelRoomPin not implemented")
return nil, status.Error(codes.Unimplemented, "method AdminCancelRoomPin not implemented")
}
func (UnimplementedRoomCommandServiceServer) MicUp(context.Context, *MicUpRequest) (*MicUpResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method MicUp not implemented")
return nil, status.Error(codes.Unimplemented, "method MicUp not implemented")
}
func (UnimplementedRoomCommandServiceServer) MicDown(context.Context, *MicDownRequest) (*MicDownResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method MicDown not implemented")
return nil, status.Error(codes.Unimplemented, "method MicDown not implemented")
}
func (UnimplementedRoomCommandServiceServer) ChangeMicSeat(context.Context, *ChangeMicSeatRequest) (*ChangeMicSeatResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ChangeMicSeat not implemented")
return nil, status.Error(codes.Unimplemented, "method ChangeMicSeat not implemented")
}
func (UnimplementedRoomCommandServiceServer) ConfirmMicPublishing(context.Context, *ConfirmMicPublishingRequest) (*ConfirmMicPublishingResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ConfirmMicPublishing not implemented")
return nil, status.Error(codes.Unimplemented, "method ConfirmMicPublishing not implemented")
}
func (UnimplementedRoomCommandServiceServer) MicHeartbeat(context.Context, *MicHeartbeatRequest) (*MicHeartbeatResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method MicHeartbeat not implemented")
return nil, status.Error(codes.Unimplemented, "method MicHeartbeat not implemented")
}
func (UnimplementedRoomCommandServiceServer) SetMicMute(context.Context, *SetMicMuteRequest) (*SetMicMuteResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method SetMicMute not implemented")
return nil, status.Error(codes.Unimplemented, "method SetMicMute not implemented")
}
func (UnimplementedRoomCommandServiceServer) ApplyRTCEvent(context.Context, *ApplyRTCEventRequest) (*ApplyRTCEventResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ApplyRTCEvent not implemented")
return nil, status.Error(codes.Unimplemented, "method ApplyRTCEvent not implemented")
}
func (UnimplementedRoomCommandServiceServer) SetMicSeatLock(context.Context, *SetMicSeatLockRequest) (*SetMicSeatLockResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method SetMicSeatLock not implemented")
return nil, status.Error(codes.Unimplemented, "method SetMicSeatLock not implemented")
}
func (UnimplementedRoomCommandServiceServer) SetChatEnabled(context.Context, *SetChatEnabledRequest) (*SetChatEnabledResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method SetChatEnabled not implemented")
return nil, status.Error(codes.Unimplemented, "method SetChatEnabled not implemented")
}
func (UnimplementedRoomCommandServiceServer) SetRoomPassword(context.Context, *SetRoomPasswordRequest) (*SetRoomPasswordResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method SetRoomPassword not implemented")
return nil, status.Error(codes.Unimplemented, "method SetRoomPassword not implemented")
}
func (UnimplementedRoomCommandServiceServer) SetRoomAdmin(context.Context, *SetRoomAdminRequest) (*SetRoomAdminResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method SetRoomAdmin not implemented")
return nil, status.Error(codes.Unimplemented, "method SetRoomAdmin not implemented")
}
func (UnimplementedRoomCommandServiceServer) MuteUser(context.Context, *MuteUserRequest) (*MuteUserResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method MuteUser not implemented")
return nil, status.Error(codes.Unimplemented, "method MuteUser not implemented")
}
func (UnimplementedRoomCommandServiceServer) KickUser(context.Context, *KickUserRequest) (*KickUserResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method KickUser not implemented")
return nil, status.Error(codes.Unimplemented, "method KickUser not implemented")
}
func (UnimplementedRoomCommandServiceServer) UnbanUser(context.Context, *UnbanUserRequest) (*UnbanUserResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method UnbanUser not implemented")
return nil, status.Error(codes.Unimplemented, "method UnbanUser not implemented")
}
func (UnimplementedRoomCommandServiceServer) SystemEvictUser(context.Context, *SystemEvictUserRequest) (*SystemEvictUserResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method SystemEvictUser not implemented")
return nil, status.Error(codes.Unimplemented, "method SystemEvictUser not implemented")
}
func (UnimplementedRoomCommandServiceServer) SendGift(context.Context, *SendGiftRequest) (*SendGiftResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method SendGift not implemented")
return nil, status.Error(codes.Unimplemented, "method SendGift not implemented")
}
func (UnimplementedRoomCommandServiceServer) FollowRoom(context.Context, *FollowRoomRequest) (*FollowRoomResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method FollowRoom not implemented")
return nil, status.Error(codes.Unimplemented, "method FollowRoom not implemented")
}
func (UnimplementedRoomCommandServiceServer) UnfollowRoom(context.Context, *UnfollowRoomRequest) (*UnfollowRoomResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method UnfollowRoom not implemented")
return nil, status.Error(codes.Unimplemented, "method UnfollowRoom not implemented")
}
func (UnimplementedRoomCommandServiceServer) mustEmbedUnimplementedRoomCommandServiceServer() {}
func (UnimplementedRoomCommandServiceServer) testEmbeddedByValue() {}
@ -576,7 +576,7 @@ type UnsafeRoomCommandServiceServer interface {
}
func RegisterRoomCommandServiceServer(s grpc.ServiceRegistrar, srv RoomCommandServiceServer) {
// If the following call pancis, it indicates UnimplementedRoomCommandServiceServer was
// If the following call panics, it indicates UnimplementedRoomCommandServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
@ -1364,10 +1364,10 @@ type RoomGuardServiceServer interface {
type UnimplementedRoomGuardServiceServer struct{}
func (UnimplementedRoomGuardServiceServer) CheckSpeakPermission(context.Context, *CheckSpeakPermissionRequest) (*CheckSpeakPermissionResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method CheckSpeakPermission not implemented")
return nil, status.Error(codes.Unimplemented, "method CheckSpeakPermission not implemented")
}
func (UnimplementedRoomGuardServiceServer) VerifyRoomPresence(context.Context, *VerifyRoomPresenceRequest) (*VerifyRoomPresenceResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method VerifyRoomPresence not implemented")
return nil, status.Error(codes.Unimplemented, "method VerifyRoomPresence not implemented")
}
func (UnimplementedRoomGuardServiceServer) mustEmbedUnimplementedRoomGuardServiceServer() {}
func (UnimplementedRoomGuardServiceServer) testEmbeddedByValue() {}
@ -1380,7 +1380,7 @@ type UnsafeRoomGuardServiceServer interface {
}
func RegisterRoomGuardServiceServer(s grpc.ServiceRegistrar, srv RoomGuardServiceServer) {
// If the following call pancis, it indicates UnimplementedRoomGuardServiceServer was
// If the following call panics, it indicates UnimplementedRoomGuardServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
@ -1677,49 +1677,49 @@ type RoomQueryServiceServer interface {
type UnimplementedRoomQueryServiceServer struct{}
func (UnimplementedRoomQueryServiceServer) AdminListRooms(context.Context, *AdminListRoomsRequest) (*AdminListRoomsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method AdminListRooms not implemented")
return nil, status.Error(codes.Unimplemented, "method AdminListRooms not implemented")
}
func (UnimplementedRoomQueryServiceServer) AdminGetRoom(context.Context, *AdminGetRoomRequest) (*AdminGetRoomResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method AdminGetRoom not implemented")
return nil, status.Error(codes.Unimplemented, "method AdminGetRoom not implemented")
}
func (UnimplementedRoomQueryServiceServer) AdminGetRoomRocketConfig(context.Context, *AdminGetRoomRocketConfigRequest) (*AdminGetRoomRocketConfigResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method AdminGetRoomRocketConfig not implemented")
return nil, status.Error(codes.Unimplemented, "method AdminGetRoomRocketConfig not implemented")
}
func (UnimplementedRoomQueryServiceServer) AdminGetRoomSeatConfig(context.Context, *AdminGetRoomSeatConfigRequest) (*AdminGetRoomSeatConfigResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method AdminGetRoomSeatConfig not implemented")
return nil, status.Error(codes.Unimplemented, "method AdminGetRoomSeatConfig not implemented")
}
func (UnimplementedRoomQueryServiceServer) AdminListRoomPins(context.Context, *AdminListRoomPinsRequest) (*AdminListRoomPinsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method AdminListRoomPins not implemented")
return nil, status.Error(codes.Unimplemented, "method AdminListRoomPins not implemented")
}
func (UnimplementedRoomQueryServiceServer) ListRooms(context.Context, *ListRoomsRequest) (*ListRoomsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListRooms not implemented")
return nil, status.Error(codes.Unimplemented, "method ListRooms not implemented")
}
func (UnimplementedRoomQueryServiceServer) ListRoomFeeds(context.Context, *ListRoomFeedsRequest) (*ListRoomsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListRoomFeeds not implemented")
return nil, status.Error(codes.Unimplemented, "method ListRoomFeeds not implemented")
}
func (UnimplementedRoomQueryServiceServer) ListRoomGiftLeaderboard(context.Context, *ListRoomGiftLeaderboardRequest) (*ListRoomGiftLeaderboardResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListRoomGiftLeaderboard not implemented")
return nil, status.Error(codes.Unimplemented, "method ListRoomGiftLeaderboard not implemented")
}
func (UnimplementedRoomQueryServiceServer) GetMyRoom(context.Context, *GetMyRoomRequest) (*GetMyRoomResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetMyRoom not implemented")
return nil, status.Error(codes.Unimplemented, "method GetMyRoom not implemented")
}
func (UnimplementedRoomQueryServiceServer) GetCurrentRoom(context.Context, *GetCurrentRoomRequest) (*GetCurrentRoomResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetCurrentRoom not implemented")
return nil, status.Error(codes.Unimplemented, "method GetCurrentRoom not implemented")
}
func (UnimplementedRoomQueryServiceServer) GetRoomSnapshot(context.Context, *GetRoomSnapshotRequest) (*GetRoomSnapshotResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetRoomSnapshot not implemented")
return nil, status.Error(codes.Unimplemented, "method GetRoomSnapshot not implemented")
}
func (UnimplementedRoomQueryServiceServer) ListRoomBackgrounds(context.Context, *ListRoomBackgroundsRequest) (*ListRoomBackgroundsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListRoomBackgrounds not implemented")
return nil, status.Error(codes.Unimplemented, "method ListRoomBackgrounds not implemented")
}
func (UnimplementedRoomQueryServiceServer) GetRoomRocket(context.Context, *GetRoomRocketRequest) (*GetRoomRocketResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetRoomRocket not implemented")
return nil, status.Error(codes.Unimplemented, "method GetRoomRocket not implemented")
}
func (UnimplementedRoomQueryServiceServer) ListRoomOnlineUsers(context.Context, *ListRoomOnlineUsersRequest) (*ListRoomOnlineUsersResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListRoomOnlineUsers not implemented")
return nil, status.Error(codes.Unimplemented, "method ListRoomOnlineUsers not implemented")
}
func (UnimplementedRoomQueryServiceServer) ListRoomBannedUsers(context.Context, *ListRoomBannedUsersRequest) (*ListRoomBannedUsersResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListRoomBannedUsers not implemented")
return nil, status.Error(codes.Unimplemented, "method ListRoomBannedUsers not implemented")
}
func (UnimplementedRoomQueryServiceServer) mustEmbedUnimplementedRoomQueryServiceServer() {}
func (UnimplementedRoomQueryServiceServer) testEmbeddedByValue() {}
@ -1732,7 +1732,7 @@ type UnsafeRoomQueryServiceServer interface {
}
func RegisterRoomQueryServiceServer(s grpc.ServiceRegistrar, srv RoomQueryServiceServer) {
// If the following call pancis, it indicates UnimplementedRoomQueryServiceServer was
// If the following call panics, it indicates UnimplementedRoomQueryServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.

View File

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.35.1
// protoc-gen-go v1.36.11
// protoc v7.35.0
// source: proto/user/v1/auth.proto
@ -11,6 +11,7 @@ import (
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
@ -22,13 +23,12 @@ const (
// LoginPasswordRequest 使用当前 display_user_id 和已设置密码登录。
type LoginPasswordRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
Meta *RequestMeta `protobuf:"bytes,1,opt,name=meta,proto3" json:"meta,omitempty"`
DisplayUserId string `protobuf:"bytes,2,opt,name=display_user_id,json=displayUserId,proto3" json:"display_user_id,omitempty"`
Password string `protobuf:"bytes,3,opt,name=password,proto3" json:"password,omitempty"`
unknownFields protoimpl.UnknownFields
Meta *RequestMeta `protobuf:"bytes,1,opt,name=meta,proto3" json:"meta,omitempty"`
DisplayUserId string `protobuf:"bytes,2,opt,name=display_user_id,json=displayUserId,proto3" json:"display_user_id,omitempty"`
Password string `protobuf:"bytes,3,opt,name=password,proto3" json:"password,omitempty"`
sizeCache protoimpl.SizeCache
}
func (x *LoginPasswordRequest) Reset() {
@ -84,30 +84,29 @@ func (x *LoginPasswordRequest) GetPassword() string {
// LoginThirdPartyRequest 使用三方凭证登录或注册。
type LoginThirdPartyRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Meta *RequestMeta `protobuf:"bytes,1,opt,name=meta,proto3" json:"meta,omitempty"`
Provider string `protobuf:"bytes,2,opt,name=provider,proto3" json:"provider,omitempty"`
Credential string `protobuf:"bytes,3,opt,name=credential,proto3" json:"credential,omitempty"`
Username string `protobuf:"bytes,4,opt,name=username,proto3" json:"username,omitempty"`
Gender string `protobuf:"bytes,5,opt,name=gender,proto3" json:"gender,omitempty"`
Country string `protobuf:"bytes,6,opt,name=country,proto3" json:"country,omitempty"`
InviteCode string `protobuf:"bytes,7,opt,name=invite_code,json=inviteCode,proto3" json:"invite_code,omitempty"`
DeviceId string `protobuf:"bytes,8,opt,name=device_id,json=deviceId,proto3" json:"device_id,omitempty"`
Device string `protobuf:"bytes,9,opt,name=device,proto3" json:"device,omitempty"`
Avatar string `protobuf:"bytes,10,opt,name=avatar,proto3" json:"avatar,omitempty"`
Birth string `protobuf:"bytes,11,opt,name=birth,proto3" json:"birth,omitempty"`
AppVersion string `protobuf:"bytes,12,opt,name=app_version,json=appVersion,proto3" json:"app_version,omitempty"`
Source string `protobuf:"bytes,13,opt,name=source,proto3" json:"source,omitempty"`
Platform string `protobuf:"bytes,14,opt,name=platform,proto3" json:"platform,omitempty"`
Language string `protobuf:"bytes,15,opt,name=language,proto3" json:"language,omitempty"`
OsVersion string `protobuf:"bytes,16,opt,name=os_version,json=osVersion,proto3" json:"os_version,omitempty"`
BuildNumber string `protobuf:"bytes,17,opt,name=build_number,json=buildNumber,proto3" json:"build_number,omitempty"`
Timezone string `protobuf:"bytes,18,opt,name=timezone,proto3" json:"timezone,omitempty"`
InstallChannel string `protobuf:"bytes,19,opt,name=install_channel,json=installChannel,proto3" json:"install_channel,omitempty"`
Campaign string `protobuf:"bytes,20,opt,name=campaign,proto3" json:"campaign,omitempty"`
state protoimpl.MessageState `protogen:"open.v1"`
Meta *RequestMeta `protobuf:"bytes,1,opt,name=meta,proto3" json:"meta,omitempty"`
Provider string `protobuf:"bytes,2,opt,name=provider,proto3" json:"provider,omitempty"`
Credential string `protobuf:"bytes,3,opt,name=credential,proto3" json:"credential,omitempty"`
Username string `protobuf:"bytes,4,opt,name=username,proto3" json:"username,omitempty"`
Gender string `protobuf:"bytes,5,opt,name=gender,proto3" json:"gender,omitempty"`
Country string `protobuf:"bytes,6,opt,name=country,proto3" json:"country,omitempty"`
InviteCode string `protobuf:"bytes,7,opt,name=invite_code,json=inviteCode,proto3" json:"invite_code,omitempty"`
DeviceId string `protobuf:"bytes,8,opt,name=device_id,json=deviceId,proto3" json:"device_id,omitempty"`
Device string `protobuf:"bytes,9,opt,name=device,proto3" json:"device,omitempty"`
Avatar string `protobuf:"bytes,10,opt,name=avatar,proto3" json:"avatar,omitempty"`
Birth string `protobuf:"bytes,11,opt,name=birth,proto3" json:"birth,omitempty"`
AppVersion string `protobuf:"bytes,12,opt,name=app_version,json=appVersion,proto3" json:"app_version,omitempty"`
Source string `protobuf:"bytes,13,opt,name=source,proto3" json:"source,omitempty"`
Platform string `protobuf:"bytes,14,opt,name=platform,proto3" json:"platform,omitempty"`
Language string `protobuf:"bytes,15,opt,name=language,proto3" json:"language,omitempty"`
OsVersion string `protobuf:"bytes,16,opt,name=os_version,json=osVersion,proto3" json:"os_version,omitempty"`
BuildNumber string `protobuf:"bytes,17,opt,name=build_number,json=buildNumber,proto3" json:"build_number,omitempty"`
Timezone string `protobuf:"bytes,18,opt,name=timezone,proto3" json:"timezone,omitempty"`
InstallChannel string `protobuf:"bytes,19,opt,name=install_channel,json=installChannel,proto3" json:"install_channel,omitempty"`
Campaign string `protobuf:"bytes,20,opt,name=campaign,proto3" json:"campaign,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *LoginThirdPartyRequest) Reset() {
@ -282,14 +281,13 @@ func (x *LoginThirdPartyRequest) GetCampaign() string {
// AuthResponse 返回标准认证令牌。
type AuthResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Token *AuthToken `protobuf:"bytes,1,opt,name=token,proto3" json:"token,omitempty"`
IsNewUser bool `protobuf:"varint,2,opt,name=is_new_user,json=isNewUser,proto3" json:"is_new_user,omitempty"`
ProfileCompleted bool `protobuf:"varint,3,opt,name=profile_completed,json=profileCompleted,proto3" json:"profile_completed,omitempty"`
OnboardingStatus string `protobuf:"bytes,4,opt,name=onboarding_status,json=onboardingStatus,proto3" json:"onboarding_status,omitempty"`
state protoimpl.MessageState `protogen:"open.v1"`
Token *AuthToken `protobuf:"bytes,1,opt,name=token,proto3" json:"token,omitempty"`
IsNewUser bool `protobuf:"varint,2,opt,name=is_new_user,json=isNewUser,proto3" json:"is_new_user,omitempty"`
ProfileCompleted bool `protobuf:"varint,3,opt,name=profile_completed,json=profileCompleted,proto3" json:"profile_completed,omitempty"`
OnboardingStatus string `protobuf:"bytes,4,opt,name=onboarding_status,json=onboardingStatus,proto3" json:"onboarding_status,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *AuthResponse) Reset() {
@ -352,13 +350,12 @@ func (x *AuthResponse) GetOnboardingStatus() string {
// SetPasswordRequest 为已经三方登录的用户首次设置密码。
type SetPasswordRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
Meta *RequestMeta `protobuf:"bytes,1,opt,name=meta,proto3" json:"meta,omitempty"`
UserId int64 `protobuf:"varint,2,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
Password string `protobuf:"bytes,3,opt,name=password,proto3" json:"password,omitempty"`
unknownFields protoimpl.UnknownFields
Meta *RequestMeta `protobuf:"bytes,1,opt,name=meta,proto3" json:"meta,omitempty"`
UserId int64 `protobuf:"varint,2,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
Password string `protobuf:"bytes,3,opt,name=password,proto3" json:"password,omitempty"`
sizeCache protoimpl.SizeCache
}
func (x *SetPasswordRequest) Reset() {
@ -414,11 +411,10 @@ func (x *SetPasswordRequest) GetPassword() string {
// SetPasswordResponse 返回密码是否已经成功设置。
type SetPasswordResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
PasswordSet bool `protobuf:"varint,1,opt,name=password_set,json=passwordSet,proto3" json:"password_set,omitempty"`
unknownFields protoimpl.UnknownFields
PasswordSet bool `protobuf:"varint,1,opt,name=password_set,json=passwordSet,proto3" json:"password_set,omitempty"`
sizeCache protoimpl.SizeCache
}
func (x *SetPasswordResponse) Reset() {
@ -460,29 +456,28 @@ func (x *SetPasswordResponse) GetPasswordSet() bool {
// QuickCreateAccountRequest 为测试和三方联调快速创建一个可密码登录的完整账号。
type QuickCreateAccountRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Meta *RequestMeta `protobuf:"bytes,1,opt,name=meta,proto3" json:"meta,omitempty"`
Password string `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"`
Username string `protobuf:"bytes,3,opt,name=username,proto3" json:"username,omitempty"`
Avatar string `protobuf:"bytes,4,opt,name=avatar,proto3" json:"avatar,omitempty"`
Gender string `protobuf:"bytes,5,opt,name=gender,proto3" json:"gender,omitempty"`
Country string `protobuf:"bytes,6,opt,name=country,proto3" json:"country,omitempty"`
InviteCode string `protobuf:"bytes,7,opt,name=invite_code,json=inviteCode,proto3" json:"invite_code,omitempty"`
DeviceId string `protobuf:"bytes,8,opt,name=device_id,json=deviceId,proto3" json:"device_id,omitempty"`
Device string `protobuf:"bytes,9,opt,name=device,proto3" json:"device,omitempty"`
OsVersion string `protobuf:"bytes,10,opt,name=os_version,json=osVersion,proto3" json:"os_version,omitempty"`
Birth string `protobuf:"bytes,11,opt,name=birth,proto3" json:"birth,omitempty"`
AppVersion string `protobuf:"bytes,12,opt,name=app_version,json=appVersion,proto3" json:"app_version,omitempty"`
BuildNumber string `protobuf:"bytes,13,opt,name=build_number,json=buildNumber,proto3" json:"build_number,omitempty"`
Source string `protobuf:"bytes,14,opt,name=source,proto3" json:"source,omitempty"`
InstallChannel string `protobuf:"bytes,15,opt,name=install_channel,json=installChannel,proto3" json:"install_channel,omitempty"`
Campaign string `protobuf:"bytes,16,opt,name=campaign,proto3" json:"campaign,omitempty"`
Platform string `protobuf:"bytes,17,opt,name=platform,proto3" json:"platform,omitempty"`
Language string `protobuf:"bytes,18,opt,name=language,proto3" json:"language,omitempty"`
Timezone string `protobuf:"bytes,19,opt,name=timezone,proto3" json:"timezone,omitempty"`
state protoimpl.MessageState `protogen:"open.v1"`
Meta *RequestMeta `protobuf:"bytes,1,opt,name=meta,proto3" json:"meta,omitempty"`
Password string `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"`
Username string `protobuf:"bytes,3,opt,name=username,proto3" json:"username,omitempty"`
Avatar string `protobuf:"bytes,4,opt,name=avatar,proto3" json:"avatar,omitempty"`
Gender string `protobuf:"bytes,5,opt,name=gender,proto3" json:"gender,omitempty"`
Country string `protobuf:"bytes,6,opt,name=country,proto3" json:"country,omitempty"`
InviteCode string `protobuf:"bytes,7,opt,name=invite_code,json=inviteCode,proto3" json:"invite_code,omitempty"`
DeviceId string `protobuf:"bytes,8,opt,name=device_id,json=deviceId,proto3" json:"device_id,omitempty"`
Device string `protobuf:"bytes,9,opt,name=device,proto3" json:"device,omitempty"`
OsVersion string `protobuf:"bytes,10,opt,name=os_version,json=osVersion,proto3" json:"os_version,omitempty"`
Birth string `protobuf:"bytes,11,opt,name=birth,proto3" json:"birth,omitempty"`
AppVersion string `protobuf:"bytes,12,opt,name=app_version,json=appVersion,proto3" json:"app_version,omitempty"`
BuildNumber string `protobuf:"bytes,13,opt,name=build_number,json=buildNumber,proto3" json:"build_number,omitempty"`
Source string `protobuf:"bytes,14,opt,name=source,proto3" json:"source,omitempty"`
InstallChannel string `protobuf:"bytes,15,opt,name=install_channel,json=installChannel,proto3" json:"install_channel,omitempty"`
Campaign string `protobuf:"bytes,16,opt,name=campaign,proto3" json:"campaign,omitempty"`
Platform string `protobuf:"bytes,17,opt,name=platform,proto3" json:"platform,omitempty"`
Language string `protobuf:"bytes,18,opt,name=language,proto3" json:"language,omitempty"`
Timezone string `protobuf:"bytes,19,opt,name=timezone,proto3" json:"timezone,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *QuickCreateAccountRequest) Reset() {
@ -650,12 +645,11 @@ func (x *QuickCreateAccountRequest) GetTimezone() string {
// QuickCreateAccountResponse 返回新账号登录态display_user_id 即后续账号密码登录用账号。
type QuickCreateAccountResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
Token *AuthToken `protobuf:"bytes,1,opt,name=token,proto3" json:"token,omitempty"`
PasswordSet bool `protobuf:"varint,2,opt,name=password_set,json=passwordSet,proto3" json:"password_set,omitempty"`
unknownFields protoimpl.UnknownFields
Token *AuthToken `protobuf:"bytes,1,opt,name=token,proto3" json:"token,omitempty"`
PasswordSet bool `protobuf:"varint,2,opt,name=password_set,json=passwordSet,proto3" json:"password_set,omitempty"`
sizeCache protoimpl.SizeCache
}
func (x *QuickCreateAccountResponse) Reset() {
@ -704,12 +698,11 @@ func (x *QuickCreateAccountResponse) GetPasswordSet() bool {
// RefreshTokenRequest 使用 refresh token 换取新 token。
type RefreshTokenRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
Meta *RequestMeta `protobuf:"bytes,1,opt,name=meta,proto3" json:"meta,omitempty"`
RefreshToken string `protobuf:"bytes,2,opt,name=refresh_token,json=refreshToken,proto3" json:"refresh_token,omitempty"`
unknownFields protoimpl.UnknownFields
Meta *RequestMeta `protobuf:"bytes,1,opt,name=meta,proto3" json:"meta,omitempty"`
RefreshToken string `protobuf:"bytes,2,opt,name=refresh_token,json=refreshToken,proto3" json:"refresh_token,omitempty"`
sizeCache protoimpl.SizeCache
}
func (x *RefreshTokenRequest) Reset() {
@ -758,11 +751,10 @@ func (x *RefreshTokenRequest) GetRefreshToken() string {
// RefreshTokenResponse 返回轮换后的令牌。
type RefreshTokenResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
Token *AuthToken `protobuf:"bytes,1,opt,name=token,proto3" json:"token,omitempty"`
unknownFields protoimpl.UnknownFields
Token *AuthToken `protobuf:"bytes,1,opt,name=token,proto3" json:"token,omitempty"`
sizeCache protoimpl.SizeCache
}
func (x *RefreshTokenResponse) Reset() {
@ -804,13 +796,12 @@ func (x *RefreshTokenResponse) GetToken() *AuthToken {
// LogoutRequest 失效指定会话或 refresh token。
type LogoutRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
Meta *RequestMeta `protobuf:"bytes,1,opt,name=meta,proto3" json:"meta,omitempty"`
SessionId string `protobuf:"bytes,2,opt,name=session_id,json=sessionId,proto3" json:"session_id,omitempty"`
RefreshToken string `protobuf:"bytes,3,opt,name=refresh_token,json=refreshToken,proto3" json:"refresh_token,omitempty"`
unknownFields protoimpl.UnknownFields
Meta *RequestMeta `protobuf:"bytes,1,opt,name=meta,proto3" json:"meta,omitempty"`
SessionId string `protobuf:"bytes,2,opt,name=session_id,json=sessionId,proto3" json:"session_id,omitempty"`
RefreshToken string `protobuf:"bytes,3,opt,name=refresh_token,json=refreshToken,proto3" json:"refresh_token,omitempty"`
sizeCache protoimpl.SizeCache
}
func (x *LogoutRequest) Reset() {
@ -866,11 +857,10 @@ func (x *LogoutRequest) GetRefreshToken() string {
// LogoutResponse 返回会话是否被失效。
type LogoutResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
Revoked bool `protobuf:"varint,1,opt,name=revoked,proto3" json:"revoked,omitempty"`
unknownFields protoimpl.UnknownFields
Revoked bool `protobuf:"varint,1,opt,name=revoked,proto3" json:"revoked,omitempty"`
sizeCache protoimpl.SizeCache
}
func (x *LogoutResponse) Reset() {
@ -912,16 +902,15 @@ func (x *LogoutResponse) GetRevoked() bool {
// RecordLoginBlockedRequest 记录 gateway 入口快拦拒绝的登录审计。
type RecordLoginBlockedRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
Meta *RequestMeta `protobuf:"bytes,1,opt,name=meta,proto3" json:"meta,omitempty"`
Channel string `protobuf:"bytes,2,opt,name=channel,proto3" json:"channel,omitempty"`
Platform string `protobuf:"bytes,3,opt,name=platform,proto3" json:"platform,omitempty"`
Language string `protobuf:"bytes,4,opt,name=language,proto3" json:"language,omitempty"`
Timezone string `protobuf:"bytes,5,opt,name=timezone,proto3" json:"timezone,omitempty"`
BlockReason string `protobuf:"bytes,6,opt,name=block_reason,json=blockReason,proto3" json:"block_reason,omitempty"`
unknownFields protoimpl.UnknownFields
Meta *RequestMeta `protobuf:"bytes,1,opt,name=meta,proto3" json:"meta,omitempty"`
Channel string `protobuf:"bytes,2,opt,name=channel,proto3" json:"channel,omitempty"`
Platform string `protobuf:"bytes,3,opt,name=platform,proto3" json:"platform,omitempty"`
Language string `protobuf:"bytes,4,opt,name=language,proto3" json:"language,omitempty"`
Timezone string `protobuf:"bytes,5,opt,name=timezone,proto3" json:"timezone,omitempty"`
BlockReason string `protobuf:"bytes,6,opt,name=block_reason,json=blockReason,proto3" json:"block_reason,omitempty"`
sizeCache protoimpl.SizeCache
}
func (x *RecordLoginBlockedRequest) Reset() {
@ -998,11 +987,10 @@ func (x *RecordLoginBlockedRequest) GetBlockReason() string {
// RecordLoginBlockedResponse 返回审计写入是否成功。
type RecordLoginBlockedResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
Recorded bool `protobuf:"varint,1,opt,name=recorded,proto3" json:"recorded,omitempty"`
unknownFields protoimpl.UnknownFields
Recorded bool `protobuf:"varint,1,opt,name=recorded,proto3" json:"recorded,omitempty"`
sizeCache protoimpl.SizeCache
}
func (x *RecordLoginBlockedResponse) Reset() {
@ -1044,13 +1032,12 @@ func (x *RecordLoginBlockedResponse) GetRecorded() bool {
// AppHeartbeatRequest 刷新当前登录会话的 App 在线心跳。
type AppHeartbeatRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
Meta *RequestMeta `protobuf:"bytes,1,opt,name=meta,proto3" json:"meta,omitempty"`
UserId int64 `protobuf:"varint,2,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
SessionId string `protobuf:"bytes,3,opt,name=session_id,json=sessionId,proto3" json:"session_id,omitempty"`
unknownFields protoimpl.UnknownFields
Meta *RequestMeta `protobuf:"bytes,1,opt,name=meta,proto3" json:"meta,omitempty"`
UserId int64 `protobuf:"varint,2,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
SessionId string `protobuf:"bytes,3,opt,name=session_id,json=sessionId,proto3" json:"session_id,omitempty"`
sizeCache protoimpl.SizeCache
}
func (x *AppHeartbeatRequest) Reset() {
@ -1106,13 +1093,12 @@ func (x *AppHeartbeatRequest) GetSessionId() string {
// AppHeartbeatResponse 返回本次会话心跳写入时间。
type AppHeartbeatResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
Accepted bool `protobuf:"varint,1,opt,name=accepted,proto3" json:"accepted,omitempty"`
HeartbeatAtMs int64 `protobuf:"varint,2,opt,name=heartbeat_at_ms,json=heartbeatAtMs,proto3" json:"heartbeat_at_ms,omitempty"`
ServerTimeMs int64 `protobuf:"varint,3,opt,name=server_time_ms,json=serverTimeMs,proto3" json:"server_time_ms,omitempty"`
unknownFields protoimpl.UnknownFields
Accepted bool `protobuf:"varint,1,opt,name=accepted,proto3" json:"accepted,omitempty"`
HeartbeatAtMs int64 `protobuf:"varint,2,opt,name=heartbeat_at_ms,json=heartbeatAtMs,proto3" json:"heartbeat_at_ms,omitempty"`
ServerTimeMs int64 `protobuf:"varint,3,opt,name=server_time_ms,json=serverTimeMs,proto3" json:"server_time_ms,omitempty"`
sizeCache protoimpl.SizeCache
}
func (x *AppHeartbeatResponse) Reset() {
@ -1168,241 +1154,126 @@ func (x *AppHeartbeatResponse) GetServerTimeMs() int64 {
var File_proto_user_v1_auth_proto protoreflect.FileDescriptor
var file_proto_user_v1_auth_proto_rawDesc = []byte{
0x0a, 0x18, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x2f, 0x76, 0x31, 0x2f,
0x61, 0x75, 0x74, 0x68, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0d, 0x68, 0x79, 0x61, 0x70,
0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x1a, 0x18, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x2f, 0x75, 0x73, 0x65, 0x72, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x22, 0x8a, 0x01, 0x0a, 0x14, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x50, 0x61, 0x73,
0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x04,
0x6d, 0x65, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x68, 0x79, 0x61,
0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x12, 0x26, 0x0a, 0x0f,
0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18,
0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x55, 0x73,
0x65, 0x72, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64,
0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64,
0x22, 0xea, 0x04, 0x0a, 0x16, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x54, 0x68, 0x69, 0x72, 0x64, 0x50,
0x61, 0x72, 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x04, 0x6d,
0x65, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x68, 0x79, 0x61, 0x70,
0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x12, 0x1a, 0x0a, 0x08, 0x70,
0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70,
0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x64, 0x65,
0x6e, 0x74, 0x69, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x72, 0x65,
0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e,
0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e,
0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x67, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x18, 0x05, 0x20,
0x01, 0x28, 0x09, 0x52, 0x06, 0x67, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x63,
0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f,
0x75, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x5f,
0x63, 0x6f, 0x64, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x69, 0x6e, 0x76, 0x69,
0x74, 0x65, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65,
0x5f, 0x69, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63,
0x65, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x18, 0x09, 0x20,
0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x61,
0x76, 0x61, 0x74, 0x61, 0x72, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x76, 0x61,
0x74, 0x61, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x62, 0x69, 0x72, 0x74, 0x68, 0x18, 0x0b, 0x20, 0x01,
0x28, 0x09, 0x52, 0x05, 0x62, 0x69, 0x72, 0x74, 0x68, 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x70, 0x70,
0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a,
0x61, 0x70, 0x70, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6f,
0x75, 0x72, 0x63, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72,
0x63, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x0e,
0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x12, 0x1a,
0x0a, 0x08, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09,
0x52, 0x08, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x6f, 0x73,
0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x10, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09,
0x6f, 0x73, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x62, 0x75, 0x69,
0x6c, 0x64, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x11, 0x20, 0x01, 0x28, 0x09, 0x52,
0x0b, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x1a, 0x0a, 0x08,
0x74, 0x69, 0x6d, 0x65, 0x7a, 0x6f, 0x6e, 0x65, 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08,
0x74, 0x69, 0x6d, 0x65, 0x7a, 0x6f, 0x6e, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x69, 0x6e, 0x73, 0x74,
0x61, 0x6c, 0x6c, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x18, 0x13, 0x20, 0x01, 0x28,
0x09, 0x52, 0x0e, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65,
0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x61, 0x6d, 0x70, 0x61, 0x69, 0x67, 0x6e, 0x18, 0x14, 0x20,
0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x61, 0x6d, 0x70, 0x61, 0x69, 0x67, 0x6e, 0x22, 0xb8, 0x01,
0x0a, 0x0c, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2e,
0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e,
0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75,
0x74, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1e,
0x0a, 0x0b, 0x69, 0x73, 0x5f, 0x6e, 0x65, 0x77, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x18, 0x02, 0x20,
0x01, 0x28, 0x08, 0x52, 0x09, 0x69, 0x73, 0x4e, 0x65, 0x77, 0x55, 0x73, 0x65, 0x72, 0x12, 0x2b,
0x0a, 0x11, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65,
0x74, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x70, 0x72, 0x6f, 0x66, 0x69,
0x6c, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x12, 0x2b, 0x0a, 0x11, 0x6f,
0x6e, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73,
0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x6f, 0x6e, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x69,
0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x79, 0x0a, 0x12, 0x53, 0x65, 0x74, 0x50,
0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2e,
0x0a, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x68,
0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x12, 0x17,
0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52,
0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77,
0x6f, 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77,
0x6f, 0x72, 0x64, 0x22, 0x38, 0x0a, 0x13, 0x53, 0x65, 0x74, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f,
0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x61,
0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x5f, 0x73, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08,
0x52, 0x0b, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x53, 0x65, 0x74, 0x22, 0xcd, 0x04,
0x0a, 0x19, 0x51, 0x75, 0x69, 0x63, 0x6b, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x63, 0x63,
0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x04, 0x6d,
0x65, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x68, 0x79, 0x61, 0x70,
0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x12, 0x1a, 0x0a, 0x08, 0x70,
0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70,
0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e,
0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e,
0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x76, 0x61, 0x74, 0x61, 0x72, 0x18, 0x04, 0x20,
0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x76, 0x61, 0x74, 0x61, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x67,
0x65, 0x6e, 0x64, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x67, 0x65, 0x6e,
0x64, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x06,
0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x1f, 0x0a,
0x0b, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x07, 0x20, 0x01,
0x28, 0x09, 0x52, 0x0a, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x1b,
0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28,
0x09, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x64,
0x65, 0x76, 0x69, 0x63, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x65, 0x76,
0x69, 0x63, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x6f, 0x73, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f,
0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6f, 0x73, 0x56, 0x65, 0x72, 0x73, 0x69,
0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x62, 0x69, 0x72, 0x74, 0x68, 0x18, 0x0b, 0x20, 0x01, 0x28,
0x09, 0x52, 0x05, 0x62, 0x69, 0x72, 0x74, 0x68, 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x70, 0x70, 0x5f,
0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61,
0x70, 0x70, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x62, 0x75, 0x69,
0x6c, 0x64, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52,
0x0b, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06,
0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x6f,
0x75, 0x72, 0x63, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x5f,
0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x69,
0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x1a, 0x0a,
0x08, 0x63, 0x61, 0x6d, 0x70, 0x61, 0x69, 0x67, 0x6e, 0x18, 0x10, 0x20, 0x01, 0x28, 0x09, 0x52,
0x08, 0x63, 0x61, 0x6d, 0x70, 0x61, 0x69, 0x67, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6c, 0x61,
0x74, 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x11, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6c, 0x61,
0x74, 0x66, 0x6f, 0x72, 0x6d, 0x12, 0x1a, 0x0a, 0x08, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67,
0x65, 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67,
0x65, 0x12, 0x1a, 0x0a, 0x08, 0x74, 0x69, 0x6d, 0x65, 0x7a, 0x6f, 0x6e, 0x65, 0x18, 0x13, 0x20,
0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x69, 0x6d, 0x65, 0x7a, 0x6f, 0x6e, 0x65, 0x22, 0x6f, 0x0a,
0x1a, 0x51, 0x75, 0x69, 0x63, 0x6b, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x63, 0x63, 0x6f,
0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2e, 0x0a, 0x05, 0x74,
0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x68, 0x79, 0x61,
0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x54,
0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x70,
0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x5f, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28,
0x08, 0x52, 0x0b, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x53, 0x65, 0x74, 0x22, 0x6a,
0x0a, 0x13, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x01, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72,
0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x52,
0x04, 0x6d, 0x65, 0x74, 0x61, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68,
0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x72, 0x65,
0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x46, 0x0a, 0x14, 0x52, 0x65,
0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x12, 0x2e, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x18, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76,
0x31, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x05, 0x74, 0x6f, 0x6b,
0x65, 0x6e, 0x22, 0x83, 0x01, 0x0a, 0x0d, 0x4c, 0x6f, 0x67, 0x6f, 0x75, 0x74, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e,
0x76, 0x31, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x04,
0x6d, 0x65, 0x74, 0x61, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f,
0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f,
0x6e, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x5f, 0x74,
0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x72, 0x65, 0x66, 0x72,
0x65, 0x73, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x2a, 0x0a, 0x0e, 0x4c, 0x6f, 0x67, 0x6f,
0x75, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65,
0x76, 0x6f, 0x6b, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x72, 0x65, 0x76,
0x6f, 0x6b, 0x65, 0x64, 0x22, 0xdc, 0x01, 0x0a, 0x19, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x4c,
0x6f, 0x67, 0x69, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x12, 0x2e, 0x0a, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x1a, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31,
0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x04, 0x6d, 0x65,
0x74, 0x61, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x18, 0x02, 0x20,
0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x1a, 0x0a, 0x08,
0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08,
0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x12, 0x1a, 0x0a, 0x08, 0x6c, 0x61, 0x6e, 0x67,
0x75, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6c, 0x61, 0x6e, 0x67,
0x75, 0x61, 0x67, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x74, 0x69, 0x6d, 0x65, 0x7a, 0x6f, 0x6e, 0x65,
0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x69, 0x6d, 0x65, 0x7a, 0x6f, 0x6e, 0x65,
0x12, 0x21, 0x0a, 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e,
0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x61,
0x73, 0x6f, 0x6e, 0x22, 0x38, 0x0a, 0x1a, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x4c, 0x6f, 0x67,
0x69, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x65, 0x64, 0x18, 0x01, 0x20,
0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x65, 0x64, 0x22, 0x7d, 0x0a,
0x13, 0x41, 0x70, 0x70, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e,
0x76, 0x31, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x04,
0x6d, 0x65, 0x74, 0x61, 0x12, 0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18,
0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1d, 0x0a,
0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28,
0x09, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x22, 0x80, 0x01, 0x0a,
0x14, 0x41, 0x70, 0x70, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x52, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65,
0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65,
0x64, 0x12, 0x26, 0x0a, 0x0f, 0x68, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x5f, 0x61,
0x74, 0x5f, 0x6d, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x68, 0x65, 0x61, 0x72,
0x74, 0x62, 0x65, 0x61, 0x74, 0x41, 0x74, 0x4d, 0x73, 0x12, 0x24, 0x0a, 0x0e, 0x73, 0x65, 0x72,
0x76, 0x65, 0x72, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28,
0x03, 0x52, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x54, 0x69, 0x6d, 0x65, 0x4d, 0x73, 0x32,
0xdc, 0x05, 0x0a, 0x0b, 0x41, 0x75, 0x74, 0x68, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12,
0x51, 0x0a, 0x0d, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64,
0x12, 0x23, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31,
0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73,
0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x12, 0x55, 0x0a, 0x0f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x54, 0x68, 0x69, 0x72, 0x64,
0x50, 0x61, 0x72, 0x74, 0x79, 0x12, 0x25, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73,
0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x54, 0x68, 0x69, 0x72, 0x64,
0x50, 0x61, 0x72, 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x68,
0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74,
0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x54, 0x0a, 0x0b, 0x53, 0x65, 0x74,
0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x21, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70,
0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x50, 0x61, 0x73, 0x73,
0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x68, 0x79,
0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x50,
0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
0x69, 0x0a, 0x12, 0x51, 0x75, 0x69, 0x63, 0x6b, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x63,
0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x28, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73,
0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x69, 0x63, 0x6b, 0x43, 0x72, 0x65, 0x61, 0x74,
0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
0x29, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e,
0x51, 0x75, 0x69, 0x63, 0x6b, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75,
0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x57, 0x0a, 0x0c, 0x52, 0x65,
0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x22, 0x2e, 0x68, 0x79, 0x61,
0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x66, 0x72, 0x65,
0x73, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23,
0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x52,
0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x12, 0x45, 0x0a, 0x06, 0x4c, 0x6f, 0x67, 0x6f, 0x75, 0x74, 0x12, 0x1c, 0x2e,
0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f,
0x67, 0x6f, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x68, 0x79,
0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x6f,
0x75, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x69, 0x0a, 0x12, 0x52, 0x65,
0x63, 0x6f, 0x72, 0x64, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64,
0x12, 0x28, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31,
0x2e, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x42, 0x6c, 0x6f, 0x63,
0x6b, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x68, 0x79, 0x61,
0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x72,
0x64, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x52, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x57, 0x0a, 0x0c, 0x41, 0x70, 0x70, 0x48, 0x65, 0x61, 0x72,
0x74, 0x62, 0x65, 0x61, 0x74, 0x12, 0x22, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73,
0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x70, 0x70, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65,
0x61, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x68, 0x79, 0x61, 0x70,
0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x70, 0x70, 0x48, 0x65, 0x61,
0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x26,
0x5a, 0x24, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x2f, 0x61, 0x70,
0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x2f, 0x76, 0x31, 0x3b,
0x75, 0x73, 0x65, 0x72, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
const file_proto_user_v1_auth_proto_rawDesc = "" +
"\n" +
"\x18proto/user/v1/auth.proto\x12\rhyapp.user.v1\x1a\x18proto/user/v1/user.proto\"\x8a\x01\n" +
"\x14LoginPasswordRequest\x12.\n" +
"\x04meta\x18\x01 \x01(\v2\x1a.hyapp.user.v1.RequestMetaR\x04meta\x12&\n" +
"\x0fdisplay_user_id\x18\x02 \x01(\tR\rdisplayUserId\x12\x1a\n" +
"\bpassword\x18\x03 \x01(\tR\bpassword\"\xea\x04\n" +
"\x16LoginThirdPartyRequest\x12.\n" +
"\x04meta\x18\x01 \x01(\v2\x1a.hyapp.user.v1.RequestMetaR\x04meta\x12\x1a\n" +
"\bprovider\x18\x02 \x01(\tR\bprovider\x12\x1e\n" +
"\n" +
"credential\x18\x03 \x01(\tR\n" +
"credential\x12\x1a\n" +
"\busername\x18\x04 \x01(\tR\busername\x12\x16\n" +
"\x06gender\x18\x05 \x01(\tR\x06gender\x12\x18\n" +
"\acountry\x18\x06 \x01(\tR\acountry\x12\x1f\n" +
"\vinvite_code\x18\a \x01(\tR\n" +
"inviteCode\x12\x1b\n" +
"\tdevice_id\x18\b \x01(\tR\bdeviceId\x12\x16\n" +
"\x06device\x18\t \x01(\tR\x06device\x12\x16\n" +
"\x06avatar\x18\n" +
" \x01(\tR\x06avatar\x12\x14\n" +
"\x05birth\x18\v \x01(\tR\x05birth\x12\x1f\n" +
"\vapp_version\x18\f \x01(\tR\n" +
"appVersion\x12\x16\n" +
"\x06source\x18\r \x01(\tR\x06source\x12\x1a\n" +
"\bplatform\x18\x0e \x01(\tR\bplatform\x12\x1a\n" +
"\blanguage\x18\x0f \x01(\tR\blanguage\x12\x1d\n" +
"\n" +
"os_version\x18\x10 \x01(\tR\tosVersion\x12!\n" +
"\fbuild_number\x18\x11 \x01(\tR\vbuildNumber\x12\x1a\n" +
"\btimezone\x18\x12 \x01(\tR\btimezone\x12'\n" +
"\x0finstall_channel\x18\x13 \x01(\tR\x0einstallChannel\x12\x1a\n" +
"\bcampaign\x18\x14 \x01(\tR\bcampaign\"\xb8\x01\n" +
"\fAuthResponse\x12.\n" +
"\x05token\x18\x01 \x01(\v2\x18.hyapp.user.v1.AuthTokenR\x05token\x12\x1e\n" +
"\vis_new_user\x18\x02 \x01(\bR\tisNewUser\x12+\n" +
"\x11profile_completed\x18\x03 \x01(\bR\x10profileCompleted\x12+\n" +
"\x11onboarding_status\x18\x04 \x01(\tR\x10onboardingStatus\"y\n" +
"\x12SetPasswordRequest\x12.\n" +
"\x04meta\x18\x01 \x01(\v2\x1a.hyapp.user.v1.RequestMetaR\x04meta\x12\x17\n" +
"\auser_id\x18\x02 \x01(\x03R\x06userId\x12\x1a\n" +
"\bpassword\x18\x03 \x01(\tR\bpassword\"8\n" +
"\x13SetPasswordResponse\x12!\n" +
"\fpassword_set\x18\x01 \x01(\bR\vpasswordSet\"\xcd\x04\n" +
"\x19QuickCreateAccountRequest\x12.\n" +
"\x04meta\x18\x01 \x01(\v2\x1a.hyapp.user.v1.RequestMetaR\x04meta\x12\x1a\n" +
"\bpassword\x18\x02 \x01(\tR\bpassword\x12\x1a\n" +
"\busername\x18\x03 \x01(\tR\busername\x12\x16\n" +
"\x06avatar\x18\x04 \x01(\tR\x06avatar\x12\x16\n" +
"\x06gender\x18\x05 \x01(\tR\x06gender\x12\x18\n" +
"\acountry\x18\x06 \x01(\tR\acountry\x12\x1f\n" +
"\vinvite_code\x18\a \x01(\tR\n" +
"inviteCode\x12\x1b\n" +
"\tdevice_id\x18\b \x01(\tR\bdeviceId\x12\x16\n" +
"\x06device\x18\t \x01(\tR\x06device\x12\x1d\n" +
"\n" +
"os_version\x18\n" +
" \x01(\tR\tosVersion\x12\x14\n" +
"\x05birth\x18\v \x01(\tR\x05birth\x12\x1f\n" +
"\vapp_version\x18\f \x01(\tR\n" +
"appVersion\x12!\n" +
"\fbuild_number\x18\r \x01(\tR\vbuildNumber\x12\x16\n" +
"\x06source\x18\x0e \x01(\tR\x06source\x12'\n" +
"\x0finstall_channel\x18\x0f \x01(\tR\x0einstallChannel\x12\x1a\n" +
"\bcampaign\x18\x10 \x01(\tR\bcampaign\x12\x1a\n" +
"\bplatform\x18\x11 \x01(\tR\bplatform\x12\x1a\n" +
"\blanguage\x18\x12 \x01(\tR\blanguage\x12\x1a\n" +
"\btimezone\x18\x13 \x01(\tR\btimezone\"o\n" +
"\x1aQuickCreateAccountResponse\x12.\n" +
"\x05token\x18\x01 \x01(\v2\x18.hyapp.user.v1.AuthTokenR\x05token\x12!\n" +
"\fpassword_set\x18\x02 \x01(\bR\vpasswordSet\"j\n" +
"\x13RefreshTokenRequest\x12.\n" +
"\x04meta\x18\x01 \x01(\v2\x1a.hyapp.user.v1.RequestMetaR\x04meta\x12#\n" +
"\rrefresh_token\x18\x02 \x01(\tR\frefreshToken\"F\n" +
"\x14RefreshTokenResponse\x12.\n" +
"\x05token\x18\x01 \x01(\v2\x18.hyapp.user.v1.AuthTokenR\x05token\"\x83\x01\n" +
"\rLogoutRequest\x12.\n" +
"\x04meta\x18\x01 \x01(\v2\x1a.hyapp.user.v1.RequestMetaR\x04meta\x12\x1d\n" +
"\n" +
"session_id\x18\x02 \x01(\tR\tsessionId\x12#\n" +
"\rrefresh_token\x18\x03 \x01(\tR\frefreshToken\"*\n" +
"\x0eLogoutResponse\x12\x18\n" +
"\arevoked\x18\x01 \x01(\bR\arevoked\"\xdc\x01\n" +
"\x19RecordLoginBlockedRequest\x12.\n" +
"\x04meta\x18\x01 \x01(\v2\x1a.hyapp.user.v1.RequestMetaR\x04meta\x12\x18\n" +
"\achannel\x18\x02 \x01(\tR\achannel\x12\x1a\n" +
"\bplatform\x18\x03 \x01(\tR\bplatform\x12\x1a\n" +
"\blanguage\x18\x04 \x01(\tR\blanguage\x12\x1a\n" +
"\btimezone\x18\x05 \x01(\tR\btimezone\x12!\n" +
"\fblock_reason\x18\x06 \x01(\tR\vblockReason\"8\n" +
"\x1aRecordLoginBlockedResponse\x12\x1a\n" +
"\brecorded\x18\x01 \x01(\bR\brecorded\"}\n" +
"\x13AppHeartbeatRequest\x12.\n" +
"\x04meta\x18\x01 \x01(\v2\x1a.hyapp.user.v1.RequestMetaR\x04meta\x12\x17\n" +
"\auser_id\x18\x02 \x01(\x03R\x06userId\x12\x1d\n" +
"\n" +
"session_id\x18\x03 \x01(\tR\tsessionId\"\x80\x01\n" +
"\x14AppHeartbeatResponse\x12\x1a\n" +
"\baccepted\x18\x01 \x01(\bR\baccepted\x12&\n" +
"\x0fheartbeat_at_ms\x18\x02 \x01(\x03R\rheartbeatAtMs\x12$\n" +
"\x0eserver_time_ms\x18\x03 \x01(\x03R\fserverTimeMs2\xdc\x05\n" +
"\vAuthService\x12Q\n" +
"\rLoginPassword\x12#.hyapp.user.v1.LoginPasswordRequest\x1a\x1b.hyapp.user.v1.AuthResponse\x12U\n" +
"\x0fLoginThirdParty\x12%.hyapp.user.v1.LoginThirdPartyRequest\x1a\x1b.hyapp.user.v1.AuthResponse\x12T\n" +
"\vSetPassword\x12!.hyapp.user.v1.SetPasswordRequest\x1a\".hyapp.user.v1.SetPasswordResponse\x12i\n" +
"\x12QuickCreateAccount\x12(.hyapp.user.v1.QuickCreateAccountRequest\x1a).hyapp.user.v1.QuickCreateAccountResponse\x12W\n" +
"\fRefreshToken\x12\".hyapp.user.v1.RefreshTokenRequest\x1a#.hyapp.user.v1.RefreshTokenResponse\x12E\n" +
"\x06Logout\x12\x1c.hyapp.user.v1.LogoutRequest\x1a\x1d.hyapp.user.v1.LogoutResponse\x12i\n" +
"\x12RecordLoginBlocked\x12(.hyapp.user.v1.RecordLoginBlockedRequest\x1a).hyapp.user.v1.RecordLoginBlockedResponse\x12W\n" +
"\fAppHeartbeat\x12\".hyapp.user.v1.AppHeartbeatRequest\x1a#.hyapp.user.v1.AppHeartbeatResponseB&Z$hyapp.local/api/proto/user/v1;userv1b\x06proto3"
var (
file_proto_user_v1_auth_proto_rawDescOnce sync.Once
file_proto_user_v1_auth_proto_rawDescData = file_proto_user_v1_auth_proto_rawDesc
file_proto_user_v1_auth_proto_rawDescData []byte
)
func file_proto_user_v1_auth_proto_rawDescGZIP() []byte {
file_proto_user_v1_auth_proto_rawDescOnce.Do(func() {
file_proto_user_v1_auth_proto_rawDescData = protoimpl.X.CompressGZIP(file_proto_user_v1_auth_proto_rawDescData)
file_proto_user_v1_auth_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_proto_user_v1_auth_proto_rawDesc), len(file_proto_user_v1_auth_proto_rawDesc)))
})
return file_proto_user_v1_auth_proto_rawDescData
}
@ -1472,7 +1343,7 @@ func file_proto_user_v1_auth_proto_init() {
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_proto_user_v1_auth_proto_rawDesc,
RawDescriptor: unsafe.Slice(unsafe.StringData(file_proto_user_v1_auth_proto_rawDesc), len(file_proto_user_v1_auth_proto_rawDesc)),
NumEnums: 0,
NumMessages: 15,
NumExtensions: 0,
@ -1483,7 +1354,6 @@ func file_proto_user_v1_auth_proto_init() {
MessageInfos: file_proto_user_v1_auth_proto_msgTypes,
}.Build()
File_proto_user_v1_auth_proto = out.File
file_proto_user_v1_auth_proto_rawDesc = nil
file_proto_user_v1_auth_proto_goTypes = nil
file_proto_user_v1_auth_proto_depIdxs = nil
}

View File

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.5.1
// - protoc-gen-go-grpc v1.6.2
// - protoc v7.35.0
// source: proto/user/v1/auth.proto
@ -158,28 +158,28 @@ type AuthServiceServer interface {
type UnimplementedAuthServiceServer struct{}
func (UnimplementedAuthServiceServer) LoginPassword(context.Context, *LoginPasswordRequest) (*AuthResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method LoginPassword not implemented")
return nil, status.Error(codes.Unimplemented, "method LoginPassword not implemented")
}
func (UnimplementedAuthServiceServer) LoginThirdParty(context.Context, *LoginThirdPartyRequest) (*AuthResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method LoginThirdParty not implemented")
return nil, status.Error(codes.Unimplemented, "method LoginThirdParty not implemented")
}
func (UnimplementedAuthServiceServer) SetPassword(context.Context, *SetPasswordRequest) (*SetPasswordResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method SetPassword not implemented")
return nil, status.Error(codes.Unimplemented, "method SetPassword not implemented")
}
func (UnimplementedAuthServiceServer) QuickCreateAccount(context.Context, *QuickCreateAccountRequest) (*QuickCreateAccountResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method QuickCreateAccount not implemented")
return nil, status.Error(codes.Unimplemented, "method QuickCreateAccount not implemented")
}
func (UnimplementedAuthServiceServer) RefreshToken(context.Context, *RefreshTokenRequest) (*RefreshTokenResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method RefreshToken not implemented")
return nil, status.Error(codes.Unimplemented, "method RefreshToken not implemented")
}
func (UnimplementedAuthServiceServer) Logout(context.Context, *LogoutRequest) (*LogoutResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Logout not implemented")
return nil, status.Error(codes.Unimplemented, "method Logout not implemented")
}
func (UnimplementedAuthServiceServer) RecordLoginBlocked(context.Context, *RecordLoginBlockedRequest) (*RecordLoginBlockedResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method RecordLoginBlocked not implemented")
return nil, status.Error(codes.Unimplemented, "method RecordLoginBlocked not implemented")
}
func (UnimplementedAuthServiceServer) AppHeartbeat(context.Context, *AppHeartbeatRequest) (*AppHeartbeatResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method AppHeartbeat not implemented")
return nil, status.Error(codes.Unimplemented, "method AppHeartbeat not implemented")
}
func (UnimplementedAuthServiceServer) mustEmbedUnimplementedAuthServiceServer() {}
func (UnimplementedAuthServiceServer) testEmbeddedByValue() {}
@ -192,7 +192,7 @@ type UnsafeAuthServiceServer interface {
}
func RegisterAuthServiceServer(s grpc.ServiceRegistrar, srv AuthServiceServer) {
// If the following call pancis, it indicates UnimplementedAuthServiceServer was
// If the following call panics, it indicates UnimplementedAuthServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.5.1
// - protoc-gen-go-grpc v1.6.2
// - protoc v7.35.0
// source: proto/user/v1/host.proto
@ -301,61 +301,61 @@ type UserHostServiceServer interface {
type UnimplementedUserHostServiceServer struct{}
func (UnimplementedUserHostServiceServer) SearchAgencies(context.Context, *SearchAgenciesRequest) (*SearchAgenciesResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method SearchAgencies not implemented")
return nil, status.Error(codes.Unimplemented, "method SearchAgencies not implemented")
}
func (UnimplementedUserHostServiceServer) ApplyToAgency(context.Context, *ApplyToAgencyRequest) (*ApplyToAgencyResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ApplyToAgency not implemented")
return nil, status.Error(codes.Unimplemented, "method ApplyToAgency not implemented")
}
func (UnimplementedUserHostServiceServer) ReviewAgencyApplication(context.Context, *ReviewAgencyApplicationRequest) (*ReviewAgencyApplicationResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ReviewAgencyApplication not implemented")
return nil, status.Error(codes.Unimplemented, "method ReviewAgencyApplication not implemented")
}
func (UnimplementedUserHostServiceServer) KickAgencyHost(context.Context, *KickAgencyHostRequest) (*KickAgencyHostResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method KickAgencyHost not implemented")
return nil, status.Error(codes.Unimplemented, "method KickAgencyHost not implemented")
}
func (UnimplementedUserHostServiceServer) InviteAgency(context.Context, *InviteAgencyRequest) (*InviteAgencyResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method InviteAgency not implemented")
return nil, status.Error(codes.Unimplemented, "method InviteAgency not implemented")
}
func (UnimplementedUserHostServiceServer) InviteBD(context.Context, *InviteBDRequest) (*InviteBDResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method InviteBD not implemented")
return nil, status.Error(codes.Unimplemented, "method InviteBD not implemented")
}
func (UnimplementedUserHostServiceServer) ListBDLeaderBDs(context.Context, *ListBDLeaderBDsRequest) (*ListBDLeaderBDsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListBDLeaderBDs not implemented")
return nil, status.Error(codes.Unimplemented, "method ListBDLeaderBDs not implemented")
}
func (UnimplementedUserHostServiceServer) ListBDLeaderAgencies(context.Context, *ListBDLeaderAgenciesRequest) (*ListBDLeaderAgenciesResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListBDLeaderAgencies not implemented")
return nil, status.Error(codes.Unimplemented, "method ListBDLeaderAgencies not implemented")
}
func (UnimplementedUserHostServiceServer) ListBDAgencies(context.Context, *ListBDAgenciesRequest) (*ListBDAgenciesResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListBDAgencies not implemented")
return nil, status.Error(codes.Unimplemented, "method ListBDAgencies not implemented")
}
func (UnimplementedUserHostServiceServer) ProcessRoleInvitation(context.Context, *ProcessRoleInvitationRequest) (*ProcessRoleInvitationResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ProcessRoleInvitation not implemented")
return nil, status.Error(codes.Unimplemented, "method ProcessRoleInvitation not implemented")
}
func (UnimplementedUserHostServiceServer) GetHostProfile(context.Context, *GetHostProfileRequest) (*GetHostProfileResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetHostProfile not implemented")
return nil, status.Error(codes.Unimplemented, "method GetHostProfile not implemented")
}
func (UnimplementedUserHostServiceServer) GetBDProfile(context.Context, *GetBDProfileRequest) (*GetBDProfileResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetBDProfile not implemented")
return nil, status.Error(codes.Unimplemented, "method GetBDProfile not implemented")
}
func (UnimplementedUserHostServiceServer) GetCoinSellerProfile(context.Context, *GetCoinSellerProfileRequest) (*GetCoinSellerProfileResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetCoinSellerProfile not implemented")
return nil, status.Error(codes.Unimplemented, "method GetCoinSellerProfile not implemented")
}
func (UnimplementedUserHostServiceServer) ListActiveCoinSellersInMyRegion(context.Context, *ListActiveCoinSellersInMyRegionRequest) (*ListActiveCoinSellersInMyRegionResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListActiveCoinSellersInMyRegion not implemented")
return nil, status.Error(codes.Unimplemented, "method ListActiveCoinSellersInMyRegion not implemented")
}
func (UnimplementedUserHostServiceServer) GetUserRoleSummary(context.Context, *GetUserRoleSummaryRequest) (*GetUserRoleSummaryResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetUserRoleSummary not implemented")
return nil, status.Error(codes.Unimplemented, "method GetUserRoleSummary not implemented")
}
func (UnimplementedUserHostServiceServer) GetAgency(context.Context, *GetAgencyRequest) (*GetAgencyResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetAgency not implemented")
return nil, status.Error(codes.Unimplemented, "method GetAgency not implemented")
}
func (UnimplementedUserHostServiceServer) CheckBusinessCapability(context.Context, *CheckBusinessCapabilityRequest) (*CheckBusinessCapabilityResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method CheckBusinessCapability not implemented")
return nil, status.Error(codes.Unimplemented, "method CheckBusinessCapability not implemented")
}
func (UnimplementedUserHostServiceServer) GetAgencyMembers(context.Context, *GetAgencyMembersRequest) (*GetAgencyMembersResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetAgencyMembers not implemented")
return nil, status.Error(codes.Unimplemented, "method GetAgencyMembers not implemented")
}
func (UnimplementedUserHostServiceServer) GetAgencyApplications(context.Context, *GetAgencyApplicationsRequest) (*GetAgencyApplicationsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetAgencyApplications not implemented")
return nil, status.Error(codes.Unimplemented, "method GetAgencyApplications not implemented")
}
func (UnimplementedUserHostServiceServer) mustEmbedUnimplementedUserHostServiceServer() {}
func (UnimplementedUserHostServiceServer) testEmbeddedByValue() {}
@ -368,7 +368,7 @@ type UnsafeUserHostServiceServer interface {
}
func RegisterUserHostServiceServer(s grpc.ServiceRegistrar, srv UserHostServiceServer) {
// If the following call pancis, it indicates UnimplementedUserHostServiceServer was
// If the following call panics, it indicates UnimplementedUserHostServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
@ -961,31 +961,31 @@ type UserHostAdminServiceServer interface {
type UnimplementedUserHostAdminServiceServer struct{}
func (UnimplementedUserHostAdminServiceServer) CreateBDLeader(context.Context, *CreateBDLeaderRequest) (*CreateBDLeaderResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreateBDLeader not implemented")
return nil, status.Error(codes.Unimplemented, "method CreateBDLeader not implemented")
}
func (UnimplementedUserHostAdminServiceServer) CreateBD(context.Context, *CreateBDRequest) (*CreateBDResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreateBD not implemented")
return nil, status.Error(codes.Unimplemented, "method CreateBD not implemented")
}
func (UnimplementedUserHostAdminServiceServer) SetBDStatus(context.Context, *SetBDStatusRequest) (*SetBDStatusResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method SetBDStatus not implemented")
return nil, status.Error(codes.Unimplemented, "method SetBDStatus not implemented")
}
func (UnimplementedUserHostAdminServiceServer) CreateCoinSeller(context.Context, *CreateCoinSellerRequest) (*CreateCoinSellerResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreateCoinSeller not implemented")
return nil, status.Error(codes.Unimplemented, "method CreateCoinSeller not implemented")
}
func (UnimplementedUserHostAdminServiceServer) SetCoinSellerStatus(context.Context, *SetCoinSellerStatusRequest) (*SetCoinSellerStatusResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method SetCoinSellerStatus not implemented")
return nil, status.Error(codes.Unimplemented, "method SetCoinSellerStatus not implemented")
}
func (UnimplementedUserHostAdminServiceServer) CreateAgency(context.Context, *CreateAgencyRequest) (*CreateAgencyResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreateAgency not implemented")
return nil, status.Error(codes.Unimplemented, "method CreateAgency not implemented")
}
func (UnimplementedUserHostAdminServiceServer) CloseAgency(context.Context, *CloseAgencyRequest) (*CloseAgencyResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method CloseAgency not implemented")
return nil, status.Error(codes.Unimplemented, "method CloseAgency not implemented")
}
func (UnimplementedUserHostAdminServiceServer) DeleteAgency(context.Context, *DeleteAgencyRequest) (*DeleteAgencyResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method DeleteAgency not implemented")
return nil, status.Error(codes.Unimplemented, "method DeleteAgency not implemented")
}
func (UnimplementedUserHostAdminServiceServer) SetAgencyJoinEnabled(context.Context, *SetAgencyJoinEnabledRequest) (*SetAgencyJoinEnabledResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method SetAgencyJoinEnabled not implemented")
return nil, status.Error(codes.Unimplemented, "method SetAgencyJoinEnabled not implemented")
}
func (UnimplementedUserHostAdminServiceServer) mustEmbedUnimplementedUserHostAdminServiceServer() {}
func (UnimplementedUserHostAdminServiceServer) testEmbeddedByValue() {}
@ -998,7 +998,7 @@ type UnsafeUserHostAdminServiceServer interface {
}
func RegisterUserHostAdminServiceServer(s grpc.ServiceRegistrar, srv UserHostAdminServiceServer) {
// If the following call pancis, it indicates UnimplementedUserHostAdminServiceServer was
// If the following call panics, it indicates UnimplementedUserHostAdminServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.

File diff suppressed because it is too large Load Diff

View File

@ -408,6 +408,11 @@ message CPRelationship {
reserved "equipped";
int64 formed_at_ms = 9;
int64 updated_at_ms = 10;
int64 current_level_threshold = 11;
int64 next_level_threshold = 12;
int64 needed_for_next_level = 13;
int32 level_progress_percent = 14;
bool max_level = 15;
}
message ListCPApplicationsRequest {
@ -619,6 +624,58 @@ message SetUserStatusResponse {
string access_token_revoke_error = 11;
}
message ManagerUserBlock {
string block_id = 1;
int64 manager_user_id = 2;
int64 target_user_id = 3;
string target_display_user_id = 4;
string target_username = 5;
string target_avatar = 6;
string country = 7;
int64 blocked_until_ms = 8;
string status = 9;
string reason = 10;
int64 created_at_ms = 11;
int64 updated_at_ms = 12;
}
message CreateManagerUserBlockRequest {
RequestMeta meta = 1;
string command_id = 2;
int64 manager_user_id = 3;
int64 target_user_id = 4;
int64 blocked_until_ms = 5;
string reason = 6;
}
message CreateManagerUserBlockResponse {
ManagerUserBlock block = 1;
SetUserStatusResponse status = 2;
}
message ListManagerUserBlocksRequest {
RequestMeta meta = 1;
int64 manager_user_id = 2;
string status = 3;
int32 page_size = 4;
}
message ListManagerUserBlocksResponse {
repeated ManagerUserBlock blocks = 1;
}
message UnblockManagerUserRequest {
RequestMeta meta = 1;
int64 manager_user_id = 2;
string block_id = 3;
string reason = 4;
}
message UnblockManagerUserResponse {
ManagerUserBlock block = 1;
SetUserStatusResponse status = 2;
}
// CompleteOnboardingRequest
message CompleteOnboardingRequest {
RequestMeta meta = 1;
@ -874,6 +931,9 @@ service UserService {
rpc ChangeUserCountry(ChangeUserCountryRequest) returns (ChangeUserCountryResponse);
rpc AdminChangeUserCountry(AdminChangeUserCountryRequest) returns (ChangeUserCountryResponse);
rpc SetUserStatus(SetUserStatusRequest) returns (SetUserStatusResponse);
rpc CreateManagerUserBlock(CreateManagerUserBlockRequest) returns (CreateManagerUserBlockResponse);
rpc ListManagerUserBlocks(ListManagerUserBlocksRequest) returns (ListManagerUserBlocksResponse);
rpc UnblockManagerUser(UnblockManagerUserRequest) returns (UnblockManagerUserResponse);
rpc CompleteOnboarding(CompleteOnboardingRequest) returns (CompleteOnboardingResponse);
}
@ -910,6 +970,7 @@ service UserCronService {
rpc ProcessLoginIPRiskBatch(CronBatchRequest) returns (CronBatchResponse);
rpc ProcessRegionRebuildBatch(CronBatchRequest) returns (CronBatchResponse);
rpc CompensateMicOpenSessions(CronBatchRequest) returns (CronBatchResponse);
rpc ExpireManagerUserBlocks(CronBatchRequest) returns (CronBatchResponse);
}
// UserDeviceService App token

View File

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.5.1
// - protoc-gen-go-grpc v1.6.2
// - protoc v7.35.0
// source: proto/user/v1/user.proto
@ -29,6 +29,9 @@ const (
UserService_ChangeUserCountry_FullMethodName = "/hyapp.user.v1.UserService/ChangeUserCountry"
UserService_AdminChangeUserCountry_FullMethodName = "/hyapp.user.v1.UserService/AdminChangeUserCountry"
UserService_SetUserStatus_FullMethodName = "/hyapp.user.v1.UserService/SetUserStatus"
UserService_CreateManagerUserBlock_FullMethodName = "/hyapp.user.v1.UserService/CreateManagerUserBlock"
UserService_ListManagerUserBlocks_FullMethodName = "/hyapp.user.v1.UserService/ListManagerUserBlocks"
UserService_UnblockManagerUser_FullMethodName = "/hyapp.user.v1.UserService/UnblockManagerUser"
UserService_CompleteOnboarding_FullMethodName = "/hyapp.user.v1.UserService/CompleteOnboarding"
)
@ -48,6 +51,9 @@ type UserServiceClient interface {
ChangeUserCountry(ctx context.Context, in *ChangeUserCountryRequest, opts ...grpc.CallOption) (*ChangeUserCountryResponse, error)
AdminChangeUserCountry(ctx context.Context, in *AdminChangeUserCountryRequest, opts ...grpc.CallOption) (*ChangeUserCountryResponse, error)
SetUserStatus(ctx context.Context, in *SetUserStatusRequest, opts ...grpc.CallOption) (*SetUserStatusResponse, error)
CreateManagerUserBlock(ctx context.Context, in *CreateManagerUserBlockRequest, opts ...grpc.CallOption) (*CreateManagerUserBlockResponse, error)
ListManagerUserBlocks(ctx context.Context, in *ListManagerUserBlocksRequest, opts ...grpc.CallOption) (*ListManagerUserBlocksResponse, error)
UnblockManagerUser(ctx context.Context, in *UnblockManagerUserRequest, opts ...grpc.CallOption) (*UnblockManagerUserResponse, error)
CompleteOnboarding(ctx context.Context, in *CompleteOnboardingRequest, opts ...grpc.CallOption) (*CompleteOnboardingResponse, error)
}
@ -159,6 +165,36 @@ func (c *userServiceClient) SetUserStatus(ctx context.Context, in *SetUserStatus
return out, nil
}
func (c *userServiceClient) CreateManagerUserBlock(ctx context.Context, in *CreateManagerUserBlockRequest, opts ...grpc.CallOption) (*CreateManagerUserBlockResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(CreateManagerUserBlockResponse)
err := c.cc.Invoke(ctx, UserService_CreateManagerUserBlock_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *userServiceClient) ListManagerUserBlocks(ctx context.Context, in *ListManagerUserBlocksRequest, opts ...grpc.CallOption) (*ListManagerUserBlocksResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ListManagerUserBlocksResponse)
err := c.cc.Invoke(ctx, UserService_ListManagerUserBlocks_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *userServiceClient) UnblockManagerUser(ctx context.Context, in *UnblockManagerUserRequest, opts ...grpc.CallOption) (*UnblockManagerUserResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(UnblockManagerUserResponse)
err := c.cc.Invoke(ctx, UserService_UnblockManagerUser_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *userServiceClient) CompleteOnboarding(ctx context.Context, in *CompleteOnboardingRequest, opts ...grpc.CallOption) (*CompleteOnboardingResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(CompleteOnboardingResponse)
@ -185,6 +221,9 @@ type UserServiceServer interface {
ChangeUserCountry(context.Context, *ChangeUserCountryRequest) (*ChangeUserCountryResponse, error)
AdminChangeUserCountry(context.Context, *AdminChangeUserCountryRequest) (*ChangeUserCountryResponse, error)
SetUserStatus(context.Context, *SetUserStatusRequest) (*SetUserStatusResponse, error)
CreateManagerUserBlock(context.Context, *CreateManagerUserBlockRequest) (*CreateManagerUserBlockResponse, error)
ListManagerUserBlocks(context.Context, *ListManagerUserBlocksRequest) (*ListManagerUserBlocksResponse, error)
UnblockManagerUser(context.Context, *UnblockManagerUserRequest) (*UnblockManagerUserResponse, error)
CompleteOnboarding(context.Context, *CompleteOnboardingRequest) (*CompleteOnboardingResponse, error)
mustEmbedUnimplementedUserServiceServer()
}
@ -197,37 +236,46 @@ type UserServiceServer interface {
type UnimplementedUserServiceServer struct{}
func (UnimplementedUserServiceServer) GetUser(context.Context, *GetUserRequest) (*GetUserResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetUser not implemented")
return nil, status.Error(codes.Unimplemented, "method GetUser not implemented")
}
func (UnimplementedUserServiceServer) BusinessUserLookup(context.Context, *BusinessUserLookupRequest) (*BusinessUserLookupResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method BusinessUserLookup not implemented")
return nil, status.Error(codes.Unimplemented, "method BusinessUserLookup not implemented")
}
func (UnimplementedUserServiceServer) GetMyProfileStats(context.Context, *GetMyProfileStatsRequest) (*GetMyProfileStatsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetMyProfileStats not implemented")
return nil, status.Error(codes.Unimplemented, "method GetMyProfileStats not implemented")
}
func (UnimplementedUserServiceServer) BatchGetUsers(context.Context, *BatchGetUsersRequest) (*BatchGetUsersResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method BatchGetUsers not implemented")
return nil, status.Error(codes.Unimplemented, "method BatchGetUsers not implemented")
}
func (UnimplementedUserServiceServer) ListUserIDs(context.Context, *ListUserIDsRequest) (*ListUserIDsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListUserIDs not implemented")
return nil, status.Error(codes.Unimplemented, "method ListUserIDs not implemented")
}
func (UnimplementedUserServiceServer) GetUserMicLifetimeStats(context.Context, *GetUserMicLifetimeStatsRequest) (*GetUserMicLifetimeStatsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetUserMicLifetimeStats not implemented")
return nil, status.Error(codes.Unimplemented, "method GetUserMicLifetimeStats not implemented")
}
func (UnimplementedUserServiceServer) UpdateUserProfile(context.Context, *UpdateUserProfileRequest) (*UpdateUserProfileResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method UpdateUserProfile not implemented")
return nil, status.Error(codes.Unimplemented, "method UpdateUserProfile not implemented")
}
func (UnimplementedUserServiceServer) ChangeUserCountry(context.Context, *ChangeUserCountryRequest) (*ChangeUserCountryResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ChangeUserCountry not implemented")
return nil, status.Error(codes.Unimplemented, "method ChangeUserCountry not implemented")
}
func (UnimplementedUserServiceServer) AdminChangeUserCountry(context.Context, *AdminChangeUserCountryRequest) (*ChangeUserCountryResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method AdminChangeUserCountry not implemented")
return nil, status.Error(codes.Unimplemented, "method AdminChangeUserCountry not implemented")
}
func (UnimplementedUserServiceServer) SetUserStatus(context.Context, *SetUserStatusRequest) (*SetUserStatusResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method SetUserStatus not implemented")
return nil, status.Error(codes.Unimplemented, "method SetUserStatus not implemented")
}
func (UnimplementedUserServiceServer) CreateManagerUserBlock(context.Context, *CreateManagerUserBlockRequest) (*CreateManagerUserBlockResponse, error) {
return nil, status.Error(codes.Unimplemented, "method CreateManagerUserBlock not implemented")
}
func (UnimplementedUserServiceServer) ListManagerUserBlocks(context.Context, *ListManagerUserBlocksRequest) (*ListManagerUserBlocksResponse, error) {
return nil, status.Error(codes.Unimplemented, "method ListManagerUserBlocks not implemented")
}
func (UnimplementedUserServiceServer) UnblockManagerUser(context.Context, *UnblockManagerUserRequest) (*UnblockManagerUserResponse, error) {
return nil, status.Error(codes.Unimplemented, "method UnblockManagerUser not implemented")
}
func (UnimplementedUserServiceServer) CompleteOnboarding(context.Context, *CompleteOnboardingRequest) (*CompleteOnboardingResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method CompleteOnboarding not implemented")
return nil, status.Error(codes.Unimplemented, "method CompleteOnboarding not implemented")
}
func (UnimplementedUserServiceServer) mustEmbedUnimplementedUserServiceServer() {}
func (UnimplementedUserServiceServer) testEmbeddedByValue() {}
@ -240,7 +288,7 @@ type UnsafeUserServiceServer interface {
}
func RegisterUserServiceServer(s grpc.ServiceRegistrar, srv UserServiceServer) {
// If the following call pancis, it indicates UnimplementedUserServiceServer was
// If the following call panics, it indicates UnimplementedUserServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
@ -430,6 +478,60 @@ func _UserService_SetUserStatus_Handler(srv interface{}, ctx context.Context, de
return interceptor(ctx, in, info, handler)
}
func _UserService_CreateManagerUserBlock_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(CreateManagerUserBlockRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(UserServiceServer).CreateManagerUserBlock(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: UserService_CreateManagerUserBlock_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(UserServiceServer).CreateManagerUserBlock(ctx, req.(*CreateManagerUserBlockRequest))
}
return interceptor(ctx, in, info, handler)
}
func _UserService_ListManagerUserBlocks_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ListManagerUserBlocksRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(UserServiceServer).ListManagerUserBlocks(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: UserService_ListManagerUserBlocks_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(UserServiceServer).ListManagerUserBlocks(ctx, req.(*ListManagerUserBlocksRequest))
}
return interceptor(ctx, in, info, handler)
}
func _UserService_UnblockManagerUser_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(UnblockManagerUserRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(UserServiceServer).UnblockManagerUser(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: UserService_UnblockManagerUser_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(UserServiceServer).UnblockManagerUser(ctx, req.(*UnblockManagerUserRequest))
}
return interceptor(ctx, in, info, handler)
}
func _UserService_CompleteOnboarding_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(CompleteOnboardingRequest)
if err := dec(in); err != nil {
@ -495,6 +597,18 @@ var UserService_ServiceDesc = grpc.ServiceDesc{
MethodName: "SetUserStatus",
Handler: _UserService_SetUserStatus_Handler,
},
{
MethodName: "CreateManagerUserBlock",
Handler: _UserService_CreateManagerUserBlock_Handler,
},
{
MethodName: "ListManagerUserBlocks",
Handler: _UserService_ListManagerUserBlocks_Handler,
},
{
MethodName: "UnblockManagerUser",
Handler: _UserService_UnblockManagerUser_Handler,
},
{
MethodName: "CompleteOnboarding",
Handler: _UserService_CompleteOnboarding_Handler,
@ -683,37 +797,37 @@ type UserSocialServiceServer interface {
type UnimplementedUserSocialServiceServer struct{}
func (UnimplementedUserSocialServiceServer) RecordProfileVisit(context.Context, *RecordProfileVisitRequest) (*RecordProfileVisitResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method RecordProfileVisit not implemented")
return nil, status.Error(codes.Unimplemented, "method RecordProfileVisit not implemented")
}
func (UnimplementedUserSocialServiceServer) ListProfileVisitors(context.Context, *ListProfileVisitorsRequest) (*ListProfileVisitorsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListProfileVisitors not implemented")
return nil, status.Error(codes.Unimplemented, "method ListProfileVisitors not implemented")
}
func (UnimplementedUserSocialServiceServer) FollowUser(context.Context, *FollowUserRequest) (*FollowUserResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method FollowUser not implemented")
return nil, status.Error(codes.Unimplemented, "method FollowUser not implemented")
}
func (UnimplementedUserSocialServiceServer) UnfollowUser(context.Context, *UnfollowUserRequest) (*UnfollowUserResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method UnfollowUser not implemented")
return nil, status.Error(codes.Unimplemented, "method UnfollowUser not implemented")
}
func (UnimplementedUserSocialServiceServer) ListFollowing(context.Context, *ListFollowingRequest) (*ListFollowingResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListFollowing not implemented")
return nil, status.Error(codes.Unimplemented, "method ListFollowing not implemented")
}
func (UnimplementedUserSocialServiceServer) ApplyFriend(context.Context, *ApplyFriendRequest) (*ApplyFriendResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ApplyFriend not implemented")
return nil, status.Error(codes.Unimplemented, "method ApplyFriend not implemented")
}
func (UnimplementedUserSocialServiceServer) AcceptFriendApplication(context.Context, *AcceptFriendApplicationRequest) (*AcceptFriendApplicationResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method AcceptFriendApplication not implemented")
return nil, status.Error(codes.Unimplemented, "method AcceptFriendApplication not implemented")
}
func (UnimplementedUserSocialServiceServer) DeleteFriend(context.Context, *DeleteFriendRequest) (*DeleteFriendResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method DeleteFriend not implemented")
return nil, status.Error(codes.Unimplemented, "method DeleteFriend not implemented")
}
func (UnimplementedUserSocialServiceServer) ListFriends(context.Context, *ListFriendsRequest) (*ListFriendsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListFriends not implemented")
return nil, status.Error(codes.Unimplemented, "method ListFriends not implemented")
}
func (UnimplementedUserSocialServiceServer) ListFriendApplications(context.Context, *ListFriendApplicationsRequest) (*ListFriendApplicationsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListFriendApplications not implemented")
return nil, status.Error(codes.Unimplemented, "method ListFriendApplications not implemented")
}
func (UnimplementedUserSocialServiceServer) SubmitReport(context.Context, *SubmitReportRequest) (*SubmitReportResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method SubmitReport not implemented")
return nil, status.Error(codes.Unimplemented, "method SubmitReport not implemented")
}
func (UnimplementedUserSocialServiceServer) mustEmbedUnimplementedUserSocialServiceServer() {}
func (UnimplementedUserSocialServiceServer) testEmbeddedByValue() {}
@ -726,7 +840,7 @@ type UnsafeUserSocialServiceServer interface {
}
func RegisterUserSocialServiceServer(s grpc.ServiceRegistrar, srv UserSocialServiceServer) {
// If the following call pancis, it indicates UnimplementedUserSocialServiceServer was
// If the following call panics, it indicates UnimplementedUserSocialServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
@ -1078,16 +1192,16 @@ type UserCPServiceServer interface {
type UnimplementedUserCPServiceServer struct{}
func (UnimplementedUserCPServiceServer) ListCPApplications(context.Context, *ListCPApplicationsRequest) (*ListCPApplicationsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListCPApplications not implemented")
return nil, status.Error(codes.Unimplemented, "method ListCPApplications not implemented")
}
func (UnimplementedUserCPServiceServer) AcceptCPApplication(context.Context, *AcceptCPApplicationRequest) (*AcceptCPApplicationResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method AcceptCPApplication not implemented")
return nil, status.Error(codes.Unimplemented, "method AcceptCPApplication not implemented")
}
func (UnimplementedUserCPServiceServer) RejectCPApplication(context.Context, *RejectCPApplicationRequest) (*RejectCPApplicationResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method RejectCPApplication not implemented")
return nil, status.Error(codes.Unimplemented, "method RejectCPApplication not implemented")
}
func (UnimplementedUserCPServiceServer) ListCPRelationships(context.Context, *ListCPRelationshipsRequest) (*ListCPRelationshipsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListCPRelationships not implemented")
return nil, status.Error(codes.Unimplemented, "method ListCPRelationships not implemented")
}
func (UnimplementedUserCPServiceServer) mustEmbedUnimplementedUserCPServiceServer() {}
func (UnimplementedUserCPServiceServer) testEmbeddedByValue() {}
@ -1100,7 +1214,7 @@ type UnsafeUserCPServiceServer interface {
}
func RegisterUserCPServiceServer(s grpc.ServiceRegistrar, srv UserCPServiceServer) {
// If the following call pancis, it indicates UnimplementedUserCPServiceServer was
// If the following call panics, it indicates UnimplementedUserCPServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
@ -1259,7 +1373,7 @@ type UserCPInternalServiceServer interface {
type UnimplementedUserCPInternalServiceServer struct{}
func (UnimplementedUserCPInternalServiceServer) ConsumeRoomGiftCPEvent(context.Context, *ConsumeRoomGiftCPEventRequest) (*ConsumeRoomGiftCPEventResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ConsumeRoomGiftCPEvent not implemented")
return nil, status.Error(codes.Unimplemented, "method ConsumeRoomGiftCPEvent not implemented")
}
func (UnimplementedUserCPInternalServiceServer) mustEmbedUnimplementedUserCPInternalServiceServer() {}
func (UnimplementedUserCPInternalServiceServer) testEmbeddedByValue() {}
@ -1272,7 +1386,7 @@ type UnsafeUserCPInternalServiceServer interface {
}
func RegisterUserCPInternalServiceServer(s grpc.ServiceRegistrar, srv UserCPInternalServiceServer) {
// If the following call pancis, it indicates UnimplementedUserCPInternalServiceServer was
// If the following call panics, it indicates UnimplementedUserCPInternalServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
@ -1320,6 +1434,7 @@ const (
UserCronService_ProcessLoginIPRiskBatch_FullMethodName = "/hyapp.user.v1.UserCronService/ProcessLoginIPRiskBatch"
UserCronService_ProcessRegionRebuildBatch_FullMethodName = "/hyapp.user.v1.UserCronService/ProcessRegionRebuildBatch"
UserCronService_CompensateMicOpenSessions_FullMethodName = "/hyapp.user.v1.UserCronService/CompensateMicOpenSessions"
UserCronService_ExpireManagerUserBlocks_FullMethodName = "/hyapp.user.v1.UserCronService/ExpireManagerUserBlocks"
)
// UserCronServiceClient is the client API for UserCronService service.
@ -1331,6 +1446,7 @@ type UserCronServiceClient interface {
ProcessLoginIPRiskBatch(ctx context.Context, in *CronBatchRequest, opts ...grpc.CallOption) (*CronBatchResponse, error)
ProcessRegionRebuildBatch(ctx context.Context, in *CronBatchRequest, opts ...grpc.CallOption) (*CronBatchResponse, error)
CompensateMicOpenSessions(ctx context.Context, in *CronBatchRequest, opts ...grpc.CallOption) (*CronBatchResponse, error)
ExpireManagerUserBlocks(ctx context.Context, in *CronBatchRequest, opts ...grpc.CallOption) (*CronBatchResponse, error)
}
type userCronServiceClient struct {
@ -1371,6 +1487,16 @@ func (c *userCronServiceClient) CompensateMicOpenSessions(ctx context.Context, i
return out, nil
}
func (c *userCronServiceClient) ExpireManagerUserBlocks(ctx context.Context, in *CronBatchRequest, opts ...grpc.CallOption) (*CronBatchResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(CronBatchResponse)
err := c.cc.Invoke(ctx, UserCronService_ExpireManagerUserBlocks_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
// UserCronServiceServer is the server API for UserCronService service.
// All implementations must embed UnimplementedUserCronServiceServer
// for forward compatibility.
@ -1380,6 +1506,7 @@ type UserCronServiceServer interface {
ProcessLoginIPRiskBatch(context.Context, *CronBatchRequest) (*CronBatchResponse, error)
ProcessRegionRebuildBatch(context.Context, *CronBatchRequest) (*CronBatchResponse, error)
CompensateMicOpenSessions(context.Context, *CronBatchRequest) (*CronBatchResponse, error)
ExpireManagerUserBlocks(context.Context, *CronBatchRequest) (*CronBatchResponse, error)
mustEmbedUnimplementedUserCronServiceServer()
}
@ -1391,13 +1518,16 @@ type UserCronServiceServer interface {
type UnimplementedUserCronServiceServer struct{}
func (UnimplementedUserCronServiceServer) ProcessLoginIPRiskBatch(context.Context, *CronBatchRequest) (*CronBatchResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ProcessLoginIPRiskBatch not implemented")
return nil, status.Error(codes.Unimplemented, "method ProcessLoginIPRiskBatch not implemented")
}
func (UnimplementedUserCronServiceServer) ProcessRegionRebuildBatch(context.Context, *CronBatchRequest) (*CronBatchResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ProcessRegionRebuildBatch not implemented")
return nil, status.Error(codes.Unimplemented, "method ProcessRegionRebuildBatch not implemented")
}
func (UnimplementedUserCronServiceServer) CompensateMicOpenSessions(context.Context, *CronBatchRequest) (*CronBatchResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method CompensateMicOpenSessions not implemented")
return nil, status.Error(codes.Unimplemented, "method CompensateMicOpenSessions not implemented")
}
func (UnimplementedUserCronServiceServer) ExpireManagerUserBlocks(context.Context, *CronBatchRequest) (*CronBatchResponse, error) {
return nil, status.Error(codes.Unimplemented, "method ExpireManagerUserBlocks not implemented")
}
func (UnimplementedUserCronServiceServer) mustEmbedUnimplementedUserCronServiceServer() {}
func (UnimplementedUserCronServiceServer) testEmbeddedByValue() {}
@ -1410,7 +1540,7 @@ type UnsafeUserCronServiceServer interface {
}
func RegisterUserCronServiceServer(s grpc.ServiceRegistrar, srv UserCronServiceServer) {
// If the following call pancis, it indicates UnimplementedUserCronServiceServer was
// If the following call panics, it indicates UnimplementedUserCronServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
@ -1474,6 +1604,24 @@ func _UserCronService_CompensateMicOpenSessions_Handler(srv interface{}, ctx con
return interceptor(ctx, in, info, handler)
}
func _UserCronService_ExpireManagerUserBlocks_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(CronBatchRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(UserCronServiceServer).ExpireManagerUserBlocks(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: UserCronService_ExpireManagerUserBlocks_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(UserCronServiceServer).ExpireManagerUserBlocks(ctx, req.(*CronBatchRequest))
}
return interceptor(ctx, in, info, handler)
}
// UserCronService_ServiceDesc is the grpc.ServiceDesc for UserCronService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
@ -1493,6 +1641,10 @@ var UserCronService_ServiceDesc = grpc.ServiceDesc{
MethodName: "CompensateMicOpenSessions",
Handler: _UserCronService_CompensateMicOpenSessions_Handler,
},
{
MethodName: "ExpireManagerUserBlocks",
Handler: _UserCronService_ExpireManagerUserBlocks_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "proto/user/v1/user.proto",
@ -1560,10 +1712,10 @@ type UserDeviceServiceServer interface {
type UnimplementedUserDeviceServiceServer struct{}
func (UnimplementedUserDeviceServiceServer) BindPushToken(context.Context, *BindPushTokenRequest) (*BindPushTokenResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method BindPushToken not implemented")
return nil, status.Error(codes.Unimplemented, "method BindPushToken not implemented")
}
func (UnimplementedUserDeviceServiceServer) DeletePushToken(context.Context, *DeletePushTokenRequest) (*DeletePushTokenResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method DeletePushToken not implemented")
return nil, status.Error(codes.Unimplemented, "method DeletePushToken not implemented")
}
func (UnimplementedUserDeviceServiceServer) mustEmbedUnimplementedUserDeviceServiceServer() {}
func (UnimplementedUserDeviceServiceServer) testEmbeddedByValue() {}
@ -1576,7 +1728,7 @@ type UnsafeUserDeviceServiceServer interface {
}
func RegisterUserDeviceServiceServer(s grpc.ServiceRegistrar, srv UserDeviceServiceServer) {
// If the following call pancis, it indicates UnimplementedUserDeviceServiceServer was
// If the following call panics, it indicates UnimplementedUserDeviceServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
@ -1691,7 +1843,7 @@ type AppRegistryServiceServer interface {
type UnimplementedAppRegistryServiceServer struct{}
func (UnimplementedAppRegistryServiceServer) ResolveApp(context.Context, *ResolveAppRequest) (*ResolveAppResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ResolveApp not implemented")
return nil, status.Error(codes.Unimplemented, "method ResolveApp not implemented")
}
func (UnimplementedAppRegistryServiceServer) mustEmbedUnimplementedAppRegistryServiceServer() {}
func (UnimplementedAppRegistryServiceServer) testEmbeddedByValue() {}
@ -1704,7 +1856,7 @@ type UnsafeAppRegistryServiceServer interface {
}
func RegisterAppRegistryServiceServer(s grpc.ServiceRegistrar, srv AppRegistryServiceServer) {
// If the following call pancis, it indicates UnimplementedAppRegistryServiceServer was
// If the following call panics, it indicates UnimplementedAppRegistryServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
@ -1812,10 +1964,10 @@ type CountryAdminServiceServer interface {
type UnimplementedCountryAdminServiceServer struct{}
func (UnimplementedCountryAdminServiceServer) ListCountries(context.Context, *ListCountriesRequest) (*ListCountriesResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListCountries not implemented")
return nil, status.Error(codes.Unimplemented, "method ListCountries not implemented")
}
func (UnimplementedCountryAdminServiceServer) UpdateCountry(context.Context, *UpdateCountryRequest) (*CountryResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method UpdateCountry not implemented")
return nil, status.Error(codes.Unimplemented, "method UpdateCountry not implemented")
}
func (UnimplementedCountryAdminServiceServer) mustEmbedUnimplementedCountryAdminServiceServer() {}
func (UnimplementedCountryAdminServiceServer) testEmbeddedByValue() {}
@ -1828,7 +1980,7 @@ type UnsafeCountryAdminServiceServer interface {
}
func RegisterCountryAdminServiceServer(s grpc.ServiceRegistrar, srv CountryAdminServiceServer) {
// If the following call pancis, it indicates UnimplementedCountryAdminServiceServer was
// If the following call panics, it indicates UnimplementedCountryAdminServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
@ -1956,10 +2108,10 @@ type CountryQueryServiceServer interface {
type UnimplementedCountryQueryServiceServer struct{}
func (UnimplementedCountryQueryServiceServer) ListRegistrationCountries(context.Context, *ListRegistrationCountriesRequest) (*ListRegistrationCountriesResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListRegistrationCountries not implemented")
return nil, status.Error(codes.Unimplemented, "method ListRegistrationCountries not implemented")
}
func (UnimplementedCountryQueryServiceServer) ListLoginRiskBlockedCountries(context.Context, *ListLoginRiskBlockedCountriesRequest) (*ListLoginRiskBlockedCountriesResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListLoginRiskBlockedCountries not implemented")
return nil, status.Error(codes.Unimplemented, "method ListLoginRiskBlockedCountries not implemented")
}
func (UnimplementedCountryQueryServiceServer) mustEmbedUnimplementedCountryQueryServiceServer() {}
func (UnimplementedCountryQueryServiceServer) testEmbeddedByValue() {}
@ -1972,7 +2124,7 @@ type UnsafeCountryQueryServiceServer interface {
}
func RegisterCountryQueryServiceServer(s grpc.ServiceRegistrar, srv CountryQueryServiceServer) {
// If the following call pancis, it indicates UnimplementedCountryQueryServiceServer was
// If the following call panics, it indicates UnimplementedCountryQueryServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
@ -2128,16 +2280,16 @@ type RegionAdminServiceServer interface {
type UnimplementedRegionAdminServiceServer struct{}
func (UnimplementedRegionAdminServiceServer) ListRegions(context.Context, *ListRegionsRequest) (*ListRegionsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListRegions not implemented")
return nil, status.Error(codes.Unimplemented, "method ListRegions not implemented")
}
func (UnimplementedRegionAdminServiceServer) GetRegion(context.Context, *GetRegionRequest) (*RegionResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetRegion not implemented")
return nil, status.Error(codes.Unimplemented, "method GetRegion not implemented")
}
func (UnimplementedRegionAdminServiceServer) UpdateRegion(context.Context, *UpdateRegionRequest) (*RegionResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method UpdateRegion not implemented")
return nil, status.Error(codes.Unimplemented, "method UpdateRegion not implemented")
}
func (UnimplementedRegionAdminServiceServer) ReplaceRegionCountries(context.Context, *ReplaceRegionCountriesRequest) (*RegionResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ReplaceRegionCountries not implemented")
return nil, status.Error(codes.Unimplemented, "method ReplaceRegionCountries not implemented")
}
func (UnimplementedRegionAdminServiceServer) mustEmbedUnimplementedRegionAdminServiceServer() {}
func (UnimplementedRegionAdminServiceServer) testEmbeddedByValue() {}
@ -2150,7 +2302,7 @@ type UnsafeRegionAdminServiceServer interface {
}
func RegisterRegionAdminServiceServer(s grpc.ServiceRegistrar, srv RegionAdminServiceServer) {
// If the following call pancis, it indicates UnimplementedRegionAdminServiceServer was
// If the following call panics, it indicates UnimplementedRegionAdminServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
@ -2361,19 +2513,19 @@ type UserIdentityServiceServer interface {
type UnimplementedUserIdentityServiceServer struct{}
func (UnimplementedUserIdentityServiceServer) GetUserIdentity(context.Context, *GetUserIdentityRequest) (*GetUserIdentityResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetUserIdentity not implemented")
return nil, status.Error(codes.Unimplemented, "method GetUserIdentity not implemented")
}
func (UnimplementedUserIdentityServiceServer) ResolveDisplayUserID(context.Context, *ResolveDisplayUserIDRequest) (*ResolveDisplayUserIDResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ResolveDisplayUserID not implemented")
return nil, status.Error(codes.Unimplemented, "method ResolveDisplayUserID not implemented")
}
func (UnimplementedUserIdentityServiceServer) ChangeDisplayUserID(context.Context, *ChangeDisplayUserIDRequest) (*ChangeDisplayUserIDResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ChangeDisplayUserID not implemented")
return nil, status.Error(codes.Unimplemented, "method ChangeDisplayUserID not implemented")
}
func (UnimplementedUserIdentityServiceServer) ApplyPrettyDisplayUserID(context.Context, *ApplyPrettyDisplayUserIDRequest) (*ApplyPrettyDisplayUserIDResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ApplyPrettyDisplayUserID not implemented")
return nil, status.Error(codes.Unimplemented, "method ApplyPrettyDisplayUserID not implemented")
}
func (UnimplementedUserIdentityServiceServer) ExpirePrettyDisplayUserID(context.Context, *ExpirePrettyDisplayUserIDRequest) (*ExpirePrettyDisplayUserIDResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ExpirePrettyDisplayUserID not implemented")
return nil, status.Error(codes.Unimplemented, "method ExpirePrettyDisplayUserID not implemented")
}
func (UnimplementedUserIdentityServiceServer) mustEmbedUnimplementedUserIdentityServiceServer() {}
func (UnimplementedUserIdentityServiceServer) testEmbeddedByValue() {}
@ -2386,7 +2538,7 @@ type UnsafeUserIdentityServiceServer interface {
}
func RegisterUserIdentityServiceServer(s grpc.ServiceRegistrar, srv UserIdentityServiceServer) {
// If the following call pancis, it indicates UnimplementedUserIdentityServiceServer was
// If the following call panics, it indicates UnimplementedUserIdentityServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.5.1
// - protoc-gen-go-grpc v1.6.2
// - protoc v7.35.0
// source: proto/wallet/v1/wallet.proto
@ -93,13 +93,13 @@ type WalletCronServiceServer interface {
type UnimplementedWalletCronServiceServer struct{}
func (UnimplementedWalletCronServiceServer) ProcessHostSalaryDailySettlementBatch(context.Context, *CronBatchRequest) (*CronBatchResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ProcessHostSalaryDailySettlementBatch not implemented")
return nil, status.Error(codes.Unimplemented, "method ProcessHostSalaryDailySettlementBatch not implemented")
}
func (UnimplementedWalletCronServiceServer) ProcessHostSalaryHalfMonthSettlementBatch(context.Context, *CronBatchRequest) (*CronBatchResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ProcessHostSalaryHalfMonthSettlementBatch not implemented")
return nil, status.Error(codes.Unimplemented, "method ProcessHostSalaryHalfMonthSettlementBatch not implemented")
}
func (UnimplementedWalletCronServiceServer) ProcessHostSalaryMonthEndBatch(context.Context, *CronBatchRequest) (*CronBatchResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ProcessHostSalaryMonthEndBatch not implemented")
return nil, status.Error(codes.Unimplemented, "method ProcessHostSalaryMonthEndBatch not implemented")
}
func (UnimplementedWalletCronServiceServer) mustEmbedUnimplementedWalletCronServiceServer() {}
func (UnimplementedWalletCronServiceServer) testEmbeddedByValue() {}
@ -112,7 +112,7 @@ type UnsafeWalletCronServiceServer interface {
}
func RegisterWalletCronServiceServer(s grpc.ServiceRegistrar, srv WalletCronServiceServer) {
// If the following call pancis, it indicates UnimplementedWalletCronServiceServer was
// If the following call panics, it indicates UnimplementedWalletCronServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
@ -1120,208 +1120,208 @@ type WalletServiceServer interface {
type UnimplementedWalletServiceServer struct{}
func (UnimplementedWalletServiceServer) DebitGift(context.Context, *DebitGiftRequest) (*DebitGiftResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method DebitGift not implemented")
return nil, status.Error(codes.Unimplemented, "method DebitGift not implemented")
}
func (UnimplementedWalletServiceServer) BatchDebitGift(context.Context, *BatchDebitGiftRequest) (*BatchDebitGiftResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method BatchDebitGift not implemented")
return nil, status.Error(codes.Unimplemented, "method BatchDebitGift not implemented")
}
func (UnimplementedWalletServiceServer) GetBalances(context.Context, *GetBalancesRequest) (*GetBalancesResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetBalances not implemented")
return nil, status.Error(codes.Unimplemented, "method GetBalances not implemented")
}
func (UnimplementedWalletServiceServer) GetActiveHostSalaryPolicy(context.Context, *GetActiveHostSalaryPolicyRequest) (*GetActiveHostSalaryPolicyResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetActiveHostSalaryPolicy not implemented")
return nil, status.Error(codes.Unimplemented, "method GetActiveHostSalaryPolicy not implemented")
}
func (UnimplementedWalletServiceServer) GetHostSalaryProgress(context.Context, *GetHostSalaryProgressRequest) (*GetHostSalaryProgressResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetHostSalaryProgress not implemented")
return nil, status.Error(codes.Unimplemented, "method GetHostSalaryProgress not implemented")
}
func (UnimplementedWalletServiceServer) AdminCreditAsset(context.Context, *AdminCreditAssetRequest) (*AdminCreditAssetResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method AdminCreditAsset not implemented")
return nil, status.Error(codes.Unimplemented, "method AdminCreditAsset not implemented")
}
func (UnimplementedWalletServiceServer) AdminCreditCoinSellerStock(context.Context, *AdminCreditCoinSellerStockRequest) (*AdminCreditCoinSellerStockResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method AdminCreditCoinSellerStock not implemented")
return nil, status.Error(codes.Unimplemented, "method AdminCreditCoinSellerStock not implemented")
}
func (UnimplementedWalletServiceServer) TransferCoinFromSeller(context.Context, *TransferCoinFromSellerRequest) (*TransferCoinFromSellerResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method TransferCoinFromSeller not implemented")
return nil, status.Error(codes.Unimplemented, "method TransferCoinFromSeller not implemented")
}
func (UnimplementedWalletServiceServer) ListCoinSellerSalaryExchangeRateTiers(context.Context, *ListCoinSellerSalaryExchangeRateTiersRequest) (*ListCoinSellerSalaryExchangeRateTiersResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListCoinSellerSalaryExchangeRateTiers not implemented")
return nil, status.Error(codes.Unimplemented, "method ListCoinSellerSalaryExchangeRateTiers not implemented")
}
func (UnimplementedWalletServiceServer) ExchangeSalaryToCoin(context.Context, *ExchangeSalaryToCoinRequest) (*ExchangeSalaryToCoinResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ExchangeSalaryToCoin not implemented")
return nil, status.Error(codes.Unimplemented, "method ExchangeSalaryToCoin not implemented")
}
func (UnimplementedWalletServiceServer) TransferSalaryToCoinSeller(context.Context, *TransferSalaryToCoinSellerRequest) (*TransferSalaryToCoinSellerResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method TransferSalaryToCoinSeller not implemented")
return nil, status.Error(codes.Unimplemented, "method TransferSalaryToCoinSeller not implemented")
}
func (UnimplementedWalletServiceServer) ListResources(context.Context, *ListResourcesRequest) (*ListResourcesResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListResources not implemented")
return nil, status.Error(codes.Unimplemented, "method ListResources not implemented")
}
func (UnimplementedWalletServiceServer) GetResource(context.Context, *GetResourceRequest) (*GetResourceResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetResource not implemented")
return nil, status.Error(codes.Unimplemented, "method GetResource not implemented")
}
func (UnimplementedWalletServiceServer) CreateResource(context.Context, *CreateResourceRequest) (*ResourceResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreateResource not implemented")
return nil, status.Error(codes.Unimplemented, "method CreateResource not implemented")
}
func (UnimplementedWalletServiceServer) UpdateResource(context.Context, *UpdateResourceRequest) (*ResourceResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method UpdateResource not implemented")
return nil, status.Error(codes.Unimplemented, "method UpdateResource not implemented")
}
func (UnimplementedWalletServiceServer) SetResourceStatus(context.Context, *SetResourceStatusRequest) (*ResourceResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method SetResourceStatus not implemented")
return nil, status.Error(codes.Unimplemented, "method SetResourceStatus not implemented")
}
func (UnimplementedWalletServiceServer) ListResourceGroups(context.Context, *ListResourceGroupsRequest) (*ListResourceGroupsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListResourceGroups not implemented")
return nil, status.Error(codes.Unimplemented, "method ListResourceGroups not implemented")
}
func (UnimplementedWalletServiceServer) GetResourceGroup(context.Context, *GetResourceGroupRequest) (*GetResourceGroupResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetResourceGroup not implemented")
return nil, status.Error(codes.Unimplemented, "method GetResourceGroup not implemented")
}
func (UnimplementedWalletServiceServer) CreateResourceGroup(context.Context, *CreateResourceGroupRequest) (*ResourceGroupResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreateResourceGroup not implemented")
return nil, status.Error(codes.Unimplemented, "method CreateResourceGroup not implemented")
}
func (UnimplementedWalletServiceServer) UpdateResourceGroup(context.Context, *UpdateResourceGroupRequest) (*ResourceGroupResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method UpdateResourceGroup not implemented")
return nil, status.Error(codes.Unimplemented, "method UpdateResourceGroup not implemented")
}
func (UnimplementedWalletServiceServer) SetResourceGroupStatus(context.Context, *SetResourceGroupStatusRequest) (*ResourceGroupResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method SetResourceGroupStatus not implemented")
return nil, status.Error(codes.Unimplemented, "method SetResourceGroupStatus not implemented")
}
func (UnimplementedWalletServiceServer) ListGiftConfigs(context.Context, *ListGiftConfigsRequest) (*ListGiftConfigsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListGiftConfigs not implemented")
return nil, status.Error(codes.Unimplemented, "method ListGiftConfigs not implemented")
}
func (UnimplementedWalletServiceServer) ListGiftTypeConfigs(context.Context, *ListGiftTypeConfigsRequest) (*ListGiftTypeConfigsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListGiftTypeConfigs not implemented")
return nil, status.Error(codes.Unimplemented, "method ListGiftTypeConfigs not implemented")
}
func (UnimplementedWalletServiceServer) CreateGiftConfig(context.Context, *CreateGiftConfigRequest) (*GiftConfigResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreateGiftConfig not implemented")
return nil, status.Error(codes.Unimplemented, "method CreateGiftConfig not implemented")
}
func (UnimplementedWalletServiceServer) UpdateGiftConfig(context.Context, *UpdateGiftConfigRequest) (*GiftConfigResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method UpdateGiftConfig not implemented")
return nil, status.Error(codes.Unimplemented, "method UpdateGiftConfig not implemented")
}
func (UnimplementedWalletServiceServer) SetGiftConfigStatus(context.Context, *SetGiftConfigStatusRequest) (*GiftConfigResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method SetGiftConfigStatus not implemented")
return nil, status.Error(codes.Unimplemented, "method SetGiftConfigStatus not implemented")
}
func (UnimplementedWalletServiceServer) UpsertGiftTypeConfig(context.Context, *UpsertGiftTypeConfigRequest) (*GiftTypeConfigResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method UpsertGiftTypeConfig not implemented")
return nil, status.Error(codes.Unimplemented, "method UpsertGiftTypeConfig not implemented")
}
func (UnimplementedWalletServiceServer) GrantResource(context.Context, *GrantResourceRequest) (*ResourceGrantResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GrantResource not implemented")
return nil, status.Error(codes.Unimplemented, "method GrantResource not implemented")
}
func (UnimplementedWalletServiceServer) GrantResourceGroup(context.Context, *GrantResourceGroupRequest) (*ResourceGrantResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GrantResourceGroup not implemented")
return nil, status.Error(codes.Unimplemented, "method GrantResourceGroup not implemented")
}
func (UnimplementedWalletServiceServer) ListUserResources(context.Context, *ListUserResourcesRequest) (*ListUserResourcesResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListUserResources not implemented")
return nil, status.Error(codes.Unimplemented, "method ListUserResources not implemented")
}
func (UnimplementedWalletServiceServer) EquipUserResource(context.Context, *EquipUserResourceRequest) (*EquipUserResourceResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method EquipUserResource not implemented")
return nil, status.Error(codes.Unimplemented, "method EquipUserResource not implemented")
}
func (UnimplementedWalletServiceServer) UnequipUserResource(context.Context, *UnequipUserResourceRequest) (*UnequipUserResourceResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method UnequipUserResource not implemented")
return nil, status.Error(codes.Unimplemented, "method UnequipUserResource not implemented")
}
func (UnimplementedWalletServiceServer) BatchGetUserEquippedResources(context.Context, *BatchGetUserEquippedResourcesRequest) (*BatchGetUserEquippedResourcesResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method BatchGetUserEquippedResources not implemented")
return nil, status.Error(codes.Unimplemented, "method BatchGetUserEquippedResources not implemented")
}
func (UnimplementedWalletServiceServer) ListResourceGrants(context.Context, *ListResourceGrantsRequest) (*ListResourceGrantsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListResourceGrants not implemented")
return nil, status.Error(codes.Unimplemented, "method ListResourceGrants not implemented")
}
func (UnimplementedWalletServiceServer) ListResourceShopItems(context.Context, *ListResourceShopItemsRequest) (*ListResourceShopItemsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListResourceShopItems not implemented")
return nil, status.Error(codes.Unimplemented, "method ListResourceShopItems not implemented")
}
func (UnimplementedWalletServiceServer) UpsertResourceShopItems(context.Context, *UpsertResourceShopItemsRequest) (*UpsertResourceShopItemsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method UpsertResourceShopItems not implemented")
return nil, status.Error(codes.Unimplemented, "method UpsertResourceShopItems not implemented")
}
func (UnimplementedWalletServiceServer) SetResourceShopItemStatus(context.Context, *SetResourceShopItemStatusRequest) (*ResourceShopItemResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method SetResourceShopItemStatus not implemented")
return nil, status.Error(codes.Unimplemented, "method SetResourceShopItemStatus not implemented")
}
func (UnimplementedWalletServiceServer) PurchaseResourceShopItem(context.Context, *PurchaseResourceShopItemRequest) (*PurchaseResourceShopItemResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method PurchaseResourceShopItem not implemented")
return nil, status.Error(codes.Unimplemented, "method PurchaseResourceShopItem not implemented")
}
func (UnimplementedWalletServiceServer) ListRechargeBills(context.Context, *ListRechargeBillsRequest) (*ListRechargeBillsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListRechargeBills not implemented")
return nil, status.Error(codes.Unimplemented, "method ListRechargeBills not implemented")
}
func (UnimplementedWalletServiceServer) GetWalletOverview(context.Context, *GetWalletOverviewRequest) (*GetWalletOverviewResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetWalletOverview not implemented")
return nil, status.Error(codes.Unimplemented, "method GetWalletOverview not implemented")
}
func (UnimplementedWalletServiceServer) GetWalletValueSummary(context.Context, *GetWalletValueSummaryRequest) (*GetWalletValueSummaryResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetWalletValueSummary not implemented")
return nil, status.Error(codes.Unimplemented, "method GetWalletValueSummary not implemented")
}
func (UnimplementedWalletServiceServer) GetUserGiftWall(context.Context, *GetUserGiftWallRequest) (*GetUserGiftWallResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetUserGiftWall not implemented")
return nil, status.Error(codes.Unimplemented, "method GetUserGiftWall not implemented")
}
func (UnimplementedWalletServiceServer) ListRechargeProducts(context.Context, *ListRechargeProductsRequest) (*ListRechargeProductsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListRechargeProducts not implemented")
return nil, status.Error(codes.Unimplemented, "method ListRechargeProducts not implemented")
}
func (UnimplementedWalletServiceServer) ConfirmGooglePayment(context.Context, *ConfirmGooglePaymentRequest) (*ConfirmGooglePaymentResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ConfirmGooglePayment not implemented")
return nil, status.Error(codes.Unimplemented, "method ConfirmGooglePayment not implemented")
}
func (UnimplementedWalletServiceServer) ListAdminRechargeProducts(context.Context, *ListAdminRechargeProductsRequest) (*ListAdminRechargeProductsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListAdminRechargeProducts not implemented")
return nil, status.Error(codes.Unimplemented, "method ListAdminRechargeProducts not implemented")
}
func (UnimplementedWalletServiceServer) CreateRechargeProduct(context.Context, *CreateRechargeProductRequest) (*RechargeProductResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreateRechargeProduct not implemented")
return nil, status.Error(codes.Unimplemented, "method CreateRechargeProduct not implemented")
}
func (UnimplementedWalletServiceServer) UpdateRechargeProduct(context.Context, *UpdateRechargeProductRequest) (*RechargeProductResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method UpdateRechargeProduct not implemented")
return nil, status.Error(codes.Unimplemented, "method UpdateRechargeProduct not implemented")
}
func (UnimplementedWalletServiceServer) DeleteRechargeProduct(context.Context, *DeleteRechargeProductRequest) (*DeleteRechargeProductResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method DeleteRechargeProduct not implemented")
return nil, status.Error(codes.Unimplemented, "method DeleteRechargeProduct not implemented")
}
func (UnimplementedWalletServiceServer) GetDiamondExchangeConfig(context.Context, *GetDiamondExchangeConfigRequest) (*GetDiamondExchangeConfigResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetDiamondExchangeConfig not implemented")
return nil, status.Error(codes.Unimplemented, "method GetDiamondExchangeConfig not implemented")
}
func (UnimplementedWalletServiceServer) ListWalletTransactions(context.Context, *ListWalletTransactionsRequest) (*ListWalletTransactionsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListWalletTransactions not implemented")
return nil, status.Error(codes.Unimplemented, "method ListWalletTransactions not implemented")
}
func (UnimplementedWalletServiceServer) ListVipPackages(context.Context, *ListVipPackagesRequest) (*ListVipPackagesResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListVipPackages not implemented")
return nil, status.Error(codes.Unimplemented, "method ListVipPackages not implemented")
}
func (UnimplementedWalletServiceServer) GetMyVip(context.Context, *GetMyVipRequest) (*GetMyVipResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetMyVip not implemented")
return nil, status.Error(codes.Unimplemented, "method GetMyVip not implemented")
}
func (UnimplementedWalletServiceServer) PurchaseVip(context.Context, *PurchaseVipRequest) (*PurchaseVipResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method PurchaseVip not implemented")
return nil, status.Error(codes.Unimplemented, "method PurchaseVip not implemented")
}
func (UnimplementedWalletServiceServer) GrantVip(context.Context, *GrantVipRequest) (*GrantVipResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GrantVip not implemented")
return nil, status.Error(codes.Unimplemented, "method GrantVip not implemented")
}
func (UnimplementedWalletServiceServer) ListAdminVipLevels(context.Context, *ListAdminVipLevelsRequest) (*ListAdminVipLevelsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListAdminVipLevels not implemented")
return nil, status.Error(codes.Unimplemented, "method ListAdminVipLevels not implemented")
}
func (UnimplementedWalletServiceServer) UpdateAdminVipLevels(context.Context, *UpdateAdminVipLevelsRequest) (*UpdateAdminVipLevelsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method UpdateAdminVipLevels not implemented")
return nil, status.Error(codes.Unimplemented, "method UpdateAdminVipLevels not implemented")
}
func (UnimplementedWalletServiceServer) CreditTaskReward(context.Context, *CreditTaskRewardRequest) (*CreditTaskRewardResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreditTaskReward not implemented")
return nil, status.Error(codes.Unimplemented, "method CreditTaskReward not implemented")
}
func (UnimplementedWalletServiceServer) CreditLuckyGiftReward(context.Context, *CreditLuckyGiftRewardRequest) (*CreditLuckyGiftRewardResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreditLuckyGiftReward not implemented")
return nil, status.Error(codes.Unimplemented, "method CreditLuckyGiftReward not implemented")
}
func (UnimplementedWalletServiceServer) CreditRoomTurnoverReward(context.Context, *CreditRoomTurnoverRewardRequest) (*CreditRoomTurnoverRewardResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreditRoomTurnoverReward not implemented")
return nil, status.Error(codes.Unimplemented, "method CreditRoomTurnoverReward not implemented")
}
func (UnimplementedWalletServiceServer) ApplyGameCoinChange(context.Context, *ApplyGameCoinChangeRequest) (*ApplyGameCoinChangeResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ApplyGameCoinChange not implemented")
return nil, status.Error(codes.Unimplemented, "method ApplyGameCoinChange not implemented")
}
func (UnimplementedWalletServiceServer) GetRedPacketConfig(context.Context, *GetRedPacketConfigRequest) (*GetRedPacketConfigResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetRedPacketConfig not implemented")
return nil, status.Error(codes.Unimplemented, "method GetRedPacketConfig not implemented")
}
func (UnimplementedWalletServiceServer) UpdateRedPacketConfig(context.Context, *UpdateRedPacketConfigRequest) (*UpdateRedPacketConfigResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method UpdateRedPacketConfig not implemented")
return nil, status.Error(codes.Unimplemented, "method UpdateRedPacketConfig not implemented")
}
func (UnimplementedWalletServiceServer) CreateRedPacket(context.Context, *CreateRedPacketRequest) (*CreateRedPacketResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreateRedPacket not implemented")
return nil, status.Error(codes.Unimplemented, "method CreateRedPacket not implemented")
}
func (UnimplementedWalletServiceServer) ClaimRedPacket(context.Context, *ClaimRedPacketRequest) (*ClaimRedPacketResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ClaimRedPacket not implemented")
return nil, status.Error(codes.Unimplemented, "method ClaimRedPacket not implemented")
}
func (UnimplementedWalletServiceServer) ListRedPackets(context.Context, *ListRedPacketsRequest) (*ListRedPacketsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListRedPackets not implemented")
return nil, status.Error(codes.Unimplemented, "method ListRedPackets not implemented")
}
func (UnimplementedWalletServiceServer) GetRedPacket(context.Context, *GetRedPacketRequest) (*GetRedPacketResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetRedPacket not implemented")
return nil, status.Error(codes.Unimplemented, "method GetRedPacket not implemented")
}
func (UnimplementedWalletServiceServer) ExpireRedPackets(context.Context, *ExpireRedPacketsRequest) (*ExpireRedPacketsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ExpireRedPackets not implemented")
return nil, status.Error(codes.Unimplemented, "method ExpireRedPackets not implemented")
}
func (UnimplementedWalletServiceServer) RetryRedPacketRefund(context.Context, *RetryRedPacketRefundRequest) (*RetryRedPacketRefundResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method RetryRedPacketRefund not implemented")
return nil, status.Error(codes.Unimplemented, "method RetryRedPacketRefund not implemented")
}
func (UnimplementedWalletServiceServer) mustEmbedUnimplementedWalletServiceServer() {}
func (UnimplementedWalletServiceServer) testEmbeddedByValue() {}
@ -1334,7 +1334,7 @@ type UnsafeWalletServiceServer interface {
}
func RegisterWalletServiceServer(s grpc.ServiceRegistrar, srv WalletServiceServer) {
// If the following call pancis, it indicates UnimplementedWalletServiceServer was
// If the following call panics, it indicates UnimplementedWalletServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.

View File

@ -12,11 +12,12 @@
规则:
- 每个用户同时只能有一个 active 关系,不按类型拆数量,也不需要后台配置数量。
- CP 关系每个用户最多 1 个,不提供可调数量。
- 兄弟和姐妹的每人 active 数量由后台 CP 配置控制。
- A 和 B 同时只能存在一种 active 关系。
- A 可以给 B 发送 `cp``brother``sister` 多种 CP 礼物,形成多条 pending 申请。
- B 接受其中一个申请后,同一对用户其他 pending 申请会被置为 `blocked`,不能再接受。
- 申请默认 24 小时过期,当前不提供后台配置入口
- 申请默认 24 小时过期。
- 不提供佩戴 CP 功能;资料卡如果要展示关系,直接展示用户当前 active 关系。
## 2. 服务归属
@ -118,6 +119,7 @@ sequenceDiagram
U-->>N: UserCPApplicationAccepted MQ
U-->>N: UserCPRelationshipCreated MQ
N->>IM: B -> A C2C cp_application_accepted
N->>IM: A -> B C2C cp_application_accepted
N->>IM: room cp_application_accepted, recipient_user_id=A
N->>IM: region cp_relationship_created
```
@ -128,7 +130,9 @@ sequenceDiagram
- pending 申请过期后不能接受gateway envelope 返回 `CONFLICT`;内部 gRPC 语义是 FailedPrecondition。
- 接受时同时校验 A 和 B 是否已经有任意 active 关系;任一方已有关系就返回 `CONFLICT`
- 同意成功后写 active 关系,关闭同一对用户其他 pending 申请。
- 同意或拒绝都向 A 发送 C2C IM 和房间 IM。
- 同意会发两条 C2C`B -> A` 表示“我同意了你”,`A -> B` 表示“对方已同意你的申请”。
- 同意和拒绝都会向 A 发送房间 IM`recipient_user_id=A` 限定展示对象。
- 拒绝仍只向 A 发送一条 C2C 和一条房间 IM。
- 同意成功后额外发送区域 IM区域取申请来源房间的 `visible_region_id`
## 7. 亲密值和升级
@ -140,9 +144,11 @@ sequenceDiagram
- 普通礼物按 100% 热度值增加亲密值。
- 幸运礼物、超级幸运礼物直接使用钱包回执里的最终 `heat_value`,不重复计算比例。
- 多目标送礼时按每个目标的账务回执分别增加对应关系亲密值。
- 等级范围 1 到 5当前使用 user-service 默认等级规则,不提供后台配置入口
- 等级范围 1 到 5等级亲密值阈值和奖励资源组由后台 CP 配置维护
- 每个等级可绑定奖励资源组,升级时调用 `wallet-service` 给双方发放资源。
- 奖励发放幂等键为 `cp_level_reward:{relationship_id}:{level}:{user_id}`
- App 关系查询返回当前等级阈值、下一等级阈值、距离下一级和进度百分比。
- 资料卡弹窗只能使用 `/api/v1/cp/relationships` 返回的阈值字段展示进度;客户端不能读取后台配置或硬编码等级表。
## 8. App HTTP接口
@ -155,6 +161,27 @@ sequenceDiagram
| `POST` | `/api/v1/cp/applications/{application_id}/reject` | 拒绝申请 |
| `GET` | `/api/v1/cp/relationships` | 查询我的 active 关系 |
`/api/v1/cp/relationships` 的 relationship item 包含:
| 字段 | 说明 |
| --- | --- |
| `relationship_id` | 关系 ID |
| `relation_type` | `cp``brother``sister` |
| `status` | 当前为 `active` |
| `me` | 当前用户快照 |
| `partner` | 对方用户快照 |
| `intimacy_value` | 当前亲密值 |
| `level` | 当前等级 |
| `current_level_threshold` | 当前等级起始亲密值 |
| `next_level_threshold` | 下一等级所需亲密值;满级时为 0 |
| `needed_for_next_level` | 距离下一级还需要多少亲密值;满级时为 0 |
| `level_progress_percent` | 当前等级内进度百分比0 到 100 |
| `max_level` | 是否已经没有下一等级 |
| `formed_at_ms` | 关系形成时间 |
| `updated_at_ms` | 最近更新时间 |
这些字段由 user-service 从 `user_cp_level_rules` 计算gateway 只做透传。客户端资料卡不能根据本地表推算这些值。
申请列表参数:
| 参数 | 类型 | 必填 | 说明 |
@ -184,8 +211,8 @@ sequenceDiagram
| 表 | 说明 |
| --- | --- |
| `user_cp_relation_configs` | 历史兼容表;当前固定 `max_count_per_user=1`,不提供后台配置 |
| `user_cp_level_rules` | 默认等级阈值和奖励资源组 |
| `user_cp_relation_configs` | CP/兄弟/姐妹数量与申请过期配置CP 数量服务端固定为 1兄弟/姐妹读取后台配置 |
| `user_cp_level_rules` | 等级阈值和奖励资源组 |
| `user_cp_gift_event_consumption` | `RoomGiftSent` 消费幂等 |
| `user_cp_applications` | CP 关系申请 |
| `user_cp_relationships` | 双人关系事实 |
@ -195,7 +222,8 @@ sequenceDiagram
关键唯一约束:
- active 关系按用户对唯一:`(app_code, user_a_id, user_b_id, status='active')`
- active 关系按单用户唯一:任一用户已有 active 关系就不能再接受新关系。
- CP active 关系按单用户唯一:任一用户已有 CP 关系就不能再接受新的 CP 关系。
- 兄弟/姐妹 active 关系按后台配置做单用户数量上限校验。
- pending 申请按用户对和类型唯一:`(app_code, requester_user_id, target_user_id, relation_type, status='pending')`
## 10. 相关IM
@ -204,13 +232,14 @@ sequenceDiagram
| --- | --- | --- |
| `cp_application_created` | C2C | A 送 CP 礼物触发申请后A 发给 B |
| `cp_application_created` | 房间群 | A 送 CP 礼物触发申请后发到来源房间payload 带 `recipient_user_id=B` |
| `cp_application_accepted` | C2C | B 同意后B 发给 A |
| `cp_application_accepted` | C2C | B 同意后B 发给 Apayload 带 `c2c_notice_role=accepted_to_requester``message_text=我同意了你` |
| `cp_application_accepted` | C2C | B 同意后A 发给 Bpayload 带 `c2c_notice_role=accepted_to_acceptor``message_text=对方已同意你的申请` |
| `cp_application_accepted` | 房间群 | B 同意后发到来源房间payload 带 `recipient_user_id=A` |
| `cp_application_rejected` | C2C | B 拒绝后B 发给 A |
| `cp_application_rejected` | 房间群 | B 拒绝后发到来源房间payload 带 `recipient_user_id=A` |
| `cp_relationship_created` | 区域群 | 关系建立后发送区域播报 |
C2C 使用腾讯云 IM `From_Account` 表达真实操作人:申请为 A处理结果为 B。区域 IM 群 ID 使用 `hy_<app_code>_bc_r_<region_id>`,本地测试可通过 `group_id_prefix` 加前缀。
C2C 使用腾讯云 IM `From_Account` 表达会话方向:申请为 A拒绝为 B同意会发 `B -> A``A -> B` 两条。区域 IM 群 ID 使用 `hy_<app_code>_bc_r_<region_id>`,本地测试可通过 `group_id_prefix` 加前缀。
## 11. 错误码

View File

@ -1,6 +1,6 @@
# CP关系Flutter对接
本文只写 App 需要对接的地址、参数、返回值和相关 IM。CP 关系包含 `cp``brother``sister` 三种类型;每个用户同时只能有一个 active 关系,不需要佩戴接口,也没有后台数量配置
本文只写 App 需要对接的地址、参数、返回值和相关 IM。CP 关系包含 `cp``brother``sister` 三种类型;CP 关系每个用户最多 1 个,兄弟和姐妹数量由后台 CP 配置控制;不需要佩戴接口
## 1. 礼物面板CP Tab
@ -274,6 +274,11 @@ X-App-Code: lalu
},
"intimacy_value": 1888,
"level": 2,
"current_level_threshold": 1000,
"next_level_threshold": 3000,
"needed_for_next_level": 1112,
"level_progress_percent": 44,
"max_level": false,
"formed_at_ms": 1779259100000,
"updated_at_ms": 1779259200000
}
@ -284,8 +289,12 @@ X-App-Code: lalu
说明:
- 每个用户同时只能有一个 active 关系。
- CP 关系每个用户最多 1 个;兄弟和姐妹可拥有数量以后台配置为准。
- A/B 两个用户之间同一时间只能存在一种 active 关系,不能同时是 CP、兄弟或姐妹。
- 不需要佩戴接口,资料卡直接展示当前 active 关系。
- 资料卡弹窗只使用后端返回的 `intimacy_value``level``current_level_threshold``next_level_threshold``needed_for_next_level``level_progress_percent``max_level`
- `max_level=true``next_level_threshold=0` 时表示没有下一等级,隐藏“距离下一级”和进度条。
- Flutter 不要用本地常量、礼物价格或后台配置接口自行推算进度条和“距离下一级”。
## 7. 相关IM
@ -347,7 +356,7 @@ Data
### 7.2 申请被同意
C2C 和房间 IM 都会收到
C2C 会收到双方互发的两条消息,房间 IM 仍然只给申请发起人 A 展示
| 字段 | 值 |
| --- | --- |
@ -362,6 +371,13 @@ Data 字段和申请消息一致,差异是:
| `decided_at_ms` | 处理时间 |
| `recipient_user_id` | 房间 IM 中为 A 的 user_id |
C2C 差异:
| 方向 | `c2c_notice_role` | `message_text` |
| --- | --- | --- |
| B -> A | `accepted_to_requester` | `我同意了你` |
| A -> B | `accepted_to_acceptor` | `对方已同意你的申请` |
处理:
- A 收到后刷新 `/api/v1/cp/relationships`
@ -453,3 +469,4 @@ Data
- 同一个 `application_id` 在房间和 C2C 里可能都出现,按钮状态要共享。
- 申请过期时间以 `expires_at_ms` 为准。
- 亲密值和等级以后端关系接口返回为准,不在 Flutter 里按礼物价格推算。
- 进度条和距离下一级只使用关系接口返回的阈值字段;字段缺失时不展示,不要伪造 `next_level_threshold``needed_for_next_level` 或进度百分比。

View File

@ -1824,7 +1824,7 @@ paths:
- manager-center
summary: 经理中心赠送单个资源
operationId: grantManagerResource
description: 客户端只提交命令 ID、目标用户和资源 ID数量固定为 1有效期固定为 0`grant_source` 固定为 `manager_center`。
description: 客户端提交命令 ID、目标用户、资源 ID 和可选有效期;数量固定为 1有效期只能是 7/30/90 天,未传默认 7 天`grant_source` 固定为 `manager_center`。
security:
- BearerAuth: []
parameters:
@ -3575,10 +3575,16 @@ definitions:
resource_id:
type: string
description: 资源 IDwallet-service 会在事务内复验 `manager_grant_enabled`。
duration_ms:
type: integer
format: int64
enum: [604800000, 2592000000, 7776000000]
default: 604800000
description: 资源有效期,仅允许 7/30/90 天;未传时 gateway 默认 7 天。
reason:
type: string
default: manager_center
description: 不接受 `quantity`、`duration_ms`、`wallet_asset_amount` 等客户端计算字段。
description: 不接受 `quantity`、`wallet_asset_amount` 等客户端计算字段。
ManagerResourceGrantData:
type: object
properties:

View File

@ -195,6 +195,29 @@ type EventResult struct {
RewardJobCount int64
}
// SetUserLevelCommand 描述经理中心直接设置用户等级的事实输入。
// TotalValue 不由调用方提交activity-service 会读取目标等级阈值后在事务内写入。
type SetUserLevelCommand struct {
CommandID string
UserID int64
Track string
Level int32
OperatorUserID int64
Reason string
}
// SetUserLevelResult 返回经理设置等级后的账户事实和补发奖励数量。
type SetUserLevelResult struct {
EventID string
Status string
Track string
PreviousLevel int32
NewLevel int32
TotalValue int64
RewardJobCount int64
ServerTimeMS int64
}
// RewardJob 是等级奖励资源组的异步发放任务。
type RewardJob struct {
AppCode string

View File

@ -32,6 +32,7 @@ type Repository interface {
ListLevelConfig(ctx context.Context, query domain.ConfigQuery, nowMS int64) (domain.Config, error)
ListLevelRewards(ctx context.Context, query domain.RewardQuery) ([]domain.RewardJob, int64, error)
ConsumeLevelEvent(ctx context.Context, event domain.ValueEvent, nowMS int64) (domain.EventResult, error)
SetUserLevel(ctx context.Context, command domain.SetUserLevelCommand, nowMS int64) (domain.SetUserLevelResult, error)
UpsertLevelTrack(ctx context.Context, command domain.TrackCommand, nowMS int64) (domain.Track, bool, error)
UpsertLevelRule(ctx context.Context, command domain.RuleCommand, nowMS int64) (domain.Rule, bool, error)
UpsertLevelTier(ctx context.Context, command domain.TierCommand, nowMS int64) (domain.Tier, bool, error)
@ -220,6 +221,30 @@ func (s *Service) ConsumeLevelEvent(ctx context.Context, event domain.ValueEvent
return s.repository.ConsumeLevelEvent(ctx, event, s.now().UnixMilli())
}
// SetUserLevel 是经理中心的直接等级调整入口。
// 它不接收累计值避免调用方伪造进度service 只校验轨道、等级和操作者,阈值读取与奖励补发在 repository 事务里完成。
func (s *Service) SetUserLevel(ctx context.Context, command domain.SetUserLevelCommand) (domain.SetUserLevelResult, error) {
if err := s.requireRepository(); err != nil {
return domain.SetUserLevelResult{}, err
}
command.CommandID = strings.TrimSpace(command.CommandID)
command.Track = normalizeTrack(command.Track)
command.Reason = strings.TrimSpace(command.Reason)
if command.CommandID == "" || command.UserID <= 0 || command.OperatorUserID <= 0 {
return domain.SetUserLevelResult{}, xerr.New(xerr.InvalidArgument, "set user level command is incomplete")
}
if !validTrack(command.Track) {
return domain.SetUserLevelResult{}, xerr.New(xerr.InvalidArgument, "track is invalid")
}
if command.Level < 1 || command.Level > 11 {
return domain.SetUserLevelResult{}, xerr.New(xerr.InvalidArgument, "level must be between 1 and 11")
}
if command.Reason == "" {
command.Reason = "manager_center_level"
}
return s.repository.SetUserLevel(ctx, command, s.now().UnixMilli())
}
// HandleRoomEvent 把 room-service 已提交送礼事实映射为财富和魅力两条等级增量。
func (s *Service) HandleRoomEvent(ctx context.Context, envelope *roomeventsv1.EventEnvelope) (int32, error) {
if envelope == nil {

View File

@ -265,6 +265,112 @@ func (r *Repository) ConsumeLevelEvent(ctx context.Context, event domain.ValueEv
}, nil
}
// SetUserLevel 在一个事务里把用户某条轨道直接设置到目标等级阈值。
// 经理中心需要支持降级,所以这里不能复用只接受正增量的 ConsumeLevelEvent但仍写 growth_level_value_events 保留幂等事件和审计来源。
func (r *Repository) SetUserLevel(ctx context.Context, command domain.SetUserLevelCommand, nowMS int64) (domain.SetUserLevelResult, error) {
if r == nil || r.db == nil {
return domain.SetUserLevelResult{}, xerr.New(xerr.Unavailable, "mysql repository is not configured")
}
eventID := "manager_level:" + command.CommandID
tx, err := r.db.BeginTx(ctx, nil)
if err != nil {
return domain.SetUserLevelResult{}, err
}
defer func() { _ = tx.Rollback() }()
account, err := r.ensureLevelAccountForUpdate(ctx, tx, command.UserID, command.Track, nowMS)
if err != nil {
return domain.SetUserLevelResult{}, err
}
rules, err := r.listActiveLevelRules(ctx, tx, command.Track)
if err != nil {
return domain.SetUserLevelResult{}, err
}
targetValue, ok := requiredValueForLevel(rules, command.Level)
if !ok {
return domain.SetUserLevelResult{}, xerr.New(xerr.NotFound, "level rule not found")
}
tiers, err := r.listActiveLevelTiers(ctx, tx, command.Track)
if err != nil {
return domain.SetUserLevelResult{}, err
}
delta := targetValue - account.TotalValue
inserted, err := r.insertLevelValueEvent(ctx, tx, domain.ValueEvent{
EventID: eventID,
SourceEventID: command.CommandID,
SourceService: "manager_center",
SourceEventType: "ManagerSetUserLevel",
UserID: command.UserID,
Track: command.Track,
MetricType: "manager_set_level",
ValueDelta: delta,
OccurredAtMS: nowMS,
DimensionsJSON: normalizeJSONMap(map[string]any{
"operator_user_id": command.OperatorUserID,
"target_level": command.Level,
"reason": command.Reason,
}),
}, nowMS)
if err != nil {
return domain.SetUserLevelResult{}, err
}
if !inserted {
if err := tx.Commit(); err != nil {
return domain.SetUserLevelResult{}, err
}
return domain.SetUserLevelResult{
EventID: eventID,
Status: domain.EventStatusDuplicate,
Track: command.Track,
NewLevel: account.CurrentLevel,
TotalValue: account.TotalValue,
ServerTimeMS: nowMS,
}, nil
}
previousLevel := account.CurrentLevel
previousTierID := account.CurrentTierID
nextLevel := levelForValue(rules, targetValue)
nextTier := tierForLevel(tiers, nextLevel)
nextTierID := nextTier.TierID
levelUpdatedAtMS := account.LevelUpdatedAtMS
if nextLevel != previousLevel {
levelUpdatedAtMS = nowMS
}
tierUpdatedAtMS := account.TierUpdatedAtMS
if nextTierID != previousTierID {
tierUpdatedAtMS = nowMS
}
if err := r.updateLevelAccount(ctx, tx, command.UserID, command.Track, targetValue, nextLevel, nextTierID, levelUpdatedAtMS, tierUpdatedAtMS, nowMS); err != nil {
return domain.SetUserLevelResult{}, err
}
if err := r.insertLevelHistory(ctx, tx, domain.ValueEvent{EventID: eventID, UserID: command.UserID, Track: command.Track}, previousLevel, nextLevel, previousTierID, nextTierID, targetValue, nowMS); err != nil {
return domain.SetUserLevelResult{}, err
}
// 经理直接设置等级时要补齐从 1 级到目标等级之间缺失的奖励;唯一键保证已发过的等级不会重复入队。
rewardJobCount, err := r.createLevelRewardJobs(ctx, tx, command.UserID, command.Track, 0, nextLevel, 0, nextTier, rules, nowMS)
if err != nil {
return domain.SetUserLevelResult{}, err
}
badgeResourceID, badgeSourceLevel := levelRuleBadgeForLevel(rules, nextLevel)
if err := r.upsertLevelDisplayProfile(ctx, tx, command.UserID, command.Track, nextLevel, nextTier, badgeResourceID, badgeSourceLevel, nowMS); err != nil {
return domain.SetUserLevelResult{}, err
}
if err := tx.Commit(); err != nil {
return domain.SetUserLevelResult{}, err
}
return domain.SetUserLevelResult{
EventID: eventID,
Status: domain.EventStatusConsumed,
Track: command.Track,
PreviousLevel: previousLevel,
NewLevel: nextLevel,
TotalValue: targetValue,
RewardJobCount: rewardJobCount,
ServerTimeMS: nowMS,
}, nil
}
func (r *Repository) UpsertLevelTrack(ctx context.Context, command domain.TrackCommand, nowMS int64) (domain.Track, bool, error) {
if r == nil || r.db == nil {
return domain.Track{}, false, xerr.New(xerr.Unavailable, "mysql repository is not configured")
@ -844,6 +950,15 @@ func levelForValue(rules []domain.Rule, totalValue int64) int32 {
return level
}
func requiredValueForLevel(rules []domain.Rule, level int32) (int64, bool) {
for _, rule := range rules {
if rule.Level == level && rule.Status == domain.StatusActive {
return rule.RequiredValue, true
}
}
return 0, false
}
func tierForLevel(tiers []domain.Tier, level int32) domain.Tier {
if level < 0 {
return domain.Tier{}
@ -856,6 +971,14 @@ func tierForLevel(tiers []domain.Tier, level int32) domain.Tier {
return domain.Tier{}
}
func normalizeJSONMap(value map[string]any) string {
data, err := json.Marshal(value)
if err != nil {
return "{}"
}
return string(data)
}
func tierByID(tiers []domain.Tier, tierID int64) domain.Tier {
if tierID <= 0 {
return domain.Tier{}

View File

@ -120,6 +120,31 @@ func (s *GrowthLevelServer) ConsumeLevelEvent(ctx context.Context, req *activity
}, nil
}
func (s *GrowthLevelServer) SetUserLevel(ctx context.Context, req *activityv1.SetUserLevelRequest) (*activityv1.SetUserLevelResponse, error) {
ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
result, err := s.svc.SetUserLevel(ctx, domain.SetUserLevelCommand{
CommandID: req.GetCommandId(),
UserID: req.GetUserId(),
Track: req.GetTrack(),
Level: req.GetLevel(),
OperatorUserID: req.GetOperatorUserId(),
Reason: req.GetReason(),
})
if err != nil {
return nil, xerr.ToGRPCError(err)
}
return &activityv1.SetUserLevelResponse{
EventId: result.EventID,
Status: result.Status,
Track: result.Track,
PreviousLevel: result.PreviousLevel,
NewLevel: result.NewLevel,
TotalValue: result.TotalValue,
RewardJobCount: result.RewardJobCount,
ServerTimeMs: result.ServerTimeMS,
}, nil
}
func (s *GrowthLevelServer) IssueRegistrationLevelBadges(ctx context.Context, req *activityv1.IssueRegistrationLevelBadgesRequest) (*activityv1.IssueRegistrationLevelBadgesResponse, error) {
ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
grants, err := s.svc.IssueRegistrationLevelBadges(ctx, req.GetUserId(), req.GetCommandId())

View File

@ -116,6 +116,7 @@ func New(cfg config.Config) (*App, error) {
"login_ip_risk": userCron.ProcessLoginIPRiskBatch,
"user_region_rebuild": userCron.ProcessRegionRebuildBatch,
"mic_open_session_compensation": userCron.CompensateMicOpenSessions,
"manager_user_block_expiry": userCron.ExpireManagerUserBlocks,
"message_fanout": activityCron.ProcessMessageFanoutBatch,
"growth_level_reward": activityCron.ProcessLevelRewardBatch,
"achievement_reward": activityCron.ProcessAchievementRewardBatch,

View File

@ -222,6 +222,14 @@ func defaultTasks() map[string]TaskConfig {
PendingPublishMaxAge: "2m",
PublishingSessionMaxAge: "12h",
},
"manager_user_block_expiry": {
Enabled: true,
Interval: "30s",
Timeout: "10s",
LockTTL: "30s",
BatchSize: 100,
AppCodes: []string{defaultAppCode},
},
}
}

View File

@ -38,6 +38,12 @@ func (c *UserCronClient) CompensateMicOpenSessions(ctx context.Context, req sche
return userCronResult(resp), err
}
// ExpireManagerUserBlocks handles timed manager-center bans after their unblock time.
func (c *UserCronClient) ExpireManagerUserBlocks(ctx context.Context, req scheduler.BatchRequest) (scheduler.BatchResult, error) {
resp, err := c.client.ExpireManagerUserBlocks(ctx, userCronRequest(req))
return userCronResult(resp), err
}
func userCronRequest(req scheduler.BatchRequest) *userv1.CronBatchRequest {
return &userv1.CronBatchRequest{
Meta: &userv1.RequestMeta{

View File

@ -80,6 +80,7 @@ CREATE TABLE IF NOT EXISTS game_launch_sessions (
updated_at_ms BIGINT NOT NULL COMMENT '更新时间UTC epoch ms',
PRIMARY KEY(app_code, session_id),
KEY idx_game_launch_user(app_code, user_id, created_at_ms),
KEY idx_game_launch_recent(app_code, user_id, scene, created_at_ms, game_id),
KEY idx_game_launch_token(app_code, launch_token_hash)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='游戏启动会话表';

View File

@ -0,0 +1,88 @@
package game
import (
"encoding/json"
"net/url"
"strings"
gamedomain "hyapp/services/game-service/internal/domain/game"
)
type bridgeScriptConfig struct {
URL string `json:"bridge_script_url"`
URLAlias string `json:"bridgeScriptUrl"`
Version string `json:"bridge_script_version"`
VersionAlias string `json:"bridgeScriptVersion"`
SHA256 string `json:"bridge_script_sha256"`
SHA256Alias string `json:"bridgeScriptSha256"`
SHA256AltAlias string `json:"bridge_script_hash"`
}
func bridgeScriptConfigFromPlatform(value any) bridgeScriptConfig {
var raw string
switch typed := value.(type) {
case gamedomain.Platform:
raw = typed.AdapterConfigJSON
case gamedomain.LaunchableGame:
raw = typed.AdapterConfigJSON
}
var config bridgeScriptConfig
_ = json.Unmarshal([]byte(strings.TrimSpace(raw)), &config)
config.URL = firstBridgeScriptValue(config.URL, config.URLAlias)
config.Version = firstBridgeScriptValue(config.Version, config.VersionAlias)
config.SHA256 = firstBridgeScriptValue(config.SHA256, config.SHA256Alias, config.SHA256AltAlias)
return config
}
func firstBridgeScriptValue(values ...string) string {
for _, value := range values {
normalized := strings.TrimSpace(value)
if normalized != "" {
return normalized
}
}
return ""
}
func appendBridgeScriptQuery(query url.Values, game gamedomain.LaunchableGame) {
config := bridgeScriptConfigFromPlatform(game)
if config.URL == "" {
return
}
// Flutter 从启动 URL 还原 bridgeConfig同时写 snake/camel 别名,避免 H5 或老客户端只识别其中一种命名。
query.Set("bridge_script_url", config.URL)
query.Set("bridgeScriptUrl", config.URL)
if config.Version != "" {
query.Set("bridge_script_version", config.Version)
query.Set("bridgeScriptVersion", config.Version)
}
if config.SHA256 != "" {
query.Set("bridge_script_sha256", config.SHA256)
query.Set("bridgeScriptSha256", config.SHA256)
}
appendBridgeKeys(query, "bridge_script_url", "bridge_script_version", "bridge_script_sha256")
}
func appendBridgeKeys(query url.Values, keys ...string) {
seen := make(map[string]bool)
values := make([]string, 0, len(keys)+1)
for _, raw := range strings.Split(query.Get("bridge_keys"), ",") {
key := strings.TrimSpace(raw)
if key == "" || seen[key] {
continue
}
seen[key] = true
values = append(values, key)
}
for _, raw := range keys {
key := strings.TrimSpace(raw)
if key == "" || seen[key] {
continue
}
seen[key] = true
values = append(values, key)
}
if len(values) > 0 {
query.Set("bridge_keys", strings.Join(values, ","))
}
}

View File

@ -8,6 +8,7 @@ import (
"encoding/json"
"fmt"
"net/url"
"sort"
"strconv"
"strings"
"sync"
@ -29,6 +30,7 @@ const defaultScene = gamedomain.SceneVoiceRoom
type Repository interface {
Ping(ctx context.Context) error
ListGames(ctx context.Context, query ListGamesQuery) ([]gamedomain.AppGame, error)
ListRecentGames(ctx context.Context, query ListRecentGamesQuery) ([]gamedomain.AppGame, error)
GetLaunchableGame(ctx context.Context, appCode string, gameID string) (gamedomain.LaunchableGame, error)
GetPlatform(ctx context.Context, appCode string, platformCode string) (gamedomain.Platform, error)
CreateLaunchSession(ctx context.Context, session gamedomain.LaunchSession) error
@ -91,6 +93,17 @@ type ListGamesQuery struct {
ClientPlatform string
}
type ListRecentGamesQuery struct {
AppCode string
UserID int64
Scene string
RoomID string
RegionID int64
Language string
ClientPlatform string
PageSize int32
}
type ListCatalogQuery struct {
AppCode string
PlatformCode string
@ -120,6 +133,13 @@ type LaunchResult struct {
ServerTimeMS int64
}
type BridgeScriptResult struct {
URL string
Version string
SHA256 string
ServerTimeMS int64
}
// New 创建 game-service 用例层。
func New(config Config, repository Repository, wallet WalletClient, user UserClient, activity ...ActivityClient) *Service {
if config.LaunchSessionTTL <= 0 {
@ -167,6 +187,62 @@ func (s *Service) ListGames(ctx context.Context, query ListGamesQuery) ([]gamedo
return games, s.now().UnixMilli(), err
}
// ListRecentGames 返回当前用户最近启动过的语音房游戏,供 App 的 My Games tab 直接展示。
func (s *Service) ListRecentGames(ctx context.Context, query ListRecentGamesQuery) ([]gamedomain.AppGame, int64, error) {
if query.UserID <= 0 {
return nil, 0, xerr.New(xerr.InvalidArgument, "user_id is required")
}
if s.repository == nil {
return nil, 0, xerr.New(xerr.Unavailable, "game repository is not configured")
}
query.AppCode = appcode.Normalize(query.AppCode)
query.Scene = normalizeScene(query.Scene)
query.Language = strings.ToLower(strings.TrimSpace(query.Language))
query.ClientPlatform = strings.ToLower(strings.TrimSpace(query.ClientPlatform))
query.PageSize = normalizeRecentGamesPageSize(query.PageSize)
ctx = appcode.WithContext(ctx, query.AppCode)
// 最近常玩只信任 game_launch_sessions 的真实启动事实,同时在仓储层重新套当前展示规则;
// 这样旧游戏下架、维护、区域不可见后不会继续出现在 My Games tab。
games, err := s.repository.ListRecentGames(ctx, query)
return games, s.now().UnixMilli(), err
}
// GetBridgeScript 返回 App 通用游戏桥接脚本配置,供客户端启动预热和打开游戏面板时做轻量刷新。
func (s *Service) GetBridgeScript(ctx context.Context, appCode string) (BridgeScriptResult, error) {
if s.repository == nil {
return BridgeScriptResult{}, xerr.New(xerr.Unavailable, "game repository is not configured")
}
appCode = appcode.Normalize(appCode)
ctx = appcode.WithContext(ctx, appCode)
platforms, err := s.repository.ListPlatforms(ctx, appCode, gamedomain.StatusActive)
if err != nil {
return BridgeScriptResult{}, err
}
// admin 会把通用桥接脚本同步写入所有平台;这里仍按 sort/code 排序后读取第一份有效配置,
// 避免数据库返回顺序变化导致 App 在相同配置之间来回切换。
sort.SliceStable(platforms, func(left, right int) bool {
if platforms[left].SortOrder != platforms[right].SortOrder {
return platforms[left].SortOrder < platforms[right].SortOrder
}
return strings.Compare(platforms[left].PlatformCode, platforms[right].PlatformCode) < 0
})
for _, platform := range platforms {
config := bridgeScriptConfigFromPlatform(platform)
if strings.TrimSpace(config.URL) == "" {
continue
}
return BridgeScriptResult{
URL: strings.TrimSpace(config.URL),
Version: strings.TrimSpace(config.Version),
SHA256: strings.TrimSpace(config.SHA256),
ServerTimeMS: s.now().UnixMilli(),
}, nil
}
return BridgeScriptResult{ServerTimeMS: s.now().UnixMilli()}, nil
}
// LaunchGame 创建短期 H5 启动会话,并把原始钱包能力限制在服务端。
func (s *Service) LaunchGame(ctx context.Context, command LaunchCommand) (LaunchResult, error) {
if command.UserID <= 0 || strings.TrimSpace(command.GameID) == "" {
@ -602,6 +678,16 @@ func normalizeScene(scene string) string {
return scene
}
func normalizeRecentGamesPageSize(pageSize int32) int32 {
if pageSize <= 0 {
return 4
}
if pageSize > 20 {
return 20
}
return pageSize
}
func buildLaunchURL(game gamedomain.LaunchableGame, session gamedomain.LaunchSession, token string) (string, error) {
base := strings.TrimSpace(game.APIBaseURL)
if strings.EqualFold(game.AdapterType, gamedomain.AdapterLeaderCCV1) && base == "" {
@ -670,6 +756,7 @@ func buildLaunchURL(game gamedomain.LaunchableGame, session gamedomain.LaunchSes
if session.RoomID != "" {
query.Set("platRoomId", session.RoomID)
}
appendBridgeScriptQuery(query, game)
parsed.RawQuery = query.Encode()
return parsed.String(), nil
}
@ -686,6 +773,7 @@ func buildLaunchURL(game gamedomain.LaunchableGame, session gamedomain.LaunchSes
if session.RoomID != "" {
query.Set("roomid", session.RoomID)
}
appendBridgeScriptQuery(query, game)
parsed.RawQuery = query.Encode()
return parsed.String(), nil
}
@ -713,6 +801,7 @@ func buildLaunchURL(game gamedomain.LaunchableGame, session gamedomain.LaunchSes
if config.Extra != "" {
query.Set("extra", config.Extra)
}
appendBridgeScriptQuery(query, game)
parsed.RawQuery = query.Encode()
return parsed.String(), nil
}
@ -737,6 +826,7 @@ func buildLaunchURL(game gamedomain.LaunchableGame, session gamedomain.LaunchSes
query.Set("currencyIcon", config.CurrencyIcon)
}
query.Set("gsp", strconv.FormatInt(config.GSP, 10))
appendBridgeScriptQuery(query, game)
parsed.RawQuery = query.Encode()
return parsed.String(), nil
}
@ -760,6 +850,7 @@ func buildLaunchURL(game gamedomain.LaunchableGame, session gamedomain.LaunchSes
query.Set("currencyIcon", config.CurrencyIcon)
query.Set("gameConfig", vivaGamesGameConfigQuery(config.CurrencyIcon))
}
appendBridgeScriptQuery(query, game)
parsed.RawQuery = query.Encode()
return parsed.String(), nil
}
@ -773,6 +864,7 @@ func buildLaunchURL(game gamedomain.LaunchableGame, session gamedomain.LaunchSes
query.Set("display_user_id", session.DisplayUserID)
query.Set("scene", session.Scene)
query.Set("room_id", session.RoomID)
appendBridgeScriptQuery(query, game)
parsed.RawQuery = query.Encode()
return parsed.String(), nil
}

View File

@ -68,6 +68,66 @@ func TestLaunchGameCreatesSessionAndRejectsMaintenance(t *testing.T) {
}
}
func TestListRecentGamesNormalizesQueryAndDefaultsLimit(t *testing.T) {
repo := &fakeRepository{
recentGames: []gamedomain.AppGame{
{GameID: "viva_2", Name: "Rich Forever"},
},
}
svc := New(Config{LaunchSessionTTL: 15 * time.Minute}, repo, &fakeWallet{}, &fakeUser{})
svc.now = func() time.Time { return time.UnixMilli(1700000000000) }
games, serverTimeMS, err := svc.ListRecentGames(context.Background(), ListRecentGamesQuery{
UserID: 42,
Scene: "",
Language: " EN-US ",
ClientPlatform: " Android ",
})
if err != nil {
t.Fatalf("ListRecentGames failed: %v", err)
}
if serverTimeMS != 1700000000000 || len(games) != 1 || games[0].GameID != "viva_2" {
t.Fatalf("recent games response mismatch: time=%d games=%+v", serverTimeMS, games)
}
if repo.lastRecentQuery.AppCode != "lalu" || repo.lastRecentQuery.Scene != gamedomain.SceneVoiceRoom || repo.lastRecentQuery.PageSize != 4 || repo.lastRecentQuery.Language != "en-us" || repo.lastRecentQuery.ClientPlatform != "android" {
t.Fatalf("recent query normalization mismatch: %+v", repo.lastRecentQuery)
}
}
func TestGetBridgeScriptUsesFirstActivePlatformConfig(t *testing.T) {
repo := &fakeRepository{
platforms: []gamedomain.Platform{
{
AppCode: "lalu",
PlatformCode: "vivagames",
Status: gamedomain.StatusActive,
SortOrder: 20,
AdapterConfigJSON: `{"bridge_script_url":"https://cdn.example.test/bridge-viva.js","bridge_script_version":"20260608.2","bridge_script_sha256":"def"}`,
},
{
AppCode: "lalu",
PlatformCode: "baishun",
Status: gamedomain.StatusActive,
SortOrder: 10,
AdapterConfigJSON: `{"bridgeScriptUrl":"https://cdn.example.test/bridge.js","bridgeScriptVersion":"20260608.1","bridgeScriptSha256":"abc"}`,
},
},
}
svc := New(Config{LaunchSessionTTL: 15 * time.Minute}, repo, &fakeWallet{}, &fakeUser{})
svc.now = func() time.Time { return time.UnixMilli(1700000000000) }
result, err := svc.GetBridgeScript(context.Background(), " LALU ")
if err != nil {
t.Fatalf("GetBridgeScript failed: %v", err)
}
if result.URL != "https://cdn.example.test/bridge.js" || result.Version != "20260608.1" || result.SHA256 != "abc" || result.ServerTimeMS != 1700000000000 {
t.Fatalf("bridge script mismatch: %+v", result)
}
if repo.lastListPlatformsAppCode != "lalu" || repo.lastListPlatformsStatus != gamedomain.StatusActive {
t.Fatalf("ListPlatforms query mismatch: app=%q status=%q", repo.lastListPlatformsAppCode, repo.lastListPlatformsStatus)
}
}
func TestLaunchGameBuildsYomiAuthURL(t *testing.T) {
repo := &fakeRepository{
launchable: gamedomain.LaunchableGame{
@ -359,6 +419,9 @@ func TestLaunchGameBuildsVivaGamesURL(t *testing.T) {
"bgm_switch":0,
"gsp":101,
"currency_icon":"https://cdn.example/coin.png",
"bridge_script_url":"https://cdn.example/game-bridge/voice-room-game-bridge.v1.js",
"bridge_script_version":"20260608.1",
"bridge_script_sha256":"78f0d2d0b52d5711c1f2a9e8d3af8a9d3db7e03c83f5bd8d2b8e629f1f5b6c13",
"game_urls":{"2":"https://dev-rich2.s3.ap-southeast-1.amazonaws.com/richForever.html?game_id=2&isShow=1"}
}`,
},
@ -389,19 +452,25 @@ func TestLaunchGameBuildsVivaGamesURL(t *testing.T) {
}
query := parsed.Query()
expected := map[string]string{
"appId": "1012127974",
"userId": "420001",
"code": "app_access_token_viva",
"gameId": "2",
"metadata": `{"room_id":"room_1"}`,
"language": "en-US",
"coinType": "0",
"amountRate": "1",
"bgmSwitch": "0",
"gsp": "101",
"currencyIcon": "https://cdn.example/coin.png",
"gameConfig": `{"currencyIcon":"https://cdn.example/coin.png"}`,
"bridge_keys": "appId,userId,code,metadata,language,coinType,amountRate,bgmSwitch,gsp,currencyIcon,gameConfig,gameId",
"appId": "1012127974",
"userId": "420001",
"code": "app_access_token_viva",
"gameId": "2",
"metadata": `{"room_id":"room_1"}`,
"language": "en-US",
"coinType": "0",
"amountRate": "1",
"bgmSwitch": "0",
"gsp": "101",
"currencyIcon": "https://cdn.example/coin.png",
"gameConfig": `{"currencyIcon":"https://cdn.example/coin.png"}`,
"bridge_keys": "appId,userId,code,metadata,language,coinType,amountRate,bgmSwitch,gsp,currencyIcon,gameConfig,gameId,bridge_script_url,bridge_script_version,bridge_script_sha256",
"bridge_script_url": "https://cdn.example/game-bridge/voice-room-game-bridge.v1.js",
"bridgeScriptUrl": "https://cdn.example/game-bridge/voice-room-game-bridge.v1.js",
"bridge_script_version": "20260608.1",
"bridgeScriptVersion": "20260608.1",
"bridge_script_sha256": "78f0d2d0b52d5711c1f2a9e8d3af8a9d3db7e03c83f5bd8d2b8e629f1f5b6c13",
"bridgeScriptSha256": "78f0d2d0b52d5711c1f2a9e8d3af8a9d3db7e03c83f5bd8d2b8e629f1f5b6c13",
}
for key, want := range expected {
if got := query.Get(key); got != want {
@ -1359,21 +1428,33 @@ func TestProcessLevelEventOutboxBatchRelaysGameSpend(t *testing.T) {
}
type fakeRepository struct {
launchable gamedomain.LaunchableGame
platform gamedomain.Platform
session gamedomain.LaunchSession
order gamedomain.GameOrder
orders []gamedomain.GameOrder
repair gamedomain.RepairOrder
levelEvents []gamedomain.LevelEventOutbox
deliveredEvents []string
failedEvents []string
launchable gamedomain.LaunchableGame
platform gamedomain.Platform
session gamedomain.LaunchSession
order gamedomain.GameOrder
orders []gamedomain.GameOrder
repair gamedomain.RepairOrder
levelEvents []gamedomain.LevelEventOutbox
recentGames []gamedomain.AppGame
lastRecentQuery ListRecentGamesQuery
platforms []gamedomain.Platform
lastListPlatformsAppCode string
lastListPlatformsStatus string
deliveredEvents []string
failedEvents []string
}
func (f *fakeRepository) Ping(context.Context) error { return nil }
func (f *fakeRepository) ListGames(context.Context, ListGamesQuery) ([]gamedomain.AppGame, error) {
return []gamedomain.AppGame{{GameID: "demo_rocket_001"}}, nil
}
func (f *fakeRepository) ListRecentGames(_ context.Context, query ListRecentGamesQuery) ([]gamedomain.AppGame, error) {
f.lastRecentQuery = query
if f.recentGames != nil {
return f.recentGames, nil
}
return []gamedomain.AppGame{{GameID: "demo_recent_001"}}, nil
}
func (f *fakeRepository) GetLaunchableGame(context.Context, string, string) (gamedomain.LaunchableGame, error) {
return f.launchable, nil
}
@ -1472,7 +1553,12 @@ func (f *fakeRepository) MarkLevelEventFailed(_ context.Context, eventID string,
f.failedEvents = append(f.failedEvents, eventID)
return nil
}
func (f *fakeRepository) ListPlatforms(context.Context, string, string) ([]gamedomain.Platform, error) {
func (f *fakeRepository) ListPlatforms(_ context.Context, appCode string, status string) ([]gamedomain.Platform, error) {
f.lastListPlatformsAppCode = appCode
f.lastListPlatformsStatus = status
if f.platforms != nil {
return f.platforms, nil
}
return nil, nil
}
func (f *fakeRepository) UpsertPlatform(context.Context, gamedomain.Platform) (gamedomain.Platform, error) {

View File

@ -404,6 +404,67 @@ func (r *Repository) ListGames(ctx context.Context, query gameservice.ListGamesQ
return items, rows.Err()
}
func (r *Repository) ListRecentGames(ctx context.Context, query gameservice.ListRecentGamesQuery) ([]gamedomain.AppGame, error) {
pageSize := int(query.PageSize)
if pageSize <= 0 {
pageSize = 4
}
// My Games 先按当前用户的启动会话聚合出每个游戏的最后一次启动时间,
// 再套当前 catalog、platform 和 display_rules保证最近玩过但已下架或当前区域不可见的游戏不会返回。
rows, err := r.db.QueryContext(ctx,
`SELECT c.game_id, c.platform_code, c.game_name, c.icon_url, c.cover_url, c.category,
c.launch_mode, c.orientation, c.safe_height, c.min_coin, c.status, p.status,
MIN(CASE WHEN r.sort_order = 0 THEN c.sort_order ELSE r.sort_order END) AS display_sort,
recent.last_played_at_ms
FROM (
SELECT game_id, MAX(created_at_ms) AS last_played_at_ms
FROM game_launch_sessions
WHERE app_code = ?
AND user_id = ?
AND scene = ?
GROUP BY game_id
) recent
JOIN game_catalog c ON c.app_code = ? AND c.game_id = recent.game_id
JOIN game_platforms p ON p.app_code = c.app_code AND p.platform_code = c.platform_code
JOIN game_display_rules r ON r.app_code = c.app_code AND r.game_id = c.game_id
WHERE c.status IN ('active', 'maintenance')
AND p.status IN ('active', 'maintenance')
AND r.scene = ?
AND r.enabled = 1
AND r.visible = 1
AND (r.region_id = 0 OR r.region_id = ?)
AND (r.language = '' OR r.language = ?)
AND (r.platform = '' OR r.platform = ?)
GROUP BY c.game_id, c.platform_code, c.game_name, c.icon_url, c.cover_url, c.category,
c.launch_mode, c.orientation, c.safe_height, c.min_coin, c.status, p.status,
c.sort_order, p.sort_order, recent.last_played_at_ms
ORDER BY recent.last_played_at_ms DESC, display_sort ASC, p.sort_order ASC, c.sort_order ASC, c.game_id ASC
LIMIT ?`,
appcode.Normalize(query.AppCode), query.UserID, query.Scene,
appcode.Normalize(query.AppCode), query.Scene, query.RegionID, query.Language, query.ClientPlatform, pageSize,
)
if err != nil {
return nil, err
}
defer rows.Close()
items := []gamedomain.AppGame{}
for rows.Next() {
var item gamedomain.AppGame
var gameStatus, platformStatus string
var displaySort int32
var lastPlayedAtMS int64
if err := rows.Scan(&item.GameID, &item.PlatformCode, &item.Name, &item.IconURL, &item.CoverURL, &item.Category, &item.LaunchMode, &item.Orientation, &item.SafeHeight, &item.MinCoin, &gameStatus, &platformStatus, &displaySort, &lastPlayedAtMS); err != nil {
return nil, err
}
item.NameKey = "game." + item.GameID + ".name"
item.SortOrder = displaySort
item.Enabled = gameStatus == gamedomain.StatusActive && platformStatus == gamedomain.StatusActive
item.Maintenance = gameStatus == gamedomain.StatusMaintenance || platformStatus == gamedomain.StatusMaintenance
items = append(items, item)
}
return items, rows.Err()
}
func (r *Repository) GetLaunchableGame(ctx context.Context, appCode string, gameID string) (gamedomain.LaunchableGame, error) {
// 启动游戏时一次性取出 catalog + platform 配置,避免拿到游戏后再查平台导致配置不一致。
row := r.db.QueryRowContext(ctx,

View File

@ -47,6 +47,44 @@ func (s *Server) ListGames(ctx context.Context, req *gamev1.ListGamesRequest) (*
return resp, nil
}
func (s *Server) ListRecentGames(ctx context.Context, req *gamev1.ListRecentGamesRequest) (*gamev1.ListGamesResponse, error) {
ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
items, serverTimeMS, err := s.svc.ListRecentGames(ctx, gameservice.ListRecentGamesQuery{
AppCode: req.GetMeta().GetAppCode(),
UserID: req.GetUserId(),
Scene: req.GetScene(),
RoomID: req.GetRoomId(),
RegionID: req.GetRegionId(),
Language: req.GetLanguage(),
ClientPlatform: req.GetClientPlatform(),
PageSize: req.GetPageSize(),
})
if err != nil {
return nil, xerr.ToGRPCError(err)
}
resp := &gamev1.ListGamesResponse{ServerTimeMs: serverTimeMS, Games: make([]*gamev1.AppGame, 0, len(items))}
for _, item := range items {
resp.Games = append(resp.Games, appGameToProto(item))
}
return resp, nil
}
func (s *Server) GetBridgeScript(ctx context.Context, req *gamev1.GetBridgeScriptRequest) (*gamev1.GetBridgeScriptResponse, error) {
ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
result, err := s.svc.GetBridgeScript(ctx, req.GetMeta().GetAppCode())
if err != nil {
return nil, xerr.ToGRPCError(err)
}
return &gamev1.GetBridgeScriptResponse{
Config: &gamev1.GameBridgeScriptConfig{
BridgeScriptUrl: result.URL,
BridgeScriptVersion: result.Version,
BridgeScriptSha256: result.SHA256,
},
ServerTimeMs: result.ServerTimeMS,
}, nil
}
func (s *Server) LaunchGame(ctx context.Context, req *gamev1.LaunchGameRequest) (*gamev1.LaunchGameResponse, error) {
ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
result, err := s.svc.LaunchGame(ctx, gameservice.LaunchCommand{
@ -195,6 +233,9 @@ func (unavailableRepository) Ping(context.Context) error {
func (unavailableRepository) ListGames(context.Context, gameservice.ListGamesQuery) ([]gamedomain.AppGame, error) {
return nil, xerr.New(xerr.Unavailable, "game repository is not configured")
}
func (unavailableRepository) ListRecentGames(context.Context, gameservice.ListRecentGamesQuery) ([]gamedomain.AppGame, error) {
return nil, xerr.New(xerr.Unavailable, "game repository is not configured")
}
func (unavailableRepository) GetLaunchableGame(context.Context, string, string) (gamedomain.LaunchableGame, error) {
return gamedomain.LaunchableGame{}, xerr.New(xerr.Unavailable, "game repository is not configured")
}

View File

@ -89,6 +89,7 @@ func New(cfg config.Config) (*App, error) {
var userProfileClient client.UserProfileClient = client.NewGRPCUserProfileClient(userConn)
var userDeviceClient client.UserDeviceClient = client.NewGRPCUserDeviceClient(userConn)
var userCountryQueryClient client.UserCountryQueryClient = client.NewGRPCUserCountryQueryClient(userConn)
var userRegionClient client.UserRegionClient = client.NewGRPCUserRegionClient(userConn)
var userHostClient client.UserHostClient = client.NewGRPCUserHostClient(userConn)
var userSocialClient client.UserSocialClient = client.NewGRPCUserSocialClient(userConn)
var userCPClient client.UserCPClient = client.NewGRPCUserCPClient(userConn)
@ -157,6 +158,7 @@ func New(cfg config.Config) (*App, error) {
handler.SetRoomQueryClient(roomQueryClient)
handler.SetUserDeviceClient(userDeviceClient)
handler.SetUserCountryQueryClient(userCountryQueryClient)
handler.SetUserRegionClient(userRegionClient)
handler.SetUserHostClient(userHostClient)
handler.SetUserSocialClient(userSocialClient)
handler.SetUserCPClient(userCPClient)

View File

@ -10,6 +10,8 @@ import (
// GameClient 抽象 gateway 对 game-service App 与回调 RPC 的依赖。
type GameClient interface {
ListGames(ctx context.Context, req *gamev1.ListGamesRequest) (*gamev1.ListGamesResponse, error)
ListRecentGames(ctx context.Context, req *gamev1.ListRecentGamesRequest) (*gamev1.ListGamesResponse, error)
GetBridgeScript(ctx context.Context, req *gamev1.GetBridgeScriptRequest) (*gamev1.GetBridgeScriptResponse, error)
LaunchGame(ctx context.Context, req *gamev1.LaunchGameRequest) (*gamev1.LaunchGameResponse, error)
HandleCallback(ctx context.Context, req *gamev1.CallbackRequest) (*gamev1.CallbackResponse, error)
}
@ -30,6 +32,14 @@ func (c *grpcGameClient) ListGames(ctx context.Context, req *gamev1.ListGamesReq
return c.app.ListGames(ctx, req)
}
func (c *grpcGameClient) ListRecentGames(ctx context.Context, req *gamev1.ListRecentGamesRequest) (*gamev1.ListGamesResponse, error) {
return c.app.ListRecentGames(ctx, req)
}
func (c *grpcGameClient) GetBridgeScript(ctx context.Context, req *gamev1.GetBridgeScriptRequest) (*gamev1.GetBridgeScriptResponse, error) {
return c.app.GetBridgeScript(ctx, req)
}
func (c *grpcGameClient) LaunchGame(ctx context.Context, req *gamev1.LaunchGameRequest) (*gamev1.LaunchGameResponse, error) {
return c.app.LaunchGame(ctx, req)
}

View File

@ -13,6 +13,7 @@ type GrowthLevelClient interface {
GetLevelTrack(ctx context.Context, req *activityv1.GetLevelTrackRequest) (*activityv1.GetLevelTrackResponse, error)
BatchGetUserLevelDisplayProfiles(ctx context.Context, req *activityv1.BatchGetUserLevelDisplayProfilesRequest) (*activityv1.BatchGetUserLevelDisplayProfilesResponse, error)
ListLevelRewards(ctx context.Context, req *activityv1.ListLevelRewardsRequest) (*activityv1.ListLevelRewardsResponse, error)
SetUserLevel(ctx context.Context, req *activityv1.SetUserLevelRequest) (*activityv1.SetUserLevelResponse, error)
}
type grpcGrowthLevelClient struct {
@ -38,3 +39,7 @@ func (c *grpcGrowthLevelClient) BatchGetUserLevelDisplayProfiles(ctx context.Con
func (c *grpcGrowthLevelClient) ListLevelRewards(ctx context.Context, req *activityv1.ListLevelRewardsRequest) (*activityv1.ListLevelRewardsResponse, error) {
return c.client.ListLevelRewards(ctx, req)
}
func (c *grpcGrowthLevelClient) SetUserLevel(ctx context.Context, req *activityv1.SetUserLevelRequest) (*activityv1.SetUserLevelResponse, error) {
return c.client.SetUserLevel(ctx, req)
}

View File

@ -37,6 +37,9 @@ type UserProfileClient interface {
CompleteOnboarding(ctx context.Context, req *userv1.CompleteOnboardingRequest) (*userv1.CompleteOnboardingResponse, error)
UpdateUserProfile(ctx context.Context, req *userv1.UpdateUserProfileRequest) (*userv1.UpdateUserProfileResponse, error)
ChangeUserCountry(ctx context.Context, req *userv1.ChangeUserCountryRequest) (*userv1.ChangeUserCountryResponse, error)
CreateManagerUserBlock(ctx context.Context, req *userv1.CreateManagerUserBlockRequest) (*userv1.CreateManagerUserBlockResponse, error)
ListManagerUserBlocks(ctx context.Context, req *userv1.ListManagerUserBlocksRequest) (*userv1.ListManagerUserBlocksResponse, error)
UnblockManagerUser(ctx context.Context, req *userv1.UnblockManagerUserRequest) (*userv1.UnblockManagerUserResponse, error)
}
// UserSocialClient 抽象 gateway 对 user-service 访问、关注和好友关系能力的依赖。
@ -74,6 +77,11 @@ type UserCountryQueryClient interface {
ListLoginRiskBlockedCountries(ctx context.Context, req *userv1.ListLoginRiskBlockedCountriesRequest) (*userv1.ListLoginRiskBlockedCountriesResponse, error)
}
// UserRegionClient 抽象 gateway 对 user-service 区域国家归属查询的依赖。
type UserRegionClient interface {
GetRegion(ctx context.Context, req *userv1.GetRegionRequest) (*userv1.RegionResponse, error)
}
// UserHostClient 抽象 gateway 对 user-service host domain 身份查询能力的依赖。
type UserHostClient interface {
SearchAgencies(ctx context.Context, req *userv1.SearchAgenciesRequest) (*userv1.SearchAgenciesResponse, error)
@ -129,6 +137,10 @@ type grpcUserCountryQueryClient struct {
client userv1.CountryQueryServiceClient
}
type grpcUserRegionClient struct {
client userv1.RegionAdminServiceClient
}
type grpcUserHostClient struct {
client userv1.UserHostServiceClient
}
@ -186,6 +198,13 @@ func NewGRPCUserCountryQueryClient(conn *grpc.ClientConn) UserCountryQueryClient
}
}
// NewGRPCUserRegionClient 用 gRPC 连接构造 user-service 区域查询 client。
func NewGRPCUserRegionClient(conn *grpc.ClientConn) UserRegionClient {
return &grpcUserRegionClient{
client: userv1.NewRegionAdminServiceClient(conn),
}
}
// NewGRPCUserHostClient 用 gRPC 连接构造 user-service host domain client。
func NewGRPCUserHostClient(conn *grpc.ClientConn) UserHostClient {
return &grpcUserHostClient{
@ -276,6 +295,18 @@ func (c *grpcUserProfileClient) ChangeUserCountry(ctx context.Context, req *user
return c.client.ChangeUserCountry(ctx, req)
}
func (c *grpcUserProfileClient) CreateManagerUserBlock(ctx context.Context, req *userv1.CreateManagerUserBlockRequest) (*userv1.CreateManagerUserBlockResponse, error) {
return c.client.CreateManagerUserBlock(ctx, req)
}
func (c *grpcUserProfileClient) ListManagerUserBlocks(ctx context.Context, req *userv1.ListManagerUserBlocksRequest) (*userv1.ListManagerUserBlocksResponse, error) {
return c.client.ListManagerUserBlocks(ctx, req)
}
func (c *grpcUserProfileClient) UnblockManagerUser(ctx context.Context, req *userv1.UnblockManagerUserRequest) (*userv1.UnblockManagerUserResponse, error) {
return c.client.UnblockManagerUser(ctx, req)
}
func (c *grpcUserSocialClient) RecordProfileVisit(ctx context.Context, req *userv1.RecordProfileVisitRequest) (*userv1.RecordProfileVisitResponse, error) {
return c.client.RecordProfileVisit(ctx, req)
}
@ -352,6 +383,10 @@ func (c *grpcUserCountryQueryClient) ListLoginRiskBlockedCountries(ctx context.C
return c.client.ListLoginRiskBlockedCountries(ctx, req)
}
func (c *grpcUserRegionClient) GetRegion(ctx context.Context, req *userv1.GetRegionRequest) (*userv1.RegionResponse, error) {
return c.client.GetRegion(ctx, req)
}
func (c *grpcUserHostClient) SearchAgencies(ctx context.Context, req *userv1.SearchAgenciesRequest) (*userv1.SearchAgenciesResponse, error) {
return c.client.SearchAgencies(ctx, req)
}

View File

@ -23,6 +23,7 @@ type WalletClient interface {
ListVipPackages(ctx context.Context, req *walletv1.ListVipPackagesRequest) (*walletv1.ListVipPackagesResponse, error)
GetMyVip(ctx context.Context, req *walletv1.GetMyVipRequest) (*walletv1.GetMyVipResponse, error)
PurchaseVip(ctx context.Context, req *walletv1.PurchaseVipRequest) (*walletv1.PurchaseVipResponse, error)
GrantVip(ctx context.Context, req *walletv1.GrantVipRequest) (*walletv1.GrantVipResponse, error)
TransferCoinFromSeller(ctx context.Context, req *walletv1.TransferCoinFromSellerRequest) (*walletv1.TransferCoinFromSellerResponse, error)
ListCoinSellerSalaryExchangeRateTiers(ctx context.Context, req *walletv1.ListCoinSellerSalaryExchangeRateTiersRequest) (*walletv1.ListCoinSellerSalaryExchangeRateTiersResponse, error)
ExchangeSalaryToCoin(ctx context.Context, req *walletv1.ExchangeSalaryToCoinRequest) (*walletv1.ExchangeSalaryToCoinResponse, error)
@ -107,6 +108,10 @@ func (c *grpcWalletClient) PurchaseVip(ctx context.Context, req *walletv1.Purcha
return c.client.PurchaseVip(ctx, req)
}
func (c *grpcWalletClient) GrantVip(ctx context.Context, req *walletv1.GrantVipRequest) (*walletv1.GrantVipResponse, error) {
return c.client.GrantVip(ctx, req)
}
func (c *grpcWalletClient) TransferCoinFromSeller(ctx context.Context, req *walletv1.TransferCoinFromSellerRequest) (*walletv1.TransferCoinFromSellerResponse, error) {
return c.client.TransferCoinFromSeller(ctx, req)
}

View File

@ -54,6 +54,81 @@ func TestListGamesUsesAuthenticatedUserRegionAndReturnsEnvelope(t *testing.T) {
}
}
func TestListRecentGamesUsesAuthenticatedUserRegionAndLimit(t *testing.T) {
gameClient := &fakeGatewayGameClient{recentResp: &gamev1.ListGamesResponse{
Games: []*gamev1.AppGame{{
GameId: "viva_2",
PlatformCode: "vivagames",
Name: "Rich Forever",
LaunchMode: "h5_popup",
Orientation: "portrait",
Enabled: true,
}},
ServerTimeMs: 1700000000000,
}}
handler := NewHandlerWithClients(&fakeRoomClient{}, nil, nil, &fakeUserProfileClient{regionID: 1001})
handler.SetGameClient(gameClient)
router := handler.Routes(auth.NewVerifier("secret"))
request := httptest.NewRequest(http.MethodGet, "/api/v1/games/recent?scene=voice_room&room_id=room-1&limit=4", nil)
request.Header.Set("Authorization", "Bearer "+signGatewayToken(t, "secret", 42))
request.Header.Set("X-App-Language", "en")
request.Header.Set("X-App-Platform", "android")
recorder := httptest.NewRecorder()
router.ServeHTTP(recorder, request)
if recorder.Code != http.StatusOK {
t.Fatalf("status mismatch: got %d body=%s", recorder.Code, recorder.Body.String())
}
if gameClient.lastRecent.GetUserId() != 42 || gameClient.lastRecent.GetRegionId() != 1001 || gameClient.lastRecent.GetScene() != "voice_room" || gameClient.lastRecent.GetRoomId() != "room-1" || gameClient.lastRecent.GetPageSize() != 4 || gameClient.lastRecent.GetClientPlatform() != "android" {
t.Fatalf("ListRecentGames request mismatch: %+v", gameClient.lastRecent)
}
var envelope httpkit.ResponseEnvelope
if err := json.NewDecoder(recorder.Body).Decode(&envelope); err != nil {
t.Fatalf("decode response failed: %v", err)
}
data := envelope.Data.(map[string]any)
games := data["games"].([]any)
first := games[0].(map[string]any)
if first["platform_code"] != "vivagames" || first["game_id"] != "viva_2" {
t.Fatalf("recent game response mismatch: %+v", first)
}
}
func TestGetBridgeScriptReturnsUniversalScriptConfig(t *testing.T) {
gameClient := &fakeGatewayGameClient{bridgeResp: &gamev1.GetBridgeScriptResponse{
Config: &gamev1.GameBridgeScriptConfig{
BridgeScriptUrl: "https://cdn.example.test/game-bridge.js",
BridgeScriptVersion: "20260608.1",
BridgeScriptSha256: "abc",
},
ServerTimeMs: 1700000000000,
}}
handler := NewHandlerWithClients(&fakeRoomClient{}, nil, nil, &fakeUserProfileClient{regionID: 1001})
handler.SetGameClient(gameClient)
router := handler.Routes(auth.NewVerifier("secret"))
request := httptest.NewRequest(http.MethodGet, "/api/v1/games/bridge-script", nil)
request.Header.Set("Authorization", "Bearer "+signGatewayToken(t, "secret", 42))
recorder := httptest.NewRecorder()
router.ServeHTTP(recorder, request)
if recorder.Code != http.StatusOK {
t.Fatalf("status mismatch: got %d body=%s", recorder.Code, recorder.Body.String())
}
if gameClient.lastBridge.GetMeta().GetActorUserId() != 42 {
t.Fatalf("GetBridgeScript request mismatch: %+v", gameClient.lastBridge)
}
var envelope httpkit.ResponseEnvelope
if err := json.NewDecoder(recorder.Body).Decode(&envelope); err != nil {
t.Fatalf("decode response failed: %v", err)
}
data := envelope.Data.(map[string]any)
if data["bridge_script_url"] != "https://cdn.example.test/game-bridge.js" || data["bridgeScriptUrl"] != "https://cdn.example.test/game-bridge.js" || data["bridge_script_version"] != "20260608.1" || data["bridge_script_sha256"] != "abc" {
t.Fatalf("bridge script response mismatch: %+v", data)
}
}
func TestLaunchGameForwardsDisplayUserID(t *testing.T) {
gameClient := &fakeGatewayGameClient{launchResp: &gamev1.LaunchGameResponse{
SessionId: "game_sess_1",
@ -100,9 +175,13 @@ func TestGameCallbackIsRawPublicResponse(t *testing.T) {
type fakeGatewayGameClient struct {
listResp *gamev1.ListGamesResponse
recentResp *gamev1.ListGamesResponse
bridgeResp *gamev1.GetBridgeScriptResponse
launchResp *gamev1.LaunchGameResponse
callbackResp *gamev1.CallbackResponse
lastList *gamev1.ListGamesRequest
lastRecent *gamev1.ListRecentGamesRequest
lastBridge *gamev1.GetBridgeScriptRequest
lastLaunch *gamev1.LaunchGameRequest
lastCallback *gamev1.CallbackRequest
}
@ -115,6 +194,22 @@ func (f *fakeGatewayGameClient) ListGames(_ context.Context, req *gamev1.ListGam
return &gamev1.ListGamesResponse{}, nil
}
func (f *fakeGatewayGameClient) ListRecentGames(_ context.Context, req *gamev1.ListRecentGamesRequest) (*gamev1.ListGamesResponse, error) {
f.lastRecent = req
if f.recentResp != nil {
return f.recentResp, nil
}
return &gamev1.ListGamesResponse{}, nil
}
func (f *fakeGatewayGameClient) GetBridgeScript(_ context.Context, req *gamev1.GetBridgeScriptRequest) (*gamev1.GetBridgeScriptResponse, error) {
f.lastBridge = req
if f.bridgeResp != nil {
return f.bridgeResp, nil
}
return &gamev1.GetBridgeScriptResponse{}, nil
}
func (f *fakeGatewayGameClient) LaunchGame(_ context.Context, req *gamev1.LaunchGameRequest) (*gamev1.LaunchGameResponse, error) {
f.lastLaunch = req
if f.launchResp != nil {

View File

@ -40,6 +40,52 @@ func (h *Handler) listGames(writer http.ResponseWriter, request *http.Request) {
httpkit.Write(writer, request, gameListDataFromProto(resp), err)
}
func (h *Handler) listRecentGames(writer http.ResponseWriter, request *http.Request) {
if h.gameClient == nil || h.userProfileClient == nil {
httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
return
}
pageSize, ok := recentGamePageSize(request)
if !ok {
httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
return
}
userID := auth.UserIDFromContext(request.Context())
userResp, err := h.userProfileClient.GetUser(request.Context(), &userv1.GetUserRequest{
Meta: httpkit.UserMeta(request, ""),
UserId: userID,
})
if err != nil {
httpkit.WriteRPCError(writer, request, err)
return
}
// 最近常玩仍传 scene、room、region、language 和 platform让 game-service 在真实启动记录之外,
// 继续按当前房间场景和用户区域过滤,避免 App 展示用户已不能进入的游戏。
resp, err := h.gameClient.ListRecentGames(request.Context(), &gamev1.ListRecentGamesRequest{
Meta: gameMeta(request),
UserId: userID,
Scene: strings.TrimSpace(request.URL.Query().Get("scene")),
RoomId: strings.TrimSpace(request.URL.Query().Get("room_id")),
RegionId: userResp.GetUser().GetRegionId(),
Language: requestLanguage(request),
ClientPlatform: httpkit.FirstHeader(request, "X-App-Platform", "X-Platform"),
PageSize: pageSize,
})
httpkit.Write(writer, request, gameListDataFromProto(resp), err)
}
func (h *Handler) getBridgeScript(writer http.ResponseWriter, request *http.Request) {
if h.gameClient == nil {
httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
return
}
// 桥接脚本配置是 App 级启动预热信息,不依赖房间和用户区域;保持接口轻量,避免拖慢进 App 主流程。
resp, err := h.gameClient.GetBridgeScript(request.Context(), &gamev1.GetBridgeScriptRequest{
Meta: gameMeta(request),
})
httpkit.Write(writer, request, gameBridgeScriptDataFromProto(resp), err)
}
func (h *Handler) launchGame(writer http.ResponseWriter, request *http.Request) {
if h.gameClient == nil || h.userProfileClient == nil {
httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
@ -133,6 +179,13 @@ func requestLanguage(request *http.Request) string {
return strings.TrimSpace(value)
}
func recentGamePageSize(request *http.Request) (int32, bool) {
if strings.TrimSpace(request.URL.Query().Get("limit")) != "" {
return httpkit.PositiveInt32Query(request, "limit", 4)
}
return httpkit.PositiveInt32Query(request, "page_size", 4)
}
func bearerAccessToken(request *http.Request) string {
header := strings.TrimSpace(request.Header.Get("Authorization"))
if !strings.HasPrefix(header, "Bearer ") {

View File

@ -35,6 +35,16 @@ type gameLaunchData struct {
ServerTimeMS int64 `json:"server_time_ms"`
}
type gameBridgeScriptData struct {
BridgeScriptURL string `json:"bridge_script_url"`
BridgeScriptURLV2 string `json:"bridgeScriptUrl"`
BridgeScriptVersion string `json:"bridge_script_version"`
BridgeScriptVersionV2 string `json:"bridgeScriptVersion"`
BridgeScriptSHA256 string `json:"bridge_script_sha256"`
BridgeScriptSHA256V2 string `json:"bridgeScriptSha256"`
ServerTimeMS int64 `json:"server_time_ms"`
}
func gameListDataFromProto(resp *gamev1.ListGamesResponse) gameListData {
if resp == nil {
return gameListData{}
@ -76,3 +86,19 @@ func gameLaunchDataFromProto(resp *gamev1.LaunchGameResponse) gameLaunchData {
ServerTimeMS: resp.GetServerTimeMs(),
}
}
func gameBridgeScriptDataFromProto(resp *gamev1.GetBridgeScriptResponse) gameBridgeScriptData {
if resp == nil {
return gameBridgeScriptData{}
}
config := resp.GetConfig()
return gameBridgeScriptData{
BridgeScriptURL: config.GetBridgeScriptUrl(),
BridgeScriptURLV2: config.GetBridgeScriptUrl(),
BridgeScriptVersion: config.GetBridgeScriptVersion(),
BridgeScriptVersionV2: config.GetBridgeScriptVersion(),
BridgeScriptSHA256: config.GetBridgeScriptSha256(),
BridgeScriptSHA256V2: config.GetBridgeScriptSha256(),
ServerTimeMS: resp.GetServerTimeMs(),
}
}

View File

@ -28,9 +28,11 @@ func New(config Config) *Handler {
func (h *Handler) Handlers() httproutes.GameHandlers {
return httproutes.GameHandlers{
ListGames: h.listGames,
LaunchGame: h.launchGame,
HandleCallback: h.handleGameCallback,
ListGames: h.listGames,
ListRecentGames: h.listRecentGames,
GetBridgeScript: h.getBridgeScript,
LaunchGame: h.launchGame,
HandleCallback: h.handleGameCallback,
}
}
@ -38,6 +40,14 @@ func (h *Handler) ListGames(writer http.ResponseWriter, request *http.Request) {
h.listGames(writer, request)
}
func (h *Handler) ListRecentGames(writer http.ResponseWriter, request *http.Request) {
h.listRecentGames(writer, request)
}
func (h *Handler) GetBridgeScript(writer http.ResponseWriter, request *http.Request) {
h.getBridgeScript(writer, request)
}
func (h *Handler) LaunchGame(writer http.ResponseWriter, request *http.Request) {
h.launchGame(writer, request)
}

View File

@ -32,6 +32,7 @@ type Handler struct {
userCPClient client.UserCPClient
userDeviceClient client.UserDeviceClient
userCountryClient client.UserCountryQueryClient
userRegionClient client.UserRegionClient
userHostClient client.UserHostClient
appRegistryClient client.AppRegistryClient
walletClient client.WalletClient
@ -161,6 +162,11 @@ func (h *Handler) SetUserCountryQueryClient(userCountryClient client.UserCountry
h.userCountryClient = userCountryClient
}
// SetUserRegionClient 注入 user-service 区域国家归属查询 client。
func (h *Handler) SetUserRegionClient(userRegionClient client.UserRegionClient) {
h.userRegionClient = userRegionClient
}
// SetUserDeviceClient 注入 user-service 设备推送 token client。
func (h *Handler) SetUserDeviceClient(userDeviceClient client.UserDeviceClient) {
h.userDeviceClient = userDeviceClient

View File

@ -116,6 +116,14 @@ type ManagerHandlers struct {
ListManagerGrantResources http.HandlerFunc
GrantManagerResource http.HandlerFunc
LookupBusinessUsers http.HandlerFunc
GetManagerOverview http.HandlerFunc
SearchManagerUsers http.HandlerFunc
GrantManagerVIP http.HandlerFunc
ManagerBlocks http.HandlerFunc
ListManagerBlocks http.HandlerFunc
CreateManagerBlock http.HandlerFunc
UnblockManagerBlock http.HandlerFunc
SetManagerUserLevel http.HandlerFunc
}
type RoomHandlers struct {
@ -227,9 +235,11 @@ type VIPHandlers struct {
}
type GameHandlers struct {
ListGames http.HandlerFunc
LaunchGame http.HandlerFunc
HandleCallback http.HandlerFunc
ListGames http.HandlerFunc
ListRecentGames http.HandlerFunc
GetBridgeScript http.HandlerFunc
LaunchGame http.HandlerFunc
HandleCallback http.HandlerFunc
}
type routes struct {
@ -368,8 +378,14 @@ func (r routes) registerUserRoutes() {
func (r routes) registerManagerRoutes() {
h := r.config.Manager
r.profile("/manager-center/overview", http.MethodGet, h.GetManagerOverview)
r.profile("/manager-center/users/search", http.MethodGet, h.SearchManagerUsers)
r.profile("/manager-center/resource-grants/resources", http.MethodGet, h.ListManagerGrantResources)
r.profile("/manager-center/resource-grants", http.MethodPost, h.GrantManagerResource)
r.profile("/manager-center/vip-grants", http.MethodPost, h.GrantManagerVIP)
r.profile("/manager-center/blocks", "", h.ManagerBlocks)
r.profile("/manager-center/blocks/{block_id}/unblock", http.MethodPost, h.UnblockManagerBlock)
r.profile("/manager-center/levels", http.MethodPost, h.SetManagerUserLevel)
r.profile("/business/users/lookup", http.MethodGet, h.LookupBusinessUsers)
}
@ -493,6 +509,8 @@ func (r routes) registerVIPRoutes() {
func (r routes) registerGameRoutes() {
h := r.config.Game
r.profile("/games", http.MethodGet, h.ListGames)
r.profile("/games/recent", http.MethodGet, h.ListRecentGames)
r.profile("/games/bridge-script", http.MethodGet, h.GetBridgeScript)
r.profile("/games/{game_id}/launch", http.MethodPost, h.LaunchGame)
r.public("/game-callbacks/{platform_code}/{operation}", http.MethodPost, h.HandleCallback)
}

View File

@ -2,12 +2,14 @@ package http
import (
"bytes"
"context"
"encoding/json"
"hyapp/services/gateway-service/internal/transport/http/httpkit"
"net/http"
"net/http/httptest"
"testing"
activityv1 "hyapp.local/api/proto/activity/v1"
userv1 "hyapp.local/api/proto/user/v1"
walletv1 "hyapp.local/api/proto/wallet/v1"
"hyapp/services/gateway-service/internal/auth"
@ -20,6 +22,14 @@ func newManagerCenterTestRouter(walletClient *fakeWalletClient, hostClient *fake
return handler.Routes(auth.NewVerifier("secret"))
}
func newManagerCenterTestRouterWithGrowth(walletClient *fakeWalletClient, hostClient *fakeUserHostClient, profileClient *fakeUserProfileClient, growthClient *fakeGrowthLevelClient) http.Handler {
handler := NewHandlerWithClients(&fakeRoomClient{}, nil, nil, profileClient)
handler.SetWalletClient(walletClient)
handler.SetUserHostClient(hostClient)
handler.SetGrowthLevelClient(growthClient)
return handler.Routes(auth.NewVerifier("secret"))
}
func TestListManagerGrantResourcesChecksCapabilityAndFiltersWalletRequest(t *testing.T) {
walletClient := &fakeWalletClient{
listResourcesResp: &walletv1.ListResourcesResponse{
@ -164,24 +174,190 @@ func TestGrantManagerResourceUsesServerControlledGrantShape(t *testing.T) {
if profileClient.lastGet == nil || profileClient.lastGet.GetUserId() != 900001 {
t.Fatalf("target user lookup mismatch: %+v", profileClient.lastGet)
}
if req := walletClient.lastGrantResource; req == nil || req.GetCommandId() != "mgr-grant-1" || req.GetTargetUserId() != 900001 || req.GetResourceId() != 101 || req.GetQuantity() != 1 || req.GetDurationMs() != 0 || req.GetOperatorUserId() != 42 || req.GetGrantSource() != "manager_center" || req.GetReason() != "manager_center" {
if req := walletClient.lastGrantResource; req == nil || req.GetCommandId() != "mgr-grant-1" || req.GetTargetUserId() != 900001 || req.GetResourceId() != 101 || req.GetQuantity() != 1 || req.GetDurationMs() != 604800000 || req.GetOperatorUserId() != 42 || req.GetGrantSource() != "manager_center" || req.GetReason() != "manager_center" {
t.Fatalf("wallet GrantResource request mismatch: %+v", req)
}
}
func TestGrantManagerResourceRejectsClientComputedQuantityOrDuration(t *testing.T) {
func TestGrantManagerResourceAllowsConfiguredDuration(t *testing.T) {
walletClient := &fakeWalletClient{}
router := newManagerCenterTestRouter(walletClient, &fakeUserHostClient{}, &fakeUserProfileClient{})
body := []byte(`{"command_id":"mgr-grant-1","target_user_id":"900001","resource_id":"101","quantity":9}`)
body := []byte(`{"command_id":"mgr-grant-30","target_user_id":"900001","resource_id":"101","duration_ms":2592000000}`)
request := httptest.NewRequest(http.MethodPost, "/api/v1/manager-center/resource-grants", bytes.NewReader(body))
request.Header.Set("Authorization", "Bearer "+signGatewayToken(t, "secret", 42))
request.Header.Set("X-Request-ID", "req-manager-grant-shape")
request.Header.Set("X-Request-ID", "req-manager-grant-duration")
recorder := httptest.NewRecorder()
router.ServeHTTP(recorder, request)
assertEnvelope(t, recorder, http.StatusBadRequest, httpkit.CodeInvalidArgument, "req-manager-grant-shape")
if walletClient.lastGrantResource != nil {
t.Fatalf("client computed grant shape must not reach wallet-service: %+v", walletClient.lastGrantResource)
if recorder.Code != http.StatusOK {
t.Fatalf("status mismatch: got %d body=%s", recorder.Code, recorder.Body.String())
}
if req := walletClient.lastGrantResource; req == nil || req.GetDurationMs() != 2592000000 {
t.Fatalf("manager grant duration was not forwarded: %+v", req)
}
}
func TestGrantManagerResourceRejectsClientComputedQuantityOrInvalidDuration(t *testing.T) {
walletClient := &fakeWalletClient{}
router := newManagerCenterTestRouter(walletClient, &fakeUserHostClient{}, &fakeUserProfileClient{})
cases := []struct {
name string
body string
}{
{name: "quantity", body: `{"command_id":"mgr-grant-1","target_user_id":"900001","resource_id":"101","quantity":9}`},
{name: "duration", body: `{"command_id":"mgr-grant-1","target_user_id":"900001","resource_id":"101","duration_ms":86400000}`},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
walletClient.lastGrantResource = nil
request := httptest.NewRequest(http.MethodPost, "/api/v1/manager-center/resource-grants", bytes.NewReader([]byte(tc.body)))
request.Header.Set("Authorization", "Bearer "+signGatewayToken(t, "secret", 42))
request.Header.Set("X-Request-ID", "req-manager-grant-shape")
recorder := httptest.NewRecorder()
router.ServeHTTP(recorder, request)
assertEnvelope(t, recorder, http.StatusBadRequest, httpkit.CodeInvalidArgument, "req-manager-grant-shape")
if walletClient.lastGrantResource != nil {
t.Fatalf("client computed grant shape must not reach wallet-service: %+v", walletClient.lastGrantResource)
}
})
}
}
func TestGrantManagerResourceRejectsCrossCountryTarget(t *testing.T) {
walletClient := &fakeWalletClient{}
profileClient := &fakeUserProfileClient{
usersByID: map[int64]*userv1.User{
42: {UserId: 42, Status: userv1.UserStatus_USER_STATUS_ACTIVE, Country: "US"},
900001: {UserId: 900001, Status: userv1.UserStatus_USER_STATUS_ACTIVE, Country: "CA"},
},
}
router := newManagerCenterTestRouter(walletClient, &fakeUserHostClient{}, profileClient)
body := []byte(`{"command_id":"mgr-grant-cross","target_user_id":"900001","resource_id":"101"}`)
request := httptest.NewRequest(http.MethodPost, "/api/v1/manager-center/resource-grants", bytes.NewReader(body))
request.Header.Set("Authorization", "Bearer "+signGatewayToken(t, "secret", 42))
request.Header.Set("X-Request-ID", "req-manager-cross-country")
recorder := httptest.NewRecorder()
router.ServeHTTP(recorder, request)
assertEnvelope(t, recorder, http.StatusForbidden, httpkit.CodePermissionDenied, "req-manager-cross-country")
if walletClient.lastGrantResource != nil {
t.Fatalf("cross-country target must not reach wallet-service: %+v", walletClient.lastGrantResource)
}
}
func TestGrantManagerVIPLimitsToVipOneToThree(t *testing.T) {
walletClient := &fakeWalletClient{}
router := newManagerCenterTestRouter(walletClient, &fakeUserHostClient{}, &fakeUserProfileClient{})
body := []byte(`{"command_id":"mgr-vip-4","target_user_id":"900001","level":4}`)
request := httptest.NewRequest(http.MethodPost, "/api/v1/manager-center/vip-grants", bytes.NewReader(body))
request.Header.Set("Authorization", "Bearer "+signGatewayToken(t, "secret", 42))
request.Header.Set("X-Request-ID", "req-manager-vip4")
recorder := httptest.NewRecorder()
router.ServeHTTP(recorder, request)
assertEnvelope(t, recorder, http.StatusBadRequest, httpkit.CodeInvalidArgument, "req-manager-vip4")
if walletClient.lastGrantVip != nil {
t.Fatalf("VIP4 must not reach wallet-service: %+v", walletClient.lastGrantVip)
}
}
func TestGrantManagerVIPUsesManagerCenterGrantSource(t *testing.T) {
walletClient := &fakeWalletClient{}
router := newManagerCenterTestRouter(walletClient, &fakeUserHostClient{}, &fakeUserProfileClient{})
body := []byte(`{"command_id":"mgr-vip-3","target_user_id":"900001","level":3}`)
request := httptest.NewRequest(http.MethodPost, "/api/v1/manager-center/vip-grants", bytes.NewReader(body))
request.Header.Set("Authorization", "Bearer "+signGatewayToken(t, "secret", 42))
request.Header.Set("X-Request-ID", "req-manager-vip3")
recorder := httptest.NewRecorder()
router.ServeHTTP(recorder, request)
if recorder.Code != http.StatusOK {
t.Fatalf("status mismatch: got %d body=%s", recorder.Code, recorder.Body.String())
}
if req := walletClient.lastGrantVip; req == nil || req.GetCommandId() != "mgr-vip-3" || req.GetTargetUserId() != 900001 || req.GetLevel() != 3 || req.GetGrantSource() != "manager_center" || req.GetOperatorUserId() != 42 {
t.Fatalf("wallet GrantVip request mismatch: %+v", req)
}
}
func TestManagerBlockAndUnblockCallUserService(t *testing.T) {
profileClient := &fakeUserProfileClient{}
router := newManagerCenterTestRouter(&fakeWalletClient{}, &fakeUserHostClient{}, profileClient)
body := []byte(`{"command_id":"block-163006","target_user_id":"900001","blocked_until_ms":1780000000000}`)
request := httptest.NewRequest(http.MethodPost, "/api/v1/manager-center/blocks", bytes.NewReader(body))
request.Header.Set("Authorization", "Bearer "+signGatewayToken(t, "secret", 42))
request.Header.Set("X-Request-ID", "req-manager-block")
recorder := httptest.NewRecorder()
router.ServeHTTP(recorder, request)
if recorder.Code != http.StatusOK {
t.Fatalf("status mismatch: got %d body=%s", recorder.Code, recorder.Body.String())
}
if req := profileClient.lastCreateBlock; req == nil || req.GetCommandId() != "block-163006" || req.GetManagerUserId() != 42 || req.GetTargetUserId() != 900001 || req.GetBlockedUntilMs() != 1780000000000 {
t.Fatalf("CreateManagerUserBlock request mismatch: %+v", req)
}
unblockReq := httptest.NewRequest(http.MethodPost, "/api/v1/manager-center/blocks/block-1/unblock", nil)
unblockReq.Header.Set("Authorization", "Bearer "+signGatewayToken(t, "secret", 42))
unblockReq.Header.Set("X-Request-ID", "req-manager-unblock")
unblockRecorder := httptest.NewRecorder()
router.ServeHTTP(unblockRecorder, unblockReq)
if unblockRecorder.Code != http.StatusOK {
t.Fatalf("unblock status mismatch: got %d body=%s", unblockRecorder.Code, unblockRecorder.Body.String())
}
if req := profileClient.lastUnblock; req == nil || req.GetManagerUserId() != 42 || req.GetBlockId() != "block-1" {
t.Fatalf("UnblockManagerUser request mismatch: %+v", req)
}
}
func TestSetManagerUserLevelCallsGrowthService(t *testing.T) {
growthClient := &fakeGrowthLevelClient{}
router := newManagerCenterTestRouterWithGrowth(&fakeWalletClient{}, &fakeUserHostClient{}, &fakeUserProfileClient{}, growthClient)
body := []byte(`{"command_id":"level-wealth-5","target_user_id":"900001","track":"wealth","level":5}`)
request := httptest.NewRequest(http.MethodPost, "/api/v1/manager-center/levels", bytes.NewReader(body))
request.Header.Set("Authorization", "Bearer "+signGatewayToken(t, "secret", 42))
request.Header.Set("X-Request-ID", "req-manager-level")
recorder := httptest.NewRecorder()
router.ServeHTTP(recorder, request)
if recorder.Code != http.StatusOK {
t.Fatalf("status mismatch: got %d body=%s", recorder.Code, recorder.Body.String())
}
if req := growthClient.lastSet; req == nil || req.GetCommandId() != "level-wealth-5" || req.GetUserId() != 900001 || req.GetTrack() != "wealth" || req.GetLevel() != 5 || req.GetOperatorUserId() != 42 {
t.Fatalf("SetUserLevel request mismatch: %+v", req)
}
}
type fakeGrowthLevelClient struct {
lastSet *activityv1.SetUserLevelRequest
}
func (f *fakeGrowthLevelClient) GetMyLevelOverview(context.Context, *activityv1.GetMyLevelOverviewRequest) (*activityv1.GetMyLevelOverviewResponse, error) {
return &activityv1.GetMyLevelOverviewResponse{}, nil
}
func (f *fakeGrowthLevelClient) GetLevelTrack(context.Context, *activityv1.GetLevelTrackRequest) (*activityv1.GetLevelTrackResponse, error) {
return &activityv1.GetLevelTrackResponse{}, nil
}
func (f *fakeGrowthLevelClient) BatchGetUserLevelDisplayProfiles(context.Context, *activityv1.BatchGetUserLevelDisplayProfilesRequest) (*activityv1.BatchGetUserLevelDisplayProfilesResponse, error) {
return &activityv1.BatchGetUserLevelDisplayProfilesResponse{}, nil
}
func (f *fakeGrowthLevelClient) ListLevelRewards(context.Context, *activityv1.ListLevelRewardsRequest) (*activityv1.ListLevelRewardsResponse, error) {
return &activityv1.ListLevelRewardsResponse{}, nil
}
func (f *fakeGrowthLevelClient) SetUserLevel(_ context.Context, req *activityv1.SetUserLevelRequest) (*activityv1.SetUserLevelResponse, error) {
f.lastSet = req
return &activityv1.SetUserLevelResponse{EventId: "event-1", Status: "applied", Track: req.GetTrack(), NewLevel: req.GetLevel(), TotalValue: 1000}, nil
}

View File

@ -11,12 +11,14 @@ type Handler struct {
walletClient client.WalletClient
userHostClient client.UserHostClient
userProfileClient client.UserProfileClient
growthLevelClient client.GrowthLevelClient
}
type Config struct {
WalletClient client.WalletClient
UserHostClient client.UserHostClient
UserProfileClient client.UserProfileClient
GrowthLevelClient client.GrowthLevelClient
}
func New(config Config) *Handler {
@ -24,6 +26,7 @@ func New(config Config) *Handler {
walletClient: config.WalletClient,
userHostClient: config.UserHostClient,
userProfileClient: config.UserProfileClient,
growthLevelClient: config.GrowthLevelClient,
}
}
@ -32,5 +35,13 @@ func (h *Handler) ManagerHandlers() httproutes.ManagerHandlers {
ListManagerGrantResources: h.listManagerGrantResources,
GrantManagerResource: h.grantManagerResource,
LookupBusinessUsers: h.lookupBusinessUsers,
GetManagerOverview: h.getManagerOverview,
SearchManagerUsers: h.searchManagerUsers,
GrantManagerVIP: h.grantManagerVIP,
ManagerBlocks: h.managerBlocks,
ListManagerBlocks: h.listManagerBlocks,
CreateManagerBlock: h.createManagerBlock,
UnblockManagerBlock: h.unblockManagerBlock,
SetManagerUserLevel: h.setManagerUserLevel,
}
}

View File

@ -8,6 +8,7 @@ import (
"strconv"
"strings"
activityv1 "hyapp.local/api/proto/activity/v1"
userv1 "hyapp.local/api/proto/user/v1"
walletv1 "hyapp.local/api/proto/wallet/v1"
"hyapp/pkg/appcode"
@ -15,7 +16,10 @@ import (
"hyapp/services/gateway-service/internal/auth"
)
const managerCenterCapability = "manager_center"
const managerResourceGrantCapability = "manager_resource_grant"
const managerGrantDayMS int64 = 24 * 60 * 60 * 1000
const managerGrantDefaultDurationMS int64 = 7 * managerGrantDayMS
type managerGrantResourceData struct {
ResourceID string `json:"resource_id"`
@ -39,6 +43,25 @@ type businessUserLookupData struct {
RegionName string `json:"region_name,omitempty"`
}
type managerUserData struct {
UserID string `json:"user_id"`
DisplayUserID string `json:"display_user_id"`
Username string `json:"username"`
Avatar string `json:"avatar"`
Status string `json:"status"`
Country string `json:"country"`
RegionID int64 `json:"region_id,omitempty"`
RegionCode string `json:"region_code,omitempty"`
RegionName string `json:"region_name,omitempty"`
Levels map[string]managerLevelData `json:"levels"`
}
type managerLevelData struct {
Track string `json:"track"`
Level int32 `json:"level"`
TierID string `json:"tier_id,omitempty"`
}
type managerResourceGrantData struct {
GrantID string `json:"grant_id"`
CommandID string `json:"command_id"`
@ -50,11 +73,124 @@ type managerResourceGrantData struct {
CreatedAtMS int64 `json:"created_at_ms"`
}
func (h *Handler) listManagerGrantResources(writer http.ResponseWriter, request *http.Request) {
if h.walletClient == nil || h.userHostClient == nil {
type managerBlockData struct {
BlockID string `json:"block_id"`
TargetUserID string `json:"target_user_id"`
TargetDisplayUserID string `json:"target_display_user_id"`
TargetUsername string `json:"target_username"`
TargetAvatar string `json:"target_avatar"`
Country string `json:"country"`
BlockedUntilMS int64 `json:"blocked_until_ms"`
Status string `json:"status"`
Reason string `json:"reason"`
CreatedAtMS int64 `json:"created_at_ms"`
UpdatedAtMS int64 `json:"updated_at_ms"`
}
type managerLevelUpdateData struct {
EventID string `json:"event_id"`
Status string `json:"status"`
Track string `json:"track"`
PreviousLevel int32 `json:"previous_level"`
NewLevel int32 `json:"new_level"`
TotalValue int64 `json:"total_value"`
RewardJobCount int64 `json:"reward_job_count"`
ServerTimeMS int64 `json:"server_time_ms"`
}
func (h *Handler) getManagerOverview(writer http.ResponseWriter, request *http.Request) {
if h.userHostClient == nil || h.userProfileClient == nil {
httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
return
}
// 经理中心首页只信 user-service 的角色事实;前端传 is_manager 或 identity 都不能绕过这里。
actor, ok := h.requireManagerCenterActor(writer, request)
if !ok {
return
}
httpkit.WriteOK(writer, request, map[string]any{
"is_manager": true,
"user_id": httpkit.UserIDString(actor.GetUserId()),
"display_user_id": actor.GetDisplayUserId(),
"username": actor.GetUsername(),
"avatar": actor.GetAvatar(),
"country": actor.GetCountry(),
"region_id": actor.GetRegionId(),
"region_code": actor.GetRegionCode(),
"region_name": actor.GetRegionName(),
})
}
func (h *Handler) searchManagerUsers(writer http.ResponseWriter, request *http.Request) {
if h.userProfileClient == nil || h.userHostClient == nil {
httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
return
}
// 搜索先走已有 business lookup 保持短号/昵称解析一致,再用 BatchGetUsers 拿国家和等级投影做最终过滤。
actor, ok := h.requireManagerCenterActor(writer, request)
if !ok {
return
}
pageSize, ok := httpkit.PositiveInt32Query(request, "page_size", 20)
if !ok {
httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
return
}
if pageSize > 20 {
pageSize = 20
}
lookupResp, err := h.userProfileClient.BusinessUserLookup(request.Context(), &userv1.BusinessUserLookupRequest{
Meta: httpkit.UserMeta(request, ""),
ActorUserId: actor.GetUserId(),
Scene: "manager_resource_grant",
Keyword: strings.TrimSpace(request.URL.Query().Get("keyword")),
PageSize: pageSize,
})
if err != nil {
httpkit.WriteRPCError(writer, request, err)
return
}
userIDs := make([]int64, 0, len(lookupResp.GetUsers()))
seen := map[int64]bool{}
for _, item := range lookupResp.GetUsers() {
if id := item.GetUserId(); id > 0 && !seen[id] {
seen[id] = true
userIDs = append(userIDs, id)
}
}
if len(userIDs) == 0 {
httpkit.WriteOK(writer, request, map[string]any{"items": []managerUserData{}, "total": 0, "page_size": pageSize})
return
}
usersResp, err := h.userProfileClient.BatchGetUsers(request.Context(), &userv1.BatchGetUsersRequest{
Meta: httpkit.UserMeta(request, ""),
UserIds: userIDs,
})
if err != nil {
httpkit.WriteRPCError(writer, request, err)
return
}
levels := h.managerLevelsByUser(request, userIDs)
items := make([]managerUserData, 0, len(userIDs))
for _, userID := range userIDs {
user := usersResp.GetUsers()[userID]
// 同国家限制必须放在 gateway 写入口和读入口都做,避免 H5 通过短号枚举跨国家用户。
if user == nil || user.GetStatus() != userv1.UserStatus_USER_STATUS_ACTIVE || !sameCountry(actor.GetCountry(), user.GetCountry()) {
continue
}
items = append(items, managerUserFromProto(user, levels[userID]))
}
httpkit.WriteOK(writer, request, map[string]any{"items": items, "total": len(items), "page_size": pageSize})
}
func (h *Handler) listManagerGrantResources(writer http.ResponseWriter, request *http.Request) {
if h.walletClient == nil || h.userHostClient == nil || h.userProfileClient == nil {
httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
return
}
if _, ok := h.requireManagerCenterActor(writer, request); !ok {
return
}
if !h.requireManagerResourceGrantCapability(writer, request) {
return
}
@ -134,7 +270,12 @@ func (h *Handler) grantManagerResource(writer http.ResponseWriter, request *http
if !httpkit.Decode(writer, request, &body) {
return
}
if body.Quantity != nil || body.DurationMS != nil || body.DurationMSAlt != nil {
if body.Quantity != nil {
httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
return
}
durationMS, durationOK := managerGrantDurationMS(body.DurationMS, body.DurationMSAlt)
if !durationOK {
httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
return
}
@ -151,16 +292,8 @@ func (h *Handler) grantManagerResource(writer http.ResponseWriter, request *http
httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
return
}
targetResp, err := h.userProfileClient.GetUser(request.Context(), &userv1.GetUserRequest{
Meta: httpkit.UserMeta(request, ""),
UserId: targetUserID,
})
if err != nil {
httpkit.WriteRPCError(writer, request, err)
return
}
if target := targetResp.GetUser(); target == nil || target.GetStatus() != userv1.UserStatus_USER_STATUS_ACTIVE {
httpkit.WriteError(writer, request, http.StatusNotFound, httpkit.CodeNotFound, "not found")
// 赠送资源前先确认目标用户仍然 active 且与经理同国家wallet-service 还会在事务内复验资源白名单。
if _, ok := h.requireSameCountryTarget(writer, request, targetUserID); !ok {
return
}
reason := strings.TrimSpace(body.Reason)
@ -169,12 +302,13 @@ func (h *Handler) grantManagerResource(writer http.ResponseWriter, request *http
}
actorUserID := auth.UserIDFromContext(request.Context())
resp, err := h.walletClient.GrantResource(request.Context(), &walletv1.GrantResourceRequest{
CommandId: commandID,
AppCode: appcode.FromContext(request.Context()),
TargetUserId: targetUserID,
ResourceId: resourceID,
Quantity: 1,
DurationMs: 0,
CommandId: commandID,
AppCode: appcode.FromContext(request.Context()),
TargetUserId: targetUserID,
ResourceId: resourceID,
Quantity: 1,
// 经理中心只能赠送产品允许的三个限时档位;数量、来源和操作人仍由网关固定,避免 H5 自行组装账务形状。
DurationMs: durationMS,
Reason: reason,
OperatorUserId: actorUserID,
GrantSource: "manager_center",
@ -199,6 +333,242 @@ func (h *Handler) grantManagerResource(writer http.ResponseWriter, request *http
httpkit.WriteOK(writer, request, grant)
}
func (h *Handler) grantManagerVIP(writer http.ResponseWriter, request *http.Request) {
if h.walletClient == nil || h.userHostClient == nil || h.userProfileClient == nil {
httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
return
}
var body struct {
CommandID string `json:"command_id"`
CommandIDAlt string `json:"commandId"`
TargetUserID json.RawMessage `json:"target_user_id"`
TargetUserIDAlt json.RawMessage `json:"targetUserId"`
Level int32 `json:"level"`
Reason string `json:"reason"`
}
if !httpkit.Decode(writer, request, &body) {
return
}
commandID := strings.TrimSpace(body.CommandID)
if commandID == "" {
commandID = strings.TrimSpace(body.CommandIDAlt)
}
targetUserID, targetOK := rawInt64(body.TargetUserID, body.TargetUserIDAlt)
if commandID == "" || !targetOK || targetUserID <= 0 || body.Level < 1 || body.Level > 3 {
httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
return
}
// 经理中心复用后台 VIP 发放账务能力,但 source 固定为 manager_center钱包侧据此保留统一 IM/outbox 行为。
if _, ok := h.requireSameCountryTarget(writer, request, targetUserID); !ok {
return
}
reason := strings.TrimSpace(body.Reason)
if reason == "" {
reason = "manager_center"
}
resp, err := h.walletClient.GrantVip(request.Context(), &walletv1.GrantVipRequest{
CommandId: commandID,
AppCode: appcode.FromContext(request.Context()),
TargetUserId: targetUserID,
Level: body.Level,
GrantSource: "manager_center",
OperatorUserId: auth.UserIDFromContext(request.Context()),
Reason: reason,
})
if err != nil {
httpkit.WriteRPCError(writer, request, err)
return
}
httpkit.WriteOK(writer, request, map[string]any{
"transaction_id": resp.GetTransactionId(),
"level": resp.GetVip().GetLevel(),
"active": resp.GetVip().GetActive(),
"expires_at_ms": resp.GetVip().GetExpiresAtMs(),
"server_time_ms": resp.GetServerTimeMs(),
})
}
func (h *Handler) managerBlocks(writer http.ResponseWriter, request *http.Request) {
switch request.Method {
case http.MethodGet:
h.listManagerBlocks(writer, request)
case http.MethodPost:
h.createManagerBlock(writer, request)
default:
httpkit.WriteError(writer, request, http.StatusMethodNotAllowed, httpkit.CodeInvalidArgument, "method not allowed")
}
}
func (h *Handler) listManagerBlocks(writer http.ResponseWriter, request *http.Request) {
if h.userProfileClient == nil || h.userHostClient == nil {
httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
return
}
actor, ok := h.requireManagerCenterActor(writer, request)
if !ok {
return
}
pageSize, ok := httpkit.PositiveInt32Query(request, "page_size", 50)
if !ok {
httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
return
}
if pageSize > 100 {
pageSize = 100
}
status := strings.TrimSpace(request.URL.Query().Get("status"))
resp, err := h.userProfileClient.ListManagerUserBlocks(request.Context(), &userv1.ListManagerUserBlocksRequest{
Meta: httpkit.UserMeta(request, ""),
ManagerUserId: actor.GetUserId(),
Status: status,
PageSize: pageSize,
})
if err != nil {
httpkit.WriteRPCError(writer, request, err)
return
}
items := make([]managerBlockData, 0, len(resp.GetBlocks()))
for _, block := range resp.GetBlocks() {
items = append(items, managerBlockFromProto(block))
}
httpkit.WriteOK(writer, request, map[string]any{"items": items, "total": len(items), "page_size": pageSize})
}
func (h *Handler) createManagerBlock(writer http.ResponseWriter, request *http.Request) {
if h.userProfileClient == nil || h.userHostClient == nil {
httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
return
}
var body struct {
CommandID string `json:"command_id"`
CommandIDAlt string `json:"commandId"`
TargetUserID json.RawMessage `json:"target_user_id"`
TargetUserIDAlt json.RawMessage `json:"targetUserId"`
BlockedUntilMS int64 `json:"blocked_until_ms"`
BlockedUntilMSAlt int64 `json:"blockedUntilMs"`
Reason string `json:"reason"`
}
if !httpkit.Decode(writer, request, &body) {
return
}
actor, ok := h.requireManagerCenterActor(writer, request)
if !ok {
return
}
commandID := strings.TrimSpace(body.CommandID)
if commandID == "" {
commandID = strings.TrimSpace(body.CommandIDAlt)
}
targetUserID, targetOK := rawInt64(body.TargetUserID, body.TargetUserIDAlt)
blockedUntilMS := body.BlockedUntilMS
if blockedUntilMS <= 0 {
blockedUntilMS = body.BlockedUntilMSAlt
}
if commandID == "" || !targetOK || targetUserID <= 0 || blockedUntilMS <= 0 {
httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
return
}
// user-service 会再次读取双方用户并校验同国家,这里保留 gateway 早拒绝以减少无效封禁事务。
if _, ok := h.requireSameCountryTargetWithActor(writer, request, actor, targetUserID); !ok {
return
}
resp, err := h.userProfileClient.CreateManagerUserBlock(request.Context(), &userv1.CreateManagerUserBlockRequest{
Meta: httpkit.UserMeta(request, ""),
CommandId: commandID,
ManagerUserId: actor.GetUserId(),
TargetUserId: targetUserID,
BlockedUntilMs: blockedUntilMS,
Reason: strings.TrimSpace(body.Reason),
})
if err != nil {
httpkit.WriteRPCError(writer, request, err)
return
}
httpkit.WriteOK(writer, request, managerBlockFromProto(resp.GetBlock()))
}
func (h *Handler) unblockManagerBlock(writer http.ResponseWriter, request *http.Request) {
if h.userProfileClient == nil || h.userHostClient == nil {
httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
return
}
actor, ok := h.requireManagerCenterActor(writer, request)
if !ok {
return
}
var body struct {
Reason string `json:"reason"`
}
if request.Body != nil && request.ContentLength != 0 {
if !httpkit.Decode(writer, request, &body) {
return
}
}
blockID := strings.TrimSpace(request.PathValue("block_id"))
if blockID == "" {
httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
return
}
resp, err := h.userProfileClient.UnblockManagerUser(request.Context(), &userv1.UnblockManagerUserRequest{
Meta: httpkit.UserMeta(request, ""),
ManagerUserId: actor.GetUserId(),
BlockId: blockID,
Reason: strings.TrimSpace(body.Reason),
})
if err != nil {
httpkit.WriteRPCError(writer, request, err)
return
}
httpkit.WriteOK(writer, request, managerBlockFromProto(resp.GetBlock()))
}
func (h *Handler) setManagerUserLevel(writer http.ResponseWriter, request *http.Request) {
if h.growthLevelClient == nil || h.userProfileClient == nil || h.userHostClient == nil {
httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
return
}
var body struct {
CommandID string `json:"command_id"`
CommandIDAlt string `json:"commandId"`
TargetUserID json.RawMessage `json:"target_user_id"`
TargetUserIDAlt json.RawMessage `json:"targetUserId"`
Track string `json:"track"`
Level int32 `json:"level"`
Reason string `json:"reason"`
}
if !httpkit.Decode(writer, request, &body) {
return
}
commandID := strings.TrimSpace(body.CommandID)
if commandID == "" {
commandID = strings.TrimSpace(body.CommandIDAlt)
}
targetUserID, targetOK := rawInt64(body.TargetUserID, body.TargetUserIDAlt)
track := strings.ToLower(strings.TrimSpace(body.Track))
if commandID == "" || !targetOK || targetUserID <= 0 || !validManagerLevelTrack(track) || body.Level < 1 || body.Level > 11 {
httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
return
}
// 等级设置会把目标轨道 total_value 直接改到对应规则阈值,并由 activity-service 补齐 1..目标等级奖励。
if _, ok := h.requireSameCountryTarget(writer, request, targetUserID); !ok {
return
}
resp, err := h.growthLevelClient.SetUserLevel(request.Context(), &activityv1.SetUserLevelRequest{
Meta: httpkit.ActivityMeta(request),
CommandId: commandID,
UserId: targetUserID,
Track: track,
Level: body.Level,
OperatorUserId: auth.UserIDFromContext(request.Context()),
Reason: strings.TrimSpace(body.Reason),
})
if err != nil {
httpkit.WriteRPCError(writer, request, err)
return
}
httpkit.WriteOK(writer, request, managerLevelUpdateFromProto(resp))
}
func (h *Handler) requireManagerResourceGrantCapability(writer http.ResponseWriter, request *http.Request) bool {
resp, err := h.userHostClient.CheckBusinessCapability(request.Context(), &userv1.CheckBusinessCapabilityRequest{
Meta: httpkit.UserMeta(request, ""),
@ -216,6 +586,76 @@ func (h *Handler) requireManagerResourceGrantCapability(writer http.ResponseWrit
return true
}
func (h *Handler) requireManagerCenterActor(writer http.ResponseWriter, request *http.Request) (*userv1.User, bool) {
if !h.requireManagerCapability(writer, request, managerCenterCapability) {
return nil, false
}
resp, err := h.userProfileClient.GetUser(request.Context(), &userv1.GetUserRequest{
Meta: httpkit.UserMeta(request, ""),
UserId: auth.UserIDFromContext(request.Context()),
})
if err != nil {
httpkit.WriteRPCError(writer, request, err)
return nil, false
}
actor := resp.GetUser()
if actor == nil || actor.GetStatus() != userv1.UserStatus_USER_STATUS_ACTIVE {
httpkit.WriteError(writer, request, http.StatusForbidden, httpkit.CodePermissionDenied, "permission denied")
return nil, false
}
return actor, true
}
func (h *Handler) requireManagerCapability(writer http.ResponseWriter, request *http.Request, capability string) bool {
resp, err := h.userHostClient.CheckBusinessCapability(request.Context(), &userv1.CheckBusinessCapabilityRequest{
Meta: httpkit.UserMeta(request, ""),
ActorUserId: auth.UserIDFromContext(request.Context()),
Capability: capability,
})
if err != nil {
httpkit.WriteRPCError(writer, request, err)
return false
}
if !resp.GetAllowed() {
httpkit.WriteError(writer, request, http.StatusForbidden, httpkit.CodePermissionDenied, "permission denied")
return false
}
return true
}
func (h *Handler) requireSameCountryTarget(writer http.ResponseWriter, request *http.Request, targetUserID int64) (*userv1.User, bool) {
actor, ok := h.requireManagerCenterActor(writer, request)
if !ok {
return nil, false
}
return h.requireSameCountryTargetWithActor(writer, request, actor, targetUserID)
}
func (h *Handler) requireSameCountryTargetWithActor(writer http.ResponseWriter, request *http.Request, actor *userv1.User, targetUserID int64) (*userv1.User, bool) {
targetResp, err := h.userProfileClient.GetUser(request.Context(), &userv1.GetUserRequest{
Meta: httpkit.UserMeta(request, ""),
UserId: targetUserID,
})
if err != nil {
httpkit.WriteRPCError(writer, request, err)
return nil, false
}
target := targetResp.GetUser()
if target == nil || target.GetStatus() != userv1.UserStatus_USER_STATUS_ACTIVE {
httpkit.WriteError(writer, request, http.StatusNotFound, httpkit.CodeNotFound, "not found")
return nil, false
}
if !sameCountry(actor.GetCountry(), target.GetCountry()) {
httpkit.WriteError(writer, request, http.StatusForbidden, httpkit.CodePermissionDenied, "permission denied")
return nil, false
}
return target, true
}
func sameCountry(left string, right string) bool {
return strings.EqualFold(strings.TrimSpace(left), strings.TrimSpace(right)) && strings.TrimSpace(left) != ""
}
func managerGrantResourceFromProto(resource *walletv1.Resource) managerGrantResourceData {
if resource == nil {
return managerGrantResourceData{}
@ -232,6 +672,140 @@ func managerGrantResourceFromProto(resource *walletv1.Resource) managerGrantReso
}
}
func managerUserFromProto(user *userv1.User, levels map[string]managerLevelData) managerUserData {
if user == nil {
return managerUserData{Levels: managerDefaultLevels()}
}
if levels == nil {
levels = managerDefaultLevels()
}
return managerUserData{
UserID: httpkit.UserIDString(user.GetUserId()),
DisplayUserID: user.GetDisplayUserId(),
Username: user.GetUsername(),
Avatar: user.GetAvatar(),
Status: userStatusString(user.GetStatus()),
Country: user.GetCountry(),
RegionID: user.GetRegionId(),
RegionCode: user.GetRegionCode(),
RegionName: user.GetRegionName(),
Levels: levels,
}
}
func (h *Handler) managerLevelsByUser(request *http.Request, userIDs []int64) map[int64]map[string]managerLevelData {
result := map[int64]map[string]managerLevelData{}
for _, userID := range userIDs {
result[userID] = managerDefaultLevels()
}
if h.growthLevelClient == nil || len(userIDs) == 0 {
return result
}
resp, err := h.growthLevelClient.BatchGetUserLevelDisplayProfiles(request.Context(), &activityv1.BatchGetUserLevelDisplayProfilesRequest{
Meta: httpkit.ActivityMeta(request),
UserIds: userIDs,
})
if err != nil {
return result
}
for _, profile := range resp.GetProfiles() {
if profile.GetUserId() <= 0 {
continue
}
result[profile.GetUserId()] = map[string]managerLevelData{
"wealth": managerLevelFromProto(profile.GetWealth()),
"charm": managerLevelFromProto(profile.GetCharm()),
"game": managerLevelFromProto(profile.GetGame()),
}
}
return result
}
func managerDefaultLevels() map[string]managerLevelData {
return map[string]managerLevelData{
"wealth": {Track: "wealth"},
"charm": {Track: "charm"},
"game": {Track: "game"},
}
}
func managerLevelFromProto(track *activityv1.LevelDisplayTrackProfile) managerLevelData {
if track == nil {
return managerLevelData{}
}
return managerLevelData{
Track: track.GetTrack(),
Level: track.GetLevel(),
TierID: httpkit.UserIDString(track.GetTierId()),
}
}
func managerBlockFromProto(block *userv1.ManagerUserBlock) managerBlockData {
if block == nil {
return managerBlockData{}
}
return managerBlockData{
BlockID: block.GetBlockId(),
TargetUserID: httpkit.UserIDString(block.GetTargetUserId()),
TargetDisplayUserID: block.GetTargetDisplayUserId(),
TargetUsername: block.GetTargetUsername(),
TargetAvatar: block.GetTargetAvatar(),
Country: block.GetCountry(),
BlockedUntilMS: block.GetBlockedUntilMs(),
Status: block.GetStatus(),
Reason: block.GetReason(),
CreatedAtMS: block.GetCreatedAtMs(),
UpdatedAtMS: block.GetUpdatedAtMs(),
}
}
func managerLevelUpdateFromProto(resp *activityv1.SetUserLevelResponse) managerLevelUpdateData {
if resp == nil {
return managerLevelUpdateData{}
}
return managerLevelUpdateData{
EventID: resp.GetEventId(),
Status: resp.GetStatus(),
Track: resp.GetTrack(),
PreviousLevel: resp.GetPreviousLevel(),
NewLevel: resp.GetNewLevel(),
TotalValue: resp.GetTotalValue(),
RewardJobCount: resp.GetRewardJobCount(),
ServerTimeMS: resp.GetServerTimeMs(),
}
}
func validManagerLevelTrack(track string) bool {
switch track {
case "wealth", "charm", "game":
return true
default:
return false
}
}
func managerGrantDurationMS(primary *int64, alternate *int64) (int64, bool) {
// 旧 H5 还没有 duration 字段,默认 7 天保证入口兼容;新 H5 只能在 7/30/90 天之间选择。
if primary == nil && alternate == nil {
return managerGrantDefaultDurationMS, true
}
if primary != nil && alternate != nil && *primary != *alternate {
return 0, false
}
value := int64(0)
if primary != nil {
value = *primary
} else {
value = *alternate
}
switch value {
case 7 * managerGrantDayMS, 30 * managerGrantDayMS, 90 * managerGrantDayMS:
return value, true
default:
return 0, false
}
}
func businessUserLookupFromProto(user *userv1.BusinessUserLookupItem) businessUserLookupData {
if user == nil {
return businessUserLookupData{}

View File

@ -311,13 +311,20 @@ type fakeUserProfileClient struct {
lastComplete *userv1.CompleteOnboardingRequest
lastUpdate *userv1.UpdateUserProfileRequest
lastCountry *userv1.ChangeUserCountryRequest
lastCreateBlock *userv1.CreateManagerUserBlockRequest
lastListBlocks *userv1.ListManagerUserBlocksRequest
lastUnblock *userv1.UnblockManagerUserRequest
getErr error
statsResp *userv1.GetMyProfileStatsResponse
businessLookupResp *userv1.BusinessUserLookupResponse
businessLookupErr error
blocksResp *userv1.ListManagerUserBlocksResponse
createBlockResp *userv1.CreateManagerUserBlockResponse
unblockResp *userv1.UnblockManagerUserResponse
statsErr error
completeErr error
countryErr error
blockErr error
regionID int64
regionByUserID map[int64]int64
usersByID map[int64]*userv1.User
@ -389,6 +396,12 @@ type fakeUserCountryQueryClient struct {
blockedErr error
}
type fakeUserRegionClient struct {
last *userv1.GetRegionRequest
resp *userv1.RegionResponse
err error
}
type fakeUserHostClient struct {
lastSearchAgencies *userv1.SearchAgenciesRequest
searchAgenciesResp *userv1.SearchAgenciesResponse
@ -481,6 +494,8 @@ type fakeWalletClient struct {
vipErr error
lastPurchaseVip *walletv1.PurchaseVipRequest
purchaseVipResp *walletv1.PurchaseVipResponse
lastGrantVip *walletv1.GrantVipRequest
grantVipResp *walletv1.GrantVipResponse
lastTransfer *walletv1.TransferCoinFromSellerRequest
transferResp *walletv1.TransferCoinFromSellerResponse
transferErr error
@ -710,6 +725,7 @@ func (f *fakeUserProfileClient) BatchGetUsers(_ context.Context, req *userv1.Bat
}
users[userID] = &userv1.User{
UserId: userID,
Status: userv1.UserStatus_USER_STATUS_ACTIVE,
DisplayUserId: strconv.FormatInt(100000+userID, 10),
Username: "user-" + strconv.FormatInt(userID, 10),
Gender: "female",
@ -784,6 +800,53 @@ func (f *fakeUserProfileClient) ChangeUserCountry(_ context.Context, req *userv1
}, NextChangeAllowedAtMs: 3600000, OldRegionId: 1001, NewRegionId: 2002, RegionChanged: true}, nil
}
func (f *fakeUserProfileClient) CreateManagerUserBlock(_ context.Context, req *userv1.CreateManagerUserBlockRequest) (*userv1.CreateManagerUserBlockResponse, error) {
f.lastCreateBlock = req
if f.blockErr != nil {
return nil, f.blockErr
}
if f.createBlockResp != nil {
return f.createBlockResp, nil
}
return &userv1.CreateManagerUserBlockResponse{Block: &userv1.ManagerUserBlock{
BlockId: "block-1",
ManagerUserId: req.GetManagerUserId(),
TargetUserId: req.GetTargetUserId(),
TargetDisplayUserId: "163006",
TargetUsername: "blocked",
Country: "US",
BlockedUntilMs: req.GetBlockedUntilMs(),
Status: "active",
Reason: req.GetReason(),
}}, nil
}
func (f *fakeUserProfileClient) ListManagerUserBlocks(_ context.Context, req *userv1.ListManagerUserBlocksRequest) (*userv1.ListManagerUserBlocksResponse, error) {
f.lastListBlocks = req
if f.blockErr != nil {
return nil, f.blockErr
}
if f.blocksResp != nil {
return f.blocksResp, nil
}
return &userv1.ListManagerUserBlocksResponse{}, nil
}
func (f *fakeUserProfileClient) UnblockManagerUser(_ context.Context, req *userv1.UnblockManagerUserRequest) (*userv1.UnblockManagerUserResponse, error) {
f.lastUnblock = req
if f.blockErr != nil {
return nil, f.blockErr
}
if f.unblockResp != nil {
return f.unblockResp, nil
}
return &userv1.UnblockManagerUserResponse{Block: &userv1.ManagerUserBlock{
BlockId: req.GetBlockId(),
ManagerUserId: req.GetManagerUserId(),
Status: "released",
}}, nil
}
func (f *fakeUserIdentityClient) GetUserIdentity(_ context.Context, req *userv1.GetUserIdentityRequest) (*userv1.GetUserIdentityResponse, error) {
return &userv1.GetUserIdentityResponse{Identity: &userv1.UserIdentity{
UserId: req.GetUserId(),
@ -974,6 +1037,18 @@ func (f *fakeUserCountryQueryClient) ListLoginRiskBlockedCountries(_ context.Con
return &userv1.ListLoginRiskBlockedCountriesResponse{}, nil
}
func (f *fakeUserRegionClient) GetRegion(_ context.Context, req *userv1.GetRegionRequest) (*userv1.RegionResponse, error) {
f.last = req
if f.err != nil {
return nil, f.err
}
if f.resp != nil {
return f.resp, nil
}
return &userv1.RegionResponse{Region: &userv1.Region{RegionId: req.GetRegionId(), Countries: []string{"US"}}}, nil
}
func (f *fakeMessageInboxClient) ListMessageTabs(_ context.Context, req *activityv1.ListMessageTabsRequest) (*activityv1.ListMessageTabsResponse, error) {
f.lastTabs = req
if f.err != nil {
@ -1468,6 +1543,21 @@ func (f *fakeWalletClient) PurchaseVip(_ context.Context, req *walletv1.Purchase
return &walletv1.PurchaseVipResponse{}, nil
}
func (f *fakeWalletClient) GrantVip(_ context.Context, req *walletv1.GrantVipRequest) (*walletv1.GrantVipResponse, error) {
f.lastGrantVip = req
if f.vipErr != nil {
return nil, f.vipErr
}
if f.grantVipResp != nil {
return f.grantVipResp, nil
}
return &walletv1.GrantVipResponse{
TransactionId: "vip-grant-tx",
Vip: &walletv1.UserVip{UserId: req.GetTargetUserId(), Level: req.GetLevel(), Active: true},
ServerTimeMs: 1_780_000_000_000,
}, nil
}
func (f *fakeWalletClient) TransferCoinFromSeller(_ context.Context, req *walletv1.TransferCoinFromSellerRequest) (*walletv1.TransferCoinFromSellerResponse, error) {
f.lastTransfer = req
if f.transferErr != nil {
@ -1773,6 +1863,9 @@ func TestRoutesWriteUnifiedEnvelopeAndGenerateRequestID(t *testing.T) {
if profileClient.lastGet == nil || profileClient.lastGet.GetUserId() != 42 || client.lastCreate.GetVisibleRegionId() != 1001 {
t.Fatalf("CreateRoom must derive visible_region_id from user-service: profile=%+v create=%+v", profileClient.lastGet, client.lastCreate)
}
if client.lastCreate.GetOwnerCountryCode() != "US" {
t.Fatalf("CreateRoom must derive owner_country_code from user-service: %+v", client.lastCreate)
}
if client.lastCreate.GetRoomName() != "Lobby" || client.lastCreate.GetRoomAvatar() != "https://cdn.example.com/room.png" || client.lastCreate.GetRoomDescription() != "welcome" {
t.Fatalf("CreateRoom should trim and forward room profile fields: %+v", client.lastCreate)
}
@ -2267,16 +2360,29 @@ func TestJoinRoomReturnsInitialDataWithIMGroupRTCTokenAndProfiles(t *testing.T)
func TestListRoomsUsesUserRegionFromUserService(t *testing.T) {
profileClient := &fakeUserProfileClient{regionID: 1001}
regionClient := &fakeUserRegionClient{resp: &userv1.RegionResponse{Region: &userv1.Region{
RegionId: 1001,
Countries: []string{"AE", "US", "TR"},
}}}
countryClient := &fakeUserCountryQueryClient{resp: &userv1.ListRegistrationCountriesResponse{
Countries: []*userv1.Country{
{CountryId: 1, CountryCode: "AE", CountryName: "United Arab Emirates", CountryDisplayName: "UAE", Flag: "🇦🇪"},
{CountryId: 2, CountryCode: "US", CountryName: "United States", CountryDisplayName: "US", Flag: "🇺🇸"},
{CountryId: 3, CountryCode: "TR", CountryName: "Türkiye", CountryDisplayName: "TR", Flag: "🇹🇷"},
},
}}
queryClient := &fakeRoomQueryClient{resp: &roomv1.ListRoomsResponse{
Rooms: []*roomv1.RoomListItem{
{RoomId: "room-1", VisibleRegionId: 1001, Heat: 90, Locked: true},
{RoomId: "room-1", VisibleRegionId: 1001, Heat: 90, Locked: true, CountryCode: "US"},
},
NextCursor: "cursor-2",
}}
handler := NewHandlerWithClients(&fakeRoomClient{}, nil, nil, profileClient)
handler.SetRoomQueryClient(queryClient)
handler.SetUserRegionClient(regionClient)
handler.SetUserCountryQueryClient(countryClient)
router := handler.Routes(auth.NewVerifier("secret"))
request := httptest.NewRequest(http.MethodGet, "/api/v1/rooms?tab=hot&limit=2&cursor=cursor-1&q=room&visible_region_id=9999", nil)
request := httptest.NewRequest(http.MethodGet, "/api/v1/rooms?tab=hot&limit=2&cursor=cursor-1&q=room&country=US&visible_region_id=9999", nil)
request.Header.Set("Authorization", "Bearer "+signGatewayToken(t, "secret", 42))
request.Header.Set("X-Request-ID", "req-room-list")
recorder := httptest.NewRecorder()
@ -2295,21 +2401,67 @@ func TestListRoomsUsesUserRegionFromUserService(t *testing.T) {
if queryClient.lastList.GetViewerUserId() != 42 || queryClient.lastList.GetVisibleRegionId() != 1001 {
t.Fatalf("ListRooms must use authenticated user and server-side region: %+v", queryClient.lastList)
}
if queryClient.lastList.GetTab() != "hot" || queryClient.lastList.GetLimit() != 2 || queryClient.lastList.GetCursor() != "cursor-1" || queryClient.lastList.GetQuery() != "room" {
if queryClient.lastList.GetTab() != "hot" || queryClient.lastList.GetLimit() != 2 || queryClient.lastList.GetCursor() != "cursor-1" || queryClient.lastList.GetQuery() != "room" || queryClient.lastList.GetCountryCode() != "US" {
t.Fatalf("ListRooms query params were not propagated: %+v", queryClient.lastList)
}
if regionClient.last == nil || regionClient.last.GetRegionId() != 1001 || countryClient.last == nil {
t.Fatalf("ListRooms must fetch region countries and country metadata: region=%+v countries=%+v", regionClient.last, countryClient.last)
}
var response httpkit.ResponseEnvelope
if err := json.NewDecoder(recorder.Body).Decode(&response); err != nil {
t.Fatalf("decode room list response failed: %v", err)
}
data := response.Data.(map[string]any)
countries := data["countries"].([]any)
if len(countries) != 3 {
t.Fatalf("room list must expose current region countries: %+v", countries)
}
for index, want := range []string{"US", "AE", "TR"} {
country := countries[index].(map[string]any)
if country["country_code"] != want {
t.Fatalf("country order mismatch at %d: want %s got %+v", index, want, countries)
}
}
rooms := data["rooms"].([]any)
first := rooms[0].(map[string]any)
if first["im_group_id"] != "room-1" || first["locked"] != true {
if first["im_group_id"] != "room-1" || first["locked"] != true || first["country_code"] != "US" {
t.Fatalf("room list must expose room IM group id: %+v", first)
}
}
func TestListRoomsRejectsCountryOutsideUserRegion(t *testing.T) {
profileClient := &fakeUserProfileClient{regionID: 1001}
regionClient := &fakeUserRegionClient{resp: &userv1.RegionResponse{Region: &userv1.Region{
RegionId: 1001,
Countries: []string{"US", "AE"},
}}}
countryClient := &fakeUserCountryQueryClient{resp: &userv1.ListRegistrationCountriesResponse{
Countries: []*userv1.Country{
{CountryId: 1, CountryCode: "US", Flag: "🇺🇸"},
{CountryId: 2, CountryCode: "AE", Flag: "🇦🇪"},
{CountryId: 3, CountryCode: "CN", Flag: "🇨🇳"},
},
}}
queryClient := &fakeRoomQueryClient{}
handler := NewHandlerWithClients(&fakeRoomClient{}, nil, nil, profileClient)
handler.SetRoomQueryClient(queryClient)
handler.SetUserRegionClient(regionClient)
handler.SetUserCountryQueryClient(countryClient)
router := handler.Routes(auth.NewVerifier("secret"))
request := httptest.NewRequest(http.MethodGet, "/api/v1/rooms?tab=hot&limit=2&country=CN", nil)
request.Header.Set("Authorization", "Bearer "+signGatewayToken(t, "secret", 42))
recorder := httptest.NewRecorder()
router.ServeHTTP(recorder, request)
if recorder.Code != http.StatusBadRequest {
t.Fatalf("status mismatch: got %d body=%s", recorder.Code, recorder.Body.String())
}
if queryClient.lastList != nil {
t.Fatalf("invalid country must not reach room-service: %+v", queryClient.lastList)
}
}
func TestSetRoomPasswordForwardsPasswordAndReturnsLockedState(t *testing.T) {
client := &fakeRoomClient{}
router := NewHandlerWithClients(client, nil, nil, &fakeUserProfileClient{}).Routes(auth.NewVerifier("secret"))

View File

@ -15,6 +15,8 @@ type Handler struct {
roomGuardClient client.RoomGuardClient
roomQueryClient client.RoomQueryClient
userProfileClient client.UserProfileClient
userCountryClient client.UserCountryQueryClient
userRegionClient client.UserRegionClient
userSocialClient client.UserSocialClient
userHostClient client.UserHostClient
walletClient client.WalletClient
@ -28,6 +30,8 @@ type Config struct {
RoomGuardClient client.RoomGuardClient
RoomQueryClient client.RoomQueryClient
UserProfileClient client.UserProfileClient
UserCountryClient client.UserCountryQueryClient
UserRegionClient client.UserRegionClient
UserSocialClient client.UserSocialClient
UserHostClient client.UserHostClient
WalletClient client.WalletClient
@ -42,6 +46,8 @@ func New(config Config) *Handler {
roomGuardClient: config.RoomGuardClient,
roomQueryClient: config.RoomQueryClient,
userProfileClient: config.UserProfileClient,
userCountryClient: config.UserCountryClient,
userRegionClient: config.UserRegionClient,
userSocialClient: config.UserSocialClient,
userHostClient: config.UserHostClient,
walletClient: config.WalletClient,

View File

@ -63,12 +63,6 @@ type roomFeedRelationCursor struct {
// listRooms 按当前登录用户的服务端 region_id 查询房间发现列表。
func (h *Handler) listRooms(writer http.ResponseWriter, request *http.Request) {
if h.userProfileClient == nil || h.roomQueryClient == nil {
// 列表入口必须同时依赖 user-service 区域归属和 room-service 读模型。
httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
return
}
limit, ok := parseRoomListLimit(request.URL.Query().Get("limit"))
if !ok {
httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
@ -79,6 +73,11 @@ func (h *Handler) listRooms(writer http.ResponseWriter, request *http.Request) {
httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
return
}
if h.userProfileClient == nil || h.roomQueryClient == nil || h.userRegionClient == nil || h.userCountryClient == nil {
// 列表入口必须同时依赖 user-service 用户区域、区域国家、国家主数据和 room-service 读模型。
httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
return
}
viewerUserID := auth.UserIDFromContext(request.Context())
userResp, err := h.userProfileClient.GetUser(request.Context(), &userv1.GetUserRequest{
@ -90,6 +89,16 @@ func (h *Handler) listRooms(writer http.ResponseWriter, request *http.Request) {
httpkit.WriteRPCError(writer, request, err)
return
}
countries, err := h.roomListCountriesForUser(request, userResp.GetUser())
if err != nil {
httpkit.WriteRPCError(writer, request, err)
return
}
countryCode, ok := roomListCountryQuery(request, countries)
if !ok {
httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
return
}
resp, err := h.roomQueryClient.ListRooms(request.Context(), &roomv1.ListRoomsRequest{
Meta: httpkit.RoomMeta(request, "", ""),
@ -99,12 +108,15 @@ func (h *Handler) listRooms(writer http.ResponseWriter, request *http.Request) {
Cursor: request.URL.Query().Get("cursor"),
Limit: limit,
Query: roomListQuery(request),
CountryCode: countryCode,
})
if err != nil {
httpkit.WriteRPCError(writer, request, err)
return
}
httpkit.WriteOK(writer, request, roomListDataFromProto(resp))
data := roomListDataFromProto(resp)
data.Countries = countries
httpkit.WriteOK(writer, request, data)
}
// listRoomFeeds 查询 Mine 页 visited/friend/following/followed 房间流。
@ -296,6 +308,123 @@ func roomListQuery(request *http.Request) string {
return strings.TrimSpace(request.URL.Query().Get("query"))
}
func (h *Handler) roomListCountriesForUser(request *http.Request, user *userv1.User) ([]roomListCountryData, error) {
if user == nil || user.GetRegionId() <= 0 {
// 当前用户区域来自 user-service 主数据;缺失时不能猜测区域国家列表。
return nil, xerr.New(xerr.InvalidArgument, "user region is required")
}
regionResp, err := h.userRegionClient.GetRegion(request.Context(), &userv1.GetRegionRequest{
Meta: httpkit.UserMeta(request, ""),
RegionId: user.GetRegionId(),
})
if err != nil {
return nil, err
}
countryResp, err := h.userCountryClient.ListRegistrationCountries(request.Context(), &userv1.ListRegistrationCountriesRequest{
Meta: httpkit.UserMeta(request, ""),
})
if err != nil {
return nil, err
}
return roomListCountriesFromProto(user, regionResp.GetRegion(), countryResp.GetCountries()), nil
}
func roomListCountriesFromProto(user *userv1.User, region *userv1.Region, countries []*userv1.Country) []roomListCountryData {
countriesByCode := make(map[string]*userv1.Country, len(countries))
for _, country := range countries {
code := normalizeRoomListCountryCode(country.GetCountryCode())
if code == "" {
continue
}
countriesByCode[code] = country
}
seen := make(map[string]struct{})
regionCodes := make([]string, 0, len(region.GetCountries()))
regionCodeSet := make(map[string]struct{}, len(region.GetCountries()))
if region != nil {
for _, rawCode := range region.GetCountries() {
code := normalizeRoomListCountryCode(rawCode)
if code == "" {
continue
}
regionCodes = append(regionCodes, code)
regionCodeSet[code] = struct{}{}
}
}
result := make([]roomListCountryData, 0, len(countriesByCode))
add := func(code string) {
code = normalizeRoomListCountryCode(code)
if code == "" {
return
}
if _, exists := seen[code]; exists {
return
}
seen[code] = struct{}{}
result = append(result, roomListCountryDataFromProto(code, countriesByCode[code]))
}
// 国家列表严格来自当前区域;用户国家命中区域配置时提前到首位,其余国家保持 user-service 区域顺序。
if user != nil {
if _, exists := regionCodeSet[normalizeRoomListCountryCode(user.GetCountry())]; exists {
add(user.GetCountry())
}
}
for _, code := range regionCodes {
add(code)
}
return result
}
func roomListCountryDataFromProto(code string, country *userv1.Country) roomListCountryData {
item := roomListCountryData{
CountryCode: code,
CountryFlag: roomProfileCountryFlag(code),
}
if country == nil {
return item
}
item.CountryID = country.GetCountryId()
item.CountryName = strings.TrimSpace(country.GetCountryName())
item.CountryDisplayName = strings.TrimSpace(country.GetCountryDisplayName())
if flag := strings.TrimSpace(country.GetFlag()); flag != "" {
item.CountryFlag = flag
}
item.SortOrder = country.GetSortOrder()
return item
}
func roomListCountryQuery(request *http.Request, countries []roomListCountryData) (string, bool) {
raw := strings.TrimSpace(request.URL.Query().Get("country"))
if raw == "" {
raw = strings.TrimSpace(request.URL.Query().Get("country_code"))
}
code := normalizeRoomListCountryCode(raw)
if code == "" {
return "", raw == ""
}
for _, country := range countries {
if normalizeRoomListCountryCode(country.CountryCode) == code {
return code, true
}
}
return "", false
}
func normalizeRoomListCountryCode(countryCode string) string {
countryCode = strings.ToUpper(strings.TrimSpace(countryCode))
if len(countryCode) < 2 || len(countryCode) > 3 {
return ""
}
for _, ch := range countryCode {
if ch < 'A' || ch > 'Z' {
return ""
}
}
return countryCode
}
// parseRoomListLimit 只做 HTTP 字符串到数字的传输层解析。
// 默认值和最大值仍由 room-service 统一裁剪,避免 gateway 和查询服务出现双份策略。
func parseRoomListLimit(raw string) (int32, bool) {
@ -697,14 +826,15 @@ func (h *Handler) createRoom(writer http.ResponseWriter, request *http.Request)
roomID := appcode.NewScopedID(appcode.FromContext(request.Context()))
roomShortID := userResp.GetUser().GetDisplayUserId()
resp, err := h.roomClient.CreateRoom(request.Context(), &roomv1.CreateRoomRequest{
Meta: httpkit.RoomMeta(request, roomID, body.CommandID),
SeatCount: body.SeatCount,
Mode: body.Mode,
VisibleRegionId: userResp.GetUser().GetRegionId(),
RoomName: body.RoomName,
RoomAvatar: body.RoomAvatar,
RoomDescription: body.RoomDescription,
RoomShortId: roomShortID,
Meta: httpkit.RoomMeta(request, roomID, body.CommandID),
SeatCount: body.SeatCount,
Mode: body.Mode,
VisibleRegionId: userResp.GetUser().GetRegionId(),
OwnerCountryCode: userResp.GetUser().GetCountry(),
RoomName: body.RoomName,
RoomAvatar: body.RoomAvatar,
RoomDescription: body.RoomDescription,
RoomShortId: roomShortID,
})
if err != nil {
httpkit.WriteRPCError(writer, request, err)

View File

@ -11,8 +11,18 @@ import (
const joinRoomContributionRankLimit = 10
type roomListData struct {
Rooms []roomListItemData `json:"rooms"`
NextCursor string `json:"next_cursor,omitempty"`
Rooms []roomListItemData `json:"rooms"`
NextCursor string `json:"next_cursor,omitempty"`
Countries []roomListCountryData `json:"countries,omitempty"`
}
type roomListCountryData struct {
CountryID int64 `json:"country_id,omitempty"`
CountryCode string `json:"country_code"`
CountryName string `json:"country_name,omitempty"`
CountryDisplayName string `json:"country_display_name,omitempty"`
CountryFlag string `json:"country_flag,omitempty"`
SortOrder int32 `json:"sort_order,omitempty"`
}
type myRoomData struct {
@ -37,6 +47,8 @@ type roomListItemData struct {
VisibleRegionID int64 `json:"visible_region_id,omitempty"`
AppCode string `json:"app_code,omitempty"`
RoomShortID string `json:"room_short_id,omitempty"`
CountryCode string `json:"country_code,omitempty"`
CountryFlag string `json:"country_flag,omitempty"`
}
type createRoomData struct {
@ -534,6 +546,8 @@ func roomListItemDataFromProto(room *roomv1.RoomListItem) roomListItemData {
VisibleRegionID: room.GetVisibleRegionId(),
AppCode: room.GetAppCode(),
RoomShortID: room.GetRoomShortId(),
CountryCode: room.GetCountryCode(),
CountryFlag: roomProfileCountryFlag(room.GetCountryCode()),
}
}

View File

@ -26,6 +26,8 @@ func (h *Handler) Routes(jwtVerifier *auth.Verifier) http.Handler {
RoomGuardClient: h.roomGuardClient,
RoomQueryClient: h.roomQueryClient,
UserProfileClient: h.userProfileClient,
UserCountryClient: h.userCountryClient,
UserRegionClient: h.userRegionClient,
UserSocialClient: h.userSocialClient,
UserHostClient: h.userHostClient,
WalletClient: h.walletClient,
@ -92,6 +94,7 @@ func (h *Handler) Routes(jwtVerifier *auth.Verifier) http.Handler {
WalletClient: h.walletClient,
UserHostClient: h.userHostClient,
UserProfileClient: h.userProfileClient,
GrowthLevelClient: h.growthLevelClient,
})
appAPI := appapi.New(appapi.Config{
AppConfigReader: h.appConfigReader,

View File

@ -42,15 +42,20 @@ type cpApplicationData struct {
}
type cpRelationshipData struct {
RelationshipID string `json:"relationship_id"`
RelationType string `json:"relation_type"`
Status string `json:"status"`
Me *cpUserProfileData `json:"me,omitempty"`
Partner *cpUserProfileData `json:"partner,omitempty"`
IntimacyValue int64 `json:"intimacy_value"`
Level int32 `json:"level"`
FormedAtMS int64 `json:"formed_at_ms"`
UpdatedAtMS int64 `json:"updated_at_ms"`
RelationshipID string `json:"relationship_id"`
RelationType string `json:"relation_type"`
Status string `json:"status"`
Me *cpUserProfileData `json:"me,omitempty"`
Partner *cpUserProfileData `json:"partner,omitempty"`
IntimacyValue int64 `json:"intimacy_value"`
Level int32 `json:"level"`
CurrentLevelThreshold int64 `json:"current_level_threshold"`
NextLevelThreshold int64 `json:"next_level_threshold"`
NeededForNextLevel int64 `json:"needed_for_next_level"`
LevelProgressPercent int32 `json:"level_progress_percent"`
MaxLevel bool `json:"max_level"`
FormedAtMS int64 `json:"formed_at_ms"`
UpdatedAtMS int64 `json:"updated_at_ms"`
}
type cpRejectRequest struct {
@ -202,15 +207,20 @@ func cpRelationshipFromProto(item *userv1.CPRelationship) cpRelationshipData {
return cpRelationshipData{}
}
return cpRelationshipData{
RelationshipID: item.GetRelationshipId(),
RelationType: item.GetRelationType(),
Status: item.GetStatus(),
Me: cpUserProfileFromProto(item.GetMe()),
Partner: cpUserProfileFromProto(item.GetPartner()),
IntimacyValue: item.GetIntimacyValue(),
Level: item.GetLevel(),
FormedAtMS: item.GetFormedAtMs(),
UpdatedAtMS: item.GetUpdatedAtMs(),
RelationshipID: item.GetRelationshipId(),
RelationType: item.GetRelationType(),
Status: item.GetStatus(),
Me: cpUserProfileFromProto(item.GetMe()),
Partner: cpUserProfileFromProto(item.GetPartner()),
IntimacyValue: item.GetIntimacyValue(),
Level: item.GetLevel(),
CurrentLevelThreshold: item.GetCurrentLevelThreshold(),
NextLevelThreshold: item.GetNextLevelThreshold(),
NeededForNextLevel: item.GetNeededForNextLevel(),
LevelProgressPercent: item.GetLevelProgressPercent(),
MaxLevel: item.GetMaxLevel(),
FormedAtMS: item.GetFormedAtMs(),
UpdatedAtMS: item.GetUpdatedAtMs(),
}
}

View File

@ -0,0 +1,33 @@
package userapi
import (
"testing"
userv1 "hyapp.local/api/proto/user/v1"
)
func TestCPRelationshipFromProtoIncludesLevelProgress(t *testing.T) {
item := &userv1.CPRelationship{
RelationshipId: "cp_rel:1:2",
RelationType: "brother",
Status: "active",
IntimacyValue: 180,
Level: 2,
CurrentLevelThreshold: 100,
NextLevelThreshold: 300,
NeededForNextLevel: 120,
LevelProgressPercent: 40,
MaxLevel: false,
FormedAtMs: 1700000000000,
UpdatedAtMs: 1700000001000,
}
got := cpRelationshipFromProto(item)
if got.CurrentLevelThreshold != 100 ||
got.NextLevelThreshold != 300 ||
got.NeededForNextLevel != 120 ||
got.LevelProgressPercent != 40 ||
got.MaxLevel {
t.Fatalf("relationship progress fields not mapped: %+v", got)
}
}

View File

@ -189,10 +189,10 @@ func validateDelivery(delivery CPDelivery) error {
len(delivery.PayloadJSON) == 0 {
return xerr.New(xerr.InvalidArgument, "cp delivery is incomplete")
}
if delivery.Channel == channelTencentIMC2C && delivery.TargetUserID <= 0 {
if isTencentIMC2CChannel(delivery.Channel) && delivery.TargetUserID <= 0 {
return xerr.New(xerr.InvalidArgument, "cp c2c delivery target_user_id is required")
}
if delivery.Channel == channelTencentIMC2C && delivery.SenderUserID <= 0 {
if isTencentIMC2CChannel(delivery.Channel) && delivery.SenderUserID <= 0 {
return xerr.New(xerr.InvalidArgument, "cp c2c delivery sender_user_id is required")
}
if (delivery.Channel == channelTencentIMRoom || delivery.Channel == channelTencentIMRegion) && strings.TrimSpace(delivery.GroupID) == "" {
@ -201,6 +201,15 @@ func validateDelivery(delivery CPDelivery) error {
return nil
}
func isTencentIMC2CChannel(channel string) bool {
switch channel {
case channelTencentIMC2C, channelTencentIMC2CAcceptor:
return true
default:
return false
}
}
func truncateError(value string) string {
value = strings.TrimSpace(value)
if len(value) <= 512 {

View File

@ -22,9 +22,10 @@ const (
eventApplicationRejected = "UserCPApplicationRejected"
eventRelationshipCreated = "UserCPRelationshipCreated"
channelTencentIMC2C = "tencent_im_c2c"
channelTencentIMRoom = "tencent_im_room"
channelTencentIMRegion = "tencent_im_region"
channelTencentIMC2C = "tencent_im_c2c"
channelTencentIMC2CAcceptor = "tencent_im_c2c_acceptor"
channelTencentIMRoom = "tencent_im_room"
channelTencentIMRegion = "tencent_im_region"
noticeTypeApplicationCreated = "cp_application_created"
noticeTypeApplicationAccepted = "cp_application_accepted"
@ -99,7 +100,7 @@ func (s *Service) publishDelivery(ctx context.Context, delivery CPDelivery, opti
defer cancel()
var err error
switch delivery.Channel {
case channelTencentIMC2C:
case channelTencentIMC2C, channelTencentIMC2CAcceptor:
err = s.publisher.PublishUserCustomMessage(publishCtx, tencentim.CustomUserMessage{
ToAccount: tencentim.FormatUserID(delivery.TargetUserID),
FromAccount: tencentim.FormatUserID(delivery.SenderUserID),
@ -246,7 +247,7 @@ func deliveriesFromMessage(message usermq.UserOutboxMessage, groupIDPrefix strin
if err != nil {
return nil, err
}
return applicationDecisionDeliveries(appCode, message, application, payload, noticeTypeApplicationAccepted), nil
return applicationAcceptedDeliveries(appCode, message, application, payload), nil
case eventApplicationRejected:
application, err := applicationFromObject(firstObject(root, "application", "Application"))
if err != nil {
@ -309,6 +310,58 @@ func applicationDecisionDeliveries(appCode string, message usermq.UserOutboxMess
return deliveries
}
func applicationAcceptedDeliveries(appCode string, message usermq.UserOutboxMessage, application cpApplication, payload map[string]any) []CPDelivery {
// 同意成功后双方都要在 C2C 会话里看到一条消息notice_delivery_events 的幂等键只有 event_id + channel
// 所以第二条 C2C 使用独立内部 channel但 publishDelivery 仍按腾讯 IM 单聊投递。
requesterPayload := cloneJSONMap(payload)
requesterPayload["c2c_notice_role"] = "accepted_to_requester"
requesterPayload["message_text"] = "我同意了你"
acceptorPayload := cloneJSONMap(payload)
acceptorPayload["c2c_notice_role"] = "accepted_to_acceptor"
acceptorPayload["message_text"] = "对方已同意你的申请"
deliveries := []CPDelivery{
{
AppCode: appCode,
EventID: message.EventID,
EventType: message.EventType,
Channel: channelTencentIMC2C,
NoticeType: noticeTypeApplicationAccepted,
SenderUserID: application.Target.UserID,
TargetUserID: application.Requester.UserID,
PayloadJSON: mustJSON(requesterPayload),
CreatedAtMS: message.OccurredAtMS,
},
{
AppCode: appCode,
EventID: message.EventID,
EventType: message.EventType,
Channel: channelTencentIMC2CAcceptor,
NoticeType: noticeTypeApplicationAccepted,
SenderUserID: application.Requester.UserID,
TargetUserID: application.Target.UserID,
PayloadJSON: mustJSON(acceptorPayload),
CreatedAtMS: message.OccurredAtMS,
},
}
if application.RoomID == "" {
return deliveries
}
roomPayload := cloneJSONMap(payload)
roomPayload["recipient_user_id"] = strconv.FormatInt(application.Requester.UserID, 10)
deliveries = append(deliveries, CPDelivery{
AppCode: appCode,
EventID: message.EventID,
EventType: message.EventType,
Channel: channelTencentIMRoom,
NoticeType: noticeTypeApplicationAccepted,
TargetUserID: application.Requester.UserID,
GroupID: application.RoomID,
PayloadJSON: mustJSON(roomPayload),
CreatedAtMS: message.OccurredAtMS,
})
return deliveries
}
func relationshipCreatedDelivery(appCode string, message usermq.UserOutboxMessage, application cpApplication, relationshipID string, groupID string) ([]CPDelivery, error) {
payload, err := cpRelationshipBroadcastPayload(message, application, relationshipID, groupID)
if err != nil {

View File

@ -39,7 +39,7 @@ func TestProcessApplicationCreatedPublishesC2CAndRoomIM(t *testing.T) {
}
}
func TestProcessApplicationAcceptedPublishesRequesterC2CAndRoomIM(t *testing.T) {
func TestProcessApplicationAcceptedPublishesMutualC2CAndRoomIM(t *testing.T) {
repo := &fakeRepository{}
publisher := &fakePublisher{}
service := New(Config{NodeID: "notice-test"}, repo, publisher)
@ -56,13 +56,27 @@ func TestProcessApplicationAcceptedPublishesRequesterC2CAndRoomIM(t *testing.T)
if err != nil || !handled {
t.Fatalf("ProcessUserOutboxMessage failed handled=%v err=%v", handled, err)
}
if len(publisher.userMessages) != 1 || publisher.userMessages[0].FromAccount != "1002" || publisher.userMessages[0].ToAccount != "1001" {
t.Fatalf("expected requester c2c message, got %+v", publisher.userMessages)
if len(publisher.userMessages) != 2 {
t.Fatalf("expected mutual c2c messages, got %+v", publisher.userMessages)
}
if publisher.userMessages[0].FromAccount != "1002" || publisher.userMessages[0].ToAccount != "1001" {
t.Fatalf("expected acceptor -> requester c2c message, got %+v", publisher.userMessages[0])
}
requesterPayload := mustObject(string(publisher.userMessages[0].PayloadJSON))
if requesterPayload["c2c_notice_role"] != "accepted_to_requester" || requesterPayload["message_text"] != "我同意了你" {
t.Fatalf("requester c2c payload mismatch: %+v", requesterPayload)
}
if publisher.userMessages[1].FromAccount != "1001" || publisher.userMessages[1].ToAccount != "1002" {
t.Fatalf("expected requester -> acceptor c2c message, got %+v", publisher.userMessages[1])
}
acceptorPayload := mustObject(string(publisher.userMessages[1].PayloadJSON))
if acceptorPayload["c2c_notice_role"] != "accepted_to_acceptor" || acceptorPayload["message_text"] != "对方已同意你的申请" {
t.Fatalf("acceptor c2c payload mismatch: %+v", acceptorPayload)
}
if len(publisher.groupMessages) != 1 || publisher.groupMessages[0].GroupID != "room-100" {
t.Fatalf("expected requester room group message, got %+v", publisher.groupMessages)
}
if len(repo.delivered) != 2 || repo.delivered[0].Channel != channelTencentIMC2C || repo.delivered[1].Channel != channelTencentIMRoom {
if len(repo.delivered) != 3 || repo.delivered[0].Channel != channelTencentIMC2C || repo.delivered[1].Channel != channelTencentIMC2CAcceptor || repo.delivered[2].Channel != channelTencentIMRoom {
t.Fatalf("delivery channels mismatch: %+v", repo.delivered)
}
}

View File

@ -35,6 +35,7 @@ CREATE TABLE IF NOT EXISTS room_list_entries (
room_id VARCHAR(64) NOT NULL COMMENT '房间 ID',
room_short_id VARCHAR(32) NOT NULL DEFAULT '' COMMENT '房间短号 ID',
visible_region_id BIGINT NOT NULL DEFAULT 0 COMMENT '可见区域 ID',
owner_country_code VARCHAR(3) NOT NULL DEFAULT '' COMMENT '房主国家码',
owner_user_id BIGINT NOT NULL COMMENT '房主用户 ID',
title VARCHAR(128) NOT NULL DEFAULT '' COMMENT '标题',
cover_url VARCHAR(512) NOT NULL DEFAULT '' COMMENT '封面 URL',
@ -56,6 +57,8 @@ CREATE TABLE IF NOT EXISTS room_list_entries (
KEY idx_room_list_short_id (app_code, room_short_id),
KEY idx_room_list_region_hot (app_code, visible_region_id, status, sort_score DESC, room_id),
KEY idx_room_list_region_new (app_code, visible_region_id, status, created_at_ms DESC, room_id),
KEY idx_room_list_region_country_hot (app_code, visible_region_id, owner_country_code, status, sort_score DESC, room_id),
KEY idx_room_list_region_country_new (app_code, visible_region_id, owner_country_code, status, created_at_ms DESC, room_id),
KEY idx_room_list_owner (app_code, owner_user_id, updated_at_ms)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='房间列表项表';

View File

@ -64,6 +64,8 @@ type CreateRoom struct {
Mode string `json:"mode"`
// VisibleRegionID 是创建时固定的房间列表区域0 表示 GLOBAL。
VisibleRegionID int64 `json:"visible_region_id"`
// OwnerCountryCode 是创建瞬间房主所属国家,列表国家筛选以该投影为准。
OwnerCountryCode string `json:"owner_country_code,omitempty"`
// RoomName 是房间展示名称,恢复时回填 RoomExt["title"],不拆成独立状态 owner。
RoomName string `json:"room_name"`
// RoomAvatar 是创建时必填的房间封面,恢复时回填 RoomExt["cover_url"]。
@ -161,15 +163,16 @@ func (CloseRoom) Type() string { return "close_room" }
// AdminUpdateRoom 定义后台房间资料和生命周期修改请求。
type AdminUpdateRoom struct {
Base
Title *string `json:"title,omitempty"`
CoverURL *string `json:"cover_url,omitempty"`
Description *string `json:"description,omitempty"`
Mode *string `json:"mode,omitempty"`
Status *string `json:"status,omitempty"`
VisibleRegionID *int64 `json:"visible_region_id,omitempty"`
CloseReason string `json:"close_reason,omitempty"`
AdminID uint64 `json:"admin_id,omitempty"`
AdminName string `json:"admin_name,omitempty"`
Title *string `json:"title,omitempty"`
CoverURL *string `json:"cover_url,omitempty"`
Description *string `json:"description,omitempty"`
Mode *string `json:"mode,omitempty"`
Status *string `json:"status,omitempty"`
VisibleRegionID *int64 `json:"visible_region_id,omitempty"`
OwnerCountryCode *string `json:"owner_country_code,omitempty"`
CloseReason string `json:"close_reason,omitempty"`
AdminID uint64 `json:"admin_id,omitempty"`
AdminName string `json:"admin_name,omitempty"`
}
func (AdminUpdateRoom) Type() string { return "admin_update_room" }

View File

@ -243,6 +243,14 @@ func applyAdminRoomPatch(current *state.RoomState, cmd command.AdminUpdateRoom)
current.VisibleRegionID = *cmd.VisibleRegionID
changed = true
}
if cmd.OwnerCountryCode != nil {
// 房主国家只作为发现页分类投影字段保存在 RoomExt后台 HTTP 不开放直改入口,只有 user-service 国家事实同步会写入。
ownerCountryCode := normalizeRoomCountryCode(*cmd.OwnerCountryCode)
if current.RoomExt[roomExtOwnerCountryKey] != ownerCountryCode {
current.RoomExt[roomExtOwnerCountryKey] = ownerCountryCode
changed = true
}
}
if cmd.Status != nil && current.Status != *cmd.Status {
current.Status = *cmd.Status
changed = true

View File

@ -21,11 +21,12 @@ import (
)
const (
roomExtTitleKey = "title"
roomExtCoverURLKey = "cover_url"
roomExtDescriptionKey = "description"
roomExtShortIDKey = "room_short_id"
roomExtBackgroundKey = "background_url"
roomExtTitleKey = "title"
roomExtCoverURLKey = "cover_url"
roomExtDescriptionKey = "description"
roomExtShortIDKey = "room_short_id"
roomExtOwnerCountryKey = "owner_country_code"
roomExtBackgroundKey = "background_url"
// defaultRoomAvatar 只服务房间资料更新/后台清空封面后的兜底;创建房间必须显式提交封面。
defaultRoomAvatar = "hyapp://room/default-avatar"
@ -39,10 +40,11 @@ const (
// roomProfileInput 是创建房间时需要落到 RoomExt 的展示资料集合。
type roomProfileInput struct {
Name string
Avatar string
Description string
ShortID string
Name string
Avatar string
Description string
ShortID string
OwnerCountryCode string
}
// CreateRoom 创建一个新的房间执行单元,并把首个快照和事件落盘。
@ -92,15 +94,16 @@ func (s *Service) CreateRoom(ctx context.Context, req *roomv1.CreateRoomRequest)
// protobuf 请求先收敛为 command后续幂等、持久化和恢复都只认识 command 模型。
cmd := command.CreateRoom{
Base: baseFromMeta(req.GetMeta()),
OwnerUserID: actorUserID,
SeatCount: seatCount,
Mode: mode,
VisibleRegionID: normalizeVisibleRegionID(req.GetVisibleRegionId()),
RoomName: profile.Name,
RoomAvatar: profile.Avatar,
RoomDescription: profile.Description,
RoomShortID: normalizeRoomShortID(req.GetRoomShortId(), actorUserID),
Base: baseFromMeta(req.GetMeta()),
OwnerUserID: actorUserID,
SeatCount: seatCount,
Mode: mode,
VisibleRegionID: normalizeVisibleRegionID(req.GetVisibleRegionId()),
OwnerCountryCode: normalizeRoomCountryCode(req.GetOwnerCountryCode()),
RoomName: profile.Name,
RoomAvatar: profile.Avatar,
RoomDescription: profile.Description,
RoomShortID: normalizeRoomShortID(req.GetRoomShortId(), actorUserID),
}
idempotencyStartedAt := time.Now()
@ -174,10 +177,11 @@ func (s *Service) CreateRoom(ctx context.Context, req *roomv1.CreateRoomRequest)
roomState.Version = 1
roomState.VisibleRegionID = cmd.VisibleRegionID
applyRoomProfileExt(roomState, roomProfileInput{
Name: cmd.RoomName,
Avatar: cmd.RoomAvatar,
Description: cmd.RoomDescription,
ShortID: cmd.RoomShortID,
Name: cmd.RoomName,
Avatar: cmd.RoomAvatar,
Description: cmd.RoomDescription,
ShortID: cmd.RoomShortID,
OwnerCountryCode: cmd.OwnerCountryCode,
})
roomState.OnlineUsers[cmd.ActorUserID()] = &state.RoomUserState{
UserID: cmd.ActorUserID(),
@ -332,4 +336,5 @@ func applyRoomProfileExt(roomState *state.RoomState, profile roomProfileInput) {
roomState.RoomExt[roomExtCoverURLKey] = profile.Avatar
roomState.RoomExt[roomExtDescriptionKey] = profile.Description
roomState.RoomExt[roomExtShortIDKey] = profile.ShortID
roomState.RoomExt[roomExtOwnerCountryKey] = normalizeRoomCountryCode(profile.OwnerCountryCode)
}

View File

@ -36,6 +36,7 @@ const (
type roomListCursor struct {
Tab string `json:"tab"`
Query string `json:"query,omitempty"`
CountryCode string `json:"country_code,omitempty"`
SortScore int64 `json:"sort_score,omitempty"`
CreatedAtMS int64 `json:"created_at_ms,omitempty"`
UpdatedAtMS int64 `json:"updated_at_ms,omitempty"`
@ -66,7 +67,8 @@ func (s *Service) ListRooms(ctx context.Context, req *roomv1.ListRoomsRequest) (
if err != nil {
return nil, err
}
cursor, err := decodeRoomListCursor(tab, query, req.GetCursor())
countryCode := normalizeRoomCountryCode(req.GetCountryCode())
cursor, err := decodeRoomListCursor(tab, query, countryCode, req.GetCursor())
if err != nil {
return nil, err
}
@ -76,6 +78,7 @@ func (s *Service) ListRooms(ctx context.Context, req *roomv1.ListRoomsRequest) (
VisibleRegionID: normalizeVisibleRegionID(req.GetVisibleRegionId()),
Tab: tab,
Query: query,
CountryCode: countryCode,
Limit: limit + 1,
CursorSortScore: cursor.SortScore,
CursorCreatedAtMS: cursor.CreatedAtMS,
@ -90,7 +93,7 @@ func (s *Service) ListRooms(ctx context.Context, req *roomv1.ListRoomsRequest) (
return nil, err
}
return roomListResponseFromEntries(tab, query, entries, limit), nil
return roomListResponseFromEntries(tab, query, countryCode, entries, limit), nil
}
// ListRoomFeeds 查询 Mine 页 visited/friend/following/followed 房间流。
@ -112,7 +115,7 @@ func (s *Service) ListRoomFeeds(ctx context.Context, req *roomv1.ListRoomFeedsRe
if err != nil {
return nil, err
}
cursor, err := decodeRoomListCursor(tab, query, req.GetCursor())
cursor, err := decodeRoomListCursor(tab, query, "", req.GetCursor())
if err != nil {
return nil, err
}
@ -160,7 +163,7 @@ func (s *Service) ListRoomFeeds(ctx context.Context, req *roomv1.ListRoomFeedsRe
return nil, err
}
return roomListResponseFromEntries(tab, query, entries, limit), nil
return roomListResponseFromEntries(tab, query, "", entries, limit), nil
}
// GetMyRoom 查询当前用户自己创建的房间,不依赖 room_list_entries 投影命中。
@ -193,12 +196,12 @@ func (s *Service) GetMyRoom(ctx context.Context, req *roomv1.GetMyRoomRequest) (
}, nil
}
func roomListResponseFromEntries(tab string, query string, entries []RoomListEntry, limit int) *roomv1.ListRoomsResponse {
func roomListResponseFromEntries(tab string, query string, countryCode string, entries []RoomListEntry, limit int) *roomv1.ListRoomsResponse {
nextCursor := ""
if len(entries) > limit {
// 多查一条用于判断是否存在下一页;返回数据只包含请求的 limit 条。
last := entries[limit-1]
nextCursor = encodeRoomListCursor(tab, query, last)
nextCursor = encodeRoomListCursor(tab, query, countryCode, last)
entries = entries[:limit]
}
@ -280,6 +283,7 @@ func roomListEntryFromSnapshot(snapshot *roomv1.RoomSnapshot, visibleRegionID in
RoomID: snapshot.GetRoomId(),
RoomShortID: roomShortID,
VisibleRegionID: normalizeVisibleRegionID(visibleRegionID),
OwnerCountryCode: normalizeRoomCountryCode(snapshot.GetRoomExt()[roomExtOwnerCountryKey]),
OwnerUserID: snapshot.GetOwnerUserId(),
Title: title,
CoverURL: coverURL,
@ -387,11 +391,25 @@ func normalizeVisibleRegionID(regionID int64) int64 {
return regionID
}
// normalizeRoomCountryCode 把国家码收敛为大写字母;非法值按空值处理,避免污染 SQL 精确筛选。
func normalizeRoomCountryCode(countryCode string) string {
countryCode = strings.ToUpper(strings.TrimSpace(countryCode))
if len(countryCode) < 2 || len(countryCode) > 3 {
return ""
}
for _, ch := range countryCode {
if ch < 'A' || ch > 'Z' {
return ""
}
}
return countryCode
}
// decodeRoomListCursor 校验不透明 cursor 和当前 tab 一致,禁止客户端跨排序维度复用游标。
func decodeRoomListCursor(tab string, query string, encoded string) (roomListCursor, error) {
func decodeRoomListCursor(tab string, query string, countryCode string, encoded string) (roomListCursor, error) {
encoded = strings.TrimSpace(encoded)
if encoded == "" {
return roomListCursor{Tab: tab, Query: query}, nil
return roomListCursor{Tab: tab, Query: query, CountryCode: countryCode}, nil
}
payload, err := base64.RawURLEncoding.DecodeString(encoded)
@ -403,7 +421,7 @@ func decodeRoomListCursor(tab string, query string, encoded string) (roomListCur
if err := json.Unmarshal(payload, &cursor); err != nil {
return roomListCursor{}, xerr.New(xerr.InvalidArgument, "cursor is invalid")
}
if cursor.Tab != tab || cursor.Query != query || cursor.RoomID == "" {
if cursor.Tab != tab || cursor.Query != query || cursor.CountryCode != countryCode || cursor.RoomID == "" {
return roomListCursor{}, xerr.New(xerr.InvalidArgument, "cursor is invalid")
}
@ -412,10 +430,11 @@ func decodeRoomListCursor(tab string, query string, encoded string) (roomListCur
// encodeRoomListCursor 把最后一条列表记录编码成下一页边界。
// cursor 不暴露契约语义,后续调整排序字段时可以只兼容服务端解析逻辑。
func encodeRoomListCursor(tab string, query string, entry RoomListEntry) string {
func encodeRoomListCursor(tab string, query string, countryCode string, entry RoomListEntry) string {
cursor := roomListCursor{
Tab: tab,
Query: query,
CountryCode: countryCode,
SortScore: entry.SortScore,
CreatedAtMS: entry.CreatedAtMS,
UpdatedAtMS: entry.UpdatedAtMS,
@ -453,6 +472,7 @@ func roomListItemToProto(entry RoomListEntry) *roomv1.RoomListItem {
SeatCount: entry.SeatCount,
OccupiedSeatCount: entry.OccupiedSeatCount,
VisibleRegionId: entry.VisibleRegionID,
CountryCode: normalizeRoomCountryCode(entry.OwnerCountryCode),
}
}
@ -460,17 +480,18 @@ func roomListEntryFromMetaAndSnapshot(meta RoomMeta, snapshot *roomv1.RoomSnapsh
if snapshot == nil || snapshot.GetRoomId() == "" {
// GetMyRoom 兜底路径:即使快照暂时不可用,也能用 rooms 元数据返回“有房间”的最小卡片。
return RoomListEntry{
AppCode: meta.AppCode,
RoomID: meta.RoomID,
RoomShortID: meta.RoomShortID,
VisibleRegionID: normalizeVisibleRegionID(meta.VisibleRegionID),
OwnerUserID: meta.OwnerUserID,
Mode: meta.Mode,
Status: meta.Status,
Locked: meta.RoomPasswordHash != "",
SeatCount: meta.SeatCount,
CreatedAtMS: nowMS,
UpdatedAtMS: nowMS,
AppCode: meta.AppCode,
RoomID: meta.RoomID,
RoomShortID: meta.RoomShortID,
VisibleRegionID: normalizeVisibleRegionID(meta.VisibleRegionID),
OwnerCountryCode: "",
OwnerUserID: meta.OwnerUserID,
Mode: meta.Mode,
Status: meta.Status,
Locked: meta.RoomPasswordHash != "",
SeatCount: meta.SeatCount,
CreatedAtMS: nowMS,
UpdatedAtMS: nowMS,
}
}

View File

@ -148,17 +148,86 @@ func TestPinnedRoomListOrdersGlobalRegionLocalThenOtherForNew(t *testing.T) {
}
}
func createPinnedListRoom(t *testing.T, ctx context.Context, svc *roomservice.Service, roomID string, shortID string, ownerID int64, regionID int64) {
func TestRoomListFiltersByOwnerCountry(t *testing.T) {
ctx := context.Background()
repository := mysqltest.NewRepository(t)
svc := roomservice.New(roomservice.Config{
NodeID: "node-country-filter-test",
LeaseTTL: 10 * time.Second,
RankLimit: 20,
SnapshotEveryN: 1,
}, router.NewMemoryDirectory(), repository, followTestWallet{}, integration.NewNoopRoomEventPublisher(), integration.NewNoopOutboxPublisher())
createPinnedListRoom(t, ctx, svc, "room-us-low", "us-low", 3301, 9001, "US")
createPinnedListRoom(t, ctx, svc, "room-ae", "ae", 3302, 9001, "AE")
createPinnedListRoom(t, ctx, svc, "room-us-high", "us-high", 3303, 9001, "US")
repository.SetRoomSortScore("room-us-low", 100)
repository.SetRoomSortScore("room-ae", 200)
repository.SetRoomSortScore("room-us-high", 300)
firstPage, err := svc.ListRooms(ctx, &roomv1.ListRoomsRequest{
Meta: &roomv1.RequestMeta{AppCode: appcode.Default},
ViewerUserId: 4001,
VisibleRegionId: 9001,
Tab: "hot",
Limit: 1,
CountryCode: "us",
})
if err != nil {
t.Fatalf("list country filtered first page failed: %v", err)
}
if len(firstPage.GetRooms()) != 1 || firstPage.GetRooms()[0].GetRoomId() != "room-us-high" || firstPage.GetRooms()[0].GetCountryCode() != "US" {
t.Fatalf("country filter must return only matching owner country in hot order: %+v", firstPage.GetRooms())
}
if firstPage.GetNextCursor() == "" {
t.Fatalf("first country page should return cursor")
}
if _, err := svc.ListRooms(ctx, &roomv1.ListRoomsRequest{
Meta: &roomv1.RequestMeta{AppCode: appcode.Default},
ViewerUserId: 4001,
VisibleRegionId: 9001,
Tab: "hot",
Limit: 1,
CountryCode: "AE",
Cursor: firstPage.GetNextCursor(),
}); err == nil {
t.Fatalf("country cursor must not be reusable across country tabs")
}
secondPage, err := svc.ListRooms(ctx, &roomv1.ListRoomsRequest{
Meta: &roomv1.RequestMeta{AppCode: appcode.Default},
ViewerUserId: 4001,
VisibleRegionId: 9001,
Tab: "hot",
Limit: 1,
CountryCode: "US",
Cursor: firstPage.GetNextCursor(),
})
if err != nil {
t.Fatalf("list country filtered second page failed: %v", err)
}
if len(secondPage.GetRooms()) != 1 || secondPage.GetRooms()[0].GetRoomId() != "room-us-low" || secondPage.GetRooms()[0].GetCountryCode() != "US" {
t.Fatalf("second country page mismatch: %+v", secondPage.GetRooms())
}
}
func createPinnedListRoom(t *testing.T, ctx context.Context, svc *roomservice.Service, roomID string, shortID string, ownerID int64, regionID int64, countryCodes ...string) {
t.Helper()
ownerCountryCode := ""
if len(countryCodes) > 0 {
ownerCountryCode = countryCodes[0]
}
if _, err := svc.CreateRoom(ctx, &roomv1.CreateRoomRequest{
Meta: roomservice.NewRequestMeta(roomID, ownerID),
SeatCount: 10,
Mode: "voice",
VisibleRegionId: regionID,
RoomName: roomID,
RoomAvatar: testRoomCoverURL,
RoomShortId: shortID,
Meta: roomservice.NewRequestMeta(roomID, ownerID),
SeatCount: 10,
Mode: "voice",
VisibleRegionId: regionID,
OwnerCountryCode: ownerCountryCode,
RoomName: roomID,
RoomAvatar: testRoomCoverURL,
RoomShortId: shortID,
}); err != nil {
t.Fatalf("create room %s failed: %v", roomID, err)
}

View File

@ -128,6 +128,8 @@ type RoomListEntry struct {
RoomShortID string
// VisibleRegionID 是房间自身的列表区域;公共发现列表会用它把当前区域房间排在其他区域之前。
VisibleRegionID int64
// OwnerCountryCode 是房主国家码;公共发现列表国家 tab 使用它做服务端筛选。
OwnerCountryCode string
// OwnerUserID 是房间所有者,用于卡片展示和运营排查。
OwnerUserID int64
// Title 是卡片标题;空值由客户端或后续资料聚合兜底。
@ -364,6 +366,8 @@ type RoomListQuery struct {
OwnerUserID int64
// Query 非空时按 room_id 或 title 做包含匹配。
Query string
// CountryCode 非空时只返回房主国家命中的房间gateway 必须先校验它属于当前用户区域。
CountryCode string
// Limit 是实际查询数量service 层会限制最大值。
Limit int
// CursorSortScore 是 hot tab 的游标 score。

View File

@ -9,6 +9,7 @@ import (
roomv1 "hyapp.local/api/proto/room/v1"
"hyapp/pkg/appcode"
"hyapp/pkg/usermq"
"hyapp/services/room-service/internal/room/command"
"hyapp/services/room-service/internal/room/state"
)
@ -46,10 +47,14 @@ func (s *Service) HandleUserOutboxMessage(ctx context.Context, message usermq.Us
if err != nil {
return err
}
if !exists || meta.Status == state.RoomStatusDeleted || meta.VisibleRegionID == payload.NewRegionID {
if !exists || meta.Status == state.RoomStatusDeleted {
return nil
}
newCountry := normalizeRoomCountryCode(payload.NewCountry)
if meta.VisibleRegionID == payload.NewRegionID && newCountry == "" {
return nil
}
requestID := strings.TrimSpace(payload.RequestID)
if requestID == "" {
requestID = message.EventID
@ -58,17 +63,25 @@ func (s *Service) HandleUserOutboxMessage(ctx context.Context, message usermq.Us
if sentAtMS <= 0 {
sentAtMS = message.OccurredAtMS
}
_, err = s.AdminUpdateRoom(ctx, &roomv1.AdminUpdateRoomRequest{
Meta: &roomv1.RequestMeta{
AppCode: message.AppCode,
RequestId: requestID,
CommandId: message.EventID,
ActorUserId: payload.UserID,
RoomId: meta.RoomID,
SentAtMs: sentAtMS,
},
VisibleRegionId: &payload.NewRegionID,
AdminName: "user-region-sync",
})
updateMeta := &roomv1.RequestMeta{
AppCode: message.AppCode,
RequestId: requestID,
CommandId: message.EventID,
ActorUserId: payload.UserID,
RoomId: meta.RoomID,
SentAtMs: sentAtMS,
}
cmd := command.AdminUpdateRoom{
Base: baseFromMeta(updateMeta),
AdminName: "user-region-sync",
}
if meta.VisibleRegionID != payload.NewRegionID {
cmd.VisibleRegionID = &payload.NewRegionID
}
if newCountry != "" {
// UserRegionChanged 同时承载国家事实;即使区域未变,也要把房主国家投影写回房间列表。
cmd.OwnerCountryCode = &newCountry
}
_, err = s.applyAdminRoomMutation(ctx, cmd, cmd)
return err
}

View File

@ -28,13 +28,14 @@ func TestHandleUserRegionChangedUpdatesOwnerRoomRegionIdempotently(t *testing.T)
roomID := "room-user-region-sync"
ownerID := int64(163212)
if _, err := svc.CreateRoom(ctx, &roomv1.CreateRoomRequest{
Meta: roomservice.NewRequestMeta(roomID, ownerID),
SeatCount: 10,
Mode: "voice",
VisibleRegionId: 3,
RoomName: "Region Sync Room",
RoomAvatar: testRoomCoverURL,
RoomShortId: "163212",
Meta: roomservice.NewRequestMeta(roomID, ownerID),
SeatCount: 10,
Mode: "voice",
VisibleRegionId: 3,
OwnerCountryCode: "AE",
RoomName: "Region Sync Room",
RoomAvatar: testRoomCoverURL,
RoomShortId: "163212",
}); err != nil {
t.Fatalf("create room failed: %v", err)
}
@ -79,9 +80,16 @@ func TestHandleUserRegionChangedUpdatesOwnerRoomRegionIdempotently(t *testing.T)
if err != nil {
t.Fatalf("list new region entries failed: %v", err)
}
if len(newRegionEntries) != 1 || newRegionEntries[0].RoomID != roomID || newRegionEntries[0].VisibleRegionID != 2 {
if len(newRegionEntries) != 1 || newRegionEntries[0].RoomID != roomID || newRegionEntries[0].VisibleRegionID != 2 || newRegionEntries[0].OwnerCountryCode != "BD" {
t.Fatalf("new region list projection mismatch: %+v", newRegionEntries)
}
countryEntries, err := repository.ListRoomListEntries(ctx, roomservice.RoomListQuery{VisibleRegionID: 2, CountryCode: "BD", Tab: "new", Limit: 10})
if err != nil {
t.Fatalf("list new country entries failed: %v", err)
}
if len(countryEntries) != 1 || countryEntries[0].RoomID != roomID || countryEntries[0].OwnerCountryCode != "BD" {
t.Fatalf("new country list projection mismatch: %+v", countryEntries)
}
oldRegionEntries, err := repository.ListRoomListEntries(ctx, roomservice.RoomListQuery{VisibleRegionID: 3, Tab: "new", Limit: 10})
if err != nil {
t.Fatalf("list old region entries failed: %v", err)

View File

@ -98,6 +98,7 @@ func (r *Repository) Migrate(ctx context.Context) error {
room_id VARCHAR(64) NOT NULL,
room_short_id VARCHAR(32) NOT NULL DEFAULT '',
visible_region_id BIGINT NOT NULL DEFAULT 0,
owner_country_code VARCHAR(3) NOT NULL DEFAULT '',
owner_user_id BIGINT NOT NULL,
title VARCHAR(128) NOT NULL DEFAULT '',
cover_url VARCHAR(512) NOT NULL DEFAULT '',
@ -119,6 +120,8 @@ func (r *Repository) Migrate(ctx context.Context) error {
KEY idx_room_list_short_id (app_code, room_short_id),
KEY idx_room_list_region_hot (app_code, visible_region_id, status, sort_score DESC, room_id),
KEY idx_room_list_region_new (app_code, visible_region_id, status, created_at_ms DESC, room_id),
KEY idx_room_list_region_country_hot (app_code, visible_region_id, owner_country_code, status, sort_score DESC, room_id),
KEY idx_room_list_region_country_new (app_code, visible_region_id, owner_country_code, status, created_at_ms DESC, room_id),
KEY idx_room_list_owner (app_code, owner_user_id, updated_at_ms)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci`,
`CREATE TABLE IF NOT EXISTS room_user_presence (
@ -334,6 +337,9 @@ func (r *Repository) Migrate(ctx context.Context) error {
if err := r.ensureRoomListLockSchema(ctx); err != nil {
return err
}
if err := r.ensureRoomListCountrySchema(ctx); err != nil {
return err
}
if err := r.ensureRoomPasswordSchema(ctx); err != nil {
return err
}
@ -433,6 +439,44 @@ func (r *Repository) ensureRoomListLockSchema(ctx context.Context) error {
return nil
}
func (r *Repository) ensureRoomListCountrySchema(ctx context.Context) error {
hasColumn, err := r.columnExists(ctx, "room_list_entries", "owner_country_code")
if err != nil {
return err
}
if !hasColumn {
// owner_country_code 是房主国家快照,只服务发现页国家筛选;历史行先为空,后续房间投影刷新会补真实值。
if _, err := r.db.ExecContext(ctx, `ALTER TABLE room_list_entries ADD COLUMN owner_country_code VARCHAR(3) NOT NULL DEFAULT '' AFTER visible_region_id`); err != nil {
return err
}
}
indexes := []struct {
name string
statement string
}{
{
name: "idx_room_list_region_country_hot",
statement: `ALTER TABLE room_list_entries ADD INDEX idx_room_list_region_country_hot (app_code, visible_region_id, owner_country_code, status, sort_score DESC, room_id)`,
},
{
name: "idx_room_list_region_country_new",
statement: `ALTER TABLE room_list_entries ADD INDEX idx_room_list_region_country_new (app_code, visible_region_id, owner_country_code, status, created_at_ms DESC, room_id)`,
},
}
for _, item := range indexes {
hasIndex, err := r.indexExists(ctx, "room_list_entries", item.name)
if err != nil {
return err
}
if !hasIndex {
if _, err := r.db.ExecContext(ctx, item.statement); err != nil {
return err
}
}
}
return nil
}
func (r *Repository) ensureRoomAdminCloseSchema(ctx context.Context) error {
columns := []struct {
table string
@ -1458,12 +1502,13 @@ func (r *Repository) UpsertRoomListEntry(ctx context.Context, entry roomservice.
appCode := normalizedRecordAppCode(ctx, entry.AppCode)
_, err := r.db.ExecContext(ctx,
`INSERT INTO room_list_entries (
app_code, room_id, room_short_id, visible_region_id, owner_user_id, title, cover_url, mode, status, locked,
app_code, room_id, room_short_id, visible_region_id, owner_country_code, owner_user_id, title, cover_url, mode, status, locked,
heat, online_count, seat_count, occupied_seat_count, sort_score, created_at_ms, updated_at_ms
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
ON DUPLICATE KEY UPDATE
room_short_id = VALUES(room_short_id),
visible_region_id = VALUES(visible_region_id),
owner_country_code = VALUES(owner_country_code),
owner_user_id = VALUES(owner_user_id),
title = VALUES(title),
cover_url = VALUES(cover_url),
@ -1480,6 +1525,7 @@ func (r *Repository) UpsertRoomListEntry(ctx context.Context, entry roomservice.
entry.RoomID,
entry.RoomShortID,
entry.VisibleRegionID,
entry.OwnerCountryCode,
entry.OwnerUserID,
entry.Title,
entry.CoverURL,
@ -1524,6 +1570,7 @@ func (r *Repository) ListRoomListEntries(ctx context.Context, query roomservice.
&entry.RoomID,
&entry.RoomShortID,
&entry.VisibleRegionID,
&entry.OwnerCountryCode,
&entry.OwnerUserID,
&entry.Title,
&entry.CoverURL,
@ -1571,7 +1618,7 @@ func (r *Repository) ListRoomListEntriesByIDs(ctx context.Context, query roomser
args = append(args, roomID)
}
rows, err := r.db.QueryContext(ctx, `
SELECT app_code, room_id, room_short_id, visible_region_id, owner_user_id, title, cover_url, mode, status, locked,
SELECT app_code, room_id, room_short_id, visible_region_id, owner_country_code, owner_user_id, title, cover_url, mode, status, locked,
heat, online_count, seat_count, occupied_seat_count, sort_score, created_at_ms, updated_at_ms
FROM room_list_entries
WHERE app_code = ? AND room_id IN (`+placeholders+`)`, args...)
@ -1587,6 +1634,7 @@ func (r *Repository) ListRoomListEntriesByIDs(ctx context.Context, query roomser
&entry.RoomID,
&entry.RoomShortID,
&entry.VisibleRegionID,
&entry.OwnerCountryCode,
&entry.OwnerUserID,
&entry.Title,
&entry.CoverURL,
@ -1669,6 +1717,7 @@ func (r *Repository) ListRoomUserFeedEntries(ctx context.Context, query roomserv
&entry.RoomID,
&entry.RoomShortID,
&entry.VisibleRegionID,
&entry.OwnerCountryCode,
&entry.OwnerUserID,
&entry.Title,
&entry.CoverURL,
@ -1794,6 +1843,7 @@ func (r *Repository) ListRoomFollowEntries(ctx context.Context, query roomservic
&entry.RoomID,
&entry.RoomShortID,
&entry.VisibleRegionID,
&entry.OwnerCountryCode,
&entry.OwnerUserID,
&entry.Title,
&entry.CoverURL,
@ -1846,6 +1896,7 @@ func (r *Repository) ListRoomRelatedFeedEntries(ctx context.Context, query rooms
&entry.RoomID,
&entry.RoomShortID,
&entry.VisibleRegionID,
&entry.OwnerCountryCode,
&entry.OwnerUserID,
&entry.Title,
&entry.CoverURL,
@ -1889,18 +1940,18 @@ func (r *Repository) ListRoomRelatedFeedEntries(ctx context.Context, query rooms
}
const roomListSelectColumns = `
SELECT app_code, room_id, room_short_id, visible_region_id, owner_user_id, title, cover_url, mode, status, locked,
SELECT app_code, room_id, room_short_id, visible_region_id, owner_country_code, owner_user_id, title, cover_url, mode, status, locked,
heat, online_count, seat_count, occupied_seat_count, sort_score, created_at_ms, updated_at_ms
FROM room_list_entries`
const roomUserFeedSelectColumns = `
SELECT r.app_code, r.room_id, r.room_short_id, r.visible_region_id, r.owner_user_id, r.title, r.cover_url, r.mode, r.status, r.locked,
SELECT r.app_code, r.room_id, r.room_short_id, r.visible_region_id, r.owner_country_code, r.owner_user_id, r.title, r.cover_url, r.mode, r.status, r.locked,
r.heat, r.online_count, r.seat_count, r.occupied_seat_count, r.sort_score, r.created_at_ms, f.updated_at_ms
FROM room_user_feed_entries f
JOIN room_list_entries r ON r.app_code = f.app_code AND r.room_id = f.room_id`
const roomFollowSelectColumns = `
SELECT r.app_code, r.room_id, r.room_short_id, r.visible_region_id, r.owner_user_id, r.title, r.cover_url, r.mode, r.status, r.locked,
SELECT r.app_code, r.room_id, r.room_short_id, r.visible_region_id, r.owner_country_code, r.owner_user_id, r.title, r.cover_url, r.mode, r.status, r.locked,
r.heat, r.online_count, r.seat_count, r.occupied_seat_count, r.sort_score, r.created_at_ms, f.followed_at_ms
FROM room_follows f
JOIN room_list_entries r ON r.app_code = f.app_code AND r.room_id = f.room_id`
@ -1921,7 +1972,7 @@ func buildRoomListQuerySQL(query roomservice.RoomListQuery) (string, []any) {
pinWeightExpr := "CASE WHEN gp.room_id IS NOT NULL THEN gp.weight WHEN rp.room_id IS NOT NULL THEN rp.weight ELSE 0 END"
pinExpiresExpr := "CASE WHEN gp.room_id IS NOT NULL THEN gp.expires_at_ms WHEN rp.room_id IS NOT NULL THEN rp.expires_at_ms ELSE 0 END"
selectSQL := `
SELECT r.app_code, r.room_id, r.room_short_id, r.visible_region_id, r.owner_user_id, r.title, r.cover_url, r.mode, r.status, r.locked,
SELECT r.app_code, r.room_id, r.room_short_id, r.visible_region_id, r.owner_country_code, r.owner_user_id, r.title, r.cover_url, r.mode, r.status, r.locked,
r.heat, r.online_count, r.seat_count, r.occupied_seat_count, r.sort_score, r.created_at_ms, r.updated_at_ms,
` + pinnedExpr + ` AS is_pinned, ` + pinRankExpr + ` AS pin_list_rank, ` + pinWeightExpr + ` AS pin_weight, ` + pinExpiresExpr + ` AS pinned_until_ms
FROM room_list_entries r
@ -1947,6 +1998,10 @@ func buildRoomListQuerySQL(query roomservice.RoomListQuery) (string, []any) {
where = append(where, "r.owner_user_id = ?")
args = append(args, query.OwnerUserID)
}
if strings.TrimSpace(query.CountryCode) != "" {
where = append(where, "r.owner_country_code = ?")
args = append(args, strings.ToUpper(strings.TrimSpace(query.CountryCode)))
}
if strings.TrimSpace(query.Query) != "" {
where = append(where, "(r.room_id LIKE ? ESCAPE '\\\\' OR r.room_short_id LIKE ? ESCAPE '\\\\' OR r.title LIKE ? ESCAPE '\\\\')")
like := "%" + escapeRoomListLike(query.Query) + "%"

View File

@ -147,6 +147,31 @@ CREATE TABLE IF NOT EXISTS user_status_change_logs (
KEY idx_user_status_logs_request (app_code, request_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户状态变更日志表';
CREATE TABLE IF NOT EXISTS manager_user_blocks (
app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码,用于多租户隔离',
block_id VARCHAR(96) NOT NULL COMMENT '经理封禁记录 ID',
command_id VARCHAR(128) NOT NULL COMMENT '幂等命令 ID',
manager_user_id BIGINT NOT NULL COMMENT '经理用户 ID',
target_user_id BIGINT NOT NULL COMMENT '被封禁用户 ID',
target_display_user_id VARCHAR(64) NOT NULL DEFAULT '' COMMENT '被封禁用户短 ID 快照',
target_username VARCHAR(128) NOT NULL DEFAULT '' COMMENT '被封禁用户昵称快照',
target_avatar VARCHAR(1024) NOT NULL DEFAULT '' COMMENT '被封禁用户头像快照',
country VARCHAR(16) NOT NULL DEFAULT '' COMMENT '封禁时双方国家码',
target_old_status VARCHAR(32) NOT NULL DEFAULT 'active' COMMENT '封禁前用户状态',
blocked_until_ms BIGINT NOT NULL COMMENT '计划解封时间UTC epoch ms',
status VARCHAR(32) NOT NULL COMMENT 'active/released/expired',
reason VARCHAR(255) NOT NULL DEFAULT '' COMMENT '封禁原因',
released_reason VARCHAR(255) NOT NULL DEFAULT '' COMMENT '释放原因',
released_at_ms BIGINT NOT NULL DEFAULT 0 COMMENT '释放时间UTC epoch ms',
created_at_ms BIGINT NOT NULL COMMENT '创建时间UTC epoch ms',
updated_at_ms BIGINT NOT NULL COMMENT '更新时间UTC epoch ms',
PRIMARY KEY (app_code, block_id),
UNIQUE KEY uk_manager_user_blocks_command (app_code, command_id),
KEY idx_manager_user_blocks_manager (app_code, manager_user_id, status, created_at_ms),
KEY idx_manager_user_blocks_target (app_code, target_user_id, status),
KEY idx_manager_user_blocks_expire (app_code, status, blocked_until_ms)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='经理中心用户封禁记录表';
CREATE TABLE IF NOT EXISTS user_reports (
app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码,用于多租户隔离',
report_id VARCHAR(96) NOT NULL COMMENT '举报单业务 ID',
@ -506,6 +531,98 @@ CREATE TABLE IF NOT EXISTS user_outbox (
KEY idx_user_outbox_aggregate (app_code, aggregate_type, aggregate_id, created_at_ms)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户发件箱表';
-- 老测试库可能已经存在 user_outboxCREATE TABLE IF NOT EXISTS 不会补齐后续新增的 MQ 投递字段。
-- 这里用 information_schema 生成幂等 ALTER保证重复执行 initdb 时不会因重复列名失败。
SET @ddl := IF(
(SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'user_outbox' AND COLUMN_NAME = 'worker_id') = 0,
'ALTER TABLE user_outbox ADD COLUMN worker_id VARCHAR(128) NOT NULL DEFAULT '''' COMMENT ''MQ 投递 worker ID'' AFTER status',
'SELECT 1'
);
PREPARE stmt FROM @ddl;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 投递锁过期时间用于多实例 claim user_outbox老库缺这个列时 CP 通知 worker 会直接 SQL 1054。
SET @ddl := IF(
(SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'user_outbox' AND COLUMN_NAME = 'lock_until_ms') = 0,
'ALTER TABLE user_outbox ADD COLUMN lock_until_ms BIGINT NOT NULL DEFAULT 0 COMMENT ''MQ 投递锁过期时间UTC epoch ms'' AFTER worker_id',
'SELECT 1'
);
PREPARE stmt FROM @ddl;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 重试次数和下一次重试时间跟 outbox 补偿投递扫描绑定,缺列会导致 pending 查询或失败回写不可用。
SET @ddl := IF(
(SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'user_outbox' AND COLUMN_NAME = 'retry_count') = 0,
'ALTER TABLE user_outbox ADD COLUMN retry_count INT NOT NULL DEFAULT 0 COMMENT ''重试次数'' AFTER lock_until_ms',
'SELECT 1'
);
PREPARE stmt FROM @ddl;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET @ddl := IF(
(SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'user_outbox' AND COLUMN_NAME = 'next_retry_at_ms') = 0,
'ALTER TABLE user_outbox ADD COLUMN next_retry_at_ms BIGINT NOT NULL DEFAULT 0 COMMENT ''下一次 MQ 投递重试时间UTC epoch ms'' AFTER retry_count',
'SELECT 1'
);
PREPARE stmt FROM @ddl;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 失败原因用于排查投递链路,缺列时 MarkFailed 会再次失败并掩盖真实业务错误。
SET @ddl := IF(
(SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'user_outbox' AND COLUMN_NAME = 'last_error') = 0,
'ALTER TABLE user_outbox ADD COLUMN last_error VARCHAR(512) NOT NULL DEFAULT '''' COMMENT ''最后一次失败原因'' AFTER next_retry_at_ms',
'SELECT 1'
);
PREPARE stmt FROM @ddl;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- pending 索引列顺序跟 user_outbox 扫描条件绑定;历史索引存在时先删除再按当前结构重建。
SET @ddl := IF(
(SELECT COUNT(*) FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'user_outbox' AND INDEX_NAME = 'idx_user_outbox_status_created') > 0,
'ALTER TABLE user_outbox DROP INDEX idx_user_outbox_status_created',
'SELECT 1'
);
PREPARE stmt FROM @ddl;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET @ddl := 'ALTER TABLE user_outbox ADD INDEX idx_user_outbox_status_created (app_code, status, next_retry_at_ms, created_at_ms, event_id)';
PREPARE stmt FROM @ddl;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET @ddl := IF(
(SELECT COUNT(*) FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'user_outbox' AND INDEX_NAME = 'idx_user_outbox_lock') = 0,
'ALTER TABLE user_outbox ADD INDEX idx_user_outbox_lock (app_code, status, lock_until_ms, created_at_ms, event_id)',
'SELECT 1'
);
PREPARE stmt FROM @ddl;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET @ddl := IF(
(SELECT COUNT(*) FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'user_outbox' AND INDEX_NAME = 'idx_user_outbox_retention') = 0,
'ALTER TABLE user_outbox ADD INDEX idx_user_outbox_retention (app_code, status, updated_at_ms, event_id)',
'SELECT 1'
);
PREPARE stmt FROM @ddl;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET @ddl := IF(
(SELECT COUNT(*) FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'user_outbox' AND INDEX_NAME = 'idx_user_outbox_aggregate') = 0,
'ALTER TABLE user_outbox ADD INDEX idx_user_outbox_aggregate (app_code, aggregate_type, aggregate_id, created_at_ms)',
'SELECT 1'
);
PREPARE stmt FROM @ddl;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
CREATE TABLE IF NOT EXISTS user_mic_sessions (
app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码,用于多租户隔离',
mic_session_id VARCHAR(128) NOT NULL COMMENT '麦位会话 ID',

View File

@ -64,18 +64,23 @@ type Application struct {
// Relationship 表达 A/B 已建立的一种 active 关系;展示时由 service 填充 me/partner。
type Relationship struct {
RelationshipID string
RelationType string
Status string
UserAID int64
UserBID int64
Me UserProfile
Partner UserProfile
IntimacyValue int64
Level int32
SourceApplicationID string
FormedAtMS int64
UpdatedAtMS int64
RelationshipID string
RelationType string
Status string
UserAID int64
UserBID int64
Me UserProfile
Partner UserProfile
IntimacyValue int64
Level int32
CurrentLevelThreshold int64
NextLevelThreshold int64
NeededForNextLevel int64
LevelProgressPercent int32
MaxLevel bool
SourceApplicationID string
FormedAtMS int64
UpdatedAtMS int64
}
// GiftEvent 是 room outbox 中 RoomGiftSent 转换后的 CP 关系消费输入。

View File

@ -23,6 +23,14 @@ const (
OperatorTypeSystem = "system"
)
const (
ManagerUserBlockStatusActive = "active"
ManagerUserBlockStatusReleased = "released"
ManagerUserBlockStatusExpired = "expired"
managerUserBlockReasonPrefix = "manager_center_block:"
)
// UserStatusCommand 是用户治理入口的服务层命令。
type UserStatusCommand struct {
AppCode string
@ -56,6 +64,51 @@ type UserStatusResult struct {
RoomEvictError string
}
// ManagerUserBlockCommand 是经理中心封禁用户的服务层命令。
// command_id 是 H5 写操作幂等边界blocked_until_ms 决定 cron 何时自动解封。
type ManagerUserBlockCommand struct {
CommandID string
ManagerUserID int64
TargetUserID int64
BlockedUntilMS int64
Reason string
RequestID string
NowMS int64
}
// ReleaseManagerUserBlockCommand 是手动或系统到期释放经理封禁的命令。
type ReleaseManagerUserBlockCommand struct {
ManagerUserID int64
BlockID string
Status string
Reason string
RequestID string
NowMS int64
}
// ManagerUserBlock 是经理封禁列表的稳定读模型。
type ManagerUserBlock struct {
BlockID string
ManagerUserID int64
TargetUserID int64
TargetDisplayUserID string
TargetUsername string
TargetAvatar string
Country string
TargetOldStatus userdomain.Status
BlockedUntilMS int64
Status string
Reason string
CreatedAtMS int64
UpdatedAtMS int64
}
// ManagerUserBlockRelease 带上是否需要恢复 users.status避免释放记录时误放仍被其他经理封禁的用户。
type ManagerUserBlockRelease struct {
Block ManagerUserBlock
ShouldRestore bool
}
// SetUserStatus 是封禁、停用和恢复用户的通用用例入口。
// 非 active 状态会先提交用户状态和 session 吊销事实,再执行 Redis/IM/Room 的可重试副作用。
func (s *Service) SetUserStatus(ctx context.Context, command UserStatusCommand) (UserStatusResult, error) {
@ -99,6 +152,154 @@ func (s *Service) SetUserStatus(ctx context.Context, command UserStatusCommand)
return result, nil
}
// CreateManagerUserBlock 创建经理封禁记录并把目标用户状态置为 banned。
// 国家和 active 状态在 service 层校验,保证任何 gateway/H5 入口都不能跨国家或重复封禁非 active 用户。
func (s *Service) CreateManagerUserBlock(ctx context.Context, command ManagerUserBlockCommand) (ManagerUserBlock, UserStatusResult, error) {
if s.userRepository == nil || s.moderationRepository == nil {
return ManagerUserBlock{}, UserStatusResult{}, xerr.New(xerr.Unavailable, "user repository is not configured")
}
command.CommandID = strings.TrimSpace(command.CommandID)
command.Reason = strings.TrimSpace(command.Reason)
if command.CommandID == "" || command.ManagerUserID <= 0 || command.TargetUserID <= 0 || command.BlockedUntilMS <= 0 {
return ManagerUserBlock{}, UserStatusResult{}, xerr.New(xerr.InvalidArgument, "manager block command is incomplete")
}
if command.NowMS <= 0 {
command.NowMS = s.now().UTC().UnixMilli()
}
if command.BlockedUntilMS <= command.NowMS {
return ManagerUserBlock{}, UserStatusResult{}, xerr.New(xerr.InvalidArgument, "blocked_until_ms must be in the future")
}
if command.Reason == "" {
command.Reason = "manager_center"
}
manager, err := s.userRepository.GetUser(ctx, command.ManagerUserID)
if err != nil {
return ManagerUserBlock{}, UserStatusResult{}, err
}
target, err := s.userRepository.GetUser(ctx, command.TargetUserID)
if err != nil {
return ManagerUserBlock{}, UserStatusResult{}, err
}
if manager.Country == "" || target.Country == "" || !strings.EqualFold(manager.Country, target.Country) {
return ManagerUserBlock{}, UserStatusResult{}, xerr.New(xerr.PermissionDenied, "target user is outside manager country")
}
if target.Status != userdomain.StatusActive {
return ManagerUserBlock{}, UserStatusResult{}, xerr.New(xerr.InvalidArgument, "target user is not active")
}
block, err := s.moderationRepository.CreateManagerUserBlock(ctx, command, target)
if err != nil {
return ManagerUserBlock{}, UserStatusResult{}, err
}
status, err := s.SetUserStatus(ctx, UserStatusCommand{
AppCode: appcode.FromContext(ctx),
TargetUserID: command.TargetUserID,
Status: userdomain.StatusBanned,
OperatorType: OperatorTypeAppUser,
OperatorUserID: command.ManagerUserID,
Reason: managerUserBlockReasonPrefix + block.BlockID,
RequestID: command.RequestID,
NowMs: command.NowMS,
})
if err != nil {
return ManagerUserBlock{}, UserStatusResult{}, err
}
return block, status, nil
}
func (s *Service) ListManagerUserBlocks(ctx context.Context, managerUserID int64, status string, pageSize int32) ([]ManagerUserBlock, error) {
if s.moderationRepository == nil {
return nil, xerr.New(xerr.Unavailable, "moderation repository is not configured")
}
if managerUserID <= 0 {
return nil, xerr.New(xerr.InvalidArgument, "manager_user_id is required")
}
status = normalizeManagerBlockStatus(status)
if status == "" {
status = ManagerUserBlockStatusActive
}
if pageSize <= 0 || pageSize > 100 {
pageSize = 50
}
return s.moderationRepository.ListManagerUserBlocks(ctx, managerUserID, status, pageSize)
}
func (s *Service) UnblockManagerUser(ctx context.Context, command ReleaseManagerUserBlockCommand) (ManagerUserBlock, UserStatusResult, error) {
command.Status = ManagerUserBlockStatusReleased
return s.releaseManagerUserBlock(ctx, command, OperatorTypeAppUser)
}
func (s *Service) ExpireManagerUserBlocks(ctx context.Context, limit int32) (int, error) {
if s.moderationRepository == nil {
return 0, xerr.New(xerr.Unavailable, "moderation repository is not configured")
}
if limit <= 0 || limit > 500 {
limit = 100
}
nowMS := s.now().UTC().UnixMilli()
releases, err := s.moderationRepository.ExpireManagerUserBlocks(ctx, nowMS, limit)
if err != nil {
return 0, err
}
for _, release := range releases {
if !release.ShouldRestore {
// 释放封禁记录不等于一定恢复用户;仍有其他经理封禁或最后一次 ban 不是经理封禁时必须保持 banned。
continue
}
if _, err := s.SetUserStatus(ctx, UserStatusCommand{
AppCode: appcode.FromContext(ctx),
TargetUserID: release.Block.TargetUserID,
Status: userdomain.StatusActive,
OperatorType: OperatorTypeSystem,
OperatorUserID: 0,
Reason: "manager_center_block_expired:" + release.Block.BlockID,
RequestID: "manager_block_expire:" + release.Block.BlockID,
NowMs: nowMS,
}); err != nil {
return len(releases), err
}
}
return len(releases), nil
}
func (s *Service) releaseManagerUserBlock(ctx context.Context, command ReleaseManagerUserBlockCommand, operatorType string) (ManagerUserBlock, UserStatusResult, error) {
if s.moderationRepository == nil {
return ManagerUserBlock{}, UserStatusResult{}, xerr.New(xerr.Unavailable, "moderation repository is not configured")
}
command.BlockID = strings.TrimSpace(command.BlockID)
command.Reason = strings.TrimSpace(command.Reason)
if command.ManagerUserID <= 0 || command.BlockID == "" {
return ManagerUserBlock{}, UserStatusResult{}, xerr.New(xerr.InvalidArgument, "manager unblock command is incomplete")
}
if command.NowMS <= 0 {
command.NowMS = s.now().UTC().UnixMilli()
}
if command.Reason == "" {
command.Reason = "manager_center_unblock"
}
release, err := s.moderationRepository.ReleaseManagerUserBlock(ctx, command)
if err != nil {
return ManagerUserBlock{}, UserStatusResult{}, err
}
if !release.ShouldRestore {
// repository 已判断能否安全恢复;这里不能盲目 SetUserStatus(active),否则会覆盖后台封禁或并发经理封禁。
return release.Block, UserStatusResult{}, nil
}
status, err := s.SetUserStatus(ctx, UserStatusCommand{
AppCode: appcode.FromContext(ctx),
TargetUserID: release.Block.TargetUserID,
Status: userdomain.StatusActive,
OperatorType: operatorType,
OperatorUserID: command.ManagerUserID,
Reason: "manager_center_unblock:" + release.Block.BlockID,
RequestID: command.RequestID,
NowMs: command.NowMS,
})
if err != nil {
return ManagerUserBlock{}, UserStatusResult{}, err
}
return release.Block, status, nil
}
func (s *Service) writeSessionDenylist(ctx context.Context, command UserStatusCommand, sessionIDs []string, reason string) (bool, string) {
if len(sessionIDs) == 0 {
return true, ""
@ -187,6 +388,16 @@ func normalizeOperatorType(operatorType string) string {
}
}
func normalizeManagerBlockStatus(status string) string {
status = strings.ToLower(strings.TrimSpace(status))
switch status {
case ManagerUserBlockStatusActive, ManagerUserBlockStatusReleased, ManagerUserBlockStatusExpired:
return status
default:
return ""
}
}
func normalizeModerationReason(reason string, status userdomain.Status) string {
reason = strings.TrimSpace(reason)
if reason != "" {

View File

@ -117,6 +117,33 @@ func (r *fakeModerationRepository) SetUserStatus(_ context.Context, command User
return UserStatusPersistenceResult{User: r.user, RevokedSessionIDs: append([]string{}, r.sessionIDs...)}, nil
}
func (r *fakeModerationRepository) CreateManagerUserBlock(_ context.Context, command ManagerUserBlockCommand, target userdomain.User) (ManagerUserBlock, error) {
return ManagerUserBlock{
BlockID: "block-1",
ManagerUserID: command.ManagerUserID,
TargetUserID: command.TargetUserID,
TargetDisplayUserID: target.CurrentDisplayUserID,
TargetUsername: target.Username,
TargetAvatar: target.Avatar,
Country: target.Country,
BlockedUntilMS: command.BlockedUntilMS,
Status: ManagerUserBlockStatusActive,
Reason: command.Reason,
}, nil
}
func (r *fakeModerationRepository) ListManagerUserBlocks(context.Context, int64, string, int32) ([]ManagerUserBlock, error) {
return nil, nil
}
func (r *fakeModerationRepository) ReleaseManagerUserBlock(context.Context, ReleaseManagerUserBlockCommand) (ManagerUserBlockRelease, error) {
return ManagerUserBlockRelease{}, nil
}
func (r *fakeModerationRepository) ExpireManagerUserBlocks(context.Context, int64, int32) ([]ManagerUserBlockRelease, error) {
return nil, nil
}
func (r *fakeModerationRepository) GetUser(context.Context, int64) (userdomain.User, error) {
return r.user, nil
}

View File

@ -126,6 +126,10 @@ type DeviceRepository interface {
// ModerationRepository 持有用户状态和认证 session 的同库事务。
type ModerationRepository interface {
SetUserStatus(ctx context.Context, command UserStatusCommand) (UserStatusPersistenceResult, error)
CreateManagerUserBlock(ctx context.Context, command ManagerUserBlockCommand, target userdomain.User) (ManagerUserBlock, error)
ListManagerUserBlocks(ctx context.Context, managerUserID int64, status string, pageSize int32) ([]ManagerUserBlock, error)
ReleaseManagerUserBlock(ctx context.Context, command ReleaseManagerUserBlockCommand) (ManagerUserBlockRelease, error)
ExpireManagerUserBlocks(ctx context.Context, nowMS int64, limit int32) ([]ManagerUserBlockRelease, error)
}
// SessionDenylist 写入 gateway access token 快速拒绝缓存。

View File

@ -445,10 +445,30 @@ func assertPendingApplication(application cpdomain.Application, nowMs int64) err
func relationshipSelectSQL(suffix string) string {
// viewerUserID 连续出现两次me 投影和 partner 选择都必须以当前查看用户为准。
// 等级阈值也在查询时投影出来,客户端资料卡只能使用服务端计算后的进度,不能读取后台配置或硬编码等级表。
return `
SELECT
r.relationship_id, r.relation_type, r.status, r.user_a_id, r.user_b_id,
r.intimacy_value, r.level_no, r.source_application_id, r.formed_at_ms, r.updated_at_ms,
COALESCE((
SELECT current_rule.intimacy_threshold
FROM user_cp_level_rules current_rule
WHERE current_rule.app_code = r.app_code
AND current_rule.relation_type = r.relation_type
AND current_rule.level_no = r.level_no
AND current_rule.status = 'active'
LIMIT 1
), 0) AS current_level_threshold,
COALESCE((
SELECT next_rule.intimacy_threshold
FROM user_cp_level_rules next_rule
WHERE next_rule.app_code = r.app_code
AND next_rule.relation_type = r.relation_type
AND next_rule.level_no > r.level_no
AND next_rule.status = 'active'
ORDER BY next_rule.level_no ASC
LIMIT 1
), 0) AS next_level_threshold,
me.user_id, COALESCE(me.current_display_user_id, ''), COALESCE(me.username, ''), COALESCE(me.avatar, ''),
partner.user_id, COALESCE(partner.current_display_user_id, ''), COALESCE(partner.username, ''), COALESCE(partner.avatar, '')
FROM user_cp_relationships r
@ -475,12 +495,52 @@ func scanRelationship(scanner interface{ Scan(dest ...any) error }) (cpdomain.Re
err := scanner.Scan(
&relationship.RelationshipID, &relationship.RelationType, &relationship.Status, &relationship.UserAID, &relationship.UserBID,
&relationship.IntimacyValue, &relationship.Level, &relationship.SourceApplicationID, &relationship.FormedAtMS, &relationship.UpdatedAtMS,
&relationship.CurrentLevelThreshold, &relationship.NextLevelThreshold,
&relationship.Me.UserID, &relationship.Me.DisplayUserID, &relationship.Me.Username, &relationship.Me.Avatar,
&relationship.Partner.UserID, &relationship.Partner.DisplayUserID, &relationship.Partner.Username, &relationship.Partner.Avatar,
)
if err == nil {
applyRelationshipLevelProgress(&relationship)
}
return relationship, err
}
func applyRelationshipLevelProgress(relationship *cpdomain.Relationship) {
if relationship == nil {
return
}
if relationship.CurrentLevelThreshold < 0 {
relationship.CurrentLevelThreshold = 0
}
if relationship.NextLevelThreshold <= relationship.CurrentLevelThreshold {
// 没有下一等级规则时按满级处理;客户端可以直接隐藏“距离下一级”和进度条。
relationship.NextLevelThreshold = 0
relationship.NeededForNextLevel = 0
relationship.LevelProgressPercent = 100
relationship.MaxLevel = true
return
}
remaining := relationship.NextLevelThreshold - relationship.IntimacyValue
if remaining < 0 {
remaining = 0
}
relationship.NeededForNextLevel = remaining
span := relationship.NextLevelThreshold - relationship.CurrentLevelThreshold
filled := relationship.IntimacyValue - relationship.CurrentLevelThreshold
if filled < 0 {
filled = 0
}
progress := int32((filled * 100) / span)
if progress < 0 {
progress = 0
}
if progress > 100 {
progress = 100
}
relationship.LevelProgressPercent = progress
relationship.MaxLevel = false
}
func queryRelationshipByIDTx(ctx context.Context, tx *sql.Tx, appCode string, viewerUserID int64, relationshipID string) (cpdomain.Relationship, error) {
row := tx.QueryRowContext(ctx, relationshipSelectSQL(`
WHERE r.app_code = ? AND r.relationship_id = ?`),

View File

@ -114,6 +114,41 @@ func TestAcceptApplicationKeepsCPHardLimit(t *testing.T) {
}
}
func TestListRelationshipsIncludesServerLevelProgress(t *testing.T) {
schema := mysqlschema.New(t)
repo := New(schema.DB)
ctx := appcode.WithContext(context.Background(), appcode.Default)
nowMS := int64(1700000000000)
for _, userID := range []int64{4001, 4002, 4003} {
seedCPTestUser(t, schema.DB, userID, fmt.Sprintf("Progress %d", userID), nowMS)
}
seedCPLevelRules(t, schema.DB, cpdomain.RelationTypeBrother, []int64{0, 100, 300, 600, 1000}, nowMS)
seedActiveCPRelationship(t, schema.DB, "progress_mid", cpdomain.RelationTypeBrother, 4001, 4002, 180, 2, nowMS)
seedActiveCPRelationship(t, schema.DB, "progress_max", cpdomain.RelationTypeBrother, 4001, 4003, 1200, 5, nowMS+1)
relationships, total, err := repo.ListRelationships(ctx, 4001, cpdomain.RelationTypeBrother, 1, 20)
if err != nil {
t.Fatalf("list relationships failed: %v", err)
}
if total != 2 || len(relationships) != 2 {
t.Fatalf("expected two relationships, total=%d len=%d", total, len(relationships))
}
byID := map[string]cpdomain.Relationship{}
for _, relationship := range relationships {
byID[relationship.RelationshipID] = relationship
}
mid := byID["progress_mid"]
if mid.CurrentLevelThreshold != 100 || mid.NextLevelThreshold != 300 || mid.NeededForNextLevel != 120 || mid.LevelProgressPercent != 40 || mid.MaxLevel {
t.Fatalf("mid-level progress mismatch: %+v", mid)
}
max := byID["progress_max"]
if max.CurrentLevelThreshold != 1000 || max.NextLevelThreshold != 0 || max.NeededForNextLevel != 0 || max.LevelProgressPercent != 100 || !max.MaxLevel {
t.Fatalf("max-level progress mismatch: %+v", max)
}
}
func seedCPTestUser(t testing.TB, db *sql.DB, userID int64, username string, nowMS int64) {
t.Helper()
@ -149,6 +184,46 @@ func seedRelationConfig(t testing.TB, db *sql.DB, relationType string, maxCount
}
}
func seedCPLevelRules(t testing.TB, db *sql.DB, relationType string, thresholds []int64, nowMS int64) {
t.Helper()
for index, threshold := range thresholds {
level := int32(index + 1)
// 测试直接写等级阈值表,验证关系查询返回的是服务端配置结果,而不是客户端或测试本地推导结果。
if _, err := db.ExecContext(context.Background(), `
INSERT INTO user_cp_level_rules (
app_code, relation_type, level_no, intimacy_threshold,
reward_resource_group_id, level_icon_url, status, created_at_ms, updated_at_ms
) VALUES (?, ?, ?, ?, 0, '', 'active', ?, ?)
ON DUPLICATE KEY UPDATE
intimacy_threshold = VALUES(intimacy_threshold),
status = VALUES(status),
updated_at_ms = VALUES(updated_at_ms)`,
appcode.Default, relationType, level, threshold, nowMS, nowMS,
); err != nil {
t.Fatalf("seed cp level rule %s/%d failed: %v", relationType, level, err)
}
}
}
func seedActiveCPRelationship(t testing.TB, db *sql.DB, relationshipID string, relationType string, userAID int64, userBID int64, intimacy int64, level int32, nowMS int64) {
t.Helper()
firstUserID, secondUserID := orderedPair(userAID, userBID)
// active 关系事实只保存当前等级和亲密值;当前/下一阈值必须由查询时读取等级规则计算。
if _, err := db.ExecContext(context.Background(), `
INSERT INTO user_cp_relationships (
app_code, relationship_id, user_a_id, user_b_id, relation_type, status,
intimacy_value, level_no, source_application_id,
formed_at_ms, ended_at_ms, created_at_ms, updated_at_ms
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, '', ?, 0, ?, ?)`,
appcode.Default, relationshipID, firstUserID, secondUserID, relationType, cpdomain.RelationshipStatusActive,
intimacy, level, nowMS, nowMS, nowMS,
); err != nil {
t.Fatalf("seed active cp relationship %s failed: %v", relationshipID, err)
}
}
func seedPendingCPApplication(t testing.TB, db *sql.DB, applicationID string, relationType string, requesterUserID int64, targetUserID int64, nowMS int64) {
t.Helper()

View File

@ -3,8 +3,11 @@ package user
import (
"context"
"database/sql"
"errors"
"strings"
"hyapp/pkg/appcode"
"hyapp/pkg/idgen"
"hyapp/pkg/xerr"
userdomain "hyapp/services/user-service/internal/domain/user"
userservice "hyapp/services/user-service/internal/service/user"
@ -115,3 +118,253 @@ func revokedBy(operatorUserID int64) string {
}
return "user_status"
}
// CreateManagerUserBlock 写入经理封禁记录;用户主状态修改由 service 层随后调用 SetUserStatus 完成。
func (r *Repository) CreateManagerUserBlock(ctx context.Context, command userservice.ManagerUserBlockCommand, target userdomain.User) (userservice.ManagerUserBlock, error) {
if r == nil || r.db == nil {
return userservice.ManagerUserBlock{}, xerr.New(xerr.Unavailable, "mysql repository is not configured")
}
appCode := appcode.FromContext(ctx)
blockID := idgen.New("mblock")
_, err := r.db.ExecContext(ctx, `
INSERT INTO manager_user_blocks (
app_code, block_id, command_id, manager_user_id, target_user_id,
target_display_user_id, target_username, target_avatar, country, target_old_status,
blocked_until_ms, status, reason, created_at_ms, updated_at_ms
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'active', ?, ?, ?)`,
appCode, blockID, command.CommandID, command.ManagerUserID, command.TargetUserID,
target.CurrentDisplayUserID, target.Username, target.Avatar, target.Country, string(target.Status),
command.BlockedUntilMS, command.Reason, command.NowMS, command.NowMS,
)
if err != nil {
if managerBlockDuplicate(err) {
return r.getManagerUserBlockByCommand(ctx, command.CommandID)
}
return userservice.ManagerUserBlock{}, err
}
return r.getManagerUserBlock(ctx, blockID)
}
func (r *Repository) ListManagerUserBlocks(ctx context.Context, managerUserID int64, status string, pageSize int32) ([]userservice.ManagerUserBlock, error) {
if r == nil || r.db == nil {
return nil, xerr.New(xerr.Unavailable, "mysql repository is not configured")
}
rows, err := r.db.QueryContext(ctx, managerBlockSelectSQL()+`
WHERE app_code = ? AND manager_user_id = ? AND status = ?
ORDER BY created_at_ms DESC, block_id DESC
LIMIT ?`,
appcode.FromContext(ctx), managerUserID, status, pageSize,
)
if err != nil {
return nil, err
}
defer rows.Close()
return scanManagerUserBlocks(rows)
}
func (r *Repository) ReleaseManagerUserBlock(ctx context.Context, command userservice.ReleaseManagerUserBlockCommand) (userservice.ManagerUserBlockRelease, error) {
return r.releaseManagerUserBlock(ctx, command.BlockID, command.ManagerUserID, command.Status, command.Reason, command.NowMS)
}
func (r *Repository) ExpireManagerUserBlocks(ctx context.Context, nowMS int64, limit int32) ([]userservice.ManagerUserBlockRelease, error) {
if r == nil || r.db == nil {
return nil, xerr.New(xerr.Unavailable, "mysql repository is not configured")
}
rows, err := r.db.QueryContext(ctx, `
SELECT block_id, manager_user_id
FROM manager_user_blocks
WHERE app_code = ? AND status = 'active' AND blocked_until_ms <= ?
ORDER BY blocked_until_ms ASC, block_id ASC
LIMIT ?`,
appcode.FromContext(ctx), nowMS, limit,
)
if err != nil {
return nil, err
}
pending := make([]struct {
blockID string
managerUserID int64
}, 0)
for rows.Next() {
var item struct {
blockID string
managerUserID int64
}
if err := rows.Scan(&item.blockID, &item.managerUserID); err != nil {
_ = rows.Close()
return nil, err
}
pending = append(pending, item)
}
if err := rows.Close(); err != nil {
return nil, err
}
releases := make([]userservice.ManagerUserBlockRelease, 0, len(pending))
for _, item := range pending {
// 逐条释放而不是批量 UPDATE是为了给每个目标用户单独计算是否还存在其他 active 经理封禁。
release, err := r.releaseManagerUserBlock(ctx, item.blockID, item.managerUserID, userservice.ManagerUserBlockStatusExpired, "manager_center_block_expired", nowMS)
if err != nil {
return releases, err
}
releases = append(releases, release)
}
return releases, nil
}
func (r *Repository) releaseManagerUserBlock(ctx context.Context, blockID string, managerUserID int64, status string, reason string, nowMS int64) (userservice.ManagerUserBlockRelease, error) {
tx, err := r.db.BeginTx(ctx, nil)
if err != nil {
return userservice.ManagerUserBlockRelease{}, err
}
defer func() { _ = tx.Rollback() }()
block, err := r.getManagerUserBlockForUpdate(ctx, tx, blockID, managerUserID)
if err != nil {
return userservice.ManagerUserBlockRelease{}, err
}
if block.Status != userservice.ManagerUserBlockStatusActive {
if err := tx.Commit(); err != nil {
return userservice.ManagerUserBlockRelease{}, err
}
return userservice.ManagerUserBlockRelease{Block: block}, nil
}
_, err = tx.ExecContext(ctx, `
UPDATE manager_user_blocks
SET status = ?, released_reason = ?, released_at_ms = ?, updated_at_ms = ?
WHERE app_code = ? AND block_id = ?`,
status, reason, nowMS, nowMS, appcode.FromContext(ctx), blockID,
)
if err != nil {
return userservice.ManagerUserBlockRelease{}, err
}
block.Status = status
block.UpdatedAtMS = nowMS
activeCount, err := r.countActiveManagerBlocks(ctx, tx, block.TargetUserID)
if err != nil {
return userservice.ManagerUserBlockRelease{}, err
}
// 只在“没有其他经理封禁 + 原状态是 active + 最近一次 ban 由经理封禁写入”时恢复,避免误解后台封禁。
latestManagerBan, err := r.latestBanBelongsToManagerBlock(ctx, tx, block.TargetUserID)
if err != nil {
return userservice.ManagerUserBlockRelease{}, err
}
if err := tx.Commit(); err != nil {
return userservice.ManagerUserBlockRelease{}, err
}
return userservice.ManagerUserBlockRelease{
Block: block,
ShouldRestore: activeCount == 0 && block.TargetOldStatus == userdomain.StatusActive && latestManagerBan,
}, nil
}
func (r *Repository) getManagerUserBlock(ctx context.Context, blockID string) (userservice.ManagerUserBlock, error) {
row := r.db.QueryRowContext(ctx, managerBlockSelectSQL()+`WHERE app_code = ? AND block_id = ?`, appcode.FromContext(ctx), blockID)
return scanManagerUserBlock(row)
}
func (r *Repository) getManagerUserBlockByCommand(ctx context.Context, commandID string) (userservice.ManagerUserBlock, error) {
row := r.db.QueryRowContext(ctx, managerBlockSelectSQL()+`WHERE app_code = ? AND command_id = ?`, appcode.FromContext(ctx), commandID)
return scanManagerUserBlock(row)
}
func (r *Repository) getManagerUserBlockForUpdate(ctx context.Context, tx *sql.Tx, blockID string, managerUserID int64) (userservice.ManagerUserBlock, error) {
row := tx.QueryRowContext(ctx, managerBlockSelectSQL()+`
WHERE app_code = ? AND block_id = ? AND manager_user_id = ?
FOR UPDATE`,
appcode.FromContext(ctx), blockID, managerUserID,
)
return scanManagerUserBlock(row)
}
func (r *Repository) countActiveManagerBlocks(ctx context.Context, tx *sql.Tx, targetUserID int64) (int64, error) {
var count int64
err := tx.QueryRowContext(ctx, `
SELECT COUNT(*)
FROM manager_user_blocks
WHERE app_code = ? AND target_user_id = ? AND status = 'active'`,
appcode.FromContext(ctx), targetUserID,
).Scan(&count)
return count, err
}
func (r *Repository) latestBanBelongsToManagerBlock(ctx context.Context, tx *sql.Tx, targetUserID int64) (bool, error) {
var reason string
err := tx.QueryRowContext(ctx, `
SELECT reason
FROM user_status_change_logs
WHERE app_code = ? AND target_user_id = ? AND new_status = 'banned'
ORDER BY created_at_ms DESC, id DESC
LIMIT 1`,
appcode.FromContext(ctx), targetUserID,
).Scan(&reason)
if errors.Is(err, sql.ErrNoRows) {
return false, nil
}
if err != nil {
return false, err
}
return strings.HasPrefix(reason, "manager_center_block:"), nil
}
func managerBlockSelectSQL() string {
return `
SELECT block_id, manager_user_id, target_user_id, target_display_user_id,
target_username, target_avatar, country, target_old_status, blocked_until_ms,
status, reason, created_at_ms, updated_at_ms
FROM manager_user_blocks
`
}
func scanManagerUserBlocks(rows *sql.Rows) ([]userservice.ManagerUserBlock, error) {
items := make([]userservice.ManagerUserBlock, 0)
for rows.Next() {
item, err := scanManagerUserBlockRows(rows)
if err != nil {
return nil, err
}
items = append(items, item)
}
return items, rows.Err()
}
type managerBlockScanner interface {
Scan(dest ...any) error
}
func scanManagerUserBlock(row managerBlockScanner) (userservice.ManagerUserBlock, error) {
item, err := scanManagerUserBlockRows(row)
if errors.Is(err, sql.ErrNoRows) {
return userservice.ManagerUserBlock{}, xerr.New(xerr.NotFound, "manager block not found")
}
return item, err
}
func scanManagerUserBlockRows(row managerBlockScanner) (userservice.ManagerUserBlock, error) {
var item userservice.ManagerUserBlock
var oldStatus string
if err := row.Scan(
&item.BlockID,
&item.ManagerUserID,
&item.TargetUserID,
&item.TargetDisplayUserID,
&item.TargetUsername,
&item.TargetAvatar,
&item.Country,
&oldStatus,
&item.BlockedUntilMS,
&item.Status,
&item.Reason,
&item.CreatedAtMS,
&item.UpdatedAtMS,
); err != nil {
return userservice.ManagerUserBlock{}, err
}
item.TargetOldStatus = userdomain.Status(oldStatus)
return item, nil
}
func managerBlockDuplicate(err error) bool {
if err == nil {
return false
}
return strings.Contains(err.Error(), "Duplicate entry") || strings.Contains(err.Error(), "Error 1062")
}

View File

@ -9,6 +9,7 @@ import (
mictimedomain "hyapp/services/user-service/internal/domain/mictime"
userdomain "hyapp/services/user-service/internal/domain/user"
authservice "hyapp/services/user-service/internal/service/auth"
userservice "hyapp/services/user-service/internal/service/user"
)
// toProtoApp 把 App 注册表事实转换为 protobuf 投影。
@ -93,6 +94,24 @@ func toProtoBusinessUserLookupItem(item userdomain.BusinessUserLookupItem) *user
}
}
func toProtoManagerUserBlock(block userservice.ManagerUserBlock) *userv1.ManagerUserBlock {
// 经理封禁记录是 H5 弹窗的列表数据;这里仅暴露可展示和可操作的稳定字段。
return &userv1.ManagerUserBlock{
BlockId: block.BlockID,
ManagerUserId: block.ManagerUserID,
TargetUserId: block.TargetUserID,
TargetDisplayUserId: block.TargetDisplayUserID,
TargetUsername: block.TargetUsername,
TargetAvatar: block.TargetAvatar,
Country: block.Country,
BlockedUntilMs: block.BlockedUntilMS,
Status: block.Status,
Reason: block.Reason,
CreatedAtMs: block.CreatedAtMS,
UpdatedAtMs: block.UpdatedAtMS,
}
}
func toProtoInviteOverview(overview invitedomain.Overview) *userv1.InviteOverview {
if overview.MyInviteCode == "" && !overview.InviteEnabled && overview.InviteCount == 0 && overview.ValidInviteCount == 0 && overview.ValidInviteThresholdCoin == 0 {
return nil

View File

@ -140,15 +140,20 @@ func toProtoCPRelationship(relationship cpdomain.Relationship) *userv1.CPRelatio
return nil
}
return &userv1.CPRelationship{
RelationshipId: relationship.RelationshipID,
RelationType: relationship.RelationType,
Status: relationship.Status,
Me: toProtoCPUserProfile(relationship.Me),
Partner: toProtoCPUserProfile(relationship.Partner),
IntimacyValue: relationship.IntimacyValue,
Level: relationship.Level,
FormedAtMs: relationship.FormedAtMS,
UpdatedAtMs: relationship.UpdatedAtMS,
RelationshipId: relationship.RelationshipID,
RelationType: relationship.RelationType,
Status: relationship.Status,
Me: toProtoCPUserProfile(relationship.Me),
Partner: toProtoCPUserProfile(relationship.Partner),
IntimacyValue: relationship.IntimacyValue,
Level: relationship.Level,
FormedAtMs: relationship.FormedAtMS,
UpdatedAtMs: relationship.UpdatedAtMS,
CurrentLevelThreshold: relationship.CurrentLevelThreshold,
NextLevelThreshold: relationship.NextLevelThreshold,
NeededForNextLevel: relationship.NeededForNextLevel,
LevelProgressPercent: relationship.LevelProgressPercent,
MaxLevel: relationship.MaxLevel,
}
}

View File

@ -82,6 +82,24 @@ func (s *Server) CompensateMicOpenSessions(ctx context.Context, req *userv1.Cron
}, nil
}
// ExpireManagerUserBlocks 释放到期的经理封禁记录;真正是否恢复 users.status 由 user-service 内部封禁来源判断。
func (s *Server) ExpireManagerUserBlocks(ctx context.Context, req *userv1.CronBatchRequest) (*userv1.CronBatchResponse, error) {
ctx = contextWithApp(ctx, req.GetMeta())
if s.userSvc == nil {
return nil, xerr.ToGRPCError(xerr.New(xerr.Unavailable, "user service is not configured"))
}
processed, err := s.userSvc.ExpireManagerUserBlocks(ctx, req.GetBatchSize())
if err != nil {
return nil, xerr.ToGRPCError(err)
}
return &userv1.CronBatchResponse{
ClaimedCount: int32(processed),
ProcessedCount: int32(processed),
SuccessCount: int32(processed),
HasMore: processed >= normalizedBatchSize(req.GetBatchSize(), 100),
}, nil
}
func durationFromMillis(value int64) time.Duration {
if value <= 0 {
return 0

View File

@ -304,6 +304,67 @@ func (s *Server) SetUserStatus(ctx context.Context, req *userv1.SetUserStatusReq
return nil, xerr.ToGRPCError(err)
}
return toProtoSetUserStatusResponse(result), nil
}
// CreateManagerUserBlock 创建经理中心封禁记录,并同步把目标账号置为 banned。
func (s *Server) CreateManagerUserBlock(ctx context.Context, req *userv1.CreateManagerUserBlockRequest) (*userv1.CreateManagerUserBlockResponse, error) {
ctx = contextWithApp(ctx, req.GetMeta())
block, status, err := s.userSvc.CreateManagerUserBlock(ctx, userservice.ManagerUserBlockCommand{
CommandID: req.GetCommandId(),
ManagerUserID: req.GetManagerUserId(),
TargetUserID: req.GetTargetUserId(),
BlockedUntilMS: req.GetBlockedUntilMs(),
Reason: req.GetReason(),
RequestID: req.GetMeta().GetRequestId(),
NowMS: req.GetMeta().GetSentAtMs(),
})
if err != nil {
return nil, xerr.ToGRPCError(err)
}
return &userv1.CreateManagerUserBlockResponse{
Block: toProtoManagerUserBlock(block),
Status: toProtoSetUserStatusResponse(status),
}, nil
}
// ListManagerUserBlocks 返回当前经理名下的封禁记录,默认只列 active 记录。
func (s *Server) ListManagerUserBlocks(ctx context.Context, req *userv1.ListManagerUserBlocksRequest) (*userv1.ListManagerUserBlocksResponse, error) {
ctx = contextWithApp(ctx, req.GetMeta())
blocks, err := s.userSvc.ListManagerUserBlocks(ctx, req.GetManagerUserId(), req.GetStatus(), req.GetPageSize())
if err != nil {
return nil, xerr.ToGRPCError(err)
}
resp := &userv1.ListManagerUserBlocksResponse{Blocks: make([]*userv1.ManagerUserBlock, 0, len(blocks))}
for _, block := range blocks {
resp.Blocks = append(resp.Blocks, toProtoManagerUserBlock(block))
}
return resp, nil
}
// UnblockManagerUser 手动释放经理封禁service 层会判断是否可以恢复用户主状态。
func (s *Server) UnblockManagerUser(ctx context.Context, req *userv1.UnblockManagerUserRequest) (*userv1.UnblockManagerUserResponse, error) {
ctx = contextWithApp(ctx, req.GetMeta())
block, status, err := s.userSvc.UnblockManagerUser(ctx, userservice.ReleaseManagerUserBlockCommand{
BlockID: req.GetBlockId(),
ManagerUserID: req.GetManagerUserId(),
Reason: req.GetReason(),
NowMS: req.GetMeta().GetSentAtMs(),
})
if err != nil {
return nil, xerr.ToGRPCError(err)
}
return &userv1.UnblockManagerUserResponse{
Block: toProtoManagerUserBlock(block),
Status: toProtoSetUserStatusResponse(status),
}, nil
}
func toProtoSetUserStatusResponse(result userservice.UserStatusResult) *userv1.SetUserStatusResponse {
// 解封时如果还有其他封禁记录或后台封禁链路service 会返回空状态结果;空响应表示记录已释放但用户主状态不应被恢复。
if result.User.UserID == 0 {
return nil
}
return &userv1.SetUserStatusResponse{
User: toProtoUser(result.User),
RevokedSessionCount: result.RevokedSessionCount,
@ -316,7 +377,7 @@ func (s *Server) SetUserStatus(ctx context.Context, req *userv1.SetUserStatusReq
RtcKicked: result.RTCKicked,
RtcKickError: result.RTCKickError,
RoomEvictError: result.RoomEvictError,
}, nil
}
}
// RecordProfileVisit 记录访问足迹;重复访问只刷新访问次数和最后访问时间。

View File

@ -72,6 +72,8 @@ const (
VipGrantSourceActivity = "activity_grant"
// VipGrantSourceAdmin 表示后台人工赠送 VIP。
VipGrantSourceAdmin = "admin_grant"
// VipGrantSourceManagerCenter 表示经理中心按权限赠送 VIP账务仍复用后台赠送激活链路。
VipGrantSourceManagerCenter = "manager_center"
// GameOpDebit 表示游戏平台下注、局内道具等扣金币行为。
GameOpDebit = "debit"
@ -948,6 +950,8 @@ func NormalizeVipGrantSource(source string) string {
return VipGrantSourceActivity
case VipGrantSourceAdmin:
return VipGrantSourceAdmin
case VipGrantSourceManagerCenter:
return VipGrantSourceManagerCenter
default:
return ""
}

View File

@ -971,7 +971,7 @@ func (s *Service) GrantVip(ctx context.Context, command ledger.GrantVipCommand)
}
command.GrantSource = ledger.NormalizeVipGrantSource(command.GrantSource)
switch command.GrantSource {
case ledger.VipGrantSourceAdmin:
case ledger.VipGrantSourceAdmin, ledger.VipGrantSourceManagerCenter:
if command.OperatorUserID <= 0 {
return ledger.GrantVipReceipt{}, xerr.New(xerr.InvalidArgument, "operator_user_id is required")
}

View File

@ -3511,6 +3511,21 @@ func TestGrantVipUsesUnifiedActivationAndNoticeOutbox(t *testing.T) {
if got := repository.CountRows("wallet_outbox", "user_id = ? AND event_type = ?", int64(51003), "VipActivated"); got != 2 {
t.Fatalf("both vip grants should enqueue activation IM events, got %d", got)
}
managerGrant, err := svc.GrantVip(context.Background(), ledger.GrantVipCommand{
CommandID: "cmd-manager-vip2",
TargetUserID: 51004,
Level: 2,
GrantSource: ledger.VipGrantSourceManagerCenter,
OperatorUserID: 9002,
Reason: "manager center vip grant",
})
if err != nil {
t.Fatalf("GrantVip manager center failed: %v", err)
}
if gotDuration := managerGrant.Vip.ExpiresAtMS - managerGrant.Vip.StartedAtMS; gotDuration != int64(30*24*time.Hour/time.Millisecond) {
t.Fatalf("manager center VIP grant duration mismatch: got %d receipt=%+v", gotDuration, managerGrant)
}
}
// TestVipLevelSixRequiresRechargeThreshold 验证 6-10 级 VIP 必须先达到累计充值门槛。

View File

@ -16,9 +16,10 @@ import (
)
const (
bizTypeVIPPurchase = "vip_purchase"
bizTypeVIPGrant = "vip_grant"
vipOutboxAsset = "VIP"
bizTypeVIPPurchase = "vip_purchase"
bizTypeVIPGrant = "vip_grant"
vipOutboxAsset = "VIP"
managerCenterVIPGrantDurationMS = 30 * 24 * 60 * 60 * 1000
)
// GetWalletOverview 返回钱包首页摘要;开关放在 wallet-service 内,避免 gateway 硬编码账务能力。
@ -524,6 +525,10 @@ func (r *Repository) GrantVip(ctx context.Context, command ledger.GrantVipComman
if level.Status != ledger.VipStatusActive {
return ledger.GrantVipReceipt{}, xerr.New(xerr.VIPLevelDisabled, "vip level is disabled")
}
if command.GrantSource == ledger.VipGrantSourceManagerCenter {
// 经理中心只能赠送 30 天 VIP这里覆盖配置里的购买周期保证 H5 即使不能传 duration也不会受后台 VIP 套餐周期影响。
level.DurationMS = managerCenterVIPGrantDurationMS
}
current, err := r.ensureUserVipForUpdate(ctx, tx, command.TargetUserID, nowMs)
if err != nil {
return ledger.GrantVipReceipt{}, err

View File

@ -57,11 +57,31 @@ type cpApplication struct {
}
type cpRelationship struct {
RelationshipID string `json:"relationship_id"`
RelationType string `json:"relation_type"`
Status string `json:"status"`
IntimacyValue int64 `json:"intimacy_value"`
Level int32 `json:"level"`
RelationshipID string `json:"relationship_id"`
RelationType string `json:"relation_type"`
Status string `json:"status"`
IntimacyValue int64 `json:"intimacy_value"`
Level int32 `json:"level"`
CurrentLevelThreshold int64 `json:"current_level_threshold"`
NextLevelThreshold int64 `json:"next_level_threshold"`
NeededForNextLevel int64 `json:"needed_for_next_level"`
LevelProgressPercent int32 `json:"level_progress_percent"`
MaxLevel bool `json:"max_level"`
}
type cpRelationshipListData struct {
Relationships []cpRelationship `json:"relationships"`
Total int64 `json:"total"`
}
type cpLevelRuleSnapshot struct {
LevelNo int32
IntimacyThreshold int64
RewardResourceGroupID int64
LevelIconURL string
Status string
CreatedAtMS int64
UpdatedAtMS int64
}
func main() {
@ -84,6 +104,16 @@ func main() {
startMS: time.Now().UTC().UnixMilli(),
}
if env("CP_SMOKE_FOCUS", "") == "relationships_progress" {
fmt.Println("==> focused relationship progress HTTP chain")
if err := s.runRelationshipProgressFocus(ctx); err != nil {
fatal(err)
}
fmt.Println("ok focused relationship progress HTTP chain")
fmt.Println("CP relationship progress smoke passed")
return
}
steps := []struct {
name string
fn func(context.Context) error
@ -130,6 +160,208 @@ func (s *smoke) seed(ctx context.Context) error {
return s.seedGifts(ctx)
}
func (s *smoke) runRelationshipProgressFocus(ctx context.Context) (err error) {
originalRules, err := s.snapshotCPLevelRules(ctx, "cp")
if err != nil {
return err
}
// 聚焦烟测会临时写入确定性的等级规则;退出前恢复原规则,避免影响本地后台正在调的 CP 配置。
defer func() {
if restoreErr := s.restoreCPLevelRules(ctx, "cp", originalRules); err == nil && restoreErr != nil {
err = restoreErr
}
}()
// 关系和申请只使用固定 smoke 用户,退出时清掉这些事实,避免下一次烟测被旧 active 关系挡住。
defer func() {
if cleanupErr := s.cleanupCPFacts(ctx); err == nil && cleanupErr != nil {
err = cleanupErr
}
}()
if err := s.seed(ctx); err != nil {
return err
}
if err := s.seedProgressLevelRules(ctx, "cp"); err != nil {
return err
}
if err := s.seedProgressRelationship(ctx, 180, 2); err != nil {
return err
}
if err := s.assertRelationshipProgressHTTP(ctx, cpRelationship{
RelationshipID: relationshipID(userA, userB),
RelationType: "cp",
Status: "active",
IntimacyValue: 180,
Level: 2,
CurrentLevelThreshold: 100,
NextLevelThreshold: 300,
NeededForNextLevel: 120,
LevelProgressPercent: 40,
MaxLevel: false,
}); err != nil {
return err
}
if err := s.seedProgressRelationship(ctx, 1200, 5); err != nil {
return err
}
return s.assertRelationshipProgressHTTP(ctx, cpRelationship{
RelationshipID: relationshipID(userA, userB),
RelationType: "cp",
Status: "active",
IntimacyValue: 1200,
Level: 5,
CurrentLevelThreshold: 1000,
NextLevelThreshold: 0,
NeededForNextLevel: 0,
LevelProgressPercent: 100,
MaxLevel: true,
})
}
func (s *smoke) snapshotCPLevelRules(ctx context.Context, relationType string) ([]cpLevelRuleSnapshot, error) {
rows, err := s.db.QueryContext(ctx, `
SELECT level_no, intimacy_threshold, reward_resource_group_id, level_icon_url, status, created_at_ms, updated_at_ms
FROM hyapp_user.user_cp_level_rules
WHERE app_code = ? AND relation_type = ?
ORDER BY level_no ASC`, appCode, relationType)
if err != nil {
return nil, err
}
defer rows.Close()
rules := make([]cpLevelRuleSnapshot, 0)
for rows.Next() {
var rule cpLevelRuleSnapshot
if err := rows.Scan(&rule.LevelNo, &rule.IntimacyThreshold, &rule.RewardResourceGroupID, &rule.LevelIconURL, &rule.Status, &rule.CreatedAtMS, &rule.UpdatedAtMS); err != nil {
return nil, err
}
rules = append(rules, rule)
}
return rules, rows.Err()
}
func (s *smoke) restoreCPLevelRules(ctx context.Context, relationType string, rules []cpLevelRuleSnapshot) error {
tx, err := s.db.BeginTx(ctx, nil)
if err != nil {
return err
}
defer func() { _ = tx.Rollback() }()
// 先删除当前 relation_type 的临时规则,再按快照恢复;这样新增、删除和禁用状态都能回到烟测前。
if _, err := tx.ExecContext(ctx, `
DELETE FROM hyapp_user.user_cp_level_rules
WHERE app_code = ? AND relation_type = ?`, appCode, relationType); err != nil {
return err
}
for _, rule := range rules {
if _, err := tx.ExecContext(ctx, `
INSERT INTO hyapp_user.user_cp_level_rules (
app_code, relation_type, level_no, intimacy_threshold, reward_resource_group_id,
level_icon_url, status, created_at_ms, updated_at_ms
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
appCode, relationType, rule.LevelNo, rule.IntimacyThreshold, rule.RewardResourceGroupID,
rule.LevelIconURL, rule.Status, rule.CreatedAtMS, rule.UpdatedAtMS,
); err != nil {
return err
}
}
return tx.Commit()
}
func (s *smoke) seedProgressLevelRules(ctx context.Context, relationType string) error {
nowMS := time.Now().UTC().UnixMilli()
rules := []struct {
level int32
threshold int64
}{
{1, 0},
{2, 100},
{3, 300},
{4, 600},
{5, 1000},
}
for _, rule := range rules {
if _, err := s.db.ExecContext(ctx, `
INSERT INTO hyapp_user.user_cp_level_rules (
app_code, relation_type, level_no, intimacy_threshold, reward_resource_group_id,
level_icon_url, status, created_at_ms, updated_at_ms
) VALUES (?, ?, ?, ?, 0, '', 'active', ?, ?)
ON DUPLICATE KEY UPDATE
intimacy_threshold = VALUES(intimacy_threshold),
reward_resource_group_id = 0,
level_icon_url = '',
status = 'active',
updated_at_ms = VALUES(updated_at_ms)`,
appCode, relationType, rule.level, rule.threshold, nowMS, nowMS,
); err != nil {
return err
}
}
return nil
}
func (s *smoke) seedProgressRelationship(ctx context.Context, intimacyValue int64, level int32) error {
nowMS := time.Now().UTC().UnixMilli()
a, b := orderedPair(userA, userB)
// 直接写真实关系表,只绕过“送礼/申请/同意”前置链路;本用例验证的是已形成关系的服务端读模型和 gateway 输出。
_, err := s.db.ExecContext(ctx, `
INSERT INTO hyapp_user.user_cp_relationships (
app_code, relationship_id, user_a_id, user_b_id, relation_type, status,
intimacy_value, level_no, source_application_id, formed_at_ms, ended_at_ms, created_at_ms, updated_at_ms
) VALUES (?, ?, ?, ?, 'cp', 'active', ?, ?, 'cp-smoke-progress', ?, 0, ?, ?)
ON DUPLICATE KEY UPDATE
relation_type = 'cp',
status = 'active',
intimacy_value = VALUES(intimacy_value),
level_no = VALUES(level_no),
updated_at_ms = VALUES(updated_at_ms)`,
appCode, relationshipID(userA, userB), a, b, intimacyValue, level, nowMS, nowMS, nowMS,
)
return err
}
func (s *smoke) assertRelationshipProgressHTTP(ctx context.Context, want cpRelationship) error {
got, err := s.getHTTPRelationship(ctx, userA, want.RelationType)
if err != nil {
return err
}
// 新增字段必须由 HTTP 响应直接给出;这里逐项比对,防止客户端继续用等级和亲密值自行推算进度。
checks := []struct {
name string
got any
want any
}{
{"relationship_id", got.RelationshipID, want.RelationshipID},
{"relation_type", got.RelationType, want.RelationType},
{"status", got.Status, want.Status},
{"intimacy_value", got.IntimacyValue, want.IntimacyValue},
{"level", got.Level, want.Level},
{"current_level_threshold", got.CurrentLevelThreshold, want.CurrentLevelThreshold},
{"next_level_threshold", got.NextLevelThreshold, want.NextLevelThreshold},
{"needed_for_next_level", got.NeededForNextLevel, want.NeededForNextLevel},
{"level_progress_percent", got.LevelProgressPercent, want.LevelProgressPercent},
{"max_level", got.MaxLevel, want.MaxLevel},
}
for _, check := range checks {
if check.got != check.want {
return fmt.Errorf("relationship %s mismatch: got=%v want=%v full=%+v", check.name, check.got, check.want, got)
}
}
return nil
}
func (s *smoke) getHTTPRelationship(ctx context.Context, userID int64, relationType string) (cpRelationship, error) {
resp, err := s.request(ctx, http.MethodGet, "/api/v1/cp/relationships?relation_type="+relationType+"&page=1&page_size=20", userID, nil)
if err != nil {
return cpRelationship{}, err
}
var data cpRelationshipListData
if err := json.Unmarshal(resp.Data, &data); err != nil {
return cpRelationship{}, err
}
if data.Total != 1 || len(data.Relationships) != 1 {
return cpRelationship{}, fmt.Errorf("relationship list size mismatch total=%d len=%d data=%s", data.Total, len(data.Relationships), string(resp.Data))
}
return data.Relationships[0], nil
}
func (s *smoke) lookupUSRegion(ctx context.Context) (int64, error) {
var regionID int64
err := s.db.QueryRowContext(ctx, `
@ -474,6 +706,9 @@ func (s *smoke) testAcceptApplication(ctx context.Context) error {
if err := s.waitNotice(ctx, "UserCPApplicationAccepted", "tencent_im_c2c", "cp_application_accepted", userA, "delivered"); err != nil {
return err
}
if err := s.waitNotice(ctx, "UserCPApplicationAccepted", "tencent_im_c2c_acceptor", "cp_application_accepted", userB, "delivered"); err != nil {
return err
}
if err := s.waitNotice(ctx, "UserCPApplicationAccepted", "tencent_im_room", "cp_application_accepted", userA, "delivered"); err != nil {
return err
}