2026-06-08 19:00:23 +08:00

656 lines
27 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Package command 定义 room-service 可持久化、可回放的房间命令模型。
package command
import (
"encoding/json"
"fmt"
)
// 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 {
// AppCode 是多 App 租户键;同一个 room_id 在不同 App 下必须完全隔离。
AppCode string `json:"app_code"`
// 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"`
// SeatCount 决定初始化麦位数量。
SeatCount int32 `json:"seat_count"`
// Mode 记录房间模式,当前版本只保存不做复杂策略分发。
Mode string `json:"mode"`
// VisibleRegionID 是创建时固定的房间列表区域0 表示 GLOBAL。
VisibleRegionID int64 `json:"visible_region_id"`
// OwnerCountryCode 是创建瞬间房主所属国家,列表国家筛选以该投影为准。
OwnerCountryCode string `json:"owner_country_code,omitempty"`
// RoomName 是房间展示名称,恢复时回填 RoomExt["title"],不拆成独立状态 owner。
RoomName string `json:"room_name"`
// RoomAvatar 是创建时必填的房间封面,恢复时回填 RoomExt["cover_url"]。
RoomAvatar string `json:"room_avatar"`
// RoomDescription 是房间简介,恢复时回填 RoomExt["description"]。
RoomDescription string `json:"room_description"`
// RoomShortID 是房间短 ID首版等于创建者当前展示短号。
RoomShortID string `json:"room_short_id"`
}
// Type 返回命令类型。
func (CreateRoom) Type() string { return "create_room" }
// UpdateRoomProfile 定义房主修改房间展示资料和麦位数量的请求。
type UpdateRoomProfile struct {
Base
// RoomName 非 nil 表示更新房间展示名称;空字符串由服务层校验拒绝。
RoomName *string `json:"room_name,omitempty"`
// RoomAvatar 非 nil 表示更新房间头像;空字符串会恢复为系统默认头像。
RoomAvatar *string `json:"room_avatar,omitempty"`
// RoomDescription 非 nil 表示更新房间简介,允许清空。
RoomDescription *string `json:"room_description,omitempty"`
// SeatCount 非 nil 表示调整麦位总数,值必须来自后台启用配置。
SeatCount *int32 `json:"seat_count,omitempty"`
}
// Type 返回命令类型。
func (UpdateRoomProfile) Type() string { return "update_room_profile" }
// SetRoomBackground 定义房主把已保存背景图设置为当前房间背景的请求。
type SetRoomBackground struct {
Base
// BackgroundID 必须归属于当前房间,避免房主把其他房间的素材串用到自己的房间状态。
BackgroundID int64 `json:"background_id"`
// BackgroundURL 是提交前从房间背景素材表解析出的确定性快照,恢复时不再回查列表表。
BackgroundURL string `json:"background_url"`
}
func (SetRoomBackground) Type() string { return "set_room_background" }
// JoinRoom 定义业务进房请求。
type EntryVehicleSnapshot struct {
ResourceID int64 `json:"resource_id,omitempty"`
ResourceCode string `json:"resource_code,omitempty"`
Name string `json:"name,omitempty"`
AssetURL string `json:"asset_url,omitempty"`
PreviewURL string `json:"preview_url,omitempty"`
AnimationURL string `json:"animation_url,omitempty"`
MetadataJSON string `json:"metadata_json,omitempty"`
EntitlementID string `json:"entitlement_id,omitempty"`
ExpiresAtMS int64 `json:"expires_at_ms,omitempty"`
}
type JoinRoom struct {
Base
// Role 是用户在房间内的轻量角色,默认 audience。
Role string `json:"role"`
// ActorCountryID 是 gateway 从 user-service 注入的真实国家,用于统计进房活跃国家维度。
ActorCountryID int64 `json:"actor_country_id,omitempty"`
// ActorRegionID 是 gateway 从 user-service 注入的真实区域;房间 visible_region_id 不能代替用户国家。
ActorRegionID int64 `json:"actor_region_id,omitempty"`
// EntryVehicle 是 gateway 在进房时解析出的当前有效佩戴座驾快照,用于进房 IM。
EntryVehicle EntryVehicleSnapshot `json:"entry_vehicle,omitempty"`
}
// Type 返回命令类型。
func (JoinRoom) Type() string { return "join_room" }
// RoomHeartbeat 定义房间业务 presence 显式心跳。
type RoomHeartbeat struct {
Base
}
// Type 返回命令类型。
func (RoomHeartbeat) Type() string { return "room_heartbeat" }
// LeaveRoom 定义业务离房请求。
type LeaveRoom struct {
Base
}
// Type 返回命令类型。
func (LeaveRoom) Type() string { return "leave_room" }
// CloseRoom 定义房间关闭请求。
type CloseRoom struct {
Base
// Reason 记录关闭来源,例如 closed_by_owner 或 admin_closed。
Reason string `json:"reason,omitempty"`
}
// Type 返回命令类型。
func (CloseRoom) Type() string { return "close_room" }
// AdminUpdateRoom 定义后台房间资料和生命周期修改请求。
type AdminUpdateRoom struct {
Base
Title *string `json:"title,omitempty"`
CoverURL *string `json:"cover_url,omitempty"`
Description *string `json:"description,omitempty"`
Mode *string `json:"mode,omitempty"`
Status *string `json:"status,omitempty"`
VisibleRegionID *int64 `json:"visible_region_id,omitempty"`
OwnerCountryCode *string `json:"owner_country_code,omitempty"`
CloseReason string `json:"close_reason,omitempty"`
AdminID uint64 `json:"admin_id,omitempty"`
AdminName string `json:"admin_name,omitempty"`
}
func (AdminUpdateRoom) Type() string { return "admin_update_room" }
// AdminDeleteRoom 定义后台删除房间请求;删除是 Room Cell 终态,不直接删物理表。
type AdminDeleteRoom struct {
Base
AdminID uint64 `json:"admin_id,omitempty"`
AdminName string `json:"admin_name,omitempty"`
}
func (AdminDeleteRoom) Type() string { return "admin_delete_room" }
// MicUp 定义上麦请求。
type MicUp struct {
Base
// SeatNo 是目标麦位编号,从 RoomState 初始化的麦位集合中查找。
SeatNo int32 `json:"seat_no"`
// MicSessionID 是服务端为本次上麦生成的发流会话 ID确认和超时释放都必须匹配它。
MicSessionID string `json:"mic_session_id"`
// PublishDeadlineMS 是客户端必须确认 RTC 发流成功的截止时间。
PublishDeadlineMS int64 `json:"publish_deadline_ms"`
}
// Type 返回命令类型。
func (MicUp) Type() string { return "mic_up" }
// MicDown 定义下麦请求。
type MicDown struct {
Base
// TargetUserID 是被下麦用户;操作者和目标用户可以不同。
TargetUserID int64 `json:"target_user_id"`
// MicSessionID 非空时只允许释放同一发流会话,防止旧超时任务清掉新上麦。
MicSessionID string `json:"mic_session_id,omitempty"`
// Reason 记录系统触发下麦原因,例如 publish_timeout主动下麦保持为空。
Reason string `json:"reason,omitempty"`
}
// Type 返回命令类型。
func (MicDown) Type() string { return "mic_down" }
// ConfirmMicPublishing 定义 RTC 发流成功确认请求。
type ConfirmMicPublishing struct {
Base
// TargetUserID 是被确认发布音频的用户;为空时由服务层使用 ActorUserID。
TargetUserID int64 `json:"target_user_id"`
// MicSessionID 必须匹配当前麦位上的会话。
MicSessionID string `json:"mic_session_id"`
// RoomVersion 是触发客户端/RTC 事件时看到的房间版本,旧版本事件会被忽略。
RoomVersion int64 `json:"room_version"`
// EventTimeMS 是客户端 SDK 或 RTC webhook 的事件时间,必须晚于已接受事件。
EventTimeMS int64 `json:"event_time_ms"`
// Source 记录确认来源,例如 client 或 rtc_webhook。
Source string `json:"source,omitempty"`
}
// Type 返回命令类型。
func (ConfirmMicPublishing) Type() string { return "confirm_mic_publishing" }
// MicHeartbeat 定义当前麦位会话的服务端心跳刷新请求。
type MicHeartbeat struct {
Base
// TargetUserID 是被刷新麦上心跳的用户;为空时服务层使用 ActorUserID。
TargetUserID int64 `json:"target_user_id"`
// MicSessionID 必须匹配当前麦位会话,避免旧客户端心跳续住新麦位。
MicSessionID string `json:"mic_session_id"`
}
// Type 返回命令类型。
func (MicHeartbeat) Type() string { return "mic_heartbeat" }
// SetMicMute 定义麦克风静音状态同步请求。
type SetMicMute struct {
Base
// TargetUserID 是被修改麦克风静音态的麦上用户。
TargetUserID int64 `json:"target_user_id"`
// Muted 为 true 表示麦克风静音false 表示取消静音。
Muted bool `json:"muted"`
}
// Type 返回命令类型。
func (SetMicMute) Type() string { return "set_mic_mute" }
// ApplyRTCEvent 定义由可信腾讯 RTC 服务端回调驱动的房间状态事件。
type ApplyRTCEvent struct {
Base
// TargetUserID 是腾讯 RTC 回调中的 UserId必须映射回内部长 user_id。
TargetUserID int64 `json:"target_user_id"`
// EventType 是 gateway 标准化后的 RTC 事件,当前为 audio_started/audio_stopped/room_exited。
EventType string `json:"event_type"`
// EventTimeMS 是腾讯 RTC 回调中的 EventMsTs用于丢弃旧事件。
EventTimeMS int64 `json:"event_time_ms"`
// Reason 保留腾讯回调 reason 和入口语义,便于 IM 房间消息和审计排障。
Reason string `json:"reason,omitempty"`
// Source 记录确认来源,当前为 tencent_rtc_callback。
Source string `json:"source,omitempty"`
// ExternalEventID 是由 gateway 从腾讯事件字段派生的稳定事件标识。
ExternalEventID string `json:"external_event_id,omitempty"`
}
// Type 返回命令类型。
func (ApplyRTCEvent) Type() string { return "apply_rtc_event" }
// 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" }
// SetMicSeatLock 定义锁定或解锁麦位请求。
type SetMicSeatLock struct {
Base
// SeatNo 是目标麦位编号。
SeatNo int32 `json:"seat_no"`
// Locked 为 true 表示锁定false 表示解锁。
Locked bool `json:"locked"`
}
// Type 返回命令类型。
func (SetMicSeatLock) Type() string { return "set_mic_seat_lock" }
// SetChatEnabled 定义房间公屏开关请求。
type SetChatEnabled struct {
Base
// Enabled 为 false 时 CheckSpeakPermission 会拒绝所有普通公屏发言。
Enabled bool `json:"enabled"`
}
// Type 返回命令类型。
func (SetChatEnabled) Type() string { return "set_chat_enabled" }
// SetRoomPassword 定义房主设置或清空入房密码的请求。
type SetRoomPassword struct {
Base
// Locked 为 false 表示清空密码true 表示开启入房密码校验。
Locked bool `json:"locked"`
// Password 只在当前进程内参与 command_id 冲突判断,序列化时必须省略,避免明文进入 command log。
Password string `json:"-"`
// PasswordHash 是服务端生成的密码哈希command log 不能保存客户端明文。
PasswordHash string `json:"password_hash,omitempty"`
}
// Type 返回命令类型。
func (SetRoomPassword) Type() string { return "set_room_password" }
// SetRoomAdmin 定义添加或移除管理员请求。
type SetRoomAdmin struct {
Base
// TargetUserID 是被添加或移除管理员身份的用户。
TargetUserID int64 `json:"target_user_id"`
// Enabled 为 true 表示添加管理员false 表示移除管理员。
Enabled bool `json:"enabled"`
}
// Type 返回命令类型。
func (SetRoomAdmin) Type() string { return "set_room_admin" }
// 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"`
// DurationMS 为 0 表示永久 ban大于 0 表示从命令提交时间起禁止进房的时长。
DurationMS int64 `json:"duration_ms"`
}
// Type 返回命令类型。
func (KickUser) Type() string { return "kick_user" }
// SystemEvictUser 定义平台级封禁、风控或后台治理触发的房间驱逐请求。
type SystemEvictUser struct {
Base
// TargetUserID 是被系统驱逐的房间用户。
TargetUserID int64 `json:"target_user_id"`
// OperatorUserID 是触发治理动作的后台管理员或 App 管理员,不参与房间权限判断。
OperatorUserID int64 `json:"operator_user_id"`
// Reason 记录封禁、风控或后台动作来源,进入 command log 便于审计追踪。
Reason string `json:"reason"`
// BanFromRoom 控制是否写入当前房间 ban 集合。
BanFromRoom bool `json:"ban_from_room"`
}
// Type 返回命令类型。
func (SystemEvictUser) Type() string { return "system_evict_user" }
// UnbanUser 定义解除房间 ban 请求。
type UnbanUser struct {
Base
// TargetUserID 是被解除 ban 的用户。
TargetUserID int64 `json:"target_user_id"`
}
// Type 返回命令类型。
func (UnbanUser) Type() string { return "unban_user" }
// SendGift 定义送礼请求。
type SendGift struct {
Base
// TargetType 当前只支持 user保留字段避免后续 all_mic/all_room/couple 再改命令结构。
TargetType string `json:"target_type"`
// TargetUserIDs 是新协议目标用户集合target_type=user 时允许多个房间内用户。
TargetUserIDs []int64 `json:"target_user_ids,omitempty"`
// TargetUserID 是兼容单目标字段和批量主目标,所有接收方仍要求在房间内。
TargetUserID int64 `json:"target_user_id"`
// GiftID 是礼物配置标识,具体配置有效性当前由调用方和 wallet 链路保证。
GiftID string `json:"gift_id"`
// PoolID 是幸运礼物奖池标识;为空表示普通礼物或默认幸运奖池。
PoolID string `json:"pool_id,omitempty"`
// GiftCount 是礼物数量,必须为正数。
GiftCount int32 `json:"gift_count"`
// SenderRegionID 是送礼用户所属区域,由 gateway 从 user-service 注入。
SenderRegionID int64 `json:"sender_region_id,omitempty"`
// SenderCountryID 是送礼用户所属国家,由 gateway 从 user-service 注入,统计服务不能用房间区域反推。
SenderCountryID int64 `json:"sender_country_id,omitempty"`
// TargetIsHost 是 gateway 从 user-service active host profile 注入的工资入账开关。
TargetIsHost bool `json:"target_is_host,omitempty"`
// TargetHostRegionID 是工资政策匹配区域room-service 不自行推导主播身份或区域。
TargetHostRegionID int64 `json:"target_host_region_id,omitempty"`
// TargetAgencyOwnerUserID 是 gateway 从 user-service active host profile 注入的代理收款人快照。
TargetAgencyOwnerUserID int64 `json:"target_agency_owner_user_id,omitempty"`
// BillingReceiptID 是 wallet-service 成功落账后的回执,用于排障和恢复审计。
BillingReceiptID string `json:"billing_receipt_id,omitempty"`
// CoinSpent 是 sender COIN 实际扣减值,来自 wallet-service 服务端价格。
CoinSpent int64 `json:"coin_spent,omitempty"`
// GiftPointAdded 是历史命令字段GIFT_POINT 已下线,新送礼不再赋值,恢复逻辑只依赖 HeatValue。
GiftPointAdded int64 `json:"gift_point_added,omitempty"`
// HeatValue 是 Room Cell 恢复时唯一可信的热度增量,不能重新用客户端价格推导。
HeatValue int64 `json:"heat_value,omitempty"`
// PriceVersion 是本次钱包结算使用的服务端礼物价格版本。
PriceVersion string `json:"price_version,omitempty"`
// GiftTypeCode 是 wallet-service 结算时锁定的礼物类型,用于火箭礼物类型燃料规则。
GiftTypeCode string `json:"gift_type_code,omitempty"`
// HostPeriodDiamondAdded 是 wallet-service 给主播周期钻石账户本次入账的钻石数。
HostPeriodDiamondAdded int64 `json:"host_period_diamond_added,omitempty"`
// HostPeriodCycleKey 是本次周期钻石入账所属 UTC 月周期。
HostPeriodCycleKey string `json:"host_period_cycle_key,omitempty"`
// RocketTouched 表示本次送礼已经计算过火箭状态,恢复时直接使用下列确定性快照。
RocketTouched bool `json:"rocket_touched,omitempty"`
// RocketAddedFuel 是按后台规则算出的理论燃料,倒计时和溢出规则会让它不完全生效。
RocketAddedFuel int64 `json:"rocket_added_fuel,omitempty"`
// RocketEffectiveFuel 是实际写入当前等级进度的燃料,溢出不会进入下一级。
RocketEffectiveFuel int64 `json:"rocket_effective_fuel,omitempty"`
// RocketOverflowFuel 是本次礼物在当前等级阈值之外被丢弃的燃料。
RocketOverflowFuel int64 `json:"rocket_overflow_fuel,omitempty"`
// RocketLevel/RocketFuel/RocketStatus 是送礼后火箭状态快照。
RocketLevel int32 `json:"rocket_level,omitempty"`
RocketFuel int64 `json:"rocket_fuel,omitempty"`
RocketStatus string `json:"rocket_status,omitempty"`
// RocketFuelThreshold 是当前等级阈值快照。
RocketFuelThreshold int64 `json:"rocket_fuel_threshold,omitempty"`
// RocketIgnitedAtMS/RocketLaunchAtMS 只在点火进入倒计时时写入。
RocketIgnitedAtMS int64 `json:"rocket_ignited_at_ms,omitempty"`
RocketLaunchAtMS int64 `json:"rocket_launch_at_ms,omitempty"`
// RocketResetAtMS 是该火箭进度所属 UTC 日的下一次重置时间。
RocketResetAtMS int64 `json:"rocket_reset_at_ms,omitempty"`
// RocketTop1UserID/RocketIgniterUserID 在燃料满瞬间锁定,离房后仍用它们发奖。
RocketTop1UserID int64 `json:"rocket_top1_user_id,omitempty"`
RocketIgniterUserID int64 `json:"rocket_igniter_user_id,omitempty"`
// RocketID 是本轮火箭幂等 ID发射、抽奖和 IM 都围绕它关联。
RocketID string `json:"rocket_id,omitempty"`
// RocketConfigVersion 是本轮火箭采用的后台配置版本。
RocketConfigVersion int64 `json:"rocket_config_version,omitempty"`
// RocketPendingLaunches 是送礼提交后的待发射队列,恢复时不能只保存当前进度。
RocketPendingLaunches []RocketPendingLaunch `json:"rocket_pending_launches,omitempty"`
}
// Type 返回命令类型。
func (SendGift) Type() string { return "send_gift" }
// RocketRewardGrant 记录发射命令中已经结算出的资源组发放结果。
type RocketRewardGrant struct {
RewardRole string `json:"reward_role"`
UserID int64 `json:"user_id"`
RewardItemID string `json:"reward_item_id"`
ResourceGroupID int64 `json:"resource_group_id"`
DisplayName string `json:"display_name,omitempty"`
IconURL string `json:"icon_url,omitempty"`
GrantID string `json:"grant_id,omitempty"`
Status string `json:"status"`
}
// RocketPendingLaunch 记录一枚已点火火箭的延迟发射快照。
type RocketPendingLaunch struct {
RocketID string `json:"rocket_id"`
Level int32 `json:"level"`
FuelThreshold int64 `json:"fuel_threshold"`
IgnitedAtMS int64 `json:"ignited_at_ms"`
LaunchAtMS int64 `json:"launch_at_ms"`
ResetAtMS int64 `json:"reset_at_ms"`
Top1UserID int64 `json:"top1_user_id,omitempty"`
IgniterUserID int64 `json:"igniter_user_id,omitempty"`
ConfigVersion int64 `json:"config_version"`
CoverURL string `json:"cover_url,omitempty"`
}
// LaunchRoomRocket 定义系统到点发射命令。
type LaunchRoomRocket struct {
Base
RocketID string `json:"rocket_id"`
Level int32 `json:"level"`
// Current* 是发射命令提交后的当前进度快照;发射不推进当前等级,只移除待发射队列。
CurrentLevel int32 `json:"current_level"`
CurrentFuel int64 `json:"current_fuel"`
CurrentFuelThreshold int64 `json:"current_fuel_threshold"`
CurrentStatus string `json:"current_status"`
CurrentRocketID string `json:"current_rocket_id,omitempty"`
NextLevel int32 `json:"next_level"`
NextFuelThreshold int64 `json:"next_fuel_threshold"`
ConfigVersion int64 `json:"config_version"`
ResetAtMS int64 `json:"reset_at_ms"`
Top1UserID int64 `json:"top1_user_id,omitempty"`
IgniterUserID int64 `json:"igniter_user_id,omitempty"`
LaunchedAtMS int64 `json:"launched_at_ms"`
InRoomUserIDs []int64 `json:"in_room_user_ids,omitempty"`
Rewards []RocketRewardGrant `json:"rewards,omitempty"`
PendingLaunches []RocketPendingLaunch `json:"pending_launches,omitempty"`
}
// Type 返回命令类型。
func (LaunchRoomRocket) Type() string { return "launch_room_rocket" }
// Serialize 把命令编码成 command log 持久化所需的稳定载荷。
func Serialize(cmd Command) ([]byte, error) {
// 当前 command log 使用 JSON 载荷,便于排查;命令类型由独立列保存。
return json.Marshal(cmd)
}
// IdempotencyPayloadForCommand 生成用于 command_id 冲突判定的业务载荷。
func IdempotencyPayloadForCommand(cmd Command) ([]byte, error) {
payload, err := Serialize(cmd)
if err != nil {
return nil, err
}
return IdempotencyPayload(payload)
}
// IdempotencyPayload 去掉请求追踪和入口节点等易变字段,只保留业务命令语义。
func IdempotencyPayload(payload []byte) ([]byte, error) {
var values map[string]any
if err := json.Unmarshal(payload, &values); err != nil {
return nil, err
}
delete(values, "request_id")
delete(values, "gateway_node_id")
delete(values, "session_id")
delete(values, "sent_at_ms")
// SendGift 的以下字段由 wallet-service 结算后写入 command log不属于客户端请求幂等语义。
delete(values, "billing_receipt_id")
delete(values, "coin_spent")
delete(values, "gift_point_added")
delete(values, "heat_value")
delete(values, "price_version")
delete(values, "gift_type_code")
delete(values, "host_period_diamond_added")
delete(values, "host_period_cycle_key")
delete(values, "rocket_touched")
delete(values, "rocket_added_fuel")
delete(values, "rocket_effective_fuel")
delete(values, "rocket_overflow_fuel")
delete(values, "rocket_level")
delete(values, "rocket_fuel")
delete(values, "rocket_status")
delete(values, "rocket_fuel_threshold")
delete(values, "rocket_ignited_at_ms")
delete(values, "rocket_launch_at_ms")
delete(values, "rocket_reset_at_ms")
delete(values, "rocket_top1_user_id")
delete(values, "rocket_igniter_user_id")
delete(values, "rocket_id")
delete(values, "rocket_config_version")
delete(values, "rocket_pending_launches")
// LaunchRoomRocket 的以下字段是 worker 在发射时结算出的结果,不属于触发发射的幂等语义。
delete(values, "current_level")
delete(values, "current_fuel")
delete(values, "current_fuel_threshold")
delete(values, "current_status")
delete(values, "current_rocket_id")
delete(values, "config_version")
delete(values, "reset_at_ms")
delete(values, "top1_user_id")
delete(values, "igniter_user_id")
delete(values, "launched_at_ms")
delete(values, "in_room_user_ids")
delete(values, "rewards")
delete(values, "pending_launches")
// SetRoomPassword 的哈希带随机盐;通用 payload 比较先去掉哈希service 层再用 transient 明文和已存 bcrypt 哈希确认是否同一密码。
delete(values, "password_hash")
// MicUp 的发流确认 deadline 由 room-service 生成,客户端重试同一 command_id 时不能因此冲突。
delete(values, "publish_deadline_ms")
return json.Marshal(values)
}
// Deserialize 按命令类型恢复命令载荷。
func Deserialize(commandType string, payload []byte) (Command, error) {
var cmd Command
// command_type 是恢复时的分发键;未知类型表示当前代码无法保证恢复语义。
switch commandType {
case CreateRoom{}.Type():
cmd = &CreateRoom{}
case UpdateRoomProfile{}.Type():
cmd = &UpdateRoomProfile{}
case SetRoomBackground{}.Type():
cmd = &SetRoomBackground{}
case JoinRoom{}.Type():
cmd = &JoinRoom{}
case RoomHeartbeat{}.Type():
cmd = &RoomHeartbeat{}
case LeaveRoom{}.Type():
cmd = &LeaveRoom{}
case CloseRoom{}.Type():
cmd = &CloseRoom{}
case AdminUpdateRoom{}.Type():
cmd = &AdminUpdateRoom{}
case AdminDeleteRoom{}.Type():
cmd = &AdminDeleteRoom{}
case MicUp{}.Type():
cmd = &MicUp{}
case MicDown{}.Type():
cmd = &MicDown{}
case ConfirmMicPublishing{}.Type():
cmd = &ConfirmMicPublishing{}
case MicHeartbeat{}.Type():
cmd = &MicHeartbeat{}
case SetMicMute{}.Type():
cmd = &SetMicMute{}
case ApplyRTCEvent{}.Type():
cmd = &ApplyRTCEvent{}
case ChangeMicSeat{}.Type():
cmd = &ChangeMicSeat{}
case SetMicSeatLock{}.Type():
cmd = &SetMicSeatLock{}
case SetChatEnabled{}.Type():
cmd = &SetChatEnabled{}
case SetRoomPassword{}.Type():
cmd = &SetRoomPassword{}
case SetRoomAdmin{}.Type():
cmd = &SetRoomAdmin{}
case MuteUser{}.Type():
cmd = &MuteUser{}
case KickUser{}.Type():
cmd = &KickUser{}
case SystemEvictUser{}.Type():
cmd = &SystemEvictUser{}
case UnbanUser{}.Type():
cmd = &UnbanUser{}
case SendGift{}.Type():
cmd = &SendGift{}
case LaunchRoomRocket{}.Type():
cmd = &LaunchRoomRocket{}
default:
return nil, fmt.Errorf("unknown room command type: %s", commandType)
}
if err := json.Unmarshal(payload, cmd); err != nil {
// 单条命令反序列化失败说明 command log 损坏,恢复流程必须失败而不是继续构建错误状态。
return nil, err
}
return cmd, nil
}