Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DDING-108] 동아리 지원 결과 email 전송 API 구현 #256

Merged
merged 12 commits into from
Feb 16, 2025
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ dependencies {
implementation 'io.sentry:sentry-logback:7.6.0'
implementation 'com.fasterxml.jackson.core:jackson-core'
implementation 'com.github.f4b6a3:uuid-creator:6.0.0'
implementation 'software.amazon.awssdk:ses:2.24.0'
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

AWS SDK 버전 검증이 필요합니다.

AWS SDK for SES의 의존성이 추가되었습니다. 다음 사항들을 확인해 주시기 바랍니다:

  1. 최신 안정 버전 사용 여부
  2. 기존 AWS 의존성과의 호환성 (io.awspring.cloud:spring-cloud-starter-aws:2.4.4)

AWS SDK 버전을 확인하기 위해 다음 웹 검색을 실행해 주세요:


🌐 Web query:

What is the latest stable version of software.amazon.awssdk:ses?

💡 Result:

The latest stable version of software.amazon.awssdk:ses as of February 2025 is 2.30.16, based on recent AWS SDK updates[7][9]. This version includes enhancements like improved Amazon SES Mail Manager features for traffic policy management[7].

For Maven projects, use:

<dependency>
    <groupId>software.amazon.awssdk</groupId>
    <artifactId>ses</artifactId>
    <version>2.30.16</version>
</dependency>

Key details:

  • The AWS SDK for Java 2.x follows semantic versioning with regular updates[6][10]
  • Version 2.x replaced legacy 1.x SDKs (now in maintenance mode)[1][3]
  • Recent updates focus on checksum validation improvements and new SES capabilities[7][9]

Always verify dependencies against official AWS documentation or Maven Central for real-time version tracking[4][8].

Citations:


AWS SDK SES 의존성 버전 업데이트 필요

현재 build.gradle 파일에서는 software.amazon.awssdk:ses:2.24.0 버전을 사용 중입니다. 최신 웹 검색 결과에 따르면 February 2025 기준 최신 안정 버전은 2.30.16입니다.
따라서, 아래 사항을 검토해 주시기 바랍니다:

  • AWS SDK for SES 의존성을 최신 안정 버전(2.30.16)으로 업데이트할 것
  • io.awspring.cloud:spring-cloud-starter-aws:2.4.4 와의 호환성 재확인


//security
implementation 'org.springframework.boot:spring-boot-starter-security'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.ses.SesClient;

@Configuration
public class AmazonS3Config {
Expand All @@ -30,4 +34,14 @@ public AmazonS3Client amazonS3Client() {
.build();
}

@Bean
public SesClient sesClient() {
return SesClient.builder()
.region(Region.AP_NORTHEAST_2)
.credentialsProvider(StaticCredentialsProvider.create(
AwsBasicCredentials.create(accessKey, secretKey)
))
.build();
}
Comment on lines +37 to +45
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

AWS SES 클라이언트 설정 개선이 필요합니다.

다음 사항들을 개선하면 좋을 것 같습니다:

  1. Region이 하드코딩되어 있습니다. S3 설정과 같이 환경 변수로 관리하는 것이 좋습니다.
  2. SES 관련 설정이 S3Config 클래스에 있는 것은 단일 책임 원칙(SRP)에 위배됩니다.

다음과 같이 수정하는 것을 제안드립니다:

-@Bean
-public SesClient sesClient() {
-    return SesClient.builder()
-            .region(Region.AP_NORTHEAST_2)
-            .credentialsProvider(StaticCredentialsProvider.create(
-                    AwsBasicCredentials.create(accessKey, secretKey)
-            ))
-            .build();
-}

새로운 AmazonSesConfig 클래스를 생성하여 다음과 같이 구현:

@Configuration
public class AmazonSesConfig {
    @Value("${cloud.aws.region.static}")
    private String region;

    @Value("${spring.s3.access-key}")
    private String accessKey;

    @Value("${spring.s3.secret-key}")
    private String secretKey;

    @Bean
    public SesClient sesClient() {
        return SesClient.builder()
                .region(Region.of(region))
                .credentialsProvider(StaticCredentialsProvider.create(
                        AwsBasicCredentials.create(accessKey, secretKey)
                ))
                .build();
    }
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package ddingdong.ddingdongBE.common.config;

import java.util.concurrent.Executor;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {

@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(500);
executor.setThreadNamePrefix("EmailAsync-");
executor.initialize();
return executor;
}

@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new SimpleAsyncUncaughtExceptionHandler();
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import ddingdong.ddingdongBE.auth.PrincipalDetails;
import ddingdong.ddingdongBE.domain.form.controller.dto.request.CreateFormRequest;
import ddingdong.ddingdongBE.domain.form.controller.dto.request.SendApplicationResultEmailRequest;
import ddingdong.ddingdongBE.domain.form.controller.dto.request.UpdateFormRequest;
import ddingdong.ddingdongBE.domain.form.controller.dto.response.FormListResponse;
import ddingdong.ddingdongBE.domain.form.controller.dto.response.FormResponse;
Expand Down Expand Up @@ -115,4 +116,12 @@ FormStatisticsResponse getFormStatistics(
@SecurityRequirement(name = "AccessToken")
@PostMapping("/my/forms/{formId}/members/register-applicants")
void registerMembers(@PathVariable("formId") Long formId);

@Operation(summary = "동아리 지원 결과 이메일 전송 API")
@ApiResponse(responseCode = "201", description = "동아리 지원 결과 이메일 전송 성공")
@ResponseStatus(HttpStatus.OK)
@SecurityRequirement(name = "AccessToken")
Comment on lines +120 to +123
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

HTTP 상태 코드가 일관되지 않습니다.

@ApiResponse는 201을 명시하고 있지만, @ResponseStatus는 200을 반환하도록 설정되어 있습니다. 리소스 생성을 의미하는 201로 통일하는 것이 좋습니다.

다음과 같이 수정하는 것을 제안합니다:

 @Operation(summary = "동아리 지원 결과 이메일 전송 API")
 @ApiResponse(responseCode = "201", description = "동아리 지원 결과 이메일 전송 성공")
-@ResponseStatus(HttpStatus.OK)
+@ResponseStatus(HttpStatus.CREATED)
 @SecurityRequirement(name = "AccessToken")
📝 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
@Operation(summary = "동아리 지원 결과 이메일 전송 API")
@ApiResponse(responseCode = "201", description = "동아리 지원 결과 이메일 전송 성공")
@ResponseStatus(HttpStatus.OK)
@SecurityRequirement(name = "AccessToken")
@Operation(summary = "동아리 지원 결과 이메일 전송 API")
@ApiResponse(responseCode = "201", description = "동아리 지원 결과 이메일 전송 성공")
@ResponseStatus(HttpStatus.CREATED)
@SecurityRequirement(name = "AccessToken")

@PostMapping("/my/forms/{formId}/results/email")
void sendApplicationResultEmail(@PathVariable("formId") Long formId,
@RequestBody SendApplicationResultEmailRequest request);
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import ddingdong.ddingdongBE.auth.PrincipalDetails;
import ddingdong.ddingdongBE.domain.form.api.CentralFormApi;
import ddingdong.ddingdongBE.domain.form.controller.dto.request.CreateFormRequest;
import ddingdong.ddingdongBE.domain.form.controller.dto.request.SendApplicationResultEmailRequest;
import ddingdong.ddingdongBE.domain.form.controller.dto.request.UpdateFormRequest;
import ddingdong.ddingdongBE.domain.form.controller.dto.response.FormListResponse;
import ddingdong.ddingdongBE.domain.form.controller.dto.response.FormResponse;
Expand Down Expand Up @@ -93,4 +94,9 @@ public TextFieldStatisticsResponse getTextFieldStatistics(Long fieldId) {
public void registerMembers(Long formId) {
facadeCentralFormService.registerApplicantAsMember(formId);
}

@Override
public void sendApplicationResultEmail(Long formId, SendApplicationResultEmailRequest request) {
facadeCentralFormService.sendApplicationResultEmail(request.toCommand(formId));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package ddingdong.ddingdongBE.domain.form.controller.dto.request;

import ddingdong.ddingdongBE.domain.form.service.dto.command.SendApplicationResultEmailCommand;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;

public record SendApplicationResultEmailRequest(

@Schema(description = "메일 제목", example = "제목")
@NotNull(message = "메일 제목은 필수입니다.")
String title,

@Schema(description = "전송 대상",
example = "FIRST_PASS",
allowableValues = {"FIRST_PASS", "FIRST_FAIL", "FINAL_PASS", "FINAL_FAIL"}
)
@NotNull(message = "전송 대상은 필수입니다.")
String target,
Comment on lines +13 to +18
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

전송 대상을 열거형(enum)으로 변경하는 것이 좋습니다.

String 타입 대신 enum을 사용하면 타입 안전성이 향상되고 가능한 값들을 명확하게 제한할 수 있습니다.

+public enum EmailTarget {
+    FIRST_PASS, FIRST_FAIL, FINAL_PASS, FINAL_FAIL
+}

 public record SendApplicationResultEmailRequest(
         @Schema(description = "전송 대상",
                 example = "FIRST_PASS",
                 allowableValues = {"FIRST_PASS", "FIRST_FAIL", "FINAL_PASS", "FINAL_FAIL"}
         )
         @NotNull(message = "전송 대상은 필수입니다.")
-        String target,
+        EmailTarget target,
📝 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
@Schema(description = "전송 대상",
example = "FIRST_PASS",
allowableValues = {"FIRST_PASS", "FIRST_FAIL", "FINAL_PASS", "FINAL_FAIL"}
)
@NotNull(message = "전송 대상은 필수입니다.")
String target,
public enum EmailTarget {
FIRST_PASS, FIRST_FAIL, FINAL_PASS, FINAL_FAIL
}
public record SendApplicationResultEmailRequest(
@Schema(description = "전송 대상",
example = "FIRST_PASS",
allowableValues = {"FIRST_PASS", "FIRST_FAIL", "FINAL_PASS", "FINAL_FAIL"}
)
@NotNull(message = "전송 대상은 필수입니다.")
EmailTarget target,
// 다른 필드들...
) {
// toCommand 변환 메서드 등 필요한 추가 코드...
}


@Schema(description = "내용", example = "내용")
@NotNull(message = "메일 내용은 필수입니다.")
String message
) {

public SendApplicationResultEmailCommand toCommand(Long formId) {
return new SendApplicationResultEmailCommand(formId, title, target, message);
}


}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package ddingdong.ddingdongBE.domain.form.service;

import ddingdong.ddingdongBE.domain.form.service.dto.command.CreateFormCommand;
import ddingdong.ddingdongBE.domain.form.service.dto.command.SendApplicationResultEmailCommand;
import ddingdong.ddingdongBE.domain.form.service.dto.command.UpdateFormCommand;
import ddingdong.ddingdongBE.domain.form.service.dto.query.FormListQuery;
import ddingdong.ddingdongBE.domain.form.service.dto.query.FormQuery;
Expand Down Expand Up @@ -29,4 +30,6 @@ public interface FacadeCentralFormService {
void registerApplicantAsMember(Long formId);

TextFieldStatisticsQuery getTextFieldStatistics(Long fieldId);

void sendApplicationResultEmail(SendApplicationResultEmailCommand command);
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import ddingdong.ddingdongBE.domain.form.entity.FormField;
import ddingdong.ddingdongBE.domain.form.service.dto.command.CreateFormCommand;
import ddingdong.ddingdongBE.domain.form.service.dto.command.CreateFormCommand.CreateFormFieldCommand;
import ddingdong.ddingdongBE.domain.form.service.dto.command.SendApplicationResultEmailCommand;
import ddingdong.ddingdongBE.domain.form.service.dto.command.UpdateFormCommand;
import ddingdong.ddingdongBE.domain.form.service.dto.command.UpdateFormCommand.UpdateFormFieldCommand;
import ddingdong.ddingdongBE.domain.form.service.dto.query.FormListQuery;
Expand All @@ -21,30 +22,38 @@
import ddingdong.ddingdongBE.domain.form.service.dto.query.FormStatisticsQuery.ApplicantStatisticQuery;
import ddingdong.ddingdongBE.domain.form.service.dto.query.FormStatisticsQuery.DepartmentStatisticQuery;
import ddingdong.ddingdongBE.domain.form.service.dto.query.FormStatisticsQuery.FieldStatisticsQuery;
import ddingdong.ddingdongBE.domain.formapplication.entity.FormApplication;
import ddingdong.ddingdongBE.domain.formapplication.entity.FormApplicationStatus;
import ddingdong.ddingdongBE.domain.formapplication.service.FormApplicationService;
import ddingdong.ddingdongBE.domain.form.service.dto.query.MultipleFieldStatisticsQuery;
import ddingdong.ddingdongBE.domain.form.service.dto.query.MultipleFieldStatisticsQuery.OptionStatisticQuery;
import ddingdong.ddingdongBE.domain.form.service.dto.query.TextFieldStatisticsQuery;
import ddingdong.ddingdongBE.domain.form.service.dto.query.TextFieldStatisticsQuery.TextStatisticsQuery;
import ddingdong.ddingdongBE.domain.formapplication.entity.FormApplication;
import ddingdong.ddingdongBE.domain.formapplication.service.FormApplicationService;
import ddingdong.ddingdongBE.domain.user.entity.User;
import ddingdong.ddingdongBE.email.SesEmailService;
import ddingdong.ddingdongBE.email.dto.EmailContent;
import java.time.LocalDate;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
@Slf4j
public class FacadeCentralFormServiceImpl implements FacadeCentralFormService {

private final FormService formService;
private final FormFieldService formFieldService;
private final ClubService clubService;
private final FormStatisticService formStatisticService;
private final FormApplicationService formApplicationService;
private final SesEmailService sesEmailService;

@Transactional
@Override
Expand Down Expand Up @@ -132,14 +141,16 @@ public MultipleFieldStatisticsQuery getMultipleFieldStatistics(Long fieldId) {
@Override
@Transactional
public void registerApplicantAsMember(Long formId) {
List<FormApplication> finalPassedFormApplications = formApplicationService.getAllFinalPassedByFormId(
formId);
List<FormApplication> finalPassedFormApplications = formApplicationService.getAllFinalPassedByFormId(formId);
finalPassedFormApplications.forEach(formApplication -> {
Club club = formApplication.getForm().getClub();
ClubMember clubMember = ClubMember.builder().name(formApplication.getName())
ClubMember clubMember = ClubMember.builder()
.name(formApplication.getName())
.studentNumber(formApplication.getStudentNumber())
.department(formApplication.getDepartment())
.phoneNumber(formApplication.getPhoneNumber()).position(MEMBER).build();
.phoneNumber(formApplication.getPhoneNumber())
.position(MEMBER)
.build();
club.addClubMember(clubMember);
});
}
Expand All @@ -155,6 +166,23 @@ public TextFieldStatisticsQuery getTextFieldStatistics(Long fieldId) {
return new TextFieldStatisticsQuery(type, textStatisticsQueries);
}

@Override
public void sendApplicationResultEmail(SendApplicationResultEmailCommand command) {
List<FormApplication> formApplications = formApplicationService.getAllByFormIdAndFormApplicationStatus(
command.formId(),
FormApplicationStatus.findStatus(command.target())
);
EmailContent emailContent = EmailContent.of(command.title(), command.message());
CompletableFuture<Void> future = sesEmailService.sendBulkResultEmails(formApplications, emailContent);
Comment on lines +176 to +182
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

이메일 전송 로직에 대한 유효성 검사 추가 필요

현재 구현에 다음 사항들을 추가하면 좋을 것 같습니다:

  1. formApplications가 비어있는 경우에 대한 처리
  2. 이메일 제목과 내용의 유효성 검사 (null, 빈 문자열 등)

다음과 같이 수정하는 것을 제안드립니다:

 public void sendApplicationResultEmail(SendApplicationResultEmailCommand command) {
+    if (command.title() == null || command.title().trim().isEmpty()) {
+        throw new IllegalArgumentException("이메일 제목은 필수입니다.");
+    }
+    if (command.message() == null || command.message().trim().isEmpty()) {
+        throw new IllegalArgumentException("이메일 내용은 필수입니다.");
+    }
     List<FormApplication> formApplications = formApplicationService.getAllByFormIdAndFormApplicationStatus(
             command.formId(),
             FormApplicationStatus.findStatus(command.target())
     );
+    if (formApplications.isEmpty()) {
+        log.warn("지원자가 없습니다. formId: {}, status: {}", command.formId(), command.target());
+        return;
+    }
     EmailContent emailContent = EmailContent.of(command.title(), command.message());
     CompletableFuture<Void> future = sesEmailService.sendBulkResultEmails(formApplications, emailContent);
📝 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 void sendApplicationResultEmail(SendApplicationResultEmailCommand command) {
List<FormApplication> formApplications = formApplicationService.getAllByFormIdAndFormApplicationStatus(
command.formId(),
FormApplicationStatus.findStatus(command.target())
);
EmailContent emailContent = EmailContent.of(command.title(), command.message());
CompletableFuture<Void> future = sesEmailService.sendBulkResultEmails(formApplications, emailContent);
public void sendApplicationResultEmail(SendApplicationResultEmailCommand command) {
if (command.title() == null || command.title().trim().isEmpty()) {
throw new IllegalArgumentException("이메일 제목은 필수입니다.");
}
if (command.message() == null || command.message().trim().isEmpty()) {
throw new IllegalArgumentException("이메일 내용은 필수입니다.");
}
List<FormApplication> formApplications = formApplicationService.getAllByFormIdAndFormApplicationStatus(
command.formId(),
FormApplicationStatus.findStatus(command.target())
);
if (formApplications.isEmpty()) {
log.warn("지원자가 없습니다. formId: {}, status: {}", command.formId(), command.target());
return;
}
EmailContent emailContent = EmailContent.of(command.title(), command.message());
CompletableFuture<Void> future = sesEmailService.sendBulkResultEmails(formApplications, emailContent);
}


try {
future.get(5, TimeUnit.MINUTES); // 최대 5분 대기
} catch (Exception e) {
log.error("Failed to send bulk emails", e);
throw new RuntimeException("이메일 전송 중 오류가 발생했습니다.", e);
}
}

private FormListQuery buildFormListQuery(Form form) {
boolean isActive = TimeUtils.isDateInRange(LocalDate.now(), form.getStartDate(),
form.getEndDate());
Expand All @@ -177,13 +205,13 @@ public void validateDuplicationDate(Club club, LocalDate startDate, LocalDate en
}

private List<FormField> toUpdateFormFields(Form originform,
List<UpdateFormFieldCommand> updateFormFieldCommands) {
List<UpdateFormFieldCommand> updateFormFieldCommands) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

p4)
컨벤션이 잘못된 것 맞나요?

return updateFormFieldCommands.stream()
.map(formFieldCommand -> formFieldCommand.toEntity(originform)).toList();
}

private List<FormField> toCreateFormFields(Form savedForm,
List<CreateFormFieldCommand> createFormFieldCommands) {
List<CreateFormFieldCommand> createFormFieldCommands) {
return createFormFieldCommands.stream()
.map(formFieldCommand -> formFieldCommand.toEntity(savedForm)).toList();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package ddingdong.ddingdongBE.domain.form.service.dto.command;

public record SendApplicationResultEmailCommand(
Long formId,
String title,
String target,
String message
) {

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import ddingdong.ddingdongBE.domain.form.entity.Form;
import ddingdong.ddingdongBE.domain.formapplication.entity.FormApplication;
import ddingdong.ddingdongBE.domain.formapplication.entity.FormApplicationStatus;
import ddingdong.ddingdongBE.domain.formapplication.repository.dto.DepartmentInfo;
import ddingdong.ddingdongBE.domain.formapplication.repository.dto.RecentFormInfo;
import java.time.LocalDate;
Expand All @@ -15,19 +16,19 @@
@Repository
public interface FormApplicationRepository extends JpaRepository<FormApplication, Long> {

@Query(value = """
select *
from form_application f
where (:currentCursorId = -1 or id < :currentCursorId)
and f.form_id = :formId
order by f.id DESC
limit :size
""", nativeQuery = true)
Slice<FormApplication> findPageByFormIdOrderById(
@Param("formId") Long formId,
@Param("size") int size,
@Param("currentCursorId") Long currentCursorId
);
@Query(value = """
select *
from form_application f
where (:currentCursorId = -1 or id < :currentCursorId)
and f.form_id = :formId
order by f.id DESC
limit :size
""", nativeQuery = true)
Slice<FormApplication> findPageByFormIdOrderById(
@Param("formId") Long formId,
@Param("size") int size,
@Param("currentCursorId") Long currentCursorId
);

Long countByForm(Form form);

Expand Down Expand Up @@ -63,11 +64,14 @@ List<RecentFormInfo> findRecentFormByDateWithApplicationCount(
@Param("date") LocalDate date,
@Param("size") int size
);

@Query(value = """
select fa
from FormApplication fa
where fa.form.id = :formId and (fa.status = 'FINAL_PASS' or (fa.status = 'FIRST_PASS' and fa.form.hasInterview = false))
""")
List<FormApplication> findAllFinalPassedByFormId(@Param("formId") Long formId);

List<FormApplication> getAllByFormIdAndStatus(Long formId, FormApplicationStatus status);

}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package ddingdong.ddingdongBE.domain.formapplication.service;

import ddingdong.ddingdongBE.domain.formapplication.entity.FormApplication;
import ddingdong.ddingdongBE.domain.formapplication.entity.FormApplicationStatus;
import org.springframework.data.domain.Slice;

import java.util.List;
Expand All @@ -16,4 +17,6 @@ public interface FormApplicationService {
List<FormApplication> getAllById(List<Long> applicationIds);

List<FormApplication> getAllFinalPassedByFormId(Long formId);

List<FormApplication> getAllByFormIdAndFormApplicationStatus(Long formId, FormApplicationStatus status);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import ddingdong.ddingdongBE.common.exception.PersistenceException.ResourceNotFound;
import ddingdong.ddingdongBE.domain.formapplication.entity.FormApplication;
import ddingdong.ddingdongBE.domain.formapplication.entity.FormApplicationStatus;
import ddingdong.ddingdongBE.domain.formapplication.repository.FormApplicationRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.PageRequest;
Expand Down Expand Up @@ -44,6 +45,11 @@ public List<FormApplication> getAllFinalPassedByFormId(Long formId) {
return formApplicationRepository.findAllFinalPassedByFormId(formId);
}

@Override
public List<FormApplication> getAllByFormIdAndFormApplicationStatus(Long formId, FormApplicationStatus status) {
return formApplicationRepository.getAllByFormIdAndStatus(formId, status);
}

@Override
public FormApplication getById(Long applicationId) {
return formApplicationRepository.findById(applicationId)
Expand Down
Loading
Loading