150 lines
4.2 KiB
Go
150 lines
4.2 KiB
Go
package livechat
|
|
|
|
import (
|
|
"context"
|
|
"sync"
|
|
"time"
|
|
|
|
"gorm.io/gorm"
|
|
|
|
biz "kra/internal/biz/livechat"
|
|
)
|
|
|
|
type Provider interface{ DB() *gorm.DB }
|
|
|
|
type repo struct {
|
|
data Provider
|
|
mu sync.RWMutex
|
|
nextID uint
|
|
rooms map[uint][]*biz.Message
|
|
}
|
|
|
|
func NewRepo(data Provider) biz.Repo { return &repo{data: data, rooms: make(map[uint][]*biz.Message)} }
|
|
|
|
type messagePO struct {
|
|
ID uint `gorm:"primaryKey"`
|
|
RoomID uint `gorm:"index"`
|
|
Type string `gorm:"size:20;index"`
|
|
UserID uint `gorm:"index"`
|
|
Username string `gorm:"size:120"`
|
|
Nickname string `gorm:"size:120"`
|
|
Avatar string `gorm:"size:1000"`
|
|
Content string `gorm:"size:255"`
|
|
GiftID uint
|
|
GiftName string `gorm:"size:60"`
|
|
GiftIcon string `gorm:"size:1000"`
|
|
Quantity int
|
|
DiamondTotal int64
|
|
CreatedAt time.Time `gorm:"index"`
|
|
}
|
|
|
|
func (messagePO) TableName() string { return "live_chat_messages" }
|
|
|
|
type userSnapshot struct {
|
|
ID uint
|
|
Username string
|
|
Nickname string
|
|
Avatar string
|
|
}
|
|
|
|
func (r *repo) ListRecent(ctx context.Context, roomID uint, limit int) ([]*biz.Message, error) {
|
|
if err := r.ensureRoom(ctx, roomID); err != nil {
|
|
return nil, err
|
|
}
|
|
r.mu.RLock()
|
|
items := r.rooms[roomID]
|
|
if len(items) > limit {
|
|
items = items[len(items)-limit:]
|
|
}
|
|
out := make([]*biz.Message, 0, len(items))
|
|
for _, item := range items {
|
|
out = append(out, cloneMessage(item))
|
|
}
|
|
r.mu.RUnlock()
|
|
return out, nil
|
|
}
|
|
|
|
func (r *repo) CreateChat(ctx context.Context, roomID, userID uint, content string) (*biz.Message, error) {
|
|
if err := r.ensureRoom(ctx, roomID); err != nil {
|
|
return nil, err
|
|
}
|
|
user, err := r.user(ctx, userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return r.append(&biz.Message{RoomID: roomID, Type: biz.TypeChat, UserID: user.ID, Username: user.Username, Nickname: user.Nickname, Avatar: user.Avatar, Content: content}), nil
|
|
}
|
|
|
|
func (r *repo) CreateGift(ctx context.Context, gift *biz.GiftSnapshot) (*biz.Message, error) {
|
|
if err := r.ensureRoom(ctx, gift.RoomID); err != nil {
|
|
return nil, err
|
|
}
|
|
user, err := r.user(ctx, gift.UserID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
username, nickname := gift.Username, gift.Nickname
|
|
if username == "" {
|
|
username = user.Username
|
|
}
|
|
if nickname == "" {
|
|
nickname = user.Nickname
|
|
}
|
|
return r.append(&biz.Message{RoomID: gift.RoomID, Type: biz.TypeGift, UserID: gift.UserID, Username: username, Nickname: nickname, Avatar: user.Avatar, GiftID: gift.GiftID, GiftName: gift.GiftName, GiftIcon: gift.GiftIcon, Quantity: gift.Quantity, DiamondTotal: gift.DiamondTotal}), nil
|
|
}
|
|
|
|
func (r *repo) append(item *biz.Message) *biz.Message {
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
r.nextID++
|
|
item.ID = r.nextID
|
|
item.CreatedAt = time.Now()
|
|
items := append(r.rooms[item.RoomID], cloneMessage(item))
|
|
if len(items) > 100 {
|
|
items = append([]*biz.Message(nil), items[len(items)-100:]...)
|
|
}
|
|
r.rooms[item.RoomID] = items
|
|
return cloneMessage(item)
|
|
}
|
|
|
|
func (r *repo) Clear(_ context.Context, roomID uint) error {
|
|
r.mu.Lock()
|
|
delete(r.rooms, roomID)
|
|
r.mu.Unlock()
|
|
return nil
|
|
}
|
|
|
|
func (r *repo) ensureRoom(ctx context.Context, roomID uint) error {
|
|
var count int64
|
|
if err := r.data.DB().WithContext(ctx).Table("live_rooms").Where("id = ? AND deleted_at IS NULL AND enabled = ?", roomID, true).Count(&count).Error; err != nil {
|
|
return err
|
|
}
|
|
if count == 0 {
|
|
return biz.ErrRoomNotFound
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (r *repo) user(ctx context.Context, userID uint) (*userSnapshot, error) {
|
|
var row userSnapshot
|
|
if err := r.data.DB().WithContext(ctx).Table("sys_users").Select("id, username, nick_name AS nickname, header_img AS avatar").Where("id = ? AND deleted_at IS NULL AND enable = ?", userID, 1).Scan(&row).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
if row.ID == 0 {
|
|
return nil, biz.ErrUserNotFound
|
|
}
|
|
return &row, nil
|
|
}
|
|
|
|
func toBiz(row *messagePO) *biz.Message {
|
|
return &biz.Message{ID: row.ID, RoomID: row.RoomID, Type: row.Type, UserID: row.UserID, Username: row.Username, Nickname: row.Nickname, Avatar: row.Avatar, Content: row.Content, GiftID: row.GiftID, GiftName: row.GiftName, GiftIcon: row.GiftIcon, Quantity: row.Quantity, DiamondTotal: row.DiamondTotal, CreatedAt: row.CreatedAt}
|
|
}
|
|
|
|
func cloneMessage(item *biz.Message) *biz.Message {
|
|
if item == nil {
|
|
return nil
|
|
}
|
|
copy := *item
|
|
return ©
|
|
}
|