217 lines
7.9 KiB
Go
217 lines
7.9 KiB
Go
// Package mysql 实现 room-service 的 MySQL 持久化边界。
|
||
package mysql
|
||
|
||
import (
|
||
"context"
|
||
"database/sql"
|
||
"errors"
|
||
"strings"
|
||
"time"
|
||
|
||
"hyapp/pkg/appcode"
|
||
roomservice "hyapp/services/room-service/internal/room/service"
|
||
)
|
||
|
||
// SaveRoomMeta 插入房间元数据。
|
||
func (r *Repository) SaveRoomMeta(ctx context.Context, meta roomservice.RoomMeta) error {
|
||
// rooms 表只保存恢复初始状态需要的元数据,实时态以 snapshot/command log 为准。
|
||
appCode := normalizedRecordAppCode(ctx, meta.AppCode)
|
||
nowMS := time.Now().UTC().UnixMilli()
|
||
_, err := r.db.ExecContext(ctx,
|
||
`INSERT INTO rooms (app_code, room_id, room_short_id, owner_user_id, seat_count, mode, status, room_password_hash, visible_region_id, created_at_ms, updated_at_ms)
|
||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||
appCode,
|
||
meta.RoomID,
|
||
meta.RoomShortID,
|
||
meta.OwnerUserID,
|
||
meta.SeatCount,
|
||
meta.Mode,
|
||
meta.Status,
|
||
meta.RoomPasswordHash,
|
||
meta.VisibleRegionID,
|
||
nowMS,
|
||
nowMS,
|
||
)
|
||
|
||
return mapRoomMetaDuplicateError(err)
|
||
}
|
||
|
||
// GetRoomMeta 查询房间元数据。
|
||
func (r *Repository) GetRoomMeta(ctx context.Context, roomID string) (roomservice.RoomMeta, bool, error) {
|
||
// meta 是无内存 Cell 恢复的入口;不存在时上层返回 room not found。
|
||
row := r.db.QueryRowContext(ctx,
|
||
`SELECT app_code, room_id, room_short_id, owner_user_id, seat_count, mode, status, room_password_hash, visible_region_id
|
||
FROM rooms
|
||
WHERE app_code = ? AND room_id = ?`,
|
||
appcode.FromContext(ctx),
|
||
roomID,
|
||
)
|
||
|
||
var meta roomservice.RoomMeta
|
||
if err := row.Scan(&meta.AppCode, &meta.RoomID, &meta.RoomShortID, &meta.OwnerUserID, &meta.SeatCount, &meta.Mode, &meta.Status, &meta.RoomPasswordHash, &meta.VisibleRegionID); err != nil {
|
||
if errors.Is(err, sql.ErrNoRows) {
|
||
// 未创建房间不是数据库错误,用 exists=false 表达。
|
||
return roomservice.RoomMeta{}, false, nil
|
||
}
|
||
|
||
return roomservice.RoomMeta{}, false, err
|
||
}
|
||
|
||
return meta, true, nil
|
||
}
|
||
|
||
// GetRoomMetaByGlobalID 从全局 room_id 解析房间所属 App。
|
||
// 该查询只允许可信内部回调使用;uk_rooms_room_id 同时保证查询命中索引并阻止跨 App 歧义。
|
||
func (r *Repository) GetRoomMetaByGlobalID(ctx context.Context, roomID string) (roomservice.RoomMeta, bool, error) {
|
||
row := r.db.QueryRowContext(ctx,
|
||
`SELECT app_code, room_id, room_short_id, owner_user_id, seat_count, mode, status, room_password_hash, visible_region_id
|
||
FROM rooms
|
||
WHERE room_id = ?
|
||
LIMIT 1`,
|
||
strings.TrimSpace(roomID),
|
||
)
|
||
|
||
var meta roomservice.RoomMeta
|
||
if err := row.Scan(&meta.AppCode, &meta.RoomID, &meta.RoomShortID, &meta.OwnerUserID, &meta.SeatCount, &meta.Mode, &meta.Status, &meta.RoomPasswordHash, &meta.VisibleRegionID); err != nil {
|
||
if errors.Is(err, sql.ErrNoRows) {
|
||
return roomservice.RoomMeta{}, false, nil
|
||
}
|
||
return roomservice.RoomMeta{}, false, err
|
||
}
|
||
return meta, true, nil
|
||
}
|
||
|
||
// GetRoomMetaByShortID 通过展示短房号解析内部房间 ID。
|
||
func (r *Repository) GetRoomMetaByShortID(ctx context.Context, roomShortID string) (roomservice.RoomMeta, bool, error) {
|
||
// rooms.uk_rooms_short_id 保证同 app 下短房号唯一;这里只做解析,不改变 GetRoomMeta 的精确 room_id 语义。
|
||
row := r.db.QueryRowContext(ctx,
|
||
`SELECT app_code, room_id, room_short_id, owner_user_id, seat_count, mode, status, room_password_hash, visible_region_id
|
||
FROM rooms
|
||
WHERE app_code = ? AND room_short_id = ?
|
||
LIMIT 1`,
|
||
appcode.FromContext(ctx),
|
||
strings.TrimSpace(roomShortID),
|
||
)
|
||
|
||
var meta roomservice.RoomMeta
|
||
if err := row.Scan(&meta.AppCode, &meta.RoomID, &meta.RoomShortID, &meta.OwnerUserID, &meta.SeatCount, &meta.Mode, &meta.Status, &meta.RoomPasswordHash, &meta.VisibleRegionID); err != nil {
|
||
if errors.Is(err, sql.ErrNoRows) {
|
||
return roomservice.RoomMeta{}, false, nil
|
||
}
|
||
|
||
return roomservice.RoomMeta{}, false, err
|
||
}
|
||
|
||
return meta, true, nil
|
||
}
|
||
|
||
// GetRoomMetaByOwner 查询用户已经创建的房间。
|
||
func (r *Repository) GetRoomMetaByOwner(ctx context.Context, ownerUserID int64) (roomservice.RoomMeta, bool, error) {
|
||
// rooms.owner_user_id 有唯一约束,查询最多返回一条;该读路径用于 CreateRoom 前置失败。
|
||
row := r.db.QueryRowContext(ctx,
|
||
`SELECT app_code, room_id, room_short_id, owner_user_id, seat_count, mode, status, room_password_hash, visible_region_id
|
||
FROM rooms
|
||
WHERE app_code = ? AND owner_user_id = ?
|
||
LIMIT 1`,
|
||
appcode.FromContext(ctx),
|
||
ownerUserID,
|
||
)
|
||
|
||
var meta roomservice.RoomMeta
|
||
if err := row.Scan(&meta.AppCode, &meta.RoomID, &meta.RoomShortID, &meta.OwnerUserID, &meta.SeatCount, &meta.Mode, &meta.Status, &meta.RoomPasswordHash, &meta.VisibleRegionID); err != nil {
|
||
if errors.Is(err, sql.ErrNoRows) {
|
||
return roomservice.RoomMeta{}, false, nil
|
||
}
|
||
|
||
return roomservice.RoomMeta{}, false, err
|
||
}
|
||
|
||
return meta, true, nil
|
||
}
|
||
|
||
// SaveRoomBackground 保存房间背景图素材;相同房间相同 URL 按幂等返回原记录。
|
||
func (r *Repository) SaveRoomBackground(ctx context.Context, background roomservice.RoomBackgroundImage) (roomservice.RoomBackgroundImage, error) {
|
||
appCode := normalizedRecordAppCode(ctx, background.AppCode)
|
||
roomID := strings.TrimSpace(background.RoomID)
|
||
imageURL := strings.TrimSpace(background.ImageURL)
|
||
nowMS := background.CreatedAtMS
|
||
if nowMS <= 0 {
|
||
nowMS = time.Now().UTC().UnixMilli()
|
||
}
|
||
|
||
_, err := r.db.ExecContext(ctx, `
|
||
INSERT INTO room_background_images (
|
||
app_code, room_id, image_url, created_by_user_id, created_at_ms, updated_at_ms
|
||
) VALUES (?, ?, ?, ?, ?, ?)
|
||
ON DUPLICATE KEY UPDATE updated_at_ms = VALUES(updated_at_ms)
|
||
`, appCode, roomID, imageURL, background.CreatedByUserID, nowMS, nowMS)
|
||
if err != nil {
|
||
return roomservice.RoomBackgroundImage{}, err
|
||
}
|
||
|
||
row := r.db.QueryRowContext(ctx, `
|
||
SELECT app_code, background_id, room_id, image_url, created_by_user_id, created_at_ms, updated_at_ms
|
||
FROM room_background_images
|
||
WHERE app_code = ? AND room_id = ? AND image_url = ?
|
||
LIMIT 1
|
||
`, appCode, roomID, imageURL)
|
||
return scanRoomBackground(row)
|
||
}
|
||
|
||
// GetRoomBackground 精确读取一个房间背景图素材。
|
||
func (r *Repository) GetRoomBackground(ctx context.Context, roomID string, backgroundID int64) (roomservice.RoomBackgroundImage, bool, error) {
|
||
row := r.db.QueryRowContext(ctx, `
|
||
SELECT app_code, background_id, room_id, image_url, created_by_user_id, created_at_ms, updated_at_ms
|
||
FROM room_background_images
|
||
WHERE app_code = ? AND room_id = ? AND background_id = ?
|
||
LIMIT 1
|
||
`, appcode.FromContext(ctx), strings.TrimSpace(roomID), backgroundID)
|
||
|
||
background, err := scanRoomBackground(row)
|
||
if err != nil {
|
||
if errors.Is(err, sql.ErrNoRows) {
|
||
return roomservice.RoomBackgroundImage{}, false, nil
|
||
}
|
||
return roomservice.RoomBackgroundImage{}, false, err
|
||
}
|
||
return background, true, nil
|
||
}
|
||
|
||
// ListRoomBackgrounds 返回某个房间保存过的背景图素材,按最近保存优先。
|
||
func (r *Repository) ListRoomBackgrounds(ctx context.Context, roomID string) ([]roomservice.RoomBackgroundImage, error) {
|
||
rows, err := r.db.QueryContext(ctx, `
|
||
SELECT app_code, background_id, room_id, image_url, created_by_user_id, created_at_ms, updated_at_ms
|
||
FROM room_background_images
|
||
WHERE app_code = ? AND room_id = ?
|
||
ORDER BY created_at_ms DESC, background_id DESC
|
||
`, appcode.FromContext(ctx), strings.TrimSpace(roomID))
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
defer rows.Close()
|
||
|
||
backgrounds := []roomservice.RoomBackgroundImage{}
|
||
for rows.Next() {
|
||
background, err := scanRoomBackground(rows)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
backgrounds = append(backgrounds, background)
|
||
}
|
||
return backgrounds, rows.Err()
|
||
}
|
||
|
||
func scanRoomBackground(scanner interface{ Scan(dest ...any) error }) (roomservice.RoomBackgroundImage, error) {
|
||
var background roomservice.RoomBackgroundImage
|
||
err := scanner.Scan(
|
||
&background.AppCode,
|
||
&background.BackgroundID,
|
||
&background.RoomID,
|
||
&background.ImageURL,
|
||
&background.CreatedByUserID,
|
||
&background.CreatedAtMS,
|
||
&background.UpdatedAtMS,
|
||
)
|
||
return background, err
|
||
}
|