// Package command 定义 room-service 可持久化、可回放的房间命令模型。 package command import "encoding/json" // Command 统一表达可写入 command log 的房间命令。 type Command interface { // ID 返回命令幂等键,通常来自 gateway 的 RequestMeta.command_id。 ID() string // RoomID 返回命令所属房间,Room Cell 路由和 command log 分区依赖它。 RoomID() string // ActorUserID 返回命令操作者,不能由具体命令字段重复推导。 ActorUserID() int64 // Type 返回稳定命令类型,持久化和恢复反序列化都依赖该字符串。 Type() string } // Base 承载所有房间命令都共享的元信息。 type Base struct { // RequestID 是入口请求 ID,用于追踪一次外部调用。 RequestID string `json:"request_id"` // CommandID 是幂等主键,重复命令必须返回已提交状态而不是重复变更房间。 CommandID string `json:"command_id"` // ActorID 是操作者用户 ID,房间权限和 presence 校验以它为准。 ActorID int64 `json:"actor_user_id"` // Room 是目标房间 ID,命令只允许作用于一个 Room Cell。 Room string `json:"room_id"` // GatewayNodeID 记录入口节点,便于排查命令来源。 GatewayNodeID string `json:"gateway_node_id"` // SessionID 记录 gateway 看到的客户端会话,不等同于腾讯云 IM 连接。 SessionID string `json:"session_id"` // SentAtMS 是客户端请求进入系统的时间,恢复回放时用于还原用户态时间字段。 SentAtMS int64 `json:"sent_at_ms"` } // ID 返回命令幂等主键。 func (b Base) ID() string { return b.CommandID } // RoomID 返回命令所属房间。 func (b Base) RoomID() string { return b.Room } // ActorUserID 返回命令操作者。 func (b Base) ActorUserID() int64 { return b.ActorID } // CreateRoom 定义房间创建请求。 type CreateRoom struct { Base // OwnerUserID 是房间所有者,创建后自动进入管理员集合。 OwnerUserID int64 `json:"owner_user_id"` // HostUserID 是主持人,创建后也进入管理员集合。 HostUserID int64 `json:"host_user_id"` // SeatCount 决定初始化麦位数量。 SeatCount int32 `json:"seat_count"` // Mode 记录房间模式,当前版本只保存不做复杂策略分发。 Mode string `json:"mode"` } // Type 返回命令类型。 func (CreateRoom) Type() string { return "create_room" } // JoinRoom 定义业务进房请求。 type JoinRoom struct { Base // Role 是用户在房间内的轻量角色,默认 audience。 Role string `json:"role"` } // Type 返回命令类型。 func (JoinRoom) Type() string { return "join_room" } // LeaveRoom 定义业务离房请求。 type LeaveRoom struct { Base } // Type 返回命令类型。 func (LeaveRoom) Type() string { return "leave_room" } // MicUp 定义上麦请求。 type MicUp struct { Base // SeatNo 是目标麦位编号,从 RoomState 初始化的麦位集合中查找。 SeatNo int32 `json:"seat_no"` } // Type 返回命令类型。 func (MicUp) Type() string { return "mic_up" } // MicDown 定义下麦请求。 type MicDown struct { Base // TargetUserID 是被下麦用户;操作者和目标用户可以不同。 TargetUserID int64 `json:"target_user_id"` } // Type 返回命令类型。 func (MicDown) Type() string { return "mic_down" } // ChangeMicSeat 定义换麦位请求。 type ChangeMicSeat struct { Base // TargetUserID 是需要换位的用户。 TargetUserID int64 `json:"target_user_id"` // SeatNo 是目标麦位编号。 SeatNo int32 `json:"seat_no"` } // Type 返回命令类型。 func (ChangeMicSeat) Type() string { return "change_mic_seat" } // MuteUser 定义禁言请求。 type MuteUser struct { Base // TargetUserID 是被修改禁言状态的用户。 TargetUserID int64 `json:"target_user_id"` // Muted 为 true 表示禁言,false 表示解除禁言。 Muted bool `json:"muted"` } // Type 返回命令类型。 func (MuteUser) Type() string { return "mute_user" } // KickUser 定义踢人请求。 type KickUser struct { Base // TargetUserID 是被踢出并加入 ban 集合的用户。 TargetUserID int64 `json:"target_user_id"` } // Type 返回命令类型。 func (KickUser) Type() string { return "kick_user" } // SendGift 定义送礼请求。 type SendGift struct { Base // TargetUserID 是礼物接收方,当前版本要求接收方在房间内。 TargetUserID int64 `json:"target_user_id"` // GiftID 是礼物配置标识,具体配置有效性当前由调用方和 wallet 链路保证。 GiftID string `json:"gift_id"` // GiftCount 是礼物数量,必须为正数。 GiftCount int32 `json:"gift_count"` // GiftUnitValue 是单个礼物价值,必须为正数,账务扣费由 wallet-service 执行。 GiftUnitValue int64 `json:"gift_unit_value"` } // Type 返回命令类型。 func (SendGift) Type() string { return "send_gift" } // Serialize 把命令编码成 command log 持久化所需的稳定载荷。 func Serialize(cmd Command) ([]byte, error) { // 当前 command log 使用 JSON 载荷,便于排查;命令类型由独立列保存。 return json.Marshal(cmd) } // Deserialize 按命令类型恢复命令载荷。 func Deserialize(commandType string, payload []byte) (Command, error) { var cmd Command // command_type 是恢复时的分发键,未知命令返回 nil 让恢复流程安全跳过。 switch commandType { case CreateRoom{}.Type(): cmd = &CreateRoom{} case JoinRoom{}.Type(): cmd = &JoinRoom{} case LeaveRoom{}.Type(): cmd = &LeaveRoom{} case MicUp{}.Type(): cmd = &MicUp{} case MicDown{}.Type(): cmd = &MicDown{} case ChangeMicSeat{}.Type(): cmd = &ChangeMicSeat{} case MuteUser{}.Type(): cmd = &MuteUser{} case KickUser{}.Type(): cmd = &KickUser{} case SendGift{}.Type(): cmd = &SendGift{} default: return nil, nil } if err := json.Unmarshal(payload, cmd); err != nil { // 单条命令反序列化失败说明 command log 损坏,恢复流程必须失败而不是继续构建错误状态。 return nil, err } return cmd, nil }