Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: 스터디 수료 쿠폰을 스터디 당 하나만 가지도록 변경 #851

Merged
merged 3 commits into from
Jan 28, 2025

Conversation

Sangwook02
Copy link
Member

@Sangwook02 Sangwook02 commented Jan 26, 2025

🌱 관련 이슈

📌 작업 내용 및 특이사항

📝 참고사항

📚 기타

Summary by CodeRabbit

Summary by CodeRabbit

  • 새로운 기능

    • 쿠폰 생성 및 발급 로직 개선
    • 중복 쿠폰 생성 방지 기능 추가
    • 스터디 완료 기반 쿠폰 관리 기능 강화
    • 쿠폰 리포지토리에 새로운 조회 메서드 추가
  • 개선 사항

    • 쿠폰 조회 및 생성 메서드 최적화
    • 데이터베이스에서 쿠폰의 고유성 보장
  • 기타

    • 금액 관련 상수 FIVE_THOUSAND 추가

@Sangwook02 Sangwook02 self-assigned this Jan 26, 2025
@Sangwook02 Sangwook02 requested a review from a team as a code owner January 26, 2025 12:38
Copy link

coderabbitai bot commented Jan 26, 2025

개요

Walkthrough

이 풀 리퀘스트는 쿠폰 관리 시스템의 개선에 중점을 두고 있습니다. Money 클래스에 새로운 상수를 추가하고, CouponService의 쿠폰 생성 로직을 리팩토링했습니다. 주요 변경사항은 스터디별로 하나의 쿠폰만 생성되도록 보장하는 것으로, 중복 쿠폰 생성 문제를 해결합니다.

Changes

파일 변경 요약
src/main/java/com/gdschongik/gdsc/domain/common/vo/Money.java FIVE_THOUSAND 정적 상수 추가
src/main/java/com/gdschongik/gdsc/domain/coupon/application/CouponService.java findOrCreate 메서드 추가, 쿠폰 생성 로직 개선
src/main/java/com/gdschongik/gdsc/domain/coupon/dao/CouponRepository.java findByCouponTypeAndStudy 메서드 추가
src/main/java/com/gdschongik/gdsc/domain/coupon/domain/Coupon.java @Table 어노테이션 추가, 고유 제약 조건 설정

Assessment against linked issues

