Skip to content

feature/KD-73: 자격증 신청 관리 메뉴와 졸업 논문 관리 메뉴에서 승인된 사용자 승인 취소 기능 추가#330

Merged
hoTan35 merged 3 commits intodevelopfrom
feature/KD-73
Mar 27, 2026
Merged

feature/KD-73: 자격증 신청 관리 메뉴와 졸업 논문 관리 메뉴에서 승인된 사용자 승인 취소 기능 추가#330
hoTan35 merged 3 commits intodevelopfrom
feature/KD-73

Conversation

@hoTan35
Copy link
Copy Markdown
Contributor

@hoTan35 hoTan35 commented Mar 25, 2026

Summary

자격증 신청 관리 메뉴와 졸업 논문 관리 메뉴에서 승인된 사용자 승인 취소 기능을 추가했습니다.

Tasks

  • Certificate, Thesis 도메인 엔티티 내 승인 취소(disapprove) 메서드 추가
  • CertificateCommandService, ThesisCommandService 내 승인 취소 비즈니스 로직 구현
  • GraduationUserBatchDisapproveRequest/Response DTO 생성 및 Swagger 스키마 정의
  • GraduationUserAdminFacade 내 일괄 승인 취소 비즈니스 로직 추가
  • GraduationUserAdminController 내 일괄 승인 취소 API(/batch/disapprove)구현
  • GraduationUserAdminFacadeTest 내 일괄 승인 취소 기능 검증 테스트 코드 추가

@hoTan35 hoTan35 self-assigned this Mar 25, 2026
@hoTan35 hoTan35 added the ✨feature create new feature label Mar 25, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 25, 2026

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: c0dfefd3-f198-45da-8467-36a83321d82d

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

졸업 사용자의 승인을 취소하는 배치 작업 기능을 추가합니다. 컨트롤러 계층, 파사드, 도메인 모델에 disapprove 메서드를 도입하여 인증서와 논문의 승인 상태를 false로 설정하는 역방향 워크플로우를 구현합니다.

Changes

Cohort / File(s) 요약
프레젠테이션 계층 - 파사드 및 컨트롤러
aics-admin/src/main/java/kgu/developers/admin/graduationUser/application/GraduationUserAdminFacade.java, aics-admin/src/main/java/kgu/developers/admin/graduationUser/presentation/GraduationUserAdminController.java, aics-admin/src/main/java/kgu/developers/admin/graduationUser/presentation/GraduationUserAdminControllerImpl.java
배치 승인 취소 엔드포인트 추가. disapproveGraduationUsers 메서드는 인증서 및 논문의 승인 취소 를 조율하고, 컨트롤러에서 PATCH /batch/disapprove 로 매핑됨.
요청/응답 DTO
aics-admin/src/main/java/kgu/developers/admin/graduationUser/presentation/request/GraduationUserBatchDisapproveRequest.java, aics-admin/src/main/java/kgu/developers/admin/graduationUser/presentation/response/GraduationUserBatchDisapproveResponse.java
배치 승인 취소 작업의 요청/응답 구조. 요청은 검증된 양수 ID 목록을 수용하고, 응답은 취소된 사용자 ID 목록을 반환함.
인증서 도메인
aics-domain/src/main/java/kgu/developers/domain/certificate/domain/Certificate.java, aics-domain/src/main/java/kgu/developers/domain/certificate/application/command/CertificateCommandService.java
disapprove() 메서드 추가로 승인 상태를 false로 설정. 커맨드 서비스는 인증서 조회, 상태 확인 후 취소 처리를 담당.
논문 도메인
aics-domain/src/main/java/kgu/developers/domain/thesis/domain/Thesis.java, aics-domain/src/main/java/kgu/developers/domain/thesis/application/command/ThesisCommandService.java
disapprove() 메서드 추가로 승인 상태를 false로 설정. 커맨드 서비스는 비승인 상태 확인 후 취소 처리를 수행.
테스트
aics-admin/src/testFixtures/java/graduationUser/application/GraduationUserAdminFacadeTest.java
disapproveGraduationUsers 메서드의 성공 케이스에 대한 테스트 추가. 인증서와 논문의 승인 플래그가 false로 변경되는지 검증.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 분

