Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[산타] 리나 미션 제출합니다. #14

Open
wants to merge 8 commits into
base: leena-main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions kotlin-christmas/docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@

## 기능 요구 사항

- [x] 방문 날짜 입력
> 1 이상 31 이하의 숫자
>
> 에러 메시지: "[ERROR] 유효하지 않은 날짜입니다. 다시 입력해 주세요."
- [x] 주문 메뉴 입력
- 형식: ${메뉴 이름}-${수량}
> 준수해야할 사항
> - 메뉴의 개수는 1 이상의 숫자
> - 메뉴판에 메뉴가 존재해야 함
> - 메뉴 형식이 일치해야함
> - 중복 메뉴가 있으면 안됨
> - 음료만 주문하면 안됨
> - 메뉴 개수는 총 20개 이하여야 함
> - 에러 메시지: [ERROR] 유효하지 않은 주문입니다. 다시 입력해 주세요
- [x] 할인 전 총 주문 금액 계산
- [x] 증정 메뉴
> 할인 전 총주문 금액이 12만 원 이상일 때, 샴페인 1개 증정
- [x] 혜택 내역
> - 크리스마스 디데이 할인
> - 이벤트 기간: 2023.12.1 ~ 2023.12.25
> - 1,000원으로 시작하여 크리스마스가 다가올수록 날마다 할인 금액이 100원씩 증가
> - 총주문 금액에서 해당 금액만큼 할인
> (e.g. 시작일인 12월 1일에 1,000원, 2일에 1,100원, ..., 25일엔 3,400원 할인)
> - 평일 할인(일요일~목요일): 평일에는 디저트 메뉴를 메뉴 1개당 2,023원 할인
> - 주말 할인(금요일, 토요일): 주말에는 메인 메뉴를 메뉴 1개당 2,023원 할인
> - 특별 할인: 이벤트 달력에 별이 있으면 총주문 금액에서 1,000원 할인
> - 증정 이벤트: 할인 전 총주문 금액이 12만 원 이상일 때, 샴페인 1개 증정
> - 이벤트 기간: '크리스마스 디데이 할인'을 제외한 다른 이벤트는 2023.12.1 ~ 2023.12.31 동안 적용
- [x] 총 혜택 금액
> 할인 금액의 합계 + 증정 메뉴의 가격
- [x] 할인 후 예상 결제 금액
> 할인 전 총주문 금액 - 할인 금액
- [x] 12월 이벤트 배지
3 changes: 2 additions & 1 deletion kotlin-christmas/src/main/kotlin/christmas/Application.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package christmas

fun main() {
TODO("프로그램 구현")
val controller = EventController()
controller.execute()
}
32 changes: 32 additions & 0 deletions kotlin-christmas/src/main/kotlin/christmas/EventController.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package christmas

import christmas.data.UserMenu
import christmas.view.InputView
import christmas.view.OutputView

class EventController(
private val inputView: InputView = InputView(),
private val outputView: OutputView = OutputView()
) {

fun execute() {
val date = readDate()
val menuList = readMenu()
outputView.printEventBenefitMessage(date)
outputView.printMenuList(menuList)

val eventService = EventService(date, menuList)
outputView.printResult(eventService.eventBenefit)
}

private fun readDate(): Int {
outputView.printWelcomeMessage()
outputView.printReadDateMessage()
return inputView.readDate()
}

private fun readMenu(): List<UserMenu> {
outputView.printReadMenuMessage()
return inputView.readMenu()
}
}
53 changes: 53 additions & 0 deletions kotlin-christmas/src/main/kotlin/christmas/EventService.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package christmas

import christmas.data.*
import christmas.enums.Badge
import christmas.enums.Menu
import christmas.util.*

