TIL 2023-10-30 호텔 예약 시스템 팀과제02

장규빈·2023년 10월 30일

TIL

목록 보기
13/59
post-thumbnail

호텔 예약 시스템

만들어진 클래스들을 활용하여 호텔예약 시스템을 구현해보았다.

import controller.HotelController;

public class HotelApplication {
   public static void main(String[] args) {
      HotelController hotelController = new HotelController();
      hotelController.run();
   }
}
package controller;

import service.GuestService;
import service.HotelService;
import service.ManagerService;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

import static service.HotelService.getHotelService;
import static util.UtilContext.errorMessage;
import static util.UtilContext.sc;

public class HotelController {

   HotelService hotelService = getHotelService();
   GuestService guestService = new GuestService(hotelService);
   ManagerService managerService = new ManagerService(hotelService);

   public void run() {
      hotelService.initRoom();
      displayMode();
   }

   public void displayMode() {
      System.out.println("환영합니다. JAVA 호텔 입니다.");
      modeInputHandling();
   }

   public void modeInputHandling() {
      System.out.println();
      System.out.println("Mode 를 선택해주세요.");
      System.out.println("1. Guest Mode");
      System.out.println("2. Manager Mode");
      int command = sc.nextInt();
      switch (command) {
         case 1 -> {
            guestService.displayGuestMode();
            modeInputHandling();
         }
         case 2 -> {
            if (passwordCheck()) {
               managerService.displayManagerMode();
               modeInputHandling();
            } else {
               passwordNotMatchMessage();
               modeInputHandling();
            }
         }
         default -> {
            errorMessage();
            modeInputHandling();
         }
      }
   }

   public boolean passwordCheck() {
      System.out.print("관리자 비밀번호를 입력해주세요: ");
      try {
         return encryption(String.valueOf(sc.nextInt())).equals(hotelService.getHotelPassword());
      } catch (Exception e) {
         return false;
      }
   }

   public void passwordNotMatchMessage() {
      System.out.println("비밀번호가 일치하지 않습니다.");
      System.out.println("시스템이 돌아갑니다.");
   }

   private String encryption(String password) throws NoSuchAlgorithmException {
      MessageDigest md = MessageDigest.getInstance("SHA-256");
      md.update(password.getBytes());

      return bytesToHex(md.digest());
   }

   private String bytesToHex(byte[] bytes) {
      StringBuilder sb = new StringBuilder();
      for (byte b : bytes) {
         sb.append(String.format("%02x", b));
      }
      return sb.toString();
   }
}
package service;

import model.Hotel;
import model.Reservation;
import model.ProductRoom;
import constant.RoomType;
import model.User;

import java.time.LocalDate;
import java.util.List;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

public class HotelService {
   Hotel hotel = new Hotel();
   private static HotelService hotelService;

   public static HotelService getHotelService() {
      if (hotelService == null) {
         hotelService = new HotelService();
      }
      return hotelService;
   }

   public void initRoom() {
      LocalDate localDate = LocalDate.now();
      for (int i = 0; i < 7; i++) {
         hotel.getProductRooms().add(new ProductRoom(RoomType.STANDARD, 30000, 101, localDate.plusDays(i)));
         hotel.getProductRooms().add(new ProductRoom(RoomType.SUPERIOR, 40000, 102, localDate.plusDays(i)));
         hotel.getProductRooms().add(new ProductRoom(RoomType.DELUXE, 50000, 103, localDate.plusDays(i)));
         hotel.getProductRooms().add(new ProductRoom(RoomType.SUITE, 60000, 104, localDate.plusDays(i)));
      }
   }

   public String getHotelPassword() {
      return hotel.getPassword();
   }

   public List<LocalDate> findAvailableDays() {
      return hotel.getProductRooms().stream()
              .map(ProductRoom::getReservedDate)
              .distinct()
              .collect(Collectors.toList());
   }

   public List<ProductRoom> findProductRoomByDate(LocalDate date) {
      return hotel.getProductRooms().stream()
              .filter(room -> room.getReservedDate().equals(date))
              .toList();
   }

   public User findUserByPhoneNumber(String phoneNumber) {
      return hotel.getUsers().stream()
              .filter(u -> u.getPhoneNumber().equals(phoneNumber))
              .findFirst().orElse(null);
   }

