From 7433b244119789287098e8c8ee9ec9081c063b9c Mon Sep 17 00:00:00 2001 From: zhx Date: Fri, 29 May 2026 16:48:00 +0800 Subject: [PATCH] feat: add user report management --- api/proto/user/v1/user.pb.go | 906 ++++++++++++------ api/proto/user/v1/user.proto | 30 + api/proto/user/v1/user_grpc.pb.go | 38 + docs/flutter对接/用户举报Flutter对接.md | 272 ++++++ server/admin/cmd/server/main.go | 2 + .../admin/internal/modules/report/handler.go | 83 ++ .../admin/internal/modules/report/request.go | 11 + .../admin/internal/modules/report/response.go | 38 + .../admin/internal/modules/report/routes.go | 15 + .../admin/internal/modules/report/service.go | 274 ++++++ .../internal/modules/report/service_test.go | 38 + server/admin/internal/repository/seed.go | 11 +- server/admin/internal/router/router.go | 3 + .../migrations/028_report_navigation.sql | 43 + .../internal/client/user_client.go | 5 + .../transport/http/httproutes/router.go | 2 + .../transport/http/social_handler_test.go | 44 + .../transport/http/userapi/handler.go | 1 + .../transport/http/userapi/report_handler.go | 94 ++ .../deploy/mysql/initdb/001_user_service.sql | 22 + .../user-service/internal/domain/user/user.go | 36 + .../internal/service/user/moderation_test.go | 6 + .../internal/service/user/report.go | 117 +++ .../internal/service/user/report_test.go | 47 + .../internal/service/user/service.go | 2 + .../storage/mysql/user/report_repository.go | 51 + .../internal/testutil/mysqltest/mysqltest.go | 4 + .../internal/transport/grpc/convert.go | 15 + .../internal/transport/grpc/server.go | 10 + 29 files changed, 1908 insertions(+), 312 deletions(-) create mode 100644 docs/flutter对接/用户举报Flutter对接.md create mode 100644 server/admin/internal/modules/report/handler.go create mode 100644 server/admin/internal/modules/report/request.go create mode 100644 server/admin/internal/modules/report/response.go create mode 100644 server/admin/internal/modules/report/routes.go create mode 100644 server/admin/internal/modules/report/service.go create mode 100644 server/admin/internal/modules/report/service_test.go create mode 100644 server/admin/migrations/028_report_navigation.sql create mode 100644 services/gateway-service/internal/transport/http/userapi/report_handler.go create mode 100644 services/user-service/internal/service/user/report.go create mode 100644 services/user-service/internal/service/user/report_test.go create mode 100644 services/user-service/internal/storage/mysql/user/report_repository.go diff --git a/api/proto/user/v1/user.pb.go b/api/proto/user/v1/user.pb.go index 11a333c0..36a0c2d5 100644 --- a/api/proto/user/v1/user.pb.go +++ b/api/proto/user/v1/user.pb.go @@ -3279,6 +3279,260 @@ func (x *ListFriendApplicationsResponse) GetTotal() int64 { return 0 } +// UserReport 是 App 用户提交的治理举报单。 +type UserReport struct { + state protoimpl.MessageState `protogen:"open.v1"` + ReportId string `protobuf:"bytes,1,opt,name=report_id,json=reportId,proto3" json:"report_id,omitempty"` + ReporterUserId int64 `protobuf:"varint,2,opt,name=reporter_user_id,json=reporterUserId,proto3" json:"reporter_user_id,omitempty"` + TargetType string `protobuf:"bytes,3,opt,name=target_type,json=targetType,proto3" json:"target_type,omitempty"` + UserId int64 `protobuf:"varint,4,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` + RoomId string `protobuf:"bytes,5,opt,name=room_id,json=roomId,proto3" json:"room_id,omitempty"` + ReportType string `protobuf:"bytes,6,opt,name=report_type,json=reportType,proto3" json:"report_type,omitempty"` + Reason string `protobuf:"bytes,7,opt,name=reason,proto3" json:"reason,omitempty"` + ImageUrls []string `protobuf:"bytes,8,rep,name=image_urls,json=imageUrls,proto3" json:"image_urls,omitempty"` + Status string `protobuf:"bytes,9,opt,name=status,proto3" json:"status,omitempty"` + CreatedAtMs int64 `protobuf:"varint,10,opt,name=created_at_ms,json=createdAtMs,proto3" json:"created_at_ms,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UserReport) Reset() { + *x = UserReport{} + mi := &file_proto_user_v1_user_proto_msgTypes[45] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UserReport) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UserReport) ProtoMessage() {} + +func (x *UserReport) ProtoReflect() protoreflect.Message { + mi := &file_proto_user_v1_user_proto_msgTypes[45] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UserReport.ProtoReflect.Descriptor instead. +func (*UserReport) Descriptor() ([]byte, []int) { + return file_proto_user_v1_user_proto_rawDescGZIP(), []int{45} +} + +func (x *UserReport) GetReportId() string { + if x != nil { + return x.ReportId + } + return "" +} + +func (x *UserReport) GetReporterUserId() int64 { + if x != nil { + return x.ReporterUserId + } + return 0 +} + +func (x *UserReport) GetTargetType() string { + if x != nil { + return x.TargetType + } + return "" +} + +func (x *UserReport) GetUserId() int64 { + if x != nil { + return x.UserId + } + return 0 +} + +func (x *UserReport) GetRoomId() string { + if x != nil { + return x.RoomId + } + return "" +} + +func (x *UserReport) GetReportType() string { + if x != nil { + return x.ReportType + } + return "" +} + +func (x *UserReport) GetReason() string { + if x != nil { + return x.Reason + } + return "" +} + +func (x *UserReport) GetImageUrls() []string { + if x != nil { + return x.ImageUrls + } + return nil +} + +func (x *UserReport) GetStatus() string { + if x != nil { + return x.Status + } + return "" +} + +func (x *UserReport) GetCreatedAtMs() int64 { + if x != nil { + return x.CreatedAtMs + } + return 0 +} + +// SubmitReportRequest 提交用户或房间举报;user_id 和 room_id 必须二选一。 +type SubmitReportRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Meta *RequestMeta `protobuf:"bytes,1,opt,name=meta,proto3" json:"meta,omitempty"` + ReporterUserId int64 `protobuf:"varint,2,opt,name=reporter_user_id,json=reporterUserId,proto3" json:"reporter_user_id,omitempty"` + UserId int64 `protobuf:"varint,3,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` + RoomId string `protobuf:"bytes,4,opt,name=room_id,json=roomId,proto3" json:"room_id,omitempty"` + ReportType string `protobuf:"bytes,5,opt,name=report_type,json=reportType,proto3" json:"report_type,omitempty"` + Reason string `protobuf:"bytes,6,opt,name=reason,proto3" json:"reason,omitempty"` + ImageUrls []string `protobuf:"bytes,7,rep,name=image_urls,json=imageUrls,proto3" json:"image_urls,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SubmitReportRequest) Reset() { + *x = SubmitReportRequest{} + mi := &file_proto_user_v1_user_proto_msgTypes[46] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SubmitReportRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SubmitReportRequest) ProtoMessage() {} + +func (x *SubmitReportRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_user_v1_user_proto_msgTypes[46] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SubmitReportRequest.ProtoReflect.Descriptor instead. +func (*SubmitReportRequest) Descriptor() ([]byte, []int) { + return file_proto_user_v1_user_proto_rawDescGZIP(), []int{46} +} + +func (x *SubmitReportRequest) GetMeta() *RequestMeta { + if x != nil { + return x.Meta + } + return nil +} + +func (x *SubmitReportRequest) GetReporterUserId() int64 { + if x != nil { + return x.ReporterUserId + } + return 0 +} + +func (x *SubmitReportRequest) GetUserId() int64 { + if x != nil { + return x.UserId + } + return 0 +} + +func (x *SubmitReportRequest) GetRoomId() string { + if x != nil { + return x.RoomId + } + return "" +} + +func (x *SubmitReportRequest) GetReportType() string { + if x != nil { + return x.ReportType + } + return "" +} + +func (x *SubmitReportRequest) GetReason() string { + if x != nil { + return x.Reason + } + return "" +} + +func (x *SubmitReportRequest) GetImageUrls() []string { + if x != nil { + return x.ImageUrls + } + return nil +} + +type SubmitReportResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Report *UserReport `protobuf:"bytes,1,opt,name=report,proto3" json:"report,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SubmitReportResponse) Reset() { + *x = SubmitReportResponse{} + mi := &file_proto_user_v1_user_proto_msgTypes[47] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SubmitReportResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SubmitReportResponse) ProtoMessage() {} + +func (x *SubmitReportResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_user_v1_user_proto_msgTypes[47] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SubmitReportResponse.ProtoReflect.Descriptor instead. +func (*SubmitReportResponse) Descriptor() ([]byte, []int) { + return file_proto_user_v1_user_proto_rawDescGZIP(), []int{47} +} + +func (x *SubmitReportResponse) GetReport() *UserReport { + if x != nil { + return x.Report + } + return nil +} + // BatchGetUsersRequest 批量查询用户主状态。 type BatchGetUsersRequest struct { state protoimpl.MessageState `protogen:"open.v1"` @@ -3290,7 +3544,7 @@ type BatchGetUsersRequest struct { func (x *BatchGetUsersRequest) Reset() { *x = BatchGetUsersRequest{} - mi := &file_proto_user_v1_user_proto_msgTypes[45] + mi := &file_proto_user_v1_user_proto_msgTypes[48] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3302,7 +3556,7 @@ func (x *BatchGetUsersRequest) String() string { func (*BatchGetUsersRequest) ProtoMessage() {} func (x *BatchGetUsersRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_v1_user_proto_msgTypes[45] + mi := &file_proto_user_v1_user_proto_msgTypes[48] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3315,7 +3569,7 @@ func (x *BatchGetUsersRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use BatchGetUsersRequest.ProtoReflect.Descriptor instead. func (*BatchGetUsersRequest) Descriptor() ([]byte, []int) { - return file_proto_user_v1_user_proto_rawDescGZIP(), []int{45} + return file_proto_user_v1_user_proto_rawDescGZIP(), []int{48} } func (x *BatchGetUsersRequest) GetMeta() *RequestMeta { @@ -3342,7 +3596,7 @@ type BatchGetUsersResponse struct { func (x *BatchGetUsersResponse) Reset() { *x = BatchGetUsersResponse{} - mi := &file_proto_user_v1_user_proto_msgTypes[46] + mi := &file_proto_user_v1_user_proto_msgTypes[49] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3354,7 +3608,7 @@ func (x *BatchGetUsersResponse) String() string { func (*BatchGetUsersResponse) ProtoMessage() {} func (x *BatchGetUsersResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_v1_user_proto_msgTypes[46] + mi := &file_proto_user_v1_user_proto_msgTypes[49] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3367,7 +3621,7 @@ func (x *BatchGetUsersResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use BatchGetUsersResponse.ProtoReflect.Descriptor instead. func (*BatchGetUsersResponse) Descriptor() ([]byte, []int) { - return file_proto_user_v1_user_proto_rawDescGZIP(), []int{46} + return file_proto_user_v1_user_proto_rawDescGZIP(), []int{49} } func (x *BatchGetUsersResponse) GetUsers() map[int64]*User { @@ -3392,7 +3646,7 @@ type ListUserIDsRequest struct { func (x *ListUserIDsRequest) Reset() { *x = ListUserIDsRequest{} - mi := &file_proto_user_v1_user_proto_msgTypes[47] + mi := &file_proto_user_v1_user_proto_msgTypes[50] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3404,7 +3658,7 @@ func (x *ListUserIDsRequest) String() string { func (*ListUserIDsRequest) ProtoMessage() {} func (x *ListUserIDsRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_v1_user_proto_msgTypes[47] + mi := &file_proto_user_v1_user_proto_msgTypes[50] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3417,7 +3671,7 @@ func (x *ListUserIDsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListUserIDsRequest.ProtoReflect.Descriptor instead. func (*ListUserIDsRequest) Descriptor() ([]byte, []int) { - return file_proto_user_v1_user_proto_rawDescGZIP(), []int{47} + return file_proto_user_v1_user_proto_rawDescGZIP(), []int{50} } func (x *ListUserIDsRequest) GetMeta() *RequestMeta { @@ -3474,7 +3728,7 @@ type ListUserIDsResponse struct { func (x *ListUserIDsResponse) Reset() { *x = ListUserIDsResponse{} - mi := &file_proto_user_v1_user_proto_msgTypes[48] + mi := &file_proto_user_v1_user_proto_msgTypes[51] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3486,7 +3740,7 @@ func (x *ListUserIDsResponse) String() string { func (*ListUserIDsResponse) ProtoMessage() {} func (x *ListUserIDsResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_v1_user_proto_msgTypes[48] + mi := &file_proto_user_v1_user_proto_msgTypes[51] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3499,7 +3753,7 @@ func (x *ListUserIDsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListUserIDsResponse.ProtoReflect.Descriptor instead. func (*ListUserIDsResponse) Descriptor() ([]byte, []int) { - return file_proto_user_v1_user_proto_rawDescGZIP(), []int{48} + return file_proto_user_v1_user_proto_rawDescGZIP(), []int{51} } func (x *ListUserIDsResponse) GetUserIds() []int64 { @@ -3538,7 +3792,7 @@ type UpdateUserProfileRequest struct { func (x *UpdateUserProfileRequest) Reset() { *x = UpdateUserProfileRequest{} - mi := &file_proto_user_v1_user_proto_msgTypes[49] + mi := &file_proto_user_v1_user_proto_msgTypes[52] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3550,7 +3804,7 @@ func (x *UpdateUserProfileRequest) String() string { func (*UpdateUserProfileRequest) ProtoMessage() {} func (x *UpdateUserProfileRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_v1_user_proto_msgTypes[49] + mi := &file_proto_user_v1_user_proto_msgTypes[52] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3563,7 +3817,7 @@ func (x *UpdateUserProfileRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateUserProfileRequest.ProtoReflect.Descriptor instead. func (*UpdateUserProfileRequest) Descriptor() ([]byte, []int) { - return file_proto_user_v1_user_proto_rawDescGZIP(), []int{49} + return file_proto_user_v1_user_proto_rawDescGZIP(), []int{52} } func (x *UpdateUserProfileRequest) GetMeta() *RequestMeta { @@ -3618,7 +3872,7 @@ type UpdateUserProfileResponse struct { func (x *UpdateUserProfileResponse) Reset() { *x = UpdateUserProfileResponse{} - mi := &file_proto_user_v1_user_proto_msgTypes[50] + mi := &file_proto_user_v1_user_proto_msgTypes[53] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3630,7 +3884,7 @@ func (x *UpdateUserProfileResponse) String() string { func (*UpdateUserProfileResponse) ProtoMessage() {} func (x *UpdateUserProfileResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_v1_user_proto_msgTypes[50] + mi := &file_proto_user_v1_user_proto_msgTypes[53] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3643,7 +3897,7 @@ func (x *UpdateUserProfileResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateUserProfileResponse.ProtoReflect.Descriptor instead. func (*UpdateUserProfileResponse) Descriptor() ([]byte, []int) { - return file_proto_user_v1_user_proto_rawDescGZIP(), []int{50} + return file_proto_user_v1_user_proto_rawDescGZIP(), []int{53} } func (x *UpdateUserProfileResponse) GetUser() *User { @@ -3665,7 +3919,7 @@ type ChangeUserCountryRequest struct { func (x *ChangeUserCountryRequest) Reset() { *x = ChangeUserCountryRequest{} - mi := &file_proto_user_v1_user_proto_msgTypes[51] + mi := &file_proto_user_v1_user_proto_msgTypes[54] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3677,7 +3931,7 @@ func (x *ChangeUserCountryRequest) String() string { func (*ChangeUserCountryRequest) ProtoMessage() {} func (x *ChangeUserCountryRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_v1_user_proto_msgTypes[51] + mi := &file_proto_user_v1_user_proto_msgTypes[54] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3690,7 +3944,7 @@ func (x *ChangeUserCountryRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ChangeUserCountryRequest.ProtoReflect.Descriptor instead. func (*ChangeUserCountryRequest) Descriptor() ([]byte, []int) { - return file_proto_user_v1_user_proto_rawDescGZIP(), []int{51} + return file_proto_user_v1_user_proto_rawDescGZIP(), []int{54} } func (x *ChangeUserCountryRequest) GetMeta() *RequestMeta { @@ -3728,7 +3982,7 @@ type ChangeUserCountryResponse struct { func (x *ChangeUserCountryResponse) Reset() { *x = ChangeUserCountryResponse{} - mi := &file_proto_user_v1_user_proto_msgTypes[52] + mi := &file_proto_user_v1_user_proto_msgTypes[55] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3740,7 +3994,7 @@ func (x *ChangeUserCountryResponse) String() string { func (*ChangeUserCountryResponse) ProtoMessage() {} func (x *ChangeUserCountryResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_v1_user_proto_msgTypes[52] + mi := &file_proto_user_v1_user_proto_msgTypes[55] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3753,7 +4007,7 @@ func (x *ChangeUserCountryResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ChangeUserCountryResponse.ProtoReflect.Descriptor instead. func (*ChangeUserCountryResponse) Descriptor() ([]byte, []int) { - return file_proto_user_v1_user_proto_rawDescGZIP(), []int{52} + return file_proto_user_v1_user_proto_rawDescGZIP(), []int{55} } func (x *ChangeUserCountryResponse) GetUser() *User { @@ -3808,7 +4062,7 @@ type SetUserStatusRequest struct { func (x *SetUserStatusRequest) Reset() { *x = SetUserStatusRequest{} - mi := &file_proto_user_v1_user_proto_msgTypes[53] + mi := &file_proto_user_v1_user_proto_msgTypes[56] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3820,7 +4074,7 @@ func (x *SetUserStatusRequest) String() string { func (*SetUserStatusRequest) ProtoMessage() {} func (x *SetUserStatusRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_v1_user_proto_msgTypes[53] + mi := &file_proto_user_v1_user_proto_msgTypes[56] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3833,7 +4087,7 @@ func (x *SetUserStatusRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SetUserStatusRequest.ProtoReflect.Descriptor instead. func (*SetUserStatusRequest) Descriptor() ([]byte, []int) { - return file_proto_user_v1_user_proto_rawDescGZIP(), []int{53} + return file_proto_user_v1_user_proto_rawDescGZIP(), []int{56} } func (x *SetUserStatusRequest) GetMeta() *RequestMeta { @@ -3899,7 +4153,7 @@ type SetUserStatusResponse struct { func (x *SetUserStatusResponse) Reset() { *x = SetUserStatusResponse{} - mi := &file_proto_user_v1_user_proto_msgTypes[54] + mi := &file_proto_user_v1_user_proto_msgTypes[57] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3911,7 +4165,7 @@ func (x *SetUserStatusResponse) String() string { func (*SetUserStatusResponse) ProtoMessage() {} func (x *SetUserStatusResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_v1_user_proto_msgTypes[54] + mi := &file_proto_user_v1_user_proto_msgTypes[57] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3924,7 +4178,7 @@ func (x *SetUserStatusResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use SetUserStatusResponse.ProtoReflect.Descriptor instead. func (*SetUserStatusResponse) Descriptor() ([]byte, []int) { - return file_proto_user_v1_user_proto_rawDescGZIP(), []int{54} + return file_proto_user_v1_user_proto_rawDescGZIP(), []int{57} } func (x *SetUserStatusResponse) GetUser() *User { @@ -4020,7 +4274,7 @@ type CompleteOnboardingRequest struct { func (x *CompleteOnboardingRequest) Reset() { *x = CompleteOnboardingRequest{} - mi := &file_proto_user_v1_user_proto_msgTypes[55] + mi := &file_proto_user_v1_user_proto_msgTypes[58] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4032,7 +4286,7 @@ func (x *CompleteOnboardingRequest) String() string { func (*CompleteOnboardingRequest) ProtoMessage() {} func (x *CompleteOnboardingRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_v1_user_proto_msgTypes[55] + mi := &file_proto_user_v1_user_proto_msgTypes[58] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4045,7 +4299,7 @@ func (x *CompleteOnboardingRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CompleteOnboardingRequest.ProtoReflect.Descriptor instead. func (*CompleteOnboardingRequest) Descriptor() ([]byte, []int) { - return file_proto_user_v1_user_proto_rawDescGZIP(), []int{55} + return file_proto_user_v1_user_proto_rawDescGZIP(), []int{58} } func (x *CompleteOnboardingRequest) GetMeta() *RequestMeta { @@ -4112,7 +4366,7 @@ type CompleteOnboardingResponse struct { func (x *CompleteOnboardingResponse) Reset() { *x = CompleteOnboardingResponse{} - mi := &file_proto_user_v1_user_proto_msgTypes[56] + mi := &file_proto_user_v1_user_proto_msgTypes[59] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4124,7 +4378,7 @@ func (x *CompleteOnboardingResponse) String() string { func (*CompleteOnboardingResponse) ProtoMessage() {} func (x *CompleteOnboardingResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_v1_user_proto_msgTypes[56] + mi := &file_proto_user_v1_user_proto_msgTypes[59] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4137,7 +4391,7 @@ func (x *CompleteOnboardingResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use CompleteOnboardingResponse.ProtoReflect.Descriptor instead. func (*CompleteOnboardingResponse) Descriptor() ([]byte, []int) { - return file_proto_user_v1_user_proto_rawDescGZIP(), []int{56} + return file_proto_user_v1_user_proto_rawDescGZIP(), []int{59} } func (x *CompleteOnboardingResponse) GetUser() *User { @@ -4201,7 +4455,7 @@ type BindPushTokenRequest struct { func (x *BindPushTokenRequest) Reset() { *x = BindPushTokenRequest{} - mi := &file_proto_user_v1_user_proto_msgTypes[57] + mi := &file_proto_user_v1_user_proto_msgTypes[60] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4213,7 +4467,7 @@ func (x *BindPushTokenRequest) String() string { func (*BindPushTokenRequest) ProtoMessage() {} func (x *BindPushTokenRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_v1_user_proto_msgTypes[57] + mi := &file_proto_user_v1_user_proto_msgTypes[60] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4226,7 +4480,7 @@ func (x *BindPushTokenRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use BindPushTokenRequest.ProtoReflect.Descriptor instead. func (*BindPushTokenRequest) Descriptor() ([]byte, []int) { - return file_proto_user_v1_user_proto_rawDescGZIP(), []int{57} + return file_proto_user_v1_user_proto_rawDescGZIP(), []int{60} } func (x *BindPushTokenRequest) GetMeta() *RequestMeta { @@ -4302,7 +4556,7 @@ type BindPushTokenResponse struct { func (x *BindPushTokenResponse) Reset() { *x = BindPushTokenResponse{} - mi := &file_proto_user_v1_user_proto_msgTypes[58] + mi := &file_proto_user_v1_user_proto_msgTypes[61] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4314,7 +4568,7 @@ func (x *BindPushTokenResponse) String() string { func (*BindPushTokenResponse) ProtoMessage() {} func (x *BindPushTokenResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_v1_user_proto_msgTypes[58] + mi := &file_proto_user_v1_user_proto_msgTypes[61] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4327,7 +4581,7 @@ func (x *BindPushTokenResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use BindPushTokenResponse.ProtoReflect.Descriptor instead. func (*BindPushTokenResponse) Descriptor() ([]byte, []int) { - return file_proto_user_v1_user_proto_rawDescGZIP(), []int{58} + return file_proto_user_v1_user_proto_rawDescGZIP(), []int{61} } func (x *BindPushTokenResponse) GetBound() bool { @@ -4358,7 +4612,7 @@ type DeletePushTokenRequest struct { func (x *DeletePushTokenRequest) Reset() { *x = DeletePushTokenRequest{} - mi := &file_proto_user_v1_user_proto_msgTypes[59] + mi := &file_proto_user_v1_user_proto_msgTypes[62] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4370,7 +4624,7 @@ func (x *DeletePushTokenRequest) String() string { func (*DeletePushTokenRequest) ProtoMessage() {} func (x *DeletePushTokenRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_v1_user_proto_msgTypes[59] + mi := &file_proto_user_v1_user_proto_msgTypes[62] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4383,7 +4637,7 @@ func (x *DeletePushTokenRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DeletePushTokenRequest.ProtoReflect.Descriptor instead. func (*DeletePushTokenRequest) Descriptor() ([]byte, []int) { - return file_proto_user_v1_user_proto_rawDescGZIP(), []int{59} + return file_proto_user_v1_user_proto_rawDescGZIP(), []int{62} } func (x *DeletePushTokenRequest) GetMeta() *RequestMeta { @@ -4424,7 +4678,7 @@ type DeletePushTokenResponse struct { func (x *DeletePushTokenResponse) Reset() { *x = DeletePushTokenResponse{} - mi := &file_proto_user_v1_user_proto_msgTypes[60] + mi := &file_proto_user_v1_user_proto_msgTypes[63] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4436,7 +4690,7 @@ func (x *DeletePushTokenResponse) String() string { func (*DeletePushTokenResponse) ProtoMessage() {} func (x *DeletePushTokenResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_v1_user_proto_msgTypes[60] + mi := &file_proto_user_v1_user_proto_msgTypes[63] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4449,7 +4703,7 @@ func (x *DeletePushTokenResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use DeletePushTokenResponse.ProtoReflect.Descriptor instead. func (*DeletePushTokenResponse) Descriptor() ([]byte, []int) { - return file_proto_user_v1_user_proto_rawDescGZIP(), []int{60} + return file_proto_user_v1_user_proto_rawDescGZIP(), []int{63} } func (x *DeletePushTokenResponse) GetDeleted() bool { @@ -4488,7 +4742,7 @@ type Country struct { func (x *Country) Reset() { *x = Country{} - mi := &file_proto_user_v1_user_proto_msgTypes[61] + mi := &file_proto_user_v1_user_proto_msgTypes[64] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4500,7 +4754,7 @@ func (x *Country) String() string { func (*Country) ProtoMessage() {} func (x *Country) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_v1_user_proto_msgTypes[61] + mi := &file_proto_user_v1_user_proto_msgTypes[64] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4513,7 +4767,7 @@ func (x *Country) ProtoReflect() protoreflect.Message { // Deprecated: Use Country.ProtoReflect.Descriptor instead. func (*Country) Descriptor() ([]byte, []int) { - return file_proto_user_v1_user_proto_rawDescGZIP(), []int{61} + return file_proto_user_v1_user_proto_rawDescGZIP(), []int{64} } func (x *Country) GetCountryId() int64 { @@ -4625,7 +4879,7 @@ type Region struct { func (x *Region) Reset() { *x = Region{} - mi := &file_proto_user_v1_user_proto_msgTypes[62] + mi := &file_proto_user_v1_user_proto_msgTypes[65] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4637,7 +4891,7 @@ func (x *Region) String() string { func (*Region) ProtoMessage() {} func (x *Region) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_v1_user_proto_msgTypes[62] + mi := &file_proto_user_v1_user_proto_msgTypes[65] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4650,7 +4904,7 @@ func (x *Region) ProtoReflect() protoreflect.Message { // Deprecated: Use Region.ProtoReflect.Descriptor instead. func (*Region) Descriptor() ([]byte, []int) { - return file_proto_user_v1_user_proto_rawDescGZIP(), []int{62} + return file_proto_user_v1_user_proto_rawDescGZIP(), []int{65} } func (x *Region) GetRegionId() int64 { @@ -4726,7 +4980,7 @@ type ListCountriesRequest struct { func (x *ListCountriesRequest) Reset() { *x = ListCountriesRequest{} - mi := &file_proto_user_v1_user_proto_msgTypes[63] + mi := &file_proto_user_v1_user_proto_msgTypes[66] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4738,7 +4992,7 @@ func (x *ListCountriesRequest) String() string { func (*ListCountriesRequest) ProtoMessage() {} func (x *ListCountriesRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_v1_user_proto_msgTypes[63] + mi := &file_proto_user_v1_user_proto_msgTypes[66] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4751,7 +5005,7 @@ func (x *ListCountriesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListCountriesRequest.ProtoReflect.Descriptor instead. func (*ListCountriesRequest) Descriptor() ([]byte, []int) { - return file_proto_user_v1_user_proto_rawDescGZIP(), []int{63} + return file_proto_user_v1_user_proto_rawDescGZIP(), []int{66} } func (x *ListCountriesRequest) GetMeta() *RequestMeta { @@ -4777,7 +5031,7 @@ type ListCountriesResponse struct { func (x *ListCountriesResponse) Reset() { *x = ListCountriesResponse{} - mi := &file_proto_user_v1_user_proto_msgTypes[64] + mi := &file_proto_user_v1_user_proto_msgTypes[67] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4789,7 +5043,7 @@ func (x *ListCountriesResponse) String() string { func (*ListCountriesResponse) ProtoMessage() {} func (x *ListCountriesResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_v1_user_proto_msgTypes[64] + mi := &file_proto_user_v1_user_proto_msgTypes[67] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4802,7 +5056,7 @@ func (x *ListCountriesResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListCountriesResponse.ProtoReflect.Descriptor instead. func (*ListCountriesResponse) Descriptor() ([]byte, []int) { - return file_proto_user_v1_user_proto_rawDescGZIP(), []int{64} + return file_proto_user_v1_user_proto_rawDescGZIP(), []int{67} } func (x *ListCountriesResponse) GetCountries() []*Country { @@ -4831,7 +5085,7 @@ type UpdateCountryRequest struct { func (x *UpdateCountryRequest) Reset() { *x = UpdateCountryRequest{} - mi := &file_proto_user_v1_user_proto_msgTypes[65] + mi := &file_proto_user_v1_user_proto_msgTypes[68] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4843,7 +5097,7 @@ func (x *UpdateCountryRequest) String() string { func (*UpdateCountryRequest) ProtoMessage() {} func (x *UpdateCountryRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_v1_user_proto_msgTypes[65] + mi := &file_proto_user_v1_user_proto_msgTypes[68] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4856,7 +5110,7 @@ func (x *UpdateCountryRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateCountryRequest.ProtoReflect.Descriptor instead. func (*UpdateCountryRequest) Descriptor() ([]byte, []int) { - return file_proto_user_v1_user_proto_rawDescGZIP(), []int{65} + return file_proto_user_v1_user_proto_rawDescGZIP(), []int{68} } func (x *UpdateCountryRequest) GetMeta() *RequestMeta { @@ -4938,7 +5192,7 @@ type CountryResponse struct { func (x *CountryResponse) Reset() { *x = CountryResponse{} - mi := &file_proto_user_v1_user_proto_msgTypes[66] + mi := &file_proto_user_v1_user_proto_msgTypes[69] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4950,7 +5204,7 @@ func (x *CountryResponse) String() string { func (*CountryResponse) ProtoMessage() {} func (x *CountryResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_v1_user_proto_msgTypes[66] + mi := &file_proto_user_v1_user_proto_msgTypes[69] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4963,7 +5217,7 @@ func (x *CountryResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use CountryResponse.ProtoReflect.Descriptor instead. func (*CountryResponse) Descriptor() ([]byte, []int) { - return file_proto_user_v1_user_proto_rawDescGZIP(), []int{66} + return file_proto_user_v1_user_proto_rawDescGZIP(), []int{69} } func (x *CountryResponse) GetCountry() *Country { @@ -4982,7 +5236,7 @@ type ListRegistrationCountriesRequest struct { func (x *ListRegistrationCountriesRequest) Reset() { *x = ListRegistrationCountriesRequest{} - mi := &file_proto_user_v1_user_proto_msgTypes[67] + mi := &file_proto_user_v1_user_proto_msgTypes[70] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4994,7 +5248,7 @@ func (x *ListRegistrationCountriesRequest) String() string { func (*ListRegistrationCountriesRequest) ProtoMessage() {} func (x *ListRegistrationCountriesRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_v1_user_proto_msgTypes[67] + mi := &file_proto_user_v1_user_proto_msgTypes[70] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5007,7 +5261,7 @@ func (x *ListRegistrationCountriesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListRegistrationCountriesRequest.ProtoReflect.Descriptor instead. func (*ListRegistrationCountriesRequest) Descriptor() ([]byte, []int) { - return file_proto_user_v1_user_proto_rawDescGZIP(), []int{67} + return file_proto_user_v1_user_proto_rawDescGZIP(), []int{70} } func (x *ListRegistrationCountriesRequest) GetMeta() *RequestMeta { @@ -5026,7 +5280,7 @@ type ListRegistrationCountriesResponse struct { func (x *ListRegistrationCountriesResponse) Reset() { *x = ListRegistrationCountriesResponse{} - mi := &file_proto_user_v1_user_proto_msgTypes[68] + mi := &file_proto_user_v1_user_proto_msgTypes[71] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5038,7 +5292,7 @@ func (x *ListRegistrationCountriesResponse) String() string { func (*ListRegistrationCountriesResponse) ProtoMessage() {} func (x *ListRegistrationCountriesResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_v1_user_proto_msgTypes[68] + mi := &file_proto_user_v1_user_proto_msgTypes[71] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5051,7 +5305,7 @@ func (x *ListRegistrationCountriesResponse) ProtoReflect() protoreflect.Message // Deprecated: Use ListRegistrationCountriesResponse.ProtoReflect.Descriptor instead. func (*ListRegistrationCountriesResponse) Descriptor() ([]byte, []int) { - return file_proto_user_v1_user_proto_rawDescGZIP(), []int{68} + return file_proto_user_v1_user_proto_rawDescGZIP(), []int{71} } func (x *ListRegistrationCountriesResponse) GetCountries() []*Country { @@ -5072,7 +5326,7 @@ type LoginRiskBlockedCountry struct { func (x *LoginRiskBlockedCountry) Reset() { *x = LoginRiskBlockedCountry{} - mi := &file_proto_user_v1_user_proto_msgTypes[69] + mi := &file_proto_user_v1_user_proto_msgTypes[72] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5084,7 +5338,7 @@ func (x *LoginRiskBlockedCountry) String() string { func (*LoginRiskBlockedCountry) ProtoMessage() {} func (x *LoginRiskBlockedCountry) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_v1_user_proto_msgTypes[69] + mi := &file_proto_user_v1_user_proto_msgTypes[72] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5097,7 +5351,7 @@ func (x *LoginRiskBlockedCountry) ProtoReflect() protoreflect.Message { // Deprecated: Use LoginRiskBlockedCountry.ProtoReflect.Descriptor instead. func (*LoginRiskBlockedCountry) Descriptor() ([]byte, []int) { - return file_proto_user_v1_user_proto_rawDescGZIP(), []int{69} + return file_proto_user_v1_user_proto_rawDescGZIP(), []int{72} } func (x *LoginRiskBlockedCountry) GetCountryCode() string { @@ -5130,7 +5384,7 @@ type ListLoginRiskBlockedCountriesRequest struct { func (x *ListLoginRiskBlockedCountriesRequest) Reset() { *x = ListLoginRiskBlockedCountriesRequest{} - mi := &file_proto_user_v1_user_proto_msgTypes[70] + mi := &file_proto_user_v1_user_proto_msgTypes[73] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5142,7 +5396,7 @@ func (x *ListLoginRiskBlockedCountriesRequest) String() string { func (*ListLoginRiskBlockedCountriesRequest) ProtoMessage() {} func (x *ListLoginRiskBlockedCountriesRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_v1_user_proto_msgTypes[70] + mi := &file_proto_user_v1_user_proto_msgTypes[73] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5155,7 +5409,7 @@ func (x *ListLoginRiskBlockedCountriesRequest) ProtoReflect() protoreflect.Messa // Deprecated: Use ListLoginRiskBlockedCountriesRequest.ProtoReflect.Descriptor instead. func (*ListLoginRiskBlockedCountriesRequest) Descriptor() ([]byte, []int) { - return file_proto_user_v1_user_proto_rawDescGZIP(), []int{70} + return file_proto_user_v1_user_proto_rawDescGZIP(), []int{73} } func (x *ListLoginRiskBlockedCountriesRequest) GetMeta() *RequestMeta { @@ -5175,7 +5429,7 @@ type ListLoginRiskBlockedCountriesResponse struct { func (x *ListLoginRiskBlockedCountriesResponse) Reset() { *x = ListLoginRiskBlockedCountriesResponse{} - mi := &file_proto_user_v1_user_proto_msgTypes[71] + mi := &file_proto_user_v1_user_proto_msgTypes[74] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5187,7 +5441,7 @@ func (x *ListLoginRiskBlockedCountriesResponse) String() string { func (*ListLoginRiskBlockedCountriesResponse) ProtoMessage() {} func (x *ListLoginRiskBlockedCountriesResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_v1_user_proto_msgTypes[71] + mi := &file_proto_user_v1_user_proto_msgTypes[74] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5200,7 +5454,7 @@ func (x *ListLoginRiskBlockedCountriesResponse) ProtoReflect() protoreflect.Mess // Deprecated: Use ListLoginRiskBlockedCountriesResponse.ProtoReflect.Descriptor instead. func (*ListLoginRiskBlockedCountriesResponse) Descriptor() ([]byte, []int) { - return file_proto_user_v1_user_proto_rawDescGZIP(), []int{71} + return file_proto_user_v1_user_proto_rawDescGZIP(), []int{74} } func (x *ListLoginRiskBlockedCountriesResponse) GetCountries() []*LoginRiskBlockedCountry { @@ -5227,7 +5481,7 @@ type ListRegionsRequest struct { func (x *ListRegionsRequest) Reset() { *x = ListRegionsRequest{} - mi := &file_proto_user_v1_user_proto_msgTypes[72] + mi := &file_proto_user_v1_user_proto_msgTypes[75] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5239,7 +5493,7 @@ func (x *ListRegionsRequest) String() string { func (*ListRegionsRequest) ProtoMessage() {} func (x *ListRegionsRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_v1_user_proto_msgTypes[72] + mi := &file_proto_user_v1_user_proto_msgTypes[75] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5252,7 +5506,7 @@ func (x *ListRegionsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListRegionsRequest.ProtoReflect.Descriptor instead. func (*ListRegionsRequest) Descriptor() ([]byte, []int) { - return file_proto_user_v1_user_proto_rawDescGZIP(), []int{72} + return file_proto_user_v1_user_proto_rawDescGZIP(), []int{75} } func (x *ListRegionsRequest) GetMeta() *RequestMeta { @@ -5278,7 +5532,7 @@ type ListRegionsResponse struct { func (x *ListRegionsResponse) Reset() { *x = ListRegionsResponse{} - mi := &file_proto_user_v1_user_proto_msgTypes[73] + mi := &file_proto_user_v1_user_proto_msgTypes[76] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5290,7 +5544,7 @@ func (x *ListRegionsResponse) String() string { func (*ListRegionsResponse) ProtoMessage() {} func (x *ListRegionsResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_v1_user_proto_msgTypes[73] + mi := &file_proto_user_v1_user_proto_msgTypes[76] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5303,7 +5557,7 @@ func (x *ListRegionsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListRegionsResponse.ProtoReflect.Descriptor instead. func (*ListRegionsResponse) Descriptor() ([]byte, []int) { - return file_proto_user_v1_user_proto_rawDescGZIP(), []int{73} + return file_proto_user_v1_user_proto_rawDescGZIP(), []int{76} } func (x *ListRegionsResponse) GetRegions() []*Region { @@ -5323,7 +5577,7 @@ type GetRegionRequest struct { func (x *GetRegionRequest) Reset() { *x = GetRegionRequest{} - mi := &file_proto_user_v1_user_proto_msgTypes[74] + mi := &file_proto_user_v1_user_proto_msgTypes[77] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5335,7 +5589,7 @@ func (x *GetRegionRequest) String() string { func (*GetRegionRequest) ProtoMessage() {} func (x *GetRegionRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_v1_user_proto_msgTypes[74] + mi := &file_proto_user_v1_user_proto_msgTypes[77] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5348,7 +5602,7 @@ func (x *GetRegionRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetRegionRequest.ProtoReflect.Descriptor instead. func (*GetRegionRequest) Descriptor() ([]byte, []int) { - return file_proto_user_v1_user_proto_rawDescGZIP(), []int{74} + return file_proto_user_v1_user_proto_rawDescGZIP(), []int{77} } func (x *GetRegionRequest) GetMeta() *RequestMeta { @@ -5380,7 +5634,7 @@ type UpdateRegionRequest struct { func (x *UpdateRegionRequest) Reset() { *x = UpdateRegionRequest{} - mi := &file_proto_user_v1_user_proto_msgTypes[75] + mi := &file_proto_user_v1_user_proto_msgTypes[78] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5392,7 +5646,7 @@ func (x *UpdateRegionRequest) String() string { func (*UpdateRegionRequest) ProtoMessage() {} func (x *UpdateRegionRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_v1_user_proto_msgTypes[75] + mi := &file_proto_user_v1_user_proto_msgTypes[78] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5405,7 +5659,7 @@ func (x *UpdateRegionRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateRegionRequest.ProtoReflect.Descriptor instead. func (*UpdateRegionRequest) Descriptor() ([]byte, []int) { - return file_proto_user_v1_user_proto_rawDescGZIP(), []int{75} + return file_proto_user_v1_user_proto_rawDescGZIP(), []int{78} } func (x *UpdateRegionRequest) GetMeta() *RequestMeta { @@ -5462,7 +5716,7 @@ type ReplaceRegionCountriesRequest struct { func (x *ReplaceRegionCountriesRequest) Reset() { *x = ReplaceRegionCountriesRequest{} - mi := &file_proto_user_v1_user_proto_msgTypes[76] + mi := &file_proto_user_v1_user_proto_msgTypes[79] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5474,7 +5728,7 @@ func (x *ReplaceRegionCountriesRequest) String() string { func (*ReplaceRegionCountriesRequest) ProtoMessage() {} func (x *ReplaceRegionCountriesRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_v1_user_proto_msgTypes[76] + mi := &file_proto_user_v1_user_proto_msgTypes[79] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5487,7 +5741,7 @@ func (x *ReplaceRegionCountriesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ReplaceRegionCountriesRequest.ProtoReflect.Descriptor instead. func (*ReplaceRegionCountriesRequest) Descriptor() ([]byte, []int) { - return file_proto_user_v1_user_proto_rawDescGZIP(), []int{76} + return file_proto_user_v1_user_proto_rawDescGZIP(), []int{79} } func (x *ReplaceRegionCountriesRequest) GetMeta() *RequestMeta { @@ -5527,7 +5781,7 @@ type RegionResponse struct { func (x *RegionResponse) Reset() { *x = RegionResponse{} - mi := &file_proto_user_v1_user_proto_msgTypes[77] + mi := &file_proto_user_v1_user_proto_msgTypes[80] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5539,7 +5793,7 @@ func (x *RegionResponse) String() string { func (*RegionResponse) ProtoMessage() {} func (x *RegionResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_v1_user_proto_msgTypes[77] + mi := &file_proto_user_v1_user_proto_msgTypes[80] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5552,7 +5806,7 @@ func (x *RegionResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RegionResponse.ProtoReflect.Descriptor instead. func (*RegionResponse) Descriptor() ([]byte, []int) { - return file_proto_user_v1_user_proto_rawDescGZIP(), []int{77} + return file_proto_user_v1_user_proto_rawDescGZIP(), []int{80} } func (x *RegionResponse) GetRegion() *Region { @@ -5578,7 +5832,7 @@ type UserIdentity struct { func (x *UserIdentity) Reset() { *x = UserIdentity{} - mi := &file_proto_user_v1_user_proto_msgTypes[78] + mi := &file_proto_user_v1_user_proto_msgTypes[81] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5590,7 +5844,7 @@ func (x *UserIdentity) String() string { func (*UserIdentity) ProtoMessage() {} func (x *UserIdentity) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_v1_user_proto_msgTypes[78] + mi := &file_proto_user_v1_user_proto_msgTypes[81] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5603,7 +5857,7 @@ func (x *UserIdentity) ProtoReflect() protoreflect.Message { // Deprecated: Use UserIdentity.ProtoReflect.Descriptor instead. func (*UserIdentity) Descriptor() ([]byte, []int) { - return file_proto_user_v1_user_proto_rawDescGZIP(), []int{78} + return file_proto_user_v1_user_proto_rawDescGZIP(), []int{81} } func (x *UserIdentity) GetUserId() int64 { @@ -5666,7 +5920,7 @@ type GetUserIdentityRequest struct { func (x *GetUserIdentityRequest) Reset() { *x = GetUserIdentityRequest{} - mi := &file_proto_user_v1_user_proto_msgTypes[79] + mi := &file_proto_user_v1_user_proto_msgTypes[82] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5678,7 +5932,7 @@ func (x *GetUserIdentityRequest) String() string { func (*GetUserIdentityRequest) ProtoMessage() {} func (x *GetUserIdentityRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_v1_user_proto_msgTypes[79] + mi := &file_proto_user_v1_user_proto_msgTypes[82] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5691,7 +5945,7 @@ func (x *GetUserIdentityRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetUserIdentityRequest.ProtoReflect.Descriptor instead. func (*GetUserIdentityRequest) Descriptor() ([]byte, []int) { - return file_proto_user_v1_user_proto_rawDescGZIP(), []int{79} + return file_proto_user_v1_user_proto_rawDescGZIP(), []int{82} } func (x *GetUserIdentityRequest) GetMeta() *RequestMeta { @@ -5718,7 +5972,7 @@ type GetUserIdentityResponse struct { func (x *GetUserIdentityResponse) Reset() { *x = GetUserIdentityResponse{} - mi := &file_proto_user_v1_user_proto_msgTypes[80] + mi := &file_proto_user_v1_user_proto_msgTypes[83] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5730,7 +5984,7 @@ func (x *GetUserIdentityResponse) String() string { func (*GetUserIdentityResponse) ProtoMessage() {} func (x *GetUserIdentityResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_v1_user_proto_msgTypes[80] + mi := &file_proto_user_v1_user_proto_msgTypes[83] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5743,7 +5997,7 @@ func (x *GetUserIdentityResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetUserIdentityResponse.ProtoReflect.Descriptor instead. func (*GetUserIdentityResponse) Descriptor() ([]byte, []int) { - return file_proto_user_v1_user_proto_rawDescGZIP(), []int{80} + return file_proto_user_v1_user_proto_rawDescGZIP(), []int{83} } func (x *GetUserIdentityResponse) GetIdentity() *UserIdentity { @@ -5764,7 +6018,7 @@ type ResolveDisplayUserIDRequest struct { func (x *ResolveDisplayUserIDRequest) Reset() { *x = ResolveDisplayUserIDRequest{} - mi := &file_proto_user_v1_user_proto_msgTypes[81] + mi := &file_proto_user_v1_user_proto_msgTypes[84] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5776,7 +6030,7 @@ func (x *ResolveDisplayUserIDRequest) String() string { func (*ResolveDisplayUserIDRequest) ProtoMessage() {} func (x *ResolveDisplayUserIDRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_v1_user_proto_msgTypes[81] + mi := &file_proto_user_v1_user_proto_msgTypes[84] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5789,7 +6043,7 @@ func (x *ResolveDisplayUserIDRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ResolveDisplayUserIDRequest.ProtoReflect.Descriptor instead. func (*ResolveDisplayUserIDRequest) Descriptor() ([]byte, []int) { - return file_proto_user_v1_user_proto_rawDescGZIP(), []int{81} + return file_proto_user_v1_user_proto_rawDescGZIP(), []int{84} } func (x *ResolveDisplayUserIDRequest) GetMeta() *RequestMeta { @@ -5816,7 +6070,7 @@ type ResolveDisplayUserIDResponse struct { func (x *ResolveDisplayUserIDResponse) Reset() { *x = ResolveDisplayUserIDResponse{} - mi := &file_proto_user_v1_user_proto_msgTypes[82] + mi := &file_proto_user_v1_user_proto_msgTypes[85] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5828,7 +6082,7 @@ func (x *ResolveDisplayUserIDResponse) String() string { func (*ResolveDisplayUserIDResponse) ProtoMessage() {} func (x *ResolveDisplayUserIDResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_v1_user_proto_msgTypes[82] + mi := &file_proto_user_v1_user_proto_msgTypes[85] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5841,7 +6095,7 @@ func (x *ResolveDisplayUserIDResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ResolveDisplayUserIDResponse.ProtoReflect.Descriptor instead. func (*ResolveDisplayUserIDResponse) Descriptor() ([]byte, []int) { - return file_proto_user_v1_user_proto_rawDescGZIP(), []int{82} + return file_proto_user_v1_user_proto_rawDescGZIP(), []int{85} } func (x *ResolveDisplayUserIDResponse) GetIdentity() *UserIdentity { @@ -5865,7 +6119,7 @@ type ChangeDisplayUserIDRequest struct { func (x *ChangeDisplayUserIDRequest) Reset() { *x = ChangeDisplayUserIDRequest{} - mi := &file_proto_user_v1_user_proto_msgTypes[83] + mi := &file_proto_user_v1_user_proto_msgTypes[86] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5877,7 +6131,7 @@ func (x *ChangeDisplayUserIDRequest) String() string { func (*ChangeDisplayUserIDRequest) ProtoMessage() {} func (x *ChangeDisplayUserIDRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_v1_user_proto_msgTypes[83] + mi := &file_proto_user_v1_user_proto_msgTypes[86] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5890,7 +6144,7 @@ func (x *ChangeDisplayUserIDRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ChangeDisplayUserIDRequest.ProtoReflect.Descriptor instead. func (*ChangeDisplayUserIDRequest) Descriptor() ([]byte, []int) { - return file_proto_user_v1_user_proto_rawDescGZIP(), []int{83} + return file_proto_user_v1_user_proto_rawDescGZIP(), []int{86} } func (x *ChangeDisplayUserIDRequest) GetMeta() *RequestMeta { @@ -5938,7 +6192,7 @@ type ChangeDisplayUserIDResponse struct { func (x *ChangeDisplayUserIDResponse) Reset() { *x = ChangeDisplayUserIDResponse{} - mi := &file_proto_user_v1_user_proto_msgTypes[84] + mi := &file_proto_user_v1_user_proto_msgTypes[87] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5950,7 +6204,7 @@ func (x *ChangeDisplayUserIDResponse) String() string { func (*ChangeDisplayUserIDResponse) ProtoMessage() {} func (x *ChangeDisplayUserIDResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_v1_user_proto_msgTypes[84] + mi := &file_proto_user_v1_user_proto_msgTypes[87] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5963,7 +6217,7 @@ func (x *ChangeDisplayUserIDResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ChangeDisplayUserIDResponse.ProtoReflect.Descriptor instead. func (*ChangeDisplayUserIDResponse) Descriptor() ([]byte, []int) { - return file_proto_user_v1_user_proto_rawDescGZIP(), []int{84} + return file_proto_user_v1_user_proto_rawDescGZIP(), []int{87} } func (x *ChangeDisplayUserIDResponse) GetIdentity() *UserIdentity { @@ -5987,7 +6241,7 @@ type ApplyPrettyDisplayUserIDRequest struct { func (x *ApplyPrettyDisplayUserIDRequest) Reset() { *x = ApplyPrettyDisplayUserIDRequest{} - mi := &file_proto_user_v1_user_proto_msgTypes[85] + mi := &file_proto_user_v1_user_proto_msgTypes[88] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5999,7 +6253,7 @@ func (x *ApplyPrettyDisplayUserIDRequest) String() string { func (*ApplyPrettyDisplayUserIDRequest) ProtoMessage() {} func (x *ApplyPrettyDisplayUserIDRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_v1_user_proto_msgTypes[85] + mi := &file_proto_user_v1_user_proto_msgTypes[88] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6012,7 +6266,7 @@ func (x *ApplyPrettyDisplayUserIDRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ApplyPrettyDisplayUserIDRequest.ProtoReflect.Descriptor instead. func (*ApplyPrettyDisplayUserIDRequest) Descriptor() ([]byte, []int) { - return file_proto_user_v1_user_proto_rawDescGZIP(), []int{85} + return file_proto_user_v1_user_proto_rawDescGZIP(), []int{88} } func (x *ApplyPrettyDisplayUserIDRequest) GetMeta() *RequestMeta { @@ -6061,7 +6315,7 @@ type ApplyPrettyDisplayUserIDResponse struct { func (x *ApplyPrettyDisplayUserIDResponse) Reset() { *x = ApplyPrettyDisplayUserIDResponse{} - mi := &file_proto_user_v1_user_proto_msgTypes[86] + mi := &file_proto_user_v1_user_proto_msgTypes[89] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6073,7 +6327,7 @@ func (x *ApplyPrettyDisplayUserIDResponse) String() string { func (*ApplyPrettyDisplayUserIDResponse) ProtoMessage() {} func (x *ApplyPrettyDisplayUserIDResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_v1_user_proto_msgTypes[86] + mi := &file_proto_user_v1_user_proto_msgTypes[89] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6086,7 +6340,7 @@ func (x *ApplyPrettyDisplayUserIDResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ApplyPrettyDisplayUserIDResponse.ProtoReflect.Descriptor instead. func (*ApplyPrettyDisplayUserIDResponse) Descriptor() ([]byte, []int) { - return file_proto_user_v1_user_proto_rawDescGZIP(), []int{86} + return file_proto_user_v1_user_proto_rawDescGZIP(), []int{89} } func (x *ApplyPrettyDisplayUserIDResponse) GetIdentity() *UserIdentity { @@ -6115,7 +6369,7 @@ type ExpirePrettyDisplayUserIDRequest struct { func (x *ExpirePrettyDisplayUserIDRequest) Reset() { *x = ExpirePrettyDisplayUserIDRequest{} - mi := &file_proto_user_v1_user_proto_msgTypes[87] + mi := &file_proto_user_v1_user_proto_msgTypes[90] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6127,7 +6381,7 @@ func (x *ExpirePrettyDisplayUserIDRequest) String() string { func (*ExpirePrettyDisplayUserIDRequest) ProtoMessage() {} func (x *ExpirePrettyDisplayUserIDRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_v1_user_proto_msgTypes[87] + mi := &file_proto_user_v1_user_proto_msgTypes[90] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6140,7 +6394,7 @@ func (x *ExpirePrettyDisplayUserIDRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ExpirePrettyDisplayUserIDRequest.ProtoReflect.Descriptor instead. func (*ExpirePrettyDisplayUserIDRequest) Descriptor() ([]byte, []int) { - return file_proto_user_v1_user_proto_rawDescGZIP(), []int{87} + return file_proto_user_v1_user_proto_rawDescGZIP(), []int{90} } func (x *ExpirePrettyDisplayUserIDRequest) GetMeta() *RequestMeta { @@ -6174,7 +6428,7 @@ type ExpirePrettyDisplayUserIDResponse struct { func (x *ExpirePrettyDisplayUserIDResponse) Reset() { *x = ExpirePrettyDisplayUserIDResponse{} - mi := &file_proto_user_v1_user_proto_msgTypes[88] + mi := &file_proto_user_v1_user_proto_msgTypes[91] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6186,7 +6440,7 @@ func (x *ExpirePrettyDisplayUserIDResponse) String() string { func (*ExpirePrettyDisplayUserIDResponse) ProtoMessage() {} func (x *ExpirePrettyDisplayUserIDResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_v1_user_proto_msgTypes[88] + mi := &file_proto_user_v1_user_proto_msgTypes[91] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6199,7 +6453,7 @@ func (x *ExpirePrettyDisplayUserIDResponse) ProtoReflect() protoreflect.Message // Deprecated: Use ExpirePrettyDisplayUserIDResponse.ProtoReflect.Descriptor instead. func (*ExpirePrettyDisplayUserIDResponse) Descriptor() ([]byte, []int) { - return file_proto_user_v1_user_proto_rawDescGZIP(), []int{88} + return file_proto_user_v1_user_proto_rawDescGZIP(), []int{91} } func (x *ExpirePrettyDisplayUserIDResponse) GetIdentity() *UserIdentity { @@ -6472,7 +6726,35 @@ const file_proto_user_v1_user_proto_rawDesc = "" + "\tpage_size\x18\x05 \x01(\x05R\bpageSize\"|\n" + "\x1eListFriendApplicationsResponse\x12D\n" + "\fapplications\x18\x01 \x03(\v2 .hyapp.user.v1.FriendApplicationR\fapplications\x12\x14\n" + - "\x05total\x18\x02 \x01(\x03R\x05total\"a\n" + + "\x05total\x18\x02 \x01(\x03R\x05total\"\xba\x02\n" + + "\n" + + "UserReport\x12\x1b\n" + + "\treport_id\x18\x01 \x01(\tR\breportId\x12(\n" + + "\x10reporter_user_id\x18\x02 \x01(\x03R\x0ereporterUserId\x12\x1f\n" + + "\vtarget_type\x18\x03 \x01(\tR\n" + + "targetType\x12\x17\n" + + "\auser_id\x18\x04 \x01(\x03R\x06userId\x12\x17\n" + + "\aroom_id\x18\x05 \x01(\tR\x06roomId\x12\x1f\n" + + "\vreport_type\x18\x06 \x01(\tR\n" + + "reportType\x12\x16\n" + + "\x06reason\x18\a \x01(\tR\x06reason\x12\x1d\n" + + "\n" + + "image_urls\x18\b \x03(\tR\timageUrls\x12\x16\n" + + "\x06status\x18\t \x01(\tR\x06status\x12\"\n" + + "\rcreated_at_ms\x18\n" + + " \x01(\x03R\vcreatedAtMs\"\xf9\x01\n" + + "\x13SubmitReportRequest\x12.\n" + + "\x04meta\x18\x01 \x01(\v2\x1a.hyapp.user.v1.RequestMetaR\x04meta\x12(\n" + + "\x10reporter_user_id\x18\x02 \x01(\x03R\x0ereporterUserId\x12\x17\n" + + "\auser_id\x18\x03 \x01(\x03R\x06userId\x12\x17\n" + + "\aroom_id\x18\x04 \x01(\tR\x06roomId\x12\x1f\n" + + "\vreport_type\x18\x05 \x01(\tR\n" + + "reportType\x12\x16\n" + + "\x06reason\x18\x06 \x01(\tR\x06reason\x12\x1d\n" + + "\n" + + "image_urls\x18\a \x03(\tR\timageUrls\"I\n" + + "\x14SubmitReportResponse\x121\n" + + "\x06report\x18\x01 \x01(\v2\x19.hyapp.user.v1.UserReportR\x06report\"a\n" + "\x14BatchGetUsersRequest\x12.\n" + "\x04meta\x18\x01 \x01(\v2\x1a.hyapp.user.v1.RequestMetaR\x04meta\x12\x19\n" + "\buser_ids\x18\x02 \x03(\x03R\auserIds\"\xad\x01\n" + @@ -6727,7 +7009,7 @@ const file_proto_user_v1_user_proto_rawDesc = "" + "\x11UpdateUserProfile\x12'.hyapp.user.v1.UpdateUserProfileRequest\x1a(.hyapp.user.v1.UpdateUserProfileResponse\x12f\n" + "\x11ChangeUserCountry\x12'.hyapp.user.v1.ChangeUserCountryRequest\x1a(.hyapp.user.v1.ChangeUserCountryResponse\x12Z\n" + "\rSetUserStatus\x12#.hyapp.user.v1.SetUserStatusRequest\x1a$.hyapp.user.v1.SetUserStatusResponse\x12i\n" + - "\x12CompleteOnboarding\x12(.hyapp.user.v1.CompleteOnboardingRequest\x1a).hyapp.user.v1.CompleteOnboardingResponse2\xea\a\n" + + "\x12CompleteOnboarding\x12(.hyapp.user.v1.CompleteOnboardingRequest\x1a).hyapp.user.v1.CompleteOnboardingResponse2\xc3\b\n" + "\x11UserSocialService\x12i\n" + "\x12RecordProfileVisit\x12(.hyapp.user.v1.RecordProfileVisitRequest\x1a).hyapp.user.v1.RecordProfileVisitResponse\x12l\n" + "\x13ListProfileVisitors\x12).hyapp.user.v1.ListProfileVisitorsRequest\x1a*.hyapp.user.v1.ListProfileVisitorsResponse\x12Q\n" + @@ -6739,7 +7021,8 @@ const file_proto_user_v1_user_proto_rawDesc = "" + "\x17AcceptFriendApplication\x12-.hyapp.user.v1.AcceptFriendApplicationRequest\x1a..hyapp.user.v1.AcceptFriendApplicationResponse\x12W\n" + "\fDeleteFriend\x12\".hyapp.user.v1.DeleteFriendRequest\x1a#.hyapp.user.v1.DeleteFriendResponse\x12T\n" + "\vListFriends\x12!.hyapp.user.v1.ListFriendsRequest\x1a\".hyapp.user.v1.ListFriendsResponse\x12u\n" + - "\x16ListFriendApplications\x12,.hyapp.user.v1.ListFriendApplicationsRequest\x1a-.hyapp.user.v1.ListFriendApplicationsResponse2\xaf\x02\n" + + "\x16ListFriendApplications\x12,.hyapp.user.v1.ListFriendApplicationsRequest\x1a-.hyapp.user.v1.ListFriendApplicationsResponse\x12W\n" + + "\fSubmitReport\x12\".hyapp.user.v1.SubmitReportRequest\x1a#.hyapp.user.v1.SubmitReportResponse2\xaf\x02\n" + "\x0fUserCronService\x12\\\n" + "\x17ProcessLoginIPRiskBatch\x12\x1f.hyapp.user.v1.CronBatchRequest\x1a .hyapp.user.v1.CronBatchResponse\x12^\n" + "\x19ProcessRegionRebuildBatch\x12\x1f.hyapp.user.v1.CronBatchRequest\x1a .hyapp.user.v1.CronBatchResponse\x12^\n" + @@ -6781,7 +7064,7 @@ func file_proto_user_v1_user_proto_rawDescGZIP() []byte { } var file_proto_user_v1_user_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_proto_user_v1_user_proto_msgTypes = make([]protoimpl.MessageInfo, 90) +var file_proto_user_v1_user_proto_msgTypes = make([]protoimpl.MessageInfo, 93) var file_proto_user_v1_user_proto_goTypes = []any{ (UserStatus)(0), // 0: hyapp.user.v1.UserStatus (*RequestMeta)(nil), // 1: hyapp.user.v1.RequestMeta @@ -6829,51 +7112,54 @@ var file_proto_user_v1_user_proto_goTypes = []any{ (*FriendApplication)(nil), // 43: hyapp.user.v1.FriendApplication (*ListFriendApplicationsRequest)(nil), // 44: hyapp.user.v1.ListFriendApplicationsRequest (*ListFriendApplicationsResponse)(nil), // 45: hyapp.user.v1.ListFriendApplicationsResponse - (*BatchGetUsersRequest)(nil), // 46: hyapp.user.v1.BatchGetUsersRequest - (*BatchGetUsersResponse)(nil), // 47: hyapp.user.v1.BatchGetUsersResponse - (*ListUserIDsRequest)(nil), // 48: hyapp.user.v1.ListUserIDsRequest - (*ListUserIDsResponse)(nil), // 49: hyapp.user.v1.ListUserIDsResponse - (*UpdateUserProfileRequest)(nil), // 50: hyapp.user.v1.UpdateUserProfileRequest - (*UpdateUserProfileResponse)(nil), // 51: hyapp.user.v1.UpdateUserProfileResponse - (*ChangeUserCountryRequest)(nil), // 52: hyapp.user.v1.ChangeUserCountryRequest - (*ChangeUserCountryResponse)(nil), // 53: hyapp.user.v1.ChangeUserCountryResponse - (*SetUserStatusRequest)(nil), // 54: hyapp.user.v1.SetUserStatusRequest - (*SetUserStatusResponse)(nil), // 55: hyapp.user.v1.SetUserStatusResponse - (*CompleteOnboardingRequest)(nil), // 56: hyapp.user.v1.CompleteOnboardingRequest - (*CompleteOnboardingResponse)(nil), // 57: hyapp.user.v1.CompleteOnboardingResponse - (*BindPushTokenRequest)(nil), // 58: hyapp.user.v1.BindPushTokenRequest - (*BindPushTokenResponse)(nil), // 59: hyapp.user.v1.BindPushTokenResponse - (*DeletePushTokenRequest)(nil), // 60: hyapp.user.v1.DeletePushTokenRequest - (*DeletePushTokenResponse)(nil), // 61: hyapp.user.v1.DeletePushTokenResponse - (*Country)(nil), // 62: hyapp.user.v1.Country - (*Region)(nil), // 63: hyapp.user.v1.Region - (*ListCountriesRequest)(nil), // 64: hyapp.user.v1.ListCountriesRequest - (*ListCountriesResponse)(nil), // 65: hyapp.user.v1.ListCountriesResponse - (*UpdateCountryRequest)(nil), // 66: hyapp.user.v1.UpdateCountryRequest - (*CountryResponse)(nil), // 67: hyapp.user.v1.CountryResponse - (*ListRegistrationCountriesRequest)(nil), // 68: hyapp.user.v1.ListRegistrationCountriesRequest - (*ListRegistrationCountriesResponse)(nil), // 69: hyapp.user.v1.ListRegistrationCountriesResponse - (*LoginRiskBlockedCountry)(nil), // 70: hyapp.user.v1.LoginRiskBlockedCountry - (*ListLoginRiskBlockedCountriesRequest)(nil), // 71: hyapp.user.v1.ListLoginRiskBlockedCountriesRequest - (*ListLoginRiskBlockedCountriesResponse)(nil), // 72: hyapp.user.v1.ListLoginRiskBlockedCountriesResponse - (*ListRegionsRequest)(nil), // 73: hyapp.user.v1.ListRegionsRequest - (*ListRegionsResponse)(nil), // 74: hyapp.user.v1.ListRegionsResponse - (*GetRegionRequest)(nil), // 75: hyapp.user.v1.GetRegionRequest - (*UpdateRegionRequest)(nil), // 76: hyapp.user.v1.UpdateRegionRequest - (*ReplaceRegionCountriesRequest)(nil), // 77: hyapp.user.v1.ReplaceRegionCountriesRequest - (*RegionResponse)(nil), // 78: hyapp.user.v1.RegionResponse - (*UserIdentity)(nil), // 79: hyapp.user.v1.UserIdentity - (*GetUserIdentityRequest)(nil), // 80: hyapp.user.v1.GetUserIdentityRequest - (*GetUserIdentityResponse)(nil), // 81: hyapp.user.v1.GetUserIdentityResponse - (*ResolveDisplayUserIDRequest)(nil), // 82: hyapp.user.v1.ResolveDisplayUserIDRequest - (*ResolveDisplayUserIDResponse)(nil), // 83: hyapp.user.v1.ResolveDisplayUserIDResponse - (*ChangeDisplayUserIDRequest)(nil), // 84: hyapp.user.v1.ChangeDisplayUserIDRequest - (*ChangeDisplayUserIDResponse)(nil), // 85: hyapp.user.v1.ChangeDisplayUserIDResponse - (*ApplyPrettyDisplayUserIDRequest)(nil), // 86: hyapp.user.v1.ApplyPrettyDisplayUserIDRequest - (*ApplyPrettyDisplayUserIDResponse)(nil), // 87: hyapp.user.v1.ApplyPrettyDisplayUserIDResponse - (*ExpirePrettyDisplayUserIDRequest)(nil), // 88: hyapp.user.v1.ExpirePrettyDisplayUserIDRequest - (*ExpirePrettyDisplayUserIDResponse)(nil), // 89: hyapp.user.v1.ExpirePrettyDisplayUserIDResponse - nil, // 90: hyapp.user.v1.BatchGetUsersResponse.UsersEntry + (*UserReport)(nil), // 46: hyapp.user.v1.UserReport + (*SubmitReportRequest)(nil), // 47: hyapp.user.v1.SubmitReportRequest + (*SubmitReportResponse)(nil), // 48: hyapp.user.v1.SubmitReportResponse + (*BatchGetUsersRequest)(nil), // 49: hyapp.user.v1.BatchGetUsersRequest + (*BatchGetUsersResponse)(nil), // 50: hyapp.user.v1.BatchGetUsersResponse + (*ListUserIDsRequest)(nil), // 51: hyapp.user.v1.ListUserIDsRequest + (*ListUserIDsResponse)(nil), // 52: hyapp.user.v1.ListUserIDsResponse + (*UpdateUserProfileRequest)(nil), // 53: hyapp.user.v1.UpdateUserProfileRequest + (*UpdateUserProfileResponse)(nil), // 54: hyapp.user.v1.UpdateUserProfileResponse + (*ChangeUserCountryRequest)(nil), // 55: hyapp.user.v1.ChangeUserCountryRequest + (*ChangeUserCountryResponse)(nil), // 56: hyapp.user.v1.ChangeUserCountryResponse + (*SetUserStatusRequest)(nil), // 57: hyapp.user.v1.SetUserStatusRequest + (*SetUserStatusResponse)(nil), // 58: hyapp.user.v1.SetUserStatusResponse + (*CompleteOnboardingRequest)(nil), // 59: hyapp.user.v1.CompleteOnboardingRequest + (*CompleteOnboardingResponse)(nil), // 60: hyapp.user.v1.CompleteOnboardingResponse + (*BindPushTokenRequest)(nil), // 61: hyapp.user.v1.BindPushTokenRequest + (*BindPushTokenResponse)(nil), // 62: hyapp.user.v1.BindPushTokenResponse + (*DeletePushTokenRequest)(nil), // 63: hyapp.user.v1.DeletePushTokenRequest + (*DeletePushTokenResponse)(nil), // 64: hyapp.user.v1.DeletePushTokenResponse + (*Country)(nil), // 65: hyapp.user.v1.Country + (*Region)(nil), // 66: hyapp.user.v1.Region + (*ListCountriesRequest)(nil), // 67: hyapp.user.v1.ListCountriesRequest + (*ListCountriesResponse)(nil), // 68: hyapp.user.v1.ListCountriesResponse + (*UpdateCountryRequest)(nil), // 69: hyapp.user.v1.UpdateCountryRequest + (*CountryResponse)(nil), // 70: hyapp.user.v1.CountryResponse + (*ListRegistrationCountriesRequest)(nil), // 71: hyapp.user.v1.ListRegistrationCountriesRequest + (*ListRegistrationCountriesResponse)(nil), // 72: hyapp.user.v1.ListRegistrationCountriesResponse + (*LoginRiskBlockedCountry)(nil), // 73: hyapp.user.v1.LoginRiskBlockedCountry + (*ListLoginRiskBlockedCountriesRequest)(nil), // 74: hyapp.user.v1.ListLoginRiskBlockedCountriesRequest + (*ListLoginRiskBlockedCountriesResponse)(nil), // 75: hyapp.user.v1.ListLoginRiskBlockedCountriesResponse + (*ListRegionsRequest)(nil), // 76: hyapp.user.v1.ListRegionsRequest + (*ListRegionsResponse)(nil), // 77: hyapp.user.v1.ListRegionsResponse + (*GetRegionRequest)(nil), // 78: hyapp.user.v1.GetRegionRequest + (*UpdateRegionRequest)(nil), // 79: hyapp.user.v1.UpdateRegionRequest + (*ReplaceRegionCountriesRequest)(nil), // 80: hyapp.user.v1.ReplaceRegionCountriesRequest + (*RegionResponse)(nil), // 81: hyapp.user.v1.RegionResponse + (*UserIdentity)(nil), // 82: hyapp.user.v1.UserIdentity + (*GetUserIdentityRequest)(nil), // 83: hyapp.user.v1.GetUserIdentityRequest + (*GetUserIdentityResponse)(nil), // 84: hyapp.user.v1.GetUserIdentityResponse + (*ResolveDisplayUserIDRequest)(nil), // 85: hyapp.user.v1.ResolveDisplayUserIDRequest + (*ResolveDisplayUserIDResponse)(nil), // 86: hyapp.user.v1.ResolveDisplayUserIDResponse + (*ChangeDisplayUserIDRequest)(nil), // 87: hyapp.user.v1.ChangeDisplayUserIDRequest + (*ChangeDisplayUserIDResponse)(nil), // 88: hyapp.user.v1.ChangeDisplayUserIDResponse + (*ApplyPrettyDisplayUserIDRequest)(nil), // 89: hyapp.user.v1.ApplyPrettyDisplayUserIDRequest + (*ApplyPrettyDisplayUserIDResponse)(nil), // 90: hyapp.user.v1.ApplyPrettyDisplayUserIDResponse + (*ExpirePrettyDisplayUserIDRequest)(nil), // 91: hyapp.user.v1.ExpirePrettyDisplayUserIDRequest + (*ExpirePrettyDisplayUserIDResponse)(nil), // 92: hyapp.user.v1.ExpirePrettyDisplayUserIDResponse + nil, // 93: hyapp.user.v1.BatchGetUsersResponse.UsersEntry } var file_proto_user_v1_user_proto_depIdxs = []int32{ 1, // 0: hyapp.user.v1.ResolveAppRequest.meta:type_name -> hyapp.user.v1.RequestMeta @@ -6909,130 +7195,134 @@ var file_proto_user_v1_user_proto_depIdxs = []int32{ 40, // 30: hyapp.user.v1.ListFriendsResponse.records:type_name -> hyapp.user.v1.FriendRecord 1, // 31: hyapp.user.v1.ListFriendApplicationsRequest.meta:type_name -> hyapp.user.v1.RequestMeta 43, // 32: hyapp.user.v1.ListFriendApplicationsResponse.applications:type_name -> hyapp.user.v1.FriendApplication - 1, // 33: hyapp.user.v1.BatchGetUsersRequest.meta:type_name -> hyapp.user.v1.RequestMeta - 90, // 34: hyapp.user.v1.BatchGetUsersResponse.users:type_name -> hyapp.user.v1.BatchGetUsersResponse.UsersEntry - 1, // 35: hyapp.user.v1.ListUserIDsRequest.meta:type_name -> hyapp.user.v1.RequestMeta - 1, // 36: hyapp.user.v1.UpdateUserProfileRequest.meta:type_name -> hyapp.user.v1.RequestMeta - 5, // 37: hyapp.user.v1.UpdateUserProfileResponse.user:type_name -> hyapp.user.v1.User - 1, // 38: hyapp.user.v1.ChangeUserCountryRequest.meta:type_name -> hyapp.user.v1.RequestMeta - 5, // 39: hyapp.user.v1.ChangeUserCountryResponse.user:type_name -> hyapp.user.v1.User - 1, // 40: hyapp.user.v1.SetUserStatusRequest.meta:type_name -> hyapp.user.v1.RequestMeta - 0, // 41: hyapp.user.v1.SetUserStatusRequest.status:type_name -> hyapp.user.v1.UserStatus - 5, // 42: hyapp.user.v1.SetUserStatusResponse.user:type_name -> hyapp.user.v1.User - 1, // 43: hyapp.user.v1.CompleteOnboardingRequest.meta:type_name -> hyapp.user.v1.RequestMeta - 5, // 44: hyapp.user.v1.CompleteOnboardingResponse.user:type_name -> hyapp.user.v1.User - 13, // 45: hyapp.user.v1.CompleteOnboardingResponse.token:type_name -> hyapp.user.v1.AuthToken - 7, // 46: hyapp.user.v1.CompleteOnboardingResponse.invite:type_name -> hyapp.user.v1.InviteBinding - 1, // 47: hyapp.user.v1.BindPushTokenRequest.meta:type_name -> hyapp.user.v1.RequestMeta - 1, // 48: hyapp.user.v1.DeletePushTokenRequest.meta:type_name -> hyapp.user.v1.RequestMeta - 1, // 49: hyapp.user.v1.ListCountriesRequest.meta:type_name -> hyapp.user.v1.RequestMeta - 62, // 50: hyapp.user.v1.ListCountriesResponse.countries:type_name -> hyapp.user.v1.Country - 1, // 51: hyapp.user.v1.UpdateCountryRequest.meta:type_name -> hyapp.user.v1.RequestMeta - 62, // 52: hyapp.user.v1.CountryResponse.country:type_name -> hyapp.user.v1.Country - 1, // 53: hyapp.user.v1.ListRegistrationCountriesRequest.meta:type_name -> hyapp.user.v1.RequestMeta - 62, // 54: hyapp.user.v1.ListRegistrationCountriesResponse.countries:type_name -> hyapp.user.v1.Country - 1, // 55: hyapp.user.v1.ListLoginRiskBlockedCountriesRequest.meta:type_name -> hyapp.user.v1.RequestMeta - 70, // 56: hyapp.user.v1.ListLoginRiskBlockedCountriesResponse.countries:type_name -> hyapp.user.v1.LoginRiskBlockedCountry - 1, // 57: hyapp.user.v1.ListRegionsRequest.meta:type_name -> hyapp.user.v1.RequestMeta - 63, // 58: hyapp.user.v1.ListRegionsResponse.regions:type_name -> hyapp.user.v1.Region - 1, // 59: hyapp.user.v1.GetRegionRequest.meta:type_name -> hyapp.user.v1.RequestMeta - 1, // 60: hyapp.user.v1.UpdateRegionRequest.meta:type_name -> hyapp.user.v1.RequestMeta - 1, // 61: hyapp.user.v1.ReplaceRegionCountriesRequest.meta:type_name -> hyapp.user.v1.RequestMeta - 63, // 62: hyapp.user.v1.RegionResponse.region:type_name -> hyapp.user.v1.Region - 1, // 63: hyapp.user.v1.GetUserIdentityRequest.meta:type_name -> hyapp.user.v1.RequestMeta - 79, // 64: hyapp.user.v1.GetUserIdentityResponse.identity:type_name -> hyapp.user.v1.UserIdentity - 1, // 65: hyapp.user.v1.ResolveDisplayUserIDRequest.meta:type_name -> hyapp.user.v1.RequestMeta - 79, // 66: hyapp.user.v1.ResolveDisplayUserIDResponse.identity:type_name -> hyapp.user.v1.UserIdentity - 1, // 67: hyapp.user.v1.ChangeDisplayUserIDRequest.meta:type_name -> hyapp.user.v1.RequestMeta - 79, // 68: hyapp.user.v1.ChangeDisplayUserIDResponse.identity:type_name -> hyapp.user.v1.UserIdentity - 1, // 69: hyapp.user.v1.ApplyPrettyDisplayUserIDRequest.meta:type_name -> hyapp.user.v1.RequestMeta - 79, // 70: hyapp.user.v1.ApplyPrettyDisplayUserIDResponse.identity:type_name -> hyapp.user.v1.UserIdentity - 1, // 71: hyapp.user.v1.ExpirePrettyDisplayUserIDRequest.meta:type_name -> hyapp.user.v1.RequestMeta - 79, // 72: hyapp.user.v1.ExpirePrettyDisplayUserIDResponse.identity:type_name -> hyapp.user.v1.UserIdentity - 5, // 73: hyapp.user.v1.BatchGetUsersResponse.UsersEntry.value:type_name -> hyapp.user.v1.User - 14, // 74: hyapp.user.v1.UserService.GetUser:input_type -> hyapp.user.v1.GetUserRequest - 16, // 75: hyapp.user.v1.UserService.BusinessUserLookup:input_type -> hyapp.user.v1.BusinessUserLookupRequest - 20, // 76: hyapp.user.v1.UserService.GetMyProfileStats:input_type -> hyapp.user.v1.GetMyProfileStatsRequest - 46, // 77: hyapp.user.v1.UserService.BatchGetUsers:input_type -> hyapp.user.v1.BatchGetUsersRequest - 48, // 78: hyapp.user.v1.UserService.ListUserIDs:input_type -> hyapp.user.v1.ListUserIDsRequest - 9, // 79: hyapp.user.v1.UserService.GetUserMicLifetimeStats:input_type -> hyapp.user.v1.GetUserMicLifetimeStatsRequest - 50, // 80: hyapp.user.v1.UserService.UpdateUserProfile:input_type -> hyapp.user.v1.UpdateUserProfileRequest - 52, // 81: hyapp.user.v1.UserService.ChangeUserCountry:input_type -> hyapp.user.v1.ChangeUserCountryRequest - 54, // 82: hyapp.user.v1.UserService.SetUserStatus:input_type -> hyapp.user.v1.SetUserStatusRequest - 56, // 83: hyapp.user.v1.UserService.CompleteOnboarding:input_type -> hyapp.user.v1.CompleteOnboardingRequest - 22, // 84: hyapp.user.v1.UserSocialService.RecordProfileVisit:input_type -> hyapp.user.v1.RecordProfileVisitRequest - 25, // 85: hyapp.user.v1.UserSocialService.ListProfileVisitors:input_type -> hyapp.user.v1.ListProfileVisitorsRequest - 27, // 86: hyapp.user.v1.UserSocialService.FollowUser:input_type -> hyapp.user.v1.FollowUserRequest - 29, // 87: hyapp.user.v1.UserSocialService.UnfollowUser:input_type -> hyapp.user.v1.UnfollowUserRequest - 32, // 88: hyapp.user.v1.UserSocialService.ListFollowing:input_type -> hyapp.user.v1.ListFollowingRequest - 34, // 89: hyapp.user.v1.UserSocialService.ApplyFriend:input_type -> hyapp.user.v1.ApplyFriendRequest - 36, // 90: hyapp.user.v1.UserSocialService.AcceptFriendApplication:input_type -> hyapp.user.v1.AcceptFriendApplicationRequest - 38, // 91: hyapp.user.v1.UserSocialService.DeleteFriend:input_type -> hyapp.user.v1.DeleteFriendRequest - 41, // 92: hyapp.user.v1.UserSocialService.ListFriends:input_type -> hyapp.user.v1.ListFriendsRequest - 44, // 93: hyapp.user.v1.UserSocialService.ListFriendApplications:input_type -> hyapp.user.v1.ListFriendApplicationsRequest - 11, // 94: hyapp.user.v1.UserCronService.ProcessLoginIPRiskBatch:input_type -> hyapp.user.v1.CronBatchRequest - 11, // 95: hyapp.user.v1.UserCronService.ProcessRegionRebuildBatch:input_type -> hyapp.user.v1.CronBatchRequest - 11, // 96: hyapp.user.v1.UserCronService.CompensateMicOpenSessions:input_type -> hyapp.user.v1.CronBatchRequest - 58, // 97: hyapp.user.v1.UserDeviceService.BindPushToken:input_type -> hyapp.user.v1.BindPushTokenRequest - 60, // 98: hyapp.user.v1.UserDeviceService.DeletePushToken:input_type -> hyapp.user.v1.DeletePushTokenRequest - 3, // 99: hyapp.user.v1.AppRegistryService.ResolveApp:input_type -> hyapp.user.v1.ResolveAppRequest - 64, // 100: hyapp.user.v1.CountryAdminService.ListCountries:input_type -> hyapp.user.v1.ListCountriesRequest - 66, // 101: hyapp.user.v1.CountryAdminService.UpdateCountry:input_type -> hyapp.user.v1.UpdateCountryRequest - 68, // 102: hyapp.user.v1.CountryQueryService.ListRegistrationCountries:input_type -> hyapp.user.v1.ListRegistrationCountriesRequest - 71, // 103: hyapp.user.v1.CountryQueryService.ListLoginRiskBlockedCountries:input_type -> hyapp.user.v1.ListLoginRiskBlockedCountriesRequest - 73, // 104: hyapp.user.v1.RegionAdminService.ListRegions:input_type -> hyapp.user.v1.ListRegionsRequest - 75, // 105: hyapp.user.v1.RegionAdminService.GetRegion:input_type -> hyapp.user.v1.GetRegionRequest - 76, // 106: hyapp.user.v1.RegionAdminService.UpdateRegion:input_type -> hyapp.user.v1.UpdateRegionRequest - 77, // 107: hyapp.user.v1.RegionAdminService.ReplaceRegionCountries:input_type -> hyapp.user.v1.ReplaceRegionCountriesRequest - 80, // 108: hyapp.user.v1.UserIdentityService.GetUserIdentity:input_type -> hyapp.user.v1.GetUserIdentityRequest - 82, // 109: hyapp.user.v1.UserIdentityService.ResolveDisplayUserID:input_type -> hyapp.user.v1.ResolveDisplayUserIDRequest - 84, // 110: hyapp.user.v1.UserIdentityService.ChangeDisplayUserID:input_type -> hyapp.user.v1.ChangeDisplayUserIDRequest - 86, // 111: hyapp.user.v1.UserIdentityService.ApplyPrettyDisplayUserID:input_type -> hyapp.user.v1.ApplyPrettyDisplayUserIDRequest - 88, // 112: hyapp.user.v1.UserIdentityService.ExpirePrettyDisplayUserID:input_type -> hyapp.user.v1.ExpirePrettyDisplayUserIDRequest - 15, // 113: hyapp.user.v1.UserService.GetUser:output_type -> hyapp.user.v1.GetUserResponse - 18, // 114: hyapp.user.v1.UserService.BusinessUserLookup:output_type -> hyapp.user.v1.BusinessUserLookupResponse - 21, // 115: hyapp.user.v1.UserService.GetMyProfileStats:output_type -> hyapp.user.v1.GetMyProfileStatsResponse - 47, // 116: hyapp.user.v1.UserService.BatchGetUsers:output_type -> hyapp.user.v1.BatchGetUsersResponse - 49, // 117: hyapp.user.v1.UserService.ListUserIDs:output_type -> hyapp.user.v1.ListUserIDsResponse - 10, // 118: hyapp.user.v1.UserService.GetUserMicLifetimeStats:output_type -> hyapp.user.v1.GetUserMicLifetimeStatsResponse - 51, // 119: hyapp.user.v1.UserService.UpdateUserProfile:output_type -> hyapp.user.v1.UpdateUserProfileResponse - 53, // 120: hyapp.user.v1.UserService.ChangeUserCountry:output_type -> hyapp.user.v1.ChangeUserCountryResponse - 55, // 121: hyapp.user.v1.UserService.SetUserStatus:output_type -> hyapp.user.v1.SetUserStatusResponse - 57, // 122: hyapp.user.v1.UserService.CompleteOnboarding:output_type -> hyapp.user.v1.CompleteOnboardingResponse - 23, // 123: hyapp.user.v1.UserSocialService.RecordProfileVisit:output_type -> hyapp.user.v1.RecordProfileVisitResponse - 26, // 124: hyapp.user.v1.UserSocialService.ListProfileVisitors:output_type -> hyapp.user.v1.ListProfileVisitorsResponse - 28, // 125: hyapp.user.v1.UserSocialService.FollowUser:output_type -> hyapp.user.v1.FollowUserResponse - 30, // 126: hyapp.user.v1.UserSocialService.UnfollowUser:output_type -> hyapp.user.v1.UnfollowUserResponse - 33, // 127: hyapp.user.v1.UserSocialService.ListFollowing:output_type -> hyapp.user.v1.ListFollowingResponse - 35, // 128: hyapp.user.v1.UserSocialService.ApplyFriend:output_type -> hyapp.user.v1.ApplyFriendResponse - 37, // 129: hyapp.user.v1.UserSocialService.AcceptFriendApplication:output_type -> hyapp.user.v1.AcceptFriendApplicationResponse - 39, // 130: hyapp.user.v1.UserSocialService.DeleteFriend:output_type -> hyapp.user.v1.DeleteFriendResponse - 42, // 131: hyapp.user.v1.UserSocialService.ListFriends:output_type -> hyapp.user.v1.ListFriendsResponse - 45, // 132: hyapp.user.v1.UserSocialService.ListFriendApplications:output_type -> hyapp.user.v1.ListFriendApplicationsResponse - 12, // 133: hyapp.user.v1.UserCronService.ProcessLoginIPRiskBatch:output_type -> hyapp.user.v1.CronBatchResponse - 12, // 134: hyapp.user.v1.UserCronService.ProcessRegionRebuildBatch:output_type -> hyapp.user.v1.CronBatchResponse - 12, // 135: hyapp.user.v1.UserCronService.CompensateMicOpenSessions:output_type -> hyapp.user.v1.CronBatchResponse - 59, // 136: hyapp.user.v1.UserDeviceService.BindPushToken:output_type -> hyapp.user.v1.BindPushTokenResponse - 61, // 137: hyapp.user.v1.UserDeviceService.DeletePushToken:output_type -> hyapp.user.v1.DeletePushTokenResponse - 4, // 138: hyapp.user.v1.AppRegistryService.ResolveApp:output_type -> hyapp.user.v1.ResolveAppResponse - 65, // 139: hyapp.user.v1.CountryAdminService.ListCountries:output_type -> hyapp.user.v1.ListCountriesResponse - 67, // 140: hyapp.user.v1.CountryAdminService.UpdateCountry:output_type -> hyapp.user.v1.CountryResponse - 69, // 141: hyapp.user.v1.CountryQueryService.ListRegistrationCountries:output_type -> hyapp.user.v1.ListRegistrationCountriesResponse - 72, // 142: hyapp.user.v1.CountryQueryService.ListLoginRiskBlockedCountries:output_type -> hyapp.user.v1.ListLoginRiskBlockedCountriesResponse - 74, // 143: hyapp.user.v1.RegionAdminService.ListRegions:output_type -> hyapp.user.v1.ListRegionsResponse - 78, // 144: hyapp.user.v1.RegionAdminService.GetRegion:output_type -> hyapp.user.v1.RegionResponse - 78, // 145: hyapp.user.v1.RegionAdminService.UpdateRegion:output_type -> hyapp.user.v1.RegionResponse - 78, // 146: hyapp.user.v1.RegionAdminService.ReplaceRegionCountries:output_type -> hyapp.user.v1.RegionResponse - 81, // 147: hyapp.user.v1.UserIdentityService.GetUserIdentity:output_type -> hyapp.user.v1.GetUserIdentityResponse - 83, // 148: hyapp.user.v1.UserIdentityService.ResolveDisplayUserID:output_type -> hyapp.user.v1.ResolveDisplayUserIDResponse - 85, // 149: hyapp.user.v1.UserIdentityService.ChangeDisplayUserID:output_type -> hyapp.user.v1.ChangeDisplayUserIDResponse - 87, // 150: hyapp.user.v1.UserIdentityService.ApplyPrettyDisplayUserID:output_type -> hyapp.user.v1.ApplyPrettyDisplayUserIDResponse - 89, // 151: hyapp.user.v1.UserIdentityService.ExpirePrettyDisplayUserID:output_type -> hyapp.user.v1.ExpirePrettyDisplayUserIDResponse - 113, // [113:152] is the sub-list for method output_type - 74, // [74:113] is the sub-list for method input_type - 74, // [74:74] is the sub-list for extension type_name - 74, // [74:74] is the sub-list for extension extendee - 0, // [0:74] is the sub-list for field type_name + 1, // 33: hyapp.user.v1.SubmitReportRequest.meta:type_name -> hyapp.user.v1.RequestMeta + 46, // 34: hyapp.user.v1.SubmitReportResponse.report:type_name -> hyapp.user.v1.UserReport + 1, // 35: hyapp.user.v1.BatchGetUsersRequest.meta:type_name -> hyapp.user.v1.RequestMeta + 93, // 36: hyapp.user.v1.BatchGetUsersResponse.users:type_name -> hyapp.user.v1.BatchGetUsersResponse.UsersEntry + 1, // 37: hyapp.user.v1.ListUserIDsRequest.meta:type_name -> hyapp.user.v1.RequestMeta + 1, // 38: hyapp.user.v1.UpdateUserProfileRequest.meta:type_name -> hyapp.user.v1.RequestMeta + 5, // 39: hyapp.user.v1.UpdateUserProfileResponse.user:type_name -> hyapp.user.v1.User + 1, // 40: hyapp.user.v1.ChangeUserCountryRequest.meta:type_name -> hyapp.user.v1.RequestMeta + 5, // 41: hyapp.user.v1.ChangeUserCountryResponse.user:type_name -> hyapp.user.v1.User + 1, // 42: hyapp.user.v1.SetUserStatusRequest.meta:type_name -> hyapp.user.v1.RequestMeta + 0, // 43: hyapp.user.v1.SetUserStatusRequest.status:type_name -> hyapp.user.v1.UserStatus + 5, // 44: hyapp.user.v1.SetUserStatusResponse.user:type_name -> hyapp.user.v1.User + 1, // 45: hyapp.user.v1.CompleteOnboardingRequest.meta:type_name -> hyapp.user.v1.RequestMeta + 5, // 46: hyapp.user.v1.CompleteOnboardingResponse.user:type_name -> hyapp.user.v1.User + 13, // 47: hyapp.user.v1.CompleteOnboardingResponse.token:type_name -> hyapp.user.v1.AuthToken + 7, // 48: hyapp.user.v1.CompleteOnboardingResponse.invite:type_name -> hyapp.user.v1.InviteBinding + 1, // 49: hyapp.user.v1.BindPushTokenRequest.meta:type_name -> hyapp.user.v1.RequestMeta + 1, // 50: hyapp.user.v1.DeletePushTokenRequest.meta:type_name -> hyapp.user.v1.RequestMeta + 1, // 51: hyapp.user.v1.ListCountriesRequest.meta:type_name -> hyapp.user.v1.RequestMeta + 65, // 52: hyapp.user.v1.ListCountriesResponse.countries:type_name -> hyapp.user.v1.Country + 1, // 53: hyapp.user.v1.UpdateCountryRequest.meta:type_name -> hyapp.user.v1.RequestMeta + 65, // 54: hyapp.user.v1.CountryResponse.country:type_name -> hyapp.user.v1.Country + 1, // 55: hyapp.user.v1.ListRegistrationCountriesRequest.meta:type_name -> hyapp.user.v1.RequestMeta + 65, // 56: hyapp.user.v1.ListRegistrationCountriesResponse.countries:type_name -> hyapp.user.v1.Country + 1, // 57: hyapp.user.v1.ListLoginRiskBlockedCountriesRequest.meta:type_name -> hyapp.user.v1.RequestMeta + 73, // 58: hyapp.user.v1.ListLoginRiskBlockedCountriesResponse.countries:type_name -> hyapp.user.v1.LoginRiskBlockedCountry + 1, // 59: hyapp.user.v1.ListRegionsRequest.meta:type_name -> hyapp.user.v1.RequestMeta + 66, // 60: hyapp.user.v1.ListRegionsResponse.regions:type_name -> hyapp.user.v1.Region + 1, // 61: hyapp.user.v1.GetRegionRequest.meta:type_name -> hyapp.user.v1.RequestMeta + 1, // 62: hyapp.user.v1.UpdateRegionRequest.meta:type_name -> hyapp.user.v1.RequestMeta + 1, // 63: hyapp.user.v1.ReplaceRegionCountriesRequest.meta:type_name -> hyapp.user.v1.RequestMeta + 66, // 64: hyapp.user.v1.RegionResponse.region:type_name -> hyapp.user.v1.Region + 1, // 65: hyapp.user.v1.GetUserIdentityRequest.meta:type_name -> hyapp.user.v1.RequestMeta + 82, // 66: hyapp.user.v1.GetUserIdentityResponse.identity:type_name -> hyapp.user.v1.UserIdentity + 1, // 67: hyapp.user.v1.ResolveDisplayUserIDRequest.meta:type_name -> hyapp.user.v1.RequestMeta + 82, // 68: hyapp.user.v1.ResolveDisplayUserIDResponse.identity:type_name -> hyapp.user.v1.UserIdentity + 1, // 69: hyapp.user.v1.ChangeDisplayUserIDRequest.meta:type_name -> hyapp.user.v1.RequestMeta + 82, // 70: hyapp.user.v1.ChangeDisplayUserIDResponse.identity:type_name -> hyapp.user.v1.UserIdentity + 1, // 71: hyapp.user.v1.ApplyPrettyDisplayUserIDRequest.meta:type_name -> hyapp.user.v1.RequestMeta + 82, // 72: hyapp.user.v1.ApplyPrettyDisplayUserIDResponse.identity:type_name -> hyapp.user.v1.UserIdentity + 1, // 73: hyapp.user.v1.ExpirePrettyDisplayUserIDRequest.meta:type_name -> hyapp.user.v1.RequestMeta + 82, // 74: hyapp.user.v1.ExpirePrettyDisplayUserIDResponse.identity:type_name -> hyapp.user.v1.UserIdentity + 5, // 75: hyapp.user.v1.BatchGetUsersResponse.UsersEntry.value:type_name -> hyapp.user.v1.User + 14, // 76: hyapp.user.v1.UserService.GetUser:input_type -> hyapp.user.v1.GetUserRequest + 16, // 77: hyapp.user.v1.UserService.BusinessUserLookup:input_type -> hyapp.user.v1.BusinessUserLookupRequest + 20, // 78: hyapp.user.v1.UserService.GetMyProfileStats:input_type -> hyapp.user.v1.GetMyProfileStatsRequest + 49, // 79: hyapp.user.v1.UserService.BatchGetUsers:input_type -> hyapp.user.v1.BatchGetUsersRequest + 51, // 80: hyapp.user.v1.UserService.ListUserIDs:input_type -> hyapp.user.v1.ListUserIDsRequest + 9, // 81: hyapp.user.v1.UserService.GetUserMicLifetimeStats:input_type -> hyapp.user.v1.GetUserMicLifetimeStatsRequest + 53, // 82: hyapp.user.v1.UserService.UpdateUserProfile:input_type -> hyapp.user.v1.UpdateUserProfileRequest + 55, // 83: hyapp.user.v1.UserService.ChangeUserCountry:input_type -> hyapp.user.v1.ChangeUserCountryRequest + 57, // 84: hyapp.user.v1.UserService.SetUserStatus:input_type -> hyapp.user.v1.SetUserStatusRequest + 59, // 85: hyapp.user.v1.UserService.CompleteOnboarding:input_type -> hyapp.user.v1.CompleteOnboardingRequest + 22, // 86: hyapp.user.v1.UserSocialService.RecordProfileVisit:input_type -> hyapp.user.v1.RecordProfileVisitRequest + 25, // 87: hyapp.user.v1.UserSocialService.ListProfileVisitors:input_type -> hyapp.user.v1.ListProfileVisitorsRequest + 27, // 88: hyapp.user.v1.UserSocialService.FollowUser:input_type -> hyapp.user.v1.FollowUserRequest + 29, // 89: hyapp.user.v1.UserSocialService.UnfollowUser:input_type -> hyapp.user.v1.UnfollowUserRequest + 32, // 90: hyapp.user.v1.UserSocialService.ListFollowing:input_type -> hyapp.user.v1.ListFollowingRequest + 34, // 91: hyapp.user.v1.UserSocialService.ApplyFriend:input_type -> hyapp.user.v1.ApplyFriendRequest + 36, // 92: hyapp.user.v1.UserSocialService.AcceptFriendApplication:input_type -> hyapp.user.v1.AcceptFriendApplicationRequest + 38, // 93: hyapp.user.v1.UserSocialService.DeleteFriend:input_type -> hyapp.user.v1.DeleteFriendRequest + 41, // 94: hyapp.user.v1.UserSocialService.ListFriends:input_type -> hyapp.user.v1.ListFriendsRequest + 44, // 95: hyapp.user.v1.UserSocialService.ListFriendApplications:input_type -> hyapp.user.v1.ListFriendApplicationsRequest + 47, // 96: hyapp.user.v1.UserSocialService.SubmitReport:input_type -> hyapp.user.v1.SubmitReportRequest + 11, // 97: hyapp.user.v1.UserCronService.ProcessLoginIPRiskBatch:input_type -> hyapp.user.v1.CronBatchRequest + 11, // 98: hyapp.user.v1.UserCronService.ProcessRegionRebuildBatch:input_type -> hyapp.user.v1.CronBatchRequest + 11, // 99: hyapp.user.v1.UserCronService.CompensateMicOpenSessions:input_type -> hyapp.user.v1.CronBatchRequest + 61, // 100: hyapp.user.v1.UserDeviceService.BindPushToken:input_type -> hyapp.user.v1.BindPushTokenRequest + 63, // 101: hyapp.user.v1.UserDeviceService.DeletePushToken:input_type -> hyapp.user.v1.DeletePushTokenRequest + 3, // 102: hyapp.user.v1.AppRegistryService.ResolveApp:input_type -> hyapp.user.v1.ResolveAppRequest + 67, // 103: hyapp.user.v1.CountryAdminService.ListCountries:input_type -> hyapp.user.v1.ListCountriesRequest + 69, // 104: hyapp.user.v1.CountryAdminService.UpdateCountry:input_type -> hyapp.user.v1.UpdateCountryRequest + 71, // 105: hyapp.user.v1.CountryQueryService.ListRegistrationCountries:input_type -> hyapp.user.v1.ListRegistrationCountriesRequest + 74, // 106: hyapp.user.v1.CountryQueryService.ListLoginRiskBlockedCountries:input_type -> hyapp.user.v1.ListLoginRiskBlockedCountriesRequest + 76, // 107: hyapp.user.v1.RegionAdminService.ListRegions:input_type -> hyapp.user.v1.ListRegionsRequest + 78, // 108: hyapp.user.v1.RegionAdminService.GetRegion:input_type -> hyapp.user.v1.GetRegionRequest + 79, // 109: hyapp.user.v1.RegionAdminService.UpdateRegion:input_type -> hyapp.user.v1.UpdateRegionRequest + 80, // 110: hyapp.user.v1.RegionAdminService.ReplaceRegionCountries:input_type -> hyapp.user.v1.ReplaceRegionCountriesRequest + 83, // 111: hyapp.user.v1.UserIdentityService.GetUserIdentity:input_type -> hyapp.user.v1.GetUserIdentityRequest + 85, // 112: hyapp.user.v1.UserIdentityService.ResolveDisplayUserID:input_type -> hyapp.user.v1.ResolveDisplayUserIDRequest + 87, // 113: hyapp.user.v1.UserIdentityService.ChangeDisplayUserID:input_type -> hyapp.user.v1.ChangeDisplayUserIDRequest + 89, // 114: hyapp.user.v1.UserIdentityService.ApplyPrettyDisplayUserID:input_type -> hyapp.user.v1.ApplyPrettyDisplayUserIDRequest + 91, // 115: hyapp.user.v1.UserIdentityService.ExpirePrettyDisplayUserID:input_type -> hyapp.user.v1.ExpirePrettyDisplayUserIDRequest + 15, // 116: hyapp.user.v1.UserService.GetUser:output_type -> hyapp.user.v1.GetUserResponse + 18, // 117: hyapp.user.v1.UserService.BusinessUserLookup:output_type -> hyapp.user.v1.BusinessUserLookupResponse + 21, // 118: hyapp.user.v1.UserService.GetMyProfileStats:output_type -> hyapp.user.v1.GetMyProfileStatsResponse + 50, // 119: hyapp.user.v1.UserService.BatchGetUsers:output_type -> hyapp.user.v1.BatchGetUsersResponse + 52, // 120: hyapp.user.v1.UserService.ListUserIDs:output_type -> hyapp.user.v1.ListUserIDsResponse + 10, // 121: hyapp.user.v1.UserService.GetUserMicLifetimeStats:output_type -> hyapp.user.v1.GetUserMicLifetimeStatsResponse + 54, // 122: hyapp.user.v1.UserService.UpdateUserProfile:output_type -> hyapp.user.v1.UpdateUserProfileResponse + 56, // 123: hyapp.user.v1.UserService.ChangeUserCountry:output_type -> hyapp.user.v1.ChangeUserCountryResponse + 58, // 124: hyapp.user.v1.UserService.SetUserStatus:output_type -> hyapp.user.v1.SetUserStatusResponse + 60, // 125: hyapp.user.v1.UserService.CompleteOnboarding:output_type -> hyapp.user.v1.CompleteOnboardingResponse + 23, // 126: hyapp.user.v1.UserSocialService.RecordProfileVisit:output_type -> hyapp.user.v1.RecordProfileVisitResponse + 26, // 127: hyapp.user.v1.UserSocialService.ListProfileVisitors:output_type -> hyapp.user.v1.ListProfileVisitorsResponse + 28, // 128: hyapp.user.v1.UserSocialService.FollowUser:output_type -> hyapp.user.v1.FollowUserResponse + 30, // 129: hyapp.user.v1.UserSocialService.UnfollowUser:output_type -> hyapp.user.v1.UnfollowUserResponse + 33, // 130: hyapp.user.v1.UserSocialService.ListFollowing:output_type -> hyapp.user.v1.ListFollowingResponse + 35, // 131: hyapp.user.v1.UserSocialService.ApplyFriend:output_type -> hyapp.user.v1.ApplyFriendResponse + 37, // 132: hyapp.user.v1.UserSocialService.AcceptFriendApplication:output_type -> hyapp.user.v1.AcceptFriendApplicationResponse + 39, // 133: hyapp.user.v1.UserSocialService.DeleteFriend:output_type -> hyapp.user.v1.DeleteFriendResponse + 42, // 134: hyapp.user.v1.UserSocialService.ListFriends:output_type -> hyapp.user.v1.ListFriendsResponse + 45, // 135: hyapp.user.v1.UserSocialService.ListFriendApplications:output_type -> hyapp.user.v1.ListFriendApplicationsResponse + 48, // 136: hyapp.user.v1.UserSocialService.SubmitReport:output_type -> hyapp.user.v1.SubmitReportResponse + 12, // 137: hyapp.user.v1.UserCronService.ProcessLoginIPRiskBatch:output_type -> hyapp.user.v1.CronBatchResponse + 12, // 138: hyapp.user.v1.UserCronService.ProcessRegionRebuildBatch:output_type -> hyapp.user.v1.CronBatchResponse + 12, // 139: hyapp.user.v1.UserCronService.CompensateMicOpenSessions:output_type -> hyapp.user.v1.CronBatchResponse + 62, // 140: hyapp.user.v1.UserDeviceService.BindPushToken:output_type -> hyapp.user.v1.BindPushTokenResponse + 64, // 141: hyapp.user.v1.UserDeviceService.DeletePushToken:output_type -> hyapp.user.v1.DeletePushTokenResponse + 4, // 142: hyapp.user.v1.AppRegistryService.ResolveApp:output_type -> hyapp.user.v1.ResolveAppResponse + 68, // 143: hyapp.user.v1.CountryAdminService.ListCountries:output_type -> hyapp.user.v1.ListCountriesResponse + 70, // 144: hyapp.user.v1.CountryAdminService.UpdateCountry:output_type -> hyapp.user.v1.CountryResponse + 72, // 145: hyapp.user.v1.CountryQueryService.ListRegistrationCountries:output_type -> hyapp.user.v1.ListRegistrationCountriesResponse + 75, // 146: hyapp.user.v1.CountryQueryService.ListLoginRiskBlockedCountries:output_type -> hyapp.user.v1.ListLoginRiskBlockedCountriesResponse + 77, // 147: hyapp.user.v1.RegionAdminService.ListRegions:output_type -> hyapp.user.v1.ListRegionsResponse + 81, // 148: hyapp.user.v1.RegionAdminService.GetRegion:output_type -> hyapp.user.v1.RegionResponse + 81, // 149: hyapp.user.v1.RegionAdminService.UpdateRegion:output_type -> hyapp.user.v1.RegionResponse + 81, // 150: hyapp.user.v1.RegionAdminService.ReplaceRegionCountries:output_type -> hyapp.user.v1.RegionResponse + 84, // 151: hyapp.user.v1.UserIdentityService.GetUserIdentity:output_type -> hyapp.user.v1.GetUserIdentityResponse + 86, // 152: hyapp.user.v1.UserIdentityService.ResolveDisplayUserID:output_type -> hyapp.user.v1.ResolveDisplayUserIDResponse + 88, // 153: hyapp.user.v1.UserIdentityService.ChangeDisplayUserID:output_type -> hyapp.user.v1.ChangeDisplayUserIDResponse + 90, // 154: hyapp.user.v1.UserIdentityService.ApplyPrettyDisplayUserID:output_type -> hyapp.user.v1.ApplyPrettyDisplayUserIDResponse + 92, // 155: hyapp.user.v1.UserIdentityService.ExpirePrettyDisplayUserID:output_type -> hyapp.user.v1.ExpirePrettyDisplayUserIDResponse + 116, // [116:156] is the sub-list for method output_type + 76, // [76:116] is the sub-list for method input_type + 76, // [76:76] is the sub-list for extension type_name + 76, // [76:76] is the sub-list for extension extendee + 0, // [0:76] is the sub-list for field type_name } func init() { file_proto_user_v1_user_proto_init() } @@ -7040,15 +7330,15 @@ func file_proto_user_v1_user_proto_init() { if File_proto_user_v1_user_proto != nil { return } - file_proto_user_v1_user_proto_msgTypes[49].OneofWrappers = []any{} - file_proto_user_v1_user_proto_msgTypes[63].OneofWrappers = []any{} + file_proto_user_v1_user_proto_msgTypes[52].OneofWrappers = []any{} + file_proto_user_v1_user_proto_msgTypes[66].OneofWrappers = []any{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_proto_user_v1_user_proto_rawDesc), len(file_proto_user_v1_user_proto_rawDesc)), NumEnums: 1, - NumMessages: 90, + NumMessages: 93, NumExtensions: 0, NumServices: 9, }, diff --git a/api/proto/user/v1/user.proto b/api/proto/user/v1/user.proto index dc6c6df9..baa58b18 100644 --- a/api/proto/user/v1/user.proto +++ b/api/proto/user/v1/user.proto @@ -360,6 +360,35 @@ message ListFriendApplicationsResponse { int64 total = 2; } +// UserReport 是 App 用户提交的治理举报单。 +message UserReport { + string report_id = 1; + int64 reporter_user_id = 2; + string target_type = 3; + int64 user_id = 4; + string room_id = 5; + string report_type = 6; + string reason = 7; + repeated string image_urls = 8; + string status = 9; + int64 created_at_ms = 10; +} + +// SubmitReportRequest 提交用户或房间举报;user_id 和 room_id 必须二选一。 +message SubmitReportRequest { + RequestMeta meta = 1; + int64 reporter_user_id = 2; + int64 user_id = 3; + string room_id = 4; + string report_type = 5; + string reason = 6; + repeated string image_urls = 7; +} + +message SubmitReportResponse { + UserReport report = 1; +} + // BatchGetUsersRequest 批量查询用户主状态。 message BatchGetUsersRequest { RequestMeta meta = 1; @@ -716,6 +745,7 @@ service UserSocialService { rpc DeleteFriend(DeleteFriendRequest) returns (DeleteFriendResponse); rpc ListFriends(ListFriendsRequest) returns (ListFriendsResponse); rpc ListFriendApplications(ListFriendApplicationsRequest) returns (ListFriendApplicationsResponse); + rpc SubmitReport(SubmitReportRequest) returns (SubmitReportResponse); } // UserCronService 只给 cron-service 调用,业务状态仍由 user-service owner 修改。 diff --git a/api/proto/user/v1/user_grpc.pb.go b/api/proto/user/v1/user_grpc.pb.go index 2d4602d4..c4fc2f91 100644 --- a/api/proto/user/v1/user_grpc.pb.go +++ b/api/proto/user/v1/user_grpc.pb.go @@ -477,6 +477,7 @@ const ( UserSocialService_DeleteFriend_FullMethodName = "/hyapp.user.v1.UserSocialService/DeleteFriend" UserSocialService_ListFriends_FullMethodName = "/hyapp.user.v1.UserSocialService/ListFriends" UserSocialService_ListFriendApplications_FullMethodName = "/hyapp.user.v1.UserSocialService/ListFriendApplications" + UserSocialService_SubmitReport_FullMethodName = "/hyapp.user.v1.UserSocialService/SubmitReport" ) // UserSocialServiceClient is the client API for UserSocialService service. @@ -495,6 +496,7 @@ type UserSocialServiceClient interface { DeleteFriend(ctx context.Context, in *DeleteFriendRequest, opts ...grpc.CallOption) (*DeleteFriendResponse, error) ListFriends(ctx context.Context, in *ListFriendsRequest, opts ...grpc.CallOption) (*ListFriendsResponse, error) ListFriendApplications(ctx context.Context, in *ListFriendApplicationsRequest, opts ...grpc.CallOption) (*ListFriendApplicationsResponse, error) + SubmitReport(ctx context.Context, in *SubmitReportRequest, opts ...grpc.CallOption) (*SubmitReportResponse, error) } type userSocialServiceClient struct { @@ -605,6 +607,16 @@ func (c *userSocialServiceClient) ListFriendApplications(ctx context.Context, in return out, nil } +func (c *userSocialServiceClient) SubmitReport(ctx context.Context, in *SubmitReportRequest, opts ...grpc.CallOption) (*SubmitReportResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(SubmitReportResponse) + err := c.cc.Invoke(ctx, UserSocialService_SubmitReport_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + // UserSocialServiceServer is the server API for UserSocialService service. // All implementations must embed UnimplementedUserSocialServiceServer // for forward compatibility. @@ -621,6 +633,7 @@ type UserSocialServiceServer interface { DeleteFriend(context.Context, *DeleteFriendRequest) (*DeleteFriendResponse, error) ListFriends(context.Context, *ListFriendsRequest) (*ListFriendsResponse, error) ListFriendApplications(context.Context, *ListFriendApplicationsRequest) (*ListFriendApplicationsResponse, error) + SubmitReport(context.Context, *SubmitReportRequest) (*SubmitReportResponse, error) mustEmbedUnimplementedUserSocialServiceServer() } @@ -661,6 +674,9 @@ func (UnimplementedUserSocialServiceServer) ListFriends(context.Context, *ListFr func (UnimplementedUserSocialServiceServer) ListFriendApplications(context.Context, *ListFriendApplicationsRequest) (*ListFriendApplicationsResponse, error) { return nil, status.Error(codes.Unimplemented, "method ListFriendApplications not implemented") } +func (UnimplementedUserSocialServiceServer) SubmitReport(context.Context, *SubmitReportRequest) (*SubmitReportResponse, error) { + return nil, status.Error(codes.Unimplemented, "method SubmitReport not implemented") +} func (UnimplementedUserSocialServiceServer) mustEmbedUnimplementedUserSocialServiceServer() {} func (UnimplementedUserSocialServiceServer) testEmbeddedByValue() {} @@ -862,6 +878,24 @@ func _UserSocialService_ListFriendApplications_Handler(srv interface{}, ctx cont return interceptor(ctx, in, info, handler) } +func _UserSocialService_SubmitReport_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SubmitReportRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(UserSocialServiceServer).SubmitReport(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: UserSocialService_SubmitReport_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(UserSocialServiceServer).SubmitReport(ctx, req.(*SubmitReportRequest)) + } + return interceptor(ctx, in, info, handler) +} + // UserSocialService_ServiceDesc is the grpc.ServiceDesc for UserSocialService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -909,6 +943,10 @@ var UserSocialService_ServiceDesc = grpc.ServiceDesc{ MethodName: "ListFriendApplications", Handler: _UserSocialService_ListFriendApplications_Handler, }, + { + MethodName: "SubmitReport", + Handler: _UserSocialService_SubmitReport_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "proto/user/v1/user.proto", diff --git a/docs/flutter对接/用户举报Flutter对接.md b/docs/flutter对接/用户举报Flutter对接.md new file mode 100644 index 00000000..20b821a8 --- /dev/null +++ b/docs/flutter对接/用户举报Flutter对接.md @@ -0,0 +1,272 @@ +# 用户举报 Flutter 对接 + +本文描述 Flutter App 对接用户举报和房间举报的 HTTP 契约。通用文件上传接口用于上传举报图片并拿到 URL,提交举报接口只接收 JSON。 + +## 基础地址 + +本地开发地址: + +```text +http://127.0.0.1:13000 +``` + +线上环境使用当前 App 配置里的 gateway base URL。下面所有路径都拼在 gateway base URL 后。 + +## 请求头 + +| Header | 必填 | 示例 | 说明 | +| --- | --- | --- | --- | +| `Authorization` | 是 | `Bearer ` | 登录 access token;举报发起人只从 token 读取,Flutter 不传举报发起人 ID。 | +| `X-App-Code` | 否 | `lalu` | App 编码;登录态接口最终以 token 内的 `app_code` 为准。 | +| `Content-Type` | 提交举报必填 | `application/json` | 提交举报使用 JSON。 | +| `Content-Type` | 上传图片必填 | `multipart/form-data` | 上传举报图片使用 multipart。 | + +统一响应 envelope: + +```json +{ + "code": "OK", + "message": "ok", + "request_id": "req_abc", + "data": {} +} +``` + +Flutter 只在 `code == "OK"` 时读取 `data`。失败时记录 `request_id`,用于后端排查。 + +## 调用顺序 + +```text +1. 用户在举报弹窗选择举报类型。 +2. 如有举报图,逐张调用 POST /api/v1/files/upload,收集每次返回的 data.url。 +3. 调用 POST /api/v1/reports 提交举报。 +4. 提交成功后关闭弹窗,并用 data.report_id 写本地日志。 +``` + +## 上传举报图片 + +上传接口只负责把文件写入对象存储并返回 URL,不创建举报单。 + +```http +POST /api/v1/files/upload +Authorization: Bearer +X-App-Code: lalu +Content-Type: multipart/form-data + +file= +``` + +请求字段: + +| 字段 | 必填 | 类型 | 说明 | +| --- | --- | --- | --- | +| `file` | 是 | multipart file | 单文件最大 20MB。多图逐张上传,Flutter 把每张图返回的 `data.url` 放进提交举报接口的 `image_urls`。 | + +成功响应: + +```json +{ + "code": "OK", + "message": "ok", + "request_id": "req_upload_report_image", + "data": { + "url": "https://cdn.example.com/app/files/10001/20260529/file_xxx.jpg", + "object_key": "app/files/10001/20260529/file_xxx.jpg", + "content_type": "image/jpeg", + "size_bytes": 345678 + } +} +``` + +Flutter 提交举报时只需要使用 `data.url`。 + +## 提交举报 + +```http +POST /api/v1/reports +Authorization: Bearer +X-App-Code: lalu +Content-Type: application/json + +{ + "user_id": "10002", + "report_type": "pornography", + "reason": "用户在房间内发布违规内容", + "image_urls": [ + "https://cdn.example.com/app/files/10001/20260529/file_xxx.jpg" + ] +} +``` + +举报房间示例: + +```http +POST /api/v1/reports +Authorization: Bearer +X-App-Code: lalu +Content-Type: application/json + +{ + "room_id": "lalu_abc123", + "report_type": "fraud", + "reason": "房间引导线下交易", + "image_urls": [] +} +``` + +请求字段: + +| 字段 | 必填 | 类型 | 说明 | +| --- | --- | --- | --- | +| `user_id` | 条件必填 | string | 被举报用户 ID。和 `room_id` 二选一,只能传一个。 | +| `room_id` | 条件必填 | string | 被举报房间 ID。和 `user_id` 二选一,只能传一个。 | +| `report_type` | 是 | string | 举报类型,取值见下方枚举。 | +| `reason` | 否 | string | 举报理由;Flutter 可允许用户不填。 | +| `image_urls` | 否 | string[] | 举报图 URL 数组;无图可传 `[]` 或不传。 | + +目标校验规则: + +| 场景 | 结果 | +| --- | --- | +| 只传 `user_id` | 举报用户。 | +| 只传 `room_id` | 举报房间。 | +| `user_id` 和 `room_id` 都不传 | `400 INVALID_ARGUMENT`。 | +| `user_id` 和 `room_id` 同时传 | `400 INVALID_ARGUMENT`,Flutter 需要拆成一次明确的用户举报或房间举报。 | + +举报发起人不在请求体里传。gateway 必须从 `Authorization` 对应的 access token 读取当前登录用户,避免客户端伪造举报发起人。 + +## 举报类型 + +| UI 文案 | `report_type` | +| --- | --- | +| Politics | `politics` | +| Pornography | `pornography` | +| Graphic Violence | `graphic_violence` | +| Verbal Abuse | `verbal_abuse` | +| Fraud | `fraud` | +| Illegal Content | `illegal_content` | +| Minors | `minors` | +| In-Person Transactions | `in_person_transactions` | +| Other | `other` | + +## 返回体模拟数据 + +举报用户成功: + +```json +{ + "code": "OK", + "message": "ok", + "request_id": "req_report_user", + "data": { + "report_id": "rpt_10001_1778000000000", + "target_type": "user", + "user_id": "10002", + "room_id": "", + "report_type": "pornography", + "status": "submitted", + "created_at_ms": 1778000000000 + } +} +``` + +举报房间成功: + +```json +{ + "code": "OK", + "message": "ok", + "request_id": "req_report_room", + "data": { + "report_id": "rpt_10001_1778000001000", + "target_type": "room", + "user_id": "", + "room_id": "lalu_abc123", + "report_type": "fraud", + "status": "submitted", + "created_at_ms": 1778000001000 + } +} +``` + +字段说明: + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `report_id` | string | 举报单 ID;Flutter 可用于本地日志和客服排查。 | +| `target_type` | string | `user` 或 `room`,由服务端按本次传入目标生成。 | +| `user_id` | string | 被举报用户 ID;房间举报返回空字符串。 | +| `room_id` | string | 被举报房间 ID;用户举报返回空字符串。 | +| `report_type` | string | 本次提交的举报类型。 | +| `status` | string | 首次提交固定返回 `submitted`。 | +| `created_at_ms` | int64 | 举报创建时间,Unix epoch milliseconds。 | + +## 错误处理 + +失败返回仍使用统一 envelope: + +```json +{ + "code": "INVALID_ARGUMENT", + "message": "invalid argument", + "request_id": "req_abc" +} +``` + +| HTTP 状态码 | `code` | Flutter 处理 | +| --- | --- | --- | +| `400` | `INVALID_JSON` | JSON 格式错误,检查本地序列化逻辑。 | +| `400` | `INVALID_ARGUMENT` | `user_id/room_id` 二选一不满足、`report_type` 非法、图片 URL 格式非法或字段超长。 | +| `401` | `UNAUTHORIZED` | token 缺失、无效、过期或会话失效,重新登录。 | +| `403` | `PROFILE_REQUIRED` | 用户资料未完成,跳转资料补全流程。 | +| `404` | `NOT_FOUND` | 被举报用户不存在,刷新页面状态。 | +| `502` | `UPSTREAM_ERROR` | 内部服务暂不可用,可提示稍后重试。 | + +## Flutter 数据模型建议 + +```dart +class SubmitReportRequest { + SubmitReportRequest({ + this.userId, + this.roomId, + required this.reportType, + this.reason, + this.imageUrls = const [], + }); + + final String? userId; + final String? roomId; + final String reportType; + final String? reason; + final List imageUrls; + + Map toJson() => { + if (userId != null && userId!.isNotEmpty) 'user_id': userId, + if (roomId != null && roomId!.isNotEmpty) 'room_id': roomId, + 'report_type': reportType, + if (reason != null && reason!.trim().isNotEmpty) 'reason': reason!.trim(), + if (imageUrls.isNotEmpty) 'image_urls': imageUrls, + }; +} +``` + +提交前本地校验: + +```dart +void validateReportTarget({String? userId, String? roomId}) { + final hasUser = userId != null && userId.trim().isNotEmpty; + final hasRoom = roomId != null && roomId.trim().isNotEmpty; + if (hasUser == hasRoom) { + throw ArgumentError('user_id and room_id must be exactly one'); + } +} +``` + +## 实现边界 + +- Flutter 不传举报发起人 ID,服务端只认 token 中的当前用户。 +- `user_id` 表示被举报用户,不是举报发起人。 +- `room_id` 表示被举报房间,不要求同时传房间内某个用户。 +- `user_id` 和 `room_id` 必须二选一,不能同时提交一个混合举报。 +- 举报图片先上传再提交 URL;提交举报接口不接收 multipart 文件。 +- `message` 不作为业务分支依据,Flutter 只按 `code` 和 HTTP 状态处理。 diff --git a/server/admin/cmd/server/main.go b/server/admin/cmd/server/main.go index 433a24e6..6c6115c6 100644 --- a/server/admin/cmd/server/main.go +++ b/server/admin/cmd/server/main.go @@ -47,6 +47,7 @@ import ( redpacketmodule "hyapp-admin-server/internal/modules/redpacket" regionblockmodule "hyapp-admin-server/internal/modules/regionblock" registrationrewardmodule "hyapp-admin-server/internal/modules/registrationreward" + reportmodule "hyapp-admin-server/internal/modules/report" resourcemodule "hyapp-admin-server/internal/modules/resource" roomadminmodule "hyapp-admin-server/internal/modules/roomadmin" roomtreasuremodule "hyapp-admin-server/internal/modules/roomtreasure" @@ -227,6 +228,7 @@ func main() { Payment: paymentmodule.New(walletclient.NewGRPC(walletConn), auditHandler), RBAC: rbacmodule.New(store, auditHandler), RedPacket: redpacketmodule.New(walletclient.NewGRPC(walletConn), auditHandler), + Report: reportmodule.New(userDB, roomClient), RegistrationReward: registrationrewardmodule.New(activityclient.NewGRPC(activityConn), userDB, auditHandler), RegionBlock: regionblockmodule.New(userDB, auditHandler), Resource: resourcemodule.New(walletclient.NewGRPC(walletConn), store, userDB, auditHandler), diff --git a/server/admin/internal/modules/report/handler.go b/server/admin/internal/modules/report/handler.go new file mode 100644 index 00000000..daf98e6e --- /dev/null +++ b/server/admin/internal/modules/report/handler.go @@ -0,0 +1,83 @@ +package report + +import ( + "database/sql" + "strconv" + "strings" + + "hyapp-admin-server/internal/appctx" + "hyapp-admin-server/internal/integration/roomclient" + "hyapp-admin-server/internal/modules/shared" + "hyapp-admin-server/internal/response" + + "github.com/gin-gonic/gin" +) + +type Handler struct { + service *Service +} + +func New(userDB *sql.DB, roomClient roomclient.Client) *Handler { + return &Handler{service: NewService(userDB, roomClient)} +} + +func (h *Handler) ListReports(c *gin.Context) { + query, ok := parseListQuery(c) + if !ok { + return + } + items, total, err := h.service.ListReports(c.Request.Context(), appctx.FromContext(c.Request.Context()), query) + if err != nil { + response.ServerError(c, "获取举报列表失败") + return + } + response.OK(c, response.Page{Items: items, Page: query.Page, PageSize: query.PageSize, Total: total}) +} + +func parseListQuery(c *gin.Context) (listQuery, bool) { + options := shared.ListOptions(c) + startAtMS, ok := optionalInt64(c, "start_at_ms", "startAtMs") + if !ok { + response.BadRequest(c, "开始时间不正确") + return listQuery{}, false + } + endAtMS, ok := optionalInt64(c, "end_at_ms", "endAtMs") + if !ok { + response.BadRequest(c, "结束时间不正确") + return listQuery{}, false + } + if startAtMS > 0 && endAtMS > 0 && startAtMS >= endAtMS { + response.BadRequest(c, "时间区间不正确") + return listQuery{}, false + } + return normalizeListQuery(listQuery{ + Page: options.Page, + PageSize: options.PageSize, + Keyword: options.Keyword, + TargetType: firstQuery(c, "target_type", "targetType"), + ReportType: firstQuery(c, "report_type", "reportType"), + StartAtMS: startAtMS, + EndAtMS: endAtMS, + }), true +} + +func optionalInt64(c *gin.Context, keys ...string) (int64, bool) { + value := strings.TrimSpace(firstQuery(c, keys...)) + if value == "" { + return 0, true + } + parsed, err := strconv.ParseInt(value, 10, 64) + if err != nil || parsed < 0 { + return 0, false + } + return parsed, true +} + +func firstQuery(c *gin.Context, keys ...string) string { + for _, key := range keys { + if value := strings.TrimSpace(c.Query(key)); value != "" { + return value + } + } + return "" +} diff --git a/server/admin/internal/modules/report/request.go b/server/admin/internal/modules/report/request.go new file mode 100644 index 00000000..7c14c2eb --- /dev/null +++ b/server/admin/internal/modules/report/request.go @@ -0,0 +1,11 @@ +package report + +type listQuery struct { + Page int + PageSize int + Keyword string + TargetType string + ReportType string + StartAtMS int64 + EndAtMS int64 +} diff --git a/server/admin/internal/modules/report/response.go b/server/admin/internal/modules/report/response.go new file mode 100644 index 00000000..391772f1 --- /dev/null +++ b/server/admin/internal/modules/report/response.go @@ -0,0 +1,38 @@ +package report + +type reportUserDTO struct { + Avatar string `json:"avatar"` + DefaultDisplayUserID string `json:"defaultDisplayUserId"` + DisplayUserID string `json:"displayUserId"` + UserID string `json:"userId"` + Username string `json:"username"` +} + +type reportRoomDTO struct { + CoverURL string `json:"coverUrl"` + RoomID string `json:"roomId"` + RoomShortID string `json:"roomShortId"` + Title string `json:"title"` +} + +type reportTargetDTO struct { + Type string `json:"type"` + Room *reportRoomDTO `json:"room,omitempty"` + User *reportUserDTO `json:"user,omitempty"` +} + +type reportDTO struct { + CreatedAtMS int64 `json:"createdAtMs"` + ImageURLs []string `json:"imageUrls"` + Reason string `json:"reason"` + ReportID string `json:"reportId"` + ReporterUserID string `json:"reporterUserId"` + ReportType string `json:"reportType"` + RequestID string `json:"requestId"` + RoomID string `json:"roomId"` + Status string `json:"status"` + Target reportTargetDTO `json:"target"` + TargetType string `json:"targetType"` + UpdatedAtMS int64 `json:"updatedAtMs"` + UserID string `json:"userId"` +} diff --git a/server/admin/internal/modules/report/routes.go b/server/admin/internal/modules/report/routes.go new file mode 100644 index 00000000..4fb26f3f --- /dev/null +++ b/server/admin/internal/modules/report/routes.go @@ -0,0 +1,15 @@ +package report + +import ( + "hyapp-admin-server/internal/middleware" + + "github.com/gin-gonic/gin" +) + +func RegisterRoutes(protected *gin.RouterGroup, h *Handler) { + if h == nil { + return + } + + protected.GET("/admin/operations/reports", middleware.RequirePermission("report:view"), h.ListReports) +} diff --git a/server/admin/internal/modules/report/service.go b/server/admin/internal/modules/report/service.go new file mode 100644 index 00000000..46557fee --- /dev/null +++ b/server/admin/internal/modules/report/service.go @@ -0,0 +1,274 @@ +package report + +import ( + "context" + "database/sql" + "encoding/json" + "fmt" + "strconv" + "strings" + + "hyapp-admin-server/internal/integration/roomclient" +) + +const ( + targetTypeUser = "user" + targetTypeRoom = "room" +) + +type Service struct { + roomClient roomclient.Client + userDB *sql.DB +} + +func NewService(userDB *sql.DB, roomClient roomclient.Client) *Service { + return &Service{userDB: userDB, roomClient: roomClient} +} + +func (s *Service) ListReports(ctx context.Context, appCode string, query listQuery) ([]reportDTO, int64, error) { + query = normalizeListQuery(query) + if s == nil || s.userDB == nil { + return nil, 0, fmt.Errorf("user mysql is not configured") + } + + whereSQL, args := reportWhere(appCode, query) + var total int64 + if err := s.userDB.QueryRowContext(ctx, ` + SELECT COUNT(*) + FROM user_reports r + LEFT JOIN users u ON u.app_code = r.app_code AND u.user_id = r.target_user_id + `+whereSQL, + args..., + ).Scan(&total); err != nil { + return nil, 0, err + } + + rows, err := s.userDB.QueryContext(ctx, ` + SELECT r.report_id, r.reporter_user_id, r.target_type, r.target_user_id, r.room_id, + r.report_type, r.reason, CAST(r.image_urls_json AS CHAR), r.status, r.request_id, + r.created_at_ms, r.updated_at_ms, + COALESCE(u.current_display_user_id, ''), COALESCE(u.default_display_user_id, ''), + COALESCE(u.username, ''), COALESCE(u.avatar, '') + FROM user_reports r + LEFT JOIN users u ON u.app_code = r.app_code AND u.user_id = r.target_user_id + `+whereSQL+` + ORDER BY r.created_at_ms DESC, r.report_id DESC + LIMIT ? OFFSET ?`, + append(args, query.PageSize, offset(query.Page, query.PageSize))..., + ) + if err != nil { + return nil, 0, err + } + defer rows.Close() + + items := make([]reportDTO, 0, query.PageSize) + for rows.Next() { + var item reportDTO + var reporterUserID int64 + var targetUserID sql.NullInt64 + var imageURLsJSON string + var displayUserID string + var defaultDisplayUserID string + var username string + var avatar string + if err := rows.Scan( + &item.ReportID, + &reporterUserID, + &item.TargetType, + &targetUserID, + &item.RoomID, + &item.ReportType, + &item.Reason, + &imageURLsJSON, + &item.Status, + &item.RequestID, + &item.CreatedAtMS, + &item.UpdatedAtMS, + &displayUserID, + &defaultDisplayUserID, + &username, + &avatar, + ); err != nil { + return nil, 0, err + } + imageURLs, err := parseImageURLs(imageURLsJSON) + if err != nil { + return nil, 0, err + } + item.ReporterUserID = formatID(reporterUserID) + item.ImageURLs = imageURLs + item.Target = reportTargetDTO{Type: item.TargetType} + if targetUserID.Valid && targetUserID.Int64 > 0 { + item.UserID = formatID(targetUserID.Int64) + item.Target.User = &reportUserDTO{ + Avatar: avatar, + DefaultDisplayUserID: defaultDisplayUserID, + DisplayUserID: displayUserID, + UserID: item.UserID, + Username: username, + } + } + if item.TargetType == targetTypeRoom { + item.Target.Room = &reportRoomDTO{RoomID: item.RoomID} + } + items = append(items, item) + } + if err := rows.Err(); err != nil { + return nil, 0, err + } + + if err := s.fillRoomTargets(ctx, items); err != nil { + return nil, 0, err + } + return items, total, nil +} + +func normalizeListQuery(query listQuery) listQuery { + if query.Page < 1 { + query.Page = 1 + } + if query.PageSize < 1 { + query.PageSize = 20 + } + if query.PageSize > 100 { + query.PageSize = 100 + } + query.Keyword = strings.TrimSpace(query.Keyword) + query.TargetType = normalizeTargetType(query.TargetType) + query.ReportType = strings.ToLower(strings.TrimSpace(query.ReportType)) + if query.StartAtMS < 0 { + query.StartAtMS = 0 + } + if query.EndAtMS < 0 { + query.EndAtMS = 0 + } + return query +} + +func normalizeTargetType(value string) string { + switch strings.ToLower(strings.TrimSpace(value)) { + case targetTypeUser, "用户": + return targetTypeUser + case targetTypeRoom, "房间": + return targetTypeRoom + default: + return "" + } +} + +func reportWhere(appCode string, query listQuery) (string, []any) { + conditions := []string{"r.app_code = ?"} + args := []any{appCode} + if query.TargetType != "" { + conditions = append(conditions, "r.target_type = ?") + args = append(args, query.TargetType) + } + if query.ReportType != "" { + conditions = append(conditions, "r.report_type = ?") + args = append(args, query.ReportType) + } + if query.StartAtMS > 0 { + conditions = append(conditions, "r.created_at_ms >= ?") + args = append(args, query.StartAtMS) + } + if query.EndAtMS > 0 { + conditions = append(conditions, "r.created_at_ms < ?") + args = append(args, query.EndAtMS) + } + if query.Keyword != "" { + like := "%" + query.Keyword + "%" + conditions = append(conditions, `(r.report_id LIKE ? OR r.room_id LIKE ? OR CAST(r.target_user_id AS CHAR) LIKE ? OR CAST(r.reporter_user_id AS CHAR) LIKE ? OR u.current_display_user_id LIKE ? OR u.default_display_user_id LIKE ? OR u.username LIKE ?)`) + args = append(args, like, like, like, like, like, like, like) + } + return "WHERE " + strings.Join(conditions, " AND "), args +} + +func parseImageURLs(raw string) ([]string, error) { + if strings.TrimSpace(raw) == "" { + return []string{}, nil + } + var urls []string + if err := json.Unmarshal([]byte(raw), &urls); err != nil { + return nil, err + } + if urls == nil { + return []string{}, nil + } + return urls, nil +} + +func (s *Service) fillRoomTargets(ctx context.Context, items []reportDTO) error { + if s == nil || s.roomClient == nil || len(items) == 0 { + return nil + } + roomIDs := make([]string, 0, len(items)) + for _, item := range items { + if item.TargetType == targetTypeRoom && strings.TrimSpace(item.RoomID) != "" { + roomIDs = append(roomIDs, item.RoomID) + } + } + rooms, err := s.queryRooms(ctx, uniqueStrings(roomIDs)) + if err != nil { + return err + } + for index := range items { + if items[index].TargetType != targetTypeRoom { + continue + } + room := reportRoomDTO{RoomID: items[index].RoomID} + if found, ok := rooms[items[index].RoomID]; ok { + room = found + } + items[index].Target.Room = &room + } + return nil +} + +func (s *Service) queryRooms(ctx context.Context, roomIDs []string) (map[string]reportRoomDTO, error) { + rooms := make(map[string]reportRoomDTO, len(roomIDs)) + for _, roomID := range roomIDs { + room, err := s.roomClient.GetRoom(ctx, roomclient.GetRoomRequest{RoomID: roomID}) + if err != nil { + // 举报列表是治理入口,房间被删或房间服务短暂不可读时仍保留举报事实本身。 + if ctx.Err() != nil { + return nil, ctx.Err() + } + continue + } + rooms[roomID] = reportRoomDTO{ + CoverURL: room.CoverURL, + RoomID: room.RoomID, + RoomShortID: room.RoomShortID, + Title: room.Title, + } + } + return rooms, nil +} + +func formatID(value int64) string { + if value <= 0 { + return "" + } + return strconv.FormatInt(value, 10) +} + +func uniqueStrings(values []string) []string { + seen := make(map[string]struct{}, len(values)) + unique := make([]string, 0, len(values)) + for _, value := range values { + value = strings.TrimSpace(value) + if value == "" { + continue + } + if _, ok := seen[value]; ok { + continue + } + seen[value] = struct{}{} + unique = append(unique, value) + } + return unique +} + +func offset(page int, pageSize int) int { + return (page - 1) * pageSize +} diff --git a/server/admin/internal/modules/report/service_test.go b/server/admin/internal/modules/report/service_test.go new file mode 100644 index 00000000..f357855f --- /dev/null +++ b/server/admin/internal/modules/report/service_test.go @@ -0,0 +1,38 @@ +package report + +import "testing" + +func TestNormalizeListQueryCapsPageSizeAndFilters(t *testing.T) { + query := normalizeListQuery(listQuery{Page: -1, PageSize: 1000, Keyword: " abc ", TargetType: "用户", ReportType: " Fraud "}) + if query.Page != 1 || query.PageSize != 100 || query.Keyword != "abc" || query.TargetType != targetTypeUser || query.ReportType != "fraud" { + t.Fatalf("normalized query mismatch: %+v", query) + } +} + +func TestReportWhereUsesStableFilters(t *testing.T) { + query := listQuery{ + Keyword: "10001", + TargetType: targetTypeRoom, + ReportType: "fraud", + StartAtMS: 100, + EndAtMS: 200, + } + where, args := reportWhere("lalu", query) + want := "WHERE r.app_code = ? AND r.target_type = ? AND r.report_type = ? AND r.created_at_ms >= ? AND r.created_at_ms < ? AND (r.report_id LIKE ? OR r.room_id LIKE ? OR CAST(r.target_user_id AS CHAR) LIKE ? OR CAST(r.reporter_user_id AS CHAR) LIKE ? OR u.current_display_user_id LIKE ? OR u.default_display_user_id LIKE ? OR u.username LIKE ?)" + if where != want { + t.Fatalf("where mismatch:\nwant %s\n got %s", want, where) + } + if len(args) != 12 || args[0] != "lalu" || args[1] != targetTypeRoom || args[2] != "fraud" || args[3] != int64(100) || args[4] != int64(200) || args[5] != "%10001%" || args[11] != "%10001%" { + t.Fatalf("args mismatch: %#v", args) + } +} + +func TestParseImageURLsNormalizesEmptyJSON(t *testing.T) { + urls, err := parseImageURLs("null") + if err != nil { + t.Fatalf("parse images failed: %v", err) + } + if len(urls) != 0 { + t.Fatalf("empty images mismatch: %#v", urls) + } +} diff --git a/server/admin/internal/repository/seed.go b/server/admin/internal/repository/seed.go index 86533e53..ef5470a6 100644 --- a/server/admin/internal/repository/seed.go +++ b/server/admin/internal/repository/seed.go @@ -76,6 +76,7 @@ var defaultPermissions = []model.Permission{ {Name: "金币流水查看", Code: "coin-ledger:view", Kind: "menu"}, {Name: "金币增减查看", Code: "coin-adjustment:view", Kind: "menu"}, {Name: "金币增减创建", Code: "coin-adjustment:create", Kind: "button"}, + {Name: "举报列表查看", Code: "report:view", Kind: "menu"}, {Name: "支付账单查看", Code: "payment-bill:view", Kind: "menu"}, {Name: "内购配置查看", Code: "payment-product:view", Kind: "menu"}, {Name: "内购配置创建", Code: "payment-product:create", Kind: "button"}, @@ -238,6 +239,7 @@ func (s *Store) seedMenus() error { {ParentID: &operationsID, Title: "金币流水", Code: "operation-coin-ledger", Path: "/operations/coin-ledger", Icon: "receipt", PermissionCode: "coin-ledger:view", Sort: 68, Visible: true}, {ParentID: &operationsID, Title: "金币增减", Code: "operation-coin-adjustment", Path: "/operations/coin-adjustments", Icon: "wallet", PermissionCode: "coin-adjustment:view", Sort: 69, Visible: true}, {ParentID: &operationsID, Title: "幸运礼物", Code: "lucky-gift", Path: "/operations/lucky-gift", Icon: "redeem", PermissionCode: "lucky-gift:view", Sort: 70, Visible: true}, + {ParentID: &operationsID, Title: "举报列表", Code: "operation-reports", Path: "/operations/reports", Icon: "flag", PermissionCode: "report:view", Sort: 71, Visible: true}, {ParentID: &paymentID, Title: "账单列表", Code: "payment-bill-list", Path: "/payment/bills", Icon: "receipt", PermissionCode: "payment-bill:view", Sort: 68, Visible: true}, {ParentID: &activityID, Title: "每日任务", Code: "daily-task-list", Path: "/activities/daily-tasks", Icon: "task", PermissionCode: "daily-task:view", Sort: 69, Visible: true}, {ParentID: &activityID, Title: "注册奖励", Code: "registration-reward", Path: "/activities/registration-reward", Icon: "gift", PermissionCode: "registration-reward:view", Sort: 70, Visible: true}, @@ -482,7 +484,7 @@ func defaultRolePermissionCodes(code string) []string { "agency:view", "agency:create", "agency:status", "bd:view", "bd:create", "bd:update", "coin-seller:view", "coin-seller:create", "coin-seller:update", "coin-seller:stock-credit", - "coin-ledger:view", "coin-adjustment:view", "coin-adjustment:create", "payment-bill:view", "payment-product:view", "payment-product:create", "payment-product:update", "payment-product:delete", + "coin-ledger:view", "coin-adjustment:view", "coin-adjustment:create", "report:view", "payment-bill:view", "payment-product:view", "payment-product:create", "payment-product:update", "payment-product:delete", "lucky-gift:view", "lucky-gift:update", "game:view", "game:create", "game:update", "game:status", "game:delete", "daily-task:view", "daily-task:create", "daily-task:update", "daily-task:status", @@ -496,7 +498,7 @@ func defaultRolePermissionCodes(code string) []string { "upload:create", } case "auditor": - return []string{"overview:view", "log:view", "user:view", "app-user:view", "level-config:view", "region-block:view", "room:view", "room-pin:view", "room-config:view", "app-config:view", "app-version:view", "resource:view", "resource-shop:view", "resource-group:view", "resource-grant:view", "gift:view", "emoji-pack:view", "coin-ledger:view", "coin-adjustment:view", "lucky-gift:view", "payment-bill:view", "payment-product:view", "game:view", "daily-task:view", "achievement:view", "seven-day-checkin:view", "room-treasure:view", "red-packet:view", "vip-config:view", "role:view", "permission:view", "job:view"} + return []string{"overview:view", "log:view", "user:view", "app-user:view", "level-config:view", "region-block:view", "room:view", "room-pin:view", "room-config:view", "app-config:view", "app-version:view", "resource:view", "resource-shop:view", "resource-group:view", "resource-grant:view", "gift:view", "emoji-pack:view", "coin-ledger:view", "coin-adjustment:view", "report:view", "lucky-gift:view", "payment-bill:view", "payment-product:view", "game:view", "daily-task:view", "achievement:view", "seven-day-checkin:view", "room-treasure:view", "red-packet:view", "vip-config:view", "role:view", "permission:view", "job:view"} case "readonly": return []string{ "overview:view", @@ -523,6 +525,7 @@ func defaultRolePermissionCodes(code string) []string { "coin-seller:view", "coin-ledger:view", "coin-adjustment:view", + "report:view", "lucky-gift:view", "payment-bill:view", "payment-product:view", @@ -564,7 +567,7 @@ func defaultRolePermissionMigrationCodes(code string) []string { "country:view", "country:create", "country:update", "country:status", "region:view", "region:create", "region:update", "region:status", "coin-seller:view", "coin-seller:create", "coin-seller:update", "coin-seller:stock-credit", - "coin-ledger:view", "coin-adjustment:view", "coin-adjustment:create", "payment-bill:view", "payment-product:view", "payment-product:create", "payment-product:update", "payment-product:delete", + "coin-ledger:view", "coin-adjustment:view", "coin-adjustment:create", "report:view", "payment-bill:view", "payment-product:view", "payment-product:create", "payment-product:update", "payment-product:delete", "lucky-gift:view", "lucky-gift:update", "game:view", "game:create", "game:update", "game:status", "game:delete", "daily-task:view", "daily-task:create", "daily-task:update", "daily-task:status", @@ -575,7 +578,7 @@ func defaultRolePermissionMigrationCodes(code string) []string { "vip-config:view", "vip-config:update", "vip-config:grant", } case "auditor", "readonly": - return []string{"level-config:view", "region-block:view", "room:view", "room-pin:view", "room-config:view", "app-config:view", "app-version:view", "resource:view", "resource-shop:view", "resource-group:view", "resource-grant:view", "gift:view", "emoji-pack:view", "coin-seller:view", "coin-ledger:view", "coin-adjustment:view", "lucky-gift:view", "payment-bill:view", "payment-product:view", "game:view", "daily-task:view", "achievement:view", "seven-day-checkin:view", "room-treasure:view", "red-packet:view", "vip-config:view"} + return []string{"level-config:view", "region-block:view", "room:view", "room-pin:view", "room-config:view", "app-config:view", "app-version:view", "resource:view", "resource-shop:view", "resource-group:view", "resource-grant:view", "gift:view", "emoji-pack:view", "coin-seller:view", "coin-ledger:view", "coin-adjustment:view", "report:view", "lucky-gift:view", "payment-bill:view", "payment-product:view", "game:view", "daily-task:view", "achievement:view", "seven-day-checkin:view", "room-treasure:view", "red-packet:view", "vip-config:view"} default: return nil } diff --git a/server/admin/internal/router/router.go b/server/admin/internal/router/router.go index 24c21854..e5526e05 100644 --- a/server/admin/internal/router/router.go +++ b/server/admin/internal/router/router.go @@ -27,6 +27,7 @@ import ( "hyapp-admin-server/internal/modules/redpacket" "hyapp-admin-server/internal/modules/regionblock" "hyapp-admin-server/internal/modules/registrationreward" + reportmodule "hyapp-admin-server/internal/modules/report" resourcemodule "hyapp-admin-server/internal/modules/resource" "hyapp-admin-server/internal/modules/roomadmin" "hyapp-admin-server/internal/modules/roomtreasure" @@ -64,6 +65,7 @@ type Handlers struct { Payment *payment.Handler RBAC *rbac.Handler RedPacket *redpacket.Handler + Report *reportmodule.Handler RegistrationReward *registrationreward.Handler RegionBlock *regionblock.Handler Resource *resourcemodule.Handler @@ -93,6 +95,7 @@ func New(cfg config.Config, auth *service.AuthService, h Handlers) *gin.Engine { menu.RegisterRoutes(protected, h.Menu) rbac.RegisterRoutes(protected, h.RBAC) redpacket.RegisterRoutes(protected, h.RedPacket) + reportmodule.RegisterRoutes(protected, h.Report) resourcemodule.RegisterRoutes(protected, h.Resource) adminuser.RegisterRoutes(protected, h.AdminUser) appuser.RegisterRoutes(protected, h.AppUser) diff --git a/server/admin/migrations/028_report_navigation.sql b/server/admin/migrations/028_report_navigation.sql new file mode 100644 index 00000000..958888d9 --- /dev/null +++ b/server/admin/migrations/028_report_navigation.sql @@ -0,0 +1,43 @@ +SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci; + +SET @now_ms = CAST(UNIX_TIMESTAMP(UTC_TIMESTAMP(3)) * 1000 AS UNSIGNED); + +INSERT INTO admin_permissions (name, code, kind, description, created_at_ms, updated_at_ms) VALUES + ('举报列表查看', 'report:view', 'menu', '', @now_ms, @now_ms) +ON DUPLICATE KEY UPDATE + name = VALUES(name), + kind = VALUES(kind), + description = VALUES(description), + updated_at_ms = @now_ms; + +INSERT INTO admin_menus (parent_id, title, code, path, icon, permission_code, sort, visible, created_at_ms, updated_at_ms) VALUES + (NULL, '运营管理', 'operations', '', 'operations', '', 68, TRUE, @now_ms, @now_ms) +ON DUPLICATE KEY UPDATE + title = VALUES(title), + path = VALUES(path), + icon = VALUES(icon), + permission_code = VALUES(permission_code), + sort = VALUES(sort), + visible = VALUES(visible), + updated_at_ms = @now_ms; + +INSERT INTO admin_menus (parent_id, title, code, path, icon, permission_code, sort, visible, created_at_ms, updated_at_ms) +SELECT parent.id, '举报列表', 'operation-reports', '/operations/reports', 'flag', 'report:view', 71, TRUE, @now_ms, @now_ms +FROM admin_menus parent +WHERE parent.code = 'operations' +ON DUPLICATE KEY UPDATE + parent_id = VALUES(parent_id), + title = VALUES(title), + path = VALUES(path), + icon = VALUES(icon), + permission_code = VALUES(permission_code), + sort = VALUES(sort), + visible = VALUES(visible), + updated_at_ms = @now_ms; + +INSERT IGNORE INTO admin_role_permissions (role_id, permission_id) +SELECT admin_role.id, admin_permission.id +FROM admin_roles admin_role +JOIN admin_permissions admin_permission +WHERE admin_role.code IN ('platform-admin', 'ops-admin', 'auditor', 'readonly') + AND admin_permission.code = 'report:view'; diff --git a/services/gateway-service/internal/client/user_client.go b/services/gateway-service/internal/client/user_client.go index e2fdf20b..cad45b37 100644 --- a/services/gateway-service/internal/client/user_client.go +++ b/services/gateway-service/internal/client/user_client.go @@ -50,6 +50,7 @@ type UserSocialClient interface { DeleteFriend(ctx context.Context, req *userv1.DeleteFriendRequest) (*userv1.DeleteFriendResponse, error) ListFriends(ctx context.Context, req *userv1.ListFriendsRequest) (*userv1.ListFriendsResponse, error) ListFriendApplications(ctx context.Context, req *userv1.ListFriendApplicationsRequest) (*userv1.ListFriendApplicationsResponse, error) + SubmitReport(ctx context.Context, req *userv1.SubmitReportRequest) (*userv1.SubmitReportResponse, error) } // UserDeviceClient 抽象 gateway 对 user-service 设备 push token 的依赖。 @@ -281,6 +282,10 @@ func (c *grpcUserSocialClient) ListFriendApplications(ctx context.Context, req * return c.client.ListFriendApplications(ctx, req) } +func (c *grpcUserSocialClient) SubmitReport(ctx context.Context, req *userv1.SubmitReportRequest) (*userv1.SubmitReportResponse, error) { + return c.client.SubmitReport(ctx, req) +} + func (c *grpcUserDeviceClient) BindPushToken(ctx context.Context, req *userv1.BindPushTokenRequest) (*userv1.BindPushTokenResponse, error) { return c.client.BindPushToken(ctx, req) } diff --git a/services/gateway-service/internal/transport/http/httproutes/router.go b/services/gateway-service/internal/transport/http/httproutes/router.go index f91fcb66..d583baf3 100644 --- a/services/gateway-service/internal/transport/http/httproutes/router.go +++ b/services/gateway-service/internal/transport/http/httproutes/router.go @@ -84,6 +84,7 @@ type UserHandlers struct { ListMyResources http.HandlerFunc EquipMyResource http.HandlerFunc UserSocialAction http.HandlerFunc + SubmitReport http.HandlerFunc } type ManagerHandlers struct { @@ -294,6 +295,7 @@ func (r routes) registerUserRoutes() { r.profile("/users/me", "", h.GetMyProfile) r.profile("/users/me/resources", "", h.ListMyResources) r.profile("/users/me/resources/{resource_id}/equip", "", h.EquipMyResource) + r.profile("/reports", http.MethodPost, h.SubmitReport) r.profile("/users/{user_id}/{action...}", "", h.UserSocialAction) } diff --git a/services/gateway-service/internal/transport/http/social_handler_test.go b/services/gateway-service/internal/transport/http/social_handler_test.go index e833a645..8b14ddc3 100644 --- a/services/gateway-service/internal/transport/http/social_handler_test.go +++ b/services/gateway-service/internal/transport/http/social_handler_test.go @@ -6,6 +6,7 @@ import ( "hyapp/services/gateway-service/internal/transport/http/httpkit" "net/http" "net/http/httptest" + "strings" "testing" userv1 "hyapp.local/api/proto/user/v1" @@ -23,6 +24,7 @@ type fakeUserSocialClient struct { lastDelete *userv1.DeleteFriendRequest lastFriends *userv1.ListFriendsRequest lastFriendApps *userv1.ListFriendApplicationsRequest + lastReport *userv1.SubmitReportRequest } func (f *fakeUserSocialClient) RecordProfileVisit(_ context.Context, req *userv1.RecordProfileVisitRequest) (*userv1.RecordProfileVisitResponse, error) { @@ -75,6 +77,26 @@ func (f *fakeUserSocialClient) ListFriendApplications(_ context.Context, req *us return &userv1.ListFriendApplicationsResponse{Applications: []*userv1.FriendApplication{{RequesterUserId: 10002, TargetUserId: req.GetUserId(), Status: "pending", CreatedAtMs: 9000, UpdatedAtMs: 9000}}, Total: 1}, nil } +func (f *fakeUserSocialClient) SubmitReport(_ context.Context, req *userv1.SubmitReportRequest) (*userv1.SubmitReportResponse, error) { + f.lastReport = req + targetType := "user" + if req.GetRoomId() != "" { + targetType = "room" + } + return &userv1.SubmitReportResponse{Report: &userv1.UserReport{ + ReportId: "rpt-test", + ReporterUserId: req.GetReporterUserId(), + TargetType: targetType, + UserId: req.GetUserId(), + RoomId: req.GetRoomId(), + ReportType: req.GetReportType(), + Reason: req.GetReason(), + ImageUrls: req.GetImageUrls(), + Status: "submitted", + CreatedAtMs: 1778000000000, + }}, nil +} + func TestFriendApplicationFlowRoutesUseBackendThenIMOwnedReminder(t *testing.T) { socialClient := &fakeUserSocialClient{} handler := NewHandler(&fakeRoomClient{}) @@ -104,6 +126,28 @@ func TestFriendApplicationFlowRoutesUseBackendThenIMOwnedReminder(t *testing.T) } } +func TestSubmitReportRoutePassesAuthenticatedReporterAndSingleTarget(t *testing.T) { + socialClient := &fakeUserSocialClient{} + handler := NewHandler(&fakeRoomClient{}) + handler.SetUserSocialClient(socialClient) + router := handler.Routes(auth.NewVerifier("secret")) + + request := httptest.NewRequest(http.MethodPost, "/api/v1/reports", strings.NewReader(`{"user_id":"10002","report_type":"pornography","reason":"bad","image_urls":["https://cdn.example.com/a.png"]}`)) + request.Header.Set("Authorization", "Bearer "+signGatewayTokenWithProfile(t, "secret", 10001, true)) + response := httptest.NewRecorder() + router.ServeHTTP(response, request) + if response.Code != http.StatusOK { + t.Fatalf("submit report status = %d body=%s", response.Code, response.Body.String()) + } + if socialClient.lastReport == nil || socialClient.lastReport.GetReporterUserId() != 10001 || socialClient.lastReport.GetUserId() != 10002 || socialClient.lastReport.GetRoomId() != "" || socialClient.lastReport.GetReportType() != "pornography" { + t.Fatalf("submit report request mismatch: %+v", socialClient.lastReport) + } + var envelope httpkit.ResponseEnvelope + if err := json.Unmarshal(response.Body.Bytes(), &envelope); err != nil || envelope.Code != httpkit.CodeOK { + t.Fatalf("invalid envelope: %+v err=%v", envelope, err) + } +} + func TestSocialListRoutesPassPagingAndDirection(t *testing.T) { socialClient := &fakeUserSocialClient{} handler := NewHandler(&fakeRoomClient{}) diff --git a/services/gateway-service/internal/transport/http/userapi/handler.go b/services/gateway-service/internal/transport/http/userapi/handler.go index c4397cc1..ec829bbb 100644 --- a/services/gateway-service/internal/transport/http/userapi/handler.go +++ b/services/gateway-service/internal/transport/http/userapi/handler.go @@ -61,6 +61,7 @@ func (h *Handler) UserHandlers() httproutes.UserHandlers { ListMyFriendApplications: h.listMyFriendApplications, GetMyProfile: h.getMyProfile, UserSocialAction: h.userSocialAction, + SubmitReport: h.submitReport, } } diff --git a/services/gateway-service/internal/transport/http/userapi/report_handler.go b/services/gateway-service/internal/transport/http/userapi/report_handler.go new file mode 100644 index 00000000..e9795de8 --- /dev/null +++ b/services/gateway-service/internal/transport/http/userapi/report_handler.go @@ -0,0 +1,94 @@ +package userapi + +import ( + "encoding/json" + "net/http" + "strconv" + "strings" + + userv1 "hyapp.local/api/proto/user/v1" + "hyapp/services/gateway-service/internal/auth" + "hyapp/services/gateway-service/internal/transport/http/httpkit" +) + +type submitReportData struct { + ReportID string `json:"report_id"` + TargetType string `json:"target_type"` + UserID string `json:"user_id"` + RoomID string `json:"room_id"` + ReportType string `json:"report_type"` + Status string `json:"status"` + CreatedAtMs int64 `json:"created_at_ms"` +} + +// submitReport 只把当前登录用户和举报目标透传到 user-service;举报发起人不能来自客户端 body。 +func (h *Handler) submitReport(writer http.ResponseWriter, request *http.Request) { + if h.userSocialClient == nil { + httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error") + return + } + var body struct { + UserID json.RawMessage `json:"user_id"` + RoomID string `json:"room_id"` + ReportType string `json:"report_type"` + Reason string `json:"reason"` + ImageURLs []string `json:"image_urls"` + } + if !httpkit.Decode(writer, request, &body) { + return + } + userID, ok := optionalReportUserID(body.UserID) + if !ok { + httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument") + return + } + resp, err := h.userSocialClient.SubmitReport(request.Context(), &userv1.SubmitReportRequest{ + Meta: httpkit.UserMeta(request, ""), + ReporterUserId: auth.UserIDFromContext(request.Context()), + UserId: userID, + RoomId: body.RoomID, + ReportType: body.ReportType, + Reason: body.Reason, + ImageUrls: body.ImageURLs, + }) + if err != nil { + httpkit.WriteRPCError(writer, request, err) + return + } + httpkit.WriteOK(writer, request, reportData(resp.GetReport())) +} + +func optionalReportUserID(raw json.RawMessage) (int64, bool) { + if len(raw) == 0 || string(raw) == "null" { + return 0, true + } + var text string + if err := json.Unmarshal(raw, &text); err == nil { + text = strings.TrimSpace(text) + if text == "" { + return 0, true + } + value, err := strconv.ParseInt(text, 10, 64) + return value, err == nil && value > 0 + } + var value int64 + if err := json.Unmarshal(raw, &value); err != nil { + return 0, false + } + return value, value > 0 +} + +func reportData(report *userv1.UserReport) submitReportData { + if report == nil { + return submitReportData{} + } + return submitReportData{ + ReportID: report.GetReportId(), + TargetType: report.GetTargetType(), + UserID: httpkit.UserIDString(report.GetUserId()), + RoomID: report.GetRoomId(), + ReportType: report.GetReportType(), + Status: report.GetStatus(), + CreatedAtMs: report.GetCreatedAtMs(), + } +} diff --git a/services/user-service/deploy/mysql/initdb/001_user_service.sql b/services/user-service/deploy/mysql/initdb/001_user_service.sql index 7d1911d3..079ac0c2 100644 --- a/services/user-service/deploy/mysql/initdb/001_user_service.sql +++ b/services/user-service/deploy/mysql/initdb/001_user_service.sql @@ -146,6 +146,28 @@ 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 user_reports ( + app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码,用于多租户隔离', + report_id VARCHAR(96) NOT NULL COMMENT '举报单业务 ID', + reporter_user_id BIGINT NOT NULL COMMENT '举报发起人用户 ID', + target_type VARCHAR(16) NOT NULL COMMENT '举报目标类型:user/room', + target_user_id BIGINT NULL COMMENT '被举报用户 ID', + room_id VARCHAR(96) NOT NULL DEFAULT '' COMMENT '被举报房间 ID', + report_type VARCHAR(64) NOT NULL COMMENT '举报类型', + reason VARCHAR(512) NOT NULL DEFAULT '' COMMENT '举报理由', + image_urls_json JSON NOT NULL COMMENT '举报图片 URL 数组', + status VARCHAR(32) NOT NULL COMMENT '处理状态', + request_id VARCHAR(96) NOT NULL DEFAULT '' COMMENT '链路请求 ID,用于排查问题', + created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms', + updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms', + PRIMARY KEY (app_code, report_id), + KEY idx_user_reports_reporter (app_code, reporter_user_id, created_at_ms), + KEY idx_user_reports_target_user (app_code, target_user_id, created_at_ms), + KEY idx_user_reports_room (app_code, room_id, created_at_ms), + KEY idx_user_reports_status (app_code, status, created_at_ms), + KEY idx_user_reports_request (app_code, request_id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户举报表'; + CREATE TABLE IF NOT EXISTS display_user_id_sequences ( app_code VARCHAR(32) NOT NULL PRIMARY KEY COMMENT '应用编码,用于多租户隔离', next_value BIGINT NOT NULL COMMENT '下一个值', diff --git a/services/user-service/internal/domain/user/user.go b/services/user-service/internal/domain/user/user.go index 939e2c2a..31fed33f 100644 --- a/services/user-service/internal/domain/user/user.go +++ b/services/user-service/internal/domain/user/user.go @@ -192,6 +192,12 @@ const ( FriendApplicationStatusPending = "pending" // FriendApplicationStatusAccepted 表示好友申请已同意。 FriendApplicationStatusAccepted = "accepted" + // ReportTargetUser 表示举报目标是单个用户。 + ReportTargetUser = "user" + // ReportTargetRoom 表示举报目标是单个房间。 + ReportTargetRoom = "room" + // ReportStatusSubmitted 表示举报单已提交,等待后台治理流程处理。 + ReportStatusSubmitted = "submitted" ) // ProfileVisitRecord 是访问某个用户主页的去重记录。 @@ -225,6 +231,36 @@ type FriendApplication struct { UpdatedAtMs int64 } +// Report 是 App 用户提交的治理举报事实。 +type Report struct { + // AppCode 是举报所属 App,后台治理只在同 App 范围内处理。 + AppCode string + // ReportID 是举报单不可变业务 ID。 + ReportID string + // ReporterUserID 是举报发起人,只能来自已认证 token。 + ReporterUserID int64 + // TargetType 区分 user/room,避免靠空字段猜测举报对象。 + TargetType string + // UserID 是被举报用户 ID;房间举报时为 0。 + UserID int64 + // RoomID 是被举报房间 ID;用户举报时为空。 + RoomID string + // ReportType 是客户端选择的稳定举报类型枚举。 + ReportType string + // Reason 是用户补充的举报理由。 + Reason string + // ImageURLs 是用户上传后的举报图片 URL 快照。 + ImageURLs []string + // Status 是举报单当前处理状态。 + Status string + // RequestID 是提交举报时的 gateway 链路追踪 ID。 + RequestID string + // CreatedAtMs 是举报创建时间,UTC epoch ms。 + CreatedAtMs int64 + // UpdatedAtMs 是举报最后更新时间,UTC epoch ms。 + UpdatedAtMs int64 +} + // BusinessUserLookupItem 是带业务场景权限裁剪后的用户查询结果。 // 它只包含选择目标用户所需字段,不能扩展手机号、登录身份或钱包资产。 type BusinessUserLookupItem struct { diff --git a/services/user-service/internal/service/user/moderation_test.go b/services/user-service/internal/service/user/moderation_test.go index fcde8318..43db0e73 100644 --- a/services/user-service/internal/service/user/moderation_test.go +++ b/services/user-service/internal/service/user/moderation_test.go @@ -105,6 +105,7 @@ type fakeModerationRepository struct { user userdomain.User sessionIDs []string lastCommand UserStatusCommand + lastReport userdomain.Report } func (r *fakeModerationRepository) SetUserStatus(_ context.Context, command UserStatusCommand) (UserStatusPersistenceResult, error) { @@ -161,6 +162,11 @@ func (r *fakeModerationRepository) ListFriendApplications(context.Context, int64 return nil, 0, nil } +func (r *fakeModerationRepository) SubmitReport(_ context.Context, report userdomain.Report) (userdomain.Report, error) { + r.lastReport = report + return report, nil +} + func (r *fakeModerationRepository) BusinessUserLookup(context.Context, string, bool, int32) ([]userdomain.BusinessUserLookupItem, error) { return nil, nil } diff --git a/services/user-service/internal/service/user/report.go b/services/user-service/internal/service/user/report.go new file mode 100644 index 00000000..3a36a577 --- /dev/null +++ b/services/user-service/internal/service/user/report.go @@ -0,0 +1,117 @@ +package user + +import ( + "context" + "net/url" + "strings" + "unicode/utf8" + + "hyapp/pkg/idgen" + "hyapp/pkg/roomid" + "hyapp/pkg/xerr" + userdomain "hyapp/services/user-service/internal/domain/user" +) + +const ( + maxReportReasonRunes = 512 + maxReportImages = 9 + maxReportImageURLLen = 1024 +) + +var validReportTypes = map[string]struct{}{ + "politics": {}, + "pornography": {}, + "graphic_violence": {}, + "verbal_abuse": {}, + "fraud": {}, + "illegal_content": {}, + "minors": {}, + "in_person_transactions": {}, + "other": {}, +} + +// SubmitReport 校验 App 举报目标和证据 URL,并把不可变举报事实交给 user repository 持久化。 +func (s *Service) SubmitReport(ctx context.Context, reporterUserID int64, userID int64, roomID string, reportType string, reason string, imageURLs []string, requestID string) (userdomain.Report, error) { + if s.userRepository == nil { + return userdomain.Report{}, xerr.New(xerr.Unavailable, "user repository is not configured") + } + reporterUserID, userID, roomID, reportType, reason, imageURLs, targetType, err := normalizeReportInput(reporterUserID, userID, roomID, reportType, reason, imageURLs) + if err != nil { + return userdomain.Report{}, err + } + nowMs := s.now().UnixMilli() + report := userdomain.Report{ + ReportID: idgen.New("rpt"), + ReporterUserID: reporterUserID, + TargetType: targetType, + UserID: userID, + RoomID: roomID, + ReportType: reportType, + Reason: reason, + ImageURLs: imageURLs, + Status: userdomain.ReportStatusSubmitted, + RequestID: strings.TrimSpace(requestID), + CreatedAtMs: nowMs, + UpdatedAtMs: nowMs, + } + + return s.userRepository.SubmitReport(ctx, report) +} + +func normalizeReportInput(reporterUserID int64, userID int64, roomID string, reportType string, reason string, imageURLs []string) (int64, int64, string, string, string, []string, string, error) { + roomID = strings.TrimSpace(roomID) + reportType = strings.ToLower(strings.TrimSpace(reportType)) + reason = strings.TrimSpace(reason) + if reporterUserID <= 0 { + return 0, 0, "", "", "", nil, "", xerr.New(xerr.InvalidArgument, "reporter_user_id is required") + } + hasUser := userID > 0 + hasRoom := roomID != "" + if hasUser == hasRoom { + return 0, 0, "", "", "", nil, "", xerr.New(xerr.InvalidArgument, "user_id and room_id must be exactly one") + } + if hasUser && userID == reporterUserID { + return 0, 0, "", "", "", nil, "", xerr.New(xerr.InvalidArgument, "cannot report self") + } + targetType := userdomain.ReportTargetUser + if hasRoom { + if !roomid.ValidStringID(roomID) { + return 0, 0, "", "", "", nil, "", xerr.New(xerr.InvalidArgument, "room_id is invalid") + } + targetType = userdomain.ReportTargetRoom + } + if _, ok := validReportTypes[reportType]; !ok { + return 0, 0, "", "", "", nil, "", xerr.New(xerr.InvalidArgument, "report_type is invalid") + } + if utf8.RuneCountInString(reason) > maxReportReasonRunes { + return 0, 0, "", "", "", nil, "", xerr.New(xerr.InvalidArgument, "reason is too long") + } + normalizedImages, err := normalizeReportImageURLs(imageURLs) + if err != nil { + return 0, 0, "", "", "", nil, "", err + } + + return reporterUserID, userID, roomID, reportType, reason, normalizedImages, targetType, nil +} + +func normalizeReportImageURLs(imageURLs []string) ([]string, error) { + if len(imageURLs) > maxReportImages { + return nil, xerr.New(xerr.InvalidArgument, "too many report images") + } + normalized := make([]string, 0, len(imageURLs)) + for _, raw := range imageURLs { + value := strings.TrimSpace(raw) + if value == "" { + continue + } + if len(value) > maxReportImageURLLen { + return nil, xerr.New(xerr.InvalidArgument, "image url is too long") + } + parsed, err := url.Parse(value) + if err != nil || parsed.Host == "" || (parsed.Scheme != "http" && parsed.Scheme != "https") { + return nil, xerr.New(xerr.InvalidArgument, "image url is invalid") + } + normalized = append(normalized, value) + } + return normalized, nil +} diff --git a/services/user-service/internal/service/user/report_test.go b/services/user-service/internal/service/user/report_test.go new file mode 100644 index 00000000..7da0fcaa --- /dev/null +++ b/services/user-service/internal/service/user/report_test.go @@ -0,0 +1,47 @@ +package user + +import ( + "context" + "testing" + "time" + + "hyapp/pkg/appcode" + userdomain "hyapp/services/user-service/internal/domain/user" +) + +func TestSubmitReportAcceptsExactlyOneTarget(t *testing.T) { + repository := &fakeModerationRepository{} + svc := New(repository, WithClock(func() time.Time { return time.UnixMilli(1_778_000_000_000).UTC() })) + ctx := appcode.WithContext(context.Background(), "lalu") + + report, err := svc.SubmitReport(ctx, 10001, 10002, "", " Pornography ", " reason ", []string{" https://cdn.example.com/report.png "}, "req-report") + if err != nil { + t.Fatalf("SubmitReport failed: %v", err) + } + if report.ReportID == "" || report.TargetType != userdomain.ReportTargetUser || report.ReportType != "pornography" || report.Reason != "reason" || len(report.ImageURLs) != 1 { + t.Fatalf("report normalization mismatch: %+v", report) + } + if repository.lastReport.ReporterUserID != 10001 || repository.lastReport.UserID != 10002 || repository.lastReport.RequestID != "req-report" || repository.lastReport.CreatedAtMs != 1_778_000_000_000 { + t.Fatalf("repository report mismatch: %+v", repository.lastReport) + } +} + +func TestSubmitReportRejectsMissingOrMixedTarget(t *testing.T) { + svc := New(&fakeModerationRepository{}) + ctx := appcode.WithContext(context.Background(), "lalu") + tests := []struct { + name string + userID int64 + roomID string + }{ + {name: "missing target"}, + {name: "mixed target", userID: 10002, roomID: "room_1"}, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + if _, err := svc.SubmitReport(ctx, 10001, test.userID, test.roomID, "fraud", "", nil, "req-report"); err == nil { + t.Fatalf("SubmitReport must reject target combination user=%d room=%q", test.userID, test.roomID) + } + }) + } +} diff --git a/services/user-service/internal/service/user/service.go b/services/user-service/internal/service/user/service.go index a501a1d3..9f9ec2c4 100644 --- a/services/user-service/internal/service/user/service.go +++ b/services/user-service/internal/service/user/service.go @@ -37,6 +37,8 @@ type UserRepository interface { ListFriends(ctx context.Context, userID int64, page int32, pageSize int32, cursorUpdatedAtMS int64, cursorUserID int64) ([]userdomain.FriendRecord, int64, error) // ListFriendApplications 分页读取好友申请。 ListFriendApplications(ctx context.Context, userID int64, direction string, page int32, pageSize int32) ([]userdomain.FriendApplication, int64, error) + // SubmitReport 持久化 App 用户提交的治理举报单。 + SubmitReport(ctx context.Context, report userdomain.Report) (userdomain.Report, error) // BusinessUserLookup 按已授权业务场景查询目标用户,返回字段必须保持最小化。 BusinessUserLookup(ctx context.Context, keyword string, numericKeyword bool, pageSize int32) ([]userdomain.BusinessUserLookupItem, error) // BatchGetUsers 批量查询用户主状态,缺失用户不应返回占位对象。 diff --git a/services/user-service/internal/storage/mysql/user/report_repository.go b/services/user-service/internal/storage/mysql/user/report_repository.go new file mode 100644 index 00000000..51c2cf40 --- /dev/null +++ b/services/user-service/internal/storage/mysql/user/report_repository.go @@ -0,0 +1,51 @@ +package user + +import ( + "context" + "encoding/json" + + "hyapp/pkg/appcode" + userdomain "hyapp/services/user-service/internal/domain/user" +) + +// SubmitReport 持久化举报事实。用户目标会校验同 App 存在;房间目标只记录 room_id,不回查 room-service 明细表。 +func (r *Repository) SubmitReport(ctx context.Context, report userdomain.Report) (userdomain.Report, error) { + tx, err := r.db.BeginTx(ctx, nil) + if err != nil { + return userdomain.Report{}, err + } + defer tx.Rollback() + + appCode := appcode.FromContext(ctx) + if err := ensureSocialUser(ctx, tx, appCode, report.ReporterUserID); err != nil { + return userdomain.Report{}, err + } + if report.UserID > 0 { + if err := ensureSocialUser(ctx, tx, appCode, report.UserID); err != nil { + return userdomain.Report{}, err + } + } + imageURLsJSON, err := json.Marshal(report.ImageURLs) + if err != nil { + return userdomain.Report{}, err + } + if _, err := tx.ExecContext(ctx, ` + INSERT INTO user_reports ( + app_code, report_id, reporter_user_id, target_type, target_user_id, room_id, + report_type, reason, image_urls_json, status, request_id, created_at_ms, updated_at_ms + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, + appCode, report.ReportID, report.ReporterUserID, report.TargetType, nullableReportUserID(report.UserID), report.RoomID, + report.ReportType, report.Reason, string(imageURLsJSON), report.Status, report.RequestID, report.CreatedAtMs, report.UpdatedAtMs, + ); err != nil { + return userdomain.Report{}, err + } + report.AppCode = appCode + return report, tx.Commit() +} + +func nullableReportUserID(userID int64) any { + if userID <= 0 { + return nil + } + return userID +} diff --git a/services/user-service/internal/testutil/mysqltest/mysqltest.go b/services/user-service/internal/testutil/mysqltest/mysqltest.go index f9b7a7af..c4279c4f 100644 --- a/services/user-service/internal/testutil/mysqltest/mysqltest.go +++ b/services/user-service/internal/testutil/mysqltest/mysqltest.go @@ -111,6 +111,10 @@ func (r *Repository) ListFriendApplications(ctx context.Context, userID int64, d return r.Repository.UserRepository().ListFriendApplications(ctx, userID, direction, page, pageSize) } +func (r *Repository) SubmitReport(ctx context.Context, report userdomain.Report) (userdomain.Report, error) { + return r.Repository.UserRepository().SubmitReport(ctx, report) +} + // BusinessUserLookup 让测试 wrapper 继续满足业务场景用户查询接口。 func (r *Repository) BusinessUserLookup(ctx context.Context, keyword string, numericKeyword bool, pageSize int32) ([]userdomain.BusinessUserLookupItem, error) { return r.Repository.UserRepository().BusinessUserLookup(ctx, keyword, numericKeyword, pageSize) diff --git a/services/user-service/internal/transport/grpc/convert.go b/services/user-service/internal/transport/grpc/convert.go index 3dfbd00b..c1f5fa06 100644 --- a/services/user-service/internal/transport/grpc/convert.go +++ b/services/user-service/internal/transport/grpc/convert.go @@ -176,6 +176,21 @@ func toProtoFriendApplication(application userdomain.FriendApplication) *userv1. } } +func toProtoUserReport(report userdomain.Report) *userv1.UserReport { + return &userv1.UserReport{ + ReportId: report.ReportID, + ReporterUserId: report.ReporterUserID, + TargetType: report.TargetType, + UserId: report.UserID, + RoomId: report.RoomID, + ReportType: report.ReportType, + Reason: report.Reason, + ImageUrls: append([]string{}, report.ImageURLs...), + Status: report.Status, + CreatedAtMs: report.CreatedAtMs, + } +} + // toProtoCountry 把国家领域对象转换为管理 RPC 响应。 func toProtoCountry(country userdomain.Country) *userv1.Country { return &userv1.Country{ diff --git a/services/user-service/internal/transport/grpc/server.go b/services/user-service/internal/transport/grpc/server.go index 071b7341..77f79440 100644 --- a/services/user-service/internal/transport/grpc/server.go +++ b/services/user-service/internal/transport/grpc/server.go @@ -408,6 +408,16 @@ func (s *Server) ListFriendApplications(ctx context.Context, req *userv1.ListFri return resp, nil } +// SubmitReport 记录 App 用户提交的用户或房间举报。 +func (s *Server) SubmitReport(ctx context.Context, req *userv1.SubmitReportRequest) (*userv1.SubmitReportResponse, error) { + ctx = contextWithApp(ctx, req.GetMeta()) + report, err := s.userSvc.SubmitReport(ctx, req.GetReporterUserId(), req.GetUserId(), req.GetRoomId(), req.GetReportType(), req.GetReason(), req.GetImageUrls(), req.GetMeta().GetRequestId()) + if err != nil { + return nil, xerr.ToGRPCError(err) + } + return &userv1.SubmitReportResponse{Report: toProtoUserReport(report)}, nil +} + // BatchGetUsers 批量返回用户主状态,缺失用户不填充占位对象。 func (s *Server) BatchGetUsers(ctx context.Context, req *userv1.BatchGetUsersRequest) (*userv1.BatchGetUsersResponse, error) { ctx = contextWithApp(ctx, req.GetMeta())