diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..8af972c --- /dev/null +++ b/.gitattributes @@ -0,0 +1,3 @@ +/gradlew text eol=lf +*.bat text eol=crlf +*.jar binary diff --git a/.github/workflows/dev_deploy.yml b/.github/workflows/dev_deploy.yml new file mode 100644 index 0000000..313a476 --- /dev/null +++ b/.github/workflows/dev_deploy.yml @@ -0,0 +1,11 @@ +name: sammool's Dev CI/CD + +on: + pull_request: + types: [closed] + workflow_dispatch: #(2).수동 실행 가능하도록 + +jobs: + build: + runs-on: ubuntu-latest + if: github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'develop' \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c2065bc --- /dev/null +++ b/.gitignore @@ -0,0 +1,37 @@ +HELP.md +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..16022e1 --- /dev/null +++ b/build.gradle @@ -0,0 +1,68 @@ +plugins { + id 'java' + id 'org.springframework.boot' version '3.4.4' + id 'io.spring.dependency-management' version '1.1.7' +} + +group = 'umc' +version = '0.0.1-SNAPSHOT' + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(19) + } +} + +configurations { + compileOnly { + extendsFrom annotationProcessor + } +} + +repositories { + mavenCentral() +} + +dependencies { + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.hibernate.orm:hibernate-core:6.0.2.Final' // Hibernate 6.0.2 이상 + implementation 'mysql:mysql-connector-java:8.0.33' // MySQL 드라이버 추가 + + compileOnly 'org.projectlombok:lombok' + runtimeOnly 'com.mysql:mysql-connector-j' + annotationProcessor 'org.projectlombok:lombok' + testImplementation 'org.springframework.boot:spring-boot-starter-test' + testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + + // queryDSL + implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta' + annotationProcessor "com.querydsl:querydsl-apt:5.0.0:jakarta" + annotationProcessor "jakarta.annotation:jakarta.annotation-api" + annotationProcessor "jakarta.persistence:jakarta.persistence-api" +} + +tasks.named('test') { + useJUnitPlatform() +} + + +// Querydsl 설정부 +def generated = 'src/main/generated' + + +sourceSets { + main { + java { + srcDirs += generated + } + } +} + +configurations { + querydsl.extendsFrom compileClasspath +} + +tasks.withType(JavaCompile).configureEach { + options.annotationProcessorGeneratedSourcesDirectory = file(generated) +} \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..9bbc975 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..37f853b --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100644 index 0000000..faf9300 --- /dev/null +++ b/gradlew @@ -0,0 +1,251 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..9d21a21 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,94 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..ebf1ef8 --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'spring' diff --git a/src/main/java/umc/spring/Application.java b/src/main/java/umc/spring/Application.java new file mode 100644 index 0000000..d947b93 --- /dev/null +++ b/src/main/java/umc/spring/Application.java @@ -0,0 +1,34 @@ +package umc.spring; + +import org.springframework.boot.CommandLineRunner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; + +@SpringBootApplication +@EnableJpaAuditing +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + + @Bean + public CommandLineRunner run(ApplicationContext context){ + return args -> { +// MemberRepository memberRepository = context.getBean(MemberRepository.class); +// MemberMissionRepository memberMissionRepository = context.getBean(MemberMissionRepository.class); +// String regionName = "서울"; +// Long userId = 2L; +// +// System.out.println("Executing findStoresByNameAndScore with parameters:"); +// +// System.out.println(memberRepository.getMypageInfo(userId)); +// memberMissionRepository.findPossibleMissions(regionName, userId,10L,10).forEach(System.out::println); +// Integer i = memberMissionRepository.regionMissionCount(regionName, userId); +// System.out.println(i); + }; + } +} diff --git a/src/main/java/umc/spring/apiPayload/ApiResponse.java b/src/main/java/umc/spring/apiPayload/ApiResponse.java new file mode 100644 index 0000000..46f00e2 --- /dev/null +++ b/src/main/java/umc/spring/apiPayload/ApiResponse.java @@ -0,0 +1,33 @@ +package umc.spring.apiPayload; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import lombok.AllArgsConstructor; +import lombok.Getter; +import umc.spring.apiPayload.code.SuccessStatus; + +@Getter +@AllArgsConstructor +@JsonPropertyOrder({"isSuccess", "code", "message", "result"}) +public class ApiResponse { + @JsonProperty("isSuccess") + private final boolean isSuccess; + private final String code; + private final String message; + @JsonInclude(JsonInclude.Include.NON_NULL) + private T result; + + + public static ApiResponse onSuccess(T result){ + return new ApiResponse<>(true, SuccessStatus._OK.getCode() , SuccessStatus._OK.getMessage(), result); + } + + public static ApiResponse of(BaseCode code, T result){ + return new ApiResponse<>(true, code.getReasonHttpStatus().getCode() , code.getReasonHttpStatus().getMessage(), result); + } + + public static ApiResponse onFailure(String code,String message,T data){ + return new ApiResponse<>(false,code,message,data); + } +} diff --git a/src/main/java/umc/spring/apiPayload/BaseCode.java b/src/main/java/umc/spring/apiPayload/BaseCode.java new file mode 100644 index 0000000..194df53 --- /dev/null +++ b/src/main/java/umc/spring/apiPayload/BaseCode.java @@ -0,0 +1,6 @@ +package umc.spring.apiPayload; + +public interface BaseCode { + ReasonDTO getReason(); + ReasonDTO getReasonHttpStatus(); +} diff --git a/src/main/java/umc/spring/apiPayload/BaseErrorCode.java b/src/main/java/umc/spring/apiPayload/BaseErrorCode.java new file mode 100644 index 0000000..86769d1 --- /dev/null +++ b/src/main/java/umc/spring/apiPayload/BaseErrorCode.java @@ -0,0 +1,6 @@ +package umc.spring.apiPayload; + +public interface BaseErrorCode { + ErrorReasonDTO getReason(); + ErrorReasonDTO getReasonHttpStatus(); +} diff --git a/src/main/java/umc/spring/apiPayload/ErrorReasonDTO.java b/src/main/java/umc/spring/apiPayload/ErrorReasonDTO.java new file mode 100644 index 0000000..289ee83 --- /dev/null +++ b/src/main/java/umc/spring/apiPayload/ErrorReasonDTO.java @@ -0,0 +1,16 @@ +package umc.spring.apiPayload; + +import lombok.Builder; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@Builder +public class ErrorReasonDTO { + private HttpStatus httpStatus; + private final boolean isSuccess; + private final String code; + private final String message; + + public boolean getIsSuccess(){return isSuccess;} +} diff --git a/src/main/java/umc/spring/apiPayload/ReasonDTO.java b/src/main/java/umc/spring/apiPayload/ReasonDTO.java new file mode 100644 index 0000000..bc8c8b8 --- /dev/null +++ b/src/main/java/umc/spring/apiPayload/ReasonDTO.java @@ -0,0 +1,17 @@ +package umc.spring.apiPayload; + +import lombok.Builder; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@Builder +public class ReasonDTO { + private HttpStatus httpStatus; + + private final boolean isSuccess; + private final String code; + private final String message; + + public boolean getIsSuccess(){return isSuccess;} +} diff --git a/src/main/java/umc/spring/apiPayload/code/ErrorStatus.java b/src/main/java/umc/spring/apiPayload/code/ErrorStatus.java new file mode 100644 index 0000000..4a0f6ca --- /dev/null +++ b/src/main/java/umc/spring/apiPayload/code/ErrorStatus.java @@ -0,0 +1,58 @@ +package umc.spring.apiPayload.code; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; +import umc.spring.apiPayload.BaseErrorCode; +import umc.spring.apiPayload.ErrorReasonDTO; + +@Getter +@AllArgsConstructor +public enum ErrorStatus implements BaseErrorCode { + + _INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "COMMON500", "서버 에러, 관리자에게 문의 바랍니다."), + _BAD_REQUEST(HttpStatus.BAD_REQUEST,"COMMON400","잘못된 요청입니다."), + _UNAUTHORIZED(HttpStatus.UNAUTHORIZED,"COMMON401","인증이 필요합니다."), + _FORBIDDEN(HttpStatus.FORBIDDEN, "COMMON403", "금지된 요청입니다."), + + // 멤버 관려 에러 + MEMBER_NOT_FOUND(HttpStatus.BAD_REQUEST, "MEMBER4001", "사용자가 없습니다."), + NICKNAME_NOT_EXIST(HttpStatus.BAD_REQUEST, "MEMBER4002", "닉네임은 필수 입니다."), + FOOD_CATEGORY_NOT_FOUND(HttpStatus.BAD_REQUEST, "MEMBER4003", "선호 음식을 선택해주세요"), + //Mission 관련 에러 + MISSION_NOT_FOUND(HttpStatus.BAD_REQUEST, "MISSION4001", "해당 미션이 존재하지 않습니다"), + MISSION_ALREADY_CHALLENGED(HttpStatus.BAD_REQUEST, "MISSION4002", "해당 미션은 이미 진행중인 미션입니다."), + //Member_mission관련 에러 + MEMBER_MISSION_NOT_FOUND(HttpStatus.NOT_FOUND, "MEMBERMISSION4001", "해당 유저의 해당 미션이 존재하지 않습니다."), + //Store 관련 에러 + STORE_NOT_FOUND(HttpStatus.BAD_REQUEST, "STORE4001", "가게가 존재하지 않습니다."), + //Region 관련 에러 + REGION_NOT_EXIST(HttpStatus.BAD_REQUEST, "REGION4001", "지역이 존재하지 않습니다."), + TEMP_EXCEPTION(HttpStatus.BAD_REQUEST, "TEMP4001", "test exception"), + //Page 관련 에러 + PAGE_NOT_EXIST(HttpStatus.BAD_REQUEST, "PAGE4001", "존재하지 않는 페이지입니다."), + INVALID_TOKEN(HttpStatus.BAD_REQUEST, "TOKEN4001", "존재하지 않는 토큰입니다."), + INVALID_PASSWORD(HttpStatus.BAD_REQUEST, "TOKEN4001", "비밀번호가 일치하지 않습니다."); + private final HttpStatus httpStatus; + private final String code; + private final String message; + + @Override + public ErrorReasonDTO getReason(){ + return ErrorReasonDTO.builder() + .message(message) + .code(code) + .isSuccess(false) + .build(); + } + + @Override + public ErrorReasonDTO getReasonHttpStatus(){ + return ErrorReasonDTO.builder() + .message(message) + .code(code) + .isSuccess(false) + .httpStatus(httpStatus) + .build(); + } +} diff --git a/src/main/java/umc/spring/apiPayload/code/SuccessStatus.java b/src/main/java/umc/spring/apiPayload/code/SuccessStatus.java new file mode 100644 index 0000000..2afa035 --- /dev/null +++ b/src/main/java/umc/spring/apiPayload/code/SuccessStatus.java @@ -0,0 +1,37 @@ +package umc.spring.apiPayload.code; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; +import umc.spring.apiPayload.BaseCode; +import umc.spring.apiPayload.ReasonDTO; + +@Getter +@AllArgsConstructor +public enum SuccessStatus implements BaseCode { + + _OK(HttpStatus.OK, "COMMON200", "성공입니다."); + + private final HttpStatus httpStatus; + private final String code; + private final String message; + + @Override + public ReasonDTO getReason(){ + return ReasonDTO.builder() + .message(message) + .code(code) + .isSuccess(true) + .build(); + } + + @Override + public ReasonDTO getReasonHttpStatus(){ + return ReasonDTO.builder() + .message(message) + .code(code) + .isSuccess(true) + .httpStatus(httpStatus) + .build(); + } +} diff --git a/src/main/java/umc/spring/apiPayload/exception/ExceptionAdvice.java b/src/main/java/umc/spring/apiPayload/exception/ExceptionAdvice.java new file mode 100644 index 0000000..302d979 --- /dev/null +++ b/src/main/java/umc/spring/apiPayload/exception/ExceptionAdvice.java @@ -0,0 +1,147 @@ +package umc.spring.apiPayload.exception; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.ConstraintViolation; +import jakarta.validation.ConstraintViolationException; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.HttpStatusCode; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.context.request.ServletWebRequest; +import org.springframework.web.context.request.WebRequest; +import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; +import umc.spring.apiPayload.ApiResponse; +import umc.spring.apiPayload.ErrorReasonDTO; +import umc.spring.apiPayload.code.ErrorStatus; + +import javax.swing.text.html.Option; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Optional; + +@Slf4j +@RestControllerAdvice(annotations = {RestController.class}) +public class ExceptionAdvice extends ResponseEntityExceptionHandler { + + + @ExceptionHandler + public ResponseEntity validation(ConstraintViolationException e, WebRequest request) { + String errorMessage = e.getConstraintViolations().stream() + .map(constraintViolation -> constraintViolation.getMessage()) + .findFirst() + .orElseThrow(() -> new RuntimeException("ConstraintViolationException 추출 도중 에러 발생")); + return handleExceptionInternalConstraint(e, ErrorStatus.valueOf(errorMessage), HttpHeaders.EMPTY,request); + } + + @Override + public ResponseEntity handleMethodArgumentNotValid(MethodArgumentNotValidException e, HttpHeaders headers, HttpStatusCode status, WebRequest request) { + + Map errors = new LinkedHashMap<>(); + + e.getBindingResult().getFieldErrors().stream() + .forEach(fieldError -> { + String fieldName = fieldError.getField(); + String errorMessage = Optional.ofNullable(fieldError.getDefaultMessage()).orElse(""); + errors.merge(fieldName, errorMessage, (existingErrorMessage, newErrorMessage) -> existingErrorMessage + ", " + newErrorMessage); + }); + + e.getBindingResult().getGlobalErrors().stream() + .forEach(objectError->{ + String objectName = objectError.getObjectName(); + String errorMessage = Optional.ofNullable(objectError.getDefaultMessage()).orElse(""); + errors.put(objectName,errorMessage); + }); + log.info("에러 발생"); + return handleExceptionInternalArgs(e, HttpHeaders.EMPTY,ErrorStatus.valueOf("_BAD_REQUEST"),request,errors); + } + + + @ExceptionHandler + public ResponseEntity exception(Exception e, WebRequest request) { + e.printStackTrace(); + + return handleExceptionInternalFalse(e, ErrorStatus._INTERNAL_SERVER_ERROR, HttpHeaders.EMPTY, ErrorStatus._INTERNAL_SERVER_ERROR.getHttpStatus(),request, e.getMessage()); + } + + @ExceptionHandler(value = GeneralException.class) //GeneralException에 대한 오류를 처리 + public ResponseEntity onThrowException(GeneralException generalException, HttpServletRequest request) { + ErrorReasonDTO errorReasonHttpStatus = generalException.getErrorReasonHttpStatus(); + return handleExceptionInternal(generalException,errorReasonHttpStatus,null,request); + } + +// @ExceptionHandler(value = ConstraintViolationException.class) +// public ResponseEntity handleConstraintViolation(ConstraintViolationException ex, WebRequest request) { +// String errorKey = ex.getConstraintViolations().stream() +// .map(v -> v.getMessage()) +// .findFirst() +// .orElse("INVALID_REQUEST"); +// +// ErrorStatus status; +// +// switch (errorKey) { +// case "STORE_NOT_FOUND" -> status = ErrorStatus.STORE_NOT_FOUND; +// case "PAGE_NOT_EXIST" -> status = ErrorStatus.PAGE_NOT_EXIST; +// default -> status = ErrorStatus._BAD_REQUEST; +// } +// +// ApiResponse body = ApiResponse.onFailure(status.getCode(), status.getMessage(), null); +// return ResponseEntity.status(status.getHttpStatus()).body(body); +// } + + private ResponseEntity handleExceptionInternal(Exception e, ErrorReasonDTO reason, + HttpHeaders headers, HttpServletRequest request) { + + ApiResponse body = ApiResponse.onFailure(reason.getCode(),reason.getMessage(),null); +// e.printStackTrace(); + + WebRequest webRequest = new ServletWebRequest(request); + return super.handleExceptionInternal( + e, + body, + headers, + reason.getHttpStatus(), + webRequest + ); + } + + private ResponseEntity handleExceptionInternalFalse(Exception e, ErrorStatus errorCommonStatus, + HttpHeaders headers, HttpStatus status, WebRequest request, String errorPoint) { + ApiResponse body = ApiResponse.onFailure(errorCommonStatus.getCode(),errorCommonStatus.getMessage(),errorPoint); + return super.handleExceptionInternal( + e, + body, + headers, + status, + request + ); + } + + private ResponseEntity handleExceptionInternalArgs(Exception e, HttpHeaders headers, ErrorStatus errorCommonStatus, + WebRequest request, Map errorArgs) { + ApiResponse body = ApiResponse.onFailure(errorCommonStatus.getCode(),errorCommonStatus.getMessage(),errorArgs); + return super.handleExceptionInternal( + e, + body, + headers, + errorCommonStatus.getHttpStatus(), + request + ); + } + + private ResponseEntity handleExceptionInternalConstraint(Exception e, ErrorStatus errorCommonStatus, + HttpHeaders headers, WebRequest request) { + ApiResponse body = ApiResponse.onFailure(errorCommonStatus.getCode(), errorCommonStatus.getMessage(), null); + return super.handleExceptionInternal( + e, + body, + headers, + errorCommonStatus.getHttpStatus(), + request + ); + } +} diff --git a/src/main/java/umc/spring/apiPayload/exception/GeneralException.java b/src/main/java/umc/spring/apiPayload/exception/GeneralException.java new file mode 100644 index 0000000..55da271 --- /dev/null +++ b/src/main/java/umc/spring/apiPayload/exception/GeneralException.java @@ -0,0 +1,21 @@ +package umc.spring.apiPayload.exception; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import umc.spring.apiPayload.BaseCode; +import umc.spring.apiPayload.BaseErrorCode; +import umc.spring.apiPayload.ErrorReasonDTO; + +@Getter +@AllArgsConstructor +public class GeneralException extends RuntimeException { + private BaseErrorCode code; + + public ErrorReasonDTO getErrorReason(){ + return this.code.getReason(); + } + + public ErrorReasonDTO getErrorReasonHttpStatus(){ + return this.code.getReasonHttpStatus(); + } +} diff --git a/src/main/java/umc/spring/apiPayload/exception/handler/FoodCategoryHandler.java b/src/main/java/umc/spring/apiPayload/exception/handler/FoodCategoryHandler.java new file mode 100644 index 0000000..9d92569 --- /dev/null +++ b/src/main/java/umc/spring/apiPayload/exception/handler/FoodCategoryHandler.java @@ -0,0 +1,8 @@ +package umc.spring.apiPayload.exception.handler; + +import umc.spring.apiPayload.BaseErrorCode; +import umc.spring.apiPayload.exception.GeneralException; + +public class FoodCategoryHandler extends GeneralException { + public FoodCategoryHandler(BaseErrorCode code){super(code);} +} diff --git a/src/main/java/umc/spring/apiPayload/exception/handler/MemberHandler.java b/src/main/java/umc/spring/apiPayload/exception/handler/MemberHandler.java new file mode 100644 index 0000000..d99a65a --- /dev/null +++ b/src/main/java/umc/spring/apiPayload/exception/handler/MemberHandler.java @@ -0,0 +1,10 @@ +package umc.spring.apiPayload.exception.handler; + +import umc.spring.apiPayload.BaseErrorCode; +import umc.spring.apiPayload.exception.GeneralException; + +public class MemberHandler extends GeneralException { + public MemberHandler(BaseErrorCode code) { + super(code); + } +} diff --git a/src/main/java/umc/spring/apiPayload/exception/handler/MemberMissionHandler.java b/src/main/java/umc/spring/apiPayload/exception/handler/MemberMissionHandler.java new file mode 100644 index 0000000..34b41d7 --- /dev/null +++ b/src/main/java/umc/spring/apiPayload/exception/handler/MemberMissionHandler.java @@ -0,0 +1,11 @@ +package umc.spring.apiPayload.exception.handler; + +import umc.spring.apiPayload.BaseErrorCode; +import umc.spring.apiPayload.exception.GeneralException; + +public class MemberMissionHandler extends GeneralException { + + public MemberMissionHandler(BaseErrorCode code) { + super(code); + } +} diff --git a/src/main/java/umc/spring/apiPayload/exception/handler/MissionHandler.java b/src/main/java/umc/spring/apiPayload/exception/handler/MissionHandler.java new file mode 100644 index 0000000..c298aa6 --- /dev/null +++ b/src/main/java/umc/spring/apiPayload/exception/handler/MissionHandler.java @@ -0,0 +1,10 @@ +package umc.spring.apiPayload.exception.handler; + +import umc.spring.apiPayload.BaseErrorCode; +import umc.spring.apiPayload.exception.GeneralException; + +public class MissionHandler extends GeneralException { + public MissionHandler(BaseErrorCode code) { + super(code); + } +} diff --git a/src/main/java/umc/spring/apiPayload/exception/handler/RegionHandler.java b/src/main/java/umc/spring/apiPayload/exception/handler/RegionHandler.java new file mode 100644 index 0000000..f40d62b --- /dev/null +++ b/src/main/java/umc/spring/apiPayload/exception/handler/RegionHandler.java @@ -0,0 +1,8 @@ +package umc.spring.apiPayload.exception.handler; + +import umc.spring.apiPayload.BaseErrorCode; +import umc.spring.apiPayload.exception.GeneralException; + +public class RegionHandler extends GeneralException { + public RegionHandler(BaseErrorCode code){super(code);} +} diff --git a/src/main/java/umc/spring/apiPayload/exception/handler/StoreHandler.java b/src/main/java/umc/spring/apiPayload/exception/handler/StoreHandler.java new file mode 100644 index 0000000..946e05f --- /dev/null +++ b/src/main/java/umc/spring/apiPayload/exception/handler/StoreHandler.java @@ -0,0 +1,10 @@ +package umc.spring.apiPayload.exception.handler; + +import umc.spring.apiPayload.BaseErrorCode; +import umc.spring.apiPayload.exception.GeneralException; + +public class StoreHandler extends GeneralException { + public StoreHandler(BaseErrorCode code) { + super(code); + } +} diff --git a/src/main/java/umc/spring/apiPayload/exception/handler/TempHandler.java b/src/main/java/umc/spring/apiPayload/exception/handler/TempHandler.java new file mode 100644 index 0000000..af39a60 --- /dev/null +++ b/src/main/java/umc/spring/apiPayload/exception/handler/TempHandler.java @@ -0,0 +1,10 @@ +package umc.spring.apiPayload.exception.handler; + +import umc.spring.apiPayload.BaseErrorCode; +import umc.spring.apiPayload.exception.GeneralException; + +public class TempHandler extends GeneralException { + public TempHandler(BaseErrorCode errorCode){ + super(errorCode); + } +} diff --git a/src/main/java/umc/spring/config/QueryDSLConfig.java b/src/main/java/umc/spring/config/QueryDSLConfig.java new file mode 100644 index 0000000..9948c3f --- /dev/null +++ b/src/main/java/umc/spring/config/QueryDSLConfig.java @@ -0,0 +1,20 @@ +package umc.spring.config; + +import com.querydsl.jpa.impl.JPAQueryFactory; +import jakarta.persistence.EntityManager; +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import umc.spring.domain.Store; + +@Configuration +@RequiredArgsConstructor +public class QueryDSLConfig { + private final EntityManager entityManager; + + @Bean + public JPAQueryFactory jpaQueryFactory(){ + return new JPAQueryFactory(entityManager); + } + +} diff --git a/src/main/java/umc/spring/config/SwaggerConfig.java b/src/main/java/umc/spring/config/SwaggerConfig.java new file mode 100644 index 0000000..1072b0a --- /dev/null +++ b/src/main/java/umc/spring/config/SwaggerConfig.java @@ -0,0 +1,36 @@ +package umc.spring.config; + +import io.swagger.v3.oas.models.Components; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.security.SecurityRequirement; +import io.swagger.v3.oas.models.security.SecurityScheme; +import io.swagger.v3.oas.models.servers.Server; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class SwaggerConfig { + + @Bean + public OpenAPI UMCstudyAPI(){ + Info info = new Info() + .title("UMC Server WorkBook API") + .description("UMC Server WorkBook API") + .version("1.0.0"); + String jwtSchemeName = "JWT TOKEN"; + SecurityRequirement securityRequirement = new SecurityRequirement().addList(jwtSchemeName); + Components components = new Components() + .addSecuritySchemes(jwtSchemeName, new SecurityScheme() + .name(jwtSchemeName) + .type(SecurityScheme.Type.HTTP) // HTTP 방식 + .scheme("bearer") + .bearerFormat("JWT")); + + return new OpenAPI() + .addServersItem(new Server().url("/")) + .info(info) + .addSecurityItem(securityRequirement) + .components(components); + } +} diff --git a/src/main/java/umc/spring/config/properties/Constants.java b/src/main/java/umc/spring/config/properties/Constants.java new file mode 100644 index 0000000..e6b2850 --- /dev/null +++ b/src/main/java/umc/spring/config/properties/Constants.java @@ -0,0 +1,6 @@ +package umc.spring.config.properties; + +public final class Constants { + public static final String AUTH_HEADER = "Authorization"; + public static final String TOKEN_PREFIX = "Bearer "; +} diff --git a/src/main/java/umc/spring/config/properties/JwtProperties.java b/src/main/java/umc/spring/config/properties/JwtProperties.java new file mode 100644 index 0000000..4288385 --- /dev/null +++ b/src/main/java/umc/spring/config/properties/JwtProperties.java @@ -0,0 +1,22 @@ +package umc.spring.config.properties; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +@Component +@Getter +@Setter +@ConfigurationProperties("jwt.token") +public class JwtProperties { + private String secretKey=""; + private Expiration expiration; + + @Getter + @Setter + public static class Expiration{ + private Long access; + // TODO: refreshToken + } +} \ No newline at end of file diff --git a/src/main/java/umc/spring/config/security/CustomUserDetailService.java b/src/main/java/umc/spring/config/security/CustomUserDetailService.java new file mode 100644 index 0000000..4905c21 --- /dev/null +++ b/src/main/java/umc/spring/config/security/CustomUserDetailService.java @@ -0,0 +1,27 @@ +package umc.spring.config.security; + +import lombok.RequiredArgsConstructor; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; +import umc.spring.domain.Member; +import umc.spring.repository.MemberRepository; + +@Service +@RequiredArgsConstructor +public class CustomUserDetailService implements UserDetailsService { + private final MemberRepository memberRepository; + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException{ + Member member = memberRepository.findByEmail(username).orElseThrow(() -> new UsernameNotFoundException("해당 이메일을 가진 유저가 존재하지 않습니다.")); + + return User + .withUsername(member.getName()) + .password(member.getPassword()) + .roles(member.getRole().name()) + .build(); + } +} diff --git a/src/main/java/umc/spring/config/security/SecurityConfig.java b/src/main/java/umc/spring/config/security/SecurityConfig.java new file mode 100644 index 0000000..0782b3c --- /dev/null +++ b/src/main/java/umc/spring/config/security/SecurityConfig.java @@ -0,0 +1,43 @@ +package umc.spring.config.security; + +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import umc.spring.config.security.jwt.JwtAuthenticationFilter; +import umc.spring.config.security.jwt.JwtTokenProvider; + +@EnableWebSecurity +@Configuration +@RequiredArgsConstructor +public class SecurityConfig { + private final JwtTokenProvider jwtTokenProvider; + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http + .sessionManagement(session -> + session.sessionCreationPolicy(SessionCreationPolicy.STATELESS) + ) + .authorizeHttpRequests( + (requests) -> requests + .requestMatchers("/", "/members/join", "/members/login", "/swagger-ui/**", "/v3/api-docs/**").permitAll() + .requestMatchers("/admin/**").hasRole("ADMIN") + .anyRequest().authenticated() + ) + .csrf(csrf->csrf.disable()) + .addFilterBefore(new JwtAuthenticationFilter(jwtTokenProvider), UsernamePasswordAuthenticationFilter.class); + + return http.build(); + } + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } +} diff --git a/src/main/java/umc/spring/config/security/jwt/JwtAuthenticationFilter.java b/src/main/java/umc/spring/config/security/jwt/JwtAuthenticationFilter.java new file mode 100644 index 0000000..1cd3c5c --- /dev/null +++ b/src/main/java/umc/spring/config/security/jwt/JwtAuthenticationFilter.java @@ -0,0 +1,43 @@ +package umc.spring.config.security.jwt; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.util.StringUtils; +import org.springframework.web.filter.OncePerRequestFilter; +import umc.spring.config.properties.Constants; + +import java.io.IOException; + +@RequiredArgsConstructor +public class JwtAuthenticationFilter extends OncePerRequestFilter { + + private final JwtTokenProvider jwtTokenProvider; + + @Override + protected void doFilterInternal(HttpServletRequest request, + HttpServletResponse response, + FilterChain filterChain) + throws ServletException, IOException { + + String token = resolveToken(request); + + if(StringUtils.hasText(token) && jwtTokenProvider.validateToken(token)) { + Authentication authentication = jwtTokenProvider.getAuthentication(token); + SecurityContextHolder.getContext().setAuthentication(authentication); + } + filterChain.doFilter(request, response); + } + + private String resolveToken(HttpServletRequest request) { + String bearerToken = request.getHeader(Constants.AUTH_HEADER); + if(StringUtils.hasText(bearerToken) && bearerToken.startsWith(Constants.TOKEN_PREFIX)) { + return bearerToken.substring(Constants.TOKEN_PREFIX.length()); + } + return null; + } +} diff --git a/src/main/java/umc/spring/config/security/jwt/JwtTokenProvider.java b/src/main/java/umc/spring/config/security/jwt/JwtTokenProvider.java new file mode 100644 index 0000000..a3a62bf --- /dev/null +++ b/src/main/java/umc/spring/config/security/jwt/JwtTokenProvider.java @@ -0,0 +1,84 @@ +package umc.spring.config.security.jwt; + +import io.jsonwebtoken.*; +import io.jsonwebtoken.security.Keys; +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.userdetails.User; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; +import umc.spring.apiPayload.code.ErrorStatus; +import umc.spring.apiPayload.exception.handler.MemberHandler; +import umc.spring.config.properties.Constants; +import umc.spring.config.properties.JwtProperties; + +import java.security.Key; +import java.util.Date; +import java.util.Collections; + +@Component +@RequiredArgsConstructor +public class JwtTokenProvider { + + private final JwtProperties jwtProperties; + + private Key getSigningKey() { + return Keys.hmacShaKeyFor(jwtProperties.getSecretKey().getBytes()); + } + + public String generateToken(Authentication authentication) { + String email = authentication.getName(); + + return Jwts.builder() + .setSubject(email) + .claim("role", authentication.getAuthorities().iterator().next().getAuthority()) + .setIssuedAt(new Date()) + .setExpiration(new Date(System.currentTimeMillis() + jwtProperties.getExpiration().getAccess())) + .signWith(getSigningKey(), SignatureAlgorithm.HS256) + .compact(); + } + + public boolean validateToken(String token) { + try { + Jwts.parserBuilder() + .setSigningKey(getSigningKey()) + .build() + .parseClaimsJws(token); + return true; + } catch (JwtException | IllegalArgumentException e) { + return false; + } + } + + public Authentication getAuthentication(String token) { + Claims claims = Jwts.parserBuilder() + .setSigningKey(getSigningKey()) + .build() + .parseClaimsJws(token) + .getBody(); + + String email = claims.getSubject(); + String role = claims.get("role", String.class); + + User principal = new User(email, "", Collections.singleton(() -> role)); + return new UsernamePasswordAuthenticationToken(principal, token, principal.getAuthorities()); + } + + public static String resolveToken(HttpServletRequest request) { + String bearerToken = request.getHeader(Constants.AUTH_HEADER); + if(StringUtils.hasText(bearerToken) && bearerToken.startsWith(Constants.TOKEN_PREFIX)) { + return bearerToken.substring(Constants.TOKEN_PREFIX.length()); + } + return null; + } + + public Authentication extractAuthentication(HttpServletRequest request){ + String accessToken = resolveToken(request); + if(accessToken == null || !validateToken(accessToken)) { + throw new MemberHandler(ErrorStatus.INVALID_TOKEN); + } + return getAuthentication(accessToken); + } +} \ No newline at end of file diff --git a/src/main/java/umc/spring/converter/MemberConverter.java b/src/main/java/umc/spring/converter/MemberConverter.java new file mode 100644 index 0000000..31e32bd --- /dev/null +++ b/src/main/java/umc/spring/converter/MemberConverter.java @@ -0,0 +1,78 @@ +package umc.spring.converter; + +import org.springframework.data.domain.Page; +import umc.spring.domain.Member; +import umc.spring.domain.Review; +import umc.spring.domain.enums.Gender; +import umc.spring.web.dto.MemberRequestDto; +import umc.spring.web.dto.MemberResponseDto; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public class MemberConverter { + public static MemberResponseDto.JoinResultDTO toJoinResultDTO(Member member){ + return MemberResponseDto.JoinResultDTO.builder() + .memberId(member.getId()) + .createdAt(LocalDateTime.now()) + .build(); + } + + public static MemberResponseDto.LoginResultDto toLoginResultDto(Long memberId, String accessToken){ + return MemberResponseDto.LoginResultDto.builder() + .memberId(memberId) + .accessToken(accessToken) + .build(); + } + + public static MemberResponseDto.MemberInfoDto toMemberInfoDto(Member member){ + return MemberResponseDto.MemberInfoDto.builder() + .email(member.getEmail()) + .name(member.getName()) + .gender(member.getGender()) + .build(); + } + + public static Member toMember(MemberRequestDto.JoinDto request){ + Gender gender = null; + switch (request.getGender()) { + case 1: gender = Gender.MALE; break; + case 2: gender = Gender.FEMALE; break; + case 3: gender = Gender.UNKNOWN; break; + } + return Member.builder() + .address(request.getAddress()) + .gender(gender) + .name(request.getName()) + .birth(request.getBirth()) + .email(request.getEmail()) + .phoneNumber(request.getPhoneNumber()) + .role(request.getRole()) + .memberFoodList(new ArrayList<>()) + .build(); + } + + public static MemberResponseDto.ReviewPreviewDto reviewPreviewDto(Review review){ + return MemberResponseDto.ReviewPreviewDto.builder() + .username(review.getMember().getName()) + .score(review.getScore()) + .createdAt(review.getCreatedAt().toLocalDate()) + .content(review.getContent()) + .build(); + } + public static MemberResponseDto.ReviewPreviewListDto reviewPreviewListDto(Page reviewList){ + List reviewPreviewDtoList = reviewList.stream(). + map(MemberConverter::reviewPreviewDto).collect(Collectors.toList()); + + return MemberResponseDto.ReviewPreviewListDto.builder() + .isLast(reviewList.isLast()) + .isFirst(reviewList.isFirst()) + .totalPage(reviewList.getTotalPages()) + .totalElements(reviewList.getTotalElements()) + .listSize(reviewPreviewDtoList.size()) + .reviewList(reviewPreviewDtoList) + .build(); + } +} diff --git a/src/main/java/umc/spring/converter/MemberFoodConverter.java b/src/main/java/umc/spring/converter/MemberFoodConverter.java new file mode 100644 index 0000000..13ef72b --- /dev/null +++ b/src/main/java/umc/spring/converter/MemberFoodConverter.java @@ -0,0 +1,18 @@ +package umc.spring.converter; + +import umc.spring.domain.FoodCategory; +import umc.spring.domain.mapping.MemberFood; + +import java.util.List; +import java.util.stream.Collectors; + +public class MemberFoodConverter { + public static List toMemberFoodList(List foodCategoryList){ + return foodCategoryList.stream() + .map(foodCategory -> + MemberFood.builder() + .foodCategory(foodCategory) + .build() + ).collect(Collectors.toList()); + } +} diff --git a/src/main/java/umc/spring/converter/MemberMissionConverter.java b/src/main/java/umc/spring/converter/MemberMissionConverter.java new file mode 100644 index 0000000..7ba1eea --- /dev/null +++ b/src/main/java/umc/spring/converter/MemberMissionConverter.java @@ -0,0 +1,67 @@ +package umc.spring.converter; + +import org.springframework.data.domain.Page; +import umc.spring.domain.Mission; +import umc.spring.domain.enums.MissionStatus; +import umc.spring.domain.mapping.MemberMission; +import umc.spring.web.dto.MemberResponseDto; +import umc.spring.web.dto.MissionRequestDto; +import umc.spring.web.dto.MissionResponseDto; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.stream.Collectors; + +public class MemberMissionConverter { + + public static MissionResponseDto.ChallengeMissionResultDto toChallengeMissionResultDto(MemberMission memberMission){ + return MissionResponseDto.ChallengeMissionResultDto.builder() + .memberId(memberMission.getMember().getId()) + .missionId(memberMission.getMission().getId()) + .status(memberMission.getStatus()) + .createdAt(LocalDateTime.now()) + .build(); + } + + public static MemberMission toMemberMission(MissionRequestDto.ChallengeMissionDto request){ + return MemberMission.builder() + .status(MissionStatus.ONGOING) + .build(); + } + + + public static MemberResponseDto.MissionPreviewDto missionPreviewDto(MemberMission memberMission){ + Mission mission = memberMission.getMission(); + + return MemberResponseDto.MissionPreviewDto.builder() + .description(mission.getDescription()) + .point(mission.getPoint()) + .deadline(mission.getDeadline()) + .status(memberMission.getStatus()) + .build(); + } + + public static MemberResponseDto.MissionPreviewListDto missionPreviewListDto(Page missionList){ + List missionPreviewList = missionList.stream() + .map(MemberMissionConverter::missionPreviewDto).collect(Collectors.toList()); + + return MemberResponseDto.MissionPreviewListDto.builder() + .isFirst(missionList.isFirst()) + .isLast(missionList.isLast()) + .totalPage(missionList.getTotalPages()) + .totalElements(missionList.getTotalElements()) + .listSize(missionPreviewList.size()) + .missionList(missionPreviewList) + .build(); + } + + public static MissionResponseDto.CompleteMissionResultDto completeMissionDto(MemberMission memberMission){ + return MissionResponseDto.CompleteMissionResultDto.builder() + .storeName(memberMission.getMission().getStore().getName()) + .description(memberMission.getMission().getDescription()) + .completedAt(memberMission.getCompletedAt()) + .point(memberMission.getMission().getPoint()) + .status(memberMission.getStatus()) + .build(); + } +} diff --git a/src/main/java/umc/spring/converter/MissionConverter.java b/src/main/java/umc/spring/converter/MissionConverter.java new file mode 100644 index 0000000..aa24961 --- /dev/null +++ b/src/main/java/umc/spring/converter/MissionConverter.java @@ -0,0 +1,55 @@ +package umc.spring.converter; + +import org.springframework.data.domain.Page; +import umc.spring.apiPayload.ApiResponse; +import umc.spring.domain.Mission; +import umc.spring.domain.Store; +import umc.spring.web.dto.MissionRequestDto; +import umc.spring.web.dto.MissionResponseDto; +import umc.spring.web.dto.StoreResponseDto; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public class MissionConverter { + + public static MissionResponseDto.AddMissionToStoreResultDto toAddMissionToStoreResultDto(Mission mission){ + return MissionResponseDto.AddMissionToStoreResultDto.builder() + .missionId(mission.getId()) + .storeId(mission.getStore().getId()) + .createdAt(LocalDateTime.now()) + .build(); + } + + public static Mission toMission(MissionRequestDto.AddMissionToStoreDto request){ + return Mission.builder() + .point(request.getPoint()) + .description(request.getDescription()) + .memberMissionList(new ArrayList<>()) + .deadline(request.getDeadline()) + .build(); + } + + public static StoreResponseDto.MissionPreviewDto missionPreviewDto(Mission mission){ + return StoreResponseDto.MissionPreviewDto.builder() + .description(mission.getDescription()) + .point(mission.getPoint()) + .deadline(mission.getDeadline()) + .build(); + } + + public static StoreResponseDto.MissionPreviewListDto missionPreviewListDto(Page missionList){ + List missionPreviewList = missionList.stream() + .map(MissionConverter::missionPreviewDto).collect(Collectors.toList()); + return StoreResponseDto.MissionPreviewListDto.builder() + .isLast(missionList.isLast()) + .isFirst(missionList.isFirst()) + .totalPage(missionList.getTotalPages()) + .totalElements(missionList.getTotalElements()) + .listSize(missionPreviewList.size()) + .missionList(missionPreviewList) + .build(); + } +} diff --git a/src/main/java/umc/spring/converter/ReviewConverter.java b/src/main/java/umc/spring/converter/ReviewConverter.java new file mode 100644 index 0000000..7ff8640 --- /dev/null +++ b/src/main/java/umc/spring/converter/ReviewConverter.java @@ -0,0 +1,54 @@ +package umc.spring.converter; + +import org.springframework.data.domain.Page; +import umc.spring.domain.Review; +import umc.spring.web.dto.MemberResponseDto; +import umc.spring.web.dto.ReviewRequestDto; +import umc.spring.web.dto.ReviewResponseDto; +import umc.spring.web.dto.StoreResponseDto; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public class ReviewConverter { + public static ReviewResponseDto.ReviewAddResultDto toReviewAddResultDto(Review review){ + return ReviewResponseDto.ReviewAddResultDto.builder() + .reviewId(review.getId()) + .createdAt(LocalDateTime.now()) + .build(); + } + + public static Review toReview(ReviewRequestDto.ReviewAddDto request){ + return Review.builder() + .content(request.getContent()) + .score(request.getScore()) + .reviewImageList(new ArrayList<>()) + .build(); + + } + + public static StoreResponseDto.ReviewPreviewDto reviewPreviewDto(Review review){ + return StoreResponseDto.ReviewPreviewDto.builder() + .ownerNickname(review.getMember().getName()) + .score(review.getScore()) + .createdAt(review.getCreatedAt().toLocalDate()) + .content(review.getContent()) + .build(); + } + public static StoreResponseDto.ReviewPreviewListDto reviewPreviewListDto(Page reviewList){ + List reviewPreviewDtoList = reviewList.stream(). + map(ReviewConverter::reviewPreviewDto).collect(Collectors.toList()); + + return StoreResponseDto.ReviewPreviewListDto.builder() + .isLast(reviewList.isLast()) + .isFirst(reviewList.isFirst()) + .totalPage(reviewList.getTotalPages()) + .totalElements(reviewList.getTotalElements()) + .listSize(reviewPreviewDtoList.size()) + .reviewList(reviewPreviewDtoList) + .build(); + } + +} diff --git a/src/main/java/umc/spring/converter/StoreConverter.java b/src/main/java/umc/spring/converter/StoreConverter.java new file mode 100644 index 0000000..4b21918 --- /dev/null +++ b/src/main/java/umc/spring/converter/StoreConverter.java @@ -0,0 +1,23 @@ +package umc.spring.converter; + +import umc.spring.domain.Store; +import umc.spring.web.dto.StoreRequestDto; +import umc.spring.web.dto.StoreResponseDto; + +import java.time.LocalDateTime; + +public class StoreConverter { + public static StoreResponseDto.AddStoreToRegionResultDto toAddStoreToRegionResultDto(Store store){ + return StoreResponseDto.AddStoreToRegionResultDto.builder() + .storeId(store.getId()) + .createdAt(LocalDateTime.now()) + .build(); + } + + public static Store toStore(StoreRequestDto.AddStoreToRegionDto request){ + return Store.builder() + .name(request.getName()) + .address(request.getAddress()) + .build(); + } +} diff --git a/src/main/java/umc/spring/converter/TempConverter.java b/src/main/java/umc/spring/converter/TempConverter.java new file mode 100644 index 0000000..cfba835 --- /dev/null +++ b/src/main/java/umc/spring/converter/TempConverter.java @@ -0,0 +1,17 @@ +package umc.spring.converter; + +import umc.spring.web.dto.TempResponse; + +public class TempConverter { + public static TempResponse.TempTestDTO toTempTestDTO(){ + return TempResponse.TempTestDTO.builder() + .testString("This is Test!") + .build(); + } + + public static TempResponse.TempExceptionDTO toTempExceptionDTO(Integer flag){ + return TempResponse.TempExceptionDTO.builder() + .flag(flag) + .build(); + } +} diff --git a/src/main/java/umc/spring/domain/FoodCategory.java b/src/main/java/umc/spring/domain/FoodCategory.java new file mode 100644 index 0000000..5a818c3 --- /dev/null +++ b/src/main/java/umc/spring/domain/FoodCategory.java @@ -0,0 +1,19 @@ +package umc.spring.domain; + +import jakarta.persistence.*; +import lombok.*; +import umc.spring.domain.common.BaseEntity; + +@Entity +@Getter +@Builder +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor +public class FoodCategory extends BaseEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false) + private String name; +} diff --git a/src/main/java/umc/spring/domain/Member.java b/src/main/java/umc/spring/domain/Member.java new file mode 100644 index 0000000..b65730d --- /dev/null +++ b/src/main/java/umc/spring/domain/Member.java @@ -0,0 +1,88 @@ +package umc.spring.domain; + +import jakarta.persistence.*; +import lombok.*; +import org.hibernate.annotations.ColumnDefault; +import org.hibernate.annotations.DynamicInsert; +import org.hibernate.annotations.DynamicUpdate; +import umc.spring.domain.common.BaseEntity; +import umc.spring.domain.enums.Gender; +import umc.spring.domain.enums.Role; +import umc.spring.domain.enums.SocialType; +import umc.spring.domain.enums.MemberStatus; +import umc.spring.domain.mapping.MemberFood; +import umc.spring.domain.mapping.MemberMission; +import umc.spring.domain.mapping.MemberTerms; + +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.List; + +@Entity +@Getter +@Setter +@DynamicUpdate +@DynamicInsert +@Builder +@NoArgsConstructor(access = AccessLevel.PROTECTED) //기본 생성자 +@AllArgsConstructor(access = AccessLevel.PUBLIC) //default가 public +public class Member extends BaseEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + @Column(nullable = false, length = 50) + private String name; + + @Column(nullable = false, length = 50) + private String address; + + @Column(nullable = true, length = 20) + private String phoneNumber; + + @Column(nullable = false, length = 50) + private String email; + + @Column(nullable = false) + private String password; + + @Enumerated(EnumType.STRING) + private Role role; + + @Column(nullable = false) + private LocalDate birth; + + //private String specAddress; + + @Enumerated(EnumType.STRING) + private Gender gender; + + @Enumerated(EnumType.STRING) + private SocialType socialType; + + @Enumerated(EnumType.STRING) + @Column(columnDefinition = "VARCHAR(15) DEFAULT 'ACTIVE'") + private MemberStatus status; + + private LocalDate inactiveDate; + + @ColumnDefault("0") + private Integer point; + + @OneToMany(mappedBy = "member", cascade = CascadeType.ALL) // Member의 변화에 따라 매핑한 엔티티가 영향을 받음 + private List memberFoodList = new ArrayList<>(); + + @OneToMany(mappedBy = "member", cascade = CascadeType.ALL) + private List memberTermsList = new ArrayList<>(); + + @OneToMany(mappedBy = "member", cascade = CascadeType.ALL) + private List reviewList = new ArrayList<>(); + + @OneToMany(mappedBy = "member", cascade = CascadeType.ALL) + private List memberMissionList = new ArrayList<>(); + + public void encodedPassword(String password){ + this.password = password; + } + +} diff --git a/src/main/java/umc/spring/domain/Mission.java b/src/main/java/umc/spring/domain/Mission.java new file mode 100644 index 0000000..c42afe5 --- /dev/null +++ b/src/main/java/umc/spring/domain/Mission.java @@ -0,0 +1,41 @@ +package umc.spring.domain; +import jakarta.persistence.*; +import lombok.*; +import umc.spring.domain.common.BaseEntity; +import umc.spring.domain.mapping.MemberMission; + +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.List; + +@Entity +@Getter +@Builder +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor +public class Mission extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private Integer point; + + private LocalDate deadline; + + private String description; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "store_id") + private Store store; + + @OneToMany(mappedBy = "mission", cascade = CascadeType.ALL) + private List memberMissionList = new ArrayList<>(); + + public void setStore(Store store){ + this.store = store; + if(!store.getMissionList().contains(this)){ + store.getMissionList().add(this); + } + } +} diff --git a/src/main/java/umc/spring/domain/Region.java b/src/main/java/umc/spring/domain/Region.java new file mode 100644 index 0000000..7535f1c --- /dev/null +++ b/src/main/java/umc/spring/domain/Region.java @@ -0,0 +1,29 @@ +package umc.spring.domain; +import jakarta.persistence.*; +import lombok.*; +import umc.spring.domain.common.BaseEntity; + +import java.util.ArrayList; +import java.util.List; + +@Entity +@Getter +@Builder +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor +public class Region extends BaseEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false, length = 20) + private String name; + + @OneToMany(mappedBy = "region", cascade = CascadeType.ALL) + private List storeList = new ArrayList<>(); + + public void addStore(Store store){ + storeList.add(store); + store.setRegion(this); + } +} diff --git a/src/main/java/umc/spring/domain/Review.java b/src/main/java/umc/spring/domain/Review.java new file mode 100644 index 0000000..9eb0c7d --- /dev/null +++ b/src/main/java/umc/spring/domain/Review.java @@ -0,0 +1,56 @@ +package umc.spring.domain; +import jakarta.persistence.*; +import lombok.*; +import umc.spring.domain.common.BaseEntity; + +import java.util.ArrayList; +import java.util.List; + +@Entity +@Getter +@Setter +@Builder +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor +public class Review extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String content; + + @Column(nullable = false) + private Float score; + + @OneToMany(mappedBy = "review", cascade = CascadeType.ALL) + private List reviewImageList = new ArrayList<>(); + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "store_id") + private Store store; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id") + private Member member; + + public void setMember(Member member) { + this.member = member; + if (!member.getReviewList().contains(this)) { + member.getReviewList().add(this); + } + } + public void setStore(Store store) { + this.store = store; + if (!store.getReviewList().contains(this)) { + store.getReviewList().add(this); + } + } + public void addReviewImage(ReviewImage image) { + this.reviewImageList.add(image); + image.setReview(this); // 연관관계 주인 쪽 설정 + } + + + +} diff --git a/src/main/java/umc/spring/domain/ReviewImage.java b/src/main/java/umc/spring/domain/ReviewImage.java new file mode 100644 index 0000000..8037f24 --- /dev/null +++ b/src/main/java/umc/spring/domain/ReviewImage.java @@ -0,0 +1,23 @@ +package umc.spring.domain; +import jakarta.persistence.*; +import lombok.*; +import umc.spring.domain.common.BaseEntity; + +@Entity +@Getter +@Setter +@Builder +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor +public class ReviewImage extends BaseEntity{ + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String imageUrl; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "review_id") + private Review review; +} diff --git a/src/main/java/umc/spring/domain/SalesTime.java b/src/main/java/umc/spring/domain/SalesTime.java new file mode 100644 index 0000000..eb7b0a9 --- /dev/null +++ b/src/main/java/umc/spring/domain/SalesTime.java @@ -0,0 +1,30 @@ +package umc.spring.domain; +import jakarta.persistence.*; +import lombok.*; +import umc.spring.domain.common.BaseEntity; + +import java.sql.Timestamp; +import java.time.LocalDateTime; + +@Entity +@Getter +@Builder +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor +public class SalesTime extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private int dayOfWeek; //(범위 1~7, validation) + + private LocalDateTime openTime; + + private LocalDateTime closeTime; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "store_id") + private Store store; + +} diff --git a/src/main/java/umc/spring/domain/Store.java b/src/main/java/umc/spring/domain/Store.java new file mode 100644 index 0000000..08b664f --- /dev/null +++ b/src/main/java/umc/spring/domain/Store.java @@ -0,0 +1,57 @@ +package umc.spring.domain; + +import jakarta.persistence.*; +import lombok.*; +import org.springframework.stereotype.Service; +import umc.spring.domain.common.BaseEntity; + +import java.util.ArrayList; +import java.util.List; + +@Entity +@Getter +@Setter +@Builder +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor +public class Store extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false, length = 50) + private String name; + + @Column(nullable = false, length = 50) + private String address; + + @Column(nullable = true) + private Float score = 0.0f; + + private String category; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "region_id") + private Region region; + + @OneToMany(mappedBy = "store", cascade = CascadeType.ALL) + private List reviewList = new ArrayList<>(); + + @OneToMany(mappedBy = "store", cascade = CascadeType.ALL) + private List missionList = new ArrayList<>(); + + @OneToMany(mappedBy = "store", cascade = CascadeType.ALL) + private List salesTimeList = new ArrayList<>(); + + @Override + public String toString() { + return "Store{" + + "id=" + id + + ", name='" + name + '\'' + + ", address='" + address + '\'' + + ", score=" + score + + ", region=" + (region != null ? region.getName() : "N/A") + // region의 이름 출력 + '}'; + } +} diff --git a/src/main/java/umc/spring/domain/Terms.java b/src/main/java/umc/spring/domain/Terms.java new file mode 100644 index 0000000..b31df3c --- /dev/null +++ b/src/main/java/umc/spring/domain/Terms.java @@ -0,0 +1,34 @@ +package umc.spring.domain; + +import jakarta.persistence.*; +import lombok.*; +import umc.spring.domain.common.BaseEntity; +import umc.spring.domain.enums.TermType; +import umc.spring.domain.mapping.MemberTerms; + +import java.util.ArrayList; +import java.util.List; + +@Entity +@Getter +@Builder +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor +public class Terms extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false, length = 100) + private String name; + + @Column(length = 225) + private String description; + + @Enumerated(EnumType.STRING) + private TermType type; + + @OneToMany(mappedBy = "terms", cascade = CascadeType.ALL) + private List memberTermsList = new ArrayList<>(); +} diff --git a/src/main/java/umc/spring/domain/common/BaseEntity.java b/src/main/java/umc/spring/domain/common/BaseEntity.java new file mode 100644 index 0000000..f83a2a8 --- /dev/null +++ b/src/main/java/umc/spring/domain/common/BaseEntity.java @@ -0,0 +1,23 @@ +package umc.spring.domain.common; + + +import jakarta.persistence.EntityListeners; +import jakarta.persistence.MappedSuperclass; +import lombok.Getter; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +import java.time.LocalDateTime; + +@MappedSuperclass // 직접 테이블로 매핑되지 않고, 상속받는 자식 엔티티에 필드만 물려줌 +@EntityListeners(AuditingEntityListener.class) //엔티티의 생명주기를 감지해서 자동으로 날찌를 채워주는 리스너 +@Getter +public abstract class BaseEntity { + + @CreatedDate + private LocalDateTime createdAt; + + @LastModifiedDate + private LocalDateTime updatedAt; +} diff --git a/src/main/java/umc/spring/domain/enums/Gender.java b/src/main/java/umc/spring/domain/enums/Gender.java new file mode 100644 index 0000000..abdde7f --- /dev/null +++ b/src/main/java/umc/spring/domain/enums/Gender.java @@ -0,0 +1,5 @@ +package umc.spring.domain.enums; + +public enum Gender { + MALE, FEMALE, UNKNOWN +} diff --git a/src/main/java/umc/spring/domain/enums/MemberStatus.java b/src/main/java/umc/spring/domain/enums/MemberStatus.java new file mode 100644 index 0000000..b416298 --- /dev/null +++ b/src/main/java/umc/spring/domain/enums/MemberStatus.java @@ -0,0 +1,5 @@ +package umc.spring.domain.enums; + +public enum MemberStatus { + ACTIVE, INACTIVE +} diff --git a/src/main/java/umc/spring/domain/enums/MissionStatus.java b/src/main/java/umc/spring/domain/enums/MissionStatus.java new file mode 100644 index 0000000..ac47724 --- /dev/null +++ b/src/main/java/umc/spring/domain/enums/MissionStatus.java @@ -0,0 +1,5 @@ +package umc.spring.domain.enums; + +public enum MissionStatus { + COMPLETE, WAIT, ONGOING +} diff --git a/src/main/java/umc/spring/domain/enums/Role.java b/src/main/java/umc/spring/domain/enums/Role.java new file mode 100644 index 0000000..61d3b50 --- /dev/null +++ b/src/main/java/umc/spring/domain/enums/Role.java @@ -0,0 +1,5 @@ +package umc.spring.domain.enums; + +public enum Role { + ADMIN, USER +} diff --git a/src/main/java/umc/spring/domain/enums/SocialType.java b/src/main/java/umc/spring/domain/enums/SocialType.java new file mode 100644 index 0000000..6206309 --- /dev/null +++ b/src/main/java/umc/spring/domain/enums/SocialType.java @@ -0,0 +1,5 @@ +package umc.spring.domain.enums; + +public enum SocialType { + KAKAO,GOOGLE,NAVER,APPLE +} diff --git a/src/main/java/umc/spring/domain/enums/TermType.java b/src/main/java/umc/spring/domain/enums/TermType.java new file mode 100644 index 0000000..72e8028 --- /dev/null +++ b/src/main/java/umc/spring/domain/enums/TermType.java @@ -0,0 +1,5 @@ +package umc.spring.domain.enums; + +public enum TermType { + OPTIONAL,ESSENTIAL +} diff --git a/src/main/java/umc/spring/domain/mapping/MemberFood.java b/src/main/java/umc/spring/domain/mapping/MemberFood.java new file mode 100644 index 0000000..ff38492 --- /dev/null +++ b/src/main/java/umc/spring/domain/mapping/MemberFood.java @@ -0,0 +1,38 @@ +package umc.spring.domain.mapping; +import jakarta.persistence.*; +import lombok.*; +import umc.spring.domain.FoodCategory; +import umc.spring.domain.Member; +import umc.spring.domain.common.BaseEntity; + +@Entity +@Getter +@Builder +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor +public class MemberFood extends BaseEntity{ + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id") + private Member member; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "category_id") + private FoodCategory foodCategory; + + + //연관관계 편의 메서드 + public void setMember(Member member){ + if(this.member != null) + member.getMemberFoodList().remove(this); + this.member = member; + member.getMemberFoodList().add(this); + } + + public void setFoodCategory(FoodCategory foodCategory){ + this.foodCategory = foodCategory; + } //양방향 아니라서 한 쪽만 넣어줌 +} diff --git a/src/main/java/umc/spring/domain/mapping/MemberMission.java b/src/main/java/umc/spring/domain/mapping/MemberMission.java new file mode 100644 index 0000000..e92842e --- /dev/null +++ b/src/main/java/umc/spring/domain/mapping/MemberMission.java @@ -0,0 +1,53 @@ +package umc.spring.domain.mapping; +import jakarta.persistence.*; +import lombok.*; +import umc.spring.domain.Member; +import umc.spring.domain.Mission; +import umc.spring.domain.common.BaseEntity; +import umc.spring.domain.enums.MissionStatus; + +import java.time.LocalDate; +import java.time.LocalDateTime; + +@Entity +@Getter +@Setter +@Builder +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor +public class MemberMission extends BaseEntity{ + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Enumerated(EnumType.STRING) + @Column(columnDefinition = "VARCHAR(10) DEFAULT 'WAIT'") + private MissionStatus status; + + private String certificationNumber; + + private LocalDateTime completedAt; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id") + private Member member; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name="mission_id") + private Mission mission; + + public void setMember(Member member){ + this.member = member; + if(!member.getMemberMissionList().contains(this)){ + member.getMemberMissionList().add(this); + } + } + public void setMission(Mission mission){ + this.mission = mission; + if(!mission.getMemberMissionList().contains(this)){ + mission.getMemberMissionList().add(this); + } + } + +} diff --git a/src/main/java/umc/spring/domain/mapping/MemberTerms.java b/src/main/java/umc/spring/domain/mapping/MemberTerms.java new file mode 100644 index 0000000..7b99e19 --- /dev/null +++ b/src/main/java/umc/spring/domain/mapping/MemberTerms.java @@ -0,0 +1,32 @@ +package umc.spring.domain.mapping; +import jakarta.persistence.*; +import lombok.*; +import umc.spring.domain.Member; +import umc.spring.domain.Terms; +import umc.spring.domain.common.BaseEntity; + +import java.time.LocalDateTime; + +@Entity +@Getter +@Builder +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor +public class MemberTerms extends BaseEntity{ + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id") + private Member member; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "terms_id") + private Terms terms; + + private Boolean isAgreed; + + private LocalDateTime agreedAt; +} diff --git a/src/main/java/umc/spring/repository/FoodCategoryRepository.java b/src/main/java/umc/spring/repository/FoodCategoryRepository.java new file mode 100644 index 0000000..1558776 --- /dev/null +++ b/src/main/java/umc/spring/repository/FoodCategoryRepository.java @@ -0,0 +1,8 @@ +package umc.spring.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import umc.spring.domain.FoodCategory; + +public interface FoodCategoryRepository extends JpaRepository { + +} diff --git a/src/main/java/umc/spring/repository/MemberMissionRepository.java b/src/main/java/umc/spring/repository/MemberMissionRepository.java new file mode 100644 index 0000000..5bbfa8a --- /dev/null +++ b/src/main/java/umc/spring/repository/MemberMissionRepository.java @@ -0,0 +1,16 @@ +package umc.spring.repository; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.jpa.repository.JpaRepository; +import umc.spring.domain.Member; +import umc.spring.domain.enums.MissionStatus; +import umc.spring.domain.mapping.MemberMission; + +import java.util.Optional; + +public interface MemberMissionRepository extends JpaRepository { + boolean existsByMemberIdAndMissionId(Long memberId, Long MissionId); + Page findByMemberAndStatus (Member member, MissionStatus status, PageRequest pageRequest); + Optional findByMissionIdAndMemberId(Long memberId, Long missionId); +} diff --git a/src/main/java/umc/spring/repository/MemberRepository.java b/src/main/java/umc/spring/repository/MemberRepository.java new file mode 100644 index 0000000..999b4c1 --- /dev/null +++ b/src/main/java/umc/spring/repository/MemberRepository.java @@ -0,0 +1,10 @@ +package umc.spring.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import umc.spring.domain.Member; + +import java.util.Optional; + +public interface MemberRepository extends JpaRepository { + Optional findByEmail(String email); +} diff --git a/src/main/java/umc/spring/repository/MissionRepository.java b/src/main/java/umc/spring/repository/MissionRepository.java new file mode 100644 index 0000000..6380aa9 --- /dev/null +++ b/src/main/java/umc/spring/repository/MissionRepository.java @@ -0,0 +1,11 @@ +package umc.spring.repository; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.jpa.repository.JpaRepository; +import umc.spring.domain.Mission; +import umc.spring.domain.Store; + +public interface MissionRepository extends JpaRepository { + Page findAllByStore(Store store, PageRequest pageRequest); +} diff --git a/src/main/java/umc/spring/repository/RegionRepository.java b/src/main/java/umc/spring/repository/RegionRepository.java new file mode 100644 index 0000000..d9b66a5 --- /dev/null +++ b/src/main/java/umc/spring/repository/RegionRepository.java @@ -0,0 +1,7 @@ +package umc.spring.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import umc.spring.domain.Region; + +public interface RegionRepository extends JpaRepository { +} diff --git a/src/main/java/umc/spring/repository/ReviewRepository.java b/src/main/java/umc/spring/repository/ReviewRepository.java new file mode 100644 index 0000000..4b8bd91 --- /dev/null +++ b/src/main/java/umc/spring/repository/ReviewRepository.java @@ -0,0 +1,13 @@ +package umc.spring.repository; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.jpa.repository.JpaRepository; +import umc.spring.domain.Member; +import umc.spring.domain.Review; +import umc.spring.domain.Store; + +public interface ReviewRepository extends JpaRepository { + Page findAllByStore(Store store, PageRequest pageRequest); + Page findAllByMember(Member member, PageRequest pageRequest); +} diff --git a/src/main/java/umc/spring/repository/storeRepository/StoreRepository.java b/src/main/java/umc/spring/repository/storeRepository/StoreRepository.java new file mode 100644 index 0000000..4c0be6e --- /dev/null +++ b/src/main/java/umc/spring/repository/storeRepository/StoreRepository.java @@ -0,0 +1,7 @@ +package umc.spring.repository.storeRepository; + +import org.springframework.data.jpa.repository.JpaRepository; +import umc.spring.domain.Store; + +public interface StoreRepository extends JpaRepository, StoreRepositoryCustom { +} diff --git a/src/main/java/umc/spring/repository/storeRepository/StoreRepositoryCustom.java b/src/main/java/umc/spring/repository/storeRepository/StoreRepositoryCustom.java new file mode 100644 index 0000000..dcf2f0d --- /dev/null +++ b/src/main/java/umc/spring/repository/storeRepository/StoreRepositoryCustom.java @@ -0,0 +1,9 @@ +package umc.spring.repository.storeRepository; + +import umc.spring.domain.Store; + +import java.util.List; + +public interface StoreRepositoryCustom { + List dynamicQueryWithBooleanBuilder(String name, Float score); +} diff --git a/src/main/java/umc/spring/repository/storeRepository/StoreRepositoryImpl.java b/src/main/java/umc/spring/repository/storeRepository/StoreRepositoryImpl.java new file mode 100644 index 0000000..61c21b4 --- /dev/null +++ b/src/main/java/umc/spring/repository/storeRepository/StoreRepositoryImpl.java @@ -0,0 +1,36 @@ +package umc.spring.repository.storeRepository; + +import com.querydsl.core.BooleanBuilder; +import com.querydsl.jpa.impl.JPAQueryFactory; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; +import umc.spring.domain.QStore; +import umc.spring.domain.Store; + +import java.util.List; + +@Repository +@RequiredArgsConstructor +public class StoreRepositoryImpl implements StoreRepositoryCustom{ + + private final JPAQueryFactory jpaQueryFactory; + private final QStore store = QStore.store; + + @Override + public List dynamicQueryWithBooleanBuilder(String name, Float score){ + BooleanBuilder predicate = new BooleanBuilder(); + + if(name!=null){ + predicate.and(store.name.eq(name)); + } + + if(score!=null){ + predicate.and(store.score.goe(4.0f)); + } + + return jpaQueryFactory + .selectFrom(store) + .where(predicate) + .fetch(); + } +} diff --git a/src/main/java/umc/spring/service/memberMissionService/MemberMissionCommandService.java b/src/main/java/umc/spring/service/memberMissionService/MemberMissionCommandService.java new file mode 100644 index 0000000..12c748e --- /dev/null +++ b/src/main/java/umc/spring/service/memberMissionService/MemberMissionCommandService.java @@ -0,0 +1,11 @@ +package umc.spring.service.memberMissionService; + +import umc.spring.converter.MemberMissionConverter; +import umc.spring.domain.mapping.MemberMission; +import umc.spring.web.dto.MissionRequestDto; + +public interface MemberMissionCommandService { + MemberMission challengeMission(MissionRequestDto.ChallengeMissionDto request); + boolean existsByMemberIdAndMissionId(Long memberId, Long missionId); + MemberMission setMissionComplete(Long missionId, Long memberId); +} diff --git a/src/main/java/umc/spring/service/memberMissionService/MemberMissionCommandServiceImpl.java b/src/main/java/umc/spring/service/memberMissionService/MemberMissionCommandServiceImpl.java new file mode 100644 index 0000000..f0e059f --- /dev/null +++ b/src/main/java/umc/spring/service/memberMissionService/MemberMissionCommandServiceImpl.java @@ -0,0 +1,59 @@ +package umc.spring.service.memberMissionService; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import umc.spring.apiPayload.code.ErrorStatus; +import umc.spring.apiPayload.exception.handler.MemberHandler; +import umc.spring.apiPayload.exception.handler.MemberMissionHandler; +import umc.spring.apiPayload.exception.handler.MissionHandler; +import umc.spring.converter.MemberMissionConverter; +import umc.spring.domain.Member; +import umc.spring.domain.Mission; +import umc.spring.domain.enums.MissionStatus; +import umc.spring.domain.mapping.MemberMission; +import umc.spring.repository.MemberMissionRepository; +import umc.spring.repository.MemberRepository; +import umc.spring.repository.MissionRepository; +import umc.spring.web.dto.MissionRequestDto; + +import java.time.LocalDateTime; +import java.util.UUID; + +@Service +@RequiredArgsConstructor +public class MemberMissionCommandServiceImpl implements MemberMissionCommandService{ + + private final MemberMissionRepository memberMissionRepository; + private final MemberRepository memberRepository; + private final MissionRepository missionRepository; + + @Override + public MemberMission challengeMission(MissionRequestDto.ChallengeMissionDto request){ + MemberMission memberMission = MemberMissionConverter.toMemberMission(request); + + Member member = memberRepository.findById(request.getMemberId()) + .orElseThrow(() -> new MemberHandler(ErrorStatus.MEMBER_NOT_FOUND)); + Mission mission = missionRepository.findById(request.getMissionId()) + .orElseThrow(() -> new MissionHandler(ErrorStatus.MISSION_NOT_FOUND)); + memberMission.setMember(member); + memberMission.setMission(mission); + + return memberMissionRepository.save(memberMission); + } + @Override + public boolean existsByMemberIdAndMissionId(Long memberId, Long missionId){ + return memberMissionRepository.existsByMemberIdAndMissionId(memberId, missionId); + } + + @Override + @Transactional + public MemberMission setMissionComplete(Long missionId, Long memberId) { + MemberMission memberMission = memberMissionRepository.findByMissionIdAndMemberId(missionId, memberId).orElseThrow(()->new MemberMissionHandler(ErrorStatus.MEMBER_MISSION_NOT_FOUND)); + memberMission.setStatus(MissionStatus.COMPLETE); + memberMission.setCompletedAt(LocalDateTime.now()); + memberMission.setCertificationNumber(UUID.randomUUID().toString()); + return memberMission; + } + +} diff --git a/src/main/java/umc/spring/service/memberMissionService/MemberMissionQueryService.java b/src/main/java/umc/spring/service/memberMissionService/MemberMissionQueryService.java new file mode 100644 index 0000000..4fd14ae --- /dev/null +++ b/src/main/java/umc/spring/service/memberMissionService/MemberMissionQueryService.java @@ -0,0 +1,9 @@ +package umc.spring.service.memberMissionService; + +import org.springframework.data.domain.Page; +import umc.spring.domain.enums.MissionStatus; +import umc.spring.domain.mapping.MemberMission; + +public interface MemberMissionQueryService { + Page getMissionPage(Long memberId, MissionStatus status, Integer page); +} diff --git a/src/main/java/umc/spring/service/memberMissionService/MemberMissionQueryServiceImpl.java b/src/main/java/umc/spring/service/memberMissionService/MemberMissionQueryServiceImpl.java new file mode 100644 index 0000000..f747c92 --- /dev/null +++ b/src/main/java/umc/spring/service/memberMissionService/MemberMissionQueryServiceImpl.java @@ -0,0 +1,24 @@ +package umc.spring.service.memberMissionService; + +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.stereotype.Service; +import umc.spring.apiPayload.code.ErrorStatus; +import umc.spring.apiPayload.exception.handler.MemberHandler; +import umc.spring.domain.Member; +import umc.spring.domain.enums.MissionStatus; +import umc.spring.domain.mapping.MemberMission; +import umc.spring.repository.MemberMissionRepository; +import umc.spring.repository.MemberRepository; + +@Service +@RequiredArgsConstructor +public class MemberMissionQueryServiceImpl implements MemberMissionQueryService{ + private final MemberRepository memberRepository; + private final MemberMissionRepository memberMissionRepository; + public Page getMissionPage(Long memberId, MissionStatus status, Integer page){ + Member member = memberRepository.findById(memberId).orElseThrow(()->new MemberHandler(ErrorStatus.MEMBER_NOT_FOUND)); + return memberMissionRepository.findByMemberAndStatus(member,status, PageRequest.of(page,10)); + } +} diff --git a/src/main/java/umc/spring/service/memberService/FoodCategoryService.java b/src/main/java/umc/spring/service/memberService/FoodCategoryService.java new file mode 100644 index 0000000..04e9f4d --- /dev/null +++ b/src/main/java/umc/spring/service/memberService/FoodCategoryService.java @@ -0,0 +1,5 @@ +package umc.spring.service.memberService; + +public interface FoodCategoryService { + boolean existById(Long id); +} diff --git a/src/main/java/umc/spring/service/memberService/FoodCategoryServiceImpl.java b/src/main/java/umc/spring/service/memberService/FoodCategoryServiceImpl.java new file mode 100644 index 0000000..b9f25cd --- /dev/null +++ b/src/main/java/umc/spring/service/memberService/FoodCategoryServiceImpl.java @@ -0,0 +1,16 @@ +package umc.spring.service.memberService; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import umc.spring.repository.FoodCategoryRepository; + +@Service +@RequiredArgsConstructor +public class FoodCategoryServiceImpl implements FoodCategoryService { + private final FoodCategoryRepository foodCategoryRepository; + + @Override + public boolean existById(Long id) { + return foodCategoryRepository.existsById(id); + } +} diff --git a/src/main/java/umc/spring/service/memberService/MemberCommandService.java b/src/main/java/umc/spring/service/memberService/MemberCommandService.java new file mode 100644 index 0000000..f7a0ec9 --- /dev/null +++ b/src/main/java/umc/spring/service/memberService/MemberCommandService.java @@ -0,0 +1,10 @@ +package umc.spring.service.memberService; + +import umc.spring.domain.Member; +import umc.spring.web.dto.MemberRequestDto; +import umc.spring.web.dto.MemberResponseDto; + +public interface MemberCommandService { + public Member joinMember(MemberRequestDto.JoinDto request); + MemberResponseDto.LoginResultDto loginMember(MemberRequestDto.LoginRequestDto request); +} diff --git a/src/main/java/umc/spring/service/memberService/MemberCommandServiceImpl.java b/src/main/java/umc/spring/service/memberService/MemberCommandServiceImpl.java new file mode 100644 index 0000000..50c050b --- /dev/null +++ b/src/main/java/umc/spring/service/memberService/MemberCommandServiceImpl.java @@ -0,0 +1,74 @@ +package umc.spring.service.memberService; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import umc.spring.apiPayload.code.ErrorStatus; +import umc.spring.apiPayload.exception.handler.FoodCategoryHandler; +import umc.spring.apiPayload.exception.handler.MemberHandler; +import umc.spring.config.security.jwt.JwtTokenProvider; +import umc.spring.converter.MemberConverter; +import umc.spring.converter.MemberFoodConverter; +import umc.spring.domain.FoodCategory; +import umc.spring.domain.Member; +import umc.spring.domain.mapping.MemberFood; +import umc.spring.repository.FoodCategoryRepository; +import umc.spring.repository.MemberRepository; +import umc.spring.web.dto.MemberRequestDto; +import umc.spring.web.dto.MemberResponseDto; + +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +@Slf4j +@Service +@RequiredArgsConstructor +public class MemberCommandServiceImpl implements MemberCommandService { + private final MemberRepository memberRepository; + private final FoodCategoryRepository foodCategoryRepository; + private final PasswordEncoder passwordEncoder; + private final JwtTokenProvider jwtTokenProvider; + + @Override + @Transactional + public Member joinMember(MemberRequestDto.JoinDto request){ + Member newMember = MemberConverter.toMember(request); + newMember.encodedPassword(passwordEncoder.encode(request.getPassword())); //비밀번호 암호화 후 저장 + List foodCategoryList = request.getPreferCategory().stream(). + map(category->{ + return foodCategoryRepository.findById(category).orElseThrow(() -> new FoodCategoryHandler(ErrorStatus.FOOD_CATEGORY_NOT_FOUND)); + }).collect(Collectors.toList()); + + List memberFoodList = MemberFoodConverter.toMemberFoodList(foodCategoryList); + memberFoodList.forEach(memberFood -> { + memberFood.setMember(newMember); + }); + log.info("join"); + return memberRepository.save(newMember); + } + + @Override + @Transactional + public MemberResponseDto.LoginResultDto loginMember(MemberRequestDto.LoginRequestDto request) { + Member member = memberRepository.findByEmail(request.getEmail()).orElseThrow(() -> new MemberHandler(ErrorStatus.MEMBER_NOT_FOUND)); + if(!passwordEncoder.matches(request.getPassword(), member.getPassword())){ + throw new MemberHandler(ErrorStatus.INVALID_PASSWORD); + } + + Authentication authentication = new UsernamePasswordAuthenticationToken( + member.getEmail(), null, Collections.singleton(()->member.getRole().name()) + ); + + String accessToken = jwtTokenProvider.generateToken(authentication); + return MemberConverter.toLoginResultDto( + member.getId(), + accessToken + ); + } + +} diff --git a/src/main/java/umc/spring/service/memberService/MemberQueryService.java b/src/main/java/umc/spring/service/memberService/MemberQueryService.java new file mode 100644 index 0000000..e24a1d4 --- /dev/null +++ b/src/main/java/umc/spring/service/memberService/MemberQueryService.java @@ -0,0 +1,11 @@ +package umc.spring.service.memberService; + +import jakarta.servlet.http.HttpServletRequest; +import org.springframework.data.domain.Page; +import umc.spring.domain.Review; +import umc.spring.web.dto.MemberResponseDto; + +public interface MemberQueryService { + Page getReviewList(Long memberId, Integer page); + MemberResponseDto.MemberInfoDto getMemberInfo(HttpServletRequest request); +} diff --git a/src/main/java/umc/spring/service/memberService/MemberQueryServiceImpl.java b/src/main/java/umc/spring/service/memberService/MemberQueryServiceImpl.java new file mode 100644 index 0000000..451a34d --- /dev/null +++ b/src/main/java/umc/spring/service/memberService/MemberQueryServiceImpl.java @@ -0,0 +1,43 @@ +package umc.spring.service.memberService; + +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.security.core.Authentication; +import org.springframework.stereotype.Service; +import umc.spring.apiPayload.code.ErrorStatus; +import umc.spring.apiPayload.exception.handler.MemberHandler; +import umc.spring.config.security.jwt.JwtTokenProvider; +import umc.spring.converter.MemberConverter; +import umc.spring.domain.Member; +import umc.spring.domain.Review; +import umc.spring.repository.MemberRepository; +import umc.spring.repository.ReviewRepository; +import umc.spring.web.dto.MemberResponseDto; + +@Service +@RequiredArgsConstructor +public class MemberQueryServiceImpl implements MemberQueryService { + + private final MemberRepository memberRepository; + private final ReviewRepository reviewRepository; + private final JwtTokenProvider jwtTokenProvider; + + @Override + public Page getReviewList(Long memberId, Integer page){ + Member member = memberRepository.findById(memberId).orElseThrow(()-> new MemberHandler(ErrorStatus.MEMBER_NOT_FOUND)); + Page memberPage = reviewRepository.findAllByMember(member, PageRequest.of(page, 10)); + return memberPage; + } + + @Override + public MemberResponseDto.MemberInfoDto getMemberInfo(HttpServletRequest request) { + Authentication authentication = jwtTokenProvider.extractAuthentication(request); + String email = authentication.getName(); + + Member member = memberRepository.findByEmail(email).orElseThrow(()->new MemberHandler(ErrorStatus.MISSION_NOT_FOUND)); + return MemberConverter.toMemberInfoDto(member); + } + +} diff --git a/src/main/java/umc/spring/service/missionService/MissionCommandService.java b/src/main/java/umc/spring/service/missionService/MissionCommandService.java new file mode 100644 index 0000000..ab42300 --- /dev/null +++ b/src/main/java/umc/spring/service/missionService/MissionCommandService.java @@ -0,0 +1,9 @@ +package umc.spring.service.missionService; + +import umc.spring.domain.Mission; +import umc.spring.domain.Review; +import umc.spring.web.dto.MissionRequestDto; + +public interface MissionCommandService { + Mission addMission(MissionRequestDto.AddMissionToStoreDto request); +} diff --git a/src/main/java/umc/spring/service/missionService/MissionCommandServiceImpl.java b/src/main/java/umc/spring/service/missionService/MissionCommandServiceImpl.java new file mode 100644 index 0000000..8ff4479 --- /dev/null +++ b/src/main/java/umc/spring/service/missionService/MissionCommandServiceImpl.java @@ -0,0 +1,35 @@ +package umc.spring.service.missionService; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import umc.spring.apiPayload.code.ErrorStatus; +import umc.spring.apiPayload.exception.handler.StoreHandler; +import umc.spring.converter.MissionConverter; +import umc.spring.domain.Mission; +import umc.spring.domain.Review; +import umc.spring.domain.Store; +import umc.spring.repository.MissionRepository; +import umc.spring.repository.storeRepository.StoreRepository; +import umc.spring.web.dto.MissionRequestDto; + +import java.util.Optional; + +@Service +@RequiredArgsConstructor +public class MissionCommandServiceImpl implements MissionCommandService{ + + private final MissionRepository missionRepository; + private final StoreRepository storeRepository; + + @Override + @Transactional + public Mission addMission(MissionRequestDto.AddMissionToStoreDto request){ + Mission mission = MissionConverter.toMission(request); + Store store = storeRepository.findById(request.getStoreId()) + .orElseThrow(()->new StoreHandler(ErrorStatus.STORE_NOT_FOUND)); + + mission.setStore(store); + return missionRepository.save(mission); + } +} diff --git a/src/main/java/umc/spring/service/missionService/MissionQueryService.java b/src/main/java/umc/spring/service/missionService/MissionQueryService.java new file mode 100644 index 0000000..be0749e --- /dev/null +++ b/src/main/java/umc/spring/service/missionService/MissionQueryService.java @@ -0,0 +1,8 @@ +package umc.spring.service.missionService; + +import org.springframework.data.domain.Page; +import umc.spring.domain.Mission; + +public interface MissionQueryService { + +} diff --git a/src/main/java/umc/spring/service/missionService/MissionQueryServiceImpl.java b/src/main/java/umc/spring/service/missionService/MissionQueryServiceImpl.java new file mode 100644 index 0000000..d5768bd --- /dev/null +++ b/src/main/java/umc/spring/service/missionService/MissionQueryServiceImpl.java @@ -0,0 +1,21 @@ +package umc.spring.service.missionService; + +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.stereotype.Service; +import umc.spring.apiPayload.code.ErrorStatus; +import umc.spring.apiPayload.exception.handler.StoreHandler; +import umc.spring.domain.Mission; +import umc.spring.domain.Store; +import umc.spring.repository.MissionRepository; +import umc.spring.repository.storeRepository.StoreRepository; + +@Service +@RequiredArgsConstructor +public class MissionQueryServiceImpl implements MissionQueryService{ + + private final StoreRepository storeRepository; + private final MissionRepository missionRepository; + +} diff --git a/src/main/java/umc/spring/service/reviewService/ReviewCommandService.java b/src/main/java/umc/spring/service/reviewService/ReviewCommandService.java new file mode 100644 index 0000000..9838efe --- /dev/null +++ b/src/main/java/umc/spring/service/reviewService/ReviewCommandService.java @@ -0,0 +1,8 @@ +package umc.spring.service.reviewService; + +import umc.spring.domain.Review; +import umc.spring.web.dto.ReviewRequestDto; + +public interface ReviewCommandService { + Review addReview(ReviewRequestDto.ReviewAddDto request); +} diff --git a/src/main/java/umc/spring/service/reviewService/ReviewCommandServiceImpl.java b/src/main/java/umc/spring/service/reviewService/ReviewCommandServiceImpl.java new file mode 100644 index 0000000..958bb30 --- /dev/null +++ b/src/main/java/umc/spring/service/reviewService/ReviewCommandServiceImpl.java @@ -0,0 +1,55 @@ +package umc.spring.service.reviewService; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import umc.spring.apiPayload.code.ErrorStatus; +import umc.spring.apiPayload.exception.handler.MemberHandler; +import umc.spring.apiPayload.exception.handler.StoreHandler; +import umc.spring.apiPayload.exception.handler.TempHandler; +import umc.spring.converter.ReviewConverter; +import umc.spring.domain.Member; +import umc.spring.domain.Review; +import umc.spring.domain.ReviewImage; +import umc.spring.domain.Store; +import umc.spring.repository.MemberRepository; +import umc.spring.repository.ReviewRepository; +import umc.spring.repository.storeRepository.StoreRepository; +import umc.spring.web.dto.ReviewRequestDto; + +import java.util.List; + +@Service +@RequiredArgsConstructor +public class ReviewCommandServiceImpl implements ReviewCommandService{ + + private final StoreRepository storeRepository; + private final MemberRepository memberRepository; + private final ReviewRepository reviewRepository; + + @Override + @Transactional + public Review addReview(ReviewRequestDto.ReviewAddDto request){ + Review review = ReviewConverter.toReview(request); + List urls = request.getImageUrl(); + if (urls != null && !urls.isEmpty()) { + for (String url : urls) { + review.addReviewImage( + ReviewImage.builder() + .imageUrl(url) + .build() + ); + } + } + + Store store = storeRepository.findById(request.getStoreId()) + .orElseThrow(()->new StoreHandler(ErrorStatus.STORE_NOT_FOUND)); + Member member = memberRepository.findById(1L) //하드코딩 + .orElseThrow(()-> new MemberHandler(ErrorStatus.MEMBER_NOT_FOUND)); + + review.setStore(store); + review.setMember(member); + return reviewRepository.save(review); + + } +} diff --git a/src/main/java/umc/spring/service/reviewService/ReviewQueryService.java b/src/main/java/umc/spring/service/reviewService/ReviewQueryService.java new file mode 100644 index 0000000..c904d7f --- /dev/null +++ b/src/main/java/umc/spring/service/reviewService/ReviewQueryService.java @@ -0,0 +1,8 @@ +package umc.spring.service.reviewService; + +import org.springframework.data.domain.Page; +import umc.spring.domain.Review; + +public interface ReviewQueryService { + +} diff --git a/src/main/java/umc/spring/service/reviewService/ReviewQueryServiceImpl.java b/src/main/java/umc/spring/service/reviewService/ReviewQueryServiceImpl.java new file mode 100644 index 0000000..4a63960 --- /dev/null +++ b/src/main/java/umc/spring/service/reviewService/ReviewQueryServiceImpl.java @@ -0,0 +1,20 @@ +package umc.spring.service.reviewService; + +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.stereotype.Service; +import umc.spring.domain.Review; +import umc.spring.domain.Store; +import umc.spring.repository.ReviewRepository; +import umc.spring.repository.storeRepository.StoreRepository; + +@Service +@RequiredArgsConstructor +public class ReviewQueryServiceImpl implements ReviewQueryService { + + private final ReviewRepository reviewRepository; + private final StoreRepository storeRepository; + + +} diff --git a/src/main/java/umc/spring/service/storeService/StoreCommandService.java b/src/main/java/umc/spring/service/storeService/StoreCommandService.java new file mode 100644 index 0000000..0e1a13a --- /dev/null +++ b/src/main/java/umc/spring/service/storeService/StoreCommandService.java @@ -0,0 +1,9 @@ +package umc.spring.service.storeService; + +import umc.spring.domain.Store; +import umc.spring.web.dto.StoreRequestDto; + +public interface StoreCommandService { + public Store addStore(StoreRequestDto.AddStoreToRegionDto request); + +} diff --git a/src/main/java/umc/spring/service/storeService/StoreCommandServiceImpl.java b/src/main/java/umc/spring/service/storeService/StoreCommandServiceImpl.java new file mode 100644 index 0000000..604f586 --- /dev/null +++ b/src/main/java/umc/spring/service/storeService/StoreCommandServiceImpl.java @@ -0,0 +1,31 @@ +package umc.spring.service.storeService; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import umc.spring.apiPayload.code.ErrorStatus; +import umc.spring.apiPayload.exception.handler.RegionHandler; +import umc.spring.converter.StoreConverter; +import umc.spring.domain.Region; +import umc.spring.domain.Store; +import umc.spring.repository.RegionRepository; +import umc.spring.repository.storeRepository.StoreRepository; +import umc.spring.web.dto.StoreRequestDto; + +@Service +@RequiredArgsConstructor +public class StoreCommandServiceImpl implements StoreCommandService { + private final StoreRepository storeRepository; + private final RegionRepository regionRepository; + + @Override + @Transactional + public Store addStore(StoreRequestDto.AddStoreToRegionDto request){ + Store store = StoreConverter.toStore(request); + Region region = regionRepository.findById(request.getRegionId()) + .orElseThrow(()->new RegionHandler(ErrorStatus.REGION_NOT_EXIST)); + region.addStore(store); + return storeRepository.save(store); + } + +} diff --git a/src/main/java/umc/spring/service/storeService/StoreQueryService.java b/src/main/java/umc/spring/service/storeService/StoreQueryService.java new file mode 100644 index 0000000..2e8ce03 --- /dev/null +++ b/src/main/java/umc/spring/service/storeService/StoreQueryService.java @@ -0,0 +1,11 @@ +package umc.spring.service.storeService; + +import org.springframework.data.domain.Page; +import umc.spring.domain.Mission; +import umc.spring.domain.Review; + +public interface StoreQueryService { + Page getReviewList(Long storeId, Integer page); + Page getMissionList(Long storeId, Integer page); + Boolean existById(Long storeId); +} diff --git a/src/main/java/umc/spring/service/storeService/StoreQueryServiceImpl.java b/src/main/java/umc/spring/service/storeService/StoreQueryServiceImpl.java new file mode 100644 index 0000000..b54fdd3 --- /dev/null +++ b/src/main/java/umc/spring/service/storeService/StoreQueryServiceImpl.java @@ -0,0 +1,51 @@ +package umc.spring.service.storeService; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.stereotype.Service; +import umc.spring.apiPayload.code.ErrorStatus; +import umc.spring.apiPayload.exception.handler.StoreHandler; +import umc.spring.domain.Mission; +import umc.spring.domain.Review; +import umc.spring.domain.Store; +import umc.spring.repository.MissionRepository; +import umc.spring.repository.ReviewRepository; +import umc.spring.repository.storeRepository.StoreRepository; + +@Slf4j +@Service +@RequiredArgsConstructor +public class StoreQueryServiceImpl implements StoreQueryService{ + + private final StoreRepository storeRepository; + private final ReviewRepository reviewRepository; + private final MissionRepository missionRepository; + + @Override + public Page getReviewList(Long storeId, Integer page){ + Store store = storeRepository.findById(storeId).get(); + log.info("store:{}",store.getId()); + Page storePage = reviewRepository.findAllByStore(store, PageRequest.of(page,10)); + for (Review review : storePage) { + log.info("review:{}", review); + } + return storePage; + } + @Override + public Page getMissionList(Long storeId, Integer page){ + Store store = storeRepository.findById(storeId).orElseThrow( + () -> new StoreHandler(ErrorStatus.STORE_NOT_FOUND) + ); + + Page missionPage = missionRepository.findAllByStore(store, PageRequest.of(page, 10)); + return missionPage; + } + + @Override + public Boolean existById(Long storeId){ + return storeRepository.existsById(storeId); + } + +} diff --git a/src/main/java/umc/spring/service/tempService/TempQueryService.java b/src/main/java/umc/spring/service/tempService/TempQueryService.java new file mode 100644 index 0000000..08e6f22 --- /dev/null +++ b/src/main/java/umc/spring/service/tempService/TempQueryService.java @@ -0,0 +1,5 @@ +package umc.spring.service.tempService; + +public interface TempQueryService { + void checkFlag(Integer flag); +} diff --git a/src/main/java/umc/spring/service/tempService/TempQueryServiceImpl.java b/src/main/java/umc/spring/service/tempService/TempQueryServiceImpl.java new file mode 100644 index 0000000..eabed36 --- /dev/null +++ b/src/main/java/umc/spring/service/tempService/TempQueryServiceImpl.java @@ -0,0 +1,16 @@ +package umc.spring.service.tempService; + +import org.springframework.stereotype.Service; +import umc.spring.apiPayload.code.ErrorStatus; +import umc.spring.apiPayload.exception.handler.TempHandler; + +@Service +public class TempQueryServiceImpl implements TempQueryService{ + + @Override + public void checkFlag(Integer flag){ + if(flag==1){ + throw new TempHandler(ErrorStatus.TEMP_EXCEPTION); + } + } +} diff --git a/src/main/java/umc/spring/validation/annotation/ExistCategories.java b/src/main/java/umc/spring/validation/annotation/ExistCategories.java new file mode 100644 index 0000000..9eff722 --- /dev/null +++ b/src/main/java/umc/spring/validation/annotation/ExistCategories.java @@ -0,0 +1,17 @@ +package umc.spring.validation.annotation; + +import jakarta.validation.Constraint; +import jakarta.validation.Payload; +import umc.spring.validation.validator.CategoriesExistValidator; + +import java.lang.annotation.*; + +@Documented +@Constraint(validatedBy = CategoriesExistValidator.class) //사용자가 validation 커스텀 +@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER}) +@Retention(RetentionPolicy.RUNTIME) +public @interface ExistCategories { + String message() default "해당하는 카테고리가 존재하지 않습니다."; + Class[] groups() default {}; + Class[] payload() default {}; //메타데이터를 전달할 때 사용 +} diff --git a/src/main/java/umc/spring/validation/annotation/ExistStore.java b/src/main/java/umc/spring/validation/annotation/ExistStore.java new file mode 100644 index 0000000..23300e5 --- /dev/null +++ b/src/main/java/umc/spring/validation/annotation/ExistStore.java @@ -0,0 +1,18 @@ +package umc.spring.validation.annotation; + +import jakarta.validation.Constraint; +import jakarta.validation.Payload; +import umc.spring.validation.validator.StoreExistValidator; +import umc.spring.validation.validator.StoreExistValidator; + +import java.lang.annotation.*; +@Documented +@Constraint(validatedBy = StoreExistValidator.class) +@Target( { ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER }) +@Retention(RetentionPolicy.RUNTIME) +public @interface ExistStore { + + String message() default "해당하는 가게가 존재하지 않습니다."; + Class[] groups() default {}; + Class[] payload() default {}; +} \ No newline at end of file diff --git a/src/main/java/umc/spring/validation/annotation/NotAlreadyChallenged.java b/src/main/java/umc/spring/validation/annotation/NotAlreadyChallenged.java new file mode 100644 index 0000000..a971009 --- /dev/null +++ b/src/main/java/umc/spring/validation/annotation/NotAlreadyChallenged.java @@ -0,0 +1,17 @@ +package umc.spring.validation.annotation; + +import jakarta.validation.Constraint; +import jakarta.validation.Payload; +import umc.spring.validation.validator.AlreadyChallengedValidator; + +import java.lang.annotation.*; + +@Documented +@Constraint(validatedBy = AlreadyChallengedValidator.class) +@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE}) +@Retention(value = RetentionPolicy.RUNTIME) +public @interface NotAlreadyChallenged { + String message() default "이미 진행중인 미션입니다."; + Class[] groups() default {}; + Class[] payload() default {}; //메타데이터를 전달할 때 사용 +} diff --git a/src/main/java/umc/spring/validation/annotation/ValidPage.java b/src/main/java/umc/spring/validation/annotation/ValidPage.java new file mode 100644 index 0000000..6cef110 --- /dev/null +++ b/src/main/java/umc/spring/validation/annotation/ValidPage.java @@ -0,0 +1,19 @@ +package umc.spring.validation.annotation; + +import jakarta.validation.Constraint; +import jakarta.validation.Payload; +import umc.spring.validation.validator.AlreadyChallengedValidator; +import umc.spring.validation.validator.ValidPageValidator; + +import java.lang.annotation.*; + +@Documented +@Constraint(validatedBy = ValidPageValidator.class) +@Target({ElementType.METHOD, ElementType.PARAMETER}) +@Retention(value = RetentionPolicy.RUNTIME) +public @interface ValidPage { + String message() default "유효하지 않은 페이지입니다."; + Class[] groups() default {}; + Class[] payload() default {}; + +} diff --git a/src/main/java/umc/spring/validation/validator/AlreadyChallengedValidator.java b/src/main/java/umc/spring/validation/validator/AlreadyChallengedValidator.java new file mode 100644 index 0000000..6f98bb6 --- /dev/null +++ b/src/main/java/umc/spring/validation/validator/AlreadyChallengedValidator.java @@ -0,0 +1,37 @@ +package umc.spring.validation.validator; + +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import umc.spring.apiPayload.code.ErrorStatus; +import umc.spring.domain.mapping.MemberMission; +import umc.spring.repository.MemberMissionRepository; +import umc.spring.service.memberMissionService.MemberMissionCommandService; +import umc.spring.validation.annotation.NotAlreadyChallenged; +import umc.spring.web.dto.MissionRequestDto; + +@Slf4j +@Component +@RequiredArgsConstructor +public class AlreadyChallengedValidator implements ConstraintValidator { + + private final MemberMissionCommandService memberMissionCommandService; + + @Override + public void initialize(NotAlreadyChallenged constraintAnnotation) { + ConstraintValidator.super.initialize(constraintAnnotation); + } + + @Override + public boolean isValid(MissionRequestDto.ChallengeMissionDto request, ConstraintValidatorContext context) { + + boolean isValid = !(memberMissionCommandService.existsByMemberIdAndMissionId(request.getMemberId(), request.getMissionId())); + if(!isValid){ + context.disableDefaultConstraintViolation(); + context.buildConstraintViolationWithTemplate(ErrorStatus.MISSION_ALREADY_CHALLENGED.toString()).addConstraintViolation(); + } + return isValid; + } +} diff --git a/src/main/java/umc/spring/validation/validator/CategoriesExistValidator.java b/src/main/java/umc/spring/validation/validator/CategoriesExistValidator.java new file mode 100644 index 0000000..414a7ac --- /dev/null +++ b/src/main/java/umc/spring/validation/validator/CategoriesExistValidator.java @@ -0,0 +1,34 @@ +package umc.spring.validation.validator; + +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import umc.spring.apiPayload.code.ErrorStatus; +import umc.spring.service.memberService.FoodCategoryService; +import umc.spring.validation.annotation.ExistCategories; + +import java.util.List; + +@Component +@RequiredArgsConstructor +public class CategoriesExistValidator implements ConstraintValidator> { + + private final FoodCategoryService foodCategoryService; + + @Override + public void initialize(ExistCategories constraintAnnotation) { + ConstraintValidator.super.initialize(constraintAnnotation); + } + + @Override + public boolean isValid(List values, ConstraintValidatorContext context) { + boolean isValid = values.stream().allMatch(value -> + foodCategoryService.existById(value)); + if(!isValid){ + context.disableDefaultConstraintViolation(); + context.buildConstraintViolationWithTemplate(ErrorStatus.FOOD_CATEGORY_NOT_FOUND.toString()).addConstraintViolation(); + } + return isValid; + } +} diff --git a/src/main/java/umc/spring/validation/validator/StoreExistValidator.java b/src/main/java/umc/spring/validation/validator/StoreExistValidator.java new file mode 100644 index 0000000..65cf57b --- /dev/null +++ b/src/main/java/umc/spring/validation/validator/StoreExistValidator.java @@ -0,0 +1,38 @@ +package umc.spring.validation.validator; + +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import umc.spring.repository.storeRepository.StoreRepository; +import umc.spring.service.storeService.StoreCommandService; +import umc.spring.service.storeService.StoreQueryService; +import umc.spring.validation.annotation.ExistStore; +import umc.spring.apiPayload.code.ErrorStatus; +import umc.spring.service.storeService.StoreCommandService; +import umc.spring.validation.annotation.ExistStore; + +@Component +@RequiredArgsConstructor +public class StoreExistValidator implements ConstraintValidator { + + private final StoreQueryService storeQueryService; + + @Override + public void initialize(ExistStore constraintAnnotation) { + ConstraintValidator.super.initialize(constraintAnnotation); + } + + @Override + public boolean isValid(Long storeId, ConstraintValidatorContext context) { + + boolean isValid = storeQueryService.existById(storeId); + + if (!isValid) { + context.disableDefaultConstraintViolation(); + context.buildConstraintViolationWithTemplate(ErrorStatus.STORE_NOT_FOUND.toString()).addConstraintViolation(); + } + + return isValid; + } +} diff --git a/src/main/java/umc/spring/validation/validator/ValidPageValidator.java b/src/main/java/umc/spring/validation/validator/ValidPageValidator.java new file mode 100644 index 0000000..18145cc --- /dev/null +++ b/src/main/java/umc/spring/validation/validator/ValidPageValidator.java @@ -0,0 +1,24 @@ +package umc.spring.validation.validator; + +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import umc.spring.apiPayload.code.ErrorStatus; +import umc.spring.validation.annotation.ValidPage; + +public class ValidPageValidator implements ConstraintValidator { + @Override + public void initialize(ValidPage constraintAnnotation) { + ConstraintValidator.super.initialize(constraintAnnotation); + } + + @Override + public boolean isValid(Integer page, ConstraintValidatorContext context) { + if(page > 0) + return true; + else{ + context.disableDefaultConstraintViolation(); + context.buildConstraintViolationWithTemplate(ErrorStatus.PAGE_NOT_EXIST.toString()).addConstraintViolation(); + } + return false; + } +} diff --git a/src/main/java/umc/spring/web/controller/MemberRestController.java b/src/main/java/umc/spring/web/controller/MemberRestController.java new file mode 100644 index 0000000..08ad585 --- /dev/null +++ b/src/main/java/umc/spring/web/controller/MemberRestController.java @@ -0,0 +1,99 @@ +package umc.spring.web.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import umc.spring.apiPayload.ApiResponse; +import umc.spring.apiPayload.code.ErrorStatus; +import umc.spring.converter.MemberConverter; +import umc.spring.converter.MemberMissionConverter; +import umc.spring.domain.Member; +import umc.spring.domain.Review; +import umc.spring.domain.enums.MissionStatus; +import umc.spring.domain.mapping.MemberMission; +import umc.spring.service.memberMissionService.MemberMissionQueryService; +import umc.spring.service.memberService.MemberCommandService; +import umc.spring.service.memberService.MemberQueryService; +import umc.spring.validation.annotation.ValidPage; +import umc.spring.web.dto.MemberRequestDto; +import umc.spring.web.dto.MemberResponseDto; + +@RestController +@Validated +@RequiredArgsConstructor +@RequestMapping("/members") +public class MemberRestController { + private final MemberCommandService memberCommandService; + private final MemberQueryService memberQueryService; + private final MemberMissionQueryService memberMissionQueryService; + + @PostMapping("/join") + @Operation(summary = "유저 회원가입 API",description = "유저가 회원가입하는 API입니다.") + public ApiResponse join(@RequestBody @Valid MemberRequestDto.JoinDto request){ + Member member = memberCommandService.joinMember(request); + return ApiResponse.onSuccess(MemberConverter.toJoinResultDTO(member)); + } + + @PostMapping("/login") + @Operation(summary = "유저 로그인 API",description = "유저가 로그인하는 API입니다.") + public ApiResponse login(@RequestBody @Valid MemberRequestDto.LoginRequestDto request) { + return ApiResponse.onSuccess(memberCommandService.loginMember(request)); + } + + @GetMapping("/info") + @Operation(summary = "유저 내 정보 조회 API - 인증 필요", + description = "유저가 내 정보를 조회하는 API입니다.", + security = { @SecurityRequirement(name = "JWT TOKEN") } + ) + public ApiResponse getMyInfo(HttpServletRequest request) { + return ApiResponse.onSuccess(memberQueryService.getMemberInfo(request)); + } + + @GetMapping("{memberId}/reviews") + @Operation(summary = "특정 유저의 리뷰 목록 조회 API", description = "특정 유저의 리뷰들의 목록을 조회하는 API이며, 페이징을 포함합니다. query String 으로 page 번호를 주세요") + @ApiResponses({ + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공"), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH003", description = "access 토큰을 주세요", content = @Content(schema = @Schema(implementation = ApiResponse.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH004", description = "access 토큰 만료", content = @Content(schema = @Schema(implementation = ApiResponse.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "MEMBER4001", description = "사용자가 존재하지 않습니다", content = @Content(schema = @Schema(implementation = ApiResponse.class))) + }) + @Parameters({ + @Parameter(name = "memberId", description = "멤버의 아이디, path variable") + }) + public ApiResponse getReviewList(@PathVariable(name = "memberId") Long memberId, + @ValidPage @RequestParam(name = "page") Integer page){ + Page reviewList = memberQueryService.getReviewList(memberId, page-1); + return ApiResponse.onSuccess(MemberConverter.reviewPreviewListDto(reviewList)); + } + + @GetMapping("{memberId}/missions")@Operation(summary = "내가 진행중인 미션 목록 조회 API", description = "특정 유저의 진행중인 미션 목록을 조회하는 API이며, 페이징을 포함합니다. query String 으로 page 번호와 미션 status를 주세요") + @ApiResponses({ + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공"), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH003", description = "access 토큰을 주세요", content = @Content(schema = @Schema(implementation = ApiResponse.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH004", description = "access 토큰 만료", content = @Content(schema = @Schema(implementation = ApiResponse.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "MEMBER4001", description = "사용자가 존재하지 않습니다", content = @Content(schema = @Schema(implementation = ApiResponse.class))) + }) + @Parameters({ + @Parameter(name = "memberId", description = "멤버의 아이디, path variable", in = ParameterIn.PATH), + @Parameter(name="page", description = "페이지 번호(1부터 시작)", in = ParameterIn.QUERY), + @Parameter(name="status", description = "미션 상태", in = ParameterIn.QUERY) + }) + public ApiResponse getMissionList( + @PathVariable(name = "memberId") Long memberId, @ValidPage @RequestParam(name = "page") Integer page + , @RequestParam(name = "status") MissionStatus status){ + + Page memberMissionList = memberMissionQueryService.getMissionPage(memberId, status, page-1); + return ApiResponse.onSuccess(MemberMissionConverter.missionPreviewListDto(memberMissionList)); + } +} diff --git a/src/main/java/umc/spring/web/controller/MemberViewController.java b/src/main/java/umc/spring/web/controller/MemberViewController.java new file mode 100644 index 0000000..62936c6 --- /dev/null +++ b/src/main/java/umc/spring/web/controller/MemberViewController.java @@ -0,0 +1,61 @@ +package umc.spring.web.controller; + + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PostMapping; +import umc.spring.service.memberService.MemberCommandService; +import umc.spring.web.dto.MemberRequestDto; +; + +@Controller +@RequiredArgsConstructor + +public class MemberViewController { + + private final MemberCommandService memberCommandService; + + // thymeleaf 사용을 위해 일부가 변경되었습니다. + // 실제로는 8주차에서 작성한 컨트롤러와 동일하게 작성하시면 됩니다!! + @GetMapping("/signup") + public String signupPage(Model model) { + model.addAttribute("memberJoinDto", new MemberRequestDto.JoinDto()); + return "signup"; + } + @GetMapping("/login") + public String loginPage() { + return "login"; + } + + @PostMapping("/members/signup") + public String joinMember(@ModelAttribute("memberJoinDto") MemberRequestDto.JoinDto request, // 협업시에는 기존 RequestBody 어노테이션을 붙여주시면 됩니다! + BindingResult bindingResult, + Model model) { + if (bindingResult.hasErrors()) { + // 뷰에 데이터 바인딩이 실패할 경우 signup 페이지를 유지합니다. + return "signup"; + } + + try { + memberCommandService.joinMember(request); + return "redirect:/login"; + } catch (Exception e) { + // 회원가입 과정에서 에러가 발생할 경우 에러 메시지를 보내고, signup 페이디를 유지합니다. + model.addAttribute("error", e.getMessage()); + return "signup"; + } + } + @GetMapping("/home") + public String home() { + return "home"; + } + + @GetMapping("/admin") + public String admin() { + return "admin"; + } +} diff --git a/src/main/java/umc/spring/web/controller/MissionRestController.java b/src/main/java/umc/spring/web/controller/MissionRestController.java new file mode 100644 index 0000000..878ec20 --- /dev/null +++ b/src/main/java/umc/spring/web/controller/MissionRestController.java @@ -0,0 +1,65 @@ +package umc.spring.web.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.web.bind.annotation.*; +import umc.spring.apiPayload.ApiResponse; +import umc.spring.converter.MemberMissionConverter; +import umc.spring.converter.MissionConverter; +import umc.spring.domain.Mission; +import umc.spring.domain.enums.MissionStatus; +import umc.spring.domain.mapping.MemberMission; +import umc.spring.service.memberMissionService.MemberMissionCommandService; +import umc.spring.service.missionService.MissionCommandService; +import umc.spring.validation.annotation.ValidPage; +import umc.spring.web.dto.MemberResponseDto; +import umc.spring.web.dto.MissionRequestDto; +import umc.spring.web.dto.MissionResponseDto; + +@RestController +@RequestMapping("/missions") +@RequiredArgsConstructor +public class MissionRestController { + + private final MissionCommandService missionCommandService; + private final MemberMissionCommandService memberMissionCommandService; + + @PostMapping("/add") + public ApiResponse addMission(@RequestBody @Valid MissionRequestDto.AddMissionToStoreDto request){ + Mission mission = missionCommandService.addMission(request); + return ApiResponse.onSuccess(MissionConverter.toAddMissionToStoreResultDto(mission)); + } + + @PostMapping("/challenge") + public ApiResponse + challangeMission(@RequestBody @Valid MissionRequestDto.ChallengeMissionDto request){ + MemberMission memberMission = memberMissionCommandService.challengeMission(request); + return ApiResponse.onSuccess(MemberMissionConverter.toChallengeMissionResultDto(memberMission)); + } + + @PatchMapping("/complete")@Operation(summary = "진행중인 미션 진행 완료로 변경 API", description = "진행중인 미션을 진행 완료로 변경하는 API입니다.") + @ApiResponses({ + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공"), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH003", description = "access 토큰을 주세요", content = @Content(schema = @Schema(implementation = ApiResponse.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH004", description = "access 토큰 만료", content = @Content(schema = @Schema(implementation = ApiResponse.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "MISSION4001", description = "해당 미션이 존재하지 않습니다", content = @Content(schema = @Schema(implementation = ApiResponse.class))) + }) + @io.swagger.v3.oas.annotations.parameters.RequestBody( + description = "미션 완료 요청 바디", + required = true, + content = @Content(schema = @Schema(implementation = MissionRequestDto.CompleteMissionDto.class)) + ) + public ApiResponse setMissionComplete(@RequestBody @Valid MissionRequestDto.CompleteMissionDto request){ + + MemberMission memberMission = memberMissionCommandService.setMissionComplete(request.getMissionId(), request.getMemberId()); + return ApiResponse.onSuccess(MemberMissionConverter.completeMissionDto(memberMission)); + } +} diff --git a/src/main/java/umc/spring/web/controller/ReviewRestController.java b/src/main/java/umc/spring/web/controller/ReviewRestController.java new file mode 100644 index 0000000..8986603 --- /dev/null +++ b/src/main/java/umc/spring/web/controller/ReviewRestController.java @@ -0,0 +1,35 @@ +package umc.spring.web.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.web.bind.annotation.*; +import umc.spring.apiPayload.ApiResponse; +import umc.spring.converter.ReviewConverter; +import umc.spring.domain.Review; +import umc.spring.service.reviewService.ReviewCommandService; +import umc.spring.service.reviewService.ReviewQueryService; +import umc.spring.web.dto.ReviewRequestDto; +import umc.spring.web.dto.ReviewResponseDto; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/reviews") +public class ReviewRestController { + private final ReviewCommandService reviewCommandService; + private final ReviewQueryService reviewQueryService; + + @PostMapping("/add") + public ApiResponse AddReview(@RequestBody @Valid ReviewRequestDto.ReviewAddDto request){ + Review review = reviewCommandService.addReview(request); + return ApiResponse.onSuccess(ReviewConverter.toReviewAddResultDto(review)); + } + + +} diff --git a/src/main/java/umc/spring/web/controller/StoreRestController.java b/src/main/java/umc/spring/web/controller/StoreRestController.java new file mode 100644 index 0000000..5d345c9 --- /dev/null +++ b/src/main/java/umc/spring/web/controller/StoreRestController.java @@ -0,0 +1,79 @@ +package umc.spring.web.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import umc.spring.apiPayload.ApiResponse; +import umc.spring.converter.MemberConverter; +import umc.spring.converter.MissionConverter; +import umc.spring.converter.ReviewConverter; +import umc.spring.converter.StoreConverter; +import umc.spring.domain.Member; +import umc.spring.domain.Mission; +import umc.spring.domain.Review; +import umc.spring.domain.Store; +import umc.spring.service.storeService.StoreCommandService; +import umc.spring.service.storeService.StoreQueryService; +import umc.spring.validation.annotation.ExistStore; +import umc.spring.validation.annotation.ValidPage; +import umc.spring.web.dto.*; + +@RestController +@Validated +@RequestMapping("/stores") +@RequiredArgsConstructor +public class StoreRestController { + + private final StoreCommandService storeCommandService; + private final StoreQueryService storeQueryService; + + @PostMapping("/add") + public ApiResponse AddStore(@RequestBody @Valid StoreRequestDto.AddStoreToRegionDto request){ + Store store = storeCommandService.addStore(request); + return ApiResponse.onSuccess(StoreConverter.toAddStoreToRegionResultDto(store)); + } + + @GetMapping("/{storeId}/reviews") + @Operation(summary = "특정 가게의 리뷰 목록 조회 API", description = "특정 가게의 리뷰들의 목록을 조회하는 API이며, " + + "페이징을 포함합니다. query String으로 page 번호를 주세요") + @ApiResponses({ + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공"), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH003", description = "access 토큰을 주세요", content = @Content(schema = @Schema(implementation = ApiResponse.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH004", description = "access 토큰 만료", content = @Content(schema = @Schema(implementation = ApiResponse.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH006", description = "access 토큰 모양이 이상함", content = @Content(schema = @Schema(implementation = ApiResponse.class))) + }) + @Parameters({ + @Parameter(name = "storeId", description = "가게의 아이디, path variable입니다!") + }) + public ApiResponse getReviewList( + @PathVariable(name = "storeId") Long storeId, @RequestParam(name = "page") Integer page){ + + Page reviewList = storeQueryService.getReviewList(storeId, page); + return ApiResponse.onSuccess(ReviewConverter.reviewPreviewListDto(reviewList)); + } + + @GetMapping("{storeId}/missions") + @Operation(summary = "특정 가게 미션 목록 조회 API", description = "특정 가게의 미션의 목록을 조회하는 API이며, 페이징을 포함합니다. query String 으로 page 번호를 주세요") + @ApiResponses({ + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공"), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH003", description = "access 토큰을 주세요", content = @Content(schema = @Schema(implementation = ApiResponse.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH004", description = "access 토큰 만료", content = @Content(schema = @Schema(implementation = ApiResponse.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "STORE4001", description = "가게가 존재하지 않습니다", content = @Content(schema = @Schema(implementation = ApiResponse.class))) + }) + @Parameters({ + @Parameter(name = "storeId", description = "가게의 아이디, path variable") + }) + public ApiResponse getMissionList( + @ExistStore @PathVariable(name = "storeId") Long storeId, @ValidPage @RequestParam(name = "page") Integer page){ + Page missionList = storeQueryService.getMissionList(storeId, page-1); + return ApiResponse.onSuccess(MissionConverter.missionPreviewListDto(missionList)); + } +} diff --git a/src/main/java/umc/spring/web/controller/TempRestController.java b/src/main/java/umc/spring/web/controller/TempRestController.java new file mode 100644 index 0000000..5c62be9 --- /dev/null +++ b/src/main/java/umc/spring/web/controller/TempRestController.java @@ -0,0 +1,30 @@ +package umc.spring.web.controller; + +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import umc.spring.apiPayload.ApiResponse; +import umc.spring.converter.TempConverter; +import umc.spring.service.tempService.TempQueryService; +import umc.spring.web.dto.TempResponse; + +@RestController +@RequestMapping("/temp") +@RequiredArgsConstructor +public class TempRestController { + + private final TempQueryService tempQueryService; + + @GetMapping("/test") + public ApiResponse testAPI(){ + return ApiResponse.onSuccess(TempConverter.toTempTestDTO()); + } + + @GetMapping("/exception") + public ApiResponse exceptionAPI(@RequestParam("flag") Integer flag){ + tempQueryService.checkFlag(flag); + return ApiResponse.onSuccess(TempConverter.toTempExceptionDTO(flag)); + } +} diff --git a/src/main/java/umc/spring/web/dto/MemberRequestDto.java b/src/main/java/umc/spring/web/dto/MemberRequestDto.java new file mode 100644 index 0000000..c145d06 --- /dev/null +++ b/src/main/java/umc/spring/web/dto/MemberRequestDto.java @@ -0,0 +1,50 @@ +package umc.spring.web.dto; + +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.Getter; +import lombok.Setter; +import umc.spring.domain.enums.Gender; +import umc.spring.domain.enums.Role; +import umc.spring.validation.annotation.ExistCategories; + +import java.time.LocalDate; +import java.util.List; + +public class MemberRequestDto { + @Getter + @Setter + public static class JoinDto{ + @NotBlank + String name; + @NotNull + Integer gender; + @NotNull + LocalDate birth; + @Size(min = 5, max = 12) + String address; + @Email + String email; + @NotBlank + String password; + @Size(max = 15) + String phoneNumber; + @ExistCategories + List preferCategory; + @NotNull + Role role; + } + + @Getter + @Setter + public static class LoginRequestDto{ + @NotBlank(message = "이메일은 필수입니다.") + @Email(message = "올바른 이메일 형식이어야 합니다.") + private String email; + + @NotBlank(message = "패스워드는 필수입니다.") + private String password; + } +} diff --git a/src/main/java/umc/spring/web/dto/MemberResponseDto.java b/src/main/java/umc/spring/web/dto/MemberResponseDto.java new file mode 100644 index 0000000..f84dfee --- /dev/null +++ b/src/main/java/umc/spring/web/dto/MemberResponseDto.java @@ -0,0 +1,84 @@ +package umc.spring.web.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import umc.spring.domain.enums.Gender; +import umc.spring.domain.enums.MissionStatus; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.List; + +public class MemberResponseDto { + @Builder + @Getter + @NoArgsConstructor + @AllArgsConstructor + public static class JoinResultDTO{ + Long memberId; + LocalDateTime createdAt; + } + + @Builder + @Getter + @AllArgsConstructor + public static class ReviewPreviewListDto{ + List reviewList; + Integer listSize; + Integer totalPage; + Long totalElements; + Boolean isFirst; + Boolean isLast; + } + + @Builder + @Getter + @AllArgsConstructor + public static class ReviewPreviewDto{ + String username; + String content; + Float score; + LocalDate createdAt; + } + + @Builder + @Getter + @AllArgsConstructor + public static class MissionPreviewListDto{ + List missionList; + Integer listSize; + Integer totalPage; + Long totalElements; + Boolean isFirst; + Boolean isLast; + } + + @Builder + @Getter + @AllArgsConstructor + public static class MissionPreviewDto{ + String description; + MissionStatus status; + Integer point; + LocalDate deadline; + } + + @Builder + @Getter + @AllArgsConstructor + public static class LoginResultDto{ + Long memberId; + String accessToken; + } + @Builder + @Getter + @AllArgsConstructor + public static class MemberInfoDto{ + String name; + String email; + Gender gender; + } + +} diff --git a/src/main/java/umc/spring/web/dto/MissionRequestDto.java b/src/main/java/umc/spring/web/dto/MissionRequestDto.java new file mode 100644 index 0000000..ebbd0cc --- /dev/null +++ b/src/main/java/umc/spring/web/dto/MissionRequestDto.java @@ -0,0 +1,41 @@ +package umc.spring.web.dto; + +import jakarta.validation.constraints.Future; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Getter; +import umc.spring.validation.annotation.NotAlreadyChallenged; + +import java.time.LocalDate; + +public class MissionRequestDto { + + @Getter + public static class AddMissionToStoreDto{ + @NotNull + Long storeId; + @NotBlank(message = "설명을 입력해주세요.") + String description; + @NotNull(message = "포인트를 입력해주세요") + Integer point; + @Future + LocalDate deadline; + } + + @Getter + @NotAlreadyChallenged + public static class ChallengeMissionDto{ + @NotNull + Long missionId; + @NotNull + Long memberId; + } + + @Getter + public static class CompleteMissionDto{ + @NotNull + Long memberId; + @NotNull + Long missionId; + } +} diff --git a/src/main/java/umc/spring/web/dto/MissionResponseDto.java b/src/main/java/umc/spring/web/dto/MissionResponseDto.java new file mode 100644 index 0000000..f7045c3 --- /dev/null +++ b/src/main/java/umc/spring/web/dto/MissionResponseDto.java @@ -0,0 +1,41 @@ +package umc.spring.web.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import umc.spring.domain.enums.MissionStatus; + +import java.time.LocalDateTime; + +public class MissionResponseDto { + + @AllArgsConstructor + @Getter + @Builder + public static class AddMissionToStoreResultDto{ + Long missionId; + Long storeId; + LocalDateTime createdAt; + } + + @AllArgsConstructor + @Getter + @Builder + public static class ChallengeMissionResultDto{ + Long missionId; + Long memberId; + MissionStatus status; + LocalDateTime createdAt; + } + + @AllArgsConstructor + @Getter + @Builder + public static class CompleteMissionResultDto{ + MissionStatus status; + String storeName; + String description; + Integer point; + LocalDateTime completedAt; + } +} diff --git a/src/main/java/umc/spring/web/dto/ReviewRequestDto.java b/src/main/java/umc/spring/web/dto/ReviewRequestDto.java new file mode 100644 index 0000000..5f42f0c --- /dev/null +++ b/src/main/java/umc/spring/web/dto/ReviewRequestDto.java @@ -0,0 +1,23 @@ +package umc.spring.web.dto; + +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Getter; + +import java.util.List; + +public class ReviewRequestDto { + @Getter + public static class ReviewAddDto{ + @NotBlank + String content; + @Max(5) @Min(0) + Float score; + @NotNull + Long storeId; + + List imageUrl; + } +} diff --git a/src/main/java/umc/spring/web/dto/ReviewResponseDto.java b/src/main/java/umc/spring/web/dto/ReviewResponseDto.java new file mode 100644 index 0000000..1b31a19 --- /dev/null +++ b/src/main/java/umc/spring/web/dto/ReviewResponseDto.java @@ -0,0 +1,25 @@ +package umc.spring.web.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.List; + +public class ReviewResponseDto { + + @Getter + @Builder + @AllArgsConstructor + public static class ReviewAddResultDto{ + Long reviewId; + LocalDateTime createdAt; + } + + + + + +} diff --git a/src/main/java/umc/spring/web/dto/StoreRequestDto.java b/src/main/java/umc/spring/web/dto/StoreRequestDto.java new file mode 100644 index 0000000..52effe5 --- /dev/null +++ b/src/main/java/umc/spring/web/dto/StoreRequestDto.java @@ -0,0 +1,17 @@ +package umc.spring.web.dto; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Getter; + +public class StoreRequestDto { + @Getter + public static class AddStoreToRegionDto{ + @NotNull + String name; + @NotBlank + String address; + @NotNull + Long regionId; + } +} diff --git a/src/main/java/umc/spring/web/dto/StoreResponseDto.java b/src/main/java/umc/spring/web/dto/StoreResponseDto.java new file mode 100644 index 0000000..6778eb8 --- /dev/null +++ b/src/main/java/umc/spring/web/dto/StoreResponseDto.java @@ -0,0 +1,62 @@ +package umc.spring.web.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.List; + +public class StoreResponseDto { + @Getter + @Builder + @AllArgsConstructor + public static class AddStoreToRegionResultDto{ + Long storeId; + LocalDateTime createdAt; + } + + @Builder + @Getter + @AllArgsConstructor + public static class ReviewPreviewListDto{ + List reviewList; + Integer listSize; + Integer totalPage; + Long totalElements; + Boolean isFirst; + Boolean isLast; + } + + @Builder + @Getter + @AllArgsConstructor + public static class ReviewPreviewDto{ + String ownerNickname; + Float score; + String content; + LocalDate createdAt; + } + + @Builder + @Getter + @AllArgsConstructor + public static class MissionPreviewListDto{ + List missionList; + Integer listSize; + Integer totalPage; + Long totalElements; + Boolean isFirst; + Boolean isLast; + } + + @Builder + @Getter + @AllArgsConstructor + public static class MissionPreviewDto{ + String description; + Integer point; + LocalDate deadline; + } +} diff --git a/src/main/java/umc/spring/web/dto/TempRequest.java b/src/main/java/umc/spring/web/dto/TempRequest.java new file mode 100644 index 0000000..6978f93 --- /dev/null +++ b/src/main/java/umc/spring/web/dto/TempRequest.java @@ -0,0 +1,4 @@ +package umc.spring.web.dto; + +public class TempRequest { +} diff --git a/src/main/java/umc/spring/web/dto/TempResponse.java b/src/main/java/umc/spring/web/dto/TempResponse.java new file mode 100644 index 0000000..563770d --- /dev/null +++ b/src/main/java/umc/spring/web/dto/TempResponse.java @@ -0,0 +1,24 @@ +package umc.spring.web.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +public class TempResponse { + @Builder + @Getter + @NoArgsConstructor + @AllArgsConstructor + public static class TempTestDTO{ + String testString; + } + + @Builder + @Getter + @NoArgsConstructor + @AllArgsConstructor + public static class TempExceptionDTO{ + Integer flag; + } +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 0000000..f6af816 --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,28 @@ + + +spring: + datasource: + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://localhost:3306/mission_db + username: root + password: 3mirsammoolsom* + application: + name:sping + sql: + init: + mode:always + jpa: + properties: + hibernate: + dialect: org.hibernate.dialect.MySQL8Dialect + show_sql: true + format_sql: true + use_sql_comments: true + hbm2ddl.auto: update + default_batch_fetch_size: 1000 + +jwt: + token: + secretKey: umceightfightingjwttokenauthentication + expiration: + access: 14400000 \ No newline at end of file diff --git a/src/main/resources/templates/admin.html b/src/main/resources/templates/admin.html new file mode 100644 index 0000000..c7066e5 --- /dev/null +++ b/src/main/resources/templates/admin.html @@ -0,0 +1,10 @@ +Add commentMore actions + + + Admin Page + + +

