스프링부트+jsp로 배달사이트 만들기-32 매출 관리

2021. 12. 20. 17:12스프링부트

매출관리 페이지를 추가합니다

admin -> sales.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ include file="/WEB-INF/view/include/link.jsp" %>
<link rel="stylesheet" href="/css/admin/sales.css" >
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js" integrity="sha512-qTXRIMyZIFb8iQcfjXWCO8+M5Tbc38Qi5WzdPOYZHIlZpzBHG3L3by84BBBOiRGiEb7KKtAOAs5qYdUiZiQNNQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<%@ include file="/WEB-INF/view/include/header.jsp" %>
  
  
    
	<main>
		<section>
  			<div class="today">
  				<span>
  					<span>오늘 매출</span>
  					<span id="today"></span>
  				</span>
  				
  				<button>상세보기</button>
  				
  			</div>
  			
  		</section>
  		
		<section class="detail today_detail">
  			<div>
				<h3 class="sales_today_detail_title">
					<span>매출 상세</span>
					<span>
						<button class="sort_name reverse">이름순</button>
						<button class="sort_price reverse">가격순</button>
					</span>
				</h3>
				
  				<div class="sales_today_detail">
  					<div>메뉴</div>
  					<div>수량</div>
  					<div>가격</div>
  				</div>
  			</div>
  		</section>
	
	
		<section class="graph_section" onselectstart="return false;" > 
			<div class="box">
				<button class="year_btn">연 매출</button>
				<button class="month_btn">이번 달 매출</button>
				<button class="week_btn">이번 주 매출</button>
				<input type="month"name="date" id="date">
				<button class="other_month_search">검색</button>
			
				<h1>1월 총 합계</h1>
				
				<div>(단위 : 만원)</div>
				<div class="graph_box">
					<ul>
					<!-- 
						<li>
							<span class="sales"></span>
							<div class="graph"></div>
							<span class="graph_date"></span>
						</li>
						 -->
					</ul>
				
					<div class="graph_background">
						<div></div>
						<div></div>
						<div></div>
						<div></div>
						<div></div>
					</div>
				</div>
			
			</div>
		</section>
		
		<section class="detail other_detail">
  			<div>
  				<h3 class="sales_today_detail_title">
  					<span id="other_detail_date"></span>
  					<span>
						<button class="sort_name reverse">이름순</button>
						<button class="sort_price reverse">가격순</button>
					</span>
  				</h3>
  				<div class="sales_today_detail">
  					<!-- <div>메뉴</div>
  					<div>수량</div>
  					<div>가격</div> -->
  				</div>
  			</div>
  		</section>
  		
	</main>

<script src="/js/util/util.js"></script>
<script src="/js/admin/sales.js"></script> 


</body>
</html>

 

admin -> sales.css

@charset "utf-8";

body {
	background: #F3F5F7;
}


main {
	width: 90%;
	max-width: 1200px;
	margin: 20px auto 100px;
}

section {
	background: #fff;	
	padding: 10px 5px;
	border: 1px solid #E0E5EE;
	margin-bottom: 20px;
}

section.detail {
	display: none;
    max-height: 400px;
    overflow-y: auto;
}

.today {
	display: flex;
	justify-content: space-between;	
	align-items: center;
}

.today #today {
	font-weight: bold;
	margin-left: 10px;
}

.sales_today_detail_title {
	margin-bottom: 5px; 
	font-weight: 500;
	display: flex;
	justify-content: space-between;
}

.sales_today_detail {
	display: grid;
	grid-template-columns: 3fr 1fr 1fr;
    grid-template-rows: 40px;
}

.sales_today_detail > div {
	border: 1px solid #ddd;
	line-height: 40px;
	padding-left: 10px; 
	white-space: nowrap;
    text-overflow: ellipsis;
    overflow: hidden;
}


section.graph_section {
	overflow-x: auto;	
}

section.graph_section::-webkit-scrollbar {
    display: none;
}


.box {
	min-width: 650px;
}


main button {
	padding: 5px 10px;
	border-radius: 5px;
	background: #fff;
	border: 1px solid #ccc;
	font-size: 1.2rem;
}

main #date {
	padding: 5px 10px;
	border-radius: 5px;
	font-weight: bold;
	border: 1px solid #ccc;
	font-size: 1.2rem;
}

