Skip to content

Conversation

@HyemIin
Copy link
Member

@HyemIin HyemIin commented Jul 25, 2025

작업 요약

  • 예약 상태 변경 시 날짜만 다른 객체 구분 가능하도록 수정

Issue Link

문제점 및 어려움

해결 방안

Reference

Summary by CodeRabbit

  • 버그 수정

    • 예약 조회 시 당일 예약만 정확히 필터링하여 처리하도록 개선되었습니다.
    • 오늘 예약이 없는 경우 예외 메시지가 더 명확하게 안내됩니다.
  • 문서

    • 예약 생성 엔드포인트의 Swagger/OpenAPI 설명이 제거되었습니다.
  • 신규 기능

    • 매장, 사용자, 날짜 범위로 예약을 조회하는 기능이 추가되었습니다.
    • 매장 상세 조회 시 로그인한 사용자의 북마크 여부가 표시됩니다.
    • 예약 상태 변경 시 호출, 확정, 취소 상태를 하나의 통합된 응답으로 제공합니다.
    • 예약 상태 변경 관련 응답에 상세 정보와 메시지가 포함된 새로운 데이터 구조가 도입되었습니다.
  • 기능 개선

    • 예약 상태 처리 로직이 통합되어 권한 검증과 상태 변경이 일관되게 수행됩니다.
    • 예약 호출 API가 비활성화되고, 관련 상태 변경은 통합된 메서드로 대체되었습니다.

- 예약 상태 변경 시 날짜만 다른 객체 구분 가능하도록 수정
@HyemIin HyemIin self-assigned this Jul 25, 2025
@coderabbitai
Copy link

coderabbitai bot commented Jul 25, 2025

## Walkthrough

이번 변경에서는 예약 상태 처리 및 권한 검증 로직을 통합하여 `ReservationService` 내 상태 전환을 단일 메서드로 관리하도록 리팩토링했습니다. 또한, 당일 예약 조회를 위한 저장소 쿼리 메서드가 추가되고, 사용자 API 예약 생성 엔드포인트의 Swagger 어노테이션이 제거되었습니다. 사용자 API의 매장 조회 기능은 OAuth2 사용자 정보를 반영하도록 확장되었습니다.

## Changes

| 파일/경로                                                                                  | 변경 요약                                                                                                         |
|--------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------|
| .../reservation/service/ReservationService.java                                            | 예약 상태 처리 메서드 통합 및 권한 검증 메서드 추가, 당일 예약 조회 메서드 추가, 반환 타입 변경 및 기존 메서드 제거    |
| .../reservation/controller/ReservationController.java (admin-api)                          | `callWaiting` API 메서드 주석 처리로 비활성화, `updateEntry` 반환 타입 변경                                        |
| .../reservation/dto/EntryStatusResponseDto.java                                            | 신규 DTO 클래스 추가 (예약 상태 응답용)                                                                            |
| .../reservation/repository/ReservationRepository.java                                      | 당일 예약 조회용 날짜 범위 필터링 쿼리 메서드 2개 추가 및 LocalDateTime import 추가                                |
| .../reservation/controller/ReservationController.java (user-api)                           | 예약 생성 메서드 Swagger/OpenAPI 어노테이션 제거                                                                  |
| .../store/controller/StoreController.java (user-api)                                      | 매장 조회 메서드에 OAuth2 사용자 정보 파라미터 추가                                                               |
| .../store/dto/StorePageReadDto.java (user-api)                                            | 북마크 여부 필드 추가 및 기본/북마크 포함 DTO 생성 메서드 추가                                                     |
| .../store/service/StoreService.java (user-api)                                            | 매장 조회 서비스 메서드 시그니처에 OAuth2 사용자 정보 파라미터 추가                                               |
| .../store/service/StoreServiceImpl.java (user-api)                                        | OAuth2 사용자 정보 기반 북마크 여부 조회 로직 추가, DTO 반환 방식 분기 처리, 관련 의존성 및 import 추가/정리         |

## Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/#158_Store예약많은순반환타입개선

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@github-actions github-actions bot requested a review from Jjiggu July 25, 2025 07:24
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/service/ReservationService.java (1)

179-190: 날짜 필터링 로직에 타임존 고려사항이 있습니다.

당일 예약 필터링 로직이 올바르게 구현되었지만, 시스템 기본 타임존을 사용하고 있습니다.

-LocalDate today = LocalDate.now();
-LocalDateTime startOfDay = today.atStartOfDay();
-LocalDateTime endOfDay = today.atTime(LocalTime.MAX);
+ZoneId storeTimezone = // 가게별 타임존 또는 시스템 타임존
+LocalDate today = LocalDate.now(storeTimezone);
+LocalDateTime startOfDay = today.atStartOfDay();
+LocalDateTime endOfDay = today.atTime(LocalTime.MAX);

또한 예외 메시지의 다국어 지원을 고려해보세요.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 87cdf4f and e1debac.

📒 Files selected for processing (3)
  • nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/service/ReservationService.java (3 hunks)
  • nowait-app-user-api/src/main/java/com/nowait/applicationuser/reservation/controller/ReservationController.java (0 hunks)
  • nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/reservation/repository/ReservationRepository.java (2 hunks)
💤 Files with no reviewable changes (1)
  • nowait-app-user-api/src/main/java/com/nowait/applicationuser/reservation/controller/ReservationController.java
🔇 Additional comments (3)
nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/service/ReservationService.java (3)

155-155: 사용자 조회 로직 개선이 좋습니다.

memberDetails의 사용자 객체 대신 userRepository에서 직접 조회하도록 변경한 것은 데이터의 일관성과 명확성을 높이는 좋은 개선입니다.


193-193: 상태 업데이트 순서 개선이 적절합니다.

Redis 삭제 후 DB 상태 업데이트를 수행하도록 순서를 변경한 것은 데이터 일관성 측면에서 좋은 개선입니다. Redis 작업 실패 시 DB 상태가 불일치되는 문제를 방지할 수 있습니다.


183-190: 새로운 리포지토리 메서드 활용이 적절합니다.

날짜 범위 기반 예약 조회를 통해 당일 예약만 처리하도록 개선된 점이 PR 목표와 일치합니다. 이는 날짜별 객체 구분 문제를 효과적으로 해결합니다.


Optional<Reservation> findByStore_StoreIdAndUserId(Long storeId, Long userId);

Optional<Reservation> findByStore_StoreIdAndUserIdAndRequestedAtBetween(Long storeId, Long userId, LocalDateTime start, LocalDateTime end);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

잠재적인 데이터 무결성 문제를 확인해주세요.

이 메서드는 Optional<Reservation>을 반환하여 최대 하나의 결과만 기대한다는 것을 나타내지만, 비즈니스 로직상 한 사용자가 같은 날 같은 가게에 여러 예약을 할 수 있는 경우 예상치 못한 결과가 발생할 수 있습니다.

만약 여러 예약이 존재할 가능성이 있다면, List<Reservation>을 반환하거나 추가적인 필터링 조건을 고려해보세요.

다음 스크립트를 실행하여 실제 사용 패턴을 확인해보세요:


🏁 Script executed:

#!/bin/bash
# 같은 날 같은 사용자의 여러 예약 가능성 확인
ast-grep --pattern 'findByStore_StoreIdAndUserIdAndRequestedAtBetween($_, $_, $_, $_)'

Length of output: 88


다음 스크립트로 해당 메서드 선언 위치를 다시 확인해주세요:


🏁 Script executed:

#!/bin/bash
# findByStore_StoreIdAndUserIdAndRequestedAtBetween 메서드 선언 위치 검색
rg 'findByStore_StoreIdAndUserIdAndRequestedAtBetween' -n --glob '*.java'

Length of output: 502


