66 lines
2.6 KiB
Go

// Package roomtime runs room presence outbox consumers and exposes in-room duration compensation.
package roomtime
import (
"context"
"time"
"hyapp/pkg/xerr"
roomtimedomain "hyapp/services/user-service/internal/domain/roomtime"
)
// Repository applies room presence events and compensates abnormal open sessions.
type Repository interface {
ProcessRoomPresenceEvent(ctx context.Context, event roomtimedomain.RoomPresenceEvent) (roomtimedomain.ApplyResult, error)
CompensateOpenSessions(ctx context.Context, options roomtimedomain.CompensationOptions) (roomtimedomain.CompensationResult, error)
}
// Service owns the user in-room duration read model.
type Service struct {
repository Repository
}
// CompensationWorkerOptions controls abnormal open session closure.
type CompensationWorkerOptions struct {
WorkerID string
BatchSize int
OpenSessionMaxAge time.Duration
}
// New creates a room time service with the repository that owns idempotent application.
func New(repository Repository) *Service {
return &Service{repository: repository}
}
// ConsumeRoomPresenceEvent applies one MQ-delivered RoomUserJoined/RoomUserLeft/RoomUserKicked fact.
func (s *Service) ConsumeRoomPresenceEvent(ctx context.Context, event roomtimedomain.RoomPresenceEvent) (roomtimedomain.ApplyResult, error) {
if s == nil || s.repository == nil {
return roomtimedomain.ApplyResult{}, xerr.New(xerr.Unavailable, "room time repository is not configured")
}
switch event.EventType {
case roomtimedomain.RoomUserJoinedEventType, roomtimedomain.RoomUserLeftEventType, roomtimedomain.RoomUserKickedEventType:
default:
return roomtimedomain.ApplyResult{Skipped: true, Reason: "unsupported_event_type"}, nil
}
return s.repository.ProcessRoomPresenceEvent(ctx, event)
}
// ProcessOpenSessionCompensationBatch exposes one bounded compensation scan for cron-service.
func (s *Service) ProcessOpenSessionCompensationBatch(ctx context.Context, options CompensationWorkerOptions) (roomtimedomain.CompensationResult, error) {
if s == nil || s.repository == nil {
return roomtimedomain.CompensationResult{}, xerr.New(xerr.Unavailable, "room time repository is not configured")
}
if options.BatchSize <= 0 {
options.BatchSize = 200
}
if options.OpenSessionMaxAge <= 0 {
// stale worker 2 分钟内会把断线用户判离房;这里只兜 room-service 节点崩溃或 outbox 丢失的极端场景。
options.OpenSessionMaxAge = 24 * time.Hour
}
return s.repository.CompensateOpenSessions(ctx, roomtimedomain.CompensationOptions{
NowMs: time.Now().UnixMilli(),
BatchSize: options.BatchSize,
OpenSessionMaxAgeMs: options.OpenSessionMaxAge.Milliseconds(),
})
}