스프링부트+jsp로 배달사이트 만들기-33 내 포인트, 리뷰 확인

2021. 12. 22. 01:34스프링부트

포인트 확인 페이지

user 패키지에

jsp, css, js를 추가합니다

 

point.js

$(document).ready(function(){
	$(".point_regi button").click(function(){
		$(".point_number_area").fadeToggle(100);
	})
	
	$(".point_number_area input[type=button]").click(function(){
		
		  const giftCardNum = $(".point_number").val().replaceAll(" ", "");
		  
		  if(!giftCardNum) {
			  return;
		  }
		  
	   $.ajax({
		   url: "/user/pointRegist",
			 data: {giftCardNum : giftCardNum},
		   type: "POST"
	   })
	   .done(function(result){
		   const point = result.point; 
		   swal(point.toLocaleString() + "원이 적립되었습니다");
		   
		   const newPoint = Number($("#my_point").data("point") + point); 
		   $("#my_point").text(newPoint.toLocaleString());
		   $("#my_point").data("point", newPoint);
		   
		   $(".point_number_area").fadeToggle(100);
			htmlWrite(result);  		   
		})
		.fail(function(result){
		   if(!result.responseJSON) {
			   alert("에러");
		   } else {
			   swal(result.responseJSON.errorMsg);
		   }
		})
	})
	
	
   function htmlWrite(result){
		const date = new Date();
		const now =  date.getFullYear() + "." + (date.getMonth() + 1) + "." + date.getDate();
		const html = 
			`<li>
			   <div>
					  <div>${result.info }</div>
				   <div>${now }</div>
			   </div>

			   <div>
				   <div class="plus">${result["point"].toLocaleString() }</div>
			   </div>
		   </li><hr>`;
		$(".point_his").prepend(html);
	}     
})

 

 

point.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/user/point.css" >
<%@ include file="/WEB-INF/view//include/header.jsp" %>
	
    <section class="title">
        <h1>포인트</h1>
    </section>
	
	<main>
		<div class="my_point">
            <div>
                <span>보유 포인트</span>
                <span id="my_point" data-point="${point }"><fm:formatNumber value="${point }" /></span>
            </div>
            
            <div class="point_regi">
                <div>
                    <button>상품권 등록하기</button>
                </div>
				
                <div class="point_number_area">
					<div>
	                    <input type="text" class="point_number" maxlength="20" name="giftCardNum" placeholder="상품권 번호를 입력해주세요">
	                    <input type="button" value="등록">
	                </div>
                </div>
            </div>
            
		<div style="font-size: 15px; margin-top: 10px;">상품권번호 QKRTNALS</div>
		
		
        </div>
		
		<h2>포인트 이용 내역</h2><hr>
		<ul class="point_his">
			<c:forEach items="${myPoint }" var="myPoint">
				<li>
	                <div>
	                    <div>${myPoint.info }</div>
	                    <div><fm:formatDate value="${myPoint.usedDate }" pattern="yyyy.MM.dd" /> </div>
	                </div>
	
	                <div>
	                	<c:if test="${myPoint.point > 0 }">
	                		<div class="plus"><fm:formatNumber value="${myPoint.point }" pattern="###,###"/></div>
	                	</c:if>
	                	
	                	<c:if test="${myPoint.point < 0 }">
	                		<div class="used"><fm:formatNumber value="${myPoint.point }" pattern="###,###"/></div>
	                	</c:if>
	                	
	                </div>
				</li><hr>
	
			</c:forEach>
			
		</ul>
	</main>
	
	
<%@ include file="/WEB-INF/view//include/nav.jsp" %>

<%@ include file="/WEB-INF/view//include/footer.jsp" %>
	
<script src="/js/user/point.js"></script>
	
</body>
</html>

 

 

point.css

@charset "utf-8";
section.title {
	width: 100%;
}

section.title h1 {
	text-align: center;
	margin: 30px 0 30px 0;
}

main {
	width: 90%;
	max-width: 1000px;
	margin: 0 auto;
	padding-bottom: 30px;
}

main h2 {
	margin-top: 30px;
	padding: 15px
}

.my_point {
	text-align: center;
}

#my_point:after {
	content: '원';
}

