장바구니 실시간 연산

기여·2024년 8월 1일
0

소소한 개발팁

목록 보기
64/103
post-thumbnail

기획의도:

  • 장바구니에서 주문량 변경 시마다 금액 소계와 총계가 실시간으로 계산되어 표시되도록
  • 최소 주문량은 1개
    (0 값을 허용하면 코드 추가해야 하고, 그럴 바엔 해당 cart 삭제하는 게 깔끔하겠다.)
  • 최대 주문량은 주문가능 수량(또는 잔고)만큼으로 한정
    ㄴ 추후 품절 처리할 경우를 대비하는 차원에서~
  • 동일 상품 2개 이상이면 배송비는 한 번만 합산
  • 배송비 = 0원이면 고객 기분 좋게 '무료베송' 문구를 대체로 표시
  • 천단위에 콤마 찍기

유의사항:
가독성을 위해 String으로 format된 숫자 값을 프론트에 출력하되,
정상적인 연산 위해서는 int값 그대로 전달하도록 input/type=hidden 태그에 감싸서 같이 포함시킬 것.

1, Vo

@Data
public class CartVo {	
	private String cartid;	
	private String uid;	// 사용자 id
	private String username; // 당시 로그인 id (관리자 포함)
	private String product_code;	
	private String pname;
	private String pimgStr;	
	
	private int pstock;	
	private int pquantity;	
	
	//연산 시 int 사용, 표시는 fmt 사용
	private int pprice;
	private int pdelifee;
	
	// 통화 변환. 천단위 콤마 표시
		public String getFmtPprice() {
			NumberFormat numberFormat = 
					NumberFormat.getNumberInstance(Locale.getDefault());
			
			return numberFormat.format(pprice);
		}
		
		public String getFmtPdelifee() {
			NumberFormat numberFormat = 
					NumberFormat.getNumberInstance(Locale.getDefault());
			
			return numberFormat.format(pdelifee);
		}	
}

2, Mapper

@Mapper
public interface CartMapper {
	//uid별 전체 cart 보기
	@Select ("select cartid, uid as username, c.product_code, pquantity, "
			+ "pname, pprice, pdelifee, pimgStr, pstock "
			+ "from cart c join products p on c.product_code = p.product_code "
			+ "where uid=#{username} "
			+ "order by cartid desc")
	
			List<CartVo> cart (CartVo vo);	
	
	@Delete ("delete from cart where cartid=#{cartid}")
			void delCart(CartVo vo);
}

3, Service

@Service
public class CartSvc {
	
	@Autowired
	CartMapper cartMapper;	
		(@Transactional)	    
	
	public List<CartVo> cart (CartVo vo) {
		return cartMapper.cart(vo); //uid별 cart 보기
	}

	public void delCart(CartVo vo) {
		cartMapper.delCart(vo);
	}	
}

4, Ctrl
연산은 프론트에서 진행하기에 여기선 생략

@RequestMapping("/cart/")
@Controller
public class CartCtrl {
	
	@Autowired
	CartSvc cartSvc;
	
	@Autowired
	HttpSession session;
	
	@GetMapping("cart")
	public String cart(Model model, CartVo vo) {
		System.out.println("uid/username별 cart 보기");
		
		String username=(String) session.getAttribute("username");
		vo.setUsername(username); //프론트에 username만 전달됨. uid=null
		
		List<CartVo> li = cartSvc.cart(vo);
        //해당 선언 있어야 lisize 계산됨 (혹은 session.user별 cart.size로 대체 가능)
				
	    model.addAttribute("li", li);	    
	    model.addAttribute("lisize", li.size());	    
	    
	    System.out.println("cart username: " + vo.getUid());
		
		return "cart/cart";
	}	
	
	@GetMapping("delCart")
	public String delCart(CartVo vo) {
		System.out.println("delCart id: " + vo.getCartid());
		
		cartSvc.delCart(vo);
		
		String username = (String) session.getAttribute("username");
        vo.setUid(username);

		return "redirect:cart?uid=" + vo.getUid();
	}
}

5, html

<h1 th:text="'cart (' + ${lisize} + '건)'"></h1>