   public List<Reservation> findReservationByDate(LocalDate date) {
      return hotel.getReservations().stream()
              .filter(reservation -> reservation.productRoom().getReservedDate().equals(date))
              .toList();
   }

   public List<Reservation> findReservationByName(String name) {
      return hotel.getReservations().stream()
              .filter(reservation -> reservation.userName().equals(name))
              .toList();
   }

   public List<Reservation> findReservationByPhoneNumber(String phoneNumber) {
      return hotel.getReservations().stream()
              .filter(reservation -> reservation.userPhoneNumber().equals(phoneNumber))
              .toList();
   }

   public void addReservation(Reservation reservation) {
      hotel.getReservations().add(reservation);

   }

   public void cancelReservation(Reservation reservation) {
      hotel.getReservations().remove(reservation);
   }

   public void addUser(User user) {
      hotel.getUsers().add(user);
   }

   public boolean validatePhoneNumber(String phoneNumber) {
      String pattern = "^\\d{3}-\\d{3,4}-\\d{4}$";
      return Pattern.matches(pattern, phoneNumber);
   }

   public boolean existsPhoneNumber(String phoneNumber) {
      return hotel.getUsers().stream().anyMatch(u -> u.getPhoneNumber().equals(phoneNumber));
   }

   public void addAsset(int price) {
      hotel.setAsset(hotel.getAsset() + price);
   }

   public void deductAsset(int price) {
      hotel.setAsset(hotel.getAsset() - price);
   }

   public int getAsset() {
      return hotel.getAsset();
   }

   public List<ProductRoom> findEmptyProductRoomByDate(LocalDate date) {
      return hotel.getProductRooms().stream()
              .filter(room -> room.getReservedDate().equals(date))
              .filter(empty -> !empty.isReserved())
              .toList();
   }

   public boolean findReservationByExistingName(String name) {
      return hotel.getReservations().stream()
              .anyMatch(reservation -> reservation.userName().equals(name));
   }

   public boolean findReservationByExistingPhoneNumber(String phone) {
      return hotel.getReservations().stream()
              .anyMatch(reservation -> reservation.userPhoneNumber().equals(phone));
   }

   public boolean findReservationByExistingDate(LocalDate date) {
      return hotel.getReservations().stream()
              .anyMatch(reservation -> reservation.productRoom().getReservedDate().equals(date));
   }
}
package service;

import model.ProductRoom;
import model.Reservation;
import model.User;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;

import static util.UtilContext.backMessage;
import static util.UtilContext.errorMessage;
import static util.UtilContext.line;
import static util.UtilContext.lineWithText;
import static util.UtilContext.sc;

public class GuestService {
   private final HotelService hotelService;
   private final UserService userService = new UserService();
   private final ProductRoomService productRoomService = new ProductRoomService();

   public GuestService(HotelService hotelService) {
      this.hotelService = hotelService;
   }

   public void displayGuestMode() {
      while (true) {
         lineWithText("Guest Mode");
         System.out.println("1. 로그인");
         System.out.println("2. 회원 가입");
         backMessage();
         int command = sc.nextInt();
         if (command == 0) {
            break;
         } else if (command == 1) {
            signIn();
         } else if (command == 2) {
            signUp();
         } else {
            errorMessage();
         }
      }
   }

   private void signIn() {
      lineWithText("JAVA 호텔 로그인");
      phoneNumberContext();
      String phoneNumber = sc.next();
      if (hotelService.validatePhoneNumber(phoneNumber)) {
         if (hotelService.existsPhoneNumber(phoneNumber)) {
            User user = hotelService.findUserByPhoneNumber(phoneNumber);
            displayUserService(user);
         } else {
            System.out.println("존재하지 않는 핸드폰 번호 입니다.");
         }
      } else {
         System.out.println("\n핸드폰 번호의 입력이 올바르지 않습니다!");
      }
   }

   private static void phoneNumberContext() {
      System.out.println("ex)000-0000-0000");
      System.out.println("자신의 핸드폰 번호를 입력해주세요.");
      System.out.print("핸드폰 번호: ");
   }

