[feat] 정산, 결제 실패 로직 추가 / 스케줄 삭제 API 구현 / 정산 관련 API 응답 수정 #125
[feat] 정산, 결제 실패 로직 추가 / 스케줄 삭제 API 구현 / 정산 관련 API 응답 수정 #125
Conversation
|
Caution Review failedThe pull request is closed. Walkthrough결제 확정/실패 흐름과 정산/지갑 연동을 재설계하고, 스케줄 삭제 API를 추가했습니다. 검색 API는 내 모임 목록 응답을 DTO로 확장했습니다. 일부 JPA 연관의 cascade를 제거했으며, 스케줄링을 애플리케이션에 활성화했습니다. 여러 레포지토리/DTO/에러코드 시그니처가 변경되었습니다. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant PaymentController
participant PaymentService
participant TossClient
participant User/Wallet
participant Repo
Client->>PaymentController: POST /payments/confirm
PaymentController->>PaymentService: confirm(req)
PaymentService->>Repo: claimPayment(orderId, amount)
alt existing/in-progress/done
Repo-->>PaymentService: Payment
else create
Repo-->>PaymentService: new Payment or conflict handle
end
PaymentService->>TossClient: confirmPayment(req)
TossClient-->>PaymentService: response(status, method, paymentKey)
PaymentService->>User/Wallet: load user + wallet, add balance, create WalletTransaction(CHARGE)
PaymentService->>Repo: payment.updateOnConfirm(paymentKey, Status, Method, WalletTransaction)
PaymentService-->>PaymentController: ok
PaymentController-->>Client: 200
sequenceDiagram
participant TossWebhook/Client
participant PaymentController
participant PaymentService
participant Repo
participant User/Wallet
TossWebhook/Client->>PaymentController: POST /payments/fail
PaymentController->>PaymentService: reportFail(req)
PaymentService->>Repo: find/create Payment by orderId
alt already DONE or same paymentKey
PaymentService-->>PaymentController: return
else
PaymentService->>User/Wallet: load references
PaymentService->>Repo: record WalletTransaction(FAILED), cancel Payment
end
PaymentController-->>TossWebhook/Client: 200
sequenceDiagram
participant Client
participant ScheduleController
participant ScheduleService
participant ClubRepo/ScheduleRepo
participant ChatRepo
Client->>ScheduleController: DELETE /clubs/{clubId}/schedules/{scheduleId}
ScheduleController->>ScheduleService: deleteSchedule(clubId, scheduleId)
ScheduleService->>ClubRepo/ScheduleRepo: validate club & schedule(READY, future)
ScheduleService->>ScheduleRepo: verify leader via UserSchedule
ScheduleService->>ChatRepo: find ChatRoom for schedule
ScheduleService->>ClubRepo/ScheduleRepo: remove from club, delete ChatRoom, delete Schedule
ScheduleService-->>ScheduleController: void
ScheduleController-->>Client: 200
sequenceDiagram
participant User
participant SettlementService
participant WalletService
participant Repo
participant TxManager
User->>SettlementService: updateUserSettlement(clubId, scheduleId)
SettlementService->>Repo: load wallets, settlement, validations
SettlementService->>WalletService: record outgoing/incoming ledger
alt success
WalletService-->>SettlementService: ok
SettlementService->>Repo: persist & notify
else failure
SettlementService->>TxManager: register rollback hook
TxManager-->>WalletService: onRollback -> createFailedWalletTransactions(...)
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. 📜 Recent review detailsConfiguration used: CodeRabbit UI 💡 Knowledge Base configuration:
You can enable these sources in your CodeRabbit configuration. 📒 Files selected for processing (15)
✨ Finishing Touches
🧪 Generate unit tests
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
The merge-base changed after approval.
|
래빗 안녕 |
The merge-base changed after approval.
There was a problem hiding this comment.
Actionable comments posted: 10
🔭 Outside diff range comments (2)
src/main/java/com/example/onlyone/domain/payment/service/PaymentService.java (2)
92-100: 외부 API 호출을 트랜잭션 경계 밖으로 이동 + 예외 로깅 강화클래스 레벨 @transactional로 인해 네트워크 호출(
tossPaymentClient.confirmPayment)이 트랜잭션 안에서 실행됩니다. 장시간 트랜잭션 유지로 인한 잠금/타임아웃 리스크를 피하기 위해 외부 호출은 트랜잭션 밖에서 수행하고, DB 반영은 별도 트랜잭션으로 분리하는 것을 권장합니다. 또한 예외 매핑 전에 진단 로그를 남겨 운영 가시성을 높여주세요.로깅 추가 예(간단 적용):
response = tossPaymentClient.confirmPayment(req); + if (response.getOrderId() == null || !response.getOrderId().equals(req.getOrderId())) { + log.warn("OrderId mismatch between request and response. req={}, res={}", req.getOrderId(), response.getOrderId()); + throw new CustomException(ErrorCode.INVALID_PAYMENT_INFO); + } } catch (FeignException.BadRequest e) { - throw new CustomException(ErrorCode.INVALID_PAYMENT_INFO); + log.warn("Toss confirm bad request: orderId={}, message={}", req.getOrderId(), e.getMessage()); + throw new CustomException(ErrorCode.INVALID_PAYMENT_INFO); } catch (FeignException e) { - throw new CustomException(ErrorCode.TOSS_PAYMENT_FAILED); + log.error("Toss confirm failed: orderId={}, message={}", req.getOrderId(), e.getMessage()); + throw new CustomException(ErrorCode.TOSS_PAYMENT_FAILED); } catch (Exception e) { - throw new CustomException(ErrorCode.INTERNAL_SERVER_ERROR); + log.error("Toss confirm unexpected error: orderId={}, message={}", req.getOrderId(), e.getMessage(), e); + throw new CustomException(ErrorCode.INTERNAL_SERVER_ERROR); }트랜잭션 분리 예시(참고용, 별도 메서드로 분리):
// confirm(...)은 @Transactional을 적용하지 않거나, NOT_SUPPORTED로 지정 public ConfirmTossPayResponse confirm(ConfirmTossPayRequest req) { // 1) 외부 호출 ConfirmTossPayResponse response = tossPaymentClient.confirmPayment(req); // 2) DB 반영 (별도 트랜잭션) persistAfterConfirm(req, response); return response; } @Transactional // 또는 REQUIRES_NEW void persistAfterConfirm(ConfirmTossPayRequest req, ConfirmTossPayResponse response) { // 현재 confirm(...)의 DB 갱신 로직을 이동 }
104-111: 보안/무결성: 잔액 업데이트에 요청값(req) 대신 승인 응답 금액 사용지갑 반영 금액을 클라이언트가 보낸
req.getAmount()로 신뢰하면 조작 여지가 생깁니다. 토스 승인 응답의 총액으로 반영하세요.- int amount = Math.toIntExact(req.getAmount()); + int amount = Math.toIntExact(response.getTotalAmount()); // 포인트 업데이트 wallet.updateBalance(wallet.getBalance() + amount)추가로, 방어적으로
req.getAmount()와response.getTotalAmount()가 다를 경우 로그/차단하는 검사도 고려하세요.
🧹 Nitpick comments (13)
src/main/java/com/example/onlyone/domain/user/dto/response/MySettlementDto.java (1)
22-29: 주석 처리된 코드를 제거하거나 완성해주세요.주석 처리된 factory 메서드가 미완성 상태로 남아있습니다. 사용하지 않는다면 제거하고, 필요하다면 완성해주세요.
-// public static MySettlementDto from(Page<MySettlementDto> userSettlement) { -// return MySettlementDto.builder() -// .clubId(userSettlement.getSettlement().getSchedule().getClub().getClubId()) -// .settlementId(userSettlement.getSettlement().getSettlementId()) -// .amount(userSettlement.getSettlement().getSchedule().getCost()) -// .mainImage(userSettlement.getSettlement().getSchedule().getClub().getClubImage()) -// .build(); -// }src/main/java/com/example/onlyone/global/exception/ErrorCode.java (1)
56-58: 새로 추가된 스케줄 관련 에러 코드가 적절합니다.스케줄 삭제 기능에 필요한 에러 코드들이 잘 정의되었습니다. 다만 두 개의 메시지에서 쉼표 오타가 있습니다.
- MEMBER_CANNOT_MODIFY_SCHEDULE(403, "SCHEDULE_403_1", "리더만 정기 모임을 수정할 수 있습니다,"), - MEMBER_CANNOT_DELETE_SCHEDULE(403, "SCHEDULE_403_2", "리더만 정기 모임을 삭제할 수 있습니다,"), + MEMBER_CANNOT_MODIFY_SCHEDULE(403, "SCHEDULE_403_1", "리더만 정기 모임을 수정할 수 있습니다."), + MEMBER_CANNOT_DELETE_SCHEDULE(403, "SCHEDULE_403_2", "리더만 정기 모임을 삭제할 수 있습니다."),src/main/java/com/example/onlyone/domain/user/dto/response/MySettlementResponseDto.java (1)
18-18: 접근 제한자 누락을 수정해주세요.
mySettlementList필드에 접근 제한자가 누락되었습니다.- List<MySettlementDto> mySettlementList; + private List<MySettlementDto> mySettlementList;src/main/java/com/example/onlyone/domain/schedule/controller/ScheduleController.java (1)
77-83: 삭제 성공 시 204 No Content가 더 RESTful합니다삭제 요청이 성공했을 때 본문이 없는 204를 반환하는 것이 일반적입니다. 현재 공통 응답 규약(CommonResponse)을 유지해야 한다면 200도 허용되지만, 가능하다면 다음처럼 204로 단순화하는 것을 권장합니다.
- return ResponseEntity.ok(CommonResponse.success(null)); + return ResponseEntity.noContent().build();src/main/java/com/example/onlyone/domain/user/controller/UserController.java (1)
105-110: 과도한 요청 방지를 위해 페이지 크기 상한을 두세요클라이언트가 size를 크게 지정해 과부하를 유발할 수 있습니다. 서비스 단에서 size 상한(예: 50)을 적용해 방어적으로 동작하도록 권장합니다.
적용 예시:
@GetMapping("/settlement") public ResponseEntity<?> getMySettlementList(@PageableDefault(size = 20, sort = "createdAt", direction = Sort.Direction.DESC) Pageable pageable) { - return ResponseEntity.ok(CommonResponse.success(userService.getMySettlementList(pageable))); + int maxSize = 50; + Pageable capped = org.springframework.data.domain.PageRequest.of( + pageable.getPageNumber(), + Math.min(pageable.getPageSize(), maxSize), + pageable.getSort() + ); + return ResponseEntity.ok(CommonResponse.success(userService.getMySettlementList(capped))); }추가로 필요한 import:
import org.springframework.data.domain.PageRequest;src/main/java/com/example/onlyone/domain/schedule/entity/Schedule.java (2)
60-62: 중복 방지를 위해 Set 사용을 고려하세요참여자 연관(UserSchedule)은 논리적으로 중복이 없어야 합니다. 컬렉션 타입을 Set으로 바꾸면 중복 방지와 contains/remove 성능이 개선됩니다. 대규모 삭제 시에도 의미가 명확합니다.
- @OneToMany(mappedBy = "schedule", cascade = CascadeType.ALL, orphanRemoval = true) - private List<UserSchedule> userSchedules = new ArrayList<>(); + @OneToMany(mappedBy = "schedule", cascade = CascadeType.ALL, orphanRemoval = true) + private Set<UserSchedule> userSchedules = new HashSet<>();해당 변경에 필요한 import 수정:
-import java.util.ArrayList; -import java.util.List; +import java.util.HashSet; +import java.util.Set;양방향 연관을 사용할 계획이라면 helper 메서드(add/remove)를 두어 양쪽 일관성을 보장하는 것도 추천합니다.
10-12: Set 전환 시 import 정리 필요컬렉션 타입을 Set으로 바꿀 경우 HashSet/Set import로 교체하세요.
-import java.util.ArrayList; -import java.util.List; +import java.util.HashSet; +import java.util.Set;src/main/java/com/example/onlyone/domain/settlement/repository/UserSettlementRepository.java (3)
28-37: 불필요한 조인 제거로 쿼리 간소화 제안첫 번째 조회에서는 sch를 사용하지 않으므로 join st.schedule sch는 성능상 불필요합니다.
from UserSettlement us join us.user u join us.settlement st - join st.schedule sch where us.settlement = :settlement
51-74: 정렬 기준 보강 제안: 완료 시간 우선 정렬요청(REQUESTED)과 완료(COMPLETED)를 함께 보여줄 때, createdAt만으로 정렬하면 완료 항목의 최신성이 반영되지 않을 수 있습니다. 완료 시간(completedTime) 존재 시 우선 사용하도록 정렬을 보강하는 것을 권장합니다.
- order by us.createdAt desc + order by coalesce(us.completedTime, us.createdAt) desc주의: completedTime 컬럼이 null 허용이어야 하며 JPQL의 coalesce 지원 하에 동작합니다.
103-103: 미정산 존재 여부 확인용 메서드 보강 제안현재 existsByUserAndSettlementStatusNot(user, COMPLETED)는 CANCELED/FAILED까지 포함시킬 수 있습니다. UI 요구가 “요청(REQUESTED)만 미정산”이라면 다음 메서드 추가를 권장합니다.
Optional<UserSettlement> findByUserAndSchedule( @Param("user") User user, @Param("schedule") Schedule schedule ); - boolean existsByUserAndSettlementStatusNot(User user, SettlementStatus settlementStatus); + boolean existsByUserAndSettlementStatusNot(User user, SettlementStatus settlementStatus); + boolean existsByUserAndSettlementStatus(User user, SettlementStatus settlementStatus);SearchService.getMyClubs()에서 REQUESTED만 검사하도록 변경하면 의미가 더 명확해집니다(별도 코멘트 참고).
src/main/java/com/example/onlyone/domain/settlement/service/SettlementService.java (3)
122-123: 불필요한 주석 코드 제거중복된 getCurrentUser() 호출 주석은 혼란을 줄 수 있어 제거를 권장합니다.
-// User user = userService.getCurrentUser();
166-167: TODO 처리 권장정산 알림 TODO는 함수 내부에 남아있습니다. 현재 성공 시점에 createNotification은 호출되고 있으므로, TODO는 제거하거나 별도 후처리(예: 비동기 재전송, 실패 큐 등) 로직을 추가해 주세요.
169-204: 실패 기록 보존을 위한 트랜잭션 분리 권장 (REQUIRES_NEW + 별도 빈)현재 실패 시 saveWalletTransactions도 동일 트랜잭션에서 수행되어 예외 재발생 시 함께 롤백됩니다. 실패 로그를 “항상” 보존하려면 REQUIRES_NEW 트랜잭션에서 분리 커밋되어야 합니다. 단, 동일 클래스 내 self-invocation은 프록시를 우회하므로 효과가 없습니다. 별도 컴포넌트로 분리해 호출하거나 ApplicationEvent/Outbox 패턴을 고려해 주세요.
예시(별도 빈):
@Service public class LedgerService { private final WalletTransactionRepository walletTransactionRepository; private final TransferRepository transferRepository; @Transactional(propagation = Propagation.REQUIRES_NEW) public void saveWalletTransactions(Wallet wallet, Wallet leaderWallet, int amount, WalletTransactionStatus status, UserSettlement userSettlement) { // 현재 saveWalletTransactions 구현 그대로 이동 } }그리고 SettlementService에서는 LedgerService를 주입받아 호출합니다. 이렇게 하면 실패 시에도 트랜잭션 로그가 남습니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (19)
src/main/java/com/example/onlyone/domain/chat/entity/ChatRoom.java(1 hunks)src/main/java/com/example/onlyone/domain/chat/entity/Message.java(1 hunks)src/main/java/com/example/onlyone/domain/payment/controller/PaymentController.java(1 hunks)src/main/java/com/example/onlyone/domain/payment/repository/PaymentRepository.java(1 hunks)src/main/java/com/example/onlyone/domain/payment/service/PaymentService.java(4 hunks)src/main/java/com/example/onlyone/domain/schedule/controller/ScheduleController.java(1 hunks)src/main/java/com/example/onlyone/domain/schedule/entity/Schedule.java(2 hunks)src/main/java/com/example/onlyone/domain/schedule/service/ScheduleService.java(1 hunks)src/main/java/com/example/onlyone/domain/search/dto/response/MyMeetingListResponseDto.java(1 hunks)src/main/java/com/example/onlyone/domain/search/service/SearchService.java(3 hunks)src/main/java/com/example/onlyone/domain/settlement/controller/SettlementController.java(0 hunks)src/main/java/com/example/onlyone/domain/settlement/dto/response/UserSettlementDto.java(0 hunks)src/main/java/com/example/onlyone/domain/settlement/repository/UserSettlementRepository.java(4 hunks)src/main/java/com/example/onlyone/domain/settlement/service/SettlementService.java(2 hunks)src/main/java/com/example/onlyone/domain/user/controller/UserController.java(2 hunks)src/main/java/com/example/onlyone/domain/user/dto/response/MySettlementDto.java(1 hunks)src/main/java/com/example/onlyone/domain/user/dto/response/MySettlementResponseDto.java(1 hunks)src/main/java/com/example/onlyone/domain/user/service/UserService.java(4 hunks)src/main/java/com/example/onlyone/global/exception/ErrorCode.java(1 hunks)
💤 Files with no reviewable changes (2)
- src/main/java/com/example/onlyone/domain/settlement/dto/response/UserSettlementDto.java
- src/main/java/com/example/onlyone/domain/settlement/controller/SettlementController.java
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-07-31T06:40:23.207Z
Learnt from: gkdudans
PR: GoormOnlyOne/OnlyOne-Back#53
File: src/main/java/com/example/onlyone/domain/schedule/service/ScheduleService.java:52-62
Timestamp: 2025-07-31T06:40:23.207Z
Learning: In the OnlyOne application's ScheduleService, the user has refactored the updateScheduleStatus scheduled method to use JPQL batch update instead of iterating through individual entities for better performance. They are also considering future improvements with Spring Batch or QueryDSL update functionality for more complex batch processing scenarios.
Applied to files:
src/main/java/com/example/onlyone/domain/schedule/service/ScheduleService.javasrc/main/java/com/example/onlyone/domain/schedule/entity/Schedule.java
🧬 Code Graph Analysis (3)
src/main/java/com/example/onlyone/domain/user/dto/response/MySettlementDto.java (2)
src/main/java/com/example/onlyone/domain/search/dto/response/MyMeetingListResponseDto.java (1)
Builder(8-14)src/main/java/com/example/onlyone/domain/user/dto/response/MySettlementResponseDto.java (1)
Builder(10-29)
src/main/java/com/example/onlyone/domain/search/dto/response/MyMeetingListResponseDto.java (1)
src/main/java/com/example/onlyone/domain/user/dto/response/MySettlementDto.java (1)
Builder(10-30)
src/main/java/com/example/onlyone/domain/user/dto/response/MySettlementResponseDto.java (1)
src/main/java/com/example/onlyone/domain/user/dto/response/MySettlementDto.java (1)
Builder(10-30)
🔇 Additional comments (20)
src/main/java/com/example/onlyone/domain/user/dto/response/MySettlementDto.java (2)
10-12: Lombok 어노테이션 구성이 적절합니다.Builder 패턴과 Getter, AllArgsConstructor를 적절히 조합하여 불변 객체로 설계되었습니다.
14-20: 필드 구성이 정산 정보 조회에 적합합니다.정산 조회에 필요한 핵심 정보들(클럽ID, 스케줄ID, 금액, 이미지, 상태, 제목, 생성일시)을 모두 포함하고 있어 적절합니다.
src/main/java/com/example/onlyone/domain/user/dto/response/MySettlementResponseDto.java (2)
10-18: 페이지네이션 응답 DTO 구조가 표준적입니다.Spring의 Page 인터페이스와 일관된 필드명을 사용하여 표준적인 페이지네이션 응답 구조를 구현했습니다.
20-28: 정적 팩토리 메서드가 잘 구현되었습니다.Spring의 Page 객체를 적절히 매핑하여 응답 DTO로 변환하는 로직이 깔끔합니다.
src/main/java/com/example/onlyone/domain/chat/entity/ChatRoom.java (1)
27-31: CASCADE 제거가 적절합니다.ChatRoom이 삭제될 때 Club이 함께 삭제되는 것을 방지하기 위해 CascadeType.ALL을 제거한 것은 올바른 변경입니다. ChatRoom과 Club은 다대일 관계에서 자식이 부모를 삭제해서는 안 됩니다.
src/main/java/com/example/onlyone/domain/chat/entity/Message.java (1)
29-32: CASCADE 제거로 안전성이 향상되었습니다.Message에서 User로의 CASCADE를 제거하여 메시지 삭제 시 사용자가 함께 삭제되는 위험을 방지했습니다. 이는 데이터 일관성과 안전성 측면에서 올바른 변경입니다.
src/main/java/com/example/onlyone/domain/schedule/controller/ScheduleController.java (1)
77-83: 엔드포인트 추가 전반은 명확하고 일관적입니다경로/메서드/Swagger 문서화 모두 명확하며 서비스 계층 위임도 적절합니다.
src/main/java/com/example/onlyone/domain/user/controller/UserController.java (1)
13-15: 페이징 관련 의존성 추가 적절Pageable, Sort, PageableDefault 추가가 새 엔드포인트 요구사항과 잘 맞습니다.
src/main/java/com/example/onlyone/domain/schedule/service/ScheduleService.java (1)
228-233: 확인 완료 — ChatRoom 삭제 시 UserChatRoom 연관 삭제 보장됨ChatRoom 엔티티에 userChatRooms에 대해
cascade = CascadeType.ALL및orphanRemoval = true설정이 존재합니다. 따라서chatRoomRepository.delete(chatRoom)호출 시 연관된 UserChatRoom이 JPA 레벨에서 함께 제거되어 FK 제약 위반 우려는 없습니다.
- 확인 위치: src/main/java/com/example/onlyone/domain/chat/entity/ChatRoom.java —
@OneToMany(mappedBy = "chatRoom", cascade = CascadeType.ALL, orphanRemoval = true)(messages 약 라인 46, userChatRooms 약 라인 49)src/main/java/com/example/onlyone/domain/search/service/SearchService.java (3)
10-12: 의존성 및 DTO 도입 적절MyMeetingListResponseDto 도입과 Settlement 연동을 위한 import 추가가 변경 목적과 정합합니다.
37-37: UserSettlementRepository 주입 적절정산 상태 존재 여부 확인을 위해 필요한 의존성 주입이 적절합니다.
186-198: 확인 완료 — getMyClubs 반환 타입 변경이 호출부에 반영되어 문제없음요약: rg 결과, getMyClubs() 호출부는 한 곳(SearchController)뿐이며 컨트롤러에서 반환값을 CommonResponse.success(...)로 그대로 전달하고 있습니다. MyMeetingListResponseDto도 서비스/DTO에만 정의되어 있어 추가 수정 불필요합니다.
수정/검토 위치:
- src/main/java/com/example/onlyone/domain/search/controller/SearchController.java:83 — getMyClubs() 호출부
- src/main/java/com/example/onlyone/domain/search/service/SearchService.java:187 — 변경된 메서드 시그니처
- src/main/java/com/example/onlyone/domain/search/dto/response/MyMeetingListResponseDto.java — DTO 정의
src/main/java/com/example/onlyone/domain/user/service/UserService.java (4)
6-9: 정산 DTO 및 Repository import 추가 적절정산 조회 API 도입을 위한 import 추가가 일관적입니다.
28-29: Page/Pageable 및 LocalDateTime import 적절페이징 및 기간 필터링을 위한 의존성 추가 적절합니다.
Also applies to: 37-37
51-52: UserSettlementRepository 주입 적절정산 조회를 User 도메인으로 이동하는 방향과 정합합니다.
382-387: 최근/요청 정산 목록 조회 API 도입 적절현재 사용자 컨텍스트 기반, 10일 컷오프 필터, 페이징 변환까지 흐름이 간결합니다.
src/main/java/com/example/onlyone/domain/settlement/repository/UserSettlementRepository.java (2)
4-4: MySettlementDto 도입 적절Projection 기반 조회에 맞는 DTO import가 적절합니다.
87-91: 메서드 명세/프로젝션 일치 양호findMyRecentOrRequested는 DTO 생성자 인자 순서 및 countQuery와 정합적으로 보입니다.
src/main/java/com/example/onlyone/domain/settlement/service/SettlementService.java (1)
139-143: 비관적 락(PESSIMISTIC_WRITE) 적용 확인 — 수정 불필요WalletRepository에 @lock(LockModeType.PESSIMISTIC_WRITE)가 선언되어 있어 findByUser 호출에 비관적 락이 적용됩니다(스크립트 검증 완료).
- src/main/java/com/example/onlyone/domain/wallet/repository/WalletRepository.java — 라인 12: @lock(LockModeType.PESSIMISTIC_WRITE), 라인 13: Optional findByUser(User user);
src/main/java/com/example/onlyone/domain/payment/service/PaymentService.java (1)
117-126: 결론: “cascade 미설정” 지적은 부정확합니다 — WalletTransaction.payment에 CascadeType.ALL이 설정되어 있습니다간단히 검증한 결과, src/main/java/com/example/onlyone/domain/wallet/entity/WalletTransaction.java 에서
@OnetoOne(cascade = CascadeType.ALL, orphanRemoval = true) 로 Payment와 연관이 설정되어 있습니다.
PaymentService.confirm()에서는 walletTransactionRepository.save(walletTransaction) 이후에 payment 객체를 생성해 walletTransaction.updatePayment(payment) 하고 있으므로, 같은 트랜잭션 내에서 flush 시 JPA의 영속성 전파(cascade)로 Payment가 영속화됩니다. 따라서 반드시 paymentRepository.save(payment)를 추가해야 한다는 원리적 지적은 맞지 않습니다.권장 보완(안전성/명확성)
파일 위치
- src/main/java/com/example/onlyone/domain/wallet/entity/WalletTransaction.java — @OnetoOne(cascade = CascadeType.ALL, orphanRemoval = true)
- src/main/java/com/example/onlyone/domain/payment/service/PaymentService.java — confirm(...) (walletTransactionRepository.save(...) → walletTransaction.updatePayment(payment))
- src/main/java/com/example/onlyone/domain/payment/repository/PaymentRepository.java — findByTossOrderId(...) (멱등성 체크)
제안
- 명시적으로 저장을 원하면 paymentRepository.save(payment)를 호출해도 되며(읽는 사람에게 의도 명확), 또는 payment를 연결한 후 walletTransactionRepository.save(walletTransaction)를 한 번 더 호출해도 됩니다.
- race condition 대비: tossOrderId에 유니크 제약이 있으므로 저장 시 DataIntegrityViolationException(또는 JPA 예외)을 잡아 ErrorCode.ALREADY_COMPLETED_PAYMENT로 매핑하는 처리는 권장합니다.
권장 코드 예시(선택적)
walletTransaction.updatePayment(payment); - return response; + // cascade = CascadeType.ALL 이 설정되어 있으므로 대부분의 경우 명시적 save는 불필요합니다. + // (명시적 저장을 선호하면 아래 주석 해제) + // paymentRepository.save(payment); + // 또는 저장 주위에서 DataIntegrityViolationException을 catch하여 ALREADY_COMPLETED_PAYMENT로 매핑하세요. + return response;Likely an incorrect or invalid review comment.
src/main/java/com/example/onlyone/domain/payment/controller/PaymentController.java
Outdated
Show resolved
Hide resolved
src/main/java/com/example/onlyone/domain/payment/repository/PaymentRepository.java
Show resolved
Hide resolved
src/main/java/com/example/onlyone/domain/payment/service/PaymentService.java
Outdated
Show resolved
Hide resolved
src/main/java/com/example/onlyone/domain/schedule/service/ScheduleService.java
Show resolved
Hide resolved
src/main/java/com/example/onlyone/domain/schedule/service/ScheduleService.java
Show resolved
Hide resolved
src/main/java/com/example/onlyone/domain/search/dto/response/MyMeetingListResponseDto.java
Show resolved
Hide resolved
src/main/java/com/example/onlyone/domain/search/service/SearchService.java
Show resolved
Hide resolved
src/main/java/com/example/onlyone/domain/settlement/service/SettlementService.java
Outdated
Show resolved
Hide resolved
src/main/java/com/example/onlyone/domain/settlement/service/SettlementService.java
Outdated
Show resolved
Hide resolved
…into feat/schedule/gkdudans
현재 시각 이후만 가능하도록 수정
The merge-base changed after approval.
[feat] 정산, 결제 실패 로직 추가 / 스케줄 삭제 API 구현 / 정산 관련 API 응답 수정
#️⃣ Issue Number
📝 요약(Summary)
🛠️ PR 유형
어떤 변경 사항이 있나요?
📸스크린샷 (선택)
💬 공유사항 to 리뷰어
✅ PR Checklist
PR이 다음 요구 사항을 충족하는지 확인하세요.
Summary by CodeRabbit
New Features
Bug Fixes
Chores