Skip to content

Commit fd0bc9b

Browse files
Merge pull request #92 from SWYP-mingling/feature/SW-97-guest-meeting-status
[SW-97] guest meeting status
2 parents d319b7d + f57033c commit fd0bc9b

5 files changed

Lines changed: 247 additions & 1 deletion

File tree

src/main/java/swyp/mingling/domain/meeting/controller/MeetingController.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ public class MeetingController {
3636
private final GetMeetingStatusUseCase getMeetingStatusUseCase;
3737
private final RecommendPlaceUseCase recommendPlaceUseCase;
3838
private final MidPointAsyncUseCase midPointAsyncUseCase;
39+
private final GuestMeetingStatusUseCase guestMeetingStatusUseCase;
3940

4041
/**
4142
* 모임 생성 API
@@ -177,5 +178,17 @@ public ApiResponse<String> updateDeparture(
177178
return ApiResponse.success(deletedNickname);
178179

179180
}
181+
182+
183+
@MeetingApiDocumentation.GuestStatusDoc
184+
@GetMapping("/{meetingId}/guestStatus")
185+
public ApiResponse<GuestStatusResponse> guestMeetingStatus(
186+
@PathVariable("meetingId") UUID meetingId) {
187+
188+
GuestStatusResponse execute = guestMeetingStatusUseCase.execute(meetingId);
189+
190+
return ApiResponse.success(execute);
191+
192+
}
180193
}
181194

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package swyp.mingling.domain.meeting.dto.response;
2+
3+
import io.swagger.v3.oas.annotations.media.Schema;
4+
import lombok.AllArgsConstructor;
5+
import lombok.Getter;
6+
7+
import java.time.LocalDateTime;
8+
import java.util.List;
9+
10+
/**
11+
* 모임 참여 현황 조회 응답 DTO
12+
*/
13+
@Getter
14+
@AllArgsConstructor
15+
@Schema(description = "모임 참여 현황 조회 응답")
16+
public class GuestStatusResponse {
17+
18+
@Schema(description = "모임 명", example = "K6 성능 테스트")
19+
private String meetingName;
20+
21+
@Schema(description = "모임 목적", example = "카페")
22+
private String category;
23+
24+
@Schema(description = "전체 참여자 수", example = "10")
25+
private int totalParticipantCount;
26+
27+
@Schema(description = "출발지 입력 참여자 수", example = "2")
28+
private int currentParticipantCount;
29+
30+
@Schema(description = "모임 참여자 목록")
31+
private List<ParticipantInfo> participants;
32+
33+
/**
34+
* 모임 참여자 정보
35+
*/
36+
@Getter
37+
@AllArgsConstructor
38+
@Schema(description = "모임 참여자 정보")
39+
public static class ParticipantInfo {
40+
41+
@Schema(description = "참여자 이름", example = "김밍글")
42+
private String userName;
43+
44+
public static ParticipantInfo of(
45+
String userName
46+
) {
47+
return new ParticipantInfo(userName);
48+
}
49+
50+
}
51+
52+
/**
53+
* 정적 팩토리 메서드
54+
*/
55+
public static GuestStatusResponse of(
56+
String meetingName,
57+
String category,
58+
int totalParticipantCount,
59+
int currentParticipantCount,
60+
List<ParticipantInfo> participants
61+
) {
62+
return new GuestStatusResponse(
63+
meetingName,
64+
category,
65+
totalParticipantCount,
66+
currentParticipantCount,
67+
participants);
68+
}
69+
70+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package swyp.mingling.domain.meeting.service;
2+
3+
4+
import lombok.RequiredArgsConstructor;
5+
import org.springframework.stereotype.Service;
6+
import swyp.mingling.domain.meeting.dto.response.GuestStatusResponse;
7+
import swyp.mingling.domain.meeting.entity.Meeting;
8+
import swyp.mingling.domain.meeting.repository.MeetingQueryRepository;
9+
import swyp.mingling.domain.meeting.repository.MeetingRepository;
10+
import swyp.mingling.domain.participant.entity.Participant;
11+
import swyp.mingling.global.exception.BusinessException;
12+
13+
import java.util.List;
14+
import java.util.UUID;
15+
16+
@Service
17+
@RequiredArgsConstructor
18+
public class GuestMeetingStatusUseCase {
19+
20+
private final MeetingQueryRepository meetingQueryRepository;
21+
private final MeetingRepository meetingRepository;
22+
23+
public GuestStatusResponse execute(UUID meetingId) {
24+
25+
// 1. 데이터 조회
26+
Meeting meeting = meetingQueryRepository.findMeetingStatusById(meetingId)
27+
.orElseThrow(() -> BusinessException.meetingNotFound());
28+
29+
// 2. 출발지 설정된 참여자
30+
List<Participant> confirmedParticipants = meeting.getParticipants().stream()
31+
.filter(p -> p.getDeparture() != null)
32+
.toList();
33+
34+
// 3. DTO 변환
35+
List<GuestStatusResponse.ParticipantInfo> participantInfos =
36+
confirmedParticipants.stream()
37+
.map(p -> GuestStatusResponse.ParticipantInfo.of(
38+
p.getNickname()
39+
))
40+
.toList();
41+
42+
String category = meetingRepository.findPurposeNamesByMeetingId(meetingId).orElse("식당");
43+
44+
return GuestStatusResponse.of(
45+
meeting.getName(),
46+
category,
47+
meeting.getCount(),
48+
participantInfos.size(),
49+
participantInfos
50+
);
51+
52+
}
53+
}

src/main/java/swyp/mingling/global/config/WebConfig.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ public void addInterceptors(InterceptorRegistry registry) {
2424
"/api-docs/**",
2525
"/api/status",
2626
"/actuator/health",
27-
"/meeting/result/**");
27+
"/meeting/result/**",
28+
"/meeting/**/guestStatus");
2829

2930
}
3031

