diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f7aa753 --- /dev/null +++ b/.gitignore @@ -0,0 +1,53 @@ +mannabom-server/HELP.md +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### IntelliJ IDEA ### +.idea/ +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ + +# Logs +*.log + +# Spring local configs / secrets +**/application-local.yml +**/application-local.properties +**/*.secret.* + +mannabom-server/.env* +mannabom-server/src/.env* +.DS_Store + +#firebase 비밀키 +**/src/main/resources/firebase/ +**/src/main/resources/firebase/*.json \ No newline at end of file diff --git a/mannabom-server/.gitattributes b/mannabom-server/.gitattributes new file mode 100644 index 0000000..8af972c --- /dev/null +++ b/mannabom-server/.gitattributes @@ -0,0 +1,3 @@ +/gradlew text eol=lf +*.bat text eol=crlf +*.jar binary diff --git a/mannabom-server/.github/PULL_REQUEST_TEMPLATE/default.md b/mannabom-server/.github/PULL_REQUEST_TEMPLATE/default.md new file mode 100644 index 0000000..81e7ca4 --- /dev/null +++ b/mannabom-server/.github/PULL_REQUEST_TEMPLATE/default.md @@ -0,0 +1,22 @@ +## 작업 내용 +- + +## 변경 이유 +- + +## 주요 변경 사항 +- + +## 테스트 +- [ ] 로컬 실행 확인 +- [ ] API 테스트 확인 +- [ ] DB 마이그레이션 확인 + +## 관련 이슈 +- closes # + +## 리뷰 포인트 +- + +## 참고 사항 +- \ No newline at end of file diff --git a/mannabom-server/.github/workflows/ci.yml b/mannabom-server/.github/workflows/ci.yml new file mode 100644 index 0000000..24252a1 --- /dev/null +++ b/mannabom-server/.github/workflows/ci.yml @@ -0,0 +1,85 @@ +name: CI + +on: + pull_request: + branches: [ "main", "dev" ] + push: + branches: [ "main", "dev" ] + +jobs: + test-and-coverage: + name: test-and-coverage + runs-on: ubuntu-latest + + services: + postgres: + image: postgres:13 + env: + POSTGRES_DB: mannabom_test + POSTGRES_USER: testuser + POSTGRES_PASSWORD: testpass + ports: + - 5432:5432 + options: >- + --health-cmd="pg_isready -U testuser -d mannabom_test" + --health-interval=10s + --health-timeout=5s + --health-retries=5 + + redis: + image: redis:6-alpine + ports: + - 6379:6379 + options: >- + --health-cmd="redis-cli ping" + --health-interval=10s + --health-timeout=5s + --health-retries=5 + + steps: + - name: Repository 체크 아웃 + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: JDK 17 설치 + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: 17 + + - name: Gradle 세팅 + uses: gradle/actions/setup-gradle@v4 + + - name: 실행 권한 부여 + working-directory: mannabom-server + run: chmod +x gradlew + + - name: Gradle 실행 및 테스트 결과 리포트 작성 + working-directory: mannabom-server + env: + SPRING_PROFILES_ACTIVE: test + run: ./gradlew clean test jacocoTestReport jacocoTestCoverageVerification + + - name: Python 설치 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: diff-cover 설치 + run: pip install diff-cover + + - name: PR 변경항목에 대해 테스트 커버리지 충족 여부 확인 + if: github.event_name == 'pull_request' + working-directory: mannabom-server + run: | + diff-cover build/reports/jacoco/test/jacocoTestReport.xml \ + --compare-branch=origin/${{ github.base_ref }} \ + --fail-under=80 + + - name: 테스트 결과 리포트 업로드 + if: always() + uses: actions/upload-artifact@v4 + with: + name: jacoco-report + path: mannabom-server/build/reports/jacoco/test/html/ \ No newline at end of file diff --git a/mannabom-server/Dockerfile b/mannabom-server/Dockerfile new file mode 100644 index 0000000..42bec2c --- /dev/null +++ b/mannabom-server/Dockerfile @@ -0,0 +1,11 @@ +FROM eclipse-temurin:17-jre-alpine + +WORKDIR /app + +ARG JAR_FILE=build/libs/*.jar +COPY ${JAR_FILE} app.jar + +ENV TZ=Asia/Seoul +RUN apk add --no-cache tzdata + +ENTRYPOINT ["java", "-jar","app.jar"] \ No newline at end of file diff --git a/mannabom-server/build.gradle b/mannabom-server/build.gradle new file mode 100644 index 0000000..2cbc2ae --- /dev/null +++ b/mannabom-server/build.gradle @@ -0,0 +1,114 @@ +plugins { + id 'java' + id 'org.springframework.boot' version '3.5.4' + id 'io.spring.dependency-management' version '1.1.7' + id 'jacoco' +} + +group = 'com.mannabom' +version = '0.0.1-SNAPSHOT' + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(17) + } +} + +repositories { + mavenCentral() +} + +dependencies { + implementation 'org.springframework.boot:spring-boot-starter-websocket' + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' // JPA/Hibernate + implementation 'org.springframework.boot:spring-boot-starter-security' // Spring Security + implementation 'org.springframework.boot:spring-boot-starter-web' // REST API + implementation 'org.springframework.boot:spring-boot-starter-validation' // 입력값 검증 + implementation 'org.springframework.boot:spring-boot-starter-data-redis' // Redis 연동 + implementation 'org.springframework.boot:spring-boot-starter-webflux' // 카카오 API 호출용 + implementation 'org.springframework.boot:spring-boot-starter-mail' // 이메일 SMTP + + // JWT 토큰 처리 + implementation 'io.jsonwebtoken:jjwt-api:0.12.3' + implementation 'io.jsonwebtoken:jjwt-impl:0.12.6' + implementation 'io.jsonwebtoken:jjwt-jackson:0.12.3' + + // 파일 업로드 처리 (로컬 개발용) + implementation 'commons-io:commons-io:2.14.0' + + // 파일 업로드 처리: AWS S3 SDK v2 (AWS S3 배포용) + implementation 'software.amazon.awssdk:s3:2.20.26' + implementation 'software.amazon.awssdk:auth:2.20.26' + implementation 'software.amazon.awssdk:regions:2.20.26' + + // Lombok + compileOnly 'org.projectlombok:lombok' + annotationProcessor 'org.projectlombok:lombok' + + testCompileOnly 'org.projectlombok:lombok' + testAnnotationProcessor 'org.projectlombok:lombok' + + // DB + runtimeOnly 'org.postgresql:postgresql' + + // fcm + implementation 'com.google.firebase:firebase-admin:9.2.0' + + testImplementation 'org.springframework.boot:spring-boot-starter-test' + testImplementation 'org.springframework.security:spring-security-test' + testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + + + // QueryDSL 설정 + implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta' + annotationProcessor "com.querydsl:querydsl-apt:5.0.0:jakarta" + annotationProcessor "jakarta.annotation:jakarta.annotation-api" + annotationProcessor "jakarta.persistence:jakarta.persistence-api" + + //매칭 시간 만료를 위한 redisson + implementation 'org.redisson:redisson-spring-boot-starter:3.26.0' + + //db 형상관리 flyway + implementation 'org.flywaydb:flyway-core' + implementation 'org.flywaydb:flyway-database-postgresql' + + +} + +test { + useJUnitPlatform() + finalizedBy jacocoTestReport +} + +jacoco { + toolVersion = "0.8.13" +} + +jacocoTestReport { + dependsOn test + + reports { + xml.required = true + csv.required = false + html.required = true + } +} + +jacocoTestCoverageVerification { + dependsOn test + + violationRules { + rule { + enabled = true + element = 'BUNDLE' + + limit { + counter = 'LINE' + value = 'COVEREDRATIO' + minimum = 0.70 + } + } + } +} + +check.dependsOn jacocoTestCoverageVerification \ No newline at end of file diff --git a/mannabom-server/docker-compose.local.yml b/mannabom-server/docker-compose.local.yml new file mode 100644 index 0000000..5078dcd --- /dev/null +++ b/mannabom-server/docker-compose.local.yml @@ -0,0 +1,32 @@ +# Docker Compose 설정 +version: '3.8' + +services: + # PostgreSQL DB + postgres: + image: postgres:13 + container_name: mannabom-postgres + environment: + POSTGRES_DB: mannabom_db + POSTGRES_USER: postgres # 사용자명 + POSTGRES_PASSWORD: password # 비밀번호 + ports: + - "5432:5432" # 호스트:컨테이너 포트 매핑 + volumes: + - postgres_data:/var/lib/postgresql/data + restart: unless-stopped # 재부팅 시 자동 시작 + + # Redis 캐시/세션 저장소 + redis: + image: redis:6-alpine + container_name: mannabom-redis + ports: + - "6379:6379" # 호스트:컨테이너 포트 매핑 + volumes: + - redis_data:/data + restart: unless-stopped + +# 볼륨 정의 +volumes: + postgres_data: + redis_data: \ No newline at end of file diff --git a/mannabom-server/docker-compose.prod.yml b/mannabom-server/docker-compose.prod.yml new file mode 100644 index 0000000..1466164 --- /dev/null +++ b/mannabom-server/docker-compose.prod.yml @@ -0,0 +1,33 @@ + +services: + mannabom-server: + image: ${ECR_REGISTRY_URI}:${IMAGE_TAG} + container_name: mannabom-server + environment: + - SPRING_PROFILES_ACTIVE=prod + env_file: + - .env + ports: + - 8080:8080 + depends_on: + - redis + networks: + - mannabom-net + volumes: + - /opt/mannabom/firebase:/opt/mannabom/firebase + + redis: + image: redis:alpine + container_name: redis + volumes: + - redis_data:/data + restart: unless-stopped + networks: + - mannabom-net + +networks: + mannabom-net: + driver: bridge + +volumes: + redis_data: {} \ No newline at end of file diff --git a/mannabom-server/gradle/wrapper/gradle-wrapper.jar b/mannabom-server/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..d997cfc Binary files /dev/null and b/mannabom-server/gradle/wrapper/gradle-wrapper.jar differ diff --git a/mannabom-server/gradle/wrapper/gradle-wrapper.properties b/mannabom-server/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..c61a118 --- /dev/null +++ b/mannabom-server/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/mannabom-server/gradlew b/mannabom-server/gradlew new file mode 100755 index 0000000..b821d99 --- /dev/null +++ b/mannabom-server/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, likeRequest: +# +# 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 likeRequest 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/mannabom-server/gradlew.bat b/mannabom-server/gradlew.bat new file mode 100644 index 0000000..c4bdd3a --- /dev/null +++ b/mannabom-server/gradlew.bat @@ -0,0 +1,93 @@ +@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 + + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -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/mannabom-server/settings.gradle b/mannabom-server/settings.gradle new file mode 100644 index 0000000..de04807 --- /dev/null +++ b/mannabom-server/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'mannabom-server' diff --git a/mannabom-server/src/main/java/com/mannabom/mannabom_server/MannabomServerApplication.java b/mannabom-server/src/main/java/com/mannabom/mannabom_server/MannabomServerApplication.java new file mode 100644 index 0000000..71f063b --- /dev/null +++ b/mannabom-server/src/main/java/com/mannabom/mannabom_server/MannabomServerApplication.java @@ -0,0 +1,13 @@ +package com.mannabom.mannabom_server; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class MannabomServerApplication { + + public static void main(String[] args) { + SpringApplication.run(MannabomServerApplication.class, args); + } + +} diff --git a/mannabom-server/src/main/resources/application-prod.yml b/mannabom-server/src/main/resources/application-prod.yml new file mode 100644 index 0000000..8fefbd2 --- /dev/null +++ b/mannabom-server/src/main/resources/application-prod.yml @@ -0,0 +1,140 @@ +spring: + application: + name: mannabom-server + + # AWS RDS PostgreSQL 설정 + datasource: + url: jdbc:postgresql://${AWS_RDS_ENDPOINT}:${AWS_RDS_PORT:5432}/${AWS_RDS_DATABASE} + username: ${AWS_RDS_USERNAME} + password: ${AWS_RDS_PASSWORD} + driver-class-name: org.postgresql.Driver + + # JPA/Hibernate 설정 + jpa: + hibernate: + ddl-auto: validate # update 사용 (배포용) + default_batch_fetch_size: 100 + show-sql: false # SQL 로그 비활성화 (배포용) + + flyway: + enabled: true + locations: classpath:db/migration + baseline-on-migrate: true + + # EC2 Redis 설정 (우선 AWS 전환X) + data: + redis: + host: ${REDIS_HOST} # EC2 Private IP 또는 localhost (같은 서버인 경우) + port: ${REDIS_PORT:6379} + password: ${REDIS_PASSWORD:} # Redis에 비밀번호 설정한 경우 + + # 파일 업로드 설정 + servlet: + multipart: + max-file-size: 10MB + max-request-size: 30MB + + # 이메일 설정 (우선 AWS 전환X) + mail: + host: smtp.gmail.com + port: 587 + username: ${MAIL_USERNAME} + password: ${MAIL_PASSWORD} + properties: + mail: + smtp: + auth: true + starttls: + enable: true + +# 커스텀 설정 +app: + # JWT 토큰 설정 + jwt: + secret: ${JWT_SECRET} # + access-token-expiration: 86_400_000 # 24시간 (밀리초) + refresh-token-expiration: 604_800_000 # 7일 (밀리초) + + # 카카오 OAuth 설정 (프로덕션) + kakao: + client-id: ${KAKAO_CLIENT_ID} + redirect-uri: ${KAKAO_REDIRECT_URI:https://your-domain.com/api/auth/login/kakao} + development: + skip-age-verification: true # 이거 아직 카카오 디벨로퍼 준비 안되서 + default-birth-year: 2000 + default-gender: "MALE" + + # AWS S3 설정 + aws: + region: ${AWS_REGION:ap-northeast-2} # 서울 리전 + s3: + bucket-name: ${AWS_S3_BUCKET_NAME} + access-key: ${AWS_ACCESS_KEY_ID} + secret-key: ${AWS_SECRET_ACCESS_KEY} + base-url: https://${AWS_S3_BUCKET_NAME}.s3.${AWS_REGION:ap-northeast-2}.amazonaws.com/ + + # 이메일 인증 설정 + email: + dummy-mode: false # 실제 이메일 발송 + +# 로깅 설정 (프로덕션) +logging: + level: + com.mannabom: INFO # 배포용은 INFO 레벨 + org.springframework.security: WARN + org.springframework.web: WARN + file: + name: /var/log/mannabom/app.log # 로그 파일 저장 + +# 서버 설정 +server: + port: ${SERVER_PORT:8080} + +meeting: + cursor: + secret: ${MEETING_SECRET_KEY:local-dev-key-do-not-user-in-prod} + match: + scheduler: + initial-delay: 1800000 + fixed_delay: 1800000 + +#fcm 설정 +fcm: + service-account-path: ${FCM_FILE_PATH:} + project_id: ${FCM_PROJECT_ID:} + url: https://fcm.googleapis.com/v1/projects/${fcm.project_id}/messages:send + google_api: https://www.googleapis.com/auth/cloud-platform + +# 운영 정책 설정 +match: + policy: + cooldown-hours: 72 # 프로필 추천으로 제공된 사용자가 다시 추천으로 뜰 수 있는 최소 시간 + candidate-pool-size: 200 # 추천을 하기 전에 조건에 맞는 사용자 추출(후보군 수) + pick-pool-size: 50 # 이번 추천 리스트로 뽑을 인원수의 상한(최종 인원수) + +ting: + policy: + vip-threshold: 200 # VIP로 분류되는 팅 보유 갯수 기준 + cost: + extra-profile: 5 # 팅을 소모해서 추가적인 프로필 추천을 받을 때 비용(팅 갯수) + extra-profile-bundle5: 20 # 추가 프로필 5개 묶음 비용 + message: 40 # 팅을 소모해서 메시지를 보낼 때 비용(팅 갯수) + like: 20 # 팅을 소모해서 호감 보낼 때 사용(팅 갯수) + view-extra-photo: 5 # 상대 사진 추가로 보기 + view-score: 5 # 상대가 내게 준 점수 조회 비용 + view-liked-me-profile: 5 # 나를 찜한 사람 프로필 보기 (변수 이름을 잘못 지었다 ㅠㅠ 수정 귀찮음,, 헷갈리지 말기) + view-high-score-profile: 5 # 나에게 높은 점수 준 유저 프로필 조회 비용 + +benefit: + policy: + membership: # 맴버쉽 혜택 + monthly-extra-profiles: 35 + monthly-free-messages: 10 + monthly-free-likes: 5 + vip: # VIP 혜택 + daily-extra-profiles: 2 + daily-free-messages: 0 + daily-free-likes: 0 + basic: # 기본 지급 + daily-profile: 10 + daily-love-view: 10 \ No newline at end of file diff --git a/mannabom-server/src/main/resources/application-test.yml b/mannabom-server/src/main/resources/application-test.yml new file mode 100644 index 0000000..68a949a --- /dev/null +++ b/mannabom-server/src/main/resources/application-test.yml @@ -0,0 +1,131 @@ +spring: + application: + name: mannabom-server + + datasource: + url: jdbc:postgresql://${TEST_DB_HOST:localhost}:${TEST_DB_PORT:5432}/${TEST_DB_NAME:mannabom_test} + username: ${TEST_DB_USERNAME:testuser} + password: ${TEST_DB_PASSWORD:testpass} + driver-class-name: org.postgresql.Driver + + jpa: + hibernate: + ddl-auto: validate + properties: + hibernate: + default_batch_fetch_size: 100 + use_sql_comments: true + format_sql: true + show-sql: false + + flyway: + enabled: true + locations: classpath:db/migration + baseline-on-migrate: true + + data: + redis: + host: ${TEST_REDIS_HOST:localhost} + port: ${TEST_REDIS_PORT:6379} + + servlet: + multipart: + max-file-size: 10MB + max-request-size: 30MB + + mail: + host: smtp.gmail.com + port: 587 + username: ${MAIL_USERNAME:test@example.com} + password: ${MAIL_PASSWORD:test-password} + properties: + mail: + smtp: + auth: true + starttls: + enable: true + +app: + aws: + region: ${AWS_REGION:ap-northeast-2} + s3: + bucket-name: ${S3_BUCKET_NAME:test-bucket} + access-key: ${S3_ACCESS_KEY:test-access-key} + secret-key: ${S3_SECRET_KEY:test-secret-key} + base-url: ${S3_BASE_URL:http://localhost/test-bucket} + + jwt: + secret: ${JWT_SECRET:mannabom-test-secret-key-for-ci-at-least-32chars} + access-token-expiration: 86_400_000 + refresh-token-expiration: 604_800_000 + + kakao: + client-id: ${KAKAO_CLIENT_ID:test-kakao-client-id} + redirect-uri: http://localhost:8080/api/auth/login/kakao + development: + skip-age-verification: true + default-birth-year: 2000 + default-gender: "MALE" + + file: + upload-dir: ./uploads-test + base-url: http://localhost:8080/files/ + + email: + dummy-mode: true + +logging: + level: + com.mannabom: INFO + org.springframework.security: WARN + org.hibernate.SQL: WARN + +server: + port: 8080 + +meeting: + cursor: + secret: ${MEETING_SECRET_KEY:test-meeting-secret-key} + match: + scheduler: + initial-delay: 1800000 + fixed_delay: 1800000 + +fcm: + service-account-path: ${FCM_FILE_PATH:} + project_id: ${FCM_PROJECT_ID:test-project} + url: https://fcm.googleapis.com/v1/projects/${fcm.project_id}/messages:send + google_api: https://www.googleapis.com/auth/cloud-platform + +match: + policy: + cooldown-hours: 72 + candidate-pool-size: 200 + pick-pool-size: 50 + +ting: + policy: + vip-threshold: 200 + cost: + extra-profile: 5 + extra-profile-bundle5: 20 + message: 40 + like: 20 + view-extra-photo: 5 + view-score: 5 + view-liked-me-profile: 5 + view-high-score-profile: 5 + +benefit: + policy: + membership: + monthly-extra-profiles: 35 + monthly-free-messages: 10 + monthly-free-likes: 5 + vip: + daily-extra-profiles: 2 + daily-free-messages: 0 + daily-free-likes: 0 + basic: + daily-profile: 10 + daily-love-view: 10 \ No newline at end of file diff --git a/mannabom-server/src/main/resources/application.yaml b/mannabom-server/src/main/resources/application.yaml new file mode 100644 index 0000000..64704f3 --- /dev/null +++ b/mannabom-server/src/main/resources/application.yaml @@ -0,0 +1,157 @@ +spring: + application: + name: mannabom-server + + # PostgreSQL DB 설정 (로컬용) + datasource: + url: jdbc:postgresql://localhost:5432/mannabom_db + username: postgres + password: password + driver-class-name: org.postgresql.Driver + + # JPA/Hibernate 설정 + jpa: + hibernate: + ddl-auto: validate # create(DB테스트용), update(개발용), validate(운영용) + default_batch_fetch_size: 100 + properties: + hibernate.hibernate.use_sql_comments: true + hibernate: + format_sql: true + show-sql: false # SQL 쿼리 로그 출력 (개발용) + + + flyway: + enabled: true + locations: classpath:db/migration + baseline-on-migrate: false + + + + + + # Redis 설정 + data: + redis: + host: localhost + port: 6379 + + # 파일 업로드 설정 + servlet: + multipart: + max-file-size: 10MB + max-request-size: 30MB + + # 이메일 설정 (역시 이후 AWS 전환해야) + mail: + host: smtp.gmail.com + port: 587 + username: ${MAIL_USERNAME:your-email@gmail.com} + password: ${MAIL_PASSWORD:your-app-password} + properties: + mail: + smtp: + auth: true + starttls: + enable: true + +# 커스텀 설정 +app: + aws: + region: ${AWS_REGION} + s3: + bucket-name: ${S3_BUCKET_NAME} + access-key: ${S3_ACCESS_KEY} + secret-key: ${S3_SECRET_KEY} + base-url: ${S3_BASE_URL} + # jwt 토큰 설정 + jwt: + secret: ${JWT_SECRET:mannabom-super-secret-key-for-development-only-change-in-production} # 32자 이상 필수 + access-token-expiration: 86_400_000 # 24시간 (밀리초) + refresh-token-expiration: 604_800_000 # 7일 (밀리초) + + # 카카오 OAuth 설정 + kakao: + client-id: ${KAKAO_CLIENT_ID:your-kakao-client-id} + redirect-uri: http://localhost:8080/api/auth/login/kakao # 임시로 카카오 디벨로퍼에 등록한 URI + # 운영환경에서는 실제 도메인으로 변경 필요 + # redirect-uri: https://company-domain.com/api/auth/login/kakao + + # 사업자 등록 하기 전 임시 개발환경 전용 설정 + development: + skip-age-verification: true # 연령검증 스킵 + default-birth-year: 2000 # 더미 출생년도 (24세) + default-gender: "MALE" # 더미 성별 + + # 파일 저장 설정 (로컬 개발용) + file: + upload-dir: ./uploads/ # 로컬 파일 저장 경로 + base-url: http://localhost:8080/files/ # 파일 접근 URL + # AWS S3 사용 시 설정 변경 필요 + + # 이메일 인증 설정 (로컬 개발용) + email: + dummy-mode: false # false=실제발송, true=콘솔테스트 + +# 로깅 설정 (개발용) +logging: + level: + com.mannabom: DEBUG + org.springframework.security: DEBUG + org.hibernate.SQL: DEBUG + org.hibernate.type.descriptor.sql: TRACE + +# 서버 설정 +server: + port: 8080 + + +# 미팅 리스트 커서 설정 +meeting: + cursor: + secret: ${MEETING_SECRET_KEY:local-dev-key-do-not-user-in-prod} + match: + scheduler: + initial-delay: 1800000 + fixed_delay: 1800000 + +#fcm 설정 +fcm: + service-account-path: ${FCM_FILE_PATH:} + project_id: ${FCM_PROJECT_ID:} + url: https://fcm.googleapis.com/v1/projects/${fcm.project_id}/messages:send + google_api: https://www.googleapis.com/auth/cloud-platform + +# 운영 정책 설정 +match: + policy: + cooldown-hours: 72 # 프로필 추천으로 제공된 사용자가 다시 추천으로 뜰 수 있는 최소 시간 + candidate-pool-size: 200 # 추천을 하기 전에 조건에 맞는 사용자 추출(후보군 수) + pick-pool-size: 50 # 이번 추천 리스트로 뽑을 인원수의 상한(최종 인원수) + +ting: + policy: + vip-threshold: 200 # VIP로 분류되는 팅 보유 갯수 기준 + cost: + extra-profile: 5 # 팅을 소모해서 추가적인 프로필 추천을 받을 때 비용(팅 갯수) + extra-profile-bundle5: 20 # 추가 프로필 5개 묶음 비용 + message: 40 # 팅을 소모해서 메시지를 보낼 때 비용(팅 갯수) + like: 20 # 팅을 소모해서 호감 보낼 때 사용(팅 갯수) + view-extra-photo: 5 # 상대 사진 추가로 보기 + view-score: 5 # 상대가 내게 준 점수 조회 비용 + view-liked-me-profile: 5 # 나를 찜한 사람 프로필 보기 (변수 이름을 잘못 지었다 ㅠㅠ 수정 귀찮음,, 헷갈리지 말기) + view-high-score-profile: 5 # 나에게 높은 점수 준 유저 프로필 조회 비용 + +benefit: + policy: + membership: # 맴버쉽 혜택 + monthly-extra-profiles: 35 + monthly-free-messages: 10 + monthly-free-likes: 5 + vip: # VIP 혜택 + daily-extra-profiles: 2 + daily-free-messages: 0 + daily-free-likes: 0 + basic: # 기본 지급 + daily-profile: 10 + daily-love-view: 10 \ No newline at end of file diff --git a/mannabom-server/src/main/resources/data/regions.csv b/mannabom-server/src/main/resources/data/regions.csv new file mode 100644 index 0000000..bfa6a42 --- /dev/null +++ b/mannabom-server/src/main/resources/data/regions.csv @@ -0,0 +1,245 @@ +sigunguCode,sidoCode,sidoName,sigunguName +11000,11,서울특별시,전체 +11680,11,서울특별시,강남구 +11740,11,서울특별시,강동구 +11305,11,서울특별시,강북구 +11500,11,서울특별시,강서구 +11620,11,서울특별시,관악구 +11215,11,서울특별시,광진구 +11530,11,서울특별시,구로구 +11545,11,서울특별시,금천구 +11350,11,서울특별시,노원구 +11320,11,서울특별시,도봉구 +11230,11,서울특별시,동대문구 +11590,11,서울특별시,동작구 +11440,11,서울특별시,마포구 +11410,11,서울특별시,서대문구 +11650,11,서울특별시,서초구 +11200,11,서울특별시,성동구 +11290,11,서울특별시,성북구 +11710,11,서울특별시,송파구 +11470,11,서울특별시,양천구 +11560,11,서울특별시,영등포구 +11170,11,서울특별시,용산구 +11380,11,서울특별시,은평구 +11110,11,서울특별시,종로구 +11140,11,서울특별시,중구 +11260,11,서울특별시,중랑구 +26000,26,부산광역시,전체 +26440,26,부산광역시,강서구 +26410,26,부산광역시,금정구 +26710,26,부산광역시,기장군 +26290,26,부산광역시,남구 +26170,26,부산광역시,동구 +26260,26,부산광역시,동래구 +26230,26,부산광역시,부산진구 +26320,26,부산광역시,북구 +26530,26,부산광역시,사상구 +26380,26,부산광역시,사하구 +26140,26,부산광역시,서구 +26500,26,부산광역시,수영구 +26470,26,부산광역시,연제구 +26200,26,부산광역시,영도구 +26110,26,부산광역시,중구 +26350,26,부산광역시,해운대구 +27000,27,대구광역시,전체 +27200,27,대구광역시,남구 +27290,27,대구광역시,달서구 +27710,27,대구광역시,달성군 +27140,27,대구광역시,동구 +27230,27,대구광역시,북구 +27170,27,대구광역시,서구 +27260,27,대구광역시,수성구 +27110,27,대구광역시,중구 +28000,28,인천광역시,전체 +28710,28,인천광역시,강화군 +28245,28,인천광역시,계양구 +28200,28,인천광역시,남동구 +28140,28,인천광역시,동구 +28177,28,인천광역시,미추홀구 +28237,28,인천광역시,부평구 +28260,28,인천광역시,서구 +28185,28,인천광역시,연수구 +28720,28,인천광역시,옹진군 +28110,28,인천광역시,중구 +29000,29,광주광역시,전체 +29200,29,광주광역시,광산구 +29155,29,광주광역시,남구 +29110,29,광주광역시,동구 +29170,29,광주광역시,북구 +29140,29,광주광역시,서구 +30000,30,대전광역시,전체 +30230,30,대전광역시,대덕구 +30110,30,대전광역시,동구 +30170,30,대전광역시,서구 +30200,30,대전광역시,유성구 +30140,30,대전광역시,중구 +31000,31,울산광역시,전체 +31140,31,울산광역시,남구 +31170,31,울산광역시,동구 +31200,31,울산광역시,북구 +31710,31,울산광역시,울주군 +31110,31,울산광역시,중구 +36110,36,세종특별자치시,세종특별자치시 +41000,41,경기도,전체 +41820,41,경기도,가평군 +41281,41,경기도,고양시 +41290,41,경기도,과천시 +41210,41,경기도,광명시 +41610,41,경기도,광주시 +41310,41,경기도,구리시 +41410,41,경기도,군포시 +41570,41,경기도,김포시 +41360,41,경기도,남양주시 +41250,41,경기도,동두천시 +41190,41,경기도,부천시 +41135,41,경기도,성남시 +41390,41,경기도,시흥시 +41273,41,경기도,안산시 +41550,41,경기도,안성시 +41173,41,경기도,안양시 +41630,41,경기도,양주시 +41830,41,경기도,양평군 +41670,41,경기도,여주시 +41800,41,경기도,연천군 +41370,41,경기도,오산시 +41463,41,경기도,용인시 +41430,41,경기도,의왕시 +41150,41,경기도,의정부시 +41500,41,경기도,이천시 +41480,41,경기도,파주시 +41220,41,경기도,평택시 +41650,41,경기도,포천시 +41450,41,경기도,하남시 +41590,41,경기도,화성시 +42000,42,강원도,전체 +42150,42,강원도,강릉시 +42820,42,강원도,고성군 +42170,42,강원도,동해시 +42230,42,강원도,삼척시 +42210,42,강원도,속초시 +42800,42,강원도,양구군 +42830,42,강원도,양양군 +42750,42,강원도,영월군 +42130,42,강원도,원주시 +42810,42,강원도,인제군 +42770,42,강원도,정선군 +42780,42,강원도,철원군 +42110,42,강원도,춘천시 +42190,42,강원도,태백시 +42760,42,강원도,평창군 +42720,42,강원도,홍천군 +42790,42,강원도,화천군 +42730,42,강원도,횡성군 +43000,43,충청북도,전체 +43760,43,충청북도,괴산군 +43800,43,충청북도,단양군 +43720,43,충청북도,보은군 +43740,43,충청북도,영동군 +43730,43,충청북도,옥천군 +43770,43,충청북도,음성군 +43150,43,충청북도,제천시 +43745,43,충청북도,증평군 +43750,43,충청북도,진천군 +43111,43,충청북도,청주시 +43130,43,충청북도,충주시 +44000,44,충청남도,전체 +44250,44,충청남도,계룡시 +44150,44,충청남도,공주시 +44710,44,충청남도,금산군 +44230,44,충청남도,논산시 +44270,44,충청남도,당진시 +44180,44,충청남도,보령시 +44760,44,충청남도,부여군 +44210,44,충청남도,서산시 +44770,44,충청남도,서천군 +44200,44,충청남도,아산시 +44810,44,충청남도,예산군 +44131,44,충청남도,천안시 +44790,44,충청남도,청양군 +44825,44,충청남도,태안군 +44800,44,충청남도,홍성군 +45000,45,전라북도,전체 +45790,45,전라북도,고창군 +45130,45,전라북도,군산시 +45210,45,전라북도,김제시 +45190,45,전라북도,남원시 +45730,45,전라북도,무주군 +45800,45,전라북도,부안군 +45770,45,전라북도,순창군 +45710,45,전라북도,완주군 +45140,45,전라북도,익산시 +45750,45,전라북도,임실군 +45740,45,전라북도,장수군 +45113,45,전라북도,전주시 +45180,45,전라북도,정읍시 +45720,45,전라북도,진안군 +46000,46,전라남도,전체 +46810,46,전라남도,강진군 +46770,46,전라남도,고흥군 +46720,46,전라남도,곡성군 +46230,46,전라남도,광양시 +46730,46,전라남도,구례군 +46170,46,전라남도,나주시 +46710,46,전라남도,담양군 +46110,46,전라남도,목포시 +46840,46,전라남도,무안군 +46780,46,전라남도,보성군 +46150,46,전라남도,순천시 +46910,46,전라남도,신안군 +46130,46,전라남도,여수시 +46870,46,전라남도,영광군 +46830,46,전라남도,영암군 +46890,46,전라남도,완도군 +46880,46,전라남도,장성군 +46800,46,전라남도,장흥군 +46900,46,전라남도,진도군 +46860,46,전라남도,함평군 +46820,46,전라남도,해남군 +46790,46,전라남도,화순군 +47000,47,경상북도,전체 +47290,47,경상북도,경산시 +47130,47,경상북도,경주시 +47830,47,경상북도,고령군 +47190,47,경상북도,구미시 +47720,47,경상북도,군위군 +47150,47,경상북도,김천시 +47280,47,경상북도,문경시 +47920,47,경상북도,봉화군 +47250,47,경상북도,상주시 +47840,47,경상북도,성주군 +47170,47,경상북도,안동시 +47770,47,경상북도,영덕군 +47760,47,경상북도,영양군 +47210,47,경상북도,영주시 +47230,47,경상북도,영천시 +47900,47,경상북도,예천군 +47940,47,경상북도,울릉군 +47930,47,경상북도,울진군 +47730,47,경상북도,의성군 +47820,47,경상북도,청도군 +47750,47,경상북도,청송군 +47850,47,경상북도,칠곡군 +47111,47,경상북도,포항시 +48000,48,경상남도,전체 +48310,48,경상남도,거제시 +48880,48,경상남도,거창군 +48820,48,경상남도,고성군 +48250,48,경상남도,김해시 +48840,48,경상남도,남해군 +48270,48,경상남도,밀양시 +48240,48,경상남도,사천시 +48860,48,경상남도,산청군 +48330,48,경상남도,양산시 +48720,48,경상남도,의령군 +48170,48,경상남도,진주시 +48740,48,경상남도,창녕군 +48125,48,경상남도,창원시 +48220,48,경상남도,통영시 +48850,48,경상남도,하동군 +48730,48,경상남도,함안군 +48870,48,경상남도,함양군 +48890,48,경상남도,합천군 +50000,50,제주특별자치도,전체 +50130,50,제주특별자치도,서귀포시 +50110,50,제주특별자치도,제주시 \ No newline at end of file diff --git a/mannabom-server/src/main/resources/data/regions_original.csv b/mannabom-server/src/main/resources/data/regions_original.csv new file mode 100644 index 0000000..348b47c --- /dev/null +++ b/mannabom-server/src/main/resources/data/regions_original.csv @@ -0,0 +1,251 @@ +sigunguCode,sidoCode,sidoName,sigunguName +11680,11,서울특별시,강남구 +11740,11,서울특별시,강동구 +11305,11,서울특별시,강북구 +11500,11,서울특별시,강서구 +11620,11,서울특별시,관악구 +11215,11,서울특별시,광진구 +11530,11,서울특별시,구로구 +11545,11,서울특별시,금천구 +11350,11,서울특별시,노원구 +11320,11,서울특별시,도봉구 +11230,11,서울특별시,동대문구 +11590,11,서울특별시,동작구 +11440,11,서울특별시,마포구 +11410,11,서울특별시,서대문구 +11650,11,서울특별시,서초구 +11200,11,서울특별시,성동구 +11290,11,서울특별시,성북구 +11710,11,서울특별시,송파구 +11470,11,서울특별시,양천구 +11560,11,서울특별시,영등포구 +11170,11,서울특별시,용산구 +11380,11,서울특별시,은평구 +11110,11,서울특별시,종로구 +11140,11,서울특별시,중구 +11260,11,서울특별시,중랑구 +26440,26,부산광역시,강서구 +26410,26,부산광역시,금정구 +26710,26,부산광역시,기장군 +26290,26,부산광역시,남구 +26170,26,부산광역시,동구 +26260,26,부산광역시,동래구 +26230,26,부산광역시,부산진구 +26320,26,부산광역시,북구 +26530,26,부산광역시,사상구 +26380,26,부산광역시,사하구 +26140,26,부산광역시,서구 +26500,26,부산광역시,수영구 +26470,26,부산광역시,연제구 +26200,26,부산광역시,영도구 +26110,26,부산광역시,중구 +26350,26,부산광역시,해운대구 +27200,27,대구광역시,남구 +27290,27,대구광역시,달서구 +27710,27,대구광역시,달성군 +27140,27,대구광역시,동구 +27230,27,대구광역시,북구 +27170,27,대구광역시,서구 +27260,27,대구광역시,수성구 +27110,27,대구광역시,중구 +28710,28,인천광역시,강화군 +28245,28,인천광역시,계양구 +28200,28,인천광역시,남동구 +28140,28,인천광역시,동구 +28177,28,인천광역시,미추홀구 +28237,28,인천광역시,부평구 +28260,28,인천광역시,서구 +28185,28,인천광역시,연수구 +28720,28,인천광역시,옹진군 +28110,28,인천광역시,중구 +29200,29,광주광역시,광산구 +29155,29,광주광역시,남구 +29110,29,광주광역시,동구 +29170,29,광주광역시,북구 +29140,29,광주광역시,서구 +30230,30,대전광역시,대덕구 +30110,30,대전광역시,동구 +30170,30,대전광역시,서구 +30200,30,대전광역시,유성구 +30140,30,대전광역시,중구 +31140,31,울산광역시,남구 +31170,31,울산광역시,동구 +31200,31,울산광역시,북구 +31710,31,울산광역시,울주군 +31110,31,울산광역시,중구 +36110,36,세종특별자치시,세종특별자치시 +41820,41,경기도,가평군 +41281,41,경기도,고양시 덕양구 +41285,41,경기도,고양시 일산동구 +41287,41,경기도,고양시 일산서구 +41290,41,경기도,과천시 +41210,41,경기도,광명시 +41610,41,경기도,광주시 +41310,41,경기도,구리시 +41410,41,경기도,군포시 +41570,41,경기도,김포시 +41360,41,경기도,남양주시 +41250,41,경기도,동두천시 +41190,41,경기도,부천시 +41135,41,경기도,성남시 분당구 +41131,41,경기도,성남시 수정구 +41133,41,경기도,성남시 중원구 +41113,41,경기도,수원시 권선구 +41117,41,경기도,수원시 영통구 +41111,41,경기도,수원시 장안구 +41115,41,경기도,수원시 팔달구 +41390,41,경기도,시흥시 +41273,41,경기도,안산시 단원구 +41271,41,경기도,안산시 상록구 +41550,41,경기도,안성시 +41173,41,경기도,안양시 동안구 +41171,41,경기도,안양시 만안구 +41630,41,경기도,양주시 +41830,41,경기도,양평군 +41670,41,경기도,여주시 +41800,41,경기도,연천군 +41370,41,경기도,오산시 +41463,41,경기도,용인시 기흥구 +41465,41,경기도,용인시 수지구 +41461,41,경기도,용인시 처인구 +41430,41,경기도,의왕시 +41150,41,경기도,의정부시 +41500,41,경기도,이천시 +41480,41,경기도,파주시 +41220,41,경기도,평택시 +41650,41,경기도,포천시 +41450,41,경기도,하남시 +41590,41,경기도,화성시 +42150,42,강원도,강릉시 +42820,42,강원도,고성군 +42170,42,강원도,동해시 +42230,42,강원도,삼척시 +42210,42,강원도,속초시 +42800,42,강원도,양구군 +42830,42,강원도,양양군 +42750,42,강원도,영월군 +42130,42,강원도,원주시 +42810,42,강원도,인제군 +42770,42,강원도,정선군 +42780,42,강원도,철원군 +42110,42,강원도,춘천시 +42190,42,강원도,태백시 +42760,42,강원도,평창군 +42720,42,강원도,홍천군 +42790,42,강원도,화천군 +42730,42,강원도,횡성군 +43760,43,충청북도,괴산군 +43800,43,충청북도,단양군 +43720,43,충청북도,보은군 +43740,43,충청북도,영동군 +43730,43,충청북도,옥천군 +43770,43,충청북도,음성군 +43150,43,충청북도,제천시 +43745,43,충청북도,증평군 +43750,43,충청북도,진천군 +43111,43,충청북도,청주시 상당구 +43112,43,충청북도,청주시 서원구 +43114,43,충청북도,청주시 청원구 +43113,43,충청북도,청주시 흥덕구 +43130,43,충청북도,충주시 +44250,44,충청남도,계룡시 +44150,44,충청남도,공주시 +44710,44,충청남도,금산군 +44230,44,충청남도,논산시 +44270,44,충청남도,당진시 +44180,44,충청남도,보령시 +44760,44,충청남도,부여군 +44210,44,충청남도,서산시 +44770,44,충청남도,서천군 +44200,44,충청남도,아산시 +44810,44,충청남도,예산군 +44131,44,충청남도,천안시 동남구 +44133,44,충청남도,천안시 서북구 +44790,44,충청남도,청양군 +44825,44,충청남도,태안군 +44800,44,충청남도,홍성군 +45790,45,전라북도,고창군 +45130,45,전라북도,군산시 +45210,45,전라북도,김제시 +45190,45,전라북도,남원시 +45730,45,전라북도,무주군 +45800,45,전라북도,부안군 +45770,45,전라북도,순창군 +45710,45,전라북도,완주군 +45140,45,전라북도,익산시 +45750,45,전라북도,임실군 +45740,45,전라북도,장수군 +45113,45,전라북도,전주시 덕진구 +45111,45,전라북도,전주시 완산구 +45180,45,전라북도,정읍시 +45720,45,전라북도,진안군 +46810,46,전라남도,강진군 +46770,46,전라남도,고흥군 +46720,46,전라남도,곡성군 +46230,46,전라남도,광양시 +46730,46,전라남도,구례군 +46170,46,전라남도,나주시 +46710,46,전라남도,담양군 +46110,46,전라남도,목포시 +46840,46,전라남도,무안군 +46780,46,전라남도,보성군 +46150,46,전라남도,순천시 +46910,46,전라남도,신안군 +46130,46,전라남도,여수시 +46870,46,전라남도,영광군 +46830,46,전라남도,영암군 +46890,46,전라남도,완도군 +46880,46,전라남도,장성군 +46800,46,전라남도,장흥군 +46900,46,전라남도,진도군 +46860,46,전라남도,함평군 +46820,46,전라남도,해남군 +46790,46,전라남도,화순군 +47290,47,경상북도,경산시 +47130,47,경상북도,경주시 +47830,47,경상북도,고령군 +47190,47,경상북도,구미시 +47720,47,경상북도,군위군 +47150,47,경상북도,김천시 +47280,47,경상북도,문경시 +47920,47,경상북도,봉화군 +47250,47,경상북도,상주시 +47840,47,경상북도,성주군 +47170,47,경상북도,안동시 +47770,47,경상북도,영덕군 +47760,47,경상북도,영양군 +47210,47,경상북도,영주시 +47230,47,경상북도,영천시 +47900,47,경상북도,예천군 +47940,47,경상북도,울릉군 +47930,47,경상북도,울진군 +47730,47,경상북도,의성군 +47820,47,경상북도,청도군 +47750,47,경상북도,청송군 +47850,47,경상북도,칠곡군 +47111,47,경상북도,포항시 남구 +47113,47,경상북도,포항시 북구 +48310,48,경상남도,거제시 +48880,48,경상남도,거창군 +48820,48,경상남도,고성군 +48250,48,경상남도,김해시 +48840,48,경상남도,남해군 +48270,48,경상남도,밀양시 +48240,48,경상남도,사천시 +48860,48,경상남도,산청군 +48330,48,경상남도,양산시 +48720,48,경상남도,의령군 +48170,48,경상남도,진주시 +48740,48,경상남도,창녕군 +48125,48,경상남도,창원시 마산합포구 +48127,48,경상남도,창원시 마산회원구 +48123,48,경상남도,창원시 성산구 +48121,48,경상남도,창원시 의창구 +48129,48,경상남도,창원시 진해구 +48220,48,경상남도,통영시 +48850,48,경상남도,하동군 +48730,48,경상남도,함안군 +48870,48,경상남도,함양군 +48890,48,경상남도,합천군 +50130,50,제주특별자치도,서귀포시 +50110,50,제주특별자치도,제주시 \ No newline at end of file diff --git a/mannabom-server/src/main/resources/db/migration/V1__baseline.sql b/mannabom-server/src/main/resources/db/migration/V1__baseline.sql new file mode 100644 index 0000000..990052c --- /dev/null +++ b/mannabom-server/src/main/resources/db/migration/V1__baseline.sql @@ -0,0 +1,266 @@ +-- public.device_token definition + +-- Drop table + +-- DROP TABLE public.device_token; + +CREATE TABLE public.device_token ( + id int8 GENERATED BY DEFAULT AS IDENTITY( INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START 1 CACHE 1 NO CYCLE) NOT NULL, + active bool NOT NULL, + last_seen_at timestamptz(6) NULL, + "token" varchar(255) NOT NULL, + user_id int8 NULL, + CONSTRAINT device_token_pkey PRIMARY KEY (id), + CONSTRAINT idx_device_token_token UNIQUE (token) +); +CREATE INDEX idx_device_token_active ON public.device_token USING btree (active); +CREATE INDEX idx_device_token_user ON public.device_token USING btree (user_id); + + +-- public.love_view_recommend_history definition + +-- Drop table + +-- DROP TABLE public.love_view_recommend_history; + +CREATE TABLE public.love_view_recommend_history ( + id int8 GENERATED BY DEFAULT AS IDENTITY( INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START 1 CACHE 1 NO CYCLE) NOT NULL, + recommend_type varchar(255) NOT NULL, + recommended_at timestamp(6) NOT NULL, + requester_user_id int8 NOT NULL, + target_user_id int8 NOT NULL, + CONSTRAINT love_view_recommend_history_pkey PRIMARY KEY (id), + CONSTRAINT love_view_recommend_history_recommend_type_check CHECK (((recommend_type)::text = ANY ((ARRAY['FREE'::character varying, 'EXTRA_VIP'::character varying, 'EXTRA_MEMBERSHIP'::character varying, 'EXTRA_TING'::character varying])::text[]))) +); +CREATE INDEX idx_req_target_time ON public.love_view_recommend_history USING btree (requester_user_id, target_user_id, recommended_at); +CREATE INDEX idx_req_time ON public.love_view_recommend_history USING btree (requester_user_id, recommended_at); + + +-- public.policy_config definition + +-- Drop table + +-- DROP TABLE public.policy_config; + +CREATE TABLE public.policy_config ( + id int8 NOT NULL, + benefit_basic_daily_love_view int4 NULL, + benefit_basic_daily_profile int4 NULL, + benefit_membership_cycle_extra_profiles int4 NULL, + benefit_membership_cycle_free_likes int4 NULL, + benefit_membership_cycle_free_messages int4 NULL, + benefit_vip_daily_extra_profiles int4 NULL, + benefit_vip_daily_free_likes int4 NULL, + benefit_vip_daily_free_messages int4 NULL, + match_candidate_pool_size int4 NULL, + match_cooldown_hours int4 NULL, + match_pick_pool_size int4 NULL, + ting_cost_extra_profile int4 NULL, + ting_cost_extra_profile_bundle5 int4 NULL, + ting_cost_like int4 NULL, + ting_cost_message int4 NULL, + ting_cost_view_extra_photo int4 NULL, + ting_cost_view_high_score_profile int4 NULL, + ting_cost_view_liked_me_profile int4 NULL, + ting_cost_view_score int4 NULL, + ting_vip_threshold int4 NULL, + updated_at timestamp(6) NOT NULL, + "version" int8 NULL, + CONSTRAINT policy_config_pkey PRIMARY KEY (id) +); + + +-- public.profile_recommend_history definition + +-- Drop table + +-- DROP TABLE public.profile_recommend_history; + +CREATE TABLE public.profile_recommend_history ( + id int8 GENERATED BY DEFAULT AS IDENTITY( INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START 1 CACHE 1 NO CYCLE) NOT NULL, + recommend_type varchar(255) NOT NULL, + recommended_at timestamp(6) NOT NULL, + requester_user_id int8 NOT NULL, + target_user_id int8 NOT NULL, + CONSTRAINT profile_recommend_history_pkey PRIMARY KEY (id), + CONSTRAINT profile_recommend_history_recommend_type_check CHECK (((recommend_type)::text = ANY ((ARRAY['FREE'::character varying, 'EXTRA_VIP'::character varying, 'EXTRA_MEMBERSHIP'::character varying, 'EXTRA_TING'::character varying])::text[]))) +); + + +-- public.questions definition + +-- Drop table + +-- DROP TABLE public.questions; + +CREATE TABLE public.questions ( + question_id int8 GENERATED BY DEFAULT AS IDENTITY( INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START 1 CACHE 1 NO CYCLE) NOT NULL, + created_at timestamp(6) NOT NULL, + updated_at timestamp(6) NOT NULL, + question text NOT NULL, + question_type varchar(255) NOT NULL, + CONSTRAINT questions_pkey PRIMARY KEY (question_id), + CONSTRAINT questions_question_type_check CHECK (((question_type)::text = ANY ((ARRAY['REQUIRED_TEXT'::character varying, 'REQUIRED_CHOICE'::character varying, 'OPTIONAL_TEXT'::character varying])::text[]))) +); + + +-- public.ting_wallet definition + +-- Drop table + +-- DROP TABLE public.ting_wallet; + +CREATE TABLE public.ting_wallet ( + user_id int8 NOT NULL, + daily_love_view_granted_date date NULL, + daily_love_view_remaining int4 NOT NULL, + daily_profile_granted_date date NULL, + daily_profile_remaining int4 NOT NULL, + event_ting int4 NOT NULL, + extra_profiles_by_ting_remaining int4 NOT NULL, + m_active_until timestamp(6) NULL, + m_cycle_start_at timestamp(6) NULL, + m_monthly_extra_profiles_remaining int4 NOT NULL, + m_monthly_free_likes_remaining int4 NOT NULL, + m_monthly_extra_messages_remaining int4 NOT NULL, + ting int4 NOT NULL, + "version" int8 NULL, + vip_daily_extra_profiles_remaining int4 NOT NULL, + vip_daily_free_likes_remaining int4 NOT NULL, + vip_daily_free_messages_remaining int4 NOT NULL, + vip_granted_date date NULL, + CONSTRAINT ting_wallet_pkey PRIMARY KEY (user_id) +); + + +-- public.university definition + +-- Drop table + +-- DROP TABLE public.university; + +CREATE TABLE public.university ( + university_id int8 GENERATED BY DEFAULT AS IDENTITY( INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START 1 CACHE 1 NO CYCLE) NOT NULL, + created_at timestamp(6) NOT NULL, + updated_at timestamp(6) NOT NULL, + "domain" varchar(255) NOT NULL, + "name" varchar(255) NOT NULL, + CONSTRAINT ukl0v23mm0y4u53a26k7malrfu1 UNIQUE (domain), + CONSTRAINT university_pkey PRIMARY KEY (university_id) +); + + +-- public.users definition + +-- Drop table + +-- DROP TABLE public.users; + +CREATE TABLE public.users ( + user_id int8 GENERATED BY DEFAULT AS IDENTITY( INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START 1 CACHE 1 NO CYCLE) NOT NULL, + created_at timestamp(6) NOT NULL, + updated_at timestamp(6) NOT NULL, + is_membership bool NOT NULL, + is_verified bool NOT NULL, + kakao_id varchar(255) NOT NULL, + phone_num varchar(255) NULL, + user_name varchar(255) NULL, + CONSTRAINT ukk4ycaj27putgcujmehwbsrmmc UNIQUE (kakao_id), + CONSTRAINT users_pkey PRIMARY KEY (user_id) +); + + +-- public.profile definition + +-- Drop table + +-- DROP TABLE public.profile; + +CREATE TABLE public.profile ( + profile_id int8 GENERATED BY DEFAULT AS IDENTITY( INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START 1 CACHE 1 NO CYCLE) NOT NULL, + created_at timestamp(6) NOT NULL, + updated_at timestamp(6) NOT NULL, + alcohol varchar(255) NULL, + birth_date date NULL, + body_type varchar(255) NULL, + email varchar(255) NULL, + gender varchar(255) NOT NULL, + grade float8 NULL, + height int4 NULL, + mbti varchar(4) NULL, + nick_name varchar(255) NULL, + region_sido varchar(255) NULL, + region_sigungu varchar(255) NULL, + smoking varchar(255) NULL, + university varchar(255) NULL, + user_id int8 NOT NULL, + CONSTRAINT profile_alcohol_check CHECK (((alcohol)::text = ANY ((ARRAY['NON_DRINKER'::character varying, 'OCCASIONAL_DRINKER'::character varying, 'FREQUENT_DRINKER'::character varying])::text[]))), + CONSTRAINT profile_body_type_check CHECK (((body_type)::text = ANY ((ARRAY['SLIM'::character varying, 'AVERAGE'::character varying, 'CHUBBY'::character varying])::text[]))), + CONSTRAINT profile_gender_check CHECK (((gender)::text = ANY ((ARRAY['MALE'::character varying, 'FEMALE'::character varying])::text[]))), + CONSTRAINT profile_pkey PRIMARY KEY (profile_id), + CONSTRAINT profile_smoking_check CHECK (((smoking)::text = ANY ((ARRAY['NON_SMOKER'::character varying, 'VAPE_ONLY'::character varying, 'REGULAR_SMOKER'::character varying])::text[]))), + CONSTRAINT ukc1dkiawnlj6uoe6fnlwd6j83j UNIQUE (user_id), + CONSTRAINT ukcs9f0fwxtv6241hk3b82qyyyh UNIQUE (nick_name), + CONSTRAINT fks14jvsf9tqrcnly0afsv0ngwv FOREIGN KEY (user_id) REFERENCES public.users(user_id) +); + + +-- public.profile_image definition + +-- Drop table + +-- DROP TABLE public.profile_image; + +CREATE TABLE public.profile_image ( + image_id int8 GENERATED BY DEFAULT AS IDENTITY( INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START 1 CACHE 1 NO CYCLE) NOT NULL, + created_at timestamp(6) NOT NULL, + updated_at timestamp(6) NOT NULL, + file_name varchar(255) NOT NULL, + image_index int4 NOT NULL, + is_main bool NOT NULL, + original_name varchar(255) NULL, + url varchar(255) NOT NULL, + profile_id int8 NOT NULL, + CONSTRAINT profile_image_pkey PRIMARY KEY (image_id), + CONSTRAINT fk7499uukxrqn9c6ue4g8uox6ao FOREIGN KEY (profile_id) REFERENCES public.profile(profile_id) +); + + +-- public.question_answer definition + +-- Drop table + +-- DROP TABLE public.question_answer; + +CREATE TABLE public.question_answer ( + answer_id int8 GENERATED BY DEFAULT AS IDENTITY( INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START 1 CACHE 1 NO CYCLE) NOT NULL, + created_at timestamp(6) NOT NULL, + updated_at timestamp(6) NOT NULL, + answer text NULL, + profile_id int8 NOT NULL, + question_id int8 NOT NULL, + CONSTRAINT question_answer_pkey PRIMARY KEY (answer_id), + CONSTRAINT ukceaeg7ab6u9w7tht8f2lxfvb9 UNIQUE (profile_id, question_id), + CONSTRAINT fk6dicp7lwymw77ry50wa2wrlab FOREIGN KEY (profile_id) REFERENCES public.profile(profile_id), + CONSTRAINT fkpgc6cidvg9xr27j4170epkcaq FOREIGN KEY (question_id) REFERENCES public.questions(question_id) +); + + +-- public.refresh_tokens definition + +-- Drop table + +-- DROP TABLE public.refresh_tokens; + +CREATE TABLE public.refresh_tokens ( + token_id int8 GENERATED BY DEFAULT AS IDENTITY( INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START 1 CACHE 1 NO CYCLE) NOT NULL, + created_at timestamp(6) NOT NULL, + updated_at timestamp(6) NOT NULL, + expires_at timestamp(6) NOT NULL, + refresh_token varchar(512) NOT NULL, + user_id int8 NOT NULL, + CONSTRAINT refresh_tokens_pkey PRIMARY KEY (token_id), + CONSTRAINT uk1yihy5j142kjit22kgccjixro UNIQUE (refresh_token), + CONSTRAINT uk7tdcd6ab5wsgoudnvj7xf1b7l UNIQUE (user_id), + CONSTRAINT fk1lih5y2npsf8u5o3vhdb9y0os FOREIGN KEY (user_id) REFERENCES public.users(user_id) +); \ No newline at end of file diff --git a/mannabom-server/src/main/resources/db/migration/V2__add_meeting_and_chat.sql b/mannabom-server/src/main/resources/db/migration/V2__add_meeting_and_chat.sql new file mode 100644 index 0000000..ad935b7 --- /dev/null +++ b/mannabom-server/src/main/resources/db/migration/V2__add_meeting_and_chat.sql @@ -0,0 +1,125 @@ +-- V2__add_meeting_and_chat.sql +-- 1. Notification (알림) - 로컬에만 있는 기능 추가 +CREATE TABLE IF NOT EXISTS public.notification ( + id int8 GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + created_at timestamptz(6) NOT NULL, + is_read bool NOT NULL, + "type" int2 NULL CHECK (((type >= 0) AND (type <= 5))), + user_id int8 NULL, + "data" text NULL, + message varchar(255) NULL, + title varchar(255) NULL + ); +-- 1. [필수] Meeting 테이블이 참조하는 Regions 테이블 생성 (운영에 없다면 생성) +CREATE TABLE IF NOT EXISTS public.regions ( + sigungu_code varchar(5) NOT NULL, + sido_code varchar(2) NOT NULL, + sido_name varchar(255) NULL, + sigungu_name varchar(255) NULL, + CONSTRAINT regions_pkey PRIMARY KEY (sigungu_code) + ); + +-- 2. Meeting(미팅) 도메인 테이블 생성 +CREATE TABLE public.meeting ( + id int8 GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + created_at timestamptz(6) NOT NULL, + updated_at timestamptz(6) NOT NULL, + code varchar(255) NOT NULL, + room_name varchar(255) NULL, + meeting_status int2 NULL, + + -- 인원 및 나이 제한 + max_members int4 NULL, + current_members int4 NULL, + min_age int4 NULL, + max_age int4 NULL, + avg_age float8 NOT NULL, + gender int2 NULL, + + -- 매칭 관련 + occupancy_score int4 NULL, + remaining_reject_count int4 NULL, + matching_start_at timestamptz(6) NULL, + + -- 외래키 컬럼 + created_by int8 NOT NULL, + sigungu_code varchar(5) NULL, + + CONSTRAINT meeting_code_key UNIQUE (code), + CONSTRAINT fk_meeting_creator FOREIGN KEY (created_by) REFERENCES public.users(user_id), + CONSTRAINT fk_meeting_region FOREIGN KEY (sigungu_code) REFERENCES public.regions(sigungu_code) +); + +CREATE TABLE public.meeting_members ( + id int8 GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + joined_at timestamptz(6) NOT NULL, + meeting_role int2 NULL, + status int2 NULL, + meeting_id int8 NULL, + user_id int8 NULL, + CONSTRAINT fk_meeting_members_meeting FOREIGN KEY (meeting_id) REFERENCES public.meeting(id), + CONSTRAINT fk_meeting_members_user FOREIGN KEY (user_id) REFERENCES public.users(user_id) +); + +CREATE TABLE public.meeting_matches ( + id int8 GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + created_at timestamptz(6) NOT NULL, + decision_dead_line timestamptz(6) NULL, + matching_status int2 NULL, + meeting1decision int2 NULL, + meeting2decision int2 NULL, + meeting1_id int8 NOT NULL, + meeting2_id int8 NOT NULL, + CONSTRAINT fk_match_meeting1 FOREIGN KEY (meeting1_id) REFERENCES public.meeting(id), + CONSTRAINT fk_match_meeting2 FOREIGN KEY (meeting2_id) REFERENCES public.meeting(id) +); + +-- 3. Chat(채팅) 도메인 테이블 생성 +CREATE TABLE public.chat_rooms ( + id int8 GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + created_at timestamptz(6) NOT NULL, + updated_at timestamptz(6) NOT NULL, + chat_status int2 NULL CHECK (chat_status >= 0 AND chat_status <= 1), + "type" int2 NULL, + match_id int8 NULL UNIQUE, + meeting_id int8 NULL UNIQUE, + CONSTRAINT fk_chat_match FOREIGN KEY (match_id) REFERENCES public.meeting_matches(id), + CONSTRAINT fk_chat_meeting FOREIGN KEY (meeting_id) REFERENCES public.meeting(id) +); + +CREATE TABLE public.chat_members ( + id int8 GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + joined_at timestamptz(6) NULL, + last_read_message_id int4 NULL, + room_id int8 NOT NULL, + user_id int8 NOT NULL, + CONSTRAINT fk_chat_members_room FOREIGN KEY (room_id) REFERENCES public.chat_rooms(id), + CONSTRAINT fk_chat_members_user FOREIGN KEY (user_id) REFERENCES public.users(user_id) +); + +CREATE TABLE public.chat_messages ( + id int8 GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + created_at timestamptz(6) NOT NULL, + updated_at timestamptz(6) NOT NULL, + "content" varchar(255) NULL, + "type" int2 NULL, + room_id int8 NULL, + sender_user_id int8 NULL, + CONSTRAINT fk_chat_msg_room FOREIGN KEY (room_id) REFERENCES public.chat_rooms(id), + CONSTRAINT fk_chat_msg_user FOREIGN KEY (sender_user_id) REFERENCES public.users(user_id) +); + +-- 4. Profile 테이블 구조 변경 (로컬 코드와 맞추기 위함) +-- 로컬에서는 university_id, region_id(sigungu_code) FK를 사용하는 것으로 보임 +-- 운영에 있는 기존 데이터(varchar)를 보존하면서 컬럼을 추가함 + +ALTER TABLE public.profile ADD COLUMN IF NOT EXISTS university_id int8; +ALTER TABLE public.profile ADD COLUMN IF NOT EXISTS region_id varchar(5); + +-- FK 연결 (데이터가 NULL이어도 제약조건 생성 가능) +ALTER TABLE public.profile + ADD CONSTRAINT fk_profile_university FOREIGN KEY (university_id) REFERENCES public.university(university_id); + +ALTER TABLE public.profile + ADD CONSTRAINT fk_profile_region FOREIGN KEY (region_id) REFERENCES public.regions(sigungu_code); + diff --git a/mannabom-server/src/main/resources/db/migration/V3__add_request_table_and_extra_photo_table_and_rating_table.sql b/mannabom-server/src/main/resources/db/migration/V3__add_request_table_and_extra_photo_table_and_rating_table.sql new file mode 100644 index 0000000..8656795 --- /dev/null +++ b/mannabom-server/src/main/resources/db/migration/V3__add_request_table_and_extra_photo_table_and_rating_table.sql @@ -0,0 +1,77 @@ +-- like_request 테이블 생성 +CREATE TABLE like_request ( + id BIGINT GENERATED BY DEFAULT AS IDENTITY, + from_user_id BIGINT NOT NULL, + to_user_id BIGINT NOT NULL, + status VARCHAR(255) NOT NULL, + created_at TIMESTAMP NOT NULL, + responded_at TIMESTAMP, + PRIMARY KEY (id) +); + +-- 유니크 제약조건 추가 (같은 사람에게 중복 요청 방지) +ALTER TABLE like_request + ADD CONSTRAINT uk_like_from_to + UNIQUE (from_user_id, to_user_id); + +-- 인덱스 생성 (조회 성능 최적화) +CREATE INDEX idx_like_to_status ON like_request (to_user_id, status); +CREATE INDEX idx_like_from_status ON like_request (from_user_id, status); + + + +-- message_request 테이블 생성 +CREATE TABLE message_request ( + id BIGINT GENERATED BY DEFAULT AS IDENTITY, + from_user_id BIGINT NOT NULL, + to_user_id BIGINT NOT NULL, + message VARCHAR(200), + status VARCHAR(255) NOT NULL, + created_at TIMESTAMP NOT NULL, + responded_at TIMESTAMP, + PRIMARY KEY (id) +); + +-- 유니크 제약조건 추가 (같은 사람에게 중복 메시지 요청 방지) +ALTER TABLE message_request + ADD CONSTRAINT uk_msg_from_to + UNIQUE (from_user_id, to_user_id); + +-- 인덱스 생성 (조회 성능 최적화) +CREATE INDEX idx_msgreq_to_status ON message_request (to_user_id, status); +CREATE INDEX idx_msgreq_from_status ON message_request (from_user_id, status); + + + +-- profile_extra_photo_unlock 테이블 생성 +CREATE TABLE profile_extra_photo_unlock ( + unlock_id BIGINT GENERATED BY DEFAULT AS IDENTITY, + requester_user_id BIGINT NOT NULL, + target_user_id BIGINT NOT NULL, + photo_id BIGINT NOT NULL, + created_at TIMESTAMP, + updated_at TIMESTAMP, + PRIMARY KEY (unlock_id) +); + +-- 유니크 제약조건 추가 (중복 해제 방지) +ALTER TABLE profile_extra_photo_unlock +ADD CONSTRAINT uk_unlock_requester_target_photo +UNIQUE (requester_user_id, target_user_id, photo_id); + + +-- profile_rating 테이블 생성 +CREATE TABLE profile_rating ( + profile_rating_id BIGINT GENERATED BY DEFAULT AS IDENTITY, + from_user_id BIGINT NOT NULL, + target_user_id BIGINT NOT NULL, + score INTEGER NOT NULL, + created_at TIMESTAMP, + updated_at TIMESTAMP, + PRIMARY KEY (profile_rating_id) +); + +-- 유니크 제약조건 추가 (한 사람이 동일 대상을 중복 평가 불가) +ALTER TABLE profile_rating +ADD CONSTRAINT uk_profile_rating_from_to +UNIQUE (from_user_id, target_user_id); diff --git a/mannabom-server/src/main/resources/db/migration/V4__add_reject_reason_to_like_request.sql b/mannabom-server/src/main/resources/db/migration/V4__add_reject_reason_to_like_request.sql new file mode 100644 index 0000000..cc2c3f7 --- /dev/null +++ b/mannabom-server/src/main/resources/db/migration/V4__add_reject_reason_to_like_request.sql @@ -0,0 +1,2 @@ +ALTER TABLE like_request +ADD COLUMN IF NOT EXISTS reject_reason VARCHAR(255); diff --git a/mannabom-server/src/main/resources/db/migration/V5__add_reject_reason_to_message_request.sql b/mannabom-server/src/main/resources/db/migration/V5__add_reject_reason_to_message_request.sql new file mode 100644 index 0000000..d00b6e9 --- /dev/null +++ b/mannabom-server/src/main/resources/db/migration/V5__add_reject_reason_to_message_request.sql @@ -0,0 +1,2 @@ +ALTER TABLE message_request +ADD COLUMN IF NOT EXISTS reject_reason VARCHAR(255); diff --git a/mannabom-server/src/main/resources/db/migration/V6__add_source_to_like_request.sql b/mannabom-server/src/main/resources/db/migration/V6__add_source_to_like_request.sql new file mode 100644 index 0000000..d826ae3 --- /dev/null +++ b/mannabom-server/src/main/resources/db/migration/V6__add_source_to_like_request.sql @@ -0,0 +1,9 @@ +ALTER TABLE like_request +ADD COLUMN source VARCHAR(30); + +UPDATE like_request +SET source = 'PROFILE_MATCH' +WHERE source IS NULL; + +ALTER TABLE like_request +ALTER COLUMN source SET NOT NULL; \ No newline at end of file diff --git a/mannabom-server/src/main/resources/db/migration/V7__add_source_to_message_request.sql b/mannabom-server/src/main/resources/db/migration/V7__add_source_to_message_request.sql new file mode 100644 index 0000000..17d093e --- /dev/null +++ b/mannabom-server/src/main/resources/db/migration/V7__add_source_to_message_request.sql @@ -0,0 +1,9 @@ +ALTER TABLE message_request +ADD COLUMN source VARCHAR(30); + +UPDATE message_request +SET source = 'PROFILE_MATCH' +WHERE source IS NULL; + +ALTER TABLE message_request +ALTER COLUMN source SET NOT NULL; \ No newline at end of file diff --git a/mannabom-server/src/main/resources/db/migration/V8__migrate_profile_data_and_add_column_chat_room_table.sql b/mannabom-server/src/main/resources/db/migration/V8__migrate_profile_data_and_add_column_chat_room_table.sql new file mode 100644 index 0000000..5d1f387 --- /dev/null +++ b/mannabom-server/src/main/resources/db/migration/V8__migrate_profile_data_and_add_column_chat_room_table.sql @@ -0,0 +1,44 @@ +-- 1. [데이터 이관] 기존 문자열(university) -> 새로운 ID(university_id) 매핑 +-- 이름이 정확히 일치하는 경우에만 업데이트 +UPDATE public.profile p +SET university_id = u.university_id + FROM public.university u +WHERE p.university = u.name + AND p.university_id IS NULL; -- 이미 값이 있는 건 건드리지 않음 + +-- 2. [데이터 이관] 기존 문자열(region) -> 새로운 ID(region_id) 매핑 +-- 시도/시군구 이름이 모두 일치하는 경우 업데이트 +UPDATE public.profile p +SET region_id = r.sigungu_code + FROM public.regions r +WHERE p.region_sido = r.sido_name + AND p.region_sigungu = r.sigungu_name + AND p.region_id IS NULL; + +-- 3. [컬럼 삭제] 데이터 이관이 끝났으므로 기존 컬럼 삭제 +ALTER TABLE public.profile DROP COLUMN IF EXISTS university; +ALTER TABLE public.profile DROP COLUMN IF EXISTS region_sido; +ALTER TABLE public.profile DROP COLUMN IF EXISTS region_sigungu; + + +-- V5__add_recommend_history_to_chat_rooms.sql + + + +-- 1. 새로운 연관관계 컬럼 추가 +ALTER TABLE public.chat_rooms ADD COLUMN IF NOT EXISTS profile_recommend_history_id int8; +ALTER TABLE public.chat_rooms ADD COLUMN IF NOT EXISTS love_view_recommend_history_id int8; + +-- 2. 1:1 관계를 위한 UNIQUE 제약 조건 추가 +ALTER TABLE public.chat_rooms ADD CONSTRAINT uk_chat_profile_history UNIQUE (profile_recommend_history_id); +ALTER TABLE public.chat_rooms ADD CONSTRAINT uk_chat_loveview_history UNIQUE (love_view_recommend_history_id); + +-- 3. 외래키(FK) 설정 +-- 참조하는 테이블 이름(profile_recommend_history 등)이 실제 생성된 이름과 맞는지 확인하세요! +ALTER TABLE public.chat_rooms + ADD CONSTRAINT fk_chat_profile_history FOREIGN KEY (profile_recommend_history_id) + REFERENCES public.profile_recommend_history(id); + +ALTER TABLE public.chat_rooms + ADD CONSTRAINT fk_chat_loveview_history FOREIGN KEY (love_view_recommend_history_id) + REFERENCES public.love_view_recommend_history(id); \ No newline at end of file diff --git a/mannabom-server/src/test/java/com/mannabom/mannabom_server/MannabomServerApplicationTests.java b/mannabom-server/src/test/java/com/mannabom/mannabom_server/MannabomServerApplicationTests.java new file mode 100644 index 0000000..81dc095 --- /dev/null +++ b/mannabom-server/src/test/java/com/mannabom/mannabom_server/MannabomServerApplicationTests.java @@ -0,0 +1,13 @@ +package com.mannabom.mannabom_server; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class MannabomServerApplicationTests { + + @Test + void contextLoads() { + } + +}