<form action="" method="post">
	<input type="hidden" name="uid" value="${session.username}">
	
	<div class="allCart" style="width: 800px; display: flex; flex-direction: column; align-items: center;">	
		<div style="width: 100%; display: flex; justify-content: flex-end; margin-bottom: 10px;">
		
			<input type="hidden" name="totalPay" id="totalPayInput" th:value="${totalPay}"><!-- totalPay값 입력 받을 영역 -->    
		    <span id="totalPayText"></span><!-- totalPay값, 즉 예상결제액 표시할 영역 -->
		    
		    &emsp;
		    <input type="submit"  value="주문하기" class=button>
		    	
		</div>
	</div>
	
	<!-- table은 추후 갤러리 식으로 바꿀까 -->
	
	<table width=800>
		<tr class="tr_color">
			<th>no</th> 
			<th>pname</th> 		
			<th>pimg</th>
			<th>pprice</th>		
			
			<th>pquantity</th>		
			<th>pdelifee</th>		 
			<th>hab</th>		 
			<th>del</th>		 
			
		</tr>
			
		<tr th:each="m, stat : ${li}" class="tr_color">	
			
			<td th:text="${stat.count}">
				<input type="hidden" name="cartid" th:value="${m.cartid}">
			</td>
			
			<td>
				<a th:href="@{/prd/viewPrd(product_code=${m.product_code})}" 
		        th:text="${m.pname}" target="_blank">
		        </a>	        
	        </td>		
			
			<td>		
				<a th:href="@{/prd/viewPrd(product_code=${m.product_code})}" target="_blank">
		            <img th:src="@{/img/}+${m.pimgStr}" width="100" />
		        </a>	        	        	
			</td>
					
			<td>
			    <input type="hidden" name="pprice" th:value="${m.pprice}">
			    <span th:text="${m.fmtPprice + '원'}">0</span>
			</td>
			
			<td>		
				<input type="number" name="pquantity" class="quantity-input" 
				th:value="${m.pquantity}" th:text="' / '+${m.pstock}" min="1" th:max="${m.pstock}">
				<!-- class="quantity-input" 통해 입력된 주문량을 js에 전달 -->
			</td>
			
			<td>
			    <input type="hidden" name="pdelifee" th:value="${m.pdelifee}">
			    <span th:text="${m.pdelifee == 0 ? '무료배송❗' : m.fmtPdelifee + '원'}">0</span>
			</td>			
					
			<td th:text="${m.pprice * m.pquantity + m.pdelifee}">합계</td>		
			
			<td>
				<a th:href="@{/cart/delCart(cartid=${m.cartid})}" >✖️</a>
			</td>		
		</tr>		
	</table>	
</form>

6, script

<script>
    document.addEventListener('DOMContentLoaded', function() {    	
        const quantityInputs = document.querySelectorAll('.quantity-input');
        const totalPayInput = document.getElementById('totalPayInput');
        const totalPayText = document.getElementById('totalPayText');
		
        // 천원단위에 콤마 찍기
        function formatNumber(number) {
            return number.toLocaleString();
        }
		
        //계산하기
        function updateTotals() {
            let totalPay = 0;
            quantityInputs.forEach(input => {
                const row = input.closest('tr');
                const price = parseFloat(row.querySelector('input[name="pprice"]').value);
                const quantity = parseInt(input.value, 10);
                const deliveryFee = parseFloat(row.querySelector('input[name="pdelifee"]').value);
                const itemTotal = (price * quantity) + deliveryFee;
                row.querySelector('td:nth-child(7)').textContent = formatNumber(itemTotal) + '원'; // cart별 hab 업데이트
                totalPay += itemTotal;
            });
            totalPayInput.value = totalPay;
            totalPayText.innerHTML = '예상결제액: <strong>' + formatNumber(totalPay) + '원</strong>'; // totalPay 업데이트
        }

        quantityInputs.forEach(input => {
            input.addEventListener('input', updateTotals); // 주문량 변경 시마다 각각 업데이트
        });

        updateTotals(); // 내장 계산기
    });
</script>
profile
기기 좋아하는 여자

0개의 댓글