From 57b2d0dc7a8ef440027df9ccaea85653ad688110 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=95=88=ED=9B=88=EA=B8=B0?= Date: Mon, 9 Mar 2026 17:01:53 +0900 Subject: [PATCH 1/3] =?UTF-8?q?=E2=9C=A8=20Feat:=20=EB=A7=A4=EC=B9=AD=20?= =?UTF-8?q?=EC=B0=B8=EA=B0=80=EC=9E=90=20=EB=AA=A9=EB=A1=9D=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20API=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GET /api/matches/{matchId}/participants JOINED 상태 참가자만 반환 (N+1 방지 fetch join) --- .../domain/match/controller/MatchController.java | 12 +++++++++++- .../repository/MatchParticipantRepository.java | 4 ++++ .../domain/match/service/MatchService.java | 3 +++ .../domain/match/service/MatchServiceImpl.java | 14 ++++++++++++++ 4 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/be/sportizebe/domain/match/controller/MatchController.java b/src/main/java/com/be/sportizebe/domain/match/controller/MatchController.java index efa6bbb..2846882 100644 --- a/src/main/java/com/be/sportizebe/domain/match/controller/MatchController.java +++ b/src/main/java/com/be/sportizebe/domain/match/controller/MatchController.java @@ -3,6 +3,7 @@ import com.be.sportizebe.domain.match.dto.request.MatchNearRequest; import com.be.sportizebe.domain.match.dto.response.MatchDetailResponse; import com.be.sportizebe.domain.match.dto.response.MatchNearResponse; +import com.be.sportizebe.domain.match.dto.response.MatchParticipantResponse; import com.be.sportizebe.domain.match.dto.response.MyMatchResponse; import com.be.sportizebe.domain.match.service.MatchService; import com.be.sportizebe.global.cache.dto.UserAuthInfo; @@ -75,7 +76,16 @@ public ResponseEntity>> getMyMatches( return ResponseEntity.ok(BaseResponse.success("내 매칭 목록 조회 성공", response)); } -@Operation(summary = "내 주변 매칭 목록 조회", description = "매칭상태가 OPEN인 매칭들만 보여준다.") +@Operation(summary = "매칭 참가자 목록 조회", description = "JOINED 상태인 참가자만 반환한다.") + @GetMapping("/{matchId}/participants") + public ResponseEntity>> getMatchParticipants( + @PathVariable Long matchId + ) { + List response = matchService.getMatchParticipants(matchId); + return ResponseEntity.ok(BaseResponse.success("매칭 참가자 목록 조회 성공", response)); + } + + @Operation(summary = "내 주변 매칭 목록 조회", description = "매칭상태가 OPEN인 매칭들만 보여준다.") @GetMapping("/near") public ResponseEntity>> getNearMatches( @ParameterObject @Valid @ModelAttribute MatchNearRequest request diff --git a/src/main/java/com/be/sportizebe/domain/match/repository/MatchParticipantRepository.java b/src/main/java/com/be/sportizebe/domain/match/repository/MatchParticipantRepository.java index ad7a326..52130b2 100644 --- a/src/main/java/com/be/sportizebe/domain/match/repository/MatchParticipantRepository.java +++ b/src/main/java/com/be/sportizebe/domain/match/repository/MatchParticipantRepository.java @@ -32,4 +32,8 @@ public interface MatchParticipantRepository extends JpaRepository findAllByUserIdAndStatusFetch(@Param("userId") Long userId, @Param("status") MatchParticipantStatus status); + // 매칭 참가자 목록 (N+1 방지 fetch join) + @Query("SELECT mp FROM MatchParticipant mp JOIN FETCH mp.user WHERE mp.matchRoom.id = :matchId AND mp.status = :status") + List findAllByMatchRoomIdAndStatusFetch(@Param("matchId") Long matchId, @Param("status") MatchParticipantStatus status); + } \ No newline at end of file diff --git a/src/main/java/com/be/sportizebe/domain/match/service/MatchService.java b/src/main/java/com/be/sportizebe/domain/match/service/MatchService.java index 42bd489..3ae98a6 100644 --- a/src/main/java/com/be/sportizebe/domain/match/service/MatchService.java +++ b/src/main/java/com/be/sportizebe/domain/match/service/MatchService.java @@ -4,6 +4,7 @@ import com.be.sportizebe.domain.match.dto.request.MatchNearRequest; import com.be.sportizebe.domain.match.dto.response.MatchDetailResponse; import com.be.sportizebe.domain.match.dto.response.MatchNearResponse; +import com.be.sportizebe.domain.match.dto.response.MatchParticipantResponse; import com.be.sportizebe.domain.match.dto.response.MatchResponse; import com.be.sportizebe.domain.match.dto.response.MyMatchResponse; @@ -27,4 +28,6 @@ public interface MatchService { List getMyMatches(Long userId); // 참여 중인 매칭 목록 + List getMatchParticipants(Long matchId); // 매칭 참가자 목록 + } diff --git a/src/main/java/com/be/sportizebe/domain/match/service/MatchServiceImpl.java b/src/main/java/com/be/sportizebe/domain/match/service/MatchServiceImpl.java index 28958eb..6bfd4c2 100644 --- a/src/main/java/com/be/sportizebe/domain/match/service/MatchServiceImpl.java +++ b/src/main/java/com/be/sportizebe/domain/match/service/MatchServiceImpl.java @@ -3,6 +3,7 @@ import com.be.sportizebe.domain.match.dto.request.MatchNearRequest; import com.be.sportizebe.domain.match.dto.response.MatchDetailResponse; import com.be.sportizebe.domain.match.dto.response.MatchNearResponse; +import com.be.sportizebe.domain.match.dto.response.MatchParticipantResponse; import com.be.sportizebe.domain.match.dto.response.MatchResponse; import com.be.sportizebe.domain.match.dto.response.MyMatchResponse; import com.be.sportizebe.domain.match.entity.MatchParticipant; @@ -165,6 +166,19 @@ public List getMyMatches(Long userId) { } @Override + @Transactional(readOnly = true) + public List getMatchParticipants(Long matchId) { + if (!matchRoomRepository.existsById(matchId)) { + throw new CustomException(MatchErrorCode.MATCH_NOT_FOUND); + } + return matchParticipantRepository + .findAllByMatchRoomIdAndStatusFetch(matchId, MatchParticipantStatus.JOINED) + .stream() + .map(MatchParticipantResponse::from) + .toList(); + } + + @Override @Transactional(readOnly = true) public List getNearMatches(MatchNearRequest request) { String sportsName = request.getSportsName() == null From cb8800e75111aa69e526a0d97f55da09bc031d6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=95=88=ED=9B=88=EA=B8=B0?= Date: Mon, 9 Mar 2026 17:04:37 +0900 Subject: [PATCH 2/3] =?UTF-8?q?=E2=9C=A8=20Feat:=20=EA=B5=AC=EC=9E=A5=20?= =?UTF-8?q?=EA=B8=B0=EB=B0=98=20=EB=A7=A4=EC=B9=AD=20=EB=AA=A9=EB=A1=9D=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20API=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GET /api/matches?facilityId={facilityId} OPEN/FULL 상태이며 시작 전인 매칭만 반환 --- .../domain/match/controller/MatchController.java | 11 +++++++++++ .../match/repository/MatchRoomRepository.java | 5 +++++ .../domain/match/service/MatchService.java | 2 ++ .../domain/match/service/MatchServiceImpl.java | 15 +++++++++++++++ 4 files changed, 33 insertions(+) diff --git a/src/main/java/com/be/sportizebe/domain/match/controller/MatchController.java b/src/main/java/com/be/sportizebe/domain/match/controller/MatchController.java index 2846882..d3202af 100644 --- a/src/main/java/com/be/sportizebe/domain/match/controller/MatchController.java +++ b/src/main/java/com/be/sportizebe/domain/match/controller/MatchController.java @@ -4,6 +4,7 @@ import com.be.sportizebe.domain.match.dto.response.MatchDetailResponse; import com.be.sportizebe.domain.match.dto.response.MatchNearResponse; import com.be.sportizebe.domain.match.dto.response.MatchParticipantResponse; +import com.be.sportizebe.domain.match.dto.response.MatchResponse; import com.be.sportizebe.domain.match.dto.response.MyMatchResponse; import com.be.sportizebe.domain.match.service.MatchService; import com.be.sportizebe.global.cache.dto.UserAuthInfo; @@ -24,6 +25,7 @@ import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.util.List; @@ -85,6 +87,15 @@ public ResponseEntity>> getMatchPart return ResponseEntity.ok(BaseResponse.success("매칭 참가자 목록 조회 성공", response)); } + @Operation(summary = "구장 기반 매칭 목록 조회", description = "해당 구장의 OPEN/FULL 상태이며 아직 시작 전인 매칭을 반환한다.") + @GetMapping + public ResponseEntity>> getMatchesByFacility( + @RequestParam Long facilityId + ) { + List response = matchService.getMatchesByFacility(facilityId); + return ResponseEntity.ok(BaseResponse.success("구장 매칭 목록 조회 성공", response)); + } + @Operation(summary = "내 주변 매칭 목록 조회", description = "매칭상태가 OPEN인 매칭들만 보여준다.") @GetMapping("/near") public ResponseEntity>> getNearMatches( diff --git a/src/main/java/com/be/sportizebe/domain/match/repository/MatchRoomRepository.java b/src/main/java/com/be/sportizebe/domain/match/repository/MatchRoomRepository.java index 921d0a6..11ddf4d 100644 --- a/src/main/java/com/be/sportizebe/domain/match/repository/MatchRoomRepository.java +++ b/src/main/java/com/be/sportizebe/domain/match/repository/MatchRoomRepository.java @@ -1,6 +1,7 @@ package com.be.sportizebe.domain.match.repository; import com.be.sportizebe.domain.match.entity.MatchRoom; +import com.be.sportizebe.domain.match.entity.MatchStatus; import com.be.sportizebe.domain.match.repository.projection.MatchNearProjection; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; @@ -46,6 +47,10 @@ List findNear( @Param("sportsName") String sportsName ); + // 구장 기반 모집 중 매칭 목록 (OPEN/FULL, 미래 일정만) + List findByFacilityIdAndStatusInAndScheduledAtAfterOrderByScheduledAtAsc( + Long facilityId, List statuses, java.time.LocalDateTime now); + // scheduledAt이 지난 OPEN/FULL → CLOSED @Modifying @Query(value = """ diff --git a/src/main/java/com/be/sportizebe/domain/match/service/MatchService.java b/src/main/java/com/be/sportizebe/domain/match/service/MatchService.java index 3ae98a6..22882f9 100644 --- a/src/main/java/com/be/sportizebe/domain/match/service/MatchService.java +++ b/src/main/java/com/be/sportizebe/domain/match/service/MatchService.java @@ -30,4 +30,6 @@ public interface MatchService { List getMatchParticipants(Long matchId); // 매칭 참가자 목록 + List getMatchesByFacility(Long facilityId); // 구장 기반 매칭 목록 + } diff --git a/src/main/java/com/be/sportizebe/domain/match/service/MatchServiceImpl.java b/src/main/java/com/be/sportizebe/domain/match/service/MatchServiceImpl.java index 6bfd4c2..3af35bf 100644 --- a/src/main/java/com/be/sportizebe/domain/match/service/MatchServiceImpl.java +++ b/src/main/java/com/be/sportizebe/domain/match/service/MatchServiceImpl.java @@ -24,6 +24,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.be.sportizebe.domain.match.dto.request.MatchCreateRequest; +import java.time.LocalDateTime; import java.util.List; import java.util.Optional; @@ -178,6 +179,20 @@ public List getMatchParticipants(Long matchId) { .toList(); } + @Override + @Transactional(readOnly = true) + public List getMatchesByFacility(Long facilityId) { + return matchRoomRepository + .findByFacilityIdAndStatusInAndScheduledAtAfterOrderByScheduledAtAsc( + facilityId, + List.of(MatchStatus.OPEN, MatchStatus.FULL), + LocalDateTime.now() + ) + .stream() + .map(MatchResponse::from) + .toList(); + } + @Override @Transactional(readOnly = true) public List getNearMatches(MatchNearRequest request) { From 8097b9ffc55bcf2c7749dfc71be78f3b916ccaf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=95=88=ED=9B=88=EA=B8=B0?= Date: Mon, 9 Mar 2026 17:32:40 +0900 Subject: [PATCH 3/3] =?UTF-8?q?:wrench:Setting:=20gitignore=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 871f511..cc12f90 100644 --- a/.gitignore +++ b/.gitignore @@ -43,3 +43,4 @@ src/main/resources/*.yml # 기타 CLAUDE.md +Makefile \ No newline at end of file