Skip to content

Conversation

@1winhyun
Copy link
Member

@1winhyun 1winhyun commented Jan 13, 2026

🔀 변경 내용

  • 스탬프 기능을 구현했습니다.

✅ 작업 항목

  • 리뷰 생성 시 스탬프를 같이 부여할 수 있도록 기능을 구현했습니다. (Stamp 테이블 추가)
    현재는 ocr 기능이 구현되어있지 않기에 현재 ocr 인증이 성공했다는 가정하에 진행했습니다.
    또한 리뷰 생성 시 반환값에 해당 유저의 스탬프 수를 추가했습니다.
  • User 엔티티에 rewardNeeded 컬럼을 추가하였으며 스탬프 10개 획득 시 해당 컬럼이 true로 바뀌도록 구현했습니다.
  • 관리자는 rewardNeeded가 true인, 보상 지급이 필요한 유저들을 조회하는 기능을 구현했습니다.
  • 관리자가 사용자에게 보상을 지급하는 기능을 구현했습니다. (Reward 테이블 추가) 보상 지급 시 해당 유저의 rewardNeeded는 다시 false로 전환.
  • 사용자의 스탬프 개수 및 스탬프 받은 리뷰들을 조회하는 기능을 구현했습니다.
  • 사용자의 스탬프 보상을 조회하는 기능을 구현했습니다.

📸 스크린샷 (선택)

리뷰 생성 시 stamp도 생성되며 반환값에 현재 사용자의 스탬프 개수 반환

image

관리자의 보상 지급이 필요한 유저 조회 기능

image

관리자의 보상 지급 기능

image

사용자의 스탬프 개수 및 스탬프 받은 리뷰 조회 기능

image

사용자의 스탬프 보상 조회 기능

image

📎 참고 이슈

관련 이슈 번호 #63

Summary by CodeRabbit

  • 새로운 기능
    • 스탬프·보상 시스템 도입: 리뷰 작성 시 스탬프 지급, 스탬프 10개 이상이면 보상 대상 표시
    • 개인용 스탬프 정보 조회(API): 스탬프 개수 및 관련 리뷰 목록 확인
    • 개인용 보상 내역 조회(API): 받은 보상 목록 확인(이미지·지급일 포함)
    • 관리자용 API: 보상이 필요한 사용자 목록 조회 및 개별 보상 지급(보상 이미지 업로드)
    • 리뷰 검증 시 스탬프 자동 부여 및 보상 흐름 연동

✏️ Tip: You can customize this high-level summary in your review settings.

@1winhyun 1winhyun self-assigned this Jan 13, 2026
@1winhyun 1winhyun added ♻️Refactor 리팩토링 ✨Feat 새로운 기능 개발 labels Jan 13, 2026
@1winhyun 1winhyun linked an issue Jan 13, 2026 that may be closed by this pull request
@coderabbitai
Copy link

coderabbitai bot commented Jan 13, 2026

Walkthrough

스탬프 부여·조회, 보상 발급 흐름과 관련된 스탬프·보상 도메인(엔티티/레포/서비스/컨트롤러/매퍼/DTO) 및 리뷰 연동(스탬프 부여/카운트 포함) 기능이 추가되었습니다.

Changes

Cohort / File(s) 변경 사항
Manager 보상 기능
src/main/java/com/campus/campus/domain/manager/application/dto/request/RewardRequest.java, src/main/java/com/campus/campus/domain/manager/application/dto/response/StampRewardNeededUserListResponse.java, src/main/java/com/campus/campus/domain/manager/application/mapper/ManagerMapper.java, src/main/java/com/campus/campus/domain/manager/application/service/ManagerService.java, src/main/java/com/campus/campus/domain/manager/presentation/ManagerController.java, src/main/java/com/campus/campus/domain/manager/presentation/ManagerResponseCode.java
보상 관련 요청/응답 DTO 추가; ManagerMapper에 응답/엔티티 변환 메서드 추가; ManagerService에 보상 필요 사용자 조회 및 보상 지급 로직 추가; Controller에 관련 GET/POST 엔드포인트 추가; 응답 코드 상수 추가
Stamp 도메인 모델
src/main/java/com/campus/campus/domain/stamp/domain/entity/Stamp.java, src/main/java/com/campus/campus/domain/stamp/domain/entity/Reward.java, src/main/java/com/campus/campus/domain/stamp/domain/repository/StampRepository.java, src/main/java/com/campus/campus/domain/stamp/domain/repository/RewardRepository.java
Stamp/Reward JPA 엔티티 신규 추가 및 User/Review 관계 정의; StampRepository에 존재/카운트/커스텀 조회 쿼리 추가; RewardRepository에 사용자별 보상 조회 메서드 추가
Stamp 서비스 및 표현층
src/main/java/com/campus/campus/domain/stamp/application/service/StampService.java, src/main/java/com/campus/campus/domain/stamp/application/mapper/StampMapper.java, src/main/java/com/campus/campus/domain/stamp/application/dto/response/RewardResponse.java, src/main/java/com/campus/campus/domain/stamp/application/dto/response/StampInfoResponse.java, src/main/java/com/campus/campus/domain/stamp/application/dto/response/StampReviewResponse.java, src/main/java/com/campus/campus/domain/stamp/presentation/StampController.java, src/main/java/com/campus/campus/domain/stamp/presentation/StampResponseCode.java
스탬프 부여(중복 체크) 및 조회, 보상 조회 메서드 추가; StampMapper에 변환/생성 메서드 추가; 응답 DTO 3종 추가; StampController 및 응답 코드 enum 추가
Review 통합
src/main/java/com/campus/campus/domain/review/application/dto/response/ReviewCreateResult.java, src/main/java/com/campus/campus/domain/review/application/mapper/ReviewMapper.java, src/main/java/com/campus/campus/domain/review/application/service/ReviewService.java, src/main/java/com/campus/campus/domain/review/domain/entity/Review.java
ReviewCreateResult에 numberOfUserStamp 필드 추가 및 매퍼 시그니처 업데이트; ReviewService에 StampService/StampRepository 의존성 추가 및 OCR 검증 시 스탬프 부여 호출 통합; Review 엔티티에 verify() 메서드 추가
User 엔티티 확장 및 레포 변경
src/main/java/com/campus/campus/domain/user/domain/entity/User.java, src/main/java/com/campus/campus/domain/user/domain/repository/UserRepository.java
User 엔티티에 rewardNeeded 필드 및 업데이트 메서드 추가; UserRepository에 rewardNeeded=true 사용자와 스탬프 카운트를 함께 조회하는 JPQL 메서드 추가

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant ReviewService
    participant StampService
    participant StampRepository
    participant UserRepository
    participant ManagerService
    participant ManagerController

    User->>ReviewService: 리뷰 작성 요청
    ReviewService->>ReviewService: OCR 검증 -> review.verify()
    ReviewService->>StampService: grantStampForReview(user, review)
    StampService->>StampRepository: existsByReview(review)?
    alt 없음
        StampService->>StampRepository: save(new Stamp(user, review))
        StampService->>StampRepository: countByUser(user)
        alt count >= 10 && user.rewardNeeded == false
            StampService->>UserRepository: updateRewardNeeded(user, true)
        end
    end
    ReviewService->>StampRepository: countByUser(user) 조회
    ReviewService->>User: ReviewCreateResult(스탬프수 포함) 반환

    User->>ManagerController: GET /managers/rewards/necessary-users
    ManagerController->>ManagerService: getStampRewardNeededUserList()
    ManagerService->>UserRepository: findRewardNeededUsersWithStampCount()
    ManagerService->>ManagerController: 사용자 목록 반환

    User->>ManagerController: POST /managers/reward/grant/{userId}
    ManagerController->>ManagerService: grantRewardToUser(userId, rewardRequest)
    ManagerService->>UserRepository: findByIdAndDeletedAtIsNull(userId)
    ManagerService->>ManagerService: create Reward, save via RewardRepository
    ManagerService->>UserRepository: updateRewardNeeded(user, false)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • 1224kang
  • jjaeroong

