Skip to content

Commit

Permalink
[Feat/#412] 이메일 OPEN, DELIVERY DELAY 이벤트 처리 (#419)
Browse files Browse the repository at this point in the history
* feat: 전송 아티클 이벤트 테이블 추가 및 필요 enum 추가

* refactor: 배치 전송후 전송 기록 추가

* feat: 전송 아티클 이벤트 추가 및 조회 쿼리 추가

* feat: AddEmailLogUseCase 구현

* feat: ArticleLogService 구현

* feat: ArticleMemberService 구현

* feat: ReadArticleByEmailUseCase 구현

* feat: addEmailLog 구현

* feat: readArticleByEmail 구현

* fix: 요청으로 Byte를 받지 못하는 문제 해결

* feat: 아티클 로그 요청 시큐리티 해제

* fix: execute가 없어서 쿼리가 동작하지 않는 문제 해결

* fix: EmailLogEventType 변환할 때 type의 대소문자 구분하지 않고 변한할 수 있도록 수정

* feat: 로그 기록을 위한 인증 예외 주소 추가

* refactor: 아티클 이메일 전송시 전송 이벤트 기록하도록 수정

* fix: 요청을 처리할 수 있도록 수정

* fix: todo로 예외 발생하는 문제 해결

* fix: eventType이 재대로 저장되지 않는 문제 해결
  • Loading branch information
belljun3395 authored Sep 27, 2024
1 parent 0694864 commit ac38810
Show file tree
Hide file tree
Showing 34 changed files with 532 additions and 13 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.few.api.repo.dao.log

import com.few.api.repo.dao.log.command.InsertEventCommand
import com.few.api.repo.dao.log.query.SelectEventByMessageIdAndEventTypeQuery
import com.few.api.repo.dao.log.record.SendArticleEventHistoryRecord
import jooq.jooq_dsl.tables.SendArticleEventHistory
import org.jooq.DSLContext
import org.springframework.stereotype.Repository

@Repository
class SendArticleEventHistoryDao(
private val dslContext: DSLContext,
) {

fun insertEvent(command: InsertEventCommand) {
dslContext.insertInto(SendArticleEventHistory.SEND_ARTICLE_EVENT_HISTORY)
.set(SendArticleEventHistory.SEND_ARTICLE_EVENT_HISTORY.MEMBER_ID, command.memberId)
.set(SendArticleEventHistory.SEND_ARTICLE_EVENT_HISTORY.ARTICLE_ID, command.articleId)
.set(SendArticleEventHistory.SEND_ARTICLE_EVENT_HISTORY.MESSAGE_ID, command.messageId)
.set(SendArticleEventHistory.SEND_ARTICLE_EVENT_HISTORY.EVENT_TYPE_CD, command.eventType)
.set(SendArticleEventHistory.SEND_ARTICLE_EVENT_HISTORY.SEND_TYPE_CD, command.sendType)
.execute()
}

fun selectEventByMessageId(query: SelectEventByMessageIdAndEventTypeQuery): SendArticleEventHistoryRecord? {
return dslContext.select(
SendArticleEventHistory.SEND_ARTICLE_EVENT_HISTORY.MEMBER_ID.`as`(
SendArticleEventHistoryRecord::memberId.name
),
SendArticleEventHistory.SEND_ARTICLE_EVENT_HISTORY.ARTICLE_ID.`as`(
SendArticleEventHistoryRecord::articleId.name
),
SendArticleEventHistory.SEND_ARTICLE_EVENT_HISTORY.MESSAGE_ID.`as`(
SendArticleEventHistoryRecord::messageId.name
),
SendArticleEventHistory.SEND_ARTICLE_EVENT_HISTORY.EVENT_TYPE_CD.`as`(
SendArticleEventHistoryRecord::eventType.name
),
SendArticleEventHistory.SEND_ARTICLE_EVENT_HISTORY.SEND_TYPE_CD.`as`(
SendArticleEventHistoryRecord::sendType.name
)
)
.from(SendArticleEventHistory.SEND_ARTICLE_EVENT_HISTORY)
.where(SendArticleEventHistory.SEND_ARTICLE_EVENT_HISTORY.MESSAGE_ID.eq(query.messageId))
.and(SendArticleEventHistory.SEND_ARTICLE_EVENT_HISTORY.EVENT_TYPE_CD.eq(query.eventType))
.fetchOne()?.into(SendArticleEventHistoryRecord::class.java)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.few.api.repo.dao.log.command

data class InsertEventCommand(
val memberId: Long,
val articleId: Long,
val messageId: String,
val eventType: Byte,
val sendType: Byte,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.few.api.repo.dao.log.query

data class SelectEventByMessageIdAndEventTypeQuery(
val messageId: String,
val eventType: Byte,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.few.api.repo.dao.log.record

data class SendArticleEventHistoryRecord(
val memberId: Long,
val articleId: Long,
val messageId: String,
val eventType: Byte,
val sendType: Byte,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.few.api.domain.article.service

import com.few.api.domain.article.service.dto.InsertOpenEventDto
import com.few.api.domain.article.service.dto.SelectDeliveryEventByMessageIdDto
import com.few.api.repo.dao.log.SendArticleEventHistoryDao
import com.few.api.repo.dao.log.command.InsertEventCommand
import com.few.api.repo.dao.log.query.SelectEventByMessageIdAndEventTypeQuery
import com.few.api.repo.dao.log.record.SendArticleEventHistoryRecord
import org.springframework.stereotype.Service

@Service
class ArticleLogService(
private val sendArticleEventHistoryDao: SendArticleEventHistoryDao,
) {

fun selectDeliveryEventByMessageId(dto: SelectDeliveryEventByMessageIdDto): SendArticleEventHistoryRecord? {
return sendArticleEventHistoryDao.selectEventByMessageId(
SelectEventByMessageIdAndEventTypeQuery(
dto.messageId,
dto.eventType
)
)
}

fun insertOpenEvent(dto: InsertOpenEventDto) {
sendArticleEventHistoryDao.insertEvent(
InsertEventCommand(
memberId = dto.memberId,
articleId = dto.articleId,
messageId = dto.messageId,
eventType = dto.eventType,
sendType = dto.sendType
)
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.few.api.domain.article.service

import com.few.api.domain.article.service.dto.ReadMemberByEmailDto
import com.few.api.repo.dao.member.MemberDao
import com.few.api.repo.dao.member.query.SelectMemberByEmailQuery
import org.springframework.stereotype.Service

@Service
class ArticleMemberService(
private val memberDao: MemberDao,
) {

fun readMemberByEmail(dto: ReadMemberByEmailDto): Long? {
return memberDao.selectMemberByEmail(
SelectMemberByEmailQuery(dto.email)
)?.memberId
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.few.api.domain.article.service.dto

data class InsertOpenEventDto(
val memberId: Long,
val articleId: Long,
val messageId: String,
val eventType: Byte,
val sendType: Byte,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.few.api.domain.article.service.dto

data class ReadMemberByEmailDto(
val email: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.few.api.domain.article.service.dto

data class SelectDeliveryEventByMessageIdDto(
val messageId: String,
val eventType: Byte,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.few.api.domain.article.usecase

import com.few.api.domain.article.service.ArticleLogService
import com.few.api.domain.article.service.ArticleMemberService
import com.few.api.domain.article.service.dto.InsertOpenEventDto
import com.few.api.domain.article.service.dto.ReadMemberByEmailDto
import com.few.api.domain.article.service.dto.SelectDeliveryEventByMessageIdDto
import com.few.api.domain.article.usecase.dto.ReadArticleByEmailUseCaseIn
import com.few.api.web.support.EmailLogEventType
import org.springframework.stereotype.Component
import org.springframework.transaction.annotation.Transactional
import org.webjars.NotFoundException

@Component
class ReadArticleByEmailUseCase(
private val memberService: ArticleMemberService,
private val articleLogService: ArticleLogService,
) {

@Transactional
fun execute(useCaseIn: ReadArticleByEmailUseCaseIn) {
val memberId =
memberService.readMemberByEmail(ReadMemberByEmailDto(useCaseIn.destination[0]))
?: throw NotFoundException("member.notfound.email")

val record =
articleLogService.selectDeliveryEventByMessageId(
SelectDeliveryEventByMessageIdDto(
useCaseIn.messageId,
EmailLogEventType.DELIVERY.code
)
)
?: throw IllegalStateException("event is not found")

articleLogService.insertOpenEvent(
InsertOpenEventDto(
memberId = memberId,
articleId = record.articleId,
messageId = record.messageId,
eventType = EmailLogEventType.OPEN.code,
sendType = record.sendType
)
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.few.api.domain.article.usecase.dto

import com.few.api.web.support.EmailLogEventType
import com.few.api.web.support.SendType

data class ReadArticleByEmailUseCaseIn(
val messageId: String,
val destination: List<String>,
val eventType: EmailLogEventType,
val sendType: SendType,
)
46 changes: 46 additions & 0 deletions api/src/main/kotlin/com/few/api/domain/log/AddEmailLogUseCase.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.few.api.domain.log

import com.few.api.domain.log.dto.AddEmailLogUseCaseIn
import com.few.api.repo.dao.log.SendArticleEventHistoryDao
import com.few.api.repo.dao.log.command.InsertEventCommand
import com.few.api.repo.dao.log.query.SelectEventByMessageIdAndEventTypeQuery
import com.few.api.repo.dao.member.MemberDao
import com.few.api.repo.dao.member.query.SelectMemberByEmailQuery
import com.few.api.web.support.EmailLogEventType
import org.springframework.stereotype.Component
import org.springframework.transaction.annotation.Transactional
import org.webjars.NotFoundException

@Component
class AddEmailLogUseCase(
private val memberDao: MemberDao,
private val sendArticleEventHistoryDao: SendArticleEventHistoryDao,
) {
@Transactional
fun execute(useCaseIn: AddEmailLogUseCaseIn) {
val (memberId, _, _, _) = memberDao.selectMemberByEmail(
SelectMemberByEmailQuery(useCaseIn.destination[0])
) ?: throw NotFoundException("member.notfound.email")

val record =
sendArticleEventHistoryDao.selectEventByMessageId(
SelectEventByMessageIdAndEventTypeQuery(
useCaseIn.messageId,
EmailLogEventType.SEND.code
)
)
?: throw IllegalStateException("event is not found")

sendArticleEventHistoryDao.insertEvent(
InsertEventCommand(
memberId = memberId,
articleId = record.articleId,
messageId = record.messageId,
eventType = useCaseIn.eventType.code,
sendType = record.sendType
)
).let {
// TODO("다른 이벤트는 필요시 추가한다.")
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.few.api.domain.log.dto

import com.few.api.web.support.EmailLogEventType
import java.time.LocalDateTime

data class AddEmailLogUseCaseIn(
val eventType: EmailLogEventType,
val messageId: String,
val destination: List<String>,
val mailTimestamp: LocalDateTime,
)
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,14 @@ package com.few.api.domain.subscription.handler
import com.few.api.config.DatabaseAccessThreadPoolConfig.Companion.DATABASE_ACCESS_POOL
import com.few.api.domain.common.lock.LockFor
import com.few.api.domain.common.lock.LockIdentifier
import com.few.api.domain.subscription.service.SubscriptionArticleService
import com.few.api.domain.subscription.service.SubscriptionMemberService
import com.few.api.domain.subscription.service.SubscriptionEmailService
import com.few.api.domain.subscription.service.SubscriptionWorkbookService
import com.few.api.domain.subscription.service.*
import com.few.api.domain.subscription.service.dto.*
import com.few.api.exception.common.NotFoundException
import com.few.api.repo.dao.subscription.SubscriptionDao
import com.few.api.repo.dao.subscription.command.UpdateArticleProgressCommand
import com.few.api.repo.dao.subscription.command.UpdateLastArticleProgressCommand
import com.few.api.repo.dao.subscription.query.SelectSubscriptionQuery
import com.few.api.web.support.SendType
import com.few.data.common.code.CategoryType
import com.few.email.service.article.dto.Content
import io.github.oshai.kotlinlogging.KotlinLogging
Expand All @@ -27,6 +25,7 @@ class SendWorkbookArticleAsyncHandler(
private val memberService: SubscriptionMemberService,
private val articleService: SubscriptionArticleService,
private val workbookService: SubscriptionWorkbookService,
private val subscriptionLogService: SubscriptionLogService,
private val subscriptionDao: SubscriptionDao,
private val emailService: SubscriptionEmailService,
) {
Expand Down Expand Up @@ -80,8 +79,19 @@ class SendWorkbookArticleAsyncHandler(
)
)

runCatching { emailService.sendArticleEmail(sendArticleInDto) }
runCatching {
emailService.sendArticleEmail(sendArticleInDto)
}
.onSuccess {
subscriptionLogService.insertSendEvent(
InsertSendEventDto(
memberId = memberId,
articleId = article.id,
messageId = it,
sendType = SendType.AWSSES.code
)
)

val lastDayArticleId =
workbookService.readWorkbookLastArticleId(
ReadWorkbookLastArticleIdInDto(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ class SubscriptionEmailService(
private const val ARTICLE_TEMPLATE = "article"
}

fun sendArticleEmail(dto: SendArticleInDto) {
sendArticleEmailService.send(
fun sendArticleEmail(dto: SendArticleInDto): String {
return sendArticleEmailService.send(
SendArticleEmailArgs(
dto.toEmail,
ARTICLE_SUBJECT_TEMPLATE.format(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.few.api.domain.subscription.service

import com.few.api.domain.subscription.service.dto.InsertSendEventDto
import com.few.api.repo.dao.log.SendArticleEventHistoryDao
import com.few.api.repo.dao.log.command.InsertEventCommand
import com.few.api.web.support.EmailLogEventType
import org.springframework.stereotype.Service

@Service
class SubscriptionLogService(
private val sendArticleEventHistoryDao: SendArticleEventHistoryDao,
) {

fun insertSendEvent(dto: InsertSendEventDto) {
sendArticleEventHistoryDao.insertEvent(
InsertEventCommand(
memberId = dto.memberId,
articleId = dto.articleId,
messageId = dto.messageId,
eventType = EmailLogEventType.SEND.code,
sendType = dto.sendType
)
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.few.api.domain.subscription.service.dto

data class InsertSendEventDto(
val memberId: Long,
val articleId: Long,
val messageId: String,
val sendType: Byte,
)
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,9 @@ class WebSecurityConfig(

/** 어드민 */
AntPathRequestMatcher("/api/v1/admin/**", HttpMethod.POST.name()),
AntPathRequestMatcher("/api/v1/articles/views", HttpMethod.POST.name()),
AntPathRequestMatcher("/api/v1/logs", HttpMethod.POST.name()),
AntPathRequestMatcher("/api/v1/logs/email/articles", HttpMethod.POST.name()),
AntPathRequestMatcher("/batch/**"),

/** 인증 불필요 */
Expand Down Expand Up @@ -188,6 +190,8 @@ class WebSecurityConfig(

/** 어드민 */
AntPathRequestMatcher("/api/v1/admin/**", HttpMethod.POST.name()),
AntPathRequestMatcher("/api/v1/articles/views", HttpMethod.POST.name()),
AntPathRequestMatcher("/api/v1/logs/email/articles", HttpMethod.POST.name()),
AntPathRequestMatcher("/api/v1/logs", HttpMethod.POST.name()),
AntPathRequestMatcher("/batch/**"),

Expand Down
Loading

0 comments on commit ac38810

Please sign in to comment.