main h1 {
	margin: 20px 0;
}

main .graph_box {
	position: relative;
	height: 55vh;
}

main .graph_box .graph_background {
	position: absolute;
	top: 0;
	width: 100%;
	height: 100%;
	
	display: none; 
	
}

main .graph_box .graph_background div {
	height: 20%;
	width: 100%;
	border: 1px solid #ddd;
	border-top: none;
}

main .graph_box ul {
	display: flex;
	height: 100%;
}

main .graph_box ul li {
	flex: 1;
	margin-right: 5px;
	display: flex;
	align-items: center;
	justify-content: end;
	flex-direction: column;
}

main .graph_box .graph {
	width: 30%;
	min-height: 3px;
	background: green;
	z-index: 1;
	border-radius: 5px 5px 0 0;
	position: relative;
	transition: 0.1s;
	cursor: pointer;
}

main .graph_box .graph:hover {
    transform: scaleX(1.2);
    background: red;
}

main .graph_box .sales {
	font-size: 1.15rem;
}

main .graph_box .graph_date {
	font-size: 1.15rem;
}



@media ( max-width :1250px) {
	#tab {
		width: 100%;
	}
}

@media ( max-width :1024px) {
	section.tab {
		margin-top: 0px;
		display: none;
	}
	.tab .box {
		display: block;
	}
	header .admin_page_btn {
		display: none;
	}
	header .menu_tab {
		display: block;
	}
}

@media ( max-width :767px) {
	main {
		width: 95%;
	}
	section.tab ul li {
		font-size: 16px;
		height: 50px;
	}
	section.tab ul li a {
		line-height: 50px;
	}
}

@media ( max-width :480px) {
	main {
		width: 100%;
	}
	
	
}

 

admin -> sales.js