.my_point div {
	font-size: 3rem;
}

.my_point .point_regi {
	margin: 0 auto;
	margin-top: 20px;
	border-radius: 10px;
	border: 1px solid #999;
	width: 40%;
	min-width: 500px;
}

.my_point .point_regi button {
	height: 50px;
	font-size: 2rem;
	color: #28a8a8;
	background: none;
	border: none;
	width: 100%;
}

.my_point .point_regi .point_number_area {
	display: none;	
}

.my_point .point_regi .point_number_area > div {
	border-top: 1px solid #ddd;
	height: 50px;
	display: flex;
	justify-content: space-between;
	padding: 0 15px;
	align-items: center;
}

.my_point .point_regi .point_number_area input {
	border: none;
	height: 70%;
}

.my_point .point_regi .point_number_area input[type=text] {
	flex: 5;
}

.my_point .point_regi .point_number_area input[type=button] {
	flex: 1;
	background: #30DAD9;
	color: #fff;
}

main ul li {
	display: flex;
	justify-content: space-between;
	padding: 15px;
}

main ul li>div:last-child {
	text-align: right;
}

main ul li .plus {
	color: #FF7701;
}

main ul li .plus:before {
	content: '+';
}

main ul li .plus:after {
	content: '원 적립';
}

main ul li .used:after {
	content: '원 사용';
}


@media ( max-width : 767px) {
	main {
		width: 100%;
		max-width: 1000px;
		margin: 0 auto;
	}
	.my_point .point_regi {
		width: 95%;
		border-top: 1px solid #ddd;
		border-bottom: 1px solid #ddd;
		min-width: 0;
	}
	.my_point .point_regi .point_number_area input[type=button] {
		border-radius: 10px;
	}
}

 

 

UserController에 내 포인트 페이지 메서드를 추가합니다

@GetMapping("/user/point")
public String point(@AuthenticationPrincipal LoginService user, Model model) {
	long id = user.getUser().getId();
	List<Point> myPoint = userService.myPoint(id);
	model.addAttribute("myPoint", myPoint);
	model.addAttribute("point", user.getUser().getPoint());
	
	return "user/point";
}

 

UserServiceImp

@Override
public List<Point> myPoint(long id) {
	return userDAO.myPoint(id);
}

 

UserDAOImp

@Override
public List<Point> myPoint(long id) {
	return sql.selectList("user.myPoint", id);
}

 

UserMapper

<select id="myPoint" resultType="Point">
	SELECT 
		USER_ID
		,USED_DATE
		,INFO
		,POINT
	FROM
		BM_POINT    
	WHERE 
		USER_ID = #{id }
	ORDER BY
		USED_DATE DESC
</select>

 

 

 

 

상품권 등록할 메서드를 추가합니다

@PostMapping("/user/pointRegist")
public ResponseEntity<Map<String, Object>> pointRegist(String giftCardNum, @AuthenticationPrincipal LoginService user, HttpSession session) {
	ResponseEntity<Map<String, Object>> point = userService.pointRegist(giftCardNum, user);
	return point;
}

상품권 번호와 유저정보를 파라미터로 상품권을 등록합니다

 

UserServiceImp

@Transactional
@Override
public ResponseEntity<Map<String, Object>> pointRegist(String giftCardNum, LoginService user) {
	long userId = user.getUser().getId();
	
	Map<String, Object> giftCard = userDAO.getGiftCart(giftCardNum, userId);
	
	if(giftCard != null) {
		String info = giftCard.get("info").toString();
		int point = Integer.parseInt(giftCard.get("point").toString());
		int id = Integer.parseInt(giftCard.get("userId").toString());
		
		if(id == 0) { // 등록한 적 없을 때
			userDAO.pointRegist(userId, info, giftCardNum, point);
			UserInfoSessionUpdate.sessionUpdate(point+"", "point", user);
			return new ResponseEntity<Map<String,Object>>(giftCard, HttpStatus.OK);
			
		} else { // 이미 등록한 카드
			Map<String, Object> errorMsg = new HashMap<>();
			errorMsg.put("errorMsg", "이미 등록한 번호입니다");
			
			return new ResponseEntity<Map<String,Object>>(errorMsg, HttpStatus.BAD_REQUEST);
		}
	}
	Map<String, Object> errorMsg = new HashMap<>();
	errorMsg.put("errorMsg", "잘못된 번호입니다");
	
	System.out.println(errorMsg);
	return new ResponseEntity<Map<String,Object>>(errorMsg, HttpStatus.BAD_REQUEST);
}

