2026-04-27 02:29:42 +08:00

184 lines
6.0 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 integration
import (
"context"
"fmt"
"google.golang.org/protobuf/proto"
roomeventsv1 "hyapp/api/proto/events/room/v1"
"hyapp/pkg/tencentim"
)
// TencentIMPublisher 是 room-service 到腾讯云 IM 的生产路径发布器。
type TencentIMPublisher struct {
// client 持有腾讯云 IM REST API、UserSig 和管理员账号配置。
client *tencentim.RESTClient
}
// NewTencentIMPublisher 创建腾讯云 IM 房间消息发布器。
func NewTencentIMPublisher(client *tencentim.RESTClient) *TencentIMPublisher {
return &TencentIMPublisher{client: client}
}
// EnsureRoomGroup 确保每个 Room Cell 都有对应腾讯云 IM 群组。
func (p *TencentIMPublisher) EnsureRoomGroup(ctx context.Context, roomID string, ownerUserID int64) error {
if p == nil || p.client == nil {
// 未启用腾讯云 IM 时保持无副作用,便于本地测试只跑 room-service。
return nil
}
return p.client.EnsureRoomGroup(ctx, roomID, ownerUserID)
}
// PublishRoomEvent 把房间系统事件发送到腾讯云 IM 群。
func (p *TencentIMPublisher) PublishRoomEvent(ctx context.Context, event tencentim.RoomEvent) error {
if p == nil || p.client == nil {
// 未启用腾讯云 IM 时不阻断房间状态提交。
return nil
}
return p.client.PublishRoomEvent(ctx, event)
}
// PublishOutboxEvent 补偿投递 room_outbox 中可转成房间系统消息的事件。
func (p *TencentIMPublisher) PublishOutboxEvent(ctx context.Context, envelope *roomeventsv1.EventEnvelope) error {
if p == nil || p.client == nil || envelope == nil {
return nil
}
event, publish, err := roomEventFromEnvelope(envelope)
if err != nil || !publish {
return err
}
return p.client.PublishRoomEvent(ctx, event)
}
// noopRoomEventPublisher 是本地和测试路径的房间消息发布占位实现。
type noopRoomEventPublisher struct{}
// NewNoopRoomEventPublisher 返回无外部副作用的房间消息发布器。
func NewNoopRoomEventPublisher() RoomEventPublisher {
return noopRoomEventPublisher{}
}
// EnsureRoomGroup 在 noop 实现中不做外部调用。
func (noopRoomEventPublisher) EnsureRoomGroup(context.Context, string, int64) error {
return nil
}
// PublishRoomEvent 在 noop 实现中不做外部调用。
func (noopRoomEventPublisher) PublishRoomEvent(context.Context, tencentim.RoomEvent) error {
return nil
}
// noopOutboxPublisher 是首版异步 outbox 消费占位实现。
type noopOutboxPublisher struct{}
// NewNoopOutboxPublisher 返回一个占位 outbox 发布器。
// 当前版本保留 outbox 表和 worker 语义activity/audit 投递后续替换该实现。
func NewNoopOutboxPublisher() OutboxPublisher {
return noopOutboxPublisher{}
}
// PublishOutboxEvent 直接吞掉 outbox 事件。
func (noopOutboxPublisher) PublishOutboxEvent(context.Context, *roomeventsv1.EventEnvelope) error {
// 腾讯云 IM 同步广播不依赖该 outboxactivity/audit 仍会复用同一事件源。
return nil
}
func roomEventFromEnvelope(envelope *roomeventsv1.EventEnvelope) (tencentim.RoomEvent, bool, error) {
base := tencentim.RoomEvent{
EventID: envelope.GetEventId(),
RoomID: envelope.GetRoomId(),
EventType: eventTypeForClient(envelope.GetEventType()),
RoomVersion: envelope.GetRoomVersion(),
}
switch envelope.GetEventType() {
case "RoomUserJoined":
var body roomeventsv1.RoomUserJoined
if err := proto.Unmarshal(envelope.GetBody(), &body); err != nil {
return tencentim.RoomEvent{}, false, err
}
base.ActorUserID = body.GetUserId()
base.TargetUserID = body.GetUserId()
base.Attributes = map[string]string{"role": body.GetRole()}
return base, true, nil
case "RoomUserLeft":
var body roomeventsv1.RoomUserLeft
if err := proto.Unmarshal(envelope.GetBody(), &body); err != nil {
return tencentim.RoomEvent{}, false, err
}
base.ActorUserID = body.GetUserId()
base.TargetUserID = body.GetUserId()
return base, true, nil
case "RoomMicChanged":
var body roomeventsv1.RoomMicChanged
if err := proto.Unmarshal(envelope.GetBody(), &body); err != nil {
return tencentim.RoomEvent{}, false, err
}
base.ActorUserID = body.GetActorUserId()
base.TargetUserID = body.GetTargetUserId()
base.SeatNo = body.GetToSeat()
if base.SeatNo == 0 {
base.SeatNo = body.GetFromSeat()
}
base.Attributes = map[string]string{"action": body.GetAction()}
return base, true, nil
case "RoomUserMuted":
var body roomeventsv1.RoomUserMuted
if err := proto.Unmarshal(envelope.GetBody(), &body); err != nil {
return tencentim.RoomEvent{}, false, err
}
base.ActorUserID = body.GetActorUserId()
base.TargetUserID = body.GetTargetUserId()
base.Attributes = map[string]string{"muted": fmt.Sprintf("%t", body.GetMuted())}
return base, true, nil
case "RoomUserKicked":
var body roomeventsv1.RoomUserKicked
if err := proto.Unmarshal(envelope.GetBody(), &body); err != nil {
return tencentim.RoomEvent{}, false, err
}
base.ActorUserID = body.GetActorUserId()
base.TargetUserID = body.GetTargetUserId()
return base, true, nil
case "RoomGiftSent":
var body roomeventsv1.RoomGiftSent
if err := proto.Unmarshal(envelope.GetBody(), &body); err != nil {
return tencentim.RoomEvent{}, false, err
}
base.ActorUserID = body.GetSenderUserId()
base.TargetUserID = body.GetTargetUserId()
base.GiftValue = body.GetGiftValue()
base.Attributes = map[string]string{
"gift_id": body.GetGiftId(),
"gift_count": fmt.Sprintf("%d", body.GetGiftCount()),
"billing_receipt_id": body.GetBillingReceiptId(),
}
return base, true, nil
default:
// RoomCreated/HeatChanged/RankChanged 不单独推客户端,避免一条命令产生多条系统消息。
return tencentim.RoomEvent{}, false, nil
}
}
func eventTypeForClient(eventType string) string {
switch eventType {
case "RoomUserJoined":
return "room_user_joined"
case "RoomUserLeft":
return "room_user_left"
case "RoomMicChanged":
return "room_mic_changed"
case "RoomUserMuted":
return "room_user_muted"
case "RoomUserKicked":
return "room_user_kicked"
case "RoomGiftSent":
return "room_gift_sent"
default:
return eventType
}
}