$(document).ready(function(){
	
const pathArr = location.pathname.split("/");
const storeId = pathArr[pathArr.length-1];


const dateInput = document.getElementById("date");
dateInput.valueAsDate = new Date();

// getDetail이 true일때 상세보기 가능
let getDetail = true;

function graphDraw(data, format, title){
	let html = "";
	for(i=0;i<data.length-1;i++) {
		html +=
		`<li>
			<span class="sales"></span>
			<div class="graph" data-date="${data[i].orderDate}"></div>
			<span class="graph_date">${moment(data[i].orderDate).format(format) }</span>
		</li>`
	}
	$(".graph_box ul").html(html);
	
	
	if(!data[data.length-1]) {
		$("main h1").text(title +"0원");
		return;
	}
		
	const total = data[data.length-1].total;
	
	$("main h1").text(title + total.toLocaleString() + "원");
	
	for(i=0;i<data.length-1;i++) {
		const sum = data[i].total;
		const avg = sum / total * 100;
		
		$(".graph_box li").eq(i).find(".graph").css("height", avg +"%");
		
		if(sum != 0){
			$(".graph_box li").eq(i).find(".sales").text((sum/10000).toFixed(1));
		}
	}
}


	
function sales(term){
	const date = moment(new Date()).format("YYYY-MM");
	
	let title = "";
	let format = "";
	const data = {
		date : date,
		storeId : storeId
	};
	
	switch(term) {
		case "week": {
			format = "MM월 DD일";
			data.term = "week";
			title = "이번 주 총 합계 ";
			break;
		} 
		case "thisMonth": {
			format = "D";
			data.term = "month";
			title = moment(date).format("M") + "월 총 합계 ";
			break;
		} 
		
		case "month": {
			format = "D";
			data.date = $("#date").val();
			data.term = "month";
			title = moment(data.date).format("M") + "월 총 합계 ";
			break;
		}
		
		
		case "year": {
			format = "MM월"
			data.term = "year";
			title = moment(date).format("YYYY") + "년 총 합계 ";
			break;
		}
	}
	
	$.ajax({
		url: "/admin/management/sales",
		type: "GET",
		data: data
	})
	.done(function(result) {
		console.log(result);
		
		graphDraw(result, format, title);
		
	})
	.fail(function(data){
		alert("에러");
	})
}





// 그래프 가로 스크롤
(function(){
	let x;
	let left;
	let down;
	
  	const target = $(".graph_section");
	    
	target.mousedown(function(e){
	  down = true;
	  x = e.pageX;
	  left = $(this).scrollLeft();
	});
	
	$(window).mousemove(function(e){
	  if(down){
	    var newX = e.pageX;
	    target.scrollLeft(left - newX + x);
	  }
	});
	
	$(window).mouseup(function(e){
		down = false;
	});
})();
  
		
		
		
// 그래프 막대 그리기
function detailHtml(result){
	let html = `<div>메뉴</div>
				<div>수량</div>
				<div>가격</div>`;
				
	for(i=0;i<result.menuList.length;i++) {
		const menu = result.menuList[i];
		const option = menu.optionName == null ? "" : "[" + menu.optionName + "]";
		
		html +=
			`<div>${menu.foodName}${option }</div>
			 <div>${menu.amount}</div>
			 <div>${menu.totalPrice.toLocaleString()}</div>`;
	}
	
	return html;
}


// 하루 매출 상세보기
function salesDetail(data, fnc){
	$.ajax({
		url: "/admin/management/salesDetail",
		type: "GET",
		data: data
	})
	.done(function(result){
		fnc(result);
		
	})
	.fail(function(){
		alert("에러");
	})
}




// 기본페이지 주 매출
sales("week");

// 월 매출
$(".month_btn").click(function(){
	sales("thisMonth");
	getDetail = true;
})

// 다른 달 일 매출
$(".other_month_search").click(function(){
	sales("month");
	getDetail = true;
})

// 이번주 매출
$(".week_btn").click(function(){
	sales("week");
	getDetail = true;
})

// 올해 매출
$(".year_btn").click(function(){
	sales("year");
	getDetail = false;
})



// 오늘 매출 실행함수
const todayDetail = function(result){
	$("#today").text(result.total.toLocaleString() +"원");
	const html = detailHtml(result);
	
	$(".today_detail .sales_today_detail").html(html);
}

// 오늘 매출 보기
salesDetail({storeId : storeId}, todayDetail);


// 오늘 매출 상세 표시
$(".today button").click(function(){
	$(".today_detail").fadeToggle(200);
});


// 이름순 정렬
$(".today_detail .sort_name").click(function(){
	const data ={
		storeId : storeId, 
	};
	
	if($(this).hasClass("reverse")) {
		$(this).removeClass("reverse");
		data.sort = "nameR";
	} else {
		$(this).addClass("reverse");
		data.sort = "name";
	}
	
	salesDetail(data, todayDetail);
})


// 가격순 정렬
$(".today_detail .sort_price").click(function(){
	const data ={
		storeId : storeId, 
	};
	
	if($(this).hasClass("reverse")) {
		$(this).removeClass("reverse");
		data.sort = "priceR";
		
	} else {
		$(this).addClass("reverse");
		data.sort = "price";
	}
	
	salesDetail(data, todayDetail);
})



const detail = function(result) {
	const html = detailHtml(result);
	$(".other_detail .sales_today_detail").html(html);
}




// 그래프 막대 클릭
$(document).on("click", ".graph",  function(){
	if(getDetail) {
		const date = $(this).data("date");
		const data ={storeId : storeId, date : date};
		
		salesDetail(data, function(result) {
			$(".other_detail").show();
			const offset = $(".other_detail").offset().top /2;
			$("html").animate({ scrollTop: offset }, 400);
		
			const html = detailHtml(result);
			$(".other_detail .sales_today_detail").html(html);
            $("#other_detail_date").text(date);
		});
	}
})

// 다른날 상세보기 이름순 정렬 (그래프클릭)
$(".other_detail .sort_name").click(function(){
	const data ={
		storeId : storeId, 
	};
	
	if($(this).hasClass("reverse")) {
		$(this).removeClass("reverse");
		data.sort = "nameR";
	} else {
		$(this).addClass("reverse");
		data.sort = "name";
	}
	
	salesDetail(data, detail);
	
})

// 다른날 상세보기 가격순 정렬 (그래프클릭)
$(".other_detail .sort_price").click(function(){
	const data ={
		storeId : storeId, 
	};
	
	if($(this).hasClass("reverse")) {
		$(this).removeClass("reverse");
		data.sort = "priceR";
		
	} else {
		$(this).addClass("reverse");
		data.sort = "price";
	}
	
	salesDetail(data, detail);
})







})

 

 

매출 관리페이지 메서드를 추가합니다

