86 lines
2.7 KiB
Go
86 lines
2.7 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
|
|
roomv1 "hyapp.local/api/proto/room/v1"
|
|
"hyapp/pkg/roomid"
|
|
"hyapp/pkg/xerr"
|
|
)
|
|
|
|
// FollowRoom 建立当前用户对房间的关注关系。
|
|
// 关注关系是用户个性化读模型事实,不改变 Room Cell 核心状态,也不需要 Redis lease 串行化。
|
|
func (s *Service) FollowRoom(ctx context.Context, req *roomv1.FollowRoomRequest) (*roomv1.FollowRoomResponse, error) {
|
|
ctx = contextFromMeta(ctx, req.GetMeta())
|
|
roomID, userID, err := normalizeRoomFollowInput(req.GetMeta(), req.GetRoomId(), req.GetUserId())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
meta, exists, err := s.repository.GetRoomMeta(ctx, roomID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !exists {
|
|
return nil, xerr.New(xerr.NotFound, "room not found")
|
|
}
|
|
if isClosedRoomStatus(meta.Status) {
|
|
// 已关闭房间不再建立新的关注关系;历史关注仍可通过 UnfollowRoom 幂等取消。
|
|
return nil, xerr.New(xerr.RoomClosed, "room closed")
|
|
}
|
|
|
|
nowMS := s.clock.Now().UnixMilli()
|
|
record, err := s.repository.FollowRoom(ctx, userID, roomID, nowMS)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &roomv1.FollowRoomResponse{
|
|
RoomId: roomID,
|
|
Followed: true,
|
|
FollowedAtMs: record.FollowedAtMS,
|
|
ServerTimeMs: nowMS,
|
|
}, nil
|
|
}
|
|
|
|
// UnfollowRoom 取消当前用户对房间的关注关系。
|
|
// 未关注或历史房间已关闭都按幂等成功处理,方便客户端在本地状态不确定时直接收敛。
|
|
func (s *Service) UnfollowRoom(ctx context.Context, req *roomv1.UnfollowRoomRequest) (*roomv1.UnfollowRoomResponse, error) {
|
|
ctx = contextFromMeta(ctx, req.GetMeta())
|
|
roomID, userID, err := normalizeRoomFollowInput(req.GetMeta(), req.GetRoomId(), req.GetUserId())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
nowMS := s.clock.Now().UnixMilli()
|
|
if err := s.repository.UnfollowRoom(ctx, userID, roomID, nowMS); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &roomv1.UnfollowRoomResponse{
|
|
RoomId: roomID,
|
|
Followed: false,
|
|
ServerTimeMs: nowMS,
|
|
}, nil
|
|
}
|
|
|
|
func normalizeRoomFollowInput(meta *roomv1.RequestMeta, rawRoomID string, userID int64) (string, int64, error) {
|
|
roomID := strings.TrimSpace(rawRoomID)
|
|
metaRoomID := strings.TrimSpace(meta.GetRoomId())
|
|
if roomID == "" {
|
|
roomID = metaRoomID
|
|
}
|
|
if metaRoomID != "" && roomID != metaRoomID {
|
|
// meta.room_id 是链路和审计字段;与 body/path room_id 不一致时必须拒绝,避免写错关系。
|
|
return "", 0, xerr.New(xerr.InvalidArgument, "room_id is invalid")
|
|
}
|
|
if !roomid.ValidStringID(roomID) {
|
|
return "", 0, xerr.New(xerr.InvalidArgument, "room_id is invalid")
|
|
}
|
|
if userID <= 0 {
|
|
return "", 0, xerr.New(xerr.InvalidArgument, "user_id is required")
|
|
}
|
|
return roomID, userID, nil
|
|
}
|