목표 해결 여부 설명
스터디 수료 쿠폰이 스터디 당 하나만 존재 [#840]

Possibly related PRs

Suggested labels

✨ feature

Suggested reviewers

  • kckc0608
  • uwoobeat

Poem

🐰 토끼의 코드 마법
쿠폰 하나로 정리하니
중복은 사라지고
시스템은 깨끗하게
코드의 향기 퍼지네! 🎉

✨ Finishing Touches
  • 📝 Generate Docstrings (Beta)

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ 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.
    • Generate unit testing code for this file.
    • 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 generate unit testing code for this file.
    • @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 generate unit testing code.
    • @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.

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. (Beta)
  • @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.

Copy link

Job Summary for Gradle

Check Style and Test to Develop :: build-test
Gradle Root Project Requested Tasks Gradle Version Build Outcome Build Scan®
gdsc check 8.5 Build Scan published

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

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between d49b980 and 95b867a.

📒 Files selected for processing (3)
  • src/main/java/com/gdschongik/gdsc/domain/common/vo/Money.java (1 hunks)
  • src/main/java/com/gdschongik/gdsc/domain/coupon/application/CouponService.java (2 hunks)
  • src/main/java/com/gdschongik/gdsc/domain/coupon/dao/CouponRepository.java (1 hunks)
🧰 Additional context used
📓 Learnings (1)
src/main/java/com/gdschongik/gdsc/domain/coupon/application/CouponService.java (1)
Learnt from: kckc0608
PR: GDSC-Hongik/gdsc-server#847
File: src/main/java/com/gdschongik/gdsc/domain/coupon/application/CouponService.java:115-115
Timestamp: 2025-01-22T12:27:00.185Z
Learning: Discount amounts in the coupon domain should be managed as constants rather than hardcoded values, especially for specific coupon types like study completion coupons.
🔇 Additional comments (3)
src/main/java/com/gdschongik/gdsc/domain/coupon/dao/CouponRepository.java (1)

10-10: 메서드 구현이 적절합니다!

Spring Data JPA 명명 규칙을 잘 따르고 있으며, Optional 반환 타입을 사용하여 null 안전성을 보장합니다.

src/main/java/com/gdschongik/gdsc/domain/common/vo/Money.java (1)

21-21: 상수 추출이 잘 되었습니다!

PR #847에서 논의된 하드코딩 문제를 적절히 해결했습니다. Money 값 객체에 상수를 정의함으로써 코드의 유지보수성이 향상되었습니다.

src/main/java/com/gdschongik/gdsc/domain/coupon/application/CouponService.java (1)

124-130: findOrCreate 메서드 구현이 깔끔합니다!

기존 쿠폰을 찾거나 새로 생성하는 로직이 잘 구현되어 있습니다. Money.FIVE_THOUSAND 상수를 사용하여 하드코딩을 제거한 점도 좋습니다.

Comment on lines 111 to +112

String couponName = couponNameUtil.generateStudyCompletionCouponName(study);
// TODO: 요청할 때마다 새로운 쿠폰 생성되는 문제 수정: 스터디마다 하나의 쿠폰만 존재하도록 쿠폰 타입 및 참조 식별자 추가
Coupon coupon = Coupon.createAutomatic(couponName, Money.from(5000L), CouponType.STUDY_COMPLETION, study);
couponRepository.save(coupon);
Coupon coupon = findOrCreate(CouponType.STUDY_COMPLETION, study);
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

🛠️ Refactor suggestion

스터디 히스토리 유효성 검사 추가가 필요합니다

현재 구현에서는 다음과 같은 잠재적인 문제가 있습니다:

  1. studyHistories가 비어있는 경우 IndexOutOfBoundsException 발생 가능
  2. 서로 다른 스터디의 히스토리가 포함된 경우 잘못된 쿠폰이 발급될 수 있음

리뷰 코멘트에서 제안된 검증 로직 추가를 권장드립니다:

if (studyHistories.isEmpty()) {
    throw new CustomException(STUDY_HISTORY_NOT_FOUND);
}

boolean allSameStudy = studyHistories.stream()
        .map(StudyHistory::getStudy)
        .distinct()
        .count() == 1;
if (!allSameStudy) {
    throw new CustomException(INVALID_STUDY_HISTORIES);
}

추가로 INVALID_STUDY_HISTORIES 에러 코드도 정의가 필요합니다.

🔗 Analysis chain

studyHistories의 유효성 검사가 필요합니다.

다음 사항들을 확인해주세요:

  1. studyHistories가 비어있는 경우에 대한 처리
  2. 모든 studyHistory가 동일한 study에 속하는지 검증

아래는 검증을 위한 코드 제안입니다:

+        if (studyHistories.isEmpty()) {
+            throw new CustomException(STUDY_HISTORY_NOT_FOUND);
+        }
+
+        boolean allSameStudy = studyHistories.stream()
+                .map(StudyHistory::getStudy)
+                .distinct()
+                .count() == 1;
+        if (!allSameStudy) {
+            throw new CustomException(INVALID_STUDY_HISTORIES);
+        }
+
         Study study = studyHistories.get(0).getStudy();
🏁 Scripts executed

The following scripts were executed for the analysis:

Error: No shell scripts found


Script:

#!/bin/bash
# Get the complete implementation of CouponService
ast-grep --pattern 'class CouponService {
  $$$
}'

# Search for any existing validation logic related to studyHistories
rg "studyHistories" -A 5 -B 5

# Check for custom exception definitions
rg "STUDY_HISTORY_NOT_FOUND|INVALID_STUDY_HISTORIES" -A 2 -B 2

Length of output: 26389

@@ -123,4 +120,12 @@ public void createAndIssueCouponByStudyHistories(List<Long> studyHistoryIds) {
"[CouponService] 스터디 수료 쿠폰 발급: issuedCouponIds={}",
issuedCoupons.stream().map(IssuedCoupon::getId).toList());
}

private Coupon findOrCreate(CouponType couponType, Study study) {
Copy link
Member

Choose a reason for hiding this comment

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

저번에 findOrCreate 계열 따닥 이슈 있었는데, 이것도 unique constraint 있어야 방지할 수 있지 않을까 싶네요

Copy link
Member Author

Choose a reason for hiding this comment

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

오 그렇겠네요
쿠폰에 unique constraint 걸어두겠습니다~

@@ -123,4 +120,12 @@ public void createAndIssueCouponByStudyHistories(List<Long> studyHistoryIds) {
"[CouponService] 스터디 수료 쿠폰 발급: issuedCouponIds={}",
issuedCoupons.stream().map(IssuedCoupon::getId).toList());
}