class EventService(
private val date: Int,
private val menuList: List<UserMenu>,
private val getDiscountBenefit: GetDiscountBenefit = GetDiscountBenefit(date, menuList),
) {
private var _eventBenefit = EventBenefit()
val eventBenefit get() = _eventBenefit

init {
saveTotalAmountBeforeBenefit()
getBenefit()
getBadge()
}

private fun saveTotalAmountBeforeBenefit() {
val totalAmount = menuList.sumOf { it.menu.price * it.amount }
_eventBenefit = _eventBenefit.copy(
totalAmountBeforeBenefit = totalAmount
)
}

private fun getBenefit() {
if (eventBenefit.totalAmountBeforeBenefit < EVENT_START_AMOUNT) {
return
}
_eventBenefit = _eventBenefit.copy(
discountBenefit = getDiscountBenefit.discountBenefit,
presentationMenu = getPresentationMenu()
)
}

private fun getPresentationMenu(): UserMenu? {
if (eventBenefit.totalAmountBeforeBenefit >= PRESENTATION_AMOUNT) {
return UserMenu(Menu.CHAMPAGNE,1)
}
return null
}

private fun getBadge() {
val benefitAmount = eventBenefit.getBenefit()
_eventBenefit = _eventBenefit.copy(
badge = Badge.getByValue(benefitAmount)
)
}

}
74 changes: 74 additions & 0 deletions kotlin-christmas/src/main/kotlin/christmas/GetDiscountBenefit.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package christmas

import christmas.data.DiscountBenefit
import christmas.data.UserMenu
import christmas.enums.Category
import christmas.util.*

class GetDiscountBenefit(
private val date: Int,
private val menuList: List<UserMenu>
) {

private var _discountBenefit = DiscountBenefit()
val discountBenefit get() = _discountBenefit

init {
getBenefit()
}

private fun getBenefit() {
christmasDDaySale()
weekdaySale()
weekendSale()
specialSale()
}

private fun christmasDDaySale() {
if (date !in D_DAY_MIN_DATE..D_DAY_MAX_DATE) {
return
}
_discountBenefit = _discountBenefit.copy(
christmasDDay = D_DAY_MIN_PRICE + (date - 1) * D_DAY_INCREASE_PRICE
)
}

private fun weekdaySale() {
// 평일 할인(일요일~목요일): 평일에는 디저트 메뉴를 메뉴 1개당 2,023원 할인
if(isWeekday()) {
val dessertMenuCount = getMenuCount(Category.DESSERT)
_discountBenefit = _discountBenefit.copy(
weekday = dessertMenuCount * WEEKDAY_DISCOUNT
)
}
}

private fun weekendSale() {
// 주말 할인(금요일, 토요일): 주말에는 메인 메뉴를 메뉴 1개당 2,023원 할인
if (isWeekend()){
val mainMenuCount = getMenuCount(Category.MAIN)
_discountBenefit = _discountBenefit.copy(
weekend = mainMenuCount * WEEKEND_DISCOUNT
)
}
}

private fun specialSale() {
// 특별 할인: 이벤트 달력에 별이 있으면 총주문 금액에서 1,000원 할인
if (isSpecialDay()){
_discountBenefit = _discountBenefit.copy(
special = SPECIAL_DISCOUNT
)
}
}

private fun getMenuCount(category: Category) =
menuList.filter{ it.menu.category == category }.sumOf { it.amount }

private fun isWeekday() = !isWeekend()

private fun isWeekend() = date % 7 in listOf(1, 2)

private fun isSpecialDay() = date % 7 == 3 || date == 25

}
36 changes: 36 additions & 0 deletions kotlin-christmas/src/main/kotlin/christmas/data/EventBenefit.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package christmas.data

import christmas.enums.Badge

data class EventBenefit(
val discountBenefit: DiscountBenefit = DiscountBenefit(),
val totalAmountBeforeBenefit: Int = 0,
val presentationMenu: UserMenu? = null,
val badge: Badge? = null
) {
fun getTotalAmountAfterBenefit(): Int {
// 할인 후 예상 결제 금액 = 할인 전 총주문 금액 - 할인 금액
return totalAmountBeforeBenefit - discountBenefit.getTotalPrice()
}

fun getBenefit(): Int {
// 총혜택 금액 = 할인 금액의 합계 + 증정 메뉴의 가격
return discountBenefit.getTotalPrice() + getPresentationPrice()
}

private fun getPresentationPrice(): Int {
if (presentationMenu == null) return 0
return presentationMenu.menu.price * presentationMenu.amount
}
}

data class DiscountBenefit(
val christmasDDay: Int = 0,
val weekday: Int = 0,
val weekend: Int = 0,
val special: Int = 0,
) {
fun getTotalPrice(): Int {
return christmasDDay + weekday + weekend + special
}
}
8 changes: 8 additions & 0 deletions kotlin-christmas/src/main/kotlin/christmas/data/UserMenu.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package christmas.data

import christmas.enums.Menu

