├── .gitignore ├── README.md ├── build.gradle ├── docker-compose └── docker-compose.yml ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── http-test ├── item-api.http ├── order-api.http └── partner-api.http ├── settings.gradle └── src ├── main ├── java │ └── dev │ │ └── practice │ │ └── order │ │ ├── OrderApplication.java │ │ ├── application │ │ ├── item │ │ │ └── ItemFacade.java │ │ ├── order │ │ │ └── OrderFacade.java │ │ └── partner │ │ │ └── PartnerFacade.java │ │ ├── common │ │ ├── exception │ │ │ ├── BaseException.java │ │ │ ├── EntityNotFoundException.java │ │ │ ├── IllegalStatusException.java │ │ │ └── InvalidParamException.java │ │ ├── interceptor │ │ │ └── CommonHttpRequestInterceptor.java │ │ ├── response │ │ │ ├── CommonControllerAdvice.java │ │ │ ├── CommonResponse.java │ │ │ └── ErrorCode.java │ │ └── util │ │ │ └── TokenGenerator.java │ │ ├── config │ │ └── JpaAuditingConfiguration.java │ │ ├── domain │ │ ├── AbstractEntity.java │ │ ├── item │ │ │ ├── Item.java │ │ │ ├── ItemCommand.java │ │ │ ├── ItemInfo.java │ │ │ ├── ItemOptionSeriesFactory.java │ │ │ ├── ItemReader.java │ │ │ ├── ItemService.java │ │ │ ├── ItemServiceImpl.java │ │ │ ├── ItemStore.java │ │ │ ├── option │ │ │ │ ├── ItemOption.java │ │ │ │ └── ItemOptionStore.java │ │ │ └── optiongroup │ │ │ │ ├── ItemOptionGroup.java │ │ │ │ └── ItemOptionGroupStore.java │ │ ├── notification │ │ │ └── NotificationService.java │ │ ├── order │ │ │ ├── Order.java │ │ │ ├── OrderCommand.java │ │ │ ├── OrderInfo.java │ │ │ ├── OrderInfoMapper.java │ │ │ ├── OrderItemSeriesFactory.java │ │ │ ├── OrderReader.java │ │ │ ├── OrderService.java │ │ │ ├── OrderServiceImpl.java │ │ │ ├── OrderStore.java │ │ │ ├── fragment │ │ │ │ └── DeliveryFragment.java │ │ │ ├── item │ │ │ │ ├── OrderItem.java │ │ │ │ ├── OrderItemOption.java │ │ │ │ └── OrderItemOptionGroup.java │ │ │ └── payment │ │ │ │ ├── PayMethod.java │ │ │ │ ├── PaymentProcessor.java │ │ │ │ └── validator │ │ │ │ ├── PayAmountValidator.java │ │ │ │ ├── PayMethodValidator.java │ │ │ │ ├── PayStatusValidator.java │ │ │ │ └── PaymentValidator.java │ │ └── partner │ │ │ ├── Partner.java │ │ │ ├── PartnerCommand.java │ │ │ ├── PartnerInfo.java │ │ │ ├── PartnerReader.java │ │ │ ├── PartnerService.java │ │ │ ├── PartnerServiceImpl.java │ │ │ └── PartnerStore.java │ │ ├── infrastructure │ │ ├── NotificationExecutor.java │ │ ├── item │ │ │ ├── ItemOptionSeriesFactoryImpl.java │ │ │ ├── ItemReaderImpl.java │ │ │ ├── ItemRepository.java │ │ │ ├── ItemStoreImpl.java │ │ │ ├── option │ │ │ │ ├── ItemOptionRepository.java │ │ │ │ └── ItemOptionStoreImpl.java │ │ │ └── optiongroup │ │ │ │ ├── ItemOptionGroupRepository.java │ │ │ │ └── ItemOptionGroupStoreImpl.java │ │ ├── order │ │ │ ├── OrderItemOptionGroupRepository.java │ │ │ ├── OrderItemOptionRepository.java │ │ │ ├── OrderItemRepository.java │ │ │ ├── OrderItemSeriesFactoryImpl.java │ │ │ ├── OrderReaderImpl.java │ │ │ ├── OrderRepository.java │ │ │ ├── OrderStoreImpl.java │ │ │ └── payment │ │ │ │ ├── KakaoPayApiCaller.java │ │ │ │ ├── NaverPayApiCaller.java │ │ │ │ ├── PaymentApiCaller.java │ │ │ │ ├── PaymentProcessorImpl.java │ │ │ │ ├── PgCardApiCaller.java │ │ │ │ └── TossPayApiCaller.java │ │ └── partner │ │ │ ├── PartnerReadImpl.java │ │ │ ├── PartnerRepository.java │ │ │ └── PartnerStoreImpl.java │ │ └── interfaces │ │ ├── item │ │ ├── ItemApiController.java │ │ ├── ItemDto.java │ │ └── ItemDtoMapper.java │ │ ├── order │ │ ├── OrderApiController.java │ │ ├── OrderDto.java │ │ └── OrderDtoMapper.java │ │ └── partner │ │ ├── PartnerApiController.java │ │ └── PartnerDto.java └── resources │ ├── application.yml │ ├── db │ └── migration │ │ └── V1__init_ddl.sql │ └── logback-local.xml └── test └── java └── dev └── practice └── order └── OrderApplicationTests.java /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | .gradle 3 | build/ 4 | !gradle/wrapper/gradle-wrapper.jar 5 | !**/src/main/**/build/ 6 | !**/src/test/**/build/ 7 | 8 | ### STS ### 9 | .apt_generated 10 | .classpath 11 | .factorypath 12 | .project 13 | .settings 14 | .springBeans 15 | .sts4-cache 16 | bin/ 17 | !**/src/main/**/bin/ 18 | !**/src/test/**/bin/ 19 | 20 | ### IntelliJ IDEA ### 21 | .idea 22 | *.iws 23 | *.iml 24 | *.ipr 25 | out/ 26 | !**/src/main/**/out/ 27 | !**/src/test/**/out/ 28 | 29 | ### NetBeans ### 30 | /nbproject/private/ 31 | /nbbuild/ 32 | /dist/ 33 | /nbdist/ 34 | /.nb-gradle/ 35 | 36 | ### VS Code ### 37 | .vscode/ 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 패스트캠퍼스 강의 - 주문 프로젝트 2 | 해당 repo 는 [패스트캠퍼스 RED - 비즈니스 성공을 위한 Java/Spring 기반 서비스 개발과 MSA 구축](https://fastcampus.co.kr/dev_red_lhc) 에 대한 예제 프로젝트 입니다 3 | 4 |
5 | 6 | ### 강의 순서에 따른 branch 순서 7 | 8 | #### 3. 주문 프로젝트 개발 9 | 10 | | branch | 강의 구간 | 11 | |---|:---:| 12 | | partner/domain | 추후 반영 | 13 | | partner/application | 추후 반영 | 14 | | partner/interfaces | 추후 반영 | 15 | | item/domain | 추후 반영 | 16 | | item/domain-service | 추후 반영 | 17 | | item/domain-with-factory | 추후 반영 | 18 | | item/domain-retrieve-info | 추후 반영 | 19 | | item/etc-impl-with-mapstruct | 추후 반영 | 20 | | order/domain | 추후 반영 | 21 | | order/domain-delivery-fragment | 추후 반영 | 22 | | order/domain-service | 추후 반영 | 23 | | order/domain-with-factory | 추후 반영 | 24 | | order/paymentProcessor | 추후 반영 | 25 | | order/paymentProcessor-validator | 추후 반영 | 26 | | order/etc-impl | 추후 반영 | 27 | 28 | 29 | #### 4. 선물하기 프로젝트 개발 30 | 31 | | branch | 강의 구간 | 특이 사항 | 32 | |---|:---:|:---:| 33 | | order/expand-gift | 추후 반영 | | 34 | | order/expand-gift-with-sqs | 추후 반영 | 해당 branch 에서는 aws AMI 계정 발급 후 application.yml 에 반영해야 함 | 35 | 36 | 37 |
38 | 39 | 40 | ### 문의 방법 41 | 강의 또는 코드 구현에 관한 질문은 greg.shiny82@gmail.com 으로 문의 바랍니다.
42 | 수강생에 한하여 답변 드릴 수 있도록 하겠습니다
43 | 현업 업무가 있기 때문에 주말에 몰아서 답변 가능합니다 44 | 45 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'org.springframework.boot' version '2.5.3' 3 | id 'io.spring.dependency-management' version '1.0.11.RELEASE' 4 | id 'java' 5 | } 6 | 7 | group = 'dev.practice' 8 | version = '0.0.1-SNAPSHOT' 9 | sourceCompatibility = '11' 10 | 11 | configurations { 12 | compileOnly { 13 | extendsFrom annotationProcessor 14 | } 15 | } 16 | 17 | repositories { 18 | mavenCentral() 19 | } 20 | 21 | dependencies { 22 | implementation 'org.springframework.boot:spring-boot-starter-data-jpa' 23 | implementation 'org.springframework.boot:spring-boot-starter-web' 24 | implementation 'org.springframework.boot:spring-boot-starter-validation' // NotEmpty 25 | 26 | implementation 'org.flywaydb:flyway-core:7.10.0' 27 | compileOnly 'org.projectlombok:lombok' 28 | 29 | // MapStruct 30 | implementation 'org.mapstruct:mapstruct:1.4.2.Final' 31 | annotationProcessor "org.mapstruct:mapstruct-processor:1.4.2.Final" 32 | annotationProcessor( 33 | 'org.projectlombok:lombok', 34 | 'org.projectlombok:lombok-mapstruct-binding:0.1.0' 35 | ) 36 | 37 | implementation 'com.google.guava:guava:28.2-jre' 38 | implementation 'org.apache.commons:commons-lang3:3.9' 39 | 40 | runtimeOnly 'com.h2database:h2' 41 | runtimeOnly 'mysql:mysql-connector-java' 42 | testImplementation 'org.springframework.boot:spring-boot-starter-test' 43 | } 44 | 45 | test { 46 | useJUnitPlatform() 47 | } 48 | -------------------------------------------------------------------------------- /docker-compose/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.7" 2 | services: 3 | order-db: 4 | image: mysql:8.0 5 | ports: 6 | - "3306:3306" 7 | environment: 8 | - MYSQL_DATABASE=order 9 | - MYSQL_ROOT_PASSWORD=root-pass 10 | - MYSQL_USER=order-svc 11 | - MYSQL_PASSWORD=order-pass 12 | - TZ=UTC 13 | command: # 명령어 실행 14 | - --character-set-server=utf8mb4 15 | - --collation-server=utf8mb4_unicode_ci 16 | - --lower_case_table_names=1 17 | volumes: 18 | - ./mysql:/var/lib/mysql -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gregshiny/example-order/af5570afb0d3509e154bd61955b473b7cba3f043/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.1.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ]; do 30 | ls=$(ls -ld "$PRG") 31 | link=$(expr "$ls" : '.*-> \(.*\)$') 32 | if expr "$link" : '/.*' >/dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=$(dirname "$PRG")"/$link" 36 | fi 37 | done 38 | SAVED="$(pwd)" 39 | cd "$(dirname \"$PRG\")/" >/dev/null 40 | APP_HOME="$(pwd -P)" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=$(basename "$0") 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn() { 53 | echo "$*" 54 | } 55 | 56 | die() { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "$(uname)" in 69 | CYGWIN*) 70 | cygwin=true 71 | ;; 72 | Darwin*) 73 | darwin=true 74 | ;; 75 | MSYS* | MINGW*) 76 | msys=true 77 | ;; 78 | NONSTOP*) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | # Determine the Java command to use to start the JVM. 86 | if [ -n "$JAVA_HOME" ]; then 87 | if [ -x "$JAVA_HOME/jre/sh/java" ]; then 88 | # IBM's JDK on AIX uses strange locations for the executables 89 | JAVACMD="$JAVA_HOME/jre/sh/java" 90 | else 91 | JAVACMD="$JAVA_HOME/bin/java" 92 | fi 93 | if [ ! -x "$JAVACMD" ]; then 94 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 95 | 96 | Please set the JAVA_HOME variable in your environment to match the 97 | location of your Java installation." 98 | fi 99 | else 100 | JAVACMD="java" 101 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 102 | 103 | Please set the JAVA_HOME variable in your environment to match the 104 | location of your Java installation." 105 | fi 106 | 107 | # Increase the maximum file descriptors if we can. 108 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ]; then 109 | MAX_FD_LIMIT=$(ulimit -H -n) 110 | if [ $? -eq 0 ]; then 111 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ]; then 112 | MAX_FD="$MAX_FD_LIMIT" 113 | fi 114 | ulimit -n $MAX_FD 115 | if [ $? -ne 0 ]; then 116 | warn "Could not set maximum file descriptor limit: $MAX_FD" 117 | fi 118 | else 119 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 120 | fi 121 | fi 122 | 123 | # For Darwin, add options to specify how the application appears in the dock 124 | if $darwin; then 125 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 126 | fi 127 | 128 | # For Cygwin or MSYS, switch paths to Windows format before running java 129 | if [ "$cygwin" = "true" -o "$msys" = "true" ]; then 130 | APP_HOME=$(cygpath --path --mixed "$APP_HOME") 131 | CLASSPATH=$(cygpath --path --mixed "$CLASSPATH") 132 | 133 | JAVACMD=$(cygpath --unix "$JAVACMD") 134 | 135 | # We build the pattern for arguments to be converted via cygpath 136 | ROOTDIRSRAW=$(find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null) 137 | SEP="" 138 | for dir in $ROOTDIRSRAW; do 139 | ROOTDIRS="$ROOTDIRS$SEP$dir" 140 | SEP="|" 141 | done 142 | OURCYGPATTERN="(^($ROOTDIRS))" 143 | # Add a user-defined pattern to the cygpath arguments 144 | if [ "$GRADLE_CYGPATTERN" != "" ]; then 145 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 146 | fi 147 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 148 | i=0 149 | for arg in "$@"; do 150 | CHECK=$(echo "$arg" | egrep -c "$OURCYGPATTERN" -) 151 | CHECK2=$(echo "$arg" | egrep -c "^-") ### Determine if an option 152 | 153 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ]; then ### Added a condition 154 | eval $(echo args$i)=$(cygpath --path --ignore --mixed "$arg") 155 | else 156 | eval $(echo args$i)="\"$arg\"" 157 | fi 158 | i=$(expr $i + 1) 159 | done 160 | case $i in 161 | 0) set -- ;; 162 | 1) set -- "$args0" ;; 163 | 2) set -- "$args0" "$args1" ;; 164 | 3) set -- "$args0" "$args1" "$args2" ;; 165 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 166 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 167 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 168 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 169 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 170 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 171 | esac 172 | fi 173 | 174 | # Escape application args 175 | save() { 176 | for i; do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/"; done 177 | echo " " 178 | } 179 | APP_ARGS=$(save "$@") 180 | 181 | # Collect all arguments for the java command, following the shell quoting and substitution rules 182 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 183 | 184 | exec "$JAVACMD" "$@" 185 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /http-test/item-api.http: -------------------------------------------------------------------------------- 1 | ### 아이템 등록 2 | POST http://localhost:8080/api/v1/items 3 | Content-Type: application/json 4 | 5 | { 6 | "partnerToken": "ptn_oWkmVAFErP2zgALi", 7 | "itemName": "티셔츠", 8 | "itemPrice": 30000, 9 | "itemOptionGroupList": [ 10 | { 11 | "ordering": "1", 12 | "itemOptionGroupName": "사이즈", 13 | "itemOptionList": [ 14 | { 15 | "ordering": 1, 16 | "itemOptionName": "SMALL", 17 | "itemOptionPrice": 0 18 | }, 19 | { 20 | "ordering": 2, 21 | "itemOptionName": "MEDIUM", 22 | "itemOptionPrice": 0 23 | }, 24 | { 25 | "ordering": 3, 26 | "itemOptionName": "LARGE", 27 | "itemOptionPrice": 0 28 | } 29 | ] 30 | 31 | }, 32 | { 33 | "ordering": "2", 34 | "itemOptionGroupName": "컬러", 35 | "itemOptionList": [ 36 | { 37 | "ordering": 1, 38 | "itemOptionName": "RED", 39 | "itemOptionPrice": 0 40 | }, 41 | { 42 | "ordering": 2, 43 | "itemOptionName": "GOLD", 44 | "itemOptionPrice": 1000 45 | } 46 | ] 47 | } 48 | ] 49 | } 50 | 51 | ### 아이템 1 활성화 52 | POST http://localhost:8080/api/v1/items/change-on-sales 53 | Content-Type: application/json 54 | 55 | { 56 | "itemToken": "itm_XqbL4h1l5mz1mnMZ" 57 | } 58 | 59 | ### 아이템 등록 2 60 | POST http://localhost:8080/api/v1/items 61 | Content-Type: application/json 62 | 63 | { 64 | "partnerToken": "ptn_oWkmVAFErP2zgALi", 65 | "itemName": "양말", 66 | "itemPrice": 10000, 67 | "itemOptionGroupList": [ 68 | { 69 | "ordering": "1", 70 | "itemOptionGroupName": "사이즈", 71 | "itemOptionList": [ 72 | { 73 | "ordering": 1, 74 | "itemOptionName": "SMALL", 75 | "itemOptionPrice": 0 76 | }, 77 | { 78 | "ordering": 2, 79 | "itemOptionName": "MEDIUM", 80 | "itemOptionPrice": 0 81 | }, 82 | { 83 | "ordering": 3, 84 | "itemOptionName": "LARGE", 85 | "itemOptionPrice": 0 86 | } 87 | ] 88 | 89 | }, 90 | { 91 | "ordering": "2", 92 | "itemOptionGroupName": "컬러", 93 | "itemOptionList": [ 94 | { 95 | "ordering": 1, 96 | "itemOptionName": "RED", 97 | "itemOptionPrice": 0 98 | }, 99 | { 100 | "ordering": 2, 101 | "itemOptionName": "BlUE", 102 | "itemOptionPrice": 1000 103 | } 104 | ] 105 | } 106 | ] 107 | } 108 | 109 | ### 아이템 2 활성화 110 | POST http://localhost:8080/api/v1/items/change-on-sales 111 | Content-Type: application/json 112 | 113 | { 114 | "itemToken": "itm_qCPGVzwvLVmCoZs6" 115 | } 116 | 117 | ### 아이템 등록 3 118 | POST http://localhost:8080/api/v1/items 119 | Content-Type: application/json 120 | 121 | { 122 | "partnerToken": "ptn_oWkmVAFErP2zgALi", 123 | "itemName": "양말", 124 | "itemPrice": 10000 125 | } 126 | 127 | ### 아이템 조회 128 | GET http://localhost:8080/api/v1/items/itm_lnTOhyzZcBheBsce 129 | Content-Type: application/json 130 | -------------------------------------------------------------------------------- /http-test/order-api.http: -------------------------------------------------------------------------------- 1 | ### 주문 요청 초기 저장 2 | POST http://localhost:8080/api/v1/orders/init 3 | Content-Type: application/json 4 | 5 | { 6 | "userId": "12345", 7 | "payMethod": "NAVER_PAY", 8 | "receiverName": "이희창", 9 | "receiverPhone": "01000001234", 10 | "receiverZipcode": "12345", 11 | "receiverAddress1": "서울시 강동구", 12 | "receiverAddress2": "우리집", 13 | "etcMessage": "감사합니다", 14 | "orderItemList": [ 15 | { 16 | "orderCount": 2, 17 | "itemToken": "itm_XqbL4h1l5mz1mnMZ", 18 | "itemName": "티셔츠", 19 | "itemPrice": "30000", 20 | "orderItemOptionGroupList": [ 21 | { 22 | "ordering": 1, 23 | "itemOptionGroupName": "사이즈", 24 | "orderItemOptionList": [ 25 | { 26 | "ordering": 1, 27 | "itemOptionName": "MEDIUM", 28 | "itemOptionPrice": 0 29 | } 30 | ] 31 | }, 32 | { 33 | "ordering": 2, 34 | "itemOptionGroupName": "컬러", 35 | "orderItemOptionList": [ 36 | { 37 | "ordering": 1, 38 | "itemOptionName": "GOLD", 39 | "itemOptionPrice": 1000 40 | } 41 | ] 42 | } 43 | ] 44 | }, 45 | { 46 | "orderCount": 1, 47 | "itemToken": "itm_qCPGVzwvLVmCoZs6", 48 | "itemName": "양말", 49 | "itemPrice": "10000", 50 | "orderItemOptionGroupList": [ 51 | { 52 | "ordering": 1, 53 | "itemOptionGroupName": "사이즈", 54 | "orderItemOptionList": [ 55 | { 56 | "ordering": 1, 57 | "itemOptionName": "LARGE", 58 | "itemOptionPrice": 0 59 | } 60 | ] 61 | }, 62 | { 63 | "ordering": 2, 64 | "itemOptionGroupName": "컬러", 65 | "orderItemOptionList": [ 66 | { 67 | "ordering": 1, 68 | "itemOptionName": "RED", 69 | "itemOptionPrice": 0 70 | } 71 | ] 72 | } 73 | ] 74 | } 75 | ] 76 | } 77 | 78 | ### 주문 결제 처리 79 | POST http://localhost:8080/api/v1/orders/payment-order 80 | Content-Type: application/json 81 | 82 | { 83 | "orderToken": "ord_lqAcdTqvORlU7ry3", 84 | "payMethod": "NAVER_PAY", 85 | "amount": "72000", 86 | "orderDescription": "주문테스트" 87 | } 88 | 89 | ### 주문 조회 90 | GET http://localhost:8080/api/v1/orders/ord_lqAcdTqvORlU7ry3 91 | -------------------------------------------------------------------------------- /http-test/partner-api.http: -------------------------------------------------------------------------------- 1 | ### 파트너 등록 2 | POST http://localhost:8080/api/v1/partners 3 | Content-Type: application/json 4 | 5 | { 6 | "partnerName": "테스트", 7 | "businessNo": "1234123456", 8 | "email": "greg.shiny82@gmail.com" 9 | } -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'order' 2 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/OrderApplication.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class OrderApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(OrderApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/application/item/ItemFacade.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.application.item; 2 | 3 | import dev.practice.order.domain.notification.NotificationService; 4 | import dev.practice.order.domain.item.ItemCommand; 5 | import dev.practice.order.domain.item.ItemInfo; 6 | import dev.practice.order.domain.item.ItemService; 7 | import lombok.RequiredArgsConstructor; 8 | import lombok.extern.slf4j.Slf4j; 9 | import org.springframework.stereotype.Service; 10 | 11 | @Slf4j 12 | @Service 13 | @RequiredArgsConstructor 14 | public class ItemFacade { 15 | private final ItemService itemService; 16 | private final NotificationService notificationService; 17 | 18 | public String registerItem(ItemCommand.RegisterItemRequest request, String partnerToken) { 19 | var itemToken = itemService.registerItem(request, partnerToken); 20 | notificationService.sendEmail(null, null, null); 21 | return itemToken; 22 | } 23 | 24 | public void changeOnSaleItem(String itemToken) { 25 | itemService.changeOnSale(itemToken); 26 | } 27 | 28 | public void changeEndOfSaleItem(String itemToken) { 29 | itemService.changeEndOfSale(itemToken); 30 | } 31 | 32 | public ItemInfo.Main retrieveItemInfo(String itemToken) { 33 | return itemService.retrieveItemInfo(itemToken); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/application/order/OrderFacade.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.application.order; 2 | 3 | import dev.practice.order.domain.notification.NotificationService; 4 | import dev.practice.order.domain.order.OrderCommand; 5 | import dev.practice.order.domain.order.OrderInfo; 6 | import dev.practice.order.domain.order.OrderService; 7 | import lombok.RequiredArgsConstructor; 8 | import lombok.extern.slf4j.Slf4j; 9 | import org.springframework.stereotype.Service; 10 | 11 | @Slf4j 12 | @Service 13 | @RequiredArgsConstructor 14 | public class OrderFacade { 15 | private final OrderService orderService; 16 | private final NotificationService notificationService; 17 | 18 | public String registerOrder(OrderCommand.RegisterOrder registerOrder) { 19 | var orderToken = orderService.registerOrder(registerOrder); 20 | notificationService.sendKakao("ORDER_COMPLETE", "content"); 21 | return orderToken; 22 | } 23 | 24 | public OrderInfo.Main retrieveOrder(String orderToken) { 25 | return orderService.retrieveOrder(orderToken); 26 | } 27 | 28 | public void paymentOrder(OrderCommand.PaymentRequest paymentRequest) { 29 | orderService.paymentOrder(paymentRequest); 30 | notificationService.sendKakao(null, null); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/application/partner/PartnerFacade.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.application.partner; 2 | 3 | import dev.practice.order.domain.notification.NotificationService; 4 | import dev.practice.order.domain.partner.PartnerCommand; 5 | import dev.practice.order.domain.partner.PartnerInfo; 6 | import dev.practice.order.domain.partner.PartnerService; 7 | import lombok.RequiredArgsConstructor; 8 | import lombok.extern.slf4j.Slf4j; 9 | import org.springframework.stereotype.Service; 10 | 11 | @Slf4j 12 | @Service 13 | @RequiredArgsConstructor 14 | public class PartnerFacade { 15 | private final PartnerService partnerService; 16 | private final NotificationService notificationService; 17 | 18 | public PartnerInfo registerPartner(PartnerCommand command) { 19 | var partnerInfo = partnerService.registerPartner(command); 20 | notificationService.sendEmail(partnerInfo.getEmail(), "title", "description"); 21 | return partnerInfo; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/common/exception/BaseException.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.common.exception; 2 | 3 | import dev.practice.order.common.response.ErrorCode; 4 | import lombok.Getter; 5 | 6 | /** 7 | * BaseException 또는 BaseException 을 확장한 Exception 은 8 | * 서비스 운영에서 예상이 가능한 Exception 을 표현한다. 9 | * 10 | * 그렇기 때문에 http status: 200 이면서 result: FAIL 을 표현하고 11 | * 특정 ErrorCode 만 alert 를 포함한 모니터링 대상으로 한다. 12 | */ 13 | @Getter 14 | public class BaseException extends RuntimeException { 15 | private ErrorCode errorCode; 16 | 17 | public BaseException() { 18 | } 19 | 20 | public BaseException(ErrorCode errorCode) { 21 | super(errorCode.getErrorMsg()); 22 | this.errorCode = errorCode; 23 | } 24 | 25 | public BaseException(String message, ErrorCode errorCode) { 26 | super(message); 27 | this.errorCode = errorCode; 28 | } 29 | 30 | public BaseException(String message, ErrorCode errorCode, Throwable cause) { 31 | super(message, cause); 32 | this.errorCode = errorCode; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/common/exception/EntityNotFoundException.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.common.exception; 2 | 3 | import dev.practice.order.common.response.ErrorCode; 4 | 5 | public class EntityNotFoundException extends BaseException { 6 | 7 | public EntityNotFoundException() { 8 | super(ErrorCode.COMMON_INVALID_PARAMETER); 9 | } 10 | 11 | public EntityNotFoundException(String message) { 12 | super(message, ErrorCode.COMMON_INVALID_PARAMETER); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/common/exception/IllegalStatusException.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.common.exception; 2 | 3 | 4 | import dev.practice.order.common.response.ErrorCode; 5 | 6 | public class IllegalStatusException extends BaseException { 7 | 8 | public IllegalStatusException() { 9 | super(ErrorCode.COMMON_ILLEGAL_STATUS); 10 | } 11 | 12 | public IllegalStatusException(String message) { 13 | super(message, ErrorCode.COMMON_ILLEGAL_STATUS); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/common/exception/InvalidParamException.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.common.exception; 2 | 3 | import dev.practice.order.common.response.ErrorCode; 4 | 5 | public class InvalidParamException extends BaseException { 6 | 7 | public InvalidParamException() { 8 | super(ErrorCode.COMMON_INVALID_PARAMETER); 9 | } 10 | 11 | public InvalidParamException(ErrorCode errorCode) { 12 | super(errorCode); 13 | } 14 | 15 | public InvalidParamException(String errorMsg) { 16 | super(errorMsg, ErrorCode.COMMON_INVALID_PARAMETER); 17 | } 18 | 19 | public InvalidParamException(String message, ErrorCode errorCode) { 20 | super(message, errorCode); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/common/interceptor/CommonHttpRequestInterceptor.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.common.interceptor; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.slf4j.MDC; 5 | import org.springframework.stereotype.Component; 6 | import org.springframework.util.StringUtils; 7 | import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; 8 | 9 | import javax.servlet.http.HttpServletRequest; 10 | import javax.servlet.http.HttpServletResponse; 11 | import java.util.UUID; 12 | 13 | @Slf4j 14 | @Component 15 | public class CommonHttpRequestInterceptor extends HandlerInterceptorAdapter { 16 | 17 | public static final String HEADER_REQUEST_UUID_KEY = "x-request-id"; 18 | 19 | @Override 20 | public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { 21 | String requestEventId = request.getHeader(HEADER_REQUEST_UUID_KEY); 22 | if (StringUtils.isEmpty(requestEventId)) { 23 | requestEventId = UUID.randomUUID().toString(); 24 | } 25 | 26 | MDC.put(HEADER_REQUEST_UUID_KEY, requestEventId); 27 | return true; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/common/response/CommonControllerAdvice.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.common.response; 2 | 3 | import com.google.common.collect.Lists; 4 | import dev.practice.order.common.exception.BaseException; 5 | import dev.practice.order.common.interceptor.CommonHttpRequestInterceptor; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.apache.catalina.connector.ClientAbortException; 8 | import org.slf4j.MDC; 9 | import org.springframework.core.NestedExceptionUtils; 10 | import org.springframework.http.HttpStatus; 11 | import org.springframework.validation.BindingResult; 12 | import org.springframework.validation.FieldError; 13 | import org.springframework.web.bind.MethodArgumentNotValidException; 14 | import org.springframework.web.bind.annotation.ControllerAdvice; 15 | import org.springframework.web.bind.annotation.ExceptionHandler; 16 | import org.springframework.web.bind.annotation.ResponseBody; 17 | import org.springframework.web.bind.annotation.ResponseStatus; 18 | 19 | import java.util.List; 20 | 21 | @Slf4j 22 | @ControllerAdvice 23 | public class CommonControllerAdvice { 24 | 25 | private static final List SPECIFIC_ALERT_TARGET_ERROR_CODE_LIST = Lists.newArrayList(); 26 | 27 | /** 28 | * http status: 500 AND result: FAIL 29 | * 시스템 예외 상황. 집중 모니터링 대상 30 | * 31 | * @param e 32 | * @return 33 | */ 34 | @ResponseBody 35 | @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) 36 | @ExceptionHandler(value = Exception.class) 37 | public CommonResponse onException(Exception e) { 38 | String eventId = MDC.get(CommonHttpRequestInterceptor.HEADER_REQUEST_UUID_KEY); 39 | log.error("eventId = {} ", eventId, e); 40 | return CommonResponse.fail(ErrorCode.COMMON_SYSTEM_ERROR); 41 | } 42 | 43 | /** 44 | * http status: 200 AND result: FAIL 45 | * 시스템은 이슈 없고, 비즈니스 로직 처리에서 에러가 발생함 46 | * 47 | * @param e 48 | * @return 49 | */ 50 | @ResponseBody 51 | @ResponseStatus(HttpStatus.OK) 52 | @ExceptionHandler(value = BaseException.class) 53 | public CommonResponse onBaseException(BaseException e) { 54 | String eventId = MDC.get(CommonHttpRequestInterceptor.HEADER_REQUEST_UUID_KEY); 55 | if (SPECIFIC_ALERT_TARGET_ERROR_CODE_LIST.contains(e.getErrorCode())) { 56 | log.error("[BaseException] eventId = {}, cause = {}, errorMsg = {}", eventId, NestedExceptionUtils.getMostSpecificCause(e), NestedExceptionUtils.getMostSpecificCause(e).getMessage()); 57 | } else { 58 | log.warn("[BaseException] eventId = {}, cause = {}, errorMsg = {}", eventId, NestedExceptionUtils.getMostSpecificCause(e), NestedExceptionUtils.getMostSpecificCause(e).getMessage()); 59 | } 60 | return CommonResponse.fail(e.getMessage(), e.getErrorCode().name()); 61 | } 62 | 63 | /** 64 | * 예상치 않은 Exception 중에서 모니터링 skip 이 가능한 Exception 을 처리할 때 65 | * ex) ClientAbortException 66 | * 67 | * @param e 68 | * @return 69 | */ 70 | @ResponseBody 71 | @ResponseStatus(HttpStatus.OK) 72 | @ExceptionHandler(value = {ClientAbortException.class}) 73 | public CommonResponse skipException(Exception e) { 74 | String eventId = MDC.get(CommonHttpRequestInterceptor.HEADER_REQUEST_UUID_KEY); 75 | log.warn("[skipException] eventId = {}, cause = {}, errorMsg = {}", eventId, NestedExceptionUtils.getMostSpecificCause(e), NestedExceptionUtils.getMostSpecificCause(e).getMessage()); 76 | return CommonResponse.fail(ErrorCode.COMMON_SYSTEM_ERROR); 77 | } 78 | 79 | /** 80 | * http status: 400 AND result: FAIL 81 | * request parameter 에러 82 | * 83 | * @param e 84 | * @return 85 | */ 86 | @ResponseBody 87 | @ResponseStatus(HttpStatus.BAD_REQUEST) 88 | @ExceptionHandler(value = {MethodArgumentNotValidException.class}) 89 | public CommonResponse methodArgumentNotValidException(MethodArgumentNotValidException e) { 90 | String eventId = MDC.get(CommonHttpRequestInterceptor.HEADER_REQUEST_UUID_KEY); 91 | log.warn("[BaseException] eventId = {}, errorMsg = {}", eventId, NestedExceptionUtils.getMostSpecificCause(e).getMessage()); 92 | BindingResult bindingResult = e.getBindingResult(); 93 | FieldError fe = bindingResult.getFieldError(); 94 | if (fe != null) { 95 | String message = "Request Error" + " " + fe.getField() + "=" + fe.getRejectedValue() + " (" + fe.getDefaultMessage() + ")"; 96 | return CommonResponse.fail(message, ErrorCode.COMMON_INVALID_PARAMETER.name()); 97 | } else { 98 | return CommonResponse.fail(ErrorCode.COMMON_INVALID_PARAMETER.getErrorMsg(), ErrorCode.COMMON_INVALID_PARAMETER.name()); 99 | } 100 | } 101 | } -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/common/response/CommonResponse.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.common.response; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | 8 | @Getter 9 | @Builder 10 | @NoArgsConstructor 11 | @AllArgsConstructor 12 | public class CommonResponse { 13 | private Result result; 14 | private T data; 15 | private String message; 16 | private String errorCode; 17 | 18 | public static CommonResponse success(T data, String message) { 19 | return (CommonResponse) CommonResponse.builder() 20 | .result(Result.SUCCESS) 21 | .data(data) 22 | .message(message) 23 | .build(); 24 | } 25 | 26 | public static CommonResponse success(T data) { 27 | return success(data, null); 28 | } 29 | 30 | public static CommonResponse fail(String message, String errorCode) { 31 | return CommonResponse.builder() 32 | .result(Result.FAIL) 33 | .message(message) 34 | .errorCode(errorCode) 35 | .build(); 36 | } 37 | 38 | public static CommonResponse fail(ErrorCode errorCode) { 39 | return CommonResponse.builder() 40 | .result(Result.FAIL) 41 | .message(errorCode.getErrorMsg()) 42 | .errorCode(errorCode.name()) 43 | .build(); 44 | } 45 | 46 | public enum Result { 47 | SUCCESS, FAIL 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/common/response/ErrorCode.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.common.response; 2 | 3 | import lombok.Getter; 4 | import lombok.RequiredArgsConstructor; 5 | 6 | @Getter 7 | @RequiredArgsConstructor 8 | public enum ErrorCode { 9 | COMMON_SYSTEM_ERROR("일시적인 오류가 발생했습니다. 잠시 후 다시 시도해주세요."), // 장애 상황 10 | COMMON_INVALID_PARAMETER("요청한 값이 올바르지 않습니다."), 11 | COMMON_ENTITY_NOT_FOUND("존재하지 않는 엔티티입니다."), 12 | COMMON_ILLEGAL_STATUS("잘못된 상태값입니다."), 13 | 14 | // GIFT 15 | GIFT_NOT_RECEIVABLE_CONDITION("선물 수락이 가능한 상태가 아닙니다."), 16 | GIFT_NOT_MODIFY_DELIVERY_CONDITION("배송지 변경이 가능한 상태가 아닙니다."); 17 | 18 | private final String errorMsg; 19 | 20 | public String getErrorMsg(Object... arg) { 21 | return String.format(errorMsg, arg); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/common/util/TokenGenerator.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.common.util; 2 | 3 | import org.apache.commons.lang3.RandomStringUtils; 4 | 5 | public class TokenGenerator { 6 | private static final int TOKEN_LENGTH = 20; 7 | 8 | public static String randomCharacter(int length) { 9 | return RandomStringUtils.randomAlphanumeric(length); 10 | } 11 | 12 | public static String randomCharacterWithPrefix(String prefix) { 13 | return prefix + randomCharacter(TOKEN_LENGTH - prefix.length()); 14 | } 15 | } -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/config/JpaAuditingConfiguration.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.config; 2 | 3 | import org.springframework.context.annotation.Configuration; 4 | import org.springframework.data.jpa.repository.config.EnableJpaAuditing; 5 | 6 | @EnableJpaAuditing 7 | @Configuration 8 | public class JpaAuditingConfiguration { 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/domain/AbstractEntity.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.domain; 2 | 3 | import lombok.Getter; 4 | import org.hibernate.annotations.CreationTimestamp; 5 | import org.hibernate.annotations.UpdateTimestamp; 6 | import org.springframework.data.jpa.domain.support.AuditingEntityListener; 7 | 8 | import javax.persistence.EntityListeners; 9 | import javax.persistence.MappedSuperclass; 10 | import java.time.ZonedDateTime; 11 | 12 | @Getter 13 | @MappedSuperclass 14 | @EntityListeners(AuditingEntityListener.class) 15 | public class AbstractEntity { 16 | 17 | @CreationTimestamp 18 | private ZonedDateTime createdAt; 19 | 20 | @UpdateTimestamp 21 | private ZonedDateTime updatedAt; 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/domain/item/Item.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.domain.item; 2 | 3 | import com.google.common.collect.Lists; 4 | import dev.practice.order.common.exception.InvalidParamException; 5 | import dev.practice.order.common.util.TokenGenerator; 6 | import dev.practice.order.domain.AbstractEntity; 7 | import dev.practice.order.domain.item.optiongroup.ItemOptionGroup; 8 | import lombok.Builder; 9 | import lombok.Getter; 10 | import lombok.NoArgsConstructor; 11 | import lombok.RequiredArgsConstructor; 12 | import org.apache.commons.lang3.StringUtils; 13 | 14 | import javax.persistence.*; 15 | import java.util.List; 16 | 17 | @Getter 18 | @Entity 19 | @NoArgsConstructor 20 | @Table(name = "items") 21 | public class Item extends AbstractEntity { 22 | private static final String ITEM_PREFIX = "itm_"; 23 | 24 | @Id 25 | @GeneratedValue(strategy = GenerationType.IDENTITY) 26 | private Long id; 27 | private String itemToken; 28 | private Long partnerId; 29 | private String itemName; 30 | private Long itemPrice; 31 | 32 | @OneToMany(fetch = FetchType.LAZY, mappedBy = "item", cascade = CascadeType.PERSIST) 33 | private List itemOptionGroupList = Lists.newArrayList(); 34 | 35 | @Enumerated(EnumType.STRING) 36 | private Status status; 37 | 38 | @Getter 39 | @RequiredArgsConstructor 40 | public enum Status { 41 | PREPARE("판매준비중"), 42 | ON_SALE("판매중"), 43 | END_OF_SALE("판매종료"); 44 | 45 | private final String description; 46 | } 47 | 48 | @Builder 49 | public Item(Long partnerId, String itemName, Long itemPrice) { 50 | if (partnerId == null) throw new InvalidParamException("Item.partnerId"); 51 | if (StringUtils.isBlank(itemName)) throw new InvalidParamException("Item.itemName"); 52 | if (itemPrice == null) throw new InvalidParamException("Item.itemPrice"); 53 | 54 | this.partnerId = partnerId; 55 | this.itemToken = TokenGenerator.randomCharacterWithPrefix(ITEM_PREFIX); 56 | this.itemName = itemName; 57 | this.itemPrice = itemPrice; 58 | this.status = Status.PREPARE; 59 | } 60 | 61 | public void changeOnSale() { 62 | this.status = Status.ON_SALE; 63 | } 64 | 65 | public void changeEndOfSale() { 66 | this.status = Status.END_OF_SALE; 67 | } 68 | 69 | public boolean availableSales() { 70 | return this.status == Status.ON_SALE; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/domain/item/ItemCommand.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.domain.item; 2 | 3 | import dev.practice.order.domain.item.option.ItemOption; 4 | import dev.practice.order.domain.item.optiongroup.ItemOptionGroup; 5 | import lombok.Builder; 6 | import lombok.Getter; 7 | import lombok.ToString; 8 | 9 | import java.util.List; 10 | 11 | public class ItemCommand { 12 | 13 | @Getter 14 | @Builder 15 | @ToString 16 | public static class RegisterItemRequest { 17 | private final String itemName; 18 | private final Long itemPrice; 19 | private final List itemOptionGroupRequestList; // ex) 색상, 사이즈 20 | 21 | public Item toEntity(Long partnerId) { 22 | return Item.builder() 23 | .partnerId(partnerId) 24 | .itemName(itemName) 25 | .itemPrice(itemPrice) 26 | .build(); 27 | } 28 | } 29 | 30 | @Getter 31 | @Builder 32 | @ToString 33 | public static class RegisterItemOptionGroupRequest { // ex) 색상 34 | private final Integer ordering; 35 | private final String itemOptionGroupName; 36 | private final List itemOptionRequestList; // ex) R, B, W 37 | 38 | public ItemOptionGroup toEntity(Item item) { 39 | return ItemOptionGroup.builder() 40 | .item(item) 41 | .ordering(ordering) 42 | .itemOptionGroupName(itemOptionGroupName) 43 | .build(); 44 | } 45 | } 46 | 47 | @Getter 48 | @Builder 49 | @ToString 50 | public static class RegisterItemOptionRequest { 51 | private final Integer ordering; 52 | private final String itemOptionName; 53 | private final Long itemOptionPrice; 54 | 55 | public ItemOption toEntity(ItemOptionGroup itemOptionGroup) { 56 | return ItemOption.builder() 57 | .itemOptionGroup(itemOptionGroup) 58 | .ordering(ordering) 59 | .itemOptionName(itemOptionName) 60 | .itemOptionPrice(itemOptionPrice) 61 | .build(); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/domain/item/ItemInfo.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.domain.item; 2 | 3 | import dev.practice.order.domain.item.option.ItemOption; 4 | import dev.practice.order.domain.item.optiongroup.ItemOptionGroup; 5 | import lombok.Getter; 6 | import lombok.ToString; 7 | 8 | import java.util.List; 9 | 10 | public class ItemInfo { 11 | 12 | @Getter 13 | @ToString 14 | public static class Main { 15 | private final String itemToken; 16 | private final Long partnerId; 17 | private final String itemName; 18 | private final Long itemPrice; 19 | private final Item.Status status; 20 | private final List itemOptionGroupList; 21 | 22 | public Main(Item item, List itemOptionGroupInfoList) { 23 | this.itemToken = item.getItemToken(); 24 | this.partnerId = item.getPartnerId(); 25 | this.itemName = item.getItemName(); 26 | this.itemPrice = item.getItemPrice(); 27 | this.status = item.getStatus(); 28 | this.itemOptionGroupList = itemOptionGroupInfoList; 29 | } 30 | } 31 | 32 | @Getter 33 | @ToString 34 | public static class ItemOptionGroupInfo { 35 | private final Integer ordering; 36 | private final String itemOptionGroupName; 37 | private final List itemOptionList; 38 | 39 | public ItemOptionGroupInfo(ItemOptionGroup itemOptionGroup, List itemOptionList) { 40 | this.ordering = itemOptionGroup.getOrdering(); 41 | this.itemOptionGroupName = itemOptionGroup.getItemOptionGroupName(); 42 | this.itemOptionList = itemOptionList; 43 | } 44 | } 45 | 46 | @Getter 47 | @ToString 48 | public static class ItemOptionInfo { 49 | private final Integer ordering; 50 | private final String itemOptionName; 51 | private final Long itemOptionPrice; 52 | 53 | public ItemOptionInfo(ItemOption itemOption) { 54 | this.ordering = itemOption.getOrdering(); 55 | this.itemOptionName = itemOption.getItemOptionName(); 56 | this.itemOptionPrice = itemOption.getItemOptionPrice(); 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/domain/item/ItemOptionSeriesFactory.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.domain.item; 2 | 3 | 4 | import dev.practice.order.domain.item.optiongroup.ItemOptionGroup; 5 | 6 | import java.util.List; 7 | 8 | public interface ItemOptionSeriesFactory { 9 | List store(ItemCommand.RegisterItemRequest request, Item item); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/domain/item/ItemReader.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.domain.item; 2 | 3 | import java.util.List; 4 | 5 | public interface ItemReader { 6 | Item getItemBy(String itemToken); 7 | List getItemOptionSeries(Item item); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/domain/item/ItemService.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.domain.item; 2 | 3 | public interface ItemService { 4 | String registerItem(ItemCommand.RegisterItemRequest request, String partnerToken); 5 | void changeOnSale(String itemToken); 6 | void changeEndOfSale(String itemToken); 7 | ItemInfo.Main retrieveItemInfo(String itemToken); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/domain/item/ItemServiceImpl.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.domain.item; 2 | 3 | import dev.practice.order.domain.partner.PartnerReader; 4 | import lombok.RequiredArgsConstructor; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.springframework.stereotype.Service; 7 | import org.springframework.transaction.annotation.Transactional; 8 | 9 | @Slf4j 10 | @Service 11 | @RequiredArgsConstructor 12 | public class ItemServiceImpl implements ItemService { 13 | private final PartnerReader partnerReader; 14 | private final ItemStore itemStore; 15 | private final ItemReader itemReader; 16 | private final ItemOptionSeriesFactory itemOptionSeriesFactory; 17 | 18 | @Override 19 | @Transactional 20 | public String registerItem(ItemCommand.RegisterItemRequest command, String partnerToken) { 21 | var partner = partnerReader.getPartner(partnerToken); 22 | var initItem = command.toEntity(partner.getId()); 23 | var item = itemStore.store(initItem); 24 | itemOptionSeriesFactory.store(command, item); 25 | return item.getItemToken(); 26 | } 27 | 28 | @Override 29 | @Transactional 30 | public void changeOnSale(String itemToken) { 31 | var item = itemReader.getItemBy(itemToken); 32 | item.changeOnSale(); 33 | } 34 | 35 | @Override 36 | @Transactional 37 | public void changeEndOfSale(String itemToken) { 38 | var item = itemReader.getItemBy(itemToken); 39 | item.changeEndOfSale(); 40 | } 41 | 42 | @Override 43 | @Transactional(readOnly = true) 44 | public ItemInfo.Main retrieveItemInfo(String itemToken) { 45 | var item = itemReader.getItemBy(itemToken); 46 | var itemOptionGroupInfoList = itemReader.getItemOptionSeries(item); 47 | return new ItemInfo.Main(item, itemOptionGroupInfoList); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/domain/item/ItemStore.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.domain.item; 2 | 3 | public interface ItemStore { 4 | Item store(Item initItem); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/domain/item/option/ItemOption.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.domain.item.option; 2 | 3 | 4 | import dev.practice.order.common.exception.InvalidParamException; 5 | import dev.practice.order.domain.AbstractEntity; 6 | import dev.practice.order.domain.item.optiongroup.ItemOptionGroup; 7 | import lombok.Builder; 8 | import lombok.Getter; 9 | import lombok.NoArgsConstructor; 10 | import lombok.extern.slf4j.Slf4j; 11 | import org.apache.commons.lang3.StringUtils; 12 | 13 | import javax.persistence.*; 14 | 15 | @Slf4j 16 | @Getter 17 | @Entity 18 | @NoArgsConstructor 19 | @Table(name = "item_options") 20 | public class ItemOption extends AbstractEntity { 21 | 22 | @Id 23 | @GeneratedValue(strategy = GenerationType.IDENTITY) 24 | private Long id; 25 | 26 | @ManyToOne 27 | @JoinColumn(name = "item_option_group_id") 28 | private ItemOptionGroup itemOptionGroup; 29 | private Integer ordering; 30 | private String itemOptionName; 31 | private Long itemOptionPrice; 32 | 33 | @Builder 34 | public ItemOption( 35 | ItemOptionGroup itemOptionGroup, 36 | Integer ordering, 37 | String itemOptionName, 38 | Long itemOptionPrice 39 | ) { 40 | if (itemOptionGroup == null) throw new InvalidParamException("ItemOption.itemOptionGroup"); 41 | if (ordering == null) throw new InvalidParamException("ItemOption.ordering"); 42 | if (StringUtils.isBlank(itemOptionName)) throw new InvalidParamException("ItemOption.itemOptionName"); 43 | if (itemOptionPrice == null) throw new InvalidParamException("ItemOption.itemOptionPrice"); 44 | 45 | this.itemOptionGroup = itemOptionGroup; 46 | this.ordering = ordering; 47 | this.itemOptionName = itemOptionName; 48 | this.itemOptionPrice = itemOptionPrice; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/domain/item/option/ItemOptionStore.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.domain.item.option; 2 | 3 | public interface ItemOptionStore { 4 | void store(ItemOption itemOption); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/domain/item/optiongroup/ItemOptionGroup.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.domain.item.optiongroup; 2 | 3 | import com.google.common.collect.Lists; 4 | import dev.practice.order.common.exception.InvalidParamException; 5 | import dev.practice.order.domain.AbstractEntity; 6 | import dev.practice.order.domain.item.Item; 7 | import dev.practice.order.domain.item.option.ItemOption; 8 | import lombok.Builder; 9 | import lombok.Getter; 10 | import lombok.NoArgsConstructor; 11 | import lombok.extern.slf4j.Slf4j; 12 | import org.apache.commons.lang3.StringUtils; 13 | 14 | import javax.persistence.*; 15 | import java.util.List; 16 | 17 | @Slf4j 18 | @Getter 19 | @Entity 20 | @NoArgsConstructor 21 | @Table(name = "item_option_groups") 22 | public class ItemOptionGroup extends AbstractEntity { 23 | 24 | @Id 25 | @GeneratedValue(strategy = GenerationType.IDENTITY) 26 | private Long id; 27 | 28 | @ManyToOne 29 | @JoinColumn(name = "item_id") 30 | private Item item; 31 | private Integer ordering; 32 | private String itemOptionGroupName; 33 | 34 | @OneToMany(fetch = FetchType.LAZY, mappedBy = "itemOptionGroup", cascade = CascadeType.PERSIST) 35 | private List itemOptionList = Lists.newArrayList(); 36 | 37 | @Builder 38 | public ItemOptionGroup(Item item, Integer ordering, String itemOptionGroupName) { 39 | if (item == null) throw new InvalidParamException("ItemOptionGroup.item"); 40 | if (ordering == null) throw new InvalidParamException("ItemOptionGroup.ordering"); 41 | if (StringUtils.isBlank(itemOptionGroupName)) 42 | throw new InvalidParamException("ItemOptionGroup.itemOptionGroupName"); 43 | 44 | this.item = item; 45 | this.ordering = ordering; 46 | this.itemOptionGroupName = itemOptionGroupName; 47 | } 48 | 49 | public ItemOptionGroup addItemOption(ItemOption itemOption) { 50 | this.itemOptionList.add(itemOption); 51 | return this; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/domain/item/optiongroup/ItemOptionGroupStore.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.domain.item.optiongroup; 2 | 3 | public interface ItemOptionGroupStore { 4 | ItemOptionGroup store(ItemOptionGroup itemOptionGroup); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/domain/notification/NotificationService.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.domain.notification; 2 | 3 | public interface NotificationService { 4 | void sendEmail(String email, String title, String description); 5 | void sendKakao(String phoneNo, String description); 6 | void sendSms(String phoneNo, String description); 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/domain/order/Order.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.domain.order; 2 | 3 | import com.google.common.collect.Lists; 4 | import dev.practice.order.common.exception.IllegalStatusException; 5 | import dev.practice.order.common.exception.InvalidParamException; 6 | import dev.practice.order.common.util.TokenGenerator; 7 | import dev.practice.order.domain.AbstractEntity; 8 | import dev.practice.order.domain.order.fragment.DeliveryFragment; 9 | import dev.practice.order.domain.order.item.OrderItem; 10 | import lombok.*; 11 | import lombok.extern.slf4j.Slf4j; 12 | import org.apache.commons.lang3.StringUtils; 13 | 14 | import javax.persistence.*; 15 | import java.time.ZonedDateTime; 16 | import java.util.List; 17 | 18 | @Slf4j 19 | @Getter 20 | @Entity 21 | @ToString 22 | @NoArgsConstructor 23 | @Table(name = "orders") 24 | public class Order extends AbstractEntity { 25 | 26 | private static final String ORDER_PREFIX = "ord_"; 27 | 28 | @Id 29 | @GeneratedValue(strategy = GenerationType.IDENTITY) 30 | private Long id; 31 | private String orderToken; 32 | private Long userId; 33 | private String payMethod; 34 | 35 | @OneToMany(fetch = FetchType.LAZY, mappedBy = "order", cascade = CascadeType.PERSIST) 36 | private List orderItemList = Lists.newArrayList(); 37 | 38 | @Embedded 39 | private DeliveryFragment deliveryFragment; 40 | 41 | private ZonedDateTime orderedAt; 42 | 43 | @Enumerated(EnumType.STRING) 44 | private Status status; 45 | 46 | @Getter 47 | @RequiredArgsConstructor 48 | public enum Status { 49 | INIT("주문시작"), 50 | ORDER_COMPLETE("주문완료"), 51 | DELIVERY_PREPARE("배송준비"), 52 | IN_DELIVERY("배송중"), 53 | DELIVERY_COMPLETE("배송완료"); 54 | 55 | private final String description; 56 | } 57 | 58 | @Builder 59 | public Order( 60 | Long userId, 61 | String payMethod, 62 | DeliveryFragment deliveryFragment 63 | ) { 64 | if (userId == null) throw new InvalidParamException("Order.userId"); 65 | if (StringUtils.isEmpty(payMethod)) throw new InvalidParamException("Order.payMethod"); 66 | if (deliveryFragment == null) throw new InvalidParamException("Order.deliveryFragment"); 67 | 68 | this.orderToken = TokenGenerator.randomCharacterWithPrefix(ORDER_PREFIX); 69 | this.userId = userId; 70 | this.payMethod = payMethod; 71 | this.deliveryFragment = deliveryFragment; 72 | this.orderedAt = ZonedDateTime.now(); 73 | this.status = Status.INIT; 74 | } 75 | 76 | /** 77 | * 주문 가격 = 주문 상품의 총 가격 78 | * 주문 하나의 상품의 가격 = (상품 가격 + 상품 옵션 가격) * 주문 갯수 79 | */ 80 | public Long calculateTotalAmount() { 81 | return orderItemList.stream() 82 | .mapToLong(OrderItem::calculateTotalAmount) 83 | .sum(); 84 | } 85 | 86 | public void orderComplete() { 87 | if (this.status != Status.INIT) throw new IllegalStatusException(); 88 | this.status = Status.ORDER_COMPLETE; 89 | } 90 | 91 | public boolean isAlreadyPaymentComplete() { 92 | switch (this.status) { 93 | case ORDER_COMPLETE: 94 | case DELIVERY_PREPARE: 95 | case IN_DELIVERY: 96 | case DELIVERY_COMPLETE: 97 | return true; 98 | } 99 | return false; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/domain/order/OrderCommand.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.domain.order; 2 | 3 | import dev.practice.order.domain.item.Item; 4 | import dev.practice.order.domain.order.fragment.DeliveryFragment; 5 | import dev.practice.order.domain.order.item.OrderItem; 6 | import dev.practice.order.domain.order.item.OrderItemOption; 7 | import dev.practice.order.domain.order.item.OrderItemOptionGroup; 8 | import dev.practice.order.domain.order.payment.PayMethod; 9 | import lombok.Builder; 10 | import lombok.Getter; 11 | import lombok.ToString; 12 | 13 | import java.util.List; 14 | 15 | public class OrderCommand { 16 | 17 | @Getter 18 | @Builder 19 | @ToString 20 | public static class RegisterOrder { 21 | private final Long userId; 22 | private final String payMethod; 23 | private final String receiverName; 24 | private final String receiverPhone; 25 | private final String receiverZipcode; 26 | private final String receiverAddress1; 27 | private final String receiverAddress2; 28 | private final String etcMessage; 29 | private final List orderItemList; 30 | 31 | public Order toEntity() { 32 | var deliveryFragment = DeliveryFragment.builder() 33 | .receiverName(receiverName) 34 | .receiverPhone(receiverPhone) 35 | .receiverZipcode(receiverZipcode) 36 | .receiverAddress1(receiverAddress1) 37 | .receiverAddress2(receiverAddress2) 38 | .etcMessage(etcMessage) 39 | .build(); 40 | 41 | return Order.builder() 42 | .userId(userId) 43 | .payMethod(payMethod) 44 | .deliveryFragment(deliveryFragment) 45 | .build(); 46 | } 47 | } 48 | 49 | @Getter 50 | @Builder 51 | @ToString 52 | public static class RegisterOrderItem { 53 | private final Integer orderCount; 54 | private final String itemToken; 55 | private final String itemName; 56 | private final Long itemPrice; 57 | private final List orderItemOptionGroupList; 58 | 59 | public OrderItem toEntity(Order order, Item item) { 60 | return OrderItem.builder() 61 | .order(order) 62 | .orderCount(orderCount) 63 | .partnerId(item.getPartnerId()) 64 | .itemId(item.getId()) 65 | .itemToken(itemToken) 66 | .itemName(itemName) 67 | .itemPrice(itemPrice) 68 | .build(); 69 | } 70 | } 71 | 72 | @Getter 73 | @Builder 74 | @ToString 75 | public static class RegisterOrderItemOptionGroup { 76 | private final Integer ordering; 77 | private final String itemOptionGroupName; 78 | private final List orderItemOptionList; 79 | 80 | public OrderItemOptionGroup toEntity(OrderItem orderItem) { 81 | return OrderItemOptionGroup.builder() 82 | .orderItem(orderItem) 83 | .ordering(ordering) 84 | .itemOptionGroupName(itemOptionGroupName) 85 | .build(); 86 | } 87 | } 88 | 89 | @Getter 90 | @Builder 91 | @ToString 92 | public static class RegisterOrderItemOption { 93 | private final Integer ordering; 94 | private final String itemOptionName; 95 | private final Long itemOptionPrice; 96 | 97 | public OrderItemOption toEntity(OrderItemOptionGroup orderItemOptionGroup) { 98 | return OrderItemOption.builder() 99 | .orderItemOptionGroup(orderItemOptionGroup) 100 | .ordering(ordering) 101 | .itemOptionName(itemOptionName) 102 | .itemOptionPrice(itemOptionPrice) 103 | .build(); 104 | } 105 | } 106 | 107 | @Getter 108 | @Builder 109 | @ToString 110 | public static class PaymentRequest { 111 | private final String orderToken; 112 | private final Long amount; 113 | private final PayMethod payMethod; 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/domain/order/OrderInfo.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.domain.order; 2 | 3 | import lombok.Builder; 4 | import lombok.Getter; 5 | import lombok.ToString; 6 | 7 | import java.time.ZonedDateTime; 8 | import java.util.List; 9 | 10 | public class OrderInfo { 11 | 12 | @Getter 13 | @Builder 14 | @ToString 15 | public static class Main { 16 | private final Long orderId; 17 | private final String orderToken; 18 | private final Long userId; 19 | private final String payMethod; 20 | private final Long totalAmount; 21 | private final DeliveryInfo deliveryInfo; 22 | private final ZonedDateTime orderedAt; 23 | private final String status; 24 | private final String statusDescription; 25 | private final List orderItemList; 26 | } 27 | 28 | @Getter 29 | @Builder 30 | @ToString 31 | public static class DeliveryInfo { 32 | private final String receiverName; 33 | private final String receiverPhone; 34 | private final String receiverZipcode; 35 | private final String receiverAddress1; 36 | private final String receiverAddress2; 37 | private final String etcMessage; 38 | } 39 | 40 | @Getter 41 | @Builder 42 | @ToString 43 | public static class OrderItem { 44 | private final Integer orderCount; 45 | private final Long partnerId; 46 | private final Long itemId; 47 | private final String itemName; 48 | private final Long totalAmount; 49 | private final Long itemPrice; 50 | private final String deliveryStatus; 51 | private final String deliveryStatusDescription; 52 | private final List orderItemOptionGroupList; 53 | } 54 | 55 | @Getter 56 | @Builder 57 | @ToString 58 | public static class OrderItemOptionGroup { 59 | private final Integer ordering; 60 | private final String itemOptionGroupName; 61 | private final List orderItemOptionList; 62 | } 63 | 64 | @Getter 65 | @Builder 66 | @ToString 67 | public static class OrderItemOption { 68 | private final Integer ordering; 69 | private final String itemOptionName; 70 | private final Long itemOptionPrice; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/domain/order/OrderInfoMapper.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.domain.order; 2 | 3 | import dev.practice.order.domain.order.item.OrderItem; 4 | import dev.practice.order.domain.order.item.OrderItemOption; 5 | import dev.practice.order.domain.order.item.OrderItemOptionGroup; 6 | import org.mapstruct.*; 7 | 8 | import java.util.List; 9 | 10 | @Mapper( 11 | componentModel = "spring", 12 | injectionStrategy = InjectionStrategy.CONSTRUCTOR, 13 | unmappedTargetPolicy = ReportingPolicy.ERROR 14 | ) 15 | public interface OrderInfoMapper { 16 | 17 | @Mappings({ 18 | @Mapping(source = "order.id", target = "orderId"), 19 | @Mapping(source = "order.deliveryFragment", target = "deliveryInfo"), 20 | @Mapping(expression = "java(order.calculateTotalAmount())", target = "totalAmount"), 21 | @Mapping(expression = "java(order.getStatus().name())", target = "status"), 22 | @Mapping(expression = "java(order.getStatus().getDescription())", target = "statusDescription") 23 | }) 24 | OrderInfo.Main of(Order order, List orderItemList); 25 | 26 | @Mappings({ 27 | @Mapping(expression = "java(orderItem.getDeliveryStatus().name())", target = "deliveryStatus"), 28 | @Mapping(expression = "java(orderItem.getDeliveryStatus().getDescription())", target = "deliveryStatusDescription"), 29 | @Mapping(expression = "java(orderItem.calculateTotalAmount())", target = "totalAmount") 30 | }) 31 | OrderInfo.OrderItem of(OrderItem orderItem); 32 | 33 | OrderInfo.OrderItemOptionGroup of(OrderItemOptionGroup orderItemOptionGroup); 34 | 35 | OrderInfo.OrderItemOption of(OrderItemOption orderItemOption); 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/domain/order/OrderItemSeriesFactory.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.domain.order; 2 | 3 | import dev.practice.order.domain.order.item.OrderItem; 4 | 5 | import java.util.List; 6 | 7 | public interface OrderItemSeriesFactory { 8 | List store(Order order, OrderCommand.RegisterOrder requestOrder); 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/domain/order/OrderReader.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.domain.order; 2 | 3 | public interface OrderReader { 4 | Order getOrder(String orderToken); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/domain/order/OrderService.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.domain.order; 2 | 3 | public interface OrderService { 4 | String registerOrder(OrderCommand.RegisterOrder registerOrder); 5 | 6 | void paymentOrder(OrderCommand.PaymentRequest paymentRequest); 7 | 8 | OrderInfo.Main retrieveOrder(String orderToken); 9 | 10 | } -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/domain/order/OrderServiceImpl.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.domain.order; 2 | 3 | import dev.practice.order.domain.order.payment.PaymentProcessor; 4 | import lombok.RequiredArgsConstructor; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.springframework.stereotype.Service; 7 | import org.springframework.transaction.annotation.Transactional; 8 | 9 | @Slf4j 10 | @Service 11 | @RequiredArgsConstructor 12 | public class OrderServiceImpl implements OrderService { 13 | private final OrderStore orderStore; 14 | private final OrderReader orderReader; 15 | private final OrderItemSeriesFactory orderItemSeriesFactory; 16 | private final PaymentProcessor paymentProcessor; 17 | private final OrderInfoMapper orderInfoMapper; 18 | 19 | @Override 20 | @Transactional 21 | public String registerOrder(OrderCommand.RegisterOrder requestOrder) { 22 | Order order = orderStore.store(requestOrder.toEntity()); 23 | orderItemSeriesFactory.store(order, requestOrder); 24 | return order.getOrderToken(); 25 | } 26 | 27 | @Override 28 | @Transactional 29 | public void paymentOrder(OrderCommand.PaymentRequest paymentRequest) { 30 | var orderToken = paymentRequest.getOrderToken(); 31 | var order = orderReader.getOrder(orderToken); 32 | paymentProcessor.pay(order, paymentRequest); 33 | order.orderComplete(); 34 | } 35 | 36 | @Override 37 | @Transactional(readOnly = true) 38 | public OrderInfo.Main retrieveOrder(String orderToken) { 39 | var order = orderReader.getOrder(orderToken); 40 | var orderItemList = order.getOrderItemList(); 41 | return orderInfoMapper.of(order, orderItemList); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/domain/order/OrderStore.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.domain.order; 2 | 3 | import dev.practice.order.domain.order.item.OrderItem; 4 | import dev.practice.order.domain.order.item.OrderItemOption; 5 | import dev.practice.order.domain.order.item.OrderItemOptionGroup; 6 | 7 | public interface OrderStore { 8 | Order store(Order order); 9 | OrderItem store(OrderItem orderItem); 10 | OrderItemOptionGroup store(OrderItemOptionGroup orderItemOptionGroup); 11 | OrderItemOption store(OrderItemOption orderItemOption); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/domain/order/fragment/DeliveryFragment.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.domain.order.fragment; 2 | 3 | import dev.practice.order.common.exception.InvalidParamException; 4 | import lombok.Builder; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | import org.apache.commons.lang3.StringUtils; 8 | 9 | import javax.persistence.Embeddable; 10 | 11 | @Getter 12 | @Embeddable 13 | @NoArgsConstructor 14 | public class DeliveryFragment { 15 | private String receiverName; 16 | private String receiverPhone; 17 | private String receiverZipcode; 18 | private String receiverAddress1; 19 | private String receiverAddress2; 20 | private String etcMessage; 21 | 22 | @Builder 23 | public DeliveryFragment( 24 | String receiverName, 25 | String receiverPhone, 26 | String receiverZipcode, 27 | String receiverAddress1, 28 | String receiverAddress2, 29 | String etcMessage 30 | ) { 31 | if (StringUtils.isEmpty(receiverName)) throw new InvalidParamException("DeliveryFragment receiverName"); 32 | if (StringUtils.isEmpty(receiverPhone)) throw new InvalidParamException("DeliveryFragment receiverPhone"); 33 | if (StringUtils.isEmpty(receiverZipcode)) throw new InvalidParamException("DeliveryFragment receiverZipcode"); 34 | if (StringUtils.isEmpty(receiverAddress1)) throw new InvalidParamException("DeliveryFragment receiverAddress1"); 35 | if (StringUtils.isEmpty(receiverAddress2)) throw new InvalidParamException("DeliveryFragment receiverAddress2"); 36 | if (StringUtils.isEmpty(etcMessage)) throw new InvalidParamException("DeliveryFragment etcMessage"); 37 | 38 | this.receiverName = receiverName; 39 | this.receiverPhone = receiverPhone; 40 | this.receiverZipcode = receiverZipcode; 41 | this.receiverAddress1 = receiverAddress1; 42 | this.receiverAddress2 = receiverAddress2; 43 | this.etcMessage = etcMessage; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/domain/order/item/OrderItem.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.domain.order.item; 2 | 3 | import com.google.common.collect.Lists; 4 | import dev.practice.order.common.exception.InvalidParamException; 5 | import dev.practice.order.domain.AbstractEntity; 6 | import dev.practice.order.domain.order.Order; 7 | import lombok.AllArgsConstructor; 8 | import lombok.Builder; 9 | import lombok.Getter; 10 | import lombok.NoArgsConstructor; 11 | import org.apache.commons.lang3.StringUtils; 12 | 13 | import javax.persistence.*; 14 | import java.util.List; 15 | 16 | @Entity 17 | @Getter 18 | @NoArgsConstructor 19 | @Table(name = "order_items") 20 | public class OrderItem extends AbstractEntity { 21 | 22 | @Id 23 | @GeneratedValue(strategy = GenerationType.IDENTITY) 24 | private Long id; 25 | 26 | @ManyToOne 27 | @JoinColumn(name = "order_id") 28 | private Order order; 29 | 30 | private Integer orderCount; 31 | private Long partnerId; 32 | private Long itemId; 33 | private String itemName; 34 | private String itemToken; 35 | private Long itemPrice; 36 | 37 | @OneToMany(fetch = FetchType.LAZY, mappedBy = "orderItem", cascade = CascadeType.PERSIST) 38 | private List orderItemOptionGroupList = Lists.newArrayList(); 39 | 40 | @Enumerated(EnumType.STRING) 41 | private DeliveryStatus deliveryStatus; 42 | 43 | @Getter 44 | @AllArgsConstructor 45 | public enum DeliveryStatus { 46 | BEFORE_DELIVERY("배송전"), 47 | DELIVERY_PREPARE("배송준비중"), 48 | DELIVERING("배송중"), 49 | COMPLETE_DELIVERY("배송완료"); 50 | 51 | private final String description; 52 | } 53 | 54 | @Builder 55 | public OrderItem( 56 | Order order, 57 | Integer orderCount, 58 | Long partnerId, 59 | Long itemId, 60 | String itemName, 61 | String itemToken, 62 | Long itemPrice 63 | ) { 64 | if (order == null) throw new InvalidParamException("OrderItemLine.order"); 65 | if (orderCount == null) throw new InvalidParamException("OrderItemLine.orderCount"); 66 | if (partnerId == null) throw new InvalidParamException("OrderItemLine.partnerId"); 67 | if (itemId == null && StringUtils.isEmpty(itemName)) 68 | throw new InvalidParamException("OrderItemLine.itemNo and itemName"); 69 | if (StringUtils.isEmpty(itemToken)) throw new InvalidParamException("OrderItemLine.itemToken"); 70 | if (itemPrice == null) throw new InvalidParamException("OrderItemLine.itemPrice"); 71 | 72 | this.order = order; 73 | this.orderCount = orderCount; 74 | this.partnerId = partnerId; 75 | this.itemId = itemId; 76 | this.itemName = itemName; 77 | this.itemToken = itemToken; 78 | this.itemPrice = itemPrice; 79 | this.deliveryStatus = DeliveryStatus.BEFORE_DELIVERY; 80 | } 81 | 82 | public Long calculateTotalAmount() { 83 | var itemOptionTotalAmount = orderItemOptionGroupList.stream() 84 | .mapToLong(OrderItemOptionGroup::calculateTotalAmount) 85 | .sum(); 86 | return (itemPrice + itemOptionTotalAmount) * orderCount; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/domain/order/item/OrderItemOption.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.domain.order.item; 2 | 3 | import dev.practice.order.common.exception.InvalidParamException; 4 | import dev.practice.order.domain.AbstractEntity; 5 | import lombok.Builder; 6 | import lombok.Getter; 7 | import lombok.NoArgsConstructor; 8 | import org.apache.commons.lang3.StringUtils; 9 | 10 | import javax.persistence.*; 11 | 12 | @Entity 13 | @Getter 14 | @NoArgsConstructor 15 | @Table(name = "order_item_options") 16 | public class OrderItemOption extends AbstractEntity { 17 | 18 | @Id 19 | @GeneratedValue(strategy = GenerationType.IDENTITY) 20 | private Long id; 21 | 22 | @ManyToOne 23 | @JoinColumn(name = "order_item_option_group_id") 24 | private OrderItemOptionGroup orderItemOptionGroup; 25 | private Integer ordering; 26 | private String itemOptionName; 27 | private Long itemOptionPrice; 28 | 29 | @Builder 30 | public OrderItemOption( 31 | OrderItemOptionGroup orderItemOptionGroup, 32 | Integer ordering, 33 | String itemOptionName, 34 | Long itemOptionPrice) { 35 | if (orderItemOptionGroup == null) throw new InvalidParamException(); 36 | if (ordering == null) throw new InvalidParamException(); 37 | if (StringUtils.isEmpty(itemOptionName)) throw new InvalidParamException(); 38 | if (itemOptionPrice == null) throw new InvalidParamException(); 39 | 40 | this.orderItemOptionGroup = orderItemOptionGroup; 41 | this.ordering = ordering; 42 | this.itemOptionName = itemOptionName; 43 | this.itemOptionPrice = itemOptionPrice; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/domain/order/item/OrderItemOptionGroup.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.domain.order.item; 2 | 3 | import com.google.common.collect.Lists; 4 | import dev.practice.order.common.exception.InvalidParamException; 5 | import dev.practice.order.domain.AbstractEntity; 6 | import lombok.Builder; 7 | import lombok.Getter; 8 | import lombok.NoArgsConstructor; 9 | import lombok.extern.slf4j.Slf4j; 10 | import org.apache.commons.lang3.StringUtils; 11 | 12 | import javax.persistence.*; 13 | import java.util.List; 14 | 15 | @Slf4j 16 | @Getter 17 | @Entity 18 | @NoArgsConstructor 19 | @Table(name = "order_item_option_groups") 20 | public class OrderItemOptionGroup extends AbstractEntity { 21 | 22 | @Id 23 | @GeneratedValue(strategy = GenerationType.IDENTITY) 24 | private Long id; 25 | 26 | @ManyToOne 27 | @JoinColumn(name = "order_item_id") 28 | private OrderItem orderItem; 29 | private Integer ordering; 30 | private String itemOptionGroupName; 31 | 32 | @OneToMany(fetch = FetchType.LAZY, mappedBy = "orderItemOptionGroup", cascade = CascadeType.PERSIST) 33 | private List orderItemOptionList = Lists.newArrayList(); 34 | 35 | @Builder 36 | public OrderItemOptionGroup( 37 | OrderItem orderItem, 38 | Integer ordering, 39 | String itemOptionGroupName 40 | ) { 41 | if (orderItem == null) throw new InvalidParamException(); 42 | if (ordering == null) throw new InvalidParamException(); 43 | if (StringUtils.isEmpty(itemOptionGroupName)) throw new InvalidParamException(); 44 | 45 | this.orderItem = orderItem; 46 | this.ordering = ordering; 47 | this.itemOptionGroupName = itemOptionGroupName; 48 | } 49 | 50 | public Long calculateTotalAmount() { 51 | return orderItemOptionList.stream() 52 | .mapToLong(OrderItemOption::getItemOptionPrice) 53 | .sum(); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/domain/order/payment/PayMethod.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.domain.order.payment; 2 | 3 | public enum PayMethod { 4 | CARD, NAVER_PAY, TOSS_PAY, KAKAO_PAY 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/domain/order/payment/PaymentProcessor.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.domain.order.payment; 2 | 3 | import dev.practice.order.domain.order.Order; 4 | import dev.practice.order.domain.order.OrderCommand; 5 | 6 | public interface PaymentProcessor { 7 | void pay(Order order, OrderCommand.PaymentRequest request); 8 | } -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/domain/order/payment/validator/PayAmountValidator.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.domain.order.payment.validator; 2 | 3 | import dev.practice.order.common.exception.InvalidParamException; 4 | import dev.practice.order.domain.order.Order; 5 | import dev.practice.order.domain.order.OrderCommand; 6 | import org.springframework.stereotype.Component; 7 | 8 | @org.springframework.core.annotation.Order(value = 1) 9 | @Component 10 | public class PayAmountValidator implements PaymentValidator { 11 | 12 | @Override 13 | public void validate(Order order, OrderCommand.PaymentRequest paymentRequest) { 14 | if (!order.calculateTotalAmount().equals(paymentRequest.getAmount())) 15 | throw new InvalidParamException("주문가격이 불일치합니다"); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/domain/order/payment/validator/PayMethodValidator.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.domain.order.payment.validator; 2 | 3 | import dev.practice.order.common.exception.InvalidParamException; 4 | import dev.practice.order.domain.order.Order; 5 | import dev.practice.order.domain.order.OrderCommand; 6 | import org.springframework.stereotype.Component; 7 | 8 | @org.springframework.core.annotation.Order(value = 2) 9 | @Component 10 | public class PayMethodValidator implements PaymentValidator { 11 | 12 | @Override 13 | public void validate(Order order, OrderCommand.PaymentRequest paymentRequest) { 14 | if (!order.getPayMethod().equals(paymentRequest.getPayMethod().name())) { 15 | throw new InvalidParamException("주문 과정에서의 결제수단이 다릅니다."); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/domain/order/payment/validator/PayStatusValidator.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.domain.order.payment.validator; 2 | 3 | import dev.practice.order.common.exception.InvalidParamException; 4 | import dev.practice.order.domain.order.Order; 5 | import dev.practice.order.domain.order.OrderCommand; 6 | import org.springframework.stereotype.Component; 7 | 8 | @org.springframework.core.annotation.Order(value = 3) 9 | @Component 10 | public class PayStatusValidator implements PaymentValidator { 11 | 12 | @Override 13 | public void validate(Order order, OrderCommand.PaymentRequest paymentRequest) { 14 | if (order.isAlreadyPaymentComplete()) throw new InvalidParamException("이미 결제완료된 주문입니다"); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/domain/order/payment/validator/PaymentValidator.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.domain.order.payment.validator; 2 | 3 | import dev.practice.order.domain.order.Order; 4 | import dev.practice.order.domain.order.OrderCommand; 5 | 6 | public interface PaymentValidator { 7 | void validate(Order order, OrderCommand.PaymentRequest paymentRequest); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/domain/partner/Partner.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.domain.partner; 2 | 3 | import dev.practice.order.common.exception.InvalidParamException; 4 | import dev.practice.order.common.util.TokenGenerator; 5 | import dev.practice.order.domain.AbstractEntity; 6 | import lombok.Builder; 7 | import lombok.Getter; 8 | import lombok.NoArgsConstructor; 9 | import lombok.RequiredArgsConstructor; 10 | import lombok.extern.slf4j.Slf4j; 11 | import org.apache.commons.lang3.StringUtils; 12 | 13 | import javax.persistence.*; 14 | 15 | @Slf4j 16 | @Getter 17 | @Entity 18 | @NoArgsConstructor 19 | @Table(name = "partners") 20 | public class Partner extends AbstractEntity { 21 | private static final String PREFIX_PARTNER = "ptn_"; 22 | 23 | @Id 24 | @GeneratedValue(strategy = GenerationType.IDENTITY) 25 | private Long id; 26 | private String partnerToken; 27 | private String partnerName; 28 | private String businessNo; 29 | private String email; 30 | 31 | @Enumerated(EnumType.STRING) 32 | private Status status; 33 | 34 | @Getter 35 | @RequiredArgsConstructor 36 | public enum Status { 37 | ENABLE("활성화"), DISABLE("비활성화"); 38 | private final String description; 39 | } 40 | 41 | @Builder 42 | public Partner(String partnerName, String businessNo, String email) { 43 | if (StringUtils.isEmpty(partnerName)) throw new InvalidParamException("empty partnerName"); 44 | if (StringUtils.isEmpty(businessNo)) throw new InvalidParamException("empty businessNo"); 45 | if (StringUtils.isEmpty(email)) throw new InvalidParamException("empty email"); 46 | 47 | this.partnerToken = TokenGenerator.randomCharacterWithPrefix(PREFIX_PARTNER); 48 | this.partnerName = partnerName; 49 | this.businessNo = businessNo; 50 | this.email = email; 51 | this.status = Status.ENABLE; 52 | } 53 | 54 | public void enable() { 55 | this.status = Status.ENABLE; 56 | } 57 | 58 | public void disable() { 59 | this.status = Status.DISABLE; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/domain/partner/PartnerCommand.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.domain.partner; 2 | 3 | import lombok.Builder; 4 | import lombok.Getter; 5 | import lombok.ToString; 6 | 7 | @Getter 8 | @Builder 9 | @ToString 10 | public class PartnerCommand { 11 | private final String partnerName; 12 | private final String businessNo; 13 | private final String email; 14 | 15 | public Partner toEntity() { 16 | return Partner.builder() 17 | .partnerName(partnerName) 18 | .businessNo(businessNo) 19 | .email(email) 20 | .build(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/domain/partner/PartnerInfo.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.domain.partner; 2 | 3 | import lombok.Getter; 4 | 5 | @Getter 6 | public class PartnerInfo { 7 | private final Long id; 8 | private final String partnerToken; 9 | private final String partnerName; 10 | private final String businessNo; 11 | private final String email; 12 | private final Partner.Status status; 13 | 14 | public PartnerInfo(Partner partner) { 15 | this.id = partner.getId(); 16 | this.partnerToken = partner.getPartnerToken(); 17 | this.partnerName = partner.getPartnerName(); 18 | this.businessNo = partner.getBusinessNo(); 19 | this.email = partner.getEmail(); 20 | this.status = partner.getStatus(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/domain/partner/PartnerReader.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.domain.partner; 2 | 3 | public interface PartnerReader { 4 | Partner getPartner(Long partnerId); 5 | Partner getPartner(String partnerToken); 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/domain/partner/PartnerService.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.domain.partner; 2 | 3 | public interface PartnerService { 4 | PartnerInfo registerPartner(PartnerCommand command); 5 | PartnerInfo getPartnerInfo(String partnerToken); 6 | PartnerInfo enablePartner(String partnerToken); 7 | PartnerInfo disablePartner(String partnerToken); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/domain/partner/PartnerServiceImpl.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.domain.partner; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.stereotype.Service; 6 | import org.springframework.transaction.annotation.Transactional; 7 | 8 | @Slf4j 9 | @Service 10 | @RequiredArgsConstructor 11 | public class PartnerServiceImpl implements PartnerService { 12 | private final PartnerStore partnerStore; 13 | private final PartnerReader partnerReader; 14 | 15 | @Override 16 | @Transactional 17 | public PartnerInfo registerPartner(PartnerCommand command) { 18 | var initPartner = command.toEntity(); 19 | Partner partner = partnerStore.store(initPartner); 20 | return new PartnerInfo(partner); 21 | } 22 | 23 | @Override 24 | @Transactional(readOnly = true) 25 | public PartnerInfo getPartnerInfo(String partnerToken) { 26 | Partner partner = partnerReader.getPartner(partnerToken); 27 | return new PartnerInfo(partner); 28 | } 29 | 30 | @Override 31 | @Transactional 32 | public PartnerInfo enablePartner(String partnerToken) { 33 | Partner partner = partnerReader.getPartner(partnerToken); 34 | partner.enable(); 35 | return new PartnerInfo(partner); 36 | } 37 | 38 | @Override 39 | @Transactional 40 | public PartnerInfo disablePartner(String partnerToken) { 41 | Partner partner = partnerReader.getPartner(partnerToken); 42 | partner.disable(); 43 | return new PartnerInfo(partner); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/domain/partner/PartnerStore.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.domain.partner; 2 | 3 | public interface PartnerStore { 4 | Partner store(Partner initPartner); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/infrastructure/NotificationExecutor.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.infrastructure; 2 | 3 | import dev.practice.order.domain.notification.NotificationService; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.stereotype.Component; 6 | 7 | @Slf4j 8 | @Component 9 | public class NotificationExecutor implements NotificationService { 10 | 11 | @Override 12 | public void sendEmail(String email, String title, String description) { 13 | log.info("sendEmail"); 14 | } 15 | 16 | @Override 17 | public void sendKakao(String phoneNo, String description) { 18 | log.info("sendKakao"); 19 | } 20 | 21 | @Override 22 | public void sendSms(String phoneNo, String description) { 23 | log.info("sendSms"); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/infrastructure/item/ItemOptionSeriesFactoryImpl.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.infrastructure.item; 2 | 3 | import dev.practice.order.domain.item.Item; 4 | import dev.practice.order.domain.item.ItemCommand; 5 | import dev.practice.order.domain.item.ItemOptionSeriesFactory; 6 | import dev.practice.order.domain.item.option.ItemOptionStore; 7 | import dev.practice.order.domain.item.optiongroup.ItemOptionGroup; 8 | import dev.practice.order.domain.item.optiongroup.ItemOptionGroupStore; 9 | import lombok.RequiredArgsConstructor; 10 | import lombok.extern.slf4j.Slf4j; 11 | import org.springframework.stereotype.Component; 12 | import org.springframework.util.CollectionUtils; 13 | 14 | import java.util.Collections; 15 | import java.util.List; 16 | import java.util.stream.Collectors; 17 | 18 | @Slf4j 19 | @Component 20 | @RequiredArgsConstructor 21 | public class ItemOptionSeriesFactoryImpl implements ItemOptionSeriesFactory { 22 | private final ItemOptionGroupStore itemOptionGroupStore; 23 | private final ItemOptionStore itemOptionStore; 24 | 25 | @Override 26 | public List store(ItemCommand.RegisterItemRequest command, Item item) { 27 | var itemOptionGroupRequestList = command.getItemOptionGroupRequestList(); 28 | if (CollectionUtils.isEmpty(itemOptionGroupRequestList)) return Collections.emptyList(); 29 | 30 | return itemOptionGroupRequestList.stream() 31 | .map(requestItemOptionGroup -> { 32 | // itemOptionGroup store 33 | var initItemOptionGroup = requestItemOptionGroup.toEntity(item); 34 | var itemOptionGroup = itemOptionGroupStore.store(initItemOptionGroup); 35 | 36 | // itemOption store 37 | requestItemOptionGroup.getItemOptionRequestList().forEach(requestItemOption -> { 38 | var initItemOption = requestItemOption.toEntity(itemOptionGroup); 39 | itemOptionStore.store(initItemOption); 40 | }); 41 | 42 | return itemOptionGroup; 43 | }).collect(Collectors.toList()); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/infrastructure/item/ItemReaderImpl.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.infrastructure.item; 2 | 3 | import dev.practice.order.common.exception.EntityNotFoundException; 4 | import dev.practice.order.domain.item.Item; 5 | import dev.practice.order.domain.item.ItemInfo; 6 | import dev.practice.order.domain.item.ItemReader; 7 | import lombok.RequiredArgsConstructor; 8 | import lombok.extern.slf4j.Slf4j; 9 | import org.springframework.stereotype.Component; 10 | 11 | import java.util.List; 12 | import java.util.stream.Collectors; 13 | 14 | @Slf4j 15 | @Component 16 | @RequiredArgsConstructor 17 | public class ItemReaderImpl implements ItemReader { 18 | private final ItemRepository itemRepository; 19 | 20 | @Override 21 | public Item getItemBy(String itemToken) { 22 | return itemRepository.findByItemToken(itemToken) 23 | .orElseThrow(EntityNotFoundException::new); 24 | } 25 | 26 | @Override 27 | public List getItemOptionSeries(Item item) { 28 | var itemOptionGroupList = item.getItemOptionGroupList(); 29 | return itemOptionGroupList.stream() 30 | .map(itemOptionGroup -> { 31 | var itemOptionList = itemOptionGroup.getItemOptionList(); 32 | var itemOptionInfoList = itemOptionList.stream() 33 | .map(ItemInfo.ItemOptionInfo::new) 34 | .collect(Collectors.toList()); 35 | 36 | return new ItemInfo.ItemOptionGroupInfo(itemOptionGroup, itemOptionInfoList); 37 | }).collect(Collectors.toList()); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/infrastructure/item/ItemRepository.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.infrastructure.item; 2 | 3 | 4 | import dev.practice.order.domain.item.Item; 5 | import org.springframework.data.jpa.repository.JpaRepository; 6 | 7 | import java.util.Optional; 8 | 9 | public interface ItemRepository extends JpaRepository { 10 | Optional findByItemToken(String itemToken); 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/infrastructure/item/ItemStoreImpl.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.infrastructure.item; 2 | 3 | import dev.practice.order.common.exception.InvalidParamException; 4 | import dev.practice.order.domain.item.Item; 5 | import dev.practice.order.domain.item.ItemStore; 6 | import lombok.RequiredArgsConstructor; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.apache.commons.lang3.StringUtils; 9 | import org.springframework.stereotype.Component; 10 | 11 | @Slf4j 12 | @Component 13 | @RequiredArgsConstructor 14 | public class ItemStoreImpl implements ItemStore { 15 | private final ItemRepository itemRepository; 16 | 17 | @Override 18 | public Item store(Item item) { 19 | validCheck(item); 20 | return itemRepository.save(item); 21 | } 22 | 23 | private void validCheck(Item item) { 24 | if (StringUtils.isEmpty(item.getItemToken())) throw new InvalidParamException("Item.itemToken"); 25 | if (StringUtils.isEmpty(item.getItemName())) throw new InvalidParamException("Item.itemName"); 26 | if (item.getPartnerId() == null) throw new InvalidParamException("Item.partnerId"); 27 | if (item.getItemPrice() == null) throw new InvalidParamException("Item.itemPrice"); 28 | if (item.getStatus() == null) throw new InvalidParamException("Item.status"); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/infrastructure/item/option/ItemOptionRepository.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.infrastructure.item.option; 2 | 3 | 4 | import dev.practice.order.domain.item.option.ItemOption; 5 | import org.springframework.data.jpa.repository.JpaRepository; 6 | 7 | public interface ItemOptionRepository extends JpaRepository { 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/infrastructure/item/option/ItemOptionStoreImpl.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.infrastructure.item.option; 2 | 3 | import dev.practice.order.domain.item.option.ItemOption; 4 | import dev.practice.order.domain.item.option.ItemOptionStore; 5 | import lombok.RequiredArgsConstructor; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.stereotype.Component; 8 | 9 | @Slf4j 10 | @Component 11 | @RequiredArgsConstructor 12 | public class ItemOptionStoreImpl implements ItemOptionStore { 13 | 14 | private final ItemOptionRepository itemOptionRepository; 15 | 16 | @Override 17 | public void store(ItemOption itemOption) { 18 | itemOptionRepository.save(itemOption); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/infrastructure/item/optiongroup/ItemOptionGroupRepository.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.infrastructure.item.optiongroup; 2 | 3 | import dev.practice.order.domain.item.optiongroup.ItemOptionGroup; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | public interface ItemOptionGroupRepository extends JpaRepository { 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/infrastructure/item/optiongroup/ItemOptionGroupStoreImpl.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.infrastructure.item.optiongroup; 2 | 3 | import dev.practice.order.domain.item.optiongroup.ItemOptionGroup; 4 | import dev.practice.order.domain.item.optiongroup.ItemOptionGroupStore; 5 | import lombok.RequiredArgsConstructor; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.stereotype.Component; 8 | 9 | @Slf4j 10 | @Component 11 | @RequiredArgsConstructor 12 | public class ItemOptionGroupStoreImpl implements ItemOptionGroupStore { 13 | 14 | private final ItemOptionGroupRepository itemOptionGroupRepository; 15 | 16 | @Override 17 | public ItemOptionGroup store(ItemOptionGroup itemOptionGroup) { 18 | return itemOptionGroupRepository.save(itemOptionGroup); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/infrastructure/order/OrderItemOptionGroupRepository.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.infrastructure.order; 2 | 3 | import dev.practice.order.domain.order.item.OrderItemOptionGroup; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | public interface OrderItemOptionGroupRepository extends JpaRepository { 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/infrastructure/order/OrderItemOptionRepository.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.infrastructure.order; 2 | 3 | import dev.practice.order.domain.order.item.OrderItemOption; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | public interface OrderItemOptionRepository extends JpaRepository { 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/infrastructure/order/OrderItemRepository.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.infrastructure.order; 2 | 3 | import dev.practice.order.domain.order.item.OrderItem; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | public interface OrderItemRepository extends JpaRepository { 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/infrastructure/order/OrderItemSeriesFactoryImpl.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.infrastructure.order; 2 | 3 | import dev.practice.order.domain.item.ItemReader; 4 | import dev.practice.order.domain.order.Order; 5 | import dev.practice.order.domain.order.OrderCommand; 6 | import dev.practice.order.domain.order.OrderItemSeriesFactory; 7 | import dev.practice.order.domain.order.OrderStore; 8 | import dev.practice.order.domain.order.item.OrderItem; 9 | import lombok.RequiredArgsConstructor; 10 | import org.springframework.stereotype.Component; 11 | 12 | import java.util.List; 13 | import java.util.stream.Collectors; 14 | 15 | @Component 16 | @RequiredArgsConstructor 17 | public class OrderItemSeriesFactoryImpl implements OrderItemSeriesFactory { 18 | private final ItemReader itemReader; 19 | private final OrderStore orderStore; 20 | 21 | @Override 22 | public List store(Order order, OrderCommand.RegisterOrder requestOrder) { 23 | return requestOrder.getOrderItemList().stream() 24 | .map(orderItemRequest -> { 25 | var item = itemReader.getItemBy(orderItemRequest.getItemToken()); 26 | var initOrderItem = orderItemRequest.toEntity(order, item); 27 | var orderItem = orderStore.store(initOrderItem); 28 | 29 | orderItemRequest.getOrderItemOptionGroupList().forEach(orderItemOptionGroupRequest -> { 30 | var initOrderItemOptionGroup = orderItemOptionGroupRequest.toEntity(orderItem); 31 | var orderItemOptionGroup = orderStore.store(initOrderItemOptionGroup); 32 | 33 | orderItemOptionGroupRequest.getOrderItemOptionList().forEach(orderItemOptionRequest -> { 34 | var initOrderItemOption = orderItemOptionRequest.toEntity(orderItemOptionGroup); 35 | orderStore.store(initOrderItemOption); 36 | }); 37 | }); 38 | return orderItem; 39 | }).collect(Collectors.toList()); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/infrastructure/order/OrderReaderImpl.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.infrastructure.order; 2 | 3 | import dev.practice.order.common.exception.EntityNotFoundException; 4 | import dev.practice.order.domain.order.Order; 5 | import dev.practice.order.domain.order.OrderReader; 6 | import lombok.RequiredArgsConstructor; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.springframework.stereotype.Component; 9 | 10 | @Slf4j 11 | @Component 12 | @RequiredArgsConstructor 13 | public class OrderReaderImpl implements OrderReader { 14 | 15 | private final OrderRepository orderRepository; 16 | 17 | @Override 18 | public Order getOrder(String orderToken) { 19 | return orderRepository.findByOrderToken(orderToken) 20 | .orElseThrow(EntityNotFoundException::new); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/infrastructure/order/OrderRepository.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.infrastructure.order; 2 | 3 | import dev.practice.order.domain.order.Order; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | import java.util.Optional; 7 | 8 | public interface OrderRepository extends JpaRepository { 9 | Optional findByOrderToken(String orderToken); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/infrastructure/order/OrderStoreImpl.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.infrastructure.order; 2 | 3 | import dev.practice.order.domain.order.Order; 4 | import dev.practice.order.domain.order.OrderStore; 5 | import dev.practice.order.domain.order.item.OrderItem; 6 | import dev.practice.order.domain.order.item.OrderItemOption; 7 | import dev.practice.order.domain.order.item.OrderItemOptionGroup; 8 | import lombok.RequiredArgsConstructor; 9 | import lombok.extern.slf4j.Slf4j; 10 | import org.springframework.stereotype.Component; 11 | 12 | @Slf4j 13 | @Component 14 | @RequiredArgsConstructor 15 | public class OrderStoreImpl implements OrderStore { 16 | private final OrderRepository orderRepository; 17 | private final OrderItemRepository orderItemRepository; 18 | private final OrderItemOptionGroupRepository orderItemOptionGroupRepository; 19 | private final OrderItemOptionRepository orderItemOptionRepository; 20 | 21 | @Override 22 | public Order store(Order order) { 23 | return orderRepository.save(order); 24 | } 25 | 26 | @Override 27 | public OrderItem store(OrderItem orderItem) { 28 | return orderItemRepository.save(orderItem); 29 | } 30 | 31 | @Override 32 | public OrderItemOptionGroup store(OrderItemOptionGroup orderItemOptionGroup) { 33 | return orderItemOptionGroupRepository.save(orderItemOptionGroup); 34 | } 35 | 36 | @Override 37 | public OrderItemOption store(OrderItemOption orderItemOption) { 38 | return orderItemOptionRepository.save(orderItemOption); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/infrastructure/order/payment/KakaoPayApiCaller.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.infrastructure.order.payment; 2 | 3 | import dev.practice.order.domain.order.OrderCommand; 4 | import dev.practice.order.domain.order.payment.PayMethod; 5 | import lombok.RequiredArgsConstructor; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.stereotype.Component; 8 | 9 | @Slf4j 10 | @Component 11 | @RequiredArgsConstructor 12 | public class KakaoPayApiCaller implements PaymentApiCaller { 13 | 14 | @Override 15 | public boolean support(PayMethod payMethod) { 16 | return PayMethod.KAKAO_PAY == payMethod; 17 | } 18 | 19 | @Override 20 | public void pay(OrderCommand.PaymentRequest request) { 21 | // TODO - 구현 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/infrastructure/order/payment/NaverPayApiCaller.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.infrastructure.order.payment; 2 | 3 | import dev.practice.order.domain.order.OrderCommand; 4 | import dev.practice.order.domain.order.payment.PayMethod; 5 | import lombok.RequiredArgsConstructor; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.stereotype.Component; 8 | 9 | @Slf4j 10 | @Component 11 | @RequiredArgsConstructor 12 | public class NaverPayApiCaller implements PaymentApiCaller { 13 | 14 | @Override 15 | public boolean support(PayMethod payMethod) { 16 | return PayMethod.NAVER_PAY == payMethod; 17 | } 18 | 19 | @Override 20 | public void pay(OrderCommand.PaymentRequest request) { 21 | // TODO - 구현 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/infrastructure/order/payment/PaymentApiCaller.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.infrastructure.order.payment; 2 | 3 | import dev.practice.order.domain.order.OrderCommand; 4 | import dev.practice.order.domain.order.payment.PayMethod; 5 | 6 | public interface PaymentApiCaller { 7 | boolean support(PayMethod payMethod); 8 | void pay(OrderCommand.PaymentRequest request); 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/infrastructure/order/payment/PaymentProcessorImpl.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.infrastructure.order.payment; 2 | 3 | import dev.practice.order.common.exception.InvalidParamException; 4 | import dev.practice.order.domain.order.Order; 5 | import dev.practice.order.domain.order.OrderCommand; 6 | import dev.practice.order.domain.order.payment.PaymentProcessor; 7 | import dev.practice.order.domain.order.payment.validator.PaymentValidator; 8 | import lombok.RequiredArgsConstructor; 9 | import lombok.extern.slf4j.Slf4j; 10 | import org.springframework.stereotype.Component; 11 | 12 | import java.util.List; 13 | 14 | @Slf4j 15 | @Component 16 | @RequiredArgsConstructor 17 | public class PaymentProcessorImpl implements PaymentProcessor { 18 | private final List paymentValidatorList; 19 | private final List paymentApiCallerList; 20 | 21 | @Override 22 | public void pay(Order order, OrderCommand.PaymentRequest paymentRequest) { 23 | paymentValidatorList.forEach(paymentValidator -> paymentValidator.validate(order, paymentRequest)); 24 | PaymentApiCaller payApiCaller = routingApiCaller(paymentRequest); 25 | payApiCaller.pay(paymentRequest); 26 | } 27 | 28 | private PaymentApiCaller routingApiCaller(OrderCommand.PaymentRequest request) { 29 | return paymentApiCallerList.stream() 30 | .filter(paymentApiCaller -> paymentApiCaller.support(request.getPayMethod())) 31 | .findFirst() 32 | .orElseThrow(InvalidParamException::new); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/infrastructure/order/payment/PgCardApiCaller.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.infrastructure.order.payment; 2 | 3 | import dev.practice.order.domain.order.OrderCommand; 4 | import dev.practice.order.domain.order.payment.PayMethod; 5 | import lombok.RequiredArgsConstructor; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.stereotype.Component; 8 | 9 | @Slf4j 10 | @Component 11 | @RequiredArgsConstructor 12 | public class PgCardApiCaller implements PaymentApiCaller { 13 | 14 | @Override 15 | public boolean support(PayMethod payMethod) { 16 | return PayMethod.CARD == payMethod; 17 | } 18 | 19 | @Override 20 | public void pay(OrderCommand.PaymentRequest request) { 21 | // TODO - 구현 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/infrastructure/order/payment/TossPayApiCaller.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.infrastructure.order.payment; 2 | 3 | import dev.practice.order.domain.order.OrderCommand; 4 | import dev.practice.order.domain.order.payment.PayMethod; 5 | import lombok.RequiredArgsConstructor; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.stereotype.Component; 8 | 9 | @Slf4j 10 | @Component 11 | @RequiredArgsConstructor 12 | public class TossPayApiCaller implements PaymentApiCaller { 13 | 14 | @Override 15 | public boolean support(PayMethod payMethod) { 16 | return PayMethod.TOSS_PAY == payMethod; 17 | } 18 | 19 | @Override 20 | public void pay(OrderCommand.PaymentRequest request) { 21 | // TODO - 구현 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/infrastructure/partner/PartnerReadImpl.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.infrastructure.partner; 2 | 3 | import dev.practice.order.common.exception.EntityNotFoundException; 4 | import dev.practice.order.domain.partner.Partner; 5 | import dev.practice.order.domain.partner.PartnerReader; 6 | import lombok.RequiredArgsConstructor; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.springframework.stereotype.Component; 9 | 10 | @Slf4j 11 | @Component 12 | @RequiredArgsConstructor 13 | public class PartnerReadImpl implements PartnerReader { 14 | private final PartnerRepository partnerRepository; 15 | 16 | @Override 17 | public Partner getPartner(Long partnerId) { 18 | return partnerRepository.findById(partnerId) 19 | .orElseThrow(EntityNotFoundException::new); 20 | } 21 | 22 | @Override 23 | public Partner getPartner(String partnerToken) { 24 | return partnerRepository.findByPartnerToken(partnerToken) 25 | .orElseThrow(EntityNotFoundException::new); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/infrastructure/partner/PartnerRepository.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.infrastructure.partner; 2 | 3 | import dev.practice.order.domain.partner.Partner; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | import java.util.Optional; 7 | 8 | public interface PartnerRepository extends JpaRepository { 9 | Optional findByPartnerToken(String partnerToken); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/infrastructure/partner/PartnerStoreImpl.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.infrastructure.partner; 2 | 3 | import dev.practice.order.common.exception.InvalidParamException; 4 | import dev.practice.order.domain.partner.Partner; 5 | import dev.practice.order.domain.partner.PartnerStore; 6 | import lombok.RequiredArgsConstructor; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.apache.commons.lang3.StringUtils; 9 | import org.springframework.stereotype.Component; 10 | 11 | @Slf4j 12 | @Component 13 | @RequiredArgsConstructor 14 | public class PartnerStoreImpl implements PartnerStore { 15 | 16 | private final PartnerRepository partnerRepository; 17 | 18 | @Override 19 | public Partner store(Partner partner) { 20 | if (StringUtils.isEmpty(partner.getPartnerToken())) throw new InvalidParamException("partner.getPartnerToken()"); 21 | if (StringUtils.isEmpty(partner.getPartnerName())) throw new InvalidParamException("partner.getPartnerName()"); 22 | if (StringUtils.isEmpty(partner.getBusinessNo())) throw new InvalidParamException("partner.getBusinessNo()"); 23 | if (StringUtils.isEmpty(partner.getEmail())) throw new InvalidParamException("partner.getEmail()"); 24 | if (partner.getStatus() == null) throw new InvalidParamException("partner.getStatus()"); 25 | 26 | return partnerRepository.save(partner); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/interfaces/item/ItemApiController.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.interfaces.item; 2 | 3 | import dev.practice.order.application.item.ItemFacade; 4 | import dev.practice.order.common.response.CommonResponse; 5 | import lombok.RequiredArgsConstructor; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.web.bind.annotation.*; 8 | 9 | import javax.validation.Valid; 10 | 11 | @Slf4j 12 | @RestController 13 | @RequiredArgsConstructor 14 | @RequestMapping("/api/v1/items") 15 | public class ItemApiController { 16 | private final ItemFacade itemFacade; 17 | private final ItemDtoMapper itemDtoMapper; 18 | 19 | @PostMapping 20 | public CommonResponse registerItem(@RequestBody @Valid ItemDto.RegisterItemRequest request) { 21 | var partnerToken = request.getPartnerToken(); 22 | var itemCommand = itemDtoMapper.of(request); 23 | var itemToken = itemFacade.registerItem(itemCommand, partnerToken); 24 | var response = itemDtoMapper.of(itemToken); 25 | return CommonResponse.success(response); 26 | } 27 | 28 | @PostMapping("/change-on-sales") 29 | public CommonResponse changeOnSaleItem(@RequestBody @Valid ItemDto.ChangeStatusItemRequest request) { 30 | var itemToken = request.getItemToken(); 31 | itemFacade.changeOnSaleItem(itemToken); 32 | return CommonResponse.success("OK"); 33 | } 34 | 35 | @PostMapping("/change-end-of-sales") 36 | public CommonResponse changeEndOfSaleItem(@RequestBody @Valid ItemDto.ChangeStatusItemRequest request) { 37 | var itemToken = request.getItemToken(); 38 | itemFacade.changeEndOfSaleItem(itemToken); 39 | return CommonResponse.success("OK"); 40 | } 41 | 42 | @GetMapping("/{itemToken}") 43 | public CommonResponse retrieve(@PathVariable("itemToken") String itemToken) { 44 | var itemInfo = itemFacade.retrieveItemInfo(itemToken); 45 | var response = itemDtoMapper.of(itemInfo); 46 | return CommonResponse.success(response); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/interfaces/item/ItemDto.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.interfaces.item; 2 | 3 | import dev.practice.order.domain.item.Item; 4 | import lombok.Builder; 5 | import lombok.Getter; 6 | import lombok.Setter; 7 | import lombok.ToString; 8 | 9 | import java.util.List; 10 | 11 | public class ItemDto { 12 | 13 | @Getter 14 | @Setter 15 | @ToString 16 | public static class RegisterItemRequest { 17 | private String partnerToken; 18 | private String itemName; 19 | private Long itemPrice; 20 | private List itemOptionGroupList; 21 | } 22 | 23 | @Getter 24 | @Setter 25 | @ToString 26 | public static class RegisterItemOptionGroupRequest { 27 | private Integer ordering; 28 | private String itemOptionGroupName; 29 | private List itemOptionList; 30 | } 31 | 32 | @Getter 33 | @Setter 34 | @ToString 35 | public static class RegisterItemOptionRequest { 36 | private Integer ordering; 37 | private String itemOptionName; 38 | private Long itemOptionPrice; 39 | } 40 | 41 | @Getter 42 | @Builder 43 | @ToString 44 | public static class RegisterResponse { 45 | private final String itemToken; 46 | } 47 | 48 | @Getter 49 | @Setter 50 | @ToString 51 | public static class ChangeStatusItemRequest { 52 | private String itemToken; 53 | } 54 | 55 | @Getter 56 | @Builder 57 | @ToString 58 | public static class Main { 59 | private final String itemToken; 60 | private final Long partnerId; 61 | private final String itemName; 62 | private final Long itemPrice; 63 | private final Item.Status status; 64 | private final List itemOptionGroupList; 65 | } 66 | 67 | @Getter 68 | @Builder 69 | @ToString 70 | public static class ItemOptionGroupInfo { 71 | private final Integer ordering; 72 | private final String itemOptionGroupName; 73 | private final List itemOptionList; 74 | } 75 | 76 | @Getter 77 | @Builder 78 | @ToString 79 | public static class ItemOptionInfo { 80 | private final Integer ordering; 81 | private final String itemOptionName; 82 | private final Long itemOptionPrice; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/interfaces/item/ItemDtoMapper.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.interfaces.item; 2 | 3 | import dev.practice.order.domain.item.ItemCommand; 4 | import dev.practice.order.domain.item.ItemInfo; 5 | import org.mapstruct.*; 6 | 7 | @Mapper( 8 | componentModel = "spring", 9 | injectionStrategy = InjectionStrategy.CONSTRUCTOR, 10 | unmappedTargetPolicy = ReportingPolicy.ERROR 11 | ) 12 | public interface ItemDtoMapper { 13 | 14 | // register 15 | @Mappings({@Mapping(source = "request.itemOptionGroupList", target = "itemOptionGroupRequestList")}) 16 | ItemCommand.RegisterItemRequest of(ItemDto.RegisterItemRequest request); 17 | 18 | @Mappings({@Mapping(source = "itemOptionList", target = "itemOptionRequestList")}) 19 | ItemCommand.RegisterItemOptionGroupRequest of(ItemDto.RegisterItemOptionGroupRequest request); 20 | 21 | ItemCommand.RegisterItemOptionRequest of(ItemDto.RegisterItemOptionRequest request); 22 | 23 | ItemDto.RegisterResponse of(String itemToken); 24 | 25 | // retrieve 26 | ItemDto.Main of(ItemInfo.Main main); 27 | 28 | ItemDto.ItemOptionGroupInfo of(ItemInfo.ItemOptionGroupInfo itemOptionGroup); 29 | 30 | ItemDto.ItemOptionInfo of(ItemInfo.ItemOptionInfo itemOption); 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/interfaces/order/OrderApiController.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.interfaces.order; 2 | 3 | import dev.practice.order.application.order.OrderFacade; 4 | import dev.practice.order.common.response.CommonResponse; 5 | import lombok.RequiredArgsConstructor; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.web.bind.annotation.*; 8 | 9 | import javax.validation.Valid; 10 | 11 | @Slf4j 12 | @RestController 13 | @RequiredArgsConstructor 14 | @RequestMapping("/api/v1/orders") 15 | public class OrderApiController { 16 | private final OrderFacade orderFacade; 17 | private final OrderDtoMapper orderDtoMapper; 18 | 19 | @PostMapping("/init") 20 | public CommonResponse registerOrder(@RequestBody @Valid OrderDto.RegisterOrderRequest request) { 21 | var orderCommand = orderDtoMapper.of(request); 22 | var orderToken = orderFacade.registerOrder(orderCommand); 23 | var response = orderDtoMapper.of(orderToken); 24 | return CommonResponse.success(response); 25 | } 26 | 27 | @GetMapping("/{orderToken}") 28 | public CommonResponse retrieveOrder(@PathVariable String orderToken) { 29 | var orderResult = orderFacade.retrieveOrder(orderToken); 30 | var response = orderDtoMapper.of(orderResult); 31 | return CommonResponse.success(response); 32 | } 33 | 34 | @PostMapping("/payment-order") 35 | public CommonResponse paymentOrder(@RequestBody @Valid OrderDto.PaymentRequest paymentRequest) { 36 | var paymentCommand = orderDtoMapper.of(paymentRequest); 37 | orderFacade.paymentOrder(paymentCommand); 38 | return CommonResponse.success("OK"); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/interfaces/order/OrderDto.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.interfaces.order; 2 | 3 | import dev.practice.order.domain.order.payment.PayMethod; 4 | import lombok.Builder; 5 | import lombok.Getter; 6 | import lombok.Setter; 7 | import lombok.ToString; 8 | 9 | import javax.validation.constraints.NotBlank; 10 | import javax.validation.constraints.NotNull; 11 | import java.util.List; 12 | 13 | public class OrderDto { 14 | 15 | @Getter 16 | @Setter 17 | @ToString 18 | public static class RegisterOrderRequest { 19 | @NotNull(message = "userId 는 필수값입니다") 20 | private Long userId; 21 | 22 | @NotBlank(message = "payMethod 는 필수값입니다") 23 | private String payMethod; 24 | 25 | @NotBlank(message = "receiverName 는 필수값입니다") 26 | private String receiverName; 27 | 28 | @NotBlank(message = "receiverPhone 는 필수값입니다") 29 | private String receiverPhone; 30 | 31 | @NotBlank(message = "receiverZipcode 는 필수값입니다") 32 | private String receiverZipcode; 33 | 34 | @NotBlank(message = "receiverAddress1 는 필수값입니다") 35 | private String receiverAddress1; 36 | 37 | @NotBlank(message = "receiverAddress2 는 필수값입니다") 38 | private String receiverAddress2; 39 | 40 | @NotBlank(message = "etcMessage 는 필수값입니다") 41 | private String etcMessage; 42 | 43 | private List orderItemList; 44 | } 45 | 46 | @Getter 47 | @Setter 48 | @ToString 49 | public static class RegisterOrderItem { 50 | @NotNull(message = "orderCount 는 필수값입니다") 51 | private Integer orderCount; 52 | 53 | @NotBlank(message = "itemToken 는 필수값입니다") 54 | private String itemToken; 55 | 56 | @NotBlank(message = "itemName 는 필수값입니다") 57 | private String itemName; 58 | 59 | @NotNull(message = "itemPrice 는 필수값입니다") 60 | private Long itemPrice; 61 | 62 | private List orderItemOptionGroupList; 63 | } 64 | 65 | @Getter 66 | @Setter 67 | @ToString 68 | public static class RegisterOrderItemOptionGroupRequest { 69 | @NotNull(message = "ordering 는 필수값입니다") 70 | private Integer ordering; 71 | 72 | @NotBlank(message = "itemOptionGroupName 는 필수값입니다") 73 | private String itemOptionGroupName; 74 | 75 | private List orderItemOptionList; 76 | } 77 | 78 | @Getter 79 | @Setter 80 | @ToString 81 | public static class RegisterOrderItemOptionRequest { 82 | @NotNull(message = "ordering 는 필수값입니다") 83 | private Integer ordering; 84 | 85 | @NotBlank(message = "itemOptionName 는 필수값입니다") 86 | private String itemOptionName; 87 | 88 | @NotNull(message = "itemOptionPrice 는 필수값입니다") 89 | private Long itemOptionPrice; 90 | } 91 | 92 | @Getter 93 | @Builder 94 | @ToString 95 | public static class RegisterResponse { 96 | private final String orderToken; 97 | } 98 | 99 | @Getter 100 | @Setter 101 | @ToString 102 | public static class PaymentRequest { 103 | @NotBlank(message = "orderToken 는 필수값입니다") 104 | private String orderToken; 105 | 106 | @NotNull(message = "payMethod 는 필수값입니다") 107 | private PayMethod payMethod; 108 | 109 | @NotNull(message = "amount 는 필수값입니다") 110 | private Long amount; 111 | 112 | @NotBlank(message = "orderDescription 는 필수값입니다") 113 | private String orderDescription; 114 | } 115 | 116 | // 조회 117 | @Getter 118 | @Builder 119 | @ToString 120 | public static class Main { 121 | private final String orderToken; 122 | private final Long userId; 123 | private final String payMethod; 124 | private final Long totalAmount; 125 | private final DeliveryInfo deliveryInfo; 126 | private final String orderedAt; 127 | private final String status; 128 | private final String statusDescription; 129 | private final List orderItemList; 130 | } 131 | 132 | @Getter 133 | @Builder 134 | @ToString 135 | public static class DeliveryInfo { 136 | private final String receiverName; 137 | private final String receiverPhone; 138 | private final String receiverZipcode; 139 | private final String receiverAddress1; 140 | private final String receiverAddress2; 141 | private final String etcMessage; 142 | } 143 | 144 | @Getter 145 | @Builder 146 | @ToString 147 | public static class OrderItem { 148 | private final Integer orderCount; 149 | private final Long partnerId; 150 | private final Long itemId; 151 | private final String itemName; 152 | private final Long totalAmount; 153 | private final Long itemPrice; 154 | private final String deliveryStatus; 155 | private final String deliveryStatusDescription; 156 | private final List orderItemOptionGroupList; 157 | } 158 | 159 | @Getter 160 | @Builder 161 | @ToString 162 | public static class OrderItemOptionGroup { 163 | private final Integer ordering; 164 | private final String itemOptionGroupName; 165 | private final List orderItemOptionList; 166 | } 167 | 168 | @Getter 169 | @Builder 170 | @ToString 171 | public static class OrderItemOption { 172 | private final Integer ordering; 173 | private final String itemOptionName; 174 | private final Long itemOptionPrice; 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/interfaces/order/OrderDtoMapper.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.interfaces.order; 2 | 3 | import dev.practice.order.domain.order.OrderCommand; 4 | import dev.practice.order.domain.order.OrderInfo; 5 | import org.mapstruct.*; 6 | 7 | @Mapper( 8 | componentModel = "spring", 9 | injectionStrategy = InjectionStrategy.CONSTRUCTOR, 10 | unmappedTargetPolicy = ReportingPolicy.ERROR 11 | ) 12 | public interface OrderDtoMapper { 13 | 14 | @Mappings({ 15 | @Mapping(source = "orderedAt", target = "orderedAt", dateFormat = "yyyy-MM-dd HH:mm:ss") 16 | }) 17 | OrderDto.Main of(OrderInfo.Main mainResult); 18 | 19 | OrderDto.DeliveryInfo of(OrderInfo.DeliveryInfo deliveryResult); 20 | 21 | OrderDto.OrderItem of(OrderInfo.OrderItem orderItemResult); 22 | 23 | OrderCommand.RegisterOrder of(OrderDto.RegisterOrderRequest request); 24 | 25 | OrderCommand.RegisterOrderItem of(OrderDto.RegisterOrderItem request); 26 | 27 | OrderCommand.RegisterOrderItemOptionGroup of(OrderDto.RegisterOrderItemOptionGroupRequest request); 28 | 29 | OrderCommand.RegisterOrderItemOption of(OrderDto.RegisterOrderItemOptionRequest request); 30 | 31 | OrderDto.RegisterResponse of(String orderToken); 32 | 33 | OrderCommand.PaymentRequest of(OrderDto.PaymentRequest request); 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/interfaces/partner/PartnerApiController.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.interfaces.partner; 2 | 3 | import dev.practice.order.application.partner.PartnerFacade; 4 | import dev.practice.order.common.response.CommonResponse; 5 | import lombok.RequiredArgsConstructor; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.web.bind.annotation.PostMapping; 8 | import org.springframework.web.bind.annotation.RequestBody; 9 | import org.springframework.web.bind.annotation.RequestMapping; 10 | import org.springframework.web.bind.annotation.RestController; 11 | 12 | import javax.validation.Valid; 13 | 14 | @Slf4j 15 | @RestController 16 | @RequiredArgsConstructor 17 | @RequestMapping("/api/v1/partners") 18 | public class PartnerApiController { 19 | private final PartnerFacade partnerFacade; 20 | 21 | @PostMapping 22 | public CommonResponse registerPartner(@RequestBody @Valid PartnerDto.RegisterRequest request) { 23 | var command = request.toCommand(); 24 | var partnerInfo = partnerFacade.registerPartner(command); 25 | var response = new PartnerDto.RegisterResponse(partnerInfo); 26 | return CommonResponse.success(response); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/dev/practice/order/interfaces/partner/PartnerDto.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order.interfaces.partner; 2 | 3 | import dev.practice.order.domain.partner.Partner; 4 | import dev.practice.order.domain.partner.PartnerCommand; 5 | import dev.practice.order.domain.partner.PartnerInfo; 6 | import lombok.Getter; 7 | import lombok.Setter; 8 | import lombok.ToString; 9 | 10 | import javax.validation.constraints.Email; 11 | import javax.validation.constraints.NotEmpty; 12 | 13 | public class PartnerDto { 14 | 15 | @Getter 16 | @Setter 17 | @ToString 18 | public static class RegisterRequest { 19 | @NotEmpty(message = "partnerName 는 필수값입니다") 20 | private String partnerName; 21 | 22 | @NotEmpty(message = "businessNo 는 필수값입니다") 23 | private String businessNo; 24 | 25 | @Email(message = "email 형식에 맞아야 합니다") 26 | @NotEmpty(message = "email 는 필수값입니다") 27 | private String email; 28 | 29 | public PartnerCommand toCommand() { 30 | return PartnerCommand.builder() 31 | .partnerName(partnerName) 32 | .businessNo(businessNo) 33 | .email(email) 34 | .build(); 35 | } 36 | } 37 | 38 | @Getter 39 | @ToString 40 | public static class RegisterResponse { 41 | private final String partnerToken; 42 | private final String partnerName; 43 | private final String businessNo; 44 | private final String email; 45 | private final Partner.Status status; 46 | 47 | public RegisterResponse(PartnerInfo partnerInfo) { 48 | this.partnerToken = partnerInfo.getPartnerToken(); 49 | this.partnerName = partnerInfo.getPartnerName(); 50 | this.businessNo = partnerInfo.getBusinessNo(); 51 | this.email = partnerInfo.getEmail(); 52 | this.status = partnerInfo.getStatus(); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | shutdown: graceful 3 | 4 | spring: 5 | lifecycle: 6 | timeout-per-shutdown-phase: 20s 7 | jpa: 8 | show-sql: true 9 | # database-platform: mysql 10 | hibernate: 11 | ddl-auto: none 12 | datasource: 13 | hikari: 14 | driver-class-name: com.mysql.cj.jdbc.Driver 15 | jdbc-url: jdbc:mysql://localhost:3306/order?serverTimezone=UTC&characterEncoding=UTF-8 16 | username: order-svc 17 | password: order-pass 18 | flyway: 19 | user: order-svc 20 | password: order-pass 21 | schemas: order 22 | url: jdbc:mysql://localhost:3306/order?serverTimezone=UTC&characterEncoding=UTF-8 23 | enabled: true 24 | 25 | # h2: 26 | # console: 27 | # enabled: true 28 | # path: /h2-console 29 | 30 | logging: 31 | config: classpath:logback-local.xml -------------------------------------------------------------------------------- /src/main/resources/db/migration/V1__init_ddl.sql: -------------------------------------------------------------------------------- 1 | -- partner 2 | create table partners 3 | ( 4 | id bigint auto_increment primary key comment 'ID', 5 | partner_token varchar(255) not null comment 'partner_token', 6 | partner_name varchar(255) not null comment '파트너명', 7 | business_no varchar(255) not null comment '사업자등록번호', 8 | email varchar(255) not null comment 'email', 9 | status varchar(30) not null default 'ENABLE' comment '상태', 10 | created_at datetime(6) not null comment '생성 일시', 11 | updated_at datetime(6) null comment '수정 일시' 12 | ) comment 'partners' charset = utf8mb4; 13 | 14 | create 15 | index partner_idx01 on partners (partner_token); 16 | 17 | create 18 | index partner_idx02 on partners (created_at); 19 | 20 | create 21 | index partner_idx03 on partners (updated_at); 22 | 23 | -- item 24 | create table items 25 | ( 26 | id bigint auto_increment primary key comment 'ID', 27 | item_token varchar(255) not null comment 'item_token', 28 | partner_id bigint not null comment '파트너 ID', 29 | item_name varchar(255) not null comment '상품명', 30 | item_price int(11) not null comment '상품 가격', 31 | status varchar(30) not null default 'PREPARE' comment '상태', 32 | deleted_at datetime(6) null comment '삭제 일시', 33 | created_at datetime(6) not null comment '생성 일시', 34 | updated_at datetime(6) null comment '수정 일시' 35 | ) comment 'items' charset = utf8mb4; 36 | 37 | create 38 | index item_idx01 on items (item_token); 39 | 40 | create 41 | index item_idx02 on items (partner_id); 42 | 43 | create 44 | index item_idx03 on items (created_at); 45 | 46 | create 47 | index item_idx04 on items (updated_at); 48 | 49 | create 50 | index item_idx05 on items (deleted_at); 51 | 52 | 53 | -- item_option_group 54 | create table item_option_groups 55 | ( 56 | id bigint auto_increment primary key comment 'ID', 57 | item_id bigint not null comment '상품 ID', 58 | ordering tinyint(3) not null comment '정렬순서', 59 | item_option_group_name varchar(30) not null comment '옵션그룹명 (색상, 사이즈 등)', 60 | created_at datetime(6) not null comment '생성 일시', 61 | updated_at datetime(6) null comment '수정 일시' 62 | ) comment 'item_option_groups' charset = utf8mb4; 63 | 64 | create 65 | index item_option_group_idx01 on item_option_groups (item_id); 66 | 67 | create 68 | index item_option_group_idx02 on item_option_groups (created_at); 69 | 70 | create 71 | index item_option_group_idx03 on item_option_groups (updated_at); 72 | 73 | 74 | -- item_option 75 | create table item_options 76 | ( 77 | id bigint auto_increment primary key comment 'ID', 78 | item_option_group_id bigint not null comment '상품 옵션 그룹 ID', 79 | ordering tinyint(3) not null comment '정렬순서', 80 | item_option_name varchar(30) not null comment '옵션명 (빨강, 파랑, XL, L)', 81 | item_option_price int(11) not null comment '상품 옵션 가격', 82 | created_at datetime(6) not null comment '생성 일시', 83 | updated_at datetime(6) null comment '수정 일시' 84 | ) comment 'item_options' charset = utf8mb4; 85 | 86 | create 87 | index item_option_idx01 on item_options (item_option_group_id); 88 | 89 | create 90 | index item_option_idx02 on item_options (created_at); 91 | 92 | create 93 | index item_option_idx03 on item_options (updated_at); 94 | 95 | 96 | -- order 97 | create table orders 98 | ( 99 | id bigint auto_increment primary key comment 'ID', 100 | order_token varchar(255) not null comment 'order_token', 101 | user_id bigint not null comment '유저 ID', 102 | pay_method varchar(30) not null comment '결제수단', 103 | receiver_name varchar(30) not null comment '수령자명', 104 | receiver_phone varchar(30) not null comment '수령자 휴대폰번호', 105 | receiver_zipcode varchar(10) not null comment '수령자 우편번호', 106 | receiver_address1 varchar(255) not null comment '수령자 주소1', 107 | receiver_address2 varchar(255) not null comment '수령자 주소2', 108 | etc_message varchar(255) not null comment '남기는 말', 109 | status varchar(30) not null default 'INIT' comment '상태', 110 | ordered_at datetime(6) not null comment '주문 일시', 111 | created_at datetime(6) not null comment '생성 일시', 112 | updated_at datetime(6) null comment '수정 일시' 113 | ) comment 'orders' charset = utf8mb4; 114 | 115 | create 116 | index order_idx01 on orders (order_token); 117 | 118 | create 119 | index order_idx02 on orders (user_id); 120 | 121 | create 122 | index order_idx03 on orders (ordered_at); 123 | 124 | create 125 | index order_idx04 on orders (created_at); 126 | 127 | create 128 | index order_idx05 on orders (updated_at); 129 | 130 | 131 | -- order_items 132 | create table order_items 133 | ( 134 | id bigint auto_increment primary key comment 'ID', 135 | order_id bigint not null comment 'order_id', 136 | order_count tinyint not null comment '주문갯수', 137 | partner_id bigint not null comment '파트너 ID', 138 | item_id bigint not null comment '상품 ID', 139 | item_name varchar(255) not null comment '상품명', 140 | item_token varchar(30) not null comment '상품 token', 141 | item_price int(11) not null comment '상품 가격', 142 | delivery_status varchar(30) not null default 'BEFORE_DELIVERY' comment '상태', 143 | created_at datetime(6) not null comment '생성 일시', 144 | updated_at datetime(6) null comment '수정 일시' 145 | ) comment 'order_items' charset = utf8mb4; 146 | 147 | create 148 | index order_item_idx01 on order_items (order_id); 149 | 150 | create 151 | index order_item_idx02 on order_items (partner_id); 152 | 153 | create 154 | index order_item_idx03 on order_items (item_id); 155 | 156 | create 157 | index order_item_idx04 on order_items (item_token); 158 | 159 | create 160 | index order_item_idx05 on order_items (created_at); 161 | 162 | create 163 | index order_item_idx06 on order_items (updated_at); 164 | 165 | 166 | -- order_item_option_groups 167 | create table order_item_option_groups 168 | ( 169 | id bigint auto_increment primary key comment 'ID', 170 | order_item_id bigint not null comment 'order_item_id', 171 | ordering tinyint(3) not null comment '정렬순서', 172 | item_option_group_name varchar(255) not null comment '상품 옵션 그룹명', 173 | created_at datetime(6) not null comment '생성 일시', 174 | updated_at datetime(6) null comment '수정 일시' 175 | ) comment 'order_item_option_groups' charset = utf8mb4; 176 | 177 | create 178 | index order_item_option_groups_idx01 on order_item_option_groups (order_item_id); 179 | 180 | create 181 | index order_item_option_groups_idx02 on order_item_option_groups (created_at); 182 | 183 | create 184 | index order_item_option_groups_idx03 on order_item_option_groups (updated_at); 185 | 186 | 187 | -- order_item_options 188 | create table order_item_options 189 | ( 190 | id bigint auto_increment primary key comment 'ID', 191 | order_item_option_group_id bigint not null comment 'order_item_option_group_id', 192 | ordering tinyint(3) not null comment '정렬순서', 193 | item_option_name varchar(255) not null comment '상품 옵션명', 194 | item_option_price int(11) not null comment '상품 옵션 가격', 195 | created_at datetime(6) not null comment '생성 일시', 196 | updated_at datetime(6) null comment '수정 일시' 197 | ) comment 'order_item_options' charset = utf8mb4; 198 | 199 | create 200 | index order_item_options_idx01 on order_item_options (order_item_option_group_id); 201 | 202 | create 203 | index order_item_options_idx02 on order_item_options (created_at); 204 | 205 | create 206 | index order_item_options_idx03 on order_item_options (updated_at); -------------------------------------------------------------------------------- /src/main/resources/logback-local.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | %d{yyyy:MM:dd HH:mm:ss.SSS} %-5level --- [%thread] %logger{35} : %msg %n 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/test/java/dev/practice/order/OrderApplicationTests.java: -------------------------------------------------------------------------------- 1 | package dev.practice.order; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class OrderApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | --------------------------------------------------------------------------------