Skip to content

Commit c49cf54

Browse files
authored
Fix/party search (#95)
* feat : 거리 계산 쿼리 PartySearchRepository로 분리 (거리순, 페이징, 검색) * feat : 거리 계산 API 구현
1 parent 2414699 commit c49cf54

4 files changed

Lines changed: 101 additions & 31 deletions

File tree

src/main/java/ita/tinybite/domain/party/controller/PartyController.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -490,10 +490,13 @@ public APIResponse<PartyQueryListResponse> getParty(
490490
schema = @Schema(allowableValues = {"ALL", "DELIVERY", "GROCERY", "HOUSEHOLD"})
491491
)
492492
@RequestParam(defaultValue = "ALL") PartyCategory category,
493+
@RequestParam(required = false, name = "lat") Double userLat,
494+
@RequestParam(required = false, name = "lon") Double userLon,
493495
@RequestParam(defaultValue = "0") int page,
494496
@RequestParam(defaultValue = "20") int size
495497
) {
496-
return APIResponse.success(partySearchService.searchParty(q, category, page, size));
498+
499+
return APIResponse.success(partySearchService.searchParty(q, category, page, size, userLat, userLon));
497500
}
498501

499502
@Operation(
@@ -511,7 +514,7 @@ public APIResponse<List<String>> getRecentLog() {
511514
@Operation(
512515
summary = "특정 최근 검색어 삭제",
513516
description = """
514-
최근 검색어에서 특정 검색어를 삭제합니다. <br>
517+
최근 검색어에서 특정 검색어를 삭제합니다. <br>
515518
이때 검색어에 대한 Id값은 없고, 최근 검색어 자체를 keyword에 넣어주시면 됩니다.
516519
"""
517520
)
Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,14 @@
11
package ita.tinybite.domain.party.repository;
22

3-
import io.lettuce.core.dynamic.annotation.Param;
4-
import ita.tinybite.domain.chat.enums.ChatRoomType;
53
import ita.tinybite.domain.party.entity.Party;
64
import ita.tinybite.domain.party.enums.PartyCategory;
75
import java.util.List;
86
import java.util.Optional;
97

108
import ita.tinybite.domain.party.enums.PartyStatus;
11-
import org.springframework.data.domain.Page;
12-
import org.springframework.data.domain.Pageable;
139
import org.springframework.data.jpa.repository.JpaRepository;
1410
import org.springframework.data.jpa.repository.Query;
11+
import org.springframework.data.repository.query.Param;
1512
import org.springframework.stereotype.Repository;
1613

1714
@Repository
@@ -25,8 +22,4 @@ public interface PartyRepository extends JpaRepository<Party, Long> {
2522
List<Party> findByPickupLocation_PlaceAndCategory(String place, PartyCategory category);
2623

2724
List<Party> findByHostUserIdAndStatus(Long userId, PartyStatus partyStatus);
28-
29-
Page<Party> findByTitleContaining(String title, Pageable pageable);
30-
31-
Page<Party> findByTitleContainingAndCategory(String title, PartyCategory category, Pageable pageable);
3225
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package ita.tinybite.domain.party.repository;
2+
3+
import ita.tinybite.domain.party.entity.Party;
4+
import ita.tinybite.domain.party.enums.PartyCategory;
5+
import org.springframework.data.domain.Page;
6+
import org.springframework.data.domain.Pageable;
7+
import org.springframework.data.jpa.repository.JpaRepository;
8+
import org.springframework.data.jpa.repository.Query;
9+
import org.springframework.data.repository.query.Param;
10+
import org.springframework.stereotype.Repository;
11+
12+
@Repository
13+
public interface PartySearchRepository extends JpaRepository<Party, Long> {
14+
15+
Page<Party> findByTitleContaining(String title, Pageable pageable);
16+
17+
Page<Party> findByTitleContainingAndCategory(String title, PartyCategory category, Pageable pageable);
18+
19+
@Query(value = """
20+
SELECT p.*
21+
FROM party p
22+
WHERE p.title LIKE CONCAT('%', :title, '%')
23+
ORDER BY (6371000 * acos(
24+
cos(radians(:lat)) * cos(radians(p.pickup_latitude))
25+
* cos(radians(p.pickup_longitude) - radians(:lon))
26+
+ sin(radians(:lat)) * sin(radians(p.pickup_latitude))))
27+
""", countQuery = """
28+
SELECT COUNT(*)
29+
FROM party p
30+
WHERE p.title LIKE CONCAT('%', :title, '%')
31+
""", nativeQuery = true)
32+
Page<Party> findByTitleContainingWithDistance(String title, @Param("lat") Double lat, @Param("lon") Double lon, Pageable pageable);
33+
34+
@Query(value = """
35+
SELECT p.*
36+
FROM party p
37+
WHERE p.
38+
ORDER BY (6371000 * acos(
39+
cos(radians(:lat)) * cos(radians(p.pickup_latitude))
40+
* cos(radians(p.pickup_longitude) - radians(:lon))
41+
+ sin(radians(:lat)) * sin(radians(p.pickup_latitude))))
42+
""", countQuery = """
43+
SELECT COUNT(*)
44+
FROM party p
45+
WHERE p.title LIKE CONCAT('%', :title, '%')
46+
""", nativeQuery = true)
47+
Page<Party> findByTitleContainingAndCategoryWithDistance(String q, Double lat, Double lon, PartyCategory category, Pageable pageable);
48+
}

src/main/java/ita/tinybite/domain/party/service/PartySearchService.java

Lines changed: 47 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
import ita.tinybite.domain.party.enums.ParticipantStatus;
88
import ita.tinybite.domain.party.enums.PartyCategory;
99
import ita.tinybite.domain.party.repository.PartyParticipantRepository;
10-
import ita.tinybite.domain.party.repository.PartyRepository;
10+
import ita.tinybite.domain.party.repository.PartySearchRepository;
11+
import ita.tinybite.global.util.DistanceCalculator;
1112
import lombok.RequiredArgsConstructor;
1213
import org.springframework.data.domain.Page;
1314
import org.springframework.data.domain.PageRequest;
@@ -23,7 +24,7 @@
2324
@RequiredArgsConstructor
2425
public class PartySearchService {
2526

26-
private final PartyRepository partyRepository;
27+
private final PartySearchRepository partySearchRepository;
2728
private final PartyParticipantRepository participantRepository;
2829
private final StringRedisTemplate redisTemplate;
2930
private final SecurityProvider securityProvider;
@@ -35,7 +36,7 @@ private String key(Long userId) {
3536
}
3637

3738
// 파티 검색 조회
38-
public PartyQueryListResponse searchParty(String q, PartyCategory category, int page, int size) {
39+
public PartyQueryListResponse searchParty(String q, PartyCategory category, int page, int size, Double lat, Double lon) {
3940
Long userId = securityProvider.getCurrentUser().getUserId();
4041

4142
// recent_search:{userId}
@@ -45,24 +46,49 @@ public PartyQueryListResponse searchParty(String q, PartyCategory category, int
4546
redisTemplate.opsForZSet().add(key, q, System.currentTimeMillis());
4647

4748
Pageable pageable = PageRequest.of(page, size);
48-
49-
// category가 없을 시에는 ALL로 처리
50-
Page<Party> result = (category == null || category == PartyCategory.ALL)
51-
? partyRepository.findByTitleContaining(q, pageable)
52-
: partyRepository.findByTitleContainingAndCategory(q, category, pageable);
53-
54-
List<PartyCardResponse> partyCardResponseList = result.stream()
55-
.map(party -> {
56-
int currentParticipants = participantRepository
57-
.countByPartyIdAndStatus(party.getId(), ParticipantStatus.APPROVED);
58-
return PartyCardResponse.from(party, currentParticipants);
59-
})
60-
.toList();
61-
62-
return PartyQueryListResponse.builder()
63-
.parties(partyCardResponseList)
64-
.hasNext(result.hasNext())
65-
.build();
49+
List<PartyCardResponse> partyCardResponseList;
50+
51+
// 거리 정보 X
52+
if(lat == null || lon == null) {
53+
// category가 없을 시에는 ALL로 처리
54+
Page<Party> queryResults = (category == null || category == PartyCategory.ALL)
55+
? partySearchRepository.findByTitleContaining(q, pageable)
56+
: partySearchRepository.findByTitleContainingAndCategory(q, category, pageable);
57+
58+
partyCardResponseList = queryResults.stream()
59+
.map(party -> {
60+
int currentParticipants = participantRepository
61+
.countByPartyIdAndStatus(party.getId(), ParticipantStatus.APPROVED);
62+
return PartyCardResponse.from(party, currentParticipants);
63+
})
64+
.toList();
65+
66+
return PartyQueryListResponse.builder()
67+
.parties(partyCardResponseList)
68+
.hasNext(queryResults.hasNext())
69+
.build();
70+
} else {
71+
// 거리 정보 O (lat, lon)
72+
Page<Party> queryResults = (category == null || category == PartyCategory.ALL)
73+
? partySearchRepository.findByTitleContainingWithDistance(q, lat, lon, pageable)
74+
: partySearchRepository.findByTitleContainingAndCategoryWithDistance(q, lat, lon, category, pageable);
75+
76+
partyCardResponseList = queryResults.stream()
77+
.map(party -> {
78+
int currentParticipants = participantRepository
79+
.countByPartyIdAndStatus(party.getId(), ParticipantStatus.APPROVED);
80+
PartyCardResponse res = PartyCardResponse.from(party, currentParticipants);
81+
Double distance = DistanceCalculator.calculateDistance(lat, lon, party.getPickupLocation().getPickupLatitude(), party.getPickupLocation().getPickupLongitude());
82+
res.addDistanceKm(distance);
83+
return res;
84+
})
85+
.toList();
86+
87+
return PartyQueryListResponse.builder()
88+
.parties(partyCardResponseList)
89+
.hasNext(queryResults.hasNext())
90+
.build();
91+
}
6692
}
6793

6894

0 commit comments

Comments
 (0)