@IsMyStore
@GetMapping("/admin/management/sales/{id}")
public String sales(@PathVariable long id) {
	return "admin/sales";
}

 

 

 

데이터를 불러오는 메서드가 없어서 에러가 나니 sales.js의 아래 부분을 주석처리 해놓습니다

 

 

오늘 매출 데이터를 불러올 메서드를 추가합니다

@IsMyStore
@GetMapping("/admin/management/salesDetail")
public ResponseEntity<Map<String, Object>> salesDetail(long storeId, String date){
	System.out.println("매출 상세");
	
	System.out.printf("가게 번호 : %d, 날짜 : %s ", storeId, date);
	Map<String, Object> salseToday = adminService.salesDetail(storeId, date);
	
	return new ResponseEntity<Map<String, Object>>(salseToday, HttpStatus.OK);
}

date는 2021-12-1 형태로 넘어오게 되는데 date가 null이라면 오늘 매출, date가 null이 아니라면 해당 날짜의 매출데이터를 가져옵니다

 

Service와 DAO를 추가합니다

AdminServiceImp

@Override
	public Map<String, Object> salesDetail(long storeId, String date) {
		List<SalesDetail> salesToday = adminDAO.salesDetail(storeId, date);
		long total = 0;
		
		List<Cart> menuList = new ArrayList<>();
		
		for(int i=0;i<salesToday.size();i++) {
			List<Cart> cartList = FoodInfoFromJson.foodInfoFromJson(salesToday.get(i).getFoodInfo());
			
			for(int j=0;j<cartList.size();j++) {
				Cart cart = cartList.get(j);
				if(menuList.contains(cart)) {
					
					int index = menuList.indexOf(cart);
					int amount = cart.getAmount();
					int price = cart.getTotalPrice();
					
					menuList.get(index).setAmount(amount + menuList.get(index).getAmount());
					menuList.get(index).setTotalPrice(price + menuList.get(index).getTotalPrice());
					
				} else {
					menuList.add(cartList.get(j));
				}
			}
			
			total += salesToday.get(i).getTotalPrice();
		}
		
		Map<String, Object> map = new HashMap<>();
		

		
		map.put("menuList", menuList);
		map.put("total", total);
		
		return map;
	}

salesDetail 메서드를 실행해 메뉴 목록과 메뉴가격의 합을 가져옵니다

DB에서 JSON_VALUE를 사용해 처리하고 싶었지만 optionName이 아래와 같이 []로 감싸져있어 값을 가져오는데 실패했습니다

{"optionName":["치즈크러스트로 변경","파스타 추가","베이컨 토핑 추가","치즈 토핑 추가"]}  

 

메뉴 목록을 가져와 장바구니추가 메서드를 구현할 때 처럼 같은 메뉴끼리 분류해 LIST에 담고 메뉴의 총합과 함께 반환합니다

 

AdminDAOImp

@Override
public List<SalesDetail> salesDetail(long storeId, String date) {
	Map<String, Object> map = new HashMap<>();
	map.put("storeId", storeId);
	map.put("date", date);
	
	return sql.selectList("admin.salesDetail", map);
}

 

AdminMapper

<select id="salesDetail" resultType="SalesDetail">
	WITH T_ORDER AS (
	    SELECT * FROM (
	        SELECT ORDER_NUM, STORE_ID, ORDER_DATE, TOTAL_PRICE, DELEVERY_STATUS FROM BM_ORDER_USER
	        UNION ALL
	        SELECT ORDER_NUM, STORE_ID, ORDER_DATE, TOTAL_PRICE, DELEVERY_STATUS FROM BM_ORDER_NON_USER
	    )
	    WHERE     STORE_ID = #{storeId }
	    AND   	  DELEVERY_STATUS = '배달 완료'
	    <if test="date == null">
	    AND   TO_CHAR(ORDER_DATE, 'YYYYMMDD') = TO_CHAR(SYSDATE, 'YYYYMMDD')
	    </if>
	    
	    <if test="date != null">
	    AND   TO_CHAR(ORDER_DATE, 'YYYY-MM-DD') = #{date }
	    </if>
	    
	),
	T_DETAIL AS (
	    SELECT  ORDER_NUM, 
	            LISTAGG(FOOD_INFO, '/') FOOD_INFO
	    FROM BM_ORDER_DETAIL_USER N
	    GROUP BY    ORDER_NUM
	    UNION ALL
	    SELECT  ORDER_NUM, 
	            LISTAGG(FOOD_INFO, '/') FOOD_INFO
	    FROM BM_ORDER_DETAIL_NON_USER N
	    GROUP BY    ORDER_NUM
	)
	SELECT      TOTAL_PRICE
	            ,FOOD_INFO
	FROM        T_ORDER O
	LEFT JOIN   T_DETAIL D
	ON          O.ORDER_NUM = D.ORDER_NUM
	</select>

 

 

 

