131 lines
3.4 KiB
Go
131 lines
3.4 KiB
Go
package live
|
|
|
|
import (
|
|
"context"
|
|
"sort"
|
|
"sync"
|
|
"time"
|
|
|
|
bizlive "kra/internal/biz/live"
|
|
)
|
|
|
|
type streamState struct {
|
|
stream *bizlive.Stream
|
|
users map[uint]struct{}
|
|
}
|
|
|
|
type streamRepo struct {
|
|
mu sync.RWMutex
|
|
streams map[string]*streamState
|
|
}
|
|
|
|
func NewStreamRepo() bizlive.StreamRepo {
|
|
return &streamRepo{streams: make(map[string]*streamState)}
|
|
}
|
|
|
|
func (r *streamRepo) Get(_ context.Context, streamID string) (*bizlive.Stream, error) {
|
|
r.mu.RLock()
|
|
defer r.mu.RUnlock()
|
|
state := r.streams[streamID]
|
|
if state == nil {
|
|
return &bizlive.Stream{StreamID: streamID, UpdatedAt: time.Now()}, nil
|
|
}
|
|
return clone(state.stream), nil
|
|
}
|
|
|
|
func (r *streamRepo) List(_ context.Context) ([]*bizlive.Stream, error) {
|
|
r.mu.RLock()
|
|
defer r.mu.RUnlock()
|
|
items := make([]*bizlive.Stream, 0, len(r.streams))
|
|
for _, state := range r.streams {
|
|
if state.stream.PublisherOnline {
|
|
items = append(items, clone(state.stream))
|
|
}
|
|
}
|
|
sort.Slice(items, func(i, j int) bool { return items[i].StreamID < items[j].StreamID })
|
|
return items, nil
|
|
}
|
|
|
|
func (r *streamRepo) RegisterPublisher(_ context.Context, streamID string, userID uint) (*bizlive.Stream, error) {
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
state := r.streams[streamID]
|
|
if state == nil {
|
|
if len(r.streams) >= bizlive.MaxStreams {
|
|
return nil, bizlive.ErrMaxStreams
|
|
}
|
|
state = &streamState{stream: &bizlive.Stream{StreamID: streamID}, users: make(map[uint]struct{})}
|
|
r.streams[streamID] = state
|
|
}
|
|
if state.stream.PublisherOnline {
|
|
return nil, bizlive.ErrPublisherExists
|
|
}
|
|
state.stream.PublisherID = userID
|
|
state.stream.PublisherOnline = true
|
|
state.stream.UpdatedAt = time.Now()
|
|
return clone(state.stream), nil
|
|
}
|
|
|
|
func (r *streamRepo) UnregisterPublisher(_ context.Context, streamID string, userID uint) error {
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
state := r.streams[streamID]
|
|
if state == nil || !state.stream.PublisherOnline || state.stream.PublisherID != userID {
|
|
return bizlive.ErrPublisherNotFound
|
|
}
|
|
state.stream.PublisherID = 0
|
|
state.stream.PublisherOnline = false
|
|
state.stream.UpdatedAt = time.Now()
|
|
if len(state.users) == 0 {
|
|
delete(r.streams, streamID)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (r *streamRepo) RegisterSubscriber(_ context.Context, streamID string, userID uint) (*bizlive.Stream, error) {
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
state := r.streams[streamID]
|
|
if state == nil {
|
|
if len(r.streams) >= bizlive.MaxStreams {
|
|
return nil, bizlive.ErrMaxStreams
|
|
}
|
|
state = &streamState{stream: &bizlive.Stream{StreamID: streamID}, users: make(map[uint]struct{})}
|
|
r.streams[streamID] = state
|
|
}
|
|
if _, exists := state.users[userID]; exists {
|
|
return nil, bizlive.ErrSubscriberExists
|
|
}
|
|
state.users[userID] = struct{}{}
|
|
state.stream.SubscriberCount = len(state.users)
|
|
state.stream.UpdatedAt = time.Now()
|
|
return clone(state.stream), nil
|
|
}
|
|
|
|
func (r *streamRepo) UnregisterSubscriber(_ context.Context, streamID string, userID uint) error {
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
state := r.streams[streamID]
|
|
if state == nil {
|
|
return bizlive.ErrSubscriberNotFound
|
|
}
|
|
if _, exists := state.users[userID]; !exists {
|
|
return bizlive.ErrSubscriberNotFound
|
|
}
|
|
delete(state.users, userID)
|
|
state.stream.SubscriberCount = len(state.users)
|
|
state.stream.UpdatedAt = time.Now()
|
|
if !state.stream.PublisherOnline && len(state.users) == 0 {
|
|
delete(r.streams, streamID)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func clone(stream *bizlive.Stream) *bizlive.Stream {
|
|
if stream == nil {
|
|
return nil
|
|
}
|
|
value := *stream
|
|
return &value
|
|
}
|