Possibly related PRs

  • 기존 배치 승인 기능 구현을 위한 역 작업으로, 같은 파사드, 컨트롤러, DTO 구조에서 승인 취소 로직을 반영합니다.
  • 논문과 인증서 커맨드 서비스에 disapprove 메서드를 추가하여 기존 approve 메서드와 대응되는 도메인 기능을 제공합니다.
  • 졸업 사용자 관리 API의 관리자 작업 표면을 확장하는 동일한 기능 영역의 변경사항입니다.

Suggested reviewers

  • JangYeongHu
  • dkdltm221
  • LeeHanEum
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목이 변경사항의 주요 내용을 명확하게 요약하고 있습니다. 자격증과 졸업 논문 관리에서 승인 취소 기능 추가라는 핵심 변경사항을 잘 표현하고 있습니다.
Description check ✅ Passed PR 설명이 변경 사항과 관련성이 있으며, 자격증 및 졸업 논문 관리에서 승인 취소 기능 추가라는 전체 목표와 일치합니다.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/KD-73

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 25, 2026

Test Coverage Report

Overall Project 79.62% -1.89%
Files changed 54.68%

Module Coverage
aics-admin 90.11% -1.39% 🍏
aics-domain 73.78% -2.49%
Files
Module File Coverage
aics-admin GraduationUserAdminFacade.java 84.54% -2.58% 🍏
aics-domain CertificateCommandService.java 0% -23.4%
ThesisCommandService.java 0% -21.15%
Certificate.java 0% -8.51%
Thesis.java 0% -8.51%

Copy link
Copy Markdown
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: 1

🧹 Nitpick comments (3)
aics-admin/src/testFixtures/java/graduationUser/application/GraduationUserAdminFacadeTest.java (1)

318-339: 테스트 구현이 적절합니다.

테스트가 기존 approveGraduationUsers_Success 패턴을 따르고 있습니다. 선택적으로 다음 개선을 고려해 볼 수 있습니다:

  1. assertEquals(false, ...) 대신 assertFalse(...) 사용
  2. disapproveGraduationUsers 반환값인 GraduationUserBatchDisapproveResponse에 대한 검증 추가
♻️ 선택적 개선 제안
         // then
-        assertEquals(false, fakeThesisRepository.findApprovalByIdAndDeletedAtIsNull(1L).get());
-        assertEquals(false, fakeThesisRepository.findApprovalByIdAndDeletedAtIsNull(2L).get());
-        assertEquals(false, fakeCertificateRepository.findApprovalByIdAndDeletedAtIsNull(1L).get());
+        GraduationUserBatchDisapproveResponse response = graduationUserAdminFacade.disapproveGraduationUsers(request);
+
+        assertNotNull(response);
+        assertFalse(fakeThesisRepository.findApprovalByIdAndDeletedAtIsNull(1L).get());
+        assertFalse(fakeThesisRepository.findApprovalByIdAndDeletedAtIsNull(2L).get());
+        assertFalse(fakeCertificateRepository.findApprovalByIdAndDeletedAtIsNull(1L).get());
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@aics-admin/src/testFixtures/java/graduationUser/application/GraduationUserAdminFacadeTest.java`
around lines 318 - 339, Replace the boolean equality assertions with assertFalse
for readability and add assertions to verify the returned
GraduationUserBatchDisapproveResponse from
graduationUserAdminFacade.disapproveGraduationUsers(request) (e.g., check that
the response is not null and contains expected ids/count), while keeping the
existing repository checks using
fakeThesisRepository.findApprovalByIdAndDeletedAtIsNull(1L/2L) and
fakeCertificateRepository.findApprovalByIdAndDeletedAtIsNull(1L) to ensure
approvals were cleared; update the test method disapproveGraduationUsers_Success
accordingly to capture the response and assert its expected fields and use
assertFalse for the repository booleans.
aics-admin/src/main/java/kgu/developers/admin/graduationUser/application/GraduationUserAdminFacade.java (2)

203-223: approveGraduationUsers와 분기/루프 구조가 거의 동일해 이후 변경 시 드리프트 위험이 큽니다.

승인/승인취소 공통 루프를 private helper로 추출하면 로직 일관성 유지가 쉬워집니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@aics-admin/src/main/java/kgu/developers/admin/graduationUser/application/GraduationUserAdminFacade.java`
around lines 203 - 223, Extract the duplicated approval/disapproval loop in
GraduationUserAdminFacade into a private helper (e.g., processGraduationUsers)
that iterates the List<GraduationUser> and invokes a passed-in action per user
to handle GraduationType.CERTIFICATE and GraduationType.THESIS cases; update
both approveGraduationUsers and the current disapproval loop (the code using
certificateCommandService.disapprove, thesisCommandService.disapprove, and
collecting disapprovedUserIds) to call this helper with appropriate lambdas or
method references so the certificateId/midThesisId/finalThesisId checks and
result-collection logic are centralized and not duplicated.

