<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layouts/layout1}">
<head>
<meta name="_csrf" th:content="${_csrf.token}"/>
<meta name="_csrf_header" th:content="${_csrf.headerName}"/>
</head>
<th:block layout:fragment="script">
<script th:inline="javascript">
$(document).ready(function() {
$("input[name=cartChkBox]").change(function() {
getOrderTotalPrice();
});
});
function getOrderTotalPrice() {
var orderTotalPrice = 0;
$("input[name=cartChkBox]:checked").each(function() {
var cartItemId = $(this).val();
var price = $("#price_" + cartItemId).attr("data-price");
var count = $("#count_" + cartItemId).val();
orderTotalPrice += price * count;
});
$("#orderTotalPrice").html(orderTotalPrice+'원');
}
function changeCount(obj) {
var count = obj.value;
var cartItemId = obj.id.split('_')[1];
var price = $("#price_" + cartItemId).data("price");
var totalPrice = count * price;
$("#totalPrice_" + cartItemId).html(totalPrice+"원");
getOrderTotalPrice();
updateCartItemCount(cartItemId, count);
}
function checkAll() {
if($("#checkall").prop("checked")) {
$("input[name=cartChkBox]").prop("checked", true);
} else {
$("input[name=cartChkBox]").prop("checked", false);
}
getOrderTotalPrice();
}
function updateCartItemCount(cartItemId, count) {
var token = $("meta[name='_csrf']").attr("content");
var header = $("meta[name='_csrf_header']").attr("content");
var url = "/cartItem/" + cartItemId + "?count=" + count;
$.ajax({
url : url,
type : "PATCH",
beforeSend : function(xhr) {
xhr.setRequestHeader(header, token);
},
dataType : "json",
cache : false,
success : function(result, status) {
console.log("cartItem count update success");
},
error : function(jqXHR, status, error) {
if(jqXHR.status == '401') {
alert('로그인 후 이용해주세요.');
location.href='/members/login';
} else {
alert(jqXHR.responseText);
}
}
});
}
function deleteCartItem(obj) {
var cartItemId = obj.dataset.id;
var token = $("meta[name='_csrf']").attr("content");
var header = $("meta[name='_csrf_header']").attr("content");
var url = "/cartItem/" + cartItemId;
$.ajax({
url : url,
type : "DELETE",
beforeSend : function(xhr) {
xhr.setRequestHeader(header, token);
},
dataType : "json",
cache : false,
success : function(result, status) {
location.href='/cart';
},
error : function(jqXHR, status, error) {
if (jqXHR.status == '401') {
alert('로그인 후 이용해주세요.');
location.href='/members/login';
} else {
alert(jqXHR.responseText);
}
}
});
}
function orders() {
var token = $("meta[name='_csrf']").attr("content");
var header = $("meta[name='_csrf_header']").attr("content");
var url = "/cart/orders";
var dataList = new Array();
var paramData = new Object();
$("input[name=cartChkBox]:checked").each(function() {
var cartItemId = $(this).val();
var data = new Object();
data["cartItemId"] = cartItemId;
dataList.push(data);
});
paramData['cartOrderDtoList'] = dataList;
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) {
alert("주문이 완료 되었습니다.");
location.href='/orders';
},
error : function(jqXHR, status, error) {
if (jqXHR.status == '401') {
alert('로그인 후 이용해주세요.');
location.href='/members/login';
} else {
alert(jqXHR.responseText);
}
}
});
}
</script>
</th:block>
<!--사용자 css 추가-->
<th:block layout:fragment="css">
<style>
.content-mg {
margin-left: 30%;
margin-right: 30%;
margin-top: 2%;
margin-bottom: 100px;
}
.repImgDiv {
margin-right: 15px;
margin-left: 15px;
height: auto;
}
.repImg {
height: 100px;
width: 100px;
}
.card {
width: 750px;
height: 100%;
padding: 30px;
margin-bottom: 20px;
}
.fs18 {
font-size: 18px;
}
.fs24 {
font-size: 24px;
}
</style>
</th:block>
<div layout:fragment="content" class="content-mg">
<h2 class="mb-4">장바구니 목록</h2>
<div>
<table class="table">
<colgroup>
<col width="15%">
<col width="70%">
<col width="15%">
</colgroup>
<thead>
<tr class="text-center">
<td>
<input type="checkbox" id="checkall" onclick="checkAll()"> 전체선택
</td>
<td>상품정보</td>
<td>상품금액</td>
</tr>
</thead>
<tbody>
<tr th:each="cartItem : ${cartItems}">
<td class="text-center align-middle">
<input type="checkbox" name="cartChkBox" th:value="${cartItem.cartItemId}">
</td>
<td class="d-flex">
<div class="repImgDiv align-self-center">
<img th:src="${cartItem.imgUrl}" class="rounded repImg" th:alt="${cartItem.itemNm}">
</div>
<div class="align-self-center">
<span th:text="${cartItem.itemNm}" class="fs24 font-weight-bold"></span>
<div class="fs18 font-weight-light">
<span class="input-group mt-2">
<span th:id="'price_' + ${cartItem.cartItemId}" th:data-price="${cartItem.price}"
th:text="${cartItem.price} + '원'" class="align-self-center mr-2">
</span>
<input type="number" name="count" th:id="'count_' + ${cartItem.cartItemId}"
th:value="${cartItem.count}" min="1" onchange="changeCount(this)">
<button type="button" class="close" aria-label="Close">
<span aria-hidden="true" th:data-id="${cartItem.cartItemId}"
onclick="deleteCartItem(this)">×</span>
</button>
</span>
</div>
</div>
</td>
<td class="text-center align-middle">
<span th:id="'totalPrice_' + ${cartItem.cartItemId}" name="totalPrice"
th:text="${cartItem.price * cartItem.count} + '원'"></span>
</td>
</tr>
</tbody>
</table>
<h2 class="text-center">
총 주문 금액 : <span id="orderTotalPrice" class="text-danger">0원</span>
</h2>
<div class="text-center mt-3">
<button type="button" class="btn btn-primary btn-lg" onclick="orders()">주문하기</button>
</div>
</div>
</div>
</html>
package com.shop.dto;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
@Getter
@Setter
public class CartOrderDto {
private Long cartItemId;
private List<CartOrderDto> cartOrderDtoList;
}
package com.shop.service;
import com.shop.dto.OrderDto;
import com.shop.dto.OrderHistDto;
import com.shop.dto.OrderItemDto;
import com.shop.entity.*;
import com.shop.repository.ItemImgRepository;
import com.shop.repository.ItemRepository;
import com.shop.repository.MemberRepository;
import com.shop.repository.OrderRepository;
import jakarta.persistence.EntityNotFoundException;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.thymeleaf.util.StringUtils;
import java.util.ArrayList;
import java.util.List;
@Service
@Transactional
@RequiredArgsConstructor
public class OrderService {
private final ItemRepository itemRepository;
private final MemberRepository memberRepository;
private final OrderRepository orderRepository;
private final ItemImgRepository itemImgRepository;
public Long order(OrderDto orderDto, String email) {
Item item = itemRepository.findById(orderDto.getItemId())
.orElseThrow(EntityNotFoundException::new);
Member member = memberRepository.findByEmail(email);
List<OrderItem> orderItemList = new ArrayList<>();
OrderItem orderItem = OrderItem.createOrderItem(item, orderDto.getCount());
orderItemList.add(orderItem);
Order order = Order.createOrder(member, orderItemList);
orderRepository.save(order);
return order.getId();
}
// Entity Order, OrderItem
// change OrderHistDto, OrderItemDto-> result Page<OrderHistDto>
@Transactional(readOnly = true)
public Page<OrderHistDto> getOrderList(String email, Pageable pageable) {
List<Order> orders = orderRepository.findOrders(email, pageable);
Long totalCount = orderRepository.countOrder(email);
List<OrderHistDto> orderHistDtos = new ArrayList<>();
// Order -> OrderHistDto
// OrderItem -> OrderItemDto+
for (Order order : orders) {
OrderHistDto orderHistDto = new OrderHistDto(order);
List<OrderItem> orderItems = order.getOrderItems();
for (OrderItem orderItem : orderItems) {
ItemImg itemImg = itemImgRepository.findByItemIdAndRepImgYn(orderItem.getItem().getId(),
"Y");
OrderItemDto orderItemDto = new OrderItemDto(orderItem, itemImg.getImgUrl());
orderHistDto.addOrderItemDto(orderItemDto);
}
orderHistDtos.add(orderHistDto);
}
return new PageImpl<OrderHistDto>(orderHistDtos, pageable, totalCount);
}
@Transactional(readOnly = true)
public boolean validateOrder(Long orderId, String email) {
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();
}
public Long orders(List<OrderDto> orderDtoList, String email) {
Member member = memberRepository.findByEmail(email);
List<OrderItem> orderItemList = new ArrayList<>();
for (OrderDto orderDto : orderDtoList) {
Item item = itemRepository.findById(orderDto.getItemId())
.orElseThrow(EntityNotFoundException::new);
OrderItem orderItem = OrderItem.createOrderItem(item, orderDto.getCount());
orderItemList.add(orderItem);
}
Order order = Order.createOrder(member, orderItemList);
orderRepository.save(order);
return order.getId();
}
}
package com.shop.service;
import com.shop.dto.CartDetailDto;
import com.shop.dto.CartItemDto;
import com.shop.dto.CartOrderDto;
import com.shop.dto.OrderDto;
import com.shop.entity.Cart;
import com.shop.entity.CartItem;
import com.shop.entity.Item;
import com.shop.entity.Member;
import com.shop.repository.CartItemRepository;
import com.shop.repository.CartRepository;
import com.shop.repository.ItemRepository;
import com.shop.repository.MemberRepository;
import jakarta.persistence.EntityExistsException;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.thymeleaf.util.StringUtils;
import java.util.ArrayList;
import java.util.List;
@Service
@RequiredArgsConstructor
@Transactional
public class CartService {
private final ItemRepository itemRepository;
private final MemberRepository memberRepository;
private final CartRepository cartRepository;
private final CartItemRepository cartItemRepository;
private final OrderService orderService;
public Long addCart(CartItemDto cartItemDto, String email) {
Item item = itemRepository.findById(cartItemDto.getItemId())
.orElseThrow(EntityExistsException::new);
Member member = memberRepository.findByEmail(email);
Cart cart = cartRepository.findByMemberId(member.getId());
if (cart == null) {
cart = Cart.createCart(member);
cartRepository.save(cart);
}
CartItem savedCartItem = cartItemRepository.findByCartIdAndItemId(cart.getId(), item.getId());
if (savedCartItem != null) {
savedCartItem.addCount(cartItemDto.getCount());
return savedCartItem.getId();
} else {
CartItem cartItem = CartItem.createCartItem(cart, item, cartItemDto.getCount());
cartItemRepository.save(cartItem);
return cartItem.getId();
}
}
@Transactional(readOnly = true)
public List<CartDetailDto> getCartList(String email) {
List<CartDetailDto> cartDetailDtoList = new ArrayList<>();
Member member = memberRepository.findByEmail(email);
Cart cart = cartRepository.findByMemberId(member.getId());
if (cart == null) {
return cartDetailDtoList;
}
cartDetailDtoList = cartItemRepository.findCartDetailDtoList(cart.getId());
return cartDetailDtoList;
}
@Transactional(readOnly = true)
public boolean validateCartItem(Long cartItemId, String email) {
Member curMember = memberRepository.findByEmail(email);
CartItem cartItem = cartItemRepository.findById(cartItemId)
.orElseThrow(EntityExistsException::new);
Member savedMember = cartItem.getCart().getMember();
if (!StringUtils.equals(curMember.getEmail(), savedMember.getEmail())) {
return false;
}
return true;
}
public void updateCartItemCount(Long cartItemId, int count) {
CartItem cartItem = cartItemRepository.findById(cartItemId)
.orElseThrow(EntityExistsException::new);
cartItem.updateCount(count);
}
public void deleteCartItem(Long cartItemId) {
CartItem cartItem = cartItemRepository.findById(cartItemId)
.orElseThrow(EntityExistsException::new);
cartItemRepository.delete(cartItem);
}
public Long orderCartItem(List<CartOrderDto> cartOrderDtoList, String email) {
List<OrderDto> orderDtoList = new ArrayList<>();
for (CartOrderDto cartOrderDto : cartOrderDtoList) {
CartItem cartItem = cartItemRepository.findById(cartOrderDto.getCartItemId())
.orElseThrow(EntityExistsException::new);
OrderDto orderDto = new OrderDto();
orderDto.setItemId(cartItem.getItem().getId());
orderDto.setCount(cartItem.getCount());
orderDtoList.add(orderDto);
}
Long orderId = orderService.orders(orderDtoList, email);
for (CartOrderDto cartOrderDto : cartOrderDtoList) {
CartItem cartItem = cartItemRepository.findById(cartOrderDto.getCartItemId())
.orElseThrow(EntityExistsException::new);
cartItemRepository.delete(cartItem);
}
return orderId;
}
}
package com.shop.controller;
import com.shop.dto.CartDetailDto;
import com.shop.dto.CartItemDto;
import com.shop.dto.CartOrderDto;
import com.shop.service.CartService;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.*;
import java.security.Principal;
import java.util.List;
@Controller
@RequiredArgsConstructor
public class CartController {
private final CartService cartService;
@PostMapping(value = "/cart")
ResponseEntity order(@RequestBody @Valid CartItemDto cartItemDto,
BindingResult bindingResult, Principal principal) {
if (bindingResult.hasErrors()) {
StringBuilder sb = new StringBuilder();
List<FieldError> fieldErrors = bindingResult.getFieldErrors();
for (FieldError fieldError : fieldErrors) {
sb.append(fieldError.getDefaultMessage());
}
return new ResponseEntity<String>(sb.toString(), HttpStatus.BAD_REQUEST);
}
String email = principal.getName();
Long cartItemId;
try {
cartItemId = cartService.addCart(cartItemDto, email);
} catch (Exception e) {
return new ResponseEntity<String>(e.getMessage(), HttpStatus.BAD_REQUEST);
}
return new ResponseEntity<Long>(cartItemId, HttpStatus.OK);
}
@GetMapping(value = "/cart")
public String orderHist(Principal principal, Model model) {
List<CartDetailDto> cartDetailDtoList = cartService.getCartList(principal.getName());
model.addAttribute("cartItems", cartDetailDtoList);
return "cart/cartList";
}
@PatchMapping(value = "/cartItem/{cartItemId}")
public @ResponseBody ResponseEntity updateCartItem(@PathVariable("cartItemId") Long cartItemId,
int count, Principal principal) {
System.out.println(cartItemId);
if (count <= 0) {
return new ResponseEntity<String>("최소 1개 이상 담아주세요.", HttpStatus.BAD_REQUEST);
} else if (!cartService.validateCartItem(cartItemId, principal.getName())) {
return new ResponseEntity<String>("수정 권한이 없습니다.", HttpStatus.FORBIDDEN);
}
cartService.updateCartItemCount(cartItemId, count);
return new ResponseEntity<Long>(cartItemId, HttpStatus.OK);
}
@DeleteMapping(value = "/cartItem/{cartItemId}")
public @ResponseBody ResponseEntity deleteCartItem(@PathVariable("cartItemId") Long cartItemId,
Principal principal) {
if (!cartService.validateCartItem(cartItemId, principal.getName())) {
return new ResponseEntity<String>("수정 권한이 없습니다.", HttpStatus.FORBIDDEN);
}
cartService.deleteCartItem(cartItemId);
return new ResponseEntity<Long>(cartItemId, HttpStatus.OK);
}
@PostMapping(value = "/cart/orders")
public @ResponseBody ResponseEntity orderCartItem(@RequestBody CartOrderDto cartOrderDto,
Principal principal) {
System.out.println(cartOrderDto.getCartItemId());
List<CartOrderDto> cartOrderDtoList = cartOrderDto.getCartOrderDtoList();
if (cartOrderDtoList == null || cartOrderDtoList.size() == 0) {
return new ResponseEntity<String>("주문할 상품을 선택해주세요.", HttpStatus.FORBIDDEN);
}
for (CartOrderDto cartOrder : cartOrderDtoList) {
if (!cartService.validateCartItem(cartOrder.getCartItemId(), principal.getName())) {
return new ResponseEntity<String>("주문 권한이 없습니다.", HttpStatus.FORBIDDEN);
}
}
Long orderId;
try {
orderId = cartService.orderCartItem(cartOrderDtoList, principal.getName());
}
catch (Exception e) {
return new ResponseEntity<String>(e.getMessage(), HttpStatus.BAD_REQUEST);
}
return new ResponseEntity<Long>(orderId, HttpStatus.OK);
}
}
오늘은 장바구니까지 완료하며 쇼핑몰을 마무리하였다.
소셜 로그인만 하면 완료된다!