Admin Page

+

관리자만 접근할 수 있는 페이지입니다.

+ + \ No newline at end of file diff --git a/src/main/resources/templates/home.html b/src/main/resources/templates/home.html new file mode 100644 index 0000000..8c10cb1 --- /dev/null +++ b/src/main/resources/templates/home.html @@ -0,0 +1,20 @@ + + + + Home + + +

Welcome to Home Page!

+ +

+ + + + + +
+ +
+ \ No newline at end of file diff --git a/src/main/resources/templates/login.html b/src/main/resources/templates/login.html new file mode 100644 index 0000000..7804a3a --- /dev/null +++ b/src/main/resources/templates/login.html @@ -0,0 +1,26 @@ + + + + Login + + +

Login

+
+
+ + +
+
+ + +
+ +
+ +

사용자 이름 또는 비밀번호가 잘못되었습니다.

+

로그아웃되었습니다.

+ + +

계정이 없나요? Sign up

+ + \ No newline at end of file diff --git a/src/main/resources/templates/signup.html b/src/main/resources/templates/signup.html new file mode 100644 index 0000000..856134b --- /dev/null +++ b/src/main/resources/templates/signup.html @@ -0,0 +1,70 @@ + + + + 회원가입 + + +

회원가입

+
+
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ + + +
+
+ +
+ + +
+ + +
+ + diff --git a/src/test/java/umc/spring/ApplicationTests.java b/src/test/java/umc/spring/ApplicationTests.java new file mode 100644 index 0000000..dc36e6c --- /dev/null +++ b/src/test/java/umc/spring/ApplicationTests.java @@ -0,0 +1,13 @@ +package umc.spring; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class ApplicationTests { + + @Test + void contextLoads() { + } + +}