이름순 가격순 정렬을 기능을 추가하기위해  기존 salesDetail 클래스에 sort를 추가로 받을 수 있게합니다

sort의 값으로는 name, nameR, price, prcieR이 넘어옵니다(오름차순, 내림차순)

@IsMyStore
@GetMapping("/admin/management/salesDetail")
public ResponseEntity<Map<String, Object>> salesDetail(long storeId, String date, String sort){
	System.out.println("매출 상세");
	
	System.out.printf("가게 번호 : %d, 날짜 : %s, 정렬 : %s%n ", storeId, date, sort);
	Map<String, Object> salseToday = adminService.salesDetail(storeId, date, sort);
	
	return new ResponseEntity<Map<String, Object>>(salseToday, HttpStatus.OK);
}

 

util 패키지에 메뉴목록 정렬 클래스를 추가합니다

 

SalesSort

package com.baemin.util;

import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import com.baemin.dto.Cart;

public class SalesSort {

	public SalesSort(List<Cart> menuList, String sort) {
		sort(menuList, sort);
	}
	
	public void sort(List<Cart> menuList, String sort) {
		
		Collections.sort(menuList, new Comparator<Cart>() {
			@Override
			public int compare(Cart o1, Cart o2) {
				
				// 가격 오름차순
				if("price".equals(sort)) {
					return o1.getTotalPrice() - o2.getTotalPrice();
				}
				
				// 가격 내림차순
				else if("priceR".equals(sort)) {
					return o2.getTotalPrice() - o1.getTotalPrice();
				}
				
				// 기본 정렬 이름 오름차순
				else {
					String name1= o1.getFoodName();
					String name2= o2.getFoodName();
					
					if(name1.compareTo(name2) == 0) {
						String[] option1 = o1.getOptionName();
						String[] option2 = o2.getOptionName();
						if(option1 == null) {
							return 1;
						}
						if(option2 == null) {
							return -1;
						}
						
						return option1[0].compareTo(option2[0]);
					}
					
					if("nameR".equals(sort)) {
						return o2.getFoodName().compareTo(o1.getFoodName());
					} else {
						return o1.getFoodName().compareTo(o2.getFoodName());
					}
				}
			}
		});
	}
}

생성자를 통해 객체 생성시 sort메서드를 실행하게 했습니다

 

Collections.sort()메서드에 Comparator의 compare 메서드를 오버라이딩 해서 정렬 기준을 정해줍니다

 

// 가격 오름차순
if("price".equals(sort)) {
	return o1.getTotalPrice() - o2.getTotalPrice();
}

// 가격 내림차순
else if("priceR".equals(sort)) {
	return o2.getTotalPrice() - o1.getTotalPrice();
}

두 객체의 totalPrice를 - 했을때 값이 음수냐 양수냐에 따라 오름차순 내림차순이 결정됩니다

 

// 기본 정렬 이름 오름차순
else {
	String name1= o1.getFoodName();
	String name2= o2.getFoodName();
	
	if(name1.compareTo(name2) == 0) {
		String[] option1 = o1.getOptionName();
		String[] option2 = o2.getOptionName();
		if(option1 == null) {
			return 1;
		}
		if(option2 == null) {
			return -1;
		}
		
		return option1[0].compareTo(option2[0]);
	}
	
	if("nameR".equals(sort)) {
		return o2.getFoodName().compareTo(o1.getFoodName());
	} else {
		return o1.getFoodName().compareTo(o2.getFoodName());
	}
}

두 객체의 이름을 비교하고 이름이 같다면 옵션의 이름을 기준으로 정렬을 합니다

 

AdminServiceImp의 salesDetail메서드에 추가한 정렬 클래스를 실행합니다

 

 

 

 

가격순,이름순 버튼 클릭시 정렬이 되고 한번 더 클릭시 역정렬이 됩니다

 

 


