diff --git a/src/main/java/com/aibe/team2/domain/interview/repository/InterviewSessionRepositoryCustom.java b/src/main/java/com/aibe/team2/domain/interview/repository/InterviewSessionRepositoryCustom.java index 7bb3731..f9c93e1 100644 --- a/src/main/java/com/aibe/team2/domain/interview/repository/InterviewSessionRepositoryCustom.java +++ b/src/main/java/com/aibe/team2/domain/interview/repository/InterviewSessionRepositoryCustom.java @@ -1,9 +1,16 @@ package com.aibe.team2.domain.interview.repository; +import com.aibe.team2.domain.interview.enums.InterviewType; import com.aibe.team2.domain.mypage.dto.response.InterviewSessionListResponse; -import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; +import java.util.List; + public interface InterviewSessionRepositoryCustom { - Page findInterviewSessionList(Long memberId, Pageable pageable); + List findInterviewSessionList( + Long memberId, + InterviewType type, + String keyword, + Pageable pageable + ); } diff --git a/src/main/java/com/aibe/team2/domain/interview/repository/InterviewSessionRepositoryImpl.java b/src/main/java/com/aibe/team2/domain/interview/repository/InterviewSessionRepositoryImpl.java index 4c37344..fcabac8 100644 --- a/src/main/java/com/aibe/team2/domain/interview/repository/InterviewSessionRepositoryImpl.java +++ b/src/main/java/com/aibe/team2/domain/interview/repository/InterviewSessionRepositoryImpl.java @@ -1,13 +1,15 @@ package com.aibe.team2.domain.interview.repository; +import com.aibe.team2.domain.interview.enums.InterviewType; import com.aibe.team2.domain.mypage.dto.response.InterviewSessionListResponse; import com.querydsl.core.types.Projections; +import com.querydsl.core.types.dsl.BooleanExpression; import com.querydsl.jpa.impl.JPAQuery; import com.querydsl.jpa.impl.JPAQueryFactory; import lombok.RequiredArgsConstructor; -import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.support.PageableExecutionUtils; +import org.springframework.util.StringUtils; import java.util.List; @@ -21,10 +23,25 @@ public class InterviewSessionRepositoryImpl implements InterviewSessionRepositor private final JPAQueryFactory queryFactory; @Override - public Page findInterviewSessionList(Long memberId, Pageable pageable) { + public List findInterviewSessionList( + Long memberId, + InterviewType type, + String keyword, + Pageable pageable + ) { + // 1. 공통 조건(from, join, where)을 포함하는 기본 쿼리 생성 + JPAQuery baseQuery = queryFactory + .from(interviewSession) + .leftJoin(resume).on(interviewSession.resumeId.eq(resume.id)) + .leftJoin(jobPosting).on(interviewSession.jobPostingId.eq(jobPosting.id)) + .where( + interviewSession.memberId.eq(memberId), + eqType(type), + containsKeyword(keyword) + ); - // 1. 실제 데이터 조회 쿼리 - List content = queryFactory + // 2. 실제 데이터 조회 쿼리 후 곧바로 List 반환 + return baseQuery.clone() .select(Projections.constructor(InterviewSessionListResponse.class, interviewSession.id, resume.title, @@ -36,23 +53,30 @@ public Page findInterviewSessionList(Long memberId interviewSession.finalScore, interviewSession.createdAt )) - .from(interviewSession) - // [핵심] 엔티티에 연관관계가 없으므로 ON 절로 ID 값을 직접 매칭 - .leftJoin(resume).on(interviewSession.resumeId.eq(resume.id)) - .leftJoin(jobPosting).on(interviewSession.jobPostingId.eq(jobPosting.id)) - .where(interviewSession.memberId.eq(memberId)) - .orderBy(interviewSession.createdAt.desc()) // 최신순 정렬 - .offset(pageable.getOffset()) // 페이징 시작점 - .limit(pageable.getPageSize()) // 페이지 크기 - .fetch(); - - // 2. 전체 개수 조회 쿼리 - JPAQuery countQuery = queryFactory - .select(interviewSession.count()) - .from(interviewSession) - .where(interviewSession.memberId.eq(memberId)); + .orderBy(interviewSession.createdAt.desc()) + .offset(pageable.getOffset()) + .limit(pageable.getPageSize()) + .fetch(); // fetch()는 List를 반환합니다. + } + + // 검색어 포함 여부 확인 + private BooleanExpression containsKeyword(String keyword) { + if (!StringUtils.hasText(keyword)) { + return null; // 검색어가 없으면 조건을 무시 + } + // 예시: 회사명 또는 자기소개서 제목에 검색어가 포함되어 있는지 검사 + return jobPosting.companyName.contains(keyword) + .or(resume.title.contains(keyword)); + } + + private BooleanExpression eqType(InterviewType type) { + if (type == null) { + return null; // 프론트에서 값이 안 오면(전체 조회 시) 조건을 무시 + } - // 3. 데이터와 카운트 쿼리를 조합하여 Page 객체로 반환 - return PageableExecutionUtils.getPage(content, pageable, countQuery::fetchOne); + // 1. 현재 엔티티의 필드가 String 타입이므로, Enum 객체를 String으로 변환(type.name())하여 비교합니다. + // 2. 나중을 위해 기술 부채(Technical Debt)를 기록해 둡니다. + // TODO: 향후 InterviewSession 엔티티의 interviewType 필드를 Enum으로 변경 후 eq(type)으로 리팩토링 필요 + return interviewSession.interviewType.eq(type.name()); } } diff --git a/src/main/java/com/aibe/team2/domain/mypage/controller/MypageInterviewController.java b/src/main/java/com/aibe/team2/domain/mypage/controller/MypageInterviewController.java index 3003088..50bffa1 100644 --- a/src/main/java/com/aibe/team2/domain/mypage/controller/MypageInterviewController.java +++ b/src/main/java/com/aibe/team2/domain/mypage/controller/MypageInterviewController.java @@ -1,6 +1,6 @@ package com.aibe.team2.domain.mypage.controller; -import com.aibe.team2.domain.auth.dto.CustomUserDetails; +import com.aibe.team2.domain.interview.enums.InterviewType; import com.aibe.team2.domain.mypage.dto.response.InterviewSessionListResponse; import com.aibe.team2.domain.mypage.service.MypageInterviewService; import com.aibe.team2.global.common.annotation.LoginMemberId; @@ -10,7 +10,6 @@ import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.http.ResponseEntity; -import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; @@ -28,10 +27,13 @@ public class MypageInterviewController { public ResponseEntity> getInterviewSessionList( @LoginMemberId Long memberId, @RequestParam(defaultValue = "0") int page, - @RequestParam(defaultValue = "10") int size + @RequestParam(defaultValue = "10") int size, + @RequestParam(name = "type", required = false) InterviewType type, + @RequestParam(required = false) String keyword ) { Pageable pageRequest = PageRequest.of(page, size); - Page response = mypageInterviewService.getInterviewSessionList(memberId, pageRequest); + Page response = + mypageInterviewService.getInterviewSessionList(memberId, type, keyword, pageRequest); return ResponseEntity.ok(response); } diff --git a/src/main/java/com/aibe/team2/domain/mypage/service/MypageInterviewService.java b/src/main/java/com/aibe/team2/domain/mypage/service/MypageInterviewService.java index 104ee48..7b1377a 100644 --- a/src/main/java/com/aibe/team2/domain/mypage/service/MypageInterviewService.java +++ b/src/main/java/com/aibe/team2/domain/mypage/service/MypageInterviewService.java @@ -1,5 +1,7 @@ package com.aibe.team2.domain.mypage.service; +import com.aibe.team2.domain.interview.enums.InterviewMode; +import com.aibe.team2.domain.interview.enums.InterviewType; import com.aibe.team2.domain.interview.repository.InterviewSessionRepository; import com.aibe.team2.domain.mypage.dto.response.InterviewSessionListResponse; import lombok.RequiredArgsConstructor; @@ -15,8 +17,8 @@ public class MypageInterviewService { private final InterviewSessionRepository interviewSessionRepository; - public Page getInterviewSessionList(Long memberId, Pageable pageable) { + public Page getInterviewSessionList(Long memberId, InterviewType type, String keyword, Pageable pageable) { - return interviewSessionRepository.findInterviewSessionList(memberId, pageable); + return interviewSessionRepository.findInterviewSessionList(memberId, type, keyword, pageable); } } diff --git a/src/main/java/com/aibe/team2/global/redis/RedisConfig.java b/src/main/java/com/aibe/team2/global/redis/RedisConfig.java index 973e484..920f4c1 100644 --- a/src/main/java/com/aibe/team2/global/redis/RedisConfig.java +++ b/src/main/java/com/aibe/team2/global/redis/RedisConfig.java @@ -49,6 +49,7 @@ public GenericJackson2JsonRedisSerializer customJsonSerializer() { .allowIfBaseType("java.util") // List, Map 등 자바 기본 컬렉션 허용 .allowIfBaseType("java.time") // LocalDateTime 등 시간 객체 허용 .allowIfBaseType("java.lang") // String, Long, Integer 등 기본 래퍼 타입 허용 + .allowIfBaseType("java.math") // [추가] BigDecimal 등 수학 관련 객체 허용! .build(); ObjectMapper objectMapper = new ObjectMapper();