197-199: 배치 입력 ID 중복 제거를 먼저 적용하면 불필요한 조회/승인취소 호출을 줄일 수 있습니다.

request.ids()에 중복이 들어오면 동일 사용자에 대해 같은 작업이 반복될 수 있습니다.

제안 수정안
         List<GraduationUser> users = request.ids().stream()
+                .distinct()
                 .map(graduationUserQueryService::getById)
                 .toList();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@aics-admin/src/main/java/kgu/developers/admin/graduationUser/application/GraduationUserAdminFacade.java`
around lines 197 - 199, The code maps request.ids() directly to
graduationUserQueryService.getById causing duplicate IDs to result in repeated
DB/service calls; update GraduationUserAdminFacade to deduplicate the IDs first
(e.g., use request.ids().stream().distinct() or collect into a Set) before
mapping to getById so the users List and any subsequent operations (e.g.,
approval/cancel methods that use users) are only executed once per unique user.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@aics-admin/src/main/java/kgu/developers/admin/graduationUser/presentation/response/GraduationUserBatchDisapproveResponse.java`:
- Around line 15-18: In GraduationUserBatchDisapproveResponse.from(List<Long>
disapprovedUserIds) avoid storing the incoming list reference directly; create a
defensive copy (e.g., new ArrayList<>(disapprovedUserIds)) and pass that to the
builder for the disapprovedIds field (or wrap it with
Collections.unmodifiableList(...) if immutability is desired) so external
mutations of disapprovedUserIds cannot alter the response object's state.

---

Nitpick comments:
In
`@aics-admin/src/main/java/kgu/developers/admin/graduationUser/application/GraduationUserAdminFacade.java`:
- Around line 203-223: Extract the duplicated approval/disapproval loop in
GraduationUserAdminFacade into a private helper (e.g., processGraduationUsers)
that iterates the List<GraduationUser> and invokes a passed-in action per user
to handle GraduationType.CERTIFICATE and GraduationType.THESIS cases; update
both approveGraduationUsers and the current disapproval loop (the code using
certificateCommandService.disapprove, thesisCommandService.disapprove, and
collecting disapprovedUserIds) to call this helper with appropriate lambdas or
method references so the certificateId/midThesisId/finalThesisId checks and
result-collection logic are centralized and not duplicated.
- Around line 197-199: The code maps request.ids() directly to
graduationUserQueryService.getById causing duplicate IDs to result in repeated
DB/service calls; update GraduationUserAdminFacade to deduplicate the IDs first
(e.g., use request.ids().stream().distinct() or collect into a Set) before
mapping to getById so the users List and any subsequent operations (e.g.,
approval/cancel methods that use users) are only executed once per unique user.

In
`@aics-admin/src/testFixtures/java/graduationUser/application/GraduationUserAdminFacadeTest.java`:
- Around line 318-339: Replace the boolean equality assertions with assertFalse
for readability and add assertions to verify the returned
GraduationUserBatchDisapproveResponse from
graduationUserAdminFacade.disapproveGraduationUsers(request) (e.g., check that
the response is not null and contains expected ids/count), while keeping the
existing repository checks using
fakeThesisRepository.findApprovalByIdAndDeletedAtIsNull(1L/2L) and
fakeCertificateRepository.findApprovalByIdAndDeletedAtIsNull(1L) to ensure
approvals were cleared; update the test method disapproveGraduationUsers_Success
accordingly to capture the response and assert its expected fields and use
assertFalse for the repository booleans.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 9974e23f-501e-422f-bfbb-e46d6fba9090

📥 Commits

Reviewing files that changed from the base of the PR and between 9887688 and 5645544.

