Merge remote-tracking branch 'origin/test'
# Conflicts: # services/gateway-service/internal/transport/http/response_test.go
This commit is contained in:
commit
86ec305448
File diff suppressed because it is too large
Load Diff
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -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
@ -95,6 +95,7 @@ message RoomMicChanged {
|
||||
int64 publish_event_time_ms = 10;
|
||||
bool mic_muted = 11;
|
||||
string seat_status = 12;
|
||||
int64 target_gift_value = 13;
|
||||
}
|
||||
|
||||
// RoomMicSeatLocked 表达单个麦位锁定状态变更。
|
||||
@ -163,6 +164,7 @@ message RoomGiftSent {
|
||||
string gift_name = 15;
|
||||
string gift_icon_url = 16;
|
||||
string gift_animation_url = 17;
|
||||
int64 target_gift_value = 18;
|
||||
}
|
||||
|
||||
// RoomHeatChanged 表达热度变化结果。
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -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
@ -32,6 +32,7 @@ message RoomUser {
|
||||
string role = 2;
|
||||
int64 joined_at_ms = 3;
|
||||
int64 last_seen_at_ms = 4;
|
||||
int64 gift_value = 5;
|
||||
}
|
||||
|
||||
// RoomOnlineUser 是在线用户列表的展示读模型。
|
||||
@ -67,6 +68,8 @@ message SeatState {
|
||||
string seat_status = 10;
|
||||
// mic_heartbeat_at_ms 是当前 mic_session 最近一次服务端接受麦上心跳的 UTC epoch ms。
|
||||
int64 mic_heartbeat_at_ms = 11;
|
||||
// gift_value 是当前房间内该麦位用户收到的礼物热度累计,用户离房后清零。
|
||||
int64 gift_value = 12;
|
||||
}
|
||||
|
||||
// RankItem 表达房间内本地礼物榜项目。
|
||||
@ -425,6 +428,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 +906,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 +954,7 @@ message RoomListItem {
|
||||
string app_code = 13;
|
||||
string room_short_id = 14;
|
||||
bool locked = 15;
|
||||
string country_code = 16;
|
||||
}
|
||||
|
||||
// ListRoomsResponse 返回一页房间卡片和下一页不透明 cursor。
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
@ -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
@ -408,6 +408,14 @@ 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;
|
||||
// breakup_cost_coins 是当前关系类型后台配置的解除费用;active 关系返回配置值,ended 关系返回实际支付值。
|
||||
int64 breakup_cost_coins = 16;
|
||||
int64 ended_at_ms = 17;
|
||||
}
|
||||
|
||||
message ListCPApplicationsRequest {
|
||||
@ -462,6 +470,52 @@ message ListCPRelationshipsResponse {
|
||||
reserved "max_count_by_relation_type";
|
||||
}
|
||||
|
||||
message PrepareBreakCPRelationshipRequest {
|
||||
RequestMeta meta = 1;
|
||||
int64 user_id = 2;
|
||||
string relationship_id = 3;
|
||||
string command_id = 4;
|
||||
}
|
||||
|
||||
message PrepareBreakCPRelationshipResponse {
|
||||
CPRelationship relationship = 1;
|
||||
string command_id = 2;
|
||||
string status = 3;
|
||||
int64 breakup_cost_coins = 4;
|
||||
string wallet_transaction_id = 5;
|
||||
int64 coin_balance_after = 6;
|
||||
}
|
||||
|
||||
message ConfirmBreakCPRelationshipRequest {
|
||||
RequestMeta meta = 1;
|
||||
int64 user_id = 2;
|
||||
string relationship_id = 3;
|
||||
string command_id = 4;
|
||||
string wallet_transaction_id = 5;
|
||||
int64 paid_coin_amount = 6;
|
||||
int64 coin_balance_after = 7;
|
||||
}
|
||||
|
||||
message ConfirmBreakCPRelationshipResponse {
|
||||
CPRelationship relationship = 1;
|
||||
int64 breakup_cost_coins = 2;
|
||||
string wallet_transaction_id = 3;
|
||||
int64 coin_balance_after = 4;
|
||||
}
|
||||
|
||||
message CancelBreakCPRelationshipRequest {
|
||||
RequestMeta meta = 1;
|
||||
int64 user_id = 2;
|
||||
string relationship_id = 3;
|
||||
string command_id = 4;
|
||||
string reason = 5;
|
||||
}
|
||||
|
||||
message CancelBreakCPRelationshipResponse {
|
||||
string command_id = 1;
|
||||
string status = 2;
|
||||
}
|
||||
|
||||
message RoomGiftCPEvent {
|
||||
RequestMeta meta = 1;
|
||||
string event_id = 2;
|
||||
@ -619,6 +673,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 +980,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);
|
||||
}
|
||||
|
||||
@ -898,6 +1007,9 @@ service UserCPService {
|
||||
rpc AcceptCPApplication(AcceptCPApplicationRequest) returns (AcceptCPApplicationResponse);
|
||||
rpc RejectCPApplication(RejectCPApplicationRequest) returns (RejectCPApplicationResponse);
|
||||
rpc ListCPRelationships(ListCPRelationshipsRequest) returns (ListCPRelationshipsResponse);
|
||||
rpc PrepareBreakCPRelationship(PrepareBreakCPRelationshipRequest) returns (PrepareBreakCPRelationshipResponse);
|
||||
rpc ConfirmBreakCPRelationship(ConfirmBreakCPRelationshipRequest) returns (ConfirmBreakCPRelationshipResponse);
|
||||
rpc CancelBreakCPRelationship(CancelBreakCPRelationshipRequest) returns (CancelBreakCPRelationshipResponse);
|
||||
}
|
||||
|
||||
// UserCPInternalService 给 owner outbox 消费 worker 调用,不暴露给 App 或后台。
|
||||
@ -910,6 +1022,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 的绑定和失效。
|
||||
|
||||
@ -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.
|
||||
@ -991,10 +1105,13 @@ var UserSocialService_ServiceDesc = grpc.ServiceDesc{
|
||||
}
|
||||
|
||||
const (
|
||||
UserCPService_ListCPApplications_FullMethodName = "/hyapp.user.v1.UserCPService/ListCPApplications"
|
||||
UserCPService_AcceptCPApplication_FullMethodName = "/hyapp.user.v1.UserCPService/AcceptCPApplication"
|
||||
UserCPService_RejectCPApplication_FullMethodName = "/hyapp.user.v1.UserCPService/RejectCPApplication"
|
||||
UserCPService_ListCPRelationships_FullMethodName = "/hyapp.user.v1.UserCPService/ListCPRelationships"
|
||||
UserCPService_ListCPApplications_FullMethodName = "/hyapp.user.v1.UserCPService/ListCPApplications"
|
||||
UserCPService_AcceptCPApplication_FullMethodName = "/hyapp.user.v1.UserCPService/AcceptCPApplication"
|
||||
UserCPService_RejectCPApplication_FullMethodName = "/hyapp.user.v1.UserCPService/RejectCPApplication"
|
||||
UserCPService_ListCPRelationships_FullMethodName = "/hyapp.user.v1.UserCPService/ListCPRelationships"
|
||||
UserCPService_PrepareBreakCPRelationship_FullMethodName = "/hyapp.user.v1.UserCPService/PrepareBreakCPRelationship"
|
||||
UserCPService_ConfirmBreakCPRelationship_FullMethodName = "/hyapp.user.v1.UserCPService/ConfirmBreakCPRelationship"
|
||||
UserCPService_CancelBreakCPRelationship_FullMethodName = "/hyapp.user.v1.UserCPService/CancelBreakCPRelationship"
|
||||
)
|
||||
|
||||
// UserCPServiceClient is the client API for UserCPService service.
|
||||
@ -1007,6 +1124,9 @@ type UserCPServiceClient interface {
|
||||
AcceptCPApplication(ctx context.Context, in *AcceptCPApplicationRequest, opts ...grpc.CallOption) (*AcceptCPApplicationResponse, error)
|
||||
RejectCPApplication(ctx context.Context, in *RejectCPApplicationRequest, opts ...grpc.CallOption) (*RejectCPApplicationResponse, error)
|
||||
ListCPRelationships(ctx context.Context, in *ListCPRelationshipsRequest, opts ...grpc.CallOption) (*ListCPRelationshipsResponse, error)
|
||||
PrepareBreakCPRelationship(ctx context.Context, in *PrepareBreakCPRelationshipRequest, opts ...grpc.CallOption) (*PrepareBreakCPRelationshipResponse, error)
|
||||
ConfirmBreakCPRelationship(ctx context.Context, in *ConfirmBreakCPRelationshipRequest, opts ...grpc.CallOption) (*ConfirmBreakCPRelationshipResponse, error)
|
||||
CancelBreakCPRelationship(ctx context.Context, in *CancelBreakCPRelationshipRequest, opts ...grpc.CallOption) (*CancelBreakCPRelationshipResponse, error)
|
||||
}
|
||||
|
||||
type userCPServiceClient struct {
|
||||
@ -1057,6 +1177,36 @@ func (c *userCPServiceClient) ListCPRelationships(ctx context.Context, in *ListC
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *userCPServiceClient) PrepareBreakCPRelationship(ctx context.Context, in *PrepareBreakCPRelationshipRequest, opts ...grpc.CallOption) (*PrepareBreakCPRelationshipResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(PrepareBreakCPRelationshipResponse)
|
||||
err := c.cc.Invoke(ctx, UserCPService_PrepareBreakCPRelationship_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *userCPServiceClient) ConfirmBreakCPRelationship(ctx context.Context, in *ConfirmBreakCPRelationshipRequest, opts ...grpc.CallOption) (*ConfirmBreakCPRelationshipResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(ConfirmBreakCPRelationshipResponse)
|
||||
err := c.cc.Invoke(ctx, UserCPService_ConfirmBreakCPRelationship_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *userCPServiceClient) CancelBreakCPRelationship(ctx context.Context, in *CancelBreakCPRelationshipRequest, opts ...grpc.CallOption) (*CancelBreakCPRelationshipResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(CancelBreakCPRelationshipResponse)
|
||||
err := c.cc.Invoke(ctx, UserCPService_CancelBreakCPRelationship_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// UserCPServiceServer is the server API for UserCPService service.
|
||||
// All implementations must embed UnimplementedUserCPServiceServer
|
||||
// for forward compatibility.
|
||||
@ -1067,6 +1217,9 @@ type UserCPServiceServer interface {
|
||||
AcceptCPApplication(context.Context, *AcceptCPApplicationRequest) (*AcceptCPApplicationResponse, error)
|
||||
RejectCPApplication(context.Context, *RejectCPApplicationRequest) (*RejectCPApplicationResponse, error)
|
||||
ListCPRelationships(context.Context, *ListCPRelationshipsRequest) (*ListCPRelationshipsResponse, error)
|
||||
PrepareBreakCPRelationship(context.Context, *PrepareBreakCPRelationshipRequest) (*PrepareBreakCPRelationshipResponse, error)
|
||||
ConfirmBreakCPRelationship(context.Context, *ConfirmBreakCPRelationshipRequest) (*ConfirmBreakCPRelationshipResponse, error)
|
||||
CancelBreakCPRelationship(context.Context, *CancelBreakCPRelationshipRequest) (*CancelBreakCPRelationshipResponse, error)
|
||||
mustEmbedUnimplementedUserCPServiceServer()
|
||||
}
|
||||
|
||||
@ -1078,16 +1231,25 @@ 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) PrepareBreakCPRelationship(context.Context, *PrepareBreakCPRelationshipRequest) (*PrepareBreakCPRelationshipResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method PrepareBreakCPRelationship not implemented")
|
||||
}
|
||||
func (UnimplementedUserCPServiceServer) ConfirmBreakCPRelationship(context.Context, *ConfirmBreakCPRelationshipRequest) (*ConfirmBreakCPRelationshipResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method ConfirmBreakCPRelationship not implemented")
|
||||
}
|
||||
func (UnimplementedUserCPServiceServer) CancelBreakCPRelationship(context.Context, *CancelBreakCPRelationshipRequest) (*CancelBreakCPRelationshipResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method CancelBreakCPRelationship not implemented")
|
||||
}
|
||||
func (UnimplementedUserCPServiceServer) mustEmbedUnimplementedUserCPServiceServer() {}
|
||||
func (UnimplementedUserCPServiceServer) testEmbeddedByValue() {}
|
||||
@ -1100,7 +1262,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.
|
||||
@ -1182,6 +1344,60 @@ func _UserCPService_ListCPRelationships_Handler(srv interface{}, ctx context.Con
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _UserCPService_PrepareBreakCPRelationship_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(PrepareBreakCPRelationshipRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(UserCPServiceServer).PrepareBreakCPRelationship(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: UserCPService_PrepareBreakCPRelationship_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(UserCPServiceServer).PrepareBreakCPRelationship(ctx, req.(*PrepareBreakCPRelationshipRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _UserCPService_ConfirmBreakCPRelationship_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ConfirmBreakCPRelationshipRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(UserCPServiceServer).ConfirmBreakCPRelationship(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: UserCPService_ConfirmBreakCPRelationship_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(UserCPServiceServer).ConfirmBreakCPRelationship(ctx, req.(*ConfirmBreakCPRelationshipRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _UserCPService_CancelBreakCPRelationship_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(CancelBreakCPRelationshipRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(UserCPServiceServer).CancelBreakCPRelationship(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: UserCPService_CancelBreakCPRelationship_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(UserCPServiceServer).CancelBreakCPRelationship(ctx, req.(*CancelBreakCPRelationshipRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// UserCPService_ServiceDesc is the grpc.ServiceDesc for UserCPService service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
@ -1205,6 +1421,18 @@ var UserCPService_ServiceDesc = grpc.ServiceDesc{
|
||||
MethodName: "ListCPRelationships",
|
||||
Handler: _UserCPService_ListCPRelationships_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "PrepareBreakCPRelationship",
|
||||
Handler: _UserCPService_PrepareBreakCPRelationship_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ConfirmBreakCPRelationship",
|
||||
Handler: _UserCPService_ConfirmBreakCPRelationship_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "CancelBreakCPRelationship",
|
||||
Handler: _UserCPService_CancelBreakCPRelationship_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "proto/user/v1/user.proto",
|
||||
@ -1259,7 +1487,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 +1500,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 +1548,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 +1560,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 +1601,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 +1620,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 +1632,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 +1654,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 +1718,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 +1755,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 +1826,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 +1842,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 +1957,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 +1970,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 +2078,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 +2094,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 +2222,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 +2238,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 +2394,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 +2416,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 +2627,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 +2652,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
@ -1264,6 +1264,22 @@ message PurchaseVipResponse {
|
||||
repeated VipRewardItem reward_items = 6;
|
||||
}
|
||||
|
||||
message DebitCPBreakupFeeRequest {
|
||||
string command_id = 1;
|
||||
string app_code = 2;
|
||||
int64 user_id = 3;
|
||||
string relationship_id = 4;
|
||||
string relation_type = 5;
|
||||
int64 amount = 6;
|
||||
}
|
||||
|
||||
message DebitCPBreakupFeeResponse {
|
||||
string transaction_id = 1;
|
||||
int64 coin_spent = 2;
|
||||
int64 coin_balance_after = 3;
|
||||
AssetBalance balance = 4;
|
||||
}
|
||||
|
||||
message GrantVipRequest {
|
||||
string command_id = 1;
|
||||
string app_code = 2;
|
||||
@ -1657,6 +1673,7 @@ service WalletService {
|
||||
rpc ListVipPackages(ListVipPackagesRequest) returns (ListVipPackagesResponse);
|
||||
rpc GetMyVip(GetMyVipRequest) returns (GetMyVipResponse);
|
||||
rpc PurchaseVip(PurchaseVipRequest) returns (PurchaseVipResponse);
|
||||
rpc DebitCPBreakupFee(DebitCPBreakupFeeRequest) returns (DebitCPBreakupFeeResponse);
|
||||
rpc GrantVip(GrantVipRequest) returns (GrantVipResponse);
|
||||
rpc ListAdminVipLevels(ListAdminVipLevelsRequest) returns (ListAdminVipLevelsResponse);
|
||||
rpc UpdateAdminVipLevels(UpdateAdminVipLevelsRequest) returns (UpdateAdminVipLevelsResponse);
|
||||
|
||||
@ -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.
|
||||
@ -254,6 +254,7 @@ const (
|
||||
WalletService_ListVipPackages_FullMethodName = "/hyapp.wallet.v1.WalletService/ListVipPackages"
|
||||
WalletService_GetMyVip_FullMethodName = "/hyapp.wallet.v1.WalletService/GetMyVip"
|
||||
WalletService_PurchaseVip_FullMethodName = "/hyapp.wallet.v1.WalletService/PurchaseVip"
|
||||
WalletService_DebitCPBreakupFee_FullMethodName = "/hyapp.wallet.v1.WalletService/DebitCPBreakupFee"
|
||||
WalletService_GrantVip_FullMethodName = "/hyapp.wallet.v1.WalletService/GrantVip"
|
||||
WalletService_ListAdminVipLevels_FullMethodName = "/hyapp.wallet.v1.WalletService/ListAdminVipLevels"
|
||||
WalletService_UpdateAdminVipLevels_FullMethodName = "/hyapp.wallet.v1.WalletService/UpdateAdminVipLevels"
|
||||
@ -330,6 +331,7 @@ type WalletServiceClient interface {
|
||||
ListVipPackages(ctx context.Context, in *ListVipPackagesRequest, opts ...grpc.CallOption) (*ListVipPackagesResponse, error)
|
||||
GetMyVip(ctx context.Context, in *GetMyVipRequest, opts ...grpc.CallOption) (*GetMyVipResponse, error)
|
||||
PurchaseVip(ctx context.Context, in *PurchaseVipRequest, opts ...grpc.CallOption) (*PurchaseVipResponse, error)
|
||||
DebitCPBreakupFee(ctx context.Context, in *DebitCPBreakupFeeRequest, opts ...grpc.CallOption) (*DebitCPBreakupFeeResponse, error)
|
||||
GrantVip(ctx context.Context, in *GrantVipRequest, opts ...grpc.CallOption) (*GrantVipResponse, error)
|
||||
ListAdminVipLevels(ctx context.Context, in *ListAdminVipLevelsRequest, opts ...grpc.CallOption) (*ListAdminVipLevelsResponse, error)
|
||||
UpdateAdminVipLevels(ctx context.Context, in *UpdateAdminVipLevelsRequest, opts ...grpc.CallOption) (*UpdateAdminVipLevelsResponse, error)
|
||||
@ -885,6 +887,16 @@ func (c *walletServiceClient) PurchaseVip(ctx context.Context, in *PurchaseVipRe
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *walletServiceClient) DebitCPBreakupFee(ctx context.Context, in *DebitCPBreakupFeeRequest, opts ...grpc.CallOption) (*DebitCPBreakupFeeResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(DebitCPBreakupFeeResponse)
|
||||
err := c.cc.Invoke(ctx, WalletService_DebitCPBreakupFee_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *walletServiceClient) GrantVip(ctx context.Context, in *GrantVipRequest, opts ...grpc.CallOption) (*GrantVipResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(GrantVipResponse)
|
||||
@ -1094,6 +1106,7 @@ type WalletServiceServer interface {
|
||||
ListVipPackages(context.Context, *ListVipPackagesRequest) (*ListVipPackagesResponse, error)
|
||||
GetMyVip(context.Context, *GetMyVipRequest) (*GetMyVipResponse, error)
|
||||
PurchaseVip(context.Context, *PurchaseVipRequest) (*PurchaseVipResponse, error)
|
||||
DebitCPBreakupFee(context.Context, *DebitCPBreakupFeeRequest) (*DebitCPBreakupFeeResponse, error)
|
||||
GrantVip(context.Context, *GrantVipRequest) (*GrantVipResponse, error)
|
||||
ListAdminVipLevels(context.Context, *ListAdminVipLevelsRequest) (*ListAdminVipLevelsResponse, error)
|
||||
UpdateAdminVipLevels(context.Context, *UpdateAdminVipLevelsRequest) (*UpdateAdminVipLevelsResponse, error)
|
||||
@ -1120,208 +1133,211 @@ 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) DebitCPBreakupFee(context.Context, *DebitCPBreakupFeeRequest) (*DebitCPBreakupFeeResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method DebitCPBreakupFee 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 +1350,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.
|
||||
@ -2298,6 +2314,24 @@ func _WalletService_PurchaseVip_Handler(srv interface{}, ctx context.Context, de
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _WalletService_DebitCPBreakupFee_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(DebitCPBreakupFeeRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(WalletServiceServer).DebitCPBreakupFee(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: WalletService_DebitCPBreakupFee_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(WalletServiceServer).DebitCPBreakupFee(ctx, req.(*DebitCPBreakupFeeRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _WalletService_GrantVip_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GrantVipRequest)
|
||||
if err := dec(in); err != nil {
|
||||
@ -2787,6 +2821,10 @@ var WalletService_ServiceDesc = grpc.ServiceDesc{
|
||||
MethodName: "PurchaseVip",
|
||||
Handler: _WalletService_PurchaseVip_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "DebitCPBreakupFee",
|
||||
Handler: _WalletService_DebitCPBreakupFee_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GrantVip",
|
||||
Handler: _WalletService_GrantVip_Handler,
|
||||
|
||||
@ -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,11 +144,25 @@ 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接口
|
||||
## 8. 解除关系
|
||||
|
||||
用户可以主动解除自己的 active 关系。解除费用由后台 CP 配置按关系类型维护,单位是金币。
|
||||
|
||||
规则:
|
||||
|
||||
- App 先通过 `/api/v1/cp/relationships` 读取 `breakup_cost_coins` 展示费用。
|
||||
- 解除接口必须传 `command_id`,同一次解除重试复用同一个 `command_id`。
|
||||
- gateway 先在 user-service 创建解除占位,再调用 wallet-service 扣金币,最后确认关系为 `ended`。
|
||||
- 如果 wallet-service 扣费失败,user-service 会取消解除占位,关系仍保持 active。
|
||||
- 解除成功后不会发送 C2C、房间 IM 或区域 IM;客户端使用接口返回和关系列表刷新 UI。
|
||||
|
||||
## 9. App HTTP接口
|
||||
|
||||
所有接口走 `gateway-service`,响应使用 `{code,message,request_id,data}`。
|
||||
|
||||
@ -154,6 +172,30 @@ sequenceDiagram
|
||||
| `POST` | `/api/v1/cp/applications/{application_id}/accept` | 同意申请 |
|
||||
| `POST` | `/api/v1/cp/applications/{application_id}/reject` | 拒绝申请 |
|
||||
| `GET` | `/api/v1/cp/relationships` | 查询我的 active 关系 |
|
||||
| `POST` | `/api/v1/cp/relationships/{relationship_id}/break` | 解除关系并扣金币 |
|
||||
|
||||
`/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` | 是否已经没有下一等级 |
|
||||
| `breakup_cost_coins` | 当前关系解除需要支付的金币数 |
|
||||
| `formed_at_ms` | 关系形成时间 |
|
||||
| `ended_at_ms` | 解除时间;active 关系为 0 |
|
||||
| `updated_at_ms` | 最近更新时间 |
|
||||
|
||||
这些字段由 user-service 从 `user_cp_level_rules` 计算,gateway 只做透传。客户端资料卡不能根据本地表推算这些值。
|
||||
|
||||
申请列表参数:
|
||||
|
||||
@ -178,41 +220,61 @@ sequenceDiagram
|
||||
| --- | --- | --- | --- |
|
||||
| `reason` | string | 否 | 拒绝原因,只进入 IM/outbox payload |
|
||||
|
||||
## 9. 数据表
|
||||
解除请求体:
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `command_id` | string | 是 | 解除关系幂等键,同一次重试必须复用 |
|
||||
|
||||
解除返回值:
|
||||
|
||||
| 字段 | 说明 |
|
||||
| --- | --- |
|
||||
| `relationship` | 已解除的关系,`status=ended` |
|
||||
| `breakup_cost_coins` | 本次实际扣除金币数 |
|
||||
| `wallet_transaction_id` | 钱包扣费交易 ID;费用为 0 时为空 |
|
||||
| `coin_balance_after` | 扣费后金币余额;费用为 0 时为 0 |
|
||||
|
||||
## 10. 数据表
|
||||
|
||||
表放在 `user-service` 库,所有表带 `app_code`,时间使用 UTC epoch milliseconds。
|
||||
|
||||
| 表 | 说明 |
|
||||
| --- | --- |
|
||||
| `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` | 双人关系事实 |
|
||||
| `user_cp_relationship_breakups` | 解除关系 pending/confirmed/canceled 占位和幂等事实 |
|
||||
| `user_cp_level_reward_grants` | 等级奖励发放幂等 |
|
||||
| `user_outbox` | CP 事件事实 |
|
||||
|
||||
关键唯一约束:
|
||||
|
||||
- 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')`。
|
||||
- 解除关系按 `command_id` 幂等;同一关系同一时间只能有一条 pending 解除占位。
|
||||
|
||||
## 10. 相关IM
|
||||
## 11. 相关IM
|
||||
|
||||
| IM | 通道 | 发送时机 |
|
||||
| --- | --- | --- |
|
||||
| `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 发给 A,payload 带 `c2c_notice_role=accepted_to_requester`、`message_text=我同意了你` |
|
||||
| `cp_application_accepted` | C2C | B 同意后,A 发给 B,payload 带 `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` | 区域群 | 关系建立后发送区域播报 |
|
||||
| 无 | 无 | 解除关系成功不发 IM,App 根据解除接口返回刷新 |
|
||||
|
||||
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. 错误码
|
||||
## 12. 错误码
|
||||
|
||||
| 场景 | code | 说明 |
|
||||
| --- | --- | --- |
|
||||
@ -220,9 +282,11 @@ C2C 使用腾讯云 IM `From_Account` 表达真实操作人:申请为 A,处
|
||||
| 申请已过期 | `CONFLICT` | 不能再同意;内部 gRPC 语义是 FailedPrecondition |
|
||||
| 已有关系 | `CONFLICT` | A/B 任一方已经有 active 关系 |
|
||||
| 无权限处理 | `PERMISSION_DENIED` | 不是申请目标用户 |
|
||||
| 余额不足 | `INSUFFICIENT_BALANCE` | 解除关系扣金币失败 |
|
||||
| 关系不可解除 | `CONFLICT` | 关系不是 active,或已有 pending 解除 |
|
||||
| 幂等冲突 | `CONFLICT` | command_id 或事件消费 payload 不一致 |
|
||||
|
||||
## 12. 验证项
|
||||
## 13. 验证项
|
||||
|
||||
- CP 礼物发送成功后,`RoomGiftSent` 带 `gift_type_code=cp`、`cp_relation_type`、礼物 icon 快照。
|
||||
- A 给 B 连发 `cp`、`brother`、`sister` 三种礼物,会生成三条 pending 申请。
|
||||
@ -230,4 +294,5 @@ C2C 使用腾讯云 IM `From_Account` 表达真实操作人:申请为 A,处
|
||||
- A/B 建立关系后,A 或 B 都不能再和第三人建立 active 关系。
|
||||
- pending 申请超过默认 24 小时后不能接受。
|
||||
- active 关系双方互送任意礼物,亲密值按 `heat_value` 增加。
|
||||
- active 关系解除会扣除后台配置的金币费用,扣费失败不改变关系状态。
|
||||
- 同意、拒绝、关系成立的 IM 都写入 `notice_delivery_events`,失败可重试。
|
||||
|
||||
@ -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,7 +274,14 @@ 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,
|
||||
"breakup_cost_coins": 88,
|
||||
"formed_at_ms": 1779259100000,
|
||||
"ended_at_ms": 0,
|
||||
"updated_at_ms": 1779259200000
|
||||
}
|
||||
],
|
||||
@ -284,14 +291,66 @@ 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 不要用本地常量、礼物价格或后台配置接口自行推算进度条和“距离下一级”。
|
||||
- `breakup_cost_coins` 是解除当前关系需要支付的金币数,弹窗和二次确认直接使用该值。
|
||||
|
||||
## 7. 相关IM
|
||||
## 7. 解除关系
|
||||
|
||||
地址:
|
||||
|
||||
```http
|
||||
POST /api/v1/cp/relationships/{relationship_id}/break
|
||||
Authorization: Bearer <access_token>
|
||||
X-App-Code: lalu
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"command_id": "break_cp_rel_10001_10002_1779259300000"
|
||||
}
|
||||
```
|
||||
|
||||
参数:
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `relationship_id` | string | 是 | URL path,关系 ID |
|
||||
| `command_id` | string | 是 | 解除关系幂等键,同一次重试必须复用 |
|
||||
|
||||
返回值:
|
||||
|
||||
```json
|
||||
{
|
||||
"relationship": {
|
||||
"relationship_id": "cp_rel_10001_10002",
|
||||
"relation_type": "cp",
|
||||
"status": "ended",
|
||||
"breakup_cost_coins": 88,
|
||||
"formed_at_ms": 1779259100000,
|
||||
"ended_at_ms": 1779259300000,
|
||||
"updated_at_ms": 1779259300000
|
||||
},
|
||||
"breakup_cost_coins": 88,
|
||||
"wallet_transaction_id": "wallet_tx_break_1",
|
||||
"coin_balance_after": 912
|
||||
}
|
||||
```
|
||||
|
||||
说明:
|
||||
|
||||
- 解除前先展示 `/api/v1/cp/relationships` 返回的 `breakup_cost_coins`。
|
||||
- 金币不足时接口返回 `INSUFFICIENT_BALANCE`,关系仍保持 active。
|
||||
- 解除成功不发 C2C、房间 IM 或区域 IM;Flutter 使用接口返回刷新资料卡和关系列表。
|
||||
|
||||
## 8. 相关IM
|
||||
|
||||
所有 IM 都是 Tencent `TIMCustomElem`,`MsgContent.Desc` 等于下表事件名,`MsgContent.Data` 是 JSON 字符串。客户端按 `event_id` 去重。
|
||||
|
||||
### 7.1 收到关系申请
|
||||
### 8.1 收到关系申请
|
||||
|
||||
房间 IM 和 C2C IM 都会收到:
|
||||
|
||||
@ -345,9 +404,9 @@ Data:
|
||||
- C2C IM 在私聊里显示同意/拒绝按钮。
|
||||
- `expires_at_ms <= server_time_ms` 时按钮置灰并刷新申请列表。
|
||||
|
||||
### 7.2 申请被同意
|
||||
### 8.2 申请被同意
|
||||
|
||||
C2C 和房间 IM 都会收到:
|
||||
C2C 会收到双方互发的两条消息,房间 IM 仍然只给申请发起人 A 展示:
|
||||
|
||||
| 字段 | 值 |
|
||||
| --- | --- |
|
||||
@ -362,13 +421,20 @@ 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`。
|
||||
- B 操作成功后也刷新关系列表。
|
||||
- 同一对用户其他 pending 申请需要置灰。
|
||||
|
||||
### 7.3 申请被拒绝
|
||||
### 8.3 申请被拒绝
|
||||
|
||||
C2C 和房间 IM 都会收到:
|
||||
|
||||
@ -386,7 +452,7 @@ Data 字段和申请消息一致,差异是:
|
||||
| `decided_at_ms` | 处理时间 |
|
||||
| `recipient_user_id` | 房间 IM 中为 A 的 user_id |
|
||||
|
||||
### 7.4 区域关系成立播报
|
||||
### 8.4 区域关系成立播报
|
||||
|
||||
用户组成任意关系后,会发送区域 IM:
|
||||
|
||||
@ -453,3 +519,4 @@ Data:
|
||||
- 同一个 `application_id` 在房间和 C2C 里可能都出现,按钮状态要共享。
|
||||
- 申请过期时间以 `expires_at_ms` 为准。
|
||||
- 亲密值和等级以后端关系接口返回为准,不在 Flutter 里按礼物价格推算。
|
||||
- 进度条和距离下一级只使用关系接口返回的阈值字段;字段缺失时不展示,不要伪造 `next_level_threshold`、`needed_for_next_level` 或进度百分比。
|
||||
|
||||
@ -34,6 +34,18 @@
|
||||
| `levels[].in_room_rewards` | 在房奖励候选 |
|
||||
| `levels[].top1_rewards` | 贡献第一奖励候选 |
|
||||
| `levels[].igniter_rewards` | 点火人奖励候选 |
|
||||
| `levels[].*_rewards[].resource_group_id` | 奖励资源组 ID |
|
||||
| `levels[].*_rewards[].resource_group` | 奖励资源组详情 |
|
||||
| `levels[].*_rewards[].resource_group.items` | 资源组明细 |
|
||||
| `levels[].*_rewards[].resource_group.items[].item_type` | `wallet_asset` 或 `resource` |
|
||||
| `levels[].*_rewards[].resource_group.items[].wallet_asset_type` | 金币等奖励资产类型,例如 `COIN` |
|
||||
| `levels[].*_rewards[].resource_group.items[].wallet_asset_amount` | 金币等奖励数量 |
|
||||
| `levels[].*_rewards[].resource_group.items[].resource` | 聊天气泡、麦位特效等资源素材 |
|
||||
| `levels[].*_rewards[].resource_group.items[].resource.resource_type` | 资源类型,例如 `chat_bubble`、`mic_effect` |
|
||||
| `levels[].*_rewards[].resource_group.items[].resource.asset_url` | 资源主素材 |
|
||||
| `levels[].*_rewards[].resource_group.items[].resource.preview_url` | 资源预览图 |
|
||||
| `levels[].*_rewards[].resource_group.items[].resource.animation_url` | 资源动效 |
|
||||
| `levels[].*_rewards[].resource_group.items[].duration_ms` | 资源有效期毫秒,`0` 表示永久 |
|
||||
| `state.current_level` | 当前正在累计的等级 |
|
||||
| `state.current_fuel` | 当前等级已累计燃料 |
|
||||
| `state.fuel_threshold` | 当前等级阈值 |
|
||||
@ -48,8 +60,12 @@
|
||||
| `state.pending_launches[].igniter_user_id` | 点火人 |
|
||||
| `state.pending_launches[].cover_url` | 小封面图 |
|
||||
| `state.last_rewards` | 最近一次发射奖励结果 |
|
||||
| `state.last_rewards[].resource_group_id` | 本次中奖资源组 ID |
|
||||
| `state.last_rewards[].resource_group` | 本次中奖资源组详情,字段同上 |
|
||||
| `server_time_ms` | 服务端 UTC 毫秒 |
|
||||
|
||||
`levels[].*_rewards` 表示 `in_room_rewards`、`top1_rewards`、`igniter_rewards` 三个奖励池。客户端展示金币数量、奖励天数、聊天气泡、麦位特效时,直接读 `resource_group.items`。
|
||||
|
||||
## 送礼
|
||||
|
||||
地址:`POST /api/v1/rooms/gift/send`
|
||||
@ -258,6 +274,8 @@
|
||||
| `rewards[].grant_id` | 发奖回执 |
|
||||
| `rewards[].status` | 发奖状态 |
|
||||
|
||||
奖励 IM 用于通知本次哪些人中奖。需要渲染金币数量、奖励天数、聊天气泡或麦位特效素材时,收到 IM 后调用 `GET /api/v1/rooms/{room_id}/rocket`,读取 `state.last_rewards[].resource_group`。
|
||||
|
||||
## 客户端处理
|
||||
|
||||
- 进房先调 `GET /api/v1/rooms/{room_id}/rocket`。
|
||||
|
||||
@ -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: 资源 ID;wallet-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:
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
## 服务边界
|
||||
|
||||
- `room-service` 是火箭进度、点火、待发射队列和奖励结算 owner。
|
||||
- `gateway-service` 只提供 App HTTP 入口和鉴权后的协议转换。
|
||||
- `gateway-service` 只提供 App HTTP 入口和鉴权后的协议转换;`GET /api/v1/rooms/{room_id}/rocket` 会按奖励 `resource_group_id` 查询 wallet-service 并嵌入 `resource_group.items`。
|
||||
- `server/admin` 只读写 `room-service` 内部配置接口,不直连 room 数据库。
|
||||
- `wallet-service` 负责资源组发放。
|
||||
- `activity-service` 负责区域/全局飘屏和带用户资料的奖励房间 IM。
|
||||
@ -92,6 +92,7 @@
|
||||
| `RoomRocketRewardGranted` | `room_rocket_reward_granted` 房间奖励 IM | activity-service |
|
||||
|
||||
奖励 IM 由 activity-service 查询用户公开资料后组装,包含头像、昵称、短 ID 和奖励信息。room-service 不反查 user-service。
|
||||
奖励资源组内容不写入 Room Cell;客户端需要金币数量、有效期、聊天气泡或麦位特效素材时,通过 rocket GET 接口读取展开后的 `resource_group`。
|
||||
|
||||
## 高频处理
|
||||
|
||||
|
||||
@ -86,6 +86,29 @@ func RegionBroadcastGroupIDWithPrefix(prefix string, value string, regionID int6
|
||||
return groupID, nil
|
||||
}
|
||||
|
||||
// RoomGroupID 生成腾讯云 IM 房间群 ID。
|
||||
func RoomGroupID(value string) (string, error) {
|
||||
return RoomGroupIDWithPrefix("", value)
|
||||
}
|
||||
|
||||
// RoomGroupIDWithPrefix 生成带环境前缀的腾讯云 IM 房间群 ID。
|
||||
func RoomGroupIDWithPrefix(prefix string, value string) (string, error) {
|
||||
prefix = strings.TrimSpace(prefix)
|
||||
if err := validateGroupIDPrefix(prefix); err != nil {
|
||||
return "", err
|
||||
}
|
||||
room := strings.TrimSpace(value)
|
||||
if !roomid.ValidStringID(room) {
|
||||
return "", fmt.Errorf("room_id is invalid for group_id")
|
||||
}
|
||||
// 房间群 GroupID 必须和内部 room_id 保持一致;线上已有腾讯 IM 群也是这个 ID,不能在投递边界改写。
|
||||
groupID := prefix + room
|
||||
if len(groupID) > roomid.MaxStringIDLength {
|
||||
return "", fmt.Errorf("room group_id is too long")
|
||||
}
|
||||
return groupID, nil
|
||||
}
|
||||
|
||||
// Parse 在调用任何 room guard 前完成 GroupID 分类。
|
||||
// 解析顺序必须先播报群、后房间群;否则 hy_<app>_bc_* 这类合法短字符串可能被 roomid 规则吞掉。
|
||||
func Parse(groupID string) ParsedGroup {
|
||||
@ -101,8 +124,8 @@ func Parse(groupID string) ParsedGroup {
|
||||
return ParsedGroup{Kind: KindUnknown}
|
||||
}
|
||||
|
||||
if roomid.ValidStringID(groupID) {
|
||||
return ParsedGroup{Kind: KindRoom, RoomID: groupID}
|
||||
if roomID, ok := RoomIDFromRoomGroupID(groupID); ok {
|
||||
return ParsedGroup{Kind: KindRoom, RoomID: roomID}
|
||||
}
|
||||
return ParsedGroup{Kind: KindUnknown}
|
||||
}
|
||||
@ -118,17 +141,32 @@ func ParseWithPrefix(groupID string, prefix string) ParsedGroup {
|
||||
return Parse(groupID)
|
||||
}
|
||||
if after, ok := strings.CutPrefix(groupID, prefix); ok {
|
||||
return parseBroadcastGroupID(after)
|
||||
if parsed := parseBroadcastGroupID(after); parsed.Kind != KindUnknown {
|
||||
return parsed
|
||||
}
|
||||
if roomID, ok := RoomIDFromRoomGroupID(after); ok {
|
||||
return ParsedGroup{Kind: KindRoom, RoomID: roomID}
|
||||
}
|
||||
return ParsedGroup{Kind: KindUnknown}
|
||||
}
|
||||
if strings.HasPrefix(groupID, "hy_") && strings.Contains(groupID, "_bc_") {
|
||||
return ParsedGroup{Kind: KindUnknown}
|
||||
}
|
||||
if roomid.ValidStringID(groupID) {
|
||||
return ParsedGroup{Kind: KindRoom, RoomID: groupID}
|
||||
if roomID, ok := RoomIDFromRoomGroupID(groupID); ok {
|
||||
return ParsedGroup{Kind: KindRoom, RoomID: roomID}
|
||||
}
|
||||
return ParsedGroup{Kind: KindUnknown}
|
||||
}
|
||||
|
||||
// RoomIDFromRoomGroupID 把腾讯云 IM 房间群 ID 还原成内部 room_id。
|
||||
func RoomIDFromRoomGroupID(groupID string) (string, bool) {
|
||||
groupID = strings.TrimSpace(groupID)
|
||||
if !roomid.ValidStringID(groupID) {
|
||||
return "", false
|
||||
}
|
||||
return groupID, true
|
||||
}
|
||||
|
||||
func parseBroadcastGroupID(groupID string) ParsedGroup {
|
||||
if strings.HasPrefix(groupID, "hy_") && strings.HasSuffix(groupID, globalSuffix) {
|
||||
app := strings.TrimSuffix(strings.TrimPrefix(groupID, "hy_"), globalSuffix)
|
||||
|
||||
@ -43,6 +43,21 @@ func TestParseRoomAndRejectInvalidBroadcast(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRoomGroupIDPreservesRoomID(t *testing.T) {
|
||||
roomID := "lalu_2963d975-316e-444d-ad5c-f82fc36c28ca"
|
||||
groupID, err := RoomGroupID(roomID)
|
||||
if err != nil {
|
||||
t.Fatalf("RoomGroupID failed: %v", err)
|
||||
}
|
||||
if groupID != roomID {
|
||||
t.Fatalf("room group mismatch: %s", groupID)
|
||||
}
|
||||
parsed := Parse(groupID)
|
||||
if parsed.Kind != KindRoom || parsed.RoomID != roomID {
|
||||
t.Fatalf("room group parse mismatch: %+v", parsed)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBroadcastGroupIDWithPrefix(t *testing.T) {
|
||||
global, err := GlobalBroadcastGroupIDWithPrefix("test_", "lalu")
|
||||
if err != nil {
|
||||
@ -73,4 +88,14 @@ func TestBroadcastGroupIDWithPrefix(t *testing.T) {
|
||||
if parsed := ParseWithPrefix("room-1001", "test_"); parsed.Kind != KindRoom || parsed.RoomID != "room-1001" {
|
||||
t.Fatalf("room parse with prefix mismatch: %+v", parsed)
|
||||
}
|
||||
prefixedRoom, err := RoomGroupIDWithPrefix("test_", "lalu_2963d975-316e-444d-ad5c-f82fc36c28ca")
|
||||
if err != nil {
|
||||
t.Fatalf("RoomGroupIDWithPrefix failed: %v", err)
|
||||
}
|
||||
if prefixedRoom != "test_lalu_2963d975-316e-444d-ad5c-f82fc36c28ca" {
|
||||
t.Fatalf("prefixed room group mismatch: %s", prefixedRoom)
|
||||
}
|
||||
if parsed := ParseWithPrefix(prefixedRoom, "test_"); parsed.Kind != KindRoom || parsed.RoomID != "lalu_2963d975-316e-444d-ad5c-f82fc36c28ca" {
|
||||
t.Fatalf("prefixed room parse mismatch: %+v", parsed)
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@ import (
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
@ -94,6 +95,7 @@ type AccountProfile struct {
|
||||
|
||||
// RoomEvent 是 room-service 发给腾讯 IM 的房间系统消息负载。
|
||||
type RoomEvent struct {
|
||||
GroupID string `json:"-"`
|
||||
EventID string `json:"event_id"`
|
||||
RoomID string `json:"room_id"`
|
||||
EventType string `json:"event_type"`
|
||||
@ -220,6 +222,10 @@ func (c *RESTClient) PublishRoomEvent(ctx context.Context, event RoomEvent) erro
|
||||
// event_id 是客户端去重、日志追踪和补偿投递判断的共同锚点。
|
||||
return fmt.Errorf("room event is incomplete")
|
||||
}
|
||||
groupID := strings.TrimSpace(event.GroupID)
|
||||
if groupID == "" {
|
||||
groupID = event.RoomID
|
||||
}
|
||||
|
||||
eventPayload, err := json.Marshal(event)
|
||||
if err != nil {
|
||||
@ -227,7 +233,7 @@ func (c *RESTClient) PublishRoomEvent(ctx context.Context, event RoomEvent) erro
|
||||
}
|
||||
|
||||
return c.PublishGroupCustomMessage(ctx, CustomGroupMessage{
|
||||
GroupID: event.RoomID,
|
||||
GroupID: groupID,
|
||||
EventID: event.EventID,
|
||||
Desc: event.EventType,
|
||||
Ext: "room_system_message",
|
||||
@ -473,6 +479,31 @@ type restResponse struct {
|
||||
ErrorCode int `json:"ErrorCode"`
|
||||
}
|
||||
|
||||
// RESTError 保留腾讯云 IM 业务错误码,让上层能区分缺群、参数错误和限流。
|
||||
type RESTError struct {
|
||||
Code int
|
||||
Info string
|
||||
}
|
||||
|
||||
// Error 实现 error,沿用既有日志格式,避免线上检索规则失效。
|
||||
func (e RESTError) Error() string {
|
||||
return fmt.Sprintf("tencent im rest failed: code=%d info=%s", e.Code, e.Info)
|
||||
}
|
||||
|
||||
// IsRESTErrorCode 判断错误链中是否包含指定腾讯云 IM 业务错误码。
|
||||
func IsRESTErrorCode(err error, codes ...int) bool {
|
||||
var restErr RESTError
|
||||
if !errors.As(err, &restErr) {
|
||||
return false
|
||||
}
|
||||
for _, code := range codes {
|
||||
if restErr.Code == code {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (r restResponse) err() error {
|
||||
if r.ErrorCode == 0 && strings.EqualFold(r.ActionStatus, "OK") {
|
||||
return nil
|
||||
@ -482,7 +513,7 @@ func (r restResponse) err() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("tencent im rest failed: code=%d info=%s", r.ErrorCode, r.ErrorInfo)
|
||||
return RESTError{Code: r.ErrorCode, Info: r.ErrorInfo}
|
||||
}
|
||||
|
||||
func isAccountAlreadyImported(response restResponse) bool {
|
||||
|
||||
@ -58,6 +58,7 @@ func TestRESTClientPublishRoomEventBuildsCustomMessage(t *testing.T) {
|
||||
})
|
||||
|
||||
err := client.PublishRoomEvent(context.Background(), RoomEvent{
|
||||
GroupID: "room_1001",
|
||||
EventID: "evt-1",
|
||||
RoomID: "room-1001",
|
||||
EventType: "room_gift_sent",
|
||||
@ -73,7 +74,7 @@ func TestRESTClientPublishRoomEventBuildsCustomMessage(t *testing.T) {
|
||||
if capturedPath != "/"+sendGroupMsgCommand {
|
||||
t.Fatalf("path mismatch: got %q", capturedPath)
|
||||
}
|
||||
if capturedBody["GroupId"] != "room-1001" {
|
||||
if capturedBody["GroupId"] != "room_1001" {
|
||||
t.Fatalf("group id mismatch: %+v", capturedBody)
|
||||
}
|
||||
body, ok := capturedBody["MsgBody"].([]any)
|
||||
@ -91,6 +92,9 @@ func TestRESTClientPublishRoomEventBuildsCustomMessage(t *testing.T) {
|
||||
if !strings.Contains(content["Data"].(string), `"event_id":"evt-1"`) {
|
||||
t.Fatalf("event payload missing event_id: %s", content["Data"])
|
||||
}
|
||||
if !strings.Contains(content["Data"].(string), `"room_id":"room-1001"`) {
|
||||
t.Fatalf("event payload must keep internal room_id: %s", content["Data"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestRESTClientPublishGroupCustomMessageBuildsBroadcastPayload(t *testing.T) {
|
||||
|
||||
@ -561,6 +561,7 @@ func roomPinFromProto(input *roomv1.AdminRoomPin) RoomPin {
|
||||
|
||||
func roomRocketConfigToProto(input RoomRocketConfig) *roomv1.AdminRoomRocketConfig {
|
||||
return &roomv1.AdminRoomRocketConfig{
|
||||
AppCode: input.AppCode,
|
||||
Enabled: input.Enabled,
|
||||
FuelSource: input.FuelSource,
|
||||
LaunchDelayMs: input.LaunchDelayMS,
|
||||
|
||||
10
server/admin/internal/integration/roomclient/client_test.go
Normal file
10
server/admin/internal/integration/roomclient/client_test.go
Normal file
@ -0,0 +1,10 @@
|
||||
package roomclient
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestRoomRocketConfigToProtoKeepsAppCode(t *testing.T) {
|
||||
config := roomRocketConfigToProto(RoomRocketConfig{AppCode: "lalu"})
|
||||
if config.GetAppCode() != "lalu" {
|
||||
t.Fatalf("proto app code mismatch: %q", config.GetAppCode())
|
||||
}
|
||||
}
|
||||
@ -52,6 +52,7 @@ type relationDTO struct {
|
||||
DisplayName string `json:"displayName"`
|
||||
MaxCountPerUser int32 `json:"maxCountPerUser"`
|
||||
ApplicationExpireHours int32 `json:"applicationExpireHours"`
|
||||
BreakupCostCoins int64 `json:"breakupCostCoins"`
|
||||
Status string `json:"status"`
|
||||
Levels []levelDTO `json:"levels"`
|
||||
UpdatedAtMS int64 `json:"updatedAtMs"`
|
||||
@ -109,15 +110,16 @@ func (s *Service) UpdateConfig(ctx context.Context, appCode string, req updateRe
|
||||
for _, relation := range relations {
|
||||
if _, err := tx.ExecContext(ctx, `
|
||||
INSERT INTO user_cp_relation_configs (
|
||||
app_code, relation_type, max_count_per_user, application_expire_hours,
|
||||
app_code, relation_type, max_count_per_user, application_expire_hours, breakup_cost_coins,
|
||||
status, created_at_ms, updated_at_ms
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
max_count_per_user = VALUES(max_count_per_user),
|
||||
application_expire_hours = VALUES(application_expire_hours),
|
||||
breakup_cost_coins = VALUES(breakup_cost_coins),
|
||||
status = VALUES(status),
|
||||
updated_at_ms = VALUES(updated_at_ms)`,
|
||||
appCode, relation.RelationType, relation.MaxCountPerUser, relation.ApplicationExpireHours,
|
||||
appCode, relation.RelationType, relation.MaxCountPerUser, relation.ApplicationExpireHours, relation.BreakupCostCoins,
|
||||
relation.Status, nowMs, nowMs,
|
||||
); err != nil {
|
||||
return configDTO{}, err
|
||||
@ -149,7 +151,7 @@ func (s *Service) UpdateConfig(ctx context.Context, appCode string, req updateRe
|
||||
|
||||
func (s *Service) listRelations(ctx context.Context, appCode string) ([]relationDTO, error) {
|
||||
rows, err := s.db.QueryContext(ctx, `
|
||||
SELECT relation_type, max_count_per_user, application_expire_hours, status, updated_at_ms
|
||||
SELECT relation_type, max_count_per_user, application_expire_hours, breakup_cost_coins, status, updated_at_ms
|
||||
FROM user_cp_relation_configs
|
||||
WHERE app_code = ?`, appCode)
|
||||
if err != nil {
|
||||
@ -159,7 +161,7 @@ func (s *Service) listRelations(ctx context.Context, appCode string) ([]relation
|
||||
relationsByType := map[string]relationDTO{}
|
||||
for rows.Next() {
|
||||
var item relationDTO
|
||||
if err := rows.Scan(&item.RelationType, &item.MaxCountPerUser, &item.ApplicationExpireHours, &item.Status, &item.UpdatedAtMS); err != nil {
|
||||
if err := rows.Scan(&item.RelationType, &item.MaxCountPerUser, &item.ApplicationExpireHours, &item.BreakupCostCoins, &item.Status, &item.UpdatedAtMS); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
item.DisplayName = relationLabels[item.RelationType]
|
||||
@ -222,9 +224,9 @@ func (s *Service) ensureDefaultConfig(ctx context.Context, appCode string, nowMs
|
||||
}
|
||||
if _, err := s.db.ExecContext(ctx, `
|
||||
INSERT INTO user_cp_relation_configs (
|
||||
app_code, relation_type, max_count_per_user, application_expire_hours,
|
||||
app_code, relation_type, max_count_per_user, application_expire_hours, breakup_cost_coins,
|
||||
status, created_at_ms, updated_at_ms
|
||||
) VALUES (?, ?, ?, ?, 'active', ?, ?)
|
||||
) VALUES (?, ?, ?, ?, 0, 'active', ?, ?)
|
||||
ON DUPLICATE KEY UPDATE relation_type = VALUES(relation_type)`,
|
||||
appCode, relationType, maxCount, defaultExpireHours, nowMs, nowMs,
|
||||
); err != nil {
|
||||
@ -256,6 +258,7 @@ func (s *Service) ensureSchema(ctx context.Context) error {
|
||||
relation_type VARCHAR(32) NOT NULL,
|
||||
max_count_per_user INT NOT NULL,
|
||||
application_expire_hours INT NOT NULL,
|
||||
breakup_cost_coins BIGINT NOT NULL DEFAULT 0,
|
||||
status VARCHAR(32) NOT NULL DEFAULT 'active',
|
||||
created_at_ms BIGINT NOT NULL,
|
||||
updated_at_ms BIGINT NOT NULL,
|
||||
@ -263,6 +266,11 @@ func (s *Service) ensureSchema(ctx context.Context) error {
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci`); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.ensureColumn(ctx, "user_cp_relation_configs", "breakup_cost_coins", `
|
||||
ALTER TABLE user_cp_relation_configs
|
||||
ADD COLUMN breakup_cost_coins BIGINT NOT NULL DEFAULT 0 AFTER application_expire_hours`); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := s.db.ExecContext(ctx, `
|
||||
CREATE TABLE IF NOT EXISTS user_cp_level_rules (
|
||||
app_code VARCHAR(32) NOT NULL DEFAULT 'lalu',
|
||||
@ -280,6 +288,23 @@ func (s *Service) ensureSchema(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *Service) ensureColumn(ctx context.Context, tableName string, columnName string, alterSQL string) error {
|
||||
var found string
|
||||
err := s.db.QueryRowContext(ctx, `
|
||||
SELECT COLUMN_NAME
|
||||
FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ? AND COLUMN_NAME = ?
|
||||
LIMIT 1`, tableName, columnName).Scan(&found)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
if !errors.Is(err, sql.ErrNoRows) {
|
||||
return err
|
||||
}
|
||||
_, err = s.db.ExecContext(ctx, alterSQL)
|
||||
return err
|
||||
}
|
||||
|
||||
func normalizeRelations(input []relationDTO) ([]relationDTO, error) {
|
||||
if len(input) == 0 {
|
||||
return nil, fmt.Errorf("%w: 关系配置不能为空", ErrInvalidArgument)
|
||||
@ -305,6 +330,9 @@ func normalizeRelations(input []relationDTO) ([]relationDTO, error) {
|
||||
if relation.ApplicationExpireHours <= 0 {
|
||||
relation.ApplicationExpireHours = defaultExpireHours
|
||||
}
|
||||
if relation.BreakupCostCoins < 0 {
|
||||
return nil, fmt.Errorf("%w: %s解除费用不能小于0", ErrInvalidArgument, relationLabels[relation.RelationType])
|
||||
}
|
||||
relation.Status = defaultString(strings.TrimSpace(relation.Status), statusActive)
|
||||
relation.Levels = normalizeLevelDefaults(relation.Levels)
|
||||
if err := validateLevels(relation.Levels); err != nil {
|
||||
|
||||
@ -71,6 +71,7 @@ type giftDTO struct {
|
||||
PresentationJSON string `json:"presentationJson"`
|
||||
PriceVersion string `json:"priceVersion"`
|
||||
GiftTypeCode string `json:"giftTypeCode"`
|
||||
CPRelationType string `json:"cpRelationType"`
|
||||
ChargeAssetType string `json:"chargeAssetType"`
|
||||
CoinPrice int64 `json:"coinPrice"`
|
||||
EffectiveFromMS int64 `json:"effectiveFromMs"`
|
||||
@ -293,6 +294,7 @@ func giftFromProto(gift *walletv1.GiftConfig) giftDTO {
|
||||
PresentationJSON: gift.GetPresentationJson(),
|
||||
PriceVersion: gift.GetPriceVersion(),
|
||||
GiftTypeCode: gift.GetGiftTypeCode(),
|
||||
CPRelationType: gift.GetCpRelationType(),
|
||||
ChargeAssetType: gift.GetChargeAssetType(),
|
||||
CoinPrice: gift.GetCoinPrice(),
|
||||
EffectiveFromMS: gift.GetEffectiveFromMs(),
|
||||
|
||||
@ -131,6 +131,7 @@ type giftRequest struct {
|
||||
PresentationJSON string `json:"presentationJson"`
|
||||
PriceVersion string `json:"priceVersion"`
|
||||
GiftTypeCode string `json:"giftTypeCode"`
|
||||
CPRelationType string `json:"cpRelationType"`
|
||||
ChargeAssetType string `json:"chargeAssetType"`
|
||||
CoinPrice int64 `json:"coinPrice"`
|
||||
HeatValue int64 `json:"heatValue"` // 兼容旧后台请求;新后台不再展示或提交热度。
|
||||
@ -366,6 +367,7 @@ func (r giftRequest) createProto(c *gin.Context) *walletv1.CreateGiftConfigReque
|
||||
PresentationJson: strings.TrimSpace(r.PresentationJSON),
|
||||
PriceVersion: strings.TrimSpace(r.PriceVersion),
|
||||
GiftTypeCode: strings.TrimSpace(r.GiftTypeCode),
|
||||
CpRelationType: r.cpRelationType(),
|
||||
ChargeAssetType: strings.TrimSpace(r.ChargeAssetType),
|
||||
CoinPrice: r.CoinPrice,
|
||||
GiftPointAmount: 0,
|
||||
@ -391,6 +393,7 @@ func (r giftRequest) updateProto(c *gin.Context, giftID string) *walletv1.Update
|
||||
PresentationJson: strings.TrimSpace(r.PresentationJSON),
|
||||
PriceVersion: strings.TrimSpace(r.PriceVersion),
|
||||
GiftTypeCode: strings.TrimSpace(r.GiftTypeCode),
|
||||
CpRelationType: r.cpRelationType(),
|
||||
ChargeAssetType: strings.TrimSpace(r.ChargeAssetType),
|
||||
CoinPrice: r.CoinPrice,
|
||||
GiftPointAmount: 0,
|
||||
@ -404,6 +407,27 @@ func (r giftRequest) updateProto(c *gin.Context, giftID string) *walletv1.Update
|
||||
}
|
||||
}
|
||||
|
||||
func (r giftRequest) cpRelationType() string {
|
||||
if strings.TrimSpace(r.GiftTypeCode) != "cp" {
|
||||
return ""
|
||||
}
|
||||
// CP 关系类型最终由 wallet-service 写回 presentation_json;兼容旧前端只提交 JSON 的请求,避免保存时把兄弟/姐妹关系清掉。
|
||||
if value := strings.TrimSpace(r.CPRelationType); value != "" {
|
||||
return value
|
||||
}
|
||||
var payload map[string]any
|
||||
if err := json.Unmarshal([]byte(strings.TrimSpace(r.PresentationJSON)), &payload); err != nil {
|
||||
return ""
|
||||
}
|
||||
if value, ok := payload["cp_relation_type"].(string); ok {
|
||||
return strings.TrimSpace(value)
|
||||
}
|
||||
if value, ok := payload["cpRelationType"].(string); ok {
|
||||
return strings.TrimSpace(value)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (r giftTypeRequest) upsertProto(c *gin.Context, typeCode string) *walletv1.UpsertGiftTypeConfigRequest {
|
||||
return &walletv1.UpsertGiftTypeConfigRequest{
|
||||
RequestId: middleware.CurrentRequestID(c),
|
||||
|
||||
63
server/admin/internal/modules/resource/request_test.go
Normal file
63
server/admin/internal/modules/resource/request_test.go
Normal file
@ -0,0 +1,63 @@
|
||||
package resource
|
||||
|
||||
import (
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"hyapp-admin-server/internal/appctx"
|
||||
"hyapp-admin-server/internal/middleware"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func TestGiftRequestProtoKeepsExplicitCPRelationType(t *testing.T) {
|
||||
req := giftRequest{
|
||||
GiftTypeCode: "cp",
|
||||
CPRelationType: "brother",
|
||||
PresentationJSON: `{"cp_relation_type":"sister"}`,
|
||||
}
|
||||
|
||||
proto := req.updateProto(newGiftRequestTestContext(), "love")
|
||||
|
||||
if proto.GetCpRelationType() != "brother" {
|
||||
t.Fatalf("explicit cp relation type should win, got %q", proto.GetCpRelationType())
|
||||
}
|
||||
}
|
||||
|
||||
func TestGiftRequestProtoFallsBackToPresentationCPRelationType(t *testing.T) {
|
||||
req := giftRequest{
|
||||
GiftID: "love",
|
||||
GiftTypeCode: "cp",
|
||||
PresentationJSON: `{"cp_relation_type":"sister"}`,
|
||||
}
|
||||
|
||||
proto := req.createProto(newGiftRequestTestContext())
|
||||
|
||||
if proto.GetCpRelationType() != "sister" {
|
||||
t.Fatalf("presentation cp relation type should be forwarded, got %q", proto.GetCpRelationType())
|
||||
}
|
||||
}
|
||||
|
||||
func TestGiftRequestProtoClearsCPRelationTypeForNonCPGift(t *testing.T) {
|
||||
req := giftRequest{
|
||||
GiftTypeCode: "normal",
|
||||
CPRelationType: "brother",
|
||||
PresentationJSON: `{"cp_relation_type":"brother"}`,
|
||||
}
|
||||
|
||||
proto := req.updateProto(newGiftRequestTestContext(), "rose")
|
||||
|
||||
if proto.GetCpRelationType() != "" {
|
||||
t.Fatalf("non-cp gift should not forward cp relation type, got %q", proto.GetCpRelationType())
|
||||
}
|
||||
}
|
||||
|
||||
func newGiftRequestTestContext() *gin.Context {
|
||||
gin.SetMode(gin.TestMode)
|
||||
recorder := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(recorder)
|
||||
request := httptest.NewRequest("POST", "/admin/gifts", nil)
|
||||
c.Request = request.WithContext(appctx.WithContext(request.Context(), "lalu"))
|
||||
c.Set(middleware.ContextRequestID, "resource-test-request")
|
||||
return c
|
||||
}
|
||||
@ -7,6 +7,7 @@ import (
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"hyapp-admin-server/internal/appctx"
|
||||
"hyapp-admin-server/internal/integration/roomclient"
|
||||
)
|
||||
|
||||
@ -108,17 +109,7 @@ func (s *Service) UpdateConfig(ctx context.Context, req updateRoomRocketConfigRe
|
||||
if s.roomClient == nil {
|
||||
return RoomRocketConfig{}, fmt.Errorf("room service client is not configured")
|
||||
}
|
||||
config, err := normalizeConfig(RoomRocketConfig{
|
||||
Enabled: req.Enabled,
|
||||
FuelSource: req.FuelSource,
|
||||
LaunchDelayMS: req.LaunchDelayMS,
|
||||
BroadcastEnabled: req.BroadcastEnabled,
|
||||
BroadcastScope: req.BroadcastScope,
|
||||
BroadcastDelayMS: req.BroadcastDelayMS,
|
||||
RewardStackPolicy: req.RewardStackPolicy,
|
||||
Levels: req.Levels,
|
||||
GiftFuelRules: req.GiftFuelRules,
|
||||
})
|
||||
config, err := normalizeConfig(configFromUpdateRequest(ctx, req))
|
||||
if err != nil {
|
||||
return RoomRocketConfig{}, err
|
||||
}
|
||||
@ -132,6 +123,22 @@ func (s *Service) UpdateConfig(ctx context.Context, req updateRoomRocketConfigRe
|
||||
return configFromClient(saved)
|
||||
}
|
||||
|
||||
func configFromUpdateRequest(ctx context.Context, req updateRoomRocketConfigRequest) RoomRocketConfig {
|
||||
// 房间火箭配置按 App 隔离;更新请求体不让前端传 app_code,必须使用中间件写入的上下文值。
|
||||
return RoomRocketConfig{
|
||||
AppCode: appctx.FromContext(ctx),
|
||||
Enabled: req.Enabled,
|
||||
FuelSource: req.FuelSource,
|
||||
LaunchDelayMS: req.LaunchDelayMS,
|
||||
BroadcastEnabled: req.BroadcastEnabled,
|
||||
BroadcastScope: req.BroadcastScope,
|
||||
BroadcastDelayMS: req.BroadcastDelayMS,
|
||||
RewardStackPolicy: req.RewardStackPolicy,
|
||||
Levels: req.Levels,
|
||||
GiftFuelRules: req.GiftFuelRules,
|
||||
}
|
||||
}
|
||||
|
||||
func defaultConfig(appCode string) RoomRocketConfig {
|
||||
levels := make([]RoomRocketLevelConfig, 0, roomRocketLevelCount)
|
||||
for index, threshold := range defaultLevelFuelThresholds {
|
||||
|
||||
@ -1,10 +1,24 @@
|
||||
package roomrocket
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"hyapp-admin-server/internal/appctx"
|
||||
)
|
||||
|
||||
func TestConfigFromUpdateRequestUsesContextAppCode(t *testing.T) {
|
||||
request := updateRoomRocketConfigRequest{Levels: defaultConfig("ignored").Levels}
|
||||
config := configFromUpdateRequest(appctx.WithContext(context.Background(), "LALU"), request)
|
||||
if config.AppCode != "lalu" {
|
||||
t.Fatalf("config app code should come from context: %+v", config)
|
||||
}
|
||||
if _, err := normalizeConfig(config); err != nil {
|
||||
t.Fatalf("normalizeConfig should accept context app code: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalizeConfigDefaultsAndSortsLevels(t *testing.T) {
|
||||
config, err := normalizeConfig(RoomRocketConfig{
|
||||
AppCode: "lalu",
|
||||
|
||||
@ -58,7 +58,7 @@ rocketmq:
|
||||
secret_key: ""
|
||||
namespace: ""
|
||||
room_outbox:
|
||||
enabled: false
|
||||
enabled: true
|
||||
topic: "hyapp_room_outbox"
|
||||
consumer_group: "hyapp-activity-room-outbox"
|
||||
consumer_max_reconsume_times: 16
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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{}
|
||||
|
||||
@ -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())
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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{
|
||||
|
||||
@ -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='游戏启动会话表';
|
||||
|
||||
|
||||
@ -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, ","))
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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")
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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 访问、关注和好友关系能力的依赖。
|
||||
@ -60,6 +63,9 @@ type UserCPClient interface {
|
||||
AcceptCPApplication(ctx context.Context, req *userv1.AcceptCPApplicationRequest) (*userv1.AcceptCPApplicationResponse, error)
|
||||
RejectCPApplication(ctx context.Context, req *userv1.RejectCPApplicationRequest) (*userv1.RejectCPApplicationResponse, error)
|
||||
ListCPRelationships(ctx context.Context, req *userv1.ListCPRelationshipsRequest) (*userv1.ListCPRelationshipsResponse, error)
|
||||
PrepareBreakCPRelationship(ctx context.Context, req *userv1.PrepareBreakCPRelationshipRequest) (*userv1.PrepareBreakCPRelationshipResponse, error)
|
||||
ConfirmBreakCPRelationship(ctx context.Context, req *userv1.ConfirmBreakCPRelationshipRequest) (*userv1.ConfirmBreakCPRelationshipResponse, error)
|
||||
CancelBreakCPRelationship(ctx context.Context, req *userv1.CancelBreakCPRelationshipRequest) (*userv1.CancelBreakCPRelationshipResponse, error)
|
||||
}
|
||||
|
||||
// UserDeviceClient 抽象 gateway 对 user-service 设备 push token 的依赖。
|
||||
@ -74,6 +80,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 +140,10 @@ type grpcUserCountryQueryClient struct {
|
||||
client userv1.CountryQueryServiceClient
|
||||
}
|
||||
|
||||
type grpcUserRegionClient struct {
|
||||
client userv1.RegionAdminServiceClient
|
||||
}
|
||||
|
||||
type grpcUserHostClient struct {
|
||||
client userv1.UserHostServiceClient
|
||||
}
|
||||
@ -186,6 +201,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 +298,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)
|
||||
}
|
||||
@ -336,6 +370,18 @@ func (c *grpcUserCPClient) ListCPRelationships(ctx context.Context, req *userv1.
|
||||
return c.client.ListCPRelationships(ctx, req)
|
||||
}
|
||||
|
||||
func (c *grpcUserCPClient) PrepareBreakCPRelationship(ctx context.Context, req *userv1.PrepareBreakCPRelationshipRequest) (*userv1.PrepareBreakCPRelationshipResponse, error) {
|
||||
return c.client.PrepareBreakCPRelationship(ctx, req)
|
||||
}
|
||||
|
||||
func (c *grpcUserCPClient) ConfirmBreakCPRelationship(ctx context.Context, req *userv1.ConfirmBreakCPRelationshipRequest) (*userv1.ConfirmBreakCPRelationshipResponse, error) {
|
||||
return c.client.ConfirmBreakCPRelationship(ctx, req)
|
||||
}
|
||||
|
||||
func (c *grpcUserCPClient) CancelBreakCPRelationship(ctx context.Context, req *userv1.CancelBreakCPRelationshipRequest) (*userv1.CancelBreakCPRelationshipResponse, error) {
|
||||
return c.client.CancelBreakCPRelationship(ctx, req)
|
||||
}
|
||||
|
||||
func (c *grpcUserDeviceClient) BindPushToken(ctx context.Context, req *userv1.BindPushTokenRequest) (*userv1.BindPushTokenResponse, error) {
|
||||
return c.client.BindPushToken(ctx, req)
|
||||
}
|
||||
@ -352,6 +398,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)
|
||||
}
|
||||
|
||||
@ -23,6 +23,8 @@ 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)
|
||||
DebitCPBreakupFee(ctx context.Context, req *walletv1.DebitCPBreakupFeeRequest) (*walletv1.DebitCPBreakupFeeResponse, 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 +109,14 @@ func (c *grpcWalletClient) PurchaseVip(ctx context.Context, req *walletv1.Purcha
|
||||
return c.client.PurchaseVip(ctx, req)
|
||||
}
|
||||
|
||||
func (c *grpcWalletClient) DebitCPBreakupFee(ctx context.Context, req *walletv1.DebitCPBreakupFeeRequest) (*walletv1.DebitCPBreakupFeeResponse, error) {
|
||||
return c.client.DebitCPBreakupFee(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)
|
||||
}
|
||||
|
||||
@ -175,6 +175,7 @@ func (h *Handler) listWeeklyStarHistory(writer http.ResponseWriter, request *htt
|
||||
for _, cycle := range resp.GetCycles() {
|
||||
cycleItems = append(cycleItems, cycle.GetCycle())
|
||||
ids = append(ids, weeklyStarUserIDs(cycle.GetTopEntries(), nil)...)
|
||||
cycleItems = append(cycleItems, cycle.GetCycle())
|
||||
}
|
||||
users := h.resolveWeeklyStarUsers(request, ids)
|
||||
resourceGroups, ok := h.resolveWeeklyStarResourceGroups(writer, request, cycleItems...)
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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 ") {
|
||||
|
||||
@ -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(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -102,6 +102,7 @@ type UserHandlers struct {
|
||||
AcceptCPApplication http.HandlerFunc
|
||||
RejectCPApplication http.HandlerFunc
|
||||
ListCPRelationships http.HandlerFunc
|
||||
BreakCPRelationship http.HandlerFunc
|
||||
GetMyProfile http.HandlerFunc
|
||||
GetMyAppearance http.HandlerFunc
|
||||
GetUserAppearance http.HandlerFunc
|
||||
@ -116,6 +117,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 +236,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 {
|
||||
@ -356,6 +367,7 @@ func (r routes) registerUserRoutes() {
|
||||
r.profile("/cp/applications/{application_id}/accept", http.MethodPost, h.AcceptCPApplication)
|
||||
r.profile("/cp/applications/{application_id}/reject", http.MethodPost, h.RejectCPApplication)
|
||||
r.profile("/cp/relationships", http.MethodGet, h.ListCPRelationships)
|
||||
r.profile("/cp/relationships/{relationship_id}/break", http.MethodPost, h.BreakCPRelationship)
|
||||
r.profile("/appearance", http.MethodGet, h.GetMyAppearance)
|
||||
r.profile("/users/me/appearance", http.MethodGet, h.GetMyAppearance)
|
||||
r.profile("/users/me", "", h.GetMyProfile)
|
||||
@ -368,8 +380,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 +511,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)
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@ -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{}
|
||||
|
||||
@ -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
|
||||
@ -720,6 +735,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",
|
||||
@ -794,6 +810,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(),
|
||||
@ -984,6 +1047,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 {
|
||||
@ -1511,6 +1586,25 @@ func (f *fakeWalletClient) PurchaseVip(_ context.Context, req *walletv1.Purchase
|
||||
return &walletv1.PurchaseVipResponse{}, nil
|
||||
}
|
||||
|
||||
func (f *fakeWalletClient) DebitCPBreakupFee(context.Context, *walletv1.DebitCPBreakupFeeRequest) (*walletv1.DebitCPBreakupFeeResponse, error) {
|
||||
return &walletv1.DebitCPBreakupFeeResponse{}, 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 {
|
||||
@ -1816,6 +1910,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)
|
||||
}
|
||||
@ -2310,16 +2407,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()
|
||||
@ -2338,21 +2448,177 @@ 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 TestGetRoomRocketEmbedsRewardResourceGroups(t *testing.T) {
|
||||
queryClient := &fakeRoomQueryClient{rocketResp: &roomv1.GetRoomRocketResponse{
|
||||
Rocket: &roomv1.RoomRocketInfo{
|
||||
Enabled: true,
|
||||
LaunchDelayMs: 1200,
|
||||
Levels: []*roomv1.RoomRocketLevel{{
|
||||
Level: 1,
|
||||
FuelThreshold: 100,
|
||||
InRoomRewards: []*roomv1.RoomRocketRewardItem{{
|
||||
RewardItemId: "in-room-coin",
|
||||
ResourceGroupId: 23,
|
||||
Weight: 100,
|
||||
DisplayName: "1888 Coins",
|
||||
}},
|
||||
}},
|
||||
State: &roomv1.RoomRocketState{
|
||||
CurrentLevel: 1,
|
||||
CurrentFuel: 0,
|
||||
FuelThreshold: 100,
|
||||
Status: "charging",
|
||||
LastRewards: []*roomv1.RoomRocketRewardGrant{{
|
||||
RewardRole: "igniter",
|
||||
UserId: 42,
|
||||
RewardItemId: "chat-bubble",
|
||||
ResourceGroupId: 24,
|
||||
DisplayName: "Blue Bubble",
|
||||
Status: "succeeded",
|
||||
}},
|
||||
},
|
||||
},
|
||||
ServerTimeMs: 1_780_000_000_000,
|
||||
}}
|
||||
walletClient := &fakeWalletClient{resourceGroupsByID: map[int64]*walletv1.ResourceGroup{
|
||||
23: {
|
||||
GroupId: 23,
|
||||
GroupCode: "coin_1888",
|
||||
Name: "Coin Reward",
|
||||
Status: "active",
|
||||
Items: []*walletv1.ResourceGroupItem{{
|
||||
GroupItemId: 2301,
|
||||
ItemType: "wallet_asset",
|
||||
WalletAssetType: "COIN",
|
||||
WalletAssetAmount: 1888,
|
||||
Quantity: 1,
|
||||
}},
|
||||
},
|
||||
24: {
|
||||
GroupId: 24,
|
||||
GroupCode: "bubble_7d",
|
||||
Name: "Chat Bubble",
|
||||
Status: "active",
|
||||
Items: []*walletv1.ResourceGroupItem{{
|
||||
GroupItemId: 2401,
|
||||
ItemType: "resource",
|
||||
ResourceId: 8101,
|
||||
Resource: &walletv1.Resource{
|
||||
ResourceId: 8101,
|
||||
ResourceCode: "bubble_blue",
|
||||
ResourceType: "chat_bubble",
|
||||
Name: "Blue Bubble",
|
||||
Status: "active",
|
||||
AssetUrl: "https://cdn.example/bubble.png",
|
||||
},
|
||||
Quantity: 1,
|
||||
DurationMs: 604800000,
|
||||
}},
|
||||
},
|
||||
}}
|
||||
handler := NewHandlerWithClients(&fakeRoomClient{}, nil, nil, &fakeUserProfileClient{})
|
||||
handler.SetRoomQueryClient(queryClient)
|
||||
handler.SetWalletClient(walletClient)
|
||||
router := handler.Routes(auth.NewVerifier("secret"))
|
||||
request := httptest.NewRequest(http.MethodGet, "/api/v1/rooms/room_1001/rocket", nil)
|
||||
request.Header.Set("Authorization", "Bearer "+signGatewayToken(t, "secret", 42))
|
||||
request.Header.Set("X-Request-ID", "req-room-rocket")
|
||||
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 queryClient.lastRocket == nil || queryClient.lastRocket.GetViewerUserId() != 42 || queryClient.lastRocket.GetRoomId() != "room_1001" {
|
||||
t.Fatalf("GetRoomRocket must forward authenticated viewer and room: %+v", queryClient.lastRocket)
|
||||
}
|
||||
var envelope httpkit.ResponseEnvelope
|
||||
if err := json.NewDecoder(recorder.Body).Decode(&envelope); err != nil {
|
||||
t.Fatalf("decode rocket response failed: %v", err)
|
||||
}
|
||||
data := envelope.Data.(map[string]any)
|
||||
level := data["levels"].([]any)[0].(map[string]any)
|
||||
inRoomReward := level["in_room_rewards"].([]any)[0].(map[string]any)
|
||||
inRoomGroup := inRoomReward["resource_group"].(map[string]any)
|
||||
if inRoomReward["resource_group_id"].(float64) != 23 || inRoomGroup["group_id"].(float64) != 23 {
|
||||
t.Fatalf("rocket reward must embed resource group id 23: %+v", inRoomReward)
|
||||
}
|
||||
coinItem := inRoomGroup["items"].([]any)[0].(map[string]any)
|
||||
if coinItem["item_type"] != "wallet_asset" || coinItem["wallet_asset_type"] != "COIN" || coinItem["wallet_asset_amount"].(float64) != 1888 {
|
||||
t.Fatalf("rocket reward group must expose wallet asset amount: %+v", coinItem)
|
||||
}
|
||||
state := data["state"].(map[string]any)
|
||||
lastReward := state["last_rewards"].([]any)[0].(map[string]any)
|
||||
lastGroup := lastReward["resource_group"].(map[string]any)
|
||||
resourceItem := lastGroup["items"].([]any)[0].(map[string]any)
|
||||
resource := resourceItem["resource"].(map[string]any)
|
||||
if lastReward["resource_group_id"].(float64) != 24 || resource["resource_type"] != "chat_bubble" || resource["asset_url"] != "https://cdn.example/bubble.png" || resourceItem["duration_ms"].(float64) != 604800000 {
|
||||
t.Fatalf("rocket last reward must expose resource material and duration: reward=%+v item=%+v", lastReward, resourceItem)
|
||||
}
|
||||
}
|
||||
|
||||
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"))
|
||||
@ -4714,16 +4980,18 @@ func TestAchievementAndBadgeRoutesEmbedResourceMaterials(t *testing.T) {
|
||||
|
||||
func TestWeeklyStarCurrentEmbedsRewardResourceGroups(t *testing.T) {
|
||||
rewardResource := &walletv1.Resource{
|
||||
ResourceId: 8101,
|
||||
ResourceCode: "weekly_star_frame",
|
||||
ResourceType: "avatar_frame",
|
||||
Name: "Weekly Star Frame",
|
||||
Status: "active",
|
||||
Grantable: true,
|
||||
GrantStrategy: "new_entitlement",
|
||||
UsageScopes: []string{"profile"},
|
||||
AssetUrl: "https://cdn.example/weekly-star-frame.png",
|
||||
SortOrder: 10,
|
||||
ResourceId: 8101,
|
||||
ResourceCode: "weekly_star_frame",
|
||||
ResourceType: "avatar_frame",
|
||||
Name: "Weekly Star Frame",
|
||||
Status: "active",
|
||||
Grantable: true,
|
||||
GrantStrategy: "new_entitlement",
|
||||
WalletAssetType: "FRAME",
|
||||
UsageScopes: []string{"profile"},
|
||||
AssetUrl: "https://cdn.example/weekly-frame.png",
|
||||
PreviewUrl: "https://cdn.example/weekly-frame-preview.png",
|
||||
SortOrder: 10,
|
||||
}
|
||||
weeklyStarClient := &fakeWeeklyStarClient{
|
||||
currentResp: &activityv1.GetWeeklyStarCurrentResponse{
|
||||
@ -4733,33 +5001,47 @@ func TestWeeklyStarCurrentEmbedsRewardResourceGroups(t *testing.T) {
|
||||
RegionId: 686,
|
||||
Title: "Weekly Star",
|
||||
Status: "active",
|
||||
StartMs: 1778256000000,
|
||||
EndMs: 1778860800000,
|
||||
Gifts: []*activityv1.WeeklyStarGift{{GiftId: "308", SortOrder: 1}},
|
||||
Rewards: []*activityv1.WeeklyStarReward{{RankNo: 1, ResourceGroupId: 23}},
|
||||
StartMs: 1780848000000,
|
||||
EndMs: 1781452740000,
|
||||
Gifts: []*activityv1.WeeklyStarGift{{
|
||||
GiftId: "308",
|
||||
SortOrder: 1,
|
||||
}},
|
||||
Rewards: []*activityv1.WeeklyStarReward{{
|
||||
RankNo: 1,
|
||||
ResourceGroupId: 23,
|
||||
}},
|
||||
},
|
||||
TopEntries: []*activityv1.WeeklyStarLeaderboardEntry{{RankNo: 1, UserId: 88, Score: 120000, FirstScoredAtMs: 1778256001000, LastScoredAtMs: 1778256002000}},
|
||||
ServerTimeMs: 1778256003000,
|
||||
TopEntries: []*activityv1.WeeklyStarLeaderboardEntry{{
|
||||
RankNo: 1,
|
||||
UserId: 88,
|
||||
Score: 120000,
|
||||
FirstScoredAtMs: 1780849000000,
|
||||
LastScoredAtMs: 1780849900000,
|
||||
}},
|
||||
ServerTimeMs: 1780916324006,
|
||||
},
|
||||
}
|
||||
walletClient := &fakeWalletClient{
|
||||
resourceGroupsByID: map[int64]*walletv1.ResourceGroup{23: {
|
||||
GroupId: 23,
|
||||
GroupCode: "weekly_star_top1",
|
||||
Name: "Top 1 Resource Group",
|
||||
Status: "active",
|
||||
GroupId: 23,
|
||||
GroupCode: "weekly_star_top1",
|
||||
Name: "Top 1 Resource Group",
|
||||
Status: "active",
|
||||
Description: "Top 1 reward materials",
|
||||
Items: []*walletv1.ResourceGroupItem{{
|
||||
GroupItemId: 101,
|
||||
GroupItemId: 501,
|
||||
ItemType: "resource",
|
||||
ResourceId: rewardResource.GetResourceId(),
|
||||
ResourceId: 8101,
|
||||
Resource: rewardResource,
|
||||
Quantity: 1,
|
||||
DurationMs: 7 * 24 * int64(time.Hour/time.Millisecond),
|
||||
SortOrder: 10,
|
||||
SortOrder: 1,
|
||||
}},
|
||||
}},
|
||||
}
|
||||
userClient := &fakeUserProfileClient{
|
||||
regionID: 686,
|
||||
usersByID: map[int64]*userv1.User{
|
||||
42: {UserId: 42, DisplayUserId: "163000", Username: "viewer", RegionId: 686},
|
||||
88: {UserId: 88, DisplayUserId: "163088", Username: "weekly_ranker", Avatar: "https://cdn.example/ranker.png", RegionId: 686},
|
||||
@ -4775,27 +5057,28 @@ func TestWeeklyStarCurrentEmbedsRewardResourceGroups(t *testing.T) {
|
||||
recorder := httptest.NewRecorder()
|
||||
router.ServeHTTP(recorder, request)
|
||||
if recorder.Code != http.StatusOK {
|
||||
t.Fatalf("weekly star status mismatch: got %d body=%s", recorder.Code, recorder.Body.String())
|
||||
t.Fatalf("weekly star current status mismatch: got %d body=%s", recorder.Code, recorder.Body.String())
|
||||
}
|
||||
var envelope struct {
|
||||
Code string `json:"code"`
|
||||
Data struct {
|
||||
Cycle struct {
|
||||
Rewards []struct {
|
||||
RankNo int32 `json:"rank_no"`
|
||||
ResourceGroupID int64 `json:"resource_group_id"`
|
||||
ResourceGroup struct {
|
||||
GroupID int64 `json:"group_id"`
|
||||
Name string `json:"name"`
|
||||
Items []struct {
|
||||
ResourceID int64 `json:"resource_id"`
|
||||
Quantity int64 `json:"quantity"`
|
||||
DurationMS int64 `json:"duration_ms"`
|
||||
Resource struct {
|
||||
ResourceID int64 `json:"resource_id"`
|
||||
ResourceType string `json:"resource_type"`
|
||||
Name string `json:"name"`
|
||||
AssetURL string `json:"asset_url"`
|
||||
} `json:"resource"`
|
||||
Quantity int64 `json:"quantity"`
|
||||
DurationMS int64 `json:"duration_ms"`
|
||||
} `json:"items"`
|
||||
} `json:"resource_group"`
|
||||
} `json:"rewards"`
|
||||
@ -4808,10 +5091,10 @@ func TestWeeklyStarCurrentEmbedsRewardResourceGroups(t *testing.T) {
|
||||
} `json:"data"`
|
||||
}
|
||||
if err := json.NewDecoder(recorder.Body).Decode(&envelope); err != nil {
|
||||
t.Fatalf("decode weekly star response failed: %v", err)
|
||||
t.Fatalf("decode weekly star current response failed: %v", err)
|
||||
}
|
||||
if weeklyStarClient.lastCurrent == nil || weeklyStarClient.lastCurrent.GetUserId() != 42 || weeklyStarClient.lastCurrent.GetRegionId() != 686 {
|
||||
t.Fatalf("weekly star request mismatch: %+v", weeklyStarClient.lastCurrent)
|
||||
t.Fatalf("weekly star current request mismatch: %+v", weeklyStarClient.lastCurrent)
|
||||
}
|
||||
if envelope.Code != httpkit.CodeOK || len(envelope.Data.Cycle.Rewards) != 1 {
|
||||
t.Fatalf("weekly star response missing rewards: %+v", envelope)
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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) {
|
||||
@ -611,7 +740,41 @@ func (h *Handler) getRoomRocket(writer http.ResponseWriter, request *http.Reques
|
||||
httpkit.WriteRPCError(writer, request, err)
|
||||
return
|
||||
}
|
||||
httpkit.WriteOK(writer, request, roomRocketDataFromProto(resp))
|
||||
resourceGroups, ok := h.resolveRoomRocketResourceGroups(writer, request, resp)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
httpkit.WriteOK(writer, request, roomRocketDataFromProto(resp, resourceGroups))
|
||||
}
|
||||
|
||||
// resolveRoomRocketResourceGroups 展开奖励池和最近中奖结果引用的资源组。
|
||||
// room-service 只保存房间状态和奖励资源组 ID;gateway 在 HTTP 出口补齐素材和钱包资产明细,避免 Room Cell 持有展示快照。
|
||||
func (h *Handler) resolveRoomRocketResourceGroups(writer http.ResponseWriter, request *http.Request, resp *roomv1.GetRoomRocketResponse) (map[int64]*roomRocketResourceGroupData, bool) {
|
||||
ids := roomRocketResourceGroupIDs(resp)
|
||||
groups := make(map[int64]*roomRocketResourceGroupData, len(ids))
|
||||
if len(ids) == 0 {
|
||||
return groups, true
|
||||
}
|
||||
if h.walletClient == nil {
|
||||
httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
|
||||
return nil, false
|
||||
}
|
||||
for _, id := range ids {
|
||||
groupResp, err := h.walletClient.GetResourceGroup(request.Context(), &walletv1.GetResourceGroupRequest{
|
||||
RequestId: httpkit.RequestIDFromContext(request.Context()),
|
||||
AppCode: appcode.FromContext(request.Context()),
|
||||
GroupId: id,
|
||||
})
|
||||
if err != nil {
|
||||
httpkit.WriteRPCError(writer, request, err)
|
||||
return nil, false
|
||||
}
|
||||
group := roomRocketResourceGroupFromProto(groupResp.GetGroup())
|
||||
if group != nil && group.GroupID > 0 {
|
||||
groups[group.GroupID] = group
|
||||
}
|
||||
}
|
||||
return groups, true
|
||||
}
|
||||
|
||||
// handleRoomFollow 建立或取消当前用户对房间的关注关系。
|
||||
@ -697,14 +860,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)
|
||||
|
||||
@ -83,6 +83,21 @@ func TestCreateRoomDataIncludesExplicitIMGroupID(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRoomSeatDataIncludesGiftValue(t *testing.T) {
|
||||
seats := roomSeatDataFromSnapshot(&roomv1.RoomSnapshot{
|
||||
RoomId: "room-seat-gift-value",
|
||||
MicSeats: []*roomv1.SeatState{{
|
||||
SeatNo: 3,
|
||||
UserId: 1003,
|
||||
GiftValue: 367,
|
||||
}},
|
||||
})
|
||||
|
||||
if len(seats) != 1 || seats[0].SeatNo != 3 || seats[0].UserID != "1003" || seats[0].GiftValue != 367 {
|
||||
t.Fatalf("seat data must expose gift_value: %+v", seats)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRoomProfileAgeFromBirth(t *testing.T) {
|
||||
now := time.Date(2026, time.May, 27, 12, 0, 0, 0, time.UTC)
|
||||
tests := []struct {
|
||||
|
||||
@ -3,16 +3,28 @@ package roomapi
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"hyapp/pkg/imgroup"
|
||||
"hyapp/pkg/tencentrtc"
|
||||
|
||||
roomv1 "hyapp.local/api/proto/room/v1"
|
||||
walletv1 "hyapp.local/api/proto/wallet/v1"
|
||||
)
|
||||
|
||||
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 +49,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 {
|
||||
@ -191,11 +205,12 @@ type roomRocketLevelData struct {
|
||||
}
|
||||
|
||||
type roomRocketRewardData struct {
|
||||
RewardItemID string `json:"reward_item_id"`
|
||||
ResourceGroupID int64 `json:"resource_group_id"`
|
||||
Weight int64 `json:"weight,omitempty"`
|
||||
DisplayName string `json:"display_name,omitempty"`
|
||||
IconURL string `json:"icon_url,omitempty"`
|
||||
RewardItemID string `json:"reward_item_id"`
|
||||
ResourceGroupID int64 `json:"resource_group_id"`
|
||||
ResourceGroup *roomRocketResourceGroupData `json:"resource_group,omitempty"`
|
||||
Weight int64 `json:"weight,omitempty"`
|
||||
DisplayName string `json:"display_name,omitempty"`
|
||||
IconURL string `json:"icon_url,omitempty"`
|
||||
}
|
||||
|
||||
type roomRocketStateData struct {
|
||||
@ -229,14 +244,59 @@ type roomRocketPendingLaunchData struct {
|
||||
}
|
||||
|
||||
type roomRocketRewardGrantData struct {
|
||||
RewardRole string `json:"reward_role"`
|
||||
UserID string `json:"user_id"`
|
||||
RewardItemID string `json:"reward_item_id"`
|
||||
ResourceGroupID int64 `json:"resource_group_id"`
|
||||
DisplayName string `json:"display_name,omitempty"`
|
||||
IconURL string `json:"icon_url,omitempty"`
|
||||
GrantID string `json:"grant_id,omitempty"`
|
||||
Status string `json:"status,omitempty"`
|
||||
RewardRole string `json:"reward_role"`
|
||||
UserID string `json:"user_id"`
|
||||
RewardItemID string `json:"reward_item_id"`
|
||||
ResourceGroupID int64 `json:"resource_group_id"`
|
||||
ResourceGroup *roomRocketResourceGroupData `json:"resource_group,omitempty"`
|
||||
DisplayName string `json:"display_name,omitempty"`
|
||||
IconURL string `json:"icon_url,omitempty"`
|
||||
GrantID string `json:"grant_id,omitempty"`
|
||||
Status string `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
type roomRocketResourceData struct {
|
||||
ResourceID int64 `json:"resource_id"`
|
||||
ResourceCode string `json:"resource_code"`
|
||||
ResourceType string `json:"resource_type"`
|
||||
Name string `json:"name"`
|
||||
Status string `json:"status"`
|
||||
Grantable bool `json:"grantable"`
|
||||
GrantStrategy string `json:"grant_strategy"`
|
||||
WalletAssetType string `json:"wallet_asset_type,omitempty"`
|
||||
WalletAssetAmount int64 `json:"wallet_asset_amount,omitempty"`
|
||||
UsageScopes []string `json:"usage_scopes"`
|
||||
AssetURL string `json:"asset_url"`
|
||||
PreviewURL string `json:"preview_url"`
|
||||
AnimationURL string `json:"animation_url"`
|
||||
MetadataJSON string `json:"metadata_json"`
|
||||
SortOrder int32 `json:"sort_order"`
|
||||
CreatedAtMS int64 `json:"created_at_ms"`
|
||||
UpdatedAtMS int64 `json:"updated_at_ms"`
|
||||
}
|
||||
|
||||
type roomRocketResourceGroupItemData struct {
|
||||
GroupItemID int64 `json:"group_item_id"`
|
||||
ItemType string `json:"item_type"`
|
||||
ResourceID int64 `json:"resource_id"`
|
||||
Resource *roomRocketResourceData `json:"resource,omitempty"`
|
||||
WalletAssetType string `json:"wallet_asset_type,omitempty"`
|
||||
WalletAssetAmount int64 `json:"wallet_asset_amount,omitempty"`
|
||||
Quantity int64 `json:"quantity"`
|
||||
DurationMS int64 `json:"duration_ms"`
|
||||
SortOrder int32 `json:"sort_order"`
|
||||
}
|
||||
|
||||
type roomRocketResourceGroupData struct {
|
||||
GroupID int64 `json:"group_id"`
|
||||
GroupCode string `json:"group_code"`
|
||||
Name string `json:"name"`
|
||||
Status string `json:"status"`
|
||||
Description string `json:"description"`
|
||||
SortOrder int32 `json:"sort_order"`
|
||||
Items []roomRocketResourceGroupItemData `json:"items"`
|
||||
CreatedAtMS int64 `json:"created_at_ms"`
|
||||
UpdatedAtMS int64 `json:"updated_at_ms"`
|
||||
}
|
||||
|
||||
type roomGiftRecipientData struct {
|
||||
@ -347,6 +407,7 @@ type roomSeatData struct {
|
||||
MicMuted bool `json:"mic_muted"`
|
||||
SeatStatus string `json:"seat_status,omitempty"`
|
||||
MicHeartbeatAtMS int64 `json:"mic_heartbeat_at_ms,omitempty"`
|
||||
GiftValue int64 `json:"gift_value"`
|
||||
}
|
||||
|
||||
type roomRankItemData struct {
|
||||
@ -406,15 +467,15 @@ func createRoomDataFromProto(resp *roomv1.CreateRoomResponse) createRoomData {
|
||||
}
|
||||
}
|
||||
|
||||
func roomRocketDataFromProto(resp *roomv1.GetRoomRocketResponse) roomRocketData {
|
||||
func roomRocketDataFromProto(resp *roomv1.GetRoomRocketResponse, resourceGroups map[int64]*roomRocketResourceGroupData) roomRocketData {
|
||||
if resp == nil || resp.GetRocket() == nil {
|
||||
return roomRocketData{}
|
||||
}
|
||||
rocket := resp.GetRocket()
|
||||
return roomRocketData{
|
||||
Enabled: rocket.GetEnabled(),
|
||||
Levels: roomRocketLevelsFromProto(rocket.GetLevels()),
|
||||
State: roomRocketStateFromProto(rocket.GetState()),
|
||||
Levels: roomRocketLevelsFromProto(rocket.GetLevels(), resourceGroups),
|
||||
State: roomRocketStateFromProto(rocket.GetState(), resourceGroups),
|
||||
BroadcastScope: rocket.GetBroadcastScope(),
|
||||
LaunchDelayMS: rocket.GetLaunchDelayMs(),
|
||||
BroadcastDelayMS: rocket.GetBroadcastDelayMs(),
|
||||
@ -423,7 +484,7 @@ func roomRocketDataFromProto(resp *roomv1.GetRoomRocketResponse) roomRocketData
|
||||
}
|
||||
}
|
||||
|
||||
func roomRocketLevelsFromProto(levels []*roomv1.RoomRocketLevel) []roomRocketLevelData {
|
||||
func roomRocketLevelsFromProto(levels []*roomv1.RoomRocketLevel, resourceGroups map[int64]*roomRocketResourceGroupData) []roomRocketLevelData {
|
||||
out := make([]roomRocketLevelData, 0, len(levels))
|
||||
for _, level := range levels {
|
||||
out = append(out, roomRocketLevelData{
|
||||
@ -433,20 +494,21 @@ func roomRocketLevelsFromProto(levels []*roomv1.RoomRocketLevel) []roomRocketLev
|
||||
AnimationURL: level.GetAnimationUrl(),
|
||||
LaunchAnimationURL: level.GetLaunchAnimationUrl(),
|
||||
LaunchedImageURL: level.GetLaunchedImageUrl(),
|
||||
InRoomRewards: roomRocketRewardsFromProto(level.GetInRoomRewards()),
|
||||
Top1Rewards: roomRocketRewardsFromProto(level.GetTop1Rewards()),
|
||||
IgniterRewards: roomRocketRewardsFromProto(level.GetIgniterRewards()),
|
||||
InRoomRewards: roomRocketRewardsFromProto(level.GetInRoomRewards(), resourceGroups),
|
||||
Top1Rewards: roomRocketRewardsFromProto(level.GetTop1Rewards(), resourceGroups),
|
||||
IgniterRewards: roomRocketRewardsFromProto(level.GetIgniterRewards(), resourceGroups),
|
||||
})
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func roomRocketRewardsFromProto(rewards []*roomv1.RoomRocketRewardItem) []roomRocketRewardData {
|
||||
func roomRocketRewardsFromProto(rewards []*roomv1.RoomRocketRewardItem, resourceGroups map[int64]*roomRocketResourceGroupData) []roomRocketRewardData {
|
||||
out := make([]roomRocketRewardData, 0, len(rewards))
|
||||
for _, reward := range rewards {
|
||||
out = append(out, roomRocketRewardData{
|
||||
RewardItemID: reward.GetRewardItemId(),
|
||||
ResourceGroupID: reward.GetResourceGroupId(),
|
||||
ResourceGroup: resourceGroups[reward.GetResourceGroupId()],
|
||||
Weight: reward.GetWeight(),
|
||||
DisplayName: reward.GetDisplayName(),
|
||||
IconURL: reward.GetIconUrl(),
|
||||
@ -455,7 +517,7 @@ func roomRocketRewardsFromProto(rewards []*roomv1.RoomRocketRewardItem) []roomRo
|
||||
return out
|
||||
}
|
||||
|
||||
func roomRocketStateFromProto(state *roomv1.RoomRocketState) roomRocketStateData {
|
||||
func roomRocketStateFromProto(state *roomv1.RoomRocketState, resourceGroups map[int64]*roomRocketResourceGroupData) roomRocketStateData {
|
||||
if state == nil {
|
||||
return roomRocketStateData{}
|
||||
}
|
||||
@ -472,7 +534,7 @@ func roomRocketStateFromProto(state *roomv1.RoomRocketState) roomRocketStateData
|
||||
IgniterUserID: formatOptionalUserID(state.GetIgniterUserId()),
|
||||
RocketID: state.GetRocketId(),
|
||||
ConfigVersion: state.GetConfigVersion(),
|
||||
LastRewards: roomRocketRewardGrantsFromProto(state.GetLastRewards()),
|
||||
LastRewards: roomRocketRewardGrantsFromProto(state.GetLastRewards(), resourceGroups),
|
||||
PendingLaunches: roomRocketPendingLaunchesFromProto(state.GetPendingLaunches()),
|
||||
}
|
||||
}
|
||||
@ -496,7 +558,7 @@ func roomRocketPendingLaunchesFromProto(launches []*roomv1.RoomRocketPendingLaun
|
||||
return out
|
||||
}
|
||||
|
||||
func roomRocketRewardGrantsFromProto(rewards []*roomv1.RoomRocketRewardGrant) []roomRocketRewardGrantData {
|
||||
func roomRocketRewardGrantsFromProto(rewards []*roomv1.RoomRocketRewardGrant, resourceGroups map[int64]*roomRocketResourceGroupData) []roomRocketRewardGrantData {
|
||||
out := make([]roomRocketRewardGrantData, 0, len(rewards))
|
||||
for _, reward := range rewards {
|
||||
out = append(out, roomRocketRewardGrantData{
|
||||
@ -504,6 +566,7 @@ func roomRocketRewardGrantsFromProto(rewards []*roomv1.RoomRocketRewardGrant) []
|
||||
UserID: formatOptionalUserID(reward.GetUserId()),
|
||||
RewardItemID: reward.GetRewardItemId(),
|
||||
ResourceGroupID: reward.GetResourceGroupId(),
|
||||
ResourceGroup: resourceGroups[reward.GetResourceGroupId()],
|
||||
DisplayName: reward.GetDisplayName(),
|
||||
IconURL: reward.GetIconUrl(),
|
||||
GrantID: reward.GetGrantId(),
|
||||
@ -513,6 +576,95 @@ func roomRocketRewardGrantsFromProto(rewards []*roomv1.RoomRocketRewardGrant) []
|
||||
return out
|
||||
}
|
||||
|
||||
func roomRocketResourceGroupIDs(resp *roomv1.GetRoomRocketResponse) []int64 {
|
||||
if resp == nil || resp.GetRocket() == nil {
|
||||
return nil
|
||||
}
|
||||
seen := map[int64]struct{}{}
|
||||
ids := make([]int64, 0)
|
||||
add := func(id int64) {
|
||||
if id <= 0 {
|
||||
return
|
||||
}
|
||||
if _, exists := seen[id]; exists {
|
||||
return
|
||||
}
|
||||
seen[id] = struct{}{}
|
||||
ids = append(ids, id)
|
||||
}
|
||||
for _, level := range resp.GetRocket().GetLevels() {
|
||||
for _, reward := range level.GetInRoomRewards() {
|
||||
add(reward.GetResourceGroupId())
|
||||
}
|
||||
for _, reward := range level.GetTop1Rewards() {
|
||||
add(reward.GetResourceGroupId())
|
||||
}
|
||||
for _, reward := range level.GetIgniterRewards() {
|
||||
add(reward.GetResourceGroupId())
|
||||
}
|
||||
}
|
||||
for _, reward := range resp.GetRocket().GetState().GetLastRewards() {
|
||||
add(reward.GetResourceGroupId())
|
||||
}
|
||||
return ids
|
||||
}
|
||||
|
||||
func roomRocketResourceGroupFromProto(group *walletv1.ResourceGroup) *roomRocketResourceGroupData {
|
||||
if group == nil {
|
||||
return nil
|
||||
}
|
||||
items := make([]roomRocketResourceGroupItemData, 0, len(group.GetItems()))
|
||||
for _, item := range group.GetItems() {
|
||||
items = append(items, roomRocketResourceGroupItemData{
|
||||
GroupItemID: item.GetGroupItemId(),
|
||||
ItemType: item.GetItemType(),
|
||||
ResourceID: item.GetResourceId(),
|
||||
Resource: roomRocketResourceFromProto(item.GetResource()),
|
||||
WalletAssetType: item.GetWalletAssetType(),
|
||||
WalletAssetAmount: item.GetWalletAssetAmount(),
|
||||
Quantity: item.GetQuantity(),
|
||||
DurationMS: item.GetDurationMs(),
|
||||
SortOrder: item.GetSortOrder(),
|
||||
})
|
||||
}
|
||||
return &roomRocketResourceGroupData{
|
||||
GroupID: group.GetGroupId(),
|
||||
GroupCode: group.GetGroupCode(),
|
||||
Name: group.GetName(),
|
||||
Status: group.GetStatus(),
|
||||
Description: group.GetDescription(),
|
||||
SortOrder: group.GetSortOrder(),
|
||||
Items: items,
|
||||
CreatedAtMS: group.GetCreatedAtMs(),
|
||||
UpdatedAtMS: group.GetUpdatedAtMs(),
|
||||
}
|
||||
}
|
||||
|
||||
func roomRocketResourceFromProto(item *walletv1.Resource) *roomRocketResourceData {
|
||||
if item == nil {
|
||||
return nil
|
||||
}
|
||||
return &roomRocketResourceData{
|
||||
ResourceID: item.GetResourceId(),
|
||||
ResourceCode: item.GetResourceCode(),
|
||||
ResourceType: item.GetResourceType(),
|
||||
Name: item.GetName(),
|
||||
Status: item.GetStatus(),
|
||||
Grantable: item.GetGrantable(),
|
||||
GrantStrategy: item.GetGrantStrategy(),
|
||||
WalletAssetType: item.GetWalletAssetType(),
|
||||
WalletAssetAmount: item.GetWalletAssetAmount(),
|
||||
UsageScopes: item.GetUsageScopes(),
|
||||
AssetURL: item.GetAssetUrl(),
|
||||
PreviewURL: item.GetPreviewUrl(),
|
||||
AnimationURL: item.GetAnimationUrl(),
|
||||
MetadataJSON: item.GetMetadataJson(),
|
||||
SortOrder: item.GetSortOrder(),
|
||||
CreatedAtMS: item.GetCreatedAtMs(),
|
||||
UpdatedAtMS: item.GetUpdatedAtMs(),
|
||||
}
|
||||
}
|
||||
|
||||
func roomListItemDataFromProto(room *roomv1.RoomListItem) roomListItemData {
|
||||
if room == nil {
|
||||
return roomListItemData{}
|
||||
@ -534,6 +686,8 @@ func roomListItemDataFromProto(room *roomv1.RoomListItem) roomListItemData {
|
||||
VisibleRegionID: room.GetVisibleRegionId(),
|
||||
AppCode: room.GetAppCode(),
|
||||
RoomShortID: room.GetRoomShortId(),
|
||||
CountryCode: room.GetCountryCode(),
|
||||
CountryFlag: roomProfileCountryFlag(room.GetCountryCode()),
|
||||
}
|
||||
}
|
||||
|
||||
@ -689,6 +843,7 @@ func roomSeatDataFromSnapshot(snapshot *roomv1.RoomSnapshot) []roomSeatData {
|
||||
MicMuted: seat.GetMicMuted(),
|
||||
SeatStatus: seat.GetSeatStatus(),
|
||||
MicHeartbeatAtMS: seat.GetMicHeartbeatAtMs(),
|
||||
GiftValue: seat.GetGiftValue(),
|
||||
})
|
||||
}
|
||||
return items
|
||||
@ -933,8 +1088,12 @@ func roomSeatCounts(snapshot *roomv1.RoomSnapshot) (int32, int32) {
|
||||
}
|
||||
|
||||
func roomIMGroupID(roomID string) string {
|
||||
// 当前房间群规则固定为腾讯云 IM GroupID 等于内部 room_id;显式下发给客户端,避免端侧硬编码规则。
|
||||
return roomID
|
||||
// 房间 IM 群 ID 必须等于内部 room_id;线上腾讯云已按这个 ID 建群,客户端也按这个字段入群。
|
||||
groupID, err := imgroup.RoomGroupID(roomID)
|
||||
if err != nil {
|
||||
return roomID
|
||||
}
|
||||
return groupID
|
||||
}
|
||||
|
||||
func formatOptionalUserID(userID int64) string {
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -5,6 +5,7 @@ import (
|
||||
"strings"
|
||||
|
||||
userv1 "hyapp.local/api/proto/user/v1"
|
||||
walletv1 "hyapp.local/api/proto/wallet/v1"
|
||||
"hyapp/services/gateway-service/internal/auth"
|
||||
"hyapp/services/gateway-service/internal/transport/http/httpkit"
|
||||
)
|
||||
@ -42,21 +43,32 @@ 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"`
|
||||
BreakupCostCoins int64 `json:"breakup_cost_coins"`
|
||||
FormedAtMS int64 `json:"formed_at_ms"`
|
||||
EndedAtMS int64 `json:"ended_at_ms"`
|
||||
UpdatedAtMS int64 `json:"updated_at_ms"`
|
||||
}
|
||||
|
||||
type cpRejectRequest struct {
|
||||
Reason string `json:"reason"`
|
||||
}
|
||||
|
||||
type cpBreakRelationshipRequest struct {
|
||||
CommandID string `json:"command_id"`
|
||||
}
|
||||
|
||||
// listCPApplications 返回当前用户收到或发出的 CP/兄弟/姐妹申请。
|
||||
func (h *Handler) listCPApplications(writer http.ResponseWriter, request *http.Request) {
|
||||
if h.userCPClient == nil {
|
||||
@ -177,6 +189,96 @@ func (h *Handler) listCPRelationships(writer http.ResponseWriter, request *http.
|
||||
})
|
||||
}
|
||||
|
||||
// breakCPRelationship 先在 user-service 创建解除占位,再扣钱包,最后确认关系 ended,避免并发请求重复扣费。
|
||||
func (h *Handler) breakCPRelationship(writer http.ResponseWriter, request *http.Request) {
|
||||
if h.userCPClient == nil || h.walletClient == nil {
|
||||
httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
|
||||
return
|
||||
}
|
||||
relationshipID := strings.TrimSpace(request.PathValue("relationship_id"))
|
||||
if relationshipID == "" {
|
||||
httpkit.WriteError(writer, request, http.StatusNotFound, httpkit.CodeNotFound, "not found")
|
||||
return
|
||||
}
|
||||
var body cpBreakRelationshipRequest
|
||||
if !httpkit.Decode(writer, request, &body) {
|
||||
return
|
||||
}
|
||||
commandID := strings.TrimSpace(body.CommandID)
|
||||
if commandID == "" {
|
||||
httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "command_id is required")
|
||||
return
|
||||
}
|
||||
userID := auth.UserIDFromContext(request.Context())
|
||||
meta := httpkit.UserMeta(request, "")
|
||||
prepare, err := h.userCPClient.PrepareBreakCPRelationship(request.Context(), &userv1.PrepareBreakCPRelationshipRequest{
|
||||
Meta: meta,
|
||||
UserId: userID,
|
||||
RelationshipId: relationshipID,
|
||||
CommandId: commandID,
|
||||
})
|
||||
if err != nil {
|
||||
httpkit.WriteRPCError(writer, request, err)
|
||||
return
|
||||
}
|
||||
if prepare.GetStatus() == "confirmed" {
|
||||
httpkit.WriteOK(writer, request, map[string]any{
|
||||
"relationship": cpRelationshipFromProto(prepare.GetRelationship()),
|
||||
"breakup_cost_coins": prepare.GetBreakupCostCoins(),
|
||||
"wallet_transaction_id": prepare.GetWalletTransactionId(),
|
||||
"coin_balance_after": prepare.GetCoinBalanceAfter(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
walletTransactionID := ""
|
||||
coinBalanceAfter := int64(0)
|
||||
cost := prepare.GetBreakupCostCoins()
|
||||
if cost > 0 {
|
||||
walletResp, walletErr := h.walletClient.DebitCPBreakupFee(request.Context(), &walletv1.DebitCPBreakupFeeRequest{
|
||||
CommandId: commandID,
|
||||
AppCode: meta.GetAppCode(),
|
||||
UserId: userID,
|
||||
RelationshipId: relationshipID,
|
||||
RelationType: prepare.GetRelationship().GetRelationType(),
|
||||
Amount: cost,
|
||||
})
|
||||
if walletErr != nil {
|
||||
// 钱包失败表示关系不应解除;取消 pending 占位后把真实钱包错误返回给客户端。
|
||||
_, _ = h.userCPClient.CancelBreakCPRelationship(request.Context(), &userv1.CancelBreakCPRelationshipRequest{
|
||||
Meta: meta,
|
||||
UserId: userID,
|
||||
RelationshipId: relationshipID,
|
||||
CommandId: commandID,
|
||||
Reason: "wallet_debit_failed",
|
||||
})
|
||||
httpkit.WriteRPCError(writer, request, walletErr)
|
||||
return
|
||||
}
|
||||
walletTransactionID = walletResp.GetTransactionId()
|
||||
coinBalanceAfter = walletResp.GetCoinBalanceAfter()
|
||||
}
|
||||
confirmed, err := h.userCPClient.ConfirmBreakCPRelationship(request.Context(), &userv1.ConfirmBreakCPRelationshipRequest{
|
||||
Meta: meta,
|
||||
UserId: userID,
|
||||
RelationshipId: relationshipID,
|
||||
CommandId: commandID,
|
||||
WalletTransactionId: walletTransactionID,
|
||||
PaidCoinAmount: cost,
|
||||
CoinBalanceAfter: coinBalanceAfter,
|
||||
})
|
||||
if err != nil {
|
||||
httpkit.WriteRPCError(writer, request, err)
|
||||
return
|
||||
}
|
||||
httpkit.WriteOK(writer, request, map[string]any{
|
||||
"relationship": cpRelationshipFromProto(confirmed.GetRelationship()),
|
||||
"breakup_cost_coins": confirmed.GetBreakupCostCoins(),
|
||||
"wallet_transaction_id": confirmed.GetWalletTransactionId(),
|
||||
"coin_balance_after": confirmed.GetCoinBalanceAfter(),
|
||||
})
|
||||
}
|
||||
|
||||
func cpApplicationFromProto(item *userv1.CPApplication) cpApplicationData {
|
||||
if item == nil {
|
||||
return cpApplicationData{}
|
||||
@ -202,15 +304,22 @@ 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(),
|
||||
BreakupCostCoins: item.GetBreakupCostCoins(),
|
||||
FormedAtMS: item.GetFormedAtMs(),
|
||||
EndedAtMS: item.GetEndedAtMs(),
|
||||
UpdatedAtMS: item.GetUpdatedAtMs(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,37 @@
|
||||
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,
|
||||
BreakupCostCoins: 88,
|
||||
FormedAtMs: 1700000000000,
|
||||
EndedAtMs: 1700000002000,
|
||||
UpdatedAtMs: 1700000001000,
|
||||
}
|
||||
|
||||
got := cpRelationshipFromProto(item)
|
||||
if got.CurrentLevelThreshold != 100 ||
|
||||
got.NextLevelThreshold != 300 ||
|
||||
got.NeededForNextLevel != 120 ||
|
||||
got.LevelProgressPercent != 40 ||
|
||||
got.MaxLevel ||
|
||||
got.BreakupCostCoins != 88 ||
|
||||
got.EndedAtMS != 1700000002000 {
|
||||
t.Fatalf("relationship progress fields not mapped: %+v", got)
|
||||
}
|
||||
}
|
||||
@ -88,6 +88,7 @@ func (h *Handler) UserHandlers() httproutes.UserHandlers {
|
||||
AcceptCPApplication: h.acceptCPApplication,
|
||||
RejectCPApplication: h.rejectCPApplication,
|
||||
ListCPRelationships: h.listCPRelationships,
|
||||
BreakCPRelationship: h.breakCPRelationship,
|
||||
GetMyProfile: h.getMyProfile,
|
||||
GetMyAppearance: h.getMyAppearance,
|
||||
GetUserAppearance: h.getUserAppearance,
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -68,7 +68,7 @@ rocketmq:
|
||||
tencent_im_consumer_group: "hyapp-room-im-bridge"
|
||||
consumer_max_reconsume_times: 16
|
||||
rocket_launch:
|
||||
enabled: false
|
||||
enabled: true
|
||||
topic: "hyapp_room_rocket_launch"
|
||||
producer_group: "hyapp-room-rocket-launch-producer"
|
||||
consumer_group: "hyapp-room-rocket-launch"
|
||||
|
||||
@ -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='房间列表项表';
|
||||
|
||||
|
||||
@ -7,6 +7,7 @@ import (
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
roomeventsv1 "hyapp.local/api/proto/events/room/v1"
|
||||
"hyapp/pkg/imgroup"
|
||||
"hyapp/pkg/tencentim"
|
||||
)
|
||||
|
||||
@ -28,7 +29,11 @@ func (p *TencentIMPublisher) EnsureRoomGroup(ctx context.Context, roomID string,
|
||||
return nil
|
||||
}
|
||||
|
||||
return p.client.EnsureRoomGroup(ctx, roomID, ownerUserID)
|
||||
groupID, err := imgroup.RoomGroupID(roomID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return p.client.EnsureRoomGroup(ctx, groupID, ownerUserID)
|
||||
}
|
||||
|
||||
// PublishRoomEvent 把房间系统事件发送到腾讯云 IM 群。
|
||||
@ -38,7 +43,22 @@ func (p *TencentIMPublisher) PublishRoomEvent(ctx context.Context, event tencent
|
||||
return nil
|
||||
}
|
||||
|
||||
return p.client.PublishRoomEvent(ctx, event)
|
||||
groupID, err := imgroup.RoomGroupID(event.RoomID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
event.GroupID = groupID
|
||||
if err := p.client.PublishRoomEvent(ctx, event); err != nil {
|
||||
if !tencentim.IsRESTErrorCode(err, 10010, 10015) {
|
||||
return err
|
||||
}
|
||||
// 历史房间可能先产生 outbox 事件、但腾讯云群没有成功创建;保持原 GroupID,补建后只重发一次。
|
||||
if ensureErr := p.client.EnsureRoomGroup(ctx, groupID, 0); ensureErr != nil {
|
||||
return fmt.Errorf("ensure room im group after publish failure: %w", ensureErr)
|
||||
}
|
||||
return p.client.PublishRoomEvent(ctx, event)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// PublishOutboxEvent 补偿投递 room_outbox 中可转成房间系统消息的事件。
|
||||
@ -52,7 +72,7 @@ func (p *TencentIMPublisher) PublishOutboxEvent(ctx context.Context, envelope *r
|
||||
return err
|
||||
}
|
||||
// 建群是腾讯 IM 的外部副作用,必须留在 outbox worker 内,不能阻塞 CreateRoom 返回。
|
||||
return p.client.EnsureRoomGroup(ctx, envelope.GetRoomId(), body.GetOwnerUserId())
|
||||
return p.EnsureRoomGroup(ctx, envelope.GetRoomId(), body.GetOwnerUserId())
|
||||
}
|
||||
|
||||
event, publish, err := roomEventFromEnvelope(envelope)
|
||||
@ -61,12 +81,22 @@ func (p *TencentIMPublisher) PublishOutboxEvent(ctx context.Context, envelope *r
|
||||
}
|
||||
if envelope.GetEventType() == "RoomUserKicked" {
|
||||
// KickUser 的安全边界除了系统消息,还必须补偿移除腾讯云 IM 群成员。
|
||||
if err := p.client.DeleteRoomGroupMember(ctx, envelope.GetRoomId(), event.TargetUserID); err != nil {
|
||||
groupID, err := imgroup.RoomGroupID(envelope.GetRoomId())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := p.client.DeleteGroupMember(ctx, groupID, event.TargetUserID); err != nil {
|
||||
if !tencentim.IsRESTErrorCode(err, 10010, 10015) {
|
||||
return err
|
||||
}
|
||||
// 群缺失时没有成员可删,先补建群,再继续投递 kick 系统消息以推进 outbox。
|
||||
if ensureErr := p.client.EnsureRoomGroup(ctx, groupID, 0); ensureErr != nil {
|
||||
return fmt.Errorf("ensure room im group after member removal failure: %w", ensureErr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return p.client.PublishRoomEvent(ctx, event)
|
||||
return p.PublishRoomEvent(ctx, event)
|
||||
}
|
||||
|
||||
// noopRoomEventPublisher 是本地和测试路径的房间消息发布占位实现。
|
||||
@ -163,6 +193,7 @@ func roomEventFromEnvelope(envelope *roomeventsv1.EventEnvelope) (tencentim.Room
|
||||
"publish_event_time_ms": fmt.Sprintf("%d", body.GetPublishEventTimeMs()),
|
||||
"mic_muted": fmt.Sprintf("%t", body.GetMicMuted()),
|
||||
"seat_status": body.GetSeatStatus(),
|
||||
"target_gift_value": fmt.Sprintf("%d", body.GetTargetGiftValue()),
|
||||
}
|
||||
return base, true, nil
|
||||
case "RoomMicSeatLocked":
|
||||
@ -270,6 +301,7 @@ func roomEventFromEnvelope(envelope *roomeventsv1.EventEnvelope) (tencentim.Room
|
||||
"gift_count": fmt.Sprintf("%d", body.GetGiftCount()),
|
||||
"coin_spent": fmt.Sprintf("%d", body.GetCoinSpent()),
|
||||
"billing_receipt_id": body.GetBillingReceiptId(),
|
||||
"target_gift_value": fmt.Sprintf("%d", body.GetTargetGiftValue()),
|
||||
}
|
||||
return base, true, nil
|
||||
case "RoomRocketFuelChanged":
|
||||
|
||||
@ -3,6 +3,7 @@ package integration
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"testing"
|
||||
@ -87,6 +88,56 @@ func TestRoomBackgroundChangedPublishesDedicatedIMEvent(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRoomGiftSentCarriesTargetGiftValueInIMAttributes(t *testing.T) {
|
||||
record, err := outbox.Build("room-gift-value", "RoomGiftSent", 8, time.Now(), &roomeventsv1.RoomGiftSent{
|
||||
SenderUserId: 1001,
|
||||
TargetUserId: 1002,
|
||||
GiftId: "gift_rose",
|
||||
GiftCount: 1,
|
||||
GiftValue: 117,
|
||||
TargetGiftValue: 367,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Build RoomGiftSent envelope failed: %v", err)
|
||||
}
|
||||
|
||||
event, publish, err := roomEventFromEnvelope(record.Envelope)
|
||||
if err != nil {
|
||||
t.Fatalf("RoomGiftSent should decode: %v", err)
|
||||
}
|
||||
if !publish {
|
||||
t.Fatal("RoomGiftSent must publish Tencent IM message")
|
||||
}
|
||||
if event.EventType != "room_gift_sent" || event.GiftValue != 117 || event.Attributes["target_gift_value"] != "367" {
|
||||
t.Fatalf("gift IM event target gift value mismatch: %+v", event)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRoomMicChangedCarriesTargetGiftValueInIMAttributes(t *testing.T) {
|
||||
record, err := outbox.Build("room-mic-value", "RoomMicChanged", 9, time.Now(), &roomeventsv1.RoomMicChanged{
|
||||
ActorUserId: 1001,
|
||||
TargetUserId: 1002,
|
||||
FromSeat: 2,
|
||||
ToSeat: 5,
|
||||
Action: "change",
|
||||
TargetGiftValue: 367,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Build RoomMicChanged envelope failed: %v", err)
|
||||
}
|
||||
|
||||
event, publish, err := roomEventFromEnvelope(record.Envelope)
|
||||
if err != nil {
|
||||
t.Fatalf("RoomMicChanged should decode: %v", err)
|
||||
}
|
||||
if !publish {
|
||||
t.Fatal("RoomMicChanged must publish Tencent IM message")
|
||||
}
|
||||
if event.EventType != "room_mic_changed" || event.SeatNo != 5 || event.Attributes["target_gift_value"] != "367" {
|
||||
t.Fatalf("mic IM event target gift value mismatch: %+v", event)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRoomPasswordChangedSkipsRoomGroupIM(t *testing.T) {
|
||||
record, err := outbox.Build("room-lock", "RoomPasswordChanged", 8, time.Now(), &roomeventsv1.RoomPasswordChanged{
|
||||
ActorUserId: 42,
|
||||
@ -108,6 +159,7 @@ func TestRoomPasswordChangedSkipsRoomGroupIM(t *testing.T) {
|
||||
|
||||
func TestTencentIMPublisherRemovesGroupMemberBeforeKickMessage(t *testing.T) {
|
||||
paths := make([]string, 0, 2)
|
||||
bodies := make([]map[string]any, 0, 2)
|
||||
client, err := tencentim.NewRESTClient(tencentim.RESTConfig{
|
||||
SDKAppID: 1400000000,
|
||||
SecretKey: "secret",
|
||||
@ -115,6 +167,11 @@ func TestTencentIMPublisherRemovesGroupMemberBeforeKickMessage(t *testing.T) {
|
||||
AdminUserSigTTL: time.Hour,
|
||||
HTTPClient: &http.Client{Transport: roundTripFunc(func(request *http.Request) (*http.Response, error) {
|
||||
paths = append(paths, request.URL.Path)
|
||||
var body map[string]any
|
||||
if err := json.NewDecoder(request.Body).Decode(&body); err != nil {
|
||||
t.Fatalf("decode request body failed: %v", err)
|
||||
}
|
||||
bodies = append(bodies, body)
|
||||
return &http.Response{
|
||||
StatusCode: http.StatusOK,
|
||||
Body: io.NopCloser(bytes.NewReader([]byte(`{"ActionStatus":"OK","ErrorCode":0,"ErrorInfo":""}`))),
|
||||
@ -141,6 +198,72 @@ func TestTencentIMPublisherRemovesGroupMemberBeforeKickMessage(t *testing.T) {
|
||||
if len(paths) != 2 || paths[0] != "/v4/group_open_http_svc/delete_group_member" || paths[1] != "/v4/group_open_http_svc/send_group_msg" {
|
||||
t.Fatalf("kick outbox should delete member before system message, paths=%+v", paths)
|
||||
}
|
||||
if bodies[0]["GroupId"] != "room-kick" || bodies[1]["GroupId"] != "room-kick" {
|
||||
t.Fatalf("kick outbox must use original room group id, bodies=%+v", bodies)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTencentIMPublisherEnsuresRoomGroupAndRetriesWhenGroupMissing(t *testing.T) {
|
||||
paths := make([]string, 0, 3)
|
||||
bodies := make([]map[string]any, 0, 3)
|
||||
client, err := tencentim.NewRESTClient(tencentim.RESTConfig{
|
||||
SDKAppID: 1400000000,
|
||||
SecretKey: "secret",
|
||||
AdminIdentifier: "administrator",
|
||||
AdminUserSigTTL: time.Hour,
|
||||
HTTPClient: &http.Client{Transport: roundTripFunc(func(request *http.Request) (*http.Response, error) {
|
||||
paths = append(paths, request.URL.Path)
|
||||
var body map[string]any
|
||||
if err := json.NewDecoder(request.Body).Decode(&body); err != nil {
|
||||
t.Fatalf("decode request body failed: %v", err)
|
||||
}
|
||||
bodies = append(bodies, body)
|
||||
if len(paths) == 1 {
|
||||
return &http.Response{
|
||||
StatusCode: http.StatusOK,
|
||||
Body: io.NopCloser(bytes.NewReader([]byte(`{"ActionStatus":"FAIL","ErrorCode":10015,"ErrorInfo":"bytes_group_id invalid"}`))),
|
||||
Header: make(http.Header),
|
||||
}, nil
|
||||
}
|
||||
return &http.Response{
|
||||
StatusCode: http.StatusOK,
|
||||
Body: io.NopCloser(bytes.NewReader([]byte(`{"ActionStatus":"OK","ErrorCode":0,"ErrorInfo":""}`))),
|
||||
Header: make(http.Header),
|
||||
}, nil
|
||||
})},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("NewRESTClient failed: %v", err)
|
||||
}
|
||||
|
||||
publisher := NewTencentIMPublisher(client)
|
||||
err = publisher.PublishRoomEvent(context.Background(), tencentim.RoomEvent{
|
||||
EventID: "evt-retry",
|
||||
RoomID: "lalu_2963d975-316e-444d-ad5c-f82fc36c28ca",
|
||||
EventType: "room_mic_changed",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("PublishRoomEvent should retry after ensuring group: %v", err)
|
||||
}
|
||||
|
||||
wantPaths := []string{
|
||||
"/v4/group_open_http_svc/send_group_msg",
|
||||
"/v4/group_open_http_svc/create_group",
|
||||
"/v4/group_open_http_svc/send_group_msg",
|
||||
}
|
||||
if len(paths) != len(wantPaths) {
|
||||
t.Fatalf("path count mismatch: paths=%+v bodies=%+v", paths, bodies)
|
||||
}
|
||||
for index, want := range wantPaths {
|
||||
if paths[index] != want {
|
||||
t.Fatalf("path[%d] mismatch: got %s want %s", index, paths[index], want)
|
||||
}
|
||||
}
|
||||
for index, body := range bodies {
|
||||
if body["GroupId"] != "lalu_2963d975-316e-444d-ad5c-f82fc36c28ca" {
|
||||
t.Fatalf("request %d must preserve original room group id: %+v", index, body)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type roundTripFunc func(*http.Request) (*http.Response, error)
|
||||
|
||||
@ -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" }
|
||||
@ -416,6 +419,8 @@ type SendGift struct {
|
||||
GiftPointAdded int64 `json:"gift_point_added,omitempty"`
|
||||
// HeatValue 是 Room Cell 恢复时唯一可信的热度增量,不能重新用客户端价格推导。
|
||||
HeatValue int64 `json:"heat_value,omitempty"`
|
||||
// TargetGiftValues 记录每个收礼用户本次从 wallet receipt 得到的热度增量;恢复时只按目标增量重建收礼用户在线态。
|
||||
TargetGiftValues map[int64]int64 `json:"target_gift_values,omitempty"`
|
||||
// PriceVersion 是本次钱包结算使用的服务端礼物价格版本。
|
||||
PriceVersion string `json:"price_version,omitempty"`
|
||||
// GiftTypeCode 是 wallet-service 结算时锁定的礼物类型,用于火箭礼物类型燃料规则。
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -153,6 +153,26 @@ func (s *Service) SendGift(ctx context.Context, req *roomv1.SendGiftRequest) (*r
|
||||
// 扣费成功后,Room Cell 同步更新热度和本地礼物榜。
|
||||
current.Heat += heatValue
|
||||
current.GiftRank = localRank.ApplyGift(cmd.ActorUserID(), heatValue, now)
|
||||
targetGiftValueDeltas := make(map[int64]int64, len(targetBillings))
|
||||
targetCurrentGiftValues := make(map[int64]int64, len(targetBillings))
|
||||
for _, targetBilling := range targetBillings {
|
||||
if targetBilling.Billing == nil || targetBilling.TargetUserID <= 0 {
|
||||
continue
|
||||
}
|
||||
targetGiftValue := targetBilling.Billing.GetHeatValue()
|
||||
if userState := current.OnlineUsers[targetBilling.TargetUserID]; userState != nil {
|
||||
// 收礼用户热度完全使用 wallet-service 已结算的 HeatValue,room-service 不重新判断普通/幸运/超级幸运比例。
|
||||
if targetGiftValue > 0 {
|
||||
userState.GiftValue += targetGiftValue
|
||||
targetGiftValueDeltas[targetBilling.TargetUserID] += targetGiftValue
|
||||
}
|
||||
targetCurrentGiftValues[targetBilling.TargetUserID] = userState.GiftValue
|
||||
}
|
||||
}
|
||||
if len(targetGiftValueDeltas) > 0 {
|
||||
// 命令日志只保存每个目标本次增量;累计值由在线用户态按 replay 顺序重建。
|
||||
settledCommand.TargetGiftValues = targetGiftValueDeltas
|
||||
}
|
||||
current.Version++
|
||||
rocketApply, err := s.applyRoomRocketGift(now, current, rocketConfig, cmd, &settledCommand, billing, roomMeta)
|
||||
if err != nil {
|
||||
@ -184,6 +204,7 @@ func (s *Service) SendGift(ctx context.Context, req *roomv1.SendGiftRequest) (*r
|
||||
GiftName: targetBilling.Billing.GetGiftName(),
|
||||
GiftIconUrl: targetBilling.Billing.GetGiftIconUrl(),
|
||||
GiftAnimationUrl: targetBilling.Billing.GetGiftAnimationUrl(),
|
||||
TargetGiftValue: targetCurrentGiftValues[targetBilling.TargetUserID],
|
||||
})
|
||||
if err != nil {
|
||||
return mutationResult{}, nil, err
|
||||
@ -277,6 +298,7 @@ func (s *Service) SendGift(ctx context.Context, req *roomv1.SendGiftRequest) (*r
|
||||
"gift_name": billing.GetGiftName(),
|
||||
"gift_icon_url": billing.GetGiftIconUrl(),
|
||||
"gift_animation_url": billing.GetGiftAnimationUrl(),
|
||||
"target_gift_value": fmt.Sprintf("%d", targetCurrentGiftValues[cmd.TargetUserID]),
|
||||
},
|
||||
},
|
||||
}, records, nil
|
||||
|
||||
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -71,6 +71,7 @@ func (s *Service) MicUp(ctx context.Context, req *roomv1.MicUpRequest) (*roomv1.
|
||||
current.MicSeats[index].MicHeartbeatAtMS = 0
|
||||
current.MicSeats[index].MicMuted = false
|
||||
seatStatus := current.MicSeats[index].SeatStatus()
|
||||
targetGiftValue := roomUserGiftValue(current, cmd.ActorUserID())
|
||||
|
||||
// RoomMicChanged 是房间外消费者和腾讯云 IM 共同使用的麦位事件源。
|
||||
micEvent, err := outbox.Build(current.RoomID, "RoomMicChanged", current.Version, now, &roomeventsv1.RoomMicChanged{
|
||||
@ -83,6 +84,7 @@ func (s *Service) MicUp(ctx context.Context, req *roomv1.MicUpRequest) (*roomv1.
|
||||
PublishState: state.MicPublishPending,
|
||||
PublishDeadlineMs: cmd.PublishDeadlineMS,
|
||||
SeatStatus: seatStatus,
|
||||
TargetGiftValue: targetGiftValue,
|
||||
})
|
||||
if err != nil {
|
||||
return mutationResult{}, nil, err
|
||||
@ -107,6 +109,7 @@ func (s *Service) MicUp(ctx context.Context, req *roomv1.MicUpRequest) (*roomv1.
|
||||
"mic_session_id": cmd.MicSessionID,
|
||||
"publish_deadline_ms": int64String(cmd.PublishDeadlineMS),
|
||||
"seat_status": seatStatus,
|
||||
"target_gift_value": int64String(targetGiftValue),
|
||||
},
|
||||
},
|
||||
}, []outbox.Record{micEvent}, nil
|
||||
@ -296,6 +299,7 @@ func (s *Service) ChangeMicSeat(ctx context.Context, req *roomv1.ChangeMicSeatRe
|
||||
current.MicSeats[toIndex].MicMuted = movedSeat.MicMuted
|
||||
current.Version++
|
||||
seatStatus := current.MicSeats[toIndex].SeatStatus()
|
||||
targetGiftValue := roomUserGiftValue(current, cmd.TargetUserID)
|
||||
|
||||
micEvent, err := outbox.Build(current.RoomID, "RoomMicChanged", current.Version, now, &roomeventsv1.RoomMicChanged{
|
||||
ActorUserId: cmd.ActorUserID(),
|
||||
@ -307,6 +311,7 @@ func (s *Service) ChangeMicSeat(ctx context.Context, req *roomv1.ChangeMicSeatRe
|
||||
PublishState: movedSeat.PublishState,
|
||||
PublishDeadlineMs: movedSeat.PublishDeadlineMS,
|
||||
SeatStatus: seatStatus,
|
||||
TargetGiftValue: targetGiftValue,
|
||||
})
|
||||
if err != nil {
|
||||
return mutationResult{}, nil, err
|
||||
@ -328,6 +333,7 @@ func (s *Service) ChangeMicSeat(ctx context.Context, req *roomv1.ChangeMicSeatRe
|
||||
"mic_session_id": movedSeat.MicSessionID,
|
||||
"publish_deadline_ms": int64String(movedSeat.PublishDeadlineMS),
|
||||
"seat_status": seatStatus,
|
||||
"target_gift_value": int64String(targetGiftValue),
|
||||
},
|
||||
},
|
||||
}, []outbox.Record{micEvent}, nil
|
||||
@ -401,6 +407,7 @@ func (s *Service) ConfirmMicPublishing(ctx context.Context, req *roomv1.ConfirmM
|
||||
}
|
||||
current.Version++
|
||||
seatStatus := current.MicSeats[index].SeatStatus()
|
||||
targetGiftValue := roomUserGiftValue(current, cmd.TargetUserID)
|
||||
|
||||
micEvent, err := outbox.Build(current.RoomID, "RoomMicChanged", current.Version, now, &roomeventsv1.RoomMicChanged{
|
||||
ActorUserId: cmd.ActorUserID(),
|
||||
@ -414,6 +421,7 @@ func (s *Service) ConfirmMicPublishing(ctx context.Context, req *roomv1.ConfirmM
|
||||
PublishEventTimeMs: cmd.EventTimeMS,
|
||||
MicMuted: current.MicSeats[index].MicMuted,
|
||||
SeatStatus: seatStatus,
|
||||
TargetGiftValue: targetGiftValue,
|
||||
})
|
||||
if err != nil {
|
||||
return mutationResult{}, nil, err
|
||||
@ -441,6 +449,7 @@ func (s *Service) ConfirmMicPublishing(ctx context.Context, req *roomv1.ConfirmM
|
||||
"source": cmd.Source,
|
||||
"mic_muted": boolString(current.MicSeats[index].MicMuted),
|
||||
"seat_status": seatStatus,
|
||||
"target_gift_value": int64String(targetGiftValue),
|
||||
},
|
||||
},
|
||||
}, []outbox.Record{micEvent}, nil
|
||||
@ -565,6 +574,7 @@ func (s *Service) SetMicMute(ctx context.Context, req *roomv1.SetMicMuteRequest)
|
||||
current.MicSeats[index].MicMuted = cmd.Muted
|
||||
current.Version++
|
||||
seatStatus := current.MicSeats[index].SeatStatus()
|
||||
targetGiftValue := roomUserGiftValue(current, cmd.TargetUserID)
|
||||
|
||||
action := "mic_unmute"
|
||||
if cmd.Muted {
|
||||
@ -581,6 +591,7 @@ func (s *Service) SetMicMute(ctx context.Context, req *roomv1.SetMicMuteRequest)
|
||||
PublishDeadlineMs: seat.PublishDeadlineMS,
|
||||
MicMuted: cmd.Muted,
|
||||
SeatStatus: seatStatus,
|
||||
TargetGiftValue: targetGiftValue,
|
||||
})
|
||||
if err != nil {
|
||||
return mutationResult{}, nil, err
|
||||
@ -598,11 +609,12 @@ func (s *Service) SetMicMute(ctx context.Context, req *roomv1.SetMicMuteRequest)
|
||||
SeatNo: seat.SeatNo,
|
||||
RoomVersion: current.Version,
|
||||
Attributes: map[string]string{
|
||||
"action": action,
|
||||
"mic_session_id": seat.MicSessionID,
|
||||
"publish_state": seat.PublishState,
|
||||
"mic_muted": boolString(cmd.Muted),
|
||||
"seat_status": seatStatus,
|
||||
"action": action,
|
||||
"mic_session_id": seat.MicSessionID,
|
||||
"publish_state": seat.PublishState,
|
||||
"mic_muted": boolString(cmd.Muted),
|
||||
"seat_status": seatStatus,
|
||||
"target_gift_value": int64String(targetGiftValue),
|
||||
},
|
||||
},
|
||||
}, []outbox.Record{micEvent}, nil
|
||||
@ -654,3 +666,14 @@ func protoSeatByUser(snapshot *roomv1.RoomSnapshot, userID int64) *roomv1.SeatSt
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func roomUserGiftValue(current *state.RoomState, userID int64) int64 {
|
||||
if current == nil || userID <= 0 {
|
||||
return 0
|
||||
}
|
||||
if userState := current.OnlineUsers[userID]; userState != nil {
|
||||
// 麦位事件只透出在线用户态中的收礼热度,避免把麦位号当成热度归属。
|
||||
return userState.GiftValue
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -379,6 +379,15 @@ func replay(current *state.RoomState, cmd command.Command, committedAtMS int64)
|
||||
// 送礼回放不能再次调用 wallet,只使用命令中记录的钱包结算热度重建展示态。
|
||||
total := typed.HeatValue
|
||||
current.Heat += total
|
||||
for targetUserID, giftValue := range sendGiftTargetGiftValues(typed) {
|
||||
if giftValue <= 0 {
|
||||
continue
|
||||
}
|
||||
if userState := current.OnlineUsers[targetUserID]; userState != nil {
|
||||
// 收礼用户热度只恢复当时仍在房间内的在线态;后续 Leave/Kick/Stale 命令会删除在线态并自然清零。
|
||||
userState.GiftValue += giftValue
|
||||
}
|
||||
}
|
||||
applied := false
|
||||
for index := range current.GiftRank {
|
||||
if current.GiftRank[index].UserID == typed.ActorUserID() {
|
||||
@ -417,6 +426,21 @@ func replay(current *state.RoomState, cmd command.Command, committedAtMS int64)
|
||||
return nil
|
||||
}
|
||||
|
||||
func sendGiftTargetGiftValues(cmd *command.SendGift) map[int64]int64 {
|
||||
if cmd == nil {
|
||||
return nil
|
||||
}
|
||||
if len(cmd.TargetGiftValues) > 0 {
|
||||
return cmd.TargetGiftValues
|
||||
}
|
||||
if cmd.TargetUserID > 0 && len(cmd.TargetUserIDs) <= 1 && cmd.HeatValue > 0 {
|
||||
// 旧单目标命令没有 TargetGiftValues 时,HeatValue 同时就是该唯一收礼人的本次热度增量。
|
||||
return map[int64]int64{cmd.TargetUserID: cmd.HeatValue}
|
||||
}
|
||||
// 旧多目标命令无法从聚合 HeatValue 反推出每个收礼人的值,宁可不恢复也不能把总值错误扩散给每个目标。
|
||||
return nil
|
||||
}
|
||||
|
||||
func rocketStateFromSettledGift(cmd *command.SendGift) *state.RocketState {
|
||||
return &state.RocketState{
|
||||
CurrentLevel: cmd.RocketLevel,
|
||||
|
||||
@ -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。
|
||||
|
||||
@ -129,6 +129,12 @@ func TestSendGiftBatchDebitsMultipleTargets(t *testing.T) {
|
||||
if resp.GetRoomHeat() != 30 || resp.GetBillingReceiptId() != "receipt-202,receipt-303" {
|
||||
t.Fatalf("multi-target gift response must aggregate wallet results: %+v", resp)
|
||||
}
|
||||
if firstUser := onlineUserByID(resp.GetRoom(), firstTargetID); firstUser == nil || firstUser.GetGiftValue() != 11 {
|
||||
t.Fatalf("first target must receive only its own heat value: %+v", firstUser)
|
||||
}
|
||||
if secondUser := onlineUserByID(resp.GetRoom(), secondTargetID); secondUser == nil || secondUser.GetGiftValue() != 19 {
|
||||
t.Fatalf("second target must receive only its own heat value: %+v", secondUser)
|
||||
}
|
||||
if len(wallet.debitRequests) != 0 {
|
||||
t.Fatalf("multi-target gift must use wallet BatchDebitGift, single debits=%+v", wallet.debitRequests)
|
||||
}
|
||||
@ -150,9 +156,15 @@ func TestSendGiftBatchDebitsMultipleTargets(t *testing.T) {
|
||||
if got[firstTargetID].GetBillingReceiptId() != "receipt-202" || got[firstTargetID].GetGiftValue() != 11 || got[firstTargetID].GetCommandId() != "cmd-gift-multi:target:202" {
|
||||
t.Fatalf("first target GiftSent mismatch: %+v", got[firstTargetID])
|
||||
}
|
||||
if got[firstTargetID].GetTargetGiftValue() != 11 {
|
||||
t.Fatalf("first target GiftSent must carry its cumulative value: %+v", got[firstTargetID])
|
||||
}
|
||||
if got[secondTargetID].GetBillingReceiptId() != "receipt-303" || got[secondTargetID].GetGiftValue() != 19 || got[secondTargetID].GetCommandId() != "cmd-gift-multi:target:303" {
|
||||
t.Fatalf("second target GiftSent mismatch: %+v", got[secondTargetID])
|
||||
}
|
||||
if got[secondTargetID].GetTargetGiftValue() != 19 {
|
||||
t.Fatalf("second target GiftSent must carry its cumulative value: %+v", got[secondTargetID])
|
||||
}
|
||||
}
|
||||
|
||||
func (w *rocketTestWallet) GrantResourceGroup(_ context.Context, req *walletv1.GrantResourceGroupRequest) (*walletv1.ResourceGrantResponse, error) {
|
||||
|
||||
@ -0,0 +1,192 @@
|
||||
package service_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
roomeventsv1 "hyapp.local/api/proto/events/room/v1"
|
||||
roomv1 "hyapp.local/api/proto/room/v1"
|
||||
walletv1 "hyapp.local/api/proto/wallet/v1"
|
||||
"hyapp/services/room-service/internal/integration"
|
||||
roomservice "hyapp/services/room-service/internal/room/service"
|
||||
"hyapp/services/room-service/internal/router"
|
||||
"hyapp/services/room-service/internal/testutil/mysqltest"
|
||||
)
|
||||
|
||||
func TestRoomUserGiftValueFollowsMicChangeAndResetsAfterLeave(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
repository := mysqltest.NewRepository(t)
|
||||
now := &fixedRoomRocketClock{now: time.Date(2026, 6, 8, 10, 0, 0, 0, time.UTC)}
|
||||
wallet := &rocketTestWallet{debits: []*walletv1.DebitGiftResponse{{
|
||||
BillingReceiptId: "receipt-target-heat",
|
||||
CoinSpent: 1000,
|
||||
ChargeAmount: 1000,
|
||||
HeatValue: 117,
|
||||
GiftTypeCode: "normal",
|
||||
}}}
|
||||
svc := newUserGiftValueTestService(repository, wallet, now, "node-user-gift-value")
|
||||
|
||||
roomID := "room-user-gift-value"
|
||||
ownerID := int64(61001)
|
||||
targetID := int64(61002)
|
||||
createRocketRoom(t, ctx, svc, roomID, ownerID, 9001)
|
||||
joinRocketRoom(t, ctx, svc, roomID, targetID)
|
||||
if _, err := svc.MicUp(ctx, &roomv1.MicUpRequest{
|
||||
Meta: rocketMeta(roomID, targetID, "target-heat-up"),
|
||||
SeatNo: 2,
|
||||
}); err != nil {
|
||||
t.Fatalf("target mic up failed: %v", err)
|
||||
}
|
||||
|
||||
giftResp, err := svc.SendGift(ctx, &roomv1.SendGiftRequest{
|
||||
Meta: rocketMeta(roomID, ownerID, "target-heat-gift"),
|
||||
TargetType: "user",
|
||||
TargetUserId: targetID,
|
||||
GiftId: "gift-target-heat",
|
||||
GiftCount: 1,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("send gift failed: %v", err)
|
||||
}
|
||||
if user := onlineUserByID(giftResp.GetRoom(), targetID); user == nil || user.GetGiftValue() != 117 {
|
||||
t.Fatalf("target online user gift_value mismatch: %+v", user)
|
||||
}
|
||||
if seat := seatByNo(giftResp.GetRoom(), 2); seat == nil || seat.GetUserId() != targetID || seat.GetGiftValue() != 117 {
|
||||
t.Fatalf("target seat gift_value mismatch after gift: %+v", seat)
|
||||
}
|
||||
giftEvents := roomGiftSentEvents(t, ctx, repository)
|
||||
if len(giftEvents) != 1 || giftEvents[0].GetTargetGiftValue() != 117 {
|
||||
t.Fatalf("RoomGiftSent must carry target cumulative gift value: %+v", giftEvents)
|
||||
}
|
||||
|
||||
changeResp, err := svc.ChangeMicSeat(ctx, &roomv1.ChangeMicSeatRequest{
|
||||
Meta: rocketMeta(roomID, targetID, "target-heat-change"),
|
||||
TargetUserId: targetID,
|
||||
SeatNo: 5,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("change mic failed: %v", err)
|
||||
}
|
||||
if oldSeat := seatByNo(changeResp.GetRoom(), 2); oldSeat == nil || oldSeat.GetUserId() != 0 || oldSeat.GetGiftValue() != 0 {
|
||||
t.Fatalf("old seat must be empty after change: %+v", oldSeat)
|
||||
}
|
||||
if newSeat := seatByNo(changeResp.GetRoom(), 5); newSeat == nil || newSeat.GetUserId() != targetID || newSeat.GetGiftValue() != 117 {
|
||||
t.Fatalf("gift value must follow user to new seat: %+v", newSeat)
|
||||
}
|
||||
if changed := firstMicChangedEventByAction(t, ctx, repository, "change"); changed == nil || changed.GetTargetGiftValue() != 117 {
|
||||
t.Fatalf("RoomMicChanged/change must carry target gift value: %+v", changed)
|
||||
}
|
||||
|
||||
if _, err := svc.LeaveRoom(ctx, &roomv1.LeaveRoomRequest{Meta: rocketMeta(roomID, targetID, "target-heat-leave")}); err != nil {
|
||||
t.Fatalf("leave room failed: %v", err)
|
||||
}
|
||||
joinRocketRoom(t, ctx, svc, roomID, targetID)
|
||||
reupResp, err := svc.MicUp(ctx, &roomv1.MicUpRequest{
|
||||
Meta: rocketMeta(roomID, targetID, "target-heat-reup"),
|
||||
SeatNo: 1,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("target mic re-up failed: %v", err)
|
||||
}
|
||||
if user := onlineUserByID(reupResp.GetRoom(), targetID); user == nil || user.GetGiftValue() != 0 {
|
||||
t.Fatalf("gift value must reset after leave and rejoin: %+v", user)
|
||||
}
|
||||
if seat := seatByNo(reupResp.GetRoom(), 1); seat == nil || seat.GetUserId() != targetID || seat.GetGiftValue() != 0 {
|
||||
t.Fatalf("rejoined user seat gift_value must be zero: %+v", seat)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRoomUserGiftValueRecoversFromCommandLog(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
repository := mysqltest.NewRepository(t)
|
||||
now := &fixedRoomRocketClock{now: time.Date(2026, 6, 8, 10, 30, 0, 0, time.UTC)}
|
||||
wallet := &rocketTestWallet{debits: []*walletv1.DebitGiftResponse{{
|
||||
BillingReceiptId: "receipt-replay-target-heat",
|
||||
CoinSpent: 1500,
|
||||
ChargeAmount: 1500,
|
||||
HeatValue: 367,
|
||||
GiftTypeCode: "lucky",
|
||||
}}}
|
||||
svc := newUserGiftValueTestService(repository, wallet, now, "node-user-gift-replay-writer")
|
||||
|
||||
roomID := "room-user-gift-replay"
|
||||
ownerID := int64(62001)
|
||||
targetID := int64(62002)
|
||||
createRocketRoom(t, ctx, svc, roomID, ownerID, 9001)
|
||||
joinRocketRoom(t, ctx, svc, roomID, targetID)
|
||||
if _, err := svc.MicUp(ctx, &roomv1.MicUpRequest{
|
||||
Meta: rocketMeta(roomID, targetID, "replay-up"),
|
||||
SeatNo: 2,
|
||||
}); err != nil {
|
||||
t.Fatalf("target mic up failed: %v", err)
|
||||
}
|
||||
if _, err := svc.SendGift(ctx, &roomv1.SendGiftRequest{
|
||||
Meta: rocketMeta(roomID, ownerID, "replay-gift"),
|
||||
TargetType: "user",
|
||||
TargetUserId: targetID,
|
||||
GiftId: "gift-replay-target-heat",
|
||||
GiftCount: 1,
|
||||
}); err != nil {
|
||||
t.Fatalf("send gift failed: %v", err)
|
||||
}
|
||||
if _, err := svc.ChangeMicSeat(ctx, &roomv1.ChangeMicSeatRequest{
|
||||
Meta: rocketMeta(roomID, targetID, "replay-change"),
|
||||
TargetUserId: targetID,
|
||||
SeatNo: 4,
|
||||
}); err != nil {
|
||||
t.Fatalf("change mic failed: %v", err)
|
||||
}
|
||||
|
||||
recoveredSvc := newUserGiftValueTestService(repository, &rocketTestWallet{}, now, "node-user-gift-replay-reader")
|
||||
resp, err := recoveredSvc.GetRoomSnapshot(ctx, &roomv1.GetRoomSnapshotRequest{
|
||||
Meta: rocketMeta(roomID, ownerID, "replay-snapshot"),
|
||||
RoomId: roomID,
|
||||
ViewerUserId: ownerID,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("recover room snapshot failed: %v", err)
|
||||
}
|
||||
if user := onlineUserByID(resp.GetRoom(), targetID); user == nil || user.GetGiftValue() != 367 {
|
||||
t.Fatalf("recovered online user gift_value mismatch: %+v", user)
|
||||
}
|
||||
if oldSeat := seatByNo(resp.GetRoom(), 2); oldSeat == nil || oldSeat.GetUserId() != 0 || oldSeat.GetGiftValue() != 0 {
|
||||
t.Fatalf("recovered old seat must stay empty: %+v", oldSeat)
|
||||
}
|
||||
if newSeat := seatByNo(resp.GetRoom(), 4); newSeat == nil || newSeat.GetUserId() != targetID || newSeat.GetGiftValue() != 367 {
|
||||
t.Fatalf("recovered seat must keep target gift value after change: %+v", newSeat)
|
||||
}
|
||||
}
|
||||
|
||||
func newUserGiftValueTestService(repository *mysqltest.Repository, wallet *rocketTestWallet, clock *fixedRoomRocketClock, nodeID string) *roomservice.Service {
|
||||
return roomservice.New(roomservice.Config{
|
||||
NodeID: nodeID,
|
||||
LeaseTTL: 10 * time.Second,
|
||||
RankLimit: 20,
|
||||
SnapshotEveryN: 100,
|
||||
Clock: clock,
|
||||
}, router.NewMemoryDirectory(), repository, wallet, integration.NewNoopRoomEventPublisher(), integration.NewNoopOutboxPublisher())
|
||||
}
|
||||
|
||||
func firstMicChangedEventByAction(t *testing.T, ctx context.Context, repository *mysqltest.Repository, action string) *roomeventsv1.RoomMicChanged {
|
||||
t.Helper()
|
||||
|
||||
records, err := repository.ListPendingOutbox(ctx, 100)
|
||||
if err != nil {
|
||||
t.Fatalf("list pending outbox failed: %v", err)
|
||||
}
|
||||
for _, record := range records {
|
||||
if record.EventType != "RoomMicChanged" {
|
||||
continue
|
||||
}
|
||||
var event roomeventsv1.RoomMicChanged
|
||||
if err := proto.Unmarshal(record.Envelope.GetBody(), &event); err != nil {
|
||||
t.Fatalf("decode mic changed event failed: %v", err)
|
||||
}
|
||||
if event.GetAction() == action {
|
||||
return &event
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -100,6 +100,8 @@ type RoomUserState struct {
|
||||
JoinedAtMS int64
|
||||
// LastSeenAtMS 是最后一次房间业务操作时间,不等同于腾讯云 IM 心跳。
|
||||
LastSeenAtMS int64
|
||||
// GiftValue 是该用户本次在线停留期间在当前房间收到的礼物热度,离房删除 OnlineUsers 后自然清零。
|
||||
GiftValue int64
|
||||
}
|
||||
|
||||
// UserModerationState 保存房间内 ban/mute 这类治理状态的时间边界。
|
||||
@ -387,12 +389,20 @@ func (s *RoomState) ToProto() *roomv1.RoomSnapshot {
|
||||
Role: userState.Role,
|
||||
JoinedAtMs: userState.JoinedAtMS,
|
||||
LastSeenAtMs: userState.LastSeenAtMS,
|
||||
GiftValue: userState.GiftValue,
|
||||
})
|
||||
}
|
||||
|
||||
seats := make([]*roomv1.SeatState, 0, len(s.MicSeats))
|
||||
|
||||
for _, seat := range s.MicSeats {
|
||||
seatGiftValue := int64(0)
|
||||
if seat.UserID > 0 {
|
||||
if userState, exists := s.OnlineUsers[seat.UserID]; exists && userState != nil {
|
||||
// 麦位展示值从在线用户态派生,切麦只移动 UserID,不会重置已收到的礼物热度。
|
||||
seatGiftValue = userState.GiftValue
|
||||
}
|
||||
}
|
||||
// 麦位顺序沿用内部切片顺序,即初始化的 SeatNo 升序和后续原位修改。
|
||||
seats = append(seats, &roomv1.SeatState{
|
||||
SeatNo: seat.SeatNo,
|
||||
@ -406,6 +416,7 @@ func (s *RoomState) ToProto() *roomv1.RoomSnapshot {
|
||||
MicMuted: seat.MicMuted,
|
||||
SeatStatus: seat.SeatStatus(),
|
||||
MicHeartbeatAtMs: seat.MicHeartbeatAtMS,
|
||||
GiftValue: seatGiftValue,
|
||||
})
|
||||
}
|
||||
|
||||
@ -494,6 +505,7 @@ func FromProto(snapshot *roomv1.RoomSnapshot) *RoomState {
|
||||
Role: user.GetRole(),
|
||||
JoinedAtMS: user.GetJoinedAtMs(),
|
||||
LastSeenAtMS: user.GetLastSeenAtMs(),
|
||||
GiftValue: user.GetGiftValue(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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) + "%"
|
||||
|
||||
@ -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',
|
||||
@ -400,6 +425,7 @@ CREATE TABLE IF NOT EXISTS user_cp_relation_configs (
|
||||
relation_type VARCHAR(32) NOT NULL COMMENT '关系类型:cp、brother、sister',
|
||||
max_count_per_user INT NOT NULL COMMENT '每个用户可拥有的该类型 active 关系数量上限',
|
||||
application_expire_hours INT NOT NULL COMMENT '申请过期小时数',
|
||||
breakup_cost_coins BIGINT NOT NULL DEFAULT 0 COMMENT '用户主动解除该类型关系需要支付的金币数',
|
||||
status VARCHAR(32) NOT NULL DEFAULT 'active' COMMENT '业务状态',
|
||||
created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms',
|
||||
updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms',
|
||||
@ -462,6 +488,11 @@ CREATE TABLE IF NOT EXISTS user_cp_relationships (
|
||||
source_application_id VARCHAR(128) NOT NULL DEFAULT '' COMMENT '建立关系的申请 ID',
|
||||
formed_at_ms BIGINT NOT NULL COMMENT '建立时间,UTC epoch ms',
|
||||
ended_at_ms BIGINT NOT NULL DEFAULT 0 COMMENT '解除时间,UTC epoch ms',
|
||||
ended_by_user_id BIGINT NOT NULL DEFAULT 0 COMMENT '主动解除关系的用户 ID',
|
||||
breakup_cost_coins BIGINT NOT NULL DEFAULT 0 COMMENT '本次解除实际支付金币数',
|
||||
breakup_command_id VARCHAR(128) NOT NULL DEFAULT '' COMMENT '解除关系命令 ID',
|
||||
breakup_wallet_transaction_id VARCHAR(128) NOT NULL DEFAULT '' COMMENT '解除扣费钱包交易 ID',
|
||||
breakup_coin_balance_after BIGINT NOT NULL DEFAULT 0 COMMENT '解除扣费后用户金币余额',
|
||||
created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms',
|
||||
updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms',
|
||||
PRIMARY KEY (app_code, relationship_id),
|
||||
@ -470,6 +501,26 @@ CREATE TABLE IF NOT EXISTS user_cp_relationships (
|
||||
KEY idx_user_cp_rel_user_b (app_code, user_b_id, status, relation_type, updated_at_ms)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户 CP 关系表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS user_cp_relationship_breakups (
|
||||
app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码,用于多租户隔离',
|
||||
command_id VARCHAR(128) NOT NULL COMMENT '解除关系命令 ID',
|
||||
relationship_id VARCHAR(128) NOT NULL COMMENT '关系 ID',
|
||||
requester_user_id BIGINT NOT NULL COMMENT '发起解除的用户 ID',
|
||||
relation_type VARCHAR(32) NOT NULL COMMENT '关系类型:cp、brother、sister',
|
||||
breakup_cost_coins BIGINT NOT NULL DEFAULT 0 COMMENT '创建解除占位时读取到的金币费用',
|
||||
status VARCHAR(32) NOT NULL COMMENT '解除状态:pending、confirmed、canceled',
|
||||
wallet_transaction_id VARCHAR(128) NOT NULL DEFAULT '' COMMENT '解除扣费钱包交易 ID',
|
||||
coin_balance_after BIGINT NOT NULL DEFAULT 0 COMMENT '扣费后金币余额',
|
||||
reason VARCHAR(256) NOT NULL DEFAULT '' COMMENT '取消原因',
|
||||
created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms',
|
||||
updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms',
|
||||
confirmed_at_ms BIGINT NOT NULL DEFAULT 0 COMMENT '确认解除时间,UTC epoch ms',
|
||||
canceled_at_ms BIGINT NOT NULL DEFAULT 0 COMMENT '取消解除时间,UTC epoch ms',
|
||||
PRIMARY KEY (app_code, command_id),
|
||||
KEY idx_user_cp_breakup_relationship_status (app_code, relationship_id, status, updated_at_ms),
|
||||
KEY idx_user_cp_breakup_requester_status (app_code, requester_user_id, status, updated_at_ms)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户 CP 关系解除占位表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS user_cp_gift_event_consumption (
|
||||
app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码,用于多租户隔离',
|
||||
room_event_id VARCHAR(128) NOT NULL COMMENT 'room outbox 事件 ID',
|
||||
@ -506,6 +557,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_outbox,CREATE 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',
|
||||
|
||||
@ -15,6 +15,10 @@ const (
|
||||
RelationshipStatusActive = "active"
|
||||
RelationshipStatusEnded = "ended"
|
||||
|
||||
BreakupStatusPending = "pending"
|
||||
BreakupStatusConfirmed = "confirmed"
|
||||
BreakupStatusCanceled = "canceled"
|
||||
|
||||
GiftTypeCodeCP = "cp"
|
||||
|
||||
EventTypeApplicationCreated = "UserCPApplicationCreated"
|
||||
@ -23,6 +27,7 @@ const (
|
||||
EventTypeRelationshipCreated = "UserCPRelationshipCreated"
|
||||
EventTypeRelationshipIntimacyChanged = "UserCPRelationshipIntimacyChanged"
|
||||
EventTypeRelationshipLevelUp = "UserCPRelationshipLevelUp"
|
||||
EventTypeRelationshipEnded = "UserCPRelationshipEnded"
|
||||
)
|
||||
|
||||
// UserProfile 是 CP 申请和关系展示用的用户快照。
|
||||
@ -64,18 +69,42 @@ 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
|
||||
CurrentLevelThreshold int64
|
||||
NextLevelThreshold int64
|
||||
NeededForNextLevel int64
|
||||
LevelProgressPercent int32
|
||||
MaxLevel bool
|
||||
SourceApplicationID string
|
||||
FormedAtMS int64
|
||||
EndedAtMS int64
|
||||
BreakupCostCoins int64
|
||||
UpdatedAtMS int64
|
||||
}
|
||||
|
||||
// RelationshipBreakup 是一次解除关系命令的占位事实;pending 阻止同一关系被并发重复扣费。
|
||||
type RelationshipBreakup struct {
|
||||
CommandID string
|
||||
RelationshipID string
|
||||
RequesterUserID int64
|
||||
RelationType string
|
||||
BreakupCostCoins int64
|
||||
Status string
|
||||
UserAID int64
|
||||
UserBID int64
|
||||
Me UserProfile
|
||||
Partner UserProfile
|
||||
IntimacyValue int64
|
||||
Level int32
|
||||
SourceApplicationID string
|
||||
FormedAtMS int64
|
||||
WalletTransactionID string
|
||||
CoinBalanceAfter int64
|
||||
Reason string
|
||||
CreatedAtMS int64
|
||||
UpdatedAtMS int64
|
||||
ConfirmedAtMS int64
|
||||
CanceledAtMS int64
|
||||
}
|
||||
|
||||
// GiftEvent 是 room outbox 中 RoomGiftSent 转换后的 CP 关系消费输入。
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user