180 lines
6.3 KiB
Go
180 lines
6.3 KiB
Go
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
|
||
}
|