// Package outbox 定义 room-service 房间外事件的统一封装和状态模型。 package outbox import ( "time" "google.golang.org/protobuf/proto" roomeventsv1 "hyapp.local/api/proto/events/room/v1" "hyapp/pkg/appcode" "hyapp/pkg/idgen" ) const ( // StatusPending 表示事件已经入库,但尚未投递完成。 StatusPending = "pending" // StatusDelivering 表示事件已被某个 worker 抢占,正在投递中。 StatusDelivering = "delivering" // StatusRetryable 表示上次投递失败,后续 worker 可继续重试。 StatusRetryable = "retryable" // StatusFailed 表示事件不可重试,需要人工或专项补偿处理。 StatusFailed = "failed" // StatusDelivered 表示事件已成功投递到外部消费者。 StatusDelivered = "delivered" ) // Record 表达 room_outbox 表的一条逻辑记录。 type Record struct { // AppCode 是事件所属 App,补偿投递和消费者都不能跨 App 合并房间事件。 AppCode string // EventID 是 outbox 事件幂等键,同时会传给腾讯云 IM 作为房间系统事件 event_id。 EventID string // EventType 是事件类型字符串,例如 RoomGiftSent、RoomMicChanged。 EventType string // RoomID 是事件所属房间,外部消费者可按房间分区处理。 RoomID string // Status 表示投递状态,只在 repository/worker 侧变更。 Status string // Envelope 是 protobuf 统一信封,body 内是具体事件消息的序列化结果。 Envelope *roomeventsv1.EventEnvelope // CreatedAtMS 是事件入库的 UTC 毫秒时间戳,也是待投递扫描的排序依据。 CreatedAtMS int64 // RetryCount 记录补偿投递失败次数,便于后续告警和退避策略。 RetryCount int // LastError 保存最近一次投递失败原因。 LastError string // WorkerID 记录当前抢占该事件的 worker。 WorkerID string // LockUntilMS 是 delivering 锁的截止时间。 LockUntilMS int64 // NextRetryAtMS 是 retryable 事件下一次允许抢占的 UTC 毫秒时间。 NextRetryAtMS int64 } // Build 把具体事件体封装成统一 outbox 记录。 func Build(roomID string, eventType string, roomVersion int64, occurredAt time.Time, body proto.Message) (Record, error) { // 事件体先序列化到信封 body,避免 outbox 表按事件类型拆列。 bodyBytes, err := proto.Marshal(body) if err != nil { return Record{}, err } // EventID 在 room-service 生成,后续同步广播和异步补偿都用同一个 ID 去重。 eventID := idgen.New("evt") return Record{ AppCode: appcode.Default, EventID: eventID, EventType: eventType, RoomID: roomID, Status: StatusPending, Envelope: &roomeventsv1.EventEnvelope{ AppCode: appcode.Default, EventId: eventID, RoomId: roomID, EventType: eventType, RoomVersion: roomVersion, OccurredAtMs: occurredAt.UnixMilli(), Body: bodyBytes, }, CreatedAtMS: occurredAt.UnixMilli(), }, nil }