getGiftCart()를 실행해 DB에 상품권 번호가 있는지 검색합니다

상품권이 없다면 Map에 errorMsg를 담아 리턴합니다

 

상품권이 있다면 유저id를 넘겨 해당 상품권을 사용한 적이 있는지 확인합니다

사용한적이 없다면 등록하고 sessionUpdate()를 실행해 세션을 업데이트 합니다

 

기존의 sessionUpdate()의 파라미터에 HttpSession을 빼고 메서드 안에서 session을 받아오게 수정합니다

다른 클래스에서 기존 sessionUpdate()을 사용하던 코드들을 수정합니다

public class UserInfoSessionUpdate {

	public static void sessionUpdate(String value, String valueType, LoginService user) {
		ServletRequestAttributes attr = (ServletRequestAttributes)RequestContextHolder.currentRequestAttributes();
		HttpSession session = attr.getRequest().getSession();
		
		LoginService loginService = (LoginService) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
		
		if(valueType.equals("nickname")) {
			loginService.getUser().setNickname(value);
		} 
		else  if(valueType.equals("password")) {
			loginService.getUser().setPassword(value);
		}
		else if(valueType.equals("point")) {
			int point = loginService.getUser().getPoint() + Integer.parseInt(value);
			loginService.getUser().setPoint(point);
		}

		SecurityContext sc = SecurityContextHolder.getContext();

		sc.setAuthentication(new UsernamePasswordAuthenticationToken(loginService, null, user.getAuthorities()));
		
		session.setAttribute("SPRING_SECURITY_CONTEXT", sc);
	}
}

 

 

UserDAOImp

@Override
public Map<String, Object> getGiftCart(String giftCardNum, long userId) {
	Map<String, Object> map = new HashMap<>();
	map.put("userId", userId);
	map.put("giftCardNum", giftCardNum);
	return sql.selectOne("user.getGiftCart", map);
}

@Override
public void pointRegist(long userId, String info, String giftCardNum, int point) {
	Map<String, Object> map = new HashMap<>();
	map.put("userId", userId);
	map.put("info", info);
	map.put("giftCardNum", giftCardNum);
	map.put("point", point);
	sql.insert("user.pointRegist", map);
}

 

UserMapper