   private void signUp() {
      lineWithText("JAVA 호텔 회원 가입");
      System.out.println("자신의 정보를 등록해주세요.");
      System.out.print("이름: ");
      String name = sc.next();
      phoneNumberContext();
      String phoneNumber = sc.next();
      if (hotelService.validatePhoneNumber(phoneNumber)) {
         if (!hotelService.existsPhoneNumber(phoneNumber)) {
            hotelService.addUser(new User(name, phoneNumber));
            System.out.println("\n회원 가입이 완료되었습니다.");
         } else {
            System.out.println("\n이미 존재하는 핸드폰 번호입니다.");
            signUp();
         }
      } else {
         System.out.println("\n핸드폰 번호의 입력이 올바르지 않습니다!");
         signUp();
      }
   }

   private void displayUserService(User user) {
      System.out.println();
      System.out.println("반갑습니다. " + user.getName() + "님~");
      lineWithText("회원 모드");
      System.out.println("1. 호텔 예약하기");
      System.out.println("2. 예약한 호텔 조회하기");
      System.out.println("3. 포인트 충전하기");
      System.out.println("4. 포인트 조회하기");
      System.out.println("5. 예약 취소하기");
      System.out.println("6. 포인트 환전하기");
      System.out.println("7. 로그아웃");
      serviceInputHandling(user);
   }

   private void serviceInputHandling(User user) {
      System.out.println();
      System.out.print("입력: ");
      int command = sc.nextInt();
      switch (command) {
         case 1 -> showAvailableDays(user);
         case 2 -> findReservedHotel(user);
         case 3 -> chargePoint(user);
         case 4 -> getUserPoint(user);
         case 5 -> showReservation(user);
         case 6 -> exchangePoint(user);
         case 7 -> logout();
         default -> {
            errorMessage();
            displayUserService(user);
         }
      }
   }

   private void showAvailableDays(User user) {
      List<LocalDate> availableDays = hotelService.findAvailableDays();
      System.out.println("예약 하실 날짜를 선택해주세요.");
      for (int i = 0; i < availableDays.size(); i++) {
         System.out.printf("%2d.  %10s\n", i + 1, availableDays.get(i));
      }
      backMessage();
      int command = sc.nextInt();
      if (command == 0) {
         displayUserService(user);
      } else if (command >= 1 && command <= availableDays.size()) {
         showAvailableRoom(command, availableDays, user);
      } else {
         errorMessage();
         showAvailableDays(user);
      }
   }

   private void showAvailableRoom(int command, List<LocalDate> availableDays, User user) {
      LocalDate date = availableDays.get(command - 1);
      List<ProductRoom> productRooms = hotelService.findProductRoomByDate(date);
      lineWithText(date.toString());
      System.out.println("예약 하실 객실을 선택해주세요.");
      System.out.println();
      for (int i = 0; i < productRooms.size(); i++) {
         ProductRoom productRoom = productRooms.get(i);
         String isReserved = !productRoom.isReserved() ? "예약 가능" : "예약 불가능";
         System.out.printf("%2d. %4d호 | %-8s | %-6d ₩ | %-8s\n", i + 1, productRoom.getRoomNumber(), productRoom.getRoomType(), productRoom.getCost(), isReserved);
      }
      backMessage();
      int roomCommand = sc.nextInt();
      if (roomCommand == 0) {
         showAvailableDays(user);
      } else if (roomCommand >= 1 && roomCommand <= productRooms.size()) {
         ProductRoom productRoom = productRooms.get(roomCommand - 1);
         if (productRoom.isReserved()) {
            System.out.println("이미 예약된 방입니다.");
            showAvailableRoom(command, availableDays, user);
         } else {
            selectRoom(user, productRoom);
         }
      } else {
         errorMessage();
         showAvailableRoom(command, availableDays, user);
      }
   }

   private void selectRoom(User user, ProductRoom productRoom) {
      if (productRoom.getCost() <= user.getPoint()) {
         line();
         System.out.printf("%-4d호 | %-8s | %-6d\n",
                 productRoom.getRoomNumber(),
                 productRoom.getRoomType(),
                 productRoom.getCost());
         System.out.println("예약 하시겠습니까?");
         System.out.println();
         System.out.println("1. 확인         2. 취소");
         int command = sc.nextInt();
         switch (command) {
            case 1 -> reservationHotel(user, productRoom);
            case 2 -> displayUserService(user);
            default -> {
               errorMessage();
               displayUserService(user);
            }
         }
      } else {
         System.out.println(user.getName() + "님 포인트가 부족합니다.");
         System.out.println("1. 포인트 충전      2. 취소");
         int command = sc.nextInt();
         switch (command) {
            case 1 -> chargePoint(user);
            case 2 -> displayUserService(user);
            default -> {
               errorMessage();
               displayUserService(user);
            }
         }
      }
   }

