- 주문 이력에서 원하지 않는 주문을 취소하는 기능도 필요하다. 주문을 취소할 경우 해당 주문의 상태를 취소 상태로 만들어주고, 주문할 때 상품의 재고를 감소시켰던 만큼 다시 더해주는 로직을 작성하면 된다.
- 상품의 재고를 더해주기 위해 Item 클래스에 addStock 메서드를 생성한다.
- Item.java
// 상품에 재고를 더해주기 위해서 Item 클래스에 addStock 메소드를 생성한다.
public void addStock(int stockNumber) {
// 1. 상품의 재고를 증가시키는 메소드이다.
this.stockNumber += stockNumber;
}
- addStock을 호출하게 되면 재고를 증가시킨다.
- 주문을 취소할 경우 주문 수량만큼 상품의 재고를 증가시키는 메소드를 구현한다.
- OrderItem.java
public void cancel() {
// 주문 취소 시 주문 수량만큼 상품의 재고를 더해준다.
this.getItem().addStock(count);
}
- cancel() 메서드는 this -> orderItem에서 Item 엔티티의 addstock을 호출함으로써 count -> 주문 수량만큼 재고를 증가시킨다.
- Item 클래스에 주문 취소 시 주문 수량을 재고에 더해주는 로직과 주문 상태를 취소 상태로 바꿔주는 메서드를 구현한다.
- Order.java
public void cancelOrder() {
this.orderStatus = OrderStatus.CANCEL;
for (OrderItem orderItem : orderItems) {
orderItem.cancel();
}
}
- Order 클래스의 cancelOrder() 메서드를 실행시키면 orderStatus -> 주문 상태가 CANCEL이 되고, 회원의 주문에 있는 Item들을 돌면서 취소한 재고만큼 재고 수량을 늘려주는 cancel() 메서드를 실행시킨다.
- OrderService 클래스에 주문을 취소하는 로직을 구현한다.
- OrderService.java
@Transactional(readOnly = true)
public boolean validateOrder(Long orderId, String email) {
// 1. 현재 로그인한 사용자와 주문 데이터를 생성한 사용자가 같은지 검사한다.
// 같을 때는 true를 반환하고 같지 않을 경우는 false를 반환한다.
Member curMember = memberRepository.findByEmail(email);
Order order = orderRepository.findById(orderId).orElseThrow(EntityNotFoundException::new);
Member savedMember = order.getMember();
if (!StringUtils.equals(curMember.getEmail(), savedMember.getEmail())) {
return false;
}
return true;
}
public void cancelOrder(Long orderId) {
Order order = orderRepository.findById(orderId).orElseThrow(EntityNotFoundException::new);
order.cancelOrder();
// 2. 주문 취소 상태를 변경하면 변경 감지 기능에 의해서 트랜잭션이 끝날 때 update 쿼리가 실행된다.
}
}
public boolean validateOrder(Long orderId, String email) {
// 1. 현재 로그인한 사용자와 주문 데이터를 생성한 사용자가 같은지 검사한다.
// 같을 때는 true를 반환하고 같지 않을 경우는 false를 반환한다.
Member curMember = memberRepository.findByEmail(email);
Order order = orderRepository.findById(orderId).orElseThrow(EntityNotFoundException::new);
Member savedMember = order.getMember();
if (!StringUtils.equals(curMember.getEmail(), savedMember.getEmail())) {
return false;
}
return true;
}
- validateOrder 메서드를 통해 orderId와 email을 통해 orderId에 대한 Member가 email의 Member와 일치한 지 확인하는 메서드이다.
- 추후에 주문한 회원임을 증명해야지만 주문을 취소할 수 있도록 하기 위한 메서드이다.
public void cancelOrder(Long orderId) {
Order order = orderRepository.findById(orderId).orElseThrow(EntityNotFoundException::new);
order.cancelOrder();
// 2. 주문 취소 상태를 변경하면 변경 감지 기능에 의해서 트랜잭션이 끝날 때 update 쿼리가 실행된다.
}
}
- orderId를 조회하여 해당 order 취소하는 메서드이다.
- orderService 클래스의 cancelOrder 메서드와 order 클래스의 cancelOrder 메서드는 서로 다른 메서드이다.
- orderService.cancelOrder() -> order.cancelOrder() -> orderItem.cancelOrder()을 호출하게 된다.
- OrderController 클래스에 orderId를 받아서 주문 취소 로직을 호출하는 메서드를 생성한다.
- 장바구니에서처럼 비동기 요청을 받아서 처리한다.
- OrderController.java
@PostMapping("/order/{orderId}/cancel")
public @ResponseBody ResponseEntity cancelOrder(
@PathVariable("orderId") Long orderId, Principal principal) {
if (!orderService.validateOrder(orderId, principal.getName())) {
// 1. 자바스크립트에서 취소할 주문 번호는 조작이 가능하므로 다른 사람의 주문을 취소하지 못하도록
// 주문 취소 권한 검사를 합니다.
return new ResponseEntity<String>("주문 취소 권한이 없습니다.", HttpStatus.FORBIDDEN);
}
orderService.cancelOrder(orderId);
// 2. 주문 취소 로직을 호출한다.
return new ResponseEntity<Long>(orderId, HttpStatus.OK);
}
@PostMapping("/order/{orderId}/cancel")
public @ResponseBody ResponseEntity cancelOrder(
@PathVariable("orderId") Long orderId, Principal principal) {
if (!orderService.validateOrder(orderId, principal.getName())) {
// 1. 자바스크립트에서 취소할 주문 번호는 조작이 가능하므로 다른 사람의 주문을 취소하지 못하도록
// 주문 취소 권한 검사를 합니다.
return new ResponseEntity<String>("주문 취소 권한이 없습니다.", HttpStatus.FORBIDDEN);
}
* @ResponseBody는 주로 REST API 또는 AJAX 요청에 응답할 때 사용한다.
- POST 방식으로 넘어온 /order/{orderId}/cancel 요청에 대해 cancelOrder 메서드를 실행한다.
- @ResponseBody 어노테이션을 사용했기 메서드의 리턴값을 때문에 HTTP 응답 본문(body)으로 사용하겠다는 것을 나타낸다.
- validateOrder에 대한 검증에 성공하면
orderService.cancelOrder(orderId);
// 2. 주문 취소 로직을 호출한다.
return new ResponseEntity<Long>(orderId, HttpStatus.OK);
- orderService의 cancelOrder() 메서드를 실행시키고, HTTP 응답으로 ResponseEntity에 OK에 대한 상태 코드를 전달한다.
- 주문 취소 로직이 화면에서 동작 할 수 있도록 주문 취소 화면에 주문 취소 기능을 호출하는 자바스크립트 함수를 생성한다.
- orderHist.html
<!-- 사용자 스크립트 추가 -->
<th:block layout:fragment="script">
<script th:inline="javascript">
function cancelOrder(orderId) {
var token = $("meta[name='_csrf']").attr("content");
var header = $("meta[name='_csrf_header']").attr("content");
var url = "/order/" + orderId + "/cancel";
var paramData = {
// 1. 취소할 주문 번호를 파라미터로 넘겨준다.
orderId : orderId,
};
var param = JSON.stringify(paramData);
$.ajax({
url : url,
type : "POST",
contentType : "application/json",
data : param,
beforeSend : function(xhr){
/* 데이터를 전송하기 전에 헤더에 csrf값을 설정 */
xhr.setRequestHeader(header, token);
},
dataType : "json",
cache : false,
success : function(result, status){
// 2. 주문이 정상적으로 취소됐으면 현재 페이지로 다시 redirect 한다.
alert("주문이 취소 되었습니다.");
location.href='/orders/' + [[${page}]];
},
error : function(jqXHR, status, error){
if(jqXHR.status == '401'){
alert('로그인 후 이용해주세요');
location.href='/members/login';
} else{
alert(jqXHR.responseText);
}
}
});
}
</script>
function cancelOrder(orderId) {
var token = $("meta[name='_csrf']").attr("content");
var header = $("meta[name='_csrf_header']").attr("content");
var url = "/order/" + orderId + "/cancel";
var paramData = {
// 1. 취소할 주문 번호를 파라미터로 넘겨준다.
orderId : orderId,
};
var param = JSON.stringify(paramData);
- token과 header를 통해 CSRF 토큰 값을 가져온다. 이 값들은 인증된 사용자임을 증명하기 위해서 사용한다.
- url 변수에 /order/orderId/cancel 값을 넣고, paramData에 String으로 데이터를 넣은 다음 해당 데이터를 JSON형식으로 변형시킨 다음 JSON 형식으로 변형된 데이터를 param 변수에 넣는다.
$.ajax({
url : url,
type : "POST",
contentType : "application/json",
data : param,
beforeSend : function(xhr){
/* 데이터를 전송하기 전에 헤더에 csrf값을 설정 */
xhr.setRequestHeader(header, token);
},
dataType : "json",
cache : false,
success : function(result, status){
// 2. 주문이 정상적으로 취소됐으면 현재 페이지로 다시 redirect 한다.
alert("주문이 취소 되었습니다.");
location.href='/orders/' + [[${page}]];
},
error : function(jqXHR, status, error){
if(jqXHR.status == '401'){
alert('로그인 후 이용해주세요');
location.href='/members/login';
} else{
alert(jqXHR.responseText);
}
}
});
- 해당 페이지를 비동기 방식으로 동작 시키기 위해 (일반적인 Form에 POST 방식으로 전달하는 방식은 해당 페이지가 초기화된다. 하지만 비동기 방식을 사용하면 페이지가 초기화 되는 것이 아닌 특정 부분만 동작할 수 있게 설정 할 수 있다.) ajax 방식을 이용한다.
- ajax에 작성되는 내용들은 HTTP 요청의 구성 요소이다. url (/order/" + orderId + "/cancel)로 요청을 보내고, type는 POST로 보내기 때문에 Controller에서 POST 방식으로 해당 경로로 오는 데이터들을 body를 통해 전달 받을 수 있다.
<button type="button" class="btn btn-outline-secondary"
th:value="${order.orderId}" onclick="cancelOrder(this.value)">주문취소</button>
- button을 통해 버튼 클릭 즉, onclick이 실행되면, JavaScript의 cancelOrder 메서드가 실행된다.
function cancelOrder(orderId) {
var token = $("meta[name='_csrf']").attr("content");
var header = $("meta[name='_csrf_header']").attr("content");
var url = "/order/" + orderId + "/cancel";
var paramData = {
// 1. 취소할 주문 번호를 파라미터로 넘겨준다.
orderId : orderId,
.......
- 위에서 비동기로 동작시키기 위한 해당 메서드가 실행된다.
'Portfolio, Project > Project(Programming)' 카테고리의 다른 글
Project (7-2) 장바구니 조회, 수정, 삭제 (0) | 2023.08.17 |
---|---|
Project (7-1) 장바구니 담기 (0) | 2023.08.16 |
Project (6-2) 주문 이력 조회하기 (0) | 2023.08.15 |
Project (6-1) 주문 기능 구현 (0) | 2023.08.14 |
Project (5-3) 메인 화면, 상품 상세 페이지 (1) (추가 내용 작성 필요할 듯...) (0) | 2023.08.11 |