해당 호출부의 처리 방식을 확인하기 위해 아래 스크립트를 실행해주세요:


🏁 Script executed:

#!/bin/bash
# ReservationService에서 Optional<Reservation> 처리 방식 확인
rg -n -C3 "findByStore_StoreIdAndUserIdAndRequestedAtBetween" --glob 'ReservationService.java'

Length of output: 1086


Service 로직의 전체 흐름을 확인하기 위해 호출부 전후를 더 넓게 출력해보세요:


🏁 Script executed:

#!/bin/bash
# ReservationService.java 170~200 라인 출력
sed -n '170,200p' nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/service/ReservationService.java

Length of output: 1235


단일 조회 가정 검증 및 처리 로직 수정 필요

현재 findByStore_StoreIdAndUserIdAndRequestedAtBetweenOptional<Reservation>을 반환하도록 정의되어 있어, 동일한 매장·사용자·날짜 범위에 2건 이상의 예약이 존재할 경우 Spring Data JPA에서 NonUniqueResultException이 발생할 수 있습니다.
비즈니스 요구사항에 따라 아래 중 하나로 보완하세요:

  • 복수 예약을 정상 처리해야 한다면
    • 리포지토리 메서드 반환 타입을 List<Reservation>으로 변경
    • 서비스 계층에서 반환된 리스트를 순회/검증하도록 수정
  • 단일 예약만 처리할 의도라면
    findFirstByStore_StoreIdAndUserIdAndRequestedAtBetweenOrderByRequestedAtAsc(...) 등으로 메서드명을 변경해 하나만 조회
  • 도메인 레벨에서 storeId+userId+requestedAt 조합의 유니크 제약 조건을 추가해 1:1 예약만 허용

점검 및 수정이 필요한 위치:

  • nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/reservation/repository/ReservationRepository.java: 메서드 시그니처
  • nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/service/ReservationService.java (181–187라인): 조회 후 orElseThrow(...) 처리 로직
🤖 Prompt for AI Agents
In
nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/reservation/repository/ReservationRepository.java
at line 22, the method findByStore_StoreIdAndUserIdAndRequestedAtBetween returns
Optional<Reservation> which can cause NonUniqueResultException if multiple
reservations exist for the same store, user, and date range. To fix this, either
change the return type to List<Reservation> to handle multiple results or rename
the method to
findFirstByStore_StoreIdAndUserIdAndRequestedAtBetweenOrderByRequestedAtAsc to
ensure only one result is fetched. Additionally, update the service layer in
nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/service/ReservationService.java
around lines 181-187 to handle the new return type accordingly, either by
iterating over the list or handling the single result properly.

HyemIin added 2 commits July 25, 2025 17:36
- 예약 상태 변경 시 날짜만 다른 객체 구분 가능하도록 수정
@HyemIin HyemIin merged commit 1262326 into develop Jul 25, 2025
1 check was pending
@HyemIin HyemIin deleted the feature/#158_Store예약많은순반환타입개선 branch July 25, 2025 09:02
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🔭 Outside diff range comments (1)
nowait-app-user-api/src/main/java/com/nowait/applicationuser/store/service/StoreServiceImpl.java (1)

137-143: Redis 대기 수 조회 로직에 치명적인 버그 존재

redisTemplate.opsForZSet().zCard(key) 호출 결과가 waitingSize 변수에 할당되지 않아서 모든 매장의 대기 수가 0으로 표시됩니다.

다음과 같이 수정해야 합니다:

		String key = "waiting:" + storeId;
		Long waitingSize = 0L;
		try {
-			redisTemplate.opsForZSet().zCard(key);
+			waitingSize = redisTemplate.opsForZSet().zCard(key);
		} catch (Exception e) {
			waitingSize = 0L; // Redis 접근 실패 시 0으로 처리
		}
♻️ Duplicate comments (1)
nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/reservation/repository/ReservationRepository.java (1)

22-30: 이전 리뷰 코멘트와 동일한 데이터 무결성 문제

