diff --git a/src/main/kotlin/com/petqua/application/order/OrderService.kt b/src/main/kotlin/com/petqua/application/order/OrderService.kt new file mode 100644 index 00000000..7e97af96 --- /dev/null +++ b/src/main/kotlin/com/petqua/application/order/OrderService.kt @@ -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", + ) + } +} diff --git a/src/main/kotlin/com/petqua/application/order/dto/OrderDtos.kt b/src/main/kotlin/com/petqua/application/order/dto/OrderDtos.kt new file mode 100644 index 00000000..143634cf --- /dev/null +++ b/src/main/kotlin/com/petqua/application/order/dto/OrderDtos.kt @@ -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, + 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, +) diff --git a/src/main/kotlin/com/petqua/application/product/dto/ProductDtos.kt b/src/main/kotlin/com/petqua/application/product/dto/ProductDtos.kt index 872e91eb..3045d603 100644 --- a/src/main/kotlin/com/petqua/application/product/dto/ProductDtos.kt +++ b/src/main/kotlin/com/petqua/application/product/dto/ProductDtos.kt @@ -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( @@ -108,22 +109,22 @@ data class ProductDetailResponse( val descriptionImageUrls: List, @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 = "사육 온도 최소", @@ -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, diff --git a/src/main/kotlin/com/petqua/common/config/DataInitializer.kt b/src/main/kotlin/com/petqua/common/config/DataInitializer.kt index 57fc136e..4f48ca22 100644 --- a/src/main/kotlin/com/petqua/common/config/DataInitializer.kt +++ b/src/main/kotlin/com/petqua/common/config/DataInitializer.kt @@ -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 @@ -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") @@ -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() @@ -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( @@ -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, ) diff --git a/src/main/kotlin/com/petqua/domain/order/Order.kt b/src/main/kotlin/com/petqua/domain/order/Order.kt index 7817dd25..070fff33 100644 --- a/src/main/kotlin/com/petqua/domain/order/Order.kt +++ b/src/main/kotlin/com/petqua/domain/order/Order.kt @@ -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, diff --git a/src/main/kotlin/com/petqua/domain/order/OrderName.kt b/src/main/kotlin/com/petqua/domain/order/OrderName.kt index abc2e42f..679454ba 100644 --- a/src/main/kotlin/com/petqua/domain/order/OrderName.kt +++ b/src/main/kotlin/com/petqua/domain/order/OrderName.kt @@ -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): OrderName { + return OrderName("${products.first().name} 외 ${products.size - 1}건") + } + } } diff --git a/src/main/kotlin/com/petqua/domain/order/OrderNumber.kt b/src/main/kotlin/com/petqua/domain/order/OrderNumber.kt index 6b5b91f4..0bd7b29b 100644 --- a/src/main/kotlin/com/petqua/domain/order/OrderNumber.kt +++ b/src/main/kotlin/com/petqua/domain/order/OrderNumber.kt @@ -2,13 +2,22 @@ package com.petqua.domain.order import jakarta.persistence.Column import jakarta.persistence.Embeddable +import java.time.Instant +import java.time.ZoneId +import java.time.format.DateTimeFormatter +import java.util.UUID @Embeddable -class OrderNumber( - +data class OrderNumber( @Column(nullable = false, unique = true) val value: String, ) { - //TODO : 주문 번호 생성 로직 + companion object { + fun generate(): OrderNumber { // 202402211607026029E90DB030 + val createdTime = Instant.now().atZone(ZoneId.of("Asia/Seoul")).toLocalDateTime() + val uuid = UUID.randomUUID().toString().replace("-", "").substring(0, 12).uppercase() + return OrderNumber(createdTime.format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")) + uuid) + } + } } diff --git a/src/main/kotlin/com/petqua/domain/order/OrderProduct.kt b/src/main/kotlin/com/petqua/domain/order/OrderProduct.kt index 48e9aa0c..7d455428 100644 --- a/src/main/kotlin/com/petqua/domain/order/OrderProduct.kt +++ b/src/main/kotlin/com/petqua/domain/order/OrderProduct.kt @@ -2,15 +2,16 @@ package com.petqua.domain.order import com.petqua.domain.delivery.DeliveryMethod import com.petqua.domain.product.option.Sex +import jakarta.persistence.AttributeOverride import jakarta.persistence.Column import jakarta.persistence.Embeddable +import jakarta.persistence.Embedded import jakarta.persistence.EnumType import jakarta.persistence.Enumerated import java.math.BigDecimal @Embeddable -class OrderProduct( - +data class OrderProduct( @Column(nullable = false) val quantity: Int, @@ -26,8 +27,9 @@ class OrderProduct( @Column(nullable = false) val deliveryFee: BigDecimal, - @Column(nullable = false) - val shippingNumber: String, + @Embedded + @AttributeOverride(name = "value", column = Column(name = "shippingNumber")) + var shippingNumber: ShippingNumber, @Column(nullable = false) val orderPrice: BigDecimal, diff --git a/src/main/kotlin/com/petqua/domain/order/OrderRepository.kt b/src/main/kotlin/com/petqua/domain/order/OrderRepository.kt new file mode 100644 index 00000000..27632406 --- /dev/null +++ b/src/main/kotlin/com/petqua/domain/order/OrderRepository.kt @@ -0,0 +1,6 @@ +package com.petqua.domain.order + +import org.springframework.data.jpa.repository.JpaRepository + +interface OrderRepository : JpaRepository { +} diff --git a/src/main/kotlin/com/petqua/domain/order/OrderShippingAddress.kt b/src/main/kotlin/com/petqua/domain/order/OrderShippingAddress.kt index a1eeae5b..ead75414 100644 --- a/src/main/kotlin/com/petqua/domain/order/OrderShippingAddress.kt +++ b/src/main/kotlin/com/petqua/domain/order/OrderShippingAddress.kt @@ -4,7 +4,7 @@ import jakarta.persistence.Column import jakarta.persistence.Embeddable @Embeddable -class OrderShippingAddress( +data class OrderShippingAddress( @Column(nullable = false) val receiver: String, @@ -19,6 +19,18 @@ class OrderShippingAddress( @Column(nullable = false) val detailAddress: String, - val requestMessage: String, + val requestMessage: String?, ) { + companion object { + fun from(shippingAddress: ShippingAddress, requestMessage: String?): OrderShippingAddress { + return OrderShippingAddress( + receiver = shippingAddress.receiver, + phoneNumber = shippingAddress.phoneNumber, + zipCode = shippingAddress.zipCode, + address = shippingAddress.address, + detailAddress = shippingAddress.detailAddress, + requestMessage = requestMessage, + ) + } + } } diff --git a/src/main/kotlin/com/petqua/domain/order/OrderStatus.kt b/src/main/kotlin/com/petqua/domain/order/OrderStatus.kt index 3c2f7fc9..e88bf161 100644 --- a/src/main/kotlin/com/petqua/domain/order/OrderStatus.kt +++ b/src/main/kotlin/com/petqua/domain/order/OrderStatus.kt @@ -4,7 +4,7 @@ enum class OrderStatus( private val description: String, ) { - ORDER_CHECKING("주문 확인 중"), + ORDER_CREATED("주문 생성"), ORDERED("주문 완료"), ORDER_CONFIRMED("주문 확인"), CANCELED("주문 취소"), diff --git a/src/main/kotlin/com/petqua/domain/order/ShippingNumber.kt b/src/main/kotlin/com/petqua/domain/order/ShippingNumber.kt new file mode 100644 index 00000000..ec7a7fbb --- /dev/null +++ b/src/main/kotlin/com/petqua/domain/order/ShippingNumber.kt @@ -0,0 +1,18 @@ +package com.petqua.domain.order + +import com.petqua.domain.delivery.DeliveryMethod +import jakarta.persistence.Column +import jakarta.persistence.Embeddable + +@Embeddable +data class ShippingNumber( + @Column(nullable = false, unique = true) + val value: String, +) { + + companion object { + fun of(storeId: Long, deliveryMethod: DeliveryMethod, orderNumber: OrderNumber): ShippingNumber { + return ShippingNumber("${orderNumber.value}${storeId}${deliveryMethod.name}") + } + } +} diff --git a/src/main/kotlin/com/petqua/domain/payment/tosspayment/TossPayment.kt b/src/main/kotlin/com/petqua/domain/payment/tosspayment/TossPayment.kt index 4176c5c3..6f37631e 100644 --- a/src/main/kotlin/com/petqua/domain/payment/tosspayment/TossPayment.kt +++ b/src/main/kotlin/com/petqua/domain/payment/tosspayment/TossPayment.kt @@ -2,6 +2,7 @@ package com.petqua.domain.payment.tosspayment import com.petqua.domain.order.OrderName import com.petqua.domain.order.OrderNumber +import jakarta.persistence.AttributeOverride import jakarta.persistence.Column import jakarta.persistence.Embedded import jakarta.persistence.Entity @@ -21,9 +22,11 @@ class TossPayment( val paymentKey: String, @Embedded + @AttributeOverride(name = "value", column = Column(name = "order_number", nullable = false, unique = true)) val orderNumber: OrderNumber, @Embedded + @AttributeOverride(name = "value", column = Column(name = "order_name", nullable = false)) val orderName: OrderName, @Enumerated(STRING) diff --git a/src/main/kotlin/com/petqua/domain/product/Product.kt b/src/main/kotlin/com/petqua/domain/product/Product.kt index 71e30b9b..b6fae75b 100644 --- a/src/main/kotlin/com/petqua/domain/product/Product.kt +++ b/src/main/kotlin/com/petqua/domain/product/Product.kt @@ -2,8 +2,10 @@ package com.petqua.domain.product import com.petqua.common.domain.BaseEntity import com.petqua.common.domain.SoftDeleteEntity +import com.petqua.domain.delivery.DeliveryMethod import com.petqua.domain.product.review.ProductReviewStatistics import com.petqua.exception.product.ProductException +import com.petqua.exception.product.ProductExceptionType.INVALID_DELIVERY_METHOD import com.petqua.exception.product.ProductExceptionType.NOT_FOUND_PRODUCT import jakarta.persistence.AttributeOverride import jakarta.persistence.Column @@ -53,17 +55,11 @@ class Product( @Column(nullable = false) var isDeleted: Boolean = false, - @Column(nullable = false) - val canDeliverSafely: Boolean, + val safeDeliveryFee: BigDecimal?, - @Column(nullable = false) - val canDeliverCommonly: Boolean, + val commonDeliveryFee: BigDecimal?, - @Column(nullable = false) - val canPickUp: Boolean, - - @Column(nullable = false) - val productOptionId: Long, + val pickUpDeliveryFee: BigDecimal?, val productDescriptionId: Long?, @@ -83,13 +79,19 @@ class Product( wishCount = wishCount.decrease() } + fun getDeliveryFee(deliveryMethod: DeliveryMethod): BigDecimal { + return when (deliveryMethod) { + DeliveryMethod.SAFETY -> safeDeliveryFee ?: throw ProductException(INVALID_DELIVERY_METHOD) + DeliveryMethod.COMMON -> commonDeliveryFee ?: throw ProductException(INVALID_DELIVERY_METHOD) + DeliveryMethod.PICK_UP -> pickUpDeliveryFee ?: throw ProductException(INVALID_DELIVERY_METHOD) + else -> throw ProductException(INVALID_DELIVERY_METHOD) + } + } + override fun validateDeleted() { if (isDeleted) { throw ProductException(NOT_FOUND_PRODUCT) } } - override fun toString(): String { - return "Product(id=$id, name='$name', categoryId=$categoryId, price=$price, storeId=$storeId, discountRate=$discountRate, discountPrice=$discountPrice, wishCount=$wishCount, reviewCount=$reviewCount, reviewTotalScore=$reviewTotalScore, thumbnailUrl='$thumbnailUrl', isDeleted=$isDeleted, canDeliverSafely=$canDeliverSafely, canDeliverCommonly=$canDeliverCommonly, canPickUp=$canPickUp)" - } } diff --git a/src/main/kotlin/com/petqua/domain/product/ProductCustomRepositoryImpl.kt b/src/main/kotlin/com/petqua/domain/product/ProductCustomRepositoryImpl.kt index 587bc1a6..907dedd0 100644 --- a/src/main/kotlin/com/petqua/domain/product/ProductCustomRepositoryImpl.kt +++ b/src/main/kotlin/com/petqua/domain/product/ProductCustomRepositoryImpl.kt @@ -56,7 +56,7 @@ class ProductCustomRepositoryImpl( leftJoin(ProductDescription::class).on(path(Product::productDescriptionId).eq(path(ProductDescription::id))), join(ProductInfo::class).on(path(Product::productInfoId).eq(path(ProductInfo::id))), join(Category::class).on(path(Product::categoryId).eq(path(Category::id))), - join(ProductOption::class).on(path(Product::productOptionId).eq(path(ProductOption::id))) + join(ProductOption::class).on(path(Product::id).eq(path(ProductOption::productId))) ).whereAnd( path(Product::id).eq(id), active(), diff --git a/src/main/kotlin/com/petqua/domain/product/ProductDynamicJpqlGenerator.kt b/src/main/kotlin/com/petqua/domain/product/ProductDynamicJpqlGenerator.kt index dfd98ed2..a56dc800 100644 --- a/src/main/kotlin/com/petqua/domain/product/ProductDynamicJpqlGenerator.kt +++ b/src/main/kotlin/com/petqua/domain/product/ProductDynamicJpqlGenerator.kt @@ -45,9 +45,9 @@ class ProductDynamicJpqlGenerator : Jpql() { fun Jpql.productDeliveryOptionBy(deliveryMethod: DeliveryMethod): Predicate? { return when (deliveryMethod) { - DeliveryMethod.SAFETY -> path(Product::canDeliverSafely).eq(true) - DeliveryMethod.COMMON -> path(Product::canDeliverCommonly).eq(true) - DeliveryMethod.PICK_UP -> path(Product::canPickUp).eq(true) + DeliveryMethod.SAFETY -> path(Product::safeDeliveryFee).isNotNull() + DeliveryMethod.COMMON -> path(Product::commonDeliveryFee).isNotNull() + DeliveryMethod.PICK_UP -> path(Product::pickUpDeliveryFee).isNotNull() else -> null } } diff --git a/src/main/kotlin/com/petqua/domain/product/ProductRepository.kt b/src/main/kotlin/com/petqua/domain/product/ProductRepository.kt index 7a48824d..7a642421 100644 --- a/src/main/kotlin/com/petqua/domain/product/ProductRepository.kt +++ b/src/main/kotlin/com/petqua/domain/product/ProductRepository.kt @@ -3,4 +3,6 @@ package com.petqua.domain.product import org.springframework.data.jpa.repository.JpaRepository interface ProductRepository : JpaRepository, ProductCustomRepository { + + fun findAllByIsDeletedFalseAndIdIn(ids: List): Set } diff --git a/src/main/kotlin/com/petqua/domain/product/dto/ProductDtos.kt b/src/main/kotlin/com/petqua/domain/product/dto/ProductDtos.kt index 14707a80..9ae552c3 100644 --- a/src/main/kotlin/com/petqua/domain/product/dto/ProductDtos.kt +++ b/src/main/kotlin/com/petqua/domain/product/dto/ProductDtos.kt @@ -11,6 +11,7 @@ import com.petqua.domain.product.option.ProductOption import com.petqua.exception.product.ProductException import com.petqua.exception.product.ProductExceptionType import io.swagger.v3.oas.annotations.media.Schema +import java.math.BigDecimal data class ProductReadCondition( val canDeliverSafely: Boolean? = null, @@ -73,9 +74,9 @@ data class ProductWithInfoResponse( val thumbnailUrl: String, val descriptionTitle: String, val descriptionContent: String, - val canDeliverSafely: Boolean, - val canDeliverCommonly: Boolean, - val canPickUp: Boolean, + val safeDeliveryFee: BigDecimal?, + val commonDeliveryFee: BigDecimal?, + val pickUpDeliveryFee: BigDecimal?, val optimalTemperatureMin: Int, val optimalTemperatureMax: Int, val difficultyLevel: String, @@ -105,9 +106,9 @@ data class ProductWithInfoResponse( thumbnailUrl = product.thumbnailUrl, descriptionTitle = productDescription.title, descriptionContent = productDescription.content, - canDeliverSafely = product.canDeliverSafely, - canDeliverCommonly = product.canDeliverCommonly, - canPickUp = product.canPickUp, + safeDeliveryFee = product.safeDeliveryFee, + commonDeliveryFee = product.commonDeliveryFee, + pickUpDeliveryFee = product.pickUpDeliveryFee, optimalTemperatureMin = productInfo.optimalTemperature.optimalTemperatureMin, optimalTemperatureMax = productInfo.optimalTemperature.optimalTemperatureMax, difficultyLevel = productInfo.difficultyLevel.description, @@ -190,22 +191,22 @@ data class ProductResponse( val thumbnailUrl: 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 = "찜 여부", @@ -225,8 +226,8 @@ data class ProductResponse( product.reviewCount, product.averageReviewScore(), product.thumbnailUrl, - product.canDeliverSafely, - product.canDeliverCommonly, - product.canPickUp, + product.safeDeliveryFee, + product.commonDeliveryFee, + product.pickUpDeliveryFee, ) } diff --git a/src/main/kotlin/com/petqua/domain/product/option/ProductOption.kt b/src/main/kotlin/com/petqua/domain/product/option/ProductOption.kt index 50205501..a06852a1 100644 --- a/src/main/kotlin/com/petqua/domain/product/option/ProductOption.kt +++ b/src/main/kotlin/com/petqua/domain/product/option/ProductOption.kt @@ -15,6 +15,9 @@ class ProductOption( @Id @GeneratedValue(strategy = GenerationType.IDENTITY) val id: Long = 0L, + @Column(nullable = false) + val productId: Long, + @Column(nullable = false) @Enumerated(STRING) val sex: Sex, @@ -26,4 +29,11 @@ class ProductOption( fun hasDistinctSex(): Boolean { return sex != Sex.HERMAPHRODITE } + + fun isSame(other: ProductOption): Boolean { + return productId == other.productId + && sex == other.sex + && additionalPrice == other.additionalPrice + } + } diff --git a/src/main/kotlin/com/petqua/domain/product/option/ProductOptionRepository.kt b/src/main/kotlin/com/petqua/domain/product/option/ProductOptionRepository.kt index 0e5acff7..141dc02d 100644 --- a/src/main/kotlin/com/petqua/domain/product/option/ProductOptionRepository.kt +++ b/src/main/kotlin/com/petqua/domain/product/option/ProductOptionRepository.kt @@ -2,4 +2,6 @@ package com.petqua.domain.product.option import org.springframework.data.jpa.repository.JpaRepository -interface ProductOptionRepository : JpaRepository +interface ProductOptionRepository : JpaRepository { + fun findByProductIdIn(productIds: List): Set +} diff --git a/src/main/kotlin/com/petqua/domain/product/option/Sex.kt b/src/main/kotlin/com/petqua/domain/product/option/Sex.kt index d11c1925..a8a6af6d 100644 --- a/src/main/kotlin/com/petqua/domain/product/option/Sex.kt +++ b/src/main/kotlin/com/petqua/domain/product/option/Sex.kt @@ -1,5 +1,9 @@ package com.petqua.domain.product.option +import com.petqua.exception.product.ProductException +import com.petqua.exception.product.ProductExceptionType.INVALID_PRODUCT_OPTION +import java.util.Locale + enum class Sex( val description: String, ) { @@ -8,4 +12,15 @@ enum class Sex( MALE("수"), HERMAPHRODITE("자웅동체"), ; + + fun isLogical(other: Sex): Boolean { + return this != HERMAPHRODITE + } + + companion object { + fun from(name: String): Sex { + return enumValues().find { it.name == name.uppercase(Locale.ENGLISH) } + ?: throw ProductException(INVALID_PRODUCT_OPTION) + } + } } diff --git a/src/main/kotlin/com/petqua/domain/store/StoreRepository.kt b/src/main/kotlin/com/petqua/domain/store/StoreRepository.kt index c378ba12..d0bc7799 100644 --- a/src/main/kotlin/com/petqua/domain/store/StoreRepository.kt +++ b/src/main/kotlin/com/petqua/domain/store/StoreRepository.kt @@ -3,4 +3,5 @@ package com.petqua.domain.store import org.springframework.data.jpa.repository.JpaRepository interface StoreRepository : JpaRepository { + fun findByIdIn(ids: List): Set } diff --git a/src/main/kotlin/com/petqua/exception/order/OrderException.kt b/src/main/kotlin/com/petqua/exception/order/OrderException.kt new file mode 100644 index 00000000..361ea29f --- /dev/null +++ b/src/main/kotlin/com/petqua/exception/order/OrderException.kt @@ -0,0 +1,13 @@ +package com.petqua.exception.order + +import com.petqua.common.exception.BaseException +import com.petqua.common.exception.BaseExceptionType + +class OrderException( + private val exceptionType: OrderExceptionType, +) : BaseException() { + + override fun exceptionType(): BaseExceptionType { + return exceptionType + } +} diff --git a/src/main/kotlin/com/petqua/exception/order/OrderExceptionType.kt b/src/main/kotlin/com/petqua/exception/order/OrderExceptionType.kt new file mode 100644 index 00000000..1bfc022f --- /dev/null +++ b/src/main/kotlin/com/petqua/exception/order/OrderExceptionType.kt @@ -0,0 +1,29 @@ +package com.petqua.exception.order + +import com.petqua.common.exception.BaseExceptionType +import org.springframework.http.HttpStatus +import org.springframework.http.HttpStatus.BAD_REQUEST + +enum class OrderExceptionType( + private val httpStatus: HttpStatus, + private val code: String, + private val errorMessage: String, +) : BaseExceptionType { + + PRODUCT_NOT_FOUND(BAD_REQUEST, "O01", "주문한 상품이 존재하지 않습니다."), + + ORDER_PRICE_NOT_MATCH(BAD_REQUEST, "O10", "주문한 상품의 가격이 일치하지 않습니다."), + ; + + override fun httpStatus(): HttpStatus { + return httpStatus + } + + override fun code(): String { + return code + } + + override fun errorMessage(): String { + return errorMessage + } +} diff --git a/src/main/kotlin/com/petqua/exception/order/ShippingAddressExceptionType.kt b/src/main/kotlin/com/petqua/exception/order/ShippingAddressExceptionType.kt index 6d0e7040..9059d4d3 100644 --- a/src/main/kotlin/com/petqua/exception/order/ShippingAddressExceptionType.kt +++ b/src/main/kotlin/com/petqua/exception/order/ShippingAddressExceptionType.kt @@ -2,6 +2,7 @@ package com.petqua.exception.order import com.petqua.common.exception.BaseExceptionType import org.springframework.http.HttpStatus +import org.springframework.http.HttpStatus.BAD_REQUEST enum class ShippingAddressExceptionType( private val httpStatus: HttpStatus, @@ -9,11 +10,12 @@ enum class ShippingAddressExceptionType( private val errorMessage: String, ) : BaseExceptionType { - INVALID_PHONE_NUMBER(HttpStatus.BAD_REQUEST, "SA02", "잘못된 휴대전화 번호입니다."), - EMPTY_ADDRESS(HttpStatus.BAD_REQUEST, "SA03", "주소가 입력되지 않았습니다."), - EMPTY_DETAIL_ADDRESS(HttpStatus.BAD_REQUEST, "SA04", "상세주소가 입력되지 않았습니다."), - EMPTY_NAME(HttpStatus.BAD_REQUEST, "SA05", "배송지 이름이 입력되지 않았습니다."), - EMPTY_RECEIVER(HttpStatus.BAD_REQUEST, "SA06", "받는 사람이 입력되지 않았습니다."), + NOT_FOUND_SHIPPING_ADDRESS(BAD_REQUEST, "SA01", "존재하지 않는 배송지입니다."), + INVALID_PHONE_NUMBER(BAD_REQUEST, "SA02", "잘못된 휴대전화 번호입니다."), + EMPTY_ADDRESS(BAD_REQUEST, "SA03", "주소가 입력되지 않았습니다."), + EMPTY_DETAIL_ADDRESS(BAD_REQUEST, "SA04", "상세주소가 입력되지 않았습니다."), + EMPTY_NAME(BAD_REQUEST, "SA05", "배송지 이름이 입력되지 않았습니다."), + EMPTY_RECEIVER(BAD_REQUEST, "SA06", "받는 사람이 입력되지 않았습니다."), ; override fun httpStatus(): HttpStatus { diff --git a/src/main/kotlin/com/petqua/exception/product/ProductExceptionType.kt b/src/main/kotlin/com/petqua/exception/product/ProductExceptionType.kt index 90827ed9..21bb69da 100644 --- a/src/main/kotlin/com/petqua/exception/product/ProductExceptionType.kt +++ b/src/main/kotlin/com/petqua/exception/product/ProductExceptionType.kt @@ -16,6 +16,10 @@ enum class ProductExceptionType( INVALID_SEARCH_WORD(BAD_REQUEST, "P10", "유효하지 않은 검색어입니다."), WISH_COUNT_UNDER_MINIMUM(BAD_REQUEST, "P20", "찜 개수는 0 이상이어야 합니다."), + + INVALID_PRODUCT_OPTION(BAD_REQUEST, "P30", "유효하지 않은 상품 옵션입니다."), + + INVALID_DELIVERY_METHOD(BAD_REQUEST, "P31", "유효하지 않는 배송 방법입니다."), ; override fun httpStatus(): HttpStatus { diff --git a/src/main/kotlin/com/petqua/presentation/order/OrderController.kt b/src/main/kotlin/com/petqua/presentation/order/OrderController.kt new file mode 100644 index 00000000..0d2982d6 --- /dev/null +++ b/src/main/kotlin/com/petqua/presentation/order/OrderController.kt @@ -0,0 +1,28 @@ +package com.petqua.presentation.order + +import com.petqua.application.order.OrderService +import com.petqua.application.order.dto.SaveOrderResponse +import com.petqua.domain.auth.Auth +import com.petqua.domain.auth.LoginMember +import com.petqua.presentation.order.dto.SaveOrderRequest +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 + +@RequestMapping("/orders") +@RestController +class OrderController( + private val orderService: OrderService, +) { + + @PostMapping + fun save( + @Auth loginMember: LoginMember, + @RequestBody request: SaveOrderRequest, + ): ResponseEntity { + val response = orderService.save(request.toCommand(loginMember.memberId)) + return ResponseEntity.ok(response) + } +} diff --git a/src/main/kotlin/com/petqua/presentation/order/dto/OrderDtos.kt b/src/main/kotlin/com/petqua/presentation/order/dto/OrderDtos.kt new file mode 100644 index 00000000..05fdf657 --- /dev/null +++ b/src/main/kotlin/com/petqua/presentation/order/dto/OrderDtos.kt @@ -0,0 +1,55 @@ +package com.petqua.presentation.order.dto + +import com.petqua.application.order.dto.OrderProductCommand +import com.petqua.application.order.dto.SaveOrderCommand +import com.petqua.domain.delivery.DeliveryMethod +import com.petqua.domain.product.option.Sex +import java.math.BigDecimal + +data class SaveOrderRequest( + val shippingAddressId: Long, + val shippingRequest: String?, + val orderProductRequests: List, + val totalAmount: BigDecimal, +) { + + fun toCommand(memberId: Long): SaveOrderCommand { + return SaveOrderCommand( + memberId = memberId, + shippingAddressId = shippingAddressId, + shippingRequest = shippingRequest, + orderProductCommands = orderProductRequests.map { it.toCommand() }, + totalAmount = totalAmount, + ) + } +} + +data class OrderProductRequest( + val productId: Long, + val storeId: Long, + val quantity: Int, + val originalPrice: BigDecimal, + val discountRate: Int, + val discountPrice: BigDecimal, + val orderPrice: BigDecimal, + val sex: String, + val additionalPrice: BigDecimal, + val deliveryFee: BigDecimal, + val deliveryMethod: String, +) { + + fun toCommand(): OrderProductCommand { + return OrderProductCommand( + productId = productId, + quantity = quantity, + originalPrice = originalPrice, + discountRate = discountRate, + discountPrice = discountPrice, + orderPrice = orderPrice, + sex = Sex.from(sex), + additionalPrice = additionalPrice, + deliveryFee = deliveryFee, + deliveryMethod = DeliveryMethod.from(deliveryMethod), + ) + } +} diff --git a/src/test/kotlin/com/petqua/application/order/OrderServiceTest.kt b/src/test/kotlin/com/petqua/application/order/OrderServiceTest.kt new file mode 100644 index 00000000..57181a56 --- /dev/null +++ b/src/test/kotlin/com/petqua/application/order/OrderServiceTest.kt @@ -0,0 +1,488 @@ +package com.petqua.application.order + +import com.petqua.domain.delivery.DeliveryMethod.COMMON +import com.petqua.domain.delivery.DeliveryMethod.SAFETY +import com.petqua.domain.member.MemberRepository +import com.petqua.domain.order.OrderRepository +import com.petqua.domain.order.OrderStatus.ORDER_CREATED +import com.petqua.domain.order.ShippingAddressRepository +import com.petqua.domain.product.ProductRepository +import com.petqua.domain.product.option.ProductOptionRepository +import com.petqua.domain.product.option.Sex +import com.petqua.domain.product.option.Sex.FEMALE +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.NOT_FOUND_SHIPPING_ADDRESS +import com.petqua.exception.product.ProductException +import com.petqua.exception.product.ProductExceptionType.INVALID_PRODUCT_OPTION +import com.petqua.test.DataCleaner +import com.petqua.test.fixture.member +import com.petqua.test.fixture.orderProductCommand +import com.petqua.test.fixture.product +import com.petqua.test.fixture.productOption +import com.petqua.test.fixture.saveOrderCommand +import com.petqua.test.fixture.shippingAddress +import com.petqua.test.fixture.store +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.core.spec.style.BehaviorSpec +import io.kotest.inspectors.forAll +import io.kotest.matchers.shouldBe +import java.math.BigDecimal +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment.NONE + +@SpringBootTest(webEnvironment = NONE) +class OrderServiceTest( + private val orderService: OrderService, + private val orderRepository: OrderRepository, + private val productRepository: ProductRepository, + private val storeRepository: StoreRepository, + private val memberRepository: MemberRepository, + private val productOptionRepository: ProductOptionRepository, + private val shippingAddressRepository: ShippingAddressRepository, + private val dataCleaner: DataCleaner, +) : BehaviorSpec({ + + Given("주문을 저장 할 때") { + + When("상품이 존재 하지 않으면") { + val memberId = memberRepository.save(member()).id + val productA = productRepository.save(product()) + val productB = productRepository.save(product()) + val orderProductCommands = listOf( + orderProductCommand( + productId = productA.id, + ), + orderProductCommand( + productId = productB.id, + ), + orderProductCommand( + productId = Long.MIN_VALUE, + ), + ) + val command = saveOrderCommand( + memberId = memberId, + orderProductCommands = orderProductCommands, + ) + + Then("예외가 발생 한다") { + shouldThrow { + orderService.save(command) + }.exceptionType() shouldBe (PRODUCT_NOT_FOUND) + } + } + + When("유효하지 않은 상품 옵션을 입력하면") { + val memberId = memberRepository.save(member()).id + val productA = productRepository.save(product()) + val productB = productRepository.save(product()) + val productOptionA = productOptionRepository.save( + productOption( + productId = productA.id, + sex = FEMALE, + additionalPrice = BigDecimal.ONE + ) + ) + val productOptionB = productOptionRepository.save( + productOption( + productId = productB.id, + sex = Sex.HERMAPHRODITE, + additionalPrice = BigDecimal.TEN + ) + ) + val orderProductCommands = listOf( + orderProductCommand( + productId = productA.id, + sex = Sex.HERMAPHRODITE, + ), + orderProductCommand( + productId = productB.id, + sex = productOptionB.sex, + additionalPrice = productOptionB.additionalPrice, + ), + ) + val command = saveOrderCommand( + memberId = memberId, + orderProductCommands = orderProductCommands, + ) + + Then("예외가 발생 한다") { + shouldThrow { + orderService.save(command) + }.exceptionType() shouldBe (INVALID_PRODUCT_OPTION) + } + } + + When("존재 하지 않는 배송 정보를 입력 하면") { + val memberId = memberRepository.save(member()).id + val productA = productRepository.save(product()) + val productOptionA = productOptionRepository.save( + productOption( + productId = productA.id, + sex = FEMALE, + additionalPrice = 1.toBigDecimal() + ) + ) + val orderProductCommands = listOf( + orderProductCommand( + productId = productA.id, + sex = productOptionA.sex, + additionalPrice = productOptionA.additionalPrice, + ), + ) + + val command = saveOrderCommand( + memberId = memberId, + orderProductCommands = orderProductCommands, + shippingAddressId = Long.MIN_VALUE, + ) + + Then("예외가 발생 한다") { + shouldThrow { + orderService.save(command) + }.exceptionType() shouldBe (NOT_FOUND_SHIPPING_ADDRESS) + } + } + + When("주문 상품과 실제 상품의 가격이 다르면") { + val memberId = memberRepository.save(member()).id + val productA = productRepository.save( + product( + price = BigDecimal.TEN, + discountRate = 10, + discountPrice = 9.toBigDecimal(), + commonDeliveryFee = 3000.toBigDecimal(), + ) + ) + val productB = productRepository.save( + product( + price = BigDecimal.TEN, + discountRate = 10, + discountPrice = 9.toBigDecimal(), + safeDeliveryFee = 5000.toBigDecimal(), + ) + ) + val productOptionA = productOptionRepository.save( + productOption( + productId = productA.id, + sex = FEMALE, + additionalPrice = 1.toBigDecimal() + ) + ) + val productOptionB = productOptionRepository.save( + productOption( + productId = productB.id, + sex = Sex.HERMAPHRODITE, + additionalPrice = 10.toBigDecimal() + ) + ) + val shippingAddress = shippingAddressRepository.save( + shippingAddress( + memberId = memberId, + ) + ) + val orderProductCommands = listOf( + orderProductCommand( + productId = productA.id, + originalPrice = productA.price, + discountRate = productA.discountRate, + discountPrice = productA.discountPrice, + sex = productOptionA.sex, + orderPrice = BigDecimal.TEN, + additionalPrice = productOptionA.additionalPrice, + deliveryMethod = COMMON, + ), + orderProductCommand( + productId = productB.id, + originalPrice = productB.price, + discountRate = productB.discountRate, + discountPrice = productB.discountPrice, + sex = productOptionB.sex, + orderPrice = 9.toBigDecimal(), + additionalPrice = productOptionB.additionalPrice, + deliveryMethod = SAFETY, + ), + ) + val command = saveOrderCommand( + memberId = memberId, + orderProductCommands = orderProductCommands, + shippingAddressId = shippingAddress.id, + ) + + Then("예외가 발생 한다") { + shouldThrow { + orderService.save(command) + }.exceptionType() shouldBe (ORDER_PRICE_NOT_MATCH) + } + } + + When("주문 상품과 실제 상품의 배송비가 다르면") { + val memberId = memberRepository.save(member()).id + val productA = productRepository.save( + product( + commonDeliveryFee = 3000.toBigDecimal(), + ) + ) + val productB = productRepository.save( + product( + safeDeliveryFee = 5000.toBigDecimal(), + ) + ) + val productOptionA = productOptionRepository.save( + productOption( + productId = productA.id, + sex = FEMALE, + additionalPrice = 1.toBigDecimal() + ) + ) + val productOptionB = productOptionRepository.save( + productOption( + productId = productB.id, + sex = Sex.HERMAPHRODITE, + additionalPrice = 10.toBigDecimal() + ) + ) + val shippingAddress = shippingAddressRepository.save( + shippingAddress( + memberId = memberId, + ) + ) + val orderProductCommands = listOf( + orderProductCommand( + productId = productA.id, + originalPrice = productA.price, + discountRate = productA.discountRate, + discountPrice = productA.discountPrice, + sex = productOptionA.sex, + orderPrice = BigDecimal.TEN, + additionalPrice = productOptionA.additionalPrice, + deliveryFee = 1.toBigDecimal(), + ), + orderProductCommand( + productId = productB.id, + originalPrice = productB.price, + discountRate = productB.discountRate, + discountPrice = productB.discountPrice, + sex = productOptionB.sex, + orderPrice = 9.toBigDecimal(), + additionalPrice = productOptionB.additionalPrice, + deliveryFee = 5000.toBigDecimal(), + ), + ) + val command = saveOrderCommand( + memberId = memberId, + orderProductCommands = orderProductCommands, + shippingAddressId = shippingAddress.id, + ) + + Then("예외가 발생 한다") { + shouldThrow { + orderService.save(command) + }.exceptionType() shouldBe (ORDER_PRICE_NOT_MATCH) + } + } + + When("전체 주문 금액이 일치 하지 않으면") { + val memberId = memberRepository.save(member()).id + val productA1 = productRepository.save( + product( + storeId = 1L, + commonDeliveryFee = 3000.toBigDecimal(), + ) + ) + val productA2 = productRepository.save( + product( + storeId = 1L, + commonDeliveryFee = 3000.toBigDecimal(), + ) + ) + val productB = productRepository.save( + product( + storeId = 2L, + safeDeliveryFee = 5000.toBigDecimal(), + ) + ) + + val productOptionA1 = productOptionRepository.save( + productOption( + productId = productA1.id, + sex = FEMALE, + ) + ) + val productOptionA2 = productOptionRepository.save( + productOption( + productId = productA2.id, + sex = FEMALE, + ) + ) + val productOptionB = productOptionRepository.save( + productOption( + productId = productB.id, + sex = Sex.HERMAPHRODITE + ) + ) + val shippingAddress = shippingAddressRepository.save( + shippingAddress( + memberId = memberId, + ) + ) + val orderProductCommands = listOf( + orderProductCommand( + productId = productA1.id, + originalPrice = productA1.price, + discountRate = productA1.discountRate, + discountPrice = productA1.discountPrice, + sex = productOptionA1.sex, + orderPrice = BigDecimal.ONE, + additionalPrice = productOptionA1.additionalPrice, + deliveryFee = 3000.toBigDecimal(), + deliveryMethod = COMMON, + ), + orderProductCommand( + productId = productA2.id, + originalPrice = productA2.price, + discountRate = productA2.discountRate, + discountPrice = productA2.discountPrice, + sex = productOptionA2.sex, + orderPrice = BigDecimal.ONE, + additionalPrice = productOptionA2.additionalPrice, + deliveryFee = 3000.toBigDecimal(), + deliveryMethod = COMMON, + ), + orderProductCommand( + productId = productB.id, + originalPrice = productB.price, + discountRate = productB.discountRate, + discountPrice = productB.discountPrice, + sex = productOptionB.sex, + orderPrice = BigDecimal.ONE, + additionalPrice = productOptionB.additionalPrice, + deliveryFee = 5000.toBigDecimal(), + deliveryMethod = SAFETY, + ), + ) + val command = saveOrderCommand( + memberId = memberId, + orderProductCommands = orderProductCommands, + shippingAddressId = shippingAddress.id, + totalAmount = 11003.toBigDecimal(), + ) + + Then("예외가 발생 한다") { + shouldThrow { + orderService.save(command) + }.exceptionType() shouldBe (ORDER_PRICE_NOT_MATCH) + } + } + } + + Given("주문 생성을") { + + When("명령하면") { + val storeAId = storeRepository.save(store()).id + val storeBId = storeRepository.save(store()).id + val memberId = memberRepository.save(member()).id + val productA1 = productRepository.save( + product( + storeId = storeAId, + commonDeliveryFee = 3000.toBigDecimal(), + ) + ) + val productA2 = productRepository.save( + product( + storeId = storeAId, + commonDeliveryFee = 3000.toBigDecimal(), + ) + ) + val productB = productRepository.save( + product( + storeId = storeBId, + safeDeliveryFee = 5000.toBigDecimal(), + ) + ) + + val productOptionA1 = productOptionRepository.save( + productOption( + productId = productA1.id, + sex = FEMALE, + ) + ) + val productOptionA2 = productOptionRepository.save( + productOption( + productId = productA2.id, + sex = FEMALE, + ) + ) + val productOptionB = productOptionRepository.save( + productOption( + productId = productB.id, + sex = Sex.HERMAPHRODITE + ) + ) + val shippingAddress = shippingAddressRepository.save( + shippingAddress( + memberId = memberId, + ) + ) + val orderProductCommands = listOf( + orderProductCommand( + productId = productA1.id, + originalPrice = productA1.price, + discountRate = productA1.discountRate, + discountPrice = productA1.discountPrice, + sex = productOptionA1.sex, + orderPrice = BigDecimal.ONE, + additionalPrice = productOptionA1.additionalPrice, + deliveryFee = 3000.toBigDecimal(), + deliveryMethod = COMMON, + ), + orderProductCommand( + productId = productA2.id, + originalPrice = productA2.price, + discountRate = productA2.discountRate, + discountPrice = productA2.discountPrice, + sex = productOptionA2.sex, + orderPrice = BigDecimal.ONE, + additionalPrice = productOptionA2.additionalPrice, + deliveryFee = 3000.toBigDecimal(), + deliveryMethod = COMMON, + ), + orderProductCommand( + productId = productB.id, + originalPrice = productB.price, + discountRate = productB.discountRate, + discountPrice = productB.discountPrice, + sex = productOptionB.sex, + orderPrice = BigDecimal.ONE, + additionalPrice = productOptionB.additionalPrice, + deliveryFee = 5000.toBigDecimal(), + deliveryMethod = SAFETY, + ), + ) + val command = saveOrderCommand( + memberId = memberId, + orderProductCommands = orderProductCommands, + shippingAddressId = shippingAddress.id, + totalAmount = 8003.toBigDecimal(), + ) + val response = orderService.save(command) + + Then("주문이 저장된다") { + val orders = orderRepository.findAll() + orders.forAll { + it.orderNumber.value shouldBe response.orderId + it.orderName.value shouldBe response.orderName + it.status shouldBe ORDER_CREATED + } + orders.distinctBy { it.orderProduct.shippingNumber }.size shouldBe 2 + } + } + } + + afterContainer { + dataCleaner.clean() + } +}) diff --git a/src/test/kotlin/com/petqua/application/product/ProductServiceTest.kt b/src/test/kotlin/com/petqua/application/product/ProductServiceTest.kt index 879f2fec..72858fe1 100644 --- a/src/test/kotlin/com/petqua/application/product/ProductServiceTest.kt +++ b/src/test/kotlin/com/petqua/application/product/ProductServiceTest.kt @@ -45,10 +45,10 @@ import com.petqua.test.fixture.wishProduct import io.kotest.assertions.throwables.shouldThrow import io.kotest.core.spec.style.BehaviorSpec import io.kotest.matchers.shouldBe -import org.springframework.boot.test.context.SpringBootTest -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment import java.math.BigDecimal import kotlin.Long.Companion.MIN_VALUE +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment @SpringBootTest(webEnvironment = WebEnvironment.NONE) class ProductServiceTest( @@ -92,11 +92,6 @@ class ProductServiceTest( temperament = Temperament.PEACEFUL, ) ) - val productOption = productOptionRepository.save( - productOption( - sex = FEMALE, - ) - ) val product = productRepository.save( product( name = "고정구피", @@ -105,11 +100,16 @@ class ProductServiceTest( discountPrice = BigDecimal.ZERO, reviewCount = 0, reviewTotalScore = 0, - productOptionId = productOption.id, productDescriptionId = productDescription.id, productInfoId = productInfo.id, ) ) + val productOption = productOptionRepository.save( + productOption( + productId = product.id, + sex = FEMALE, + ) + ) val productImage = productImageRepository.save( productImage( productId = product.id, diff --git a/src/test/kotlin/com/petqua/application/product/category/CategoryServiceTest.kt b/src/test/kotlin/com/petqua/application/product/category/CategoryServiceTest.kt index 2cd35940..29a4e67f 100644 --- a/src/test/kotlin/com/petqua/application/product/category/CategoryServiceTest.kt +++ b/src/test/kotlin/com/petqua/application/product/category/CategoryServiceTest.kt @@ -23,9 +23,9 @@ import io.kotest.assertions.assertSoftly import io.kotest.core.spec.style.BehaviorSpec import io.kotest.matchers.collections.shouldContainExactly import io.kotest.matchers.shouldBe +import java.math.BigDecimal import org.springframework.boot.test.context.SpringBootTest import org.springframework.boot.test.context.SpringBootTest.WebEnvironment.NONE -import java.math.BigDecimal @SpringBootTest(webEnvironment = NONE) class CategoryServiceTest( @@ -79,9 +79,9 @@ class CategoryServiceTest( categoryId = category1.id, discountPrice = BigDecimal.ZERO, reviewCount = 0, - canDeliverySafely = false, - canDeliveryCommonly = false, - canPickUp = true, + safeDeliveryFee = null, + commonDeliveryFee = null, + pickUpDeliveryFee = 0.toBigDecimal(), ) ) val product2 = productRepository.save( @@ -91,9 +91,9 @@ class CategoryServiceTest( categoryId = category2.id, discountPrice = BigDecimal.ONE, reviewCount = 2, - canDeliverySafely = false, - canDeliveryCommonly = true, - canPickUp = true, + safeDeliveryFee = null, + commonDeliveryFee = 3000.toBigDecimal(), + pickUpDeliveryFee = 0.toBigDecimal(), ) ) val product3 = productRepository.save( @@ -103,9 +103,9 @@ class CategoryServiceTest( categoryId = category2.id, discountPrice = BigDecimal.TEN, reviewCount = 1, - canDeliverySafely = true, - canDeliveryCommonly = true, - canPickUp = false, + safeDeliveryFee = 5000.toBigDecimal(), + commonDeliveryFee = 3000.toBigDecimal(), + pickUpDeliveryFee = null, ) ) wishProductRepository.save( diff --git a/src/test/kotlin/com/petqua/domain/product/ProductCustomRepositoryImplTest.kt b/src/test/kotlin/com/petqua/domain/product/ProductCustomRepositoryImplTest.kt index c120a107..3440d6c8 100644 --- a/src/test/kotlin/com/petqua/domain/product/ProductCustomRepositoryImplTest.kt +++ b/src/test/kotlin/com/petqua/domain/product/ProductCustomRepositoryImplTest.kt @@ -40,11 +40,11 @@ import io.kotest.core.spec.style.BehaviorSpec import io.kotest.matchers.collections.shouldContainExactly import io.kotest.matchers.collections.shouldHaveSize import io.kotest.matchers.shouldBe -import org.springframework.boot.test.context.SpringBootTest import java.math.BigDecimal.ONE import java.math.BigDecimal.TEN import java.math.BigDecimal.ZERO import kotlin.Long.Companion.MIN_VALUE +import org.springframework.boot.test.context.SpringBootTest @SpringBootTest class ProductCustomRepositoryImplTest( @@ -85,16 +85,6 @@ class ProductCustomRepositoryImplTest( temperament = PEACEFUL, ) ) - val productOption1 = productOptionRepository.save( - productOption( - sex = Sex.MALE, - ) - ) - val productOption2 = productOptionRepository.save( - productOption( - sex = Sex.MALE, - ) - ) val productDescription1 = productDescriptionRepository.save( productDescription( title = "물생활 핵 인싸어, 레드 브론즈 구피", @@ -109,7 +99,6 @@ class ProductCustomRepositoryImplTest( discountPrice = ZERO, reviewCount = 0, reviewTotalScore = 0, - productOptionId = productOption1.id, productDescriptionId = productDescription1.id, productInfoId = productInfo1.id, ) @@ -122,10 +111,21 @@ class ProductCustomRepositoryImplTest( discountPrice = ZERO, reviewCount = 0, reviewTotalScore = 0, - productOptionId = productOption2.id, productInfoId = productInfo2.id, ) ) + val productOption1 = productOptionRepository.save( + productOption( + productId = product1.id, + sex = Sex.MALE, + ) + ) + val productOption2 = productOptionRepository.save( + productOption( + productId = product2.id, + sex = Sex.MALE, + ) + ) When("Id를 입력하면") { val productWithInfoResponse = productRepository.findProductWithInfoByIdOrThrow(product1.id) { @@ -347,9 +347,9 @@ class ProductCustomRepositoryImplTest( discountPrice = ZERO, reviewCount = 0, reviewTotalScore = 0, - canDeliverySafely = false, - canDeliveryCommonly = false, - canPickUp = true, + safeDeliveryFee = null, + commonDeliveryFee = null, + pickUpDeliveryFee = 0.toBigDecimal(), ) ) val product2 = productRepository.save( @@ -359,9 +359,9 @@ class ProductCustomRepositoryImplTest( discountPrice = ONE, reviewCount = 1, reviewTotalScore = 1, - canDeliverySafely = false, - canDeliveryCommonly = true, - canPickUp = true, + safeDeliveryFee = null, + commonDeliveryFee = 3000.toBigDecimal(), + pickUpDeliveryFee = 0.toBigDecimal(), ) ) val product3 = productRepository.save( @@ -371,9 +371,9 @@ class ProductCustomRepositoryImplTest( discountPrice = ONE, reviewCount = 1, reviewTotalScore = 5, - canDeliverySafely = true, - canDeliveryCommonly = false, - canPickUp = true, + safeDeliveryFee = 5000.toBigDecimal(), + commonDeliveryFee = null, + pickUpDeliveryFee = 0.toBigDecimal(), ) ) val product4 = productRepository.save( @@ -383,9 +383,9 @@ class ProductCustomRepositoryImplTest( discountPrice = TEN, reviewCount = 2, reviewTotalScore = 10, - canDeliverySafely = true, - canDeliveryCommonly = true, - canPickUp = true, + safeDeliveryFee = 5000.toBigDecimal(), + commonDeliveryFee = 3000.toBigDecimal(), + pickUpDeliveryFee = 0.toBigDecimal(), ) ) diff --git a/src/test/kotlin/com/petqua/domain/product/ProductTest.kt b/src/test/kotlin/com/petqua/domain/product/ProductTest.kt index 76cd9528..cdef297c 100644 --- a/src/test/kotlin/com/petqua/domain/product/ProductTest.kt +++ b/src/test/kotlin/com/petqua/domain/product/ProductTest.kt @@ -1,8 +1,10 @@ package com.petqua.domain.product +import com.petqua.domain.order.OrderNumber import com.petqua.test.fixture.product import io.kotest.core.spec.style.BehaviorSpec import io.kotest.matchers.shouldBe +import org.junit.jupiter.api.Test class ProductTest : BehaviorSpec({ @@ -20,4 +22,11 @@ class ProductTest : BehaviorSpec({ } } } -}) + + +}) { + @Test + fun test() { + println(OrderNumber.generate().value) + } +} diff --git a/src/test/kotlin/com/petqua/domain/product/category/CategoryCustomRepositoryImplTest.kt b/src/test/kotlin/com/petqua/domain/product/category/CategoryCustomRepositoryImplTest.kt index f662eb6f..e5e26e08 100644 --- a/src/test/kotlin/com/petqua/domain/product/category/CategoryCustomRepositoryImplTest.kt +++ b/src/test/kotlin/com/petqua/domain/product/category/CategoryCustomRepositoryImplTest.kt @@ -19,8 +19,8 @@ import io.kotest.core.spec.style.BehaviorSpec import io.kotest.matchers.collections.shouldContainExactly import io.kotest.matchers.collections.shouldHaveSize import io.kotest.matchers.shouldBe -import org.springframework.boot.test.context.SpringBootTest import java.math.BigDecimal +import org.springframework.boot.test.context.SpringBootTest @SpringBootTest class CategoryCustomRepositoryImplTest( @@ -42,9 +42,9 @@ class CategoryCustomRepositoryImplTest( categoryId = category1.id, discountPrice = BigDecimal.ZERO, reviewCount = 0, - canDeliverySafely = false, - canDeliveryCommonly = false, - canPickUp = true, + safeDeliveryFee = null, + commonDeliveryFee = null, + pickUpDeliveryFee = 0.toBigDecimal(), ) ) val product2 = productRepository.save( @@ -54,9 +54,9 @@ class CategoryCustomRepositoryImplTest( categoryId = category2.id, discountPrice = BigDecimal.ONE, reviewCount = 2, - canDeliverySafely = false, - canDeliveryCommonly = true, - canPickUp = true, + safeDeliveryFee = null, + commonDeliveryFee = 3000.toBigDecimal(), + pickUpDeliveryFee = 0.toBigDecimal(), ) ) val product3 = productRepository.save( @@ -66,9 +66,9 @@ class CategoryCustomRepositoryImplTest( categoryId = category2.id, discountPrice = BigDecimal.TEN, reviewCount = 1, - canDeliverySafely = true, - canDeliveryCommonly = true, - canPickUp = false, + safeDeliveryFee = 5000.toBigDecimal(), + commonDeliveryFee = 3000.toBigDecimal(), + pickUpDeliveryFee = null, ) ) @@ -255,25 +255,25 @@ class CategoryCustomRepositoryImplTest( name = "고정구피", storeId = store.id, categoryId = category1.id, - canDeliverySafely = false, - canDeliveryCommonly = false, - canPickUp = true, + safeDeliveryFee = null, + commonDeliveryFee = null, + pickUpDeliveryFee = 0.toBigDecimal(), ), product( name = "팬시구피", storeId = store.id, categoryId = category2.id, - canDeliverySafely = false, - canDeliveryCommonly = true, - canPickUp = true, + safeDeliveryFee = null, + commonDeliveryFee = 3000.toBigDecimal(), + pickUpDeliveryFee = 0.toBigDecimal(), ), product( name = "팬시구피 세트", storeId = store.id, categoryId = category2.id, - canDeliverySafely = true, - canDeliveryCommonly = true, - canPickUp = false, + safeDeliveryFee = 5000.toBigDecimal(), + commonDeliveryFee = 3000.toBigDecimal(), + pickUpDeliveryFee = null, ) ) ) diff --git a/src/test/kotlin/com/petqua/presentation/product/ProductControllerTest.kt b/src/test/kotlin/com/petqua/presentation/product/ProductControllerTest.kt index 611e415c..05d0f012 100644 --- a/src/test/kotlin/com/petqua/presentation/product/ProductControllerTest.kt +++ b/src/test/kotlin/com/petqua/presentation/product/ProductControllerTest.kt @@ -49,13 +49,13 @@ import io.kotest.matchers.collections.shouldContainExactly import io.kotest.matchers.shouldBe import io.kotest.matchers.string.shouldContain import io.restassured.common.mapper.TypeRef -import org.springframework.http.HttpStatus -import org.springframework.http.HttpStatus.BAD_REQUEST -import org.springframework.http.HttpStatus.NOT_FOUND import java.math.BigDecimal.ONE import java.math.BigDecimal.TEN import java.math.BigDecimal.ZERO import kotlin.Long.Companion.MIN_VALUE +import org.springframework.http.HttpStatus +import org.springframework.http.HttpStatus.BAD_REQUEST +import org.springframework.http.HttpStatus.NOT_FOUND class ProductControllerTest( private val productRepository: ProductRepository, @@ -98,11 +98,6 @@ class ProductControllerTest( temperament = Temperament.PEACEFUL, ) ) - val productOption = productOptionRepository.save( - productOption( - sex = HERMAPHRODITE - ) - ) val product = productRepository.save( product( name = "고정구피", @@ -111,11 +106,16 @@ class ProductControllerTest( discountPrice = ZERO, reviewCount = 0, reviewTotalScore = 0, - productOptionId = productOption.id, productDescriptionId = productDescription.id, productInfoId = productInfo.id ) ) + val productOption = productOptionRepository.save( + productOption( + productId = product.id, + sex = HERMAPHRODITE + ) + ) val productImage = productImageRepository.save( productImage( productId = product.id, @@ -545,9 +545,9 @@ class ProductControllerTest( name = "블루네온 구피", storeId = store.id, reviewCount = 2, - canDeliverySafely = false, - canDeliveryCommonly = false, - canPickUp = true, + safeDeliveryFee = null, + commonDeliveryFee = null, + pickUpDeliveryFee = 0.toBigDecimal(), ) ) val product2 = productRepository.save( @@ -555,9 +555,9 @@ class ProductControllerTest( name = "레드턱시도 구피", storeId = store.id, reviewCount = 3, - canDeliverySafely = false, - canDeliveryCommonly = true, - canPickUp = true, + safeDeliveryFee = null, + commonDeliveryFee = 3000.toBigDecimal(), + pickUpDeliveryFee = 0.toBigDecimal(), ) ) val product3 = productRepository.save( @@ -565,9 +565,9 @@ class ProductControllerTest( name = "열대어 브론디 턱시도 구피", storeId = store.id, reviewCount = 1, - canDeliverySafely = true, - canDeliveryCommonly = false, - canPickUp = true, + safeDeliveryFee = 5000.toBigDecimal(), + commonDeliveryFee = null, + pickUpDeliveryFee = 0.toBigDecimal(), ) ) val product4 = productRepository.save( @@ -575,9 +575,9 @@ class ProductControllerTest( name = "단정 금붕어", storeId = store.id, reviewCount = 0, - canDeliverySafely = true, - canDeliveryCommonly = true, - canPickUp = true, + safeDeliveryFee = 5000.toBigDecimal(), + commonDeliveryFee = 3000.toBigDecimal(), + pickUpDeliveryFee = 0.toBigDecimal(), ) ) diff --git a/src/test/kotlin/com/petqua/presentation/product/category/CategoryControllerTest.kt b/src/test/kotlin/com/petqua/presentation/product/category/CategoryControllerTest.kt index b4fa1893..f3b1257e 100644 --- a/src/test/kotlin/com/petqua/presentation/product/category/CategoryControllerTest.kt +++ b/src/test/kotlin/com/petqua/presentation/product/category/CategoryControllerTest.kt @@ -22,9 +22,9 @@ import io.kotest.matchers.collections.shouldHaveSize import io.kotest.matchers.shouldBe import io.kotest.matchers.string.shouldContain import io.restassured.common.mapper.TypeRef +import java.math.BigDecimal import org.springframework.http.HttpStatus.BAD_REQUEST import org.springframework.http.HttpStatus.OK -import java.math.BigDecimal class CategoryControllerTest( private val categoryRepository: CategoryRepository, @@ -93,9 +93,9 @@ class CategoryControllerTest( categoryId = category1.id, discountPrice = BigDecimal.ZERO, reviewCount = 0, - canDeliverySafely = false, - canDeliveryCommonly = false, - canPickUp = true, + safeDeliveryFee = null, + commonDeliveryFee = null, + pickUpDeliveryFee = 0.toBigDecimal(), ) ) val product2 = productRepository.save( @@ -105,9 +105,9 @@ class CategoryControllerTest( categoryId = category2.id, discountPrice = BigDecimal.ONE, reviewCount = 2, - canDeliverySafely = false, - canDeliveryCommonly = true, - canPickUp = true, + safeDeliveryFee = null, + commonDeliveryFee = 3000.toBigDecimal(), + pickUpDeliveryFee = 0.toBigDecimal(), ) ) val product3 = productRepository.save( @@ -117,9 +117,9 @@ class CategoryControllerTest( categoryId = category2.id, discountPrice = BigDecimal.TEN, reviewCount = 1, - canDeliverySafely = true, - canDeliveryCommonly = true, - canPickUp = false, + safeDeliveryFee = 5000.toBigDecimal(), + commonDeliveryFee = 3000.toBigDecimal(), + pickUpDeliveryFee = null, ) ) diff --git a/src/test/kotlin/com/petqua/test/fixture/OrderFixture.kt b/src/test/kotlin/com/petqua/test/fixture/OrderFixture.kt new file mode 100644 index 00000000..81e47718 --- /dev/null +++ b/src/test/kotlin/com/petqua/test/fixture/OrderFixture.kt @@ -0,0 +1,55 @@ +package com.petqua.test.fixture + +import com.petqua.application.order.dto.OrderProductCommand +import com.petqua.application.order.dto.SaveOrderCommand +import com.petqua.domain.delivery.DeliveryMethod +import com.petqua.domain.delivery.DeliveryMethod.COMMON +import com.petqua.domain.product.option.Sex +import com.petqua.domain.product.option.Sex.FEMALE +import java.math.BigDecimal +import java.math.BigDecimal.ONE +import java.math.BigDecimal.ZERO + +private const val DEFAULT_SCALE = 2 + +fun saveOrderCommand( + memberId: Long = 0L, + shippingAddressId: Long = 0L, + shippingRequest: String? = null, + orderProductCommands: List, + totalAmount: BigDecimal = ONE * orderProductCommands.size.toBigDecimal(), +): SaveOrderCommand { + return SaveOrderCommand( + memberId = memberId, + shippingAddressId = shippingAddressId, + shippingRequest = shippingRequest, + orderProductCommands = orderProductCommands, + totalAmount = totalAmount.setScale(DEFAULT_SCALE), + ) +} + +fun orderProductCommand( + productId: Long = 0L, + quantity: Int = 1, + originalPrice: BigDecimal = ONE, + discountRate: Int = 0, + discountPrice: BigDecimal = originalPrice, + orderPrice: BigDecimal = ONE, + sex: Sex = FEMALE, + additionalPrice: BigDecimal = ZERO, + deliveryFee: BigDecimal = 3000.toBigDecimal(), + deliveryMethod: DeliveryMethod = COMMON, +): OrderProductCommand { + return OrderProductCommand( + productId = productId, + quantity = quantity, + originalPrice = originalPrice.setScale(DEFAULT_SCALE), + discountRate = discountRate, + discountPrice = discountPrice.setScale(DEFAULT_SCALE), + orderPrice = orderPrice.setScale(DEFAULT_SCALE), + sex = sex, + additionalPrice = additionalPrice.setScale(DEFAULT_SCALE), + deliveryFee = deliveryFee.setScale(DEFAULT_SCALE), + deliveryMethod = deliveryMethod, + ) +} diff --git a/src/test/kotlin/com/petqua/test/fixture/ProductFixtures.kt b/src/test/kotlin/com/petqua/test/fixture/ProductFixtures.kt index 11e5c389..e0c447ce 100644 --- a/src/test/kotlin/com/petqua/test/fixture/ProductFixtures.kt +++ b/src/test/kotlin/com/petqua/test/fixture/ProductFixtures.kt @@ -35,10 +35,9 @@ fun product( reviewTotalScore: Int = 0, thumbnailUrl: String = "image.jpg", isDeleted: Boolean = false, - canDeliverySafely: Boolean = true, - canDeliveryCommonly: Boolean = true, - canPickUp: Boolean = true, - productOptionId: Long = 0, + safeDeliveryFee: BigDecimal? = null, + commonDeliveryFee: BigDecimal? = null, + pickUpDeliveryFee: BigDecimal? = null, productDescriptionId: Long? = null, productInfoId: Long = 0, ): Product { @@ -55,10 +54,9 @@ fun product( reviewTotalScore = reviewTotalScore, thumbnailUrl = thumbnailUrl, isDeleted = isDeleted, - canDeliverSafely = canDeliverySafely, - canDeliverCommonly = canDeliveryCommonly, - canPickUp = canPickUp, - productOptionId = productOptionId, + safeDeliveryFee = safeDeliveryFee?.setScale(DEFAULT_SCALE), + commonDeliveryFee = commonDeliveryFee?.setScale(DEFAULT_SCALE), + pickUpDeliveryFee = pickUpDeliveryFee?.setScale(DEFAULT_SCALE), productDescriptionId = productDescriptionId, productInfoId = productInfoId ) @@ -109,12 +107,14 @@ fun productImage( } fun productOption( - id: Long = 0, + id: Long = 0L, + productId: Long = 0L, sex: Sex, additionalPrice: BigDecimal = BigDecimal.ZERO, ): ProductOption { return ProductOption( id = id, + productId = productId, sex = sex, additionalPrice = additionalPrice, ) @@ -159,9 +159,9 @@ fun productDetailResponse( descriptionTitle = productDescription.title.value, descriptionContent = productDescription.content.value, descriptionImageUrls = descriptionImageUrls, - canDeliverSafely = product.canDeliverSafely, - canDeliverCommonly = product.canDeliverCommonly, - canPickUp = product.canPickUp, + safeDeliveryFee = product.safeDeliveryFee, + commonDeliveryFee = product.commonDeliveryFee, + pickUpDeliveryFee = product.pickUpDeliveryFee, optimalTemperatureMin = productInfo.optimalTemperature.optimalTemperatureMin, optimalTemperatureMax = productInfo.optimalTemperature.optimalTemperatureMax, difficultyLevel = productInfo.difficultyLevel.description, @@ -189,9 +189,9 @@ fun productResponse( reviewCount = product.reviewCount, reviewAverageScore = product.averageReviewScore(), thumbnailUrl = product.thumbnailUrl, - canDeliverSafely = product.canDeliverSafely, - canDeliverCommonly = product.canDeliverCommonly, - canPickUp = product.canPickUp, + safeDeliveryFee = product.safeDeliveryFee, + commonDeliveryFee = product.commonDeliveryFee, + pickUpDeliveryFee = product.pickUpDeliveryFee, isWished = isWished, ) }