SeatController
@GetMapping("/api/seat")
public String seat(HttpServletRequest request) {
long showtimeId = (long) session.getAttribute("showtimeId");
SeatResponse.DTO model = seatService.좌석메인화면(showtimeId);
request.setAttribute("model", model);
return "seat/view";
}
좌석메인화면에 필요한 데이터를 조회하기 위해 showtimeId 가 필요한데
session으로 전달받아서 사용하였다.
SeatService
public SeatResponse.DTO 좌석메인화면(long id) {
Showtime showtimePS = showtimeRepository.mFindById(id)
.orElseThrow(() -> new Exception404("상영시간정보가 없습니다."));
Long screenId = showtimePS.getScreen().getId();
Screen screenPS = screenRepository.mFindAllById(screenId)
.orElseThrow(() -> new Exception404("상영관정보가 없습니다."));
Integer totalSeat = seatRepository.mFindCountOfTotalSeat(id);
if(totalSeat == null || totalSeat == 0){
throw new Exception404("좌석 정보가 없습니다.");
}
Integer reservedSeat = ticketRepository.mFindCountOfReservedSeats(id);
if(reservedSeat == null){
reservedSeat = 0;
}
//System.out.println("예약된 좌석 수 " + reservedSeat);
return new SeatResponse.DTO(showtimePS, screenPS, totalSeat, reservedSeat);
}
null이나 0이 return 되는 경우
GlobalExceptionHandler
로 throw 해서 자바스크립트 alert 창이 뜨도록 처리하였다.SeatRepository
@Query("select st from Showtime st left join fetch st.movie mt where st.id=:id")
Optional<Showtime> mFindById(@Param("id") Long id);
컨벤션을 지키기 위해 m 을 매서드 이름 앞에 붙였다.
SeatResponse
@Data
public static class DTO {
// showtime
private String startedAt; // 시작 시간
private String endedAt; // 끝나는 시간
private String wholeShowTime; // 일자 + 시간
private Integer price; // 영화 가격 ( 1좌석당, 1티켓당 )
// movie
private Integer runtime; // 영화 러닝타임
private String movieNm; // 영화 pk
private String ratingGrade; // 등급
//poster
private String posterUrl; // 포스터
// screen
private String screenNm; // 상영관 pk
// cinema
private String cinemaNm; // 영화관 pk
private String cinemaImg; // 영화관 이미지
// seat count
private Integer totalSeats;
private Integer remainingSeats;
public DTO(Showtime showtimePS, Screen screenPS, Integer totalSeat, Integer reservedSeat) {
String formatGrande = "yyyy.MM.dd(E)";
String formatPequeno = "HH:mm";
String formatTotal = "yyyy.MM.dd(E) HH:mm";
//showtimePS.getStartedAt(); // 2024-09-12 14:00:00.0
// 시작 시간 형식 변환
this.startedAt = convertTimeStampToString(showtimePS.getStartedAt(), formatGrande);
// 종료 시간 계산 ( 시작 시간 + runtime )
this.endedAt = calculateEndTime(showtimePS.getStartedAt(), showtimePS.getMovie().getRuntime()); // 종료 시간 계산
// 전체 시간 형식 변환
this.wholeShowTime = convertTimeStampToString(showtimePS.getStartedAt(), formatTotal);
this.price = showtimePS.getPrice();
this.runtime = showtimePS.getMovie().getRuntime();
this.movieNm = showtimePS.getMovie().getMovieNm();
this.posterUrl = showtimePS.getMovie().getPosterUrls().get(0).getUrl();
String grade = showtimePS.getMovie().getRatingGrade();
if(grade == "전체"){
this.ratingGrade = "전체 관람가";
}else{
this.ratingGrade = grade + " 관람가";
}
this.screenNm = screenPS.getName();
this.cinemaNm = screenPS.getCinema().getName();
this.cinemaImg = screenPS.getCinema().getImgName();
// 좌석 수
this.totalSeats = totalSeat;
this.remainingSeats = totalSeats - reservedSeat;
System.out.println(remainingSeats);
}
// Timestamp를 지정한 format으로 변환하는 매서드
public static String convertTimeStampToString(Timestamp timestamp, String format) {
if(timestamp == null) return "";
try {
Date date = new Date();
date.setTime(timestamp.getTime());
//return new SimpleDateFormat(format).format(date);
// Locale 설정으로 요일을 한국어로 출력
SimpleDateFormat sdf = new SimpleDateFormat(format, Locale.KOREAN);
return sdf.format(date);
} catch (Exception e) {
throw new Exception404("날짜 정보가 뭔가 잘못되었습니다.");
}
}
// 시작 시간에 runtime을 더해 종료 시간을 계산하는 매서드
public static String calculateEndTime(Timestamp startedAt, int runtimeMinutes){
LocalDateTime startDateTime = startedAt.toLocalDateTime(); // TimeStamp를 LocalDateTime으로 변환
LocalDateTime endDateTime = startDateTime.plus(runtimeMinutes, ChronoUnit.MINUTES); // runtime을 분 단위로 더함
// HH :mm 형식으로 변환
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm");
return endDateTime.format(formatter);
}
}
좌석 페이지에 필요한 정보들을 담았다
좌석은 제외.
→ 좌석 코드까지 들어가니 코드가 너무 길어져서 좌석을 제외한 정보만 담았다.
→ 좌석은 AJAX 로 요청해서 SeatDTO 에 담아서 return 하도록 코드를 분리하였다.
seatView.mustache
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>title</title> <!-- 좌석 선택 -->
<!-- 부트스트랩 CSS 링크 -->
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="/css/seat.css">
<link rel="stylesheet" href="/css/header.css">
</head>
<body>
{{>layout/header}}
<main>
<div id="hidden-data" data-price="{{model.price}}" data-showtimeid="{{showtimeId}}"></div>
<div class="container mt-5">
<!-- 상단 인원 / 좌석 -->
<div class="text-center mb-4">
<div class="bg-dark text-white py-2 mb-3">인원 / 좌석</div>
</div>
<section>
<div class="count__seat__section">
<!-- 인원 선택 -->
<div class="count__section">
<div class="mb-4">
<div class="text-center">
<label>일반</label>
<div class="btn-group btn-group-toggle" data-toggle="buttons">
<button class="btn btn-outline-primary" id="count0" value="0" onclick="resetCount()">0</button><!-- 0 클릭 시 초기화 -->
<button class="btn btn-outline-primary" id="count1" value="1" onclick="getCount(this.value)">1</button>
<button class="btn btn-outline-primary" id="count2" value="2" onclick="getCount(this.value)">2</button>
<button class="btn btn-outline-primary" id="count3" value="3" onclick="getCount(this.value)">3</button>
<button class="btn btn-outline-primary" id="count4" value="4" onclick="getCount(this.value)">4</button>
</div>
</div>
</div>
</div>
<div class="vertical__line"></div>
<div class="movie__info__section">
<!-- 상영 정보 -->
<div class="text-center mb-4">
<img class="screen__img" src="/img/cinema01.jpg"><!-- model.cinema 이미지로 수정 -->
<p class="font-weight-bold">{{model.cinemaNm}} | {{model.screenNm}} | 남은좌석 {{model.remainingSeats}}/{{model.totalSeats}}</p>
<p>{{model.wholeShowTime}} ~ {{model.endedAt}}</p><!-- 2024.09.04 (수) 20:15 -->
</div>
</div>
</div>
</section>
<!-- 좌석 배치 -->
<div class="seat-selection text-center mb-4">
<div class="bg-dark text-white py-2 mb-3">SCREEN</div>
<div id="seat-container"></div>
<!-- 좌석 반복 추가 -->
</div>
</div>
<!-- 하단 검은창 -->
<div class="row bg-dark text-white py-2 mb-3">
<div class="col text-left">
<button class="btn btn-secondary">영화선택</button>
</div>
<div class="blackbox__middle__container">
<div class="blackbox__img__div">
<img class="movie__img" src="{{model.posterUrl}}">
</div>
<div class="blackbox__movie__div">
<div>{{model.movieNm}}</div>
<div>{{model.ratingGrade}}</div>
</div>
<div>
<table>
<tr>
<td class="left__td">극장</td>
<td class="right__td">{{model.cinemaNm}}</td>
</tr>
<tr>
<td>일시</td>
<td>{{model.wholeShowTime}}</td><!-- 2024.9.14(토) 12:00 -->
</tr>
<tr>
<td>상영관</td>
<td>{{model.screenNm}}</td>
</tr>
<tr>
<td>인원</td>
<td id="peopleNum"></td><!-- js로 -->
</tr>
</table>
</div>
<div class="seat__num__box" id="seatNumBox">
<!-- JS 로 동적으로 넣기 -->
</div>
<div class="seat__payment__box" id="paymentBox">
<!-- JS 로 동적으로 넣기 -->
</div>
</div>
<div class="col text-right">
<form action="/api/seat/reservation" method="post">
<!--<input type="hidden" name="showtimeId" value="">-->
<input type="hidden" name="showtimeId" value="{{showtimeId}}">
<input type="hidden" name="selectedSeatsIds" id="selectedSeatsIds">
<input type="hidden" name="totalPrice" id="totalPrice">
<button onclick="return didYouSelectAll()" class="btn btn-secondary" type="submit">결제선택</button>
</form>
</div>
</div>
<!-- 부트스트랩 JS 및 jQuery -->
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.5.3/dist/umd/popper.min.js"></script>
<script src="/js/seat2.js"></script>
</main>
{{>layout/footer}}
</body>
</html>

좌석을 제외한 부분의 정보가 잘 들어왔다.
Share article