-
Notifications
You must be signed in to change notification settings - Fork 1
[feat] 방 검색 api 개발 #59
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
6865dc2
33d392d
3effb9f
718dfca
29747ee
50c25fc
914f7fe
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| package konkuk.thip.common.util; | ||
|
|
||
| import java.time.Duration; | ||
| import java.time.LocalDate; | ||
| import java.time.LocalDateTime; | ||
|
|
||
| public class DateUtilsss { | ||
|
|
||
| public static String formatAfterTime(LocalDate date) { | ||
| LocalDateTime now = LocalDateTime.now(); | ||
| LocalDateTime dateTime = date.atStartOfDay(); | ||
| Duration d = Duration.between(now, dateTime); | ||
|
|
||
| if (d.isNegative() || d.isZero()) { | ||
| return "??"; | ||
| } | ||
|
|
||
| long days = d.toDays(); | ||
| if (days > 0) { | ||
| return days + "일 뒤 "; | ||
| } | ||
|
|
||
| long hours = d.toHours(); | ||
| if (hours > 0) { | ||
| return hours + "시간 뒤 "; | ||
| } | ||
|
|
||
| long minutes = d.toMinutes(); | ||
| return minutes + "분 뒤 "; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| package konkuk.thip.room.adapter.in.web.response; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| public record RoomSearchResponse( | ||
| List<RoomSearchResult> roomList, | ||
| int page, // 현재 페이지 | ||
| int size, // 현재 페이지에 포함된 데이터 수 | ||
| boolean last, | ||
| boolean first | ||
| ) { | ||
|
|
||
| public record RoomSearchResult( | ||
| Long roomId, | ||
| String bookImageUrl, | ||
| String roomName, | ||
| int memberCount, | ||
| int recruitCount, | ||
| String deadlineDate, | ||
| String category | ||
| ) {} | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| package konkuk.thip.room.adapter.out.persistence; | ||
|
|
||
| import lombok.Getter; | ||
| import lombok.RequiredArgsConstructor; | ||
|
|
||
| import java.util.Arrays; | ||
|
|
||
| @Getter | ||
| @RequiredArgsConstructor | ||
| public enum CategoryName { | ||
|
|
||
| /** | ||
| * DB에 저장되어 있는 모든 카테고리들의 이름 | ||
| * TODO : DB에서 value를 통해 카테고리를 조회하는것보다 id로 조회하는게 성능상 좋으니, id 값도 같이 보관 ?? | ||
| */ | ||
| SCIENCE_IT("과학/IT"), | ||
| Literature("문학"), | ||
| ART("예술"), | ||
| SOCIAL_SCIENCE("사회과확"), | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 카테고리 이름의 오타를 수정하세요. "사회과확"이 "사회과학"으로 수정되어야 합니다. - SOCIAL_SCIENCE("사회과확"),
+ SOCIAL_SCIENCE("사회과학"),🤖 Prompt for AI Agents |
||
| HUMANITY("인문학"); | ||
|
|
||
| private final String value; | ||
|
|
||
| public static CategoryName from(String value) { | ||
| return Arrays.stream(CategoryName.values()) | ||
| .filter(categoryName -> categoryName.getValue().equals(value)) | ||
| .findFirst() | ||
| .orElseThrow( | ||
| () -> new IllegalArgumentException("현재 카테고리 이름 : " + value) | ||
| ); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| package konkuk.thip.room.adapter.out.persistence; | ||
|
|
||
| import konkuk.thip.room.adapter.in.web.response.RoomSearchResponse; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion 레이어 의존성 검토가 필요합니다. Persistence layer의 인터페이스에서 web layer의 response DTO를 직접 의존하고 있습니다. 이는 레이어 간 의존성을 증가시킬 수 있습니다. 가능하다면 persistence layer에서 사용할 별도의 DTO나 도메인 객체를 정의하는 것을 고려해보세요. 🤖 Prompt for AI Agents |
||
| import org.springframework.data.domain.Page; | ||
| import org.springframework.data.domain.Pageable; | ||
|
|
||
| public interface RoomQueryRepository { | ||
|
|
||
| Page<RoomSearchResponse.RoomSearchResult> searchRoom(String keyword, String category, Pageable pageable); | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,140 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| package konkuk.thip.room.adapter.out.persistence; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.querydsl.core.BooleanBuilder; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.querydsl.core.Tuple; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.querydsl.core.types.Order; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.querydsl.core.types.OrderSpecifier; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.querydsl.core.types.dsl.CaseBuilder; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.querydsl.core.types.dsl.NumberExpression; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.querydsl.jpa.impl.JPAQueryFactory; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import konkuk.thip.book.adapter.out.jpa.QBookJpaEntity; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import konkuk.thip.common.util.DateUtilsss; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import konkuk.thip.room.adapter.in.web.response.RoomSearchResponse; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import konkuk.thip.room.adapter.out.jpa.QRoomJpaEntity; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import konkuk.thip.user.adapter.out.jpa.QUserRoomJpaEntity; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import lombok.RequiredArgsConstructor; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.springframework.data.domain.Page; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.springframework.data.domain.PageImpl; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.springframework.data.domain.Pageable; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.springframework.data.domain.Sort; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.springframework.stereotype.Repository; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.time.LocalDate; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.util.List; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Repository | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @RequiredArgsConstructor | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public class RoomQueryRepositoryImpl implements RoomQueryRepository { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private final JPAQueryFactory queryFactory; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private final QRoomJpaEntity room = QRoomJpaEntity.roomJpaEntity; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private final QBookJpaEntity book = QBookJpaEntity.bookJpaEntity; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private final QUserRoomJpaEntity userRoom = QUserRoomJpaEntity.userRoomJpaEntity; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Override | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public Page<RoomSearchResponse.RoomSearchResult> searchRoom(String keyword, String category, Pageable pageable) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 1. 검색 조건(where) 조립 : 방이름 or 첵제목에 keyword 포함, category 필터 적용, 멤버 모집중인(= 활동 시작전인) 방만 검색 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| BooleanBuilder where = new BooleanBuilder(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // keyword 필터 (빈 문자열이면 생략) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. p2: 저희 서비스에서 모임방 검색후 검색어와 일치하는 모임방을 카테고리/정렬 필터에 따른 값으로 보여주는 것으로 알고있는데 이렇게 되면 keyword 필터가 필수이지않을까요..? 현재 구현하신것으로는 키워드가 없으면 키워드 없이 필터나/정렬으로만 결과가 반환되서 전체 책의 정렬 결과가 반환 될 것같은데 키워드포함 정렬결과가 반환되어야할것같습니다..! |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (keyword != null && !keyword.isBlank()) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| where.and(room.title.containsIgnoreCase(keyword).or(book.title.containsIgnoreCase(keyword))); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // category 필터 (빈 문자열이면 생략) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (category != null && !category.isBlank()) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| where.and(room.categoryJpaEntity.value.eq(category)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 모집중인 방만 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| where.and(room.startDate.after(LocalDate.now())); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 2. 페이징된 content 조회 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 우선순위 표현식 : keyword가 방 제목에 매칭되면 1, 아니면 0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| NumberExpression<Integer> priorityExpr = new CaseBuilder() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .when(room.title.containsIgnoreCase(keyword)).then(1) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .otherwise(0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
hd0rable marked this conversation as resolved.
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| NumberExpression<Long> memberCountExpr = userRoom.userRoomId.count(); // 방 별 멤버수 표현식 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| List<Tuple> tuples = queryFactory | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .select( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| room.roomId, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| book.imageUrl, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| room.title, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| memberCountExpr, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| room.recruitCount, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| room.startDate, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| room.categoryJpaEntity.value | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .from(room) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .join(room.bookJpaEntity, book) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .leftJoin(userRoom).on(userRoom.roomJpaEntity.eq(room)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .where(where) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .groupBy( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| room.roomId, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| book.imageUrl, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| room.title, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| room.recruitCount, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| room.startDate, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| room.categoryJpaEntity.value | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .orderBy( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 1차 정렬 : 설정된 정렬 조건, 2차 정렬 : 방이름으로 방 검색 > 책제목으로 방 검색 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| toOrderSpecifier(pageable.getSort(), room, memberCountExpr), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| priorityExpr.desc() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .offset(pageable.getOffset()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .limit(pageable.getPageSize()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .fetch(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // TODO : 추후에 오프셋 페이징이 아니라, 키셋 페이징 기법 도입 검토 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 3. Tuple → DTO 매핑 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| List<RoomSearchResponse.RoomSearchResult> content = tuples.stream() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .map(t -> new RoomSearchResponse.RoomSearchResult( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| t.get(room.roomId), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| t.get(book.imageUrl), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| t.get(room.title), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 참여자 수를 int로 캐스팅 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| t.get(memberCountExpr).intValue(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| t.get(room.recruitCount), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 모집마감일 까지 남은 시간 포맷 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| DateUtilsss.formatAfterTime(t.get(room.startDate)), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| t.get(room.categoryJpaEntity.value) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| )) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .toList(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 4. 전체 개수 조회 (페이징 정보 계산용) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Long totalCount = queryFactory | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .select(room.count()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .from(room) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .join(room.bookJpaEntity, book) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .where(where) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .fetchOne(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| long total = (totalCount != null) ? totalCount : 0L; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 5. PageImpl 생성하여 반환 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return new PageImpl<>(content, pageable, total); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * 지원하는 정렬 키에 대해, 미리 정의된 Q 필드를 반환합니다. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * sort가 없으면 '마감 임박순' 으로 기본 처리합니다. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private OrderSpecifier<?> toOrderSpecifier(Sort sort, QRoomJpaEntity room, NumberExpression<Long> memberCountExpr) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // sort 파라미터가 없으면 기본 마감 임박순 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (sort.isUnsorted()) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return room.startDate.asc(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 클라이언트가 보낸 첫 번째 sort 키를 꺼냅니다. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String key = sort.stream().findFirst().get().getProperty(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| switch (key) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case "memberCount": | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // user_rooms 테이블에서 현재 참여자 수 집계 → 내림차순 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return new OrderSpecifier<>(Order.DESC, memberCountExpr); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case "deadLine": | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| default: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // deadLine: 마감 임박순 = startDate 빠른 순서대로(오름차순) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return room.startDate.asc(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+121
to
+139
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion 정렬 키 추출 시 안전성을 개선해주세요.
private OrderSpecifier<?> toOrderSpecifier(Sort sort, QRoomJpaEntity room, NumberExpression<Long> memberCountExpr) {
// sort 파라미터가 없으면 기본 마감 임박순
if (sort.isUnsorted()) {
return room.startDate.asc();
}
// 클라이언트가 보낸 첫 번째 sort 키를 꺼냅니다.
- String key = sort.stream().findFirst().get().getProperty();
+ String key = sort.stream()
+ .findFirst()
+ .map(Sort.Order::getProperty)
+ .orElse("deadLine");
switch (key) {📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| package konkuk.thip.room.adapter.out.persistence; | ||
|
|
||
| import lombok.Getter; | ||
| import lombok.RequiredArgsConstructor; | ||
|
|
||
| import java.util.Arrays; | ||
|
|
||
| @Getter | ||
| @RequiredArgsConstructor | ||
| public enum RoomSearchSortParam { | ||
|
|
||
| DEADLINE("deadline"), | ||
| MEMBER_COUNT("memberCount"), | ||
| RECOMMEND("인플루언서, 작가 추천"); // 개발 미정 | ||
|
|
||
| private final String value; | ||
|
|
||
| public static RoomSearchSortParam from(String value) { | ||
| return Arrays.stream(RoomSearchSortParam.values()) | ||
| .filter(param -> param.getValue().equals(value)) | ||
| .findFirst() | ||
| .orElseThrow( | ||
| () -> new IllegalArgumentException("현재 정렬 조건 param : " + value) | ||
| ); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| package konkuk.thip.room.application.port.in; | ||
|
|
||
| import konkuk.thip.room.adapter.in.web.response.RoomSearchResponse; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion UseCase에서 web layer 의존성 검토가 필요합니다. Application layer의 UseCase에서 web layer의 response DTO를 직접 의존하고 있습니다. 이는 Clean Architecture의 의존성 규칙을 위반할 수 있습니다. Application layer에서 사용할 독립적인 DTO나 도메인 객체를 정의하는 것을 고려해보세요. 🤖 Prompt for AI Agents |
||
|
|
||
| public interface RoomSearchUseCase { | ||
|
|
||
| RoomSearchResponse searchRoom(String keyword, String category, String sort, int page); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,13 @@ | ||
| package konkuk.thip.room.application.port.out; | ||
|
|
||
| import konkuk.thip.room.adapter.in.web.response.RoomSearchResponse; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion 아키텍처 레이어 의존성 검토가 필요합니다. Port 인터페이스(application layer)에서 web layer의 response DTO를 직접 의존하고 있습니다. Clean Architecture 원칙에 따르면 application layer는 adapter layer에 의존하지 않아야 합니다. 도메인/application layer에서 사용할 별도의 DTO나 도메인 객체를 정의하는 것을 고려해보세요. 🤖 Prompt for AI Agents |
||
| import org.springframework.data.domain.Page; | ||
| import org.springframework.data.domain.Pageable; | ||
|
|
||
| import java.time.LocalDate; | ||
|
|
||
| public interface RoomQueryPort { | ||
| int countRecruitingRoomsByBookAndStartDateAfter(Long bookId, LocalDate currentDate); | ||
|
|
||
| Page<RoomSearchResponse.RoomSearchResult> searchRoom(String keyword, String category, Pageable pageable); | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
클래스 이름의 오타를 수정하세요.
클래스 이름에
DateUtilsss라고 되어 있는데,DateUtils로 수정해야 합니다.📝 Committable suggestion
🤖 Prompt for AI Agents