Poem

🐇 도장 톡톡 찍어 모은 열,
작은 발자국에 보상이 빛나네.
관리자의 손으로 선물 전해지면,
캠퍼스에 웃음이 활짝 번지리.
함께 뛰어든 우리, 축하해라! 🎁

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 3.03% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목은 스탬프 기능 구현이라는 변경사항의 핵심을 명확하게 전달하며, 전체 변경 사항을 잘 요약하고 있습니다.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

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

Comment @coderabbitai help to get the list of available commands and usage tips.

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

🤖 Fix all issues with AI agents
In
@src/main/java/com/campus/campus/domain/manager/presentation/ManagerController.java:
- Around line 83-90: The grantRewardToUser endpoint is missing @RequestBody on
the RewardRequest parameter and the @PostMapping path lacks a leading slash;
update the @PostMapping from "reward/grant/{userId}" to "/reward/grant/{userId}"
and annotate the RewardRequest parameter in the grantRewardToUser method with
@RequestBody so the request body is parsed correctly before calling
managerService.grantRewardToUser(userId, rewardRequest).

In
@src/main/java/com/campus/campus/domain/review/application/dto/response/ReviewCreateResult.java:
- Line 9: Field name NumberOfUserStamp in the record ReviewCreateResult violates
Java camelCase conventions and breaks JSON key consistency; rename the field to
numberOfUserStamp, update all references/usages of ReviewCreateResult
(constructors, callers, builders, tests), and if you rely on explicit JSON names
add or update the JSON property annotation (e.g., @JsonProperty) to ensure the
serialized key is "numberOfUserStamp" where needed.
🧹 Nitpick comments (10)
src/main/java/com/campus/campus/domain/manager/presentation/ManagerResponseCode.java (1)

16-18: GRANT_REWARD_SUCCESS에 201 상태 코드 고려

보상 지급 시 새로운 Reward 엔티티가 생성되므로, 다른 생성 작업들(NOTICE_CREATE_SUCCESS, POST_CREATE_SUCCESS)과의 일관성을 위해 HttpStatus.CREATED(201) 사용을 고려해볼 수 있습니다.

현재 200도 동작상 문제는 없으며, "지급"이라는 액션 관점에서는 200도 적절할 수 있습니다.

선택적 수정 제안
-	GRANT_REWARD_SUCCESS(200, HttpStatus.OK, "스탬프 보상 지급에 성공했습니다.");
+	GRANT_REWARD_SUCCESS(201, HttpStatus.CREATED, "스탬프 보상 지급에 성공했습니다.");
src/main/java/com/campus/campus/domain/review/application/mapper/ReviewMapper.java (1)

90-96: DTO 필드명 수정 시 빌더 호출도 함께 변경 필요

ReviewCreateResult의 필드명이 numberOfUserStamp(camelCase)로 수정되면 이 빌더 호출도 함께 변경해야 합니다.

♻️ DTO 수정 후 적용할 변경
 public ReviewCreateResult toReviewCreateResult(boolean isFirstReviewOfPlace, long userReviewCountOfPlace,
 	int numberOfUserStamp) {
 	return ReviewCreateResult.builder()
 		.isFirstReviewOfPlace(isFirstReviewOfPlace)
 		.userReviewCountOfPlace((int)userReviewCountOfPlace)
-		.NumberOfUserStamp(numberOfUserStamp)
+		.numberOfUserStamp(numberOfUserStamp)
 		.build();
 }
src/main/java/com/campus/campus/domain/review/application/service/ReviewService.java (2)

83-88: 리뷰 저장 전에 스탬프가 부여되는 순서 검토 필요

현재 reviewreviewRepository.save(review) (line 75)로 저장된 후 스탬프가 부여되므로 트랜잭션 내에서 문제는 없습니다. 그러나 review.verify()가 영속성 컨텍스트 내에서 변경을 추적하는지 확인이 필요합니다.

또한, OCR 검증이 항상 true로 설정되어 있어 모든 리뷰에 스탬프가 부여됩니다. OCR 구현 시 이 부분을 수정해야 한다는 점을 TODO 또는 이슈로 추적하는 것을 권장합니다.


291-291: Java 변수 명명 규칙 위반: PascalCase → camelCase

NumberOfStamp는 Java 변수 명명 규칙에 따라 numberOfStamp로 변경해야 합니다.

♻️ 수정 제안
-		int NumberOfStamp = stampRepository.countByUser(user);
+		int numberOfStamp = stampRepository.countByUser(user);