   private void reservationHotel(User user, ProductRoom productRoom) {
      Reservation reservation = new Reservation(productRoom, user.getName(), user.getPhoneNumber(), LocalDateTime.now());
      int roomCost = productRoom.getCost();
      hotelService.addReservation(reservation);
      userService.deductPoint(user, roomCost);
      productRoomService.changeReservationState(productRoom, true);
      System.out.println("예약이 완료되었습니다.");
      displayUserService(user);
   }

   private void findReservedHotel(User user) {
      String userPhoneNumber = user.getPhoneNumber();
      List<Reservation> reservations = hotelService.findReservationByPhoneNumber(userPhoneNumber);
      line();
      showReservationHandling(reservations);
      System.out.println();
      System.out.println("메인 화면으로 돌아갑니다.");
      displayUserService(user);
   }

   private static boolean showReservationHandling(List<Reservation> reservations) {
      if (reservations.size() == 0) {
         System.out.println("조회 가능한 예약이 없습니다.");
         return false;
      }
      int idx = 1;
      for (Reservation reservation : reservations) {
         ProductRoom productRoom = reservation.productRoom();
         System.out.printf("%2d. %10s | %3d호 | %8s | 예약한 시간: %-15s\n",
                 idx++,
                 productRoom.getReservedDate().toString(),
                 productRoom.getRoomNumber(),
                 productRoom.getRoomType(),
                 reservation.createdAt().toString());
      }
      return true;
   }

   private void chargePoint(User user) {
      System.out.println("충전할 포인트를 입력해주세요.");
      int point = sc.nextInt();
      userService.chargePoint(user, point);
      hotelService.addAsset(point);
      line();
      System.out.println("충전이 완료 되었습니다.");
      System.out.println(user.getName() + "님 현재 잔액: " + user.getPoint() + " ₩");
      displayUserService(user);
   }

   private void getUserPoint(User user) {
      String userName = user.getName();
      int point = user.getPoint();
      System.out.println(userName + "님의 현재 포인트: " + point + " ₩ 입니다.");
      System.out.println("1. 포인트 충전하기        2. 뒤로 가기");
      int command = sc.nextInt();
      switch (command) {
         case 1 -> chargePoint(user);
         case 2 -> displayUserService(user);
         default -> {
            errorMessage();
            displayUserService(user);
         }
      }
   }

   private void showReservation(User user) {
      String userPhoneNumber = user.getPhoneNumber();
      List<Reservation> reservations = hotelService.findReservationByPhoneNumber(userPhoneNumber);
      line();
      if (showReservationHandling(reservations)) {
         int command = sc.nextInt();
         if (command >= 1 && command <= reservations.size()) {
            Reservation reservation = reservations.get(command - 1);
            cancelReservation(user, reservation);
         }
      } else {
         displayUserService(user);
      }
   }

   private void cancelReservation(User user, Reservation reservation) {
      ProductRoom productRoom = reservation.productRoom();
      System.out.printf("%10s | %3d호 | %8s | 예약한 시간: %-15s\n",
              productRoom.getReservedDate().toString(),
              productRoom.getRoomNumber(),
              productRoom.getRoomType(),
              reservation.createdAt().toString());
      System.out.println();
      System.out.println("정말 취소하시겠습니까?");
      System.out.println("1. 예약 취소       2. 뒤로 가기");
      int command = sc.nextInt();
      if (command == 1) {
         int roomCost = productRoom.getCost();
         hotelService.cancelReservation(reservation);
         userService.chargePoint(user, roomCost);
         productRoomService.changeReservationState(productRoom, false);
         System.out.println("예약이 취소 되었습니다.");
      }
      displayUserService(user);
   }

