Skip to content

[WTH-426] 회비 유저 사이드 api 구현#86

Merged
hyxklee merged 5 commits into
devfrom
feat/WTH-426-회비-유저-사이드-API-구현
Jul 1, 2026

Hidden character warning

The head ref may contain hidden characters: "feat/WTH-426-\ud68c\ube44-\uc720\uc800-\uc0ac\uc774\ub4dc-API-\uad6c\ud604"
Merged

[WTH-426] 회비 유저 사이드 api 구현#86
hyxklee merged 5 commits into
devfrom
feat/WTH-426-회비-유저-사이드-API-구현

Conversation

@hyxklee

@hyxklee hyxklee commented Jun 30, 2026

Copy link
Copy Markdown
Member

📌 Summary

어떤 작업인지 한 줄 요약해 주세요.

회비 유저 사이드 API 구현

📝 Changes

변경사항을 what, why, how로 구분해 작성해 주세요.

What

회비 유저 사이드 API 구현

Why

유저 사이드 기능 완성을 위해

How

  • 거래 내역 무한스크롤 조회
  • 개인 정보에 맞는 납부 현황 조회
  • 조회 가능한 회비 기수 조회 API -> 회비 드롭다운은 해당 API 이용해서 채워주세요! 기본값은 최신으로 보여주기
  • UI에 맞추기 위해 거래 생성시 생성자 이름 하드 코딩(큰 의미가 있는 데이터는 아니라고 생각해서 하드코딩으로 했습니당)

📸 Screenshots / Logs

필요시 스크린샷 or 로그를 첨부해주세요.

💡 Reviewer 참고사항

리뷰에 참고할 내용을 작성해주세요.

우선 빠르게 테스트해볼 수 있는 시나리오는 해봤는데, 아직 부족할 수 있습니다. 엣지는 프론트 연동 + QA 하면서 찾고 한 번에 수정하는게 좋을 것 같아요!
@dalzzy GET API들 보면 필터가 많은데, 기존처럼 전부 조회해서 프론트에서 자체 필터링 하는게 첫 번째고, 필요시 필터를 포함한 조회를 할 수 있게 부가적으로 열어둔 필터라고 생각해주시면 됩니다! ALL이 기본!

거래 내역 목록 API에서 duesSummary가 있는데 얘는 첫 페이지(무한 스크롤 초기 조회) 에서만 값을 주고 있어용. 그래서 첫 조회시 해당 정보를 캐싱해서 사용하고, 나머지만 업데이트 하는 방식으로 구현 부탁드립니닷. 회비 집계를 매번 반복할 수 없어서 이렇게 구현 했어요

✅ Checklist

  • PR 제목 설정 완료 (WTH-123 인증 필터 설정)
  • 테스트 구현 완료
  • 리뷰어 등록 완료
  • 자체 코드 리뷰 완료

Summary by CodeRabbit

  • New Features

    • 내 회비 정보, 기수 목록, 거래 목록/상세 조회가 추가되었습니다.
    • 거래에 영수증 첨부 및 조회가 가능해졌습니다.
    • 무한 스크롤용 응답 형식이 도입되었습니다.
  • Bug Fixes

    • 거래 목록에서 환불 표시와 회비 집계 기준이 더 정확해졌습니다.
    • 일부 거래는 등록자 이름이 함께 표시됩니다.
  • Breaking Changes

    • 기존 회비/영수증 저장·수정·삭제 관련 일부 API와 응답 형식이 변경되거나 제거되었습니다.

hyxklee and others added 3 commits June 30, 2026 21:06
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@hyxklee hyxklee requested review from dalzzy and soo0711 June 30, 2026 12:17
@hyxklee hyxklee self-assigned this Jun 30, 2026
@hyxklee hyxklee added the ✨ Feature 새로운 기능 추가 label Jun 30, 2026
@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@hyxklee, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 33 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 60f558b8-2715-4794-b6b0-caed5e57d641

📥 Commits

Reviewing files that changed from the base of the PR and between abfdedd and e12c8c5.

📒 Files selected for processing (5)
  • src/main/kotlin/com/weeth/domain/account/application/dto/request/SavePaymentTargetsRequest.kt
  • src/main/kotlin/com/weeth/domain/account/domain/repository/AccountPaymentTargetRepository.kt
  • src/main/kotlin/com/weeth/domain/account/presentation/AccountRegisterController.kt
  • src/test/kotlin/com/weeth/domain/account/application/usecase/command/MembershipFeeRegistrationIntegrationTest.kt
  • src/test/kotlin/com/weeth/domain/account/application/usecase/command/RegisterAccountUseCaseTest.kt
📝 Walkthrough

Walkthrough

