2026-05-12 19:34:47 +08:00

83 lines
2.9 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 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
}