<select id="getGiftCart" resultType="Map">
	SELECT	    POINT "point"
				,INFO "info"
				,NVL(U.USER_ID, 0) "userId"
	FROM	    BM_GIFT_CARD G
	LEFT JOIN   (SELECT * FROM BM_USED_CARD WHERE USER_ID = #{userId }) U
	ON          G.GIFT_CARD_NUM = U.GIFT_CARD_NUM
	WHERE 	    G.GIFT_CARD_NUM = #{giftCardNum }
</select>



<insert id="pointRegist" >
	INSERT ALL
		INTO BM_POINT (
			USER_ID
			,INFO
			,POINT
		) VALUES (
			#{userId }
			,#{info }
			,#{point }
		)
		
		INTO BM_USED_CARD (
			USER_ID
			,GIFT_CARD_NUM
		) VALUES (
			#{userId }
			,#{giftCardNum }
		)
	SELECT * FROM DUAL	
</insert>

getGiftCard는 따로 dto를 생성하지않고 map으로 받았습니다

 

 

DB에서 사용한 상품권번호를 저장할 테이블을 생성합니다

CREATE TABLE BM_USED_CARD (
    USER_ID NUMBER,
    GIFT_CARD_NUM VARCHAR2(50)
);

 

상품권을 하나 추가합니다

INSERT INTO BM_GIFT_CARD VALUES ('QKRTNALS', 10000, '상품권 포인트 충전');
COMMIT;

 

 

 

 

두 번 등록했을때

 

 


내 리뷰 페이지

 

user패키지에 jsp, css js를 추가합니다

 

myReview.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/user/myReview.css">
<link rel="stylesheet" href="/css/modal.css" >
<link rel="stylesheet" href="/css/user/modal_review.css">

<%@ include file="/WEB-INF/view//include/header.jsp"%>

<main>
	<section id="main">

		<h1>리뷰 관리</h1>

		<h2>내가 쓴 리뷰</h2>

		<ul class="comment">

			<c:forEach items="${myReviewList }" var="myReviewList" >
				<li>
				<input type="hidden" value="${myReviewList }" class="review" >
					<div class="myReview">
						<div class="btn_box">
							<button class="modify_btn">수정하기</button>
							<button class="delete_btn">삭제</button>
							<input type="hidden" value="${myReviewList.orderNum }" class="review_order_num">
							<input type="hidden" value="${myReviewList.score }" class="review_score">
							<input type="hidden" value="${myReviewList.reviewImg }" class="review_img">
							<input type="hidden" value="${myReviewList.storeId }" class="review_store_id">
							<input type="hidden" value="${myReviewList.reviewContent }" class="review_review_content">
						</div>
						
						<div class="title">
							<h3>${myReviewList.storeName }</h3>
 
 							<c:forEach begin="0" end="4" var="i">
 								<c:if test="${myReviewList.score >= i}">
 									<i class="fas fa-star"></i>
 								</c:if> 
 								<c:if test="${myReviewList.score < i}">
 									<i class="far fa-star"></i>
 								</c:if> 
 							
 							</c:forEach>
							<span><fm:formatDate value="${myReviewList.regiDate }" pattern="yyyy-MM-dd" /></span>
						</div>

						<c:if test="${!empty myReviewList.reviewImg }">
							<div class="img_box">
								<img alt="이미지" src="${myReviewList.reviewImg }">
							</div>
						</c:if>

						<div class="text">${myReviewList.reviewContent }</div>
					</div> 
					
					<c:if test="${!empty myReviewList.bossComment }">
						<div class="boss_comment">
							<h3>사장님</h3>
							<div>${myReviewList.bossComment }</div>
						</div>
					</c:if>
				</li>
			</c:forEach>


		</ul>

	</section>
</main>


	<%@ include file="/WEB-INF/view/include/nav.jsp"%>

	<%@ include file="/WEB-INF/view/include/footer.jsp"%>
	
	<%@ include file="/WEB-INF/view/modal/modal_review.jsp"%>

	<script src="/js/user/myReview.js"></script>



</body>
</html>

 

기존의 orderList.css에서 리뷰모달 관련 코드를 modal_review.css를 만들어 이동합니다

 

orderList.css

@charset "UTF-8";

body {
	background: #F6F6F6;
}
a:hover {
	color: unset;
}

.wrap {
	width: 1000px;
	margin: 0 auto;
	display: flex;
	flex-wrap: wrap;
	min-height: calc(100vh - 210px);
}

.temp_img {
	margin: 20px auto;
	display: block;
	width: 90%;
	max-width: 500px;
}


section.title {
	width: 100%;
}

section.title h1 {
	text-align: center;
	margin: 30px 0 50px 0;
}

/* main */
main {
	width: 100%;
}

main ul .order_box {
	width: 1000px;
	height: 150px;
	margin: 0 auto;
	border-bottom: 1px solid #ccc;
	background: cornsilk;
	padding: 20px;
}

#order_list li {
	border: 1px solid #ddd;
	display: flex;
	padding: 15px;
}

#order_list li .img_box {
	height: 130px;
	width: 130px;
	border: 1px solid #ddd;
	border-radius: 10px;
	margin-right: 20px;
	overflow: hidden;
}

#order_list li .img_box img {
	width: 100%;
	height: 100%;
}

#order_list li .info_box {
	flex: 4;
	overflow: hidden;
}

#order_list li .info_box h2,
#order_list li .info_box > span {
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

#order_list li .review_btn_box {
	flex: 1;
	text-align: center;
}

#order_list li .review_btn_box button {
	height: 30px;
	width: 100px;
	background: none;
	margin-bottom: 10px;
	font-size: 1.3rem;
}



