Skip to content

Sisc1 121 베팅시 획득 포인트, 베팅 인원수 추가#122

Merged
discipline24 merged 4 commits intomainfrom
SISC1-121-BE-베팅시-획득-포인트-베팅-인원수-추가
Nov 19, 2025

Hidden character warning

The head ref may contain hidden characters: "SISC1-121-BE-\ubca0\ud305\uc2dc-\ud68d\ub4dd-\ud3ec\uc778\ud2b8-\ubca0\ud305-\uc778\uc6d0\uc218-\ucd94\uac00"
Merged

Sisc1 121 베팅시 획득 포인트, 베팅 인원수 추가#122
discipline24 merged 4 commits intomainfrom
SISC1-121-BE-베팅시-획득-포인트-베팅-인원수-추가

Conversation

@cksdid202
Copy link
Contributor

@cksdid202 cksdid202 commented Nov 19, 2025

  • 베팅시 획득 포인트, 베팅 인원수 추가

Summary by CodeRabbit

  • 새로운 기능

    • 베팅 라운드 응답에 예상 보상 배수 및 상향/하향 통계(건수·포인트)가 추가되어 라운드 상세 정보 강화
    • 활성 라운드 조회 응답 포맷이 개선되어 클라이언트에서 더 많은 메타데이터 확인 가능
    • 베팅 통계 집계 방식 개선으로 업데이트 신뢰성 향상
  • 버그 수정

    • 활성 베팅 라운드 부재 시 404 응답 반환으로 상태 처리 개선

@cksdid202 cksdid202 linked an issue Nov 19, 2025 that may be closed by this pull request
3 tasks
@coderabbitai
Copy link

coderabbitai bot commented Nov 19, 2025

Walkthrough

컨트롤러의 반환 타입을 BetRoundResponse DTO로 변경하고, BetRound 엔티티에 베팅 통계 필드를 추가했으며 서비스와 레포지토리에서 통계의 원자적 업데이트와 DTO 변환을 도입해 활성 라운드 조회 및 베팅 통계 갱신 흐름을 수정했습니다.

Changes

Cohort / File(s) 변경 요약
컨트롤러 API 업데이트
backend/src/main/java/org/sejongisc/backend/betting/controller/BettingController.java
getTodayBetRound 반환 타입을 ResponseEntity<BetRoundResponse>로 변경, 서비스 호출을 getActiveRoundResponse(scope)로 전환하고 존재하지 않을 시 404 반환 처리 추가
새 DTO 추가
backend/src/main/java/org/sejongisc/backend/betting/dto/BetRoundResponse.java
BetRoundResponse DTO 추가(@Getter, @Builder) 및 from(BetRound) 정적 팩토리로 엔티티의 필드와 통계/예상 보상값 매핑
엔티티 확장
backend/src/main/java/org/sejongisc/backend/betting/entity/BetRound.java
베팅 통계 필드 4개 추가: upBetCount, upTotalPoints, downBetCount, downTotalPointsgetEstimatedRewardMultiplier(BetOption) 메서드 추가
서비스 로직 업데이트
backend/src/main/java/org/sejongisc/backend/betting/service/BettingService.java
postUserBet에서 무료/유료 스테이크 분기 변경, 기존 addBetStats 제거 후 레포지토리의 원자적 증가 호출 사용으로 전환, getActiveRoundResponse(Scope) 추가(엔티티→DTO 변환)
레포지토리 원자 업데이트
backend/src/main/java/org/sejongisc/backend/betting/repository/BetRoundRepository.java
incrementUpStats(UUID,long)incrementDownStats(UUID,long)에 대한 @Modifying JPQL 업데이트 쿼리 추가(통계 카운트/토탈 포인트 원자 증가)

Sequence Diagram(s)