매출 그래프

 

주석 처리했던 sales.js의 아래 부분 주석을 해제합니다

 

 

매출 그래프 데이터를 불러올 메서드를 추가합니다

@IsMyStore
@GetMapping("/admin/management/sales")
public ResponseEntity<List<Sales>> sales(long storeId, String date, String term) {
//		term =
//		week, month, year
	
	List<Sales> sales = adminService.sales(storeId,date, term);
	return new ResponseEntity<List<Sales>>(sales, HttpStatus.OK);
}

 

 

 

date는 달력 선택 후 검색하기를 눌렀을 때 2021-12 형태로 년-달 까지만 받습니다

term은 이번주,이번달,연 매출 클릭 시 각각 week, month, year 값으로 넘어옵니다

 

Service와 DAO에 추가합니다

 

AdminServiceImp

@Override
public List<Sales> sales(long storeId, String date, String term) {
	date = date + "-01";
	
	Map<String, Object> map = new HashMap<>();
	map.put("storeId", storeId);
	map.put("date", date);
	map.put("term", term);
	
	return adminDAO.sales(map);
}

date의 형태가 2021-12 처럼 달까지만 표시되기 때문에 -01을 붙여 2021-12-01처럼 만들어줍니다

 

AdminDAOImp

@Override
public List<Sales> sales(Map<String, Object> map) {
	return sql.selectList("admin.sales", map);
}

 

AdminMapper