Receipt 도메인(엔티티·매퍼·유스케이스·컨트롤러) 전체 삭제, Account 잔액을 currentBalance 단일 필드로 통합, AccountTransactionregisteredByName 추가, 거래에 파일 첨부(최대 1개) 지원, 납부 대상 저장 방식을 delta에서 snapshot으로 전환, 부원용 계정 조회 API(cardinals/me/transactions) 신설.

Changes

Account 도메인 리팩토링 및 부원 API 신설

Layer / File(s) Summary
도메인 엔티티 정리 및 DB 마이그레이션
src/main/kotlin/.../domain/entity/Account.kt, AccountTransaction.kt, Receipt.kt, domain/enums/AccountTargetStatus.kt, domain/enums/FileOwnerType.kt, db/migration/V6__drop_legacy_account_receipt.sql, db/migration/V7__add_account_transaction_registered_by_name.sql
Account에서 totalAmount/currentAmount 필드 및 spend/cancelSpend/adjustSpend/create 메서드 제거 후 currentBalance 단일화, AccountTransactionregisteredByName: String? 필드 및 registered_by_name 컬럼 추가, Receipt 엔티티 전체 삭제, FileOwnerType.RECEIPT 제거, V6(컬럼·테이블 드롭)/V7(컬럼 추가) 마이그레이션 적용.
리포지토리 쿼리 확장
domain/repository/AccountRepository.kt, AccountTransactionRepository.kt, AccountPaymentTargetRepository.kt
AccountRepositorymemberVisible=true + ACTIVE 기수 내림차순 조회 추가, AccountTransactionRepositoryfindMemberVisibleTransactions(Slice)/countMemberVisibleTransactions/sumNetDuesAmountByAccountId(DUES−REFUND) JPQL 추가, AccountPaymentTargetRepositoryfindByAccountIdAndClubMemberIdEXCLUDED 집계 주의 KDoc 추가.
Receipt·레거시 Account 인프라 제거
application/exception/AccountErrorCode.kt, ReceiptAccountMismatchException.kt, application/usecase/command/ManageAccountUseCase.kt
AccountErrorCode에서 RECEIPT_NOT_FOUND/RECEIPT_ACCOUNT_MISMATCH 제거, ReceiptAccountMismatchException 삭제, ManageAccountUseCase에서 cardinalReader/clubReader 의존성 및 save 메서드 제거(updateMemberVisibility만 잔존).
납부 대상 스냅샷 방식 전환
application/dto/request/SavePaymentTargetsRequest.kt, application/usecase/command/RegisterAccountUseCase.kt, presentation/AccountRegisterController.kt
SavePaymentTargetsRequest.targetedClubMemberIds @Schema를 스냅샷 방식(미포함=자동 제외, 빈 목록=전원 제외)으로 재서술, savePaymentTargets 내부 로직을 delta→snapshot으로 교체(excluded 처리 제거, 미선택 멤버 UNPAID 가드 후 exclude 전환), 경로 payment-targetsregistration/payment-targetsPATCHPUT 전환.
거래 파일 첨부 및 registeredByName 연동
dto/request/SaveAccountTransactionRequest.kt, UpdateAccountTransactionRequest.kt, mapper/AccountTransactionMapper.kt, usecase/command/ManageAccountTransactionUseCase.kt, ManageAccountPaymentUseCase.kt
거래 저장/수정 요청 DTO에 files: List<@NotNull FileSaveRequest>?(최대 1개) 추가, AccountTransactionMapperregisteredByName/receipts/hasReceipt 파라미터 추가, ManageAccountTransactionUseCaseFileRepository/FileReader/FileMapper 주입 및 replaceTransactionReceipts 헬퍼 구현, ManageAccountPaymentUseCase에서 requireAdmin 결과를 먼저 획득 후 registeredByName = admin.user.name 세팅.
부원용 응답 DTO 및 SliceResponse 추가
dto/response/AccountCardinalResponse.kt, MyAccountResponse.kt, MemberTransactionResponse.kt, MemberTransactionDetailResponse.kt, MemberAccountTransactionsResponse.kt, AccountTransactionResponse.kt, global/common/response/SliceResponse.kt, dto/request/AccountTransactionFilter.kt
AccountCardinalResponse/MyAccountResponse(중첩 DTO 포함)/MemberTransactionResponse/MemberTransactionDetailResponse/MemberAccountTransactionsResponse(TransactionCounts·DuesSummary 포함) 신규 추가, AccountTransactionResponsehasReceipt/receipts 추가, 무한 스크롤용 SliceResponse<T> 추가, AccountTransactionFilter Javadoc 갱신.
부원용 매퍼 및 접근 해석기 구현
mapper/MemberTransactionMapper.kt, mapper/MyAccountMapper.kt, usecase/MemberAccountAccessResolver.kt
MemberTransactionMapper 추가(toResponse/toDetailResponse, REFUND source를 "환불"로 마스킹하는 memberVisibleSource() 확장 함수, DEFAULT_REGISTERED_BY_NAME="운영진" 상수), MyAccountMapper 추가(toCardinalResponse/toResponse, AccountPaymentTarget?.toMyPaymentResponse() private 확장 함수), MemberAccountAccessResolver 추가(활성 부원 검증→memberVisible+ACTIVE 계정 조회→MemberAccountAccess 반환).
부원용 쿼리 서비스 구현
usecase/query/GetMyAccountQueryService.kt, GetMyAccountTransactionQueryService.kt, GetAccountTransactionQueryService.kt
GetMyAccountQueryService 추가(findCardinals: 기수 내림차순+isLatest, findMyAccount: 결제 타깃/목표액 기반 응답), GetMyAccountTransactionQueryService 추가(findTransactions: DUES 필터 시 빈 슬라이스/page=0만 집계/hasReceipt 배치 조회, findTransaction: 가시성 판정 후 상세 반환), GetAccountTransactionQueryServiceFileReader/FileMapper 주입 및 hasReceipt 배치 조회 연동.
API 엔드포인트 및 응답 코드 갱신
presentation/AccountController.kt, AccountResponseCode.kt
AccountController를 4개 엔드포인트(GET /cardinals, GET /{cardinal}/me, GET /{cardinal}/transactions, GET /{cardinal}/transactions/{transactionId})로 교체, AccountResponseCode에서 레거시 성공 코드 제거 및 ACCOUNT_MY_SUMMARY_FIND_SUCCESS(10129)/ACCOUNT_CARDINAL_LIST_FIND_SUCCESS(10130) 추가.
테스트 갱신
test/.../*Test.kt, test/.../fixture/*
AccountTransactionRequestValidationTest 추가(files @Size 검증), ManageAccountPaymentUseCaseTestregisteredByName 검증 추가, ManageAccountTransactionUseCaseTest에 files null/빈 배열/교체 3분기 시나리오 추가, RegisterAccountUseCaseTest 스냅샷 스텁 전환, MembershipFeeRegistrationIntegrationTest에 S12(sumNetDuesAmountByAccountId) 추가, GetMyAccountQueryServiceTest/GetMyAccountTransactionQueryServiceTest/AccountControllerTest 신규 추가, AccountTestFixture currentBalance 전환.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant AccountController
  participant GetMyAccountTransactionQueryService
  participant MemberAccountAccessResolver
  participant AccountTransactionRepository
  participant FileReader
  participant MemberTransactionMapper

  Client->>AccountController: GET /{cardinal}/transactions?filter=ALL&page=0
  AccountController->>GetMyAccountTransactionQueryService: findTransactions(clubId, cardinal, filter, sort, page, size, userId)
  GetMyAccountTransactionQueryService->>MemberAccountAccessResolver: resolve(clubId, cardinal, userId)
  MemberAccountAccessResolver-->>GetMyAccountTransactionQueryService: MemberAccountAccess(account, member)
  GetMyAccountTransactionQueryService->>AccountTransactionRepository: findMemberVisibleTransactions(accountId, clubMemberId, publicTypes, includeRefund, pageable)
  AccountTransactionRepository-->>GetMyAccountTransactionQueryService: Slice<AccountTransaction>
  GetMyAccountTransactionQueryService->>FileReader: findAll(ACCOUNT_TRANSACTION, transactionIds)
  FileReader-->>GetMyAccountTransactionQueryService: Map<ownerId, List<File>>
  GetMyAccountTransactionQueryService->>AccountTransactionRepository: sumNetDuesAmountByAccountId(accountId)
  GetMyAccountTransactionQueryService->>AccountTransactionRepository: countMemberVisibleTransactions(...)
  GetMyAccountTransactionQueryService->>MemberTransactionMapper: toResponse(transaction, hasReceipt)
  GetMyAccountTransactionQueryService-->>AccountController: MemberAccountTransactionsResponse(counts, duesSummary, transactions)
  AccountController-->>Client: 200 OK
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

  • Team-Weeth/weeth-server#78: Account/AccountPaymentTarget/AccountTransaction 도메인 엔티티·enum·레포지토리 초기 구현을 포함하며, 현재 PR이 이를 기반으로 잔액 필드 통합 및 registeredByName 추가를 수행함.
  • Team-Weeth/weeth-server#84: AccountTransactionFilter(enum/Javadoc)와 AccountTransactionResponse DTO 영역이 현재 PR과 동일 파일에서 변경되어 직접적으로 연관됨.
  • Team-Weeth/weeth-server#80: ManageAccountUseCase·AccountRegisterController 영역의 온보딩 API 구현을 포함하며, 현재 PR이 해당 컴포넌트의 save 메서드 제거 및 라우팅 변경을 수행함.

Suggested labels

✨ Feature

Suggested reviewers

  • JIN921
  • soo0711

Poem

🐇 영수증 테이블이여, 안녕!
currentBalance 하나로 깔끔하게~
부원도 이제 거래 목록을 볼 수 있어,
스냅샷으로 납부 대상 딱 정리했지.
파일도 첨부되고, 등록자도 기록되고 🎉
토끼는 오늘도 리팩토링에 만족해!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed 제목이 회비 유저 사이드 API 구현이라는 주요 변경점을 간결하게 잘 요약합니다.
Description check ✅ Passed 요구된 섹션 구조와 What/Why/How, 체크리스트가 모두 포함되어 있어 템플릿을 대체로 충족합니다.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/WTH-426-회비-유저-사이드-API-구현

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🧹 Nitpick comments (4)
src/test/kotlin/com/weeth/domain/account/application/usecase/command/ManageAccountTransactionUseCaseTest.kt (2)

67-67: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

여기도 등록자 이름을 문자열 리터럴로 고정하지 않는 편이 좋습니다.

"적순" 대신 adminMember.user.name을 쓰면 fixture 기본값 변경에 덜 취약하고, 테스트가 유스케이스 계약만 검증하게 됩니다.

Also applies to: 152-152

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/test/kotlin/com/weeth/domain/account/application/usecase/command/ManageAccountTransactionUseCaseTest.kt`
at line 67, The test setup is hardcoding the registrar/admin member name instead
of using the fixture value, which makes it brittle. In
ManageAccountTransactionUseCaseTest, replace the fixed string reference with
adminMember.user.name wherever the expected registrar name is asserted, so the
test stays aligned with ClubMemberTestFixture and UserTestFixture defaults and
only verifies the use case contract.

115-116: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

영수증 교체 케이스가 “새 파일이 응답에 반영되는지”는 아직 못 잡습니다.

fileMapper.toFileResponse(any())가 항상 같은 receiptResponse를 돌려서, 구현이 실수로 oldReceipt를 응답에 매핑해도 이 테스트는 통과합니다. oldReceipt/newReceipt에 서로 다른 응답을 매핑하거나 fileMapper.toFileResponse(newReceipt) 호출을 직접 검증해 두는 편이 안전합니다.

Also applies to: 287-309

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/test/kotlin/com/weeth/domain/account/application/usecase/command/ManageAccountTransactionUseCaseTest.kt`
around lines 115 - 116, The receipt replacement test is too permissive because
`fileMapper.toFileResponse(any())` always returns the same `receiptResponse`, so
it cannot detect if `ManageAccountTransactionUseCase` maps `oldReceipt` instead
of `newReceipt`. Update the test setup around `fileMapper.toFileResponse` and
the `ManageAccountTransactionUseCase` flow so `oldReceipt` and `newReceipt`
produce different responses, or explicitly verify
`fileMapper.toFileResponse(newReceipt)` is called and reflected in the result.
src/test/kotlin/com/weeth/domain/account/application/usecase/command/ManageAccountPaymentUseCaseTest.kt (1)

53-57: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

등록자 이름 기대값은 fixture 값에서 읽어오세요.

지금은 "적순"을 하드코딩해서 UserTestFixture의 기본 이름만 바뀌어도 유스케이스 계약과 무관하게 테스트가 깨집니다. 기대값을 adminMember.user.name으로 두면 검증 대상이 registeredByName = admin.user.name 계약에만 남습니다.

수정 예시
-                                it.first().registeredByName == "적순"
+                                it.first().registeredByName == adminMember.user.name
...
-                                it.first().registeredByName == "적순"
+                                it.first().registeredByName == adminMember.user.name

Also applies to: 90-91, 160-161

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/test/kotlin/com/weeth/domain/account/application/usecase/command/ManageAccountPaymentUseCaseTest.kt`
around lines 53 - 57, The tests are hardcoding the expected registrant name
instead of deriving it from the fixture, which makes them fragile against
unrelated fixture changes. Update the assertions in
ManageAccountPaymentUseCaseTest to use adminMember.user.name as the expected
value wherever the registered-by name is checked, so the test only validates the
registeredByName = admin.user.name contract and stays aligned with
UserTestFixture and ClubMemberTestFixture.
src/test/kotlin/com/weeth/domain/account/presentation/AccountControllerTest.kt (1)

22-156: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

컨트롤러 계약은 MVC 테스트로 한 번 더 고정하세요.
지금처럼 메서드를 직접 호출하면 서비스 위임만 보이고, @CurrentUser 주입·@RequestParam 기본값·CommonResponse<T> 응답 형태는 놓칠 수 있습니다. 대표 케이스 1개는 @WebMvcTest + @MockkBean으로 추가하는 편이 안전합니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/test/kotlin/com/weeth/domain/account/presentation/AccountControllerTest.kt`
around lines 22 - 156, Add a WebMvcTest for AccountController to lock down the
controller contract, since the current direct method calls only verify service
delegation. Create one representative MVC test using AccountController with
`@MockkBean` mocks for GetMyAccountQueryService and
GetMyAccountTransactionQueryService, and assert `@CurrentUser/`@RequestParam
binding, default parameter handling, and the CommonResponse<T> shape are
returned correctly.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@src/main/kotlin/com/weeth/domain/account/application/dto/request/SavePaymentTargetsRequest.kt`:
- Around line 5-13: The SavePaymentTargetsRequest.targetedClubMemberIds field is
currently defaulting to emptyList(), which makes a missing JSON field
indistinguishable from an intentional empty selection; update the
SavePaymentTargetsRequest data class so targetedClubMemberIds is required with
no default value, and keep the Schema metadata/example aligned with that
stricter contract to ensure callers must explicitly provide the list.

In
`@src/main/kotlin/com/weeth/domain/account/application/usecase/command/RegisterAccountUseCase.kt`:
- Around line 279-339: The `savePaymentTargets` use case currently only locks
the account via `getAccountWithLock()` and can still mutate an ACTIVE account’s
payment targets. Add an explicit status guard in
`RegisterAccountUseCase.savePaymentTargets` (using
`account`/`AccountRegistrationStep` or the account’s current state) so this
registration-only path accepts only DRAFT accounts and rejects ACTIVE ones
before any `paymentTargetRepository` changes. Keep the check near the start of
the method, after loading `account`, so no TARGETED/EXCLUDED updates or new
`AccountPaymentTarget.createTargeted` rows can be applied to operational
ledgers.

In
`@src/main/kotlin/com/weeth/domain/account/application/usecase/query/GetMyAccountTransactionQueryService.kt`:
- Around line 66-73: In GetMyAccountTransactionQueryService, the paging branch
is nulling out both duesSummary and counts, but the review asks to keep counts
available on follow-up pages. Update the logic around countByFilter and
MemberAccountTransactionsResponse.DuesSummaryResponse so only duesSummary is
first-page-only, while counts is returned consistently for every page, or
otherwise align the response contract and client usage if counts is truly meant
to be first-page-only.

In
`@src/main/kotlin/com/weeth/domain/account/domain/repository/AccountPaymentTargetRepository.kt`:
- Around line 201-205: The KDoc example in AccountPaymentTargetRepository is
inconsistent with the stated rule because it uses addition instead of
subtraction for deriving the excluded count. Update the documentation near the
countActiveClubMemberTargetsByAccountIdAndTargetStatus reference so the example
matches the invariant “active roster − active TARGETED” and clearly shows
subtraction-based derivation, keeping the wording aligned with
AccountTargetStatus.

In
`@src/main/kotlin/com/weeth/domain/account/presentation/AccountRegisterController.kt`:
- Around line 171-177: The OpenAPI docs in AccountRegisterController are
inconsistent: the PUT endpoint for payment targets is documented as a full
snapshot/replace operation, but the related `findPaymentTargets()` description
still tells clients to send only changed members as deltas. Update the
`findPaymentTargets()` operation text to match the snapshot contract used by
`registration/payment-targets`, and ensure the wording clearly states that
omitted members are excluded and that the full target set should be handled
consistently by the frontend.

In `@src/main/resources/db/migration/V6__drop_legacy_account_receipt.sql`:
- Line 10: The V6 migration currently drops only the receipt table, but it also
needs to account for legacy RECEIPT-owned file data tied to
FileOwnerType.RECEIPT. Update the migration around the receipt cleanup step to
either delete or migrate any remaining RECEIPT file records before dropping the
table, or add a guard/assertion that fails if such rows still exist, so no
orphaned data or enum-mapping issues remain after the migration.

---

Nitpick comments:
In
`@src/test/kotlin/com/weeth/domain/account/application/usecase/command/ManageAccountPaymentUseCaseTest.kt`:
- Around line 53-57: The tests are hardcoding the expected registrant name
instead of deriving it from the fixture, which makes them fragile against
unrelated fixture changes. Update the assertions in
ManageAccountPaymentUseCaseTest to use adminMember.user.name as the expected
value wherever the registered-by name is checked, so the test only validates the
registeredByName = admin.user.name contract and stays aligned with
UserTestFixture and ClubMemberTestFixture.

In
`@src/test/kotlin/com/weeth/domain/account/application/usecase/command/ManageAccountTransactionUseCaseTest.kt`:
- Line 67: The test setup is hardcoding the registrar/admin member name instead
of using the fixture value, which makes it brittle. In
ManageAccountTransactionUseCaseTest, replace the fixed string reference with
adminMember.user.name wherever the expected registrar name is asserted, so the
test stays aligned with ClubMemberTestFixture and UserTestFixture defaults and
only verifies the use case contract.
- Around line 115-116: The receipt replacement test is too permissive because
`fileMapper.toFileResponse(any())` always returns the same `receiptResponse`, so
it cannot detect if `ManageAccountTransactionUseCase` maps `oldReceipt` instead
of `newReceipt`. Update the test setup around `fileMapper.toFileResponse` and
the `ManageAccountTransactionUseCase` flow so `oldReceipt` and `newReceipt`
produce different responses, or explicitly verify
`fileMapper.toFileResponse(newReceipt)` is called and reflected in the result.

In
`@src/test/kotlin/com/weeth/domain/account/presentation/AccountControllerTest.kt`:
- Around line 22-156: Add a WebMvcTest for AccountController to lock down the
controller contract, since the current direct method calls only verify service
delegation. Create one representative MVC test using AccountController with
`@MockkBean` mocks for GetMyAccountQueryService and
GetMyAccountTransactionQueryService, and assert `@CurrentUser/`@RequestParam
binding, default parameter handling, and the CommonResponse<T> shape are
returned correctly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 759fdb0e-1aee-4863-9663-112b6d2f8510

📥 Commits

Reviewing files that changed from the base of the PR and between 9ef820c and abfdedd.

📒 Files selected for processing (68)
  • src/main/kotlin/com/weeth/domain/account/application/dto/request/AccountSaveRequest.kt
  • src/main/kotlin/com/weeth/domain/account/application/dto/request/AccountTransactionFilter.kt
  • src/main/kotlin/com/weeth/domain/account/application/dto/request/ReceiptSaveRequest.kt
  • src/main/kotlin/com/weeth/domain/account/application/dto/request/ReceiptUpdateRequest.kt
  • src/main/kotlin/com/weeth/domain/account/application/dto/request/SaveAccountTransactionRequest.kt
  • src/main/kotlin/com/weeth/domain/account/application/dto/request/SavePaymentTargetsRequest.kt
  • src/main/kotlin/com/weeth/domain/account/application/dto/request/UpdateAccountTransactionRequest.kt
  • src/main/kotlin/com/weeth/domain/account/application/dto/response/AccountCardinalResponse.kt
  • src/main/kotlin/com/weeth/domain/account/application/dto/response/AccountResponse.kt
  • src/main/kotlin/com/weeth/domain/account/application/dto/response/AccountTransactionResponse.kt
  • src/main/kotlin/com/weeth/domain/account/application/dto/response/MemberAccountTransactionsResponse.kt
  • src/main/kotlin/com/weeth/domain/account/application/dto/response/MemberTransactionDetailResponse.kt
  • src/main/kotlin/com/weeth/domain/account/application/dto/response/MemberTransactionResponse.kt
  • src/main/kotlin/com/weeth/domain/account/application/dto/response/MyAccountResponse.kt
  • src/main/kotlin/com/weeth/domain/account/application/dto/response/ReceiptResponse.kt
  • src/main/kotlin/com/weeth/domain/account/application/exception/AccountErrorCode.kt
  • src/main/kotlin/com/weeth/domain/account/application/exception/ReceiptAccountMismatchException.kt
  • src/main/kotlin/com/weeth/domain/account/application/exception/ReceiptNotFoundException.kt
  • src/main/kotlin/com/weeth/domain/account/application/mapper/AccountMapper.kt
  • src/main/kotlin/com/weeth/domain/account/application/mapper/AccountTransactionMapper.kt
  • src/main/kotlin/com/weeth/domain/account/application/mapper/MemberTransactionMapper.kt
  • src/main/kotlin/com/weeth/domain/account/application/mapper/MyAccountMapper.kt
  • src/main/kotlin/com/weeth/domain/account/application/mapper/ReceiptMapper.kt
  • src/main/kotlin/com/weeth/domain/account/application/usecase/MemberAccountAccessResolver.kt
  • src/main/kotlin/com/weeth/domain/account/application/usecase/command/ManageAccountPaymentUseCase.kt
  • src/main/kotlin/com/weeth/domain/account/application/usecase/command/ManageAccountTransactionUseCase.kt
  • src/main/kotlin/com/weeth/domain/account/application/usecase/command/ManageAccountUseCase.kt
  • src/main/kotlin/com/weeth/domain/account/application/usecase/command/ManageReceiptUseCase.kt
  • src/main/kotlin/com/weeth/domain/account/application/usecase/command/RegisterAccountUseCase.kt
  • src/main/kotlin/com/weeth/domain/account/application/usecase/query/GetAccountQueryService.kt
  • src/main/kotlin/com/weeth/domain/account/application/usecase/query/GetAccountTransactionQueryService.kt
  • src/main/kotlin/com/weeth/domain/account/application/usecase/query/GetMyAccountQueryService.kt
  • src/main/kotlin/com/weeth/domain/account/application/usecase/query/GetMyAccountTransactionQueryService.kt
  • src/main/kotlin/com/weeth/domain/account/domain/entity/Account.kt
  • src/main/kotlin/com/weeth/domain/account/domain/entity/AccountTransaction.kt
  • src/main/kotlin/com/weeth/domain/account/domain/entity/Receipt.kt
  • src/main/kotlin/com/weeth/domain/account/domain/enums/AccountTargetStatus.kt
  • src/main/kotlin/com/weeth/domain/account/domain/repository/AccountPaymentTargetRepository.kt
  • src/main/kotlin/com/weeth/domain/account/domain/repository/AccountRepository.kt
  • src/main/kotlin/com/weeth/domain/account/domain/repository/AccountTransactionRepository.kt
  • src/main/kotlin/com/weeth/domain/account/domain/repository/ReceiptRepository.kt
  • src/main/kotlin/com/weeth/domain/account/presentation/AccountController.kt
  • src/main/kotlin/com/weeth/domain/account/presentation/AccountRegisterController.kt
  • src/main/kotlin/com/weeth/domain/account/presentation/AccountResponseCode.kt
  • src/main/kotlin/com/weeth/domain/account/presentation/ReceiptAdminController.kt
  • src/main/kotlin/com/weeth/domain/file/domain/enums/FileOwnerType.kt
  • src/main/kotlin/com/weeth/global/common/response/SliceResponse.kt
  • src/main/resources/db/migration/V6__drop_legacy_account_receipt.sql
  • src/main/resources/db/migration/V7__add_account_transaction_registered_by_name.sql
  • src/test/kotlin/com/weeth/domain/account/application/dto/request/AccountTransactionRequestValidationTest.kt
  • src/test/kotlin/com/weeth/domain/account/application/usecase/command/ManageAccountPaymentUseCaseTest.kt
  • src/test/kotlin/com/weeth/domain/account/application/usecase/command/ManageAccountTransactionUseCaseTest.kt
  • src/test/kotlin/com/weeth/domain/account/application/usecase/command/ManageAccountUseCaseTest.kt
  • src/test/kotlin/com/weeth/domain/account/application/usecase/command/ManageReceiptUseCaseTest.kt
  • src/test/kotlin/com/weeth/domain/account/application/usecase/command/MembershipFeeRegistrationIntegrationTest.kt
  • src/test/kotlin/com/weeth/domain/account/application/usecase/command/RegisterAccountUseCaseTest.kt
  • src/test/kotlin/com/weeth/domain/account/application/usecase/query/GetAccountQueryServiceTest.kt
  • src/test/kotlin/com/weeth/domain/account/application/usecase/query/GetAccountRegistrationQueryServiceTest.kt
  • src/test/kotlin/com/weeth/domain/account/application/usecase/query/GetAccountTransactionQueryServiceTest.kt
  • src/test/kotlin/com/weeth/domain/account/application/usecase/query/GetMyAccountQueryServiceTest.kt
  • src/test/kotlin/com/weeth/domain/account/application/usecase/query/GetMyAccountTransactionQueryServiceTest.kt
  • src/test/kotlin/com/weeth/domain/account/domain/entity/AccountTest.kt
  • src/test/kotlin/com/weeth/domain/account/domain/entity/ReceiptTest.kt
  • src/test/kotlin/com/weeth/domain/account/domain/repository/AccountRepositoryTest.kt
  • src/test/kotlin/com/weeth/domain/account/fixture/AccountTestFixture.kt
  • src/test/kotlin/com/weeth/domain/account/fixture/ReceiptTestFixture.kt
  • src/test/kotlin/com/weeth/domain/account/presentation/AccountControllerTest.kt
  • src/test/kotlin/com/weeth/domain/file/domain/repository/FileRepositoryTest.kt
💤 Files with no reviewable changes (23)
  • src/main/kotlin/com/weeth/domain/account/application/dto/response/AccountResponse.kt
  • src/main/kotlin/com/weeth/domain/account/application/exception/ReceiptAccountMismatchException.kt
  • src/main/kotlin/com/weeth/domain/account/domain/repository/ReceiptRepository.kt
  • src/test/kotlin/com/weeth/domain/account/domain/entity/ReceiptTest.kt
  • src/main/kotlin/com/weeth/domain/account/application/dto/request/ReceiptUpdateRequest.kt
  • src/test/kotlin/com/weeth/domain/account/application/usecase/command/ManageReceiptUseCaseTest.kt
  • src/main/kotlin/com/weeth/domain/account/application/dto/request/AccountSaveRequest.kt
  • src/main/kotlin/com/weeth/domain/account/application/mapper/ReceiptMapper.kt
  • src/main/kotlin/com/weeth/domain/file/domain/enums/FileOwnerType.kt
  • src/main/kotlin/com/weeth/domain/account/application/dto/request/ReceiptSaveRequest.kt
  • src/main/kotlin/com/weeth/domain/account/application/exception/ReceiptNotFoundException.kt
  • src/main/kotlin/com/weeth/domain/account/application/dto/response/ReceiptResponse.kt
  • src/main/kotlin/com/weeth/domain/account/domain/entity/Receipt.kt
  • src/test/kotlin/com/weeth/domain/account/fixture/ReceiptTestFixture.kt
  • src/main/kotlin/com/weeth/domain/account/application/usecase/command/ManageReceiptUseCase.kt
  • src/main/kotlin/com/weeth/domain/account/application/usecase/query/GetAccountQueryService.kt
  • src/test/kotlin/com/weeth/domain/account/application/usecase/query/GetAccountRegistrationQueryServiceTest.kt
  • src/test/kotlin/com/weeth/domain/account/application/usecase/query/GetAccountQueryServiceTest.kt
  • src/main/kotlin/com/weeth/domain/account/application/mapper/AccountMapper.kt
  • src/test/kotlin/com/weeth/domain/account/domain/repository/AccountRepositoryTest.kt
  • src/test/kotlin/com/weeth/domain/account/domain/entity/AccountTest.kt
  • src/main/kotlin/com/weeth/domain/account/application/usecase/command/ManageAccountUseCase.kt
  • src/main/kotlin/com/weeth/domain/account/presentation/ReceiptAdminController.kt

Comment on lines +66 to +73
val (counts, duesSummary) =
if (pageable.pageNumber == 0) {
val duesTotalAmount = transactionRepository.sumNetDuesAmountByAccountId(account.id).toInt()
countByFilter(account.id, member.id, duesTotalAmount) to
MemberAccountTransactionsResponse.DuesSummaryResponse(totalAmount = duesTotalAmount)
} else {
null to null
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

후속 페이지에서도 counts를 비우지 마세요.

Line 66-73은 duesSummary뿐 아니라 counts도 함께 null로 만듭니다. 이렇게 되면 탭 카운트도 첫 응답을 별도로 캐시해야 해서 API 계약이 달라집니다. counts는 모든 페이지에서 유지하거나, 정말 첫 페이지 전용이라면 응답 스펙과 클라이언트 계약을 함께 정리한 뒤 머지하는 편이 안전합니다. Based on PR objectives: duesSummary만 첫 페이지에서 반환되고 프런트가 이를 캐시한다고 명시돼 있습니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/kotlin/com/weeth/domain/account/application/usecase/query/GetMyAccountTransactionQueryService.kt`
around lines 66 - 73, In GetMyAccountTransactionQueryService, the paging branch
is nulling out both duesSummary and counts, but the review asks to keep counts
available on follow-up pages. Update the logic around countByFilter and
MemberAccountTransactionsResponse.DuesSummaryResponse so only duesSummary is
first-page-only, while counts is returned consistently for every page, or
otherwise align the response contract and client usage if counts is truly meant
to be first-page-only.

Comment thread src/main/resources/db/migration/V6__drop_legacy_account_receipt.sql

@soo0711 soo0711 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

수고하셨습니다!! 코드래빗 피드백만 보면 될 것 같습니닷 👍

hyxklee added 2 commits July 1, 2026 11:52
# Conflicts:
#	src/main/kotlin/com/weeth/domain/account/domain/repository/AccountPaymentTargetRepository.kt
@hyxklee hyxklee merged commit 0c16486 into dev Jul 1, 2026
2 checks passed
@hyxklee hyxklee deleted the feat/WTH-426-회비-유저-사이드-API-구현 branch July 1, 2026 03:00
@coderabbitai coderabbitai Bot mentioned this pull request Jul 6, 2026
4 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨ Feature 새로운 기능 추가

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants