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