PlatformInsightService.kt
package com.distasilucas.cryptobalancetracker.service
import com.distasilucas.cryptobalancetracker.constants.PLATFORMS_BALANCES_INSIGHTS_CACHE
import com.distasilucas.cryptobalancetracker.constants.PLATFORM_INSIGHTS_CACHE
import com.distasilucas.cryptobalancetracker.entity.Platform
import com.distasilucas.cryptobalancetracker.entity.UserCrypto
import com.distasilucas.cryptobalancetracker.exception.ApiException
import com.distasilucas.cryptobalancetracker.model.response.insights.BalancesChartResponse
import com.distasilucas.cryptobalancetracker.model.response.insights.platform.CryptoInsights
import com.distasilucas.cryptobalancetracker.model.response.insights.platform.PlatformInsightsResponse
import io.github.oshai.kotlinlogging.KotlinLogging
import org.springframework.cache.annotation.Cacheable
import org.springframework.http.HttpStatus
import org.springframework.stereotype.Service
import java.math.BigDecimal
@Service
data class PlatformInsightService(
private val platformService: PlatformService,
private val userCryptoService: UserCryptoService,
private val cryptoService: CryptoService,
private val insightsService: InsightsService
) {
private val logger = KotlinLogging.logger { }
@Cacheable(cacheNames = [PLATFORM_INSIGHTS_CACHE], key = "#platformId")
fun retrievePlatformInsights(platformId: String): PlatformInsightsResponse {
logger.info { "Retrieving insights for platform with id $platformId" }
val userCryptosInPlatform = userCryptoService.findAllByPlatformId(platformId)
if (userCryptosInPlatform.isEmpty()) {
throw ApiException(HttpStatus.NO_CONTENT, "There is no user cryptos in platform")
}
val platform = platformService.retrievePlatformById(platformId)
val cryptosIds = userCryptosInPlatform.map { it.coingeckoCryptoId }
val cryptos = cryptoService.findAllByIds(cryptosIds)
val userCryptosQuantity = insightsService.getUserCryptoQuantity(userCryptosInPlatform)
val totalBalances = insightsService.getTotalBalances(cryptos, userCryptosQuantity)
val cryptosMap = cryptos.associateBy { it.id }
val cryptosInsights = userCryptosInPlatform.map { userCrypto ->
val crypto = cryptosMap[userCrypto.coingeckoCryptoId] ?: throw ApiException("User crypto not found")
val quantity = userCryptosQuantity[userCrypto.coingeckoCryptoId]
val cryptoTotalBalances = insightsService.getCryptoTotalBalances(crypto, quantity!!)
CryptoInsights(
id = userCrypto.id,
cryptoInfo = crypto.toCryptoInfo(),
quantity = quantity.toPlainString(),
percentage = insightsService.calculatePercentage(totalBalances.usd(), cryptoTotalBalances.usd()),
balances = cryptoTotalBalances
)
}.sortedByDescending { it.percentage }
return PlatformInsightsResponse(platform.name, totalBalances, cryptosInsights)
}
@Cacheable(cacheNames = [PLATFORMS_BALANCES_INSIGHTS_CACHE])
fun retrievePlatformsBalancesInsights(): List<BalancesChartResponse> {
logger.info { "Retrieving all platforms balances insights" }
val userCryptos = userCryptoService.findAll()
if (userCryptos.isEmpty()) return emptyList()
val platformsIds = userCryptos.map { it.platformId }.toSet()
val platforms = platformService.findAllByIds(platformsIds)
val userCryptoQuantity = insightsService.getUserCryptoQuantity(userCryptos)
val platformsUserCryptos = getPlatformsUserCryptos(platforms, userCryptos)
val cryptosIds = platformsUserCryptos.values.flatMap { it.map { it.coingeckoCryptoId } }.toSet()
val cryptos = cryptoService.findAllByIds(cryptosIds)
val totalBalances = insightsService.getTotalBalances(cryptos, userCryptoQuantity)
val cryptosMap = cryptos.associateBy { it.id }
return platformsUserCryptos.map { (platformName, userCryptos) ->
var totalUSDBalance = BigDecimal.ZERO
userCryptos.forEach { userCrypto ->
val crypto = cryptosMap[userCrypto.coingeckoCryptoId] ?: throw ApiException("User crypto ${userCrypto.id} not found")
val balance = insightsService.getCryptoTotalBalances(crypto, userCrypto.quantity)
totalUSDBalance = totalUSDBalance.plus(BigDecimal(balance.usd()))
}
val percentage = insightsService.calculatePercentage(totalBalances.usd(), totalUSDBalance.toPlainString())
BalancesChartResponse(platformName, totalUSDBalance, percentage)
}.sortedByDescending { it.percentage }
}
// Map <platform name, List<user cryptos>>
private fun getPlatformsUserCryptos(
platforms: List<Platform>,
userCryptos: List<UserCrypto>
): Map<String, List<UserCrypto>> {
val platformsMap = platforms.associateBy { it.id }
return userCryptos.groupBy { crypto ->
platformsMap[crypto.platformId]?.name ?: throw ApiException("Platform with id ${crypto.platformId} not found")
}
}
}