sequenceDiagram
    participant C as 클라이언트
    participant Ctrl as BettingController
    participant Svc as BettingService
    participant Repo as BetRoundRepository
    participant DB as Database

    C->>Ctrl: GET /bet/today?scope=...
    Ctrl->>Svc: getActiveRoundResponse(scope)
    Svc->>Repo: findTopByScopeAndActive(...)
    Repo->>DB: SELECT active bet round
    DB-->>Repo: BetRound 또는 null
    Repo-->>Svc: Optional<BetRound>
    Svc->>Svc: BetRound -> BetRoundResponse.from()
    Svc-->>Ctrl: Optional<BetRoundResponse>
    alt 존재
        Ctrl-->>C: 200 OK (BetRoundResponse)
    else 미존재
        Ctrl-->>C: 404 Not Found
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

주의 검토 항목:

  • DB 마이그레이션(새 칼럼) 및 기존 데이터 호환성 확인
  • @Modifying 쿼리의 트랜잭션 경계 및 동시성 동작(증가 연산) 테스트
  • BetRoundResponse.from()의 보상 계산(getEstimatedRewardMultiplier) 정확성 검증
  • postUserBet 경로에서 무료/유료 분기와 통계 증가 호출이 모든 케이스에 적용되는지 확인

Possibly related issues

Possibly related PRs

Suggested reviewers

  • discipline24
  • msciki7

🐰
통계가 모여 당근처럼 빛나네,
DTO 옷 입고 라운드가 춤추네.
오름·내림 숫자, 포인트 노래하니,
작은 코드 한 줄에 기쁨 한 움큼.
윙크하며 축하해요, 냠냠 🥕✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 69.23% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목은 풀 리퀘스트의 주요 변경사항인 베팅 통계(획득 포인트, 베팅 인원수) 추가를 명확하게 반영하고 있습니다.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch SISC1-121-BE-베팅시-획득-포인트-베팅-인원수-추가

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

🧹 Nitpick comments (4)
backend/src/main/java/org/sejongisc/backend/betting/entity/BetRound.java (1)

125-134: BigDecimal 계산에서 double 캐스팅 사용 – 정밀도 손실 우려

getEstimatedRewardMultiplier

return BigDecimal.valueOf((double) totalPool / optionPool);

처럼 long -> double -> BigDecimal 경로를 타고 있어, BigDecimal을 쓰는 이점(정밀도, 예측 가능한 스케일)이 거의 사라집니다. 향후 이 값을 기반으로 실제 보상/배당을 계산할 예정이라면 정밀도가 중요해질 가능성이 높습니다.

예를 들어 다음처럼 BigDecimal 연산으로 직접 나누도록 리팩터링하는 것을 제안드립니다:

-        long totalPool = upTotalPoints + downTotalPoints;
-        long optionPool = (option == BetOption.RISE) ? upTotalPoints : downTotalPoints;
+        long totalPool = upTotalPoints + downTotalPoints;
+        long optionPool = (option == BetOption.RISE) ? upTotalPoints : downTotalPoints;
...
-        // 공식: (전체 베팅 포인트 / 내 옵션 포인트) * 수수료 등 보정
-        return BigDecimal.valueOf((double) totalPool / optionPool);
+        // 공식: (전체 베팅 포인트 / 내 옵션 포인트) * 수수료 등 보정
+        BigDecimal total = BigDecimal.valueOf(totalPool);
+        BigDecimal optionTotal = BigDecimal.valueOf(optionPool);
+        // 필요한 소수점 자릿수/반올림 전략은 기획에 맞춰 조정
+        return total.divide(optionTotal, 2, java.math.RoundingMode.HALF_UP);

소수점 자리 수(위 예시는 2자리)와 반올림 방식은 실제 기획에 맞춰 조정하면 될 것 같습니다.

backend/src/main/java/org/sejongisc/backend/betting/service/BettingService.java (2)

143-149: 베팅 통계가 취소/실패 케이스를 어떻게 다루는지 요구사항 확인 필요