새로 추가된 두 메서드 모두 Optional<Reservation>을 반환하여 단일 결과를 가정하고 있지만, 동일한 매장·사용자·날짜 범위에 여러 예약이 존재할 경우 NonUniqueResultException이 발생할 수 있습니다.

특히 24-30라인의 findByStore_StoreIdAndUserIdAndStatusInAndRequestedAtBetween 메서드는 여러 상태를 조건으로 하므로, 더욱 복수 결과가 나올 가능성이 높습니다.

비즈니스 요구사항에 따라 다음 중 하나로 수정하세요:

  • 복수 예약 처리가 필요하면 List<Reservation> 반환
  • 단일 예약만 처리할 의도라면 findFirstBy...OrderBy... 패턴 사용
  • 도메인 레벨에서 유니크 제약 조건 추가
🧹 Nitpick comments (2)
nowait-app-user-api/src/main/java/com/nowait/applicationuser/store/dto/StorePageReadDto.java (1)

31-87: 기능은 올바르나 코드 중복 개선 필요

북마크 상태 처리 기능이 올바르게 구현되었으나, 두 팩토리 메서드 간에 상당한 코드 중복이 존재합니다.

다음과 같이 리팩토링하여 중복을 제거할 수 있습니다:

-	public static StorePageReadDto fromEntity(Store store, List<StoreImageUploadResponse> allImages, String departmentName, Long waitingCount) {
-
-		StoreImageUploadResponse profile = allImages.stream()
-			.filter(image -> image.getImageType() == ImageType.PROFILE)
-			.findFirst()
-			.orElse(null);
-
-		List<StoreImageUploadResponse> banners = allImages.stream()
-			.filter(image -> image.getImageType() == ImageType.BANNER)
-			.toList();
-
-		return StorePageReadDto.builder()
-			.createdAt(store.getCreatedAt())
-			.storeId(store.getStoreId())
-			.waitingCount(waitingCount)
-			.departmentId(store.getDepartmentId())
-			.departmentName(departmentName)
-			.name(store.getName())
-			.location(store.getLocation())
-			.description(store.getDescription())
-			.isActive(store.getIsActive())
-			.deleted(store.getDeleted())
-			.profileImage(profile)
-			.bannerImages(banners)
-			.isBookmarked(false)
-			.build();
-	}
-
-	public static StorePageReadDto fromEntityWithBookmark(
-		Store store, List<StoreImageUploadResponse> allImages, String departmentName, Long waitingCount, Boolean isBookmarked
-	) {
-
-		StoreImageUploadResponse profile = allImages.stream()
-			.filter(image -> image.getImageType() == ImageType.PROFILE)
-			.findFirst()
-			.orElse(null);
-
-		List<StoreImageUploadResponse> banners = allImages.stream()
-			.filter(image -> image.getImageType() == ImageType.BANNER)
-			.toList();
-
-		return StorePageReadDto.builder()
-			.createdAt(store.getCreatedAt())
-			.storeId(store.getStoreId())
-			.waitingCount(waitingCount)
-			.departmentId(store.getDepartmentId())
-			.departmentName(departmentName)
-			.name(store.getName())
-			.location(store.getLocation())
-			.description(store.getDescription())
-			.isActive(store.getIsActive())
-			.deleted(store.getDeleted())
-			.profileImage(profile)
-			.bannerImages(banners)
-			.isBookmarked(isBookmarked)
-			.build();
-	}
+	public static StorePageReadDto fromEntity(Store store, List<StoreImageUploadResponse> allImages, String departmentName, Long waitingCount) {
+		return fromEntityWithBookmark(store, allImages, departmentName, waitingCount, false);
+	}
+
+	public static StorePageReadDto fromEntityWithBookmark(
+		Store store, List<StoreImageUploadResponse> allImages, String departmentName, Long waitingCount, Boolean isBookmarked
+	) {
+		StoreImageUploadResponse profile = allImages.stream()
+			.filter(image -> image.getImageType() == ImageType.PROFILE)
+			.findFirst()
+			.orElse(null);
+
+		List<StoreImageUploadResponse> banners = allImages.stream()
+			.filter(image -> image.getImageType() == ImageType.BANNER)
+			.toList();
+
+		return StorePageReadDto.builder()
+			.createdAt(store.getCreatedAt())
+			.storeId(store.getStoreId())
+			.waitingCount(waitingCount)
+			.departmentId(store.getDepartmentId())
+			.departmentName(departmentName)
+			.name(store.getName())
+			.location(store.getLocation())
+			.description(store.getDescription())
+			.isActive(store.getIsActive())
+			.deleted(store.getDeleted())
+			.profileImage(profile)
+			.bannerImages(banners)
+			.isBookmarked(isBookmarked)
+			.build();
+	}
nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/controller/ReservationController.java (1)

