Skip to content

Commit

Permalink
feat: 주문 API 추가
Browse files Browse the repository at this point in the history
Co-authored-by: Combi153 <[email protected]>
Co-authored-by: hgo641 <[email protected]>
  • Loading branch information
TaeyeonRoyce committed Feb 21, 2024
1 parent 33d30f1 commit 47985cd
Show file tree
Hide file tree
Showing 38 changed files with 1,173 additions and 195 deletions.
136 changes: 136 additions & 0 deletions src/main/kotlin/com/petqua/application/order/OrderService.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
package com.petqua.application.order

import com.petqua.application.order.dto.SaveOrderCommand
import com.petqua.application.order.dto.SaveOrderResponse
import com.petqua.common.domain.findByIdOrThrow
import com.petqua.common.util.throwExceptionWhen
import com.petqua.domain.order.Order
import com.petqua.domain.order.OrderName
import com.petqua.domain.order.OrderNumber
import com.petqua.domain.order.OrderRepository
import com.petqua.domain.order.OrderShippingAddress
import com.petqua.domain.order.OrderStatus.ORDER_CREATED
import com.petqua.domain.order.ShippingAddressRepository
import com.petqua.domain.order.ShippingNumber
import com.petqua.domain.product.ProductRepository
import com.petqua.domain.product.option.ProductOptionRepository
import com.petqua.domain.store.StoreRepository
import com.petqua.exception.order.OrderException
import com.petqua.exception.order.OrderExceptionType.ORDER_PRICE_NOT_MATCH
import com.petqua.exception.order.OrderExceptionType.PRODUCT_NOT_FOUND
import com.petqua.exception.order.ShippingAddressException
import com.petqua.exception.order.ShippingAddressExceptionType
import com.petqua.exception.product.ProductException
import com.petqua.exception.product.ProductExceptionType.INVALID_PRODUCT_OPTION
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional

@Transactional
@Service
class OrderService(
private val orderRepository: OrderRepository,
private val productRepository: ProductRepository,
private val productOptionRepository: ProductOptionRepository,
private val shippingAddressRepository: ShippingAddressRepository,
private val storeRepository: StoreRepository,
) {

fun save(command: SaveOrderCommand): SaveOrderResponse {
// TODO 상품 존재 검증
val productIds = command.orderProductCommands.map { it.productId }
val productById = productRepository.findAllByIsDeletedFalseAndIdIn(productIds).associateBy { it.id }
val products = productById.map { it.value }

throwExceptionWhen(products.size != productIds.size) { OrderException(PRODUCT_NOT_FOUND) }

// TODO 상품 유효성 검증 - 올바른 옵션 매칭인가?
val productOptions = productOptionRepository.findByProductIdIn(productIds)

command.orderProductCommands.forEach { productOptionCommand ->
productOptions.find { it.productId == productOptionCommand.productId }?.let {
throwExceptionWhen(!it.isSame(productOptionCommand.toProductOption())) {
ProductException(INVALID_PRODUCT_OPTION)
}
} ?: throw ProductException(INVALID_PRODUCT_OPTION)
}

// TODO 배송지 존재 검증
val shippingAddress = shippingAddressRepository.findByIdOrThrow(
command.shippingAddressId, ShippingAddressException(
ShippingAddressExceptionType.NOT_FOUND_SHIPPING_ADDRESS
)
)

// TODO 총 가격 검증
// 1. 상품 가격
command.orderProductCommands.forEach { productCommand ->
val product = productById[productCommand.productId]
?: throw OrderException(PRODUCT_NOT_FOUND)
val productOption = productOptions.find { it.productId == product.id }
?: throw ProductException(INVALID_PRODUCT_OPTION)

throwExceptionWhen(
productCommand.orderPrice != (product.discountPrice + productOption.additionalPrice) * productCommand.quantity.toBigDecimal()
|| productCommand.deliveryFee != product.getDeliveryFee(productCommand.deliveryMethod)
) {
OrderException(
ORDER_PRICE_NOT_MATCH
)
}
}

// 3. 총 배송비 검증 (스토어로 묶인 뒤 배송비 검증)
val groupBy = products.groupBy { product ->
Pair(
product.storeId,
command.orderProductCommands.find { it.productId == product.id }?.deliveryMethod
?: throw OrderException(PRODUCT_NOT_FOUND)
)
}
val orderDeliveryFee = groupBy.map { (storeDeliveryMethod, products) ->
val deliveryMethod = storeDeliveryMethod.second
products.first().getDeliveryFee(deliveryMethod).toInt()
}.sum()

// 4. 총 결제 금액 검증
throwExceptionWhen(command.totalAmount != orderDeliveryFee.toBigDecimal() + command.orderProductCommands.sumOf { it.orderPrice }) {
OrderException(
ORDER_PRICE_NOT_MATCH
)
}

// TODO: TODO 재고 검증

val storesById = storeRepository.findByIdIn(products.map { it.storeId }).associateBy { it.id }
val orderNumber = OrderNumber.generate()
val orderName = OrderName.from(products)
// TODO 주문 저장 로직
val orders = command.orderProductCommands.map { productCommand ->
val product = productById[productCommand.productId]
?: throw OrderException(PRODUCT_NOT_FOUND)

Order(
memberId = command.memberId,
orderNumber = orderNumber,
orderName = orderName,
orderShippingAddress = OrderShippingAddress.from(shippingAddress, command.shippingRequest),
orderProduct = productCommand.toOrderProduct(
shippingNumber = ShippingNumber.of(product.storeId, productCommand.deliveryMethod, orderNumber),
product = product,
storeName = storesById[product.storeId]?.name ?: throw OrderException(PRODUCT_NOT_FOUND),
),
isAbleToCancel = true,
status = ORDER_CREATED,
totalAmount = command.totalAmount,
)
}
orderRepository.saveAll(orders)

return SaveOrderResponse(
orderId = orders.first().orderNumber.value,
orderName = orders.first().orderName.value,
successUrl = "successUrl",
failUrl = "failUrl",
)
}
}
69 changes: 69 additions & 0 deletions src/main/kotlin/com/petqua/application/order/dto/OrderDtos.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package com.petqua.application.order.dto