<select id="sales" resultType="Sales">
	
	WITH T_ORDER AS (
	    SELECT  STORE_ID
	    		,TO_DATE(ORDER_DATE) ORDER_DATE
                ,TOTAL_PRICE
        FROM (
	        SELECT TO_CHAR(ORDER_DATE, 'YYYY/MM/DD') ORDER_DATE, STORE_ID, TOTAL_PRICE, DELEVERY_STATUS FROM BM_ORDER_USER
	        UNION ALL
	        SELECT TO_CHAR(ORDER_DATE, 'YYYY/MM/DD') ORDER_DATE, STORE_ID, TOTAL_PRICE, DELEVERY_STATUS FROM BM_ORDER_NON_USER
	    )
	    WHERE	STORE_ID = #{storeId }
	    AND   	DELEVERY_STATUS = '배달 완료'
	    <choose>
	    	<when test="term == 'year'">
	    	AND   ORDER_DATE BETWEEN TRUNC(TO_DATE(#{date }), 'YYYY') AND LAST_DAY(#{date })
	    	</when>
	    
	   		<otherwise>
	    	AND   ORDER_DATE BETWEEN TRUNC(TO_DATE(#{date }), 'MM') AND LAST_DAY(#{date })
	    	</otherwise>
	    </choose>
	)
	<if test="term == 'month'">
    SELECT	CAL.ORDER_DATE
    		,SUM(O.TOTAL_PRICE) TOTAL 
 	FROM (
 		
        SELECT FIRST_DAY + LEVEL -1 ORDER_DATE
        FROM    (
            SELECT TRUNC(TO_DATE(#{date }), 'MM') FIRST_DAY FROM DUAL
        ) 
        CONNECT BY FIRST_DAY + LEVEL -1 <![CDATA[ <= ]]> LAST_DAY(#{date })
    ) CAL
    LEFT JOIN   T_ORDER O
    ON          CAL.ORDER_DATE = O.ORDER_DATE
    GROUP BY ROLLUP(CAL.ORDER_DATE) 
    ORDER BY ORDER_DATE
	</if>
	
	
	<if test="term == 'week'">
    SELECT	CAL.ORDER_DATE
    		,SUM(O.TOTAL_PRICE) TOTAL 
 	FROM (
 		
        SELECT FIRST_DAY + LEVEL -1 ORDER_DATE
        FROM    (
            SELECT TRUNC(SYSDATE, 'IW') FIRST_DAY FROM DUAL
        ) 
        CONNECT BY FIRST_DAY + LEVEL <![CDATA[ <= ]]> FIRST_DAY + 7 
    ) CAL
    LEFT JOIN   T_ORDER O
    ON          CAL.ORDER_DATE = O.ORDER_DATE
    GROUP BY ROLLUP(CAL.ORDER_DATE) 
    ORDER BY ORDER_DATE
   	</if>
        
        
        
        
	<if test="term == 'year'">
    SELECT	TRUNC(CAL.ORDER_DATE, 'MM') ORDER_DATE
    		,SUM(O.TOTAL_PRICE) TOTAL 
 	FROM (
       SELECT ADD_MONTHS(FIRST_DAY, LEVEL -1) ORDER_DATE
        FROM    (
            SELECT TRUNC(SYSDATE, 'YYYY') FIRST_DAY FROM DUAL
        ) 
        CONNECT BY LEVEL <![CDATA[ <= ]]> 12
    ) CAL
    LEFT JOIN   T_ORDER O
    ON          CAL.ORDER_DATE = TRUNC(O.ORDER_DATE, 'MM')
    GROUP BY ROLLUP(TRUNC(CAL.ORDER_DATE, 'MM')) 
    ORDER BY ORDER_DATE
    </if>
	
	</select>

여러가지 날짜 함수들과 형변환을 해야해서 까다로웠습니다

 

 

WITH T_ORDER AS (
	SELECT  STORE_ID
			,TO_DATE(ORDER_DATE) ORDER_DATE
			,TOTAL_PRICE
	FROM (
		SELECT TO_CHAR(ORDER_DATE, 'YYYY/MM/DD') ORDER_DATE, STORE_ID, TOTAL_PRICE, DELEVERY_STATUS FROM BM_ORDER_USER
		UNION ALL
		SELECT TO_CHAR(ORDER_DATE, 'YYYY/MM/DD') ORDER_DATE, STORE_ID, TOTAL_PRICE, DELEVERY_STATUS FROM BM_ORDER_NON_USER
	)
	WHERE	STORE_ID = #{storeId }
	AND   	DELEVERY_STATUS = '배달 완료'
	<choose>
		<when test="term == 'year'">
		AND   ORDER_DATE BETWEEN TRUNC(TO_DATE(#{date }), 'YYYY') AND LAST_DAY(#{date })
		</when>
	
		   <otherwise>
		AND   ORDER_DATE BETWEEN TRUNC(TO_DATE(#{date }), 'MM') AND LAST_DAY(#{date })
		</otherwise>
	</choose>
  1. 주문 테이블을 UNION ALL 합니다
  2.  TRUNC 함수의 두번째 인자에 넣어준 패턴에 따라 #{date}를 달까지만 표시할지 해까지만 표시할지 정해줄수있습니다
  3. LAST_DAY 함수를 사용하면 해당 달의 마지막 날짜를 구할 수 있습니다
  4. term이 year라면 #{date}를 해까지 표시하고 2021-12-22 => 2021-01-01,  #{date}달의 마지막 날짜까지 검색합니다 2021-12-22 => 2021-12-31
  5.  term이 year가 아니라면 #{date}를  2021-12-22 => 2021-12-01 변환 후 마지막 달까지 검색합니다

 

<if test="term == 'month'">
    SELECT	CAL.ORDER_DATE
    		,SUM(O.TOTAL_PRICE) TOTAL 
 	FROM (
 		
        SELECT FIRST_DAY + LEVEL -1 ORDER_DATE
        FROM    (
            SELECT TRUNC(TO_DATE(#{date }), 'MM') FIRST_DAY FROM DUAL
        ) 
        CONNECT BY FIRST_DAY + LEVEL -1 <![CDATA[ <= ]]> LAST_DAY(#{date })
    ) CAL
    LEFT JOIN   T_ORDER O
    ON          CAL.ORDER_DATE = O.ORDER_DATE
    GROUP BY ROLLUP(CAL.ORDER_DATE) 
    ORDER BY ORDER_DATE
 </if>

mybatis에서 부등호를 사용하려면 <![CDATA[]]로 감싸서 사용해야합니다

 

  1. term이 month일 경우 해당 달의 FIRST_DAY를 구하고 LAST_DAY함수로 마지막 날짜를 구합니다
  2. LEVEL, CONNECT BY를 사용해 첫날 ~ 마지막날까지 달력을 만듭니다
  3. 이 달력과 T_ORDER 테이블을 LEFT JOIN합니다
  4. ROLLUP을 사용해 ORDER_DATE를 기준으로 합계를 구합니다

 

 

 

term이 week와 year때돼 마찬가지로 일주일, 1년 달력을 만들고 달력과 T_ORDER를 조인했습니다