Skip to content

feat: [#153] 관리자용 챌린지 인증 구현#167

Merged
HJunng merged 9 commits intomainfrom
feat/#153-admin-challenge-certifications
Jul 29, 2025
Merged

feat: [#153] 관리자용 챌린지 인증 구현#167
HJunng merged 9 commits intomainfrom
feat/#153-admin-challenge-certifications

Conversation

@HJunng
Copy link

@HJunng HJunng commented Jul 29, 2025

#️⃣ 연관된 이슈

closed #153

📝 작업 내용

관리자가 챌린지 인증을 효율적으로 관리할 수 있는 API를 구현했습니다.

개인/팀 챌린지 인증 목록을 복합 조건으로 조회하고, 인증 상태를 관리할 수 있는 기능을 제공합니다.

🔵 개인 챌린지 인증 관리

  • 1. 개인 챌린지 인증 목록 복합 조건 조회 (메인)
    • 챌린지 ID, 참여자 memberKey, 인증 상태로 필터링
    • 커서 기반 페이징 (기본 10건)
  • 1-1. 개인 챌린지 제목 목록 조회 (드롭다운용)
  • 1-2. 참여자 memberKey 목록 조회 (드롭다운용)

🟡 팀 챌린지 인증 관리

  • 2. 팀 챌린지 인증 목록 복합 조건 조회 (메인)
    • 챌린지 ID, 그룹 코드, 인증 상태로 필터링
    • 커서 기반 페이징 (기본 10건)
  • 2-1. 팀 챌린지 제목 목록 조회 (드롭다운용)
  • 2-2. 그룹 코드 목록 조회 (드롭다운용)

공통 기능

  • 3. 인증 상태 업데이트 (승인/거절)
    • PENDING → PAID/REJECTED 상태 변경

💬 리뷰 요구사항(선택)

리뷰어가 특별히 봐주었으면 하는 부분이 있다면 작성해주세요

ex) 메서드 명칭

📌 PR 진행 시 참고사항

  • 리뷰어는 좋은 코드 방향을 제시하되, 수정을 강요하지 않습니다.
  • 좋은 코드를 발견하면 칭찬과 격려를 아끼지 않습니다.
  • 리뷰는 Reviewer로 지정된 시점 기준으로 3일 이내에 진행해 주세요.
  • Comment 작성 시 아래 Prefix를 사용해 주세요:
    • P1: 꼭 반영해 주세요 (Request Changes) – 이슈나 취약점 관련
    • P2: 반영을 고려해 주세요 (Comment) – 개선 의견
    • P3: 단순 제안 (Chore)

Summary by CodeRabbit

  • New Features

    • Added comprehensive admin APIs for managing challenge certifications, including endpoints to view challenge titles, participant member keys, group codes, and filterable certification lists with cursor-based pagination.
    • Enabled administrators to approve or reject certifications with explicit status updates.
  • Improvements

    • Enhanced certification status management with a new status enum (PENDING, PAID, REJECTED) for clearer tracking.
    • Updated API documentation for all new admin endpoints and request/response formats.
  • Bug Fixes

    • Improved error messages for invalid or already-processed certifications.
  • Refactor

    • Refactored existing certification DTOs and entities to use the new status enum, providing more descriptive certification state information.

HJunng added 6 commits July 29, 2025 18:33
- 기존 챌린지 인증 관련 DTO 필드, 네이밍 수정 및 불필요한 코드 제거
- 관리자용 인증 상태 업데이트, 참여자, 챌린지 제목 등 신규 DTO 추가
- 개인/팀 챌린지 인증 DTO 생성 메서드 리팩토링
- 인증 상태 enum 적용 및 관련 필드 개선
- CertificationStatus enum 추가 및 기존 Boolean 필드 대체
- 인증 상태 관리에 따른 승인/거절/수정 메서드 리팩토링
- 상태 기반의 예외 처리 및 상태 확인 메서드 추가
- 개인/팀 챌린지 인증 검색을 위한 복합 조건 및 페이징 쿼리 로직 추가
- 챌린지 그룹 코드 및 참여자 memberKey 조회 기능 구현
- 인증 상태 기반 필터링 로직 리팩토링
- 개인/팀 챌린지 제목, 그룹 코드, 참여자 조회 메서드 구현
- 관리자용 복합 조건 인증 검색 및 필터링 로직 추가
- 인증 상태 업데이트 로직 및 내부 메서드 리팩토링
- 개인/팀 챌린지 제목, 그룹 코드, 참여자 조회 엔드포인트 구현
- 복합 조건 기반 인증 검색 및 페이징 로직 추가
- 인증 상태 업데이트 메서드 구현 및 메시지 추가
- 관련 Swagger 문서 인터페이스 정의 및 주석 보완
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jul 29, 2025

Walkthrough

A comprehensive administrative API layer for challenge certifications was introduced, including new REST controllers, DTOs, service logic, repository queries, and documentation. The certification approval logic was refactored from a boolean flag to a status enum, with corresponding changes across entities, DTOs, and repositories. Extensive filtering, pagination, and status update features for admin use were added.

Changes

Cohort / File(s) Change Summary
Admin Controller & Docs
src/main/java/com/example/green/domain/challengecert/controller/AdminChallengeCertificationController.java,
src/main/java/com/example/green/domain/challengecert/controller/docs/AdminChallengeCertificationControllerDocs.java
Added a secured admin REST controller with endpoints for listing, filtering, and updating challenge certifications; introduced Swagger/OpenAPI documentation interface for all admin endpoints.
Admin DTOs
src/main/java/com/example/green/domain/challengecert/dto/AdminCertificationStatusUpdateRequestDto.java,
src/main/java/com/example/green/domain/challengecert/dto/AdminChallengeTitleResponseDto.java,
src/main/java/com/example/green/domain/challengecert/dto/AdminGroupCodeResponseDto.java,
src/main/java/com/example/green/domain/challengecert/dto/AdminParticipantMemberKeyResponseDto.java,
src/main/java/com/example/green/domain/challengecert/dto/AdminPersonalCertificationSearchRequestDto.java,
src/main/java/com/example/green/domain/challengecert/dto/AdminTeamCertificationSearchRequestDto.java
Introduced DTO records for admin challenge certification operations, including request/response types and search filters for both personal and team challenges.
Certification Status Refactor
src/main/java/com/example/green/domain/challengecert/entity/BaseChallengeCertification.java,
src/main/java/com/example/green/domain/challengecert/enums/CertificationStatus.java,
src/main/java/com/example/green/domain/challengecert/exception/ChallengeCertExceptionMessage.java
Replaced boolean approval logic with a CertificationStatus enum (PENDING, PAID, REJECTED); updated entity fields, approval/rejection methods, and exception messages accordingly.
Certification DTO Refactor
src/main/java/com/example/green/domain/challengecert/dto/ChallengeCertificationDetailResponseDto.java,
src/main/java/com/example/green/domain/challengecert/dto/ChallengeCertificationListResponseDto.java
Refactored certification DTOs to reflect new status enum, include member info, and update static factory methods for new entity fields.
Repository Enhancements
src/main/java/com/example/green/domain/challengecert/repository/PersonalChallengeCertificationRepositoryCustom.java,
src/main/java/com/example/green/domain/challengecert/repository/PersonalChallengeCertificationRepositoryImpl.java,
src/main/java/com/example/green/domain/challengecert/repository/TeamChallengeCertificationRepositoryCustom.java,
src/main/java/com/example/green/domain/challengecert/repository/TeamChallengeCertificationRepositoryImpl.java
Added custom repository methods for admin filtering, participant/group listing, and cursor-based pagination for both personal and team certifications; implemented dynamic query logic.
Service Layer
src/main/java/com/example/green/domain/challengecert/service/ChallengeCertificationService.java
Added admin service methods for listing, filtering, and updating challenge certifications; integrated new DTOs and status logic; added transactional status update.
Response Messages
src/main/java/com/example/green/domain/challengecert/controller/message/ChallengeCertificationResponseMessage.java
Added new enum constants for admin API response messages.
Test Update
src/test/java/com/example/green/domain/challengecert/repository/PersonalChallengeCertificationRepositoryImplTest.java
Updated test assertion to use new status enum instead of boolean approval.

Sequence Diagram(s)

sequenceDiagram
    participant Admin as Admin User
    participant Controller as AdminChallengeCertificationController
    participant Service as ChallengeCertificationService
    participant Repo as CertificationRepository

    Admin->>Controller: PATCH /api/admin/certifications/{id} (status update)
    Controller->>Service: updateCertificationStatus(certificationId, status)
    Service->>Repo: findById(certificationId)
    Repo-->>Service: Certification entity
    Service->>Service: validate status & update entity (approve/reject)
    Service->>Repo: save changes
    Service-->>Controller: void
    Controller-->>Admin: ApiTemplate<Void> (success)
Loading
sequenceDiagram
    participant Admin as Admin User
    participant Controller as AdminChallengeCertificationController
    participant Service as ChallengeCertificationService
    participant Repo as CertificationRepository

    Admin->>Controller: GET /api/admin/personal-certifications (with filters)
    Controller->>Service: getPersonalCertificationsWithFilters(searchRequest)
    Service->>Repo: findPersonalCertificationsWithFilters(searchRequest)
    Repo-->>Service: CursorTemplate with DTOs
    Service-->>Controller: CursorTemplate with DTOs
    Controller-->>Admin: ApiTemplate<CursorTemplate<...>>
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Assessment against linked issues

Objective Addressed Explanation
Implement challenge certification API (#153)
Support admin listing/filtering of certifications (#153)
Support admin approval/rejection of certifications (#153)
Use status enum for certification state (#153)

Poem

In the garden of code where green leaves grow,
Admins now wield power, with filters in tow.
Approve or reject, with statuses clear,
The challenge certs march—no bugs to fear!
🐇✨
With a hop and a patch, this rabbit gives cheer!

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.


📜 Recent review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 55a7a7a and 732a136.

📒 Files selected for processing (14)
  • src/main/java/com/example/green/domain/challengecert/controller/AdminChallengeCertificationController.java (1 hunks)
  • src/main/java/com/example/green/domain/challengecert/dto/AdminPersonalCertificationSearchRequestDto.java (1 hunks)
  • src/main/java/com/example/green/domain/challengecert/dto/AdminTeamCertificationSearchRequestDto.java (1 hunks)
  • src/main/java/com/example/green/domain/challengecert/repository/PersonalChallengeCertificationRepositoryCustom.java (2 hunks)
  • src/main/java/com/example/green/domain/challengecert/repository/PersonalChallengeCertificationRepositoryImpl.java (2 hunks)
  • src/main/java/com/example/green/domain/challengecert/repository/TeamChallengeCertificationRepositoryCustom.java (2 hunks)
  • src/main/java/com/example/green/domain/challengecert/repository/TeamChallengeCertificationRepositoryImpl.java (2 hunks)
  • src/main/java/com/example/green/domain/challengecert/service/ChallengeCertificationService.java (5 hunks)
  • src/test/java/com/example/green/domain/challengecert/controller/ChallengeCertificationControllerTest.java (8 hunks)
  • src/test/java/com/example/green/domain/challengecert/entity/PersonalChallengeCertificationTest.java (4 hunks)
  • src/test/java/com/example/green/domain/challengecert/entity/TeamChallengeCertificationTest.java (2 hunks)
  • src/test/java/com/example/green/domain/challengecert/repository/PersonalChallengeCertificationRepositoryImplTest.java (4 hunks)
  • src/test/java/com/example/green/domain/challengecert/repository/TeamChallengeCertificationRepositoryImplTest.java (4 hunks)
  • src/test/java/com/example/green/domain/challengecert/service/ChallengeCertificationServiceTest.java (7 hunks)
🚧 Files skipped from review as they are similar to previous changes (7)
  • src/test/java/com/example/green/domain/challengecert/repository/PersonalChallengeCertificationRepositoryImplTest.java
  • src/main/java/com/example/green/domain/challengecert/repository/TeamChallengeCertificationRepositoryCustom.java
  • src/main/java/com/example/green/domain/challengecert/dto/AdminTeamCertificationSearchRequestDto.java
  • src/main/java/com/example/green/domain/challengecert/repository/PersonalChallengeCertificationRepositoryImpl.java
  • src/main/java/com/example/green/domain/challengecert/dto/AdminPersonalCertificationSearchRequestDto.java
  • src/main/java/com/example/green/domain/challengecert/controller/AdminChallengeCertificationController.java
  • src/main/java/com/example/green/domain/challengecert/repository/PersonalChallengeCertificationRepositoryCustom.java
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/main/java/com/example/green/domain/challengecert/repository/TeamChallengeCertificationRepositoryImpl.java (1)
src/main/java/com/example/green/domain/challengecert/repository/PersonalChallengeCertificationRepositoryImpl.java (1)
  • Repository (26-171)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: check
🔇 Additional comments (30)
src/test/java/com/example/green/domain/challengecert/entity/TeamChallengeCertificationTest.java (2)

19-19: LGTM: Import added for status enum refactor.

The import for CertificationStatus is correctly added to support the domain model refactoring from boolean approval to status-based certification handling.


108-108: LGTM: Test assertion updated for status-based certification.

The test assertion correctly verifies that newly created certifications have a PENDING status, which aligns with the domain model refactoring from boolean approval flags to the CertificationStatus enum.

src/test/java/com/example/green/domain/challengecert/repository/TeamChallengeCertificationRepositoryImplTest.java (3)

24-24: LGTM: Import added for status enum refactor.

The import for CertificationStatus correctly supports the domain model refactoring to status-based certification handling.


105-113: LGTM: Test assertions updated for new DTO structure.

The test assertions are correctly updated to use the new DTO field names:

  • certificationId()id()
  • Added member-centric fields (memberId(), memberNickname())
  • status() field with CertificationStatus.PENDING

This aligns well with the refactored DTO structure that focuses on member information rather than challenge details.


133-133: LGTM: Consistent DTO field usage throughout tests.

The test assertions consistently use the new DTO field structure (id(), memberNickname()) across all test methods, ensuring comprehensive coverage of the refactored DTO.

Also applies to: 178-178

src/test/java/com/example/green/domain/challengecert/entity/PersonalChallengeCertificationTest.java (5)

18-18: LGTM: Import added for status enum refactor.

The import for CertificationStatus correctly supports the domain model refactoring to status-based certification handling.


97-97: LGTM: Test assertion updated for status-based certification.

The test assertion correctly verifies that newly created certifications have a PENDING status, aligning with the domain model refactoring from boolean approval flags to the CertificationStatus enum.


188-195: LGTM: Approval logic simplified and status checks updated.

The approval method call is correctly simplified (parameterless), and the assertions properly verify both the legacy isApproved() method and the new CertificationStatus.PAID status. This demonstrates good backward compatibility during the refactoring.


223-231: LGTM: New rejection handling test added.

The test correctly verifies that rejected certifications cannot be approved again, demonstrating proper status transition validation with the new reject() method and updated exception handling.


219-219: LGTM: Exception messages updated for better semantics.

The exception message is correctly updated to CERTIFICATION_ALREADY_PROCESSED, which provides clearer semantics than the previous approval-specific messaging, covering both approval and rejection scenarios.

Also applies to: 230-230, 244-244

src/test/java/com/example/green/domain/challengecert/service/ChallengeCertificationServiceTest.java (4)

37-37: LGTM: Import added for status enum refactor.

The import for CertificationStatus correctly supports the domain model refactoring to status-based certification handling.


394-403: LGTM: DTO builder updated for new structure.

The DTO builder is correctly updated to use the new field structure:

  • id() instead of certificationId()
  • Added member-centric fields (memberId(), memberNickname(), memberEmail())
  • Added certification detail fields (certificationImageUrl(), certificationReview())
  • status() field with CertificationStatus.PENDING

This aligns well with the refactored DTO that focuses on member information and status-based handling.


417-421: LGTM: Consistent DTO usage across test methods.

The test data builders and assertions consistently use the new DTO field structure across all test methods, ensuring comprehensive coverage of the refactored DTOs with proper status enum values (PENDING, PAID).

Also applies to: 453-457


471-489: LGTM: Mock setups and assertions updated for new DTO fields.

The mock setups and test assertions are correctly updated to use the new DTO field names (id(), memberId(), memberNickname(), status()), ensuring the tests properly validate the refactored service layer behavior.

Also applies to: 505-523

src/test/java/com/example/green/domain/challengecert/controller/ChallengeCertificationControllerTest.java (5)

10-10: LGTM: Imports added for enhanced DTO support.

The imports for LocalDateTime and CertificationStatus correctly support the enhanced DTO structure with temporal fields and status-based certification handling.

Also applies to: 29-29


369-387: LGTM: Test data builders updated for new DTO structure.

The DTO builders are correctly updated to use the new field structure:

  • id() instead of certificationId()
  • Member-centric fields (memberId(), memberNickname(), memberEmail())
  • Certification detail fields (certificationImageUrl(), certificationReview())
  • status() field with CertificationStatus enum values

This properly reflects the refactored API response structure.


404-408: LGTM: JSON path assertions updated for new API response.

The JSON path assertions correctly validate the new API response structure using the updated field names (id, memberNickname, status) and properly check enum string values in the JSON response.

Also applies to: 446-448


457-467: LGTM: Detail response constructor updated with comprehensive fields.

The detail response constructor is correctly updated to include all the new fields:

  • Member information (memberId, memberNickname, memberEmail)
  • Temporal fields (certifiedAt as LocalDateTime, certifiedDate)
  • Status field with CertificationStatus.PAID

This provides comprehensive detail information in the API response.


479-483: LGTM: Detail response JSON assertions updated.

The JSON path assertions for the detail response correctly validate the new field structure (id, memberNickname, status) and ensure the controller properly serializes the enhanced DTO.

src/main/java/com/example/green/domain/challengecert/repository/TeamChallengeCertificationRepositoryImpl.java (5)

4-4: LGTM! Necessary imports for new admin functionality.

The added imports are required for the new repository methods that support admin features - QueryDSL entities, DTOs, enums, and Projections for DTO mapping.

Also applies to: 6-6, 8-8, 15-16, 19-19, 22-22


92-94: Good refactoring for consistency.

The change to use a static factory method aligns with the pattern used in PersonalChallengeCertificationRepositoryImpl and improves separation of concerns.


96-107: LGTM! Clean implementation for retrieving group codes.

The method efficiently uses QueryDSL projections to map directly to DTOs, avoiding unnecessary entity loading.


109-149: LGTM! Well-structured cursor-based pagination with filtering.

The implementation correctly follows the established pattern from PersonalChallengeCertificationRepositoryImpl, with proper join fetching to avoid N+1 queries and efficient cursor-based pagination.


151-172: LGTM! Well-designed filter conditions with proper null handling.

The helper methods follow best practices:

  • Proper null/empty checks before creating expressions
  • String trimming for user input in groupCodeCondition
  • Consistent pattern with PersonalChallengeCertificationRepositoryImpl
src/main/java/com/example/green/domain/challengecert/service/ChallengeCertificationService.java (6)

17-21: LGTM! Appropriate imports and page size constant for admin features.

The imports support the new admin functionality, and the reduced page size (10) for admin operations is reasonable for detailed administrative views.

Also applies to: 52-52


138-140: LGTM! Consistent use of static factory methods.

The changes align with the DTO refactoring pattern used throughout the codebase, using specific factory methods for personal and team certifications.

Also applies to: 146-146


253-258: LGTM! Simple and effective title retrieval methods.

These methods provide clean admin endpoints for retrieving challenge titles. The use of findAll() is acceptable for admin dropdown populations where the dataset is typically manageable.

Also applies to: 263-268


273-277: LGTM! Proper validation before delegating to repository.

Both methods correctly validate challenge existence before retrieving participant/group data, preventing unnecessary database queries for non-existent challenges.

Also applies to: 282-286


291-300: LGTM! Well-structured filter methods with appropriate validation.

Both methods properly check challenge existence only when a specific challenge is selected (not "all challenges"), avoiding unnecessary validation for broad queries.

Also applies to: 305-314


375-386: LGTM! Clean helper methods for challenge retrieval.

These private methods provide a consistent way to retrieve and validate challenge existence with appropriate error handling.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/#153-admin-challenge-certifications

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
🪧 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.
    • Explain this complex logic.
    • 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 explain this code block.
    • @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 explain its main purpose.
    • @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.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

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.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @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
Contributor

@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: 3

🧹 Nitpick comments (12)
src/main/java/com/example/green/domain/challengecert/enums/CertificationStatus.java (1)

6-8: Consider more generic status naming for broader applicability.

The enum structure is well-designed, but PAID might be too specific for a general certification status. Consider APPROVED instead to make it more universally applicable across different certification contexts.

Also, note that the test file expects a REQUESTED status (line 111 in the test), but this enum only defines PENDING, PAID, and REJECTED.

 public enum CertificationStatus {
-	PENDING, PAID, REJECTED
+	PENDING, APPROVED, REJECTED
src/main/java/com/example/green/domain/challengecert/dto/AdminParticipantMemberKeyResponseDto.java (3)

6-13: Consider adding API documentation and simplifying the factory method.

The record structure is well-defined and appropriate. However, consider these improvements:

  1. Add Swagger annotations for API documentation consistency with other DTOs
  2. The static factory method of is redundant since records provide constructors automatically

Apply this diff to add API documentation:

+import io.swagger.v3.oas.annotations.media.Schema;
+
 /**
  * 관리자 챌린지 참여자 memberKey 응답 DTO
  */
+@Schema(description = "관리자 챌린지 참여자 memberKey 응답")
 public record AdminParticipantMemberKeyResponseDto(
+	@Schema(description = "회원 키", example = "google_3421")
 	String memberKey,
+	@Schema(description = "닉네임", example = "사용자닉네임")
 	String nickname
 ) {
-	public static AdminParticipantMemberKeyResponseDto of(String memberKey, String nickname) {
-		return new AdminParticipantMemberKeyResponseDto(memberKey, nickname);
-	}
 }

Alternatively, if you prefer to keep the factory method for consistency with other DTOs, the current implementation is acceptable.


10-12: Remove redundant factory method.

The static factory method of is unnecessary since records automatically provide a constructor with the same signature. This adds unnecessary code without any additional value.

-	public static AdminParticipantMemberKeyResponseDto of(String memberKey, String nickname) {
-		return new AdminParticipantMemberKeyResponseDto(memberKey, nickname);
-	}

6-9: Add field documentation and validation.

Consider adding Swagger annotations and validation to improve API documentation and ensure data integrity.

 public record AdminParticipantMemberKeyResponseDto(
+	@Schema(description = "참여자 회원 키", example = "google_3421")
+	@NotBlank
 	String memberKey,
+	@Schema(description = "참여자 닉네임", example = "홍길동")
+	@NotBlank
 	String nickname
 ) {
src/main/java/com/example/green/domain/challengecert/dto/AdminChallengeTitleResponseDto.java (2)

9-21: Add API documentation for consistency with admin endpoints.

The record structure and factory method are well-implemented and follow established patterns in the codebase. However, consider adding Swagger annotations for API documentation consistency.

Apply this diff to add API documentation:

+import io.swagger.v3.oas.annotations.media.Schema;
+
 /**
  * 관리자 챌린지 제목 응답 DTO  
  */
+@Schema(description = "관리자 챌린지 제목 응답")
 public record AdminChallengeTitleResponseDto(
+	@Schema(description = "챌린지 ID", example = "1")
 	Long challengeId,
+	@Schema(description = "챌린지 명", example = "7일 운동 챌린지")
 	String challengeName,
+	@Schema(description = "챌린지 타입")
 	ChallengeType challengeType
 ) {

9-13: Add Swagger documentation for better API specs.

Consider adding field documentation to improve the generated API documentation.

 public record AdminChallengeTitleResponseDto(
+	@Schema(description = "챌린지 ID", example = "1")
 	Long challengeId,
+	@Schema(description = "챌린지 이름", example = "환경보호 챌린지")
 	String challengeName,
+	@Schema(description = "챌린지 타입")
 	ChallengeType challengeType
 ) {
src/main/java/com/example/green/domain/challengecert/repository/PersonalChallengeCertificationRepositoryImpl.java (1)

113-127: Consider fetch join performance implications

Multiple fetch joins can lead to Cartesian product issues. Review if all fetch joins are necessary, particularly the member fetch join on line 117.

If member data is already loaded through the participation relationship, you might optimize by removing redundant fetch joins:

 		List<PersonalChallengeCertification> certifications = queryFactory
 			.selectFrom(personalChallengeCertification)
 			.join(personalChallengeCertification.participation, personalChallengeParticipation).fetchJoin()
 			.join(personalChallengeParticipation.personalChallenge, personalChallenge).fetchJoin()
-			.join(personalChallengeParticipation.member, member).fetchJoin()
+			.join(personalChallengeParticipation.member, member)
src/main/java/com/example/green/domain/challengecert/dto/ChallengeCertificationListResponseDto.java (1)

40-74: Consider reducing code duplication between factory methods

Both factory methods have identical implementation logic. Consider extracting a common method that accepts the base type to eliminate duplication.

You could reduce duplication by leveraging the common base class:

+	private static ChallengeCertificationListResponseDto fromBaseCertification(
+		BaseChallengeCertification certification) {
+		return new ChallengeCertificationListResponseDto(
+			certification.getId(),
+			certification.getMember().getId(),
+			certification.getMember().getProfile().getNickname(),
+			certification.getMember().getEmail(),
+			certification.getCertificationImageUrl(),
+			certification.getCertificationReview(),
+			certification.getCertifiedDate(),
+			certification.getStatus()
+		);
+	}
+
 	public static ChallengeCertificationListResponseDto fromPersonalChallengeCertification(
 		PersonalChallengeCertification certification) {
-
-		return new ChallengeCertificationListResponseDto(
-			certification.getId(),
-			certification.getMember().getId(),
-			certification.getMember().getProfile().getNickname(),
-			certification.getMember().getEmail(),
-			certification.getCertificationImageUrl(),
-			certification.getCertificationReview(),
-			certification.getCertifiedDate(),
-			certification.getStatus()
-		);
+		return fromBaseCertification(certification);
 	}

 	public static ChallengeCertificationListResponseDto fromTeamChallengeCertification(
 		TeamChallengeCertification certification) {
-
-		return new ChallengeCertificationListResponseDto(
-			certification.getId(),
-			certification.getMember().getId(),
-			certification.getMember().getProfile().getNickname(),
-			certification.getMember().getEmail(),
-			certification.getCertificationImageUrl(),
-			certification.getCertificationReview(),
-			certification.getCertifiedDate(),
-			certification.getStatus()
-		);
+		return fromBaseCertification(certification);
 	}
src/main/java/com/example/green/domain/challengecert/entity/BaseChallengeCertification.java (2)

61-61: Remove redundant status initialization

The status field is initialized twice - once at declaration (line 61) and again in the constructor (line 76). Since the field is already initialized at declaration, the constructor assignment is redundant.

 	protected BaseChallengeCertification(
 		Member member,
 		String certificationImageUrl,
 		String certificationReview,
 		LocalDateTime certifiedAt,
 		LocalDate certifiedDate
 	) {
 		this.member = member;
 		this.certificationImageUrl = certificationImageUrl;
 		this.certificationReview = certificationReview;
 		this.certifiedAt = certifiedAt;
 		this.certifiedDate = certifiedDate;
-		this.status = CertificationStatus.PENDING;
 	}

Also applies to: 76-76


115-124: Consider consolidating permission check methods

The canApprove() and canReject() methods have identical implementations. If the business logic is truly the same for both operations, consider consolidating them or documenting why they're separate.

If approval and rejection will always have the same preconditions:

+	/**
+	 * 상태 변경 가능 여부 확인
+	 */
+	public boolean canChangeStatus() {
+		return this.status == CertificationStatus.PENDING;
+	}
+
 	/**
 	 * 승인 가능 여부 확인
 	 */
 	public boolean canApprove() {
-		return this.status == CertificationStatus.PENDING;
+		return canChangeStatus();
 	}

 	/**
 	 * 거절 가능 여부 확인
 	 */
 	public boolean canReject() {
-		return this.status == CertificationStatus.PENDING;
+		return canChangeStatus();
 	}

This makes it clear that both operations have the same precondition and simplifies future maintenance if the logic needs to change.

src/main/java/com/example/green/domain/challengecert/service/ChallengeCertificationService.java (1)

315-336: Consider improving the certification lookup logic

The current implementation searches both repositories sequentially. Consider using a more efficient approach or at least documenting why both lookups are necessary.

You could use a more functional approach:

 @Transactional
 public void updateCertificationStatus(Long certificationId, String status) {
-    // 먼저 개인 챌린지 인증에서 찾아보기
-    PersonalChallengeCertification personalCertification = personalChallengeCertificationRepository
-        .findById(certificationId)
-        .orElse(null);
-
-    if (personalCertification != null) {
-        updateCertificationStatusInternal(personalCertification, status);
-        return;
-    }
-
-    // 팀 챌린지 인증에서 찾아보기
-    TeamChallengeCertification teamCertification = teamChallengeCertificationRepository
-        .findById(certificationId)
-        .orElseThrow(() -> new ChallengeCertException(ChallengeCertExceptionMessage.CERTIFICATION_NOT_FOUND));
-
-    updateCertificationStatusInternal(teamCertification, status);
+    var certification = personalChallengeCertificationRepository.findById(certificationId)
+        .<BaseChallengeCertification>map(cert -> cert)
+        .or(() -> teamChallengeCertificationRepository.findById(certificationId)
+            .map(cert -> cert))
+        .orElseThrow(() -> new ChallengeCertException(ChallengeCertExceptionMessage.CERTIFICATION_NOT_FOUND));
+    
+    updateCertificationStatusInternal(certification, status);
 }
src/main/java/com/example/green/domain/challengecert/dto/AdminTeamCertificationSearchRequestDto.java (1)

14-28: Consider adding validation annotations.

For better data integrity, consider adding validation annotations, especially for cursor and challengeId fields.

 @Schema(description = "챌린지 ID (선택사항, null이면 전체 조회)", example = "1")
+@Positive
 Long challengeId,

 @Schema(description = "그룹 코드 (선택사항, null이면 전체 그룹)", example = "T-20250109-143523-C8NQ")
 String groupCode,

 @Schema(description = "인증 상태 리스트 (선택사항, null이면 전체 상태)",
 	example = "[\"PENDING\", \"PAID\", \"REJECTED\"]")
+@Valid
 List<CertificationStatus> statuses,

 @Schema(description = "커서 (페이징용, 마지막 인증 ID)")
+@Positive
 Long cursor,
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between fd3d689 and 55a7a7a.

📒 Files selected for processing (20)
  • src/main/java/com/example/green/domain/challengecert/controller/AdminChallengeCertificationController.java (1 hunks)
  • src/main/java/com/example/green/domain/challengecert/controller/docs/AdminChallengeCertificationControllerDocs.java (1 hunks)
  • src/main/java/com/example/green/domain/challengecert/controller/message/ChallengeCertificationResponseMessage.java (1 hunks)
  • src/main/java/com/example/green/domain/challengecert/dto/AdminCertificationStatusUpdateRequestDto.java (1 hunks)
  • src/main/java/com/example/green/domain/challengecert/dto/AdminChallengeTitleResponseDto.java (1 hunks)
  • src/main/java/com/example/green/domain/challengecert/dto/AdminGroupCodeResponseDto.java (1 hunks)
  • src/main/java/com/example/green/domain/challengecert/dto/AdminParticipantMemberKeyResponseDto.java (1 hunks)
  • src/main/java/com/example/green/domain/challengecert/dto/AdminPersonalCertificationSearchRequestDto.java (1 hunks)
  • src/main/java/com/example/green/domain/challengecert/dto/AdminTeamCertificationSearchRequestDto.java (1 hunks)
  • src/main/java/com/example/green/domain/challengecert/dto/ChallengeCertificationDetailResponseDto.java (1 hunks)
  • src/main/java/com/example/green/domain/challengecert/dto/ChallengeCertificationListResponseDto.java (2 hunks)
  • src/main/java/com/example/green/domain/challengecert/entity/BaseChallengeCertification.java (4 hunks)
  • src/main/java/com/example/green/domain/challengecert/enums/CertificationStatus.java (1 hunks)
  • src/main/java/com/example/green/domain/challengecert/exception/ChallengeCertExceptionMessage.java (1 hunks)
  • src/main/java/com/example/green/domain/challengecert/repository/PersonalChallengeCertificationRepositoryCustom.java (2 hunks)
  • src/main/java/com/example/green/domain/challengecert/repository/PersonalChallengeCertificationRepositoryImpl.java (2 hunks)
  • src/main/java/com/example/green/domain/challengecert/repository/TeamChallengeCertificationRepositoryCustom.java (2 hunks)
  • src/main/java/com/example/green/domain/challengecert/repository/TeamChallengeCertificationRepositoryImpl.java (2 hunks)
  • src/main/java/com/example/green/domain/challengecert/service/ChallengeCertificationService.java (4 hunks)
  • src/test/java/com/example/green/domain/challengecert/repository/PersonalChallengeCertificationRepositoryImplTest.java (2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (5)
src/main/java/com/example/green/domain/challengecert/dto/AdminParticipantMemberKeyResponseDto.java (1)
src/main/java/com/example/green/domain/challenge/controller/dto/admin/AdminChallengeParticipantListResponseDto.java (1)
  • AdminChallengeParticipantListResponseDto (9-39)
src/main/java/com/example/green/domain/challengecert/entity/BaseChallengeCertification.java (1)
src/main/java/com/example/green/domain/challengecert/exception/ChallengeCertException.java (1)
  • ChallengeCertException (6-11)
src/main/java/com/example/green/domain/challengecert/dto/AdminChallengeTitleResponseDto.java (1)
src/main/java/com/example/green/domain/challenge/controller/dto/admin/AdminChallengeDetailResponseDto.java (1)
  • AdminChallengeDetailResponseDto (13-91)
src/main/java/com/example/green/domain/challengecert/repository/TeamChallengeCertificationRepositoryImpl.java (1)
src/main/java/com/example/green/domain/challengecert/repository/PersonalChallengeCertificationRepositoryImpl.java (1)
  • Repository (26-171)
src/main/java/com/example/green/domain/challengecert/repository/PersonalChallengeCertificationRepositoryImpl.java (1)
src/main/java/com/example/green/domain/challengecert/repository/TeamChallengeCertificationRepositoryImpl.java (1)
  • Repository (28-173)
🪛 GitHub Actions: PR Check
src/test/java/com/example/green/domain/challengecert/repository/PersonalChallengeCertificationRepositoryImplTest.java

[error] 104-177: Compilation error: cannot find symbol methods certificationId(), challengeId(), challengeTitle(), status() with variable REQUESTED in CertificationStatus in ChallengeCertificationListResponseDto.

🔇 Additional comments (26)
src/main/java/com/example/green/domain/challengecert/dto/AdminCertificationStatusUpdateRequestDto.java (1)

10-14: LGTM! Well-designed DTO with proper validation.

The record structure is clean and appropriate for this use case. The @NotNull validation ensures data integrity, and the documentation clearly explains the purpose.

src/main/java/com/example/green/domain/challengecert/exception/ChallengeCertExceptionMessage.java (2)

13-13: Good refactoring to support the new status system.

Renaming from CERTIFICATION_ALREADY_APPROVED to CERTIFICATION_ALREADY_PROCESSED is more appropriate with the new enum-based status system where certifications can be either approved (PAID) or rejected.


18-18: Well-added validation support for the new status system.

The new INVALID_CERTIFICATION_STATUS exception message provides proper validation feedback for the admin status update functionality.

src/main/java/com/example/green/domain/challengecert/dto/AdminGroupCodeResponseDto.java (1)

6-14: LGTM! Clean and well-structured DTO.

The record design is appropriate, and the static factory method of() provides a convenient way to create instances. The field names are clear and self-documenting.

src/main/java/com/example/green/domain/challengecert/controller/message/ChallengeCertificationResponseMessage.java (2)

14-23: LGTM! Well-organized admin response messages.

The new admin-specific response message constants are well-structured with:

  • Consistent ADMIN_ prefix naming pattern
  • Clear, descriptive Korean messages
  • Good organization with comment separation
  • Complete coverage of admin operations

The implementation follows established patterns and aligns perfectly with the new admin API functionality.


16-23: LGTM! Comprehensive admin response messages.

The new admin response messages are well-organized, descriptive, and cover all the administrative functionality mentioned in the PR objectives. The separation with a comment is a good organizational practice.

src/main/java/com/example/green/domain/challengecert/dto/AdminTeamCertificationSearchRequestDto.java (3)

13-55: LGTM! Well-designed search request DTO with comprehensive filtering.

The record is excellently structured with:

  • Appropriate filtering fields: challengeId, groupCode, statuses for comprehensive search criteria
  • Smart size handling: Compact constructor enforcing backend-controlled page size of 10
  • Comprehensive helper methods: Clear boolean checks for filtering logic with proper null/empty handling
  • Excellent documentation: Well-annotated Swagger schemas with meaningful examples

The isAllGroups() method properly handles both null and whitespace-only strings, which is a thoughtful implementation detail.


30-33: LGTM! Good use of compact constructor.

Using the compact constructor to enforce a fixed page size is a clean approach that prevents clients from overriding the backend's pagination constraints.


35-54: LGTM! Well-designed helper methods.

The helper methods provide clear, readable ways to check filter conditions. The isAllGroups() method appropriately handles both null and empty string cases.

src/main/java/com/example/green/domain/challengecert/repository/TeamChallengeCertificationRepositoryCustom.java (3)

40-54: LGTM! Well-designed repository interface extension for admin operations.

The new repository methods are excellently designed with:

  • Clear method names: Explicitly indicate admin functionality and purpose
  • Appropriate signatures: Proper use of admin DTOs and return types
  • Comprehensive documentation: Detailed JavaDoc following established patterns
  • Consistent design: Aligns with existing repository method patterns

Both methods properly support the admin API requirements for group code retrieval and filtered team certification queries with cursor-based pagination.


40-45: LGTM! Well-documented admin method.

The method signature and documentation clearly describe the purpose and usage for retrieving group codes for admin dropdowns.


47-54: LGTM! Comprehensive filtering method design.

The method signature appropriately uses the search DTO for complex filtering criteria and maintains consistency with cursor-based pagination patterns used elsewhere in the codebase.

src/main/java/com/example/green/domain/challengecert/repository/PersonalChallengeCertificationRepositoryCustom.java (1)

41-54: Well-designed admin query methods

The new repository methods follow good design patterns with clear responsibilities:

  • findParticipantMemberKeysByChallengeId provides efficient participant lookup
  • findPersonalCertificationsWithFilters supports complex filtering with pagination

The JavaDoc documentation clearly describes the purpose and parameters.

src/main/java/com/example/green/domain/challengecert/dto/AdminPersonalCertificationSearchRequestDto.java (1)

45-47: Consistent trimming in helper method

The isAllMembers() method trims the memberKey for the empty check, which is good. Ensure this matches the actual filtering logic in the repository implementation.

src/main/java/com/example/green/domain/challengecert/dto/ChallengeCertificationDetailResponseDto.java (2)

10-20: Clean refactoring to Java record

Excellent refactoring from a builder-based class to a Java record. The shift from challenge-centric to member-centric fields aligns well with administrative needs, and the CertificationStatus enum provides better type safety than the previous boolean field.


22-52: Null safety confirmed for Member.profile

The profile field is an @Embedded value object with a protected no-args constructor and is always initialized in both cases:

  • When creating a new Member (via Member.create), the constructor sets this.profile using the builder.
  • When loading from the database, JPA instantiates the embeddable via its no-args constructor before populating its fields.

Therefore calls to member.getProfile().getNickname() cannot result in a null-pointer exception. No changes needed here.

src/main/java/com/example/green/domain/challengecert/repository/PersonalChallengeCertificationRepositoryImpl.java (2)

95-107: Efficient participant lookup query

Good use of QueryDSL projections for efficient DTO mapping. The distinct() ensures unique participants are returned.


149-170: Well-structured query condition builders

The helper methods follow QueryDSL best practices:

  • Returning null for empty conditions (ignored by QueryDSL)
  • Proper string trimming for memberKey
  • Clear separation of concerns
src/main/java/com/example/green/domain/challengecert/repository/TeamChallengeCertificationRepositoryImpl.java (3)

97-107: Clean group code query implementation

Good use of projections to efficiently map team challenge group data to DTOs.


151-172: Consistent condition builders across repositories

The condition helper methods maintain consistency with the personal challenge repository implementation, which is excellent for maintainability.


113-128: Performance risk: deep fetch-join in findTeamCertificationsWithFilters

This single query traverses six joins (five with fetchJoin), which can easily blow up into Cartesian products and slow down large datasets. I didn’t find any existing batch-fetch, query-hint, or entity-graph configurations in the codebase, so consider one of the following optimizations:

• Introduce a @BatchSize or XML <batch-size> on the related associations.
• Apply @EntityGraph on the repository method to load only the needed paths.
• Use @QueryHints({ @QueryHint(name = "org.hibernate.fetchSize", value = "…") }) to tune fetch sizes.
• Break this into two simpler queries—first load certification IDs by pagination, then batch-fetch the associations.
• Switch to a projection (DTO) query if only read-only data is needed.

Please verify which approach best fits your use case and add the corresponding configuration.

src/main/java/com/example/green/domain/challengecert/dto/ChallengeCertificationListResponseDto.java (1)

12-38: Well-structured DTO with improved type safety

The refactoring from a Boolean approved field to the CertificationStatus enum improves type safety and provides more expressive status representations. The addition of member information fields enhances the DTO's utility for administrative views.

src/main/java/com/example/green/domain/challengecert/entity/BaseChallengeCertification.java (1)

82-97: Well-implemented status transition methods

The approve() and reject() methods properly enforce state transitions by only allowing changes from PENDING status. The use of a generic CERTIFICATION_ALREADY_PROCESSED exception for both cases is appropriate.

src/main/java/com/example/green/domain/challengecert/controller/docs/AdminChallengeCertificationControllerDocs.java (1)

23-200: Excellent API documentation

The documentation is comprehensive and well-structured, with:

  • Clear operation descriptions and usage flows
  • Detailed parameter explanations with examples
  • Helpful cross-references between related endpoints
  • Well-explained pagination patterns

This level of documentation will significantly improve the developer experience for API consumers.

src/main/java/com/example/green/domain/challengecert/controller/AdminChallengeCertificationController.java (1)

106-113: Enum-to-String Conversion Verified

The CertificationStatus.name() values (PENDING, PAID, REJECTED) exactly match the case labels in ChallengeCertificationService.updateCertificationStatusInternal, so using .name() here is safe and requires no changes.

src/main/java/com/example/green/domain/challengecert/dto/AdminChallengeTitleResponseDto.java (1)

14-20: LGTM! Good use of factory method.

The from factory method provides semantic clarity for entity-to-DTO conversion and is appropriate here, unlike simple parameter passing.

Comment on lines 341 to 359
private void updateCertificationStatusInternal(Object certification, String status) {
switch (status.toUpperCase()) {
case "PAID" -> {
if (certification instanceof PersonalChallengeCertification personal) {
personal.approve();
} else if (certification instanceof TeamChallengeCertification team) {
team.approve();
}
}
case "REJECTED" -> {
if (certification instanceof PersonalChallengeCertification personal) {
personal.reject();
} else if (certification instanceof TeamChallengeCertification team) {
team.reject();
}
}
default -> throw new ChallengeCertException(ChallengeCertExceptionMessage.INVALID_CERTIFICATION_STATUS);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

⚠️ Potential issue

Improve type safety in status update logic

Using Object type and repeated instanceof checks reduces type safety and creates code duplication.

Since both PersonalChallengeCertification and TeamChallengeCertification extend BaseChallengeCertification which has the approve() and reject() methods, use the base type:

-private void updateCertificationStatusInternal(Object certification, String status) {
+private void updateCertificationStatusInternal(BaseChallengeCertification certification, String status) {
     switch (status.toUpperCase()) {
-        case "PAID" -> {
-            if (certification instanceof PersonalChallengeCertification personal) {
-                personal.approve();
-            } else if (certification instanceof TeamChallengeCertification team) {
-                team.approve();
-            }
-        }
-        case "REJECTED" -> {
-            if (certification instanceof PersonalChallengeCertification personal) {
-                personal.reject();
-            } else if (certification instanceof TeamChallengeCertification team) {
-                team.reject();
-            }
-        }
+        case "PAID" -> certification.approve();
+        case "REJECTED" -> certification.reject();
         default -> throw new ChallengeCertException(ChallengeCertExceptionMessage.INVALID_CERTIFICATION_STATUS);
     }
 }

This eliminates the type checks and code duplication while maintaining the same functionality.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private void updateCertificationStatusInternal(Object certification, String status) {
switch (status.toUpperCase()) {
case "PAID" -> {
if (certification instanceof PersonalChallengeCertification personal) {
personal.approve();
} else if (certification instanceof TeamChallengeCertification team) {
team.approve();
}
}
case "REJECTED" -> {
if (certification instanceof PersonalChallengeCertification personal) {
personal.reject();
} else if (certification instanceof TeamChallengeCertification team) {
team.reject();
}
}
default -> throw new ChallengeCertException(ChallengeCertExceptionMessage.INVALID_CERTIFICATION_STATUS);
}
}
private void updateCertificationStatusInternal(BaseChallengeCertification certification, String status) {
switch (status.toUpperCase()) {
case "PAID" -> certification.approve();
case "REJECTED" -> certification.reject();
default -> throw new ChallengeCertException(ChallengeCertExceptionMessage.INVALID_CERTIFICATION_STATUS);
}
}
🤖 Prompt for AI Agents
In
src/main/java/com/example/green/domain/challengecert/service/ChallengeCertificationService.java
around lines 341 to 359, the method updateCertificationStatusInternal uses
Object type and multiple instanceof checks causing code duplication and reduced
type safety. Change the method parameter type from Object to
BaseChallengeCertification, then call approve() or reject() directly on this
base type without any instanceof checks. This simplifies the code, removes
duplication, and leverages polymorphism since both
PersonalChallengeCertification and TeamChallengeCertification inherit from
BaseChallengeCertification.

HJunng added 3 commits July 29, 2025 20:52
- 페이지 크기 필드 제거 및 백엔드 고정값 명시
- DTO 주석에 페이지 크기 고정 여부 설명 추가
- `pageSize` 파라미터 도입으로 인증 검색 메서드 리팩토링
- 기존 Boolean 필드를 CertificationStatus enum으로 대체
- 테스트 DTO와 메서드의 필드 네이밍 변경 및 상태 기반 검증 로직 추가
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dev:feature 기능 개발 dev:fix 기능 수정 dev:test 테스트

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[기능추가] 챌린지 인증 기능 구현

1 participant