@media ( max-width :1023px) {
	.wrap {
		width: 99%;
	}
	section.title {
		height: 80px;
	}

}

@media ( max-width :767px) {
	.wrap {
		width: 99%;
		min-height: calc(100vh - 160px);
	}
	
	#order_list li .img_box {
		height: 100px;
		width: 100px;
		margin-right: 15px;
	}
	
	#order_list li .review_btn_box button {
		width: 80px;
		border-radius: 10px;
	}
}



@media ( max-width :480px) {
	#order_list li {
		padding: 15px 5px;
	}

	#order_list li .review_btn_box button {
		width: 60px;
	}
	
	#order_list li .img_box {
		margin-right: 10px;
	}
	
}

 

 

modal_review.css를 생성합니다

@charset "UTF-8";


.modal  .modal_box {
	height: calc(100% - 80px);
}

.modal form {
	display: inline;
}

.modal .score_box {
	width: 80%;
	margin: 0 auto;
	text-align: center;
	padding: 20px;
}
.modal .score_box i {
	font-size: 22px;
}

.modal .score_box .fas {
	color: gold;
}

.modal .review_text {
	margin: 0 auto;
	width: 90%;
}

.modal .review_text textarea {
	width: 90%;
	margin: 0 auto 30px auto;
	display: block;
	padding: 5px;
	font-size: 1.6rem;;
	background: #eee;
	border: none;
}


.modal .review_submit_btn {
	background: #ddd;
}


@media ( max-width :767px) {
	.modal .review_btn_box {
		right: 0px;
		top: 5px
	}
	.modal .review_btn_box button {
		width: 80px;
		font-size: 10px;
	}
}

 

 

myReview.css

@charset "utf-8";
.title .fas {
	color: gold;
}

#main {
	width: 100%;
	max-width: 1000px;
	margin: 20px auto ;
	min-height: calc(100vh - 250px);
}

#main h1 {
	text-align: center;
	margin-bottom: 30px;
}

#main li {
	position: relative;
	margin: 10px 0 20px 0px;
}

#main .myReview,
#main .boss_comment {
	border: 1px solid #ddd;
	border-radius: 10px;
	padding: 10px;
	margin-bottom: 10px;
}


#main .comment .btn_box {
	position: absolute;
	right: 15px;
	top: 15px;
}

#main .comment .btn_box button {
	padding: 5px;
	border-radius: 5px;
	background: #fff;
}



#main .comment .title {
	margin-bottom: 10px;
}

#main .img_box img {
	width: 30%;
	border-radius: 10px;
}

#main .text {
	font-size: 15px;
}

#main .boss_comment {
	width: 90%;
	background: #ddd;
	margin-left:auto; 
}

.boss_comment h3 {
	margin-bottom: 5px;
}




@media  (max-width:767px) {
	#main {
		width: 95%;
	}
	
	button {
		-webkit-appearance : none;
	}

}

 

 

myReview.js

$(document).ready(function(){
	
	
	$(".modify_btn").click(function(){
		const orderNum = $(this).siblings(".review_order_num").val();
		const reviewImg = $(this).siblings(".review_img").val();
		const storeId = $(this).siblings(".review_store_id").val();
		const reviewContent = $(this).siblings(".review_review_content").val();
		const modal = $(".review_modify_modal")
		
		// 기본 상태 초기화
		modal.find("textarea").val(reviewContent);
		modal.find(".order_num").val(orderNum);
		modal.find(".store_id").val(storeId);
		modal.find(".score").val(0);
		modal.find(".score_box i").removeClass("fas");
		
		const previewBox = $(".preview_box");
		const preview = previewBox.find(".preview");
		
		if(reviewImg) {
			modal.find(previewBox).show();
			preview.attr("src", reviewImg);
		} else {
			modal.find(previewBox).hide();
			preview.attr("src", "");
		}
		
		openModal(modal);
		
		
		
		
		// 별점주기
		let score = 0;
	
		$(".score_box i").off().click(function() {
			score = $(this).index() + 1;
				
			$(".score_box i").removeClass("fas");
			$(this).addClass("fas").prevAll().addClass("fas");
	
			modal.find(".score").val(score);
	
			inputCheck(modal);
		});
		
		
		
		$(".review_text textarea").off().keyup(function() {
			inputCheck(modal);
		})
		
		
		
		// 리뷰 작성, 별점 체크 했는지 확인
		function inputCheck(modal) {
			let text = modal.find(".review_text textarea").val();
			let score = modal.find(".score").val();
			
			if(text.length == 0 || score == "" || score == null) {
				modal.find(".review_submit_btn").css("background", "#ddd");
				modal.find(".review_submit_btn").attr("disabled", true);
			} else {
				modal.find(".review_submit_btn").attr("disabled", false);
				modal.find(".review_submit_btn").css("background", "#30DAD9");
			}
		}
		
		
	})
	
	
	

	$(".delete_btn").click(function() {
		const orderNum = $(this).siblings(".review_order_num").val();
		const target = $(this).parents("li");
		
		swal({
			buttons: ["취소", "삭제"],
			text: "리뷰를 삭제합니다",
		})
		.then(function(value) {
			if (value) {
				
				 $.ajax({
					url: "/user/review",
					type: "DELETE",
					data: {orderNum : orderNum},
				})
				.done(function(){
					target.remove();
				})
				.fail(function(){
					alert("에러");
				})
			}
		});	
		
	})
	
	
	
	
	$(".img").change(function(e){
		imgPreview(e, $(this));
	})
	
	     
})

 

 

 

컨트롤러, Service, DAO에 리뷰 페이지 메서드와 리뷰 삭제 메서드를 추가합니다

 

@GetMapping("/user/myReview")
public String myreView(@AuthenticationPrincipal LoginService user, Model model) {
	long id = user.getUser().getId();
	List<Review> myReviewList = userService.myReviewList(id);
	model.addAttribute("myReviewList", myReviewList);

	return "user/myReview";
}



@DeleteMapping("/user/review")
public ResponseEntity<String> deleteReview(@AuthenticationPrincipal LoginService user, String orderNum) {
	long id = user.getUser().getId();
	userService.deleteReview(id, orderNum);
	return new ResponseEntity<String>(HttpStatus.OK);
}

 

UserServiceImp

@Override
public List<Review> myReviewList(long id) {
	return userDAO.myReviewList(id);
}


@Override
public void deleteReview(long id, String orderNum) {
	Map<String, Object> map = new HashMap<>();
	map.put("userId", id);
	map.put("orderNum", orderNum);
	userDAO.deleteReview(map);
}

 

UserDAOImp

@Override
public List<Review> myReviewList(long id) {
	return sql.selectList("user.myReviewList", id);
}

@Override
public void deleteReview(Map<String, Object> map) {
	sql.delete("user.deleteReview", map);
	
}

 

UserMapper

<select id="myReviewList" resultType="Review">
	SELECT  ORDER_NUM
			,STORE_ID
			,REVIEW_CONTENT
			,BOSS_COMMENT
			,REGI_DATE
			,SCORE
			,REVIEW_IMG
			,USER_ID
	FROM    BM_REVIEW
	WHERE   USER_ID = #{id }
	ORDER BY REGI_DATE DESC
</select>



<delete id="deleteReview">
	DELETE BM_REVIEW 
	WHERE	USER_ID = #{userId }
	AND		ORDER_NUM = #{orderNum } 
</delete>

 

 

 

 

 

 

리뷰 수정하기는 기존의 StoreController의 reviewModify() 메서드를 사용하기 때문에 orderList 페이지로 이동하게 됩니다 

수정요청을 한 페이지에 따라 리다이렉트 페이지를 구분 할 수 있게 수정합니다

StoreController

// 리뷰 수정
@PostMapping("/store/reviewModify")
public String reviewModify(Review review, MultipartFile file, @AuthenticationPrincipal LoginService user, HttpServletRequest request) throws IOException {
	if(!file.isEmpty()){
		String img = uploadFile.fildUpload(file);
		review.setReviewImg(img);
	}
	long userId = user.getUser().getId();
	review.setUserId(userId);

	storeService.reviewModify(review);
	
	String domain = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort();
	String referer = request.getHeader("referer");
	String redirect = referer.replace(domain, ""); 

	return "redirect:" + redirect;
}