From aa43bf65a9b248bedb79d2580f3ceaf5ff8f4aae Mon Sep 17 00:00:00 2001 From: zhx Date: Wed, 27 May 2026 15:11:00 +0800 Subject: [PATCH] =?UTF-8?q?=E5=B9=B8=E8=BF=90=E7=A4=BC=E7=89=A9=E6=96=87?= =?UTF-8?q?=E6=A1=A3=E7=9A=84=E9=80=BB=E8=BE=91=E5=92=8C=E6=88=BF=E9=97=B4?= =?UTF-8?q?=E7=94=A8=E6=88=B7=E5=88=97=E8=A1=A8=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/proto/room/v1/room.pb.go | 1284 +++++++++-------- api/proto/room/v1/room.proto | 14 + docs/flutter对接/幸运礼物Flutter对接.md | 22 +- .../幸运礼物接口闭环Flutter对接.md | 32 +- docs/幸运礼物Activity服务开发.md | 2 + docs/幸运礼物产品方案.md | 2 + docs/幸运礼物线上落地文档.md | 397 +++-- docs/房内红包需求接口拆分.md | 745 ++++++++++ docs/语音房房间用户列表前端接入文档.md | 420 ++++++ docs/语音房房间用户列表后端技术架构设计.md | 574 ++++++++ .../internal/service/luckygift/config.go | 12 + .../internal/service/luckygift/service.go | 18 + .../storage/mysql/lucky_gift_repository.go | 37 + .../transport/http/roomapi/room_handler.go | 63 +- .../transport/http/roomapi/room_view.go | 39 +- .../deploy/mysql/initdb/001_room_service.sql | 13 + .../internal/room/service/current_room.go | 1 + .../internal/room/service/gift.go | 37 +- .../internal/room/service/management.go | 6 +- .../room/service/online_users_query.go | 36 + .../room/service/online_users_test.go | 95 ++ .../internal/room/service/pipeline.go | 15 +- .../internal/room/service/recovery.go | 7 +- .../internal/room/service/repository.go | 16 + .../internal/room/service/service.go | 2 + .../room-service/internal/room/state/state.go | 32 +- .../internal/storage/mysql/repository.go | 84 +- 27 files changed, 3197 insertions(+), 808 deletions(-) create mode 100644 docs/房内红包需求接口拆分.md create mode 100644 docs/语音房房间用户列表前端接入文档.md create mode 100644 docs/语音房房间用户列表后端技术架构设计.md create mode 100644 services/room-service/internal/room/service/online_users_test.go diff --git a/api/proto/room/v1/room.pb.go b/api/proto/room/v1/room.pb.go index 4fb41d13..c0276937 100644 --- a/api/proto/room/v1/room.pb.go +++ b/api/proto/room/v1/room.pb.go @@ -255,6 +255,100 @@ func (x *RoomUser) GetLastSeenAtMs() int64 { return 0 } +// RoomOnlineUser 是在线用户列表的展示读模型。 +// role 保留进房 presence 语义;room_role 只表达房间管理员身份;is_owner 单独表达房主身份。 +type RoomOnlineUser struct { + state protoimpl.MessageState `protogen:"open.v1"` + UserId int64 `protobuf:"varint,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` + Role string `protobuf:"bytes,2,opt,name=role,proto3" json:"role,omitempty"` + RoomRole string `protobuf:"bytes,3,opt,name=room_role,json=roomRole,proto3" json:"room_role,omitempty"` + GiftValue int64 `protobuf:"varint,4,opt,name=gift_value,json=giftValue,proto3" json:"gift_value,omitempty"` + JoinedAtMs int64 `protobuf:"varint,5,opt,name=joined_at_ms,json=joinedAtMs,proto3" json:"joined_at_ms,omitempty"` + LastSeenAtMs int64 `protobuf:"varint,6,opt,name=last_seen_at_ms,json=lastSeenAtMs,proto3" json:"last_seen_at_ms,omitempty"` + IsOwner bool `protobuf:"varint,7,opt,name=is_owner,json=isOwner,proto3" json:"is_owner,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RoomOnlineUser) Reset() { + *x = RoomOnlineUser{} + mi := &file_proto_room_v1_room_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RoomOnlineUser) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RoomOnlineUser) ProtoMessage() {} + +func (x *RoomOnlineUser) ProtoReflect() protoreflect.Message { + mi := &file_proto_room_v1_room_proto_msgTypes[3] + 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 RoomOnlineUser.ProtoReflect.Descriptor instead. +func (*RoomOnlineUser) Descriptor() ([]byte, []int) { + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{3} +} + +func (x *RoomOnlineUser) GetUserId() int64 { + if x != nil { + return x.UserId + } + return 0 +} + +func (x *RoomOnlineUser) GetRole() string { + if x != nil { + return x.Role + } + return "" +} + +func (x *RoomOnlineUser) GetRoomRole() string { + if x != nil { + return x.RoomRole + } + return "" +} + +func (x *RoomOnlineUser) GetGiftValue() int64 { + if x != nil { + return x.GiftValue + } + return 0 +} + +func (x *RoomOnlineUser) GetJoinedAtMs() int64 { + if x != nil { + return x.JoinedAtMs + } + return 0 +} + +func (x *RoomOnlineUser) GetLastSeenAtMs() int64 { + if x != nil { + return x.LastSeenAtMs + } + return 0 +} + +func (x *RoomOnlineUser) GetIsOwner() bool { + if x != nil { + return x.IsOwner + } + return false +} + // SeatState 表达单个麦位当前占用和 RTC 发流确认状态。 type SeatState struct { state protoimpl.MessageState `protogen:"open.v1"` @@ -281,7 +375,7 @@ type SeatState struct { func (x *SeatState) Reset() { *x = SeatState{} - mi := &file_proto_room_v1_room_proto_msgTypes[3] + mi := &file_proto_room_v1_room_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -293,7 +387,7 @@ func (x *SeatState) String() string { func (*SeatState) ProtoMessage() {} func (x *SeatState) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[3] + mi := &file_proto_room_v1_room_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -306,7 +400,7 @@ func (x *SeatState) ProtoReflect() protoreflect.Message { // Deprecated: Use SeatState.ProtoReflect.Descriptor instead. func (*SeatState) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{3} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{4} } func (x *SeatState) GetSeatNo() int32 { @@ -392,7 +486,7 @@ type RankItem struct { func (x *RankItem) Reset() { *x = RankItem{} - mi := &file_proto_room_v1_room_proto_msgTypes[4] + mi := &file_proto_room_v1_room_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -404,7 +498,7 @@ func (x *RankItem) String() string { func (*RankItem) ProtoMessage() {} func (x *RankItem) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[4] + mi := &file_proto_room_v1_room_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -417,7 +511,7 @@ func (x *RankItem) ProtoReflect() protoreflect.Message { // Deprecated: Use RankItem.ProtoReflect.Descriptor instead. func (*RankItem) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{4} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{5} } func (x *RankItem) GetUserId() int64 { @@ -474,7 +568,7 @@ type LuckyGiftDrawResult struct { func (x *LuckyGiftDrawResult) Reset() { *x = LuckyGiftDrawResult{} - mi := &file_proto_room_v1_room_proto_msgTypes[5] + mi := &file_proto_room_v1_room_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -486,7 +580,7 @@ func (x *LuckyGiftDrawResult) String() string { func (*LuckyGiftDrawResult) ProtoMessage() {} func (x *LuckyGiftDrawResult) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[5] + mi := &file_proto_room_v1_room_proto_msgTypes[6] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -499,7 +593,7 @@ func (x *LuckyGiftDrawResult) ProtoReflect() protoreflect.Message { // Deprecated: Use LuckyGiftDrawResult.ProtoReflect.Descriptor instead. func (*LuckyGiftDrawResult) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{5} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{6} } func (x *LuckyGiftDrawResult) GetEnabled() bool { @@ -635,7 +729,7 @@ type RoomTreasureRewardItem struct { func (x *RoomTreasureRewardItem) Reset() { *x = RoomTreasureRewardItem{} - mi := &file_proto_room_v1_room_proto_msgTypes[6] + mi := &file_proto_room_v1_room_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -647,7 +741,7 @@ func (x *RoomTreasureRewardItem) String() string { func (*RoomTreasureRewardItem) ProtoMessage() {} func (x *RoomTreasureRewardItem) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[6] + mi := &file_proto_room_v1_room_proto_msgTypes[7] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -660,7 +754,7 @@ func (x *RoomTreasureRewardItem) ProtoReflect() protoreflect.Message { // Deprecated: Use RoomTreasureRewardItem.ProtoReflect.Descriptor instead. func (*RoomTreasureRewardItem) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{6} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{7} } func (x *RoomTreasureRewardItem) GetRewardItemId() string { @@ -716,7 +810,7 @@ type RoomTreasureLevel struct { func (x *RoomTreasureLevel) Reset() { *x = RoomTreasureLevel{} - mi := &file_proto_room_v1_room_proto_msgTypes[7] + mi := &file_proto_room_v1_room_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -728,7 +822,7 @@ func (x *RoomTreasureLevel) String() string { func (*RoomTreasureLevel) ProtoMessage() {} func (x *RoomTreasureLevel) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[7] + mi := &file_proto_room_v1_room_proto_msgTypes[8] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -741,7 +835,7 @@ func (x *RoomTreasureLevel) ProtoReflect() protoreflect.Message { // Deprecated: Use RoomTreasureLevel.ProtoReflect.Descriptor instead. func (*RoomTreasureLevel) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{7} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{8} } func (x *RoomTreasureLevel) GetLevel() int32 { @@ -824,7 +918,7 @@ type RoomTreasureRewardGrant struct { func (x *RoomTreasureRewardGrant) Reset() { *x = RoomTreasureRewardGrant{} - mi := &file_proto_room_v1_room_proto_msgTypes[8] + mi := &file_proto_room_v1_room_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -836,7 +930,7 @@ func (x *RoomTreasureRewardGrant) String() string { func (*RoomTreasureRewardGrant) ProtoMessage() {} func (x *RoomTreasureRewardGrant) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[8] + mi := &file_proto_room_v1_room_proto_msgTypes[9] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -849,7 +943,7 @@ func (x *RoomTreasureRewardGrant) ProtoReflect() protoreflect.Message { // Deprecated: Use RoomTreasureRewardGrant.ProtoReflect.Descriptor instead. func (*RoomTreasureRewardGrant) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{8} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{9} } func (x *RoomTreasureRewardGrant) GetRewardRole() string { @@ -930,7 +1024,7 @@ type RoomTreasureState struct { func (x *RoomTreasureState) Reset() { *x = RoomTreasureState{} - mi := &file_proto_room_v1_room_proto_msgTypes[9] + mi := &file_proto_room_v1_room_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -942,7 +1036,7 @@ func (x *RoomTreasureState) String() string { func (*RoomTreasureState) ProtoMessage() {} func (x *RoomTreasureState) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[9] + mi := &file_proto_room_v1_room_proto_msgTypes[10] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -955,7 +1049,7 @@ func (x *RoomTreasureState) ProtoReflect() protoreflect.Message { // Deprecated: Use RoomTreasureState.ProtoReflect.Descriptor instead. func (*RoomTreasureState) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{9} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{10} } func (x *RoomTreasureState) GetCurrentLevel() int32 { @@ -1066,7 +1160,7 @@ type RoomTreasureInfo struct { func (x *RoomTreasureInfo) Reset() { *x = RoomTreasureInfo{} - mi := &file_proto_room_v1_room_proto_msgTypes[10] + mi := &file_proto_room_v1_room_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1078,7 +1172,7 @@ func (x *RoomTreasureInfo) String() string { func (*RoomTreasureInfo) ProtoMessage() {} func (x *RoomTreasureInfo) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[10] + mi := &file_proto_room_v1_room_proto_msgTypes[11] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1091,7 +1185,7 @@ func (x *RoomTreasureInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use RoomTreasureInfo.ProtoReflect.Descriptor instead. func (*RoomTreasureInfo) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{10} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{11} } func (x *RoomTreasureInfo) GetEnabled() bool { @@ -1165,7 +1259,7 @@ type RoomTreasureGiftEnergyRule struct { func (x *RoomTreasureGiftEnergyRule) Reset() { *x = RoomTreasureGiftEnergyRule{} - mi := &file_proto_room_v1_room_proto_msgTypes[11] + mi := &file_proto_room_v1_room_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1177,7 +1271,7 @@ func (x *RoomTreasureGiftEnergyRule) String() string { func (*RoomTreasureGiftEnergyRule) ProtoMessage() {} func (x *RoomTreasureGiftEnergyRule) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[11] + mi := &file_proto_room_v1_room_proto_msgTypes[12] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1190,7 +1284,7 @@ func (x *RoomTreasureGiftEnergyRule) ProtoReflect() protoreflect.Message { // Deprecated: Use RoomTreasureGiftEnergyRule.ProtoReflect.Descriptor instead. func (*RoomTreasureGiftEnergyRule) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{11} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{12} } func (x *RoomTreasureGiftEnergyRule) GetRuleId() string { @@ -1258,7 +1352,7 @@ type AdminRoomTreasureConfig struct { func (x *AdminRoomTreasureConfig) Reset() { *x = AdminRoomTreasureConfig{} - mi := &file_proto_room_v1_room_proto_msgTypes[12] + mi := &file_proto_room_v1_room_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1270,7 +1364,7 @@ func (x *AdminRoomTreasureConfig) String() string { func (*AdminRoomTreasureConfig) ProtoMessage() {} func (x *AdminRoomTreasureConfig) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[12] + mi := &file_proto_room_v1_room_proto_msgTypes[13] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1283,7 +1377,7 @@ func (x *AdminRoomTreasureConfig) ProtoReflect() protoreflect.Message { // Deprecated: Use AdminRoomTreasureConfig.ProtoReflect.Descriptor instead. func (*AdminRoomTreasureConfig) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{12} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{13} } func (x *AdminRoomTreasureConfig) GetAppCode() string { @@ -1393,7 +1487,7 @@ type AdminGetRoomTreasureConfigRequest struct { func (x *AdminGetRoomTreasureConfigRequest) Reset() { *x = AdminGetRoomTreasureConfigRequest{} - mi := &file_proto_room_v1_room_proto_msgTypes[13] + mi := &file_proto_room_v1_room_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1405,7 +1499,7 @@ func (x *AdminGetRoomTreasureConfigRequest) String() string { func (*AdminGetRoomTreasureConfigRequest) ProtoMessage() {} func (x *AdminGetRoomTreasureConfigRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[13] + mi := &file_proto_room_v1_room_proto_msgTypes[14] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1418,7 +1512,7 @@ func (x *AdminGetRoomTreasureConfigRequest) ProtoReflect() protoreflect.Message // Deprecated: Use AdminGetRoomTreasureConfigRequest.ProtoReflect.Descriptor instead. func (*AdminGetRoomTreasureConfigRequest) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{13} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{14} } func (x *AdminGetRoomTreasureConfigRequest) GetMeta() *RequestMeta { @@ -1438,7 +1532,7 @@ type AdminGetRoomTreasureConfigResponse struct { func (x *AdminGetRoomTreasureConfigResponse) Reset() { *x = AdminGetRoomTreasureConfigResponse{} - mi := &file_proto_room_v1_room_proto_msgTypes[14] + mi := &file_proto_room_v1_room_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1450,7 +1544,7 @@ func (x *AdminGetRoomTreasureConfigResponse) String() string { func (*AdminGetRoomTreasureConfigResponse) ProtoMessage() {} func (x *AdminGetRoomTreasureConfigResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[14] + mi := &file_proto_room_v1_room_proto_msgTypes[15] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1463,7 +1557,7 @@ func (x *AdminGetRoomTreasureConfigResponse) ProtoReflect() protoreflect.Message // Deprecated: Use AdminGetRoomTreasureConfigResponse.ProtoReflect.Descriptor instead. func (*AdminGetRoomTreasureConfigResponse) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{14} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{15} } func (x *AdminGetRoomTreasureConfigResponse) GetConfig() *AdminRoomTreasureConfig { @@ -1491,7 +1585,7 @@ type AdminUpdateRoomTreasureConfigRequest struct { func (x *AdminUpdateRoomTreasureConfigRequest) Reset() { *x = AdminUpdateRoomTreasureConfigRequest{} - mi := &file_proto_room_v1_room_proto_msgTypes[15] + mi := &file_proto_room_v1_room_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1503,7 +1597,7 @@ func (x *AdminUpdateRoomTreasureConfigRequest) String() string { func (*AdminUpdateRoomTreasureConfigRequest) ProtoMessage() {} func (x *AdminUpdateRoomTreasureConfigRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[15] + mi := &file_proto_room_v1_room_proto_msgTypes[16] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1516,7 +1610,7 @@ func (x *AdminUpdateRoomTreasureConfigRequest) ProtoReflect() protoreflect.Messa // Deprecated: Use AdminUpdateRoomTreasureConfigRequest.ProtoReflect.Descriptor instead. func (*AdminUpdateRoomTreasureConfigRequest) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{15} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{16} } func (x *AdminUpdateRoomTreasureConfigRequest) GetMeta() *RequestMeta { @@ -1550,7 +1644,7 @@ type AdminUpdateRoomTreasureConfigResponse struct { func (x *AdminUpdateRoomTreasureConfigResponse) Reset() { *x = AdminUpdateRoomTreasureConfigResponse{} - mi := &file_proto_room_v1_room_proto_msgTypes[16] + mi := &file_proto_room_v1_room_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1562,7 +1656,7 @@ func (x *AdminUpdateRoomTreasureConfigResponse) String() string { func (*AdminUpdateRoomTreasureConfigResponse) ProtoMessage() {} func (x *AdminUpdateRoomTreasureConfigResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[16] + mi := &file_proto_room_v1_room_proto_msgTypes[17] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1575,7 +1669,7 @@ func (x *AdminUpdateRoomTreasureConfigResponse) ProtoReflect() protoreflect.Mess // Deprecated: Use AdminUpdateRoomTreasureConfigResponse.ProtoReflect.Descriptor instead. func (*AdminUpdateRoomTreasureConfigResponse) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{16} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{17} } func (x *AdminUpdateRoomTreasureConfigResponse) GetConfig() *AdminRoomTreasureConfig { @@ -1604,7 +1698,7 @@ type AdminRoomSeatConfig struct { func (x *AdminRoomSeatConfig) Reset() { *x = AdminRoomSeatConfig{} - mi := &file_proto_room_v1_room_proto_msgTypes[17] + mi := &file_proto_room_v1_room_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1616,7 +1710,7 @@ func (x *AdminRoomSeatConfig) String() string { func (*AdminRoomSeatConfig) ProtoMessage() {} func (x *AdminRoomSeatConfig) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[17] + mi := &file_proto_room_v1_room_proto_msgTypes[18] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1629,7 +1723,7 @@ func (x *AdminRoomSeatConfig) ProtoReflect() protoreflect.Message { // Deprecated: Use AdminRoomSeatConfig.ProtoReflect.Descriptor instead. func (*AdminRoomSeatConfig) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{17} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{18} } func (x *AdminRoomSeatConfig) GetCandidateSeatCounts() []int32 { @@ -1669,7 +1763,7 @@ type AdminGetRoomSeatConfigRequest struct { func (x *AdminGetRoomSeatConfigRequest) Reset() { *x = AdminGetRoomSeatConfigRequest{} - mi := &file_proto_room_v1_room_proto_msgTypes[18] + mi := &file_proto_room_v1_room_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1681,7 +1775,7 @@ func (x *AdminGetRoomSeatConfigRequest) String() string { func (*AdminGetRoomSeatConfigRequest) ProtoMessage() {} func (x *AdminGetRoomSeatConfigRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[18] + mi := &file_proto_room_v1_room_proto_msgTypes[19] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1694,7 +1788,7 @@ func (x *AdminGetRoomSeatConfigRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use AdminGetRoomSeatConfigRequest.ProtoReflect.Descriptor instead. func (*AdminGetRoomSeatConfigRequest) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{18} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{19} } func (x *AdminGetRoomSeatConfigRequest) GetMeta() *RequestMeta { @@ -1714,7 +1808,7 @@ type AdminGetRoomSeatConfigResponse struct { func (x *AdminGetRoomSeatConfigResponse) Reset() { *x = AdminGetRoomSeatConfigResponse{} - mi := &file_proto_room_v1_room_proto_msgTypes[19] + mi := &file_proto_room_v1_room_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1726,7 +1820,7 @@ func (x *AdminGetRoomSeatConfigResponse) String() string { func (*AdminGetRoomSeatConfigResponse) ProtoMessage() {} func (x *AdminGetRoomSeatConfigResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[19] + mi := &file_proto_room_v1_room_proto_msgTypes[20] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1739,7 +1833,7 @@ func (x *AdminGetRoomSeatConfigResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use AdminGetRoomSeatConfigResponse.ProtoReflect.Descriptor instead. func (*AdminGetRoomSeatConfigResponse) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{19} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{20} } func (x *AdminGetRoomSeatConfigResponse) GetConfig() *AdminRoomSeatConfig { @@ -1767,7 +1861,7 @@ type AdminUpdateRoomSeatConfigRequest struct { func (x *AdminUpdateRoomSeatConfigRequest) Reset() { *x = AdminUpdateRoomSeatConfigRequest{} - mi := &file_proto_room_v1_room_proto_msgTypes[20] + mi := &file_proto_room_v1_room_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1779,7 +1873,7 @@ func (x *AdminUpdateRoomSeatConfigRequest) String() string { func (*AdminUpdateRoomSeatConfigRequest) ProtoMessage() {} func (x *AdminUpdateRoomSeatConfigRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[20] + mi := &file_proto_room_v1_room_proto_msgTypes[21] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1792,7 +1886,7 @@ func (x *AdminUpdateRoomSeatConfigRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use AdminUpdateRoomSeatConfigRequest.ProtoReflect.Descriptor instead. func (*AdminUpdateRoomSeatConfigRequest) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{20} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{21} } func (x *AdminUpdateRoomSeatConfigRequest) GetMeta() *RequestMeta { @@ -1826,7 +1920,7 @@ type AdminUpdateRoomSeatConfigResponse struct { func (x *AdminUpdateRoomSeatConfigResponse) Reset() { *x = AdminUpdateRoomSeatConfigResponse{} - mi := &file_proto_room_v1_room_proto_msgTypes[21] + mi := &file_proto_room_v1_room_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1838,7 +1932,7 @@ func (x *AdminUpdateRoomSeatConfigResponse) String() string { func (*AdminUpdateRoomSeatConfigResponse) ProtoMessage() {} func (x *AdminUpdateRoomSeatConfigResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[21] + mi := &file_proto_room_v1_room_proto_msgTypes[22] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1851,7 +1945,7 @@ func (x *AdminUpdateRoomSeatConfigResponse) ProtoReflect() protoreflect.Message // Deprecated: Use AdminUpdateRoomSeatConfigResponse.ProtoReflect.Descriptor instead. func (*AdminUpdateRoomSeatConfigResponse) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{21} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{22} } func (x *AdminUpdateRoomSeatConfigResponse) GetConfig() *AdminRoomSeatConfig { @@ -1883,7 +1977,7 @@ type AdminRoomPinRoom struct { func (x *AdminRoomPinRoom) Reset() { *x = AdminRoomPinRoom{} - mi := &file_proto_room_v1_room_proto_msgTypes[22] + mi := &file_proto_room_v1_room_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1895,7 +1989,7 @@ func (x *AdminRoomPinRoom) String() string { func (*AdminRoomPinRoom) ProtoMessage() {} func (x *AdminRoomPinRoom) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[22] + mi := &file_proto_room_v1_room_proto_msgTypes[23] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1908,7 +2002,7 @@ func (x *AdminRoomPinRoom) ProtoReflect() protoreflect.Message { // Deprecated: Use AdminRoomPinRoom.ProtoReflect.Descriptor instead. func (*AdminRoomPinRoom) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{22} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{23} } func (x *AdminRoomPinRoom) GetRoomId() string { @@ -1981,7 +2075,7 @@ type AdminRoomPin struct { func (x *AdminRoomPin) Reset() { *x = AdminRoomPin{} - mi := &file_proto_room_v1_room_proto_msgTypes[23] + mi := &file_proto_room_v1_room_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1993,7 +2087,7 @@ func (x *AdminRoomPin) String() string { func (*AdminRoomPin) ProtoMessage() {} func (x *AdminRoomPin) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[23] + mi := &file_proto_room_v1_room_proto_msgTypes[24] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2006,7 +2100,7 @@ func (x *AdminRoomPin) ProtoReflect() protoreflect.Message { // Deprecated: Use AdminRoomPin.ProtoReflect.Descriptor instead. func (*AdminRoomPin) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{23} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{24} } func (x *AdminRoomPin) GetId() int64 { @@ -2114,7 +2208,7 @@ type AdminListRoomPinsRequest struct { func (x *AdminListRoomPinsRequest) Reset() { *x = AdminListRoomPinsRequest{} - mi := &file_proto_room_v1_room_proto_msgTypes[24] + mi := &file_proto_room_v1_room_proto_msgTypes[25] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2126,7 +2220,7 @@ func (x *AdminListRoomPinsRequest) String() string { func (*AdminListRoomPinsRequest) ProtoMessage() {} func (x *AdminListRoomPinsRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[24] + mi := &file_proto_room_v1_room_proto_msgTypes[25] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2139,7 +2233,7 @@ func (x *AdminListRoomPinsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use AdminListRoomPinsRequest.ProtoReflect.Descriptor instead. func (*AdminListRoomPinsRequest) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{24} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{25} } func (x *AdminListRoomPinsRequest) GetMeta() *RequestMeta { @@ -2195,7 +2289,7 @@ type AdminListRoomPinsResponse struct { func (x *AdminListRoomPinsResponse) Reset() { *x = AdminListRoomPinsResponse{} - mi := &file_proto_room_v1_room_proto_msgTypes[25] + mi := &file_proto_room_v1_room_proto_msgTypes[26] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2207,7 +2301,7 @@ func (x *AdminListRoomPinsResponse) String() string { func (*AdminListRoomPinsResponse) ProtoMessage() {} func (x *AdminListRoomPinsResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[25] + mi := &file_proto_room_v1_room_proto_msgTypes[26] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2220,7 +2314,7 @@ func (x *AdminListRoomPinsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use AdminListRoomPinsResponse.ProtoReflect.Descriptor instead. func (*AdminListRoomPinsResponse) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{25} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{26} } func (x *AdminListRoomPinsResponse) GetPins() []*AdminRoomPin { @@ -2257,7 +2351,7 @@ type AdminCreateRoomPinRequest struct { func (x *AdminCreateRoomPinRequest) Reset() { *x = AdminCreateRoomPinRequest{} - mi := &file_proto_room_v1_room_proto_msgTypes[26] + mi := &file_proto_room_v1_room_proto_msgTypes[27] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2269,7 +2363,7 @@ func (x *AdminCreateRoomPinRequest) String() string { func (*AdminCreateRoomPinRequest) ProtoMessage() {} func (x *AdminCreateRoomPinRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[26] + mi := &file_proto_room_v1_room_proto_msgTypes[27] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2282,7 +2376,7 @@ func (x *AdminCreateRoomPinRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use AdminCreateRoomPinRequest.ProtoReflect.Descriptor instead. func (*AdminCreateRoomPinRequest) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{26} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{27} } func (x *AdminCreateRoomPinRequest) GetMeta() *RequestMeta { @@ -2330,7 +2424,7 @@ type AdminCreateRoomPinResponse struct { func (x *AdminCreateRoomPinResponse) Reset() { *x = AdminCreateRoomPinResponse{} - mi := &file_proto_room_v1_room_proto_msgTypes[27] + mi := &file_proto_room_v1_room_proto_msgTypes[28] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2342,7 +2436,7 @@ func (x *AdminCreateRoomPinResponse) String() string { func (*AdminCreateRoomPinResponse) ProtoMessage() {} func (x *AdminCreateRoomPinResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[27] + mi := &file_proto_room_v1_room_proto_msgTypes[28] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2355,7 +2449,7 @@ func (x *AdminCreateRoomPinResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use AdminCreateRoomPinResponse.ProtoReflect.Descriptor instead. func (*AdminCreateRoomPinResponse) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{27} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{28} } func (x *AdminCreateRoomPinResponse) GetPin() *AdminRoomPin { @@ -2383,7 +2477,7 @@ type AdminCancelRoomPinRequest struct { func (x *AdminCancelRoomPinRequest) Reset() { *x = AdminCancelRoomPinRequest{} - mi := &file_proto_room_v1_room_proto_msgTypes[28] + mi := &file_proto_room_v1_room_proto_msgTypes[29] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2395,7 +2489,7 @@ func (x *AdminCancelRoomPinRequest) String() string { func (*AdminCancelRoomPinRequest) ProtoMessage() {} func (x *AdminCancelRoomPinRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[28] + mi := &file_proto_room_v1_room_proto_msgTypes[29] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2408,7 +2502,7 @@ func (x *AdminCancelRoomPinRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use AdminCancelRoomPinRequest.ProtoReflect.Descriptor instead. func (*AdminCancelRoomPinRequest) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{28} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{29} } func (x *AdminCancelRoomPinRequest) GetMeta() *RequestMeta { @@ -2442,7 +2536,7 @@ type AdminCancelRoomPinResponse struct { func (x *AdminCancelRoomPinResponse) Reset() { *x = AdminCancelRoomPinResponse{} - mi := &file_proto_room_v1_room_proto_msgTypes[29] + mi := &file_proto_room_v1_room_proto_msgTypes[30] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2454,7 +2548,7 @@ func (x *AdminCancelRoomPinResponse) String() string { func (*AdminCancelRoomPinResponse) ProtoMessage() {} func (x *AdminCancelRoomPinResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[29] + mi := &file_proto_room_v1_room_proto_msgTypes[30] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2467,7 +2561,7 @@ func (x *AdminCancelRoomPinResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use AdminCancelRoomPinResponse.ProtoReflect.Descriptor instead. func (*AdminCancelRoomPinResponse) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{29} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{30} } func (x *AdminCancelRoomPinResponse) GetPin() *AdminRoomPin { @@ -2514,7 +2608,7 @@ type RoomSnapshot struct { func (x *RoomSnapshot) Reset() { *x = RoomSnapshot{} - mi := &file_proto_room_v1_room_proto_msgTypes[30] + mi := &file_proto_room_v1_room_proto_msgTypes[31] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2526,7 +2620,7 @@ func (x *RoomSnapshot) String() string { func (*RoomSnapshot) ProtoMessage() {} func (x *RoomSnapshot) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[30] + mi := &file_proto_room_v1_room_proto_msgTypes[31] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2539,7 +2633,7 @@ func (x *RoomSnapshot) ProtoReflect() protoreflect.Message { // Deprecated: Use RoomSnapshot.ProtoReflect.Descriptor instead. func (*RoomSnapshot) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{30} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{31} } func (x *RoomSnapshot) GetRoomId() string { @@ -2701,7 +2795,7 @@ type CreateRoomRequest struct { func (x *CreateRoomRequest) Reset() { *x = CreateRoomRequest{} - mi := &file_proto_room_v1_room_proto_msgTypes[31] + mi := &file_proto_room_v1_room_proto_msgTypes[32] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2713,7 +2807,7 @@ func (x *CreateRoomRequest) String() string { func (*CreateRoomRequest) ProtoMessage() {} func (x *CreateRoomRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[31] + mi := &file_proto_room_v1_room_proto_msgTypes[32] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2726,7 +2820,7 @@ func (x *CreateRoomRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateRoomRequest.ProtoReflect.Descriptor instead. func (*CreateRoomRequest) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{31} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{32} } func (x *CreateRoomRequest) GetMeta() *RequestMeta { @@ -2796,7 +2890,7 @@ type CreateRoomResponse struct { func (x *CreateRoomResponse) Reset() { *x = CreateRoomResponse{} - mi := &file_proto_room_v1_room_proto_msgTypes[32] + mi := &file_proto_room_v1_room_proto_msgTypes[33] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2808,7 +2902,7 @@ func (x *CreateRoomResponse) String() string { func (*CreateRoomResponse) ProtoMessage() {} func (x *CreateRoomResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[32] + mi := &file_proto_room_v1_room_proto_msgTypes[33] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2821,7 +2915,7 @@ func (x *CreateRoomResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateRoomResponse.ProtoReflect.Descriptor instead. func (*CreateRoomResponse) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{32} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{33} } func (x *CreateRoomResponse) GetResult() *CommandResult { @@ -2853,7 +2947,7 @@ type UpdateRoomProfileRequest struct { func (x *UpdateRoomProfileRequest) Reset() { *x = UpdateRoomProfileRequest{} - mi := &file_proto_room_v1_room_proto_msgTypes[33] + mi := &file_proto_room_v1_room_proto_msgTypes[34] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2865,7 +2959,7 @@ func (x *UpdateRoomProfileRequest) String() string { func (*UpdateRoomProfileRequest) ProtoMessage() {} func (x *UpdateRoomProfileRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[33] + mi := &file_proto_room_v1_room_proto_msgTypes[34] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2878,7 +2972,7 @@ func (x *UpdateRoomProfileRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateRoomProfileRequest.ProtoReflect.Descriptor instead. func (*UpdateRoomProfileRequest) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{33} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{34} } func (x *UpdateRoomProfileRequest) GetMeta() *RequestMeta { @@ -2927,7 +3021,7 @@ type UpdateRoomProfileResponse struct { func (x *UpdateRoomProfileResponse) Reset() { *x = UpdateRoomProfileResponse{} - mi := &file_proto_room_v1_room_proto_msgTypes[34] + mi := &file_proto_room_v1_room_proto_msgTypes[35] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2939,7 +3033,7 @@ func (x *UpdateRoomProfileResponse) String() string { func (*UpdateRoomProfileResponse) ProtoMessage() {} func (x *UpdateRoomProfileResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[34] + mi := &file_proto_room_v1_room_proto_msgTypes[35] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2952,7 +3046,7 @@ func (x *UpdateRoomProfileResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateRoomProfileResponse.ProtoReflect.Descriptor instead. func (*UpdateRoomProfileResponse) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{34} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{35} } func (x *UpdateRoomProfileResponse) GetResult() *CommandResult { @@ -2982,7 +3076,7 @@ type JoinRoomRequest struct { func (x *JoinRoomRequest) Reset() { *x = JoinRoomRequest{} - mi := &file_proto_room_v1_room_proto_msgTypes[35] + mi := &file_proto_room_v1_room_proto_msgTypes[36] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2994,7 +3088,7 @@ func (x *JoinRoomRequest) String() string { func (*JoinRoomRequest) ProtoMessage() {} func (x *JoinRoomRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[35] + mi := &file_proto_room_v1_room_proto_msgTypes[36] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3007,7 +3101,7 @@ func (x *JoinRoomRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use JoinRoomRequest.ProtoReflect.Descriptor instead. func (*JoinRoomRequest) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{35} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{36} } func (x *JoinRoomRequest) GetMeta() *RequestMeta { @@ -3043,7 +3137,7 @@ type JoinRoomResponse struct { func (x *JoinRoomResponse) Reset() { *x = JoinRoomResponse{} - mi := &file_proto_room_v1_room_proto_msgTypes[36] + mi := &file_proto_room_v1_room_proto_msgTypes[37] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3055,7 +3149,7 @@ func (x *JoinRoomResponse) String() string { func (*JoinRoomResponse) ProtoMessage() {} func (x *JoinRoomResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[36] + mi := &file_proto_room_v1_room_proto_msgTypes[37] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3068,7 +3162,7 @@ func (x *JoinRoomResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use JoinRoomResponse.ProtoReflect.Descriptor instead. func (*JoinRoomResponse) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{36} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{37} } func (x *JoinRoomResponse) GetResult() *CommandResult { @@ -3103,7 +3197,7 @@ type RoomHeartbeatRequest struct { func (x *RoomHeartbeatRequest) Reset() { *x = RoomHeartbeatRequest{} - mi := &file_proto_room_v1_room_proto_msgTypes[37] + mi := &file_proto_room_v1_room_proto_msgTypes[38] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3115,7 +3209,7 @@ func (x *RoomHeartbeatRequest) String() string { func (*RoomHeartbeatRequest) ProtoMessage() {} func (x *RoomHeartbeatRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[37] + mi := &file_proto_room_v1_room_proto_msgTypes[38] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3128,7 +3222,7 @@ func (x *RoomHeartbeatRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RoomHeartbeatRequest.ProtoReflect.Descriptor instead. func (*RoomHeartbeatRequest) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{37} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{38} } func (x *RoomHeartbeatRequest) GetMeta() *RequestMeta { @@ -3150,7 +3244,7 @@ type RoomHeartbeatResponse struct { func (x *RoomHeartbeatResponse) Reset() { *x = RoomHeartbeatResponse{} - mi := &file_proto_room_v1_room_proto_msgTypes[38] + mi := &file_proto_room_v1_room_proto_msgTypes[39] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3162,7 +3256,7 @@ func (x *RoomHeartbeatResponse) String() string { func (*RoomHeartbeatResponse) ProtoMessage() {} func (x *RoomHeartbeatResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[38] + mi := &file_proto_room_v1_room_proto_msgTypes[39] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3175,7 +3269,7 @@ func (x *RoomHeartbeatResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RoomHeartbeatResponse.ProtoReflect.Descriptor instead. func (*RoomHeartbeatResponse) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{38} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{39} } func (x *RoomHeartbeatResponse) GetResult() *CommandResult { @@ -3209,7 +3303,7 @@ type LeaveRoomRequest struct { func (x *LeaveRoomRequest) Reset() { *x = LeaveRoomRequest{} - mi := &file_proto_room_v1_room_proto_msgTypes[39] + mi := &file_proto_room_v1_room_proto_msgTypes[40] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3221,7 +3315,7 @@ func (x *LeaveRoomRequest) String() string { func (*LeaveRoomRequest) ProtoMessage() {} func (x *LeaveRoomRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[39] + mi := &file_proto_room_v1_room_proto_msgTypes[40] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3234,7 +3328,7 @@ func (x *LeaveRoomRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use LeaveRoomRequest.ProtoReflect.Descriptor instead. func (*LeaveRoomRequest) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{39} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{40} } func (x *LeaveRoomRequest) GetMeta() *RequestMeta { @@ -3255,7 +3349,7 @@ type LeaveRoomResponse struct { func (x *LeaveRoomResponse) Reset() { *x = LeaveRoomResponse{} - mi := &file_proto_room_v1_room_proto_msgTypes[40] + mi := &file_proto_room_v1_room_proto_msgTypes[41] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3267,7 +3361,7 @@ func (x *LeaveRoomResponse) String() string { func (*LeaveRoomResponse) ProtoMessage() {} func (x *LeaveRoomResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[40] + mi := &file_proto_room_v1_room_proto_msgTypes[41] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3280,7 +3374,7 @@ func (x *LeaveRoomResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use LeaveRoomResponse.ProtoReflect.Descriptor instead. func (*LeaveRoomResponse) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{40} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{41} } func (x *LeaveRoomResponse) GetResult() *CommandResult { @@ -3308,7 +3402,7 @@ type CloseRoomRequest struct { func (x *CloseRoomRequest) Reset() { *x = CloseRoomRequest{} - mi := &file_proto_room_v1_room_proto_msgTypes[41] + mi := &file_proto_room_v1_room_proto_msgTypes[42] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3320,7 +3414,7 @@ func (x *CloseRoomRequest) String() string { func (*CloseRoomRequest) ProtoMessage() {} func (x *CloseRoomRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[41] + mi := &file_proto_room_v1_room_proto_msgTypes[42] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3333,7 +3427,7 @@ func (x *CloseRoomRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CloseRoomRequest.ProtoReflect.Descriptor instead. func (*CloseRoomRequest) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{41} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{42} } func (x *CloseRoomRequest) GetMeta() *RequestMeta { @@ -3361,7 +3455,7 @@ type CloseRoomResponse struct { func (x *CloseRoomResponse) Reset() { *x = CloseRoomResponse{} - mi := &file_proto_room_v1_room_proto_msgTypes[42] + mi := &file_proto_room_v1_room_proto_msgTypes[43] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3373,7 +3467,7 @@ func (x *CloseRoomResponse) String() string { func (*CloseRoomResponse) ProtoMessage() {} func (x *CloseRoomResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[42] + mi := &file_proto_room_v1_room_proto_msgTypes[43] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3386,7 +3480,7 @@ func (x *CloseRoomResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use CloseRoomResponse.ProtoReflect.Descriptor instead. func (*CloseRoomResponse) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{42} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{43} } func (x *CloseRoomResponse) GetResult() *CommandResult { @@ -3431,7 +3525,7 @@ type AdminRoomListItem struct { func (x *AdminRoomListItem) Reset() { *x = AdminRoomListItem{} - mi := &file_proto_room_v1_room_proto_msgTypes[43] + mi := &file_proto_room_v1_room_proto_msgTypes[44] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3443,7 +3537,7 @@ func (x *AdminRoomListItem) String() string { func (*AdminRoomListItem) ProtoMessage() {} func (x *AdminRoomListItem) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[43] + mi := &file_proto_room_v1_room_proto_msgTypes[44] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3456,7 +3550,7 @@ func (x *AdminRoomListItem) ProtoReflect() protoreflect.Message { // Deprecated: Use AdminRoomListItem.ProtoReflect.Descriptor instead. func (*AdminRoomListItem) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{43} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{44} } func (x *AdminRoomListItem) GetRoomId() string { @@ -3608,7 +3702,7 @@ type AdminListRoomsRequest struct { func (x *AdminListRoomsRequest) Reset() { *x = AdminListRoomsRequest{} - mi := &file_proto_room_v1_room_proto_msgTypes[44] + mi := &file_proto_room_v1_room_proto_msgTypes[45] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3620,7 +3714,7 @@ func (x *AdminListRoomsRequest) String() string { func (*AdminListRoomsRequest) ProtoMessage() {} func (x *AdminListRoomsRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[44] + mi := &file_proto_room_v1_room_proto_msgTypes[45] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3633,7 +3727,7 @@ func (x *AdminListRoomsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use AdminListRoomsRequest.ProtoReflect.Descriptor instead. func (*AdminListRoomsRequest) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{44} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{45} } func (x *AdminListRoomsRequest) GetMeta() *RequestMeta { @@ -3703,7 +3797,7 @@ type AdminListRoomsResponse struct { func (x *AdminListRoomsResponse) Reset() { *x = AdminListRoomsResponse{} - mi := &file_proto_room_v1_room_proto_msgTypes[45] + mi := &file_proto_room_v1_room_proto_msgTypes[46] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3715,7 +3809,7 @@ func (x *AdminListRoomsResponse) String() string { func (*AdminListRoomsResponse) ProtoMessage() {} func (x *AdminListRoomsResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[45] + mi := &file_proto_room_v1_room_proto_msgTypes[46] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3728,7 +3822,7 @@ func (x *AdminListRoomsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use AdminListRoomsResponse.ProtoReflect.Descriptor instead. func (*AdminListRoomsResponse) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{45} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{46} } func (x *AdminListRoomsResponse) GetRooms() []*AdminRoomListItem { @@ -3762,7 +3856,7 @@ type AdminGetRoomRequest struct { func (x *AdminGetRoomRequest) Reset() { *x = AdminGetRoomRequest{} - mi := &file_proto_room_v1_room_proto_msgTypes[46] + mi := &file_proto_room_v1_room_proto_msgTypes[47] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3774,7 +3868,7 @@ func (x *AdminGetRoomRequest) String() string { func (*AdminGetRoomRequest) ProtoMessage() {} func (x *AdminGetRoomRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[46] + mi := &file_proto_room_v1_room_proto_msgTypes[47] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3787,7 +3881,7 @@ func (x *AdminGetRoomRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use AdminGetRoomRequest.ProtoReflect.Descriptor instead. func (*AdminGetRoomRequest) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{46} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{47} } func (x *AdminGetRoomRequest) GetMeta() *RequestMeta { @@ -3814,7 +3908,7 @@ type AdminGetRoomResponse struct { func (x *AdminGetRoomResponse) Reset() { *x = AdminGetRoomResponse{} - mi := &file_proto_room_v1_room_proto_msgTypes[47] + mi := &file_proto_room_v1_room_proto_msgTypes[48] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3826,7 +3920,7 @@ func (x *AdminGetRoomResponse) String() string { func (*AdminGetRoomResponse) ProtoMessage() {} func (x *AdminGetRoomResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[47] + mi := &file_proto_room_v1_room_proto_msgTypes[48] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3839,7 +3933,7 @@ func (x *AdminGetRoomResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use AdminGetRoomResponse.ProtoReflect.Descriptor instead. func (*AdminGetRoomResponse) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{47} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{48} } func (x *AdminGetRoomResponse) GetRoom() *AdminRoomListItem { @@ -3874,7 +3968,7 @@ type AdminUpdateRoomRequest struct { func (x *AdminUpdateRoomRequest) Reset() { *x = AdminUpdateRoomRequest{} - mi := &file_proto_room_v1_room_proto_msgTypes[48] + mi := &file_proto_room_v1_room_proto_msgTypes[49] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3886,7 +3980,7 @@ func (x *AdminUpdateRoomRequest) String() string { func (*AdminUpdateRoomRequest) ProtoMessage() {} func (x *AdminUpdateRoomRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[48] + mi := &file_proto_room_v1_room_proto_msgTypes[49] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3899,7 +3993,7 @@ func (x *AdminUpdateRoomRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use AdminUpdateRoomRequest.ProtoReflect.Descriptor instead. func (*AdminUpdateRoomRequest) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{48} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{49} } func (x *AdminUpdateRoomRequest) GetMeta() *RequestMeta { @@ -3982,7 +4076,7 @@ type AdminUpdateRoomResponse struct { func (x *AdminUpdateRoomResponse) Reset() { *x = AdminUpdateRoomResponse{} - mi := &file_proto_room_v1_room_proto_msgTypes[49] + mi := &file_proto_room_v1_room_proto_msgTypes[50] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3994,7 +4088,7 @@ func (x *AdminUpdateRoomResponse) String() string { func (*AdminUpdateRoomResponse) ProtoMessage() {} func (x *AdminUpdateRoomResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[49] + mi := &file_proto_room_v1_room_proto_msgTypes[50] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4007,7 +4101,7 @@ func (x *AdminUpdateRoomResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use AdminUpdateRoomResponse.ProtoReflect.Descriptor instead. func (*AdminUpdateRoomResponse) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{49} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{50} } func (x *AdminUpdateRoomResponse) GetResult() *CommandResult { @@ -4035,7 +4129,7 @@ type AdminDeleteRoomRequest struct { func (x *AdminDeleteRoomRequest) Reset() { *x = AdminDeleteRoomRequest{} - mi := &file_proto_room_v1_room_proto_msgTypes[50] + mi := &file_proto_room_v1_room_proto_msgTypes[51] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4047,7 +4141,7 @@ func (x *AdminDeleteRoomRequest) String() string { func (*AdminDeleteRoomRequest) ProtoMessage() {} func (x *AdminDeleteRoomRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[50] + mi := &file_proto_room_v1_room_proto_msgTypes[51] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4060,7 +4154,7 @@ func (x *AdminDeleteRoomRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use AdminDeleteRoomRequest.ProtoReflect.Descriptor instead. func (*AdminDeleteRoomRequest) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{50} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{51} } func (x *AdminDeleteRoomRequest) GetMeta() *RequestMeta { @@ -4094,7 +4188,7 @@ type AdminDeleteRoomResponse struct { func (x *AdminDeleteRoomResponse) Reset() { *x = AdminDeleteRoomResponse{} - mi := &file_proto_room_v1_room_proto_msgTypes[51] + mi := &file_proto_room_v1_room_proto_msgTypes[52] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4106,7 +4200,7 @@ func (x *AdminDeleteRoomResponse) String() string { func (*AdminDeleteRoomResponse) ProtoMessage() {} func (x *AdminDeleteRoomResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[51] + mi := &file_proto_room_v1_room_proto_msgTypes[52] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4119,7 +4213,7 @@ func (x *AdminDeleteRoomResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use AdminDeleteRoomResponse.ProtoReflect.Descriptor instead. func (*AdminDeleteRoomResponse) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{51} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{52} } func (x *AdminDeleteRoomResponse) GetResult() *CommandResult { @@ -4147,7 +4241,7 @@ type MicUpRequest struct { func (x *MicUpRequest) Reset() { *x = MicUpRequest{} - mi := &file_proto_room_v1_room_proto_msgTypes[52] + mi := &file_proto_room_v1_room_proto_msgTypes[53] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4159,7 +4253,7 @@ func (x *MicUpRequest) String() string { func (*MicUpRequest) ProtoMessage() {} func (x *MicUpRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[52] + mi := &file_proto_room_v1_room_proto_msgTypes[53] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4172,7 +4266,7 @@ func (x *MicUpRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use MicUpRequest.ProtoReflect.Descriptor instead. func (*MicUpRequest) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{52} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{53} } func (x *MicUpRequest) GetMeta() *RequestMeta { @@ -4203,7 +4297,7 @@ type MicUpResponse struct { func (x *MicUpResponse) Reset() { *x = MicUpResponse{} - mi := &file_proto_room_v1_room_proto_msgTypes[53] + mi := &file_proto_room_v1_room_proto_msgTypes[54] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4215,7 +4309,7 @@ func (x *MicUpResponse) String() string { func (*MicUpResponse) ProtoMessage() {} func (x *MicUpResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[53] + mi := &file_proto_room_v1_room_proto_msgTypes[54] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4228,7 +4322,7 @@ func (x *MicUpResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use MicUpResponse.ProtoReflect.Descriptor instead. func (*MicUpResponse) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{53} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{54} } func (x *MicUpResponse) GetResult() *CommandResult { @@ -4279,7 +4373,7 @@ type MicDownRequest struct { func (x *MicDownRequest) Reset() { *x = MicDownRequest{} - mi := &file_proto_room_v1_room_proto_msgTypes[54] + mi := &file_proto_room_v1_room_proto_msgTypes[55] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4291,7 +4385,7 @@ func (x *MicDownRequest) String() string { func (*MicDownRequest) ProtoMessage() {} func (x *MicDownRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[54] + mi := &file_proto_room_v1_room_proto_msgTypes[55] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4304,7 +4398,7 @@ func (x *MicDownRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use MicDownRequest.ProtoReflect.Descriptor instead. func (*MicDownRequest) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{54} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{55} } func (x *MicDownRequest) GetMeta() *RequestMeta { @@ -4340,7 +4434,7 @@ type MicDownResponse struct { func (x *MicDownResponse) Reset() { *x = MicDownResponse{} - mi := &file_proto_room_v1_room_proto_msgTypes[55] + mi := &file_proto_room_v1_room_proto_msgTypes[56] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4352,7 +4446,7 @@ func (x *MicDownResponse) String() string { func (*MicDownResponse) ProtoMessage() {} func (x *MicDownResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[55] + mi := &file_proto_room_v1_room_proto_msgTypes[56] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4365,7 +4459,7 @@ func (x *MicDownResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use MicDownResponse.ProtoReflect.Descriptor instead. func (*MicDownResponse) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{55} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{56} } func (x *MicDownResponse) GetResult() *CommandResult { @@ -4401,7 +4495,7 @@ type ChangeMicSeatRequest struct { func (x *ChangeMicSeatRequest) Reset() { *x = ChangeMicSeatRequest{} - mi := &file_proto_room_v1_room_proto_msgTypes[56] + mi := &file_proto_room_v1_room_proto_msgTypes[57] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4413,7 +4507,7 @@ func (x *ChangeMicSeatRequest) String() string { func (*ChangeMicSeatRequest) ProtoMessage() {} func (x *ChangeMicSeatRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[56] + mi := &file_proto_room_v1_room_proto_msgTypes[57] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4426,7 +4520,7 @@ func (x *ChangeMicSeatRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ChangeMicSeatRequest.ProtoReflect.Descriptor instead. func (*ChangeMicSeatRequest) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{56} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{57} } func (x *ChangeMicSeatRequest) GetMeta() *RequestMeta { @@ -4461,7 +4555,7 @@ type ChangeMicSeatResponse struct { func (x *ChangeMicSeatResponse) Reset() { *x = ChangeMicSeatResponse{} - mi := &file_proto_room_v1_room_proto_msgTypes[57] + mi := &file_proto_room_v1_room_proto_msgTypes[58] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4473,7 +4567,7 @@ func (x *ChangeMicSeatResponse) String() string { func (*ChangeMicSeatResponse) ProtoMessage() {} func (x *ChangeMicSeatResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[57] + mi := &file_proto_room_v1_room_proto_msgTypes[58] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4486,7 +4580,7 @@ func (x *ChangeMicSeatResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ChangeMicSeatResponse.ProtoReflect.Descriptor instead. func (*ChangeMicSeatResponse) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{57} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{58} } func (x *ChangeMicSeatResponse) GetResult() *CommandResult { @@ -4520,7 +4614,7 @@ type ConfirmMicPublishingRequest struct { func (x *ConfirmMicPublishingRequest) Reset() { *x = ConfirmMicPublishingRequest{} - mi := &file_proto_room_v1_room_proto_msgTypes[58] + mi := &file_proto_room_v1_room_proto_msgTypes[59] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4532,7 +4626,7 @@ func (x *ConfirmMicPublishingRequest) String() string { func (*ConfirmMicPublishingRequest) ProtoMessage() {} func (x *ConfirmMicPublishingRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[58] + mi := &file_proto_room_v1_room_proto_msgTypes[59] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4545,7 +4639,7 @@ func (x *ConfirmMicPublishingRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ConfirmMicPublishingRequest.ProtoReflect.Descriptor instead. func (*ConfirmMicPublishingRequest) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{58} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{59} } func (x *ConfirmMicPublishingRequest) GetMeta() *RequestMeta { @@ -4602,7 +4696,7 @@ type ConfirmMicPublishingResponse struct { func (x *ConfirmMicPublishingResponse) Reset() { *x = ConfirmMicPublishingResponse{} - mi := &file_proto_room_v1_room_proto_msgTypes[59] + mi := &file_proto_room_v1_room_proto_msgTypes[60] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4614,7 +4708,7 @@ func (x *ConfirmMicPublishingResponse) String() string { func (*ConfirmMicPublishingResponse) ProtoMessage() {} func (x *ConfirmMicPublishingResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[59] + mi := &file_proto_room_v1_room_proto_msgTypes[60] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4627,7 +4721,7 @@ func (x *ConfirmMicPublishingResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ConfirmMicPublishingResponse.ProtoReflect.Descriptor instead. func (*ConfirmMicPublishingResponse) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{59} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{60} } func (x *ConfirmMicPublishingResponse) GetResult() *CommandResult { @@ -4664,7 +4758,7 @@ type SetMicMuteRequest struct { func (x *SetMicMuteRequest) Reset() { *x = SetMicMuteRequest{} - mi := &file_proto_room_v1_room_proto_msgTypes[60] + mi := &file_proto_room_v1_room_proto_msgTypes[61] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4676,7 +4770,7 @@ func (x *SetMicMuteRequest) String() string { func (*SetMicMuteRequest) ProtoMessage() {} func (x *SetMicMuteRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[60] + mi := &file_proto_room_v1_room_proto_msgTypes[61] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4689,7 +4783,7 @@ func (x *SetMicMuteRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SetMicMuteRequest.ProtoReflect.Descriptor instead. func (*SetMicMuteRequest) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{60} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{61} } func (x *SetMicMuteRequest) GetMeta() *RequestMeta { @@ -4725,7 +4819,7 @@ type SetMicMuteResponse struct { func (x *SetMicMuteResponse) Reset() { *x = SetMicMuteResponse{} - mi := &file_proto_room_v1_room_proto_msgTypes[61] + mi := &file_proto_room_v1_room_proto_msgTypes[62] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4737,7 +4831,7 @@ func (x *SetMicMuteResponse) String() string { func (*SetMicMuteResponse) ProtoMessage() {} func (x *SetMicMuteResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[61] + mi := &file_proto_room_v1_room_proto_msgTypes[62] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4750,7 +4844,7 @@ func (x *SetMicMuteResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use SetMicMuteResponse.ProtoReflect.Descriptor instead. func (*SetMicMuteResponse) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{61} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{62} } func (x *SetMicMuteResponse) GetResult() *CommandResult { @@ -4791,7 +4885,7 @@ type ApplyRTCEventRequest struct { func (x *ApplyRTCEventRequest) Reset() { *x = ApplyRTCEventRequest{} - mi := &file_proto_room_v1_room_proto_msgTypes[62] + mi := &file_proto_room_v1_room_proto_msgTypes[63] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4803,7 +4897,7 @@ func (x *ApplyRTCEventRequest) String() string { func (*ApplyRTCEventRequest) ProtoMessage() {} func (x *ApplyRTCEventRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[62] + mi := &file_proto_room_v1_room_proto_msgTypes[63] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4816,7 +4910,7 @@ func (x *ApplyRTCEventRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ApplyRTCEventRequest.ProtoReflect.Descriptor instead. func (*ApplyRTCEventRequest) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{62} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{63} } func (x *ApplyRTCEventRequest) GetMeta() *RequestMeta { @@ -4880,7 +4974,7 @@ type ApplyRTCEventResponse struct { func (x *ApplyRTCEventResponse) Reset() { *x = ApplyRTCEventResponse{} - mi := &file_proto_room_v1_room_proto_msgTypes[63] + mi := &file_proto_room_v1_room_proto_msgTypes[64] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4892,7 +4986,7 @@ func (x *ApplyRTCEventResponse) String() string { func (*ApplyRTCEventResponse) ProtoMessage() {} func (x *ApplyRTCEventResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[63] + mi := &file_proto_room_v1_room_proto_msgTypes[64] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4905,7 +4999,7 @@ func (x *ApplyRTCEventResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ApplyRTCEventResponse.ProtoReflect.Descriptor instead. func (*ApplyRTCEventResponse) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{63} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{64} } func (x *ApplyRTCEventResponse) GetResult() *CommandResult { @@ -4941,7 +5035,7 @@ type SetMicSeatLockRequest struct { func (x *SetMicSeatLockRequest) Reset() { *x = SetMicSeatLockRequest{} - mi := &file_proto_room_v1_room_proto_msgTypes[64] + mi := &file_proto_room_v1_room_proto_msgTypes[65] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4953,7 +5047,7 @@ func (x *SetMicSeatLockRequest) String() string { func (*SetMicSeatLockRequest) ProtoMessage() {} func (x *SetMicSeatLockRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[64] + mi := &file_proto_room_v1_room_proto_msgTypes[65] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4966,7 +5060,7 @@ func (x *SetMicSeatLockRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SetMicSeatLockRequest.ProtoReflect.Descriptor instead. func (*SetMicSeatLockRequest) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{64} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{65} } func (x *SetMicSeatLockRequest) GetMeta() *RequestMeta { @@ -5001,7 +5095,7 @@ type SetMicSeatLockResponse struct { func (x *SetMicSeatLockResponse) Reset() { *x = SetMicSeatLockResponse{} - mi := &file_proto_room_v1_room_proto_msgTypes[65] + mi := &file_proto_room_v1_room_proto_msgTypes[66] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5013,7 +5107,7 @@ func (x *SetMicSeatLockResponse) String() string { func (*SetMicSeatLockResponse) ProtoMessage() {} func (x *SetMicSeatLockResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[65] + mi := &file_proto_room_v1_room_proto_msgTypes[66] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5026,7 +5120,7 @@ func (x *SetMicSeatLockResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use SetMicSeatLockResponse.ProtoReflect.Descriptor instead. func (*SetMicSeatLockResponse) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{65} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{66} } func (x *SetMicSeatLockResponse) GetResult() *CommandResult { @@ -5054,7 +5148,7 @@ type SetChatEnabledRequest struct { func (x *SetChatEnabledRequest) Reset() { *x = SetChatEnabledRequest{} - mi := &file_proto_room_v1_room_proto_msgTypes[66] + mi := &file_proto_room_v1_room_proto_msgTypes[67] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5066,7 +5160,7 @@ func (x *SetChatEnabledRequest) String() string { func (*SetChatEnabledRequest) ProtoMessage() {} func (x *SetChatEnabledRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[66] + mi := &file_proto_room_v1_room_proto_msgTypes[67] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5079,7 +5173,7 @@ func (x *SetChatEnabledRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SetChatEnabledRequest.ProtoReflect.Descriptor instead. func (*SetChatEnabledRequest) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{66} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{67} } func (x *SetChatEnabledRequest) GetMeta() *RequestMeta { @@ -5107,7 +5201,7 @@ type SetChatEnabledResponse struct { func (x *SetChatEnabledResponse) Reset() { *x = SetChatEnabledResponse{} - mi := &file_proto_room_v1_room_proto_msgTypes[67] + mi := &file_proto_room_v1_room_proto_msgTypes[68] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5119,7 +5213,7 @@ func (x *SetChatEnabledResponse) String() string { func (*SetChatEnabledResponse) ProtoMessage() {} func (x *SetChatEnabledResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[67] + mi := &file_proto_room_v1_room_proto_msgTypes[68] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5132,7 +5226,7 @@ func (x *SetChatEnabledResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use SetChatEnabledResponse.ProtoReflect.Descriptor instead. func (*SetChatEnabledResponse) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{67} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{68} } func (x *SetChatEnabledResponse) GetResult() *CommandResult { @@ -5162,7 +5256,7 @@ type SetRoomPasswordRequest struct { func (x *SetRoomPasswordRequest) Reset() { *x = SetRoomPasswordRequest{} - mi := &file_proto_room_v1_room_proto_msgTypes[68] + mi := &file_proto_room_v1_room_proto_msgTypes[69] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5174,7 +5268,7 @@ func (x *SetRoomPasswordRequest) String() string { func (*SetRoomPasswordRequest) ProtoMessage() {} func (x *SetRoomPasswordRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[68] + mi := &file_proto_room_v1_room_proto_msgTypes[69] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5187,7 +5281,7 @@ func (x *SetRoomPasswordRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SetRoomPasswordRequest.ProtoReflect.Descriptor instead. func (*SetRoomPasswordRequest) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{68} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{69} } func (x *SetRoomPasswordRequest) GetMeta() *RequestMeta { @@ -5222,7 +5316,7 @@ type SetRoomPasswordResponse struct { func (x *SetRoomPasswordResponse) Reset() { *x = SetRoomPasswordResponse{} - mi := &file_proto_room_v1_room_proto_msgTypes[69] + mi := &file_proto_room_v1_room_proto_msgTypes[70] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5234,7 +5328,7 @@ func (x *SetRoomPasswordResponse) String() string { func (*SetRoomPasswordResponse) ProtoMessage() {} func (x *SetRoomPasswordResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[69] + mi := &file_proto_room_v1_room_proto_msgTypes[70] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5247,7 +5341,7 @@ func (x *SetRoomPasswordResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use SetRoomPasswordResponse.ProtoReflect.Descriptor instead. func (*SetRoomPasswordResponse) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{69} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{70} } func (x *SetRoomPasswordResponse) GetResult() *CommandResult { @@ -5276,7 +5370,7 @@ type SetRoomAdminRequest struct { func (x *SetRoomAdminRequest) Reset() { *x = SetRoomAdminRequest{} - mi := &file_proto_room_v1_room_proto_msgTypes[70] + mi := &file_proto_room_v1_room_proto_msgTypes[71] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5288,7 +5382,7 @@ func (x *SetRoomAdminRequest) String() string { func (*SetRoomAdminRequest) ProtoMessage() {} func (x *SetRoomAdminRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[70] + mi := &file_proto_room_v1_room_proto_msgTypes[71] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5301,7 +5395,7 @@ func (x *SetRoomAdminRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SetRoomAdminRequest.ProtoReflect.Descriptor instead. func (*SetRoomAdminRequest) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{70} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{71} } func (x *SetRoomAdminRequest) GetMeta() *RequestMeta { @@ -5336,7 +5430,7 @@ type SetRoomAdminResponse struct { func (x *SetRoomAdminResponse) Reset() { *x = SetRoomAdminResponse{} - mi := &file_proto_room_v1_room_proto_msgTypes[71] + mi := &file_proto_room_v1_room_proto_msgTypes[72] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5348,7 +5442,7 @@ func (x *SetRoomAdminResponse) String() string { func (*SetRoomAdminResponse) ProtoMessage() {} func (x *SetRoomAdminResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[71] + mi := &file_proto_room_v1_room_proto_msgTypes[72] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5361,7 +5455,7 @@ func (x *SetRoomAdminResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use SetRoomAdminResponse.ProtoReflect.Descriptor instead. func (*SetRoomAdminResponse) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{71} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{72} } func (x *SetRoomAdminResponse) GetResult() *CommandResult { @@ -5390,7 +5484,7 @@ type MuteUserRequest struct { func (x *MuteUserRequest) Reset() { *x = MuteUserRequest{} - mi := &file_proto_room_v1_room_proto_msgTypes[72] + mi := &file_proto_room_v1_room_proto_msgTypes[73] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5402,7 +5496,7 @@ func (x *MuteUserRequest) String() string { func (*MuteUserRequest) ProtoMessage() {} func (x *MuteUserRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[72] + mi := &file_proto_room_v1_room_proto_msgTypes[73] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5415,7 +5509,7 @@ func (x *MuteUserRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use MuteUserRequest.ProtoReflect.Descriptor instead. func (*MuteUserRequest) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{72} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{73} } func (x *MuteUserRequest) GetMeta() *RequestMeta { @@ -5450,7 +5544,7 @@ type MuteUserResponse struct { func (x *MuteUserResponse) Reset() { *x = MuteUserResponse{} - mi := &file_proto_room_v1_room_proto_msgTypes[73] + mi := &file_proto_room_v1_room_proto_msgTypes[74] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5462,7 +5556,7 @@ func (x *MuteUserResponse) String() string { func (*MuteUserResponse) ProtoMessage() {} func (x *MuteUserResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[73] + mi := &file_proto_room_v1_room_proto_msgTypes[74] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5475,7 +5569,7 @@ func (x *MuteUserResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use MuteUserResponse.ProtoReflect.Descriptor instead. func (*MuteUserResponse) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{73} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{74} } func (x *MuteUserResponse) GetResult() *CommandResult { @@ -5503,7 +5597,7 @@ type KickUserRequest struct { func (x *KickUserRequest) Reset() { *x = KickUserRequest{} - mi := &file_proto_room_v1_room_proto_msgTypes[74] + mi := &file_proto_room_v1_room_proto_msgTypes[75] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5515,7 +5609,7 @@ func (x *KickUserRequest) String() string { func (*KickUserRequest) ProtoMessage() {} func (x *KickUserRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[74] + mi := &file_proto_room_v1_room_proto_msgTypes[75] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5528,7 +5622,7 @@ func (x *KickUserRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use KickUserRequest.ProtoReflect.Descriptor instead. func (*KickUserRequest) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{74} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{75} } func (x *KickUserRequest) GetMeta() *RequestMeta { @@ -5560,7 +5654,7 @@ type KickUserResponse struct { func (x *KickUserResponse) Reset() { *x = KickUserResponse{} - mi := &file_proto_room_v1_room_proto_msgTypes[75] + mi := &file_proto_room_v1_room_proto_msgTypes[76] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5572,7 +5666,7 @@ func (x *KickUserResponse) String() string { func (*KickUserResponse) ProtoMessage() {} func (x *KickUserResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[75] + mi := &file_proto_room_v1_room_proto_msgTypes[76] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5585,7 +5679,7 @@ func (x *KickUserResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use KickUserResponse.ProtoReflect.Descriptor instead. func (*KickUserResponse) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{75} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{76} } func (x *KickUserResponse) GetResult() *CommandResult { @@ -5627,7 +5721,7 @@ type UnbanUserRequest struct { func (x *UnbanUserRequest) Reset() { *x = UnbanUserRequest{} - mi := &file_proto_room_v1_room_proto_msgTypes[76] + mi := &file_proto_room_v1_room_proto_msgTypes[77] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5639,7 +5733,7 @@ func (x *UnbanUserRequest) String() string { func (*UnbanUserRequest) ProtoMessage() {} func (x *UnbanUserRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[76] + mi := &file_proto_room_v1_room_proto_msgTypes[77] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5652,7 +5746,7 @@ func (x *UnbanUserRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UnbanUserRequest.ProtoReflect.Descriptor instead. func (*UnbanUserRequest) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{76} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{77} } func (x *UnbanUserRequest) GetMeta() *RequestMeta { @@ -5680,7 +5774,7 @@ type UnbanUserResponse struct { func (x *UnbanUserResponse) Reset() { *x = UnbanUserResponse{} - mi := &file_proto_room_v1_room_proto_msgTypes[77] + mi := &file_proto_room_v1_room_proto_msgTypes[78] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5692,7 +5786,7 @@ func (x *UnbanUserResponse) String() string { func (*UnbanUserResponse) ProtoMessage() {} func (x *UnbanUserResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[77] + mi := &file_proto_room_v1_room_proto_msgTypes[78] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5705,7 +5799,7 @@ func (x *UnbanUserResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use UnbanUserResponse.ProtoReflect.Descriptor instead. func (*UnbanUserResponse) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{77} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{78} } func (x *UnbanUserResponse) GetResult() *CommandResult { @@ -5738,7 +5832,7 @@ type SystemEvictUserRequest struct { func (x *SystemEvictUserRequest) Reset() { *x = SystemEvictUserRequest{} - mi := &file_proto_room_v1_room_proto_msgTypes[78] + mi := &file_proto_room_v1_room_proto_msgTypes[79] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5750,7 +5844,7 @@ func (x *SystemEvictUserRequest) String() string { func (*SystemEvictUserRequest) ProtoMessage() {} func (x *SystemEvictUserRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[78] + mi := &file_proto_room_v1_room_proto_msgTypes[79] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5763,7 +5857,7 @@ func (x *SystemEvictUserRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SystemEvictUserRequest.ProtoReflect.Descriptor instead. func (*SystemEvictUserRequest) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{78} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{79} } func (x *SystemEvictUserRequest) GetMeta() *RequestMeta { @@ -5816,7 +5910,7 @@ type SystemEvictUserResponse struct { func (x *SystemEvictUserResponse) Reset() { *x = SystemEvictUserResponse{} - mi := &file_proto_room_v1_room_proto_msgTypes[79] + mi := &file_proto_room_v1_room_proto_msgTypes[80] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5828,7 +5922,7 @@ func (x *SystemEvictUserResponse) String() string { func (*SystemEvictUserResponse) ProtoMessage() {} func (x *SystemEvictUserResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[79] + mi := &file_proto_room_v1_room_proto_msgTypes[80] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5841,7 +5935,7 @@ func (x *SystemEvictUserResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use SystemEvictUserResponse.ProtoReflect.Descriptor instead. func (*SystemEvictUserResponse) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{79} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{80} } func (x *SystemEvictUserResponse) GetHadCurrentRoom() bool { @@ -5904,7 +5998,7 @@ type SendGiftRequest struct { func (x *SendGiftRequest) Reset() { *x = SendGiftRequest{} - mi := &file_proto_room_v1_room_proto_msgTypes[80] + mi := &file_proto_room_v1_room_proto_msgTypes[81] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5916,7 +6010,7 @@ func (x *SendGiftRequest) String() string { func (*SendGiftRequest) ProtoMessage() {} func (x *SendGiftRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[80] + mi := &file_proto_room_v1_room_proto_msgTypes[81] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5929,7 +6023,7 @@ func (x *SendGiftRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SendGiftRequest.ProtoReflect.Descriptor instead. func (*SendGiftRequest) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{80} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{81} } func (x *SendGiftRequest) GetMeta() *RequestMeta { @@ -5997,7 +6091,7 @@ type SendGiftResponse struct { func (x *SendGiftResponse) Reset() { *x = SendGiftResponse{} - mi := &file_proto_room_v1_room_proto_msgTypes[81] + mi := &file_proto_room_v1_room_proto_msgTypes[82] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6009,7 +6103,7 @@ func (x *SendGiftResponse) String() string { func (*SendGiftResponse) ProtoMessage() {} func (x *SendGiftResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[81] + mi := &file_proto_room_v1_room_proto_msgTypes[82] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6022,7 +6116,7 @@ func (x *SendGiftResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use SendGiftResponse.ProtoReflect.Descriptor instead. func (*SendGiftResponse) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{81} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{82} } func (x *SendGiftResponse) GetResult() *CommandResult { @@ -6086,7 +6180,7 @@ type CheckSpeakPermissionRequest struct { func (x *CheckSpeakPermissionRequest) Reset() { *x = CheckSpeakPermissionRequest{} - mi := &file_proto_room_v1_room_proto_msgTypes[82] + mi := &file_proto_room_v1_room_proto_msgTypes[83] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6098,7 +6192,7 @@ func (x *CheckSpeakPermissionRequest) String() string { func (*CheckSpeakPermissionRequest) ProtoMessage() {} func (x *CheckSpeakPermissionRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[82] + mi := &file_proto_room_v1_room_proto_msgTypes[83] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6111,7 +6205,7 @@ func (x *CheckSpeakPermissionRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CheckSpeakPermissionRequest.ProtoReflect.Descriptor instead. func (*CheckSpeakPermissionRequest) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{82} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{83} } func (x *CheckSpeakPermissionRequest) GetRoomId() string { @@ -6147,7 +6241,7 @@ type CheckSpeakPermissionResponse struct { func (x *CheckSpeakPermissionResponse) Reset() { *x = CheckSpeakPermissionResponse{} - mi := &file_proto_room_v1_room_proto_msgTypes[83] + mi := &file_proto_room_v1_room_proto_msgTypes[84] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6159,7 +6253,7 @@ func (x *CheckSpeakPermissionResponse) String() string { func (*CheckSpeakPermissionResponse) ProtoMessage() {} func (x *CheckSpeakPermissionResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[83] + mi := &file_proto_room_v1_room_proto_msgTypes[84] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6172,7 +6266,7 @@ func (x *CheckSpeakPermissionResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use CheckSpeakPermissionResponse.ProtoReflect.Descriptor instead. func (*CheckSpeakPermissionResponse) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{83} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{84} } func (x *CheckSpeakPermissionResponse) GetAllowed() bool { @@ -6210,7 +6304,7 @@ type VerifyRoomPresenceRequest struct { func (x *VerifyRoomPresenceRequest) Reset() { *x = VerifyRoomPresenceRequest{} - mi := &file_proto_room_v1_room_proto_msgTypes[84] + mi := &file_proto_room_v1_room_proto_msgTypes[85] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6222,7 +6316,7 @@ func (x *VerifyRoomPresenceRequest) String() string { func (*VerifyRoomPresenceRequest) ProtoMessage() {} func (x *VerifyRoomPresenceRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[84] + mi := &file_proto_room_v1_room_proto_msgTypes[85] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6235,7 +6329,7 @@ func (x *VerifyRoomPresenceRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use VerifyRoomPresenceRequest.ProtoReflect.Descriptor instead. func (*VerifyRoomPresenceRequest) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{84} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{85} } func (x *VerifyRoomPresenceRequest) GetRoomId() string { @@ -6285,7 +6379,7 @@ type VerifyRoomPresenceResponse struct { func (x *VerifyRoomPresenceResponse) Reset() { *x = VerifyRoomPresenceResponse{} - mi := &file_proto_room_v1_room_proto_msgTypes[85] + mi := &file_proto_room_v1_room_proto_msgTypes[86] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6297,7 +6391,7 @@ func (x *VerifyRoomPresenceResponse) String() string { func (*VerifyRoomPresenceResponse) ProtoMessage() {} func (x *VerifyRoomPresenceResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[85] + mi := &file_proto_room_v1_room_proto_msgTypes[86] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6310,7 +6404,7 @@ func (x *VerifyRoomPresenceResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use VerifyRoomPresenceResponse.ProtoReflect.Descriptor instead. func (*VerifyRoomPresenceResponse) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{85} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{86} } func (x *VerifyRoomPresenceResponse) GetPresent() bool { @@ -6351,7 +6445,7 @@ type ListRoomsRequest struct { func (x *ListRoomsRequest) Reset() { *x = ListRoomsRequest{} - mi := &file_proto_room_v1_room_proto_msgTypes[86] + mi := &file_proto_room_v1_room_proto_msgTypes[87] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6363,7 +6457,7 @@ func (x *ListRoomsRequest) String() string { func (*ListRoomsRequest) ProtoMessage() {} func (x *ListRoomsRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[86] + mi := &file_proto_room_v1_room_proto_msgTypes[87] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6376,7 +6470,7 @@ func (x *ListRoomsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListRoomsRequest.ProtoReflect.Descriptor instead. func (*ListRoomsRequest) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{86} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{87} } func (x *ListRoomsRequest) GetMeta() *RequestMeta { @@ -6447,7 +6541,7 @@ type ListRoomFeedsRequest struct { func (x *ListRoomFeedsRequest) Reset() { *x = ListRoomFeedsRequest{} - mi := &file_proto_room_v1_room_proto_msgTypes[87] + mi := &file_proto_room_v1_room_proto_msgTypes[88] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6459,7 +6553,7 @@ func (x *ListRoomFeedsRequest) String() string { func (*ListRoomFeedsRequest) ProtoMessage() {} func (x *ListRoomFeedsRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[87] + mi := &file_proto_room_v1_room_proto_msgTypes[88] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6472,7 +6566,7 @@ func (x *ListRoomFeedsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListRoomFeedsRequest.ProtoReflect.Descriptor instead. func (*ListRoomFeedsRequest) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{87} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{88} } func (x *ListRoomFeedsRequest) GetMeta() *RequestMeta { @@ -6544,7 +6638,7 @@ type ListRoomGiftLeaderboardRequest struct { func (x *ListRoomGiftLeaderboardRequest) Reset() { *x = ListRoomGiftLeaderboardRequest{} - mi := &file_proto_room_v1_room_proto_msgTypes[88] + mi := &file_proto_room_v1_room_proto_msgTypes[89] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6556,7 +6650,7 @@ func (x *ListRoomGiftLeaderboardRequest) String() string { func (*ListRoomGiftLeaderboardRequest) ProtoMessage() {} func (x *ListRoomGiftLeaderboardRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[88] + mi := &file_proto_room_v1_room_proto_msgTypes[89] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6569,7 +6663,7 @@ func (x *ListRoomGiftLeaderboardRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListRoomGiftLeaderboardRequest.ProtoReflect.Descriptor instead. func (*ListRoomGiftLeaderboardRequest) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{88} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{89} } func (x *ListRoomGiftLeaderboardRequest) GetMeta() *RequestMeta { @@ -6611,7 +6705,7 @@ type RoomFeedRelatedUser struct { func (x *RoomFeedRelatedUser) Reset() { *x = RoomFeedRelatedUser{} - mi := &file_proto_room_v1_room_proto_msgTypes[89] + mi := &file_proto_room_v1_room_proto_msgTypes[90] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6623,7 +6717,7 @@ func (x *RoomFeedRelatedUser) String() string { func (*RoomFeedRelatedUser) ProtoMessage() {} func (x *RoomFeedRelatedUser) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[89] + mi := &file_proto_room_v1_room_proto_msgTypes[90] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6636,7 +6730,7 @@ func (x *RoomFeedRelatedUser) ProtoReflect() protoreflect.Message { // Deprecated: Use RoomFeedRelatedUser.ProtoReflect.Descriptor instead. func (*RoomFeedRelatedUser) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{89} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{90} } func (x *RoomFeedRelatedUser) GetUserId() int64 { @@ -6676,7 +6770,7 @@ type RoomListItem struct { func (x *RoomListItem) Reset() { *x = RoomListItem{} - mi := &file_proto_room_v1_room_proto_msgTypes[90] + mi := &file_proto_room_v1_room_proto_msgTypes[91] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6688,7 +6782,7 @@ func (x *RoomListItem) String() string { func (*RoomListItem) ProtoMessage() {} func (x *RoomListItem) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[90] + mi := &file_proto_room_v1_room_proto_msgTypes[91] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6701,7 +6795,7 @@ func (x *RoomListItem) ProtoReflect() protoreflect.Message { // Deprecated: Use RoomListItem.ProtoReflect.Descriptor instead. func (*RoomListItem) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{90} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{91} } func (x *RoomListItem) GetRoomId() string { @@ -6813,7 +6907,7 @@ type ListRoomsResponse struct { func (x *ListRoomsResponse) Reset() { *x = ListRoomsResponse{} - mi := &file_proto_room_v1_room_proto_msgTypes[91] + mi := &file_proto_room_v1_room_proto_msgTypes[92] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6825,7 +6919,7 @@ func (x *ListRoomsResponse) String() string { func (*ListRoomsResponse) ProtoMessage() {} func (x *ListRoomsResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[91] + mi := &file_proto_room_v1_room_proto_msgTypes[92] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6838,7 +6932,7 @@ func (x *ListRoomsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListRoomsResponse.ProtoReflect.Descriptor instead. func (*ListRoomsResponse) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{91} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{92} } func (x *ListRoomsResponse) GetRooms() []*RoomListItem { @@ -6868,7 +6962,7 @@ type RoomGiftLeaderboardItem struct { func (x *RoomGiftLeaderboardItem) Reset() { *x = RoomGiftLeaderboardItem{} - mi := &file_proto_room_v1_room_proto_msgTypes[92] + mi := &file_proto_room_v1_room_proto_msgTypes[93] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6880,7 +6974,7 @@ func (x *RoomGiftLeaderboardItem) String() string { func (*RoomGiftLeaderboardItem) ProtoMessage() {} func (x *RoomGiftLeaderboardItem) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[92] + mi := &file_proto_room_v1_room_proto_msgTypes[93] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6893,7 +6987,7 @@ func (x *RoomGiftLeaderboardItem) ProtoReflect() protoreflect.Message { // Deprecated: Use RoomGiftLeaderboardItem.ProtoReflect.Descriptor instead. func (*RoomGiftLeaderboardItem) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{92} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{93} } func (x *RoomGiftLeaderboardItem) GetRank() int64 { @@ -6939,7 +7033,7 @@ type ListRoomGiftLeaderboardResponse struct { func (x *ListRoomGiftLeaderboardResponse) Reset() { *x = ListRoomGiftLeaderboardResponse{} - mi := &file_proto_room_v1_room_proto_msgTypes[93] + mi := &file_proto_room_v1_room_proto_msgTypes[94] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6951,7 +7045,7 @@ func (x *ListRoomGiftLeaderboardResponse) String() string { func (*ListRoomGiftLeaderboardResponse) ProtoMessage() {} func (x *ListRoomGiftLeaderboardResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[93] + mi := &file_proto_room_v1_room_proto_msgTypes[94] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6964,7 +7058,7 @@ func (x *ListRoomGiftLeaderboardResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListRoomGiftLeaderboardResponse.ProtoReflect.Descriptor instead. func (*ListRoomGiftLeaderboardResponse) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{93} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{94} } func (x *ListRoomGiftLeaderboardResponse) GetItems() []*RoomGiftLeaderboardItem { @@ -7021,7 +7115,7 @@ type GetMyRoomRequest struct { func (x *GetMyRoomRequest) Reset() { *x = GetMyRoomRequest{} - mi := &file_proto_room_v1_room_proto_msgTypes[94] + mi := &file_proto_room_v1_room_proto_msgTypes[95] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7033,7 +7127,7 @@ func (x *GetMyRoomRequest) String() string { func (*GetMyRoomRequest) ProtoMessage() {} func (x *GetMyRoomRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[94] + mi := &file_proto_room_v1_room_proto_msgTypes[95] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7046,7 +7140,7 @@ func (x *GetMyRoomRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetMyRoomRequest.ProtoReflect.Descriptor instead. func (*GetMyRoomRequest) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{94} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{95} } func (x *GetMyRoomRequest) GetMeta() *RequestMeta { @@ -7076,7 +7170,7 @@ type GetMyRoomResponse struct { func (x *GetMyRoomResponse) Reset() { *x = GetMyRoomResponse{} - mi := &file_proto_room_v1_room_proto_msgTypes[95] + mi := &file_proto_room_v1_room_proto_msgTypes[96] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7088,7 +7182,7 @@ func (x *GetMyRoomResponse) String() string { func (*GetMyRoomResponse) ProtoMessage() {} func (x *GetMyRoomResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[95] + mi := &file_proto_room_v1_room_proto_msgTypes[96] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7101,7 +7195,7 @@ func (x *GetMyRoomResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetMyRoomResponse.ProtoReflect.Descriptor instead. func (*GetMyRoomResponse) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{95} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{96} } func (x *GetMyRoomResponse) GetHasRoom() bool { @@ -7137,7 +7231,7 @@ type GetCurrentRoomRequest struct { func (x *GetCurrentRoomRequest) Reset() { *x = GetCurrentRoomRequest{} - mi := &file_proto_room_v1_room_proto_msgTypes[96] + mi := &file_proto_room_v1_room_proto_msgTypes[97] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7149,7 +7243,7 @@ func (x *GetCurrentRoomRequest) String() string { func (*GetCurrentRoomRequest) ProtoMessage() {} func (x *GetCurrentRoomRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[96] + mi := &file_proto_room_v1_room_proto_msgTypes[97] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7162,7 +7256,7 @@ func (x *GetCurrentRoomRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetCurrentRoomRequest.ProtoReflect.Descriptor instead. func (*GetCurrentRoomRequest) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{96} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{97} } func (x *GetCurrentRoomRequest) GetMeta() *RequestMeta { @@ -7198,7 +7292,7 @@ type GetCurrentRoomResponse struct { func (x *GetCurrentRoomResponse) Reset() { *x = GetCurrentRoomResponse{} - mi := &file_proto_room_v1_room_proto_msgTypes[97] + mi := &file_proto_room_v1_room_proto_msgTypes[98] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7210,7 +7304,7 @@ func (x *GetCurrentRoomResponse) String() string { func (*GetCurrentRoomResponse) ProtoMessage() {} func (x *GetCurrentRoomResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[97] + mi := &file_proto_room_v1_room_proto_msgTypes[98] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7223,7 +7317,7 @@ func (x *GetCurrentRoomResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetCurrentRoomResponse.ProtoReflect.Descriptor instead. func (*GetCurrentRoomResponse) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{97} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{98} } func (x *GetCurrentRoomResponse) GetHasCurrentRoom() bool { @@ -7302,7 +7396,7 @@ type GetRoomSnapshotRequest struct { func (x *GetRoomSnapshotRequest) Reset() { *x = GetRoomSnapshotRequest{} - mi := &file_proto_room_v1_room_proto_msgTypes[98] + mi := &file_proto_room_v1_room_proto_msgTypes[99] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7314,7 +7408,7 @@ func (x *GetRoomSnapshotRequest) String() string { func (*GetRoomSnapshotRequest) ProtoMessage() {} func (x *GetRoomSnapshotRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[98] + mi := &file_proto_room_v1_room_proto_msgTypes[99] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7327,7 +7421,7 @@ func (x *GetRoomSnapshotRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetRoomSnapshotRequest.ProtoReflect.Descriptor instead. func (*GetRoomSnapshotRequest) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{98} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{99} } func (x *GetRoomSnapshotRequest) GetMeta() *RequestMeta { @@ -7364,7 +7458,7 @@ type GetRoomSnapshotResponse struct { func (x *GetRoomSnapshotResponse) Reset() { *x = GetRoomSnapshotResponse{} - mi := &file_proto_room_v1_room_proto_msgTypes[99] + mi := &file_proto_room_v1_room_proto_msgTypes[100] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7376,7 +7470,7 @@ func (x *GetRoomSnapshotResponse) String() string { func (*GetRoomSnapshotResponse) ProtoMessage() {} func (x *GetRoomSnapshotResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[99] + mi := &file_proto_room_v1_room_proto_msgTypes[100] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7389,7 +7483,7 @@ func (x *GetRoomSnapshotResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetRoomSnapshotResponse.ProtoReflect.Descriptor instead. func (*GetRoomSnapshotResponse) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{99} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{100} } func (x *GetRoomSnapshotResponse) GetRoom() *RoomSnapshot { @@ -7426,7 +7520,7 @@ type GetRoomTreasureRequest struct { func (x *GetRoomTreasureRequest) Reset() { *x = GetRoomTreasureRequest{} - mi := &file_proto_room_v1_room_proto_msgTypes[100] + mi := &file_proto_room_v1_room_proto_msgTypes[101] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7438,7 +7532,7 @@ func (x *GetRoomTreasureRequest) String() string { func (*GetRoomTreasureRequest) ProtoMessage() {} func (x *GetRoomTreasureRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[100] + mi := &file_proto_room_v1_room_proto_msgTypes[101] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7451,7 +7545,7 @@ func (x *GetRoomTreasureRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetRoomTreasureRequest.ProtoReflect.Descriptor instead. func (*GetRoomTreasureRequest) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{100} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{101} } func (x *GetRoomTreasureRequest) GetMeta() *RequestMeta { @@ -7485,7 +7579,7 @@ type GetRoomTreasureResponse struct { func (x *GetRoomTreasureResponse) Reset() { *x = GetRoomTreasureResponse{} - mi := &file_proto_room_v1_room_proto_msgTypes[101] + mi := &file_proto_room_v1_room_proto_msgTypes[102] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7497,7 +7591,7 @@ func (x *GetRoomTreasureResponse) String() string { func (*GetRoomTreasureResponse) ProtoMessage() {} func (x *GetRoomTreasureResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[101] + mi := &file_proto_room_v1_room_proto_msgTypes[102] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7510,7 +7604,7 @@ func (x *GetRoomTreasureResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetRoomTreasureResponse.ProtoReflect.Descriptor instead. func (*GetRoomTreasureResponse) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{101} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{102} } func (x *GetRoomTreasureResponse) GetTreasure() *RoomTreasureInfo { @@ -7535,13 +7629,14 @@ type ListRoomOnlineUsersRequest struct { ViewerUserId int64 `protobuf:"varint,3,opt,name=viewer_user_id,json=viewerUserId,proto3" json:"viewer_user_id,omitempty"` Page int32 `protobuf:"varint,4,opt,name=page,proto3" json:"page,omitempty"` PageSize int32 `protobuf:"varint,5,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"` + Sort string `protobuf:"bytes,6,opt,name=sort,proto3" json:"sort,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ListRoomOnlineUsersRequest) Reset() { *x = ListRoomOnlineUsersRequest{} - mi := &file_proto_room_v1_room_proto_msgTypes[102] + mi := &file_proto_room_v1_room_proto_msgTypes[103] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7553,7 +7648,7 @@ func (x *ListRoomOnlineUsersRequest) String() string { func (*ListRoomOnlineUsersRequest) ProtoMessage() {} func (x *ListRoomOnlineUsersRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[102] + mi := &file_proto_room_v1_room_proto_msgTypes[103] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7566,7 +7661,7 @@ func (x *ListRoomOnlineUsersRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListRoomOnlineUsersRequest.ProtoReflect.Descriptor instead. func (*ListRoomOnlineUsersRequest) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{102} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{103} } func (x *ListRoomOnlineUsersRequest) GetMeta() *RequestMeta { @@ -7604,6 +7699,13 @@ func (x *ListRoomOnlineUsersRequest) GetPageSize() int32 { return 0 } +func (x *ListRoomOnlineUsersRequest) GetSort() string { + if x != nil { + return x.Sort + } + return "" +} + type ListRoomOnlineUsersResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Users []*RoomUser `protobuf:"bytes,1,rep,name=users,proto3" json:"users,omitempty"` @@ -7611,13 +7713,14 @@ type ListRoomOnlineUsersResponse struct { Page int32 `protobuf:"varint,3,opt,name=page,proto3" json:"page,omitempty"` PageSize int32 `protobuf:"varint,4,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"` ServerTimeMs int64 `protobuf:"varint,5,opt,name=server_time_ms,json=serverTimeMs,proto3" json:"server_time_ms,omitempty"` + Items []*RoomOnlineUser `protobuf:"bytes,6,rep,name=items,proto3" json:"items,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ListRoomOnlineUsersResponse) Reset() { *x = ListRoomOnlineUsersResponse{} - mi := &file_proto_room_v1_room_proto_msgTypes[103] + mi := &file_proto_room_v1_room_proto_msgTypes[104] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7629,7 +7732,7 @@ func (x *ListRoomOnlineUsersResponse) String() string { func (*ListRoomOnlineUsersResponse) ProtoMessage() {} func (x *ListRoomOnlineUsersResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[103] + mi := &file_proto_room_v1_room_proto_msgTypes[104] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7642,7 +7745,7 @@ func (x *ListRoomOnlineUsersResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListRoomOnlineUsersResponse.ProtoReflect.Descriptor instead. func (*ListRoomOnlineUsersResponse) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{103} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{104} } func (x *ListRoomOnlineUsersResponse) GetUsers() []*RoomUser { @@ -7680,6 +7783,13 @@ func (x *ListRoomOnlineUsersResponse) GetServerTimeMs() int64 { return 0 } +func (x *ListRoomOnlineUsersResponse) GetItems() []*RoomOnlineUser { + if x != nil { + return x.Items + } + return nil +} + // FollowRoomRequest 建立当前用户对房间的关注关系。 // 该关系是 room-service 的用户-房间低频关系事实,不进入 Room Cell command log。 type FollowRoomRequest struct { @@ -7693,7 +7803,7 @@ type FollowRoomRequest struct { func (x *FollowRoomRequest) Reset() { *x = FollowRoomRequest{} - mi := &file_proto_room_v1_room_proto_msgTypes[104] + mi := &file_proto_room_v1_room_proto_msgTypes[105] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7705,7 +7815,7 @@ func (x *FollowRoomRequest) String() string { func (*FollowRoomRequest) ProtoMessage() {} func (x *FollowRoomRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[104] + mi := &file_proto_room_v1_room_proto_msgTypes[105] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7718,7 +7828,7 @@ func (x *FollowRoomRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use FollowRoomRequest.ProtoReflect.Descriptor instead. func (*FollowRoomRequest) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{104} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{105} } func (x *FollowRoomRequest) GetMeta() *RequestMeta { @@ -7754,7 +7864,7 @@ type FollowRoomResponse struct { func (x *FollowRoomResponse) Reset() { *x = FollowRoomResponse{} - mi := &file_proto_room_v1_room_proto_msgTypes[105] + mi := &file_proto_room_v1_room_proto_msgTypes[106] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7766,7 +7876,7 @@ func (x *FollowRoomResponse) String() string { func (*FollowRoomResponse) ProtoMessage() {} func (x *FollowRoomResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[105] + mi := &file_proto_room_v1_room_proto_msgTypes[106] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7779,7 +7889,7 @@ func (x *FollowRoomResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use FollowRoomResponse.ProtoReflect.Descriptor instead. func (*FollowRoomResponse) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{105} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{106} } func (x *FollowRoomResponse) GetRoomId() string { @@ -7822,7 +7932,7 @@ type UnfollowRoomRequest struct { func (x *UnfollowRoomRequest) Reset() { *x = UnfollowRoomRequest{} - mi := &file_proto_room_v1_room_proto_msgTypes[106] + mi := &file_proto_room_v1_room_proto_msgTypes[107] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7834,7 +7944,7 @@ func (x *UnfollowRoomRequest) String() string { func (*UnfollowRoomRequest) ProtoMessage() {} func (x *UnfollowRoomRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[106] + mi := &file_proto_room_v1_room_proto_msgTypes[107] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7847,7 +7957,7 @@ func (x *UnfollowRoomRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UnfollowRoomRequest.ProtoReflect.Descriptor instead. func (*UnfollowRoomRequest) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{106} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{107} } func (x *UnfollowRoomRequest) GetMeta() *RequestMeta { @@ -7882,7 +7992,7 @@ type UnfollowRoomResponse struct { func (x *UnfollowRoomResponse) Reset() { *x = UnfollowRoomResponse{} - mi := &file_proto_room_v1_room_proto_msgTypes[107] + mi := &file_proto_room_v1_room_proto_msgTypes[108] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7894,7 +8004,7 @@ func (x *UnfollowRoomResponse) String() string { func (*UnfollowRoomResponse) ProtoMessage() {} func (x *UnfollowRoomResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_room_v1_room_proto_msgTypes[107] + mi := &file_proto_room_v1_room_proto_msgTypes[108] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7907,7 +8017,7 @@ func (x *UnfollowRoomResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use UnfollowRoomResponse.ProtoReflect.Descriptor instead. func (*UnfollowRoomResponse) Descriptor() ([]byte, []int) { - return file_proto_room_v1_room_proto_rawDescGZIP(), []int{107} + return file_proto_room_v1_room_proto_rawDescGZIP(), []int{108} } func (x *UnfollowRoomResponse) GetRoomId() string { @@ -7958,7 +8068,17 @@ const file_proto_room_v1_room_proto_rawDesc = "" + "\x04role\x18\x02 \x01(\tR\x04role\x12 \n" + "\fjoined_at_ms\x18\x03 \x01(\x03R\n" + "joinedAtMs\x12%\n" + - "\x0flast_seen_at_ms\x18\x04 \x01(\x03R\flastSeenAtMs\"\x83\x03\n" + + "\x0flast_seen_at_ms\x18\x04 \x01(\x03R\flastSeenAtMs\"\xdd\x01\n" + + "\x0eRoomOnlineUser\x12\x17\n" + + "\auser_id\x18\x01 \x01(\x03R\x06userId\x12\x12\n" + + "\x04role\x18\x02 \x01(\tR\x04role\x12\x1b\n" + + "\troom_role\x18\x03 \x01(\tR\broomRole\x12\x1d\n" + + "\n" + + "gift_value\x18\x04 \x01(\x03R\tgiftValue\x12 \n" + + "\fjoined_at_ms\x18\x05 \x01(\x03R\n" + + "joinedAtMs\x12%\n" + + "\x0flast_seen_at_ms\x18\x06 \x01(\x03R\flastSeenAtMs\x12\x19\n" + + "\bis_owner\x18\a \x01(\bR\aisOwner\"\x83\x03\n" + "\tSeatState\x12\x17\n" + "\aseat_no\x18\x01 \x01(\x05R\x06seatNo\x12\x17\n" + "\auser_id\x18\x02 \x01(\x03R\x06userId\x12\x16\n" + @@ -8560,19 +8680,21 @@ const file_proto_room_v1_room_proto_rawDesc = "" + "\x0eviewer_user_id\x18\x03 \x01(\x03R\fviewerUserId\"|\n" + "\x17GetRoomTreasureResponse\x12;\n" + "\btreasure\x18\x01 \x01(\v2\x1f.hyapp.room.v1.RoomTreasureInfoR\btreasure\x12$\n" + - "\x0eserver_time_ms\x18\x02 \x01(\x03R\fserverTimeMs\"\xbc\x01\n" + + "\x0eserver_time_ms\x18\x02 \x01(\x03R\fserverTimeMs\"\xd0\x01\n" + "\x1aListRoomOnlineUsersRequest\x12.\n" + "\x04meta\x18\x01 \x01(\v2\x1a.hyapp.room.v1.RequestMetaR\x04meta\x12\x17\n" + "\aroom_id\x18\x02 \x01(\tR\x06roomId\x12$\n" + "\x0eviewer_user_id\x18\x03 \x01(\x03R\fviewerUserId\x12\x12\n" + "\x04page\x18\x04 \x01(\x05R\x04page\x12\x1b\n" + - "\tpage_size\x18\x05 \x01(\x05R\bpageSize\"\xb9\x01\n" + + "\tpage_size\x18\x05 \x01(\x05R\bpageSize\x12\x12\n" + + "\x04sort\x18\x06 \x01(\tR\x04sort\"\xee\x01\n" + "\x1bListRoomOnlineUsersResponse\x12-\n" + "\x05users\x18\x01 \x03(\v2\x17.hyapp.room.v1.RoomUserR\x05users\x12\x14\n" + "\x05total\x18\x02 \x01(\x03R\x05total\x12\x12\n" + "\x04page\x18\x03 \x01(\x05R\x04page\x12\x1b\n" + "\tpage_size\x18\x04 \x01(\x05R\bpageSize\x12$\n" + - "\x0eserver_time_ms\x18\x05 \x01(\x03R\fserverTimeMs\"u\n" + + "\x0eserver_time_ms\x18\x05 \x01(\x03R\fserverTimeMs\x123\n" + + "\x05items\x18\x06 \x03(\v2\x1d.hyapp.room.v1.RoomOnlineUserR\x05items\"u\n" + "\x11FollowRoomRequest\x12.\n" + "\x04meta\x18\x01 \x01(\v2\x1a.hyapp.room.v1.RequestMetaR\x04meta\x12\x17\n" + "\aroom_id\x18\x02 \x01(\tR\x06roomId\x12\x17\n" + @@ -8654,337 +8776,339 @@ func file_proto_room_v1_room_proto_rawDescGZIP() []byte { return file_proto_room_v1_room_proto_rawDescData } -var file_proto_room_v1_room_proto_msgTypes = make([]protoimpl.MessageInfo, 109) +var file_proto_room_v1_room_proto_msgTypes = make([]protoimpl.MessageInfo, 110) var file_proto_room_v1_room_proto_goTypes = []any{ (*RequestMeta)(nil), // 0: hyapp.room.v1.RequestMeta (*CommandResult)(nil), // 1: hyapp.room.v1.CommandResult (*RoomUser)(nil), // 2: hyapp.room.v1.RoomUser - (*SeatState)(nil), // 3: hyapp.room.v1.SeatState - (*RankItem)(nil), // 4: hyapp.room.v1.RankItem - (*LuckyGiftDrawResult)(nil), // 5: hyapp.room.v1.LuckyGiftDrawResult - (*RoomTreasureRewardItem)(nil), // 6: hyapp.room.v1.RoomTreasureRewardItem - (*RoomTreasureLevel)(nil), // 7: hyapp.room.v1.RoomTreasureLevel - (*RoomTreasureRewardGrant)(nil), // 8: hyapp.room.v1.RoomTreasureRewardGrant - (*RoomTreasureState)(nil), // 9: hyapp.room.v1.RoomTreasureState - (*RoomTreasureInfo)(nil), // 10: hyapp.room.v1.RoomTreasureInfo - (*RoomTreasureGiftEnergyRule)(nil), // 11: hyapp.room.v1.RoomTreasureGiftEnergyRule - (*AdminRoomTreasureConfig)(nil), // 12: hyapp.room.v1.AdminRoomTreasureConfig - (*AdminGetRoomTreasureConfigRequest)(nil), // 13: hyapp.room.v1.AdminGetRoomTreasureConfigRequest - (*AdminGetRoomTreasureConfigResponse)(nil), // 14: hyapp.room.v1.AdminGetRoomTreasureConfigResponse - (*AdminUpdateRoomTreasureConfigRequest)(nil), // 15: hyapp.room.v1.AdminUpdateRoomTreasureConfigRequest - (*AdminUpdateRoomTreasureConfigResponse)(nil), // 16: hyapp.room.v1.AdminUpdateRoomTreasureConfigResponse - (*AdminRoomSeatConfig)(nil), // 17: hyapp.room.v1.AdminRoomSeatConfig - (*AdminGetRoomSeatConfigRequest)(nil), // 18: hyapp.room.v1.AdminGetRoomSeatConfigRequest - (*AdminGetRoomSeatConfigResponse)(nil), // 19: hyapp.room.v1.AdminGetRoomSeatConfigResponse - (*AdminUpdateRoomSeatConfigRequest)(nil), // 20: hyapp.room.v1.AdminUpdateRoomSeatConfigRequest - (*AdminUpdateRoomSeatConfigResponse)(nil), // 21: hyapp.room.v1.AdminUpdateRoomSeatConfigResponse - (*AdminRoomPinRoom)(nil), // 22: hyapp.room.v1.AdminRoomPinRoom - (*AdminRoomPin)(nil), // 23: hyapp.room.v1.AdminRoomPin - (*AdminListRoomPinsRequest)(nil), // 24: hyapp.room.v1.AdminListRoomPinsRequest - (*AdminListRoomPinsResponse)(nil), // 25: hyapp.room.v1.AdminListRoomPinsResponse - (*AdminCreateRoomPinRequest)(nil), // 26: hyapp.room.v1.AdminCreateRoomPinRequest - (*AdminCreateRoomPinResponse)(nil), // 27: hyapp.room.v1.AdminCreateRoomPinResponse - (*AdminCancelRoomPinRequest)(nil), // 28: hyapp.room.v1.AdminCancelRoomPinRequest - (*AdminCancelRoomPinResponse)(nil), // 29: hyapp.room.v1.AdminCancelRoomPinResponse - (*RoomSnapshot)(nil), // 30: hyapp.room.v1.RoomSnapshot - (*CreateRoomRequest)(nil), // 31: hyapp.room.v1.CreateRoomRequest - (*CreateRoomResponse)(nil), // 32: hyapp.room.v1.CreateRoomResponse - (*UpdateRoomProfileRequest)(nil), // 33: hyapp.room.v1.UpdateRoomProfileRequest - (*UpdateRoomProfileResponse)(nil), // 34: hyapp.room.v1.UpdateRoomProfileResponse - (*JoinRoomRequest)(nil), // 35: hyapp.room.v1.JoinRoomRequest - (*JoinRoomResponse)(nil), // 36: hyapp.room.v1.JoinRoomResponse - (*RoomHeartbeatRequest)(nil), // 37: hyapp.room.v1.RoomHeartbeatRequest - (*RoomHeartbeatResponse)(nil), // 38: hyapp.room.v1.RoomHeartbeatResponse - (*LeaveRoomRequest)(nil), // 39: hyapp.room.v1.LeaveRoomRequest - (*LeaveRoomResponse)(nil), // 40: hyapp.room.v1.LeaveRoomResponse - (*CloseRoomRequest)(nil), // 41: hyapp.room.v1.CloseRoomRequest - (*CloseRoomResponse)(nil), // 42: hyapp.room.v1.CloseRoomResponse - (*AdminRoomListItem)(nil), // 43: hyapp.room.v1.AdminRoomListItem - (*AdminListRoomsRequest)(nil), // 44: hyapp.room.v1.AdminListRoomsRequest - (*AdminListRoomsResponse)(nil), // 45: hyapp.room.v1.AdminListRoomsResponse - (*AdminGetRoomRequest)(nil), // 46: hyapp.room.v1.AdminGetRoomRequest - (*AdminGetRoomResponse)(nil), // 47: hyapp.room.v1.AdminGetRoomResponse - (*AdminUpdateRoomRequest)(nil), // 48: hyapp.room.v1.AdminUpdateRoomRequest - (*AdminUpdateRoomResponse)(nil), // 49: hyapp.room.v1.AdminUpdateRoomResponse - (*AdminDeleteRoomRequest)(nil), // 50: hyapp.room.v1.AdminDeleteRoomRequest - (*AdminDeleteRoomResponse)(nil), // 51: hyapp.room.v1.AdminDeleteRoomResponse - (*MicUpRequest)(nil), // 52: hyapp.room.v1.MicUpRequest - (*MicUpResponse)(nil), // 53: hyapp.room.v1.MicUpResponse - (*MicDownRequest)(nil), // 54: hyapp.room.v1.MicDownRequest - (*MicDownResponse)(nil), // 55: hyapp.room.v1.MicDownResponse - (*ChangeMicSeatRequest)(nil), // 56: hyapp.room.v1.ChangeMicSeatRequest - (*ChangeMicSeatResponse)(nil), // 57: hyapp.room.v1.ChangeMicSeatResponse - (*ConfirmMicPublishingRequest)(nil), // 58: hyapp.room.v1.ConfirmMicPublishingRequest - (*ConfirmMicPublishingResponse)(nil), // 59: hyapp.room.v1.ConfirmMicPublishingResponse - (*SetMicMuteRequest)(nil), // 60: hyapp.room.v1.SetMicMuteRequest - (*SetMicMuteResponse)(nil), // 61: hyapp.room.v1.SetMicMuteResponse - (*ApplyRTCEventRequest)(nil), // 62: hyapp.room.v1.ApplyRTCEventRequest - (*ApplyRTCEventResponse)(nil), // 63: hyapp.room.v1.ApplyRTCEventResponse - (*SetMicSeatLockRequest)(nil), // 64: hyapp.room.v1.SetMicSeatLockRequest - (*SetMicSeatLockResponse)(nil), // 65: hyapp.room.v1.SetMicSeatLockResponse - (*SetChatEnabledRequest)(nil), // 66: hyapp.room.v1.SetChatEnabledRequest - (*SetChatEnabledResponse)(nil), // 67: hyapp.room.v1.SetChatEnabledResponse - (*SetRoomPasswordRequest)(nil), // 68: hyapp.room.v1.SetRoomPasswordRequest - (*SetRoomPasswordResponse)(nil), // 69: hyapp.room.v1.SetRoomPasswordResponse - (*SetRoomAdminRequest)(nil), // 70: hyapp.room.v1.SetRoomAdminRequest - (*SetRoomAdminResponse)(nil), // 71: hyapp.room.v1.SetRoomAdminResponse - (*MuteUserRequest)(nil), // 72: hyapp.room.v1.MuteUserRequest - (*MuteUserResponse)(nil), // 73: hyapp.room.v1.MuteUserResponse - (*KickUserRequest)(nil), // 74: hyapp.room.v1.KickUserRequest - (*KickUserResponse)(nil), // 75: hyapp.room.v1.KickUserResponse - (*UnbanUserRequest)(nil), // 76: hyapp.room.v1.UnbanUserRequest - (*UnbanUserResponse)(nil), // 77: hyapp.room.v1.UnbanUserResponse - (*SystemEvictUserRequest)(nil), // 78: hyapp.room.v1.SystemEvictUserRequest - (*SystemEvictUserResponse)(nil), // 79: hyapp.room.v1.SystemEvictUserResponse - (*SendGiftRequest)(nil), // 80: hyapp.room.v1.SendGiftRequest - (*SendGiftResponse)(nil), // 81: hyapp.room.v1.SendGiftResponse - (*CheckSpeakPermissionRequest)(nil), // 82: hyapp.room.v1.CheckSpeakPermissionRequest - (*CheckSpeakPermissionResponse)(nil), // 83: hyapp.room.v1.CheckSpeakPermissionResponse - (*VerifyRoomPresenceRequest)(nil), // 84: hyapp.room.v1.VerifyRoomPresenceRequest - (*VerifyRoomPresenceResponse)(nil), // 85: hyapp.room.v1.VerifyRoomPresenceResponse - (*ListRoomsRequest)(nil), // 86: hyapp.room.v1.ListRoomsRequest - (*ListRoomFeedsRequest)(nil), // 87: hyapp.room.v1.ListRoomFeedsRequest - (*ListRoomGiftLeaderboardRequest)(nil), // 88: hyapp.room.v1.ListRoomGiftLeaderboardRequest - (*RoomFeedRelatedUser)(nil), // 89: hyapp.room.v1.RoomFeedRelatedUser - (*RoomListItem)(nil), // 90: hyapp.room.v1.RoomListItem - (*ListRoomsResponse)(nil), // 91: hyapp.room.v1.ListRoomsResponse - (*RoomGiftLeaderboardItem)(nil), // 92: hyapp.room.v1.RoomGiftLeaderboardItem - (*ListRoomGiftLeaderboardResponse)(nil), // 93: hyapp.room.v1.ListRoomGiftLeaderboardResponse - (*GetMyRoomRequest)(nil), // 94: hyapp.room.v1.GetMyRoomRequest - (*GetMyRoomResponse)(nil), // 95: hyapp.room.v1.GetMyRoomResponse - (*GetCurrentRoomRequest)(nil), // 96: hyapp.room.v1.GetCurrentRoomRequest - (*GetCurrentRoomResponse)(nil), // 97: hyapp.room.v1.GetCurrentRoomResponse - (*GetRoomSnapshotRequest)(nil), // 98: hyapp.room.v1.GetRoomSnapshotRequest - (*GetRoomSnapshotResponse)(nil), // 99: hyapp.room.v1.GetRoomSnapshotResponse - (*GetRoomTreasureRequest)(nil), // 100: hyapp.room.v1.GetRoomTreasureRequest - (*GetRoomTreasureResponse)(nil), // 101: hyapp.room.v1.GetRoomTreasureResponse - (*ListRoomOnlineUsersRequest)(nil), // 102: hyapp.room.v1.ListRoomOnlineUsersRequest - (*ListRoomOnlineUsersResponse)(nil), // 103: hyapp.room.v1.ListRoomOnlineUsersResponse - (*FollowRoomRequest)(nil), // 104: hyapp.room.v1.FollowRoomRequest - (*FollowRoomResponse)(nil), // 105: hyapp.room.v1.FollowRoomResponse - (*UnfollowRoomRequest)(nil), // 106: hyapp.room.v1.UnfollowRoomRequest - (*UnfollowRoomResponse)(nil), // 107: hyapp.room.v1.UnfollowRoomResponse - nil, // 108: hyapp.room.v1.RoomSnapshot.RoomExtEntry + (*RoomOnlineUser)(nil), // 3: hyapp.room.v1.RoomOnlineUser + (*SeatState)(nil), // 4: hyapp.room.v1.SeatState + (*RankItem)(nil), // 5: hyapp.room.v1.RankItem + (*LuckyGiftDrawResult)(nil), // 6: hyapp.room.v1.LuckyGiftDrawResult + (*RoomTreasureRewardItem)(nil), // 7: hyapp.room.v1.RoomTreasureRewardItem + (*RoomTreasureLevel)(nil), // 8: hyapp.room.v1.RoomTreasureLevel + (*RoomTreasureRewardGrant)(nil), // 9: hyapp.room.v1.RoomTreasureRewardGrant + (*RoomTreasureState)(nil), // 10: hyapp.room.v1.RoomTreasureState + (*RoomTreasureInfo)(nil), // 11: hyapp.room.v1.RoomTreasureInfo + (*RoomTreasureGiftEnergyRule)(nil), // 12: hyapp.room.v1.RoomTreasureGiftEnergyRule + (*AdminRoomTreasureConfig)(nil), // 13: hyapp.room.v1.AdminRoomTreasureConfig + (*AdminGetRoomTreasureConfigRequest)(nil), // 14: hyapp.room.v1.AdminGetRoomTreasureConfigRequest + (*AdminGetRoomTreasureConfigResponse)(nil), // 15: hyapp.room.v1.AdminGetRoomTreasureConfigResponse + (*AdminUpdateRoomTreasureConfigRequest)(nil), // 16: hyapp.room.v1.AdminUpdateRoomTreasureConfigRequest + (*AdminUpdateRoomTreasureConfigResponse)(nil), // 17: hyapp.room.v1.AdminUpdateRoomTreasureConfigResponse + (*AdminRoomSeatConfig)(nil), // 18: hyapp.room.v1.AdminRoomSeatConfig + (*AdminGetRoomSeatConfigRequest)(nil), // 19: hyapp.room.v1.AdminGetRoomSeatConfigRequest + (*AdminGetRoomSeatConfigResponse)(nil), // 20: hyapp.room.v1.AdminGetRoomSeatConfigResponse + (*AdminUpdateRoomSeatConfigRequest)(nil), // 21: hyapp.room.v1.AdminUpdateRoomSeatConfigRequest + (*AdminUpdateRoomSeatConfigResponse)(nil), // 22: hyapp.room.v1.AdminUpdateRoomSeatConfigResponse + (*AdminRoomPinRoom)(nil), // 23: hyapp.room.v1.AdminRoomPinRoom + (*AdminRoomPin)(nil), // 24: hyapp.room.v1.AdminRoomPin + (*AdminListRoomPinsRequest)(nil), // 25: hyapp.room.v1.AdminListRoomPinsRequest + (*AdminListRoomPinsResponse)(nil), // 26: hyapp.room.v1.AdminListRoomPinsResponse + (*AdminCreateRoomPinRequest)(nil), // 27: hyapp.room.v1.AdminCreateRoomPinRequest + (*AdminCreateRoomPinResponse)(nil), // 28: hyapp.room.v1.AdminCreateRoomPinResponse + (*AdminCancelRoomPinRequest)(nil), // 29: hyapp.room.v1.AdminCancelRoomPinRequest + (*AdminCancelRoomPinResponse)(nil), // 30: hyapp.room.v1.AdminCancelRoomPinResponse + (*RoomSnapshot)(nil), // 31: hyapp.room.v1.RoomSnapshot + (*CreateRoomRequest)(nil), // 32: hyapp.room.v1.CreateRoomRequest + (*CreateRoomResponse)(nil), // 33: hyapp.room.v1.CreateRoomResponse + (*UpdateRoomProfileRequest)(nil), // 34: hyapp.room.v1.UpdateRoomProfileRequest + (*UpdateRoomProfileResponse)(nil), // 35: hyapp.room.v1.UpdateRoomProfileResponse + (*JoinRoomRequest)(nil), // 36: hyapp.room.v1.JoinRoomRequest + (*JoinRoomResponse)(nil), // 37: hyapp.room.v1.JoinRoomResponse + (*RoomHeartbeatRequest)(nil), // 38: hyapp.room.v1.RoomHeartbeatRequest + (*RoomHeartbeatResponse)(nil), // 39: hyapp.room.v1.RoomHeartbeatResponse + (*LeaveRoomRequest)(nil), // 40: hyapp.room.v1.LeaveRoomRequest + (*LeaveRoomResponse)(nil), // 41: hyapp.room.v1.LeaveRoomResponse + (*CloseRoomRequest)(nil), // 42: hyapp.room.v1.CloseRoomRequest + (*CloseRoomResponse)(nil), // 43: hyapp.room.v1.CloseRoomResponse + (*AdminRoomListItem)(nil), // 44: hyapp.room.v1.AdminRoomListItem + (*AdminListRoomsRequest)(nil), // 45: hyapp.room.v1.AdminListRoomsRequest + (*AdminListRoomsResponse)(nil), // 46: hyapp.room.v1.AdminListRoomsResponse + (*AdminGetRoomRequest)(nil), // 47: hyapp.room.v1.AdminGetRoomRequest + (*AdminGetRoomResponse)(nil), // 48: hyapp.room.v1.AdminGetRoomResponse + (*AdminUpdateRoomRequest)(nil), // 49: hyapp.room.v1.AdminUpdateRoomRequest + (*AdminUpdateRoomResponse)(nil), // 50: hyapp.room.v1.AdminUpdateRoomResponse + (*AdminDeleteRoomRequest)(nil), // 51: hyapp.room.v1.AdminDeleteRoomRequest + (*AdminDeleteRoomResponse)(nil), // 52: hyapp.room.v1.AdminDeleteRoomResponse + (*MicUpRequest)(nil), // 53: hyapp.room.v1.MicUpRequest + (*MicUpResponse)(nil), // 54: hyapp.room.v1.MicUpResponse + (*MicDownRequest)(nil), // 55: hyapp.room.v1.MicDownRequest + (*MicDownResponse)(nil), // 56: hyapp.room.v1.MicDownResponse + (*ChangeMicSeatRequest)(nil), // 57: hyapp.room.v1.ChangeMicSeatRequest + (*ChangeMicSeatResponse)(nil), // 58: hyapp.room.v1.ChangeMicSeatResponse + (*ConfirmMicPublishingRequest)(nil), // 59: hyapp.room.v1.ConfirmMicPublishingRequest + (*ConfirmMicPublishingResponse)(nil), // 60: hyapp.room.v1.ConfirmMicPublishingResponse + (*SetMicMuteRequest)(nil), // 61: hyapp.room.v1.SetMicMuteRequest + (*SetMicMuteResponse)(nil), // 62: hyapp.room.v1.SetMicMuteResponse + (*ApplyRTCEventRequest)(nil), // 63: hyapp.room.v1.ApplyRTCEventRequest + (*ApplyRTCEventResponse)(nil), // 64: hyapp.room.v1.ApplyRTCEventResponse + (*SetMicSeatLockRequest)(nil), // 65: hyapp.room.v1.SetMicSeatLockRequest + (*SetMicSeatLockResponse)(nil), // 66: hyapp.room.v1.SetMicSeatLockResponse + (*SetChatEnabledRequest)(nil), // 67: hyapp.room.v1.SetChatEnabledRequest + (*SetChatEnabledResponse)(nil), // 68: hyapp.room.v1.SetChatEnabledResponse + (*SetRoomPasswordRequest)(nil), // 69: hyapp.room.v1.SetRoomPasswordRequest + (*SetRoomPasswordResponse)(nil), // 70: hyapp.room.v1.SetRoomPasswordResponse + (*SetRoomAdminRequest)(nil), // 71: hyapp.room.v1.SetRoomAdminRequest + (*SetRoomAdminResponse)(nil), // 72: hyapp.room.v1.SetRoomAdminResponse + (*MuteUserRequest)(nil), // 73: hyapp.room.v1.MuteUserRequest + (*MuteUserResponse)(nil), // 74: hyapp.room.v1.MuteUserResponse + (*KickUserRequest)(nil), // 75: hyapp.room.v1.KickUserRequest + (*KickUserResponse)(nil), // 76: hyapp.room.v1.KickUserResponse + (*UnbanUserRequest)(nil), // 77: hyapp.room.v1.UnbanUserRequest + (*UnbanUserResponse)(nil), // 78: hyapp.room.v1.UnbanUserResponse + (*SystemEvictUserRequest)(nil), // 79: hyapp.room.v1.SystemEvictUserRequest + (*SystemEvictUserResponse)(nil), // 80: hyapp.room.v1.SystemEvictUserResponse + (*SendGiftRequest)(nil), // 81: hyapp.room.v1.SendGiftRequest + (*SendGiftResponse)(nil), // 82: hyapp.room.v1.SendGiftResponse + (*CheckSpeakPermissionRequest)(nil), // 83: hyapp.room.v1.CheckSpeakPermissionRequest + (*CheckSpeakPermissionResponse)(nil), // 84: hyapp.room.v1.CheckSpeakPermissionResponse + (*VerifyRoomPresenceRequest)(nil), // 85: hyapp.room.v1.VerifyRoomPresenceRequest + (*VerifyRoomPresenceResponse)(nil), // 86: hyapp.room.v1.VerifyRoomPresenceResponse + (*ListRoomsRequest)(nil), // 87: hyapp.room.v1.ListRoomsRequest + (*ListRoomFeedsRequest)(nil), // 88: hyapp.room.v1.ListRoomFeedsRequest + (*ListRoomGiftLeaderboardRequest)(nil), // 89: hyapp.room.v1.ListRoomGiftLeaderboardRequest + (*RoomFeedRelatedUser)(nil), // 90: hyapp.room.v1.RoomFeedRelatedUser + (*RoomListItem)(nil), // 91: hyapp.room.v1.RoomListItem + (*ListRoomsResponse)(nil), // 92: hyapp.room.v1.ListRoomsResponse + (*RoomGiftLeaderboardItem)(nil), // 93: hyapp.room.v1.RoomGiftLeaderboardItem + (*ListRoomGiftLeaderboardResponse)(nil), // 94: hyapp.room.v1.ListRoomGiftLeaderboardResponse + (*GetMyRoomRequest)(nil), // 95: hyapp.room.v1.GetMyRoomRequest + (*GetMyRoomResponse)(nil), // 96: hyapp.room.v1.GetMyRoomResponse + (*GetCurrentRoomRequest)(nil), // 97: hyapp.room.v1.GetCurrentRoomRequest + (*GetCurrentRoomResponse)(nil), // 98: hyapp.room.v1.GetCurrentRoomResponse + (*GetRoomSnapshotRequest)(nil), // 99: hyapp.room.v1.GetRoomSnapshotRequest + (*GetRoomSnapshotResponse)(nil), // 100: hyapp.room.v1.GetRoomSnapshotResponse + (*GetRoomTreasureRequest)(nil), // 101: hyapp.room.v1.GetRoomTreasureRequest + (*GetRoomTreasureResponse)(nil), // 102: hyapp.room.v1.GetRoomTreasureResponse + (*ListRoomOnlineUsersRequest)(nil), // 103: hyapp.room.v1.ListRoomOnlineUsersRequest + (*ListRoomOnlineUsersResponse)(nil), // 104: hyapp.room.v1.ListRoomOnlineUsersResponse + (*FollowRoomRequest)(nil), // 105: hyapp.room.v1.FollowRoomRequest + (*FollowRoomResponse)(nil), // 106: hyapp.room.v1.FollowRoomResponse + (*UnfollowRoomRequest)(nil), // 107: hyapp.room.v1.UnfollowRoomRequest + (*UnfollowRoomResponse)(nil), // 108: hyapp.room.v1.UnfollowRoomResponse + nil, // 109: hyapp.room.v1.RoomSnapshot.RoomExtEntry } var file_proto_room_v1_room_proto_depIdxs = []int32{ - 6, // 0: hyapp.room.v1.RoomTreasureLevel.in_room_rewards:type_name -> hyapp.room.v1.RoomTreasureRewardItem - 6, // 1: hyapp.room.v1.RoomTreasureLevel.top1_rewards:type_name -> hyapp.room.v1.RoomTreasureRewardItem - 6, // 2: hyapp.room.v1.RoomTreasureLevel.igniter_rewards:type_name -> hyapp.room.v1.RoomTreasureRewardItem - 8, // 3: hyapp.room.v1.RoomTreasureState.last_rewards:type_name -> hyapp.room.v1.RoomTreasureRewardGrant - 7, // 4: hyapp.room.v1.RoomTreasureInfo.levels:type_name -> hyapp.room.v1.RoomTreasureLevel - 9, // 5: hyapp.room.v1.RoomTreasureInfo.state:type_name -> hyapp.room.v1.RoomTreasureState - 7, // 6: hyapp.room.v1.AdminRoomTreasureConfig.levels:type_name -> hyapp.room.v1.RoomTreasureLevel - 11, // 7: hyapp.room.v1.AdminRoomTreasureConfig.gift_energy_rules:type_name -> hyapp.room.v1.RoomTreasureGiftEnergyRule + 7, // 0: hyapp.room.v1.RoomTreasureLevel.in_room_rewards:type_name -> hyapp.room.v1.RoomTreasureRewardItem + 7, // 1: hyapp.room.v1.RoomTreasureLevel.top1_rewards:type_name -> hyapp.room.v1.RoomTreasureRewardItem + 7, // 2: hyapp.room.v1.RoomTreasureLevel.igniter_rewards:type_name -> hyapp.room.v1.RoomTreasureRewardItem + 9, // 3: hyapp.room.v1.RoomTreasureState.last_rewards:type_name -> hyapp.room.v1.RoomTreasureRewardGrant + 8, // 4: hyapp.room.v1.RoomTreasureInfo.levels:type_name -> hyapp.room.v1.RoomTreasureLevel + 10, // 5: hyapp.room.v1.RoomTreasureInfo.state:type_name -> hyapp.room.v1.RoomTreasureState + 8, // 6: hyapp.room.v1.AdminRoomTreasureConfig.levels:type_name -> hyapp.room.v1.RoomTreasureLevel + 12, // 7: hyapp.room.v1.AdminRoomTreasureConfig.gift_energy_rules:type_name -> hyapp.room.v1.RoomTreasureGiftEnergyRule 0, // 8: hyapp.room.v1.AdminGetRoomTreasureConfigRequest.meta:type_name -> hyapp.room.v1.RequestMeta - 12, // 9: hyapp.room.v1.AdminGetRoomTreasureConfigResponse.config:type_name -> hyapp.room.v1.AdminRoomTreasureConfig + 13, // 9: hyapp.room.v1.AdminGetRoomTreasureConfigResponse.config:type_name -> hyapp.room.v1.AdminRoomTreasureConfig 0, // 10: hyapp.room.v1.AdminUpdateRoomTreasureConfigRequest.meta:type_name -> hyapp.room.v1.RequestMeta - 12, // 11: hyapp.room.v1.AdminUpdateRoomTreasureConfigRequest.config:type_name -> hyapp.room.v1.AdminRoomTreasureConfig - 12, // 12: hyapp.room.v1.AdminUpdateRoomTreasureConfigResponse.config:type_name -> hyapp.room.v1.AdminRoomTreasureConfig + 13, // 11: hyapp.room.v1.AdminUpdateRoomTreasureConfigRequest.config:type_name -> hyapp.room.v1.AdminRoomTreasureConfig + 13, // 12: hyapp.room.v1.AdminUpdateRoomTreasureConfigResponse.config:type_name -> hyapp.room.v1.AdminRoomTreasureConfig 0, // 13: hyapp.room.v1.AdminGetRoomSeatConfigRequest.meta:type_name -> hyapp.room.v1.RequestMeta - 17, // 14: hyapp.room.v1.AdminGetRoomSeatConfigResponse.config:type_name -> hyapp.room.v1.AdminRoomSeatConfig + 18, // 14: hyapp.room.v1.AdminGetRoomSeatConfigResponse.config:type_name -> hyapp.room.v1.AdminRoomSeatConfig 0, // 15: hyapp.room.v1.AdminUpdateRoomSeatConfigRequest.meta:type_name -> hyapp.room.v1.RequestMeta - 17, // 16: hyapp.room.v1.AdminUpdateRoomSeatConfigResponse.config:type_name -> hyapp.room.v1.AdminRoomSeatConfig - 22, // 17: hyapp.room.v1.AdminRoomPin.room:type_name -> hyapp.room.v1.AdminRoomPinRoom + 18, // 16: hyapp.room.v1.AdminUpdateRoomSeatConfigResponse.config:type_name -> hyapp.room.v1.AdminRoomSeatConfig + 23, // 17: hyapp.room.v1.AdminRoomPin.room:type_name -> hyapp.room.v1.AdminRoomPinRoom 0, // 18: hyapp.room.v1.AdminListRoomPinsRequest.meta:type_name -> hyapp.room.v1.RequestMeta - 23, // 19: hyapp.room.v1.AdminListRoomPinsResponse.pins:type_name -> hyapp.room.v1.AdminRoomPin + 24, // 19: hyapp.room.v1.AdminListRoomPinsResponse.pins:type_name -> hyapp.room.v1.AdminRoomPin 0, // 20: hyapp.room.v1.AdminCreateRoomPinRequest.meta:type_name -> hyapp.room.v1.RequestMeta - 23, // 21: hyapp.room.v1.AdminCreateRoomPinResponse.pin:type_name -> hyapp.room.v1.AdminRoomPin + 24, // 21: hyapp.room.v1.AdminCreateRoomPinResponse.pin:type_name -> hyapp.room.v1.AdminRoomPin 0, // 22: hyapp.room.v1.AdminCancelRoomPinRequest.meta:type_name -> hyapp.room.v1.RequestMeta - 23, // 23: hyapp.room.v1.AdminCancelRoomPinResponse.pin:type_name -> hyapp.room.v1.AdminRoomPin - 3, // 24: hyapp.room.v1.RoomSnapshot.mic_seats:type_name -> hyapp.room.v1.SeatState + 24, // 23: hyapp.room.v1.AdminCancelRoomPinResponse.pin:type_name -> hyapp.room.v1.AdminRoomPin + 4, // 24: hyapp.room.v1.RoomSnapshot.mic_seats:type_name -> hyapp.room.v1.SeatState 2, // 25: hyapp.room.v1.RoomSnapshot.online_users:type_name -> hyapp.room.v1.RoomUser - 4, // 26: hyapp.room.v1.RoomSnapshot.gift_rank:type_name -> hyapp.room.v1.RankItem - 108, // 27: hyapp.room.v1.RoomSnapshot.room_ext:type_name -> hyapp.room.v1.RoomSnapshot.RoomExtEntry - 9, // 28: hyapp.room.v1.RoomSnapshot.treasure:type_name -> hyapp.room.v1.RoomTreasureState + 5, // 26: hyapp.room.v1.RoomSnapshot.gift_rank:type_name -> hyapp.room.v1.RankItem + 109, // 27: hyapp.room.v1.RoomSnapshot.room_ext:type_name -> hyapp.room.v1.RoomSnapshot.RoomExtEntry + 10, // 28: hyapp.room.v1.RoomSnapshot.treasure:type_name -> hyapp.room.v1.RoomTreasureState 0, // 29: hyapp.room.v1.CreateRoomRequest.meta:type_name -> hyapp.room.v1.RequestMeta 1, // 30: hyapp.room.v1.CreateRoomResponse.result:type_name -> hyapp.room.v1.CommandResult - 30, // 31: hyapp.room.v1.CreateRoomResponse.room:type_name -> hyapp.room.v1.RoomSnapshot + 31, // 31: hyapp.room.v1.CreateRoomResponse.room:type_name -> hyapp.room.v1.RoomSnapshot 0, // 32: hyapp.room.v1.UpdateRoomProfileRequest.meta:type_name -> hyapp.room.v1.RequestMeta 1, // 33: hyapp.room.v1.UpdateRoomProfileResponse.result:type_name -> hyapp.room.v1.CommandResult - 30, // 34: hyapp.room.v1.UpdateRoomProfileResponse.room:type_name -> hyapp.room.v1.RoomSnapshot + 31, // 34: hyapp.room.v1.UpdateRoomProfileResponse.room:type_name -> hyapp.room.v1.RoomSnapshot 0, // 35: hyapp.room.v1.JoinRoomRequest.meta:type_name -> hyapp.room.v1.RequestMeta 1, // 36: hyapp.room.v1.JoinRoomResponse.result:type_name -> hyapp.room.v1.CommandResult 2, // 37: hyapp.room.v1.JoinRoomResponse.user:type_name -> hyapp.room.v1.RoomUser - 30, // 38: hyapp.room.v1.JoinRoomResponse.room:type_name -> hyapp.room.v1.RoomSnapshot + 31, // 38: hyapp.room.v1.JoinRoomResponse.room:type_name -> hyapp.room.v1.RoomSnapshot 0, // 39: hyapp.room.v1.RoomHeartbeatRequest.meta:type_name -> hyapp.room.v1.RequestMeta 1, // 40: hyapp.room.v1.RoomHeartbeatResponse.result:type_name -> hyapp.room.v1.CommandResult 2, // 41: hyapp.room.v1.RoomHeartbeatResponse.user:type_name -> hyapp.room.v1.RoomUser - 30, // 42: hyapp.room.v1.RoomHeartbeatResponse.room:type_name -> hyapp.room.v1.RoomSnapshot + 31, // 42: hyapp.room.v1.RoomHeartbeatResponse.room:type_name -> hyapp.room.v1.RoomSnapshot 0, // 43: hyapp.room.v1.LeaveRoomRequest.meta:type_name -> hyapp.room.v1.RequestMeta 1, // 44: hyapp.room.v1.LeaveRoomResponse.result:type_name -> hyapp.room.v1.CommandResult - 30, // 45: hyapp.room.v1.LeaveRoomResponse.room:type_name -> hyapp.room.v1.RoomSnapshot + 31, // 45: hyapp.room.v1.LeaveRoomResponse.room:type_name -> hyapp.room.v1.RoomSnapshot 0, // 46: hyapp.room.v1.CloseRoomRequest.meta:type_name -> hyapp.room.v1.RequestMeta 1, // 47: hyapp.room.v1.CloseRoomResponse.result:type_name -> hyapp.room.v1.CommandResult - 30, // 48: hyapp.room.v1.CloseRoomResponse.room:type_name -> hyapp.room.v1.RoomSnapshot + 31, // 48: hyapp.room.v1.CloseRoomResponse.room:type_name -> hyapp.room.v1.RoomSnapshot 0, // 49: hyapp.room.v1.AdminListRoomsRequest.meta:type_name -> hyapp.room.v1.RequestMeta - 43, // 50: hyapp.room.v1.AdminListRoomsResponse.rooms:type_name -> hyapp.room.v1.AdminRoomListItem + 44, // 50: hyapp.room.v1.AdminListRoomsResponse.rooms:type_name -> hyapp.room.v1.AdminRoomListItem 0, // 51: hyapp.room.v1.AdminGetRoomRequest.meta:type_name -> hyapp.room.v1.RequestMeta - 43, // 52: hyapp.room.v1.AdminGetRoomResponse.room:type_name -> hyapp.room.v1.AdminRoomListItem + 44, // 52: hyapp.room.v1.AdminGetRoomResponse.room:type_name -> hyapp.room.v1.AdminRoomListItem 0, // 53: hyapp.room.v1.AdminUpdateRoomRequest.meta:type_name -> hyapp.room.v1.RequestMeta 1, // 54: hyapp.room.v1.AdminUpdateRoomResponse.result:type_name -> hyapp.room.v1.CommandResult - 30, // 55: hyapp.room.v1.AdminUpdateRoomResponse.room:type_name -> hyapp.room.v1.RoomSnapshot + 31, // 55: hyapp.room.v1.AdminUpdateRoomResponse.room:type_name -> hyapp.room.v1.RoomSnapshot 0, // 56: hyapp.room.v1.AdminDeleteRoomRequest.meta:type_name -> hyapp.room.v1.RequestMeta 1, // 57: hyapp.room.v1.AdminDeleteRoomResponse.result:type_name -> hyapp.room.v1.CommandResult - 30, // 58: hyapp.room.v1.AdminDeleteRoomResponse.room:type_name -> hyapp.room.v1.RoomSnapshot + 31, // 58: hyapp.room.v1.AdminDeleteRoomResponse.room:type_name -> hyapp.room.v1.RoomSnapshot 0, // 59: hyapp.room.v1.MicUpRequest.meta:type_name -> hyapp.room.v1.RequestMeta 1, // 60: hyapp.room.v1.MicUpResponse.result:type_name -> hyapp.room.v1.CommandResult - 30, // 61: hyapp.room.v1.MicUpResponse.room:type_name -> hyapp.room.v1.RoomSnapshot + 31, // 61: hyapp.room.v1.MicUpResponse.room:type_name -> hyapp.room.v1.RoomSnapshot 0, // 62: hyapp.room.v1.MicDownRequest.meta:type_name -> hyapp.room.v1.RequestMeta 1, // 63: hyapp.room.v1.MicDownResponse.result:type_name -> hyapp.room.v1.CommandResult - 30, // 64: hyapp.room.v1.MicDownResponse.room:type_name -> hyapp.room.v1.RoomSnapshot + 31, // 64: hyapp.room.v1.MicDownResponse.room:type_name -> hyapp.room.v1.RoomSnapshot 0, // 65: hyapp.room.v1.ChangeMicSeatRequest.meta:type_name -> hyapp.room.v1.RequestMeta 1, // 66: hyapp.room.v1.ChangeMicSeatResponse.result:type_name -> hyapp.room.v1.CommandResult - 30, // 67: hyapp.room.v1.ChangeMicSeatResponse.room:type_name -> hyapp.room.v1.RoomSnapshot + 31, // 67: hyapp.room.v1.ChangeMicSeatResponse.room:type_name -> hyapp.room.v1.RoomSnapshot 0, // 68: hyapp.room.v1.ConfirmMicPublishingRequest.meta:type_name -> hyapp.room.v1.RequestMeta 1, // 69: hyapp.room.v1.ConfirmMicPublishingResponse.result:type_name -> hyapp.room.v1.CommandResult - 30, // 70: hyapp.room.v1.ConfirmMicPublishingResponse.room:type_name -> hyapp.room.v1.RoomSnapshot + 31, // 70: hyapp.room.v1.ConfirmMicPublishingResponse.room:type_name -> hyapp.room.v1.RoomSnapshot 0, // 71: hyapp.room.v1.SetMicMuteRequest.meta:type_name -> hyapp.room.v1.RequestMeta 1, // 72: hyapp.room.v1.SetMicMuteResponse.result:type_name -> hyapp.room.v1.CommandResult - 30, // 73: hyapp.room.v1.SetMicMuteResponse.room:type_name -> hyapp.room.v1.RoomSnapshot + 31, // 73: hyapp.room.v1.SetMicMuteResponse.room:type_name -> hyapp.room.v1.RoomSnapshot 0, // 74: hyapp.room.v1.ApplyRTCEventRequest.meta:type_name -> hyapp.room.v1.RequestMeta 1, // 75: hyapp.room.v1.ApplyRTCEventResponse.result:type_name -> hyapp.room.v1.CommandResult - 30, // 76: hyapp.room.v1.ApplyRTCEventResponse.room:type_name -> hyapp.room.v1.RoomSnapshot + 31, // 76: hyapp.room.v1.ApplyRTCEventResponse.room:type_name -> hyapp.room.v1.RoomSnapshot 0, // 77: hyapp.room.v1.SetMicSeatLockRequest.meta:type_name -> hyapp.room.v1.RequestMeta 1, // 78: hyapp.room.v1.SetMicSeatLockResponse.result:type_name -> hyapp.room.v1.CommandResult - 30, // 79: hyapp.room.v1.SetMicSeatLockResponse.room:type_name -> hyapp.room.v1.RoomSnapshot + 31, // 79: hyapp.room.v1.SetMicSeatLockResponse.room:type_name -> hyapp.room.v1.RoomSnapshot 0, // 80: hyapp.room.v1.SetChatEnabledRequest.meta:type_name -> hyapp.room.v1.RequestMeta 1, // 81: hyapp.room.v1.SetChatEnabledResponse.result:type_name -> hyapp.room.v1.CommandResult - 30, // 82: hyapp.room.v1.SetChatEnabledResponse.room:type_name -> hyapp.room.v1.RoomSnapshot + 31, // 82: hyapp.room.v1.SetChatEnabledResponse.room:type_name -> hyapp.room.v1.RoomSnapshot 0, // 83: hyapp.room.v1.SetRoomPasswordRequest.meta:type_name -> hyapp.room.v1.RequestMeta 1, // 84: hyapp.room.v1.SetRoomPasswordResponse.result:type_name -> hyapp.room.v1.CommandResult - 30, // 85: hyapp.room.v1.SetRoomPasswordResponse.room:type_name -> hyapp.room.v1.RoomSnapshot + 31, // 85: hyapp.room.v1.SetRoomPasswordResponse.room:type_name -> hyapp.room.v1.RoomSnapshot 0, // 86: hyapp.room.v1.SetRoomAdminRequest.meta:type_name -> hyapp.room.v1.RequestMeta 1, // 87: hyapp.room.v1.SetRoomAdminResponse.result:type_name -> hyapp.room.v1.CommandResult - 30, // 88: hyapp.room.v1.SetRoomAdminResponse.room:type_name -> hyapp.room.v1.RoomSnapshot + 31, // 88: hyapp.room.v1.SetRoomAdminResponse.room:type_name -> hyapp.room.v1.RoomSnapshot 0, // 89: hyapp.room.v1.MuteUserRequest.meta:type_name -> hyapp.room.v1.RequestMeta 1, // 90: hyapp.room.v1.MuteUserResponse.result:type_name -> hyapp.room.v1.CommandResult - 30, // 91: hyapp.room.v1.MuteUserResponse.room:type_name -> hyapp.room.v1.RoomSnapshot + 31, // 91: hyapp.room.v1.MuteUserResponse.room:type_name -> hyapp.room.v1.RoomSnapshot 0, // 92: hyapp.room.v1.KickUserRequest.meta:type_name -> hyapp.room.v1.RequestMeta 1, // 93: hyapp.room.v1.KickUserResponse.result:type_name -> hyapp.room.v1.CommandResult - 30, // 94: hyapp.room.v1.KickUserResponse.room:type_name -> hyapp.room.v1.RoomSnapshot + 31, // 94: hyapp.room.v1.KickUserResponse.room:type_name -> hyapp.room.v1.RoomSnapshot 0, // 95: hyapp.room.v1.UnbanUserRequest.meta:type_name -> hyapp.room.v1.RequestMeta 1, // 96: hyapp.room.v1.UnbanUserResponse.result:type_name -> hyapp.room.v1.CommandResult - 30, // 97: hyapp.room.v1.UnbanUserResponse.room:type_name -> hyapp.room.v1.RoomSnapshot + 31, // 97: hyapp.room.v1.UnbanUserResponse.room:type_name -> hyapp.room.v1.RoomSnapshot 0, // 98: hyapp.room.v1.SystemEvictUserRequest.meta:type_name -> hyapp.room.v1.RequestMeta 1, // 99: hyapp.room.v1.SystemEvictUserResponse.result:type_name -> hyapp.room.v1.CommandResult - 30, // 100: hyapp.room.v1.SystemEvictUserResponse.room:type_name -> hyapp.room.v1.RoomSnapshot + 31, // 100: hyapp.room.v1.SystemEvictUserResponse.room:type_name -> hyapp.room.v1.RoomSnapshot 0, // 101: hyapp.room.v1.SendGiftRequest.meta:type_name -> hyapp.room.v1.RequestMeta 1, // 102: hyapp.room.v1.SendGiftResponse.result:type_name -> hyapp.room.v1.CommandResult - 4, // 103: hyapp.room.v1.SendGiftResponse.gift_rank:type_name -> hyapp.room.v1.RankItem - 30, // 104: hyapp.room.v1.SendGiftResponse.room:type_name -> hyapp.room.v1.RoomSnapshot - 9, // 105: hyapp.room.v1.SendGiftResponse.treasure:type_name -> hyapp.room.v1.RoomTreasureState - 5, // 106: hyapp.room.v1.SendGiftResponse.lucky_gift:type_name -> hyapp.room.v1.LuckyGiftDrawResult + 5, // 103: hyapp.room.v1.SendGiftResponse.gift_rank:type_name -> hyapp.room.v1.RankItem + 31, // 104: hyapp.room.v1.SendGiftResponse.room:type_name -> hyapp.room.v1.RoomSnapshot + 10, // 105: hyapp.room.v1.SendGiftResponse.treasure:type_name -> hyapp.room.v1.RoomTreasureState + 6, // 106: hyapp.room.v1.SendGiftResponse.lucky_gift:type_name -> hyapp.room.v1.LuckyGiftDrawResult 0, // 107: hyapp.room.v1.ListRoomsRequest.meta:type_name -> hyapp.room.v1.RequestMeta 0, // 108: hyapp.room.v1.ListRoomFeedsRequest.meta:type_name -> hyapp.room.v1.RequestMeta - 89, // 109: hyapp.room.v1.ListRoomFeedsRequest.related_users:type_name -> hyapp.room.v1.RoomFeedRelatedUser + 90, // 109: hyapp.room.v1.ListRoomFeedsRequest.related_users:type_name -> hyapp.room.v1.RoomFeedRelatedUser 0, // 110: hyapp.room.v1.ListRoomGiftLeaderboardRequest.meta:type_name -> hyapp.room.v1.RequestMeta - 90, // 111: hyapp.room.v1.ListRoomsResponse.rooms:type_name -> hyapp.room.v1.RoomListItem - 90, // 112: hyapp.room.v1.RoomGiftLeaderboardItem.room:type_name -> hyapp.room.v1.RoomListItem - 92, // 113: hyapp.room.v1.ListRoomGiftLeaderboardResponse.items:type_name -> hyapp.room.v1.RoomGiftLeaderboardItem + 91, // 111: hyapp.room.v1.ListRoomsResponse.rooms:type_name -> hyapp.room.v1.RoomListItem + 91, // 112: hyapp.room.v1.RoomGiftLeaderboardItem.room:type_name -> hyapp.room.v1.RoomListItem + 93, // 113: hyapp.room.v1.ListRoomGiftLeaderboardResponse.items:type_name -> hyapp.room.v1.RoomGiftLeaderboardItem 0, // 114: hyapp.room.v1.GetMyRoomRequest.meta:type_name -> hyapp.room.v1.RequestMeta - 90, // 115: hyapp.room.v1.GetMyRoomResponse.room:type_name -> hyapp.room.v1.RoomListItem + 91, // 115: hyapp.room.v1.GetMyRoomResponse.room:type_name -> hyapp.room.v1.RoomListItem 0, // 116: hyapp.room.v1.GetCurrentRoomRequest.meta:type_name -> hyapp.room.v1.RequestMeta 0, // 117: hyapp.room.v1.GetRoomSnapshotRequest.meta:type_name -> hyapp.room.v1.RequestMeta - 30, // 118: hyapp.room.v1.GetRoomSnapshotResponse.room:type_name -> hyapp.room.v1.RoomSnapshot + 31, // 118: hyapp.room.v1.GetRoomSnapshotResponse.room:type_name -> hyapp.room.v1.RoomSnapshot 0, // 119: hyapp.room.v1.GetRoomTreasureRequest.meta:type_name -> hyapp.room.v1.RequestMeta - 10, // 120: hyapp.room.v1.GetRoomTreasureResponse.treasure:type_name -> hyapp.room.v1.RoomTreasureInfo + 11, // 120: hyapp.room.v1.GetRoomTreasureResponse.treasure:type_name -> hyapp.room.v1.RoomTreasureInfo 0, // 121: hyapp.room.v1.ListRoomOnlineUsersRequest.meta:type_name -> hyapp.room.v1.RequestMeta 2, // 122: hyapp.room.v1.ListRoomOnlineUsersResponse.users:type_name -> hyapp.room.v1.RoomUser - 0, // 123: hyapp.room.v1.FollowRoomRequest.meta:type_name -> hyapp.room.v1.RequestMeta - 0, // 124: hyapp.room.v1.UnfollowRoomRequest.meta:type_name -> hyapp.room.v1.RequestMeta - 31, // 125: hyapp.room.v1.RoomCommandService.CreateRoom:input_type -> hyapp.room.v1.CreateRoomRequest - 33, // 126: hyapp.room.v1.RoomCommandService.UpdateRoomProfile:input_type -> hyapp.room.v1.UpdateRoomProfileRequest - 35, // 127: hyapp.room.v1.RoomCommandService.JoinRoom:input_type -> hyapp.room.v1.JoinRoomRequest - 37, // 128: hyapp.room.v1.RoomCommandService.RoomHeartbeat:input_type -> hyapp.room.v1.RoomHeartbeatRequest - 39, // 129: hyapp.room.v1.RoomCommandService.LeaveRoom:input_type -> hyapp.room.v1.LeaveRoomRequest - 41, // 130: hyapp.room.v1.RoomCommandService.CloseRoom:input_type -> hyapp.room.v1.CloseRoomRequest - 48, // 131: hyapp.room.v1.RoomCommandService.AdminUpdateRoom:input_type -> hyapp.room.v1.AdminUpdateRoomRequest - 50, // 132: hyapp.room.v1.RoomCommandService.AdminDeleteRoom:input_type -> hyapp.room.v1.AdminDeleteRoomRequest - 15, // 133: hyapp.room.v1.RoomCommandService.AdminUpdateRoomTreasureConfig:input_type -> hyapp.room.v1.AdminUpdateRoomTreasureConfigRequest - 20, // 134: hyapp.room.v1.RoomCommandService.AdminUpdateRoomSeatConfig:input_type -> hyapp.room.v1.AdminUpdateRoomSeatConfigRequest - 26, // 135: hyapp.room.v1.RoomCommandService.AdminCreateRoomPin:input_type -> hyapp.room.v1.AdminCreateRoomPinRequest - 28, // 136: hyapp.room.v1.RoomCommandService.AdminCancelRoomPin:input_type -> hyapp.room.v1.AdminCancelRoomPinRequest - 52, // 137: hyapp.room.v1.RoomCommandService.MicUp:input_type -> hyapp.room.v1.MicUpRequest - 54, // 138: hyapp.room.v1.RoomCommandService.MicDown:input_type -> hyapp.room.v1.MicDownRequest - 56, // 139: hyapp.room.v1.RoomCommandService.ChangeMicSeat:input_type -> hyapp.room.v1.ChangeMicSeatRequest - 58, // 140: hyapp.room.v1.RoomCommandService.ConfirmMicPublishing:input_type -> hyapp.room.v1.ConfirmMicPublishingRequest - 60, // 141: hyapp.room.v1.RoomCommandService.SetMicMute:input_type -> hyapp.room.v1.SetMicMuteRequest - 62, // 142: hyapp.room.v1.RoomCommandService.ApplyRTCEvent:input_type -> hyapp.room.v1.ApplyRTCEventRequest - 64, // 143: hyapp.room.v1.RoomCommandService.SetMicSeatLock:input_type -> hyapp.room.v1.SetMicSeatLockRequest - 66, // 144: hyapp.room.v1.RoomCommandService.SetChatEnabled:input_type -> hyapp.room.v1.SetChatEnabledRequest - 68, // 145: hyapp.room.v1.RoomCommandService.SetRoomPassword:input_type -> hyapp.room.v1.SetRoomPasswordRequest - 70, // 146: hyapp.room.v1.RoomCommandService.SetRoomAdmin:input_type -> hyapp.room.v1.SetRoomAdminRequest - 72, // 147: hyapp.room.v1.RoomCommandService.MuteUser:input_type -> hyapp.room.v1.MuteUserRequest - 74, // 148: hyapp.room.v1.RoomCommandService.KickUser:input_type -> hyapp.room.v1.KickUserRequest - 76, // 149: hyapp.room.v1.RoomCommandService.UnbanUser:input_type -> hyapp.room.v1.UnbanUserRequest - 78, // 150: hyapp.room.v1.RoomCommandService.SystemEvictUser:input_type -> hyapp.room.v1.SystemEvictUserRequest - 80, // 151: hyapp.room.v1.RoomCommandService.SendGift:input_type -> hyapp.room.v1.SendGiftRequest - 104, // 152: hyapp.room.v1.RoomCommandService.FollowRoom:input_type -> hyapp.room.v1.FollowRoomRequest - 106, // 153: hyapp.room.v1.RoomCommandService.UnfollowRoom:input_type -> hyapp.room.v1.UnfollowRoomRequest - 82, // 154: hyapp.room.v1.RoomGuardService.CheckSpeakPermission:input_type -> hyapp.room.v1.CheckSpeakPermissionRequest - 84, // 155: hyapp.room.v1.RoomGuardService.VerifyRoomPresence:input_type -> hyapp.room.v1.VerifyRoomPresenceRequest - 44, // 156: hyapp.room.v1.RoomQueryService.AdminListRooms:input_type -> hyapp.room.v1.AdminListRoomsRequest - 46, // 157: hyapp.room.v1.RoomQueryService.AdminGetRoom:input_type -> hyapp.room.v1.AdminGetRoomRequest - 13, // 158: hyapp.room.v1.RoomQueryService.AdminGetRoomTreasureConfig:input_type -> hyapp.room.v1.AdminGetRoomTreasureConfigRequest - 18, // 159: hyapp.room.v1.RoomQueryService.AdminGetRoomSeatConfig:input_type -> hyapp.room.v1.AdminGetRoomSeatConfigRequest - 24, // 160: hyapp.room.v1.RoomQueryService.AdminListRoomPins:input_type -> hyapp.room.v1.AdminListRoomPinsRequest - 86, // 161: hyapp.room.v1.RoomQueryService.ListRooms:input_type -> hyapp.room.v1.ListRoomsRequest - 87, // 162: hyapp.room.v1.RoomQueryService.ListRoomFeeds:input_type -> hyapp.room.v1.ListRoomFeedsRequest - 88, // 163: hyapp.room.v1.RoomQueryService.ListRoomGiftLeaderboard:input_type -> hyapp.room.v1.ListRoomGiftLeaderboardRequest - 94, // 164: hyapp.room.v1.RoomQueryService.GetMyRoom:input_type -> hyapp.room.v1.GetMyRoomRequest - 96, // 165: hyapp.room.v1.RoomQueryService.GetCurrentRoom:input_type -> hyapp.room.v1.GetCurrentRoomRequest - 98, // 166: hyapp.room.v1.RoomQueryService.GetRoomSnapshot:input_type -> hyapp.room.v1.GetRoomSnapshotRequest - 100, // 167: hyapp.room.v1.RoomQueryService.GetRoomTreasure:input_type -> hyapp.room.v1.GetRoomTreasureRequest - 102, // 168: hyapp.room.v1.RoomQueryService.ListRoomOnlineUsers:input_type -> hyapp.room.v1.ListRoomOnlineUsersRequest - 32, // 169: hyapp.room.v1.RoomCommandService.CreateRoom:output_type -> hyapp.room.v1.CreateRoomResponse - 34, // 170: hyapp.room.v1.RoomCommandService.UpdateRoomProfile:output_type -> hyapp.room.v1.UpdateRoomProfileResponse - 36, // 171: hyapp.room.v1.RoomCommandService.JoinRoom:output_type -> hyapp.room.v1.JoinRoomResponse - 38, // 172: hyapp.room.v1.RoomCommandService.RoomHeartbeat:output_type -> hyapp.room.v1.RoomHeartbeatResponse - 40, // 173: hyapp.room.v1.RoomCommandService.LeaveRoom:output_type -> hyapp.room.v1.LeaveRoomResponse - 42, // 174: hyapp.room.v1.RoomCommandService.CloseRoom:output_type -> hyapp.room.v1.CloseRoomResponse - 49, // 175: hyapp.room.v1.RoomCommandService.AdminUpdateRoom:output_type -> hyapp.room.v1.AdminUpdateRoomResponse - 51, // 176: hyapp.room.v1.RoomCommandService.AdminDeleteRoom:output_type -> hyapp.room.v1.AdminDeleteRoomResponse - 16, // 177: hyapp.room.v1.RoomCommandService.AdminUpdateRoomTreasureConfig:output_type -> hyapp.room.v1.AdminUpdateRoomTreasureConfigResponse - 21, // 178: hyapp.room.v1.RoomCommandService.AdminUpdateRoomSeatConfig:output_type -> hyapp.room.v1.AdminUpdateRoomSeatConfigResponse - 27, // 179: hyapp.room.v1.RoomCommandService.AdminCreateRoomPin:output_type -> hyapp.room.v1.AdminCreateRoomPinResponse - 29, // 180: hyapp.room.v1.RoomCommandService.AdminCancelRoomPin:output_type -> hyapp.room.v1.AdminCancelRoomPinResponse - 53, // 181: hyapp.room.v1.RoomCommandService.MicUp:output_type -> hyapp.room.v1.MicUpResponse - 55, // 182: hyapp.room.v1.RoomCommandService.MicDown:output_type -> hyapp.room.v1.MicDownResponse - 57, // 183: hyapp.room.v1.RoomCommandService.ChangeMicSeat:output_type -> hyapp.room.v1.ChangeMicSeatResponse - 59, // 184: hyapp.room.v1.RoomCommandService.ConfirmMicPublishing:output_type -> hyapp.room.v1.ConfirmMicPublishingResponse - 61, // 185: hyapp.room.v1.RoomCommandService.SetMicMute:output_type -> hyapp.room.v1.SetMicMuteResponse - 63, // 186: hyapp.room.v1.RoomCommandService.ApplyRTCEvent:output_type -> hyapp.room.v1.ApplyRTCEventResponse - 65, // 187: hyapp.room.v1.RoomCommandService.SetMicSeatLock:output_type -> hyapp.room.v1.SetMicSeatLockResponse - 67, // 188: hyapp.room.v1.RoomCommandService.SetChatEnabled:output_type -> hyapp.room.v1.SetChatEnabledResponse - 69, // 189: hyapp.room.v1.RoomCommandService.SetRoomPassword:output_type -> hyapp.room.v1.SetRoomPasswordResponse - 71, // 190: hyapp.room.v1.RoomCommandService.SetRoomAdmin:output_type -> hyapp.room.v1.SetRoomAdminResponse - 73, // 191: hyapp.room.v1.RoomCommandService.MuteUser:output_type -> hyapp.room.v1.MuteUserResponse - 75, // 192: hyapp.room.v1.RoomCommandService.KickUser:output_type -> hyapp.room.v1.KickUserResponse - 77, // 193: hyapp.room.v1.RoomCommandService.UnbanUser:output_type -> hyapp.room.v1.UnbanUserResponse - 79, // 194: hyapp.room.v1.RoomCommandService.SystemEvictUser:output_type -> hyapp.room.v1.SystemEvictUserResponse - 81, // 195: hyapp.room.v1.RoomCommandService.SendGift:output_type -> hyapp.room.v1.SendGiftResponse - 105, // 196: hyapp.room.v1.RoomCommandService.FollowRoom:output_type -> hyapp.room.v1.FollowRoomResponse - 107, // 197: hyapp.room.v1.RoomCommandService.UnfollowRoom:output_type -> hyapp.room.v1.UnfollowRoomResponse - 83, // 198: hyapp.room.v1.RoomGuardService.CheckSpeakPermission:output_type -> hyapp.room.v1.CheckSpeakPermissionResponse - 85, // 199: hyapp.room.v1.RoomGuardService.VerifyRoomPresence:output_type -> hyapp.room.v1.VerifyRoomPresenceResponse - 45, // 200: hyapp.room.v1.RoomQueryService.AdminListRooms:output_type -> hyapp.room.v1.AdminListRoomsResponse - 47, // 201: hyapp.room.v1.RoomQueryService.AdminGetRoom:output_type -> hyapp.room.v1.AdminGetRoomResponse - 14, // 202: hyapp.room.v1.RoomQueryService.AdminGetRoomTreasureConfig:output_type -> hyapp.room.v1.AdminGetRoomTreasureConfigResponse - 19, // 203: hyapp.room.v1.RoomQueryService.AdminGetRoomSeatConfig:output_type -> hyapp.room.v1.AdminGetRoomSeatConfigResponse - 25, // 204: hyapp.room.v1.RoomQueryService.AdminListRoomPins:output_type -> hyapp.room.v1.AdminListRoomPinsResponse - 91, // 205: hyapp.room.v1.RoomQueryService.ListRooms:output_type -> hyapp.room.v1.ListRoomsResponse - 91, // 206: hyapp.room.v1.RoomQueryService.ListRoomFeeds:output_type -> hyapp.room.v1.ListRoomsResponse - 93, // 207: hyapp.room.v1.RoomQueryService.ListRoomGiftLeaderboard:output_type -> hyapp.room.v1.ListRoomGiftLeaderboardResponse - 95, // 208: hyapp.room.v1.RoomQueryService.GetMyRoom:output_type -> hyapp.room.v1.GetMyRoomResponse - 97, // 209: hyapp.room.v1.RoomQueryService.GetCurrentRoom:output_type -> hyapp.room.v1.GetCurrentRoomResponse - 99, // 210: hyapp.room.v1.RoomQueryService.GetRoomSnapshot:output_type -> hyapp.room.v1.GetRoomSnapshotResponse - 101, // 211: hyapp.room.v1.RoomQueryService.GetRoomTreasure:output_type -> hyapp.room.v1.GetRoomTreasureResponse - 103, // 212: hyapp.room.v1.RoomQueryService.ListRoomOnlineUsers:output_type -> hyapp.room.v1.ListRoomOnlineUsersResponse - 169, // [169:213] is the sub-list for method output_type - 125, // [125:169] is the sub-list for method input_type - 125, // [125:125] is the sub-list for extension type_name - 125, // [125:125] is the sub-list for extension extendee - 0, // [0:125] is the sub-list for field type_name + 3, // 123: hyapp.room.v1.ListRoomOnlineUsersResponse.items:type_name -> hyapp.room.v1.RoomOnlineUser + 0, // 124: hyapp.room.v1.FollowRoomRequest.meta:type_name -> hyapp.room.v1.RequestMeta + 0, // 125: hyapp.room.v1.UnfollowRoomRequest.meta:type_name -> hyapp.room.v1.RequestMeta + 32, // 126: hyapp.room.v1.RoomCommandService.CreateRoom:input_type -> hyapp.room.v1.CreateRoomRequest + 34, // 127: hyapp.room.v1.RoomCommandService.UpdateRoomProfile:input_type -> hyapp.room.v1.UpdateRoomProfileRequest + 36, // 128: hyapp.room.v1.RoomCommandService.JoinRoom:input_type -> hyapp.room.v1.JoinRoomRequest + 38, // 129: hyapp.room.v1.RoomCommandService.RoomHeartbeat:input_type -> hyapp.room.v1.RoomHeartbeatRequest + 40, // 130: hyapp.room.v1.RoomCommandService.LeaveRoom:input_type -> hyapp.room.v1.LeaveRoomRequest + 42, // 131: hyapp.room.v1.RoomCommandService.CloseRoom:input_type -> hyapp.room.v1.CloseRoomRequest + 49, // 132: hyapp.room.v1.RoomCommandService.AdminUpdateRoom:input_type -> hyapp.room.v1.AdminUpdateRoomRequest + 51, // 133: hyapp.room.v1.RoomCommandService.AdminDeleteRoom:input_type -> hyapp.room.v1.AdminDeleteRoomRequest + 16, // 134: hyapp.room.v1.RoomCommandService.AdminUpdateRoomTreasureConfig:input_type -> hyapp.room.v1.AdminUpdateRoomTreasureConfigRequest + 21, // 135: hyapp.room.v1.RoomCommandService.AdminUpdateRoomSeatConfig:input_type -> hyapp.room.v1.AdminUpdateRoomSeatConfigRequest + 27, // 136: hyapp.room.v1.RoomCommandService.AdminCreateRoomPin:input_type -> hyapp.room.v1.AdminCreateRoomPinRequest + 29, // 137: hyapp.room.v1.RoomCommandService.AdminCancelRoomPin:input_type -> hyapp.room.v1.AdminCancelRoomPinRequest + 53, // 138: hyapp.room.v1.RoomCommandService.MicUp:input_type -> hyapp.room.v1.MicUpRequest + 55, // 139: hyapp.room.v1.RoomCommandService.MicDown:input_type -> hyapp.room.v1.MicDownRequest + 57, // 140: hyapp.room.v1.RoomCommandService.ChangeMicSeat:input_type -> hyapp.room.v1.ChangeMicSeatRequest + 59, // 141: hyapp.room.v1.RoomCommandService.ConfirmMicPublishing:input_type -> hyapp.room.v1.ConfirmMicPublishingRequest + 61, // 142: hyapp.room.v1.RoomCommandService.SetMicMute:input_type -> hyapp.room.v1.SetMicMuteRequest + 63, // 143: hyapp.room.v1.RoomCommandService.ApplyRTCEvent:input_type -> hyapp.room.v1.ApplyRTCEventRequest + 65, // 144: hyapp.room.v1.RoomCommandService.SetMicSeatLock:input_type -> hyapp.room.v1.SetMicSeatLockRequest + 67, // 145: hyapp.room.v1.RoomCommandService.SetChatEnabled:input_type -> hyapp.room.v1.SetChatEnabledRequest + 69, // 146: hyapp.room.v1.RoomCommandService.SetRoomPassword:input_type -> hyapp.room.v1.SetRoomPasswordRequest + 71, // 147: hyapp.room.v1.RoomCommandService.SetRoomAdmin:input_type -> hyapp.room.v1.SetRoomAdminRequest + 73, // 148: hyapp.room.v1.RoomCommandService.MuteUser:input_type -> hyapp.room.v1.MuteUserRequest + 75, // 149: hyapp.room.v1.RoomCommandService.KickUser:input_type -> hyapp.room.v1.KickUserRequest + 77, // 150: hyapp.room.v1.RoomCommandService.UnbanUser:input_type -> hyapp.room.v1.UnbanUserRequest + 79, // 151: hyapp.room.v1.RoomCommandService.SystemEvictUser:input_type -> hyapp.room.v1.SystemEvictUserRequest + 81, // 152: hyapp.room.v1.RoomCommandService.SendGift:input_type -> hyapp.room.v1.SendGiftRequest + 105, // 153: hyapp.room.v1.RoomCommandService.FollowRoom:input_type -> hyapp.room.v1.FollowRoomRequest + 107, // 154: hyapp.room.v1.RoomCommandService.UnfollowRoom:input_type -> hyapp.room.v1.UnfollowRoomRequest + 83, // 155: hyapp.room.v1.RoomGuardService.CheckSpeakPermission:input_type -> hyapp.room.v1.CheckSpeakPermissionRequest + 85, // 156: hyapp.room.v1.RoomGuardService.VerifyRoomPresence:input_type -> hyapp.room.v1.VerifyRoomPresenceRequest + 45, // 157: hyapp.room.v1.RoomQueryService.AdminListRooms:input_type -> hyapp.room.v1.AdminListRoomsRequest + 47, // 158: hyapp.room.v1.RoomQueryService.AdminGetRoom:input_type -> hyapp.room.v1.AdminGetRoomRequest + 14, // 159: hyapp.room.v1.RoomQueryService.AdminGetRoomTreasureConfig:input_type -> hyapp.room.v1.AdminGetRoomTreasureConfigRequest + 19, // 160: hyapp.room.v1.RoomQueryService.AdminGetRoomSeatConfig:input_type -> hyapp.room.v1.AdminGetRoomSeatConfigRequest + 25, // 161: hyapp.room.v1.RoomQueryService.AdminListRoomPins:input_type -> hyapp.room.v1.AdminListRoomPinsRequest + 87, // 162: hyapp.room.v1.RoomQueryService.ListRooms:input_type -> hyapp.room.v1.ListRoomsRequest + 88, // 163: hyapp.room.v1.RoomQueryService.ListRoomFeeds:input_type -> hyapp.room.v1.ListRoomFeedsRequest + 89, // 164: hyapp.room.v1.RoomQueryService.ListRoomGiftLeaderboard:input_type -> hyapp.room.v1.ListRoomGiftLeaderboardRequest + 95, // 165: hyapp.room.v1.RoomQueryService.GetMyRoom:input_type -> hyapp.room.v1.GetMyRoomRequest + 97, // 166: hyapp.room.v1.RoomQueryService.GetCurrentRoom:input_type -> hyapp.room.v1.GetCurrentRoomRequest + 99, // 167: hyapp.room.v1.RoomQueryService.GetRoomSnapshot:input_type -> hyapp.room.v1.GetRoomSnapshotRequest + 101, // 168: hyapp.room.v1.RoomQueryService.GetRoomTreasure:input_type -> hyapp.room.v1.GetRoomTreasureRequest + 103, // 169: hyapp.room.v1.RoomQueryService.ListRoomOnlineUsers:input_type -> hyapp.room.v1.ListRoomOnlineUsersRequest + 33, // 170: hyapp.room.v1.RoomCommandService.CreateRoom:output_type -> hyapp.room.v1.CreateRoomResponse + 35, // 171: hyapp.room.v1.RoomCommandService.UpdateRoomProfile:output_type -> hyapp.room.v1.UpdateRoomProfileResponse + 37, // 172: hyapp.room.v1.RoomCommandService.JoinRoom:output_type -> hyapp.room.v1.JoinRoomResponse + 39, // 173: hyapp.room.v1.RoomCommandService.RoomHeartbeat:output_type -> hyapp.room.v1.RoomHeartbeatResponse + 41, // 174: hyapp.room.v1.RoomCommandService.LeaveRoom:output_type -> hyapp.room.v1.LeaveRoomResponse + 43, // 175: hyapp.room.v1.RoomCommandService.CloseRoom:output_type -> hyapp.room.v1.CloseRoomResponse + 50, // 176: hyapp.room.v1.RoomCommandService.AdminUpdateRoom:output_type -> hyapp.room.v1.AdminUpdateRoomResponse + 52, // 177: hyapp.room.v1.RoomCommandService.AdminDeleteRoom:output_type -> hyapp.room.v1.AdminDeleteRoomResponse + 17, // 178: hyapp.room.v1.RoomCommandService.AdminUpdateRoomTreasureConfig:output_type -> hyapp.room.v1.AdminUpdateRoomTreasureConfigResponse + 22, // 179: hyapp.room.v1.RoomCommandService.AdminUpdateRoomSeatConfig:output_type -> hyapp.room.v1.AdminUpdateRoomSeatConfigResponse + 28, // 180: hyapp.room.v1.RoomCommandService.AdminCreateRoomPin:output_type -> hyapp.room.v1.AdminCreateRoomPinResponse + 30, // 181: hyapp.room.v1.RoomCommandService.AdminCancelRoomPin:output_type -> hyapp.room.v1.AdminCancelRoomPinResponse + 54, // 182: hyapp.room.v1.RoomCommandService.MicUp:output_type -> hyapp.room.v1.MicUpResponse + 56, // 183: hyapp.room.v1.RoomCommandService.MicDown:output_type -> hyapp.room.v1.MicDownResponse + 58, // 184: hyapp.room.v1.RoomCommandService.ChangeMicSeat:output_type -> hyapp.room.v1.ChangeMicSeatResponse + 60, // 185: hyapp.room.v1.RoomCommandService.ConfirmMicPublishing:output_type -> hyapp.room.v1.ConfirmMicPublishingResponse + 62, // 186: hyapp.room.v1.RoomCommandService.SetMicMute:output_type -> hyapp.room.v1.SetMicMuteResponse + 64, // 187: hyapp.room.v1.RoomCommandService.ApplyRTCEvent:output_type -> hyapp.room.v1.ApplyRTCEventResponse + 66, // 188: hyapp.room.v1.RoomCommandService.SetMicSeatLock:output_type -> hyapp.room.v1.SetMicSeatLockResponse + 68, // 189: hyapp.room.v1.RoomCommandService.SetChatEnabled:output_type -> hyapp.room.v1.SetChatEnabledResponse + 70, // 190: hyapp.room.v1.RoomCommandService.SetRoomPassword:output_type -> hyapp.room.v1.SetRoomPasswordResponse + 72, // 191: hyapp.room.v1.RoomCommandService.SetRoomAdmin:output_type -> hyapp.room.v1.SetRoomAdminResponse + 74, // 192: hyapp.room.v1.RoomCommandService.MuteUser:output_type -> hyapp.room.v1.MuteUserResponse + 76, // 193: hyapp.room.v1.RoomCommandService.KickUser:output_type -> hyapp.room.v1.KickUserResponse + 78, // 194: hyapp.room.v1.RoomCommandService.UnbanUser:output_type -> hyapp.room.v1.UnbanUserResponse + 80, // 195: hyapp.room.v1.RoomCommandService.SystemEvictUser:output_type -> hyapp.room.v1.SystemEvictUserResponse + 82, // 196: hyapp.room.v1.RoomCommandService.SendGift:output_type -> hyapp.room.v1.SendGiftResponse + 106, // 197: hyapp.room.v1.RoomCommandService.FollowRoom:output_type -> hyapp.room.v1.FollowRoomResponse + 108, // 198: hyapp.room.v1.RoomCommandService.UnfollowRoom:output_type -> hyapp.room.v1.UnfollowRoomResponse + 84, // 199: hyapp.room.v1.RoomGuardService.CheckSpeakPermission:output_type -> hyapp.room.v1.CheckSpeakPermissionResponse + 86, // 200: hyapp.room.v1.RoomGuardService.VerifyRoomPresence:output_type -> hyapp.room.v1.VerifyRoomPresenceResponse + 46, // 201: hyapp.room.v1.RoomQueryService.AdminListRooms:output_type -> hyapp.room.v1.AdminListRoomsResponse + 48, // 202: hyapp.room.v1.RoomQueryService.AdminGetRoom:output_type -> hyapp.room.v1.AdminGetRoomResponse + 15, // 203: hyapp.room.v1.RoomQueryService.AdminGetRoomTreasureConfig:output_type -> hyapp.room.v1.AdminGetRoomTreasureConfigResponse + 20, // 204: hyapp.room.v1.RoomQueryService.AdminGetRoomSeatConfig:output_type -> hyapp.room.v1.AdminGetRoomSeatConfigResponse + 26, // 205: hyapp.room.v1.RoomQueryService.AdminListRoomPins:output_type -> hyapp.room.v1.AdminListRoomPinsResponse + 92, // 206: hyapp.room.v1.RoomQueryService.ListRooms:output_type -> hyapp.room.v1.ListRoomsResponse + 92, // 207: hyapp.room.v1.RoomQueryService.ListRoomFeeds:output_type -> hyapp.room.v1.ListRoomsResponse + 94, // 208: hyapp.room.v1.RoomQueryService.ListRoomGiftLeaderboard:output_type -> hyapp.room.v1.ListRoomGiftLeaderboardResponse + 96, // 209: hyapp.room.v1.RoomQueryService.GetMyRoom:output_type -> hyapp.room.v1.GetMyRoomResponse + 98, // 210: hyapp.room.v1.RoomQueryService.GetCurrentRoom:output_type -> hyapp.room.v1.GetCurrentRoomResponse + 100, // 211: hyapp.room.v1.RoomQueryService.GetRoomSnapshot:output_type -> hyapp.room.v1.GetRoomSnapshotResponse + 102, // 212: hyapp.room.v1.RoomQueryService.GetRoomTreasure:output_type -> hyapp.room.v1.GetRoomTreasureResponse + 104, // 213: hyapp.room.v1.RoomQueryService.ListRoomOnlineUsers:output_type -> hyapp.room.v1.ListRoomOnlineUsersResponse + 170, // [170:214] is the sub-list for method output_type + 126, // [126:170] is the sub-list for method input_type + 126, // [126:126] is the sub-list for extension type_name + 126, // [126:126] is the sub-list for extension extendee + 0, // [0:126] is the sub-list for field type_name } func init() { file_proto_room_v1_room_proto_init() } @@ -8992,15 +9116,15 @@ func file_proto_room_v1_room_proto_init() { if File_proto_room_v1_room_proto != nil { return } - file_proto_room_v1_room_proto_msgTypes[33].OneofWrappers = []any{} - file_proto_room_v1_room_proto_msgTypes[48].OneofWrappers = []any{} + file_proto_room_v1_room_proto_msgTypes[34].OneofWrappers = []any{} + file_proto_room_v1_room_proto_msgTypes[49].OneofWrappers = []any{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_proto_room_v1_room_proto_rawDesc), len(file_proto_room_v1_room_proto_rawDesc)), NumEnums: 0, - NumMessages: 109, + NumMessages: 110, NumExtensions: 0, NumServices: 3, }, diff --git a/api/proto/room/v1/room.proto b/api/proto/room/v1/room.proto index a7843afd..a131c2c2 100644 --- a/api/proto/room/v1/room.proto +++ b/api/proto/room/v1/room.proto @@ -34,6 +34,18 @@ message RoomUser { int64 last_seen_at_ms = 4; } +// RoomOnlineUser 是在线用户列表的展示读模型。 +// role 保留进房 presence 语义;room_role 只表达房间管理员身份;is_owner 单独表达房主身份。 +message RoomOnlineUser { + int64 user_id = 1; + string role = 2; + string room_role = 3; + int64 gift_value = 4; + int64 joined_at_ms = 5; + int64 last_seen_at_ms = 6; + bool is_owner = 7; +} + // SeatState 表达单个麦位当前占用和 RTC 发流确认状态。 message SeatState { int32 seat_no = 1; @@ -903,6 +915,7 @@ message ListRoomOnlineUsersRequest { int64 viewer_user_id = 3; int32 page = 4; int32 page_size = 5; + string sort = 6; } message ListRoomOnlineUsersResponse { @@ -911,6 +924,7 @@ message ListRoomOnlineUsersResponse { int32 page = 3; int32 page_size = 4; int64 server_time_ms = 5; + repeated RoomOnlineUser items = 6; } // FollowRoomRequest 建立当前用户对房间的关注关系。 diff --git a/docs/flutter对接/幸运礼物Flutter对接.md b/docs/flutter对接/幸运礼物Flutter对接.md index 925dbad2..45d24261 100644 --- a/docs/flutter对接/幸运礼物Flutter对接.md +++ b/docs/flutter对接/幸运礼物Flutter对接.md @@ -73,7 +73,7 @@ App 不要展示后台 RTP 数值,不要展示实时概率,不要本地推 1. 进入房间后调用 `GET /api/v1/rooms/{room_id}/gift-panel` 获取礼物面板、金币余额和收礼人。 2. 找到 `gift_type_code=lucky` 或 `gift_type_code=super_lucky` 的礼物,读取该礼物对应的 `pool_id`。当前可从 `presentation_json.lucky_pool_id` 下发,后续建议提升为顶层字段 `lucky_pool_id`。 -3. 用户点击幸运礼物前,可调用 `POST /api/v1/activities/lucky-gifts/check` 判断奖池是否启用。 +3. 用户点击幸运礼物前,可调用 `POST /api/v1/activities/lucky-gifts/check` 判断奖池是否启用;当前 check 接口要求 `pool_id` 必填。 4. 用户确认送礼时调用 `POST /api/v1/rooms/gift/send`,请求体带上 `pool_id`。 5. 成功响应里如果有 `lucky_gift`,立即播放中奖或未中奖反馈。 6. 房间内监听 `room_gift_sent` 和 `lucky_gift_drawn` IM,给其他用户同步展示。 @@ -134,7 +134,7 @@ X-App-Code: lalu ## 检查幸运礼物状态 -建议新增 App HTTP 接口: +已实现 App HTTP 接口: ```http POST /api/v1/activities/lucky-gifts/check @@ -159,7 +159,7 @@ Content-Type: application/json | --- | --- | --- | --- | | `room_id` | string | 是 | 当前房间 ID。 | | `gift_id` | string | 是 | 用户准备送出的幸运礼物 ID。 | -| `pool_id` | string | 否 | 奖池 ID;为空时服务端使用 `default`。 | +| `pool_id` | string | 是 | 奖池 ID;当前 gateway check 接口必填。 | 成功响应: @@ -200,6 +200,8 @@ Content-Type: application/json | `enabled=false` | 置灰幸运抽奖入口,但普通礼物展示可继续。 | | 接口失败 | 不在本地兜底开奖;可以提示稍后重试或隐藏幸运玩法。 | +注意:`room-service` 内部仍会把空 `pool_id` 归一化为默认奖池,但 App check 接口不接受空值。客户端必须从礼物面板拿到明确 `pool_id` 后再走幸运礼物流程。 + ## 送礼并抽奖 已存在 App HTTP 入口: @@ -236,7 +238,7 @@ Content-Type: application/json | `target_user_id` | int64 | 否 | 单目标兼容字段;新代码优先用 `target_user_ids`。 | | `gift_id` | string | 是 | 礼物 ID。 | | `gift_count` | int32 | 是 | 礼物数量,必须大于 0。 | -| `pool_id` | string | 否 | 幸运礼物奖池 ID;不同奖池完全隔离。普通礼物可不传。 | +| `pool_id` | string | 幸运礼物必填 | 幸运礼物奖池 ID;不同奖池完全隔离。普通礼物可不传。 | 成功响应节选: @@ -279,7 +281,7 @@ Content-Type: application/json } ``` -`lucky_gift` 字段为建议新增。普通礼物或奖池未启用时可以为空。 +`lucky_gift` 字段已实现。普通礼物或未执行幸运礼物抽奖时可以为空。 `data` 字段: @@ -293,7 +295,7 @@ Content-Type: application/json | `gift_rank` | array | 房间礼物榜快照。 | | `room` | object | 房间快照。 | | `treasure` | object | 宝箱状态;如果房间宝箱开启可直接更新。 | -| `lucky_gift` | object | 幸运礼物抽奖结果;建议新增。 | +| `lucky_gift` | object | 幸运礼物抽奖结果;已实现。 | `lucky_gift` 字段: @@ -307,7 +309,7 @@ Content-Type: application/json | `rule_version` | int64 | 抽奖时使用的规则版本。 | | `experience_pool` | string | `novice`、`intermediate`、`advanced`。 | | `selected_tier_id` | string | 服务端选中的奖档 ID。 | -| `multiplier_ppm` | int64 | 建议新增;用于直接展示倍率。后端暂未返回时客户端不要本地解析倍率。 | +| `multiplier_ppm` | int64 | 已返回;用于直接展示倍率。客户端不要从 `selected_tier_id` 解析倍率。 | | `base_reward_coins` | int64 | 基础 RTP 返奖。 | | `room_atmosphere_reward_coins` | int64 | 房间气氛补贴奖励。 | | `activity_subsidy_coins` | int64 | 活动补贴奖励。 | @@ -407,7 +409,7 @@ Payload 示例: ## IM:幸运礼物开奖结果 -建议新增房间 IM: +已实现房间 IM: | 字段 | 值 | | --- | --- | @@ -460,7 +462,7 @@ Payload 示例: | `target_user_id` | int64 | 收礼用户。 | | `coin_spent` | int64 | 本次实际扣费。 | | `selected_tier_id` | string | 中奖奖档。 | -| `multiplier_ppm` | int64 | 中奖倍率,建议新增。 | +| `multiplier_ppm` | int64 | 中奖倍率,已返回。 | | `effective_reward_coins` | int64 | 用户可见奖励金币。 | | `reward_status` | string | `pending`、`granted`、`failed`。 | | `stage_feedback` | bool | 是否阶段反馈。 | @@ -529,7 +531,7 @@ Payload 示例: ## IM:高倍全站或区域播报 -建议新增播报 IM,用于高倍中奖或运营配置的大额中奖展示。 +已实现区域播报 IM,用于高倍中奖或运营配置的大额中奖展示;当前服务端触发阈值是 `multiplier_ppm >= 1000000` 且有 `visible_region_id`。 | 字段 | 值 | | --- | --- | diff --git a/docs/flutter对接/幸运礼物接口闭环Flutter对接.md b/docs/flutter对接/幸运礼物接口闭环Flutter对接.md index 4a3ff69a..f48fd573 100644 --- a/docs/flutter对接/幸运礼物接口闭环Flutter对接.md +++ b/docs/flutter对接/幸运礼物接口闭环Flutter对接.md @@ -28,7 +28,7 @@ | --- | --- | --- | --- | | `room_id` | string | 是 | 当前房间 ID | | `gift_id` | string | 是 | 实际送出的礼物 ID,例如 `rose` | -| `pool_id` | string | 是 | 幸运礼物奖池 ID,例如 `lucky`、`super_lucky` | +| `pool_id` | string | 是 | 幸运礼物奖池 ID,例如 `lucky`、`super_lucky`;当前 check 接口必填 | 返回 `data`: @@ -59,11 +59,13 @@ | 参数 | 类型 | 必填 | 说明 | | --- | --- | --- | --- | | `room_id` | string | 是 | 房间 ID | -| `target_user_id` | int/string | 是 | 收礼用户 ID | +| `command_id` | string | 是 | 本次送礼动作幂等键;同一次超时重试必须复用 | +| `target_user_id` | int/string | 兼容 | 旧单目标字段;新客户端优先使用 `target_user_ids` | +| `target_user_ids` | int[] | 是 | 当前 `target_type=user` 时必须且只能 1 个 | +| `target_type` | string | 否 | 当前只支持 `user`,为空按 `user` | | `gift_id` | string | 是 | 礼物 ID,例如 `rose` | | `gift_count` | int | 是 | 数量,必须大于 0 | | `pool_id` | string | 幸运礼物必填 | `lucky` 或 `super_lucky` | -| `client_sequence` | string | 建议 | 客户端幂等序列 | 返回 `data.lucky_gift`: @@ -81,30 +83,37 @@ | `room_atmosphere_reward_coins` | int | 房间气氛池返奖 | | `activity_subsidy_coins` | int | 活动补贴返奖 | | `effective_reward_coins` | int | 用户实际获得金币 | -| `reward_status` | string | 初始通常为 `pending`,到账由后端 outbox 补偿 | +| `reward_status` | string | 有返奖或阶段反馈时初始通常为 `pending`,到账和房间 IM 由后端 outbox 补偿 | | `stage_feedback` | bool | 是否阶段反馈,可播放轻量中奖表现 | | `high_multiplier` | bool | 是否高倍 | | `created_at_ms` | int | UTC epoch ms | 客户端处理: -- `data.lucky_gift != null`:立即调用 `_applyLuckyGiftResult()`,播放中奖内容和数量 SVGA。 -- `effective_reward_coins > 0`:展示中奖金额;余额不要本地相加,以 `WalletBalanceChanged` 或余额刷新为准。 +- `data.lucky_gift != null`:立即调用 `_applyLuckyGiftResult()`,播放中奖或阶段反馈表现。 +- `effective_reward_coins > 0 && reward_status == "pending"`:展示中奖金额和“奖励处理中”,余额不要本地相加。 +- `effective_reward_coins > 0 && reward_status == "granted"`:可以展示已到账状态;余额仍以 `WalletBalanceChanged` 或余额刷新为准。 - `data.lucky_gift == null`:按普通礼物表现处理。 ## 3. 错误处理 | code | 场景 | 客户端处理 | | --- | --- | --- | -| `INVALID_ARGUMENT` | 参数缺失、数量非法、pool_id 缺失 | 停止提交,提示用户重试或刷新礼物面板 | +| `INVALID_ARGUMENT` | 参数缺失、数量非法、`pool_id` 缺失、`target_user_ids` 非单目标 | 停止提交,提示用户重试或刷新礼物面板 | | `UNAUTHENTICATED` | token 失效 | 重新登录 | | `INSUFFICIENT_BALANCE` | 金币不足 | 打开充值入口或提示余额不足 | | `RULE_NOT_ACTIVE` | 幸运礼物规则未启用 | 降级普通礼物或刷新礼物配置 | | `UPSTREAM_ERROR`/`UNAVAILABLE` | 内部服务或 IM/钱包临时不可用 | 不重复扣本地余额;允许用户稍后重试 | -| `IDEMPOTENCY_CONFLICT`/`CONFLICT` | 同一客户端命令参数变化 | 重新生成 `client_sequence` 后再提交 | +| `IDEMPOTENCY_CONFLICT`/`CONFLICT` | 同一 `command_id` 对应的请求参数变化,或规则/奖池/风控冲突 | 参数变化时重新生成 `command_id`;规则冲突时刷新礼物面板 | 送礼失败时后端不会返回 `lucky_gift`,客户端不要播放中奖 UI。送礼成功但返奖到账延迟时,房间 IM 和钱包 IM 会继续补偿。 +幂等规则: + +- HTTP 超时、断网、502:同一次用户动作使用原 `command_id` 重试。 +- 已收到成功响应或钱包余额已扣减:不要生成新 `command_id` 再发同一笔礼物。 +- 需要重新点击送礼:生成新的 `command_id`。 + ## 4. 房间 IM 腾讯云 IM 群自定义消息: @@ -132,7 +141,13 @@ "sender_user_id": 42, "target_user_id": 99, "coin_spent": 100, + "rule_version": 1, + "experience_pool": "novice", + "selected_tier_id": "novice_2x", "multiplier_ppm": 2000000, + "base_reward_coins": 200, + "room_atmosphere_reward_coins": 0, + "activity_subsidy_coins": 0, "effective_reward_coins": 200, "stage_feedback": true, "high_multiplier": false, @@ -145,6 +160,7 @@ - 只处理 `Desc == "lucky_gift_drawn"` 且 `event_type == "lucky_gift_drawn"`。 - 用 `event_id` 或 `draw_id` 去重;同一抽奖不要重复播放。 - 当前用户已通过 HTTP 送礼响应播放过同一 `draw_id` 时,收到 IM 只更新状态,不重复播放。 +- `effective_reward_coins > 0` 但还没有收到钱包余额 IM 时,只展示中奖结果和处理中状态,不本地加金币。 ## 5. 钱包余额 IM diff --git a/docs/幸运礼物Activity服务开发.md b/docs/幸运礼物Activity服务开发.md index 87fe788f..5bd18b1e 100644 --- a/docs/幸运礼物Activity服务开发.md +++ b/docs/幸运礼物Activity服务开发.md @@ -2,6 +2,8 @@ 本文档定义幸运礼物在 `activity-service` 中的开发落地。产品规则见 `docs/幸运礼物产品方案.md`。 +当前代码已经落地到 `api/proto/activity/v1/activity.proto`、`services/activity-service/internal/service/luckygift` 和 `services/activity-service/internal/storage/mysql/lucky_gift_repository.go`。线上事实以 `docs/幸运礼物线上落地文档.md` 为准;本文件保留开发阶段设计背景,涉及“建议新增 proto”“固定概率表”“后台权重表”的段落不再代表当前实现。当前实现的核心配置是 `pool_id + multiplier_ppms`,后台配置多个倍率,服务端在抽奖时按 RTP 窗口、奖池和风控自动生成运行时权重。 + ## Ownership 幸运礼物归属 `activity-service`,但金币账务仍归属 `wallet-service`,房间状态仍归属 `room-service`。 diff --git a/docs/幸运礼物产品方案.md b/docs/幸运礼物产品方案.md index c3321c9e..4173e901 100644 --- a/docs/幸运礼物产品方案.md +++ b/docs/幸运礼物产品方案.md @@ -2,6 +2,8 @@ 本文档定义语音房幸运礼物的产品规则。幸运礼物使用金币赠送,奖励也返还金币 +当前代码实现以 `docs/幸运礼物线上落地文档.md` 为准。已落地版本不是固定概率表模型,而是后台配置 `multiplier_ppms` 多个倍率,服务端按 RTP 窗口、三层奖池和风控容量在运行时自动生成候选权重;因此本文中“概率表/权重表”只保留为产品抽象,不代表后台可以直接配置固定命中概率。 + ## Positioning | Object | Meaning | diff --git a/docs/幸运礼物线上落地文档.md b/docs/幸运礼物线上落地文档.md index 09fe13e6..41fdbbb2 100644 --- a/docs/幸运礼物线上落地文档.md +++ b/docs/幸运礼物线上落地文档.md @@ -1,146 +1,157 @@ # 幸运礼物线上落地文档 -本文档定义当前幸运礼物在 `activity-service` 的线上落地边界、后台参数、抽奖事务和验证方式。幸运礼物只拥有活动决策、RTP 窗口、奖池和抽奖事实;金币扣费和返奖入账仍归 `wallet-service`,房间状态和房间表现仍归 `room-service`。 +本文档按当前代码实现整理幸运礼物的服务边界、配置语义、抽奖链路、体验模拟方式和已知问题。当前幸运礼物由 `activity-service` 持有规则、RTP 窗口、奖池、风控计数、抽奖事实和异步副作用 outbox;金币扣费与返奖入账仍归 `wallet-service`,房间状态、热度、榜单和送礼消息仍归 `room-service`。 -## Current Scope +## 相关代码 -本次落地包含: - -| Scope | Status | Notes | +| 模块 | 文件 | 当前职责 | | --- | --- | --- | -| `LuckyGiftService.CheckLuckyGift` | done | 房间送礼前检查礼物是否启用,并返回当前体验池 | -| `LuckyGiftService.ExecuteLuckyGiftDraw` | done | 扣费成功后执行抽奖决策,写抽奖事实和 outbox | -| `AdminLuckyGiftService.GetLuckyGiftConfig` | done | 后台按 `pool_id` 读取幸运礼物奖池规则;未配置时返回默认草稿 | -| `AdminLuckyGiftService.UpsertLuckyGiftConfig` | done | 后台按 `pool_id` 发布独立幸运礼物奖池规则版本 | -| `AdminLuckyGiftService.ListLuckyGiftDraws` | done | 后台审计抽奖记录 | -| 三层基础奖池 | done | platform / room / gift | -| RTP 双窗口 | done | global + gift | -| 风控计数 | done | user hour/day、device day、room hour、anchor day | -| 房间气氛池 | done | 独立预算,不污染基础 RTP | -| 活动预算池 | done | 独立补贴,不污染基础 RTP | -| 返奖入账 | done | `activity_outbox` worker 调 `wallet-service.CreditLuckyGiftReward`,钱包 outbox 继续触发余额私信 | -| 房间中奖 IM | done | `activity_outbox` worker 投递腾讯云 IM 群消息 `lucky_gift_drawn` | -| App 检查接口 | done | gateway 暴露 `POST /api/v1/activities/lucky-gifts/check` | +| protobuf 契约 | `api/proto/activity/v1/activity.proto` | `LuckyGiftConfig`、`multiplier_ppms`、`LuckyGiftDrawResult.multiplier_ppm`、后台配置和审计 RPC | +| 房间送礼主链路 | `services/room-service/internal/room/service/gift.go` | 送礼前可检查奖池,扣费成功后同步调用 `ExecuteLuckyGiftDraw`,把 `lucky_gift` 放入 `SendGiftResponse` | +| 幸运礼物领域模型 | `services/activity-service/internal/domain/luckygift/lucky_gift.go` | 规则、奖档、Check/Draw 命令、抽奖结果和审计查询结构 | +| 规则归一化 | `services/activity-service/internal/service/luckygift/config.go` | 后台输入 `multiplier_ppms` 后生成三档体验池奖档;权重不是后台配置项 | +| 抽奖服务 | `services/activity-service/internal/service/luckygift/service.go` | Check、Draw、outbox worker、返奖入账、房间 IM、区域播报 | +| MySQL 抽奖事务 | `services/activity-service/internal/storage/mysql/lucky_gift_repository.go` | 规则锁、RTP 窗口、三层池、风控计数、候选过滤、随机、draw fact、outbox | +| activity schema | `services/activity-service/deploy/mysql/initdb/001_activity_service.sql` | `lucky_gift_rules`、`lucky_rtp_windows`、`lucky_pools`、`lucky_user_states`、`lucky_draw_records` 等表 | +| gateway 检查接口 | `services/gateway-service/internal/transport/http/activityapi/lucky_gift_handler.go` | `POST /api/v1/activities/lucky-gifts/check`,当前要求 `room_id/gift_id/pool_id` 都非空 | +| gateway 送礼接口 | `services/gateway-service/internal/transport/http/roomapi/room_handler.go` | `POST /api/v1/rooms/gift/send` 透传 `pool_id` 和 `command_id` | +| admin 配置接口 | `server/admin/internal/modules/luckygift/handler.go` | 后台读写 `pool_id` 维度的规则,支持 `multiplier_ppms` | +| 体验模拟工具 | `tools/lucky-gift-v2-demo/main.go` | 离线模拟用户 RTP 分布、奖池水位、风控限制和高倍命中 | -`activity-service` 抽奖事务内仍不调用钱包、房间 RPC 或腾讯云 IM。抽奖事务只写事实和 `activity_outbox`;事务外由 lucky gift worker 抢占 outbox,先幂等返奖,再投递房间中奖 IM,最后标记 draw 和 outbox 状态。 +## 当前范围 -## Call Flow - -```text -room-service SendGift - 1. CheckLuckyGift(meta, user_id, room_id, gift_id, pool_id) - 2. wallet-service 扣费成功 - 3. ExecuteLuckyGiftDraw(command_id, user_id, target_user_id, gift_count, device_id, room_id, anchor_id, gift_id, pool_id, coin_spent, paid_at_ms) - 4. activity-service 本地事务写 draw fact + outbox - 5. lucky gift worker 幂等调用 wallet-service.CreditLuckyGiftReward - 6. wallet-service 写 WalletBalanceChanged,notice-service 投递私信余额通知 - 7. lucky gift worker 投递房间群 IM lucky_gift_drawn - 8. activity-service 标记 reward_status=granted、outbox=delivered -``` - -`command_id` 是抽奖幂等键。重复 `ExecuteLuckyGiftDraw` 会返回同一条 `lucky_draw_records`,不会重复消耗 RTP、奖池或预算。 - -后台按 `pool_id` 维护多份互相独立的幸运礼物奖池规则,实际抽奖仍记录用户发送的真实 `gift_id`。后台不暴露单抽消耗;服务端保存固定内部换算单位,不同幸运礼物实际消耗不同,抽奖事务按实际 `coin_spent` 等比缩放基础奖档,并按实际 `coin_spent` 累计 RTP 目标和奖池入账。同一个 `gift_id` 可以请求不同 `pool_id`,不同 `pool_id` 的 RTP 窗口、基础奖池、房间气氛池、活动预算、用户阶段和风控计数互不共享。 - -## Transaction Boundary - -`ExecuteLuckyGiftDraw` 使用一个 MySQL 本地事务,事务内必须完成: - -1. 按 `command_id` 锁定幂等记录。 -2. 锁定 `lucky_gift_rules` 中 `pool_id` 对应规则。 -3. 锁定或创建用户体验状态 `lucky_user_states`。 -4. 锁定或创建全站 RTP 窗口、幸运礼物整体子窗口。 -5. 锁定或创建平台、房间、幸运礼物整体三层基础奖池。 -6. 锁定或创建房间气氛池。 -7. 锁定或创建活动总预算桶和 UTC 日预算桶。 -8. 锁定用户、设备、房间、主播风控计数。 -9. 抽前过滤所有不可支付候选。 -10. 执行一次服务端安全随机。 -11. 更新 RTP 窗口、奖池、预算、风控计数、用户状态。 -12. 插入 `lucky_draw_records`。 -13. 必要时插入 `activity_outbox`。 - -事务内不得调用 `wallet-service`、`room-service` 或腾讯云 IM,避免外部 RPC 卡住奖池行锁。 - -## RTP Accounting - -基础返奖只使用 `base_reward_coins`: - -```text -base_rtp = sum(base_reward_coins) / sum(coin_spent) -``` - -用户可见返奖使用: - -```text -effective_reward_coins = base_reward_coins - + room_atmosphere_reward_coins - + activity_subsidy_coins -``` - -房间气氛池和活动预算只能提高 `effective_rtp`,不能用于补平基础 RTP。 - -## Admin Parameters - -后台参数分为 6 类。 - -后台 HTTP 入口: - -| Method | Path | Permission | Meaning | -| --- | --- | --- | --- | -| `GET` | `/api/v1/admin/activity/lucky-gifts/config?pool_id=pool_95` | `lucky-gift:view` | 读取指定奖池规则;未配置返回默认草稿 | -| `PUT` | `/api/v1/admin/activity/lucky-gifts/config?pool_id=pool_95` | `lucky-gift:update` | 发布指定奖池的新规则版本 | -| `GET` | `/api/v1/admin/activity/lucky-gifts/draws` | `lucky-gift:view` | 查询抽奖审计记录 | - -| Group | Fields | Meaning | +| 能力 | 状态 | 代码事实 | | --- | --- | --- | -| RTP 成本 | `target_rtp_ppm`, `pool_rate_ppm`, `global_window_draws`, `gift_window_draws` | 控制基础 RTP 和收敛窗口 | -| 三层池 | `platform_pool_weight_ppm`, `room_pool_weight_ppm`, `gift_pool_weight_ppm`, `initial_*_pool`, `*_reserve` | 控制平台、房间、礼物各自承担的基础返奖能力 | -| 倍率配置 | `multiplier_ppms`, generated `tiers` | 运营只配置用户可能命中的倍率;服务按 RTP 窗口、水位和风控自动生成实时概率 | -| 体验池 | `novice_draw_limit`, `intermediate_draw_limit` | 控制新手/中级/高级阶段,不直接决定成本 | -| 高倍 | `high_multiplier`, `high_water_pool_multiple`, `large_tier_enabled` | 控制 100x/500x 等高倍倍率是否可进入候选 | -| 风控 | `max_single_payout`, `user_hourly_payout_cap`, `user_daily_payout_cap`, `device_daily_payout_cap`, `room_hourly_payout_cap`, `anchor_daily_payout_cap` | 控制单用户、设备、房间、主播关联风险敞口 | -| 娱乐补贴 | `room_atmosphere_rate_ppm`, `room_atmosphere_initial`, `room_atmosphere_reserve`, `activity_budget`, `activity_daily_limit` | 控制房间爆点和活动补贴 | +| 送礼前检查 | 已实现 | gateway check 要求 `pool_id`;activity 按 `pool_id` 读规则,未配置返回 `not_configured` | +| 扣费后抽奖 | 已实现 | `room-service` 先调 `wallet-service.DebitGift`,扣费成功后调 `activity-service.ExecuteLuckyGiftDraw` | +| 同步返回抽奖结果 | 已实现 | `SendGiftResponse.lucky_gift` 直接返回给送礼用户 | +| 后台多奖池 | 已实现 | 规则主键实际是 `(app_code, gift_id)`,但当前 `gift_id` 列保存的是 `pool_id` | +| 多倍数配置 | 已实现 | 后台配置 `multiplier_ppms`,服务端生成每个体验池的候选倍率 | +| RTP 双窗口 | 已实现 | 当前窗口 scope 是 `pool` 和 `pool_gift`,都按 `pool_id` 隔离 | +| 三层基础奖池 | 已实现 | platform / room / gift 三层池按 `pool_id` 隔离,room 池再加 `room_id` | +| 风控计数 | 已实现 | user hour/day、device day、room hour、anchor day | +| 房间气氛池 | 已实现 | 只在基础 RTP 不需要强制追平时参与候选 | +| 活动补贴池 | 已实现 | 只在基础 RTP 不需要强制追平时参与候选 | +| 返奖入账 | 已实现 | 抽奖事务写 `activity_outbox`,worker 调 `wallet-service.CreditLuckyGiftReward` | +| 房间中奖 IM | 已实现 | worker 投递 `lucky_gift_drawn` 群消息 | +| 区域中奖播报 | 已实现 | `multiplier_ppm >= 1x` 且有 `visible_region_id` 时写区域播报 outbox | +| 完整候选审计 | 未完整 | `candidate_tiers_json` 当前只保存命中候选和限制标记,不保存完整过滤后候选表 | +| 随机摘要审计 | 未实现 | 当前没有 `random_digest` 字段落库 | -调整方向: +## 主链路 -| Goal | Primary Change | -| --- | --- | -| 降成本 | 降 `target_rtp_ppm`,降补贴预算,提高高水位倍数 | -| 增刺激 | 增加高倍倍率,增房间气氛和活动预算 | -| 新手更友好 | 增加 0.2x/0.5x/1x 等小倍率,延长 `novice_draw_limit` | -| 防套利 | 降低用户/设备/房间/主播上限,关闭高倍 | -| RTP 更稳 | 缩小 RTP 窗口 | -| 体验更自然 | 放大 RTP 窗口,但接受短周期波动 | - -## Data Tables - -| Table | Owner Data | -| --- | --- | -| `lucky_gift_rules` | 按 `pool_id` 保存幸运礼物奖池规则和后台可调参数 | -| `lucky_rtp_windows` | 按 `pool_id` 保存 RTP 控制窗口 | -| `lucky_pools` | 按 `pool_id` 保存 platform / room / gift 三层基础奖池 | -| `lucky_room_atmosphere_pools` | 按 `pool_id + room_id` 保存房间气氛预算 | -| `lucky_activity_budgets` | 按 `pool_id` 保存活动补贴预算;`budget_day=__total__` 控总额,UTC 日期行控日释放 | -| `lucky_user_states` | 用户在单个 `pool_id` 内的累计付费抽数 | -| `lucky_risk_counters` | 按 `pool_id` 保存风控窗口计数 | -| `lucky_draw_records` | 每次抽奖事实和审计快照 | - -## Verification - -Proto 变更后必须运行: - -```bash -PATH=/tmp/codex-protoc/bin:$HOME/go/bin:$PATH make proto +```text +Client + -> gateway POST /api/v1/rooms/gift/send + -> room-service SendGift + -> 可选 CheckLuckyGift(pool_id 非空时送礼前检查) + -> wallet-service DebitGift + -> activity-service ExecuteLuckyGiftDraw + -> activity-service 本地事务写 draw fact + activity_outbox + -> room-service 更新 Room Cell 热度、榜单、礼物 outbox + -> gateway 返回 data.lucky_gift + -> lucky gift worker 补偿返奖入账和 lucky_gift_drawn IM ``` -服务编译验证: +当前有两个触发入口: -```bash -go test ./services/activity-service/... +| 入口 | 行为 | +| --- | --- | +| 请求显式传 `pool_id` | `room-service` 会在扣费前调用 `CheckLuckyGift`,规则未启用则不扣费 | +| 请求未传 `pool_id` 但钱包返回 `gift_type_code=lucky/super_lucky` | `room-service` 会在扣费后按默认奖池抽奖,`activity-service` 把空 `pool_id` 归一化为 `default` | + +客户端 check 接口当前要求 `pool_id` 必填,所以 App 不应依赖“空 `pool_id` 默认奖池”的隐式路径。礼物面板需要下发明确的 `pool_id`。 + +## 多倍数配置 + +后台现在的核心配置不是固定概率表,而是倍率列表: + +```json +{ + "pool_id": "lucky", + "gift_price": 100, + "target_rtp_ppm": 950000, + "pool_rate_ppm": 950000, + "high_multiplier": 100, + "large_tier_enabled": true, + "multiplier_ppms": [ + 0, + 200000, + 500000, + 1000000, + 2000000, + 5000000, + 10000000, + 20000000, + 50000000, + 100000000, + 500000000 + ] +} ``` -模型验证: +倍率单位是 ppm: + +| 值 | 含义 | `gift_price=100` 时基础返奖 | +| ---: | --- | ---: | +| `0` | 未中奖 | `0` | +| `200000` | `0.2x` | `20` | +| `500000` | `0.5x` | `50` | +| `1000000` | `1x` | `100` | +| `2000000` | `2x` | `200` | +| `100000000` | `100x` | `10000` | +| `500000000` | `500x` | `50000` | + +服务端发布配置时会执行这些归一化: + +1. 如果 `multiplier_ppms` 非空,直接用它去重、排序并过滤负数。 +2. 如果 `multiplier_ppms` 为空,则从传入 `tiers` 里反推倍率。 +3. 如果两者都为空,则使用默认倍率列表。 +4. 每个倍率都会生成 `novice`、`intermediate`、`advanced` 三个体验池各一条奖档。 +5. 生成奖档的 `weight` 固定为 `0`,真实权重由抽奖时 RTP 窗口动态计算。 +6. `multiplier_ppm >= high_multiplier * 1000000` 的奖档会标记 `high_water_only`。 + +这意味着当前后台不能按体验池单独配置概率,也不能直接配置“新手 2x 权重大、中级 5x 权重大”这类固定权重。后台能控制的是可用倍率集合、RTP 目标、窗口大小、奖池水位、风控上限和高倍开关。 + +## 抽奖算法事实 + +抽奖事务按 `command_id` 幂等。重复请求返回同一条 `lucky_draw_records`,不会重复消耗奖池或 RTP。 + +核心步骤: + +```text +1. 锁定 command_id 对应 draw fact;存在则直接返回 +2. 按 pool_id 锁定幸运礼物规则;未启用则失败 +3. 用真实 coin_spent 替换配置里的参考 gift_price,并等比缩放奖档和风控上限 +4. 锁定用户在该 pool_id 下的累计付费抽数,选择 novice/intermediate/advanced +5. 锁定 pool RTP 窗口和 pool_gift RTP 子窗口 +6. 锁定 platform、room、gift 三层基础奖池 +7. 锁定房间气氛池、活动预算和风控计数 +8. 计算本次基础 RTP 最小必付额 min_required +9. 过滤低于 min_required、超过奖池容量、超过风控容量或高水位不足的倍率 +10. 从可支付基础候选中按 RTP 缺口自动生成 1 个或 2 个带权候选 +11. 当 min_required=0 时,额外加入房间气氛、活动补贴和阶段反馈候选 +12. 使用 crypto/rand 做一次加权随机 +13. 更新 RTP 窗口、三层池、气氛池、活动预算、风控计数、用户抽数 +14. 写 lucky_draw_records;有返奖或展示副作用时写 activity_outbox +``` + +运行时基础候选权重不是后台配置权重。`luckyAutoWeightedBaseCandidates` 会按当前窗口缺口选择: + +| RTP 状态 | 当前行为 | +| --- | --- | +| 没有基础 RTP 缺口 | 优先选择 `0x` 候选;如果倍率列表没有 `0`,会选择最低可用倍率 | +| 缺口正好落在某个倍率以下 | 只给最接近且可支付的低档或高档候选分配 `1000000` 权重 | +| 缺口落在两个倍率之间 | 只保留相邻两个倍率,权重按线性插值分配 | +| 没有可用候选但必须追平 | 生成 `rtp_balance` 修正候选;如果超过容量则返回冲突 | + +因此,多倍数列表不是“所有倍率每次都按固定概率参与随机”。它是 RTP 控制器可选择的离散返奖点。 + +## 用户体验模拟 + +体验模拟工具用于评估参数是否会带来可接受的用户体感分布。它输出全局 RTP、有效 RTP、100k/1m 窗口偏差、奖池水位、用户 RTP 分位数和用户净输赢分位数。 + +基础命令: ```bash go run ./tools/lucky-gift-v2-demo \ @@ -151,40 +162,110 @@ go run ./tools/lucky-gift-v2-demo \ -draws-max 100000 \ -unique-draws \ -base-rtp 95 \ - -initial 1000000000 \ -cost 500 \ -global-window 100000 \ - -gift-window 100000 + -gift-window 100000 \ + -pool-rate-ppm 950000 \ + -high-multiplier 100 \ + -high-water-pool-multiple 2 \ + -user-csv /tmp/lucky-gift-users.csv +``` + +重点看这些输出: + +| 输出 | 判断方式 | +| --- | --- | +| `base_rtp` | 基础成本口径,应贴近 `base-rtp` | +| `effective_rtp` | 用户可见返还,允许因气氛池和活动补贴高于基础 RTP | +| `100k_blocks max_abs_deviation` | 应小于 `1pp` | +| `1m_blocks max_abs_deviation` | 应小于 `0.5pp` | +| `high_multiplier_hits` | 评估高倍实际出现频率是否符合运营预期 | +| `risk_cap_limited_draws` | 过高说明风控上限压得太低,会牺牲 RTP 追平能力 | +| `pool_cap_limited_draws` | 过高说明初始奖池、入池比例或 reserve 配置不健康 | +| `user_effective_rtp_p05/median/p95` | 用户体验分布;单用户不会收敛到全局 RTP | +| `user_net_p05/median/p95` | 用户净输赢体感;用于判断投诉风险和高倍刺激强度 | + +当前模拟工具的限制: + +| 限制 | 影响 | +| --- | --- | +| 不能直接输入 `multiplier_ppms` | 它使用内置新手/中级/高级 tier table,不能完全复现后台任意倍率列表 | +| 使用伪随机 `math/rand` | 便于复现实验,不等同线上 `crypto/rand` | +| 不连接真实 MySQL | 不验证行锁、幂等、outbox 和钱包补偿 | +| 不模拟真实礼物面板 | 只模拟付费抽数、成本、奖池和用户结果分布 | + +使用模拟结论上线前,仍需用真实 `activity-service` MySQL 测试覆盖配置发布、抽奖事务、重复 `command_id`、outbox 返奖和 IM 投递。 + +## 当前问题 + +| 问题 | 影响 | 建议动作 | +| --- | --- | --- | +| `lucky_gift_rules.gift_id` 实际保存 `pool_id` | 文档、后台和审计容易把真实礼物 ID 与奖池 ID 混淆 | 后续 DB 字段改名为 `pool_id`,或在所有接口文档明确“规则表 gift_id 是历史列名” | +| 后台传入 `tiers.weight` 不生效 | 运营以为配置了固定概率,但线上按 RTP 缺口自动权重抽 | 后台隐藏或只读展示 `tiers.weight`,主编辑项改为 `multiplier_ppms` | +| `multiplier_ppms` 可以不含 `0` | 没有 RTP 缺口时最低正倍率可能被强制选中,用户体感和成本都会异常 | 后台校验建议强制包含 `0`,除非明确做“每抽必返”奖池 | +| 三个体验池使用同一倍率集合 | 无法做到新手池低倍密集、高级池高倍更多的差异化倍率表 | 增加按 pool 分组的倍率配置,或保留统一倍率但明确只能靠阶段和 RTP 控制差异 | +| 当前服务不使用后台固定概率 | “概率表版本”“权重表”类文档与代码不一致 | 文档统一改为“倍率列表 + 运行时 RTP 自动权重” | +| `candidate_tiers_json` 不保存完整候选表 | 后台审计无法复盘每次过滤后的完整随机空间 | 落库完整候选列表、过滤原因、总权重和随机落点 | +| 没有 `random_digest` | 不能证明随机材料与候选表一致 | 增加随机摘要或 seed commit-reveal 方案 | +| 同步返回通常是 `pending` | 客户端如果看到返奖就展示“已到账”,会早于钱包入账事实 | 客户端只在钱包 IM 或余额刷新后更新余额;HTTP 只展示“中奖/处理中” | +| check 接口要求 `pool_id`,送礼链路又支持空 `pool_id` 默认奖池 | App 行为和服务端隐式兜底不一致 | 礼物面板必须明确下发 `pool_id`,客户端幸运礼物送礼必传 | +| 区域播报阈值硬编码为 `>= 1x` | 后台不能独立控制哪些中奖进入区域飘屏 | 增加 `broadcast_min_multiplier_ppm` 或 `broadcast_min_reward_coins` | +| 模拟工具不能输入多倍数列表 | 不能验证“多个倍数配置”对实际体感的影响 | 给 `tools/lucky-gift-v2-demo` 增加 `-multipliers-ppm` 参数,或新增基于线上 `Config` 的模拟入口 | + +## 后台配置建议 + +| 目标 | 建议 | +| --- | --- | +| 常规幸运礼物 | `multiplier_ppms` 包含 `0/0.2x/0.5x/1x/2x/5x/10x/20x`,高倍关闭或保持较高水位门槛 | +| 超级幸运礼物 | 在常规列表基础上增加 `50x/100x/500x`,同时提高初始池、水位倍数和风控 cap | +| 新手体验 | 保留 `0.2x/0.5x/1x/2x`,不要只配置 `1x` 以上倍率 | +| 降低投诉 | 降低 `effective_rtp` 波动,增加小倍率数量,减少 `500x` 等极高倍 | +| 控制成本 | 降低 `target_rtp_ppm` 或补贴预算,不要用过低风控 cap 硬压基础 RTP | + +高风险配置示例: + +| 配置 | 风险 | +| --- | --- | +| `multiplier_ppms=[1000000,2000000]` | 没有未中奖档,低缺口时仍可能返 `1x` | +| `target_rtp_ppm=950000` 但最大倍率低于 `1x` | 发布会被校验拒绝,无法追平 RTP | +| `large_tier_enabled=true` 但初始池很低 | 高倍长期被过滤,用户看到配置但实际不出 | +| `user_hourly_payout_cap` 低于小时期望返奖 | 风控会阻断 RTP 追平,触发 `cannot be settled` | + +## 验证 + +文档或配置语义变更后至少跑相关单元测试: + +```bash +go test ./services/activity-service/internal/service/luckygift +go test ./services/activity-service/internal/storage/mysql +go test ./services/room-service/internal/room/service +go test ./services/gateway-service/internal/transport/http/activityapi +``` + +参数上线前跑体验模拟: + +```bash +go run ./tools/lucky-gift-v2-demo \ + -users 1000 \ + -devices 900 \ + -rooms 50 \ + -draws-min 0 \ + -draws-max 100000 \ + -unique-draws \ + -base-rtp 95 \ + -cost 500 \ + -global-window 100000 \ + -gift-window 100000 \ + -user-csv /tmp/lucky-gift-users.csv ``` 验收口径: -| Check | Requirement | +| 项 | 要求 | | --- | --- | -| `100000` paid draws | 基础 RTP 偏差 `< 1%` | -| `1000000` paid draws | 基础 RTP 偏差 `< 0.5%` | -| lucky gift RTP | 幸运礼物整体子窗口独立收敛 | -| pool safety | 三层池不低于 reserve | -| subsidy isolation | 房间气氛/活动补贴不进入基础 RTP | -| idempotency | 重复 command_id 返回同一 draw | - -## Rollout - -建议上线顺序: - -1. 后台创建规则草稿,`enabled=false`。 -2. 跑模型模拟并审核参数。 -3. 发布 `enabled=true` 到全局幸运礼物配置。 -4. 只开放内部房间或白名单房间。 -5. 观察 `base_rtp`、`effective_rtp`、pool balance、risk reject、outbox pending。 -6. 稳定后逐步开放更多房间和礼物。 - -必须保留的开关: - -| Switch | Effect | -| --- | --- | -| `enabled=false` | 全站幸运礼物停止抽奖 | -| `large_tier_enabled=false` | 关闭高倍奖档 | -| `activity_budget=0` | 关闭活动补贴 | -| `room_atmosphere_rate_ppm=0` | 关闭房间气氛补贴 | -| 降低各风控 cap | 快速压缩风险敞口 | +| `100000` paid draws | 基础 RTP 偏差 `< 1pp` | +| `1000000` paid draws | 基础 RTP 偏差 `< 0.5pp` | +| pool safety | 三层池和气氛池不低于 reserve | +| idempotency | 重复 `command_id` 返回同一 draw | +| reward status | 有返奖时先 `pending`,worker 成功后 `granted` | +| Flutter 体验 | 不本地加余额;余额以 `WalletBalanceChanged` 或余额查询为准 | diff --git a/docs/房内红包需求接口拆分.md b/docs/房内红包需求接口拆分.md new file mode 100644 index 00000000..1fe6fc05 --- /dev/null +++ b/docs/房内红包需求接口拆分.md @@ -0,0 +1,745 @@ +# 房内红包需求接口拆分 + +## 1. 结论 + +房内红包拆成两类通道: + +- HTTP 接口:承载有状态业务,包括配置、发送、领取、详情、记录、退款后台操作;房间在线态使用语音房通用接口。 +- IM 消息:承载实时 UI 触达,包括房内弹窗、延时红包同语区飘窗、倒计时结束强弹、侧边栏数量刷新。 + +本需求以产品文档为准: + +- 红包类型:即时红包、延时红包。 +- 发送成功立刻扣除金币。 +- 发送者本人可以领取自己发出的红包。 +- 红包有效领取时间为 24 小时,过期未领取金额退回发送者。 +- 即时红包不触发同语区广播飘窗。 +- 延时红包发送成功后触发同语区广播飘窗。 +- 当前房间内无论即时还是延时,发送成功后都需要立刻弹出抢红包窗口。 + +现有历史文档中的 `2 小时退款` 不适用于本次新需求,应改为 24 小时。 + +## 2. 统一响应约定 + +现有 Go 网关成功响应会同时返回 `body` 和 `data`,客户端读取任意一个都可以。 + +成功响应: + +```json +{ + "status": true, + "errorCode": 0, + "errorMsg": "success", + "body": {}, + "code": "success", + "message": "success", + "data": {}, + "time": 1780000000000 +} +``` + +错误响应: + +```json +{ + "status": false, + "errorCode": 409, + "errorCodeName": "voice_room_red_packet_expired", + "errorMsg": "red packet expired", + "body": null, + "code": "voice_room_red_packet_expired", + "message": "red packet expired", + "data": null, + "time": 1780000000000 +} +``` + +接口文档下文只定义 `body/data` 中的业务字段。 + +## 3. 哪些需要使用 IM + +| 场景 | IM key | 发送范围 | 使用 IM 的原因 | +| --- | --- | --- | --- | +| 红包创建后当前房间立刻弹窗 | `ROOM_RED_PACKET`,`event=CREATED`,`scope=ROOM` | 当前房间 IM 群 | 房内所有在线用户需要实时收到弹窗,不能依赖轮询 | +| 延时红包同语区飘窗引流 | `ROOM_RED_PACKET`,`event=CREATED`,`scope=REGION` | 发送者所在同语区 IM 群 | 首页和其他房间用户没有主动请求,现有区域飘屏能力通过语区 IM 群触达 | +| 延时红包倒计时结束强弹 | `ROOM_RED_PACKET`,`event=OPEN_DUE`,`scope=ROOM` | 当前房间 IM 群 | 用户可能提前关闭弹窗,到点必须再次强制弹出 | +| 红包抢完、过期、退款后刷新侧边栏 | `ROOM_RED_PACKET`,`event=STATUS_CHANGED`,`scope=ROOM` | 当前房间 IM 群 | 房内侧边栏红包数量需要实时减少 | + +不使用 IM 的动作: + +| 动作 | 原因 | +| --- | --- | +| 发送红包 | 需要鉴权、扣款、档位校验、日上限、幂等落库 | +| 抢红包 | 需要鉴权、并发防超卖、领取幂等、钱包入账 | +| 查询配置 | 普通读接口,礼物面板打开时请求即可 | +| 查询详情和记录 | 需要分页、权限和稳定数据 | +| 过期退款 | 服务端定时任务和后台补偿,不应由客户端 IM 触发 | + +## 4. IM 消息格式 + +现有腾讯 IM 自定义消息通过 `TIMCustomElem.MsgContent.Data` 发送 JSON 字符串。业务层统一使用外层: + +```json +{ + "type": "ROOM_RED_PACKET", + "data": {} +} +``` + +腾讯 IM 实际 payload: + +```json +{ + "MsgType": "TIMCustomElem", + "MsgContent": { + "Data": "{\"type\":\"ROOM_RED_PACKET\",\"data\":{...}}", + "Desc": "customBody", + "Sound": "dingdong.aiff" + } +} +``` + +### 4.1 CREATED + +当前房间弹窗和同语区飘窗都使用 `CREATED`,通过 `scope` 区分。 + +```json +{ + "type": "ROOM_RED_PACKET", + "data": { + "event": "CREATED", + "scope": "ROOM", + "packetNo": "VRRP2026052712340001", + "packetId": "VRRP2026052712340001", + "roomId": "9001", + "regionCode": "AR", + "packetMode": "DELAYED", + "senderUserId": "10001", + "actualAccount": "10001", + "userNickname": "Tom", + "userAvatar": "https://example.com/avatar.png", + "totalAmount": 15000, + "totalCount": 5, + "remainCount": 5, + "status": "ACTIVE", + "delaySeconds": 300, + "claimStartTime": 1780000000000, + "expireTime": 1780086400000, + "serverTime": 1779999700000 + } +} +``` + +字段说明: + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `event` | string | 固定为 `CREATED` | +| `scope` | string | `ROOM` 表示房间弹窗,`REGION` 表示同语区飘窗 | +| `packetNo` | string | 红包唯一编号,接口参数使用该字段 | +| `packetId` | string | 兼容旧 Flutter 飘屏字段,值同 `packetNo` | +| `roomId` | string | 红包所属房间,点击飘窗进入该房间 | +| `regionCode` | string | 语区编码 | +| `packetMode` | string | `IMMEDIATE` 或 `DELAYED` | +| `senderUserId` | string | 发送者用户 ID | +| `actualAccount` | string | 发送者账号 | +| `userNickname` | string | 发送者昵称 | +| `userAvatar` | string | 发送者头像 | +| `totalAmount` | int64 | 红包总金币 | +| `totalCount` | int | 红包份数 | +| `remainCount` | int | 剩余可领份数 | +| `status` | string | 红包状态 | +| `delaySeconds` | int | 后台配置的延时秒数 | +| `claimStartTime` | int64 | 可领取开始时间,毫秒时间戳 | +| `expireTime` | int64 | 过期时间,毫秒时间戳,创建后 24 小时 | +| `serverTime` | int64 | 服务端当前时间,毫秒时间戳 | + +发送规则: + +- 即时红包:只发送 `scope=ROOM` 到当前房间 IM 群。 +- 延时红包:发送 `scope=ROOM` 到当前房间 IM 群;同时发送 `scope=REGION` 到同语区 IM 群。 + +### 4.2 OPEN_DUE + +延时红包倒计时结束后,服务端向当前房间发送强弹消息。 + +```json +{ + "type": "ROOM_RED_PACKET", + "data": { + "event": "OPEN_DUE", + "scope": "ROOM", + "packetNo": "VRRP2026052712340001", + "packetId": "VRRP2026052712340001", + "roomId": "9001", + "packetMode": "DELAYED", + "claimStartTime": 1780000000000, + "expireTime": 1780086400000, + "serverTime": 1780000000000 + } +} +``` + +### 4.3 STATUS_CHANGED + +红包被抢完、过期、退款完成后,服务端向当前房间发送状态刷新消息。 + +```json +{ + "type": "ROOM_RED_PACKET", + "data": { + "event": "STATUS_CHANGED", + "scope": "ROOM", + "packetNo": "VRRP2026052712340001", + "packetId": "VRRP2026052712340001", + "roomId": "9001", + "status": "EXPIRED", + "remainCount": 0, + "refundedAmount": 3000, + "serverTime": 1780086400000 + } +} +``` + +客户端收到后刷新侧边栏未领取数量;如果当前展示的是该红包弹窗或详情页,同步刷新状态。 + +## 5. App HTTP 接口 + +### 5.1 获取当前用户语区 IM 群 + +```http +GET /app/region-im-group/current +``` + +兼容旧路径: + +```http +GET /app/voice-room/red-packet/im-group +``` + +返回: + +```json +{ + "configured": true, + "sysOrigin": "LIKEI", + "regionCode": "AR", + "groupId": "@TGS#xxxx", + "groupName": "region-im-group-LIKEI-AR", + "status": "ACTIVE" +} +``` + +客户端处理: + +- `configured=true` 且 `groupId` 非空时加入腾讯 IM 群。 +- `joinGroup` 返回成功或已在群内都按成功处理。 +- 区域变化时退出旧区域群并加入新区域群。 + +### 5.2 通用房间心跳 + +```http +POST /api/v1/rooms/heartbeat +``` + +请求: + +```json +{ + "room_id": "9001", + "command_id": "cmd_heartbeat_9001_10001_1780000000000" +} +``` + +返回: + +```json +{ + "result": { + "ok": true + }, + "user": { + "user_id": "10001", + "room_id": "9001", + "last_seen_at_ms": 1780000000000 + }, + "room": {} +} +``` + +用途: + +- 这是语音房通用 presence 能力,不属于红包专用接口。 +- 只刷新已经存在的业务 presence,不能替代进房 `JoinRoom`。 +- 红包发送、红包领取、侧边栏有效红包查询都依赖该 presence 判断用户是否仍在当前房间。 +- 用户不在房间、已离房、被踢或房间关闭时,心跳应失败,客户端应停止心跳并清理本地房间态。 + +客户端心跳间隔由房间模块统一配置,必须小于 room-service 的 presence stale 窗口。 + +### 5.3 通用离开房间 + +```http +POST /api/v1/rooms/leave +``` + +请求: + +```json +{ + "room_id": "9001", + "command_id": "cmd_leave_9001_10001_1780000000000" +} +``` + +返回: + +```json +{ + "result": { + "ok": true + }, + "room": {} +} +``` + +用途: + +- 这是语音房通用离房接口,不属于红包专用接口。 +- 主动关闭房间页、切换账号、被客户端确认退出时调用。 +- 离房后 room-service 移除业务 presence,红包领取接口再校验该用户时应返回不在房间。 +- 如果用户在麦上,离房流程同时释放麦位。 +- LeaveRoom 后客户端必须停止房间心跳、退出 RTC 房、退出或忽略 IM 房间群消息。 + +### 5.4 红包配置 + +```http +GET /app/voice-room/red-packet/config +``` + +返回: + +```json +{ + "enabled": true, + "dailySendLimit": 10, + "todaySendCount": 1, + "delaySeconds": 300, + "expireSeconds": 86400, + "amountOptions": [15000, 30000, 70000, 100000], + "countOptions": [5, 10, 20, 50], + "ruleUrl": "https://example.com/red-packet-rule" +} +``` + +参数定义: + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `enabled` | bool | 红包功能开关 | +| `dailySendLimit` | int | 每日发送上限 | +| `todaySendCount` | int | 当前用户当天已发送数量 | +| `delaySeconds` | int | 延时红包等待秒数,后台全局唯一配置 | +| `expireSeconds` | int | 固定 86400 秒 | +| `amountOptions` | int64[] | 后台配置金额档位 | +| `countOptions` | int[] | 后台配置红包份数档位 | +| `ruleUrl` | string | 红包规则 H5 地址 | + +### 5.5 发送红包 + +```http +POST /app/voice-room/red-packet/send +``` + +请求: + +```json +{ + "requestId": "client-unique-id", + "roomId": "9001", + "packetMode": "DELAYED", + "totalAmount": 15000, + "totalCount": 5 +} +``` + +参数定义: + +| 字段 | 类型 | 必填 | 说明 | +| --- | --- | --- | --- | +| `requestId` | string | 是 | 客户端幂等 ID,重复请求返回同一红包 | +| `roomId` | string | 是 | 当前房间 ID | +| `packetMode` | string | 是 | `IMMEDIATE` 或 `DELAYED` | +| `totalAmount` | int64 | 是 | 总金币,必须命中后台金额档位 | +| `totalCount` | int | 是 | 红包份数,必须命中后台份数档位 | + +返回: + +```json +{ + "packetNo": "VRRP2026052712340001", + "roomId": "9001", + "packetMode": "DELAYED", + "totalAmount": 15000, + "totalCount": 5, + "remainAmount": 15000, + "remainCount": 5, + "claimStartTime": "2026-05-27T21:05:00+08:00", + "claimStartTimeMs": 1780000000000, + "expireTime": "2026-05-28T21:00:00+08:00", + "expireTimeMs": 1780086400000, + "status": "ACTIVE", + "balanceAfter": 100000 +} +``` + +服务端逻辑: + +1. 校验登录态、功能开关,并通过 room-service 通用 presence 校验发送者仍在 `roomId` 房间内。 +2. 校验金额和份数档位。 +3. 校验每日发送上限。 +4. 校验余额并立刻扣金币。 +5. 预拆随机份额,写红包主表和份额表。 +6. 即时红包 `claimStartTime = createdAt`。 +7. 延时红包 `claimStartTime = createdAt + delaySeconds`。 +8. 所有红包 `expireTime = createdAt + 24h`。 +9. 发送对应 IM 消息。 + +### 5.6 房间有效红包列表 + +```http +GET /app/voice-room/red-packet/active?roomId=9001 +``` + +用途: + +- 进入房间时初始化侧边栏红包数量。 +- 用户关闭弹窗后,通过侧边栏图标重新唤起红包窗口。 +- 收到 IM 状态变化后刷新列表。 + +返回: + +```json +{ + "roomId": "9001", + "unclaimedCount": 3, + "items": [ + { + "packetNo": "VRRP2026052712340001", + "packetMode": "DELAYED", + "senderUserId": "10001", + "senderNickname": "Tom", + "senderAvatar": "https://example.com/avatar.png", + "totalAmount": 15000, + "totalCount": 5, + "remainCount": 5, + "claimStartTime": "2026-05-27T21:05:00+08:00", + "claimStartTimeMs": 1780000000000, + "expireTime": "2026-05-28T21:00:00+08:00", + "expireTimeMs": 1780086400000, + "status": "ACTIVE", + "currentUserClaimed": false, + "currentUserAmount": 0 + } + ] +} +``` + +`unclaimedCount` 统计当前用户未领取且未过期、未抢完的红包数量。 + +### 5.7 抢红包 + +```http +POST /app/voice-room/red-packet/claim +``` + +请求: + +```json +{ + "packetNo": "VRRP2026052712340001" +} +``` + +返回: + +```json +{ + "packetNo": "VRRP2026052712340001", + "claimed": true, + "amount": 3000, + "remainCount": 4, + "totalCount": 5, + "claimTime": "2026-05-27T21:05:10+08:00", + "claimTimeMs": 1780000010000, + "status": "ACTIVE" +} +``` + +领取失败但业务可展示的情况通过错误码返回: + +- `voice_room_red_packet_room_presence_required`:用户不在红包所属房间内,需要先进入房间。 +- `voice_room_red_packet_not_started`:延时红包未到可领取时间。 +- `voice_room_red_packet_expired`:红包已过期。 +- `voice_room_red_packet_finished`:红包已抢完。 +- `voice_room_region_not_match`:非同语区不可领取。 + +领取前校验: + +- 抢红包接口不创建、不刷新房间 presence,只读取 room-service 当前 presence。 +- 用户必须仍在 `packetNo` 对应的 `roomId` 房间内,否则返回 `voice_room_red_packet_room_presence_required`。 +- 进入房间和离开房间仍由通用房间接口负责,不在红包接口里重复实现。 + +幂等要求: + +- 同一用户重复领取同一红包,返回第一次领取结果。 +- 钱包入账处理中,返回 `claim_processing`,客户端可重试或查详情。 + +### 5.8 红包详情 + +```http +GET /app/voice-room/red-packet/detail?packetNo=VRRP2026052712340001 +``` + +返回: + +```json +{ + "packetNo": "VRRP2026052712340001", + "roomId": "9001", + "packetMode": "DELAYED", + "sendTime": "2026-05-27 21:00:00", + "sendTimeMs": 1779999700000, + "totalCount": 5, + "totalAmount": 15000, + "remainCount": 4, + "remainAmount": 12000, + "claimedCount": 1, + "claimedAmount": 3000, + "refundAmount": 0, + "claimStartTime": "2026-05-27T21:05:00+08:00", + "claimStartTimeMs": 1780000000000, + "expireTime": "2026-05-28T21:00:00+08:00", + "expireTimeMs": 1780086400000, + "status": "ACTIVE", + "sender": { + "userId": "10001", + "account": "10001", + "nickname": "Tom", + "avatar": "https://example.com/avatar.png" + }, + "currentUserClaimed": true, + "currentUserAmount": 3000 +} +``` + +### 5.9 领取记录列表 + +```http +GET /app/voice-room/red-packet/claim-records?packetNo=VRRP2026052712340001&cursor=&limit=20 +``` + +返回: + +```json +{ + "packetNo": "VRRP2026052712340001", + "totalCount": 5, + "claimedCount": 1, + "claimedAmount": 3000, + "records": [ + { + "id": "2052643780674195456", + "userId": "10002", + "account": "10002", + "nickname": "Jerry", + "avatar": "https://example.com/avatar2.png", + "amount": 3000, + "claimTime": "2026-05-27 21:05:10", + "claimTimeMs": 1780000010000 + } + ], + "nextCursor": "2052643780674195456", + "hasMore": false +} +``` + +### 5.10 发送记录列表 + +```http +GET /app/voice-room/red-packet/sent-records?cursor=&limit=20 +``` + +返回: + +```json +{ + "records": [ + { + "packetNo": "VRRP2026052712340001", + "roomId": "9001", + "packetMode": "DELAYED", + "sendTime": "2026-05-27 21:00:00", + "sendTimeMs": 1779999700000, + "totalCount": 5, + "totalAmount": 15000, + "refundAmount": 0, + "status": "ACTIVE" + } + ], + "nextCursor": "VRRP2026052712340001", + "hasMore": false +} +``` + +列表页展示字段: + +- 发送时间:`sendTime` +- 红包个数:`totalCount` +- 红包总金额:`totalAmount` +- 退回金额:`refundAmount` + +## 6. 后台接口 + +### 6.1 查询配置 + +```http +GET /resident-activity/voice-room-red-packet/config +``` + +返回: + +```json +{ + "enabled": true, + "dailySendLimit": 10, + "delaySeconds": 300, + "expireSeconds": 86400, + "amountOptions": [15000, 30000, 70000, 100000], + "countOptions": [5, 10, 20, 50], + "ruleUrl": "https://example.com/red-packet-rule" +} +``` + +### 6.2 保存配置 + +```http +POST /resident-activity/voice-room-red-packet/config +``` + +请求: + +```json +{ + "enabled": true, + "dailySendLimit": 10, + "delaySeconds": 300, + "amountOptions": [15000, 30000, 70000, 100000], + "countOptions": [5, 10, 20, 50], + "ruleUrl": "https://example.com/red-packet-rule" +} +``` + +说明: + +- `expireSeconds` 固定为 86400,不建议后台配置。 +- `amountOptions` 和 `countOptions` 必须都是正整数数组。 +- `totalAmount >= totalCount`,保证每份最小金额大于 0。 + +### 6.3 红包记录查询 + +```http +GET /resident-activity/voice-room-red-packet/records?sysOrigin=LIKEI&roomId=&senderUserId=&status=&startTime=&endTime=&page=1&pageSize=20 +``` + +返回: + +```json +{ + "page": 1, + "pageSize": 20, + "total": 1, + "records": [ + { + "packetNo": "VRRP2026052712340001", + "roomId": "9001", + "senderUserId": "10001", + "senderNickname": "Tom", + "packetMode": "DELAYED", + "totalAmount": 15000, + "totalCount": 5, + "claimedAmount": 3000, + "claimedCount": 1, + "refundAmount": 0, + "status": "ACTIVE", + "sendTime": "2026-05-27 21:00:00", + "expireTime": "2026-05-28 21:00:00" + } + ] +} +``` + +### 6.4 退款重试 + +```http +POST /resident-activity/voice-room-red-packet/refund/retry +``` + +请求: + +```json +{ + "packetNo": "VRRP2026052712340001" +} +``` + +返回: + +```json +{ + "packetNo": "VRRP2026052712340001", + "refundAmount": 3000, + "refundStatus": "SUCCESS" +} +``` + +## 7. 错误码 + +| code | 场景 | 客户端处理 | +| --- | --- | --- | +| `voice_room_red_packet_disabled` | 功能关闭 | 隐藏入口或提示暂不可用 | +| `voice_room_red_packet_amount_invalid` | 金额不在后台档位 | 刷新配置 | +| `voice_room_red_packet_count_invalid` | 个数不在后台档位 | 刷新配置 | +| `voice_room_red_packet_daily_limit_exceeded` | 今日发送上限 | 提示今日发送次数已达上限 | +| `voice_room_red_packet_room_presence_required` | 用户不在当前房间在线态 | 提示需在房间内操作 | +| `voice_room_red_packet_insufficient_balance` | 余额不足 | 拉起充值弹窗 | +| `voice_room_region_missing` | 用户区域缺失 | 提示暂不可参与 | +| `voice_room_region_not_match` | 非同语区领取 | 提示非同语区不可领取 | +| `voice_room_red_packet_not_found` | 红包不存在 | 刷新列表 | +| `voice_room_red_packet_not_started` | 延时红包未到时间 | 展示倒计时 | +| `voice_room_red_packet_expired` | 红包已过期 | 提示已过期并刷新侧边栏 | +| `voice_room_red_packet_finished` | 红包已抢完 | 提示已抢完并刷新侧边栏 | +| `claim_processing` | 钱包入账处理中 | 展示处理中,可重试或查详情 | +| `voice_room_red_packet_wallet_deduct_failed` | 扣款失败 | 提示发送失败 | +| `voice_room_red_packet_wallet_income_failed` | 入账失败 | 提示领取处理中或稍后重试 | +| `voice_room_red_packet_refund_failed` | 退款失败 | 后台重试 | + +## 8. 验证点 + +功能验证: + +- 发送即时红包,当前房间用户收到 `ROOM_RED_PACKET CREATED scope=ROOM`,同语区群不收到飘窗。 +- 发送延时红包,当前房间用户收到 `scope=ROOM` 弹窗,同语区用户收到 `scope=REGION` 飘窗。 +- 延时红包未到 `claimStartTime` 领取返回 `voice_room_red_packet_not_started`。 +- 延时红包到点后房间收到 `OPEN_DUE`,已关闭弹窗的用户被强弹。 +- 发送者本人可以领取自己发送的红包。 +- 同一用户重复领取同一红包返回同一领取结果。 +- 并发领取不会超过 `totalAmount` 和 `totalCount`。 +- 红包创建 24 小时后,未领取金额退回发送者。 +- 领取成功、抢完、过期后房间侧边栏未领取数量实时减少。 + +接口验证: + +- 所有成功接口同时返回 `body` 和 `data`。 +- 所有失败接口 `status=false`,`body/data=null`,`code` 与 `errorCodeName` 一致。 +- `requestId` 重复发送只生成一个红包。 +- `packetNo + userId` 重复领取只生成一条领取记录。 diff --git a/docs/语音房房间用户列表前端接入文档.md b/docs/语音房房间用户列表前端接入文档.md new file mode 100644 index 00000000..dc1ec2d8 --- /dev/null +++ b/docs/语音房房间用户列表前端接入文档.md @@ -0,0 +1,420 @@ +# 语音房房间用户列表前端接入文档 + +## 1. 接入范围 + + +- 房间在线用户列表接口。 +- 设置或取消房间管理员接口。 +- 相关腾讯云 IM 房间系统消息。 +- 前端错误处理建议。 + +## 2. 通用约定 + +### 2.1 鉴权 + +所有接口都需要登录态: + +```http +Authorization: Bearer +``` + +### 2.2 ID 类型 + +HTTP 返回里的用户 ID 统一按字符串处理,避免 JavaScript 大整数精度问题。 + +示例: + +```json +{ + "user_id": "10001" +} +``` + +### 2.3 时间类型 + +所有时间字段都是毫秒时间戳: + +```json +{ + "server_time_ms": 1760000006000 +} +``` + +### 2.4 房间身份 + +房间用户身份拆成两个字段: + +| 字段 | 说明 | +| --- | --- | +| `is_owner` | 是否是当前房间房主 | +| `room_role` | 是否是当前房间管理员,只表示管理员集合 | + +`room_role` 只会返回: + +| 值 | 说明 | +| --- | --- | +| `normal` | 当前房间普通用户 | +| `admin` | 当前房间管理员 | + +注意: + +- 房主不是管理员,房主用户返回 `is_owner = true`。 +- 房主如果不在管理员集合里,`room_role` 返回 `normal`。 +- 前端展示“房主”标识时看 `is_owner`,展示“管理员”标识时看 `room_role = admin`。 +- 当前登录用户是否展示管理员操作入口,优先看房间详情里的 `permissions.is_owner`;如果只在用户列表内判断,则看当前登录用户对应 item 的 `is_owner`。 +- `role` 是进房 presence 角色,例如 `audience`,不要用于判断房主或管理员。 + +## 3. 查询房间在线用户列表 + +### 3.1 接口定义 + +```http +GET /api/v1/rooms/{room_id}/online-users +``` + +### 3.2 Path 参数 + +| 参数 | 类型 | 必填 | 说明 | +| --- | --- | --- | --- | +| `room_id` | string | 是 | 房间 ID | + +### 3.3 Query 参数 + +| 参数 | 类型 | 必填 | 默认值 | 说明 | +| --- | --- | --- | --- | --- | +| `page` | int | 否 | `1` | 页码,从 `1` 开始 | +| `page_size` | int | 否 | `50` | 每页数量,最大 `100` | +| `sort` | string | 否 | `online` | `online` 按在线活跃排序;`gift_value` 按礼物价值排序 | + +### 3.4 请求示例 + +```bash +curl -H "Authorization: Bearer $TOKEN" \ + "https://api.global-interaction.com/api/v1/rooms/room_10001/online-users?page=1&page_size=50" +``` + +### 3.5 返回示例 + +```json +{ + "items": [ + { + "user_id": "10001", + "role": "audience", + "is_owner": true, + "room_role": "normal", + "gift_value": 188800, + "joined_at_ms": 1760000000000, + "last_seen_at_ms": 1760000005000, + "profile": { + "nickname": "Alice", + "avatar_url": "https://example.com/a.png" + } + }, + { + "user_id": "10002", + "role": "audience", + "is_owner": false, + "room_role": "admin", + "gift_value": 0, + "joined_at_ms": 1760000001000, + "last_seen_at_ms": 1760000004000, + "profile": { + "nickname": "Bob", + "avatar_url": "https://example.com/b.png" + } + } + ], + "total": 2, + "page": 1, + "page_size": 50, + "server_time_ms": 1760000006000 +} +``` + +### 3.6 返回字段 + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `items` | array | 当前页在线用户 | +| `items[].user_id` | string | 用户 ID | +| `items[].role` | string | 进房 presence 角色 | +| `items[].is_owner` | bool | 是否是当前房间房主 | +| `items[].room_role` | string | 当前房间管理员身份:`normal` 或 `admin` | +| `items[].gift_value` | int64 | 当前房间内累计送礼价值,未送礼返回 `0` | +| `items[].joined_at_ms` | int64 | 进房时间 | +| `items[].last_seen_at_ms` | int64 | 最近房间业务心跳时间 | +| `items[].profile.nickname` | string | 用户昵称 | +| `items[].profile.avatar_url` | string | 用户头像 | +| `total` | int64 | 在线用户总数 | +| `page` | int | 当前页 | +| `page_size` | int | 当前每页数量 | +| `server_time_ms` | int64 | 服务端时间 | + +### 3.7 前端展示建议 + +- `gift_value` 展示的是该用户在当前房间内“送出”的礼物价值,不是收到的礼物价值。 +- `gift_value = 0` 时仍然展示该用户。 +- 房主标识看 `is_owner = true`。 +- 管理员标识看 `room_role = admin`。 +- 房主不是管理员,不要因为 `is_owner = true` 就把管理员标识点亮。 +- 用户列表弹窗打开时,可以先拉第一页;滚动到底再拉下一页。 +- 收到相关 IM 后,如果列表弹窗正在展示,可以局部更新;状态不确定时重新拉接口。 + +## 4. 设置或取消管理员,只有房主才显示 + +### 4.1 接口定义 + +```http +POST /api/v1/rooms/admin/set +Content-Type: application/json +``` + +### 4.2 请求参数 + +| 参数 | 类型 | 必填 | 说明 | +| --- | --- | --- | --- | +| `room_id` | string | 是 | 房间 ID | +| `command_id` | string | 是 | 客户端生成的幂等 ID,重试同一次操作时保持不变 | +| `target_user_id` | string/int64 | 是 | 被设置或取消管理员的用户 ID | +| `enabled` | bool | 是 | `true` 设置管理员;`false` 取消管理员 | + +### 4.3 设置管理员请求示例 + +```json +{ + "room_id": "room_10001", + "command_id": "cmd-10001", + "target_user_id": "10002", + "enabled": true +} +``` + +### 4.4 取消管理员请求示例 + +```json +{ + "room_id": "room_10001", + "command_id": "cmd-10002", + "target_user_id": "10002", + "enabled": false +} +``` + +### 4.5 返回示例 + +建议后端返回: + +```json +{ + "result": { + "applied": true, + "room_version": 12 + }, + "target_user_id": "10002", + "room_role": "admin", + "server_time_ms": 1760000006000 +} +``` + +字段说明: + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `result.applied` | bool | 本次请求是否实际改变了房间状态 | +| `result.room_version` | int64 | 房间状态版本 | +| `target_user_id` | string | 被操作用户 | +| `room_role` | string | 操作后的身份,`admin` 或 `normal` | +| `server_time_ms` | int64 | 服务端时间 | + +如果后端第一版仍直接返回完整 room-service gRPC 结构,前端只需要关注命令是否成功;最终用户列表状态以接口重拉或 IM 刷新为准。 + +### 4.6 前端操作边界 + +- 只有房主可以设置或取消管理员。 +- 房主可以操作所有非房主用户的管理员身份。 +- 管理员之间不可互相设置或取消管理员。 +- 房主不是管理员,不允许对房主执行设置或取消管理员。 +- 目标用户必须当前在房间内。 +- 前端重试同一次操作时,必须复用同一个 `command_id`。 +- 操作成功后,可以等待 `room_admin_changed` IM,也可以直接刷新用户列表。 + +## 5. 错误处理 + +### 5.1 通用错误 + +| HTTP 状态 | 场景 | 前端处理 | +| --- | --- | --- | +| `401` | 未登录或 token 失效 | 跳登录或刷新登录态 | +| `403` | 无权限,例如用户不在房间或不是房主 | 提示无权限,并刷新房间状态 | +| `404` | 房间不存在或目标用户不在房间 | 提示目标不存在或用户已离开 | +| `409` | 状态冲突,例如目标用户是房主 | 提示当前状态不允许操作,并刷新用户列表 | +| `5xx` | 服务端异常 | 保留当前 UI,提示稍后重试 | + +### 5.2 查询用户列表错误 + +| 场景 | 前端处理 | +| --- | --- | +| 当前用户不在房间 | 关闭用户列表弹窗,刷新当前房间状态 | +| 分页为空 | 正常展示空列表或结束加载更多 | +| `page_size` 超过上限 | 使用服务端返回的 `page_size` 为准 | + +### 5.3 设置管理员错误 + +| 场景 | 前端处理 | +| --- | --- | +| 非房主操作 | 隐藏或禁用管理入口,提示无权限 | +| 操作目标是房主 | 不展示管理员操作入口;如果服务端返回错误,刷新列表 | +| 目标用户已离房 | 提示用户已离开,刷新列表 | +| 重复点击 | 前端按钮进入 loading;同一次请求复用同一个 `command_id` | +| 请求超时 | 可以用同一个 `command_id` 重试 | + +## 6. 相关 IM + +### 6.1 IM 消息形态 + +房间系统消息通过腾讯云 IM `TIMCustomElem` 下发。 + +前端解析入口: + +```text +TIMCustomElem.MsgContent.Data +``` + +`Data` 是 JSON 字符串。 + +示例: + +```json +{ + "event_id": "evt-admin-10001", + "room_id": "room_10001", + "event_type": "room_admin_changed", + "actor_user_id": 10001, + "target_user_id": 10002, + "room_version": 12, + "attributes": { + "enabled": "true" + } +} +``` + +通用字段: + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `event_id` | string | 事件唯一 ID,前端用于去重 | +| `room_id` | string | 房间 ID | +| `event_type` | string | 事件类型 | +| `actor_user_id` | int64 | 操作人 | +| `target_user_id` | int64 | 被操作人 | +| `room_version` | int64 | 房间版本 | +| `attributes` | object | 字符串键值扩展字段 | + +### 6.2 管理员变更 IM + +事件类型: + +```text +room_admin_changed +``` + +消息示例: + +```json +{ + "event_id": "evt-admin-10001", + "room_id": "room_10001", + "event_type": "room_admin_changed", + "actor_user_id": 10001, + "target_user_id": 10002, + "room_version": 12, + "attributes": { + "enabled": "true" + } +} +``` + +字段说明: + +| 字段 | 说明 | +| --- | --- | +| `actor_user_id` | 发起操作的房主 | +| `target_user_id` | 被设置或取消管理员的用户 | +| `attributes.enabled` | `"true"` 设置管理员;`"false"` 取消管理员 | + +前端处理: + +- 使用 `event_id` 去重。 +- 如果当前用户是 `target_user_id`,更新当前用户管理权限 UI。 +- 如果用户列表已打开,更新对应用户的 `room_role`。 +- 如果列表数据不完整,重新请求在线用户列表接口。 + +### 6.3 送礼成功 IM + +事件类型: + +```text +room_gift_sent +``` + +消息示例: + +```json +{ + "event_id": "evt-gift-10001", + "room_id": "room_10001", + "event_type": "room_gift_sent", + "actor_user_id": 10002, + "target_user_id": 10003, + "gift_value": 5200, + "room_heat": 999999, + "room_version": 13, + "attributes": { + "gift_id": "rose", + "pool_id": "", + "gift_count": "10", + "billing_receipt_id": "receipt-10001", + "coin_spent": "5200", + "gift_point_added": "5200", + "price_version": "v1" + } +} +``` + +字段说明: + +| 字段 | 说明 | +| --- | --- | +| `actor_user_id` | 送礼用户,用户列表里需要累加 `gift_value` 的用户 | +| `target_user_id` | 收礼用户 | +| `gift_value` | 本次送礼价值增量 | +| `room_heat` | 送礼后的房间热度 | +| `attributes.gift_id` | 礼物 ID | +| `attributes.gift_count` | 礼物数量,字符串 | +| `attributes.billing_receipt_id` | 钱包结算流水 | + +前端处理: + +- 如果用户列表已打开,并且当前页包含 `actor_user_id`,可以执行 `gift_value += event.gift_value`。 +- 如果当前列表按 `gift_value` 排序,建议重新拉接口,避免分页顺序错乱。 +- 如果当前页不包含 `actor_user_id`,不要手动插入用户,分页完整性以接口为准。 + +### 6.4 进出房 IM + +用户列表也会受进出房影响: + +| 事件 | 建议处理 | +| --- | --- | +| `room_user_joined` | 用户列表打开时重新拉列表 | +| `room_user_left` | 用户列表打开时移除用户或重新拉列表 | + +## 7. 前端刷新策略 + +推荐策略: + +1. 打开用户列表弹窗时,请求 `GET /api/v1/rooms/{room_id}/online-users`。 +2. 收到 `room_admin_changed` 时,局部更新 `room_role`;不确定时重拉。 +3. 收到 `room_gift_sent` 时,局部累加 `gift_value`;如果当前排序依赖礼物价值,重拉。 +4. 收到 `room_user_joined` 或 `room_user_left` 时,列表打开状态下重拉。 +5. 所有 IM 使用 `event_id` 去重。 diff --git a/docs/语音房房间用户列表后端技术架构设计.md b/docs/语音房房间用户列表后端技术架构设计.md new file mode 100644 index 00000000..d5267f1c --- /dev/null +++ b/docs/语音房房间用户列表后端技术架构设计.md @@ -0,0 +1,574 @@ +# 语音房房间用户列表后端技术架构设计 + +## 1. 背景 + +当前 `room-service` 已有基础能力: + +- `GET /api/v1/rooms/{room_id}/online-users` 查询在线用户。 +- `POST /api/v1/rooms/admin/set` 设置或取消管理员。 +- `RoomAdminChanged` outbox 事件和腾讯云 IM `room_admin_changed` 消息。 +- `RoomGiftSent` outbox 事件和腾讯云 IM `room_gift_sent` 消息。 + +现有在线用户列表只返回 presence 信息,不包含: + +- 用户在当前房间内累计送出的礼物价值。 +- 用户针对当前房间的管理身份:`normal` 或 `admin`。 + +## 2. 目标 + +本次后端开发目标: + +1. 在线用户列表返回所有当前在房间内的用户。 +2. 每个用户返回当前房间内累计送礼价值 `gift_value`。 +3. 每个用户返回是否为房主 `is_owner`。 +4. 每个用户返回当前房间管理员身份 `room_role`,只允许 `normal` 或 `admin`。 +5. 复用同一个接口完成设置管理员和取消管理员。 +6. 管理员变更和送礼成功继续通过 IM 通知客户端刷新。 + +## 3. 非目标 + +本次不做: + +- 历史房间贡献榜。 +- 日榜、周榜、月榜。 +- 离线预设管理员。 +- 平台后台管理员权限。 +- 客户端上报价格参与礼物价值计算。 +- 使用 `GiftRank` 作为完整用户礼物价值数据源。 + +## 4. 现有代码基础 + +### 4.1 Proto + +文件: + +```text +api/proto/room/v1/room.proto +``` + +现有 RPC: + +```proto +rpc ListRoomOnlineUsers(ListRoomOnlineUsersRequest) returns (ListRoomOnlineUsersResponse); +rpc SetRoomAdmin(SetRoomAdminRequest) returns (SetRoomAdminResponse); +``` + +现有 `RoomUser.role` 已用于 presence 角色,不能改成 `normal/admin`。 + +### 4.2 房间状态 + +文件: + +```text +services/room-service/internal/room/state/state.go +``` + +当前 `RoomState` 已包含: + +| 字段 | 说明 | +| --- | --- | +| `OnlineUsers` | 当前房间业务在线用户 | +| `AdminUsers` | 当前房间管理员集合 | +| `GiftRank` | 房间礼物榜 Top N | + +注意:`GiftRank` 受 `rank_limit` 限制,不能覆盖所有用户。 + +### 4.3 设置管理员 + +文件: + +```text +services/room-service/internal/room/service/management.go +``` + +`SetRoomAdmin` 需要满足以下行为: + +- 只有房主可操作。 +- 目标用户必须在当前房间内。 +- 房主不是管理员,不允许对房主执行设置或取消管理员。 +- 管理员之间不可互相操作。 +- 重复设置同一状态是 no-op。 +- 状态变化时生成 `RoomAdminChanged` outbox 事件。 +- IM 事件类型为 `room_admin_changed`。 + +这部分服务逻辑可以复用,主要补 HTTP DTO 和文档约束。 + +### 4.4 送礼事件 + +文件: + +```text +services/room-service/internal/room/service/gift.go +``` + +当前 `SendGift` 成功后: + +- 使用 wallet-service 结算结果。 +- 使用 `heatValue` 增加房间热度。 +- 使用 `heatValue` 更新本地礼物榜。 +- 生成 `RoomGiftSent`、`RoomHeatChanged`、`RoomRankChanged` outbox 事件。 +- 对客户端只投递 `room_gift_sent` IM。 + +新增 `gift_value` 必须使用同一个 `heatValue` 口径。 + +## 5. 数据模型设计 + +### 5.1 在线来源 + +在线用户来源保持不变: + +```text +room_user_presence +``` + +判断条件: + +```text +app_code = ? +room_id = ? +status = active +``` + +### 5.2 礼物价值统计表 + +新增完整房间用户送礼统计表: + +```sql +CREATE TABLE room_user_gift_stats ( + app_code VARCHAR(64) NOT NULL, + room_id VARCHAR(128) NOT NULL, + user_id BIGINT NOT NULL, + gift_value BIGINT NOT NULL DEFAULT 0, + last_gift_at_ms BIGINT NOT NULL DEFAULT 0, + created_at_ms BIGINT NOT NULL, + updated_at_ms BIGINT NOT NULL, + PRIMARY KEY (app_code, room_id, user_id), + KEY idx_room_user_gift_stats_room_value ( + app_code, + room_id, + gift_value DESC, + last_gift_at_ms DESC, + user_id + ) +); +``` + +字段说明: + +| 字段 | 说明 | +| --- | --- | +| `app_code` | 应用编码 | +| `room_id` | 房间 ID | +| `user_id` | 送礼用户 ID | +| `gift_value` | 当前房间生命周期内累计送礼价值 | +| `last_gift_at_ms` | 最近一次成功送礼时间 | +| `created_at_ms` | 创建时间 | +| `updated_at_ms` | 更新时间 | + +### 5.3 为什么不复用 `GiftRank` + +`GiftRank` 是榜单结构,不是完整统计结构: + +- 受 `rank_limit` 限制,默认只保留 Top N。 +- 不包含送礼价值为 `0` 的用户。 +- 在线用户超过 Top N 时无法返回完整 `gift_value`。 + +所以在线用户列表必须读取完整统计表,并且对无统计记录的在线用户返回 `gift_value = 0`。 + +## 6. Proto 设计 + +新增在线用户展示结构,不改原 `RoomUser` 语义: + +```proto +message RoomOnlineUser { + int64 user_id = 1; + string role = 2; + string room_role = 3; + int64 gift_value = 4; + int64 joined_at_ms = 5; + int64 last_seen_at_ms = 6; + bool is_owner = 7; +} +``` + +扩展响应: + +```proto +message ListRoomOnlineUsersResponse { + repeated RoomUser users = 1; + int64 total = 2; + int32 page = 3; + int32 page_size = 4; + int64 server_time_ms = 5; + repeated RoomOnlineUser items = 6; +} +``` + +兼容策略: + +- `users = 1` 保留旧客户端和旧 gateway 兼容。 +- 新 gateway 优先使用 `items = 6`。 +- 不把 `RoomUser.role` 改成 `normal/admin`。 + +## 7. 查询链路设计 + +### 7.1 HTTP 链路 + +```text +gateway-service + -> room-service ListRoomOnlineUsers + -> user-service/profile 聚合用户展示资料 + -> HTTP 返回 items +``` + +HTTP 返回字段需要新增: + +| 字段 | 来源 | +| --- | --- | +| `is_owner` | `user_id == owner_user_id` | +| `room_role` | room-service 基于管理员集合派生 | +| `gift_value` | room-service 查询统计表 | +| `joined_at_ms` | room_user_presence | +| `last_seen_at_ms` | room_user_presence | +| `profile` | gateway 现有聚合逻辑 | + +### 7.2 room-service 查询流程 + +1. 校验 `room_id`、`viewer_user_id`、分页参数。 +2. 校验调用方当前在该房间内。 +3. 读取当前房间 snapshot,拿到 `owner_user_id` 和 `admin_user_ids`。 +4. 查询 `room_user_presence` 当前在线用户分页。 +5. 左连接 `room_user_gift_stats` 获取 `gift_value`。 +6. 服务层派生每个用户的 `is_owner` 和 `room_role`。 +7. 返回 `items` 和兼容字段 `users`。 + +### 7.3 `is_owner` 和 `room_role` 派生规则 + +```text +if user_id == owner_user_id: + is_owner = true +else: + is_owner = false + +if user_id in admin_user_ids: + room_role = "admin" +else: + room_role = "normal" +``` + +房主不是管理员,所以不能因为 `is_owner = true` 就返回 `room_role = admin`。 + +不建议把 `room_role` 冗余写入 `room_user_presence`,因为管理员身份属于房间状态,不属于 presence 状态。 + +如果当前代码为了兼容权限把房主写入或恢复进 `AdminUsers`,在线列表展示和管理员操作必须做过滤: + +- 展示时 `user_id == owner_user_id` 固定按 `is_owner = true` 返回,`room_role` 不因房主身份变成 `admin`。 +- 设置或取消管理员时,`target_user_id == owner_user_id` 直接拒绝。 +- 管理员集合里的 owner 不能对外表现为管理员。 + +### 7.4 排序 + +第一版建议保持现有在线排序,避免影响旧客户端: + +```sql +ORDER BY last_seen_at_ms DESC, user_id ASC +``` + +如果要支持 `sort=gift_value`: + +```sql +ORDER BY gift_value DESC, last_seen_at_ms DESC, user_id ASC +``` + +分页总数仍以当前在线用户数为准。 + +## 8. 写入链路设计 + +### 8.1 送礼成功累计 `gift_value` + +在 `SendGift` 钱包扣款成功并准备提交房间状态时,使用结算后的 `heatValue` 累加: + +```sql +INSERT INTO room_user_gift_stats ( + app_code, + room_id, + user_id, + gift_value, + last_gift_at_ms, + created_at_ms, + updated_at_ms +) VALUES (?, ?, ?, ?, ?, ?, ?) +ON DUPLICATE KEY UPDATE + gift_value = gift_value + VALUES(gift_value), + last_gift_at_ms = VALUES(last_gift_at_ms), + updated_at_ms = VALUES(updated_at_ms); +``` + +写入边界: + +- 只在送礼成功后写入。 +- 使用 `heatValue`,不能使用客户端价格。 +- 统计送礼用户 `actor_user_id`,不是收礼用户。 +- 必须和现有命令幂等边界一致,重复命令不能重复累计。 + +### 8.2 幂等要求 + +已有房间命令链路通过 `command_id` 判断命令是否已应用。 + +新增统计写入必须满足: + +- 命令第一次成功应用时累计一次。 +- 同一 `command_id` 重试时不再次累计。 +- recovery replay 不能重复写统计表。 + +推荐做法: + +- 将统计写入纳入保存 mutation 的同一事务或同一 applied 分支。 +- 已 applied 的命令直接返回已有结果,不进入统计累加。 + +### 8.3 历史数据补偿 + +如果上线前已有房间内送礼数据,需要决定是否补偿: + +- 不补偿:上线后新送礼才有 `gift_value`,旧房间可能显示不完整。 +- 补偿:从已有 `RoomGiftSent` outbox 或命令日志回放,按 `sender_user_id` 和 `gift_value` 聚合写入 `room_user_gift_stats`。 + +产品如果要求老房间准确,必须执行补偿。 + +## 9. 管理员接口设计 + +### 9.1 现有 HTTP 接口 + +```http +POST /api/v1/rooms/admin/set +``` + +请求: + +```json +{ + "room_id": "room_10001", + "command_id": "cmd-10001", + "target_user_id": "10002", + "enabled": true +} +``` + +### 9.2 服务边界 + +保持现有 `SetRoomAdmin` 逻辑: + +- 只有房主可设置或取消。 +- 目标用户必须当前在房间内。 +- 房主不是管理员,不能对房主执行设置或取消管理员。 +- 管理员之间不可互相操作。 +- 重复设置同一状态不生成重复 outbox 和 IM。 +- 成功变更时写入 `RoomAdminChanged` outbox。 + +### 9.3 Gateway DTO + +建议不要直接透出完整 `RoomSnapshot`,新增稳定 HTTP DTO: + +```json +{ + "result": { + "applied": true, + "room_version": 12 + }, + "target_user_id": "10002", + "room_role": "admin", + "server_time_ms": 1760000006000 +} +``` + +## 10. IM 设计 + +### 10.1 投递形态 + +room-service 通过 outbox worker 向腾讯云 IM 投递 `TIMCustomElem`: + +| Tencent 字段 | 值 | +| --- | --- | +| `MsgType` | `TIMCustomElem` | +| `MsgContent.Ext` | `room_system_message` | +| `MsgContent.Desc` | 客户端事件类型,例如 `room_admin_changed` | +| `MsgContent.Data` | JSON 序列化后的 `RoomEvent` | +| `CloudCustomData` | 同一份 JSON,用于服务端透传和排查 | + +### 10.2 管理员变更 + +outbox 事件: + +```text +RoomAdminChanged +``` + +客户端事件: + +```text +room_admin_changed +``` + +payload: + +```json +{ + "event_id": "evt-admin-10001", + "room_id": "room_10001", + "event_type": "room_admin_changed", + "actor_user_id": 10001, + "target_user_id": 10002, + "room_version": 12, + "attributes": { + "enabled": "true" + } +} +``` + +字段来源: + +| 字段 | 来源 | +| --- | --- | +| `event_id` | outbox event id | +| `actor_user_id` | `RoomAdminChanged.actor_user_id` | +| `target_user_id` | `RoomAdminChanged.target_user_id` | +| `attributes.enabled` | `RoomAdminChanged.enabled` 转字符串 | + +### 10.3 送礼成功 + +outbox 事件: + +```text +RoomGiftSent +``` + +客户端事件: + +```text +room_gift_sent +``` + +payload: + +```json +{ + "event_id": "evt-gift-10001", + "room_id": "room_10001", + "event_type": "room_gift_sent", + "actor_user_id": 10002, + "target_user_id": 10003, + "gift_value": 5200, + "room_heat": 999999, + "room_version": 13, + "attributes": { + "gift_id": "rose", + "pool_id": "", + "gift_count": "10", + "billing_receipt_id": "receipt-10001", + "coin_spent": "5200", + "gift_point_added": "5200", + "price_version": "v1" + } +} +``` + +字段来源: + +| 字段 | 来源 | +| --- | --- | +| `actor_user_id` | 送礼用户 | +| `target_user_id` | 收礼用户 | +| `gift_value` | 本次 `heatValue` | +| `room_heat` | 送礼后的房间热度 | +| `attributes.billing_receipt_id` | wallet-service 结算流水 | + +### 10.4 不新增列表专用 IM + +用户列表接口本身不新增 IM。 + +客户端刷新依赖已有事件: + +| 变化 | IM | +| --- | --- | +| 进房 | `room_user_joined` | +| 离房 | `room_user_left` | +| 管理员变化 | `room_admin_changed` | +| 送礼成功 | `room_gift_sent` | + +## 11. 错误边界 + +| 场景 | gRPC code | HTTP code | +| --- | --- | --- | +| `room_id` 为空 | `INVALID_ARGUMENT` | `400` | +| `viewer_user_id` 为空 | `INVALID_ARGUMENT` | `400` | +| 调用方不在房间内 | `PERMISSION_DENIED` | `403` | +| 房间不存在 | `NOT_FOUND` | `404` | +| 非房主设置管理员 | `PERMISSION_DENIED` | `403` | +| 目标用户不在房间 | `NOT_FOUND` | `404` | +| 操作目标是房主 | `FAILED_PRECONDITION` | `409` | +| `page_size` 超过上限 | 正常截断 | `200` | + +## 12. 开发步骤 + +1. 新增 MySQL migration:`room_user_gift_stats`。 +2. 修改 `api/proto/room/v1/room.proto`,新增 `RoomOnlineUser` 和 `ListRoomOnlineUsersResponse.items`。 +3. 重新生成 proto 代码。 +4. 扩展 room repository: + - 送礼统计累加方法。 + - 在线用户列表查询左连接礼物统计。 +5. 修改 `SendGift` 成功提交路径,累计 `gift_value`。 +6. 修改 `ListRoomOnlineUsers` service: + - 校验 viewer 在房间内。 + - 读取 snapshot 派生 `is_owner` 和 `room_role`。 + - 返回 `items`。 +7. 修改 gateway 在线用户 DTO: + - 增加 `is_owner`。 + - 增加 `room_role`。 + - 增加 `gift_value`。 + - 增加 `joined_at_ms`。 +8. 修改 gateway 管理员设置响应 DTO,避免直接暴露完整 snapshot。 +9. 补充单元测试和 HTTP handler 测试。 + +## 13. 测试清单 + +### 13.1 在线用户列表 + +- 普通用户返回 `room_role = normal`。 +- 房主返回 `is_owner = true`。 +- 房主不是管理员时返回 `room_role = normal`。 +- 被设置管理员的用户返回 `room_role = admin`。 +- 未送礼用户返回 `gift_value = 0`。 +- 已送礼用户返回累计 `gift_value`。 +- 在线用户超过 `rank_limit` 时,非 Top N 用户仍有正确 `gift_value`。 +- 不在房间内的用户查询失败。 + +### 13.2 管理员设置 + +- 房主设置管理员成功。 +- 房主取消管理员成功。 +- 非房主设置失败。 +- 目标用户不在房间时失败。 +- 对房主执行设置或取消管理员失败。 +- 管理员之间互相设置或取消管理员失败。 +- 重复设置同一状态不重复发送 `RoomAdminChanged`。 + +### 13.3 送礼统计 + +- 成功送礼累计一次。 +- 钱包扣款失败不累计。 +- 同一 `command_id` 重试不重复累计。 +- recovery replay 不重复累计。 + +### 13.4 IM + +- 管理员变更投递 `room_admin_changed`。 +- `attributes.enabled = "true"` 表示设置管理员。 +- `attributes.enabled = "false"` 表示取消管理员。 +- 送礼成功投递 `room_gift_sent`。 +- `room_gift_sent.gift_value` 与统计表累加值口径一致。 + +建议执行: + +```bash +go test ./services/room-service/... ./services/gateway-service/... +``` diff --git a/services/activity-service/internal/service/luckygift/config.go b/services/activity-service/internal/service/luckygift/config.go index da790371..f48b437e 100644 --- a/services/activity-service/internal/service/luckygift/config.go +++ b/services/activity-service/internal/service/luckygift/config.go @@ -55,14 +55,17 @@ func DefaultConfig(appCode, poolID string) domain.Config { } } +// validateConfig 只校验会破坏线上抽奖不变量的字段;展示类默认值留给后台表单处理。 func validateConfig(config domain.Config) error { if normalizePoolID(config.PoolID) == "" { return xerr.New(xerr.InvalidArgument, "lucky gift pool id is required") } + // 当前 DB 历史列名仍叫 gift_id,但业务语义已经是 pool_id;这里强制二者一致,避免后台提交真实礼物 ID 后把窗口和奖池切散。 config.GiftID = normalizePoolID(config.PoolID) if config.GiftPrice <= 0 || config.TargetRTPPPM <= 0 || config.TargetRTPPPM > ppmScale { return xerr.New(xerr.InvalidArgument, "gift price or target RTP is invalid") } + // 入池比例低于目标 RTP 时,即使长期抽数足够也无法靠奖池支付基础返奖,必须在发布前拒绝。 if config.PoolRatePPM < config.TargetRTPPPM || config.PoolRatePPM > ppmScale { return xerr.New(xerr.InvalidArgument, "pool rate must be between target RTP and 100%") } @@ -83,6 +86,7 @@ func validateConfig(config domain.Config) error { config.DeviceDailyPayoutCap <= 0 || config.RoomHourlyPayoutCap <= 0 || config.AnchorDailyPayoutCap <= 0 { return xerr.New(xerr.InvalidArgument, "risk payout caps must be positive") } + // tiers 是倍率列表归一化后的运行时候选集合;即使后台只提交 multiplier_ppms,发布前也必须先生成 tiers。 if len(config.Tiers) == 0 { return xerr.New(xerr.InvalidArgument, "reward tiers are required") } @@ -95,18 +99,21 @@ func validateConfig(config domain.Config) error { maxMultiplierPPM = tier.MultiplierPPM } } + // 最大倍率低于目标 RTP 时,窗口最后几抽可能没有足够高的基础候选追平目标,只能提前拒绝这类配置。 if maxMultiplierPPM < config.TargetRTPPPM { return xerr.New(xerr.InvalidArgument, "max lucky gift multiplier must not be lower than target RTP") } return nil } +// normalizeTiers 把后台“可中倍率”转换成三档体验池候选;后台传入的权重不会成为线上概率。 func normalizeTiers(tiers []domain.Tier, multipliers []int64, giftPrice, highMultiplier int64) ([]domain.Tier, []int64) { multiplierPPMs := normalizeMultiplierPPMs(multipliers, tiers, giftPrice) return buildMultiplierTiers(giftPrice, highMultiplier, multiplierPPMs), multiplierPPMs } func normalizeMultiplierPPMs(input []int64, tiers []domain.Tier, giftPrice int64) []int64 { + // 新后台直接提交 multiplier_ppms;旧调用方如果仍提交 tiers,则从 reward_coins 反推倍率以减少迁移断点。 values := append([]int64(nil), input...) if len(values) == 0 { for _, tier := range tiers { @@ -119,18 +126,21 @@ func normalizeMultiplierPPMs(input []int64, tiers []domain.Tier, giftPrice int64 } } } + // 没有任何输入时使用保守默认倍率,保证默认草稿可被后台查看和复制,但仍默认 disabled。 if len(values) == 0 { values = defaultMultiplierPPMs() } seen := map[int64]bool{} out := make([]int64, 0, len(values)) for _, value := range values { + // 负倍率没有业务含义;重复倍率会造成同一返奖点被重复计权,因此在入口处去重。 if value < 0 || seen[value] { continue } seen[value] = true out = append(out, value) } + // 全部被过滤时回退默认列表,避免构造出空 tiers 后在更深层事务里失败。 if len(out) == 0 { out = defaultMultiplierPPMs() } @@ -147,6 +157,7 @@ func buildMultiplierTiers(cost, highMultiplier int64, multipliers []int64) []dom TierID: pool + "_" + multiplierTierID(multiplierPPM), RewardCoins: cost * multiplierPPM / ppmScale, MultiplierPPM: multiplierPPM, + // 权重固定为 0 是有意设计:线上概率由 RTP 缺口、奖池容量和风控容量运行时生成。 Weight: 0, HighWaterOnly: highMultiplier > 0 && multiplierPPM >= highMultiplier*ppmScale, Enabled: true, @@ -185,6 +196,7 @@ func multiplierTierID(multiplierPPM int64) string { if fraction == 0 { return fmt.Sprintf("%dx", whole) } + // 小数倍率用下划线表达,避免 tier_id 出现点号后和后台路径/筛选语法冲突。 text := strings.TrimRight(strings.TrimRight(fmt.Sprintf("%d_%06dx", whole, fraction), "0"), "_") return text } diff --git a/services/activity-service/internal/service/luckygift/service.go b/services/activity-service/internal/service/luckygift/service.go index c9e1a6d1..4276b7fd 100644 --- a/services/activity-service/internal/service/luckygift/service.go +++ b/services/activity-service/internal/service/luckygift/service.go @@ -100,6 +100,7 @@ func (s *Service) Check(ctx context.Context, cmd domain.CheckCommand) (domain.Ch if err := s.requireRepository(); err != nil { return domain.CheckResult{}, err } + // Check 是送礼前拦截,不产生抽奖事实;这里只归一化入参,最终启停仍以仓储中的当前规则为准。 cmd.PoolID = normalizePoolID(cmd.PoolID) cmd.GiftID = strings.TrimSpace(cmd.GiftID) cmd.RoomID = strings.TrimSpace(cmd.RoomID) @@ -113,6 +114,7 @@ func (s *Service) Draw(ctx context.Context, cmd domain.DrawCommand) (domain.Draw if err := s.requireRepository(); err != nil { return domain.DrawResult{}, err } + // Draw 只接受扣费成功后的服务端事实;客户端不能提交价格、倍率、奖励或风控绕过字段。 cmd.CommandID = strings.TrimSpace(cmd.CommandID) cmd.PoolID = normalizePoolID(cmd.PoolID) cmd.GiftID = strings.TrimSpace(cmd.GiftID) @@ -126,6 +128,7 @@ func (s *Service) Draw(ctx context.Context, cmd domain.DrawCommand) (domain.Draw return domain.DrawResult{}, xerr.New(xerr.InvalidArgument, "lucky gift draw metadata is invalid") } if cmd.PaidAtMS <= 0 { + // paid_at_ms 是扣费事实时间,缺失时用 UTC 服务端时间补齐,避免使用本地时区切风控窗口。 cmd.PaidAtMS = s.now().UTC().UnixMilli() } return s.repository.ExecuteLuckyGiftDraw(ctx, cmd, s.now().UTC().UnixMilli()) @@ -162,12 +165,14 @@ func (s *Service) ProcessPendingDrawOutbox(ctx context.Context, options WorkerOp return 0, err } options = normalizeWorkerOptions(options) + // outbox 先抢占再处理,避免多实例同时给同一 draw 返奖或重复投递房间 IM。 events, err := s.repository.ClaimPendingLuckyGiftOutbox(ctx, options.WorkerID, s.now().UTC().UnixMilli(), options.LockTTL, options.BatchSize) if err != nil { return 0, err } processed := 0 for _, event := range events { + // 单条失败立即返回,让外层记录批次错误;已处理成功的事件已经各自落 delivered,不会被回滚。 if err := s.processDrawOutbox(ctx, event, options); err != nil { return processed, err } @@ -179,6 +184,7 @@ func (s *Service) ProcessPendingDrawOutbox(ctx context.Context, options WorkerOp func (s *Service) processDrawOutbox(ctx context.Context, event domain.DrawOutbox, options WorkerOptions) error { payload, err := decodeDrawOutboxPayload(event.PayloadJSON) if err != nil { + // payload 不可解析说明抽奖事实旁路已经损坏,重试也无法恢复,直接走失败收敛。 return s.markOutboxFailed(ctx, event, options, err, false) } eventCtx := appcode.WithContext(ctx, event.AppCode) @@ -188,6 +194,7 @@ func (s *Service) processDrawOutbox(ctx context.Context, event domain.DrawOutbox if s.wallet == nil { return s.markOutboxFailed(eventCtx, event, options, fmt.Errorf("lucky gift wallet client is not configured"), false) } + // 返奖 command_id 派生自 draw_id,钱包侧可用它做幂等;worker 重试不会重复加币。 resp, err := s.wallet.CreditLuckyGiftReward(eventCtx, &walletv1.CreditLuckyGiftRewardRequest{ CommandId: "lucky_reward:" + payload.DrawID, AppCode: event.AppCode, @@ -206,12 +213,14 @@ func (s *Service) processDrawOutbox(ctx context.Context, event domain.DrawOutbox credited = true } if s.publisher == nil { + // 已返奖但缺少房间发布依赖时不能把 draw 标记 failed,否则会误导账务;只让 outbox 保持可重试。 return s.markOutboxFailed(eventCtx, event, options, fmt.Errorf("lucky gift room publisher is not configured"), credited) } if shouldPublishLuckyGiftRegionBroadcast(payload) { if s.broadcaster == nil { return s.markOutboxFailed(eventCtx, event, options, fmt.Errorf("lucky gift region broadcaster is not configured"), credited) } + // 区域播报先写 broadcast outbox,不直接发 IM;这里失败意味着大额表现未落地,需要保留重试。 if err := s.publishLuckyGiftRegionBroadcast(eventCtx, payload); err != nil { return s.markOutboxFailed(eventCtx, event, options, err, credited) } @@ -220,6 +229,7 @@ func (s *Service) processDrawOutbox(ctx context.Context, event domain.DrawOutbox return s.markOutboxFailed(eventCtx, event, options, err, credited) } nowMS := s.now().UTC().UnixMilli() + // 所有外部副作用都成功后再把 draw 从 pending 收敛到 granted;客户端据此区分“处理中”和“已完成”。 if err := s.repository.MarkLuckyGiftDrawGranted(eventCtx, event.AppCode, payload.DrawID, walletTransactionID, nowMS); err != nil { return err } @@ -231,6 +241,7 @@ func (s *Service) publishLuckyGiftDrawn(ctx context.Context, payload luckyGiftDr if err != nil { return err } + // 房间 IM 只是表现层事实同步,不修改 Room Cell;超时由 outbox 重试兜底。 publishCtx, cancel := context.WithTimeout(ctx, options.PublishTimeout) defer cancel() return s.publisher.PublishGroupCustomMessage(publishCtx, tencentim.CustomGroupMessage{ @@ -264,10 +275,12 @@ func (s *Service) markOutboxFailed(ctx context.Context, event domain.DrawOutbox, nextRetryCount := event.RetryCount + 1 if nextRetryCount >= options.MaxRetry { if !alreadyCredited { + // 未返奖的 draw 可以标记 failed;已返奖但 IM 失败时不能覆盖账务事实,只停 outbox 并告警。 _ = s.repository.MarkLuckyGiftDrawFailed(ctx, event.AppCode, drawIDFromPayload(event.PayloadJSON), cause.Error(), nowMS) } return s.repository.MarkLuckyGiftOutboxFailed(ctx, event, nextRetryCount, cause.Error(), nowMS) } + // 指数退避降低故障期间对钱包和 IM 的压力,同时保留 pending 状态给客户端展示“处理中”。 nextRetryAtMS := nowMS + luckyGiftBackoff(nextRetryCount).Milliseconds() return s.repository.MarkLuckyGiftOutboxRetryable(ctx, event, nextRetryCount, nextRetryAtMS, cause.Error(), nowMS) } @@ -282,6 +295,7 @@ func (s *Service) GetConfig(ctx context.Context, poolID string) (domain.Config, return domain.Config{}, err } if !exists { + // 未配置时返回 disabled 草稿,后台可以直接编辑发布;不能在运行时把草稿当启用规则。 return DefaultConfig(appcode.FromContext(ctx), poolID), nil } return config, nil @@ -293,7 +307,9 @@ func (s *Service) UpsertConfig(ctx context.Context, config domain.Config) (domai } config.AppCode = appcode.FromContext(ctx) config.PoolID = normalizePoolID(config.PoolID) + // 规则表历史上用 gift_id 承载作用域;当前以 pool_id 为真实业务键,发布时必须覆盖掉外部传入的 gift_id。 config.GiftID = config.PoolID + // 后台现在配置倍率列表,服务端生成三档体验池候选;概率权重必须留给抽奖时的 RTP 控制器。 config.Tiers, config.MultiplierPPMs = normalizeTiers(config.Tiers, config.MultiplierPPMs, config.GiftPrice, config.HighMultiplier) if err := validateConfig(config); err != nil { return domain.Config{}, err @@ -423,6 +439,7 @@ func decodeDrawOutboxPayload(payloadJSON string) (luckyGiftDrawnPayload, error) if err := json.Unmarshal([]byte(payloadJSON), &payload); err != nil { return luckyGiftDrawnPayload{}, fmt.Errorf("invalid lucky gift outbox payload: %w", err) } + // 兼容早期 payload 缺省字段,避免历史 pending outbox 因新增字段永久失败。 if payload.EventType == "" { payload.EventType = "lucky_gift_drawn" } @@ -432,6 +449,7 @@ func decodeDrawOutboxPayload(payloadJSON string) (luckyGiftDrawnPayload, error) if payload.SenderUserID == 0 { payload.SenderUserID = payload.UserID } + // 这些字段是返奖、房间 IM 和幂等去重的最小闭环,缺任何一个都不能安全补偿。 if payload.DrawID == "" || payload.EventID == "" || payload.RoomID == "" || payload.GiftID == "" || payload.UserID <= 0 { return luckyGiftDrawnPayload{}, fmt.Errorf("lucky gift outbox payload is incomplete") } diff --git a/services/activity-service/internal/storage/mysql/lucky_gift_repository.go b/services/activity-service/internal/storage/mysql/lucky_gift_repository.go index b0958db6..2971ef5f 100644 --- a/services/activity-service/internal/storage/mysql/lucky_gift_repository.go +++ b/services/activity-service/internal/storage/mysql/lucky_gift_repository.go @@ -322,17 +322,20 @@ func (r *Repository) ExecuteLuckyGiftDraw(ctx context.Context, cmd domain.DrawCo if !exists || !config.Enabled { return domain.DrawResult{}, xerr.New(xerr.RuleNotActive, "lucky gift rule is not active") } + // 后台配置的 gift_price 只是参考价格;实际抽奖必须按钱包扣费后的 coin_spent 等比缩放奖档和风控上限。 config = luckyRuntimeConfig(config, cmd.CoinSpent) userPaidDraws, err := r.getLuckyUserStateForUpdate(ctx, tx, appCode, cmd.UserID, config.GiftID, nowMS) if err != nil { return domain.DrawResult{}, err } + // 体验池只按该 pool_id 下的用户累计付费抽数切换,不承诺单用户 RTP 或保底中奖。 nextPaidDraw := userPaidDraws + 1 experiencePool := luckyExperiencePool(nextPaidDraw, config) stageFeedback := luckyStageFeedback(nextPaidDraw, config) minFutureMax := luckyMinFutureMax(config) + // 全站窗口和礼物子窗口当前都按 pool_id 隔离;withCurrentWager 会把本次扣费先计入目标支出。 globalWindow, err := r.getOpenLuckyRTPWindow(ctx, tx, appCode, "pool", config.GiftID, config.GlobalWindowDraws, config.TargetRTPPPM, nowMS) if err != nil { return domain.DrawResult{}, err @@ -344,6 +347,7 @@ func (r *Repository) ExecuteLuckyGiftDraw(ctx context.Context, cmd domain.DrawCo } giftWindow = giftWindow.withCurrentWager(cmd.CoinSpent) + // 三层基础奖池共同承担 base_reward_coins,任一层容量不足都必须在随机前过滤对应奖档。 platformPool, err := r.getOrCreateLuckyPool(ctx, tx, appCode, "platform", config.GiftID, config.InitialPlatformPool, config.PlatformReserve, nowMS) if err != nil { return domain.DrawResult{}, err @@ -356,25 +360,30 @@ func (r *Repository) ExecuteLuckyGiftDraw(ctx context.Context, cmd domain.DrawCo if err != nil { return domain.DrawResult{}, err } + // 房间气氛池是额外体验预算,不进入基础 RTP;它独立于 Room Cell 状态。 atmosphere, err := r.getOrCreateLuckyAtmosphere(ctx, tx, appCode, luckyPoolScopedID(config.GiftID, cmd.RoomID), config.RoomAtmosphereInitial, config.RoomAtmosphereReserve, nowMS) if err != nil { return domain.DrawResult{}, err } + // 每笔扣费先按入池比例补充三层基础池,再判断本次能否支付,避免同一事务里先抽后补造成水位偏差。 if err := r.creditLuckyBasePools(ctx, tx, appCode, config, cmd.CoinSpent, &platformPool, &roomPool, &giftPool, nowMS); err != nil { return domain.DrawResult{}, err } if err := r.creditLuckyAtmosphere(ctx, tx, appCode, atmosphere.ScopeID, cmd.CoinSpent*config.RoomAtmosphereRatePPM/luckyPPMScale, &atmosphere, nowMS); err != nil { return domain.DrawResult{}, err } + // 活动预算和日预算按 UTC 天切分,只作为 effective reward 的额外补贴,不用来补基础 RTP 缺口。 activityRemaining, activityDay, err := r.getLuckyActivityRemaining(ctx, tx, appCode, config, cmd.PaidAtMS, nowMS) if err != nil { return domain.DrawResult{}, err } + // 风控计数同事务锁定,确保并发抽奖不会同时越过用户、设备、房间或主播上限。 counters, err := r.getLuckyRiskCounters(ctx, tx, appCode, config.GiftID, cmd, nowMS) if err != nil { return domain.DrawResult{}, err } + // 所有过滤都发生在随机前;随机后只能落事实和副作用 outbox,不能再改成另一个结果。 candidate, limited, err := r.selectLuckyCandidate(config, experiencePool, stageFeedback, globalWindow, giftWindow, platformPool, roomPool, giftPool, atmosphere, activityRemaining, counters, minFutureMax) if err != nil { return domain.DrawResult{}, err @@ -457,6 +466,7 @@ type luckyApplyInput struct { func (r *Repository) applyLuckyDraw(ctx context.Context, tx *sql.Tx, input luckyApplyInput) error { platformOut, roomOut, giftOut := luckySplitWeighted(input.Candidate.BaseReward, input.Config) + // 只有基础返奖写入 RTP 窗口;房间气氛和活动补贴不会污染平台成本口径。 if _, err := tx.ExecContext(ctx, ` UPDATE lucky_rtp_windows SET paid_draws = paid_draws + 1, @@ -488,6 +498,7 @@ func (r *Repository) applyLuckyDraw(ctx context.Context, tx *sql.Tx, input lucky if err := r.debitLuckyPool(ctx, tx, input.AppCode, "platform", input.Config.GiftID, platformOut, input.NowMS); err != nil { return err } + // 基础返奖按配置权重拆到 platform/room/gift 三层池,保证任一层不能被透支到 reserve 以下。 if err := r.debitLuckyPool(ctx, tx, input.AppCode, "room", input.RoomPool.ScopeID, roomOut, input.NowMS); err != nil { return err } @@ -495,6 +506,7 @@ func (r *Repository) applyLuckyDraw(ctx context.Context, tx *sql.Tx, input lucky return err } if input.Candidate.RoomReward > 0 { + // 气氛奖励只扣房间气氛池,不回写 Room Cell;客户端表现由后续 IM 事件驱动。 if _, err := tx.ExecContext(ctx, ` UPDATE lucky_room_atmosphere_pools SET balance = balance - ?, total_out = total_out + ?, updated_at_ms = ? @@ -505,6 +517,7 @@ func (r *Repository) applyLuckyDraw(ctx context.Context, tx *sql.Tx, input lucky } } if input.Candidate.ActivityReward > 0 { + // 活动补贴同时扣总预算和 UTC 日预算;任何一个不足都已经在候选过滤阶段被排除。 if _, err := tx.ExecContext(ctx, ` UPDATE lucky_activity_budgets SET spent_coins = spent_coins + ?, @@ -528,6 +541,7 @@ func (r *Repository) applyLuckyDraw(ctx context.Context, tx *sql.Tx, input lucky if err := r.updateLuckyRiskCounters(ctx, tx, input.AppCode, input.Config.GiftID, input.Command, input.Candidate.effectiveReward(), input.NowMS); err != nil { return err } + // 用户体验池推进以付费抽数为准,即使未中奖也要计数,避免用户重复停留在新手阶段。 if _, err := tx.ExecContext(ctx, ` UPDATE lucky_user_states SET paid_draws = paid_draws + 1, updated_at_ms = ? @@ -536,6 +550,7 @@ func (r *Repository) applyLuckyDraw(ctx context.Context, tx *sql.Tx, input lucky ); err != nil { return err } + // 当前审计快照先记录命中候选和过滤原因;后续若要完整复盘随机空间,需要扩展为完整候选表。 candidateSnapshot, _ := json.Marshal(map[string]any{ "base_reward": input.Candidate.BaseReward, "limited": input.Limited, @@ -573,6 +588,7 @@ func (r *Repository) applyLuckyDraw(ctx context.Context, tx *sql.Tx, input lucky return err } if luckyDrawNeedsOutbox(input.Candidate, input.StageFeedback) { + // outbox 是钱包入账和房间 IM 的唯一异步出口;事务内只写事实,不调用外部 RPC。 payload, _ := json.Marshal(map[string]any{ "event_type": "lucky_gift_drawn", "event_id": "lucky_gift_drawn:" + input.DrawID, @@ -1325,7 +1341,9 @@ func (r *Repository) updateLuckyRiskCounters(ctx context.Context, tx *sql.Tx, ap } func (r *Repository) selectLuckyCandidate(config domain.Config, pool string, stageFeedback bool, globalWindow, giftWindow luckyRTPWindow, platformPool, roomPool, giftPool, atmosphere luckyPool, activityRemaining int64, counters map[string]luckyCounter, minFutureMax int64) (luckyCandidate, map[string]bool, error) { + // minRequired 是为了保证窗口后续仍可追平 RTP 的本次最低基础返奖;全站和子窗口取更严格的一侧。 minRequired := maxInt64(globalWindow.minRequired(minFutureMax), giftWindow.minRequired(minFutureMax)) + // 三层池按责任权重反推“本次最多可支付的基础返奖总额”,不能只看某一个池的余额。 poolCapacity := luckyWeightedPoolCapacity(platformPool, roomPool, giftPool, config) riskCapacity := luckyRiskCapacity(config, counters) baseMaxAllowed := minInt64(poolCapacity, riskCapacity) @@ -1334,6 +1352,7 @@ func (r *Repository) selectLuckyCandidate(config domain.Config, pool string, sta baseCandidates := make([]luckyCandidate, 0, len(config.Tiers)) var totalWeight int64 for _, tier := range config.Tiers { + // 体验池只决定当前用户能看到哪些倍率点;真实权重稍后按 RTP 缺口自动生成。 if tier.Pool != pool || !tier.Enabled { continue } @@ -1342,6 +1361,7 @@ func (r *Repository) selectLuckyCandidate(config domain.Config, pool string, sta limited["large_tier_disabled"] = true continue } + // 低于 minRequired 的档位会让窗口后续无解,因此即使它是小奖也必须在随机前过滤。 if tier.RewardCoins < minRequired { continue } @@ -1354,6 +1374,7 @@ func (r *Repository) selectLuckyCandidate(config domain.Config, pool string, sta } continue } + // 高倍必须同时满足 RTP 高水位和三层池容量;不能靠随机后降档来兜底。 if highMultiplier && !luckyHasHighWater(tier.RewardCoins, globalWindow, giftWindow, poolCapacity, config) { limited["high_water"] = true continue @@ -1367,11 +1388,13 @@ func (r *Repository) selectLuckyCandidate(config domain.Config, pool string, sta HighMultiplier: highMultiplier, }) } + // 把可支付倍率点压缩成最多两个基础候选,使期望返奖贴近当前 RTP 缺口。 candidates := luckyAutoWeightedBaseCandidates(baseCandidates, minRequired, globalWindow, giftWindow) for _, candidate := range candidates { totalWeight += candidate.Weight } if minRequired == 0 { + // 只有基础 RTP 当前不强制追平时,娱乐补贴才可进入随机;补贴不能承担基础 RTP 结清责任。 if atmosphere.capacity() >= config.GiftPrice && riskCapacity >= config.GiftPrice { candidates = append(candidates, luckyCandidate{TierID: "room_shared_1x", Source: domain.SourceRoomAtmosphere, Pool: pool, Weight: 1200, RoomReward: config.GiftPrice}) totalWeight += 1200 @@ -1391,8 +1414,10 @@ func (r *Repository) selectLuckyCandidate(config domain.Config, pool string, sta } if len(candidates) == 0 { if minRequired > baseMaxAllowed { + // 窗口需要支付但奖池或风控容量不足,此时必须失败并暴露配置问题,不能透支或改结果。 return luckyCandidate{}, limited, xerr.New(xerr.Conflict, fmt.Sprintf("lucky gift RTP window cannot be settled: min_required=%d max_allowed=%d", minRequired, baseMaxAllowed)) } + // 没有离散倍率可用但容量足够时,用修正档精确补齐窗口缺口,保证长期 RTP 收敛。 return luckyCandidate{ TierID: luckyCorrectionTier(minRequired), Source: domain.SourceBaseRTP, @@ -1402,6 +1427,7 @@ func (r *Repository) selectLuckyCandidate(config domain.Config, pool string, sta HighMultiplier: minRequired >= config.GiftPrice*config.HighMultiplier, }, limited, nil } + // 使用 crypto/rand 生成随机落点;候选集合已经全部可支付,随机后不再做业务降级。 index, err := luckySecureIndex(totalWeight) if err != nil { return luckyCandidate{}, limited, err @@ -1460,6 +1486,7 @@ func luckyRuntimeConfig(config domain.Config, spent int64) domain.Config { if referenceCost <= 0 || spent <= 0 || referenceCost == spent { return config } + // 同一个 pool 可被不同价格礼物复用;按真实扣费缩放奖档和 cap,避免低价礼物借高价配置爆奖。 config.MaxSinglePayout = luckyScaleReward(config.MaxSinglePayout, referenceCost, spent) config.UserHourlyPayoutCap = luckyScaleReward(config.UserHourlyPayoutCap, referenceCost, spent) config.UserDailyPayoutCap = luckyScaleReward(config.UserDailyPayoutCap, referenceCost, spent) @@ -1487,10 +1514,12 @@ func luckyStageFeedback(paidDrawNumber int64, config domain.Config) bool { if limit <= 0 || paidDrawNumber <= 0 || paidDrawNumber > limit { return false } + // 阶段反馈是表现事件,不是金币保底;是否发币仍由候选预算和 outbox 决定。 return paidDrawNumber == 1 || paidDrawNumber == limit/4 || paidDrawNumber == limit/2 || paidDrawNumber == limit*3/4 || paidDrawNumber == limit } func luckyMinFutureMax(config domain.Config) int64 { + // 取三个体验池中“可用最大返奖”的最小值,作为未来每抽最保守的追平能力。 maxByPool := map[string]int64{ domain.PoolNovice: 0, domain.PoolIntermediate: 0, @@ -1523,6 +1552,7 @@ func luckyAutoWeightedBaseCandidates(candidates []luckyCandidate, minRequired in if len(candidates) == 0 { return nil } + // 同一返奖金额只保留一个候选,避免相同倍率点因三档配置重复而意外放大概率。 candidates = luckyDedupeRewardCandidates(candidates) desired := maxInt64(minRequired, luckyDesiredBaseReward(globalWindow, giftWindow)) if desired <= 0 { @@ -1533,10 +1563,12 @@ func luckyAutoWeightedBaseCandidates(candidates []luckyCandidate, minRequired in break } } + // 无基础缺口时优先选 0x;如果运营没配置 0x,就只能选最低可用倍率,这也是配置侧需要关注的风险。 zero.Weight = luckyPPMScale return []luckyCandidate{zero} } if desired <= candidates[0].BaseReward || len(candidates) == 1 { + // 缺口低于最低可用档时只能全权选择最低档,否则会留下无法支付的负缺口。 first := candidates[0] first.Weight = luckyPPMScale return []luckyCandidate{first} @@ -1561,10 +1593,12 @@ func luckyAutoWeightedBaseCandidates(candidates []luckyCandidate, minRequired in upper.Weight = luckyPPMScale return []luckyCandidate{upper} } + // 在相邻两个倍率之间线性插值,使本次基础返奖期望值尽量贴近 RTP 目标缺口。 lower.Weight = luckyPPMScale - upperWeight upper.Weight = upperWeight return []luckyCandidate{lower, upper} } + // 缺口超过所有离散倍率时选择最高可用档;真正无解会在上层 minRequired/baseMaxAllowed 检查处暴露。 last := candidates[len(candidates)-1] last.Weight = luckyPPMScale return []luckyCandidate{last} @@ -1609,6 +1643,7 @@ func luckySplitWeighted(amount int64, config domain.Config) (int64, int64, int64 func luckyWeightedPoolCapacity(platform, room, gift luckyPool, config domain.Config) int64 { capacity := int64(^uint64(0) >> 1) if config.PlatformPoolWeightPPM > 0 { + // 反推总返奖容量时必须除以责任权重;例如平台只承担 20%,平台可用 20 才能支撑总返奖 100。 capacity = minInt64(capacity, platform.capacity()*luckyPPMScale/config.PlatformPoolWeightPPM) } if config.RoomPoolWeightPPM > 0 { @@ -1624,6 +1659,7 @@ func luckyWeightedPoolCapacity(platform, room, gift luckyPool, config domain.Con } func luckyRiskCapacity(config domain.Config, counters map[string]luckyCounter) int64 { + // 风控是硬上限,所有会发币的来源都共享同一个 effective reward 剩余额度。 capacity := config.MaxSinglePayout capacity = minInt64(capacity, config.UserHourlyPayoutCap-counters[luckyCounterKey("user", "hour")].Payout) capacity = minInt64(capacity, config.UserDailyPayoutCap-counters[luckyCounterKey("user", "day")].Payout) @@ -1671,6 +1707,7 @@ func luckyMultiplierPPMsFromTiers(tiers []domain.Tier, giftPrice int64) []int64 } func luckyHasHighWater(reward int64, globalWindow, giftWindow luckyRTPWindow, poolCapacity int64, config domain.Config) bool { + // 高倍必须来自此前少付形成的 RTP headroom,并且三层池总容量要达到额外水位倍数。 if globalWindow.highWaterHeadroom(config.GiftPrice) < reward || giftWindow.highWaterHeadroom(config.GiftPrice) < reward { return false } diff --git a/services/gateway-service/internal/transport/http/roomapi/room_handler.go b/services/gateway-service/internal/transport/http/roomapi/room_handler.go index 54fb65cb..3261f925 100644 --- a/services/gateway-service/internal/transport/http/roomapi/room_handler.go +++ b/services/gateway-service/internal/transport/http/roomapi/room_handler.go @@ -23,6 +23,33 @@ const ( maxRoomFeedRelationScanCount = 500 ) +type flexibleInt64 int64 + +func (v *flexibleInt64) UnmarshalJSON(data []byte) error { + raw := strings.TrimSpace(string(data)) + if raw == "" || raw == "null" { + *v = 0 + return nil + } + if strings.HasPrefix(raw, `"`) && strings.HasSuffix(raw, `"`) { + var text string + if err := json.Unmarshal(data, &text); err != nil { + return err + } + raw = strings.TrimSpace(text) + } + if raw == "" { + *v = 0 + return nil + } + parsed, err := strconv.ParseInt(raw, 10, 64) + if err != nil { + return err + } + *v = flexibleInt64(parsed) + return nil +} + type roomFeedRelationCursor struct { Tab string `json:"tab"` Query string `json:"query,omitempty"` @@ -428,15 +455,22 @@ func (h *Handler) listRoomOnlineUsers(writer http.ResponseWriter, request *http. ViewerUserId: auth.UserIDFromContext(request.Context()), Page: page, PageSize: pageSize, + Sort: strings.TrimSpace(request.URL.Query().Get("sort")), }) if err != nil { httpkit.WriteRPCError(writer, request, err) return } - userIDs := make([]int64, 0, len(resp.GetUsers())) - for _, user := range resp.GetUsers() { + onlineItems := resp.GetItems() + userIDs := make([]int64, 0, len(onlineItems)+len(resp.GetUsers())) + for _, user := range onlineItems { userIDs = append(userIDs, user.GetUserId()) } + if len(onlineItems) == 0 { + for _, user := range resp.GetUsers() { + userIDs = append(userIDs, user.GetUserId()) + } + } httpkit.WriteOK(writer, request, roomOnlineUserDataFromProto(resp, h.roomDisplayProfileMap(request, userIDs))) } @@ -992,10 +1026,10 @@ func (h *Handler) setRoomPassword(writer http.ResponseWriter, request *http.Requ // setRoomAdmin 把管理员增删 HTTP JSON 请求转换为 room-service SetRoomAdmin 命令。 func (h *Handler) setRoomAdmin(writer http.ResponseWriter, request *http.Request) { var body struct { - RoomID string `json:"room_id"` - CommandID string `json:"command_id"` - TargetUserID int64 `json:"target_user_id"` - Enabled bool `json:"enabled"` + RoomID string `json:"room_id"` + CommandID string `json:"command_id"` + TargetUserID flexibleInt64 `json:"target_user_id"` + Enabled bool `json:"enabled"` } if !httpkit.Decode(writer, request, &body) { @@ -1004,10 +1038,23 @@ func (h *Handler) setRoomAdmin(writer http.ResponseWriter, request *http.Request resp, err := h.roomClient.SetRoomAdmin(request.Context(), &roomv1.SetRoomAdminRequest{ Meta: httpkit.RoomMeta(request, body.RoomID, body.CommandID), - TargetUserId: body.TargetUserID, + TargetUserId: int64(body.TargetUserID), Enabled: body.Enabled, }) - httpkit.Write(writer, request, resp, err) + if err != nil { + httpkit.WriteRPCError(writer, request, err) + return + } + roomRole := "normal" + if body.Enabled { + roomRole = "admin" + } + httpkit.WriteOK(writer, request, setRoomAdminData{ + Result: commandResultDataFromProto(resp.GetResult()), + TargetUserID: formatOptionalUserID(int64(body.TargetUserID)), + RoomRole: roomRole, + ServerTimeMS: resp.GetResult().GetServerTimeMs(), + }) } // muteUser 把禁言 HTTP JSON 请求转换为 room-service MuteUser 命令。 diff --git a/services/gateway-service/internal/transport/http/roomapi/room_view.go b/services/gateway-service/internal/transport/http/roomapi/room_view.go index 3836dee7..5d136d4f 100644 --- a/services/gateway-service/internal/transport/http/roomapi/room_view.go +++ b/services/gateway-service/internal/transport/http/roomapi/room_view.go @@ -76,10 +76,21 @@ type roomOnlineUsersData struct { type roomOnlineUserData struct { UserID string `json:"user_id"` Role string `json:"role,omitempty"` + IsOwner bool `json:"is_owner"` + RoomRole string `json:"room_role,omitempty"` + GiftValue int64 `json:"gift_value"` + JoinedAtMS int64 `json:"joined_at_ms,omitempty"` LastSeenAtMS int64 `json:"last_seen_at_ms,omitempty"` Profile *roomDisplayProfileData `json:"profile,omitempty"` } +type setRoomAdminData struct { + Result roomCommandResultData `json:"result"` + TargetUserID string `json:"target_user_id"` + RoomRole string `json:"room_role"` + ServerTimeMS int64 `json:"server_time_ms"` +} + type roomPermissionsData struct { IsOwner bool `json:"is_owner"` IsAdmin bool `json:"is_admin"` @@ -567,7 +578,7 @@ func roomPermissionsFromSnapshot(snapshot *roomv1.RoomSnapshot, viewerUserID int return roomPermissionsData{} } isOwner := snapshot.GetOwnerUserId() == viewerUserID - isAdmin := containsInt64(snapshot.GetAdminUserIds(), viewerUserID) + isAdmin := !isOwner && containsInt64(snapshot.GetAdminUserIds(), viewerUserID) isMuted := containsInt64(snapshot.GetMuteUserIds(), viewerUserID) inRoom := protoRoomUserByID(snapshot, viewerUserID) != nil return roomPermissionsData{ @@ -632,11 +643,19 @@ func roomOnlineUserDataFromProto(resp *roomv1.ListRoomOnlineUsersResponse, profi if resp == nil { return roomOnlineUsersData{} } - items := make([]roomOnlineUserData, 0, len(resp.GetUsers())) - for _, user := range resp.GetUsers() { + source := resp.GetItems() + if len(source) == 0 { + source = roomOnlineUsersFromLegacyUsers(resp.GetUsers()) + } + items := make([]roomOnlineUserData, 0, len(source)) + for _, user := range source { item := roomOnlineUserData{ UserID: formatOptionalUserID(user.GetUserId()), Role: user.GetRole(), + IsOwner: user.GetIsOwner(), + RoomRole: user.GetRoomRole(), + GiftValue: user.GetGiftValue(), + JoinedAtMS: user.GetJoinedAtMs(), LastSeenAtMS: user.GetLastSeenAtMs(), } if profile, ok := profiles[user.GetUserId()]; ok { @@ -654,6 +673,20 @@ func roomOnlineUserDataFromProto(resp *roomv1.ListRoomOnlineUsersResponse, profi } } +func roomOnlineUsersFromLegacyUsers(users []*roomv1.RoomUser) []*roomv1.RoomOnlineUser { + items := make([]*roomv1.RoomOnlineUser, 0, len(users)) + for _, user := range users { + items = append(items, &roomv1.RoomOnlineUser{ + UserId: user.GetUserId(), + Role: user.GetRole(), + JoinedAtMs: user.GetJoinedAtMs(), + LastSeenAtMs: user.GetLastSeenAtMs(), + RoomRole: "normal", + }) + } + return items +} + func protoRoomUserByID(snapshot *roomv1.RoomSnapshot, userID int64) *roomv1.RoomUser { if snapshot == nil || userID <= 0 { return nil diff --git a/services/room-service/deploy/mysql/initdb/001_room_service.sql b/services/room-service/deploy/mysql/initdb/001_room_service.sql index f8716051..53912453 100644 --- a/services/room-service/deploy/mysql/initdb/001_room_service.sql +++ b/services/room-service/deploy/mysql/initdb/001_room_service.sql @@ -68,6 +68,7 @@ CREATE TABLE IF NOT EXISTS room_user_presence ( mic_session_id VARCHAR(128) NOT NULL DEFAULT '' COMMENT '麦位会话 ID', room_version BIGINT NOT NULL COMMENT 'Room Cell 状态版本', status VARCHAR(32) NOT NULL COMMENT '业务状态', + joined_at_ms BIGINT NOT NULL DEFAULT 0 COMMENT '进房时间,UTC epoch ms', last_seen_at_ms BIGINT NOT NULL DEFAULT 0 COMMENT '最后活跃时间,UTC epoch ms', updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms', PRIMARY KEY (app_code, user_id), @@ -75,6 +76,18 @@ CREATE TABLE IF NOT EXISTS room_user_presence ( KEY idx_room_user_presence_updated (app_code, status, updated_at_ms) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='房间用户在线状态表'; +CREATE TABLE IF NOT EXISTS room_user_gift_stats ( + app_code VARCHAR(32) NOT NULL COMMENT '应用编码,用于多租户隔离', + room_id VARCHAR(64) NOT NULL COMMENT '房间 ID', + user_id BIGINT NOT NULL COMMENT '送礼用户 ID', + gift_value BIGINT NOT NULL DEFAULT 0 COMMENT '当前房间生命周期内累计送礼价值', + last_gift_at_ms BIGINT NOT NULL DEFAULT 0 COMMENT '最近一次送礼时间,UTC epoch ms', + created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms', + updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms', + PRIMARY KEY (app_code, room_id, user_id), + KEY idx_room_user_gift_stats_room_value (app_code, room_id, gift_value DESC, last_gift_at_ms DESC, user_id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='房间用户送礼价值统计表'; + CREATE TABLE IF NOT EXISTS room_snapshots ( app_code VARCHAR(32) NOT NULL COMMENT '应用编码,用于多租户隔离', room_id VARCHAR(64) NOT NULL COMMENT '房间 ID', diff --git a/services/room-service/internal/room/service/current_room.go b/services/room-service/internal/room/service/current_room.go index 74aa54c1..e16082bb 100644 --- a/services/room-service/internal/room/service/current_room.go +++ b/services/room-service/internal/room/service/current_room.go @@ -138,6 +138,7 @@ func roomPresenceProjectionFromSnapshot(snapshot *roomv1.RoomSnapshot, updatedAt Role: user.GetRole(), RoomVersion: snapshot.GetVersion(), Status: roomPresenceStatusActive, + JoinedAtMS: user.GetJoinedAtMs(), LastSeenAtMS: user.GetLastSeenAtMs(), UpdatedAtMS: updatedAtMS, } diff --git a/services/room-service/internal/room/service/gift.go b/services/room-service/internal/room/service/gift.go index e1e626ea..eb4135ff 100644 --- a/services/room-service/internal/room/service/gift.go +++ b/services/room-service/internal/room/service/gift.go @@ -69,6 +69,7 @@ func (s *Service) SendGift(ctx context.Context, req *roomv1.SendGiftRequest) (*r } luckyEnabled := false if cmd.PoolID != "" { + // 客户端显式传 pool_id 时必须先检查活动规则;规则未启用就拒绝,避免先扣费再发现不能抽奖。 if s.luckyGift == nil { return mutationResult{}, nil, xerr.New(xerr.Unavailable, "lucky gift service is not configured") } @@ -85,6 +86,7 @@ func (s *Service) SendGift(ctx context.Context, req *roomv1.SendGiftRequest) (*r if checkResp == nil || !checkResp.GetEnabled() { return mutationResult{}, nil, xerr.New(xerr.RuleNotActive, "lucky gift rule is not active") } + // Check 只说明“允许按该 pool 抽奖”,真实价格、区域可用性和扣费仍以 wallet-service 为准。 luckyEnabled = true } treasureConfig, err := s.roomTreasureConfig(ctx) @@ -120,33 +122,39 @@ func (s *Service) SendGift(ctx context.Context, req *roomv1.SendGiftRequest) (*r settledCommand.PriceVersion = billing.GetPriceVersion() settledCommand.GiftTypeCode = billing.GetGiftTypeCode() if !luckyEnabled { + // 未显式传 pool_id 的旧客户端仍可通过钱包礼物类型触发默认幸运礼物逻辑;新客户端应传明确 pool_id。 luckyEnabled = s.shouldDrawLuckyGift(cmd.PoolID, billing.GetGiftTypeCode()) } var luckyGift *roomv1.LuckyGiftDrawResult if luckyEnabled { + // 抽奖必须发生在扣费成功之后;activity-service 只接收钱包结算后的 coin_spent,不信任客户端价格。 drawResp, err := s.luckyGift.ExecuteLuckyGiftDraw(ctx, &activityv1.ExecuteLuckyGiftDrawRequest{ LuckyGift: &activityv1.LuckyGiftMeta{ - Meta: activityMetaFromRoom(ctx, req.GetMeta()), - CommandId: cmd.ID(), - UserId: cmd.ActorUserID(), - TargetUserId: cmd.TargetUserID, - DeviceId: luckyGiftDeviceID(cmd), - RoomId: cmd.RoomID(), - AnchorId: luckyGiftAnchorID(roomMeta), - GiftId: cmd.GiftID, - GiftCount: cmd.GiftCount, - CoinSpent: billing.GetCoinSpent(), + Meta: activityMetaFromRoom(ctx, req.GetMeta()), + CommandId: cmd.ID(), + UserId: cmd.ActorUserID(), + TargetUserId: cmd.TargetUserID, + // 目前没有独立设备 ID 字段,优先用 session_id;没有时退回 command_id,保证风控 scope 不为空。 + DeviceId: luckyGiftDeviceID(cmd), + RoomId: cmd.RoomID(), + AnchorId: luckyGiftAnchorID(roomMeta), + GiftId: cmd.GiftID, + GiftCount: cmd.GiftCount, + CoinSpent: billing.GetCoinSpent(), + // 房间链路统一使用 UTC epoch ms;不能把本地时区时间传给活动风控窗口。 PaidAtMs: now.UTC().UnixMilli(), PoolId: cmd.PoolID, VisibleRegionId: roomMeta.VisibleRegionID, }, }) if err != nil { + // 抽奖失败时整笔送礼失败并依赖 wallet 幂等重试;不能落普通礼物后丢失幸运礼物抽奖事实。 return mutationResult{}, nil, err } if drawResp == nil { return mutationResult{}, nil, xerr.New(xerr.Unavailable, "lucky gift draw response is empty") } + // 同步返回只给当前送礼用户做即时表现;返奖到账和房间开奖结果仍由 activity outbox 补偿。 luckyGift = luckyGiftResultFromProto(drawResp.GetResult()) } @@ -227,6 +235,15 @@ func (s *Service) SendGift(ctx context.Context, req *roomv1.SendGiftRequest) (*r CoinSpent: roomGiftLeaderboardCoinSpent(billing), OccurredAtMS: now.UnixMilli(), }, + roomUserGiftStats: []RoomUserGiftStatIncrement{ + { + AppCode: appcode.FromContext(ctx), + RoomID: current.RoomID, + UserID: cmd.ActorUserID(), + GiftValue: heatValue, + LastGiftAtMS: now.UnixMilli(), + }, + }, syncEvent: &tencentim.RoomEvent{ // 同步广播只选 GiftSent 主事件作为客户端房间系统消息,Heat/Rank 通过字段携带。 EventID: giftEvent.EventID, diff --git a/services/room-service/internal/room/service/management.go b/services/room-service/internal/room/service/management.go index 4ca6a948..6636c46c 100644 --- a/services/room-service/internal/room/service/management.go +++ b/services/room-service/internal/room/service/management.go @@ -168,9 +168,9 @@ func (s *Service) SetRoomAdmin(ctx context.Context, req *roomv1.SetRoomAdminRequ // 首版不支持预设管理员,避免把不存在或已离房用户加入管理集合。 return mutationResult{}, nil, xerr.New(xerr.NotFound, "target not in room") } - if !cmd.Enabled && cmd.TargetUserID == current.OwnerUserID { - // owner 的管理身份由专门字段决定,不能通过 admin 集合移除。 - return mutationResult{}, nil, xerr.New(xerr.PermissionDenied, "permission denied") + if cmd.TargetUserID == current.OwnerUserID { + // owner 不是管理员,不能对 owner 执行设置或取消管理员。 + return mutationResult{}, nil, xerr.New(xerr.Conflict, "owner cannot be room admin target") } if current.AdminUsers[cmd.TargetUserID] == cmd.Enabled { // 重复添加或重复移除是 no-op,避免重复发送 RoomAdminChanged。 diff --git a/services/room-service/internal/room/service/online_users_query.go b/services/room-service/internal/room/service/online_users_query.go index b487c709..1559a60d 100644 --- a/services/room-service/internal/room/service/online_users_query.go +++ b/services/room-service/internal/room/service/online_users_query.go @@ -2,6 +2,7 @@ package service import ( "context" + "strings" roomv1 "hyapp.local/api/proto/room/v1" "hyapp/pkg/appcode" @@ -55,29 +56,64 @@ func (s *Service) ListRoomOnlineUsers(ctx context.Context, req *roomv1.ListRoomO // 单页上限在 service 层压实,repository 不接受无限制分页。 pageSize = maxRoomOnlineUsersPageSize } + sortMode := strings.TrimSpace(req.GetSort()) + if sortMode != "" && sortMode != "online" && sortMode != "gift_value" { + return nil, xerr.New(xerr.InvalidArgument, "sort is invalid") + } + + snapshot, err := s.currentSnapshot(ctx, roomID) + if err != nil { + return nil, err + } + if snapshot == nil { + return nil, xerr.New(xerr.NotFound, "room not found") + } + adminUsers := make(map[int64]bool, len(snapshot.GetAdminUserIds())) + for _, userID := range snapshot.GetAdminUserIds() { + if userID > 0 && userID != snapshot.GetOwnerUserId() { + adminUsers[userID] = true + } + } result, err := s.repository.ListRoomOnlineUsers(ctx, RoomOnlineUserQuery{ AppCode: appcode.FromContext(ctx), RoomID: roomID, Page: page, PageSize: pageSize, + Sort: sortMode, }) if err != nil { return nil, err } users := make([]*roomv1.RoomUser, 0, len(result.Users)) + items := make([]*roomv1.RoomOnlineUser, 0, len(result.Users)) for _, user := range result.Users { // 在线列表只返回轻量房间用户态,完整用户资料仍由 user-service 或 gateway 聚合。 users = append(users, &roomv1.RoomUser{ UserId: user.UserID, Role: user.Role, + JoinedAtMs: user.JoinedAtMS, LastSeenAtMs: user.LastSeenAtMS, }) + roomRole := "normal" + if adminUsers[user.UserID] { + roomRole = "admin" + } + items = append(items, &roomv1.RoomOnlineUser{ + UserId: user.UserID, + Role: user.Role, + RoomRole: roomRole, + GiftValue: user.GiftValue, + JoinedAtMs: user.JoinedAtMS, + LastSeenAtMs: user.LastSeenAtMS, + IsOwner: user.UserID == snapshot.GetOwnerUserId(), + }) } return &roomv1.ListRoomOnlineUsersResponse{ Users: users, + Items: items, Total: result.Total, Page: int32(result.Page), PageSize: int32(result.PageSize), diff --git a/services/room-service/internal/room/service/online_users_test.go b/services/room-service/internal/room/service/online_users_test.go new file mode 100644 index 00000000..f06167eb --- /dev/null +++ b/services/room-service/internal/room/service/online_users_test.go @@ -0,0 +1,95 @@ +package service_test + +import ( + "context" + "encoding/json" + "testing" + "time" + + roomv1 "hyapp.local/api/proto/room/v1" + walletv1 "hyapp.local/api/proto/wallet/v1" + "hyapp/pkg/appcode" + roomservice "hyapp/services/room-service/internal/room/service" + "hyapp/services/room-service/internal/testutil/mysqltest" +) + +func TestListRoomOnlineUsersReturnsOwnerAdminRoleAndGiftValue(t *testing.T) { + ctx := context.Background() + repository := mysqltest.NewRepository(t) + wallet := &treasureTestWallet{debits: []*walletv1.DebitGiftResponse{{ + BillingReceiptId: "receipt-online-users", + CoinSpent: 321, + GiftPointAdded: 321, + HeatValue: 321, + GiftTypeCode: "normal", + }}} + now := &fixedRoomTreasureClock{now: time.Date(2026, 5, 27, 12, 0, 0, 0, time.UTC)} + svc := newTreasureTestService(t, repository, wallet, now) + + roomID := "room-online-users" + ownerID := int64(1001) + adminID := int64(1002) + normalID := int64(1003) + createTreasureRoom(t, ctx, svc, roomID, ownerID, 9001) + joinTreasureRoom(t, ctx, svc, roomID, adminID) + joinTreasureRoom(t, ctx, svc, roomID, normalID) + + if _, err := svc.SetRoomAdmin(ctx, &roomv1.SetRoomAdminRequest{ + Meta: roomservice.NewRequestMeta(roomID, ownerID), + TargetUserId: adminID, + Enabled: true, + }); err != nil { + t.Fatalf("set admin failed: %v", err) + } + if _, err := svc.SetRoomAdmin(ctx, &roomv1.SetRoomAdminRequest{ + Meta: roomservice.NewRequestMeta(roomID, ownerID), + TargetUserId: ownerID, + Enabled: true, + }); err == nil { + t.Fatalf("setting owner as admin must fail") + } + + if _, err := svc.SendGift(ctx, &roomv1.SendGiftRequest{ + Meta: treasureMeta(roomID, adminID, "online-users-gift"), + TargetType: "user", + TargetUserId: ownerID, + GiftId: "gift-online-users", + GiftCount: 1, + }); err != nil { + t.Fatalf("send gift failed: %v", err) + } + + resp, err := svc.ListRoomOnlineUsers(ctx, &roomv1.ListRoomOnlineUsersRequest{ + Meta: &roomv1.RequestMeta{AppCode: appcode.Default, RoomId: roomID}, + RoomId: roomID, + ViewerUserId: ownerID, + Page: 1, + PageSize: 10, + Sort: "gift_value", + }) + if err != nil { + t.Fatalf("list online users failed: %v", err) + } + if len(resp.GetItems()) != 3 { + t.Fatalf("expected three online users, got %+v", resp.GetItems()) + } + + byID := make(map[int64]*roomv1.RoomOnlineUser, len(resp.GetItems())) + for _, item := range resp.GetItems() { + byID[item.GetUserId()] = item + } + if owner := byID[ownerID]; owner == nil || !owner.GetIsOwner() || owner.GetRoomRole() != "normal" { + t.Fatalf("owner must be is_owner=true and room_role=normal: %+v", owner) + } + if admin := byID[adminID]; admin == nil || admin.GetIsOwner() || admin.GetRoomRole() != "admin" || admin.GetGiftValue() != 321 { + t.Fatalf("admin gift value or role mismatch: %+v", admin) + } + if normal := byID[normalID]; normal == nil || normal.GetIsOwner() || normal.GetRoomRole() != "normal" || normal.GetGiftValue() != 0 { + t.Fatalf("normal user mismatch: %+v", normal) + } + payload, err := json.Marshal(resp.GetItems()) + if err != nil { + t.Fatalf("marshal online users failed: %v", err) + } + t.Logf("online users response items: %s", payload) +} diff --git a/services/room-service/internal/room/service/pipeline.go b/services/room-service/internal/room/service/pipeline.go index 6113ad06..1ddf6b45 100644 --- a/services/room-service/internal/room/service/pipeline.go +++ b/services/room-service/internal/room/service/pipeline.go @@ -91,13 +91,14 @@ func (s *Service) mutateRoom(ctx context.Context, cmd command.Command, replayabl LeaseToken: lease.LeaseToken, CreatedAtMS: now.UnixMilli(), }, - OutboxRecords: outboxRecords, - RoomStatus: result.roomStatus, - RoomSeatCount: result.roomSeatCount, - RoomPasswordHash: result.roomPasswordHash, - RoomMode: result.roomMode, - VisibleRegionID: result.visibleRegionID, - CloseInfo: result.closeInfo, + OutboxRecords: outboxRecords, + RoomStatus: result.roomStatus, + RoomSeatCount: result.roomSeatCount, + RoomPasswordHash: result.roomPasswordHash, + RoomMode: result.roomMode, + VisibleRegionID: result.visibleRegionID, + CloseInfo: result.closeInfo, + RoomUserGiftStats: result.roomUserGiftStats, }); err != nil { return nil, err } diff --git a/services/room-service/internal/room/service/recovery.go b/services/room-service/internal/room/service/recovery.go index 39a70958..02f8ad99 100644 --- a/services/room-service/internal/room/service/recovery.go +++ b/services/room-service/internal/room/service/recovery.go @@ -309,11 +309,12 @@ func replay(current *state.RoomState, cmd command.Command) error { } current.Version++ case *command.SetRoomAdmin: - if typed.Enabled { + if typed.TargetUserID == current.OwnerUserID { + // owner 不是管理员;旧命令或异常数据回放时不把 owner 写入 admin 集合。 + } else if typed.Enabled { // 管理员集合是持久权限,恢复后用户重新进房即可继续具备 admin 身份。 current.AdminUsers[typed.TargetUserID] = true - } else if typed.TargetUserID != current.OwnerUserID { - // owner 的管理身份不能被 admin 删除命令移除。 + } else { delete(current.AdminUsers, typed.TargetUserID) } current.Version++ diff --git a/services/room-service/internal/room/service/repository.go b/services/room-service/internal/room/service/repository.go index 2850cf3d..b9981602 100644 --- a/services/room-service/internal/room/service/repository.go +++ b/services/room-service/internal/room/service/repository.go @@ -71,6 +71,17 @@ type MutationCommit struct { VisibleRegionID *int64 // CloseInfo 非 nil 时同步更新后台生命周期审计字段。 CloseInfo *RoomCloseInfo + // RoomUserGiftStats 是本次命令产生的房间用户送礼价值增量,必须和命令日志同事务提交。 + RoomUserGiftStats []RoomUserGiftStatIncrement +} + +// RoomUserGiftStatIncrement 表达当前房间内某个送礼用户的礼物价值增量。 +type RoomUserGiftStatIncrement struct { + AppCode string + RoomID string + UserID int64 + GiftValue int64 + LastGiftAtMS int64 } type RoomCloseInfo struct { @@ -293,10 +304,14 @@ type RoomPresence struct { RoomVersion int64 // Status 表示该用户 presence 是否仍 active。 Status string + // JoinedAtMS 是用户进入当前房间的时间。 + JoinedAtMS int64 // LastSeenAtMS 是用户在 Room Cell 内的最后业务心跳时间。 LastSeenAtMS int64 // UpdatedAtMS 是投影写入时间。 UpdatedAtMS int64 + // GiftValue 是该用户在当前房间生命周期内作为送礼方累计送出的礼物价值。 + GiftValue int64 } // RoomOnlineUserQuery 是房间在线用户分页读模型查询条件。 @@ -305,6 +320,7 @@ type RoomOnlineUserQuery struct { RoomID string Page int PageSize int + Sort string } // RoomOnlineUserPage 是 room_user_presence 中某房间 active 用户的一页结果。 diff --git a/services/room-service/internal/room/service/service.go b/services/room-service/internal/room/service/service.go index 1f5e7c82..ebc979f2 100644 --- a/services/room-service/internal/room/service/service.go +++ b/services/room-service/internal/room/service/service.go @@ -124,6 +124,8 @@ type mutationResult struct { walletDebitMS int64 // roomGiftLeaderboard 是 SendGift 成功后写入跨房间金币榜的轻量增量。 roomGiftLeaderboard *RoomGiftLeaderboardIncrement + // roomUserGiftStats 是当前房间用户送礼价值统计,和命令日志同事务提交。 + roomUserGiftStats []RoomUserGiftStatIncrement } // New 初始化 room-service 领域服务。 diff --git a/services/room-service/internal/room/state/state.go b/services/room-service/internal/room/state/state.go index 44fc9c2f..b41ea3c1 100644 --- a/services/room-service/internal/room/state/state.go +++ b/services/room-service/internal/room/state/state.go @@ -171,7 +171,7 @@ func (t *TreasureState) Clone() *TreasureState { type RoomState struct { // RoomID 是该状态所属房间。 RoomID string - // OwnerUserID 是房间所有者,进入 AdminUsers。 + // OwnerUserID 是房间所有者;owner 权限由专门字段派生,不写入 AdminUsers。 OwnerUserID int64 // Mode 是房间模式,当前只作为状态字段透出。 Mode string @@ -222,13 +222,10 @@ func NewRoomState(roomID string, ownerUserID int64, seatCount int32, mode string ChatEnabled: true, MicSeats: seats, OnlineUsers: make(map[int64]*RoomUserState), - AdminUsers: map[int64]bool{ - // owner 默认具备管理权限,后续可以扩展管理员命令。 - ownerUserID: true, - }, - BanUsers: make(map[int64]bool), - MuteUsers: make(map[int64]bool), - RoomExt: make(map[string]string), + AdminUsers: make(map[int64]bool), + BanUsers: make(map[int64]bool), + MuteUsers: make(map[int64]bool), + RoomExt: make(map[string]string), } } @@ -389,7 +386,7 @@ func (s *RoomState) ToProto() *roomv1.RoomSnapshot { ChatEnabled: s.ChatEnabled, MicSeats: seats, OnlineUsers: users, - AdminUserIds: sortedSetIDs(s.AdminUsers), + AdminUserIds: sortedSetIDsExcept(s.AdminUsers, s.OwnerUserID), BanUserIds: sortedSetIDs(s.BanUsers), MuteUserIds: sortedSetIDs(s.MuteUsers), GiftRank: rankItems, @@ -456,10 +453,12 @@ func FromProto(snapshot *roomv1.RoomSnapshot) *RoomState { for _, userID := range snapshot.GetAdminUserIds() { // set 在 protobuf 中用数组表达,恢复时重新转为 map。 + if userID == restored.OwnerUserID { + // owner 不是管理员;旧快照里如果残留 owner,恢复时过滤掉。 + continue + } restored.AdminUsers[userID] = true } - // owner 是派生管理员身份,快照恢复后必须重新压实该不变量。 - restored.AdminUsers[restored.OwnerUserID] = true for _, userID := range snapshot.GetBanUserIds() { restored.BanUsers[userID] = true @@ -496,6 +495,17 @@ func sortedSetIDs(values map[int64]bool) []int64 { return ids } +func sortedSetIDsExcept(values map[int64]bool, excluded int64) []int64 { + ids := make([]int64, 0, len(values)) + for userID, enabled := range values { + if enabled && userID != excluded { + ids = append(ids, userID) + } + } + slices.Sort(ids) + return ids +} + func cloneStringMap(input map[string]string) map[string]string { if input == nil { // protobuf map 缺省按空 map 处理,调用方可以安全写入。 diff --git a/services/room-service/internal/storage/mysql/repository.go b/services/room-service/internal/storage/mysql/repository.go index 5086a317..c290f8d5 100644 --- a/services/room-service/internal/storage/mysql/repository.go +++ b/services/room-service/internal/storage/mysql/repository.go @@ -129,12 +129,24 @@ func (r *Repository) Migrate(ctx context.Context) error { mic_session_id VARCHAR(128) NOT NULL DEFAULT '', room_version BIGINT NOT NULL, status VARCHAR(32) NOT NULL, + joined_at_ms BIGINT NOT NULL DEFAULT 0, last_seen_at_ms BIGINT NOT NULL DEFAULT 0, updated_at_ms BIGINT NOT NULL, PRIMARY KEY (app_code, user_id), KEY idx_room_user_presence_room (app_code, room_id, status), KEY idx_room_user_presence_updated (app_code, status, updated_at_ms) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci`, + `CREATE TABLE IF NOT EXISTS room_user_gift_stats ( + app_code VARCHAR(32) NOT NULL, + room_id VARCHAR(64) NOT NULL, + user_id BIGINT NOT NULL, + gift_value BIGINT NOT NULL DEFAULT 0, + last_gift_at_ms BIGINT NOT NULL DEFAULT 0, + created_at_ms BIGINT NOT NULL, + updated_at_ms BIGINT NOT NULL, + PRIMARY KEY (app_code, room_id, user_id), + KEY idx_room_user_gift_stats_room_value (app_code, room_id, gift_value DESC, last_gift_at_ms DESC, user_id) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci`, `CREATE TABLE IF NOT EXISTS room_snapshots ( app_code VARCHAR(32) NOT NULL, room_id VARCHAR(64) NOT NULL, @@ -314,10 +326,26 @@ func (r *Repository) Migrate(ctx context.Context) error { if err := r.ensureRoomAdminCloseSchema(ctx); err != nil { return err } + if err := r.ensureRoomUserPresenceSchema(ctx); err != nil { + return err + } return r.ensureOutboxRetrySchema(ctx) } +func (r *Repository) ensureRoomUserPresenceSchema(ctx context.Context) error { + hasColumn, err := r.columnExists(ctx, "room_user_presence", "joined_at_ms") + if err != nil { + return err + } + if !hasColumn { + if _, err := r.db.ExecContext(ctx, `ALTER TABLE room_user_presence ADD COLUMN joined_at_ms BIGINT NOT NULL DEFAULT 0 AFTER status`); err != nil { + return err + } + } + return nil +} + func (r *Repository) ensureRoomPasswordSchema(ctx context.Context) error { hasColumn, err := r.columnExists(ctx, "rooms", "room_password_hash") if err != nil { @@ -712,6 +740,35 @@ func (r *Repository) SaveMutation(ctx context.Context, commit roomservice.Mutati } } } + for _, stat := range commit.RoomUserGiftStats { + if stat.UserID <= 0 || stat.GiftValue <= 0 { + continue + } + statAppCode := normalizedRecordAppCode(ctx, stat.AppCode) + statRoomID := strings.TrimSpace(stat.RoomID) + if statRoomID == "" { + statRoomID = commit.Command.RoomID + } + if _, err := tx.ExecContext(ctx, + `INSERT INTO room_user_gift_stats ( + app_code, room_id, user_id, gift_value, last_gift_at_ms, created_at_ms, updated_at_ms + ) VALUES (?, ?, ?, ?, ?, ?, ?) + ON DUPLICATE KEY UPDATE + gift_value = gift_value + VALUES(gift_value), + last_gift_at_ms = VALUES(last_gift_at_ms), + updated_at_ms = VALUES(updated_at_ms)`, + statAppCode, + statRoomID, + stat.UserID, + stat.GiftValue, + stat.LastGiftAtMS, + commit.Command.CreatedAtMS, + commit.Command.CreatedAtMS, + ); err != nil { + _ = tx.Rollback() + return err + } + } return tx.Commit() } @@ -2121,8 +2178,8 @@ func (r *Repository) ProjectRoomPresence(ctx context.Context, snapshot roomservi } if _, err := tx.ExecContext(ctx, `INSERT INTO room_user_presence ( - app_code, user_id, room_id, role, publish_state, mic_session_id, room_version, status, last_seen_at_ms, updated_at_ms - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + app_code, user_id, room_id, role, publish_state, mic_session_id, room_version, status, joined_at_ms, last_seen_at_ms, updated_at_ms + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE room_id = VALUES(room_id), role = VALUES(role), @@ -2130,6 +2187,7 @@ func (r *Repository) ProjectRoomPresence(ctx context.Context, snapshot roomservi mic_session_id = VALUES(mic_session_id), room_version = VALUES(room_version), status = VALUES(status), + joined_at_ms = VALUES(joined_at_ms), last_seen_at_ms = VALUES(last_seen_at_ms), updated_at_ms = VALUES(updated_at_ms)`, appCode, @@ -2140,6 +2198,7 @@ func (r *Repository) ProjectRoomPresence(ctx context.Context, snapshot roomservi user.MicSessionID, snapshot.RoomVersion, roomPresenceStatusActiveSQL, + user.JoinedAtMS, user.LastSeenAtMS, snapshot.UpdatedAtMS, ); err != nil { @@ -2154,7 +2213,7 @@ func (r *Repository) ProjectRoomPresence(ctx context.Context, snapshot roomservi // GetCurrentRoomPresence 查询用户当前 active 房间,不扫描发现页列表。 func (r *Repository) GetCurrentRoomPresence(ctx context.Context, userID int64) (roomservice.RoomPresence, bool, error) { row := r.db.QueryRowContext(ctx, - `SELECT app_code, user_id, room_id, role, publish_state, mic_session_id, room_version, status, last_seen_at_ms, updated_at_ms + `SELECT app_code, user_id, room_id, role, publish_state, mic_session_id, room_version, status, joined_at_ms, last_seen_at_ms, updated_at_ms FROM room_user_presence WHERE app_code = ? AND user_id = ? AND status = ? LIMIT 1`, @@ -2173,6 +2232,7 @@ func (r *Repository) GetCurrentRoomPresence(ctx context.Context, userID int64) ( &presence.MicSessionID, &presence.RoomVersion, &presence.Status, + &presence.JoinedAtMS, &presence.LastSeenAtMS, &presence.UpdatedAtMS, ); err != nil { @@ -2210,11 +2270,19 @@ func (r *Repository) ListRoomOnlineUsers(ctx context.Context, query roomservice. return roomservice.RoomOnlineUserPage{}, err } + orderBy := "p.last_seen_at_ms DESC, p.user_id ASC" + if strings.EqualFold(strings.TrimSpace(query.Sort), "gift_value") { + orderBy = "COALESCE(g.gift_value, 0) DESC, p.last_seen_at_ms DESC, p.user_id ASC" + } + rows, err := r.db.QueryContext(ctx, - `SELECT app_code, user_id, room_id, role, publish_state, mic_session_id, room_version, status, last_seen_at_ms, updated_at_ms - FROM room_user_presence - WHERE app_code = ? AND room_id = ? AND status = ? - ORDER BY last_seen_at_ms DESC, user_id ASC + `SELECT p.app_code, p.user_id, p.room_id, p.role, p.publish_state, p.mic_session_id, p.room_version, p.status, + p.joined_at_ms, p.last_seen_at_ms, p.updated_at_ms, COALESCE(g.gift_value, 0) + FROM room_user_presence p + LEFT JOIN room_user_gift_stats g + ON g.app_code = p.app_code AND g.room_id = p.room_id AND g.user_id = p.user_id + WHERE p.app_code = ? AND p.room_id = ? AND p.status = ? + ORDER BY `+orderBy+` LIMIT ? OFFSET ?`, appCode, query.RoomID, @@ -2239,8 +2307,10 @@ func (r *Repository) ListRoomOnlineUsers(ctx context.Context, query roomservice. &presence.MicSessionID, &presence.RoomVersion, &presence.Status, + &presence.JoinedAtMS, &presence.LastSeenAtMS, &presence.UpdatedAtMS, + &presence.GiftValue, ); err != nil { return roomservice.RoomOnlineUserPage{}, err }