private Coupon findOrCreate(CouponType couponType, Study study) {
Copy link
Member

Choose a reason for hiding this comment

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

그리고 submitAssignment 에서도 findOrCreate 가 있는데, 여기서는 create 분기에서는 repository.save를 호출하지 않고 그냥 정적 팩토리로 생성된 객체를 리턴하고, 바깥에서 save를 호출하는 식으로 하고 있네요.

해당 로직에서는 find 분기로 가져온 과제제출이력을 수정할 니즈가 있어서, 바깥에서도 save를 항상 호출 가능한 상황이었지만...
여기서는 순수하게 조회 or 생성이기 때문에, 안에서 save를 호출하는 것이 맞을 것 같네요.

그래도 AssignmentHistory의 findOrCreate 내부에서 save를 호출하도록 투두 하나 추가해주면 좋을 것 같아요

Copy link
Member Author

Choose a reason for hiding this comment

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

네네 todo 추가하고 issue도 만들어둘게요

Copy link

Job Summary for Gradle

Check Style and Test to Develop :: build-test
Gradle Root Project Requested Tasks Gradle Version Build Outcome Build Scan®
gdsc check 8.5 Build Scan published

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: 0

🧹 Nitpick comments (1)
src/main/java/com/gdschongik/gdsc/domain/coupon/domain/Coupon.java (1)

29-29: 애플리케이션 레벨의 유효성 검사 추가를 고려해보세요.

데이터베이스 제약 조건과 함께, 서비스 레이어에서도 중복 쿠폰 생성 시도를 미리 확인하는 것이 좋습니다. 이를 통해:

  • 불필요한 데이터베이스 작업을 줄일 수 있습니다
  • 사용자 친화적인 에러 메시지를 제공할 수 있습니다
  • 동시성 문제를 더 우아하게 처리할 수 있습니다

서비스 레이어에 유효성 검사 로직을 추가하는 예시 코드를 제공해드릴까요?

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 95b867a and d2db7d6.

📒 Files selected for processing (2)
  • src/main/java/com/gdschongik/gdsc/domain/coupon/domain/Coupon.java (1 hunks)
  • src/main/java/com/gdschongik/gdsc/domain/study/application/StudentStudyHistoryService.java (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • src/main/java/com/gdschongik/gdsc/domain/study/application/StudentStudyHistoryService.java
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build-test
🔇 Additional comments (2)
src/main/java/com/gdschongik/gdsc/domain/coupon/domain/Coupon.java (2)

20-21: 필요한 임포트문이 잘 추가되었습니다.

데이터베이스 제약 조건을 위한 필수 어노테이션들이 정확하게 임포트되었습니다.


29-29: 스터디당 하나의 쿠폰만 허용하도록 제약 조건이 잘 구현되었습니다.

coupon_typestudy_id 컬럼의 조합에 대한 유니크 제약 조건 추가로 각 스터디 당 동일한 타입의 쿠폰이 중복 생성되는 것을 방지할 수 있습니다.

다음 스크립트를 실행하여 데이터베이스 스키마가 올바르게 적용되었는지 확인하시기 바랍니다:

✅ Verification successful

스터디당 동일한 쿠폰 타입의 중복 생성 방지가 올바르게 구현되었습니다.

  • @Table 어노테이션의 uniqueConstraints 설정이 coupon_typestudy_id 컬럼에 대해 올바르게 정의되어 있습니다.
  • Study 엔티티와의 관계 매핑이 @ManyToOne으로 적절하게 구현되어 있어, 데이터베이스 레벨에서 제약 조건이 정상적으로 적용됩니다.
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: 데이터베이스 스키마에서 유니크 제약 조건이 올바르게 적용되었는지 확인

# JPA 엔티티에서 유니크 제약 조건 검색
rg -l "UniqueConstraint.*coupon_type.*study_id" .

# 마이그레이션 파일에서 유니크 제약 조건 검색
fd -e sql . | xargs rg "UNIQUE.*coupon_type.*study_id"

Length of output: 177


Script:

#!/bin/bash
# Check complete Coupon entity structure and Study relationship
ast-grep --pattern 'class Coupon {
  $$$
}'  src/main/java/com/gdschongik/gdsc/domain/coupon/domain/Coupon.java

# Look for Study entity and its relationship
fd Study.java

Length of output: 193


Script:

#!/bin/bash
# Get complete content of both entity files
cat src/main/java/com/gdschongik/gdsc/domain/coupon/domain/Coupon.java
cat src/main/java/com/gdschongik/gdsc/domain/study/domain/Study.java

# Check for any other files that might reference these entities
rg -l "Coupon|Study" --type java

Length of output: 21020

Copy link
Member

@kckc0608 kckc0608 left a comment

Choose a reason for hiding this comment

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

lgtm

Copy link
Member

@uwoobeat uwoobeat left a comment

Choose a reason for hiding this comment

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

lgtm

@Sangwook02 Sangwook02 merged commit d2df1e2 into develop Jan 28, 2025
1 check passed
@Sangwook02 Sangwook02 deleted the feature/840-single-completion-coupon-per-study branch January 28, 2025 12:00
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.

✨ 스터디 수료 쿠폰이 스터디 당 하나만 존재하도록 변경
3 participants