data class UserMenu(
val menu: Menu,
val amount: Int
)
23 changes: 23 additions & 0 deletions kotlin-christmas/src/main/kotlin/christmas/enums/Badge.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package christmas.enums

enum class Badge(
val badgeName: String,
val price: Int
){
STAR("별", 5_000),
TREE("트리", 10_000),
SANTA("산타", 20_000),
;

companion object {
fun getByValue(value: Int): Badge? {
return when (value) {
in 0 until STAR.price -> null
in STAR.price until TREE.price -> STAR
in TREE.price until SANTA.price -> TREE
else -> SANTA
}
}
}

}
43 changes: 43 additions & 0 deletions kotlin-christmas/src/main/kotlin/christmas/enums/Menu.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package christmas.enums

enum class Menu (
val koreanName: String,
val price: Int,
val category: Category
) {
// 애피타이저
MUSHROOM_SOUP("양송이수프", 6_000, Category.APPETIZER),
TAPAS("타파스", 5_500, Category.APPETIZER),
CAESAR_SALAD("시저샐러드", 8_800, Category.APPETIZER),

// 메인
T_BONE_STEAK("티본스테이크", 55_000, Category.MAIN),
BBQ_LIP("바비큐립", 54_000, Category.MAIN),
SEAFOOD_PASTA("해산물파스타", 35_000, Category.MAIN),
CHRISTMAS_PASTA("크리스마스파스타", 25_000, Category.MAIN),

// 디저트
CHOCOLATE_CAKE("초코케이크", 15_000, Category.DESSERT),
ICE_CREAM("아이스크림", 5_000, Category.DESSERT),

// 음료
ZERO_COKE("제로콜라", 3_000, Category.BEVERAGE),
RED_WINE("레드와인", 60_000, Category.BEVERAGE),
CHAMPAGNE("샴페인", 25_000, Category.BEVERAGE),
;

companion object {
fun getByKoreanName(name: String): Menu {
return entries.firstOrNull { it.koreanName == name } ?: throw IllegalArgumentException()
}
}
}

enum class Category(
private val koreanName: String
) {
APPETIZER("애피타이저"),
MAIN("메인"),
DESSERT("디저트"),
BEVERAGE("음료"),
}
25 changes: 25 additions & 0 deletions kotlin-christmas/src/main/kotlin/christmas/util/Const.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package christmas.util

const val ERROR_PREFIX = "[ERROR]"
const val NOTHING = "없음"

const val EVENT_MONTH = 12

const val MIN_DATE = 1
const val MAX_DATE = 31

const val D_DAY_MIN_DATE = 1
const val D_DAY_MAX_DATE = 25
const val D_DAY_MIN_PRICE = 1_000
const val D_DAY_INCREASE_PRICE = 100

const val WEEKDAY_DISCOUNT = 2023
const val WEEKEND_DISCOUNT = 2023

const val SPECIAL_DISCOUNT = 1000

const val MENU_MAX_AMOUNT = 20

const val EVENT_START_AMOUNT = 10_000

const val PRESENTATION_AMOUNT = 120_000
48 changes: 48 additions & 0 deletions kotlin-christmas/src/main/kotlin/christmas/util/Validation.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package christmas.util

import christmas.data.UserMenu
import christmas.enums.Category
import christmas.enums.Menu

class Validation {

fun checkDate(input: String): Int {
val inputInt = input.toIntOrNull()
require(inputInt != null)
require(inputInt in MIN_DATE..MAX_DATE)
return inputInt
}

fun checkMenu(input: String): List<UserMenu> {
val inputList = input.split(',')
val menuList = inputList.map { menu ->
UserMenu(getMenu(menu), getPrice(menu))
}
checkMenuList(menuList)
return menuList
}

private fun checkMenuList(menuList: List<UserMenu>) {
// 중복 메뉴가 있으면 안됨
require(menuList.distinctBy { it.menu.koreanName }.size == menuList.size)

// 음료만 주문하면 안됨
require(!menuList.all { it.menu.category == Category.BEVERAGE })

// 메뉴 개수는 총 20개 이하여야 함
require(menuList.sumOf { it.amount } <= MENU_MAX_AMOUNT)
}

private fun getPrice(input: String): Int {
val price = input.substringAfter('-').toIntOrNull()
require(price != null)
require(price > 0)
return price
}

private fun getMenu(input: String): Menu {
val name = input.substringBefore('-')
return Menu.getByKoreanName(name)
}

}
Loading