📒 Files selected for processing (10)
  • aics-admin/src/main/java/kgu/developers/admin/graduationUser/application/GraduationUserAdminFacade.java
  • aics-admin/src/main/java/kgu/developers/admin/graduationUser/presentation/GraduationUserAdminController.java
  • aics-admin/src/main/java/kgu/developers/admin/graduationUser/presentation/GraduationUserAdminControllerImpl.java
  • aics-admin/src/main/java/kgu/developers/admin/graduationUser/presentation/request/GraduationUserBatchDisapproveRequest.java
  • aics-admin/src/main/java/kgu/developers/admin/graduationUser/presentation/response/GraduationUserBatchDisapproveResponse.java
  • aics-admin/src/testFixtures/java/graduationUser/application/GraduationUserAdminFacadeTest.java
  • aics-domain/src/main/java/kgu/developers/domain/certificate/application/command/CertificateCommandService.java
  • aics-domain/src/main/java/kgu/developers/domain/certificate/domain/Certificate.java
  • aics-domain/src/main/java/kgu/developers/domain/thesis/application/command/ThesisCommandService.java
  • aics-domain/src/main/java/kgu/developers/domain/thesis/domain/Thesis.java

Comment on lines +15 to +18
public static GraduationUserBatchDisapproveResponse from(List<Long> disapprovedUserIds) {
return GraduationUserBatchDisapproveResponse.builder()
.disapprovedIds(disapprovedUserIds)
.build();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

from()에서 입력 리스트 참조를 그대로 보관하면 응답 값이 외부 변경에 오염될 수 있습니다.

호출자가 disapprovedUserIds를 이후에 수정하면 응답 객체의 내용도 함께 바뀔 수 있어, 방어적 복사를 권장합니다.

제안 수정안
 import java.util.List;
+import java.util.Objects;
@@
     public static GraduationUserBatchDisapproveResponse from(List<Long> disapprovedUserIds) {
             return GraduationUserBatchDisapproveResponse.builder()
-                    .disapprovedIds(disapprovedUserIds)
+                    .disapprovedIds(List.copyOf(Objects.requireNonNull(disapprovedUserIds)))
                     .build();
     }
📝 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
public static GraduationUserBatchDisapproveResponse from(List<Long> disapprovedUserIds) {
return GraduationUserBatchDisapproveResponse.builder()
.disapprovedIds(disapprovedUserIds)
.build();
public static GraduationUserBatchDisapproveResponse from(List<Long> disapprovedUserIds) {
return GraduationUserBatchDisapproveResponse.builder()
.disapprovedIds(List.copyOf(Objects.requireNonNull(disapprovedUserIds)))
.build();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@aics-admin/src/main/java/kgu/developers/admin/graduationUser/presentation/response/GraduationUserBatchDisapproveResponse.java`
around lines 15 - 18, In GraduationUserBatchDisapproveResponse.from(List<Long>
disapprovedUserIds) avoid storing the incoming list reference directly; create a
defensive copy (e.g., new ArrayList<>(disapprovedUserIds)) and pass that to the
builder for the disapprovedIds field (or wrap it with
Collections.unmodifiableList(...) if immutability is desired) so external
mutations of disapprovedUserIds cannot alter the response object's state.

Copy link
Copy Markdown
Contributor

@JangYeongHu JangYeongHu left a comment

Choose a reason for hiding this comment

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

LGTM!

이슈만 추가해서 연관 이슈에 달아주세요.

@2heunxun
Copy link
Copy Markdown
Contributor

LGTM~
작업자 이름만 본인이름으로 바꿔주시면 될 거 같습니다 고생하셨어요 :D

Copy link
Copy Markdown
Contributor

@dkdltm221 dkdltm221 left a comment

Choose a reason for hiding this comment

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

LGTM👍
고생하셨어요!
새로 추가한 서비스 로직에 대한 테스트 코드가 필요해보입니다!

@hoTan35 hoTan35 merged commit d2a862b into develop Mar 27, 2026
2 of 3 checks passed
@hoTan35 hoTan35 deleted the feature/KD-73 branch March 27, 2026 09:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨feature create new feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

자격증 신청 관리 메뉴와 졸업 논문 관리 메뉴에서 승인된 사용자 승인 취소

4 participants