-
Notifications
You must be signed in to change notification settings - Fork 0
[EDMT-457] 회원 포인트 시스템 구현 및 AI 생성 시 포인트 차감 기능 추가 #78
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
Conversation
Walkthrough학생 기록 생성 시 포인트 차감 기능을 도입합니다. 회원 테이블에 포인트 필드를 추가하고, 포인트 서비스를 통해 비관적 잠금으로 트랜잭션 안전성을 보장하며 작업 생성 시 100포인트를 차감합니다. Changes
Sequence Diagram(s)sequenceDiagram
participant StudentRecordAIFacade
participant PointService
participant MemberRepository
participant Member
StudentRecordAIFacade->>PointService: deductPoints(memberId, 100)
PointService->>MemberRepository: findByIdWithLock(memberId)
rect rgb(200, 220, 255)
Note over MemberRepository: PESSIMISTIC_WRITE 잠금 획득
MemberRepository-->>PointService: Member (잠금 상태)
end
alt 회원 존재 여부
PointService->>PointService: 회원 없음 검증
PointService-->>StudentRecordAIFacade: MEMBER_NOT_FOUND 예외
else 포인트 충분 여부
PointService->>Member: 포인트 < 100?
Member-->>PointService: true
PointService-->>StudentRecordAIFacade: INSUFFICIENT_POINTS 예외
else 정상 처리
PointService->>Member: deductPoints(100)
rect rgb(200, 255, 220)
Note over Member: point 차감 (예: 1000 → 900)
end
PointService-->>StudentRecordAIFacade: 업데이트된 Member 반환
end
StudentRecordAIFacade->>StudentRecordAIFacade: 작업 생성 진행
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20-25 minutes 주의 검토 항목:
Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this 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 (3)
edukit-core/src/main/java/com/edukit/core/member/service/PointService.java (1)
17-30: 포인트 차감 로직이 올바르게 구현되었습니다.트랜잭션과 비관적 잠금을 통한 동시성 제어가 적절하며, 포인트 부족 시 명확한 예외 처리도 잘 되어 있습니다. JPA dirty checking을 활용한 자동 저장 방식도 정상적으로 동작합니다.
선택적으로 Line 22의
currentPoint변수는 제거하고 Line 24에서 직접member.getPoint()를 사용할 수 있습니다:- int currentPoint = member.getPoint(); - - if (currentPoint < pointsToDeduct) { + if (member.getPoint() < pointsToDeduct) { throw new MemberException(MemberErrorCode.INSUFFICIENT_POINTS); }edukit-api/src/main/java/com/edukit/studentrecord/facade/StudentRecordAIFacade.java (1)
38-40: 작업 생성 로직 순서 개선을 제안합니다.현재 포인트 차감(Line 38)이 학생 기록 검증(Line 40)보다 먼저 실행됩니다. 학생 기록이 존재하지 않거나 권한이 없는 경우, 포인트가 부족한 상황에서도
RECORD_NOT_FOUND같은 에러가 먼저 발생하여 사용자에게 혼란을 줄 수 있습니다. 트랜잭션 롤백으로 데이터 정합성은 보장되지만, UX 개선을 위해 검증 로직을 먼저 수행하는 것을 권장합니다.다음과 같이 순서를 변경하는 것을 고려해보세요:
public StudentRecordTaskResponse createTaskId(final long memberId, final long recordId, final int byteCount, final String userPrompt) { + StudentRecord studentRecord = studentRecordService.getRecordDetail(memberId, recordId); + Member member = pointService.deductPoints(memberId, DEDUCTED_POINTS); - StudentRecord studentRecord = studentRecordService.getRecordDetail(memberId, recordId); - String requestPrompt = AIPromptGenerator.createStreamingPrompt(studentRecord.getStudentRecordType(), byteCount, userPrompt); StudentRecordAITask task = aiTaskService.createAITask(member, userPrompt);edukit-core/src/main/java/com/edukit/core/member/db/entity/Member.java (1)
150-152: 포인트 차감 메서드에 방어적 검증 추가를 제안합니다.현재
PointService에서 검증이 이루어지지만, 엔티티 메서드 자체에 음수 방지 로직을 추가하면 다른 곳에서 직접 호출 시 무결성을 보장할 수 있습니다.다음과 같은 방어 로직을 고려해보세요:
public void deductPoints(final int pointsToDeduct) { + if (pointsToDeduct < 0) { + throw new IllegalArgumentException("차감 포인트는 0 이상이어야 합니다."); + } + if (this.point < pointsToDeduct) { + throw new IllegalStateException("포인트가 부족하여 차감할 수 없습니다."); + } this.point -= pointsToDeduct; }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
edukit-api/src/main/java/com/edukit/studentrecord/facade/StudentRecordAIFacade.java(2 hunks)edukit-api/src/main/resources/db/migration/V10__Add_member_point.sql(1 hunks)edukit-core/src/main/java/com/edukit/core/member/db/entity/Member.java(4 hunks)edukit-core/src/main/java/com/edukit/core/member/db/repository/MemberRepository.java(2 hunks)edukit-core/src/main/java/com/edukit/core/member/exception/MemberErrorCode.java(1 hunks)edukit-core/src/main/java/com/edukit/core/member/service/PointService.java(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
edukit-core/src/main/java/com/edukit/core/member/service/PointService.java (2)
edukit-core/src/main/java/com/edukit/core/member/exception/MemberException.java (1)
MemberException(6-11)edukit-api/src/main/java/com/edukit/studentrecord/facade/StudentRecordAIFacade.java (1)
Service(21-62)
🔇 Additional comments (6)
edukit-api/src/main/resources/db/migration/V10__Add_member_point.sql (1)
1-3: 마이그레이션이 올바르게 구현되었습니다.기존 회원 데이터에 대해 자동으로 기본값 1000이 적용되며, NOT NULL 제약조건도 적절합니다.
edukit-core/src/main/java/com/edukit/core/member/db/repository/MemberRepository.java (1)
26-28: 비관적 잠금 구현이 적절합니다.포인트 차감 시 동시성 제어를 위한
PESSIMISTIC_WRITE락 사용이 올바르며, 소프트 삭제된 회원을 필터링하는 쿼리도 정확합니다.edukit-core/src/main/java/com/edukit/core/member/exception/MemberErrorCode.java (1)
18-19: 에러 코드 추가가 적절합니다.코드 체계와 메시지 작성이 기존 패턴을 잘 따르고 있으며, 사용자에게 명확한 안내를 제공합니다.
edukit-api/src/main/java/com/edukit/studentrecord/facade/StudentRecordAIFacade.java (1)
32-32: 포인트 차감량 상수가 명확하게 정의되었습니다.100포인트 차감이 적절한지에 대한 PR 목표의 질문과 관련하여, 현재 상수로 정의된 방식은 향후 조정이 용이합니다. 필요시 향후 외부 설정(application.yml)으로 이동하여 배포 없이 조정 가능하도록 개선할 수 있습니다.
edukit-core/src/main/java/com/edukit/core/member/db/entity/Member.java (2)
61-62: 포인트 필드 추가가 올바르게 구현되었습니다.원시 타입
int사용으로 null 안전성을 확보했으며,INITIAL_POINT상수를 통한 기본값 관리도 적절합니다.Also applies to: 72-72
106-115: 회원 복구 시 포인트 처리 정책 확인이 필요합니다.
restore()메서드가 포인트 필드를 수정하지 않아, 탈퇴 후 복구 시 기존 포인트 잔액이 그대로 유지됩니다. 의도된 동작이라면 문제없으나, 복구 시INITIAL_POINT로 초기화해야 하는 정책이라면 수정이 필요합니다.비즈니스 정책을 확인하고, 필요시 다음과 같이 포인트를 초기화하세요:
public void restore(final String password, final Subject subject, final String nickname, final School school) { this.isDeleted = false; this.deletedAt = null; this.password = password; this.subject = subject; this.nickname = nickname; this.school = school; this.role = MemberRole.PENDING_TEACHER; this.verifiedAt = null; + this.point = INITIAL_POINT; // 복구 시 포인트 초기화가 필요한 경우 }
📣 Jira Ticket
EDMT-457
👩💻 작업 내용
회원 포인트 시스템을 구현하고 AI 생성 시 포인트 차감 기능을 추가했습니다.
주요 변경사항
회원 엔티티에 포인트 필드 추가
Member엔티티에point컬럼 추가 (기본값: 1000)포인트 차감 로직 구현
PointService신규 생성INSUFFICIENT_POINTS에러 코드 추가)AI 생성 시 포인트 차감 통합
StudentRecordAIFacade에서 AI 작업 생성 시 100 포인트 차감기술적 구현
@Lock(LockModeType.PESSIMISTIC_WRITE)를 사용하여 포인트 차감 시 동시성 문제 방지@Transactional을 통한 원자적 작업 보장📝 리뷰 요청 & 논의하고 싶은 내용
Test Plan
✅ Manual Testing Checklist
🧪 Automated Tests
./gradlew test./gradlew buildPointService단위 테스트 (포인트 차감, 부족 시 예외)StudentRecordAIFacade통합 테스트🔍 Code Quality
Deployment Notes
관련 커밋
298f392- [feat] add point column to Member entity and initialize with default value44c0097- [feat] implement point validation for member actions6c44296- [feat] implement point deduction logic in Member and PointService0d099c3- [feat] implement pessimistic locking for point deduction in PointService350aeec- [refac] update deductPoints method to return Member object in PointService🤖 Generated with Claude Code
Summary by CodeRabbit