현재 흐름:

  • stake = isFree ? 0 : stakePoints;
  • 바로 betRound.addBetStats(option, stake); 호출
  • 이후 유료 베팅이면 포인트 검증·차감, 실패 시 예외 → 트랜잭션 롤백

이라서, 예외로 롤백되는 케이스에서는 통계도 같이 롤백되므로 데이터 오염은 없을 것으로 보입니다.

다만 도메인 관점에서:

  • cancelUserBet에서 통계를 되돌리는 로직이 없어, 통계가 "현재 유효한 베팅 수/포인트"가 아니라 라운드 동안 발생한 누적 베팅 시도(취소 포함) 기준이 됩니다.
  • API/기획 상 "베팅 인원 수"를 UX에서 어떻게 보여줄지(현재 참여 중인지, 한 번이라도 참여했는지)에 따라 이 동작이 맞을 수도, 아닐 수도 있습니다.

요구사항이 "현재 유효한 ACTIVE 베팅 기준" 이라면:

  • cancelUserBet에서 BetRound에 대해 역연산(removeBetStats(option, stakePoints))을 호출하는 방식을 고려해 볼 수 있을 것 같습니다.

현 기획 의도를 한 번만 더 확인해 보시면 좋겠습니다.


4-4: BetRoundResponse 매핑용 서비스 메서드 추가는 깔끔합니다

getActiveRoundResponse에서 Optional.map(BetRoundResponse::from) 패턴을 사용해 컨트롤러가 바로 DTO를 받을 수 있게 한 구조는 깔끔해 보입니다.
기존 getActiveRound(Scope)도 남겨두어 내부 용도로 사용할 수 있어 확장성도 괜찮습니다.

추가로 읽기 전용 트랜잭션이 필요하다면, 향후에는 서비스 전체 혹은 개별 메서드에 @Transactional(readOnly = true)를 붙이는 것도 고려해 볼 수 있습니다.

Also applies to: 182-186

backend/src/main/java/org/sejongisc/backend/betting/dto/BetRoundResponse.java (1)

12-48: expectedUpReward/expectedDownReward 의미(배당 배율 vs 포인트) 정리 제안

현재 DTO 주석은

// 예상 획득 포인트 (100포인트 베팅 기준 예시)

라고 되어 있지만, 실제 값은 BetRound.getEstimatedRewardMultiplier(...)의 리턴값(전체 포인트 / 옵션 포인트)이라서 **"예상 포인트"라기보다는 배당 배율(멀티플라이어)**에 가깝습니다.

프론트/기획에서 기대하는 값이:

  • 배당 배율이라면: 필드명/주석을 expectedUpMultiplier 등으로 바꾸거나, 주석을 "예상 배당 배율 (프론트에서 기준 포인트를 곱해 사용)"처럼 명확히 해 두는 편이 좋고,
  • **실제 예상 포인트(예: 100포인트 기준)**라면: DTO 생성 시점에 기준 포인트를 곱해 준 값을 넣는 편이 더 직관적일 것 같습니다.

지금도 기능적으로 문제는 없지만, API 스펙 의미가 처음부터 분명해져야 이후 클라이언트 코드나 문서에서 혼동이 줄어들 것 같습니다.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 62ef6e8 and 3ef9bea.

📒 Files selected for processing (4)
  • backend/src/main/java/org/sejongisc/backend/betting/controller/BettingController.java (2 hunks)
  • backend/src/main/java/org/sejongisc/backend/betting/dto/BetRoundResponse.java (1 hunks)
  • backend/src/main/java/org/sejongisc/backend/betting/entity/BetRound.java (2 hunks)
  • backend/src/main/java/org/sejongisc/backend/betting/service/BettingService.java (3 hunks)
🔇 Additional comments (1)
backend/src/main/java/org/sejongisc/backend/betting/controller/BettingController.java (1)

10-11: DTO 기반 응답 전환 및 404 처리 방식 적절