   private void exchangePoint(User user) {
      String userName = user.getName();
      int point = user.getPoint();
      System.out.println(userName + "님의 현재 포인트: " + point + " ₩ 입니다.");
      System.out.println();
      System.out.println("환전할 금액을 입력해주세요.");
      int money = sc.nextInt();
      if (money > point) {
         System.out.println("최대 환전 금액은 " + point + " ₩ 입니다.");
         exchangePoint(user);
      } else {
         System.out.println("환전이 완료되었습니다.");
         hotelService.deductAsset(money);
         userService.deductPoint(user, money);
         displayUserService(user);
      }
   }

   private void logout() {
      System.out.println("로그아웃 되었습니다.");
   }
}
package service;

import model.ProductRoom;
import model.Reservation;

import java.time.LocalDate;

import static util.UtilContext.*;

public class ManagerService {
   private final HotelService hotelService;

   public ManagerService(HotelService hotelService) {
      this.hotelService = hotelService;
   }

   public void displayManagerMode() {
      while (true) {
         lineWithText("Manger Mode");
         System.out.println("1. 예약 현황");
         System.out.println("2. 자산 현황");
         System.out.println("9. 시스템 종료");
         backMessage();
         int command = sc.nextInt();
         if (command == 0) {
            break;
         } else if (command == 1) {
            reservationStatus();
         } else if (command == 2) {
            assertStatus();
         } else if (command == 9) {
            System.exit(0);
         } else {
            errorMessage();
         }
      }
   }

   public void reservationStatus() {
      System.out.println();
      System.out.println("매뉴를 선택해 주세요.");
      System.out.println("1. 빈객실 찾기");
      System.out.println("2. 예약 찾기");
      System.out.println("3. 오늘 객실 현황");
      backMessage();
      int command = sc.nextInt();
      switch (command) {
         case 1 -> findProductRoomByDate();
         case 2 -> findReservation();
         case 3 -> findReservationByToday();
         case 0 -> {
         }
         default -> {
            errorMessage();
            reservationStatus();
         }
      }
   }

   public void findProductRoomByDate() {
      showHotelDate();
      int command = sc.nextInt();
      if (command == 0) {
         reservationStatus();
      } else if (0 < command && command <= hotelService.findAvailableDays().size()) {
         findProductRoomIsReservedByDate(hotelService.findAvailableDays().get(command - 1));
      } else {
         errorMessage();
         findProductRoomByDate();
      }
   }

   public void findProductRoomIsReservedByDate(LocalDate day) {
      System.out.println();
      System.out.println("선택하신 날짜는 " + day + " 입니다");
      for (ProductRoom room : hotelService.findEmptyProductRoomByDate(day)) {
         System.out.println(room.getRoomType());
      }
      backMessage();
      int command = sc.nextInt();
      if (command == 0) {
         reservationStatus();
      } else {
         errorMessage();
         findProductRoomIsReservedByDate(day);
      }
   }

   public void findReservation() {
      System.out.println();
      System.out.println("방법을 선택해 주세요!");
      System.out.println("1. 이름으로 찾기");
      System.out.println("2. 번호로 찾기");
      System.out.println("3. 날짜로 찾기");
      backMessage();
      int command = sc.nextInt();
      switch (command) {
         case 1 -> findReservationByExistingName();
         case 2 -> findReservationByValidatePhoneNumber();
         case 3 -> findReservationByDayChoice();
         case 0 -> reservationStatus();
         default -> {
            errorMessage();
            reservationStatus();
         }
      }
   }

   public void findReservationByExistingName() {
      System.out.println();
      System.out.println("이름을 입력해 주세요!");
      String command = sc.next();
      if (hotelService.findReservationByExistingName(command)) {
         findReservationByName(command);
      } else {
         System.out.println("예약 내역이 없습니다.");
         findReservation();
      }
   }

   public void findReservationByName(String name) {
      for (Reservation reservation : hotelService.findReservationByName(name)) {
         System.out.println(reservation.productRoom().getReservedDate() + "\t" + reservation.productRoom().getRoomType() + '\t'
                 + reservation.userName() + "\t" + reservation.userPhoneNumber());
      }
      backMessage();
      int command = sc.nextInt();
      if (command == 0) {
         reservationStatus();
      } else {
         errorMessage();
         findReservationByName(name);
      }
   }

