diff --git a/src/main/java/io/oeid/mogakgo/common/base/CursorPaginationInfoReq.java b/src/main/java/io/oeid/mogakgo/common/base/CursorPaginationInfoReq.java index 6881b2ea..98c1c4db 100644 --- a/src/main/java/io/oeid/mogakgo/common/base/CursorPaginationInfoReq.java +++ b/src/main/java/io/oeid/mogakgo/common/base/CursorPaginationInfoReq.java @@ -19,9 +19,10 @@ public class CursorPaginationInfoReq { @Nullable private final Sort.Direction sortOrder; - public CursorPaginationInfoReq(Long cursorId, int pageSize, Direction sortOrder) { + public CursorPaginationInfoReq(@Nullable Long cursorId, int pageSize, Direction sortOrder) { this.cursorId = cursorId; this.pageSize = pageSize; - this.sortOrder = Objects.requireNonNullElse(sortOrder, Direction.ASC); + // 최근순 기본 + this.sortOrder = Objects.requireNonNullElse(sortOrder, Direction.DESC); } } diff --git a/src/main/java/io/oeid/mogakgo/common/swagger/template/MatchingSwagger.java b/src/main/java/io/oeid/mogakgo/common/swagger/template/MatchingSwagger.java index 0838d801..e539bd17 100644 --- a/src/main/java/io/oeid/mogakgo/common/swagger/template/MatchingSwagger.java +++ b/src/main/java/io/oeid/mogakgo/common/swagger/template/MatchingSwagger.java @@ -1,11 +1,15 @@ package io.oeid.mogakgo.common.swagger.template; +import io.oeid.mogakgo.common.base.CursorPaginationInfoReq; +import io.oeid.mogakgo.common.base.CursorPaginationResult; import io.oeid.mogakgo.core.properties.swagger.error.SwaggerMatchingErrorExamples; import io.oeid.mogakgo.core.properties.swagger.error.SwaggerProjectErrorExamples; import io.oeid.mogakgo.core.properties.swagger.error.SwaggerUserErrorExamples; +import io.oeid.mogakgo.domain.matching.presentation.dto.MatchingHistoryRes; import io.oeid.mogakgo.domain.matching.presentation.dto.MatchingId; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.ExampleObject; import io.swagger.v3.oas.annotations.responses.ApiResponse; @@ -44,4 +48,29 @@ ResponseEntity cancel( @Parameter(description = "매칭 ID", required = true) Long matchingId ); + @Operation(summary = "본인의 매칭 기록 가지고 오기", description = "회원이 본인의 매칭 기록을 가지고 올 때 사용하는 API") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공"), + @ApiResponse(responseCode = "403", description = "요청을 보낸 사람이 매칭 취소할 권한이 안됨 (매칭 참여자가 아님)", + content = @Content( + mediaType = "application/json", + examples = @ExampleObject(name = "E090201", value = SwaggerMatchingErrorExamples.MATCHING_FORBIDDEN_OPERATION))), + @ApiResponse(responseCode = "404", description = "요청한 데이터가 존재하지 않음", + content = @Content( + mediaType = "application/json", + examples = { + @ExampleObject(name = "E020301", value = SwaggerUserErrorExamples.USER_NOT_FOUND) + })), + }) + @Parameters({ + @Parameter(name = "cursorId", description = "기준이 되는 커서 ID", example = "1"), + @Parameter(name = "pageSize", description = "요청할 데이터 크기", example = "5", required = true), + @Parameter(name = "sortOrder", description = "정렬 방향", example = "ASC"), + }) + ResponseEntity> getMyMatches( + @Parameter(hidden = true) Long tokenId, + @Parameter(description = "유저 ID", required = true) Long userId, + @Parameter(hidden = true) CursorPaginationInfoReq pageable + ); + } diff --git a/src/main/java/io/oeid/mogakgo/domain/matching/application/MatchingService.java b/src/main/java/io/oeid/mogakgo/domain/matching/application/MatchingService.java index da9094f8..08b1e4f5 100644 --- a/src/main/java/io/oeid/mogakgo/domain/matching/application/MatchingService.java +++ b/src/main/java/io/oeid/mogakgo/domain/matching/application/MatchingService.java @@ -1,10 +1,14 @@ package io.oeid.mogakgo.domain.matching.application; +import static io.oeid.mogakgo.exception.code.ErrorCode403.MATCHING_FORBIDDEN_OPERATION; import static io.oeid.mogakgo.exception.code.ErrorCode404.MATCHING_NOT_FOUND; +import io.oeid.mogakgo.common.base.CursorPaginationInfoReq; +import io.oeid.mogakgo.common.base.CursorPaginationResult; import io.oeid.mogakgo.domain.matching.domain.entity.Matching; import io.oeid.mogakgo.domain.matching.exception.MatchingException; import io.oeid.mogakgo.domain.matching.infrastructure.MatchingJpaRepository; +import io.oeid.mogakgo.domain.matching.presentation.dto.MatchingHistoryRes; import io.oeid.mogakgo.domain.project_join_req.domain.entity.ProjectJoinRequest; import io.oeid.mogakgo.domain.user.application.UserCommonService; import io.oeid.mogakgo.domain.user.domain.User; @@ -46,6 +50,18 @@ public Long cancel(Long tokenUserId, Long matchingId) { return matching.getId(); } + public CursorPaginationResult getMyMatches( + Long tokenUserId, Long userId, CursorPaginationInfoReq cursorPaginationInfoReq + ) { + User tokenUser = userCommonService.getUserById(tokenUserId); + // 본인만 매칭 기록 조회 가능 + if (!tokenUser.getId().equals(userId)) { + throw new MatchingException(MATCHING_FORBIDDEN_OPERATION); + } + + return matchingJpaRepository.getMyMatches(tokenUserId, cursorPaginationInfoReq); + } + private Matching getMatching(Long matchingId) { return matchingJpaRepository.findById(matchingId) .orElseThrow(() -> new MatchingException(MATCHING_NOT_FOUND)); diff --git a/src/main/java/io/oeid/mogakgo/domain/matching/infrastructure/MatchingJpaRepository.java b/src/main/java/io/oeid/mogakgo/domain/matching/infrastructure/MatchingJpaRepository.java index b421c25d..39f47e71 100644 --- a/src/main/java/io/oeid/mogakgo/domain/matching/infrastructure/MatchingJpaRepository.java +++ b/src/main/java/io/oeid/mogakgo/domain/matching/infrastructure/MatchingJpaRepository.java @@ -5,8 +5,11 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; -public interface MatchingJpaRepository extends JpaRepository { +@Repository +public interface MatchingJpaRepository extends JpaRepository, + MatchingRepositoryCustom { @Query("select m from Matching m join Project p on m.project.id = p.id " + "where (m.sender.id = :userId or p.creator.id = :userId) and m.matchingStatus = 'PROGRESS'") diff --git a/src/main/java/io/oeid/mogakgo/domain/matching/infrastructure/MatchingRepositoryCustom.java b/src/main/java/io/oeid/mogakgo/domain/matching/infrastructure/MatchingRepositoryCustom.java new file mode 100644 index 00000000..81887574 --- /dev/null +++ b/src/main/java/io/oeid/mogakgo/domain/matching/infrastructure/MatchingRepositoryCustom.java @@ -0,0 +1,14 @@ +package io.oeid.mogakgo.domain.matching.infrastructure; + +import io.oeid.mogakgo.common.base.CursorPaginationInfoReq; +import io.oeid.mogakgo.common.base.CursorPaginationResult; +import io.oeid.mogakgo.domain.matching.presentation.dto.MatchingHistoryRes; +import java.util.List; + +public interface MatchingRepositoryCustom { + + CursorPaginationResult getMyMatches( + Long userId, CursorPaginationInfoReq cursorPaginationInfoReq + ); + +} diff --git a/src/main/java/io/oeid/mogakgo/domain/matching/infrastructure/MatchingRepositoryCustomImpl.java b/src/main/java/io/oeid/mogakgo/domain/matching/infrastructure/MatchingRepositoryCustomImpl.java new file mode 100644 index 00000000..87eb76b6 --- /dev/null +++ b/src/main/java/io/oeid/mogakgo/domain/matching/infrastructure/MatchingRepositoryCustomImpl.java @@ -0,0 +1,64 @@ +package io.oeid.mogakgo.domain.matching.infrastructure; + +import static io.oeid.mogakgo.domain.matching.domain.entity.QMatching.matching; +import static io.oeid.mogakgo.domain.project.domain.entity.QProject.project; + +import com.querydsl.core.types.Projections; +import com.querydsl.core.types.dsl.BooleanExpression; +import com.querydsl.jpa.impl.JPAQueryFactory; +import io.oeid.mogakgo.common.base.CursorPaginationInfoReq; +import io.oeid.mogakgo.common.base.CursorPaginationResult; +import io.oeid.mogakgo.domain.matching.presentation.dto.MatchingHistoryRes; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +@Repository +@RequiredArgsConstructor +public class MatchingRepositoryCustomImpl implements MatchingRepositoryCustom { + + private final JPAQueryFactory jpaQueryFactory; + + + @Override + public CursorPaginationResult getMyMatches( + Long userId, CursorPaginationInfoReq cursorPaginationInfoReq + ) { + List matchingHistoryResList = jpaQueryFactory + .select( + Projections.constructor( + MatchingHistoryRes.class, + matching.id, + matching.matchingStatus, + matching.project.creator.avatarUrl, + matching.project.meetingInfo.meetDetail, + matching.project.meetingInfo.meetStartTime, + matching.project.meetingInfo.meetEndTime + ) + ) + .from(matching) + .join(matching.project) + .where( + participantInMatching(userId), + cursorIdCondition(cursorPaginationInfoReq.getCursorId()) + ) + // 최근순 + .orderBy(matching.id.desc()) + .limit(cursorPaginationInfoReq.getPageSize() + 1) + .fetch(); + + return CursorPaginationResult.fromDataWithExtraItemForNextCheck(matchingHistoryResList, + cursorPaginationInfoReq.getPageSize()); + } + + private BooleanExpression cursorIdCondition(Long cursorId) { + return cursorId != null ? matching.id.lt(cursorId) : null; + } + + private BooleanExpression participantInMatching(Long userId) { + return userId != null ? (matching.sender.id.eq(userId).or(matching.project.creator.id.eq( + userId))) : null; + } + + +} diff --git a/src/main/java/io/oeid/mogakgo/domain/matching/presentation/MatchingController.java b/src/main/java/io/oeid/mogakgo/domain/matching/presentation/MatchingController.java index c66b8b68..1f07f0c0 100644 --- a/src/main/java/io/oeid/mogakgo/domain/matching/presentation/MatchingController.java +++ b/src/main/java/io/oeid/mogakgo/domain/matching/presentation/MatchingController.java @@ -1,12 +1,16 @@ package io.oeid.mogakgo.domain.matching.presentation; import io.oeid.mogakgo.common.annotation.UserId; +import io.oeid.mogakgo.common.base.CursorPaginationInfoReq; +import io.oeid.mogakgo.common.base.CursorPaginationResult; import io.oeid.mogakgo.common.swagger.template.MatchingSwagger; import io.oeid.mogakgo.domain.matching.application.MatchingService; +import io.oeid.mogakgo.domain.matching.presentation.dto.MatchingHistoryRes; import io.oeid.mogakgo.domain.matching.presentation.dto.MatchingId; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; @@ -24,4 +28,13 @@ public ResponseEntity cancel(@UserId Long userId, @PathVariable Long return ResponseEntity.ok(new MatchingId(matchingService.cancel(userId, matchingId))); } + @GetMapping("/my/{userId}") + public ResponseEntity> getMyMatches( + @UserId Long tokenId, @PathVariable Long userId, + @ModelAttribute CursorPaginationInfoReq pageable + ) { + return ResponseEntity.ok( + matchingService.getMyMatches(tokenId, userId, pageable)); + } + } diff --git a/src/main/java/io/oeid/mogakgo/domain/matching/presentation/dto/MatchingHistoryRes.java b/src/main/java/io/oeid/mogakgo/domain/matching/presentation/dto/MatchingHistoryRes.java new file mode 100644 index 00000000..2c2f53aa --- /dev/null +++ b/src/main/java/io/oeid/mogakgo/domain/matching/presentation/dto/MatchingHistoryRes.java @@ -0,0 +1,27 @@ +package io.oeid.mogakgo.domain.matching.presentation.dto; + +import io.oeid.mogakgo.domain.matching.domain.entity.enums.MatchingStatus; +import io.swagger.v3.oas.annotations.media.Schema; +import java.time.LocalDateTime; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Schema(description = "매칭 이력") +@Getter +@AllArgsConstructor +public class MatchingHistoryRes { + + @Schema(description = "매칭 ID") + private final Long matchingId; + @Schema(description = "매칭 상태") + private final MatchingStatus status; + @Schema(description = "상대방 프로필 이미지 URL") + private final String anotherUserAvatarUrl; + @Schema(description = "프로젝트 위치 상세") + private final String projectLocationDetail; + @Schema(description = "프로젝트 시작 시간") + private final LocalDateTime projectStartTime; + @Schema(description = "프로젝트 종료 시간") + private final LocalDateTime projectEndTime; + +} diff --git a/src/main/java/io/oeid/mogakgo/domain/project_join_req/infrastructure/ProjectJoinRequestRepositoryCustomImpl.java b/src/main/java/io/oeid/mogakgo/domain/project_join_req/infrastructure/ProjectJoinRequestRepositoryCustomImpl.java index 1a144bee..a2e05315 100644 --- a/src/main/java/io/oeid/mogakgo/domain/project_join_req/infrastructure/ProjectJoinRequestRepositoryCustomImpl.java +++ b/src/main/java/io/oeid/mogakgo/domain/project_join_req/infrastructure/ProjectJoinRequestRepositoryCustomImpl.java @@ -53,6 +53,8 @@ public CursorPaginationResult findByConditionWithPaginati projectIdEq(projectId), requestStatusEq(requestStatus) ) + // 오래 된 순 + .orderBy(projectJoinRequest.id.asc()) .limit(pageable.getPageSize() + 1) .fetch();