diff --git a/rest-controller-practice/.gitignore b/rest-controller-practice/.gitignore new file mode 100644 index 00000000..c2065bc2 --- /dev/null +++ b/rest-controller-practice/.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/rest-controller-practice/build.gradle b/rest-controller-practice/build.gradle new file mode 100644 index 00000000..1ade5743 --- /dev/null +++ b/rest-controller-practice/build.gradle @@ -0,0 +1,58 @@ +plugins { + id 'org.springframework.boot' version '2.4.2' + id 'io.spring.dependency-management' version '1.0.11.RELEASE' + id 'org.asciidoctor.convert' version '1.5.8' + id 'java' +} + +group = 'com.example.jpa' +version = '0.0.1-SNAPSHOT' +sourceCompatibility = '1.8' + +configurations { + compileOnly { + extendsFrom annotationProcessor + } +} + +repositories { + mavenCentral() +} + +ext { + set('snippetsDir', file("build/generated-snippets")) +} + +dependencies { + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + implementation 'org.springframework.boot:spring-boot-starter-security' + implementation 'org.springframework.boot:spring-boot-starter-web' + + implementation("org.springframework.boot:spring-boot-starter-validation") + + + // https://mvnrepository.com/artifact/com.auth0/java-jwt + implementation group: 'com.auth0', name: 'java-jwt', version: '3.13.0' + // https://mvnrepository.com/artifact/org.qlrm/qlrm + implementation group: 'org.qlrm', name: 'qlrm', version: '2.1.1' + + + + compileOnly 'org.projectlombok:lombok' + runtimeOnly 'com.h2database:h2' + annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor' + annotationProcessor 'org.projectlombok:lombok' + testImplementation 'org.springframework.boot:spring-boot-starter-test' + testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc' + testImplementation 'org.springframework.security:spring-security-test' +} + +test { + outputs.dir snippetsDir + useJUnitPlatform() +} + +asciidoctor { + inputs.dir snippetsDir + dependsOn test +} diff --git a/rest-controller-practice/gradle/wrapper/gradle-wrapper.jar b/rest-controller-practice/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..e708b1c0 Binary files /dev/null and b/rest-controller-practice/gradle/wrapper/gradle-wrapper.jar differ diff --git a/rest-controller-practice/gradle/wrapper/gradle-wrapper.properties b/rest-controller-practice/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..2a563242 --- /dev/null +++ b/rest-controller-practice/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.2-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/rest-controller-practice/gradlew b/rest-controller-practice/gradlew new file mode 100644 index 00000000..8f890474 --- /dev/null +++ b/rest-controller-practice/gradlew @@ -0,0 +1,184 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ]; do + ls=$(ls -ld "$PRG") + link=$(expr "$ls" : '.*-> \(.*\)$') + if expr "$link" : '/.*' >/dev/null; then + PRG="$link" + else + PRG=$(dirname "$PRG")"/$link" + fi +done +SAVED="$(pwd)" +cd "$(dirname \"$PRG\")/" >/dev/null +APP_HOME="$(pwd -P)" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=$(basename "$0") + +# 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"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn() { + echo "$*" +} + +die() { + echo + echo "$*" + echo + exit 1 +} + +# 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 + ;; +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" + which java >/dev/null 2>&1 || 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 + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ]; then + MAX_FD_LIMIT=$(ulimit -H -n) + if [ $? -eq 0 ]; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ]; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ]; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ]; then + APP_HOME=$(cygpath --path --mixed "$APP_HOME") + CLASSPATH=$(cygpath --path --mixed "$CLASSPATH") + + JAVACMD=$(cygpath --unix "$JAVACMD") + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=$(find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null) + SEP="" + for dir in $ROOTDIRSRAW; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ]; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@"; do + CHECK=$(echo "$arg" | egrep -c "$OURCYGPATTERN" -) + CHECK2=$(echo "$arg" | egrep -c "^-") ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ]; then ### Added a condition + eval $(echo args$i)=$(cygpath --path --ignore --mixed "$arg") + else + eval $(echo args$i)="\"$arg\"" + fi + i=$(expr $i + 1) + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save() { + for i; do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/"; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/rest-controller-practice/gradlew.bat b/rest-controller-practice/gradlew.bat new file mode 100644 index 00000000..107acd32 --- /dev/null +++ b/rest-controller-practice/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +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%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="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! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/rest-controller-practice/settings.gradle b/rest-controller-practice/settings.gradle new file mode 100644 index 00000000..9711e820 --- /dev/null +++ b/rest-controller-practice/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'rest-controller' diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/GlobalExceptionHandler.java b/rest-controller-practice/src/main/java/com/example/restcontroller/GlobalExceptionHandler.java new file mode 100644 index 00000000..ca7580fe --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/GlobalExceptionHandler.java @@ -0,0 +1,44 @@ +package com.example.restcontroller; + +import com.example.restcontroller.board.exception.BoardTypeNotFoundException; +import com.example.restcontroller.notice.exception.AlreadyDeletedException; +import com.example.restcontroller.notice.exception.DuplicateNoticeException; +import com.example.restcontroller.notice.exception.NoticeNotFoundException; +import com.example.restcontroller.user.exception.ExistsEmailException; +import com.example.restcontroller.user.exception.PasswordNotMatchException; +import com.example.restcontroller.user.exception.UserNotFoundException; +import com.example.restcontroller.user.model.ResponseMessage; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +@RestControllerAdvice +public class GlobalExceptionHandler { + + @ExceptionHandler({ NoticeNotFoundException.class, + DuplicateNoticeException.class, + UserNotFoundException.class, + ExistsEmailException.class, + PasswordNotMatchException.class, + BoardTypeNotFoundException.class }) + public ResponseEntity badRequest(RuntimeException e) { + return new ResponseEntity<>(ResponseMessage.fail(e.getMessage()), HttpStatus.BAD_REQUEST); + } + + @ExceptionHandler(DataIntegrityViolationException.class) + public ResponseEntity internalServerError(DataIntegrityViolationException e) { + return new ResponseEntity<>("회원 가입 실패", HttpStatus.INTERNAL_SERVER_ERROR); + } + + @ExceptionHandler(AlreadyDeletedException.class) + public ResponseEntity ok(AlreadyDeletedException e) { + return new ResponseEntity<>(e.getMessage(), HttpStatus.OK); + } + + @ExceptionHandler(Exception.class) + public ResponseEntity exception(Exception e) { + return new ResponseEntity<>(e.getMessage(), HttpStatus.OK); + } +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/RestControllerApplication.java b/rest-controller-practice/src/main/java/com/example/restcontroller/RestControllerApplication.java new file mode 100644 index 00000000..05363f63 --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/RestControllerApplication.java @@ -0,0 +1,13 @@ +package com.example.restcontroller; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class RestControllerApplication { + + public static void main(String[] args) { + SpringApplication.run(RestControllerApplication.class, args); + } + +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/board/controller/ApiBoardController.java b/rest-controller-practice/src/main/java/com/example/restcontroller/board/controller/ApiBoardController.java new file mode 100644 index 00000000..f36e835a --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/board/controller/ApiBoardController.java @@ -0,0 +1,201 @@ +package com.example.restcontroller.board.controller; + +import com.auth0.jwt.exceptions.JWTVerificationException; +import com.example.restcontroller.board.entity.BoardType; +import com.example.restcontroller.board.model.*; +import com.example.restcontroller.board.service.BoardService; +import com.example.restcontroller.common.model.ResponseResult; +import com.example.restcontroller.notice.model.ResponseError; +import com.example.restcontroller.user.model.ResponseMessage; +import com.example.restcontroller.util.JWTUtils; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import java.util.List; + +@RequiredArgsConstructor +@RestController +public class ApiBoardController { + + private final BoardService boardService; + + @PostMapping("/api/board/type") + public ResponseEntity chapter3_1(@RequestBody @Valid BoardTypeInput boardTypeInput, BindingResult bindingResult) { + if (bindingResult.hasFieldErrors()) { + List responseErrors = ResponseError.of(bindingResult.getFieldErrors()); + ResponseEntity.badRequest().body(ResponseMessage.fail("입력값이 정확하지 않습니다.", responseErrors)); + } + + ServiceResult result = boardService.addBoard(boardTypeInput); + + if (!result.isResult()) { + return ResponseEntity.ok().body(ResponseMessage.fail(result.getMessage())); + } + return ResponseEntity.ok().body(ResponseMessage.success()); + } + + @PutMapping("/api/board/type/{id}") + public ResponseEntity chapter3_2(@PathVariable Long id, + @RequestBody @Valid BoardTypeInput boardTypeInput, BindingResult bindingResult) { + if (bindingResult.hasFieldErrors()) { + List responseErrors = ResponseError.of(bindingResult.getFieldErrors()); + ResponseEntity.badRequest().body(ResponseMessage.fail("입력값이 정확하지 않습니다.", responseErrors)); + } + System.out.println(boardTypeInput.getName()); + ServiceResult result = boardService.updateBoard(id, boardTypeInput); + + + if (!result.isResult()) { + return ResponseEntity.ok().body(ResponseMessage.fail(result.getMessage())); + } + return ResponseEntity.ok().body(ResponseMessage.success()); + } + + @DeleteMapping("/api/board/type/{id}") + public ResponseEntity chapter3_3(@PathVariable Long id) { + ServiceResult result = boardService.deleteBoard(id); + + if (!result.isResult()) { + return ResponseEntity.ok().body(ResponseMessage.fail(result.getMessage())); + } + return ResponseEntity.ok().body(ResponseMessage.success()); + } + + @GetMapping("/api/board/type") + public ResponseEntity chapter3_4() { + List boardTypeList = boardService.getBoardTypeList(); + + return ResponseEntity.ok().body(ResponseMessage.success(boardTypeList)); + } + + @PatchMapping("/api/board/type/{id}/using") + public ResponseEntity chapter3_5(@PathVariable Long id, @RequestBody BoardTypeUsing boardTypeUsing) { + ServiceResult result = boardService.setBoardTypeUsing(id, boardTypeUsing); + + if (!result.isResult()) { + return ResponseEntity.ok().body(ResponseMessage.fail(result.getMessage())); + } + return ResponseEntity.ok().body(ResponseMessage.success()); + } + + @GetMapping("/api/board/type/count") + public ResponseEntity chapter3_6() { + List list = boardService.getBoardTypeCount(); + + return ResponseEntity.ok().body(ResponseMessage.success(list)); + } + + @PatchMapping("/api/board/{id}/top") + public ResponseEntity chapter3_7(@PathVariable Long id) { + ServiceResult result = boardService.setBoardTop(id, true); + + if (!result.isResult()) { + return ResponseEntity.ok().body(ResponseMessage.fail(result.getMessage())); + } + return ResponseEntity.ok().body(ResponseMessage.success()); + } + + @PatchMapping("/api/board/{id}/top/clear") + public ResponseEntity chapter3_8(@PathVariable Long id) { + ServiceResult result = boardService.setBoardTop(id, false); + + if (!result.isResult()) { + return ResponseEntity.ok().body(ResponseMessage.fail(result.getMessage())); + } + return ResponseEntity.ok().body(ResponseMessage.success()); + } + + @PatchMapping("/api/board/{id}/publish") + public ResponseEntity chapter3_9(@PathVariable Long id, @RequestBody BoardPeriod boardPeriod) { + ServiceResult result = boardService.setBoardPeriod(id, boardPeriod); + + if (!result.isResult()) { + return ResponseResult.fail(result.getMessage()); + } + + return ResponseResult.success(); + } + + @PutMapping("/api/board/{id}/hits") + public ResponseEntity chapter3_10(@PathVariable Long id, @RequestHeader("TOKEN") String token) { + + String email = ""; + try { + email = JWTUtils.getIssuer(token); + } catch (JWTVerificationException e) { + return ResponseResult.fail("토큰 정보가 정확하지 않습니다."); + } + + ServiceResult result = boardService.setBoardHits(id, email); + if (result.isFail()) { + return ResponseResult.fail(result.getMessage()); + } + + return ResponseResult.success(); + } + + @PutMapping("/api/board/{id}/like") + public ResponseEntity chapter3_11(@PathVariable Long id, @RequestHeader("TOKEN") String token) { + String email = ""; + try { + email = JWTUtils.getIssuer(token); + } catch (JWTVerificationException e) { + return ResponseResult.fail("토큰 정보가 정확하지 않습니다."); + } + + ServiceResult result = boardService.setBoardLike(id, email); + return ResponseResult.result(result); + } + + @PutMapping("/api/board/{id}/unlike") + public ResponseEntity chapter3_12(@PathVariable Long id, @RequestHeader("TOKEN") String token) { + String email = ""; + try { + email = JWTUtils.getIssuer(token); + } catch (JWTVerificationException e) { + return ResponseResult.fail("토큰 정보가 정확하지 않습니다."); + } + + ServiceResult result = boardService.setBoardUnLike(id, email); + return ResponseResult.result(result); + } + + @PutMapping("/api/board/{id}/report") + public ResponseEntity chapter3_13(@PathVariable Long id, + @RequestHeader("TOKEN") String token, + @RequestBody BoardReportInput boardReportInput) { + + String email = ""; + try { + email = JWTUtils.getIssuer(token); + } catch (JWTVerificationException e) { + return ResponseResult.fail("토큰 정보가 정확하지 않습니다."); + } + ServiceResult result = boardService.addReport(id, email, boardReportInput); + + return ResponseResult.result(result); + } +} + + + + + + + + + + + + + + + + + + + + diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/board/entity/Board.java b/rest-controller-practice/src/main/java/com/example/restcontroller/board/entity/Board.java new file mode 100644 index 00000000..334d66dc --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/board/entity/Board.java @@ -0,0 +1,53 @@ +package com.example.restcontroller.board.entity; + +import com.example.restcontroller.user.entity.User; +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +@Entity +public class Board { + + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne + @JoinColumn + private User user; + + @ManyToOne + @JoinColumn + private BoardType boardType; + + @JsonIgnore + @OneToMany(mappedBy = "board") + List boardHitsList = new ArrayList<>(); + + @JsonIgnore + @OneToMany(mappedBy = "board") + List boardLikeList = new ArrayList<>(); + + private String title; + + private String content; + + private boolean topYn; + + private LocalDateTime regDate; + + private LocalDate publishStartDate; + private LocalDate publishEndDate; + +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/board/entity/BoardHits.java b/rest-controller-practice/src/main/java/com/example/restcontroller/board/entity/BoardHits.java new file mode 100644 index 00000000..d4bb4e75 --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/board/entity/BoardHits.java @@ -0,0 +1,31 @@ +package com.example.restcontroller.board.entity; + +import com.example.restcontroller.user.entity.User; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; +import java.time.LocalDateTime; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +@Entity +public class BoardHits { + + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne + @JoinColumn + private Board board; + + @ManyToOne + @JoinColumn + private User user; + + private LocalDateTime regDate; +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/board/entity/BoardLike.java b/rest-controller-practice/src/main/java/com/example/restcontroller/board/entity/BoardLike.java new file mode 100644 index 00000000..e48e5f7f --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/board/entity/BoardLike.java @@ -0,0 +1,31 @@ +package com.example.restcontroller.board.entity; + +import com.example.restcontroller.user.entity.User; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; +import java.time.LocalDateTime; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +@Entity +public class BoardLike { + + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne + @JoinColumn + private Board board; + + @ManyToOne + @JoinColumn + private User user; + + private LocalDateTime regDate; +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/board/entity/BoardReport.java b/rest-controller-practice/src/main/java/com/example/restcontroller/board/entity/BoardReport.java new file mode 100644 index 00000000..b44f3855 --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/board/entity/BoardReport.java @@ -0,0 +1,41 @@ +package com.example.restcontroller.board.entity; + +import com.example.restcontroller.user.entity.User; +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +@Entity +public class BoardReport { + + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + // 신고자 + private Long userId; + private String userName; + private String userEmail; + + // 신고 게시글 + private Long boardId; + private Long boardUserId; + private String boardTitle; + private String boardContents; + private LocalDateTime boardRegDate; + + // 신고내용 + private String comments; + private LocalDateTime regDate; +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/board/entity/BoardType.java b/rest-controller-practice/src/main/java/com/example/restcontroller/board/entity/BoardType.java new file mode 100644 index 00000000..da8ef5ed --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/board/entity/BoardType.java @@ -0,0 +1,35 @@ +package com.example.restcontroller.board.entity; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +@Entity +public class BoardType { + + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String boardName; + + private LocalDateTime regDate; + + private LocalDateTime updateDate; + + private boolean usingYn; + + @JsonIgnore + @OneToMany(mappedBy = "boardType") + List boardList = new ArrayList<>(); +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/board/exception/BoardTypeNotFoundException.java b/rest-controller-practice/src/main/java/com/example/restcontroller/board/exception/BoardTypeNotFoundException.java new file mode 100644 index 00000000..e5b283db --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/board/exception/BoardTypeNotFoundException.java @@ -0,0 +1,7 @@ +package com.example.restcontroller.board.exception; + +public class BoardTypeNotFoundException extends RuntimeException { + public BoardTypeNotFoundException(String message) { + super(message); + } +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/board/model/BoardPeriod.java b/rest-controller-practice/src/main/java/com/example/restcontroller/board/model/BoardPeriod.java new file mode 100644 index 00000000..391ab3c9 --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/board/model/BoardPeriod.java @@ -0,0 +1,18 @@ +package com.example.restcontroller.board.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDate; + +@AllArgsConstructor +@NoArgsConstructor +@Builder +@Data +public class BoardPeriod { + + private LocalDate startDate; + private LocalDate endDate; +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/board/model/BoardReportInput.java b/rest-controller-practice/src/main/java/com/example/restcontroller/board/model/BoardReportInput.java new file mode 100644 index 00000000..301a359e --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/board/model/BoardReportInput.java @@ -0,0 +1,15 @@ +package com.example.restcontroller.board.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@AllArgsConstructor +@NoArgsConstructor +@Builder +@Data +public class BoardReportInput { + + private String comments; +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/board/model/BoardTypeCount.java b/rest-controller-practice/src/main/java/com/example/restcontroller/board/model/BoardTypeCount.java new file mode 100644 index 00000000..43d528a8 --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/board/model/BoardTypeCount.java @@ -0,0 +1,34 @@ +package com.example.restcontroller.board.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigInteger; +import java.sql.Timestamp; +import java.time.LocalDateTime; + +@AllArgsConstructor +@NoArgsConstructor +@Builder +@Data +public class BoardTypeCount { + + private Long id; + private String boardName; + private LocalDateTime regDate; + private boolean usingYn; + private Long boardCount; + + public static BoardTypeCount of(Object[] arr) { + return BoardTypeCount.builder() + .id(((BigInteger) arr[0]).longValue()) + .boardName((String) arr[1]) + .regDate(((Timestamp) arr[2]).toLocalDateTime()) + .usingYn((Boolean) arr[3]) + .boardCount(((BigInteger) arr[4]).longValue()) + .build(); + + } +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/board/model/BoardTypeInput.java b/rest-controller-practice/src/main/java/com/example/restcontroller/board/model/BoardTypeInput.java new file mode 100644 index 00000000..29f0b66f --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/board/model/BoardTypeInput.java @@ -0,0 +1,18 @@ +package com.example.restcontroller.board.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotBlank; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class BoardTypeInput { + + @NotBlank(message = "게시판 제목은 필수 항목입니다.") + private String name; +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/board/model/BoardTypeUsing.java b/rest-controller-practice/src/main/java/com/example/restcontroller/board/model/BoardTypeUsing.java new file mode 100644 index 00000000..678e973d --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/board/model/BoardTypeUsing.java @@ -0,0 +1,15 @@ +package com.example.restcontroller.board.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class BoardTypeUsing { + + private boolean usingYn; +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/board/model/ServiceResult.java b/rest-controller-practice/src/main/java/com/example/restcontroller/board/model/ServiceResult.java new file mode 100644 index 00000000..972804f1 --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/board/model/ServiceResult.java @@ -0,0 +1,33 @@ +package com.example.restcontroller.board.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class ServiceResult { + + private boolean result; + private String message; + + public static ServiceResult fail(String message) { + return ServiceResult.builder() + .result(false) + .message(message) + .build(); + } + + public static ServiceResult success() { + return ServiceResult.builder() + .result(true) + .build(); + } + + public boolean isFail() { + return !result; + } +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/board/repository/BoardHitsRepository.java b/rest-controller-practice/src/main/java/com/example/restcontroller/board/repository/BoardHitsRepository.java new file mode 100644 index 00000000..f64ac174 --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/board/repository/BoardHitsRepository.java @@ -0,0 +1,13 @@ +package com.example.restcontroller.board.repository; + +import com.example.restcontroller.board.entity.Board; +import com.example.restcontroller.board.entity.BoardHits; +import com.example.restcontroller.user.entity.User; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface BoardHitsRepository extends JpaRepository { + + long countByBoardAndUser(Board board, User user); +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/board/repository/BoardLikeRepository.java b/rest-controller-practice/src/main/java/com/example/restcontroller/board/repository/BoardLikeRepository.java new file mode 100644 index 00000000..09f01b96 --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/board/repository/BoardLikeRepository.java @@ -0,0 +1,17 @@ +package com.example.restcontroller.board.repository; + +import com.example.restcontroller.board.entity.Board; +import com.example.restcontroller.board.entity.BoardHits; +import com.example.restcontroller.board.entity.BoardLike; +import com.example.restcontroller.user.entity.User; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface BoardLikeRepository extends JpaRepository { + + long countByBoardAndUser(Board board, User user); + + void deleteByBoardAndUser(Board board, User user); + +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/board/repository/BoardReportRepository.java b/rest-controller-practice/src/main/java/com/example/restcontroller/board/repository/BoardReportRepository.java new file mode 100644 index 00000000..2a1e154e --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/board/repository/BoardReportRepository.java @@ -0,0 +1,9 @@ +package com.example.restcontroller.board.repository; + +import com.example.restcontroller.board.entity.BoardReport; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface BoardReportRepository extends JpaRepository { +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/board/repository/BoardRepository.java b/rest-controller-practice/src/main/java/com/example/restcontroller/board/repository/BoardRepository.java new file mode 100644 index 00000000..ef215ed4 --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/board/repository/BoardRepository.java @@ -0,0 +1,12 @@ +package com.example.restcontroller.board.repository; + +import com.example.restcontroller.board.entity.Board; +import com.example.restcontroller.board.entity.BoardType; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface BoardRepository extends JpaRepository { + + long countByBoardType(BoardType boardType); +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/board/repository/BoardTypeCustomRepository.java b/rest-controller-practice/src/main/java/com/example/restcontroller/board/repository/BoardTypeCustomRepository.java new file mode 100644 index 00000000..d6199d88 --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/board/repository/BoardTypeCustomRepository.java @@ -0,0 +1,35 @@ +package com.example.restcontroller.board.repository; + +import com.example.restcontroller.board.model.BoardTypeCount; +import lombok.RequiredArgsConstructor; +import org.qlrm.mapper.JpaResultMapper; +import org.springframework.stereotype.Repository; + +import javax.persistence.EntityManager; +import javax.persistence.Query; +import java.util.List; +import java.util.stream.Collectors; + +@RequiredArgsConstructor +@Repository +public class BoardTypeCustomRepository { + + private final EntityManager em; + + public List getBoardTypeCount() { + String sql = "select bt.id, bt.board_name, bt.reg_date, bt.using_yn, " + + "(select count(*) from board b where b.board_type_id = bt.id) as board_count " + + "from board_type bt where bt.using_yn=true"; +// SELECT bt.id, bt.board_name, bt.reg_date, bt.using_yn, ct.count FROM (select board_type_id, count(*) count from board group by board_type_id) ct left outer join board_type bt on bt.id=ct.board_type_id; +// List list = em.createNativeQuery(sql).getResultList(); + + List resultList = em.createNativeQuery(sql).getResultList(); + List list = resultList.stream().map(BoardTypeCount::of).collect(Collectors.toList()); + +// // 라이브러리 사용 +// Query nativeQuery = em.createNativeQuery(sql); +// JpaResultMapper jpaResultMapper = new JpaResultMapper(); +// List list = jpaResultMapper.list(nativeQuery, BoardTypeCount.class); + return list; + } +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/board/repository/BoardTypeRepository.java b/rest-controller-practice/src/main/java/com/example/restcontroller/board/repository/BoardTypeRepository.java new file mode 100644 index 00000000..3de237d3 --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/board/repository/BoardTypeRepository.java @@ -0,0 +1,12 @@ +package com.example.restcontroller.board.repository; + +import com.example.restcontroller.board.entity.BoardType; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +public interface BoardTypeRepository extends JpaRepository { + int countByBoardName(String boardName); +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/board/service/BoardService.java b/rest-controller-practice/src/main/java/com/example/restcontroller/board/service/BoardService.java new file mode 100644 index 00000000..e52ea144 --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/board/service/BoardService.java @@ -0,0 +1,33 @@ +package com.example.restcontroller.board.service; + +import com.example.restcontroller.board.entity.BoardType; +import com.example.restcontroller.board.model.*; + +import java.util.List; + +public interface BoardService { + + ServiceResult addBoard(BoardTypeInput boardTypeInput); + + ServiceResult updateBoard(Long id, BoardTypeInput boardTypeInput); + + ServiceResult deleteBoard(Long id); + + List getBoardTypeList(); + + ServiceResult setBoardTypeUsing(Long id, BoardTypeUsing boardTypeUsing); + + List getBoardTypeCount(); + + ServiceResult setBoardTop(Long id, boolean topYn); + + ServiceResult setBoardPeriod(Long id, BoardPeriod boardPeriod); + + ServiceResult setBoardHits(Long id, String email); + + ServiceResult setBoardLike(Long id, String email); + + ServiceResult setBoardUnLike(Long id, String email); + + ServiceResult addReport(Long id, String email, BoardReportInput boardReportInput); +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/board/service/BoardServiceImpl.java b/rest-controller-practice/src/main/java/com/example/restcontroller/board/service/BoardServiceImpl.java new file mode 100644 index 00000000..e1a09d34 --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/board/service/BoardServiceImpl.java @@ -0,0 +1,241 @@ +package com.example.restcontroller.board.service; + +import com.example.restcontroller.board.entity.*; +import com.example.restcontroller.board.exception.BoardTypeNotFoundException; +import com.example.restcontroller.board.model.*; +import com.example.restcontroller.board.repository.*; +import com.example.restcontroller.user.entity.User; +import com.example.restcontroller.user.repository.UserRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Optional; + +@RequiredArgsConstructor +@Service +@Transactional(readOnly = true) +public class BoardServiceImpl implements BoardService { + + private final BoardTypeRepository boardTypeRepository; + private final BoardRepository boardRepository; + private final BoardTypeCustomRepository boardTypeCustomRepository; + private final BoardHitsRepository boardHitsRepository; + private final UserRepository userRepository; + private final BoardLikeRepository boardLikeRepository; + private final BoardReportRepository boardReportRepository; + + @Transactional + @Override + public ServiceResult addBoard(BoardTypeInput boardTypeInput) { + int result = boardTypeRepository.countByBoardName(boardTypeInput.getName()); + if (result > 0) { + return ServiceResult.fail("이미 동일한 게시판명이 존재합니다."); + } + + BoardType boardType = BoardType.builder() + .boardName(boardTypeInput.getName()) + .regDate(LocalDateTime.now()) + .build(); + + boardTypeRepository.save(boardType); + + return ServiceResult.success(); + } + + @Transactional + @Override + public ServiceResult updateBoard(Long id, BoardTypeInput boardTypeInput) { + int result = boardTypeRepository.countByBoardName(boardTypeInput.getName()); + if (result > 0) { + return ServiceResult.fail("이미 동일한 게시판명이 존재합니다."); + } + + BoardType boardTypeEntity = boardTypeRepository.findById(id) + .orElseThrow(() -> new BoardTypeNotFoundException("삭제할 게시판 타입이 없습니다.")); + + boardTypeEntity.setBoardName(boardTypeInput.getName()); + boardTypeEntity.setUpdateDate(LocalDateTime.now()); + +// boardTypeRepository.save(boardTypeEntity); + + return ServiceResult.success(); + } + + @Transactional + @Override + public ServiceResult deleteBoard(Long id) { + BoardType boardType = boardTypeRepository.findById(id) + .orElseThrow(() -> new BoardTypeNotFoundException("삭제할 게시판 타입이 없습니다.")); + + if (boardRepository.countByBoardType(boardType) > 0 ) { + return ServiceResult.fail("삭제할 게시판타입의 게시글이 존재합니다."); + } + + boardTypeRepository.delete(boardType); + return ServiceResult.success(); + } + + @Override + public List getBoardTypeList() { + return boardTypeRepository.findAll(); + } + + @Transactional + @Override + public ServiceResult setBoardTypeUsing(Long id, BoardTypeUsing boardTypeUsing) { + BoardType boardType = boardTypeRepository.findById(id) + .orElseThrow(() -> new BoardTypeNotFoundException("게시판 타입이 없습니다.")); + + boardType.setUsingYn(boardTypeUsing.isUsingYn()); + + return ServiceResult.success(); + } + + @Override + public List getBoardTypeCount() { + return boardTypeCustomRepository.getBoardTypeCount(); + } + + @Transactional + @Override + public ServiceResult setBoardTop(Long id, boolean topYn) { + Optional board = boardRepository.findById(id); + if (!board.isPresent()) { + return ServiceResult.fail("게시글이 존재하지 않습니다"); + } + + Board boardEntity = board.get(); + if (boardEntity.isTopYn() == topYn) { + String msg = topYn ? "이미 게시글이 최상단에 배치되어 있습니다." : "이미 게시글이 최상단 배치가 해제되어 있습니다."; + return ServiceResult.fail(msg); + } + + boardEntity.setTopYn(topYn); + return ServiceResult.success(); + } + + @Transactional + @Override + public ServiceResult setBoardPeriod(Long id, BoardPeriod boardPeriod) { + Optional board = boardRepository.findById(id); + if (!board.isPresent()) { + return ServiceResult.fail("게시글이 존재하지 않습니다"); + } + Board boardEntity = board.get(); + boardEntity.setPublishStartDate(boardPeriod.getStartDate()); + boardEntity.setPublishEndDate(boardPeriod.getEndDate()); + return ServiceResult.success(); + } + + @Transactional + @Override + public ServiceResult setBoardHits(Long id, String email) { + Optional board = boardRepository.findById(id); + if (!board.isPresent()) { + return ServiceResult.fail("게시글이 존재하지 않습니다"); + } + Board boardEntity = board.get(); + + Optional optionalUser = userRepository.findByEmail(email); + if (!optionalUser.isPresent()) { + return ServiceResult.fail("회원 정보가 존재하지 않습니다"); + } + User userEntity = optionalUser.get(); + if (boardHitsRepository.countByBoardAndUser(boardEntity, userEntity) > 0) { + return ServiceResult.fail("이미 조회수가 있습니다."); + } + + boardHitsRepository.save(BoardHits.builder() + .board(boardEntity) + .user(userEntity) + .regDate(LocalDateTime.now()) + .build()); + + return ServiceResult.success(); + } + + @Transactional + @Override + public ServiceResult setBoardLike(Long id, String email) { + Optional board = boardRepository.findById(id); + if (!board.isPresent()) { + return ServiceResult.fail("게시글이 존재하지 않습니다"); + } + Board boardEntity = board.get(); + + Optional optionalUser = userRepository.findByEmail(email); + if (!optionalUser.isPresent()) { + return ServiceResult.fail("회원 정보가 존재하지 않습니다"); + } + + User userEntity = optionalUser.get(); + if (boardLikeRepository.countByBoardAndUser(boardEntity, userEntity) > 0) { + return ServiceResult.fail("이미 좋아요를 누른 게시글 입니다."); + } + + boardLikeRepository.save(BoardLike.builder() + .board(boardEntity) + .user(userEntity) + .regDate(LocalDateTime.now()) + .build()); + + return ServiceResult.success(); + } + + @Transactional + @Override + public ServiceResult setBoardUnLike(Long id, String email) { + Optional board = boardRepository.findById(id); + if (!board.isPresent()) { + return ServiceResult.fail("게시글이 존재하지 않습니다"); + } + Board boardEntity = board.get(); + + Optional optionalUser = userRepository.findByEmail(email); + if (!optionalUser.isPresent()) { + return ServiceResult.fail("회원 정보가 존재하지 않습니다"); + } + + User userEntity = optionalUser.get(); + if (boardLikeRepository.countByBoardAndUser(boardEntity, userEntity) < 1) { + return ServiceResult.fail("좋아요한 내용이 없습니다."); + } + boardLikeRepository.deleteByBoardAndUser(boardEntity, userEntity); + + return ServiceResult.success(); + } + + @Override + public ServiceResult addReport(Long id, String email, BoardReportInput boardReportInput) { + Optional board = boardRepository.findById(id); + if (!board.isPresent()) { + return ServiceResult.fail("게시글이 존재하지 않습니다"); + } + Board boardEntity = board.get(); + + Optional optionalUser = userRepository.findByEmail(email); + if (!optionalUser.isPresent()) { + return ServiceResult.fail("회원 정보가 존재하지 않습니다"); + } + User userEntity = optionalUser.get(); + + BoardReport boardReport = BoardReport.builder() + .userId(userEntity.getId()) + .userName(userEntity.getUserName()) + .userEmail(userEntity.getEmail()) + .boardId(boardEntity.getId()) + .boardUserId(boardEntity.getUser().getId()) + .boardTitle(boardEntity.getTitle()) + .boardContents(boardEntity.getContent()) + .boardRegDate(boardEntity.getRegDate()) + .comments(boardReportInput.getComments()) + .regDate(LocalDateTime.now()) + .build(); + + boardReportRepository.save(boardReport); + return ServiceResult.success(); + } +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/common/model/ResponseResult.java b/rest-controller-practice/src/main/java/com/example/restcontroller/common/model/ResponseResult.java new file mode 100644 index 00000000..d5f2d8c0 --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/common/model/ResponseResult.java @@ -0,0 +1,24 @@ +package com.example.restcontroller.common.model; + +import com.example.restcontroller.board.model.ServiceResult; +import com.example.restcontroller.user.model.ResponseMessage; +import org.springframework.http.ResponseEntity; + +public class ResponseResult { + + + public static ResponseEntity fail(String message) { + return ResponseEntity.badRequest().body(ResponseMessage.fail(message)); + } + + public static ResponseEntity success() { + return ResponseEntity.ok().body(ResponseMessage.success()); + } + + public static ResponseEntity result(ServiceResult result) { + if (result.isFail()) { + return fail(result.getMessage()); + } + return success(); + } +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/config/SecurityConfig.java b/rest-controller-practice/src/main/java/com/example/restcontroller/config/SecurityConfig.java new file mode 100644 index 00000000..2141ed4b --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/config/SecurityConfig.java @@ -0,0 +1,23 @@ +package com.example.restcontroller.config; + +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.annotation.web.configuration.WebSecurityConfigurerAdapter; + +@Configuration +@EnableWebSecurity +public class SecurityConfig extends WebSecurityConfigurerAdapter { + + @Override + protected void configure(HttpSecurity http) throws Exception { + + http.csrf().disable(); + http.headers().frameOptions().sameOrigin(); + + http.authorizeRequests() + .anyRequest().permitAll(); + + } + +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/hello/StartController.java b/rest-controller-practice/src/main/java/com/example/restcontroller/hello/StartController.java new file mode 100644 index 00000000..4dbd239f --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/hello/StartController.java @@ -0,0 +1,27 @@ +package com.example.restcontroller.hello; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; + +@Controller +public class StartController { + + @RequestMapping(value = "/first-url", method = RequestMethod.GET) + public void chapter1_1() { + } + + @ResponseBody + @RequestMapping("/helloworld") + public String chapter1_2() { + return "hello world"; + } + + @ResponseBody + @GetMapping("/hello-spring") + public String chapter1_3() { + return "hello spring"; + } +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/hello/StartRestController.java b/rest-controller-practice/src/main/java/com/example/restcontroller/hello/StartRestController.java new file mode 100644 index 00000000..5ac9f1cb --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/hello/StartRestController.java @@ -0,0 +1,18 @@ +package com.example.restcontroller.hello; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class StartRestController { + + @GetMapping("/hello-rest") + public String chapter1_4() { + return "\"hello rest\""; + } + + @GetMapping("/api/helloworld") + public String chapter1_5() { + return "hello rest api"; + } +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/notice/controller/ApiNoticeController.java b/rest-controller-practice/src/main/java/com/example/restcontroller/notice/controller/ApiNoticeController.java new file mode 100644 index 00000000..afdd2599 --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/notice/controller/ApiNoticeController.java @@ -0,0 +1,333 @@ +package com.example.restcontroller.notice.controller; + +import com.example.restcontroller.notice.entity.Notice; +import com.example.restcontroller.notice.exception.AlreadyDeletedException; +import com.example.restcontroller.notice.exception.DuplicateNoticeException; +import com.example.restcontroller.notice.exception.NoticeNotFoundException; +import com.example.restcontroller.notice.model.NoticeDeleteInput; +import com.example.restcontroller.notice.model.NoticeInput; +import com.example.restcontroller.notice.model.NoticeModel; +import com.example.restcontroller.notice.model.ResponseError; +import com.example.restcontroller.notice.repository.NoticeRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Sort; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + + +@RequiredArgsConstructor +@RestController +public class ApiNoticeController { + + private final NoticeRepository noticeRepository; + + @GetMapping("/api/notice") + public String chapter1_6() { + return "공지사항입니다."; + } + + @GetMapping("/api/notice2") + public NoticeModel chapter1_7() { + + return NoticeModel.builder() + .id(1L) + .title("공지사항입니다.") + .contents("공지사항 내용 입니다.") + .regDate(LocalDateTime.now()) + .build(); + } + + @GetMapping("/api/notice3") + public List chapter1_8() { + List noticeList = new ArrayList<>(); + + noticeList.add(NoticeModel.builder() + .id(1L) + .title("공지사항입니다.") + .contents("공지사항 내용입니다.") + .regDate(LocalDateTime.of(2021, 1, 30, 0, 0)) + .build()); + + noticeList.add(NoticeModel.builder() + .id(2L) + .title("두번째 공지사항입니다.") + .contents("두번째 공지사항 내용입니다.") + .regDate(LocalDateTime.of(2021, 1, 31, 0, 0)) + .build()); + + return noticeList; + } + + @GetMapping("/api/notice5") + public List chapter1_9() { + return new ArrayList<>(); + } + + @GetMapping("/api/notice/count") + public Integer chapter1_10() { + List noticeList = new ArrayList<>(); + noticeList.add(new NoticeModel()); + noticeList.add(new NoticeModel()); + noticeList.add(new NoticeModel()); + + return noticeList.size(); + } + + @PostMapping("/api/notice") + public NoticeModel chapter1_11(@RequestParam String title, + @RequestParam String contents) { + return NoticeModel.builder() + .id(1L) + .title(title) + .contents(contents) + .regDate(LocalDateTime.now()) + .build(); + } + + @PostMapping("/api/notice2") + public NoticeModel chapter1_12(NoticeModel noticeModel) { + return noticeModel + .setId(2L) + .setRegDate(LocalDateTime.now()); + } + + @PostMapping("/api/notice3") + public NoticeModel chapter1_13(@RequestBody NoticeModel noticeModel) { + return noticeModel + .setId(3L) + .setRegDate(LocalDateTime.now()); + } + + @PostMapping("/api/notice4") + public Notice chapter1_14(@RequestBody NoticeInput noticeInput) { + return noticeRepository.save(Notice.builder() + .title(noticeInput.getTitle()) + .contents(noticeInput.getContents()) + .regDate(LocalDateTime.now()) + .build()); + } + + @PostMapping("/api/notice5") + public Notice chapter1_15(@RequestBody NoticeInput noticeInput) { + return noticeRepository.save(Notice.builder() + .title(noticeInput.getTitle()) + .contents(noticeInput.getContents()) + .regDate(LocalDateTime.now()) + .build()); + } + + @GetMapping("/api/notice/{id}") + public Notice chapter1_16(@PathVariable Long id) { + return noticeRepository.findById(id).orElseGet(() -> null); + } + + @PutMapping("/api/notice/{id}") + public Notice chapter1_17(@PathVariable Long id, @RequestBody NoticeInput noticeInput) { + Notice noticeEntity = noticeRepository.findById(id) + .orElseThrow(() -> new IllegalArgumentException("아이디 없음")); + + noticeEntity + .setTitle(noticeInput.getTitle()) + .setContents(noticeInput.getContents()) + .setUpdateDate(LocalDateTime.now()); + + noticeRepository.save(noticeEntity); + + return noticeEntity; + } + + @PutMapping("/api/notice2/{id}") + public Notice chapter1_18(@PathVariable Long id, @RequestBody NoticeInput noticeInput) { + Notice noticeEntity = noticeRepository.findById(id) + .orElseThrow(() -> new NoticeNotFoundException("공지사항의 글이 존재하지 않습니다.")); + + noticeEntity + .setTitle(noticeInput.getTitle()) + .setContents(noticeInput.getContents()); + + noticeRepository.save(noticeEntity); + + return noticeEntity; + } + + @PutMapping("/api/notice3/{id}") + public Notice chapter1_19(@PathVariable Long id, @RequestBody NoticeInput noticeInput) { + Notice noticeEntity = noticeRepository.findById(id) + .orElseThrow(() -> new NoticeNotFoundException("공지사항의 글이 존재하지 않습니다.")); + + noticeEntity + .setTitle(noticeInput.getTitle()) + .setContents(noticeInput.getContents()) + .setUpdateDate(LocalDateTime.now()); + + noticeRepository.save(noticeEntity); + + return noticeEntity; + } + + @PatchMapping("/api/notice/{id}/hits") + public void chapter1_20(@PathVariable Long id) { + Notice noticeEntity = noticeRepository.findById(id) + .orElseThrow(() -> new NoticeNotFoundException("공지사항의 글이 존재하지 않습니다.")); + noticeEntity.setHits(noticeEntity.getHits() + 1); + noticeRepository.save(noticeEntity); + } + + @DeleteMapping("/api/notice/{id}") + public void chapter1_21(@PathVariable Long id) { + noticeRepository.deleteById(id); + } + @DeleteMapping("/api/notice2/{id}") + public void chapter1_22(@PathVariable Long id) { + Notice noticeEntity = noticeRepository.findById(id) + .orElseThrow(() -> new NoticeNotFoundException("공지사항의 글이 존재하지 않습니다.")); + + noticeRepository.delete(noticeEntity); + } + + @DeleteMapping("api/notice3/{id}") + public void chapter1_23(@PathVariable Long id) { + Notice noticeEntity = noticeRepository.findById(id) + .orElseThrow(() -> new NoticeNotFoundException("공지사항의 글이 존재하지 않습니다.")); + if (noticeEntity.isDeleted()) { + throw new AlreadyDeletedException("이미 삭제된 게시글 입니다."); + } + noticeEntity + .setDeleted(true) + .setDeleteDate(LocalDateTime.now()); + + noticeRepository.save(noticeEntity); + } + + @DeleteMapping("/api/notice4") + public void chapter1_24(@RequestBody NoticeDeleteInput noticeDeleteInput) { + + List noticeList = noticeRepository.findByIdIn(noticeDeleteInput.getIdList()) + .orElseThrow(() -> new NoticeNotFoundException("공지사항의 글이 존재하지 않습니다.")); + + noticeList.forEach(notice -> { + notice + .setDeleted(true) + .setDeleteDate(LocalDateTime.now()); + }); + + noticeRepository.saveAll(noticeList); + } + + @DeleteMapping("/api/notice/all") + public void chapter1_25() { + noticeRepository.deleteAll(); + } + + @PostMapping("/api/notice6") + public ResponseEntity chapter1_26(@RequestBody NoticeInput noticeInput) { + noticeRepository.save(Notice.builder() + .title(noticeInput.getTitle()) + .contents(noticeInput.getContents()) + .regDate(LocalDateTime.now()) + .build()); + + return new ResponseEntity<>("CREATED", HttpStatus.CREATED); + } + + @PostMapping("/api/notice7") + public ResponseEntity chapter1_27(@RequestBody @Valid NoticeInput noticeInput, + BindingResult bindingResult) { + if (bindingResult.hasFieldErrors()) { + List errors = new ArrayList<>(); + bindingResult.getFieldErrors().forEach(e -> errors.add(ResponseError.of(e))); + return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST); + } + noticeRepository.save(Notice.builder() + .title(noticeInput.getTitle()) + .contents(noticeInput.getContents()) + .regDate(LocalDateTime.now()) + .build()); + + return new ResponseEntity<>("CREATED", HttpStatus.CREATED); + } + + @PostMapping("/api/notice8") + public ResponseEntity chapter1_28(@RequestBody @Valid NoticeInput noticeInput, + BindingResult bindingResult) { + if (bindingResult.hasFieldErrors()) { + List errors = new ArrayList<>(); + bindingResult.getFieldErrors().forEach(e -> errors.add(ResponseError.of(e))); + return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST); + } + noticeRepository.save(Notice.builder() + .title(noticeInput.getTitle()) + .contents(noticeInput.getContents()) + .regDate(LocalDateTime.now()) + .build()); + + return new ResponseEntity<>("CREATED", HttpStatus.CREATED); + } + + @GetMapping("/api/notice/latest/{size}") + public Page chapter1_29(@PathVariable int size) { + return noticeRepository.findAll(PageRequest.of(0, size, Sort.Direction.DESC, "regDate")); + } + + @PostMapping("/api/notice9") + public ResponseEntity chapter1_30(@RequestBody @Valid NoticeInput noticeInput, + BindingResult bindingResult) throws DuplicateNoticeException { + if (bindingResult.hasFieldErrors()) { + List errors = new ArrayList<>(); + bindingResult.getFieldErrors().forEach(e -> errors.add(ResponseError.of(e))); + return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST); + } + + // 중복체크 + + // regDate > checkDate 이면 1분 미만 + LocalDateTime checkDate = LocalDateTime.now().minusMinutes(1); + + int noticeCount = noticeRepository.countByTitleAndContentsAndRegDateGreaterThanEqual( + noticeInput.getTitle(), + noticeInput.getContents(), + checkDate + ); + + if (noticeCount > 0 ) { + throw new DuplicateNoticeException("1분 이내에 등록된 공지사항이 존재합니다"); + } + + noticeRepository.save(Notice.builder() + .title(noticeInput.getTitle()) + .contents(noticeInput.getContents()) + .regDate(LocalDateTime.now()) + .build()); + + return new ResponseEntity<>("CREATED", HttpStatus.CREATED); + } +} + + + + + + + + + + + + + + + + + + + + diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/notice/entity/Notice.java b/rest-controller-practice/src/main/java/com/example/restcontroller/notice/entity/Notice.java new file mode 100644 index 00000000..075423ce --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/notice/entity/Notice.java @@ -0,0 +1,57 @@ +package com.example.restcontroller.notice.entity; + +import com.example.restcontroller.user.entity.User; +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; +import org.hibernate.annotations.ColumnDefault; + +import javax.persistence.*; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + +@AllArgsConstructor +@NoArgsConstructor +@Builder +@Accessors(chain = true) +@Data +@Entity +public class Notice { + + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn + private User user; + + @JsonIgnore + @OneToMany(mappedBy = "notice") + List noticeLikeList = new ArrayList<>(); + + private String title; + private String contents; + + private LocalDateTime regDate; + private LocalDateTime updateDate; + private LocalDateTime deleteDate; + + @ColumnDefault("false") + private boolean deleted; + + @ColumnDefault("0") + private Integer hits; + + @ColumnDefault("0") + private Integer likes; + + @PrePersist + private void prePersist() { + hits = hits == null ? 0 : hits; + likes = likes == null ? 0 : likes; + } +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/notice/entity/NoticeLike.java b/rest-controller-practice/src/main/java/com/example/restcontroller/notice/entity/NoticeLike.java new file mode 100644 index 00000000..42788947 --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/notice/entity/NoticeLike.java @@ -0,0 +1,34 @@ +package com.example.restcontroller.notice.entity; + +import com.example.restcontroller.user.entity.User; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; +import org.hibernate.annotations.ColumnDefault; + +import javax.persistence.*; +import java.time.LocalDateTime; + +@AllArgsConstructor +@NoArgsConstructor +@Builder +@Accessors(chain = true) +@Data +@Entity +public class NoticeLike { + + //ID, 제목, 내용, 등록일(작성일) + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn + private Notice notice; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn + private User user; + +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/notice/exception/AlreadyDeletedException.java b/rest-controller-practice/src/main/java/com/example/restcontroller/notice/exception/AlreadyDeletedException.java new file mode 100644 index 00000000..ee6b828b --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/notice/exception/AlreadyDeletedException.java @@ -0,0 +1,8 @@ +package com.example.restcontroller.notice.exception; + +public class AlreadyDeletedException extends RuntimeException { + + public AlreadyDeletedException(String message) { + super(message); + } +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/notice/exception/DuplicateNoticeException.java b/rest-controller-practice/src/main/java/com/example/restcontroller/notice/exception/DuplicateNoticeException.java new file mode 100644 index 00000000..40633cbc --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/notice/exception/DuplicateNoticeException.java @@ -0,0 +1,8 @@ +package com.example.restcontroller.notice.exception; + +public class DuplicateNoticeException extends RuntimeException { + + public DuplicateNoticeException(String message) { + super(message); + } +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/notice/exception/NoticeNotFoundException.java b/rest-controller-practice/src/main/java/com/example/restcontroller/notice/exception/NoticeNotFoundException.java new file mode 100644 index 00000000..3f557d6c --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/notice/exception/NoticeNotFoundException.java @@ -0,0 +1,8 @@ +package com.example.restcontroller.notice.exception; + +public class NoticeNotFoundException extends RuntimeException{ + + public NoticeNotFoundException(String message) { + super(message); + } +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/notice/model/NoticeDeleteInput.java b/rest-controller-practice/src/main/java/com/example/restcontroller/notice/model/NoticeDeleteInput.java new file mode 100644 index 00000000..2fc1a3ae --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/notice/model/NoticeDeleteInput.java @@ -0,0 +1,11 @@ +package com.example.restcontroller.notice.model; + +import lombok.Data; + +import java.util.List; + +@Data +public class NoticeDeleteInput { + + List idList; +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/notice/model/NoticeInput.java b/rest-controller-practice/src/main/java/com/example/restcontroller/notice/model/NoticeInput.java new file mode 100644 index 00000000..cfe382b1 --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/notice/model/NoticeInput.java @@ -0,0 +1,25 @@ +package com.example.restcontroller.notice.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.hibernate.validator.constraints.Length; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; + +@AllArgsConstructor +@NoArgsConstructor +@Data +@Builder +public class NoticeInput { + + @NotBlank(message = "제목을 입력 해주세요") + @Size(min = 10, max = 50, message = "제목은 최소 10자 이상 최대 50자 입력 가능합니다.") + private String title; + + @NotBlank(message = "내용을 입력 해주세요") + @Length(min =30, max = 1000, message = "내용은 최소 50자 이상 최대 1000자 입력 가능합니다.") + private String contents; +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/notice/model/NoticeModel.java b/rest-controller-practice/src/main/java/com/example/restcontroller/notice/model/NoticeModel.java new file mode 100644 index 00000000..2d633cb5 --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/notice/model/NoticeModel.java @@ -0,0 +1,24 @@ +package com.example.restcontroller.notice.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.time.LocalDateTime; + +@AllArgsConstructor +@NoArgsConstructor +@Builder +@Accessors(chain = true) +@Data +public class NoticeModel { + + //ID, 제목, 내용, 등록일(작성일) + private Long id; + private String title; + private String contents; + private LocalDateTime regDate; + +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/notice/model/NoticeResponse.java b/rest-controller-practice/src/main/java/com/example/restcontroller/notice/model/NoticeResponse.java new file mode 100644 index 00000000..beb3b833 --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/notice/model/NoticeResponse.java @@ -0,0 +1,42 @@ +package com.example.restcontroller.notice.model; + +import com.example.restcontroller.notice.entity.Notice; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class NoticeResponse { + + private Long id; + + private Long regUserId; + private String regUserName; + + private String title; + private String contents; + private LocalDateTime regDate; + private LocalDateTime updateDate; + private Integer hits; + private Integer likes; + + public static NoticeResponse of(Notice notice) { + return NoticeResponse.builder() + .id(notice.getId()) + .regUserId(notice.getUser().getId()) + .regUserName(notice.getUser().getUserName()) + .title(notice.getTitle()) + .contents(notice.getContents()) + .regDate(notice.getRegDate()) + .updateDate(notice.getUpdateDate()) + .hits(notice.getHits()) + .likes(notice.getLikes()) + .build(); + } +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/notice/model/ResponseError.java b/rest-controller-practice/src/main/java/com/example/restcontroller/notice/model/ResponseError.java new file mode 100644 index 00000000..2582046c --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/notice/model/ResponseError.java @@ -0,0 +1,32 @@ +package com.example.restcontroller.notice.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.validation.FieldError; + +import java.util.List; +import java.util.stream.Collectors; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ResponseError { + private String field; + private String message; + + public static ResponseError of(FieldError e) { + return ResponseError.builder() + .field(e.getField()) + .message(e.getDefaultMessage()) + .build(); + } + + public static List of(List fieldErrors) { + return fieldErrors.stream() + .map(ResponseError::of) + .collect(Collectors.toList()); + } +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/notice/repository/NoticeLikeRepository.java b/rest-controller-practice/src/main/java/com/example/restcontroller/notice/repository/NoticeLikeRepository.java new file mode 100644 index 00000000..e3fd6b00 --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/notice/repository/NoticeLikeRepository.java @@ -0,0 +1,15 @@ +package com.example.restcontroller.notice.repository; + +import com.example.restcontroller.notice.entity.NoticeLike; +import com.example.restcontroller.user.entity.User; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + +@Repository +public interface NoticeLikeRepository extends JpaRepository { + + List findByUser(User user); +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/notice/repository/NoticeRepository.java b/rest-controller-practice/src/main/java/com/example/restcontroller/notice/repository/NoticeRepository.java new file mode 100644 index 00000000..f9d47ed2 --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/notice/repository/NoticeRepository.java @@ -0,0 +1,25 @@ +package com.example.restcontroller.notice.repository; + +import com.example.restcontroller.notice.entity.Notice; +import com.example.restcontroller.user.entity.User; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Optional; + +@Repository +public interface NoticeRepository extends JpaRepository { + + Optional> findByIdIn(List idList); + + // 제목동일, 내용동일, 등록시간이 체크시간보다 크다. + Optional> findByTitleAndContentsAndRegDateIsGreaterThanEqual(String title, String contents, LocalDateTime regDate); + + int countByTitleAndContentsAndRegDateGreaterThanEqual(String title, String contents, LocalDateTime regDate); + + Optional> findByUser(User user); + + int countByUser(User user); +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/user/controller/ApiAdminUserController.java b/rest-controller-practice/src/main/java/com/example/restcontroller/user/controller/ApiAdminUserController.java new file mode 100644 index 00000000..bc60345a --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/user/controller/ApiAdminUserController.java @@ -0,0 +1,141 @@ +package com.example.restcontroller.user.controller; + +import com.example.restcontroller.notice.repository.NoticeRepository; +import com.example.restcontroller.user.entity.User; +import com.example.restcontroller.user.exception.UserNotFoundException; +import com.example.restcontroller.user.model.*; +import com.example.restcontroller.user.repository.UserLoginHistoryRepository; +import com.example.restcontroller.user.repository.UserRepository; +import com.example.restcontroller.user.service.UserService; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Optional; + +@RestController +@RequiredArgsConstructor +public class ApiAdminUserController { + + private final UserRepository userRepository; + private final NoticeRepository noticeRepository; + private final UserLoginHistoryRepository userLoginHistoryRepository; + private final UserService userService; + + @GetMapping("/api/admin/user") + public ResponseEntity chapter2_18() { + UserCount userCount = UserCount.builder() + .totalCount(userRepository.count()) + .data(userRepository.findAll()) + .build(); + + return ResponseEntity.ok(userCount); + } + + @GetMapping("/api/admin/user/{id}") + public ResponseEntity chapter2_19(@PathVariable Long id) { + + Optional userEntity = userRepository.findById(id); + if (userEntity.isEmpty()) { + return ResponseEntity.badRequest().body(ResponseMessage.fail("사용자 정보가 존재하지 않습니다.")); + } + return ResponseEntity.ok().body(ResponseMessage.success(userEntity.get())); + } + + @GetMapping("/api/admin/user/search") + public ResponseEntity chapter2_20(@RequestBody UserSearch userSearch) { + List userList = userRepository.findByEmailContainsAndUserNameContainsAndPhoneContains( + userSearch.getEmail(), userSearch.getUserName(), userSearch.getPhone() + ); + + return ResponseEntity.ok().body(ResponseMessage.success(userList)); + } + + @PatchMapping("/api/admin/user/{id}/status") + public ResponseEntity chapter2_21(@PathVariable Long id) { + User userEntity = userRepository.findById(id) + .orElseThrow(() -> new UserNotFoundException("유저가 없습니다.")); + + String userStatus = userEntity.changeUserStatus(); + userRepository.save(userEntity); + + return ResponseEntity.ok().body(userStatus); + } + + @DeleteMapping("/api/admin/user/{id}") + public ResponseEntity chapter2_22(@PathVariable Long id) { + User userEntity = userRepository.findById(id) + .orElseThrow(() -> new UserNotFoundException("유저가 없습니다.")); + + if (noticeRepository.countByUser(userEntity) > 0) { + return new ResponseEntity<>(ResponseMessage.fail("사용자가 작성한 공지사항이 있습니다."), HttpStatus.BAD_REQUEST); + } + userRepository.deleteById(id); + return ResponseEntity.ok().build(); + } + + @GetMapping("/api/admin/user/login/history") + public ResponseEntity chapter2_23() { + return ResponseEntity.ok().body(ResponseMessage.success(userLoginHistoryRepository.findAll())); + } + + @PatchMapping("/api/admin/user/{id}/lock") + public ResponseEntity chapter2_24(@PathVariable Long id) { + User userEntity = userRepository.findById(id) + .orElseThrow(() -> new UserNotFoundException("유저가 없습니다.")); + + if (userEntity.isLockYn()) { + return ResponseEntity.badRequest().body(ResponseMessage.fail("이미 접속제한이 된 사용자 입니다.")); + } + userEntity.setLockYn(true); + userRepository.save(userEntity); + + return ResponseEntity.ok().body(ResponseMessage.success()); + } + + @PatchMapping("/api/admin/user/{id}/unlock") + public ResponseEntity chapter2_25(@PathVariable Long id) { + User userEntity = userRepository.findById(id) + .orElseThrow(() -> new UserNotFoundException("유저가 없습니다.")); + + if (!userEntity.isLockYn()) { + return ResponseEntity.badRequest().body(ResponseMessage.fail("접속 가능한 사용자 입니다.")); + } + userEntity.setLockYn(false); + userRepository.save(userEntity); + + return ResponseEntity.ok().body(ResponseMessage.success()); + } + + @GetMapping("/api/admin/user/status/count") + public ResponseEntity chapter2_26() { + UserSummary userSummary = userService.getUserStatusCount(); + return ResponseEntity.ok().body(ResponseMessage.success(userSummary)); + } + + @GetMapping("/api/admin/user/today") + public ResponseEntity chapter2_27() { + List todayUsers = userService.getTodayUsers(); + return ResponseEntity.ok().body(ResponseMessage.success(todayUsers)); + } + + @GetMapping("/api/admin/user/notice/count") + public ResponseEntity chapter2_28() { + List userNoticeCountList = userService.getUserNoticeCount(); + return ResponseEntity.ok().body(ResponseMessage.success(userNoticeCountList)); + } + + @GetMapping("/api/admin/user/log/count") + public ResponseEntity chapter2_29() { + List userLogCountList = userService.getUserLogCount(); + return ResponseEntity.ok().body(ResponseMessage.success(userLogCountList)); + } + + @GetMapping("/api/admin/user/like/best") + public ResponseEntity chapter2_30() { + List userLogCountList = userService.getUserLikeBest(); + return ResponseEntity.ok().body(ResponseMessage.success(userLogCountList)); + } +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/user/controller/ApiUserController.java b/rest-controller-practice/src/main/java/com/example/restcontroller/user/controller/ApiUserController.java new file mode 100644 index 00000000..972b1135 --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/user/controller/ApiUserController.java @@ -0,0 +1,355 @@ +package com.example.restcontroller.user.controller; + +import com.auth0.jwt.JWT; +import com.auth0.jwt.algorithms.Algorithm; +import com.auth0.jwt.exceptions.SignatureVerificationException; +import com.example.restcontroller.notice.entity.Notice; +import com.example.restcontroller.notice.entity.NoticeLike; +import com.example.restcontroller.notice.exception.NoticeNotFoundException; +import com.example.restcontroller.notice.model.NoticeResponse; +import com.example.restcontroller.notice.model.ResponseError; +import com.example.restcontroller.notice.repository.NoticeLikeRepository; +import com.example.restcontroller.notice.repository.NoticeRepository; +import com.example.restcontroller.user.entity.User; +import com.example.restcontroller.user.exception.ExistsEmailException; +import com.example.restcontroller.user.exception.PasswordNotMatchException; +import com.example.restcontroller.user.exception.UserNotFoundException; +import com.example.restcontroller.user.model.*; +import com.example.restcontroller.user.repository.UserRepository; +import com.example.restcontroller.util.JWTUtils; +import com.example.restcontroller.util.PasswordUtils; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; +import javax.validation.Valid; +import java.sql.Timestamp; +import java.time.LocalDateTime; +import java.util.Date; +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; + +@RestController +@RequiredArgsConstructor +public class ApiUserController { + + private final UserRepository userRepository; + private final NoticeRepository noticeRepository; + private final NoticeLikeRepository noticeLikeRepository; + + @PostMapping("/api/user") + public ResponseEntity chapter2_1(@RequestBody @Valid UserInput userInput, BindingResult bindingResult) { + if (bindingResult.hasFieldErrors()) { + List errors = bindingResult.getFieldErrors().stream() + .map(ResponseError::of) + .collect(Collectors.toList()); + + return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST); + } + return new ResponseEntity<>(HttpStatus.CREATED); + } + + @PostMapping("/api/user2") + public ResponseEntity chapter2_2(@RequestBody @Valid UserInput userInput, BindingResult bindingResult) { + if (bindingResult.hasFieldErrors()) { + List errors = bindingResult.getFieldErrors().stream() + .map(ResponseError::of) + .collect(Collectors.toList()); + + return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST); + } + userRepository.save(new User(userInput)); + return new ResponseEntity<>(HttpStatus.CREATED); + } + + @PutMapping("/api/user/{id}") + public ResponseEntity chapter2_3(@PathVariable Long id, @RequestBody @Valid UserUpdate userUpdate, BindingResult bindingResult) { + if (bindingResult.hasFieldErrors()) { + List errors = bindingResult.getFieldErrors().stream() + .map(ResponseError::of) + .collect(Collectors.toList()); + + return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST); + } + + User userEntity = userRepository.findById(id) + .orElseThrow(() -> new UserNotFoundException("사용자 정보가 없습니다.")); + + User user = User.updateUser(userEntity, userUpdate.getPhone()); + userRepository.save(user); + + return new ResponseEntity<>(HttpStatus.OK); + } + + @GetMapping("/api/user/{id}") + public ResponseEntity chapter2_4(@PathVariable Long id) { + User userEntity = userRepository.findById(id) + .orElseThrow(() -> new UserNotFoundException("사용자 정보가 없습니다.")); + +// UserResponse response = new UserResponse(userEntity); + UserResponse response = UserResponse.of(userEntity); + + return new ResponseEntity<>(response, HttpStatus.OK); + + } + + @GetMapping("/api/user/{id}/notice") + public List chapter2_5(@PathVariable Long id) { + User userEntity = userRepository.findById(id) + .orElseThrow(() -> new UserNotFoundException("사용자 정보가 없습니다.")); + + List noticeList = noticeRepository.findByUser(userEntity) + .orElseThrow(() -> new NoticeNotFoundException("공지사항이 없습니다.")); + + return noticeList.stream().map(NoticeResponse::of).collect(Collectors.toList()); + } + + @PostMapping("/api/user3") + public ResponseEntity chapter2_6(@RequestBody @Valid UserInput userInput, BindingResult bindingResult) { + if (bindingResult.hasFieldErrors()) { + List errors = bindingResult.getFieldErrors().stream() + .map(ResponseError::of) + .collect(Collectors.toList()); + + return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST); + } + + if (userRepository.countByEmail(userInput.getEmail()) > 0) { + throw new ExistsEmailException("이미 존재하는 이메일 입니다."); + } + + userRepository.save(new User(userInput)); + + return new ResponseEntity<>(HttpStatus.CREATED); + } + + @PatchMapping("/api/user/{id}/password") + public ResponseEntity chapter2_7(@PathVariable Long id, @RequestBody UserPasswordUpdate userPasswordUpdate, BindingResult bindingResult) { + if (bindingResult.hasFieldErrors()) { + List errors = bindingResult.getFieldErrors().stream() + .map(ResponseError::of) + .collect(Collectors.toList()); + + return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST); + } + + User userEntity = userRepository.findByIdAndPassword(id, userPasswordUpdate.getPassword()) + .orElseThrow(() -> new PasswordNotMatchException("비밀번호가 일치하지 않습니다.")); + + userEntity.setPassword(userPasswordUpdate.getNewPassword()); + + userRepository.save(userEntity); + + return new ResponseEntity<>(HttpStatus.OK); + } + + private String getEncryptPassword(String password) { + BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); + return passwordEncoder.encode(password); + } + + @PostMapping("/api/user4") + public ResponseEntity chapter2_8(@RequestBody @Valid UserInput userInput, BindingResult bindingResult) { + if (bindingResult.hasFieldErrors()) { + List errors = bindingResult.getFieldErrors().stream() + .map(ResponseError::of) + .collect(Collectors.toList()); + + return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST); + } + + userRepository.save(User.builder() + .email(userInput.getEmail()) + .userName(userInput.getUserName()) + .password(getEncryptPassword(userInput.getPassword())) + .phone(userInput.getPhone()) + .regDate(LocalDateTime.now()) + .build()); + return new ResponseEntity<>(HttpStatus.CREATED); + } + + @DeleteMapping("/api/user/{id}") + public ResponseEntity chapter2_9(@PathVariable Long id) { + User userEntity = userRepository.findById(id) + .orElseThrow(() -> new UserNotFoundException("사용자가 없습니다.")); + + userRepository.deleteById(id); + return new ResponseEntity<>(HttpStatus.OK); + } + + @GetMapping("/api/user") + public ResponseEntity chapter2_10(@Valid FindUserId findUserId, BindingResult bindingResult) { + if (bindingResult.hasFieldErrors()) { + List errors = bindingResult.getFieldErrors().stream() + .map(ResponseError::of) + .collect(Collectors.toList()); + + return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST); + } + + User userEntity = userRepository.findByUserNameAndPhone(findUserId.getUserName(), findUserId.getPhone()) + .orElseThrow(() -> new UserNotFoundException("사용자가 없습니다.")); + + return new ResponseEntity<>(userEntity.getEmail(), HttpStatus.OK); + } + + private String getResetPassword() { + return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 10); + } + + void sendSMS(String message) { + System.out.println("문자메시지 전송 : " + message); + } + + @GetMapping("/api/user/{id}/password/reset") + public ResponseEntity chapter2_11(@PathVariable Long id) { + User userEntity = userRepository.findById(id) + .orElseThrow(() -> new UserNotFoundException("사용자가 없습니다.")); + + String temp = getResetPassword(); + userEntity.setPassword(getEncryptPassword(temp)); + + userRepository.save(userEntity); + + String message = String.format("[%s]님의 임시비밀번호가 [%s]로 초기화 되었습니다.", + userEntity.getUserName(), + temp); + sendSMS(message); + return ResponseEntity.ok().build(); + } + + @GetMapping("/api/user/{id}/notice/like") + public ResponseEntity chapter2_12(@PathVariable Long id) { + User userEntity = userRepository.findById(id) + .orElseThrow(() -> new UserNotFoundException("사용자가 없습니다.")); + + List noticeLikeList = noticeLikeRepository.findByUser(userEntity); + + return ResponseEntity.ok().body(noticeLikeList); + } + + @PostMapping("/api/user/login") + public ResponseEntity chapter2_13(@RequestBody @Valid UserLogin userLogin, BindingResult bindingResult) { + if (bindingResult.hasFieldErrors()) { + List errors = bindingResult.getFieldErrors().stream() + .map(ResponseError::of) + .collect(Collectors.toList()); + + return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST); + } + + User userEntity = userRepository.findByEmail(userLogin.getEmail()) + .orElseThrow(() -> new UserNotFoundException("사용자가 없습니다.")); + + if (!PasswordUtils.equalPassword(userLogin.getPassword(), userEntity.getPassword())) { + throw new PasswordNotMatchException("비밀번호가 일치하지 않습니다."); + } + + return ResponseEntity.ok().build(); + } + + @PostMapping("/api/user/login2") + public ResponseEntity chapter2_14(@RequestBody @Valid UserLogin userLogin, BindingResult bindingResult) { + if (bindingResult.hasFieldErrors()) { + List errors = bindingResult.getFieldErrors().stream() + .map(ResponseError::of) + .collect(Collectors.toList()); + + return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST); + } + + User userEntity = userRepository.findByEmail(userLogin.getEmail()) + .orElseThrow(() -> new UserNotFoundException("사용자가 없습니다.")); + + if (!PasswordUtils.equalPassword(userLogin.getPassword(), userEntity.getPassword())) { + throw new PasswordNotMatchException("비밀번호가 일치하지 않습니다."); + } + + String token = JWT.create() + .withExpiresAt(new Date()) + .withClaim("user_id", userEntity.getId()) + .withSubject(userEntity.getEmail()) + .withIssuer(userEntity.getEmail()) + .sign(Algorithm.HMAC512("kim".getBytes())); + + + return ResponseEntity.ok().body(UserLoginToken.builder().token(token).build()); + } + + @PostMapping("/api/user/login3") + public ResponseEntity chapter2_15(@RequestBody @Valid UserLogin userLogin, BindingResult bindingResult) { + if (bindingResult.hasFieldErrors()) { + List errors = bindingResult.getFieldErrors().stream() + .map(ResponseError::of) + .collect(Collectors.toList()); + + return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST); + } + + User userEntity = userRepository.findByEmail(userLogin.getEmail()) + .orElseThrow(() -> new UserNotFoundException("사용자가 없습니다.")); + + if (!PasswordUtils.equalPassword(userLogin.getPassword(), userEntity.getPassword())) { + throw new PasswordNotMatchException("비밀번호가 일치하지 않습니다."); + } + + + String token = JWT.create() + .withExpiresAt(Timestamp.valueOf(LocalDateTime.now().plusMinutes(30))) + .withClaim("user_id", userEntity.getId()) + .withSubject(userEntity.getEmail()) + .withIssuer(userEntity.getEmail()) + .sign(Algorithm.HMAC512("kim".getBytes())); + + + return ResponseEntity.ok().body(UserLoginToken.builder().token(token).build()); + } + + @PatchMapping("/api/user/login") + public ResponseEntity chapter2_16(HttpServletRequest request) { + String token = request.getHeader("TOKEN"); + + String email; + try { + email = JWT.require(Algorithm.HMAC512("kim".getBytes())) + .build() + .verify(token) + .getIssuer(); + } catch (SignatureVerificationException e) { + throw new PasswordNotMatchException("유효한 토큰이 아닙니다."); + } + + User userEntity = userRepository.findByEmail(email) + .orElseThrow(() -> new UserNotFoundException("사용자 정보 없음")); + + String newToken = JWT.create() + .withExpiresAt(Timestamp.valueOf(LocalDateTime.now().plusMinutes(30))) + .withClaim("user_id", userEntity.getId()) + .withSubject(userEntity.getEmail()) + .withIssuer(userEntity.getEmail()) + .sign(Algorithm.HMAC512("kim".getBytes())); + + return ResponseEntity.ok().body(UserLoginToken.builder().token(newToken).build()); + } + + @DeleteMapping("/api/user/login") + public ResponseEntity chapter2_17(@RequestHeader("TOKEN") String token) { + try { + String email = JWTUtils.getIssuer(token); + } catch (SignatureVerificationException e) { + return new ResponseEntity<>("토큰이 유효하지 않습니다.", HttpStatus.BAD_REQUEST); + } + + // 세션, 쿠키삭제 + // 클라이언트 쿠키/로컬스토리지/세션스토리지 + // 블랙리스트 작성 + // ... + + return ResponseEntity.ok().build(); + } +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/user/entity/User.java b/rest-controller-practice/src/main/java/com/example/restcontroller/user/entity/User.java new file mode 100644 index 00000000..c3cae243 --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/user/entity/User.java @@ -0,0 +1,94 @@ +package com.example.restcontroller.user.entity; + +import com.example.restcontroller.board.entity.Board; +import com.example.restcontroller.board.entity.BoardHits; +import com.example.restcontroller.board.entity.BoardLike; +import com.example.restcontroller.board.entity.BoardType; +import com.example.restcontroller.notice.entity.Notice; +import com.example.restcontroller.notice.entity.NoticeLike; +import com.example.restcontroller.user.model.UserInput; +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.*; + +import javax.persistence.*; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + + +@Getter @Setter +@AllArgsConstructor +@NoArgsConstructor +@Builder +@Entity +public class User { + + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(unique = true, nullable = false) + private String email; + + @Column(nullable = false) + private String userName; + + @Column(nullable = false) + private String password; + + @Column(nullable = false) + private String phone; + + @Enumerated(EnumType.STRING) + private UserStatus status; + + @Column + private boolean lockYn; + + private LocalDateTime regDate; + private LocalDateTime updateDate; + + @JsonIgnore + @OneToMany(mappedBy = "user") + List noticeList = new ArrayList<>(); + + @JsonIgnore + @OneToMany(mappedBy = "user") + List noticeLikeList = new ArrayList<>(); + + @JsonIgnore + @OneToMany(mappedBy = "user") + List boardList = new ArrayList<>(); + + @JsonIgnore + @OneToMany(mappedBy = "user") + List boardHitsList = new ArrayList<>(); + + @JsonIgnore + @OneToMany(mappedBy = "user") + List boardLikeList = new ArrayList<>(); + + @PrePersist + public void prePersist() { + status = status == null ? UserStatus.USING : status; + } + + public User(UserInput userInput) { + email = userInput.getEmail(); + userName = userInput.getUserName(); + password = userInput.getPassword(); + phone = userInput.getPhone(); + regDate = LocalDateTime.now(); + } + + public static User updateUser(User user, String phone) { + user.phone = phone; + user.updateDate = LocalDateTime.now(); + return user; + } + + public String changeUserStatus() { + status = status.equals(UserStatus.USING) ? UserStatus.STOP : UserStatus.USING; + return status.toString(); + } + +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/user/entity/UserLoginHistory.java b/rest-controller-practice/src/main/java/com/example/restcontroller/user/entity/UserLoginHistory.java new file mode 100644 index 00000000..e68fef00 --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/user/entity/UserLoginHistory.java @@ -0,0 +1,35 @@ +package com.example.restcontroller.user.entity; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; +import java.time.LocalDateTime; + +@Entity +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class UserLoginHistory { + + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column + private Long userId; + + @Column + private String email; + + @Column + private String userName; + + @Column + private LocalDateTime loginDate; + + @Column + private String ipAddr; +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/user/entity/UserStatus.java b/rest-controller-practice/src/main/java/com/example/restcontroller/user/entity/UserStatus.java new file mode 100644 index 00000000..ab63f55f --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/user/entity/UserStatus.java @@ -0,0 +1,14 @@ +package com.example.restcontroller.user.entity; + +public enum UserStatus { + NONE, USING, STOP; + + int value; + + UserStatus() { + } + + public int getValue() { + return this.value; + } +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/user/exception/ExistsEmailException.java b/rest-controller-practice/src/main/java/com/example/restcontroller/user/exception/ExistsEmailException.java new file mode 100644 index 00000000..8d2c0840 --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/user/exception/ExistsEmailException.java @@ -0,0 +1,7 @@ +package com.example.restcontroller.user.exception; + +public class ExistsEmailException extends RuntimeException { + public ExistsEmailException(String s) { + super(s); + } +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/user/exception/PasswordNotMatchException.java b/rest-controller-practice/src/main/java/com/example/restcontroller/user/exception/PasswordNotMatchException.java new file mode 100644 index 00000000..5d55bf6b --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/user/exception/PasswordNotMatchException.java @@ -0,0 +1,7 @@ +package com.example.restcontroller.user.exception; + +public class PasswordNotMatchException extends RuntimeException { + public PasswordNotMatchException(String message) { + super(message); + } +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/user/exception/UserNotFoundException.java b/rest-controller-practice/src/main/java/com/example/restcontroller/user/exception/UserNotFoundException.java new file mode 100644 index 00000000..e74dab6c --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/user/exception/UserNotFoundException.java @@ -0,0 +1,8 @@ +package com.example.restcontroller.user.exception; + +public class UserNotFoundException extends RuntimeException{ + + public UserNotFoundException(String message) { + super(message); + } +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/user/model/FindUserId.java b/rest-controller-practice/src/main/java/com/example/restcontroller/user/model/FindUserId.java new file mode 100644 index 00000000..8fe3fd9a --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/user/model/FindUserId.java @@ -0,0 +1,24 @@ +package com.example.restcontroller.user.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.Email; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; + +@AllArgsConstructor +@NoArgsConstructor +@Data +@Builder +public class FindUserId { + + @NotBlank(message = "이름은 필수 항목 입니다.") + private String userName; + + @NotBlank(message = "연락처는 필수 항목 입니다.") + @Size(max = 20, message = "연락처는 최대 20자까지 입력해야 합니다.") + private String phone; +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/user/model/ResponseMessage.java b/rest-controller-practice/src/main/java/com/example/restcontroller/user/model/ResponseMessage.java new file mode 100644 index 00000000..d66b3739 --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/user/model/ResponseMessage.java @@ -0,0 +1,50 @@ +package com.example.restcontroller.user.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.http.HttpStatus; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class ResponseMessage { + + private ResponseMessageHeader header; + + private T body; + + public static ResponseMessage fail(String message, T data) { + return ResponseMessage.builder() + .header(ResponseMessageHeader.builder() + .result(false) + .resultCode(HttpStatus.BAD_REQUEST.value()) + .message(message) + .status(HttpStatus.BAD_REQUEST) + .build()) + .body(data) + .build(); + } + + public static ResponseMessage fail(String message) { + return fail(message, null); + } + + public static ResponseMessage success(T data) { + return ResponseMessage.builder() + .header(ResponseMessageHeader.builder() + .result(true) + .resultCode(HttpStatus.OK.value()) + .message("") + .status(HttpStatus.OK) + .build()) + .body(data) + .build(); + } + + public static ResponseMessage success() { + return success(null); + } +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/user/model/ResponseMessageHeader.java b/rest-controller-practice/src/main/java/com/example/restcontroller/user/model/ResponseMessageHeader.java new file mode 100644 index 00000000..a55ee0c9 --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/user/model/ResponseMessageHeader.java @@ -0,0 +1,20 @@ +package com.example.restcontroller.user.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.http.HttpStatus; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class ResponseMessageHeader { + + private boolean result; + private int resultCode; + private String message; + private HttpStatus status; + +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/user/model/UserCount.java b/rest-controller-practice/src/main/java/com/example/restcontroller/user/model/UserCount.java new file mode 100644 index 00000000..2d0b0eaf --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/user/model/UserCount.java @@ -0,0 +1,20 @@ +package com.example.restcontroller.user.model; + +import com.example.restcontroller.user.entity.User; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class UserCount { + + private Long totalCount; + + private List data; +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/user/model/UserInput.java b/rest-controller-practice/src/main/java/com/example/restcontroller/user/model/UserInput.java new file mode 100644 index 00000000..53d38866 --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/user/model/UserInput.java @@ -0,0 +1,31 @@ +package com.example.restcontroller.user.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.Email; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class UserInput { + + @NotBlank(message = "이메일은 필수 항목 입니다.") + @Email(message = "이메일 형식에 맞게 입력해 주세요.") + private String email; + + @NotBlank(message = "이름은 필수 항목 입니다.") + private String userName; + + @NotBlank(message = "비밀번호는 필수 항목 입니다.") + @Size(min = 4, message = "비밀번호는 4자 이상 입력해야 합니다.") + private String password; + + @NotBlank(message = "연락처는 필수 항목 입니다.") + @Size(max = 20, message = "연락처는 최대 20자까지 입력해야 합니다.") + private String phone; +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/user/model/UserLogCount.java b/rest-controller-practice/src/main/java/com/example/restcontroller/user/model/UserLogCount.java new file mode 100644 index 00000000..19202697 --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/user/model/UserLogCount.java @@ -0,0 +1,20 @@ +package com.example.restcontroller.user.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class UserLogCount { + + private Long id; + private String email; + private String userName; + + private Long noticeCount; + private Long noticeLikeCount; +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/user/model/UserLogin.java b/rest-controller-practice/src/main/java/com/example/restcontroller/user/model/UserLogin.java new file mode 100644 index 00000000..42269c87 --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/user/model/UserLogin.java @@ -0,0 +1,21 @@ +package com.example.restcontroller.user.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotBlank; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class UserLogin { + + @NotBlank(message = "이메일 항목은 필수 입니다.") + private String email; + + @NotBlank(message = "비밀번호 항목은 필수 입니다.") + private String password; +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/user/model/UserLoginToken.java b/rest-controller-practice/src/main/java/com/example/restcontroller/user/model/UserLoginToken.java new file mode 100644 index 00000000..f2ff596f --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/user/model/UserLoginToken.java @@ -0,0 +1,15 @@ +package com.example.restcontroller.user.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class UserLoginToken { + + private String token; +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/user/model/UserNoticeCount.java b/rest-controller-practice/src/main/java/com/example/restcontroller/user/model/UserNoticeCount.java new file mode 100644 index 00000000..7e4a68cb --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/user/model/UserNoticeCount.java @@ -0,0 +1,29 @@ +package com.example.restcontroller.user.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class UserNoticeCount { + + private Long id; + private String email; + private String userName; + + private Long noticeCount; + + public static UserNoticeCount of(Object[] data) { + return UserNoticeCount.builder() + .id((Long) data[0]) + .email((String) data[1]) + .userName((String) data[2]) + .noticeCount((Long) data[3]) + .build(); + } + +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/user/model/UserPasswordUpdate.java b/rest-controller-practice/src/main/java/com/example/restcontroller/user/model/UserPasswordUpdate.java new file mode 100644 index 00000000..a637ecc6 --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/user/model/UserPasswordUpdate.java @@ -0,0 +1,23 @@ +package com.example.restcontroller.user.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class UserPasswordUpdate { + + @NotBlank(message = "비밀번호를 입력하세요.") + private String password; + + @Size(min = 4, max = 20, message = "비밀번호는 4-20 사이의 길이로 입력해 주세요.") + @NotBlank(message = "비밀번호를 입력하세요.") + private String newPassword; +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/user/model/UserResponse.java b/rest-controller-practice/src/main/java/com/example/restcontroller/user/model/UserResponse.java new file mode 100644 index 00000000..152a5321 --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/user/model/UserResponse.java @@ -0,0 +1,33 @@ +package com.example.restcontroller.user.model; + +import com.example.restcontroller.user.entity.User; +import lombok.*; + + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class UserResponse { + + private Long id; + private String email; + private String userName; + private String phone; + + public UserResponse(User user) { + this.id = user.getId(); + this.email = user.getEmail(); + this.userName = user.getUserName(); + this.phone = user.getPhone(); + } + + public static UserResponse of(User user) { + return UserResponse.builder() + .id(user.getId()) + .email(user.getEmail()) + .userName(user.getUserName()) + .phone(user.getPhone()) + .build(); + } +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/user/model/UserSearch.java b/rest-controller-practice/src/main/java/com/example/restcontroller/user/model/UserSearch.java new file mode 100644 index 00000000..b77c4cd2 --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/user/model/UserSearch.java @@ -0,0 +1,17 @@ +package com.example.restcontroller.user.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class UserSearch { + + private String email; + private String userName; + private String phone; +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/user/model/UserSummary.java b/rest-controller-practice/src/main/java/com/example/restcontroller/user/model/UserSummary.java new file mode 100644 index 00000000..af66ef81 --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/user/model/UserSummary.java @@ -0,0 +1,17 @@ +package com.example.restcontroller.user.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class UserSummary { + + private Long totalUserCount; + private Long usingUserCount; + private Long stopUserCount; +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/user/model/UserUpdate.java b/rest-controller-practice/src/main/java/com/example/restcontroller/user/model/UserUpdate.java new file mode 100644 index 00000000..6f681cc1 --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/user/model/UserUpdate.java @@ -0,0 +1,20 @@ +package com.example.restcontroller.user.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class UserUpdate { + + @Size(max = 20, message = "연락처는 최대 20자 까지 입력해야 합니다.") + @NotBlank(message = "연락처는 필수 항목 입니다.") + private String phone; +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/user/repository/UserCustomRepository.java b/rest-controller-practice/src/main/java/com/example/restcontroller/user/repository/UserCustomRepository.java new file mode 100644 index 00000000..37cab446 --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/user/repository/UserCustomRepository.java @@ -0,0 +1,40 @@ +package com.example.restcontroller.user.repository; + +import com.example.restcontroller.user.model.UserNoticeCount; +import com.example.restcontroller.user.model.UserLogCount; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +import javax.persistence.EntityManager; +import java.util.List; + +@Repository +@RequiredArgsConstructor +public class UserCustomRepository { + + private final EntityManager entityManager; + + public List findUserNoticeCount() { + String sql = "select u.id, u.email, u.user_name, (select count(*) from Notice n where n.user_id = u.id) notice_count from User u "; + List list = entityManager.createNativeQuery(sql).getResultList(); + return list; + } + + public List findUserLogCount() { + String sql = "select u.id, u.email, u.user_name" + + ", (select count(*) from notice n where n.user_id = u.id) notice_count" + + ", (select count(*) from notice_like nl where nl.user_id = u.id) notice_like_count" + + " from user u "; + List list = entityManager.createNativeQuery(sql).getResultList(); + return list; + } + + public List findUserLikeBest() { + String sql = "select u.id, u.email, u.user_name" + + ", (select count(*) from notice_like nl where nl.user_id = u.id) notice_like_count" + + " from user u " + + " order by notice_like_count desc"; + List list = entityManager.createNativeQuery(sql).getResultList(); + return list; + } +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/user/repository/UserLoginHistoryRepository.java b/rest-controller-practice/src/main/java/com/example/restcontroller/user/repository/UserLoginHistoryRepository.java new file mode 100644 index 00000000..f0d65305 --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/user/repository/UserLoginHistoryRepository.java @@ -0,0 +1,9 @@ +package com.example.restcontroller.user.repository; + +import com.example.restcontroller.user.entity.UserLoginHistory; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface UserLoginHistoryRepository extends JpaRepository { +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/user/repository/UserRepository.java b/rest-controller-practice/src/main/java/com/example/restcontroller/user/repository/UserRepository.java new file mode 100644 index 00000000..3da1b6a7 --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/user/repository/UserRepository.java @@ -0,0 +1,33 @@ +package com.example.restcontroller.user.repository; + +import com.example.restcontroller.user.entity.User; +import com.example.restcontroller.user.entity.UserStatus; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Optional; + +@Repository +public interface UserRepository extends JpaRepository { + + int countByEmail(String email); + + Optional findByIdAndPassword(Long id, String password); + Optional findByUserNameAndPhone(String userName, String phone); + + Optional findByEmail(String email); + + List findByEmailContainsAndUserNameContainsAndPhoneContains(String email, String userName, String phone); + + Long countByStatus(UserStatus userStatus); + + List findByRegDateBetween(LocalDateTime startDate, LocalDateTime endDate); + + @Query("select u from User u where u.regDate between :startDate and :endDate") + List findTodayUsers(LocalDateTime startDate, LocalDateTime endDate); + +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/user/service/UserService.java b/rest-controller-practice/src/main/java/com/example/restcontroller/user/service/UserService.java new file mode 100644 index 00000000..b719d5bc --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/user/service/UserService.java @@ -0,0 +1,21 @@ +package com.example.restcontroller.user.service; + +import com.example.restcontroller.user.model.UserNoticeCount; +import com.example.restcontroller.user.model.UserLogCount; +import com.example.restcontroller.user.model.UserResponse; +import com.example.restcontroller.user.model.UserSummary; + +import java.util.List; + +public interface UserService { + + UserSummary getUserStatusCount(); + + List getTodayUsers(); + + List getUserNoticeCount(); + + List getUserLogCount(); + + List getUserLikeBest(); +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/user/service/UserServiceImpl.java b/rest-controller-practice/src/main/java/com/example/restcontroller/user/service/UserServiceImpl.java new file mode 100644 index 00000000..c9d56320 --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/user/service/UserServiceImpl.java @@ -0,0 +1,65 @@ +package com.example.restcontroller.user.service; + + +import com.example.restcontroller.user.entity.UserStatus; +import com.example.restcontroller.user.model.UserNoticeCount; +import com.example.restcontroller.user.model.UserLogCount; +import com.example.restcontroller.user.model.UserResponse; +import com.example.restcontroller.user.model.UserSummary; +import com.example.restcontroller.user.repository.UserCustomRepository; +import com.example.restcontroller.user.repository.UserRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.List; +import java.util.stream.Collectors; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class UserServiceImpl implements UserService { + + private final UserRepository userRepository; + private final UserCustomRepository userCustomRepository; + + @Override + public UserSummary getUserStatusCount() { + Long usingUserCount = userRepository.countByStatus(UserStatus.USING); + Long stopUserCount = userRepository.countByStatus(UserStatus.STOP); + Long totalUserCount = userRepository.count(); + return UserSummary.builder() + .stopUserCount(stopUserCount) + .usingUserCount(usingUserCount) + .totalUserCount(totalUserCount) + .build(); + } + + @Override + public List getTodayUsers() { + + LocalDateTime startDate = LocalDateTime.of(LocalDate.now(), LocalTime.of(0, 0)); + LocalDateTime endDate = startDate.plusDays(1); + + return userRepository.findTodayUsers(startDate, endDate) + .stream().map(UserResponse::of).collect(Collectors.toList()); + } + + @Override + public List getUserNoticeCount() { + return userCustomRepository.findUserNoticeCount(); + } + + @Override + public List getUserLogCount() { + return userCustomRepository.findUserLogCount(); + } + + @Override + public List getUserLikeBest() { + return userCustomRepository.findUserLikeBest(); + } +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/util/JWTUtils.java b/rest-controller-practice/src/main/java/com/example/restcontroller/util/JWTUtils.java new file mode 100644 index 00000000..d992ba56 --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/util/JWTUtils.java @@ -0,0 +1,21 @@ +package com.example.restcontroller.util; + +import com.auth0.jwt.JWT; +import com.auth0.jwt.algorithms.Algorithm; +import com.auth0.jwt.exceptions.SignatureVerificationException; +import com.example.restcontroller.user.exception.PasswordNotMatchException; +import lombok.experimental.UtilityClass; +import org.springframework.security.crypto.bcrypt.BCrypt; + +@UtilityClass +public class JWTUtils { + + private final String KEY = "kim"; + + public static String getIssuer(String token) { + return JWT.require(Algorithm.HMAC512(KEY.getBytes())) + .build() + .verify(token) + .getIssuer(); + } +} diff --git a/rest-controller-practice/src/main/java/com/example/restcontroller/util/PasswordUtils.java b/rest-controller-practice/src/main/java/com/example/restcontroller/util/PasswordUtils.java new file mode 100644 index 00000000..4efc0f96 --- /dev/null +++ b/rest-controller-practice/src/main/java/com/example/restcontroller/util/PasswordUtils.java @@ -0,0 +1,12 @@ +package com.example.restcontroller.util; + +import lombok.experimental.UtilityClass; +import org.springframework.security.crypto.bcrypt.BCrypt; + +@UtilityClass +public class PasswordUtils { + + public static boolean equalPassword(String password, String encryptedPassword) { + return BCrypt.checkpw(password, encryptedPassword); + } +} diff --git a/rest-controller-practice/src/main/resources/application.yml b/rest-controller-practice/src/main/resources/application.yml new file mode 100644 index 00000000..27fc1d1f --- /dev/null +++ b/rest-controller-practice/src/main/resources/application.yml @@ -0,0 +1,39 @@ +spring: + h2: + console: + enabled: true + path: /h2-console + + datasource: + url: jdbc:h2:mem:testDb + #url: jdbc:h2:file:/Users/parkkyutae/Documents/sources/github/fastcampus-demo-01/record-example/sample1/backofficeDb + driver-class-name: org.h2.Driver + username: sa + password: + + jpa: + hibernate: + ddl-auto: create-drop + generate-ddl: true + + properties: + hibernate: + format_sql: true + show-sql: true + + jackson: + serialization: + fail-on-empty-beans: false + + mvc: + hiddenmethod: + filter: + enabled: true + + mustache: + suffix: .html + +logging: + level: + org.hibernate.SQL: trace + org.hibernate.type: trace \ No newline at end of file diff --git a/rest-controller-practice/src/main/resources/data.sql b/rest-controller-practice/src/main/resources/data.sql new file mode 100644 index 00000000..0c8207c7 --- /dev/null +++ b/rest-controller-practice/src/main/resources/data.sql @@ -0,0 +1,51 @@ + +insert into user(email, password, phone, reg_date, user_name, status, lock_yn) +values + ('test11@naver.com', '$2a$10$2ElPbt2mwiAc9IYH3rgJz.YZInXlUd363utdyU0TWfne6Y3vKh8h6', '010-1234-1234', '2021-02-20 00:50:11.000000', 'kim', 'USING', 0), + ('test22@gmail.com', '$2a$10$2ElPbt2mwiAc9IYH3rgJz.YZInXlUd363utdyU0TWfne6Y3vKh8h6', '010-4321-1111', '2021-02-25 12:33:16.000000', 'lee', 'USING', 0), + ('test33@naver.com', '$2a$10$2ElPbt2mwiAc9IYH3rgJz.YZInXlUd363utdyU0TWfne6Y3vKh8h6', '010-5555-3333', now(), 'hong', 'USING', 0), + ('test44@gmail.com', '$2a$10$2ElPbt2mwiAc9IYH3rgJz.YZInXlUd363utdyU0TWfne6Y3vKh8h6', '010-4343-2546', now(), 'park', 'STOP', 0); + + +insert into notice(contents, reg_date, title, user_id) +values + ('내용1', now(), '제목1', 1), + ('내용2', now(), '제목2', 1), + ('내용3', now(), '제목3', 1), + ('내용4', now(), '제목4', 2), + ('내용5', now(), '제목5', 2), + ('내용6', now(), '제목6', 3), + ('내용7', now(), '제목7', 3), + ('내용8', now(), '제목8', 3), + ('내용9', now(), '제목9', 3); + +insert into notice_like(notice_id, user_id) +values + (1, 1), + (1, 2), + (2, 1), + (2, 2), + (2, 3), + (3, 1), + (4, 1), + (4, 2), + (5, 2), + (6, 4), + (7, 1), + (7, 4); + +insert into board_type (board_name, reg_date, using_yn) +values + ('게시판1', now(), 1), + ('게시판2', now(), 0), + ('게시판3', now(), 1); + +insert into board (board_type_id, user_id, title, content, reg_date, top_yn) +values + (1, 1, '게시글1', '게시글 내용1', now(), 0), + (1, 2, '게시글2', '게시글 내용2', now(), 0), + (1, 3, '게시글3', '게시글 내용3', now(), 0), + (2, 1, '게시글4', '게시글 내용4', now(), 0), + (2, 2, '게시글5', '게시글 내용5', now(), 0), + (3, 1, '게시글6', '게시글 내용6', now(), 0), + (3, 3, '게시글7', '게시글 내용7', now(), 0); \ No newline at end of file diff --git a/rest-controller-practice/src/test/java/com/example/restcontroller/RestControllerApplicationTests.java b/rest-controller-practice/src/test/java/com/example/restcontroller/RestControllerApplicationTests.java new file mode 100644 index 00000000..24d036f7 --- /dev/null +++ b/rest-controller-practice/src/test/java/com/example/restcontroller/RestControllerApplicationTests.java @@ -0,0 +1,13 @@ +package com.example.restcontroller; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class RestControllerApplicationTests { + + @Test + void contextLoads() { + } + +}