281 lines
7.8 KiB
Go
281 lines
7.8 KiB
Go
package roomadmin
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"hyapp-admin-server/internal/integration/roomclient"
|
|
)
|
|
|
|
const (
|
|
roomPinStatusActive = "active"
|
|
roomPinStatusCancelled = "cancelled"
|
|
roomPinStatusExpired = "expired"
|
|
roomPinStatusAll = "all"
|
|
defaultRoomPinDays = int64(30)
|
|
maxRoomPinDays = int64(3650)
|
|
roomPinDayMillis = int64(24 * 60 * 60 * 1000)
|
|
)
|
|
|
|
type RoomPinRoom struct {
|
|
CoverURL string `json:"coverUrl"`
|
|
RegionName string `json:"regionName"`
|
|
RoomID string `json:"roomId"`
|
|
RoomShortID string `json:"roomShortId"`
|
|
Status string `json:"status"`
|
|
Title string `json:"title"`
|
|
VisibleRegionID int64 `json:"visibleRegionId"`
|
|
}
|
|
|
|
type RoomPin struct {
|
|
CancelledAtMS int64 `json:"cancelledAtMs"`
|
|
CancelledByAdminID uint `json:"cancelledByAdminId"`
|
|
CreatedAtMS int64 `json:"createdAtMs"`
|
|
CreatedByAdminID uint `json:"createdByAdminId"`
|
|
ExpiresAtMS int64 `json:"expiresAtMs"`
|
|
ID int64 `json:"id"`
|
|
PinnedAtMS int64 `json:"pinnedAtMs"`
|
|
RemainingMS int64 `json:"remainingMs"`
|
|
Room RoomPinRoom `json:"room"`
|
|
Status string `json:"status"`
|
|
UpdatedAtMS int64 `json:"updatedAtMs"`
|
|
User RoomOwner `json:"user"`
|
|
Weight int64 `json:"weight"`
|
|
}
|
|
|
|
func (s *Service) ListRoomPins(ctx context.Context, query roomPinListQuery) ([]RoomPin, int64, error) {
|
|
if s.roomClient == nil {
|
|
return nil, 0, fmt.Errorf("room service client is not configured")
|
|
}
|
|
query = normalizeRoomPinListQuery(query)
|
|
result, err := s.roomClient.ListRoomPins(ctx, roomclient.ListRoomPinsRequest{
|
|
Page: query.Page,
|
|
PageSize: query.PageSize,
|
|
Keyword: query.Keyword,
|
|
Status: query.Status,
|
|
RegionID: query.RegionID,
|
|
})
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
items := roomPinsFromClient(result.Pins)
|
|
if err := s.fillRoomPinDetails(ctx, items); err != nil {
|
|
return nil, 0, err
|
|
}
|
|
return items, result.Total, nil
|
|
}
|
|
|
|
func (s *Service) CreateRoomPin(ctx context.Context, req createRoomPinRequest, actorID uint) (RoomPin, error) {
|
|
if s.roomClient == nil {
|
|
return RoomPin{}, fmt.Errorf("room service client is not configured")
|
|
}
|
|
input, err := normalizeCreateRoomPinRequest(req)
|
|
if err != nil {
|
|
return RoomPin{}, err
|
|
}
|
|
pin, err := s.roomClient.CreateRoomPin(ctx, roomclient.CreateRoomPinRequest{
|
|
RoomID: input.RoomID,
|
|
Weight: input.Weight,
|
|
DurationDays: input.DurationDays,
|
|
AdminID: uint64(actorID),
|
|
})
|
|
if err != nil {
|
|
return RoomPin{}, mapRoomClientError(err)
|
|
}
|
|
item := roomPinFromClient(pin)
|
|
items := []RoomPin{item}
|
|
if err := s.fillRoomPinDetails(ctx, items); err != nil {
|
|
return RoomPin{}, err
|
|
}
|
|
return items[0], nil
|
|
}
|
|
|
|
func (s *Service) CancelRoomPin(ctx context.Context, pinID int64, actorID uint) (RoomPin, error) {
|
|
if s.roomClient == nil {
|
|
return RoomPin{}, fmt.Errorf("room service client is not configured")
|
|
}
|
|
if pinID <= 0 {
|
|
return RoomPin{}, fmt.Errorf("%w: 置顶 ID 不正确", ErrInvalidArgument)
|
|
}
|
|
pin, err := s.roomClient.CancelRoomPin(ctx, roomclient.CancelRoomPinRequest{PinID: pinID, AdminID: uint64(actorID)})
|
|
if err != nil {
|
|
return RoomPin{}, mapRoomClientError(err)
|
|
}
|
|
item := roomPinFromClient(pin)
|
|
items := []RoomPin{item}
|
|
if err := s.fillRoomPinDetails(ctx, items); err != nil {
|
|
return RoomPin{}, err
|
|
}
|
|
return items[0], nil
|
|
}
|
|
|
|
func normalizeRoomPinListQuery(query roomPinListQuery) roomPinListQuery {
|
|
if query.Page < 1 {
|
|
query.Page = 1
|
|
}
|
|
if query.PageSize < 1 {
|
|
query.PageSize = 20
|
|
}
|
|
if query.PageSize > 100 {
|
|
query.PageSize = 100
|
|
}
|
|
query.Keyword = strings.TrimSpace(query.Keyword)
|
|
query.Status = normalizeRoomPinStatus(query.Status)
|
|
if query.RegionID < 0 {
|
|
query.RegionID = 0
|
|
}
|
|
return query
|
|
}
|
|
|
|
func normalizeRoomPinStatus(status string) string {
|
|
switch strings.ToLower(strings.TrimSpace(status)) {
|
|
case "", roomPinStatusActive:
|
|
return roomPinStatusActive
|
|
case roomPinStatusExpired:
|
|
return roomPinStatusExpired
|
|
case roomPinStatusCancelled:
|
|
return roomPinStatusCancelled
|
|
case roomPinStatusAll:
|
|
return roomPinStatusAll
|
|
default:
|
|
return roomPinStatusActive
|
|
}
|
|
}
|
|
|
|
func normalizeCreateRoomPinRequest(req createRoomPinRequest) (createRoomPinRequest, error) {
|
|
req.RoomID = strings.TrimSpace(req.RoomID)
|
|
if req.RoomID == "" || len(req.RoomID) > 64 {
|
|
return req, fmt.Errorf("%w: 请选择房间", ErrInvalidArgument)
|
|
}
|
|
if req.Weight < 0 {
|
|
return req, fmt.Errorf("%w: 权重不能小于 0", ErrInvalidArgument)
|
|
}
|
|
if req.DurationDays == 0 {
|
|
req.DurationDays = defaultRoomPinDays
|
|
}
|
|
if req.DurationDays < 0 || req.DurationDays > maxRoomPinDays {
|
|
return req, fmt.Errorf("%w: 置顶时间必须在 1 到 3650 天之间", ErrInvalidArgument)
|
|
}
|
|
return req, nil
|
|
}
|
|
|
|
func roomPinsFromClient(input []roomclient.RoomPin) []RoomPin {
|
|
out := make([]RoomPin, 0, len(input))
|
|
for _, pin := range input {
|
|
out = append(out, roomPinFromClient(pin))
|
|
}
|
|
return out
|
|
}
|
|
|
|
func roomPinFromClient(input roomclient.RoomPin) RoomPin {
|
|
status := input.Room.Status
|
|
if status != "active" {
|
|
status = "closed"
|
|
}
|
|
ownerUserID := strconv.FormatInt(input.Room.OwnerUserID, 10)
|
|
remainingMS := input.ExpiresAtMS - time.Now().UTC().UnixMilli()
|
|
if remainingMS < 0 {
|
|
remainingMS = 0
|
|
}
|
|
return RoomPin{
|
|
ID: input.ID,
|
|
Weight: input.Weight,
|
|
Status: input.Status,
|
|
PinnedAtMS: input.PinnedAtMS,
|
|
ExpiresAtMS: input.ExpiresAtMS,
|
|
CancelledAtMS: input.CancelledAtMS,
|
|
CreatedByAdminID: uint(input.CreatedByAdminID),
|
|
CancelledByAdminID: uint(input.CancelledByAdminID),
|
|
CreatedAtMS: input.CreatedAtMS,
|
|
UpdatedAtMS: input.UpdatedAtMS,
|
|
RemainingMS: remainingMS,
|
|
User: RoomOwner{UserID: ownerUserID},
|
|
Room: RoomPinRoom{
|
|
RoomID: input.Room.RoomID,
|
|
RoomShortID: input.Room.RoomShortID,
|
|
VisibleRegionID: input.Room.VisibleRegionID,
|
|
Title: input.Room.Title,
|
|
CoverURL: input.Room.CoverURL,
|
|
Status: status,
|
|
},
|
|
}
|
|
}
|
|
|
|
type roomPinScanner interface {
|
|
Scan(dest ...any) error
|
|
}
|
|
|
|
func scanRoomPin(row roomPinScanner) (RoomPin, error) {
|
|
var item RoomPin
|
|
var ownerUserID int64
|
|
err := row.Scan(
|
|
&item.ID,
|
|
&item.Room.VisibleRegionID,
|
|
&item.Room.RoomID,
|
|
&item.Weight,
|
|
&item.Status,
|
|
&item.PinnedAtMS,
|
|
&item.ExpiresAtMS,
|
|
&item.CancelledAtMS,
|
|
&item.CreatedByAdminID,
|
|
&item.CancelledByAdminID,
|
|
&item.CreatedAtMS,
|
|
&item.UpdatedAtMS,
|
|
&item.Room.RoomShortID,
|
|
&item.Room.Title,
|
|
&item.Room.CoverURL,
|
|
&ownerUserID,
|
|
&item.Room.Status,
|
|
)
|
|
item.User = RoomOwner{UserID: strconv.FormatInt(ownerUserID, 10)}
|
|
item.RemainingMS = item.ExpiresAtMS - time.Now().UTC().UnixMilli()
|
|
if item.RemainingMS < 0 {
|
|
item.RemainingMS = 0
|
|
}
|
|
if item.Room.Status != "active" {
|
|
item.Room.Status = "closed"
|
|
}
|
|
return item, err
|
|
}
|
|
|
|
func (s *Service) fillRoomPinDetails(ctx context.Context, items []RoomPin) error {
|
|
if s.userDB == nil || len(items) == 0 {
|
|
return nil
|
|
}
|
|
|
|
ownerIDs := make([]int64, 0, len(items))
|
|
regionIDs := make([]int64, 0, len(items))
|
|
for _, item := range items {
|
|
if ownerID, err := strconv.ParseInt(item.User.UserID, 10, 64); err == nil && ownerID > 0 {
|
|
ownerIDs = append(ownerIDs, ownerID)
|
|
}
|
|
if item.Room.VisibleRegionID > 0 {
|
|
regionIDs = append(regionIDs, item.Room.VisibleRegionID)
|
|
}
|
|
}
|
|
|
|
owners, err := s.queryRoomOwners(ctx, uniqueInt64s(ownerIDs))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
regions, err := s.queryRegionNames(ctx, uniqueInt64s(regionIDs))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for index := range items {
|
|
if ownerID, err := strconv.ParseInt(items[index].User.UserID, 10, 64); err == nil {
|
|
if owner, ok := owners[ownerID]; ok {
|
|
items[index].User = owner
|
|
}
|
|
}
|
|
if name, ok := regions[items[index].Room.VisibleRegionID]; ok {
|
|
items[index].Room.RegionName = name
|
|
}
|
|
}
|
|
return nil
|
|
}
|