BetRound 엔티티 대신 BetRoundResponse DTO를 반환하고, 활성 라운드가 없을 때 404 Not Found를 돌려주는 구조로 바뀐 점이 명확하고 REST API 관점에서도 자연스럽습니다.

Optional.map(ResponseEntity::ok).orElseGet(ResponseEntity.notFound()::build) 패턴도 읽기 좋고, Swagger description의 200/404 설명과 실제 동작이 잘 일치합니다.

Also applies to: 51-60

Comment on lines +113 to +123

// [추가] 통계 업데이트 메서드 (서비스에서 호출)
public void addBetStats(BetOption option, int points) {
if (option == BetOption.RISE) { // UP
this.upBetCount++;
this.upTotalPoints += points;
} else if (option == BetOption.FALL) { // DOWN
this.downBetCount++;
this.downTotalPoints += points;
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

동시 베팅 시 통계(LONG/COUNT) 갱신에서 Lost Update 위험

addBetStatsBetRound 엔티티의 필드를 단순히 ++, +=로 변경하고 있습니다. 여러 사용자가 같은 라운드에 동시에 베팅하면:

  1. 각 트랜잭션이 같은 BetRound 스냅샷을 로드하고
  2. 각각 통계를 증가시킨 뒤
  3. 마지막 커밋이 이전 커밋의 값을 덮어써서, 일부 베팅의 카운트/포인트가 유실되는 lost update 상황이 발생할 수 있습니다.

JPA에서 별도 락/버전 필드를 두지 않은 상태에서 공유 카운터를 이렇게 업데이트하면 전형적으로 발생하는 문제라, 통계 신뢰도가 크게 떨어질 수 있습니다.

대안 예시:

  • 엔티티에 @Version Long version;을 추가해 낙관적 락을 걸고, OptimisticLockException 발생 시 재시도 처리, 또는
  • BetRoundRepository@Modifying JPQL/Native 쿼리로
    upBetCount = upBetCount + 1, upTotalPoints = upTotalPoints + :points 같은 DB 단일 UPDATE 쿼리를 사용해 원자적으로 갱신

중 하나를 고려해 주시면 좋겠습니다.

🤖 Prompt for AI Agents
In backend/src/main/java/org/sejongisc/backend/betting/entity/BetRound.java
around lines 113 to 123, add protection against lost-updates caused by
concurrent ++/+= updates in addBetStats: either add an optimistic lock version
field (e.g. @Version Long version) on the BetRound entity and have callers
handle OptimisticLockException with a retry loop, or remove the in-memory
increments and implement atomic DB-side updates via a repository @Modifying
JPQL/native update (e.g. set upBetCount = upBetCount + 1, upTotalPoints =
upTotalPoints + :points) so increments occur in a single atomic SQL UPDATE;
choose one approach and update service/repository logic accordingly (add version
field and retry logic if optimistic locking; or create and call the @Modifying
update method and stop mutating entity fields in-memory).

@cksdid202 cksdid202 changed the title feat: 베팅시 획득 포인트, 베팅 인원수 추가 Sisc1 121 베팅시 획득 포인트, 베팅 인원수 추가 Nov 19, 2025
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 3ef9bea and 47dd612.

📒 Files selected for processing (3)
  • backend/src/main/java/org/sejongisc/backend/betting/entity/BetRound.java (2 hunks)
  • backend/src/main/java/org/sejongisc/backend/betting/repository/BetRoundRepository.java (2 hunks)
  • backend/src/main/java/org/sejongisc/backend/betting/service/BettingService.java (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • backend/src/main/java/org/sejongisc/backend/betting/service/BettingService.java
  • backend/src/main/java/org/sejongisc/backend/betting/entity/BetRound.java
🔇 Additional comments (1)
backend/src/main/java/org/sejongisc/backend/betting/repository/BetRoundRepository.java (1)

24-32: 검증 완료: 모든 필드명과 트랜잭션 컨텍스트가 정상입니다.

원자적 업데이트 메서드의 구현이 올바르게 되어 있습니다:

  • 필드명 일치: JPQL 쿼리의 모든 필드명(betRoundID, upBetCount, upTotalPoints, downBetCount, downTotalPoints)이 BetRound 엔티티의 실제 필드명과 정확히 일치합니다.
  • 트랜잭션 컨텍스트: incrementUpStatsincrementDownStats 호출이 @Transactional이 적용된 postUserBet 메서드(라인 132-155) 내에서 이루어지므로 트랜잭션 컨텍스트가 보장됩니다.
  • 동시성 안전성: @Modifying(clearAutomatically = true)를 통해 영속성 컨텍스트가 적절히 동기화됩니다.

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)
backend/src/main/java/org/sejongisc/backend/betting/repository/BetRoundRepository.java (1)

24-32: 통계 JPQL UPDATE 자체는 좋지만, 필드명/트랜잭션/clear 옵션 한 번만 점검해 주세요.

  • 지금처럼 upBetCount, upTotalPoints, downBetCount, downTotalPoints를 JPQL UPDATE ... SET x = x + 1 형태로 올리는 건 기존 read-modify-write 방식보다 훨씬 동시성에 안전해서 방향은 좋아 보입니다.
  • 다만 JPQL에서는 DB 컬럼명이 아니라 엔티티의 자바 필드명을 써야 해서, WHERE b.betRoundID = :id 부분이 BetRound 엔티티의 실제 PK 필드명이 맞는지 한 번만 확인해 주세요. (일반적으로 betRoundId처럼 Id 소문자 패턴을 많이 써서 헷갈리기 쉬운 부분입니다.)
  • @Modifying(clearAutomatically = true)는 쿼리 실행 후 영속성 컨텍스트 전체를 비우기 때문에, 같은 트랜잭션 안에서 User, Bet 등 다른 엔티티를 이미 로드해 둔 상태에서 이 메서드를 호출하면 예상치 못하게 전부 detach될 수 있습니다.
    • 만약 postUserBet 트랜잭션 안에서 이 메서드 호출 후에도 다른 엔티티를 계속 다룬다면, clearAutomatically를 빼고 대신 필요 시 재조회(findById)나 EntityManager#refresh를 사용하는 방식도 고려해 볼 만합니다.
  • 사용자 포인트 차감, 베팅 엔티티 저장, 이 통계 UPDATE 두 개가 반드시 하나의 트랜잭션 안에서 처리되는지도 함께 확인해 주세요. 중간에 예외가 나면 일부만 반영되는 상황을 막으려면 서비스 레이어에 @Transactional이 잘 잡혀 있어야 합니다.
  • 선택 사항이지만, 반환 타입을 void 대신 int로 두고 업데이트된 row 수를 받으면, 존재하지 않는 라운드 ID에 대한 호출을 방어하는 데 도움이 될 수 있습니다.
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 47dd612 and 4bdfaf5.

📒 Files selected for processing (1)
  • backend/src/main/java/org/sejongisc/backend/betting/repository/BetRoundRepository.java (2 hunks)
🔇 Additional comments (1)
backend/src/main/java/org/sejongisc/backend/betting/repository/BetRoundRepository.java (1)

3-8: Spring Data JPA용 import 정리 잘 되었습니다.

이전 리뷰에서 지적되었던 Lettuce @Param 사용 문제가 org.springframework.data.repository.query.Param + @Modifying, @Query 조합으로 잘 교체되었습니다. 현재 import 구성은 Spring Data JPA 기준으로 문제 없어 보입니다.

@discipline24 discipline24 merged commit dd56304 into main Nov 19, 2025
1 check passed
@discipline24 discipline24 deleted the SISC1-121-BE-베팅시-획득-포인트-베팅-인원수-추가 branch November 19, 2025 12:51
@cksdid202 cksdid202 restored the SISC1-121-BE-베팅시-획득-포인트-베팅-인원수-추가 branch November 20, 2025 07:44
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