55-69: 기능 통합으로 인한 엔드포인트 비활성화

callWaiting 엔드포인트가 주석 처리되어 있습니다. 서비스 레이어에서 processEntryStatus 메서드로 기능이 통합된 것으로 보입니다.

완전히 제거할 예정이라면 주석 대신 삭제를 고려해보세요.

-	// @PatchMapping("/admin/{storeId}/call/{userId}")
-	// @Operation(summary = "예약팀 호출", description = "특정 예약에 대한 호출 진행(호출하는 순간 10분 타임어택)")
-	// @ApiResponse(responseCode = "200", description = "예약팀 상태 변경 :  WAITING -> CALLING")
-	// public ResponseEntity<?> callWaiting(@PathVariable Long storeId,
-	// 	@PathVariable String userId,
-	// 	@AuthenticationPrincipal MemberDetails memberDetails) {
-	// 	CallingWaitingResponseDto response = reservationService.callWaiting(storeId, userId, memberDetails);
-	// 	return ResponseEntity
-	// 		.status(HttpStatus.OK)
-	// 		.body(
-	// 			ApiUtils.success(
-	// 				response
-	// 			)
-	// );
-	// }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e1debac and 5d2d29f.

📒 Files selected for processing (8)
  • nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/controller/ReservationController.java (3 hunks)
  • nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/dto/EntryStatusResponseDto.java (1 hunks)
  • nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/service/ReservationService.java (4 hunks)
  • nowait-app-user-api/src/main/java/com/nowait/applicationuser/store/controller/StoreController.java (3 hunks)
  • nowait-app-user-api/src/main/java/com/nowait/applicationuser/store/dto/StorePageReadDto.java (2 hunks)
  • nowait-app-user-api/src/main/java/com/nowait/applicationuser/store/service/StoreService.java (1 hunks)
  • nowait-app-user-api/src/main/java/com/nowait/applicationuser/store/service/StoreServiceImpl.java (5 hunks)
  • nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/reservation/repository/ReservationRepository.java (2 hunks)
✅ Files skipped from review due to trivial changes (1)
  • nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/dto/EntryStatusResponseDto.java
🔇 Additional comments (15)
nowait-app-user-api/src/main/java/com/nowait/applicationuser/store/service/StoreService.java (2)

12-12: 적절한 import 추가

사용자 인증 정보를 처리하기 위한 CustomOAuth2User import가 적절히 추가되었습니다.


18-18: 사용자별 매장 정보 조회를 위한 메서드 시그니처 업데이트

OAuth2 사용자 정보를 매개변수로 추가하여 북마크 상태와 같은 사용자별 기능을 지원할 수 있도록 개선되었습니다.

nowait-app-user-api/src/main/java/com/nowait/applicationuser/store/controller/StoreController.java (2)

6-6: 필요한 import 문 추가

@AuthenticationPrincipal과 CustomOAuth2User import가 적절히 추가되어 사용자 인증 정보 처리가 가능합니다.

Also applies to: 16-16


