Skip to content

Commit 11a9282

Browse files
committed
feat: 대기 확인 기능 추가
1 parent 85600d0 commit 11a9282

4 files changed

Lines changed: 104 additions & 24 deletions

File tree

src/main/java/com/waitit/capstone/domain/queue/controller/QueueController.java

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.waitit.capstone.domain.manager.dto.HostResponse;
44
import com.waitit.capstone.domain.manager.service.HostService;
55
import com.waitit.capstone.domain.queue.QueueMapper;
6+
import com.waitit.capstone.domain.queue.dto.MyQueueStatusResponse;
67
import com.waitit.capstone.domain.queue.dto.QueResponseDto;
78
import com.waitit.capstone.domain.queue.dto.QueueDto;
89
import com.waitit.capstone.domain.queue.dto.QueueRegistrationResponse;
@@ -24,19 +25,16 @@
2425
public class QueueController {
2526
private final QueueService queueService;
2627
private final QueueMapper queueMapper;
27-
private final HostService hostService; // HostService 주입 추가
28+
private final HostService hostService;
2829

2930
@Operation(summary = "대기열 등록", description = "특정 가게의 대기열에 사용자를 등록하고, 등록된 가게의 상세 정보를 함께 반환합니다.")
3031
@PostMapping("/{id}")
3132
public ResponseEntity<QueueRegistrationResponse> registerQueue(@PathVariable Long id, @RequestBody QueueRequest queueRequest, HttpServletRequest request){
32-
// 1. 대기열 등록
3333
QueueDto dto = queueMapper.requestToDto(queueRequest);
3434
int waitingNumber = queueService.registerQueue(id, dto);
3535

36-
// 2. 등록된 가게 정보 조회
3736
HostResponse hostInfo = hostService.getHost(id);
3837

39-
// 3. 최종 응답 생성
4038
QueueRegistrationResponse response = QueueRegistrationResponse.builder()
4139
.message("대기열 등록에 성공했습니다.")
4240
.waitingNumber(waitingNumber)
@@ -59,6 +57,16 @@ public ResponseEntity<QueResponseDto> getMyPosition(
5957
return ResponseEntity.ok(responseDto);
6058
}
6159

60+
/**
61+
* [추가] 나의 실시간 대기 현황 조회 API
62+
*/
63+
@Operation(summary = "나의 실시간 대기 현황 조회", description = "전화번호를 통해 현재 대기 중인 가게의 상세 현황을 조회합니다.")
64+
@GetMapping("/my-status")
65+
public ResponseEntity<MyQueueStatusResponse> getMyQueueStatus(@RequestParam String phoneNumber) {
66+
MyQueueStatusResponse response = queueService.getMyQueueStatus(phoneNumber);
67+
return ResponseEntity.ok(response);
68+
}
69+
6270
@Operation(summary = "대기열 등록 취소", description = "대기열 등록을 취소합니다.")
6371
@DeleteMapping("/{id}")
6472
public ResponseEntity<?> deleteMyQueue(
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.waitit.capstone.domain.queue.dto;
2+
3+
import com.waitit.capstone.domain.manager.dto.HostResponse;
4+
import lombok.Builder;
5+
import lombok.Getter;
6+
7+
import java.time.LocalDateTime;
8+
9+
@Getter
10+
@Builder
11+
public class MyQueueStatusResponse {
12+
private String message;
13+
private boolean isWaiting; // 현재 대기 중인지 여부
14+
private int myWaitingNumber; // 나의 대기 순번 (대기 중이 아닐 경우 0)
15+
private int totalWaitingCount; // 총 대기 인원
16+
private String estimatedWaitTime; // 예상 대기 시간 (예: "15분")
17+
private HostResponse hostInfo; // 내가 대기 중인 가게 정보
18+
}

src/main/java/com/waitit/capstone/domain/queue/service/QueueService.java

Lines changed: 53 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,37 @@
11
package com.waitit.capstone.domain.queue.service;
22

3+
import com.waitit.capstone.domain.manager.HostRepository;
4+
import com.waitit.capstone.domain.manager.dto.HostResponse;
5+
import com.waitit.capstone.domain.manager.HostMapper;
6+
import com.waitit.capstone.domain.queue.dto.MyQueueStatusResponse;
37
import com.waitit.capstone.domain.queue.dto.QueueDto;
8+
import lombok.RequiredArgsConstructor;
49
import org.springframework.stereotype.Service;
510

11+
import java.time.LocalDateTime;
612
import java.util.*;
713
import java.util.concurrent.ConcurrentHashMap;
814

915
@Service
16+
@RequiredArgsConstructor // HostRepository, HostMapper 주입을 위해 추가
1017
public class QueueService {
1118

1219
private final Map<Long, Deque<QueueDto>> waitQueues = new ConcurrentHashMap<>();
1320
private final Map<Long, Map<QueueDto, Long>> postponeQueues = new ConcurrentHashMap<>();
1421

22+
private final HostRepository hostRepository;
23+
private final HostMapper hostMapper;
24+
1525
private static final long POSTPONE_DURATION_MILLIS = 10 * 60 * 1000L; // 10분
26+
private static final int AVG_WAIT_TIME_PER_PERSON = 5; // 1인당 평균 대기시간 (분)
1627

1728
public boolean isHostActive(Long hostId) {
1829
return waitQueues.containsKey(hostId);
1930
}
2031

21-
/**
22-
* [수정] 전화번호 중복 등록 방지 로직 추가
23-
*/
2432
public int registerQueue(Long hostId, QueueDto dto) {
2533
Deque<QueueDto> queue = waitQueues.computeIfAbsent(hostId, k -> new LinkedList<>());
2634

27-
// 중복 검사: 큐에 이미 같은 전화번호가 있는지 확인
2835
boolean isDuplicate = queue.stream()
2936
.anyMatch(userInQueue -> userInQueue.getPhoneNumber().equals(dto.getPhoneNumber()));
3037

@@ -49,7 +56,6 @@ public int getMyPosition(Long hostId, QueueDto myDto) {
4956
return position;
5057
}
5158
}
52-
5359
return 0; // 대기열에 사용자가 없음
5460
}
5561

@@ -95,4 +101,46 @@ public List<QueueDto> getQueueByHostId(Long hostId) {
95101
Deque<QueueDto> queue = waitQueues.get(hostId);
96102
return (queue != null) ? new ArrayList<>(queue) : Collections.emptyList();
97103
}
104+
105+
/**
106+
* [추가] 나의 실시간 대기 현황을 조회합니다.
107+
*/
108+
public MyQueueStatusResponse getMyQueueStatus(String phoneNumber) {
109+
for (Map.Entry<Long, Deque<QueueDto>> entry : waitQueues.entrySet()) {
110+
Long hostId = entry.getKey();
111+
Deque<QueueDto> queue = entry.getValue();
112+
113+
int position = 0;
114+
for (QueueDto userInQueue : queue) {
115+
position++;
116+
if (userInQueue.getPhoneNumber().equals(phoneNumber)) {
117+
// 사용자를 찾음! 이제 정보 조합
118+
HostResponse hostInfo = hostRepository.findHostById(hostId)
119+
.map(hostMapper::hostToDto)
120+
.orElseThrow(() -> new IllegalStateException("대기열에 있는 가게 정보를 찾을 수 없습니다."));
121+
122+
int totalWaitingCount = queue.size();
123+
String estimatedTime = (position * AVG_WAIT_TIME_PER_PERSON) + "분";
124+
125+
return MyQueueStatusResponse.builder()
126+
.message("현재 대기 중입니다.")
127+
.isWaiting(true)
128+
.myWaitingNumber(position)
129+
.totalWaitingCount(totalWaitingCount)
130+
.estimatedWaitTime(estimatedTime)
131+
.hostInfo(hostInfo)
132+
.build();
133+
}
134+
}
135+
}
136+
// 모든 대기열을 찾아도 사용자를 찾지 못함
137+
return MyQueueStatusResponse.builder()
138+
.message("현재 대기 중인 가게가 없습니다.")
139+
.isWaiting(false)
140+
.myWaitingNumber(0)
141+
.totalWaitingCount(0)
142+
.estimatedWaitTime("-")
143+
.hostInfo(null)
144+
.build();
145+
}
98146
}

src/main/java/com/waitit/capstone/domain/recommendation/service/RecommendationService.java

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,12 @@
99
import com.waitit.capstone.domain.recommendation.dto.RecommendationResponse;
1010
import com.waitit.capstone.domain.kakao.service.KakaoMapsService;
1111
import lombok.RequiredArgsConstructor;
12-
import org.redisson.api.RList;
13-
import org.redisson.api.RedissonClient;
1412
import org.springframework.stereotype.Service;
1513
import org.springframework.transaction.annotation.Transactional;
1614

1715
import java.util.List;
1816
import java.util.Optional;
17+
import java.util.Set;
1918
import java.util.stream.Collectors;
2019

2120
@Service
@@ -26,7 +25,6 @@ public class RecommendationService {
2625
private final HostRepository hostRepository;
2726
private final QueueService queueService;
2827
private final KakaoMapsService kakaoMapsService;
29-
private final RedissonClient redissonClient;
3028

3129
private static final int AVG_WAIT_TIME_PER_PERSON = 5;
3230
private static final int SHORT_WAIT_THRESHOLD = 30;
@@ -36,20 +34,29 @@ public class RecommendationService {
3634
private static final String CATEGORY_ATTRACTION = "AT4";
3735

3836
public RecommendationResponse recommend(String phoneNumber, double latitude, double longitude) {
39-
List<Host> activeHosts = hostRepository.findAllByIsActive(true);
37+
// [수정] DB가 아닌, 실제 대기열이 있는 가게 ID 목록을 QueueService에서 가져옴
38+
Set<Long> activeHostIds = queueService.getActiveHostIds();
39+
if (activeHostIds.isEmpty()) {
40+
// 활성화된 가게가 없으면, 대기 중이 아닌 것으로 간주하고 빈 주변 가게 목록 반환
41+
return RecommendationResponse.builder()
42+
.isWaiting(false)
43+
.hostRecommendations(List.of())
44+
.build();
45+
}
46+
List<Host> activeHosts = hostRepository.findAllById(activeHostIds);
4047

48+
// 사용자가 대기 중인 가게를 찾음
4149
Optional<Host> waitingHostOptional = activeHosts.stream()
4250
.filter(host -> {
43-
QueueDto userDto = QueueDto.builder().phoneNumber(phoneNumber).name("user").count(1).build();
44-
return queueService.getMyPosition(host.getId(), userDto) > 0;
51+
// getMyPosition은 0보다 큰 값을 반환하면 대기 중임을 의미
52+
return queueService.getMyPosition(host.getId(), QueueDto.builder().phoneNumber(phoneNumber).build()) > 0;
4553
})
4654
.findFirst();
4755

4856
// 1. 사용자가 대기 중일 경우
4957
if (waitingHostOptional.isPresent()) {
5058
Host waitingHost = waitingHostOptional.get();
51-
QueueDto userDto = QueueDto.builder().phoneNumber(phoneNumber).name("user").count(1).build();
52-
int myPosition = queueService.getMyPosition(waitingHost.getId(), userDto);
59+
int myPosition = queueService.getMyPosition(waitingHost.getId(), QueueDto.builder().phoneNumber(phoneNumber).build());
5360
int estimatedWaitTime = myPosition * AVG_WAIT_TIME_PER_PERSON;
5461

5562
KakaoSearchResponse recommendations;
@@ -60,25 +67,24 @@ public RecommendationResponse recommend(String phoneNumber, double latitude, dou
6067
}
6168
return RecommendationResponse.builder()
6269
.isWaiting(true)
63-
.placeRecommendations(recommendations) // 수정
70+
.placeRecommendations(recommendations)
6471
.build();
6572
}
6673
// 2. 사용자가 대기 중이 아닐 경우
6774
else {
6875
List<NearbyHostResponse> nearbyHosts = activeHosts.stream()
6976
.map(host -> {
7077
double distance = calculateDistance(latitude, longitude, host.getLatitude(), host.getLongitude());
71-
RList<QueueDto> queue = redissonClient.getList("waitList:" + host.getId());
72-
return new Object[]{host, distance, queue.size()};
78+
int waitingCount = queueService.getQueueByHostId(host.getId()).size();
79+
return NearbyHostResponse.from(host, waitingCount, distance);
7380
})
74-
.filter(obj -> (double) obj[1] <= SEARCH_RADIUS)
75-
.sorted((obj1, obj2) -> Double.compare((double) obj1[1], (double) obj2[1]))
76-
.map(obj -> NearbyHostResponse.from((Host) obj[0], (int) obj[2], (double) obj[1]))
81+
.filter(nearbyHost -> nearbyHost.getDistance() <= SEARCH_RADIUS)
82+
.sorted((h1, h2) -> Integer.compare(h1.getDistance(), h2.getDistance()))
7783
.collect(Collectors.toList());
7884

7985
return RecommendationResponse.builder()
8086
.isWaiting(false)
81-
.hostRecommendations(nearbyHosts) // 수정
87+
.hostRecommendations(nearbyHosts)
8288
.build();
8389
}
8490
}

0 commit comments

Comments
 (0)