#12 [스프링 스터디] 쇼핑몰 만들기 프로젝트 - 상품 주문 취소

myeonji·2022년 1월 31일
0

오늘은 구매자가 상품 주문을 취소하는 주문 취소 기능을 구현해보겠습니다.

1. 주문(판매) 상태를 나타내기 위해 OrderItem Entity와 SaleItem Entity에 isCancel 변수를 추가합니다.

  • boolean으로 해도 되지만, 저는 int 형으로 0=false, 1=true로 사용하였습니다.
  • 주문내역에는 주문취소/취소완료 를, 판매내역에는 판매완료/판매취소를 나타냅니다.

!! 이 부분은 재설정이 필요합니다. 저처럼 실수하지 마세요.. !!
2. OrderItem과 SaleItem Entity를 @OneToOne 어노테이션으로 연관관계를 설정합니다.

  • OrderItem의 상태가 취소완료 상태가 되면 SaleItem의 상태도 판매취소로 변경되어야 합니다.
  • 따라서 둘의 연관관계를 @OneToOne으로 설정하였습니다.

3. Controller에서 매개변수로 orderItemId를 받아 @PostMapping을 설정합니다.

  • html에서 주문취소 버튼을 누르면 주문을 취소하는 유저의 id와 해당 상품의 id를 받아 처리하는 것이기 때문에 @PostMapping으로 설정해야 합니다.
  • 주문내역 총 개수에서 취소하는 상품의 개수만큼 감소시킵니다.
// 주문 취소 기능
    @PostMapping("/user/{id}/checkout/cancel/{orderItemId}")
    public String cancelOrder(@PathVariable("id") Integer id, @PathVariable("orderItemId") Integer orderItemId, Model model, @AuthenticationPrincipal PrincipalDetails principalDetails) {

        // 로그인이 되어있는 유저의 id와 주문 취소하는 유저의 id가 같아야 한다.
        if (principalDetails.getUser().getId() == id) {
            OrderItem cancelItem= orderService.findOrderitem(orderItemId);  // 취소할 상품 찾기
            User user = userPageService.findUser(id); // 취소하는 유저 찾기

            // 주문 내역 총 개수에서 취소 상품 개수 줄어듬
            List<OrderItem> orderItemList = orderService.findUserOrderItems(id);
            int totalCount = 0;
            for (OrderItem orderItem : orderItemList) {
                totalCount += orderItem.getOrderCount();
            }
            totalCount = totalCount - cancelItem.getOrderCount();

            orderService.orderCancel(user, cancelItem);

            model.addAttribute("totalCount", totalCount);
            model.addAttribute("orderItems", orderItemList);
            model.addAttribute("user", user);

            return "redirect:/user/orderHist/{id}";

        }
        
        else {
            return "redirect:/main";
        }
    }

4. Service에서 주문 취소 관련 기능을 구현합니다.

  • 판매자의 판매내역 총 개수(totalCount) 감소
  • 해당 item의 재고(stock) 증가
  • 해당 item의 판매량(count) 감소
  • 판매자의 수익 감소
  • 구매자의 잔액 증가
  • orderItem의 주문 상태 1(false=주문취소)로 바꾸기
  • saleItem의 판매 상태 1(false=판매취소)로 바꾸기
