Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/develop' into feature/auth
Browse files Browse the repository at this point in the history
  • Loading branch information
sm9171 committed Oct 25, 2023
2 parents 0116428 + 32f1010 commit 3b108b6
Show file tree
Hide file tree
Showing 61 changed files with 921 additions and 273 deletions.
2 changes: 2 additions & 0 deletions commerce-api/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ dependencies {
implementation(project(":commerce-support:monitoring"))
runtimeOnly(project(":commerce-infra:db-main"))
runtimeOnly(project(":commerce-infra:redis-main"))
runtimeOnly(project(":commerce-infra:external-api"))

compileOnly("org.springframework:spring-tx")
implementation("org.springframework.boot:spring-boot-starter-web")
Expand All @@ -21,5 +22,6 @@ dependencies {
testRuntimeOnly("com.h2database:h2")
testImplementation("org.testcontainers:testcontainers:1.17.6")
testImplementation("org.testcontainers:junit-jupiter:1.17.6")
testImplementation("io.mockk:mockk:1.12.5")
testImplementation("com.redis.testcontainers:testcontainers-redis-junit-jupiter:1.4.6")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.hanghae.commerce.claim.application

import com.hanghae.commerce.claim.domain.OrderCancelService
import com.hanghae.commerce.claim.domain.OrderCancelValidator
import com.hanghae.commerce.claim.domain.command.OrderCancelCommand
import com.hanghae.commerce.order.domain.OrderReader
import org.springframework.stereotype.Component

@Component
class OrderCancelFacade(
private val orderCancelValidator: OrderCancelValidator,
private val orderCancelService: OrderCancelService,
private val orderReader: OrderReader,
) {
fun cancel(command: OrderCancelCommand) {
val order = orderReader.read(command.orderId)
orderCancelValidator.validate(command.bankAccount)
orderCancelService.cancel(command, order)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.hanghae.commerce.claim.domain

import com.hanghae.commerce.event.Event
import com.hanghae.commerce.payment.domain.BankAccount

data class OrderCancelCompletedEvent(
val orderId: String,
val bankAccount: BankAccount?,
) : Event
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.hanghae.commerce.claim.domain

import com.hanghae.commerce.claim.domain.command.OrderCancelCommand
import com.hanghae.commerce.event.CommerceEventPublisher
import com.hanghae.commerce.order.domain.OrderWriter
import com.hanghae.commerce.order.domain.Order
import org.springframework.stereotype.Service

@Service
class OrderCancelService(
private val orderWriter: OrderWriter,
private val eventPublisher: CommerceEventPublisher,
) {
fun cancel(command: OrderCancelCommand, order: Order) {
order.cancel(command.reason)
orderWriter.write(order)
eventPublisher.publish(
OrderCancelCompletedEvent(
orderId = command.orderId,
bankAccount = command.bankAccount,
),
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.hanghae.commerce.claim.domain

import com.hanghae.commerce.payment.domain.BankAccount
import com.hanghae.commerce.payment.domain.BankAccountValidator
import org.springframework.stereotype.Component

@Component
class OrderCancelValidator(
private val bankAccountValidator: BankAccountValidator,
) {
fun validate(command: BankAccount?) {
validateRefundableBankAccount(command)
}

private fun validateRefundableBankAccount(command: BankAccount?) {
if (command == null) {
return
}
require(bankAccountValidator.validate(command)) { "Invalid bank account" }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.hanghae.commerce.claim.domain.command

import com.hanghae.commerce.payment.domain.BankAccount
data class OrderCancelCommand(
val orderId: String,
val userId: String,
val reason: String,
val bankAccount: BankAccount? = null,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.hanghae.commerce.claim.presentation

import com.hanghae.commerce.claim.application.OrderCancelFacade
import com.hanghae.commerce.claim.presentation.dto.OrderCancelRequest
import com.hanghae.commerce.claim.presentation.dto.toCommand
import org.springframework.http.ResponseEntity
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

@RestController
@RequestMapping("/api/claims")
class ClaimController(
private val claimFacade: OrderCancelFacade,
) {
@PostMapping("/cancel")
fun cancel(
@RequestBody request: OrderCancelRequest,
): ResponseEntity<String> {
claimFacade.cancel(request.toCommand())
return ResponseEntity.ok("ok")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.hanghae.commerce.claim.presentation.dto

import com.hanghae.commerce.claim.domain.command.OrderCancelCommand

fun OrderCancelRequest.toCommand(): OrderCancelCommand {
return OrderCancelCommand(
orderId = this.orderId,
userId = this.userId,
reason = this.reason,
bankAccount = this.bankAccount,
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.hanghae.commerce.claim.presentation.dto

import com.hanghae.commerce.payment.domain.BankAccount

data class OrderCancelRequest(
val orderId: String,
val userId: String,
val reason: String,
val bankAccount: BankAccount,
)
Original file line number Diff line number Diff line change
Expand Up @@ -5,35 +5,48 @@ import com.hanghae.commerce.item.domain.Item
import com.hanghae.commerce.item.domain.ItemReader
import com.hanghae.commerce.item.domain.ItemWriter
import com.hanghae.commerce.order.domain.OrderItem
import com.hanghae.commerce.order.domain.command.OrderCommand
import com.hanghae.commerce.order.exception.SoldOutException
import org.springframework.stereotype.Component
import java.util.concurrent.TimeUnit

@Component
class StockManager(
class ItemStockService(
private val itemReader: ItemReader,
private val itemWriter: ItemWriter,
) {

@DistributedLock(
key = "#orderItem.itemId",
key = "#orderItemCommand.itemId",
timeUnit = TimeUnit.SECONDS,
waitTime = 10,
leaseTime = 60,
)
fun verifyStockRemains(orderItem: OrderItem) {
val item: Item = itemReader.getItemByItemId(orderItem.itemId) ?: throw IllegalArgumentException()
checkStockAndUpdateQuantity(item, orderItem)
fun verifyStockRemains(orderItemCommand: OrderCommand.OrderItem) {
val item: Item = itemReader.getItemByItemId(orderItemCommand.itemId) ?: throw IllegalArgumentException("존재하지 않는 상품입니다.")
checkStockAndUpdateQuantity(item, orderItemCommand.quantity)
}

private fun checkStockAndUpdateQuantity(
item: Item,
orderItem: OrderItem,
orderItemQuantity: Int,
) {
if (item.stock - orderItem.quantity < 0) {
if (item.stock - orderItemQuantity < 0) {
throw SoldOutException("재고가 부족합니다.")
}
item.stock -= orderItem.quantity
item.stock -= orderItemQuantity
itemWriter.save(item)
}

@DistributedLock(
key = "#orderItem.itemId",
timeUnit = TimeUnit.SECONDS,
waitTime = 10,
leaseTime = 60,
)
fun restore(orderItem: OrderItem) {
val item: Item = itemReader.getItemByItemId(orderItem.itemId) ?: throw IllegalArgumentException()
item.stock += orderItem.quantity
itemWriter.save(item)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.hanghae.commerce.item.domain

import com.hanghae.commerce.claim.domain.OrderCancelCompletedEvent
import com.hanghae.commerce.item.application.ItemStockService
import com.hanghae.commerce.order.domain.OrderReader
import com.hanghae.commerce.order.domain.OrderItem
import org.springframework.context.event.EventListener
import org.springframework.stereotype.Component

@Component
class ItemStockEventListener(
private val orderReader: OrderReader,
private val itemStockService: ItemStockService,
) {
@EventListener
fun onOrderCanceled(event: OrderCancelCompletedEvent) {
val orderItemList = orderReader.read(event.orderId).orderItemList
restoreItemStock(orderItemList)
}

private fun restoreItemStock(orderItemList: List<OrderItem>) {
orderItemList.forEach(itemStockService::restore)
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.hanghae.commerce.order.application

import com.hanghae.commerce.item.application.ItemStockService
import com.hanghae.commerce.order.domain.OrderService
import com.hanghae.commerce.order.domain.command.OrderCommand
import org.springframework.stereotype.Service

@Service
class OrderFacade(
private val orderService: OrderService,
private val itemStockService: ItemStockService,
) {
fun order(command: OrderCommand): String {
verifyStockRemains(command.orderItemList)
return orderService.create(command)
}

private fun verifyStockRemains(orderItems: List<OrderCommand.OrderItem>) {
orderItems.forEach {
itemStockService.verifyStockRemains(it)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.hanghae.commerce.order.domain

import com.hanghae.commerce.common.IdentifierConstants
import com.hanghae.commerce.order.domain.command.OrderCreateCommand

class Order(
val id: String = IdentifierConstants.NOT_YET_PERSISTED_ID,
Expand All @@ -10,38 +9,20 @@ class Order(
val discountAmount: Int,
val paymentAmount: Int,
val deliveryFee: Int,
var cancelReason: String? = null,
var status: OrderStatus,
val orderItemList: List<OrderItem>,
) {
fun completePayment() {
this.status = OrderStatus.DELIVERY_PREPARE
}

companion object {
fun from(orderCreateCommand: OrderCreateCommand): Order {
val orderItemList = orderCreateCommand.orderItemList

val orderAmount: Int = calculateOrderAmount(orderItemList)
val deliveryFee: Int = calculateDeliveryFee(orderAmount)
val paymentAmount: Int = orderAmount + deliveryFee

return Order(
userId = orderCreateCommand.userId,
orderAmount = orderAmount,
discountAmount = 0,
paymentAmount = paymentAmount,
deliveryFee = deliveryFee,
status = OrderStatus.PAYMENT_WAIT,
orderItemList = orderItemList,
)
}

private fun calculateDeliveryFee(orderAmount: Int): Int {
return if (orderAmount < 50000) 2500 else 0
}
fun cancel(reason: String) {
this.status = OrderStatus.ORDER_CANCEL
this.cancelReason = reason
}

private fun calculateOrderAmount(orderLines: List<OrderItem>): Int {
return orderLines.sumOf { it.getAmount() }
}
fun isPaymentWait(): Boolean {
return this.status == OrderStatus.PAYMENT_WAIT
}
}

This file was deleted.

Loading

0 comments on commit 3b108b6

Please sign in to comment.