Skip to content

Commit

Permalink
release v1.1.10
Browse files Browse the repository at this point in the history
  • Loading branch information
DongGeon0908 committed Aug 12, 2024
2 parents 4b9a814 + dcd642c commit a1d2192
Show file tree
Hide file tree
Showing 10 changed files with 148 additions and 56 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ class EnvelopeFacade(
customCategory = customCategory
).run { categoryAssignmentService.saveSync(this) }

publisher.publishEvent(CreateEnvelopeEvent(createdEnvelope, ledger))
publisher.publishEvent(CreateEnvelopeEvent(createdEnvelope, ledger, user))

createdEnvelope
}
Expand Down Expand Up @@ -183,7 +183,7 @@ class EnvelopeFacade(
this.customCategory = customCategory
}.run { categoryAssignmentService.saveSync(this) }

publisher.publishEvent(UpdateEnvelopeEvent(updatedEnvelope))
publisher.publishEvent(UpdateEnvelopeEvent(updatedEnvelope, user))

updatedEnvelope
}
Expand Down Expand Up @@ -226,7 +226,7 @@ class EnvelopeFacade(
targetType = CategoryAssignmentType.ENVELOPE
)

DeleteEnvelopeEvent(envelope, user.uid)
DeleteEnvelopeEvent(envelope, user)
.run { publisher.publishEvent(this) }
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ import com.oksusu.susu.api.event.model.DeleteEnvelopeEvent
import com.oksusu.susu.api.event.model.UpdateEnvelopeEvent
import com.oksusu.susu.api.friend.application.FriendRelationshipService
import com.oksusu.susu.api.friend.application.FriendService
import com.oksusu.susu.api.statistic.application.UserEnvelopeStatisticService
import com.oksusu.susu.client.common.coroutine.ErrorPublishingCoroutineExceptionHandler
import com.oksusu.susu.common.extension.mdcCoroutineScope
import com.oksusu.susu.common.extension.parZipWithMDC
import com.oksusu.susu.domain.envelope.domain.vo.EnvelopeType
import com.oksusu.susu.domain.envelope.infrastructure.LedgerRepository
import io.github.oshai.kotlinlogging.KotlinLogging
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
Expand All @@ -27,9 +29,13 @@ class EnvelopeEventListener(
private val ledgerRepository: LedgerRepository,
private val friendRelationshipService: FriendRelationshipService,
private val coroutineExceptionHandler: ErrorPublishingCoroutineExceptionHandler,
private val userEnvelopeStatisticService: UserEnvelopeStatisticService,
) {
private val logger = KotlinLogging.logger { }

@TransactionalEventListener
fun handle(event: CreateEnvelopeEvent) {
/** 장부 처리 집계 변경 */
event.ledger?.let { ledger ->
when (event.envelope.type) {
EnvelopeType.RECEIVED -> {
Expand All @@ -43,6 +49,20 @@ class EnvelopeEventListener(

ledgerService.saveSync(ledger)
}

/** 통계 캐싱 처리 */
mdcCoroutineScope(Dispatchers.IO + Job() + coroutineExceptionHandler.handler, event.traceId).launch {
/** 통계 캐싱 안되어있으면 중단 */
userEnvelopeStatisticService.getStatisticOrNull(event.user.uid) ?: run { return@launch }

logger.info { "[${event.publishAt}] ${event.user.uid} refresh user envelope statistic 시작" }

val statistic = userEnvelopeStatisticService.createUserEnvelopeStatistic(event.user.uid)

userEnvelopeStatisticService.save(event.user.uid, statistic)

logger.info { "[${event.publishAt}] ${event.user.uid} refresh user envelope statistic 끝" }
}
}

@TransactionalEventListener
Expand All @@ -62,13 +82,27 @@ class EnvelopeEventListener(
}
}
}

/** 통계 캐싱 처리 */
mdcCoroutineScope(Dispatchers.IO + Job() + coroutineExceptionHandler.handler, event.traceId).launch {
/** 통계 캐싱 안되어있으면 중단 */
userEnvelopeStatisticService.getStatisticOrNull(event.user.uid) ?: run { return@launch }

logger.info { "[${event.publishAt}] ${event.user.uid} refresh user envelope statistic 시작" }

val statistic = userEnvelopeStatisticService.createUserEnvelopeStatistic(event.user.uid)

userEnvelopeStatisticService.save(event.user.uid, statistic)

logger.info { "[${event.publishAt}] ${event.user.uid} refresh user envelope statistic 끝" }
}
}

@TransactionalEventListener
fun handel(event: DeleteEnvelopeEvent) {
mdcCoroutineScope(Dispatchers.IO + Job() + coroutineExceptionHandler.handler, event.traceId).launch {
val count = envelopeService.countByUidAndFriendId(
uid = event.uid,
uid = event.user.uid,
friendId = event.envelope.friendId
)

Expand All @@ -92,5 +126,19 @@ class EnvelopeEventListener(
friendRelationshipService.deleteByFriendIdSync(event.envelope.friendId)
}
}

/** 통계 캐싱 처리 */
mdcCoroutineScope(Dispatchers.IO + Job() + coroutineExceptionHandler.handler, event.traceId).launch {
/** 통계 캐싱 안되어있으면 중단 */
userEnvelopeStatisticService.getStatisticOrNull(event.user.uid) ?: run { return@launch }

logger.info { "[${event.publishAt}] ${event.user.uid} refresh user envelope statistic 시작" }

val statistic = userEnvelopeStatisticService.createUserEnvelopeStatistic(event.user.uid)

userEnvelopeStatisticService.save(event.user.uid, statistic)

logger.info { "[${event.publishAt}] ${event.user.uid} refresh user envelope statistic 끝" }
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,28 @@ class SystemActionLogEventListener(
@EventListener
fun subscribe(event: SystemActionLogEvent) {
mdcCoroutineScope(Dispatchers.IO + Job() + coroutineExceptionHandler.handler, event.traceId).launch {
SystemActionLog(
ipAddress = event.ipAddress,
path = event.path,
httpMethod = event.method,
userAgent = event.userAgent,
host = event.host,
referer = event.referer,
extra = event.extra
).run { systemActionLogService.record(this) }
if (check(event)) {
SystemActionLog(
ipAddress = event.ipAddress,
path = event.path,
httpMethod = event.method,
userAgent = event.userAgent,
host = event.host,
referer = event.referer,
extra = event.extra
).run { systemActionLogService.record(this) }
}
}
}

private fun check(event: SystemActionLogEvent): Boolean {
return !NON_TARGET_PATH.contains(event.path)
}

companion object {
private val NON_TARGET_PATH = setOf(
"/api/v1/health",
"/health"
)
}
}
5 changes: 4 additions & 1 deletion api/src/main/kotlin/com/oksusu/susu/api/event/model/Event.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.oksusu.susu.api.event.model

import com.oksusu.susu.api.auth.model.AuthUser
import com.oksusu.susu.api.extension.remoteIp
import com.oksusu.susu.cache.model.OidcPublicKeysCacheModel
import com.oksusu.susu.cache.statistic.domain.UserEnvelopeStatistic
Expand Down Expand Up @@ -103,15 +104,17 @@ data class CacheUserEnvelopeStatisticEvent(
data class CreateEnvelopeEvent(
val envelope: Envelope,
val ledger: Ledger?,
val user: AuthUser,
) : BaseEvent()

data class UpdateEnvelopeEvent(
val envelope: Envelope,
val user: AuthUser,
) : BaseEvent()

data class DeleteEnvelopeEvent(
val envelope: Envelope,
val uid: Long,
val user: AuthUser,
) : BaseEvent()

data class CreateUserWithdrawEvent(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,10 @@ class HealthResource(
fun healthCheckV1() = environment.activeProfiles.first()
.run { HealthResponse.from(this) }
.wrapOk()

@Operation(summary = "health-check")
@GetMapping("/health")
fun healthCheck() = environment.activeProfiles.first()
.run { HealthResponse.from(this) }
.wrapOk()
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,9 @@ import com.oksusu.susu.api.statistic.model.response.UserEnvelopeStatisticRespons
import com.oksusu.susu.api.statistic.model.vo.SusuEnvelopeStatisticRequest
import com.oksusu.susu.cache.statistic.domain.UserEnvelopeStatistic
import com.oksusu.susu.common.extension.parZipWithMDC
import com.oksusu.susu.common.extension.yearMonth
import io.github.oshai.kotlinlogging.KotlinLogging
import org.springframework.context.ApplicationEventPublisher
import org.springframework.stereotype.Service
import java.time.LocalDate

@Service
class StatisticFacade(
Expand All @@ -22,7 +20,6 @@ class StatisticFacade(
private val susuEnvelopeStatisticService: SusuEnvelopeStatisticService,
private val susuSpecificEnvelopeStatisticService: SusuSpecificEnvelopeStatisticService,
private val relationshipService: RelationshipService,
private val envelopeStatisticService: EnvelopeStatisticService,
private val eventPublisher: ApplicationEventPublisher,
) {
private val logger = KotlinLogging.logger { }
Expand All @@ -35,53 +32,29 @@ class StatisticFacade(
return UserEnvelopeStatisticResponse.from(this)
}

val userEnvelopeStatistic = parZipWithMDC(
/** 최근 사용 금액 1년 */
{ envelopeStatisticService.getRecentSpentFor1Year(user.uid) },
/** 최다 수수 관계 */
{ envelopeStatisticService.getMostFrequentRelationship(user.uid) },
/** 최다 수수 경조사 */
{ envelopeStatisticService.getMostFrequentCategory(user.uid) },
/** 가장 많이 받은 금액 */
{ envelopeStatisticService.getMaxReceivedEnvelope(user.uid) },
/** 가장 많이 보낸 금액 */
{ envelopeStatisticService.getMaxSentEnvelope(user.uid) }
) {
recentSpent,
mostFrequentRelationShip,
mostFrequentCategory,
maxReceivedEnvelope,
maxSentEnvelope,
->
val userEnvelopeStatistic = createUserEnvelopeStatistic(user.uid)

/** 최근 사용 금액 8달 */
val before8Month = LocalDate.now().minusMonths(7).yearMonth()
val recentSpentForLast8Months = recentSpent?.filter { spent ->
spent.title >= before8Month
}
return UserEnvelopeStatisticResponse.from(userEnvelopeStatistic)
}

/** 경조사비 가장 많이 쓴 달 */
val mostSpentMonth = recentSpent?.maxBy { model -> model.value }?.title?.substring(4)?.toLong()
suspend fun refreshUserEnvelopeStatistic(user: AuthUser): UserEnvelopeStatisticResponse {
val userEnvelopeStatistic = createUserEnvelopeStatistic(user.uid)

UserEnvelopeStatistic(
recentSpent = recentSpentForLast8Months,
mostSpentMonth = mostSpentMonth,
mostFrequentRelationShip = mostFrequentRelationShip,
mostFrequentCategory = mostFrequentCategory,
maxReceivedEnvelope = maxReceivedEnvelope,
maxSentEnvelope = maxSentEnvelope
)
}
return UserEnvelopeStatisticResponse.from(userEnvelopeStatistic)
}

private suspend fun createUserEnvelopeStatistic(uid: Long): UserEnvelopeStatistic {
val userEnvelopeStatistic = userEnvelopeStatisticService.createUserEnvelopeStatistic(uid)

/** 유저 봉투 통계 캐싱 */
eventPublisher.publishEvent(
CacheUserEnvelopeStatisticEvent(
uid = user.uid,
uid = uid,
statistic = userEnvelopeStatistic
)
)

return UserEnvelopeStatisticResponse.from(userEnvelopeStatistic)
return userEnvelopeStatistic
}

suspend fun getSusuEnvelopeStatistic(requestParam: SusuEnvelopeStatisticRequest): SusuEnvelopeStatisticResponse {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,58 @@ package com.oksusu.susu.api.statistic.application

import com.oksusu.susu.cache.statistic.domain.UserEnvelopeStatistic
import com.oksusu.susu.cache.statistic.infrastructure.UserEnvelopeStatisticRepository
import com.oksusu.susu.common.extension.parZipWithMDC
import com.oksusu.susu.common.extension.withMDCContext
import com.oksusu.susu.common.extension.yearMonth
import kotlinx.coroutines.Dispatchers
import org.springframework.stereotype.Service
import java.time.LocalDate

@Service
class UserEnvelopeStatisticService(
private val userEnvelopeStatisticRepository: UserEnvelopeStatisticRepository,
private val envelopeStatisticService: EnvelopeStatisticService,
) {
suspend fun createUserEnvelopeStatistic(uid: Long): UserEnvelopeStatistic {
return parZipWithMDC(
/** 최근 사용 금액 1년 */
{ envelopeStatisticService.getRecentSpentFor1Year(uid) },
/** 최다 수수 관계 */
{ envelopeStatisticService.getMostFrequentRelationship(uid) },
/** 최다 수수 경조사 */
{ envelopeStatisticService.getMostFrequentCategory(uid) },
/** 가장 많이 받은 금액 */
{ envelopeStatisticService.getMaxReceivedEnvelope(uid) },
/** 가장 많이 보낸 금액 */
{ envelopeStatisticService.getMaxSentEnvelope(uid) }
) {
recentSpent,
mostFrequentRelationShip,
mostFrequentCategory,
maxReceivedEnvelope,
maxSentEnvelope,
->

/** 최근 사용 금액 8달 */
val before8Month = LocalDate.now().minusMonths(7).yearMonth()
val recentSpentForLast8Months = recentSpent?.filter { spent ->
spent.title >= before8Month
}

/** 경조사비 가장 많이 쓴 달 */
val mostSpentMonth = recentSpent?.maxBy { model -> model.value }?.title?.substring(4)?.toLong()

UserEnvelopeStatistic(
recentSpent = recentSpentForLast8Months,
mostSpentMonth = mostSpentMonth,
mostFrequentRelationShip = mostFrequentRelationShip,
mostFrequentCategory = mostFrequentCategory,
maxReceivedEnvelope = maxReceivedEnvelope,
maxSentEnvelope = maxSentEnvelope
)
}
}

suspend fun save(uid: Long, userEnvelopeStatistic: UserEnvelopeStatistic) {
return withMDCContext(Dispatchers.IO) {
userEnvelopeStatisticRepository.save(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ class StatisticResource(
user: AuthUser,
) = statisticFacade.getUserEnvelopeStatistic(user).wrapOk()

@Operation(summary = "나의 통계 새로고침")
@GetMapping("/mine/envelope/refresh")
suspend fun refreshUserEnvelopeStatistic(
user: AuthUser,
) = statisticFacade.refreshUserEnvelopeStatistic(user).wrapOk()

@Operation(summary = "수수 통계")
@GetMapping("/susu/envelope")
suspend fun getSusuEnvelopeStatistic(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import io.github.oshai.kotlinlogging.KotlinLogging
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.springframework.scheduling.annotation.Scheduled
import org.springframework.stereotype.Component

@Component
Expand All @@ -17,7 +16,7 @@ class UserScheduler(
) {
private val logger = KotlinLogging.logger { }

@Scheduled(cron = "0 0 3 * * *")
// @Scheduled(cron = "0 0 3 * * *")
fun deleteWithdrawUserData() {
CoroutineScope(Dispatchers.IO + coroutineExceptionHandler.handler).launch {
runCatching {
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
version=1.1.9
version=1.1.10
kotlin.code.style=official

0 comments on commit a1d2192

Please sign in to comment.