From 26ff77d6ad3dfa5b685dcd3d95745b892735ce62 Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Sat, 14 Oct 2023 22:07:29 +0900 Subject: [PATCH 01/86] =?UTF-8?q?init:=20=ED=94=84=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EB=9E=A8=20=EC=B4=88=EA=B8=B0=20=EC=84=A4=EC=A0=95=20=EC=84=B8?= =?UTF-8?q?=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 37 +++ build.gradle | 34 +++ gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 63721 bytes gradle/wrapper/gradle-wrapper.properties | 7 + gradlew | 249 ++++++++++++++++++ gradlew.bat | 92 +++++++ settings.gradle | 1 + .../basic/SpringbootBasicApplication.java | 13 + src/main/resources/application.properties | 1 + .../SpringbootBasicApplicationTests.java | 13 + 10 files changed, 447 insertions(+) create mode 100644 .gitignore create mode 100644 build.gradle create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100755 gradlew create mode 100644 gradlew.bat create mode 100644 settings.gradle create mode 100644 src/main/java/org/programmers/springboot/basic/SpringbootBasicApplication.java create mode 100644 src/main/resources/application.properties create mode 100644 src/test/java/org/programmers/springboot/basic/SpringbootBasicApplicationTests.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..c2065bc262 --- /dev/null +++ b/.gitignore @@ -0,0 +1,37 @@ +HELP.md +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000000..5c32e5ab33 --- /dev/null +++ b/build.gradle @@ -0,0 +1,34 @@ +plugins { + id 'java' + id 'org.springframework.boot' version '3.1.4' + id 'io.spring.dependency-management' version '1.1.3' +} + +group = 'org.programmers' +version = '0.0.1-SNAPSHOT' + +java { + sourceCompatibility = '17' +} + +configurations { + compileOnly { + extendsFrom annotationProcessor + } +} + +repositories { + mavenCentral() +} + +dependencies { + implementation 'org.springframework.boot:spring-boot-starter:3.1.4' + implementation 'org.springframework.boot:spring-boot-starter-web' + compileOnly 'org.projectlombok:lombok' + annotationProcessor 'org.projectlombok:lombok' + testImplementation 'org.springframework.boot:spring-boot-starter-test' +} + +tasks.named('test') { + useJUnitPlatform() +} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..7f93135c49b765f8051ef9d0a6055ff8e46073d8 GIT binary patch literal 63721 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鯊%1Et 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⁢ zv5o9#E7!G0XmdzfsS{efPNi`1b44~SZ4Z8fuX!I}#8g+(wxzQwUT#Xb2(tbY1+EUhGKoT@KEU9Ktl>_0 z%bjDJg;#*gtJZv!-Zs`?^}v5eKmnbjqlvnSzE@_SP|LG_PJ6CYU+6zY6>92%E+ z=j@TZf-iW4(%U{lnYxQA;7Q!b;^brF8n0D>)`q5>|WDDXLrqYU_tKN2>=#@~OE7grMnNh?UOz-O~6 z6%rHy{#h9K0AT+lDC7q4{hw^|q6*Ry;;L%Q@)Ga}$60_q%D)rv(CtS$CQbpq9|y1e zRSrN4;$Jyl{m5bZw`$8TGvb}(LpY{-cQ)fcyJv7l3S52TLXVDsphtv&aPuDk1OzCA z4A^QtC(!11`IsNx_HnSy?>EKpHJWT^wmS~hc^p^zIIh@9f6U@I2 zC=Mve{j2^)mS#U$e{@Q?SO6%LDsXz@SY+=cK_QMmXBIU)j!$ajc-zLx3V60EXJ!qC zi<%2x8Q24YN+&8U@CIlN zrZkcT9yh%LrlGS9`G)KdP(@9Eo-AQz@8GEFWcb7U=a0H^ZVbLmz{+&M7W(nXJ4sN8 zJLR7eeK(K8`2-}j(T7JsO`L!+CvbueT%izanm-^A1Dn{`1Nw`9P?cq;7no+XfC`K(GO9?O^5zNIt4M+M8LM0=7Gz8UA@Z0N+lg+cX)NfazRu z5D)~HA^(u%w^cz+@2@_#S|u>GpB+j4KzQ^&Wcl9f z&hG#bCA(Yk0D&t&aJE^xME^&E-&xGHhXn%}psEIj641H+Nl-}boj;)Zt*t(4wZ5DN z@GXF$bL=&pBq-#vkTkh>7hl%K5|3 z{`Vn9b$iR-SoGENp}bn4;fR3>9sA%X2@1L3aE9yTra;Wb#_`xWwLSLdfu+PAu+o3| zGVnpzPr=ch{uuoHjtw7+_!L_2;knQ!DuDl0R`|%jr+}jFzXtrHIKc323?JO{l&;VF z*L1+}JU7%QJOg|5|Tc|D8fN zJORAg=_vsy{ak|o);@)Yh8Lkcg@$FG3k@ep36BRa^>~UmnRPziS>Z=`Jb2x*Q#`%A zU*i3&Vg?TluO@X0O;r2Jl6LKLUOVhSqg1*qOt^|8*c7 zo(298@+r$k_wQNGHv{|$tW(T8L+4_`FQ{kEW5Jgg{yf7ey4ss_(SNKfz(N9lx&a;< je(UuV8hP?p&}TPdm1I$XmG#(RzlD&B2izSj9sl%y5~4qc literal 0 HcmV?d00001 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000000..ac72c34e8a --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100755 index 0000000000..0adc8e1a53 --- /dev/null +++ b/gradlew @@ -0,0 +1,249 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000000..6689b85bee --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000000..0fb15a766c --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'springboot.basic' diff --git a/src/main/java/org/programmers/springboot/basic/SpringbootBasicApplication.java b/src/main/java/org/programmers/springboot/basic/SpringbootBasicApplication.java new file mode 100644 index 0000000000..c30ef31c7f --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/SpringbootBasicApplication.java @@ -0,0 +1,13 @@ +package org.programmers.springboot.basic; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class SpringbootBasicApplication { + + public static void main(String[] args) { + SpringApplication.run(SpringbootBasicApplication.class, args); + } + +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/src/main/resources/application.properties @@ -0,0 +1 @@ + diff --git a/src/test/java/org/programmers/springboot/basic/SpringbootBasicApplicationTests.java b/src/test/java/org/programmers/springboot/basic/SpringbootBasicApplicationTests.java new file mode 100644 index 0000000000..a5d2b8cb59 --- /dev/null +++ b/src/test/java/org/programmers/springboot/basic/SpringbootBasicApplicationTests.java @@ -0,0 +1,13 @@ +package org.programmers.springboot.basic; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class SpringbootBasicApplicationTests { + + @Test + void contextLoads() { + } + +} From ace60ec5a52a7250dd4be74f67301befdf1bdef8 Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Thu, 19 Oct 2023 01:00:11 +0900 Subject: [PATCH 02/86] =?UTF-8?q?init:=20application.properties=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C.=20build.gradle=20=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=B6=94=EA=B0=80.?= =?UTF-8?q?=20=EC=9E=90=EB=B0=94=20=EB=B2=84=EC=A0=84=2017=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 11 +++-------- src/main/resources/application.properties | 1 - 2 files changed, 3 insertions(+), 9 deletions(-) delete mode 100644 src/main/resources/application.properties diff --git a/build.gradle b/build.gradle index 5c32e5ab33..733468d565 100644 --- a/build.gradle +++ b/build.gradle @@ -11,21 +11,16 @@ java { sourceCompatibility = '17' } -configurations { - compileOnly { - extendsFrom annotationProcessor - } -} - repositories { mavenCentral() } dependencies { - implementation 'org.springframework.boot:spring-boot-starter:3.1.4' - implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.boot:spring-boot-starter' compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' + annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor' + testImplementation 'org.assertj:assertj-core:3.24.2' testImplementation 'org.springframework.boot:spring-boot-starter-test' } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties deleted file mode 100644 index 8b13789179..0000000000 --- a/src/main/resources/application.properties +++ /dev/null @@ -1 +0,0 @@ - From 549a09548f0c8b98f6a52de33ec794cb4306b76a Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Thu, 19 Oct 2023 01:06:13 +0900 Subject: [PATCH 03/86] =?UTF-8?q?update:=20Application=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=20=EC=9D=B4=EB=A6=84=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...asicApplication.java => VoucherManagementApplication.java} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename src/main/java/org/programmers/springboot/basic/{SpringbootBasicApplication.java => VoucherManagementApplication.java} (68%) diff --git a/src/main/java/org/programmers/springboot/basic/SpringbootBasicApplication.java b/src/main/java/org/programmers/springboot/basic/VoucherManagementApplication.java similarity index 68% rename from src/main/java/org/programmers/springboot/basic/SpringbootBasicApplication.java rename to src/main/java/org/programmers/springboot/basic/VoucherManagementApplication.java index c30ef31c7f..c0918fec96 100644 --- a/src/main/java/org/programmers/springboot/basic/SpringbootBasicApplication.java +++ b/src/main/java/org/programmers/springboot/basic/VoucherManagementApplication.java @@ -4,10 +4,10 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication -public class SpringbootBasicApplication { +public class VoucherManagementApplication { public static void main(String[] args) { - SpringApplication.run(SpringbootBasicApplication.class, args); + SpringApplication.run(VoucherManagementApplication.class, args); } } From 161141f530eb0f21c6e4bc728069a4a192630f03 Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Thu, 19 Oct 2023 01:14:29 +0900 Subject: [PATCH 04/86] =?UTF-8?q?feature:=20Voucher=20=EC=9D=B8=ED=84=B0?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=8A=A4,=20FixedAmountVoucher=20/=20Percent?= =?UTF-8?q?DiscountVoucher=20=EA=B5=AC=ED=98=84=EC=B2=B4=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../voucher/entity/FixedAmountVoucher.java | 21 +++++++++++++++++++ .../entity/PercentDiscountVoucher.java | 21 +++++++++++++++++++ .../basic/domain/voucher/entity/Voucher.java | 11 ++++++++++ 3 files changed, 53 insertions(+) create mode 100644 src/main/java/org/programmers/springboot/basic/domain/voucher/entity/FixedAmountVoucher.java create mode 100644 src/main/java/org/programmers/springboot/basic/domain/voucher/entity/PercentDiscountVoucher.java create mode 100644 src/main/java/org/programmers/springboot/basic/domain/voucher/entity/Voucher.java diff --git a/src/main/java/org/programmers/springboot/basic/domain/voucher/entity/FixedAmountVoucher.java b/src/main/java/org/programmers/springboot/basic/domain/voucher/entity/FixedAmountVoucher.java new file mode 100644 index 0000000000..42d82f472c --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/domain/voucher/entity/FixedAmountVoucher.java @@ -0,0 +1,21 @@ +package org.programmers.springboot.basic.domain.voucher.entity; + +import java.util.UUID; + +public record FixedAmountVoucher(UUID voucherId, Long discount, VoucherType voucherType) implements Voucher { + + @Override + public UUID getVoucherId() { + return this.voucherId; + } + + @Override + public VoucherType getVoucherType() { + return this.voucherType; + } + + @Override + public Long getDiscount() { + return this.discount; + } +} diff --git a/src/main/java/org/programmers/springboot/basic/domain/voucher/entity/PercentDiscountVoucher.java b/src/main/java/org/programmers/springboot/basic/domain/voucher/entity/PercentDiscountVoucher.java new file mode 100644 index 0000000000..ee4f74adc9 --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/domain/voucher/entity/PercentDiscountVoucher.java @@ -0,0 +1,21 @@ +package org.programmers.springboot.basic.domain.voucher.entity; + +import java.util.UUID; + +public record PercentDiscountVoucher(UUID voucherId, Long discount, VoucherType voucherType) implements Voucher { + + @Override + public UUID getVoucherId() { + return this.voucherId; + } + + @Override + public VoucherType getVoucherType() { + return this.voucherType; + } + + @Override + public Long getDiscount() { + return this.discount; + } +} diff --git a/src/main/java/org/programmers/springboot/basic/domain/voucher/entity/Voucher.java b/src/main/java/org/programmers/springboot/basic/domain/voucher/entity/Voucher.java new file mode 100644 index 0000000000..a5ccc7405e --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/domain/voucher/entity/Voucher.java @@ -0,0 +1,11 @@ +package org.programmers.springboot.basic.domain.voucher.entity; + +import java.io.Serializable; +import java.util.UUID; + +public interface Voucher extends Serializable { + + UUID getVoucherId(); + VoucherType getVoucherType(); + Long getDiscount(); +} From e55402fe32410245235425cd68c2b385b6660530 Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Thu, 19 Oct 2023 01:18:03 +0900 Subject: [PATCH 05/86] =?UTF-8?q?feature:=20VoucherType=20Enum=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84,=20type=EC=97=90=20=EB=A7=9E=EB=8A=94=20VoucherType?= =?UTF-8?q?=20=EB=B0=98=ED=99=98=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/voucher/entity/VoucherType.java | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 src/main/java/org/programmers/springboot/basic/domain/voucher/entity/VoucherType.java diff --git a/src/main/java/org/programmers/springboot/basic/domain/voucher/entity/VoucherType.java b/src/main/java/org/programmers/springboot/basic/domain/voucher/entity/VoucherType.java new file mode 100644 index 0000000000..7f9bab5388 --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/domain/voucher/entity/VoucherType.java @@ -0,0 +1,29 @@ +package org.programmers.springboot.basic.domain.voucher.entity; + +import org.programmers.springboot.basic.domain.voucher.exception.VoucherNotFoundException; + +import java.util.Arrays; + +public enum VoucherType { + + FIXED(1), + PERCENT(2); + + private final int type; + + VoucherType(int type) { + this.type = type; + } + + public int getType() { + return this.type; + } + + public static VoucherType valueOfVoucherByType(int type) { + + return Arrays.stream(VoucherType.values()) + .filter(voucher -> voucher.getType() == type) + .findAny() + .orElseThrow(VoucherNotFoundException::new); + } +} From b0f3a9ad1c00ce694a67505e2c2c7a7bb837f494 Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Thu, 19 Oct 2023 01:22:24 +0900 Subject: [PATCH 06/86] =?UTF-8?q?feature:=20save,=20findAll=20=EB=A9=94?= =?UTF-8?q?=EC=84=9C=EB=93=9C=EB=A5=BC=20=EA=B0=96=EB=8A=94=20VoucherRepos?= =?UTF-8?q?itory=20=EC=9D=B8=ED=84=B0=ED=8E=98=EC=9D=B4=EC=8A=A4=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/voucher/repository/VoucherRepository.java | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 src/main/java/org/programmers/springboot/basic/domain/voucher/repository/VoucherRepository.java diff --git a/src/main/java/org/programmers/springboot/basic/domain/voucher/repository/VoucherRepository.java b/src/main/java/org/programmers/springboot/basic/domain/voucher/repository/VoucherRepository.java new file mode 100644 index 0000000000..43db0ab2b2 --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/domain/voucher/repository/VoucherRepository.java @@ -0,0 +1,11 @@ +package org.programmers.springboot.basic.domain.voucher.repository; + +import org.programmers.springboot.basic.domain.voucher.entity.Voucher; + +import java.util.List; + +public interface VoucherRepository { + + void save(Voucher voucher); + List findAll(); +} From 4a919e27c141d5a132c689fca8e25ff698078fdf Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Thu, 19 Oct 2023 01:25:34 +0900 Subject: [PATCH 07/86] =?UTF-8?q?feature:=20service=20=EB=A0=88=EC=9D=B4?= =?UTF-8?q?=EC=96=B4=EC=99=80=20controller=20=EB=A0=88=EC=9D=B4=EC=96=B4?= =?UTF-8?q?=20=EA=B0=84=EC=9D=98=20=EC=9A=94=EC=B2=AD=EA=B3=BC=20=EC=9D=91?= =?UTF-8?q?=EB=8B=B5=20=EC=A0=84=EB=8B=AC=EC=9D=84=20=EC=9C=84=ED=95=9C=20?= =?UTF-8?q?requestDto,=20responseDto=20=EC=B6=94=EA=B0=80,=20csv=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=20=EA=B0=84=EC=9D=98=20=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=20=EC=A0=84=EB=8B=AC=EC=9D=84=20=EC=9C=84=ED=95=9C=20?= =?UTF-8?q?Dto=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../basic/domain/voucher/dto/CsvVoucherDto.java | 8 ++++++++ .../basic/domain/voucher/dto/VoucherRequestDto.java | 6 ++++++ .../basic/domain/voucher/dto/VoucherResponseDto.java | 8 ++++++++ 3 files changed, 22 insertions(+) create mode 100644 src/main/java/org/programmers/springboot/basic/domain/voucher/dto/CsvVoucherDto.java create mode 100644 src/main/java/org/programmers/springboot/basic/domain/voucher/dto/VoucherRequestDto.java create mode 100644 src/main/java/org/programmers/springboot/basic/domain/voucher/dto/VoucherResponseDto.java diff --git a/src/main/java/org/programmers/springboot/basic/domain/voucher/dto/CsvVoucherDto.java b/src/main/java/org/programmers/springboot/basic/domain/voucher/dto/CsvVoucherDto.java new file mode 100644 index 0000000000..3e88c43ab8 --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/domain/voucher/dto/CsvVoucherDto.java @@ -0,0 +1,8 @@ +package org.programmers.springboot.basic.domain.voucher.dto; + +import org.programmers.springboot.basic.domain.voucher.entity.VoucherType; + +import java.util.UUID; + +public record CsvVoucherDto(UUID voucherId, VoucherType voucherType, Long discount) { +} diff --git a/src/main/java/org/programmers/springboot/basic/domain/voucher/dto/VoucherRequestDto.java b/src/main/java/org/programmers/springboot/basic/domain/voucher/dto/VoucherRequestDto.java new file mode 100644 index 0000000000..99a050a817 --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/domain/voucher/dto/VoucherRequestDto.java @@ -0,0 +1,6 @@ +package org.programmers.springboot.basic.domain.voucher.dto; + +import org.programmers.springboot.basic.domain.voucher.entity.VoucherType; + +public record VoucherRequestDto(VoucherType voucherType, Long discount) { +} diff --git a/src/main/java/org/programmers/springboot/basic/domain/voucher/dto/VoucherResponseDto.java b/src/main/java/org/programmers/springboot/basic/domain/voucher/dto/VoucherResponseDto.java new file mode 100644 index 0000000000..0451c613e7 --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/domain/voucher/dto/VoucherResponseDto.java @@ -0,0 +1,8 @@ +package org.programmers.springboot.basic.domain.voucher.dto; + +import org.programmers.springboot.basic.domain.voucher.entity.VoucherType; + +import java.util.UUID; + +public record VoucherResponseDto(UUID voucherId, VoucherType voucherType, Long discount) { +} From d30cf51e8871226a66d752a2d01c7420b29f0a4a Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Thu, 19 Oct 2023 01:32:39 +0900 Subject: [PATCH 08/86] =?UTF-8?q?feature:=20=EB=AA=85=EB=A0=B9=EC=96=B4?= =?UTF-8?q?=EC=99=80=20VoucherType=EC=97=90=20=EB=94=B0=EB=A5=B8=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20=EB=A1=9C=EC=A7=81=EA=B3=BC=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=EB=A1=9C=EC=A7=81=EC=9D=84=20Service=20=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=EC=96=B4=EC=97=90=EA=B2=8C=20=ED=98=B8=EC=B6=9C?= =?UTF-8?q?=ED=95=98=EB=8A=94=20VoucherController=20=EC=B6=94=EA=B0=80,=20?= =?UTF-8?q?console=EB=A1=9C=20=EC=9E=85=EB=A0=A5=EB=B0=9B=EC=9D=80=20disco?= =?UTF-8?q?unt=20=EA=B0=92=EC=9D=98=20=EC=9C=A0=ED=9A=A8=EC=84=B1=EC=9D=84?= =?UTF-8?q?=20=EA=B2=80=EC=A6=9D=ED=95=98=EB=8A=94=20validateHandler=20?= =?UTF-8?q?=EC=9D=B8=ED=84=B0=ED=8E=98=EC=9D=B4=EC=8A=A4=EC=99=80=20?= =?UTF-8?q?=ED=83=80=EC=9E=85=EB=B3=84=20=EA=B5=AC=ED=98=84=EC=B2=B4=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../voucher/controller/VoucherController.java | 79 +++++++++++++++++++ .../validate/FixedAmountVoucherValidator.java | 16 ++++ .../PercentDiscountVoucherValidator.java | 16 ++++ .../service/validate/ValidateHandler.java | 5 ++ 4 files changed, 116 insertions(+) create mode 100644 src/main/java/org/programmers/springboot/basic/domain/voucher/controller/VoucherController.java create mode 100644 src/main/java/org/programmers/springboot/basic/domain/voucher/service/validate/FixedAmountVoucherValidator.java create mode 100644 src/main/java/org/programmers/springboot/basic/domain/voucher/service/validate/PercentDiscountVoucherValidator.java create mode 100644 src/main/java/org/programmers/springboot/basic/domain/voucher/service/validate/ValidateHandler.java diff --git a/src/main/java/org/programmers/springboot/basic/domain/voucher/controller/VoucherController.java b/src/main/java/org/programmers/springboot/basic/domain/voucher/controller/VoucherController.java new file mode 100644 index 0000000000..8555b4d312 --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/domain/voucher/controller/VoucherController.java @@ -0,0 +1,79 @@ +package org.programmers.springboot.basic.domain.voucher.controller; + +import lombok.extern.slf4j.Slf4j; +import org.programmers.springboot.basic.AppConstants; +import org.programmers.springboot.basic.domain.voucher.dto.VoucherRequestDto; +import org.programmers.springboot.basic.domain.voucher.dto.VoucherResponseDto; +import org.programmers.springboot.basic.domain.voucher.entity.VoucherType; +import org.programmers.springboot.basic.domain.voucher.exception.IllegalDiscountException; +import org.programmers.springboot.basic.domain.voucher.exception.VoucherNotFoundException; +import org.programmers.springboot.basic.domain.voucher.service.VoucherService; +import org.programmers.springboot.basic.util.manager.ConsoleIOManager; +import org.springframework.stereotype.Controller; + +import java.util.List; + +@Slf4j +@Controller +public class VoucherController { + + private final ConsoleIOManager consoleIOManager; + private final VoucherService voucherService; + + public VoucherController(ConsoleIOManager consoleIOManager, VoucherService voucherService) { + this.consoleIOManager = consoleIOManager; + this.voucherService = voucherService; + } + + public void create() { + + boolean flag = true; + int type = AppConstants.NONE; + Long discount = null; + + while (flag) { + + consoleIOManager.printCreateHandler(); + + try { + type = consoleIOManager.getInteger(); + } catch (NumberFormatException e) { + log.error(e.toString()); + } + + switch (type) { + case AppConstants.FIXED_AMOUNT, AppConstants.PERCENT_DISCOUNT -> { + try { + consoleIOManager.printDiscount(); + discount = consoleIOManager.getLong(); + } catch (NumberFormatException e) { + log.error(e.toString()); + } + flag = false; + } + default -> System.out.println("[System] 잘못된 모드의 접근입니다."); + } + } + + try { + VoucherType voucherType = VoucherType.valueOfVoucherByType(type); + VoucherRequestDto requestDto = new VoucherRequestDto(voucherType, discount); + this.voucherService.create(requestDto); // <-- IllegalDiscountException + } catch (IllegalDiscountException e) { + log.error(e.toString()); + } + } + + public void list() { + + consoleIOManager.printListHandler(); + + try { + List responseDtos = voucherService.findAll(); + consoleIOManager.printVoucher(responseDtos); + } catch (VoucherNotFoundException e) { + log.error(e.toString()); + } + + } +} diff --git a/src/main/java/org/programmers/springboot/basic/domain/voucher/service/validate/FixedAmountVoucherValidator.java b/src/main/java/org/programmers/springboot/basic/domain/voucher/service/validate/FixedAmountVoucherValidator.java new file mode 100644 index 0000000000..eeae7986e9 --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/domain/voucher/service/validate/FixedAmountVoucherValidator.java @@ -0,0 +1,16 @@ +package org.programmers.springboot.basic.domain.voucher.service.validate; + +import org.programmers.springboot.basic.AppConstants; +import org.programmers.springboot.basic.domain.voucher.exception.IllegalDiscountException; +import org.springframework.stereotype.Component; + +@Component +public class FixedAmountVoucherValidator implements ValidateHandler { + + @Override + public void validate(Long discount) { + if (discount < AppConstants.MIN_FIXED_DISCOUNT) { + throw new IllegalDiscountException("Exception Occurred: Illegal Discount value! Possible from 0."); + } + } +} diff --git a/src/main/java/org/programmers/springboot/basic/domain/voucher/service/validate/PercentDiscountVoucherValidator.java b/src/main/java/org/programmers/springboot/basic/domain/voucher/service/validate/PercentDiscountVoucherValidator.java new file mode 100644 index 0000000000..d1f95dcec8 --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/domain/voucher/service/validate/PercentDiscountVoucherValidator.java @@ -0,0 +1,16 @@ +package org.programmers.springboot.basic.domain.voucher.service.validate; + +import org.programmers.springboot.basic.AppConstants; +import org.programmers.springboot.basic.domain.voucher.exception.IllegalDiscountException; +import org.springframework.stereotype.Component; + +@Component +public class PercentDiscountVoucherValidator implements ValidateHandler { + + @Override + public void validate(Long discount) { + if (discount < AppConstants.MIN_PERCENT_DISCOUNT || discount > AppConstants.MAX_PERCENT_DISCOUNT) { + throw new IllegalDiscountException("Exception Occurred: Illegal Discount value! Possible from 0 to 100."); + } + } +} diff --git a/src/main/java/org/programmers/springboot/basic/domain/voucher/service/validate/ValidateHandler.java b/src/main/java/org/programmers/springboot/basic/domain/voucher/service/validate/ValidateHandler.java new file mode 100644 index 0000000000..067ff0b440 --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/domain/voucher/service/validate/ValidateHandler.java @@ -0,0 +1,5 @@ +package org.programmers.springboot.basic.domain.voucher.service.validate; + +public interface ValidateHandler { + void validate(Long discount); +} From c6c30fa6e618f11ff5332fc885d3834233fd833b Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Thu, 19 Oct 2023 01:35:46 +0900 Subject: [PATCH 09/86] =?UTF-8?q?feature:=20voucher=20=EA=B4=80=EB=A0=A8?= =?UTF-8?q?=20=EC=BB=A4=EC=8A=A4=ED=85=80=20=EC=98=88=EC=99=B8=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../voucher/exception/IllegalDiscountException.java | 13 +++++++++++++ .../voucher/exception/VoucherNotFoundException.java | 13 +++++++++++++ .../VoucherValidatorNotFoundException.java | 13 +++++++++++++ 3 files changed, 39 insertions(+) create mode 100644 src/main/java/org/programmers/springboot/basic/domain/voucher/exception/IllegalDiscountException.java create mode 100644 src/main/java/org/programmers/springboot/basic/domain/voucher/exception/VoucherNotFoundException.java create mode 100644 src/main/java/org/programmers/springboot/basic/domain/voucher/exception/VoucherValidatorNotFoundException.java diff --git a/src/main/java/org/programmers/springboot/basic/domain/voucher/exception/IllegalDiscountException.java b/src/main/java/org/programmers/springboot/basic/domain/voucher/exception/IllegalDiscountException.java new file mode 100644 index 0000000000..2c5eecb56f --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/domain/voucher/exception/IllegalDiscountException.java @@ -0,0 +1,13 @@ +package org.programmers.springboot.basic.domain.voucher.exception; + +public class IllegalDiscountException extends RuntimeException { + + public IllegalDiscountException(String msg) { + super(msg); + } + + @Override + public String getMessage() { + return super.getMessage(); + } +} diff --git a/src/main/java/org/programmers/springboot/basic/domain/voucher/exception/VoucherNotFoundException.java b/src/main/java/org/programmers/springboot/basic/domain/voucher/exception/VoucherNotFoundException.java new file mode 100644 index 0000000000..9c85ce3fb8 --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/domain/voucher/exception/VoucherNotFoundException.java @@ -0,0 +1,13 @@ +package org.programmers.springboot.basic.domain.voucher.exception; + +public class VoucherNotFoundException extends RuntimeException { + + public VoucherNotFoundException() { + super("Exception Occurred: No matching vouchers found!"); + } + + @Override + public String getMessage() { + return super.getMessage(); + } +} diff --git a/src/main/java/org/programmers/springboot/basic/domain/voucher/exception/VoucherValidatorNotFoundException.java b/src/main/java/org/programmers/springboot/basic/domain/voucher/exception/VoucherValidatorNotFoundException.java new file mode 100644 index 0000000000..02d0de2195 --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/domain/voucher/exception/VoucherValidatorNotFoundException.java @@ -0,0 +1,13 @@ +package org.programmers.springboot.basic.domain.voucher.exception; + +public class VoucherValidatorNotFoundException extends RuntimeException { + + public VoucherValidatorNotFoundException() { + super("Exception Occurred: No matching validator found!"); + } + + @Override + public String getMessage() { + return super.getMessage(); + } +} From aa6b869d56e558f57b1c6bbd4284039a64f03821 Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Thu, 19 Oct 2023 01:37:26 +0900 Subject: [PATCH 10/86] =?UTF-8?q?feature:=20controller=20=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=EC=96=B4=EC=99=80=20service=20=EB=A0=88=EC=9D=B4?= =?UTF-8?q?=EC=96=B4=20=EA=B0=84=20=EC=9A=94=EC=B2=AD=EA=B3=BC=20=EC=9D=91?= =?UTF-8?q?=EB=8B=B5=EC=9D=84=20=EC=9C=84=ED=95=9C=20=EC=A7=81=EB=A0=AC?= =?UTF-8?q?=ED=99=94,=20=EC=97=AD=EC=A7=81=EB=A0=AC=ED=99=94=EB=A5=BC=20?= =?UTF-8?q?=EB=8B=B4=EB=8B=B9=ED=95=98=EB=8A=94=20Mapper=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/voucher/mapper/VoucherMapper.java | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 src/main/java/org/programmers/springboot/basic/domain/voucher/mapper/VoucherMapper.java diff --git a/src/main/java/org/programmers/springboot/basic/domain/voucher/mapper/VoucherMapper.java b/src/main/java/org/programmers/springboot/basic/domain/voucher/mapper/VoucherMapper.java new file mode 100644 index 0000000000..d22ab5c805 --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/domain/voucher/mapper/VoucherMapper.java @@ -0,0 +1,60 @@ +package org.programmers.springboot.basic.domain.voucher.mapper; + +import org.programmers.springboot.basic.AppConstants; +import org.programmers.springboot.basic.config.VoucherConfig; +import org.programmers.springboot.basic.domain.voucher.dto.CsvVoucherDto; +import org.programmers.springboot.basic.domain.voucher.dto.VoucherRequestDto; +import org.programmers.springboot.basic.domain.voucher.dto.VoucherResponseDto; +import org.programmers.springboot.basic.domain.voucher.entity.Voucher; +import org.programmers.springboot.basic.domain.voucher.entity.VoucherType; +import org.springframework.stereotype.Component; + +import java.util.UUID; + +@Component +public class VoucherMapper { + + private final VoucherConfig voucherConfig; + + public VoucherMapper(VoucherConfig voucherConfig) { + this.voucherConfig = voucherConfig; + } + + public String serialize(Voucher voucher) { + + return voucher.getVoucherId() + + AppConstants.CSV_SEPARATOR + + voucher.getVoucherType() + + AppConstants.CSV_SEPARATOR + + voucher.getDiscount(); + } + + public CsvVoucherDto deserialize(String[] token) { + + UUID voucherId = UUID.fromString(token[0]); + VoucherType voucherType = VoucherType.valueOf(token[1]); + Long discount = Long.parseLong(token[2]); + + return voucherConfig.getCsvVoucherDto(voucherId, voucherType, discount); + } + + public Voucher mapToVoucher(CsvVoucherDto csvVoucherDto) { + + return switch (csvVoucherDto.voucherType()) { + case FIXED -> voucherConfig.getFixedAmountVoucher(csvVoucherDto); + case PERCENT -> voucherConfig.getPercentDiscountVoucher(csvVoucherDto); + }; + } + + public Voucher mapToVoucher(VoucherRequestDto voucherRequestDto) { + + return switch (voucherRequestDto.voucherType()) { + case FIXED -> voucherConfig.getFixedAmountVoucher(voucherRequestDto); + case PERCENT -> voucherConfig.getPercentDiscountVoucher(voucherRequestDto); + }; + } + + public VoucherResponseDto mapToVoucherDto(Voucher voucher) { + return voucherConfig.getVoucherResponseDto(voucher); + } +} From c7a01a2cfcef9bc7635b4418f9ff5e396382ef4a Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Thu, 19 Oct 2023 01:39:42 +0900 Subject: [PATCH 11/86] =?UTF-8?q?feature:=20voucher=20=EC=A0=80=EC=9E=A5?= =?UTF-8?q?=20=EB=A1=9C=EC=A7=81=EA=B3=BC=20=EC=A1=B0=ED=9A=8C=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=EC=9D=84=20=EA=B0=96=EB=8A=94=20=EC=9D=B8=EB=A9=94?= =?UTF-8?q?=EB=AA=A8=EB=A6=AC=20=EC=A0=80=EC=9E=A5=EC=86=8C=EC=9D=98=20Vou?= =?UTF-8?q?cherRepository=20=EA=B5=AC=ED=98=84=EC=B2=B4=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80,=20=EB=8F=99=EC=8B=9C=EC=84=B1=20=EC=A0=9C=EC=96=B4?= =?UTF-8?q?=EB=A5=BC=20=EC=9C=84=ED=95=9C=20concurrentHashMap=20=ED=83=80?= =?UTF-8?q?=EC=9E=85=EC=9D=98=20=EC=A0=80=EC=9E=A5=EC=86=8C=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/MemoryVoucherRepository.java | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 src/main/java/org/programmers/springboot/basic/domain/voucher/repository/MemoryVoucherRepository.java diff --git a/src/main/java/org/programmers/springboot/basic/domain/voucher/repository/MemoryVoucherRepository.java b/src/main/java/org/programmers/springboot/basic/domain/voucher/repository/MemoryVoucherRepository.java new file mode 100644 index 0000000000..862e5690a5 --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/domain/voucher/repository/MemoryVoucherRepository.java @@ -0,0 +1,29 @@ +package org.programmers.springboot.basic.domain.voucher.repository; + +import lombok.extern.slf4j.Slf4j; +import org.programmers.springboot.basic.domain.voucher.entity.Voucher; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +@Slf4j +@Profile("dev") +@Repository +public class MemoryVoucherRepository implements VoucherRepository { + + private final Map storage = new ConcurrentHashMap<>(); + + @Override + public void save(Voucher voucher) { + this.storage.put(voucher.getVoucherId(), voucher); + } + + @Override + public List findAll() { + return storage.values().stream().toList(); + } +} From 5e9473943d3396a98d76f7826253d6eea0d5b8c2 Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Thu, 19 Oct 2023 01:41:56 +0900 Subject: [PATCH 12/86] =?UTF-8?q?feature:=20controller=20=EC=9A=94?= =?UTF-8?q?=EC=B2=AD=EC=9D=84=20=EC=B2=98=EB=A6=AC=ED=95=98=EA=B3=A0=20rep?= =?UTF-8?q?ository=20=EB=A0=88=EC=9D=B4=EC=96=B4=EB=A1=9C=20=EC=9A=94?= =?UTF-8?q?=EC=B2=AD=EC=9D=84=20=EC=A0=84=EB=8B=AC=ED=95=98=EB=8A=94=20Ser?= =?UTF-8?q?vice=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=B6=94=EA=B0=80,=20?= =?UTF-8?q?=EC=9E=85=EB=A0=A5=EB=B0=9B=EC=9D=80=20=EC=A0=95=EB=B3=B4?= =?UTF-8?q?=EC=9D=98=20=EC=9C=A0=ED=9A=A8=EC=84=B1=EC=9D=B4=20=EA=B2=80?= =?UTF-8?q?=EC=A6=9D=EB=90=98=EC=97=88=EC=9D=84=20=EB=95=8C=EB=A7=8C=20rep?= =?UTF-8?q?ository=EC=97=90=EA=B2=8C=20=EC=9A=94=EC=B2=AD=EC=9D=84=20?= =?UTF-8?q?=EC=A0=84=EB=8B=AC=ED=95=98=EB=8F=84=EB=A1=9D=20=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../voucher/service/VoucherService.java | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 src/main/java/org/programmers/springboot/basic/domain/voucher/service/VoucherService.java diff --git a/src/main/java/org/programmers/springboot/basic/domain/voucher/service/VoucherService.java b/src/main/java/org/programmers/springboot/basic/domain/voucher/service/VoucherService.java new file mode 100644 index 0000000000..1035c3f728 --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/domain/voucher/service/VoucherService.java @@ -0,0 +1,69 @@ +package org.programmers.springboot.basic.domain.voucher.service; + +import lombok.extern.slf4j.Slf4j; +import org.programmers.springboot.basic.domain.voucher.dto.VoucherRequestDto; +import org.programmers.springboot.basic.domain.voucher.dto.VoucherResponseDto; +import org.programmers.springboot.basic.domain.voucher.entity.Voucher; +import org.programmers.springboot.basic.domain.voucher.entity.VoucherType; +import org.programmers.springboot.basic.domain.voucher.exception.VoucherNotFoundException; +import org.programmers.springboot.basic.domain.voucher.exception.VoucherValidatorNotFoundException; +import org.programmers.springboot.basic.domain.voucher.mapper.VoucherMapper; +import org.programmers.springboot.basic.domain.voucher.repository.VoucherRepository; +import org.programmers.springboot.basic.domain.voucher.service.validate.ValidateHandler; +import org.programmers.springboot.basic.util.exception.CSVFileIOFailureException; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@Slf4j +@Service +public class VoucherService { + + private final Map validateHandlers; + private final VoucherMapper voucherMapper; + private final VoucherRepository voucherRepository; + + public VoucherService(Map validateHandlers, VoucherMapper voucherMapper, VoucherRepository voucherRepository) { + this.validateHandlers = validateHandlers; + this.voucherMapper = voucherMapper; + this.voucherRepository = voucherRepository; + } + + public void create(VoucherRequestDto voucherRequestDto) { + + Long discount = voucherRequestDto.discount(); + VoucherType voucherType = voucherRequestDto.voucherType(); + + ValidateHandler handler = validateHandlers.get(voucherType); + + if (handler == null) { + throw new VoucherValidatorNotFoundException(); + } + + handler.validate(discount); + Voucher voucher = voucherMapper.mapToVoucher(voucherRequestDto); + this.voucherRepository.save(voucher); + } + + public List findAll() { + + List vouchers; + + try { + vouchers = voucherRepository.findAll(); + } catch (CSVFileIOFailureException e) { + log.error(e.toString()); + throw new VoucherNotFoundException(); + } + + if (vouchers.isEmpty()) { + throw new VoucherNotFoundException(); + } + + return vouchers.stream() + .map(voucherMapper::mapToVoucherDto) + .collect(Collectors.toList()); + } +} From cb9555f0491bd7b7170d962a885abaee143de20a Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Thu, 19 Oct 2023 01:43:33 +0900 Subject: [PATCH 13/86] =?UTF-8?q?feature:=20console=20=EA=B4=80=EB=A0=A8?= =?UTF-8?q?=20=EB=A1=9C=EC=A7=81=EC=9D=84=20=EB=8B=B4=EB=8B=B9=ED=95=98?= =?UTF-8?q?=EB=8A=94=20manager=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../basic/util/manager/ConsoleIOManager.java | 97 +++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 src/main/java/org/programmers/springboot/basic/util/manager/ConsoleIOManager.java diff --git a/src/main/java/org/programmers/springboot/basic/util/manager/ConsoleIOManager.java b/src/main/java/org/programmers/springboot/basic/util/manager/ConsoleIOManager.java new file mode 100644 index 0000000000..7b86cf8658 --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/util/manager/ConsoleIOManager.java @@ -0,0 +1,97 @@ +package org.programmers.springboot.basic.util.manager; + +import org.programmers.springboot.basic.domain.customer.dto.CustomerResponseDto; +import org.programmers.springboot.basic.domain.voucher.dto.VoucherResponseDto; +import org.programmers.springboot.basic.util.exception.ConsoleIOFailureException; +import org.springframework.stereotype.Component; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.List; + +@Component +public class ConsoleIOManager { + + public BufferedReader getBufferedReader() { + return new BufferedReader(new InputStreamReader(System.in)); + } + + public String getInput() { + try { + return getBufferedReader().readLine(); + } catch (IOException e) { + throw new ConsoleIOFailureException("[System] 콘솔에 입력된 값을 받아오는데 실패했습니다.\n"); + } + } + + public int getInteger() { + return Integer.parseInt(getInput()); + } + + public Long getLong() { + return Long.parseLong(getInput()); + } + + public void printSystem() { + + System.out.println("=== Voucher Program ==="); + System.out.println("Type exit to exit the program."); + System.out.println("Type create to a new voucher."); + System.out.println("Type list to list all voucher."); + System.out.println("Type blacklist to list all blacklist."); + } + + public void printCreateHandler() { + + System.out.println("=== CREATE ==="); + System.out.println("1. FixedAmountVoucher"); + System.out.println("2. PercentAmountVoucher"); + } + + public void printListHandler() { + + System.out.println("=== LIST ==="); + } + + public void printBlackListHandler() { + + System.out.println("=== BLACKLIST ==="); + } + + public void printVoucher(List responseDtos) { + responseDtos.forEach(this::printVoucherInfo); + } + + public void printBlackList(List responseDtos) { + responseDtos.forEach(this::printBlackListInfo); + } + + public void printVoucherInfo(VoucherResponseDto responseDto) { + + System.out.println("voucherId: " + responseDto.voucherId()); + System.out.println("voucherType: " + responseDto.voucherType()); + System.out.println("discount: " + responseDto.discount()); + System.out.println("---------------------------------\n"); + } + + public void printBlackListInfo(CustomerResponseDto responseDto) { + + System.out.println("customerId: " + responseDto.customerId()); + System.out.println("name: " + responseDto.name()); + System.out.println("customerType: " + responseDto.customerType()); + System.out.println("---------------------------------\n"); + } + + public void printDiscount() { + System.out.println("Q. Discount를 입력하세요."); + } + + public void printExit() { + System.out.println("[System] 시스템을 종료합니다."); + } + + public void printErrCommand() { + System.out.println("[System] 잘못된 명령의 접근입니다."); + } +} From 8a93031940c65572a1f0533ccc256b78e557ed8b Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Thu, 19 Oct 2023 01:44:36 +0900 Subject: [PATCH 14/86] =?UTF-8?q?feature:=20csv=20=ED=8C=8C=EC=9D=BC=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20=EB=A1=9C=EC=A7=81=EC=9D=84=20=EB=8B=B4?= =?UTF-8?q?=EB=8B=B9=ED=95=98=EB=8A=94=20manager=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../basic/util/manager/CSVFileManager.java | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 src/main/java/org/programmers/springboot/basic/util/manager/CSVFileManager.java diff --git a/src/main/java/org/programmers/springboot/basic/util/manager/CSVFileManager.java b/src/main/java/org/programmers/springboot/basic/util/manager/CSVFileManager.java new file mode 100644 index 0000000000..0bccdf44e8 --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/util/manager/CSVFileManager.java @@ -0,0 +1,25 @@ +package org.programmers.springboot.basic.util.manager; + +import org.springframework.stereotype.Component; + +import java.io.*; + +@Component +public class CSVFileManager { + + public BufferedWriter getBufferedWriter(File FILE) throws IOException { + return new BufferedWriter(getFIleWriter(FILE)); + } + + public FileWriter getFIleWriter(File FILE) throws IOException { + return new FileWriter(FILE, true); + } + + public BufferedReader getBufferedReader(File FILE) throws IOException { + return new BufferedReader(getFileReader(FILE)); + } + + public FileReader getFileReader(File FILE) throws IOException { + return new FileReader(FILE); + } +} From 39005b03278f14f74e17634da9ae90cd38f8ab5d Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Thu, 19 Oct 2023 01:46:41 +0900 Subject: [PATCH 15/86] =?UTF-8?q?feature:=20Voucher=20=EA=B4=80=EB=A0=A8?= =?UTF-8?q?=ED=95=9C=20=EA=B0=9D=EC=B2=B4=20=EC=83=9D=EC=84=B1=20=ED=8C=A9?= =?UTF-8?q?=ED=86=A0=EB=A6=AC=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=EC=9D=84=20=EA=B0=96=EB=8A=94=20config=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../basic/config/VoucherConfig.java | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 src/main/java/org/programmers/springboot/basic/config/VoucherConfig.java diff --git a/src/main/java/org/programmers/springboot/basic/config/VoucherConfig.java b/src/main/java/org/programmers/springboot/basic/config/VoucherConfig.java new file mode 100644 index 0000000000..fc37bcde38 --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/config/VoucherConfig.java @@ -0,0 +1,64 @@ +package org.programmers.springboot.basic.config; + +import org.programmers.springboot.basic.domain.voucher.dto.CsvVoucherDto; +import org.programmers.springboot.basic.domain.voucher.dto.VoucherRequestDto; +import org.programmers.springboot.basic.domain.voucher.dto.VoucherResponseDto; +import org.programmers.springboot.basic.domain.voucher.entity.FixedAmountVoucher; +import org.programmers.springboot.basic.domain.voucher.entity.PercentDiscountVoucher; +import org.programmers.springboot.basic.domain.voucher.entity.Voucher; +import org.programmers.springboot.basic.domain.voucher.entity.VoucherType; +import org.programmers.springboot.basic.domain.voucher.service.validate.FixedAmountVoucherValidator; +import org.programmers.springboot.basic.domain.voucher.service.validate.PercentDiscountVoucherValidator; +import org.programmers.springboot.basic.domain.voucher.service.validate.ValidateHandler; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +@Component +public class VoucherConfig { + + private final FixedAmountVoucherValidator fixedAmountVoucherValidator; + private final PercentDiscountVoucherValidator percentDiscountVoucherValidator; + + @Autowired + public VoucherConfig(FixedAmountVoucherValidator fixedAmountVoucherValidator, PercentDiscountVoucherValidator percentDiscountVoucherValidator) { + this.fixedAmountVoucherValidator = fixedAmountVoucherValidator; + this.percentDiscountVoucherValidator = percentDiscountVoucherValidator; + } + + public Voucher getFixedAmountVoucher(VoucherRequestDto requestDto) { + return new FixedAmountVoucher(UUID.randomUUID(), requestDto.discount(), requestDto.voucherType()); + } + + public Voucher getFixedAmountVoucher(CsvVoucherDto csvVoucherDto) { + return new FixedAmountVoucher(csvVoucherDto.voucherId(), csvVoucherDto.discount(), csvVoucherDto.voucherType()); + } + + public Voucher getPercentDiscountVoucher(VoucherRequestDto requestDto) { + return new PercentDiscountVoucher(UUID.randomUUID(), requestDto.discount(), requestDto.voucherType()); + } + + public Voucher getPercentDiscountVoucher(CsvVoucherDto csvVoucherDto) { + return new PercentDiscountVoucher(csvVoucherDto.voucherId(), csvVoucherDto.discount(), csvVoucherDto.voucherType()); + } + + public VoucherResponseDto getVoucherResponseDto(Voucher voucher) { + return new VoucherResponseDto(voucher.getVoucherId(), voucher.getVoucherType(), voucher.getDiscount()); + } + + public CsvVoucherDto getCsvVoucherDto(UUID voucherId, VoucherType voucherType, Long discount) { + return new CsvVoucherDto(voucherId, voucherType, discount); + } + + @Bean + public Map validateHandlers() { + Map handler = new HashMap<>(); + handler.put(VoucherType.FIXED, this.fixedAmountVoucherValidator); + handler.put(VoucherType.PERCENT, this.percentDiscountVoucherValidator); + return handler; + } +} From a8fc358a3bcf36a9208367b10f5a42706428a09d Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Thu, 19 Oct 2023 01:49:40 +0900 Subject: [PATCH 16/86] =?UTF-8?q?feature:=20=EA=B3=A0=EA=B0=9D=20Customer?= =?UTF-8?q?=20=EB=8F=84=EB=A9=94=EC=9D=B8=20=EC=B6=94=EA=B0=80,=20?= =?UTF-8?q?=EC=9D=91=EB=8B=B5=20=EC=A0=84=EB=8B=AC=EC=9D=84=20=EC=9C=84?= =?UTF-8?q?=ED=95=9C=20Dto=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/customer/dto/CsvCustomerDto.java | 8 ++++++ .../customer/dto/CustomerResponseDto.java | 8 ++++++ .../domain/customer/entity/Customer.java | 28 +++++++++++++++++++ 3 files changed, 44 insertions(+) create mode 100644 src/main/java/org/programmers/springboot/basic/domain/customer/dto/CsvCustomerDto.java create mode 100644 src/main/java/org/programmers/springboot/basic/domain/customer/dto/CustomerResponseDto.java create mode 100644 src/main/java/org/programmers/springboot/basic/domain/customer/entity/Customer.java diff --git a/src/main/java/org/programmers/springboot/basic/domain/customer/dto/CsvCustomerDto.java b/src/main/java/org/programmers/springboot/basic/domain/customer/dto/CsvCustomerDto.java new file mode 100644 index 0000000000..aeb4135d98 --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/domain/customer/dto/CsvCustomerDto.java @@ -0,0 +1,8 @@ +package org.programmers.springboot.basic.domain.customer.dto; + +import org.programmers.springboot.basic.domain.customer.entity.CustomerType; + +import java.util.UUID; + +public record CsvCustomerDto(UUID customerId, String name, CustomerType customerType) { +} diff --git a/src/main/java/org/programmers/springboot/basic/domain/customer/dto/CustomerResponseDto.java b/src/main/java/org/programmers/springboot/basic/domain/customer/dto/CustomerResponseDto.java new file mode 100644 index 0000000000..5c4860c61c --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/domain/customer/dto/CustomerResponseDto.java @@ -0,0 +1,8 @@ +package org.programmers.springboot.basic.domain.customer.dto; + +import org.programmers.springboot.basic.domain.customer.entity.CustomerType; + +import java.util.UUID; + +public record CustomerResponseDto(UUID customerId, String name, CustomerType customerType) { +} diff --git a/src/main/java/org/programmers/springboot/basic/domain/customer/entity/Customer.java b/src/main/java/org/programmers/springboot/basic/domain/customer/entity/Customer.java new file mode 100644 index 0000000000..07061ae47e --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/domain/customer/entity/Customer.java @@ -0,0 +1,28 @@ +package org.programmers.springboot.basic.domain.customer.entity; + +import java.util.UUID; + +public class Customer { + + private final UUID customerId; + private final String name; + private final CustomerType customerType; + + public Customer(UUID customerId, String name, CustomerType customerType) { + this.customerId = customerId; + this.name = name; + this.customerType = customerType; + } + + public UUID getCustomerId() { + return this.customerId; + } + + public String getName() { + return this.name; + } + + public CustomerType getCustomerType() { + return this.customerType; + } +} From b36e226f5836ee3baa83b2ba6c0339cd58e02323 Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Thu, 19 Oct 2023 01:51:19 +0900 Subject: [PATCH 17/86] =?UTF-8?q?feature:=20=EA=B3=A0=EA=B0=9D=20=ED=83=80?= =?UTF-8?q?=EC=9E=85=EC=9D=84=20=EA=B7=9C=EC=A0=95=ED=95=98=EB=8A=94=20Enu?= =?UTF-8?q?m=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=B6=94=EA=B0=80,=20?= =?UTF-8?q?=EB=B8=94=EB=9E=99=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=EB=A1=9C=EC=A7=81=EC=9D=84=20Service=20=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=EC=96=B4=EC=97=90=EA=B2=8C=20=EC=A0=84=EB=8B=AC?= =?UTF-8?q?=ED=95=98=EB=8A=94=20Controller=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/CustomerController.java | 35 +++++++++++++++++++ .../domain/customer/entity/CustomerType.java | 7 ++++ 2 files changed, 42 insertions(+) create mode 100644 src/main/java/org/programmers/springboot/basic/domain/customer/controller/CustomerController.java create mode 100644 src/main/java/org/programmers/springboot/basic/domain/customer/entity/CustomerType.java diff --git a/src/main/java/org/programmers/springboot/basic/domain/customer/controller/CustomerController.java b/src/main/java/org/programmers/springboot/basic/domain/customer/controller/CustomerController.java new file mode 100644 index 0000000000..8ae4ca2d61 --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/domain/customer/controller/CustomerController.java @@ -0,0 +1,35 @@ +package org.programmers.springboot.basic.domain.customer.controller; + +import lombok.extern.slf4j.Slf4j; +import org.programmers.springboot.basic.domain.customer.dto.CustomerResponseDto; +import org.programmers.springboot.basic.domain.customer.exception.CustomerNotFoundException; +import org.programmers.springboot.basic.domain.customer.service.CustomerService; +import org.programmers.springboot.basic.util.manager.ConsoleIOManager; +import org.springframework.stereotype.Controller; + +import java.util.List; + +@Slf4j +@Controller +public class CustomerController { + + private final ConsoleIOManager consoleIOManager; + private final CustomerService customerService; + + public CustomerController(ConsoleIOManager consoleIOManager, CustomerService customerService) { + this.consoleIOManager = consoleIOManager; + this.customerService = customerService; + } + + public void blacklist() { + + this.consoleIOManager.printBlackListHandler(); + + try { + List responseDtos = customerService.findBlackList(); + consoleIOManager.printBlackList(responseDtos); + } catch (CustomerNotFoundException e) { + log.error(e.toString()); + } + } +} diff --git a/src/main/java/org/programmers/springboot/basic/domain/customer/entity/CustomerType.java b/src/main/java/org/programmers/springboot/basic/domain/customer/entity/CustomerType.java new file mode 100644 index 0000000000..c00cccc53e --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/domain/customer/entity/CustomerType.java @@ -0,0 +1,7 @@ +package org.programmers.springboot.basic.domain.customer.entity; + +public enum CustomerType { + + NORMAL, + BLACK +} From 5545978ae7bc1cb9d46846aff99f81398db8cdb4 Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Thu, 19 Oct 2023 01:53:05 +0900 Subject: [PATCH 18/86] =?UTF-8?q?feature:=20csv=20=ED=8C=8C=EC=9D=BC?= =?UTF-8?q?=EA=B3=BC=20console=20=EA=B4=80=EB=A0=A8=20=EC=BB=A4=EC=8A=A4?= =?UTF-8?q?=ED=85=80=20=EC=98=88=EC=99=B8=20=ED=81=B4=EB=9E=98=EC=8A=A4=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../util/exception/CSVFileIOFailureException.java | 13 +++++++++++++ .../util/exception/ConsoleIOFailureException.java | 13 +++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 src/main/java/org/programmers/springboot/basic/util/exception/CSVFileIOFailureException.java create mode 100644 src/main/java/org/programmers/springboot/basic/util/exception/ConsoleIOFailureException.java diff --git a/src/main/java/org/programmers/springboot/basic/util/exception/CSVFileIOFailureException.java b/src/main/java/org/programmers/springboot/basic/util/exception/CSVFileIOFailureException.java new file mode 100644 index 0000000000..53b6d5a5dd --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/util/exception/CSVFileIOFailureException.java @@ -0,0 +1,13 @@ +package org.programmers.springboot.basic.util.exception; + +public class CSVFileIOFailureException extends RuntimeException { + + public CSVFileIOFailureException(String msg) { + super(msg); + } + + @Override + public String getMessage() { + return super.getMessage(); + } +} diff --git a/src/main/java/org/programmers/springboot/basic/util/exception/ConsoleIOFailureException.java b/src/main/java/org/programmers/springboot/basic/util/exception/ConsoleIOFailureException.java new file mode 100644 index 0000000000..e446975542 --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/util/exception/ConsoleIOFailureException.java @@ -0,0 +1,13 @@ +package org.programmers.springboot.basic.util.exception; + +public class ConsoleIOFailureException extends RuntimeException { + + public ConsoleIOFailureException(String msg) { + super(msg); + } + + @Override + public String getMessage() { + return super.getMessage(); + } +} From 326a64f70b1f131ed3fb8b1d7df42016448dff49 Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Thu, 19 Oct 2023 01:56:59 +0900 Subject: [PATCH 19/86] =?UTF-8?q?feature:=20=EB=AA=85=EB=A0=B9=EC=96=B4=20?= =?UTF-8?q?=ED=83=80=EC=9E=85=EC=9D=84=20=EA=B7=9C=EC=A0=95=ED=95=98?= =?UTF-8?q?=EB=8A=94=20commandType=20Enum=20=ED=81=B4=EB=9E=98=EC=8A=A4=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80,=20commandLineRunner=EB=A5=BC=20=EC=9D=B4?= =?UTF-8?q?=EC=9A=A9=ED=95=9C=20command-line-application=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84,=20=EB=AA=85=EB=A0=B9=EC=96=B4=EC=97=90=20=EB=94=B0?= =?UTF-8?q?=EB=9D=BC=20=EC=A0=81=EC=A0=88=ED=95=9C=20controller=EC=97=90?= =?UTF-8?q?=EA=B2=8C=20=EC=9A=94=EC=B2=AD=EC=9D=84=20=EC=A0=84=EB=8B=AC?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../springboot/basic/util/CommandType.java | 25 +++++++ .../basic/util/manager/CommandManager.java | 71 +++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 src/main/java/org/programmers/springboot/basic/util/CommandType.java create mode 100644 src/main/java/org/programmers/springboot/basic/util/manager/CommandManager.java diff --git a/src/main/java/org/programmers/springboot/basic/util/CommandType.java b/src/main/java/org/programmers/springboot/basic/util/CommandType.java new file mode 100644 index 0000000000..cc3ca18f35 --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/util/CommandType.java @@ -0,0 +1,25 @@ +package org.programmers.springboot.basic.util; + +import org.programmers.springboot.basic.util.exception.CommandNotFoundException; + +import java.util.Arrays; + +public enum CommandType { + + CREATE, + LIST, + BLACKLIST, + EXIT; + + public static CommandType valueOfCommand(String command) { + + return Arrays.stream(values()) + .filter(value -> value.getLowerCase().equals(command)) + .findAny() + .orElseThrow(CommandNotFoundException::new); + } + + public String getLowerCase() { + return this.name().toLowerCase(); + } +} diff --git a/src/main/java/org/programmers/springboot/basic/util/manager/CommandManager.java b/src/main/java/org/programmers/springboot/basic/util/manager/CommandManager.java new file mode 100644 index 0000000000..43bf7646ed --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/util/manager/CommandManager.java @@ -0,0 +1,71 @@ +package org.programmers.springboot.basic.util.manager; + +import lombok.extern.slf4j.Slf4j; +import org.programmers.springboot.basic.domain.customer.controller.CustomerController; +import org.programmers.springboot.basic.domain.voucher.controller.VoucherController; +import org.programmers.springboot.basic.util.CommandType; +import org.programmers.springboot.basic.util.exception.CommandNotFoundException; +import org.programmers.springboot.basic.util.exception.ConsoleIOFailureException; +import org.springframework.boot.CommandLineRunner; +import org.springframework.stereotype.Component; + +@Slf4j +@Component +public class CommandManager implements CommandLineRunner { + + private final ConsoleIOManager consoleIOManager; + private final VoucherController voucherController; + private final CustomerController customerController; + + public CommandManager(ConsoleIOManager consoleIOManager, VoucherController voucherController, CustomerController customerController) { + this.consoleIOManager = consoleIOManager; + this.voucherController = voucherController; + this.customerController = customerController; + } + + @Override + public void run(String... args) { + + boolean flag = true; + + try { + while (flag) { + + consoleIOManager.printSystem(); + + String input = consoleIOManager.getInput(); + CommandType command = CommandType.valueOfCommand(input); + + switch (command) { + + case CREATE -> createHandler(); + + case LIST -> listHandler(); + + case BLACKLIST -> blackListHandler(); + + case EXIT -> { + consoleIOManager.printExit(); + flag = false; + } + + default -> consoleIOManager.printErrCommand(); + } + } + } catch (ConsoleIOFailureException | CommandNotFoundException e) { + log.error(e.toString()); + } + } + + private void createHandler() { + this.voucherController.create(); + } + + private void listHandler() { + this.voucherController.list(); + } + + private void blackListHandler() { + this.customerController.blacklist(); + } +} From a5ced0dc9ae53e85e0e916b8fb64d9d9974a3bf7 Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Thu, 19 Oct 2023 01:58:07 +0900 Subject: [PATCH 20/86] =?UTF-8?q?feature:=20customer=20=EB=8F=84=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=20=EA=B4=80=EB=A0=A8=20Service=20=ED=81=B4=EB=9E=98?= =?UTF-8?q?=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../customer/service/CustomerService.java | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 src/main/java/org/programmers/springboot/basic/domain/customer/service/CustomerService.java diff --git a/src/main/java/org/programmers/springboot/basic/domain/customer/service/CustomerService.java b/src/main/java/org/programmers/springboot/basic/domain/customer/service/CustomerService.java new file mode 100644 index 0000000000..694d798abc --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/domain/customer/service/CustomerService.java @@ -0,0 +1,38 @@ +package org.programmers.springboot.basic.domain.customer.service; + +import lombok.extern.slf4j.Slf4j; +import org.programmers.springboot.basic.config.CustomerConfig; +import org.programmers.springboot.basic.domain.customer.dto.CustomerResponseDto; +import org.programmers.springboot.basic.domain.customer.entity.Customer; +import org.programmers.springboot.basic.domain.customer.exception.CustomerNotFoundException; +import org.programmers.springboot.basic.domain.customer.repository.CustomerRepository; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.stream.Collectors; + +@Slf4j +@Service +public class CustomerService { + + private final CustomerRepository customerRepository; + private final CustomerConfig customerConfig; + + public CustomerService(CustomerRepository customerRepository, CustomerConfig customerConfig) { + this.customerRepository = customerRepository; + this.customerConfig = customerConfig; + } + + public List findBlackList() { + + List customers = this.customerRepository.findBlackList(); + + if (customers.isEmpty()) { + throw new CustomerNotFoundException(); + } + + return customers.stream() + .map(customerConfig::getCustomerResponseDto) + .collect(Collectors.toList()); + } +} From 15a8d59e7effe59cc2fec269b35f4c5bc24c82ff Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Thu, 19 Oct 2023 01:58:38 +0900 Subject: [PATCH 21/86] =?UTF-8?q?feature:=20Dto=20=EC=9D=B4=EC=9A=A9?= =?UTF-8?q?=EC=9D=84=20=EC=9C=84=ED=95=9C=20=EC=A7=81=EB=A0=AC=ED=99=94,?= =?UTF-8?q?=20=EC=97=AD=EC=A7=81=EB=A0=AC=ED=99=94=EB=A5=BC=20=EB=8B=B4?= =?UTF-8?q?=EB=8B=B9=ED=95=98=EB=8A=94=20customer=20=EB=8F=84=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=EC=9D=98=20Mapper=20=ED=81=B4=EB=9E=98=EC=8A=A4=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../customer/mapper/CustomerMapper.java | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 src/main/java/org/programmers/springboot/basic/domain/customer/mapper/CustomerMapper.java diff --git a/src/main/java/org/programmers/springboot/basic/domain/customer/mapper/CustomerMapper.java b/src/main/java/org/programmers/springboot/basic/domain/customer/mapper/CustomerMapper.java new file mode 100644 index 0000000000..2f2a05245c --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/domain/customer/mapper/CustomerMapper.java @@ -0,0 +1,32 @@ +package org.programmers.springboot.basic.domain.customer.mapper; + +import org.programmers.springboot.basic.config.CustomerConfig; +import org.programmers.springboot.basic.domain.customer.dto.CsvCustomerDto; +import org.programmers.springboot.basic.domain.customer.entity.Customer; +import org.programmers.springboot.basic.domain.customer.entity.CustomerType; +import org.springframework.stereotype.Component; + +import java.util.UUID; + +@Component +public class CustomerMapper { + + private final CustomerConfig customerConfig; + + public CustomerMapper(CustomerConfig customerConfig) { + this.customerConfig = customerConfig; + } + + public CsvCustomerDto deserialize(String[] token) { + + UUID customerId = UUID.fromString(token[0]); + String name = token[1]; + CustomerType customerType = CustomerType.valueOf(token[2]); + + return this.customerConfig.getCsvCustomerDto(customerId, name, customerType); + } + + public Customer mapToCustomer(CsvCustomerDto customerDto) { + return this.customerConfig.getCustomer(customerDto); + } +} From 079c3b830f248264e4faa3adca1b934ebfb630ea Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Thu, 19 Oct 2023 01:59:23 +0900 Subject: [PATCH 22/86] =?UTF-8?q?feature:=20Customer=20=EB=8F=84=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=EA=B3=BC=20CommandType=20=EA=B4=80=EB=A0=A8=20?= =?UTF-8?q?=EC=BB=A4=EC=8A=A4=ED=85=80=20=EC=98=88=EC=99=B8=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exception/CustomerNotFoundException.java | 13 +++++++++++++ .../util/exception/CommandNotFoundException.java | 13 +++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 src/main/java/org/programmers/springboot/basic/domain/customer/exception/CustomerNotFoundException.java create mode 100644 src/main/java/org/programmers/springboot/basic/util/exception/CommandNotFoundException.java diff --git a/src/main/java/org/programmers/springboot/basic/domain/customer/exception/CustomerNotFoundException.java b/src/main/java/org/programmers/springboot/basic/domain/customer/exception/CustomerNotFoundException.java new file mode 100644 index 0000000000..2e718b6ab5 --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/domain/customer/exception/CustomerNotFoundException.java @@ -0,0 +1,13 @@ +package org.programmers.springboot.basic.domain.customer.exception; + +public class CustomerNotFoundException extends RuntimeException { + + public CustomerNotFoundException() { + super("Exception Occurred: No matching customers found!"); + } + + @Override + public String getMessage() { + return super.getMessage(); + } +} diff --git a/src/main/java/org/programmers/springboot/basic/util/exception/CommandNotFoundException.java b/src/main/java/org/programmers/springboot/basic/util/exception/CommandNotFoundException.java new file mode 100644 index 0000000000..c56dcde292 --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/util/exception/CommandNotFoundException.java @@ -0,0 +1,13 @@ +package org.programmers.springboot.basic.util.exception; + +public class CommandNotFoundException extends RuntimeException { + + public CommandNotFoundException() { + super("Exception Occurred: No matching commands found!"); + } + + @Override + public String getMessage() { + return super.getMessage(); + } +} From 0034a7551657aa11a33e03b6b82cf8b848b33ba7 Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Thu, 19 Oct 2023 02:00:20 +0900 Subject: [PATCH 23/86] =?UTF-8?q?feature:=20Customer=20=EB=8F=84=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=20=EA=B0=9D=EC=B2=B4=20=EC=83=9D=EC=84=B1=EC=9D=84=20?= =?UTF-8?q?=EB=8B=B4=EB=8B=B9=ED=95=98=EB=8A=94=20Config=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../basic/config/CustomerConfig.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 src/main/java/org/programmers/springboot/basic/config/CustomerConfig.java diff --git a/src/main/java/org/programmers/springboot/basic/config/CustomerConfig.java b/src/main/java/org/programmers/springboot/basic/config/CustomerConfig.java new file mode 100644 index 0000000000..434c442aa9 --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/config/CustomerConfig.java @@ -0,0 +1,24 @@ +package org.programmers.springboot.basic.config; + +import org.programmers.springboot.basic.domain.customer.dto.CsvCustomerDto; +import org.programmers.springboot.basic.domain.customer.dto.CustomerResponseDto; +import org.programmers.springboot.basic.domain.customer.entity.Customer; +import org.programmers.springboot.basic.domain.customer.entity.CustomerType; +import org.springframework.stereotype.Component; + +import java.util.UUID; + +@Component +public class CustomerConfig { + public Customer getCustomer(CsvCustomerDto csvCustomerDto) { + return new Customer(csvCustomerDto.customerId(), csvCustomerDto.name(), csvCustomerDto.customerType()); + } + + public CustomerResponseDto getCustomerResponseDto(Customer customer) { + return new CustomerResponseDto(customer.getCustomerId(), customer.getName(), customer.getCustomerType()); + } + + public CsvCustomerDto getCsvCustomerDto(UUID customerId, String name, CustomerType customerType) { + return new CsvCustomerDto(customerId, name, customerType); + } +} From ce5ab480329b32ff3dda8fd2baf71777e0386f23 Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Thu, 19 Oct 2023 02:02:00 +0900 Subject: [PATCH 24/86] =?UTF-8?q?feature:=20=EC=95=A0=ED=94=8C=EB=A6=AC?= =?UTF-8?q?=EC=BC=80=EC=9D=B4=EC=85=98=EC=97=90=EC=84=9C=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=EB=90=98=EB=8A=94=20=EB=AA=85=EC=8B=9C=EC=A0=81?= =?UTF-8?q?=EC=9D=B8=20=EC=9D=98=EB=AF=B8=EC=9D=98=20=EC=83=81=EC=88=98?= =?UTF-8?q?=EB=93=A4=EC=9D=84=20=EB=8B=A4=EB=A3=A8=EB=8A=94=20Constants=20?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../programmers/springboot/basic/AppConstants.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/main/java/org/programmers/springboot/basic/AppConstants.java diff --git a/src/main/java/org/programmers/springboot/basic/AppConstants.java b/src/main/java/org/programmers/springboot/basic/AppConstants.java new file mode 100644 index 0000000000..5ac5f25262 --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/AppConstants.java @@ -0,0 +1,12 @@ +package org.programmers.springboot.basic; + +public final class AppConstants { + + public static final Long MIN_FIXED_DISCOUNT = 0L; + public static final Long MIN_PERCENT_DISCOUNT = 0L; + public static final Long MAX_PERCENT_DISCOUNT = 100L; + public static final int NONE = 0; + public static final int FIXED_AMOUNT = 1; + public static final int PERCENT_DISCOUNT = 2; + public static final String CSV_SEPARATOR = ","; +} From dc494482b177002c82109b14a7bbac4ac04026fe Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Thu, 19 Oct 2023 03:05:19 +0900 Subject: [PATCH 25/86] =?UTF-8?q?feature:=20customer=20=EB=8F=84=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=EC=9D=84=20=EC=A0=80=EC=9E=A5=EC=86=8C=EC=97=90=20?= =?UTF-8?q?=EC=A0=80=EC=9E=A5=ED=95=98=EA=B3=A0=20=EC=A1=B0=ED=9A=8C?= =?UTF-8?q?=ED=95=98=EB=8A=94=20Repository=20=ED=81=B4=EB=9E=98=EC=8A=A4?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/CustomerRepository.java | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 src/main/java/org/programmers/springboot/basic/domain/customer/repository/CustomerRepository.java diff --git a/src/main/java/org/programmers/springboot/basic/domain/customer/repository/CustomerRepository.java b/src/main/java/org/programmers/springboot/basic/domain/customer/repository/CustomerRepository.java new file mode 100644 index 0000000000..4e631fda10 --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/domain/customer/repository/CustomerRepository.java @@ -0,0 +1,83 @@ +package org.programmers.springboot.basic.domain.customer.repository; + +import lombok.extern.slf4j.Slf4j; +import org.programmers.springboot.basic.AppConstants; +import org.programmers.springboot.basic.VoucherManagementApplication; +import org.programmers.springboot.basic.domain.customer.dto.CsvCustomerDto; +import org.programmers.springboot.basic.domain.customer.entity.Customer; +import org.programmers.springboot.basic.domain.customer.mapper.CustomerMapper; +import org.programmers.springboot.basic.util.exception.CSVFileIOFailureException; +import org.programmers.springboot.basic.util.manager.CSVFileManager; +import org.programmers.springboot.basic.util.properties.FileProperties; +import org.springframework.stereotype.Repository; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +@Slf4j +@Repository +public class CustomerRepository { + + private final File FILE; + private final CSVFileManager csvFileManager; + private final CustomerMapper customerMapper; + private final FileProperties fileProperties; + + public CustomerRepository(CSVFileManager csvFileManager, CustomerMapper customerMapper, FileProperties fileProperties) { + this.csvFileManager = csvFileManager; + this.customerMapper = customerMapper; + this.fileProperties = fileProperties; + FILE = new File(getFilePath()); + } + + public List findBlackList() { + return getBlackListFromFile(); + } + + private List getBlackListFromFile() { + + List blackList = new ArrayList<>(); + String line; + + try { + BufferedReader reader = this.csvFileManager.getBufferedReader(FILE); + + while ((line = reader.readLine()) != null) { + String[] token = line.split(AppConstants.CSV_SEPARATOR); + CsvCustomerDto customerDto = customerMapper.deserialize(token); + Customer customer = customerMapper.mapToCustomer(customerDto); + blackList.add(customer); + } + } catch (FileNotFoundException e) { + throw new CSVFileIOFailureException("Exception Occurred: No matching file found!"); + } catch (IOException e) { + throw new CSVFileIOFailureException("Exception Occurred: Failed to read file!"); + } + + return blackList; + } + + private boolean isRunningFromJar() { + return VoucherManagementApplication.class.getResource("VoucherManagementApplication.class").toString().startsWith("jar:"); + } + + private String getFilePath() { + + String folderPath = this.fileProperties.getUserDir(); + String fileName = this.fileProperties.getNames().get("voucher").getFileName(); + String resourcePath = this.fileProperties.getResources().getPath(); + String filePath = folderPath + resourcePath + fileName; + + if (isRunningFromJar()) { + folderPath = this.fileProperties.getProjDir(); + resourcePath = this.fileProperties.getResources().getJar(); + filePath = folderPath + resourcePath + fileName; + } + + return filePath; + } +} From 82a06c546c61d84ddb989252dc6fa6f40e171f34 Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Thu, 19 Oct 2023 03:06:17 +0900 Subject: [PATCH 26/86] =?UTF-8?q?feature:=20voucher=20=EB=8F=84=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=EC=9D=84=20=ED=8C=8C=EC=9D=BC=EC=97=90=20=EC=A0=80?= =?UTF-8?q?=EC=9E=A5=ED=95=98=EA=B3=A0=20=EC=A1=B0=ED=9A=8C=ED=95=98?= =?UTF-8?q?=EB=8A=94=20Repository=20=ED=81=B4=EB=9E=98=EC=8A=A4=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/FileVoucherRepository.java | 120 ++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 src/main/java/org/programmers/springboot/basic/domain/voucher/repository/FileVoucherRepository.java diff --git a/src/main/java/org/programmers/springboot/basic/domain/voucher/repository/FileVoucherRepository.java b/src/main/java/org/programmers/springboot/basic/domain/voucher/repository/FileVoucherRepository.java new file mode 100644 index 0000000000..82be416b3e --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/domain/voucher/repository/FileVoucherRepository.java @@ -0,0 +1,120 @@ +package org.programmers.springboot.basic.domain.voucher.repository; + +import lombok.extern.slf4j.Slf4j; +import org.programmers.springboot.basic.AppConstants; +import org.programmers.springboot.basic.VoucherManagementApplication; +import org.programmers.springboot.basic.domain.voucher.dto.CsvVoucherDto; +import org.programmers.springboot.basic.domain.voucher.entity.Voucher; +import org.programmers.springboot.basic.domain.voucher.mapper.VoucherMapper; +import org.programmers.springboot.basic.util.exception.CSVFileIOFailureException; +import org.programmers.springboot.basic.util.manager.CSVFileManager; +import org.programmers.springboot.basic.util.properties.FileProperties; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Repository; + +import java.io.*; +import java.util.*; +import java.util.concurrent.locks.ReentrantLock; + +@Slf4j +@Profile("default") +@Repository +public class FileVoucherRepository implements VoucherRepository { + + private final File FILE; + private final CSVFileManager csvFileManager; + private final VoucherMapper voucherMapper; + private final FileProperties fileProperties; + private final ReentrantLock lock = new ReentrantLock(); + + public FileVoucherRepository(CSVFileManager csvFileManager, VoucherMapper voucherMapper, FileProperties fileProperties) { + this.csvFileManager = csvFileManager; + this.voucherMapper = voucherMapper; + this.fileProperties = fileProperties; + String filePath = getFilePath(); + FILE = new File(filePath); + } + + @Override + public void save(Voucher voucher) { + lock.lock(); + try { + write(voucher); + } finally { + lock.unlock(); + } + } + + @Override + public List findAll() { + lock.lock(); + try { + return read(); + } finally { + lock.unlock(); + } + } + + private List read() { + + List voucherList = new ArrayList<>(); + String line; + + try { + BufferedReader reader = this.csvFileManager.getBufferedReader(FILE); + + while ((line = reader.readLine()) != null) { + String[] token = line.split(AppConstants.CSV_SEPARATOR); + //if (token.length < 3) return voucherList; + CsvVoucherDto voucherDto = voucherMapper.deserialize(token); + Voucher voucher = voucherMapper.mapToVoucher(voucherDto); + voucherList.add(voucher); + } + + } catch (FileNotFoundException e) { + throw new CSVFileIOFailureException("Exception Occurred: No matching file found!"); + } catch (IOException e) { + throw new CSVFileIOFailureException("Exception Occurred: Failed to read file!"); + } + + return voucherList; + } + + + private void write(Voucher voucher) { + + try { + BufferedWriter writer = this.csvFileManager.getBufferedWriter(FILE); + + String serializer = this.voucherMapper.serialize(voucher); + writer.write(serializer); + writer.newLine(); + writer.flush(); + + } catch (FileNotFoundException e) { + throw new CSVFileIOFailureException("Exception Occurred: No matching file found!"); + } catch (IOException e) { + throw new CSVFileIOFailureException("Exception Occurred: Failed to write file!"); + } + } + + private boolean isRunningFromJar() { + return VoucherManagementApplication.class.getResource("VoucherManagementApplication.class").toString().startsWith("jar:"); + } + + private String getFilePath() { + + String folderPath = this.fileProperties.getUserDir(); + String fileName = this.fileProperties.getNames().get("voucher").getFileName(); + String resourcePath = this.fileProperties.getResources().getPath(); + String filePath = folderPath + resourcePath + fileName; + + if (isRunningFromJar()) { + folderPath = this.fileProperties.getProjDir(); + resourcePath = this.fileProperties.getResources().getJar(); + filePath = folderPath + resourcePath + fileName; + } + + return filePath; + } +} From b461235711d350812d54163b160a83210c768aa1 Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Thu, 19 Oct 2023 03:09:33 +0900 Subject: [PATCH 27/86] =?UTF-8?q?feature:=20yaml=20=EC=A0=95=EB=B3=B4?= =?UTF-8?q?=EB=A5=BC=20=EB=B0=94=EC=9D=B8=EB=94=A9=ED=95=98=EB=8A=94=20Pro?= =?UTF-8?q?perties=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../basic/util/properties/FileProperties.java | 97 +++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 src/main/java/org/programmers/springboot/basic/util/properties/FileProperties.java diff --git a/src/main/java/org/programmers/springboot/basic/util/properties/FileProperties.java b/src/main/java/org/programmers/springboot/basic/util/properties/FileProperties.java new file mode 100644 index 0000000000..1c74a45a6c --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/util/properties/FileProperties.java @@ -0,0 +1,97 @@ +package org.programmers.springboot.basic.util.properties; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.Map; + +@Component +@ConfigurationProperties(prefix = "file") +public class FileProperties { + + private final Map names = new HashMap<>(); + private Resources resources; + private String userDir; + private String projDir; + + + public Map getNames() { + return names; + } + public Resources getResources() { + return resources; + } + + public void setResources(Resources resources) { + this.resources = resources; + } + + public String getUserDir() { + return userDir; + } + + public void setUserDir(String userDir) { + this.userDir = userDir; + } + + public String getProjDir() { + return projDir; + } + + public void setProjDir(String projDir) { + this.projDir = projDir; + } + + public static class PathProperties { + + private String fileName; + private String testFileName; + + public String getFileName() { + return fileName; + } + + public void setFileName(String fileName) { + this.fileName = fileName; + } + + public String getTestFileName() { + return testFileName; + } + + public void setTestFileName(String testFileName) { + this.testFileName = testFileName; + } + } + + public static class Resources { + private String path; + private String testPath; + private String jar; + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + public String getTestPath() { + return testPath; + } + + public void setTestPath(String testPath) { + this.testPath = testPath; + } + + public String getJar() { + return jar; + } + + public void setJar(String jar) { + this.jar = jar; + } + } +} From 35e8151563c4c4444fa19ee40f721fc3c6bd90c0 Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Thu, 19 Oct 2023 03:10:49 +0900 Subject: [PATCH 28/86] =?UTF-8?q?feature:=20=ED=8C=8C=EC=9D=BC=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=EC=A0=95=EB=B3=B4=EB=A5=BC=20=EB=8B=B4=EA=B3=A0=20?= =?UTF-8?q?=EC=9E=88=EB=8A=94=20application.yaml=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.yaml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 src/main/resources/application.yaml diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml new file mode 100644 index 0000000000..8d5796363a --- /dev/null +++ b/src/main/resources/application.yaml @@ -0,0 +1,14 @@ +file: + proj-dir: /Users/hongjiin/Documents/Programmers/Dev.course/Mission#1/Fork/springboot-basic/ + names: + voucher: + file-name: voucherRecord.csv + test-file-name: testVoucherRecord.csv + customer: + file-name: customer_blacklist.csv + test-file-name: test_customer_blacklist.csv + user-dir: ${user.dir} + resources: + path: /src/main/resources/ + test-path: /src/test/resources/ + jar: /build/resources/main/ From 8ae75a3e0f22d7179920fa482d523baeff83caf5 Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Thu, 19 Oct 2023 03:11:50 +0900 Subject: [PATCH 29/86] =?UTF-8?q?feature:=20customer=20=EB=8F=84=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=EA=B3=BC=20voucher=20=EB=8F=84=EB=A9=94=EC=9D=B8?= =?UTF-8?q?=EC=9D=98=20=EC=84=9C=EB=B9=84=EC=8A=A4=20=ED=8C=8C=EC=9D=BC?= =?UTF-8?q?=EA=B3=BC=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=ED=8C=8C=EC=9D=BC=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/customer_blacklist.csv | 5 +++++ src/main/resources/voucherRecord.csv | 2 ++ src/test/resources/testVoucherRecord.csv | 0 src/test/resources/test_customer_blacklist.csv | 5 +++++ 4 files changed, 12 insertions(+) create mode 100644 src/main/resources/customer_blacklist.csv create mode 100644 src/main/resources/voucherRecord.csv create mode 100644 src/test/resources/testVoucherRecord.csv create mode 100644 src/test/resources/test_customer_blacklist.csv diff --git a/src/main/resources/customer_blacklist.csv b/src/main/resources/customer_blacklist.csv new file mode 100644 index 0000000000..47a2fdd88e --- /dev/null +++ b/src/main/resources/customer_blacklist.csv @@ -0,0 +1,5 @@ +94401b49-c776-48ba-87da-239719e7a728,마리오,BLACK +d7d3ddd8-9eb4-4cfa-9b6a-788fc4297f3e,와리오,BLACK +86a5b3ba-8a47-475e-9111-3b3f1bf41a71,피치,BLACK +599504e1-84ee-4c7b-b524-007e699b153e,루이지,BLACK +75ad9183-dd31-49e7-a82e-d089af728765,키노피오,BLACK diff --git a/src/main/resources/voucherRecord.csv b/src/main/resources/voucherRecord.csv new file mode 100644 index 0000000000..e578a28049 --- /dev/null +++ b/src/main/resources/voucherRecord.csv @@ -0,0 +1,2 @@ +27fce720-8943-444b-8c69-20ea7497a2f6,FIXED,1500 +6aeebac4-1d47-4989-a1e4-5d58091b798a,PERCENT,50 diff --git a/src/test/resources/testVoucherRecord.csv b/src/test/resources/testVoucherRecord.csv new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/test/resources/test_customer_blacklist.csv b/src/test/resources/test_customer_blacklist.csv new file mode 100644 index 0000000000..47a2fdd88e --- /dev/null +++ b/src/test/resources/test_customer_blacklist.csv @@ -0,0 +1,5 @@ +94401b49-c776-48ba-87da-239719e7a728,마리오,BLACK +d7d3ddd8-9eb4-4cfa-9b6a-788fc4297f3e,와리오,BLACK +86a5b3ba-8a47-475e-9111-3b3f1bf41a71,피치,BLACK +599504e1-84ee-4c7b-b524-007e699b153e,루이지,BLACK +75ad9183-dd31-49e7-a82e-d089af728765,키노피오,BLACK From 03b9506d85b0f3a9493e82e743b4f0b7f5b7beed Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Thu, 19 Oct 2023 03:22:33 +0900 Subject: [PATCH 30/86] =?UTF-8?q?feature:=20log=20=EA=B4=80=EB=A0=A8=20?= =?UTF-8?q?=EC=A0=95=EB=B3=B4=EB=A5=BC=20=EB=8B=B4=EC=9D=80=20logback.xml?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/logback.xml | 50 ++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 src/main/resources/logback.xml diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml new file mode 100644 index 0000000000..022d931c1b --- /dev/null +++ b/src/main/resources/logback.xml @@ -0,0 +1,50 @@ + + + + + + + + + + logs/error.log + false + + logs/error-%d{yyyy-MM-dd}.log + + + ${LOG_FILE_PATTERN} + + + + + + ERROR + ACCEPT + DENY + + + ${LOG_PATTERN} + + + + + + + + + + + + From 714f7209da3fa21dc9e83ce97fe23448780b0809 Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Thu, 19 Oct 2023 03:26:30 +0900 Subject: [PATCH 31/86] =?UTF-8?q?feature:=20logback.xml=20name=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/logback.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml index 022d931c1b..e262509779 100644 --- a/src/main/resources/logback.xml +++ b/src/main/resources/logback.xml @@ -28,7 +28,7 @@ + class="ch.qos.logback.core.ConsoleAppender"> ERROR ACCEPT From b38e324cbe31998ca7668e93e4ba9d9d360fe665 Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Thu, 19 Oct 2023 03:32:11 +0900 Subject: [PATCH 32/86] =?UTF-8?q?feature:=20'jar'=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EC=A4=91=EB=B3=B5=EB=90=98=EB=8A=94=20/=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 8d5796363a..2a9c76afbe 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -11,4 +11,4 @@ file: resources: path: /src/main/resources/ test-path: /src/test/resources/ - jar: /build/resources/main/ + jar: build/resources/main/ From dbd911865f1003dc127a599c44f3a7e3d70c67fc Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Thu, 19 Oct 2023 03:42:47 +0900 Subject: [PATCH 33/86] =?UTF-8?q?update:=20getFilePath=EC=9D=98=20fileName?= =?UTF-8?q?=EC=9D=84=20=EB=B0=9B=EC=95=84=EC=98=A4=EB=8A=94=20get=20?= =?UTF-8?q?=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=9D=B8=EC=9E=90=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/customer/repository/CustomerRepository.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/programmers/springboot/basic/domain/customer/repository/CustomerRepository.java b/src/main/java/org/programmers/springboot/basic/domain/customer/repository/CustomerRepository.java index 4e631fda10..23d14b812c 100644 --- a/src/main/java/org/programmers/springboot/basic/domain/customer/repository/CustomerRepository.java +++ b/src/main/java/org/programmers/springboot/basic/domain/customer/repository/CustomerRepository.java @@ -31,7 +31,9 @@ public CustomerRepository(CSVFileManager csvFileManager, CustomerMapper customer this.csvFileManager = csvFileManager; this.customerMapper = customerMapper; this.fileProperties = fileProperties; - FILE = new File(getFilePath()); + String filePath = getFilePath(); + System.out.println("filePath: " + filePath); + FILE = new File(filePath); } public List findBlackList() { @@ -68,7 +70,7 @@ private boolean isRunningFromJar() { private String getFilePath() { String folderPath = this.fileProperties.getUserDir(); - String fileName = this.fileProperties.getNames().get("voucher").getFileName(); + String fileName = this.fileProperties.getNames().get("customer").getFileName(); String resourcePath = this.fileProperties.getResources().getPath(); String filePath = folderPath + resourcePath + fileName; @@ -76,6 +78,7 @@ private String getFilePath() { folderPath = this.fileProperties.getProjDir(); resourcePath = this.fileProperties.getResources().getJar(); filePath = folderPath + resourcePath + fileName; + System.out.println("filePath: " + filePath); } return filePath; From d8fbddb5671966537cd5a53e34185b692ea5c809 Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Thu, 19 Oct 2023 03:44:32 +0900 Subject: [PATCH 34/86] =?UTF-8?q?update:=20application.yaml=20'jar'=20file?= =?UTF-8?q?Path=EC=97=90=20'/'=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 2a9c76afbe..8d5796363a 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -11,4 +11,4 @@ file: resources: path: /src/main/resources/ test-path: /src/test/resources/ - jar: build/resources/main/ + jar: /build/resources/main/ From 2ea82f0127a37b3c8d56279893db8d22527e47ee Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Thu, 19 Oct 2023 03:46:26 +0900 Subject: [PATCH 35/86] =?UTF-8?q?update:=20=EB=94=94=EB=B2=84=EA=B9=85=20?= =?UTF-8?q?=EC=9C=84=ED=95=9C=20System.out.println()=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../basic/domain/customer/repository/CustomerRepository.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/org/programmers/springboot/basic/domain/customer/repository/CustomerRepository.java b/src/main/java/org/programmers/springboot/basic/domain/customer/repository/CustomerRepository.java index 23d14b812c..32aa469b5c 100644 --- a/src/main/java/org/programmers/springboot/basic/domain/customer/repository/CustomerRepository.java +++ b/src/main/java/org/programmers/springboot/basic/domain/customer/repository/CustomerRepository.java @@ -32,7 +32,6 @@ public CustomerRepository(CSVFileManager csvFileManager, CustomerMapper customer this.customerMapper = customerMapper; this.fileProperties = fileProperties; String filePath = getFilePath(); - System.out.println("filePath: " + filePath); FILE = new File(filePath); } @@ -78,7 +77,6 @@ private String getFilePath() { folderPath = this.fileProperties.getProjDir(); resourcePath = this.fileProperties.getResources().getJar(); filePath = folderPath + resourcePath + fileName; - System.out.println("filePath: " + filePath); } return filePath; From ce5be87df1540766e853fe3e26cb02cc0537e24c Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Thu, 19 Oct 2023 14:29:40 +0900 Subject: [PATCH 36/86] =?UTF-8?q?update:=20=EC=82=AC=EC=9A=A9=EB=90=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EB=8A=94=20Serializable=20=EC=9D=98?= =?UTF-8?q?=EC=A1=B4=EC=84=B1=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../springboot/basic/domain/voucher/entity/Voucher.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/org/programmers/springboot/basic/domain/voucher/entity/Voucher.java b/src/main/java/org/programmers/springboot/basic/domain/voucher/entity/Voucher.java index a5ccc7405e..e1739c1831 100644 --- a/src/main/java/org/programmers/springboot/basic/domain/voucher/entity/Voucher.java +++ b/src/main/java/org/programmers/springboot/basic/domain/voucher/entity/Voucher.java @@ -1,9 +1,8 @@ package org.programmers.springboot.basic.domain.voucher.entity; -import java.io.Serializable; import java.util.UUID; -public interface Voucher extends Serializable { +public interface Voucher { UUID getVoucherId(); VoucherType getVoucherType(); From fe63dac29ae1c516dfba926beb06e498ec113660 Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Thu, 19 Oct 2023 14:44:04 +0900 Subject: [PATCH 37/86] =?UTF-8?q?feature=20:=20FileVoucherRepository?= =?UTF-8?q?=EC=9D=98=20=EB=A9=94=EC=84=9C=EB=93=9C=EC=97=90=20=EB=8C=80?= =?UTF-8?q?=ED=95=B4=20=EB=8F=99=EC=8B=9C=EC=84=B1=20=EC=A0=9C=EC=96=B4=20?= =?UTF-8?q?=EC=84=B1=EA=B3=B5=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/FileVoucherRepositoryTest.java | 313 ++++++++++++++++++ 1 file changed, 313 insertions(+) create mode 100644 src/test/java/org/programmers/springboot/basic/voucher/repository/FileVoucherRepositoryTest.java diff --git a/src/test/java/org/programmers/springboot/basic/voucher/repository/FileVoucherRepositoryTest.java b/src/test/java/org/programmers/springboot/basic/voucher/repository/FileVoucherRepositoryTest.java new file mode 100644 index 0000000000..7b9334658a --- /dev/null +++ b/src/test/java/org/programmers/springboot/basic/voucher/repository/FileVoucherRepositoryTest.java @@ -0,0 +1,313 @@ +package org.programmers.springboot.basic.voucher.repository; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.programmers.springboot.basic.AppConstants; +import org.programmers.springboot.basic.VoucherManagementApplication; +import org.programmers.springboot.basic.config.VoucherConfig; +import org.programmers.springboot.basic.domain.voucher.dto.CsvVoucherDto; +import org.programmers.springboot.basic.domain.voucher.dto.VoucherRequestDto; +import org.programmers.springboot.basic.domain.voucher.entity.FixedAmountVoucher; +import org.programmers.springboot.basic.domain.voucher.entity.PercentDiscountVoucher; +import org.programmers.springboot.basic.domain.voucher.entity.Voucher; +import org.programmers.springboot.basic.domain.voucher.entity.VoucherType; +import org.programmers.springboot.basic.domain.voucher.exception.VoucherValidatorNotFoundException; +import org.programmers.springboot.basic.domain.voucher.mapper.VoucherMapper; +import org.programmers.springboot.basic.domain.voucher.repository.FileVoucherRepository; +import org.programmers.springboot.basic.domain.voucher.repository.VoucherRepository; +import org.programmers.springboot.basic.domain.voucher.service.VoucherService; +import org.programmers.springboot.basic.domain.voucher.service.validate.FixedAmountVoucherValidator; +import org.programmers.springboot.basic.domain.voucher.service.validate.PercentDiscountVoucherValidator; +import org.programmers.springboot.basic.domain.voucher.service.validate.ValidateHandler; +import org.programmers.springboot.basic.util.exception.CSVFileIOFailureException; +import org.programmers.springboot.basic.util.manager.CSVFileManager; +import org.programmers.springboot.basic.util.properties.FileProperties; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.ReentrantLock; + +import static org.assertj.core.api.Assertions.assertThat; + +@ExtendWith(SpringExtension.class) +@ComponentScan( + basePackages = {"org.programmers.springboot.basic"}, + basePackageClasses = FileVoucherRepositoryTest.class +) +@ContextConfiguration(classes = { + CSVFileManager.class, + VoucherMapper.class, + VoucherConfig.class, + FileVoucherRepository.class, + PercentDiscountVoucherValidator.class, + FixedAmountVoucherValidator.class, + FileProperties.class +}) +@EnableConfigurationProperties(value = FileProperties.class) +@TestPropertySource("classpath:application.yaml") +@SpringBootTest +public class FileVoucherRepositoryTest { + + private final CSVFileManager csvFileManager; + private final VoucherMapper voucherMapper; + private final FileProperties fileProperties; + private final Map validateHandlers; + + @Autowired + public FileVoucherRepositoryTest(CSVFileManager csvFileManager, VoucherMapper voucherMapper, FileProperties fileProperties, Map validateHandlers) { + this.csvFileManager = csvFileManager; + this.voucherMapper = voucherMapper; + this.fileProperties = fileProperties; + this.validateHandlers = validateHandlers; + } + + private static boolean isRunningFromJar() { + return VoucherManagementApplication.class.getResource("VoucherManagementApplication.class").toString().startsWith("jar:"); + } + + private String getTestFilePath() { + + String folderPath = this.fileProperties.getUserDir(); + String fileName = this.fileProperties.getNames().get("voucher").getFileName(); + String resourcePath = this.fileProperties.getResources().getPath(); + String filePath = folderPath + resourcePath + fileName; + + if (isRunningFromJar()) { + folderPath = this.fileProperties.getProjDir(); + resourcePath = this.fileProperties.getResources().getJar(); + filePath = folderPath + resourcePath + fileName; + } + + return filePath; + } + + @AfterEach + public void after() { + try { + String filePath = getTestFilePath(); + Path path = Path.of(filePath); + if (Files.exists(path)) Files.write(path, new byte[0], StandardOpenOption.TRUNCATE_EXISTING); + } catch (IOException e) { + e.printStackTrace(); + } + + } + + static class TestVoucherService extends VoucherService { + + private final VoucherRepository voucherRepository; + private final VoucherMapper voucherMapper; + private final Map validateHandlers; + + public TestVoucherService(Map validateHandlers, VoucherMapper voucherMapper, CSVFileManager csvFileManager, VoucherRepository voucherRepository, FileProperties fileProperties) { + super(validateHandlers, voucherMapper, voucherRepository); + this.validateHandlers = validateHandlers; + this.voucherMapper = voucherMapper; + this.voucherRepository = new TestFileVoucherRepository(csvFileManager, voucherMapper, fileProperties); + } + + @Override + public void create(VoucherRequestDto voucherRequestDto) { + Long discount = voucherRequestDto.discount(); + VoucherType voucherType = voucherRequestDto.voucherType(); + + ValidateHandler handler = validateHandlers.get(voucherType); + + if (handler == null) { + throw new VoucherValidatorNotFoundException(); + } + + handler.validate(discount); + Voucher voucher = voucherMapper.mapToVoucher(voucherRequestDto); + this.voucherRepository.save(voucher); + } + } + + static class TestFileVoucherRepository extends FileVoucherRepository { + + private final File FILE; + private final CSVFileManager csvFileManager; + private final VoucherMapper voucherMapper; + private final FileProperties fileProperties; + + ReentrantLock lock = new ReentrantLock(); + + public TestFileVoucherRepository(CSVFileManager csvFileManager, VoucherMapper voucherMapper, FileProperties fileProperties) { + super(csvFileManager, voucherMapper, fileProperties); + this.csvFileManager = csvFileManager; + this.voucherMapper = voucherMapper; + this.fileProperties = fileProperties; + String filePath = getTestFilePath(); + FILE = new File(filePath); + } + + private String getTestFilePath() { + + String folderPath = this.fileProperties.getUserDir(); + String fileName = this.fileProperties.getNames().get("voucher").getFileName(); + String resourcePath = this.fileProperties.getResources().getPath(); + String filePath = folderPath + resourcePath + fileName; + + if (isRunningFromJar()) { + folderPath = this.fileProperties.getProjDir(); + resourcePath = this.fileProperties.getResources().getJar(); + filePath = folderPath + resourcePath + fileName; + } + + return filePath; + } + + @Override + public void save(Voucher voucher) { + lock.lock(); + try { + writeVoucherToFile(voucher); + } finally { + lock.unlock(); + } + } + + private void writeVoucherToFile(Voucher voucher) { + + try { + BufferedWriter writer = this.csvFileManager.getBufferedWriter(FILE); + + String serializer = voucherMapper.serialize(voucher); + writer.write(serializer); + writer.newLine(); + writer.flush(); + + } catch (FileNotFoundException e) { + throw new CSVFileIOFailureException("Exception Occurred: No matching file found!"); + } catch (IOException e) { + throw new CSVFileIOFailureException("Exception Occurred: Failed to save file!"); + } + } + + @Override + public List findAll() { + lock.lock(); + try { + return getVoucherFromFile(); + } finally { + lock.unlock(); + } + } + + private List getVoucherFromFile() { + + List voucherList = new ArrayList<>(); + String line; + + try { + BufferedReader reader = this.csvFileManager.getBufferedReader(FILE); + + while ((line = reader.readLine()) != null) { + String[] token = line.split(AppConstants.CSV_SEPARATOR); + CsvVoucherDto voucherDto = voucherMapper.deserialize(token); + Voucher voucher = voucherMapper.mapToVoucher(voucherDto); + voucherList.add(voucher); + } + } catch (FileNotFoundException e) { + throw new CSVFileIOFailureException("Exception Occurred: No matching file found!"); + } catch (IOException e) { + throw new CSVFileIOFailureException("Exception Occurred: Failed to read file!"); + } + + return voucherList; + } + } + + @Test + @DisplayName("파일 쓰기에 대한 동시성 제어 성공 테스트") + public void successConcurrencyControlAboutFileWrite() throws InterruptedException { + + final TestFileVoucherRepository testFileVoucherRepository = new TestFileVoucherRepository( + this.csvFileManager, this.voucherMapper, this.fileProperties + ); + + Voucher voucher1 = new PercentDiscountVoucher(UUID.randomUUID(), 10L, VoucherType.PERCENT); + Voucher voucher2 = new PercentDiscountVoucher(UUID.randomUUID(), 50L, VoucherType.PERCENT); + Voucher voucher3 = new PercentDiscountVoucher(UUID.randomUUID(), 60L, VoucherType.PERCENT); + Voucher voucher4 = new FixedAmountVoucher(UUID.randomUUID(), 1000L, VoucherType.FIXED); + Voucher voucher5 = new FixedAmountVoucher(UUID.randomUUID(), 1500L, VoucherType.FIXED); + + int threadCount = 5; + ExecutorService executorService = Executors.newFixedThreadPool(threadCount); + + try { + for (int i = 0; i < threadCount; i++) { + executorService.submit(() -> { + testFileVoucherRepository.save(voucher1); + testFileVoucherRepository.save(voucher2); + testFileVoucherRepository.save(voucher3); + testFileVoucherRepository.save(voucher4); + testFileVoucherRepository.save(voucher5); + }); + } + + executorService.shutdown(); + executorService.awaitTermination(10, TimeUnit.SECONDS); + } finally { + executorService.shutdownNow(); + } + + testFileVoucherRepository.findAll().forEach(System.out::println); + } + + @Test + public void testConcurrency() throws InterruptedException { + + int threadCount = 20; + int iterations = 100; + + final TestFileVoucherRepository testFileVoucherRepository = new TestFileVoucherRepository( + this.csvFileManager, this.voucherMapper, this.fileProperties + ); + + final TestVoucherService testVoucherService = new TestVoucherService( + this.validateHandlers, this.voucherMapper, this.csvFileManager, testFileVoucherRepository, this.fileProperties + ); + + CountDownLatch latch = new CountDownLatch(threadCount); + + ExecutorService executorService = Executors.newFixedThreadPool(threadCount); + + Runnable task = () -> { + for (int i = 0; i < iterations; i++) { + testVoucherService.create(new VoucherRequestDto(VoucherType.FIXED, 50L)); + } + latch.countDown(); + }; + + for (int i = 0; i < threadCount; i++) { + executorService.execute(task); + } + + latch.await(); + + List vouchers = testFileVoucherRepository.findAll(); + assertThat(iterations * threadCount).isEqualTo(vouchers.size()); + executorService.shutdown(); + } +} + From 038c6e3459c8b3ccd3d27ef232117efb9da3a823 Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Thu, 19 Oct 2023 19:31:34 +0900 Subject: [PATCH 38/86] =?UTF-8?q?update=20:=20.jar=20=EC=8B=A4=ED=96=89=20?= =?UTF-8?q?=EC=97=AC=EB=B6=80=EB=A5=BC=20=ED=99=95=EC=9D=B8=ED=95=98?= =?UTF-8?q?=EB=8A=94=20isRunningFromJar=20=EB=A9=94=EC=84=9C=EB=93=9C?= =?UTF-8?q?=EB=A5=BC=20AppConfig=20=ED=81=B4=EB=9E=98=EC=8A=A4=EC=97=90=20?= =?UTF-8?q?static=20=EB=A9=94=EC=84=9C=EB=93=9C=EB=A1=9C=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/customer/repository/CustomerRepository.java | 8 ++------ .../voucher/repository/FileVoucherRepository.java | 8 ++------ .../voucher/repository/FileVoucherRepositoryTest.java | 10 +++------- 3 files changed, 7 insertions(+), 19 deletions(-) diff --git a/src/main/java/org/programmers/springboot/basic/domain/customer/repository/CustomerRepository.java b/src/main/java/org/programmers/springboot/basic/domain/customer/repository/CustomerRepository.java index 32aa469b5c..af2cb0cb7c 100644 --- a/src/main/java/org/programmers/springboot/basic/domain/customer/repository/CustomerRepository.java +++ b/src/main/java/org/programmers/springboot/basic/domain/customer/repository/CustomerRepository.java @@ -1,8 +1,8 @@ package org.programmers.springboot.basic.domain.customer.repository; import lombok.extern.slf4j.Slf4j; +import org.programmers.springboot.basic.config.AppConfig; import org.programmers.springboot.basic.AppConstants; -import org.programmers.springboot.basic.VoucherManagementApplication; import org.programmers.springboot.basic.domain.customer.dto.CsvCustomerDto; import org.programmers.springboot.basic.domain.customer.entity.Customer; import org.programmers.springboot.basic.domain.customer.mapper.CustomerMapper; @@ -62,10 +62,6 @@ private List getBlackListFromFile() { return blackList; } - private boolean isRunningFromJar() { - return VoucherManagementApplication.class.getResource("VoucherManagementApplication.class").toString().startsWith("jar:"); - } - private String getFilePath() { String folderPath = this.fileProperties.getUserDir(); @@ -73,7 +69,7 @@ private String getFilePath() { String resourcePath = this.fileProperties.getResources().getPath(); String filePath = folderPath + resourcePath + fileName; - if (isRunningFromJar()) { + if (AppConfig.isRunningFromJar()) { folderPath = this.fileProperties.getProjDir(); resourcePath = this.fileProperties.getResources().getJar(); filePath = folderPath + resourcePath + fileName; diff --git a/src/main/java/org/programmers/springboot/basic/domain/voucher/repository/FileVoucherRepository.java b/src/main/java/org/programmers/springboot/basic/domain/voucher/repository/FileVoucherRepository.java index 82be416b3e..c93e48e84e 100644 --- a/src/main/java/org/programmers/springboot/basic/domain/voucher/repository/FileVoucherRepository.java +++ b/src/main/java/org/programmers/springboot/basic/domain/voucher/repository/FileVoucherRepository.java @@ -2,7 +2,7 @@ import lombok.extern.slf4j.Slf4j; import org.programmers.springboot.basic.AppConstants; -import org.programmers.springboot.basic.VoucherManagementApplication; +import org.programmers.springboot.basic.config.AppConfig; import org.programmers.springboot.basic.domain.voucher.dto.CsvVoucherDto; import org.programmers.springboot.basic.domain.voucher.entity.Voucher; import org.programmers.springboot.basic.domain.voucher.mapper.VoucherMapper; @@ -98,10 +98,6 @@ private void write(Voucher voucher) { } } - private boolean isRunningFromJar() { - return VoucherManagementApplication.class.getResource("VoucherManagementApplication.class").toString().startsWith("jar:"); - } - private String getFilePath() { String folderPath = this.fileProperties.getUserDir(); @@ -109,7 +105,7 @@ private String getFilePath() { String resourcePath = this.fileProperties.getResources().getPath(); String filePath = folderPath + resourcePath + fileName; - if (isRunningFromJar()) { + if (AppConfig.isRunningFromJar()) { folderPath = this.fileProperties.getProjDir(); resourcePath = this.fileProperties.getResources().getJar(); filePath = folderPath + resourcePath + fileName; diff --git a/src/test/java/org/programmers/springboot/basic/voucher/repository/FileVoucherRepositoryTest.java b/src/test/java/org/programmers/springboot/basic/voucher/repository/FileVoucherRepositoryTest.java index 7b9334658a..5b9aa3bcf5 100644 --- a/src/test/java/org/programmers/springboot/basic/voucher/repository/FileVoucherRepositoryTest.java +++ b/src/test/java/org/programmers/springboot/basic/voucher/repository/FileVoucherRepositoryTest.java @@ -5,7 +5,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.programmers.springboot.basic.AppConstants; -import org.programmers.springboot.basic.VoucherManagementApplication; +import org.programmers.springboot.basic.config.AppConfig; import org.programmers.springboot.basic.config.VoucherConfig; import org.programmers.springboot.basic.domain.voucher.dto.CsvVoucherDto; import org.programmers.springboot.basic.domain.voucher.dto.VoucherRequestDto; @@ -80,10 +80,6 @@ public FileVoucherRepositoryTest(CSVFileManager csvFileManager, VoucherMapper vo this.validateHandlers = validateHandlers; } - private static boolean isRunningFromJar() { - return VoucherManagementApplication.class.getResource("VoucherManagementApplication.class").toString().startsWith("jar:"); - } - private String getTestFilePath() { String folderPath = this.fileProperties.getUserDir(); @@ -91,7 +87,7 @@ private String getTestFilePath() { String resourcePath = this.fileProperties.getResources().getPath(); String filePath = folderPath + resourcePath + fileName; - if (isRunningFromJar()) { + if (AppConfig.isRunningFromJar()) { folderPath = this.fileProperties.getProjDir(); resourcePath = this.fileProperties.getResources().getJar(); filePath = folderPath + resourcePath + fileName; @@ -167,7 +163,7 @@ private String getTestFilePath() { String resourcePath = this.fileProperties.getResources().getPath(); String filePath = folderPath + resourcePath + fileName; - if (isRunningFromJar()) { + if (AppConfig.isRunningFromJar()) { folderPath = this.fileProperties.getProjDir(); resourcePath = this.fileProperties.getResources().getJar(); filePath = folderPath + resourcePath + fileName; From 04cad26ba02f77af25d9d144be11a7f60397a0c9 Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Thu, 19 Oct 2023 19:31:34 +0900 Subject: [PATCH 39/86] =?UTF-8?q?update=20:=20.jar=20=EC=8B=A4=ED=96=89=20?= =?UTF-8?q?=EC=97=AC=EB=B6=80=EB=A5=BC=20=ED=99=95=EC=9D=B8=ED=95=98?= =?UTF-8?q?=EB=8A=94=20isRunningFromJar=20=EB=A9=94=EC=84=9C=EB=93=9C?= =?UTF-8?q?=EB=A5=BC=20AppConfig=20=ED=81=B4=EB=9E=98=EC=8A=A4=EC=97=90=20?= =?UTF-8?q?static=20=EB=A9=94=EC=84=9C=EB=93=9C=EB=A1=9C=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../springboot/basic/config/AppConfig.java | 12 ++++++++++++ .../customer/repository/CustomerRepository.java | 8 ++------ .../voucher/repository/FileVoucherRepository.java | 8 ++------ .../repository/FileVoucherRepositoryTest.java | 10 +++------- 4 files changed, 19 insertions(+), 19 deletions(-) create mode 100644 src/main/java/org/programmers/springboot/basic/config/AppConfig.java diff --git a/src/main/java/org/programmers/springboot/basic/config/AppConfig.java b/src/main/java/org/programmers/springboot/basic/config/AppConfig.java new file mode 100644 index 0000000000..1e38a9969a --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/config/AppConfig.java @@ -0,0 +1,12 @@ +package org.programmers.springboot.basic.config; + +import org.programmers.springboot.basic.VoucherManagementApplication; + +import java.util.Objects; + +public class AppConfig { + + public static boolean isRunningFromJar() { + return Objects.requireNonNull(VoucherManagementApplication.class.getResource("VoucherManagementApplication.class")).toString().startsWith("jar:"); + } +} diff --git a/src/main/java/org/programmers/springboot/basic/domain/customer/repository/CustomerRepository.java b/src/main/java/org/programmers/springboot/basic/domain/customer/repository/CustomerRepository.java index 32aa469b5c..af2cb0cb7c 100644 --- a/src/main/java/org/programmers/springboot/basic/domain/customer/repository/CustomerRepository.java +++ b/src/main/java/org/programmers/springboot/basic/domain/customer/repository/CustomerRepository.java @@ -1,8 +1,8 @@ package org.programmers.springboot.basic.domain.customer.repository; import lombok.extern.slf4j.Slf4j; +import org.programmers.springboot.basic.config.AppConfig; import org.programmers.springboot.basic.AppConstants; -import org.programmers.springboot.basic.VoucherManagementApplication; import org.programmers.springboot.basic.domain.customer.dto.CsvCustomerDto; import org.programmers.springboot.basic.domain.customer.entity.Customer; import org.programmers.springboot.basic.domain.customer.mapper.CustomerMapper; @@ -62,10 +62,6 @@ private List getBlackListFromFile() { return blackList; } - private boolean isRunningFromJar() { - return VoucherManagementApplication.class.getResource("VoucherManagementApplication.class").toString().startsWith("jar:"); - } - private String getFilePath() { String folderPath = this.fileProperties.getUserDir(); @@ -73,7 +69,7 @@ private String getFilePath() { String resourcePath = this.fileProperties.getResources().getPath(); String filePath = folderPath + resourcePath + fileName; - if (isRunningFromJar()) { + if (AppConfig.isRunningFromJar()) { folderPath = this.fileProperties.getProjDir(); resourcePath = this.fileProperties.getResources().getJar(); filePath = folderPath + resourcePath + fileName; diff --git a/src/main/java/org/programmers/springboot/basic/domain/voucher/repository/FileVoucherRepository.java b/src/main/java/org/programmers/springboot/basic/domain/voucher/repository/FileVoucherRepository.java index 82be416b3e..c93e48e84e 100644 --- a/src/main/java/org/programmers/springboot/basic/domain/voucher/repository/FileVoucherRepository.java +++ b/src/main/java/org/programmers/springboot/basic/domain/voucher/repository/FileVoucherRepository.java @@ -2,7 +2,7 @@ import lombok.extern.slf4j.Slf4j; import org.programmers.springboot.basic.AppConstants; -import org.programmers.springboot.basic.VoucherManagementApplication; +import org.programmers.springboot.basic.config.AppConfig; import org.programmers.springboot.basic.domain.voucher.dto.CsvVoucherDto; import org.programmers.springboot.basic.domain.voucher.entity.Voucher; import org.programmers.springboot.basic.domain.voucher.mapper.VoucherMapper; @@ -98,10 +98,6 @@ private void write(Voucher voucher) { } } - private boolean isRunningFromJar() { - return VoucherManagementApplication.class.getResource("VoucherManagementApplication.class").toString().startsWith("jar:"); - } - private String getFilePath() { String folderPath = this.fileProperties.getUserDir(); @@ -109,7 +105,7 @@ private String getFilePath() { String resourcePath = this.fileProperties.getResources().getPath(); String filePath = folderPath + resourcePath + fileName; - if (isRunningFromJar()) { + if (AppConfig.isRunningFromJar()) { folderPath = this.fileProperties.getProjDir(); resourcePath = this.fileProperties.getResources().getJar(); filePath = folderPath + resourcePath + fileName; diff --git a/src/test/java/org/programmers/springboot/basic/voucher/repository/FileVoucherRepositoryTest.java b/src/test/java/org/programmers/springboot/basic/voucher/repository/FileVoucherRepositoryTest.java index 7b9334658a..5b9aa3bcf5 100644 --- a/src/test/java/org/programmers/springboot/basic/voucher/repository/FileVoucherRepositoryTest.java +++ b/src/test/java/org/programmers/springboot/basic/voucher/repository/FileVoucherRepositoryTest.java @@ -5,7 +5,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.programmers.springboot.basic.AppConstants; -import org.programmers.springboot.basic.VoucherManagementApplication; +import org.programmers.springboot.basic.config.AppConfig; import org.programmers.springboot.basic.config.VoucherConfig; import org.programmers.springboot.basic.domain.voucher.dto.CsvVoucherDto; import org.programmers.springboot.basic.domain.voucher.dto.VoucherRequestDto; @@ -80,10 +80,6 @@ public FileVoucherRepositoryTest(CSVFileManager csvFileManager, VoucherMapper vo this.validateHandlers = validateHandlers; } - private static boolean isRunningFromJar() { - return VoucherManagementApplication.class.getResource("VoucherManagementApplication.class").toString().startsWith("jar:"); - } - private String getTestFilePath() { String folderPath = this.fileProperties.getUserDir(); @@ -91,7 +87,7 @@ private String getTestFilePath() { String resourcePath = this.fileProperties.getResources().getPath(); String filePath = folderPath + resourcePath + fileName; - if (isRunningFromJar()) { + if (AppConfig.isRunningFromJar()) { folderPath = this.fileProperties.getProjDir(); resourcePath = this.fileProperties.getResources().getJar(); filePath = folderPath + resourcePath + fileName; @@ -167,7 +163,7 @@ private String getTestFilePath() { String resourcePath = this.fileProperties.getResources().getPath(); String filePath = folderPath + resourcePath + fileName; - if (isRunningFromJar()) { + if (AppConfig.isRunningFromJar()) { folderPath = this.fileProperties.getProjDir(); resourcePath = this.fileProperties.getResources().getJar(); filePath = folderPath + resourcePath + fileName; From 3583061f49d84055efec229006a3f5e1ae690ba5 Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Thu, 19 Oct 2023 20:08:23 +0900 Subject: [PATCH 40/86] =?UTF-8?q?update=20:=20application.yaml=EC=97=90=20?= =?UTF-8?q?@Profile('default')=20=ED=99=9C=EC=84=B1=ED=99=94=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 8d5796363a..13f3c356ca 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -1,3 +1,7 @@ +spring: + profiles: + active: default + file: proj-dir: /Users/hongjiin/Documents/Programmers/Dev.course/Mission#1/Fork/springboot-basic/ names: From f86d4e4e8266cf35e76af83bf80a7e14af117b14 Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Thu, 19 Oct 2023 20:51:14 +0900 Subject: [PATCH 41/86] =?UTF-8?q?update=20:=20FileVoucherRepository=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=EC=97=90=EC=84=9C=20TestVoucherServ?= =?UTF-8?q?ice=EC=99=80=20TestFileVoucherRepository=EC=97=90=20=EB=8C=80?= =?UTF-8?q?=ED=95=B4=20@Component=20=EC=B6=94=EA=B0=80,=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=EC=9E=90=20=EC=9E=90=EB=8F=99=20=EC=A3=BC=EC=9E=85=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD,=20=ED=95=84=EC=9A=94=EC=97=86=EB=8A=94=20?= =?UTF-8?q?=ED=95=84=EB=93=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/FileVoucherRepository.java | 1 - .../repository/FileVoucherRepositoryTest.java | 53 +++++-------------- 2 files changed, 13 insertions(+), 41 deletions(-) diff --git a/src/main/java/org/programmers/springboot/basic/domain/voucher/repository/FileVoucherRepository.java b/src/main/java/org/programmers/springboot/basic/domain/voucher/repository/FileVoucherRepository.java index c93e48e84e..38ac025249 100644 --- a/src/main/java/org/programmers/springboot/basic/domain/voucher/repository/FileVoucherRepository.java +++ b/src/main/java/org/programmers/springboot/basic/domain/voucher/repository/FileVoucherRepository.java @@ -65,7 +65,6 @@ private List read() { while ((line = reader.readLine()) != null) { String[] token = line.split(AppConstants.CSV_SEPARATOR); - //if (token.length < 3) return voucherList; CsvVoucherDto voucherDto = voucherMapper.deserialize(token); Voucher voucher = voucherMapper.mapToVoucher(voucherDto); voucherList.add(voucher); diff --git a/src/test/java/org/programmers/springboot/basic/voucher/repository/FileVoucherRepositoryTest.java b/src/test/java/org/programmers/springboot/basic/voucher/repository/FileVoucherRepositoryTest.java index 5b9aa3bcf5..eaa145b6cc 100644 --- a/src/test/java/org/programmers/springboot/basic/voucher/repository/FileVoucherRepositoryTest.java +++ b/src/test/java/org/programmers/springboot/basic/voucher/repository/FileVoucherRepositoryTest.java @@ -28,6 +28,8 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Component; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit.jupiter.SpringExtension; @@ -67,39 +69,19 @@ @SpringBootTest public class FileVoucherRepositoryTest { - private final CSVFileManager csvFileManager; - private final VoucherMapper voucherMapper; - private final FileProperties fileProperties; - private final Map validateHandlers; + private final TestFileVoucherRepository testFileVoucherRepository; + private final TestVoucherService testVoucherService; @Autowired - public FileVoucherRepositoryTest(CSVFileManager csvFileManager, VoucherMapper voucherMapper, FileProperties fileProperties, Map validateHandlers) { - this.csvFileManager = csvFileManager; - this.voucherMapper = voucherMapper; - this.fileProperties = fileProperties; - this.validateHandlers = validateHandlers; - } - - private String getTestFilePath() { - - String folderPath = this.fileProperties.getUserDir(); - String fileName = this.fileProperties.getNames().get("voucher").getFileName(); - String resourcePath = this.fileProperties.getResources().getPath(); - String filePath = folderPath + resourcePath + fileName; - - if (AppConfig.isRunningFromJar()) { - folderPath = this.fileProperties.getProjDir(); - resourcePath = this.fileProperties.getResources().getJar(); - filePath = folderPath + resourcePath + fileName; - } - - return filePath; + public FileVoucherRepositoryTest(TestFileVoucherRepository testFileVoucherRepository, TestVoucherService testVoucherService) { + this.testFileVoucherRepository = testFileVoucherRepository; + this.testVoucherService = testVoucherService; } @AfterEach public void after() { try { - String filePath = getTestFilePath(); + String filePath = testFileVoucherRepository.getTestFilePath(); Path path = Path.of(filePath); if (Files.exists(path)) Files.write(path, new byte[0], StandardOpenOption.TRUNCATE_EXISTING); } catch (IOException e) { @@ -108,17 +90,18 @@ public void after() { } + @Component static class TestVoucherService extends VoucherService { private final VoucherRepository voucherRepository; private final VoucherMapper voucherMapper; private final Map validateHandlers; - public TestVoucherService(Map validateHandlers, VoucherMapper voucherMapper, CSVFileManager csvFileManager, VoucherRepository voucherRepository, FileProperties fileProperties) { + public TestVoucherService(Map validateHandlers, VoucherMapper voucherMapper, VoucherRepository voucherRepository) { super(validateHandlers, voucherMapper, voucherRepository); this.validateHandlers = validateHandlers; this.voucherMapper = voucherMapper; - this.voucherRepository = new TestFileVoucherRepository(csvFileManager, voucherMapper, fileProperties); + this.voucherRepository = voucherRepository; } @Override @@ -138,6 +121,8 @@ public void create(VoucherRequestDto voucherRequestDto) { } } + @Component + @Primary static class TestFileVoucherRepository extends FileVoucherRepository { private final File FILE; @@ -237,10 +222,6 @@ private List getVoucherFromFile() { @DisplayName("파일 쓰기에 대한 동시성 제어 성공 테스트") public void successConcurrencyControlAboutFileWrite() throws InterruptedException { - final TestFileVoucherRepository testFileVoucherRepository = new TestFileVoucherRepository( - this.csvFileManager, this.voucherMapper, this.fileProperties - ); - Voucher voucher1 = new PercentDiscountVoucher(UUID.randomUUID(), 10L, VoucherType.PERCENT); Voucher voucher2 = new PercentDiscountVoucher(UUID.randomUUID(), 50L, VoucherType.PERCENT); Voucher voucher3 = new PercentDiscountVoucher(UUID.randomUUID(), 60L, VoucherType.PERCENT); @@ -276,14 +257,6 @@ public void testConcurrency() throws InterruptedException { int threadCount = 20; int iterations = 100; - final TestFileVoucherRepository testFileVoucherRepository = new TestFileVoucherRepository( - this.csvFileManager, this.voucherMapper, this.fileProperties - ); - - final TestVoucherService testVoucherService = new TestVoucherService( - this.validateHandlers, this.voucherMapper, this.csvFileManager, testFileVoucherRepository, this.fileProperties - ); - CountDownLatch latch = new CountDownLatch(threadCount); ExecutorService executorService = Executors.newFixedThreadPool(threadCount); From 441453030a4b2692706436340049a2e2441d8148 Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Fri, 20 Oct 2023 21:42:59 +0900 Subject: [PATCH 42/86] =?UTF-8?q?update=20:=20.jar=EB=A1=9C=20=EC=8B=A4?= =?UTF-8?q?=ED=96=89=ED=96=88=EC=9D=84=20=EB=95=8C,=20arg=EB=A1=9C=20path?= =?UTF-8?q?=EB=A5=BC=20=EC=A0=84=EB=8B=AC=ED=95=B4=20yaml=EC=97=90=20?= =?UTF-8?q?=EC=A2=85=EC=86=8D=EB=90=98=EC=A7=80=20=EC=95=8A=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EB=B3=80=EA=B2=BD=ED=95=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/CustomerRepository.java | 17 +++++++---- .../repository/FileVoucherRepository.java | 21 ++++++++------ .../basic/util/manager/CommandManager.java | 12 ++++++++ .../util/properties/ExternalProperties.java | 28 +++++++++++++++++++ src/main/resources/application.yaml | 4 +++ 5 files changed, 68 insertions(+), 14 deletions(-) create mode 100644 src/main/java/org/programmers/springboot/basic/util/properties/ExternalProperties.java diff --git a/src/main/java/org/programmers/springboot/basic/domain/customer/repository/CustomerRepository.java b/src/main/java/org/programmers/springboot/basic/domain/customer/repository/CustomerRepository.java index af2cb0cb7c..1b612618bf 100644 --- a/src/main/java/org/programmers/springboot/basic/domain/customer/repository/CustomerRepository.java +++ b/src/main/java/org/programmers/springboot/basic/domain/customer/repository/CustomerRepository.java @@ -8,6 +8,7 @@ import org.programmers.springboot.basic.domain.customer.mapper.CustomerMapper; import org.programmers.springboot.basic.util.exception.CSVFileIOFailureException; import org.programmers.springboot.basic.util.manager.CSVFileManager; +import org.programmers.springboot.basic.util.properties.ExternalProperties; import org.programmers.springboot.basic.util.properties.FileProperties; import org.springframework.stereotype.Repository; @@ -22,17 +23,21 @@ @Repository public class CustomerRepository { - private final File FILE; private final CSVFileManager csvFileManager; private final CustomerMapper customerMapper; private final FileProperties fileProperties; + private final ExternalProperties externalProperties; - public CustomerRepository(CSVFileManager csvFileManager, CustomerMapper customerMapper, FileProperties fileProperties) { + public CustomerRepository(CSVFileManager csvFileManager, CustomerMapper customerMapper, FileProperties fileProperties, ExternalProperties externalProperties) { this.csvFileManager = csvFileManager; this.customerMapper = customerMapper; this.fileProperties = fileProperties; + this.externalProperties = externalProperties; + } + + public File getFILE() { String filePath = getFilePath(); - FILE = new File(filePath); + return new File(filePath); } public List findBlackList() { @@ -45,7 +50,7 @@ private List getBlackListFromFile() { String line; try { - BufferedReader reader = this.csvFileManager.getBufferedReader(FILE); + BufferedReader reader = this.csvFileManager.getBufferedReader(getFILE()); while ((line = reader.readLine()) != null) { String[] token = line.split(AppConstants.CSV_SEPARATOR); @@ -70,8 +75,8 @@ private String getFilePath() { String filePath = folderPath + resourcePath + fileName; if (AppConfig.isRunningFromJar()) { - folderPath = this.fileProperties.getProjDir(); - resourcePath = this.fileProperties.getResources().getJar(); + folderPath = externalProperties.getProjDir(); + resourcePath = externalProperties.getResourceDir(); filePath = folderPath + resourcePath + fileName; } diff --git a/src/main/java/org/programmers/springboot/basic/domain/voucher/repository/FileVoucherRepository.java b/src/main/java/org/programmers/springboot/basic/domain/voucher/repository/FileVoucherRepository.java index 38ac025249..40a942ef39 100644 --- a/src/main/java/org/programmers/springboot/basic/domain/voucher/repository/FileVoucherRepository.java +++ b/src/main/java/org/programmers/springboot/basic/domain/voucher/repository/FileVoucherRepository.java @@ -8,6 +8,7 @@ import org.programmers.springboot.basic.domain.voucher.mapper.VoucherMapper; import org.programmers.springboot.basic.util.exception.CSVFileIOFailureException; import org.programmers.springboot.basic.util.manager.CSVFileManager; +import org.programmers.springboot.basic.util.properties.ExternalProperties; import org.programmers.springboot.basic.util.properties.FileProperties; import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Repository; @@ -21,18 +22,17 @@ @Repository public class FileVoucherRepository implements VoucherRepository { - private final File FILE; private final CSVFileManager csvFileManager; private final VoucherMapper voucherMapper; private final FileProperties fileProperties; + private final ExternalProperties externalProperties; private final ReentrantLock lock = new ReentrantLock(); - public FileVoucherRepository(CSVFileManager csvFileManager, VoucherMapper voucherMapper, FileProperties fileProperties) { + public FileVoucherRepository(CSVFileManager csvFileManager, VoucherMapper voucherMapper, FileProperties fileProperties, ExternalProperties externalProperties) { this.csvFileManager = csvFileManager; this.voucherMapper = voucherMapper; this.fileProperties = fileProperties; - String filePath = getFilePath(); - FILE = new File(filePath); + this.externalProperties = externalProperties; } @Override @@ -55,13 +55,18 @@ public List findAll() { } } + public File getFILE() { + String filePath = getFilePath(); + return new File(filePath); + } + private List read() { List voucherList = new ArrayList<>(); String line; try { - BufferedReader reader = this.csvFileManager.getBufferedReader(FILE); + BufferedReader reader = this.csvFileManager.getBufferedReader(getFILE()); while ((line = reader.readLine()) != null) { String[] token = line.split(AppConstants.CSV_SEPARATOR); @@ -83,7 +88,7 @@ private List read() { private void write(Voucher voucher) { try { - BufferedWriter writer = this.csvFileManager.getBufferedWriter(FILE); + BufferedWriter writer = this.csvFileManager.getBufferedWriter(getFILE()); String serializer = this.voucherMapper.serialize(voucher); writer.write(serializer); @@ -105,8 +110,8 @@ private String getFilePath() { String filePath = folderPath + resourcePath + fileName; if (AppConfig.isRunningFromJar()) { - folderPath = this.fileProperties.getProjDir(); - resourcePath = this.fileProperties.getResources().getJar(); + folderPath = this.externalProperties.getProjDir(); + resourcePath = this.externalProperties.getResourceDir(); filePath = folderPath + resourcePath + fileName; } diff --git a/src/main/java/org/programmers/springboot/basic/util/manager/CommandManager.java b/src/main/java/org/programmers/springboot/basic/util/manager/CommandManager.java index 43bf7646ed..c0d3648dda 100644 --- a/src/main/java/org/programmers/springboot/basic/util/manager/CommandManager.java +++ b/src/main/java/org/programmers/springboot/basic/util/manager/CommandManager.java @@ -6,6 +6,7 @@ import org.programmers.springboot.basic.util.CommandType; import org.programmers.springboot.basic.util.exception.CommandNotFoundException; import org.programmers.springboot.basic.util.exception.ConsoleIOFailureException; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.stereotype.Component; @@ -17,6 +18,7 @@ public class CommandManager implements CommandLineRunner { private final VoucherController voucherController; private final CustomerController customerController; + @Autowired public CommandManager(ConsoleIOManager consoleIOManager, VoucherController voucherController, CustomerController customerController) { this.consoleIOManager = consoleIOManager; this.voucherController = voucherController; @@ -26,6 +28,8 @@ public CommandManager(ConsoleIOManager consoleIOManager, VoucherController vouch @Override public void run(String... args) { + bindArgument(args); + boolean flag = true; try { @@ -57,6 +61,14 @@ public void run(String... args) { } } + private void bindArgument(String... args) { + if (args.length >= 2) { + System.setProperty("spring.config.name", "application"); + System.setProperty("external.proj-dir", args[0]); + System.setProperty("external.resource-dir", args[1]); + } + } + private void createHandler() { this.voucherController.create(); } diff --git a/src/main/java/org/programmers/springboot/basic/util/properties/ExternalProperties.java b/src/main/java/org/programmers/springboot/basic/util/properties/ExternalProperties.java new file mode 100644 index 0000000000..d6eb5c75d7 --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/util/properties/ExternalProperties.java @@ -0,0 +1,28 @@ +package org.programmers.springboot.basic.util.properties; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +@Component +@ConfigurationProperties(prefix = "external") +public class ExternalProperties { + + private String projDir; + private String resourceDir; + + public String getProjDir() { + return projDir; + } + + public void setProjDir(String projDir) { + this.projDir = projDir; + } + + public String getResourceDir() { + return resourceDir; + } + + public void setResourceDir(String resourceDir) { + this.resourceDir = resourceDir; + } +} diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 13f3c356ca..229daba457 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -16,3 +16,7 @@ file: path: /src/main/resources/ test-path: /src/test/resources/ jar: /build/resources/main/ + +external: + proj-dir: "" + resource-dir: "" From 268873def095f09663ee2595656992e39efe1cda Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Fri, 20 Oct 2023 21:42:59 +0900 Subject: [PATCH 43/86] =?UTF-8?q?update=20:=20=ED=8C=A8=ED=82=A4=EC=A7=95?= =?UTF-8?q?=EB=90=9C=20.jar=EB=A1=9C=20=EC=8B=A4=ED=96=89=ED=96=88?= =?UTF-8?q?=EC=9D=84=20=EB=95=8C,=20arg=EB=A1=9C=20path=EB=A5=BC=20?= =?UTF-8?q?=EC=A0=84=EB=8B=AC=ED=95=B4=20yaml=EC=97=90=20=EC=A2=85?= =?UTF-8?q?=EC=86=8D=EB=90=98=EC=A7=80=20=EC=95=8A=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=ED=95=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/CustomerRepository.java | 17 +++++++---- .../repository/FileVoucherRepository.java | 21 ++++++++------ .../basic/util/manager/CommandManager.java | 12 ++++++++ .../util/properties/ExternalProperties.java | 28 +++++++++++++++++++ src/main/resources/application.yaml | 4 +++ 5 files changed, 68 insertions(+), 14 deletions(-) create mode 100644 src/main/java/org/programmers/springboot/basic/util/properties/ExternalProperties.java diff --git a/src/main/java/org/programmers/springboot/basic/domain/customer/repository/CustomerRepository.java b/src/main/java/org/programmers/springboot/basic/domain/customer/repository/CustomerRepository.java index af2cb0cb7c..1b612618bf 100644 --- a/src/main/java/org/programmers/springboot/basic/domain/customer/repository/CustomerRepository.java +++ b/src/main/java/org/programmers/springboot/basic/domain/customer/repository/CustomerRepository.java @@ -8,6 +8,7 @@ import org.programmers.springboot.basic.domain.customer.mapper.CustomerMapper; import org.programmers.springboot.basic.util.exception.CSVFileIOFailureException; import org.programmers.springboot.basic.util.manager.CSVFileManager; +import org.programmers.springboot.basic.util.properties.ExternalProperties; import org.programmers.springboot.basic.util.properties.FileProperties; import org.springframework.stereotype.Repository; @@ -22,17 +23,21 @@ @Repository public class CustomerRepository { - private final File FILE; private final CSVFileManager csvFileManager; private final CustomerMapper customerMapper; private final FileProperties fileProperties; + private final ExternalProperties externalProperties; - public CustomerRepository(CSVFileManager csvFileManager, CustomerMapper customerMapper, FileProperties fileProperties) { + public CustomerRepository(CSVFileManager csvFileManager, CustomerMapper customerMapper, FileProperties fileProperties, ExternalProperties externalProperties) { this.csvFileManager = csvFileManager; this.customerMapper = customerMapper; this.fileProperties = fileProperties; + this.externalProperties = externalProperties; + } + + public File getFILE() { String filePath = getFilePath(); - FILE = new File(filePath); + return new File(filePath); } public List findBlackList() { @@ -45,7 +50,7 @@ private List getBlackListFromFile() { String line; try { - BufferedReader reader = this.csvFileManager.getBufferedReader(FILE); + BufferedReader reader = this.csvFileManager.getBufferedReader(getFILE()); while ((line = reader.readLine()) != null) { String[] token = line.split(AppConstants.CSV_SEPARATOR); @@ -70,8 +75,8 @@ private String getFilePath() { String filePath = folderPath + resourcePath + fileName; if (AppConfig.isRunningFromJar()) { - folderPath = this.fileProperties.getProjDir(); - resourcePath = this.fileProperties.getResources().getJar(); + folderPath = externalProperties.getProjDir(); + resourcePath = externalProperties.getResourceDir(); filePath = folderPath + resourcePath + fileName; } diff --git a/src/main/java/org/programmers/springboot/basic/domain/voucher/repository/FileVoucherRepository.java b/src/main/java/org/programmers/springboot/basic/domain/voucher/repository/FileVoucherRepository.java index 38ac025249..40a942ef39 100644 --- a/src/main/java/org/programmers/springboot/basic/domain/voucher/repository/FileVoucherRepository.java +++ b/src/main/java/org/programmers/springboot/basic/domain/voucher/repository/FileVoucherRepository.java @@ -8,6 +8,7 @@ import org.programmers.springboot.basic.domain.voucher.mapper.VoucherMapper; import org.programmers.springboot.basic.util.exception.CSVFileIOFailureException; import org.programmers.springboot.basic.util.manager.CSVFileManager; +import org.programmers.springboot.basic.util.properties.ExternalProperties; import org.programmers.springboot.basic.util.properties.FileProperties; import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Repository; @@ -21,18 +22,17 @@ @Repository public class FileVoucherRepository implements VoucherRepository { - private final File FILE; private final CSVFileManager csvFileManager; private final VoucherMapper voucherMapper; private final FileProperties fileProperties; + private final ExternalProperties externalProperties; private final ReentrantLock lock = new ReentrantLock(); - public FileVoucherRepository(CSVFileManager csvFileManager, VoucherMapper voucherMapper, FileProperties fileProperties) { + public FileVoucherRepository(CSVFileManager csvFileManager, VoucherMapper voucherMapper, FileProperties fileProperties, ExternalProperties externalProperties) { this.csvFileManager = csvFileManager; this.voucherMapper = voucherMapper; this.fileProperties = fileProperties; - String filePath = getFilePath(); - FILE = new File(filePath); + this.externalProperties = externalProperties; } @Override @@ -55,13 +55,18 @@ public List findAll() { } } + public File getFILE() { + String filePath = getFilePath(); + return new File(filePath); + } + private List read() { List voucherList = new ArrayList<>(); String line; try { - BufferedReader reader = this.csvFileManager.getBufferedReader(FILE); + BufferedReader reader = this.csvFileManager.getBufferedReader(getFILE()); while ((line = reader.readLine()) != null) { String[] token = line.split(AppConstants.CSV_SEPARATOR); @@ -83,7 +88,7 @@ private List read() { private void write(Voucher voucher) { try { - BufferedWriter writer = this.csvFileManager.getBufferedWriter(FILE); + BufferedWriter writer = this.csvFileManager.getBufferedWriter(getFILE()); String serializer = this.voucherMapper.serialize(voucher); writer.write(serializer); @@ -105,8 +110,8 @@ private String getFilePath() { String filePath = folderPath + resourcePath + fileName; if (AppConfig.isRunningFromJar()) { - folderPath = this.fileProperties.getProjDir(); - resourcePath = this.fileProperties.getResources().getJar(); + folderPath = this.externalProperties.getProjDir(); + resourcePath = this.externalProperties.getResourceDir(); filePath = folderPath + resourcePath + fileName; } diff --git a/src/main/java/org/programmers/springboot/basic/util/manager/CommandManager.java b/src/main/java/org/programmers/springboot/basic/util/manager/CommandManager.java index 43bf7646ed..c0d3648dda 100644 --- a/src/main/java/org/programmers/springboot/basic/util/manager/CommandManager.java +++ b/src/main/java/org/programmers/springboot/basic/util/manager/CommandManager.java @@ -6,6 +6,7 @@ import org.programmers.springboot.basic.util.CommandType; import org.programmers.springboot.basic.util.exception.CommandNotFoundException; import org.programmers.springboot.basic.util.exception.ConsoleIOFailureException; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.stereotype.Component; @@ -17,6 +18,7 @@ public class CommandManager implements CommandLineRunner { private final VoucherController voucherController; private final CustomerController customerController; + @Autowired public CommandManager(ConsoleIOManager consoleIOManager, VoucherController voucherController, CustomerController customerController) { this.consoleIOManager = consoleIOManager; this.voucherController = voucherController; @@ -26,6 +28,8 @@ public CommandManager(ConsoleIOManager consoleIOManager, VoucherController vouch @Override public void run(String... args) { + bindArgument(args); + boolean flag = true; try { @@ -57,6 +61,14 @@ public void run(String... args) { } } + private void bindArgument(String... args) { + if (args.length >= 2) { + System.setProperty("spring.config.name", "application"); + System.setProperty("external.proj-dir", args[0]); + System.setProperty("external.resource-dir", args[1]); + } + } + private void createHandler() { this.voucherController.create(); } diff --git a/src/main/java/org/programmers/springboot/basic/util/properties/ExternalProperties.java b/src/main/java/org/programmers/springboot/basic/util/properties/ExternalProperties.java new file mode 100644 index 0000000000..d6eb5c75d7 --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/util/properties/ExternalProperties.java @@ -0,0 +1,28 @@ +package org.programmers.springboot.basic.util.properties; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +@Component +@ConfigurationProperties(prefix = "external") +public class ExternalProperties { + + private String projDir; + private String resourceDir; + + public String getProjDir() { + return projDir; + } + + public void setProjDir(String projDir) { + this.projDir = projDir; + } + + public String getResourceDir() { + return resourceDir; + } + + public void setResourceDir(String resourceDir) { + this.resourceDir = resourceDir; + } +} diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 13f3c356ca..229daba457 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -16,3 +16,7 @@ file: path: /src/main/resources/ test-path: /src/test/resources/ jar: /build/resources/main/ + +external: + proj-dir: "" + resource-dir: "" From 6be15f79825dde4a6cf64cb24b033c5d90283b21 Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Sat, 21 Oct 2023 11:01:02 +0900 Subject: [PATCH 44/86] =?UTF-8?q?update=20:=20application.yaml=EA=B3=BC=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=83=81?= =?UTF-8?q?=EC=97=90=EC=84=9C=20.jar=20=EC=8B=A4=ED=96=89=EC=97=90=20?= =?UTF-8?q?=EB=8C=80=ED=95=B4=20path=EB=A5=BC=20=EC=96=BB=EC=96=B4?= =?UTF-8?q?=EC=98=A4=EB=8A=94=20=EB=82=B4=EC=9A=A9=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../basic/util/properties/FileProperties.java | 19 +--------- src/main/resources/application.yaml | 2 -- .../repository/FileVoucherRepositoryTest.java | 35 ++++++++----------- 3 files changed, 15 insertions(+), 41 deletions(-) diff --git a/src/main/java/org/programmers/springboot/basic/util/properties/FileProperties.java b/src/main/java/org/programmers/springboot/basic/util/properties/FileProperties.java index 1c74a45a6c..f4ece01844 100644 --- a/src/main/java/org/programmers/springboot/basic/util/properties/FileProperties.java +++ b/src/main/java/org/programmers/springboot/basic/util/properties/FileProperties.java @@ -13,12 +13,12 @@ public class FileProperties { private final Map names = new HashMap<>(); private Resources resources; private String userDir; - private String projDir; public Map getNames() { return names; } + public Resources getResources() { return resources; } @@ -35,14 +35,6 @@ public void setUserDir(String userDir) { this.userDir = userDir; } - public String getProjDir() { - return projDir; - } - - public void setProjDir(String projDir) { - this.projDir = projDir; - } - public static class PathProperties { private String fileName; @@ -68,7 +60,6 @@ public void setTestFileName(String testFileName) { public static class Resources { private String path; private String testPath; - private String jar; public String getPath() { return path; @@ -85,13 +76,5 @@ public String getTestPath() { public void setTestPath(String testPath) { this.testPath = testPath; } - - public String getJar() { - return jar; - } - - public void setJar(String jar) { - this.jar = jar; - } } } diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 229daba457..f7eea4ccc2 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -3,7 +3,6 @@ spring: active: default file: - proj-dir: /Users/hongjiin/Documents/Programmers/Dev.course/Mission#1/Fork/springboot-basic/ names: voucher: file-name: voucherRecord.csv @@ -15,7 +14,6 @@ file: resources: path: /src/main/resources/ test-path: /src/test/resources/ - jar: /build/resources/main/ external: proj-dir: "" diff --git a/src/test/java/org/programmers/springboot/basic/voucher/repository/FileVoucherRepositoryTest.java b/src/test/java/org/programmers/springboot/basic/voucher/repository/FileVoucherRepositoryTest.java index eaa145b6cc..6d045a7b9d 100644 --- a/src/test/java/org/programmers/springboot/basic/voucher/repository/FileVoucherRepositoryTest.java +++ b/src/test/java/org/programmers/springboot/basic/voucher/repository/FileVoucherRepositoryTest.java @@ -5,7 +5,6 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.programmers.springboot.basic.AppConstants; -import org.programmers.springboot.basic.config.AppConfig; import org.programmers.springboot.basic.config.VoucherConfig; import org.programmers.springboot.basic.domain.voucher.dto.CsvVoucherDto; import org.programmers.springboot.basic.domain.voucher.dto.VoucherRequestDto; @@ -23,6 +22,7 @@ import org.programmers.springboot.basic.domain.voucher.service.validate.ValidateHandler; import org.programmers.springboot.basic.util.exception.CSVFileIOFailureException; import org.programmers.springboot.basic.util.manager.CSVFileManager; +import org.programmers.springboot.basic.util.properties.ExternalProperties; import org.programmers.springboot.basic.util.properties.FileProperties; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.context.properties.EnableConfigurationProperties; @@ -62,7 +62,8 @@ FileVoucherRepository.class, PercentDiscountVoucherValidator.class, FixedAmountVoucherValidator.class, - FileProperties.class + FileProperties.class, + FileVoucherRepositoryTest.class }) @EnableConfigurationProperties(value = FileProperties.class) @TestPropertySource("classpath:application.yaml") @@ -90,7 +91,7 @@ public void after() { } - @Component + @Component("testVoucherService") static class TestVoucherService extends VoucherService { private final VoucherRepository voucherRepository; @@ -117,11 +118,11 @@ public void create(VoucherRequestDto voucherRequestDto) { handler.validate(discount); Voucher voucher = voucherMapper.mapToVoucher(voucherRequestDto); - this.voucherRepository.save(voucher); + voucherRepository.save(voucher); } } - @Component + @Component("testFileVoucherRepository") @Primary static class TestFileVoucherRepository extends FileVoucherRepository { @@ -132,8 +133,8 @@ static class TestFileVoucherRepository extends FileVoucherRepository { ReentrantLock lock = new ReentrantLock(); - public TestFileVoucherRepository(CSVFileManager csvFileManager, VoucherMapper voucherMapper, FileProperties fileProperties) { - super(csvFileManager, voucherMapper, fileProperties); + public TestFileVoucherRepository(CSVFileManager csvFileManager, VoucherMapper voucherMapper, FileProperties fileProperties, ExternalProperties externalProperties) { + super(csvFileManager, voucherMapper, fileProperties, externalProperties); this.csvFileManager = csvFileManager; this.voucherMapper = voucherMapper; this.fileProperties = fileProperties; @@ -143,18 +144,10 @@ public TestFileVoucherRepository(CSVFileManager csvFileManager, VoucherMapper vo private String getTestFilePath() { - String folderPath = this.fileProperties.getUserDir(); - String fileName = this.fileProperties.getNames().get("voucher").getFileName(); - String resourcePath = this.fileProperties.getResources().getPath(); - String filePath = folderPath + resourcePath + fileName; - - if (AppConfig.isRunningFromJar()) { - folderPath = this.fileProperties.getProjDir(); - resourcePath = this.fileProperties.getResources().getJar(); - filePath = folderPath + resourcePath + fileName; - } - - return filePath; + String folderPath = fileProperties.getUserDir(); + String fileName = fileProperties.getNames().get("voucher").getFileName(); + String resourcePath = fileProperties.getResources().getPath(); + return folderPath + resourcePath + fileName; } @Override @@ -170,7 +163,7 @@ public void save(Voucher voucher) { private void writeVoucherToFile(Voucher voucher) { try { - BufferedWriter writer = this.csvFileManager.getBufferedWriter(FILE); + BufferedWriter writer = csvFileManager.getBufferedWriter(FILE); String serializer = voucherMapper.serialize(voucher); writer.write(serializer); @@ -200,7 +193,7 @@ private List getVoucherFromFile() { String line; try { - BufferedReader reader = this.csvFileManager.getBufferedReader(FILE); + BufferedReader reader = csvFileManager.getBufferedReader(FILE); while ((line = reader.readLine()) != null) { String[] token = line.split(AppConstants.CSV_SEPARATOR); From 3001cfd327c01a9a3f3cd7d8cab3bdc9959c61a1 Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Sat, 21 Oct 2023 23:40:08 +0900 Subject: [PATCH 45/86] =?UTF-8?q?update=20:=20private=20=EC=83=9D=EC=84=B1?= =?UTF-8?q?=EC=9E=90=EB=A5=BC=20=EC=B6=94=EA=B0=80=ED=95=B4=20=EB=B6=88?= =?UTF-8?q?=ED=95=84=EC=9A=94=ED=95=9C=20=ED=81=B4=EB=9E=98=EC=8A=A4=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=EC=9D=84=20=EB=B0=A9=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/org/programmers/springboot/basic/AppConstants.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/org/programmers/springboot/basic/AppConstants.java b/src/main/java/org/programmers/springboot/basic/AppConstants.java index 5ac5f25262..09229a0520 100644 --- a/src/main/java/org/programmers/springboot/basic/AppConstants.java +++ b/src/main/java/org/programmers/springboot/basic/AppConstants.java @@ -2,6 +2,8 @@ public final class AppConstants { + private AppConstants() {} + public static final Long MIN_FIXED_DISCOUNT = 0L; public static final Long MIN_PERCENT_DISCOUNT = 0L; public static final Long MAX_PERCENT_DISCOUNT = 100L; From e9e2d157a82ca999dcbce89620cfc29fb8236a68 Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Sat, 4 Nov 2023 17:30:37 +0900 Subject: [PATCH 46/86] =?UTF-8?q?init:=20=ED=94=84=EB=A1=9C=EC=A0=9D?= =?UTF-8?q?=ED=8A=B8=20=EC=B4=88=EA=B8=B0=20=EC=84=B8=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../springboot/basic/config/AppConfig.java | 12 - .../basic/config/CustomerConfig.java | 24 -- .../domain/customer/dto/CsvCustomerDto.java | 8 - .../customer/mapper/CustomerMapper.java | 32 -- .../domain/voucher/dto/CsvVoucherDto.java | 8 - .../voucher/entity/FixedAmountVoucher.java | 21 -- .../entity/PercentDiscountVoucher.java | 21 -- .../basic/domain/voucher/entity/Voucher.java | 10 - .../repository/FileVoucherRepository.java | 120 -------- .../repository/MemoryVoucherRepository.java | 29 -- .../basic/util/manager/CSVFileManager.java | 25 -- .../SpringbootBasicApplicationTests.java | 13 - .../repository/FileVoucherRepositoryTest.java | 275 ------------------ 13 files changed, 598 deletions(-) delete mode 100644 src/main/java/org/programmers/springboot/basic/config/AppConfig.java delete mode 100644 src/main/java/org/programmers/springboot/basic/config/CustomerConfig.java delete mode 100644 src/main/java/org/programmers/springboot/basic/domain/customer/dto/CsvCustomerDto.java delete mode 100644 src/main/java/org/programmers/springboot/basic/domain/customer/mapper/CustomerMapper.java delete mode 100644 src/main/java/org/programmers/springboot/basic/domain/voucher/dto/CsvVoucherDto.java delete mode 100644 src/main/java/org/programmers/springboot/basic/domain/voucher/entity/FixedAmountVoucher.java delete mode 100644 src/main/java/org/programmers/springboot/basic/domain/voucher/entity/PercentDiscountVoucher.java delete mode 100644 src/main/java/org/programmers/springboot/basic/domain/voucher/entity/Voucher.java delete mode 100644 src/main/java/org/programmers/springboot/basic/domain/voucher/repository/FileVoucherRepository.java delete mode 100644 src/main/java/org/programmers/springboot/basic/domain/voucher/repository/MemoryVoucherRepository.java delete mode 100644 src/main/java/org/programmers/springboot/basic/util/manager/CSVFileManager.java delete mode 100644 src/test/java/org/programmers/springboot/basic/SpringbootBasicApplicationTests.java delete mode 100644 src/test/java/org/programmers/springboot/basic/voucher/repository/FileVoucherRepositoryTest.java diff --git a/src/main/java/org/programmers/springboot/basic/config/AppConfig.java b/src/main/java/org/programmers/springboot/basic/config/AppConfig.java deleted file mode 100644 index 1e38a9969a..0000000000 --- a/src/main/java/org/programmers/springboot/basic/config/AppConfig.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.programmers.springboot.basic.config; - -import org.programmers.springboot.basic.VoucherManagementApplication; - -import java.util.Objects; - -public class AppConfig { - - public static boolean isRunningFromJar() { - return Objects.requireNonNull(VoucherManagementApplication.class.getResource("VoucherManagementApplication.class")).toString().startsWith("jar:"); - } -} diff --git a/src/main/java/org/programmers/springboot/basic/config/CustomerConfig.java b/src/main/java/org/programmers/springboot/basic/config/CustomerConfig.java deleted file mode 100644 index 434c442aa9..0000000000 --- a/src/main/java/org/programmers/springboot/basic/config/CustomerConfig.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.programmers.springboot.basic.config; - -import org.programmers.springboot.basic.domain.customer.dto.CsvCustomerDto; -import org.programmers.springboot.basic.domain.customer.dto.CustomerResponseDto; -import org.programmers.springboot.basic.domain.customer.entity.Customer; -import org.programmers.springboot.basic.domain.customer.entity.CustomerType; -import org.springframework.stereotype.Component; - -import java.util.UUID; - -@Component -public class CustomerConfig { - public Customer getCustomer(CsvCustomerDto csvCustomerDto) { - return new Customer(csvCustomerDto.customerId(), csvCustomerDto.name(), csvCustomerDto.customerType()); - } - - public CustomerResponseDto getCustomerResponseDto(Customer customer) { - return new CustomerResponseDto(customer.getCustomerId(), customer.getName(), customer.getCustomerType()); - } - - public CsvCustomerDto getCsvCustomerDto(UUID customerId, String name, CustomerType customerType) { - return new CsvCustomerDto(customerId, name, customerType); - } -} diff --git a/src/main/java/org/programmers/springboot/basic/domain/customer/dto/CsvCustomerDto.java b/src/main/java/org/programmers/springboot/basic/domain/customer/dto/CsvCustomerDto.java deleted file mode 100644 index aeb4135d98..0000000000 --- a/src/main/java/org/programmers/springboot/basic/domain/customer/dto/CsvCustomerDto.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.programmers.springboot.basic.domain.customer.dto; - -import org.programmers.springboot.basic.domain.customer.entity.CustomerType; - -import java.util.UUID; - -public record CsvCustomerDto(UUID customerId, String name, CustomerType customerType) { -} diff --git a/src/main/java/org/programmers/springboot/basic/domain/customer/mapper/CustomerMapper.java b/src/main/java/org/programmers/springboot/basic/domain/customer/mapper/CustomerMapper.java deleted file mode 100644 index 2f2a05245c..0000000000 --- a/src/main/java/org/programmers/springboot/basic/domain/customer/mapper/CustomerMapper.java +++ /dev/null @@ -1,32 +0,0 @@ -package org.programmers.springboot.basic.domain.customer.mapper; - -import org.programmers.springboot.basic.config.CustomerConfig; -import org.programmers.springboot.basic.domain.customer.dto.CsvCustomerDto; -import org.programmers.springboot.basic.domain.customer.entity.Customer; -import org.programmers.springboot.basic.domain.customer.entity.CustomerType; -import org.springframework.stereotype.Component; - -import java.util.UUID; - -@Component -public class CustomerMapper { - - private final CustomerConfig customerConfig; - - public CustomerMapper(CustomerConfig customerConfig) { - this.customerConfig = customerConfig; - } - - public CsvCustomerDto deserialize(String[] token) { - - UUID customerId = UUID.fromString(token[0]); - String name = token[1]; - CustomerType customerType = CustomerType.valueOf(token[2]); - - return this.customerConfig.getCsvCustomerDto(customerId, name, customerType); - } - - public Customer mapToCustomer(CsvCustomerDto customerDto) { - return this.customerConfig.getCustomer(customerDto); - } -} diff --git a/src/main/java/org/programmers/springboot/basic/domain/voucher/dto/CsvVoucherDto.java b/src/main/java/org/programmers/springboot/basic/domain/voucher/dto/CsvVoucherDto.java deleted file mode 100644 index 3e88c43ab8..0000000000 --- a/src/main/java/org/programmers/springboot/basic/domain/voucher/dto/CsvVoucherDto.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.programmers.springboot.basic.domain.voucher.dto; - -import org.programmers.springboot.basic.domain.voucher.entity.VoucherType; - -import java.util.UUID; - -public record CsvVoucherDto(UUID voucherId, VoucherType voucherType, Long discount) { -} diff --git a/src/main/java/org/programmers/springboot/basic/domain/voucher/entity/FixedAmountVoucher.java b/src/main/java/org/programmers/springboot/basic/domain/voucher/entity/FixedAmountVoucher.java deleted file mode 100644 index 42d82f472c..0000000000 --- a/src/main/java/org/programmers/springboot/basic/domain/voucher/entity/FixedAmountVoucher.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.programmers.springboot.basic.domain.voucher.entity; - -import java.util.UUID; - -public record FixedAmountVoucher(UUID voucherId, Long discount, VoucherType voucherType) implements Voucher { - - @Override - public UUID getVoucherId() { - return this.voucherId; - } - - @Override - public VoucherType getVoucherType() { - return this.voucherType; - } - - @Override - public Long getDiscount() { - return this.discount; - } -} diff --git a/src/main/java/org/programmers/springboot/basic/domain/voucher/entity/PercentDiscountVoucher.java b/src/main/java/org/programmers/springboot/basic/domain/voucher/entity/PercentDiscountVoucher.java deleted file mode 100644 index ee4f74adc9..0000000000 --- a/src/main/java/org/programmers/springboot/basic/domain/voucher/entity/PercentDiscountVoucher.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.programmers.springboot.basic.domain.voucher.entity; - -import java.util.UUID; - -public record PercentDiscountVoucher(UUID voucherId, Long discount, VoucherType voucherType) implements Voucher { - - @Override - public UUID getVoucherId() { - return this.voucherId; - } - - @Override - public VoucherType getVoucherType() { - return this.voucherType; - } - - @Override - public Long getDiscount() { - return this.discount; - } -} diff --git a/src/main/java/org/programmers/springboot/basic/domain/voucher/entity/Voucher.java b/src/main/java/org/programmers/springboot/basic/domain/voucher/entity/Voucher.java deleted file mode 100644 index e1739c1831..0000000000 --- a/src/main/java/org/programmers/springboot/basic/domain/voucher/entity/Voucher.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.programmers.springboot.basic.domain.voucher.entity; - -import java.util.UUID; - -public interface Voucher { - - UUID getVoucherId(); - VoucherType getVoucherType(); - Long getDiscount(); -} diff --git a/src/main/java/org/programmers/springboot/basic/domain/voucher/repository/FileVoucherRepository.java b/src/main/java/org/programmers/springboot/basic/domain/voucher/repository/FileVoucherRepository.java deleted file mode 100644 index 40a942ef39..0000000000 --- a/src/main/java/org/programmers/springboot/basic/domain/voucher/repository/FileVoucherRepository.java +++ /dev/null @@ -1,120 +0,0 @@ -package org.programmers.springboot.basic.domain.voucher.repository; - -import lombok.extern.slf4j.Slf4j; -import org.programmers.springboot.basic.AppConstants; -import org.programmers.springboot.basic.config.AppConfig; -import org.programmers.springboot.basic.domain.voucher.dto.CsvVoucherDto; -import org.programmers.springboot.basic.domain.voucher.entity.Voucher; -import org.programmers.springboot.basic.domain.voucher.mapper.VoucherMapper; -import org.programmers.springboot.basic.util.exception.CSVFileIOFailureException; -import org.programmers.springboot.basic.util.manager.CSVFileManager; -import org.programmers.springboot.basic.util.properties.ExternalProperties; -import org.programmers.springboot.basic.util.properties.FileProperties; -import org.springframework.context.annotation.Profile; -import org.springframework.stereotype.Repository; - -import java.io.*; -import java.util.*; -import java.util.concurrent.locks.ReentrantLock; - -@Slf4j -@Profile("default") -@Repository -public class FileVoucherRepository implements VoucherRepository { - - private final CSVFileManager csvFileManager; - private final VoucherMapper voucherMapper; - private final FileProperties fileProperties; - private final ExternalProperties externalProperties; - private final ReentrantLock lock = new ReentrantLock(); - - public FileVoucherRepository(CSVFileManager csvFileManager, VoucherMapper voucherMapper, FileProperties fileProperties, ExternalProperties externalProperties) { - this.csvFileManager = csvFileManager; - this.voucherMapper = voucherMapper; - this.fileProperties = fileProperties; - this.externalProperties = externalProperties; - } - - @Override - public void save(Voucher voucher) { - lock.lock(); - try { - write(voucher); - } finally { - lock.unlock(); - } - } - - @Override - public List findAll() { - lock.lock(); - try { - return read(); - } finally { - lock.unlock(); - } - } - - public File getFILE() { - String filePath = getFilePath(); - return new File(filePath); - } - - private List read() { - - List voucherList = new ArrayList<>(); - String line; - - try { - BufferedReader reader = this.csvFileManager.getBufferedReader(getFILE()); - - while ((line = reader.readLine()) != null) { - String[] token = line.split(AppConstants.CSV_SEPARATOR); - CsvVoucherDto voucherDto = voucherMapper.deserialize(token); - Voucher voucher = voucherMapper.mapToVoucher(voucherDto); - voucherList.add(voucher); - } - - } catch (FileNotFoundException e) { - throw new CSVFileIOFailureException("Exception Occurred: No matching file found!"); - } catch (IOException e) { - throw new CSVFileIOFailureException("Exception Occurred: Failed to read file!"); - } - - return voucherList; - } - - - private void write(Voucher voucher) { - - try { - BufferedWriter writer = this.csvFileManager.getBufferedWriter(getFILE()); - - String serializer = this.voucherMapper.serialize(voucher); - writer.write(serializer); - writer.newLine(); - writer.flush(); - - } catch (FileNotFoundException e) { - throw new CSVFileIOFailureException("Exception Occurred: No matching file found!"); - } catch (IOException e) { - throw new CSVFileIOFailureException("Exception Occurred: Failed to write file!"); - } - } - - private String getFilePath() { - - String folderPath = this.fileProperties.getUserDir(); - String fileName = this.fileProperties.getNames().get("voucher").getFileName(); - String resourcePath = this.fileProperties.getResources().getPath(); - String filePath = folderPath + resourcePath + fileName; - - if (AppConfig.isRunningFromJar()) { - folderPath = this.externalProperties.getProjDir(); - resourcePath = this.externalProperties.getResourceDir(); - filePath = folderPath + resourcePath + fileName; - } - - return filePath; - } -} diff --git a/src/main/java/org/programmers/springboot/basic/domain/voucher/repository/MemoryVoucherRepository.java b/src/main/java/org/programmers/springboot/basic/domain/voucher/repository/MemoryVoucherRepository.java deleted file mode 100644 index 862e5690a5..0000000000 --- a/src/main/java/org/programmers/springboot/basic/domain/voucher/repository/MemoryVoucherRepository.java +++ /dev/null @@ -1,29 +0,0 @@ -package org.programmers.springboot.basic.domain.voucher.repository; - -import lombok.extern.slf4j.Slf4j; -import org.programmers.springboot.basic.domain.voucher.entity.Voucher; -import org.springframework.context.annotation.Profile; -import org.springframework.stereotype.Repository; - -import java.util.List; -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; - -@Slf4j -@Profile("dev") -@Repository -public class MemoryVoucherRepository implements VoucherRepository { - - private final Map storage = new ConcurrentHashMap<>(); - - @Override - public void save(Voucher voucher) { - this.storage.put(voucher.getVoucherId(), voucher); - } - - @Override - public List findAll() { - return storage.values().stream().toList(); - } -} diff --git a/src/main/java/org/programmers/springboot/basic/util/manager/CSVFileManager.java b/src/main/java/org/programmers/springboot/basic/util/manager/CSVFileManager.java deleted file mode 100644 index 0bccdf44e8..0000000000 --- a/src/main/java/org/programmers/springboot/basic/util/manager/CSVFileManager.java +++ /dev/null @@ -1,25 +0,0 @@ -package org.programmers.springboot.basic.util.manager; - -import org.springframework.stereotype.Component; - -import java.io.*; - -@Component -public class CSVFileManager { - - public BufferedWriter getBufferedWriter(File FILE) throws IOException { - return new BufferedWriter(getFIleWriter(FILE)); - } - - public FileWriter getFIleWriter(File FILE) throws IOException { - return new FileWriter(FILE, true); - } - - public BufferedReader getBufferedReader(File FILE) throws IOException { - return new BufferedReader(getFileReader(FILE)); - } - - public FileReader getFileReader(File FILE) throws IOException { - return new FileReader(FILE); - } -} diff --git a/src/test/java/org/programmers/springboot/basic/SpringbootBasicApplicationTests.java b/src/test/java/org/programmers/springboot/basic/SpringbootBasicApplicationTests.java deleted file mode 100644 index a5d2b8cb59..0000000000 --- a/src/test/java/org/programmers/springboot/basic/SpringbootBasicApplicationTests.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.programmers.springboot.basic; - -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; - -@SpringBootTest -class SpringbootBasicApplicationTests { - - @Test - void contextLoads() { - } - -} diff --git a/src/test/java/org/programmers/springboot/basic/voucher/repository/FileVoucherRepositoryTest.java b/src/test/java/org/programmers/springboot/basic/voucher/repository/FileVoucherRepositoryTest.java deleted file mode 100644 index 6d045a7b9d..0000000000 --- a/src/test/java/org/programmers/springboot/basic/voucher/repository/FileVoucherRepositoryTest.java +++ /dev/null @@ -1,275 +0,0 @@ -package org.programmers.springboot.basic.voucher.repository; - -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.programmers.springboot.basic.AppConstants; -import org.programmers.springboot.basic.config.VoucherConfig; -import org.programmers.springboot.basic.domain.voucher.dto.CsvVoucherDto; -import org.programmers.springboot.basic.domain.voucher.dto.VoucherRequestDto; -import org.programmers.springboot.basic.domain.voucher.entity.FixedAmountVoucher; -import org.programmers.springboot.basic.domain.voucher.entity.PercentDiscountVoucher; -import org.programmers.springboot.basic.domain.voucher.entity.Voucher; -import org.programmers.springboot.basic.domain.voucher.entity.VoucherType; -import org.programmers.springboot.basic.domain.voucher.exception.VoucherValidatorNotFoundException; -import org.programmers.springboot.basic.domain.voucher.mapper.VoucherMapper; -import org.programmers.springboot.basic.domain.voucher.repository.FileVoucherRepository; -import org.programmers.springboot.basic.domain.voucher.repository.VoucherRepository; -import org.programmers.springboot.basic.domain.voucher.service.VoucherService; -import org.programmers.springboot.basic.domain.voucher.service.validate.FixedAmountVoucherValidator; -import org.programmers.springboot.basic.domain.voucher.service.validate.PercentDiscountVoucherValidator; -import org.programmers.springboot.basic.domain.voucher.service.validate.ValidateHandler; -import org.programmers.springboot.basic.util.exception.CSVFileIOFailureException; -import org.programmers.springboot.basic.util.manager.CSVFileManager; -import org.programmers.springboot.basic.util.properties.ExternalProperties; -import org.programmers.springboot.basic.util.properties.FileProperties; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.Primary; -import org.springframework.stereotype.Component; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.TestPropertySource; -import org.springframework.test.context.junit.jupiter.SpringExtension; - -import java.io.*; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.StandardOpenOption; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.locks.ReentrantLock; - -import static org.assertj.core.api.Assertions.assertThat; - -@ExtendWith(SpringExtension.class) -@ComponentScan( - basePackages = {"org.programmers.springboot.basic"}, - basePackageClasses = FileVoucherRepositoryTest.class -) -@ContextConfiguration(classes = { - CSVFileManager.class, - VoucherMapper.class, - VoucherConfig.class, - FileVoucherRepository.class, - PercentDiscountVoucherValidator.class, - FixedAmountVoucherValidator.class, - FileProperties.class, - FileVoucherRepositoryTest.class -}) -@EnableConfigurationProperties(value = FileProperties.class) -@TestPropertySource("classpath:application.yaml") -@SpringBootTest -public class FileVoucherRepositoryTest { - - private final TestFileVoucherRepository testFileVoucherRepository; - private final TestVoucherService testVoucherService; - - @Autowired - public FileVoucherRepositoryTest(TestFileVoucherRepository testFileVoucherRepository, TestVoucherService testVoucherService) { - this.testFileVoucherRepository = testFileVoucherRepository; - this.testVoucherService = testVoucherService; - } - - @AfterEach - public void after() { - try { - String filePath = testFileVoucherRepository.getTestFilePath(); - Path path = Path.of(filePath); - if (Files.exists(path)) Files.write(path, new byte[0], StandardOpenOption.TRUNCATE_EXISTING); - } catch (IOException e) { - e.printStackTrace(); - } - - } - - @Component("testVoucherService") - static class TestVoucherService extends VoucherService { - - private final VoucherRepository voucherRepository; - private final VoucherMapper voucherMapper; - private final Map validateHandlers; - - public TestVoucherService(Map validateHandlers, VoucherMapper voucherMapper, VoucherRepository voucherRepository) { - super(validateHandlers, voucherMapper, voucherRepository); - this.validateHandlers = validateHandlers; - this.voucherMapper = voucherMapper; - this.voucherRepository = voucherRepository; - } - - @Override - public void create(VoucherRequestDto voucherRequestDto) { - Long discount = voucherRequestDto.discount(); - VoucherType voucherType = voucherRequestDto.voucherType(); - - ValidateHandler handler = validateHandlers.get(voucherType); - - if (handler == null) { - throw new VoucherValidatorNotFoundException(); - } - - handler.validate(discount); - Voucher voucher = voucherMapper.mapToVoucher(voucherRequestDto); - voucherRepository.save(voucher); - } - } - - @Component("testFileVoucherRepository") - @Primary - static class TestFileVoucherRepository extends FileVoucherRepository { - - private final File FILE; - private final CSVFileManager csvFileManager; - private final VoucherMapper voucherMapper; - private final FileProperties fileProperties; - - ReentrantLock lock = new ReentrantLock(); - - public TestFileVoucherRepository(CSVFileManager csvFileManager, VoucherMapper voucherMapper, FileProperties fileProperties, ExternalProperties externalProperties) { - super(csvFileManager, voucherMapper, fileProperties, externalProperties); - this.csvFileManager = csvFileManager; - this.voucherMapper = voucherMapper; - this.fileProperties = fileProperties; - String filePath = getTestFilePath(); - FILE = new File(filePath); - } - - private String getTestFilePath() { - - String folderPath = fileProperties.getUserDir(); - String fileName = fileProperties.getNames().get("voucher").getFileName(); - String resourcePath = fileProperties.getResources().getPath(); - return folderPath + resourcePath + fileName; - } - - @Override - public void save(Voucher voucher) { - lock.lock(); - try { - writeVoucherToFile(voucher); - } finally { - lock.unlock(); - } - } - - private void writeVoucherToFile(Voucher voucher) { - - try { - BufferedWriter writer = csvFileManager.getBufferedWriter(FILE); - - String serializer = voucherMapper.serialize(voucher); - writer.write(serializer); - writer.newLine(); - writer.flush(); - - } catch (FileNotFoundException e) { - throw new CSVFileIOFailureException("Exception Occurred: No matching file found!"); - } catch (IOException e) { - throw new CSVFileIOFailureException("Exception Occurred: Failed to save file!"); - } - } - - @Override - public List findAll() { - lock.lock(); - try { - return getVoucherFromFile(); - } finally { - lock.unlock(); - } - } - - private List getVoucherFromFile() { - - List voucherList = new ArrayList<>(); - String line; - - try { - BufferedReader reader = csvFileManager.getBufferedReader(FILE); - - while ((line = reader.readLine()) != null) { - String[] token = line.split(AppConstants.CSV_SEPARATOR); - CsvVoucherDto voucherDto = voucherMapper.deserialize(token); - Voucher voucher = voucherMapper.mapToVoucher(voucherDto); - voucherList.add(voucher); - } - } catch (FileNotFoundException e) { - throw new CSVFileIOFailureException("Exception Occurred: No matching file found!"); - } catch (IOException e) { - throw new CSVFileIOFailureException("Exception Occurred: Failed to read file!"); - } - - return voucherList; - } - } - - @Test - @DisplayName("파일 쓰기에 대한 동시성 제어 성공 테스트") - public void successConcurrencyControlAboutFileWrite() throws InterruptedException { - - Voucher voucher1 = new PercentDiscountVoucher(UUID.randomUUID(), 10L, VoucherType.PERCENT); - Voucher voucher2 = new PercentDiscountVoucher(UUID.randomUUID(), 50L, VoucherType.PERCENT); - Voucher voucher3 = new PercentDiscountVoucher(UUID.randomUUID(), 60L, VoucherType.PERCENT); - Voucher voucher4 = new FixedAmountVoucher(UUID.randomUUID(), 1000L, VoucherType.FIXED); - Voucher voucher5 = new FixedAmountVoucher(UUID.randomUUID(), 1500L, VoucherType.FIXED); - - int threadCount = 5; - ExecutorService executorService = Executors.newFixedThreadPool(threadCount); - - try { - for (int i = 0; i < threadCount; i++) { - executorService.submit(() -> { - testFileVoucherRepository.save(voucher1); - testFileVoucherRepository.save(voucher2); - testFileVoucherRepository.save(voucher3); - testFileVoucherRepository.save(voucher4); - testFileVoucherRepository.save(voucher5); - }); - } - - executorService.shutdown(); - executorService.awaitTermination(10, TimeUnit.SECONDS); - } finally { - executorService.shutdownNow(); - } - - testFileVoucherRepository.findAll().forEach(System.out::println); - } - - @Test - public void testConcurrency() throws InterruptedException { - - int threadCount = 20; - int iterations = 100; - - CountDownLatch latch = new CountDownLatch(threadCount); - - ExecutorService executorService = Executors.newFixedThreadPool(threadCount); - - Runnable task = () -> { - for (int i = 0; i < iterations; i++) { - testVoucherService.create(new VoucherRequestDto(VoucherType.FIXED, 50L)); - } - latch.countDown(); - }; - - for (int i = 0; i < threadCount; i++) { - executorService.execute(task); - } - - latch.await(); - - List vouchers = testFileVoucherRepository.findAll(); - assertThat(iterations * threadCount).isEqualTo(vouchers.size()); - executorService.shutdown(); - } -} - From 6b28f1e2d17a3ddf96f5745738bbbd396c93ca17 Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Sat, 4 Nov 2023 17:32:03 +0900 Subject: [PATCH 47/86] =?UTF-8?q?feat:=20=EB=8D=B0=EC=9D=B4=ED=84=B0?= =?UTF-8?q?=EB=B2=A0=EC=9D=B4=EC=8A=A4=20=EC=97=B0=EA=B2=B0=EC=9D=84=20?= =?UTF-8?q?=EC=9C=84=ED=95=9C=20DataSource,=20Profile=20active=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.yaml | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index f7eea4ccc2..b92ff6ac5c 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -1,20 +1,10 @@ spring: profiles: active: default - -file: - names: - voucher: - file-name: voucherRecord.csv - test-file-name: testVoucherRecord.csv - customer: - file-name: customer_blacklist.csv - test-file-name: test_customer_blacklist.csv - user-dir: ${user.dir} - resources: - path: /src/main/resources/ - test-path: /src/test/resources/ - -external: - proj-dir: "" - resource-dir: "" + datasource: + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://localhost:3306/voucherManagement?serverTimeZone=Asia/Seoul + username: user + password: user1234 + thymeleaf: + prefix: classpath:/templates/ From 45e5dfd6188dc56d5c3d74c11db311abaf00d843 Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Sat, 4 Nov 2023 17:34:49 +0900 Subject: [PATCH 48/86] =?UTF-8?q?feat:=20=ED=98=84=EC=9E=AC=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=EB=90=98=EC=A7=80=20=EC=95=8A=EB=8A=94=20week1,2=20?= =?UTF-8?q?=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=84=B8=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/CustomerController.java | 99 +++++- .../voucher/controller/VoucherController.java | 112 ++++++- .../wallet/controller/WalletController.java | 125 ++++++++ .../springboot/basic/util/CommandType.java | 15 +- .../exception/CommandNotFoundException.java | 2 +- .../basic/util/manager/CommandManager.java | 115 +++++-- .../basic/util/manager/ConsoleIOManager.java | 281 ++++++++++++++++-- src/main/resources/logback.xml | 48 +-- .../resources/application-test.properties | 4 + 9 files changed, 703 insertions(+), 98 deletions(-) create mode 100644 src/main/java/org/programmers/springboot/basic/domain/wallet/controller/WalletController.java create mode 100644 src/test/resources/application-test.properties diff --git a/src/main/java/org/programmers/springboot/basic/domain/customer/controller/CustomerController.java b/src/main/java/org/programmers/springboot/basic/domain/customer/controller/CustomerController.java index 8ae4ca2d61..b4ba9997b8 100644 --- a/src/main/java/org/programmers/springboot/basic/domain/customer/controller/CustomerController.java +++ b/src/main/java/org/programmers/springboot/basic/domain/customer/controller/CustomerController.java @@ -1,8 +1,11 @@ package org.programmers.springboot.basic.domain.customer.controller; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.programmers.springboot.basic.domain.customer.dto.CustomerRequestDto; import org.programmers.springboot.basic.domain.customer.dto.CustomerResponseDto; -import org.programmers.springboot.basic.domain.customer.exception.CustomerNotFoundException; +import org.programmers.springboot.basic.domain.customer.exception.*; +import org.programmers.springboot.basic.domain.customer.mapper.CustomerMapper; import org.programmers.springboot.basic.domain.customer.service.CustomerService; import org.programmers.springboot.basic.util.manager.ConsoleIOManager; import org.springframework.stereotype.Controller; @@ -11,25 +14,105 @@ @Slf4j @Controller +@RequiredArgsConstructor public class CustomerController { private final ConsoleIOManager consoleIOManager; private final CustomerService customerService; + public void create() { - public CustomerController(ConsoleIOManager consoleIOManager, CustomerService customerService) { - this.consoleIOManager = consoleIOManager; - this.customerService = customerService; + String name = null; + String email = null; + + consoleIOManager.printCustomerCreateHandler(); + + try { + name = consoleIOManager.getInput(); + email = consoleIOManager.getInput(); + CustomerRequestDto requestDto = CustomerMapper.INSTANCE.mapToRequestDto(name, email); + customerService.create(requestDto); + } catch (IllegalEmailException e) { + log.warn("Your input is Invalid Email Type: '{}'", email); + } catch (DuplicateEmailException e) { + log.warn("Duplicate email '{}' is already exists", email); + } + } + + public void list() { + + List responseDtos = customerService.findAllCustomer(); + responseDtos.forEach(CustomerResponseDto::printCustomerInfo); } public void blacklist() { - this.consoleIOManager.printBlackListHandler(); + List responseDtos = customerService.findByCustomerIsBlack(); + responseDtos.forEach(CustomerResponseDto::printCustomerInfo); + } + + public void addToBlackList() { + + String input = null; + + consoleIOManager.printAddBlackHandler(); + + try { + input = consoleIOManager.getInput(); + CustomerRequestDto requestDto = CustomerMapper.INSTANCE.mapToRequestDtoWithUUID(input); + customerService.addBlackCustomer(requestDto); + } catch (IllegalArgumentException e) { + log.warn("Your input is Invalid UUID string: {}", input); + } catch (DuplicateBlackCustomerException e) { + log.warn("customer of customerId '{}' is already exists from blacklist", input); + } catch (CustomerNotFoundException e) { + log.warn("No customer of customerId '{}' found", input); + } + } + + public void deleteFromBlackList() { + + String input = null; + + consoleIOManager.printDeleteBlackHandler(); + + try { + input = consoleIOManager.getInput(); + CustomerRequestDto requestDto = CustomerMapper.INSTANCE.mapToRequestDtoWithUUID(input); + customerService.deleteBlackCustomer(requestDto); + } catch (IllegalArgumentException e) { + log.error("Your input is Invalid UUID string: '{}'", input); + } catch (BlackCustomerNotFoundException e) { + log.warn("No customer found from blacklist that matches the customerId '{}'", input); + } catch (CustomerNotFoundException e) { + log.warn("No customer of customerId '{}' found", input); + } + } + + public void delete() { + + String input = null; + + consoleIOManager.printCustomerDeleteHandler(); try { - List responseDtos = customerService.findBlackList(); - consoleIOManager.printBlackList(responseDtos); + input = consoleIOManager.getInput(); + CustomerRequestDto requestDto = CustomerMapper.INSTANCE.mapToRequestDtoWithUUID(input); + customerService.deleteCustomer(requestDto); + } catch (IllegalArgumentException e) { + log.error("Your input is Invalid UUID string: '{}'", input); } catch (CustomerNotFoundException e) { - log.error(e.toString()); + log.warn("No customer of customerId '{}' found", input); + } + } + + public void deleteAll() { + + consoleIOManager.printDeleteAllHandler(); + + String input = consoleIOManager.getInput(); + switch (input) { + case "Y", "y" -> customerService.deleteAll(); + default -> {} } } } diff --git a/src/main/java/org/programmers/springboot/basic/domain/voucher/controller/VoucherController.java b/src/main/java/org/programmers/springboot/basic/domain/voucher/controller/VoucherController.java index 8555b4d312..adaa0b4842 100644 --- a/src/main/java/org/programmers/springboot/basic/domain/voucher/controller/VoucherController.java +++ b/src/main/java/org/programmers/springboot/basic/domain/voucher/controller/VoucherController.java @@ -5,10 +5,13 @@ import org.programmers.springboot.basic.domain.voucher.dto.VoucherRequestDto; import org.programmers.springboot.basic.domain.voucher.dto.VoucherResponseDto; import org.programmers.springboot.basic.domain.voucher.entity.VoucherType; +import org.programmers.springboot.basic.domain.voucher.exception.DuplicateVoucherException; import org.programmers.springboot.basic.domain.voucher.exception.IllegalDiscountException; import org.programmers.springboot.basic.domain.voucher.exception.VoucherNotFoundException; +import org.programmers.springboot.basic.domain.voucher.mapper.VoucherMapper; import org.programmers.springboot.basic.domain.voucher.service.VoucherService; import org.programmers.springboot.basic.util.manager.ConsoleIOManager; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import java.util.List; @@ -20,6 +23,7 @@ public class VoucherController { private final ConsoleIOManager consoleIOManager; private final VoucherService voucherService; + @Autowired public VoucherController(ConsoleIOManager consoleIOManager, VoucherService voucherService) { this.consoleIOManager = consoleIOManager; this.voucherService = voucherService; @@ -30,10 +34,11 @@ public void create() { boolean flag = true; int type = AppConstants.NONE; Long discount = null; + VoucherType voucherType = null; while (flag) { - consoleIOManager.printCreateHandler(); + consoleIOManager.printVoucherCreateHandler(); try { type = consoleIOManager.getInteger(); @@ -43,12 +48,7 @@ public void create() { switch (type) { case AppConstants.FIXED_AMOUNT, AppConstants.PERCENT_DISCOUNT -> { - try { - consoleIOManager.printDiscount(); - discount = consoleIOManager.getLong(); - } catch (NumberFormatException e) { - log.error(e.toString()); - } + discount = requestDiscount(); flag = false; } default -> System.out.println("[System] 잘못된 모드의 접근입니다."); @@ -56,24 +56,106 @@ public void create() { } try { - VoucherType voucherType = VoucherType.valueOfVoucherByType(type); - VoucherRequestDto requestDto = new VoucherRequestDto(voucherType, discount); - this.voucherService.create(requestDto); // <-- IllegalDiscountException + voucherType = VoucherType.valueOf(type); + VoucherRequestDto requestDto = VoucherMapper.INSTANCE.mapToRequestDto(voucherType, discount); + voucherService.create(requestDto); } catch (IllegalDiscountException e) { - log.error(e.toString()); + log.error("your input is Invalid Discount Range '{}'", discount); + } catch (DuplicateVoucherException e) { + log.warn("voucher of voucherType '{}' and discount '{} is already exists", voucherType, discount); } } + private Long requestDiscount() { + + boolean flag = true; + String input = null; + Long discount = null; + + while (flag) { + try { + consoleIOManager.printRequestDiscount(); + input = consoleIOManager.getInput(); + discount = Long.parseLong(input); + flag = false; + } catch (NumberFormatException e) { + log.error("Your input is Invalid Type of Long: '{}", input); + } + } + + return discount; + } + public void list() { - consoleIOManager.printListHandler(); + List responseDtos = voucherService.findAll(); + consoleIOManager.printVoucher(responseDtos); + } + + public void find() { + + consoleIOManager.printFindHandler(); + + String input = consoleIOManager.getInput(); + + try { + VoucherRequestDto requestDto = VoucherMapper.INSTANCE.mapToRequestDtoWithUUID(input); + VoucherResponseDto responseDto = this.voucherService.findById(requestDto); + consoleIOManager.printFoundVoucher(responseDto); + } catch (IllegalArgumentException e) { + log.error("Your input is Invalid UUID string: '{}'", input); + } catch (VoucherNotFoundException e) { + log.warn("No voucher found for voucherId: '{}'", input); + } + } + + public void update() { + + String input = null; + Long discount = null; + + consoleIOManager.printUpdateHandler(); try { - List responseDtos = voucherService.findAll(); - consoleIOManager.printVoucher(responseDtos); + input = consoleIOManager.getInput(); + discount = Long.parseLong(input); + input = consoleIOManager.getInput(); + VoucherRequestDto requestDto = VoucherMapper.INSTANCE.mapToRequestDtoWithIdNDiscount(input, discount); + voucherService.updateVoucher(requestDto); + } catch (NumberFormatException e) { + log.error("Your input is Invalid Type of Long: '{}'", input); + } catch (IllegalArgumentException e) { + log.error("Your input is Invalid UUID string: '{}'", input); } catch (VoucherNotFoundException e) { - log.error(e.toString()); + log.warn("No voucher of voucherId '{}' found", input); } + } + + public void delete() { + + String input = null; + + consoleIOManager.printVoucherDeleteHandler(); + try { + input = consoleIOManager.getInput(); + VoucherRequestDto requestDto = VoucherMapper.INSTANCE.mapToRequestDtoWithUUID(input); + voucherService.deleteVoucher(requestDto); + } catch (IllegalArgumentException e) { + log.error("Your input is Invalid UUID string: '{}'", input); + } catch (VoucherNotFoundException e) { + log.warn("No voucher of voucherId '{}' found", input); + } + + } + + public void deleteAll() { + + consoleIOManager.printDeleteAllHandler(); + + switch (consoleIOManager.getInput()) { + case "Y", "y" -> voucherService.deleteAll(); + default -> {} + } } } diff --git a/src/main/java/org/programmers/springboot/basic/domain/wallet/controller/WalletController.java b/src/main/java/org/programmers/springboot/basic/domain/wallet/controller/WalletController.java new file mode 100644 index 0000000000..6a523caafc --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/domain/wallet/controller/WalletController.java @@ -0,0 +1,125 @@ +package org.programmers.springboot.basic.domain.wallet.controller; + +import lombok.extern.slf4j.Slf4j; +import org.programmers.springboot.basic.domain.customer.dto.CustomerResponseDto; +import org.programmers.springboot.basic.domain.customer.entity.Customer; +import org.programmers.springboot.basic.domain.customer.exception.CustomerNotFoundException; +import org.programmers.springboot.basic.domain.customer.exception.IllegalEmailException; +import org.programmers.springboot.basic.domain.voucher.dto.VoucherResponseDto; +import org.programmers.springboot.basic.domain.voucher.entity.Voucher; +import org.programmers.springboot.basic.domain.voucher.exception.VoucherNotFoundException; +import org.programmers.springboot.basic.domain.wallet.dto.WalletRequestDto; +import org.programmers.springboot.basic.domain.wallet.dto.WalletResponseDto; +import org.programmers.springboot.basic.domain.wallet.exception.DuplicateWalletException; +import org.programmers.springboot.basic.domain.wallet.mapper.WalletMapper; +import org.programmers.springboot.basic.domain.wallet.service.WalletService; +import org.programmers.springboot.basic.util.manager.ConsoleIOManager; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; + +import java.util.List; + +@Slf4j +@Controller +public class WalletController { + + private final ConsoleIOManager consoleIOManager; + private final WalletService walletService; + + @Autowired + public WalletController(ConsoleIOManager consoleIOManager, WalletService walletService) { + this.consoleIOManager = consoleIOManager; + this.walletService = walletService; + } + + public void addWallet() { + + String email = null; + String voucherId = null; + + consoleIOManager.printAssignHandler(); + + try { + email = consoleIOManager.getInput(); + voucherId = consoleIOManager.getInput(); + + WalletRequestDto requestDto = WalletMapper.INSTANCE.mapToRequestDto(email, voucherId); + this.walletService.createWallet(requestDto); + } catch (IllegalArgumentException e) { + log.warn("Your input is Invalid UUID string: '{}'", voucherId); + } catch (IllegalEmailException e) { + log.warn("Your input is Invalid Email Type: '{}'", email); + } catch (DuplicateWalletException e) { + log.warn("email '{}' and voucherId '{}' is already exists in Wallet", email, voucherId); + } catch (CustomerNotFoundException e) { + log.warn("No customer of email '{}' found", email); + } catch (VoucherNotFoundException e) { + log.warn("No voucher of voucherId '{}' found", voucherId); + } + } + + public void findVoucherListByEmail() { + + String email = null; + + consoleIOManager.printListByConsumerHandler(); + + try { + email = consoleIOManager.getInput(); + + WalletRequestDto requestDto = WalletMapper.INSTANCE.mapToRequestDtoWithEmail(email); + List walletResponseDtos = this.walletService.walletListByEmail(requestDto); + List voucherResponseDtos = this.walletService.voucherListFromWallet(walletResponseDtos); + Customer customer = this.walletService.findCustomerByEmail(requestDto); + consoleIOManager.printWalletInfoByConsumer(customer, voucherResponseDtos); + } catch (IllegalEmailException e) { + log.warn("Your input is Invalid Email Type: '{}'", email); + } catch (CustomerNotFoundException e) { + log.warn("No customer of email '{}' found", email); + } + } + + + public void findCustomerListByVoucherId() { + + String input = null; + + consoleIOManager.printListByVoucherHandler(); + + try { + input = consoleIOManager.getInput(); + WalletRequestDto requestDto = WalletMapper.INSTANCE.mapToRequestDtoWithUUID(input); + List walletResponseDtos = this.walletService.walletListByVoucherId(requestDto); + List customerResponseDtos = this.walletService.customerListFromWallet(walletResponseDtos); + Voucher voucher = this.walletService.findVoucherById(requestDto); + consoleIOManager.printWalletInfoByVoucher(voucher, customerResponseDtos); + } catch (IllegalArgumentException e) { + log.warn("Your input is Invalid UUID string: '{}'", input); + } catch (VoucherNotFoundException e) { + log.warn("No voucher of voucherId '{}' found", input); + } + } + + public void removeVoucherFromWallet() { + + String email = null; + String voucherId = null; + + consoleIOManager.printRemoveVoucherFromWalletHandler(); + + try { + email = consoleIOManager.getInput(); + voucherId = consoleIOManager.getInput(); + WalletRequestDto requestDto = WalletMapper.INSTANCE.mapToRequestDto(email, voucherId); + this.walletService.removeVoucherFromWallet(requestDto); + } catch (IllegalArgumentException e) { + log.warn("Your input is Invalid UUID string: '{}'", voucherId); + } catch (IllegalEmailException e) { + log.warn("Your input is Invalid Email Type: '{}'", email); + } catch (CustomerNotFoundException e) { + log.warn("No customer of email '{}' found", email); + } catch (VoucherNotFoundException e) { + log.warn("No voucher of voucherId '{}' found", voucherId); + } + } +} diff --git a/src/main/java/org/programmers/springboot/basic/util/CommandType.java b/src/main/java/org/programmers/springboot/basic/util/CommandType.java index cc3ca18f35..18f057be28 100644 --- a/src/main/java/org/programmers/springboot/basic/util/CommandType.java +++ b/src/main/java/org/programmers/springboot/basic/util/CommandType.java @@ -6,10 +6,23 @@ public enum CommandType { + VOUCHER, + CUSTOMER, CREATE, LIST, + FIND, + UPDATE, BLACKLIST, - EXIT; + ADD_BLACK, + DELETE_BLACK, + WALLET, + REMOVE, + DELETE, + DELETE_ALL, + ASSIGN, + BACK, + EXIT, + ERROR; public static CommandType valueOfCommand(String command) { diff --git a/src/main/java/org/programmers/springboot/basic/util/exception/CommandNotFoundException.java b/src/main/java/org/programmers/springboot/basic/util/exception/CommandNotFoundException.java index c56dcde292..d208cdaa45 100644 --- a/src/main/java/org/programmers/springboot/basic/util/exception/CommandNotFoundException.java +++ b/src/main/java/org/programmers/springboot/basic/util/exception/CommandNotFoundException.java @@ -3,7 +3,7 @@ public class CommandNotFoundException extends RuntimeException { public CommandNotFoundException() { - super("Exception Occurred: No matching commands found!"); + super("No matching commands found!"); } @Override diff --git a/src/main/java/org/programmers/springboot/basic/util/manager/CommandManager.java b/src/main/java/org/programmers/springboot/basic/util/manager/CommandManager.java index c0d3648dda..9d657feab8 100644 --- a/src/main/java/org/programmers/springboot/basic/util/manager/CommandManager.java +++ b/src/main/java/org/programmers/springboot/basic/util/manager/CommandManager.java @@ -3,6 +3,7 @@ import lombok.extern.slf4j.Slf4j; import org.programmers.springboot.basic.domain.customer.controller.CustomerController; import org.programmers.springboot.basic.domain.voucher.controller.VoucherController; +import org.programmers.springboot.basic.domain.wallet.controller.WalletController; import org.programmers.springboot.basic.util.CommandType; import org.programmers.springboot.basic.util.exception.CommandNotFoundException; import org.programmers.springboot.basic.util.exception.ConsoleIOFailureException; @@ -17,19 +18,19 @@ public class CommandManager implements CommandLineRunner { private final ConsoleIOManager consoleIOManager; private final VoucherController voucherController; private final CustomerController customerController; + private final WalletController walletController; @Autowired - public CommandManager(ConsoleIOManager consoleIOManager, VoucherController voucherController, CustomerController customerController) { + public CommandManager(ConsoleIOManager consoleIOManager, VoucherController voucherController, CustomerController customerController, WalletController walletController) { this.consoleIOManager = consoleIOManager; this.voucherController = voucherController; this.customerController = customerController; + this.walletController = walletController; } @Override public void run(String... args) { - bindArgument(args); - boolean flag = true; try { @@ -38,46 +39,106 @@ public void run(String... args) { consoleIOManager.printSystem(); String input = consoleIOManager.getInput(); - CommandType command = CommandType.valueOfCommand(input); + CommandType command = CommandType.ERROR; + try { + command = CommandType.valueOfCommand(input); + log.info("Command '{}' invoked in voucherManagement Program", command); + } catch (CommandNotFoundException e) { + log.warn(e.toString()); + flag = false; + } switch (command) { - case CREATE -> createHandler(); + case VOUCHER -> voucherHandler(); + case CUSTOMER -> customerHandler(); + case EXIT -> { + consoleIOManager.printExit(); + flag = false; + } + case ERROR -> consoleIOManager.printErrCommand(); + } + } + } catch (ConsoleIOFailureException e) { + log.error(e.toString()); + } + } - case LIST -> listHandler(); + private void voucherHandler() { - case BLACKLIST -> blackListHandler(); + boolean flag = true; - case EXIT -> { - consoleIOManager.printExit(); - flag = false; - } + try { + while (flag) { + consoleIOManager.printVoucherSystem(); + + String input = consoleIOManager.getInput(); + CommandType command = CommandType.ERROR; + try { + command = CommandType.valueOfCommand(input); + log.info("Command '{}' invoked in voucherHandler", command); + } catch (CommandNotFoundException e) { + log.warn(e.toString()); + } + + switch (command) { + case CREATE -> voucherController.create(); + case LIST -> voucherController.list(); + case FIND -> voucherController.find(); + case CUSTOMER -> walletController.findCustomerListByVoucherId(); + case UPDATE -> voucherController.update(); + case DELETE -> voucherController.delete(); + case DELETE_ALL -> voucherController.deleteAll(); + case BACK -> { + consoleIOManager.printBackHandler(); + flag = false; + } default -> consoleIOManager.printErrCommand(); } } - } catch (ConsoleIOFailureException | CommandNotFoundException e) { + } catch (ConsoleIOFailureException e) { log.error(e.toString()); } } - private void bindArgument(String... args) { - if (args.length >= 2) { - System.setProperty("spring.config.name", "application"); - System.setProperty("external.proj-dir", args[0]); - System.setProperty("external.resource-dir", args[1]); - } - } + private void customerHandler() { - private void createHandler() { - this.voucherController.create(); - } + boolean flag = true; - private void listHandler() { - this.voucherController.list(); - } + try { + while (flag) { + consoleIOManager.printCustomerSystem(); - private void blackListHandler() { - this.customerController.blacklist(); + String input = consoleIOManager.getInput(); + CommandType command = CommandType.ERROR; + try { + command = CommandType.valueOfCommand(input); + } catch (CommandNotFoundException e) { + log.warn(e.toString()); + } + + switch (command) { + + case CREATE -> customerController.create(); + case LIST -> customerController.list(); + case BLACKLIST -> customerController.blacklist(); + case ASSIGN -> walletController.addWallet(); + case WALLET -> walletController.findVoucherListByEmail(); + case REMOVE -> walletController.removeVoucherFromWallet(); + case DELETE -> customerController.delete(); + case ADD_BLACK -> customerController.addToBlackList(); + case DELETE_BLACK -> customerController.deleteFromBlackList(); + case DELETE_ALL -> customerController.deleteAll(); + case BACK -> { + consoleIOManager.printBackHandler(); + flag = false; + } + default -> consoleIOManager.printErrCommand(); + } + } + } catch (ConsoleIOFailureException e) { + log.error(e.toString()); + } } } diff --git a/src/main/java/org/programmers/springboot/basic/util/manager/ConsoleIOManager.java b/src/main/java/org/programmers/springboot/basic/util/manager/ConsoleIOManager.java index 7b86cf8658..64702fd472 100644 --- a/src/main/java/org/programmers/springboot/basic/util/manager/ConsoleIOManager.java +++ b/src/main/java/org/programmers/springboot/basic/util/manager/ConsoleIOManager.java @@ -1,7 +1,9 @@ package org.programmers.springboot.basic.util.manager; import org.programmers.springboot.basic.domain.customer.dto.CustomerResponseDto; +import org.programmers.springboot.basic.domain.customer.entity.Customer; import org.programmers.springboot.basic.domain.voucher.dto.VoucherResponseDto; +import org.programmers.springboot.basic.domain.voucher.entity.Voucher; import org.programmers.springboot.basic.util.exception.ConsoleIOFailureException; import org.springframework.stereotype.Component; @@ -35,63 +37,292 @@ public Long getLong() { public void printSystem() { - System.out.println("=== Voucher Program ==="); - System.out.println("Type exit to exit the program."); - System.out.println("Type create to a new voucher."); - System.out.println("Type list to list all voucher."); - System.out.println("Type blacklist to list all blacklist."); + String msg = """ + === Voucher Management Program === + Type exit to exit the program. + Type voucher to enter a voucher program. + Type customer to enter a customer program. + """; + System.out.println(msg); } - public void printCreateHandler() { + public void printVoucherSystem() { - System.out.println("=== CREATE ==="); - System.out.println("1. FixedAmountVoucher"); - System.out.println("2. PercentAmountVoucher"); + String msg = """ + === Voucher Program === + Type create to a new voucher. + Type list to list all voucher. + Type find to find a voucher by voucherId. + Type customer to list all customer by voucherId. + Type update to update voucher. + Type delete to delete a voucher by voucherId. + Type delete_all to delete all voucher. + Type back to back voucherManagement program. + """; + System.out.println(msg); } - public void printListHandler() { + public void printCustomerSystem() { - System.out.println("=== LIST ==="); + String msg = """ + === Customer Program === + Type create to a new customer. + Type list to list all customer. + Type blacklist to list all blacklist. + Type assign to add voucher in wallet by email and voucherId. + Type wallet to list all voucher by email. + Type remove to remove voucher from wallet by voucherId. + Type delete to delete voucher. + Type add_black to add customer to blacklist. + Type delete_black to delete customer from blacklist. + Type delete_all to delete all customer. + Type back to back voucherManagement program + """; + System.out.println(msg); } - public void printBlackListHandler() { + public void printVoucherCreateHandler() { - System.out.println("=== BLACKLIST ==="); + String msg = """ + === CREATE === + 1. FixedAmountVoucher + 2. PercentAmountVoucher + + Q. Enter type of NUMBER you want to create: + """; + System.out.println(msg); + } + + public void printFoundVoucher(VoucherResponseDto voucherResponseDto) { + + String msg = """ + === LIST === + """; + System.out.println(msg); + printVoucherInfo(voucherResponseDto); + } + + public void printCustomerCreateHandler() { + + String msg = """ + === CREATE === + Q. Enter name type of STRING you want to create: + Q. Enter email type of STRING you want to create: + """; + System.out.println(msg); + } + + public void printFindHandler() { + + String msg = """ + === FIND === + Q. Enter voucherId type of UUID you want to find: + """; + System.out.println(msg); + } + + public void printUpdateHandler() { + + String msg = """ + === UPDATE === + Q. Enter voucherId type of UUID you want to update: + Q. Enter discount type of NUMBER you want to update: + """; + System.out.println(msg); + } + + public void printVoucherDeleteHandler() { + + String msg = """ + === DELETE === + Q. Enter voucherId type of UUID you want to delete: + """; + System.out.println(msg); + } + + public void printCustomerDeleteHandler() { + + String msg = """ + === DELETE === + Q. Enter customerId type of UUID you want to delete: + """; + System.out.println(msg); + } + + public void printDeleteAllHandler() { + String msg = """ + === DELETE ALL === + Q, Enter Y if you really want to delete all info (or not: any else) + """; + System.out.println(msg); + } + + public void printAddBlackHandler() { + + String msg = """ + === ADD BLACKLIST === + Q. Enter customerId type of UUID you want to add blacklist: + """; + System.out.println(msg); + } + + public void printDeleteBlackHandler() { + + String msg = """ + === DELETE BLACKLIST === + Q. Enter customerId type of UUID you want to delete from blacklist: + """; + System.out.println(msg); + } + + public void printListByConsumerHandler() { + + String msg = """ + === Voucher LIST ABOUT Email === + Q. Enter email type of STRING you want to list vouchers owned by the customer: + """; + System.out.println(msg); + } + + public void printListByVoucherHandler() { + + String msg = """ + === Customer LIST ABOUT VoucherId === + Q. Enter voucherId type of UUID you want to list customers grouped by the voucher: + """; + System.out.println(msg); + } + + public void printRemoveVoucherFromWalletHandler() { + + String msg = """ + === REMOVE VOUCHER FROM WALLET === + Q. Enter email and voucherId (type of UUID) you want to remove voucher from wallet: + """; + System.out.println(msg); + } + + public void printAssignHandler() { + + String msg = """ + === ASSIGN === + Q. Enter email type of STRING you want to assign: + Q. Enter voucherId type of UUID you want to assign from wallet: + """; + System.out.println(msg); } public void printVoucher(List responseDtos) { + String msg = """ + === Voucher List === + """; + System.out.println(msg); responseDtos.forEach(this::printVoucherInfo); } + public void printCustomer(List responseDtos) { + String msg = """ + === Customer List === + """; + System.out.println(msg); + responseDtos.forEach(this::printCustomerInfo); + } + public void printBlackList(List responseDtos) { + String msg = """ + === BlackList === + """; + System.out.println(msg); responseDtos.forEach(this::printBlackListInfo); } public void printVoucherInfo(VoucherResponseDto responseDto) { - System.out.println("voucherId: " + responseDto.voucherId()); - System.out.println("voucherType: " + responseDto.voucherType()); - System.out.println("discount: " + responseDto.discount()); - System.out.println("---------------------------------\n"); + String msg = String.format(""" + voucherId: %s + voucherType: %s + discount: %d%% + --------------------------------- + """, responseDto.getVoucherId().toString(), responseDto.getVoucherType(), responseDto.getDiscount()); + System.out.println(msg); + } + + public void printCustomerInfo(CustomerResponseDto responseDto) { + + String msg = String.format(""" + customerId: %s + email: %s + name: %s + customerType: %s + --------------------------------- + """, responseDto.getCustomerId().toString(), responseDto.getEmail(), responseDto.getName(), responseDto.getCustomerType()); + System.out.println(msg); } public void printBlackListInfo(CustomerResponseDto responseDto) { - System.out.println("customerId: " + responseDto.customerId()); - System.out.println("name: " + responseDto.name()); - System.out.println("customerType: " + responseDto.customerType()); - System.out.println("---------------------------------\n"); + String msg = String.format(""" + customerId: %s + email: %s + name: %s + --------------------------------- + """, responseDto.getCustomerId().toString(), responseDto.getEmail(), responseDto.getName()); + System.out.println(msg); + } + + public void printWalletInfoByConsumer(Customer customer, List responseDtos) { + + String msg = String.format(""" + === customer Info === + customerId: %s + email: %s + name: %s + customerType: %s + ========================================== + """, customer.getCustomerId().toString(), customer.getEmail(), customer.getName(), customer.getCustomerType()); + System.out.println(msg); + printVoucher(responseDtos); + } + + public void printWalletInfoByVoucher(Voucher voucher, List responseDtos) { + + String msg = String.format(""" + === voucher Info === + voucherId: %s + voucherType: %s + discount: %d%% + ========================================== + """, voucher.getVoucherId().toString(), voucher.getVoucherType(), voucher.getDiscount()); + System.out.println(msg); + printCustomer(responseDtos); + } + + public void printBackHandler() { + + String msg = """ + [System] VoucherManagement 메뉴로 돌아갑니다. + """; + System.out.println(msg); } - public void printDiscount() { - System.out.println("Q. Discount를 입력하세요."); + public void printRequestDiscount() { + String msg = """ + Q. Enter discount type of NUMBER you want to create. + """; + System.out.println(msg); } public void printExit() { - System.out.println("[System] 시스템을 종료합니다."); + String msg = """ + [System] 시스템을 종료합니다. + """; + System.out.println(msg); } public void printErrCommand() { - System.out.println("[System] 잘못된 명령의 접근입니다."); + String msg = """ + [System] 잘못된 명령의 접근입니다. + """; + System.out.println(msg); } } diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml index e262509779..e6c290ac73 100644 --- a/src/main/resources/logback.xml +++ b/src/main/resources/logback.xml @@ -5,29 +5,37 @@ converterClass="org.springframework.boot.logging.logback.ColorConverter"/> - + + + INFO + ACCEPT + DENY + + + ${CONSOLE_LOG} + + - logs/error.log - false - - logs/error-%d{yyyy-MM-dd}.log - + name="WARN_STDOUT" + class="ch.qos.logback.core.ConsoleAppender"> + + WARN + ACCEPT + DENY + - ${LOG_FILE_PATTERN} + ${CONSOLE_LOG} ERROR @@ -35,16 +43,14 @@ DENY - ${LOG_PATTERN} + ${CONSOLE_LOG} - - - - - - + + + + diff --git a/src/test/resources/application-test.properties b/src/test/resources/application-test.properties new file mode 100644 index 0000000000..677ab0d6cb --- /dev/null +++ b/src/test/resources/application-test.properties @@ -0,0 +1,4 @@ +spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver +spring.datasource.url=jdbc:mysql://localhost:3306/voucherManagement?serverTimeZone=Asia/Seoul +spring.datasource.username=user +spring.datasource.password=user1234 From 20ebe219f429de66a4dd33d16589849cbc05c8d2 Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Sat, 4 Nov 2023 17:35:19 +0900 Subject: [PATCH 49/86] =?UTF-8?q?feat:=20=EC=9D=98=EC=A1=B4=EC=84=B1=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 733468d565..bfd93208fe 100644 --- a/build.gradle +++ b/build.gradle @@ -17,8 +17,15 @@ repositories { dependencies { implementation 'org.springframework.boot:spring-boot-starter' - compileOnly 'org.projectlombok:lombok' + implementation 'org.springframework.boot:spring-boot-starter-data-jdbc' + implementation 'org.springframework.boot:spring-boot-starter-aop' + implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' + implementation 'com.mysql:mysql-connector-j:8.0.32' + implementation 'org.projectlombok:lombok' + implementation 'org.mapstruct:mapstruct:1.4.2.Final' annotationProcessor 'org.projectlombok:lombok' + annotationProcessor 'org.mapstruct:mapstruct-processor:1.4.2.Final' annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor' testImplementation 'org.assertj:assertj-core:3.24.2' testImplementation 'org.springframework.boot:spring-boot-starter-test' From 854a5aeef4a04a3235e6486ae1a66f58a28196ef Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Sat, 4 Nov 2023 17:37:28 +0900 Subject: [PATCH 50/86] =?UTF-8?q?feat:=20=EB=A9=94=EC=9D=B8=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20(home.html)=20=EA=B3=BC=20=EC=97=90?= =?UTF-8?q?=EB=9F=AC=ED=8E=98=EC=9D=B4=EC=A7=80=20(error.html)=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/templates/error.html | 20 +++++++++ src/main/resources/templates/home.html | 55 +++++++++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 src/main/resources/templates/error.html create mode 100644 src/main/resources/templates/home.html diff --git a/src/main/resources/templates/error.html b/src/main/resources/templates/error.html new file mode 100644 index 0000000000..807a657d46 --- /dev/null +++ b/src/main/resources/templates/error.html @@ -0,0 +1,20 @@ + + + + + Error Occurred + + + +
+

Error Occurred!

+

+
+ +
+ +
+ + + + diff --git a/src/main/resources/templates/home.html b/src/main/resources/templates/home.html new file mode 100644 index 0000000000..5c18b8e0fa --- /dev/null +++ b/src/main/resources/templates/home.html @@ -0,0 +1,55 @@ + + + + + + VoucherManagement + + + +
+

VoucherManagement

+
+
+

About Voucher

+ +
+
+
+

About Wallet

+ +
+
+ + + + From a9cd31fc66051ea472673ddbcbae3d24e4eac714 Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Sat, 4 Nov 2023 17:40:21 +0900 Subject: [PATCH 51/86] =?UTF-8?q?feat:=20voucher=20=EA=B4=80=EB=A0=A8=20Ht?= =?UTF-8?q?ml=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../templates/voucher/createForm.html | 36 +++++++++++++++++ .../templates/voucher/deleteForm.html | 25 ++++++++++++ .../resources/templates/voucher/find.html | 39 +++++++++++++++++++ .../resources/templates/voucher/findForm.html | 24 ++++++++++++ .../templates/voucher/updateForm.html | 28 +++++++++++++ .../templates/voucher/voucherList.html | 39 +++++++++++++++++++ 6 files changed, 191 insertions(+) create mode 100644 src/main/resources/templates/voucher/createForm.html create mode 100644 src/main/resources/templates/voucher/deleteForm.html create mode 100644 src/main/resources/templates/voucher/find.html create mode 100644 src/main/resources/templates/voucher/findForm.html create mode 100644 src/main/resources/templates/voucher/updateForm.html create mode 100644 src/main/resources/templates/voucher/voucherList.html diff --git a/src/main/resources/templates/voucher/createForm.html b/src/main/resources/templates/voucher/createForm.html new file mode 100644 index 0000000000..6327a4539a --- /dev/null +++ b/src/main/resources/templates/voucher/createForm.html @@ -0,0 +1,36 @@ + + + + + + Create Voucher + + + +
+

Voucher Type:

+
+ 1. FixedMount
+ 2. PercentDiscount

+
+ +
+
+ + +
+
+ + +
+ +
+ +
+ +
+
+ + + + diff --git a/src/main/resources/templates/voucher/deleteForm.html b/src/main/resources/templates/voucher/deleteForm.html new file mode 100644 index 0000000000..7ea45b256f --- /dev/null +++ b/src/main/resources/templates/voucher/deleteForm.html @@ -0,0 +1,25 @@ + + + + + + + Delete Voucher + + + +
+
+ + +
+ +
+ +
+ +
+ + + + diff --git a/src/main/resources/templates/voucher/find.html b/src/main/resources/templates/voucher/find.html new file mode 100644 index 0000000000..6ec81bf317 --- /dev/null +++ b/src/main/resources/templates/voucher/find.html @@ -0,0 +1,39 @@ + + + + + + Voucher Information + + + + +
+

Voucher Information

+ + + + + + + + + + + + + + + + + +
Voucher IDVoucher TypeDiscountCustomer Email
+
+
+
+ +
+
+ + + diff --git a/src/main/resources/templates/voucher/findForm.html b/src/main/resources/templates/voucher/findForm.html new file mode 100644 index 0000000000..faca8e835b --- /dev/null +++ b/src/main/resources/templates/voucher/findForm.html @@ -0,0 +1,24 @@ + + + + + Find Voucher + + + + +
+
+ + +
+ +
+ +
+ +
+ + + + diff --git a/src/main/resources/templates/voucher/updateForm.html b/src/main/resources/templates/voucher/updateForm.html new file mode 100644 index 0000000000..1888cd59f7 --- /dev/null +++ b/src/main/resources/templates/voucher/updateForm.html @@ -0,0 +1,28 @@ + + + + + Update Voucher + + + + +
+
+ + +
+
+ + +
+ +
+ +
+ +
+ + + + diff --git a/src/main/resources/templates/voucher/voucherList.html b/src/main/resources/templates/voucher/voucherList.html new file mode 100644 index 0000000000..49c0495b2f --- /dev/null +++ b/src/main/resources/templates/voucher/voucherList.html @@ -0,0 +1,39 @@ + + + + + + Voucher Information + + + + +
+

Voucher List

+ + + + + + + + + + + + + + + + + +
Voucher IDVoucher TypeDiscountCustomer Email
+
+
+
+ +
+
+ + + From f516bdf8f7cfb3496c1f0901084b6c824ec538b8 Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Sat, 4 Nov 2023 17:40:58 +0900 Subject: [PATCH 52/86] =?UTF-8?q?feat:=20wallet=20=EA=B4=80=EB=A0=A8=20Htm?= =?UTF-8?q?l=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resources/templates/wallet/addForm.html | 51 ++++++++++++++++ .../wallet/customerListInWallet.html | 58 ++++++++++++++++++ .../templates/wallet/deleteForm.html | 30 ++++++++++ .../templates/wallet/findCustomerForm.html | 27 +++++++++ .../templates/wallet/findVoucherForm.html | 27 +++++++++ .../templates/wallet/voucherListInWallet.html | 60 +++++++++++++++++++ 6 files changed, 253 insertions(+) create mode 100644 src/main/resources/templates/wallet/addForm.html create mode 100644 src/main/resources/templates/wallet/customerListInWallet.html create mode 100644 src/main/resources/templates/wallet/deleteForm.html create mode 100644 src/main/resources/templates/wallet/findCustomerForm.html create mode 100644 src/main/resources/templates/wallet/findVoucherForm.html create mode 100644 src/main/resources/templates/wallet/voucherListInWallet.html diff --git a/src/main/resources/templates/wallet/addForm.html b/src/main/resources/templates/wallet/addForm.html new file mode 100644 index 0000000000..bc161515eb --- /dev/null +++ b/src/main/resources/templates/wallet/addForm.html @@ -0,0 +1,51 @@ + + + + + Add Voucher to Wallet + + + + +

Add Voucher to Wallet

+
+

Voucher List

+ + + + + + + + + + + + + + + + + +
IDTypeDiscountCreated At
+
+
+
+ + +
+
+ + +
+ +
+ +
+ +
+
+ + + + diff --git a/src/main/resources/templates/wallet/customerListInWallet.html b/src/main/resources/templates/wallet/customerListInWallet.html new file mode 100644 index 0000000000..459583b36b --- /dev/null +++ b/src/main/resources/templates/wallet/customerListInWallet.html @@ -0,0 +1,58 @@ + + + + + Customer List + + + +
+

Voucher Information

+ + + + + + + + + + + + + + + + + +
Voucher IDVoucher TypeDiscountCreated At
+
+
+
+
+

Customer List

+ + + + + + + + + + + + + + + + + +
Customer IDNameEmailCustomerType
+
+ +
+ +
+ + diff --git a/src/main/resources/templates/wallet/deleteForm.html b/src/main/resources/templates/wallet/deleteForm.html new file mode 100644 index 0000000000..18caff5bd5 --- /dev/null +++ b/src/main/resources/templates/wallet/deleteForm.html @@ -0,0 +1,30 @@ + + + + + Remove Voucher from Wallet + + + +

Remove Voucher from Wallet

+
+
+
+ + +
+
+ + +
+ +
+ +
+ +
+
+ + + + diff --git a/src/main/resources/templates/wallet/findCustomerForm.html b/src/main/resources/templates/wallet/findCustomerForm.html new file mode 100644 index 0000000000..a489c2a3fd --- /dev/null +++ b/src/main/resources/templates/wallet/findCustomerForm.html @@ -0,0 +1,27 @@ + + + + + Find All Customer From Voucher + + + +
+

Find Vouchers By VoucherId

+
+
+
+ + +
+ +
+ +
+ +
+
+ + + + diff --git a/src/main/resources/templates/wallet/findVoucherForm.html b/src/main/resources/templates/wallet/findVoucherForm.html new file mode 100644 index 0000000000..3643a3f8e3 --- /dev/null +++ b/src/main/resources/templates/wallet/findVoucherForm.html @@ -0,0 +1,27 @@ + + + + + Find All Voucher In Wallet + + + +
+

Find Customers By Email

+
+
+
+ + +
+ +
+ +
+ +
+
+ + + + diff --git a/src/main/resources/templates/wallet/voucherListInWallet.html b/src/main/resources/templates/wallet/voucherListInWallet.html new file mode 100644 index 0000000000..0a472a64bc --- /dev/null +++ b/src/main/resources/templates/wallet/voucherListInWallet.html @@ -0,0 +1,60 @@ + + + + + Voucher List + + + +
+

Customer Information

+ + + + + + + + + + + + + + + + + +
Customer IDNameEmailCustomerType
+
+
+
+
+

Voucher List

+ + + + + + + + + + + + + + + + + +
Voucher IdVoucher TypeDiscountCreated At
+
+ +
+ +
+ + + + From 4446edb82408ed8a0e5654e673d4154e77816e69 Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Sat, 4 Nov 2023 17:42:29 +0900 Subject: [PATCH 53/86] =?UTF-8?q?feat:=20=EA=B8=B0=EC=A1=B4=20=EA=B3=BC?= =?UTF-8?q?=EC=A0=9C=EC=97=90=20=EC=82=AC=EC=9A=A9=EB=90=9C=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/CustomerRepository.java | 85 ------------------- 1 file changed, 85 deletions(-) delete mode 100644 src/main/java/org/programmers/springboot/basic/domain/customer/repository/CustomerRepository.java diff --git a/src/main/java/org/programmers/springboot/basic/domain/customer/repository/CustomerRepository.java b/src/main/java/org/programmers/springboot/basic/domain/customer/repository/CustomerRepository.java deleted file mode 100644 index 1b612618bf..0000000000 --- a/src/main/java/org/programmers/springboot/basic/domain/customer/repository/CustomerRepository.java +++ /dev/null @@ -1,85 +0,0 @@ -package org.programmers.springboot.basic.domain.customer.repository; - -import lombok.extern.slf4j.Slf4j; -import org.programmers.springboot.basic.config.AppConfig; -import org.programmers.springboot.basic.AppConstants; -import org.programmers.springboot.basic.domain.customer.dto.CsvCustomerDto; -import org.programmers.springboot.basic.domain.customer.entity.Customer; -import org.programmers.springboot.basic.domain.customer.mapper.CustomerMapper; -import org.programmers.springboot.basic.util.exception.CSVFileIOFailureException; -import org.programmers.springboot.basic.util.manager.CSVFileManager; -import org.programmers.springboot.basic.util.properties.ExternalProperties; -import org.programmers.springboot.basic.util.properties.FileProperties; -import org.springframework.stereotype.Repository; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -@Slf4j -@Repository -public class CustomerRepository { - - private final CSVFileManager csvFileManager; - private final CustomerMapper customerMapper; - private final FileProperties fileProperties; - private final ExternalProperties externalProperties; - - public CustomerRepository(CSVFileManager csvFileManager, CustomerMapper customerMapper, FileProperties fileProperties, ExternalProperties externalProperties) { - this.csvFileManager = csvFileManager; - this.customerMapper = customerMapper; - this.fileProperties = fileProperties; - this.externalProperties = externalProperties; - } - - public File getFILE() { - String filePath = getFilePath(); - return new File(filePath); - } - - public List findBlackList() { - return getBlackListFromFile(); - } - - private List getBlackListFromFile() { - - List blackList = new ArrayList<>(); - String line; - - try { - BufferedReader reader = this.csvFileManager.getBufferedReader(getFILE()); - - while ((line = reader.readLine()) != null) { - String[] token = line.split(AppConstants.CSV_SEPARATOR); - CsvCustomerDto customerDto = customerMapper.deserialize(token); - Customer customer = customerMapper.mapToCustomer(customerDto); - blackList.add(customer); - } - } catch (FileNotFoundException e) { - throw new CSVFileIOFailureException("Exception Occurred: No matching file found!"); - } catch (IOException e) { - throw new CSVFileIOFailureException("Exception Occurred: Failed to read file!"); - } - - return blackList; - } - - private String getFilePath() { - - String folderPath = this.fileProperties.getUserDir(); - String fileName = this.fileProperties.getNames().get("customer").getFileName(); - String resourcePath = this.fileProperties.getResources().getPath(); - String filePath = folderPath + resourcePath + fileName; - - if (AppConfig.isRunningFromJar()) { - folderPath = externalProperties.getProjDir(); - resourcePath = externalProperties.getResourceDir(); - filePath = folderPath + resourcePath + fileName; - } - - return filePath; - } -} From 4e39a1a4e1a2364f8892560718649d297090ac4f Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Sat, 4 Nov 2023 17:42:49 +0900 Subject: [PATCH 54/86] =?UTF-8?q?feat:=20=EB=AA=85=EC=8B=9C=EC=A0=81?= =?UTF-8?q?=EC=9D=B8=20=EC=9D=98=EB=AF=B8=EB=A5=BC=20=EB=8B=B4=EB=8A=94=20?= =?UTF-8?q?=EC=83=81=EC=88=98=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/programmers/springboot/basic/AppConstants.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/programmers/springboot/basic/AppConstants.java b/src/main/java/org/programmers/springboot/basic/AppConstants.java index 09229a0520..9e4ac8a466 100644 --- a/src/main/java/org/programmers/springboot/basic/AppConstants.java +++ b/src/main/java/org/programmers/springboot/basic/AppConstants.java @@ -1,8 +1,10 @@ package org.programmers.springboot.basic; -public final class AppConstants { +import lombok.AccessLevel; +import lombok.NoArgsConstructor; - private AppConstants() {} +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public final class AppConstants { public static final Long MIN_FIXED_DISCOUNT = 0L; public static final Long MIN_PERCENT_DISCOUNT = 0L; @@ -10,5 +12,5 @@ private AppConstants() {} public static final int NONE = 0; public static final int FIXED_AMOUNT = 1; public static final int PERCENT_DISCOUNT = 2; - public static final String CSV_SEPARATOR = ","; + public static final String SUCCESS = "Complete success!"; } From d17ec4a2339a431c9ef9de4a469051ace9e53a9b Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Sat, 4 Nov 2023 17:43:53 +0900 Subject: [PATCH 55/86] =?UTF-8?q?feat:=20discount=20=ED=83=80=EC=9E=85?= =?UTF-8?q?=EC=9D=84=20=EA=B2=80=EC=A6=9D=ED=95=98=EB=8A=94=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...lidator.java => FixedAmountVoucherValidationStrategy.java} | 4 +--- ...tor.java => PercentDiscountVoucherValidationStrategy.java} | 4 +--- .../{ValidateHandler.java => VoucherValidationStrategy.java} | 2 +- 3 files changed, 3 insertions(+), 7 deletions(-) rename src/main/java/org/programmers/springboot/basic/domain/voucher/service/validate/{FixedAmountVoucherValidator.java => FixedAmountVoucherValidationStrategy.java} (78%) rename src/main/java/org/programmers/springboot/basic/domain/voucher/service/validate/{PercentDiscountVoucherValidator.java => PercentDiscountVoucherValidationStrategy.java} (79%) rename src/main/java/org/programmers/springboot/basic/domain/voucher/service/validate/{ValidateHandler.java => VoucherValidationStrategy.java} (71%) diff --git a/src/main/java/org/programmers/springboot/basic/domain/voucher/service/validate/FixedAmountVoucherValidator.java b/src/main/java/org/programmers/springboot/basic/domain/voucher/service/validate/FixedAmountVoucherValidationStrategy.java similarity index 78% rename from src/main/java/org/programmers/springboot/basic/domain/voucher/service/validate/FixedAmountVoucherValidator.java rename to src/main/java/org/programmers/springboot/basic/domain/voucher/service/validate/FixedAmountVoucherValidationStrategy.java index eeae7986e9..fd6e5a9364 100644 --- a/src/main/java/org/programmers/springboot/basic/domain/voucher/service/validate/FixedAmountVoucherValidator.java +++ b/src/main/java/org/programmers/springboot/basic/domain/voucher/service/validate/FixedAmountVoucherValidationStrategy.java @@ -2,10 +2,8 @@ import org.programmers.springboot.basic.AppConstants; import org.programmers.springboot.basic.domain.voucher.exception.IllegalDiscountException; -import org.springframework.stereotype.Component; -@Component -public class FixedAmountVoucherValidator implements ValidateHandler { +public class FixedAmountVoucherValidationStrategy implements VoucherValidationStrategy { @Override public void validate(Long discount) { diff --git a/src/main/java/org/programmers/springboot/basic/domain/voucher/service/validate/PercentDiscountVoucherValidator.java b/src/main/java/org/programmers/springboot/basic/domain/voucher/service/validate/PercentDiscountVoucherValidationStrategy.java similarity index 79% rename from src/main/java/org/programmers/springboot/basic/domain/voucher/service/validate/PercentDiscountVoucherValidator.java rename to src/main/java/org/programmers/springboot/basic/domain/voucher/service/validate/PercentDiscountVoucherValidationStrategy.java index d1f95dcec8..888af2c1bf 100644 --- a/src/main/java/org/programmers/springboot/basic/domain/voucher/service/validate/PercentDiscountVoucherValidator.java +++ b/src/main/java/org/programmers/springboot/basic/domain/voucher/service/validate/PercentDiscountVoucherValidationStrategy.java @@ -2,10 +2,8 @@ import org.programmers.springboot.basic.AppConstants; import org.programmers.springboot.basic.domain.voucher.exception.IllegalDiscountException; -import org.springframework.stereotype.Component; -@Component -public class PercentDiscountVoucherValidator implements ValidateHandler { +public class PercentDiscountVoucherValidationStrategy implements VoucherValidationStrategy { @Override public void validate(Long discount) { diff --git a/src/main/java/org/programmers/springboot/basic/domain/voucher/service/validate/ValidateHandler.java b/src/main/java/org/programmers/springboot/basic/domain/voucher/service/validate/VoucherValidationStrategy.java similarity index 71% rename from src/main/java/org/programmers/springboot/basic/domain/voucher/service/validate/ValidateHandler.java rename to src/main/java/org/programmers/springboot/basic/domain/voucher/service/validate/VoucherValidationStrategy.java index 067ff0b440..41c91c4a7f 100644 --- a/src/main/java/org/programmers/springboot/basic/domain/voucher/service/validate/ValidateHandler.java +++ b/src/main/java/org/programmers/springboot/basic/domain/voucher/service/validate/VoucherValidationStrategy.java @@ -1,5 +1,5 @@ package org.programmers.springboot.basic.domain.voucher.service.validate; -public interface ValidateHandler { +public interface VoucherValidationStrategy { void validate(Long discount); } From 7c655aec473cffad7aabbc65d0bfb0ce6f6cf7c8 Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Sat, 4 Nov 2023 17:45:02 +0900 Subject: [PATCH 56/86] =?UTF-8?q?feat:=20=EC=BB=A4=EC=8A=A4=ED=85=80=20Run?= =?UTF-8?q?timeException=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exception/BlackCustomerNotFoundException.java | 13 +++++++++++++ .../exception/CustomerNotFoundException.java | 2 +- .../exception/DuplicateBlackCustomerException.java | 13 +++++++++++++ .../customer/exception/DuplicateEmailException.java | 13 +++++++++++++ .../customer/exception/IllegalEmailException.java | 13 +++++++++++++ .../exception/DuplicateVoucherException.java | 13 +++++++++++++ .../voucher/exception/VoucherNotFoundException.java | 2 +- .../VoucherValidatorNotFoundException.java | 2 +- .../wallet/exception/DuplicateWalletException.java | 13 +++++++++++++ 9 files changed, 81 insertions(+), 3 deletions(-) create mode 100644 src/main/java/org/programmers/springboot/basic/domain/customer/exception/BlackCustomerNotFoundException.java create mode 100644 src/main/java/org/programmers/springboot/basic/domain/customer/exception/DuplicateBlackCustomerException.java create mode 100644 src/main/java/org/programmers/springboot/basic/domain/customer/exception/DuplicateEmailException.java create mode 100644 src/main/java/org/programmers/springboot/basic/domain/customer/exception/IllegalEmailException.java create mode 100644 src/main/java/org/programmers/springboot/basic/domain/voucher/exception/DuplicateVoucherException.java create mode 100644 src/main/java/org/programmers/springboot/basic/domain/wallet/exception/DuplicateWalletException.java diff --git a/src/main/java/org/programmers/springboot/basic/domain/customer/exception/BlackCustomerNotFoundException.java b/src/main/java/org/programmers/springboot/basic/domain/customer/exception/BlackCustomerNotFoundException.java new file mode 100644 index 0000000000..fa38991824 --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/domain/customer/exception/BlackCustomerNotFoundException.java @@ -0,0 +1,13 @@ +package org.programmers.springboot.basic.domain.customer.exception; + +public class BlackCustomerNotFoundException extends RuntimeException { + + public BlackCustomerNotFoundException() { + super("No matching BLACK customers found!"); + } + + @Override + public String getMessage() { + return super.getMessage(); + } +} diff --git a/src/main/java/org/programmers/springboot/basic/domain/customer/exception/CustomerNotFoundException.java b/src/main/java/org/programmers/springboot/basic/domain/customer/exception/CustomerNotFoundException.java index 2e718b6ab5..bf054b4941 100644 --- a/src/main/java/org/programmers/springboot/basic/domain/customer/exception/CustomerNotFoundException.java +++ b/src/main/java/org/programmers/springboot/basic/domain/customer/exception/CustomerNotFoundException.java @@ -3,7 +3,7 @@ public class CustomerNotFoundException extends RuntimeException { public CustomerNotFoundException() { - super("Exception Occurred: No matching customers found!"); + super("No matching customers found!"); } @Override diff --git a/src/main/java/org/programmers/springboot/basic/domain/customer/exception/DuplicateBlackCustomerException.java b/src/main/java/org/programmers/springboot/basic/domain/customer/exception/DuplicateBlackCustomerException.java new file mode 100644 index 0000000000..d6ac7bb443 --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/domain/customer/exception/DuplicateBlackCustomerException.java @@ -0,0 +1,13 @@ +package org.programmers.springboot.basic.domain.customer.exception; + +public class DuplicateBlackCustomerException extends RuntimeException { + + public DuplicateBlackCustomerException() { + super("Duplicate customer already exists in blacklist!"); + } + + @Override + public String getMessage() { + return super.getMessage(); + } +} diff --git a/src/main/java/org/programmers/springboot/basic/domain/customer/exception/DuplicateEmailException.java b/src/main/java/org/programmers/springboot/basic/domain/customer/exception/DuplicateEmailException.java new file mode 100644 index 0000000000..8c9c85fdab --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/domain/customer/exception/DuplicateEmailException.java @@ -0,0 +1,13 @@ +package org.programmers.springboot.basic.domain.customer.exception; + +public class DuplicateEmailException extends RuntimeException { + + public DuplicateEmailException() { + super("Duplicate Email already exists!"); + } + + @Override + public String getMessage() { + return super.getMessage(); + } +} diff --git a/src/main/java/org/programmers/springboot/basic/domain/customer/exception/IllegalEmailException.java b/src/main/java/org/programmers/springboot/basic/domain/customer/exception/IllegalEmailException.java new file mode 100644 index 0000000000..b8c16312f3 --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/domain/customer/exception/IllegalEmailException.java @@ -0,0 +1,13 @@ +package org.programmers.springboot.basic.domain.customer.exception; + +public class IllegalEmailException extends RuntimeException { + + public IllegalEmailException() { + super("Illegal Email Type!"); + } + + @Override + public String getMessage() { + return super.getMessage(); + } +} diff --git a/src/main/java/org/programmers/springboot/basic/domain/voucher/exception/DuplicateVoucherException.java b/src/main/java/org/programmers/springboot/basic/domain/voucher/exception/DuplicateVoucherException.java new file mode 100644 index 0000000000..6fc7c5ad20 --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/domain/voucher/exception/DuplicateVoucherException.java @@ -0,0 +1,13 @@ +package org.programmers.springboot.basic.domain.voucher.exception; + +public class DuplicateVoucherException extends RuntimeException { + + public DuplicateVoucherException() { + super("Duplicate voucher already exists!"); + } + + @Override + public String getMessage() { + return super.getMessage(); + } +} diff --git a/src/main/java/org/programmers/springboot/basic/domain/voucher/exception/VoucherNotFoundException.java b/src/main/java/org/programmers/springboot/basic/domain/voucher/exception/VoucherNotFoundException.java index 9c85ce3fb8..58a2b6ec8c 100644 --- a/src/main/java/org/programmers/springboot/basic/domain/voucher/exception/VoucherNotFoundException.java +++ b/src/main/java/org/programmers/springboot/basic/domain/voucher/exception/VoucherNotFoundException.java @@ -3,7 +3,7 @@ public class VoucherNotFoundException extends RuntimeException { public VoucherNotFoundException() { - super("Exception Occurred: No matching vouchers found!"); + super("No matching vouchers found!"); } @Override diff --git a/src/main/java/org/programmers/springboot/basic/domain/voucher/exception/VoucherValidatorNotFoundException.java b/src/main/java/org/programmers/springboot/basic/domain/voucher/exception/VoucherValidatorNotFoundException.java index 02d0de2195..d59ba628f9 100644 --- a/src/main/java/org/programmers/springboot/basic/domain/voucher/exception/VoucherValidatorNotFoundException.java +++ b/src/main/java/org/programmers/springboot/basic/domain/voucher/exception/VoucherValidatorNotFoundException.java @@ -3,7 +3,7 @@ public class VoucherValidatorNotFoundException extends RuntimeException { public VoucherValidatorNotFoundException() { - super("Exception Occurred: No matching validator found!"); + super("No matching validator found!"); } @Override diff --git a/src/main/java/org/programmers/springboot/basic/domain/wallet/exception/DuplicateWalletException.java b/src/main/java/org/programmers/springboot/basic/domain/wallet/exception/DuplicateWalletException.java new file mode 100644 index 0000000000..9838f0d623 --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/domain/wallet/exception/DuplicateWalletException.java @@ -0,0 +1,13 @@ +package org.programmers.springboot.basic.domain.wallet.exception; + +public class DuplicateWalletException extends RuntimeException { + + public DuplicateWalletException() { + super("Duplicate value already exists in wallet!"); + } + + @Override + public String getMessage() { + return super.getMessage(); + } +} From abcf986022e03de516a1323d795b16c26ef2ebf8 Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Sat, 4 Nov 2023 17:45:20 +0900 Subject: [PATCH 57/86] =?UTF-8?q?feat:=20CustomerType=20=EA=B4=80=EB=A0=A8?= =?UTF-8?q?=20ENUM=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/customer/entity/CustomerType.java | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/programmers/springboot/basic/domain/customer/entity/CustomerType.java b/src/main/java/org/programmers/springboot/basic/domain/customer/entity/CustomerType.java index c00cccc53e..de52db16bd 100644 --- a/src/main/java/org/programmers/springboot/basic/domain/customer/entity/CustomerType.java +++ b/src/main/java/org/programmers/springboot/basic/domain/customer/entity/CustomerType.java @@ -1,7 +1,29 @@ package org.programmers.springboot.basic.domain.customer.entity; +import org.programmers.springboot.basic.domain.customer.exception.CustomerNotFoundException; + +import java.util.Arrays; + public enum CustomerType { - NORMAL, - BLACK + NORMAL(0), + BLACK(1); + + private final int value; + + CustomerType(int value) { + this.value = value; + } + + public int getValue() { + return this.value; + } + + public static CustomerType valueOf(int value) { + + return Arrays.stream(values()) + .filter(customer -> customer.getValue() == value) + .findAny() + .orElseThrow(CustomerNotFoundException::new); + } } From 78b5c818fc9c9db06470158488166d019ff0e538 Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Sat, 4 Nov 2023 17:45:39 +0900 Subject: [PATCH 58/86] =?UTF-8?q?feat:=20VoucherType=20=EA=B4=80=EB=A0=A8?= =?UTF-8?q?=20ENUM=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../basic/domain/voucher/entity/VoucherType.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/programmers/springboot/basic/domain/voucher/entity/VoucherType.java b/src/main/java/org/programmers/springboot/basic/domain/voucher/entity/VoucherType.java index 7f9bab5388..dbcdadef9d 100644 --- a/src/main/java/org/programmers/springboot/basic/domain/voucher/entity/VoucherType.java +++ b/src/main/java/org/programmers/springboot/basic/domain/voucher/entity/VoucherType.java @@ -9,20 +9,20 @@ public enum VoucherType { FIXED(1), PERCENT(2); - private final int type; + private final int value; - VoucherType(int type) { - this.type = type; + VoucherType(int value) { + this.value = value; } - public int getType() { - return this.type; + public int getValue() { + return this.value; } - public static VoucherType valueOfVoucherByType(int type) { + public static VoucherType valueOf(int value) { - return Arrays.stream(VoucherType.values()) - .filter(voucher -> voucher.getType() == type) + return Arrays.stream(values()) + .filter(voucher -> voucher.getValue() == value) .findAny() .orElseThrow(VoucherNotFoundException::new); } From 1f28a80a896738ccd8a26cb1b4733974993d1037 Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Sat, 4 Nov 2023 17:46:16 +0900 Subject: [PATCH 59/86] =?UTF-8?q?feat:=20voucherType=EC=97=90=20=EB=94=B0?= =?UTF-8?q?=EB=A5=B8=20validator=20=EC=A3=BC=EC=9E=85=EC=9D=84=20=EC=9C=84?= =?UTF-8?q?=ED=95=B4=20=EC=8A=A4=ED=94=84=EB=A7=81=20=EB=B9=88=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EB=93=B1=EB=A1=9D,=20=EA=B4=80=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../basic/config/VoucherConfig.java | 61 +++++-------------- 1 file changed, 15 insertions(+), 46 deletions(-) diff --git a/src/main/java/org/programmers/springboot/basic/config/VoucherConfig.java b/src/main/java/org/programmers/springboot/basic/config/VoucherConfig.java index fc37bcde38..2a73431196 100644 --- a/src/main/java/org/programmers/springboot/basic/config/VoucherConfig.java +++ b/src/main/java/org/programmers/springboot/basic/config/VoucherConfig.java @@ -1,64 +1,33 @@ package org.programmers.springboot.basic.config; -import org.programmers.springboot.basic.domain.voucher.dto.CsvVoucherDto; -import org.programmers.springboot.basic.domain.voucher.dto.VoucherRequestDto; -import org.programmers.springboot.basic.domain.voucher.dto.VoucherResponseDto; -import org.programmers.springboot.basic.domain.voucher.entity.FixedAmountVoucher; -import org.programmers.springboot.basic.domain.voucher.entity.PercentDiscountVoucher; -import org.programmers.springboot.basic.domain.voucher.entity.Voucher; import org.programmers.springboot.basic.domain.voucher.entity.VoucherType; -import org.programmers.springboot.basic.domain.voucher.service.validate.FixedAmountVoucherValidator; -import org.programmers.springboot.basic.domain.voucher.service.validate.PercentDiscountVoucherValidator; -import org.programmers.springboot.basic.domain.voucher.service.validate.ValidateHandler; -import org.springframework.beans.factory.annotation.Autowired; +import org.programmers.springboot.basic.domain.voucher.service.validate.FixedAmountVoucherValidationStrategy; +import org.programmers.springboot.basic.domain.voucher.service.validate.PercentDiscountVoucherValidationStrategy; +import org.programmers.springboot.basic.domain.voucher.service.validate.VoucherValidationStrategy; import org.springframework.context.annotation.Bean; -import org.springframework.stereotype.Component; +import org.springframework.context.annotation.Configuration; import java.util.HashMap; import java.util.Map; -import java.util.UUID; -@Component +@Configuration public class VoucherConfig { - private final FixedAmountVoucherValidator fixedAmountVoucherValidator; - private final PercentDiscountVoucherValidator percentDiscountVoucherValidator; - - @Autowired - public VoucherConfig(FixedAmountVoucherValidator fixedAmountVoucherValidator, PercentDiscountVoucherValidator percentDiscountVoucherValidator) { - this.fixedAmountVoucherValidator = fixedAmountVoucherValidator; - this.percentDiscountVoucherValidator = percentDiscountVoucherValidator; - } - - public Voucher getFixedAmountVoucher(VoucherRequestDto requestDto) { - return new FixedAmountVoucher(UUID.randomUUID(), requestDto.discount(), requestDto.voucherType()); - } - - public Voucher getFixedAmountVoucher(CsvVoucherDto csvVoucherDto) { - return new FixedAmountVoucher(csvVoucherDto.voucherId(), csvVoucherDto.discount(), csvVoucherDto.voucherType()); - } - - public Voucher getPercentDiscountVoucher(VoucherRequestDto requestDto) { - return new PercentDiscountVoucher(UUID.randomUUID(), requestDto.discount(), requestDto.voucherType()); - } - - public Voucher getPercentDiscountVoucher(CsvVoucherDto csvVoucherDto) { - return new PercentDiscountVoucher(csvVoucherDto.voucherId(), csvVoucherDto.discount(), csvVoucherDto.voucherType()); - } - - public VoucherResponseDto getVoucherResponseDto(Voucher voucher) { - return new VoucherResponseDto(voucher.getVoucherId(), voucher.getVoucherType(), voucher.getDiscount()); + @Bean + public FixedAmountVoucherValidationStrategy fixedAmountVoucherValidator() { + return new FixedAmountVoucherValidationStrategy(); } - public CsvVoucherDto getCsvVoucherDto(UUID voucherId, VoucherType voucherType, Long discount) { - return new CsvVoucherDto(voucherId, voucherType, discount); + @Bean + public PercentDiscountVoucherValidationStrategy percentDiscountVoucherValidator() { + return new PercentDiscountVoucherValidationStrategy(); } @Bean - public Map validateHandlers() { - Map handler = new HashMap<>(); - handler.put(VoucherType.FIXED, this.fixedAmountVoucherValidator); - handler.put(VoucherType.PERCENT, this.percentDiscountVoucherValidator); + public Map validationStrategyMap() { + Map handler = new HashMap<>(); + handler.put(VoucherType.FIXED, fixedAmountVoucherValidator()); + handler.put(VoucherType.PERCENT, percentDiscountVoucherValidator()); return handler; } } From b7c55716b020fdc2a548978f9cadc66157c329fe Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Sat, 4 Nov 2023 17:48:14 +0900 Subject: [PATCH 60/86] =?UTF-8?q?feat:=20Dto=20=ED=81=B4=EB=9E=98=EC=8A=A4?= =?UTF-8?q?=EC=97=90=20=EA=B8=B0=EB=B3=B8=20=EB=A9=94=EC=84=9C=EB=93=9C,?= =?UTF-8?q?=20=EC=B6=9C=EB=A0=A5=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../customer/dto/CustomerRequestDto.java | 33 +++++++++++++++++ .../customer/dto/CustomerResponseDto.java | 37 ++++++++++++++++++- .../domain/voucher/dto/VoucherRequestDto.java | 29 ++++++++++++++- .../voucher/dto/VoucherResponseDto.java | 37 ++++++++++++++++++- 4 files changed, 133 insertions(+), 3 deletions(-) create mode 100644 src/main/java/org/programmers/springboot/basic/domain/customer/dto/CustomerRequestDto.java diff --git a/src/main/java/org/programmers/springboot/basic/domain/customer/dto/CustomerRequestDto.java b/src/main/java/org/programmers/springboot/basic/domain/customer/dto/CustomerRequestDto.java new file mode 100644 index 0000000000..cac8265fff --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/domain/customer/dto/CustomerRequestDto.java @@ -0,0 +1,33 @@ +package org.programmers.springboot.basic.domain.customer.dto; + +import lombok.Builder; +import lombok.Getter; +import org.programmers.springboot.basic.domain.customer.entity.CustomerType; +import org.programmers.springboot.basic.domain.customer.entity.vo.Email; + +import java.util.UUID; + +@Builder +@Getter +public class CustomerRequestDto { + + private final UUID customerId; + private final String name; + private final Email email; + private final CustomerType customerType; + + @Override + public int hashCode() { + return super.hashCode(); + } + + @Override + public boolean equals(Object obj) { + return super.equals(obj); + } + + @Override + public String toString() { + return super.toString(); + } +} diff --git a/src/main/java/org/programmers/springboot/basic/domain/customer/dto/CustomerResponseDto.java b/src/main/java/org/programmers/springboot/basic/domain/customer/dto/CustomerResponseDto.java index 5c4860c61c..cc244143c2 100644 --- a/src/main/java/org/programmers/springboot/basic/domain/customer/dto/CustomerResponseDto.java +++ b/src/main/java/org/programmers/springboot/basic/domain/customer/dto/CustomerResponseDto.java @@ -1,8 +1,43 @@ package org.programmers.springboot.basic.domain.customer.dto; +import lombok.Builder; +import lombok.Getter; import org.programmers.springboot.basic.domain.customer.entity.CustomerType; +import org.programmers.springboot.basic.domain.customer.entity.vo.Email; import java.util.UUID; -public record CustomerResponseDto(UUID customerId, String name, CustomerType customerType) { +@Builder +@Getter +public class CustomerResponseDto { + + private final UUID customerId; + private final String name; + private final Email email; + private final CustomerType customerType; + + @Override + public int hashCode() { + return super.hashCode(); + } + + @Override + public boolean equals(Object obj) { + return super.equals(obj); + } + + @Override + public String toString() { + return super.toString(); + } + + public String printCustomerInfo() { + return String.format(""" + customerId: %s + email: %s + name: %s + customerType: %s + --------------------------------- + """, customerId.toString(), email, name, customerType); + } } diff --git a/src/main/java/org/programmers/springboot/basic/domain/voucher/dto/VoucherRequestDto.java b/src/main/java/org/programmers/springboot/basic/domain/voucher/dto/VoucherRequestDto.java index 99a050a817..f69f17313b 100644 --- a/src/main/java/org/programmers/springboot/basic/domain/voucher/dto/VoucherRequestDto.java +++ b/src/main/java/org/programmers/springboot/basic/domain/voucher/dto/VoucherRequestDto.java @@ -1,6 +1,33 @@ package org.programmers.springboot.basic.domain.voucher.dto; +import lombok.Builder; +import lombok.Getter; import org.programmers.springboot.basic.domain.voucher.entity.VoucherType; -public record VoucherRequestDto(VoucherType voucherType, Long discount) { +import java.time.LocalDateTime; +import java.util.UUID; + +@Builder +@Getter +public class VoucherRequestDto { + + private final UUID voucherId; + private final VoucherType voucherType; + private final Long discount; + private final LocalDateTime createdAt; + + @Override + public int hashCode() { + return super.hashCode(); + } + + @Override + public boolean equals(Object obj) { + return super.equals(obj); + } + + @Override + public String toString() { + return super.toString(); + } } diff --git a/src/main/java/org/programmers/springboot/basic/domain/voucher/dto/VoucherResponseDto.java b/src/main/java/org/programmers/springboot/basic/domain/voucher/dto/VoucherResponseDto.java index 0451c613e7..b25ca38ff6 100644 --- a/src/main/java/org/programmers/springboot/basic/domain/voucher/dto/VoucherResponseDto.java +++ b/src/main/java/org/programmers/springboot/basic/domain/voucher/dto/VoucherResponseDto.java @@ -1,8 +1,43 @@ package org.programmers.springboot.basic.domain.voucher.dto; +import lombok.Builder; +import lombok.Getter; import org.programmers.springboot.basic.domain.voucher.entity.VoucherType; +import java.time.LocalDateTime; import java.util.UUID; -public record VoucherResponseDto(UUID voucherId, VoucherType voucherType, Long discount) { +@Builder +@Getter +public class VoucherResponseDto { + + private final UUID voucherId; + private final VoucherType voucherType; + private final Long discount; + private final LocalDateTime createdAt; + + @Override + public int hashCode() { + return super.hashCode(); + } + + @Override + public boolean equals(Object obj) { + return super.equals(obj); + } + + @Override + public String toString() { + return super.toString(); + } + + public String printInfo() { + return String.format(""" + === Voucher === + VoucherID: %s + Discount: %d + VoucherType: %s + CreatedAt: %s + """, voucherId.toString(), discount, voucherType, createdAt.toString()); + } } From a2a3473c6da55040f40e8671eb411a766415e798 Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Sat, 4 Nov 2023 17:48:51 +0900 Subject: [PATCH 61/86] =?UTF-8?q?feat:=20UUID=20=EB=9E=9C=EB=8D=A4=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=EC=9D=84=20=EC=9C=84=ED=95=9C=20Generator=20?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../basic/util/generator/UUIDGenerator.java | 8 ++++++++ .../util/generator/UUIDRandomGenerator.java | 16 ++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 src/main/java/org/programmers/springboot/basic/util/generator/UUIDGenerator.java create mode 100644 src/main/java/org/programmers/springboot/basic/util/generator/UUIDRandomGenerator.java diff --git a/src/main/java/org/programmers/springboot/basic/util/generator/UUIDGenerator.java b/src/main/java/org/programmers/springboot/basic/util/generator/UUIDGenerator.java new file mode 100644 index 0000000000..06dfcef7cc --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/util/generator/UUIDGenerator.java @@ -0,0 +1,8 @@ +package org.programmers.springboot.basic.util.generator; + +import java.util.UUID; + +public interface UUIDGenerator { + + UUID generateUUID(); +} diff --git a/src/main/java/org/programmers/springboot/basic/util/generator/UUIDRandomGenerator.java b/src/main/java/org/programmers/springboot/basic/util/generator/UUIDRandomGenerator.java new file mode 100644 index 0000000000..6776d89bb2 --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/util/generator/UUIDRandomGenerator.java @@ -0,0 +1,16 @@ +package org.programmers.springboot.basic.util.generator; + +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Component; + +import java.util.UUID; + +@Component +@Primary +public class UUIDRandomGenerator implements UUIDGenerator { + + @Override + public UUID generateUUID() { + return UUID.randomUUID(); + } +} From 3237166f33f40c3fd0c13ed2f5297ec4711393af Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Sat, 4 Nov 2023 17:49:17 +0900 Subject: [PATCH 62/86] =?UTF-8?q?feat:=20UUID=EC=99=80=20byte[]=20?= =?UTF-8?q?=EA=B0=84=EC=9D=98=20=EB=B3=80=ED=99=98=EC=9D=84=20=EB=8B=B4?= =?UTF-8?q?=EB=8B=B9=ED=95=98=EB=8A=94=20Converter=20=ED=81=B4=EB=9E=98?= =?UTF-8?q?=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../basic/util/converter/UUIDConverter.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 src/main/java/org/programmers/springboot/basic/util/converter/UUIDConverter.java diff --git a/src/main/java/org/programmers/springboot/basic/util/converter/UUIDConverter.java b/src/main/java/org/programmers/springboot/basic/util/converter/UUIDConverter.java new file mode 100644 index 0000000000..65b62ecef0 --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/util/converter/UUIDConverter.java @@ -0,0 +1,18 @@ +package org.programmers.springboot.basic.util.converter; + +import java.nio.ByteBuffer; +import java.util.UUID; + +public class UUIDConverter { + + private UUIDConverter() {} + + public static UUID toUUID(byte[] bytes) { + ByteBuffer byteBuffer = ByteBuffer.wrap(bytes); + return new UUID(byteBuffer.getLong(), byteBuffer.getLong()); + } + + public static byte[] toBytes(UUID uuid) { + return uuid.toString().getBytes(); + } +} From e0eb63e7aef7ff1f8b8df864db4246eaa0ce87ad Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Sat, 4 Nov 2023 17:51:03 +0900 Subject: [PATCH 63/86] =?UTF-8?q?feat:=20Customer=20=EB=8F=84=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=20=ED=81=B4=EB=9E=98=EC=8A=A4,=20TDA=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9=EC=9D=84=20=EC=9C=84=ED=95=B4=20=EC=83=81=ED=83=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=EC=9D=98=20set=20=EB=A9=94=EC=84=9C=EB=93=9C?= =?UTF-8?q?=EC=99=80=20equal=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/customer/entity/Customer.java | 39 +++++++++++++------ 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/programmers/springboot/basic/domain/customer/entity/Customer.java b/src/main/java/org/programmers/springboot/basic/domain/customer/entity/Customer.java index 07061ae47e..c0c88d076d 100644 --- a/src/main/java/org/programmers/springboot/basic/domain/customer/entity/Customer.java +++ b/src/main/java/org/programmers/springboot/basic/domain/customer/entity/Customer.java @@ -1,28 +1,45 @@ package org.programmers.springboot.basic.domain.customer.entity; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.programmers.springboot.basic.domain.customer.entity.vo.Email; +import org.programmers.springboot.basic.domain.customer.exception.BlackCustomerNotFoundException; +import org.programmers.springboot.basic.domain.customer.exception.DuplicateBlackCustomerException; + import java.util.UUID; +@Slf4j +@Getter +@AllArgsConstructor public class Customer { private final UUID customerId; private final String name; - private final CustomerType customerType; + private final Email email; + private CustomerType customerType; - public Customer(UUID customerId, String name, CustomerType customerType) { - this.customerId = customerId; - this.name = name; - this.customerType = customerType; + public int getCustomerTypeValue() { + return this.customerType.getValue(); } - public UUID getCustomerId() { - return this.customerId; + public boolean equalType(CustomerType type) { + return this.customerType.equals(type); } - public String getName() { - return this.name; + public void setBlack() { + if (this.equalType(CustomerType.BLACK)) { + log.warn("customer of customerId {} is already exists from blacklist", this.customerId); + throw new DuplicateBlackCustomerException(); + } + this.customerType = CustomerType.BLACK; } - public CustomerType getCustomerType() { - return this.customerType; + public void setNormal() { + if (this.equalType(CustomerType.NORMAL)) { + log.warn("customer of customerId {} is not exists from blacklist", this.customerId); + throw new BlackCustomerNotFoundException(); + } + this.customerType = CustomerType.NORMAL; } } From 851697991595814759be60ebd72ebc83cdb97395 Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Sat, 4 Nov 2023 17:53:26 +0900 Subject: [PATCH 64/86] =?UTF-8?q?feat:=20Voucher=20=EB=8F=84=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=20=ED=81=B4=EB=9E=98=EC=8A=A4,=20TDA=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9=EC=9D=84=20=EC=9C=84=ED=95=B4=20Discount=20=EA=B2=80?= =?UTF-8?q?=EC=A6=9D=20=EB=A9=94=EC=84=9C=EB=93=9C=EC=99=80=20MapStruct=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9=EC=9D=84=20=EC=9C=84=ED=95=9C=20set=20?= =?UTF-8?q?=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../basic/domain/voucher/entity/Voucher.java | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 src/main/java/org/programmers/springboot/basic/domain/voucher/entity/Voucher.java diff --git a/src/main/java/org/programmers/springboot/basic/domain/voucher/entity/Voucher.java b/src/main/java/org/programmers/springboot/basic/domain/voucher/entity/Voucher.java new file mode 100644 index 0000000000..2fac4ac961 --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/domain/voucher/entity/Voucher.java @@ -0,0 +1,31 @@ +package org.programmers.springboot.basic.domain.voucher.entity; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.programmers.springboot.basic.domain.voucher.service.validate.VoucherValidationStrategy; + +import java.time.LocalDateTime; +import java.util.UUID; + +@Getter +@AllArgsConstructor +public class Voucher { + + private final UUID voucherId; + private final VoucherType voucherType; + private final Long discount; + private final LocalDateTime createdAt; + private VoucherValidationStrategy validationStrategy; + + public void setValidationStrategy(VoucherValidationStrategy validationStrategy) { + this.validationStrategy = validationStrategy; + } + + public int getVoucherTypeValue() { + return this.voucherType.getValue(); + } + + public void validate() { + validationStrategy.validate(this.discount); + } +} From b1780bac63cb89d3b1398c6d6bded3510cc4bb9a Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Sat, 4 Nov 2023 17:54:21 +0900 Subject: [PATCH 65/86] =?UTF-8?q?feat:=20Email=20=EA=B2=80=EC=A6=9D?= =?UTF-8?q?=EC=9D=84=20=EC=9C=84=ED=95=9C=20=EB=8F=84=EB=A9=94=EC=9D=B8=20?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=B6=94=EA=B0=80,=20=EC=9D=B4?= =?UTF-8?q?=EB=A9=94=EC=9D=BC=EC=9D=98=20=EC=9C=A0=ED=9A=A8=EC=84=B1=20?= =?UTF-8?q?=EA=B2=80=EC=A6=9D=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/customer/entity/vo/Email.java | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 src/main/java/org/programmers/springboot/basic/domain/customer/entity/vo/Email.java diff --git a/src/main/java/org/programmers/springboot/basic/domain/customer/entity/vo/Email.java b/src/main/java/org/programmers/springboot/basic/domain/customer/entity/vo/Email.java new file mode 100644 index 0000000000..d70d79d1ed --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/domain/customer/entity/vo/Email.java @@ -0,0 +1,27 @@ +package org.programmers.springboot.basic.domain.customer.entity.vo; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.programmers.springboot.basic.domain.customer.exception.IllegalEmailException; + +import java.util.regex.Pattern; + +@Getter +@RequiredArgsConstructor +public class Email { + + private static final String REGEX = "^[_a-z0-9-]+(.[_a-z0-9-]+)*@(?:\\w+\\.)+\\w+$"; + private static final Pattern pattern = Pattern.compile(REGEX); + + private final String email; + + public static Email valueOf(String email) { + return new Email(email); + } + + public void validate() { + if (!pattern.matcher(email).matches()) { + throw new IllegalEmailException(); + } + } +} From f4a37d006fe57c84cf550113799335595ec0156a Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Sat, 4 Nov 2023 17:54:48 +0900 Subject: [PATCH 66/86] =?UTF-8?q?feat:=20LocalDateTime=EC=9D=84=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20=EC=8B=9C=EC=97=90=20=EC=A3=BC=EC=9E=85?= =?UTF-8?q?=ED=95=98=EA=B8=B0=20=EC=9C=84=ED=95=9C=20Generator=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../basic/util/generator/DateTimeGenerator.java | 8 ++++++++ .../basic/util/generator/NowDateTimeGenerator.java | 14 ++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 src/main/java/org/programmers/springboot/basic/util/generator/DateTimeGenerator.java create mode 100644 src/main/java/org/programmers/springboot/basic/util/generator/NowDateTimeGenerator.java diff --git a/src/main/java/org/programmers/springboot/basic/util/generator/DateTimeGenerator.java b/src/main/java/org/programmers/springboot/basic/util/generator/DateTimeGenerator.java new file mode 100644 index 0000000000..0a12bd2c3f --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/util/generator/DateTimeGenerator.java @@ -0,0 +1,8 @@ +package org.programmers.springboot.basic.util.generator; + +import java.time.LocalDateTime; + +public interface DateTimeGenerator { + + LocalDateTime generateDateTime(); +} diff --git a/src/main/java/org/programmers/springboot/basic/util/generator/NowDateTimeGenerator.java b/src/main/java/org/programmers/springboot/basic/util/generator/NowDateTimeGenerator.java new file mode 100644 index 0000000000..e7178abb02 --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/util/generator/NowDateTimeGenerator.java @@ -0,0 +1,14 @@ +package org.programmers.springboot.basic.util.generator; + +import org.springframework.stereotype.Component; + +import java.time.LocalDateTime; + +@Component +public class NowDateTimeGenerator implements DateTimeGenerator { + + @Override + public LocalDateTime generateDateTime() { + return LocalDateTime.now(); + } +} From 60ef32c0033177384d30e52a1a8df2ae27545a8f Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Sat, 4 Nov 2023 17:55:21 +0900 Subject: [PATCH 67/86] =?UTF-8?q?feat:=20Wallet=20=EB=8F=84=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../basic/domain/wallet/entity/Wallet.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 src/main/java/org/programmers/springboot/basic/domain/wallet/entity/Wallet.java diff --git a/src/main/java/org/programmers/springboot/basic/domain/wallet/entity/Wallet.java b/src/main/java/org/programmers/springboot/basic/domain/wallet/entity/Wallet.java new file mode 100644 index 0000000000..c12176bf7e --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/domain/wallet/entity/Wallet.java @@ -0,0 +1,17 @@ +package org.programmers.springboot.basic.domain.wallet.entity; + +import lombok.Builder; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.programmers.springboot.basic.domain.customer.entity.vo.Email; + +import java.util.UUID; + +@Getter +@Builder +@RequiredArgsConstructor +public class Wallet { + + private final UUID voucherId; + private final Email email; +} From 1251ce6159bbd37e7c97660c9e45705123107c43 Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Sat, 4 Nov 2023 17:55:50 +0900 Subject: [PATCH 68/86] =?UTF-8?q?feat:=20Wallet=20=EA=B4=80=EB=A0=A8=20Dto?= =?UTF-8?q?=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/wallet/dto/WalletRequestDto.java | 30 +++++++++++++++++++ .../domain/wallet/dto/WalletResponseDto.java | 30 +++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 src/main/java/org/programmers/springboot/basic/domain/wallet/dto/WalletRequestDto.java create mode 100644 src/main/java/org/programmers/springboot/basic/domain/wallet/dto/WalletResponseDto.java diff --git a/src/main/java/org/programmers/springboot/basic/domain/wallet/dto/WalletRequestDto.java b/src/main/java/org/programmers/springboot/basic/domain/wallet/dto/WalletRequestDto.java new file mode 100644 index 0000000000..935d5deabd --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/domain/wallet/dto/WalletRequestDto.java @@ -0,0 +1,30 @@ +package org.programmers.springboot.basic.domain.wallet.dto; + +import lombok.Builder; +import lombok.Getter; +import org.programmers.springboot.basic.domain.customer.entity.vo.Email; + +import java.util.UUID; + +@Builder +@Getter +public class WalletRequestDto { + + private final Email email; + private final UUID voucherId; + + @Override + public int hashCode() { + return super.hashCode(); + } + + @Override + public boolean equals(Object obj) { + return super.equals(obj); + } + + @Override + public String toString() { + return super.toString(); + } +} diff --git a/src/main/java/org/programmers/springboot/basic/domain/wallet/dto/WalletResponseDto.java b/src/main/java/org/programmers/springboot/basic/domain/wallet/dto/WalletResponseDto.java new file mode 100644 index 0000000000..0cfd4ac074 --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/domain/wallet/dto/WalletResponseDto.java @@ -0,0 +1,30 @@ +package org.programmers.springboot.basic.domain.wallet.dto; + +import lombok.Builder; +import lombok.Getter; +import org.programmers.springboot.basic.domain.customer.entity.vo.Email; + +import java.util.UUID; + +@Builder +@Getter +public class WalletResponseDto { + + private final Email email; + private final UUID voucherId; + + @Override + public int hashCode() { + return super.hashCode(); + } + + @Override + public boolean equals(Object obj) { + return super.equals(obj); + } + + @Override + public String toString() { + return super.toString(); + } +} From 70a100ca620cc6a8799e22b43bf3ea96a5590d0d Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Sat, 4 Nov 2023 17:58:29 +0900 Subject: [PATCH 69/86] =?UTF-8?q?feat:=20=EC=A4=91=EB=B3=B5=20=EC=B2=B4?= =?UTF-8?q?=ED=81=AC=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=B6=94=EA=B0=80,?= =?UTF-8?q?=20=EC=A1=B0=EA=B1=B4=EB=B3=84=EB=A1=9C=20=EC=A1=B0=ED=9A=8C?= =?UTF-8?q?=EB=A5=BC=20=EC=88=98=ED=96=89=ED=95=98=EB=8A=94=20find=20?= =?UTF-8?q?=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=B6=94=EA=B0=80=20(null?= =?UTF-8?q?=EC=9D=B4=20=EB=93=A4=EC=96=B4=EC=98=A4=EB=A9=B4=20=EC=A1=B0?= =?UTF-8?q?=EA=B1=B4=20=EB=AC=B4=EC=8B=9C)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/JdbcVoucherRepository.java | 131 ++++++++++++++++++ .../voucher/repository/VoucherRepository.java | 11 ++ 2 files changed, 142 insertions(+) create mode 100644 src/main/java/org/programmers/springboot/basic/domain/voucher/repository/JdbcVoucherRepository.java diff --git a/src/main/java/org/programmers/springboot/basic/domain/voucher/repository/JdbcVoucherRepository.java b/src/main/java/org/programmers/springboot/basic/domain/voucher/repository/JdbcVoucherRepository.java new file mode 100644 index 0000000000..ad9e4f3151 --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/domain/voucher/repository/JdbcVoucherRepository.java @@ -0,0 +1,131 @@ +package org.programmers.springboot.basic.domain.voucher.repository; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.programmers.springboot.basic.domain.voucher.entity.Voucher; +import org.programmers.springboot.basic.domain.voucher.entity.VoucherType; +import org.programmers.springboot.basic.domain.voucher.service.validate.VoucherValidationStrategy; +import org.programmers.springboot.basic.util.converter.UUIDConverter; +import org.springframework.dao.DataAccessException; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Repository; + +import java.sql.Timestamp; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; + +@Slf4j +@Repository +@RequiredArgsConstructor +public class JdbcVoucherRepository implements VoucherRepository { + private final JdbcTemplate jdbcTemplate; + private final Map validationStrategyMap; + + private RowMapper voucherRowMapper() { + return (rs, rowNum) -> { + UUID voucherId = UUIDConverter.toUUID(rs.getBytes("voucher_id")); + VoucherType voucherType = VoucherType.valueOf(rs.getInt("voucher_type")); + Long discount = rs.getLong("discount"); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + LocalDateTime createdAt = LocalDateTime.parse(rs.getString("createdAt"), formatter); + return new Voucher(voucherId, voucherType, discount, createdAt, validationStrategyMap.get(voucherType)); + }; + } + + @Override + public void save(final Voucher voucher) { + String sql = "INSERT INTO vouchers(voucher_id, voucher_type, discount) VALUES(UUID_TO_BIN(?), ?, ?)"; + this.jdbcTemplate.update(sql, + UUIDConverter.toBytes(voucher.getVoucherId()), + voucher.getVoucherTypeValue(), + voucher.getDiscount()); + } + + @Override + public Optional findById(UUID voucherId) { + String sql = "SELECT * FROM vouchers WHERE voucher_id = UUID_TO_BIN(?)"; + try { + return Optional.ofNullable(this.jdbcTemplate.queryForObject(sql, voucherRowMapper(), + (Object) UUIDConverter.toBytes(voucherId))); + } catch (EmptyResultDataAccessException e) { + log.warn("No voucher found for voucherId: {}", voucherId); + } catch (DataAccessException e) { + log.error("Data access exception: {}", e.toString()); + } + return Optional.empty(); + } + + @Override + public List find(UUID voucherId, VoucherType voucherType, Long discount, LocalDateTime createdAt) { + String sql = "SELECT * FROM vouchers WHERE " + + "(voucher_id = UUID_TO_BIN(?) OR UUID_TO_BIN(?) IS NULL) " + + "AND (voucher_type = ? OR ? IS NULL) " + + "AND (discount = ? OR ? IS NULL) " + + "AND (createdAt = ? OR ? IS NULL)"; + + byte[] bytes = getNullableBytes(voucherId); + Integer type = getNullableValue(voucherType); + Timestamp timestamp = getNullableTimestamp(createdAt); + return this.jdbcTemplate.query(sql, voucherRowMapper(), + bytes, bytes, + type, type, + discount, discount, + timestamp, timestamp); + } + + private byte[] getNullableBytes(UUID value) { + return value != null ? UUIDConverter.toBytes(value) : null; + } + + private Integer getNullableValue(VoucherType value) { + return value != null ? value.getValue() : null; + } + + private Timestamp getNullableTimestamp(LocalDateTime value) { + return value != null ? Timestamp.valueOf(value) : null; + } + + @Override + public Optional findByTypeNDiscount(VoucherType voucherType, Long discount) { + String sql = "SELECT * FROM vouchers WHERE voucher_type = ? AND discount = ?"; + try { + return Optional.ofNullable(this.jdbcTemplate.queryForObject(sql, voucherRowMapper(), + voucherType.getValue(), discount)); + } catch (EmptyResultDataAccessException e) { + log.warn("No voucher found for voucherType {} and discount {}", voucherType, discount); + } catch (DataAccessException e) { + log.error("Data access exception: {}", e.toString()); + } + return Optional.empty(); + } + + @Override + public List findAll() { + String sql = "SELECT * FROM vouchers ORDER BY voucher_id"; + return this.jdbcTemplate.query(sql, voucherRowMapper()); + } + + @Override + public void update(Voucher voucher) { + String sql = "UPDATE vouchers SET discount = ? WHERE voucher_id = UUID_TO_BIN(?)"; + this.jdbcTemplate.update(sql, voucher.getDiscount(), UUIDConverter.toBytes(voucher.getVoucherId())); + } + + @Override + public void delete(UUID voucherId) { + String sql = "DELETE FROM vouchers WHERE voucher_id = UUID_TO_BIN(?)"; + this.jdbcTemplate.update(sql, (Object) UUIDConverter.toBytes(voucherId)); + } + + @Override + public void deleteAll() { + String sql = "DELETE FROM vouchers"; + this.jdbcTemplate.update(sql); + } +} diff --git a/src/main/java/org/programmers/springboot/basic/domain/voucher/repository/VoucherRepository.java b/src/main/java/org/programmers/springboot/basic/domain/voucher/repository/VoucherRepository.java index 43db0ab2b2..c56c24954a 100644 --- a/src/main/java/org/programmers/springboot/basic/domain/voucher/repository/VoucherRepository.java +++ b/src/main/java/org/programmers/springboot/basic/domain/voucher/repository/VoucherRepository.java @@ -1,11 +1,22 @@ package org.programmers.springboot.basic.domain.voucher.repository; import org.programmers.springboot.basic.domain.voucher.entity.Voucher; +import org.programmers.springboot.basic.domain.voucher.entity.VoucherType; +import java.time.LocalDateTime; import java.util.List; +import java.util.Optional; +import java.util.UUID; public interface VoucherRepository { void save(Voucher voucher); + Optional findById(UUID voucherId); + List find(UUID voucherId, VoucherType voucherType, Long discount, LocalDateTime createdAt); + Optional findByTypeNDiscount(VoucherType voucherType, Long discount); List findAll(); + void update(Voucher voucher); + void delete(UUID voucherId); + void deleteAll(); + } From 99c3e50dc2974b011f4520e36343d552b86189b4 Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Sat, 4 Nov 2023 18:00:24 +0900 Subject: [PATCH 70/86] =?UTF-8?q?feat:=20Email=20=ED=83=80=EC=9E=85?= =?UTF-8?q?=EC=97=90=20=EB=8C=80=ED=95=9C=20=EB=A7=A4=ED=95=91=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/CustomerRepository.java | 20 ++++ .../repository/JdbcCustomerRepository.java | 107 ++++++++++++++++++ 2 files changed, 127 insertions(+) create mode 100644 src/main/java/org/programmers/springboot/basic/domain/customer/repository/CustomerRepository.java create mode 100644 src/main/java/org/programmers/springboot/basic/domain/customer/repository/JdbcCustomerRepository.java diff --git a/src/main/java/org/programmers/springboot/basic/domain/customer/repository/CustomerRepository.java b/src/main/java/org/programmers/springboot/basic/domain/customer/repository/CustomerRepository.java new file mode 100644 index 0000000000..41a4597e95 --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/domain/customer/repository/CustomerRepository.java @@ -0,0 +1,20 @@ +package org.programmers.springboot.basic.domain.customer.repository; + +import org.programmers.springboot.basic.domain.customer.entity.Customer; +import org.programmers.springboot.basic.domain.customer.entity.vo.Email; + +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +public interface CustomerRepository { + + void save(Customer customer); + Optional findByEmail(Email email); + Optional findById(UUID customerId); + List findAll(); + List findBlack(); + void update(Customer customer); + void delete(Customer customer); + void deleteAll(); +} diff --git a/src/main/java/org/programmers/springboot/basic/domain/customer/repository/JdbcCustomerRepository.java b/src/main/java/org/programmers/springboot/basic/domain/customer/repository/JdbcCustomerRepository.java new file mode 100644 index 0000000000..e2753a2941 --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/domain/customer/repository/JdbcCustomerRepository.java @@ -0,0 +1,107 @@ +package org.programmers.springboot.basic.domain.customer.repository; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.programmers.springboot.basic.domain.customer.entity.Customer; +import org.programmers.springboot.basic.domain.customer.entity.CustomerType; +import org.programmers.springboot.basic.domain.customer.entity.vo.Email; +import org.programmers.springboot.basic.util.converter.UUIDConverter; +import org.springframework.dao.DataAccessException; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +@Slf4j +@Repository +@RequiredArgsConstructor +public class JdbcCustomerRepository implements CustomerRepository { + + private final JdbcTemplate jdbcTemplate; + + private RowMapper customerRowMapper() { + return (rs, rowNum) -> { + UUID customerId = UUIDConverter.toUUID(rs.getBytes("customer_id")); + String name = rs.getString("name"); + Email email = Email.valueOf(rs.getString("email")); + CustomerType customerType = CustomerType.valueOf(rs.getInt("isBlack")); + return new Customer(customerId, name, email, customerType); + }; + } + + @Override + public void save(final Customer customer) { + String sql = "INSERT INTO customers (customer_id, name, email, isBlack) " + + "VALUES(UUID_TO_BIN(?), ?, ?, ?)"; + jdbcTemplate.update(sql, + customer.getCustomerId().toString().getBytes(), + customer.getName(), customer.getEmail(), + customer.getCustomerTypeValue()); + } + + @Override + public Optional findByEmail(Email email) { + String sql = "SELECT * FROM customers WHERE email = ?"; + try { + return Optional.ofNullable(jdbcTemplate.queryForObject(sql, customerRowMapper(), + email.getEmail())); + } catch (EmptyResultDataAccessException e) { + log.warn("No customer found for email: {}", email.getEmail()); + } catch (DataAccessException e) { + log.error("Data access exception: {}", e.toString()); + } + return Optional.empty(); + } + + @Override + public Optional findById(UUID customerId) { + String sql = "SELECT * FROM customers WHERE customer_id = UUID_TO_BIN(?)"; + try { + return Optional.ofNullable(jdbcTemplate.queryForObject(sql, customerRowMapper(), + (Object) UUIDConverter.toBytes(customerId))); + } catch (EmptyResultDataAccessException e) { + log.warn("No customer found for customerId: {}", customerId); + } catch (DataAccessException e) { + log.error("Data access Exception: {}", e.toString()); + } + return Optional.empty(); + } + + @Override + public List findAll() { + String sql = "SELECT * FROM customers ORDER BY customer_id"; + return jdbcTemplate.query(sql, customerRowMapper()); + } + + @Override + public List findBlack() { + String sql = "SELECT * FROM customers WHERE isBlack = TRUE"; + return jdbcTemplate.query(sql, customerRowMapper()); + } + + @Override + public void update(Customer customer) { + String sql = "UPDATE customers SET name = ?, email = ?, isBlack = ? " + + "WHERE customer_id = UUID_TO_BIN(?)"; + jdbcTemplate.update(sql, customer.getName(), + customer.getEmail(), + customer.getCustomerTypeValue(), + UUIDConverter.toBytes(customer.getCustomerId())); + } + + @Override + public void delete(Customer customer) { + String sql = "DELETE FROM customers WHERE customer_id = UUID_TO_BIN(?)"; + jdbcTemplate.update(sql, (Object) UUIDConverter.toBytes(customer.getCustomerId())); + } + + @Override + public void deleteAll() { + String sql = "DELETE FROM customers"; + jdbcTemplate.update(sql); + } +} From 145ce53826a0e0a4c134e68f1e86dbf9ce157399 Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Sat, 4 Nov 2023 18:01:25 +0900 Subject: [PATCH 71/86] =?UTF-8?q?feat:=20Wallet=20=EA=B4=80=EB=A0=A8=20Rep?= =?UTF-8?q?ository=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/JdbcWalletRepository.java | 79 +++++++++++++++++++ .../wallet/repository/WalletRepository.java | 18 +++++ 2 files changed, 97 insertions(+) create mode 100644 src/main/java/org/programmers/springboot/basic/domain/wallet/repository/JdbcWalletRepository.java create mode 100644 src/main/java/org/programmers/springboot/basic/domain/wallet/repository/WalletRepository.java diff --git a/src/main/java/org/programmers/springboot/basic/domain/wallet/repository/JdbcWalletRepository.java b/src/main/java/org/programmers/springboot/basic/domain/wallet/repository/JdbcWalletRepository.java new file mode 100644 index 0000000000..d62b66bbc8 --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/domain/wallet/repository/JdbcWalletRepository.java @@ -0,0 +1,79 @@ +package org.programmers.springboot.basic.domain.wallet.repository; + +import lombok.extern.slf4j.Slf4j; +import org.programmers.springboot.basic.domain.customer.entity.vo.Email; +import org.programmers.springboot.basic.domain.wallet.entity.Wallet; +import org.programmers.springboot.basic.util.converter.UUIDConverter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DataAccessException; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Repository; + +import javax.sql.DataSource; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +@Slf4j +@Repository +public class JdbcWalletRepository implements WalletRepository { + + private final JdbcTemplate jdbcTemplate; + + @Autowired + public JdbcWalletRepository(DataSource dataSource) { + this.jdbcTemplate = new JdbcTemplate(dataSource); + } + + public RowMapper walletRowMapper() { + return (rs, rowNum) -> { + UUID voucherId = UUIDConverter.toUUID(rs.getBytes("voucher_id")); + Email email = Email.valueOf(rs.getString("email")); + return new Wallet(voucherId, email); + }; + } + @Override + public void save(Wallet wallet) { + String sql = "INSERT INTO wallets (email, voucher_id) VALUES(?, UUID_TO_BIN(?))"; + this.jdbcTemplate.update(sql, wallet.getEmail().getEmail(), UUIDConverter.toBytes(wallet.getVoucherId())); + } + + @Override + public List findByEmail(Email email) { + String sql = "SELECT * FROM wallets WHERE email = ?"; + return this.jdbcTemplate.query(sql, walletRowMapper(), email.getEmail()); + } + + @Override + public List findById(UUID voucherId) { + String sql = "SELECT * FROM wallets WHERE voucher_id = UUID_TO_BIN(?)"; + return this.jdbcTemplate.query(sql, walletRowMapper(), (Object) UUIDConverter.toBytes(voucherId)); + } + @Override + public Optional findByEmailNId(Email email, UUID voucherId) { + String sql = "SELECT * FROM wallets WHERE email = ? AND voucher_id = UUID_TO_BIN(?)"; + try { + return Optional.ofNullable(this.jdbcTemplate.queryForObject(sql, walletRowMapper(), + email.getEmail(), UUIDConverter.toBytes(voucherId))); + } catch (EmptyResultDataAccessException e) { + log.warn("No customer found for email and voucherId: {}, {}", email.getEmail(), voucherId); + } catch (DataAccessException e) { + log.error("Data access exception: {}", e.toString()); + } + return Optional.empty(); + } + + @Override + public void delete(Wallet wallet) { + String sql = "DELETE FROM wallets WHERE email = ? AND voucher_id = UUID_TO_BIN(?)"; + this.jdbcTemplate.update(sql, wallet.getEmail().getEmail(), UUIDConverter.toBytes(wallet.getVoucherId())); + } + + @Override + public void deleteAll() { + String sql = "TRUNCATE TABLE wallets"; + this.jdbcTemplate.execute(sql); + } +} diff --git a/src/main/java/org/programmers/springboot/basic/domain/wallet/repository/WalletRepository.java b/src/main/java/org/programmers/springboot/basic/domain/wallet/repository/WalletRepository.java new file mode 100644 index 0000000000..a77f17a9d3 --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/domain/wallet/repository/WalletRepository.java @@ -0,0 +1,18 @@ +package org.programmers.springboot.basic.domain.wallet.repository; + +import org.programmers.springboot.basic.domain.customer.entity.vo.Email; +import org.programmers.springboot.basic.domain.wallet.entity.Wallet; + +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +public interface WalletRepository { + + void save(Wallet wallet); + List findByEmail(Email email); + List findById(UUID voucherId); + Optional findByEmailNId(Email email, UUID voucherId); + void delete(Wallet wallet); + void deleteAll(); +} From 31297c0598f48e9b50db945f84e4de80f3864870 Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Sat, 4 Nov 2023 18:01:45 +0900 Subject: [PATCH 72/86] =?UTF-8?q?feat:=20AOP=EB=A5=BC=20=EC=9C=84=ED=95=9C?= =?UTF-8?q?=20Logger=20=EA=B8=B0=EB=8A=A5=EC=9D=84=20=ED=95=98=EB=8A=94=20?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../springboot/basic/aop/LoggerAspect.java | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 src/main/java/org/programmers/springboot/basic/aop/LoggerAspect.java diff --git a/src/main/java/org/programmers/springboot/basic/aop/LoggerAspect.java b/src/main/java/org/programmers/springboot/basic/aop/LoggerAspect.java new file mode 100644 index 0000000000..f3a14e6176 --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/aop/LoggerAspect.java @@ -0,0 +1,56 @@ +package org.programmers.springboot.basic.aop; + +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.*; +import org.springframework.stereotype.Component; + +@Slf4j +@Aspect +@Component +public class LoggerAspect { + + private static void logMethodCall(JoinPoint joinPoint) { + log.warn("'{}' method called in '{}'", + joinPoint.getSignature().getName(), + joinPoint.getSignature().getDeclaringTypeName()); + } + + @Pointcut("execution(void org.programmers.springboot.basic..*Controller.*(..)) || " + + "execution(void org.programmers.springboot.basic..*Repository.*(..)) || " + + "execution(void org.programmers.springboot.basic..*Mapper.*(..)) || " + + "execution(void org.programmers.springboot.basic..*Service.*(..))") + public void voidMethods() {} + + @Pointcut("execution(* org.programmers.springboot.basic..*Controller.*(..)) && !voidMethods() ||" + + "execution(* org.programmers.springboot.basic..*Repository.*(..)) && ! voidMethods() || " + + "execution(* org.programmers.springboot.basic..*Service.*(..)) && ! voidMethods() || " + + "execution(* org.programmers.springboot.basic..*Mapper.*(..)) && ! voidMethods()") + public void nonVoidMethods() {} + + @Pointcut("execution(* org.programmers.springboot.basic..*.*(..))") + public void allMethods() {} + + @Before("voidMethods()") + public void logBefore(JoinPoint joinPoint) { + logMethodCall(joinPoint); + } + + @Around("nonVoidMethods()") + public Object logAroundWithResult(ProceedingJoinPoint joinPoint) throws Throwable { + log.info("'{}' method called in {}", + joinPoint.getSignature().getName(), + joinPoint.getSignature().getDeclaringTypeName()); + Object result = joinPoint.proceed(); + log.info("After '{}' method called with result '{}'", + joinPoint.getSignature().getName(), + result != null ? result.toString() : "null"); + return result; + } + + @AfterThrowing(pointcut = "allMethods()", throwing = "runtimeException") + public void logException(RuntimeException runtimeException) { + log.warn(runtimeException.getMessage()); + } +} From 8fed624d041fac66e4d2d0035034746b5e72ce3a Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Sat, 4 Nov 2023 18:02:13 +0900 Subject: [PATCH 73/86] =?UTF-8?q?feat:=20Profile=EC=97=90=20=EB=94=B0?= =?UTF-8?q?=EB=A5=B8=20=EB=B9=88=20=EB=93=B1=EB=A1=9D=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=20=E3=85=8A=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../springboot/basic/config/AppConfig.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 src/main/java/org/programmers/springboot/basic/config/AppConfig.java diff --git a/src/main/java/org/programmers/springboot/basic/config/AppConfig.java b/src/main/java/org/programmers/springboot/basic/config/AppConfig.java new file mode 100644 index 0000000000..12e55e6b5f --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/config/AppConfig.java @@ -0,0 +1,18 @@ +package org.programmers.springboot.basic.config; + + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; +import org.springframework.web.filter.HiddenHttpMethodFilter; + +@Profile("default") +@Configuration +public class AppConfig { + + @Bean + public HiddenHttpMethodFilter hiddenHttpMethodFilter() { + return new HiddenHttpMethodFilter(); + } +} + From 1bc56bf38bb53f06df366aa46b48a8f885934617 Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Sat, 4 Nov 2023 18:02:13 +0900 Subject: [PATCH 74/86] =?UTF-8?q?feat:=20Profile=EC=97=90=20=EB=94=B0?= =?UTF-8?q?=EB=A5=B8=20=EB=B9=88=20=EB=93=B1=EB=A1=9D=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../springboot/basic/config/AppConfig.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 src/main/java/org/programmers/springboot/basic/config/AppConfig.java diff --git a/src/main/java/org/programmers/springboot/basic/config/AppConfig.java b/src/main/java/org/programmers/springboot/basic/config/AppConfig.java new file mode 100644 index 0000000000..12e55e6b5f --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/config/AppConfig.java @@ -0,0 +1,18 @@ +package org.programmers.springboot.basic.config; + + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; +import org.springframework.web.filter.HiddenHttpMethodFilter; + +@Profile("default") +@Configuration +public class AppConfig { + + @Bean + public HiddenHttpMethodFilter hiddenHttpMethodFilter() { + return new HiddenHttpMethodFilter(); + } +} + From 7a1d2bc8878941a1cb75f5f7c9f92908ca8c4c4b Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Sat, 4 Nov 2023 18:03:48 +0900 Subject: [PATCH 75/86] =?UTF-8?q?feat:=20web=EC=97=90=EC=84=9C=20=EC=A0=84?= =?UTF-8?q?=EB=8B=AC=EB=B0=9B=EC=9D=80=20=EB=8D=B0=EC=9D=B4=ED=84=B0?= =?UTF-8?q?=EB=A5=BC=20=EB=8B=B4=EC=95=84=EB=91=90=EB=8A=94=20Dto=20?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=20=E3=85=8A=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../voucher/dto/VoucherControllerRequestDto.java | 14 ++++++++++++++ .../wallet/dto/WalletControllerRequestDto.java | 12 ++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 src/main/java/org/programmers/springboot/basic/domain/voucher/dto/VoucherControllerRequestDto.java create mode 100644 src/main/java/org/programmers/springboot/basic/domain/wallet/dto/WalletControllerRequestDto.java diff --git a/src/main/java/org/programmers/springboot/basic/domain/voucher/dto/VoucherControllerRequestDto.java b/src/main/java/org/programmers/springboot/basic/domain/voucher/dto/VoucherControllerRequestDto.java new file mode 100644 index 0000000000..cc23fa9a23 --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/domain/voucher/dto/VoucherControllerRequestDto.java @@ -0,0 +1,14 @@ +package org.programmers.springboot.basic.domain.voucher.dto; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public class VoucherControllerRequestDto { + + private final String voucherId; + private final String discount; + private final String voucherType; + private final String createdAt; +} diff --git a/src/main/java/org/programmers/springboot/basic/domain/wallet/dto/WalletControllerRequestDto.java b/src/main/java/org/programmers/springboot/basic/domain/wallet/dto/WalletControllerRequestDto.java new file mode 100644 index 0000000000..a582e28660 --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/domain/wallet/dto/WalletControllerRequestDto.java @@ -0,0 +1,12 @@ +package org.programmers.springboot.basic.domain.wallet.dto; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public class WalletControllerRequestDto { + + private final String voucherId; + private final String email; +} From 051959d3d6035c7eea43f90c3061d039a951c80e Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Sat, 4 Nov 2023 18:05:03 +0900 Subject: [PATCH 76/86] =?UTF-8?q?feat:=20=ED=98=84=EC=9E=AC=20active?= =?UTF-8?q?=EB=90=9C=20Profile=EC=97=90=20=EB=94=B0=EB=A5=B8=20=EB=B9=88?= =?UTF-8?q?=20=EB=93=B1=EB=A1=9D=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../basic/VoucherManagementApplication.java | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/main/java/org/programmers/springboot/basic/VoucherManagementApplication.java b/src/main/java/org/programmers/springboot/basic/VoucherManagementApplication.java index c0918fec96..1455b047b4 100644 --- a/src/main/java/org/programmers/springboot/basic/VoucherManagementApplication.java +++ b/src/main/java/org/programmers/springboot/basic/VoucherManagementApplication.java @@ -1,13 +1,36 @@ package org.programmers.springboot.basic; +import lombok.RequiredArgsConstructor; +import org.programmers.springboot.basic.util.manager.CommandManager; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; +import org.springframework.boot.web.servlet.server.ServletWebServerFactory; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Profile; @SpringBootApplication +@RequiredArgsConstructor public class VoucherManagementApplication { + private final ApplicationContext applicationContext; + public static void main(String[] args) { SpringApplication.run(VoucherManagementApplication.class, args); } + @Profile(value = "web") + @Bean + public ServletWebServerFactory tomcatServetContainer() { + return new TomcatServletWebServerFactory(); + } + + @Profile(value = "command") + @Bean + public void init() { + CommandManager commandManager = applicationContext.getBean(CommandManager.class); + commandManager.run(); + System.exit(SpringApplication.exit(applicationContext, () -> 0)); + } } From 78c33896657271e247376a01d536980a981d0c3a Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Sat, 4 Nov 2023 18:05:57 +0900 Subject: [PATCH 77/86] =?UTF-8?q?feat:=20Dto=EC=99=80=20Entity=EA=B0=84?= =?UTF-8?q?=EC=9D=98=20=EB=A7=A4=ED=95=91=EC=9D=84=20=EB=8B=B4=EB=8B=B9?= =?UTF-8?q?=ED=95=98=EB=8A=94=20Mapper=20=ED=81=B4=EB=9E=98=EC=8A=A4=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../customer/mapper/CustomerMapper.java | 51 +++++++++ .../domain/voucher/mapper/VoucherMapper.java | 100 ++++++++++++------ .../domain/wallet/mapper/WalletMapper.java | 44 ++++++++ 3 files changed, 161 insertions(+), 34 deletions(-) create mode 100644 src/main/java/org/programmers/springboot/basic/domain/customer/mapper/CustomerMapper.java create mode 100644 src/main/java/org/programmers/springboot/basic/domain/wallet/mapper/WalletMapper.java diff --git a/src/main/java/org/programmers/springboot/basic/domain/customer/mapper/CustomerMapper.java b/src/main/java/org/programmers/springboot/basic/domain/customer/mapper/CustomerMapper.java new file mode 100644 index 0000000000..f6bf3b117c --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/domain/customer/mapper/CustomerMapper.java @@ -0,0 +1,51 @@ +package org.programmers.springboot.basic.domain.customer.mapper; + +import org.mapstruct.Context; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.ReportingPolicy; +import org.mapstruct.factory.Mappers; +import org.programmers.springboot.basic.domain.customer.dto.CustomerRequestDto; +import org.programmers.springboot.basic.domain.customer.dto.CustomerResponseDto; +import org.programmers.springboot.basic.domain.customer.entity.Customer; +import org.programmers.springboot.basic.domain.customer.entity.vo.Email; +import org.programmers.springboot.basic.util.generator.UUIDGenerator; + +import java.util.UUID; + +@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE) +public interface CustomerMapper { + + CustomerMapper INSTANCE = Mappers.getMapper(CustomerMapper.class); + + @Mapping(target = "name", source = "name") + @Mapping(target = "email", expression = "java(mapToEmail(email))") + @Mapping(target = "customerType", constant = "NORMAL") + CustomerRequestDto mapToRequestDto(String name, String email); + + @Mapping(target = "customerId", source = "customerId") + CustomerRequestDto mapToRequestDtoWithUUID(String customerId); + + CustomerResponseDto mapToResponseDto(Customer customer); + + @Mapping(target = "customerId", expression = "java(generateUUID(uuidGenerator))") + Customer mapTopEntityWithGenerator(CustomerRequestDto customerRequestDto, @Context UUIDGenerator uuidGenerator); + + @Mapping(target = "customerId", expression = "java(generateUUID(uuidGenerator))") + @Mapping(target = "name", source = "name") + @Mapping(target = "email", source = "email") + @Mapping(target = "customerType", constant = "NORMAL") + Customer mapToEntityAllArgsWithGenerator(CustomerRequestDto customerRequestDto, @Context UUIDGenerator uuidGenerator); + + default UUID mapToUUID(String value) { + return UUID.fromString(value); + } + + default UUID generateUUID(@Context UUIDGenerator uuidGenerator) { + return uuidGenerator.generateUUID(); + } + + default Email mapToEmail(String email) { + return Email.valueOf(email); + } +} diff --git a/src/main/java/org/programmers/springboot/basic/domain/voucher/mapper/VoucherMapper.java b/src/main/java/org/programmers/springboot/basic/domain/voucher/mapper/VoucherMapper.java index d22ab5c805..3f02c1eb28 100644 --- a/src/main/java/org/programmers/springboot/basic/domain/voucher/mapper/VoucherMapper.java +++ b/src/main/java/org/programmers/springboot/basic/domain/voucher/mapper/VoucherMapper.java @@ -1,60 +1,92 @@ package org.programmers.springboot.basic.domain.voucher.mapper; -import org.programmers.springboot.basic.AppConstants; -import org.programmers.springboot.basic.config.VoucherConfig; -import org.programmers.springboot.basic.domain.voucher.dto.CsvVoucherDto; +import org.mapstruct.*; +import org.mapstruct.factory.Mappers; +import org.programmers.springboot.basic.domain.voucher.dto.VoucherControllerRequestDto; import org.programmers.springboot.basic.domain.voucher.dto.VoucherRequestDto; import org.programmers.springboot.basic.domain.voucher.dto.VoucherResponseDto; import org.programmers.springboot.basic.domain.voucher.entity.Voucher; import org.programmers.springboot.basic.domain.voucher.entity.VoucherType; -import org.springframework.stereotype.Component; +import org.programmers.springboot.basic.domain.voucher.service.validate.VoucherValidationStrategy; +import org.programmers.springboot.basic.util.generator.DateTimeGenerator; +import org.programmers.springboot.basic.util.generator.UUIDGenerator; +import java.time.LocalDateTime; +import java.util.Map; import java.util.UUID; -@Component -public class VoucherMapper { +@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE) +public interface VoucherMapper { - private final VoucherConfig voucherConfig; + VoucherMapper INSTANCE = Mappers.getMapper(VoucherMapper.class); - public VoucherMapper(VoucherConfig voucherConfig) { - this.voucherConfig = voucherConfig; - } + @Mapping(target = "voucherId", expression = "java(mapToUUID(voucherId))") + @Mapping(target = "voucherType", expression = "java(mapToVoucherType(voucherType))") + @Mapping(target = "discount", expression = "java(mapToLong(discount))") + @Mapping(target = "createdAt", expression = "java(mapToDateTime(createdAt))") + VoucherRequestDto mapToRequestDtoWithAllArgs(String voucherId, String voucherType, String discount, String createdAt); - public String serialize(Voucher voucher) { + @Mapping(target = "voucherId", expression = "java(mapToUUID(requestDto.getVoucherId()))") + @Mapping(target = "voucherType", expression = "java(mapToVoucherType(requestDto.getVoucherType()))") + @Mapping(target = "discount", expression = "java(mapToLong(requestDto.getDiscount()))") + @Mapping(target = "createdAt", expression = "java(mapToDateTime(requestDto.getCreatedAt()))") + VoucherRequestDto mapToRequestDtoWithControllerDto(VoucherControllerRequestDto requestDto); - return voucher.getVoucherId() + - AppConstants.CSV_SEPARATOR + - voucher.getVoucherType() + - AppConstants.CSV_SEPARATOR + - voucher.getDiscount(); - } + @Mapping(target = "voucherType", source = "voucherType") + @Mapping(target = "discount", source = "discount") + VoucherRequestDto mapToRequestDto(VoucherType voucherType, Long discount); + + @Mapping(target = "voucherId", expression = "java(mapToUUID(voucherId))") + @Mapping(target = "discount", source = "discount") + VoucherRequestDto mapToRequestDtoWithIdNDiscount(String voucherId, Long discount); - public CsvVoucherDto deserialize(String[] token) { + @Mapping(target = "voucherId", expression = "java(mapToUUID(voucherId))") + VoucherRequestDto mapToRequestDtoWithUUID(String voucherId); - UUID voucherId = UUID.fromString(token[0]); - VoucherType voucherType = VoucherType.valueOf(token[1]); - Long discount = Long.parseLong(token[2]); + @Mapping(target = "voucherId", expression = "java(generateUUID(uuidGenerator))") + @Mapping(target = "createdAt", expression = "java(generateDateTime(dateTimeGenerator))") + Voucher mapToEntityWithGenerator(VoucherRequestDto requestDto, @Context UUIDGenerator uuidGenerator, + @Context DateTimeGenerator dateTimeGenerator, + @Context Map validationStrategyMap); - return voucherConfig.getCsvVoucherDto(voucherId, voucherType, discount); + VoucherResponseDto mapToResponseDto(Voucher voucher); + + Voucher mapToEntity(VoucherRequestDto voucherRequestDto); + + default UUID mapToUUID(String voucherId) { + return voucherId != null ? UUID.fromString(voucherId) : null; } - public Voucher mapToVoucher(CsvVoucherDto csvVoucherDto) { + default LocalDateTime mapToDateTime(String createdAt) { + return createdAt != null ? LocalDateTime.parse(createdAt) : null; + } - return switch (csvVoucherDto.voucherType()) { - case FIXED -> voucherConfig.getFixedAmountVoucher(csvVoucherDto); - case PERCENT -> voucherConfig.getPercentDiscountVoucher(csvVoucherDto); - }; + default Long mapToLong(String discount) { + return discount != null ? Long.parseLong(discount) : null; } - public Voucher mapToVoucher(VoucherRequestDto voucherRequestDto) { + default VoucherType mapToVoucherType(String voucherType) { + return voucherType != null ? VoucherType.valueOf(voucherType) : null; + } + + default UUID generateUUID(@Context UUIDGenerator uuidGenerator) { + return uuidGenerator.generateUUID(); + } + + default LocalDateTime generateDateTime(@Context DateTimeGenerator dateTimeGenerator) { + return dateTimeGenerator.generateDateTime(); + } - return switch (voucherRequestDto.voucherType()) { - case FIXED -> voucherConfig.getFixedAmountVoucher(voucherRequestDto); - case PERCENT -> voucherConfig.getPercentDiscountVoucher(voucherRequestDto); - }; + default VoucherValidationStrategy generateValidator(VoucherType voucherType, @Context Map validationStrategyMap) { + return validationStrategyMap.get(voucherType); } - public VoucherResponseDto mapToVoucherDto(Voucher voucher) { - return voucherConfig.getVoucherResponseDto(voucher); + @AfterMapping + default void setValidationStrategy(@MappingTarget Voucher voucher, + VoucherRequestDto requestDto, + @Context Map validationStrategyMap) { + VoucherType voucherType = requestDto.getVoucherType(); + VoucherValidationStrategy strategy = validationStrategyMap.get(voucherType); + voucher.setValidationStrategy(strategy); } } diff --git a/src/main/java/org/programmers/springboot/basic/domain/wallet/mapper/WalletMapper.java b/src/main/java/org/programmers/springboot/basic/domain/wallet/mapper/WalletMapper.java new file mode 100644 index 0000000000..e7dd286f61 --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/domain/wallet/mapper/WalletMapper.java @@ -0,0 +1,44 @@ +package org.programmers.springboot.basic.domain.wallet.mapper; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.ReportingPolicy; +import org.mapstruct.factory.Mappers; +import org.programmers.springboot.basic.domain.customer.entity.vo.Email; +import org.programmers.springboot.basic.domain.wallet.dto.WalletRequestDto; +import org.programmers.springboot.basic.domain.wallet.dto.WalletResponseDto; +import org.programmers.springboot.basic.domain.wallet.entity.Wallet; + +import java.util.UUID; + +@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE) +public interface WalletMapper { + + WalletMapper INSTANCE = Mappers.getMapper(WalletMapper.class); + + @Mapping(target = "voucherId", expression = "java(mapToUUID(voucherId))") + @Mapping(target = "email", expression = "java(mapToEmail(email))") + WalletRequestDto mapToRequestDto(String voucherId, String email); + + @Mapping(target = "voucherId", source = "voucherId") + @Mapping(target = "email", source = "email") + Wallet mapToEntity(UUID voucherId, Email email); + + @Mapping(target = "email", source = "email") + WalletRequestDto mapToRequestDtoWithEmail(String email); + + @Mapping(target = "voucherId", expression = "java(mapToUUID(voucherId))") + WalletRequestDto mapToRequestDtoWithUUID(String voucherId); + + Wallet mapToEntity(WalletRequestDto walletRequestDto); + + WalletResponseDto mapToResponseDto(Wallet wallet); + + default UUID mapToUUID(String value) { + return UUID.fromString(value); + } + + default Email mapToEmail(String value) { + return Email.valueOf(value); + } +} From 1d8a59770b093deeaf77cb149b25936dd43cd637 Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Sat, 4 Nov 2023 18:07:11 +0900 Subject: [PATCH 78/86] =?UTF-8?q?feat:=20=EC=97=90=EB=9F=AC=20=EB=B0=9C?= =?UTF-8?q?=EC=83=9D=20=EC=8B=9C=EC=97=90=20=EB=B0=98=ED=99=98=ED=95=A0=20?= =?UTF-8?q?ResponseEntity=EB=A5=BC=20=EC=9C=84=ED=95=9C=20ErrResponse,=20E?= =?UTF-8?q?rrCode=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../basic/util/response/ErrCode.java | 25 +++++++++++++++++++ .../basic/util/response/ErrResponse.java | 15 +++++++++++ 2 files changed, 40 insertions(+) create mode 100644 src/main/java/org/programmers/springboot/basic/util/response/ErrCode.java create mode 100644 src/main/java/org/programmers/springboot/basic/util/response/ErrResponse.java diff --git a/src/main/java/org/programmers/springboot/basic/util/response/ErrCode.java b/src/main/java/org/programmers/springboot/basic/util/response/ErrCode.java new file mode 100644 index 0000000000..806148454b --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/util/response/ErrCode.java @@ -0,0 +1,25 @@ +package org.programmers.springboot.basic.util.response; + +public enum ErrCode { + + NOT_FOUND(404, "존재하지 않는 리소스"), + BAD_REQUEST(400, "유효하지 않은 타입"), + INPUT_INVALID_VALUE(401, "유효하지 않은 범위"), + INTERNAL_SERVER_ERROR(500, "서버 오류"); + + private final int status; + private final String message; + + ErrCode(int status, String message) { + this.status = status; + this.message = message; + } + + public int getStatus() { + return status; + } + + public String getMessage() { + return message; + } +} diff --git a/src/main/java/org/programmers/springboot/basic/util/response/ErrResponse.java b/src/main/java/org/programmers/springboot/basic/util/response/ErrResponse.java new file mode 100644 index 0000000000..6e02908697 --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/util/response/ErrResponse.java @@ -0,0 +1,15 @@ +package org.programmers.springboot.basic.util.response; + +import lombok.Getter; + +@Getter +public class ErrResponse { + + private final ErrCode code; + private final String message; + + public ErrResponse(ErrCode code, String message) { + this.code = code; + this.message = message; + } +} From 30b4549c19ff0e712ac61ef741e45a7d5fff3a42 Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Sat, 4 Nov 2023 18:08:07 +0900 Subject: [PATCH 79/86] =?UTF-8?q?feat:=20=EC=95=A0=ED=94=8C=EB=A6=AC?= =?UTF-8?q?=EC=BC=80=EC=9D=B4=EC=85=98=EC=9D=84=20=EC=95=84=EC=9A=B0?= =?UTF-8?q?=EB=A5=B4=EB=8A=94=20Controller,=20=EB=A9=94=EC=9D=B8=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=EB=A5=BC=20=EB=A7=A4=ED=95=91?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../basic/web/ApplicationController.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 src/main/java/org/programmers/springboot/basic/web/ApplicationController.java diff --git a/src/main/java/org/programmers/springboot/basic/web/ApplicationController.java b/src/main/java/org/programmers/springboot/basic/web/ApplicationController.java new file mode 100644 index 0000000000..52a79e0bcf --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/web/ApplicationController.java @@ -0,0 +1,15 @@ +package org.programmers.springboot.basic.web; + +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; + +@Profile("default") +@Controller +public class ApplicationController { + + @GetMapping + public String home() { + return "home"; + } +} From 34ffcff452c0db2d4ab80410807c81630f52d5b6 Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Sat, 4 Nov 2023 18:12:00 +0900 Subject: [PATCH 80/86] =?UTF-8?q?feat:=20=EC=A4=91=EB=B3=B5=20=EC=B2=B4?= =?UTF-8?q?=ED=81=AC=EB=A5=BC=20=EC=9C=84=ED=95=9C=20Duplicate=20=EB=A9=94?= =?UTF-8?q?=EC=84=9C=EB=93=9C,=20TDA=EB=A5=BC=20=EC=A0=81=EC=9A=A9?= =?UTF-8?q?=ED=95=9C=20Email=20=EA=B2=80=EC=A6=9D=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../customer/service/CustomerService.java | 115 +++++++++++++++--- 1 file changed, 101 insertions(+), 14 deletions(-) diff --git a/src/main/java/org/programmers/springboot/basic/domain/customer/service/CustomerService.java b/src/main/java/org/programmers/springboot/basic/domain/customer/service/CustomerService.java index 694d798abc..c17a0dbc34 100644 --- a/src/main/java/org/programmers/springboot/basic/domain/customer/service/CustomerService.java +++ b/src/main/java/org/programmers/springboot/basic/domain/customer/service/CustomerService.java @@ -1,38 +1,125 @@ package org.programmers.springboot.basic.domain.customer.service; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.programmers.springboot.basic.config.CustomerConfig; +import org.programmers.springboot.basic.domain.customer.dto.CustomerRequestDto; import org.programmers.springboot.basic.domain.customer.dto.CustomerResponseDto; import org.programmers.springboot.basic.domain.customer.entity.Customer; -import org.programmers.springboot.basic.domain.customer.exception.CustomerNotFoundException; +import org.programmers.springboot.basic.domain.customer.entity.vo.Email; +import org.programmers.springboot.basic.domain.customer.exception.*; +import org.programmers.springboot.basic.domain.customer.mapper.CustomerMapper; import org.programmers.springboot.basic.domain.customer.repository.CustomerRepository; +import org.programmers.springboot.basic.util.generator.UUIDGenerator; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import java.util.List; -import java.util.stream.Collectors; +import java.util.UUID; @Slf4j @Service +@RequiredArgsConstructor public class CustomerService { private final CustomerRepository customerRepository; - private final CustomerConfig customerConfig; + private final UUIDGenerator uuidGenerator; - public CustomerService(CustomerRepository customerRepository, CustomerConfig customerConfig) { - this.customerRepository = customerRepository; - this.customerConfig = customerConfig; - } + @Transactional + public void create(CustomerRequestDto customerRequestDto) { - public List findBlackList() { + Email email = customerRequestDto.getEmail(); - List customers = this.customerRepository.findBlackList(); + email.validate(); + Customer customer = CustomerMapper.INSTANCE.mapToEntityAllArgsWithGenerator(customerRequestDto, uuidGenerator); - if (customers.isEmpty()) { - throw new CustomerNotFoundException(); + if (isDuplicate(customer)) { + log.warn("Duplicate email {} is already exists", email); + throw new DuplicateEmailException(); + } else { + customerRepository.save(customer); } + } + + private boolean isDuplicate(Customer customer) { + return customerRepository.findByEmail(customer.getEmail()).isPresent(); + } + + public List findAllCustomer() { + + List customers = findAll(); + return customers.stream() + .map(CustomerMapper.INSTANCE::mapToResponseDto) + .toList(); + } + + public List findByCustomerIsBlack() { + List customers = findBlackCustomer(); return customers.stream() - .map(customerConfig::getCustomerResponseDto) - .collect(Collectors.toList()); + .map(CustomerMapper.INSTANCE::mapToResponseDto) + .toList(); + } + + @Transactional + public void addBlackCustomer(CustomerRequestDto customerRequestDto) { + + UUID customerId = customerRequestDto.getCustomerId(); + + Customer customer = findById(customerId); + customer.setBlack(); + customerRepository.update(customer); + } + + @Transactional + public void deleteCustomer(CustomerRequestDto customerRequestDto) { + + UUID customerId = customerRequestDto.getCustomerId(); + + Customer customer = findById(customerId); + customerRepository.delete(customer); + } + + @Transactional + public void deleteBlackCustomer(CustomerRequestDto customerRequestDto) { + + UUID customerId = customerRequestDto.getCustomerId(); + + Customer customer = findById(customerId); + customer.setNormal(); + customerRepository.update(customer); + } + + public Customer findById(UUID customerId) { + + return customerRepository.findById(customerId) + .orElseThrow(() -> { + log.warn("No customer found for customerId: {}", customerId); + return new CustomerNotFoundException(); + }); + } + + public CustomerResponseDto findByEmail(Email email) { + + email.validate(); + Customer customer = customerRepository.findByEmail(email) + .orElseThrow(() -> { + log.warn("No customer found for email: {}", email); + return new CustomerNotFoundException(); + }); + return CustomerMapper.INSTANCE.mapToResponseDto(customer); + } + + + public List findAll() { + return customerRepository.findAll(); + } + + public List findBlackCustomer() { + return customerRepository.findBlack(); + } + + @Transactional + public void deleteAll() { + customerRepository.deleteAll(); } } From 0856fd60960d278462237e986b7a3fa724370ad9 Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Sat, 4 Nov 2023 18:12:30 +0900 Subject: [PATCH 81/86] =?UTF-8?q?feat:=20=EC=A4=91=EB=B3=B5=20=EC=B2=B4?= =?UTF-8?q?=ED=81=AC=EB=A5=BC=20=EC=9C=84=ED=95=9C=20Duplicate=20=EB=A9=94?= =?UTF-8?q?=EC=84=9C=EB=93=9C,=20TDA=EB=A5=BC=20=EC=A0=81=EC=9A=A9?= =?UTF-8?q?=ED=95=9C=20Discount=20=EC=9C=A0=ED=9A=A8=EB=B2=94=EC=9C=84=20?= =?UTF-8?q?=EA=B2=80=EC=A6=9D=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../voucher/service/VoucherService.java | 122 +++++++++++++----- 1 file changed, 89 insertions(+), 33 deletions(-) diff --git a/src/main/java/org/programmers/springboot/basic/domain/voucher/service/VoucherService.java b/src/main/java/org/programmers/springboot/basic/domain/voucher/service/VoucherService.java index 1035c3f728..ef24d2f34d 100644 --- a/src/main/java/org/programmers/springboot/basic/domain/voucher/service/VoucherService.java +++ b/src/main/java/org/programmers/springboot/basic/domain/voucher/service/VoucherService.java @@ -1,69 +1,125 @@ package org.programmers.springboot.basic.domain.voucher.service; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.programmers.springboot.basic.domain.voucher.dto.VoucherRequestDto; import org.programmers.springboot.basic.domain.voucher.dto.VoucherResponseDto; import org.programmers.springboot.basic.domain.voucher.entity.Voucher; import org.programmers.springboot.basic.domain.voucher.entity.VoucherType; +import org.programmers.springboot.basic.domain.voucher.exception.DuplicateVoucherException; import org.programmers.springboot.basic.domain.voucher.exception.VoucherNotFoundException; -import org.programmers.springboot.basic.domain.voucher.exception.VoucherValidatorNotFoundException; import org.programmers.springboot.basic.domain.voucher.mapper.VoucherMapper; +import org.programmers.springboot.basic.util.generator.DateTimeGenerator; +import org.programmers.springboot.basic.util.generator.UUIDGenerator; import org.programmers.springboot.basic.domain.voucher.repository.VoucherRepository; -import org.programmers.springboot.basic.domain.voucher.service.validate.ValidateHandler; -import org.programmers.springboot.basic.util.exception.CSVFileIOFailureException; +import org.programmers.springboot.basic.domain.voucher.service.validate.VoucherValidationStrategy; +import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import java.time.LocalDateTime; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; +import java.util.UUID; +@Profile("default") @Slf4j @Service +@RequiredArgsConstructor public class VoucherService { - private final Map validateHandlers; - private final VoucherMapper voucherMapper; private final VoucherRepository voucherRepository; + private final UUIDGenerator uuidGenerator; + private final DateTimeGenerator dateTimeGenerator; + private final Map validationStrategyMap; - public VoucherService(Map validateHandlers, VoucherMapper voucherMapper, VoucherRepository voucherRepository) { - this.validateHandlers = validateHandlers; - this.voucherMapper = voucherMapper; - this.voucherRepository = voucherRepository; - } - + @Transactional public void create(VoucherRequestDto voucherRequestDto) { - Long discount = voucherRequestDto.discount(); - VoucherType voucherType = voucherRequestDto.voucherType(); + Long discount = voucherRequestDto.getDiscount(); + VoucherType voucherType = voucherRequestDto.getVoucherType(); - ValidateHandler handler = validateHandlers.get(voucherType); - - if (handler == null) { - throw new VoucherValidatorNotFoundException(); + Voucher voucher = VoucherMapper.INSTANCE.mapToEntityWithGenerator(voucherRequestDto, + uuidGenerator, + dateTimeGenerator, + validationStrategyMap); + voucher.validate(); + if (isDuplicate(voucher)) { + log.warn("voucher of voucherType '{}' and discount '{}' is already exists", voucherType, discount); + throw new DuplicateVoucherException(); } + voucherRepository.save(voucher); + } - handler.validate(discount); - Voucher voucher = voucherMapper.mapToVoucher(voucherRequestDto); - this.voucherRepository.save(voucher); + private boolean isDuplicate(Voucher voucher) { + return voucherRepository.findByTypeNDiscount(voucher.getVoucherType(), voucher.getDiscount()).isPresent(); } public List findAll() { - List vouchers; + List vouchers = getAll(); + return vouchers.stream() + .map(VoucherMapper.INSTANCE::mapToResponseDto) + .toList(); + } - try { - vouchers = voucherRepository.findAll(); - } catch (CSVFileIOFailureException e) { - log.error(e.toString()); - throw new VoucherNotFoundException(); - } + public VoucherResponseDto findById(VoucherRequestDto voucherRequestDto) { - if (vouchers.isEmpty()) { - throw new VoucherNotFoundException(); - } + UUID voucherId = voucherRequestDto.getVoucherId(); + + Voucher voucher = get(voucherId); + return VoucherMapper.INSTANCE.mapToResponseDto(voucher); + } + public List findByOption(VoucherRequestDto voucherRequestDto) { + + UUID voucherId = voucherRequestDto.getVoucherId(); + VoucherType voucherType = voucherRequestDto.getVoucherType(); + Long discount = voucherRequestDto.getDiscount(); + LocalDateTime createdAt = voucherRequestDto.getCreatedAt(); + List vouchers = getByOption(voucherId, voucherType, discount, createdAt); return vouchers.stream() - .map(voucherMapper::mapToVoucherDto) - .collect(Collectors.toList()); + .map(VoucherMapper.INSTANCE::mapToResponseDto) + .toList(); + } + + @Transactional + public void deleteVoucher(VoucherRequestDto voucherRequestDto) { + VoucherResponseDto voucherResponseDto = findById(voucherRequestDto); + delete(voucherResponseDto.getVoucherId()); + } + + @Transactional + public void updateVoucher(VoucherRequestDto voucherRequestDto) { + Voucher voucher = VoucherMapper.INSTANCE.mapToEntity(voucherRequestDto); + voucherRepository.update(voucher); + } + + private Voucher get(UUID voucherId) { + return voucherRepository.findById(voucherId) + .orElseThrow(() -> { + log.warn("No voucher found for voucherId: {}" , voucherId); + return new VoucherNotFoundException(); + }); + } + + private List getByOption(UUID voucherId, + VoucherType voucherType, + Long discount, + LocalDateTime createdAt) { + return voucherRepository.find(voucherId, voucherType, discount, createdAt); + } + + private List getAll() { + return voucherRepository.findAll(); + } + + private void delete(UUID voucherId) { + voucherRepository.delete(voucherId); + } + + @Transactional + public void deleteAll() { + voucherRepository.deleteAll(); } } From 7072f49ba3132547a0af4f4b03234ae84a940c31 Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Sat, 4 Nov 2023 18:13:03 +0900 Subject: [PATCH 82/86] =?UTF-8?q?feat:=20Wallet=20=EA=B4=80=EB=A0=A8=20?= =?UTF-8?q?=EC=84=9C=EB=B9=84=EC=8A=A4=20=EB=A1=9C=EC=A7=81=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80,=20TDA=EB=A5=BC=20=EC=A0=81=EC=9A=A9=ED=95=9C=20Email?= =?UTF-8?q?=20=EC=9C=A0=ED=9A=A8=EB=B2=94=EC=9C=84=20=EA=B2=80=EC=A6=9D=20?= =?UTF-8?q?=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/wallet/service/WalletService.java | 134 ++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 src/main/java/org/programmers/springboot/basic/domain/wallet/service/WalletService.java diff --git a/src/main/java/org/programmers/springboot/basic/domain/wallet/service/WalletService.java b/src/main/java/org/programmers/springboot/basic/domain/wallet/service/WalletService.java new file mode 100644 index 0000000000..90db83e716 --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/domain/wallet/service/WalletService.java @@ -0,0 +1,134 @@ +package org.programmers.springboot.basic.domain.wallet.service; + +import lombok.extern.slf4j.Slf4j; +import org.programmers.springboot.basic.domain.customer.dto.CustomerResponseDto; +import org.programmers.springboot.basic.domain.customer.entity.Customer; +import org.programmers.springboot.basic.domain.customer.entity.vo.Email; +import org.programmers.springboot.basic.domain.customer.exception.CustomerNotFoundException; +import org.programmers.springboot.basic.domain.customer.mapper.CustomerMapper; +import org.programmers.springboot.basic.domain.customer.repository.JdbcCustomerRepository; +import org.programmers.springboot.basic.domain.voucher.dto.VoucherResponseDto; +import org.programmers.springboot.basic.domain.voucher.entity.Voucher; +import org.programmers.springboot.basic.domain.voucher.exception.VoucherNotFoundException; +import org.programmers.springboot.basic.domain.voucher.mapper.VoucherMapper; +import org.programmers.springboot.basic.domain.voucher.repository.VoucherRepository; +import org.programmers.springboot.basic.domain.wallet.dto.WalletRequestDto; +import org.programmers.springboot.basic.domain.wallet.dto.WalletResponseDto; +import org.programmers.springboot.basic.domain.wallet.entity.Wallet; +import org.programmers.springboot.basic.domain.wallet.exception.DuplicateWalletException; +import org.programmers.springboot.basic.domain.wallet.mapper.WalletMapper; +import org.programmers.springboot.basic.domain.wallet.repository.WalletRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.UUID; + +@Slf4j +@Service +public class WalletService { + + private final WalletRepository walletRepository; + private final VoucherRepository voucherRepository; + private final JdbcCustomerRepository customerRepository; + + @Autowired + public WalletService(WalletRepository walletRepository, VoucherRepository voucherRepository, JdbcCustomerRepository customerRepository) { + this.walletRepository = walletRepository; + this.voucherRepository = voucherRepository; + this.customerRepository = customerRepository; + } + + @Transactional + public void createWallet(WalletRequestDto walletRequestDto) { + + Customer customer = findCustomerByEmail(walletRequestDto); + Voucher voucher = findVoucherById(walletRequestDto); + if (isDuplicate(customer, voucher)) { + log.warn("email {} and voucherId {} is already exists in Wallet", + customer.getEmail().getEmail(), customer.getCustomerId()); + throw new DuplicateWalletException(); + } + + Wallet wallet = WalletMapper.INSTANCE.mapToEntity(walletRequestDto); + this.walletRepository.save(wallet); + } + + private boolean isDuplicate(Customer customer, Voucher voucher) { + return walletRepository.findByEmailNId(customer.getEmail(), voucher.getVoucherId()).isPresent(); + } + + public List walletListByEmail(WalletRequestDto walletRequestDto) { + + Customer customer = findCustomerByEmail(walletRequestDto); + Email email = customer.getEmail(); + + email.validate(); + + List wallets = this.walletRepository.findByEmail(email); + return wallets.stream() + .map(WalletMapper.INSTANCE::mapToResponseDto) + .toList(); + } + + public List voucherListFromWallet(List walletResponseDtos) { + + return walletResponseDtos.stream() + .map(WalletResponseDto::getVoucherId) + .map(voucherRepository::findById) + .map(voucher -> voucher.orElse(null)) + .map(VoucherMapper.INSTANCE::mapToResponseDto) + .toList(); + } + + public List walletListByVoucherId(WalletRequestDto walletRequestDto) { + + Voucher voucher = findVoucherById(walletRequestDto); + UUID voucherId = voucher.getVoucherId(); + List wallets = this.walletRepository.findById(voucherId); + + return wallets.stream() + .map(WalletMapper.INSTANCE::mapToResponseDto) + .toList(); + } + + public List customerListFromWallet(List walletResponseDtos) { + + return walletResponseDtos.stream() + .map(WalletResponseDto::getEmail) + .map(customerRepository::findByEmail) + .map(customer -> customer.orElse(null)) + .map(CustomerMapper.INSTANCE::mapToResponseDto) + .toList(); + } + + public Customer findCustomerByEmail(WalletRequestDto walletRequestDto) { + + Email email = walletRequestDto.getEmail(); + email.validate(); + + return this.customerRepository.findByEmail(email) + .orElseThrow(() -> { + log.warn("No customer found for email: {}", email); + return new CustomerNotFoundException(); + }); + } + + public Voucher findVoucherById(WalletRequestDto walletRequestDto) { + + UUID voucherId = walletRequestDto.getVoucherId(); + return this.voucherRepository.findById(voucherId) + .orElseThrow(() -> { + log.warn("No voucher found for voucherId: {}", voucherId); + return new VoucherNotFoundException(); + }); + } + + @Transactional + public void removeVoucherFromWallet(WalletRequestDto walletRequestDto) { + + Wallet wallet = WalletMapper.INSTANCE.mapToEntity(walletRequestDto); + this.walletRepository.delete(wallet); + } +} From 818a9416ce9039b2d949f497143d0a8794dc4300 Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Sat, 4 Nov 2023 18:13:56 +0900 Subject: [PATCH 83/86] =?UTF-8?q?feat:=20Voucher,=20Wallet=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EB=A7=A4=ED=95=91?= =?UTF-8?q?=EC=9D=84=20=EC=9C=84=ED=95=9C=20WebController=20=E3=85=8A?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/VoucherWebController.java | 140 +++++++++++++++ .../controller/WalletWebController.java | 164 ++++++++++++++++++ 2 files changed, 304 insertions(+) create mode 100644 src/main/java/org/programmers/springboot/basic/domain/voucher/controller/VoucherWebController.java create mode 100644 src/main/java/org/programmers/springboot/basic/domain/wallet/controller/WalletWebController.java diff --git a/src/main/java/org/programmers/springboot/basic/domain/voucher/controller/VoucherWebController.java b/src/main/java/org/programmers/springboot/basic/domain/voucher/controller/VoucherWebController.java new file mode 100644 index 0000000000..fe8642b552 --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/domain/voucher/controller/VoucherWebController.java @@ -0,0 +1,140 @@ +package org.programmers.springboot.basic.domain.voucher.controller; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.programmers.springboot.basic.domain.voucher.dto.VoucherControllerRequestDto; +import org.programmers.springboot.basic.domain.voucher.dto.VoucherRequestDto; +import org.programmers.springboot.basic.domain.voucher.dto.VoucherResponseDto; +import org.programmers.springboot.basic.domain.voucher.entity.VoucherType; +import org.programmers.springboot.basic.domain.voucher.exception.DuplicateVoucherException; +import org.programmers.springboot.basic.domain.voucher.exception.IllegalDiscountException; +import org.programmers.springboot.basic.domain.voucher.exception.VoucherNotFoundException; +import org.programmers.springboot.basic.domain.voucher.mapper.VoucherMapper; +import org.programmers.springboot.basic.domain.voucher.service.VoucherService; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@Slf4j +@Controller +@RequiredArgsConstructor +@RequestMapping("/voucher") +public class VoucherWebController { + + private final VoucherService voucherService; + + @GetMapping("/createForm") + public String createVoucher() { + return "voucher/createForm"; + } + + @GetMapping("/findForm") + public String findVoucher() { + return "voucher/findForm"; + } + + @GetMapping("/updateForm") + public String updateVoucher() { + return "voucher/updateForm"; + } + + @GetMapping("/deleteForm") + public String deleteVoucher() { + return "voucher/deleteForm"; + } + + @PostMapping("/create") + public String create(VoucherControllerRequestDto requestDto, Model model) { + + VoucherType voucherType = null; + Long discount = null; + try { + voucherType = VoucherType.valueOf(Integer.parseInt(requestDto.getVoucherType())); + discount = Long.parseLong(requestDto.getDiscount()); + VoucherRequestDto voucherRequestDto = VoucherMapper.INSTANCE.mapToRequestDto(voucherType, discount); + voucherService.create(voucherRequestDto); + return "redirect:/"; + } catch (NumberFormatException e) { + log.error("your input is Invalid Discount Type '{}'", discount); + model.addAttribute("err", e.getMessage()); + } catch (IllegalDiscountException e) { + log.error("your input is Invalid Discount Range '{}'", discount); + model.addAttribute("err", e.getMessage()); + } catch (DuplicateVoucherException e) { + log.warn("voucher of voucherType '{}' and discount '{} is already exists", + voucherType, discount); + model.addAttribute("err", e.getMessage()); + } catch (VoucherNotFoundException e) { + log.warn("No matching voucher of voucherType '{}' found", voucherType); + model.addAttribute("err", e.getMessage()); + } + return "error"; + } + + @GetMapping("/find") + public String find(@RequestParam String voucherId, Model model) { + try { + VoucherRequestDto voucherRequestDto = VoucherMapper.INSTANCE.mapToRequestDtoWithUUID(voucherId); + VoucherResponseDto voucherResponseDto = voucherService.findById(voucherRequestDto); + model.addAttribute("voucher", voucherResponseDto); + return "voucher/find"; + } catch (IllegalArgumentException e) { + log.error("Your input is Invalid UUID string: '{}'", voucherId); + model.addAttribute("err", e.getMessage()); + } catch (VoucherNotFoundException e) { + log.warn("No voucher found for voucherId: '{}'", voucherId); + model.addAttribute("err", e.getMessage()); + } + return "error"; + } + + @PutMapping("/update") + public String update(VoucherControllerRequestDto requestDto, Model model) { + String voucherId = null; + Long discount = null; + try { + voucherId = requestDto.getVoucherId(); + discount = Long.parseLong(requestDto.getDiscount()); + VoucherRequestDto voucherRequestDto = VoucherMapper.INSTANCE.mapToRequestDtoWithIdNDiscount(voucherId, discount); + voucherService.updateVoucher(voucherRequestDto); + return "redirect:/"; + } catch (NumberFormatException e) { + log.error("Your input is Invalid Type of Long: '{}'", discount); + model.addAttribute("err", e.getMessage()); + } catch (IllegalArgumentException e) { + log.error("Your input is Invalid UUID string: '{}'", voucherId); + model.addAttribute("err", e.getMessage()); + } catch (VoucherNotFoundException e) { + log.warn("No voucher of voucherId '{}' found", voucherId); + model.addAttribute("err", e.getMessage()); + } + return "error"; + } + + @DeleteMapping("/delete") + public String delete(VoucherControllerRequestDto requestDto, Model model) { + String voucherId = null; + try { + voucherId = requestDto.getVoucherId(); + VoucherRequestDto voucherRequestDto = VoucherMapper.INSTANCE.mapToRequestDtoWithUUID(voucherId); + voucherService.deleteVoucher(voucherRequestDto); + return "redirect:/"; + } catch (IllegalArgumentException e) { + log.error("Your input is Invalid UUID string: '{}'", voucherId); + model.addAttribute("err", e.getMessage()); + } catch (VoucherNotFoundException e) { + log.warn("No voucher of voucherId '{}' found", voucherId); + model.addAttribute("err", e.getMessage()); + } + return "error"; + } + + @GetMapping("/list") + public String list(Model model) { + List vouchers = voucherService.findAll(); + model.addAttribute("vouchers", vouchers); + return "voucher/voucherList"; + } +} diff --git a/src/main/java/org/programmers/springboot/basic/domain/wallet/controller/WalletWebController.java b/src/main/java/org/programmers/springboot/basic/domain/wallet/controller/WalletWebController.java new file mode 100644 index 0000000000..24a7c71160 --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/domain/wallet/controller/WalletWebController.java @@ -0,0 +1,164 @@ +package org.programmers.springboot.basic.domain.wallet.controller; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.programmers.springboot.basic.domain.customer.dto.CustomerResponseDto; +import org.programmers.springboot.basic.domain.customer.entity.vo.Email; +import org.programmers.springboot.basic.domain.customer.exception.CustomerNotFoundException; +import org.programmers.springboot.basic.domain.customer.exception.IllegalEmailException; +import org.programmers.springboot.basic.domain.customer.service.CustomerService; +import org.programmers.springboot.basic.domain.voucher.dto.VoucherRequestDto; +import org.programmers.springboot.basic.domain.voucher.dto.VoucherResponseDto; +import org.programmers.springboot.basic.domain.voucher.exception.VoucherNotFoundException; +import org.programmers.springboot.basic.domain.voucher.mapper.VoucherMapper; +import org.programmers.springboot.basic.domain.voucher.service.VoucherService; +import org.programmers.springboot.basic.domain.wallet.dto.WalletControllerRequestDto; +import org.programmers.springboot.basic.domain.wallet.dto.WalletRequestDto; +import org.programmers.springboot.basic.domain.wallet.dto.WalletResponseDto; +import org.programmers.springboot.basic.domain.wallet.exception.DuplicateWalletException; +import org.programmers.springboot.basic.domain.wallet.mapper.WalletMapper; +import org.programmers.springboot.basic.domain.wallet.service.WalletService; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.*; + +import java.util.ArrayList; +import java.util.List; + +@Slf4j +@Controller +@RequiredArgsConstructor +@RequestMapping("/wallet") +public class WalletWebController { + + private final WalletService walletService; + private final VoucherService voucherService; + private final CustomerService customerService; + + @GetMapping("/addForm") + public String addWallet(Model model) { + model.addAttribute("vouchers", voucherService.findAll()); + return "wallet/addForm"; + } + + @GetMapping("/deleteForm") + public String removeWallet() { + return "wallet/deleteForm"; + } + + @GetMapping("/findVoucherForm") + public String findAllVoucher() { + return "wallet/findVoucherForm"; + } + + @GetMapping("/findCustomerForm") + public String findAllCustomer() { + return "wallet/findCustomerForm"; + } + + @PostMapping("/add") + public String addVoucherInWallet(WalletControllerRequestDto requestDto, Model model) { + + String voucherId = null; + String email = null; + try { + voucherId = requestDto.getVoucherId(); + email = requestDto.getEmail(); + + WalletRequestDto walletRequestDto = WalletMapper.INSTANCE.mapToRequestDto(voucherId, email); + walletService.createWallet(walletRequestDto); + return "redirect:/"; + } catch (IllegalArgumentException e) { + log.warn("Your input is Invalid UUID string: '{}'", voucherId); + model.addAttribute("err", e.getMessage()); + } catch (IllegalEmailException e) { + log.warn("Your input is Invalid Email Type: '{}'", email); + model.addAttribute("err", e.getMessage()); + } catch (DuplicateWalletException e) { + log.warn("email '{}' and voucherId '{}' is already exists in Wallet", email, voucherId); + model.addAttribute("err", e.getMessage()); + } catch (CustomerNotFoundException e) { + log.warn("No customer of email '{}' found", email); + model.addAttribute("err", e.getMessage()); + } catch (VoucherNotFoundException e) { + log.warn("No voucher of voucherId '{}' found", voucherId); + model.addAttribute("err", e.getMessage()); + } + return "error"; + } + + @DeleteMapping("/delete") + public String removeWallet(WalletControllerRequestDto requestDto, Model model) { + + String voucherId = null; + String email = null; + try { + voucherId = requestDto.getVoucherId(); + email = requestDto.getEmail(); + + WalletRequestDto walletRequestDto = WalletMapper.INSTANCE.mapToRequestDto(voucherId, email); + walletService.removeVoucherFromWallet(walletRequestDto); + return "redirect:/"; + } catch (IllegalArgumentException e) { + log.warn("Your input is Invalid UUID string: '{}'", voucherId); + model.addAttribute("err", e.getMessage()); + } catch (IllegalEmailException e) { + log.warn("Your input is Invalid Email Type: '{}'", email); + model.addAttribute("err", e.getMessage()); + } catch (CustomerNotFoundException e) { + log.warn("No customer of email '{}' found", email); + model.addAttribute("err", e.getMessage()); + } catch (VoucherNotFoundException e) { + log.warn("No voucher of voucherId '{}' found", voucherId); + model.addAttribute("err", e.getMessage()); + } + return "error"; + } + + @GetMapping("/vouchers") + public String voucherListInWallet(@RequestParam String email, Model model) { + + try { + WalletRequestDto walletRequestDto = WalletMapper.INSTANCE.mapToRequestDtoWithEmail(email); + List wallets = walletService.walletListByEmail(walletRequestDto); + CustomerResponseDto customerResponseDto = customerService.findByEmail(Email.valueOf(email)); + + List vouchers = new ArrayList<>(); + for (WalletResponseDto wallet: wallets) { + vouchers.add(voucherService.findById(VoucherMapper.INSTANCE.mapToRequestDtoWithUUID(wallet.getVoucherId().toString()))); + } + + model.addAttribute("customer", customerResponseDto); + model.addAttribute("vouchers", vouchers); + return "wallet/voucherListInWallet"; + } catch (Exception e) { + log.error(e.toString()); + model.addAttribute("err", e.getMessage()); + } + return "error"; + } + + @GetMapping("/customers") + public String customerListInWallet(@RequestParam String voucherId, Model model) { + + try { + WalletRequestDto walletRequestDto = WalletMapper.INSTANCE.mapToRequestDtoWithUUID(voucherId); + List wallets = walletService.walletListByVoucherId(walletRequestDto); + VoucherRequestDto requestDto = VoucherMapper.INSTANCE.mapToRequestDtoWithUUID(voucherId); + VoucherResponseDto voucherResponseDto = voucherService.findById(requestDto); + + List customers = new ArrayList<>(); + for (WalletResponseDto wallet : wallets) { + customers.add(customerService.findByEmail(wallet.getEmail())); + } + + model.addAttribute("voucher", voucherResponseDto); + model.addAttribute("customers", customers); + return "wallet/customerListInWallet"; + } catch (Exception e) { + log.error(e.toString()); + model.addAttribute("err", e.getMessage()); + } + return "error"; + } +} From 45d1d84999e8379b58af9f75cc3779116eee84c7 Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Sat, 4 Nov 2023 18:13:56 +0900 Subject: [PATCH 84/86] =?UTF-8?q?feat:=20Voucher,=20Wallet=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EB=A7=A4=ED=95=91?= =?UTF-8?q?=EC=9D=84=20=EC=9C=84=ED=95=9C=20WebController=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/VoucherWebController.java | 140 +++++++++++++++ .../controller/WalletWebController.java | 164 ++++++++++++++++++ 2 files changed, 304 insertions(+) create mode 100644 src/main/java/org/programmers/springboot/basic/domain/voucher/controller/VoucherWebController.java create mode 100644 src/main/java/org/programmers/springboot/basic/domain/wallet/controller/WalletWebController.java diff --git a/src/main/java/org/programmers/springboot/basic/domain/voucher/controller/VoucherWebController.java b/src/main/java/org/programmers/springboot/basic/domain/voucher/controller/VoucherWebController.java new file mode 100644 index 0000000000..fe8642b552 --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/domain/voucher/controller/VoucherWebController.java @@ -0,0 +1,140 @@ +package org.programmers.springboot.basic.domain.voucher.controller; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.programmers.springboot.basic.domain.voucher.dto.VoucherControllerRequestDto; +import org.programmers.springboot.basic.domain.voucher.dto.VoucherRequestDto; +import org.programmers.springboot.basic.domain.voucher.dto.VoucherResponseDto; +import org.programmers.springboot.basic.domain.voucher.entity.VoucherType; +import org.programmers.springboot.basic.domain.voucher.exception.DuplicateVoucherException; +import org.programmers.springboot.basic.domain.voucher.exception.IllegalDiscountException; +import org.programmers.springboot.basic.domain.voucher.exception.VoucherNotFoundException; +import org.programmers.springboot.basic.domain.voucher.mapper.VoucherMapper; +import org.programmers.springboot.basic.domain.voucher.service.VoucherService; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@Slf4j +@Controller +@RequiredArgsConstructor +@RequestMapping("/voucher") +public class VoucherWebController { + + private final VoucherService voucherService; + + @GetMapping("/createForm") + public String createVoucher() { + return "voucher/createForm"; + } + + @GetMapping("/findForm") + public String findVoucher() { + return "voucher/findForm"; + } + + @GetMapping("/updateForm") + public String updateVoucher() { + return "voucher/updateForm"; + } + + @GetMapping("/deleteForm") + public String deleteVoucher() { + return "voucher/deleteForm"; + } + + @PostMapping("/create") + public String create(VoucherControllerRequestDto requestDto, Model model) { + + VoucherType voucherType = null; + Long discount = null; + try { + voucherType = VoucherType.valueOf(Integer.parseInt(requestDto.getVoucherType())); + discount = Long.parseLong(requestDto.getDiscount()); + VoucherRequestDto voucherRequestDto = VoucherMapper.INSTANCE.mapToRequestDto(voucherType, discount); + voucherService.create(voucherRequestDto); + return "redirect:/"; + } catch (NumberFormatException e) { + log.error("your input is Invalid Discount Type '{}'", discount); + model.addAttribute("err", e.getMessage()); + } catch (IllegalDiscountException e) { + log.error("your input is Invalid Discount Range '{}'", discount); + model.addAttribute("err", e.getMessage()); + } catch (DuplicateVoucherException e) { + log.warn("voucher of voucherType '{}' and discount '{} is already exists", + voucherType, discount); + model.addAttribute("err", e.getMessage()); + } catch (VoucherNotFoundException e) { + log.warn("No matching voucher of voucherType '{}' found", voucherType); + model.addAttribute("err", e.getMessage()); + } + return "error"; + } + + @GetMapping("/find") + public String find(@RequestParam String voucherId, Model model) { + try { + VoucherRequestDto voucherRequestDto = VoucherMapper.INSTANCE.mapToRequestDtoWithUUID(voucherId); + VoucherResponseDto voucherResponseDto = voucherService.findById(voucherRequestDto); + model.addAttribute("voucher", voucherResponseDto); + return "voucher/find"; + } catch (IllegalArgumentException e) { + log.error("Your input is Invalid UUID string: '{}'", voucherId); + model.addAttribute("err", e.getMessage()); + } catch (VoucherNotFoundException e) { + log.warn("No voucher found for voucherId: '{}'", voucherId); + model.addAttribute("err", e.getMessage()); + } + return "error"; + } + + @PutMapping("/update") + public String update(VoucherControllerRequestDto requestDto, Model model) { + String voucherId = null; + Long discount = null; + try { + voucherId = requestDto.getVoucherId(); + discount = Long.parseLong(requestDto.getDiscount()); + VoucherRequestDto voucherRequestDto = VoucherMapper.INSTANCE.mapToRequestDtoWithIdNDiscount(voucherId, discount); + voucherService.updateVoucher(voucherRequestDto); + return "redirect:/"; + } catch (NumberFormatException e) { + log.error("Your input is Invalid Type of Long: '{}'", discount); + model.addAttribute("err", e.getMessage()); + } catch (IllegalArgumentException e) { + log.error("Your input is Invalid UUID string: '{}'", voucherId); + model.addAttribute("err", e.getMessage()); + } catch (VoucherNotFoundException e) { + log.warn("No voucher of voucherId '{}' found", voucherId); + model.addAttribute("err", e.getMessage()); + } + return "error"; + } + + @DeleteMapping("/delete") + public String delete(VoucherControllerRequestDto requestDto, Model model) { + String voucherId = null; + try { + voucherId = requestDto.getVoucherId(); + VoucherRequestDto voucherRequestDto = VoucherMapper.INSTANCE.mapToRequestDtoWithUUID(voucherId); + voucherService.deleteVoucher(voucherRequestDto); + return "redirect:/"; + } catch (IllegalArgumentException e) { + log.error("Your input is Invalid UUID string: '{}'", voucherId); + model.addAttribute("err", e.getMessage()); + } catch (VoucherNotFoundException e) { + log.warn("No voucher of voucherId '{}' found", voucherId); + model.addAttribute("err", e.getMessage()); + } + return "error"; + } + + @GetMapping("/list") + public String list(Model model) { + List vouchers = voucherService.findAll(); + model.addAttribute("vouchers", vouchers); + return "voucher/voucherList"; + } +} diff --git a/src/main/java/org/programmers/springboot/basic/domain/wallet/controller/WalletWebController.java b/src/main/java/org/programmers/springboot/basic/domain/wallet/controller/WalletWebController.java new file mode 100644 index 0000000000..24a7c71160 --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/domain/wallet/controller/WalletWebController.java @@ -0,0 +1,164 @@ +package org.programmers.springboot.basic.domain.wallet.controller; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.programmers.springboot.basic.domain.customer.dto.CustomerResponseDto; +import org.programmers.springboot.basic.domain.customer.entity.vo.Email; +import org.programmers.springboot.basic.domain.customer.exception.CustomerNotFoundException; +import org.programmers.springboot.basic.domain.customer.exception.IllegalEmailException; +import org.programmers.springboot.basic.domain.customer.service.CustomerService; +import org.programmers.springboot.basic.domain.voucher.dto.VoucherRequestDto; +import org.programmers.springboot.basic.domain.voucher.dto.VoucherResponseDto; +import org.programmers.springboot.basic.domain.voucher.exception.VoucherNotFoundException; +import org.programmers.springboot.basic.domain.voucher.mapper.VoucherMapper; +import org.programmers.springboot.basic.domain.voucher.service.VoucherService; +import org.programmers.springboot.basic.domain.wallet.dto.WalletControllerRequestDto; +import org.programmers.springboot.basic.domain.wallet.dto.WalletRequestDto; +import org.programmers.springboot.basic.domain.wallet.dto.WalletResponseDto; +import org.programmers.springboot.basic.domain.wallet.exception.DuplicateWalletException; +import org.programmers.springboot.basic.domain.wallet.mapper.WalletMapper; +import org.programmers.springboot.basic.domain.wallet.service.WalletService; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.*; + +import java.util.ArrayList; +import java.util.List; + +@Slf4j +@Controller +@RequiredArgsConstructor +@RequestMapping("/wallet") +public class WalletWebController { + + private final WalletService walletService; + private final VoucherService voucherService; + private final CustomerService customerService; + + @GetMapping("/addForm") + public String addWallet(Model model) { + model.addAttribute("vouchers", voucherService.findAll()); + return "wallet/addForm"; + } + + @GetMapping("/deleteForm") + public String removeWallet() { + return "wallet/deleteForm"; + } + + @GetMapping("/findVoucherForm") + public String findAllVoucher() { + return "wallet/findVoucherForm"; + } + + @GetMapping("/findCustomerForm") + public String findAllCustomer() { + return "wallet/findCustomerForm"; + } + + @PostMapping("/add") + public String addVoucherInWallet(WalletControllerRequestDto requestDto, Model model) { + + String voucherId = null; + String email = null; + try { + voucherId = requestDto.getVoucherId(); + email = requestDto.getEmail(); + + WalletRequestDto walletRequestDto = WalletMapper.INSTANCE.mapToRequestDto(voucherId, email); + walletService.createWallet(walletRequestDto); + return "redirect:/"; + } catch (IllegalArgumentException e) { + log.warn("Your input is Invalid UUID string: '{}'", voucherId); + model.addAttribute("err", e.getMessage()); + } catch (IllegalEmailException e) { + log.warn("Your input is Invalid Email Type: '{}'", email); + model.addAttribute("err", e.getMessage()); + } catch (DuplicateWalletException e) { + log.warn("email '{}' and voucherId '{}' is already exists in Wallet", email, voucherId); + model.addAttribute("err", e.getMessage()); + } catch (CustomerNotFoundException e) { + log.warn("No customer of email '{}' found", email); + model.addAttribute("err", e.getMessage()); + } catch (VoucherNotFoundException e) { + log.warn("No voucher of voucherId '{}' found", voucherId); + model.addAttribute("err", e.getMessage()); + } + return "error"; + } + + @DeleteMapping("/delete") + public String removeWallet(WalletControllerRequestDto requestDto, Model model) { + + String voucherId = null; + String email = null; + try { + voucherId = requestDto.getVoucherId(); + email = requestDto.getEmail(); + + WalletRequestDto walletRequestDto = WalletMapper.INSTANCE.mapToRequestDto(voucherId, email); + walletService.removeVoucherFromWallet(walletRequestDto); + return "redirect:/"; + } catch (IllegalArgumentException e) { + log.warn("Your input is Invalid UUID string: '{}'", voucherId); + model.addAttribute("err", e.getMessage()); + } catch (IllegalEmailException e) { + log.warn("Your input is Invalid Email Type: '{}'", email); + model.addAttribute("err", e.getMessage()); + } catch (CustomerNotFoundException e) { + log.warn("No customer of email '{}' found", email); + model.addAttribute("err", e.getMessage()); + } catch (VoucherNotFoundException e) { + log.warn("No voucher of voucherId '{}' found", voucherId); + model.addAttribute("err", e.getMessage()); + } + return "error"; + } + + @GetMapping("/vouchers") + public String voucherListInWallet(@RequestParam String email, Model model) { + + try { + WalletRequestDto walletRequestDto = WalletMapper.INSTANCE.mapToRequestDtoWithEmail(email); + List wallets = walletService.walletListByEmail(walletRequestDto); + CustomerResponseDto customerResponseDto = customerService.findByEmail(Email.valueOf(email)); + + List vouchers = new ArrayList<>(); + for (WalletResponseDto wallet: wallets) { + vouchers.add(voucherService.findById(VoucherMapper.INSTANCE.mapToRequestDtoWithUUID(wallet.getVoucherId().toString()))); + } + + model.addAttribute("customer", customerResponseDto); + model.addAttribute("vouchers", vouchers); + return "wallet/voucherListInWallet"; + } catch (Exception e) { + log.error(e.toString()); + model.addAttribute("err", e.getMessage()); + } + return "error"; + } + + @GetMapping("/customers") + public String customerListInWallet(@RequestParam String voucherId, Model model) { + + try { + WalletRequestDto walletRequestDto = WalletMapper.INSTANCE.mapToRequestDtoWithUUID(voucherId); + List wallets = walletService.walletListByVoucherId(walletRequestDto); + VoucherRequestDto requestDto = VoucherMapper.INSTANCE.mapToRequestDtoWithUUID(voucherId); + VoucherResponseDto voucherResponseDto = voucherService.findById(requestDto); + + List customers = new ArrayList<>(); + for (WalletResponseDto wallet : wallets) { + customers.add(customerService.findByEmail(wallet.getEmail())); + } + + model.addAttribute("voucher", voucherResponseDto); + model.addAttribute("customers", customers); + return "wallet/customerListInWallet"; + } catch (Exception e) { + log.error(e.toString()); + model.addAttribute("err", e.getMessage()); + } + return "error"; + } +} From ebcf6989a9bd26620dc4c30d5ea4c26e68439475 Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Sat, 4 Nov 2023 18:14:42 +0900 Subject: [PATCH 85/86] =?UTF-8?q?feat:=20Voucher=20=EC=84=9C=EB=B9=84?= =?UTF-8?q?=EC=8A=A4=EC=97=90=20=EB=8C=80=ED=95=9C=20REST=20API=20?= =?UTF-8?q?=EA=B0=9C=EB=B0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/VoucherAPIController.java | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 src/main/java/org/programmers/springboot/basic/domain/voucher/controller/VoucherAPIController.java diff --git a/src/main/java/org/programmers/springboot/basic/domain/voucher/controller/VoucherAPIController.java b/src/main/java/org/programmers/springboot/basic/domain/voucher/controller/VoucherAPIController.java new file mode 100644 index 0000000000..665220abd9 --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/domain/voucher/controller/VoucherAPIController.java @@ -0,0 +1,82 @@ +package org.programmers.springboot.basic.domain.voucher.controller; + +import lombok.RequiredArgsConstructor; +import org.programmers.springboot.basic.AppConstants; +import org.programmers.springboot.basic.domain.voucher.dto.VoucherControllerRequestDto; +import org.programmers.springboot.basic.domain.voucher.dto.VoucherRequestDto; +import org.programmers.springboot.basic.domain.voucher.dto.VoucherResponseDto; +import org.programmers.springboot.basic.domain.voucher.entity.VoucherType; +import org.programmers.springboot.basic.domain.voucher.mapper.VoucherMapper; +import org.programmers.springboot.basic.domain.voucher.service.VoucherService; +import org.springframework.context.annotation.Profile; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@Profile("default") +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/voucher") +public class VoucherAPIController { + + private final VoucherService voucherService; + + @PostMapping("/create") + @ResponseStatus(HttpStatus.CREATED) + public ResponseEntity create(@RequestBody VoucherControllerRequestDto requestDto) { + VoucherType voucherType = VoucherType.valueOf(requestDto.getVoucherType()); + Long discount = Long.parseLong(requestDto.getDiscount()); + + VoucherRequestDto voucherRequestDto = VoucherMapper.INSTANCE.mapToRequestDto(voucherType, discount); + voucherService.create(voucherRequestDto); + return ResponseEntity.ok(AppConstants.SUCCESS); + } + + @GetMapping("/{voucherId}") + @ResponseStatus(HttpStatus.OK) + public ResponseEntity findVoucherById(@PathVariable String voucherId) { + VoucherRequestDto requestDto = VoucherMapper.INSTANCE.mapToRequestDtoWithUUID(voucherId); + VoucherResponseDto responseDto = voucherService.findById(requestDto); + return ResponseEntity.ok(responseDto); + } + + @GetMapping() + @ResponseStatus(HttpStatus.OK) + public ResponseEntity> findVoucher( + @RequestParam(name = "voucherId", required = false) String voucherId, + @RequestParam(name = "createdAt", required = false) String createdAt, + @RequestParam(name = "discount", required = false) String discount, + @RequestParam(name = "voucherType", required = false) String voucherType + ) { + VoucherRequestDto voucherRequestDto = VoucherMapper.INSTANCE.mapToRequestDtoWithAllArgs( + voucherId, voucherType, discount, createdAt + ); + List voucherResponseDtos = voucherService.findByOption(voucherRequestDto); + return ResponseEntity.ok(voucherResponseDtos); + } + + @GetMapping("/vouchers") + @ResponseStatus(HttpStatus.OK) + public ResponseEntity> findAllVoucher() { + List vouchers = voucherService.findAll(); + return ResponseEntity.ok(vouchers); + } + + @PatchMapping("/{discount}") + public ResponseEntity updateVoucher(@PathVariable Long discount, + @RequestBody VoucherControllerRequestDto requestDto) { + VoucherRequestDto voucherRequestDto = VoucherMapper.INSTANCE + .mapToRequestDtoWithIdNDiscount(requestDto.getVoucherId(), discount); + voucherService.updateVoucher(voucherRequestDto); + return ResponseEntity.ok(AppConstants.SUCCESS); + } + + @DeleteMapping("/{voucherId}") + public ResponseEntity deleteVoucher(@PathVariable String voucherId) { + VoucherRequestDto voucherRequestDto = VoucherMapper.INSTANCE.mapToRequestDtoWithUUID(voucherId); + voucherService.deleteVoucher(voucherRequestDto); + return ResponseEntity.ok(AppConstants.SUCCESS); + } +} From 47fd1bf40c79a1b4400602ad204f0b1347a0fab3 Mon Sep 17 00:00:00 2001 From: JIN-076 Date: Sat, 4 Nov 2023 18:16:58 +0900 Subject: [PATCH 86/86] =?UTF-8?q?feat:=20=EC=98=88=EC=99=B8=EC=97=90=20?= =?UTF-8?q?=EB=8C=80=ED=95=B4=EC=84=9C=20=ED=95=9C=20=EB=B2=88=EC=97=90=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=ED=95=98=EB=8A=94=20RestControllerAdvice?= =?UTF-8?q?=EB=A5=BC=20=EC=A0=81=EC=9A=A9=ED=95=9C=20ExceptionHandler=20?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=20=E3=85=8A=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../util/handler/GlobalExceptionHandler.java | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 src/main/java/org/programmers/springboot/basic/util/handler/GlobalExceptionHandler.java diff --git a/src/main/java/org/programmers/springboot/basic/util/handler/GlobalExceptionHandler.java b/src/main/java/org/programmers/springboot/basic/util/handler/GlobalExceptionHandler.java new file mode 100644 index 0000000000..c6c79e745e --- /dev/null +++ b/src/main/java/org/programmers/springboot/basic/util/handler/GlobalExceptionHandler.java @@ -0,0 +1,84 @@ +package org.programmers.springboot.basic.util.handler; + +import org.programmers.springboot.basic.domain.customer.exception.CustomerNotFoundException; +import org.programmers.springboot.basic.domain.customer.exception.DuplicateBlackCustomerException; +import org.programmers.springboot.basic.domain.customer.exception.DuplicateEmailException; +import org.programmers.springboot.basic.domain.voucher.exception.DuplicateVoucherException; +import org.programmers.springboot.basic.domain.voucher.exception.IllegalDiscountException; +import org.programmers.springboot.basic.domain.voucher.exception.VoucherNotFoundException; +import org.programmers.springboot.basic.domain.wallet.exception.DuplicateWalletException; +import org.programmers.springboot.basic.util.response.ErrCode; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.programmers.springboot.basic.util.response.ErrResponse; + +@RestControllerAdvice +public class GlobalExceptionHandler { + + @ExceptionHandler(IllegalArgumentException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public ResponseEntity handleIllegalArgumentException(IllegalArgumentException e) { + ErrResponse response = new ErrResponse(ErrCode.BAD_REQUEST, e.getMessage()); + return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST); + } + + @ExceptionHandler(NumberFormatException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public ResponseEntity handleNumberFormatException(NumberFormatException e) { + ErrResponse response = new ErrResponse(ErrCode.BAD_REQUEST, e.getMessage()); + return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST); + } + + @ExceptionHandler(IllegalDiscountException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public ResponseEntity handleIllegalDiscountException(IllegalDiscountException e) { + ErrResponse response = new ErrResponse(ErrCode.INPUT_INVALID_VALUE, e.getMessage()); + return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST); + } + + @ExceptionHandler(DuplicateVoucherException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public ResponseEntity handleDuplicateVoucherException(DuplicateVoucherException e) { + ErrResponse response = new ErrResponse(ErrCode.BAD_REQUEST, e.getMessage()); + return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST); + } + + @ExceptionHandler(DuplicateEmailException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public ResponseEntity handleDuplicateEmailException(DuplicateEmailException e) { + ErrResponse response = new ErrResponse(ErrCode.BAD_REQUEST, e.getMessage()); + return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST); + } + + @ExceptionHandler(DuplicateBlackCustomerException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public ResponseEntity handleDuplicateBlackCustomerException(DuplicateBlackCustomerException e) { + ErrResponse response = new ErrResponse(ErrCode.BAD_REQUEST, e.getMessage()); + return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST); + } + + @ExceptionHandler(DuplicateWalletException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public ResponseEntity handleDuplicateWalletException(DuplicateWalletException e) { + ErrResponse response = new ErrResponse(ErrCode.BAD_REQUEST, e.getMessage()); + return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST); + } + + @ExceptionHandler(VoucherNotFoundException.class) + @ResponseStatus(HttpStatus.NOT_FOUND) + public ResponseEntity handleVoucherNotFoundException(VoucherNotFoundException e) { + ErrResponse response = new ErrResponse(ErrCode.NOT_FOUND, e.getMessage()); + return new ResponseEntity<>(response, HttpStatus.NOT_FOUND); + } + + @ExceptionHandler(CustomerNotFoundException.class) + @ResponseStatus(HttpStatus.NOT_FOUND) + public ResponseEntity handleCustomerNotFoundException(CustomerNotFoundException e) { + ErrResponse response = new ErrResponse(ErrCode.NOT_FOUND, e.getMessage()); + return new ResponseEntity<>(response, HttpStatus.NOT_FOUND); + } + +}