Skip to content

Commit 4549c50

Browse files
committed
feat: 건물 내 주요 편의시설 조회 구현
1 parent 7e0d2f0 commit 4549c50

6 files changed

Lines changed: 128 additions & 12 deletions

File tree

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package devkor.com.teamcback.domain.place.repository;
2+
3+
import devkor.com.teamcback.domain.place.entity.Place;
4+
import devkor.com.teamcback.domain.place.entity.PlaceType;
5+
6+
import java.util.List;
7+
8+
public interface CustomPlaceRepository {
9+
List<Place> getFacilitiesByBuildingAndTypesWithPage(Long buildingId, List<PlaceType> mainFacilityTypes, Place lastPlace, int size);
10+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package devkor.com.teamcback.domain.place.repository;
2+
3+
import com.querydsl.core.types.dsl.BooleanExpression;
4+
import com.querydsl.jpa.impl.JPAQueryFactory;
5+
import devkor.com.teamcback.domain.place.entity.Place;
6+
import devkor.com.teamcback.domain.place.entity.PlaceType;
7+
import lombok.RequiredArgsConstructor;
8+
import org.springframework.stereotype.Repository;
9+
10+
import java.util.List;
11+
12+
import static devkor.com.teamcback.domain.place.entity.QPlace.place;
13+
14+
@Repository
15+
@RequiredArgsConstructor
16+
public class CustomPlaceRepositoryImpl implements CustomPlaceRepository{
17+
18+
private final JPAQueryFactory jpaQueryFactory;
19+
20+
@Override
21+
public List<Place> getFacilitiesByBuildingAndTypesWithPage(Long buildingId, List<PlaceType> mainFacilityTypes, Place lastPlace, int size) {
22+
return jpaQueryFactory
23+
.selectFrom(place)
24+
.where(
25+
place.building.id.eq(buildingId),
26+
place.type.in(mainFacilityTypes),
27+
gtPlaceCursor(lastPlace)
28+
)
29+
.orderBy(
30+
place.floor.asc(),
31+
place.id.asc()
32+
)
33+
.limit(size)
34+
.fetch();
35+
}
36+
37+
private BooleanExpression gtPlaceCursor(Place lastPlace) {
38+
if (lastPlace== null) return null;
39+
return place.floor.gt(lastPlace.getFloor())
40+
.or(place.floor.eq(lastPlace.getFloor()).and(place.id.gt(lastPlace.getId())));
41+
}
42+
}

src/main/java/devkor/com/teamcback/domain/place/repository/PlaceRepository.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@
44
import devkor.com.teamcback.domain.place.entity.Place;
55
import devkor.com.teamcback.domain.place.entity.PlaceType;
66
import devkor.com.teamcback.domain.routes.entity.Node;
7-
import java.util.List;
8-
97
import org.springframework.data.jpa.repository.JpaRepository;
108

11-
public interface PlaceRepository extends JpaRepository<Place, Long> {
9+
import java.util.List;
10+
11+
public interface PlaceRepository extends JpaRepository<Place, Long>, CustomPlaceRepository {
1212

1313
List<Place> findAllByBuildingAndType(Building building, PlaceType type);
1414

src/main/java/devkor/com/teamcback/domain/search/contoller/SearchController.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import devkor.com.teamcback.domain.search.dto.response.*;
66
import devkor.com.teamcback.domain.search.service.SearchService;
77
import devkor.com.teamcback.global.response.CommonResponse;
8+
import devkor.com.teamcback.global.response.CursorPageRes;
89
import devkor.com.teamcback.global.security.UserDetailsImpl;
910
import io.swagger.v3.oas.annotations.Operation;
1011
import io.swagger.v3.oas.annotations.Parameter;
@@ -196,6 +197,29 @@ public CommonResponse<SearchBuildingDetailRes> searchBuildingDetail(
196197
return CommonResponse.success(searchService.searchBuildingDetail(userId, buildingId));
197198
}
198199

200+
/**
201+
* 건물 대표 편의시설 목록 조회 (no-offset)
202+
* @param buildingId 건물 id
203+
* @param lastPlaceId 마지막 편의시설 id
204+
* @param size 한 번에 가져올 크기
205+
*/
206+
@Operation(summary = "건물 주요 편의시설 조회", description = "건물 주요 편의시설 조회")
207+
@ApiResponses(value = {
208+
@ApiResponse(responseCode = "200", description = "정상 처리 되었습니다."),
209+
@ApiResponse(responseCode = "404", description = "Not Found",
210+
content = @Content(schema = @Schema(implementation = CommonResponse.class))),
211+
})
212+
@GetMapping("/buildings/{buildingId}/main-facilities")
213+
public CommonResponse<CursorPageRes<SearchMainFacilityRes>> searchBuildingMainFacilityList(
214+
@Parameter(name = "buildingId", description = "건물 id", example = "1", required = true)
215+
@PathVariable Long buildingId,
216+
@Parameter(description = "마지막 편의시설 ID")
217+
@RequestParam(required = false) Long lastPlaceId,
218+
@Parameter(description = "한 번에 가져올 크기")
219+
@RequestParam(defaultValue = "8") int size) {
220+
return CommonResponse.success(searchService.searchBuildingMainFacilityList(buildingId, lastPlaceId, size));
221+
}
222+
199223
/**
200224
* 장소 상세 정보 조회
201225
* @param placeId 장소 id

src/main/java/devkor/com/teamcback/domain/search/service/SearchService.java

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import devkor.com.teamcback.domain.user.entity.User;
2323
import devkor.com.teamcback.domain.user.repository.UserRepository;
2424
import devkor.com.teamcback.global.exception.exception.GlobalException;
25+
import devkor.com.teamcback.global.response.CursorPageRes;
2526
import lombok.RequiredArgsConstructor;
2627
import lombok.extern.slf4j.Slf4j;
2728
import org.springframework.data.domain.PageRequest;
@@ -64,23 +65,27 @@ public class SearchService {
6465
static final int BASE_SCORE_OUTDOOR_TAG_DEFAULT = 750;
6566

6667
// 건물 상세 모달 아이콘으로 표시할 편의시설 종류
67-
private final List<PlaceType> iconTypes = Arrays.asList(PlaceType.VENDING_MACHINE, PlaceType.PRINTER, PlaceType.LOUNGE,
68+
private static final List<PlaceType> iconTypes = Arrays.asList(PlaceType.VENDING_MACHINE, PlaceType.PRINTER, PlaceType.LOUNGE,
6869
PlaceType.READING_ROOM, PlaceType.STUDY_ROOM, PlaceType.CAFE, PlaceType.CONVENIENCE_STORE, PlaceType.CAFETERIA,
6970
PlaceType.SLEEPING_ROOM, PlaceType.SHOWER_ROOM, PlaceType.BANK, PlaceType.GYM);
7071

7172
// 통합 검색 결과에 표시하지 않을 편의시설 종류
7273
//TODO: 자전거보관소, 벤치 디자인 요청 후 List에서 제거
73-
private final List<String> excludedTypes = Arrays.asList(PlaceType.CLASSROOM.getName(), PlaceType.TOILET.getName(),
74+
private static final List<String> excludedTypes = Arrays.asList(PlaceType.CLASSROOM.getName(), PlaceType.TOILET.getName(),
7475
PlaceType.MEN_TOILET.getName(), PlaceType.WOMEN_TOILET.getName(), PlaceType.MEN_HANDICAPPED_TOILET.getName(),
7576
PlaceType.WOMEN_HANDICAPPED_TOILET.getName(), PlaceType.LOCKER.getName(), PlaceType.TRASH_CAN.getName(),
7677
PlaceType.BICYCLE_RACK.getName(), PlaceType.BENCH.getName());
7778

7879
// 통합 검색 결과에서 "건물명 + 기본편의시설명"의 형태로 제공되어야 하는 편의시설 종류
79-
private final List<PlaceType> outerTagTypes = Arrays.asList(PlaceType.CAFE, PlaceType.CAFETERIA, PlaceType.CONVENIENCE_STORE,
80+
private static final List<PlaceType> outerTagTypes = Arrays.asList(PlaceType.CAFE, PlaceType.CAFETERIA, PlaceType.CONVENIENCE_STORE,
8081
PlaceType.READING_ROOM, PlaceType.STUDY_ROOM, PlaceType.BOOK_RETURN_MACHINE, PlaceType.LOUNGE, PlaceType.WATER_PURIFIER,
8182
PlaceType.VENDING_MACHINE, PlaceType.PRINTER, PlaceType.TUMBLER_WASHER, PlaceType.ONESTOP_AUTO_MACHINE, PlaceType.BANK,
8283
PlaceType.SMOKING_BOOTH, PlaceType.SHOWER_ROOM, PlaceType.GYM, PlaceType.SLEEPING_ROOM, PlaceType.HEALTH_OFFICE, PlaceType.DISABLED_PARKING);
8384

85+
// 건물 상세 조회 : 대표 편의시설 종류
86+
private static final List<PlaceType> mainFacilityTypes = Arrays.asList(PlaceType.LOUNGE, PlaceType.CAFE, PlaceType.CONVENIENCE_STORE, PlaceType.CAFETERIA,
87+
PlaceType.READING_ROOM, PlaceType.STUDY_ROOM, PlaceType.GYM, PlaceType.SLEEPING_ROOM, PlaceType.SHOWER_ROOM);
88+
8489
/**
8590
* 통합 검색
8691
*/
@@ -264,10 +269,8 @@ public SearchMaskIndexByPlaceRes searchMaskIndexByPlace(Long placeId) {
264269
public SearchBuildingDetailRes searchBuildingDetail(Long userId, Long buildingId) {
265270
Building building = findBuilding(buildingId);
266271

267-
//(임의) 가져올 대표 시설 정보 List (라운지, 카페, 편의점, 식당, 헬스장, 열람실, 스터디룸, 수면실, 샤워실)
268-
//자세한 회의 후 List 수정하기
269-
List<PlaceType> types = Arrays.asList(PlaceType.LOUNGE, PlaceType.CAFE, PlaceType.CONVENIENCE_STORE, PlaceType.CAFETERIA, PlaceType.READING_ROOM, PlaceType.STUDY_ROOM, PlaceType.GYM, PlaceType.SLEEPING_ROOM, PlaceType.SHOWER_ROOM);
270-
List<Place> mainFacilities = getFacilitiesByBuildingAndTypes(building, types);
272+
// TODO: 배포 버전 수정 후 편의시설 조회 제거
273+
List<Place> mainFacilities = getFacilitiesByBuildingAndTypes(building, mainFacilityTypes);
271274
List<SearchMainFacilityRes> res = new ArrayList<>();
272275

273276
for (Place place : mainFacilities) {
@@ -292,10 +295,22 @@ public SearchBuildingDetailRes searchBuildingDetail(Long userId, Long buildingId
292295
bookmarked = true;
293296
}
294297
}
298+
return new SearchBuildingDetailRes(res, containPlaceTypes, building, bookmarked);
299+
}
300+
301+
@Transactional(readOnly = true)
302+
public CursorPageRes<SearchMainFacilityRes> searchBuildingMainFacilityList(Long buildingId, Long lastPlaceId, int size) {
303+
Place lastPlace = (lastPlaceId == null) ? null : findPlace(lastPlaceId);
295304

296-
// TODO: 커뮤니티 구상 완료되면 커뮤니티 정보 넣기
305+
List<SearchMainFacilityRes> mainFacilities = placeRepository.getFacilitiesByBuildingAndTypesWithPage(buildingId, mainFacilityTypes, lastPlace, size + 1)
306+
.stream().map(SearchMainFacilityRes::new).collect(Collectors.toList());
297307

298-
return new SearchBuildingDetailRes(res, containPlaceTypes, building, bookmarked);
308+
boolean hasNext = mainFacilities.size() > size;
309+
if (hasNext) mainFacilities.remove(size);
310+
311+
Long lastCursorId = mainFacilities.isEmpty() ? null : mainFacilities.get(mainFacilities.size() - 1).getPlaceId();
312+
313+
return new CursorPageRes<>(mainFacilities, hasNext, lastCursorId);
299314
}
300315

301316
/**
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package devkor.com.teamcback.global.response;
2+
3+
import io.swagger.v3.oas.annotations.media.Schema;
4+
import lombok.Getter;
5+
6+
import java.util.List;
7+
8+
@Getter
9+
@Schema(description = "커서 기반 페이지네이션 응답")
10+
public class CursorPageRes<T> {
11+
@Schema(description = "데이터 리스트")
12+
private List<T> content;
13+
14+
@Schema(description = "다음 페이지 존재 여부", example = "true")
15+
private boolean hasNext;
16+
17+
@Schema(description = "마지막 커서 ID", example = "99")
18+
private Long lastCursorId;
19+
20+
public CursorPageRes(List<T> content, boolean hasNext, Long lastCursorId) {
21+
this.content = content;
22+
this.hasNext = hasNext;
23+
this.lastCursorId = lastCursorId;
24+
}
25+
}

0 commit comments

Comments
 (0)