[BE-06] 게스트 초대 (초대·수락·거절·취소)
작업 내용 (설계 의도)
변경 사항
근거 TDD: 20260704-채팅시스템고도화-tdd.md (FR-11/12/13/14, RoomInvitation 상태 전이·초대 멱등).
방장이 특정 방에 정회원을 게스트로 초대하는 수명주기를 신설한다. 수락 시 게스트 참여자로 추가.
domain/message/entity/RoomInvitation.kt(신규): 필드 room/inviterUserId/inviteeUserId/status/canSpeak/expiresAt. 팩토리create(...). 전이 메서드accept()/reject()/revoke()/expire()— terminal 재전이 거부(InvitationStatus.canTransitTo 사용).domain/message/repository/RoomInvitationRepository.kt(+ Jpa/Impl 신규):save,findById,findPendingBy(roomId, inviteeUserId),findPendingByInvitee(inviteeUserId): List(초대 수신함).domain/message/service/GuestInvitationDomainService.kt(신규):invite(...)— 방장 검증 + 동일 (room,invitee) PENDING 존재 시 기존 반환(멱등).accept(invitationId, userId)— 수락자=invitee 검증 → 상태 전이 →RoomParticipant.forGuest추가.reject,revoke(호출자=방장).findMyPendingInvitations(userId)(수신함 조회).application/message/usecase/: InviteGuestUseCase, AcceptInvitationUseCase, RejectInvitationUseCase, ListMyInvitationsUseCase (신규).application/message/dto/InvitationResponse.kt(신규): TDD “응답 DTO 필드 스키마”의 InvitationResponse 표대로 — id/roomId/inviterUserId/inviteeUserId/status/canSpeak/expiresAt/createdAt.of(RoomInvitation)팩토리.presentation/message/controller/InvitationApiController.kt(신규):POST /rooms/{roomId}/invitations,GET /rooms/invitations/me(내 PENDING 초대 목록, FR-12),POST /rooms/invitations/{id}/accept,.../reject. 인증:@AuthenticationPrincipal UserPrincipal로 userId(Bearer JWT)./rooms/**인증 승격은 BE-12가 수행하므로 본 컨트롤러는 principal 기반으로 작성.- 게스트 발화 가드: 수락으로 추가된 게스트는
canSpeak/expiresAt을 가지며 발화 시 BE-03b의validateCanSpeak/NotExpired적용. - 롤백: 신규 테이블·엔티티라 코드 롤백만으로 안전. 초대 기능은 커뮤니티 플래그와 무관하게
/rooms하위 additive.
의존
- BE-03b (RoomParticipant.forGuest, 게스트 필드)
- BE-02 (InvitationStatus)
다이어그램
처리 흐름
sequenceDiagram participant Host participant Ctl as InvitationApiController participant DS as GuestInvitationDomainService participant Guest Host->>Ctl: POST /rooms/1/invitations Ctl->>DS: invite(roomId,invitee,canSpeak,expiresInDays) DS-->>Ctl: PENDING invitation Guest->>Ctl: POST /rooms/invitations/9/accept Ctl->>DS: accept → forGuest 참여자 추가
테스트 케이스
- 방장이 비멤버 정회원을 초대하면 PENDING 초대가 생성된다.
- 동일 (room, invitee)로 PENDING 상태에서 재초대하면 기존 초대가 반환된다 (멱등).
- 초대 수락 시 게스트가 canSpeak·expiresAt을 가진 참여자로 추가된다.
- 초대 거절 시 초대가 REJECTED가 되고 참여자로 추가되지 않는다.
- 이미 ACCEPTED된 초대를 다시 accept하면 거부된다 (terminal 보호).
- 방장이 아닌 사용자가 초대를 시도하면 거부된다 (초안: 방장만 초대 가능).
- 초대 대상이 아닌 사용자가 accept를 호출하면 거부된다.
GET /rooms/invitations/me는 내가 받은 PENDING 초대만 반환하고, 종료된(ACCEPTED/REJECTED/EXPIRED/REVOKED) 초대는 제외한다.- 인증 토큰(Bearer) 없이 초대 API 호출 시 401이 반환된다.