diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..b8e774cf --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea +*.ser \ No newline at end of file diff --git a/discordit/.gitattributes b/discordit/.gitattributes new file mode 100644 index 00000000..8af972cd --- /dev/null +++ b/discordit/.gitattributes @@ -0,0 +1,3 @@ +/gradlew text eol=lf +*.bat text eol=crlf +*.jar binary diff --git a/discordit/.gitignore b/discordit/.gitignore new file mode 100644 index 00000000..bb905cd0 --- /dev/null +++ b/discordit/.gitignore @@ -0,0 +1,179 @@ +# Created by https://www.toptal.com/developers/gitignore/api/windows,macos,intellij+all,java +# Edit at https://www.toptal.com/developers/gitignore?templates=windows,macos,intellij+all,java + +### Intellij+all ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +.idea + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### Intellij+all Patch ### +# Ignore everything but code style settings and run configurations +# that are supposed to be shared within teams. + +.idea/* + +!.idea/codeStyles +!.idea/runConfigurations + +### Java ### +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* +replay_pid* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### macOS Patch ### +# iCloud generated files +*.icloud + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.toptal.com/developers/gitignore/api/windows,macos,intellij+all,java \ No newline at end of file diff --git a/discordit/.gradle/8.14.4/checksums/checksums.lock b/discordit/.gradle/8.14.4/checksums/checksums.lock new file mode 100644 index 00000000..4392a487 Binary files /dev/null and b/discordit/.gradle/8.14.4/checksums/checksums.lock differ diff --git a/discordit/.gradle/8.14.4/checksums/md5-checksums.bin b/discordit/.gradle/8.14.4/checksums/md5-checksums.bin new file mode 100644 index 00000000..b7be7f87 Binary files /dev/null and b/discordit/.gradle/8.14.4/checksums/md5-checksums.bin differ diff --git a/discordit/.gradle/8.14.4/checksums/sha1-checksums.bin b/discordit/.gradle/8.14.4/checksums/sha1-checksums.bin new file mode 100644 index 00000000..674793b4 Binary files /dev/null and b/discordit/.gradle/8.14.4/checksums/sha1-checksums.bin differ diff --git a/discordit/.gradle/8.14.4/executionHistory/executionHistory.lock b/discordit/.gradle/8.14.4/executionHistory/executionHistory.lock new file mode 100644 index 00000000..8c0651e8 Binary files /dev/null and b/discordit/.gradle/8.14.4/executionHistory/executionHistory.lock differ diff --git a/discordit/.gradle/8.14.4/fileChanges/last-build.bin b/discordit/.gradle/8.14.4/fileChanges/last-build.bin new file mode 100644 index 00000000..f76dd238 Binary files /dev/null and b/discordit/.gradle/8.14.4/fileChanges/last-build.bin differ diff --git a/discordit/.gradle/8.14.4/fileHashes/fileHashes.bin b/discordit/.gradle/8.14.4/fileHashes/fileHashes.bin new file mode 100644 index 00000000..6b97a148 Binary files /dev/null and b/discordit/.gradle/8.14.4/fileHashes/fileHashes.bin differ diff --git a/discordit/.gradle/8.14.4/fileHashes/fileHashes.lock b/discordit/.gradle/8.14.4/fileHashes/fileHashes.lock new file mode 100644 index 00000000..a71e7c9e Binary files /dev/null and b/discordit/.gradle/8.14.4/fileHashes/fileHashes.lock differ diff --git a/discordit/.gradle/8.14.4/gc.properties b/discordit/.gradle/8.14.4/gc.properties new file mode 100644 index 00000000..e69de29b diff --git a/discordit/.gradle/9.0.0/checksums/checksums.lock b/discordit/.gradle/9.0.0/checksums/checksums.lock new file mode 100644 index 00000000..d3303a17 Binary files /dev/null and b/discordit/.gradle/9.0.0/checksums/checksums.lock differ diff --git a/discordit/.gradle/9.0.0/executionHistory/executionHistory.bin b/discordit/.gradle/9.0.0/executionHistory/executionHistory.bin new file mode 100644 index 00000000..1a0b8066 Binary files /dev/null and b/discordit/.gradle/9.0.0/executionHistory/executionHistory.bin differ diff --git a/discordit/.gradle/9.0.0/executionHistory/executionHistory.lock b/discordit/.gradle/9.0.0/executionHistory/executionHistory.lock new file mode 100644 index 00000000..b02398ae Binary files /dev/null and b/discordit/.gradle/9.0.0/executionHistory/executionHistory.lock differ diff --git a/discordit/.gradle/9.0.0/fileChanges/last-build.bin b/discordit/.gradle/9.0.0/fileChanges/last-build.bin new file mode 100644 index 00000000..f76dd238 Binary files /dev/null and b/discordit/.gradle/9.0.0/fileChanges/last-build.bin differ diff --git a/discordit/.gradle/9.0.0/fileHashes/fileHashes.bin b/discordit/.gradle/9.0.0/fileHashes/fileHashes.bin new file mode 100644 index 00000000..4e36d690 Binary files /dev/null and b/discordit/.gradle/9.0.0/fileHashes/fileHashes.bin differ diff --git a/discordit/.gradle/9.0.0/fileHashes/fileHashes.lock b/discordit/.gradle/9.0.0/fileHashes/fileHashes.lock new file mode 100644 index 00000000..986f3b96 Binary files /dev/null and b/discordit/.gradle/9.0.0/fileHashes/fileHashes.lock differ diff --git a/discordit/.gradle/9.0.0/fileHashes/resourceHashesCache.bin b/discordit/.gradle/9.0.0/fileHashes/resourceHashesCache.bin new file mode 100644 index 00000000..4d77fb49 Binary files /dev/null and b/discordit/.gradle/9.0.0/fileHashes/resourceHashesCache.bin differ diff --git a/discordit/.gradle/9.0.0/gc.properties b/discordit/.gradle/9.0.0/gc.properties new file mode 100644 index 00000000..e69de29b diff --git a/discordit/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/discordit/.gradle/buildOutputCleanup/buildOutputCleanup.lock new file mode 100644 index 00000000..fe21c6f9 Binary files /dev/null and b/discordit/.gradle/buildOutputCleanup/buildOutputCleanup.lock differ diff --git a/discordit/.gradle/buildOutputCleanup/cache.properties b/discordit/.gradle/buildOutputCleanup/cache.properties new file mode 100644 index 00000000..046afa29 --- /dev/null +++ b/discordit/.gradle/buildOutputCleanup/cache.properties @@ -0,0 +1,2 @@ +#Tue Jan 27 16:18:46 KST 2026 +gradle.version=8.14.4 diff --git a/discordit/.gradle/buildOutputCleanup/outputFiles.bin b/discordit/.gradle/buildOutputCleanup/outputFiles.bin new file mode 100644 index 00000000..bee2d86b Binary files /dev/null and b/discordit/.gradle/buildOutputCleanup/outputFiles.bin differ diff --git a/discordit/.gradle/file-system.probe b/discordit/.gradle/file-system.probe new file mode 100644 index 00000000..86a6209e Binary files /dev/null and b/discordit/.gradle/file-system.probe differ diff --git a/discordit/.gradle/vcs-1/gc.properties b/discordit/.gradle/vcs-1/gc.properties new file mode 100644 index 00000000..e69de29b diff --git a/discordit/build.gradle b/discordit/build.gradle new file mode 100644 index 00000000..1d3800a6 --- /dev/null +++ b/discordit/build.gradle @@ -0,0 +1,37 @@ +plugins { + id 'java' + id 'org.springframework.boot' version '3.5.10' + id 'io.spring.dependency-management' version '1.1.7' +} + +group = 'com.sprint.mission' +version = '0.0.1-SNAPSHOT' +description = 'Demo project for Spring Boot' + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(17) + } +} + +configurations { + compileOnly { + extendsFrom annotationProcessor + } +} + +repositories { + mavenCentral() +} + +dependencies { + implementation 'org.springframework.boot:spring-boot-starter-web' + compileOnly 'org.projectlombok:lombok' + annotationProcessor 'org.projectlombok:lombok' + testImplementation 'org.springframework.boot:spring-boot-starter-test' + testRuntimeOnly 'org.junit.platform:junit-platform-launcher' +} + +tasks.named('test') { + useJUnitPlatform() +} diff --git a/discordit/build/classes/java/main/com/sprint/mission/discodeit/Input.class b/discordit/build/classes/java/main/com/sprint/mission/discodeit/Input.class new file mode 100644 index 00000000..db616de0 Binary files /dev/null and b/discordit/build/classes/java/main/com/sprint/mission/discodeit/Input.class differ diff --git a/discordit/build/classes/java/main/com/sprint/mission/discodeit/app/JavaApplication$sc.class b/discordit/build/classes/java/main/com/sprint/mission/discodeit/app/JavaApplication$sc.class new file mode 100644 index 00000000..e8d01e83 Binary files /dev/null and b/discordit/build/classes/java/main/com/sprint/mission/discodeit/app/JavaApplication$sc.class differ diff --git a/discordit/build/classes/java/main/com/sprint/mission/discodeit/app/JavaApplication.class b/discordit/build/classes/java/main/com/sprint/mission/discodeit/app/JavaApplication.class new file mode 100644 index 00000000..c282a29a Binary files /dev/null and b/discordit/build/classes/java/main/com/sprint/mission/discodeit/app/JavaApplication.class differ diff --git a/discordit/build/classes/java/main/com/sprint/mission/discodeit/app/router/RoutePrintText.class b/discordit/build/classes/java/main/com/sprint/mission/discodeit/app/router/RoutePrintText.class new file mode 100644 index 00000000..346a200b Binary files /dev/null and b/discordit/build/classes/java/main/com/sprint/mission/discodeit/app/router/RoutePrintText.class differ diff --git a/discordit/build/classes/java/main/com/sprint/mission/discodeit/app/router/Router.class b/discordit/build/classes/java/main/com/sprint/mission/discodeit/app/router/Router.class new file mode 100644 index 00000000..378f3f3f Binary files /dev/null and b/discordit/build/classes/java/main/com/sprint/mission/discodeit/app/router/Router.class differ diff --git a/discordit/build/classes/java/main/com/sprint/mission/discodeit/entity/Channel.class b/discordit/build/classes/java/main/com/sprint/mission/discodeit/entity/Channel.class new file mode 100644 index 00000000..5e212390 Binary files /dev/null and b/discordit/build/classes/java/main/com/sprint/mission/discodeit/entity/Channel.class differ diff --git a/discordit/build/classes/java/main/com/sprint/mission/discodeit/entity/Message.class b/discordit/build/classes/java/main/com/sprint/mission/discodeit/entity/Message.class new file mode 100644 index 00000000..96d549e7 Binary files /dev/null and b/discordit/build/classes/java/main/com/sprint/mission/discodeit/entity/Message.class differ diff --git a/discordit/build/classes/java/main/com/sprint/mission/discodeit/entity/User.class b/discordit/build/classes/java/main/com/sprint/mission/discodeit/entity/User.class new file mode 100644 index 00000000..bc5566c5 Binary files /dev/null and b/discordit/build/classes/java/main/com/sprint/mission/discodeit/entity/User.class differ diff --git a/discordit/build/classes/java/main/com/sprint/mission/discodeit/repository/ChannelRepository.class b/discordit/build/classes/java/main/com/sprint/mission/discodeit/repository/ChannelRepository.class new file mode 100644 index 00000000..f272e525 Binary files /dev/null and b/discordit/build/classes/java/main/com/sprint/mission/discodeit/repository/ChannelRepository.class differ diff --git a/discordit/build/classes/java/main/com/sprint/mission/discodeit/repository/MessageRepository.class b/discordit/build/classes/java/main/com/sprint/mission/discodeit/repository/MessageRepository.class new file mode 100644 index 00000000..0caa8efe Binary files /dev/null and b/discordit/build/classes/java/main/com/sprint/mission/discodeit/repository/MessageRepository.class differ diff --git a/discordit/build/classes/java/main/com/sprint/mission/discodeit/repository/UserRepository.class b/discordit/build/classes/java/main/com/sprint/mission/discodeit/repository/UserRepository.class new file mode 100644 index 00000000..b60deb59 Binary files /dev/null and b/discordit/build/classes/java/main/com/sprint/mission/discodeit/repository/UserRepository.class differ diff --git a/discordit/build/classes/java/main/com/sprint/mission/discodeit/repository/file/FileChannelRepository.class b/discordit/build/classes/java/main/com/sprint/mission/discodeit/repository/file/FileChannelRepository.class new file mode 100644 index 00000000..5dd4826d Binary files /dev/null and b/discordit/build/classes/java/main/com/sprint/mission/discodeit/repository/file/FileChannelRepository.class differ diff --git a/discordit/build/classes/java/main/com/sprint/mission/discodeit/repository/file/FileMessageRepository.class b/discordit/build/classes/java/main/com/sprint/mission/discodeit/repository/file/FileMessageRepository.class new file mode 100644 index 00000000..64a0016b Binary files /dev/null and b/discordit/build/classes/java/main/com/sprint/mission/discodeit/repository/file/FileMessageRepository.class differ diff --git a/discordit/build/classes/java/main/com/sprint/mission/discodeit/repository/file/FileUserRepository.class b/discordit/build/classes/java/main/com/sprint/mission/discodeit/repository/file/FileUserRepository.class new file mode 100644 index 00000000..b8930dd0 Binary files /dev/null and b/discordit/build/classes/java/main/com/sprint/mission/discodeit/repository/file/FileUserRepository.class differ diff --git a/discordit/build/classes/java/main/com/sprint/mission/discodeit/service/ChannelService.class b/discordit/build/classes/java/main/com/sprint/mission/discodeit/service/ChannelService.class new file mode 100644 index 00000000..98b9dc5b Binary files /dev/null and b/discordit/build/classes/java/main/com/sprint/mission/discodeit/service/ChannelService.class differ diff --git a/discordit/build/classes/java/main/com/sprint/mission/discodeit/service/MessageService.class b/discordit/build/classes/java/main/com/sprint/mission/discodeit/service/MessageService.class new file mode 100644 index 00000000..61709884 Binary files /dev/null and b/discordit/build/classes/java/main/com/sprint/mission/discodeit/service/MessageService.class differ diff --git a/discordit/build/classes/java/main/com/sprint/mission/discodeit/service/UserService.class b/discordit/build/classes/java/main/com/sprint/mission/discodeit/service/UserService.class new file mode 100644 index 00000000..da9a0394 Binary files /dev/null and b/discordit/build/classes/java/main/com/sprint/mission/discodeit/service/UserService.class differ diff --git a/discordit/build/tmp/compileJava/previous-compilation-data.bin b/discordit/build/tmp/compileJava/previous-compilation-data.bin new file mode 100644 index 00000000..454a6129 Binary files /dev/null and b/discordit/build/tmp/compileJava/previous-compilation-data.bin differ diff --git a/discordit/gradle/wrapper/gradle-wrapper.jar b/discordit/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..1b33c55b Binary files /dev/null and b/discordit/gradle/wrapper/gradle-wrapper.jar differ diff --git a/discordit/gradle/wrapper/gradle-wrapper.properties b/discordit/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..aaaabb3c --- /dev/null +++ b/discordit/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.4-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/discordit/gradlew b/discordit/gradlew new file mode 100644 index 00000000..23d15a93 --- /dev/null +++ b/discordit/gradlew @@ -0,0 +1,251 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH="\\\"\\\"" + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/discordit/gradlew.bat b/discordit/gradlew.bat new file mode 100644 index 00000000..db3a6ac2 --- /dev/null +++ b/discordit/gradlew.bat @@ -0,0 +1,94 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH= + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/discordit/settings.gradle b/discordit/settings.gradle new file mode 100644 index 00000000..2437dfb2 --- /dev/null +++ b/discordit/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'discodeit' diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/DiscodeitApplication.java b/discordit/src/main/java/com/sprint/mission/discodeit/DiscodeitApplication.java new file mode 100644 index 00000000..6315d430 --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/DiscodeitApplication.java @@ -0,0 +1,27 @@ +package com.sprint.mission.discodeit; + +import com.sprint.mission.discodeit.app.router.Router; +import lombok.RequiredArgsConstructor; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; + +import java.util.Scanner; + +@SpringBootApplication +@RequiredArgsConstructor +public class DiscodeitApplication { + + @Bean + public Scanner scanner() { + return new Scanner(System.in); + } + + public static void main(String[] args) { + var context = SpringApplication.run(DiscodeitApplication.class, args); + Router router = context.getBean(Router.class); + System.out.println("프로그램을 실행합니다."); + router.route(); + } +} + diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/Input.java b/discordit/src/main/java/com/sprint/mission/discodeit/Input.java new file mode 100644 index 00000000..b5574df0 --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/Input.java @@ -0,0 +1,49 @@ +package com.sprint.mission.discodeit; + +import com.sprint.mission.discodeit.repository.UserRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.Scanner; + +@Component +@RequiredArgsConstructor +public class Input { + private final Scanner scanner; + private final UserRepository userRepository; + + private String inputChecker(String text, String regexRule) { + String inputString; + while(true) { + inputString = scanner.nextLine().trim(); + + if (inputString.isEmpty()) return null; + + if (!inputString.matches(regexRule)) { + System.err.println("잘못된 입력 형식입니다."); + continue; + } + + if (text.equals("프로필 이미지") && !inputString.matches(regexRule)) { + System.err.println("이미지는 jpg, png만 지원합니다."); + continue; + } + + if (userRepository.duplicateChecker(text, inputString)) { + System.err.println("동일한 " + text + "은(는) 존재할 수 없습니다."); + continue; + } + + break; + } + return inputString; + } + + public String inputUpdateField(String text, String regexRule) { + if (text.equals("프로필 이미지")){ + System.out.println("이미지 확장자는 jpg, png만 지원합니다."); + } + System.out.print("변경하실 " + text + " : "); + return inputChecker(text, regexRule); + } +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/UserState.java b/discordit/src/main/java/com/sprint/mission/discodeit/UserState.java new file mode 100644 index 00000000..50d9e683 --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/UserState.java @@ -0,0 +1,22 @@ +package com.sprint.mission.discodeit; + +import lombok.Getter; +import org.springframework.stereotype.Component; + +import java.util.UUID; + +@Component +@Getter +public class UserState { + private String userName = ""; + private UUID userId = null; + + public void userState(String username, UUID userId) { + this.userName = username; + this.userId = userId; + } + public void userState(String username) { + this.userName = ""; + this.userId = null; + } +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/app/JavaApplication.java b/discordit/src/main/java/com/sprint/mission/discodeit/app/JavaApplication.java new file mode 100644 index 00000000..6868ddd9 --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/app/JavaApplication.java @@ -0,0 +1,36 @@ +package com.sprint.mission.discodeit.app; + +import java.util.Scanner; + +public class JavaApplication { + private static class sc { + private static final Scanner sc = new Scanner(System.in); + } + + public static Scanner scanner() { + return sc.sc; + } + + public static void main(String[] args) { + Scanner sc = scanner(); + System.out.println("프로그램을 실행합니다."); + int menu; + + + while (true) { + System.out.println("===================="); + System.out.println("0. 프로그램 종료하기"); + System.out.println("1. 사용자 관련 서비스"); + System.out.println("2. 채널 관련 서비스"); + System.out.println("3. 메시지 관련 서비스"); + System.out.println("===================="); + + menu = sc.nextInt(); + sc.nextLine(); + + if (menu == 0) return; + +// Router.route(menu); + } + } +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/app/router/IsLogin.java b/discordit/src/main/java/com/sprint/mission/discodeit/app/router/IsLogin.java new file mode 100644 index 00000000..a7b16f46 --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/app/router/IsLogin.java @@ -0,0 +1,18 @@ +package com.sprint.mission.discodeit.app.router; + +import com.sprint.mission.discodeit.UserState; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class IsLogin { + private final UserState userState; + public boolean check(String service, int menu) { + boolean isLogin = !userState.getUserName().isEmpty(); + if(service.equals("User")) { + return isLogin || menu != 4; + } + return isLogin || menu == 3; + } +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/app/router/RouteChannel.java b/discordit/src/main/java/com/sprint/mission/discodeit/app/router/RouteChannel.java new file mode 100644 index 00000000..359456e4 --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/app/router/RouteChannel.java @@ -0,0 +1,236 @@ +package com.sprint.mission.discodeit.app.router; + +import com.sprint.mission.discodeit.UserState; +import com.sprint.mission.discodeit.dto.ResponseChannelDto; +import com.sprint.mission.discodeit.dto.UpdateChannelDto; +import com.sprint.mission.discodeit.dto.UserStatusUpdateDto; +import com.sprint.mission.discodeit.repository.BinaryContentRepository; +import com.sprint.mission.discodeit.service.ReadStatusService; +import com.sprint.mission.discodeit.service.basic.BasicChannelService; +import com.sprint.mission.discodeit.service.basic.BasicMessageService; +import com.sprint.mission.discodeit.service.basic.BasicUserService; +import com.sprint.mission.discodeit.service.basic.BasicUserStatusService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.time.Instant; +import java.util.List; +import java.util.Scanner; +import java.util.UUID; + +@Component +@RequiredArgsConstructor +public class RouteChannel { + private final BasicChannelService channelService; + private final IsLogin isLogin; + private final Scanner scanner; + private final BasicUserStatusService userStatusService; + private final ReadStatusService readStatusService; + private final BasicMessageService messageService; + private final BinaryContentRepository binaryContentRepository; + private final UserState userState; + private final BasicUserService userService; + + public void route(int routeCRUD) { + if(!isLogin.check("Channel", routeCRUD)) { + System.err.println("해당 기능은 로그인한 후 이용 가능합니다."); + return; + } + + try { + switch (routeCRUD) { + + /// create + case 1: + create(); + break; + + /// update + case 2: + update(); + break; + + /// read + case 3: + read(); + break; + + /// delete + case 4: + delete(); + break; + + /// invite Channel + case 5: + invite(); + break; + + default: + System.err.println("잘못된 입력값입니다."); + } + } catch (Exception e) { + System.err.println("[ERROR] " + e); + } + } + + private void create() { + System.out.println("사용하려는 채널명이 무엇인가요?"); + String name = scanner.nextLine().trim(); + + if (channelService.isPresent(name)) { + System.err.println("이미 존재하는 채널명입니다."); + return; + }; + + System.out.println("해당 채널의 성격을 알려주세요. (숫자 혹은 뒤의 영어를 입력해주시면 됩니다.)"); + System.out.println("1. PUBLIC"); + System.out.println("2. PRIVATE"); + String type = scanner.nextLine().trim(); + String userName = userState.getUserName(); + + if(!channelService.create(type, name, userName)) { + System.err.println("잘못된 입력값입니다. 처음으로 돌아갑니다."); + return; + }; + + UUID userId = userState.getUserId(); + UUID channelId = channelService.findChannelId(name); + accessTimeUpdate(); + + readStatusService.create(userId, channelId); + + System.out.println("성공"); + } + + private void update() { + System.out.println("변경하고자 하는 채널명을 알려주세요"); + String oldName = scanner.nextLine(); + + if (!channelService.isPresent(oldName)) { + System.err.println("해당 채널이 존재하지 않습니다."); + return; + } + + System.out.println("현재 채널명 : " + oldName); + System.out.println("무엇으로 변경하고 싶은가요? "); + + String newName = scanner.nextLine(); + + try { + if (channelService.update(new UpdateChannelDto(oldName, newName))) { + System.out.println("잘 변경되었어요!"); + accessTimeUpdate(); + } + } catch (Exception e) { + System.err.println("[ERROR] " + e); + } + } + + private void read() { + String name; + int menu; + System.out.println("전체 사용자 정보를 가져올까요?"); + System.out.println("1 : 특정 채널만 가져옵니다"); + System.out.println("2 : 전체 채널을 가져옵니다"); + + try { + menu = scanner.nextInt(); + scanner.nextLine(); + } catch (Exception e) { + System.err.println("잘못된 입력값입니다. : " + e); + return; + } + + if (menu == 1) { + System.out.println("검색할 채널명을 알려주세요"); + name = scanner.nextLine().trim(); + channelService.find(name); + } + else if (menu == 2) { + List requestAllChannelDto = channelService.findAll(userState.getUserName()); + if(requestAllChannelDto.isEmpty()) { + System.err.println("채널이 존재하지 않습니다."); + return; + } + + requestAllChannelDto.forEach(req -> { + System.out.println(req); + System.out.println("마지막 메시지 시간 : " + messageService.lastMessageTime(req.channelName())); + }); + System.out.println("총 채널 수 : " + requestAllChannelDto.size()); + accessTimeUpdate(); + } + } + + private void delete() { + System.out.println("[Warning!] 지금 계정을 삭제하려 하고 있습니다."); + System.out.println("[Warning!] 만약 잘못 들어오신 경우, 0을 눌러주시기 바랍니다."); + System.out.println("[Warning!] 계속 진행하려면 아무 숫자나 입력해주세요"); + + int n = scanner.nextInt(); + scanner.nextLine(); + + if (n == 0) { + System.out.println("처음으로 돌아갑니다."); + return; + } + + System.out.print("삭제하려는 채널명을 알려주세요: "); + String name = scanner.nextLine(); + + if (!channelService.isPresent(name)) { + System.err.println("해당 채널을 찾을 수 없습니다."); + return; + } + + UUID channelId = channelService.findChannelId(name); + + if(channelService.delete(name)) { + messageService.deleteAll(channelId); + readStatusService.deleteForChannel(channelId); + System.out.println("성공적으로 삭제되었습니다."); + } else { + System.err.println("삭제하지 못했어요!"); + } + } + + private void invite() { + System.out.println("현재 당신이 접속하고 있는 Private Channel은 다음과 같습니다."); + + try { + channelService.findAllPrivateChannel(userState.getUserName()).forEach(channel -> { + System.out.println("채널명 : " + channel.channelName()); + }); + } catch (Exception e) { + System.err.println("[ERROR] " + e); + } + + System.out.println("초대하고자 하는 채널명을 입력해주세요."); + String channelName = scanner.nextLine().trim(); + + if(!channelService.isPresent(channelName)) { + System.err.println("해당 채널이 존재하지 않습니다."); + return; + } + + System.out.println("초대하고자 하는 사용자명을 입력해주세요."); + String userName = scanner.nextLine().trim(); + + if(!userService.isPresent(userName)) { + System.err.println("해당 사용자가 존재하지 않습니다."); + return; + } + + UUID userId = userService.userNameToId(userName); + + channelService.includePrivateChannel(channelName, userName, userId); + + System.out.println("성공"); + } + + private void accessTimeUpdate() { + String userName = userState.getUserName(); + UUID userId = userState.getUserId(); + userStatusService.update(new UserStatusUpdateDto(userId, userName, Instant.now())); + } +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/app/router/RouteMessage.java b/discordit/src/main/java/com/sprint/mission/discodeit/app/router/RouteMessage.java new file mode 100644 index 00000000..6a6cdf44 --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/app/router/RouteMessage.java @@ -0,0 +1,227 @@ +package com.sprint.mission.discodeit.app.router; + +import com.sprint.mission.discodeit.UserState; +import com.sprint.mission.discodeit.dto.CreateBinaryContentDto; +import com.sprint.mission.discodeit.dto.CreateMessageDto; +import com.sprint.mission.discodeit.dto.UserStatusUpdateDto; +import com.sprint.mission.discodeit.entity.AttachmentType; +import com.sprint.mission.discodeit.exepction.FailedFound; +import com.sprint.mission.discodeit.service.ChannelService; +import com.sprint.mission.discodeit.service.basic.BasicBinaryContentService; +import com.sprint.mission.discodeit.service.basic.BasicMessageService; +import com.sprint.mission.discodeit.service.basic.BasicReadStatusService; +import com.sprint.mission.discodeit.service.basic.BasicUserStatusService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.time.Instant; +import java.util.*; + +@Component +@RequiredArgsConstructor +public class RouteMessage { + private final BasicMessageService messageService; + private final Scanner scanner; + private final IsLogin isLogin; + private final UserState userState; + private final ChannelService channelService; + private final BasicReadStatusService readStatusService; + private final BasicUserStatusService userStatusService; + private final BasicBinaryContentService binaryContentService; + + public void route(int routeCRUD) { + if(!isLogin.check("message", routeCRUD)) { + System.err.println("해당 기능은 로그인한 후 이용 가능합니다."); + return; + } + + try { + switch (routeCRUD) { + case 1: + /// create + create(); + break; + case 2: + /// update + messageService.findAllForSender(userState.getUserId()).forEach(System.out::println); + update(); + break; + case 3: + /// read + read(); + break; + case 4: + /// delete + delete(); + break; + } + } catch (Exception e) { + System.err.println("[ERROR] " + e); + } + } + + private void create() { + String senderUserName = userState.getUserName(); + + System.out.println("당신이 메시지를 보낼 수 있는 채널은 다음과 같습니다."); + channelService.findAll(senderUserName).forEach(System.out::println); + + System.out.println("현재 사용자 : " + senderUserName); + System.out.println("어디로 보내는 메시지인가요?"); + String sendeeChannelName = scanner.nextLine(); + channelService.isPresent(sendeeChannelName); + + while(true) { + System.out.println("현재 메시지를 보낼 채널은 " + sendeeChannelName + "입니다."); + System.out.println("현재 메시지를 보낼 사람은 " + senderUserName + "입니다."); + System.out.println("무어라 보내고 싶으신가요?"); + String text = scanner.nextLine(); + + List binaryContents = binaryContent(); + + System.out.println("현재 채널에 '" + text + "'라고 보내려 합니다."); + System.out.println("보내시려면 1을, 아니라면 0을 입력해주세요"); + String n = scanner.nextLine(); + + if (Objects.equals(n, "0")) { + System.out.println("처음으로 돌아갑니다."); + return; + } else if (Objects.equals(n, "1")) { + if(messageService.create(new CreateMessageDto(text, sendeeChannelName, senderUserName, binaryContents))) { + System.out.println("성공."); + accessTimeUpdate(); + } else { + System.err.println("알 수 없는 오류로 인해 실패했습니다."); + } + return; + } else { + System.err.println("잘못 입력했습니다. 메시지 입력 부분으로 돌아갑니다."); + } + } + } + + private List binaryContent() { + System.out.println("추가할 파일을 입력해주세요."); + System.out.println("마무리됐을 경우, 그냥 엔터를 입력해주세요."); + List result = new ArrayList<>(); + + while(true) { + String filename = scanner.nextLine(); + if(filename.trim().isEmpty()) return result; + if(!filename.matches("^.+\\.(png|jpg)$")) { + System.err.println("잘못된 확장자 형식입니다. jpg, png만 지원합니다."); + continue; + } + + UUID id = binaryContentService.create(new CreateBinaryContentDto(AttachmentType.MESSAGE, filename, null)); + + result.add(id); + } + } + + private void update() { + System.out.println("어떤 것을 수정하고 싶나요?"); + String messageId = scanner.nextLine(); + UUID parseUUID; + + try{ + parseUUID = UUID.fromString(messageId); + } catch (Exception e) { + System.err.println("잘못된 입력값입니다 : " + e); + return; + } + + System.out.println("무슨 내용으로 수정하고 싶나요?"); + String content = scanner.nextLine(); + + try { + if (messageService.update(userState.getUserId(), parseUUID, content)) { + System.out.println("성공적으로 변경되었습니다."); + accessTimeUpdate(); + } else { + System.err.println("알 수 없는 오류로 인해 변경하지 못했습니다."); + } + } catch (FailedFound e) { + System.err.println("[ERROR] " + e); + } + } + + private void read() { + System.out.println("현재는 내가 보낸 메시지, 특정 채널 메시지를 조회하는 기능만 있습니다."); + System.out.println("1. 내가 보낸 메시지 확인하기"); + System.out.println("2. 채널에 있는 메시지 확인하기"); + int m = scanner.nextInt(); + scanner.nextLine(); + + UUID userId = userState.getUserId(); + List requestDto; + + if (m == 1) { + requestDto = messageService.findAllForSender(userId); + accessTimeUpdate(); + } else if (m == 2) { + System.out.println("어디로 보낸 메시지인가요?"); + String sendeeChannelName = scanner.nextLine(); + if (!channelService.isPresent(sendeeChannelName)) { + System.out.println("존재하지 않는 채널입니다."); + return; + } + readStatusService.update(userId, sendeeChannelName); + requestDto = messageService.findAllInChannel(sendeeChannelName); + accessTimeUpdate(); + readStatusService.update(userId, sendeeChannelName); + } else return; + + showMessage(requestDto); + } + + private void showMessage(List requestDto) { + if(requestDto.isEmpty()) { + System.out.println("아무것도 없네요!"); + return; + } + + requestDto.forEach(System.out::println); + System.out.println("총 메세지 개수 : " + requestDto.size()); + } + + private void delete() { + UUID userId = userState.getUserId(); + + System.out.println("현재는 내가 보낸 메시지를 삭제하는 기능만 지원하고 있습니다."); + System.out.println("당신이 보낸 메시지는 다음과 같습니다."); + messageService.findAllForSender(userId).forEach(System.out::println); + + System.out.println("어떤 메시지를 삭제하고 싶나요? ID로 입력해주세요."); + String id = scanner.nextLine(); + UUID parseId; + + try{ + parseId = UUID.fromString(id); + } catch (Exception e) { + System.err.println("잘못된 입력값입니다. : " + e); + return; + } + + if (!messageService.isPresent(userId, parseId)) { + System.err.println("실패. 해당 ID를 찾지 못했습니다."); + return; + } + + System.out.println("해당 메시지를 삭제합니까? (Y or any Key)"); + String isDelete = scanner.nextLine(); + + if (isDelete.equalsIgnoreCase("Y")) { + if(messageService.delete(userId, UUID.fromString(id))) { + System.out.println("성공!"); + accessTimeUpdate(); + } else System.err.println("실패!"); + } else System.out.println("초기로 돌아갑니다"); + } + + private void accessTimeUpdate() { + String userName = userState.getUserName(); + UUID userId = userState.getUserId(); + userStatusService.update(new UserStatusUpdateDto(userId, userName, Instant.now())); + } +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/app/router/RoutePrintText.java b/discordit/src/main/java/com/sprint/mission/discodeit/app/router/RoutePrintText.java new file mode 100644 index 00000000..0f6e1e0d --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/app/router/RoutePrintText.java @@ -0,0 +1,43 @@ +package com.sprint.mission.discodeit.app.router; + +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; + +@Component +public class RoutePrintText { + static void printText(String text) { + List retouchText = new ArrayList<>(); + + switch(text.toLowerCase()) { + case "user": + retouchText.add("사용자"); + retouchText.add("User"); + break; + + case "channel": + retouchText.add("채널"); + retouchText.add("Channel"); + break; + + case "message": + retouchText.add("메세지"); + retouchText.add("Message"); + break; + + default: + return; + } + + System.out.println(retouchText.get(0) + " 관련 서비스입니다."); + System.out.println("어떤 서비스를 원하시나요?"); + System.out.println("===================="); + System.out.println("1. " + retouchText.get(1) + " Create"); + System.out.println("2. " + retouchText.get(1) + " Update"); + System.out.println("3. " + retouchText.get(1) + " Read"); + System.out.println("4. " + retouchText.get(1) + " Delete"); + if(retouchText.get(0).equals("채널")) + System.out.println("5. Private Channel Invite"); + } +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/app/router/RouteUser.java b/discordit/src/main/java/com/sprint/mission/discodeit/app/router/RouteUser.java new file mode 100644 index 00000000..67d8a8d6 --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/app/router/RouteUser.java @@ -0,0 +1,242 @@ +package com.sprint.mission.discodeit.app.router; + +import com.sprint.mission.discodeit.Input; +import com.sprint.mission.discodeit.UserState; +import com.sprint.mission.discodeit.dto.*; +import com.sprint.mission.discodeit.entity.AttachmentType; +import com.sprint.mission.discodeit.exepction.NotFound; +import com.sprint.mission.discodeit.service.ReadStatusService; +import com.sprint.mission.discodeit.service.basic.*; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.time.Instant; +import java.util.List; +import java.util.Scanner; +import java.util.UUID; + +@Component +@RequiredArgsConstructor +public class RouteUser { + private final BasicUserService userService; + private final BasicMessageService messageService; + private final BasicChannelService channelService; + private final BasicUserStatusService userStatusService; + private final BasicBinaryContentService binaryContentService; + private final Scanner scanner; + private final IsLogin isLogin; + private final Input input; + private final UserState userState; + private final ReadStatusService readStatusService; + + public void route(int routeCRUD) { + int menu; + if(!isLogin.check("User", routeCRUD)) { + System.err.println("해당 기능은 로그인한 후 이용 가능합니다."); + return; + } + + switch (routeCRUD) { + /// create + case 1: + create(); + break; + + /// update + case 2: + update(); + break; + + /// read + case 3: + System.out.println("전체 사용자 정보를 가져올까요?"); + System.out.println("1 : 특정 사용자만 가져옵니다"); + System.out.println("2 : 전체 사용자를 가져옵니다"); + + menu = scanner.nextInt(); + scanner.nextLine(); + + if (menu == 1) find(); + else if (menu == 2) findAll(); + break; + + /// delete + case 4: + delete(); + break; + } + } + + private void create() { + System.out.println("===================="); + System.out.println("회원가입에 오신 것을 환영합니다."); + System.out.print("먼저, 사용할 이름을 작성해주세요 : "); + String name = scanner.nextLine().trim(); + + if (userService.isPresent(name)) { + System.out.println("이미 존재하는 사용자명입니다."); + return; + } + + System.out.print("사용할 비밀번호를 입력해주세요 : "); + String password = scanner.nextLine(); + + System.out.print("사용할 프로필 이미지를 입력해주세요 : "); + String profileImage = scanner.nextLine(); + try { + UUID profileId = !profileImage.isEmpty() ? + binaryContentService.create(new CreateBinaryContentDto(AttachmentType.USER, profileImage, null)) : null; + + userService.create(new CreateUserDto(name, password, profileId)); + UUID id = userService.userNameToId(name); + userStatusService.create(new CreateUserStatusDto(name, id)); + System.out.println("성공적으로 추가했습니다."); + } catch (Exception e) { + System.err.println("[ERROR] " + e); + } + } + + private void update() { + System.out.println("===================="); + System.out.println("사용자 변경 메뉴입니다."); + System.out.println("현재 로그인한 사용자의 비밀번호를 입력해주세요"); + String password = scanner.nextLine(); + if(userService.isValid(userState.getUserId(), password)) { + System.err.println("비밀번호가 일치하지 않습니다. 처음으로 돌아갑니다."); + return; + } + + while(true) { + System.out.println("변경하지 않으실 필드에는 엔터를 눌러주시기 바랍니다."); + + String reName = input.inputUpdateField("사용자명", "\\S+"); + String rePassword = input.inputUpdateField("비밀번호", "\\S+"); + String reMail = input.inputUpdateField("이메일", "\\S+@\\S+\\.\\S+"); + String rePhoneNumber = input.inputUpdateField("전화번호", "^\\d{10,11}$"); + String reProfile = input.inputUpdateField("프로필 이미지", "(?i).*\\.(jpg|png)$"); + + System.out.println("이대로 진행하시겠습니까?"); + System.out.println("맞으면 y, 다시 입력하려면 re"); + System.out.println("취소하려면 n을 입력해주시기 바랍니다."); + + String finalCheckIsContinue = scanner.nextLine(); + + if(finalCheckIsContinue.equalsIgnoreCase("re")) continue; + if(finalCheckIsContinue.equalsIgnoreCase("n")) return; + + if(finalCheckIsContinue.equalsIgnoreCase("y")) { + UUID reProfileId = !reProfile.isEmpty() ? + binaryContentService.create(new CreateBinaryContentDto(AttachmentType.USER, reProfile, null)) : null; + if(reProfileId != null) binaryContentService.delete(reProfileId); + + if(userService.update(new UpdateUserDto(userState.getUserId(), reName, rePassword, reMail, rePhoneNumber, reProfileId))) { + System.out.println("성공적으로 변경되었습니다."); + accessTimeUpdate(); + return; + } + } + + System.err.println("알 수 없는 오류로 인해 실패했습니다."); + return; + } + } + + private void find() { + System.out.println("조회하고자 하는 사용자명을 입력해주세요"); + String name = scanner.nextLine(); + + try { + UserFinder requestDto = userService.find(name); + String profile = binaryContentService.find(requestDto.profileId()); + + System.out.println(requestDto.userInfo()); + System.out.println("프로필 이미지 : " + profile); + System.out.println("접속상태 : " + userStatusService.find(new FindUserStatusDto(requestDto.id(), requestDto.name()))); + System.out.println("===================="); + } catch (NotFound e) { + System.err.println("[ERROR]" + e); + } + + accessTimeUpdate(); + } + + private void findAll() { + if(userService.findAll().isEmpty()) { + System.err.println("사용자를 찾을 수 없습니다."); + } + + List findAllUser = userService.findAll(); + + findAllUser.forEach(requestDto -> { + try { + String profile = binaryContentService.find(requestDto.profileId()); + System.out.println(requestDto.userInfo()); + System.out.println("프로필 이미지 : " + profile); + } catch (Exception ignore) { + System.out.println(requestDto.userInfo()); + } + + System.out.println("접속상태 : " + userStatusService.find(new FindUserStatusDto(requestDto.id(), requestDto.name()))); + System.out.println("===================="); + }); + System.out.println("총 사용자 : " + findAllUser.size()); + + accessTimeUpdate(); + } + + private void delete() { + System.err.println("[Warning!] 지금 계정을 삭제하려 하고 있습니다."); + System.err.println("[Warning!] 만약 잘못 들어오신 경우, 0을 눌러주시기 바랍니다."); + System.err.println("[Warning!] 계속 진행하시려면 아무 숫자나 입력해주세요."); + + int n = scanner.nextInt(); + scanner.nextLine(); + if (n == 0) { + System.out.println("처음으로 돌아갑니다."); + return; + } + + System.out.println("현재 로그인한 사용자의 비밀번호를 입력해주세요."); + String password = scanner.nextLine(); + + UUID userId = userState.getUserId(); + String userName = userState.getUserName(); + List allPrivateChannelDto = channelService.findAllPrivateChannel(userName); + + if(userService.isValid(userId, password)) { + System.err.println("비밀번호가 일치하지 않습니다. 처음으로 돌아갑니다."); + return; + } + + UUID profileId = userService.find(userName).profileId(); + + try { + if(userService.delete(userId)) { // FIXME: BinaryContent, 채널 삭제로 인한 타인의 메시지 삭제 + messageService.deleteAll(userId); + userStatusService.delete(new DeleteUserStatusDto(userId, userName)); + readStatusService.deleteForUser(userId); + binaryContentService.delete(profileId); + if(!allPrivateChannelDto.isEmpty()) + allPrivateChannelDto.forEach(req -> { + channelService.excludePrivateChannel(req.channelName(), userName); + }); + if(channelService.isCeatePrivateChannel(userName)) { + messageService.deleteAll(channelService.findChannelId(userName)); + channelService.deleteAll(userName); + } + userState.userState(""); + System.out.println("성공적으로 삭제되었습니다."); + } + } catch (Exception e) { + System.err.println("[ERROR] " + e); + } + } + + private void accessTimeUpdate() { + if(userState.getUserName().isEmpty()) return; + + String userName = userState.getUserName(); + UUID userId = userState.getUserId(); + userStatusService.update(new UserStatusUpdateDto(userId, userName, Instant.now())); + } +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/app/router/Router.java b/discordit/src/main/java/com/sprint/mission/discodeit/app/router/Router.java new file mode 100644 index 00000000..0817aea3 --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/app/router/Router.java @@ -0,0 +1,105 @@ +package com.sprint.mission.discodeit.app.router; + +import com.sprint.mission.discodeit.UserState; +import com.sprint.mission.discodeit.dto.LoginDto; +import com.sprint.mission.discodeit.service.auth.AuthService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.Scanner; + +@Component +@RequiredArgsConstructor +public class Router { + private final RouteMessage routeMessage; + private final RouteUser routeUser; + private final RouteChannel routeChannel; + private final Scanner scanner; + private final AuthService authService; + private final UserState userState; + + public void route() { + int subMenu; + int menu; + + while(true) { + boolean isLogin = userState.getUserName().isEmpty(); + System.out.println("===================="); + System.out.println("0. 프로그램 종료하기"); + System.out.println(isLogin ? "1. 로그인하기" : + "1. 로그아웃하기 (현재 사용자 : " + userState.getUserName() + ")"); + System.out.println("2. 사용자 관련 서비스"); + System.out.println("3. 채널 관련 서비스"); + System.out.println("4. 메시지 관련 서비스"); + System.out.println("===================="); + + menu = scanner.nextInt(); + scanner.nextLine(); + + if (menu == 0) System.exit(0); + + switch (menu) { + case 1: + if (isLogin) { + login(); + } else { + authService.logout(); + } + break; + + case 2: + RoutePrintText.printText("user"); + subMenu = inputChecker(); + + if(subMenu == -1) continue; + + routeUser.route(subMenu); + break; + + case 3: + RoutePrintText.printText("channel"); + subMenu = inputChecker(); + + if(subMenu == -1) continue; + + routeChannel.route(subMenu); + break; + + case 4: + RoutePrintText.printText("message"); + subMenu = inputChecker(); + + if(subMenu == -1) continue; + + routeMessage.route(subMenu); + break; + + default: + return; + } + } + } + + private int inputChecker() { + try{ + return Integer.parseInt(scanner.nextLine().trim()); + } catch (NumberFormatException e) { + System.out.println("잘못된 입력값입니다."); + } + return -1; + } + + private void login() { + System.out.println("로그인 서비스입니다."); + System.out.println("로그인할 사용자명을 입력해주세요"); + String name = scanner.nextLine().trim(); + System.out.println("해당 사용자의 비밀번호를 입력해주세요"); + String password = scanner.nextLine().trim(); + + try { + authService.login(new LoginDto(name, password)); + } catch (Exception e) { + System.err.println("[ERROR] " + e); + } + } +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/config/FileRepositoryConfig.java b/discordit/src/main/java/com/sprint/mission/discodeit/config/FileRepositoryConfig.java new file mode 100644 index 00000000..8cd65017 --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/config/FileRepositoryConfig.java @@ -0,0 +1,46 @@ +package com.sprint.mission.discodeit.config; + +import com.sprint.mission.discodeit.repository.*; +import com.sprint.mission.discodeit.repository.file.*; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +@ConditionalOnProperty( + name = "discodeit.repository.type", + havingValue = "file", + matchIfMissing = false +) +public class FileRepositoryConfig { + + @Bean + public UserRepository userRepository() { + return new FileUserRepository(); + } + + @Bean + public ChannelRepository channelRepository() { + return new FileChannelRepository(); + } + + @Bean + public MessageRepository messageRepository() { + return new FileMessageRepository(); + } + + @Bean + public BinaryContentRepository binaryContentRepository() { + return new FileBinaryContentRepository(); + } + + @Bean + public ReadStatusRepository readStatusRepository() { + return new FileReadStatusRepository(); + } + + @Bean + public UserStatusRepository userStatusRepository() { + return new FileUserStatusRepository(); + } +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/config/JCFRepositoryConfig.java b/discordit/src/main/java/com/sprint/mission/discodeit/config/JCFRepositoryConfig.java new file mode 100644 index 00000000..e1006f3c --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/config/JCFRepositoryConfig.java @@ -0,0 +1,46 @@ +package com.sprint.mission.discodeit.config; + +import com.sprint.mission.discodeit.repository.*; +import com.sprint.mission.discodeit.repository.jcf.*; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +@ConditionalOnProperty( + name = "discodeit.repository.type", + havingValue = "jcf", + matchIfMissing = true +) +public class JCFRepositoryConfig { + + @Bean + public UserRepository userRepository() { + return new JCFUserRepository(); + } + + @Bean + public ChannelRepository channelRepository() { + return new JCFChannelRepository(); + } + + @Bean + public MessageRepository messageRepository() { + return new JCFMessageRepository(); + } + + @Bean + public BinaryContentRepository binaryContentRepository() { + return new JCFBinaryContentRepository(); + } + + @Bean + public ReadStatusRepository readStatusRepository() { + return new JCFReadStatusRepository(); + } + + @Bean + public UserStatusRepository userStatusRepository() { + return new JCFUserStatusRepository(); + } +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/dto/CreateBinaryContentDto.java b/discordit/src/main/java/com/sprint/mission/discodeit/dto/CreateBinaryContentDto.java new file mode 100644 index 00000000..14873061 --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/dto/CreateBinaryContentDto.java @@ -0,0 +1,8 @@ +package com.sprint.mission.discodeit.dto; + +import com.sprint.mission.discodeit.entity.AttachmentType; + +public record CreateBinaryContentDto( + AttachmentType type, String filename, byte[] bytes +) { +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/dto/CreateChannelDto.java b/discordit/src/main/java/com/sprint/mission/discodeit/dto/CreateChannelDto.java new file mode 100644 index 00000000..765d6d7d --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/dto/CreateChannelDto.java @@ -0,0 +1,4 @@ +package com.sprint.mission.discodeit.dto; + +public class CreateChannelDto { +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/dto/CreateMessageDto.java b/discordit/src/main/java/com/sprint/mission/discodeit/dto/CreateMessageDto.java new file mode 100644 index 00000000..0a4987b2 --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/dto/CreateMessageDto.java @@ -0,0 +1,12 @@ +package com.sprint.mission.discodeit.dto; + +import java.util.List; +import java.util.UUID; + +public record CreateMessageDto( + String text, + String sendeeChannelName, + String senderUserName, + List binaryContentIds +) { +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/dto/CreateUserDto.java b/discordit/src/main/java/com/sprint/mission/discodeit/dto/CreateUserDto.java new file mode 100644 index 00000000..dfb6fe88 --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/dto/CreateUserDto.java @@ -0,0 +1,16 @@ +package com.sprint.mission.discodeit.dto; + +import com.sprint.mission.discodeit.entity.User; + +import java.util.UUID; + +public record CreateUserDto( + String username, + String password, + UUID profileId +) { + public User toEntity() { + + return new User(username, password, profileId); + } +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/dto/CreateUserStatusDto.java b/discordit/src/main/java/com/sprint/mission/discodeit/dto/CreateUserStatusDto.java new file mode 100644 index 00000000..81f03d38 --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/dto/CreateUserStatusDto.java @@ -0,0 +1,6 @@ +package com.sprint.mission.discodeit.dto; + +import java.util.UUID; + +public record CreateUserStatusDto(String name, UUID id) { +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/dto/DeleteUserStatusDto.java b/discordit/src/main/java/com/sprint/mission/discodeit/dto/DeleteUserStatusDto.java new file mode 100644 index 00000000..82230a8b --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/dto/DeleteUserStatusDto.java @@ -0,0 +1,6 @@ +package com.sprint.mission.discodeit.dto; + +import java.util.UUID; + +public record DeleteUserStatusDto(UUID id, String name) { +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/dto/FindChannelDto.java b/discordit/src/main/java/com/sprint/mission/discodeit/dto/FindChannelDto.java new file mode 100644 index 00000000..b19df475 --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/dto/FindChannelDto.java @@ -0,0 +1,20 @@ +package com.sprint.mission.discodeit.dto; + +import com.sprint.mission.discodeit.entity.ChannelType; +import lombok.NonNull; + +import java.time.Instant; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; + +public record FindChannelDto( + String channelInfo, + ChannelType channelType, + Instant lastMessage +) { + private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy년 MM월 dd일 HH시 mm분 ss초").withZone(ZoneId.of("Asia/Seoul")); + + public @NonNull String getInfo() { + return channelInfo + "\n마지막 메시지 : " + FORMATTER.format(lastMessage); + } +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/dto/FindUserStatusDto.java b/discordit/src/main/java/com/sprint/mission/discodeit/dto/FindUserStatusDto.java new file mode 100644 index 00000000..c60224c1 --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/dto/FindUserStatusDto.java @@ -0,0 +1,6 @@ +package com.sprint.mission.discodeit.dto; + +import java.util.UUID; + +public record FindUserStatusDto(UUID id, String name) { +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/dto/LoginDto.java b/discordit/src/main/java/com/sprint/mission/discodeit/dto/LoginDto.java new file mode 100644 index 00000000..e671368d --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/dto/LoginDto.java @@ -0,0 +1,4 @@ +package com.sprint.mission.discodeit.dto; + +public record LoginDto(String name, String password) { +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/dto/MessageRequestDto.java b/discordit/src/main/java/com/sprint/mission/discodeit/dto/MessageRequestDto.java new file mode 100644 index 00000000..f31190c7 --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/dto/MessageRequestDto.java @@ -0,0 +1,15 @@ +package com.sprint.mission.discodeit.dto; + +import java.util.List; +import java.util.UUID; + +public record MessageRequestDto( + UUID id, + UUID channelId, + UUID userId, + List attachmentIds, + String createAt, + String updateAt, + String content +) { +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/dto/ResponseChannelDto.java b/discordit/src/main/java/com/sprint/mission/discodeit/dto/ResponseChannelDto.java new file mode 100644 index 00000000..b2d27dac --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/dto/ResponseChannelDto.java @@ -0,0 +1,45 @@ +package com.sprint.mission.discodeit.dto; + +import com.sprint.mission.discodeit.entity.ChannelType; + +import java.time.Instant; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.util.Map; +import java.util.UUID; +import java.util.stream.Collectors; + +public record ResponseChannelDto( + String channelName, + UUID channelId, + ChannelType type, + Instant createAt, + Instant updateAt, + String createUser, + Map accessibleUsers +) { + private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy년 MM월 dd일 HH시 mm분 ss초").withZone(ZoneId.of("Asia/Seoul")); + + @Override + public String toString() { + String result = "====================\n" + + "채널명 : " + channelName + "\n" + + "채널ID : " + channelId + "\n" + + "타입 : " + type + "\n" + + "채널생성일 : " + FORMATTER.format(createAt) + "\n" + + "채널수정일 : " + FORMATTER.format(updateAt) + "\n"; + + String accessUserResult = ""; + if(type.equals(ChannelType.PRIVATE)) { + try { + accessUserResult = accessibleUsers.entrySet().stream() + .map(entry -> "멤버명 : " + entry.getKey() + "\n멤버 ID : " + entry.getValue() + "\n") + .collect(Collectors.joining()); + } catch (Exception ignore) {} + } + + if(accessUserResult.isEmpty()) return result + "관리자: " + createUser; + + return result + "관리자: " + createUser + "\n" + accessUserResult; + } +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/dto/UpdateChannelDto.java b/discordit/src/main/java/com/sprint/mission/discodeit/dto/UpdateChannelDto.java new file mode 100644 index 00000000..73bb3873 --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/dto/UpdateChannelDto.java @@ -0,0 +1,4 @@ +package com.sprint.mission.discodeit.dto; + +public record UpdateChannelDto(String oldName, String newName) { +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/dto/UpdateUserDto.java b/discordit/src/main/java/com/sprint/mission/discodeit/dto/UpdateUserDto.java new file mode 100644 index 00000000..b27336d9 --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/dto/UpdateUserDto.java @@ -0,0 +1,8 @@ +package com.sprint.mission.discodeit.dto; + +import java.util.UUID; + +public record UpdateUserDto( + UUID id, String reName, String rePassword, String reMail, String rePhoneNumber, UUID reProfileId +) { +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/dto/UserFinder.java b/discordit/src/main/java/com/sprint/mission/discodeit/dto/UserFinder.java new file mode 100644 index 00000000..68e48cb9 --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/dto/UserFinder.java @@ -0,0 +1,6 @@ +package com.sprint.mission.discodeit.dto; + +import java.util.UUID; + +public record UserFinder(UUID id, String name, String userInfo, UUID profileId) { +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/dto/UserStatusUpdateDto.java b/discordit/src/main/java/com/sprint/mission/discodeit/dto/UserStatusUpdateDto.java new file mode 100644 index 00000000..6668ca85 --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/dto/UserStatusUpdateDto.java @@ -0,0 +1,7 @@ +package com.sprint.mission.discodeit.dto; + +import java.time.Instant; +import java.util.UUID; + +public record UserStatusUpdateDto(UUID id, String name, Instant time) { +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/entity/AttachmentType.java b/discordit/src/main/java/com/sprint/mission/discodeit/entity/AttachmentType.java new file mode 100644 index 00000000..a64d65c9 --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/entity/AttachmentType.java @@ -0,0 +1,5 @@ +package com.sprint.mission.discodeit.entity; + +public enum AttachmentType { + USER, MESSAGE, CHANNEL +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/entity/BinaryContent.java b/discordit/src/main/java/com/sprint/mission/discodeit/entity/BinaryContent.java new file mode 100644 index 00000000..5c042c20 --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/entity/BinaryContent.java @@ -0,0 +1,32 @@ +package com.sprint.mission.discodeit.entity; + +import lombok.Getter; + +import java.io.Serializable; +import java.time.Instant; +import java.util.UUID; + +@Getter +public class BinaryContent implements Serializable { + private final UUID id; + private final Instant createAt; + private final String fileName; + private final String fileExtension; + private final AttachmentType type; + private final byte[] bytes; + + public BinaryContent(AttachmentType type, String fileName, byte[] bytes) { + this.id = UUID.randomUUID(); + this.createAt = Instant.now(); + this.fileName = fileName.split("\\.")[0]; + this.fileExtension = fileName.split("\\.")[1]; + this.bytes = bytes; + this.type = type; + } + + @Override + public String toString() { + return "\n BinaryContent ID : " + id + + "\n fileName : " + fileName + "." + fileExtension + "\n"; + } +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/entity/Channel.java b/discordit/src/main/java/com/sprint/mission/discodeit/entity/Channel.java new file mode 100644 index 00000000..bf1798e2 --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/entity/Channel.java @@ -0,0 +1,61 @@ +package com.sprint.mission.discodeit.entity; + +import lombok.Getter; + +import java.io.Serial; +import java.io.Serializable; +import java.time.Instant; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +@Getter +public class Channel implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + private final UUID id; + private final Instant createAt; + private final String createUser; + private final Map accessibleUser = new ConcurrentHashMap<>(); + private final Map userReadStatusMap = new ConcurrentHashMap<>(); + private final ChannelType channelType; + private String name; + private Instant updateAt; + + public Channel(String name, String createUser, UUID createUserId) { + this(name, createUser, createUserId, ChannelType.PUBLIC); + } + + public Channel(String name, String createUser, UUID createUserId, ChannelType channelType) { + Instant now = Instant.now(); + this.id = UUID.randomUUID(); + this.name = name; + this.createUser = createUser; + this.channelType = channelType; + this.createAt = now; + this.updateAt = now; + accessibleUser.put(createUser, createUserId); + } + + /// setter + private void setUpdateAt() { + this.updateAt = Instant.now(); + } + + private void setName(String name) { + this.name = name; + setUpdateAt(); + } + + public void channelUpdater(String name) { + setName(name); + } + + public void addAccessibleUser(String userName, UUID userId) { + accessibleUser.put(userName, userId); + } + + public void removeAccessibleUser(String userName) { + accessibleUser.remove(userName); + } +} \ No newline at end of file diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/entity/ChannelType.java b/discordit/src/main/java/com/sprint/mission/discodeit/entity/ChannelType.java new file mode 100644 index 00000000..6a8745e8 --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/entity/ChannelType.java @@ -0,0 +1,6 @@ +package com.sprint.mission.discodeit.entity; + +public enum ChannelType { + PUBLIC, + PRIVATE +} \ No newline at end of file diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/entity/Message.java b/discordit/src/main/java/com/sprint/mission/discodeit/entity/Message.java new file mode 100644 index 00000000..82b894c1 --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/entity/Message.java @@ -0,0 +1,54 @@ +package com.sprint.mission.discodeit.entity; + +import lombok.Getter; + +import java.io.Serial; +import java.io.Serializable; +import java.time.Instant; +import java.util.List; +import java.util.UUID; + +@Getter +public class Message implements Serializable { + private final UUID id; + private final Instant createAt; + private final UUID channelId; + private final UUID userId; + private final List attachmentIds; + private Instant updateAt; + private String content; + @Serial + private static final long serialVersionUID = 1L; + + public Message(UUID channelId, UUID userId, String content, List attachmentIds) { + Instant n = Instant.now(); + this.id = UUID.randomUUID(); + this.channelId = channelId; + this.userId = userId; + this.content = content; + this.createAt = n; + this.updateAt = n; + this.attachmentIds = attachmentIds; + } + + public UUID getSenderUserId() { + return userId; + } + + public UUID getSendChannelId() { + return channelId; + } + + public void updateMessage(String content) { + setContent(content); + } + + private void setContent(String content) { + this.content = content; + setUpdateAt(); + } + + private void setUpdateAt() { + this.updateAt = Instant.now(); + } +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/entity/ReadStatus.java b/discordit/src/main/java/com/sprint/mission/discodeit/entity/ReadStatus.java new file mode 100644 index 00000000..f11fb23f --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/entity/ReadStatus.java @@ -0,0 +1,28 @@ +package com.sprint.mission.discodeit.entity; + +import lombok.Getter; + +import java.io.Serializable; +import java.time.Instant; +import java.util.UUID; + +@Getter +public class ReadStatus implements Serializable { + private final UUID id = UUID.randomUUID(); + private final UUID userId; + private final UUID channelId; + private final Instant createAt; + private Instant updateAt; + + public ReadStatus(UUID userId, UUID channelId){ + Instant now = Instant.now(); + this.userId = userId; + this.channelId = channelId; + createAt = now; + updateAt = now; + }; + + public void updateReadAt() { + updateAt = Instant.now(); + } +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/entity/User.java b/discordit/src/main/java/com/sprint/mission/discodeit/entity/User.java new file mode 100644 index 00000000..1aaacaa6 --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/entity/User.java @@ -0,0 +1,87 @@ +package com.sprint.mission.discodeit.entity; + +import lombok.Getter; + +import java.io.Serial; +import java.io.Serializable; +import java.time.Instant; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.util.UUID; + +@Getter +public class User implements Serializable { + private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy년 MM월 dd일 HH시 mm분 ss초") + .withZone(ZoneId.of("Asia/Seoul")); + private final UUID id; + private UUID profileId; + private final Instant createAt; + private Instant updateAt; + private String name; + private String password; + private String email; + private String phoneNumber; + @Serial + private static final long serialVersionUID = 1L; + + public User(String name, String password, UUID profileId) { + Instant now = Instant.now(); + this.profileId = profileId; + this.id = UUID.randomUUID(); + this.name = name; + this.password = password; + this.createAt = now; + this.updateAt = now; + } + + private void setUpdateAt() { + this.updateAt = Instant.now(); + } + + public void updateUser(String name, String password, String email, String phoneNumber, UUID profileId) { + /// null checker + boolean[] argumentsList = { + check(name), check(password), check(email), check(phoneNumber), check(String.valueOf(profileId)) + }; + if(argumentsList[0]) setName(name); + if(argumentsList[1]) setPassword(password); + if(argumentsList[2]) setEmail(email); + if(argumentsList[3]) setPhoneNumber(phoneNumber); + if(argumentsList[4]) setProfileId(profileId); + setUpdateAt(); + } + + private boolean check(String text) { + return text != null && !text.trim().isEmpty(); + } + + private void setName(String name) { + this.name = name; + } + + private void setPassword(String password) { + this.password = password; + } + + private void setEmail(String email) { + this.email = email; + } + + private void setPhoneNumber(String phoneNumber) { + this.phoneNumber = phoneNumber; + } + + private void setProfileId(UUID profileId) { + this.profileId = profileId; + } + + @Override + public String toString() { + return "사용자ID : " + this.getId() + + "\n사용자명 : " + this.getName() + + "\n이메일 : " + this.getEmail() + + "\n전화번호 : " + this.getPhoneNumber() + + "\n생성일 : " + FORMATTER.format(this.getCreateAt()) + + "\n수정일 : " + FORMATTER.format(this.getUpdateAt()); + } +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/entity/UserStatus.java b/discordit/src/main/java/com/sprint/mission/discodeit/entity/UserStatus.java new file mode 100644 index 00000000..dc693d5e --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/entity/UserStatus.java @@ -0,0 +1,44 @@ +package com.sprint.mission.discodeit.entity; + +import lombok.Getter; + +import java.io.Serializable; +import java.time.Duration; +import java.time.Instant; +import java.util.UUID; + +@Getter +public class UserStatus implements Serializable { + private final UUID id; + private final UUID userId; + private final String userName; + private final Instant createAt; + private Instant updateAt; + + public UserStatus(UUID userId, String userName) { + this.id = UUID.randomUUID(); + Instant now = Instant.now(); + this.userId = userId; + this.userName = userName; + updateAt = now; + createAt = now; + } + + public void lastAccessTimeUpdater() { + updateAt = Instant.now(); + } + + public void lastAccessTimeUpdater(Instant time) { + updateAt = time; + } + + public String isOnline() { + try { + Duration duration = Duration.between(this.getUpdateAt(), Instant.now()); + if(duration.toMinutes() > 5) return "오프라인"; + return "온라인"; + } catch (Exception ignore) { + return "오프라인"; + } + } +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/exepction/DoNotDuplicate.java b/discordit/src/main/java/com/sprint/mission/discodeit/exepction/DoNotDuplicate.java new file mode 100644 index 00000000..78392ce7 --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/exepction/DoNotDuplicate.java @@ -0,0 +1,7 @@ +package com.sprint.mission.discodeit.exepction; + +public class DoNotDuplicate extends RuntimeException { + public DoNotDuplicate(String message) { + super(message); + } +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/exepction/DoNotUpdatePrivateChannel.java b/discordit/src/main/java/com/sprint/mission/discodeit/exepction/DoNotUpdatePrivateChannel.java new file mode 100644 index 00000000..69a153b2 --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/exepction/DoNotUpdatePrivateChannel.java @@ -0,0 +1,7 @@ +package com.sprint.mission.discodeit.exepction; + +public class DoNotUpdatePrivateChannel extends RuntimeException { + public DoNotUpdatePrivateChannel(String message) { + super(message); + } +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/exepction/FailedCreate.java b/discordit/src/main/java/com/sprint/mission/discodeit/exepction/FailedCreate.java new file mode 100644 index 00000000..86f877b2 --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/exepction/FailedCreate.java @@ -0,0 +1,7 @@ +package com.sprint.mission.discodeit.exepction; + +public class FailedCreate extends RuntimeException { + public FailedCreate(String message) { + super(message); + } +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/exepction/FailedDelete.java b/discordit/src/main/java/com/sprint/mission/discodeit/exepction/FailedDelete.java new file mode 100644 index 00000000..d1917239 --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/exepction/FailedDelete.java @@ -0,0 +1,7 @@ +package com.sprint.mission.discodeit.exepction; + +public class FailedDelete extends RuntimeException { + public FailedDelete(String message) { + super(message); + } +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/exepction/FailedFound.java b/discordit/src/main/java/com/sprint/mission/discodeit/exepction/FailedFound.java new file mode 100644 index 00000000..acca7e0a --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/exepction/FailedFound.java @@ -0,0 +1,7 @@ +package com.sprint.mission.discodeit.exepction; + +public class FailedFound extends RuntimeException { + public FailedFound(String message) { + super(message); + } +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/exepction/FailedInit.java b/discordit/src/main/java/com/sprint/mission/discodeit/exepction/FailedInit.java new file mode 100644 index 00000000..98d5fbab --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/exepction/FailedInit.java @@ -0,0 +1,7 @@ +package com.sprint.mission.discodeit.exepction; + +public class FailedInit extends RuntimeException { + public FailedInit(String message) { + super(message); + } +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/exepction/FailedUpdate.java b/discordit/src/main/java/com/sprint/mission/discodeit/exepction/FailedUpdate.java new file mode 100644 index 00000000..89142438 --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/exepction/FailedUpdate.java @@ -0,0 +1,7 @@ +package com.sprint.mission.discodeit.exepction; + +public class FailedUpdate extends RuntimeException { + public FailedUpdate(String message) { + super(message); + } +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/exepction/NotFound.java b/discordit/src/main/java/com/sprint/mission/discodeit/exepction/NotFound.java new file mode 100644 index 00000000..a22bbeed --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/exepction/NotFound.java @@ -0,0 +1,7 @@ +package com.sprint.mission.discodeit.exepction; + +public class NotFound extends RuntimeException { + public NotFound(String message) { + super(message); + } +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/readme.md b/discordit/src/main/java/com/sprint/mission/discodeit/readme.md new file mode 100644 index 00000000..8d2c56c7 --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/readme.md @@ -0,0 +1,17 @@ +# 멘토님께 +먼저, 제가 테스트 코드를 작성하지 않아서 테스트가 어려운 점, 죄송합니다.\ +제가 테스트 코드를 어떻게 작성해야 할지 몰라.. 지금과 같이 되어, 아래와 같은 순서로 테스트해보시는 방안에 대한 의견을 드려봅니다. + +1. 유저 관련 메서드 + - 유저 생성 (다중 생성 및 중복 생성 테스트 / 현재는 이름 중복이 불가능합니다.) + - 유저 업데이트 (정규식을 이용했기에, 빈 문자열이 아니면 검증하도록 되어 있습니다) + - 유저 조회 +2. 채널 관련 메서드 + - 채널 생성 (다중 생성 및 중복 생성 테스트 / 현재는 이름으로 중복 검사 중입니다.) + - 채널 업데이트 (현재는 이름만 변경 가능합니다) + - 채널 조회 +3. 메시지 관련 메서드 + - 메세지 생성 (사용자명 -> 채널명 -> 보낼 메시지) + - 메세지 조회 (채널명으로, 사용자명으로 조회가 가능합니다.) + - 메세지 삭제 +4. 유저/채널 삭제시 해당 유저가 보낸/채널에 있는 메시지가 삭제됩니다. \ No newline at end of file diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/repository/BinaryContentRepository.java b/discordit/src/main/java/com/sprint/mission/discodeit/repository/BinaryContentRepository.java new file mode 100644 index 00000000..9279d4a0 --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/repository/BinaryContentRepository.java @@ -0,0 +1,18 @@ +package com.sprint.mission.discodeit.repository; + +import com.sprint.mission.discodeit.dto.CreateBinaryContentDto; + +import java.util.List; +import java.util.UUID; + +public interface BinaryContentRepository { + UUID create(CreateBinaryContentDto requestDto); + + String find(UUID id); + + List findAllByIdIn(List ids); + + boolean delete(UUID id); + + void delete(List ids); +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/repository/ChannelRepository.java b/discordit/src/main/java/com/sprint/mission/discodeit/repository/ChannelRepository.java new file mode 100644 index 00000000..5df3a256 --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/repository/ChannelRepository.java @@ -0,0 +1,43 @@ +package com.sprint.mission.discodeit.repository; + +import com.sprint.mission.discodeit.dto.ResponseChannelDto; +import com.sprint.mission.discodeit.dto.UpdateChannelDto; +import com.sprint.mission.discodeit.entity.Channel; +import com.sprint.mission.discodeit.entity.ChannelType; + +import java.util.List; +import java.util.UUID; + +public interface ChannelRepository { + boolean save(Channel channel); + boolean save(UpdateChannelDto requestDto); + String readChannel(String name); + + ChannelType getChannelType(String name); + + UUID getChannelId(String name); + + List readAllChannel(String userName); + + List findAllPrivateChannel(String userName); + + List accessiblePrivateChannel(String userName); + + ResponseChannelDto requestChannelInfo(Channel channel); + + void includePrivateChannel(String channelName, String userName, UUID userId); + + void excludePrivateChannel(String channelName, String userName); + + boolean deleteChannel(String name); + + void deleteAllChannel(String name); + + boolean isPresentChannel(String name); + + boolean isCreatePrivateChannel(String name); + + UUID channelNameToId(String name); + + String channelIdToName(UUID id); +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/repository/MessageRepository.java b/discordit/src/main/java/com/sprint/mission/discodeit/repository/MessageRepository.java new file mode 100644 index 00000000..b392ceea --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/repository/MessageRepository.java @@ -0,0 +1,25 @@ +package com.sprint.mission.discodeit.repository; + +import com.sprint.mission.discodeit.dto.MessageRequestDto; + +import java.time.Instant; +import java.util.List; +import java.util.UUID; + +public interface MessageRepository { + UUID create(String content, UUID channelId, UUID userId, List attachmentIdList); + + List findAllInChannel(UUID userId); + + Instant getLastMessageInChannel(UUID channelId); + + List findAllForSender(UUID userId); + + boolean updateMessage(UUID id, String content); + + UUID delete(UUID userId, UUID id); + + List> deleteAll(UUID channelId); + + boolean isPresentMessage(UUID userId, UUID id); +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/repository/ReadStatusRepository.java b/discordit/src/main/java/com/sprint/mission/discodeit/repository/ReadStatusRepository.java new file mode 100644 index 00000000..e11fe9ab --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/repository/ReadStatusRepository.java @@ -0,0 +1,21 @@ +package com.sprint.mission.discodeit.repository; + +import java.time.Instant; +import java.util.Map; +import java.util.UUID; + +public interface ReadStatusRepository { + void create(UUID userId, UUID channelId); + + Instant find(UUID id); + + Map findAllByUserId(UUID userId); + + boolean update(UUID userId, UUID channelId); + + boolean delete(UUID id); + + void deleteForChannel(UUID channelId); + + void deleteForUser(UUID userId); +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/repository/UserRepository.java b/discordit/src/main/java/com/sprint/mission/discodeit/repository/UserRepository.java new file mode 100644 index 00000000..592928ce --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/repository/UserRepository.java @@ -0,0 +1,28 @@ +package com.sprint.mission.discodeit.repository; + +import com.sprint.mission.discodeit.dto.CreateUserDto; +import com.sprint.mission.discodeit.dto.UpdateUserDto; +import com.sprint.mission.discodeit.dto.UserFinder; + +import java.util.List; +import java.util.UUID; + +public interface UserRepository { + UUID create(CreateUserDto dto); + + boolean update(UpdateUserDto requestDto); + + UserFinder find(String name); + + List findAll(); + + boolean delete(UUID id); + + UUID userNameToId(String name); + + String userIdToName(UUID id); + + boolean checkInvalid(UUID id, String pw); + + boolean duplicateChecker(String checkThis, String findThis); +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/repository/UserStatusRepository.java b/discordit/src/main/java/com/sprint/mission/discodeit/repository/UserStatusRepository.java new file mode 100644 index 00000000..06e3f332 --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/repository/UserStatusRepository.java @@ -0,0 +1,20 @@ +package com.sprint.mission.discodeit.repository; + +import com.sprint.mission.discodeit.dto.CreateUserStatusDto; +import com.sprint.mission.discodeit.dto.DeleteUserStatusDto; +import com.sprint.mission.discodeit.dto.FindUserStatusDto; +import com.sprint.mission.discodeit.dto.UserStatusUpdateDto; + +import java.util.List; + +public interface UserStatusRepository { + boolean create(CreateUserStatusDto requestDto); + + String find(FindUserStatusDto requestDto); + + List findAll(List requestDto); + + boolean update(UserStatusUpdateDto requestDto); + + boolean delete(DeleteUserStatusDto requestDto); +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/repository/file/FileBinaryContentRepository.java b/discordit/src/main/java/com/sprint/mission/discodeit/repository/file/FileBinaryContentRepository.java new file mode 100644 index 00000000..037707a6 --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/repository/file/FileBinaryContentRepository.java @@ -0,0 +1,117 @@ +package com.sprint.mission.discodeit.repository.file; + +import com.sprint.mission.discodeit.dto.CreateBinaryContentDto; +import com.sprint.mission.discodeit.entity.AttachmentType; +import com.sprint.mission.discodeit.entity.BinaryContent; +import com.sprint.mission.discodeit.exepction.FailedCreate; +import com.sprint.mission.discodeit.exepction.FailedDelete; +import com.sprint.mission.discodeit.exepction.FailedInit; +import com.sprint.mission.discodeit.repository.BinaryContentRepository; +import jakarta.annotation.PostConstruct; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Repository; + +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +@Repository +@Profile("file") +public class FileBinaryContentRepository implements BinaryContentRepository { + private final Map fileIdMap = new ConcurrentHashMap<>(); + private Path DIRECTORY; + private final String EXTENSION = ".ser"; + + private Path resolvePath(UUID id) { + String EXTENSION = ".ser"; + return DIRECTORY.resolve(id + EXTENSION); + } + + @PostConstruct + public void init() { + this.DIRECTORY = Paths.get(System.getProperty("user.dir"), "file-data-map", BinaryContent.class.getSimpleName()); + if (Files.notExists(DIRECTORY)) { + try { + Files.createDirectories(DIRECTORY); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + try { + Files.list(DIRECTORY).filter(path-> path.toString().endsWith(EXTENSION)) + .map(path->{ + try (FileInputStream fis = new FileInputStream(path.toFile()); + ObjectInputStream ois = new ObjectInputStream(fis)) { + return (BinaryContent) ois.readObject(); + } catch (IOException | ClassNotFoundException e) { + throw new RuntimeException(e); + } + }).forEach(binaryContent -> { + fileIdMap.put(binaryContent.getId(), binaryContent); + }); + } catch (Exception e) { + throw new FailedInit("FileBinaryContentRepository init failed"); + } + } + + @Override + public UUID create(CreateBinaryContentDto requestDto) { + AttachmentType type = requestDto.type(); + String file = requestDto.filename(); + BinaryContent binaryContent = new BinaryContent(type, file, null); + + Path path = resolvePath(binaryContent.getId()); + try(FileOutputStream fos = new FileOutputStream(path.toFile()); + ObjectOutputStream oos = new ObjectOutputStream(fos)) { + oos.writeObject(binaryContent); + } catch (IOException e) { + throw new FailedCreate("파일 생성에 실패했습니다."); + } + + fileIdMap.put(binaryContent.getId(), binaryContent); + + return binaryContent.getId(); + } + + @Override + public String find(UUID id) { + try { + return fileIdMap.get(id).toString(); + } catch (Exception e) { + System.err.println("왠지 모르지만, 여기서 오류난다. " + e.getMessage()); + return null; + } + } + + @Override + public List findAllByIdIn(List ids) { + List result = new ArrayList<>(); + ids.forEach(id -> result.add(fileIdMap.get(id).toString())); + return result; + } + + @Override + public boolean delete(UUID id) { + try { + Path path = resolvePath(id); + Files.deleteIfExists(path); + } catch (IOException e) { + throw new FailedDelete("IOException 발생"); + } + + fileIdMap.remove(id); + return true; + } + + @Override + public void delete(List ids) { + ids.forEach(this::delete); + } +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/repository/file/FileChannelRepository.java b/discordit/src/main/java/com/sprint/mission/discodeit/repository/file/FileChannelRepository.java new file mode 100644 index 00000000..116eaaaf --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/repository/file/FileChannelRepository.java @@ -0,0 +1,269 @@ +package com.sprint.mission.discodeit.repository.file; + +import com.sprint.mission.discodeit.dto.ResponseChannelDto; +import com.sprint.mission.discodeit.dto.UpdateChannelDto; +import com.sprint.mission.discodeit.entity.Channel; +import com.sprint.mission.discodeit.entity.ChannelType; +import com.sprint.mission.discodeit.exepction.*; +import com.sprint.mission.discodeit.repository.ChannelRepository; +import jakarta.annotation.PostConstruct; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Repository; + +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +@Repository +@Profile("file") +public class FileChannelRepository implements ChannelRepository { + private final Map publicChannelNameMap = new ConcurrentHashMap<>(); + private final Map publicChannelIdMap = new ConcurrentHashMap<>(); + private final Map privateChannelNameMap = new ConcurrentHashMap<>(); + private final Map privateChannelIdMap = new ConcurrentHashMap<>(); + private Path DIRECTORY; + private final String EXTENSION = ".ser"; + private Path resolvePath(UUID id) { + return DIRECTORY.resolve(id + EXTENSION); + } + + @PostConstruct + public void init() { + this.DIRECTORY = Paths.get(System.getProperty("user.dir"), "file-data-map", Channel.class.getSimpleName()); + if(Files.notExists(DIRECTORY)) { + try { + Files.createDirectories(DIRECTORY); + } + catch (IOException e) { + throw new RuntimeException(e); + } + } + + try { + Files.list(DIRECTORY) + .filter(path -> path.toString().endsWith(EXTENSION)) + .map(path -> { + try ( + FileInputStream fis = new FileInputStream(path.toFile()); + ObjectInputStream ois = new ObjectInputStream(fis) + ) { + return (Channel) ois.readObject(); + } catch (IOException | ClassNotFoundException e) { + throw new RuntimeException(e); + } + }).forEach(channel -> { + if (channel.getChannelType() == ChannelType.PRIVATE) { + privateChannelNameMap.put(channel.getName(), channel); + privateChannelIdMap.put(channel.getId(), channel); + } else { + publicChannelNameMap.put(channel.getName(), channel); + publicChannelIdMap.put(channel.getId(), channel); + } + }); + } catch (IOException e) { + throw new FailedInit("FileChannelRepository init failed"); + } + } + + /// interface + @Override + public boolean save(Channel channel) { + Path path = resolvePath(channel.getId()); + + try(FileOutputStream fos = new FileOutputStream(path.toFile()); + ObjectOutputStream oos = new ObjectOutputStream(fos)){ + oos.writeObject(channel); + oos.flush(); + if (channel.getChannelType() == ChannelType.PUBLIC) { + publicChannelNameMap.put(channel.getName(), channel); + publicChannelIdMap.put(channel.getId(), channel); + } else { + privateChannelIdMap.put(channel.getId(), channel); + privateChannelNameMap.put(channel.getName(), channel); + } + + return true; + } catch (IOException e) { + throw new FailedCreate("Channel save failed"); + } + } + + @Override + public boolean save(UpdateChannelDto requestDto) { + String oldName = requestDto.oldName(); + String newName = requestDto.newName(); + if(!isPresentChannel(oldName)) return false; + if(privateChannelNameMap.containsKey(oldName)) + throw new DoNotUpdatePrivateChannel("Do not update private channel"); + Path path; + Channel channel = publicChannelNameMap.get(oldName); + + path = resolvePath(channel.getId()); + + try(FileOutputStream fos = new FileOutputStream(path.toFile()); + ObjectOutputStream oos = new ObjectOutputStream(fos)){ + oos.writeObject(channel); + oos.flush(); + + publicChannelNameMap.put(newName, channel); + publicChannelNameMap.remove(oldName); + channel.channelUpdater(newName); + + return true; + } catch (IOException e) { + throw new FailedUpdate("Channel update failed"); + } + } + + @Override + public String readChannel(String name) { + if(publicChannelNameMap.containsKey(name)) + return publicChannelNameMap.get(name).toString(); + if(privateChannelNameMap.containsKey(name)) + return privateChannelNameMap.get(name).toString(); + throw new FailedFound("Channel not found"); + } + + @Override + public ChannelType getChannelType(String name) { + if(publicChannelNameMap.containsKey(name)) + return publicChannelNameMap.get(name).getChannelType(); + if(privateChannelNameMap.containsKey(name)) + return privateChannelNameMap.get(name).getChannelType(); + throw new FailedFound("ChannelType not found"); + } + + @Override + public UUID getChannelId(String name) { + if(publicChannelNameMap.containsKey(name)) + return publicChannelNameMap.get(name).getId(); + if(privateChannelNameMap.containsKey(name)) + return privateChannelNameMap.get(name).getId(); + + throw new FailedFound("ChannelId not found"); + } + + @Override + public List readAllChannel(String userName) { + List result = new ArrayList<>(); + + /// public + result.addAll(publicChannelNameMap.values().stream().map(this::requestChannelInfo).toList()); + + /// private + result.addAll(accessiblePrivateChannel(userName).stream().toList()); + return result; + } + + @Override + public List findAllPrivateChannel(String userName) { + if(accessiblePrivateChannel(userName).isEmpty()) + throw new NotFound("권한이 있는 Private Channel이 없습니다!"); + + return new ArrayList<>(accessiblePrivateChannel(userName).stream().toList()); + } + + @Override + public List accessiblePrivateChannel(String userName) { + List requestDto = new ArrayList<>(); + privateChannelIdMap.values().stream() + .filter(channel -> channel.getAccessibleUser() != null && channel.getAccessibleUser().containsKey(userName)) + .forEach(channel -> requestDto.add(requestChannelInfo(channel))); + + return requestDto; + } + + @Override + public ResponseChannelDto requestChannelInfo(Channel channel) { + String name = channel.getName(); + UUID id = channel.getId(); + ChannelType type = channel.getChannelType(); + Instant createAt = channel.getCreateAt(); + Instant updateAt = channel.getUpdateAt(); + String createUser = channel.getCreateUser(); + Map accessableUser = null; + try { + accessableUser = channel.getAccessibleUser(); + } catch (Exception ignore) {} + + return new ResponseChannelDto(name, id, type, createAt, updateAt, createUser, accessableUser); + } + + @Override + public void includePrivateChannel(String channelName, String userName, UUID userId) { + privateChannelNameMap.get(channelName).addAccessibleUser(userName, userId); + } + + @Override + public void excludePrivateChannel(String channelName, String userName) { + privateChannelNameMap.get(channelName).removeAccessibleUser(userName); + } + + @Override + public boolean deleteChannel(String name) { + UUID id; + boolean isPrivate = privateChannelNameMap.containsKey(name); + + if(isPrivate){ + id = privateChannelNameMap.get(name).getId(); + } else { + id = publicChannelNameMap.get(name).getId(); + } + + Path path = resolvePath(id); + try { + Files.deleteIfExists(path); + if(isPrivate){ + privateChannelNameMap.remove(name); + privateChannelIdMap.remove(id); + } else { + publicChannelNameMap.remove(name); + publicChannelIdMap.remove(id); + } + return true; + } catch (IOException e) { + throw new FailedDelete("Channel delete failed"); + } + } + + @Override + public void deleteAllChannel(String name) { + List channels = privateChannelNameMap.values().stream().filter(channel -> channel.getCreateUser().equals(name)).toList(); + + channels.forEach(channel -> deleteChannel(channel.getName())); + } + + + @Override + public boolean isPresentChannel(String name) { + return publicChannelNameMap.containsKey(name) || privateChannelNameMap.containsKey(name); + } + + @Override + public boolean isCreatePrivateChannel(String name) { + return !privateChannelNameMap.values().stream().filter(channel -> channel.getCreateUser().equals(name)).toList().isEmpty(); + } + + @Override + public UUID channelNameToId(String name) { + if(publicChannelNameMap.containsKey(name)) return publicChannelNameMap.get(name).getId(); + if(privateChannelNameMap.containsKey(name)) return privateChannelNameMap.get(name).getId(); + + throw new NotFound("해당 채널을 찾을 수 없습니다"); + } + + @Override + public String channelIdToName(UUID id) { + if(publicChannelIdMap.containsKey(id)) return publicChannelIdMap.get(id).getName(); + if(privateChannelIdMap.containsKey(id)) return privateChannelIdMap.get(id).getName(); + + throw new NotFound("해당 채널을 찾을 수 없습니다"); + } +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/repository/file/FileMessageRepository.java b/discordit/src/main/java/com/sprint/mission/discodeit/repository/file/FileMessageRepository.java new file mode 100644 index 00000000..9978177a --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/repository/file/FileMessageRepository.java @@ -0,0 +1,221 @@ +package com.sprint.mission.discodeit.repository.file; + + +import com.sprint.mission.discodeit.dto.MessageRequestDto; +import com.sprint.mission.discodeit.entity.Message; +import com.sprint.mission.discodeit.exepction.*; +import com.sprint.mission.discodeit.repository.MessageRepository; +import jakarta.annotation.PostConstruct; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Repository; + +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.Instant; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +@Repository +@Profile("file") +public class FileMessageRepository implements MessageRepository { + private final Map> channelIdMessageMap = new ConcurrentHashMap<>(); + private final Map> userIdMessageMap = new ConcurrentHashMap<>(); + private final Map messageIdMap = new ConcurrentHashMap<>(128); + private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy년 MM월 dd일 HH시 mm분 ss초").withZone(ZoneId.of("Asia/Seoul")); + + private Path DIRECTORY; + private final String EXTENSION = ".ser"; + + private Path resolvePath(UUID id) { + String EXTENSION = ".ser"; + return DIRECTORY.resolve(id + EXTENSION); + } + + @PostConstruct + public void init() { + this.DIRECTORY = Paths.get(System.getProperty("user.dir"), "file-data-map", Message.class.getSimpleName()); + if (Files.notExists(DIRECTORY)) { + try { + Files.createDirectories(DIRECTORY); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + try { + Files.list(DIRECTORY).filter(path-> path.toString().endsWith(EXTENSION)) + .map(path->{ + try ( + FileInputStream fis = new FileInputStream(path.toFile()); + ObjectInputStream ois = new ObjectInputStream(fis) + ) { + return (Message) ois.readObject(); + } catch (IOException | ClassNotFoundException e) { + throw new RuntimeException(e); + } + }).forEach(message -> { + channelIdMessageMap + .computeIfAbsent(message.getSendChannelId(), id -> new ArrayList<>()) + .add(message); + + userIdMessageMap + .computeIfAbsent(message.getSenderUserId(), id -> new ArrayList<>()) + .add(message); + + messageIdMap.put(message.getId(), message); + }); + } catch (Exception e) { + throw new FailedInit("FileMessageRepository init failed"); + } + } + + @Override + public UUID create(String content, UUID channelId, UUID userId, List attachmentIdList) { + Message message = new Message(channelId, userId, content, attachmentIdList); + + messageIdMap.put(message.getId(), message); + channelIdMessageMap.computeIfAbsent(channelId, m -> new ArrayList<>()).add(message); + userIdMessageMap.computeIfAbsent(userId, m -> new ArrayList<>()).add(message); + + Path path = resolvePath(message.getId()); + try ( + FileOutputStream fos = new FileOutputStream(path.toFile()); + ObjectOutputStream oos = new ObjectOutputStream(fos) + ) { + oos.writeObject(message); + return message.getId(); + } catch (IOException e) { + throw new FailedCreate("Message create failed"); + } + } + + @Override + public List findAllInChannel(UUID channelId) { + List result = new ArrayList<>(); + try{ + List messages = channelIdMessageMap.get(channelId); + messages.stream().sorted(Comparator.comparing(Message::getCreateAt)) + .forEach(message -> { + result.add(new MessageRequestDto( + message.getId(), + message.getChannelId(), + message.getUserId(), + message.getAttachmentIds(), + FORMATTER.format(message.getCreateAt()), + FORMATTER.format(message.getUpdateAt()), message.getContent() + )); + }); + return result; + } catch (Exception e) { + return List.of(); + } + } + + @Override + public Instant getLastMessageInChannel(UUID channelId) { + try { + return channelIdMessageMap.get(channelId) + .stream().max(Comparator.comparing(Message::getCreateAt)).orElse(null).getCreateAt(); + } catch (NullPointerException e) { + throw new FailedFound("Last message not found"); + } + } + + @Override + public List findAllForSender(UUID userId) { + List result = new ArrayList<>(); + List messages = userIdMessageMap.get(userId); + try{ + if(messages != null) { + messages.stream().sorted(Comparator.comparing(Message::getCreateAt)) + .forEach(message -> { + result.add(new MessageRequestDto( + message.getId(), + message.getChannelId(), + message.getUserId(), + message.getAttachmentIds(), + FORMATTER.format(message.getCreateAt()), + FORMATTER.format(message.getUpdateAt()), + message.getContent() + )); + }); + } + } catch (Exception e) { + throw new FailedFound("Message not found"); + } + return result; + } + + @Override + public boolean updateMessage(UUID id, String content) { + Message message = messageIdMap.get(id); + if (message == null) throw new FailedFound("Message not found"); + + message.updateMessage(content); + + Path path = resolvePath(id); + try(FileOutputStream fos = new FileOutputStream(path.toFile()); + ObjectOutputStream oos = new ObjectOutputStream(fos)) { + oos.writeObject(message); + return true; + } catch (IOException e) { + throw new FailedUpdate("Message update failed"); + } + } + + @Override + public UUID delete(UUID userId, UUID id) { + List userMessages = userIdMessageMap.get(userId); + if (userMessages == null) throw new FailedFound("Message not found(Delete)"); + + Message message = userMessages.stream().filter(e -> e.getId().equals(id)).findFirst().orElse(null); + if (message == null) throw new FailedFound("Message not found(Delete)"); + + UUID channelId = message.getSendChannelId(); + + Path path = resolvePath(id); + try { + Files.deleteIfExists(path); + userIdMessageMap.get(userId).remove(message); + + if(channelIdMessageMap.containsKey(channelId)) { + channelIdMessageMap.get(channelId).remove(message); + } + messageIdMap.remove(message.getId()); + return message.getId(); + } catch (IOException e) { + throw new FailedDelete("Message delete failed"); + } + } + + @Override + public List> deleteAll(UUID id) { + List> result = new ArrayList<>(); + if(channelIdMessageMap.containsKey(id)) { + new ArrayList<>(channelIdMessageMap.get(id)).forEach(message -> { + result.add(message.getAttachmentIds()); + delete(message.getUserId(), message.getId()); + }); + } + if(userIdMessageMap.containsKey(id)) { + new ArrayList<>(userIdMessageMap.get(id)).forEach(message -> { + result.add(message.getAttachmentIds()); + delete(message.getUserId(), message.getId()); + }); + } + return result; + } + + @Override + public boolean isPresentMessage(UUID userId, UUID id) { + List messages = userIdMessageMap.get(userId); + if(messages == null) return false; + + Object result = messages.stream().filter(message -> message.getId().equals(id)).findFirst().orElse(null); + return result != null; + } +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/repository/file/FileReadStatusRepository.java b/discordit/src/main/java/com/sprint/mission/discodeit/repository/file/FileReadStatusRepository.java new file mode 100644 index 00000000..8768824b --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/repository/file/FileReadStatusRepository.java @@ -0,0 +1,137 @@ +package com.sprint.mission.discodeit.repository.file; + +import com.sprint.mission.discodeit.entity.ReadStatus; +import com.sprint.mission.discodeit.exepction.FailedDelete; +import com.sprint.mission.discodeit.exepction.FailedInit; +import com.sprint.mission.discodeit.exepction.FailedUpdate; +import com.sprint.mission.discodeit.exepction.NotFound; +import com.sprint.mission.discodeit.repository.ReadStatusRepository; +import jakarta.annotation.PostConstruct; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Repository; + +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.Instant; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +@Repository +@Profile("file") +public class FileReadStatusRepository implements ReadStatusRepository { + private final Map idReadStatusMap = new ConcurrentHashMap<>(); + private final Map> userIdReadStatusMap = new ConcurrentHashMap<>(); + private final Map> channelIdReadStatusMap = new ConcurrentHashMap<>(); + private Path DIRECTORY; + private final String EXTENSION = ".ser"; + + private Path resolvePath(UUID id) { + String EXTENSION = ".ser"; + return DIRECTORY.resolve(id + EXTENSION); + } + + @PostConstruct + public void init() { + this.DIRECTORY = Paths.get(System.getProperty("user.dir"), "file-data-map", ReadStatus.class.getSimpleName()); + if (Files.notExists(DIRECTORY)) { + try { + Files.createDirectories(DIRECTORY); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + try { + Files.list(DIRECTORY).filter(path-> path.toString().endsWith(EXTENSION)) + .map(path->{ + try (FileInputStream fis = new FileInputStream(path.toFile()); + ObjectInputStream ois = new ObjectInputStream(fis)) { + return (ReadStatus) ois.readObject(); + } catch (IOException | ClassNotFoundException e) { + throw new RuntimeException(e); + } + }).forEach(readstatus -> { + idReadStatusMap.put(readstatus.getId(), readstatus); + userIdReadStatusMap.computeIfAbsent(readstatus.getUserId(), id -> new ArrayList<>()).add(readstatus); + }); + } catch (Exception e) { + throw new FailedInit("FileReadStatusRepository init failed"); + } + } + + @Override + public void create(UUID userId, UUID channelId) { + ReadStatus readStatus = new ReadStatus(userId, channelId); + idReadStatusMap.put(readStatus.getId(), readStatus); + userIdReadStatusMap.computeIfAbsent(userId, id -> new ArrayList<>()).add(readStatus); + channelIdReadStatusMap.computeIfAbsent(userId, id -> new ArrayList<>()).add(readStatus); + Path path = resolvePath(readStatus.getId()); + try (FileOutputStream fos = new FileOutputStream(path.toFile()); + ObjectOutputStream oos = new ObjectOutputStream(fos)) { + oos.writeObject(readStatus); + } catch (IOException e) { + throw new FailedInit("FileReadStatusRepository init failed"); + } + } + + @Override + public Instant find(UUID id) { + return idReadStatusMap.get(id).getUpdateAt(); + } + + @Override + public Map findAllByUserId(UUID userId) { + List tempList = userIdReadStatusMap.get(userId); + Map result = new HashMap<>(); + for(ReadStatus temp : tempList) { + result.put(temp.getChannelId(), temp.getUpdateAt()); + } + + return result; + } + + @Override + public boolean update(UUID userId, UUID channelId) { + ReadStatus temp = userIdReadStatusMap.get(userId).stream().filter(readStatus -> readStatus.getChannelId().equals(channelId)).findFirst().orElse(null); + + if(temp == null) throw new NotFound("상태값을 찾지 못했습니다."); + + Path path = resolvePath(temp.getId()); + try (FileOutputStream fos = new FileOutputStream(path.toFile()); + ObjectOutputStream oos = new ObjectOutputStream(fos)) { + oos.writeObject(temp); + temp.updateReadAt(); + return true; + } catch (IOException e) { + throw new FailedUpdate("ReadStatus update failed"); + } + } + + @Override + public boolean delete(UUID id) { + Path path = resolvePath(id); + try { + Files.delete(path); + ReadStatus temp = idReadStatusMap.remove(id); + userIdReadStatusMap.remove(temp.getUserId()); + channelIdReadStatusMap.remove(temp.getChannelId()); + return true; + } catch (IOException e) { + throw new FailedDelete("ReadStatus delete failed"); + } + } + + @Override + public void deleteForChannel(UUID channelId) { + List temp = channelIdReadStatusMap.get(channelId); + temp.forEach(readStatus -> delete(readStatus.getId())); + } + + @Override + public void deleteForUser(UUID userId) { + List temp = userIdReadStatusMap.get(userId); + temp.forEach(readStatus -> delete(readStatus.getId())); + } +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/repository/file/FileUserRepository.java b/discordit/src/main/java/com/sprint/mission/discodeit/repository/file/FileUserRepository.java new file mode 100644 index 00000000..ec66cf07 --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/repository/file/FileUserRepository.java @@ -0,0 +1,171 @@ +package com.sprint.mission.discodeit.repository.file; + +import com.sprint.mission.discodeit.dto.CreateUserDto; +import com.sprint.mission.discodeit.dto.UpdateUserDto; +import com.sprint.mission.discodeit.dto.UserFinder; +import com.sprint.mission.discodeit.entity.User; +import com.sprint.mission.discodeit.exepction.*; +import com.sprint.mission.discodeit.repository.UserRepository; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Repository; + +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +@Repository +@Profile("file") +public class FileUserRepository implements UserRepository { + private final Map idUserMap = new ConcurrentHashMap<>(); + private final Map userNameIdMap = new ConcurrentHashMap<>(); + private final Path DIRECTORY; + private final String EXTENSION = ".ser"; + + public FileUserRepository() { + this.DIRECTORY = Paths.get(System.getProperty("user.dir"), "file-data-map", User.class.getSimpleName()); + if (Files.notExists(DIRECTORY)) { + try { + Files.createDirectories(DIRECTORY); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + try { + Files.list(DIRECTORY) + .filter(path -> path.toString().endsWith(EXTENSION)) + .map(path -> { + try ( + FileInputStream fis = new FileInputStream(path.toFile()); + ObjectInputStream ois = new ObjectInputStream(fis) + ) { + return (User) ois.readObject(); + } catch (IOException | ClassNotFoundException e) { + throw new RuntimeException(e); + } + }).forEach(user -> { + idUserMap.put(user.getId(), user); + userNameIdMap.put(user.getName(), user.getId()); + }); + } catch (IOException e) { + throw new FailedInit("FileUserRepository init failed"); + } + } + + private Path resolvePath(UUID id) { + String EXTENSION = ".ser"; + return DIRECTORY.resolve(id + EXTENSION); + } + + @Override + public UUID create(CreateUserDto dto) { + User user = dto.toEntity(); + userNameIdMap.put(user.getName(), user.getId()); + idUserMap.put(user.getId(), user); + Path path = resolvePath(user.getId()); + + try ( + FileOutputStream fos = new FileOutputStream(path.toFile()); + ObjectOutputStream oos = new ObjectOutputStream(fos) + ) { + oos.writeObject(user); + return user.getId(); + } catch (IOException e) { + throw new FailedCreate("FileBinaryContentRepository create failed"); + } + } + + @Override + public boolean update(UpdateUserDto requestDto) { + UUID userId = requestDto.id(); + String reName = requestDto.reName(); + String rePassword = requestDto.rePassword(); + String reMail = requestDto.reMail(); + String rePhoneNumber = requestDto.rePhoneNumber(); + UUID reProfileId = requestDto.reProfileId(); + + Path path = resolvePath(userId); + + try(FileOutputStream fos = new FileOutputStream(path.toFile()); + ObjectOutputStream oos = new ObjectOutputStream(fos)) { + oos.writeObject(idUserMap.get(userId)); + idUserMap.get(userId).updateUser(reName, rePassword, reMail, rePhoneNumber, reProfileId); + return true; + } catch (IOException e) { + throw new FailedUpdate("User update failed"); + } + } + + @Override + public UserFinder find(String name) { + User user = idUserMap.get(userNameIdMap.get(name)); + UUID id = user.getId(); + String userName = user.getName(); + + return new UserFinder(id, userName, user.toString(), user.getProfileId()); + } + + @Override + public List findAll() { + List result = new ArrayList<>(); + idUserMap.values().stream().sorted(Comparator.comparing(User::getName)).forEach(user -> { + UUID id = user.getId(); + String userName = user.getName(); + result.add(new UserFinder(id, userName, user.toString(), user.getProfileId())); + }); + return result; + } + + @Override + public boolean delete(UUID id) { + Path path = resolvePath(id); + try { + Files.delete(path); + } catch (IOException e) { + throw new FailedDelete("User delete failed"); + } + userNameIdMap.remove(idUserMap.get(id).getName()); + idUserMap.remove(id); + return true; + } + + @Override + public UUID userNameToId(String name) { + try { + return userNameIdMap.get(name); + } catch (Exception e) { + throw new FailedFound("Do not found this user : " + name); + } + } + + @Override + public String userIdToName(UUID id) { + try { + return idUserMap.get(id).getName(); + } catch (Exception e) { + throw new FailedFound("Do not found this user : " + id); + } + } + + @Override + public boolean checkInvalid(UUID id, String pw) { + try { + return !idUserMap.get(id).getPassword().equals(pw); + } catch (Exception e) { + return true; + } + } + + @Override + public boolean duplicateChecker(String checkThis, String findThis) { + try { + if (checkThis.equals("이메일") && idUserMap.values().stream().anyMatch(u -> u.getEmail().equals(findThis))) return true; + if (checkThis.equals("전화번호") && idUserMap.values().stream().anyMatch(u -> u.getPhoneNumber().equals(findThis))) return true; + if (checkThis.equals("사용자명") && userNameIdMap.get(findThis) != null) return true; + } catch (Exception ignored) {} + return false; + } +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/repository/file/FileUserStatusRepository.java b/discordit/src/main/java/com/sprint/mission/discodeit/repository/file/FileUserStatusRepository.java new file mode 100644 index 00000000..a9903b8c --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/repository/file/FileUserStatusRepository.java @@ -0,0 +1,135 @@ +package com.sprint.mission.discodeit.repository.file; + +import com.sprint.mission.discodeit.dto.CreateUserStatusDto; +import com.sprint.mission.discodeit.dto.DeleteUserStatusDto; +import com.sprint.mission.discodeit.dto.FindUserStatusDto; +import com.sprint.mission.discodeit.dto.UserStatusUpdateDto; +import com.sprint.mission.discodeit.entity.UserStatus; +import com.sprint.mission.discodeit.exepction.FailedCreate; +import com.sprint.mission.discodeit.exepction.FailedDelete; +import com.sprint.mission.discodeit.exepction.FailedInit; +import com.sprint.mission.discodeit.repository.UserStatusRepository; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Repository; + +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +@Repository +@Profile("file") +public class FileUserStatusRepository implements UserStatusRepository { + private final Map idMap = new ConcurrentHashMap<>(); + private final Map userIdMap = new ConcurrentHashMap<>(); + private final Map userNameMap = new ConcurrentHashMap<>(); + private final Path DIRECTORY; + private final String EXTENSION = ".ser"; + + public FileUserStatusRepository() { + this.DIRECTORY = Paths.get(System.getProperty("user.dir"), "file-data-map", UserStatus.class.getSimpleName()); + if (Files.notExists(DIRECTORY)) { + try { + Files.createDirectories(DIRECTORY); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + try { + Files.list(DIRECTORY) + .filter(path -> path.toString().endsWith(EXTENSION)) + .map(path -> { + try ( + FileInputStream fis = new FileInputStream(path.toFile()); + ObjectInputStream ois = new ObjectInputStream(fis) + ) { + return (UserStatus) ois.readObject(); + } catch (IOException | ClassNotFoundException e) { + throw new RuntimeException(e); + } + }).forEach(userStatus -> { + idMap.put(userStatus.getId(), userStatus); + userIdMap.put(userStatus.getUserId(), userStatus); + userNameMap.put(userStatus.getUserName(), userStatus); + }); + } catch (IOException e) { + throw new FailedInit("FileUserStatusRepository init failed"); + } + } + + private Path resolvePath(UUID id) { + String EXTENSION = ".ser"; + return DIRECTORY.resolve(id + EXTENSION); + } + + @Override + public boolean create(CreateUserStatusDto requestDto){ + UserStatus userStatus = new UserStatus(requestDto.id(), requestDto.name()); + idMap.put(userStatus.getId(), userStatus); + userIdMap.put(requestDto.id(), userStatus); + userNameMap.put(requestDto.name(), userStatus); + + return save(userStatus); + } + + @Override + public String find(FindUserStatusDto requestDto) { + if(!userIdMap.containsKey(requestDto.id())) + return ""; + return userIdMap.get(requestDto.id()).isOnline(); + } + + @Override + public List findAll(List requestDto) { + List result = new ArrayList<>(); + requestDto.forEach(req -> result.add(find(req))); + return result; + } + + @Override + public boolean update(UserStatusUpdateDto requestDto) { + UserStatus userStatus = userIdMap.get(requestDto.id()); + + if(requestDto.time() == null) { + userStatus.lastAccessTimeUpdater(); + } + + userStatus.lastAccessTimeUpdater(requestDto.time()); + + return save(userStatus); + } + + @Override + public boolean delete(DeleteUserStatusDto requestDto) { + UUID id = userIdMap.get(requestDto.id()).getId(); + + Path path = resolvePath(id); + try { + Files.deleteIfExists(path); + } catch (IOException e) { + throw new FailedDelete("UserStatus delete failed"); + } + + userIdMap.remove(requestDto.id()); + idMap.remove(userNameMap.remove(requestDto.name()).getId()); + + return true; + } + + private boolean save(UserStatus userStatus) { + Path path = resolvePath(userStatus.getId()); + try(FileOutputStream fos = new FileOutputStream(path.toFile()); + ObjectOutputStream oos = new ObjectOutputStream(fos)) { + oos.writeObject(userStatus); + return true; + } catch (IOException e) { + throw new FailedCreate("UserStatus save failed"); + } + } +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFBinaryContentRepository.java b/discordit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFBinaryContentRepository.java new file mode 100644 index 00000000..78ffb193 --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFBinaryContentRepository.java @@ -0,0 +1,59 @@ +package com.sprint.mission.discodeit.repository.jcf; + +import com.sprint.mission.discodeit.dto.CreateBinaryContentDto; +import com.sprint.mission.discodeit.entity.AttachmentType; +import com.sprint.mission.discodeit.entity.BinaryContent; +import com.sprint.mission.discodeit.exepction.FailedFound; +import com.sprint.mission.discodeit.repository.BinaryContentRepository; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Repository; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +@Repository +@Profile("jcf") +public class JCFBinaryContentRepository implements BinaryContentRepository { + private final Map fileIdMap = new ConcurrentHashMap<>(); + + @Override + public UUID create(CreateBinaryContentDto requestDto) { + AttachmentType type = requestDto.type(); + String file = requestDto.filename(); + BinaryContent binaryContent = new BinaryContent(type, file, null); + + fileIdMap.put(binaryContent.getId(), binaryContent); + + return binaryContent.getId(); + } + + @Override + public String find(UUID id) { + try { + return fileIdMap.get(id).toString(); + } catch (Exception e) { + throw new FailedFound("Binary content not found"); + } + } + + @Override + public List findAllByIdIn(List ids) { + List result = new ArrayList<>(); + ids.forEach(id -> result.add(fileIdMap.get(id).toString())); + return result; + } + + @Override + public boolean delete(UUID id) { + fileIdMap.remove(id); + return true; + } + + @Override + public void delete(List ids) { + ids.forEach(this::delete); + } +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFChannelRepository.java b/discordit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFChannelRepository.java new file mode 100644 index 00000000..c2e9c91e --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFChannelRepository.java @@ -0,0 +1,194 @@ +package com.sprint.mission.discodeit.repository.jcf; + +import com.sprint.mission.discodeit.dto.ResponseChannelDto; +import com.sprint.mission.discodeit.dto.UpdateChannelDto; +import com.sprint.mission.discodeit.entity.Channel; +import com.sprint.mission.discodeit.entity.ChannelType; +import com.sprint.mission.discodeit.exepction.DoNotUpdatePrivateChannel; +import com.sprint.mission.discodeit.exepction.FailedFound; +import com.sprint.mission.discodeit.exepction.NotFound; +import com.sprint.mission.discodeit.repository.ChannelRepository; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Repository; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +@Repository +@Profile("jcf") +public class JCFChannelRepository implements ChannelRepository { + private final Map publicChannelNameMap = new ConcurrentHashMap<>(); + private final Map publicChannelIdMap = new ConcurrentHashMap<>(); + private final Map privateChannelNameMap = new ConcurrentHashMap<>(); + private final Map privateChannelIdMap = new ConcurrentHashMap<>(); + + /// interface + @Override + public boolean save(Channel channel) { + if (channel.getChannelType() == ChannelType.PUBLIC) { + publicChannelNameMap.put(channel.getName(), channel); + publicChannelIdMap.put(channel.getId(), channel); + } else { + privateChannelIdMap.put(channel.getId(), channel); + privateChannelNameMap.put(channel.getName(), channel); + } + + return true; + } + + @Override + public boolean save(UpdateChannelDto requestDto) { + String oldName = requestDto.oldName(); + String newName = requestDto.newName(); + if(!isPresentChannel(oldName)) return false; + if(privateChannelNameMap.containsKey(oldName)) + throw new DoNotUpdatePrivateChannel("Do not update private channel"); + Channel channel = publicChannelNameMap.get(oldName); + publicChannelNameMap.put(newName, channel); + publicChannelNameMap.remove(oldName); + channel.channelUpdater(newName); + return true; + } + + @Override + public String readChannel(String name) { + if(publicChannelNameMap.containsKey(name)) + return publicChannelNameMap.get(name).toString(); + if(privateChannelNameMap.containsKey(name)) + return privateChannelNameMap.get(name).toString(); + throw new FailedFound("Channel not found"); + } + + @Override + public ChannelType getChannelType(String name) { + if(publicChannelNameMap.containsKey(name)) + return publicChannelNameMap.get(name).getChannelType(); + if(privateChannelNameMap.containsKey(name)) + return privateChannelNameMap.get(name).getChannelType(); + throw new FailedFound("ChannelType not found"); + } + + @Override + public UUID getChannelId(String name) { + if(publicChannelNameMap.containsKey(name)) + return publicChannelNameMap.get(name).getId(); + if(privateChannelNameMap.containsKey(name)) + return privateChannelNameMap.get(name).getId(); + + throw new FailedFound("ChannelId not found"); + } + + @Override + public List readAllChannel(String userName) { + List result = new ArrayList<>(); + + /// public + result.addAll(publicChannelNameMap.values().stream().map(this::requestChannelInfo).toList()); + + /// private + result.addAll(accessiblePrivateChannel(userName).stream().toList()); + return result; + } + + @Override + public List findAllPrivateChannel(String userName) { + if(accessiblePrivateChannel(userName).isEmpty()) + throw new NotFound("권한이 있는 Private Channel이 없습니다!"); + + return new ArrayList<>(accessiblePrivateChannel(userName).stream().toList()); + } + + @Override + public List accessiblePrivateChannel(String userName) { + List requestDto = new ArrayList<>(); + privateChannelIdMap.values().stream() + .filter(channel -> channel.getAccessibleUser() != null && channel.getAccessibleUser().containsKey(userName)) + .forEach(channel -> requestDto.add(requestChannelInfo(channel))); + + return requestDto; + } + + @Override + public ResponseChannelDto requestChannelInfo(Channel channel) { + String name = channel.getName(); + UUID id = channel.getId(); + ChannelType type = channel.getChannelType(); + Instant createAt = channel.getCreateAt(); + Instant updateAt = channel.getUpdateAt(); + String createUser = channel.getCreateUser(); + Map accessableUser = null; + try { + accessableUser = channel.getAccessibleUser(); + } catch (Exception ignore) {} + + return new ResponseChannelDto(name, id, type, createAt, updateAt, createUser, accessableUser); + } + + @Override + public void includePrivateChannel(String channelName, String userName, UUID userId) { + privateChannelNameMap.get(channelName).addAccessibleUser(userName, userId); + } + + @Override + public void excludePrivateChannel(String channelName, String userName) { + privateChannelNameMap.get(channelName).removeAccessibleUser(userName); + } + + @Override + public boolean deleteChannel(String name) { + UUID id; + boolean isPrivate = privateChannelNameMap.containsKey(name); + + if(isPrivate){ + id = privateChannelNameMap.get(name).getId(); + } else { + id = publicChannelNameMap.get(name).getId(); + } + + if(isPrivate){ + privateChannelNameMap.remove(name); + privateChannelIdMap.remove(id); + } else { + publicChannelNameMap.remove(name); + publicChannelIdMap.remove(id); + } + return true; + } + + @Override + public void deleteAllChannel(String name) { + List channels = privateChannelNameMap.values().stream().filter(channel -> channel.getCreateUser().equals(name)).toList(); + + channels.forEach(channel -> deleteChannel(channel.getName())); + } + + @Override + public boolean isPresentChannel(String name) { + return publicChannelNameMap.containsKey(name) || privateChannelNameMap.containsKey(name); + } + + @Override + public boolean isCreatePrivateChannel(String name) { + return !privateChannelNameMap.values().stream().filter(channel -> channel.getCreateUser().equals(name)).toList().isEmpty(); + } + + @Override + public UUID channelNameToId(String name) { + if(publicChannelNameMap.containsKey(name)) return publicChannelNameMap.get(name).getId(); + if(privateChannelNameMap.containsKey(name)) return privateChannelNameMap.get(name).getId(); + + throw new NotFound("해당 채널을 찾을 수 없습니다"); + } + + @Override + public String channelIdToName(UUID id) { + if(publicChannelIdMap.containsKey(id)) return publicChannelIdMap.get(id).getName(); + if(privateChannelIdMap.containsKey(id)) return privateChannelIdMap.get(id).getName(); + + throw new NotFound("해당 채널을 찾을 수 없습니다"); + } +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFMessageRepository.java b/discordit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFMessageRepository.java new file mode 100644 index 00000000..80ec58fb --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFMessageRepository.java @@ -0,0 +1,152 @@ +package com.sprint.mission.discodeit.repository.jcf; + + +import com.sprint.mission.discodeit.dto.MessageRequestDto; +import com.sprint.mission.discodeit.entity.Message; +import com.sprint.mission.discodeit.exepction.FailedDelete; +import com.sprint.mission.discodeit.exepction.FailedFound; +import com.sprint.mission.discodeit.repository.MessageRepository; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Repository; + +import java.time.Instant; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +@Repository +@Profile("jcf") +public class JCFMessageRepository implements MessageRepository { + private final Map> channelIdMessageMap = new ConcurrentHashMap<>(); + private final Map> userIdMessageMap = new ConcurrentHashMap<>(); + private final Map messageIdMap = new ConcurrentHashMap<>(128); + private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy년 MM월 dd일 HH시 mm분 ss초").withZone(ZoneId.of("Asia/Seoul")); + + @Override + public UUID create(String content, UUID channelId, UUID userId, List attachmentIdList) { + Message message = new Message(channelId, userId, content, attachmentIdList); + + messageIdMap.put(message.getId(), message); + channelIdMessageMap.computeIfAbsent(channelId, m -> new ArrayList<>()).add(message); + userIdMessageMap.computeIfAbsent(userId, m -> new ArrayList<>()).add(message); + + return message.getId(); + } + + @Override + public List findAllInChannel(UUID channelId) { + List result = new ArrayList<>(); + try{ + List messages = channelIdMessageMap.get(channelId); + messages.stream().sorted(Comparator.comparing(Message::getCreateAt)) + .forEach(message -> { + result.add(new MessageRequestDto( + message.getId(), + message.getChannelId(), + message.getUserId(), + message.getAttachmentIds(), + FORMATTER.format(message.getCreateAt()), + FORMATTER.format(message.getUpdateAt()), message.getContent() + )); + }); + return result; + } catch (Exception e) { + return List.of(); + } + } + + @Override + public Instant getLastMessageInChannel(UUID channelId) { + try { + return channelIdMessageMap.get(channelId) + .stream().max(Comparator.comparing(Message::getCreateAt)).orElse(null).getCreateAt(); + } catch (NullPointerException e) { + throw new FailedFound("Last message not found"); + } + } + + @Override + public List findAllForSender(UUID userId) { + List result = new ArrayList<>(); + List messages = userIdMessageMap.get(userId); + try{ + if(messages != null) { + messages.stream().sorted(Comparator.comparing(Message::getCreateAt)) + .forEach(message -> { + result.add(new MessageRequestDto( + message.getId(), + message.getChannelId(), + message.getUserId(), + message.getAttachmentIds(), + FORMATTER.format(message.getCreateAt()), + FORMATTER.format(message.getUpdateAt()), + message.getContent() + )); + }); + } + } catch (Exception e) { + throw new FailedFound("Message not found"); + } + return result; + } + + @Override + public boolean updateMessage(UUID id, String content) { + Message message = messageIdMap.get(id); + if (message == null) throw new FailedFound("Message not found"); + + message.updateMessage(content); + + return true; + } + + @Override + public UUID delete(UUID userId, UUID id) { + List userMessages = userIdMessageMap.get(userId); + if (userMessages == null) throw new FailedFound("Message not found(Delete)"); + + Message message = userMessages.stream().filter(e -> e.getId().equals(id)).findFirst().orElse(null); + if (message == null) throw new FailedFound("Message not found(Delete)"); + + UUID channelId = message.getSendChannelId(); + try { + userIdMessageMap.get(userId).remove(message); + + if(channelIdMessageMap.containsKey(channelId)) { + channelIdMessageMap.get(channelId).remove(message); + } + messageIdMap.remove(message.getId()); + return message.getId(); + } catch (Exception e) { + throw new FailedDelete("Message delete failed"); + } + } + + @Override + public List> deleteAll(UUID id) { + List> result = new ArrayList<>(); + if(channelIdMessageMap.containsKey(id)) { + new ArrayList<>(channelIdMessageMap.get(id)).forEach(message -> { + result.add(message.getAttachmentIds()); + delete(message.getUserId(), message.getId()); + }); + } + if(userIdMessageMap.containsKey(id)) { + new ArrayList<>(userIdMessageMap.get(id)).forEach(message -> { + result.add(message.getAttachmentIds()); + delete(message.getUserId(), message.getId()); + }); + } + return result; + } + + @Override + public boolean isPresentMessage(UUID userId, UUID id) { + List messages = userIdMessageMap.get(userId); + if(messages == null) return false; + + Object result = messages.stream().filter(message -> message.getId().equals(id)).findFirst().orElse(null); + return result != null; + } +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFReadStatusRepository.java b/discordit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFReadStatusRepository.java new file mode 100644 index 00000000..f02725cc --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFReadStatusRepository.java @@ -0,0 +1,73 @@ +package com.sprint.mission.discodeit.repository.jcf; + +import com.sprint.mission.discodeit.entity.ReadStatus; +import com.sprint.mission.discodeit.exepction.NotFound; +import com.sprint.mission.discodeit.repository.ReadStatusRepository; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Repository; + +import java.time.Instant; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +@Repository +@Profile("jcf") +public class JCFReadStatusRepository implements ReadStatusRepository { + private final Map idReadStatusMap = new ConcurrentHashMap<>(); + private final Map> userIdReadStatusMap = new ConcurrentHashMap<>(); + private final Map> channelIdReadStatusMap = new ConcurrentHashMap<>(); + + @Override + public void create(UUID userId, UUID channelId) { + ReadStatus readStatus = new ReadStatus(userId, channelId); + idReadStatusMap.put(readStatus.getId(), readStatus); + userIdReadStatusMap.computeIfAbsent(userId, id -> new ArrayList<>()).add(readStatus); + channelIdReadStatusMap.computeIfAbsent(userId, id -> new ArrayList<>()).add(readStatus); + } + + @Override + public Instant find(UUID id) { + return idReadStatusMap.get(id).getUpdateAt(); + } + + @Override + public Map findAllByUserId(UUID userId) { + List tempList = userIdReadStatusMap.get(userId); + Map result = new HashMap<>(); + for(ReadStatus temp : tempList) { + result.put(temp.getChannelId(), temp.getUpdateAt()); + } + + return result; + } + + @Override + public boolean update(UUID userId, UUID channelId) { + ReadStatus temp = userIdReadStatusMap.get(userId).stream().filter(readStatus -> readStatus.getChannelId().equals(channelId)).findFirst().orElse(null); + + if(temp == null) throw new NotFound("상태값을 찾지 못했습니다."); + + temp.updateReadAt(); + return true; + } + + @Override + public boolean delete(UUID id) { + ReadStatus temp = idReadStatusMap.remove(id); + userIdReadStatusMap.remove(temp.getUserId()); + channelIdReadStatusMap.remove(temp.getChannelId()); + return true; + } + + @Override + public void deleteForChannel(UUID channelId) { + List temp = channelIdReadStatusMap.get(channelId); + temp.forEach(readStatus -> delete(readStatus.getId())); + } + + @Override + public void deleteForUser(UUID userId) { + List temp = userIdReadStatusMap.get(userId); + temp.forEach(readStatus -> delete(readStatus.getId())); + } +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFUserRepository.java b/discordit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFUserRepository.java new file mode 100644 index 00000000..b39a5592 --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFUserRepository.java @@ -0,0 +1,106 @@ +package com.sprint.mission.discodeit.repository.jcf; + +import com.sprint.mission.discodeit.dto.CreateUserDto; +import com.sprint.mission.discodeit.dto.UpdateUserDto; +import com.sprint.mission.discodeit.dto.UserFinder; +import com.sprint.mission.discodeit.entity.User; +import com.sprint.mission.discodeit.exepction.FailedFound; +import com.sprint.mission.discodeit.repository.UserRepository; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Repository; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +@Repository +@Profile("jcf") +public class JCFUserRepository implements UserRepository { + private final Map idUserMap = new ConcurrentHashMap<>(); + private final Map userNameIdMap = new ConcurrentHashMap<>(); + + @Override + public UUID create(CreateUserDto dto) { + User user = dto.toEntity(); + userNameIdMap.put(user.getName(), user.getId()); + idUserMap.put(user.getId(), user); + + return user.getId(); + } + + @Override + public boolean update(UpdateUserDto requestDto) { + UUID userId = requestDto.id(); + String reName = requestDto.reName(); + String rePassword = requestDto.rePassword(); + String reMail = requestDto.reMail(); + String rePhoneNumber = requestDto.rePhoneNumber(); + UUID reProfileId = requestDto.reProfileId(); + + idUserMap.get(userId).updateUser(reName, rePassword, reMail, rePhoneNumber, reProfileId); + return true; + } + + @Override + public UserFinder find(String name) { + User user = idUserMap.get(userNameIdMap.get(name)); + UUID id = user.getId(); + String userName = user.getName(); + + return new UserFinder(id, userName, user.toString(), user.getProfileId()); + } + + @Override + public List findAll() { + List result = new ArrayList<>(); + idUserMap.values().stream().sorted(Comparator.comparing(User::getName)).forEach(user -> { + UUID id = user.getId(); + String userName = user.getName(); + result.add(new UserFinder(id, userName, user.toString(), user.getProfileId())); + }); + return result; + } + + @Override + public boolean delete(UUID id) { + userNameIdMap.remove(idUserMap.get(id).getName()); + idUserMap.remove(id); + return true; + } + + @Override + public UUID userNameToId(String name) { + try { + return userNameIdMap.get(name); + } catch (Exception e) { + throw new FailedFound("Do not found this user : " + name); + } + } + + @Override + public String userIdToName(UUID id) { + try { + return idUserMap.get(id).getName(); + } catch (Exception e) { + throw new FailedFound("Do not found this user : " + id); + } + } + + @Override + public boolean checkInvalid(UUID id, String pw) { + try { + return !idUserMap.get(id).getPassword().equals(pw); + } catch (Exception e) { + return true; + } + } + + @Override + public boolean duplicateChecker(String checkThis, String findThis) { + try { + if (checkThis.equals("이메일") && idUserMap.values().stream().anyMatch(u -> u.getEmail().equals(findThis))) return true; + if (checkThis.equals("전화번호") && idUserMap.values().stream().anyMatch(u -> u.getPhoneNumber().equals(findThis))) return true; + if (checkThis.equals("사용자명") && userNameIdMap.get(findThis) != null) return true; + } catch (Exception ignored) {} + return false; + } +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFUserStatusRepository.java b/discordit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFUserStatusRepository.java new file mode 100644 index 00000000..d13b0fa0 --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFUserStatusRepository.java @@ -0,0 +1,69 @@ +package com.sprint.mission.discodeit.repository.jcf; + +import com.sprint.mission.discodeit.dto.CreateUserStatusDto; +import com.sprint.mission.discodeit.dto.DeleteUserStatusDto; +import com.sprint.mission.discodeit.dto.FindUserStatusDto; +import com.sprint.mission.discodeit.dto.UserStatusUpdateDto; +import com.sprint.mission.discodeit.entity.UserStatus; +import com.sprint.mission.discodeit.repository.UserStatusRepository; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Repository; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +@Repository +@Profile("jcf") +public class JCFUserStatusRepository implements UserStatusRepository { + private final Map idMap = new ConcurrentHashMap<>(); + private final Map userIdMap = new ConcurrentHashMap<>(); + private final Map userNameMap = new ConcurrentHashMap<>(); + + @Override + public boolean create(CreateUserStatusDto requestDto){ + UserStatus userStatus = new UserStatus(requestDto.id(), requestDto.name()); + idMap.put(userStatus.getId(), userStatus); + userIdMap.put(requestDto.id(), userStatus); + userNameMap.put(requestDto.name(), userStatus); + + return true; + } + + @Override + public String find(FindUserStatusDto requestDto) { + if(!userIdMap.containsKey(requestDto.id())) + return ""; + return userIdMap.get(requestDto.id()).isOnline(); + } + + @Override + public List findAll(List requestDto) { + List result = new ArrayList<>(); + requestDto.forEach(req -> result.add(find(req))); + return result; + } + + @Override + public boolean update(UserStatusUpdateDto requestDto) { + UserStatus userStatus = userIdMap.get(requestDto.id()); + + if(requestDto.time() == null) { + userStatus.lastAccessTimeUpdater(); + } + + userStatus.lastAccessTimeUpdater(requestDto.time()); + + return true; + } + + @Override + public boolean delete(DeleteUserStatusDto requestDto) { + userIdMap.remove(requestDto.id()); + idMap.remove(userNameMap.remove(requestDto.name()).getId()); + + return true; + } +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/service/ChannelService.java b/discordit/src/main/java/com/sprint/mission/discodeit/service/ChannelService.java new file mode 100644 index 00000000..af29f7d6 --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/service/ChannelService.java @@ -0,0 +1,28 @@ +package com.sprint.mission.discodeit.service; + +import com.sprint.mission.discodeit.dto.FindChannelDto; +import com.sprint.mission.discodeit.dto.ResponseChannelDto; +import com.sprint.mission.discodeit.dto.UpdateChannelDto; + +import java.util.List; +import java.util.UUID; + +public interface ChannelService { + boolean isPresent(String name); + boolean create(String type, String name, String createUserName); + FindChannelDto find(String name); + + List findAllPrivateChannel(String userName); + + List findAll(String userName); + boolean update(UpdateChannelDto requestDto); + boolean delete(String name); + + void deleteAll(String name); + + boolean includePrivateChannel(String channelName, String userName, UUID userId); + + void excludePrivateChannel(String channelName, String userName); + + boolean isCeatePrivateChannel(String userName); +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/service/MessageService.java b/discordit/src/main/java/com/sprint/mission/discodeit/service/MessageService.java new file mode 100644 index 00000000..16c9ea76 --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/service/MessageService.java @@ -0,0 +1,23 @@ +package com.sprint.mission.discodeit.service; + +import com.sprint.mission.discodeit.dto.CreateMessageDto; + +import java.util.List; +import java.util.UUID; + +public interface MessageService { + + boolean create(CreateMessageDto requestDto); + + boolean update(UUID userId, UUID messageId, String content); + + List findAllForSender(UUID userId); + + List findAllInChannel(String name); + + boolean delete(UUID userId, UUID messageId); + + void deleteAll(UUID channelId); + + String lastMessageTime(String channelName); +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/service/ReadStatusService.java b/discordit/src/main/java/com/sprint/mission/discodeit/service/ReadStatusService.java new file mode 100644 index 00000000..79b7a41e --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/service/ReadStatusService.java @@ -0,0 +1,18 @@ +package com.sprint.mission.discodeit.service; + +import java.time.Instant; +import java.util.UUID; + +public interface ReadStatusService { + void create(UUID userId, UUID channelId); + + Instant find(UUID id); + + void findAllByUserId(UUID userId); + + boolean update(UUID userId, String channelName); + + void deleteForChannel(UUID channelId); + + void deleteForUser(UUID userId); +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/service/UserService.java b/discordit/src/main/java/com/sprint/mission/discodeit/service/UserService.java new file mode 100644 index 00000000..b726fb04 --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/service/UserService.java @@ -0,0 +1,20 @@ +package com.sprint.mission.discodeit.service; + +import com.sprint.mission.discodeit.dto.CreateUserDto; +import com.sprint.mission.discodeit.dto.UpdateUserDto; +import com.sprint.mission.discodeit.dto.UserFinder; + +import java.util.List; +import java.util.UUID; + +public interface UserService { + boolean isPresent(String name); + + boolean isValid(UUID userId, String password); + + boolean create(CreateUserDto requestDto); + boolean update(UpdateUserDto requestDto); + UserFinder find(String name); + List findAll(); + boolean delete(UUID id); +} \ No newline at end of file diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/service/UserStatusService.java b/discordit/src/main/java/com/sprint/mission/discodeit/service/UserStatusService.java new file mode 100644 index 00000000..521fe028 --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/service/UserStatusService.java @@ -0,0 +1,4 @@ +package com.sprint.mission.discodeit.service; + +public interface UserStatusService { +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/service/auth/AuthService.java b/discordit/src/main/java/com/sprint/mission/discodeit/service/auth/AuthService.java new file mode 100644 index 00000000..214d3c98 --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/service/auth/AuthService.java @@ -0,0 +1,34 @@ +package com.sprint.mission.discodeit.service.auth; + +import com.sprint.mission.discodeit.UserState; +import com.sprint.mission.discodeit.dto.LoginDto; +import com.sprint.mission.discodeit.exepction.NotFound; +import com.sprint.mission.discodeit.repository.UserRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.UUID; + +@Service +@RequiredArgsConstructor +public class AuthService { + private final UserRepository userRepository; + private final UserState userState; + + public void login(LoginDto requestDto) { + /// 검증로직 + try { + UUID id = userRepository.userNameToId(requestDto.name()); + + if(!userRepository.checkInvalid(id, requestDto.password())) { + userState.userState(requestDto.name(), id); + } else throw new Exception(); + } catch (Exception ignore) { + throw new NotFound("해당 사용자를 찾지 못했습니다."); + } + } + + public void logout() { + userState.userState(""); + } +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicBinaryContentService.java b/discordit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicBinaryContentService.java new file mode 100644 index 00000000..d9912148 --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicBinaryContentService.java @@ -0,0 +1,31 @@ +package com.sprint.mission.discodeit.service.basic; + +import com.sprint.mission.discodeit.dto.CreateBinaryContentDto; +import com.sprint.mission.discodeit.repository.BinaryContentRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.UUID; + +@Service +@RequiredArgsConstructor +public class BasicBinaryContentService { + private final BinaryContentRepository binaryContentRepository; + + public UUID create(CreateBinaryContentDto createBinaryContentDto) { + return binaryContentRepository.create(createBinaryContentDto); + } + + public String find(UUID id) { + return binaryContentRepository.find(id); + } + + public List findAllByIdIn(List ids) { + return binaryContentRepository.findAllByIdIn(ids); + } + + public boolean delete(UUID id) { + return binaryContentRepository.delete(id); + } +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicChannelService.java b/discordit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicChannelService.java new file mode 100644 index 00000000..587baa9e --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicChannelService.java @@ -0,0 +1,99 @@ +package com.sprint.mission.discodeit.service.basic; + +import com.sprint.mission.discodeit.dto.FindChannelDto; +import com.sprint.mission.discodeit.dto.ResponseChannelDto; +import com.sprint.mission.discodeit.dto.UpdateChannelDto; +import com.sprint.mission.discodeit.entity.Channel; +import com.sprint.mission.discodeit.entity.ChannelType; +import com.sprint.mission.discodeit.repository.ChannelRepository; +import com.sprint.mission.discodeit.repository.MessageRepository; +import com.sprint.mission.discodeit.repository.UserRepository; +import com.sprint.mission.discodeit.service.ChannelService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.time.Instant; +import java.util.List; +import java.util.UUID; + +@Service +@RequiredArgsConstructor +public class BasicChannelService implements ChannelService { + private final ChannelRepository channelRepository; + private final UserRepository userRepository; + private final MessageRepository messageRepository; + + @Override + public boolean isPresent(String name) { + return channelRepository.isPresentChannel(name); + } + + @Override + public boolean create(String type, String name, String createUserName) { + UUID createUserId = userRepository.userNameToId(createUserName); + + if(type.equalsIgnoreCase("public") || type.equals("1")) { + channelRepository.save(new Channel(name, createUserName, createUserId)); + } else if (type.equalsIgnoreCase("private") || type.equals("2")) { + channelRepository.save(new Channel(name, createUserName, createUserId, ChannelType.PRIVATE)); + } else return false; + + return true; + } + + @Override + public FindChannelDto find(String name) { + UUID channelId = channelRepository.channelNameToId(name); + + String channelInfo = channelRepository.readChannel(name); + ChannelType channelType = channelRepository.getChannelType(name); + Instant lastMessageTime = messageRepository.getLastMessageInChannel(channelId); + + return new FindChannelDto(channelInfo, channelType, lastMessageTime); + } + + @Override + public List findAllPrivateChannel(String userName) { + return channelRepository.findAllPrivateChannel(userName); + } + + public UUID findChannelId(String channelName) { + return channelRepository.channelNameToId(channelName); + } + + @Override + public List findAll(String userName) { + return channelRepository.readAllChannel(userName); + } + + @Override + public boolean update(UpdateChannelDto requestDto) { + return channelRepository.save(requestDto); + } + + @Override + public boolean delete(String name) { + return channelRepository.deleteChannel(name); + } + + @Override + public void deleteAll(String name) { + channelRepository.deleteAllChannel(name); + } + + @Override + public boolean includePrivateChannel(String channelName, String userName, UUID userId) { + channelRepository.includePrivateChannel(channelName, userName, userId); + return true; + } + + @Override + public void excludePrivateChannel(String channelName, String userName) { + channelRepository.excludePrivateChannel(channelName, userName); + } + + @Override + public boolean isCeatePrivateChannel(String userName) { + return channelRepository.isCreatePrivateChannel(userName); + } +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicMessageService.java b/discordit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicMessageService.java new file mode 100644 index 00000000..7752dd83 --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicMessageService.java @@ -0,0 +1,96 @@ +package com.sprint.mission.discodeit.service.basic; + +import com.sprint.mission.discodeit.dto.CreateMessageDto; +import com.sprint.mission.discodeit.dto.MessageRequestDto; +import com.sprint.mission.discodeit.exepction.FailedFound; +import com.sprint.mission.discodeit.repository.BinaryContentRepository; +import com.sprint.mission.discodeit.repository.ChannelRepository; +import com.sprint.mission.discodeit.repository.MessageRepository; +import com.sprint.mission.discodeit.repository.UserRepository; +import com.sprint.mission.discodeit.service.MessageService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.UUID; + +@Service +@RequiredArgsConstructor +public class BasicMessageService implements MessageService { + private final UserRepository userRepository; + private final ChannelRepository channelRepository; + private final MessageRepository messageRepository; + private final BinaryContentRepository binaryContentRepository; + private final UUID nullUUID = UUID.fromString("00000000-0000-0000-0000-000000000000"); + + public boolean isPresent(UUID userId, UUID messageId) { + return messageRepository.isPresentMessage(userId, messageId); + } + + @Override + public boolean create(CreateMessageDto requestDto) { + String text = requestDto.text(); + String sendeeChannelName = requestDto.sendeeChannelName(); + String senderUserName = requestDto.senderUserName(); + List binaryContentIds = requestDto.binaryContentIds(); + + UUID channelId = channelRepository.getChannelId(sendeeChannelName); + UUID userId = userRepository.userNameToId(senderUserName); + messageRepository.create(text, channelId, userId, binaryContentIds); + + return true; + } + + @Override + public List findAllForSender(UUID userId) { + return messageRepository.findAllForSender(userId).stream().map(this::formattingMessage).toList(); + } + + @Override + public List findAllInChannel(String channelName) { + UUID channelId = channelRepository.channelNameToId(channelName); + return messageRepository.findAllInChannel(channelId).stream().map(this::formattingMessage).toList(); + } + + private String formattingMessage(MessageRequestDto dto) { + String id = "ID: " + dto.id().toString(); + String user = "사용자명: " + userRepository.userIdToName(dto.userId()); + String channel = "채널명: " + channelRepository.channelIdToName(dto.channelId()); + + return id + "\n" + + user + "\n" + + channel + "\n" + + "내용: " + dto.content() + "\n" + + "첨부파일: " + String.join("", binaryContentRepository.findAllByIdIn(dto.attachmentIds())) + "\n" + + "생성일: " + dto.createAt() + "\n" + + "수정일: " + dto.updateAt() + "\n===================="; + } + + @Override + public boolean update(UUID userId, UUID messageId, String content) { + if (messageRepository.isPresentMessage(userId, messageId)) { + throw new FailedFound("해당 ID를 찾지 못했습니다."); + } + + return messageRepository.updateMessage(messageId, content); + } + + @Override + public boolean delete(UUID userId, UUID messageId) { + UUID delete = messageRepository.delete(userId, messageId); + if(delete.equals(nullUUID)) return false; + binaryContentRepository.delete(delete); + return true; + } + + @Override + public void deleteAll(UUID id) { + messageRepository.deleteAll(id).forEach(binaryContentRepository::delete); + } + + @Override + public String lastMessageTime(String channelName) { + UUID channelId = channelRepository.channelNameToId(channelName); + return messageRepository.findAllInChannel(channelId).stream().map(MessageRequestDto::createAt).max(String::compareTo).orElse("없음"); + } +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicReadStatusService.java b/discordit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicReadStatusService.java new file mode 100644 index 00000000..f1d020ee --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicReadStatusService.java @@ -0,0 +1,50 @@ +package com.sprint.mission.discodeit.service.basic; + +import com.sprint.mission.discodeit.repository.ChannelRepository; +import com.sprint.mission.discodeit.repository.ReadStatusRepository; +import com.sprint.mission.discodeit.service.ReadStatusService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.time.Instant; +import java.util.Map; +import java.util.UUID; + +@Service +@RequiredArgsConstructor +public class BasicReadStatusService implements ReadStatusService { + private final ReadStatusRepository readStatusRepository; + private final ChannelRepository channelRepository; + + @Override + public void create(UUID userId, UUID channelId){ + readStatusRepository.create(userId, channelId); + } + + @Override + public Instant find(UUID id) { + return readStatusRepository.find(id); + } + @Override + public void findAllByUserId(UUID userId) { + Map result = readStatusRepository.findAllByUserId(userId); + } + @Override + public boolean update(UUID userId, String channelName) { + UUID channelId = channelRepository.channelNameToId(channelName); + try { + readStatusRepository.update(userId, channelId); + return true; + } catch (Exception e) { + return false; + } + } + @Override + public void deleteForChannel(UUID channelId) { + readStatusRepository.deleteForChannel(channelId); + } + @Override + public void deleteForUser(UUID userId) { + readStatusRepository.deleteForUser(userId); + } +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicUserService.java b/discordit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicUserService.java new file mode 100644 index 00000000..03d0281d --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicUserService.java @@ -0,0 +1,78 @@ +package com.sprint.mission.discodeit.service.basic; + +import com.sprint.mission.discodeit.dto.CreateUserDto; +import com.sprint.mission.discodeit.dto.UpdateUserDto; +import com.sprint.mission.discodeit.dto.UserFinder; +import com.sprint.mission.discodeit.exepction.FailedFound; +import com.sprint.mission.discodeit.repository.BinaryContentRepository; +import com.sprint.mission.discodeit.repository.UserRepository; +import com.sprint.mission.discodeit.service.UserService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.UUID; + +@Service +@RequiredArgsConstructor +public class BasicUserService implements UserService { + private final UserRepository userRepository; + private final BinaryContentRepository binaryContentRepository; + private final UUID nullUUID = UUID.fromString("00000000-0000-0000-0000-000000000000"); + + public UUID userNameToId(String name) { + return userRepository.userNameToId(name); + } + + @Override + public boolean isPresent(String name) { + return userRepository.userNameToId(name) != null; + } + + @Override + public boolean isValid(UUID userId, String password) { + return userRepository.checkInvalid(userId, password); + } + + @Override + public boolean create(CreateUserDto requestDto) { + UUID userId = userRepository.create(requestDto); + + return userId != nullUUID; + } + + /// Update + @Override + public boolean update(UpdateUserDto requestDto) { + UUID reProfileId = requestDto.reProfileId(); + UUID userId = requestDto.id(); + + if(userRepository.update(requestDto)) { + if(reProfileId != null){ + binaryContentRepository.delete(userId); + } + return true; + } + return false; + } + + /// Read + @Override + public UserFinder find(String name) { + if(userRepository.userNameToId(name) == null) + throw new FailedFound("해당 사용자를 찾지 못했습니다"); + return userRepository.find(name); + } + + @Override + public List findAll() { + return userRepository.findAll(); + } + + /// Delete + @Override + public boolean delete(UUID id) { + binaryContentRepository.delete(id); + return userRepository.delete(id); + } +} diff --git a/discordit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicUserStatusService.java b/discordit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicUserStatusService.java new file mode 100644 index 00000000..a1efbbe0 --- /dev/null +++ b/discordit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicUserStatusService.java @@ -0,0 +1,41 @@ +package com.sprint.mission.discodeit.service.basic; + +import com.sprint.mission.discodeit.dto.CreateUserStatusDto; +import com.sprint.mission.discodeit.dto.DeleteUserStatusDto; +import com.sprint.mission.discodeit.dto.FindUserStatusDto; +import com.sprint.mission.discodeit.dto.UserStatusUpdateDto; +import com.sprint.mission.discodeit.exepction.DoNotDuplicate; +import com.sprint.mission.discodeit.exepction.NotFound; +import com.sprint.mission.discodeit.repository.UserRepository; +import com.sprint.mission.discodeit.repository.UserStatusRepository; +import com.sprint.mission.discodeit.service.UserStatusService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class BasicUserStatusService implements UserStatusService { + private final UserStatusRepository userStatusRepository; + private final UserRepository userRepository; + + public void create(CreateUserStatusDto requestDto){ + FindUserStatusDto dto = new FindUserStatusDto(requestDto.id(), requestDto.name()); + + if(userRepository.userIdToName(requestDto.id()).isEmpty()) + throw new NotFound("해당 유저가 없습니다."); + if(!userStatusRepository.find(dto).isEmpty()) + throw new DoNotDuplicate("이미 존재합니다."); + + userStatusRepository.create(requestDto); + } + public String find(FindUserStatusDto requestDto) { + return userStatusRepository.find(requestDto); + } + public void findAll() {} + public void update(UserStatusUpdateDto requestDto) { + userStatusRepository.update(requestDto); + } + public void delete(DeleteUserStatusDto requestDto) { + userStatusRepository.delete(requestDto); + } +} diff --git a/discordit/src/main/resources/application.yml b/discordit/src/main/resources/application.yml new file mode 100644 index 00000000..abb908e6 --- /dev/null +++ b/discordit/src/main/resources/application.yml @@ -0,0 +1,8 @@ +spring: + application: + name: "discodeit" + +discodeit: + repository: + type: jcf # jcf | file + file-directory: .discodeit \ No newline at end of file diff --git a/discordit/src/test/java/com/sprint/mission/discodeit/DiscodeitApplicationTests.java b/discordit/src/test/java/com/sprint/mission/discodeit/DiscodeitApplicationTests.java new file mode 100644 index 00000000..3a987a21 --- /dev/null +++ b/discordit/src/test/java/com/sprint/mission/discodeit/DiscodeitApplicationTests.java @@ -0,0 +1,13 @@ +package com.sprint.mission.discodeit; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class DiscodeitApplicationTests { + + @Test + void contextLoads() { + } + +}