From 46efe3c65557ffb93ebc4f11d4cccc0579e73e94 Mon Sep 17 00:00:00 2001 From: min96 Date: Sat, 6 Apr 2024 19:51:28 +0900 Subject: [PATCH 01/38] feat: save attribute and findOrCreate --- build.gradle.kts | 8 +-- .../com/api/nft/controller/dto/NftResponse.kt | 10 +++ .../com/api/nft/domain/attribute/Attribute.kt | 16 +++++ .../domain/attribute/AttributeRepository.kt | 6 ++ .../nft/domain/{ => collection}/Collection.kt | 4 +- .../repository}/CollectionRepository.kt | 3 +- .../CollectionRepositorySupport.kt | 3 +- .../CollectionRepositorySupportImpl.kt | 3 +- .../api/nft/domain/{ => metadata}/Metadata.kt | 4 +- .../repository}/MetadataRepository.kt | 3 +- .../com/api/nft/domain/{ => nft}/Nft.kt | 4 +- .../domain/nft/repository/NftMetadataDto.kt | 13 ++++ .../domain/nft/repository/NftRepository.kt | 12 ++++ .../nft/repository/NftRepositorySupport.kt | 7 ++ .../repository/NftRepositorySupportImpl.kt | 42 ++++++++++++ .../nft/domain/repository/NftRepository.kt | 10 --- .../com/api/nft/service/AttributeService.kt | 26 +++++++ .../com/api/nft/service/CollectionService.kt | 4 +- .../com/api/nft/service/MetadataService.kt | 25 +++++++ .../kotlin/com/api/nft/service/NftService.kt | 68 +++++++++++-------- .../service/{ => external}/dto/NftResponse.kt | 2 +- .../moralis}/MoralisApiService.kt | 19 +++++- .../com/api/nft/service/metadataService.kt | 8 +-- src/main/resources/application.yml | 4 +- .../migration/V1__Initial_schema.sql | 8 +++ src/test/kotlin/com/api/nft/NftTest.kt | 25 +++++-- 26 files changed, 268 insertions(+), 69 deletions(-) create mode 100644 src/main/kotlin/com/api/nft/controller/dto/NftResponse.kt create mode 100644 src/main/kotlin/com/api/nft/domain/attribute/Attribute.kt create mode 100644 src/main/kotlin/com/api/nft/domain/attribute/AttributeRepository.kt rename src/main/kotlin/com/api/nft/domain/{ => collection}/Collection.kt (66%) rename src/main/kotlin/com/api/nft/domain/{ => collection/repository}/CollectionRepository.kt (73%) rename src/main/kotlin/com/api/nft/domain/{ => collection/repository}/CollectionRepositorySupport.kt (58%) rename src/main/kotlin/com/api/nft/domain/{ => collection/repository}/CollectionRepositorySupportImpl.kt (78%) rename src/main/kotlin/com/api/nft/domain/{ => metadata}/Metadata.kt (81%) rename src/main/kotlin/com/api/nft/domain/{ => metadata/repository}/MetadataRepository.kt (61%) rename src/main/kotlin/com/api/nft/domain/{ => nft}/Nft.kt (87%) create mode 100644 src/main/kotlin/com/api/nft/domain/nft/repository/NftMetadataDto.kt create mode 100644 src/main/kotlin/com/api/nft/domain/nft/repository/NftRepository.kt create mode 100644 src/main/kotlin/com/api/nft/domain/nft/repository/NftRepositorySupport.kt create mode 100644 src/main/kotlin/com/api/nft/domain/nft/repository/NftRepositorySupportImpl.kt delete mode 100644 src/main/kotlin/com/api/nft/domain/repository/NftRepository.kt create mode 100644 src/main/kotlin/com/api/nft/service/AttributeService.kt create mode 100644 src/main/kotlin/com/api/nft/service/MetadataService.kt rename src/main/kotlin/com/api/nft/service/{ => external}/dto/NftResponse.kt (98%) rename src/main/kotlin/com/api/nft/service/{ => external/moralis}/MoralisApiService.kt (73%) diff --git a/build.gradle.kts b/build.gradle.kts index 0428938..39977bd 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -6,6 +6,8 @@ plugins { id("org.flywaydb.flyway") version "9.21.1" kotlin("jvm") version "1.9.23" kotlin("plugin.spring") version "1.9.23" + kotlin("plugin.lombok") version "1.9.23" + id("io.freefair.lombok") version "8.1.0" } group = "com.api" @@ -21,7 +23,6 @@ repositories { dependencies { implementation("org.springframework.boot:spring-boot-starter-batch") - implementation("org.springframework.boot:spring-boot-starter-jooq") implementation("org.springframework.boot:spring-boot-starter-quartz") implementation("org.springframework.boot:spring-boot-starter-data-r2dbc") @@ -36,9 +37,8 @@ dependencies { implementation("org.jetbrains.kotlin:kotlin-reflect") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor") -// implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.13.0") -// implementation("com.fasterxml.jackson.core:jackson-databind:2.13.0") - +// implementation("com.querydsl:querydsl-sql:4.3.1") +// implementation("com.infobip:infobip-spring-data-r2dbc-querydsl-boot-starter:6.2.0") testImplementation("org.springframework.boot:spring-boot-starter-test") testImplementation("io.projectreactor:reactor-test") diff --git a/src/main/kotlin/com/api/nft/controller/dto/NftResponse.kt b/src/main/kotlin/com/api/nft/controller/dto/NftResponse.kt new file mode 100644 index 0000000..f82587b --- /dev/null +++ b/src/main/kotlin/com/api/nft/controller/dto/NftResponse.kt @@ -0,0 +1,10 @@ +package com.api.nft.controller.dto + +data class NftResponse( + val id: Long, + val collectionName: String, + val tokenId: String, + val tokenAddress: String, + val image: String, // metadata + val value: String, // attribute +) diff --git a/src/main/kotlin/com/api/nft/domain/attribute/Attribute.kt b/src/main/kotlin/com/api/nft/domain/attribute/Attribute.kt new file mode 100644 index 0000000..1080d33 --- /dev/null +++ b/src/main/kotlin/com/api/nft/domain/attribute/Attribute.kt @@ -0,0 +1,16 @@ +package com.api.nft.domain.attribute + +import org.springframework.data.annotation.Id +import org.springframework.data.relational.core.mapping.Table +import lombok.AllArgsConstructor + +@Table("attribute") +@AllArgsConstructor +class Attribute( + @Id val id: Long? = null, + val nftId : Long, + val traitType : String? = null, + val value: String? = null, +) { + +} \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/domain/attribute/AttributeRepository.kt b/src/main/kotlin/com/api/nft/domain/attribute/AttributeRepository.kt new file mode 100644 index 0000000..8542b5a --- /dev/null +++ b/src/main/kotlin/com/api/nft/domain/attribute/AttributeRepository.kt @@ -0,0 +1,6 @@ +package com.api.nft.domain.attribute + +import org.springframework.data.repository.reactive.ReactiveCrudRepository + +interface AttributeRepository : ReactiveCrudRepository { +} \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/domain/Collection.kt b/src/main/kotlin/com/api/nft/domain/collection/Collection.kt similarity index 66% rename from src/main/kotlin/com/api/nft/domain/Collection.kt rename to src/main/kotlin/com/api/nft/domain/collection/Collection.kt index 8c9aca4..da39772 100644 --- a/src/main/kotlin/com/api/nft/domain/Collection.kt +++ b/src/main/kotlin/com/api/nft/domain/collection/Collection.kt @@ -1,9 +1,11 @@ -package com.api.nft.domain +package com.api.nft.domain.collection import org.springframework.data.annotation.Id import org.springframework.data.relational.core.mapping.Table +import lombok.AllArgsConstructor @Table("collection") +@AllArgsConstructor class Collection( @Id val name: String ) { diff --git a/src/main/kotlin/com/api/nft/domain/CollectionRepository.kt b/src/main/kotlin/com/api/nft/domain/collection/repository/CollectionRepository.kt similarity index 73% rename from src/main/kotlin/com/api/nft/domain/CollectionRepository.kt rename to src/main/kotlin/com/api/nft/domain/collection/repository/CollectionRepository.kt index 014d2db..8db6a3f 100644 --- a/src/main/kotlin/com/api/nft/domain/CollectionRepository.kt +++ b/src/main/kotlin/com/api/nft/domain/collection/repository/CollectionRepository.kt @@ -1,5 +1,6 @@ -package com.api.nft.domain +package com.api.nft.domain.collection.repository +import com.api.nft.domain.collection.Collection import org.springframework.data.repository.reactive.ReactiveCrudRepository import reactor.core.publisher.Mono diff --git a/src/main/kotlin/com/api/nft/domain/CollectionRepositorySupport.kt b/src/main/kotlin/com/api/nft/domain/collection/repository/CollectionRepositorySupport.kt similarity index 58% rename from src/main/kotlin/com/api/nft/domain/CollectionRepositorySupport.kt rename to src/main/kotlin/com/api/nft/domain/collection/repository/CollectionRepositorySupport.kt index 4744224..e5ef1b7 100644 --- a/src/main/kotlin/com/api/nft/domain/CollectionRepositorySupport.kt +++ b/src/main/kotlin/com/api/nft/domain/collection/repository/CollectionRepositorySupport.kt @@ -1,5 +1,6 @@ -package com.api.nft.domain +package com.api.nft.domain.collection.repository +import com.api.nft.domain.collection.Collection import reactor.core.publisher.Mono interface CollectionRepositorySupport { diff --git a/src/main/kotlin/com/api/nft/domain/CollectionRepositorySupportImpl.kt b/src/main/kotlin/com/api/nft/domain/collection/repository/CollectionRepositorySupportImpl.kt similarity index 78% rename from src/main/kotlin/com/api/nft/domain/CollectionRepositorySupportImpl.kt rename to src/main/kotlin/com/api/nft/domain/collection/repository/CollectionRepositorySupportImpl.kt index 2b68941..eace44a 100644 --- a/src/main/kotlin/com/api/nft/domain/CollectionRepositorySupportImpl.kt +++ b/src/main/kotlin/com/api/nft/domain/collection/repository/CollectionRepositorySupportImpl.kt @@ -1,5 +1,6 @@ -package com.api.nft.domain +package com.api.nft.domain.collection.repository +import com.api.nft.domain.collection.Collection import org.springframework.data.r2dbc.core.R2dbcEntityTemplate import reactor.core.publisher.Mono diff --git a/src/main/kotlin/com/api/nft/domain/Metadata.kt b/src/main/kotlin/com/api/nft/domain/metadata/Metadata.kt similarity index 81% rename from src/main/kotlin/com/api/nft/domain/Metadata.kt rename to src/main/kotlin/com/api/nft/domain/metadata/Metadata.kt index 59ca3e9..f881e9d 100644 --- a/src/main/kotlin/com/api/nft/domain/Metadata.kt +++ b/src/main/kotlin/com/api/nft/domain/metadata/Metadata.kt @@ -1,10 +1,12 @@ -package com.api.nft.domain +package com.api.nft.domain.metadata import org.springframework.data.annotation.Id import org.springframework.data.relational.core.mapping.Column import org.springframework.data.relational.core.mapping.Table +import lombok.AllArgsConstructor @Table("metadata") +@AllArgsConstructor class Metadata( @Id val id: Long? = null, @Column("nft_id") val nftId: Long, diff --git a/src/main/kotlin/com/api/nft/domain/MetadataRepository.kt b/src/main/kotlin/com/api/nft/domain/metadata/repository/MetadataRepository.kt similarity index 61% rename from src/main/kotlin/com/api/nft/domain/MetadataRepository.kt rename to src/main/kotlin/com/api/nft/domain/metadata/repository/MetadataRepository.kt index bcecb94..096ddad 100644 --- a/src/main/kotlin/com/api/nft/domain/MetadataRepository.kt +++ b/src/main/kotlin/com/api/nft/domain/metadata/repository/MetadataRepository.kt @@ -1,5 +1,6 @@ -package com.api.nft.domain +package com.api.nft.domain.metadata.repository +import com.api.nft.domain.metadata.Metadata import org.springframework.data.repository.reactive.ReactiveCrudRepository interface MetadataRepository: ReactiveCrudRepository { diff --git a/src/main/kotlin/com/api/nft/domain/Nft.kt b/src/main/kotlin/com/api/nft/domain/nft/Nft.kt similarity index 87% rename from src/main/kotlin/com/api/nft/domain/Nft.kt rename to src/main/kotlin/com/api/nft/domain/nft/Nft.kt index 61256d4..67c2f5e 100644 --- a/src/main/kotlin/com/api/nft/domain/Nft.kt +++ b/src/main/kotlin/com/api/nft/domain/nft/Nft.kt @@ -1,10 +1,12 @@ -package com.api.nft.domain +package com.api.nft.domain.nft +import lombok.AllArgsConstructor import org.springframework.data.annotation.Id import org.springframework.data.relational.core.mapping.Column import org.springframework.data.relational.core.mapping.Table @Table("nft") +@AllArgsConstructor class Nft( @Id val id: Long? = null, @Column("token_id") val tokenId: String, diff --git a/src/main/kotlin/com/api/nft/domain/nft/repository/NftMetadataDto.kt b/src/main/kotlin/com/api/nft/domain/nft/repository/NftMetadataDto.kt new file mode 100644 index 0000000..a59edb8 --- /dev/null +++ b/src/main/kotlin/com/api/nft/domain/nft/repository/NftMetadataDto.kt @@ -0,0 +1,13 @@ +package com.api.nft.domain.nft.repository + +import org.springframework.data.relational.core.mapping.Column + +data class NftMetadataDto( + val id: Long, + val tokenId: String, + val tokenAddress: String, + val chinType: String, + val nftName: String, + val collectionName: String, + val image: String, +) diff --git a/src/main/kotlin/com/api/nft/domain/nft/repository/NftRepository.kt b/src/main/kotlin/com/api/nft/domain/nft/repository/NftRepository.kt new file mode 100644 index 0000000..e385a01 --- /dev/null +++ b/src/main/kotlin/com/api/nft/domain/nft/repository/NftRepository.kt @@ -0,0 +1,12 @@ +package com.api.nft.domain.nft.repository + +import com.api.nft.domain.nft.Nft +import org.springframework.data.repository.reactive.ReactiveCrudRepository +import reactor.core.publisher.Mono + +interface NftRepository : NftRepositorySupport, ReactiveCrudRepository { + + fun findByTokenAddressAndTokenId(tokenAddress: String, tokenId: String): Mono + + +} \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/domain/nft/repository/NftRepositorySupport.kt b/src/main/kotlin/com/api/nft/domain/nft/repository/NftRepositorySupport.kt new file mode 100644 index 0000000..5a78a96 --- /dev/null +++ b/src/main/kotlin/com/api/nft/domain/nft/repository/NftRepositorySupport.kt @@ -0,0 +1,7 @@ +package com.api.nft.domain.nft.repository + +import reactor.core.publisher.Mono + +interface NftRepositorySupport { + fun findByNftJoinMetadata(nftId: Long) : Mono +} \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/domain/nft/repository/NftRepositorySupportImpl.kt b/src/main/kotlin/com/api/nft/domain/nft/repository/NftRepositorySupportImpl.kt new file mode 100644 index 0000000..d7f06f2 --- /dev/null +++ b/src/main/kotlin/com/api/nft/domain/nft/repository/NftRepositorySupportImpl.kt @@ -0,0 +1,42 @@ +package com.api.nft.domain.nft.repository + +import org.springframework.data.r2dbc.core.R2dbcEntityTemplate +import reactor.core.publisher.Mono + +class NftRepositorySupportImpl( + private val r2dbcEntityTemplate: R2dbcEntityTemplate +): NftRepositorySupport { + override fun findByNftJoinMetadata(nftId: Long): Mono { + val query = + """ + SELECT + n.id, + n.token_id AS tokenId, + n.token_address AS tokenAddress, + n.chain_type AS chinType, + n.nft_name AS nftName, + n.collection_name AS collectionName, + m.image AS image + FROM + nft n + JOIN + metadata m ON n.id = m.nft_id + WHERE + n.id = :$1 + + """ + return r2dbcEntityTemplate.databaseClient.sql(query) + .bind(0, nftId) + .map { row, data -> + NftMetadataDto( + id = (row.get("id") as Number).toLong(), + tokenId = row.get("tokenId", String::class.java)!!, + tokenAddress = row.get("tokenAddress", String::class.java)!!, + chinType = row.get("chinType", String::class.java)!!, + nftName = row.get("nftName", String::class.java)!!, + collectionName = row.get("collectionName", String::class.java)!!, + image = row.get("image", String::class.java) ?: "" + ) + }.first() + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/domain/repository/NftRepository.kt b/src/main/kotlin/com/api/nft/domain/repository/NftRepository.kt deleted file mode 100644 index 01cfc33..0000000 --- a/src/main/kotlin/com/api/nft/domain/repository/NftRepository.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.api.nft.domain.repository - -import com.api.nft.domain.Nft -import org.springframework.data.repository.reactive.ReactiveCrudRepository -import reactor.core.publisher.Mono - -interface NftRepository : ReactiveCrudRepository { - - fun findByTokenAddressAndTokenId(tokenADdress: String, tokenId: String): Mono -} \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/service/AttributeService.kt b/src/main/kotlin/com/api/nft/service/AttributeService.kt new file mode 100644 index 0000000..dcb347c --- /dev/null +++ b/src/main/kotlin/com/api/nft/service/AttributeService.kt @@ -0,0 +1,26 @@ +package com.api.nft.service + +import com.api.nft.domain.attribute.Attribute +import com.api.nft.domain.attribute.AttributeRepository +import com.api.nft.service.external.dto.AttributeResponse +import org.springframework.stereotype.Service +import reactor.core.publisher.Flux +import reactor.core.publisher.Mono + +@Service +class AttributeService( + private val attributeRepository: AttributeRepository, +) { + + fun createAttribute(nftId: Long, attributes: List): Flux { + return Flux.fromIterable(attributes).flatMap { + attributeRepository.save( + Attribute( + nftId = nftId, + traitType = it.traitType ?: null, + value = it.value ?: null + ) + ) + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/service/CollectionService.kt b/src/main/kotlin/com/api/nft/service/CollectionService.kt index 75850a9..b685df0 100644 --- a/src/main/kotlin/com/api/nft/service/CollectionService.kt +++ b/src/main/kotlin/com/api/nft/service/CollectionService.kt @@ -1,7 +1,7 @@ package com.api.nft.service -import com.api.nft.domain.Collection -import com.api.nft.domain.CollectionRepository +import com.api.nft.domain.collection.Collection +import com.api.nft.domain.collection.repository.CollectionRepository import org.springframework.stereotype.Service import reactor.core.publisher.Mono diff --git a/src/main/kotlin/com/api/nft/service/MetadataService.kt b/src/main/kotlin/com/api/nft/service/MetadataService.kt new file mode 100644 index 0000000..6fd3e85 --- /dev/null +++ b/src/main/kotlin/com/api/nft/service/MetadataService.kt @@ -0,0 +1,25 @@ +package com.api.nft.service + +import com.api.nft.domain.metadata.Metadata +import com.api.nft.domain.metadata.repository.MetadataRepository +import com.api.nft.service.external.dto.MetadataResponse +import org.springframework.stereotype.Service +import reactor.core.publisher.Mono + +@Service +class MetadataService( + private val metadataRepository: MetadataRepository, +) { + + fun createMetadata(nftId: Long, metadata: MetadataResponse): Mono { + return metadataRepository.save( + Metadata( + nftId = nftId, + description = metadata.description, + image = MetadataResponse.parseImage(metadata.image), + animationUrl = metadata.animationUrl, + ) + ) + + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/service/NftService.kt b/src/main/kotlin/com/api/nft/service/NftService.kt index 08285db..3d33953 100644 --- a/src/main/kotlin/com/api/nft/service/NftService.kt +++ b/src/main/kotlin/com/api/nft/service/NftService.kt @@ -1,14 +1,15 @@ package com.api.nft.service -import com.api.nft.domain.Metadata -import com.api.nft.domain.MetadataRepository -import com.api.nft.domain.Nft -import com.api.nft.domain.repository.NftRepository +import com.api.nft.domain.nft.Nft +import com.api.nft.domain.nft.repository.NftMetadataDto +import com.api.nft.domain.nft.repository.NftRepository import com.api.nft.enums.ChainType -import com.api.nft.service.dto.AttributeResponse -import com.api.nft.service.dto.MetadataResponse -import com.api.nft.service.dto.NftResponse +import com.api.nft.service.external.dto.AttributeResponse +import com.api.nft.service.external.dto.MetadataResponse +import com.api.nft.service.external.dto.NftResponse +import com.api.nft.service.external.moralis.MoralisApiService import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional import reactor.core.publisher.Mono @Service @@ -16,28 +17,34 @@ class NftService( private val moralisApiService: MoralisApiService, private val nftRepository: NftRepository, private val collectionService: CollectionService, - private val metadataService: metadataService, + private val metadataService: MetadataService, + private val attributeService: AttributeService, ) { -// fun findOrCreateNft(tokenId: String, tokenAddress: String, chainType: ChainType) { -// nftRepository.findByTokenAddressAndTokenId(tokenAddress,tokenId) -// .switchIfEmpty( -// createNftProcess() -//// getNftByMoralis(tokenId,tokenAddress,chainType) -//// // 이 후 nft외래키를 가지고 metadata 생성 -//// // 또 그 이후 nft외래키를 가지고 attribute 생성 -// ) -// } -// + @Transactional + fun findOrCreateNft(tokenId: String, tokenAddress: String, chainType: ChainType): Mono { + return nftRepository.findByTokenAddressAndTokenId(tokenAddress,tokenId) + .switchIfEmpty( + createNftProcess(tokenId,tokenAddress,chainType) + ).flatMap { + nftRepository.findByNftJoinMetadata(it.id!!) + } + } + fun createNftProcess(tokenId: String, - tokenAddress: String, - chainType: ChainType - ): Mono { + tokenAddress: String, + chainType: ChainType + ): Mono { val response = getNftByMoralis(tokenId, tokenAddress, chainType) - return response.flatMap { - createNft(it.first,it.second,it.third,chainType) // 생성되고 밑에 flatMap 실행 async - .flatMap {nft -> - metadataService.createMetadata(nft.id!!,it.second) + return response.flatMap { (nftResponse, metadataResponse, attributeResponseList) -> + createNft(nftResponse, metadataResponse, chainType) + .flatMap { nft -> + metadataService.createMetadata(nft.id!!, metadataResponse) + .thenMany(attributeService.createAttribute( + nft.id, + attributeResponseList ?: emptyList() + )) + .then(Mono.just(nft)) } } } @@ -46,19 +53,20 @@ class NftService( tokenAddress: String, chainType: ChainType ) - : Mono>> { + : Mono?>> { return moralisApiService.getNft(tokenAddress,tokenId,chainType) .filter { !it.possibleSpam } .map { val metadata = MetadataResponse.toMetadataResponse(it.metadata) - val attribute = AttributeResponse.toAttributeResponse(metadata.attributes) - Triple(it,metadata,attribute) + val attributes = if (metadata != null && metadata.attributes.isNotEmpty()) + AttributeResponse.toAttributeResponse(metadata.attributes) + else null + Triple(it,metadata,attributes) } } fun createNft(nft: NftResponse, metadata: MetadataResponse, - attribute: List, chainType: ChainType ): Mono { return collectionService.findOrCreate(nft.name).flatMap { @@ -72,7 +80,7 @@ class NftService( ownerOf = nft.ownerOf, tokenHash = nft.tokenHash, amount = nft.amount.toInt() - ) + ) ) } } diff --git a/src/main/kotlin/com/api/nft/service/dto/NftResponse.kt b/src/main/kotlin/com/api/nft/service/external/dto/NftResponse.kt similarity index 98% rename from src/main/kotlin/com/api/nft/service/dto/NftResponse.kt rename to src/main/kotlin/com/api/nft/service/external/dto/NftResponse.kt index 9963c6e..aa941e0 100644 --- a/src/main/kotlin/com/api/nft/service/dto/NftResponse.kt +++ b/src/main/kotlin/com/api/nft/service/external/dto/NftResponse.kt @@ -1,4 +1,4 @@ -package com.api.nft.service.dto +package com.api.nft.service.external.dto import com.fasterxml.jackson.annotation.JsonIgnoreProperties import com.fasterxml.jackson.annotation.JsonProperty diff --git a/src/main/kotlin/com/api/nft/service/MoralisApiService.kt b/src/main/kotlin/com/api/nft/service/external/moralis/MoralisApiService.kt similarity index 73% rename from src/main/kotlin/com/api/nft/service/MoralisApiService.kt rename to src/main/kotlin/com/api/nft/service/external/moralis/MoralisApiService.kt index cadb2a3..7425b51 100644 --- a/src/main/kotlin/com/api/nft/service/MoralisApiService.kt +++ b/src/main/kotlin/com/api/nft/service/external/moralis/MoralisApiService.kt @@ -1,7 +1,7 @@ -package com.api.nft.service +package com.api.nft.service.external.moralis import com.api.nft.enums.ChainType -import com.api.nft.service.dto.NftResponse +import com.api.nft.service.external.dto.NftResponse import org.springframework.http.MediaType import org.springframework.stereotype.Service @@ -41,6 +41,21 @@ class MoralisApiService { .bodyToMono(NftResponse::class.java) } + fun getNft1(tokenAddress: String,tokenId: String,chainType: ChainType): Mono { + val chain = queryParamByChain(chainType) + + return webClient.get() + .uri { + it.path("/v2.2/nft/${tokenAddress}/${tokenId}") + it.queryParam("chain", chain) + it.build() + } + .header("X-API-Key", apiKey) + .header("Accept", MediaType.APPLICATION_JSON_VALUE) + .retrieve() + .bodyToMono(String::class.java) + } + companion object { private val apiKey = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJub25jZSI6ImJiNmIxMWJmLWNmNzItNDg0OC04OGEyLTBjYTIwODRjN2VhMyIsIm9yZ0lkIjoiMzgzODQwIiwidXNlcklkIjoiMzk0NDAyIiwidHlwZUlkIjoiMGZlYWQ5NDctZjQwZS00MDkwLWFlNGUtOTA1ZTdmMjUxZTAzIiwidHlwZSI6IlBST0pFQ1QiLCJpYXQiOjE3MTA5NTIwMjYsImV4cCI6NDg2NjcxMjAyNn0.VQE60IPGiWxdp7jKLF0jzXnxrLjEpU56H4bnfhMt0Sw" private val baseUrl = "https://deep-index.moralis.io/api" diff --git a/src/main/kotlin/com/api/nft/service/metadataService.kt b/src/main/kotlin/com/api/nft/service/metadataService.kt index 09bda8c..6fd3e85 100644 --- a/src/main/kotlin/com/api/nft/service/metadataService.kt +++ b/src/main/kotlin/com/api/nft/service/metadataService.kt @@ -1,13 +1,13 @@ package com.api.nft.service -import com.api.nft.domain.Metadata -import com.api.nft.domain.MetadataRepository -import com.api.nft.service.dto.MetadataResponse +import com.api.nft.domain.metadata.Metadata +import com.api.nft.domain.metadata.repository.MetadataRepository +import com.api.nft.service.external.dto.MetadataResponse import org.springframework.stereotype.Service import reactor.core.publisher.Mono @Service -class metadataService( +class MetadataService( private val metadataRepository: MetadataRepository, ) { diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index ba73cda..39b9a32 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -17,5 +17,5 @@ spring: logging: level: org.springframework.r2dbc: debug - io.r2dbc.postgresql: DEBUG - root: info +# io.r2dbc.postgresql: DEBUG +# root: info diff --git a/src/main/resources/db/postgresql/migration/V1__Initial_schema.sql b/src/main/resources/db/postgresql/migration/V1__Initial_schema.sql index 77eef69..5d7042d 100644 --- a/src/main/resources/db/postgresql/migration/V1__Initial_schema.sql +++ b/src/main/resources/db/postgresql/migration/V1__Initial_schema.sql @@ -24,6 +24,14 @@ CREATE TABLE IF NOT EXISTS metadata ( ); +CREATE TABLE IF NOt EXISTS attribute ( + id SERIAL PRIMARY KEY, + nft_id BIGINT REFERENCES nft(id), + trait_type varchar(255), + value varchar(255) +); + + diff --git a/src/test/kotlin/com/api/nft/NftTest.kt b/src/test/kotlin/com/api/nft/NftTest.kt index 2f81ee6..656cfbd 100644 --- a/src/test/kotlin/com/api/nft/NftTest.kt +++ b/src/test/kotlin/com/api/nft/NftTest.kt @@ -1,9 +1,9 @@ package com.api.nft -import com.api.nft.domain.Collection -import com.api.nft.domain.CollectionRepository +import com.api.nft.domain.collection.Collection +import com.api.nft.domain.collection.repository.CollectionRepository import com.api.nft.enums.ChainType -import com.api.nft.service.MoralisApiService +import com.api.nft.service.external.moralis.MoralisApiService import com.api.nft.service.NftService import org.junit.jupiter.api.Test import org.springframework.beans.factory.annotation.Autowired @@ -18,17 +18,20 @@ class NftTest( @Test fun test(){ - val tokenAddress = "0xe7900239e9332060dc975ed6f0cc5f0129d924cf" - val tokenId = "3" + val tokenAddress = "0x57a133561c74c242a0b70af9c129126244b4869f" + val tokenId = "4733" val res =moralisApiService.getNft(tokenAddress,tokenId, ChainType.POLYGON_MAINNET).block() println(res) } @Test fun test1() { - val tokenAddress = "0xe7900239e9332060dc975ed6f0cc5f0129d924cf" - val tokenId = "3" + val tokenAddress = "0x57a133561c74c242a0b70af9c129126244b4869f" + val tokenId = "4733" val res =nftService.getNftByMoralis(tokenId,tokenAddress, ChainType.POLYGON_MAINNET).block() + println(res?.first.toString()) + println(res?.second.toString()) + println(res?.third.toString()) } @Test @@ -43,4 +46,12 @@ class NftTest( val tokenName = "asdasdasd" collectionRepository.insert(Collection(tokenName)).block() } + + @Test + fun test4() { + val tokenAddress = "0xe4a8bfdc0684f62b4cfb43165021814f059819ef" + val tokenId = "4617" + val res = nftService.findOrCreateNft(tokenId,tokenAddress,ChainType.POLYGON_MAINNET).block() + println(res.toString()) + } } \ No newline at end of file From 3d7a205f0f5f045072b8fdedc7a930ed5424bb01 Mon Sep 17 00:00:00 2001 From: min96 Date: Mon, 8 Apr 2024 01:12:37 +0900 Subject: [PATCH 02/38] feat: api-batch --- build.gradle.kts | 3 --- .../com/api/nft/controller/NftController.kt | 17 ++++++++++++++++- .../api/nft/controller/dto/NftBatchRequest.kt | 9 +++++++++ .../kotlin/com/api/nft/service/NftService.kt | 9 +++++++++ .../api/nft/service/external/dto/NftResponse.kt | 5 +++-- src/main/resources/application.yml | 3 ++- 6 files changed, 39 insertions(+), 7 deletions(-) create mode 100644 src/main/kotlin/com/api/nft/controller/dto/NftBatchRequest.kt diff --git a/build.gradle.kts b/build.gradle.kts index 39977bd..0598de6 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -37,9 +37,6 @@ dependencies { implementation("org.jetbrains.kotlin:kotlin-reflect") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor") -// implementation("com.querydsl:querydsl-sql:4.3.1") -// implementation("com.infobip:infobip-spring-data-r2dbc-querydsl-boot-starter:6.2.0") - testImplementation("org.springframework.boot:spring-boot-starter-test") testImplementation("io.projectreactor:reactor-test") testImplementation("org.springframework.batch:spring-batch-test") diff --git a/src/main/kotlin/com/api/nft/controller/NftController.kt b/src/main/kotlin/com/api/nft/controller/NftController.kt index 3bd1217..f9a8d9c 100644 --- a/src/main/kotlin/com/api/nft/controller/NftController.kt +++ b/src/main/kotlin/com/api/nft/controller/NftController.kt @@ -1,10 +1,25 @@ package com.api.nft.controller +import com.api.nft.controller.dto.NftBatchRequest +import com.api.nft.controller.dto.NftResponse +import com.api.nft.domain.nft.repository.NftMetadataDto +import com.api.nft.service.NftService +import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.RequestBody import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RestController +import reactor.core.publisher.Flux +import reactor.core.publisher.Mono @RestController @RequestMapping("/v1/nft") -class NftController { +class NftController( + private val nftService: NftService, +) { + @PostMapping("/batch") + fun bath(@RequestBody requests: List): Flux { + return nftService.getBatchNftList(requests) + + } } \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/controller/dto/NftBatchRequest.kt b/src/main/kotlin/com/api/nft/controller/dto/NftBatchRequest.kt new file mode 100644 index 0000000..7d0d6cf --- /dev/null +++ b/src/main/kotlin/com/api/nft/controller/dto/NftBatchRequest.kt @@ -0,0 +1,9 @@ +package com.api.nft.controller.dto + +import com.api.nft.enums.ChainType + +data class NftBatchRequest( + val tokenId: String, + val tokenAddress: String, + val chainType: ChainType, +) \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/service/NftService.kt b/src/main/kotlin/com/api/nft/service/NftService.kt index 3d33953..32ba4e5 100644 --- a/src/main/kotlin/com/api/nft/service/NftService.kt +++ b/src/main/kotlin/com/api/nft/service/NftService.kt @@ -1,5 +1,6 @@ package com.api.nft.service +import com.api.nft.controller.dto.NftBatchRequest import com.api.nft.domain.nft.Nft import com.api.nft.domain.nft.repository.NftMetadataDto import com.api.nft.domain.nft.repository.NftRepository @@ -10,6 +11,7 @@ import com.api.nft.service.external.dto.NftResponse import com.api.nft.service.external.moralis.MoralisApiService import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional +import reactor.core.publisher.Flux import reactor.core.publisher.Mono @Service @@ -21,6 +23,13 @@ class NftService( private val attributeService: AttributeService, ) { + fun getBatchNftList(reqeusts : List): Flux { + return Flux.fromIterable(reqeusts) + .flatMap { + findOrCreateNft(it.tokenId,it.tokenAddress,it.chainType) + } + } + @Transactional fun findOrCreateNft(tokenId: String, tokenAddress: String, chainType: ChainType): Mono { return nftRepository.findByTokenAddressAndTokenId(tokenAddress,tokenId) diff --git a/src/main/kotlin/com/api/nft/service/external/dto/NftResponse.kt b/src/main/kotlin/com/api/nft/service/external/dto/NftResponse.kt index aa941e0..e2b0c9b 100644 --- a/src/main/kotlin/com/api/nft/service/external/dto/NftResponse.kt +++ b/src/main/kotlin/com/api/nft/service/external/dto/NftResponse.kt @@ -33,8 +33,9 @@ data class MetadataResponse( val description: String, val image: String, @JsonProperty("animation_url") val animationUrl: String?, - val attributes: List>, - @JsonIgnoreProperties(ignoreUnknown = true) @JsonProperty("external_url") val externalUrl: String? = null + val attributes: List> = emptyList(), + @JsonIgnoreProperties(ignoreUnknown = true) @JsonProperty("external_url") val externalUrl: String? = null, + @JsonIgnoreProperties(ignoreUnknown = true) @JsonProperty("youtube_url") val youtubeUrl: String? = null, ){ companion object { fun toMetadataResponse(metadata: String): MetadataResponse { diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 39b9a32..dfbd9d4 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -13,9 +13,10 @@ spring: username: nft password: nft - logging: level: org.springframework.r2dbc: debug # io.r2dbc.postgresql: DEBUG # root: info +server: + port: 8082 From 291c2d2f220051fe17ca0b50e6fb5a433b035b37 Mon Sep 17 00:00:00 2001 From: min-96 Date: Tue, 23 Apr 2024 18:57:18 +0900 Subject: [PATCH 03/38] fix: getNfts --- .../com/api/nft/controller/NftController.kt | 21 ++++-- src/main/kotlin/com/api/nft/domain/nft/Nft.kt | 2 +- .../domain/nft/repository/NftMetadataDto.kt | 2 +- .../nft/repository/NftRepositorySupport.kt | 2 + .../repository/NftRepositorySupportImpl.kt | 47 +++++++++++-- src/main/kotlin/com/api/nft/enums/Enums.kt | 5 ++ .../com/api/nft/service/AttributeService.kt | 5 +- .../com/api/nft/service/CollectionService.kt | 10 ++- .../kotlin/com/api/nft/service/NftService.kt | 70 +++++++++---------- .../nft/service/external/dto/NftResponse.kt | 14 ++-- .../external/moralis/MoralisApiService.kt | 6 +- .../com/api/nft/service/metadataService.kt | 6 +- src/main/kotlin/com/api/nft/util/Util.kt | 14 ++++ .../migration/V1__Initial_schema.sql | 2 +- src/test/kotlin/com/api/nft/NftTest.kt | 46 ++++++------ 15 files changed, 161 insertions(+), 91 deletions(-) create mode 100644 src/main/kotlin/com/api/nft/util/Util.kt diff --git a/src/main/kotlin/com/api/nft/controller/NftController.kt b/src/main/kotlin/com/api/nft/controller/NftController.kt index f9a8d9c..fdccda0 100644 --- a/src/main/kotlin/com/api/nft/controller/NftController.kt +++ b/src/main/kotlin/com/api/nft/controller/NftController.kt @@ -1,25 +1,34 @@ package com.api.nft.controller -import com.api.nft.controller.dto.NftBatchRequest -import com.api.nft.controller.dto.NftResponse import com.api.nft.domain.nft.repository.NftMetadataDto +import com.api.nft.enums.ChainType import com.api.nft.service.NftService +import com.api.nft.service.external.dto.NftData +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.RequestBody import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RestController import reactor.core.publisher.Flux -import reactor.core.publisher.Mono +//TODO("responseEntity로 반환) @RestController @RequestMapping("/v1/nft") class NftController( private val nftService: NftService, ) { - @PostMapping("/batch") - fun bath(@RequestBody requests: List): Flux { - return nftService.getBatchNftList(requests) + @PostMapping("/save/{chainType}") + fun save(@PathVariable chainType: ChainType,@RequestBody requests: List): Flux { + println("들어오는건 맞나요?") + return nftService.saveNfts(requests,chainType) } + + @GetMapping + fun getAllByIds(@RequestParam nftIds: List): Flux { + return nftService.findAllById(nftIds) + } } \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/domain/nft/Nft.kt b/src/main/kotlin/com/api/nft/domain/nft/Nft.kt index 67c2f5e..61637df 100644 --- a/src/main/kotlin/com/api/nft/domain/nft/Nft.kt +++ b/src/main/kotlin/com/api/nft/domain/nft/Nft.kt @@ -12,8 +12,8 @@ class Nft( @Column("token_id") val tokenId: String, @Column("token_address") val tokenAddress: String, @Column("chain_type") val chinType: String, + @Column("contract_type") val contractType: String, @Column("nft_name")val nftName: String, - @Column("owner_of") val ownerOf: String?, @Column("token_hash")val tokenHash: String?, val collectionName: String, val amount: Int, diff --git a/src/main/kotlin/com/api/nft/domain/nft/repository/NftMetadataDto.kt b/src/main/kotlin/com/api/nft/domain/nft/repository/NftMetadataDto.kt index a59edb8..cd5e6f9 100644 --- a/src/main/kotlin/com/api/nft/domain/nft/repository/NftMetadataDto.kt +++ b/src/main/kotlin/com/api/nft/domain/nft/repository/NftMetadataDto.kt @@ -1,11 +1,11 @@ package com.api.nft.domain.nft.repository -import org.springframework.data.relational.core.mapping.Column data class NftMetadataDto( val id: Long, val tokenId: String, val tokenAddress: String, + val contractType: String, val chinType: String, val nftName: String, val collectionName: String, diff --git a/src/main/kotlin/com/api/nft/domain/nft/repository/NftRepositorySupport.kt b/src/main/kotlin/com/api/nft/domain/nft/repository/NftRepositorySupport.kt index 5a78a96..8fbe5a2 100644 --- a/src/main/kotlin/com/api/nft/domain/nft/repository/NftRepositorySupport.kt +++ b/src/main/kotlin/com/api/nft/domain/nft/repository/NftRepositorySupport.kt @@ -1,7 +1,9 @@ package com.api.nft.domain.nft.repository +import reactor.core.publisher.Flux import reactor.core.publisher.Mono interface NftRepositorySupport { fun findByNftJoinMetadata(nftId: Long) : Mono + fun findAllByNftJoinMetadata(nftIds: List) : Flux } \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/domain/nft/repository/NftRepositorySupportImpl.kt b/src/main/kotlin/com/api/nft/domain/nft/repository/NftRepositorySupportImpl.kt index d7f06f2..6759fa9 100644 --- a/src/main/kotlin/com/api/nft/domain/nft/repository/NftRepositorySupportImpl.kt +++ b/src/main/kotlin/com/api/nft/domain/nft/repository/NftRepositorySupportImpl.kt @@ -1,12 +1,13 @@ package com.api.nft.domain.nft.repository import org.springframework.data.r2dbc.core.R2dbcEntityTemplate +import reactor.core.publisher.Flux import reactor.core.publisher.Mono class NftRepositorySupportImpl( private val r2dbcEntityTemplate: R2dbcEntityTemplate ): NftRepositorySupport { - override fun findByNftJoinMetadata(nftId: Long): Mono { + override fun findByNftJoinMetadata(id: Long): Mono { val query = """ SELECT @@ -15,7 +16,8 @@ class NftRepositorySupportImpl( n.token_address AS tokenAddress, n.chain_type AS chinType, n.nft_name AS nftName, - n.collection_name AS collectionName, + n.collection_name AS collectionName, + n.contract_type AS contractType, m.image AS image FROM nft n @@ -26,7 +28,7 @@ class NftRepositorySupportImpl( """ return r2dbcEntityTemplate.databaseClient.sql(query) - .bind(0, nftId) + .bind(0, id) .map { row, data -> NftMetadataDto( id = (row.get("id") as Number).toLong(), @@ -35,8 +37,45 @@ class NftRepositorySupportImpl( chinType = row.get("chinType", String::class.java)!!, nftName = row.get("nftName", String::class.java)!!, collectionName = row.get("collectionName", String::class.java)!!, - image = row.get("image", String::class.java) ?: "" + image = row.get("image", String::class.java) ?: "", + contractType = row.get("contractType", String::class.java)!!, ) }.first() } + + override fun findAllByNftJoinMetadata(ids: List): Flux { + val query = """ + SELECT + n.id, + n.token_id AS tokenId, + n.token_address AS tokenAddress, + n.chain_type AS chainType, + n.nft_name AS nftName, + n.collection_name AS collectionName, + n.contract_type AS contractType, + m.image AS image + FROM + nft n + JOIN + metadata m ON n.id = m.nft_id + WHERE + n.id IN (:$1) + """ + return r2dbcEntityTemplate.databaseClient.sql(query) + .bind(0, ids) + .map { row, metadata -> + NftMetadataDto( + id = (row.get("id") as Number).toLong(), + tokenId = row.get("tokenId", String::class.java)!!, + tokenAddress = row.get("tokenAddress", String::class.java)!!, + chinType = row.get("chainType", String::class.java)!!, + nftName = row.get("nftName", String::class.java)!!, + collectionName = row.get("collectionName", String::class.java)!!, + image = row.get("image", String::class.java) ?: "", + contractType = row.get("contractType", String::class.java)!!, + ) + } + .all() + } + } \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/enums/Enums.kt b/src/main/kotlin/com/api/nft/enums/Enums.kt index dfae135..00f502e 100644 --- a/src/main/kotlin/com/api/nft/enums/Enums.kt +++ b/src/main/kotlin/com/api/nft/enums/Enums.kt @@ -7,3 +7,8 @@ enum class ChainType{ ETHREUM_SEPOLIA, POLYGON_MUMBAI, } + +enum class NetworkType{ + ETHEREUM, + POLYGON, +} diff --git a/src/main/kotlin/com/api/nft/service/AttributeService.kt b/src/main/kotlin/com/api/nft/service/AttributeService.kt index dcb347c..3f32aac 100644 --- a/src/main/kotlin/com/api/nft/service/AttributeService.kt +++ b/src/main/kotlin/com/api/nft/service/AttributeService.kt @@ -2,17 +2,16 @@ package com.api.nft.service import com.api.nft.domain.attribute.Attribute import com.api.nft.domain.attribute.AttributeRepository -import com.api.nft.service.external.dto.AttributeResponse +import com.api.nft.service.external.dto.AttributeData import org.springframework.stereotype.Service import reactor.core.publisher.Flux -import reactor.core.publisher.Mono @Service class AttributeService( private val attributeRepository: AttributeRepository, ) { - fun createAttribute(nftId: Long, attributes: List): Flux { + fun createAttribute(nftId: Long, attributes: List): Flux { return Flux.fromIterable(attributes).flatMap { attributeRepository.save( Attribute( diff --git a/src/main/kotlin/com/api/nft/service/CollectionService.kt b/src/main/kotlin/com/api/nft/service/CollectionService.kt index b685df0..868e87f 100644 --- a/src/main/kotlin/com/api/nft/service/CollectionService.kt +++ b/src/main/kotlin/com/api/nft/service/CollectionService.kt @@ -2,6 +2,7 @@ package com.api.nft.service import com.api.nft.domain.collection.Collection import com.api.nft.domain.collection.repository.CollectionRepository +import org.springframework.dao.DuplicateKeyException import org.springframework.stereotype.Service import reactor.core.publisher.Mono @@ -13,7 +14,14 @@ class CollectionService( fun findOrCreate(name: String) : Mono { return collectionRepository.findByName(name) .switchIfEmpty( - collectionRepository.insert(Collection(name = name)) + Mono.defer { + collectionRepository.insert(Collection(name = name)) + .onErrorResume(DuplicateKeyException::class.java){ + collectionRepository.findByName(name) + } + + } + ) } } \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/service/NftService.kt b/src/main/kotlin/com/api/nft/service/NftService.kt index 32ba4e5..0f70b6c 100644 --- a/src/main/kotlin/com/api/nft/service/NftService.kt +++ b/src/main/kotlin/com/api/nft/service/NftService.kt @@ -1,14 +1,12 @@ package com.api.nft.service -import com.api.nft.controller.dto.NftBatchRequest import com.api.nft.domain.nft.Nft import com.api.nft.domain.nft.repository.NftMetadataDto import com.api.nft.domain.nft.repository.NftRepository import com.api.nft.enums.ChainType -import com.api.nft.service.external.dto.AttributeResponse -import com.api.nft.service.external.dto.MetadataResponse -import com.api.nft.service.external.dto.NftResponse -import com.api.nft.service.external.moralis.MoralisApiService +import com.api.nft.service.external.dto.AttributeData +import com.api.nft.service.external.dto.MetadataData +import com.api.nft.service.external.dto.NftData import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional import reactor.core.publisher.Flux @@ -16,66 +14,62 @@ import reactor.core.publisher.Mono @Service class NftService( - private val moralisApiService: MoralisApiService, private val nftRepository: NftRepository, private val collectionService: CollectionService, private val metadataService: MetadataService, private val attributeService: AttributeService, ) { - fun getBatchNftList(reqeusts : List): Flux { - return Flux.fromIterable(reqeusts) - .flatMap { - findOrCreateNft(it.tokenId,it.tokenAddress,it.chainType) - } + @Transactional + fun saveNfts(requests: List, chainType: ChainType): Flux { + return Flux.fromIterable(requests) + .flatMap { findOrCreateNft(it, chainType) } + } + + fun findAllById(ids: List): Flux { + return nftRepository.findAllByNftJoinMetadata(ids) } @Transactional - fun findOrCreateNft(tokenId: String, tokenAddress: String, chainType: ChainType): Mono { - return nftRepository.findByTokenAddressAndTokenId(tokenAddress,tokenId) + fun findOrCreateNft(request:NftData, chainType: ChainType): Mono { + return nftRepository.findByTokenAddressAndTokenId(request.tokenAddress,request.tokenId) .switchIfEmpty( - createNftProcess(tokenId,tokenAddress,chainType) + createNftProcess(request,chainType) ).flatMap { nftRepository.findByNftJoinMetadata(it.id!!) } } - fun createNftProcess(tokenId: String, - tokenAddress: String, + fun createNftProcess(request: NftData, chainType: ChainType ): Mono { - val response = getNftByMoralis(tokenId, tokenAddress, chainType) - return response.flatMap { (nftResponse, metadataResponse, attributeResponseList) -> - createNft(nftResponse, metadataResponse, chainType) + val response = getNftData(request, chainType) + return response.flatMap { (nftData, metadataData, attributeDataList) -> + createNft(nftData, metadataData, chainType) .flatMap { nft -> - metadataService.createMetadata(nft.id!!, metadataResponse) + metadataService.createMetadata(nft.id!!, metadataData) .thenMany(attributeService.createAttribute( nft.id, - attributeResponseList ?: emptyList() + attributeDataList ?: emptyList() )) .then(Mono.just(nft)) } } } - fun getNftByMoralis(tokenId: String, - tokenAddress: String, - chainType: ChainType - ) - : Mono?>> { - return moralisApiService.getNft(tokenAddress,tokenId,chainType) - .filter { !it.possibleSpam } - .map { - val metadata = MetadataResponse.toMetadataResponse(it.metadata) - val attributes = if (metadata != null && metadata.attributes.isNotEmpty()) - AttributeResponse.toAttributeResponse(metadata.attributes) - else null - Triple(it,metadata,attributes) + fun getNftData(request: NftData, chainType: ChainType): Mono?>> { + return Mono.fromCallable { + val metadata = MetadataData.toMetadataResponse(request.metadata) + val attributes = metadata?.attributes?.let { + if (it.isNotEmpty()) AttributeData.toAttributeResponse(it) else null + } + Triple(request, metadata, attributes) } } - fun createNft(nft: NftResponse, - metadata: MetadataResponse, + + fun createNft(nft: NftData, + metadata: MetadataData, chainType: ChainType ): Mono { return collectionService.findOrCreate(nft.name).flatMap { @@ -86,9 +80,9 @@ class NftService( chinType = chainType.toString(), nftName = metadata.name, collectionName = it.name, - ownerOf = nft.ownerOf, tokenHash = nft.tokenHash, - amount = nft.amount.toInt() + amount = nft.amount.toInt(), + contractType = nft.contractType!!, ) ) } diff --git a/src/main/kotlin/com/api/nft/service/external/dto/NftResponse.kt b/src/main/kotlin/com/api/nft/service/external/dto/NftResponse.kt index e2b0c9b..d6299b4 100644 --- a/src/main/kotlin/com/api/nft/service/external/dto/NftResponse.kt +++ b/src/main/kotlin/com/api/nft/service/external/dto/NftResponse.kt @@ -5,7 +5,7 @@ import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import com.fasterxml.jackson.module.kotlin.readValue -data class NftResponse( +data class NftData( val amount: String, @JsonProperty("token_id")val tokenId: String, @JsonProperty("token_address") val tokenAddress: String, @@ -28,7 +28,7 @@ data class NftResponse( ) -data class MetadataResponse( +data class MetadataData( val name: String, val description: String, val image: String, @@ -38,9 +38,9 @@ data class MetadataResponse( @JsonIgnoreProperties(ignoreUnknown = true) @JsonProperty("youtube_url") val youtubeUrl: String? = null, ){ companion object { - fun toMetadataResponse(metadata: String): MetadataResponse { + fun toMetadataResponse(metadata: String): MetadataData { val mapper = jacksonObjectMapper() - return mapper.readValue(metadata, MetadataResponse::class.java) + return mapper.readValue(metadata, MetadataData::class.java) } fun parseImage(image: String) : String { @@ -49,14 +49,14 @@ data class MetadataResponse( } } -data class AttributeResponse( +data class AttributeData( @JsonProperty("trait_type") val traitType: String?, val value: String? ) { companion object { - fun toAttributeResponse(attributes: List>): List { + fun toAttributeResponse(attributes: List>): List { return attributes.map { - AttributeResponse( + AttributeData( traitType = it["trait_type"], value = it["value"] ) diff --git a/src/main/kotlin/com/api/nft/service/external/moralis/MoralisApiService.kt b/src/main/kotlin/com/api/nft/service/external/moralis/MoralisApiService.kt index 7425b51..2c60e28 100644 --- a/src/main/kotlin/com/api/nft/service/external/moralis/MoralisApiService.kt +++ b/src/main/kotlin/com/api/nft/service/external/moralis/MoralisApiService.kt @@ -1,7 +1,7 @@ package com.api.nft.service.external.moralis import com.api.nft.enums.ChainType -import com.api.nft.service.external.dto.NftResponse +import com.api.nft.service.external.dto.NftData import org.springframework.http.MediaType import org.springframework.stereotype.Service @@ -26,7 +26,7 @@ class MoralisApiService { return chain } - fun getNft(tokenAddress: String,tokenId: String,chainType: ChainType): Mono { + fun getNft(tokenAddress: String,tokenId: String,chainType: ChainType): Mono { val chain = queryParamByChain(chainType) return webClient.get() @@ -38,7 +38,7 @@ class MoralisApiService { .header("X-API-Key", apiKey) .header("Accept", MediaType.APPLICATION_JSON_VALUE) .retrieve() - .bodyToMono(NftResponse::class.java) + .bodyToMono(NftData::class.java) } fun getNft1(tokenAddress: String,tokenId: String,chainType: ChainType): Mono { diff --git a/src/main/kotlin/com/api/nft/service/metadataService.kt b/src/main/kotlin/com/api/nft/service/metadataService.kt index 6fd3e85..2764867 100644 --- a/src/main/kotlin/com/api/nft/service/metadataService.kt +++ b/src/main/kotlin/com/api/nft/service/metadataService.kt @@ -2,7 +2,7 @@ package com.api.nft.service import com.api.nft.domain.metadata.Metadata import com.api.nft.domain.metadata.repository.MetadataRepository -import com.api.nft.service.external.dto.MetadataResponse +import com.api.nft.service.external.dto.MetadataData import org.springframework.stereotype.Service import reactor.core.publisher.Mono @@ -11,12 +11,12 @@ class MetadataService( private val metadataRepository: MetadataRepository, ) { - fun createMetadata(nftId: Long, metadata: MetadataResponse): Mono { + fun createMetadata(nftId: Long, metadata: MetadataData): Mono { return metadataRepository.save( Metadata( nftId = nftId, description = metadata.description, - image = MetadataResponse.parseImage(metadata.image), + image = MetadataData.parseImage(metadata.image), animationUrl = metadata.animationUrl, ) ) diff --git a/src/main/kotlin/com/api/nft/util/Util.kt b/src/main/kotlin/com/api/nft/util/Util.kt new file mode 100644 index 0000000..9530fa8 --- /dev/null +++ b/src/main/kotlin/com/api/nft/util/Util.kt @@ -0,0 +1,14 @@ +package com.api.nft.util + +import com.api.nft.enums.ChainType + +object Util { + fun String.convertNetworkTypeToChainType(): ChainType { + return when (this) { + "ETHEREUM" -> ChainType.ETHEREUM_MAINNET + "POLYGON" -> ChainType.POLYGON_MAINNET + else -> throw IllegalArgumentException("Unknown network type: $this") + } + } + +} \ No newline at end of file diff --git a/src/main/resources/db/postgresql/migration/V1__Initial_schema.sql b/src/main/resources/db/postgresql/migration/V1__Initial_schema.sql index 5d7042d..972cc2f 100644 --- a/src/main/resources/db/postgresql/migration/V1__Initial_schema.sql +++ b/src/main/resources/db/postgresql/migration/V1__Initial_schema.sql @@ -8,7 +8,7 @@ CREATE TABLE IF NOT EXISTS nft ( token_address VARCHAR(255) NOT NULL, chain_type varchar(100) NOT NULL, nft_name varchar(255) NOT NULL, - owner_of varchar(255), + contract_type varchar(255) NOT NULL, token_hash varchar(300), amount INT, collection_name varchar(500) REFERENCES collection(name) diff --git a/src/test/kotlin/com/api/nft/NftTest.kt b/src/test/kotlin/com/api/nft/NftTest.kt index 656cfbd..80dfe29 100644 --- a/src/test/kotlin/com/api/nft/NftTest.kt +++ b/src/test/kotlin/com/api/nft/NftTest.kt @@ -24,22 +24,22 @@ class NftTest( println(res) } - @Test - fun test1() { - val tokenAddress = "0x57a133561c74c242a0b70af9c129126244b4869f" - val tokenId = "4733" - val res =nftService.getNftByMoralis(tokenId,tokenAddress, ChainType.POLYGON_MAINNET).block() - println(res?.first.toString()) - println(res?.second.toString()) - println(res?.third.toString()) - } - - @Test - fun test2() { - val tokenAddress = "0xe7900239e9332060dc975ed6f0cc5f0129d924cf" - val tokenId = "3" - nftService.createNftProcess(tokenId,tokenAddress, ChainType.POLYGON_MAINNET).block() - } +// @Test +// fun test1() { +// val tokenAddress = "0x57a133561c74c242a0b70af9c129126244b4869f" +// val tokenId = "4733" +// val res =nftService.getNftByMoralis(tokenId,tokenAddress, ChainType.POLYGON_MAINNET).block() +// println(res?.first.toString()) +// println(res?.second.toString()) +// println(res?.third.toString()) +// } +// +// @Test +// fun test2() { +// val tokenAddress = "0xe7900239e9332060dc975ed6f0cc5f0129d924cf" +// val tokenId = "3" +// nftService.createNftProcess(tokenId,tokenAddress, ChainType.POLYGON_MAINNET).block() +// } @Test fun test3() { @@ -47,11 +47,11 @@ class NftTest( collectionRepository.insert(Collection(tokenName)).block() } - @Test - fun test4() { - val tokenAddress = "0xe4a8bfdc0684f62b4cfb43165021814f059819ef" - val tokenId = "4617" - val res = nftService.findOrCreateNft(tokenId,tokenAddress,ChainType.POLYGON_MAINNET).block() - println(res.toString()) - } +// @Test +// fun test4() { +// val tokenAddress = "0xe4a8bfdc0684f62b4cfb43165021814f059819ef" +// val tokenId = "4617" +// val res = nftService.findOrCreateNft(tokenId,tokenAddress,ChainType.POLYGON_MAINNET).block() +// println(res.toString()) +// } } \ No newline at end of file From 3f31ef7ea4238028946394cbb111bebde738f8aa Mon Sep 17 00:00:00 2001 From: min96 Date: Tue, 23 Apr 2024 21:26:02 +0900 Subject: [PATCH 04/38] fix: getNfts --- .../com/api/nft/service/MetadataService.kt | 6 ++--- .../com/api/nft/service/metadataService.kt | 25 ------------------- 2 files changed, 3 insertions(+), 28 deletions(-) delete mode 100644 src/main/kotlin/com/api/nft/service/metadataService.kt diff --git a/src/main/kotlin/com/api/nft/service/MetadataService.kt b/src/main/kotlin/com/api/nft/service/MetadataService.kt index 6fd3e85..2764867 100644 --- a/src/main/kotlin/com/api/nft/service/MetadataService.kt +++ b/src/main/kotlin/com/api/nft/service/MetadataService.kt @@ -2,7 +2,7 @@ package com.api.nft.service import com.api.nft.domain.metadata.Metadata import com.api.nft.domain.metadata.repository.MetadataRepository -import com.api.nft.service.external.dto.MetadataResponse +import com.api.nft.service.external.dto.MetadataData import org.springframework.stereotype.Service import reactor.core.publisher.Mono @@ -11,12 +11,12 @@ class MetadataService( private val metadataRepository: MetadataRepository, ) { - fun createMetadata(nftId: Long, metadata: MetadataResponse): Mono { + fun createMetadata(nftId: Long, metadata: MetadataData): Mono { return metadataRepository.save( Metadata( nftId = nftId, description = metadata.description, - image = MetadataResponse.parseImage(metadata.image), + image = MetadataData.parseImage(metadata.image), animationUrl = metadata.animationUrl, ) ) diff --git a/src/main/kotlin/com/api/nft/service/metadataService.kt b/src/main/kotlin/com/api/nft/service/metadataService.kt deleted file mode 100644 index 2764867..0000000 --- a/src/main/kotlin/com/api/nft/service/metadataService.kt +++ /dev/null @@ -1,25 +0,0 @@ -package com.api.nft.service - -import com.api.nft.domain.metadata.Metadata -import com.api.nft.domain.metadata.repository.MetadataRepository -import com.api.nft.service.external.dto.MetadataData -import org.springframework.stereotype.Service -import reactor.core.publisher.Mono - -@Service -class MetadataService( - private val metadataRepository: MetadataRepository, -) { - - fun createMetadata(nftId: Long, metadata: MetadataData): Mono { - return metadataRepository.save( - Metadata( - nftId = nftId, - description = metadata.description, - image = MetadataData.parseImage(metadata.image), - animationUrl = metadata.animationUrl, - ) - ) - - } -} \ No newline at end of file From a9cd4fa7fd50b3bd7032890dd369c13663a822f1 Mon Sep 17 00:00:00 2001 From: min96 Date: Tue, 23 Apr 2024 23:18:22 +0900 Subject: [PATCH 05/38] fix: send to nftCreatedData using rabbitMQ --- build.gradle.kts | 3 +++ docker-compose.yaml | 10 ++++++++ .../kotlin/com/api/nft/config/RabbitConfig.kt | 24 +++++++++++++++++++ .../com/api/nft/event/NftCreatedEvent.kt | 6 +++++ .../com/api/nft/event/NftEventListener.kt | 14 +++++++++++ .../com/api/nft/rabbitMQ/RabbitMQSender.kt | 17 +++++++++++++ .../kotlin/com/api/nft/service/NftService.kt | 10 ++++++++ src/main/resources/application.yml | 6 +++++ src/test/kotlin/com/api/nft/NftTest.kt | 8 ++++--- 9 files changed, 95 insertions(+), 3 deletions(-) create mode 100644 src/main/kotlin/com/api/nft/config/RabbitConfig.kt create mode 100644 src/main/kotlin/com/api/nft/event/NftCreatedEvent.kt create mode 100644 src/main/kotlin/com/api/nft/event/NftEventListener.kt create mode 100644 src/main/kotlin/com/api/nft/rabbitMQ/RabbitMQSender.kt diff --git a/build.gradle.kts b/build.gradle.kts index 0598de6..fca0282 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -37,9 +37,12 @@ dependencies { implementation("org.jetbrains.kotlin:kotlin-reflect") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor") + implementation("org.springframework.boot:spring-boot-starter-amqp") + testImplementation("org.springframework.boot:spring-boot-starter-test") testImplementation("io.projectreactor:reactor-test") testImplementation("org.springframework.batch:spring-batch-test") + } tasks.withType { diff --git a/docker-compose.yaml b/docker-compose.yaml index dfa5a58..cbdcd22 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -12,3 +12,13 @@ services: volumes: - ./init.sql:/docker-entrypoint-initdb.d/init.sql + rabbitmq: + image: rabbitmq:3-management + environment: + RABBITMQ_DEFAULT_USER: closeSea + RABBITMQ_DEFAULT_PASS: closeSeaP@ssword + ports: + - '5672:5672' + - '15672:15672' + volumes: + - ./rabbitmq_data:/var/lib/rabbitmq \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/config/RabbitConfig.kt b/src/main/kotlin/com/api/nft/config/RabbitConfig.kt new file mode 100644 index 0000000..d72250f --- /dev/null +++ b/src/main/kotlin/com/api/nft/config/RabbitConfig.kt @@ -0,0 +1,24 @@ +package com.api.nft.config + +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.amqp.core.* + +@Configuration +class RabbitConfig { + @Bean + fun nftQueue(): Queue { + return Queue("nftQueue", true) + } + + @Bean + fun nftExchange(): DirectExchange { + return DirectExchange("nftExchange") + } + + @Bean + fun bindingNftQueue(nftQueue: Queue, nftExchange: DirectExchange): Binding { + return BindingBuilder.bind(nftQueue).to(nftExchange).with("nftRoutingKey") + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/event/NftCreatedEvent.kt b/src/main/kotlin/com/api/nft/event/NftCreatedEvent.kt new file mode 100644 index 0000000..c2ef22f --- /dev/null +++ b/src/main/kotlin/com/api/nft/event/NftCreatedEvent.kt @@ -0,0 +1,6 @@ +package com.api.nft.event + +import com.api.nft.domain.nft.Nft +import org.springframework.context.ApplicationEvent + +data class NftCreatedEvent(val source: Any, val nft: Nft): ApplicationEvent(source) diff --git a/src/main/kotlin/com/api/nft/event/NftEventListener.kt b/src/main/kotlin/com/api/nft/event/NftEventListener.kt new file mode 100644 index 0000000..6ed69f1 --- /dev/null +++ b/src/main/kotlin/com/api/nft/event/NftEventListener.kt @@ -0,0 +1,14 @@ +package com.api.nft.event + +import com.api.nft.rabbitMQ.RabbitMQSender +import org.springframework.context.event.EventListener + +class NftEventListener( + private val provider: RabbitMQSender +){ + + @EventListener + fun onNftCreated(event: NftCreatedEvent) { + provider.nftSend(event.nft) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/rabbitMQ/RabbitMQSender.kt b/src/main/kotlin/com/api/nft/rabbitMQ/RabbitMQSender.kt new file mode 100644 index 0000000..9b1d5ea --- /dev/null +++ b/src/main/kotlin/com/api/nft/rabbitMQ/RabbitMQSender.kt @@ -0,0 +1,17 @@ +package com.api.nft.rabbitMQ + +import com.api.nft.domain.nft.Nft +import org.springframework.amqp.rabbit.core.RabbitTemplate +import org.springframework.stereotype.Service + +@Service +class RabbitMQSender( + private val rabbitTemplate: RabbitTemplate +) { + + //TODO("리팩토링") + //TODO("반환값 재정의") + fun nftSend(nft: Nft) { + rabbitTemplate.convertAndSend("nftExchange", "nftRoutingKey", nft) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/service/NftService.kt b/src/main/kotlin/com/api/nft/service/NftService.kt index 0f70b6c..e6a87a4 100644 --- a/src/main/kotlin/com/api/nft/service/NftService.kt +++ b/src/main/kotlin/com/api/nft/service/NftService.kt @@ -4,9 +4,11 @@ import com.api.nft.domain.nft.Nft import com.api.nft.domain.nft.repository.NftMetadataDto import com.api.nft.domain.nft.repository.NftRepository import com.api.nft.enums.ChainType +import com.api.nft.event.NftCreatedEvent import com.api.nft.service.external.dto.AttributeData import com.api.nft.service.external.dto.MetadataData import com.api.nft.service.external.dto.NftData +import org.springframework.context.ApplicationEventPublisher import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional import reactor.core.publisher.Flux @@ -18,6 +20,7 @@ class NftService( private val collectionService: CollectionService, private val metadataService: MetadataService, private val attributeService: AttributeService, + private val eventPublisher: ApplicationEventPublisher, ) { @Transactional @@ -53,10 +56,13 @@ class NftService( attributeDataList ?: emptyList() )) .then(Mono.just(nft)) + .doOnSuccess { eventPublisher.publishEvent(NftCreatedEvent(this, nft)) } + } } } + fun getNftData(request: NftData, chainType: ChainType): Mono?>> { return Mono.fromCallable { val metadata = MetadataData.toMetadataResponse(request.metadata) @@ -87,4 +93,8 @@ class NftService( ) } } + + fun rabbitMqEventTest() { + + } } \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index dfbd9d4..ae2b511 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -13,6 +13,12 @@ spring: username: nft password: nft + rabbitmq: + host: localhost + port: 5672 + username: closeSea + password: closeSeaP@ssword + logging: level: org.springframework.r2dbc: debug diff --git a/src/test/kotlin/com/api/nft/NftTest.kt b/src/test/kotlin/com/api/nft/NftTest.kt index 80dfe29..4a637ec 100644 --- a/src/test/kotlin/com/api/nft/NftTest.kt +++ b/src/test/kotlin/com/api/nft/NftTest.kt @@ -1,8 +1,8 @@ package com.api.nft -import com.api.nft.domain.collection.Collection import com.api.nft.domain.collection.repository.CollectionRepository import com.api.nft.enums.ChainType +import com.api.nft.rabbitMQ.RabbitMQSender import com.api.nft.service.external.moralis.MoralisApiService import com.api.nft.service.NftService import org.junit.jupiter.api.Test @@ -14,6 +14,7 @@ class NftTest( @Autowired private val moralisApiService: MoralisApiService, @Autowired private val nftService: NftService, @Autowired private val collectionRepository: CollectionRepository, + @Autowired private val rabbitMQSender: RabbitMQSender, ) { @Test @@ -43,8 +44,9 @@ class NftTest( @Test fun test3() { - val tokenName = "asdasdasd" - collectionRepository.insert(Collection(tokenName)).block() +// val tokenName = "asdasdasd" +// collectionRepository.insert(Collection(tokenName)).block() + rabbitMQSender.nftsend("nmftasd") } // @Test From 2c48d86a33fc812c06f7fc23be7fc7200dae758d Mon Sep 17 00:00:00 2001 From: min96 Date: Sun, 28 Apr 2024 22:54:05 +0900 Subject: [PATCH 06/38] feat: nft transfer --- build.gradle.kts | 2 + src/main/kotlin/com/api/nft/NftApplication.kt | 2 + .../com/api/nft/controller/NftController.kt | 2 +- .../api/nft/domain/collection/Collection.kt | 5 +- src/main/kotlin/com/api/nft/domain/nft/Nft.kt | 1 + .../com/api/nft/domain/trasfer/Transfer.kt | 16 ++++ .../nft/domain/trasfer/TransferRepository.kt | 6 ++ .../api/nft/properties/ApiKeysProperties.kt | 8 ++ .../com/api/nft/service/TransferResponse.kt | 9 ++ .../nft/service/{ => api}/AttributeService.kt | 2 +- .../service/{ => api}/CollectionService.kt | 15 ++- .../nft/service/{ => api}/MetadataService.kt | 2 +- .../api/nft/service/{ => api}/NftService.kt | 9 +- .../api/nft/service/api/TransferService.kt | 91 +++++++++++++++++++ .../dto/{NftResponse.kt => NftData.kt} | 1 - src/main/resources/application.yml | 3 + .../migration/V1__Initial_schema.sql | 14 ++- src/test/kotlin/com/api/nft/NftTest.kt | 2 +- 18 files changed, 173 insertions(+), 17 deletions(-) create mode 100644 src/main/kotlin/com/api/nft/domain/trasfer/Transfer.kt create mode 100644 src/main/kotlin/com/api/nft/domain/trasfer/TransferRepository.kt create mode 100644 src/main/kotlin/com/api/nft/properties/ApiKeysProperties.kt create mode 100644 src/main/kotlin/com/api/nft/service/TransferResponse.kt rename src/main/kotlin/com/api/nft/service/{ => api}/AttributeService.kt (95%) rename src/main/kotlin/com/api/nft/service/{ => api}/CollectionService.kt (62%) rename src/main/kotlin/com/api/nft/service/{ => api}/MetadataService.kt (95%) rename src/main/kotlin/com/api/nft/service/{ => api}/NftService.kt (94%) create mode 100644 src/main/kotlin/com/api/nft/service/api/TransferService.kt rename src/main/kotlin/com/api/nft/service/external/dto/{NftResponse.kt => NftData.kt} (97%) diff --git a/build.gradle.kts b/build.gradle.kts index fca0282..d70f61f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -39,6 +39,8 @@ dependencies { implementation("org.springframework.boot:spring-boot-starter-amqp") + implementation("org.web3j:core:4.9.1") + testImplementation("org.springframework.boot:spring-boot-starter-test") testImplementation("io.projectreactor:reactor-test") testImplementation("org.springframework.batch:spring-batch-test") diff --git a/src/main/kotlin/com/api/nft/NftApplication.kt b/src/main/kotlin/com/api/nft/NftApplication.kt index 39cf3df..34e03b9 100644 --- a/src/main/kotlin/com/api/nft/NftApplication.kt +++ b/src/main/kotlin/com/api/nft/NftApplication.kt @@ -1,9 +1,11 @@ package com.api.nft import org.springframework.boot.autoconfigure.SpringBootApplication +import org.springframework.boot.context.properties.ConfigurationPropertiesScan import org.springframework.boot.runApplication @SpringBootApplication +@ConfigurationPropertiesScan class NftApplication fun main(args: Array) { diff --git a/src/main/kotlin/com/api/nft/controller/NftController.kt b/src/main/kotlin/com/api/nft/controller/NftController.kt index fdccda0..7b2da59 100644 --- a/src/main/kotlin/com/api/nft/controller/NftController.kt +++ b/src/main/kotlin/com/api/nft/controller/NftController.kt @@ -2,7 +2,7 @@ package com.api.nft.controller import com.api.nft.domain.nft.repository.NftMetadataDto import com.api.nft.enums.ChainType -import com.api.nft.service.NftService +import com.api.nft.service.api.NftService import com.api.nft.service.external.dto.NftData import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PathVariable diff --git a/src/main/kotlin/com/api/nft/domain/collection/Collection.kt b/src/main/kotlin/com/api/nft/domain/collection/Collection.kt index da39772..711210c 100644 --- a/src/main/kotlin/com/api/nft/domain/collection/Collection.kt +++ b/src/main/kotlin/com/api/nft/domain/collection/Collection.kt @@ -3,10 +3,13 @@ package com.api.nft.domain.collection import org.springframework.data.annotation.Id import org.springframework.data.relational.core.mapping.Table import lombok.AllArgsConstructor +import org.springframework.data.relational.core.mapping.Column @Table("collection") @AllArgsConstructor class Collection( - @Id val name: String + @Id val name: String, + val logo: String?, + @Column("banner_image")val bannerImage: String?, ) { } \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/domain/nft/Nft.kt b/src/main/kotlin/com/api/nft/domain/nft/Nft.kt index 61637df..82eca62 100644 --- a/src/main/kotlin/com/api/nft/domain/nft/Nft.kt +++ b/src/main/kotlin/com/api/nft/domain/nft/Nft.kt @@ -5,6 +5,7 @@ import org.springframework.data.annotation.Id import org.springframework.data.relational.core.mapping.Column import org.springframework.data.relational.core.mapping.Table +//TODO("캡슐화") @Table("nft") @AllArgsConstructor class Nft( diff --git a/src/main/kotlin/com/api/nft/domain/trasfer/Transfer.kt b/src/main/kotlin/com/api/nft/domain/trasfer/Transfer.kt new file mode 100644 index 0000000..87d735b --- /dev/null +++ b/src/main/kotlin/com/api/nft/domain/trasfer/Transfer.kt @@ -0,0 +1,16 @@ +package com.api.nft.domain.trasfer + +import org.springframework.data.annotation.Id +import org.springframework.data.relational.core.mapping.Column +import org.springframework.data.relational.core.mapping.Table + +@Table("transfer") +class Transfer( + @Id val id: Long? = null, + @Column("nft_id") val nftId: Long, + @Column("from_address") val fromAddress: String, + @Column("to_address") val toAddress: String, + ) { + + +} \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/domain/trasfer/TransferRepository.kt b/src/main/kotlin/com/api/nft/domain/trasfer/TransferRepository.kt new file mode 100644 index 0000000..ee7be38 --- /dev/null +++ b/src/main/kotlin/com/api/nft/domain/trasfer/TransferRepository.kt @@ -0,0 +1,6 @@ +package com.api.nft.domain.trasfer + +import org.springframework.data.repository.reactive.ReactiveCrudRepository + +interface TransferRepository: ReactiveCrudRepository { +} \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/properties/ApiKeysProperties.kt b/src/main/kotlin/com/api/nft/properties/ApiKeysProperties.kt new file mode 100644 index 0000000..08b6a7c --- /dev/null +++ b/src/main/kotlin/com/api/nft/properties/ApiKeysProperties.kt @@ -0,0 +1,8 @@ +package com.api.nft.properties + +import org.springframework.boot.context.properties.ConfigurationProperties + +@ConfigurationProperties(prefix = "apikey") +data class ApiKeysProperties( + val infura: String, +) diff --git a/src/main/kotlin/com/api/nft/service/TransferResponse.kt b/src/main/kotlin/com/api/nft/service/TransferResponse.kt new file mode 100644 index 0000000..cb414bc --- /dev/null +++ b/src/main/kotlin/com/api/nft/service/TransferResponse.kt @@ -0,0 +1,9 @@ +package com.api.nft.service + +import java.math.BigInteger + +data class TransferResponse( + val from: String, + val to: String, + val tokenId: BigInteger +) diff --git a/src/main/kotlin/com/api/nft/service/AttributeService.kt b/src/main/kotlin/com/api/nft/service/api/AttributeService.kt similarity index 95% rename from src/main/kotlin/com/api/nft/service/AttributeService.kt rename to src/main/kotlin/com/api/nft/service/api/AttributeService.kt index 3f32aac..d905d5d 100644 --- a/src/main/kotlin/com/api/nft/service/AttributeService.kt +++ b/src/main/kotlin/com/api/nft/service/api/AttributeService.kt @@ -1,4 +1,4 @@ -package com.api.nft.service +package com.api.nft.service.api import com.api.nft.domain.attribute.Attribute import com.api.nft.domain.attribute.AttributeRepository diff --git a/src/main/kotlin/com/api/nft/service/CollectionService.kt b/src/main/kotlin/com/api/nft/service/api/CollectionService.kt similarity index 62% rename from src/main/kotlin/com/api/nft/service/CollectionService.kt rename to src/main/kotlin/com/api/nft/service/api/CollectionService.kt index 868e87f..68dac88 100644 --- a/src/main/kotlin/com/api/nft/service/CollectionService.kt +++ b/src/main/kotlin/com/api/nft/service/api/CollectionService.kt @@ -1,4 +1,4 @@ -package com.api.nft.service +package com.api.nft.service.api import com.api.nft.domain.collection.Collection import com.api.nft.domain.collection.repository.CollectionRepository @@ -11,11 +11,20 @@ class CollectionService( private val collectionRepository: CollectionRepository, ) { - fun findOrCreate(name: String) : Mono { + fun findOrCreate( + name: String, + logo: String?, + bannerImage: String?, + ) : Mono { return collectionRepository.findByName(name) .switchIfEmpty( Mono.defer { - collectionRepository.insert(Collection(name = name)) + collectionRepository.insert( + Collection( + name = name, + logo = logo, + bannerImage = bannerImage, + )) .onErrorResume(DuplicateKeyException::class.java){ collectionRepository.findByName(name) } diff --git a/src/main/kotlin/com/api/nft/service/MetadataService.kt b/src/main/kotlin/com/api/nft/service/api/MetadataService.kt similarity index 95% rename from src/main/kotlin/com/api/nft/service/MetadataService.kt rename to src/main/kotlin/com/api/nft/service/api/MetadataService.kt index 2764867..6df1522 100644 --- a/src/main/kotlin/com/api/nft/service/MetadataService.kt +++ b/src/main/kotlin/com/api/nft/service/api/MetadataService.kt @@ -1,4 +1,4 @@ -package com.api.nft.service +package com.api.nft.service.api import com.api.nft.domain.metadata.Metadata import com.api.nft.domain.metadata.repository.MetadataRepository diff --git a/src/main/kotlin/com/api/nft/service/NftService.kt b/src/main/kotlin/com/api/nft/service/api/NftService.kt similarity index 94% rename from src/main/kotlin/com/api/nft/service/NftService.kt rename to src/main/kotlin/com/api/nft/service/api/NftService.kt index e6a87a4..3d70252 100644 --- a/src/main/kotlin/com/api/nft/service/NftService.kt +++ b/src/main/kotlin/com/api/nft/service/api/NftService.kt @@ -1,4 +1,4 @@ -package com.api.nft.service +package com.api.nft.service.api import com.api.nft.domain.nft.Nft import com.api.nft.domain.nft.repository.NftMetadataDto @@ -21,6 +21,7 @@ class NftService( private val metadataService: MetadataService, private val attributeService: AttributeService, private val eventPublisher: ApplicationEventPublisher, + private val transferService: TransferService, ) { @Transactional @@ -78,7 +79,7 @@ class NftService( metadata: MetadataData, chainType: ChainType ): Mono { - return collectionService.findOrCreate(nft.name).flatMap { + return collectionService.findOrCreate(nft.name,nft.collectionLogo,nft.collectionBannerImage).flatMap { nftRepository.save( Nft( tokenId = nft.tokenId, @@ -93,8 +94,4 @@ class NftService( ) } } - - fun rabbitMqEventTest() { - - } } \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/service/api/TransferService.kt b/src/main/kotlin/com/api/nft/service/api/TransferService.kt new file mode 100644 index 0000000..6f220b5 --- /dev/null +++ b/src/main/kotlin/com/api/nft/service/api/TransferService.kt @@ -0,0 +1,91 @@ +package com.api.nft.service.api + +import com.api.nft.enums.ChainType +import com.api.nft.properties.ApiKeysProperties +import com.api.nft.service.TransferResponse +import org.springframework.stereotype.Service +import org.web3j.abi.EventEncoder +import org.web3j.abi.TypeReference +import org.web3j.abi.datatypes.Address +import org.web3j.abi.datatypes.Event +import org.web3j.abi.datatypes.generated.Uint256 +import org.web3j.protocol.Web3j +import org.web3j.protocol.core.DefaultBlockParameterName +import org.web3j.protocol.core.methods.request.EthFilter +import org.web3j.protocol.core.methods.response.Log +import org.web3j.protocol.http.HttpService +import org.web3j.utils.Numeric +import java.math.BigInteger + + +@Service +class TransferService( + private val apiKeysProperties: ApiKeysProperties, +) { + + fun createTransfer() { + TODO() + } + + private fun getFullUrl(chainType: ChainType): String { + val chainUrl = when (chainType) { + ChainType.ETHEREUM_MAINNET -> "mainnet.infura.io" + ChainType.POLYGON_MAINNET -> "polygon-mainnet.infura.io" + ChainType.ETHREUM_GOERLI -> "goerli.infura.io" + ChainType.ETHREUM_SEPOLIA -> "sepolia.infura.io" + ChainType.POLYGON_MUMBAI -> "polygon-mumbai.infura.io" + } + return "https://$chainUrl/v3/${apiKeysProperties.infura}" + } + + private fun getWeb3j(chainType: ChainType): Web3j { + return Web3j.build(HttpService(getFullUrl(chainType))) + } + + + fun getTransactionERC721(chainType: ChainType, contractAddress: String, tokenId: String): List { + val web3 = getWeb3j(chainType) + val transferEvent = Event( + "Transfer", + listOf( + TypeReference.create(Address::class.java, true), + TypeReference.create(Address::class.java, true), + TypeReference.create(Uint256::class.java, true) + ) + ) + + val eventSignature = EventEncoder.encode(transferEvent) + + val filter = EthFilter( + DefaultBlockParameterName.EARLIEST, + DefaultBlockParameterName.LATEST, + contractAddress + ).addSingleTopic(eventSignature) + .addSingleTopic(null) + .addSingleTopic(null) + .addSingleTopic(Numeric.toHexStringWithPrefixZeroPadded(BigInteger(tokenId), 64)) + + val responses = mutableListOf() + + try { + val logs = web3.ethGetLogs(filter).send() + logs.logs.forEach { + val log = it.get() as Log + val (from, to, token) = parseTransfer(log) + responses.add(TransferResponse(from, to, token)) + } + } catch (e: Exception) { + println("Error fetching logs: ${e.message}") + } + + return responses + } + + private fun parseTransfer(log: Log): Triple { + val fromAddress = "0x" + log.topics[1].substring(log.topics[1].length - 40) + val toAddress = "0x" + log.topics[2].substring(log.topics[2].length - 40) + val tokenId = Numeric.toBigInt(log.topics[3]) + return Triple(fromAddress, toAddress, tokenId) + } +} + diff --git a/src/main/kotlin/com/api/nft/service/external/dto/NftResponse.kt b/src/main/kotlin/com/api/nft/service/external/dto/NftData.kt similarity index 97% rename from src/main/kotlin/com/api/nft/service/external/dto/NftResponse.kt rename to src/main/kotlin/com/api/nft/service/external/dto/NftData.kt index d6299b4..a9c18e7 100644 --- a/src/main/kotlin/com/api/nft/service/external/dto/NftResponse.kt +++ b/src/main/kotlin/com/api/nft/service/external/dto/NftData.kt @@ -3,7 +3,6 @@ package com.api.nft.service.external.dto import com.fasterxml.jackson.annotation.JsonIgnoreProperties import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper -import com.fasterxml.jackson.module.kotlin.readValue data class NftData( val amount: String, diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index ae2b511..e901756 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -26,3 +26,6 @@ logging: # root: info server: port: 8082 + +apikey: + infura: "98b672d2ce9a4089a3a5cb5081dde2fa" \ No newline at end of file diff --git a/src/main/resources/db/postgresql/migration/V1__Initial_schema.sql b/src/main/resources/db/postgresql/migration/V1__Initial_schema.sql index 972cc2f..24fd046 100644 --- a/src/main/resources/db/postgresql/migration/V1__Initial_schema.sql +++ b/src/main/resources/db/postgresql/migration/V1__Initial_schema.sql @@ -1,5 +1,7 @@ CREATE TABLE IF NOT EXISTS collection ( - name varchar(500) PRIMARY KEY + name varchar(500) PRIMARY KEY, + logo varchar(500), + banner_image varchar(500) ); CREATE TABLE IF NOT EXISTS nft ( @@ -24,7 +26,7 @@ CREATE TABLE IF NOT EXISTS metadata ( ); -CREATE TABLE IF NOt EXISTS attribute ( +CREATE TABLE IF NOT EXISTS attribute ( id SERIAL PRIMARY KEY, nft_id BIGINT REFERENCES nft(id), trait_type varchar(255), @@ -32,6 +34,14 @@ CREATE TABLE IF NOt EXISTS attribute ( ); +CREATE TABLE IF NOT EXISTS transfer ( + id SERIAL PRIMARY KEY, + nft_id BIGINT REFERENCES nft(id), + from_address varchar(255) not null, + to_address varchar(255) not null +); + + diff --git a/src/test/kotlin/com/api/nft/NftTest.kt b/src/test/kotlin/com/api/nft/NftTest.kt index 4a637ec..89a7ba9 100644 --- a/src/test/kotlin/com/api/nft/NftTest.kt +++ b/src/test/kotlin/com/api/nft/NftTest.kt @@ -4,7 +4,7 @@ import com.api.nft.domain.collection.repository.CollectionRepository import com.api.nft.enums.ChainType import com.api.nft.rabbitMQ.RabbitMQSender import com.api.nft.service.external.moralis.MoralisApiService -import com.api.nft.service.NftService +import com.api.nft.service.api.NftService import org.junit.jupiter.api.Test import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.context.SpringBootTest From a62690d278c26895104006b5c11b4a702d0958aa Mon Sep 17 00:00:00 2001 From: min96 Date: Fri, 3 May 2024 00:28:24 +0900 Subject: [PATCH 07/38] feat: nft transfer --- .../api/nft/domain/collection/Collection.kt | 1 + .../com/api/nft/event/NftCreatedEvent.kt | 3 +- .../com/api/nft/event/NftEventListener.kt | 2 +- .../api/nft/properties/ApiKeysProperties.kt | 1 + .../com/api/nft/rabbitMQ/RabbitMQSender.kt | 2 +- .../api/nft/service/api/CollectionService.kt | 2 + .../com/api/nft/service/api/NftService.kt | 11 ++- .../api/nft/service/api/TransferService.kt | 88 ++++++------------- .../api/nft/service/external/dto/NftData.kt | 4 +- .../service/external/dto/NftTransferData.kt | 36 ++++++++ .../external/moralis/MoralisApiService.kt | 22 ++--- src/main/resources/application.yml | 3 +- .../migration/V1__Initial_schema.sql | 3 +- src/test/kotlin/com/api/nft/NftTest.kt | 10 +-- 14 files changed, 92 insertions(+), 96 deletions(-) create mode 100644 src/main/kotlin/com/api/nft/service/external/dto/NftTransferData.kt diff --git a/src/main/kotlin/com/api/nft/domain/collection/Collection.kt b/src/main/kotlin/com/api/nft/domain/collection/Collection.kt index 711210c..230b192 100644 --- a/src/main/kotlin/com/api/nft/domain/collection/Collection.kt +++ b/src/main/kotlin/com/api/nft/domain/collection/Collection.kt @@ -11,5 +11,6 @@ class Collection( @Id val name: String, val logo: String?, @Column("banner_image")val bannerImage: String?, + val description : String?, ) { } \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/event/NftCreatedEvent.kt b/src/main/kotlin/com/api/nft/event/NftCreatedEvent.kt index c2ef22f..6170199 100644 --- a/src/main/kotlin/com/api/nft/event/NftCreatedEvent.kt +++ b/src/main/kotlin/com/api/nft/event/NftCreatedEvent.kt @@ -3,4 +3,5 @@ package com.api.nft.event import com.api.nft.domain.nft.Nft import org.springframework.context.ApplicationEvent -data class NftCreatedEvent(val source: Any, val nft: Nft): ApplicationEvent(source) +data class NftCreatedEvent(val eventSource: Any, val nft: Nft): ApplicationEvent(eventSource) + diff --git a/src/main/kotlin/com/api/nft/event/NftEventListener.kt b/src/main/kotlin/com/api/nft/event/NftEventListener.kt index 6ed69f1..12fc17d 100644 --- a/src/main/kotlin/com/api/nft/event/NftEventListener.kt +++ b/src/main/kotlin/com/api/nft/event/NftEventListener.kt @@ -9,6 +9,6 @@ class NftEventListener( @EventListener fun onNftCreated(event: NftCreatedEvent) { - provider.nftSend(event.nft) + provider.nftSend(event.nft.id!!) } } \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/properties/ApiKeysProperties.kt b/src/main/kotlin/com/api/nft/properties/ApiKeysProperties.kt index 08b6a7c..b3e171e 100644 --- a/src/main/kotlin/com/api/nft/properties/ApiKeysProperties.kt +++ b/src/main/kotlin/com/api/nft/properties/ApiKeysProperties.kt @@ -5,4 +5,5 @@ import org.springframework.boot.context.properties.ConfigurationProperties @ConfigurationProperties(prefix = "apikey") data class ApiKeysProperties( val infura: String, + val moralis: String, ) diff --git a/src/main/kotlin/com/api/nft/rabbitMQ/RabbitMQSender.kt b/src/main/kotlin/com/api/nft/rabbitMQ/RabbitMQSender.kt index 9b1d5ea..24030c7 100644 --- a/src/main/kotlin/com/api/nft/rabbitMQ/RabbitMQSender.kt +++ b/src/main/kotlin/com/api/nft/rabbitMQ/RabbitMQSender.kt @@ -11,7 +11,7 @@ class RabbitMQSender( //TODO("리팩토링") //TODO("반환값 재정의") - fun nftSend(nft: Nft) { + fun nftSend(nft: Long) { rabbitTemplate.convertAndSend("nftExchange", "nftRoutingKey", nft) } } \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/service/api/CollectionService.kt b/src/main/kotlin/com/api/nft/service/api/CollectionService.kt index 68dac88..82e6fbe 100644 --- a/src/main/kotlin/com/api/nft/service/api/CollectionService.kt +++ b/src/main/kotlin/com/api/nft/service/api/CollectionService.kt @@ -15,6 +15,7 @@ class CollectionService( name: String, logo: String?, bannerImage: String?, + description: String?, ) : Mono { return collectionRepository.findByName(name) .switchIfEmpty( @@ -24,6 +25,7 @@ class CollectionService( name = name, logo = logo, bannerImage = bannerImage, + description = description, )) .onErrorResume(DuplicateKeyException::class.java){ collectionRepository.findByName(name) diff --git a/src/main/kotlin/com/api/nft/service/api/NftService.kt b/src/main/kotlin/com/api/nft/service/api/NftService.kt index 3d70252..1dece0f 100644 --- a/src/main/kotlin/com/api/nft/service/api/NftService.kt +++ b/src/main/kotlin/com/api/nft/service/api/NftService.kt @@ -24,7 +24,7 @@ class NftService( private val transferService: TransferService, ) { - @Transactional + fun saveNfts(requests: List, chainType: ChainType): Flux { return Flux.fromIterable(requests) .flatMap { findOrCreateNft(it, chainType) } @@ -34,7 +34,6 @@ class NftService( return nftRepository.findAllByNftJoinMetadata(ids) } - @Transactional fun findOrCreateNft(request:NftData, chainType: ChainType): Mono { return nftRepository.findByTokenAddressAndTokenId(request.tokenAddress,request.tokenId) .switchIfEmpty( @@ -58,6 +57,7 @@ class NftService( )) .then(Mono.just(nft)) .doOnSuccess { eventPublisher.publishEvent(NftCreatedEvent(this, nft)) } +// .doOnSuccess { transferService.createTransfer(nft) } } } @@ -79,7 +79,12 @@ class NftService( metadata: MetadataData, chainType: ChainType ): Mono { - return collectionService.findOrCreate(nft.name,nft.collectionLogo,nft.collectionBannerImage).flatMap { + return collectionService.findOrCreate( + nft.name, + nft.collectionLogo, + nft.collectionBannerImage, + metadata.description, + ).flatMap { nftRepository.save( Nft( tokenId = nft.tokenId, diff --git a/src/main/kotlin/com/api/nft/service/api/TransferService.kt b/src/main/kotlin/com/api/nft/service/api/TransferService.kt index 6f220b5..c98e1a8 100644 --- a/src/main/kotlin/com/api/nft/service/api/TransferService.kt +++ b/src/main/kotlin/com/api/nft/service/api/TransferService.kt @@ -1,8 +1,14 @@ package com.api.nft.service.api +import com.api.nft.domain.nft.Nft +import com.api.nft.domain.trasfer.Transfer +import com.api.nft.domain.trasfer.TransferRepository import com.api.nft.enums.ChainType import com.api.nft.properties.ApiKeysProperties import com.api.nft.service.TransferResponse +import com.api.nft.service.external.dto.NftTransferData +import com.api.nft.service.external.moralis.MoralisApiService +import com.api.nft.util.Util.convertNetworkTypeToChainType import org.springframework.stereotype.Service import org.web3j.abi.EventEncoder import org.web3j.abi.TypeReference @@ -20,72 +26,28 @@ import java.math.BigInteger @Service class TransferService( - private val apiKeysProperties: ApiKeysProperties, + private val moralisApiService: MoralisApiService, + private val transferRepository: TransferRepository, ) { - fun createTransfer() { - TODO() - } - private fun getFullUrl(chainType: ChainType): String { - val chainUrl = when (chainType) { - ChainType.ETHEREUM_MAINNET -> "mainnet.infura.io" - ChainType.POLYGON_MAINNET -> "polygon-mainnet.infura.io" - ChainType.ETHREUM_GOERLI -> "goerli.infura.io" - ChainType.ETHREUM_SEPOLIA -> "sepolia.infura.io" - ChainType.POLYGON_MUMBAI -> "polygon-mumbai.infura.io" - } - return "https://$chainUrl/v3/${apiKeysProperties.infura}" - } + fun createTransfer(nft: Nft) + { + moralisApiService.getNftTransfer( + nft.tokenAddress, + nft.tokenId, + nft.chinType.convertNetworkTypeToChainType() + ).map { + // transferRepository.save(it.toEntity(nft.id!!)) + } + } + +// private fun NftTransferData.toEntity(nftId: Long) = +// Transfer( +// nftId = nftId, +// fromAddress = this.result, +// toAddress = this.to +// ) - private fun getWeb3j(chainType: ChainType): Web3j { - return Web3j.build(HttpService(getFullUrl(chainType))) - } - - - fun getTransactionERC721(chainType: ChainType, contractAddress: String, tokenId: String): List { - val web3 = getWeb3j(chainType) - val transferEvent = Event( - "Transfer", - listOf( - TypeReference.create(Address::class.java, true), - TypeReference.create(Address::class.java, true), - TypeReference.create(Uint256::class.java, true) - ) - ) - - val eventSignature = EventEncoder.encode(transferEvent) - - val filter = EthFilter( - DefaultBlockParameterName.EARLIEST, - DefaultBlockParameterName.LATEST, - contractAddress - ).addSingleTopic(eventSignature) - .addSingleTopic(null) - .addSingleTopic(null) - .addSingleTopic(Numeric.toHexStringWithPrefixZeroPadded(BigInteger(tokenId), 64)) - - val responses = mutableListOf() - - try { - val logs = web3.ethGetLogs(filter).send() - logs.logs.forEach { - val log = it.get() as Log - val (from, to, token) = parseTransfer(log) - responses.add(TransferResponse(from, to, token)) - } - } catch (e: Exception) { - println("Error fetching logs: ${e.message}") - } - - return responses - } - - private fun parseTransfer(log: Log): Triple { - val fromAddress = "0x" + log.topics[1].substring(log.topics[1].length - 40) - val toAddress = "0x" + log.topics[2].substring(log.topics[2].length - 40) - val tokenId = Numeric.toBigInt(log.topics[3]) - return Triple(fromAddress, toAddress, tokenId) - } } diff --git a/src/main/kotlin/com/api/nft/service/external/dto/NftData.kt b/src/main/kotlin/com/api/nft/service/external/dto/NftData.kt index a9c18e7..372012f 100644 --- a/src/main/kotlin/com/api/nft/service/external/dto/NftData.kt +++ b/src/main/kotlin/com/api/nft/service/external/dto/NftData.kt @@ -10,8 +10,8 @@ data class NftData( @JsonProperty("token_address") val tokenAddress: String, @JsonProperty("contract_type") val contractType: String?, @JsonProperty("owner_of") val ownerOf: String?, - @JsonProperty("last_metadata_sync") val lastMetadataSync: String, - @JsonProperty("last_token_uri_sync") val lastTokenUriSync: String, + @JsonProperty("last_metadata_sync") val lastMetadataSync: String?, + @JsonProperty("last_token_uri_sync") val lastTokenUriSync: String?, val metadata: String, @JsonProperty("block_number") val blockNumber: String, @JsonProperty("block_number_minted") val blockNumberMinted: String?, diff --git a/src/main/kotlin/com/api/nft/service/external/dto/NftTransferData.kt b/src/main/kotlin/com/api/nft/service/external/dto/NftTransferData.kt new file mode 100644 index 0000000..34cd3b5 --- /dev/null +++ b/src/main/kotlin/com/api/nft/service/external/dto/NftTransferData.kt @@ -0,0 +1,36 @@ +package com.api.nft.service.external.dto + +import com.fasterxml.jackson.annotation.JsonProperty + +data class NftTransferData( + val page: String, + @JsonProperty("page_size")val pageSize: String, + val cursor: String, + val result: List, + @JsonProperty("block_exists") val blockExists: Boolean, + @JsonProperty("index_complete") val indexComplete: Boolean +) + +data class ResultData ( + @JsonProperty("token_address") val tokenAddress: String, + @JsonProperty("token_id") val tokenId: String, + @JsonProperty("from_address") val fromAddress: String, + @JsonProperty("from_address_label")val fromAddressLabel: String, + @JsonProperty("to_address") val toAddress: String, + @JsonProperty("to_address_label") val toAddressLabel: String, + val value: String, + val amount: String, + @JsonProperty("contract_type") val contractType: String, + @JsonProperty("block_number") val blockNumber: String, + @JsonProperty("block_timestamp") val blockTimestamp: String, + @JsonProperty("block_hash") val blockHash: String, + @JsonProperty("transaction_hash") val transactionHash: String, + @JsonProperty("transaction_type") val transactionType: String, + @JsonProperty("transaction_index") val transactionIndex: String, + @JsonProperty("log_index") val logIndex: String, + val operator: String, + @JsonProperty("possible_spam") val possibleSpam: String, + @JsonProperty("verified_collection") val verifiedCollection : String, + + +) diff --git a/src/main/kotlin/com/api/nft/service/external/moralis/MoralisApiService.kt b/src/main/kotlin/com/api/nft/service/external/moralis/MoralisApiService.kt index 2c60e28..63d1018 100644 --- a/src/main/kotlin/com/api/nft/service/external/moralis/MoralisApiService.kt +++ b/src/main/kotlin/com/api/nft/service/external/moralis/MoralisApiService.kt @@ -2,10 +2,13 @@ package com.api.nft.service.external.moralis import com.api.nft.enums.ChainType import com.api.nft.service.external.dto.NftData +import com.api.nft.service.external.dto.NftTransferData import org.springframework.http.MediaType import org.springframework.stereotype.Service import org.springframework.web.reactive.function.client.WebClient +import org.springframework.web.reactive.function.client.bodyToMono +import reactor.core.publisher.Flux import reactor.core.publisher.Mono @Service @@ -26,35 +29,22 @@ class MoralisApiService { return chain } - fun getNft(tokenAddress: String,tokenId: String,chainType: ChainType): Mono { + fun getNftTransfer(tokenAddress: String,tokenId: String,chainType: ChainType): Mono{ val chain = queryParamByChain(chainType) return webClient.get() .uri { - it.path("/v2.2/nft/${tokenAddress}/${tokenId}") + it.path("/v2.2/nft/${tokenAddress}/${tokenId}/transfers") it.queryParam("chain", chain) it.build() } .header("X-API-Key", apiKey) .header("Accept", MediaType.APPLICATION_JSON_VALUE) .retrieve() - .bodyToMono(NftData::class.java) + .bodyToMono(NftTransferData::class.java) } - fun getNft1(tokenAddress: String,tokenId: String,chainType: ChainType): Mono { - val chain = queryParamByChain(chainType) - return webClient.get() - .uri { - it.path("/v2.2/nft/${tokenAddress}/${tokenId}") - it.queryParam("chain", chain) - it.build() - } - .header("X-API-Key", apiKey) - .header("Accept", MediaType.APPLICATION_JSON_VALUE) - .retrieve() - .bodyToMono(String::class.java) - } companion object { private val apiKey = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJub25jZSI6ImJiNmIxMWJmLWNmNzItNDg0OC04OGEyLTBjYTIwODRjN2VhMyIsIm9yZ0lkIjoiMzgzODQwIiwidXNlcklkIjoiMzk0NDAyIiwidHlwZUlkIjoiMGZlYWQ5NDctZjQwZS00MDkwLWFlNGUtOTA1ZTdmMjUxZTAzIiwidHlwZSI6IlBST0pFQ1QiLCJpYXQiOjE3MTA5NTIwMjYsImV4cCI6NDg2NjcxMjAyNn0.VQE60IPGiWxdp7jKLF0jzXnxrLjEpU56H4bnfhMt0Sw" diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index e901756..720e225 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -28,4 +28,5 @@ server: port: 8082 apikey: - infura: "98b672d2ce9a4089a3a5cb5081dde2fa" \ No newline at end of file + infura: "98b672d2ce9a4089a3a5cb5081dde2fa" + moralis: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJub25jZSI6ImJiNmIxMWJmLWNmNzItNDg0OC04OGEyLTBjYTIwODRjN2VhMyIsIm9yZ0lkIjoiMzgzODQwIiwidXNlcklkIjoiMzk0NDAyIiwidHlwZUlkIjoiMGZlYWQ5NDctZjQwZS00MDkwLWFlNGUtOTA1ZTdmMjUxZTAzIiwidHlwZSI6IlBST0pFQ1QiLCJpYXQiOjE3MTA5NTIwMjYsImV4cCI6NDg2NjcxMjAyNn0.VQE60IPGiWxdp7jKLF0jzXnxrLjEpU56H4bnfhMt0Sw" \ No newline at end of file diff --git a/src/main/resources/db/postgresql/migration/V1__Initial_schema.sql b/src/main/resources/db/postgresql/migration/V1__Initial_schema.sql index 24fd046..bd97b42 100644 --- a/src/main/resources/db/postgresql/migration/V1__Initial_schema.sql +++ b/src/main/resources/db/postgresql/migration/V1__Initial_schema.sql @@ -1,7 +1,8 @@ CREATE TABLE IF NOT EXISTS collection ( name varchar(500) PRIMARY KEY, logo varchar(500), - banner_image varchar(500) + banner_image varchar(500), + description varchar(1000) ); CREATE TABLE IF NOT EXISTS nft ( diff --git a/src/test/kotlin/com/api/nft/NftTest.kt b/src/test/kotlin/com/api/nft/NftTest.kt index 89a7ba9..8117bcf 100644 --- a/src/test/kotlin/com/api/nft/NftTest.kt +++ b/src/test/kotlin/com/api/nft/NftTest.kt @@ -5,6 +5,7 @@ import com.api.nft.enums.ChainType import com.api.nft.rabbitMQ.RabbitMQSender import com.api.nft.service.external.moralis.MoralisApiService import com.api.nft.service.api.NftService +import com.api.nft.service.api.TransferService import org.junit.jupiter.api.Test import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.context.SpringBootTest @@ -15,13 +16,14 @@ class NftTest( @Autowired private val nftService: NftService, @Autowired private val collectionRepository: CollectionRepository, @Autowired private val rabbitMQSender: RabbitMQSender, + @Autowired private val transferService: TransferService, ) { @Test fun test(){ val tokenAddress = "0x57a133561c74c242a0b70af9c129126244b4869f" val tokenId = "4733" - val res =moralisApiService.getNft(tokenAddress,tokenId, ChainType.POLYGON_MAINNET).block() + val res =moralisApiService.getNftTransfer(tokenAddress,tokenId, ChainType.POLYGON_MAINNET).block() println(res) } @@ -42,12 +44,6 @@ class NftTest( // nftService.createNftProcess(tokenId,tokenAddress, ChainType.POLYGON_MAINNET).block() // } - @Test - fun test3() { -// val tokenName = "asdasdasd" -// collectionRepository.insert(Collection(tokenName)).block() - rabbitMQSender.nftsend("nmftasd") - } // @Test // fun test4() { From 96225dc3b22e06d772d0a455b2882d9d6d07dbcd Mon Sep 17 00:00:00 2001 From: min-96 Date: Fri, 10 May 2024 01:57:47 +0900 Subject: [PATCH 08/38] feat: add Transaction --- .../com/api/nft/domain/trasfer/Transfer.kt | 2 + .../com/api/nft/service/api/NftService.kt | 2 +- .../api/nft/service/api/TransferService.kt | 63 +++++++++---------- .../service/external/dto/NftTransferData.kt | 2 - src/main/kotlin/com/api/nft/util/Util.kt | 13 ++++ .../migration/V1__Initial_schema.sql | 4 +- 6 files changed, 48 insertions(+), 38 deletions(-) diff --git a/src/main/kotlin/com/api/nft/domain/trasfer/Transfer.kt b/src/main/kotlin/com/api/nft/domain/trasfer/Transfer.kt index 87d735b..4e0042e 100644 --- a/src/main/kotlin/com/api/nft/domain/trasfer/Transfer.kt +++ b/src/main/kotlin/com/api/nft/domain/trasfer/Transfer.kt @@ -10,6 +10,8 @@ class Transfer( @Column("nft_id") val nftId: Long, @Column("from_address") val fromAddress: String, @Column("to_address") val toAddress: String, + @Column("block_number") val blockNumber: Long, + @Column("block_timestamp") val blockTimestamp: Long, ) { diff --git a/src/main/kotlin/com/api/nft/service/api/NftService.kt b/src/main/kotlin/com/api/nft/service/api/NftService.kt index 1dece0f..6cf0344 100644 --- a/src/main/kotlin/com/api/nft/service/api/NftService.kt +++ b/src/main/kotlin/com/api/nft/service/api/NftService.kt @@ -57,7 +57,7 @@ class NftService( )) .then(Mono.just(nft)) .doOnSuccess { eventPublisher.publishEvent(NftCreatedEvent(this, nft)) } -// .doOnSuccess { transferService.createTransfer(nft) } + .doOnSuccess { transferService.createTransfer(nft) } } } diff --git a/src/main/kotlin/com/api/nft/service/api/TransferService.kt b/src/main/kotlin/com/api/nft/service/api/TransferService.kt index c98e1a8..f0194a8 100644 --- a/src/main/kotlin/com/api/nft/service/api/TransferService.kt +++ b/src/main/kotlin/com/api/nft/service/api/TransferService.kt @@ -3,25 +3,12 @@ package com.api.nft.service.api import com.api.nft.domain.nft.Nft import com.api.nft.domain.trasfer.Transfer import com.api.nft.domain.trasfer.TransferRepository -import com.api.nft.enums.ChainType -import com.api.nft.properties.ApiKeysProperties -import com.api.nft.service.TransferResponse -import com.api.nft.service.external.dto.NftTransferData +import com.api.nft.service.external.dto.ResultData import com.api.nft.service.external.moralis.MoralisApiService import com.api.nft.util.Util.convertNetworkTypeToChainType +import com.api.nft.util.Util.toEpochMilli import org.springframework.stereotype.Service -import org.web3j.abi.EventEncoder -import org.web3j.abi.TypeReference -import org.web3j.abi.datatypes.Address -import org.web3j.abi.datatypes.Event -import org.web3j.abi.datatypes.generated.Uint256 -import org.web3j.protocol.Web3j -import org.web3j.protocol.core.DefaultBlockParameterName -import org.web3j.protocol.core.methods.request.EthFilter -import org.web3j.protocol.core.methods.response.Log -import org.web3j.protocol.http.HttpService -import org.web3j.utils.Numeric -import java.math.BigInteger +import reactor.core.publisher.Flux @Service @@ -30,24 +17,32 @@ class TransferService( private val transferRepository: TransferRepository, ) { - - fun createTransfer(nft: Nft) - { - moralisApiService.getNftTransfer( - nft.tokenAddress, - nft.tokenId, - nft.chinType.convertNetworkTypeToChainType() - ).map { - // transferRepository.save(it.toEntity(nft.id!!)) - } - } - -// private fun NftTransferData.toEntity(nftId: Long) = -// Transfer( -// nftId = nftId, -// fromAddress = this.result, -// toAddress = this.to -// ) + fun createTransfer(nft: Nft) { + moralisApiService.getNftTransfer( + nft.tokenAddress, + nft.tokenId, + nft.chinType.convertNetworkTypeToChainType() + ).flatMapMany { + Flux.fromIterable(it.result) + }.map { + it.toEntity(nft.id!!) + }.flatMap { transfer -> + transferRepository.save(transfer) + }.doOnError { error -> + println("Error during transfer creation: $error") + }.then() + .subscribe() + } + + + private fun ResultData.toEntity(nftId: Long): Transfer = + Transfer( + nftId = nftId, + fromAddress = this.fromAddress, + toAddress = this.toAddress, + blockNumber = this.blockNumber.toLong(), + blockTimestamp = this.blockTimestamp.toEpochMilli() + ) } diff --git a/src/main/kotlin/com/api/nft/service/external/dto/NftTransferData.kt b/src/main/kotlin/com/api/nft/service/external/dto/NftTransferData.kt index 34cd3b5..b50c3d6 100644 --- a/src/main/kotlin/com/api/nft/service/external/dto/NftTransferData.kt +++ b/src/main/kotlin/com/api/nft/service/external/dto/NftTransferData.kt @@ -31,6 +31,4 @@ data class ResultData ( val operator: String, @JsonProperty("possible_spam") val possibleSpam: String, @JsonProperty("verified_collection") val verifiedCollection : String, - - ) diff --git a/src/main/kotlin/com/api/nft/util/Util.kt b/src/main/kotlin/com/api/nft/util/Util.kt index 9530fa8..44fafb7 100644 --- a/src/main/kotlin/com/api/nft/util/Util.kt +++ b/src/main/kotlin/com/api/nft/util/Util.kt @@ -1,6 +1,8 @@ package com.api.nft.util import com.api.nft.enums.ChainType +import java.time.Instant +import java.time.format.DateTimeFormatter object Util { fun String.convertNetworkTypeToChainType(): ChainType { @@ -11,4 +13,15 @@ object Util { } } + fun timestampToString(timestamp: Long): String { + val instant = Instant.ofEpochMilli(timestamp) + return DateTimeFormatter.ISO_INSTANT.format(instant) + } + + fun Long.toIsoString(): String = DateTimeFormatter.ISO_INSTANT.format(Instant.ofEpochMilli(this)) + + fun String.toEpochMilli(): Long { + return Instant.parse(this).toEpochMilli() + } + } \ No newline at end of file diff --git a/src/main/resources/db/postgresql/migration/V1__Initial_schema.sql b/src/main/resources/db/postgresql/migration/V1__Initial_schema.sql index bd97b42..56a2851 100644 --- a/src/main/resources/db/postgresql/migration/V1__Initial_schema.sql +++ b/src/main/resources/db/postgresql/migration/V1__Initial_schema.sql @@ -39,7 +39,9 @@ CREATE TABLE IF NOT EXISTS transfer ( id SERIAL PRIMARY KEY, nft_id BIGINT REFERENCES nft(id), from_address varchar(255) not null, - to_address varchar(255) not null + to_address varchar(255) not null, + block_number bigint not null, + block_timestamp bigint not null ); From 4fb168e60288950899821474dbef7e46ff4d1bac Mon Sep 17 00:00:00 2001 From: min-96 Date: Mon, 13 May 2024 01:42:21 +0900 Subject: [PATCH 09/38] feat: update Transfer --- .../domain/nft/repository/NftRepository.kt | 4 +- .../com/api/nft/domain/trasfer/Transfer.kt | 2 +- .../nft/domain/trasfer/TransferRepository.kt | 3 + .../com/api/nft/service/api/NftService.kt | 11 +- .../api/nft/service/api/TransferService.kt | 61 ++++++++++- .../nft/service/external/dto/EthLogRequest.kt | 9 ++ .../service/external/dto/EthLogResponse.kt | 36 ++++++ .../nft/service/external/dto/InfuraRequest.kt | 8 ++ .../service/external/dto/InfuraResponse.kt | 14 +++ .../service/external/dto/NftTransferData.kt | 8 +- .../external/infura/InfuraApiService.kt | 103 ++++++++++++++++++ .../external/moralis/MoralisApiService.kt | 5 +- src/main/kotlin/com/api/nft/util/Util.kt | 4 +- src/test/kotlin/com/api/nft/NftTest.kt | 67 +++++++----- 14 files changed, 288 insertions(+), 47 deletions(-) create mode 100644 src/main/kotlin/com/api/nft/service/external/dto/EthLogRequest.kt create mode 100644 src/main/kotlin/com/api/nft/service/external/dto/EthLogResponse.kt create mode 100644 src/main/kotlin/com/api/nft/service/external/dto/InfuraRequest.kt create mode 100644 src/main/kotlin/com/api/nft/service/external/dto/InfuraResponse.kt create mode 100644 src/main/kotlin/com/api/nft/service/external/infura/InfuraApiService.kt diff --git a/src/main/kotlin/com/api/nft/domain/nft/repository/NftRepository.kt b/src/main/kotlin/com/api/nft/domain/nft/repository/NftRepository.kt index e385a01..a0f44b4 100644 --- a/src/main/kotlin/com/api/nft/domain/nft/repository/NftRepository.kt +++ b/src/main/kotlin/com/api/nft/domain/nft/repository/NftRepository.kt @@ -4,9 +4,7 @@ import com.api.nft.domain.nft.Nft import org.springframework.data.repository.reactive.ReactiveCrudRepository import reactor.core.publisher.Mono -interface NftRepository : NftRepositorySupport, ReactiveCrudRepository { +interface NftRepository : ReactiveCrudRepository, NftRepositorySupport { fun findByTokenAddressAndTokenId(tokenAddress: String, tokenId: String): Mono - - } \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/domain/trasfer/Transfer.kt b/src/main/kotlin/com/api/nft/domain/trasfer/Transfer.kt index 4e0042e..01e9ea8 100644 --- a/src/main/kotlin/com/api/nft/domain/trasfer/Transfer.kt +++ b/src/main/kotlin/com/api/nft/domain/trasfer/Transfer.kt @@ -11,7 +11,7 @@ class Transfer( @Column("from_address") val fromAddress: String, @Column("to_address") val toAddress: String, @Column("block_number") val blockNumber: Long, - @Column("block_timestamp") val blockTimestamp: Long, + @Column("block_timestamp") val blockTimestamp: Long?, ) { diff --git a/src/main/kotlin/com/api/nft/domain/trasfer/TransferRepository.kt b/src/main/kotlin/com/api/nft/domain/trasfer/TransferRepository.kt index ee7be38..458104b 100644 --- a/src/main/kotlin/com/api/nft/domain/trasfer/TransferRepository.kt +++ b/src/main/kotlin/com/api/nft/domain/trasfer/TransferRepository.kt @@ -1,6 +1,9 @@ package com.api.nft.domain.trasfer import org.springframework.data.repository.reactive.ReactiveCrudRepository +import reactor.core.publisher.Flux interface TransferRepository: ReactiveCrudRepository { + + fun findByNftIdOrderByBlockTimestampDesc(nftId: Long) : Flux } \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/service/api/NftService.kt b/src/main/kotlin/com/api/nft/service/api/NftService.kt index 6cf0344..ebbe929 100644 --- a/src/main/kotlin/com/api/nft/service/api/NftService.kt +++ b/src/main/kotlin/com/api/nft/service/api/NftService.kt @@ -43,9 +43,7 @@ class NftService( } } - fun createNftProcess(request: NftData, - chainType: ChainType - ): Mono { + fun createNftProcess(request: NftData, chainType: ChainType): Mono { val response = getNftData(request, chainType) return response.flatMap { (nftData, metadataData, attributeDataList) -> createNft(nftData, metadataData, chainType) @@ -57,8 +55,9 @@ class NftService( )) .then(Mono.just(nft)) .doOnSuccess { eventPublisher.publishEvent(NftCreatedEvent(this, nft)) } - .doOnSuccess { transferService.createTransfer(nft) } - + .flatMap { + transferService.createTransfer(nft).thenReturn(nft) + } } } } @@ -67,7 +66,7 @@ class NftService( fun getNftData(request: NftData, chainType: ChainType): Mono?>> { return Mono.fromCallable { val metadata = MetadataData.toMetadataResponse(request.metadata) - val attributes = metadata?.attributes?.let { + val attributes = metadata.attributes.let { if (it.isNotEmpty()) AttributeData.toAttributeResponse(it) else null } Triple(request, metadata, attributes) diff --git a/src/main/kotlin/com/api/nft/service/api/TransferService.kt b/src/main/kotlin/com/api/nft/service/api/TransferService.kt index f0194a8..4d0c4fe 100644 --- a/src/main/kotlin/com/api/nft/service/api/TransferService.kt +++ b/src/main/kotlin/com/api/nft/service/api/TransferService.kt @@ -1,24 +1,32 @@ package com.api.nft.service.api import com.api.nft.domain.nft.Nft +import com.api.nft.domain.nft.repository.NftRepository import com.api.nft.domain.trasfer.Transfer import com.api.nft.domain.trasfer.TransferRepository +import com.api.nft.enums.ChainType +import com.api.nft.service.external.dto.EthLogResponse import com.api.nft.service.external.dto.ResultData +import com.api.nft.service.external.infura.InfuraApiService import com.api.nft.service.external.moralis.MoralisApiService import com.api.nft.util.Util.convertNetworkTypeToChainType import com.api.nft.util.Util.toEpochMilli import org.springframework.stereotype.Service import reactor.core.publisher.Flux +import reactor.core.publisher.Mono +import java.math.BigInteger @Service class TransferService( private val moralisApiService: MoralisApiService, private val transferRepository: TransferRepository, + private val nftRepository: NftRepository, + private val infuraApiService: InfuraApiService, ) { - fun createTransfer(nft: Nft) { - moralisApiService.getNftTransfer( + fun createTransfer(nft: Nft): Mono { + return moralisApiService.getNftTransfer( nft.tokenAddress, nft.tokenId, nft.chinType.convertNetworkTypeToChainType() @@ -31,10 +39,7 @@ class TransferService( }.doOnError { error -> println("Error during transfer creation: $error") }.then() - .subscribe() } - - private fun ResultData.toEntity(nftId: Long): Transfer = Transfer( nftId = nftId, @@ -44,5 +49,51 @@ class TransferService( blockTimestamp = this.blockTimestamp.toEpochMilli() ) + fun findOrUpdateByNftId(nftId: Long): Flux { + return nftRepository.findById(nftId).flatMapMany { nft -> + transferRepository.findByNftIdOrderByBlockTimestampDesc(nftId).next() + .flatMapMany { + updateByTransfer(it,nft) + .thenMany( transferRepository.findByNftIdOrderByBlockTimestampDesc(nftId)) + } + } + } + + fun updateByTransfer(transfer: Transfer, nft: Nft): Mono { + val fromBlock = transfer.blockNumber + 1 + return infuraApiService.getEthLogs( + fromBlock.toString(), + nft.chinType.convertNetworkTypeToChainType(), + nft.tokenAddress, + nft.tokenId + ).flatMapMany { ethLogResponses -> + Flux.fromIterable(ethLogResponses) + }.flatMap { ethLogResponse -> + ethLogResponse.toTransferEntity(nft.id!!, nft.chinType.convertNetworkTypeToChainType()) + .flatMap { + transferRepository.save(it) + } + }.then() + } + + + private fun EthLogResponse.toTransferEntity(nftId: Long, chainType: ChainType): Mono { + return infuraApiService.getBlockTimestamp(blockNumber, chainType) + .map { timestamp -> + Transfer( + toAddress = parseAddress(topics[2]), + fromAddress = parseAddress(topics[1]), blockNumber = BigInteger(blockNumber.substring(2), 16).toLong(), + nftId = nftId, + blockTimestamp = timestamp + ) + } + } + + + + private fun parseAddress(address: String): String { + return "0x" + address.substring(26).padStart(40, '0') + } + } diff --git a/src/main/kotlin/com/api/nft/service/external/dto/EthLogRequest.kt b/src/main/kotlin/com/api/nft/service/external/dto/EthLogRequest.kt new file mode 100644 index 0000000..f072acf --- /dev/null +++ b/src/main/kotlin/com/api/nft/service/external/dto/EthLogRequest.kt @@ -0,0 +1,9 @@ +package com.api.nft.service.external.dto + +data class EthLogRequest( + val fromBlock: String, + val toBlock: String, + val address: String, + val topics: List, +) + diff --git a/src/main/kotlin/com/api/nft/service/external/dto/EthLogResponse.kt b/src/main/kotlin/com/api/nft/service/external/dto/EthLogResponse.kt new file mode 100644 index 0000000..ce41176 --- /dev/null +++ b/src/main/kotlin/com/api/nft/service/external/dto/EthLogResponse.kt @@ -0,0 +1,36 @@ +package com.api.nft.service.external.dto + +import com.api.nft.domain.trasfer.Transfer +import java.math.BigInteger + +data class EthLogResponse( + val address: String, + val blockHash: String, + val blockNumber: String, + val data: String, + val logIndex: String, + val topics: List, + val transactionHash: String, + val transactionIndex: String, +){ + companion object{ + + fun toResponse(map: Map): EthLogResponse { + return EthLogResponse( + address = map["address"] as String, + blockHash = map["blockHash"] as String, + blockNumber = map["blockNumber"] as String, + data = map["data"] as String, + logIndex = map["logIndex"] as String, + topics = map["topics"] as List, + transactionHash = map["transactionHash"] as String, + transactionIndex = map["transactionIndex"] as String + ) + } + } + + private fun parseAddress(address: String): String { + return "0x" + address.substring(26).padStart(40, '0') + } +} + diff --git a/src/main/kotlin/com/api/nft/service/external/dto/InfuraRequest.kt b/src/main/kotlin/com/api/nft/service/external/dto/InfuraRequest.kt new file mode 100644 index 0000000..2ecfee0 --- /dev/null +++ b/src/main/kotlin/com/api/nft/service/external/dto/InfuraRequest.kt @@ -0,0 +1,8 @@ +package com.api.nft.service.external.dto + +data class InfuraRequest( + val jsonrpc: String = "2.0", + val method: String, + val params: List = emptyList(), + val id: Int = 1 +) diff --git a/src/main/kotlin/com/api/nft/service/external/dto/InfuraResponse.kt b/src/main/kotlin/com/api/nft/service/external/dto/InfuraResponse.kt new file mode 100644 index 0000000..362e547 --- /dev/null +++ b/src/main/kotlin/com/api/nft/service/external/dto/InfuraResponse.kt @@ -0,0 +1,14 @@ +package com.api.nft.service.external.dto + +data class InfuraResponse( + val jsonrpc: String, + val id: Int, + val result: List>, +) { + + fun toEthLogResponses(): List { + return result.map { EthLogResponse.toResponse(it) } + } + +} + diff --git a/src/main/kotlin/com/api/nft/service/external/dto/NftTransferData.kt b/src/main/kotlin/com/api/nft/service/external/dto/NftTransferData.kt index b50c3d6..0726e7a 100644 --- a/src/main/kotlin/com/api/nft/service/external/dto/NftTransferData.kt +++ b/src/main/kotlin/com/api/nft/service/external/dto/NftTransferData.kt @@ -5,7 +5,7 @@ import com.fasterxml.jackson.annotation.JsonProperty data class NftTransferData( val page: String, @JsonProperty("page_size")val pageSize: String, - val cursor: String, + val cursor: String?, val result: List, @JsonProperty("block_exists") val blockExists: Boolean, @JsonProperty("index_complete") val indexComplete: Boolean @@ -15,9 +15,9 @@ data class ResultData ( @JsonProperty("token_address") val tokenAddress: String, @JsonProperty("token_id") val tokenId: String, @JsonProperty("from_address") val fromAddress: String, - @JsonProperty("from_address_label")val fromAddressLabel: String, + @JsonProperty("from_address_label")val fromAddressLabel: String?, @JsonProperty("to_address") val toAddress: String, - @JsonProperty("to_address_label") val toAddressLabel: String, + @JsonProperty("to_address_label") val toAddressLabel: String?, val value: String, val amount: String, @JsonProperty("contract_type") val contractType: String, @@ -28,7 +28,7 @@ data class ResultData ( @JsonProperty("transaction_type") val transactionType: String, @JsonProperty("transaction_index") val transactionIndex: String, @JsonProperty("log_index") val logIndex: String, - val operator: String, + val operator: String?, @JsonProperty("possible_spam") val possibleSpam: String, @JsonProperty("verified_collection") val verifiedCollection : String, ) diff --git a/src/main/kotlin/com/api/nft/service/external/infura/InfuraApiService.kt b/src/main/kotlin/com/api/nft/service/external/infura/InfuraApiService.kt new file mode 100644 index 0000000..3523a8c --- /dev/null +++ b/src/main/kotlin/com/api/nft/service/external/infura/InfuraApiService.kt @@ -0,0 +1,103 @@ +package com.api.nft.service.external.infura + +import com.api.nft.enums.ChainType +import com.api.nft.service.external.dto.EthLogRequest +import com.api.nft.service.external.dto.EthLogResponse +import com.api.nft.service.external.dto.InfuraRequest +import com.api.nft.service.external.dto.InfuraResponse +import org.springframework.http.MediaType +import org.springframework.stereotype.Service +import org.springframework.web.reactive.function.client.WebClient +import org.web3j.abi.EventEncoder +import org.web3j.abi.TypeReference +import org.web3j.abi.datatypes.Address +import org.web3j.abi.datatypes.Event +import org.web3j.abi.datatypes.generated.Uint256 +import org.web3j.utils.Numeric +import reactor.core.publisher.Mono +import java.math.BigInteger +import java.time.Instant + + +@Service +class InfuraApiService { + + private fun urlByChain(chainType: ChainType) : WebClient { + val baseUrl = when (chainType) { + ChainType.ETHEREUM_MAINNET -> "https://mainnet.infura.io" + ChainType.POLYGON_MAINNET -> "https://polygon-mainnet.infura.io" + ChainType.ETHREUM_GOERLI -> "https://goerli.infura.io" + ChainType.ETHREUM_SEPOLIA -> "https://sepolia.infura.io" + ChainType.POLYGON_MUMBAI -> "https://polygon-mumbai.infura.io" + } + return WebClient.builder() + .baseUrl(baseUrl) + .build() + } + + + fun getEthLogs( + fromBlock: String, + chainType: ChainType, + tokenAddress: String, + tokenId: String, + ): Mono>{ + + val transferEvent = Event( + "Transfer", + listOf( + TypeReference.create(Address::class.java, true), + TypeReference.create(Address::class.java, true), + TypeReference.create(Uint256::class.java, true), + ) + ) + + val eventSignature = EventEncoder.encode(transferEvent) + val topicTokenId = Numeric.toHexStringWithPrefixZeroPadded(BigInteger(tokenId), 64) + val toHexFromBlock = Numeric.toHexStringWithPrefix(BigInteger(fromBlock)) + + + val requestBody = InfuraRequest( + method = "eth_getLogs", + params = listOf(EthLogRequest( + toHexFromBlock, + "latest", + tokenAddress, + listOf(eventSignature, null, null, topicTokenId))) + ) + val webClient = urlByChain(chainType) + + return webClient.post() + .uri("/v3/$apiKey") + .contentType(MediaType.APPLICATION_JSON) + .bodyValue(requestBody) + .retrieve() + .bodyToMono(InfuraResponse::class.java) + .mapNotNull { it.toEthLogResponses() } + + } + + fun getBlockTimestamp(blockNumber: String, chainType: ChainType): Mono { + val webClient = urlByChain(chainType) + val requestBody = InfuraRequest( + method = "eth_getBlockByNumber", + params = listOf(blockNumber, false), + ) + + return webClient.post() + .uri("/v3/$apiKey") + .contentType(MediaType.APPLICATION_JSON) + .bodyValue(requestBody) + .retrieve() + .bodyToMono(Map::class.java) + .mapNotNull { response -> + val result = response["result"] as Map + val timestamp = result["timestamp"].toString() + Instant.ofEpochSecond(Numeric.decodeQuantity(timestamp).longValueExact()).toEpochMilli() + } + } + + companion object{ + private val apiKey = "98b672d2ce9a4089a3a5cb5081dde2fa" + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/service/external/moralis/MoralisApiService.kt b/src/main/kotlin/com/api/nft/service/external/moralis/MoralisApiService.kt index 63d1018..183c0cd 100644 --- a/src/main/kotlin/com/api/nft/service/external/moralis/MoralisApiService.kt +++ b/src/main/kotlin/com/api/nft/service/external/moralis/MoralisApiService.kt @@ -29,7 +29,10 @@ class MoralisApiService { return chain } - fun getNftTransfer(tokenAddress: String,tokenId: String,chainType: ChainType): Mono{ + fun getNftTransfer(tokenAddress: String, + tokenId: String, + chainType: ChainType + ): Mono{ val chain = queryParamByChain(chainType) return webClient.get() diff --git a/src/main/kotlin/com/api/nft/util/Util.kt b/src/main/kotlin/com/api/nft/util/Util.kt index 44fafb7..657a855 100644 --- a/src/main/kotlin/com/api/nft/util/Util.kt +++ b/src/main/kotlin/com/api/nft/util/Util.kt @@ -7,8 +7,8 @@ import java.time.format.DateTimeFormatter object Util { fun String.convertNetworkTypeToChainType(): ChainType { return when (this) { - "ETHEREUM" -> ChainType.ETHEREUM_MAINNET - "POLYGON" -> ChainType.POLYGON_MAINNET + "ETHEREUM_MAINNET" -> ChainType.ETHEREUM_MAINNET + "POLYGON_MAINNET" -> ChainType.POLYGON_MAINNET else -> throw IllegalArgumentException("Unknown network type: $this") } } diff --git a/src/test/kotlin/com/api/nft/NftTest.kt b/src/test/kotlin/com/api/nft/NftTest.kt index 8117bcf..a77c701 100644 --- a/src/test/kotlin/com/api/nft/NftTest.kt +++ b/src/test/kotlin/com/api/nft/NftTest.kt @@ -1,14 +1,20 @@ package com.api.nft import com.api.nft.domain.collection.repository.CollectionRepository +import com.api.nft.domain.nft.repository.NftRepository +import com.api.nft.domain.trasfer.Transfer import com.api.nft.enums.ChainType import com.api.nft.rabbitMQ.RabbitMQSender import com.api.nft.service.external.moralis.MoralisApiService import com.api.nft.service.api.NftService import com.api.nft.service.api.TransferService +import com.api.nft.service.external.dto.EthLogResponse +import com.api.nft.service.external.infura.InfuraApiService import org.junit.jupiter.api.Test import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.context.SpringBootTest +import reactor.test.StepVerifier +import java.math.BigInteger @SpringBootTest class NftTest( @@ -17,6 +23,8 @@ class NftTest( @Autowired private val collectionRepository: CollectionRepository, @Autowired private val rabbitMQSender: RabbitMQSender, @Autowired private val transferService: TransferService, + @Autowired private val nftRepository: NftRepository, + @Autowired private val infuraApiService: InfuraApiService, ) { @Test @@ -27,29 +35,38 @@ class NftTest( println(res) } -// @Test -// fun test1() { -// val tokenAddress = "0x57a133561c74c242a0b70af9c129126244b4869f" -// val tokenId = "4733" -// val res =nftService.getNftByMoralis(tokenId,tokenAddress, ChainType.POLYGON_MAINNET).block() -// println(res?.first.toString()) -// println(res?.second.toString()) -// println(res?.third.toString()) -// } -// -// @Test -// fun test2() { -// val tokenAddress = "0xe7900239e9332060dc975ed6f0cc5f0129d924cf" -// val tokenId = "3" -// nftService.createNftProcess(tokenId,tokenAddress, ChainType.POLYGON_MAINNET).block() -// } - - -// @Test -// fun test4() { -// val tokenAddress = "0xe4a8bfdc0684f62b4cfb43165021814f059819ef" -// val tokenId = "4617" -// val res = nftService.findOrCreateNft(tokenId,tokenAddress,ChainType.POLYGON_MAINNET).block() -// println(res.toString()) -// } + @Test + fun createTransfer() { + val nft = nftRepository.findById(25).block() + nft?.let { + transferService.createTransfer(it).block() + } ?: error("NFT not found") + } + + @Test + fun createTransferTest1() { + val nftMono = nftRepository.findById(22) + + StepVerifier.create(nftMono) + .assertNext { nft -> + transferService.createTransfer(nft) + } + .verifyComplete() + } + + @Test + fun getEth() { + val res = infuraApiService.getEthLogs("56498172",ChainType.POLYGON_MAINNET,"0xa3784fe9104fdc0b988769fba7459ece2fb36eea","1").block() + println(res.toString()) + } + + + @Test + fun findOrUpdateByNftId() { + //56498172 + transferService.findOrUpdateByNftId(31).blockLast() + } + + + } \ No newline at end of file From 76f99019b365ea1cf415120b05b1d8788f19ba3d Mon Sep 17 00:00:00 2001 From: min-96 Date: Thu, 16 May 2024 14:42:54 +0900 Subject: [PATCH 10/38] fix: apply bean to RabbitMQSender --- .../kotlin/com/api/nft/config/RabbitConfig.kt | 12 +++++++ .../com/api/nft/controller/NftController.kt | 16 ++++++++- .../com/api/nft/event/NftCreatedEvent.kt | 4 +-- .../com/api/nft/event/NftEventListener.kt | 5 ++- .../com/api/nft/rabbitMQ/RabbitMQSender.kt | 6 ++-- .../com/api/nft/service/api/NftService.kt | 12 +++++-- .../api/nft/service/api/TransferService.kt | 2 -- .../com/api/nft/service/dto/NftResponse.kt | 10 ++++++ src/test/kotlin/com/api/nft/NftTest.kt | 35 +++++++++++++++++++ 9 files changed, 90 insertions(+), 12 deletions(-) create mode 100644 src/main/kotlin/com/api/nft/service/dto/NftResponse.kt diff --git a/src/main/kotlin/com/api/nft/config/RabbitConfig.kt b/src/main/kotlin/com/api/nft/config/RabbitConfig.kt index d72250f..66a4c96 100644 --- a/src/main/kotlin/com/api/nft/config/RabbitConfig.kt +++ b/src/main/kotlin/com/api/nft/config/RabbitConfig.kt @@ -3,6 +3,9 @@ package com.api.nft.config import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import org.springframework.amqp.core.* +import org.springframework.amqp.rabbit.connection.ConnectionFactory +import org.springframework.amqp.rabbit.core.RabbitTemplate +import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter @Configuration class RabbitConfig { @@ -21,4 +24,13 @@ class RabbitConfig { return BindingBuilder.bind(nftQueue).to(nftExchange).with("nftRoutingKey") } + @Bean + fun jsonMessageConverter(): Jackson2JsonMessageConverter = Jackson2JsonMessageConverter() + + @Bean + fun rabbitTemplate(connectionFactory: ConnectionFactory, jsonMessageConverter: Jackson2JsonMessageConverter): RabbitTemplate { + val template = RabbitTemplate(connectionFactory) + template.messageConverter = jsonMessageConverter + return template + } } \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/controller/NftController.kt b/src/main/kotlin/com/api/nft/controller/NftController.kt index 7b2da59..ffa270c 100644 --- a/src/main/kotlin/com/api/nft/controller/NftController.kt +++ b/src/main/kotlin/com/api/nft/controller/NftController.kt @@ -1,9 +1,13 @@ package com.api.nft.controller import com.api.nft.domain.nft.repository.NftMetadataDto +import com.api.nft.domain.trasfer.Transfer import com.api.nft.enums.ChainType import com.api.nft.service.api.NftService +import com.api.nft.service.api.TransferService import com.api.nft.service.external.dto.NftData +import org.springframework.http.HttpStatus +import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.PostMapping @@ -12,17 +16,18 @@ import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RestController import reactor.core.publisher.Flux +import reactor.core.publisher.Mono //TODO("responseEntity로 반환) @RestController @RequestMapping("/v1/nft") class NftController( private val nftService: NftService, + private val transferService: TransferService, ) { @PostMapping("/save/{chainType}") fun save(@PathVariable chainType: ChainType,@RequestBody requests: List): Flux { - println("들어오는건 맞나요?") return nftService.saveNfts(requests,chainType) } @@ -31,4 +36,13 @@ class NftController( fun getAllByIds(@RequestParam nftIds: List): Flux { return nftService.findAllById(nftIds) } + + @GetMapping("/transfer") + fun getTransfers(@RequestParam nftId: Long) : Mono>> { + return transferService.findOrUpdateByNftId(nftId) + .collectList() + .map { ResponseEntity.ok(it) } + .defaultIfEmpty( ResponseEntity.notFound().build()) + .onErrorResume { Mono.just(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build()) } + } } \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/event/NftCreatedEvent.kt b/src/main/kotlin/com/api/nft/event/NftCreatedEvent.kt index 6170199..35d0373 100644 --- a/src/main/kotlin/com/api/nft/event/NftCreatedEvent.kt +++ b/src/main/kotlin/com/api/nft/event/NftCreatedEvent.kt @@ -1,7 +1,7 @@ package com.api.nft.event -import com.api.nft.domain.nft.Nft +import com.api.nft.service.dto.NftResponse import org.springframework.context.ApplicationEvent -data class NftCreatedEvent(val eventSource: Any, val nft: Nft): ApplicationEvent(eventSource) +data class NftCreatedEvent(val eventSource: Any, val nft: NftResponse): ApplicationEvent(eventSource) diff --git a/src/main/kotlin/com/api/nft/event/NftEventListener.kt b/src/main/kotlin/com/api/nft/event/NftEventListener.kt index 12fc17d..7f642b0 100644 --- a/src/main/kotlin/com/api/nft/event/NftEventListener.kt +++ b/src/main/kotlin/com/api/nft/event/NftEventListener.kt @@ -2,13 +2,16 @@ package com.api.nft.event import com.api.nft.rabbitMQ.RabbitMQSender import org.springframework.context.event.EventListener +import org.springframework.stereotype.Component +@Component class NftEventListener( private val provider: RabbitMQSender ){ @EventListener fun onNftCreated(event: NftCreatedEvent) { - provider.nftSend(event.nft.id!!) + println("메세지 오는가?") + provider.nftSend(event.nft) } } \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/rabbitMQ/RabbitMQSender.kt b/src/main/kotlin/com/api/nft/rabbitMQ/RabbitMQSender.kt index 24030c7..613cfea 100644 --- a/src/main/kotlin/com/api/nft/rabbitMQ/RabbitMQSender.kt +++ b/src/main/kotlin/com/api/nft/rabbitMQ/RabbitMQSender.kt @@ -1,6 +1,6 @@ package com.api.nft.rabbitMQ -import com.api.nft.domain.nft.Nft +import com.api.nft.service.dto.NftResponse import org.springframework.amqp.rabbit.core.RabbitTemplate import org.springframework.stereotype.Service @@ -9,9 +9,7 @@ class RabbitMQSender( private val rabbitTemplate: RabbitTemplate ) { - //TODO("리팩토링") - //TODO("반환값 재정의") - fun nftSend(nft: Long) { + fun nftSend(nft: NftResponse) { rabbitTemplate.convertAndSend("nftExchange", "nftRoutingKey", nft) } } \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/service/api/NftService.kt b/src/main/kotlin/com/api/nft/service/api/NftService.kt index ebbe929..9ecfd7c 100644 --- a/src/main/kotlin/com/api/nft/service/api/NftService.kt +++ b/src/main/kotlin/com/api/nft/service/api/NftService.kt @@ -5,12 +5,12 @@ import com.api.nft.domain.nft.repository.NftMetadataDto import com.api.nft.domain.nft.repository.NftRepository import com.api.nft.enums.ChainType import com.api.nft.event.NftCreatedEvent +import com.api.nft.service.dto.NftResponse import com.api.nft.service.external.dto.AttributeData import com.api.nft.service.external.dto.MetadataData import com.api.nft.service.external.dto.NftData import org.springframework.context.ApplicationEventPublisher import org.springframework.stereotype.Service -import org.springframework.transaction.annotation.Transactional import reactor.core.publisher.Flux import reactor.core.publisher.Mono @@ -54,7 +54,7 @@ class NftService( attributeDataList ?: emptyList() )) .then(Mono.just(nft)) - .doOnSuccess { eventPublisher.publishEvent(NftCreatedEvent(this, nft)) } + .doOnSuccess { eventPublisher.publishEvent(NftCreatedEvent(this, nft.toResponse())) } .flatMap { transferService.createTransfer(nft).thenReturn(nft) } @@ -62,6 +62,14 @@ class NftService( } } + private fun Nft.toResponse() = NftResponse( + id = this.id!!, + tokenId = this.tokenId, + tokenAddress = this.tokenAddress, + chainType = this.chinType, + nftName = this.nftName, + collectionName = this.collectionName + ) fun getNftData(request: NftData, chainType: ChainType): Mono?>> { return Mono.fromCallable { diff --git a/src/main/kotlin/com/api/nft/service/api/TransferService.kt b/src/main/kotlin/com/api/nft/service/api/TransferService.kt index 4d0c4fe..ee33cf8 100644 --- a/src/main/kotlin/com/api/nft/service/api/TransferService.kt +++ b/src/main/kotlin/com/api/nft/service/api/TransferService.kt @@ -89,8 +89,6 @@ class TransferService( } } - - private fun parseAddress(address: String): String { return "0x" + address.substring(26).padStart(40, '0') } diff --git a/src/main/kotlin/com/api/nft/service/dto/NftResponse.kt b/src/main/kotlin/com/api/nft/service/dto/NftResponse.kt new file mode 100644 index 0000000..2fe8b1a --- /dev/null +++ b/src/main/kotlin/com/api/nft/service/dto/NftResponse.kt @@ -0,0 +1,10 @@ +package com.api.nft.service.dto + +data class NftResponse( + val id : Long, + val tokenId: String, + val tokenAddress: String, + val chainType: String, + val nftName: String, + val collectionName: String +) diff --git a/src/test/kotlin/com/api/nft/NftTest.kt b/src/test/kotlin/com/api/nft/NftTest.kt index a77c701..4a23b5f 100644 --- a/src/test/kotlin/com/api/nft/NftTest.kt +++ b/src/test/kotlin/com/api/nft/NftTest.kt @@ -1,18 +1,22 @@ package com.api.nft import com.api.nft.domain.collection.repository.CollectionRepository +import com.api.nft.domain.nft.Nft import com.api.nft.domain.nft.repository.NftRepository import com.api.nft.domain.trasfer.Transfer import com.api.nft.enums.ChainType +import com.api.nft.event.NftCreatedEvent import com.api.nft.rabbitMQ.RabbitMQSender import com.api.nft.service.external.moralis.MoralisApiService import com.api.nft.service.api.NftService import com.api.nft.service.api.TransferService +import com.api.nft.service.dto.NftResponse import com.api.nft.service.external.dto.EthLogResponse import com.api.nft.service.external.infura.InfuraApiService import org.junit.jupiter.api.Test import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.context.SpringBootTest +import org.springframework.context.ApplicationEventPublisher import reactor.test.StepVerifier import java.math.BigInteger @@ -25,6 +29,7 @@ class NftTest( @Autowired private val transferService: TransferService, @Autowired private val nftRepository: NftRepository, @Autowired private val infuraApiService: InfuraApiService, + @Autowired private val eventPublisher: ApplicationEventPublisher, ) { @Test @@ -67,6 +72,36 @@ class NftTest( transferService.findOrUpdateByNftId(31).blockLast() } + @Test + fun rabbitMQTest() { + val nft = Nft( + id = 1, + tokenId = "hello", + tokenAddress = "helloAddress", + chinType = "POLYGON", + contractType = "helloContractType", + nftName = "nftName", + tokenHash = null, + collectionName = "nftCollection", + amount = 3 + ) + eventPublisher.publishEvent(NftCreatedEvent(this,nft.toResponse())) + // rabbitMQSender.nftSend(nft.toResponse()) + // rabbitMQSender.nftSend1("asdasasdas") + } + + private fun Nft.toResponse() = NftResponse( + id = this.id!!, + tokenId = this.tokenId, + tokenAddress = this.tokenAddress, + chainType = this.chinType, + nftName = this.nftName, + collectionName = this.collectionName + ) + + + + } \ No newline at end of file From 1f1596836a75b2390a0f0d3a1857f3ea92127ed3 Mon Sep 17 00:00:00 2001 From: min-96 Date: Thu, 16 May 2024 16:49:06 +0900 Subject: [PATCH 11/38] refactor: WIP --- .../com/api/nft/event/NftEventListener.kt | 2 +- .../nft/event/{ => dto}/NftCreatedEvent.kt | 3 +- .../nft/{service => event}/dto/NftResponse.kt | 2 +- .../com/api/nft/rabbitMQ/RabbitMQSender.kt | 2 +- .../com/api/nft/service/api/NftService.kt | 4 +-- src/test/kotlin/com/api/nft/NftTest.kt | 33 ++----------------- 6 files changed, 8 insertions(+), 38 deletions(-) rename src/main/kotlin/com/api/nft/event/{ => dto}/NftCreatedEvent.kt (69%) rename src/main/kotlin/com/api/nft/{service => event}/dto/NftResponse.kt (85%) diff --git a/src/main/kotlin/com/api/nft/event/NftEventListener.kt b/src/main/kotlin/com/api/nft/event/NftEventListener.kt index 7f642b0..8012940 100644 --- a/src/main/kotlin/com/api/nft/event/NftEventListener.kt +++ b/src/main/kotlin/com/api/nft/event/NftEventListener.kt @@ -1,5 +1,6 @@ package com.api.nft.event +import com.api.nft.event.dto.NftCreatedEvent import com.api.nft.rabbitMQ.RabbitMQSender import org.springframework.context.event.EventListener import org.springframework.stereotype.Component @@ -11,7 +12,6 @@ class NftEventListener( @EventListener fun onNftCreated(event: NftCreatedEvent) { - println("메세지 오는가?") provider.nftSend(event.nft) } } \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/event/NftCreatedEvent.kt b/src/main/kotlin/com/api/nft/event/dto/NftCreatedEvent.kt similarity index 69% rename from src/main/kotlin/com/api/nft/event/NftCreatedEvent.kt rename to src/main/kotlin/com/api/nft/event/dto/NftCreatedEvent.kt index 35d0373..c9db6bd 100644 --- a/src/main/kotlin/com/api/nft/event/NftCreatedEvent.kt +++ b/src/main/kotlin/com/api/nft/event/dto/NftCreatedEvent.kt @@ -1,6 +1,5 @@ -package com.api.nft.event +package com.api.nft.event.dto -import com.api.nft.service.dto.NftResponse import org.springframework.context.ApplicationEvent data class NftCreatedEvent(val eventSource: Any, val nft: NftResponse): ApplicationEvent(eventSource) diff --git a/src/main/kotlin/com/api/nft/service/dto/NftResponse.kt b/src/main/kotlin/com/api/nft/event/dto/NftResponse.kt similarity index 85% rename from src/main/kotlin/com/api/nft/service/dto/NftResponse.kt rename to src/main/kotlin/com/api/nft/event/dto/NftResponse.kt index 2fe8b1a..c4919e1 100644 --- a/src/main/kotlin/com/api/nft/service/dto/NftResponse.kt +++ b/src/main/kotlin/com/api/nft/event/dto/NftResponse.kt @@ -1,4 +1,4 @@ -package com.api.nft.service.dto +package com.api.nft.event.dto data class NftResponse( val id : Long, diff --git a/src/main/kotlin/com/api/nft/rabbitMQ/RabbitMQSender.kt b/src/main/kotlin/com/api/nft/rabbitMQ/RabbitMQSender.kt index 613cfea..16eb00f 100644 --- a/src/main/kotlin/com/api/nft/rabbitMQ/RabbitMQSender.kt +++ b/src/main/kotlin/com/api/nft/rabbitMQ/RabbitMQSender.kt @@ -1,6 +1,6 @@ package com.api.nft.rabbitMQ -import com.api.nft.service.dto.NftResponse +import com.api.nft.event.dto.NftResponse import org.springframework.amqp.rabbit.core.RabbitTemplate import org.springframework.stereotype.Service diff --git a/src/main/kotlin/com/api/nft/service/api/NftService.kt b/src/main/kotlin/com/api/nft/service/api/NftService.kt index 9ecfd7c..beee9e6 100644 --- a/src/main/kotlin/com/api/nft/service/api/NftService.kt +++ b/src/main/kotlin/com/api/nft/service/api/NftService.kt @@ -4,8 +4,8 @@ import com.api.nft.domain.nft.Nft import com.api.nft.domain.nft.repository.NftMetadataDto import com.api.nft.domain.nft.repository.NftRepository import com.api.nft.enums.ChainType -import com.api.nft.event.NftCreatedEvent -import com.api.nft.service.dto.NftResponse +import com.api.nft.event.dto.NftCreatedEvent +import com.api.nft.event.dto.NftResponse import com.api.nft.service.external.dto.AttributeData import com.api.nft.service.external.dto.MetadataData import com.api.nft.service.external.dto.NftData diff --git a/src/test/kotlin/com/api/nft/NftTest.kt b/src/test/kotlin/com/api/nft/NftTest.kt index 4a23b5f..d182668 100644 --- a/src/test/kotlin/com/api/nft/NftTest.kt +++ b/src/test/kotlin/com/api/nft/NftTest.kt @@ -3,22 +3,19 @@ package com.api.nft import com.api.nft.domain.collection.repository.CollectionRepository import com.api.nft.domain.nft.Nft import com.api.nft.domain.nft.repository.NftRepository -import com.api.nft.domain.trasfer.Transfer import com.api.nft.enums.ChainType -import com.api.nft.event.NftCreatedEvent +import com.api.nft.event.dto.NftCreatedEvent import com.api.nft.rabbitMQ.RabbitMQSender import com.api.nft.service.external.moralis.MoralisApiService import com.api.nft.service.api.NftService import com.api.nft.service.api.TransferService -import com.api.nft.service.dto.NftResponse -import com.api.nft.service.external.dto.EthLogResponse +import com.api.nft.event.dto.NftResponse import com.api.nft.service.external.infura.InfuraApiService import org.junit.jupiter.api.Test import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.context.SpringBootTest import org.springframework.context.ApplicationEventPublisher import reactor.test.StepVerifier -import java.math.BigInteger @SpringBootTest class NftTest( @@ -32,32 +29,6 @@ class NftTest( @Autowired private val eventPublisher: ApplicationEventPublisher, ) { - @Test - fun test(){ - val tokenAddress = "0x57a133561c74c242a0b70af9c129126244b4869f" - val tokenId = "4733" - val res =moralisApiService.getNftTransfer(tokenAddress,tokenId, ChainType.POLYGON_MAINNET).block() - println(res) - } - - @Test - fun createTransfer() { - val nft = nftRepository.findById(25).block() - nft?.let { - transferService.createTransfer(it).block() - } ?: error("NFT not found") - } - - @Test - fun createTransferTest1() { - val nftMono = nftRepository.findById(22) - - StepVerifier.create(nftMono) - .assertNext { nft -> - transferService.createTransfer(nft) - } - .verifyComplete() - } @Test fun getEth() { From 71341f04f0b90e2a4811acf7153c8ac8d645eaf1 Mon Sep 17 00:00:00 2001 From: min-96 Date: Thu, 16 May 2024 16:56:11 +0900 Subject: [PATCH 12/38] fix: build gradle --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index d70f61f..6daaf93 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -43,7 +43,7 @@ dependencies { testImplementation("org.springframework.boot:spring-boot-starter-test") testImplementation("io.projectreactor:reactor-test") - testImplementation("org.springframework.batch:spring-batch-test") + } From bfb1df9f13edf61aec664b56452a61bef8fd7947 Mon Sep 17 00:00:00 2001 From: min-96 Date: Mon, 27 May 2024 18:14:01 +0900 Subject: [PATCH 13/38] refactor: add enum --- .../kotlin/com/api/nft/config/R2dbcConfig.kt | 26 +++++++++++++++++++ src/main/kotlin/com/api/nft/domain/nft/Nft.kt | 6 +++-- .../domain/nft/repository/NftMetadataDto.kt | 7 +++-- .../repository/NftRepositorySupportImpl.kt | 10 ++++--- src/main/kotlin/com/api/nft/enums/Enums.kt | 14 +++++++--- .../com/api/nft/event/dto/NftResponse.kt | 4 ++- .../com/api/nft/service/api/NftService.kt | 14 ++++++++-- .../api/nft/service/api/TransferService.kt | 7 ++--- .../api/nft/service/external/dto/NftData.kt | 3 +-- .../external/infura/InfuraApiService.kt | 9 ++++--- .../external/moralis/MoralisApiService.kt | 16 ++++++++---- .../com/api/nft/util/ChainTypeConvert.kt | 8 ++++++ .../com/api/nft/util/StringToEnumConvert.kt | 11 ++++++++ .../migration/V1__Initial_schema.sql | 20 ++++++++++++-- src/test/kotlin/com/api/nft/NftTest.kt | 5 ++-- 15 files changed, 128 insertions(+), 32 deletions(-) create mode 100644 src/main/kotlin/com/api/nft/util/ChainTypeConvert.kt create mode 100644 src/main/kotlin/com/api/nft/util/StringToEnumConvert.kt diff --git a/src/main/kotlin/com/api/nft/config/R2dbcConfig.kt b/src/main/kotlin/com/api/nft/config/R2dbcConfig.kt index 3f76631..20e5685 100644 --- a/src/main/kotlin/com/api/nft/config/R2dbcConfig.kt +++ b/src/main/kotlin/com/api/nft/config/R2dbcConfig.kt @@ -1,15 +1,24 @@ package com.api.nft.config +import com.api.nft.enums.ChainType +import com.api.nft.enums.ContractType +import com.api.nft.util.ChainTypeConvert +import com.api.nft.util.ContractTypeConverter +import com.api.nft.util.StringToEnumConverter import io.r2dbc.postgresql.PostgresqlConnectionConfiguration import io.r2dbc.postgresql.PostgresqlConnectionFactory +import io.r2dbc.postgresql.codec.EnumCodec import io.r2dbc.spi.ConnectionFactory import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration +import org.springframework.core.convert.converter.Converter import org.springframework.data.r2dbc.config.AbstractR2dbcConfiguration +import org.springframework.data.r2dbc.convert.R2dbcCustomConversions import org.springframework.data.r2dbc.core.R2dbcEntityTemplate import org.springframework.data.r2dbc.repository.config.EnableR2dbcRepositories import org.springframework.r2dbc.connection.R2dbcTransactionManager import org.springframework.transaction.ReactiveTransactionManager +import java.util.ArrayList @Configuration @EnableR2dbcRepositories @@ -24,10 +33,27 @@ class R2dbcConfig : AbstractR2dbcConfiguration() { .database("nft") .username("nft") .password("nft") + .codecRegistrar( + EnumCodec.builder() + .withEnum("contract_type", ContractType::class.java) + .withEnum("chain_type", ChainType::class.java) + .build() + ) .build() return PostgresqlConnectionFactory(configuration) } + @Bean + override fun r2dbcCustomConversions(): R2dbcCustomConversions { + val converters: MutableList?> = ArrayList?>() + converters.add(ContractTypeConverter(ContractType::class.java)) + converters.add(StringToEnumConverter(ContractType::class.java)) + converters.add(ChainTypeConvert(ChainType::class.java)) + converters.add(StringToEnumConverter(ChainType::class.java)) + return R2dbcCustomConversions(storeConversions, converters) + } + + @Bean fun transactionManager(connectionFactory: ConnectionFactory?): ReactiveTransactionManager { return R2dbcTransactionManager(connectionFactory!!) diff --git a/src/main/kotlin/com/api/nft/domain/nft/Nft.kt b/src/main/kotlin/com/api/nft/domain/nft/Nft.kt index 82eca62..8802dc3 100644 --- a/src/main/kotlin/com/api/nft/domain/nft/Nft.kt +++ b/src/main/kotlin/com/api/nft/domain/nft/Nft.kt @@ -1,5 +1,7 @@ package com.api.nft.domain.nft +import com.api.nft.enums.ChainType +import com.api.nft.enums.ContractType import lombok.AllArgsConstructor import org.springframework.data.annotation.Id import org.springframework.data.relational.core.mapping.Column @@ -12,8 +14,8 @@ class Nft( @Id val id: Long? = null, @Column("token_id") val tokenId: String, @Column("token_address") val tokenAddress: String, - @Column("chain_type") val chinType: String, - @Column("contract_type") val contractType: String, + @Column("chain_type") val chinType: ChainType, + @Column("contract_type") val contractType: ContractType, @Column("nft_name")val nftName: String, @Column("token_hash")val tokenHash: String?, val collectionName: String, diff --git a/src/main/kotlin/com/api/nft/domain/nft/repository/NftMetadataDto.kt b/src/main/kotlin/com/api/nft/domain/nft/repository/NftMetadataDto.kt index cd5e6f9..c63f4bc 100644 --- a/src/main/kotlin/com/api/nft/domain/nft/repository/NftMetadataDto.kt +++ b/src/main/kotlin/com/api/nft/domain/nft/repository/NftMetadataDto.kt @@ -1,12 +1,15 @@ package com.api.nft.domain.nft.repository +import com.api.nft.enums.ChainType +import com.api.nft.enums.ContractType + data class NftMetadataDto( val id: Long, val tokenId: String, val tokenAddress: String, - val contractType: String, - val chinType: String, + val contractType: ContractType, + val chinType: ChainType, val nftName: String, val collectionName: String, val image: String, diff --git a/src/main/kotlin/com/api/nft/domain/nft/repository/NftRepositorySupportImpl.kt b/src/main/kotlin/com/api/nft/domain/nft/repository/NftRepositorySupportImpl.kt index 6759fa9..a96d47d 100644 --- a/src/main/kotlin/com/api/nft/domain/nft/repository/NftRepositorySupportImpl.kt +++ b/src/main/kotlin/com/api/nft/domain/nft/repository/NftRepositorySupportImpl.kt @@ -1,5 +1,7 @@ package com.api.nft.domain.nft.repository +import com.api.nft.enums.ChainType +import com.api.nft.enums.ContractType import org.springframework.data.r2dbc.core.R2dbcEntityTemplate import reactor.core.publisher.Flux import reactor.core.publisher.Mono @@ -34,11 +36,11 @@ class NftRepositorySupportImpl( id = (row.get("id") as Number).toLong(), tokenId = row.get("tokenId", String::class.java)!!, tokenAddress = row.get("tokenAddress", String::class.java)!!, - chinType = row.get("chinType", String::class.java)!!, + chinType = row.get("chinType", ChainType::class.java)!!, nftName = row.get("nftName", String::class.java)!!, collectionName = row.get("collectionName", String::class.java)!!, image = row.get("image", String::class.java) ?: "", - contractType = row.get("contractType", String::class.java)!!, + contractType = row.get("contractType", ContractType::class.java)!!, ) }.first() } @@ -68,11 +70,11 @@ class NftRepositorySupportImpl( id = (row.get("id") as Number).toLong(), tokenId = row.get("tokenId", String::class.java)!!, tokenAddress = row.get("tokenAddress", String::class.java)!!, - chinType = row.get("chainType", String::class.java)!!, + chinType = row.get("chainType", ChainType::class.java)!!, nftName = row.get("nftName", String::class.java)!!, collectionName = row.get("collectionName", String::class.java)!!, image = row.get("image", String::class.java) ?: "", - contractType = row.get("contractType", String::class.java)!!, + contractType = row.get("contractType", ContractType::class.java)!!, ) } .all() diff --git a/src/main/kotlin/com/api/nft/enums/Enums.kt b/src/main/kotlin/com/api/nft/enums/Enums.kt index 00f502e..3427b30 100644 --- a/src/main/kotlin/com/api/nft/enums/Enums.kt +++ b/src/main/kotlin/com/api/nft/enums/Enums.kt @@ -2,13 +2,21 @@ package com.api.nft.enums enum class ChainType{ ETHEREUM_MAINNET, + LINEA_MAINNET, + LINEA_SEPOLIA, POLYGON_MAINNET, - ETHREUM_GOERLI, - ETHREUM_SEPOLIA, - POLYGON_MUMBAI, + ETHEREUM_HOLESKY, + ETHEREUM_SEPOLIA, + POLYGON_AMOY, + } + enum class NetworkType{ ETHEREUM, POLYGON, } + +enum class ContractType{ + ERC721, ERC1155 +} diff --git a/src/main/kotlin/com/api/nft/event/dto/NftResponse.kt b/src/main/kotlin/com/api/nft/event/dto/NftResponse.kt index c4919e1..12fd827 100644 --- a/src/main/kotlin/com/api/nft/event/dto/NftResponse.kt +++ b/src/main/kotlin/com/api/nft/event/dto/NftResponse.kt @@ -1,10 +1,12 @@ package com.api.nft.event.dto +import com.api.nft.enums.ChainType + data class NftResponse( val id : Long, val tokenId: String, val tokenAddress: String, - val chainType: String, + val chainType: ChainType, val nftName: String, val collectionName: String ) diff --git a/src/main/kotlin/com/api/nft/service/api/NftService.kt b/src/main/kotlin/com/api/nft/service/api/NftService.kt index beee9e6..e4412e9 100644 --- a/src/main/kotlin/com/api/nft/service/api/NftService.kt +++ b/src/main/kotlin/com/api/nft/service/api/NftService.kt @@ -4,6 +4,7 @@ import com.api.nft.domain.nft.Nft import com.api.nft.domain.nft.repository.NftMetadataDto import com.api.nft.domain.nft.repository.NftRepository import com.api.nft.enums.ChainType +import com.api.nft.enums.ContractType import com.api.nft.event.dto.NftCreatedEvent import com.api.nft.event.dto.NftResponse import com.api.nft.service.external.dto.AttributeData @@ -34,6 +35,7 @@ class NftService( return nftRepository.findAllByNftJoinMetadata(ids) } + // 리팩토링 fun findOrCreateNft(request:NftData, chainType: ChainType): Mono { return nftRepository.findByTokenAddressAndTokenId(request.tokenAddress,request.tokenId) .switchIfEmpty( @@ -96,14 +98,22 @@ class NftService( Nft( tokenId = nft.tokenId, tokenAddress = nft.tokenAddress, - chinType = chainType.toString(), + chinType = chainType, nftName = metadata.name, collectionName = it.name, tokenHash = nft.tokenHash, amount = nft.amount.toInt(), - contractType = nft.contractType!!, + contractType = nft.contractType.toContractEnum(), ) ) } } + + fun String.toContractEnum() : ContractType { + return when(this) { + "ERC721" -> ContractType.ERC721 + "ERC1155" -> ContractType.ERC1155 + else -> throw IllegalArgumentException("not support contractType") + } + } } \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/service/api/TransferService.kt b/src/main/kotlin/com/api/nft/service/api/TransferService.kt index ee33cf8..4eb5ead 100644 --- a/src/main/kotlin/com/api/nft/service/api/TransferService.kt +++ b/src/main/kotlin/com/api/nft/service/api/TransferService.kt @@ -29,7 +29,7 @@ class TransferService( return moralisApiService.getNftTransfer( nft.tokenAddress, nft.tokenId, - nft.chinType.convertNetworkTypeToChainType() + nft.chinType ).flatMapMany { Flux.fromIterable(it.result) }.map { @@ -49,6 +49,7 @@ class TransferService( blockTimestamp = this.blockTimestamp.toEpochMilli() ) + // 동시성 이슈 체크 fun findOrUpdateByNftId(nftId: Long): Flux { return nftRepository.findById(nftId).flatMapMany { nft -> transferRepository.findByNftIdOrderByBlockTimestampDesc(nftId).next() @@ -63,13 +64,13 @@ class TransferService( val fromBlock = transfer.blockNumber + 1 return infuraApiService.getEthLogs( fromBlock.toString(), - nft.chinType.convertNetworkTypeToChainType(), + nft.chinType, nft.tokenAddress, nft.tokenId ).flatMapMany { ethLogResponses -> Flux.fromIterable(ethLogResponses) }.flatMap { ethLogResponse -> - ethLogResponse.toTransferEntity(nft.id!!, nft.chinType.convertNetworkTypeToChainType()) + ethLogResponse.toTransferEntity(nft.id!!, nft.chinType) .flatMap { transferRepository.save(it) } diff --git a/src/main/kotlin/com/api/nft/service/external/dto/NftData.kt b/src/main/kotlin/com/api/nft/service/external/dto/NftData.kt index 372012f..0c94caf 100644 --- a/src/main/kotlin/com/api/nft/service/external/dto/NftData.kt +++ b/src/main/kotlin/com/api/nft/service/external/dto/NftData.kt @@ -8,7 +8,7 @@ data class NftData( val amount: String, @JsonProperty("token_id")val tokenId: String, @JsonProperty("token_address") val tokenAddress: String, - @JsonProperty("contract_type") val contractType: String?, + @JsonProperty("contract_type") val contractType: String, @JsonProperty("owner_of") val ownerOf: String?, @JsonProperty("last_metadata_sync") val lastMetadataSync: String?, @JsonProperty("last_token_uri_sync") val lastTokenUriSync: String?, @@ -26,7 +26,6 @@ data class NftData( @JsonProperty("collection_banner_image") val collectionBannerImage: String?, ) - data class MetadataData( val name: String, val description: String, diff --git a/src/main/kotlin/com/api/nft/service/external/infura/InfuraApiService.kt b/src/main/kotlin/com/api/nft/service/external/infura/InfuraApiService.kt index 3523a8c..48ce03e 100644 --- a/src/main/kotlin/com/api/nft/service/external/infura/InfuraApiService.kt +++ b/src/main/kotlin/com/api/nft/service/external/infura/InfuraApiService.kt @@ -26,16 +26,17 @@ class InfuraApiService { val baseUrl = when (chainType) { ChainType.ETHEREUM_MAINNET -> "https://mainnet.infura.io" ChainType.POLYGON_MAINNET -> "https://polygon-mainnet.infura.io" - ChainType.ETHREUM_GOERLI -> "https://goerli.infura.io" - ChainType.ETHREUM_SEPOLIA -> "https://sepolia.infura.io" - ChainType.POLYGON_MUMBAI -> "https://polygon-mumbai.infura.io" + ChainType.LINEA_MAINNET -> "https://linea-mainnet.infura.io" + ChainType.LINEA_SEPOLIA -> "https://linea-sepolia.infura.io" + ChainType.ETHEREUM_HOLESKY -> "https://polygon-mumbai.infura.io" + ChainType.ETHEREUM_SEPOLIA -> "https://sepolia.infura.io" + ChainType.POLYGON_AMOY -> "https://polygon-amoy.infura.io" } return WebClient.builder() .baseUrl(baseUrl) .build() } - fun getEthLogs( fromBlock: String, chainType: ChainType, diff --git a/src/main/kotlin/com/api/nft/service/external/moralis/MoralisApiService.kt b/src/main/kotlin/com/api/nft/service/external/moralis/MoralisApiService.kt index 183c0cd..1d690c6 100644 --- a/src/main/kotlin/com/api/nft/service/external/moralis/MoralisApiService.kt +++ b/src/main/kotlin/com/api/nft/service/external/moralis/MoralisApiService.kt @@ -20,11 +20,13 @@ class MoralisApiService { private fun queryParamByChain(chain: ChainType): String? { val chain = when (chain) { - ChainType.ETHEREUM_MAINNET -> "eth" - ChainType.POLYGON_MAINNET -> "polygon" - ChainType.ETHREUM_GOERLI -> "goerli" - ChainType.POLYGON_MUMBAI -> "munbai" - ChainType.ETHREUM_SEPOLIA -> "sepolia" + ChainType.ETHEREUM_MAINNET -> "0x1" + ChainType.POLYGON_MAINNET -> "0x89" + ChainType.LINEA_MAINNET -> "0xe708" + ChainType.LINEA_SEPOLIA -> "0xe705" + ChainType.ETHEREUM_HOLESKY -> "0x4268" + ChainType.ETHEREUM_SEPOLIA -> "0xaa36a7" + ChainType.POLYGON_AMOY -> "0xaa36a7" } return chain } @@ -48,6 +50,10 @@ class MoralisApiService { } + fun getNftMetadata() { + + } + companion object { private val apiKey = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJub25jZSI6ImJiNmIxMWJmLWNmNzItNDg0OC04OGEyLTBjYTIwODRjN2VhMyIsIm9yZ0lkIjoiMzgzODQwIiwidXNlcklkIjoiMzk0NDAyIiwidHlwZUlkIjoiMGZlYWQ5NDctZjQwZS00MDkwLWFlNGUtOTA1ZTdmMjUxZTAzIiwidHlwZSI6IlBST0pFQ1QiLCJpYXQiOjE3MTA5NTIwMjYsImV4cCI6NDg2NjcxMjAyNn0.VQE60IPGiWxdp7jKLF0jzXnxrLjEpU56H4bnfhMt0Sw" diff --git a/src/main/kotlin/com/api/nft/util/ChainTypeConvert.kt b/src/main/kotlin/com/api/nft/util/ChainTypeConvert.kt new file mode 100644 index 0000000..7782a7d --- /dev/null +++ b/src/main/kotlin/com/api/nft/util/ChainTypeConvert.kt @@ -0,0 +1,8 @@ +package com.api.nft.util + +import com.api.nft.enums.ChainType +import com.api.nft.enums.ContractType +import org.springframework.data.r2dbc.convert.EnumWriteSupport + +data class ContractTypeConverter>(private val enumType: Class) : EnumWriteSupport() +data class ChainTypeConvert>(private val enumType: Class): EnumWriteSupport() diff --git a/src/main/kotlin/com/api/nft/util/StringToEnumConvert.kt b/src/main/kotlin/com/api/nft/util/StringToEnumConvert.kt new file mode 100644 index 0000000..a9cf2a5 --- /dev/null +++ b/src/main/kotlin/com/api/nft/util/StringToEnumConvert.kt @@ -0,0 +1,11 @@ +package com.api.nft.util + +import org.springframework.core.convert.converter.Converter +import org.springframework.data.convert.ReadingConverter + +@ReadingConverter +class StringToEnumConverter>(private val enumType: Class) : Converter { + override fun convert(source: String): T { + return java.lang.Enum.valueOf(enumType, source) + } +} \ No newline at end of file diff --git a/src/main/resources/db/postgresql/migration/V1__Initial_schema.sql b/src/main/resources/db/postgresql/migration/V1__Initial_schema.sql index 56a2851..41cb6e0 100644 --- a/src/main/resources/db/postgresql/migration/V1__Initial_schema.sql +++ b/src/main/resources/db/postgresql/migration/V1__Initial_schema.sql @@ -1,3 +1,19 @@ +CREATE TYPE contract_type AS ENUM( + 'ERC721', + 'ERC1155' + ); + +CREATE TYPE chain_type AS ENUM ( + 'ETHEREUM_MAINNET', + 'LINEA_MAINNET', + 'LINEA_SEPOLIA', + 'POLYGON_MAINNET', + 'ETHEREUM_HOLESKY', + 'ETHEREUM_SEPOLIA', + 'POLYGON_AMOY' + ); + + CREATE TABLE IF NOT EXISTS collection ( name varchar(500) PRIMARY KEY, logo varchar(500), @@ -9,9 +25,9 @@ CREATE TABLE IF NOT EXISTS nft ( id SERIAL PRIMARY KEY, token_id VARCHAR(255) NOT NULL, token_address VARCHAR(255) NOT NULL, - chain_type varchar(100) NOT NULL, + chain_type chain_type NOT NULL, nft_name varchar(255) NOT NULL, - contract_type varchar(255) NOT NULL, + contract_type contract_type NOT NULL, token_hash varchar(300), amount INT, collection_name varchar(500) REFERENCES collection(name) diff --git a/src/test/kotlin/com/api/nft/NftTest.kt b/src/test/kotlin/com/api/nft/NftTest.kt index d182668..af318c5 100644 --- a/src/test/kotlin/com/api/nft/NftTest.kt +++ b/src/test/kotlin/com/api/nft/NftTest.kt @@ -4,6 +4,7 @@ import com.api.nft.domain.collection.repository.CollectionRepository import com.api.nft.domain.nft.Nft import com.api.nft.domain.nft.repository.NftRepository import com.api.nft.enums.ChainType +import com.api.nft.enums.ContractType import com.api.nft.event.dto.NftCreatedEvent import com.api.nft.rabbitMQ.RabbitMQSender import com.api.nft.service.external.moralis.MoralisApiService @@ -49,8 +50,8 @@ class NftTest( id = 1, tokenId = "hello", tokenAddress = "helloAddress", - chinType = "POLYGON", - contractType = "helloContractType", + chinType = ChainType.POLYGON_MAINNET, + contractType = ContractType.ERC721, nftName = "nftName", tokenHash = null, collectionName = "nftCollection", From 3e393484ffcb06ae8adedc979c46c2f379639623 Mon Sep 17 00:00:00 2001 From: min-96 Date: Mon, 27 May 2024 19:57:47 +0900 Subject: [PATCH 14/38] refactor: NftByWallet --- .../com/api/nft/controller/NftController.kt | 10 ++-- src/main/kotlin/com/api/nft/domain/nft/Nft.kt | 2 +- .../domain/nft/repository/NftRepository.kt | 3 +- .../com/api/nft/event/dto/NftResponse.kt | 2 +- .../api/nft/service/api/AttributeService.kt | 4 +- .../api/nft/service/api/MetadataService.kt | 6 +-- .../com/api/nft/service/api/NftService.kt | 48 +++++++++-------- .../api/nft/service/external/dto/NftData.kt | 52 +++++++++++-------- .../external/moralis/MoralisApiService.kt | 33 ++++++++++-- .../migration/V1__Initial_schema.sql | 2 +- src/test/kotlin/com/api/nft/NftTest.kt | 8 +++ 11 files changed, 109 insertions(+), 61 deletions(-) diff --git a/src/main/kotlin/com/api/nft/controller/NftController.kt b/src/main/kotlin/com/api/nft/controller/NftController.kt index ffa270c..34da622 100644 --- a/src/main/kotlin/com/api/nft/controller/NftController.kt +++ b/src/main/kotlin/com/api/nft/controller/NftController.kt @@ -26,11 +26,11 @@ class NftController( private val transferService: TransferService, ) { - @PostMapping("/save/{chainType}") - fun save(@PathVariable chainType: ChainType,@RequestBody requests: List): Flux { - return nftService.saveNfts(requests,chainType) - - } + // @PostMapping("/save/{chainType}") + // fun save(@PathVariable chainType: ChainType,@RequestBody requests: List): Flux { + // return nftService.saveNfts(requests,chainType) + // + // } @GetMapping fun getAllByIds(@RequestParam nftIds: List): Flux { diff --git a/src/main/kotlin/com/api/nft/domain/nft/Nft.kt b/src/main/kotlin/com/api/nft/domain/nft/Nft.kt index 8802dc3..e931fca 100644 --- a/src/main/kotlin/com/api/nft/domain/nft/Nft.kt +++ b/src/main/kotlin/com/api/nft/domain/nft/Nft.kt @@ -16,7 +16,7 @@ class Nft( @Column("token_address") val tokenAddress: String, @Column("chain_type") val chinType: ChainType, @Column("contract_type") val contractType: ContractType, - @Column("nft_name")val nftName: String, + @Column("nft_name")val nftName: String?, @Column("token_hash")val tokenHash: String?, val collectionName: String, val amount: Int, diff --git a/src/main/kotlin/com/api/nft/domain/nft/repository/NftRepository.kt b/src/main/kotlin/com/api/nft/domain/nft/repository/NftRepository.kt index a0f44b4..fd57dce 100644 --- a/src/main/kotlin/com/api/nft/domain/nft/repository/NftRepository.kt +++ b/src/main/kotlin/com/api/nft/domain/nft/repository/NftRepository.kt @@ -1,10 +1,11 @@ package com.api.nft.domain.nft.repository import com.api.nft.domain.nft.Nft +import com.api.nft.enums.ChainType import org.springframework.data.repository.reactive.ReactiveCrudRepository import reactor.core.publisher.Mono interface NftRepository : ReactiveCrudRepository, NftRepositorySupport { - fun findByTokenAddressAndTokenId(tokenAddress: String, tokenId: String): Mono + fun findByTokenAddressAndTokenIdAndChinType(tokenAddress: String, tokenId: String,chainType: ChainType): Mono } \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/event/dto/NftResponse.kt b/src/main/kotlin/com/api/nft/event/dto/NftResponse.kt index 12fd827..03e0b86 100644 --- a/src/main/kotlin/com/api/nft/event/dto/NftResponse.kt +++ b/src/main/kotlin/com/api/nft/event/dto/NftResponse.kt @@ -7,6 +7,6 @@ data class NftResponse( val tokenId: String, val tokenAddress: String, val chainType: ChainType, - val nftName: String, + val nftName: String?, val collectionName: String ) diff --git a/src/main/kotlin/com/api/nft/service/api/AttributeService.kt b/src/main/kotlin/com/api/nft/service/api/AttributeService.kt index d905d5d..ce2e57f 100644 --- a/src/main/kotlin/com/api/nft/service/api/AttributeService.kt +++ b/src/main/kotlin/com/api/nft/service/api/AttributeService.kt @@ -2,7 +2,7 @@ package com.api.nft.service.api import com.api.nft.domain.attribute.Attribute import com.api.nft.domain.attribute.AttributeRepository -import com.api.nft.service.external.dto.AttributeData +import com.api.nft.service.external.dto.NftAttribute import org.springframework.stereotype.Service import reactor.core.publisher.Flux @@ -11,7 +11,7 @@ class AttributeService( private val attributeRepository: AttributeRepository, ) { - fun createAttribute(nftId: Long, attributes: List): Flux { + fun createAttribute(nftId: Long, attributes: List): Flux { return Flux.fromIterable(attributes).flatMap { attributeRepository.save( Attribute( diff --git a/src/main/kotlin/com/api/nft/service/api/MetadataService.kt b/src/main/kotlin/com/api/nft/service/api/MetadataService.kt index 6df1522..10da639 100644 --- a/src/main/kotlin/com/api/nft/service/api/MetadataService.kt +++ b/src/main/kotlin/com/api/nft/service/api/MetadataService.kt @@ -2,7 +2,7 @@ package com.api.nft.service.api import com.api.nft.domain.metadata.Metadata import com.api.nft.domain.metadata.repository.MetadataRepository -import com.api.nft.service.external.dto.MetadataData +import com.api.nft.service.external.dto.NftMetadata import org.springframework.stereotype.Service import reactor.core.publisher.Mono @@ -11,12 +11,12 @@ class MetadataService( private val metadataRepository: MetadataRepository, ) { - fun createMetadata(nftId: Long, metadata: MetadataData): Mono { + fun createMetadata(nftId: Long, metadata: NftMetadata): Mono { return metadataRepository.save( Metadata( nftId = nftId, description = metadata.description, - image = MetadataData.parseImage(metadata.image), + image = NftMetadata.parseImage(metadata.image), animationUrl = metadata.animationUrl, ) ) diff --git a/src/main/kotlin/com/api/nft/service/api/NftService.kt b/src/main/kotlin/com/api/nft/service/api/NftService.kt index e4412e9..1a1d478 100644 --- a/src/main/kotlin/com/api/nft/service/api/NftService.kt +++ b/src/main/kotlin/com/api/nft/service/api/NftService.kt @@ -7,9 +7,11 @@ import com.api.nft.enums.ChainType import com.api.nft.enums.ContractType import com.api.nft.event.dto.NftCreatedEvent import com.api.nft.event.dto.NftResponse -import com.api.nft.service.external.dto.AttributeData -import com.api.nft.service.external.dto.MetadataData +import com.api.nft.service.external.dto.NftAttribute import com.api.nft.service.external.dto.NftData +import com.api.nft.service.external.dto.NftMetadata +import com.api.nft.service.external.infura.InfuraApiService +import com.api.nft.service.external.moralis.MoralisApiService import org.springframework.context.ApplicationEventPublisher import org.springframework.stereotype.Service import reactor.core.publisher.Flux @@ -23,28 +25,32 @@ class NftService( private val attributeService: AttributeService, private val eventPublisher: ApplicationEventPublisher, private val transferService: TransferService, + private val moralisApiService: MoralisApiService ) { - fun saveNfts(requests: List, chainType: ChainType): Flux { - return Flux.fromIterable(requests) - .flatMap { findOrCreateNft(it, chainType) } - } - fun findAllById(ids: List): Flux { return nftRepository.findAllByNftJoinMetadata(ids) } - // 리팩토링 - fun findOrCreateNft(request:NftData, chainType: ChainType): Mono { - return nftRepository.findByTokenAddressAndTokenId(request.tokenAddress,request.tokenId) + fun findOrCreateNft(tokenAddress: String,tokenId: String, chainType: ChainType): Mono { + return nftRepository.findByTokenAddressAndTokenIdAndChinType(tokenAddress,tokenId,chainType) .switchIfEmpty( - createNftProcess(request,chainType) - ).flatMap { - nftRepository.findByNftJoinMetadata(it.id!!) - } + moralisApiService.getNFTMetadata(tokenAddress,tokenId,chainType) + .flatMap { createNftProcess(it,chainType) } + ).map { it.toResponse() } + } + fun getByWalletNft(wallet: String,chainType: ChainType): Flux { + return moralisApiService.getNFTsByAddress(wallet, chainType) + .flatMapMany { Flux.fromIterable(it.result) } + .filter { it.contractType == "ERC721"} + .flatMap { findOrCreateNft(it.tokenAddress, it.tokenId, chainType) } + + } + + fun createNftProcess(request: NftData, chainType: ChainType): Mono { val response = getNftData(request, chainType) return response.flatMap { (nftData, metadataData, attributeDataList) -> @@ -73,11 +79,11 @@ class NftService( collectionName = this.collectionName ) - fun getNftData(request: NftData, chainType: ChainType): Mono?>> { + fun getNftData(request: NftData, chainType: ChainType): Mono?>> { return Mono.fromCallable { - val metadata = MetadataData.toMetadataResponse(request.metadata) + val metadata = NftMetadata.toMetadataResponse(request.metadata) val attributes = metadata.attributes.let { - if (it.isNotEmpty()) AttributeData.toAttributeResponse(it) else null + if (it.isNotEmpty()) NftAttribute.toAttributeResponse(it) else null } Triple(request, metadata, attributes) } @@ -85,24 +91,24 @@ class NftService( fun createNft(nft: NftData, - metadata: MetadataData, + metadata: NftMetadata?, chainType: ChainType ): Mono { return collectionService.findOrCreate( nft.name, nft.collectionLogo, nft.collectionBannerImage, - metadata.description, + metadata?.description, ).flatMap { nftRepository.save( Nft( tokenId = nft.tokenId, tokenAddress = nft.tokenAddress, chinType = chainType, - nftName = metadata.name, + nftName = metadata?.name, collectionName = it.name, tokenHash = nft.tokenHash, - amount = nft.amount.toInt(), + amount = nft.amount?.toInt() ?: 0, contractType = nft.contractType.toContractEnum(), ) ) diff --git a/src/main/kotlin/com/api/nft/service/external/dto/NftData.kt b/src/main/kotlin/com/api/nft/service/external/dto/NftData.kt index 0c94caf..219a6ab 100644 --- a/src/main/kotlin/com/api/nft/service/external/dto/NftData.kt +++ b/src/main/kotlin/com/api/nft/service/external/dto/NftData.kt @@ -4,41 +4,51 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +data class NFTByWalletResponse( + val status: String, + val page: Int, + @JsonProperty("page_size") val pageSize: Int, + val cursor: String?, + val result: List +) + data class NftData( - val amount: String, - @JsonProperty("token_id")val tokenId: String, @JsonProperty("token_address") val tokenAddress: String, + @JsonProperty("token_id") val tokenId: String, @JsonProperty("contract_type") val contractType: String, - @JsonProperty("owner_of") val ownerOf: String?, - @JsonProperty("last_metadata_sync") val lastMetadataSync: String?, - @JsonProperty("last_token_uri_sync") val lastTokenUriSync: String?, - val metadata: String, + @JsonProperty("owner_of") val ownerOf: String, @JsonProperty("block_number") val blockNumber: String, @JsonProperty("block_number_minted") val blockNumberMinted: String?, + @JsonProperty("token_uri") val tokenUri: String?, + val metadata: String?, + @JsonProperty("normalized_metadata") val normalizedMetadata: String?, + val media: String?, + val amount: String?, val name: String, - val symbol: String, - @JsonProperty("token_hash") val tokenHash: String, - @JsonProperty("token_uri") val tokenUri: String, - @JsonProperty("minter_address") val minterAddress: String?, - @JsonProperty("verified_collection") val verifiedCollection: Boolean, - @JsonProperty("possible_spam") val possibleSpam: Boolean, - @JsonProperty("collection_logo") val collectionLogo: String?, - @JsonProperty("collection_banner_image") val collectionBannerImage: String?, + val symbol: String?, + @JsonProperty("token_hash") val tokenHash: String?, + @JsonProperty("last_token_uri_sync") val lastTokenUriSync: String?, + @JsonProperty("last_metadata_sync") val lastMetadataSync: String?, + @JsonProperty("possible_spam") val possibleSpam: Boolean?, + @JsonProperty("verified_collection") val verifiedCollection: Boolean?, + @JsonIgnoreProperties(ignoreUnknown = true) @JsonProperty("collection_logo") val collectionLogo: String?, + @JsonIgnoreProperties(ignoreUnknown = true) @JsonProperty("collection_banner_image") val collectionBannerImage: String?, + @JsonIgnoreProperties(ignoreUnknown = true) @JsonProperty("minter_address") val minterAddress: String? ) -data class MetadataData( +data class NftMetadata( val name: String, val description: String, val image: String, @JsonProperty("animation_url") val animationUrl: String?, val attributes: List> = emptyList(), @JsonIgnoreProperties(ignoreUnknown = true) @JsonProperty("external_url") val externalUrl: String? = null, - @JsonIgnoreProperties(ignoreUnknown = true) @JsonProperty("youtube_url") val youtubeUrl: String? = null, + @JsonIgnoreProperties(ignoreUnknown = true) @JsonProperty("youtube_url") val youtube_url: String? = null ){ companion object { - fun toMetadataResponse(metadata: String): MetadataData { + fun toMetadataResponse(metadata: String?): NftMetadata { val mapper = jacksonObjectMapper() - return mapper.readValue(metadata, MetadataData::class.java) + return mapper.readValue(metadata, NftMetadata::class.java) } fun parseImage(image: String) : String { @@ -47,14 +57,14 @@ data class MetadataData( } } -data class AttributeData( +data class NftAttribute( @JsonProperty("trait_type") val traitType: String?, val value: String? ) { companion object { - fun toAttributeResponse(attributes: List>): List { + fun toAttributeResponse(attributes: List>): List { return attributes.map { - AttributeData( + NftAttribute( traitType = it["trait_type"], value = it["value"] ) diff --git a/src/main/kotlin/com/api/nft/service/external/moralis/MoralisApiService.kt b/src/main/kotlin/com/api/nft/service/external/moralis/MoralisApiService.kt index 1d690c6..52d5223 100644 --- a/src/main/kotlin/com/api/nft/service/external/moralis/MoralisApiService.kt +++ b/src/main/kotlin/com/api/nft/service/external/moralis/MoralisApiService.kt @@ -1,14 +1,12 @@ package com.api.nft.service.external.moralis import com.api.nft.enums.ChainType +import com.api.nft.service.external.dto.NFTByWalletResponse import com.api.nft.service.external.dto.NftData import com.api.nft.service.external.dto.NftTransferData - import org.springframework.http.MediaType import org.springframework.stereotype.Service import org.springframework.web.reactive.function.client.WebClient -import org.springframework.web.reactive.function.client.bodyToMono -import reactor.core.publisher.Flux import reactor.core.publisher.Mono @Service @@ -49,12 +47,37 @@ class MoralisApiService { .bodyToMono(NftTransferData::class.java) } + fun getNFTsByAddress(walletAddress: String,chainType: ChainType): Mono { + val chain = queryParamByChain(chainType) + return webClient.get() + .uri { + it.path("/v2.2/${walletAddress}/nft") + it.queryParam("chain", chain) + it.queryParam("exclude_spam", true) + it.build() + } + .header("X-API-Key", apiKey) + .header("Accept", MediaType.APPLICATION_JSON_VALUE) + .retrieve() + .bodyToMono(NFTByWalletResponse::class.java) + } - fun getNftMetadata() { - + fun getNFTMetadata(tokenAddress: String,tokenId:String,chainType: ChainType): Mono { + val chain = queryParamByChain(chainType) + return webClient.get() + .uri { + it.path("/v2.2/nft/${tokenAddress}/${tokenId}") + it.queryParam("chain", chain) + it.build() + } + .header("X-API-Key", apiKey) + .header("Accept", MediaType.APPLICATION_JSON_VALUE) + .retrieve() + .bodyToMono(NftData::class.java) } + companion object { private val apiKey = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJub25jZSI6ImJiNmIxMWJmLWNmNzItNDg0OC04OGEyLTBjYTIwODRjN2VhMyIsIm9yZ0lkIjoiMzgzODQwIiwidXNlcklkIjoiMzk0NDAyIiwidHlwZUlkIjoiMGZlYWQ5NDctZjQwZS00MDkwLWFlNGUtOTA1ZTdmMjUxZTAzIiwidHlwZSI6IlBST0pFQ1QiLCJpYXQiOjE3MTA5NTIwMjYsImV4cCI6NDg2NjcxMjAyNn0.VQE60IPGiWxdp7jKLF0jzXnxrLjEpU56H4bnfhMt0Sw" private val baseUrl = "https://deep-index.moralis.io/api" diff --git a/src/main/resources/db/postgresql/migration/V1__Initial_schema.sql b/src/main/resources/db/postgresql/migration/V1__Initial_schema.sql index 41cb6e0..6a25ff8 100644 --- a/src/main/resources/db/postgresql/migration/V1__Initial_schema.sql +++ b/src/main/resources/db/postgresql/migration/V1__Initial_schema.sql @@ -26,7 +26,7 @@ CREATE TABLE IF NOT EXISTS nft ( token_id VARCHAR(255) NOT NULL, token_address VARCHAR(255) NOT NULL, chain_type chain_type NOT NULL, - nft_name varchar(255) NOT NULL, + nft_name varchar(255), contract_type contract_type NOT NULL, token_hash varchar(300), amount INT, diff --git a/src/test/kotlin/com/api/nft/NftTest.kt b/src/test/kotlin/com/api/nft/NftTest.kt index af318c5..d57ba83 100644 --- a/src/test/kotlin/com/api/nft/NftTest.kt +++ b/src/test/kotlin/com/api/nft/NftTest.kt @@ -72,7 +72,15 @@ class NftTest( ) + @Test + fun getByWalletNft() { + val wallet = "0x01b72b4aa3f66f213d62d53e829bc172a6a72867" + val res= nftService.getByWalletNft(wallet,ChainType.POLYGON_MAINNET).blockLast() + println(res?.nftName) + println(res?.tokenId) + println(res?.tokenAddress) + } From 239364ec460a86df4a8400bd9f3c2cf9c7b820b8 Mon Sep 17 00:00:00 2001 From: min96 Date: Tue, 28 May 2024 00:12:49 +0900 Subject: [PATCH 15/38] refactor: fix api --- .../com/api/nft/controller/NftController.kt | 15 +++++++++++++++ src/main/kotlin/com/api/nft/domain/nft/Nft.kt | 2 +- .../nft/domain/nft/repository/NftMetadataDto.kt | 2 +- .../nft/domain/nft/repository/NftRepository.kt | 2 +- .../nft/repository/NftRepositorySupportImpl.kt | 6 +++--- .../kotlin/com/api/nft/event/dto/NftResponse.kt | 2 -- .../com/api/nft/service/api/CollectionService.kt | 2 -- .../kotlin/com/api/nft/service/api/NftService.kt | 13 +++++-------- .../com/api/nft/service/api/TransferService.kt | 6 +++--- .../api/nft/service/external/dto/NftRequest.kt | 10 ++++++++++ src/test/kotlin/com/api/nft/NftTest.kt | 7 ++----- 11 files changed, 41 insertions(+), 26 deletions(-) create mode 100644 src/main/kotlin/com/api/nft/service/external/dto/NftRequest.kt diff --git a/src/main/kotlin/com/api/nft/controller/NftController.kt b/src/main/kotlin/com/api/nft/controller/NftController.kt index 34da622..b2c6f90 100644 --- a/src/main/kotlin/com/api/nft/controller/NftController.kt +++ b/src/main/kotlin/com/api/nft/controller/NftController.kt @@ -3,9 +3,11 @@ package com.api.nft.controller import com.api.nft.domain.nft.repository.NftMetadataDto import com.api.nft.domain.trasfer.Transfer import com.api.nft.enums.ChainType +import com.api.nft.event.dto.NftResponse import com.api.nft.service.api.NftService import com.api.nft.service.api.TransferService import com.api.nft.service.external.dto.NftData +import com.api.nft.service.external.dto.NftRequest import org.springframework.http.HttpStatus import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.GetMapping @@ -45,4 +47,17 @@ class NftController( .defaultIfEmpty( ResponseEntity.notFound().build()) .onErrorResume { Mono.just(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build()) } } + + @PostMapping + fun findOrCreate(@RequestBody request: NftRequest): Mono { + return nftService.findOrCreateNft(request.tokenAddress,request.tokenId,request.chainType) + } + + @GetMapping("/wallet/{chainType}") + fun getByWalletNft(@PathVariable chainType: ChainType, @RequestParam wallet: String) : Flux { + return nftService.getByWalletNft(wallet,chainType) + + } + + } \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/domain/nft/Nft.kt b/src/main/kotlin/com/api/nft/domain/nft/Nft.kt index e931fca..4c7b0b4 100644 --- a/src/main/kotlin/com/api/nft/domain/nft/Nft.kt +++ b/src/main/kotlin/com/api/nft/domain/nft/Nft.kt @@ -14,7 +14,7 @@ class Nft( @Id val id: Long? = null, @Column("token_id") val tokenId: String, @Column("token_address") val tokenAddress: String, - @Column("chain_type") val chinType: ChainType, + @Column("chain_type") val chainType: ChainType, @Column("contract_type") val contractType: ContractType, @Column("nft_name")val nftName: String?, @Column("token_hash")val tokenHash: String?, diff --git a/src/main/kotlin/com/api/nft/domain/nft/repository/NftMetadataDto.kt b/src/main/kotlin/com/api/nft/domain/nft/repository/NftMetadataDto.kt index c63f4bc..4f95d09 100644 --- a/src/main/kotlin/com/api/nft/domain/nft/repository/NftMetadataDto.kt +++ b/src/main/kotlin/com/api/nft/domain/nft/repository/NftMetadataDto.kt @@ -9,7 +9,7 @@ data class NftMetadataDto( val tokenId: String, val tokenAddress: String, val contractType: ContractType, - val chinType: ChainType, + val chainType: ChainType, val nftName: String, val collectionName: String, val image: String, diff --git a/src/main/kotlin/com/api/nft/domain/nft/repository/NftRepository.kt b/src/main/kotlin/com/api/nft/domain/nft/repository/NftRepository.kt index fd57dce..96b6c6a 100644 --- a/src/main/kotlin/com/api/nft/domain/nft/repository/NftRepository.kt +++ b/src/main/kotlin/com/api/nft/domain/nft/repository/NftRepository.kt @@ -7,5 +7,5 @@ import reactor.core.publisher.Mono interface NftRepository : ReactiveCrudRepository, NftRepositorySupport { - fun findByTokenAddressAndTokenIdAndChinType(tokenAddress: String, tokenId: String,chainType: ChainType): Mono + fun findByTokenAddressAndTokenIdAndChainType(tokenAddress: String, tokenId: String,chainType: ChainType): Mono } \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/domain/nft/repository/NftRepositorySupportImpl.kt b/src/main/kotlin/com/api/nft/domain/nft/repository/NftRepositorySupportImpl.kt index a96d47d..cf14bba 100644 --- a/src/main/kotlin/com/api/nft/domain/nft/repository/NftRepositorySupportImpl.kt +++ b/src/main/kotlin/com/api/nft/domain/nft/repository/NftRepositorySupportImpl.kt @@ -16,7 +16,7 @@ class NftRepositorySupportImpl( n.id, n.token_id AS tokenId, n.token_address AS tokenAddress, - n.chain_type AS chinType, + n.chain_type AS chainType, n.nft_name AS nftName, n.collection_name AS collectionName, n.contract_type AS contractType, @@ -36,7 +36,7 @@ class NftRepositorySupportImpl( id = (row.get("id") as Number).toLong(), tokenId = row.get("tokenId", String::class.java)!!, tokenAddress = row.get("tokenAddress", String::class.java)!!, - chinType = row.get("chinType", ChainType::class.java)!!, + chainType = row.get("chainType", ChainType::class.java)!!, nftName = row.get("nftName", String::class.java)!!, collectionName = row.get("collectionName", String::class.java)!!, image = row.get("image", String::class.java) ?: "", @@ -70,7 +70,7 @@ class NftRepositorySupportImpl( id = (row.get("id") as Number).toLong(), tokenId = row.get("tokenId", String::class.java)!!, tokenAddress = row.get("tokenAddress", String::class.java)!!, - chinType = row.get("chainType", ChainType::class.java)!!, + chainType = row.get("chainType", ChainType::class.java)!!, nftName = row.get("nftName", String::class.java)!!, collectionName = row.get("collectionName", String::class.java)!!, image = row.get("image", String::class.java) ?: "", diff --git a/src/main/kotlin/com/api/nft/event/dto/NftResponse.kt b/src/main/kotlin/com/api/nft/event/dto/NftResponse.kt index 03e0b86..e4df42a 100644 --- a/src/main/kotlin/com/api/nft/event/dto/NftResponse.kt +++ b/src/main/kotlin/com/api/nft/event/dto/NftResponse.kt @@ -7,6 +7,4 @@ data class NftResponse( val tokenId: String, val tokenAddress: String, val chainType: ChainType, - val nftName: String?, - val collectionName: String ) diff --git a/src/main/kotlin/com/api/nft/service/api/CollectionService.kt b/src/main/kotlin/com/api/nft/service/api/CollectionService.kt index 82e6fbe..78b8c52 100644 --- a/src/main/kotlin/com/api/nft/service/api/CollectionService.kt +++ b/src/main/kotlin/com/api/nft/service/api/CollectionService.kt @@ -30,9 +30,7 @@ class CollectionService( .onErrorResume(DuplicateKeyException::class.java){ collectionRepository.findByName(name) } - } - ) } } \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/service/api/NftService.kt b/src/main/kotlin/com/api/nft/service/api/NftService.kt index 1a1d478..dcc621c 100644 --- a/src/main/kotlin/com/api/nft/service/api/NftService.kt +++ b/src/main/kotlin/com/api/nft/service/api/NftService.kt @@ -34,7 +34,7 @@ class NftService( } fun findOrCreateNft(tokenAddress: String,tokenId: String, chainType: ChainType): Mono { - return nftRepository.findByTokenAddressAndTokenIdAndChinType(tokenAddress,tokenId,chainType) + return nftRepository.findByTokenAddressAndTokenIdAndChainType(tokenAddress,tokenId,chainType) .switchIfEmpty( moralisApiService.getNFTMetadata(tokenAddress,tokenId,chainType) .flatMap { createNftProcess(it,chainType) } @@ -47,7 +47,6 @@ class NftService( .flatMapMany { Flux.fromIterable(it.result) } .filter { it.contractType == "ERC721"} .flatMap { findOrCreateNft(it.tokenAddress, it.tokenId, chainType) } - } @@ -74,9 +73,7 @@ class NftService( id = this.id!!, tokenId = this.tokenId, tokenAddress = this.tokenAddress, - chainType = this.chinType, - nftName = this.nftName, - collectionName = this.collectionName + chainType = this.chainType, ) fun getNftData(request: NftData, chainType: ChainType): Mono?>> { @@ -96,15 +93,15 @@ class NftService( ): Mono { return collectionService.findOrCreate( nft.name, - nft.collectionLogo, - nft.collectionBannerImage, + nft.collectionLogo ?: metadata?.image, + nft.collectionBannerImage ?: metadata?.image, metadata?.description, ).flatMap { nftRepository.save( Nft( tokenId = nft.tokenId, tokenAddress = nft.tokenAddress, - chinType = chainType, + chainType = chainType, nftName = metadata?.name, collectionName = it.name, tokenHash = nft.tokenHash, diff --git a/src/main/kotlin/com/api/nft/service/api/TransferService.kt b/src/main/kotlin/com/api/nft/service/api/TransferService.kt index 4eb5ead..79a509e 100644 --- a/src/main/kotlin/com/api/nft/service/api/TransferService.kt +++ b/src/main/kotlin/com/api/nft/service/api/TransferService.kt @@ -29,7 +29,7 @@ class TransferService( return moralisApiService.getNftTransfer( nft.tokenAddress, nft.tokenId, - nft.chinType + nft.chainType ).flatMapMany { Flux.fromIterable(it.result) }.map { @@ -64,13 +64,13 @@ class TransferService( val fromBlock = transfer.blockNumber + 1 return infuraApiService.getEthLogs( fromBlock.toString(), - nft.chinType, + nft.chainType, nft.tokenAddress, nft.tokenId ).flatMapMany { ethLogResponses -> Flux.fromIterable(ethLogResponses) }.flatMap { ethLogResponse -> - ethLogResponse.toTransferEntity(nft.id!!, nft.chinType) + ethLogResponse.toTransferEntity(nft.id!!, nft.chainType) .flatMap { transferRepository.save(it) } diff --git a/src/main/kotlin/com/api/nft/service/external/dto/NftRequest.kt b/src/main/kotlin/com/api/nft/service/external/dto/NftRequest.kt new file mode 100644 index 0000000..a5204e0 --- /dev/null +++ b/src/main/kotlin/com/api/nft/service/external/dto/NftRequest.kt @@ -0,0 +1,10 @@ +package com.api.nft.service.external.dto + +import com.api.nft.enums.ChainType + +data class NftRequest( + val id: Long, + val tokenAddress: String, + val tokenId: String, + val chainType: ChainType +) diff --git a/src/test/kotlin/com/api/nft/NftTest.kt b/src/test/kotlin/com/api/nft/NftTest.kt index d57ba83..54f957a 100644 --- a/src/test/kotlin/com/api/nft/NftTest.kt +++ b/src/test/kotlin/com/api/nft/NftTest.kt @@ -50,7 +50,7 @@ class NftTest( id = 1, tokenId = "hello", tokenAddress = "helloAddress", - chinType = ChainType.POLYGON_MAINNET, + chainType = ChainType.POLYGON_MAINNET, contractType = ContractType.ERC721, nftName = "nftName", tokenHash = null, @@ -66,9 +66,7 @@ class NftTest( id = this.id!!, tokenId = this.tokenId, tokenAddress = this.tokenAddress, - chainType = this.chinType, - nftName = this.nftName, - collectionName = this.collectionName + chainType = this.chainType, ) @@ -76,7 +74,6 @@ class NftTest( fun getByWalletNft() { val wallet = "0x01b72b4aa3f66f213d62d53e829bc172a6a72867" val res= nftService.getByWalletNft(wallet,ChainType.POLYGON_MAINNET).blockLast() - println(res?.nftName) println(res?.tokenId) println(res?.tokenAddress) From 7b46434e35e2753615546b6fc075f1f0c355ceb7 Mon Sep 17 00:00:00 2001 From: min-96 Date: Sun, 9 Jun 2024 16:27:07 +0900 Subject: [PATCH 16/38] feat: RabbitMQReceiver to ListingResponse --- .../kotlin/com/api/nft/config/RabbitConfig.kt | 51 +++++++++++++------ .../com/api/nft/domain/attribute/Attribute.kt | 2 +- .../api/nft/domain/collection/Collection.kt | 2 +- src/main/kotlin/com/api/nft/domain/nft/Nft.kt | 2 +- .../com/api/nft/domain/nft/NftListing.kt | 14 +++++ .../nft/repository/NftListingRepository.kt | 9 ++++ .../com/api/nft/domain/trasfer/Transfer.kt | 2 +- src/main/kotlin/com/api/nft/enums/Enums.kt | 4 ++ .../com/api/nft/rabbitMQ/RabbitMQReceiver.kt | 16 ++++++ .../api/nft/service/api/NftListingService.kt | 16 ++++++ .../api/nft/service/dto/ListingResponse.kt | 14 +++++ .../migration/V1__Initial_schema.sql | 14 +++++ 12 files changed, 127 insertions(+), 19 deletions(-) create mode 100644 src/main/kotlin/com/api/nft/domain/nft/NftListing.kt create mode 100644 src/main/kotlin/com/api/nft/domain/nft/repository/NftListingRepository.kt create mode 100644 src/main/kotlin/com/api/nft/rabbitMQ/RabbitMQReceiver.kt create mode 100644 src/main/kotlin/com/api/nft/service/api/NftListingService.kt create mode 100644 src/main/kotlin/com/api/nft/service/dto/ListingResponse.kt diff --git a/src/main/kotlin/com/api/nft/config/RabbitConfig.kt b/src/main/kotlin/com/api/nft/config/RabbitConfig.kt index 66a4c96..b75cc2a 100644 --- a/src/main/kotlin/com/api/nft/config/RabbitConfig.kt +++ b/src/main/kotlin/com/api/nft/config/RabbitConfig.kt @@ -1,8 +1,11 @@ package com.api.nft.config +import org.springframework.amqp.core.Binding +import org.springframework.amqp.core.BindingBuilder +import org.springframework.amqp.core.DirectExchange +import org.springframework.amqp.core.Queue import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration -import org.springframework.amqp.core.* import org.springframework.amqp.rabbit.connection.ConnectionFactory import org.springframework.amqp.rabbit.core.RabbitTemplate import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter @@ -10,27 +13,45 @@ import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter @Configuration class RabbitConfig { @Bean - fun nftQueue(): Queue { - return Queue("nftQueue", true) - } + fun jsonMessageConverter(): Jackson2JsonMessageConverter = Jackson2JsonMessageConverter() @Bean - fun nftExchange(): DirectExchange { - return DirectExchange("nftExchange") + fun rabbitTemplate( + connectionFactory: ConnectionFactory, + jsonMessageConverter: Jackson2JsonMessageConverter + ): RabbitTemplate { + val template = RabbitTemplate(connectionFactory) + template.messageConverter = jsonMessageConverter + return template } - @Bean - fun bindingNftQueue(nftQueue: Queue, nftExchange: DirectExchange): Binding { - return BindingBuilder.bind(nftQueue).to(nftExchange).with("nftRoutingKey") + private fun createQueue(name: String, durable: Boolean = true): Queue { + return Queue(name, durable) + } + + private fun createExchange(name: String): DirectExchange { + return DirectExchange(name) + } + + private fun createBinding(queue: Queue, exchange: DirectExchange, routingKey: String): Binding { + return BindingBuilder.bind(queue).to(exchange).with(routingKey) } @Bean - fun jsonMessageConverter(): Jackson2JsonMessageConverter = Jackson2JsonMessageConverter() + fun nftQueue() = createQueue("nftQueue") @Bean - fun rabbitTemplate(connectionFactory: ConnectionFactory, jsonMessageConverter: Jackson2JsonMessageConverter): RabbitTemplate { - val template = RabbitTemplate(connectionFactory) - template.messageConverter = jsonMessageConverter - return template - } + fun nftExchange() = createExchange("nftExchange") + + @Bean + fun bindingNftQueue(nftQueue: Queue, nftExchange: DirectExchange) = createBinding(nftQueue, nftExchange, "nftRoutingKey") + + @Bean + fun listingQueue() = createQueue("listingQueue") + + @Bean + fun listingExchange() = createExchange("listingExchange") + + @Bean + fun bindingListingQueue(listingQueue: Queue, listingExchange: DirectExchange) = createBinding(listingQueue, listingExchange, "listingRoutingKey") } \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/domain/attribute/Attribute.kt b/src/main/kotlin/com/api/nft/domain/attribute/Attribute.kt index 1080d33..e6052fc 100644 --- a/src/main/kotlin/com/api/nft/domain/attribute/Attribute.kt +++ b/src/main/kotlin/com/api/nft/domain/attribute/Attribute.kt @@ -6,7 +6,7 @@ import lombok.AllArgsConstructor @Table("attribute") @AllArgsConstructor -class Attribute( +data class Attribute( @Id val id: Long? = null, val nftId : Long, val traitType : String? = null, diff --git a/src/main/kotlin/com/api/nft/domain/collection/Collection.kt b/src/main/kotlin/com/api/nft/domain/collection/Collection.kt index 230b192..6971a68 100644 --- a/src/main/kotlin/com/api/nft/domain/collection/Collection.kt +++ b/src/main/kotlin/com/api/nft/domain/collection/Collection.kt @@ -7,7 +7,7 @@ import org.springframework.data.relational.core.mapping.Column @Table("collection") @AllArgsConstructor -class Collection( +data class Collection( @Id val name: String, val logo: String?, @Column("banner_image")val bannerImage: String?, diff --git a/src/main/kotlin/com/api/nft/domain/nft/Nft.kt b/src/main/kotlin/com/api/nft/domain/nft/Nft.kt index 4c7b0b4..f21e2eb 100644 --- a/src/main/kotlin/com/api/nft/domain/nft/Nft.kt +++ b/src/main/kotlin/com/api/nft/domain/nft/Nft.kt @@ -10,7 +10,7 @@ import org.springframework.data.relational.core.mapping.Table //TODO("캡슐화") @Table("nft") @AllArgsConstructor -class Nft( +data class Nft( @Id val id: Long? = null, @Column("token_id") val tokenId: String, @Column("token_address") val tokenAddress: String, diff --git a/src/main/kotlin/com/api/nft/domain/nft/NftListing.kt b/src/main/kotlin/com/api/nft/domain/nft/NftListing.kt new file mode 100644 index 0000000..e51869b --- /dev/null +++ b/src/main/kotlin/com/api/nft/domain/nft/NftListing.kt @@ -0,0 +1,14 @@ +package com.api.nft.domain.nft + +import com.api.nft.enums.TokenType +import org.springframework.data.relational.core.mapping.Table +import java.math.BigDecimal + +//1:1 매핑으로? +@Table("nft_listing") +data class NftListing( + val id: Long? = null, + val price: BigDecimal, + val tokenType: TokenType, + val nftId: Long, +) diff --git a/src/main/kotlin/com/api/nft/domain/nft/repository/NftListingRepository.kt b/src/main/kotlin/com/api/nft/domain/nft/repository/NftListingRepository.kt new file mode 100644 index 0000000..48dd547 --- /dev/null +++ b/src/main/kotlin/com/api/nft/domain/nft/repository/NftListingRepository.kt @@ -0,0 +1,9 @@ +package com.api.nft.domain.nft.repository + +import com.api.nft.domain.nft.NftListing +import org.springframework.data.r2dbc.repository.R2dbcRepository +import reactor.core.publisher.Mono + +interface NftListingRepository : R2dbcRepository { + fun findByNftId(nftId: Long) : Mono +} \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/domain/trasfer/Transfer.kt b/src/main/kotlin/com/api/nft/domain/trasfer/Transfer.kt index 01e9ea8..7141e59 100644 --- a/src/main/kotlin/com/api/nft/domain/trasfer/Transfer.kt +++ b/src/main/kotlin/com/api/nft/domain/trasfer/Transfer.kt @@ -5,7 +5,7 @@ import org.springframework.data.relational.core.mapping.Column import org.springframework.data.relational.core.mapping.Table @Table("transfer") -class Transfer( +data class Transfer( @Id val id: Long? = null, @Column("nft_id") val nftId: Long, @Column("from_address") val fromAddress: String, diff --git a/src/main/kotlin/com/api/nft/enums/Enums.kt b/src/main/kotlin/com/api/nft/enums/Enums.kt index 3427b30..3531380 100644 --- a/src/main/kotlin/com/api/nft/enums/Enums.kt +++ b/src/main/kotlin/com/api/nft/enums/Enums.kt @@ -20,3 +20,7 @@ enum class NetworkType{ enum class ContractType{ ERC721, ERC1155 } + +enum class TokenType { + SAND, MATIC, ETH, BTC +} \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/rabbitMQ/RabbitMQReceiver.kt b/src/main/kotlin/com/api/nft/rabbitMQ/RabbitMQReceiver.kt new file mode 100644 index 0000000..ce4c7bd --- /dev/null +++ b/src/main/kotlin/com/api/nft/rabbitMQ/RabbitMQReceiver.kt @@ -0,0 +1,16 @@ +package com.api.nft.rabbitMQ + +import com.api.nft.service.api.NftListingService +import com.api.nft.service.dto.ListingResponse +import org.springframework.amqp.rabbit.annotation.RabbitListener +import org.springframework.stereotype.Service + +@Service +class RabbitMQReceiver( + private val nftListingService: NftListingService, +) { + @RabbitListener(queues = ["listingQueue"]) + fun listingMessage(listing: ListingResponse){ + nftListingService.update(listing) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/service/api/NftListingService.kt b/src/main/kotlin/com/api/nft/service/api/NftListingService.kt new file mode 100644 index 0000000..ff9eace --- /dev/null +++ b/src/main/kotlin/com/api/nft/service/api/NftListingService.kt @@ -0,0 +1,16 @@ +package com.api.nft.service.api + +import com.api.nft.domain.nft.repository.NftListingRepository +import com.api.nft.service.dto.ListingResponse +import org.springframework.stereotype.Service + +@Service +class NftListingService( + private val nftListingRepository: NftListingRepository, +) { + + fun update(listing: ListingResponse) { + // 있으면 update 없으면 저장 // 있으면 가격비교 후 + nftListingRepository.findByNftId(listing.nftId) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/service/dto/ListingResponse.kt b/src/main/kotlin/com/api/nft/service/dto/ListingResponse.kt new file mode 100644 index 0000000..f5fbf23 --- /dev/null +++ b/src/main/kotlin/com/api/nft/service/dto/ListingResponse.kt @@ -0,0 +1,14 @@ +package com.api.nft.service.dto + +import com.api.nft.enums.TokenType +import java.math.BigDecimal + +data class ListingResponse( + val id : Long, + val nftId : Long, + val address: String, + val createdDateTime: Long, + val endDateTime: Long, + val price: BigDecimal, + val tokenType: TokenType +) diff --git a/src/main/resources/db/postgresql/migration/V1__Initial_schema.sql b/src/main/resources/db/postgresql/migration/V1__Initial_schema.sql index 6a25ff8..c02c85d 100644 --- a/src/main/resources/db/postgresql/migration/V1__Initial_schema.sql +++ b/src/main/resources/db/postgresql/migration/V1__Initial_schema.sql @@ -13,6 +13,14 @@ CREATE TYPE chain_type AS ENUM ( 'POLYGON_AMOY' ); +CREATE TYPE token_type AS ENUM ( + 'MATIC', + 'BTC', + 'ETH', + 'SAND' + ); + + CREATE TABLE IF NOT EXISTS collection ( name varchar(500) PRIMARY KEY, @@ -60,6 +68,12 @@ CREATE TABLE IF NOT EXISTS transfer ( block_timestamp bigint not null ); +CREATE TABLE IF NOT EXISTS nft_listing ( + id SERIAL PRIMARY KEY, + nft_id BIGINT REFERENCES nft(id), + price DECIMAL(19, 4) NOT NULL, + token_type token_type +); From 2b08fb8bfa5dda4c2db1224c5617f6e1f4e1a7c5 Mon Sep 17 00:00:00 2001 From: min-96 Date: Sun, 9 Jun 2024 19:06:31 +0900 Subject: [PATCH 17/38] feat: listing update --- build.gradle.kts | 2 +- src/main/kotlin/com/api/nft/NftApplication.kt | 2 ++ .../kotlin/com/api/nft/config/R2dbcConfig.kt | 5 +++ .../com/api/nft/domain/nft/NftListing.kt | 6 +++- .../nft/repository/NftListingRepository.kt | 8 +++++ .../com/api/nft/rabbitMQ/RabbitMQReceiver.kt | 2 +- .../com/api/nft/schedule/PriceScheduler.kt | 26 +++++++++++++++ .../api/nft/service/api/NftListingService.kt | 31 +++++++++++++++-- .../external/binance/BinanceApiService.kt | 33 +++++++++++++++++++ .../dto/BinanceTickerPriceResponse.kt | 8 +++++ .../com/api/nft/storage/PriceStorage.kt | 18 ++++++++++ .../com/api/nft/util/ChainTypeConvert.kt | 2 ++ src/test/kotlin/com/api/nft/NftTest.kt | 10 ++++++ 13 files changed, 147 insertions(+), 6 deletions(-) create mode 100644 src/main/kotlin/com/api/nft/schedule/PriceScheduler.kt create mode 100644 src/main/kotlin/com/api/nft/service/external/binance/BinanceApiService.kt create mode 100644 src/main/kotlin/com/api/nft/service/external/dto/BinanceTickerPriceResponse.kt create mode 100644 src/main/kotlin/com/api/nft/storage/PriceStorage.kt diff --git a/build.gradle.kts b/build.gradle.kts index 6daaf93..3f9cd76 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -24,7 +24,6 @@ repositories { dependencies { implementation("org.springframework.boot:spring-boot-starter-batch") implementation("org.springframework.boot:spring-boot-starter-quartz") - implementation("org.springframework.boot:spring-boot-starter-data-r2dbc") implementation("org.postgresql:r2dbc-postgresql:1.0.4.RELEASE") implementation("org.postgresql:postgresql:42.7.3") @@ -39,6 +38,7 @@ dependencies { implementation("org.springframework.boot:spring-boot-starter-amqp") + implementation("org.web3j:core:4.9.1") testImplementation("org.springframework.boot:spring-boot-starter-test") diff --git a/src/main/kotlin/com/api/nft/NftApplication.kt b/src/main/kotlin/com/api/nft/NftApplication.kt index 34e03b9..e9c3141 100644 --- a/src/main/kotlin/com/api/nft/NftApplication.kt +++ b/src/main/kotlin/com/api/nft/NftApplication.kt @@ -3,9 +3,11 @@ package com.api.nft import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.context.properties.ConfigurationPropertiesScan import org.springframework.boot.runApplication +import org.springframework.scheduling.annotation.EnableScheduling @SpringBootApplication @ConfigurationPropertiesScan +@EnableScheduling class NftApplication fun main(args: Array) { diff --git a/src/main/kotlin/com/api/nft/config/R2dbcConfig.kt b/src/main/kotlin/com/api/nft/config/R2dbcConfig.kt index 20e5685..78e2e16 100644 --- a/src/main/kotlin/com/api/nft/config/R2dbcConfig.kt +++ b/src/main/kotlin/com/api/nft/config/R2dbcConfig.kt @@ -2,9 +2,11 @@ package com.api.nft.config import com.api.nft.enums.ChainType import com.api.nft.enums.ContractType +import com.api.nft.enums.TokenType import com.api.nft.util.ChainTypeConvert import com.api.nft.util.ContractTypeConverter import com.api.nft.util.StringToEnumConverter +import com.api.nft.util.TokenTypeConvert import io.r2dbc.postgresql.PostgresqlConnectionConfiguration import io.r2dbc.postgresql.PostgresqlConnectionFactory import io.r2dbc.postgresql.codec.EnumCodec @@ -37,6 +39,7 @@ class R2dbcConfig : AbstractR2dbcConfiguration() { EnumCodec.builder() .withEnum("contract_type", ContractType::class.java) .withEnum("chain_type", ChainType::class.java) + .withEnum("token_type", TokenType::class.java) .build() ) .build() @@ -50,6 +53,8 @@ class R2dbcConfig : AbstractR2dbcConfiguration() { converters.add(StringToEnumConverter(ContractType::class.java)) converters.add(ChainTypeConvert(ChainType::class.java)) converters.add(StringToEnumConverter(ChainType::class.java)) + converters.add(TokenTypeConvert(TokenType::class.java)) + converters.add(StringToEnumConverter(TokenType::class.java)) return R2dbcCustomConversions(storeConversions, converters) } diff --git a/src/main/kotlin/com/api/nft/domain/nft/NftListing.kt b/src/main/kotlin/com/api/nft/domain/nft/NftListing.kt index e51869b..458903f 100644 --- a/src/main/kotlin/com/api/nft/domain/nft/NftListing.kt +++ b/src/main/kotlin/com/api/nft/domain/nft/NftListing.kt @@ -11,4 +11,8 @@ data class NftListing( val price: BigDecimal, val tokenType: TokenType, val nftId: Long, -) +){ + fun update(newPrice: BigDecimal,new_tokenType: TokenType): NftListing { + return this.copy(price = newPrice, tokenType = new_tokenType) + } +} diff --git a/src/main/kotlin/com/api/nft/domain/nft/repository/NftListingRepository.kt b/src/main/kotlin/com/api/nft/domain/nft/repository/NftListingRepository.kt index 48dd547..36ff764 100644 --- a/src/main/kotlin/com/api/nft/domain/nft/repository/NftListingRepository.kt +++ b/src/main/kotlin/com/api/nft/domain/nft/repository/NftListingRepository.kt @@ -1,9 +1,17 @@ package com.api.nft.domain.nft.repository import com.api.nft.domain.nft.NftListing +import com.api.nft.enums.TokenType +import org.springframework.data.r2dbc.repository.Query import org.springframework.data.r2dbc.repository.R2dbcRepository import reactor.core.publisher.Mono +import java.math.BigDecimal interface NftListingRepository : R2dbcRepository { + fun findByNftId(nftId: Long) : Mono + + @Query("UPDATE nft_listing SET price = :price, token_type = :tokenType WHERE nft_id = :nftId") + fun updateListing(nftId: Long, price: BigDecimal, tokenType: TokenType): Mono + } \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/rabbitMQ/RabbitMQReceiver.kt b/src/main/kotlin/com/api/nft/rabbitMQ/RabbitMQReceiver.kt index ce4c7bd..b23e0db 100644 --- a/src/main/kotlin/com/api/nft/rabbitMQ/RabbitMQReceiver.kt +++ b/src/main/kotlin/com/api/nft/rabbitMQ/RabbitMQReceiver.kt @@ -11,6 +11,6 @@ class RabbitMQReceiver( ) { @RabbitListener(queues = ["listingQueue"]) fun listingMessage(listing: ListingResponse){ - nftListingService.update(listing) + nftListingService.update(listing).subscribe() } } \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/schedule/PriceScheduler.kt b/src/main/kotlin/com/api/nft/schedule/PriceScheduler.kt new file mode 100644 index 0000000..68d7d85 --- /dev/null +++ b/src/main/kotlin/com/api/nft/schedule/PriceScheduler.kt @@ -0,0 +1,26 @@ +package com.api.nft.schedule + +import com.api.nft.enums.TokenType +import com.api.nft.service.external.binance.BinanceApiService +import com.api.nft.storage.PriceStorage +import org.springframework.scheduling.annotation.Scheduled +import org.springframework.stereotype.Component + +@Component +class PriceScheduler( + private val priceStorage: PriceStorage, + private val binanceApiService: BinanceApiService, + ) +{ + @Scheduled(fixedRate = 3600000) + fun updatePrices() { + TokenType.entries.map { + binanceApiService.getTickerPrice(it) + .subscribe { response-> + priceStorage.update(it,response.price) + println("Updated ${it} price to ${response.price}") + } + } + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/service/api/NftListingService.kt b/src/main/kotlin/com/api/nft/service/api/NftListingService.kt index ff9eace..971076c 100644 --- a/src/main/kotlin/com/api/nft/service/api/NftListingService.kt +++ b/src/main/kotlin/com/api/nft/service/api/NftListingService.kt @@ -1,16 +1,41 @@ package com.api.nft.service.api +import com.api.nft.domain.nft.NftListing import com.api.nft.domain.nft.repository.NftListingRepository import com.api.nft.service.dto.ListingResponse +import com.api.nft.storage.PriceStorage import org.springframework.stereotype.Service +import reactor.core.publisher.Mono +import reactor.kotlin.core.publisher.switchIfEmpty +import java.math.BigDecimal @Service class NftListingService( private val nftListingRepository: NftListingRepository, + private val priceStorage: PriceStorage, ) { - fun update(listing: ListingResponse) { - // 있으면 update 없으면 저장 // 있으면 가격비교 후 - nftListingRepository.findByNftId(listing.nftId) + fun update(newListing: ListingResponse): Mono { + return nftListingRepository.findByNftId(newListing.nftId) + .flatMap { nftListing -> + val currentPrice = priceStorage.get(nftListing.tokenType)?.multiply(nftListing.price) ?: BigDecimal.ZERO + val newPrice = priceStorage.get(newListing.tokenType)?.multiply(newListing.price) ?: BigDecimal.ZERO + if (currentPrice < newPrice) { + nftListingRepository.updateListing(nftListing.nftId, price = newListing.price,newListing.tokenType) + .thenReturn(nftListing.update(newListing.price, newListing.tokenType)) + } else { + Mono.just(nftListing) + } + }.switchIfEmpty { save(newListing) } + } + + fun save(listing: ListingResponse) : Mono { + return nftListingRepository.save( + NftListing( + nftId = listing.nftId, + price = listing.price, + tokenType = listing.tokenType + ) + ) } } \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/service/external/binance/BinanceApiService.kt b/src/main/kotlin/com/api/nft/service/external/binance/BinanceApiService.kt new file mode 100644 index 0000000..ef6b266 --- /dev/null +++ b/src/main/kotlin/com/api/nft/service/external/binance/BinanceApiService.kt @@ -0,0 +1,33 @@ +package com.api.nft.service.external.binance + +import com.api.nft.enums.TokenType +import com.api.nft.service.external.dto.BinanceTickerPriceResponse +import org.springframework.stereotype.Service +import org.springframework.web.reactive.function.client.WebClient +import reactor.core.publisher.Mono + +@Service +class BinanceApiService { + + private val webClient = WebClient.builder() + .baseUrl(baseUrl) + .build() + + + fun getTickerPrice(tokenType: TokenType): Mono { + return webClient.get() + .uri{ + it.path("/api/v3/ticker/price") + it.queryParam("symbol","${tokenType}USDT") + it.build() + } + .retrieve() + .bodyToMono(BinanceTickerPriceResponse::class.java) + } + + + + companion object { + private val baseUrl = "https://api.binance.com" + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/service/external/dto/BinanceTickerPriceResponse.kt b/src/main/kotlin/com/api/nft/service/external/dto/BinanceTickerPriceResponse.kt new file mode 100644 index 0000000..d9da1e1 --- /dev/null +++ b/src/main/kotlin/com/api/nft/service/external/dto/BinanceTickerPriceResponse.kt @@ -0,0 +1,8 @@ +package com.api.nft.service.external.dto + +import java.math.BigDecimal + +data class BinanceTickerPriceResponse( + val symbol : String, + val price: BigDecimal, +) diff --git a/src/main/kotlin/com/api/nft/storage/PriceStorage.kt b/src/main/kotlin/com/api/nft/storage/PriceStorage.kt new file mode 100644 index 0000000..e77c04e --- /dev/null +++ b/src/main/kotlin/com/api/nft/storage/PriceStorage.kt @@ -0,0 +1,18 @@ +package com.api.nft.storage + +import com.api.nft.enums.TokenType +import org.springframework.stereotype.Component +import java.math.BigDecimal +import java.util.concurrent.ConcurrentHashMap + +@Component +class PriceStorage { + private val prices: ConcurrentHashMap = ConcurrentHashMap() + + + fun update(tokenType: TokenType, price: BigDecimal) { + prices[tokenType] = price + } + + fun get(tokenType: TokenType): BigDecimal? = prices[tokenType] +} \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/util/ChainTypeConvert.kt b/src/main/kotlin/com/api/nft/util/ChainTypeConvert.kt index 7782a7d..726dd3c 100644 --- a/src/main/kotlin/com/api/nft/util/ChainTypeConvert.kt +++ b/src/main/kotlin/com/api/nft/util/ChainTypeConvert.kt @@ -2,7 +2,9 @@ package com.api.nft.util import com.api.nft.enums.ChainType import com.api.nft.enums.ContractType +import com.api.nft.enums.TokenType import org.springframework.data.r2dbc.convert.EnumWriteSupport data class ContractTypeConverter>(private val enumType: Class) : EnumWriteSupport() data class ChainTypeConvert>(private val enumType: Class): EnumWriteSupport() +data class TokenTypeConvert>(private val enumType: Class): EnumWriteSupport() diff --git a/src/test/kotlin/com/api/nft/NftTest.kt b/src/test/kotlin/com/api/nft/NftTest.kt index 54f957a..3795869 100644 --- a/src/test/kotlin/com/api/nft/NftTest.kt +++ b/src/test/kotlin/com/api/nft/NftTest.kt @@ -5,12 +5,14 @@ import com.api.nft.domain.nft.Nft import com.api.nft.domain.nft.repository.NftRepository import com.api.nft.enums.ChainType import com.api.nft.enums.ContractType +import com.api.nft.enums.TokenType import com.api.nft.event.dto.NftCreatedEvent import com.api.nft.rabbitMQ.RabbitMQSender import com.api.nft.service.external.moralis.MoralisApiService import com.api.nft.service.api.NftService import com.api.nft.service.api.TransferService import com.api.nft.event.dto.NftResponse +import com.api.nft.service.external.binance.BinanceApiService import com.api.nft.service.external.infura.InfuraApiService import org.junit.jupiter.api.Test import org.springframework.beans.factory.annotation.Autowired @@ -28,6 +30,7 @@ class NftTest( @Autowired private val nftRepository: NftRepository, @Autowired private val infuraApiService: InfuraApiService, @Autowired private val eventPublisher: ApplicationEventPublisher, + @Autowired private val binanceApiService: BinanceApiService, ) { @@ -79,6 +82,13 @@ class NftTest( } + @Test + fun binanceTest() { + val res = binanceApiService.getTickerPrice(TokenType.ETH).block() + println(res?.symbol) + println(res?.price) + } + } \ No newline at end of file From 2010fb82ce9b3c1bf313b478876571dbad6050f0 Mon Sep 17 00:00:00 2001 From: min-96 Date: Sun, 9 Jun 2024 20:28:49 +0900 Subject: [PATCH 18/38] feat: batchDelete --- src/main/kotlin/com/api/nft/config/RabbitConfig.kt | 8 ++++++++ src/main/kotlin/com/api/nft/domain/nft/NftListing.kt | 1 - .../api/nft/domain/nft/repository/NftListingRepository.kt | 2 ++ src/main/kotlin/com/api/nft/rabbitMQ/RabbitMQReceiver.kt | 6 ++++++ .../kotlin/com/api/nft/service/api/NftListingService.kt | 4 ++++ 5 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/com/api/nft/config/RabbitConfig.kt b/src/main/kotlin/com/api/nft/config/RabbitConfig.kt index b75cc2a..f7ecdcb 100644 --- a/src/main/kotlin/com/api/nft/config/RabbitConfig.kt +++ b/src/main/kotlin/com/api/nft/config/RabbitConfig.kt @@ -54,4 +54,12 @@ class RabbitConfig { @Bean fun bindingListingQueue(listingQueue: Queue, listingExchange: DirectExchange) = createBinding(listingQueue, listingExchange, "listingRoutingKey") + + @Bean + fun listingCancelQueue() = createQueue("listingCancelQueue") + + @Bean + fun listingCancelExchange() = createExchange("listingCancelExchange") + @Bean + fun bindingListingCancelQueue(listingCancelQueue: Queue, listingCancelExchange: DirectExchange) = createBinding(listingCancelQueue, listingCancelExchange, "listingCancelRoutingKey") } \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/domain/nft/NftListing.kt b/src/main/kotlin/com/api/nft/domain/nft/NftListing.kt index 458903f..5536d2f 100644 --- a/src/main/kotlin/com/api/nft/domain/nft/NftListing.kt +++ b/src/main/kotlin/com/api/nft/domain/nft/NftListing.kt @@ -4,7 +4,6 @@ import com.api.nft.enums.TokenType import org.springframework.data.relational.core.mapping.Table import java.math.BigDecimal -//1:1 매핑으로? @Table("nft_listing") data class NftListing( val id: Long? = null, diff --git a/src/main/kotlin/com/api/nft/domain/nft/repository/NftListingRepository.kt b/src/main/kotlin/com/api/nft/domain/nft/repository/NftListingRepository.kt index 36ff764..a38dc64 100644 --- a/src/main/kotlin/com/api/nft/domain/nft/repository/NftListingRepository.kt +++ b/src/main/kotlin/com/api/nft/domain/nft/repository/NftListingRepository.kt @@ -14,4 +14,6 @@ interface NftListingRepository : R2dbcRepository { @Query("UPDATE nft_listing SET price = :price, token_type = :tokenType WHERE nft_id = :nftId") fun updateListing(nftId: Long, price: BigDecimal, tokenType: TokenType): Mono + fun deleteAllByNftIdIn(nftId: List): Mono + } \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/rabbitMQ/RabbitMQReceiver.kt b/src/main/kotlin/com/api/nft/rabbitMQ/RabbitMQReceiver.kt index b23e0db..ad3e03a 100644 --- a/src/main/kotlin/com/api/nft/rabbitMQ/RabbitMQReceiver.kt +++ b/src/main/kotlin/com/api/nft/rabbitMQ/RabbitMQReceiver.kt @@ -13,4 +13,10 @@ class RabbitMQReceiver( fun listingMessage(listing: ListingResponse){ nftListingService.update(listing).subscribe() } + + @RabbitListener(queues = ["listingCancelQueue"]) + fun listingCancelMessage(nftIds: List){ + println("ids: " + nftIds.toList()) + nftListingService.batchDelete(nftIds).subscribe() + } } \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/service/api/NftListingService.kt b/src/main/kotlin/com/api/nft/service/api/NftListingService.kt index 971076c..f3409e4 100644 --- a/src/main/kotlin/com/api/nft/service/api/NftListingService.kt +++ b/src/main/kotlin/com/api/nft/service/api/NftListingService.kt @@ -38,4 +38,8 @@ class NftListingService( ) ) } + + fun batchDelete(nftIds: List): Mono { + return nftListingRepository.deleteAllByNftIdIn(nftIds) + } } \ No newline at end of file From 95e114886673166055f932e8da75965375d4a905 Mon Sep 17 00:00:00 2001 From: min-96 Date: Sun, 16 Jun 2024 01:50:07 +0900 Subject: [PATCH 19/38] feat: set redis cluster --- build.gradle.kts | 2 + docker-compose.yaml | 129 +++++++++++++++++- redis.conf | 7 + src/main/kotlin/com/api/nft/NftApplication.kt | 2 +- .../kotlin/com/api/nft/config/RedisConfig.kt | 36 +++++ .../com/api/nft/controller/NftController.kt | 7 - .../com/api/nft/service/api/NftService.kt | 1 + .../api/nft/service/api/TransferService.kt | 1 - .../service/external/dto/NftTransferData.kt | 8 +- .../external/moralis/MoralisApiService.kt | 2 +- src/main/resources/application.yml | 10 +- src/test/kotlin/com/api/nft/NftTest.kt | 14 ++ 12 files changed, 204 insertions(+), 15 deletions(-) create mode 100644 redis.conf create mode 100644 src/main/kotlin/com/api/nft/config/RedisConfig.kt diff --git a/build.gradle.kts b/build.gradle.kts index 3f9cd76..8c09c6a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -38,6 +38,8 @@ dependencies { implementation("org.springframework.boot:spring-boot-starter-amqp") + implementation("org.springframework.boot:spring-boot-starter-data-redis") + implementation("org.springframework:spring-context") implementation("org.web3j:core:4.9.1") diff --git a/docker-compose.yaml b/docker-compose.yaml index cbdcd22..49ae1a9 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -21,4 +21,131 @@ services: - '5672:5672' - '15672:15672' volumes: - - ./rabbitmq_data:/var/lib/rabbitmq \ No newline at end of file + - ./rabbitmq_data:/var/lib/rabbitmq + + redis-1: + image: redis:latest + command: redis-server --appendonly yes --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 + ports: + - "6379:6379" + networks: + redis-cluster: + ipv4_address: 172.24.0.2 + volumes: + - redis1_data:/data + - ./redis.conf:/usr/local/etc/redis/redis.conf + + redis-2: + image: redis:latest + command: redis-server --appendonly yes --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 + ports: + - "6380:6379" + networks: + redis-cluster: + ipv4_address: 172.24.0.3 + volumes: + - redis2_data:/data + - ./redis.conf:/usr/local/etc/redis/redis.conf + + + redis-3: + image: redis:latest + command: redis-server --appendonly yes --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 + ports: + - "6381:6379" + networks: + redis-cluster: + ipv4_address: 172.24.0.4 + volumes: + - redis3_data:/data + - ./redis.conf:/usr/local/etc/redis/redis.conf + + redis-4: + image: redis:latest + command: redis-server --appendonly yes --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 + ports: + - "6382:6379" + networks: + redis-cluster: + ipv4_address: 172.24.0.5 + volumes: + - redis4_data:/data + - ./redis.conf:/usr/local/etc/redis/redis.conf + + redis-5: + image: redis:latest + command: redis-server --appendonly yes --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 + ports: + - "6383:6379" + networks: + redis-cluster: + ipv4_address: 172.24.0.6 + volumes: + - redis5_data:/data + - ./redis.conf:/usr/local/etc/redis/redis.conf + + redis-6: + image: redis:latest + command: redis-server --appendonly yes --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 + ports: + - "6384:6379" + networks: + redis-cluster: + ipv4_address: 172.24.0.7 + volumes: + - redis6_data:/data + - ./redis.conf:/usr/local/etc/redis/redis.conf + + redis-7: + image: redis:latest + command: redis-server --appendonly yes --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 + ports: + - "6385:6379" + networks: + redis-cluster: + ipv4_address: 172.24.0.8 + volumes: + - redis7_data:/data + - ./redis.conf:/usr/local/etc/redis/redis.conf + + redis-8: + image: redis:latest + command: redis-server --appendonly yes --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 + ports: + - "6386:6379" + networks: + redis-cluster: + ipv4_address: 172.24.0.9 + volumes: + - redis8_data:/data + - ./redis.conf:/usr/local/etc/redis/redis.conf + + redis-9: + image: redis:latest + command: redis-server --appendonly yes --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 + ports: + - "6387:6379" + networks: + redis-cluster: + ipv4_address: 172.24.0.10 + volumes: + - redis9_data:/data + - ./redis.conf:/usr/local/etc/redis/redis.conf + +networks: + redis-cluster: + driver: bridge + ipam: + config: + - subnet: 172.24.0.0/16 + +volumes: + redis1_data: + redis2_data: + redis3_data: + redis4_data: + redis5_data: + redis6_data: + redis7_data: + redis8_data: + redis9_data: \ No newline at end of file diff --git a/redis.conf b/redis.conf new file mode 100644 index 0000000..80d66c4 --- /dev/null +++ b/redis.conf @@ -0,0 +1,7 @@ +maxmemory 2gb +maxmemory-policy allkeys-lru +save 900 1 +save 300 10 +save 60 10000 +appendonly yes +appendfilename "appendonly.aof" \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/NftApplication.kt b/src/main/kotlin/com/api/nft/NftApplication.kt index e9c3141..a52da12 100644 --- a/src/main/kotlin/com/api/nft/NftApplication.kt +++ b/src/main/kotlin/com/api/nft/NftApplication.kt @@ -7,7 +7,7 @@ import org.springframework.scheduling.annotation.EnableScheduling @SpringBootApplication @ConfigurationPropertiesScan -@EnableScheduling +//@EnableScheduling class NftApplication fun main(args: Array) { diff --git a/src/main/kotlin/com/api/nft/config/RedisConfig.kt b/src/main/kotlin/com/api/nft/config/RedisConfig.kt new file mode 100644 index 0000000..4de21a8 --- /dev/null +++ b/src/main/kotlin/com/api/nft/config/RedisConfig.kt @@ -0,0 +1,36 @@ +package com.api.nft.config + +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.data.redis.connection.RedisClusterConfiguration +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory +import org.springframework.data.redis.core.RedisTemplate +import org.springframework.data.redis.serializer.StringRedisSerializer + +@Configuration +class RedisConfig { + + @Bean + fun redisClusterConfiguration(): RedisClusterConfiguration { + val clusterNodes = listOf( + "172.24.0.2:6379", + "172.24.0.3:6379", + "172.24.0.4:6379" + ) + return RedisClusterConfiguration(clusterNodes) + } + + @Bean + fun lettuceConnectionFactory(redisClusterConfiguration: RedisClusterConfiguration): LettuceConnectionFactory { + return LettuceConnectionFactory(redisClusterConfiguration) + } + + @Bean + fun redisTemplate(lettuceConnectionFactory: LettuceConnectionFactory): RedisTemplate { + val redisTemplate = RedisTemplate() + redisTemplate.setConnectionFactory(lettuceConnectionFactory) + redisTemplate.keySerializer = StringRedisSerializer() + redisTemplate.valueSerializer = StringRedisSerializer() + return redisTemplate + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/controller/NftController.kt b/src/main/kotlin/com/api/nft/controller/NftController.kt index b2c6f90..efb2783 100644 --- a/src/main/kotlin/com/api/nft/controller/NftController.kt +++ b/src/main/kotlin/com/api/nft/controller/NftController.kt @@ -28,12 +28,6 @@ class NftController( private val transferService: TransferService, ) { - // @PostMapping("/save/{chainType}") - // fun save(@PathVariable chainType: ChainType,@RequestBody requests: List): Flux { - // return nftService.saveNfts(requests,chainType) - // - // } - @GetMapping fun getAllByIds(@RequestParam nftIds: List): Flux { return nftService.findAllById(nftIds) @@ -44,7 +38,6 @@ class NftController( return transferService.findOrUpdateByNftId(nftId) .collectList() .map { ResponseEntity.ok(it) } - .defaultIfEmpty( ResponseEntity.notFound().build()) .onErrorResume { Mono.just(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build()) } } diff --git a/src/main/kotlin/com/api/nft/service/api/NftService.kt b/src/main/kotlin/com/api/nft/service/api/NftService.kt index dcc621c..0adac6e 100644 --- a/src/main/kotlin/com/api/nft/service/api/NftService.kt +++ b/src/main/kotlin/com/api/nft/service/api/NftService.kt @@ -62,6 +62,7 @@ class NftService( )) .then(Mono.just(nft)) .doOnSuccess { eventPublisher.publishEvent(NftCreatedEvent(this, nft.toResponse())) } + .doOnSuccess { } .flatMap { transferService.createTransfer(nft).thenReturn(nft) } diff --git a/src/main/kotlin/com/api/nft/service/api/TransferService.kt b/src/main/kotlin/com/api/nft/service/api/TransferService.kt index 79a509e..dde0381 100644 --- a/src/main/kotlin/com/api/nft/service/api/TransferService.kt +++ b/src/main/kotlin/com/api/nft/service/api/TransferService.kt @@ -49,7 +49,6 @@ class TransferService( blockTimestamp = this.blockTimestamp.toEpochMilli() ) - // 동시성 이슈 체크 fun findOrUpdateByNftId(nftId: Long): Flux { return nftRepository.findById(nftId).flatMapMany { nft -> transferRepository.findByNftIdOrderByBlockTimestampDesc(nftId).next() diff --git a/src/main/kotlin/com/api/nft/service/external/dto/NftTransferData.kt b/src/main/kotlin/com/api/nft/service/external/dto/NftTransferData.kt index 0726e7a..2ef6613 100644 --- a/src/main/kotlin/com/api/nft/service/external/dto/NftTransferData.kt +++ b/src/main/kotlin/com/api/nft/service/external/dto/NftTransferData.kt @@ -1,14 +1,15 @@ package com.api.nft.service.external.dto +import com.fasterxml.jackson.annotation.JsonIgnoreProperties import com.fasterxml.jackson.annotation.JsonProperty data class NftTransferData( val page: String, - @JsonProperty("page_size")val pageSize: String, + @JsonProperty("page_size")val pageSize: String?, val cursor: String?, val result: List, @JsonProperty("block_exists") val blockExists: Boolean, - @JsonProperty("index_complete") val indexComplete: Boolean + @JsonIgnoreProperties(ignoreUnknown = true) @JsonProperty("index_complete") val indexComplete: Boolean ) data class ResultData ( @@ -29,6 +30,7 @@ data class ResultData ( @JsonProperty("transaction_index") val transactionIndex: String, @JsonProperty("log_index") val logIndex: String, val operator: String?, - @JsonProperty("possible_spam") val possibleSpam: String, + @JsonProperty("possible_spam") val possibleSpam: Boolean, @JsonProperty("verified_collection") val verifiedCollection : String, + @JsonProperty("verified") val verified : Int, ) diff --git a/src/main/kotlin/com/api/nft/service/external/moralis/MoralisApiService.kt b/src/main/kotlin/com/api/nft/service/external/moralis/MoralisApiService.kt index 52d5223..01401ce 100644 --- a/src/main/kotlin/com/api/nft/service/external/moralis/MoralisApiService.kt +++ b/src/main/kotlin/com/api/nft/service/external/moralis/MoralisApiService.kt @@ -16,7 +16,7 @@ class MoralisApiService { .baseUrl(baseUrl) .build() - private fun queryParamByChain(chain: ChainType): String? { + private fun queryParamByChain(chain: ChainType): String { val chain = when (chain) { ChainType.ETHEREUM_MAINNET -> "0x1" ChainType.POLYGON_MAINNET -> "0x89" diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 720e225..9b16b03 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -12,12 +12,20 @@ spring: url: r2dbc:postgresql://localhost:5434/nft username: nft password: nft - rabbitmq: host: localhost port: 5672 username: closeSea password: closeSeaP@ssword + data: + redis: + cluster: + nodes: 172.24.0.2:6379,172.24.0.3:6379,172.24.0.4:6379 + lettuce: + pool: + max-active: 8 + max-idle: 8 + min-idle: 0 logging: level: diff --git a/src/test/kotlin/com/api/nft/NftTest.kt b/src/test/kotlin/com/api/nft/NftTest.kt index 3795869..7a6743d 100644 --- a/src/test/kotlin/com/api/nft/NftTest.kt +++ b/src/test/kotlin/com/api/nft/NftTest.kt @@ -89,6 +89,20 @@ class NftTest( println(res?.price) } + @Test + fun transferTest() { + val nft = nftRepository.findById(3L).block() + + transferService.createTransfer(nft!!).block() + } + + @Test + fun trasfermoralist() { + val res =moralisApiService.getNftTransfer("0xa3784fe9104fdc0b988769fba7459ece2fb36eea","0",ChainType.POLYGON_MAINNET).block() + println(res.toString()) + + + } } \ No newline at end of file From 7747334a282370b5c3b05c63e247cfc631ebc906 Mon Sep 17 00:00:00 2001 From: min-96 Date: Mon, 17 Jun 2024 01:26:29 +0900 Subject: [PATCH 20/38] feat: set redis cluster --- build.gradle.kts | 1 - docker-compose.yaml | 26 +++++----- redis.conf | 13 ++--- scan_all_nodes.sh | 51 +++++++++++++++++++ .../kotlin/com/api/nft/config/RedisConfig.kt | 37 ++++++++++---- .../com/api/nft/controller/NftController.kt | 5 +- .../dto/NftMetadataResponse.kt} | 8 ++- .../nft/repository/NftRepositorySupport.kt | 5 +- .../repository/NftRepositorySupportImpl.kt | 23 ++++++--- .../com/api/nft/service/RedisService.kt | 35 +++++++++++++ .../api/nft/service/api/NftListingService.kt | 6 +++ .../com/api/nft/service/api/NftService.kt | 15 +++--- src/main/resources/application.yml | 6 ++- src/test/kotlin/com/api/nft/NftTest.kt | 38 ++++++++++++++ 14 files changed, 219 insertions(+), 50 deletions(-) create mode 100755 scan_all_nodes.sh rename src/main/kotlin/com/api/nft/{domain/nft/repository/NftMetadataDto.kt => controller/dto/NftMetadataResponse.kt} (60%) create mode 100644 src/main/kotlin/com/api/nft/service/RedisService.kt diff --git a/build.gradle.kts b/build.gradle.kts index 8c09c6a..bf49977 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -23,7 +23,6 @@ repositories { dependencies { implementation("org.springframework.boot:spring-boot-starter-batch") - implementation("org.springframework.boot:spring-boot-starter-quartz") implementation("org.springframework.boot:spring-boot-starter-data-r2dbc") implementation("org.postgresql:r2dbc-postgresql:1.0.4.RELEASE") implementation("org.postgresql:postgresql:42.7.3") diff --git a/docker-compose.yaml b/docker-compose.yaml index 49ae1a9..0eb967f 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -25,19 +25,16 @@ services: redis-1: image: redis:latest - command: redis-server --appendonly yes --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 - ports: - - "6379:6379" + command: redis-server --appendonly yes --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 --port 6379 networks: redis-cluster: ipv4_address: 172.24.0.2 volumes: - redis1_data:/data - ./redis.conf:/usr/local/etc/redis/redis.conf - redis-2: image: redis:latest - command: redis-server --appendonly yes --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 + command: redis-server --appendonly yes --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 --port 6379 ports: - "6380:6379" networks: @@ -47,10 +44,9 @@ services: - redis2_data:/data - ./redis.conf:/usr/local/etc/redis/redis.conf - redis-3: image: redis:latest - command: redis-server --appendonly yes --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 + command: redis-server --appendonly yes --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 --port 6379 ports: - "6381:6379" networks: @@ -62,7 +58,7 @@ services: redis-4: image: redis:latest - command: redis-server --appendonly yes --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 + command: redis-server --appendonly yes --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 --port 6379 ports: - "6382:6379" networks: @@ -74,7 +70,7 @@ services: redis-5: image: redis:latest - command: redis-server --appendonly yes --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 + command: redis-server --appendonly yes --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 --port 6379 ports: - "6383:6379" networks: @@ -86,7 +82,7 @@ services: redis-6: image: redis:latest - command: redis-server --appendonly yes --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 + command: redis-server --appendonly yes --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 --port 6379 ports: - "6384:6379" networks: @@ -98,7 +94,7 @@ services: redis-7: image: redis:latest - command: redis-server --appendonly yes --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 + command: redis-server --appendonly yes --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 --port 6379 ports: - "6385:6379" networks: @@ -110,7 +106,7 @@ services: redis-8: image: redis:latest - command: redis-server --appendonly yes --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 + command: redis-server --appendonly yes --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 --port 6379 ports: - "6386:6379" networks: @@ -122,7 +118,7 @@ services: redis-9: image: redis:latest - command: redis-server --appendonly yes --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 + command: redis-server --appendonly yes --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 --port 6379 ports: - "6387:6379" networks: @@ -132,6 +128,7 @@ services: - redis9_data:/data - ./redis.conf:/usr/local/etc/redis/redis.conf + networks: redis-cluster: driver: bridge @@ -139,6 +136,7 @@ networks: config: - subnet: 172.24.0.0/16 + volumes: redis1_data: redis2_data: @@ -148,4 +146,4 @@ volumes: redis6_data: redis7_data: redis8_data: - redis9_data: \ No newline at end of file + redis9_data: diff --git a/redis.conf b/redis.conf index 80d66c4..12a8651 100644 --- a/redis.conf +++ b/redis.conf @@ -1,7 +1,8 @@ -maxmemory 2gb -maxmemory-policy allkeys-lru -save 900 1 -save 300 10 -save 60 10000 +port 6379 +bind 0.0.0.0 +protected-mode no +cluster-enabled yes +cluster-config-file nodes.conf +cluster-node-timeout 5000 appendonly yes -appendfilename "appendonly.aof" \ No newline at end of file +appendfilename "appendonly.aof" diff --git a/scan_all_nodes.sh b/scan_all_nodes.sh new file mode 100755 index 0000000..15c474e --- /dev/null +++ b/scan_all_nodes.sh @@ -0,0 +1,51 @@ +#!/bin/bash + +# Define an array of all Redis nodes +nodes=( + "172.24.0.2:6379" + "172.24.0.3:6379" + "172.24.0.4:6379" + "172.24.0.5:6379" + "172.24.0.6:6379" + "172.24.0.7:6379" + "172.24.0.8:6379" + "172.24.0.9:6379" + "172.24.0.10:6379" +) + +# Function to scan all keys in a Redis node +scan_keys() { + local host=$1 + local port=$2 + local cursor=0 + + echo "Scanning keys on node $host:$port" + + while : + do + # Use redis-cli to scan keys + result=$(docker exec -it nft-redis-1-1 redis-cli -h $host -p $port SCAN $cursor) + + # Extract the new cursor and keys + cursor=$(echo "$result" | head -n 1) + keys=$(echo "$result" | tail -n +2) + + # Print keys + if [ -n "$keys" ]; then + echo "$keys" + fi + + # Break if cursor is 0 + if [ "$cursor" == "0" ]; then + break + fi + done +} + +# Loop through each node and scan keys +for node in "${nodes[@]}" +do + host=$(echo $node | cut -d':' -f1) + port=$(echo $node | cut -d':' -f2) + scan_keys $host $port +done diff --git a/src/main/kotlin/com/api/nft/config/RedisConfig.kt b/src/main/kotlin/com/api/nft/config/RedisConfig.kt index 4de21a8..d1ac14c 100644 --- a/src/main/kotlin/com/api/nft/config/RedisConfig.kt +++ b/src/main/kotlin/com/api/nft/config/RedisConfig.kt @@ -3,9 +3,13 @@ package com.api.nft.config import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import org.springframework.data.redis.connection.RedisClusterConfiguration +import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory -import org.springframework.data.redis.core.RedisTemplate +import org.springframework.data.redis.core.ReactiveRedisTemplate +import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer +import org.springframework.data.redis.serializer.RedisSerializationContext import org.springframework.data.redis.serializer.StringRedisSerializer +import java.time.Duration @Configuration class RedisConfig { @@ -15,22 +19,37 @@ class RedisConfig { val clusterNodes = listOf( "172.24.0.2:6379", "172.24.0.3:6379", - "172.24.0.4:6379" + "172.24.0.4:6379", + "172.24.0.5:6379", + "172.24.0.6:6379", + "172.24.0.7:6379", + "172.24.0.8:6379", + "172.24.0.9:6379", + "172.24.0.10:6379" ) return RedisClusterConfiguration(clusterNodes) } @Bean fun lettuceConnectionFactory(redisClusterConfiguration: RedisClusterConfiguration): LettuceConnectionFactory { - return LettuceConnectionFactory(redisClusterConfiguration) + val clientConfig = LettuceClientConfiguration.builder() + .commandTimeout(Duration.ofSeconds(10)) // 타임아웃 설정 + .build() + return LettuceConnectionFactory(redisClusterConfiguration, clientConfig) } @Bean - fun redisTemplate(lettuceConnectionFactory: LettuceConnectionFactory): RedisTemplate { - val redisTemplate = RedisTemplate() - redisTemplate.setConnectionFactory(lettuceConnectionFactory) - redisTemplate.keySerializer = StringRedisSerializer() - redisTemplate.valueSerializer = StringRedisSerializer() - return redisTemplate + fun reactiveRedisTemplate(lettuceConnectionFactory: LettuceConnectionFactory): ReactiveRedisTemplate { + val keySerializer = StringRedisSerializer() + val valueSerializer = GenericJackson2JsonRedisSerializer() + + val serializationContext = RedisSerializationContext.newSerializationContext() + .key(keySerializer) + .value(valueSerializer) + .hashKey(keySerializer) + .hashValue(valueSerializer) + .build() + + return ReactiveRedisTemplate(lettuceConnectionFactory, serializationContext) } } \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/controller/NftController.kt b/src/main/kotlin/com/api/nft/controller/NftController.kt index efb2783..6740fef 100644 --- a/src/main/kotlin/com/api/nft/controller/NftController.kt +++ b/src/main/kotlin/com/api/nft/controller/NftController.kt @@ -1,12 +1,11 @@ package com.api.nft.controller -import com.api.nft.domain.nft.repository.NftMetadataDto +import com.api.nft.controller.dto.NftMetadataResponse import com.api.nft.domain.trasfer.Transfer import com.api.nft.enums.ChainType import com.api.nft.event.dto.NftResponse import com.api.nft.service.api.NftService import com.api.nft.service.api.TransferService -import com.api.nft.service.external.dto.NftData import com.api.nft.service.external.dto.NftRequest import org.springframework.http.HttpStatus import org.springframework.http.ResponseEntity @@ -29,7 +28,7 @@ class NftController( ) { @GetMapping - fun getAllByIds(@RequestParam nftIds: List): Flux { + fun getAllByIds(@RequestParam nftIds: List): Flux { return nftService.findAllById(nftIds) } diff --git a/src/main/kotlin/com/api/nft/domain/nft/repository/NftMetadataDto.kt b/src/main/kotlin/com/api/nft/controller/dto/NftMetadataResponse.kt similarity index 60% rename from src/main/kotlin/com/api/nft/domain/nft/repository/NftMetadataDto.kt rename to src/main/kotlin/com/api/nft/controller/dto/NftMetadataResponse.kt index 4f95d09..5963428 100644 --- a/src/main/kotlin/com/api/nft/domain/nft/repository/NftMetadataDto.kt +++ b/src/main/kotlin/com/api/nft/controller/dto/NftMetadataResponse.kt @@ -1,10 +1,12 @@ -package com.api.nft.domain.nft.repository +package com.api.nft.controller.dto import com.api.nft.enums.ChainType import com.api.nft.enums.ContractType +import com.api.nft.enums.TokenType +import java.math.BigDecimal -data class NftMetadataDto( +data class NftMetadataResponse( val id: Long, val tokenId: String, val tokenAddress: String, @@ -13,4 +15,6 @@ data class NftMetadataDto( val nftName: String, val collectionName: String, val image: String, + val lastPrice: BigDecimal?, + val tokenType: TokenType?, ) diff --git a/src/main/kotlin/com/api/nft/domain/nft/repository/NftRepositorySupport.kt b/src/main/kotlin/com/api/nft/domain/nft/repository/NftRepositorySupport.kt index 8fbe5a2..188b722 100644 --- a/src/main/kotlin/com/api/nft/domain/nft/repository/NftRepositorySupport.kt +++ b/src/main/kotlin/com/api/nft/domain/nft/repository/NftRepositorySupport.kt @@ -1,9 +1,10 @@ package com.api.nft.domain.nft.repository +import com.api.nft.controller.dto.NftMetadataResponse import reactor.core.publisher.Flux import reactor.core.publisher.Mono interface NftRepositorySupport { - fun findByNftJoinMetadata(nftId: Long) : Mono - fun findAllByNftJoinMetadata(nftIds: List) : Flux + fun findByNftJoinMetadata(nftId: Long) : Mono + fun findAllByNftJoinMetadata(nftIds: List) : Flux } \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/domain/nft/repository/NftRepositorySupportImpl.kt b/src/main/kotlin/com/api/nft/domain/nft/repository/NftRepositorySupportImpl.kt index cf14bba..0f0ab9b 100644 --- a/src/main/kotlin/com/api/nft/domain/nft/repository/NftRepositorySupportImpl.kt +++ b/src/main/kotlin/com/api/nft/domain/nft/repository/NftRepositorySupportImpl.kt @@ -1,15 +1,18 @@ package com.api.nft.domain.nft.repository +import com.api.nft.controller.dto.NftMetadataResponse import com.api.nft.enums.ChainType import com.api.nft.enums.ContractType +import com.api.nft.enums.TokenType import org.springframework.data.r2dbc.core.R2dbcEntityTemplate import reactor.core.publisher.Flux import reactor.core.publisher.Mono +import java.math.BigDecimal class NftRepositorySupportImpl( private val r2dbcEntityTemplate: R2dbcEntityTemplate ): NftRepositorySupport { - override fun findByNftJoinMetadata(id: Long): Mono { + override fun findByNftJoinMetadata(id: Long): Mono { val query = """ SELECT @@ -20,19 +23,21 @@ class NftRepositorySupportImpl( n.nft_name AS nftName, n.collection_name AS collectionName, n.contract_type AS contractType, - m.image AS image + m.image AS image, + nl.price AS lastPrice, + nl.token_type AS tokenType FROM nft n JOIN metadata m ON n.id = m.nft_id + JOIN nft_listing nl ON n.id = nl.nft_id WHERE n.id = :$1 - """ return r2dbcEntityTemplate.databaseClient.sql(query) .bind(0, id) .map { row, data -> - NftMetadataDto( + NftMetadataResponse( id = (row.get("id") as Number).toLong(), tokenId = row.get("tokenId", String::class.java)!!, tokenAddress = row.get("tokenAddress", String::class.java)!!, @@ -41,11 +46,13 @@ class NftRepositorySupportImpl( collectionName = row.get("collectionName", String::class.java)!!, image = row.get("image", String::class.java) ?: "", contractType = row.get("contractType", ContractType::class.java)!!, + lastPrice = row.get("lastPrice", BigDecimal::class.java), + tokenType = row.get("tokenType", TokenType::class.java) ) }.first() } - override fun findAllByNftJoinMetadata(ids: List): Flux { + override fun findAllByNftJoinMetadata(ids: List): Flux { val query = """ SELECT n.id, @@ -56,17 +63,19 @@ class NftRepositorySupportImpl( n.collection_name AS collectionName, n.contract_type AS contractType, m.image AS image + nl.token_type AS tokenType FROM nft n JOIN metadata m ON n.id = m.nft_id + JOIN nft_listing nl ON nft.id = nl.nft_id WHERE n.id IN (:$1) """ return r2dbcEntityTemplate.databaseClient.sql(query) .bind(0, ids) .map { row, metadata -> - NftMetadataDto( + NftMetadataResponse( id = (row.get("id") as Number).toLong(), tokenId = row.get("tokenId", String::class.java)!!, tokenAddress = row.get("tokenAddress", String::class.java)!!, @@ -75,6 +84,8 @@ class NftRepositorySupportImpl( collectionName = row.get("collectionName", String::class.java)!!, image = row.get("image", String::class.java) ?: "", contractType = row.get("contractType", ContractType::class.java)!!, + lastPrice = row.get("lastPrice", BigDecimal::class.java), + tokenType = row.get("tokenType", TokenType::class.java), ) } .all() diff --git a/src/main/kotlin/com/api/nft/service/RedisService.kt b/src/main/kotlin/com/api/nft/service/RedisService.kt new file mode 100644 index 0000000..72dcfc7 --- /dev/null +++ b/src/main/kotlin/com/api/nft/service/RedisService.kt @@ -0,0 +1,35 @@ +package com.api.nft.service + +import com.api.nft.domain.nft.Nft +import com.api.nft.domain.nft.NftListing +import com.api.nft.domain.nft.repository.NftRepository +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import org.springframework.data.redis.core.ReactiveRedisTemplate +import org.springframework.stereotype.Service +import reactor.core.publisher.Mono + +@Service +class RedisService( + private val reactiveRedisTemplate: ReactiveRedisTemplate, + private val nftRepository: NftRepository, +) { + + private val logger: Logger = LoggerFactory.getLogger(RedisService::class.java) + + fun saveNftToRedis(nft: Nft): Mono { + return nftRepository.findByNftJoinMetadata(nft.id!!) + .doOnNext { data -> logger.info("Data retrieved: $data") } // Log data to verify it is fetched + .flatMap { data -> + println("asdasdasda") + reactiveRedisTemplate.opsForValue().set("NFT:${nft.id}", data).then() + } + .doOnError { error -> logger.error("Error occurred: ${error.message}", error) } // Log any errors + } + fun updateToRedis(nft: NftListing): Mono { + return nftRepository.findByNftJoinMetadata(nft.nftId) + .flatMap { data -> + reactiveRedisTemplate.opsForValue().set("NFT:${nft.nftId}", data).then() + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/service/api/NftListingService.kt b/src/main/kotlin/com/api/nft/service/api/NftListingService.kt index f3409e4..98e44c5 100644 --- a/src/main/kotlin/com/api/nft/service/api/NftListingService.kt +++ b/src/main/kotlin/com/api/nft/service/api/NftListingService.kt @@ -2,6 +2,8 @@ package com.api.nft.service.api import com.api.nft.domain.nft.NftListing import com.api.nft.domain.nft.repository.NftListingRepository +import com.api.nft.domain.nft.repository.NftRepository +import com.api.nft.service.RedisService import com.api.nft.service.dto.ListingResponse import com.api.nft.storage.PriceStorage import org.springframework.stereotype.Service @@ -13,6 +15,7 @@ import java.math.BigDecimal class NftListingService( private val nftListingRepository: NftListingRepository, private val priceStorage: PriceStorage, + private val redisService: RedisService, ) { fun update(newListing: ListingResponse): Mono { @@ -25,6 +28,9 @@ class NftListingService( .thenReturn(nftListing.update(newListing.price, newListing.tokenType)) } else { Mono.just(nftListing) + }.flatMap { updatedListing -> + redisService.updateToRedis(updatedListing) + .thenReturn(updatedListing) } }.switchIfEmpty { save(newListing) } } diff --git a/src/main/kotlin/com/api/nft/service/api/NftService.kt b/src/main/kotlin/com/api/nft/service/api/NftService.kt index 0adac6e..f230fff 100644 --- a/src/main/kotlin/com/api/nft/service/api/NftService.kt +++ b/src/main/kotlin/com/api/nft/service/api/NftService.kt @@ -1,16 +1,16 @@ package com.api.nft.service.api +import com.api.nft.controller.dto.NftMetadataResponse import com.api.nft.domain.nft.Nft -import com.api.nft.domain.nft.repository.NftMetadataDto import com.api.nft.domain.nft.repository.NftRepository import com.api.nft.enums.ChainType import com.api.nft.enums.ContractType import com.api.nft.event.dto.NftCreatedEvent import com.api.nft.event.dto.NftResponse +import com.api.nft.service.RedisService import com.api.nft.service.external.dto.NftAttribute import com.api.nft.service.external.dto.NftData import com.api.nft.service.external.dto.NftMetadata -import com.api.nft.service.external.infura.InfuraApiService import com.api.nft.service.external.moralis.MoralisApiService import org.springframework.context.ApplicationEventPublisher import org.springframework.stereotype.Service @@ -25,11 +25,11 @@ class NftService( private val attributeService: AttributeService, private val eventPublisher: ApplicationEventPublisher, private val transferService: TransferService, - private val moralisApiService: MoralisApiService + private val moralisApiService: MoralisApiService, + private val redisService: RedisService, ) { - - fun findAllById(ids: List): Flux { + fun findAllById(ids: List): Flux { return nftRepository.findAllByNftJoinMetadata(ids) } @@ -62,7 +62,10 @@ class NftService( )) .then(Mono.just(nft)) .doOnSuccess { eventPublisher.publishEvent(NftCreatedEvent(this, nft.toResponse())) } - .doOnSuccess { } + .flatMap { createdNft -> + redisService.saveNftToRedis(createdNft) + .thenReturn(createdNft) + } .flatMap { transferService.createTransfer(nft).thenReturn(nft) } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 9b16b03..8f045a5 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -20,16 +20,20 @@ spring: data: redis: cluster: - nodes: 172.24.0.2:6379,172.24.0.3:6379,172.24.0.4:6379 + nodes: 172.24.0.2:6379,172.24.0.3:6379,172.24.0.4:6379,172.24.0.5:6379,172.24.0.6:6379,172.24.0.7:6379,172.24.0.8:6379,172.24.0.9:6379,172.24.0.10:6379 lettuce: pool: max-active: 8 max-idle: 8 min-idle: 0 + + + timeout: 6000 logging: level: org.springframework.r2dbc: debug + # io.r2dbc.postgresql: DEBUG # root: info server: diff --git a/src/test/kotlin/com/api/nft/NftTest.kt b/src/test/kotlin/com/api/nft/NftTest.kt index 7a6743d..1dc9014 100644 --- a/src/test/kotlin/com/api/nft/NftTest.kt +++ b/src/test/kotlin/com/api/nft/NftTest.kt @@ -12,6 +12,9 @@ import com.api.nft.service.external.moralis.MoralisApiService import com.api.nft.service.api.NftService import com.api.nft.service.api.TransferService import com.api.nft.event.dto.NftResponse +import com.api.nft.service.RedisService +import com.api.nft.service.api.NftListingService +import com.api.nft.service.dto.ListingResponse import com.api.nft.service.external.binance.BinanceApiService import com.api.nft.service.external.infura.InfuraApiService import org.junit.jupiter.api.Test @@ -19,6 +22,7 @@ import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.context.SpringBootTest import org.springframework.context.ApplicationEventPublisher import reactor.test.StepVerifier +import java.math.BigDecimal @SpringBootTest class NftTest( @@ -31,6 +35,8 @@ class NftTest( @Autowired private val infuraApiService: InfuraApiService, @Autowired private val eventPublisher: ApplicationEventPublisher, @Autowired private val binanceApiService: BinanceApiService, + @Autowired private val redisService: RedisService, + @Autowired private val nftListingService: NftListingService, ) { @@ -101,8 +107,40 @@ class NftTest( fun trasfermoralist() { val res =moralisApiService.getNftTransfer("0xa3784fe9104fdc0b988769fba7459ece2fb36eea","0",ChainType.POLYGON_MAINNET).block() println(res.toString()) + } + + @Test + fun testNftData() { + val res =nftRepository.findByNftJoinMetadata(3).block() + println(res.toString()) + } + + + @Test + fun nftListing() { + val listing = ListingResponse( + id = 1, + nftId = 3L, + address = "0x01b72b4aa3f66f213d62d53e829bc172a6a72867", + createdDateTime = 1714662809000, + endDateTime = 1714662809000, + price = BigDecimal(0.23), + tokenType = TokenType.ETH + + ) + nftListingService.update(listing).block() + } + @Test + fun redisTest() { + val nft = nftRepository.findById(1).block() + redisService.saveNftToRedis(nft!!).block() + + } + @Test + fun asdasd() { + nftService.getByWalletNft("0x01b72b4aa3f66f213d62d53e829bc172a6a72867",ChainType.POLYGON_MAINNET).blockLast() } } \ No newline at end of file From 4ee28072278262ba43ff203029d604a05d87ee9a Mon Sep 17 00:00:00 2001 From: min-96 Date: Mon, 17 Jun 2024 03:11:09 +0900 Subject: [PATCH 21/38] feat: set redis cluster --- docker-compose.yaml | 2 ++ .../kotlin/com/api/nft/config/RedisConfig.kt | 20 +++++++++---------- src/main/resources/application.yml | 3 ++- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/docker-compose.yaml b/docker-compose.yaml index 0eb967f..c178665 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -26,6 +26,8 @@ services: redis-1: image: redis:latest command: redis-server --appendonly yes --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 --port 6379 + ports: + - "6379:6379" networks: redis-cluster: ipv4_address: 172.24.0.2 diff --git a/src/main/kotlin/com/api/nft/config/RedisConfig.kt b/src/main/kotlin/com/api/nft/config/RedisConfig.kt index d1ac14c..e5842b9 100644 --- a/src/main/kotlin/com/api/nft/config/RedisConfig.kt +++ b/src/main/kotlin/com/api/nft/config/RedisConfig.kt @@ -17,15 +17,15 @@ class RedisConfig { @Bean fun redisClusterConfiguration(): RedisClusterConfiguration { val clusterNodes = listOf( - "172.24.0.2:6379", - "172.24.0.3:6379", - "172.24.0.4:6379", - "172.24.0.5:6379", - "172.24.0.6:6379", - "172.24.0.7:6379", - "172.24.0.8:6379", - "172.24.0.9:6379", - "172.24.0.10:6379" + "localhost:6379", + "localhost:6380", + "localhost:6381", + "localhost:6382", + "localhost:6383", + "localhost:6384", + "localhost:6385", + "localhost:6386", + "localhost:6387" ) return RedisClusterConfiguration(clusterNodes) } @@ -33,7 +33,7 @@ class RedisConfig { @Bean fun lettuceConnectionFactory(redisClusterConfiguration: RedisClusterConfiguration): LettuceConnectionFactory { val clientConfig = LettuceClientConfiguration.builder() - .commandTimeout(Duration.ofSeconds(10)) // 타임아웃 설정 + .commandTimeout(Duration.ofSeconds(10)) .build() return LettuceConnectionFactory(redisClusterConfiguration, clientConfig) } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 8f045a5..c3b9946 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -20,7 +20,8 @@ spring: data: redis: cluster: - nodes: 172.24.0.2:6379,172.24.0.3:6379,172.24.0.4:6379,172.24.0.5:6379,172.24.0.6:6379,172.24.0.7:6379,172.24.0.8:6379,172.24.0.9:6379,172.24.0.10:6379 + nodes: localhost:6379,localhost:6380,localhost:6381, localhost:6382,localhost:6383,localhost:6384,localhost:6385,localhost:6386,localhost:6387 + # nodes: ,localhost:6380,localhost:6381,localhost:6382,localhost:6383,localhost:6384,localhost:6385,localhost:6386,localhost:6387 lettuce: pool: max-active: 8 From caff6940406c4d4451a77717d35011e14eaf2bd0 Mon Sep 17 00:00:00 2001 From: min-96 Date: Mon, 17 Jun 2024 13:50:07 +0900 Subject: [PATCH 22/38] refactor: NftByWallet --- docker-compose.yaml | 20 +++++++++---------- redis.conf | 3 +++ .../kotlin/com/api/nft/config/RedisConfig.kt | 18 ++++++++--------- .../com/api/nft/controller/NftController.kt | 7 +++++++ .../com/api/nft/service/RedisService.kt | 5 ++++- src/main/resources/application.yml | 5 +---- src/main/resources/http/test.http | 2 ++ src/test/kotlin/com/api/nft/NftTest.kt | 5 +++++ 8 files changed, 41 insertions(+), 24 deletions(-) create mode 100644 src/main/resources/http/test.http diff --git a/docker-compose.yaml b/docker-compose.yaml index c178665..e00b6bc 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -30,7 +30,7 @@ services: - "6379:6379" networks: redis-cluster: - ipv4_address: 172.24.0.2 + ipv4_address: 172.26.0.2 volumes: - redis1_data:/data - ./redis.conf:/usr/local/etc/redis/redis.conf @@ -41,7 +41,7 @@ services: - "6380:6379" networks: redis-cluster: - ipv4_address: 172.24.0.3 + ipv4_address: 172.26.0.3 volumes: - redis2_data:/data - ./redis.conf:/usr/local/etc/redis/redis.conf @@ -53,7 +53,7 @@ services: - "6381:6379" networks: redis-cluster: - ipv4_address: 172.24.0.4 + ipv4_address: 172.26.0.4 volumes: - redis3_data:/data - ./redis.conf:/usr/local/etc/redis/redis.conf @@ -65,7 +65,7 @@ services: - "6382:6379" networks: redis-cluster: - ipv4_address: 172.24.0.5 + ipv4_address: 172.26.0.5 volumes: - redis4_data:/data - ./redis.conf:/usr/local/etc/redis/redis.conf @@ -77,7 +77,7 @@ services: - "6383:6379" networks: redis-cluster: - ipv4_address: 172.24.0.6 + ipv4_address: 172.26.0.6 volumes: - redis5_data:/data - ./redis.conf:/usr/local/etc/redis/redis.conf @@ -89,7 +89,7 @@ services: - "6384:6379" networks: redis-cluster: - ipv4_address: 172.24.0.7 + ipv4_address: 172.26.0.7 volumes: - redis6_data:/data - ./redis.conf:/usr/local/etc/redis/redis.conf @@ -101,7 +101,7 @@ services: - "6385:6379" networks: redis-cluster: - ipv4_address: 172.24.0.8 + ipv4_address: 172.26.0.8 volumes: - redis7_data:/data - ./redis.conf:/usr/local/etc/redis/redis.conf @@ -113,7 +113,7 @@ services: - "6386:6379" networks: redis-cluster: - ipv4_address: 172.24.0.9 + ipv4_address: 172.26.0.9 volumes: - redis8_data:/data - ./redis.conf:/usr/local/etc/redis/redis.conf @@ -125,7 +125,7 @@ services: - "6387:6379" networks: redis-cluster: - ipv4_address: 172.24.0.10 + ipv4_address: 172.26.0.10 volumes: - redis9_data:/data - ./redis.conf:/usr/local/etc/redis/redis.conf @@ -136,7 +136,7 @@ networks: driver: bridge ipam: config: - - subnet: 172.24.0.0/16 + - subnet: 172.26.0.0/16 volumes: diff --git a/redis.conf b/redis.conf index 12a8651..461fa42 100644 --- a/redis.conf +++ b/redis.conf @@ -6,3 +6,6 @@ cluster-config-file nodes.conf cluster-node-timeout 5000 appendonly yes appendfilename "appendonly.aof" +cluster-announce-ip 10.10.148.169 +cluster-announce-port 6379 +cluster-announce-bus-port 16379 diff --git a/src/main/kotlin/com/api/nft/config/RedisConfig.kt b/src/main/kotlin/com/api/nft/config/RedisConfig.kt index e5842b9..1972c13 100644 --- a/src/main/kotlin/com/api/nft/config/RedisConfig.kt +++ b/src/main/kotlin/com/api/nft/config/RedisConfig.kt @@ -17,15 +17,15 @@ class RedisConfig { @Bean fun redisClusterConfiguration(): RedisClusterConfiguration { val clusterNodes = listOf( - "localhost:6379", - "localhost:6380", - "localhost:6381", - "localhost:6382", - "localhost:6383", - "localhost:6384", - "localhost:6385", - "localhost:6386", - "localhost:6387" + "10.10.148.169:6379", + // "10.10.148.169:6380", + // "10.10.148.169:6381", + // "10.10.148.169:6382", + // "10.10.148.169:6383", + // "10.10.148.169:6384", + // "10.10.148.169:6385", + // "10.10.148.169:6386", + // "10.10.148.169:6387" ) return RedisClusterConfiguration(clusterNodes) } diff --git a/src/main/kotlin/com/api/nft/controller/NftController.kt b/src/main/kotlin/com/api/nft/controller/NftController.kt index 6740fef..b1b3046 100644 --- a/src/main/kotlin/com/api/nft/controller/NftController.kt +++ b/src/main/kotlin/com/api/nft/controller/NftController.kt @@ -4,6 +4,7 @@ import com.api.nft.controller.dto.NftMetadataResponse import com.api.nft.domain.trasfer.Transfer import com.api.nft.enums.ChainType import com.api.nft.event.dto.NftResponse +import com.api.nft.service.RedisService import com.api.nft.service.api.NftService import com.api.nft.service.api.TransferService import com.api.nft.service.external.dto.NftRequest @@ -25,6 +26,7 @@ import reactor.core.publisher.Mono class NftController( private val nftService: NftService, private val transferService: TransferService, + private val redisService: RedisService, ) { @GetMapping @@ -51,5 +53,10 @@ class NftController( } + @GetMapping("/redis") + fun saveRedis() : Mono{ + return redisService.saveData("sss") + } + } \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/service/RedisService.kt b/src/main/kotlin/com/api/nft/service/RedisService.kt index 72dcfc7..a6f595a 100644 --- a/src/main/kotlin/com/api/nft/service/RedisService.kt +++ b/src/main/kotlin/com/api/nft/service/RedisService.kt @@ -21,11 +21,14 @@ class RedisService( return nftRepository.findByNftJoinMetadata(nft.id!!) .doOnNext { data -> logger.info("Data retrieved: $data") } // Log data to verify it is fetched .flatMap { data -> - println("asdasdasda") reactiveRedisTemplate.opsForValue().set("NFT:${nft.id}", data).then() } .doOnError { error -> logger.error("Error occurred: ${error.message}", error) } // Log any errors } + + fun saveData(key : String) : Mono { + return reactiveRedisTemplate.opsForValue().set("NFT:${key}","orange").then() + } fun updateToRedis(nft: NftListing): Mono { return nftRepository.findByNftJoinMetadata(nft.nftId) .flatMap { data -> diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index c3b9946..1a0ebef 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -20,16 +20,13 @@ spring: data: redis: cluster: - nodes: localhost:6379,localhost:6380,localhost:6381, localhost:6382,localhost:6383,localhost:6384,localhost:6385,localhost:6386,localhost:6387 + nodes: 10.10.148.169:6379 # nodes: ,localhost:6380,localhost:6381,localhost:6382,localhost:6383,localhost:6384,localhost:6385,localhost:6386,localhost:6387 lettuce: pool: max-active: 8 max-idle: 8 min-idle: 0 - - - timeout: 6000 logging: level: diff --git a/src/main/resources/http/test.http b/src/main/resources/http/test.http new file mode 100644 index 0000000..a00bafa --- /dev/null +++ b/src/main/resources/http/test.http @@ -0,0 +1,2 @@ +### +GET http://localhost:8082/v1/nft/redis \ No newline at end of file diff --git a/src/test/kotlin/com/api/nft/NftTest.kt b/src/test/kotlin/com/api/nft/NftTest.kt index 1dc9014..fa86dcd 100644 --- a/src/test/kotlin/com/api/nft/NftTest.kt +++ b/src/test/kotlin/com/api/nft/NftTest.kt @@ -143,4 +143,9 @@ class NftTest( nftService.getByWalletNft("0x01b72b4aa3f66f213d62d53e829bc172a6a72867",ChainType.POLYGON_MAINNET).blockLast() } + @Test + fun test() { + redisService.saveData("heelo").block() + } + } \ No newline at end of file From 12bc97d0b123a5a06d05ef3451c691a333044fc7 Mon Sep 17 00:00:00 2001 From: Donghyeon Im Date: Tue, 18 Jun 2024 06:58:59 +0900 Subject: [PATCH 23/38] feat: set redis cluster by dongdorrong --- build.gradle.kts | 1 + docker-compose.yaml | 196 ++++++++---------- .../kotlin/com/api/nft/config/RedisConfig.kt | 26 ++- src/main/resources/application.yml | 19 +- 4 files changed, 122 insertions(+), 120 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index bf49977..ddfadbc 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -45,6 +45,7 @@ dependencies { testImplementation("org.springframework.boot:spring-boot-starter-test") testImplementation("io.projectreactor:reactor-test") + implementation("io.netty:netty-resolver-dns-native-macos:4.1.68.Final:osx-aarch_64") } diff --git a/docker-compose.yaml b/docker-compose.yaml index e00b6bc..bf3fea9 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -23,129 +23,111 @@ services: volumes: - ./rabbitmq_data:/var/lib/rabbitmq - redis-1: - image: redis:latest - command: redis-server --appendonly yes --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 --port 6379 + redis-node-0: + image: docker.io/bitnami/redis-cluster:7.2 ports: - - "6379:6379" - networks: - redis-cluster: - ipv4_address: 172.26.0.2 + - 6379:6379 volumes: - - redis1_data:/data - - ./redis.conf:/usr/local/etc/redis/redis.conf - redis-2: - image: redis:latest - command: redis-server --appendonly yes --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 --port 6379 - ports: - - "6380:6379" - networks: - redis-cluster: - ipv4_address: 172.26.0.3 - volumes: - - redis2_data:/data - - ./redis.conf:/usr/local/etc/redis/redis.conf - - redis-3: - image: redis:latest - command: redis-server --appendonly yes --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 --port 6379 - ports: - - "6381:6379" - networks: - redis-cluster: - ipv4_address: 172.26.0.4 - volumes: - - redis3_data:/data - - ./redis.conf:/usr/local/etc/redis/redis.conf - - redis-4: - image: redis:latest - command: redis-server --appendonly yes --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 --port 6379 - ports: - - "6382:6379" - networks: - redis-cluster: - ipv4_address: 172.26.0.5 - volumes: - - redis4_data:/data - - ./redis.conf:/usr/local/etc/redis/redis.conf + - redis-cluster_data-0:/bitnami/redis/data + environment: + - 'REDIS_PASSWORD=bitnami' + - 'REDIS_NODES=redis-node-0:6379 redis-node-1:6380 redis-node-2:6381 redis-node-3:6382 redis-node-4:6383 redis-node-5:6384' + - 'REDIS_CLUSTER_ANNOUNCE_HOSTNAME=localhost' + - 'REDIS_CLUSTER_PREFERRED_ENDPOINT_TYPE=hostname' - redis-5: - image: redis:latest - command: redis-server --appendonly yes --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 --port 6379 + redis-node-1: + image: docker.io/bitnami/redis-cluster:7.2 ports: - - "6383:6379" - networks: - redis-cluster: - ipv4_address: 172.26.0.6 + - 6380:6380 volumes: - - redis5_data:/data - - ./redis.conf:/usr/local/etc/redis/redis.conf + - redis-cluster_data-1:/bitnami/redis/data + environment: + - 'REDIS_PASSWORD=bitnami' + - 'REDIS_NODES=redis-node-0:6379 redis-node-1:6380 redis-node-2:6381 redis-node-3:6382 redis-node-4:6383 redis-node-5:6384' + - 'REDIS_PORT_NUMBER=6380' + - 'REDIS_CLUSTER_ANNOUNCE_HOSTNAME=localhost' + - 'REDIS_CLUSTER_ANNOUNCE_PORT=6380' + - 'REDIS_CLUSTER_PREFERRED_ENDPOINT_TYPE=hostname' - redis-6: - image: redis:latest - command: redis-server --appendonly yes --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 --port 6379 + redis-node-2: + image: docker.io/bitnami/redis-cluster:7.2 ports: - - "6384:6379" - networks: - redis-cluster: - ipv4_address: 172.26.0.7 + - 6381:6381 volumes: - - redis6_data:/data - - ./redis.conf:/usr/local/etc/redis/redis.conf + - redis-cluster_data-2:/bitnami/redis/data + environment: + - 'REDIS_PASSWORD=bitnami' + - 'REDIS_NODES=redis-node-0:6379 redis-node-1:6380 redis-node-2:6381 redis-node-3:6382 redis-node-4:6383 redis-node-5:6384' + - 'REDIS_PORT_NUMBER=6381' + - 'REDIS_CLUSTER_ANNOUNCE_HOSTNAME=localhost' + - 'REDIS_CLUSTER_ANNOUNCE_PORT=6381' + - 'REDIS_CLUSTER_PREFERRED_ENDPOINT_TYPE=hostname' - redis-7: - image: redis:latest - command: redis-server --appendonly yes --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 --port 6379 + redis-node-3: + image: docker.io/bitnami/redis-cluster:7.2 ports: - - "6385:6379" - networks: - redis-cluster: - ipv4_address: 172.26.0.8 + - 6382:6382 volumes: - - redis7_data:/data - - ./redis.conf:/usr/local/etc/redis/redis.conf + - redis-cluster_data-3:/bitnami/redis/data + environment: + - 'REDIS_PASSWORD=bitnami' + - 'REDIS_NODES=redis-node-0:6379 redis-node-1:6380 redis-node-2:6381 redis-node-3:6382 redis-node-4:6383 redis-node-5:6384' + - 'REDIS_PORT_NUMBER=6382' + - 'REDIS_CLUSTER_ANNOUNCE_HOSTNAME=localhost' + - 'REDIS_CLUSTER_ANNOUNCE_PORT=6382' + - 'REDIS_CLUSTER_PREFERRED_ENDPOINT_TYPE=hostname' - redis-8: - image: redis:latest - command: redis-server --appendonly yes --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 --port 6379 + redis-node-4: + image: docker.io/bitnami/redis-cluster:7.2 ports: - - "6386:6379" - networks: - redis-cluster: - ipv4_address: 172.26.0.9 + - 6383:6383 volumes: - - redis8_data:/data - - ./redis.conf:/usr/local/etc/redis/redis.conf + - redis-cluster_data-4:/bitnami/redis/data + environment: + - 'REDIS_PASSWORD=bitnami' + - 'REDIS_NODES=redis-node-0:6379 redis-node-1:6380 redis-node-2:6381 redis-node-3:6382 redis-node-4:6383 redis-node-5:6384' + - 'REDIS_PORT_NUMBER=6383' + - 'REDIS_CLUSTER_ANNOUNCE_HOSTNAME=localhost' + - 'REDIS_CLUSTER_ANNOUNCE_PORT=6383' + - 'REDIS_CLUSTER_PREFERRED_ENDPOINT_TYPE=hostname' - redis-9: - image: redis:latest - command: redis-server --appendonly yes --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 --port 6379 + redis-node-5: + image: docker.io/bitnami/redis-cluster:7.2 ports: - - "6387:6379" - networks: - redis-cluster: - ipv4_address: 172.26.0.10 + - 6384:6384 volumes: - - redis9_data:/data - - ./redis.conf:/usr/local/etc/redis/redis.conf - - -networks: - redis-cluster: - driver: bridge - ipam: - config: - - subnet: 172.26.0.0/16 - + - redis-cluster_data-5:/bitnami/redis/data + depends_on: + - redis-node-0 + - redis-node-1 + - redis-node-2 + - redis-node-3 + - redis-node-4 + environment: + - 'REDISCLI_AUTH=bitnami' + - 'REDIS_PASSWORD=bitnami' + - 'REDIS_NODES=redis-node-0:6379 redis-node-1:6380 redis-node-2:6381 redis-node-3:6382 redis-node-4:6383 redis-node-5:6384' + - 'REDIS_PORT_NUMBER=6384' + - 'REDIS_CLUSTER_ANNOUNCE_HOSTNAME=localhost' + - 'REDIS_CLUSTER_ANNOUNCE_PORT=6384' + - 'REDIS_CLUSTER_PREFERRED_ENDPOINT_TYPE=hostname' + - 'REDIS_CLUSTER_REPLICAS=1' + - 'REDIS_CLUSTER_CREATOR=yes' volumes: - redis1_data: - redis2_data: - redis3_data: - redis4_data: - redis5_data: - redis6_data: - redis7_data: - redis8_data: - redis9_data: + redis-cluster_data-0: + driver: local + redis-cluster_data-1: + driver: local + redis-cluster_data-2: + driver: local + redis-cluster_data-3: + driver: local + redis-cluster_data-4: + driver: local + redis-cluster_data-5: + driver: local + +networks: + default: + name: local_network \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/config/RedisConfig.kt b/src/main/kotlin/com/api/nft/config/RedisConfig.kt index 1972c13..afe40c9 100644 --- a/src/main/kotlin/com/api/nft/config/RedisConfig.kt +++ b/src/main/kotlin/com/api/nft/config/RedisConfig.kt @@ -5,27 +5,27 @@ import org.springframework.context.annotation.Configuration import org.springframework.data.redis.connection.RedisClusterConfiguration import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory +import org.springframework.data.redis.connection.RedisPassword; +import io.lettuce.core.cluster.ClusterClientOptions import org.springframework.data.redis.core.ReactiveRedisTemplate import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer import org.springframework.data.redis.serializer.RedisSerializationContext import org.springframework.data.redis.serializer.StringRedisSerializer import java.time.Duration + @Configuration class RedisConfig { @Bean fun redisClusterConfiguration(): RedisClusterConfiguration { val clusterNodes = listOf( - "10.10.148.169:6379", - // "10.10.148.169:6380", - // "10.10.148.169:6381", - // "10.10.148.169:6382", - // "10.10.148.169:6383", - // "10.10.148.169:6384", - // "10.10.148.169:6385", - // "10.10.148.169:6386", - // "10.10.148.169:6387" + "localhost:6379", + "localhost:6380", + "localhost:6381", + "localhost:6382", + "localhost:6383", + "localhost:6384" ) return RedisClusterConfiguration(clusterNodes) } @@ -34,7 +34,15 @@ class RedisConfig { fun lettuceConnectionFactory(redisClusterConfiguration: RedisClusterConfiguration): LettuceConnectionFactory { val clientConfig = LettuceClientConfiguration.builder() .commandTimeout(Duration.ofSeconds(10)) + .clientOptions( + ClusterClientOptions.builder() + .autoReconnect(true) + .pingBeforeActivateConnection(true) + .build() + ) .build() + redisClusterConfiguration.setPassword(RedisPassword.of("bitnami")) + redisClusterConfiguration.setMaxRedirects(3) return LettuceConnectionFactory(redisClusterConfiguration, clientConfig) } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 1a0ebef..3a3dcb4 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -20,20 +20,31 @@ spring: data: redis: cluster: - nodes: 10.10.148.169:6379 - # nodes: ,localhost:6380,localhost:6381,localhost:6382,localhost:6383,localhost:6384,localhost:6385,localhost:6386,localhost:6387 + # dongdorrong + nodes: localhost:6371,localhost:6372,localhost:6373,localhost:6374,localhost:6375 + # # minyoung + # nodes: localhost:6379,localhost:6380,localhost:6381,localhost:6382,localhost:6383,localhost:6384,localhost:6385,localhost:6386,localhost:6387 + max-redirects: 3 + password: bitnami + timeout: 500ms + connect-ip: localhost lettuce: pool: max-active: 8 max-idle: 8 min-idle: 0 + session: + redis: + flush-mode: on_save logging: level: org.springframework.r2dbc: debug + org.springframework.data.redis: DEBUG + redis.clients.jedis: DEBUG + io.lettuce.core: DEBUG + root: INFO -# io.r2dbc.postgresql: DEBUG -# root: info server: port: 8082 From d453d2212c2705749e42c01998e4942c78f03171 Mon Sep 17 00:00:00 2001 From: min-96 Date: Tue, 18 Jun 2024 19:55:51 +0900 Subject: [PATCH 24/38] refactor: add redis-node --- docker-compose.yaml | 86 +++++++++++++++++-- .../kotlin/com/api/nft/config/RedisConfig.kt | 10 ++- .../com/api/nft/controller/NftController.kt | 5 -- .../nft/controller/dto/NftMetadataResponse.kt | 2 +- .../repository/NftRepositorySupportImpl.kt | 8 +- .../com/api/nft/service/RedisService.kt | 22 ++--- .../api/nft/service/api/NftListingService.kt | 2 +- .../com/api/nft/service/api/NftService.kt | 44 ++++++---- src/main/resources/application.yml | 5 +- src/test/kotlin/com/api/nft/NftTest.kt | 30 +++++-- 10 files changed, 145 insertions(+), 69 deletions(-) diff --git a/docker-compose.yaml b/docker-compose.yaml index bf3fea9..7d8e360 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -31,7 +31,7 @@ services: - redis-cluster_data-0:/bitnami/redis/data environment: - 'REDIS_PASSWORD=bitnami' - - 'REDIS_NODES=redis-node-0:6379 redis-node-1:6380 redis-node-2:6381 redis-node-3:6382 redis-node-4:6383 redis-node-5:6384' + - 'REDIS_NODES=redis-node-0:6379 redis-node-1:6380 redis-node-2:6381 redis-node-3:6382 redis-node-4:6383 redis-node-5:6384 redis-node-6:6385 redis-node-7:6386 redis-node-8:6387 redis-node-9:6388' - 'REDIS_CLUSTER_ANNOUNCE_HOSTNAME=localhost' - 'REDIS_CLUSTER_PREFERRED_ENDPOINT_TYPE=hostname' @@ -43,7 +43,7 @@ services: - redis-cluster_data-1:/bitnami/redis/data environment: - 'REDIS_PASSWORD=bitnami' - - 'REDIS_NODES=redis-node-0:6379 redis-node-1:6380 redis-node-2:6381 redis-node-3:6382 redis-node-4:6383 redis-node-5:6384' + - 'REDIS_NODES=redis-node-0:6379 redis-node-1:6380 redis-node-2:6381 redis-node-3:6382 redis-node-4:6383 redis-node-5:6384 redis-node-6:6385 redis-node-7:6386 redis-node-8:6387 redis-node-9:6388' - 'REDIS_PORT_NUMBER=6380' - 'REDIS_CLUSTER_ANNOUNCE_HOSTNAME=localhost' - 'REDIS_CLUSTER_ANNOUNCE_PORT=6380' @@ -57,7 +57,7 @@ services: - redis-cluster_data-2:/bitnami/redis/data environment: - 'REDIS_PASSWORD=bitnami' - - 'REDIS_NODES=redis-node-0:6379 redis-node-1:6380 redis-node-2:6381 redis-node-3:6382 redis-node-4:6383 redis-node-5:6384' + - 'REDIS_NODES=redis-node-0:6379 redis-node-1:6380 redis-node-2:6381 redis-node-3:6382 redis-node-4:6383 redis-node-5:6384 redis-node-6:6385 redis-node-7:6386 redis-node-8:6387 redis-node-9:6388' - 'REDIS_PORT_NUMBER=6381' - 'REDIS_CLUSTER_ANNOUNCE_HOSTNAME=localhost' - 'REDIS_CLUSTER_ANNOUNCE_PORT=6381' @@ -71,7 +71,7 @@ services: - redis-cluster_data-3:/bitnami/redis/data environment: - 'REDIS_PASSWORD=bitnami' - - 'REDIS_NODES=redis-node-0:6379 redis-node-1:6380 redis-node-2:6381 redis-node-3:6382 redis-node-4:6383 redis-node-5:6384' + - 'REDIS_NODES=redis-node-0:6379 redis-node-1:6380 redis-node-2:6381 redis-node-3:6382 redis-node-4:6383 redis-node-5:6384 redis-node-6:6385 redis-node-7:6386 redis-node-8:6387 redis-node-9:6388' - 'REDIS_PORT_NUMBER=6382' - 'REDIS_CLUSTER_ANNOUNCE_HOSTNAME=localhost' - 'REDIS_CLUSTER_ANNOUNCE_PORT=6382' @@ -85,7 +85,7 @@ services: - redis-cluster_data-4:/bitnami/redis/data environment: - 'REDIS_PASSWORD=bitnami' - - 'REDIS_NODES=redis-node-0:6379 redis-node-1:6380 redis-node-2:6381 redis-node-3:6382 redis-node-4:6383 redis-node-5:6384' + - 'REDIS_NODES=redis-node-0:6379 redis-node-1:6380 redis-node-2:6381 redis-node-3:6382 redis-node-4:6383 redis-node-5:6384 redis-node-6:6385 redis-node-7:6386 redis-node-8:6387 redis-node-9:6388' - 'REDIS_PORT_NUMBER=6383' - 'REDIS_CLUSTER_ANNOUNCE_HOSTNAME=localhost' - 'REDIS_CLUSTER_ANNOUNCE_PORT=6383' @@ -97,21 +97,81 @@ services: - 6384:6384 volumes: - redis-cluster_data-5:/bitnami/redis/data + environment: + - 'REDIS_PASSWORD=bitnami' + - 'REDIS_NODES=redis-node-0:6379 redis-node-1:6380 redis-node-2:6381 redis-node-3:6382 redis-node-4:6383 redis-node-5:6384 redis-node-6:6385 redis-node-7:6386 redis-node-8:6387 redis-node-9:6388' + - 'REDIS_PORT_NUMBER=6384' + - 'REDIS_CLUSTER_ANNOUNCE_HOSTNAME=localhost' + - 'REDIS_CLUSTER_ANNOUNCE_PORT=6384' + - 'REDIS_CLUSTER_PREFERRED_ENDPOINT_TYPE=hostname' + + redis-node-6: + image: docker.io/bitnami/redis-cluster:7.2 + ports: + - 6385:6385 + volumes: + - redis-cluster_data-6:/bitnami/redis/data + environment: + - 'REDIS_PASSWORD=bitnami' + - 'REDIS_NODES=redis-node-0:6379 redis-node-1:6380 redis-node-2:6381 redis-node-3:6382 redis-node-4:6383 redis-node-5:6384 redis-node-6:6385 redis-node-7:6386 redis-node-8:6387 redis-node-9:6388' + - 'REDIS_PORT_NUMBER=6385' + - 'REDIS_CLUSTER_ANNOUNCE_HOSTNAME=localhost' + - 'REDIS_CLUSTER_ANNOUNCE_PORT=6385' + - 'REDIS_CLUSTER_PREFERRED_ENDPOINT_TYPE=hostname' + + redis-node-7: + image: docker.io/bitnami/redis-cluster:7.2 + ports: + - 6386:6386 + volumes: + - redis-cluster_data-7:/bitnami/redis/data + environment: + - 'REDIS_PASSWORD=bitnami' + - 'REDIS_NODES=redis-node-0:6379 redis-node-1:6380 redis-node-2:6381 redis-node-3:6382 redis-node-4:6383 redis-node-5:6384 redis-node-6:6385 redis-node-7:6386 redis-node-8:6387 redis-node-9:6388' + - 'REDIS_PORT_NUMBER=6386' + - 'REDIS_CLUSTER_ANNOUNCE_HOSTNAME=localhost' + - 'REDIS_CLUSTER_ANNOUNCE_PORT=6386' + - 'REDIS_CLUSTER_PREFERRED_ENDPOINT_TYPE=hostname' + + redis-node-8: + image: docker.io/bitnami/redis-cluster:7.2 + ports: + - 6387:6387 + volumes: + - redis-cluster_data-8:/bitnami/redis/data + environment: + - 'REDIS_PASSWORD=bitnami' + - 'REDIS_NODES=redis-node-0:6379 redis-node-1:6380 redis-node-2:6381 redis-node-3:6382 redis-node-4:6383 redis-node-5:6384 redis-node-6:6385 redis-node-7:6386 redis-node-8:6387 redis-node-9:6388' + - 'REDIS_PORT_NUMBER=6387' + - 'REDIS_CLUSTER_ANNOUNCE_HOSTNAME=localhost' + - 'REDIS_CLUSTER_ANNOUNCE_PORT=6387' + - 'REDIS_CLUSTER_PREFERRED_ENDPOINT_TYPE=hostname' + + redis-node-9: + image: docker.io/bitnami/redis-cluster:7.2 + ports: + - 6388:6388 + volumes: + - redis-cluster_data-9:/bitnami/redis/data depends_on: - redis-node-0 - redis-node-1 - redis-node-2 - redis-node-3 - redis-node-4 + - redis-node-5 + - redis-node-6 + - redis-node-7 + - redis-node-8 environment: - 'REDISCLI_AUTH=bitnami' - 'REDIS_PASSWORD=bitnami' - - 'REDIS_NODES=redis-node-0:6379 redis-node-1:6380 redis-node-2:6381 redis-node-3:6382 redis-node-4:6383 redis-node-5:6384' - - 'REDIS_PORT_NUMBER=6384' + - 'REDIS_NODES=redis-node-0:6379 redis-node-1:6380 redis-node-2:6381 redis-node-3:6382 redis-node-4:6383 redis-node-5:6384 redis-node-6:6385 redis-node-7:6386 redis-node-8:6387 redis-node-9:6388' + - 'REDIS_PORT_NUMBER=6388' - 'REDIS_CLUSTER_ANNOUNCE_HOSTNAME=localhost' - - 'REDIS_CLUSTER_ANNOUNCE_PORT=6384' + - 'REDIS_CLUSTER_ANNOUNCE_PORT=6388' - 'REDIS_CLUSTER_PREFERRED_ENDPOINT_TYPE=hostname' - - 'REDIS_CLUSTER_REPLICAS=1' + - 'REDIS_CLUSTER_REPLICAS=2' - 'REDIS_CLUSTER_CREATOR=yes' volumes: @@ -127,6 +187,14 @@ volumes: driver: local redis-cluster_data-5: driver: local + redis-cluster_data-6: + driver: local + redis-cluster_data-7: + driver: local + redis-cluster_data-8: + driver: local + redis-cluster_data-9: + driver: local networks: default: diff --git a/src/main/kotlin/com/api/nft/config/RedisConfig.kt b/src/main/kotlin/com/api/nft/config/RedisConfig.kt index afe40c9..67f1409 100644 --- a/src/main/kotlin/com/api/nft/config/RedisConfig.kt +++ b/src/main/kotlin/com/api/nft/config/RedisConfig.kt @@ -25,7 +25,11 @@ class RedisConfig { "localhost:6381", "localhost:6382", "localhost:6383", - "localhost:6384" + "localhost:6384", + "localhost:6385", + "localhost:6386", + "localhost:6387", + "localhost:6388", ) return RedisClusterConfiguration(clusterNodes) } @@ -41,8 +45,8 @@ class RedisConfig { .build() ) .build() - redisClusterConfiguration.setPassword(RedisPassword.of("bitnami")) - redisClusterConfiguration.setMaxRedirects(3) + redisClusterConfiguration.password = RedisPassword.of("bitnami") + redisClusterConfiguration.maxRedirects = 3 return LettuceConnectionFactory(redisClusterConfiguration, clientConfig) } diff --git a/src/main/kotlin/com/api/nft/controller/NftController.kt b/src/main/kotlin/com/api/nft/controller/NftController.kt index b1b3046..ef4e74e 100644 --- a/src/main/kotlin/com/api/nft/controller/NftController.kt +++ b/src/main/kotlin/com/api/nft/controller/NftController.kt @@ -53,10 +53,5 @@ class NftController( } - @GetMapping("/redis") - fun saveRedis() : Mono{ - return redisService.saveData("sss") - } - } \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/controller/dto/NftMetadataResponse.kt b/src/main/kotlin/com/api/nft/controller/dto/NftMetadataResponse.kt index 5963428..769506d 100644 --- a/src/main/kotlin/com/api/nft/controller/dto/NftMetadataResponse.kt +++ b/src/main/kotlin/com/api/nft/controller/dto/NftMetadataResponse.kt @@ -15,6 +15,6 @@ data class NftMetadataResponse( val nftName: String, val collectionName: String, val image: String, - val lastPrice: BigDecimal?, + val lastPrice: Double?, val tokenType: TokenType?, ) diff --git a/src/main/kotlin/com/api/nft/domain/nft/repository/NftRepositorySupportImpl.kt b/src/main/kotlin/com/api/nft/domain/nft/repository/NftRepositorySupportImpl.kt index 0f0ab9b..fd07131 100644 --- a/src/main/kotlin/com/api/nft/domain/nft/repository/NftRepositorySupportImpl.kt +++ b/src/main/kotlin/com/api/nft/domain/nft/repository/NftRepositorySupportImpl.kt @@ -28,9 +28,9 @@ class NftRepositorySupportImpl( nl.token_type AS tokenType FROM nft n - JOIN + LEFT JOIN metadata m ON n.id = m.nft_id - JOIN nft_listing nl ON n.id = nl.nft_id + LEFT JOIN nft_listing nl ON n.id = nl.nft_id WHERE n.id = :$1 """ @@ -46,7 +46,7 @@ class NftRepositorySupportImpl( collectionName = row.get("collectionName", String::class.java)!!, image = row.get("image", String::class.java) ?: "", contractType = row.get("contractType", ContractType::class.java)!!, - lastPrice = row.get("lastPrice", BigDecimal::class.java), + lastPrice = row.get("lastPrice", BigDecimal::class.java)?.toDouble(), tokenType = row.get("tokenType", TokenType::class.java) ) }.first() @@ -84,7 +84,7 @@ class NftRepositorySupportImpl( collectionName = row.get("collectionName", String::class.java)!!, image = row.get("image", String::class.java) ?: "", contractType = row.get("contractType", ContractType::class.java)!!, - lastPrice = row.get("lastPrice", BigDecimal::class.java), + lastPrice = row.get("lastPrice", BigDecimal::class.java)?.toDouble(), tokenType = row.get("tokenType", TokenType::class.java), ) } diff --git a/src/main/kotlin/com/api/nft/service/RedisService.kt b/src/main/kotlin/com/api/nft/service/RedisService.kt index a6f595a..10d6186 100644 --- a/src/main/kotlin/com/api/nft/service/RedisService.kt +++ b/src/main/kotlin/com/api/nft/service/RedisService.kt @@ -1,8 +1,7 @@ package com.api.nft.service -import com.api.nft.domain.nft.Nft -import com.api.nft.domain.nft.NftListing import com.api.nft.domain.nft.repository.NftRepository +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import org.slf4j.Logger import org.slf4j.LoggerFactory import org.springframework.data.redis.core.ReactiveRedisTemplate @@ -17,22 +16,11 @@ class RedisService( private val logger: Logger = LoggerFactory.getLogger(RedisService::class.java) - fun saveNftToRedis(nft: Nft): Mono { - return nftRepository.findByNftJoinMetadata(nft.id!!) - .doOnNext { data -> logger.info("Data retrieved: $data") } // Log data to verify it is fetched + fun updateToRedis(nftId: Long): Mono { + return nftRepository.findByNftJoinMetadata(nftId) .flatMap { data -> - reactiveRedisTemplate.opsForValue().set("NFT:${nft.id}", data).then() - } - .doOnError { error -> logger.error("Error occurred: ${error.message}", error) } // Log any errors - } - - fun saveData(key : String) : Mono { - return reactiveRedisTemplate.opsForValue().set("NFT:${key}","orange").then() - } - fun updateToRedis(nft: NftListing): Mono { - return nftRepository.findByNftJoinMetadata(nft.nftId) - .flatMap { data -> - reactiveRedisTemplate.opsForValue().set("NFT:${nft.nftId}", data).then() + println("data: " + data.id) + reactiveRedisTemplate.opsForValue().set("NFT:${nftId}", data).then() } } } \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/service/api/NftListingService.kt b/src/main/kotlin/com/api/nft/service/api/NftListingService.kt index 98e44c5..2047fbf 100644 --- a/src/main/kotlin/com/api/nft/service/api/NftListingService.kt +++ b/src/main/kotlin/com/api/nft/service/api/NftListingService.kt @@ -29,7 +29,7 @@ class NftListingService( } else { Mono.just(nftListing) }.flatMap { updatedListing -> - redisService.updateToRedis(updatedListing) + redisService.updateToRedis(updatedListing.nftId) .thenReturn(updatedListing) } }.switchIfEmpty { save(newListing) } diff --git a/src/main/kotlin/com/api/nft/service/api/NftService.kt b/src/main/kotlin/com/api/nft/service/api/NftService.kt index f230fff..e6d1f7a 100644 --- a/src/main/kotlin/com/api/nft/service/api/NftService.kt +++ b/src/main/kotlin/com/api/nft/service/api/NftService.kt @@ -52,25 +52,31 @@ class NftService( fun createNftProcess(request: NftData, chainType: ChainType): Mono { val response = getNftData(request, chainType) - return response.flatMap { (nftData, metadataData, attributeDataList) -> - createNft(nftData, metadataData, chainType) - .flatMap { nft -> - metadataService.createMetadata(nft.id!!, metadataData) - .thenMany(attributeService.createAttribute( - nft.id, - attributeDataList ?: emptyList() - )) - .then(Mono.just(nft)) - .doOnSuccess { eventPublisher.publishEvent(NftCreatedEvent(this, nft.toResponse())) } - .flatMap { createdNft -> - redisService.saveNftToRedis(createdNft) - .thenReturn(createdNft) - } - .flatMap { - transferService.createTransfer(nft).thenReturn(nft) - } - } - } + return response + .flatMap { (nftData, metadataData, attributeDataList) -> + createMetadata(nftData, metadataData, attributeDataList ,chainType) } + .flatMap { createdNft -> + redisService.updateToRedis(createdNft.id!!).thenReturn(createdNft) } + .flatMap { nft -> + transferService.createTransfer(nft).thenReturn(nft) } + .doOnSuccess { + eventPublisher.publishEvent(NftCreatedEvent(this, it.toResponse())) + } + } + + + private fun createMetadata( + nftData: NftData, + metadataData: NftMetadata, + attributeDataList: List?, + chainType: ChainType + ): Mono { + return createNft(nftData, metadataData, chainType) + .flatMap { nft -> + metadataService.createMetadata(nft.id!!, metadataData) + .thenMany(attributeService.createAttribute(nft.id, attributeDataList ?: emptyList())) + .then(Mono.just(nft)) + } } private fun Nft.toResponse() = NftResponse( diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 3a3dcb4..c11451e 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -20,10 +20,7 @@ spring: data: redis: cluster: - # dongdorrong - nodes: localhost:6371,localhost:6372,localhost:6373,localhost:6374,localhost:6375 - # # minyoung - # nodes: localhost:6379,localhost:6380,localhost:6381,localhost:6382,localhost:6383,localhost:6384,localhost:6385,localhost:6386,localhost:6387 + nodes: localhost:6379,localhost:6380,localhost:6381,localhost:6382,localhost:6383,localhost:6384,localhost:6385,localhost:6386,localhost:6387,localhost:6388 max-redirects: 3 password: bitnami timeout: 500ms diff --git a/src/test/kotlin/com/api/nft/NftTest.kt b/src/test/kotlin/com/api/nft/NftTest.kt index fa86dcd..b18fed7 100644 --- a/src/test/kotlin/com/api/nft/NftTest.kt +++ b/src/test/kotlin/com/api/nft/NftTest.kt @@ -2,6 +2,7 @@ package com.api.nft import com.api.nft.domain.collection.repository.CollectionRepository import com.api.nft.domain.nft.Nft +import com.api.nft.domain.nft.repository.NftListingRepository import com.api.nft.domain.nft.repository.NftRepository import com.api.nft.enums.ChainType import com.api.nft.enums.ContractType @@ -37,6 +38,7 @@ class NftTest( @Autowired private val binanceApiService: BinanceApiService, @Autowired private val redisService: RedisService, @Autowired private val nftListingService: NftListingService, + @Autowired private val nftListingRepository: NftListingRepository, ) { @@ -131,21 +133,37 @@ class NftTest( nftListingService.update(listing).block() } + @Test + fun redisTest22( ){ + // val nftlisting = nftListingRepository.findByNftId(1L).block() + redisService.updateToRedis(1L).block() + } + @Test fun redisTest() { - val nft = nftRepository.findById(1).block() - redisService.saveNftToRedis(nft!!).block() + val nft = nftRepository.findById(8).block() + redisService.updateToRedis(nft?.id!!).block() + // val res = nftRepository.findByNftJoinMetadata(1).block() + } @Test - fun asdasd() { - nftService.getByWalletNft("0x01b72b4aa3f66f213d62d53e829bc172a6a72867",ChainType.POLYGON_MAINNET).blockLast() + fun nftService() { + val nft = nftService.findOrCreateNft(tokenAddress = "0x524cAB2ec69124574082676e6F654a18df49A048", tokenId = "5430",ChainType.ETHEREUM_MAINNET).block() + } @Test - fun test() { - redisService.saveData("heelo").block() + fun asdasd() { + nftService.getByWalletNft("0x01b72b4aa3f66f213d62d53e829bc172a6a72867",ChainType.POLYGON_MAINNET).blockLast() + // Thread.sleep(10000) + } + // @Test + // fun test() { + // redisService.saveData("heelo").block() + // } + } \ No newline at end of file From aa8568995b1db70b8f3944cf6e1469c9f7c37205 Mon Sep 17 00:00:00 2001 From: min-96 Date: Fri, 21 Jun 2024 00:07:56 +0900 Subject: [PATCH 25/38] refactor: add api (getAllByIds,getOneById) --- docker-compose.yaml | 4 ---- src/main/kotlin/com/api/nft/NftApplication.kt | 1 - .../com/api/nft/controller/NftController.kt | 6 +++++- .../kotlin/com/api/nft/service/RedisService.kt | 1 - .../kotlin/com/api/nft/service/api/NftService.kt | 16 +++++++++++++--- src/test/kotlin/com/api/nft/NftTest.kt | 11 +++++++---- 6 files changed, 25 insertions(+), 14 deletions(-) diff --git a/docker-compose.yaml b/docker-compose.yaml index 7d8e360..16f2e9c 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -195,7 +195,3 @@ volumes: driver: local redis-cluster_data-9: driver: local - -networks: - default: - name: local_network \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/NftApplication.kt b/src/main/kotlin/com/api/nft/NftApplication.kt index a52da12..e037c97 100644 --- a/src/main/kotlin/com/api/nft/NftApplication.kt +++ b/src/main/kotlin/com/api/nft/NftApplication.kt @@ -3,7 +3,6 @@ package com.api.nft import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.context.properties.ConfigurationPropertiesScan import org.springframework.boot.runApplication -import org.springframework.scheduling.annotation.EnableScheduling @SpringBootApplication @ConfigurationPropertiesScan diff --git a/src/main/kotlin/com/api/nft/controller/NftController.kt b/src/main/kotlin/com/api/nft/controller/NftController.kt index ef4e74e..d649da6 100644 --- a/src/main/kotlin/com/api/nft/controller/NftController.kt +++ b/src/main/kotlin/com/api/nft/controller/NftController.kt @@ -26,7 +26,6 @@ import reactor.core.publisher.Mono class NftController( private val nftService: NftService, private val transferService: TransferService, - private val redisService: RedisService, ) { @GetMapping @@ -34,6 +33,11 @@ class NftController( return nftService.findAllById(nftIds) } + @GetMapping("/{nftId}") + fun getOneById(@PathVariable nftId: Long): Mono { + return nftService.findById(nftId) + } + @GetMapping("/transfer") fun getTransfers(@RequestParam nftId: Long) : Mono>> { return transferService.findOrUpdateByNftId(nftId) diff --git a/src/main/kotlin/com/api/nft/service/RedisService.kt b/src/main/kotlin/com/api/nft/service/RedisService.kt index 10d6186..dcf0966 100644 --- a/src/main/kotlin/com/api/nft/service/RedisService.kt +++ b/src/main/kotlin/com/api/nft/service/RedisService.kt @@ -1,7 +1,6 @@ package com.api.nft.service import com.api.nft.domain.nft.repository.NftRepository -import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import org.slf4j.Logger import org.slf4j.LoggerFactory import org.springframework.data.redis.core.ReactiveRedisTemplate diff --git a/src/main/kotlin/com/api/nft/service/api/NftService.kt b/src/main/kotlin/com/api/nft/service/api/NftService.kt index e6d1f7a..53d0226 100644 --- a/src/main/kotlin/com/api/nft/service/api/NftService.kt +++ b/src/main/kotlin/com/api/nft/service/api/NftService.kt @@ -31,15 +31,25 @@ class NftService( fun findAllById(ids: List): Flux { return nftRepository.findAllByNftJoinMetadata(ids) + .doOnNext { nft -> + redisService.updateToRedis(nft.id!!).subscribe() + } + } + + fun findById(id: Long): Mono { + return nftRepository.findByNftJoinMetadata(id) + .doOnNext { nft -> + redisService.updateToRedis(nft.id!!).subscribe() + } } + fun findOrCreateNft(tokenAddress: String,tokenId: String, chainType: ChainType): Mono { return nftRepository.findByTokenAddressAndTokenIdAndChainType(tokenAddress,tokenId,chainType) .switchIfEmpty( moralisApiService.getNFTMetadata(tokenAddress,tokenId,chainType) .flatMap { createNftProcess(it,chainType) } ).map { it.toResponse() } - } fun getByWalletNft(wallet: String,chainType: ChainType): Flux { @@ -57,8 +67,8 @@ class NftService( createMetadata(nftData, metadataData, attributeDataList ,chainType) } .flatMap { createdNft -> redisService.updateToRedis(createdNft.id!!).thenReturn(createdNft) } - .flatMap { nft -> - transferService.createTransfer(nft).thenReturn(nft) } + // .flatMap { nft -> + // transferService.createTransfer(nft).thenReturn(nft) } .doOnSuccess { eventPublisher.publishEvent(NftCreatedEvent(this, it.toResponse())) } diff --git a/src/test/kotlin/com/api/nft/NftTest.kt b/src/test/kotlin/com/api/nft/NftTest.kt index b18fed7..34e79dd 100644 --- a/src/test/kotlin/com/api/nft/NftTest.kt +++ b/src/test/kotlin/com/api/nft/NftTest.kt @@ -161,9 +161,12 @@ class NftTest( } - // @Test - // fun test() { - // redisService.saveData("heelo").block() - // } + @Test + fun test() { + val address = "0x01b72b4aa3f66f213d62d53e829bc172a6a72867" + + val res= moralisApiService.getNFTsByAddress(address,ChainType.POLYGON_MAINNET).block() + println(res.toString()) + } } \ No newline at end of file From c475bf235989250869c4d5f0845fec4c0dfdb409 Mon Sep 17 00:00:00 2001 From: Donghyeon Im Date: Thu, 18 Jul 2024 10:21:00 +0900 Subject: [PATCH 26/38] refactor(docker-compose.yaml): add persistent volume (postgresql, rabbitmq) --- docker-compose.yaml | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/docker-compose.yaml b/docker-compose.yaml index 16f2e9c..c77d987 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -3,25 +3,27 @@ version: "3.8" services: db: image: postgres:latest - environment: - POSTGRES_USER: nft - POSTGRES_PASSWORD: nft - POSTGRES_DB: nft ports: - '5434:5432' volumes: - ./init.sql:/docker-entrypoint-initdb.d/init.sql + - ${HOME}/Google Drive/내 드라이브/marketplace/db:/var/lib/postgresql/data + environment: + POSTGRES_USER: nft + POSTGRES_PASSWORD: nft + POSTGRES_DB: nft + PGDATA: /var/lib/postgresql/data/pgdata rabbitmq: image: rabbitmq:3-management - environment: - RABBITMQ_DEFAULT_USER: closeSea - RABBITMQ_DEFAULT_PASS: closeSeaP@ssword ports: - '5672:5672' - '15672:15672' volumes: - - ./rabbitmq_data:/var/lib/rabbitmq + - ${HOME}/Google Drive/내 드라이브/marketplace/rabbitmq:/var/lib/rabbitmq + environment: + RABBITMQ_DEFAULT_USER: closeSea + RABBITMQ_DEFAULT_PASS: closeSeaP@ssword redis-node-0: image: docker.io/bitnami/redis-cluster:7.2 From d6adfe56fd8104a2ef946cd9b6b10fd44ca91c75 Mon Sep 17 00:00:00 2001 From: Donghyeon Im Date: Thu, 18 Jul 2024 12:03:30 +0900 Subject: [PATCH 27/38] fix(docker-compose.yaml): fix google drive path --- docker-compose.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker-compose.yaml b/docker-compose.yaml index c77d987..9ab99e6 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -7,7 +7,7 @@ services: - '5434:5432' volumes: - ./init.sql:/docker-entrypoint-initdb.d/init.sql - - ${HOME}/Google Drive/내 드라이브/marketplace/db:/var/lib/postgresql/data + - ${HOME}/Library/CloudStorage/GoogleDrive-dongkim.chatgpt@gmail.com/내 드라이브/marketplace/db:/var/lib/postgresql/data environment: POSTGRES_USER: nft POSTGRES_PASSWORD: nft @@ -20,7 +20,7 @@ services: - '5672:5672' - '15672:15672' volumes: - - ${HOME}/Google Drive/내 드라이브/marketplace/rabbitmq:/var/lib/rabbitmq + - ${HOME}/Library/CloudStorage/GoogleDrive-dongkim.chatgpt@gmail.com/내 드라이브/marketplace/rabbitmq:/var/lib/rabbitmq environment: RABBITMQ_DEFAULT_USER: closeSea RABBITMQ_DEFAULT_PASS: closeSeaP@ssword From 6e2da8707b197d633592cf93353ce558c08c4b5a Mon Sep 17 00:00:00 2001 From: min-96 Date: Thu, 18 Jul 2024 17:02:14 +0900 Subject: [PATCH 28/38] fix: path of googleDriv --- docker-compose.yaml | 4 ++-- .../api/nft/service/api/NftListingService.kt | 21 ++++++++++--------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/docker-compose.yaml b/docker-compose.yaml index 9ab99e6..2f85329 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -7,7 +7,7 @@ services: - '5434:5432' volumes: - ./init.sql:/docker-entrypoint-initdb.d/init.sql - - ${HOME}/Library/CloudStorage/GoogleDrive-dongkim.chatgpt@gmail.com/내 드라이브/marketplace/db:/var/lib/postgresql/data + - ${HOME}/Library/CloudStorage/GoogleDrive-dongkim.chatgpt@gmail.com/내 드라이브/marketplace/db/nft:/var/lib/postgresql/data environment: POSTGRES_USER: nft POSTGRES_PASSWORD: nft @@ -20,7 +20,7 @@ services: - '5672:5672' - '15672:15672' volumes: - - ${HOME}/Library/CloudStorage/GoogleDrive-dongkim.chatgpt@gmail.com/내 드라이브/marketplace/rabbitmq:/var/lib/rabbitmq + - ./rabbitmq_data:/var/lib/rabbitmq environment: RABBITMQ_DEFAULT_USER: closeSea RABBITMQ_DEFAULT_PASS: closeSeaP@ssword diff --git a/src/main/kotlin/com/api/nft/service/api/NftListingService.kt b/src/main/kotlin/com/api/nft/service/api/NftListingService.kt index 2047fbf..66e9f99 100644 --- a/src/main/kotlin/com/api/nft/service/api/NftListingService.kt +++ b/src/main/kotlin/com/api/nft/service/api/NftListingService.kt @@ -20,19 +20,20 @@ class NftListingService( fun update(newListing: ListingResponse): Mono { return nftListingRepository.findByNftId(newListing.nftId) - .flatMap { nftListing -> - val currentPrice = priceStorage.get(nftListing.tokenType)?.multiply(nftListing.price) ?: BigDecimal.ZERO - val newPrice = priceStorage.get(newListing.tokenType)?.multiply(newListing.price) ?: BigDecimal.ZERO - if (currentPrice < newPrice) { - nftListingRepository.updateListing(nftListing.nftId, price = newListing.price,newListing.tokenType) - .thenReturn(nftListing.update(newListing.price, newListing.tokenType)) - } else { - Mono.just(nftListing) - }.flatMap { updatedListing -> + // .flatMap { nftListing -> + // val currentPrice = priceStorage.get(nftListing.tokenType)?.multiply(nftListing.price) ?: BigDecimal.ZERO + // val newPrice = priceStorage.get(newListing.tokenType)?.multiply(newListing.price) ?: BigDecimal.ZERO + // if (currentPrice < newPrice) { + // nftListingRepository.updateListing(nftListing.nftId, price = newListing.price,newListing.tokenType) + // .thenReturn(nftListing.update(newListing.price, newListing.tokenType)) + // } else { + // Mono.just(nftListing) + // } + .flatMap { updatedListing -> redisService.updateToRedis(updatedListing.nftId) .thenReturn(updatedListing) } - }.switchIfEmpty { save(newListing) } + .switchIfEmpty { save(newListing) } } fun save(listing: ListingResponse) : Mono { From 1620e3b799d4d884dd286259efac7f8fce9d9b92 Mon Sep 17 00:00:00 2001 From: min-96 Date: Sun, 21 Jul 2024 02:08:01 +0900 Subject: [PATCH 29/38] feat: update listing --- build.gradle.kts | 4 +-- .../nft/repository/NftListingRepository.kt | 2 ++ .../com/api/nft/rabbitMQ/RabbitMQReceiver.kt | 9 +++--- .../api/nft/service/api/NftListingService.kt | 31 +++++++------------ .../api/nft/service/dto/ListingResponse.kt | 3 +- src/test/kotlin/com/api/nft/NftTest.kt | 28 ++++++++--------- 6 files changed, 37 insertions(+), 40 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index ddfadbc..4700234 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -14,7 +14,7 @@ group = "com.api" version = "0.0.1-SNAPSHOT" java { - sourceCompatibility = JavaVersion.VERSION_17 + sourceCompatibility = JavaVersion.VERSION_21 } repositories { @@ -52,7 +52,7 @@ dependencies { tasks.withType { kotlinOptions { freeCompilerArgs += "-Xjsr305=strict" - jvmTarget = "17" + jvmTarget = "21" } } diff --git a/src/main/kotlin/com/api/nft/domain/nft/repository/NftListingRepository.kt b/src/main/kotlin/com/api/nft/domain/nft/repository/NftListingRepository.kt index a38dc64..19868f3 100644 --- a/src/main/kotlin/com/api/nft/domain/nft/repository/NftListingRepository.kt +++ b/src/main/kotlin/com/api/nft/domain/nft/repository/NftListingRepository.kt @@ -16,4 +16,6 @@ interface NftListingRepository : R2dbcRepository { fun deleteAllByNftIdIn(nftId: List): Mono + fun deleteByNftId(nftId: Long): Mono + } \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/rabbitMQ/RabbitMQReceiver.kt b/src/main/kotlin/com/api/nft/rabbitMQ/RabbitMQReceiver.kt index ad3e03a..0f928ed 100644 --- a/src/main/kotlin/com/api/nft/rabbitMQ/RabbitMQReceiver.kt +++ b/src/main/kotlin/com/api/nft/rabbitMQ/RabbitMQReceiver.kt @@ -11,12 +11,13 @@ class RabbitMQReceiver( ) { @RabbitListener(queues = ["listingQueue"]) fun listingMessage(listing: ListingResponse){ + println("몇번 들어오는지 체크할게요") + println("active : " + listing.active) nftListingService.update(listing).subscribe() } - @RabbitListener(queues = ["listingCancelQueue"]) - fun listingCancelMessage(nftIds: List){ - println("ids: " + nftIds.toList()) - nftListingService.batchDelete(nftIds).subscribe() + fun listingCancelMessage(listing: ListingResponse){ +// println("ids: " + nftIds.toList()) + nftListingService.update(listing).subscribe() } } \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/service/api/NftListingService.kt b/src/main/kotlin/com/api/nft/service/api/NftListingService.kt index 66e9f99..ce582fb 100644 --- a/src/main/kotlin/com/api/nft/service/api/NftListingService.kt +++ b/src/main/kotlin/com/api/nft/service/api/NftListingService.kt @@ -14,29 +14,25 @@ import java.math.BigDecimal @Service class NftListingService( private val nftListingRepository: NftListingRepository, - private val priceStorage: PriceStorage, private val redisService: RedisService, ) { - fun update(newListing: ListingResponse): Mono { - return nftListingRepository.findByNftId(newListing.nftId) - // .flatMap { nftListing -> - // val currentPrice = priceStorage.get(nftListing.tokenType)?.multiply(nftListing.price) ?: BigDecimal.ZERO - // val newPrice = priceStorage.get(newListing.tokenType)?.multiply(newListing.price) ?: BigDecimal.ZERO - // if (currentPrice < newPrice) { - // nftListingRepository.updateListing(nftListing.nftId, price = newListing.price,newListing.tokenType) - // .thenReturn(nftListing.update(newListing.price, newListing.tokenType)) - // } else { - // Mono.just(nftListing) - // } - .flatMap { updatedListing -> - redisService.updateToRedis(updatedListing.nftId) - .thenReturn(updatedListing) + fun update(newListing: ListingResponse): Mono { + return if (newListing.active) { + println("why not save ? : " + newListing.active ) + save(newListing) + .then(redisService.updateToRedis(newListing.nftId)) + } else { + nftListingRepository.findByNftId(newListing.nftId) + .flatMap { nftListing -> + nftListingRepository.deleteByNftId(nftListing.nftId) } - .switchIfEmpty { save(newListing) } + .then(redisService.updateToRedis(newListing.nftId)) + } } fun save(listing: ListingResponse) : Mono { + println("do you save logic ?") return nftListingRepository.save( NftListing( nftId = listing.nftId, @@ -46,7 +42,4 @@ class NftListingService( ) } - fun batchDelete(nftIds: List): Mono { - return nftListingRepository.deleteAllByNftIdIn(nftIds) - } } \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/service/dto/ListingResponse.kt b/src/main/kotlin/com/api/nft/service/dto/ListingResponse.kt index f5fbf23..81def09 100644 --- a/src/main/kotlin/com/api/nft/service/dto/ListingResponse.kt +++ b/src/main/kotlin/com/api/nft/service/dto/ListingResponse.kt @@ -9,6 +9,7 @@ data class ListingResponse( val address: String, val createdDateTime: Long, val endDateTime: Long, + val active: Boolean, val price: BigDecimal, val tokenType: TokenType -) +) \ No newline at end of file diff --git a/src/test/kotlin/com/api/nft/NftTest.kt b/src/test/kotlin/com/api/nft/NftTest.kt index 34e79dd..f080130 100644 --- a/src/test/kotlin/com/api/nft/NftTest.kt +++ b/src/test/kotlin/com/api/nft/NftTest.kt @@ -118,20 +118,20 @@ class NftTest( } - @Test - fun nftListing() { - val listing = ListingResponse( - id = 1, - nftId = 3L, - address = "0x01b72b4aa3f66f213d62d53e829bc172a6a72867", - createdDateTime = 1714662809000, - endDateTime = 1714662809000, - price = BigDecimal(0.23), - tokenType = TokenType.ETH - - ) - nftListingService.update(listing).block() - } +// @Test +// fun nftListing() { +// val listing = ListingResponse( +// id = 1, +// nftId = 3L, +// address = "0x01b72b4aa3f66f213d62d53e829bc172a6a72867", +// createdDateTime = 1714662809000, +// endDateTime = 1714662809000, +// price = BigDecimal(0.23), +// tokenType = TokenType.ETH +// +// ) +// nftListingService.update(listing).block() +// } @Test fun redisTest22( ){ From 18d0682bf8fd6c03100b93ee1813a98e4ba00f84 Mon Sep 17 00:00:00 2001 From: min96 Date: Mon, 22 Jul 2024 19:43:31 +0900 Subject: [PATCH 30/38] refactor: change to FanoutExchange --- .../kotlin/com/api/nft/config/RabbitConfig.kt | 37 +++---------------- .../com/api/nft/rabbitMQ/RabbitMQReceiver.kt | 19 ++++++++-- .../com/api/nft/rabbitMQ/RabbitMQSender.kt | 2 +- 3 files changed, 22 insertions(+), 36 deletions(-) diff --git a/src/main/kotlin/com/api/nft/config/RabbitConfig.kt b/src/main/kotlin/com/api/nft/config/RabbitConfig.kt index f7ecdcb..7d8bf7b 100644 --- a/src/main/kotlin/com/api/nft/config/RabbitConfig.kt +++ b/src/main/kotlin/com/api/nft/config/RabbitConfig.kt @@ -1,9 +1,6 @@ package com.api.nft.config -import org.springframework.amqp.core.Binding -import org.springframework.amqp.core.BindingBuilder -import org.springframework.amqp.core.DirectExchange -import org.springframework.amqp.core.Queue +import org.springframework.amqp.core.* import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import org.springframework.amqp.rabbit.connection.ConnectionFactory @@ -25,41 +22,19 @@ class RabbitConfig { return template } - private fun createQueue(name: String, durable: Boolean = true): Queue { - return Queue(name, durable) - } - - private fun createExchange(name: String): DirectExchange { - return DirectExchange(name) - } - private fun createBinding(queue: Queue, exchange: DirectExchange, routingKey: String): Binding { - return BindingBuilder.bind(queue).to(exchange).with(routingKey) + private fun createFanoutExchange(name: String): FanoutExchange { + return FanoutExchange(name) } @Bean - fun nftQueue() = createQueue("nftQueue") - - @Bean - fun nftExchange() = createExchange("nftExchange") - - @Bean - fun bindingNftQueue(nftQueue: Queue, nftExchange: DirectExchange) = createBinding(nftQueue, nftExchange, "nftRoutingKey") - - @Bean - fun listingQueue() = createQueue("listingQueue") + fun nftExchange() = createFanoutExchange("nftExchange") - @Bean - fun listingExchange() = createExchange("listingExchange") @Bean - fun bindingListingQueue(listingQueue: Queue, listingExchange: DirectExchange) = createBinding(listingQueue, listingExchange, "listingRoutingKey") + fun listingExchange() = createFanoutExchange("listingExchange") - @Bean - fun listingCancelQueue() = createQueue("listingCancelQueue") @Bean - fun listingCancelExchange() = createExchange("listingCancelExchange") - @Bean - fun bindingListingCancelQueue(listingCancelQueue: Queue, listingCancelExchange: DirectExchange) = createBinding(listingCancelQueue, listingCancelExchange, "listingCancelRoutingKey") + fun listingCancelExchange() = createFanoutExchange("listingCancelExchange") } \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/rabbitMQ/RabbitMQReceiver.kt b/src/main/kotlin/com/api/nft/rabbitMQ/RabbitMQReceiver.kt index 0f928ed..c6bfbdd 100644 --- a/src/main/kotlin/com/api/nft/rabbitMQ/RabbitMQReceiver.kt +++ b/src/main/kotlin/com/api/nft/rabbitMQ/RabbitMQReceiver.kt @@ -2,6 +2,10 @@ package com.api.nft.rabbitMQ import com.api.nft.service.api.NftListingService import com.api.nft.service.dto.ListingResponse +import org.springframework.amqp.core.ExchangeTypes +import org.springframework.amqp.rabbit.annotation.Exchange +import org.springframework.amqp.rabbit.annotation.Queue +import org.springframework.amqp.rabbit.annotation.QueueBinding import org.springframework.amqp.rabbit.annotation.RabbitListener import org.springframework.stereotype.Service @@ -9,15 +13,22 @@ import org.springframework.stereotype.Service class RabbitMQReceiver( private val nftListingService: NftListingService, ) { - @RabbitListener(queues = ["listingQueue"]) + + + @RabbitListener(bindings = [QueueBinding( + value = Queue(name = "", durable = "false", exclusive = "true", autoDelete = "true"), + exchange = Exchange(value = "listingExchange", type = ExchangeTypes.FANOUT) + )]) fun listingMessage(listing: ListingResponse){ - println("몇번 들어오는지 체크할게요") println("active : " + listing.active) nftListingService.update(listing).subscribe() } - @RabbitListener(queues = ["listingCancelQueue"]) + + @RabbitListener(bindings = [QueueBinding( + value = Queue(name = "", durable = "false", exclusive = "true", autoDelete = "true"), + exchange = Exchange(value = "listingCancelExchange", type = ExchangeTypes.FANOUT) + )]) fun listingCancelMessage(listing: ListingResponse){ -// println("ids: " + nftIds.toList()) nftListingService.update(listing).subscribe() } } \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/rabbitMQ/RabbitMQSender.kt b/src/main/kotlin/com/api/nft/rabbitMQ/RabbitMQSender.kt index 16eb00f..ff507a7 100644 --- a/src/main/kotlin/com/api/nft/rabbitMQ/RabbitMQSender.kt +++ b/src/main/kotlin/com/api/nft/rabbitMQ/RabbitMQSender.kt @@ -10,6 +10,6 @@ class RabbitMQSender( ) { fun nftSend(nft: NftResponse) { - rabbitTemplate.convertAndSend("nftExchange", "nftRoutingKey", nft) + rabbitTemplate.convertAndSend("nftExchange", "", nft) } } \ No newline at end of file From b454ef92ad6a557e5e0dd5f2646accf42e7f2acd Mon Sep 17 00:00:00 2001 From: min-96 Date: Tue, 23 Jul 2024 15:54:17 +0900 Subject: [PATCH 31/38] apply config --- build.gradle.kts | 8 +++ .../com/api/nft/schedule/PriceScheduler.kt | 26 --------- .../api/nft/service/api/NftListingService.kt | 4 -- .../external/infura/InfuraApiService.kt | 13 ++--- .../external/moralis/MoralisApiService.kt | 12 ++-- .../com/api/nft/storage/PriceStorage.kt | 18 ------ src/main/resources/application-local.yml | 56 +++++++++++++++++++ src/main/resources/application.yml | 50 ----------------- src/main/resources/bootstrap.yaml | 9 +++ 9 files changed, 86 insertions(+), 110 deletions(-) delete mode 100644 src/main/kotlin/com/api/nft/schedule/PriceScheduler.kt delete mode 100644 src/main/kotlin/com/api/nft/storage/PriceStorage.kt create mode 100644 src/main/resources/application-local.yml delete mode 100644 src/main/resources/application.yml create mode 100644 src/main/resources/bootstrap.yaml diff --git a/build.gradle.kts b/build.gradle.kts index 4700234..78deaab 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -20,8 +20,10 @@ java { repositories { mavenCentral() } +extra["springCloudVersion"] = "2023.0.2" dependencies { + implementation("org.springframework.cloud:spring-cloud-starter-config") implementation("org.springframework.boot:spring-boot-starter-batch") implementation("org.springframework.boot:spring-boot-starter-data-r2dbc") implementation("org.postgresql:r2dbc-postgresql:1.0.4.RELEASE") @@ -48,6 +50,12 @@ dependencies { implementation("io.netty:netty-resolver-dns-native-macos:4.1.68.Final:osx-aarch_64") } +dependencyManagement { + imports { + mavenBom("org.springframework.cloud:spring-cloud-dependencies:${property("springCloudVersion")}") + } +} + tasks.withType { kotlinOptions { diff --git a/src/main/kotlin/com/api/nft/schedule/PriceScheduler.kt b/src/main/kotlin/com/api/nft/schedule/PriceScheduler.kt deleted file mode 100644 index 68d7d85..0000000 --- a/src/main/kotlin/com/api/nft/schedule/PriceScheduler.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.api.nft.schedule - -import com.api.nft.enums.TokenType -import com.api.nft.service.external.binance.BinanceApiService -import com.api.nft.storage.PriceStorage -import org.springframework.scheduling.annotation.Scheduled -import org.springframework.stereotype.Component - -@Component -class PriceScheduler( - private val priceStorage: PriceStorage, - private val binanceApiService: BinanceApiService, - ) -{ - @Scheduled(fixedRate = 3600000) - fun updatePrices() { - TokenType.entries.map { - binanceApiService.getTickerPrice(it) - .subscribe { response-> - priceStorage.update(it,response.price) - println("Updated ${it} price to ${response.price}") - } - } - } - -} \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/service/api/NftListingService.kt b/src/main/kotlin/com/api/nft/service/api/NftListingService.kt index ce582fb..804c105 100644 --- a/src/main/kotlin/com/api/nft/service/api/NftListingService.kt +++ b/src/main/kotlin/com/api/nft/service/api/NftListingService.kt @@ -2,14 +2,10 @@ package com.api.nft.service.api import com.api.nft.domain.nft.NftListing import com.api.nft.domain.nft.repository.NftListingRepository -import com.api.nft.domain.nft.repository.NftRepository import com.api.nft.service.RedisService import com.api.nft.service.dto.ListingResponse -import com.api.nft.storage.PriceStorage import org.springframework.stereotype.Service import reactor.core.publisher.Mono -import reactor.kotlin.core.publisher.switchIfEmpty -import java.math.BigDecimal @Service class NftListingService( diff --git a/src/main/kotlin/com/api/nft/service/external/infura/InfuraApiService.kt b/src/main/kotlin/com/api/nft/service/external/infura/InfuraApiService.kt index 48ce03e..8be892a 100644 --- a/src/main/kotlin/com/api/nft/service/external/infura/InfuraApiService.kt +++ b/src/main/kotlin/com/api/nft/service/external/infura/InfuraApiService.kt @@ -1,6 +1,7 @@ package com.api.nft.service.external.infura import com.api.nft.enums.ChainType +import com.api.nft.properties.ApiKeysProperties import com.api.nft.service.external.dto.EthLogRequest import com.api.nft.service.external.dto.EthLogResponse import com.api.nft.service.external.dto.InfuraRequest @@ -20,7 +21,9 @@ import java.time.Instant @Service -class InfuraApiService { +class InfuraApiService( + private val apiKeysProperties: ApiKeysProperties +) { private fun urlByChain(chainType: ChainType) : WebClient { val baseUrl = when (chainType) { @@ -69,7 +72,7 @@ class InfuraApiService { val webClient = urlByChain(chainType) return webClient.post() - .uri("/v3/$apiKey") + .uri("/v3/${apiKeysProperties.infura}") .contentType(MediaType.APPLICATION_JSON) .bodyValue(requestBody) .retrieve() @@ -86,7 +89,7 @@ class InfuraApiService { ) return webClient.post() - .uri("/v3/$apiKey") + .uri("/v3/${apiKeysProperties.infura}") .contentType(MediaType.APPLICATION_JSON) .bodyValue(requestBody) .retrieve() @@ -97,8 +100,4 @@ class InfuraApiService { Instant.ofEpochSecond(Numeric.decodeQuantity(timestamp).longValueExact()).toEpochMilli() } } - - companion object{ - private val apiKey = "98b672d2ce9a4089a3a5cb5081dde2fa" - } } \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/service/external/moralis/MoralisApiService.kt b/src/main/kotlin/com/api/nft/service/external/moralis/MoralisApiService.kt index 01401ce..605e570 100644 --- a/src/main/kotlin/com/api/nft/service/external/moralis/MoralisApiService.kt +++ b/src/main/kotlin/com/api/nft/service/external/moralis/MoralisApiService.kt @@ -1,6 +1,7 @@ package com.api.nft.service.external.moralis import com.api.nft.enums.ChainType +import com.api.nft.properties.ApiKeysProperties import com.api.nft.service.external.dto.NFTByWalletResponse import com.api.nft.service.external.dto.NftData import com.api.nft.service.external.dto.NftTransferData @@ -10,7 +11,9 @@ import org.springframework.web.reactive.function.client.WebClient import reactor.core.publisher.Mono @Service -class MoralisApiService { +class MoralisApiService( + private val apiKeysProperties: ApiKeysProperties, +) { private val webClient = WebClient.builder() .baseUrl(baseUrl) @@ -41,7 +44,7 @@ class MoralisApiService { it.queryParam("chain", chain) it.build() } - .header("X-API-Key", apiKey) + .header("X-API-Key", apiKeysProperties.moralis) .header("Accept", MediaType.APPLICATION_JSON_VALUE) .retrieve() .bodyToMono(NftTransferData::class.java) @@ -56,7 +59,7 @@ class MoralisApiService { it.queryParam("exclude_spam", true) it.build() } - .header("X-API-Key", apiKey) + .header("X-API-Key", apiKeysProperties.moralis) .header("Accept", MediaType.APPLICATION_JSON_VALUE) .retrieve() .bodyToMono(NFTByWalletResponse::class.java) @@ -70,7 +73,7 @@ class MoralisApiService { it.queryParam("chain", chain) it.build() } - .header("X-API-Key", apiKey) + .header("X-API-Key", apiKeysProperties.moralis) .header("Accept", MediaType.APPLICATION_JSON_VALUE) .retrieve() .bodyToMono(NftData::class.java) @@ -79,7 +82,6 @@ class MoralisApiService { companion object { - private val apiKey = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJub25jZSI6ImJiNmIxMWJmLWNmNzItNDg0OC04OGEyLTBjYTIwODRjN2VhMyIsIm9yZ0lkIjoiMzgzODQwIiwidXNlcklkIjoiMzk0NDAyIiwidHlwZUlkIjoiMGZlYWQ5NDctZjQwZS00MDkwLWFlNGUtOTA1ZTdmMjUxZTAzIiwidHlwZSI6IlBST0pFQ1QiLCJpYXQiOjE3MTA5NTIwMjYsImV4cCI6NDg2NjcxMjAyNn0.VQE60IPGiWxdp7jKLF0jzXnxrLjEpU56H4bnfhMt0Sw" private val baseUrl = "https://deep-index.moralis.io/api" } } \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/storage/PriceStorage.kt b/src/main/kotlin/com/api/nft/storage/PriceStorage.kt deleted file mode 100644 index e77c04e..0000000 --- a/src/main/kotlin/com/api/nft/storage/PriceStorage.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.api.nft.storage - -import com.api.nft.enums.TokenType -import org.springframework.stereotype.Component -import java.math.BigDecimal -import java.util.concurrent.ConcurrentHashMap - -@Component -class PriceStorage { - private val prices: ConcurrentHashMap = ConcurrentHashMap() - - - fun update(tokenType: TokenType, price: BigDecimal) { - prices[tokenType] = price - } - - fun get(tokenType: TokenType): BigDecimal? = prices[tokenType] -} \ No newline at end of file diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml new file mode 100644 index 0000000..c805f93 --- /dev/null +++ b/src/main/resources/application-local.yml @@ -0,0 +1,56 @@ +# +#spring: +# application: +# name: nft +# datasource: +# url: jdbc:postgresql://localhost:5434/nft +# username: nft +# password: nft +# flyway: +# locations: classpath:db/migration +# r2dbc: +# url: r2dbc:postgresql://localhost:5434/nft +# username: nft +# password: nft +# rabbitmq: +# host: localhost +# port: 5672 +# username: closeSea +# password: closeSeaP@ssword +# data: +# redis: +# cluster: +# nodes: localhost:6379,localhost:6380,localhost:6381,localhost:6382,localhost:6383,localhost:6384,localhost:6385,localhost:6386,localhost:6387,localhost:6388 +# max-redirects: 3 +# password: bitnami +# timeout: 500ms +# connect-ip: localhost +# lettuce: +# pool: +# max-active: 8 +# max-idle: 8 +# min-idle: 0 +# session: +# redis: +# flush-mode: on_save +# +#logging: +# level: +# org.springframework.r2dbc: debug +# org.springframework.data.redis: DEBUG +# redis.clients.jedis: DEBUG +# io.lettuce.core: DEBUG +# root: INFO +# +#server: +# port: 8082 +# + +spring: + application: + name: nft + config: + import: "optional:configserver:http://localhost:9000" + cloud: + config: + fail-fast: true diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml deleted file mode 100644 index c11451e..0000000 --- a/src/main/resources/application.yml +++ /dev/null @@ -1,50 +0,0 @@ - -spring: - application: - name: nft - datasource: - url: jdbc:postgresql://localhost:5434/nft - username: nft - password: nft - flyway: - locations: classpath:db/migration - r2dbc: - url: r2dbc:postgresql://localhost:5434/nft - username: nft - password: nft - rabbitmq: - host: localhost - port: 5672 - username: closeSea - password: closeSeaP@ssword - data: - redis: - cluster: - nodes: localhost:6379,localhost:6380,localhost:6381,localhost:6382,localhost:6383,localhost:6384,localhost:6385,localhost:6386,localhost:6387,localhost:6388 - max-redirects: 3 - password: bitnami - timeout: 500ms - connect-ip: localhost - lettuce: - pool: - max-active: 8 - max-idle: 8 - min-idle: 0 - session: - redis: - flush-mode: on_save - -logging: - level: - org.springframework.r2dbc: debug - org.springframework.data.redis: DEBUG - redis.clients.jedis: DEBUG - io.lettuce.core: DEBUG - root: INFO - -server: - port: 8082 - -apikey: - infura: "98b672d2ce9a4089a3a5cb5081dde2fa" - moralis: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJub25jZSI6ImJiNmIxMWJmLWNmNzItNDg0OC04OGEyLTBjYTIwODRjN2VhMyIsIm9yZ0lkIjoiMzgzODQwIiwidXNlcklkIjoiMzk0NDAyIiwidHlwZUlkIjoiMGZlYWQ5NDctZjQwZS00MDkwLWFlNGUtOTA1ZTdmMjUxZTAzIiwidHlwZSI6IlBST0pFQ1QiLCJpYXQiOjE3MTA5NTIwMjYsImV4cCI6NDg2NjcxMjAyNn0.VQE60IPGiWxdp7jKLF0jzXnxrLjEpU56H4bnfhMt0Sw" \ No newline at end of file diff --git a/src/main/resources/bootstrap.yaml b/src/main/resources/bootstrap.yaml new file mode 100644 index 0000000..01f8804 --- /dev/null +++ b/src/main/resources/bootstrap.yaml @@ -0,0 +1,9 @@ +spring: + application: + name: nft + cloud: + config: + uri: https://localhost:9000 + fail-fast: true + profiles: + active: local From 32dce5fc244ec5ec1f108f0bd8ea97d4a4dce17c Mon Sep 17 00:00:00 2001 From: min-96 Date: Wed, 24 Jul 2024 23:39:01 +0900 Subject: [PATCH 32/38] refactor: add statusType --- .../kotlin/com/api/nft/config/R2dbcConfig.kt | 5 +++ .../kotlin/com/api/nft/config/RabbitConfig.kt | 4 +- .../com/api/nft/domain/nft/NftListing.kt | 8 +++- .../nft/repository/NftListingRepository.kt | 7 ++-- src/main/kotlin/com/api/nft/enums/Enums.kt | 4 +- .../com/api/nft/rabbitMQ/RabbitMQReceiver.kt | 15 ++++--- .../api/nft/service/api/NftListingService.kt | 40 +++++++++++++------ .../api/nft/service/dto/ListingResponse.kt | 3 +- .../com/api/nft/util/ChainTypeConvert.kt | 2 + .../migration/V1__Initial_schema.sql | 11 ++++- 10 files changed, 67 insertions(+), 32 deletions(-) diff --git a/src/main/kotlin/com/api/nft/config/R2dbcConfig.kt b/src/main/kotlin/com/api/nft/config/R2dbcConfig.kt index 78e2e16..c1d7f43 100644 --- a/src/main/kotlin/com/api/nft/config/R2dbcConfig.kt +++ b/src/main/kotlin/com/api/nft/config/R2dbcConfig.kt @@ -2,9 +2,11 @@ package com.api.nft.config import com.api.nft.enums.ChainType import com.api.nft.enums.ContractType +import com.api.nft.enums.StatusType import com.api.nft.enums.TokenType import com.api.nft.util.ChainTypeConvert import com.api.nft.util.ContractTypeConverter +import com.api.nft.util.StatusTypeConvert import com.api.nft.util.StringToEnumConverter import com.api.nft.util.TokenTypeConvert import io.r2dbc.postgresql.PostgresqlConnectionConfiguration @@ -40,6 +42,7 @@ class R2dbcConfig : AbstractR2dbcConfiguration() { .withEnum("contract_type", ContractType::class.java) .withEnum("chain_type", ChainType::class.java) .withEnum("token_type", TokenType::class.java) + .withEnum("status_type", StatusType::class.java) .build() ) .build() @@ -55,6 +58,8 @@ class R2dbcConfig : AbstractR2dbcConfiguration() { converters.add(StringToEnumConverter(ChainType::class.java)) converters.add(TokenTypeConvert(TokenType::class.java)) converters.add(StringToEnumConverter(TokenType::class.java)) + converters.add(StatusTypeConvert(StatusType::class.java)) + converters.add(StringToEnumConverter(StatusType::class.java)) return R2dbcCustomConversions(storeConversions, converters) } diff --git a/src/main/kotlin/com/api/nft/config/RabbitConfig.kt b/src/main/kotlin/com/api/nft/config/RabbitConfig.kt index 7d8bf7b..ebcd01b 100644 --- a/src/main/kotlin/com/api/nft/config/RabbitConfig.kt +++ b/src/main/kotlin/com/api/nft/config/RabbitConfig.kt @@ -35,6 +35,6 @@ class RabbitConfig { fun listingExchange() = createFanoutExchange("listingExchange") - @Bean - fun listingCancelExchange() = createFanoutExchange("listingCancelExchange") + // @Bean + // fun listingCancelExchange() = createFanoutExchange("listingCancelExchange") } \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/domain/nft/NftListing.kt b/src/main/kotlin/com/api/nft/domain/nft/NftListing.kt index 5536d2f..2952c59 100644 --- a/src/main/kotlin/com/api/nft/domain/nft/NftListing.kt +++ b/src/main/kotlin/com/api/nft/domain/nft/NftListing.kt @@ -1,5 +1,6 @@ package com.api.nft.domain.nft +import com.api.nft.enums.StatusType import com.api.nft.enums.TokenType import org.springframework.data.relational.core.mapping.Table import java.math.BigDecimal @@ -10,8 +11,11 @@ data class NftListing( val price: BigDecimal, val tokenType: TokenType, val nftId: Long, + val statusType: StatusType, + val createdDate: Long, + val endDate: Long, ){ - fun update(newPrice: BigDecimal,new_tokenType: TokenType): NftListing { - return this.copy(price = newPrice, tokenType = new_tokenType) + fun updateStatus(statusType: StatusType): NftListing { + return this.copy(statusType = statusType) } } diff --git a/src/main/kotlin/com/api/nft/domain/nft/repository/NftListingRepository.kt b/src/main/kotlin/com/api/nft/domain/nft/repository/NftListingRepository.kt index 19868f3..f79eefd 100644 --- a/src/main/kotlin/com/api/nft/domain/nft/repository/NftListingRepository.kt +++ b/src/main/kotlin/com/api/nft/domain/nft/repository/NftListingRepository.kt @@ -1,6 +1,7 @@ package com.api.nft.domain.nft.repository import com.api.nft.domain.nft.NftListing +import com.api.nft.enums.StatusType import com.api.nft.enums.TokenType import org.springframework.data.r2dbc.repository.Query import org.springframework.data.r2dbc.repository.R2dbcRepository @@ -11,10 +12,8 @@ interface NftListingRepository : R2dbcRepository { fun findByNftId(nftId: Long) : Mono - @Query("UPDATE nft_listing SET price = :price, token_type = :tokenType WHERE nft_id = :nftId") - fun updateListing(nftId: Long, price: BigDecimal, tokenType: TokenType): Mono - - fun deleteAllByNftIdIn(nftId: List): Mono + @Query("UPDATE nft_listing SET status_type = :statusType WHERE nft_id = :nftId") + fun updateListing(nftId: Long, statusType: StatusType): Mono fun deleteByNftId(nftId: Long): Mono diff --git a/src/main/kotlin/com/api/nft/enums/Enums.kt b/src/main/kotlin/com/api/nft/enums/Enums.kt index 3531380..4574ecd 100644 --- a/src/main/kotlin/com/api/nft/enums/Enums.kt +++ b/src/main/kotlin/com/api/nft/enums/Enums.kt @@ -23,4 +23,6 @@ enum class ContractType{ enum class TokenType { SAND, MATIC, ETH, BTC -} \ No newline at end of file +} + +enum class StatusType { RESERVATION, LISTING, RESERVATION_CANCEL, CANCEL, EXPIRED } \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/rabbitMQ/RabbitMQReceiver.kt b/src/main/kotlin/com/api/nft/rabbitMQ/RabbitMQReceiver.kt index c6bfbdd..7853e6d 100644 --- a/src/main/kotlin/com/api/nft/rabbitMQ/RabbitMQReceiver.kt +++ b/src/main/kotlin/com/api/nft/rabbitMQ/RabbitMQReceiver.kt @@ -20,15 +20,14 @@ class RabbitMQReceiver( exchange = Exchange(value = "listingExchange", type = ExchangeTypes.FANOUT) )]) fun listingMessage(listing: ListingResponse){ - println("active : " + listing.active) nftListingService.update(listing).subscribe() } - @RabbitListener(bindings = [QueueBinding( - value = Queue(name = "", durable = "false", exclusive = "true", autoDelete = "true"), - exchange = Exchange(value = "listingCancelExchange", type = ExchangeTypes.FANOUT) - )]) - fun listingCancelMessage(listing: ListingResponse){ - nftListingService.update(listing).subscribe() - } + // @RabbitListener(bindings = [QueueBinding( + // value = Queue(name = "", durable = "false", exclusive = "true", autoDelete = "true"), + // exchange = Exchange(value = "listingCancelExchange", type = ExchangeTypes.FANOUT) + // )]) + // fun listingCancelMessage(listing: ListingResponse){ + // nftListingService.update(listing).subscribe() + // } } \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/service/api/NftListingService.kt b/src/main/kotlin/com/api/nft/service/api/NftListingService.kt index 804c105..28f621e 100644 --- a/src/main/kotlin/com/api/nft/service/api/NftListingService.kt +++ b/src/main/kotlin/com/api/nft/service/api/NftListingService.kt @@ -2,6 +2,7 @@ package com.api.nft.service.api import com.api.nft.domain.nft.NftListing import com.api.nft.domain.nft.repository.NftListingRepository +import com.api.nft.enums.StatusType import com.api.nft.service.RedisService import com.api.nft.service.dto.ListingResponse import org.springframework.stereotype.Service @@ -14,26 +15,39 @@ class NftListingService( ) { fun update(newListing: ListingResponse): Mono { - return if (newListing.active) { - println("why not save ? : " + newListing.active ) - save(newListing) - .then(redisService.updateToRedis(newListing.nftId)) - } else { - nftListingRepository.findByNftId(newListing.nftId) - .flatMap { nftListing -> - nftListingRepository.deleteByNftId(nftListing.nftId) - } - .then(redisService.updateToRedis(newListing.nftId)) + return when (newListing.statusType) { + StatusType.RESERVATION -> { + save(newListing) + .then(redisService.updateToRedis(newListing.nftId)) + } + StatusType.LISTING -> { + println("update type LISTING") + nftListingRepository.findByNftId(newListing.nftId) + .flatMap { nftListing -> + println("id : " + newListing.nftId) + println("statusType : " + newListing.statusType) + nftListingRepository.updateListing(nftId = nftListing.nftId, statusType = newListing.statusType) + } + .then(redisService.updateToRedis(newListing.nftId)) + } + StatusType.RESERVATION_CANCEL, StatusType.CANCEL, StatusType.EXPIRED -> { + nftListingRepository.findByNftId(newListing.nftId) + .flatMap { nftListing -> + nftListingRepository.deleteByNftId(nftListing.nftId) + } + .then(redisService.updateToRedis(newListing.nftId)) + } } } - fun save(listing: ListingResponse) : Mono { - println("do you save logic ?") return nftListingRepository.save( NftListing( nftId = listing.nftId, price = listing.price, - tokenType = listing.tokenType + tokenType = listing.tokenType, + statusType = listing.statusType, + createdDate = listing.createdDateTime, + endDate = listing.endDateTime, ) ) } diff --git a/src/main/kotlin/com/api/nft/service/dto/ListingResponse.kt b/src/main/kotlin/com/api/nft/service/dto/ListingResponse.kt index 81def09..ec01a73 100644 --- a/src/main/kotlin/com/api/nft/service/dto/ListingResponse.kt +++ b/src/main/kotlin/com/api/nft/service/dto/ListingResponse.kt @@ -1,5 +1,6 @@ package com.api.nft.service.dto +import com.api.nft.enums.StatusType import com.api.nft.enums.TokenType import java.math.BigDecimal @@ -9,7 +10,7 @@ data class ListingResponse( val address: String, val createdDateTime: Long, val endDateTime: Long, - val active: Boolean, + val statusType: StatusType, val price: BigDecimal, val tokenType: TokenType ) \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/util/ChainTypeConvert.kt b/src/main/kotlin/com/api/nft/util/ChainTypeConvert.kt index 726dd3c..2abbd0d 100644 --- a/src/main/kotlin/com/api/nft/util/ChainTypeConvert.kt +++ b/src/main/kotlin/com/api/nft/util/ChainTypeConvert.kt @@ -2,9 +2,11 @@ package com.api.nft.util import com.api.nft.enums.ChainType import com.api.nft.enums.ContractType +import com.api.nft.enums.StatusType import com.api.nft.enums.TokenType import org.springframework.data.r2dbc.convert.EnumWriteSupport data class ContractTypeConverter>(private val enumType: Class) : EnumWriteSupport() data class ChainTypeConvert>(private val enumType: Class): EnumWriteSupport() data class TokenTypeConvert>(private val enumType: Class): EnumWriteSupport() +data class StatusTypeConvert>(private val enumType: Class): EnumWriteSupport() diff --git a/src/main/resources/db/postgresql/migration/V1__Initial_schema.sql b/src/main/resources/db/postgresql/migration/V1__Initial_schema.sql index c02c85d..f5fcf39 100644 --- a/src/main/resources/db/postgresql/migration/V1__Initial_schema.sql +++ b/src/main/resources/db/postgresql/migration/V1__Initial_schema.sql @@ -21,6 +21,12 @@ CREATE TYPE token_type AS ENUM ( ); +CREATE TYPE status_type AS ENUM ( + 'RESERVATION', + 'LISTING' + ); + + CREATE TABLE IF NOT EXISTS collection ( name varchar(500) PRIMARY KEY, @@ -72,7 +78,10 @@ CREATE TABLE IF NOT EXISTS nft_listing ( id SERIAL PRIMARY KEY, nft_id BIGINT REFERENCES nft(id), price DECIMAL(19, 4) NOT NULL, - token_type token_type + token_type token_type, + status_type status_type not null, + created_date BIGINT, + end_date BIGINT ); From 09b82bbac85ee8983e4213e00fb222f0d48660ab Mon Sep 17 00:00:00 2001 From: Donghyeon Im Date: Thu, 25 Jul 2024 12:24:07 +0900 Subject: [PATCH 33/38] feat: test cicd --- .github/workflows/cicd.yaml | 201 ++++++++++++++++++++++++++++++++++++ Dockerfile | 9 ++ 2 files changed, 210 insertions(+) create mode 100644 .github/workflows/cicd.yaml create mode 100644 Dockerfile diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml new file mode 100644 index 0000000..f9b39bd --- /dev/null +++ b/.github/workflows/cicd.yaml @@ -0,0 +1,201 @@ +name: Create and publish a container image, update helm chart 'appVersion' + +on: + push: + branches: ["main", "develop", "feat/cicd"] + +############################################# +# +# Branch +# - develop > GitHub packages +# - main > Amazon ECR +# +############################################# + +jobs: + develop: + ### Reference + # https://docs.github.com/ko/actions/publishing-packages/publishing-docker-images#github-packages%EC%97%90-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EA%B2%8C%EC%8B%9C + ### + + # if: github.ref == 'refs/heads/develop' + if: github.ref == 'refs/heads/feat/cicd' + name: Build and Push Container Image to GitHub Container Registry + runs-on: ubuntu-latest + env: + REPOSITORY: auth + ENVIRONMENT: dev + permissions: + contents: read + packages: write + attestations: write + id-token: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Log in to the GitHub container registry + uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata (tags, labels) for Container image + id: meta + uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7 + with: + images: ghcr.io/${{ github.repository }} + tags: type=sha + + - name: Set up JDK 21 + uses: actions/setup-java@v2 + with: + distribution: 'adopt' + java-version: '21' + + - name: Build JAR + run: ./gradlew clean build -x test + + - name: Build and push Docker image + id: push + uses: docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4 + with: + context: . + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + + # ### Error + # # [message] Failed to persist attestation + # # Feature not available for the NTF-marketplace organization. + # # To enable this feature, please upgrade the billing plan, or make this repository public. + # # https://docs.github.com/rest/repos/repos#create-an-attestation + # ### + # - name: Generate artifact attestation + # uses: actions/attest-build-provenance@v1 + # with: + # subject-name: ${{ env.REGISTRY }}/${{ github.repository }} + # subject-digest: ${{ steps.push.outputs.digest }} + # push-to-registry: true + + - name: Checkout Private Repository + uses: actions/checkout@v4 + with: + repository: NTF-marketplace/devops + fetch-depth: 0 + ref: develop + token: ${{ secrets.PAT }} + + - name: Replace image tag in helm values.yaml + uses: mikefarah/yq@master + env: + IMAGE_VERSION: ${{ steps.meta.outputs.version }} + with: + cmd: yq eval -i '.image.tag = env(IMAGE_VERSION)' 'chart/${{ env.REPOSITORY }}_${{ env.ENVIRONMENT }}/values.yaml' + + - name: Commit helm chart changes + env: + IMAGE_VERSION: ${{ steps.meta.outputs.version }} + run: | + cd chart/${{ env.REPOSITORY }}_${{ env.ENVIRONMENT }} + git config --global user.email "hun5879@naver.com" + git config --global user.name "dongdorrong" + + git add values.yaml + git commit --message "ci: update ${{ env.REPOSITORY }}_${{ env.ENVIRONMENT }} image tag to $IMAGE_VERSION" + + - name: Push commit + uses: ad-m/github-push-action@master + with: + github_token: ${{ secrets.PAT }} + repository: NTF-marketplace/devops + branch: develop + + main: + # if: github.ref == 'refs/heads/main' + if: github.ref == 'refs/heads/feat/cicd' + name: Build and Push Container Image to Amazon ECR + runs-on: ubuntu-latest + env: + REPOSITORY: auth + ENVIRONMENT: prod + permissions: + contents: read + packages: write + attestations: write + id-token: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: ${{ secrets.AWS_DEFAULT_REGION }} + + - name: Login to Amazon ECR + id: login-ecr + uses: aws-actions/amazon-ecr-login@v2 + + - name: Extract metadata (tags, labels) for Container image + id: meta + uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7 + with: + images: ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.${{ secrets.AWS_DEFAULT_REGION }}.amazonaws.com/${{ env.REPOSITORY }}_${{ env.ENVIRONMENT }} + tags: type=sha + + - name: Set up JDK 21 + uses: actions/setup-java@v2 + with: + distribution: 'adopt' + java-version: '21' + + - name: Build JAR + run: ./gradlew clean build -x test + + - name: Build and push Docker image + id: push + uses: docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4 + with: + context: . + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + + - name: Checkout Private Repository + uses: actions/checkout@v4 + with: + repository: NTF-marketplace/devops + fetch-depth: 0 + ref: develop + token: ${{ secrets.PAT }} + + - name: Replace image tag in helm values.yaml + uses: mikefarah/yq@master + env: + IMAGE_VERSION: ${{ steps.meta.outputs.version }} + with: + cmd: yq eval -i '.image.tag = env(IMAGE_VERSION)' 'chart/${{ env.REPOSITORY }}_${{ env.ENVIRONMENT }}/values.yaml' + + - name: Commit helm chart changes + env: + IMAGE_VERSION: ${{ steps.meta.outputs.version }} + run: | + cd chart/${{ env.REPOSITORY }}_${{ env.ENVIRONMENT }} + git config --global user.email "hun5879@naver.com" + git config --global user.name "dongdorrong" + + git add values.yaml + git commit --message "ci: update ${{ env.REPOSITORY }}_${{ env.ENVIRONMENT }} image tag to $IMAGE_VERSION" + + - name: Push commit + uses: ad-m/github-push-action@master + with: + github_token: ${{ secrets.PAT }} + repository: NTF-marketplace/devops + branch: develop \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..1d0f2a8 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,9 @@ +FROM amazoncorretto:21-alpine + +COPY build/libs/*.jar /app.jar + +RUN apk update && apk upgrade && \ + # apk add --no-cache && \ + rm -rf /var/cache/apk/* + +ENTRYPOINT ["java", "-jar", "app.jar"] \ No newline at end of file From 6cfc499f0b3345393ebe39abb38ea7e842015733 Mon Sep 17 00:00:00 2001 From: Donghyeon Im Date: Thu, 25 Jul 2024 12:50:13 +0900 Subject: [PATCH 34/38] fix: test cicd --- .github/workflows/cicd.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml index f9b39bd..f9f958b 100644 --- a/.github/workflows/cicd.yaml +++ b/.github/workflows/cicd.yaml @@ -23,7 +23,7 @@ jobs: name: Build and Push Container Image to GitHub Container Registry runs-on: ubuntu-latest env: - REPOSITORY: auth + REPOSITORY: nft ENVIRONMENT: dev permissions: contents: read @@ -119,7 +119,7 @@ jobs: name: Build and Push Container Image to Amazon ECR runs-on: ubuntu-latest env: - REPOSITORY: auth + REPOSITORY: nft ENVIRONMENT: prod permissions: contents: read From 8a29b1852314b3f5d03de6055b00d7cec0395867 Mon Sep 17 00:00:00 2001 From: Donghyeon Im Date: Thu, 25 Jul 2024 12:54:33 +0900 Subject: [PATCH 35/38] fix: organize cicd --- .github/workflows/cicd.yaml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml index f9f958b..c136146 100644 --- a/.github/workflows/cicd.yaml +++ b/.github/workflows/cicd.yaml @@ -2,7 +2,7 @@ name: Create and publish a container image, update helm chart 'appVersion' on: push: - branches: ["main", "develop", "feat/cicd"] + branches: ["main", "develop"] ############################################# # @@ -18,8 +18,7 @@ jobs: # https://docs.github.com/ko/actions/publishing-packages/publishing-docker-images#github-packages%EC%97%90-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EA%B2%8C%EC%8B%9C ### - # if: github.ref == 'refs/heads/develop' - if: github.ref == 'refs/heads/feat/cicd' + if: github.ref == 'refs/heads/develop' name: Build and Push Container Image to GitHub Container Registry runs-on: ubuntu-latest env: @@ -114,8 +113,7 @@ jobs: branch: develop main: - # if: github.ref == 'refs/heads/main' - if: github.ref == 'refs/heads/feat/cicd' + if: github.ref == 'refs/heads/main' name: Build and Push Container Image to Amazon ECR runs-on: ubuntu-latest env: From cc747bfc5e343c5d5cb5504efff1357a5772bba4 Mon Sep 17 00:00:00 2001 From: min-96 Date: Sun, 28 Jul 2024 23:30:18 +0900 Subject: [PATCH 36/38] add auctionExchange --- .../kotlin/com/api/nft/config/RabbitConfig.kt | 4 +- .../com/api/nft/domain/nft/NftAuction.kt | 19 ++++++++ .../nft/repository/NftAuctionRepository.kt | 17 +++++++ src/main/kotlin/com/api/nft/enums/Enums.kt | 2 +- .../com/api/nft/rabbitMQ/RabbitMQReceiver.kt | 17 ++++--- .../api/nft/service/api/NftAuctionService.kt | 48 +++++++++++++++++++ .../api/nft/service/api/NftListingService.kt | 7 ++- .../api/nft/service/dto/AuctionResponse.kt | 16 +++++++ .../migration/V1__Initial_schema.sql | 14 +++++- 9 files changed, 129 insertions(+), 15 deletions(-) create mode 100644 src/main/kotlin/com/api/nft/domain/nft/NftAuction.kt create mode 100644 src/main/kotlin/com/api/nft/domain/nft/repository/NftAuctionRepository.kt create mode 100644 src/main/kotlin/com/api/nft/service/api/NftAuctionService.kt create mode 100644 src/main/kotlin/com/api/nft/service/dto/AuctionResponse.kt diff --git a/src/main/kotlin/com/api/nft/config/RabbitConfig.kt b/src/main/kotlin/com/api/nft/config/RabbitConfig.kt index ebcd01b..ef5b415 100644 --- a/src/main/kotlin/com/api/nft/config/RabbitConfig.kt +++ b/src/main/kotlin/com/api/nft/config/RabbitConfig.kt @@ -35,6 +35,6 @@ class RabbitConfig { fun listingExchange() = createFanoutExchange("listingExchange") - // @Bean - // fun listingCancelExchange() = createFanoutExchange("listingCancelExchange") + @Bean + fun auctionExchange() = createFanoutExchange("auctionExchange") } \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/domain/nft/NftAuction.kt b/src/main/kotlin/com/api/nft/domain/nft/NftAuction.kt new file mode 100644 index 0000000..d8eaa47 --- /dev/null +++ b/src/main/kotlin/com/api/nft/domain/nft/NftAuction.kt @@ -0,0 +1,19 @@ +package com.api.nft.domain.nft + +import com.api.nft.enums.StatusType +import com.api.nft.enums.TokenType +import org.springframework.data.relational.core.mapping.Table +import java.math.BigDecimal + +@Table("nft_auction") +data class NftAuction( + val id: Long? = null, + val startingPrice: BigDecimal, + val tokenType: TokenType, + val nftId: Long, + val statusType: StatusType, + val createdDate: Long, + val endDate: Long, + +) { +} \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/domain/nft/repository/NftAuctionRepository.kt b/src/main/kotlin/com/api/nft/domain/nft/repository/NftAuctionRepository.kt new file mode 100644 index 0000000..6545d32 --- /dev/null +++ b/src/main/kotlin/com/api/nft/domain/nft/repository/NftAuctionRepository.kt @@ -0,0 +1,17 @@ +package com.api.nft.domain.nft.repository + +import com.api.nft.domain.nft.NftAuction +import com.api.nft.enums.StatusType +import org.springframework.data.r2dbc.repository.Query +import org.springframework.data.r2dbc.repository.R2dbcRepository +import reactor.core.publisher.Mono + +interface NftAuctionRepository : R2dbcRepository { + fun findByNftId(nftId: Long) : Mono + + @Query("UPDATE nft_auction SET status_type = :statusType WHERE nft_id = :nftId") + fun updateAuction(nftId: Long, statusType: StatusType): Mono + + fun deleteByNftId(nftId: Long): Mono + +} \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/enums/Enums.kt b/src/main/kotlin/com/api/nft/enums/Enums.kt index 4574ecd..7dde924 100644 --- a/src/main/kotlin/com/api/nft/enums/Enums.kt +++ b/src/main/kotlin/com/api/nft/enums/Enums.kt @@ -25,4 +25,4 @@ enum class TokenType { SAND, MATIC, ETH, BTC } -enum class StatusType { RESERVATION, LISTING, RESERVATION_CANCEL, CANCEL, EXPIRED } \ No newline at end of file +enum class StatusType { RESERVATION, ACTIVED, RESERVATION_CANCEL, CANCEL, EXPIRED,LISTING, AUCTION } \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/rabbitMQ/RabbitMQReceiver.kt b/src/main/kotlin/com/api/nft/rabbitMQ/RabbitMQReceiver.kt index 7853e6d..3dc84bf 100644 --- a/src/main/kotlin/com/api/nft/rabbitMQ/RabbitMQReceiver.kt +++ b/src/main/kotlin/com/api/nft/rabbitMQ/RabbitMQReceiver.kt @@ -1,5 +1,6 @@ package com.api.nft.rabbitMQ +import com.api.nft.service.api.NftAuctionService import com.api.nft.service.api.NftListingService import com.api.nft.service.dto.ListingResponse import org.springframework.amqp.core.ExchangeTypes @@ -8,10 +9,12 @@ import org.springframework.amqp.rabbit.annotation.Queue import org.springframework.amqp.rabbit.annotation.QueueBinding import org.springframework.amqp.rabbit.annotation.RabbitListener import org.springframework.stereotype.Service +import com.api.nft.service.dto.AuctionResponse as AuctionResponse @Service class RabbitMQReceiver( private val nftListingService: NftListingService, + private val nftAuctionService: NftAuctionService ) { @@ -23,11 +26,11 @@ class RabbitMQReceiver( nftListingService.update(listing).subscribe() } - // @RabbitListener(bindings = [QueueBinding( - // value = Queue(name = "", durable = "false", exclusive = "true", autoDelete = "true"), - // exchange = Exchange(value = "listingCancelExchange", type = ExchangeTypes.FANOUT) - // )]) - // fun listingCancelMessage(listing: ListingResponse){ - // nftListingService.update(listing).subscribe() - // } + @RabbitListener(bindings = [QueueBinding( + value = Queue(name = "", durable = "false", exclusive = "true", autoDelete = "true"), + exchange = Exchange(value = "auctionExchange", type = ExchangeTypes.FANOUT) + )]) + fun auctionMessage(auction: AuctionResponse){ + nftAuctionService.update(auction).subscribe() + } } \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/service/api/NftAuctionService.kt b/src/main/kotlin/com/api/nft/service/api/NftAuctionService.kt new file mode 100644 index 0000000..ef214c8 --- /dev/null +++ b/src/main/kotlin/com/api/nft/service/api/NftAuctionService.kt @@ -0,0 +1,48 @@ +package com.api.nft.service.api + +import com.api.nft.domain.nft.NftAuction +import com.api.nft.domain.nft.repository.NftAuctionRepository +import com.api.nft.enums.StatusType +import com.api.nft.service.dto.AuctionResponse +import org.springframework.stereotype.Service +import reactor.core.publisher.Mono + +@Service +class NftAuctionService( + private val nftAuctionRepository: NftAuctionRepository, +) { + + fun update(newAuction: AuctionResponse): Mono { + return when (newAuction.statusType) { + StatusType.RESERVATION -> { + save(newAuction) + } + StatusType.ACTIVED -> { + nftAuctionRepository.findByNftId(newAuction.nftId) + .flatMap { + nftAuctionRepository.updateAuction(nftId = newAuction.nftId, statusType = StatusType.AUCTION) + } + } + StatusType.RESERVATION_CANCEL, StatusType.CANCEL, StatusType.EXPIRED -> { + nftAuctionRepository.findByNftId(newAuction.nftId) + .flatMap { nftAuction -> + nftAuctionRepository.deleteByNftId(nftAuction.nftId) + } + + } + else -> Mono.empty() + } + } + fun save(auction: AuctionResponse) : Mono { + return nftAuctionRepository.save( + NftAuction( + nftId = auction.nftId, + startingPrice = auction.startingPrice, + tokenType = auction.tokenType, + statusType = auction.statusType, + createdDate = auction.createdDateTime, + endDate = auction.endDateTime, + ) + ).then() + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/api/nft/service/api/NftListingService.kt b/src/main/kotlin/com/api/nft/service/api/NftListingService.kt index 28f621e..3184e30 100644 --- a/src/main/kotlin/com/api/nft/service/api/NftListingService.kt +++ b/src/main/kotlin/com/api/nft/service/api/NftListingService.kt @@ -20,13 +20,11 @@ class NftListingService( save(newListing) .then(redisService.updateToRedis(newListing.nftId)) } - StatusType.LISTING -> { - println("update type LISTING") + StatusType.ACTIVED -> { nftListingRepository.findByNftId(newListing.nftId) .flatMap { nftListing -> - println("id : " + newListing.nftId) println("statusType : " + newListing.statusType) - nftListingRepository.updateListing(nftId = nftListing.nftId, statusType = newListing.statusType) + nftListingRepository.updateListing(nftId = nftListing.nftId, statusType = StatusType.LISTING) } .then(redisService.updateToRedis(newListing.nftId)) } @@ -37,6 +35,7 @@ class NftListingService( } .then(redisService.updateToRedis(newListing.nftId)) } + else -> Mono.empty() } } fun save(listing: ListingResponse) : Mono { diff --git a/src/main/kotlin/com/api/nft/service/dto/AuctionResponse.kt b/src/main/kotlin/com/api/nft/service/dto/AuctionResponse.kt new file mode 100644 index 0000000..5023096 --- /dev/null +++ b/src/main/kotlin/com/api/nft/service/dto/AuctionResponse.kt @@ -0,0 +1,16 @@ +package com.api.nft.service.dto + +import com.api.nft.enums.StatusType +import com.api.nft.enums.TokenType +import java.math.BigDecimal + +data class AuctionResponse( + val id : Long, + val nftId : Long, + val address: String, + val createdDateTime: Long, + val endDateTime: Long, + val statusType: StatusType, + val startingPrice: BigDecimal, + val tokenType: TokenType +) diff --git a/src/main/resources/db/postgresql/migration/V1__Initial_schema.sql b/src/main/resources/db/postgresql/migration/V1__Initial_schema.sql index f5fcf39..f48d9f2 100644 --- a/src/main/resources/db/postgresql/migration/V1__Initial_schema.sql +++ b/src/main/resources/db/postgresql/migration/V1__Initial_schema.sql @@ -23,7 +23,8 @@ CREATE TYPE token_type AS ENUM ( CREATE TYPE status_type AS ENUM ( 'RESERVATION', - 'LISTING' + 'LISTING', + 'AUCTION' ); @@ -85,6 +86,17 @@ CREATE TABLE IF NOT EXISTS nft_listing ( ); +CREATE TABLE IF NOT EXISTS nft_auction ( + id SERIAL PRIMARY KEY, + nft_id BIGINT REFERENCES nft(id), + starting_price DECIMAL(19, 4) NOT NULL, + token_type token_type, + status_type status_type not null, + created_date BIGINT, + end_date BIGINT +); + + From aac22d700c3b764b981714fe2e70cdd82067fbd7 Mon Sep 17 00:00:00 2001 From: min-96 Date: Mon, 29 Jul 2024 02:13:07 +0900 Subject: [PATCH 37/38] fix: add id --- src/main/kotlin/com/api/nft/domain/nft/NftAuction.kt | 2 +- src/main/kotlin/com/api/nft/domain/nft/NftListing.kt | 2 +- .../com/api/nft/service/api/NftAuctionService.kt | 1 + .../com/api/nft/service/api/NftListingService.kt | 1 + src/main/kotlin/com/api/nft/service/api/NftService.kt | 10 ++++++---- .../db/postgresql/migration/V1__Initial_schema.sql | 4 ++-- 6 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/main/kotlin/com/api/nft/domain/nft/NftAuction.kt b/src/main/kotlin/com/api/nft/domain/nft/NftAuction.kt index d8eaa47..896863e 100644 --- a/src/main/kotlin/com/api/nft/domain/nft/NftAuction.kt +++ b/src/main/kotlin/com/api/nft/domain/nft/NftAuction.kt @@ -7,7 +7,7 @@ import java.math.BigDecimal @Table("nft_auction") data class NftAuction( - val id: Long? = null, + val id: Long, val startingPrice: BigDecimal, val tokenType: TokenType, val nftId: Long, diff --git a/src/main/kotlin/com/api/nft/domain/nft/NftListing.kt b/src/main/kotlin/com/api/nft/domain/nft/NftListing.kt index 2952c59..434ed41 100644 --- a/src/main/kotlin/com/api/nft/domain/nft/NftListing.kt +++ b/src/main/kotlin/com/api/nft/domain/nft/NftListing.kt @@ -7,7 +7,7 @@ import java.math.BigDecimal @Table("nft_listing") data class NftListing( - val id: Long? = null, + val id: Long, val price: BigDecimal, val tokenType: TokenType, val nftId: Long, diff --git a/src/main/kotlin/com/api/nft/service/api/NftAuctionService.kt b/src/main/kotlin/com/api/nft/service/api/NftAuctionService.kt index ef214c8..6eec070 100644 --- a/src/main/kotlin/com/api/nft/service/api/NftAuctionService.kt +++ b/src/main/kotlin/com/api/nft/service/api/NftAuctionService.kt @@ -36,6 +36,7 @@ class NftAuctionService( fun save(auction: AuctionResponse) : Mono { return nftAuctionRepository.save( NftAuction( + id = auction.id, nftId = auction.nftId, startingPrice = auction.startingPrice, tokenType = auction.tokenType, diff --git a/src/main/kotlin/com/api/nft/service/api/NftListingService.kt b/src/main/kotlin/com/api/nft/service/api/NftListingService.kt index 3184e30..f162055 100644 --- a/src/main/kotlin/com/api/nft/service/api/NftListingService.kt +++ b/src/main/kotlin/com/api/nft/service/api/NftListingService.kt @@ -41,6 +41,7 @@ class NftListingService( fun save(listing: ListingResponse) : Mono { return nftListingRepository.save( NftListing( + id = listing.id, nftId = listing.nftId, price = listing.price, tokenType = listing.tokenType, diff --git a/src/main/kotlin/com/api/nft/service/api/NftService.kt b/src/main/kotlin/com/api/nft/service/api/NftService.kt index 53d0226..4f21745 100644 --- a/src/main/kotlin/com/api/nft/service/api/NftService.kt +++ b/src/main/kotlin/com/api/nft/service/api/NftService.kt @@ -31,15 +31,17 @@ class NftService( fun findAllById(ids: List): Flux { return nftRepository.findAllByNftJoinMetadata(ids) - .doOnNext { nft -> - redisService.updateToRedis(nft.id!!).subscribe() + .flatMap { nft -> + redisService.updateToRedis(nft.id) + .thenReturn(nft) } } fun findById(id: Long): Mono { return nftRepository.findByNftJoinMetadata(id) - .doOnNext { nft -> - redisService.updateToRedis(nft.id!!).subscribe() + .flatMap { nft -> + redisService.updateToRedis(nft.id) + .thenReturn(nft) } } diff --git a/src/main/resources/db/postgresql/migration/V1__Initial_schema.sql b/src/main/resources/db/postgresql/migration/V1__Initial_schema.sql index f48d9f2..3167f7c 100644 --- a/src/main/resources/db/postgresql/migration/V1__Initial_schema.sql +++ b/src/main/resources/db/postgresql/migration/V1__Initial_schema.sql @@ -76,7 +76,7 @@ CREATE TABLE IF NOT EXISTS transfer ( ); CREATE TABLE IF NOT EXISTS nft_listing ( - id SERIAL PRIMARY KEY, + id BIGINT PRIMARY KEY, nft_id BIGINT REFERENCES nft(id), price DECIMAL(19, 4) NOT NULL, token_type token_type, @@ -87,7 +87,7 @@ CREATE TABLE IF NOT EXISTS nft_listing ( CREATE TABLE IF NOT EXISTS nft_auction ( - id SERIAL PRIMARY KEY, + id BIGINT PRIMARY KEY, nft_id BIGINT REFERENCES nft(id), starting_price DECIMAL(19, 4) NOT NULL, token_type token_type, From aada7f090ffe060e6cc8ede3f0f374e4dbcb51a1 Mon Sep 17 00:00:00 2001 From: min-96 Date: Tue, 30 Jul 2024 00:57:04 +0900 Subject: [PATCH 38/38] fix: tokenType to chainType --- .../nft/controller/dto/NftMetadataResponse.kt | 3 -- .../com/api/nft/domain/nft/NftAuction.kt | 4 +- .../com/api/nft/domain/nft/NftListing.kt | 3 +- .../nft/repository/NftAuctionRepository.kt | 4 +- .../nft/repository/NftListingRepository.kt | 3 +- .../repository/NftRepositorySupportImpl.kt | 50 +++++++++---------- .../api/nft/service/api/NftAuctionService.kt | 2 +- .../api/nft/service/api/NftListingService.kt | 3 +- .../api/nft/service/dto/AuctionResponse.kt | 4 +- .../api/nft/service/dto/ListingResponse.kt | 4 +- .../migration/V1__Initial_schema.sql | 4 +- 11 files changed, 39 insertions(+), 45 deletions(-) diff --git a/src/main/kotlin/com/api/nft/controller/dto/NftMetadataResponse.kt b/src/main/kotlin/com/api/nft/controller/dto/NftMetadataResponse.kt index 769506d..42be222 100644 --- a/src/main/kotlin/com/api/nft/controller/dto/NftMetadataResponse.kt +++ b/src/main/kotlin/com/api/nft/controller/dto/NftMetadataResponse.kt @@ -2,8 +2,6 @@ package com.api.nft.controller.dto import com.api.nft.enums.ChainType import com.api.nft.enums.ContractType -import com.api.nft.enums.TokenType -import java.math.BigDecimal data class NftMetadataResponse( @@ -16,5 +14,4 @@ data class NftMetadataResponse( val collectionName: String, val image: String, val lastPrice: Double?, - val tokenType: TokenType?, ) diff --git a/src/main/kotlin/com/api/nft/domain/nft/NftAuction.kt b/src/main/kotlin/com/api/nft/domain/nft/NftAuction.kt index 896863e..f91b032 100644 --- a/src/main/kotlin/com/api/nft/domain/nft/NftAuction.kt +++ b/src/main/kotlin/com/api/nft/domain/nft/NftAuction.kt @@ -1,7 +1,7 @@ package com.api.nft.domain.nft +import com.api.nft.enums.ChainType import com.api.nft.enums.StatusType -import com.api.nft.enums.TokenType import org.springframework.data.relational.core.mapping.Table import java.math.BigDecimal @@ -9,7 +9,7 @@ import java.math.BigDecimal data class NftAuction( val id: Long, val startingPrice: BigDecimal, - val tokenType: TokenType, + val chainType: ChainType, val nftId: Long, val statusType: StatusType, val createdDate: Long, diff --git a/src/main/kotlin/com/api/nft/domain/nft/NftListing.kt b/src/main/kotlin/com/api/nft/domain/nft/NftListing.kt index 434ed41..fb4f214 100644 --- a/src/main/kotlin/com/api/nft/domain/nft/NftListing.kt +++ b/src/main/kotlin/com/api/nft/domain/nft/NftListing.kt @@ -1,5 +1,6 @@ package com.api.nft.domain.nft +import com.api.nft.enums.ChainType import com.api.nft.enums.StatusType import com.api.nft.enums.TokenType import org.springframework.data.relational.core.mapping.Table @@ -9,7 +10,7 @@ import java.math.BigDecimal data class NftListing( val id: Long, val price: BigDecimal, - val tokenType: TokenType, + val chainType: ChainType, val nftId: Long, val statusType: StatusType, val createdDate: Long, diff --git a/src/main/kotlin/com/api/nft/domain/nft/repository/NftAuctionRepository.kt b/src/main/kotlin/com/api/nft/domain/nft/repository/NftAuctionRepository.kt index 6545d32..f8f9606 100644 --- a/src/main/kotlin/com/api/nft/domain/nft/repository/NftAuctionRepository.kt +++ b/src/main/kotlin/com/api/nft/domain/nft/repository/NftAuctionRepository.kt @@ -3,10 +3,10 @@ package com.api.nft.domain.nft.repository import com.api.nft.domain.nft.NftAuction import com.api.nft.enums.StatusType import org.springframework.data.r2dbc.repository.Query -import org.springframework.data.r2dbc.repository.R2dbcRepository +import org.springframework.data.repository.reactive.ReactiveCrudRepository import reactor.core.publisher.Mono -interface NftAuctionRepository : R2dbcRepository { +interface NftAuctionRepository : ReactiveCrudRepository { fun findByNftId(nftId: Long) : Mono @Query("UPDATE nft_auction SET status_type = :statusType WHERE nft_id = :nftId") diff --git a/src/main/kotlin/com/api/nft/domain/nft/repository/NftListingRepository.kt b/src/main/kotlin/com/api/nft/domain/nft/repository/NftListingRepository.kt index f79eefd..45bbfb7 100644 --- a/src/main/kotlin/com/api/nft/domain/nft/repository/NftListingRepository.kt +++ b/src/main/kotlin/com/api/nft/domain/nft/repository/NftListingRepository.kt @@ -5,10 +5,11 @@ import com.api.nft.enums.StatusType import com.api.nft.enums.TokenType import org.springframework.data.r2dbc.repository.Query import org.springframework.data.r2dbc.repository.R2dbcRepository +import org.springframework.data.repository.reactive.ReactiveCrudRepository import reactor.core.publisher.Mono import java.math.BigDecimal -interface NftListingRepository : R2dbcRepository { +interface NftListingRepository : ReactiveCrudRepository { fun findByNftId(nftId: Long) : Mono diff --git a/src/main/kotlin/com/api/nft/domain/nft/repository/NftRepositorySupportImpl.kt b/src/main/kotlin/com/api/nft/domain/nft/repository/NftRepositorySupportImpl.kt index fd07131..da84206 100644 --- a/src/main/kotlin/com/api/nft/domain/nft/repository/NftRepositorySupportImpl.kt +++ b/src/main/kotlin/com/api/nft/domain/nft/repository/NftRepositorySupportImpl.kt @@ -3,7 +3,6 @@ package com.api.nft.domain.nft.repository import com.api.nft.controller.dto.NftMetadataResponse import com.api.nft.enums.ChainType import com.api.nft.enums.ContractType -import com.api.nft.enums.TokenType import org.springframework.data.r2dbc.core.R2dbcEntityTemplate import reactor.core.publisher.Flux import reactor.core.publisher.Mono @@ -13,27 +12,27 @@ class NftRepositorySupportImpl( private val r2dbcEntityTemplate: R2dbcEntityTemplate ): NftRepositorySupport { override fun findByNftJoinMetadata(id: Long): Mono { - val query = - """ - SELECT - n.id, - n.token_id AS tokenId, - n.token_address AS tokenAddress, - n.chain_type AS chainType, - n.nft_name AS nftName, - n.collection_name AS collectionName, - n.contract_type AS contractType, - m.image AS image, - nl.price AS lastPrice, - nl.token_type AS tokenType - FROM - nft n - LEFT JOIN - metadata m ON n.id = m.nft_id - LEFT JOIN nft_listing nl ON n.id = nl.nft_id - WHERE - n.id = :$1 - """ + val query = """ + SELECT + n.id, + n.token_id AS tokenId, + n.token_address AS tokenAddress, + n.chain_type AS chainType, + n.nft_name AS nftName, + n.collection_name AS collectionName, + n.contract_type AS contractType, + m.image AS image, + nl.price AS lastPrice + FROM + nft n + LEFT JOIN + metadata m ON n.id = m.nft_id + LEFT JOIN + nft_listing nl ON n.id = nl.nft_id + WHERE + n.id = :$1 + """ + return r2dbcEntityTemplate.databaseClient.sql(query) .bind(0, id) .map { row, data -> @@ -47,7 +46,6 @@ class NftRepositorySupportImpl( image = row.get("image", String::class.java) ?: "", contractType = row.get("contractType", ContractType::class.java)!!, lastPrice = row.get("lastPrice", BigDecimal::class.java)?.toDouble(), - tokenType = row.get("tokenType", TokenType::class.java) ) }.first() } @@ -63,12 +61,11 @@ class NftRepositorySupportImpl( n.collection_name AS collectionName, n.contract_type AS contractType, m.image AS image - nl.token_type AS tokenType FROM nft n - JOIN + LEFT JOIN metadata m ON n.id = m.nft_id - JOIN nft_listing nl ON nft.id = nl.nft_id + LEFT JOIN nft_listing nl ON nft.id = nl.nft_id WHERE n.id IN (:$1) """ @@ -85,7 +82,6 @@ class NftRepositorySupportImpl( image = row.get("image", String::class.java) ?: "", contractType = row.get("contractType", ContractType::class.java)!!, lastPrice = row.get("lastPrice", BigDecimal::class.java)?.toDouble(), - tokenType = row.get("tokenType", TokenType::class.java), ) } .all() diff --git a/src/main/kotlin/com/api/nft/service/api/NftAuctionService.kt b/src/main/kotlin/com/api/nft/service/api/NftAuctionService.kt index 6eec070..78dae6d 100644 --- a/src/main/kotlin/com/api/nft/service/api/NftAuctionService.kt +++ b/src/main/kotlin/com/api/nft/service/api/NftAuctionService.kt @@ -39,7 +39,7 @@ class NftAuctionService( id = auction.id, nftId = auction.nftId, startingPrice = auction.startingPrice, - tokenType = auction.tokenType, + chainType = auction.chainType, statusType = auction.statusType, createdDate = auction.createdDateTime, endDate = auction.endDateTime, diff --git a/src/main/kotlin/com/api/nft/service/api/NftListingService.kt b/src/main/kotlin/com/api/nft/service/api/NftListingService.kt index f162055..d40058c 100644 --- a/src/main/kotlin/com/api/nft/service/api/NftListingService.kt +++ b/src/main/kotlin/com/api/nft/service/api/NftListingService.kt @@ -23,7 +23,6 @@ class NftListingService( StatusType.ACTIVED -> { nftListingRepository.findByNftId(newListing.nftId) .flatMap { nftListing -> - println("statusType : " + newListing.statusType) nftListingRepository.updateListing(nftId = nftListing.nftId, statusType = StatusType.LISTING) } .then(redisService.updateToRedis(newListing.nftId)) @@ -44,7 +43,7 @@ class NftListingService( id = listing.id, nftId = listing.nftId, price = listing.price, - tokenType = listing.tokenType, + chainType = listing.chainType, statusType = listing.statusType, createdDate = listing.createdDateTime, endDate = listing.endDateTime, diff --git a/src/main/kotlin/com/api/nft/service/dto/AuctionResponse.kt b/src/main/kotlin/com/api/nft/service/dto/AuctionResponse.kt index 5023096..b133eb0 100644 --- a/src/main/kotlin/com/api/nft/service/dto/AuctionResponse.kt +++ b/src/main/kotlin/com/api/nft/service/dto/AuctionResponse.kt @@ -1,7 +1,7 @@ package com.api.nft.service.dto +import com.api.nft.enums.ChainType import com.api.nft.enums.StatusType -import com.api.nft.enums.TokenType import java.math.BigDecimal data class AuctionResponse( @@ -12,5 +12,5 @@ data class AuctionResponse( val endDateTime: Long, val statusType: StatusType, val startingPrice: BigDecimal, - val tokenType: TokenType + val chainType: ChainType ) diff --git a/src/main/kotlin/com/api/nft/service/dto/ListingResponse.kt b/src/main/kotlin/com/api/nft/service/dto/ListingResponse.kt index ec01a73..4cc0def 100644 --- a/src/main/kotlin/com/api/nft/service/dto/ListingResponse.kt +++ b/src/main/kotlin/com/api/nft/service/dto/ListingResponse.kt @@ -1,7 +1,7 @@ package com.api.nft.service.dto +import com.api.nft.enums.ChainType import com.api.nft.enums.StatusType -import com.api.nft.enums.TokenType import java.math.BigDecimal data class ListingResponse( @@ -12,5 +12,5 @@ data class ListingResponse( val endDateTime: Long, val statusType: StatusType, val price: BigDecimal, - val tokenType: TokenType + val chainType: ChainType ) \ No newline at end of file diff --git a/src/main/resources/db/postgresql/migration/V1__Initial_schema.sql b/src/main/resources/db/postgresql/migration/V1__Initial_schema.sql index 3167f7c..bb8cbbc 100644 --- a/src/main/resources/db/postgresql/migration/V1__Initial_schema.sql +++ b/src/main/resources/db/postgresql/migration/V1__Initial_schema.sql @@ -79,7 +79,7 @@ CREATE TABLE IF NOT EXISTS nft_listing ( id BIGINT PRIMARY KEY, nft_id BIGINT REFERENCES nft(id), price DECIMAL(19, 4) NOT NULL, - token_type token_type, + chain_type chain_type, status_type status_type not null, created_date BIGINT, end_date BIGINT @@ -90,7 +90,7 @@ CREATE TABLE IF NOT EXISTS nft_auction ( id BIGINT PRIMARY KEY, nft_id BIGINT REFERENCES nft(id), starting_price DECIMAL(19, 4) NOT NULL, - token_type token_type, + chain_type chain_type, status_type status_type not null, created_date BIGINT, end_date BIGINT