-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
33 changed files
with
1,182 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
# Buy_product | ||
|
||
productId에 해당하는 product를 구매합니다. | ||
|
||
## Request | ||
|
||
### HTTP METHOD : `POST` | ||
|
||
### url : `https://api.gitanimals.org/auctions/products/{product-id}` | ||
|
||
### RequestHeader | ||
|
||
- Authorization: `{token}` | ||
|
||
### RequestBody | ||
|
||
## Response | ||
|
||
200 OK | ||
|
||
```json | ||
{ | ||
"id": "1", | ||
"sellerId": "1", | ||
"persona": { | ||
"personaId": "1", | ||
"personaType": "PENGUIN", | ||
"personaLevel": 1 | ||
}, | ||
"price": "1000", | ||
"paymentState": "SOLD_OUT", | ||
"receipt": { | ||
"buyerId": "1", | ||
"soldAt": "2022-04-29T10:15:30Z" | ||
} | ||
} | ||
``` | ||
|
||
| key | description | | ||
|----------------------|--------------------------------------------------------| | ||
| id | 등록된 상품의 id | | ||
| sellerId | 상품을 판매하는 유저의 id | | ||
| persona.personaId | persona의 id | | ||
| persona.personaType | persona의 type | | ||
| persona.personaLevel | persona의 level | | ||
| price | 등록된 상품의 가격 | | ||
| paymentState | 등록된 상품의 상태 | | ||
| receipt | 결제정보. 등록된 상품이 SOLD_OUT 상태일때만 존재하며, 아니라면, null값이 반환됩니다. | | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
# Register product | ||
|
||
shop에 product를 등록한다. | ||
|
||
## Request | ||
|
||
### HTTP METHOD : `POST` | ||
|
||
### url : `https://api.gitanimals.org/auctions/products` | ||
|
||
### RequestHeader | ||
|
||
- Authorization: `{token}` | ||
|
||
### Request body | ||
|
||
```json | ||
{ | ||
"personaId": "1", | ||
"price": "1000" | ||
} | ||
``` | ||
|
||
| key | description | | ||
|-----------|---------------------------------------------| | ||
| personaId | 등록할 펫의 id 입니다. | | ||
| price | 등록할 펫의 가격입니다. Long 범위(2^64) 안에서 설정할 수 있습니다. | | ||
|
||
# Response | ||
|
||
200 OK | ||
|
||
```json | ||
{ | ||
"id": "1", | ||
"sellerId": "1", | ||
"persona": { | ||
"personaId": "1", | ||
"personaType": "PENGUIN", | ||
"personaLevel": 1 | ||
}, | ||
"price": "1000", | ||
"paymentState": "SOLD_OUT", | ||
"receipt": { | ||
"buyerId": "1", | ||
"soldAt": "2022-04-29T10:15:30Z" | ||
} | ||
} | ||
``` | ||
|
||
| key | description | | ||
|---------------------|--------------------------------------------------------| | ||
| id | 등록된 상품의 id | | ||
| sellerId | 상품을 판매하는 유저의 id | | ||
| persona.personaId | persona의 id | | ||
| persona.personaType | persona의 type | | ||
| persona.personaLevel | persona의 level | | ||
| price | 등록된 상품의 가격 | | ||
| paymentState | 등록된 상품의 상태 | | ||
| receipt | 결제정보. 등록된 상품이 SOLD_OUT 상태일때만 존재하며, 아니라면, null값이 반환됩니다. | |
79 changes: 79 additions & 0 deletions
79
src/main/kotlin/org/gitanimals/auction/app/BuyProductFacade.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
package org.gitanimals.auction.app | ||
|
||
import org.gitanimals.auction.domain.Product | ||
import org.gitanimals.auction.domain.ProductService | ||
import org.rooftop.netx.api.Orchestrator | ||
import org.rooftop.netx.api.OrchestratorFactory | ||
import org.springframework.stereotype.Service | ||
import java.util.* | ||
|
||
@Service | ||
class BuyProductFacade( | ||
private val renderApi: RenderApi, | ||
private val identityApi: IdentityApi, | ||
private val productService: ProductService, | ||
orchestratorFactory: OrchestratorFactory, | ||
) { | ||
|
||
private lateinit var orchestrator: Orchestrator<Long, Product> | ||
|
||
fun buyProduct(token: String, productId: Long): Product { | ||
val buyer = identityApi.getUserByToken(token) | ||
val product = productService.getProductById(productId) | ||
|
||
require(product.price <= buyer.points.toLong()) { | ||
"Cannot buy product cause buyer does not have enough point \"${product.price}\" >= \"${buyer.points}\"" | ||
} | ||
|
||
return orchestrator.sagaSync( | ||
productId, | ||
mapOf( | ||
"token" to token, | ||
"idempotencyKey" to UUID.randomUUID().toString() | ||
) | ||
).decodeResultOrThrow(Product::class) | ||
} | ||
|
||
init { | ||
this.orchestrator = orchestratorFactory.create<Long>("buy product orchestrator") | ||
.startWithContext( | ||
contextOrchestrate = { context, productId -> | ||
val token = context.decodeContext("token", String::class) | ||
val buyer = identityApi.getUserByToken(token) | ||
|
||
productService.buyProduct(productId, buyer.id.toLong()) | ||
}, | ||
contextRollback = { _, productId -> productService.rollbackProduct(productId) } | ||
) | ||
.joinWithContext( | ||
contextOrchestrate = { context, product -> | ||
val token = context.decodeContext("token", String::class) | ||
val idempotencyKey = context.decodeContext("idempotencyKey", String::class) | ||
|
||
identityApi.decreasePoint(token, idempotencyKey, product.price.toString()) | ||
|
||
product | ||
}, | ||
contextRollback = { context, product -> | ||
val token = context.decodeContext("token", String::class) | ||
val idempotencyKey = context.decodeContext("idempotencyKey", String::class) | ||
|
||
identityApi.increasePoint(token, idempotencyKey, product.price.toString()) | ||
} | ||
) | ||
.commitWithContext { context, product -> | ||
val token = context.decodeContext("token", String::class) | ||
val idempotencyKey = context.decodeContext("idempotencyKey", String::class) | ||
|
||
renderApi.addPersona( | ||
token, | ||
idempotencyKey, | ||
product.persona.personaId, | ||
product.persona.personaLevel, | ||
product.persona.personaType, | ||
) | ||
|
||
product | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package org.gitanimals.auction.app | ||
|
||
interface IdentityApi { | ||
|
||
fun getUserByToken(token: String): UserResponse | ||
|
||
fun decreasePoint(token: String, idempotencyKey: String, point: String) | ||
|
||
fun increasePoint(token: String, idempotencyKey: String, point: String) | ||
|
||
data class UserResponse( | ||
val id: String, | ||
val username: String, | ||
val points: String, | ||
) | ||
} |
62 changes: 62 additions & 0 deletions
62
src/main/kotlin/org/gitanimals/auction/app/RegisterProductFacade.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
package org.gitanimals.auction.app | ||
|
||
import org.gitanimals.auction.domain.Product | ||
import org.gitanimals.auction.domain.ProductService | ||
import org.gitanimals.auction.domain.request.RegisterProductRequest | ||
import org.rooftop.netx.api.Orchestrator | ||
import org.rooftop.netx.api.OrchestratorFactory | ||
import org.springframework.stereotype.Service | ||
import java.util.* | ||
|
||
@Service | ||
class RegisterProductFacade( | ||
private val identityApi: IdentityApi, | ||
private val renderApi: RenderApi, | ||
private val productService: ProductService, | ||
orchestratorFactory: OrchestratorFactory, | ||
) { | ||
|
||
private lateinit var orchestrator: Orchestrator<RegisterProductRequest, Product> | ||
|
||
fun registerProduct(token: String, personaId: Long, price: Long): Product { | ||
val seller = identityApi.getUserByToken(token) | ||
val sellProduct = renderApi.getPersonaById(token, personaId) | ||
|
||
val request = RegisterProductRequest( | ||
sellerId = seller.id.toLong(), | ||
personaId = sellProduct.id.toLong(), | ||
personaType = sellProduct.type, | ||
personaLevel = sellProduct.level.toInt(), | ||
price = price, | ||
) | ||
|
||
return orchestrator.sagaSync( | ||
request, | ||
mapOf("token" to token, "idempotencyKey" to UUID.randomUUID().toString()), | ||
).decodeResultOrThrow(Product::class) | ||
} | ||
|
||
init { | ||
this.orchestrator = | ||
orchestratorFactory.create<RegisterProductRequest>("register product orchestrator") | ||
.startWithContext( | ||
contextOrchestrate = { context, request -> | ||
val token = context.decodeContext("token", String::class) | ||
renderApi.deletePersonaById(token, request.personaId) | ||
request | ||
}, | ||
contextRollback = { context, request -> | ||
val token = context.decodeContext("token", String::class) | ||
val idempotencyKey = context.decodeContext("idempotencyKey", String::class) | ||
renderApi.addPersona( | ||
token, | ||
idempotencyKey, | ||
request.personaId, | ||
request.personaLevel, | ||
request.personaType | ||
) | ||
} | ||
) | ||
.commit { productService.registerProduct(it) } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package org.gitanimals.auction.app | ||
|
||
interface RenderApi { | ||
|
||
fun getPersonaById(token: String, personaId: Long): PersonaResponse | ||
|
||
fun deletePersonaById(token: String, personaId: Long) | ||
|
||
fun addPersona( | ||
token: String, | ||
idempotencyKey: String, | ||
personaId: Long, | ||
personaLevel: Int, | ||
personaType: String, | ||
) | ||
|
||
data class PersonaResponse( | ||
val id: String, | ||
val type: String, | ||
val level: String, | ||
) | ||
} |
54 changes: 54 additions & 0 deletions
54
src/main/kotlin/org/gitanimals/auction/controller/AuctionController.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
package org.gitanimals.auction.controller | ||
|
||
import org.gitanimals.auction.app.BuyProductFacade | ||
import org.gitanimals.auction.app.RegisterProductFacade | ||
import org.gitanimals.auction.controller.request.RegisterProductRequest | ||
import org.gitanimals.auction.controller.response.ErrorResponse | ||
import org.gitanimals.auction.controller.response.ProductResponse | ||
import org.springframework.http.HttpHeaders | ||
import org.springframework.http.HttpStatus | ||
import org.springframework.web.bind.annotation.* | ||
|
||
@RestController | ||
class AuctionController( | ||
private val buyProductFacade: BuyProductFacade, | ||
private val registerProductFacade: RegisterProductFacade, | ||
) { | ||
|
||
@ResponseStatus(HttpStatus.OK) | ||
@PostMapping("/auctions/products") | ||
fun registerProducts( | ||
@RequestHeader(HttpHeaders.AUTHORIZATION) token: String, | ||
@RequestBody registerProductRequest: RegisterProductRequest, | ||
): ProductResponse { | ||
val product = registerProductFacade.registerProduct( | ||
token, | ||
registerProductRequest.personaId.toLong(), | ||
registerProductRequest.price.toLong(), | ||
) | ||
|
||
return ProductResponse.from(product) | ||
} | ||
|
||
@ResponseStatus(HttpStatus.OK) | ||
@PostMapping("/auctions/products/{product-id}") | ||
fun buyProducts( | ||
@RequestHeader(HttpHeaders.AUTHORIZATION) token: String, | ||
@PathVariable("product-id") productId: Long, | ||
): ProductResponse { | ||
val product = buyProductFacade.buyProduct(token, productId) | ||
|
||
return ProductResponse.from(product) | ||
} | ||
|
||
@ResponseStatus(HttpStatus.BAD_REQUEST) | ||
@ExceptionHandler(IllegalArgumentException::class) | ||
fun handleIllegalArgumentException(exception: IllegalArgumentException): ErrorResponse = | ||
ErrorResponse.from(exception) | ||
|
||
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) | ||
@ExceptionHandler(IllegalStateException::class) | ||
fun handleIllegalStateException(exception: IllegalStateException): ErrorResponse = | ||
ErrorResponse.from(exception) | ||
|
||
} |
6 changes: 6 additions & 0 deletions
6
src/main/kotlin/org/gitanimals/auction/controller/request/RegisterProductRequest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package org.gitanimals.auction.controller.request | ||
|
||
data class RegisterProductRequest( | ||
val personaId: String, | ||
val price: String, | ||
) |
11 changes: 11 additions & 0 deletions
11
src/main/kotlin/org/gitanimals/auction/controller/response/ErrorResponse.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package org.gitanimals.auction.controller.response | ||
|
||
data class ErrorResponse( | ||
val message: String, | ||
) { | ||
|
||
companion object { | ||
fun from(exception: Exception): ErrorResponse = | ||
ErrorResponse(exception.message ?: exception.localizedMessage) | ||
} | ||
} |
Oops, something went wrong.