Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package kgu.developers.admin.certificate.application;

import kgu.developers.admin.certificate.presentation.response.CertificateDetailResponse;
import kgu.developers.domain.certificate.application.query.CertificateQueryService;
import kgu.developers.domain.certificate.domain.Certificate;
import kgu.developers.domain.file.application.query.FileQueryService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

@Component
@Transactional
@RequiredArgsConstructor
public class CertificateAdminFacade {
private final CertificateQueryService certificateQueryService;
private final FileQueryService fileQueryService;

public CertificateDetailResponse getById(Long id){
Certificate certificate = certificateQueryService.getById(id);
String physicalPath = certificate.getCertificateFileId() != null
? fileQueryService.getFilePhysicalPath(certificate.getCertificateFileId())
: null;
return CertificateDetailResponse.from(certificate, physicalPath);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package kgu.developers.admin.certificate.presentation;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import kgu.developers.admin.certificate.presentation.response.CertificateDetailResponse;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;

@Tag(name = "Certificate", description = "자격증 관련 API")
public interface CertificateAdminController {
@Operation(summary = "자격증 개별 조회 API", description = """
- Description : 자격증 id로 조회합니다.
- Assignee : 주윤빈
""")
@ApiResponse(
responseCode = "200",
content = @Content(schema = @Schema(implementation = CertificateDetailResponse.class)))
ResponseEntity<CertificateDetailResponse> getCertificate(
@Parameter(description = "자격증 id",required = true) @PathVariable Long id
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package kgu.developers.admin.certificate.presentation;

import kgu.developers.admin.certificate.application.CertificateAdminFacade;
import kgu.developers.admin.certificate.presentation.response.CertificateDetailResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/v1/admin/certificate")
@RequiredArgsConstructor
@PreAuthorize("hasRole('ROLE_ADMIN')")
public class CertificateAdminControllerImpl implements CertificateAdminController {
private final CertificateAdminFacade certificateAdminFacade;

@Override
@GetMapping("/{id}")
public ResponseEntity<CertificateDetailResponse> getCertificate(@PathVariable Long id) {
return ResponseEntity.ok(certificateAdminFacade.getById(id));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package kgu.developers.admin.certificate.presentation.response;

import io.swagger.v3.oas.annotations.media.Schema;
import kgu.developers.domain.certificate.domain.Certificate;
import kgu.developers.domain.file.application.response.FilePathResponse;
import lombok.Builder;

import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.NOT_REQUIRED;
import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED;

@Builder
public record CertificateDetailResponse(
@Schema(description = "자격증 객체 id", example = "1", requiredMode = REQUIRED)
Long id,

@Schema(description = "자격증 관련 일정 ID", example = "4", requiredMode = REQUIRED)
Long scheduleId,

@Schema(description = "승인 여부", example = "false",requiredMode = REQUIRED)
boolean approval,

@Schema(description = "첨부 파일 정보",
example = "{\"id\": 1, "
+ "\"physicalPath\": \"/files/2025-certificate\"}",
requiredMode = NOT_REQUIRED)
FilePathResponse certificateFile
) {
public static CertificateDetailResponse from(Certificate certificate,String physicalPath) {
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

메서드 시그니처의 포맷을 수정해주세요.

파라미터 사이에 공백이 누락되었습니다. certificate,Stringcertificate, String으로 수정해주세요.

🔎 포맷 수정 제안
-    public static CertificateDetailResponse from(Certificate certificate,String physicalPath) {
+    public static CertificateDetailResponse from(Certificate certificate, String physicalPath) {
📝 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 CertificateDetailResponse from(Certificate certificate,String physicalPath) {
public static CertificateDetailResponse from(Certificate certificate, String physicalPath) {
🤖 Prompt for AI Agents
In
aics-api/src/main/java/kgu/developers/api/certificate/presentation/response/CertificateDetailResponse.java
around line 28, the method signature has a missing space after the comma
("certificate,String"); update the signature to include a space between
parameters ("certificate, String") so it follows standard Java formatting
conventions.

return CertificateDetailResponse.builder()
.id(certificate.getId())
.scheduleId(certificate.getScheduleId())
.approval(certificate.isApproved())
.certificateFile(certificate.getCertificateFileId() != null
? FilePathResponse.of(certificate.getCertificateFileId(),physicalPath)
: null
)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package kgu.developers.admin.thesis.application;

import kgu.developers.admin.thesis.presentation.response.ThesisDetailResponse;
import kgu.developers.domain.file.application.query.FileQueryService;
import kgu.developers.domain.thesis.application.query.ThesisQueryService;
import kgu.developers.domain.thesis.domain.Thesis;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

@Component
@Transactional
@RequiredArgsConstructor
public class ThesisAdminFacade {

private final ThesisQueryService thesisQueryService;
private final FileQueryService fileQueryService;

public ThesisDetailResponse getById(Long id){
Thesis thesis = thesisQueryService.getById(id);
String physicalPath = thesis.getThesisFileId() != null
? fileQueryService.getFilePhysicalPath(thesis.getThesisFileId())
: null;
return ThesisDetailResponse.from(thesis, physicalPath);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package kgu.developers.admin.thesis.presentation;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import kgu.developers.admin.thesis.presentation.response.ThesisDetailResponse;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;

@Tag(name = "Thesis", description = "졸업 논문 API")
public interface ThesisAdminController {

@Operation(summary = "졸업 논문 개별 조회 API", description = """
- Description :논문 id로 조회합니다.
- Assignee : 주윤빈
""")
@ApiResponse(
responseCode = "200",
content = @Content(schema = @Schema(implementation = ThesisDetailResponse.class)))
ResponseEntity<ThesisDetailResponse> getThesis(
@Parameter(description = "논문 id",required = true) @PathVariable Long id
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package kgu.developers.admin.thesis.presentation;

import kgu.developers.admin.thesis.application.ThesisAdminFacade;
import kgu.developers.admin.thesis.presentation.response.ThesisDetailResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/v1/admin/thesis")
@RequiredArgsConstructor
@PreAuthorize("hasRole('ROLE_ADMIN')")
public class ThesisAdminControllerImpl implements ThesisAdminController {
private final ThesisAdminFacade thesisAdminFacade;

@Override
@GetMapping("/{id}")
public ResponseEntity<ThesisDetailResponse> getThesis(@PathVariable Long id) {
return ResponseEntity.ok(thesisAdminFacade.getById(id));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package kgu.developers.admin.thesis.presentation.response;

import io.swagger.v3.oas.annotations.media.Schema;
import kgu.developers.domain.file.application.response.FilePathResponse;
import kgu.developers.domain.thesis.domain.Thesis;
import lombok.Builder;

import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.NOT_REQUIRED;
import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED;

@Builder
public record ThesisDetailResponse(
@Schema(description = "졸업 논문 객체 id", example = "1", requiredMode = REQUIRED)
Long id,
@Schema(description = "졸업 논문 관련 일정 ID",example = "2",requiredMode = REQUIRED)
Long scheduleId,
@Schema(description = "승인 여부", example = "false",requiredMode = REQUIRED)
boolean approval,

@Schema(description = "첨부 파일 정보",
example = "{\"id\": 1, "
+ "\"physicalPath\": \"/files/2025-thesis\"}",
requiredMode = NOT_REQUIRED)
FilePathResponse thesisFile

) {
public static ThesisDetailResponse from(Thesis thesis,String physicalPath) {
return ThesisDetailResponse.builder()
.id(thesis.getId())
.scheduleId(thesis.getScheduleId())
.approval(thesis.isApproved())
.thesisFile(thesis.getThesisFileId() != null
? FilePathResponse.of(thesis.getThesisFileId(),physicalPath): null)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package certificate.application;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

import kgu.developers.admin.certificate.application.CertificateAdminFacade;
import kgu.developers.admin.certificate.presentation.response.CertificateDetailResponse;
import kgu.developers.domain.certificate.application.query.CertificateQueryService;
import kgu.developers.domain.certificate.domain.Certificate;
import kgu.developers.domain.certificate.exception.CertificateNotFoundException;
import kgu.developers.domain.file.application.query.FileQueryService;
import mock.repository.FakeCertificateRepository;
import mock.repository.FakeFileRepository;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

public class CertificateAdminFacadeTest {

private CertificateAdminFacade certificateAdminFacade;
private FakeCertificateRepository fakeCertificateRepository;

@BeforeEach
void init(){
fakeCertificateRepository = new FakeCertificateRepository();
certificateAdminFacade= new CertificateAdminFacade(
new CertificateQueryService(fakeCertificateRepository),
new FileQueryService(new FakeFileRepository()));
fakeCertificateRepository.save(Certificate.of(1L,3L,null,true,null,null,null));
}

@Test
@DisplayName("getById는 자격증 정보를 반환한다.")
void getById_success(){
CertificateDetailResponse response = certificateAdminFacade.getById(1L);

assertEquals(1L,response.id());
assertEquals(3L,response.scheduleId());
assertEquals(true,response.approval());
assertEquals(null,response.certificateFile());
}
@Test
@DisplayName("없는 id 조회 시 CertificateNotFoundException")
void getById_notFound() {
assertThatThrownBy(() -> certificateAdminFacade.getById(999L))
.isInstanceOf(CertificateNotFoundException.class);
}


}
49 changes: 49 additions & 0 deletions aics-admin/src/testFixtures/java/thesis/ThesisAdminFacadeTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package thesis;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

import kgu.developers.admin.thesis.application.ThesisAdminFacade;
import kgu.developers.admin.thesis.presentation.response.ThesisDetailResponse;
import kgu.developers.domain.file.application.query.FileQueryService;
import kgu.developers.domain.thesis.application.query.ThesisQueryService;
import kgu.developers.domain.thesis.domain.Thesis;
import kgu.developers.domain.thesis.exception.ThesisNotFoundException;
import mock.repository.FakeFileRepository;
import mock.repository.FakeThesisRepository;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

public class ThesisAdminFacadeTest {
private ThesisAdminFacade thesisAdminFacade;
private FakeThesisRepository fakeThesisRepository;

@BeforeEach
void init(){
fakeThesisRepository = new FakeThesisRepository();
thesisAdminFacade = new ThesisAdminFacade(
new ThesisQueryService(fakeThesisRepository),
new FileQueryService(new FakeFileRepository())
);
fakeThesisRepository.save(Thesis.of(1L,3L,null,false,null,null,null));
}

@Test
@DisplayName("getById는 논문 정보를 반환한다")
void getById(){
ThesisDetailResponse response = thesisAdminFacade.getById(1L);

assertEquals(1L,response.id());
assertEquals(3L, response.scheduleId());
assertEquals(false, response.approval());
assertEquals(null, response.thesisFile());
}
@Test
@DisplayName("없는 id 조회 시 ThesisNotFoundException")
void getById_notFound() {
assertThatThrownBy(() -> thesisAdminFacade.getById(999L))
.isInstanceOf(ThesisNotFoundException.class);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,5 @@ public Long submitCertificate(MultipartFile file, Long scheduleId) {
graduationUserCommandService.updateCertificate(graduationUser, certificateId);
return certificateId;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,11 @@

import kgu.developers.api.certificate.application.CertificateFacade;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import kgu.developers.api.certificate.presentation.request.CertificateSubmitRequest;
import kgu.developers.api.certificate.presentation.response.CertificatePersistResponse;
import kgu.developers.domain.certificate.application.command.CertificateCommandService;
import lombok.RequiredArgsConstructor;

@RestController
Expand All @@ -32,6 +28,4 @@ public ResponseEntity<CertificatePersistResponse> submitCertificateAndSaveFile(
CertificatePersistResponse.of(id)
);
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,5 @@ public Long submitThesis(MultipartFile file, Long scheduleId) {
graduationUserCommandService.updateThesis(graduationUser, thesisId, schedule);
return thesisId;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,6 @@ ResponseEntity<ThesisPersistResponse> submitThesisAndSaveFile(
) @RequestPart(value = "file") MultipartFile file,
@RequestPart ThesisSubmitRequest request
);


}
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,7 @@
import kgu.developers.api.thesis.presentation.response.ThesisPersistResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import static org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE;
Expand Down
2 changes: 1 addition & 1 deletion aics-api/src/main/resources/db/schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ CREATE TABLE post
category VARCHAR(50)
CONSTRAINT post_category_check
CHECK ((category)::TEXT = ANY
(ARRAY ['NOTIFICATION', 'NEWS'])),
(ARRAY ['NOTIFICATION', 'NEWS','GRADUATION'])),
created_at TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP(6) DEFAULT NULL,
Expand Down
Loading