对接
This commit is contained in:
@@ -174,7 +174,8 @@ func (r *catalogRepo) ListRooms(ctx context.Context, filter bizlive.RoomFilter,
|
||||
Joins("LEFT JOIN live_categories ON live_categories.id = live_rooms.category_id").
|
||||
Joins("LEFT JOIN sys_users ON sys_users.id = live_rooms.anchor_user_id AND sys_users.deleted_at IS NULL")
|
||||
if filter.Name != "" {
|
||||
query = query.Where("live_rooms.name LIKE ?", "%"+filter.Name+"%")
|
||||
value := "%" + filter.Name + "%"
|
||||
query = query.Where("live_rooms.name LIKE ? OR CAST(live_rooms.id AS CHAR) LIKE ?", value, value)
|
||||
}
|
||||
if filter.AnchorName != "" {
|
||||
value := "%" + filter.AnchorName + "%"
|
||||
@@ -186,6 +187,9 @@ func (r *catalogRepo) ListRooms(ctx context.Context, filter bizlive.RoomFilter,
|
||||
if filter.Enabled != nil {
|
||||
query = query.Where("live_rooms.enabled = ?", *filter.Enabled)
|
||||
}
|
||||
if filter.CategoryEnabled != nil {
|
||||
query = query.Where("live_categories.enabled = ?", *filter.CategoryEnabled)
|
||||
}
|
||||
if filter.Status == bizlive.RoomStatusDisabled {
|
||||
query = query.Where("live_rooms.enabled = ?", false)
|
||||
} else if filter.Status == bizlive.RoomStatusOffline || filter.Status == bizlive.RoomStatusLive {
|
||||
|
||||
@@ -74,3 +74,39 @@ func TestCatalogRepoScansCategoryAndRoomFields(t *testing.T) {
|
||||
t.Fatalf("room get scan result=%+v", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCatalogRepoFiltersRoomsByCategoryEnabled(t *testing.T) {
|
||||
db := openCatalogTestDB(t)
|
||||
if err := db.Exec(`INSERT INTO sys_users(id, username, nick_name, header_img) VALUES (2, 'anchor', 'Anchor', 'avatar.png')`).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
enabledCategory := categoryPO{Name: "Games", Enabled: true}
|
||||
disabledCategory := categoryPO{Name: "Hidden", Enabled: false}
|
||||
if err := db.Create(&enabledCategory).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := db.Create(&disabledCategory).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := db.Model(&disabledCategory).Update("enabled", false).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, room := range []*roomPO{
|
||||
{Name: "Visible", CategoryID: enabledCategory.ID, CoverURL: "visible.png", AnchorUserID: 2, Enabled: true},
|
||||
{Name: "Hidden", CategoryID: disabledCategory.ID, CoverURL: "hidden.png", AnchorUserID: 2, Enabled: true},
|
||||
} {
|
||||
if err := db.Create(room).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
repo := NewCatalogRepo(catalogTestProvider{db: db})
|
||||
enabled := true
|
||||
rooms, total, err := repo.ListRooms(context.Background(), bizlive.RoomFilter{CategoryEnabled: &enabled}, bizlive.ListOptions{Limit: 10})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if total != 1 || len(rooms) != 1 || rooms[0].Name != "Visible" {
|
||||
t.Fatalf("enabled-category rooms total=%d rooms=%+v", total, rooms)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
package live
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"gorm.io/gorm"
|
||||
bizlive "kra/internal/biz/live"
|
||||
)
|
||||
|
||||
type followPO struct {
|
||||
ID uint `gorm:"primaryKey"`
|
||||
UserID uint `gorm:"not null;uniqueIndex:idx_live_room_follow_user_room"`
|
||||
RoomID uint `gorm:"not null;uniqueIndex:idx_live_room_follow_user_room"`
|
||||
AnchorUserID uint `gorm:"not null;index"`
|
||||
CreatedAt int64 `gorm:"autoCreateTime:milli"`
|
||||
}
|
||||
|
||||
func (followPO) TableName() string { return "live_room_follows" }
|
||||
|
||||
type followRepo struct{ data Provider }
|
||||
|
||||
func NewFollowRepo(data Provider) bizlive.FollowRepo { return &followRepo{data: data} }
|
||||
|
||||
func (r *followRepo) Get(ctx context.Context, userID, roomID uint) (*bizlive.Follow, error) {
|
||||
var row followPO
|
||||
err := r.db(ctx).Where("user_id = ? AND room_id = ?", userID, roomID).First(&row).Error
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, bizlive.ErrFollowNotFound
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return followToBiz(&row), nil
|
||||
}
|
||||
|
||||
func (r *followRepo) Create(ctx context.Context, item *bizlive.Follow) (*bizlive.Follow, error) {
|
||||
var room roomPO
|
||||
if err := r.db(ctx).First(&room, item.RoomID).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, bizlive.ErrRoomNotFound
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
item.AnchorUserID = room.AnchorUserID
|
||||
po := &followPO{UserID: item.UserID, RoomID: item.RoomID, AnchorUserID: item.AnchorUserID}
|
||||
if err := r.db(ctx).Create(po).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrDuplicatedKey) {
|
||||
return r.Get(ctx, item.UserID, item.RoomID)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return followToBiz(po), nil
|
||||
}
|
||||
|
||||
func (r *followRepo) Delete(ctx context.Context, userID, roomID uint) error {
|
||||
result := r.db(ctx).Where("user_id = ? AND room_id = ?", userID, roomID).Delete(&followPO{})
|
||||
if result.Error != nil {
|
||||
return result.Error
|
||||
}
|
||||
if result.RowsAffected == 0 {
|
||||
return bizlive.ErrFollowNotFound
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *followRepo) db(ctx context.Context) *gorm.DB {
|
||||
if r == nil || r.data == nil || r.data.DB() == nil {
|
||||
return nil
|
||||
}
|
||||
return r.data.DB().WithContext(ctx)
|
||||
}
|
||||
|
||||
func followToBiz(row *followPO) *bizlive.Follow {
|
||||
return &bizlive.Follow{ID: row.ID, UserID: row.UserID, RoomID: row.RoomID, AnchorUserID: row.AnchorUserID}
|
||||
}
|
||||
@@ -18,5 +18,7 @@ func Migrations() []migration.Step {
|
||||
return nil
|
||||
}
|
||||
return db.Create(&[]categoryPO{{Name: "游戏竞技", Icon: "🎮", Sort: 1, Enabled: true}, {Name: "音乐区", Icon: "🎵", Sort: 2, Enabled: true}, {Name: "闲聊电台", Icon: "🌙", Sort: 3, Enabled: true}, {Name: "舞蹈才艺", Icon: "💃", Sort: 4, Enabled: true}, {Name: "娱乐直播", Icon: "🎪", Sort: 5, Enabled: true}}).Error
|
||||
}}, {ID: "202609220002_live_follow_schema", Migrate: func(db *gorm.DB) error {
|
||||
return migration.CreateMissingTables(db, &followPO{})
|
||||
}}}
|
||||
}
|
||||
|
||||
@@ -9,5 +9,6 @@ import (
|
||||
var ProviderSet = wire.NewSet(
|
||||
NewStreamRepo,
|
||||
NewCatalogRepo,
|
||||
NewFollowRepo,
|
||||
wire.Bind(new(bizlive.CatalogRepo), new(*catalogRepo)),
|
||||
)
|
||||
|
||||
@@ -62,6 +62,11 @@ func (r *streamRepo) RegisterPublisher(_ context.Context, streamID string, userI
|
||||
}
|
||||
state.stream.PublisherID = userID
|
||||
state.stream.PublisherOnline = true
|
||||
// A new broadcast starts with a fresh in-room like counter. Keeping the
|
||||
// stream state object is useful when viewers connected before the publisher,
|
||||
// but likes must never leak into the next broadcast session.
|
||||
state.stream.LikeCount = 0
|
||||
state.stream.StartedAt = time.Now()
|
||||
state.stream.UpdatedAt = time.Now()
|
||||
return clone(state.stream), nil
|
||||
}
|
||||
@@ -75,6 +80,7 @@ func (r *streamRepo) UnregisterPublisher(_ context.Context, streamID string, use
|
||||
}
|
||||
state.stream.PublisherID = 0
|
||||
state.stream.PublisherOnline = false
|
||||
state.stream.StartedAt = time.Time{}
|
||||
state.stream.UpdatedAt = time.Now()
|
||||
if len(state.users) == 0 {
|
||||
delete(r.streams, streamID)
|
||||
@@ -121,6 +127,19 @@ func (r *streamRepo) UnregisterSubscriber(_ context.Context, streamID string, us
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *streamRepo) Like(_ context.Context, streamID string) (int, error) {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
state := r.streams[streamID]
|
||||
if state == nil {
|
||||
state = &streamState{stream: &bizlive.Stream{StreamID: streamID}, users: make(map[uint]struct{})}
|
||||
r.streams[streamID] = state
|
||||
}
|
||||
state.stream.LikeCount++
|
||||
state.stream.UpdatedAt = time.Now()
|
||||
return state.stream.LikeCount, nil
|
||||
}
|
||||
|
||||
func clone(stream *bizlive.Stream) *bizlive.Stream {
|
||||
if stream == nil {
|
||||
return nil
|
||||
|
||||
Reference in New Issue
Block a user