53-53: 사용자 인증 정보 통합이 적절히 구현됨

@AuthenticationPrincipal 어노테이션을 통해 인증된 사용자 정보가 자동 주입되고, 서비스 레이어로 올바르게 전달됩니다.

Also applies to: 58-58

nowait-app-user-api/src/main/java/com/nowait/applicationuser/store/dto/StorePageReadDto.java (1)

29-29: 북마크 상태 필드 추가

사용자별 북마크 상태를 나타내는 Boolean 필드가 적절히 추가되었습니다.

nowait-app-user-api/src/main/java/com/nowait/applicationuser/store/service/StoreServiceImpl.java (3)

19-19: 필요한 의존성 추가가 적절히 구현됨

사용자 인증 및 북마크 기능을 위한 import문과 repository 필드들이 올바르게 추가되었습니다.

Also applies to: 36-41, 53-54


124-127: 사용자 인증 정보 처리가 올바르게 구현됨

CustomOAuth2User 매개변수 추가와 데이터베이스에서 사용자 정보 조회 로직이 적절한 예외 처리와 함께 구현되었습니다.


150-154: 북마크 상태 확인 로직이 올바르게 구현됨

사용자와 매장 간의 북마크 관계를 확인하고 적절한 DTO 팩토리 메서드를 사용하여 반환하는 로직이 정확히 구현되었습니다.

nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/reservation/repository/ReservationRepository.java (1)

3-3: 적절한 import 추가

LocalDateTime import가 새로운 날짜 범위 쿼리 메서드를 위해 적절히 추가되었습니다.

nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/controller/ReservationController.java (2)

16-16: 적절한 DTO import 추가

새로운 응답 구조를 위한 EntryStatusResponseDto import가 적절히 추가되었습니다.


79-79: 응답 구조 개선으로 API 일관성 향상

updateEntry 메서드의 반환 타입이 단순 String에서 구조화된 EntryStatusResponseDto로 변경되어 API 응답의 일관성과 정보 제공이 개선되었습니다.

nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/service/ReservationService.java (4)

3-5: 날짜 처리를 위한 적절한 import 추가

새로운 날짜 기반 예약 조회 로직을 위해 LocalDate, LocalDateTime, LocalTime import가 적절히 추가되었습니다.


16-16: 리팩토링을 위한 새로운 DTO 및 엔티티 import

EntryStatusResponseDtoStore 엔티티 import가 새로운 통합 메서드를 위해 적절히 추가되었습니다.

Also applies to: 29-29


140-147: 권한 검증 로직의 중앙화

권한 검증 로직이 authorize 메서드로 중앙화되어 코드 중복이 제거되고 일관성이 향상되었습니다. 슈퍼 관리자 권한과 매장 소유자 권한을 적절히 검증합니다.


198-206: CALLING 상태 시 기존 예약을 업데이트하지 않고 새 엔티티를 생성하고 있습니다

processEntryStatus 메서드의 CALLING 분기에서 Redis 상태만 변경한 뒤, DB에는 항상 새로운 Reservation 객체를 빌드·저장하도록 구현되어 있습니다. 이 방식이 의도된 로직인지 확인 부탁드립니다.

  • 파일: nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/service/ReservationService.java
  • 메서드: processEntryStatus (CALLING 분기)
  • 주요 코드:
    // Redis 상태만 CALLING으로 변경
    waitingRedisRepository.setWaitingStatus(storeId, userId, ReservationStatus.CALLING.name());
    // …
    // DB에 무조건 새로 저장
    Store store = storeRepository.getReferenceById(storeId);
    reservation = Reservation.builder()
        .store(store)
        .user(user)
        .partySize(partySize)
        .requestedAt(now)
        .status(ReservationStatus.CALLING)
        .build();
    reservationRepository.save(reservation);
    

기존 WAITING 상태의 예약을 findById 등으로 조회해 setStatus(CALLING) 후 저장하는 형태로 변경하는 것이 더 적절할 수 있습니다. 이 로직이 의도된 것인지 확인 부탁드립니다.

