Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -76,32 +76,29 @@ class OrderService(
}

fun acceptOrder(orderId: String, ownerId: Long) {
val order = orderRepository.findById(orderId) ?: throw OrderException.OrderNotFound(orderId)
val store = order.storeId?.let { storeRepository.findById(it) } ?: throw OrderException.StoreNotFound(order.storeId.toString())

if (ownerId.toString() != store.ownerId) {
throw OrderException.OrderCanNotAccept(orderId)
}
orderLockManager.lock(orderId) {
val order = orderLockManager.lock(orderId) {
val order = orderRepository.findById(orderId) ?: throw OrderException.OrderNotFound(orderId)
val store = order.storeId?.let { storeRepository.findById(it) } ?: throw OrderException.StoreNotFound(order.storeId.toString())
if (ownerId.toString() != store.ownerId) {
throw OrderException.OrderCanNotAccept(orderId)
}
order.accept()
orderRepository.save(order)
}
orderRepository.save(order)

eventPublisher.publishEvent(OrderDetailStatusEvent(orderId, order.status))
}

fun refuseOrder(orderId: String, ownerId: Long) {
val order = orderRepository.findById(orderId) ?: throw OrderException.OrderNotFound(orderId)
val store = order.storeId?.let { storeRepository.findById(it) } ?: throw OrderException.StoreNotFound(order.storeId.toString())
val order = orderLockManager.lock(orderId) {
val order = orderRepository.findById(orderId) ?: throw OrderException.OrderNotFound(orderId)
val store = order.storeId?.let { storeRepository.findById(it) } ?: throw OrderException.StoreNotFound(order.storeId.toString())

if (ownerId.toString() != store.ownerId) {
throw OrderException.OrderCanNotRefuse(orderId)
}
orderLockManager.lock(orderId) {
if (ownerId.toString() != store.ownerId) {
throw OrderException.OrderCanNotRefuse(orderId)
}
order.refuse()
orderRepository.save(order)
}
orderRepository.save(order)

eventPublisher.publishEvent(OrderDetailStatusEvent(orderId, order.status))
}

Expand All @@ -114,7 +111,6 @@ class OrderService(
}
order.complete()
orderRepository.save(order)

eventPublisher.publishEvent(OrderDetailStatusEvent(orderId, order.status))
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ class OrderServiceTest {
}

@Test
@Disabled
fun `must change order status to accept when admin accept order`() {
// given
val owner = createAuthMember()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,9 @@ class ClientExceptionHandler {
@ExceptionHandler(OrderException::class)
fun handleOrderException(exception: OrderException): ResponseEntity<APIResponseDTO<*>> {
logger.error("handleOrderException: {}", exception.toString(), exception)
if (exception::class == OrderException.StoreIsTooFar::class) {
return ResponseEntity.status(412).body(APIResponseDTO(412, exception.message, null))
}
return ResponseEntity.status(400).body(APIResponseDTO(400, exception.message, null))
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import org.springframework.web.bind.annotation.PatchMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestHeader
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController
Expand Down Expand Up @@ -66,10 +67,12 @@ class OrderController(
@JwtAuthenticated
@PostMapping
override fun createOrder(
@RequestHeader("X-User-Lat") userLat: Double,
@RequestHeader("X-User-Lng") userLng: Double,
@RequestBody orderCreationRequest: OrderCreationRequest,
@AuthenticationPrincipal authMember: AuthMember,
): APIResponseDTO<OrderCreationResponse> {
val response = orderCreationService.createOrder(authMember.id, orderCreationRequest)
val response = orderCreationService.createOrder(authMember.id, orderCreationRequest, Pair(userLat, userLng))
return APIResponseDTO(HttpStatus.OK.value(), HttpStatus.OK.reasonPhrase, response)
}

Expand All @@ -79,7 +82,7 @@ class OrderController(
@PathVariable("orderId") orderId: String,
@AuthenticationPrincipal authMember: AuthMember,
): APIResponseDTO<Nothing?> {
orderCancellationService.cancelOrder(orderId)
orderCancellationService.cancelOrder(orderId, authMember.id)
return APIResponseDTO(HttpStatus.OK.value(), HttpStatus.OK.reasonPhrase, null)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ interface OrderCreation {
description = """
주문 항목들을 검증하고, 주문과 주문에 대한 결제 정보를 생성합니다.
PG 결제 전 호출하여 주문 번호와 주문 이름, 결제 총액 정보를 얻습니다.
사용자 위경도 정보를 받아 가게와 주문지 거리가 너무나 멀다면 생성하지 않습니다.

storeId (필수)
- 가게 ID
Expand Down Expand Up @@ -156,5 +157,10 @@ interface OrderCreation {
),
],
)
fun createOrder(orderCreationRequest: OrderCreationRequest, authMember: AuthMember): APIResponseDTO<OrderCreationResponse>
fun createOrder(
userLat: Double,
userLng: Double,
orderCreationRequest: OrderCreationRequest,
authMember: AuthMember,
): APIResponseDTO<OrderCreationResponse>
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package org.fastcampus.applicationclient.order.service
import org.fastcampus.applicationclient.aop.OrderMetered
import org.fastcampus.applicationclient.order.service.event.OrderCancellationEvent
import org.fastcampus.applicationclient.order.service.event.OrderDetailStatusEvent
import org.fastcampus.order.entity.Order
import org.fastcampus.order.exception.OrderException
import org.fastcampus.order.repository.OrderLockManager
import org.fastcampus.order.repository.OrderRepository
Expand All @@ -20,14 +21,16 @@ class OrderCancellationService(
) {
@Transactional
@OrderMetered
fun cancelOrder(orderId: String) {
val order = orderRepository.findById(orderId) ?: throw OrderException.OrderCanNotCancelled(orderId)
orderLockManager.lock(order.id) {
fun cancelOrder(orderId: String, userId: Long) {
val order: Order = orderLockManager.lock(orderId) {
val order = orderRepository.findById(orderId) ?: throw OrderException.OrderCanNotCancelled(orderId)
if (order.userId != userId) {
throw OrderException.NotMatchedUser(orderId, userId)
}
order.cancel()
orderRepository.save(order)
}
orderRepository.save(order)
refundManager.refundOrder(orderId)

refundManager.refundOrder(order.id, order.orderPrice, order.paymentId)
eventPublisher.publishEvent(OrderCancellationEvent(storeId = order.storeId ?: "", orderId = order.id))
eventPublisher.publishEvent(OrderDetailStatusEvent(orderId, order.status))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,19 @@ class OrderCreationService(
) {
@Transactional
@OrderMetered
fun createOrder(userId: Long, orderCreationRequest: OrderCreationRequest): OrderCreationResponse {
fun createOrder(userId: Long, orderCreationRequest: OrderCreationRequest, userLatAndLng: Pair<Double, Double>): OrderCreationResponse {
// 사용자 정보 조회
val loginMember = memberRepository.findById(userId)

// 스토어 검색
val storeEntity = (storeRepository.findById(orderCreationRequest.storeId))
?: throw OrderException.StoreNotFound(orderCreationRequest.storeId)

// 거리 체크
if (!storeRepository.existsStoreNearBy(storeEntity.id!!, userLatAndLng.first, userLatAndLng.second, 200.0)) {
throw OrderException.StoreIsTooFar(storeEntity.id!!)
}

// 주문내역 검사, 메뉴정보 반환받기
val targetMenuEntities = OrderValidator.checkOrderCreation(storeEntity, orderCreationRequest)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,13 @@ class LocalPaymentGateway : PaymentGateway {
amount = amount,
)
}

override fun cancel(paymentKey: String, orderId: String, amount: Long): PaymentGatewayResponse {
return PaymentGatewayResponse(
status = PaymentGatewayResponse.Status.CANCELED,
paymentKey = paymentKey,
orderId = orderId,
amount = amount,
)
}
}
2 changes: 2 additions & 0 deletions application-client/src/main/resources/http/order.http
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ Authorization: {{accessToken}}

### 주문생성
POST http://localhost:8081/api/v1/orders
X-User-Lat: 37.71936226550588
X-User-Lng: 126.69319553943058
Content-Type: application/json
Authorization: {{accessToken}}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.fastcampus.applicationclient.order.service

import org.fastcampus.applicationclient.fixture.createMember
import org.fastcampus.applicationclient.fixture.createOrderFixture
import org.fastcampus.applicationclient.order.service.event.OrderCancellationEvent
import org.fastcampus.order.entity.Order
Expand All @@ -19,6 +20,7 @@ import org.mockito.Mockito.`when`
import org.mockito.junit.jupiter.MockitoExtension
import org.mockito.kotlin.any
import org.mockito.kotlin.eq
import org.mockito.kotlin.whenever
import org.springframework.context.ApplicationEventPublisher
import strikt.api.expectThat
import strikt.api.expectThrows
Expand All @@ -37,28 +39,30 @@ class OrderCancellationServiceTest {
@InjectMocks lateinit var orderCancellationService: OrderCancellationService

@Test
@Disabled
fun `cancel order`() {
// given
val order = createOrderFixture().copy(id = "order_123", status = Order.Status.RECEIVE)
val user = createMember(id = 1L)
val order = createOrderFixture().copy(id = "order_123", status = Order.Status.RECEIVE, userId = user.id)

`when`(orderRepository.findById(order.id)).thenReturn(order)
doNothing().`when`(refundManager).refundOrder(order.id)
doNothing().`when`(refundManager).refundOrder(order.id, order.orderPrice, order.paymentId)
doNothing().`when`(eventPublisher).publishEvent(OrderCancellationEvent(storeId = order.storeId!!, orderId = order.id))
`when`(orderLockManager.lock(eq(order.id), any<() -> Unit>())).thenReturn(order.cancel())
whenever(orderLockManager.lock(eq(order.id), any<() -> Order>())).thenReturn(order)

// when
orderCancellationService.cancelOrder(order.id)
orderCancellationService.cancelOrder(order.id, user.id!!)

verify(orderRepository).findById(order.id)
verify(orderLockManager).lock(eq(order.id), any<() -> Unit>())
expectThat(order.status).isEqualTo(Order.Status.CANCEL)
}

@Test
@Disabled
fun `must throw exception when order is cancelled which has not RECEIVE status`() {
// given
var order = createOrderFixture()
val user = createMember(id = 1L)
var order = createOrderFixture(userId = user.id)
order = order.copy(status = Order.Status.REFUSE)

`when`(orderRepository.findById(order.id)).thenReturn(order)
Expand All @@ -68,7 +72,7 @@ class OrderCancellationServiceTest {

// when & then
expectThrows<OrderException.OrderCanNotCancelled> {
orderCancellationService.cancelOrder(order.id)
orderCancellationService.cancelOrder(order.id, user.id!!)
}.and {
get { orderId }.isEqualTo(order.id)
get { message }.isEqualTo("해당 주문은 취소할 수 없습니다.")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ import org.fastcampus.store.entity.StoreMenuCategory
import org.fastcampus.store.repository.StoreRepository
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.mockito.Mockito
import org.mockito.Mockito.mock
import org.mockito.Mockito.`when`
import org.mockito.kotlin.any
import org.springframework.context.ApplicationEventPublisher
import strikt.api.expectThat
import strikt.assertions.isEqualTo
Expand Down Expand Up @@ -92,6 +92,8 @@ class OrderCreationServiceTest {
.thenReturn(member)
`when`(storeRepository.findById(storeId))
.thenReturn(store)
`when`(storeRepository.existsStoreNearBy(any(), any(), any(), any()))
.thenReturn(true)
`when`(paymentRepository.save(any()))
.thenAnswer { (it.arguments[0] as Payment).copy(id = member.id) }
`when`(orderRepository.save(any()))
Expand All @@ -104,7 +106,7 @@ class OrderCreationServiceTest {
.thenAnswer { (it.arguments[0] as OrderMenuOption).copy(id = 1) }

// when
val result = orderCreationService.createOrder(1, request)
val result = orderCreationService.createOrder(1, request, Pair(0.0, 0.0))

// then
expectThat(result) {
Expand Down Expand Up @@ -312,10 +314,4 @@ class OrderCreationServiceTest {
minimumOrderAmount = 1000,
)
}

private fun <T> any(): T {
Mockito.any<T>()
@Suppress("UNCHECKED_CAST")
return null as T
}
}
11 changes: 11 additions & 0 deletions application-oss/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,23 @@ tasks.getByName("bootJar") {
enabled = true
}
dependencies {
implementation(project(":domains:member"))
implementation(project(":domains:order"))
implementation(project(":domains:payment"))
runtimeOnly(project(":infrastructure:member-postgres"))
runtimeOnly(project(":infrastructure:order-postgres"))
runtimeOnly(project(":infrastructure:payment-postgres"))
runtimeOnly(project(":infrastructure:external-pg-toss-payments"))
runtimeOnly(project(":infrastructure:external-pg-pay200"))
implementation(project(":common"))
implementation("org.springframework.boot:spring-boot-starter-batch")
implementation("org.springframework.boot:spring-boot-starter-quartz")
implementation("org.springframework:spring-tx")
implementation("org.springframework.cloud:spring-cloud-starter-openfeign")
}

dependencyManagement {
imports {
mavenBom("org.springframework.cloud:spring-cloud-dependencies:2023.0.0")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,18 @@ package org.fastcampus.applicationoss
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.cloud.openfeign.EnableFeignClients

@EnableBatchProcessing
@SpringBootApplication(
scanBasePackages = [
"org.fastcampus.applicationoss",
"org.fastcampus.order",
"org.fastcampus.payment",
"org.fastcampus.member",
],
)
@EnableFeignClients(basePackages = ["org.fastcampus.payment.gateway.client"])
class ApplicationOssApplication

fun main(args: Array<String>) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package org.fastcampus.applicationoss.batch.job.payment

import org.fastcampus.applicationoss.batch.tasklet.payment.PaymentGatewayFactory
import org.fastcampus.applicationoss.batch.tasklet.payment.RefundTasklet
import org.fastcampus.payment.repository.PaymentRepository
import org.fastcampus.payment.repository.RefundRepository
import org.springframework.batch.core.Job
import org.springframework.batch.core.Step
Expand All @@ -15,6 +17,8 @@ import org.springframework.transaction.PlatformTransactionManager
@Configuration
class RefundJobConfiguration(
private val refundRepository: RefundRepository,
private val paymentGatewayFactory: PaymentGatewayFactory,
private val paymentRepository: PaymentRepository,
) {
@Bean
@Throws(Exception::class)
Expand All @@ -29,7 +33,7 @@ class RefundJobConfiguration(
fun refundStep(jobRepository: JobRepository, transactionManager: PlatformTransactionManager): Step {
return StepBuilder("refundStep", jobRepository)
.allowStartIfComplete(true)
.tasklet(RefundTasklet(refundRepository), transactionManager)
.tasklet(RefundTasklet(refundRepository, paymentGatewayFactory, paymentRepository), transactionManager)
.build()
}
}
Loading