-		return reviewMapper.toReviewCreateResult(isFirstReviewOfPlace, count, NumberOfStamp);
+		return reviewMapper.toReviewCreateResult(isFirstReviewOfPlace, count, numberOfStamp);
src/main/java/com/campus/campus/domain/stamp/domain/repository/StampRepository.java (1)

16-16: 반환 타입 일관성 검토: int vs long

ReviewRepository의 다른 count 메서드들(예: countByPlace_PlaceId, countByPlaceAndUser)은 long을 반환합니다. 일관성을 위해 long으로 변경하는 것을 고려해주세요.

♻️ 수정 제안
-	int countByUser(User user);
+	long countByUser(User user);

이 경우 ReviewService.getCreateResult에서도 타입을 맞춰야 합니다:

long numberOfStamp = stampRepository.countByUser(user);
src/main/java/com/campus/campus/domain/stamp/application/mapper/StampMapper.java (1)

14-16: 불필요한 @RequiredArgsConstructor 어노테이션

주입할 의존성(final 필드)이 없으므로 @RequiredArgsConstructor가 불필요합니다.

♻️ 수정 제안
 @Component
-@RequiredArgsConstructor
 public class StampMapper {
src/main/java/com/campus/campus/domain/manager/application/service/ManagerService.java (2)

110-119: N+1 쿼리 문제가 있습니다.

각 사용자마다 stampRepository.countByUser(user)를 호출하여 N+1 쿼리 문제가 발생합니다. 사용자 수가 많아지면 성능 저하가 발생할 수 있습니다.

♻️ 개선 방안

StampRepository에 사용자 ID 목록으로 스탬프 수를 한 번에 조회하는 메서드를 추가하거나, 네이티브 쿼리로 조인하여 한 번에 데이터를 가져오는 것을 고려해 주세요.

예시:

// StampRepository에 추가
@Query("SELECT s.user.id, COUNT(s) FROM Stamp s WHERE s.user IN :users GROUP BY s.user.id")
List<Object[]> countByUsers(@Param("users") List<User> users);

121-131: 보상 지급 전 rewardNeeded 상태 검증이 필요합니다.

현재 구현에서는 rewardNeededfalse인 사용자에게도 보상을 지급할 수 있습니다. 의도하지 않은 중복 보상 지급을 방지하려면 검증 로직을 추가하는 것이 좋습니다.

♻️ 검증 로직 추가 제안
 @Transactional
 public void grantRewardToUser(Long userId, RewardRequest rewardRequest) {
   User user = userRepository.findByIdAndDeletedAtIsNull(userId)
     .orElseThrow(UserNotFoundException::new);
 
+  if (!user.isRewardNeeded()) {
+    throw new RewardNotNeededException(); // 적절한 예외 정의 필요
+  }
+
   Reward reward = managerMapper.createReward(user, rewardRequest);
 
   rewardRepository.save(reward);
 
   user.updateRewardNeeded(false);
 }
src/main/java/com/campus/campus/domain/manager/application/mapper/ManagerMapper.java (1)

66-71: Mapper에서 엔티티 생성 역할에 대한 검토

createReward 메서드는 엔티티를 생성하는 역할을 합니다. Mapper가 DTO 변환뿐 아니라 엔티티 생성도 담당하는 것이 현재 프로젝트의 컨벤션이라면 괜찮지만, 일반적으로 엔티티 생성은 서비스 레이어나 별도의 팩토리에서 처리하는 것이 더 명확한 책임 분리가 될 수 있습니다.

src/main/java/com/campus/campus/domain/stamp/domain/entity/Stamp.java (1)

26-29: @SuperBuilder 사용 필요

BaseEntity@SuperBuilder를 사용하고 있으므로, 상속받는 Stamp 클래스도 @Builder 대신 @SuperBuilder를 사용해야 합니다. 그렇지 않으면 빌더 패턴 사용 시 부모 클래스의 필드(createdAt, updatedAt)를 설정할 수 없습니다.

♻️ 수정 제안
 @Entity
 @Table(name = "stamps")
 @Getter
-@Builder
+@SuperBuilder
 @NoArgsConstructor(access = AccessLevel.PROTECTED)
 @AllArgsConstructor(access = AccessLevel.PRIVATE)
 public class Stamp extends BaseEntity {

SuperBuilder import 추가:

import lombok.experimental.SuperBuilder;
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 71079fe and ca51498.

📒 Files selected for processing (23)
  • src/main/java/com/campus/campus/domain/manager/application/dto/request/RewardRequest.java
  • src/main/java/com/campus/campus/domain/manager/application/dto/response/StampRewardNeededUserListResponse.java
  • src/main/java/com/campus/campus/domain/manager/application/mapper/ManagerMapper.java
  • src/main/java/com/campus/campus/domain/manager/application/service/ManagerService.java
  • src/main/java/com/campus/campus/domain/manager/presentation/ManagerController.java
  • src/main/java/com/campus/campus/domain/manager/presentation/ManagerResponseCode.java
  • src/main/java/com/campus/campus/domain/review/application/dto/response/ReviewCreateResult.java
  • src/main/java/com/campus/campus/domain/review/application/mapper/ReviewMapper.java
  • src/main/java/com/campus/campus/domain/review/application/service/ReviewService.java
  • src/main/java/com/campus/campus/domain/review/domain/entity/Review.java
  • src/main/java/com/campus/campus/domain/stamp/application/dto/response/RewardResponse.java
  • src/main/java/com/campus/campus/domain/stamp/application/dto/response/StampInfoResponse.java
  • src/main/java/com/campus/campus/domain/stamp/application/dto/response/StampReviewResponse.java
  • src/main/java/com/campus/campus/domain/stamp/application/mapper/StampMapper.java
  • src/main/java/com/campus/campus/domain/stamp/application/service/StampService.java
  • src/main/java/com/campus/campus/domain/stamp/domain/entity/Reward.java
  • src/main/java/com/campus/campus/domain/stamp/domain/entity/Stamp.java
  • src/main/java/com/campus/campus/domain/stamp/domain/repository/RewardRepository.java
  • src/main/java/com/campus/campus/domain/stamp/domain/repository/StampRepository.java
  • src/main/java/com/campus/campus/domain/stamp/presentation/StampController.java
  • src/main/java/com/campus/campus/domain/stamp/presentation/StampResponseCode.java
  • src/main/java/com/campus/campus/domain/user/domain/entity/User.java
  • src/main/java/com/campus/campus/domain/user/domain/repository/UserRepository.java
🧰 Additional context used
🧬 Code graph analysis (14)
src/main/java/com/campus/campus/domain/review/application/dto/response/ReviewCreateResult.java (5)
src/main/java/com/campus/campus/domain/review/application/dto/request/ReviewRequest.java (1)
  • ReviewRequest (14-51)
src/main/java/com/campus/campus/domain/review/application/dto/response/ReviewCreateResponse.java (1)
  • Builder (5-12)
src/main/java/com/campus/campus/domain/review/application/dto/response/ReviewResponse.java (1)
  • Builder (8-19)
src/main/java/com/campus/campus/domain/review/application/dto/response/WriteReviewResponse.java (1)
  • Builder (7-18)
src/main/java/com/campus/campus/domain/review/application/dto/response/ReviewRankingResponse.java (1)
  • ReviewRankingResponse (3-8)
src/main/java/com/campus/campus/domain/manager/presentation/ManagerResponseCode.java (2)
src/main/java/com/campus/campus/domain/councilnotice/presentation/NoticeResponseCode.java (1)
  • Getter (10-23)
src/main/java/com/campus/campus/domain/councilpost/presentation/StudentCouncilPostResponseCode.java (1)
  • Getter (10-27)
src/main/java/com/campus/campus/domain/stamp/presentation/StampResponseCode.java (8)
src/main/java/com/campus/campus/domain/councilpost/presentation/StudentCouncilPostResponseCode.java (1)
  • Getter (10-27)
src/main/java/com/campus/campus/domain/council/presentation/StudentCouncilResponseCode.java (1)
  • Getter (10-26)
src/main/java/com/campus/campus/domain/councilnotice/presentation/NoticeResponseCode.java (1)
  • Getter (10-23)
src/main/java/com/campus/campus/domain/user/presentation/UserResponseCode.java (1)
  • Getter (10-22)
src/main/java/com/campus/campus/domain/mail/presentation/EmailVerificationResponseCode.java (1)
  • Getter (10-19)
src/main/java/com/campus/campus/domain/place/presentation/PlaceResponseCode.java (1)
  • Getter (10-22)
src/main/java/com/campus/campus/global/oci/presentation/PresignedUrlResponseCode.java (1)
  • Getter (10-19)
src/main/java/com/campus/campus/domain/user/application/exception/ErrorCode.java (1)
  • Getter (10-22)
src/main/java/com/campus/campus/domain/review/application/service/ReviewService.java (2)
src/main/java/com/campus/campus/domain/review/application/dto/request/ReviewRequest.java (1)
  • ReviewRequest (14-51)
src/main/java/com/campus/campus/domain/review/domain/repository/ReviewRepository.java (1)
  • ReviewRepository (15-44)
src/main/java/com/campus/campus/domain/manager/application/dto/request/RewardRequest.java (7)
src/main/java/com/campus/campus/domain/councilpost/application/dto/request/PostRequest.java (1)
  • PostRequest (16-66)
src/main/java/com/campus/campus/domain/council/application/dto/request/StudentCouncilChangePasswordRequest.java (1)
  • StudentCouncilChangePasswordRequest (9-24)
src/main/java/com/campus/campus/domain/review/application/dto/request/ReviewRequest.java (1)
  • ReviewRequest (14-51)
src/main/java/com/campus/campus/domain/mail/application/dto/request/EmailVerificationRequest.java (1)
  • EmailVerificationRequest (6-11)
src/main/java/com/campus/campus/domain/user/application/dto/request/CampusNicknameUpdateRequest.java (1)
  • CampusNicknameUpdateRequest (6-11)
src/main/java/com/campus/campus/global/util/jwt/application/dto/request/TokenReissueRequest.java (1)
  • TokenReissueRequest (6-11)
src/main/java/com/campus/campus/domain/councilnotice/application/dto/request/NoticeRequest.java (1)
  • NoticeRequest (5-10)
src/main/java/com/campus/campus/domain/user/domain/entity/User.java (1)
src/main/java/com/campus/campus/global/entity/BaseEntity.java (1)
  • Getter (16-29)
src/main/java/com/campus/campus/domain/stamp/presentation/StampController.java (2)
src/main/java/com/campus/campus/global/util/jwt/presentation/JwtController.java (1)
  • RestController (19-42)
src/main/java/com/campus/campus/domain/user/presentation/UserController.java (1)
  • RestController (21-52)
src/main/java/com/campus/campus/domain/stamp/application/service/StampService.java (1)
src/main/java/com/campus/campus/domain/user/application/exception/UserNotFoundException.java (1)
  • UserNotFoundException (5-9)
src/main/java/com/campus/campus/domain/user/domain/repository/UserRepository.java (1)
src/main/java/com/campus/campus/domain/council/domain/repository/StudentCouncilRepository.java (2)
  • existsByLoginIdAndManagerApprovedIsTrueAndDeletedAtIsNull (46-46)
  • findByLoginIdAndManagerApprovedIsTrueAndDeletedAtIsNull (13-13)
src/main/java/com/campus/campus/domain/manager/application/service/ManagerService.java (4)
src/main/java/com/campus/campus/domain/manager/application/exception/ManagerNotFoundException.java (1)
  • ManagerNotFoundException (5-9)
src/main/java/com/campus/campus/domain/manager/application/exception/PasswordNotCorrectException.java (1)
  • PasswordNotCorrectException (5-9)
src/main/java/com/campus/campus/domain/user/application/exception/UserNotFoundException.java (1)
  • UserNotFoundException (5-9)
src/main/java/com/campus/campus/domain/user/application/service/UserService.java (1)
  • Service (27-80)
src/main/java/com/campus/campus/domain/stamp/application/dto/response/StampReviewResponse.java (4)
src/main/java/com/campus/campus/domain/review/application/dto/response/ReviewResponse.java (1)
  • Builder (8-19)
src/main/java/com/campus/campus/domain/review/application/dto/response/WriteReviewResponse.java (1)
  • Builder (7-18)
src/main/java/com/campus/campus/domain/review/application/dto/response/ReviewCreateResponse.java (1)
  • Builder (5-12)
src/main/java/com/campus/campus/domain/review/application/dto/response/ReviewRankingResponse.java (1)
  • ReviewRankingResponse (3-8)
src/main/java/com/campus/campus/domain/stamp/domain/entity/Stamp.java (1)
src/main/java/com/campus/campus/global/entity/BaseEntity.java (1)
  • Getter (16-29)
src/main/java/com/campus/campus/domain/stamp/domain/repository/StampRepository.java (2)
src/main/java/com/campus/campus/domain/review/domain/repository/ReviewRepository.java (1)
  • ReviewRepository (15-44)
src/main/java/com/campus/campus/domain/councilpost/domain/repository/StudentCouncilPostRepository.java (1)
  • StudentCouncilPostRepository (18-316)
src/main/java/com/campus/campus/domain/stamp/domain/repository/RewardRepository.java (5)
src/main/java/com/campus/campus/domain/review/domain/repository/ReviewRepository.java (1)
  • ReviewRepository (15-44)
src/main/java/com/campus/campus/domain/councilnotice/domain/repository/StudentCouncilNoticeRepository.java (1)
  • StudentCouncilNoticeRepository (13-27)
src/main/java/com/campus/campus/domain/school/domain/repository/SchoolRepository.java (1)
  • SchoolRepository (9-11)
src/main/java/com/campus/campus/domain/place/domain/repository/LikedPlacesRepository.java (1)
  • LikedPlacesRepository (14-30)
src/main/java/com/campus/campus/domain/place/domain/repository/PlaceRepository.java (1)
  • PlaceRepository (10-15)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (22)
src/main/java/com/campus/campus/domain/manager/application/dto/request/RewardRequest.java (1)

6-11: LGTM!

기존 프로젝트의 request DTO 패턴(@NotBlank, @Schema 조합)을 잘 따르고 있습니다. 구조가 간결하고 명확합니다.

src/main/java/com/campus/campus/domain/stamp/domain/repository/RewardRepository.java (1)

10-12: LGTM!

Spring Data JPA 명명 규칙을 올바르게 따르고 있으며, 최신 보상을 먼저 보여주기 위한 내림차순 정렬이 적절합니다.

향후 보상 데이터가 많아질 경우 페이지네이션 도입을 고려해볼 수 있습니다.

src/main/java/com/campus/campus/domain/stamp/presentation/StampResponseCode.java (1)

10-19: LGTM!

프로젝트의 기존 ResponseCode enum 패턴(PresignedUrlResponseCode, UserResponseCode 등)과 완벽하게 일치합니다. 구조와 명명 규칙이 일관성 있게 작성되었습니다.

src/main/java/com/campus/campus/domain/review/domain/entity/Review.java (1)

55-57: LGTM!

verify() 메서드가 간결하고 단일 책임 원칙을 잘 따르고 있습니다. 이미 검증된 리뷰에 대해 다시 호출해도 멱등성이 보장되어 안전합니다.

src/main/java/com/campus/campus/domain/user/domain/repository/UserRepository.java (1)

35-36: LGTM!

Spring Data JPA 명명 규칙을 올바르게 따르고 있으며, 기존 StudentCouncilRepository의 패턴(findByLoginIdAndManagerApprovedIsTrueAndDeletedAtIsNull)과 일관성을 유지합니다.

src/main/java/com/campus/campus/domain/user/domain/entity/User.java (2)

53-55: LGTM!

@Builder.Default@SuperBuilder와 함께 올바르게 사용하고 있습니다. 필드 명명(camelCase)과 컬럼 명명(snake_case) 규칙을 일관성 있게 따르고 있습니다.


89-92: LGTM!

기존 updateProfile, updateCampusNickname 메서드와 일관된 패턴을 따릅니다.

src/main/java/com/campus/campus/domain/stamp/application/dto/response/StampReviewResponse.java (1)

7-17: LGTM!

필드 명명 규칙과 Swagger @Schema 어노테이션이 적절하게 적용되었습니다. reviewCreatedAt의 ISO-8601 형식 예시도 적합합니다.

src/main/java/com/campus/campus/domain/stamp/application/dto/response/RewardResponse.java (1)

7-17: LGTM!

Record 구조가 적절하며 Swagger 어노테이션을 통한 API 문서화가 잘 되어 있습니다.

src/main/java/com/campus/campus/domain/stamp/domain/repository/StampRepository.java (1)

18-26: LGTM!

JOIN FETCH를 사용하여 N+1 문제를 방지하고, createdAt DESC 정렬로 최신 스탬프부터 조회하는 쿼리가 적절하게 구현되었습니다.

src/main/java/com/campus/campus/domain/stamp/application/mapper/StampMapper.java (1)

25-33: toStampReviewResponse에서 잠재적 NPE 가능성 검토

stamp.getReview().getPlace().getPlaceName() 체인에서 review 또는 place가 null일 경우 NPE가 발생할 수 있습니다. StampRepository.findAllByUserWithReviewAndPlace의 JOIN FETCH가 항상 이들을 로드하므로 현재는 안전하지만, 다른 곳에서 이 메서드가 호출될 경우를 대비해 null 체크를 고려해볼 수 있습니다.

src/main/java/com/campus/campus/domain/stamp/domain/entity/Reward.java (1)

21-39: 리뷰 코멘트 재검토 결과: 수정 불필요

Reward 엔티티의 @Builder 사용은 올바릅니다. BaseEntitycreatedAt, updatedAt 필드는 @CreatedDate, @LastModifiedDate 어노테이션으로 Spring Data Auditing이 자동으로 관리하므로, 빌더에 포함될 필요가 없습니다. 이는 Review, LikedPlace, StudentCouncilNotice 등 동일하게 BaseEntity를 상속받는 다른 엔티티들에서도 일관되게 사용되는 패턴입니다.

src/main/java/com/campus/campus/domain/manager/application/dto/response/StampRewardNeededUserListResponse.java (1)

5-17: LGTM!

DTO 구조가 깔끔하고 @Schema 어노테이션을 통한 API 문서화도 잘 되어 있습니다. rewardNeeded 필드는 이 엔드포인트가 rewardNeeded=true인 사용자만 반환하므로 항상 true일 것으로 보이지만, 명시적으로 포함하는 것이 API 응답의 명확성을 높입니다.

src/main/java/com/campus/campus/domain/manager/application/mapper/ManagerMapper.java (1)

53-64: LGTM!

닉네임 폴백 로직(campusNicknamenickname)이 적절하게 구현되어 있습니다.

src/main/java/com/campus/campus/domain/stamp/application/dto/response/StampInfoResponse.java (1)

7-13: LGTM!

스탬프 정보 응답 DTO가 간결하고 명확하게 구현되어 있습니다. @Schema 어노테이션을 통한 문서화도 적절합니다.

src/main/java/com/campus/campus/domain/manager/presentation/ManagerController.java (1)

74-81: LGTM!

보상이 필요한 사용자 목록 조회 엔드포인트가 적절하게 구현되어 있습니다. @PreAuthorize를 통한 권한 검증도 잘 적용되어 있습니다.

src/main/java/com/campus/campus/domain/stamp/domain/entity/Stamp.java (1)

35-41: LGTM!

엔티티 관계 매핑이 적절합니다. User와의 ManyToOne 관계와 Review와의 OneToOne 관계가 올바르게 설정되어 있으며, FetchType.LAZY를 사용한 것도 성능 최적화 측면에서 좋은 선택입니다.

src/main/java/com/campus/campus/domain/stamp/application/service/StampService.java (3)

46-58: LGTM!

스탬프 정보 조회 로직이 잘 구현되어 있습니다. soft delete 지원을 위한 findByIdAndDeletedAtIsNull 사용과 적절한 예외 처리가 포함되어 있습니다.


60-68: LGTM!

보상 목록 조회 로직이 일관성 있게 구현되어 있습니다.


40-43: 코드 스니펫이 현재 저장소의 코드와 일치하지 않습니다

검토 의견에 표시된 코드(40-43줄)가 실제 저장소의 코드와 다릅니다.

현재 코드는 이미 올바른 로직을 구현하고 있습니다:

if (currentStampCount % 10 == 0 && currentStampCount!=0 && !user.isRewardNeeded()) {
    user.updateRewardNeeded(true);
}

currentStampCount % 10 == 0 조건을 사용하여 스탬프 개수가 10의 배수일 때만 플래그를 설정하므로, 보상 후 재설정되었을 때 즉시 다시 플래그되는 문제는 발생하지 않습니다. 또한 currentStampCount != 0 가드 조건도 포함되어 있습니다.

Likely an incorrect or invalid review comment.

src/main/java/com/campus/campus/domain/stamp/presentation/StampController.java (2)

24-30: LGTM!

기존 컨트롤러 패턴(UserController, JwtController)과 일관된 구조로 구현되어 있습니다.


32-38: LGTM!

보상 목록 조회 엔드포인트가 올바르게 구현되어 있습니다.

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

🤖 Fix all issues with AI agents
In
@src/main/java/com/campus/campus/domain/stamp/application/service/StampService.java:
- Around line 40-43: The current logic in StampService (checking
currentStampCount % 10 == 0 && currentStampCount != 0 && !user.isRewardNeeded())
can drop reward opportunities; change it to always record a pending reward when
a new 10-stamp threshold is reached rather than gating on user.isRewardNeeded():
replace the update call with something like user.incrementPendingRewardCount()
(or add a pendingRewardCount int field on User with getters/setters and
incrementPendingRewardCount method) and call that when currentStampCount % 10 ==
0 && currentStampCount != 0; remove the dependency on user.isRewardNeeded() here
and ensure User has methods to consume/reset pendingRewardCount when the admin
processes rewards.
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ca51498 and 315da79.

📒 Files selected for processing (1)
  • src/main/java/com/campus/campus/domain/stamp/application/service/StampService.java
🧰 Additional context used
🧬 Code graph analysis (1)
src/main/java/com/campus/campus/domain/stamp/application/service/StampService.java (2)
src/main/java/com/campus/campus/domain/user/application/exception/UserNotFoundException.java (1)
  • UserNotFoundException (5-9)
src/main/java/com/campus/campus/domain/review/domain/entity/Review.java (1)
  • Entity (22-57)
🔇 Additional comments (2)
src/main/java/com/campus/campus/domain/stamp/application/service/StampService.java (2)

46-58: LGTM!

User 조회 시 soft delete 체크, 스탬프 카운트 조회, 연관 데이터 페칭 로직이 적절합니다.


60-68: LGTM!

getStampInfo와 일관된 패턴으로 사용자 조회 및 보상 목록 조회가 구현되어 있습니다.

Comment on lines +40 to +43
int currentStampCount = stampRepository.countByUser(user);
if (currentStampCount % 10 == 0 && currentStampCount!=0 && !user.isRewardNeeded()) {
user.updateRewardNeeded(true);
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

보상 임계값 누락 가능성 있는 로직 버그

현재 조건 !user.isRewardNeeded()로 인해 사용자가 보상을 받기 전에 스탬프를 계속 모으면 보상 기회를 놓칠 수 있습니다.

예시 시나리오:

  1. 사용자가 10개 스탬프 도달 → rewardNeeded = true
  2. 관리자가 보상 지급 전에 사용자가 20개 스탬프 도달
  3. 20개 시점에서 !user.isRewardNeeded()false이므로 조건 불충족
  4. 관리자가 첫 번째 보상 지급 → rewardNeeded = false로 리셋
  5. 사용자는 20개 스탬프 보상을 놓치게 됨

보상 횟수를 별도로 추적하는 방식을 고려해보세요.

♻️ 제안하는 수정 방안
 		int currentStampCount = stampRepository.countByUser(user);
-		if (currentStampCount % 10 == 0 && currentStampCount!=0 && !user.isRewardNeeded()) {
+		int expectedRewardCount = currentStampCount / 10;
+		int actualRewardCount = rewardRepository.countByUser(user);
+		if (expectedRewardCount > actualRewardCount && !user.isRewardNeeded()) {
 			user.updateRewardNeeded(true);
 		}

또는 User 엔티티에 pendingRewardCount 필드를 추가하여 누적된 미지급 보상 횟수를 추적하는 방법도 있습니다.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
int currentStampCount = stampRepository.countByUser(user);
if (currentStampCount % 10 == 0 && currentStampCount!=0 && !user.isRewardNeeded()) {
user.updateRewardNeeded(true);
}
int currentStampCount = stampRepository.countByUser(user);
int expectedRewardCount = currentStampCount / 10;
int actualRewardCount = rewardRepository.countByUser(user);
if (expectedRewardCount > actualRewardCount && !user.isRewardNeeded()) {
user.updateRewardNeeded(true);
}
🤖 Prompt for AI Agents
In
@src/main/java/com/campus/campus/domain/stamp/application/service/StampService.java
around lines 40 - 43, The current logic in StampService (checking
currentStampCount % 10 == 0 && currentStampCount != 0 && !user.isRewardNeeded())
can drop reward opportunities; change it to always record a pending reward when
a new 10-stamp threshold is reached rather than gating on user.isRewardNeeded():
replace the update call with something like user.incrementPendingRewardCount()
(or add a pendingRewardCount int field on User with getters/setters and
incrementPendingRewardCount method) and call that when currentStampCount % 10 ==
0 && currentStampCount != 0; remove the dependency on user.isRewardNeeded() here
and ensure User has methods to consume/reset pendingRewardCount when the admin
processes rewards.

Copy link
Contributor

@jjaeroong jjaeroong left a comment

Choose a reason for hiding this comment

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

고생하셨습니다!


return rewardNeededUsers.stream()
.map(user -> {
int stampCount = stampRepository.countByUser(user);
Copy link
Contributor

Choose a reason for hiding this comment

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

이 부분 혹시 n+1 괜찮을까요??

Copy link
Member Author

Choose a reason for hiding this comment

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

아 N+1 있네요... 수정했습니다!!

return CommonResponse.success(ManagerResponseCode.CERTIFY_REQUEST_LIST_SUCCESS, responses);
}

@GetMapping("/reward/need/users")
Copy link
Contributor

Choose a reason for hiding this comment

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

reward에도 복수형 취하는 것이 좋을 것 같습니다!
그리고 need가 필요하다라는 의미로 넣으신 것 같은데

/rewards/형용사-users
와 같은 방법은 어떨까요?!

사실 가벼운 부분이어서 넘어가셔도 좋습니다!

Copy link
Member Author

Choose a reason for hiding this comment

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

수정하는 것이 좋을 것 같아요!!

Copy link
Contributor

@1224kang 1224kang left a comment

Choose a reason for hiding this comment

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

고생하셨습니다.! Ocr 오늘 내로 완료해서 스탬프부분까지 한꺼번에 수정해서 올리도록 하겠습니다 .!

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

🤖 Fix all issues with AI agents
In
`@src/main/java/com/campus/campus/domain/manager/application/service/ManagerService.java`:
- Line 46: ManagerService에 주입된 미사용 의존성 `stampRepository`를 제거하세요: 클래스 필드 `private
final StampRepository stampRepository;`를 삭제하고, 생성자(또는 Lombok
`@RequiredArgsConstructor에` 의해 주입되는 경우 해당 필드 제거로 생성자 파라미터에서 빠지게)에서
`StampRepository` 관련 파라미터를 제거한 뒤 사용되지 않는 import 문도 정리합니다. 변경 후 컴파일 에러가 발생하면 관련
참조(예: 필드 사용 여부)를 재확인해 모두 제거하세요.

In
`@src/main/java/com/campus/campus/domain/user/domain/repository/UserRepository.java`:
- Line 36: 인터페이스 UserRepository의 미사용 메서드
findAllByRewardNeededIsTrueAndDeletedAtIsNull()를 제거하세요: UserRepository에서 해당 메서드
선언을 삭제하고, 프로젝트 전체에서 참조 여부를 재확인한 후 빌드 및 테스트를 실행해 컴파일 오류가 없는지 검증합니다; 또한 메서드 삭제로 인해
남는 불필요한 import나 문서 주석이 있으면 함께 정리하세요.
♻️ Duplicate comments (1)
src/main/java/com/campus/campus/domain/manager/presentation/ManagerController.java (1)

83-83: 경로에 선행 슬래시(/)가 누락되었습니다.

@PostMapping("reward/grant/{userId}")에 선행 슬래시가 없어 다른 엔드포인트(/rewards/necessary-users, /approve/council/{councilId} 등)와 일관성이 맞지 않습니다. 또한 리소스 명명 규칙상 rewards(복수형)로 통일하는 것이 좋습니다.

🐛 수정 제안
-@PostMapping("reward/grant/{userId}")
+@PostMapping("/rewards/grant/{userId}")
🧹 Nitpick comments (1)
src/main/java/com/campus/campus/domain/user/domain/repository/UserRepository.java (1)

38-46: Object[] 반환 타입 대신 타입 안전한 Projection 사용을 권장합니다.

현재 List<Object[]> 반환 타입은 서비스 레이어에서 형변환이 필요하며, 컴파일 타임 타입 체크가 불가능합니다. Spring Data JPA의 인터페이스 기반 Projection 또는 DTO Projection을 사용하면 타입 안전성을 확보할 수 있습니다.

♻️ 인터페이스 Projection 예시
// Projection interface 정의
public interface UserStampCountProjection {
    User getUser();
    Long getStampCount();
}

// Repository 메서드 변경
`@Query`("""
    SELECT u as user, COUNT(s) as stampCount
    FROM User u
    LEFT JOIN Stamp s ON s.user = u
    WHERE u.rewardNeeded = true 
      AND u.deletedAt IS NULL
    GROUP BY u
    """)
List<UserStampCountProjection> findRewardNeededUsersWithStampCount();
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 315da79 and 6dd074c.

📒 Files selected for processing (5)
  • src/main/java/com/campus/campus/domain/manager/application/service/ManagerService.java
  • src/main/java/com/campus/campus/domain/manager/presentation/ManagerController.java
  • src/main/java/com/campus/campus/domain/review/application/dto/response/ReviewCreateResult.java
  • src/main/java/com/campus/campus/domain/review/application/mapper/ReviewMapper.java
  • src/main/java/com/campus/campus/domain/user/domain/repository/UserRepository.java
🧰 Additional context used
🧬 Code graph analysis (1)
src/main/java/com/campus/campus/domain/review/application/dto/response/ReviewCreateResult.java (2)
src/main/java/com/campus/campus/domain/review/application/dto/response/ReviewCreateResponse.java (1)
  • Builder (5-12)
src/main/java/com/campus/campus/domain/review/application/service/ReviewService.java (1)
  • getCreateResult (276-286)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (6)
src/main/java/com/campus/campus/domain/review/application/dto/response/ReviewCreateResult.java (1)

6-11: LGTM!

이전 리뷰에서 지적된 NumberOfUserStampnumberOfUserStamp 네이밍 규칙 위반이 올바르게 수정되었습니다. Java camelCase 규칙과 다른 필드들과의 일관성이 유지됩니다.

src/main/java/com/campus/campus/domain/review/application/mapper/ReviewMapper.java (1)

90-97: LGTM!

메서드 시그니처와 빌더 사용이 ReviewCreateResult 레코드의 새로운 필드와 올바르게 정렬되었습니다.

ReviewService.getCreateResult에서 numberOfUserStamp 파라미터가 올바르게 전달되고 있습니다.

src/main/java/com/campus/campus/domain/manager/application/service/ManagerService.java (2)

110-121: LGTM!

N+1 문제가 findRewardNeededUsersWithStampCount() 쿼리를 통해 적절히 해결되었습니다. 단일 쿼리로 사용자와 스탬프 카운트를 함께 조회하여 성능이 개선되었습니다.


123-133: LGTM!

트랜잭션 처리와 보상 지급 로직이 올바르게 구현되었습니다. JPA의 dirty checking을 통해 user.updateRewardNeeded(false) 변경사항이 자동으로 반영됩니다.

src/main/java/com/campus/campus/domain/manager/presentation/ManagerController.java (2)

74-81: LGTM!

스탬프 보상이 필요한 유저 목록 조회 엔드포인트가 적절하게 구현되었습니다. @PreAuthorize를 통한 권한 검사도 올바르게 적용되어 있습니다.


86-89: LGTM!

이전 리뷰에서 지적된 @RequestBody@Valid 어노테이션이 올바르게 추가되었습니다.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

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

🤖 Fix all issues with AI agents
In
`@src/main/java/com/campus/campus/domain/manager/application/service/ManagerService.java`:
- Around line 121-131: The grantRewardToUser method currently grants rewards
regardless of the user's rewardNeeded state; update it to validate that the user
actually needs a reward before creating/saving one by either (A) changing the
retrieval to
userRepository.findByIdAndDeletedAtIsNullAndRewardNeededTrue(userId) and
throwing UserNotFoundException or a new BusinessException if absent, or (B)
after fetching the User via userRepository.findByIdAndDeletedAtIsNull(userId),
check user.isRewardNeeded() (or getRewardNeeded()) and throw a suitable
exception if false; only then call managerMapper.createReward(...),
rewardRepository.save(reward) and user.updateRewardNeeded(false).
🧹 Nitpick comments (1)
src/main/java/com/campus/campus/domain/manager/application/service/ManagerService.java (1)

108-119: Object[] 대신 타입 안전한 프로젝션 사용을 권장합니다.

현재 Object[]를 사용하여 쿼리 결과를 캐스팅하는 방식은 타입 안전성이 낮고, 쿼리 변경 시 런타임 오류가 발생할 수 있습니다. 프로젝션 인터페이스나 DTO 생성자를 JPQL에서 직접 사용하는 것이 더 안전합니다.

♻️ 프로젝션 인터페이스 예시
// 프로젝션 인터페이스 정의
public interface RewardNeededUserProjection {
    User getUser();
    Long getStampCount();
}

// Repository에서 사용
List<RewardNeededUserProjection> findRewardNeededUsersWithStampCount();

// Service에서 사용
public List<StampRewardNeededUserListResponse> getStampRewardNeededUserList() {
    return userRepository.findRewardNeededUsersWithStampCount().stream()
        .map(projection -> managerMapper.toStampRewardNeededUserListResponse(
            projection.getUser(), 
            projection.getStampCount().intValue()))
        .toList();
}
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6dd074c and ea9f0d0.

📒 Files selected for processing (2)
  • src/main/java/com/campus/campus/domain/manager/application/service/ManagerService.java
  • src/main/java/com/campus/campus/domain/user/domain/repository/UserRepository.java
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/java/com/campus/campus/domain/user/domain/repository/UserRepository.java
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (1)
src/main/java/com/campus/campus/domain/manager/application/service/ManagerService.java (1)

44-45: LGTM!

이전 리뷰에서 지적된 미사용 stampRepository 의존성이 제거되었습니다.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment on lines +121 to +131
@Transactional
public void grantRewardToUser(Long userId, RewardRequest rewardRequest) {
User user = userRepository.findByIdAndDeletedAtIsNull(userId)
.orElseThrow(UserNotFoundException::new);

Reward reward = managerMapper.createReward(user, rewardRequest);

rewardRepository.save(reward);

user.updateRewardNeeded(false);
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

보상 지급 전 rewardNeeded 상태 검증이 필요합니다.

현재 구현은 rewardNeededfalse인 사용자에게도 보상을 지급할 수 있습니다. 비즈니스 로직상 보상이 필요한 사용자(rewardNeeded = true)에게만 보상을 지급해야 한다면, 검증 로직을 추가하거나 레포지토리 쿼리에서 조건을 추가하는 것이 좋습니다.

🛠️ 권장 수정 예시
 `@Transactional`
 public void grantRewardToUser(Long userId, RewardRequest rewardRequest) {
     User user = userRepository.findByIdAndDeletedAtIsNull(userId)
         .orElseThrow(UserNotFoundException::new);

+    if (!user.isRewardNeeded()) {
+        throw new IllegalStateException("해당 사용자는 보상 대상이 아닙니다.");
+    }
+
     Reward reward = managerMapper.createReward(user, rewardRequest);

     rewardRepository.save(reward);

     user.updateRewardNeeded(false);
 }
🤖 Prompt for AI Agents
In
`@src/main/java/com/campus/campus/domain/manager/application/service/ManagerService.java`
around lines 121 - 131, The grantRewardToUser method currently grants rewards
regardless of the user's rewardNeeded state; update it to validate that the user
actually needs a reward before creating/saving one by either (A) changing the
retrieval to
userRepository.findByIdAndDeletedAtIsNullAndRewardNeededTrue(userId) and
throwing UserNotFoundException or a new BusinessException if absent, or (B)
after fetching the User via userRepository.findByIdAndDeletedAtIsNull(userId),
check user.isRewardNeeded() (or getRewardNeeded()) and throw a suitable
exception if false; only then call managerMapper.createReward(...),
rewardRepository.save(reward) and user.updateRewardNeeded(false).

@1winhyun 1winhyun merged commit 4109247 into dev Jan 14, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨Feat 새로운 기능 개발 ♻️Refactor 리팩토링

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEAT] 스탬프 기능 구현

4 participants