Comment on lines +150 to 163
private Reservation findTodayReservation(Long storeId, String userId) {
LocalDateTime startOfDay = LocalDate.now().atStartOfDay();
LocalDateTime endOfDay = LocalDate.now().atTime(LocalTime.MAX);

return reservationRepository
.findByStore_StoreIdAndUserIdAndStatusInAndRequestedAtBetween(
storeId,
Long.valueOf(userId),
List.of(ReservationStatus.WAITING, ReservationStatus.CALLING),
startOfDay,
endOfDay
)
.orElseThrow(() -> new IllegalArgumentException("오늘 날짜의 예약이 존재하지 않습니다."));
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

당일 예약 조회 로직의 잠재적 문제

findTodayReservation 메서드에서 리포지토리의 findByStore_StoreIdAndUserIdAndStatusInAndRequestedAtBetween 메서드를 사용하고 있는데, 이 메서드는 Optional<Reservation>을 반환하므로 동일 조건의 복수 예약 존재 시 NonUniqueResultException이 발생할 수 있습니다.

또한 158라인의 상태 필터가 WAITING, CALLING으로 제한되어 있어, 다른 상태의 당일 예약은 조회되지 않습니다.

리포지토리 메서드 수정이 필요하거나, 비즈니스 로직상 복수 예약이 가능하다면 다음과 같이 수정을 고려하세요:

-		return reservationRepository
-			.findByStore_StoreIdAndUserIdAndStatusInAndRequestedAtBetween(
-				storeId,
-				Long.valueOf(userId),
-				List.of(ReservationStatus.WAITING, ReservationStatus.CALLING),
-				startOfDay,
-				endOfDay
-			)
-			.orElseThrow(() -> new IllegalArgumentException("오늘 날짜의 예약이 존재하지 않습니다."));
+		List<Reservation> reservations = reservationRepository
+			.findByStore_StoreIdAndUserIdAndStatusInAndRequestedAtBetween(
+				storeId,
+				Long.valueOf(userId),
+				List.of(ReservationStatus.WAITING, ReservationStatus.CALLING),
+				startOfDay,
+				endOfDay
+			);
+		if (reservations.isEmpty()) {
+			throw new IllegalArgumentException("오늘 날짜의 예약이 존재하지 않습니다.");
+		}
+		return reservations.get(0); // 또는 적절한 선택 로직

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In
nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/service/ReservationService.java
around lines 150 to 163, the method findTodayReservation uses a repository
method that returns Optional<Reservation>, which can cause
NonUniqueResultException if multiple reservations match the criteria. Also, the
status filter only includes WAITING and CALLING, excluding other possible
statuses. To fix this, modify the repository method to return a list of
reservations instead of Optional, or adjust the business logic to handle
multiple reservations. Update the method to retrieve all matching reservations
for the day and handle them appropriately, such as returning a list or selecting
one based on additional criteria.

Comment on lines +172 to 242
public EntryStatusResponseDto processEntryStatus(
Long storeId,
String userId,
MemberDetails member,
ReservationStatus newStatus
) {
User manager = authorize(storeId, member);
User user = userRepository.findById(Long.valueOf(userId)).orElseThrow(UserNotFoundException::new);

String message = null;
Reservation reservation;

switch (newStatus) {
case CALLING:
// 1) Redis 상태 검사 & 변경
String curr = waitingRedisRepository.getWaitingStatus(storeId, userId);
if (!ReservationStatus.WAITING.name().equals(curr)) {
throw new IllegalStateException("이미 호출되었거나 없는 예약입니다.");
}
waitingRedisRepository.setWaitingStatus(storeId, userId, ReservationStatus.CALLING.name());

// 2) 파티 인원, 호출 시각
Integer partySize = waitingRedisRepository.getWaitingPartySize(storeId, userId);
LocalDateTime now = LocalDateTime.now();

// 3) DB에 무조건 새로 저장
Store store = storeRepository.getReferenceById(storeId);
reservation = Reservation.builder()
.store(store)
.user(user)
.partySize(partySize)
.requestedAt(now)
.status(ReservationStatus.CALLING)
.build();
reservationRepository.save(reservation);

break;

case CONFIRMED:
case CANCELLED:
// 1) Redis에서 제거
waitingRedisRepository.deleteWaiting(storeId, userId);

// 2) 오늘 날짜 예약 조회 & 상태 변경
reservation = findTodayReservation(storeId, userId);
reservation.updateStatus(newStatus);
reservationRepository.save(reservation);

// 3) 완료/취소 메시지
message = String.format(
"%s님의 예약이 %s 처리되었습니다.",
user.getNickname(),
newStatus == ReservationStatus.CONFIRMED ? "입장 완료" : "입장 취소"
);
break;

default:
throw new IllegalArgumentException("지원하지 않는 상태입니다: " + newStatus);
}
// 1. DB status 업데이트
Reservation reservation = reservationRepository.findByStore_StoreIdAndUserId(storeId, Long.valueOf(userId))
.orElseThrow(() -> new IllegalArgumentException("해당 예약이 존재하지 않습니다."));
reservation.updateStatus(status);
// 2. Redis에서 삭제
waitingRedisRepository.deleteWaiting(storeId, userId);

// 메시지 동적 반환
String action = (status == ReservationStatus.CONFIRMED) ? "입장 완료" : "입장 취소";
return user.getNickname() + "님의 예약이 " + action + " 처리되었습니다.";

// 5) 공통 DTO 반환
return EntryStatusResponseDto.builder()
.id( reservation.getId().toString())
.userId( userId)
.partySize( reservation.getPartySize())
.userName( user.getNickname())
.createdAt( reservation.getRequestedAt())
.status( reservation.getStatus().name())
.message( message)
.build();
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

상태 처리 통합 메서드의 로직 검증 필요

processEntryStatus 메서드는 여러 상태를 통합 처리하는 좋은 리팩토링이지만, 몇 가지 검토가 필요합니다:

  1. CALLING 상태 처리 (184-208라인): 새로운 Reservation 엔티티를 무조건 생성하고 있는데, 기존 예약과의 관계나 중복 처리 로직이 명확하지 않습니다.

  2. 트랜잭션 일관성: Redis 상태 변경과 DB 저장 사이의 실패 시나리오에 대한 롤백 처리가 고려되지 않았습니다.

  3. 동시성 문제: Redis 상태 확인(187라인)과 변경(191라인) 사이의 race condition 가능성이 있습니다.

다음과 같은 개선을 고려해보세요:

case CALLING:
	// 1) Redis 상태 검사 & 변경을 원자적으로 처리
-	String curr = waitingRedisRepository.getWaitingStatus(storeId, userId);
-	if (!ReservationStatus.WAITING.name().equals(curr)) {
-		throw new IllegalStateException("이미 호출되었거나 없는 예약입니다.");
-	}
-	waitingRedisRepository.setWaitingStatus(storeId, userId, ReservationStatus.CALLING.name());
+	if (!waitingRedisRepository.compareAndSetWaitingStatus(storeId, userId, 
+		ReservationStatus.WAITING.name(), ReservationStatus.CALLING.name())) {
+		throw new IllegalStateException("이미 호출되었거나 없는 예약입니다.");
+	}

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In
nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/service/ReservationService.java
between lines 172 and 242, the processEntryStatus method creates a new
Reservation entity unconditionally for CALLING status without checking for
existing reservations, risking duplicates. To fix this, add logic to check for
an existing reservation before creating a new one and update it if necessary.
Also, wrap the Redis status update and database save operations in a single
transaction or use compensating actions to ensure consistency on failure.
Finally, to prevent race conditions between Redis status check and update,
implement atomic operations or locking mechanisms around these Redis calls.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants