From 5a8a0fb6e1896e432c2abddf2d70012b9cfe83ab Mon Sep 17 00:00:00 2001 From: sunghyuki Date: Tue, 19 Nov 2024 14:33:02 +0900 Subject: [PATCH 01/35] =?UTF-8?q?feat:=20=EB=AA=A8=EB=8B=88=ED=84=B0?= =?UTF-8?q?=EB=A7=81=EA=B3=BC=20=EC=84=9C=EB=B2=84=20=ED=99=98=EA=B2=BD=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose.yml | 23 +---------------------- 1 file changed, 1 insertion(+), 22 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index cbdaae1..c76bddb 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -9,25 +9,4 @@ services: - VERSION=${TAG} - BUILDTIME=${BUILDTIME} ports: - - "8080:8080" - depends_on: - - prometheus - - prometheus: - image: prom/prometheus:latest - container_name: prometheus - volumes: - - ./prometheus.yml:/etc/prometheus/prometheus.yml - ports: - - "9090:9090" - - grafana: - image: grafana/grafana:latest - container_name: grafana - environment: - - GF_SECURITY_ADMIN_USER=admin - - GF_SECURITY_ADMIN_PASSWORD=Climingo1234! # 기본 비밀번호 설정 - ports: - - "3000:3000" - depends_on: - - prometheus \ No newline at end of file + - "8080:8080" \ No newline at end of file From 93b8fc4b7043162c789aa424d00cc4d19b726909 Mon Sep 17 00:00:00 2001 From: sunghyuki Date: Tue, 19 Nov 2024 21:09:58 +0900 Subject: [PATCH 02/35] =?UTF-8?q?feat:=20Promtail=EC=9D=B4=20=EB=8F=84?= =?UTF-8?q?=EC=BB=A4=20=EC=BB=A8=ED=85=8C=EC=9D=B4=EB=84=88=EC=9D=98=20?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EB=A5=BC=20=EC=9D=BD=EC=9D=84=20=EC=88=98=20?= =?UTF-8?q?=EC=9E=88=EB=8F=84=EB=A1=9D=20=EB=A1=9C=EA=B7=B8=20=EA=B2=BD?= =?UTF-8?q?=EB=A1=9C=EC=9D=98=20=ED=8F=B4=EB=8D=94=EB=A5=BC=20read-only?= =?UTF-8?q?=EB=A1=9C=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index c76bddb..3c0dc7f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -9,4 +9,6 @@ services: - VERSION=${TAG} - BUILDTIME=${BUILDTIME} ports: - - "8080:8080" \ No newline at end of file + - "8080:8080" + volumes: + - /var/lib/docker/containers:/var/lib/docker/containers:ro \ No newline at end of file From 297b25acfb8c8ea1450776ad0d4be049789d2450 Mon Sep 17 00:00:00 2001 From: sunghyuki Date: Wed, 20 Nov 2024 08:44:48 +0900 Subject: [PATCH 03/35] =?UTF-8?q?feat:=20Promtail=EC=9D=84=20spring=20boot?= =?UTF-8?q?=20=EC=84=9C=EB=B2=84=EC=99=80=20=EB=8F=99=EC=9D=BC=20=EC=9D=B8?= =?UTF-8?q?=EC=8A=A4=ED=84=B4=EC=8A=A4=EC=97=90=20=EB=B0=B0=ED=8F=AC?= =?UTF-8?q?=EB=90=98=EB=8F=84=EB=A1=9D=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose.yml | 19 +++++++++++++++++-- promtail-config.yml | 18 ++++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 promtail-config.yml diff --git a/docker-compose.yml b/docker-compose.yml index 3c0dc7f..17f7b30 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,7 +2,7 @@ version: '3.8' services: climingo-api: - image: climingo/climingo:${TAG} # Spring Boot 애플리케이션 Docker 이미지 + image: climingo/climingo:${TAG} # Spring Boot 애플리케이션 Docker 이미지 container_name: climingo-api environment: - JASYPT_PASSWORD=${JASYPT_PASSWORD} @@ -10,5 +10,20 @@ services: - BUILDTIME=${BUILDTIME} ports: - "8080:8080" + logging: # 로그 드라이버 설정 (json-file 기본값 사용) + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + + promtail: + image: grafana/promtail:2.9.0 # Promtail Docker 이미지 + container_name: promtail + depends_on: + - climingo-api # Spring Boot 애플리케이션이 먼저 실행되도록 설정 volumes: - - /var/lib/docker/containers:/var/lib/docker/containers:ro \ No newline at end of file + - /var/log:/var/log:ro # 필요하면 호스트 로그 디렉토리 마운트 + - /var/lib/docker/containers:/var/lib/docker/containers:ro # Docker 컨테이너 로그 경로 + - ./promtail-config.yml:/etc/promtail/promtail.yml:ro # Promtail 설정 파일 + command: + - -config.file=/etc/promtail/promtail.yml # 설정 파일 위치 \ No newline at end of file diff --git a/promtail-config.yml b/promtail-config.yml new file mode 100644 index 0000000..376937a --- /dev/null +++ b/promtail-config.yml @@ -0,0 +1,18 @@ +server: + http_listen_port: 9080 # Promtail HTTP 서버 포트 + +clients: + - url: http://ec2-43-200-200-208.ap-northeast-2.compute.amazonaws.com:3100/loki/api/v1/push # Loki 서버 URL + +positions: + filename: /tmp/positions.yaml # 로그 수집 위치 저장 파일 + +scrape_configs: + - job_name: "climingo-api" # 작업 이름 + static_configs: + - targets: + - ec2-3-35-66-48.ap-northeast-2.compute.amazonaws.com # Promtail 컨테이너가 실행되는 서버 + labels: + job: "climingo-api" # 로그 구분 태그 + service: "spring-boot" # 서비스 이름 + __path__: /var/lib/docker/containers/*/*-json.log # Docker 로그 파일 경로 From 9bd15e44c5174fc731883ccad8d9c915ee98ec96 Mon Sep 17 00:00:00 2001 From: sunghyuki Date: Wed, 20 Nov 2024 09:02:29 +0900 Subject: [PATCH 04/35] fix: promtail port setting --- docker-compose.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docker-compose.yml b/docker-compose.yml index 17f7b30..ce3f291 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -19,6 +19,8 @@ services: promtail: image: grafana/promtail:2.9.0 # Promtail Docker 이미지 container_name: promtail + ports: + - "9080:9080" depends_on: - climingo-api # Spring Boot 애플리케이션이 먼저 실행되도록 설정 volumes: From 9bcad844b7381684152a5ee3f15e385fbcb4909b Mon Sep 17 00:00:00 2001 From: sunghyuki Date: Wed, 20 Nov 2024 17:54:04 +0900 Subject: [PATCH 05/35] =?UTF-8?q?feat:=20=EC=8A=A4=ED=94=84=EB=A7=81=20?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=20=EA=B4=80=EB=A6=AC=EB=A5=BC=20=EC=9C=84?= =?UTF-8?q?=ED=95=9C=20=EC=8B=9C=EC=8A=A4=ED=85=9C=20=EA=B5=AC=EC=B6=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose.yml | 7 ++++--- promtail-config.yml | 3 +-- src/main/resources/application-prod.yml | 6 +++--- src/main/resources/logback-spring.xml | 26 +++++++++++++++++++++++++ 4 files changed, 34 insertions(+), 8 deletions(-) create mode 100644 src/main/resources/logback-spring.xml diff --git a/docker-compose.yml b/docker-compose.yml index ce3f291..6b44f89 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,6 +8,7 @@ services: - JASYPT_PASSWORD=${JASYPT_PASSWORD} - VERSION=${TAG} - BUILDTIME=${BUILDTIME} + - LOGGING_FILE_PATH=/logs # 로그 파일 경로를 환경 변수로 설정 ports: - "8080:8080" logging: # 로그 드라이버 설정 (json-file 기본값 사용) @@ -24,8 +25,8 @@ services: depends_on: - climingo-api # Spring Boot 애플리케이션이 먼저 실행되도록 설정 volumes: - - /var/log:/var/log:ro # 필요하면 호스트 로그 디렉토리 마운트 - - /var/lib/docker/containers:/var/lib/docker/containers:ro # Docker 컨테이너 로그 경로 + - /var/logs/climingo-api:/logs:ro # 로그 디렉토리를 읽기 전용으로 마운트 - ./promtail-config.yml:/etc/promtail/promtail.yml:ro # Promtail 설정 파일 command: - - -config.file=/etc/promtail/promtail.yml # 설정 파일 위치 \ No newline at end of file + - -config.file=/etc/promtail/promtail.yml # 설정 파일 위치 + restart: always \ No newline at end of file diff --git a/promtail-config.yml b/promtail-config.yml index 376937a..ed38745 100644 --- a/promtail-config.yml +++ b/promtail-config.yml @@ -14,5 +14,4 @@ scrape_configs: - ec2-3-35-66-48.ap-northeast-2.compute.amazonaws.com # Promtail 컨테이너가 실행되는 서버 labels: job: "climingo-api" # 로그 구분 태그 - service: "spring-boot" # 서비스 이름 - __path__: /var/lib/docker/containers/*/*-json.log # Docker 로그 파일 경로 + __path__: /logs/*.log # 로그 파일 경로 diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index a24f2f5..55cdbc3 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -16,9 +16,9 @@ spring: logging: level: - org.hibernate.SQL: debug - org.hibernate.type.descriptor.sql: trace - com.your.package: debug + org.hibernate.SQL: info + org.hibernate.type.descriptor.sql: info + com.your.package: info oauth2: kakao: diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml new file mode 100644 index 0000000..ffe1ab9 --- /dev/null +++ b/src/main/resources/logback-spring.xml @@ -0,0 +1,26 @@ + + + + + %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n + + + + + + ${LOGGING_FILE_PATH}/application.log + + ${LOGGING_FILE_PATH}/application.%d{yyyy-MM-dd}.log + 7 + + + %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + From 8254e54a1af40c9af9f29816efeca2ad2732fd91 Mon Sep 17 00:00:00 2001 From: sunghyuki Date: Wed, 20 Nov 2024 20:02:44 +0900 Subject: [PATCH 06/35] =?UTF-8?q?feat:=20=ED=98=B8=EC=8A=A4=ED=8A=B8=20vol?= =?UTF-8?q?ume=EC=9D=84=20spring=20server,=20promtail=20=EC=BB=A8=ED=85=8C?= =?UTF-8?q?=EC=9D=B4=EB=84=88=EA=B0=80=20=EA=B3=B5=EC=9C=A0=ED=95=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 6b44f89..e1b7a4d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -9,6 +9,8 @@ services: - VERSION=${TAG} - BUILDTIME=${BUILDTIME} - LOGGING_FILE_PATH=/logs # 로그 파일 경로를 환경 변수로 설정 + volumes: + - ./logs:/logs # 호스트와 컨테이너 간 로그 파일 공유 ports: - "8080:8080" logging: # 로그 드라이버 설정 (json-file 기본값 사용) @@ -25,7 +27,7 @@ services: depends_on: - climingo-api # Spring Boot 애플리케이션이 먼저 실행되도록 설정 volumes: - - /var/logs/climingo-api:/logs:ro # 로그 디렉토리를 읽기 전용으로 마운트 + - ./logs:/logs # 호스트 로그 디렉토리를 Promtail에 공유 - ./promtail-config.yml:/etc/promtail/promtail.yml:ro # Promtail 설정 파일 command: - -config.file=/etc/promtail/promtail.yml # 설정 파일 위치 From 5700da05475dd6ce3777623ca4cad05c38aa9cc5 Mon Sep 17 00:00:00 2001 From: sunghyuki Date: Wed, 20 Nov 2024 20:14:26 +0900 Subject: [PATCH 07/35] =?UTF-8?q?feat:=20Spring=20Boot=20Actuator=20stdout?= =?UTF-8?q?=20=EB=A1=9C=EA=B7=B8=20=EA=B8=B0=EB=A1=9D=ED=95=98=EC=A7=80=20?= =?UTF-8?q?=EC=95=8A=EB=8F=84=EB=A1=9D=20=ED=95=98=EB=8A=94=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/logback-spring.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml index ffe1ab9..d87f205 100644 --- a/src/main/resources/logback-spring.xml +++ b/src/main/resources/logback-spring.xml @@ -18,6 +18,10 @@ + + + + From edd5560ce4cc518cc26f35ea802a09cae13da76f Mon Sep 17 00:00:00 2001 From: JongKeun Kim Date: Fri, 17 Jan 2025 15:11:14 +0900 Subject: [PATCH 08/35] =?UTF-8?q?[#119]=20=EC=8A=A4=ED=82=A4=EB=A7=88=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20(#120)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add: auth_token, member 테이블에 created_date, modified_date 컬럼 추가 * update: 스키마 수정 - gym.address 길이 200자로 변경 - member.profile_url 길이 400자로 변경 * update: gym - level cascade 설정 추가 --- .../climingo/climingoApi/auth/domain/AuthToken.java | 3 ++- .../{record => global}/domain/BaseTimeEntity.java | 2 +- .../com/climingo/climingoApi/gym/domain/Address.java | 2 +- .../com/climingo/climingoApi/gym/domain/Gym.java | 12 ++++-------- .../climingo/climingoApi/member/domain/Member.java | 5 +++-- .../climingo/climingoApi/record/domain/Record.java | 1 + 6 files changed, 12 insertions(+), 13 deletions(-) rename src/main/java/com/climingo/climingoApi/{record => global}/domain/BaseTimeEntity.java (92%) diff --git a/src/main/java/com/climingo/climingoApi/auth/domain/AuthToken.java b/src/main/java/com/climingo/climingoApi/auth/domain/AuthToken.java index d3159f2..88ddd81 100644 --- a/src/main/java/com/climingo/climingoApi/auth/domain/AuthToken.java +++ b/src/main/java/com/climingo/climingoApi/auth/domain/AuthToken.java @@ -1,6 +1,7 @@ package com.climingo.climingoApi.auth.domain; import com.climingo.climingoApi.auth.api.response.TokenResponse; +import com.climingo.climingoApi.global.domain.BaseTimeEntity; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.Id; @@ -13,7 +14,7 @@ @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) @AllArgsConstructor(staticName = "of") -public class AuthToken { +public class AuthToken extends BaseTimeEntity { @Id private Long memberId; diff --git a/src/main/java/com/climingo/climingoApi/record/domain/BaseTimeEntity.java b/src/main/java/com/climingo/climingoApi/global/domain/BaseTimeEntity.java similarity index 92% rename from src/main/java/com/climingo/climingoApi/record/domain/BaseTimeEntity.java rename to src/main/java/com/climingo/climingoApi/global/domain/BaseTimeEntity.java index 0e8ed50..61204c8 100644 --- a/src/main/java/com/climingo/climingoApi/record/domain/BaseTimeEntity.java +++ b/src/main/java/com/climingo/climingoApi/global/domain/BaseTimeEntity.java @@ -1,4 +1,4 @@ -package com.climingo.climingoApi.record.domain; +package com.climingo.climingoApi.global.domain; import jakarta.persistence.EntityListeners; import jakarta.persistence.MappedSuperclass; diff --git a/src/main/java/com/climingo/climingoApi/gym/domain/Address.java b/src/main/java/com/climingo/climingoApi/gym/domain/Address.java index ac8da85..7e09395 100644 --- a/src/main/java/com/climingo/climingoApi/gym/domain/Address.java +++ b/src/main/java/com/climingo/climingoApi/gym/domain/Address.java @@ -6,7 +6,7 @@ @Embeddable public class Address { - @Column(nullable = false, length = 50) + @Column(nullable = false, length = 200) private String address; @Column(nullable = true, length = 50) diff --git a/src/main/java/com/climingo/climingoApi/gym/domain/Gym.java b/src/main/java/com/climingo/climingoApi/gym/domain/Gym.java index ba9c526..ba87c87 100644 --- a/src/main/java/com/climingo/climingoApi/gym/domain/Gym.java +++ b/src/main/java/com/climingo/climingoApi/gym/domain/Gym.java @@ -1,13 +1,8 @@ package com.climingo.climingoApi.gym.domain; import com.climingo.climingoApi.level.domain.Level; -import jakarta.persistence.Column; -import jakarta.persistence.Embedded; -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.OneToMany; +import jakarta.persistence.*; + import java.util.List; import lombok.Getter; @@ -27,6 +22,7 @@ public class Gym { @Embedded private GeoInfo geoInfo; - @OneToMany(mappedBy = "gym") + + @OneToMany(mappedBy = "gym", cascade = CascadeType.REMOVE) private List levels; } diff --git a/src/main/java/com/climingo/climingoApi/member/domain/Member.java b/src/main/java/com/climingo/climingoApi/member/domain/Member.java index cb02a43..7d014c4 100644 --- a/src/main/java/com/climingo/climingoApi/member/domain/Member.java +++ b/src/main/java/com/climingo/climingoApi/member/domain/Member.java @@ -1,5 +1,6 @@ package com.climingo.climingoApi.member.domain; +import com.climingo.climingoApi.global.domain.BaseTimeEntity; import com.climingo.climingoApi.gym.domain.Gym; import com.climingo.climingoApi.record.domain.Record; import jakarta.persistence.CascadeType; @@ -28,7 +29,7 @@ @Getter @Table(uniqueConstraints = {@UniqueConstraint(columnNames = {"auth_id", "provider_type"})}) @NoArgsConstructor(access = AccessLevel.PROTECTED) -public class Member { +public class Member extends BaseTimeEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -44,7 +45,7 @@ public class Member { @Size(min = 2, max = 16) private String nickname; - @Column(nullable = true, length = 255) + @Column(nullable = true, length = 400) private String profileUrl; @Column(nullable = true, length = 50) diff --git a/src/main/java/com/climingo/climingoApi/record/domain/Record.java b/src/main/java/com/climingo/climingoApi/record/domain/Record.java index f4db483..d35fcd9 100644 --- a/src/main/java/com/climingo/climingoApi/record/domain/Record.java +++ b/src/main/java/com/climingo/climingoApi/record/domain/Record.java @@ -1,5 +1,6 @@ package com.climingo.climingoApi.record.domain; +import com.climingo.climingoApi.global.domain.BaseTimeEntity; import com.climingo.climingoApi.gym.domain.Gym; import com.climingo.climingoApi.level.domain.Level; import com.climingo.climingoApi.member.domain.Member; From 2a2cb4ea8b3bca7a43ddf1fdb273233384b1ceb7 Mon Sep 17 00:00:00 2001 From: sunghyuki Date: Sun, 19 Jan 2025 17:41:34 +0900 Subject: [PATCH 09/35] =?UTF-8?q?feat:=20Loki=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=20=EC=A0=95=EB=B3=B4=EA=B0=80=20=EC=9C=A0?= =?UTF-8?q?=EC=9D=98=EB=AF=B8=ED=95=9C=20=EC=A0=95=EB=B3=B4=EB=A1=9C=20?= =?UTF-8?q?=ED=91=9C=ED=98=84=EB=90=98=EB=8F=84=EB=A1=9D=20=EA=B0=9C?= =?UTF-8?q?=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/filter/LoggingFilter.java | 46 +++++++++++-------- src/main/resources/application-local.yml | 5 ++ src/main/resources/application-prod.yml | 7 ++- src/main/resources/application.yml | 8 ---- src/main/resources/logback-spring.xml | 18 +++++++- 5 files changed, 52 insertions(+), 32 deletions(-) diff --git a/src/main/java/com/climingo/climingoApi/global/filter/LoggingFilter.java b/src/main/java/com/climingo/climingoApi/global/filter/LoggingFilter.java index 81e9687..6f76cf4 100644 --- a/src/main/java/com/climingo/climingoApi/global/filter/LoggingFilter.java +++ b/src/main/java/com/climingo/climingoApi/global/filter/LoggingFilter.java @@ -8,11 +8,13 @@ import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.HashSet; +import java.util.Map; import java.util.UUID; import lombok.extern.slf4j.Slf4j; import org.slf4j.MDC; import org.springframework.stereotype.Component; import org.springframework.web.filter.OncePerRequestFilter; +import org.springframework.web.servlet.HandlerMapping; import org.springframework.web.util.ContentCachingRequestWrapper; import org.springframework.web.util.ContentCachingResponseWrapper; @@ -21,13 +23,14 @@ public class LoggingFilter extends OncePerRequestFilter { private static final String MDC_KEY_TRACE_ID = "traceId"; - private static final String[] LOG_BODY_METHODS = {"POST", "PUT", "PATCH"}; - private static final String[] HEADER_KEY_LIST = {"Authorization", "transactionUniqueNum"}; - private static final String[] URI_WITHOUT_LOG = {"swagger", "api-docs", "healthcheck"}; + private static final String[] LOG_BODY_METHODS = { "POST", "PUT", "PATCH" }; + private static final String[] HEADER_KEY_LIST = { "Authorization", "transactionUniqueNum" }; // 내가 원하는 header key만 출력되도록 설정 + private static final String[] URI_WITHOUT_LOG = { "swagger", "api-docs", "healthcheck" }; + private static final int MAX_BODY_LOG_LENGTH = 1000; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, - FilterChain filterChain) throws ServletException, IOException { + FilterChain filterChain) throws ServletException, IOException { String traceId = UUID.randomUUID().toString(); MDC.put(MDC_KEY_TRACE_ID, traceId); @@ -61,15 +64,21 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse private void logRequest(ContentCachingRequestWrapper request) { String method = request.getMethod(); + String uri = request.getRequestURI(); + String clientIp = request.getRemoteAddr(); + String userAgent = request.getHeader("User-Agent"); + String queryString = request.getQueryString(); - log.info("[REQUEST] {} {}", method, request.getRequestURI()); + Map pathVariables = (Map) request.getAttribute( + HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE); - logRequestTargetHeaders(request); + log.info("[REQUEST] Method: {}, URI: {}, Client-IP: {}, User-Agent: {}, QueryString: {}, PathVariables: {}", method, uri, clientIp, userAgent, + (queryString != null ? queryString : ""), (pathVariables != null ? pathVariables : "{}")); - logRequestQueryString(request); + logRequestTargetHeaders(request); boolean isMethodAllowed = Arrays.stream(LOG_BODY_METHODS) - .anyMatch(target -> target.equalsIgnoreCase(method)); + .anyMatch(target -> target.equalsIgnoreCase(method)); if (!isMethodAllowed) { return; @@ -80,7 +89,7 @@ private void logRequest(ContentCachingRequestWrapper request) { private void logResponse(ContentCachingResponseWrapper wrappedResponse, long elapsedTime) { log.info("[RESPONSE] Status : {}, Processing Time : {}ms", wrappedResponse.getStatus(), - elapsedTime); + elapsedTime); logResponseHeaders(wrappedResponse); @@ -111,19 +120,19 @@ private void logResponseHeaders(ContentCachingResponseWrapper wrappedResponse) { }); } - private static void logRequestQueryString(ContentCachingRequestWrapper request) { - String queryString = request.getQueryString(); - if (queryString != null) { - log.info("[REQUEST] QueryString : {}", queryString); - } - } - private String getRequestMessageBody(ContentCachingRequestWrapper wrappedRequest) { - return new String(wrappedRequest.getContentAsByteArray(), StandardCharsets.UTF_8); + return truncateBody(new String(wrappedRequest.getContentAsByteArray(), StandardCharsets.UTF_8)); } private String getResponseMessageBody(ContentCachingResponseWrapper wrappedResponse) { - return new String(wrappedResponse.getContentAsByteArray(), StandardCharsets.UTF_8); + return truncateBody(new String(wrappedResponse.getContentAsByteArray(), StandardCharsets.UTF_8)); + } + + private String truncateBody(String body) { + if (body.length() > MAX_BODY_LOG_LENGTH) { + return body.substring(0, MAX_BODY_LOG_LENGTH) + " [TRUNCATED]"; + } + return body; } private void filterAndLogBody(String messageBody, boolean isRequest) { @@ -138,4 +147,5 @@ private boolean isHealthcheckOrSwaggerRequest(HttpServletRequest request) { String uri = request.getRequestURI(); return Arrays.stream(URI_WITHOUT_LOG).anyMatch(uri::contains); } + } diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml index 03aa121..d9072c6 100644 --- a/src/main/resources/application-local.yml +++ b/src/main/resources/application-local.yml @@ -22,6 +22,11 @@ spring: init: mode: always +logging: + level: + org.hibernate.SQL: debug + org.hibernate.orm.jdbc.bind: trace + oauth2: kakao: client_id: ENC(1OlukTw7U+1AbNzhNIOLn5Kw7FAdHHDEFgQZbwLHB6C7IjncLII7ZGO1RukxW0Hc) diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 55cdbc3..8fc5af1 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -10,15 +10,14 @@ spring: jpa: properties: hibernate: - show_sql: true + show_sql: false format_sql: true use_sql_comments: true logging: level: - org.hibernate.SQL: info - org.hibernate.type.descriptor.sql: info - com.your.package: info + org.hibernate.SQL: debug + org.hibernate.orm.jdbc.bind: trace oauth2: kakao: diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 28fb9c1..e9c1ce6 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -17,14 +17,6 @@ jasypt: encryptor: password: ${JASYPT_PASSWORD} -logging: - level: - org: - hibernate: - type: - descriptor: - sql: trace - cloud: aws: credentials: diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml index d87f205..b2241cd 100644 --- a/src/main/resources/logback-spring.xml +++ b/src/main/resources/logback-spring.xml @@ -18,9 +18,23 @@ + + + + + + + + + + + + - - + + + + From ed3bd7a912a69d3cb297e811c94d82a22afd81e5 Mon Sep 17 00:00:00 2001 From: sunghyuki Date: Tue, 18 Feb 2025 23:21:20 +0900 Subject: [PATCH 10/35] =?UTF-8?q?refactor:=20response=20log=EC=97=90?= =?UTF-8?q?=EC=84=9C=20body=20=EC=A0=9C=EA=B1=B0=EB=90=98=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/filter/LoggingFilter.java | 39 ++++++++----------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/src/main/java/com/climingo/climingoApi/global/filter/LoggingFilter.java b/src/main/java/com/climingo/climingoApi/global/filter/LoggingFilter.java index 6f76cf4..1e858ba 100644 --- a/src/main/java/com/climingo/climingoApi/global/filter/LoggingFilter.java +++ b/src/main/java/com/climingo/climingoApi/global/filter/LoggingFilter.java @@ -63,28 +63,17 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse } private void logRequest(ContentCachingRequestWrapper request) { - String method = request.getMethod(); - String uri = request.getRequestURI(); - String clientIp = request.getRemoteAddr(); - String userAgent = request.getHeader("User-Agent"); - String queryString = request.getQueryString(); - - Map pathVariables = (Map) request.getAttribute( - HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE); - - log.info("[REQUEST] Method: {}, URI: {}, Client-IP: {}, User-Agent: {}, QueryString: {}, PathVariables: {}", method, uri, clientIp, userAgent, - (queryString != null ? queryString : ""), (pathVariables != null ? pathVariables : "{}")); - + logRequestBasicInfo(request); logRequestTargetHeaders(request); boolean isMethodAllowed = Arrays.stream(LOG_BODY_METHODS) - .anyMatch(target -> target.equalsIgnoreCase(method)); + .anyMatch(target -> target.equalsIgnoreCase(request.getMethod())); if (!isMethodAllowed) { return; } String body = getRequestMessageBody(request); - filterAndLogBody(body, true); + filterAndLogBody(body); } private void logResponse(ContentCachingResponseWrapper wrappedResponse, long elapsedTime) { @@ -92,10 +81,20 @@ private void logResponse(ContentCachingResponseWrapper wrappedResponse, long ela elapsedTime); logResponseHeaders(wrappedResponse); + } - String responseBody = getResponseMessageBody(wrappedResponse); + private void logRequestBasicInfo(HttpServletRequest request) { + String method = request.getMethod(); + String uri = request.getRequestURI(); + String clientIp = request.getRemoteAddr(); + String userAgent = request.getHeader("User-Agent"); + String queryString = request.getQueryString(); + + Map pathVariables = (Map) request.getAttribute( + HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE); - filterAndLogBody(responseBody, false); + log.info("[REQUEST] Method: {}, URI: {}, Client-IP: {}, User-Agent: {}, QueryString: {}, PathVariables: {}", method, uri, clientIp, userAgent, + (queryString != null ? queryString : ""), (pathVariables != null ? pathVariables : "{}")); } private void logRequestTargetHeaders(ContentCachingRequestWrapper wrappedRequest) { @@ -124,10 +123,6 @@ private String getRequestMessageBody(ContentCachingRequestWrapper wrappedRequest return truncateBody(new String(wrappedRequest.getContentAsByteArray(), StandardCharsets.UTF_8)); } - private String getResponseMessageBody(ContentCachingResponseWrapper wrappedResponse) { - return truncateBody(new String(wrappedResponse.getContentAsByteArray(), StandardCharsets.UTF_8)); - } - private String truncateBody(String body) { if (body.length() > MAX_BODY_LOG_LENGTH) { return body.substring(0, MAX_BODY_LOG_LENGTH) + " [TRUNCATED]"; @@ -135,12 +130,12 @@ private String truncateBody(String body) { return body; } - private void filterAndLogBody(String messageBody, boolean isRequest) { + private void filterAndLogBody(String messageBody) { if (messageBody.isEmpty()) { return; } - log.info("[{}] Body : {}", isRequest ? "REQUEST" : "RESPONSE", messageBody); + log.info("[REQUEST] Body : {}", messageBody); } private boolean isHealthcheckOrSwaggerRequest(HttpServletRequest request) { From daf553ef097ef218e91931eaf200e8dc11f65848 Mon Sep 17 00:00:00 2001 From: JongKeun Kim Date: Mon, 3 Mar 2025 22:11:26 +0900 Subject: [PATCH 11/35] fix: discord webhook url encrypt (#125) --- src/main/resources/application-dev.yml | 2 +- src/main/resources/application-local.yml | 2 +- src/main/resources/application-prod.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 9c5bbff..a57a406 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -43,7 +43,7 @@ app: time: ${buildTime} discord: - webhook-url: https://discord.com/api/webhooks/1244136146452090890/xFhaBJYgkEazDUuKGHRwsY3B38PBy0bbKwNZDrOtMyDdMJVvlE1rza6yVcNmJO5I6csg + webhook-url: ENC(gehHZPLnSgtSFooyikqPLYXbEHKNu83waF8d7qYCaRDvCsqJf0g7bJvD2uw8jdBdWuj05amhv0d51HlQssp1eY3z3tgFOPRQKL3zmAWatttZTiHMSPiPSTbjhXXgiq7bHXQoxzdFHUVmOC6nHiXu2h2jZzyJoAz3WLhINcjRXrUEFzQGzd6hcg==) auth: cookie: diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml index d9072c6..4ba6efd 100644 --- a/src/main/resources/application-local.yml +++ b/src/main/resources/application-local.yml @@ -47,7 +47,7 @@ app: time: local_temp_build_time discord: - webhook-url: https://discord.com/api/webhooks/1246453066535014442/JTLPOUfk-LDtVVYGR6AhWEc3AdEbOIwtxyX2YxTidtC4368SMh_hOeI5v_EU80szroZb + webhook-url: ENC(UiLVuQ7dNJ9Oq8QfSJ4QrSbNPnpGhBcbOczmOOEtj4EvUKknvuCOdT0Z5hcfU6RLeKsSxg20gBDNn7nQ2/Rp4m4pCpoICFg53CYpUFPzTC6l5KCq9pXM9jQ/uUYh3U41FQM7IK/yMCQI0JN2Y/gGDj7ZNmyIfBRwHMgGOKasVYaLOCsSJCU18A==) auth: cookie: diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 8fc5af1..d375b1c 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -40,7 +40,7 @@ app: time: ${buildTime} discord: - webhook-url: https://discord.com/api/webhooks/1246451017654997014/cn3094Fmv7VSNvBnZmMoht5M5Z6ufM9Q-fF7j7d_WRkGemJ2vszV-yAhvd065MT-NzfC + webhook-url: ENC(ISW+bpEH5n+1HUj5mMBpU2a/rdcvzhdAfPcGw63uY5aQxO7owDzK5nYTZ2fE/KM6h4D9rqCzztuQzmV2YTCM7qRBnU8Iu3RIsPQVm2SYrojEYGCA353kAxUhnqVLNImgkQpBgPROCAHr3ddHwBc8yjMotZg16umfKEajevWMqnormVn8l+76tQ==) auth: cookie: From 4b425759f8297d38f958faaef0806d59527f65a0 Mon Sep 17 00:00:00 2001 From: JongKeun Kim Date: Mon, 17 Mar 2025 21:59:35 +0900 Subject: [PATCH 12/35] =?UTF-8?q?[#126]=20=EC=BB=A8=ED=85=90=EC=B8=A0(?= =?UTF-8?q?=EA=B8=B0=EB=A1=9D)=20=EC=8B=A0=EA=B3=A0=20=EA=B8=B0=EB=8A=A5?= =?UTF-8?q?=20=EA=B4=80=EB=A0=A8=20API=20=EA=B5=AC=ED=98=84=20(#128)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 신고 사유 목록 조회 api 구현 - api 엔드포인트 추가 (GET /reports/reasons) - 신고 사유 enum 추가 * feat: 신고 엔터티 구현 - report entity - report status enum - report reason jpa converter * feat: 기록 신고 api 구현 - 기록 신고 시 신고 데이터가 insert되는 api * feat: 기록 3회 이상 신고 시 해당 기록은 차단되도록 로직 추가 - record에 blocked 필드 추가 및 목데이터 수정 - 테스트를 위한 auth token 목데이터 추가 * update: 신고 사유 목록 수정 * feat: 신고 목록 조회 api 구현 * fix: response entity 추가 * chore: log 파일 git에 포함되지 않도록 추가 * chore: 불필요하게 추가된 qclass 제거 --- .gitignore | 4 +- .../climingoApi/auth/domain/QAuthToken.java | 41 --------- .../climingoApi/gym/domain/QAddress.java | 39 --------- .../climingoApi/gym/domain/QGeoInfo.java | 39 --------- .../climingo/climingoApi/gym/domain/QGym.java | 58 ------------- .../climingoApi/level/domain/QLevel.java | 57 ------------- .../climingoApi/member/domain/QMember.java | 68 --------------- .../member/domain/QPhysicalInfo.java | 41 --------- .../record/domain/QBaseTimeEntity.java | 39 --------- .../climingoApi/record/domain/QRecord.java | 71 ---------------- .../record/api/RecordReportController.java | 29 +++++++ .../api/request/ReportRecordRequest.java | 4 + .../application/ReportRecordUsecase.java | 5 ++ .../climingoApi/record/domain/Record.java | 7 ++ .../report/api/ReportController.java | 34 ++++++++ .../api/response/ReportReasonsResponse.java | 4 + .../report/api/response/ReportResponse.java | 25 ++++++ .../report/application/ReportQuery.java | 9 ++ .../report/application/ReportService.java | 64 ++++++++++++++ .../climingoApi/report/domain/Report.java | 49 +++++++++++ .../report/domain/ReportReason.java | 38 +++++++++ .../report/domain/ReportReasonConverter.java | 17 ++++ .../report/domain/ReportRepository.java | 12 +++ .../report/domain/ReportStatus.java | 30 +++++++ src/main/resources/data.sql | 85 ++++++++++--------- 25 files changed, 373 insertions(+), 496 deletions(-) delete mode 100644 src/main/generated/com/climingo/climingoApi/auth/domain/QAuthToken.java delete mode 100644 src/main/generated/com/climingo/climingoApi/gym/domain/QAddress.java delete mode 100644 src/main/generated/com/climingo/climingoApi/gym/domain/QGeoInfo.java delete mode 100644 src/main/generated/com/climingo/climingoApi/gym/domain/QGym.java delete mode 100644 src/main/generated/com/climingo/climingoApi/level/domain/QLevel.java delete mode 100644 src/main/generated/com/climingo/climingoApi/member/domain/QMember.java delete mode 100644 src/main/generated/com/climingo/climingoApi/member/domain/QPhysicalInfo.java delete mode 100644 src/main/generated/com/climingo/climingoApi/record/domain/QBaseTimeEntity.java delete mode 100644 src/main/generated/com/climingo/climingoApi/record/domain/QRecord.java create mode 100644 src/main/java/com/climingo/climingoApi/record/api/RecordReportController.java create mode 100644 src/main/java/com/climingo/climingoApi/record/api/request/ReportRecordRequest.java create mode 100644 src/main/java/com/climingo/climingoApi/record/application/ReportRecordUsecase.java create mode 100644 src/main/java/com/climingo/climingoApi/report/api/ReportController.java create mode 100644 src/main/java/com/climingo/climingoApi/report/api/response/ReportReasonsResponse.java create mode 100644 src/main/java/com/climingo/climingoApi/report/api/response/ReportResponse.java create mode 100644 src/main/java/com/climingo/climingoApi/report/application/ReportQuery.java create mode 100644 src/main/java/com/climingo/climingoApi/report/application/ReportService.java create mode 100644 src/main/java/com/climingo/climingoApi/report/domain/Report.java create mode 100644 src/main/java/com/climingo/climingoApi/report/domain/ReportReason.java create mode 100644 src/main/java/com/climingo/climingoApi/report/domain/ReportReasonConverter.java create mode 100644 src/main/java/com/climingo/climingoApi/report/domain/ReportRepository.java create mode 100644 src/main/java/com/climingo/climingoApi/report/domain/ReportStatus.java diff --git a/.gitignore b/.gitignore index 3c1d00c..0ca4d75 100644 --- a/.gitignore +++ b/.gitignore @@ -42,4 +42,6 @@ out/ !**/src/main/generated/ ### macOS -**/.DS_Store \ No newline at end of file +**/.DS_Store +/LOGGING_FILE_PATH_IS_UNDEFINED/ +/src/main/generated/ \ No newline at end of file diff --git a/src/main/generated/com/climingo/climingoApi/auth/domain/QAuthToken.java b/src/main/generated/com/climingo/climingoApi/auth/domain/QAuthToken.java deleted file mode 100644 index 422c749..0000000 --- a/src/main/generated/com/climingo/climingoApi/auth/domain/QAuthToken.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.climingo.climingoApi.auth.domain; - -import static com.querydsl.core.types.PathMetadataFactory.*; - -import com.querydsl.core.types.dsl.*; - -import com.querydsl.core.types.PathMetadata; -import javax.annotation.processing.Generated; -import com.querydsl.core.types.Path; - - -/** - * QAuthToken is a Querydsl query type for AuthToken - */ -@Generated("com.querydsl.codegen.DefaultEntitySerializer") -public class QAuthToken extends EntityPathBase { - - private static final long serialVersionUID = 2093034968L; - - public static final QAuthToken authToken = new QAuthToken("authToken"); - - public final StringPath accessToken = createString("accessToken"); - - public final NumberPath memberId = createNumber("memberId", Long.class); - - public final StringPath refreshToken = createString("refreshToken"); - - public QAuthToken(String variable) { - super(AuthToken.class, forVariable(variable)); - } - - public QAuthToken(Path path) { - super(path.getType(), path.getMetadata()); - } - - public QAuthToken(PathMetadata metadata) { - super(AuthToken.class, metadata); - } - -} - diff --git a/src/main/generated/com/climingo/climingoApi/gym/domain/QAddress.java b/src/main/generated/com/climingo/climingoApi/gym/domain/QAddress.java deleted file mode 100644 index bc8a075..0000000 --- a/src/main/generated/com/climingo/climingoApi/gym/domain/QAddress.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.climingo.climingoApi.gym.domain; - -import static com.querydsl.core.types.PathMetadataFactory.*; - -import com.querydsl.core.types.dsl.*; - -import com.querydsl.core.types.PathMetadata; -import javax.annotation.processing.Generated; -import com.querydsl.core.types.Path; - - -/** - * QAddress is a Querydsl query type for Address - */ -@Generated("com.querydsl.codegen.DefaultEmbeddableSerializer") -public class QAddress extends BeanPath
{ - - private static final long serialVersionUID = -1383726286L; - - public static final QAddress address1 = new QAddress("address1"); - - public final StringPath address = createString("address"); - - public final StringPath zipCode = createString("zipCode"); - - public QAddress(String variable) { - super(Address.class, forVariable(variable)); - } - - public QAddress(Path path) { - super(path.getType(), path.getMetadata()); - } - - public QAddress(PathMetadata metadata) { - super(Address.class, metadata); - } - -} - diff --git a/src/main/generated/com/climingo/climingoApi/gym/domain/QGeoInfo.java b/src/main/generated/com/climingo/climingoApi/gym/domain/QGeoInfo.java deleted file mode 100644 index ba3bb9e..0000000 --- a/src/main/generated/com/climingo/climingoApi/gym/domain/QGeoInfo.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.climingo.climingoApi.gym.domain; - -import static com.querydsl.core.types.PathMetadataFactory.*; - -import com.querydsl.core.types.dsl.*; - -import com.querydsl.core.types.PathMetadata; -import javax.annotation.processing.Generated; -import com.querydsl.core.types.Path; - - -/** - * QGeoInfo is a Querydsl query type for GeoInfo - */ -@Generated("com.querydsl.codegen.DefaultEmbeddableSerializer") -public class QGeoInfo extends BeanPath { - - private static final long serialVersionUID = -316096803L; - - public static final QGeoInfo geoInfo = new QGeoInfo("geoInfo"); - - public final NumberPath latitude = createNumber("latitude", Double.class); - - public final NumberPath longitude = createNumber("longitude", Double.class); - - public QGeoInfo(String variable) { - super(GeoInfo.class, forVariable(variable)); - } - - public QGeoInfo(Path path) { - super(path.getType(), path.getMetadata()); - } - - public QGeoInfo(PathMetadata metadata) { - super(GeoInfo.class, metadata); - } - -} - diff --git a/src/main/generated/com/climingo/climingoApi/gym/domain/QGym.java b/src/main/generated/com/climingo/climingoApi/gym/domain/QGym.java deleted file mode 100644 index f0d31fa..0000000 --- a/src/main/generated/com/climingo/climingoApi/gym/domain/QGym.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.climingo.climingoApi.gym.domain; - -import static com.querydsl.core.types.PathMetadataFactory.*; - -import com.querydsl.core.types.dsl.*; - -import com.querydsl.core.types.PathMetadata; -import javax.annotation.processing.Generated; -import com.querydsl.core.types.Path; -import com.querydsl.core.types.dsl.PathInits; - - -/** - * QGym is a Querydsl query type for Gym - */ -@Generated("com.querydsl.codegen.DefaultEntitySerializer") -public class QGym extends EntityPathBase { - - private static final long serialVersionUID = 693192697L; - - private static final PathInits INITS = PathInits.DIRECT2; - - public static final QGym gym = new QGym("gym"); - - public final QAddress address; - - public final QGeoInfo geoInfo; - - public final NumberPath id = createNumber("id", Long.class); - - public final ListPath levels = this.createList("levels", com.climingo.climingoApi.level.domain.Level.class, com.climingo.climingoApi.level.domain.QLevel.class, PathInits.DIRECT2); - - public final StringPath name = createString("name"); - - public QGym(String variable) { - this(Gym.class, forVariable(variable), INITS); - } - - public QGym(Path path) { - this(path.getType(), path.getMetadata(), PathInits.getFor(path.getMetadata(), INITS)); - } - - public QGym(PathMetadata metadata) { - this(metadata, PathInits.getFor(metadata, INITS)); - } - - public QGym(PathMetadata metadata, PathInits inits) { - this(Gym.class, metadata, inits); - } - - public QGym(Class type, PathMetadata metadata, PathInits inits) { - super(type, metadata, inits); - this.address = inits.isInitialized("address") ? new QAddress(forProperty("address")) : null; - this.geoInfo = inits.isInitialized("geoInfo") ? new QGeoInfo(forProperty("geoInfo")) : null; - } - -} - diff --git a/src/main/generated/com/climingo/climingoApi/level/domain/QLevel.java b/src/main/generated/com/climingo/climingoApi/level/domain/QLevel.java deleted file mode 100644 index 8a61e68..0000000 --- a/src/main/generated/com/climingo/climingoApi/level/domain/QLevel.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.climingo.climingoApi.level.domain; - -import static com.querydsl.core.types.PathMetadataFactory.*; - -import com.querydsl.core.types.dsl.*; - -import com.querydsl.core.types.PathMetadata; -import javax.annotation.processing.Generated; -import com.querydsl.core.types.Path; -import com.querydsl.core.types.dsl.PathInits; - - -/** - * QLevel is a Querydsl query type for Level - */ -@Generated("com.querydsl.codegen.DefaultEntitySerializer") -public class QLevel extends EntityPathBase { - - private static final long serialVersionUID = -69753383L; - - private static final PathInits INITS = PathInits.DIRECT2; - - public static final QLevel level = new QLevel("level"); - - public final StringPath colorNameEn = createString("colorNameEn"); - - public final StringPath colorNameKo = createString("colorNameKo"); - - public final com.climingo.climingoApi.gym.domain.QGym gym; - - public final NumberPath id = createNumber("id", Long.class); - - public final NumberPath orderNum = createNumber("orderNum", Integer.class); - - public QLevel(String variable) { - this(Level.class, forVariable(variable), INITS); - } - - public QLevel(Path path) { - this(path.getType(), path.getMetadata(), PathInits.getFor(path.getMetadata(), INITS)); - } - - public QLevel(PathMetadata metadata) { - this(metadata, PathInits.getFor(metadata, INITS)); - } - - public QLevel(PathMetadata metadata, PathInits inits) { - this(Level.class, metadata, inits); - } - - public QLevel(Class type, PathMetadata metadata, PathInits inits) { - super(type, metadata, inits); - this.gym = inits.isInitialized("gym") ? new com.climingo.climingoApi.gym.domain.QGym(forProperty("gym"), inits.get("gym")) : null; - } - -} - diff --git a/src/main/generated/com/climingo/climingoApi/member/domain/QMember.java b/src/main/generated/com/climingo/climingoApi/member/domain/QMember.java deleted file mode 100644 index 94d76cf..0000000 --- a/src/main/generated/com/climingo/climingoApi/member/domain/QMember.java +++ /dev/null @@ -1,68 +0,0 @@ -package com.climingo.climingoApi.member.domain; - -import static com.querydsl.core.types.PathMetadataFactory.*; - -import com.querydsl.core.types.dsl.*; - -import com.querydsl.core.types.PathMetadata; -import javax.annotation.processing.Generated; -import com.querydsl.core.types.Path; -import com.querydsl.core.types.dsl.PathInits; - - -/** - * QMember is a Querydsl query type for Member - */ -@Generated("com.querydsl.codegen.DefaultEntitySerializer") -public class QMember extends EntityPathBase { - - private static final long serialVersionUID = 1574182789L; - - private static final PathInits INITS = PathInits.DIRECT2; - - public static final QMember member = new QMember("member1"); - - public final StringPath authId = createString("authId"); - - public final StringPath email = createString("email"); - - public final com.climingo.climingoApi.gym.domain.QGym homeGym; - - public final NumberPath id = createNumber("id", Long.class); - - public final StringPath nickname = createString("nickname"); - - public final QPhysicalInfo physicalInfo; - - public final StringPath profileUrl = createString("profileUrl"); - - public final StringPath providerType = createString("providerType"); - - public final ListPath records = this.createList("records", com.climingo.climingoApi.record.domain.Record.class, com.climingo.climingoApi.record.domain.QRecord.class, PathInits.DIRECT2); - - public final EnumPath role = createEnum("role", UserRole.class); - - public QMember(String variable) { - this(Member.class, forVariable(variable), INITS); - } - - public QMember(Path path) { - this(path.getType(), path.getMetadata(), PathInits.getFor(path.getMetadata(), INITS)); - } - - public QMember(PathMetadata metadata) { - this(metadata, PathInits.getFor(metadata, INITS)); - } - - public QMember(PathMetadata metadata, PathInits inits) { - this(Member.class, metadata, inits); - } - - public QMember(Class type, PathMetadata metadata, PathInits inits) { - super(type, metadata, inits); - this.homeGym = inits.isInitialized("homeGym") ? new com.climingo.climingoApi.gym.domain.QGym(forProperty("homeGym"), inits.get("homeGym")) : null; - this.physicalInfo = inits.isInitialized("physicalInfo") ? new QPhysicalInfo(forProperty("physicalInfo")) : null; - } - -} - diff --git a/src/main/generated/com/climingo/climingoApi/member/domain/QPhysicalInfo.java b/src/main/generated/com/climingo/climingoApi/member/domain/QPhysicalInfo.java deleted file mode 100644 index f519944..0000000 --- a/src/main/generated/com/climingo/climingoApi/member/domain/QPhysicalInfo.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.climingo.climingoApi.member.domain; - -import static com.querydsl.core.types.PathMetadataFactory.*; - -import com.querydsl.core.types.dsl.*; - -import com.querydsl.core.types.PathMetadata; -import javax.annotation.processing.Generated; -import com.querydsl.core.types.Path; - - -/** - * QPhysicalInfo is a Querydsl query type for PhysicalInfo - */ -@Generated("com.querydsl.codegen.DefaultEmbeddableSerializer") -public class QPhysicalInfo extends BeanPath { - - private static final long serialVersionUID = 1593798352L; - - public static final QPhysicalInfo physicalInfo = new QPhysicalInfo("physicalInfo"); - - public final NumberPath armSpan = createNumber("armSpan", Double.class); - - public final NumberPath height = createNumber("height", Double.class); - - public final NumberPath weight = createNumber("weight", Double.class); - - public QPhysicalInfo(String variable) { - super(PhysicalInfo.class, forVariable(variable)); - } - - public QPhysicalInfo(Path path) { - super(path.getType(), path.getMetadata()); - } - - public QPhysicalInfo(PathMetadata metadata) { - super(PhysicalInfo.class, metadata); - } - -} - diff --git a/src/main/generated/com/climingo/climingoApi/record/domain/QBaseTimeEntity.java b/src/main/generated/com/climingo/climingoApi/record/domain/QBaseTimeEntity.java deleted file mode 100644 index 6c9fc96..0000000 --- a/src/main/generated/com/climingo/climingoApi/record/domain/QBaseTimeEntity.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.climingo.climingoApi.record.domain; - -import static com.querydsl.core.types.PathMetadataFactory.*; - -import com.querydsl.core.types.dsl.*; - -import com.querydsl.core.types.PathMetadata; -import javax.annotation.processing.Generated; -import com.querydsl.core.types.Path; - - -/** - * QBaseTimeEntity is a Querydsl query type for BaseTimeEntity - */ -@Generated("com.querydsl.codegen.DefaultSupertypeSerializer") -public class QBaseTimeEntity extends EntityPathBase { - - private static final long serialVersionUID = 1425051427L; - - public static final QBaseTimeEntity baseTimeEntity = new QBaseTimeEntity("baseTimeEntity"); - - public final DateTimePath createdDate = createDateTime("createdDate", java.time.LocalDateTime.class); - - public final DateTimePath modifiedDate = createDateTime("modifiedDate", java.time.LocalDateTime.class); - - public QBaseTimeEntity(String variable) { - super(BaseTimeEntity.class, forVariable(variable)); - } - - public QBaseTimeEntity(Path path) { - super(path.getType(), path.getMetadata()); - } - - public QBaseTimeEntity(PathMetadata metadata) { - super(BaseTimeEntity.class, metadata); - } - -} - diff --git a/src/main/generated/com/climingo/climingoApi/record/domain/QRecord.java b/src/main/generated/com/climingo/climingoApi/record/domain/QRecord.java deleted file mode 100644 index cadc957..0000000 --- a/src/main/generated/com/climingo/climingoApi/record/domain/QRecord.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.climingo.climingoApi.record.domain; - -import static com.querydsl.core.types.PathMetadataFactory.*; - -import com.querydsl.core.types.dsl.*; - -import com.querydsl.core.types.PathMetadata; -import javax.annotation.processing.Generated; -import com.querydsl.core.types.Path; -import com.querydsl.core.types.dsl.PathInits; - - -/** - * QRecord is a Querydsl query type for Record - */ -@Generated("com.querydsl.codegen.DefaultEntitySerializer") -public class QRecord extends EntityPathBase { - - private static final long serialVersionUID = -505997645L; - - private static final PathInits INITS = PathInits.DIRECT2; - - public static final QRecord record = new QRecord("record"); - - public final QBaseTimeEntity _super = new QBaseTimeEntity(this); - - public final StringPath content = createString("content"); - - //inherited - public final DateTimePath createdDate = _super.createdDate; - - public final com.climingo.climingoApi.gym.domain.QGym gym; - - public final NumberPath id = createNumber("id", Long.class); - - public final com.climingo.climingoApi.level.domain.QLevel level; - - public final com.climingo.climingoApi.member.domain.QMember member; - - //inherited - public final DateTimePath modifiedDate = _super.modifiedDate; - - public final StringPath thumbnailUrl = createString("thumbnailUrl"); - - public final StringPath videoUrl = createString("videoUrl"); - - public QRecord(String variable) { - this(Record.class, forVariable(variable), INITS); - } - - public QRecord(Path path) { - this(path.getType(), path.getMetadata(), PathInits.getFor(path.getMetadata(), INITS)); - } - - public QRecord(PathMetadata metadata) { - this(metadata, PathInits.getFor(metadata, INITS)); - } - - public QRecord(PathMetadata metadata, PathInits inits) { - this(Record.class, metadata, inits); - } - - public QRecord(Class type, PathMetadata metadata, PathInits inits) { - super(type, metadata, inits); - this.gym = inits.isInitialized("gym") ? new com.climingo.climingoApi.gym.domain.QGym(forProperty("gym"), inits.get("gym")) : null; - this.level = inits.isInitialized("level") ? new com.climingo.climingoApi.level.domain.QLevel(forProperty("level"), inits.get("level")) : null; - this.member = inits.isInitialized("member") ? new com.climingo.climingoApi.member.domain.QMember(forProperty("member"), inits.get("member")) : null; - } - -} - diff --git a/src/main/java/com/climingo/climingoApi/record/api/RecordReportController.java b/src/main/java/com/climingo/climingoApi/record/api/RecordReportController.java new file mode 100644 index 0000000..2f95de1 --- /dev/null +++ b/src/main/java/com/climingo/climingoApi/record/api/RecordReportController.java @@ -0,0 +1,29 @@ +package com.climingo.climingoApi.record.api; + +import com.climingo.climingoApi.global.auth.RequestMember; +import com.climingo.climingoApi.member.domain.Member; +import com.climingo.climingoApi.record.api.request.ReportRecordRequest; +import com.climingo.climingoApi.record.application.ReportRecordUsecase; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +@RequiredArgsConstructor +@RestController +public class RecordReportController { + + private final ReportRecordUsecase reportRecordUsecase; + + @PostMapping("/records/{recordId}/report") + public ResponseEntity reportRecord( + @PathVariable("recordId") Long recordId, + @RequestMember Member member, + @RequestBody ReportRecordRequest request) { + reportRecordUsecase.reportRecord(recordId, member.getId(), request.reasonCode()); + + return ResponseEntity.ok().build(); + } +} diff --git a/src/main/java/com/climingo/climingoApi/record/api/request/ReportRecordRequest.java b/src/main/java/com/climingo/climingoApi/record/api/request/ReportRecordRequest.java new file mode 100644 index 0000000..c948fc1 --- /dev/null +++ b/src/main/java/com/climingo/climingoApi/record/api/request/ReportRecordRequest.java @@ -0,0 +1,4 @@ +package com.climingo.climingoApi.record.api.request; + +public record ReportRecordRequest(String reasonCode) { +} diff --git a/src/main/java/com/climingo/climingoApi/record/application/ReportRecordUsecase.java b/src/main/java/com/climingo/climingoApi/record/application/ReportRecordUsecase.java new file mode 100644 index 0000000..dd4d957 --- /dev/null +++ b/src/main/java/com/climingo/climingoApi/record/application/ReportRecordUsecase.java @@ -0,0 +1,5 @@ +package com.climingo.climingoApi.record.application; + +public interface ReportRecordUsecase { + void reportRecord(Long recordId, Long reporterId, String reasonCode); +} diff --git a/src/main/java/com/climingo/climingoApi/record/domain/Record.java b/src/main/java/com/climingo/climingoApi/record/domain/Record.java index d35fcd9..2fb71c5 100644 --- a/src/main/java/com/climingo/climingoApi/record/domain/Record.java +++ b/src/main/java/com/climingo/climingoApi/record/domain/Record.java @@ -48,6 +48,8 @@ public class Record extends BaseTimeEntity { private String content; + private boolean blocked; + @Builder public Record(Long id, Member member, Level level, Gym gym, String videoUrl, String thumbnailUrl, String content) { this.id = id; @@ -57,6 +59,7 @@ public Record(Long id, Member member, Level level, Gym gym, String videoUrl, Str this.videoUrl = videoUrl; this.thumbnailUrl = thumbnailUrl; this.content = content; + this.blocked = false; } public void update(Gym gym, Level level, String videoUrl) { @@ -76,4 +79,8 @@ public boolean isSameMember(Member member) { public boolean isEditable(Member member) { return isSameMember(member) || member.isAdmin(); } + + public void block() { + this.blocked = true; + } } diff --git a/src/main/java/com/climingo/climingoApi/report/api/ReportController.java b/src/main/java/com/climingo/climingoApi/report/api/ReportController.java new file mode 100644 index 0000000..f0f782b --- /dev/null +++ b/src/main/java/com/climingo/climingoApi/report/api/ReportController.java @@ -0,0 +1,34 @@ +package com.climingo.climingoApi.report.api; + +import com.climingo.climingoApi.report.api.response.ReportReasonsResponse; +import com.climingo.climingoApi.report.api.response.ReportResponse; +import com.climingo.climingoApi.report.application.ReportQuery; +import com.climingo.climingoApi.report.domain.ReportReason; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.Arrays; +import java.util.List; + +@RequestMapping("/reports") +@RestController +@RequiredArgsConstructor +public class ReportController { + + private final ReportQuery reportQuery; + + @GetMapping("/reasons") + public ResponseEntity> readReportReasons() { + return ResponseEntity.ok(Arrays.stream(ReportReason.values()) + .map(reason -> new ReportReasonsResponse(reason.getCode(), reason.getDescription())) + .toList()); + } + + @GetMapping + public ResponseEntity> readReports() { + // TODO 필터링(상태별, 날짜별 등) 기능 추가 + // TODO 회원 role이 admin인 경우에만 접근 가능하도록 추가 구현 필요 + return ResponseEntity.ok().body(reportQuery.readReports()); + } +} diff --git a/src/main/java/com/climingo/climingoApi/report/api/response/ReportReasonsResponse.java b/src/main/java/com/climingo/climingoApi/report/api/response/ReportReasonsResponse.java new file mode 100644 index 0000000..e1e43c0 --- /dev/null +++ b/src/main/java/com/climingo/climingoApi/report/api/response/ReportReasonsResponse.java @@ -0,0 +1,4 @@ +package com.climingo.climingoApi.report.api.response; + +public record ReportReasonsResponse(String code, String description) { +} diff --git a/src/main/java/com/climingo/climingoApi/report/api/response/ReportResponse.java b/src/main/java/com/climingo/climingoApi/report/api/response/ReportResponse.java new file mode 100644 index 0000000..22769e0 --- /dev/null +++ b/src/main/java/com/climingo/climingoApi/report/api/response/ReportResponse.java @@ -0,0 +1,25 @@ +package com.climingo.climingoApi.report.api.response; + +import com.climingo.climingoApi.report.domain.Report; + +import java.time.LocalDateTime; + +public record ReportResponse(Long id, + Long recordId, + Long reporterId, + String reasonCode, + String reasonDescription, + String status, + LocalDateTime createdDate) { + + public static ReportResponse of(Report report) { + return new ReportResponse( + report.getId(), + report.getRecordId(), + report.getReporterId(), + report.getReason().getCode(), + report.getReason().getDescription(), + report.getStatus().getCode(), + report.getCreatedDate()); + } +} diff --git a/src/main/java/com/climingo/climingoApi/report/application/ReportQuery.java b/src/main/java/com/climingo/climingoApi/report/application/ReportQuery.java new file mode 100644 index 0000000..515f9c2 --- /dev/null +++ b/src/main/java/com/climingo/climingoApi/report/application/ReportQuery.java @@ -0,0 +1,9 @@ +package com.climingo.climingoApi.report.application; + +import com.climingo.climingoApi.report.api.response.ReportResponse; + +import java.util.List; + +public interface ReportQuery { + List readReports(); +} diff --git a/src/main/java/com/climingo/climingoApi/report/application/ReportService.java b/src/main/java/com/climingo/climingoApi/report/application/ReportService.java new file mode 100644 index 0000000..631ec2e --- /dev/null +++ b/src/main/java/com/climingo/climingoApi/report/application/ReportService.java @@ -0,0 +1,64 @@ +package com.climingo.climingoApi.report.application; + +import com.climingo.climingoApi.member.domain.MemberRepository; +import com.climingo.climingoApi.record.application.ReportRecordUsecase; +import com.climingo.climingoApi.record.domain.Record; +import com.climingo.climingoApi.record.domain.RecordRepository; +import com.climingo.climingoApi.report.api.response.ReportResponse; +import com.climingo.climingoApi.report.domain.Report; +import com.climingo.climingoApi.report.domain.ReportReason; +import com.climingo.climingoApi.report.domain.ReportRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.NoSuchElementException; + +@Service +@RequiredArgsConstructor +public class ReportService implements ReportRecordUsecase, ReportQuery { + + private final RecordRepository recordRepository; + private final MemberRepository memberRepository; + private final ReportRepository reportRepository; + + @Transactional + @Override + public void reportRecord(Long recordId, Long reporterId, String reasonCode) { + validateReportable(recordId, reporterId); + + reportRepository.save(Report.create(ReportReason.fromCode(reasonCode), recordId, reporterId)); + + if (reportRepository.countByRecordId(recordId) >= 3) { + blockRecord(recordId); // 해당 레코드가 3회 이상 신고 시 차단 조치 + } + + // TODO 디코 알림 + } + + private void validateReportable(Long recordId, Long reporterId) { + if (!memberRepository.existsById(reporterId)) { + throw new NoSuchElementException("존재하지 않는 회원입니다."); + } + + if (!recordRepository.existsById(recordId)) { + throw new NoSuchElementException("존재하지 않는 기록입니다."); + } + + if (reportRepository.existsByReporterIdAndRecordId(reporterId, recordId)) { + throw new IllegalArgumentException("이미 신고한 기록입니다."); + } + } + + private void blockRecord(Long recordId) { + Record record = recordRepository.findById(recordId) + .orElseThrow(() -> new NoSuchElementException("존재하지 않는 기록입니다.")); + record.block(); + } + + @Override + public List readReports() { + return reportRepository.findAll().stream().map(ReportResponse::of).toList(); + } +} diff --git a/src/main/java/com/climingo/climingoApi/report/domain/Report.java b/src/main/java/com/climingo/climingoApi/report/domain/Report.java new file mode 100644 index 0000000..f82df85 --- /dev/null +++ b/src/main/java/com/climingo/climingoApi/report/domain/Report.java @@ -0,0 +1,49 @@ +package com.climingo.climingoApi.report.domain; + +import com.climingo.climingoApi.global.domain.BaseTimeEntity; +import jakarta.persistence.*; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class Report extends BaseTimeEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false) + @Enumerated(EnumType.STRING) + private ReportReason reason; + + @JoinColumn(name = "REPORTER_ID") + private Long reporterId; + + @JoinColumn(name = "RECORD_ID") + private Long recordId; + + @Column + @Enumerated(EnumType.STRING) + private ReportStatus status; + + @Builder + public Report(Long id, ReportReason reason, Long reporterId, Long recordId, ReportStatus status) { + this.id = id; + this.reason = reason; + this.reporterId = reporterId; + this.recordId = recordId; + this.status = status; + } + + public static Report create(ReportReason reason, Long recordId, Long reporterId) { + return Report.builder() + .reason(reason) + .reporterId(reporterId) + .recordId(recordId) + .status(ReportStatus.PENDING) + .build(); + } +} diff --git a/src/main/java/com/climingo/climingoApi/report/domain/ReportReason.java b/src/main/java/com/climingo/climingoApi/report/domain/ReportReason.java new file mode 100644 index 0000000..4343466 --- /dev/null +++ b/src/main/java/com/climingo/climingoApi/report/domain/ReportReason.java @@ -0,0 +1,38 @@ +package com.climingo.climingoApi.report.domain; + +import lombok.Getter; + +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +@Getter +public enum ReportReason { + IRRELEVANT_CONTENT("01", "서비스와 관련 없는 이미지/내용"), + OBSCENE_OR_ABUSIVE("02", "음란, 욕설, 혐오유발, 비방 내용"), + ADVERTISING("03", "개인의 광고, 홍보성 내용"), + PRIVACY_RISK("04", "개인정보 유출 위험"), + SPAM("05", "게시글 도배"), + COPYRIGHT_INFRINGEMENT("06", "저작권, 초상권 침해"), + OTHER("99", "기타") + ; + + private final String code; + private final String description; + + private static final Map CODE_MAP = + Stream.of(values()).collect(Collectors.toMap(ReportReason::getCode, reason -> reason)); + + ReportReason(String code, String description) { + this.code = code; + this.description = description; + } + + public static ReportReason fromCode(String code) { + ReportReason reportReason = CODE_MAP.getOrDefault(code, null); + if (reportReason == null) { + throw new IllegalArgumentException("Invalid reason code: " + code); + } + return reportReason; + } +} diff --git a/src/main/java/com/climingo/climingoApi/report/domain/ReportReasonConverter.java b/src/main/java/com/climingo/climingoApi/report/domain/ReportReasonConverter.java new file mode 100644 index 0000000..23cacf1 --- /dev/null +++ b/src/main/java/com/climingo/climingoApi/report/domain/ReportReasonConverter.java @@ -0,0 +1,17 @@ +package com.climingo.climingoApi.report.domain; + +import jakarta.persistence.AttributeConverter; +import jakarta.persistence.Converter; + +@Converter(autoApply = true) +public class ReportReasonConverter implements AttributeConverter { + @Override + public String convertToDatabaseColumn(ReportReason reportReason) { + return reportReason.getCode(); + } + + @Override + public ReportReason convertToEntityAttribute(String reasonConde) { + return ReportReason.fromCode(reasonConde); + } +} diff --git a/src/main/java/com/climingo/climingoApi/report/domain/ReportRepository.java b/src/main/java/com/climingo/climingoApi/report/domain/ReportRepository.java new file mode 100644 index 0000000..538c239 --- /dev/null +++ b/src/main/java/com/climingo/climingoApi/report/domain/ReportRepository.java @@ -0,0 +1,12 @@ +package com.climingo.climingoApi.report.domain; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface ReportRepository extends JpaRepository { + + int countByRecordId(Long recordId); + + boolean existsByReporterIdAndRecordId(Long reporterId, Long recordId); +} diff --git a/src/main/java/com/climingo/climingoApi/report/domain/ReportStatus.java b/src/main/java/com/climingo/climingoApi/report/domain/ReportStatus.java new file mode 100644 index 0000000..58a4e10 --- /dev/null +++ b/src/main/java/com/climingo/climingoApi/report/domain/ReportStatus.java @@ -0,0 +1,30 @@ +package com.climingo.climingoApi.report.domain; + +import lombok.Getter; + +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +@Getter +public enum ReportStatus { + PENDING("PENDING", "검토 대기"), + UNDER_REVIEW("UNDER_REVIEW", "검토 중"), + ACTION_TAKEN("ACTION_TAKEN", "조치 완료"), + REJECTED("REJECTED", "반려됨"); + + private final String code; + private final String description; + + ReportStatus(String code, String description) { + this.code = code; + this.description = description; + } + + private static final Map CODE_MAP = + Stream.of(values()).collect(Collectors.toMap(ReportStatus::getCode, status -> status)); + + public static ReportStatus fromCode(String code) { + return CODE_MAP.get(code); + } +} diff --git a/src/main/resources/data.sql b/src/main/resources/data.sql index b0dc36c..5422033 100644 --- a/src/main/resources/data.sql +++ b/src/main/resources/data.sql @@ -40,47 +40,48 @@ insert into level (id, order_num, gym_id, color_name_ko, color_name_en) values ( insert into member (id, auth_id, provider_type, nickname, profile_url, email, home_gym_id, arm_span, height, weight, role) values(9999, 'test_auth_id_9999', 'kakao', 'testName', 'http://k.kakaocdn.net/dn/dTDso6/btsECljbpYi/JqJl8DHkrVbuwYlBlVGEkK/img_110x110.jpg', null, null, null, null, null, 'USER'); +insert into auth_token(member_id, access_token, refresh_token, created_date, modified_date) values (9999, 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJtZW1iZXJJZCI6OTk5OSwiYXV0aElkIjoidGVzdF9hdXRoX2lkXzk5OTkiLCJwcm92aWRlclR5cGUiOiJrYWthbyIsIm5pY2tuYW1lIjoia2FrYW8iLCJyb2xlIjoiVVNFUiIsImV4cCI6MTc0MjEyNjQxNX0.oQuBzr5tdbsT6e_3ZjDrHtukNKUO0i20P5-YrYOaCao', 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJtZW1iZXJJZCI6OTk5OSwiYXV0aElkIjoidGVzdF9hdXRoX2lkXzk5OTkiLCJwcm92aWRlclR5cGUiOiJrYWthbyIsIm5pY2tuYW1lIjoia2FrYW8iLCJyb2xlIjoiVVNFUiIsImV4cCI6MTc0MjEyNjQxNX0.oQuBzr5tdbsT6e_3ZjDrHtukNKUO0i20P5-YrYOaCao', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP()); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date) values(1, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP()); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date) values(2, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP()); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date) values(3, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP()); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date) values(4, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP()); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date) values(5, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP()); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date) values(6, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP()); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date) values(7, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP()); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date) values(8, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP()); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date) values(9, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP()); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date) values(10, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP()); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date) values(11, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP()); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date) values(12, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP()); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date) values(13, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP()); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date) values(14, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP()); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date) values(15, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP()); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date) values(16, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP()); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date) values(17, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP()); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date) values(18, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP()); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date) values(19, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP()); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date) values(20, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP()); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date) values(21, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP()); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date) values(22, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP()); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date) values(23, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP()); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date) values(24, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP()); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date) values(25, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP()); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date) values(26, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP()); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date) values(27, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP()); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date) values(28, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP()); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date) values(29, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP()); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date) values(30, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP()); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date) values(31, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP()); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date) values(32, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP()); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date) values(33, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP()); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date) values(34, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP()); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date) values(35, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP()); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date) values(36, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP()); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date) values(37, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP()); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date) values(38, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP()); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date) values(39, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP()); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date) values(40, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP()); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date) values(41, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP()); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date) values(42, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP()); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(1, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(2, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(3, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(4, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(5, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(6, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(7, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(8, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(9, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(10, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(11, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(12, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(13, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(14, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(15, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(16, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(17, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(18, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(19, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(20, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(21, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(22, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(23, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(24, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(25, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(26, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(27, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(28, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(29, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(30, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(31, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(32, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(33, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(34, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(35, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(36, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(37, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(38, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(39, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(40, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(41, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(42, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); From 1b52918c98dd3f071c595046dc8b610879dada64 Mon Sep 17 00:00:00 2001 From: JongKeun Kim Date: Sun, 13 Apr 2025 18:29:27 +0900 Subject: [PATCH 13/35] =?UTF-8?q?[#129]=20=EA=B8=B0=EB=A1=9D=20=EB=AA=A9?= =?UTF-8?q?=EB=A1=9D=20=EC=A1=B0=ED=9A=8C=20api=EC=97=90=20=EC=B0=A8?= =?UTF-8?q?=EB=8B=A8(block)=20=ED=95=84=ED=84=B0=EB=A7=81=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#130)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: 테스트용 토큰 dml 추가 * feat: record 목록 조회 시 차단 컨텐츠 필터링 기능 추가 * refactor: 신고 사유 내용 수정 * refactor: refreshtoken 만료 시간 변경 * fix: 쿼리 버그 수정 --- .../climingoApi/auth/util/JwtUtil.java | 3 +- .../climingoApi/member/domain/Member.java | 4 + .../record/api/RecordController.java | 5 +- .../record/application/RecordService.java | 4 +- .../record/domain/RecordRepositoryCustom.java | 4 +- .../domain/RecordRepositoryCustomImpl.java | 97 +++++++++++-------- .../report/application/ReportService.java | 7 +- .../climingoApi/report/domain/Block.java | 37 +++++++ .../report/domain/BlockRepository.java | 8 ++ .../report/domain/ReportReason.java | 13 ++- src/main/resources/data.sql | 2 +- 11 files changed, 123 insertions(+), 61 deletions(-) create mode 100644 src/main/java/com/climingo/climingoApi/report/domain/Block.java create mode 100644 src/main/java/com/climingo/climingoApi/report/domain/BlockRepository.java diff --git a/src/main/java/com/climingo/climingoApi/auth/util/JwtUtil.java b/src/main/java/com/climingo/climingoApi/auth/util/JwtUtil.java index ff9c4fe..2294523 100644 --- a/src/main/java/com/climingo/climingoApi/auth/util/JwtUtil.java +++ b/src/main/java/com/climingo/climingoApi/auth/util/JwtUtil.java @@ -22,12 +22,11 @@ @Component public class JwtUtil { - public static final int REFRESH_TOKEN_EXP = 60 * 60 * 24 * 7; - @Value("${auth.jwt-key}") private static String KEY; public static final int ACCESS_TOKEN_EXP = 60 * 60; + public static final int REFRESH_TOKEN_EXP = 60 * 60 * 24 * 365; // todo 만료 기간 임시로 1년 설정해 둠. 추후 변경 필요 public static final String ACCESS_TOKEN_NAME = "accessToken"; public static final String REFRESH_TOKEN_NAME = "refreshToken"; diff --git a/src/main/java/com/climingo/climingoApi/member/domain/Member.java b/src/main/java/com/climingo/climingoApi/member/domain/Member.java index 7d014c4..fc86cef 100644 --- a/src/main/java/com/climingo/climingoApi/member/domain/Member.java +++ b/src/main/java/com/climingo/climingoApi/member/domain/Member.java @@ -88,6 +88,10 @@ public static Member createGuest() { .build(); } + public boolean isGuest() { + return this.role.equals(UserRole.GUEST); + } + public boolean isSameMember(Long memberId) { return this.id != null && this.id.equals(memberId); } diff --git a/src/main/java/com/climingo/climingoApi/record/api/RecordController.java b/src/main/java/com/climingo/climingoApi/record/api/RecordController.java index 03d7b69..b56eeb3 100644 --- a/src/main/java/com/climingo/climingoApi/record/api/RecordController.java +++ b/src/main/java/com/climingo/climingoApi/record/api/RecordController.java @@ -74,13 +74,14 @@ public ResponseEntity find( // } @GetMapping("/records") - public ResponseEntity> findPage(@RequestParam(value = "gymId", required = false) Long gymId, + public ResponseEntity> findPage(@RequestMember Member member, + @RequestParam(value = "gymId", required = false) Long gymId, @RequestParam(value = "levelId", required = false) Long levelId, @RequestParam(value = "memberId", required = false) Long memberId, @RequestParam(value = "page", required = false, defaultValue = "0") @Min(0) Integer page, @RequestParam(value = "size", required = false, defaultValue = "10") @Min(1) Integer size) { - PageDto pageDto = recordService.findPage(gymId, levelId, memberId, page, size);// TODO: 예외 global 하게 처리 필요 + PageDto pageDto = recordService.findPage(member, gymId, levelId, memberId, page, size);// TODO: 예외 global 하게 처리 필요 return ResponseEntity.ok().body(pageDto); } diff --git a/src/main/java/com/climingo/climingoApi/record/application/RecordService.java b/src/main/java/com/climingo/climingoApi/record/application/RecordService.java index 07c0323..d7065c3 100644 --- a/src/main/java/com/climingo/climingoApi/record/application/RecordService.java +++ b/src/main/java/com/climingo/climingoApi/record/application/RecordService.java @@ -121,8 +121,8 @@ public List findAll(Long gymId, Long levelId, Long memberId) { } @Transactional(readOnly = true) - public PageDto findPage(Long gymId, Long levelId, Long memberId, Integer page, Integer size) { - Page recordPage = recordRepository.findRecordPage(gymId, levelId, memberId, page, size); + public PageDto findPage(Member requestMember, Long gymId, Long levelId, Long memberId, Integer page, Integer size) { + Page recordPage = recordRepository.findRecordPage(requestMember, gymId, levelId, memberId, page, size); return PageDto.builder() .totalCount(recordPage.getTotalElements()) diff --git a/src/main/java/com/climingo/climingoApi/record/domain/RecordRepositoryCustom.java b/src/main/java/com/climingo/climingoApi/record/domain/RecordRepositoryCustom.java index 65bd772..b5e00b0 100644 --- a/src/main/java/com/climingo/climingoApi/record/domain/RecordRepositoryCustom.java +++ b/src/main/java/com/climingo/climingoApi/record/domain/RecordRepositoryCustom.java @@ -1,13 +1,15 @@ package com.climingo.climingoApi.record.domain; import java.util.List; + +import com.climingo.climingoApi.member.domain.Member; import org.springframework.data.domain.Page; public interface RecordRepositoryCustom { List findAllWithDetails(Long gymId, Long levelId, Long memberId); - Page findRecordPage(Long gymId, Long levelId, Long memberId, Integer page, Integer size); + Page findRecordPage(Member requestMember, Long gymId, Long levelId, Long memberId, Integer page, Integer size); Page findMyRecordPage(Long memberId, Integer page, Integer size); diff --git a/src/main/java/com/climingo/climingoApi/record/domain/RecordRepositoryCustomImpl.java b/src/main/java/com/climingo/climingoApi/record/domain/RecordRepositoryCustomImpl.java index ac11c1c..2b88596 100644 --- a/src/main/java/com/climingo/climingoApi/record/domain/RecordRepositoryCustomImpl.java +++ b/src/main/java/com/climingo/climingoApi/record/domain/RecordRepositoryCustomImpl.java @@ -1,63 +1,74 @@ package com.climingo.climingoApi.record.domain; -import static com.climingo.climingoApi.gym.domain.QGym.gym; -import static com.climingo.climingoApi.level.domain.QLevel.level; -import static com.climingo.climingoApi.member.domain.QMember.member; -import static com.climingo.climingoApi.record.domain.QRecord.record; - +import com.climingo.climingoApi.member.domain.Member; import com.querydsl.core.types.Order; import com.querydsl.core.types.OrderSpecifier; import com.querydsl.core.types.dsl.BooleanExpression; +import com.querydsl.core.types.dsl.Expressions; import com.querydsl.jpa.impl.JPAQuery; import com.querydsl.jpa.impl.JPAQueryFactory; -import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.support.PageableExecutionUtils; +import java.util.List; + +import static com.climingo.climingoApi.gym.domain.QGym.gym; +import static com.climingo.climingoApi.level.domain.QLevel.level; +import static com.climingo.climingoApi.member.domain.QMember.member; +import static com.climingo.climingoApi.record.domain.QRecord.record; +import static com.climingo.climingoApi.report.domain.QBlock.block; + @RequiredArgsConstructor public class RecordRepositoryCustomImpl implements RecordRepositoryCustom { -// @PersistenceContext -// private final EntityManager em; - private final JPAQueryFactory queryFactory; @Override public List findAllWithDetails(Long gymId, Long levelId, Long memberId) { return queryFactory.select(record) - .from(record) - .innerJoin(record.member, member).fetchJoin() - .innerJoin(record.gym, gym).fetchJoin() - .innerJoin(record.level, level).fetchJoin() - .where(gymIdEq(gymId), - levelIdEq(levelId), - memberIdEq(memberId)) - .fetch(); + .from(record) + .innerJoin(record.member, member).fetchJoin() + .innerJoin(record.gym, gym).fetchJoin() + .innerJoin(record.level, level).fetchJoin() + .where(gymIdEq(gymId), + levelIdEq(levelId), + memberIdEq(memberId)) + .fetch(); } @Override - public Page findRecordPage(Long gymId, Long levelId, Long memberId, Integer page, Integer size) { + public Page findRecordPage(Member requestMember, Long gymId, Long levelId, Long memberId, Integer page, Integer size) { List contents = queryFactory.selectFrom(record) - .distinct() - .innerJoin(record.member, member).fetchJoin() - .innerJoin(record.gym, gym).fetchJoin() - .innerJoin(record.level, level).fetchJoin() - .where(gymIdEq(gymId), - levelIdEq(levelId), - memberIdEq(memberId)) - .orderBy(new OrderSpecifier<>(Order.DESC, record.createdDate)) - .offset((long) page * size) - .limit(size) - .fetch(); + .distinct() + .leftJoin(block) + .on(record.id.eq(block.recordId) + .and(requestMember.isGuest() ? Expressions.booleanTemplate("false") : block.memberId.eq(requestMember.getId()))) + .innerJoin(record.member, member).fetchJoin() + .innerJoin(record.gym, gym).fetchJoin() + .innerJoin(record.level, level).fetchJoin() + .where(gymIdEq(gymId), + levelIdEq(levelId), + memberIdEq(memberId), + record.blocked.isFalse(), + requestMember.isGuest() ? Expressions.booleanTemplate("true") : block.id.isNull()) + .orderBy(new OrderSpecifier<>(Order.DESC, record.createdDate)) + .offset((long) page * size) + .limit(size) + .fetch(); JPAQuery countQuery = queryFactory.select(record.count()) - .from(record) - .where(gymIdEq(gymId), - levelIdEq(levelId), - memberIdEq(memberId)); + .from(record) + .leftJoin(block) + .on(record.id.eq(block.recordId) + .and(requestMember.isGuest() ? Expressions.booleanTemplate("false") : block.memberId.eq(requestMember.getId()))) + .where(gymIdEq(gymId), + levelIdEq(levelId), + memberIdEq(memberId), + record.blocked.isFalse(), + block.id.isNull()); Pageable pageable = PageRequest.of(page, size); return PageableExecutionUtils.getPage(contents, pageable, countQuery::fetchOne); @@ -66,18 +77,18 @@ public Page findRecordPage(Long gymId, Long levelId, Long memberId, Inte @Override public Page findMyRecordPage(Long memberId, Integer page, Integer size) { List contents = queryFactory.selectFrom(record) - .innerJoin(record.member, member).fetchJoin() - .innerJoin(record.gym, gym).fetchJoin() - .innerJoin(record.level, level).fetchJoin() - .where(memberIdEq(memberId)) - .orderBy(new OrderSpecifier<>(Order.DESC, record.createdDate)) - .offset((long) page * size) - .limit(size) - .fetch(); + .innerJoin(record.member, member).fetchJoin() + .innerJoin(record.gym, gym).fetchJoin() + .innerJoin(record.level, level).fetchJoin() + .where(memberIdEq(memberId)) + .orderBy(new OrderSpecifier<>(Order.DESC, record.createdDate)) + .offset((long) page * size) + .limit(size) + .fetch(); JPAQuery countQuery = queryFactory.select(record.count()) - .from(record) - .where(memberIdEq(memberId)); + .from(record) + .where(memberIdEq(memberId)); Pageable pageable = PageRequest.of(page, size); return PageableExecutionUtils.getPage(contents, pageable, countQuery::fetchOne); diff --git a/src/main/java/com/climingo/climingoApi/report/application/ReportService.java b/src/main/java/com/climingo/climingoApi/report/application/ReportService.java index 631ec2e..186c064 100644 --- a/src/main/java/com/climingo/climingoApi/report/application/ReportService.java +++ b/src/main/java/com/climingo/climingoApi/report/application/ReportService.java @@ -5,9 +5,7 @@ import com.climingo.climingoApi.record.domain.Record; import com.climingo.climingoApi.record.domain.RecordRepository; import com.climingo.climingoApi.report.api.response.ReportResponse; -import com.climingo.climingoApi.report.domain.Report; -import com.climingo.climingoApi.report.domain.ReportReason; -import com.climingo.climingoApi.report.domain.ReportRepository; +import com.climingo.climingoApi.report.domain.*; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -22,6 +20,7 @@ public class ReportService implements ReportRecordUsecase, ReportQuery { private final RecordRepository recordRepository; private final MemberRepository memberRepository; private final ReportRepository reportRepository; + private final BlockRepository blockRepository; @Transactional @Override @@ -34,6 +33,8 @@ public void reportRecord(Long recordId, Long reporterId, String reasonCode) { blockRecord(recordId); // 해당 레코드가 3회 이상 신고 시 차단 조치 } + blockRepository.save(Block.create(reporterId, recordId)); + // TODO 디코 알림 } diff --git a/src/main/java/com/climingo/climingoApi/report/domain/Block.java b/src/main/java/com/climingo/climingoApi/report/domain/Block.java new file mode 100644 index 0000000..73e88db --- /dev/null +++ b/src/main/java/com/climingo/climingoApi/report/domain/Block.java @@ -0,0 +1,37 @@ +package com.climingo.climingoApi.report.domain; + +import com.climingo.climingoApi.global.domain.BaseTimeEntity; +import jakarta.persistence.*; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class Block extends BaseTimeEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @JoinColumn(nullable = false) + private Long memberId; + + @JoinColumn(nullable = false) + private Long recordId; + + @Builder + public Block(Long id, Long memberId, Long recordId) { + this.id = id; + this.memberId = memberId; + this.recordId = recordId; + } + + public static Block create(Long memberId, Long recordId) { + return Block.builder() + .memberId(memberId) + .recordId(recordId) + .build(); + } +} diff --git a/src/main/java/com/climingo/climingoApi/report/domain/BlockRepository.java b/src/main/java/com/climingo/climingoApi/report/domain/BlockRepository.java new file mode 100644 index 0000000..9953f40 --- /dev/null +++ b/src/main/java/com/climingo/climingoApi/report/domain/BlockRepository.java @@ -0,0 +1,8 @@ +package com.climingo.climingoApi.report.domain; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface BlockRepository extends JpaRepository { +} diff --git a/src/main/java/com/climingo/climingoApi/report/domain/ReportReason.java b/src/main/java/com/climingo/climingoApi/report/domain/ReportReason.java index 4343466..405a13c 100644 --- a/src/main/java/com/climingo/climingoApi/report/domain/ReportReason.java +++ b/src/main/java/com/climingo/climingoApi/report/domain/ReportReason.java @@ -8,13 +8,12 @@ @Getter public enum ReportReason { - IRRELEVANT_CONTENT("01", "서비스와 관련 없는 이미지/내용"), - OBSCENE_OR_ABUSIVE("02", "음란, 욕설, 혐오유발, 비방 내용"), - ADVERTISING("03", "개인의 광고, 홍보성 내용"), - PRIVACY_RISK("04", "개인정보 유출 위험"), - SPAM("05", "게시글 도배"), - COPYRIGHT_INFRINGEMENT("06", "저작권, 초상권 침해"), - OTHER("99", "기타") + IRRELEVANT_CONTENT("01", "서비스와 관련 없는 영상입니다."), + OBSCENE_OR_ABUSIVE("02", "음란, 욕설, 혐오유발, 비방성 내용입니다."), + ADVERTISING("03", "개인의 광고나 홍보성 내용입니다."), + PRIVACY_RISK("04", "개인정보 유출의 위험이 있습니다."), + SPAM("05", "게시글을 도배했습니다."), + COPYRIGHT_INFRINGEMENT("06", "저작권이나 초상권을 침해한 영상입니다."), ; private final String code; diff --git a/src/main/resources/data.sql b/src/main/resources/data.sql index 5422033..b8322a6 100644 --- a/src/main/resources/data.sql +++ b/src/main/resources/data.sql @@ -40,7 +40,7 @@ insert into level (id, order_num, gym_id, color_name_ko, color_name_en) values ( insert into member (id, auth_id, provider_type, nickname, profile_url, email, home_gym_id, arm_span, height, weight, role) values(9999, 'test_auth_id_9999', 'kakao', 'testName', 'http://k.kakaocdn.net/dn/dTDso6/btsECljbpYi/JqJl8DHkrVbuwYlBlVGEkK/img_110x110.jpg', null, null, null, null, null, 'USER'); -insert into auth_token(member_id, access_token, refresh_token, created_date, modified_date) values (9999, 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJtZW1iZXJJZCI6OTk5OSwiYXV0aElkIjoidGVzdF9hdXRoX2lkXzk5OTkiLCJwcm92aWRlclR5cGUiOiJrYWthbyIsIm5pY2tuYW1lIjoia2FrYW8iLCJyb2xlIjoiVVNFUiIsImV4cCI6MTc0MjEyNjQxNX0.oQuBzr5tdbsT6e_3ZjDrHtukNKUO0i20P5-YrYOaCao', 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJtZW1iZXJJZCI6OTk5OSwiYXV0aElkIjoidGVzdF9hdXRoX2lkXzk5OTkiLCJwcm92aWRlclR5cGUiOiJrYWthbyIsIm5pY2tuYW1lIjoia2FrYW8iLCJyb2xlIjoiVVNFUiIsImV4cCI6MTc0MjEyNjQxNX0.oQuBzr5tdbsT6e_3ZjDrHtukNKUO0i20P5-YrYOaCao', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP()); +insert into auth_token(member_id, access_token, refresh_token, created_date, modified_date) values (9999, 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJtZW1iZXJJZCI6OTk5OSwiYXV0aElkIjoidGVzdF9hdXRoX2lkXzk5OTkiLCJwcm92aWRlclR5cGUiOiJrYWthbyIsIm5pY2tuYW1lIjoia2FrYW8iLCJyb2xlIjoiVVNFUiIsImV4cCI6MTgyOTA2NDA3NH0.9BT0LZZNs7zP-yqyRU6EKp57Xz3GFflbyB_ABpF1I-A', 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJtZW1iZXJJZCI6OTk5OSwiYXV0aElkIjoidGVzdF9hdXRoX2lkXzk5OTkiLCJwcm92aWRlclR5cGUiOiJrYWthbyIsIm5pY2tuYW1lIjoia2FrYW8iLCJyb2xlIjoiVVNFUiIsImV4cCI6MTgyOTA2NDA3NH0.9BT0LZZNs7zP-yqyRU6EKp57Xz3GFflbyB_ABpF1I-A', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP()); insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(1, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(2, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); From 747db4611948e14b0c5e173c9a7c8d189c093310 Mon Sep 17 00:00:00 2001 From: JongKeun Kim Date: Thu, 17 Apr 2025 00:37:05 +0900 Subject: [PATCH 14/35] fix: discord webhook url encrypt (#132) --- src/main/resources/application-dev.yml | 2 +- src/main/resources/application-local.yml | 2 +- src/main/resources/application-prod.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index a57a406..d1863cd 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -43,7 +43,7 @@ app: time: ${buildTime} discord: - webhook-url: ENC(gehHZPLnSgtSFooyikqPLYXbEHKNu83waF8d7qYCaRDvCsqJf0g7bJvD2uw8jdBdWuj05amhv0d51HlQssp1eY3z3tgFOPRQKL3zmAWatttZTiHMSPiPSTbjhXXgiq7bHXQoxzdFHUVmOC6nHiXu2h2jZzyJoAz3WLhINcjRXrUEFzQGzd6hcg==) + webhook-url: ENC(AU5F7ZCFfWbd9B+G3JXqZLEOfEA9u+tvKjCoJQQjjZyWYkl990YV2Ijn1hh3j8qcA3im+A02HgLz1Zwuz4IV3G1n20JpFyEq2nb8Q4pV/7DZ5LW7iA+PKeA9QMqOy7lTU4iF3ZpQ7vuA0QKpEsvzcfoXs7UrIvYwgs85MFi8mThe4677K4uDtg==) auth: cookie: diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml index 4ba6efd..9fe1a3a 100644 --- a/src/main/resources/application-local.yml +++ b/src/main/resources/application-local.yml @@ -47,7 +47,7 @@ app: time: local_temp_build_time discord: - webhook-url: ENC(UiLVuQ7dNJ9Oq8QfSJ4QrSbNPnpGhBcbOczmOOEtj4EvUKknvuCOdT0Z5hcfU6RLeKsSxg20gBDNn7nQ2/Rp4m4pCpoICFg53CYpUFPzTC6l5KCq9pXM9jQ/uUYh3U41FQM7IK/yMCQI0JN2Y/gGDj7ZNmyIfBRwHMgGOKasVYaLOCsSJCU18A==) + webhook-url: ENC(dAoTC+tGjODq5gYu6ockWshjB5QW8UhBG21IBpxKF2VA/cNDU6cHW1v0M5Vl4z4YH8PFdIYMC47PH7g7m2pMRr/hFzpqFHWu0QSPsig1i/qS+n5fXc7lRj4aujLXjW+Ui6Em7xoE11ROkgILUUrfw9OA6OO0YZycbTD6w5/6IxElXs9WrxOMsw==) auth: cookie: diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index d375b1c..379a240 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -40,7 +40,7 @@ app: time: ${buildTime} discord: - webhook-url: ENC(ISW+bpEH5n+1HUj5mMBpU2a/rdcvzhdAfPcGw63uY5aQxO7owDzK5nYTZ2fE/KM6h4D9rqCzztuQzmV2YTCM7qRBnU8Iu3RIsPQVm2SYrojEYGCA353kAxUhnqVLNImgkQpBgPROCAHr3ddHwBc8yjMotZg16umfKEajevWMqnormVn8l+76tQ==) + webhook-url: ENC(X0JkZftnjbpr3pu3c30QHaGKPAi/r+7nl6sK5q49ygGsLfhD6My0wBklFJc3vXCXWOA5N72l8fNvNLXKscu0AMMQdm995Ngxd5CRhaOVLDY81fwNgAb7mK5mNraqa6PmU4AVb7VkpEl5753YKV4PcY+n8a6pnfk1ql9TJmAHyAlp8LdKK1LNVg==) auth: cookie: From ebf9e923b331e39d04429d5eff19987c00e34e8b Mon Sep 17 00:00:00 2001 From: JongKeun Kim Date: Fri, 18 Apr 2025 22:43:26 +0900 Subject: [PATCH 15/35] =?UTF-8?q?fix:=20accessToken=20=EB=A7=8C=EB=A3=8C?= =?UTF-8?q?=EC=8B=9C=EA=B0=84=20=EC=9E=84=EC=8B=9C=20=EB=B3=80=EA=B2=BD=20?= =?UTF-8?q?(#133)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/climingo/climingoApi/auth/util/JwtUtil.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/climingo/climingoApi/auth/util/JwtUtil.java b/src/main/java/com/climingo/climingoApi/auth/util/JwtUtil.java index 2294523..7011764 100644 --- a/src/main/java/com/climingo/climingoApi/auth/util/JwtUtil.java +++ b/src/main/java/com/climingo/climingoApi/auth/util/JwtUtil.java @@ -25,7 +25,7 @@ public class JwtUtil { @Value("${auth.jwt-key}") private static String KEY; - public static final int ACCESS_TOKEN_EXP = 60 * 60; + public static final int ACCESS_TOKEN_EXP = 60 * 60 * 24 * 365; // todo 만료 기간 임시로 1년 설정해 둠. 추후 변경 필요 public static final int REFRESH_TOKEN_EXP = 60 * 60 * 24 * 365; // todo 만료 기간 임시로 1년 설정해 둠. 추후 변경 필요 public static final String ACCESS_TOKEN_NAME = "accessToken"; public static final String REFRESH_TOKEN_NAME = "refreshToken"; From b2f482cf8b85746524c6081d8bbf1564794619be Mon Sep 17 00:00:00 2001 From: JongKeun Kim Date: Sun, 25 May 2025 19:56:28 +0900 Subject: [PATCH 16/35] =?UTF-8?q?add:=20401,=20403=20=ED=95=B8=EB=93=A4?= =?UTF-8?q?=EB=9F=AC=EC=97=90=EC=84=9C=20response=20=EA=B0=9D=EC=B2=B4?= =?UTF-8?q?=EC=97=90=20cors=20=EA=B4=80=EB=A0=A8=20=ED=97=A4=EB=8D=94=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=ED=95=98=EB=8F=84=EB=A1=9D=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EB=B3=80=EA=B2=BD=20(#136)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../climingoApi/global/config/SecurityConfig.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/main/java/com/climingo/climingoApi/global/config/SecurityConfig.java b/src/main/java/com/climingo/climingoApi/global/config/SecurityConfig.java index c39c24c..62b3d61 100644 --- a/src/main/java/com/climingo/climingoApi/global/config/SecurityConfig.java +++ b/src/main/java/com/climingo/climingoApi/global/config/SecurityConfig.java @@ -5,6 +5,9 @@ import com.climingo.climingoApi.global.auth.JwtAuthenticationFilter; import com.fasterxml.jackson.databind.ObjectMapper; import java.io.PrintWriter; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Bean; @@ -73,6 +76,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { "Spring security unauthorized..."); response.setStatus(HttpStatus.UNAUTHORIZED.value()); String json = new ObjectMapper().writeValueAsString(fail); + setCorsHeaders(request, response); response.setContentType(MediaType.APPLICATION_JSON_VALUE); response.setCharacterEncoding("UTF-8"); PrintWriter writer = response.getWriter(); @@ -87,6 +91,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { "Spring security forbidden..."); response.setStatus(HttpStatus.FORBIDDEN.value()); String json = new ObjectMapper().writeValueAsString(fail); + setCorsHeaders(request, response); response.setContentType(MediaType.APPLICATION_JSON_VALUE); response.setCharacterEncoding("UTF-8"); PrintWriter writer = response.getWriter(); @@ -94,6 +99,11 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { writer.flush(); }; + private void setCorsHeaders(HttpServletRequest request, HttpServletResponse response) { + response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin")); + response.setHeader("Access-Control-Allow-Credentials", "true"); + } + public record ErrorResponse(HttpStatus status, String message) { } } From 1c0ba0f4cc1ba4bfb7814b55fd931692c6147f4f Mon Sep 17 00:00:00 2001 From: JongKeun Kim Date: Tue, 1 Jul 2025 22:38:34 +0900 Subject: [PATCH 17/35] =?UTF-8?q?[#138]=20Record(=EA=B8=B0=EB=A1=9D)=20?= =?UTF-8?q?=ED=85=8C=EC=9D=B4=EB=B8=94=EC=97=90=20=EB=93=B1=EB=B0=98?= =?UTF-8?q?=EB=82=A0=EC=A7=9C(climbDate)=20=EC=B6=94=EA=B0=80=20=EB=B0=8F?= =?UTF-8?q?=20api=20=EC=88=98=EC=A0=95=20(#139)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: record 테이블 및 엔터티에 climb_date 추가 * feat: record 생성 api 요청 필드에 climbDate 추가 * feat: record 조회 api 응답 필드에 climbDate 추가 * fix: front 개발 후 not null 처리되도록 주석 처리 * feat: record 수정 api 응답 필드에 climbDate 추가 --- .../api/request/RecordCreateRequest.java | 8 +- .../api/request/RecordUpdateRequest.java | 8 +- .../record/api/response/MyRecordResponse.java | 2 +- .../record/api/response/RecordResponse.java | 2 +- .../api/response/ShortRecordResponse.java | 24 ++++-- .../record/application/RecordService.java | 6 ++ .../climingoApi/record/domain/Record.java | 16 +++- src/main/resources/data.sql | 84 +++++++++---------- .../record/application/RecordServiceTest.java | 11 ++- 9 files changed, 106 insertions(+), 55 deletions(-) diff --git a/src/main/java/com/climingo/climingoApi/record/api/request/RecordCreateRequest.java b/src/main/java/com/climingo/climingoApi/record/api/request/RecordCreateRequest.java index 416aa96..7685f83 100644 --- a/src/main/java/com/climingo/climingoApi/record/api/request/RecordCreateRequest.java +++ b/src/main/java/com/climingo/climingoApi/record/api/request/RecordCreateRequest.java @@ -6,6 +6,8 @@ import lombok.Setter; import org.springframework.web.multipart.MultipartFile; +import java.time.LocalDate; + @Setter @Getter @NoArgsConstructor @@ -20,10 +22,14 @@ public class RecordCreateRequest { @NotNull private String videoUrl; - public RecordCreateRequest(Long gymId, Long levelId, String videoUrl) { +// @NotNull todo: front 개발 완료 후 not null 처리 예정 + private LocalDate climbDate; + + public RecordCreateRequest(Long gymId, Long levelId, String videoUrl, LocalDate climbDate) { this.gymId = gymId; this.levelId = levelId; this.videoUrl = videoUrl; + this.climbDate = climbDate; } } diff --git a/src/main/java/com/climingo/climingoApi/record/api/request/RecordUpdateRequest.java b/src/main/java/com/climingo/climingoApi/record/api/request/RecordUpdateRequest.java index 3e9a0e3..57e6ac6 100644 --- a/src/main/java/com/climingo/climingoApi/record/api/request/RecordUpdateRequest.java +++ b/src/main/java/com/climingo/climingoApi/record/api/request/RecordUpdateRequest.java @@ -6,6 +6,8 @@ import lombok.Setter; import org.springframework.web.multipart.MultipartFile; +import java.time.LocalDate; + @Setter @Getter @NoArgsConstructor @@ -20,10 +22,14 @@ public class RecordUpdateRequest { @NotNull private MultipartFile video; - public RecordUpdateRequest(Long gymId, Long levelId, MultipartFile video) { + // @NotNull todo: front 개발 완료 후 not null 처리 예정 + private LocalDate climbDate; + + public RecordUpdateRequest(Long gymId, Long levelId, MultipartFile video, LocalDate climbDate) { this.gymId = gymId; this.levelId = levelId; this.video = video; + this.climbDate = climbDate; } } diff --git a/src/main/java/com/climingo/climingoApi/record/api/response/MyRecordResponse.java b/src/main/java/com/climingo/climingoApi/record/api/response/MyRecordResponse.java index 72d7713..06e785e 100644 --- a/src/main/java/com/climingo/climingoApi/record/api/response/MyRecordResponse.java +++ b/src/main/java/com/climingo/climingoApi/record/api/response/MyRecordResponse.java @@ -17,7 +17,7 @@ public class MyRecordResponse { @Builder public MyRecordResponse(Record record, Gym gym, Level level) { - this.record = new ShortRecordResponse(record.getId(), record.getVideoUrl(), record.getThumbnailUrl(), record.getCreatedDate()); + this.record = ShortRecordResponse.from(record); this.gym = new ShortGymResponse(gym); this.level = new ShortLevelResponse(level); } diff --git a/src/main/java/com/climingo/climingoApi/record/api/response/RecordResponse.java b/src/main/java/com/climingo/climingoApi/record/api/response/RecordResponse.java index 41d2e95..7421a5e 100644 --- a/src/main/java/com/climingo/climingoApi/record/api/response/RecordResponse.java +++ b/src/main/java/com/climingo/climingoApi/record/api/response/RecordResponse.java @@ -27,7 +27,7 @@ public class RecordResponse { public RecordResponse(Member requestMember, Record record) { Member recordMember = record.getMember(); this.memberInfo = new ShortMemberResponse(recordMember.getId(), recordMember.getProfileUrl(), recordMember.getNickname()); - this.record = new ShortRecordResponse(record.getId(), record.getVideoUrl(), record.getThumbnailUrl(), record.getCreatedDate()); + this.record = ShortRecordResponse.from(record); this.gym = new ShortGymResponse(record.getGym()); this.level = new ShortLevelResponse(record.getLevel()); this.editable = requestMember.isSameMember(recordMember) || requestMember.isAdmin(); diff --git a/src/main/java/com/climingo/climingoApi/record/api/response/ShortRecordResponse.java b/src/main/java/com/climingo/climingoApi/record/api/response/ShortRecordResponse.java index 0b72e9c..443d35f 100644 --- a/src/main/java/com/climingo/climingoApi/record/api/response/ShortRecordResponse.java +++ b/src/main/java/com/climingo/climingoApi/record/api/response/ShortRecordResponse.java @@ -1,25 +1,39 @@ package com.climingo.climingoApi.record.api.response; +import java.time.LocalDate; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; + +import com.climingo.climingoApi.record.domain.Record; import lombok.Getter; @Getter public class ShortRecordResponse { - private Long recordId; + private final Long recordId; + + private final String videoUrl; - private String videoUrl; + private final String thumbnailUrl; - private String thumbnailUrl; + private final String createTime; - private String createTime; + private final String climbDate; - public ShortRecordResponse(Long recordId, String videoUrl, String thumbnailUrl, LocalDateTime createTime) { + public ShortRecordResponse(Long recordId, String videoUrl, String thumbnailUrl, LocalDateTime createTime, LocalDate climbDate) { this.recordId = recordId; this.videoUrl = videoUrl; this.thumbnailUrl = thumbnailUrl; this.createTime = createTime.format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")); + this.climbDate = createTime.format(DateTimeFormatter.ofPattern("yyyyMMdd")); } + public static ShortRecordResponse from(Record record) { + return new ShortRecordResponse( + record.getId(), + record.getVideoUrl(), + record.getThumbnailUrl(), + record.getCreatedDate(), + record.getClimbDate()); + } } diff --git a/src/main/java/com/climingo/climingoApi/record/application/RecordService.java b/src/main/java/com/climingo/climingoApi/record/application/RecordService.java index d7065c3..4b75df0 100644 --- a/src/main/java/com/climingo/climingoApi/record/application/RecordService.java +++ b/src/main/java/com/climingo/climingoApi/record/application/RecordService.java @@ -16,6 +16,8 @@ import com.climingo.climingoApi.upload.S3Service; import com.climingo.climingoApi.upload.ThumbnailExtractor; import jakarta.persistence.EntityNotFoundException; + +import java.time.LocalDate; import java.util.ArrayList; import java.util.List; import java.util.NoSuchElementException; @@ -46,6 +48,8 @@ public Record createRecord(Member loginMember, RecordCreateRequest request) { .orElseThrow(() -> new EntityNotFoundException(request.getLevelId() + "is not found")); String videoUrl = request.getVideoUrl(); + LocalDate climbedDate = request.getClimbDate() == null ? LocalDate.now(): request.getClimbDate(); + String thumbnailImageUrl = ""; try { thumbnailImageUrl = s3Service.uploadThumbnailImageFile(thumbnailExtractor.extractImage(videoUrl)); @@ -61,6 +65,7 @@ public Record createRecord(Member loginMember, RecordCreateRequest request) { .content(null) .videoUrl(videoUrl) .thumbnailUrl(thumbnailImageUrl) + .climbDate(climbedDate) .build(); return recordRepository.save(record); @@ -83,6 +88,7 @@ public Record updateRecord(Member member, Long recordId, RecordUpdateRequest req // TODO: origin 영상 데이터와 updated 영상 데이터가 다른걸 어떻게 알 것인가? record.update(gym, level, null); + record.updateClimbDate(request.getClimbDate()); return record; } diff --git a/src/main/java/com/climingo/climingoApi/record/domain/Record.java b/src/main/java/com/climingo/climingoApi/record/domain/Record.java index 2fb71c5..3e899c4 100644 --- a/src/main/java/com/climingo/climingoApi/record/domain/Record.java +++ b/src/main/java/com/climingo/climingoApi/record/domain/Record.java @@ -18,6 +18,8 @@ import lombok.NoArgsConstructor; import lombok.Setter; +import java.time.LocalDate; + @Setter @Getter @Entity @@ -46,12 +48,15 @@ public class Record extends BaseTimeEntity { @Column(nullable = false, length = 400) private String thumbnailUrl; + @Column(nullable = false) + private LocalDate climbDate; + private String content; private boolean blocked; @Builder - public Record(Long id, Member member, Level level, Gym gym, String videoUrl, String thumbnailUrl, String content) { + public Record(Long id, Member member, Level level, Gym gym, String videoUrl, String thumbnailUrl, String content, LocalDate climbDate) { this.id = id; this.member = member; this.level = level; @@ -59,6 +64,7 @@ public Record(Long id, Member member, Level level, Gym gym, String videoUrl, Str this.videoUrl = videoUrl; this.thumbnailUrl = thumbnailUrl; this.content = content; + this.climbDate = climbDate; this.blocked = false; } @@ -83,4 +89,12 @@ public boolean isEditable(Member member) { public void block() { this.blocked = true; } + + public void updateClimbDate(LocalDate climbedDate) { + if (climbedDate != null) { + this.climbDate = climbedDate; + return; + } + this.climbDate = LocalDate.now(); + } } diff --git a/src/main/resources/data.sql b/src/main/resources/data.sql index b8322a6..b852705 100644 --- a/src/main/resources/data.sql +++ b/src/main/resources/data.sql @@ -42,46 +42,46 @@ insert into member (id, auth_id, provider_type, nickname, profile_url, email, ho insert into auth_token(member_id, access_token, refresh_token, created_date, modified_date) values (9999, 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJtZW1iZXJJZCI6OTk5OSwiYXV0aElkIjoidGVzdF9hdXRoX2lkXzk5OTkiLCJwcm92aWRlclR5cGUiOiJrYWthbyIsIm5pY2tuYW1lIjoia2FrYW8iLCJyb2xlIjoiVVNFUiIsImV4cCI6MTgyOTA2NDA3NH0.9BT0LZZNs7zP-yqyRU6EKp57Xz3GFflbyB_ABpF1I-A', 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJtZW1iZXJJZCI6OTk5OSwiYXV0aElkIjoidGVzdF9hdXRoX2lkXzk5OTkiLCJwcm92aWRlclR5cGUiOiJrYWthbyIsIm5pY2tuYW1lIjoia2FrYW8iLCJyb2xlIjoiVVNFUiIsImV4cCI6MTgyOTA2NDA3NH0.9BT0LZZNs7zP-yqyRU6EKp57Xz3GFflbyB_ABpF1I-A', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP()); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(1, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(2, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(3, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(4, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(5, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(6, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(7, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(8, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(9, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(10, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(11, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(12, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(13, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(14, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(15, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(16, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(17, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(18, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(19, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(20, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(21, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(22, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(23, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(24, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(25, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(26, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(27, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(28, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(29, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(30, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(31, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(32, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(33, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(34, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(35, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(36, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(37, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(38, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(39, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(40, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(41, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); -insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked) values(42, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked, climb_date) values(1, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false, now()); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked, climb_date) values(2, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false, now()); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked, climb_date) values(3, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false, now()); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked, climb_date) values(4, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false, now()); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked, climb_date) values(5, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false, now()); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked, climb_date) values(6, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false, now()); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked, climb_date) values(7, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false, now()); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked, climb_date) values(8, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false, now()); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked, climb_date) values(9, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false, now()); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked, climb_date) values(10, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false, now()); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked, climb_date) values(11, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false, now()); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked, climb_date) values(12, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false, now()); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked, climb_date) values(13, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false, now()); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked, climb_date) values(14, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false, now()); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked, climb_date) values(15, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false, now()); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked, climb_date) values(16, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false, now()); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked, climb_date) values(17, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false, now()); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked, climb_date) values(18, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false, now()); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked, climb_date) values(19, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false, now()); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked, climb_date) values(20, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false, now()); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked, climb_date) values(21, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false, now()); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked, climb_date) values(22, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false, now()); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked, climb_date) values(23, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false, now()); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked, climb_date) values(24, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false, now()); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked, climb_date) values(25, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false, now()); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked, climb_date) values(26, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false, now()); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked, climb_date) values(27, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false, now()); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked, climb_date) values(28, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false, now()); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked, climb_date) values(29, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false, now()); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked, climb_date) values(30, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false, now()); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked, climb_date) values(31, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false, now()); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked, climb_date) values(32, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false, now()); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked, climb_date) values(33, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false, now()); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked, climb_date) values(34, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false, now()); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked, climb_date) values(35, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false, now()); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked, climb_date) values(36, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false, now()); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked, climb_date) values(37, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false, now()); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked, climb_date) values(38, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false, now()); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked, climb_date) values(39, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false, now()); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked, climb_date) values(40, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false, now()); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked, climb_date) values(41, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false, now()); +insert into record(id, member_id, level_id, gym_id, video_url, thumbnail_url, content, created_date, modified_date, blocked, climb_date) values(42, 9999, 1, 1, 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EB%B9%84%EB%94%94%EC%98%A4_2024-05-16T12%3A40%3A26.647632061.MOV', 'https://climingo-api.s3.ap-northeast-2.amazonaws.com/%EC%8D%B8%EB%84%A4%EC%9D%BC_2024-05-16T21%3A35%3A57.247394.jpg', 'test_content', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false, now()); diff --git a/src/test/java/com/climingo/climingoApi/record/application/RecordServiceTest.java b/src/test/java/com/climingo/climingoApi/record/application/RecordServiceTest.java index 1961f24..580c5f6 100644 --- a/src/test/java/com/climingo/climingoApi/record/application/RecordServiceTest.java +++ b/src/test/java/com/climingo/climingoApi/record/application/RecordServiceTest.java @@ -23,6 +23,7 @@ import com.climingo.climingoApi.upload.ThumbnailExtractor; import java.io.File; import java.io.IOException; +import java.time.LocalDate; import java.util.NoSuchElementException; import java.util.Optional; import org.junit.jupiter.api.BeforeEach; @@ -65,12 +66,13 @@ void create_test() throws IOException { .id(99999L) .role(UserRole.USER) .build(); - RecordCreateRequest request = new RecordCreateRequest(mockGymId, mockLevelId, mockVideoUrl); + RecordCreateRequest request = new RecordCreateRequest(mockGymId, mockLevelId, mockVideoUrl, LocalDate.now()); Record expected = Record.builder() .member(loginMember) .videoUrl("http://mock-video-url") .thumbnailUrl("http://mock-thumbnail-url") + .climbDate(request.getClimbDate()) .build(); when(gymRepository.findById(anyLong())).thenReturn(Optional.of(mock(Gym.class))); @@ -82,6 +84,7 @@ void create_test() throws IOException { assertEquals(expected.getMember(), actual.getMember()); assertEquals(expected.getVideoUrl(), actual.getVideoUrl()); assertEquals(expected.getThumbnailUrl(), actual.getThumbnailUrl()); + assertEquals(expected.getClimbDate(), actual.getClimbDate()); } @Test @@ -118,6 +121,7 @@ void update_test() { .level(level2) .videoUrl("http://mock-video-url") .thumbnailUrl("http://mock-thumbnail-url") + .climbDate(LocalDate.now()) .build(); when(gymRepository.findById(1L)).thenReturn(Optional.of(gym1)); @@ -131,12 +135,13 @@ void update_test() { Long updatedLevelId = 2L; MultipartFile mockVideo = mock(MultipartFile.class); - RecordUpdateRequest request = new RecordUpdateRequest(updatedGymId, updatedLevelId, mockVideo); + RecordUpdateRequest request = new RecordUpdateRequest(updatedGymId, updatedLevelId, mockVideo, LocalDate.now()); Record actual = recordService.updateRecord(loginMember, 1L, request); assertEquals(expected.getMember(), actual.getMember()); assertEquals(2L, actual.getGym().getId()); assertEquals(2L, actual.getGym().getId()); + assertEquals(expected.getClimbDate(), actual.getClimbDate()); } @Test @@ -177,7 +182,7 @@ void update_test_when_not_same_member_then_throw_exception() { Long updatedLevelId = 2L; MultipartFile mockVideo = mock(MultipartFile.class); - RecordUpdateRequest request = new RecordUpdateRequest(updatedGymId, updatedLevelId, mockVideo); + RecordUpdateRequest request = new RecordUpdateRequest(updatedGymId, updatedLevelId, mockVideo, LocalDate.now()); Throwable exception = assertThrows(ForbiddenException.class, () -> recordService.updateRecord(loginMember, 1L, request)); From c282d3c683e6c2b7b12c51660cf010abb2194838 Mon Sep 17 00:00:00 2001 From: "David.Han" <51263415+abovenormal@users.noreply.github.com> Date: Sun, 10 Aug 2025 15:14:05 +0900 Subject: [PATCH 18/35] Feat/137 applying swagger api (#140) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: swaggerConfig 파일 생성 * Refactor: swagger 설정 추가 * Refactor: swagger 설정 적용 --------- Co-authored-by: sunghyuki <62830487+sunghyuki@users.noreply.github.com> Co-authored-by: JongKeun Kim --- build.gradle | 3 ++ .../climingoApi/auth/api/AuthController.java | 25 ++++++++++++ .../auth/api/request/SignInRequest.java | 3 ++ .../climingoApi/gym/api/GymController.java | 10 +++++ .../api/HealthCheckController.java | 3 ++ .../member/api/MemberController.java | 10 +++++ .../record/api/RecordController.java | 27 +++++++++++++ .../climingoApi/swagger/SwaggerConfig.java | 40 +++++++++++++++++++ .../climingoApi/upload/api/S3Controller.java | 7 ++++ src/main/resources/application.yml | 24 ++++++++++- 10 files changed, 151 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/climingo/climingoApi/swagger/SwaggerConfig.java diff --git a/build.gradle b/build.gradle index 21b0e02..01e2619 100644 --- a/build.gradle +++ b/build.gradle @@ -38,6 +38,9 @@ dependencies { implementation 'com.amazonaws:aws-java-sdk-s3:1.12.701' implementation 'net.bramp.ffmpeg:ffmpeg:0.7.0' + // Swagger UI + implementation "org.springdoc:springdoc-openapi-starter-webmvc-ui:2.6.0" + // 모니터링 implementation 'org.springframework.boot:spring-boot-starter-actuator' implementation 'io.micrometer:micrometer-registry-prometheus' diff --git a/src/main/java/com/climingo/climingoApi/auth/api/AuthController.java b/src/main/java/com/climingo/climingoApi/auth/api/AuthController.java index 5961782..3c2f61c 100644 --- a/src/main/java/com/climingo/climingoApi/auth/api/AuthController.java +++ b/src/main/java/com/climingo/climingoApi/auth/api/AuthController.java @@ -13,6 +13,13 @@ import com.climingo.climingoApi.auth.util.JwtUtil; import com.climingo.climingoApi.global.auth.RequestMember; import com.climingo.climingoApi.member.domain.Member; +import feign.Param; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import jakarta.validation.Valid; @@ -28,6 +35,7 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +@Tag(name="AuthController", description = "회원가입, 로그인 관련 api") @RestController @RequiredArgsConstructor @Validated @@ -37,6 +45,9 @@ public class AuthController { private final OAuth2ClientManager oAuth2ClientManager; @PostMapping("/sign-in") + @Operation(summary = "로그인", description = "로그인을 진행합니다.") + @Parameter(name="providerType", description = "provider 타입") + @Parameter(name="providerToken", description = "provider 유저 정보 조회용 Token") public ResponseEntity signIn( @RequestBody @Valid SignInRequest requestBody, HttpServletResponse response) { @@ -55,6 +66,14 @@ public ResponseEntity signIn( } @PostMapping("/sign-up") + @Operation(summary="회원가입", description = "회원가입을 진행합니다.") + @Parameter(name="providerType", description = "provider 타입") + @Parameter(name="authId", description = "provider에서 제공 받은 유저식별자") + @Parameter(name="nickname", description = "provider에서 제공 받은 닉네임 혹은 직접 입력") + @Parameter(name="profileImage", description = "provider에서 제공 받은 profile image url 혹은 직접 입력") + @Parameter(name="physicalInfo", description = "유저 신체정보(키, 몸무게, 암리치)") + @Parameter(name="homeGymId", description = "홈 짐 id") + public ResponseEntity signUp( @RequestBody @Valid final SignUpRequest requestBody, HttpServletResponse response) { @@ -73,6 +92,10 @@ public ResponseEntity signUp( } @GetMapping("/auth/members/exist") + @Operation(summary="회원등록 여부 확인", description = "회원등록을 확인합니다.") + @Parameter(name="providerType", description = "provider 타입") + @Parameter(name="code", description = "provider로부터 받은 code") + @Parameter(name="redirectUri", description = "provider 앱에 등록해둔 redirect uri") public ResponseEntity checkMemberEnrolled( @RequestParam("providerType") @Pattern(regexp = "^(kakao|apple)$", message = "providerType은 kakao와 apple만 유효합니다.") String providerType, @RequestParam("code") @NotNull String code, @@ -89,6 +112,7 @@ public ResponseEntity checkMemberEnrolled( } @DeleteMapping("/sign-out") + @Operation(summary = "로그아웃", description = "로그아웃을 진행합니다.") public ResponseEntity logout(HttpServletRequest request, HttpServletResponse response) { CookieUtils.deleteCookie(request, response, JwtUtil.ACCESS_TOKEN_NAME); CookieUtils.deleteCookie(request, response, JwtUtil.REFRESH_TOKEN_NAME); @@ -97,6 +121,7 @@ public ResponseEntity logout(HttpServletRequest request, HttpServletRespon } @DeleteMapping("/delete-member") + @Operation(summary="회원 탈퇴", description = "회원 탈퇴를 진행합니다.") public ResponseEntity deleteMember( @RequestMember Member member, HttpServletRequest request, HttpServletResponse response) { diff --git a/src/main/java/com/climingo/climingoApi/auth/api/request/SignInRequest.java b/src/main/java/com/climingo/climingoApi/auth/api/request/SignInRequest.java index e9c7ec4..839dee4 100644 --- a/src/main/java/com/climingo/climingoApi/auth/api/request/SignInRequest.java +++ b/src/main/java/com/climingo/climingoApi/auth/api/request/SignInRequest.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Pattern; @@ -16,9 +17,11 @@ public class SignInRequest { @JsonProperty("providerType") @Pattern(regexp = "^(kakao|apple)$", message = "providerType은 kakao와 apple만 유효합니다.") @NotNull + @Schema(description ="provider 타입",example = "kakao or apple", required = true) private final String providerType; @JsonProperty("providerToken") @NotNull + @Schema(description = "provider 유저 정보 조회용 토큰", example = "xxxxxx") private final String providerToken; } \ No newline at end of file diff --git a/src/main/java/com/climingo/climingoApi/gym/api/GymController.java b/src/main/java/com/climingo/climingoApi/gym/api/GymController.java index 618d17d..3c6a3ea 100644 --- a/src/main/java/com/climingo/climingoApi/gym/api/GymController.java +++ b/src/main/java/com/climingo/climingoApi/gym/api/GymController.java @@ -4,6 +4,11 @@ import com.climingo.climingoApi.gym.api.response.GymSearchResponse; import com.climingo.climingoApi.gym.application.GymService; import java.util.List; + +import feign.Param; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; @@ -11,6 +16,7 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +@Tag(name="GymController", description = "클라이밍장 장소, 난이도 조회 api") @RequiredArgsConstructor @RestController public class GymController { @@ -18,12 +24,16 @@ public class GymController { private final GymService gymService; @GetMapping("/search/gyms") + @Operation(summary="클라이밍장 검색", description = "클라이밍장 이름을 검색합니다.") + @Parameter(name = "keyword" , description = "클라이밍장 이름") public ResponseEntity> search(@RequestParam("keyword") String keyword) { List response = gymService.search(keyword); return ResponseEntity.ok().body(response); } @GetMapping("/gyms/{gymId}/levels") + @Operation(summary="클라이밍장 난이도 목록 조회", description = "") + @Parameter(name = "gymId", description = "클라이밍장 아이디") public ResponseEntity> findLevelList(@PathVariable Long gymId) { List response = gymService.findLevelList(gymId); return ResponseEntity.ok().body(response); diff --git a/src/main/java/com/climingo/climingoApi/healthCheck/api/HealthCheckController.java b/src/main/java/com/climingo/climingoApi/healthCheck/api/HealthCheckController.java index 99b4a68..3834726 100644 --- a/src/main/java/com/climingo/climingoApi/healthCheck/api/HealthCheckController.java +++ b/src/main/java/com/climingo/climingoApi/healthCheck/api/HealthCheckController.java @@ -2,6 +2,8 @@ import java.util.HashMap; import java.util.Map; + +import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpHeaders; @@ -11,6 +13,7 @@ import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; +@Tag(name="HealthCheckController", description = "관련 api") @RestController @RequiredArgsConstructor public class HealthCheckController { diff --git a/src/main/java/com/climingo/climingoApi/member/api/MemberController.java b/src/main/java/com/climingo/climingoApi/member/api/MemberController.java index d12743d..22ee028 100644 --- a/src/main/java/com/climingo/climingoApi/member/api/MemberController.java +++ b/src/main/java/com/climingo/climingoApi/member/api/MemberController.java @@ -5,6 +5,9 @@ import com.climingo.climingoApi.member.api.response.MemberInfoResponse; import com.climingo.climingoApi.member.application.MemberService; import com.climingo.climingoApi.member.domain.Member; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; @@ -14,6 +17,7 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; +@Tag(name="MemberController", description = "회원 정보 조회 및 수정 api") @RequiredArgsConstructor @RestController public class MemberController { @@ -21,18 +25,24 @@ public class MemberController { private final MemberService memberService; @GetMapping("/members") + @Operation(summary = "내 정보 조회", description = "사용자의 정보를 조회합니다.") + @Parameter(name = "memberId", description = "member id 를 토큰으로 부터 추출함") public ResponseEntity findMyInfo(@RequestMember Member loginMember) { MemberInfoResponse memberInfoResponse = memberService.findMemberInfo(loginMember.getId()); return ResponseEntity.ok().body(memberInfoResponse); } @GetMapping("/members/{memberId}") + @Operation(summary = "멤버 정보 상세 조회", description = "사용자 정보 상세 조회 합니다.") + @Parameter(name = "memberId", description = "멤버 아이디") public ResponseEntity findMemberInfo(@PathVariable(value = "memberId") Long memberId) { MemberInfoResponse memberInfoResponse = memberService.findMemberInfo(memberId); return ResponseEntity.ok().body(memberInfoResponse); } @PatchMapping("/members/{memberId}/nickname") + @Operation(summary = "멤버 닉네임 변경", description = "회원정보 닉네임 변경") + @Parameter(name = "nickname", description = "변경할 닉네임") public ResponseEntity updateNickname(@RequestMember Member member, @PathVariable(value = "memberId") Long memberId, @RequestBody @Valid UpdateNicknameRequest request) { memberService.updateNickname(member, memberId, request.getNickname()); diff --git a/src/main/java/com/climingo/climingoApi/record/api/RecordController.java b/src/main/java/com/climingo/climingoApi/record/api/RecordController.java index b56eeb3..f18f8ac 100644 --- a/src/main/java/com/climingo/climingoApi/record/api/RecordController.java +++ b/src/main/java/com/climingo/climingoApi/record/api/RecordController.java @@ -9,9 +9,14 @@ import com.climingo.climingoApi.record.api.response.RecordResponse; import com.climingo.climingoApi.record.application.RecordService; import com.climingo.climingoApi.record.domain.Record; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.constraints.Min; + import java.io.IOException; import java.util.Map; + import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; @@ -24,6 +29,7 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +@Tag(name="RecordController", description = "영상 기록 api") @RequiredArgsConstructor @RestController public class RecordController { @@ -31,6 +37,10 @@ public class RecordController { private final RecordService recordService; @PostMapping("/records") + @Operation(summary = "기록 생성", description = "사용자가 기록을 작성합니다.") + @Parameter(name = "gymId", description = "클라이밍장 id") + @Parameter(name = "leveId", description = "난이도 id") + @Parameter(name = "videoUrl", description = "영상 url") public ResponseEntity> create( @RequestMember Member member, @RequestBody RecordCreateRequest request) throws IOException { @@ -40,6 +50,10 @@ public ResponseEntity> create( } @PatchMapping("/records/{recordId}") + @Operation(summary = "기록 수정", description = "게시된 기록 수정합니다.(추후 수정할 필드가 생기면 추가예정)") + @Parameter(name = "gymId", description = "클라이밍장 id") + @Parameter(name = "levelId", description = "난이도 id") + @Parameter(name = "video", description = "영상") public ResponseEntity update( @RequestMember Member member, @PathVariable("recordId") Long recordId, @@ -50,6 +64,8 @@ public ResponseEntity update( } @DeleteMapping("/records/{recordId}") + @Operation(summary = "기록 삭제", description = "기록을 삭제합니다.") + @Parameter(name = "recordId", description = "기록(게시글) Id") public ResponseEntity delete( @RequestMember Member member, @PathVariable("recordId") Long recordId) { @@ -58,6 +74,8 @@ public ResponseEntity delete( } @GetMapping("/records/{recordId}") + @Operation(summary = "기록 상세 조회", description = "1건의 기록을 조회합니다.") + @Parameter(name = "recordId", description = "클라이밍장 id") public ResponseEntity find( @RequestMember Member member, @PathVariable("recordId") Long recordId) { @@ -74,6 +92,12 @@ public ResponseEntity find( // } @GetMapping("/records") + @Operation(summary = "기록 목록 조회", description = "페이징 조회") + @Parameter(name = "gymId", description = "클라이밍장 id") + @Parameter(name = "levelId", description = "난이도 id") + @Parameter(name = "memberId", description = "멤버 id") + @Parameter(name = "page", description = "페이지(페이지는 0페이지부터 시작)") + @Parameter(name = "size", description = "한 페이지에 조회할 데이터 개수") public ResponseEntity> findPage(@RequestMember Member member, @RequestParam(value = "gymId", required = false) Long gymId, @RequestParam(value = "levelId", required = false) Long levelId, @@ -87,6 +111,9 @@ public ResponseEntity> findPage(@RequestMember Member me } @GetMapping("/myRecords") + @Operation(summary = "내 기록 목록 조회", description = "사용자의 기록들을 조회합니다.") + @Parameter(name = "page", description = "페이지(페이지는 0페이지부터 시작)") + @Parameter(name = "size", description = "한 페이지에 조회할 데이터 개수") public ResponseEntity> findPageMy(@RequestMember Member member, @RequestParam(value = "page", required = false, defaultValue = "0") @Min(0) Integer page, @RequestParam(value = "size", required = false, defaultValue = "10") @Min(1) Integer size) { diff --git a/src/main/java/com/climingo/climingoApi/swagger/SwaggerConfig.java b/src/main/java/com/climingo/climingoApi/swagger/SwaggerConfig.java new file mode 100644 index 0000000..cb35065 --- /dev/null +++ b/src/main/java/com/climingo/climingoApi/swagger/SwaggerConfig.java @@ -0,0 +1,40 @@ +package com.climingo.climingoApi.swagger; + +import io.swagger.v3.oas.models.Components; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.security.SecurityRequirement; +import io.swagger.v3.oas.models.security.SecurityScheme; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class SwaggerConfig { + @Bean + public OpenAPI openAPI() { + Info info = new Info() + .title("Climingo API Document") + .version("v1.0.0") + .description("클라밍고 API 명세서입니다."); + + + // SecuritySecheme명 + String jwtSchemeName = "jwtAuth"; + // API 요청헤더에 인증정보 포함 + SecurityRequirement securityRequirement = new SecurityRequirement().addList(jwtSchemeName); + // SecuritySchemes 등록 + Components components = new Components() + .addSecuritySchemes(jwtSchemeName, new SecurityScheme() + .name(jwtSchemeName) + .type(SecurityScheme.Type.HTTP) // HTTP 방식 + .scheme("bearer") + .bearerFormat("JWT")); // 토큰 형식을 지정하는 임의의 문자(Optional) + + + return new OpenAPI() + .info(info) + .addSecurityItem(securityRequirement) + .components(components); + } + +} \ No newline at end of file diff --git a/src/main/java/com/climingo/climingoApi/upload/api/S3Controller.java b/src/main/java/com/climingo/climingoApi/upload/api/S3Controller.java index 4c88532..650052f 100644 --- a/src/main/java/com/climingo/climingoApi/upload/api/S3Controller.java +++ b/src/main/java/com/climingo/climingoApi/upload/api/S3Controller.java @@ -3,12 +3,16 @@ import com.climingo.climingoApi.upload.S3Service; import com.climingo.climingoApi.upload.api.request.PresignedUrlCreateRequest; import com.climingo.climingoApi.upload.api.response.PresignedUrlResponse; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; +@Tag(name="S3Controller", description = "영상 업로드(S3) api") @RestController @RequiredArgsConstructor public class S3Controller { @@ -16,6 +20,9 @@ public class S3Controller { private final S3Service s3Service; @PostMapping("/s3/presigned-url") + @Operation(summary = "s3 presignedUrl 발행", description = "s3 presignedUrl 발행합니다.") + @Parameter(name="fileName", description = "파일 이름") + @Parameter(name="extension", description = "파일 확장자") public ResponseEntity generatePresignedUrl( @RequestBody PresignedUrlCreateRequest request) { String presignedUrl = s3Service.generatePresignedUrl(request).toString(); diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index e9c1ce6..458cead 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -17,6 +17,13 @@ jasypt: encryptor: password: ${JASYPT_PASSWORD} +logging: + level: + org: + hibernate: + type: + descriptor: + sql: trace cloud: aws: credentials: @@ -27,4 +34,19 @@ cloud: bucket: ENC(Qrwl8xXU7tVKxLFzJ/aA9AbOEN01/a/1) auth: - jwt-key: ENC(WOI2z7L6DbSzJX8Fu8sMJKXqcvxQ7drQOnlHp6OM9CE=) \ No newline at end of file + jwt-key: ENC(WOI2z7L6DbSzJX8Fu8sMJKXqcvxQ7drQOnlHp6OM9CE + +# Swagger springdoc-ui Configuration +springdoc: + packages-to-scan: com.climingo.climingoApi + default-consumes-media-type: application/json;charset=UTF-8 + default-produces-media-type: application/json;charset=UTF-8 + cache: + disabled: true # 캐시 사용 여부 + api-docs: + version: OPENAPI_3_1 + swagger-ui: + enabled: true # Swagger UI 사용여부 : 접근 경로 => + path: /swagger-ui.html # Swagger UI 추가 접근 경로 => + tags-sorter: alpha # alpha: 알파벳 순 태그 정렬, method: HTTP Method 순 정렬 + operations-sorter: method # alpha: 알파벳 순 태그 정렬, method: HTTP Method 순 정렬 From bd8bf764630b2403aef7205f08c36741257c0d5c Mon Sep 17 00:00:00 2001 From: JongKeun Kim Date: Tue, 26 Aug 2025 22:51:59 +0900 Subject: [PATCH 19/35] =?UTF-8?q?[#141]=20=EC=B0=8D=EB=B3=BC=20crd=20api?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84=20(#143)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 찍볼 도메인 코드 추가 * feat: 찍볼 생성 api 구현 * feat: 찍볼 단건 조회 api 구현 * feat: 찍볼 삭제 api 구현 --- .../jjikboul/api/JjikboulController.java | 48 ++++++++++++++ .../api/request/JjikboulCreateRequest.java | 12 ++++ .../api/response/JjikboulCreateResponse.java | 7 ++ .../application/JjikboulCreateUseCase.java | 8 +++ .../application/JjikboulDeleteUseCase.java | 7 ++ .../application/JjikboulQueryUseCase.java | 8 +++ .../jjikboul/application/JjikboulService.java | 66 +++++++++++++++++++ .../response/JjikboulResponse.java | 29 ++++++++ .../response/ShortJjikboulResponse.java | 19 ++++++ .../climingoApi/jjikboul/domain/Jjikboul.java | 64 ++++++++++++++++++ .../jjikboul/domain/JjikboulRepository.java | 8 +++ .../jjikboul/domain/ProblemType.java | 5 ++ 12 files changed, 281 insertions(+) create mode 100644 src/main/java/com/climingo/climingoApi/jjikboul/api/JjikboulController.java create mode 100644 src/main/java/com/climingo/climingoApi/jjikboul/api/request/JjikboulCreateRequest.java create mode 100644 src/main/java/com/climingo/climingoApi/jjikboul/api/response/JjikboulCreateResponse.java create mode 100644 src/main/java/com/climingo/climingoApi/jjikboul/application/JjikboulCreateUseCase.java create mode 100644 src/main/java/com/climingo/climingoApi/jjikboul/application/JjikboulDeleteUseCase.java create mode 100644 src/main/java/com/climingo/climingoApi/jjikboul/application/JjikboulQueryUseCase.java create mode 100644 src/main/java/com/climingo/climingoApi/jjikboul/application/JjikboulService.java create mode 100644 src/main/java/com/climingo/climingoApi/jjikboul/application/response/JjikboulResponse.java create mode 100644 src/main/java/com/climingo/climingoApi/jjikboul/application/response/ShortJjikboulResponse.java create mode 100644 src/main/java/com/climingo/climingoApi/jjikboul/domain/Jjikboul.java create mode 100644 src/main/java/com/climingo/climingoApi/jjikboul/domain/JjikboulRepository.java create mode 100644 src/main/java/com/climingo/climingoApi/jjikboul/domain/ProblemType.java diff --git a/src/main/java/com/climingo/climingoApi/jjikboul/api/JjikboulController.java b/src/main/java/com/climingo/climingoApi/jjikboul/api/JjikboulController.java new file mode 100644 index 0000000..f981b84 --- /dev/null +++ b/src/main/java/com/climingo/climingoApi/jjikboul/api/JjikboulController.java @@ -0,0 +1,48 @@ +package com.climingo.climingoApi.jjikboul.api; + +import com.climingo.climingoApi.global.auth.RequestMember; +import com.climingo.climingoApi.jjikboul.api.request.JjikboulCreateRequest; +import com.climingo.climingoApi.jjikboul.api.response.JjikboulCreateResponse; +import com.climingo.climingoApi.jjikboul.application.JjikboulCreateUseCase; +import com.climingo.climingoApi.jjikboul.application.JjikboulDeleteUseCase; +import com.climingo.climingoApi.jjikboul.application.JjikboulQueryUseCase; +import com.climingo.climingoApi.jjikboul.application.response.JjikboulResponse; +import com.climingo.climingoApi.member.domain.Member; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequiredArgsConstructor +public class JjikboulController { + + private final JjikboulCreateUseCase jjikboulCreateUseCase; + private final JjikboulQueryUseCase jjikboulQueryUseCase; + private final JjikboulDeleteUseCase jjikboulDeleteUseCase; + + @GetMapping("/jjikbouls/{jjikboulId}") + public ResponseEntity getJjikboul( + @RequestMember Member member, + @PathVariable("jjikboulId") Long customProblemId) { + JjikboulResponse jjikboulResponse = jjikboulQueryUseCase.readJjikboul(member, customProblemId); + return ResponseEntity.ok().body(jjikboulResponse); + } + + @PostMapping("/jjikbouls") + public ResponseEntity jjikboulsProblem( + @RequestMember Member member, + @RequestBody JjikboulCreateRequest request) { + + Long id = jjikboulCreateUseCase.create(member, request); + + return ResponseEntity.ok().body(JjikboulCreateResponse.of(id)); + } + + @DeleteMapping("/jjikbouls/{jjikboulId}") + public ResponseEntity deleteJjikboul( + @RequestMember Member member, + @PathVariable("jjikboulId") Long customProblemId) { + jjikboulDeleteUseCase.deleteJjikboul(member, customProblemId); + return ResponseEntity.ok().build(); + } +} diff --git a/src/main/java/com/climingo/climingoApi/jjikboul/api/request/JjikboulCreateRequest.java b/src/main/java/com/climingo/climingoApi/jjikboul/api/request/JjikboulCreateRequest.java new file mode 100644 index 0000000..9f713b1 --- /dev/null +++ b/src/main/java/com/climingo/climingoApi/jjikboul/api/request/JjikboulCreateRequest.java @@ -0,0 +1,12 @@ +package com.climingo.climingoApi.jjikboul.api.request; + +import com.climingo.climingoApi.jjikboul.domain.ProblemType; + +public record JjikboulCreateRequest( + Long gymId, + Long levelId, + String problemUrl, + ProblemType problemType, + String description +) { +} diff --git a/src/main/java/com/climingo/climingoApi/jjikboul/api/response/JjikboulCreateResponse.java b/src/main/java/com/climingo/climingoApi/jjikboul/api/response/JjikboulCreateResponse.java new file mode 100644 index 0000000..4da3bb2 --- /dev/null +++ b/src/main/java/com/climingo/climingoApi/jjikboul/api/response/JjikboulCreateResponse.java @@ -0,0 +1,7 @@ +package com.climingo.climingoApi.jjikboul.api.response; + +public record JjikboulCreateResponse(Long id) { + public static JjikboulCreateResponse of(Long id) { + return new JjikboulCreateResponse(id); + } +} diff --git a/src/main/java/com/climingo/climingoApi/jjikboul/application/JjikboulCreateUseCase.java b/src/main/java/com/climingo/climingoApi/jjikboul/application/JjikboulCreateUseCase.java new file mode 100644 index 0000000..119a971 --- /dev/null +++ b/src/main/java/com/climingo/climingoApi/jjikboul/application/JjikboulCreateUseCase.java @@ -0,0 +1,8 @@ +package com.climingo.climingoApi.jjikboul.application; + +import com.climingo.climingoApi.jjikboul.api.request.JjikboulCreateRequest; +import com.climingo.climingoApi.member.domain.Member; + +public interface JjikboulCreateUseCase { + Long create(Member member, JjikboulCreateRequest request); +} diff --git a/src/main/java/com/climingo/climingoApi/jjikboul/application/JjikboulDeleteUseCase.java b/src/main/java/com/climingo/climingoApi/jjikboul/application/JjikboulDeleteUseCase.java new file mode 100644 index 0000000..baf6733 --- /dev/null +++ b/src/main/java/com/climingo/climingoApi/jjikboul/application/JjikboulDeleteUseCase.java @@ -0,0 +1,7 @@ +package com.climingo.climingoApi.jjikboul.application; + +import com.climingo.climingoApi.member.domain.Member; + +public interface JjikboulDeleteUseCase { + void deleteJjikboul(Member requestMember, Long customProblemId); +} diff --git a/src/main/java/com/climingo/climingoApi/jjikboul/application/JjikboulQueryUseCase.java b/src/main/java/com/climingo/climingoApi/jjikboul/application/JjikboulQueryUseCase.java new file mode 100644 index 0000000..899b914 --- /dev/null +++ b/src/main/java/com/climingo/climingoApi/jjikboul/application/JjikboulQueryUseCase.java @@ -0,0 +1,8 @@ +package com.climingo.climingoApi.jjikboul.application; + +import com.climingo.climingoApi.jjikboul.application.response.JjikboulResponse; +import com.climingo.climingoApi.member.domain.Member; + +public interface JjikboulQueryUseCase { + JjikboulResponse readJjikboul(Member member, Long jjikboulId); +} diff --git a/src/main/java/com/climingo/climingoApi/jjikboul/application/JjikboulService.java b/src/main/java/com/climingo/climingoApi/jjikboul/application/JjikboulService.java new file mode 100644 index 0000000..8cd6eae --- /dev/null +++ b/src/main/java/com/climingo/climingoApi/jjikboul/application/JjikboulService.java @@ -0,0 +1,66 @@ +package com.climingo.climingoApi.jjikboul.application; + +import com.climingo.climingoApi.global.exception.ForbiddenException; +import com.climingo.climingoApi.gym.domain.Gym; +import com.climingo.climingoApi.gym.domain.GymRepository; +import com.climingo.climingoApi.jjikboul.api.request.JjikboulCreateRequest; +import com.climingo.climingoApi.jjikboul.application.response.JjikboulResponse; +import com.climingo.climingoApi.jjikboul.domain.Jjikboul; +import com.climingo.climingoApi.jjikboul.domain.JjikboulRepository; +import com.climingo.climingoApi.level.domain.Level; +import com.climingo.climingoApi.level.domain.LevelRepository; +import com.climingo.climingoApi.member.domain.Member; +import jakarta.persistence.EntityNotFoundException; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +public class JjikboulService implements JjikboulCreateUseCase, JjikboulDeleteUseCase, JjikboulQueryUseCase { + + private final GymRepository gymRepository; + private final LevelRepository levelRepository; + private final JjikboulRepository jjikboulRepository; + + @Transactional + @Override + public Long create(Member member, JjikboulCreateRequest request) { + Gym gym = gymRepository.findById(request.gymId()) + .orElseThrow(() -> new EntityNotFoundException(request.gymId() + "is not found")); + + Level level = levelRepository.findById(request.levelId()) + .orElseThrow(() -> new EntityNotFoundException(request.levelId() + "is not found")); + + Jjikboul jjikboul = Jjikboul.builder() + .gym(gym) + .level(level) + .member(member) + .problemType(request.problemType()) + .problemUrl(request.problemUrl()) + .description(request.description()) + .build(); + + return jjikboulRepository.save(jjikboul).getId(); + } + + @Transactional(readOnly = true) + public JjikboulResponse readJjikboul(Member member, Long customProblemId) { + Jjikboul jjikboul = jjikboulRepository.findById(customProblemId) + .orElseThrow(() -> new EntityNotFoundException(customProblemId + "is not found")); + return JjikboulResponse.of(member, jjikboul); + } + + @Transactional + @Override + public void deleteJjikboul(Member requestMember, Long jjikboulId) { + Jjikboul jjikboul = jjikboulRepository.findById(jjikboulId) + .orElseThrow(() -> new EntityNotFoundException("존재하지 않는 찍볼 문제입니다. requestId=" + jjikboulId)); + + if (!jjikboul.isEditable(requestMember)) { + throw new ForbiddenException("다른 사용자가 업로드한 찍볼은 삭제할 수 없음"); + } + + jjikboulRepository.delete(jjikboul); + } +} diff --git a/src/main/java/com/climingo/climingoApi/jjikboul/application/response/JjikboulResponse.java b/src/main/java/com/climingo/climingoApi/jjikboul/application/response/JjikboulResponse.java new file mode 100644 index 0000000..0a07d5a --- /dev/null +++ b/src/main/java/com/climingo/climingoApi/jjikboul/application/response/JjikboulResponse.java @@ -0,0 +1,29 @@ +package com.climingo.climingoApi.jjikboul.application.response; + +import com.climingo.climingoApi.jjikboul.domain.Jjikboul; +import com.climingo.climingoApi.member.domain.Member; +import com.climingo.climingoApi.record.api.response.ShortGymResponse; +import com.climingo.climingoApi.record.api.response.ShortLevelResponse; +import com.climingo.climingoApi.record.api.response.ShortMemberResponse; +import com.fasterxml.jackson.annotation.JsonProperty; + +public record JjikboulResponse( + ShortMemberResponse memberInfo, + ShortJjikboulResponse jjikboul, + ShortGymResponse gym, + ShortLevelResponse level, + @JsonProperty("isEditable") boolean editable, + @JsonProperty("isDeletable") boolean deletable +) { + public static JjikboulResponse of(Member requestMember, Jjikboul jjikboul) { + Member jjikboulMember = jjikboul.getMember(); + return new JjikboulResponse( + new ShortMemberResponse(jjikboulMember.getId(), jjikboulMember.getProfileUrl(), jjikboulMember.getNickname()), + ShortJjikboulResponse.from(jjikboul), + new ShortGymResponse(jjikboul.getGym()), + new ShortLevelResponse(jjikboul.getLevel()), + requestMember.isSameMember(jjikboulMember) || requestMember.isAdmin(), + requestMember.isSameMember(jjikboulMember) || requestMember.isAdmin() + ); + } +} diff --git a/src/main/java/com/climingo/climingoApi/jjikboul/application/response/ShortJjikboulResponse.java b/src/main/java/com/climingo/climingoApi/jjikboul/application/response/ShortJjikboulResponse.java new file mode 100644 index 0000000..9d8cf91 --- /dev/null +++ b/src/main/java/com/climingo/climingoApi/jjikboul/application/response/ShortJjikboulResponse.java @@ -0,0 +1,19 @@ +package com.climingo.climingoApi.jjikboul.application.response; + +import com.climingo.climingoApi.jjikboul.domain.Jjikboul; +import com.climingo.climingoApi.jjikboul.domain.ProblemType; + +public record ShortJjikboulResponse( + Long jjikboulId, + String problemUrl, + ProblemType problemType, + String description +) { + public static ShortJjikboulResponse from(Jjikboul jjikboul) { + return new ShortJjikboulResponse( + jjikboul.getId(), + jjikboul.getProblemUrl(), + jjikboul.getProblemType(), + jjikboul.getDescription()); + } +} diff --git a/src/main/java/com/climingo/climingoApi/jjikboul/domain/Jjikboul.java b/src/main/java/com/climingo/climingoApi/jjikboul/domain/Jjikboul.java new file mode 100644 index 0000000..3033adf --- /dev/null +++ b/src/main/java/com/climingo/climingoApi/jjikboul/domain/Jjikboul.java @@ -0,0 +1,64 @@ +package com.climingo.climingoApi.jjikboul.domain; + +import com.climingo.climingoApi.global.domain.BaseTimeEntity; +import com.climingo.climingoApi.gym.domain.Gym; +import com.climingo.climingoApi.level.domain.Level; +import com.climingo.climingoApi.member.domain.Member; +import jakarta.persistence.*; +import lombok.*; + +@Setter +@Getter +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class Jjikboul extends BaseTimeEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "MEMBER_ID", nullable = false) + private Member member; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "LEVEL_ID", nullable = false) + private Level level; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "GYM_ID", nullable = false) + private Gym gym; + + @Column(nullable = false, length = 400) + private String problemUrl; + + @Enumerated(EnumType.STRING) + @Column(name = "PROBLEM_TYPE", nullable = false) + private ProblemType problemType; + + @Column(name = "DESCRIPTION") + private String description; + + @Column(name = "BLOCKED") + private boolean blocked; + + @Builder + public Jjikboul(Long id, Member member, Level level, Gym gym, String problemUrl, ProblemType problemType, String description, boolean blocked) { + this.id = id; + this.member = member; + this.level = level; + this.gym = gym; + this.problemUrl = problemUrl; + this.problemType = problemType; + this.description = description; + this.blocked = blocked; + } + + public boolean isEditable(Member member) { + return isSameMember(member) || member.isAdmin(); + } + + public boolean isSameMember(Member member) { + return this.member.isSameMember(member.getId()); + } +} diff --git a/src/main/java/com/climingo/climingoApi/jjikboul/domain/JjikboulRepository.java b/src/main/java/com/climingo/climingoApi/jjikboul/domain/JjikboulRepository.java new file mode 100644 index 0000000..6ef8aaf --- /dev/null +++ b/src/main/java/com/climingo/climingoApi/jjikboul/domain/JjikboulRepository.java @@ -0,0 +1,8 @@ +package com.climingo.climingoApi.jjikboul.domain; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface JjikboulRepository extends JpaRepository { +} diff --git a/src/main/java/com/climingo/climingoApi/jjikboul/domain/ProblemType.java b/src/main/java/com/climingo/climingoApi/jjikboul/domain/ProblemType.java new file mode 100644 index 0000000..12a2c60 --- /dev/null +++ b/src/main/java/com/climingo/climingoApi/jjikboul/domain/ProblemType.java @@ -0,0 +1,5 @@ +package com.climingo.climingoApi.jjikboul.domain; + +public enum ProblemType { + BOULDERING, ENDURANCE, ETC +} From 41a5a8d553d1c779d770478dff9e505b6b2f8ec0 Mon Sep 17 00:00:00 2001 From: JongKeun Kim Date: Tue, 2 Sep 2025 22:57:23 +0900 Subject: [PATCH 20/35] =?UTF-8?q?feat:=20=EC=9D=B8=ED=94=84=EB=9D=BC=20?= =?UTF-8?q?=EB=A7=88=EC=9D=B4=EA=B7=B8=EB=A0=88=EC=9D=B4=EC=85=98=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=9D=B8=ED=95=9C=20datasource=20url=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20(#149)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application-prod.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 379a240..ecabe96 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -1,7 +1,7 @@ spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver - url: ENC(JC3oNwU2JSyKxNYy8Dqrs92EOhvFX+spMRZICKwAn4gN+MSehEI5ouS2oQd1SxPp) + url: ENC(ELzhq0/YxyrRypjtoHUo/UOZ+qUg4Rh7KT9TGY1Ego8eUAYNyQE/KTQQpywom0Ydkh6dvYN8HWTUz7Yry72StmeVfgajBt7JQabvp4ULpJtXeQ3tgt29BQKV5I2q+iEn) username: ENC(/hDIlwiiOpd1p7Coppl9L4xHYDjkf9cj) password: ENC(Toy+1i51hX5nGADpusirW4loTpskgy4o) h2: From bf3454f330acfc5caf584a377797cd4e95524f42 Mon Sep 17 00:00:00 2001 From: "David.Han" <51263415+abovenormal@users.noreply.github.com> Date: Tue, 28 Oct 2025 21:41:53 +0900 Subject: [PATCH 21/35] Feature/142 search armspan cp (#147) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 기록 생성 및 수정 시 암리치(physicalinfO) 정보를 추가 할 수 있게 수정 * feat: test code 작성 진행중 * fix: test failed resolve --------- Co-authored-by: Bellroute --- .../record/application/RecordService.java | 57 ++++--- .../climingoApi/record/domain/Record.java | 20 +-- .../ClimingoApiApplicationTests.java | 2 - .../record/application/RecordServiceTest.java | 152 ++++++++++++++++++ 4 files changed, 193 insertions(+), 38 deletions(-) diff --git a/src/main/java/com/climingo/climingoApi/record/application/RecordService.java b/src/main/java/com/climingo/climingoApi/record/application/RecordService.java index 4b75df0..416c30b 100644 --- a/src/main/java/com/climingo/climingoApi/record/application/RecordService.java +++ b/src/main/java/com/climingo/climingoApi/record/application/RecordService.java @@ -6,6 +6,7 @@ import com.climingo.climingoApi.level.domain.Level; import com.climingo.climingoApi.level.domain.LevelRepository; import com.climingo.climingoApi.member.domain.Member; +import com.climingo.climingoApi.member.domain.PhysicalInfo; import com.climingo.climingoApi.record.api.request.RecordCreateRequest; import com.climingo.climingoApi.record.api.request.RecordUpdateRequest; import com.climingo.climingoApi.record.api.response.MyRecordResponse; @@ -22,6 +23,7 @@ import java.util.List; import java.util.NoSuchElementException; import java.util.stream.Collectors; + import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.data.domain.Page; @@ -42,10 +44,10 @@ public class RecordService { @Transactional public Record createRecord(Member loginMember, RecordCreateRequest request) { Gym gym = gymRepository.findById(request.getGymId()) - .orElseThrow(() -> new EntityNotFoundException(request.getGymId() + "is not found")); + .orElseThrow(() -> new EntityNotFoundException(request.getGymId() + "is not found")); Level level = levelRepository.findById(request.getLevelId()) - .orElseThrow(() -> new EntityNotFoundException(request.getLevelId() + "is not found")); + .orElseThrow(() -> new EntityNotFoundException(request.getLevelId() + "is not found")); String videoUrl = request.getVideoUrl(); LocalDate climbedDate = request.getClimbDate() == null ? LocalDate.now(): request.getClimbDate(); @@ -58,6 +60,8 @@ public Record createRecord(Member loginMember, RecordCreateRequest request) { // TODO 썸네일 이미지 저장 안되었을 때 처리 } + PhysicalInfo physicalInfo = loginMember.getPhysicalInfo(); + Record record = Record.builder() .member(loginMember) .gym(gym) @@ -66,6 +70,7 @@ public Record createRecord(Member loginMember, RecordCreateRequest request) { .videoUrl(videoUrl) .thumbnailUrl(thumbnailImageUrl) .climbDate(climbedDate) + .physicalInfo(physicalInfo) .build(); return recordRepository.save(record); @@ -74,20 +79,20 @@ public Record createRecord(Member loginMember, RecordCreateRequest request) { @Transactional public Record updateRecord(Member member, Long recordId, RecordUpdateRequest request) { Record record = recordRepository.findById(recordId) - .orElseThrow(() -> new EntityNotFoundException(recordId + "is not found")); + .orElseThrow(() -> new EntityNotFoundException(recordId + "is not found")); if (!record.isEditable(member)) { throw new ForbiddenException("다른 사용자가 업로드한 record는 수정할 수 없음"); } Gym gym = gymRepository.findById(request.getGymId()) - .orElseThrow(() -> new EntityNotFoundException(request.getGymId() + "is not found")); + .orElseThrow(() -> new EntityNotFoundException(request.getGymId() + "is not found")); Level level = levelRepository.findById(request.getLevelId()) - .orElseThrow(() -> new EntityNotFoundException(request.getLevelId() + "is not found")); + .orElseThrow(() -> new EntityNotFoundException(request.getLevelId() + "is not found")); // TODO: origin 영상 데이터와 updated 영상 데이터가 다른걸 어떻게 알 것인가? - record.update(gym, level, null); + record.update(gym, level, null, member); record.updateClimbDate(request.getClimbDate()); return record; @@ -96,7 +101,7 @@ public Record updateRecord(Member member, Long recordId, RecordUpdateRequest req @Transactional public void deleteRecord(Member member, Long recordId) { Record record = recordRepository.findById(recordId) - .orElseThrow(() -> new NoSuchElementException("존재하지 않는 기록입니다.")); + .orElseThrow(() -> new NoSuchElementException("존재하지 않는 기록입니다.")); if (!record.isEditable(member)) { throw new ForbiddenException("다른 사용자가 업로드한 record는 삭제할 수 없음"); @@ -108,7 +113,7 @@ public void deleteRecord(Member member, Long recordId) { @Transactional(readOnly = true) public RecordResponse readRecord(Member member, Long recordId) { Record record = recordRepository.findByIdWithMember(recordId) - .orElseThrow(() -> new EntityNotFoundException(recordId + "is not found")); + .orElseThrow(() -> new EntityNotFoundException(recordId + "is not found")); return new RecordResponse(member, record); } @@ -131,13 +136,13 @@ public PageDto findPage(Member requestMember, Long gymId, Long l Page recordPage = recordRepository.findRecordPage(requestMember, gymId, levelId, memberId, page, size); return PageDto.builder() - .totalCount(recordPage.getTotalElements()) - .resultCount(recordPage.getNumberOfElements()) - .totalPage((int) Math.ceil((double) recordPage.getTotalElements() / size)) - .page(page) - .isEnd(recordPage.isLast()) - .contents(toRecordResponses(recordPage.getContent())) - .build(); + .totalCount(recordPage.getTotalElements()) + .resultCount(recordPage.getNumberOfElements()) + .totalPage((int) Math.ceil((double) recordPage.getTotalElements() / size)) + .page(page) + .isEnd(recordPage.isLast()) + .contents(toRecordResponses(recordPage.getContent())) + .build(); } @Transactional(readOnly = true) @@ -145,25 +150,25 @@ public PageDto findPageMy(Long memberId, Integer page, Integer Page myRecordPage = recordRepository.findMyRecordPage(memberId, page, size); return PageDto.builder() - .totalCount(myRecordPage.getTotalElements()) - .resultCount(myRecordPage.getNumberOfElements()) - .totalPage((int) Math.ceil((double) myRecordPage.getTotalElements() / size)) - .page(page) - .isEnd(myRecordPage.isLast()) - .contents(toMyRecordResponses(myRecordPage.getContent())) - .build(); + .totalCount(myRecordPage.getTotalElements()) + .resultCount(myRecordPage.getNumberOfElements()) + .totalPage((int) Math.ceil((double) myRecordPage.getTotalElements() / size)) + .page(page) + .isEnd(myRecordPage.isLast()) + .contents(toMyRecordResponses(myRecordPage.getContent())) + .build(); } private List toRecordResponses(List records) { return records.stream() - .map(record -> new RecordResponse(record.getMember(), record)) - .collect(Collectors.toList()); + .map(record -> new RecordResponse(record.getMember(), record)) + .collect(Collectors.toList()); } private List toMyRecordResponses(List records) { return records.stream() - .map(record -> new MyRecordResponse(record, record.getGym(), record.getLevel())) - .collect(Collectors.toList()); + .map(record -> new MyRecordResponse(record, record.getGym(), record.getLevel())) + .collect(Collectors.toList()); } } diff --git a/src/main/java/com/climingo/climingoApi/record/domain/Record.java b/src/main/java/com/climingo/climingoApi/record/domain/Record.java index 3e899c4..470e0c0 100644 --- a/src/main/java/com/climingo/climingoApi/record/domain/Record.java +++ b/src/main/java/com/climingo/climingoApi/record/domain/Record.java @@ -4,14 +4,8 @@ import com.climingo.climingoApi.gym.domain.Gym; import com.climingo.climingoApi.level.domain.Level; import com.climingo.climingoApi.member.domain.Member; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; +import com.climingo.climingoApi.member.domain.PhysicalInfo; +import jakarta.persistence.*; import lombok.AccessLevel; import lombok.Builder; import lombok.Getter; @@ -53,10 +47,13 @@ public class Record extends BaseTimeEntity { private String content; + @Embedded + private PhysicalInfo physicalInfo; + private boolean blocked; @Builder - public Record(Long id, Member member, Level level, Gym gym, String videoUrl, String thumbnailUrl, String content, LocalDate climbDate) { + public Record(Long id, Member member, Level level, Gym gym, String videoUrl, String thumbnailUrl, String content, LocalDate climbDate, PhysicalInfo physicalInfo) { this.id = id; this.member = member; this.level = level; @@ -66,15 +63,18 @@ public Record(Long id, Member member, Level level, Gym gym, String videoUrl, Str this.content = content; this.climbDate = climbDate; this.blocked = false; + this.physicalInfo = physicalInfo; } - public void update(Gym gym, Level level, String videoUrl) { + public void update(Gym gym, Level level, String videoUrl, Member loginMember) { if (!gym.getId().equals(this.gym.getId())) { this.gym = gym; } if (!level.getId().equals(this.level.getId())) { this.level = level; } + + this.physicalInfo = loginMember.getPhysicalInfo(); // TODO: origin 영상 데이터와 updated 영상 데이터가 다른걸 어떻게 알 것인가? } diff --git a/src/test/java/com/climingo/climingoApi/ClimingoApiApplicationTests.java b/src/test/java/com/climingo/climingoApi/ClimingoApiApplicationTests.java index 6ad878c..bf1471d 100644 --- a/src/test/java/com/climingo/climingoApi/ClimingoApiApplicationTests.java +++ b/src/test/java/com/climingo/climingoApi/ClimingoApiApplicationTests.java @@ -1,9 +1,7 @@ package com.climingo.climingoApi; import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; -@SpringBootTest class ClimingoApiApplicationTests { @Test diff --git a/src/test/java/com/climingo/climingoApi/record/application/RecordServiceTest.java b/src/test/java/com/climingo/climingoApi/record/application/RecordServiceTest.java index 580c5f6..b8e4bef 100644 --- a/src/test/java/com/climingo/climingoApi/record/application/RecordServiceTest.java +++ b/src/test/java/com/climingo/climingoApi/record/application/RecordServiceTest.java @@ -236,4 +236,156 @@ void delete_test_when_not_same_member_then_throw_exception() { @DisplayName("기록 조회 테스트 - ") void read_test() { } + + @Test + @DisplayName("createRecord - 성공") + void createRecord_success() throws IOException { + // given + Member loginMember = Member.builder().id(1L).build(); + RecordCreateRequest request = new RecordCreateRequest(1L, 1L, "videoUrl", LocalDate.now()); + + when(gymRepository.findById(1L)).thenReturn(Optional.of(mock(Gym.class))); + when(levelRepository.findById(1L)).thenReturn(Optional.of(mock(Level.class))); + when(recordRepository.save(any(Record.class))).thenAnswer(invocation -> invocation.getArgument(0)); + + // when + Record record = recordService.createRecord(loginMember, request); + + // then + assertEquals(loginMember, record.getMember()); + assertEquals("videoUrl", record.getVideoUrl()); + } + + @Test + @DisplayName("createRecord - Gym이 존재하지 않을 때") + void createRecord_gymNotFound() { + // given + Member loginMember = Member.builder().id(1L).build(); + RecordCreateRequest request = new RecordCreateRequest(1L, 1L, "videoUrl", LocalDate.now()); + + when(gymRepository.findById(1L)).thenReturn(Optional.empty()); + + // when & then + assertThrows(Exception.class, () -> recordService.createRecord(loginMember, request)); + } + + @Test + @DisplayName("createRecord - Level이 존재하지 않을 때") + void createRecord_levelNotFound() { + // given + Member loginMember = Member.builder().id(1L).build(); + RecordCreateRequest request = new RecordCreateRequest(1L, 1L, "videoUrl", LocalDate.now()); + + when(gymRepository.findById(1L)).thenReturn(Optional.of(mock(Gym.class))); + when(levelRepository.findById(1L)).thenReturn(Optional.empty()); + + // when & then + assertThrows(Exception.class, () -> recordService.createRecord(loginMember, request)); + } + + @Test + @DisplayName("updateRecord - 성공") + void updateRecord_success() { + // given + Member loginMember = Member.builder().id(1L).build(); + + Gym gym = mock(Gym.class); + when(gym.getId()).thenReturn(1L); + + Level level = mock(Level.class); + when(level.getId()).thenReturn(1L); + + Record record = Record.builder().member(loginMember).gym(gym).level(level).build(); + RecordUpdateRequest request = new RecordUpdateRequest(1L, 1L, mock(MultipartFile.class), LocalDate.now()); + + when(recordRepository.findById(1L)).thenReturn(Optional.of(record)); + when(gymRepository.findById(1L)).thenReturn(Optional.of(gym)); + when(levelRepository.findById(1L)).thenReturn(Optional.of(level)); + + // when + Record updatedRecord = recordService.updateRecord(loginMember, 1L, request); + + // then + assertEquals(loginMember, updatedRecord.getMember()); + } + + @Test + @DisplayName("updateRecord - Record가 존재하지 않을 때") + void updateRecord_recordNotFound() { + // given + Member loginMember = Member.builder().id(1L).build(); + RecordUpdateRequest request = new RecordUpdateRequest(1L, 1L, mock(MultipartFile.class), LocalDate.now()); + + when(recordRepository.findById(1L)).thenReturn(Optional.empty()); + + // when & then + assertThrows(Exception.class, () -> recordService.updateRecord(loginMember, 1L, request)); + } + + @Test + @DisplayName("updateRecord - Gym이 존재하지 않을 때") + void updateRecord_gymNotFound() { + // given + Member loginMember = Member.builder().id(1L).build(); + + Gym gym = mock(Gym.class); + when(gym.getId()).thenReturn(1L); + + Level level = mock(Level.class); + when(level.getId()).thenReturn(1L); + + Record record = Record.builder().member(loginMember).gym(gym).level(level).build(); + RecordUpdateRequest request = new RecordUpdateRequest(1L, 1L, mock(MultipartFile.class), LocalDate.now()); + + when(recordRepository.findById(1L)).thenReturn(Optional.of(record)); + when(gymRepository.findById(1L)).thenReturn(Optional.empty()); + + // when & then + assertThrows(Exception.class, () -> recordService.updateRecord(loginMember, 1L, request)); + } + + @Test + @DisplayName("updateRecord - Level이 존재하지 않을 때") + void updateRecord_levelNotFound() { + // given + Member loginMember = Member.builder().id(1L).build(); + + Gym gym = mock(Gym.class); + when(gym.getId()).thenReturn(1L); + + Level level = mock(Level.class); + when(level.getId()).thenReturn(1L); + + Record record = Record.builder().member(loginMember).gym(gym).level(level).build(); + RecordUpdateRequest request = new RecordUpdateRequest(1L, 1L, mock(MultipartFile.class), LocalDate.now()); + + when(recordRepository.findById(1L)).thenReturn(Optional.of(record)); + when(gymRepository.findById(1L)).thenReturn(Optional.of(gym)); + when(levelRepository.findById(1L)).thenReturn(Optional.empty()); + + // when & then + assertThrows(Exception.class, () -> recordService.updateRecord(loginMember, 1L, request)); + } + + @Test + @DisplayName("updateRecord - Forbidden") + void updateRecord_forbidden() { + // given + Member loginMember = Member.builder().id(1L).build(); + Member anotherMember = Member.builder().id(2L).build(); + + Gym gym = mock(Gym.class); + when(gym.getId()).thenReturn(1L); + + Level level = mock(Level.class); + when(level.getId()).thenReturn(1L); + + Record record = Record.builder().member(anotherMember).gym(gym).level(level).build(); + RecordUpdateRequest request = new RecordUpdateRequest(1L, 1L, mock(MultipartFile.class), LocalDate.now()); + + when(recordRepository.findById(1L)).thenReturn(Optional.of(record)); + + // when & then + assertThrows(ForbiddenException.class, () -> recordService.updateRecord(loginMember, 1L, request)); + } } \ No newline at end of file From 216218671752ee97db809c97cb27683eb33d7f8d Mon Sep 17 00:00:00 2001 From: JongKeun Kim Date: Tue, 2 Dec 2025 22:24:52 +0900 Subject: [PATCH 22/35] =?UTF-8?q?[#150]=20=EC=B0=8D=EB=B3=BC=20=EB=AA=A9?= =?UTF-8?q?=EB=A1=9D=20=EC=A1=B0=ED=9A=8C=20api=20=EA=B5=AC=ED=98=84=20(#1?= =?UTF-8?q?51)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 찍볼 목록 조회 기능 추가 * feat: 빌드 실패 이슈 해결 * Update github-action-ci.yml * feat: 빌드 실패 이슈 해결 --- .../auth/api/request/SignInRequest.java | 15 ++-- .../auth/api/request/SignUpRequest.java | 26 ++++++- .../jjikboul/api/JjikboulController.java | 16 +++++ .../application/JjikboulQueryUseCase.java | 5 +- .../jjikboul/application/JjikboulService.java | 30 ++++++++ .../response/JjikboulResponse.java | 3 + .../domain/JjikboulQueryRepository.java | 68 +++++++++++++++++++ .../api/request/UpdateNicknameRequest.java | 8 ++- .../ClimingoApiApplicationTests.java | 11 --- 9 files changed, 161 insertions(+), 21 deletions(-) create mode 100644 src/main/java/com/climingo/climingoApi/jjikboul/domain/JjikboulQueryRepository.java diff --git a/src/main/java/com/climingo/climingoApi/auth/api/request/SignInRequest.java b/src/main/java/com/climingo/climingoApi/auth/api/request/SignInRequest.java index 839dee4..0f3b2a9 100644 --- a/src/main/java/com/climingo/climingoApi/auth/api/request/SignInRequest.java +++ b/src/main/java/com/climingo/climingoApi/auth/api/request/SignInRequest.java @@ -1,17 +1,14 @@ package com.climingo.climingoApi.auth.api.request; +import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Pattern; import lombok.Builder; import lombok.Getter; -@Builder @Getter -@JsonDeserialize(builder = SignInRequest.SignInRequestBuilder.class) public class SignInRequest { @JsonProperty("providerType") @@ -24,4 +21,14 @@ public class SignInRequest { @NotNull @Schema(description = "provider 유저 정보 조회용 토큰", example = "xxxxxx") private final String providerToken; + + @JsonCreator + @Builder + public SignInRequest( + @JsonProperty("providerType") String providerType, + @JsonProperty("providerToken") String providerToken + ) { + this.providerType = providerType; + this.providerToken = providerToken; + } } \ No newline at end of file diff --git a/src/main/java/com/climingo/climingoApi/auth/api/request/SignUpRequest.java b/src/main/java/com/climingo/climingoApi/auth/api/request/SignUpRequest.java index 8e7cb19..f2530f7 100644 --- a/src/main/java/com/climingo/climingoApi/auth/api/request/SignUpRequest.java +++ b/src/main/java/com/climingo/climingoApi/auth/api/request/SignUpRequest.java @@ -1,17 +1,15 @@ package com.climingo.climingoApi.auth.api.request; import com.climingo.climingoApi.member.domain.PhysicalInfo; +import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Pattern; import jakarta.validation.constraints.Size; import lombok.Builder; import lombok.Getter; -@Builder @Getter -@JsonDeserialize(builder = SignUpRequest.SignUpRequestBuilder.class) public class SignUpRequest { @JsonProperty("providerType") @@ -42,4 +40,26 @@ public class SignUpRequest { @JsonProperty("physicalInfo") private final PhysicalInfo physicalInfo; + + @JsonCreator + @Builder + public SignUpRequest( + @JsonProperty("providerType") String providerType, + @JsonProperty("authId") String authId, + @JsonProperty("providerToken") String providerToken, + @JsonProperty("nickname") String nickname, + @JsonProperty("profileUrl") String profileUrl, + @JsonProperty("email") String email, + @JsonProperty("homeGymId") Long homeGymId, + @JsonProperty("physicalInfo") PhysicalInfo physicalInfo + ) { + this.providerType = providerType; + this.authId = authId; + this.providerToken = providerToken; + this.nickname = nickname; + this.profileUrl = profileUrl; + this.email = email; + this.homeGymId = homeGymId; + this.physicalInfo = physicalInfo; + } } diff --git a/src/main/java/com/climingo/climingoApi/jjikboul/api/JjikboulController.java b/src/main/java/com/climingo/climingoApi/jjikboul/api/JjikboulController.java index f981b84..e6f9e37 100644 --- a/src/main/java/com/climingo/climingoApi/jjikboul/api/JjikboulController.java +++ b/src/main/java/com/climingo/climingoApi/jjikboul/api/JjikboulController.java @@ -8,6 +8,8 @@ import com.climingo.climingoApi.jjikboul.application.JjikboulQueryUseCase; import com.climingo.climingoApi.jjikboul.application.response.JjikboulResponse; import com.climingo.climingoApi.member.domain.Member; +import com.climingo.climingoApi.record.api.response.PageDto; +import jakarta.validation.constraints.Min; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -20,6 +22,20 @@ public class JjikboulController { private final JjikboulQueryUseCase jjikboulQueryUseCase; private final JjikboulDeleteUseCase jjikboulDeleteUseCase; + @GetMapping("/jjikbouls") + public ResponseEntity> getJjikbouls( + @RequestMember Member member, + @RequestParam(value = "gymId", required = false) Long gymId, + @RequestParam(value = "levelId", required = false) Long levelId, + @RequestParam(value = "memberId", required = false) Long memberId, + @RequestParam(value = "page", required = false, defaultValue = "0") @Min(0) Integer page, + @RequestParam(value = "size", required = false, defaultValue = "10") @Min(1) Integer size + ) { + PageDto jjikboulResponsePageDto = jjikboulQueryUseCase.readJjikbouls(member, gymId, levelId, memberId, page, size); + + return ResponseEntity.ok().body(jjikboulResponsePageDto); + } + @GetMapping("/jjikbouls/{jjikboulId}") public ResponseEntity getJjikboul( @RequestMember Member member, diff --git a/src/main/java/com/climingo/climingoApi/jjikboul/application/JjikboulQueryUseCase.java b/src/main/java/com/climingo/climingoApi/jjikboul/application/JjikboulQueryUseCase.java index 899b914..fa780db 100644 --- a/src/main/java/com/climingo/climingoApi/jjikboul/application/JjikboulQueryUseCase.java +++ b/src/main/java/com/climingo/climingoApi/jjikboul/application/JjikboulQueryUseCase.java @@ -2,7 +2,10 @@ import com.climingo.climingoApi.jjikboul.application.response.JjikboulResponse; import com.climingo.climingoApi.member.domain.Member; +import com.climingo.climingoApi.record.api.response.PageDto; public interface JjikboulQueryUseCase { JjikboulResponse readJjikboul(Member member, Long jjikboulId); -} + + PageDto readJjikbouls(Member member, Long gymId, Long levelId, Long memberId, Integer page, Integer size); +} \ No newline at end of file diff --git a/src/main/java/com/climingo/climingoApi/jjikboul/application/JjikboulService.java b/src/main/java/com/climingo/climingoApi/jjikboul/application/JjikboulService.java index 8cd6eae..9624b26 100644 --- a/src/main/java/com/climingo/climingoApi/jjikboul/application/JjikboulService.java +++ b/src/main/java/com/climingo/climingoApi/jjikboul/application/JjikboulService.java @@ -6,15 +6,21 @@ import com.climingo.climingoApi.jjikboul.api.request.JjikboulCreateRequest; import com.climingo.climingoApi.jjikboul.application.response.JjikboulResponse; import com.climingo.climingoApi.jjikboul.domain.Jjikboul; +import com.climingo.climingoApi.jjikboul.domain.JjikboulQueryRepository; import com.climingo.climingoApi.jjikboul.domain.JjikboulRepository; import com.climingo.climingoApi.level.domain.Level; import com.climingo.climingoApi.level.domain.LevelRepository; import com.climingo.climingoApi.member.domain.Member; +import com.climingo.climingoApi.record.api.response.PageDto; import jakarta.persistence.EntityNotFoundException; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.List; +import java.util.stream.Collectors; + @Service @RequiredArgsConstructor public class JjikboulService implements JjikboulCreateUseCase, JjikboulDeleteUseCase, JjikboulQueryUseCase { @@ -22,6 +28,7 @@ public class JjikboulService implements JjikboulCreateUseCase, JjikboulDeleteUse private final GymRepository gymRepository; private final LevelRepository levelRepository; private final JjikboulRepository jjikboulRepository; + private final JjikboulQueryRepository jjikboulQueryRepository; @Transactional @Override @@ -45,12 +52,35 @@ public Long create(Member member, JjikboulCreateRequest request) { } @Transactional(readOnly = true) + @Override public JjikboulResponse readJjikboul(Member member, Long customProblemId) { Jjikboul jjikboul = jjikboulRepository.findById(customProblemId) .orElseThrow(() -> new EntityNotFoundException(customProblemId + "is not found")); return JjikboulResponse.of(member, jjikboul); } + @Transactional(readOnly = true) + @Override + public PageDto readJjikbouls(Member member, Long gymId, Long levelId, Long memberId, Integer page, Integer size) { + Page jjikbouls = jjikboulQueryRepository.findJjikbouls(member, gymId, levelId, memberId, page, size); + + return PageDto.builder() + .totalCount(jjikbouls.getTotalElements()) + .resultCount(jjikbouls.getNumberOfElements()) + .totalPage((int) Math.ceil((double) jjikbouls.getTotalElements() / size)) + .page(page) + .isEnd(jjikbouls.isLast()) + .contents(toRecordResponses(jjikbouls.getContent())) + .build(); + } + + private List toRecordResponses(List jjikbouls) { + return jjikbouls.stream() + .map(jjikboul -> JjikboulResponse.of(jjikboul.getMember(), jjikboul)) + .collect(Collectors.toList()); + } + + @Transactional @Override public void deleteJjikboul(Member requestMember, Long jjikboulId) { diff --git a/src/main/java/com/climingo/climingoApi/jjikboul/application/response/JjikboulResponse.java b/src/main/java/com/climingo/climingoApi/jjikboul/application/response/JjikboulResponse.java index 0a07d5a..d2d59c4 100644 --- a/src/main/java/com/climingo/climingoApi/jjikboul/application/response/JjikboulResponse.java +++ b/src/main/java/com/climingo/climingoApi/jjikboul/application/response/JjikboulResponse.java @@ -5,7 +5,10 @@ import com.climingo.climingoApi.record.api.response.ShortGymResponse; import com.climingo.climingoApi.record.api.response.ShortLevelResponse; import com.climingo.climingoApi.record.api.response.ShortMemberResponse; +import com.climingo.climingoApi.record.api.response.ShortRecordResponse; +import com.climingo.climingoApi.record.domain.Record; import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Builder; public record JjikboulResponse( ShortMemberResponse memberInfo, diff --git a/src/main/java/com/climingo/climingoApi/jjikboul/domain/JjikboulQueryRepository.java b/src/main/java/com/climingo/climingoApi/jjikboul/domain/JjikboulQueryRepository.java new file mode 100644 index 0000000..5b37466 --- /dev/null +++ b/src/main/java/com/climingo/climingoApi/jjikboul/domain/JjikboulQueryRepository.java @@ -0,0 +1,68 @@ +package com.climingo.climingoApi.jjikboul.domain; + +import com.climingo.climingoApi.member.domain.Member; +import com.querydsl.core.types.Order; +import com.querydsl.core.types.OrderSpecifier; +import com.querydsl.core.types.dsl.BooleanExpression; +import com.querydsl.core.types.dsl.Expressions; +import com.querydsl.jpa.impl.JPAQuery; +import com.querydsl.jpa.impl.JPAQueryFactory; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.support.PageableExecutionUtils; +import org.springframework.stereotype.Repository; + +import java.util.List; + +import static com.climingo.climingoApi.gym.domain.QGym.gym; +import static com.climingo.climingoApi.jjikboul.domain.QJjikboul.jjikboul; +import static com.climingo.climingoApi.level.domain.QLevel.level; +import static com.climingo.climingoApi.member.domain.QMember.member; +import static com.climingo.climingoApi.report.domain.QBlock.block; + +@Repository +@RequiredArgsConstructor +public class JjikboulQueryRepository { + + private final JPAQueryFactory queryFactory; + + public Page findJjikbouls(Member requestMember, Long gymId, Long levelId, Long memberId, Integer page, Integer size) { + List jjikbouls = queryFactory.selectFrom(jjikboul) + .distinct() + .innerJoin(jjikboul.member, member).fetchJoin() + .innerJoin(jjikboul.gym, gym).fetchJoin() + .innerJoin(jjikboul.level, level).fetchJoin() + .where(gymIdEq(gymId), + levelIdEq(levelId), + memberIdEq(memberId), + jjikboul.blocked.isFalse()) + .orderBy(new OrderSpecifier<>(Order.DESC, jjikboul.createdDate)) + .offset((long) page * size) + .limit(size) + .fetch(); + + JPAQuery countQuery = queryFactory.select(jjikboul.count()) + .from(jjikboul) + .where(gymIdEq(gymId), + levelIdEq(levelId), + memberIdEq(memberId), + jjikboul.blocked.isFalse()); + + Pageable pageable = PageRequest.of(page, size); + return PageableExecutionUtils.getPage(jjikbouls, pageable, countQuery::fetchOne); + } + + private BooleanExpression memberIdEq(Long memberId) { + return memberId == null ? null : jjikboul.member.id.eq(memberId); + } + + private BooleanExpression gymIdEq(Long gymId) { + return gymId == null ? null : jjikboul.gym.id.eq(gymId); + } + + private BooleanExpression levelIdEq(Long levelId) { + return levelId == null ? null : jjikboul.level.id.eq(levelId); + } +} diff --git a/src/main/java/com/climingo/climingoApi/member/api/request/UpdateNicknameRequest.java b/src/main/java/com/climingo/climingoApi/member/api/request/UpdateNicknameRequest.java index 5139fae..bacb094 100644 --- a/src/main/java/com/climingo/climingoApi/member/api/request/UpdateNicknameRequest.java +++ b/src/main/java/com/climingo/climingoApi/member/api/request/UpdateNicknameRequest.java @@ -1,17 +1,21 @@ package com.climingo.climingoApi.member.api.request; +import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import jakarta.validation.constraints.Size; import lombok.Builder; import lombok.Getter; @Builder @Getter -@JsonDeserialize(builder = UpdateNicknameRequest.UpdateNicknameRequestBuilder.class) public class UpdateNicknameRequest { @JsonProperty("nickname") @Size(min = 2, max = 16) private final String nickname; + + @JsonCreator + public UpdateNicknameRequest(@JsonProperty("nickname") String nickname) { + this.nickname = nickname; + } } diff --git a/src/test/java/com/climingo/climingoApi/ClimingoApiApplicationTests.java b/src/test/java/com/climingo/climingoApi/ClimingoApiApplicationTests.java index bf1471d..e69de29 100644 --- a/src/test/java/com/climingo/climingoApi/ClimingoApiApplicationTests.java +++ b/src/test/java/com/climingo/climingoApi/ClimingoApiApplicationTests.java @@ -1,11 +0,0 @@ -package com.climingo.climingoApi; - -import org.junit.jupiter.api.Test; - -class ClimingoApiApplicationTests { - - @Test - void contextLoads() { - } - -} From a0a5e832b74fd361a656f7fe3b3ba8dce5cf0dbd Mon Sep 17 00:00:00 2001 From: JongKeun Kim Date: Tue, 27 Jan 2026 00:13:28 +0900 Subject: [PATCH 23/35] =?UTF-8?q?modify:=20datasource=20url=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20(#156)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application-prod.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index ecabe96..c90f12f 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -1,7 +1,7 @@ spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver - url: ENC(ELzhq0/YxyrRypjtoHUo/UOZ+qUg4Rh7KT9TGY1Ego8eUAYNyQE/KTQQpywom0Ydkh6dvYN8HWTUz7Yry72StmeVfgajBt7JQabvp4ULpJtXeQ3tgt29BQKV5I2q+iEn) + url: ENC(7uKXF9QupHeIphLG2YxS9Z4b793gUSHQ4hcw3Ovryz5B1IAjs0RlxCMsc/zsIEna3OWsf0fveUS0G1FSBwFiBDa8l7eym44mblmBVONkNWgr4BqsWF/1jE/PLWSvpf0ZCuB1RiF7C4d7DvHhuGY2CiBspgvdX1oz) username: ENC(/hDIlwiiOpd1p7Coppl9L4xHYDjkf9cj) password: ENC(Toy+1i51hX5nGADpusirW4loTpskgy4o) h2: From a39add524eff9f6e1b3d8488af72acdbafbfe242 Mon Sep 17 00:00:00 2001 From: JongKeun Kim Date: Sun, 1 Feb 2026 20:13:58 +0900 Subject: [PATCH 24/35] =?UTF-8?q?add:=20=EC=83=8C=EB=B0=95=EC=9A=A9=20?= =?UTF-8?q?=EC=BB=B4=ED=8F=AC=EC=A6=88=20=ED=8C=8C=EC=9D=BC=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#157)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose-sandbox.yml | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 docker-compose-sandbox.yml diff --git a/docker-compose-sandbox.yml b/docker-compose-sandbox.yml new file mode 100644 index 0000000..e1b7a4d --- /dev/null +++ b/docker-compose-sandbox.yml @@ -0,0 +1,34 @@ +version: '3.8' + +services: + climingo-api: + image: climingo/climingo:${TAG} # Spring Boot 애플리케이션 Docker 이미지 + container_name: climingo-api + environment: + - JASYPT_PASSWORD=${JASYPT_PASSWORD} + - VERSION=${TAG} + - BUILDTIME=${BUILDTIME} + - LOGGING_FILE_PATH=/logs # 로그 파일 경로를 환경 변수로 설정 + volumes: + - ./logs:/logs # 호스트와 컨테이너 간 로그 파일 공유 + ports: + - "8080:8080" + logging: # 로그 드라이버 설정 (json-file 기본값 사용) + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + + promtail: + image: grafana/promtail:2.9.0 # Promtail Docker 이미지 + container_name: promtail + ports: + - "9080:9080" + depends_on: + - climingo-api # Spring Boot 애플리케이션이 먼저 실행되도록 설정 + volumes: + - ./logs:/logs # 호스트 로그 디렉토리를 Promtail에 공유 + - ./promtail-config.yml:/etc/promtail/promtail.yml:ro # Promtail 설정 파일 + command: + - -config.file=/etc/promtail/promtail.yml # 설정 파일 위치 + restart: always \ No newline at end of file From 655803286ca432bda6e063b74f185f15bce5f716 Mon Sep 17 00:00:00 2001 From: JongKeun Kim Date: Sun, 1 Feb 2026 20:20:58 +0900 Subject: [PATCH 25/35] =?UTF-8?q?add:=20readme=20=EC=88=98=EC=A0=95=20(#15?= =?UTF-8?q?9)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 2db00c0..ea99ad1 100644 --- a/README.md +++ b/README.md @@ -1 +1,3 @@ -# climingo-api. \ No newline at end of file +# climingo-api. + +클라밍고 api 서버 \ No newline at end of file From 3b05b691ad444bf0973dcee9a27da8d698640b57 Mon Sep 17 00:00:00 2001 From: JongKeun Kim Date: Sun, 1 Feb 2026 20:39:50 +0900 Subject: [PATCH 26/35] =?UTF-8?q?dev=20=EB=A8=B8=EC=A7=80=EC=8B=9C=20cd=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=EB=A5=BC=20=EC=9C=84=ED=95=9C=20pr?= =?UTF-8?q?=20=20(#161)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add: readme 수정 * add: readme 수정 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ea99ad1..62dc550 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,3 @@ # climingo-api. -클라밍고 api 서버 \ No newline at end of file +클라밍고 api 서버 \ No newline at end of file From 6f72c0d7e1b2b26afa8fa52ab23faf8da636f890 Mon Sep 17 00:00:00 2001 From: JongKeun Kim Date: Sun, 1 Feb 2026 20:46:12 +0900 Subject: [PATCH 27/35] =?UTF-8?q?add:=20readme=20=EC=88=98=EC=A0=95=20(#16?= =?UTF-8?q?2)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 62dc550..ea99ad1 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,3 @@ # climingo-api. -클라밍고 api 서버 \ No newline at end of file +클라밍고 api 서버 \ No newline at end of file From 938122305666333ac6720eb85bb3e249c4b66d11 Mon Sep 17 00:00:00 2001 From: Bellroute Date: Sun, 1 Feb 2026 21:02:51 +0900 Subject: [PATCH 28/35] =?UTF-8?q?fix:=20github=20action=20flow=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/github-action-ci.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/github-action-ci.yml b/.github/workflows/github-action-ci.yml index af4e90a..29e817c 100644 --- a/.github/workflows/github-action-ci.yml +++ b/.github/workflows/github-action-ci.yml @@ -87,6 +87,8 @@ jobs: docker pull climingo/climingo-sandbox:$TAG cd ${{ secrets.SANDBOX_DOCKER_COMPOSE_PATH }} export TAG=$TAG + export JASYPT_PASSWORD=${{ secrets.JASYPT_PASSWORD }} + export BUILDTIME=${{ secrets.BUILDTIME }} docker-compose -f docker-compose-sandbox.yml up -d EOF @@ -136,5 +138,7 @@ jobs: docker pull climingo/climingo:$TAG cd ${{ secrets.SANDBOX_DOCKER_COMPOSE_PATH }} export TAG=$TAG + export JASYPT_PASSWORD=${{ secrets.JASYPT_PASSWORD }} + export BUILDTIME=${{ secrets.BUILDTIME }} docker-compose -f docker-compose.yml up -d EOF From 15a87ee881a9dee570eabba22be0ce0f4e0250c6 Mon Sep 17 00:00:00 2001 From: JongKeun Kim Date: Sun, 1 Feb 2026 21:08:02 +0900 Subject: [PATCH 29/35] =?UTF-8?q?chore:=20cicd=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=EB=A5=BC=20=EC=9C=84=ED=95=9C=20=EB=AC=B4=EC=9D=98?= =?UTF-8?q?=EB=AF=B8=ED=95=9C=20=EC=BB=A4=EB=B0=8B=20(#164)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ea99ad1..62dc550 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,3 @@ # climingo-api. -클라밍고 api 서버 \ No newline at end of file +클라밍고 api 서버 \ No newline at end of file From 3678b40eb9f740370189d13dcd3e22963b7e61b6 Mon Sep 17 00:00:00 2001 From: Bellroute Date: Sun, 8 Feb 2026 23:31:56 +0900 Subject: [PATCH 30/35] =?UTF-8?q?chore:=20docker-compose=20=EB=84=A4?= =?UTF-8?q?=EC=9D=B4=EB=B0=8D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose-sandbox.yml => docker-compose-dev.yml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docker-compose-sandbox.yml => docker-compose-dev.yml (100%) diff --git a/docker-compose-sandbox.yml b/docker-compose-dev.yml similarity index 100% rename from docker-compose-sandbox.yml rename to docker-compose-dev.yml From a98b528787de082e589c3322e2c5a75f1e49f349 Mon Sep 17 00:00:00 2001 From: Bellroute Date: Sun, 8 Feb 2026 23:41:36 +0900 Subject: [PATCH 31/35] =?UTF-8?q?chore:=20docker-compose=20dev=EC=9A=A9=20?= =?UTF-8?q?=EC=9D=B4=EB=AF=B8=EC=A7=80=EB=AA=85=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose-dev.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose-dev.yml b/docker-compose-dev.yml index e1b7a4d..0ffa5e6 100644 --- a/docker-compose-dev.yml +++ b/docker-compose-dev.yml @@ -2,7 +2,7 @@ version: '3.8' services: climingo-api: - image: climingo/climingo:${TAG} # Spring Boot 애플리케이션 Docker 이미지 + image: climingo/climingo-dev:${TAG} # Spring Boot 애플리케이션 Docker 이미지 container_name: climingo-api environment: - JASYPT_PASSWORD=${JASYPT_PASSWORD} From 498f60d5bf88c9c9d270d1ebe011b3a11a7e3e47 Mon Sep 17 00:00:00 2001 From: Bellroute Date: Mon, 9 Feb 2026 00:24:32 +0900 Subject: [PATCH 32/35] =?UTF-8?q?chore:=20docker-compose=20dev=EC=9A=A9=20?= =?UTF-8?q?=EC=9D=B4=EB=AF=B8=EC=A7=80=EB=AA=85=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose-dev.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose-dev.yml b/docker-compose-dev.yml index 0ffa5e6..e129466 100644 --- a/docker-compose-dev.yml +++ b/docker-compose-dev.yml @@ -3,7 +3,7 @@ version: '3.8' services: climingo-api: image: climingo/climingo-dev:${TAG} # Spring Boot 애플리케이션 Docker 이미지 - container_name: climingo-api + container_name: climingo-dev-api environment: - JASYPT_PASSWORD=${JASYPT_PASSWORD} - VERSION=${TAG} From 808305618a2c44a278d462934f2a8010163a8687 Mon Sep 17 00:00:00 2001 From: Bellroute Date: Mon, 9 Feb 2026 00:30:25 +0900 Subject: [PATCH 33/35] chore: docker-compose --- docker-compose-dev.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docker-compose-dev.yml b/docker-compose-dev.yml index e129466..a5bc744 100644 --- a/docker-compose-dev.yml +++ b/docker-compose-dev.yml @@ -31,4 +31,8 @@ services: - ./promtail-config.yml:/etc/promtail/promtail.yml:ro # Promtail 설정 파일 command: - -config.file=/etc/promtail/promtail.yml # 설정 파일 위치 - restart: always \ No newline at end of file + restart: always + +networks: + climingo-network: + external: true \ No newline at end of file From 905656252467a44733d7308485044e992cb6417b Mon Sep 17 00:00:00 2001 From: Bellroute Date: Mon, 9 Feb 2026 00:49:06 +0900 Subject: [PATCH 34/35] chore: docker-compose --- docker-compose-dev.yml | 2 ++ docker-compose.yml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/docker-compose-dev.yml b/docker-compose-dev.yml index a5bc744..c32936f 100644 --- a/docker-compose-dev.yml +++ b/docker-compose-dev.yml @@ -18,6 +18,8 @@ services: options: max-size: "10m" max-file: "3" + networks: + - climingo-network promtail: image: grafana/promtail:2.9.0 # Promtail Docker 이미지 diff --git a/docker-compose.yml b/docker-compose.yml index d5df762..74b996f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -18,6 +18,8 @@ services: options: max-size: "10m" max-file: "3" + networks: + - climingo-network promtail: image: grafana/promtail:2.9.0 # Promtail Docker 이미지 From 30fe0019e37a0042b1fc776a53e5ab858f5ea577 Mon Sep 17 00:00:00 2001 From: Bellroute Date: Mon, 9 Feb 2026 00:53:19 +0900 Subject: [PATCH 35/35] chore: docker-compose --- docker-compose-dev.yml | 6 +++--- docker-compose.yml | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docker-compose-dev.yml b/docker-compose-dev.yml index c32936f..c951a2c 100644 --- a/docker-compose-dev.yml +++ b/docker-compose-dev.yml @@ -8,9 +8,9 @@ services: - JASYPT_PASSWORD=${JASYPT_PASSWORD} - VERSION=${TAG} - BUILDTIME=${BUILDTIME} - - LOGGING_FILE_PATH=/logs # 로그 파일 경로를 환경 변수로 설정 + - LOGGING_FILE_PATH=/logs/dev # 로그 파일 경로를 환경 변수로 설정 volumes: - - ./logs:/logs # 호스트와 컨테이너 간 로그 파일 공유 + - ./logs:/logs/dev # 호스트와 컨테이너 간 로그 파일 공유 ports: - "8080:8080" logging: # 로그 드라이버 설정 (json-file 기본값 사용) @@ -29,7 +29,7 @@ services: depends_on: - climingo-api # Spring Boot 애플리케이션이 먼저 실행되도록 설정 volumes: - - ./logs:/logs # 호스트 로그 디렉토리를 Promtail에 공유 + - ./logs:/logs/dev # 호스트 로그 디렉토리를 Promtail에 공유 - ./promtail-config.yml:/etc/promtail/promtail.yml:ro # Promtail 설정 파일 command: - -config.file=/etc/promtail/promtail.yml # 설정 파일 위치 diff --git a/docker-compose.yml b/docker-compose.yml index 74b996f..847886a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,9 +8,9 @@ services: - JASYPT_PASSWORD=${JASYPT_PASSWORD} - VERSION=${TAG} - BUILDTIME=${BUILDTIME} - - LOGGING_FILE_PATH=/logs # 로그 파일 경로를 환경 변수로 설정 + - LOGGING_FILE_PATH=/logs/prod # 로그 파일 경로를 환경 변수로 설정 volumes: - - ./logs:/logs # 호스트와 컨테이너 간 로그 파일 공유 + - ./logs:/logs/prod # 호스트와 컨테이너 간 로그 파일 공유 ports: - "8080:8080" logging: # 로그 드라이버 설정 (json-file 기본값 사용) @@ -29,7 +29,7 @@ services: depends_on: - climingo-api # Spring Boot 애플리케이션이 먼저 실행되도록 설정 volumes: - - ./logs:/logs # 호스트 로그 디렉토리를 Promtail에 공유 + - ./logs:/logs/prod # 호스트 로그 디렉토리를 Promtail에 공유 - ./promtail-config.yml:/etc/promtail/promtail.yml:ro # Promtail 설정 파일 command: - -config.file=/etc/promtail/promtail.yml # 설정 파일 위치