src/main/java/swyp/mingling/global/documentation/MeetingApiDocumentation.java

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1834,5 +1834,114 @@ public class MeetingApiDocumentation {
18341834
})
18351835
public @interface DeleteDepartDoc {}
18361836

1837+
1838+
1839+
/**
1840+
* 비로그인일때 참여 현황 조회 API 문서
1841+
*/
1842+
@Target(ElementType.METHOD)
1843+
@Retention(RetentionPolicy.RUNTIME)
1844+
@Operation(
1845+
summary = "비로그인 모임 참여 현황 조회 API",
1846+
description = "비로그인일때 모임의 현재 참여 현황을 조회합니다. " +
1847+
"모임명, 모임 목적, 총 인원 수, 현재 참여자 수, 참여자 이름들을 반환합니다. "
1848+
)
1849+
@ApiResponses({
1850+
// SUCCESS
1851+
@ApiResponse(
1852+
responseCode = "200",
1853+
description = "비로그인 모임 참여 현황 조회 성공",
1854+
content = @Content(
1855+
mediaType = "application/json",
1856+
schema = @Schema(implementation = swyp.mingling.global.response.ApiResponse.class),
1857+
examples = @ExampleObject(
1858+
name = "SUCCESS",
1859+
description = "비로그인 모임 참여 현황 조회 성공",
1860+
value = """
1861+
{
1862+
"success": true,
1863+
"data": {
1864+
"meetingName": "K6 성능 테스트",
1865+
"category": "카페",
1866+
"totalParticipantCount": 5,
1867+
"currentParticipantCount": 4,
1868+
"participants": [
1869+
{
1870+
"userName": "테스터1"
1871+
},
1872+
{
1873+
"userName": "테스터2"
1874+
},
1875+
{
1876+
"userName": "테스터3"
1877+
},
1878+
{
1879+
"userName": "테스터4"
1880+
}
1881+
]
1882+
},
1883+
"timestamp": "2026-02-14T19:39:46"
1884+
}
1885+
"""
1886+
)
1887+
)
1888+
),
1889+
1890+
// UNAUTHORIZED
1891+
@ApiResponse(
1892+
responseCode = "401",
1893+
description = "사용자 인증 실패",
1894+
content = @Content(
1895+
mediaType = "application/json",
1896+
examples = @ExampleObject("""
1897+
{
1898+
"success": false,
1899+
"code": "USER_UNAUTHORIZED",
1900+
"message": "사용자 인증에 실패했습니다.",
1901+
"data": null,
1902+
"timestamp": "2026-01-23T23:00:00"
1903+
}
1904+
""")
1905+
)
1906+
),
1907+
1908+
// NOT_FOUND
1909+
@ApiResponse(
1910+
responseCode = "404",
1911+
description = "존재하지 않는 모임",
1912+
content = @Content(
1913+
mediaType = "application/json",
1914+
examples = @ExampleObject("""
1915+
{
1916+
"success": false,
1917+
"code": "MEETING_NOT_FOUND",
1918+
"message": "모임을 찾을 수 없습니다.",
1919+
"data": null,
1920+
"timestamp": "2026-01-23T23:00:00"
1921+
}
1922+
""")
1923+
)
1924+
),
1925+
1926+
// INTERNAL_SERVER_ERROR
1927+
@ApiResponse(
1928+
responseCode = "500",
1929+
description = "서버 내부 오류",
1930+
content = @Content(
1931+
mediaType = "application/json",
1932+
examples = @ExampleObject("""
1933+
{
1934+
"success": false,
1935+
"code": "INTERNAL_SERVER_ERROR",
1936+
"message": "서버 내부 오류가 발생했습니다.",
1937+
"data": null,
1938+
"timestamp": "2026-01-23T23:00:00"
1939+
}
1940+
""")
1941+
)
1942+
)
1943+
})
1944+
public @interface GuestStatusDoc {}
1945+
18371946
}
18381947

0 commit comments

Comments
 (0)