   public void findReservationByValidatePhoneNumber() {
      System.out.println();
      System.out.println("번호를 입력해 주세요!");
      System.out.println("ex) 010-0000-0000");
      String command = sc.next();
      if (hotelService.validatePhoneNumber(command)) {
         findReservationByExistingPhoneNumber(command);
      } else {
         System.out.println("\n핸드폰 번호의 입력이 올 바르지 않습니다!");
         findReservationByValidatePhoneNumber();
      }
   }

   public void findReservationByExistingPhoneNumber(String phoneNumber) {
      if (hotelService.findReservationByExistingPhoneNumber(phoneNumber)) {
         findReservationByPhoneNumber(phoneNumber);
      } else {
         System.out.println("예약된 핸드폰 번호가 없습니다!");
         findReservation();
      }
   }

   public void findReservationByPhoneNumber(String phoneNumber) {
      for (Reservation reservation : hotelService.findReservationByPhoneNumber(phoneNumber)) {
         System.out.println(reservation.productRoom().getReservedDate() + "\t" + reservation.productRoom().getRoomType() + '\t'
                 + reservation.userName() + "\t" + reservation.userPhoneNumber());
      }
      backMessage();
      int command = sc.nextInt();
      if (command == 0) {
         findReservation();
      } else {
         errorMessage();
         findReservation();
      }
   }

   public void findReservationByDayChoice() {
      showHotelDate();
      backMessage();
      int command = sc.nextInt();
      if (command == 0) {
         findReservation();
      } else if (0 < command && command <= hotelService.findAvailableDays().size()) {
         findReservationByExistingDate(hotelService.findAvailableDays().get(command - 1));
      } else {
         errorMessage();
         findReservation();
      }
   }

   public void findReservationByExistingDate(LocalDate date) {
      System.out.println();
      System.out.println("선택하신 날짜는 " + date + " 입니다.");
      if (hotelService.findReservationByExistingDate(date)) {
         findReservationByDate(date);
      } else {
         System.out.println("예약된 정보가 없습니다!");
         reservationStatus();
      }
   }

   public void findReservationByDate(LocalDate date) {
      for (Reservation reservation : hotelService.findReservationByDate(date)) {
         System.out.println(reservation.productRoom().getRoomType() + "\t" + reservation.userName() + "\t" + reservation.userPhoneNumber());
      }
      backMessage();
      int command = sc.nextInt();
      if (command == 0) {
         reservationStatus();
      } else {
         errorMessage();
         findReservation();
      }
   }

   public void findReservationByToday() {
      LocalDate date = LocalDate.now();
      findReservationByExistingDate(date);
   }

   public void assertStatus() {
      System.out.println("총 금액은 " + hotelService.getAsset() + "원 입니다.");
   }

   private void showHotelDate() {
      int i = 1;
      System.out.println();
      System.out.println("날짜를 선택해 주세요!");
      for (LocalDate day : hotelService.findAvailableDays()) {
         System.out.printf("%d. %10s", i++, day + "\n");
      }
   }
}
package service;

import model.ProductRoom;

public class ProductRoomService {
   public void changeReservationState(ProductRoom productRoom, boolean reservation) {
      productRoom.setReserved(reservation);
   }
}
package service;

import model.User;

public class UserService {
    public void chargePoint(User user, int point) {
        user.setPoint(user.getPoint() + point);
    }
    public void deductPoint(User user, int point) {
        user.setPoint(user.getPoint() - point);
    }
}

추가적으로 만든 기능

느낀점

개인과제를 피드백 받을때 객체에 대한 정의를 조금 더 간단히하고 명확하게 정의해 불필요한 클래스를 없에는것을 요구 받았다. 사실 개인과제를 처음 시작했을때에는 진짜 무늬만 자바로 만든 코드인 마냥 객체지향적이지 못한 코드들도 많았고 만들면서도 왜 이렇게 만들어야 하는지 이유를 크게 받지 못했다. 그래서 이번 팀과제를 진행할때에는 좀더 명확한 클래스설개를 통해 좀더 객체지향적으로 코드를 완성시키려고 노력했다. 확실히 다른 사람들이랑 같이 코딩을 진행하면서 서로의 코드스타일을 보며 배운점들이 많았고 내가 잘 알지못했던 부분들을 더 많이 알수 있었다.

profile
나다운사람

0개의 댓글