180 lines
6.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package service
import (
"context"
"errors"
"strings"
roomv1 "hyapp.local/api/proto/room/v1"
"hyapp/pkg/appcode"
"hyapp/pkg/xerr"
)
var adminSeatCandidates = []int32{10, 15, 20, 25, 30}
const (
roomPinTypeRegion = "region"
roomPinTypeGlobal = "global"
roomPinDayMillis = int64(24 * 60 * 60 * 1000)
)
func (s *Service) AdminGetRoomSeatConfig(ctx context.Context, _ *roomv1.AdminGetRoomSeatConfigRequest) (*roomv1.AdminGetRoomSeatConfigResponse, error) {
config, err := s.currentRoomSeatConfig(ctx)
if err != nil {
return nil, err
}
return &roomv1.AdminGetRoomSeatConfigResponse{Config: seatConfigToAdminProto(config), ServerTimeMs: s.clock.Now().UnixMilli()}, nil
}
func (s *Service) AdminUpdateRoomSeatConfig(ctx context.Context, req *roomv1.AdminUpdateRoomSeatConfigRequest) (*roomv1.AdminUpdateRoomSeatConfigResponse, error) {
if req == nil {
return nil, xerr.New(xerr.InvalidArgument, "room seat config is required")
}
config, err := normalizeRoomSeatConfig(RoomSeatConfig{
AppCode: appcode.FromContext(ctx),
AllowedSeatCounts: req.GetAllowedSeatCounts(),
DefaultSeatCount: req.GetDefaultSeatCount(),
UpdatedAtMS: s.clock.Now().UTC().UnixMilli(),
})
if err != nil {
return nil, xerr.New(xerr.InvalidArgument, err.Error())
}
if err := s.repository.UpsertRoomSeatConfig(ctx, config); err != nil {
return nil, err
}
return &roomv1.AdminUpdateRoomSeatConfigResponse{Config: seatConfigToAdminProto(config), ServerTimeMs: config.UpdatedAtMS}, nil
}
func (s *Service) AdminListRoomPins(ctx context.Context, req *roomv1.AdminListRoomPinsRequest) (*roomv1.AdminListRoomPinsResponse, error) {
nowMS := s.clock.Now().UTC().UnixMilli()
pins, total, err := s.repository.AdminListRoomPins(ctx, AdminRoomPinQuery{
Page: int(req.GetPage()),
PageSize: int(req.GetPageSize()),
Keyword: strings.TrimSpace(req.GetKeyword()),
Status: strings.TrimSpace(req.GetStatus()),
RegionID: req.GetVisibleRegionId(),
PinType: normalizeRoomPinType(req.GetPinType()),
NowMS: nowMS,
})
if err != nil {
return nil, err
}
out := make([]*roomv1.AdminRoomPin, 0, len(pins))
for _, pin := range pins {
if pin.Status == "active" && pin.ExpiresAtMS <= nowMS {
// 过期不需要回写房间状态;对后台读模型呈现 expired公共列表查询也会自动把它排除出置顶 join。
pin.Status = "expired"
}
out = append(out, roomPinToAdminProto(pin))
}
return &roomv1.AdminListRoomPinsResponse{Pins: out, Total: total, ServerTimeMs: nowMS}, nil
}
func (s *Service) AdminCreateRoomPin(ctx context.Context, req *roomv1.AdminCreateRoomPinRequest) (*roomv1.AdminCreateRoomPinResponse, error) {
if req == nil || strings.TrimSpace(req.GetRoomId()) == "" {
return nil, xerr.New(xerr.InvalidArgument, "room id is required")
}
if req.GetWeight() < 0 {
return nil, xerr.New(xerr.InvalidArgument, "room pin weight is invalid")
}
nowMS := s.clock.Now().UTC().UnixMilli()
pinnedAtMS, expiresAtMS, err := normalizeAdminRoomPinInterval(req.GetPinnedAtMs(), req.GetExpiresAtMs(), req.GetDurationDays(), nowMS)
if err != nil {
return nil, xerr.New(xerr.InvalidArgument, err.Error())
}
pin, ok, err := s.repository.AdminCreateRoomPin(ctx, AdminCreateRoomPinInput{
RoomID: strings.TrimSpace(req.GetRoomId()),
PinType: normalizeRoomPinType(req.GetPinType()),
Weight: req.GetWeight(),
DurationDays: req.GetDurationDays(),
PinnedAtMS: pinnedAtMS,
ExpiresAtMS: expiresAtMS,
AdminID: req.GetAdminId(),
NowMS: nowMS,
})
if err != nil {
return nil, err
}
if !ok {
return nil, xerr.New(xerr.Conflict, "room is not active or not found")
}
return &roomv1.AdminCreateRoomPinResponse{Pin: roomPinToAdminProto(pin), ServerTimeMs: nowMS}, nil
}
func (s *Service) AdminCancelRoomPin(ctx context.Context, req *roomv1.AdminCancelRoomPinRequest) (*roomv1.AdminCancelRoomPinResponse, error) {
if req == nil || req.GetPinId() <= 0 {
return nil, xerr.New(xerr.InvalidArgument, "room pin id is invalid")
}
nowMS := s.clock.Now().UTC().UnixMilli()
pin, ok, err := s.repository.AdminCancelRoomPin(ctx, req.GetPinId(), req.GetAdminId(), nowMS)
if err != nil {
return nil, err
}
if !ok {
return nil, xerr.New(xerr.NotFound, "room pin not found")
}
return &roomv1.AdminCancelRoomPinResponse{Pin: roomPinToAdminProto(pin), ServerTimeMs: nowMS}, nil
}
func seatConfigToAdminProto(config RoomSeatConfig) *roomv1.AdminRoomSeatConfig {
return &roomv1.AdminRoomSeatConfig{
CandidateSeatCounts: append([]int32(nil), adminSeatCandidates...),
AllowedSeatCounts: append([]int32(nil), config.AllowedSeatCounts...),
DefaultSeatCount: config.DefaultSeatCount,
UpdatedAtMs: config.UpdatedAtMS,
}
}
func roomPinToAdminProto(pin AdminRoomPinEntry) *roomv1.AdminRoomPin {
return &roomv1.AdminRoomPin{
Id: pin.ID,
VisibleRegionId: pin.VisibleRegionID,
RoomId: pin.RoomID,
Weight: pin.Weight,
Status: pin.Status,
PinType: pin.PinType,
PinnedAtMs: pin.PinnedAtMS,
ExpiresAtMs: pin.ExpiresAtMS,
CancelledAtMs: pin.CancelledAtMS,
CreatedByAdminId: pin.CreatedByAdminID,
CancelledByAdminId: pin.CancelledByAdminID,
CreatedAtMs: pin.CreatedAtMS,
UpdatedAtMs: pin.UpdatedAtMS,
Room: &roomv1.AdminRoomPinRoom{
RoomId: pin.RoomID,
RoomShortId: pin.RoomShortID,
VisibleRegionId: pin.RoomVisibleRegionID,
OwnerUserId: pin.OwnerUserID,
Title: pin.Title,
CoverUrl: pin.CoverURL,
Status: pin.RoomStatus,
},
}
}
func normalizeRoomPinType(pinType string) string {
switch strings.ToLower(strings.TrimSpace(pinType)) {
case roomPinTypeGlobal:
return roomPinTypeGlobal
default:
// 空值和未知值都回落到区域置顶,兼容旧后台只传 room_id/duration_days 的请求。
return roomPinTypeRegion
}
}
func normalizeAdminRoomPinInterval(pinnedAtMS int64, expiresAtMS int64, durationDays int64, nowMS int64) (int64, int64, error) {
if pinnedAtMS <= 0 {
pinnedAtMS = nowMS
}
if expiresAtMS <= 0 && durationDays > 0 {
expiresAtMS = pinnedAtMS + durationDays*roomPinDayMillis
}
if expiresAtMS <= pinnedAtMS {
return 0, 0, errors.New("room pin time range is invalid")
}
if expiresAtMS-pinnedAtMS > 3650*roomPinDayMillis {
return 0, 0, errors.New("room pin time range is too long")
}
return pinnedAtMS, expiresAtMS, nil
}