import com.petqua.domain.delivery.DeliveryMethod
import com.petqua.domain.order.OrderProduct
import com.petqua.domain.order.ShippingNumber
import com.petqua.domain.product.Product
import com.petqua.domain.product.option.ProductOption
import com.petqua.domain.product.option.Sex
import java.math.BigDecimal

data class SaveOrderCommand(
val memberId: Long,
val shippingAddressId: Long,
val shippingRequest: String?,
val orderProductCommands: List<OrderProductCommand>,
val totalAmount: BigDecimal,
)

data class OrderProductCommand(
val productId: Long,
val quantity: Int,
val originalPrice: BigDecimal,
val discountRate: Int,
val discountPrice: BigDecimal,
val orderPrice: BigDecimal,
val sex: Sex,
val additionalPrice: BigDecimal,
val deliveryFee: BigDecimal,
val deliveryMethod: DeliveryMethod,
) {

fun toProductOption(): ProductOption {
return ProductOption(
sex = sex,
productId = productId,
additionalPrice = additionalPrice.setScale(2),
)
}

fun toOrderProduct(
shippingNumber: ShippingNumber,
product: Product,
storeName: String,
): OrderProduct {
return OrderProduct(
quantity = quantity,
originalPrice = originalPrice,
discountRate = discountRate,
discountPrice = discountPrice,
deliveryFee = deliveryFee,
shippingNumber = shippingNumber,
orderPrice = orderPrice,
productId = productId,
productName = product.name,
thumbnailUrl = product.thumbnailUrl,
storeId = product.storeId,
storeName = storeName,
deliveryMethod = deliveryMethod,
sex = sex,
)
}
}

data class SaveOrderResponse(
val orderId: String,
val orderName: String,
val successUrl: String,
val failUrl: String,
)
25 changes: 13 additions & 12 deletions src/main/kotlin/com/petqua/application/product/dto/ProductDtos.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import com.petqua.domain.product.dto.ProductSearchCondition
import com.petqua.domain.product.dto.ProductWithInfoResponse
import com.petqua.domain.product.review.ProductReviewRecommendation
import io.swagger.v3.oas.annotations.media.Schema
import java.math.BigDecimal

