package integration import ( "context" "time" activityv1 "hyapp.local/api/proto/activity/v1" roomeventsv1 "hyapp.local/api/proto/events/room/v1" "hyapp/pkg/appcode" ) // ActivityOutboxPublisher 把已经提交的 room outbox 事实转发给 activity-service。 // 它是 best-effort 桥接:转发失败不回滚 Room Cell,因为房间状态和 command log 已经是权威事实。 type ActivityOutboxPublisher struct { client activityv1.RoomEventConsumerServiceClient timeout time.Duration } // NewActivityOutboxPublisher 创建 room-service 到 activity-service 的事件消费桥。 // timeout 较短是有意的,避免活动/播报服务慢调用拖住房间命令返回。 func NewActivityOutboxPublisher(client activityv1.RoomEventConsumerServiceClient, timeout time.Duration) *ActivityOutboxPublisher { if timeout <= 0 { timeout = time.Second } return &ActivityOutboxPublisher{client: client, timeout: timeout} } // PublishOutboxEvent 发送一条 protobuf room outbox envelope。 // RequestMeta 使用 envelope event_id 作为 request_id,排查链路时能直接从房间事件定位到活动消费日志。 func (p *ActivityOutboxPublisher) PublishOutboxEvent(ctx context.Context, envelope *roomeventsv1.EventEnvelope) error { if p == nil || p.client == nil || envelope == nil { // 未配置 activity-service 时保持 no-op,保证房间核心链路可独立运行。 return nil } callCtx, cancel := context.WithTimeout(appcode.WithContext(ctx, envelope.GetAppCode()), p.timeout) defer cancel() _, err := p.client.ConsumeRoomEvent(callCtx, &activityv1.ConsumeRoomEventRequest{ Meta: &activityv1.RequestMeta{ RequestId: envelope.GetEventId(), Caller: "room-service", SentAtMs: time.Now().UTC().UnixMilli(), AppCode: envelope.GetAppCode(), }, Envelope: envelope, }) return err }