-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[YS-201] feat: 추천 실험 공고 메일 전송 로직 구현 (#77)
* feat: implements daily matching algorithms - 매일 아침 07:55 실행되는 벌크 크론 잡의 매칭 알고리즘 로직 구현 - Querydsl 이용하여 '오늘' 등록된 공고와 모든 참여자들의 매칭 조건 비교 * feat: add missing busniess logic for matching algorithms - 기획 반영 (매칭 공고 기준 시각 변경, 참여자 당 최대 10개) * refact: optimize by filtering out null values before processing * feat: define application layers * feat: add spring actuator library to track scheduling jobs well * feat: setting scheduler jobs for successful cron jobs * test: add test codes for EmailMatchUseCaseTest - `EmailMatchUseCaseTest` 테스트 코드 추가 - 기존의 `EmailServiceTest` 삭제 * test: add test codes for GetMatchingExperimentPostsUseCase * refact: update Input type String to LocalDateTime * fix: make UrlGeneratorGateway to prevent from hard-coding URL, to meet the DIP * refact: rename email related usecases * refact: reflect code reviews - `List<<ExperimentPost>?>` 타입을 `List<ExperimentPost>` 리팩터링 - 로그 및 반환 메시지에 기존 이모지 제거 - 테스트의 용이성을 위해, 테스트용 API 구현 * refact: reflect code review - 앞서 못지운 로그, 반환 메시지 이모지 제거 - 테스트용 트리거 주석처리, API 제거 처리 - 쿼리 작성 시 불필요한 문자열 파싱 제거 * fix: fix typo for SendEmailMatchingUseCase * refact: update the codes to meet the DIP - 기존의 postId별 생성된 공고들의 url generate로직을 DIP 원칙에 맞게 개선 * fix: delete emoji to meet the requirements * fix: delete unused comments for scheduler trigger
- Loading branch information
Showing
21 changed files
with
625 additions
and
93 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
30 changes: 22 additions & 8 deletions
30
src/main/kotlin/com/dobby/backend/application/service/EmailService.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,22 +1,36 @@ | ||
package com.dobby.backend.application.service | ||
|
||
import com.dobby.backend.application.usecase.member.email.EmailCodeSendUseCase | ||
import com.dobby.backend.application.usecase.member.email.EmailVerificationUseCase | ||
import com.dobby.backend.application.usecase.member.email.SendEmailCodeUseCase | ||
import com.dobby.backend.application.usecase.member.email.SendMatchingEmailUseCase | ||
import com.dobby.backend.application.usecase.member.email.VerifyEmailUseCase | ||
import com.dobby.backend.application.usecase.member.email.GetMatchingExperimentPostsUseCase | ||
import jakarta.transaction.Transactional | ||
import org.springframework.stereotype.Service | ||
|
||
@Service | ||
class EmailService( | ||
private val emailCodeSendUseCase: EmailCodeSendUseCase, | ||
private val emailVerificationUseCase: EmailVerificationUseCase | ||
private val sendEmailCodeUseCase: SendEmailCodeUseCase, | ||
private val verifyEmailUseCase: VerifyEmailUseCase, | ||
private val sendMatchingEmailUseCase: SendMatchingEmailUseCase, | ||
private val getMatchingExperimentPostsUseCase: GetMatchingExperimentPostsUseCase | ||
) { | ||
@Transactional | ||
fun sendEmail(req: EmailCodeSendUseCase.Input) : EmailCodeSendUseCase.Output{ | ||
return emailCodeSendUseCase.execute(req) | ||
fun sendEmail(req: SendEmailCodeUseCase.Input) : SendEmailCodeUseCase.Output{ | ||
return sendEmailCodeUseCase.execute(req) | ||
} | ||
|
||
@Transactional | ||
fun verifyCode(req: EmailVerificationUseCase.Input) : EmailVerificationUseCase.Output { | ||
return emailVerificationUseCase.execute(req) | ||
fun verifyCode(req: VerifyEmailUseCase.Input) : VerifyEmailUseCase.Output { | ||
return verifyEmailUseCase.execute(req) | ||
} | ||
|
||
@Transactional | ||
fun sendMatchingEmail(req: SendMatchingEmailUseCase.Input): SendMatchingEmailUseCase.Output{ | ||
return sendMatchingEmailUseCase.execute(req) | ||
} | ||
|
||
@Transactional | ||
fun getMatchingInfo(req: GetMatchingExperimentPostsUseCase.Input): GetMatchingExperimentPostsUseCase.Output{ | ||
return getMatchingExperimentPostsUseCase.execute(req) | ||
} | ||
} |
24 changes: 24 additions & 0 deletions
24
...n/com/dobby/backend/application/usecase/member/email/GetMatchingExperimentPostsUseCase.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package com.dobby.backend.application.usecase.member.email | ||
|
||
import com.dobby.backend.application.usecase.UseCase | ||
import com.dobby.backend.domain.gateway.experiment.ExperimentPostGateway | ||
import com.dobby.backend.domain.model.experiment.ExperimentPost | ||
import java.time.LocalDateTime | ||
|
||
class GetMatchingExperimentPostsUseCase( | ||
private val experimentPostGateway: ExperimentPostGateway | ||
): UseCase<GetMatchingExperimentPostsUseCase.Input, GetMatchingExperimentPostsUseCase.Output> { | ||
|
||
data class Input( | ||
val requestTime: LocalDateTime | ||
) | ||
|
||
data class Output( | ||
val matchingPosts: Map<String, List<ExperimentPost>> | ||
) | ||
|
||
override fun execute(input : Input): Output { | ||
val result = experimentPostGateway.findMatchingExperimentPosts() | ||
return Output(matchingPosts = result) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
72 changes: 72 additions & 0 deletions
72
...ain/kotlin/com/dobby/backend/application/usecase/member/email/SendMatchingEmailUseCase.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
package com.dobby.backend.application.usecase.member.email | ||
|
||
import com.dobby.backend.application.usecase.UseCase | ||
import com.dobby.backend.domain.exception.EmailDomainNotFoundException | ||
import com.dobby.backend.domain.gateway.UrlGeneratorGateway | ||
import com.dobby.backend.domain.gateway.email.EmailGateway | ||
import com.dobby.backend.domain.model.experiment.ExperimentPost | ||
import com.dobby.backend.util.EmailUtils | ||
import java.time.LocalDate | ||
import java.time.LocalDateTime | ||
import java.time.format.DateTimeFormatter | ||
|
||
class SendMatchingEmailUseCase( | ||
private val emailGateway: EmailGateway, | ||
private val urlGeneratorGateway: UrlGeneratorGateway | ||
): UseCase<SendMatchingEmailUseCase.Input, SendMatchingEmailUseCase.Output>{ | ||
|
||
data class Input( | ||
val contactEmail: String, | ||
val experimentPosts: List<ExperimentPost>, | ||
val currentDateTime: LocalDateTime | ||
) | ||
|
||
data class Output( | ||
val isSuccess: Boolean, | ||
val message: String | ||
) | ||
|
||
override fun execute(input: Input): Output { | ||
validateEmail(input.contactEmail) | ||
|
||
val (title, content) = getFormattedEmail(input.contactEmail, input.experimentPosts) | ||
|
||
return try { | ||
emailGateway.sendEmail(input.contactEmail, title, content) | ||
Output(isSuccess = true, message = " Email successfully sent to ${input.contactEmail}") | ||
} catch (ex: Exception) { | ||
Output(isSuccess = false, message = "Failed to send to email to ${input.contactEmail}: ${ex.message}") | ||
} | ||
} | ||
|
||
private fun validateEmail(email : String){ | ||
if(!EmailUtils.isDomainExists(email)) throw EmailDomainNotFoundException | ||
} | ||
|
||
private fun getFormattedEmail(memberName: String, experimentPosts: List<ExperimentPost>): Pair<String, String> { | ||
val todayDate = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")) | ||
val emailTitle = "[그라밋🔬] $todayDate 오늘의 추천 실험 공고를 확인해보세요!" | ||
val jobListFormatted = experimentPosts.joinToString("\n\n") { post -> | ||
val postUrl = urlGeneratorGateway.getExperimentPostUrl(postId = post.id) | ||
""" | ||
🔹 **${post.title}** | ||
- 기간: ${post.startDate} ~ ${post.endDate} | ||
- 위치: ${post.univName ?: "공고참고"} | ||
- 보상: ${post.reward} | ||
- [공고 확인하기]($postUrl) | ||
""".trimIndent() | ||
} | ||
|
||
val content = """ | ||
${memberName}님과 꼭 맞는 실험 공고를 찾아왔어요 🧚 | ||
* 자세한 실험 내용이나 모집 대상은 공고 내용을 확인해 주세요. | ||
🔹 **추천 공고 목록** 🔹 | ||
$jobListFormatted | ||
더 많은 공고를 보려면 [그라밋 웹사이트](${urlGeneratorGateway.getBaseUrl()})를 방문해 주세요! | ||
""".trimIndent() | ||
|
||
return Pair(emailTitle, content) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
6 changes: 6 additions & 0 deletions
6
src/main/kotlin/com/dobby/backend/domain/gateway/UrlGeneratorGateway.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package com.dobby.backend.domain.gateway | ||
|
||
interface UrlGeneratorGateway { | ||
fun getBaseUrl(): String | ||
fun getExperimentPostUrl(postId: String): String | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
33 changes: 31 additions & 2 deletions
33
src/main/kotlin/com/dobby/backend/infrastructure/config/SchedulerConfig.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,30 +1,59 @@ | ||
package com.dobby.backend.infrastructure.config | ||
|
||
import com.dobby.backend.infrastructure.scheduler.ExpiredExperimentPostJob | ||
import com.dobby.backend.infrastructure.scheduler.SendMatchingEmailJob | ||
import org.quartz.* | ||
import org.slf4j.Logger | ||
import org.slf4j.LoggerFactory | ||
import org.springframework.context.annotation.Bean | ||
import org.springframework.context.annotation.Configuration | ||
import java.util.* | ||
|
||
@Configuration | ||
class SchedulerConfig { | ||
private val logger: Logger = LoggerFactory.getLogger(SchedulerConfig::class.java) | ||
|
||
@Bean | ||
fun expiredExperimentPostJobDetail(): JobDetail { | ||
logger.info("Registering ExpiredExperimentPostJob...") | ||
return JobBuilder.newJob(ExpiredExperimentPostJob::class.java) | ||
.withIdentity("expired_experiment_post_job") | ||
.withIdentity("expired_experiment_post_job", "DEFAULT") | ||
.storeDurably() | ||
.build() | ||
} | ||
|
||
@Bean | ||
fun expiredExperimentPostTrigger(): Trigger { | ||
logger.info("Registering ExpiredExperimentPostTrigger...") | ||
return TriggerBuilder.newTrigger() | ||
.forJob(expiredExperimentPostJobDetail()) | ||
.withIdentity("expired_experiment_post_trigger") | ||
.withIdentity("expired_experiment_post_trigger", "DEFAULT") | ||
.withSchedule( | ||
CronScheduleBuilder.dailyAtHourAndMinute(0, 0) | ||
.inTimeZone(TimeZone.getTimeZone("Asia/Seoul")) | ||
) | ||
.build() | ||
} | ||
|
||
@Bean | ||
fun sendEmailMatchingJobDetail(): JobDetail { | ||
logger.info("Registering SendEmailMatchingJobDetail...") | ||
return JobBuilder.newJob(SendMatchingEmailJob::class.java) | ||
.withIdentity("matching_email_send_job", "DEFAULT") | ||
.storeDurably() | ||
.build() | ||
} | ||
|
||
@Bean | ||
fun sendEmailMatchingPostTrigger() : Trigger { | ||
logger.info("Registering SendEmailMatchingPostTrigger...") | ||
return TriggerBuilder.newTrigger() | ||
.forJob(sendEmailMatchingJobDetail()) | ||
.withIdentity("send_matching_email_trigger", "DEFAULT") | ||
.withSchedule( | ||
CronScheduleBuilder.dailyAtHourAndMinute(8,0) | ||
.inTimeZone(TimeZone.getTimeZone("Asia/Seoul")) | ||
) | ||
.build() | ||
} | ||
} |
11 changes: 11 additions & 0 deletions
11
src/main/kotlin/com/dobby/backend/infrastructure/config/properties/UrlProperties.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package com.dobby.backend.infrastructure.config.properties | ||
|
||
import org.springframework.boot.context.properties.ConfigurationProperties | ||
import org.springframework.stereotype.Component | ||
|
||
@Component | ||
@ConfigurationProperties(prefix = "swagger") | ||
class UrlProperties ( | ||
var serverUrl : String = "" | ||
) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.