data class ProductDetailResponse(
@Schema(
Expand Down Expand Up @@ -108,22 +109,22 @@ data class ProductDetailResponse(
val descriptionImageUrls: List<String>,

@Schema(
description = "안전 배송 가능 여부",
example = "true"
description = "안전 배송 가격",
example = "5000"
)
val canDeliverSafely: Boolean,
val safeDeliveryFee: BigDecimal?,

@Schema(
description = "일반 배송 가능 여부",
example = "true"
description = "일반 배송 가격",
example = "3000"
)
val canDeliverCommonly: Boolean,
val commonDeliveryFee: BigDecimal?,

@Schema(
description = "직접 수령 가능 여부",
example = "true"
description = "픽업 배송 가격",
example = "0"
)
val canPickUp: Boolean,
val pickUpDeliveryFee: BigDecimal?,

@Schema(
description = "사육 온도 최소",
Expand Down Expand Up @@ -188,9 +189,9 @@ data class ProductDetailResponse(
descriptionTitle = productWithInfoResponse.descriptionTitle,
descriptionContent = productWithInfoResponse.descriptionContent,
descriptionImageUrls = descriptionImageUrls,
canDeliverSafely = productWithInfoResponse.canDeliverSafely,
canDeliverCommonly = productWithInfoResponse.canDeliverCommonly,
canPickUp = productWithInfoResponse.canPickUp,
safeDeliveryFee = productWithInfoResponse.safeDeliveryFee,
commonDeliveryFee = productWithInfoResponse.commonDeliveryFee,
pickUpDeliveryFee = productWithInfoResponse.pickUpDeliveryFee,
optimalTemperatureMin = productWithInfoResponse.optimalTemperatureMin,
optimalTemperatureMax = productWithInfoResponse.optimalTemperatureMax,
difficultyLevel = productWithInfoResponse.difficultyLevel,
Expand Down
40 changes: 19 additions & 21 deletions src/main/kotlin/com/petqua/common/config/DataInitializer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ import com.petqua.domain.product.detail.info.OptimalTemperature
import com.petqua.domain.product.detail.info.ProductInfo
import com.petqua.domain.product.detail.info.ProductInfoRepository
import com.petqua.domain.product.detail.info.Temperament.PEACEFUL
import com.petqua.domain.product.option.ProductOption
import com.petqua.domain.product.option.ProductOptionRepository
import com.petqua.domain.product.option.Sex.FEMALE
import com.petqua.domain.product.option.Sex.HERMAPHRODITE
Expand All @@ -47,12 +46,12 @@ import com.petqua.domain.recommendation.ProductRecommendationRepository
import com.petqua.domain.store.Store
import com.petqua.domain.store.StoreRepository
import java.math.BigDecimal
import kotlin.random.Random
import org.springframework.boot.context.event.ApplicationReadyEvent
import org.springframework.context.annotation.Profile
import org.springframework.context.event.EventListener
import org.springframework.stereotype.Component
import org.springframework.transaction.annotation.Transactional
import kotlin.random.Random

@Component
@Profile("local", "prod")
Expand Down Expand Up @@ -158,17 +157,17 @@ class DataInitializer(
(it % 2) == 0 -> category1.id
else -> category2.id
}
val canDeliverSafely = when {
(it % 4) == 0 -> true
else -> false
val safeDeliveryFee = when {
(it % 4) == 0 -> 5000.toBigDecimal()
else -> null
}
val canDeliverCommonly = when {
(it % 5) == 0 -> true
else -> false
val commonDeliveryFee = when {
(it % 5) == 0 -> 3000.toBigDecimal()
else -> null
}
val canPickUp = when {
(it % 2) == 0 -> true
else -> false
val pickUpDeliveryFee = when {
(it % 2) == 0 -> BigDecimal.ZERO
else -> null
}
val reviewCount = (1..5).random()

Expand All @@ -177,12 +176,12 @@ class DataInitializer(
(it % 7) == 0 -> FEMALE
else -> HERMAPHRODITE
}
val productOption = productOptionRepository.save(
ProductOption(
sex = sex,
additionalPrice = BigDecimal.ZERO
)
)
// val productOption = productOptionRepository.save(
// ProductOption(
// sex = sex,
// additionalPrice = BigDecimal.ZERO
// )
// )

val productInfo = productInfoRepository.save(
ProductInfo(
Expand Down Expand Up @@ -216,10 +215,9 @@ class DataInitializer(
reviewCount = reviewCount,
reviewTotalScore = (1..reviewCount).sum(),
thumbnailUrl = "https://docs.petqua.co.kr/products/thumbnails/thumbnail3.jpeg",
canDeliverSafely = canDeliverSafely,
canDeliverCommonly = canDeliverCommonly,
canPickUp = canPickUp,
productOptionId = productOption.id,
safeDeliveryFee = safeDeliveryFee,
commonDeliveryFee = commonDeliveryFee,
pickUpDeliveryFee = pickUpDeliveryFee,
productDescriptionId = productDescriptionId,
productInfoId = productInfo.id,
)
Expand Down
4 changes: 4 additions & 0 deletions src/main/kotlin/com/petqua/domain/order/Order.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ class Order(
@AttributeOverride(name = "value", column = Column(name = "orderNumber"))
val orderNumber: OrderNumber,

@Embedded
@AttributeOverride(name = "value", column = Column(name = "orderName"))
val orderName: OrderName,

@Embedded
val orderShippingAddress: OrderShippingAddress,

Expand Down
10 changes: 7 additions & 3 deletions src/main/kotlin/com/petqua/domain/order/OrderName.kt
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
package com.petqua.domain.order

import com.petqua.domain.product.Product
import jakarta.persistence.Column
import jakarta.persistence.Embeddable

@Embeddable
class OrderName(

data class OrderName(
@Column(nullable = false, unique = true)
val value: String,
) {

//TODO : 주문 이름 생성 로직
companion object {
fun from(products: List<Product>): OrderName {
return OrderName("${products.first().name}${products.size - 1}")
}
}
}
Loading

0 comments on commit 47985cd

Please sign in to comment.