// 주문 취소 기능
    @Transactional
    public void orderCancel(User user, OrderItem cancelItem) {

        // 판매자의 판매내역 totalCount 감소
        cancelItem.getSaleItem().getSale().setTotalCount(cancelItem.getSaleItem().getSale().getTotalCount()-cancelItem.getOrderCount());

        // 해당 item 재고 다시 증가
        cancelItem.getItem().setStock(cancelItem.getItem().getStock()+ cancelItem.getOrderCount());

        // 해당 item의 판매량 감소
        cancelItem.getItem().setCount(cancelItem.getItem().getCount()-cancelItem.getOrderCount());

        // 판매자 돈 감소
        cancelItem.getSaleItem().getSeller().setCoin(cancelItem.getSaleItem().getSeller().getCoin()- cancelItem.getOrderPrice());

        // 구매자 돈 증가
        cancelItem.getUser().setCoin(cancelItem.getUser().getCoin()+ cancelItem.getOrderPrice());

        // 해당 orderItem의 주문 상태 1로 변경 -> 주문 취소를 의미
        cancelItem.setIsCancel(cancelItem.getIsCancel()+1);

        // 해당 orderItem.getsaleItemId 로 saleItem 찾아서 판매 상태 1로 변경 -> 판매 취소를 의미
        cancelItem.getSaleItem().setIsCancel(cancelItem.getSaleItem().getIsCancel()+1);

        orderItemRepository.save(cancelItem);
        saleItemRepository.save(cancelItem.getSaleItem());


    }

<참고>
주문내역 html과 판매내역 html에서 타임리프를 사용하여 구현하는 과정에서 조금 헤맸습니다.
타임리프 사용법을 찾아보았더니 데이터를 넘기기 위해서는 input 태그와 th:value 가 필요하다고 하였습니다. (이를 사용하여 해결하려고 계속 시도하다가 발목이 잡혔..)

하지만 다시 천천히 주문취소 기능을 위해 데이터를 어떻게 해야할지 생각해보니, 주문을 취소하는 유저의 id와 주문을 취소할 상품의 id를 데이터로 넘길 필요가 있었습니다.
데이터를 따로 입력을 받아야 하는 부분이 아니기에 input 태그와 th:value 의 굴레에서 벗어나고 th:action 로 데이터를 넘겼습니다.

평소 PostMapping에 사용하던 식으로 구현했으면 됐는데, 사용자의 입력이 없는, 입력 데이터를 넘기지 않고 기존에 존재하는 데이터만 넘기는 PostMapping은 처음 구현해봐서 조금 헤맸습니다. ㅠㅠ

아무튼! 아래의 5번, 6번 과정을 보시면 타임리프를 사용하여 주문취소상태/판매상태 를 나타내는 html 구현을 성공했습니다.

5. 주문내역 html에서 주문취소 버튼을 만듭니다.

  • th:if 를 사용하여 만들었습니다. 주의할 점은 if 의 조건과 unless 의 조건을 동일하게 적어줘야 합니다!!
<td th:if="${orderItem.getIsCancel()} == 1">취소완료</a></td>
                        <td th:unless="${orderItem.getIsCancel()} == 1">
                            <form th:action="@{/user/{id}/checkout/cancel/{orderItemId}(id=${user.id}, orderItemId=${orderItem.id})}" th:method="POST">
                                <button type="submit">주문취소</button>
                            </form>
                        </td>

6. 판매내역 html에서 판매상태를 나타냅니다.

  • 타임리프를 이용하여 삼항 연산자를 사용하였습니다.
                        <td th:text="${saleItem.getIsCancel()} == 0 ? '판매완료' : '판매취소'">판매현황</td>

7. 주문 취소가 완료되면 완료 메세지를 띄웁니다. (JS)

<form onsubmit="return cancel();" th:action="@{/user/{id}/checkout/cancel/{orderItemId}(id=${user.id}, orderItemId=${orderItem.id})}" th:method="POST">
                                <button type="submit">주문취소</button>
                            </form>
                            <script th:inline="javascript">
                                /* <![CDATA[ */
                                function cancel() {
                                if (1) {
                                    alert("주문 취소가 완료되었습니다.")
                                    return true
                                }
                            }
                            </script>

추후에 판매자가 주문을 접수했거나, 취소 기간이 지나는 등 여러 고려사항을 렌더링하기 위해 onsubmit 을 이용하여 조건문을 사용하였습니다!

주문 취소가 완료되었습니다! 짝짝짝

0개의 댓글