From de3bdefdc806200de5a651ceaa986c6c1fe4fc4d Mon Sep 17 00:00:00 2001 From: wjdtkdgns Date: Mon, 26 Jun 2023 22:45:18 +0900 Subject: [PATCH 01/41] =?UTF-8?q?[feat]=20querydsl=20=EC=85=8B=ED=8C=85,?= =?UTF-8?q?=20const=20=EC=85=8B=ED=8C=85=20#1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Core/src/main/java/allchive/server/.gitkeep | 0 .../server/core/consts/AllchiveConst.java | 23 ++++++++++++++ .../core/helper/SpringEnvironmentHelper.java | 30 +++++++++++++++++++ Domain/build.gradle | 2 -- Domain/src/main/java/allchive/server/.gitkeep | 0 .../server/domain/config/QueryDslConfig.java | 18 +++++++++++ .../src/main/resources/application-domain.yml | 10 +++---- build.gradle | 2 +- 8 files changed, 77 insertions(+), 8 deletions(-) delete mode 100644 Core/src/main/java/allchive/server/.gitkeep create mode 100644 Core/src/main/java/allchive/server/core/consts/AllchiveConst.java create mode 100644 Core/src/main/java/allchive/server/core/helper/SpringEnvironmentHelper.java delete mode 100644 Domain/src/main/java/allchive/server/.gitkeep create mode 100644 Domain/src/main/java/allchive/server/domain/config/QueryDslConfig.java diff --git a/Core/src/main/java/allchive/server/.gitkeep b/Core/src/main/java/allchive/server/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/Core/src/main/java/allchive/server/core/consts/AllchiveConst.java b/Core/src/main/java/allchive/server/core/consts/AllchiveConst.java new file mode 100644 index 00000000..be822b9d --- /dev/null +++ b/Core/src/main/java/allchive/server/core/consts/AllchiveConst.java @@ -0,0 +1,23 @@ +package allchive.server.core.consts; + +public class AllchiveConst { + public static final String AUTH_HEADER = "Authorization"; + public static final String BEARER = "Bearer "; + public static final String TOKEN_ROLE = "role"; + public static final String TOKEN_TYPE = "type"; + public static final String ACCESS_TOKEN = "ACCESS_TOKEN"; + public static final String REFRESH_TOKEN = "REFRESH_TOKEN"; + public static final String KR_YES = "예"; + public static final String KR_NO = "아니요"; + + public static final int MILLI_TO_SECOND = 1000; + public static final int BAD_REQUEST = 400; + public static final int UNAUTHORIZED = 401; + public static final int FORBIDDEN = 403; + public static final int NOT_FOUND = 404; + public static final int INTERNAL_SERVER = 500; + + public static final String[] SwaggerPatterns = { + "/swagger-resources/**", "/swagger-ui/**", "/v3/api-docs/**", "/v3/api-docs", + }; +} diff --git a/Core/src/main/java/allchive/server/core/helper/SpringEnvironmentHelper.java b/Core/src/main/java/allchive/server/core/helper/SpringEnvironmentHelper.java new file mode 100644 index 00000000..3d93939e --- /dev/null +++ b/Core/src/main/java/allchive/server/core/helper/SpringEnvironmentHelper.java @@ -0,0 +1,30 @@ +package allchive.server.core.helper; + + +import java.util.Arrays; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class SpringEnvironmentHelper { + + private final Environment environment; + + private final String PROD = "prod"; + private final String DEV = "dev"; + + public Boolean isProdProfile() { + String[] activeProfiles = environment.getActiveProfiles(); + List currentProfile = Arrays.stream(activeProfiles).toList(); + return currentProfile.contains(PROD); + } + + public Boolean isDevProfile() { + String[] activeProfiles = environment.getActiveProfiles(); + List currentProfile = Arrays.stream(activeProfiles).toList(); + return currentProfile.contains(DEV); + } +} diff --git a/Domain/build.gradle b/Domain/build.gradle index bdf67e23..b7ac5ee1 100644 --- a/Domain/build.gradle +++ b/Domain/build.gradle @@ -4,8 +4,6 @@ jar.enabled = true dependencies { api 'org.springframework.boot:spring-boot-starter-data-jpa' api 'mysql:mysql-connector-java:8.0.33' - testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.2' - testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.2' //QueryDsl implementation "com.querydsl:querydsl-jpa:5.0.0:jakarta" diff --git a/Domain/src/main/java/allchive/server/.gitkeep b/Domain/src/main/java/allchive/server/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/Domain/src/main/java/allchive/server/domain/config/QueryDslConfig.java b/Domain/src/main/java/allchive/server/domain/config/QueryDslConfig.java new file mode 100644 index 00000000..e369778b --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/config/QueryDslConfig.java @@ -0,0 +1,18 @@ +package allchive.server.domain.config; + + +import com.querydsl.jpa.impl.JPAQueryFactory; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class QueryDslConfig { + @PersistenceContext private EntityManager entityManager; + + @Bean + public JPAQueryFactory jpaQueryFactory() { + return new JPAQueryFactory(entityManager); + } +} diff --git a/Domain/src/main/resources/application-domain.yml b/Domain/src/main/resources/application-domain.yml index 77d92f67..fcd54dfb 100644 --- a/Domain/src/main/resources/application-domain.yml +++ b/Domain/src/main/resources/application-domain.yml @@ -1,10 +1,13 @@ # common spring: datasource: - url: jdbc:mysql://${MYSQL_HOST}:3306/${DB_NAME}?useSSL=false&characterEncoding=UTF-8&serverTimezone=Asia/Seoul&allowPublicKeyRetrieval=true&tinyInt1isBit=false + url: jdbc:mysql://${MYSQL_HOST}:${MYSQL_PORT}/${DB_NAME}?useSSL=false&characterEncoding=UTF-8&serverTimezone=Asia/Seoul&allowPublicKeyRetrieval=true&tinyInt1isBit=false driver-class-name: com.mysql.cj.jdbc.Driver - password: ${MYSQL_PASSWORD} username: ${MYSQL_USERNAME} + password: ${MYSQL_PASSWORD} + hikari: + maxLifetime: 580000 + maximum-pool-size: 20 jpa: show-sql: ${SHOW_SQL:true} properties: @@ -14,9 +17,6 @@ spring: hibernate: ddl-auto: update defer-datasource-initialization: true - sql: - init: - mode: always open-in-view: false database-platform: org.hibernate.dialect.MySQL5InnoDBDialect diff --git a/build.gradle b/build.gradle index 125cb6f5..13285fe9 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '3.1.0' + id 'org.springframework.boot' version '3.0.8' id 'com.diffplug.spotless' version '6.11.0' } From 8cd1d9d9b433ba0f22d35432c2b380bd2b633e1d Mon Sep 17 00:00:00 2001 From: wjdtkdgns Date: Mon, 26 Jun 2023 23:17:06 +0900 Subject: [PATCH 02/41] =?UTF-8?q?[feat]=20=EC=97=90=EB=9F=AC,=20response?= =?UTF-8?q?=20=EC=B2=98=EB=A6=AC=20#1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/config/GlobalExceptionHandler.java | 106 ++++++++++++++++++ .../api/config/response/ResponseAdviser.java | 63 +++++++++++ .../api/config/response/SuccessResponse.java | 35 ++++++ .../allchive/server/core/dto/ErrorReason.java | 23 ++++ .../server/core/error/BaseErrorCode.java | 8 ++ .../server/core/error/BaseErrorException.java | 16 +++ .../server/core/error/ErrorResponse.java | 32 ++++++ .../server/core/error/GlobalErrorCode.java | 37 ++++++ .../server/core/error/exception/.gitkeep | 0 Domain/build.gradle | 2 + .../src/main/resources/application-domain.yml | 1 + 11 files changed, 323 insertions(+) create mode 100644 Api/src/main/java/allchive/server/api/config/GlobalExceptionHandler.java create mode 100644 Api/src/main/java/allchive/server/api/config/response/ResponseAdviser.java create mode 100644 Api/src/main/java/allchive/server/api/config/response/SuccessResponse.java create mode 100644 Core/src/main/java/allchive/server/core/dto/ErrorReason.java create mode 100644 Core/src/main/java/allchive/server/core/error/BaseErrorCode.java create mode 100644 Core/src/main/java/allchive/server/core/error/BaseErrorException.java create mode 100644 Core/src/main/java/allchive/server/core/error/ErrorResponse.java create mode 100644 Core/src/main/java/allchive/server/core/error/GlobalErrorCode.java create mode 100644 Core/src/main/java/allchive/server/core/error/exception/.gitkeep diff --git a/Api/src/main/java/allchive/server/api/config/GlobalExceptionHandler.java b/Api/src/main/java/allchive/server/api/config/GlobalExceptionHandler.java new file mode 100644 index 00000000..7f13e95f --- /dev/null +++ b/Api/src/main/java/allchive/server/api/config/GlobalExceptionHandler.java @@ -0,0 +1,106 @@ +package allchive.server.api.config; + +import allchive.server.core.dto.ErrorReason; +import allchive.server.core.error.BaseErrorException; +import allchive.server.core.error.ErrorResponse; +import allchive.server.core.error.GlobalErrorCode; +import jakarta.servlet.http.HttpServletRequest; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.HttpStatusCode; +import org.springframework.http.ResponseEntity; +import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.lang.Nullable; +import org.springframework.validation.FieldError; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.context.request.WebRequest; +import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR; + +@Slf4j +@RestControllerAdvice +public class GlobalExceptionHandler extends ResponseEntityExceptionHandler { + @Override + protected ResponseEntity handleExceptionInternal( + Exception ex, + @Nullable Object body, + HttpHeaders headers, + HttpStatusCode statusCode, + WebRequest request) { + log.error("HandleInternalException", ex); + final HttpStatus status = (HttpStatus) statusCode; + final ErrorReason errorReason = + ErrorReason.of(status.value(), status.name(), ex.getMessage()); + final ErrorResponse errorResponse = ErrorResponse.from(errorReason); + return super.handleExceptionInternal(ex, errorResponse, headers, status, request); + } + + @Nullable + @Override + protected ResponseEntity handleMethodArgumentNotValid( + MethodArgumentNotValidException ex, + HttpHeaders headers, + HttpStatusCode status, + WebRequest request) { + final HttpStatus httpStatus = (HttpStatus) status; + final List errors = ex.getBindingResult().getFieldErrors(); + final Map fieldAndErrorMessages = + errors.stream() + .collect( + Collectors.toMap( + FieldError::getField, FieldError::getDefaultMessage)); + final String errorsToJsonString = + fieldAndErrorMessages.entrySet().stream() + .map(e -> e.getKey() + " : " + e.getValue()) + .collect(Collectors.joining("|")); + final ErrorReason errorReason = + ErrorReason.of(status.value(), httpStatus.name(), errorsToJsonString); + final ErrorResponse errorResponse = ErrorResponse.from(errorReason); + return ResponseEntity.status(HttpStatus.valueOf(errorReason.getStatus())) + .body(errorResponse); + } + + @Nullable + @Override + protected ResponseEntity handleHttpMessageNotReadable( + HttpMessageNotReadableException ex, + HttpHeaders headers, + HttpStatusCode status, + WebRequest request) { + log.error("HttpMessageNotReadableException", ex); + final GlobalErrorCode globalErrorCode = GlobalErrorCode.HTTP_MESSAGE_NOT_READABLE; + final ErrorReason errorReason = globalErrorCode.getErrorReason(); + final ErrorResponse errorResponse = ErrorResponse.from(errorReason); + return ResponseEntity.status(INTERNAL_SERVER_ERROR).body(errorResponse); + } + + // 비즈니스 로직 에러 처리 + @ExceptionHandler(BaseErrorException.class) + public ResponseEntity handleBaseErrorException( + BaseErrorException e, HttpServletRequest request) { + log.error("BaseErrorException", e); + final ErrorReason errorReason = e.getErrorCode().getErrorReason(); + final ErrorResponse errorResponse = ErrorResponse.from(errorReason); + return ResponseEntity.status(HttpStatus.valueOf(errorReason.getStatus())) + .body(errorResponse); + } + + // 위에서 따로 처리하지 않은 에러를 모두 처리해줍니다. + @ExceptionHandler(Exception.class) + protected ResponseEntity handleException( + Exception e, HttpServletRequest request) { + log.error("Exception", e); + final GlobalErrorCode globalErrorCode = GlobalErrorCode._INTERNAL_SERVER_ERROR; + final ErrorReason errorReason = globalErrorCode.getErrorReason(); + final ErrorResponse errorResponse = ErrorResponse.from(errorReason); + return ResponseEntity.status(INTERNAL_SERVER_ERROR).body(errorResponse); + } +} diff --git a/Api/src/main/java/allchive/server/api/config/response/ResponseAdviser.java b/Api/src/main/java/allchive/server/api/config/response/ResponseAdviser.java new file mode 100644 index 00000000..3339996e --- /dev/null +++ b/Api/src/main/java/allchive/server/api/config/response/ResponseAdviser.java @@ -0,0 +1,63 @@ +package allchive.server.api.config.response; + +import jakarta.annotation.Nullable; +import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.springframework.core.MethodParameter; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.server.ServerHttpRequest; +import org.springframework.http.server.ServerHttpResponse; +import org.springframework.http.server.ServletServerHttpRequest; +import org.springframework.http.server.ServletServerHttpResponse; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; +import org.springframework.web.util.ContentCachingRequestWrapper; + +import static allchive.server.core.consts.AllchiveConst.SwaggerPatterns; + +@Slf4j +@RestControllerAdvice +public class ResponseAdviser implements ResponseBodyAdvice { + + @Override + public boolean supports( + MethodParameter returnType, Class> converterType) { + return true; + } + + @Nullable + public Object beforeBodyWrite( + Object body, + MethodParameter returnType, + MediaType selectedContentType, + Class selectedConverterType, + ServerHttpRequest request, + ServerHttpResponse response) { + HttpServletResponse servletResponse = + ((ServletServerHttpResponse) response).getServletResponse(); + ContentCachingRequestWrapper servletRequest = + new ContentCachingRequestWrapper( + ((ServletServerHttpRequest) request).getServletRequest()); + + for (String swaggerPattern : SwaggerPatterns) { + if (servletRequest.getRequestURL().toString().contains(swaggerPattern)) return body; + } + + HttpStatus resolve = HttpStatus.resolve(servletResponse.getStatus()); + + if (resolve == null) return body; + + if (resolve.is2xxSuccessful()) + return SuccessResponse.onSuccess(statusProvider(servletRequest.getMethod()), body); + + return body; + } + + private int statusProvider(String method) { + if (method.equals("POST")) return 201; + if (method.equals("DELETE")) return 204; + return 200; + } +} diff --git a/Api/src/main/java/allchive/server/api/config/response/SuccessResponse.java b/Api/src/main/java/allchive/server/api/config/response/SuccessResponse.java new file mode 100644 index 00000000..5c8d0ff1 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/config/response/SuccessResponse.java @@ -0,0 +1,35 @@ +package allchive.server.api.config.response; + + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Builder; +import lombok.Getter; + +@Getter +public class SuccessResponse { + @JsonProperty("status") + private int code; + + @JsonProperty("message") + private String message; + + @JsonProperty("data") + @JsonInclude(JsonInclude.Include.NON_NULL) + private T data; + + @Builder + private SuccessResponse(int code, String message, T data) { + this.code = code; + this.message = message; + this.data = data; + } + + public static SuccessResponse onSuccess(int code) { + return SuccessResponse.builder().code(code).message("요청에 성공하였습니다.").data(null).build(); + } + + public static SuccessResponse onSuccess(int code, T data) { + return SuccessResponse.builder().code(code).message("요청에 성공하였습니다.").data(data).build(); + } +} diff --git a/Core/src/main/java/allchive/server/core/dto/ErrorReason.java b/Core/src/main/java/allchive/server/core/dto/ErrorReason.java new file mode 100644 index 00000000..34cb5075 --- /dev/null +++ b/Core/src/main/java/allchive/server/core/dto/ErrorReason.java @@ -0,0 +1,23 @@ +package allchive.server.core.dto; + + +import lombok.Builder; +import lombok.Getter; + +@Getter +public class ErrorReason { + private final int status; + private final String code; + private final String reason; + + @Builder + private ErrorReason(int status, String code, String reason) { + this.status = status; + this.code = code; + this.reason = reason; + } + + public static ErrorReason of(int status, String code, String reason) { + return ErrorReason.builder().status(status).code(code).reason(reason).build(); + } +} diff --git a/Core/src/main/java/allchive/server/core/error/BaseErrorCode.java b/Core/src/main/java/allchive/server/core/error/BaseErrorCode.java new file mode 100644 index 00000000..7cc5707d --- /dev/null +++ b/Core/src/main/java/allchive/server/core/error/BaseErrorCode.java @@ -0,0 +1,8 @@ +package allchive.server.core.error; + + +import allchive.server.core.dto.ErrorReason; + +public interface BaseErrorCode { + public ErrorReason getErrorReason(); +} diff --git a/Core/src/main/java/allchive/server/core/error/BaseErrorException.java b/Core/src/main/java/allchive/server/core/error/BaseErrorException.java new file mode 100644 index 00000000..54a14601 --- /dev/null +++ b/Core/src/main/java/allchive/server/core/error/BaseErrorException.java @@ -0,0 +1,16 @@ +package allchive.server.core.error; + + +import allchive.server.core.dto.ErrorReason; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class BaseErrorException extends RuntimeException { + private BaseErrorCode errorCode; + + public ErrorReason getErrorReason() { + return this.errorCode.getErrorReason(); + } +} diff --git a/Core/src/main/java/allchive/server/core/error/ErrorResponse.java b/Core/src/main/java/allchive/server/core/error/ErrorResponse.java new file mode 100644 index 00000000..58626870 --- /dev/null +++ b/Core/src/main/java/allchive/server/core/error/ErrorResponse.java @@ -0,0 +1,32 @@ +package allchive.server.core.error; + + +import allchive.server.core.dto.ErrorReason; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import lombok.Builder; +import lombok.Getter; + +@Getter +public class ErrorResponse { + private final String timestamp = LocalDateTime.now().format(DateTimeFormatter.ISO_DATE_TIME); + private final boolean success = false; + private String code; + private int status; + private String reason; + + @Builder + private ErrorResponse(String code, int status, String reason) { + this.code = code; + this.status = status; + this.reason = reason; + } + + public static ErrorResponse from(ErrorReason errorReason) { + return ErrorResponse.builder() + .code(errorReason.getCode()) + .status(errorReason.getStatus()) + .reason(errorReason.getReason()) + .build(); + } +} diff --git a/Core/src/main/java/allchive/server/core/error/GlobalErrorCode.java b/Core/src/main/java/allchive/server/core/error/GlobalErrorCode.java new file mode 100644 index 00000000..aa0bd707 --- /dev/null +++ b/Core/src/main/java/allchive/server/core/error/GlobalErrorCode.java @@ -0,0 +1,37 @@ +package allchive.server.core.error; + +import static allchive.server.core.consts.AllchiveConst.*; + +import allchive.server.core.dto.ErrorReason; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum GlobalErrorCode implements BaseErrorCode { + + /* global error */ + HTTP_MESSAGE_NOT_READABLE(BAD_REQUEST, "GLOBAL_400_1", "잘못된 형식의 값을 입력했습니다."), + _INTERNAL_SERVER_ERROR(INTERNAL_SERVER, "GLOBAL_500_1", "서버 오류. 관리자에게 문의 부탁드립니다."), + + /*토큰 에러*/ + NO_TOKEN(UNAUTHORIZED, "AUTH_401_1", "토큰이 존재하지 않습니다"), + INVALID_AUTH_TOKEN(UNAUTHORIZED, "AUTH_401_2", "액세스 토큰이 유효하지 않습니다"), + EXPIRED_TOKEN(UNAUTHORIZED, "AUTH_401_3", "만료된 엑세스 토큰입니다"), + INVALID_REFRESH_TOKEN(UNAUTHORIZED, "AUTH_401_4", "리프레시 토큰이 유효하지 않습니다"), + EXPIRED_REFRESH_TOKEN(UNAUTHORIZED, "AUTH_401_5", "만료된 리프레시 토큰입니다"), + MISMATCH_REFRESH_TOKEN(UNAUTHORIZED, "AUTH_401_6", "리프레시 토큰의 유저 정보가 일치하지 않습니다"), + FORBIDDEN_ADMIN(FORBIDDEN, "AUTH_403_1", "권한이 부여되지 않은 사용자입니다"), + UNSUPPORTED_TOKEN(UNAUTHORIZED, "AUTH_401_7", "지원하지 않는 토큰입니다"), + INVALID_SIGNATURE(UNAUTHORIZED, "AUTH_401_8", "잘못된 JWT 서명입니다"), + ; + + private int status; + private String code; + private String reason; + + @Override + public ErrorReason getErrorReason() { + return ErrorReason.of(status, code, reason); + } +} diff --git a/Core/src/main/java/allchive/server/core/error/exception/.gitkeep b/Core/src/main/java/allchive/server/core/error/exception/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/Domain/build.gradle b/Domain/build.gradle index b7ac5ee1..c2a1be2b 100644 --- a/Domain/build.gradle +++ b/Domain/build.gradle @@ -5,6 +5,8 @@ dependencies { api 'org.springframework.boot:spring-boot-starter-data-jpa' api 'mysql:mysql-connector-java:8.0.33' + runtimeOnly 'com.h2database:h2' + //QueryDsl implementation "com.querydsl:querydsl-jpa:5.0.0:jakarta" annotationProcessor "com.querydsl:querydsl-apt:5.0.0:jakarta" diff --git a/Domain/src/main/resources/application-domain.yml b/Domain/src/main/resources/application-domain.yml index fcd54dfb..24afd8a0 100644 --- a/Domain/src/main/resources/application-domain.yml +++ b/Domain/src/main/resources/application-domain.yml @@ -19,6 +19,7 @@ spring: defer-datasource-initialization: true open-in-view: false database-platform: org.hibernate.dialect.MySQL5InnoDBDialect + database: mysql --- # dev From 56d3b8cedc6d6a7e4fedb5db070e03723c346761 Mon Sep 17 00:00:00 2001 From: wjdtkdgns Date: Tue, 27 Jun 2023 00:55:27 +0900 Subject: [PATCH 03/41] =?UTF-8?q?[feat]=20=EC=8A=A4=EC=9B=A8=EA=B1=B0=20?= =?UTF-8?q?=EC=85=8B=ED=8C=85,=20security=20=EA=B8=B0=EB=B3=B8=20=EC=85=8B?= =?UTF-8?q?=ED=8C=85=20#1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Api/build.gradle | 8 +-- .../java/allchive/server/ApiApplication.java | 1 - .../server/api/config/SwaggerConfig.java | 50 +++++++++++++++++++ .../api/config/security/CorsConfig.java | 28 +++++++++++ .../api/config/security/SecurityConfig.java | 34 +++++++++++++ .../server/api/example/ExampleController.java | 36 +++++++++++++ .../allchive/server/api/example/dto/.keep | 0 .../allchive/server/api/example/service/.keep | 0 Api/src/main/resources/application-api.yml | 14 +++++- Api/src/main/resources/application.yml | 9 ++++ Core/build.gradle | 2 +- build.gradle | 4 +- 12 files changed, 179 insertions(+), 7 deletions(-) create mode 100644 Api/src/main/java/allchive/server/api/config/SwaggerConfig.java create mode 100644 Api/src/main/java/allchive/server/api/config/security/CorsConfig.java create mode 100644 Api/src/main/java/allchive/server/api/config/security/SecurityConfig.java create mode 100644 Api/src/main/java/allchive/server/api/example/ExampleController.java create mode 100644 Api/src/main/java/allchive/server/api/example/dto/.keep create mode 100644 Api/src/main/java/allchive/server/api/example/service/.keep create mode 100644 Api/src/main/resources/application.yml diff --git a/Api/build.gradle b/Api/build.gradle index 46ddfe76..f11be552 100644 --- a/Api/build.gradle +++ b/Api/build.gradle @@ -13,9 +13,11 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-validation' implementation 'org.springframework.boot:spring-boot-starter-security' - implementation 'org.springdoc:springdoc-openapi-ui:1.6.12' - testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.2' - testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.2' + + // swagger + implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.4' + implementation 'org.springdoc:springdoc-openapi-starter-webmvc-api:2.0.4' + implementation project(':Domain') implementation project(':Core') implementation project(':Infrastructure') diff --git a/Api/src/main/java/allchive/server/ApiApplication.java b/Api/src/main/java/allchive/server/ApiApplication.java index cfd8dc67..cc95498b 100644 --- a/Api/src/main/java/allchive/server/ApiApplication.java +++ b/Api/src/main/java/allchive/server/ApiApplication.java @@ -7,7 +7,6 @@ @SpringBootApplication public class ApiApplication { public static void main(String[] args) { - System.out.println("Hello world!"); SpringApplication.run(ApiApplication.class, args); } } diff --git a/Api/src/main/java/allchive/server/api/config/SwaggerConfig.java b/Api/src/main/java/allchive/server/api/config/SwaggerConfig.java new file mode 100644 index 00000000..d7c56440 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/config/SwaggerConfig.java @@ -0,0 +1,50 @@ +package allchive.server.api.config; + + +import io.swagger.v3.oas.models.Components; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.info.License; +import io.swagger.v3.oas.models.security.SecurityScheme; +import io.swagger.v3.oas.models.security.SecurityScheme.In; +import io.swagger.v3.oas.models.security.SecurityScheme.Type; +import io.swagger.v3.oas.models.servers.Server; +import jakarta.servlet.ServletContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.List; + +@Configuration +public class SwaggerConfig { + @Bean + public OpenAPI openAPI(ServletContext servletContext) { + String contextPath = servletContext.getContextPath(); + Server server = new Server().url(contextPath); + return new OpenAPI().servers(List.of(server)).components(authSetting()).info(swaggerInfo()); + } + + private Info swaggerInfo() { + License license = new License(); + license.setUrl("https://github.com/Central-MakeUs"); + license.setName("Dachive"); + + return new Info() + .version("v0.0.1") + .title("\"Dachive 서버 API문서\"") + .description("Dachive 서버의 API 문서 입니다.") + .license(license); + } + + private Components authSetting() { + return new Components() + .addSecuritySchemes( + "access-token", + new SecurityScheme() + .type(Type.HTTP) + .scheme("bearer") + .bearerFormat("JWT") + .in(In.HEADER) + .name("Authorization")); + } +} diff --git a/Api/src/main/java/allchive/server/api/config/security/CorsConfig.java b/Api/src/main/java/allchive/server/api/config/security/CorsConfig.java new file mode 100644 index 00000000..6728e41d --- /dev/null +++ b/Api/src/main/java/allchive/server/api/config/security/CorsConfig.java @@ -0,0 +1,28 @@ +package allchive.server.api.config.security; + +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import java.util.ArrayList; + +@Configuration +@RequiredArgsConstructor +public class CorsConfig implements WebMvcConfigurer { + + @Override + public void addCorsMappings(CorsRegistry registry) { + ArrayList allowedOriginPatterns = new ArrayList<>(); + allowedOriginPatterns.add("http://localhost:3000"); + allowedOriginPatterns.add("http://localhost:8080"); + allowedOriginPatterns.add("https://www.allchive.co.kr"); + + String[] patterns = allowedOriginPatterns.toArray(String[]::new); + registry.addMapping("/**") + .allowedMethods("*") + .allowedOriginPatterns(patterns) + .allowCredentials(true) + .maxAge(3600L); + } +} \ No newline at end of file diff --git a/Api/src/main/java/allchive/server/api/config/security/SecurityConfig.java b/Api/src/main/java/allchive/server/api/config/security/SecurityConfig.java new file mode 100644 index 00000000..e9b3d59b --- /dev/null +++ b/Api/src/main/java/allchive/server/api/config/security/SecurityConfig.java @@ -0,0 +1,34 @@ +package allchive.server.api.config.security; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.web.SecurityFilterChain; + +@Configuration +@EnableWebSecurity +public class SecurityConfig { + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http + .formLogin() + .disable() + + .cors() + .disable() + + .csrf() + .disable() + + .httpBasic() + .disable() + + .sessionManagement() + .sessionCreationPolicy(SessionCreationPolicy.NEVER); + + return http.build(); + } +} \ No newline at end of file diff --git a/Api/src/main/java/allchive/server/api/example/ExampleController.java b/Api/src/main/java/allchive/server/api/example/ExampleController.java new file mode 100644 index 00000000..3bf9397e --- /dev/null +++ b/Api/src/main/java/allchive/server/api/example/ExampleController.java @@ -0,0 +1,36 @@ +package allchive.server.api.example; + + +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping(value = "/example") +@RequiredArgsConstructor +public class ExampleController { + + @GetMapping(value = "/responseGet") + public int responseGet() { + return 1; + } + + @PostMapping(value = "/responsePost") + public int responsePost() { + return 1; + } + + @PutMapping(value = "/responsePut") + public int responsePut() { + return 1; + } + + @PatchMapping(value = "/responsePatch") + public int responsePatch() { + return 1; + } + + @DeleteMapping(value = "/responseDelete") + public int responseDelete() { + return 1; + } +} diff --git a/Api/src/main/java/allchive/server/api/example/dto/.keep b/Api/src/main/java/allchive/server/api/example/dto/.keep new file mode 100644 index 00000000..e69de29b diff --git a/Api/src/main/java/allchive/server/api/example/service/.keep b/Api/src/main/java/allchive/server/api/example/service/.keep new file mode 100644 index 00000000..e69de29b diff --git a/Api/src/main/resources/application-api.yml b/Api/src/main/resources/application-api.yml index c348dcd6..c8236523 100644 --- a/Api/src/main/resources/application-api.yml +++ b/Api/src/main/resources/application-api.yml @@ -6,4 +6,16 @@ spring: - infrastructure mvc: pathmatch: - matching-strategy: ant_path_matcher \ No newline at end of file + matching-strategy: ant_path_matcher + +springdoc: + default-consumes-media-type: application/json + default-produces-media-type: application/json + swagger-ui: + tags-sorter: alpha + +--- +spring: + config: + activate: + on-profile: dev \ No newline at end of file diff --git a/Api/src/main/resources/application.yml b/Api/src/main/resources/application.yml new file mode 100644 index 00000000..91ebeeea --- /dev/null +++ b/Api/src/main/resources/application.yml @@ -0,0 +1,9 @@ +server: + servlet: + context-path: /api + forward-headers-strategy: framework + +spring: + profiles: + include: + -api \ No newline at end of file diff --git a/Core/build.gradle b/Core/build.gradle index cd78a759..e5f6f7e2 100644 --- a/Core/build.gradle +++ b/Core/build.gradle @@ -2,7 +2,7 @@ bootJar.enabled = false jar.enabled = true dependencies { - implementation 'org.springframework.boot:spring-boot-starter-security' +// implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.springframework.boot:spring-boot-starter-validation' testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.2' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.2' diff --git a/build.gradle b/build.gradle index 13285fe9..24f1d1e6 100644 --- a/build.gradle +++ b/build.gradle @@ -33,10 +33,12 @@ subprojects { } dependencies { - implementation 'org.springframework.boot:spring-boot-starter' compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' + annotationProcessor "org.springframework.boot:spring-boot-configuration-processor" testImplementation 'org.springframework.boot:spring-boot-starter-test' + testCompileOnly 'org.projectlombok:lombok' + testAnnotationProcessor 'org.projectlombok:lombok' } test { From f76bea9560da08214f28a34525b7009c5749e9e7 Mon Sep 17 00:00:00 2001 From: wjdtkdgns Date: Tue, 27 Jun 2023 00:57:08 +0900 Subject: [PATCH 04/41] =?UTF-8?q?[chore]=20git=20template=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/ISSUE_TEMPLATE/issue--user-side-qa.md | 18 ---------------- .github/PULL_REQUEST_TEMPLATE.md | 21 +++++++------------ 2 files changed, 8 insertions(+), 31 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/issue--user-side-qa.md diff --git a/.github/ISSUE_TEMPLATE/issue--user-side-qa.md b/.github/ISSUE_TEMPLATE/issue--user-side-qa.md deleted file mode 100644 index 790a637e..00000000 --- a/.github/ISSUE_TEMPLATE/issue--user-side-qa.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -name: 'Issue: User-side QA' -about: UI/UX에 대한 QA 이슈 템플릿 -title: "[UI/UX]" -labels: '' -assignees: '' - ---- - -# 🤖 QA 개요 - - -### ✅ Resolve TODO - -- [ ] - -### 📚 Remarks - diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 323bb4d2..7b9b4f1f 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,15 +1,10 @@ -## 📝 PR Summary +# 💡 PR Summary - + +## 개요 +- close #issueNumber - +## 작업사항 +- 내용을 적어주세요. -#### 🌲 Working Branch - - - -#### 🌲 TODOs - - - -### Related Issues - - +## 변경로직 +- 내용을 적어주세요. From 7e121d4751a79115d59fb695aa6aca8e7239f1ed Mon Sep 17 00:00:00 2001 From: Sanghoon Jeong <67852689+wjdtkdgns@users.noreply.github.com> Date: Thu, 29 Jun 2023 00:02:12 +0900 Subject: [PATCH 05/41] =?UTF-8?q?[feat]=20=EC=97=94=ED=8B=B0=ED=8B=B0=20?= =?UTF-8?q?=EC=84=A4=EA=B3=84=20=EB=B0=8F=20spring=20=EB=B2=84=EC=A0=84=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20(#7)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [feat] block entity 설계 #5 * [feat] user + 나머지 entity 설계 #5 * [feat] spring version 3.0.8 -> 2.7.13 --- Api/build.gradle | 5 +- .../api/config/GlobalExceptionHandler.java | 9 ++- .../server/api/config/SwaggerConfig.java | 2 +- .../api/config/response/ResponseAdviser.java | 6 +- Api/src/main/resources/application-api.yml | 21 ------ Api/src/main/resources/application.yml | 19 ++++- .../server/core/annotation/Adaptor.java | 16 +++++ .../server/core/annotation/DomainService.java | 16 +++++ .../server/core/annotation/Helper.java | 16 +++++ .../server/core/annotation/UseCase.java | 16 +++++ .../server/core/annotation/Validator.java | 16 +++++ Domain/build.gradle | 35 ++++++---- .../domain/common/model/QBaseTimeEntity.java | 39 +++++++++++ .../domain/domains/block/domain/QBlock.java | 53 ++++++++++++++ .../domains/category/domain/QCategory.java | 45 ++++++++++++ .../domains/category/domain/QTitle.java | 45 ++++++++++++ .../domains/content/domain/QContent.java | 45 ++++++++++++ .../content/domain/QContentTagGroup.java | 45 ++++++++++++ .../domain/domains/content/domain/QTag.java | 45 ++++++++++++ .../domains/recycle/domain/QRecycle.java | 45 ++++++++++++ .../domain/domains/report/domain/QReport.java | 45 ++++++++++++ .../domain/domains/user/domain/QScrap.java | 45 ++++++++++++ .../domain/domains/user/domain/QUser.java | 69 +++++++++++++++++++ .../domains/user/domain/QUserTopicGroup.java | 45 ++++++++++++ .../domains/user/domain/enums/QFormInfo.java | 41 +++++++++++ .../domains/user/domain/enums/QOauthInfo.java | 39 +++++++++++ .../domain/common/model/BaseTimeEntity.java | 26 +++++++ .../server/domain/config/QueryDslConfig.java | 8 ++- .../domains/block/adaptor/BlockAdaptor.java | 9 +++ .../domain/domains/block/domain/Block.java | 34 +++++++++ .../domains/block/domain/enums/BlockType.java | 6 ++ .../block/exception/BlockErrorCode.java | 22 ++++++ .../block/repository/BlockRepository.java | 7 ++ .../domains/block/service/BlockService.java | 7 ++ .../block/validator/BlockValidator.java | 7 ++ .../category/adaptor/CategoryAdaptor.java | 9 +++ .../category/adaptor/TitleAdaptor.java | 9 +++ .../domains/category/domain/Category.java | 18 +++++ .../domain/domains/category/domain/Title.java | 18 +++++ .../domains/category/domain/enums/.gitkeep | 0 .../category/exception/CategoryErrorCode.java | 22 ++++++ .../category/exception/TitleErrorCode.java | 22 ++++++ .../repository/CategoryRepository.java | 7 ++ .../category/repository/TitleRepository.java | 8 +++ .../category/service/CategoryService.java | 7 ++ .../category/service/TitleService.java | 7 ++ .../category/validator/CategoryValidator.java | 7 ++ .../category/validator/TitleValidator.java | 7 ++ .../content/adaptor/ContentAdaptor.java | 9 +++ .../adaptor/ContentTagGroupAdaptor.java | 9 +++ .../domains/content/adaptor/TagAdaptor.java | 9 +++ .../domains/content/domain/Content.java | 19 +++++ .../content/domain/ContentTagGroup.java | 19 +++++ .../domain/domains/content/domain/Tag.java | 18 +++++ .../domains/content/domain/enums/.gitkeep | 0 .../content/exception/ContentErrorCode.java | 22 ++++++ .../content/repository/ContentRepository.java | 7 ++ .../repository/ContentTagGroupRepository.java | 7 ++ .../content/repository/TagRepository.java | 7 ++ .../content/service/ContentService.java | 7 ++ .../service/ContentTagGroupService.java | 7 ++ .../domains/content/service/TagService.java | 7 ++ .../content/validator/ContentValidator.java | 7 ++ .../content/validator/TagValidator.java | 7 ++ .../recycle/adaptor/RecycleAdaptor.java | 9 +++ .../domains/recycle/domain/Recycle.java | 18 +++++ .../domains/recycle/domain/enums/.gitkeep | 0 .../recycle/exception/RecycleErrorCode.java | 22 ++++++ .../recycle/repository/RecycleRepository.java | 7 ++ .../recycle/service/RecycleService.java | 7 ++ .../recycle/validator/RecycleValidator.java | 7 ++ .../domains/report/adaptor/ReportAdaptor.java | 9 +++ .../domain/domains/report/domain/Report.java | 19 +++++ .../domains/report/domain/enums/.gitkeep | 0 .../report/exception/ReportErrorCode.java | 22 ++++++ .../report/repository/ReportRepository.java | 7 ++ .../domains/report/service/ReportService.java | 7 ++ .../report/validator/ReportValidator.java | 7 ++ .../domains/user/adaptor/ScrapAdaptor.java | 9 +++ .../domains/user/adaptor/UserAdaptor.java | 9 +++ .../user/adaptor/UserTopicAdaptor.java | 9 +++ .../domain/domains/user/domain/Scrap.java | 18 +++++ .../domain/domains/user/domain/User.java | 37 ++++++++++ .../domains/user/domain/UserTopicGroup.java | 19 +++++ .../domains/user/domain/enums/FormInfo.java | 16 +++++ .../domains/user/domain/enums/OauthInfo.java | 35 ++++++++++ .../user/domain/enums/OauthProvider.java | 13 ++++ .../domains/user/domain/enums/UserState.java | 19 +++++ .../user/exception/ScrapErrorCode.java | 22 ++++++ .../domains/user/exception/UserErrorCode.java | 22 ++++++ .../user/repository/ScrapRepository.java | 7 ++ .../user/repository/UserRepository.java | 7 ++ .../repository/UserTopicGroupRepository.java | 7 ++ .../domains/user/service/ScrapService.java | 7 ++ .../domains/user/service/UserService.java | 7 ++ .../user/service/UserTopicGroupService.java | 7 ++ .../user/validator/ScrapValidator.java | 7 ++ .../domains/user/validator/UserValidator.java | 7 ++ Infrastructure/build.gradle | 2 - build.gradle | 2 +- 100 files changed, 1627 insertions(+), 53 deletions(-) delete mode 100644 Api/src/main/resources/application-api.yml create mode 100644 Core/src/main/java/allchive/server/core/annotation/Adaptor.java create mode 100644 Core/src/main/java/allchive/server/core/annotation/DomainService.java create mode 100644 Core/src/main/java/allchive/server/core/annotation/Helper.java create mode 100644 Core/src/main/java/allchive/server/core/annotation/UseCase.java create mode 100644 Core/src/main/java/allchive/server/core/annotation/Validator.java create mode 100644 Domain/src/main/generated/allchive/server/domain/common/model/QBaseTimeEntity.java create mode 100644 Domain/src/main/generated/allchive/server/domain/domains/block/domain/QBlock.java create mode 100644 Domain/src/main/generated/allchive/server/domain/domains/category/domain/QCategory.java create mode 100644 Domain/src/main/generated/allchive/server/domain/domains/category/domain/QTitle.java create mode 100644 Domain/src/main/generated/allchive/server/domain/domains/content/domain/QContent.java create mode 100644 Domain/src/main/generated/allchive/server/domain/domains/content/domain/QContentTagGroup.java create mode 100644 Domain/src/main/generated/allchive/server/domain/domains/content/domain/QTag.java create mode 100644 Domain/src/main/generated/allchive/server/domain/domains/recycle/domain/QRecycle.java create mode 100644 Domain/src/main/generated/allchive/server/domain/domains/report/domain/QReport.java create mode 100644 Domain/src/main/generated/allchive/server/domain/domains/user/domain/QScrap.java create mode 100644 Domain/src/main/generated/allchive/server/domain/domains/user/domain/QUser.java create mode 100644 Domain/src/main/generated/allchive/server/domain/domains/user/domain/QUserTopicGroup.java create mode 100644 Domain/src/main/generated/allchive/server/domain/domains/user/domain/enums/QFormInfo.java create mode 100644 Domain/src/main/generated/allchive/server/domain/domains/user/domain/enums/QOauthInfo.java create mode 100644 Domain/src/main/java/allchive/server/domain/common/model/BaseTimeEntity.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/block/adaptor/BlockAdaptor.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/block/domain/Block.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/block/domain/enums/BlockType.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/block/exception/BlockErrorCode.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/block/repository/BlockRepository.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/block/service/BlockService.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/block/validator/BlockValidator.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/category/adaptor/CategoryAdaptor.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/category/adaptor/TitleAdaptor.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/category/domain/Category.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/category/domain/Title.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/category/domain/enums/.gitkeep create mode 100644 Domain/src/main/java/allchive/server/domain/domains/category/exception/CategoryErrorCode.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/category/exception/TitleErrorCode.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/category/repository/CategoryRepository.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/category/repository/TitleRepository.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/category/service/CategoryService.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/category/service/TitleService.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/category/validator/CategoryValidator.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/category/validator/TitleValidator.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentAdaptor.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentTagGroupAdaptor.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/content/adaptor/TagAdaptor.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/content/domain/Content.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/content/domain/ContentTagGroup.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/content/domain/Tag.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/content/domain/enums/.gitkeep create mode 100644 Domain/src/main/java/allchive/server/domain/domains/content/exception/ContentErrorCode.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentRepository.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentTagGroupRepository.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/content/repository/TagRepository.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/content/service/ContentService.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/content/service/ContentTagGroupService.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/content/service/TagService.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/content/validator/ContentValidator.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/content/validator/TagValidator.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/recycle/adaptor/RecycleAdaptor.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/recycle/domain/Recycle.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/recycle/domain/enums/.gitkeep create mode 100644 Domain/src/main/java/allchive/server/domain/domains/recycle/exception/RecycleErrorCode.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/recycle/repository/RecycleRepository.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/recycle/service/RecycleService.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/recycle/validator/RecycleValidator.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/report/adaptor/ReportAdaptor.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/report/domain/Report.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/report/domain/enums/.gitkeep create mode 100644 Domain/src/main/java/allchive/server/domain/domains/report/exception/ReportErrorCode.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/report/repository/ReportRepository.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/report/service/ReportService.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/report/validator/ReportValidator.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/user/adaptor/ScrapAdaptor.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/user/adaptor/UserAdaptor.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/user/adaptor/UserTopicAdaptor.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/user/domain/Scrap.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/user/domain/User.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/user/domain/UserTopicGroup.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/user/domain/enums/FormInfo.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/user/domain/enums/OauthInfo.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/user/domain/enums/OauthProvider.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/user/domain/enums/UserState.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/user/exception/ScrapErrorCode.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/user/exception/UserErrorCode.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/user/repository/ScrapRepository.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/user/repository/UserRepository.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/user/repository/UserTopicGroupRepository.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/user/service/ScrapService.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/user/service/UserService.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/user/service/UserTopicGroupService.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/user/validator/ScrapValidator.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/user/validator/UserValidator.java diff --git a/Api/build.gradle b/Api/build.gradle index f11be552..92930493 100644 --- a/Api/build.gradle +++ b/Api/build.gradle @@ -15,8 +15,9 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-security' // swagger - implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.4' - implementation 'org.springdoc:springdoc-openapi-starter-webmvc-api:2.0.4' +// implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.4' +// implementation 'org.springdoc:springdoc-openapi-starter-webmvc-api:2.0.4' + implementation 'org.springdoc:springdoc-openapi-ui:1.6.12' implementation project(':Domain') implementation project(':Core') diff --git a/Api/src/main/java/allchive/server/api/config/GlobalExceptionHandler.java b/Api/src/main/java/allchive/server/api/config/GlobalExceptionHandler.java index 7f13e95f..7c628ed4 100644 --- a/Api/src/main/java/allchive/server/api/config/GlobalExceptionHandler.java +++ b/Api/src/main/java/allchive/server/api/config/GlobalExceptionHandler.java @@ -4,11 +4,9 @@ import allchive.server.core.error.BaseErrorException; import allchive.server.core.error.ErrorResponse; import allchive.server.core.error.GlobalErrorCode; -import jakarta.servlet.http.HttpServletRequest; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; -import org.springframework.http.HttpStatusCode; import org.springframework.http.ResponseEntity; import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.lang.Nullable; @@ -19,6 +17,7 @@ import org.springframework.web.context.request.WebRequest; import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; +import javax.servlet.http.HttpServletRequest; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -33,7 +32,7 @@ protected ResponseEntity handleExceptionInternal( Exception ex, @Nullable Object body, HttpHeaders headers, - HttpStatusCode statusCode, + HttpStatus statusCode, WebRequest request) { log.error("HandleInternalException", ex); final HttpStatus status = (HttpStatus) statusCode; @@ -48,7 +47,7 @@ protected ResponseEntity handleExceptionInternal( protected ResponseEntity handleMethodArgumentNotValid( MethodArgumentNotValidException ex, HttpHeaders headers, - HttpStatusCode status, + HttpStatus status, WebRequest request) { final HttpStatus httpStatus = (HttpStatus) status; final List errors = ex.getBindingResult().getFieldErrors(); @@ -73,7 +72,7 @@ protected ResponseEntity handleMethodArgumentNotValid( protected ResponseEntity handleHttpMessageNotReadable( HttpMessageNotReadableException ex, HttpHeaders headers, - HttpStatusCode status, + HttpStatus status, WebRequest request) { log.error("HttpMessageNotReadableException", ex); final GlobalErrorCode globalErrorCode = GlobalErrorCode.HTTP_MESSAGE_NOT_READABLE; diff --git a/Api/src/main/java/allchive/server/api/config/SwaggerConfig.java b/Api/src/main/java/allchive/server/api/config/SwaggerConfig.java index d7c56440..62e963ed 100644 --- a/Api/src/main/java/allchive/server/api/config/SwaggerConfig.java +++ b/Api/src/main/java/allchive/server/api/config/SwaggerConfig.java @@ -9,10 +9,10 @@ import io.swagger.v3.oas.models.security.SecurityScheme.In; import io.swagger.v3.oas.models.security.SecurityScheme.Type; import io.swagger.v3.oas.models.servers.Server; -import jakarta.servlet.ServletContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import javax.servlet.ServletContext; import java.util.List; @Configuration diff --git a/Api/src/main/java/allchive/server/api/config/response/ResponseAdviser.java b/Api/src/main/java/allchive/server/api/config/response/ResponseAdviser.java index 3339996e..237aeb74 100644 --- a/Api/src/main/java/allchive/server/api/config/response/ResponseAdviser.java +++ b/Api/src/main/java/allchive/server/api/config/response/ResponseAdviser.java @@ -1,7 +1,5 @@ package allchive.server.api.config.response; -import jakarta.annotation.Nullable; -import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.core.MethodParameter; import org.springframework.http.HttpStatus; @@ -15,6 +13,8 @@ import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; import org.springframework.web.util.ContentCachingRequestWrapper; +import javax.servlet.http.HttpServletResponse; + import static allchive.server.core.consts.AllchiveConst.SwaggerPatterns; @Slf4j @@ -27,7 +27,7 @@ public boolean supports( return true; } - @Nullable + @Override public Object beforeBodyWrite( Object body, MethodParameter returnType, diff --git a/Api/src/main/resources/application-api.yml b/Api/src/main/resources/application-api.yml deleted file mode 100644 index c8236523..00000000 --- a/Api/src/main/resources/application-api.yml +++ /dev/null @@ -1,21 +0,0 @@ -spring: - profiles: - include: - - domain - - core - - infrastructure - mvc: - pathmatch: - matching-strategy: ant_path_matcher - -springdoc: - default-consumes-media-type: application/json - default-produces-media-type: application/json - swagger-ui: - tags-sorter: alpha - ---- -spring: - config: - activate: - on-profile: dev \ No newline at end of file diff --git a/Api/src/main/resources/application.yml b/Api/src/main/resources/application.yml index 91ebeeea..c4f14e91 100644 --- a/Api/src/main/resources/application.yml +++ b/Api/src/main/resources/application.yml @@ -6,4 +6,21 @@ server: spring: profiles: include: - -api \ No newline at end of file + - domain + - core + - infrastructure + mvc: + pathmatch: + matching-strategy: ant_path_matcher + +springdoc: + default-consumes-media-type: application/json + default-produces-media-type: application/json + swagger-ui: + tags-sorter: alpha + +--- +spring: + config: + activate: + on-profile: dev \ No newline at end of file diff --git a/Core/src/main/java/allchive/server/core/annotation/Adaptor.java b/Core/src/main/java/allchive/server/core/annotation/Adaptor.java new file mode 100644 index 00000000..416925ce --- /dev/null +++ b/Core/src/main/java/allchive/server/core/annotation/Adaptor.java @@ -0,0 +1,16 @@ +package allchive.server.core.annotation; + + +import org.springframework.core.annotation.AliasFor; +import org.springframework.stereotype.Component; + +import java.lang.annotation.*; + +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Component +public @interface Adaptor { + @AliasFor(annotation = Component.class) + String value() default ""; +} diff --git a/Core/src/main/java/allchive/server/core/annotation/DomainService.java b/Core/src/main/java/allchive/server/core/annotation/DomainService.java new file mode 100644 index 00000000..62d0732d --- /dev/null +++ b/Core/src/main/java/allchive/server/core/annotation/DomainService.java @@ -0,0 +1,16 @@ +package allchive.server.core.annotation; + + +import org.springframework.core.annotation.AliasFor; +import org.springframework.stereotype.Component; + +import java.lang.annotation.*; + +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Component +public @interface DomainService { + @AliasFor(annotation = Component.class) + String value() default ""; +} diff --git a/Core/src/main/java/allchive/server/core/annotation/Helper.java b/Core/src/main/java/allchive/server/core/annotation/Helper.java new file mode 100644 index 00000000..f9f5c8de --- /dev/null +++ b/Core/src/main/java/allchive/server/core/annotation/Helper.java @@ -0,0 +1,16 @@ +package allchive.server.core.annotation; + + +import org.springframework.core.annotation.AliasFor; +import org.springframework.stereotype.Component; + +import java.lang.annotation.*; + +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Component +public @interface Helper { + @AliasFor(annotation = Component.class) + String value() default ""; +} diff --git a/Core/src/main/java/allchive/server/core/annotation/UseCase.java b/Core/src/main/java/allchive/server/core/annotation/UseCase.java new file mode 100644 index 00000000..e02c00ac --- /dev/null +++ b/Core/src/main/java/allchive/server/core/annotation/UseCase.java @@ -0,0 +1,16 @@ +package allchive.server.core.annotation; + + +import org.springframework.core.annotation.AliasFor; +import org.springframework.stereotype.Component; + +import java.lang.annotation.*; + +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Component +public @interface UseCase { + @AliasFor(annotation = Component.class) + String value() default ""; +} diff --git a/Core/src/main/java/allchive/server/core/annotation/Validator.java b/Core/src/main/java/allchive/server/core/annotation/Validator.java new file mode 100644 index 00000000..a0c16299 --- /dev/null +++ b/Core/src/main/java/allchive/server/core/annotation/Validator.java @@ -0,0 +1,16 @@ +package allchive.server.core.annotation; + + +import org.springframework.core.annotation.AliasFor; +import org.springframework.stereotype.Component; + +import java.lang.annotation.*; + +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Component +public @interface Validator { + @AliasFor(annotation = Component.class) + String value() default ""; +} diff --git a/Domain/build.gradle b/Domain/build.gradle index c2a1be2b..43ed2263 100644 --- a/Domain/build.gradle +++ b/Domain/build.gradle @@ -2,24 +2,31 @@ bootJar.enabled = false jar.enabled = true dependencies { - api 'org.springframework.boot:spring-boot-starter-data-jpa' - api 'mysql:mysql-connector-java:8.0.33' + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + implementation 'mysql:mysql-connector-java:8.0.33' + implementation 'org.springframework.boot:spring-boot-starter-web' + // TODO : Core에 impl 했는데 여기서 이용 못하는 문제 해결해야함 + implementation 'org.springframework.boot:spring-boot-starter-validation' - runtimeOnly 'com.h2database:h2' - - //QueryDsl - implementation "com.querydsl:querydsl-jpa:5.0.0:jakarta" - annotationProcessor "com.querydsl:querydsl-apt:5.0.0:jakarta" - annotationProcessor "jakarta.annotation:jakarta.annotation-api" - annotationProcessor "jakarta.persistence:jakarta.persistence-api" +// api 'mysql:mysql-connector-java:8.0.32' implementation project(':Core') implementation project(':Infrastructure') -} - -// Querydsl 설정부 -def generated = 'src/main/generated' + //QueryDsl +// implementation "com.querydsl:querydsl-jpa:5.0.0:jakarta" +// annotationProcessor "com.querydsl:querydsl-apt:5.0.0:jakarta" +// annotationProcessor "jakarta.annotation:jakarta.annotation-api" +// annotationProcessor "jakarta.persistence:jakarta.persistence-api" + + api ("com.querydsl:querydsl-core") // querydsl + api ("com.querydsl:querydsl-jpa") // querydsl + annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jpa" // querydsl JPAAnnotationProcessor 사용 지정 + annotationProcessor("jakarta.persistence:jakarta.persistence-api") + annotationProcessor("jakarta.annotation:jakarta.annotation-api") +} +//https://jojoldu.tistory.com/372 +def generated='src/main/generated' sourceSets { main.java.srcDirs += [ generated ] } @@ -30,4 +37,4 @@ tasks.withType(JavaCompile) { clean.doLast { file(generated).deleteDir() -} \ No newline at end of file +} diff --git a/Domain/src/main/generated/allchive/server/domain/common/model/QBaseTimeEntity.java b/Domain/src/main/generated/allchive/server/domain/common/model/QBaseTimeEntity.java new file mode 100644 index 00000000..2aefedf4 --- /dev/null +++ b/Domain/src/main/generated/allchive/server/domain/common/model/QBaseTimeEntity.java @@ -0,0 +1,39 @@ +package allchive.server.domain.common.model; + +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 = 4668206L; + + public static final QBaseTimeEntity baseTimeEntity = new QBaseTimeEntity("baseTimeEntity"); + + public final DateTimePath createdAt = createDateTime("createdAt", java.time.LocalDateTime.class); + + public final DateTimePath updatedAt = createDateTime("updatedAt", 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/Domain/src/main/generated/allchive/server/domain/domains/block/domain/QBlock.java b/Domain/src/main/generated/allchive/server/domain/domains/block/domain/QBlock.java new file mode 100644 index 00000000..c2af9def --- /dev/null +++ b/Domain/src/main/generated/allchive/server/domain/domains/block/domain/QBlock.java @@ -0,0 +1,53 @@ +package allchive.server.domain.domains.block.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; + + +/** + * QBlock is a Querydsl query type for Block + */ +@Generated("com.querydsl.codegen.DefaultEntitySerializer") +public class QBlock extends EntityPathBase { + + private static final long serialVersionUID = 1039614782L; + + public static final QBlock block = new QBlock("block"); + + public final allchive.server.domain.common.model.QBaseTimeEntity _super = new allchive.server.domain.common.model.QBaseTimeEntity(this); + + public final NumberPath blockContent = createNumber("blockContent", Long.class); + + public final NumberPath blockFrom = createNumber("blockFrom", Long.class); + + public final EnumPath blockType = createEnum("blockType", allchive.server.domain.domains.block.domain.enums.BlockType.class); + + public final NumberPath blockUser = createNumber("blockUser", Long.class); + + //inherited + public final DateTimePath createdAt = _super.createdAt; + + public final NumberPath id = createNumber("id", Long.class); + + //inherited + public final DateTimePath updatedAt = _super.updatedAt; + + public QBlock(String variable) { + super(Block.class, forVariable(variable)); + } + + public QBlock(Path path) { + super(path.getType(), path.getMetadata()); + } + + public QBlock(PathMetadata metadata) { + super(Block.class, metadata); + } + +} + diff --git a/Domain/src/main/generated/allchive/server/domain/domains/category/domain/QCategory.java b/Domain/src/main/generated/allchive/server/domain/domains/category/domain/QCategory.java new file mode 100644 index 00000000..2172db24 --- /dev/null +++ b/Domain/src/main/generated/allchive/server/domain/domains/category/domain/QCategory.java @@ -0,0 +1,45 @@ +package allchive.server.domain.domains.category.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; + + +/** + * QCategory is a Querydsl query type for Category + */ +@Generated("com.querydsl.codegen.DefaultEntitySerializer") +public class QCategory extends EntityPathBase { + + private static final long serialVersionUID = -530930862L; + + public static final QCategory category = new QCategory("category"); + + public final allchive.server.domain.common.model.QBaseTimeEntity _super = new allchive.server.domain.common.model.QBaseTimeEntity(this); + + //inherited + public final DateTimePath createdAt = _super.createdAt; + + public final NumberPath id = createNumber("id", Long.class); + + //inherited + public final DateTimePath updatedAt = _super.updatedAt; + + public QCategory(String variable) { + super(Category.class, forVariable(variable)); + } + + public QCategory(Path path) { + super(path.getType(), path.getMetadata()); + } + + public QCategory(PathMetadata metadata) { + super(Category.class, metadata); + } + +} + diff --git a/Domain/src/main/generated/allchive/server/domain/domains/category/domain/QTitle.java b/Domain/src/main/generated/allchive/server/domain/domains/category/domain/QTitle.java new file mode 100644 index 00000000..b733da1d --- /dev/null +++ b/Domain/src/main/generated/allchive/server/domain/domains/category/domain/QTitle.java @@ -0,0 +1,45 @@ +package allchive.server.domain.domains.category.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; + + +/** + * QTitle is a Querydsl query type for Title + */ +@Generated("com.querydsl.codegen.DefaultEntitySerializer") +public class QTitle extends EntityPathBase { + + private static final long serialVersionUID = -1270219644L; + + public static final QTitle title = new QTitle("title"); + + public final allchive.server.domain.common.model.QBaseTimeEntity _super = new allchive.server.domain.common.model.QBaseTimeEntity(this); + + //inherited + public final DateTimePath<java.time.LocalDateTime> createdAt = _super.createdAt; + + public final NumberPath<Long> id = createNumber("id", Long.class); + + //inherited + public final DateTimePath<java.time.LocalDateTime> updatedAt = _super.updatedAt; + + public QTitle(String variable) { + super(Title.class, forVariable(variable)); + } + + public QTitle(Path<? extends Title> path) { + super(path.getType(), path.getMetadata()); + } + + public QTitle(PathMetadata metadata) { + super(Title.class, metadata); + } + +} + diff --git a/Domain/src/main/generated/allchive/server/domain/domains/content/domain/QContent.java b/Domain/src/main/generated/allchive/server/domain/domains/content/domain/QContent.java new file mode 100644 index 00000000..efb51bae --- /dev/null +++ b/Domain/src/main/generated/allchive/server/domain/domains/content/domain/QContent.java @@ -0,0 +1,45 @@ +package allchive.server.domain.domains.content.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; + + +/** + * QContent is a Querydsl query type for Content + */ +@Generated("com.querydsl.codegen.DefaultEntitySerializer") +public class QContent extends EntityPathBase<Content> { + + private static final long serialVersionUID = -1452122370L; + + public static final QContent content = new QContent("content"); + + public final allchive.server.domain.common.model.QBaseTimeEntity _super = new allchive.server.domain.common.model.QBaseTimeEntity(this); + + //inherited + public final DateTimePath<java.time.LocalDateTime> createdAt = _super.createdAt; + + public final NumberPath<Long> id = createNumber("id", Long.class); + + //inherited + public final DateTimePath<java.time.LocalDateTime> updatedAt = _super.updatedAt; + + public QContent(String variable) { + super(Content.class, forVariable(variable)); + } + + public QContent(Path<? extends Content> path) { + super(path.getType(), path.getMetadata()); + } + + public QContent(PathMetadata metadata) { + super(Content.class, metadata); + } + +} + diff --git a/Domain/src/main/generated/allchive/server/domain/domains/content/domain/QContentTagGroup.java b/Domain/src/main/generated/allchive/server/domain/domains/content/domain/QContentTagGroup.java new file mode 100644 index 00000000..c8c4a24f --- /dev/null +++ b/Domain/src/main/generated/allchive/server/domain/domains/content/domain/QContentTagGroup.java @@ -0,0 +1,45 @@ +package allchive.server.domain.domains.content.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; + + +/** + * QContentTagGroup is a Querydsl query type for ContentTagGroup + */ +@Generated("com.querydsl.codegen.DefaultEntitySerializer") +public class QContentTagGroup extends EntityPathBase<ContentTagGroup> { + + private static final long serialVersionUID = 689626147L; + + public static final QContentTagGroup contentTagGroup = new QContentTagGroup("contentTagGroup"); + + public final allchive.server.domain.common.model.QBaseTimeEntity _super = new allchive.server.domain.common.model.QBaseTimeEntity(this); + + //inherited + public final DateTimePath<java.time.LocalDateTime> createdAt = _super.createdAt; + + public final NumberPath<Long> id = createNumber("id", Long.class); + + //inherited + public final DateTimePath<java.time.LocalDateTime> updatedAt = _super.updatedAt; + + public QContentTagGroup(String variable) { + super(ContentTagGroup.class, forVariable(variable)); + } + + public QContentTagGroup(Path<? extends ContentTagGroup> path) { + super(path.getType(), path.getMetadata()); + } + + public QContentTagGroup(PathMetadata metadata) { + super(ContentTagGroup.class, metadata); + } + +} + diff --git a/Domain/src/main/generated/allchive/server/domain/domains/content/domain/QTag.java b/Domain/src/main/generated/allchive/server/domain/domains/content/domain/QTag.java new file mode 100644 index 00000000..fb78b204 --- /dev/null +++ b/Domain/src/main/generated/allchive/server/domain/domains/content/domain/QTag.java @@ -0,0 +1,45 @@ +package allchive.server.domain.domains.content.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; + + +/** + * QTag is a Querydsl query type for Tag + */ +@Generated("com.querydsl.codegen.DefaultEntitySerializer") +public class QTag extends EntityPathBase<Tag> { + + private static final long serialVersionUID = -384240481L; + + public static final QTag tag = new QTag("tag"); + + public final allchive.server.domain.common.model.QBaseTimeEntity _super = new allchive.server.domain.common.model.QBaseTimeEntity(this); + + //inherited + public final DateTimePath<java.time.LocalDateTime> createdAt = _super.createdAt; + + public final NumberPath<Long> id = createNumber("id", Long.class); + + //inherited + public final DateTimePath<java.time.LocalDateTime> updatedAt = _super.updatedAt; + + public QTag(String variable) { + super(Tag.class, forVariable(variable)); + } + + public QTag(Path<? extends Tag> path) { + super(path.getType(), path.getMetadata()); + } + + public QTag(PathMetadata metadata) { + super(Tag.class, metadata); + } + +} + diff --git a/Domain/src/main/generated/allchive/server/domain/domains/recycle/domain/QRecycle.java b/Domain/src/main/generated/allchive/server/domain/domains/recycle/domain/QRecycle.java new file mode 100644 index 00000000..23dc9eb1 --- /dev/null +++ b/Domain/src/main/generated/allchive/server/domain/domains/recycle/domain/QRecycle.java @@ -0,0 +1,45 @@ +package allchive.server.domain.domains.recycle.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; + + +/** + * QRecycle is a Querydsl query type for Recycle + */ +@Generated("com.querydsl.codegen.DefaultEntitySerializer") +public class QRecycle extends EntityPathBase<Recycle> { + + private static final long serialVersionUID = -1015076930L; + + public static final QRecycle recycle = new QRecycle("recycle"); + + public final allchive.server.domain.common.model.QBaseTimeEntity _super = new allchive.server.domain.common.model.QBaseTimeEntity(this); + + //inherited + public final DateTimePath<java.time.LocalDateTime> createdAt = _super.createdAt; + + public final NumberPath<Long> id = createNumber("id", Long.class); + + //inherited + public final DateTimePath<java.time.LocalDateTime> updatedAt = _super.updatedAt; + + public QRecycle(String variable) { + super(Recycle.class, forVariable(variable)); + } + + public QRecycle(Path<? extends Recycle> path) { + super(path.getType(), path.getMetadata()); + } + + public QRecycle(PathMetadata metadata) { + super(Recycle.class, metadata); + } + +} + diff --git a/Domain/src/main/generated/allchive/server/domain/domains/report/domain/QReport.java b/Domain/src/main/generated/allchive/server/domain/domains/report/domain/QReport.java new file mode 100644 index 00000000..11f7306a --- /dev/null +++ b/Domain/src/main/generated/allchive/server/domain/domains/report/domain/QReport.java @@ -0,0 +1,45 @@ +package allchive.server.domain.domains.report.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; + + +/** + * QReport is a Querydsl query type for Report + */ +@Generated("com.querydsl.codegen.DefaultEntitySerializer") +public class QReport extends EntityPathBase<Report> { + + private static final long serialVersionUID = 145123518L; + + public static final QReport report = new QReport("report"); + + public final allchive.server.domain.common.model.QBaseTimeEntity _super = new allchive.server.domain.common.model.QBaseTimeEntity(this); + + //inherited + public final DateTimePath<java.time.LocalDateTime> createdAt = _super.createdAt; + + public final NumberPath<Long> id = createNumber("id", Long.class); + + //inherited + public final DateTimePath<java.time.LocalDateTime> updatedAt = _super.updatedAt; + + public QReport(String variable) { + super(Report.class, forVariable(variable)); + } + + public QReport(Path<? extends Report> path) { + super(path.getType(), path.getMetadata()); + } + + public QReport(PathMetadata metadata) { + super(Report.class, metadata); + } + +} + diff --git a/Domain/src/main/generated/allchive/server/domain/domains/user/domain/QScrap.java b/Domain/src/main/generated/allchive/server/domain/domains/user/domain/QScrap.java new file mode 100644 index 00000000..f6b2998c --- /dev/null +++ b/Domain/src/main/generated/allchive/server/domain/domains/user/domain/QScrap.java @@ -0,0 +1,45 @@ +package allchive.server.domain.domains.user.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; + + +/** + * QScrap is a Querydsl query type for Scrap + */ +@Generated("com.querydsl.codegen.DefaultEntitySerializer") +public class QScrap extends EntityPathBase<Scrap> { + + private static final long serialVersionUID = 53975888L; + + public static final QScrap scrap = new QScrap("scrap"); + + public final allchive.server.domain.common.model.QBaseTimeEntity _super = new allchive.server.domain.common.model.QBaseTimeEntity(this); + + //inherited + public final DateTimePath<java.time.LocalDateTime> createdAt = _super.createdAt; + + public final NumberPath<Long> id = createNumber("id", Long.class); + + //inherited + public final DateTimePath<java.time.LocalDateTime> updatedAt = _super.updatedAt; + + public QScrap(String variable) { + super(Scrap.class, forVariable(variable)); + } + + public QScrap(Path<? extends Scrap> path) { + super(path.getType(), path.getMetadata()); + } + + public QScrap(PathMetadata metadata) { + super(Scrap.class, metadata); + } + +} + diff --git a/Domain/src/main/generated/allchive/server/domain/domains/user/domain/QUser.java b/Domain/src/main/generated/allchive/server/domain/domains/user/domain/QUser.java new file mode 100644 index 00000000..5da919cc --- /dev/null +++ b/Domain/src/main/generated/allchive/server/domain/domains/user/domain/QUser.java @@ -0,0 +1,69 @@ +package allchive.server.domain.domains.user.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; + + +/** + * QUser is a Querydsl query type for User + */ +@Generated("com.querydsl.codegen.DefaultEntitySerializer") +public class QUser extends EntityPathBase<User> { + + private static final long serialVersionUID = 2080025708L; + + private static final PathInits INITS = PathInits.DIRECT2; + + public static final QUser user = new QUser("user"); + + public final allchive.server.domain.common.model.QBaseTimeEntity _super = new allchive.server.domain.common.model.QBaseTimeEntity(this); + + //inherited + public final DateTimePath<java.time.LocalDateTime> createdAt = _super.createdAt; + + public final StringPath Email = createString("Email"); + + public final NumberPath<Long> id = createNumber("id", Long.class); + + public final DateTimePath<java.time.LocalDateTime> lastLoginAt = createDateTime("lastLoginAt", java.time.LocalDateTime.class); + + public final StringPath nickname = createString("nickname"); + + public final allchive.server.domain.domains.user.domain.enums.QOauthInfo oauthInfo; + + public final StringPath profileImgUrl = createString("profileImgUrl"); + + //inherited + public final DateTimePath<java.time.LocalDateTime> updatedAt = _super.updatedAt; + + public final EnumPath<allchive.server.domain.domains.user.domain.enums.UserState> userState = createEnum("userState", allchive.server.domain.domains.user.domain.enums.UserState.class); + + public QUser(String variable) { + this(User.class, forVariable(variable), INITS); + } + + public QUser(Path<? extends User> path) { + this(path.getType(), path.getMetadata(), PathInits.getFor(path.getMetadata(), INITS)); + } + + public QUser(PathMetadata metadata) { + this(metadata, PathInits.getFor(metadata, INITS)); + } + + public QUser(PathMetadata metadata, PathInits inits) { + this(User.class, metadata, inits); + } + + public QUser(Class<? extends User> type, PathMetadata metadata, PathInits inits) { + super(type, metadata, inits); + this.oauthInfo = inits.isInitialized("oauthInfo") ? new allchive.server.domain.domains.user.domain.enums.QOauthInfo(forProperty("oauthInfo")) : null; + } + +} + diff --git a/Domain/src/main/generated/allchive/server/domain/domains/user/domain/QUserTopicGroup.java b/Domain/src/main/generated/allchive/server/domain/domains/user/domain/QUserTopicGroup.java new file mode 100644 index 00000000..c1e768b4 --- /dev/null +++ b/Domain/src/main/generated/allchive/server/domain/domains/user/domain/QUserTopicGroup.java @@ -0,0 +1,45 @@ +package allchive.server.domain.domains.user.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; + + +/** + * QUserTopicGroup is a Querydsl query type for UserTopicGroup + */ +@Generated("com.querydsl.codegen.DefaultEntitySerializer") +public class QUserTopicGroup extends EntityPathBase<UserTopicGroup> { + + private static final long serialVersionUID = 131338268L; + + public static final QUserTopicGroup userTopicGroup = new QUserTopicGroup("userTopicGroup"); + + public final allchive.server.domain.common.model.QBaseTimeEntity _super = new allchive.server.domain.common.model.QBaseTimeEntity(this); + + //inherited + public final DateTimePath<java.time.LocalDateTime> createdAt = _super.createdAt; + + public final NumberPath<Long> id = createNumber("id", Long.class); + + //inherited + public final DateTimePath<java.time.LocalDateTime> updatedAt = _super.updatedAt; + + public QUserTopicGroup(String variable) { + super(UserTopicGroup.class, forVariable(variable)); + } + + public QUserTopicGroup(Path<? extends UserTopicGroup> path) { + super(path.getType(), path.getMetadata()); + } + + public QUserTopicGroup(PathMetadata metadata) { + super(UserTopicGroup.class, metadata); + } + +} + diff --git a/Domain/src/main/generated/allchive/server/domain/domains/user/domain/enums/QFormInfo.java b/Domain/src/main/generated/allchive/server/domain/domains/user/domain/enums/QFormInfo.java new file mode 100644 index 00000000..f1936850 --- /dev/null +++ b/Domain/src/main/generated/allchive/server/domain/domains/user/domain/enums/QFormInfo.java @@ -0,0 +1,41 @@ +package allchive.server.domain.domains.user.domain.enums; + +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; + + +/** + * QFormInfo is a Querydsl query type for FormInfo + */ +@Generated("com.querydsl.codegen.DefaultEmbeddableSerializer") +public class QFormInfo extends BeanPath<FormInfo> { + + private static final long serialVersionUID = 1297773007L; + + public static final QFormInfo formInfo = new QFormInfo("formInfo"); + + public final StringPath formId = createString("formId"); + + public final StringPath formPwd = createString("formPwd"); + + public final StringPath phone = createString("phone"); + + public QFormInfo(String variable) { + super(FormInfo.class, forVariable(variable)); + } + + public QFormInfo(Path<? extends FormInfo> path) { + super(path.getType(), path.getMetadata()); + } + + public QFormInfo(PathMetadata metadata) { + super(FormInfo.class, metadata); + } + +} + diff --git a/Domain/src/main/generated/allchive/server/domain/domains/user/domain/enums/QOauthInfo.java b/Domain/src/main/generated/allchive/server/domain/domains/user/domain/enums/QOauthInfo.java new file mode 100644 index 00000000..b9201d2c --- /dev/null +++ b/Domain/src/main/generated/allchive/server/domain/domains/user/domain/enums/QOauthInfo.java @@ -0,0 +1,39 @@ +package allchive.server.domain.domains.user.domain.enums; + +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; + + +/** + * QOauthInfo is a Querydsl query type for OauthInfo + */ +@Generated("com.querydsl.codegen.DefaultEmbeddableSerializer") +public class QOauthInfo extends BeanPath<OauthInfo> { + + private static final long serialVersionUID = -1839989432L; + + public static final QOauthInfo oauthInfo = new QOauthInfo("oauthInfo"); + + public final StringPath oid = createString("oid"); + + public final EnumPath<OauthProvider> provider = createEnum("provider", OauthProvider.class); + + public QOauthInfo(String variable) { + super(OauthInfo.class, forVariable(variable)); + } + + public QOauthInfo(Path<? extends OauthInfo> path) { + super(path.getType(), path.getMetadata()); + } + + public QOauthInfo(PathMetadata metadata) { + super(OauthInfo.class, metadata); + } + +} + diff --git a/Domain/src/main/java/allchive/server/domain/common/model/BaseTimeEntity.java b/Domain/src/main/java/allchive/server/domain/common/model/BaseTimeEntity.java new file mode 100644 index 00000000..2bafd9cf --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/common/model/BaseTimeEntity.java @@ -0,0 +1,26 @@ +package allchive.server.domain.common.model; + + +import lombok.Getter; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +import javax.persistence.Column; +import javax.persistence.EntityListeners; +import javax.persistence.MappedSuperclass; +import java.time.LocalDateTime; + +@Getter +@MappedSuperclass +@EntityListeners(AuditingEntityListener.class) +public abstract class BaseTimeEntity { + + @Column(updatable = false) + @CreatedDate + private LocalDateTime createdAt; + + @Column + @LastModifiedDate + private LocalDateTime updatedAt; +} diff --git a/Domain/src/main/java/allchive/server/domain/config/QueryDslConfig.java b/Domain/src/main/java/allchive/server/domain/config/QueryDslConfig.java index e369778b..b947f661 100644 --- a/Domain/src/main/java/allchive/server/domain/config/QueryDslConfig.java +++ b/Domain/src/main/java/allchive/server/domain/config/QueryDslConfig.java @@ -2,14 +2,16 @@ import com.querydsl.jpa.impl.JPAQueryFactory; -import jakarta.persistence.EntityManager; -import jakarta.persistence.PersistenceContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; + @Configuration public class QueryDslConfig { - @PersistenceContext private EntityManager entityManager; + @PersistenceContext + private EntityManager entityManager; @Bean public JPAQueryFactory jpaQueryFactory() { diff --git a/Domain/src/main/java/allchive/server/domain/domains/block/adaptor/BlockAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/block/adaptor/BlockAdaptor.java new file mode 100644 index 00000000..3e01e9ca --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/block/adaptor/BlockAdaptor.java @@ -0,0 +1,9 @@ +package allchive.server.domain.domains.block.adaptor; + +import allchive.server.core.annotation.Adaptor; +import lombok.RequiredArgsConstructor; + +@Adaptor +@RequiredArgsConstructor +public class BlockAdaptor { +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/block/domain/Block.java b/Domain/src/main/java/allchive/server/domain/domains/block/domain/Block.java new file mode 100644 index 00000000..d359be9f --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/block/domain/Block.java @@ -0,0 +1,34 @@ +package allchive.server.domain.domains.block.domain; + +import allchive.server.domain.common.model.BaseTimeEntity; +import allchive.server.domain.domains.block.domain.enums.BlockType; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import javax.persistence.*; +import javax.validation.constraints.NotNull; + +@Getter +@Table(name = "tbl_block") +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class Block extends BaseTimeEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + // block 한 사람 + @NotNull + private Long blockFrom; + + @Enumerated(EnumType.STRING) + private BlockType blockType; + + // Block 당한 유저 + private Long blockUser; + + // Block 당한 컨텐츠 + private Long blockContent; + +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/block/domain/enums/BlockType.java b/Domain/src/main/java/allchive/server/domain/domains/block/domain/enums/BlockType.java new file mode 100644 index 00000000..af5695f8 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/block/domain/enums/BlockType.java @@ -0,0 +1,6 @@ +package allchive.server.domain.domains.block.domain.enums; + +public enum BlockType { + USER, + CONTENT +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/block/exception/BlockErrorCode.java b/Domain/src/main/java/allchive/server/domain/domains/block/exception/BlockErrorCode.java new file mode 100644 index 00000000..d131214f --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/block/exception/BlockErrorCode.java @@ -0,0 +1,22 @@ +package allchive.server.domain.domains.block.exception; + +import allchive.server.core.dto.ErrorReason; +import allchive.server.core.error.BaseErrorCode; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@AllArgsConstructor +public enum BlockErrorCode implements BaseErrorCode { + ; + private HttpStatus status; + private String code; + private String reason; + + @Override + public ErrorReason getErrorReason() { + return ErrorReason.of(status.value(), code, reason); + + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/block/repository/BlockRepository.java b/Domain/src/main/java/allchive/server/domain/domains/block/repository/BlockRepository.java new file mode 100644 index 00000000..2cc0f788 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/block/repository/BlockRepository.java @@ -0,0 +1,7 @@ +package allchive.server.domain.domains.block.repository; + +import allchive.server.domain.domains.block.domain.Block; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface BlockRepository extends JpaRepository<Block, Long> { +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/block/service/BlockService.java b/Domain/src/main/java/allchive/server/domain/domains/block/service/BlockService.java new file mode 100644 index 00000000..60c08eb1 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/block/service/BlockService.java @@ -0,0 +1,7 @@ +package allchive.server.domain.domains.block.service; + +import allchive.server.core.annotation.DomainService; + +@DomainService +public class BlockService { +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/block/validator/BlockValidator.java b/Domain/src/main/java/allchive/server/domain/domains/block/validator/BlockValidator.java new file mode 100644 index 00000000..fa27e0f8 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/block/validator/BlockValidator.java @@ -0,0 +1,7 @@ +package allchive.server.domain.domains.block.validator; + +import allchive.server.core.annotation.Validator; + +@Validator +public class BlockValidator { +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/adaptor/CategoryAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/category/adaptor/CategoryAdaptor.java new file mode 100644 index 00000000..742948da --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/category/adaptor/CategoryAdaptor.java @@ -0,0 +1,9 @@ +package allchive.server.domain.domains.category.adaptor; + +import allchive.server.core.annotation.Adaptor; +import lombok.RequiredArgsConstructor; + +@Adaptor +@RequiredArgsConstructor +public class CategoryAdaptor { +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/adaptor/TitleAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/category/adaptor/TitleAdaptor.java new file mode 100644 index 00000000..0f0926eb --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/category/adaptor/TitleAdaptor.java @@ -0,0 +1,9 @@ +package allchive.server.domain.domains.category.adaptor; + +import allchive.server.core.annotation.Adaptor; +import lombok.RequiredArgsConstructor; + +@Adaptor +@RequiredArgsConstructor +public class TitleAdaptor { +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/domain/Category.java b/Domain/src/main/java/allchive/server/domain/domains/category/domain/Category.java new file mode 100644 index 00000000..a1f44d91 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/category/domain/Category.java @@ -0,0 +1,18 @@ +package allchive.server.domain.domains.category.domain; + +import allchive.server.domain.common.model.BaseTimeEntity; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Getter +@Table(name = "tbl_category") +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class Category extends BaseTimeEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/domain/Title.java b/Domain/src/main/java/allchive/server/domain/domains/category/domain/Title.java new file mode 100644 index 00000000..79527d79 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/category/domain/Title.java @@ -0,0 +1,18 @@ +package allchive.server.domain.domains.category.domain; + +import allchive.server.domain.common.model.BaseTimeEntity; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Getter +@Table(name = "tbl_category") +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class Title extends BaseTimeEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/domain/enums/.gitkeep b/Domain/src/main/java/allchive/server/domain/domains/category/domain/enums/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/exception/CategoryErrorCode.java b/Domain/src/main/java/allchive/server/domain/domains/category/exception/CategoryErrorCode.java new file mode 100644 index 00000000..895c9ad3 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/category/exception/CategoryErrorCode.java @@ -0,0 +1,22 @@ +package allchive.server.domain.domains.category.exception; + +import allchive.server.core.dto.ErrorReason; +import allchive.server.core.error.BaseErrorCode; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@AllArgsConstructor +public enum CategoryErrorCode implements BaseErrorCode { + ; + private HttpStatus status; + private String code; + private String reason; + + @Override + public ErrorReason getErrorReason() { + return ErrorReason.of(status.value(), code, reason); + + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/exception/TitleErrorCode.java b/Domain/src/main/java/allchive/server/domain/domains/category/exception/TitleErrorCode.java new file mode 100644 index 00000000..5f429103 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/category/exception/TitleErrorCode.java @@ -0,0 +1,22 @@ +package allchive.server.domain.domains.category.exception; + +import allchive.server.core.dto.ErrorReason; +import allchive.server.core.error.BaseErrorCode; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@AllArgsConstructor +public enum TitleErrorCode implements BaseErrorCode { + ; + private HttpStatus status; + private String code; + private String reason; + + @Override + public ErrorReason getErrorReason() { + return ErrorReason.of(status.value(), code, reason); + + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/repository/CategoryRepository.java b/Domain/src/main/java/allchive/server/domain/domains/category/repository/CategoryRepository.java new file mode 100644 index 00000000..14202d60 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/category/repository/CategoryRepository.java @@ -0,0 +1,7 @@ +package allchive.server.domain.domains.category.repository; + +import allchive.server.domain.domains.category.domain.Category; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface CategoryRepository extends JpaRepository<Category, Long> { +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/repository/TitleRepository.java b/Domain/src/main/java/allchive/server/domain/domains/category/repository/TitleRepository.java new file mode 100644 index 00000000..b3b34c58 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/category/repository/TitleRepository.java @@ -0,0 +1,8 @@ +package allchive.server.domain.domains.category.repository; + +import allchive.server.domain.domains.category.domain.Category; +import allchive.server.domain.domains.category.domain.Title; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface TitleRepository extends JpaRepository<Title, Long> { +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/service/CategoryService.java b/Domain/src/main/java/allchive/server/domain/domains/category/service/CategoryService.java new file mode 100644 index 00000000..4da12cb4 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/category/service/CategoryService.java @@ -0,0 +1,7 @@ +package allchive.server.domain.domains.category.service; + +import allchive.server.core.annotation.DomainService; + +@DomainService +public class CategoryService { +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/service/TitleService.java b/Domain/src/main/java/allchive/server/domain/domains/category/service/TitleService.java new file mode 100644 index 00000000..8424ab1f --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/category/service/TitleService.java @@ -0,0 +1,7 @@ +package allchive.server.domain.domains.category.service; + +import allchive.server.core.annotation.DomainService; + +@DomainService +public class TitleService { +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/validator/CategoryValidator.java b/Domain/src/main/java/allchive/server/domain/domains/category/validator/CategoryValidator.java new file mode 100644 index 00000000..8ff8a0fb --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/category/validator/CategoryValidator.java @@ -0,0 +1,7 @@ +package allchive.server.domain.domains.category.validator; + +import allchive.server.core.annotation.Validator; + +@Validator +public class CategoryValidator { +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/validator/TitleValidator.java b/Domain/src/main/java/allchive/server/domain/domains/category/validator/TitleValidator.java new file mode 100644 index 00000000..33c2339e --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/category/validator/TitleValidator.java @@ -0,0 +1,7 @@ +package allchive.server.domain.domains.category.validator; + +import allchive.server.core.annotation.Validator; + +@Validator +public class TitleValidator { +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentAdaptor.java new file mode 100644 index 00000000..3241c0bd --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentAdaptor.java @@ -0,0 +1,9 @@ +package allchive.server.domain.domains.content.adaptor; + +import allchive.server.core.annotation.Adaptor; +import lombok.RequiredArgsConstructor; + +@Adaptor +@RequiredArgsConstructor +public class ContentAdaptor { +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentTagGroupAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentTagGroupAdaptor.java new file mode 100644 index 00000000..4a8f2f2f --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentTagGroupAdaptor.java @@ -0,0 +1,9 @@ +package allchive.server.domain.domains.content.adaptor; + +import allchive.server.core.annotation.Adaptor; +import lombok.RequiredArgsConstructor; + +@Adaptor +@RequiredArgsConstructor +public class ContentTagGroupAdaptor { +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/TagAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/TagAdaptor.java new file mode 100644 index 00000000..cfe2e2c2 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/TagAdaptor.java @@ -0,0 +1,9 @@ +package allchive.server.domain.domains.content.adaptor; + +import allchive.server.core.annotation.Adaptor; +import lombok.RequiredArgsConstructor; + +@Adaptor +@RequiredArgsConstructor +public class TagAdaptor { +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/domain/Content.java b/Domain/src/main/java/allchive/server/domain/domains/content/domain/Content.java new file mode 100644 index 00000000..bebf1fca --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/content/domain/Content.java @@ -0,0 +1,19 @@ +package allchive.server.domain.domains.content.domain; + +import allchive.server.domain.common.model.BaseTimeEntity; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Getter +@Table(name = "tbl_content") +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class Content extends BaseTimeEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/domain/ContentTagGroup.java b/Domain/src/main/java/allchive/server/domain/domains/content/domain/ContentTagGroup.java new file mode 100644 index 00000000..e83e08f2 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/content/domain/ContentTagGroup.java @@ -0,0 +1,19 @@ +package allchive.server.domain.domains.content.domain; + +import allchive.server.domain.common.model.BaseTimeEntity; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Getter +@Table(name = "tbl_content_tag_group") +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class ContentTagGroup extends BaseTimeEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/domain/Tag.java b/Domain/src/main/java/allchive/server/domain/domains/content/domain/Tag.java new file mode 100644 index 00000000..2e065e15 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/content/domain/Tag.java @@ -0,0 +1,18 @@ +package allchive.server.domain.domains.content.domain; + +import allchive.server.domain.common.model.BaseTimeEntity; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Getter +@Table(name = "tbl_tag") +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class Tag extends BaseTimeEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/domain/enums/.gitkeep b/Domain/src/main/java/allchive/server/domain/domains/content/domain/enums/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/exception/ContentErrorCode.java b/Domain/src/main/java/allchive/server/domain/domains/content/exception/ContentErrorCode.java new file mode 100644 index 00000000..d53e2c02 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/content/exception/ContentErrorCode.java @@ -0,0 +1,22 @@ +package allchive.server.domain.domains.content.exception; + +import allchive.server.core.dto.ErrorReason; +import allchive.server.core.error.BaseErrorCode; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@AllArgsConstructor +public enum ContentErrorCode implements BaseErrorCode { + ; + private HttpStatus status; + private String code; + private String reason; + + @Override + public ErrorReason getErrorReason() { + return ErrorReason.of(status.value(), code, reason); + + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentRepository.java b/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentRepository.java new file mode 100644 index 00000000..daca5d17 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentRepository.java @@ -0,0 +1,7 @@ +package allchive.server.domain.domains.content.repository; + +import allchive.server.domain.domains.content.domain.Content; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface ContentRepository extends JpaRepository<Content, Long> { +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentTagGroupRepository.java b/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentTagGroupRepository.java new file mode 100644 index 00000000..4d0c53f2 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentTagGroupRepository.java @@ -0,0 +1,7 @@ +package allchive.server.domain.domains.content.repository; + +import allchive.server.domain.domains.content.domain.ContentTagGroup; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface ContentTagGroupRepository extends JpaRepository<ContentTagGroup, Long> { +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/repository/TagRepository.java b/Domain/src/main/java/allchive/server/domain/domains/content/repository/TagRepository.java new file mode 100644 index 00000000..6b441a52 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/content/repository/TagRepository.java @@ -0,0 +1,7 @@ +package allchive.server.domain.domains.content.repository; + +import allchive.server.domain.domains.content.domain.Tag; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface TagRepository extends JpaRepository<Tag, Long> { +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/service/ContentService.java b/Domain/src/main/java/allchive/server/domain/domains/content/service/ContentService.java new file mode 100644 index 00000000..65e635e3 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/content/service/ContentService.java @@ -0,0 +1,7 @@ +package allchive.server.domain.domains.content.service; + +import allchive.server.core.annotation.DomainService; + +@DomainService +public class ContentService { +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/service/ContentTagGroupService.java b/Domain/src/main/java/allchive/server/domain/domains/content/service/ContentTagGroupService.java new file mode 100644 index 00000000..3ee1019a --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/content/service/ContentTagGroupService.java @@ -0,0 +1,7 @@ +package allchive.server.domain.domains.content.service; + +import allchive.server.core.annotation.DomainService; + +@DomainService +public class ContentTagGroupService { +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/service/TagService.java b/Domain/src/main/java/allchive/server/domain/domains/content/service/TagService.java new file mode 100644 index 00000000..e96613fc --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/content/service/TagService.java @@ -0,0 +1,7 @@ +package allchive.server.domain.domains.content.service; + +import allchive.server.core.annotation.DomainService; + +@DomainService +public class TagService { +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/validator/ContentValidator.java b/Domain/src/main/java/allchive/server/domain/domains/content/validator/ContentValidator.java new file mode 100644 index 00000000..3973a08a --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/content/validator/ContentValidator.java @@ -0,0 +1,7 @@ +package allchive.server.domain.domains.content.validator; + +import allchive.server.core.annotation.Validator; + +@Validator +public class ContentValidator { +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/validator/TagValidator.java b/Domain/src/main/java/allchive/server/domain/domains/content/validator/TagValidator.java new file mode 100644 index 00000000..8229e08d --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/content/validator/TagValidator.java @@ -0,0 +1,7 @@ +package allchive.server.domain.domains.content.validator; + +import allchive.server.core.annotation.Validator; + +@Validator +public class TagValidator { +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/recycle/adaptor/RecycleAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/recycle/adaptor/RecycleAdaptor.java new file mode 100644 index 00000000..964057b3 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/recycle/adaptor/RecycleAdaptor.java @@ -0,0 +1,9 @@ +package allchive.server.domain.domains.recycle.adaptor; + +import allchive.server.core.annotation.Adaptor; +import lombok.RequiredArgsConstructor; + +@Adaptor +@RequiredArgsConstructor +public class RecycleAdaptor { +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/recycle/domain/Recycle.java b/Domain/src/main/java/allchive/server/domain/domains/recycle/domain/Recycle.java new file mode 100644 index 00000000..62e550e3 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/recycle/domain/Recycle.java @@ -0,0 +1,18 @@ +package allchive.server.domain.domains.recycle.domain; + +import allchive.server.domain.common.model.BaseTimeEntity; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Getter +@Table(name = "tbl_recycle") +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class Recycle extends BaseTimeEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/recycle/domain/enums/.gitkeep b/Domain/src/main/java/allchive/server/domain/domains/recycle/domain/enums/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/Domain/src/main/java/allchive/server/domain/domains/recycle/exception/RecycleErrorCode.java b/Domain/src/main/java/allchive/server/domain/domains/recycle/exception/RecycleErrorCode.java new file mode 100644 index 00000000..6133d756 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/recycle/exception/RecycleErrorCode.java @@ -0,0 +1,22 @@ +package allchive.server.domain.domains.recycle.exception; + +import allchive.server.core.dto.ErrorReason; +import allchive.server.core.error.BaseErrorCode; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@AllArgsConstructor +public enum RecycleErrorCode implements BaseErrorCode { + ; + private HttpStatus status; + private String code; + private String reason; + + @Override + public ErrorReason getErrorReason() { + return ErrorReason.of(status.value(), code, reason); + + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/recycle/repository/RecycleRepository.java b/Domain/src/main/java/allchive/server/domain/domains/recycle/repository/RecycleRepository.java new file mode 100644 index 00000000..e7668a1d --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/recycle/repository/RecycleRepository.java @@ -0,0 +1,7 @@ +package allchive.server.domain.domains.recycle.repository; + +import allchive.server.domain.domains.recycle.domain.Recycle; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface RecycleRepository extends JpaRepository<Recycle, Long> { +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/recycle/service/RecycleService.java b/Domain/src/main/java/allchive/server/domain/domains/recycle/service/RecycleService.java new file mode 100644 index 00000000..f0523666 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/recycle/service/RecycleService.java @@ -0,0 +1,7 @@ +package allchive.server.domain.domains.recycle.service; + +import allchive.server.core.annotation.DomainService; + +@DomainService +public class RecycleService { +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/recycle/validator/RecycleValidator.java b/Domain/src/main/java/allchive/server/domain/domains/recycle/validator/RecycleValidator.java new file mode 100644 index 00000000..651326d2 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/recycle/validator/RecycleValidator.java @@ -0,0 +1,7 @@ +package allchive.server.domain.domains.recycle.validator; + +import allchive.server.core.annotation.Validator; + +@Validator +public class RecycleValidator { +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/report/adaptor/ReportAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/report/adaptor/ReportAdaptor.java new file mode 100644 index 00000000..41870a1f --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/report/adaptor/ReportAdaptor.java @@ -0,0 +1,9 @@ +package allchive.server.domain.domains.report.adaptor; + +import allchive.server.core.annotation.Adaptor; +import lombok.RequiredArgsConstructor; + +@Adaptor +@RequiredArgsConstructor +public class ReportAdaptor { +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/report/domain/Report.java b/Domain/src/main/java/allchive/server/domain/domains/report/domain/Report.java new file mode 100644 index 00000000..6169aac5 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/report/domain/Report.java @@ -0,0 +1,19 @@ +package allchive.server.domain.domains.report.domain; + +import allchive.server.domain.common.model.BaseTimeEntity; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Getter +@Table(name = "tbl_report") +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class Report extends BaseTimeEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/report/domain/enums/.gitkeep b/Domain/src/main/java/allchive/server/domain/domains/report/domain/enums/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/Domain/src/main/java/allchive/server/domain/domains/report/exception/ReportErrorCode.java b/Domain/src/main/java/allchive/server/domain/domains/report/exception/ReportErrorCode.java new file mode 100644 index 00000000..87f764b5 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/report/exception/ReportErrorCode.java @@ -0,0 +1,22 @@ +package allchive.server.domain.domains.report.exception; + +import allchive.server.core.dto.ErrorReason; +import allchive.server.core.error.BaseErrorCode; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@AllArgsConstructor +public enum ReportErrorCode implements BaseErrorCode { + ; + private HttpStatus status; + private String code; + private String reason; + + @Override + public ErrorReason getErrorReason() { + return ErrorReason.of(status.value(), code, reason); + + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/report/repository/ReportRepository.java b/Domain/src/main/java/allchive/server/domain/domains/report/repository/ReportRepository.java new file mode 100644 index 00000000..6e3e7959 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/report/repository/ReportRepository.java @@ -0,0 +1,7 @@ +package allchive.server.domain.domains.report.repository; + +import allchive.server.domain.domains.report.domain.Report; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface ReportRepository extends JpaRepository<Report, Long> { +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/report/service/ReportService.java b/Domain/src/main/java/allchive/server/domain/domains/report/service/ReportService.java new file mode 100644 index 00000000..7f1ca556 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/report/service/ReportService.java @@ -0,0 +1,7 @@ +package allchive.server.domain.domains.report.service; + +import allchive.server.core.annotation.DomainService; + +@DomainService +public class ReportService { +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/report/validator/ReportValidator.java b/Domain/src/main/java/allchive/server/domain/domains/report/validator/ReportValidator.java new file mode 100644 index 00000000..03068124 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/report/validator/ReportValidator.java @@ -0,0 +1,7 @@ +package allchive.server.domain.domains.report.validator; + +import allchive.server.core.annotation.Validator; + +@Validator +public class ReportValidator { +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/ScrapAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/ScrapAdaptor.java new file mode 100644 index 00000000..1b77131f --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/ScrapAdaptor.java @@ -0,0 +1,9 @@ +package allchive.server.domain.domains.user.adaptor; + +import allchive.server.core.annotation.Adaptor; +import lombok.RequiredArgsConstructor; + +@Adaptor +@RequiredArgsConstructor +public class ScrapAdaptor { +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/UserAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/UserAdaptor.java new file mode 100644 index 00000000..ecac869d --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/UserAdaptor.java @@ -0,0 +1,9 @@ +package allchive.server.domain.domains.user.adaptor; + +import allchive.server.core.annotation.Adaptor; +import lombok.RequiredArgsConstructor; + +@Adaptor +@RequiredArgsConstructor +public class UserAdaptor { +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/UserTopicAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/UserTopicAdaptor.java new file mode 100644 index 00000000..0283f232 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/UserTopicAdaptor.java @@ -0,0 +1,9 @@ +package allchive.server.domain.domains.user.adaptor; + +import allchive.server.core.annotation.Adaptor; +import lombok.RequiredArgsConstructor; + +@Adaptor +@RequiredArgsConstructor +public class UserTopicAdaptor { +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/domain/Scrap.java b/Domain/src/main/java/allchive/server/domain/domains/user/domain/Scrap.java new file mode 100644 index 00000000..4d94c75a --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/user/domain/Scrap.java @@ -0,0 +1,18 @@ +package allchive.server.domain.domains.user.domain; + +import allchive.server.domain.common.model.BaseTimeEntity; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Getter +@Table(name = "tbl_scrap") +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class Scrap extends BaseTimeEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/domain/User.java b/Domain/src/main/java/allchive/server/domain/domains/user/domain/User.java new file mode 100644 index 00000000..367dfa85 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/user/domain/User.java @@ -0,0 +1,37 @@ +package allchive.server.domain.domains.user.domain; + +import allchive.server.domain.common.model.BaseTimeEntity; +import allchive.server.domain.domains.user.domain.enums.OauthInfo; +import allchive.server.domain.domains.user.domain.enums.UserState; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import javax.persistence.*; +import javax.validation.constraints.NotNull; +import java.time.LocalDateTime; + +@Getter +@Table(name = "tbl_user") +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class User extends BaseTimeEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @NotNull + private String nickname; + private String profileImgUrl; + + @Embedded + private OauthInfo oauthInfo; + + @NotNull + private LocalDateTime lastLoginAt; + + private String Email; + + @Enumerated(EnumType.STRING) + private UserState userState = UserState.NORMAL; +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/domain/UserTopicGroup.java b/Domain/src/main/java/allchive/server/domain/domains/user/domain/UserTopicGroup.java new file mode 100644 index 00000000..4fb6c9ee --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/user/domain/UserTopicGroup.java @@ -0,0 +1,19 @@ +package allchive.server.domain.domains.user.domain; + +import allchive.server.domain.common.model.BaseTimeEntity; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Getter +@Table(name = "tbl_user_topic_group") +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class UserTopicGroup extends BaseTimeEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/domain/enums/FormInfo.java b/Domain/src/main/java/allchive/server/domain/domains/user/domain/enums/FormInfo.java new file mode 100644 index 00000000..e3d642c9 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/user/domain/enums/FormInfo.java @@ -0,0 +1,16 @@ +package allchive.server.domain.domains.user.domain.enums; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import javax.persistence.Embeddable; + +@Embeddable +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class FormInfo { + private String formId; + private String formPwd; + private String phone; +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/domain/enums/OauthInfo.java b/Domain/src/main/java/allchive/server/domain/domains/user/domain/enums/OauthInfo.java new file mode 100644 index 00000000..7c2adc45 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/user/domain/enums/OauthInfo.java @@ -0,0 +1,35 @@ +package allchive.server.domain.domains.user.domain.enums; + + +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import javax.persistence.Embeddable; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; + +@Getter +@Embeddable +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class OauthInfo { + + @Enumerated(EnumType.STRING) + private OauthProvider provider; + + private String oid; + + @Builder + public OauthInfo(OauthProvider provider, String oid) { + this.provider = provider; + this.oid = oid; + } + + public OauthInfo withDrawOauthInfo() { + return OauthInfo.builder() + .oid(oid) + .provider(provider) + .build(); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/domain/enums/OauthProvider.java b/Domain/src/main/java/allchive/server/domain/domains/user/domain/enums/OauthProvider.java new file mode 100644 index 00000000..f16b525e --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/user/domain/enums/OauthProvider.java @@ -0,0 +1,13 @@ +package allchive.server.domain.domains.user.domain.enums; + + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum OauthProvider { + KAKAO("KAKAO"); + + private String value; +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/domain/enums/UserState.java b/Domain/src/main/java/allchive/server/domain/domains/user/domain/enums/UserState.java new file mode 100644 index 00000000..41cd08aa --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/user/domain/enums/UserState.java @@ -0,0 +1,19 @@ +package allchive.server.domain.domains.user.domain.enums; + + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter +public enum UserState { + NORMAL("NORMAL"), + // 탈퇴한유저 + DELETED("DELETED"), + // 영구정지 + FORBIDDEN("FORBIDDEN"), + // 일시정지 + SUSPENDED("SUSPENDED"); + + private String value; +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/exception/ScrapErrorCode.java b/Domain/src/main/java/allchive/server/domain/domains/user/exception/ScrapErrorCode.java new file mode 100644 index 00000000..35e3819c --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/user/exception/ScrapErrorCode.java @@ -0,0 +1,22 @@ +package allchive.server.domain.domains.user.exception; + +import allchive.server.core.dto.ErrorReason; +import allchive.server.core.error.BaseErrorCode; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@AllArgsConstructor +public enum ScrapErrorCode implements BaseErrorCode { + ; + private HttpStatus status; + private String code; + private String reason; + + @Override + public ErrorReason getErrorReason() { + return ErrorReason.of(status.value(), code, reason); + + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/exception/UserErrorCode.java b/Domain/src/main/java/allchive/server/domain/domains/user/exception/UserErrorCode.java new file mode 100644 index 00000000..414a7fca --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/user/exception/UserErrorCode.java @@ -0,0 +1,22 @@ +package allchive.server.domain.domains.user.exception; + +import allchive.server.core.dto.ErrorReason; +import allchive.server.core.error.BaseErrorCode; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@AllArgsConstructor +public enum UserErrorCode implements BaseErrorCode { + ; + private HttpStatus status; + private String code; + private String reason; + + @Override + public ErrorReason getErrorReason() { + return ErrorReason.of(status.value(), code, reason); + + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/repository/ScrapRepository.java b/Domain/src/main/java/allchive/server/domain/domains/user/repository/ScrapRepository.java new file mode 100644 index 00000000..d583c1d3 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/user/repository/ScrapRepository.java @@ -0,0 +1,7 @@ +package allchive.server.domain.domains.user.repository; + +import allchive.server.domain.domains.user.domain.Scrap; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface ScrapRepository extends JpaRepository<Scrap, Long> { +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/repository/UserRepository.java b/Domain/src/main/java/allchive/server/domain/domains/user/repository/UserRepository.java new file mode 100644 index 00000000..e97a9a89 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/user/repository/UserRepository.java @@ -0,0 +1,7 @@ +package allchive.server.domain.domains.user.repository; + +import allchive.server.domain.domains.user.domain.User; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface UserRepository extends JpaRepository<User, Long> { +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/repository/UserTopicGroupRepository.java b/Domain/src/main/java/allchive/server/domain/domains/user/repository/UserTopicGroupRepository.java new file mode 100644 index 00000000..25866135 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/user/repository/UserTopicGroupRepository.java @@ -0,0 +1,7 @@ +package allchive.server.domain.domains.user.repository; + +import allchive.server.domain.domains.user.domain.UserTopicGroup; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface UserTopicGroupRepository extends JpaRepository<UserTopicGroup, Long> { +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/service/ScrapService.java b/Domain/src/main/java/allchive/server/domain/domains/user/service/ScrapService.java new file mode 100644 index 00000000..ee6b3ede --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/user/service/ScrapService.java @@ -0,0 +1,7 @@ +package allchive.server.domain.domains.user.service; + +import allchive.server.core.annotation.DomainService; + +@DomainService +public class ScrapService { +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/service/UserService.java b/Domain/src/main/java/allchive/server/domain/domains/user/service/UserService.java new file mode 100644 index 00000000..06b494cb --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/user/service/UserService.java @@ -0,0 +1,7 @@ +package allchive.server.domain.domains.user.service; + +import allchive.server.core.annotation.DomainService; + +@DomainService +public class UserService { +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/service/UserTopicGroupService.java b/Domain/src/main/java/allchive/server/domain/domains/user/service/UserTopicGroupService.java new file mode 100644 index 00000000..e734444b --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/user/service/UserTopicGroupService.java @@ -0,0 +1,7 @@ +package allchive.server.domain.domains.user.service; + +import allchive.server.core.annotation.DomainService; + +@DomainService +public class UserTopicGroupService { +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/validator/ScrapValidator.java b/Domain/src/main/java/allchive/server/domain/domains/user/validator/ScrapValidator.java new file mode 100644 index 00000000..65ef3653 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/user/validator/ScrapValidator.java @@ -0,0 +1,7 @@ +package allchive.server.domain.domains.user.validator; + +import allchive.server.core.annotation.Validator; + +@Validator +public class ScrapValidator { +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/validator/UserValidator.java b/Domain/src/main/java/allchive/server/domain/domains/user/validator/UserValidator.java new file mode 100644 index 00000000..1534bcaf --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/user/validator/UserValidator.java @@ -0,0 +1,7 @@ +package allchive.server.domain.domains.user.validator; + +import allchive.server.core.annotation.Validator; + +@Validator +public class UserValidator { +} diff --git a/Infrastructure/build.gradle b/Infrastructure/build.gradle index 5398ee84..0cb7677d 100644 --- a/Infrastructure/build.gradle +++ b/Infrastructure/build.gradle @@ -3,8 +3,6 @@ jar { enabled = true } dependencies { api('com.slack.api:slack-api-client:1.28.0') - api 'io.github.openfeign:feign-httpclient:12.2' - api 'org.springframework.cloud:spring-cloud-starter-openfeign:4.0.1' testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.2' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.2' api project(':Core') diff --git a/build.gradle b/build.gradle index 24f1d1e6..6c665dda 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '3.0.8' + id 'org.springframework.boot' version '2.7.13' id 'com.diffplug.spotless' version '6.11.0' } From 661ab377a9e398c509876889a1fd1cea422e09db Mon Sep 17 00:00:00 2001 From: Sanghoon Jeong <67852689+wjdtkdgns@users.noreply.github.com> Date: Thu, 29 Jun 2023 01:13:13 +0900 Subject: [PATCH 06/41] =?UTF-8?q?[feat]=20redis=20=EC=85=8B=ED=8C=85,=20re?= =?UTF-8?q?disson=20=EC=85=8B=ED=8C=85=20#6=20(#8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Infrastructure/build.gradle | 3 ++ .../src/main/java/allchive/server/.gitkeep | 0 .../config/redis/RedisCacheConfig.java | 38 ++++++++++++++++ .../config/redis/RedisConfig.java | 45 +++++++++++++++++++ .../config/redis/RedissonConfig.java | 32 +++++++++++++ .../resources/application-infrastructure.yml | 22 +++++++++ docker-compose.local.yml | 9 ++++ 7 files changed, 149 insertions(+) delete mode 100644 Infrastructure/src/main/java/allchive/server/.gitkeep create mode 100644 Infrastructure/src/main/java/allchive/server/infrastructure/config/redis/RedisCacheConfig.java create mode 100644 Infrastructure/src/main/java/allchive/server/infrastructure/config/redis/RedisConfig.java create mode 100644 Infrastructure/src/main/java/allchive/server/infrastructure/config/redis/RedissonConfig.java create mode 100644 docker-compose.local.yml diff --git a/Infrastructure/build.gradle b/Infrastructure/build.gradle index 0cb7677d..922e6549 100644 --- a/Infrastructure/build.gradle +++ b/Infrastructure/build.gradle @@ -3,6 +3,9 @@ jar { enabled = true } dependencies { api('com.slack.api:slack-api-client:1.28.0') + api 'org.springframework.boot:spring-boot-starter-data-redis' + api 'org.redisson:redisson:3.19.0' + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.2' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.2' api project(':Core') diff --git a/Infrastructure/src/main/java/allchive/server/.gitkeep b/Infrastructure/src/main/java/allchive/server/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/Infrastructure/src/main/java/allchive/server/infrastructure/config/redis/RedisCacheConfig.java b/Infrastructure/src/main/java/allchive/server/infrastructure/config/redis/RedisCacheConfig.java new file mode 100644 index 00000000..3df01b09 --- /dev/null +++ b/Infrastructure/src/main/java/allchive/server/infrastructure/config/redis/RedisCacheConfig.java @@ -0,0 +1,38 @@ +package allchive.server.infrastructure.config.redis; + + +import org.springframework.cache.CacheManager; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.data.redis.cache.RedisCacheConfiguration; +import org.springframework.data.redis.cache.RedisCacheManager; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.RedisSerializationContext; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +import java.time.Duration; + +@EnableCaching +@Configuration +public class RedisCacheConfig { + @Bean + @Primary + public CacheManager redisCacheManager(RedisConnectionFactory cf) { + RedisCacheConfiguration redisCacheConfiguration = + RedisCacheConfiguration.defaultCacheConfig() + .serializeKeysWith( + RedisSerializationContext.SerializationPair.fromSerializer( + new StringRedisSerializer())) + .serializeValuesWith( + RedisSerializationContext.SerializationPair.fromSerializer( + new GenericJackson2JsonRedisSerializer())) + .entryTtl(Duration.ofMinutes(30L)); + + return RedisCacheManager.RedisCacheManagerBuilder.fromConnectionFactory(cf) + .cacheDefaults(redisCacheConfiguration) + .build(); + } +} diff --git a/Infrastructure/src/main/java/allchive/server/infrastructure/config/redis/RedisConfig.java b/Infrastructure/src/main/java/allchive/server/infrastructure/config/redis/RedisConfig.java new file mode 100644 index 00000000..72022ae6 --- /dev/null +++ b/Infrastructure/src/main/java/allchive/server/infrastructure/config/redis/RedisConfig.java @@ -0,0 +1,45 @@ +package allchive.server.infrastructure.config.redis; + + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.connection.RedisStandaloneConfiguration; +import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.data.redis.core.RedisKeyValueAdapter; +import org.springframework.data.redis.repository.configuration.EnableRedisRepositories; + +import java.time.Duration; + +@EnableRedisRepositories( + basePackages = "allchive.server", + enableKeyspaceEvents = RedisKeyValueAdapter.EnableKeyspaceEvents.ON_STARTUP) +@Configuration +public class RedisConfig { + + @Value("${spring.redis.host}") + private String host; + + @Value("${spring.redis.port}") + private int port; + + @Value("${spring.redis.password}") + private String redisPassword; + + @Bean + public RedisConnectionFactory redisConnectionFactory() { + RedisStandaloneConfiguration redisConfig = new RedisStandaloneConfiguration(host, port); + + if (redisPassword != null && !redisPassword.isBlank()) + redisConfig.setPassword(redisPassword); + + LettuceClientConfiguration clientConfig = + LettuceClientConfiguration.builder() + .commandTimeout(Duration.ofSeconds(1)) + .shutdownTimeout(Duration.ZERO) + .build(); + return new LettuceConnectionFactory(redisConfig, clientConfig); + } +} diff --git a/Infrastructure/src/main/java/allchive/server/infrastructure/config/redis/RedissonConfig.java b/Infrastructure/src/main/java/allchive/server/infrastructure/config/redis/RedissonConfig.java new file mode 100644 index 00000000..e6cd4aed --- /dev/null +++ b/Infrastructure/src/main/java/allchive/server/infrastructure/config/redis/RedissonConfig.java @@ -0,0 +1,32 @@ +package allchive.server.infrastructure.config.redis; + + +import org.redisson.Redisson; +import org.redisson.api.RedissonClient; +import org.redisson.config.Config; +import org.redisson.jcache.configuration.RedissonConfiguration; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import javax.cache.Cache; +import javax.cache.CacheManager; +import javax.cache.Caching; + +@Configuration +public class RedissonConfig { + @Value("${spring.redis.host}") + private String redisHost; + + @Value("${spring.redis.port}") + private int redisPort; + + private static final String REDISSON_HOST_PREFIX = "redis://"; + + @Bean + public RedissonClient redissonClient() { + Config config = new Config(); + config.useSingleServer().setAddress(REDISSON_HOST_PREFIX + redisHost + ":" + redisPort); + return Redisson.create(config); + } +} diff --git a/Infrastructure/src/main/resources/application-infrastructure.yml b/Infrastructure/src/main/resources/application-infrastructure.yml index e69de29b..e3f0c896 100644 --- a/Infrastructure/src/main/resources/application-infrastructure.yml +++ b/Infrastructure/src/main/resources/application-infrastructure.yml @@ -0,0 +1,22 @@ + +--- +spring: + config: + activate: + on-profile: local + + redis: + host: localhost + port: 6379 + password: qlalfqjsgh + +--- +spring: + config: + activate: + on-profile: dev + + redis: + host: ${REDIS_HOST:localhost} + port: ${REDIS_PORT:6379} + password: ${REDIS_PASSWORD:qlalfqjsgh} \ No newline at end of file diff --git a/docker-compose.local.yml b/docker-compose.local.yml new file mode 100644 index 00000000..ddb2ad05 --- /dev/null +++ b/docker-compose.local.yml @@ -0,0 +1,9 @@ +version: '3.7' + +services: + redis: + image: redis + container_name: redis + hostname: redis + ports: + - "6379:6379" \ No newline at end of file From 7e82dc4185dce8a16d2197c682d17971e5231836 Mon Sep 17 00:00:00 2001 From: Sanghoon Jeong <67852689+wjdtkdgns@users.noreply.github.com> Date: Thu, 29 Jun 2023 15:04:39 +0900 Subject: [PATCH 07/41] =?UTF-8?q?[feat]=20depoly=20flow=20=EC=84=A4?= =?UTF-8?q?=EA=B3=84=20(#10)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [feat] action, CI/CD workflow 셋팅 #3 * [fix] CI 수정 #3 * [fix] spotless 적용 #3 * [fix] action java version 변경 #3 * [fix] action CI java version 변경 #3 * [fix] action build 명령 변경 #3 * [fix] query dsl 파일 제거 및 ignore 처리 #3 * [fix] action CI spotlessCheck 삭제 #3 --- .github/workflows/CI.yml | 37 +++++++++ .github/workflows/DevCICD.yml | 77 +++++++++++++++++++ .gitignore | 5 +- .../api/config/GlobalExceptionHandler.java | 13 ++-- .../server/api/config/SwaggerConfig.java | 5 +- .../api/config/response/ResponseAdviser.java | 7 +- .../api/config/security/CorsConfig.java | 6 +- .../api/config/security/SecurityConfig.java | 10 +-- .../server/core/annotation/Adaptor.java | 3 +- .../server/core/annotation/DomainService.java | 3 +- .../server/core/annotation/Helper.java | 3 +- .../server/core/annotation/UseCase.java | 3 +- .../server/core/annotation/Validator.java | 3 +- Domain/build.gradle | 9 +-- .../domain/common/model/QBaseTimeEntity.java | 39 ---------- .../domain/domains/block/domain/QBlock.java | 53 ------------- .../domains/category/domain/QCategory.java | 45 ----------- .../domains/category/domain/QTitle.java | 45 ----------- .../domains/content/domain/QContent.java | 45 ----------- .../content/domain/QContentTagGroup.java | 45 ----------- .../domain/domains/content/domain/QTag.java | 45 ----------- .../domains/recycle/domain/QRecycle.java | 45 ----------- .../domain/domains/report/domain/QReport.java | 45 ----------- .../domain/domains/user/domain/QScrap.java | 45 ----------- .../domain/domains/user/domain/QUser.java | 69 ----------------- .../domains/user/domain/QUserTopicGroup.java | 45 ----------- .../domains/user/domain/enums/QFormInfo.java | 41 ---------- .../domains/user/domain/enums/QOauthInfo.java | 39 ---------- .../domain/common/model/BaseTimeEntity.java | 13 ++-- .../server/domain/config/QueryDslConfig.java | 8 +- .../domains/block/adaptor/BlockAdaptor.java | 4 +- .../domain/domains/block/domain/Block.java | 10 +-- .../block/exception/BlockErrorCode.java | 2 +- .../block/repository/BlockRepository.java | 4 +- .../domains/block/service/BlockService.java | 4 +- .../block/validator/BlockValidator.java | 4 +- .../category/adaptor/CategoryAdaptor.java | 4 +- .../category/adaptor/TitleAdaptor.java | 4 +- .../domains/category/domain/Category.java | 4 +- .../domain/domains/category/domain/Title.java | 4 +- .../category/exception/CategoryErrorCode.java | 2 +- .../category/exception/TitleErrorCode.java | 2 +- .../repository/CategoryRepository.java | 4 +- .../category/repository/TitleRepository.java | 5 +- .../category/service/CategoryService.java | 4 +- .../category/service/TitleService.java | 4 +- .../category/validator/CategoryValidator.java | 4 +- .../category/validator/TitleValidator.java | 4 +- .../content/adaptor/ContentAdaptor.java | 4 +- .../adaptor/ContentTagGroupAdaptor.java | 4 +- .../domains/content/adaptor/TagAdaptor.java | 4 +- .../domains/content/domain/Content.java | 5 +- .../content/domain/ContentTagGroup.java | 5 +- .../domain/domains/content/domain/Tag.java | 4 +- .../content/exception/ContentErrorCode.java | 2 +- .../content/repository/ContentRepository.java | 4 +- .../repository/ContentTagGroupRepository.java | 4 +- .../content/repository/TagRepository.java | 4 +- .../content/service/ContentService.java | 4 +- .../service/ContentTagGroupService.java | 4 +- .../domains/content/service/TagService.java | 4 +- .../content/validator/ContentValidator.java | 4 +- .../content/validator/TagValidator.java | 4 +- .../recycle/adaptor/RecycleAdaptor.java | 4 +- .../domains/recycle/domain/Recycle.java | 4 +- .../recycle/exception/RecycleErrorCode.java | 2 +- .../recycle/repository/RecycleRepository.java | 4 +- .../recycle/service/RecycleService.java | 4 +- .../recycle/validator/RecycleValidator.java | 4 +- .../domains/report/adaptor/ReportAdaptor.java | 4 +- .../domain/domains/report/domain/Report.java | 5 +- .../report/exception/ReportErrorCode.java | 2 +- .../report/repository/ReportRepository.java | 4 +- .../domains/report/service/ReportService.java | 4 +- .../report/validator/ReportValidator.java | 4 +- .../domains/user/adaptor/ScrapAdaptor.java | 4 +- .../domains/user/adaptor/UserAdaptor.java | 4 +- .../user/adaptor/UserTopicAdaptor.java | 4 +- .../domain/domains/user/domain/Scrap.java | 4 +- .../domain/domains/user/domain/User.java | 17 ++-- .../domains/user/domain/UserTopicGroup.java | 5 +- .../domains/user/domain/enums/FormInfo.java | 4 +- .../domains/user/domain/enums/OauthInfo.java | 12 +-- .../user/exception/ScrapErrorCode.java | 2 +- .../domains/user/exception/UserErrorCode.java | 2 +- .../user/repository/ScrapRepository.java | 4 +- .../user/repository/UserRepository.java | 4 +- .../repository/UserTopicGroupRepository.java | 4 +- .../domains/user/service/ScrapService.java | 4 +- .../domains/user/service/UserService.java | 4 +- .../user/service/UserTopicGroupService.java | 4 +- .../user/validator/ScrapValidator.java | 4 +- .../domains/user/validator/UserValidator.java | 4 +- .../config/redis/RedisCacheConfig.java | 3 +- .../config/redis/RedisConfig.java | 3 +- .../config/redis/RedissonConfig.java | 5 -- config/nginx/Dockerfile | 2 + config/nginx/default.conf | 18 +++++ docker-compose.dev.yml | 23 ++++++ 99 files changed, 321 insertions(+), 852 deletions(-) create mode 100644 .github/workflows/CI.yml create mode 100644 .github/workflows/DevCICD.yml delete mode 100644 Domain/src/main/generated/allchive/server/domain/common/model/QBaseTimeEntity.java delete mode 100644 Domain/src/main/generated/allchive/server/domain/domains/block/domain/QBlock.java delete mode 100644 Domain/src/main/generated/allchive/server/domain/domains/category/domain/QCategory.java delete mode 100644 Domain/src/main/generated/allchive/server/domain/domains/category/domain/QTitle.java delete mode 100644 Domain/src/main/generated/allchive/server/domain/domains/content/domain/QContent.java delete mode 100644 Domain/src/main/generated/allchive/server/domain/domains/content/domain/QContentTagGroup.java delete mode 100644 Domain/src/main/generated/allchive/server/domain/domains/content/domain/QTag.java delete mode 100644 Domain/src/main/generated/allchive/server/domain/domains/recycle/domain/QRecycle.java delete mode 100644 Domain/src/main/generated/allchive/server/domain/domains/report/domain/QReport.java delete mode 100644 Domain/src/main/generated/allchive/server/domain/domains/user/domain/QScrap.java delete mode 100644 Domain/src/main/generated/allchive/server/domain/domains/user/domain/QUser.java delete mode 100644 Domain/src/main/generated/allchive/server/domain/domains/user/domain/QUserTopicGroup.java delete mode 100644 Domain/src/main/generated/allchive/server/domain/domains/user/domain/enums/QFormInfo.java delete mode 100644 Domain/src/main/generated/allchive/server/domain/domains/user/domain/enums/QOauthInfo.java create mode 100644 config/nginx/Dockerfile create mode 100644 config/nginx/default.conf create mode 100644 docker-compose.dev.yml diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml new file mode 100644 index 00000000..d1c962a8 --- /dev/null +++ b/.github/workflows/CI.yml @@ -0,0 +1,37 @@ +name: CI + +on: + push: + branches: [ "dev", "main" ] + pull_request: + branch: 'dev' + types: [ opened, synchronize, reopened ] + +jobs: + build: + name: CI + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Set up Java + uses: actions/setup-java@v3 + with: + java-version: 17 + distribution: 'corretto' + + - name: Grant execute permission for gradlew + run: chmod +x gradlew + + - name: Build with Gradle + run: ./gradlew :Api:build + + - name: Cache Gradle packages + uses: actions/cache@v1 + with: + path: ~/.gradle/caches + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }} + restore-keys: ${{ runner.os }}-gradle \ No newline at end of file diff --git a/.github/workflows/DevCICD.yml b/.github/workflows/DevCICD.yml new file mode 100644 index 00000000..f460898f --- /dev/null +++ b/.github/workflows/DevCICD.yml @@ -0,0 +1,77 @@ +name: CI/CD + +on: + push: + branches: dev + +env: + PROFILE_DEV: dev + +permissions: + contents: read + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: checkout + uses: actions/checkout@v2 + + - name: Set up Java + uses: actions/setup-java@v3 + with: + java-version: 17 + distribution: 'corretto' + + - name: Grant execute permission for gradlew + run: chmod +x ./gradlew + + - name: Build with Gradle + run: ./gradlew build + + - name: Docker build + run: | + docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }} + docker build -t ${{ secrets.DOCKER_USERNAME }}/${{ secrets.DOCKER_DEV_REPO }} . + docker push ${{ secrets.DOCKER_USERNAME }}/${{ secrets.DOCKER_DEV_REPO }}:latest + + - name: get GitHub IP + id: ip + uses: haythem/public-ip@v1.2 + + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v1 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: ${{ secrets.AWS_REGION }} + + - name: Add GitHub IP to AWS + run: | + aws ec2 authorize-security-group-ingress --group-id ${{ secrets.AWS_SG_ID }} --protocol tcp --port 22 --cidr ${{ steps.ip.outputs.ipv4 }}/32 + + - name: Deploy + uses: appleboy/ssh-action@master + with: + host: ${{ secrets.EC2_DEV_HOST }} + username: ${{ secrets.EC2_DEV_USERNAME }} + password: ${{ secrets.EC2_DEV_PASSWORD }} + port: ${{ secrets.EC2_SSH_PORT }} + timeout: 60s + script: | + cd allchive-dev + + sudo touch .env + echo "${{ secrets.ENV_VARS }}" | sudo tee .env > /dev/null + + sudo docker stop $(sudo docker ps -a -q) + sudo docker rm $(sudo docker ps -a -q) + sudo docker rmi $(sudo docker images -q) + sudo docker pull ${{ secrets.DOCKER_USERNAME }}/${{ secrets.DOCKER_REPO }}:latest + sudo docker-compose -f ~/allchive-dev/docker-compose.yml --env-file ~/allchive-dev/.env up --build -d + sudo docker system prune --all -f + + - name: Remove IP FROM security group + run: | + aws ec2 revoke-security-group-ingress --group-id ${{ secrets.AWS_SG_ID }} --protocol tcp --port 22 --cidr ${{ steps.ip.outputs.ipv4 }}/32 diff --git a/.gitignore b/.gitignore index 7bca7f65..9d21c5b9 100644 --- a/.gitignore +++ b/.gitignore @@ -39,4 +39,7 @@ out/ .DS_Store .env -.env.* \ No newline at end of file +.env.* + + +Domain/src/main/generated/**/*.java \ No newline at end of file diff --git a/Api/src/main/java/allchive/server/api/config/GlobalExceptionHandler.java b/Api/src/main/java/allchive/server/api/config/GlobalExceptionHandler.java index 7c628ed4..4b29cac6 100644 --- a/Api/src/main/java/allchive/server/api/config/GlobalExceptionHandler.java +++ b/Api/src/main/java/allchive/server/api/config/GlobalExceptionHandler.java @@ -1,9 +1,15 @@ package allchive.server.api.config; +import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR; + import allchive.server.core.dto.ErrorReason; import allchive.server.core.error.BaseErrorException; import allchive.server.core.error.ErrorResponse; import allchive.server.core.error.GlobalErrorCode; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import javax.servlet.http.HttpServletRequest; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; @@ -17,13 +23,6 @@ import org.springframework.web.context.request.WebRequest; import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; -import javax.servlet.http.HttpServletRequest; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR; - @Slf4j @RestControllerAdvice public class GlobalExceptionHandler extends ResponseEntityExceptionHandler { diff --git a/Api/src/main/java/allchive/server/api/config/SwaggerConfig.java b/Api/src/main/java/allchive/server/api/config/SwaggerConfig.java index 62e963ed..99222fd6 100644 --- a/Api/src/main/java/allchive/server/api/config/SwaggerConfig.java +++ b/Api/src/main/java/allchive/server/api/config/SwaggerConfig.java @@ -9,12 +9,11 @@ import io.swagger.v3.oas.models.security.SecurityScheme.In; import io.swagger.v3.oas.models.security.SecurityScheme.Type; import io.swagger.v3.oas.models.servers.Server; +import java.util.List; +import javax.servlet.ServletContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import javax.servlet.ServletContext; -import java.util.List; - @Configuration public class SwaggerConfig { @Bean diff --git a/Api/src/main/java/allchive/server/api/config/response/ResponseAdviser.java b/Api/src/main/java/allchive/server/api/config/response/ResponseAdviser.java index 237aeb74..a1424f25 100644 --- a/Api/src/main/java/allchive/server/api/config/response/ResponseAdviser.java +++ b/Api/src/main/java/allchive/server/api/config/response/ResponseAdviser.java @@ -1,5 +1,8 @@ package allchive.server.api.config.response; +import static allchive.server.core.consts.AllchiveConst.SwaggerPatterns; + +import javax.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.core.MethodParameter; import org.springframework.http.HttpStatus; @@ -13,10 +16,6 @@ import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; import org.springframework.web.util.ContentCachingRequestWrapper; -import javax.servlet.http.HttpServletResponse; - -import static allchive.server.core.consts.AllchiveConst.SwaggerPatterns; - @Slf4j @RestControllerAdvice public class ResponseAdviser implements ResponseBodyAdvice<Object> { diff --git a/Api/src/main/java/allchive/server/api/config/security/CorsConfig.java b/Api/src/main/java/allchive/server/api/config/security/CorsConfig.java index 6728e41d..291e1711 100644 --- a/Api/src/main/java/allchive/server/api/config/security/CorsConfig.java +++ b/Api/src/main/java/allchive/server/api/config/security/CorsConfig.java @@ -1,12 +1,12 @@ package allchive.server.api.config.security; + +import java.util.ArrayList; import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; -import java.util.ArrayList; - @Configuration @RequiredArgsConstructor public class CorsConfig implements WebMvcConfigurer { @@ -25,4 +25,4 @@ public void addCorsMappings(CorsRegistry registry) { .allowCredentials(true) .maxAge(3600L); } -} \ No newline at end of file +} diff --git a/Api/src/main/java/allchive/server/api/config/security/SecurityConfig.java b/Api/src/main/java/allchive/server/api/config/security/SecurityConfig.java index e9b3d59b..fc7a8cd7 100644 --- a/Api/src/main/java/allchive/server/api/config/security/SecurityConfig.java +++ b/Api/src/main/java/allchive/server/api/config/security/SecurityConfig.java @@ -1,5 +1,6 @@ package allchive.server.api.config.security; + import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; @@ -13,22 +14,17 @@ public class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { - http - .formLogin() + http.formLogin() .disable() - .cors() .disable() - .csrf() .disable() - .httpBasic() .disable() - .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.NEVER); return http.build(); } -} \ No newline at end of file +} diff --git a/Core/src/main/java/allchive/server/core/annotation/Adaptor.java b/Core/src/main/java/allchive/server/core/annotation/Adaptor.java index 416925ce..a84c36bb 100644 --- a/Core/src/main/java/allchive/server/core/annotation/Adaptor.java +++ b/Core/src/main/java/allchive/server/core/annotation/Adaptor.java @@ -1,11 +1,10 @@ package allchive.server.core.annotation; +import java.lang.annotation.*; import org.springframework.core.annotation.AliasFor; import org.springframework.stereotype.Component; -import java.lang.annotation.*; - @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented diff --git a/Core/src/main/java/allchive/server/core/annotation/DomainService.java b/Core/src/main/java/allchive/server/core/annotation/DomainService.java index 62d0732d..385ad13c 100644 --- a/Core/src/main/java/allchive/server/core/annotation/DomainService.java +++ b/Core/src/main/java/allchive/server/core/annotation/DomainService.java @@ -1,11 +1,10 @@ package allchive.server.core.annotation; +import java.lang.annotation.*; import org.springframework.core.annotation.AliasFor; import org.springframework.stereotype.Component; -import java.lang.annotation.*; - @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented diff --git a/Core/src/main/java/allchive/server/core/annotation/Helper.java b/Core/src/main/java/allchive/server/core/annotation/Helper.java index f9f5c8de..0291f0e3 100644 --- a/Core/src/main/java/allchive/server/core/annotation/Helper.java +++ b/Core/src/main/java/allchive/server/core/annotation/Helper.java @@ -1,11 +1,10 @@ package allchive.server.core.annotation; +import java.lang.annotation.*; import org.springframework.core.annotation.AliasFor; import org.springframework.stereotype.Component; -import java.lang.annotation.*; - @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented diff --git a/Core/src/main/java/allchive/server/core/annotation/UseCase.java b/Core/src/main/java/allchive/server/core/annotation/UseCase.java index e02c00ac..1926bd69 100644 --- a/Core/src/main/java/allchive/server/core/annotation/UseCase.java +++ b/Core/src/main/java/allchive/server/core/annotation/UseCase.java @@ -1,11 +1,10 @@ package allchive.server.core.annotation; +import java.lang.annotation.*; import org.springframework.core.annotation.AliasFor; import org.springframework.stereotype.Component; -import java.lang.annotation.*; - @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented diff --git a/Core/src/main/java/allchive/server/core/annotation/Validator.java b/Core/src/main/java/allchive/server/core/annotation/Validator.java index a0c16299..d950aa05 100644 --- a/Core/src/main/java/allchive/server/core/annotation/Validator.java +++ b/Core/src/main/java/allchive/server/core/annotation/Validator.java @@ -1,11 +1,10 @@ package allchive.server.core.annotation; +import java.lang.annotation.*; import org.springframework.core.annotation.AliasFor; import org.springframework.stereotype.Component; -import java.lang.annotation.*; - @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented diff --git a/Domain/build.gradle b/Domain/build.gradle index 43ed2263..b1156f5c 100644 --- a/Domain/build.gradle +++ b/Domain/build.gradle @@ -8,24 +8,17 @@ dependencies { // TODO : Core에 impl 했는데 여기서 이용 못하는 문제 해결해야함 implementation 'org.springframework.boot:spring-boot-starter-validation' -// api 'mysql:mysql-connector-java:8.0.32' - implementation project(':Core') implementation project(':Infrastructure') //QueryDsl -// implementation "com.querydsl:querydsl-jpa:5.0.0:jakarta" -// annotationProcessor "com.querydsl:querydsl-apt:5.0.0:jakarta" -// annotationProcessor "jakarta.annotation:jakarta.annotation-api" -// annotationProcessor "jakarta.persistence:jakarta.persistence-api" - api ("com.querydsl:querydsl-core") // querydsl api ("com.querydsl:querydsl-jpa") // querydsl annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jpa" // querydsl JPAAnnotationProcessor 사용 지정 annotationProcessor("jakarta.persistence:jakarta.persistence-api") annotationProcessor("jakarta.annotation:jakarta.annotation-api") } -//https://jojoldu.tistory.com/372 + def generated='src/main/generated' sourceSets { main.java.srcDirs += [ generated ] diff --git a/Domain/src/main/generated/allchive/server/domain/common/model/QBaseTimeEntity.java b/Domain/src/main/generated/allchive/server/domain/common/model/QBaseTimeEntity.java deleted file mode 100644 index 2aefedf4..00000000 --- a/Domain/src/main/generated/allchive/server/domain/common/model/QBaseTimeEntity.java +++ /dev/null @@ -1,39 +0,0 @@ -package allchive.server.domain.common.model; - -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<BaseTimeEntity> { - - private static final long serialVersionUID = 4668206L; - - public static final QBaseTimeEntity baseTimeEntity = new QBaseTimeEntity("baseTimeEntity"); - - public final DateTimePath<java.time.LocalDateTime> createdAt = createDateTime("createdAt", java.time.LocalDateTime.class); - - public final DateTimePath<java.time.LocalDateTime> updatedAt = createDateTime("updatedAt", java.time.LocalDateTime.class); - - public QBaseTimeEntity(String variable) { - super(BaseTimeEntity.class, forVariable(variable)); - } - - public QBaseTimeEntity(Path<? extends BaseTimeEntity> path) { - super(path.getType(), path.getMetadata()); - } - - public QBaseTimeEntity(PathMetadata metadata) { - super(BaseTimeEntity.class, metadata); - } - -} - diff --git a/Domain/src/main/generated/allchive/server/domain/domains/block/domain/QBlock.java b/Domain/src/main/generated/allchive/server/domain/domains/block/domain/QBlock.java deleted file mode 100644 index c2af9def..00000000 --- a/Domain/src/main/generated/allchive/server/domain/domains/block/domain/QBlock.java +++ /dev/null @@ -1,53 +0,0 @@ -package allchive.server.domain.domains.block.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; - - -/** - * QBlock is a Querydsl query type for Block - */ -@Generated("com.querydsl.codegen.DefaultEntitySerializer") -public class QBlock extends EntityPathBase<Block> { - - private static final long serialVersionUID = 1039614782L; - - public static final QBlock block = new QBlock("block"); - - public final allchive.server.domain.common.model.QBaseTimeEntity _super = new allchive.server.domain.common.model.QBaseTimeEntity(this); - - public final NumberPath<Long> blockContent = createNumber("blockContent", Long.class); - - public final NumberPath<Long> blockFrom = createNumber("blockFrom", Long.class); - - public final EnumPath<allchive.server.domain.domains.block.domain.enums.BlockType> blockType = createEnum("blockType", allchive.server.domain.domains.block.domain.enums.BlockType.class); - - public final NumberPath<Long> blockUser = createNumber("blockUser", Long.class); - - //inherited - public final DateTimePath<java.time.LocalDateTime> createdAt = _super.createdAt; - - public final NumberPath<Long> id = createNumber("id", Long.class); - - //inherited - public final DateTimePath<java.time.LocalDateTime> updatedAt = _super.updatedAt; - - public QBlock(String variable) { - super(Block.class, forVariable(variable)); - } - - public QBlock(Path<? extends Block> path) { - super(path.getType(), path.getMetadata()); - } - - public QBlock(PathMetadata metadata) { - super(Block.class, metadata); - } - -} - diff --git a/Domain/src/main/generated/allchive/server/domain/domains/category/domain/QCategory.java b/Domain/src/main/generated/allchive/server/domain/domains/category/domain/QCategory.java deleted file mode 100644 index 2172db24..00000000 --- a/Domain/src/main/generated/allchive/server/domain/domains/category/domain/QCategory.java +++ /dev/null @@ -1,45 +0,0 @@ -package allchive.server.domain.domains.category.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; - - -/** - * QCategory is a Querydsl query type for Category - */ -@Generated("com.querydsl.codegen.DefaultEntitySerializer") -public class QCategory extends EntityPathBase<Category> { - - private static final long serialVersionUID = -530930862L; - - public static final QCategory category = new QCategory("category"); - - public final allchive.server.domain.common.model.QBaseTimeEntity _super = new allchive.server.domain.common.model.QBaseTimeEntity(this); - - //inherited - public final DateTimePath<java.time.LocalDateTime> createdAt = _super.createdAt; - - public final NumberPath<Long> id = createNumber("id", Long.class); - - //inherited - public final DateTimePath<java.time.LocalDateTime> updatedAt = _super.updatedAt; - - public QCategory(String variable) { - super(Category.class, forVariable(variable)); - } - - public QCategory(Path<? extends Category> path) { - super(path.getType(), path.getMetadata()); - } - - public QCategory(PathMetadata metadata) { - super(Category.class, metadata); - } - -} - diff --git a/Domain/src/main/generated/allchive/server/domain/domains/category/domain/QTitle.java b/Domain/src/main/generated/allchive/server/domain/domains/category/domain/QTitle.java deleted file mode 100644 index b733da1d..00000000 --- a/Domain/src/main/generated/allchive/server/domain/domains/category/domain/QTitle.java +++ /dev/null @@ -1,45 +0,0 @@ -package allchive.server.domain.domains.category.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; - - -/** - * QTitle is a Querydsl query type for Title - */ -@Generated("com.querydsl.codegen.DefaultEntitySerializer") -public class QTitle extends EntityPathBase<Title> { - - private static final long serialVersionUID = -1270219644L; - - public static final QTitle title = new QTitle("title"); - - public final allchive.server.domain.common.model.QBaseTimeEntity _super = new allchive.server.domain.common.model.QBaseTimeEntity(this); - - //inherited - public final DateTimePath<java.time.LocalDateTime> createdAt = _super.createdAt; - - public final NumberPath<Long> id = createNumber("id", Long.class); - - //inherited - public final DateTimePath<java.time.LocalDateTime> updatedAt = _super.updatedAt; - - public QTitle(String variable) { - super(Title.class, forVariable(variable)); - } - - public QTitle(Path<? extends Title> path) { - super(path.getType(), path.getMetadata()); - } - - public QTitle(PathMetadata metadata) { - super(Title.class, metadata); - } - -} - diff --git a/Domain/src/main/generated/allchive/server/domain/domains/content/domain/QContent.java b/Domain/src/main/generated/allchive/server/domain/domains/content/domain/QContent.java deleted file mode 100644 index efb51bae..00000000 --- a/Domain/src/main/generated/allchive/server/domain/domains/content/domain/QContent.java +++ /dev/null @@ -1,45 +0,0 @@ -package allchive.server.domain.domains.content.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; - - -/** - * QContent is a Querydsl query type for Content - */ -@Generated("com.querydsl.codegen.DefaultEntitySerializer") -public class QContent extends EntityPathBase<Content> { - - private static final long serialVersionUID = -1452122370L; - - public static final QContent content = new QContent("content"); - - public final allchive.server.domain.common.model.QBaseTimeEntity _super = new allchive.server.domain.common.model.QBaseTimeEntity(this); - - //inherited - public final DateTimePath<java.time.LocalDateTime> createdAt = _super.createdAt; - - public final NumberPath<Long> id = createNumber("id", Long.class); - - //inherited - public final DateTimePath<java.time.LocalDateTime> updatedAt = _super.updatedAt; - - public QContent(String variable) { - super(Content.class, forVariable(variable)); - } - - public QContent(Path<? extends Content> path) { - super(path.getType(), path.getMetadata()); - } - - public QContent(PathMetadata metadata) { - super(Content.class, metadata); - } - -} - diff --git a/Domain/src/main/generated/allchive/server/domain/domains/content/domain/QContentTagGroup.java b/Domain/src/main/generated/allchive/server/domain/domains/content/domain/QContentTagGroup.java deleted file mode 100644 index c8c4a24f..00000000 --- a/Domain/src/main/generated/allchive/server/domain/domains/content/domain/QContentTagGroup.java +++ /dev/null @@ -1,45 +0,0 @@ -package allchive.server.domain.domains.content.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; - - -/** - * QContentTagGroup is a Querydsl query type for ContentTagGroup - */ -@Generated("com.querydsl.codegen.DefaultEntitySerializer") -public class QContentTagGroup extends EntityPathBase<ContentTagGroup> { - - private static final long serialVersionUID = 689626147L; - - public static final QContentTagGroup contentTagGroup = new QContentTagGroup("contentTagGroup"); - - public final allchive.server.domain.common.model.QBaseTimeEntity _super = new allchive.server.domain.common.model.QBaseTimeEntity(this); - - //inherited - public final DateTimePath<java.time.LocalDateTime> createdAt = _super.createdAt; - - public final NumberPath<Long> id = createNumber("id", Long.class); - - //inherited - public final DateTimePath<java.time.LocalDateTime> updatedAt = _super.updatedAt; - - public QContentTagGroup(String variable) { - super(ContentTagGroup.class, forVariable(variable)); - } - - public QContentTagGroup(Path<? extends ContentTagGroup> path) { - super(path.getType(), path.getMetadata()); - } - - public QContentTagGroup(PathMetadata metadata) { - super(ContentTagGroup.class, metadata); - } - -} - diff --git a/Domain/src/main/generated/allchive/server/domain/domains/content/domain/QTag.java b/Domain/src/main/generated/allchive/server/domain/domains/content/domain/QTag.java deleted file mode 100644 index fb78b204..00000000 --- a/Domain/src/main/generated/allchive/server/domain/domains/content/domain/QTag.java +++ /dev/null @@ -1,45 +0,0 @@ -package allchive.server.domain.domains.content.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; - - -/** - * QTag is a Querydsl query type for Tag - */ -@Generated("com.querydsl.codegen.DefaultEntitySerializer") -public class QTag extends EntityPathBase<Tag> { - - private static final long serialVersionUID = -384240481L; - - public static final QTag tag = new QTag("tag"); - - public final allchive.server.domain.common.model.QBaseTimeEntity _super = new allchive.server.domain.common.model.QBaseTimeEntity(this); - - //inherited - public final DateTimePath<java.time.LocalDateTime> createdAt = _super.createdAt; - - public final NumberPath<Long> id = createNumber("id", Long.class); - - //inherited - public final DateTimePath<java.time.LocalDateTime> updatedAt = _super.updatedAt; - - public QTag(String variable) { - super(Tag.class, forVariable(variable)); - } - - public QTag(Path<? extends Tag> path) { - super(path.getType(), path.getMetadata()); - } - - public QTag(PathMetadata metadata) { - super(Tag.class, metadata); - } - -} - diff --git a/Domain/src/main/generated/allchive/server/domain/domains/recycle/domain/QRecycle.java b/Domain/src/main/generated/allchive/server/domain/domains/recycle/domain/QRecycle.java deleted file mode 100644 index 23dc9eb1..00000000 --- a/Domain/src/main/generated/allchive/server/domain/domains/recycle/domain/QRecycle.java +++ /dev/null @@ -1,45 +0,0 @@ -package allchive.server.domain.domains.recycle.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; - - -/** - * QRecycle is a Querydsl query type for Recycle - */ -@Generated("com.querydsl.codegen.DefaultEntitySerializer") -public class QRecycle extends EntityPathBase<Recycle> { - - private static final long serialVersionUID = -1015076930L; - - public static final QRecycle recycle = new QRecycle("recycle"); - - public final allchive.server.domain.common.model.QBaseTimeEntity _super = new allchive.server.domain.common.model.QBaseTimeEntity(this); - - //inherited - public final DateTimePath<java.time.LocalDateTime> createdAt = _super.createdAt; - - public final NumberPath<Long> id = createNumber("id", Long.class); - - //inherited - public final DateTimePath<java.time.LocalDateTime> updatedAt = _super.updatedAt; - - public QRecycle(String variable) { - super(Recycle.class, forVariable(variable)); - } - - public QRecycle(Path<? extends Recycle> path) { - super(path.getType(), path.getMetadata()); - } - - public QRecycle(PathMetadata metadata) { - super(Recycle.class, metadata); - } - -} - diff --git a/Domain/src/main/generated/allchive/server/domain/domains/report/domain/QReport.java b/Domain/src/main/generated/allchive/server/domain/domains/report/domain/QReport.java deleted file mode 100644 index 11f7306a..00000000 --- a/Domain/src/main/generated/allchive/server/domain/domains/report/domain/QReport.java +++ /dev/null @@ -1,45 +0,0 @@ -package allchive.server.domain.domains.report.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; - - -/** - * QReport is a Querydsl query type for Report - */ -@Generated("com.querydsl.codegen.DefaultEntitySerializer") -public class QReport extends EntityPathBase<Report> { - - private static final long serialVersionUID = 145123518L; - - public static final QReport report = new QReport("report"); - - public final allchive.server.domain.common.model.QBaseTimeEntity _super = new allchive.server.domain.common.model.QBaseTimeEntity(this); - - //inherited - public final DateTimePath<java.time.LocalDateTime> createdAt = _super.createdAt; - - public final NumberPath<Long> id = createNumber("id", Long.class); - - //inherited - public final DateTimePath<java.time.LocalDateTime> updatedAt = _super.updatedAt; - - public QReport(String variable) { - super(Report.class, forVariable(variable)); - } - - public QReport(Path<? extends Report> path) { - super(path.getType(), path.getMetadata()); - } - - public QReport(PathMetadata metadata) { - super(Report.class, metadata); - } - -} - diff --git a/Domain/src/main/generated/allchive/server/domain/domains/user/domain/QScrap.java b/Domain/src/main/generated/allchive/server/domain/domains/user/domain/QScrap.java deleted file mode 100644 index f6b2998c..00000000 --- a/Domain/src/main/generated/allchive/server/domain/domains/user/domain/QScrap.java +++ /dev/null @@ -1,45 +0,0 @@ -package allchive.server.domain.domains.user.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; - - -/** - * QScrap is a Querydsl query type for Scrap - */ -@Generated("com.querydsl.codegen.DefaultEntitySerializer") -public class QScrap extends EntityPathBase<Scrap> { - - private static final long serialVersionUID = 53975888L; - - public static final QScrap scrap = new QScrap("scrap"); - - public final allchive.server.domain.common.model.QBaseTimeEntity _super = new allchive.server.domain.common.model.QBaseTimeEntity(this); - - //inherited - public final DateTimePath<java.time.LocalDateTime> createdAt = _super.createdAt; - - public final NumberPath<Long> id = createNumber("id", Long.class); - - //inherited - public final DateTimePath<java.time.LocalDateTime> updatedAt = _super.updatedAt; - - public QScrap(String variable) { - super(Scrap.class, forVariable(variable)); - } - - public QScrap(Path<? extends Scrap> path) { - super(path.getType(), path.getMetadata()); - } - - public QScrap(PathMetadata metadata) { - super(Scrap.class, metadata); - } - -} - diff --git a/Domain/src/main/generated/allchive/server/domain/domains/user/domain/QUser.java b/Domain/src/main/generated/allchive/server/domain/domains/user/domain/QUser.java deleted file mode 100644 index 5da919cc..00000000 --- a/Domain/src/main/generated/allchive/server/domain/domains/user/domain/QUser.java +++ /dev/null @@ -1,69 +0,0 @@ -package allchive.server.domain.domains.user.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; - - -/** - * QUser is a Querydsl query type for User - */ -@Generated("com.querydsl.codegen.DefaultEntitySerializer") -public class QUser extends EntityPathBase<User> { - - private static final long serialVersionUID = 2080025708L; - - private static final PathInits INITS = PathInits.DIRECT2; - - public static final QUser user = new QUser("user"); - - public final allchive.server.domain.common.model.QBaseTimeEntity _super = new allchive.server.domain.common.model.QBaseTimeEntity(this); - - //inherited - public final DateTimePath<java.time.LocalDateTime> createdAt = _super.createdAt; - - public final StringPath Email = createString("Email"); - - public final NumberPath<Long> id = createNumber("id", Long.class); - - public final DateTimePath<java.time.LocalDateTime> lastLoginAt = createDateTime("lastLoginAt", java.time.LocalDateTime.class); - - public final StringPath nickname = createString("nickname"); - - public final allchive.server.domain.domains.user.domain.enums.QOauthInfo oauthInfo; - - public final StringPath profileImgUrl = createString("profileImgUrl"); - - //inherited - public final DateTimePath<java.time.LocalDateTime> updatedAt = _super.updatedAt; - - public final EnumPath<allchive.server.domain.domains.user.domain.enums.UserState> userState = createEnum("userState", allchive.server.domain.domains.user.domain.enums.UserState.class); - - public QUser(String variable) { - this(User.class, forVariable(variable), INITS); - } - - public QUser(Path<? extends User> path) { - this(path.getType(), path.getMetadata(), PathInits.getFor(path.getMetadata(), INITS)); - } - - public QUser(PathMetadata metadata) { - this(metadata, PathInits.getFor(metadata, INITS)); - } - - public QUser(PathMetadata metadata, PathInits inits) { - this(User.class, metadata, inits); - } - - public QUser(Class<? extends User> type, PathMetadata metadata, PathInits inits) { - super(type, metadata, inits); - this.oauthInfo = inits.isInitialized("oauthInfo") ? new allchive.server.domain.domains.user.domain.enums.QOauthInfo(forProperty("oauthInfo")) : null; - } - -} - diff --git a/Domain/src/main/generated/allchive/server/domain/domains/user/domain/QUserTopicGroup.java b/Domain/src/main/generated/allchive/server/domain/domains/user/domain/QUserTopicGroup.java deleted file mode 100644 index c1e768b4..00000000 --- a/Domain/src/main/generated/allchive/server/domain/domains/user/domain/QUserTopicGroup.java +++ /dev/null @@ -1,45 +0,0 @@ -package allchive.server.domain.domains.user.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; - - -/** - * QUserTopicGroup is a Querydsl query type for UserTopicGroup - */ -@Generated("com.querydsl.codegen.DefaultEntitySerializer") -public class QUserTopicGroup extends EntityPathBase<UserTopicGroup> { - - private static final long serialVersionUID = 131338268L; - - public static final QUserTopicGroup userTopicGroup = new QUserTopicGroup("userTopicGroup"); - - public final allchive.server.domain.common.model.QBaseTimeEntity _super = new allchive.server.domain.common.model.QBaseTimeEntity(this); - - //inherited - public final DateTimePath<java.time.LocalDateTime> createdAt = _super.createdAt; - - public final NumberPath<Long> id = createNumber("id", Long.class); - - //inherited - public final DateTimePath<java.time.LocalDateTime> updatedAt = _super.updatedAt; - - public QUserTopicGroup(String variable) { - super(UserTopicGroup.class, forVariable(variable)); - } - - public QUserTopicGroup(Path<? extends UserTopicGroup> path) { - super(path.getType(), path.getMetadata()); - } - - public QUserTopicGroup(PathMetadata metadata) { - super(UserTopicGroup.class, metadata); - } - -} - diff --git a/Domain/src/main/generated/allchive/server/domain/domains/user/domain/enums/QFormInfo.java b/Domain/src/main/generated/allchive/server/domain/domains/user/domain/enums/QFormInfo.java deleted file mode 100644 index f1936850..00000000 --- a/Domain/src/main/generated/allchive/server/domain/domains/user/domain/enums/QFormInfo.java +++ /dev/null @@ -1,41 +0,0 @@ -package allchive.server.domain.domains.user.domain.enums; - -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; - - -/** - * QFormInfo is a Querydsl query type for FormInfo - */ -@Generated("com.querydsl.codegen.DefaultEmbeddableSerializer") -public class QFormInfo extends BeanPath<FormInfo> { - - private static final long serialVersionUID = 1297773007L; - - public static final QFormInfo formInfo = new QFormInfo("formInfo"); - - public final StringPath formId = createString("formId"); - - public final StringPath formPwd = createString("formPwd"); - - public final StringPath phone = createString("phone"); - - public QFormInfo(String variable) { - super(FormInfo.class, forVariable(variable)); - } - - public QFormInfo(Path<? extends FormInfo> path) { - super(path.getType(), path.getMetadata()); - } - - public QFormInfo(PathMetadata metadata) { - super(FormInfo.class, metadata); - } - -} - diff --git a/Domain/src/main/generated/allchive/server/domain/domains/user/domain/enums/QOauthInfo.java b/Domain/src/main/generated/allchive/server/domain/domains/user/domain/enums/QOauthInfo.java deleted file mode 100644 index b9201d2c..00000000 --- a/Domain/src/main/generated/allchive/server/domain/domains/user/domain/enums/QOauthInfo.java +++ /dev/null @@ -1,39 +0,0 @@ -package allchive.server.domain.domains.user.domain.enums; - -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; - - -/** - * QOauthInfo is a Querydsl query type for OauthInfo - */ -@Generated("com.querydsl.codegen.DefaultEmbeddableSerializer") -public class QOauthInfo extends BeanPath<OauthInfo> { - - private static final long serialVersionUID = -1839989432L; - - public static final QOauthInfo oauthInfo = new QOauthInfo("oauthInfo"); - - public final StringPath oid = createString("oid"); - - public final EnumPath<OauthProvider> provider = createEnum("provider", OauthProvider.class); - - public QOauthInfo(String variable) { - super(OauthInfo.class, forVariable(variable)); - } - - public QOauthInfo(Path<? extends OauthInfo> path) { - super(path.getType(), path.getMetadata()); - } - - public QOauthInfo(PathMetadata metadata) { - super(OauthInfo.class, metadata); - } - -} - diff --git a/Domain/src/main/java/allchive/server/domain/common/model/BaseTimeEntity.java b/Domain/src/main/java/allchive/server/domain/common/model/BaseTimeEntity.java index 2bafd9cf..f3a7dc07 100644 --- a/Domain/src/main/java/allchive/server/domain/common/model/BaseTimeEntity.java +++ b/Domain/src/main/java/allchive/server/domain/common/model/BaseTimeEntity.java @@ -1,16 +1,15 @@ package allchive.server.domain.common.model; +import java.time.LocalDateTime; +import javax.persistence.Column; +import javax.persistence.EntityListeners; +import javax.persistence.MappedSuperclass; import lombok.Getter; import org.springframework.data.annotation.CreatedDate; import org.springframework.data.annotation.LastModifiedDate; import org.springframework.data.jpa.domain.support.AuditingEntityListener; -import javax.persistence.Column; -import javax.persistence.EntityListeners; -import javax.persistence.MappedSuperclass; -import java.time.LocalDateTime; - @Getter @MappedSuperclass @EntityListeners(AuditingEntityListener.class) @@ -20,7 +19,5 @@ public abstract class BaseTimeEntity { @CreatedDate private LocalDateTime createdAt; - @Column - @LastModifiedDate - private LocalDateTime updatedAt; + @Column @LastModifiedDate private LocalDateTime updatedAt; } diff --git a/Domain/src/main/java/allchive/server/domain/config/QueryDslConfig.java b/Domain/src/main/java/allchive/server/domain/config/QueryDslConfig.java index b947f661..84342642 100644 --- a/Domain/src/main/java/allchive/server/domain/config/QueryDslConfig.java +++ b/Domain/src/main/java/allchive/server/domain/config/QueryDslConfig.java @@ -2,16 +2,14 @@ import com.querydsl.jpa.impl.JPAQueryFactory; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; @Configuration public class QueryDslConfig { - @PersistenceContext - private EntityManager entityManager; + @PersistenceContext private EntityManager entityManager; @Bean public JPAQueryFactory jpaQueryFactory() { diff --git a/Domain/src/main/java/allchive/server/domain/domains/block/adaptor/BlockAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/block/adaptor/BlockAdaptor.java index 3e01e9ca..2a748750 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/block/adaptor/BlockAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/block/adaptor/BlockAdaptor.java @@ -1,9 +1,9 @@ package allchive.server.domain.domains.block.adaptor; + import allchive.server.core.annotation.Adaptor; import lombok.RequiredArgsConstructor; @Adaptor @RequiredArgsConstructor -public class BlockAdaptor { -} +public class BlockAdaptor {} diff --git a/Domain/src/main/java/allchive/server/domain/domains/block/domain/Block.java b/Domain/src/main/java/allchive/server/domain/domains/block/domain/Block.java index d359be9f..b5ffc4c1 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/block/domain/Block.java +++ b/Domain/src/main/java/allchive/server/domain/domains/block/domain/Block.java @@ -1,14 +1,14 @@ package allchive.server.domain.domains.block.domain; + import allchive.server.domain.common.model.BaseTimeEntity; import allchive.server.domain.domains.block.domain.enums.BlockType; +import javax.persistence.*; +import javax.validation.constraints.NotNull; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; -import javax.persistence.*; -import javax.validation.constraints.NotNull; - @Getter @Table(name = "tbl_block") @Entity @@ -19,8 +19,7 @@ public class Block extends BaseTimeEntity { private Long id; // block 한 사람 - @NotNull - private Long blockFrom; + @NotNull private Long blockFrom; @Enumerated(EnumType.STRING) private BlockType blockType; @@ -30,5 +29,4 @@ public class Block extends BaseTimeEntity { // Block 당한 컨텐츠 private Long blockContent; - } diff --git a/Domain/src/main/java/allchive/server/domain/domains/block/exception/BlockErrorCode.java b/Domain/src/main/java/allchive/server/domain/domains/block/exception/BlockErrorCode.java index d131214f..248eb69e 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/block/exception/BlockErrorCode.java +++ b/Domain/src/main/java/allchive/server/domain/domains/block/exception/BlockErrorCode.java @@ -1,5 +1,6 @@ package allchive.server.domain.domains.block.exception; + import allchive.server.core.dto.ErrorReason; import allchive.server.core.error.BaseErrorCode; import lombok.AllArgsConstructor; @@ -17,6 +18,5 @@ public enum BlockErrorCode implements BaseErrorCode { @Override public ErrorReason getErrorReason() { return ErrorReason.of(status.value(), code, reason); - } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/block/repository/BlockRepository.java b/Domain/src/main/java/allchive/server/domain/domains/block/repository/BlockRepository.java index 2cc0f788..aaf0e631 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/block/repository/BlockRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/block/repository/BlockRepository.java @@ -1,7 +1,7 @@ package allchive.server.domain.domains.block.repository; + import allchive.server.domain.domains.block.domain.Block; import org.springframework.data.jpa.repository.JpaRepository; -public interface BlockRepository extends JpaRepository<Block, Long> { -} +public interface BlockRepository extends JpaRepository<Block, Long> {} diff --git a/Domain/src/main/java/allchive/server/domain/domains/block/service/BlockService.java b/Domain/src/main/java/allchive/server/domain/domains/block/service/BlockService.java index 60c08eb1..c2072c33 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/block/service/BlockService.java +++ b/Domain/src/main/java/allchive/server/domain/domains/block/service/BlockService.java @@ -1,7 +1,7 @@ package allchive.server.domain.domains.block.service; + import allchive.server.core.annotation.DomainService; @DomainService -public class BlockService { -} +public class BlockService {} diff --git a/Domain/src/main/java/allchive/server/domain/domains/block/validator/BlockValidator.java b/Domain/src/main/java/allchive/server/domain/domains/block/validator/BlockValidator.java index fa27e0f8..31763f39 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/block/validator/BlockValidator.java +++ b/Domain/src/main/java/allchive/server/domain/domains/block/validator/BlockValidator.java @@ -1,7 +1,7 @@ package allchive.server.domain.domains.block.validator; + import allchive.server.core.annotation.Validator; @Validator -public class BlockValidator { -} +public class BlockValidator {} diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/adaptor/CategoryAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/category/adaptor/CategoryAdaptor.java index 742948da..4f7bdff8 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/category/adaptor/CategoryAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/category/adaptor/CategoryAdaptor.java @@ -1,9 +1,9 @@ package allchive.server.domain.domains.category.adaptor; + import allchive.server.core.annotation.Adaptor; import lombok.RequiredArgsConstructor; @Adaptor @RequiredArgsConstructor -public class CategoryAdaptor { -} +public class CategoryAdaptor {} diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/adaptor/TitleAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/category/adaptor/TitleAdaptor.java index 0f0926eb..9189982d 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/category/adaptor/TitleAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/category/adaptor/TitleAdaptor.java @@ -1,9 +1,9 @@ package allchive.server.domain.domains.category.adaptor; + import allchive.server.core.annotation.Adaptor; import lombok.RequiredArgsConstructor; @Adaptor @RequiredArgsConstructor -public class TitleAdaptor { -} +public class TitleAdaptor {} diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/domain/Category.java b/Domain/src/main/java/allchive/server/domain/domains/category/domain/Category.java index a1f44d91..966a1c66 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/category/domain/Category.java +++ b/Domain/src/main/java/allchive/server/domain/domains/category/domain/Category.java @@ -1,12 +1,12 @@ package allchive.server.domain.domains.category.domain; + import allchive.server.domain.common.model.BaseTimeEntity; +import javax.persistence.*; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; -import javax.persistence.*; - @Getter @Table(name = "tbl_category") @Entity diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/domain/Title.java b/Domain/src/main/java/allchive/server/domain/domains/category/domain/Title.java index 79527d79..6f40fa1e 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/category/domain/Title.java +++ b/Domain/src/main/java/allchive/server/domain/domains/category/domain/Title.java @@ -1,12 +1,12 @@ package allchive.server.domain.domains.category.domain; + import allchive.server.domain.common.model.BaseTimeEntity; +import javax.persistence.*; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; -import javax.persistence.*; - @Getter @Table(name = "tbl_category") @Entity diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/exception/CategoryErrorCode.java b/Domain/src/main/java/allchive/server/domain/domains/category/exception/CategoryErrorCode.java index 895c9ad3..aba7d15e 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/category/exception/CategoryErrorCode.java +++ b/Domain/src/main/java/allchive/server/domain/domains/category/exception/CategoryErrorCode.java @@ -1,5 +1,6 @@ package allchive.server.domain.domains.category.exception; + import allchive.server.core.dto.ErrorReason; import allchive.server.core.error.BaseErrorCode; import lombok.AllArgsConstructor; @@ -17,6 +18,5 @@ public enum CategoryErrorCode implements BaseErrorCode { @Override public ErrorReason getErrorReason() { return ErrorReason.of(status.value(), code, reason); - } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/exception/TitleErrorCode.java b/Domain/src/main/java/allchive/server/domain/domains/category/exception/TitleErrorCode.java index 5f429103..66093932 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/category/exception/TitleErrorCode.java +++ b/Domain/src/main/java/allchive/server/domain/domains/category/exception/TitleErrorCode.java @@ -1,5 +1,6 @@ package allchive.server.domain.domains.category.exception; + import allchive.server.core.dto.ErrorReason; import allchive.server.core.error.BaseErrorCode; import lombok.AllArgsConstructor; @@ -17,6 +18,5 @@ public enum TitleErrorCode implements BaseErrorCode { @Override public ErrorReason getErrorReason() { return ErrorReason.of(status.value(), code, reason); - } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/repository/CategoryRepository.java b/Domain/src/main/java/allchive/server/domain/domains/category/repository/CategoryRepository.java index 14202d60..049f816e 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/category/repository/CategoryRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/category/repository/CategoryRepository.java @@ -1,7 +1,7 @@ package allchive.server.domain.domains.category.repository; + import allchive.server.domain.domains.category.domain.Category; import org.springframework.data.jpa.repository.JpaRepository; -public interface CategoryRepository extends JpaRepository<Category, Long> { -} +public interface CategoryRepository extends JpaRepository<Category, Long> {} diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/repository/TitleRepository.java b/Domain/src/main/java/allchive/server/domain/domains/category/repository/TitleRepository.java index b3b34c58..7d415c45 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/category/repository/TitleRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/category/repository/TitleRepository.java @@ -1,8 +1,7 @@ package allchive.server.domain.domains.category.repository; -import allchive.server.domain.domains.category.domain.Category; + import allchive.server.domain.domains.category.domain.Title; import org.springframework.data.jpa.repository.JpaRepository; -public interface TitleRepository extends JpaRepository<Title, Long> { -} +public interface TitleRepository extends JpaRepository<Title, Long> {} diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/service/CategoryService.java b/Domain/src/main/java/allchive/server/domain/domains/category/service/CategoryService.java index 4da12cb4..82a03836 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/category/service/CategoryService.java +++ b/Domain/src/main/java/allchive/server/domain/domains/category/service/CategoryService.java @@ -1,7 +1,7 @@ package allchive.server.domain.domains.category.service; + import allchive.server.core.annotation.DomainService; @DomainService -public class CategoryService { -} +public class CategoryService {} diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/service/TitleService.java b/Domain/src/main/java/allchive/server/domain/domains/category/service/TitleService.java index 8424ab1f..ee7ef4b3 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/category/service/TitleService.java +++ b/Domain/src/main/java/allchive/server/domain/domains/category/service/TitleService.java @@ -1,7 +1,7 @@ package allchive.server.domain.domains.category.service; + import allchive.server.core.annotation.DomainService; @DomainService -public class TitleService { -} +public class TitleService {} diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/validator/CategoryValidator.java b/Domain/src/main/java/allchive/server/domain/domains/category/validator/CategoryValidator.java index 8ff8a0fb..fe2ab048 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/category/validator/CategoryValidator.java +++ b/Domain/src/main/java/allchive/server/domain/domains/category/validator/CategoryValidator.java @@ -1,7 +1,7 @@ package allchive.server.domain.domains.category.validator; + import allchive.server.core.annotation.Validator; @Validator -public class CategoryValidator { -} +public class CategoryValidator {} diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/validator/TitleValidator.java b/Domain/src/main/java/allchive/server/domain/domains/category/validator/TitleValidator.java index 33c2339e..fd1d6ae7 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/category/validator/TitleValidator.java +++ b/Domain/src/main/java/allchive/server/domain/domains/category/validator/TitleValidator.java @@ -1,7 +1,7 @@ package allchive.server.domain.domains.category.validator; + import allchive.server.core.annotation.Validator; @Validator -public class TitleValidator { -} +public class TitleValidator {} diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentAdaptor.java index 3241c0bd..08e71f81 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentAdaptor.java @@ -1,9 +1,9 @@ package allchive.server.domain.domains.content.adaptor; + import allchive.server.core.annotation.Adaptor; import lombok.RequiredArgsConstructor; @Adaptor @RequiredArgsConstructor -public class ContentAdaptor { -} +public class ContentAdaptor {} diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentTagGroupAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentTagGroupAdaptor.java index 4a8f2f2f..0d51a944 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentTagGroupAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentTagGroupAdaptor.java @@ -1,9 +1,9 @@ package allchive.server.domain.domains.content.adaptor; + import allchive.server.core.annotation.Adaptor; import lombok.RequiredArgsConstructor; @Adaptor @RequiredArgsConstructor -public class ContentTagGroupAdaptor { -} +public class ContentTagGroupAdaptor {} diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/TagAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/TagAdaptor.java index cfe2e2c2..97da461b 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/TagAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/TagAdaptor.java @@ -1,9 +1,9 @@ package allchive.server.domain.domains.content.adaptor; + import allchive.server.core.annotation.Adaptor; import lombok.RequiredArgsConstructor; @Adaptor @RequiredArgsConstructor -public class TagAdaptor { -} +public class TagAdaptor {} diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/domain/Content.java b/Domain/src/main/java/allchive/server/domain/domains/content/domain/Content.java index bebf1fca..bf06c879 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/domain/Content.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/domain/Content.java @@ -1,12 +1,12 @@ package allchive.server.domain.domains.content.domain; + import allchive.server.domain.common.model.BaseTimeEntity; +import javax.persistence.*; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; -import javax.persistence.*; - @Getter @Table(name = "tbl_content") @Entity @@ -15,5 +15,4 @@ public class Content extends BaseTimeEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - } diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/domain/ContentTagGroup.java b/Domain/src/main/java/allchive/server/domain/domains/content/domain/ContentTagGroup.java index e83e08f2..9195bc7d 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/domain/ContentTagGroup.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/domain/ContentTagGroup.java @@ -1,12 +1,12 @@ package allchive.server.domain.domains.content.domain; + import allchive.server.domain.common.model.BaseTimeEntity; +import javax.persistence.*; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; -import javax.persistence.*; - @Getter @Table(name = "tbl_content_tag_group") @Entity @@ -15,5 +15,4 @@ public class ContentTagGroup extends BaseTimeEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - } diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/domain/Tag.java b/Domain/src/main/java/allchive/server/domain/domains/content/domain/Tag.java index 2e065e15..c846f217 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/domain/Tag.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/domain/Tag.java @@ -1,12 +1,12 @@ package allchive.server.domain.domains.content.domain; + import allchive.server.domain.common.model.BaseTimeEntity; +import javax.persistence.*; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; -import javax.persistence.*; - @Getter @Table(name = "tbl_tag") @Entity diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/exception/ContentErrorCode.java b/Domain/src/main/java/allchive/server/domain/domains/content/exception/ContentErrorCode.java index d53e2c02..a068bd46 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/exception/ContentErrorCode.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/exception/ContentErrorCode.java @@ -1,5 +1,6 @@ package allchive.server.domain.domains.content.exception; + import allchive.server.core.dto.ErrorReason; import allchive.server.core.error.BaseErrorCode; import lombok.AllArgsConstructor; @@ -17,6 +18,5 @@ public enum ContentErrorCode implements BaseErrorCode { @Override public ErrorReason getErrorReason() { return ErrorReason.of(status.value(), code, reason); - } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentRepository.java b/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentRepository.java index daca5d17..39473620 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentRepository.java @@ -1,7 +1,7 @@ package allchive.server.domain.domains.content.repository; + import allchive.server.domain.domains.content.domain.Content; import org.springframework.data.jpa.repository.JpaRepository; -public interface ContentRepository extends JpaRepository<Content, Long> { -} +public interface ContentRepository extends JpaRepository<Content, Long> {} diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentTagGroupRepository.java b/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentTagGroupRepository.java index 4d0c53f2..e8705be1 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentTagGroupRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentTagGroupRepository.java @@ -1,7 +1,7 @@ package allchive.server.domain.domains.content.repository; + import allchive.server.domain.domains.content.domain.ContentTagGroup; import org.springframework.data.jpa.repository.JpaRepository; -public interface ContentTagGroupRepository extends JpaRepository<ContentTagGroup, Long> { -} +public interface ContentTagGroupRepository extends JpaRepository<ContentTagGroup, Long> {} diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/repository/TagRepository.java b/Domain/src/main/java/allchive/server/domain/domains/content/repository/TagRepository.java index 6b441a52..cc527e65 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/repository/TagRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/repository/TagRepository.java @@ -1,7 +1,7 @@ package allchive.server.domain.domains.content.repository; + import allchive.server.domain.domains.content.domain.Tag; import org.springframework.data.jpa.repository.JpaRepository; -public interface TagRepository extends JpaRepository<Tag, Long> { -} +public interface TagRepository extends JpaRepository<Tag, Long> {} diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/service/ContentService.java b/Domain/src/main/java/allchive/server/domain/domains/content/service/ContentService.java index 65e635e3..5de26f57 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/service/ContentService.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/service/ContentService.java @@ -1,7 +1,7 @@ package allchive.server.domain.domains.content.service; + import allchive.server.core.annotation.DomainService; @DomainService -public class ContentService { -} +public class ContentService {} diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/service/ContentTagGroupService.java b/Domain/src/main/java/allchive/server/domain/domains/content/service/ContentTagGroupService.java index 3ee1019a..44718ff7 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/service/ContentTagGroupService.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/service/ContentTagGroupService.java @@ -1,7 +1,7 @@ package allchive.server.domain.domains.content.service; + import allchive.server.core.annotation.DomainService; @DomainService -public class ContentTagGroupService { -} +public class ContentTagGroupService {} diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/service/TagService.java b/Domain/src/main/java/allchive/server/domain/domains/content/service/TagService.java index e96613fc..fe798e38 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/service/TagService.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/service/TagService.java @@ -1,7 +1,7 @@ package allchive.server.domain.domains.content.service; + import allchive.server.core.annotation.DomainService; @DomainService -public class TagService { -} +public class TagService {} diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/validator/ContentValidator.java b/Domain/src/main/java/allchive/server/domain/domains/content/validator/ContentValidator.java index 3973a08a..93b05fdb 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/validator/ContentValidator.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/validator/ContentValidator.java @@ -1,7 +1,7 @@ package allchive.server.domain.domains.content.validator; + import allchive.server.core.annotation.Validator; @Validator -public class ContentValidator { -} +public class ContentValidator {} diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/validator/TagValidator.java b/Domain/src/main/java/allchive/server/domain/domains/content/validator/TagValidator.java index 8229e08d..ed2064fe 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/validator/TagValidator.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/validator/TagValidator.java @@ -1,7 +1,7 @@ package allchive.server.domain.domains.content.validator; + import allchive.server.core.annotation.Validator; @Validator -public class TagValidator { -} +public class TagValidator {} diff --git a/Domain/src/main/java/allchive/server/domain/domains/recycle/adaptor/RecycleAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/recycle/adaptor/RecycleAdaptor.java index 964057b3..bbcdc5bb 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/recycle/adaptor/RecycleAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/recycle/adaptor/RecycleAdaptor.java @@ -1,9 +1,9 @@ package allchive.server.domain.domains.recycle.adaptor; + import allchive.server.core.annotation.Adaptor; import lombok.RequiredArgsConstructor; @Adaptor @RequiredArgsConstructor -public class RecycleAdaptor { -} +public class RecycleAdaptor {} diff --git a/Domain/src/main/java/allchive/server/domain/domains/recycle/domain/Recycle.java b/Domain/src/main/java/allchive/server/domain/domains/recycle/domain/Recycle.java index 62e550e3..58b3572d 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/recycle/domain/Recycle.java +++ b/Domain/src/main/java/allchive/server/domain/domains/recycle/domain/Recycle.java @@ -1,12 +1,12 @@ package allchive.server.domain.domains.recycle.domain; + import allchive.server.domain.common.model.BaseTimeEntity; +import javax.persistence.*; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; -import javax.persistence.*; - @Getter @Table(name = "tbl_recycle") @Entity diff --git a/Domain/src/main/java/allchive/server/domain/domains/recycle/exception/RecycleErrorCode.java b/Domain/src/main/java/allchive/server/domain/domains/recycle/exception/RecycleErrorCode.java index 6133d756..d33f7621 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/recycle/exception/RecycleErrorCode.java +++ b/Domain/src/main/java/allchive/server/domain/domains/recycle/exception/RecycleErrorCode.java @@ -1,5 +1,6 @@ package allchive.server.domain.domains.recycle.exception; + import allchive.server.core.dto.ErrorReason; import allchive.server.core.error.BaseErrorCode; import lombok.AllArgsConstructor; @@ -17,6 +18,5 @@ public enum RecycleErrorCode implements BaseErrorCode { @Override public ErrorReason getErrorReason() { return ErrorReason.of(status.value(), code, reason); - } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/recycle/repository/RecycleRepository.java b/Domain/src/main/java/allchive/server/domain/domains/recycle/repository/RecycleRepository.java index e7668a1d..d3768da1 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/recycle/repository/RecycleRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/recycle/repository/RecycleRepository.java @@ -1,7 +1,7 @@ package allchive.server.domain.domains.recycle.repository; + import allchive.server.domain.domains.recycle.domain.Recycle; import org.springframework.data.jpa.repository.JpaRepository; -public interface RecycleRepository extends JpaRepository<Recycle, Long> { -} +public interface RecycleRepository extends JpaRepository<Recycle, Long> {} diff --git a/Domain/src/main/java/allchive/server/domain/domains/recycle/service/RecycleService.java b/Domain/src/main/java/allchive/server/domain/domains/recycle/service/RecycleService.java index f0523666..bc2e38f4 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/recycle/service/RecycleService.java +++ b/Domain/src/main/java/allchive/server/domain/domains/recycle/service/RecycleService.java @@ -1,7 +1,7 @@ package allchive.server.domain.domains.recycle.service; + import allchive.server.core.annotation.DomainService; @DomainService -public class RecycleService { -} +public class RecycleService {} diff --git a/Domain/src/main/java/allchive/server/domain/domains/recycle/validator/RecycleValidator.java b/Domain/src/main/java/allchive/server/domain/domains/recycle/validator/RecycleValidator.java index 651326d2..94824465 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/recycle/validator/RecycleValidator.java +++ b/Domain/src/main/java/allchive/server/domain/domains/recycle/validator/RecycleValidator.java @@ -1,7 +1,7 @@ package allchive.server.domain.domains.recycle.validator; + import allchive.server.core.annotation.Validator; @Validator -public class RecycleValidator { -} +public class RecycleValidator {} diff --git a/Domain/src/main/java/allchive/server/domain/domains/report/adaptor/ReportAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/report/adaptor/ReportAdaptor.java index 41870a1f..14ad8dca 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/report/adaptor/ReportAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/report/adaptor/ReportAdaptor.java @@ -1,9 +1,9 @@ package allchive.server.domain.domains.report.adaptor; + import allchive.server.core.annotation.Adaptor; import lombok.RequiredArgsConstructor; @Adaptor @RequiredArgsConstructor -public class ReportAdaptor { -} +public class ReportAdaptor {} diff --git a/Domain/src/main/java/allchive/server/domain/domains/report/domain/Report.java b/Domain/src/main/java/allchive/server/domain/domains/report/domain/Report.java index 6169aac5..f86a816d 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/report/domain/Report.java +++ b/Domain/src/main/java/allchive/server/domain/domains/report/domain/Report.java @@ -1,12 +1,12 @@ package allchive.server.domain.domains.report.domain; + import allchive.server.domain.common.model.BaseTimeEntity; +import javax.persistence.*; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; -import javax.persistence.*; - @Getter @Table(name = "tbl_report") @Entity @@ -15,5 +15,4 @@ public class Report extends BaseTimeEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - } diff --git a/Domain/src/main/java/allchive/server/domain/domains/report/exception/ReportErrorCode.java b/Domain/src/main/java/allchive/server/domain/domains/report/exception/ReportErrorCode.java index 87f764b5..711696c3 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/report/exception/ReportErrorCode.java +++ b/Domain/src/main/java/allchive/server/domain/domains/report/exception/ReportErrorCode.java @@ -1,5 +1,6 @@ package allchive.server.domain.domains.report.exception; + import allchive.server.core.dto.ErrorReason; import allchive.server.core.error.BaseErrorCode; import lombok.AllArgsConstructor; @@ -17,6 +18,5 @@ public enum ReportErrorCode implements BaseErrorCode { @Override public ErrorReason getErrorReason() { return ErrorReason.of(status.value(), code, reason); - } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/report/repository/ReportRepository.java b/Domain/src/main/java/allchive/server/domain/domains/report/repository/ReportRepository.java index 6e3e7959..7b6f4638 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/report/repository/ReportRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/report/repository/ReportRepository.java @@ -1,7 +1,7 @@ package allchive.server.domain.domains.report.repository; + import allchive.server.domain.domains.report.domain.Report; import org.springframework.data.jpa.repository.JpaRepository; -public interface ReportRepository extends JpaRepository<Report, Long> { -} +public interface ReportRepository extends JpaRepository<Report, Long> {} diff --git a/Domain/src/main/java/allchive/server/domain/domains/report/service/ReportService.java b/Domain/src/main/java/allchive/server/domain/domains/report/service/ReportService.java index 7f1ca556..ed7afafd 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/report/service/ReportService.java +++ b/Domain/src/main/java/allchive/server/domain/domains/report/service/ReportService.java @@ -1,7 +1,7 @@ package allchive.server.domain.domains.report.service; + import allchive.server.core.annotation.DomainService; @DomainService -public class ReportService { -} +public class ReportService {} diff --git a/Domain/src/main/java/allchive/server/domain/domains/report/validator/ReportValidator.java b/Domain/src/main/java/allchive/server/domain/domains/report/validator/ReportValidator.java index 03068124..1f82f50b 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/report/validator/ReportValidator.java +++ b/Domain/src/main/java/allchive/server/domain/domains/report/validator/ReportValidator.java @@ -1,7 +1,7 @@ package allchive.server.domain.domains.report.validator; + import allchive.server.core.annotation.Validator; @Validator -public class ReportValidator { -} +public class ReportValidator {} diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/ScrapAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/ScrapAdaptor.java index 1b77131f..6bf5674c 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/ScrapAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/ScrapAdaptor.java @@ -1,9 +1,9 @@ package allchive.server.domain.domains.user.adaptor; + import allchive.server.core.annotation.Adaptor; import lombok.RequiredArgsConstructor; @Adaptor @RequiredArgsConstructor -public class ScrapAdaptor { -} +public class ScrapAdaptor {} diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/UserAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/UserAdaptor.java index ecac869d..b137276b 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/UserAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/UserAdaptor.java @@ -1,9 +1,9 @@ package allchive.server.domain.domains.user.adaptor; + import allchive.server.core.annotation.Adaptor; import lombok.RequiredArgsConstructor; @Adaptor @RequiredArgsConstructor -public class UserAdaptor { -} +public class UserAdaptor {} diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/UserTopicAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/UserTopicAdaptor.java index 0283f232..48aee243 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/UserTopicAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/UserTopicAdaptor.java @@ -1,9 +1,9 @@ package allchive.server.domain.domains.user.adaptor; + import allchive.server.core.annotation.Adaptor; import lombok.RequiredArgsConstructor; @Adaptor @RequiredArgsConstructor -public class UserTopicAdaptor { -} +public class UserTopicAdaptor {} diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/domain/Scrap.java b/Domain/src/main/java/allchive/server/domain/domains/user/domain/Scrap.java index 4d94c75a..6aaa0d83 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/domain/Scrap.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/domain/Scrap.java @@ -1,12 +1,12 @@ package allchive.server.domain.domains.user.domain; + import allchive.server.domain.common.model.BaseTimeEntity; +import javax.persistence.*; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; -import javax.persistence.*; - @Getter @Table(name = "tbl_scrap") @Entity diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/domain/User.java b/Domain/src/main/java/allchive/server/domain/domains/user/domain/User.java index 367dfa85..ec9c2696 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/domain/User.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/domain/User.java @@ -1,16 +1,16 @@ package allchive.server.domain.domains.user.domain; + import allchive.server.domain.common.model.BaseTimeEntity; import allchive.server.domain.domains.user.domain.enums.OauthInfo; import allchive.server.domain.domains.user.domain.enums.UserState; +import java.time.LocalDateTime; +import javax.persistence.*; +import javax.validation.constraints.NotNull; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; -import javax.persistence.*; -import javax.validation.constraints.NotNull; -import java.time.LocalDateTime; - @Getter @Table(name = "tbl_user") @Entity @@ -20,15 +20,12 @@ public class User extends BaseTimeEntity { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - @NotNull - private String nickname; + @NotNull private String nickname; private String profileImgUrl; - @Embedded - private OauthInfo oauthInfo; + @Embedded private OauthInfo oauthInfo; - @NotNull - private LocalDateTime lastLoginAt; + @NotNull private LocalDateTime lastLoginAt; private String Email; diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/domain/UserTopicGroup.java b/Domain/src/main/java/allchive/server/domain/domains/user/domain/UserTopicGroup.java index 4fb6c9ee..34dfff5a 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/domain/UserTopicGroup.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/domain/UserTopicGroup.java @@ -1,12 +1,12 @@ package allchive.server.domain.domains.user.domain; + import allchive.server.domain.common.model.BaseTimeEntity; +import javax.persistence.*; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; -import javax.persistence.*; - @Getter @Table(name = "tbl_user_topic_group") @Entity @@ -15,5 +15,4 @@ public class UserTopicGroup extends BaseTimeEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - } diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/domain/enums/FormInfo.java b/Domain/src/main/java/allchive/server/domain/domains/user/domain/enums/FormInfo.java index e3d642c9..b806f0bf 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/domain/enums/FormInfo.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/domain/enums/FormInfo.java @@ -1,11 +1,11 @@ package allchive.server.domain.domains.user.domain.enums; + +import javax.persistence.Embeddable; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; -import javax.persistence.Embeddable; - @Embeddable @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/domain/enums/OauthInfo.java b/Domain/src/main/java/allchive/server/domain/domains/user/domain/enums/OauthInfo.java index 7c2adc45..0cf9ed0b 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/domain/enums/OauthInfo.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/domain/enums/OauthInfo.java @@ -1,15 +1,14 @@ package allchive.server.domain.domains.user.domain.enums; +import javax.persistence.Embeddable; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; import lombok.AccessLevel; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; -import javax.persistence.Embeddable; -import javax.persistence.EnumType; -import javax.persistence.Enumerated; - @Getter @Embeddable @NoArgsConstructor(access = AccessLevel.PROTECTED) @@ -27,9 +26,6 @@ public OauthInfo(OauthProvider provider, String oid) { } public OauthInfo withDrawOauthInfo() { - return OauthInfo.builder() - .oid(oid) - .provider(provider) - .build(); + return OauthInfo.builder().oid(oid).provider(provider).build(); } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/exception/ScrapErrorCode.java b/Domain/src/main/java/allchive/server/domain/domains/user/exception/ScrapErrorCode.java index 35e3819c..2a81084e 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/exception/ScrapErrorCode.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/exception/ScrapErrorCode.java @@ -1,5 +1,6 @@ package allchive.server.domain.domains.user.exception; + import allchive.server.core.dto.ErrorReason; import allchive.server.core.error.BaseErrorCode; import lombok.AllArgsConstructor; @@ -17,6 +18,5 @@ public enum ScrapErrorCode implements BaseErrorCode { @Override public ErrorReason getErrorReason() { return ErrorReason.of(status.value(), code, reason); - } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/exception/UserErrorCode.java b/Domain/src/main/java/allchive/server/domain/domains/user/exception/UserErrorCode.java index 414a7fca..f62c1091 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/exception/UserErrorCode.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/exception/UserErrorCode.java @@ -1,5 +1,6 @@ package allchive.server.domain.domains.user.exception; + import allchive.server.core.dto.ErrorReason; import allchive.server.core.error.BaseErrorCode; import lombok.AllArgsConstructor; @@ -17,6 +18,5 @@ public enum UserErrorCode implements BaseErrorCode { @Override public ErrorReason getErrorReason() { return ErrorReason.of(status.value(), code, reason); - } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/repository/ScrapRepository.java b/Domain/src/main/java/allchive/server/domain/domains/user/repository/ScrapRepository.java index d583c1d3..29c7012d 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/repository/ScrapRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/repository/ScrapRepository.java @@ -1,7 +1,7 @@ package allchive.server.domain.domains.user.repository; + import allchive.server.domain.domains.user.domain.Scrap; import org.springframework.data.jpa.repository.JpaRepository; -public interface ScrapRepository extends JpaRepository<Scrap, Long> { -} +public interface ScrapRepository extends JpaRepository<Scrap, Long> {} diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/repository/UserRepository.java b/Domain/src/main/java/allchive/server/domain/domains/user/repository/UserRepository.java index e97a9a89..97eed6e2 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/repository/UserRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/repository/UserRepository.java @@ -1,7 +1,7 @@ package allchive.server.domain.domains.user.repository; + import allchive.server.domain.domains.user.domain.User; import org.springframework.data.jpa.repository.JpaRepository; -public interface UserRepository extends JpaRepository<User, Long> { -} +public interface UserRepository extends JpaRepository<User, Long> {} diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/repository/UserTopicGroupRepository.java b/Domain/src/main/java/allchive/server/domain/domains/user/repository/UserTopicGroupRepository.java index 25866135..afd0f201 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/repository/UserTopicGroupRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/repository/UserTopicGroupRepository.java @@ -1,7 +1,7 @@ package allchive.server.domain.domains.user.repository; + import allchive.server.domain.domains.user.domain.UserTopicGroup; import org.springframework.data.jpa.repository.JpaRepository; -public interface UserTopicGroupRepository extends JpaRepository<UserTopicGroup, Long> { -} +public interface UserTopicGroupRepository extends JpaRepository<UserTopicGroup, Long> {} diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/service/ScrapService.java b/Domain/src/main/java/allchive/server/domain/domains/user/service/ScrapService.java index ee6b3ede..de52b5e4 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/service/ScrapService.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/service/ScrapService.java @@ -1,7 +1,7 @@ package allchive.server.domain.domains.user.service; + import allchive.server.core.annotation.DomainService; @DomainService -public class ScrapService { -} +public class ScrapService {} diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/service/UserService.java b/Domain/src/main/java/allchive/server/domain/domains/user/service/UserService.java index 06b494cb..6f2ed7ca 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/service/UserService.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/service/UserService.java @@ -1,7 +1,7 @@ package allchive.server.domain.domains.user.service; + import allchive.server.core.annotation.DomainService; @DomainService -public class UserService { -} +public class UserService {} diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/service/UserTopicGroupService.java b/Domain/src/main/java/allchive/server/domain/domains/user/service/UserTopicGroupService.java index e734444b..0311422d 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/service/UserTopicGroupService.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/service/UserTopicGroupService.java @@ -1,7 +1,7 @@ package allchive.server.domain.domains.user.service; + import allchive.server.core.annotation.DomainService; @DomainService -public class UserTopicGroupService { -} +public class UserTopicGroupService {} diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/validator/ScrapValidator.java b/Domain/src/main/java/allchive/server/domain/domains/user/validator/ScrapValidator.java index 65ef3653..3ee54099 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/validator/ScrapValidator.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/validator/ScrapValidator.java @@ -1,7 +1,7 @@ package allchive.server.domain.domains.user.validator; + import allchive.server.core.annotation.Validator; @Validator -public class ScrapValidator { -} +public class ScrapValidator {} diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/validator/UserValidator.java b/Domain/src/main/java/allchive/server/domain/domains/user/validator/UserValidator.java index 1534bcaf..6382bacf 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/validator/UserValidator.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/validator/UserValidator.java @@ -1,7 +1,7 @@ package allchive.server.domain.domains.user.validator; + import allchive.server.core.annotation.Validator; @Validator -public class UserValidator { -} +public class UserValidator {} diff --git a/Infrastructure/src/main/java/allchive/server/infrastructure/config/redis/RedisCacheConfig.java b/Infrastructure/src/main/java/allchive/server/infrastructure/config/redis/RedisCacheConfig.java index 3df01b09..c4ac9bc5 100644 --- a/Infrastructure/src/main/java/allchive/server/infrastructure/config/redis/RedisCacheConfig.java +++ b/Infrastructure/src/main/java/allchive/server/infrastructure/config/redis/RedisCacheConfig.java @@ -1,6 +1,7 @@ package allchive.server.infrastructure.config.redis; +import java.time.Duration; import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.Bean; @@ -13,8 +14,6 @@ import org.springframework.data.redis.serializer.RedisSerializationContext; import org.springframework.data.redis.serializer.StringRedisSerializer; -import java.time.Duration; - @EnableCaching @Configuration public class RedisCacheConfig { diff --git a/Infrastructure/src/main/java/allchive/server/infrastructure/config/redis/RedisConfig.java b/Infrastructure/src/main/java/allchive/server/infrastructure/config/redis/RedisConfig.java index 72022ae6..90fdb726 100644 --- a/Infrastructure/src/main/java/allchive/server/infrastructure/config/redis/RedisConfig.java +++ b/Infrastructure/src/main/java/allchive/server/infrastructure/config/redis/RedisConfig.java @@ -1,6 +1,7 @@ package allchive.server.infrastructure.config.redis; +import java.time.Duration; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -11,8 +12,6 @@ import org.springframework.data.redis.core.RedisKeyValueAdapter; import org.springframework.data.redis.repository.configuration.EnableRedisRepositories; -import java.time.Duration; - @EnableRedisRepositories( basePackages = "allchive.server", enableKeyspaceEvents = RedisKeyValueAdapter.EnableKeyspaceEvents.ON_STARTUP) diff --git a/Infrastructure/src/main/java/allchive/server/infrastructure/config/redis/RedissonConfig.java b/Infrastructure/src/main/java/allchive/server/infrastructure/config/redis/RedissonConfig.java index e6cd4aed..f59f5857 100644 --- a/Infrastructure/src/main/java/allchive/server/infrastructure/config/redis/RedissonConfig.java +++ b/Infrastructure/src/main/java/allchive/server/infrastructure/config/redis/RedissonConfig.java @@ -4,15 +4,10 @@ import org.redisson.Redisson; import org.redisson.api.RedissonClient; import org.redisson.config.Config; -import org.redisson.jcache.configuration.RedissonConfiguration; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import javax.cache.Cache; -import javax.cache.CacheManager; -import javax.cache.Caching; - @Configuration public class RedissonConfig { @Value("${spring.redis.host}") diff --git a/config/nginx/Dockerfile b/config/nginx/Dockerfile new file mode 100644 index 00000000..32fc4fb9 --- /dev/null +++ b/config/nginx/Dockerfile @@ -0,0 +1,2 @@ +FROM nginx:1.21.4 +COPY ./default.conf /etc/nginx/conf.d/default.conf \ No newline at end of file diff --git a/config/nginx/default.conf b/config/nginx/default.conf new file mode 100644 index 00000000..e0eb6c00 --- /dev/null +++ b/config/nginx/default.conf @@ -0,0 +1,18 @@ +server { + listen 80; + server_name allchive.co.kr; + + autoindex_localtime on; + + if ($http_x_forwarded_proto != 'https') { + return 301 https://$host$request_uri; + } + + location /api { + proxy_pass http://server:8080; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Host $host; + proxy_set_header X-Real_IP $remote_addr; + proxy_redirect off; + } +} \ No newline at end of file diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml new file mode 100644 index 00000000..c26bada3 --- /dev/null +++ b/docker-compose.dev.yml @@ -0,0 +1,23 @@ +version: '3.7' + +services: + server: + image: sanghoonjeong/allchive-dev:latest + container_name: server + hostname: server + env_file: + - .env + environment: + - TZ=Asia/Seoul + expose: + - 8080 + + nginx: + depends_on: + - server + restart: always + build: + dockerfile: Dockerfile + context: './nginx' + ports: + - "80:80" \ No newline at end of file From 4a500775f8a4a85a586815cab1f2ed82976942c6 Mon Sep 17 00:00:00 2001 From: wjdtkdgns <wjdtkdgns10266@gmail.com> Date: Thu, 29 Jun 2023 15:08:55 +0900 Subject: [PATCH 08/41] =?UTF-8?q?[fix]=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/CI.yml | 2 -- .github/workflows/DevCICD.yml | 12 ++++++------ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index d1c962a8..f9228549 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -1,8 +1,6 @@ name: CI on: - push: - branches: [ "dev", "main" ] pull_request: branch: 'dev' types: [ opened, synchronize, reopened ] diff --git a/.github/workflows/DevCICD.yml b/.github/workflows/DevCICD.yml index f460898f..10e1ffde 100644 --- a/.github/workflows/DevCICD.yml +++ b/.github/workflows/DevCICD.yml @@ -28,7 +28,7 @@ jobs: run: chmod +x ./gradlew - name: Build with Gradle - run: ./gradlew build + run: ./gradlew :Api:build - name: Docker build run: | @@ -43,13 +43,13 @@ jobs: - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v1 with: - aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} - aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-access-key-id: ${{ secrets.AWS_DEV_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_DEV_SECRET_ACCESS_KEY }} aws-region: ${{ secrets.AWS_REGION }} - name: Add GitHub IP to AWS run: | - aws ec2 authorize-security-group-ingress --group-id ${{ secrets.AWS_SG_ID }} --protocol tcp --port 22 --cidr ${{ steps.ip.outputs.ipv4 }}/32 + aws ec2 authorize-security-group-ingress --group-id ${{ secrets.AWS_DEV_SG_ID }} --protocol tcp --port 22 --cidr ${{ steps.ip.outputs.ipv4 }}/32 - name: Deploy uses: appleboy/ssh-action@master @@ -63,12 +63,12 @@ jobs: cd allchive-dev sudo touch .env - echo "${{ secrets.ENV_VARS }}" | sudo tee .env > /dev/null + echo "${{ secrets.ENV_DEV_VARS }}" | sudo tee .env > /dev/null sudo docker stop $(sudo docker ps -a -q) sudo docker rm $(sudo docker ps -a -q) sudo docker rmi $(sudo docker images -q) - sudo docker pull ${{ secrets.DOCKER_USERNAME }}/${{ secrets.DOCKER_REPO }}:latest + sudo docker pull ${{ secrets.DOCKER_USERNAME }}/${{ secrets.DOCKER_DEV_REPO }}:latest sudo docker-compose -f ~/allchive-dev/docker-compose.yml --env-file ~/allchive-dev/.env up --build -d sudo docker system prune --all -f From cc8df68c4167e28c5709cefbaa9338fd199b7477 Mon Sep 17 00:00:00 2001 From: wjdtkdgns <wjdtkdgns10266@gmail.com> Date: Thu, 29 Jun 2023 15:15:36 +0900 Subject: [PATCH 09/41] =?UTF-8?q?[feat]=20Api=20module=20Dockerfile=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 Dockerfile diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..0d49ad7d --- /dev/null +++ b/Dockerfile @@ -0,0 +1,9 @@ +FROM openjdk:17-alpine + +ARG JAR_FILE=Api/build/libs/Api.jar +COPY ${JAR_FILE} app.jar + +ARG PROFILE=dev +ENV PROFILE=${PROFILE} + +ENTRYPOINT ["java","-Dspring.profiles.active=${PROFILE}", "-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"] From 07b9da35bb54f06d88459aa87d6e853a16d3768e Mon Sep 17 00:00:00 2001 From: wjdtkdgns <wjdtkdgns10266@gmail.com> Date: Thu, 29 Jun 2023 15:32:42 +0900 Subject: [PATCH 10/41] =?UTF-8?q?[fix]=20action=20error=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/DevCICD.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/DevCICD.yml b/.github/workflows/DevCICD.yml index 10e1ffde..89bae6d8 100644 --- a/.github/workflows/DevCICD.yml +++ b/.github/workflows/DevCICD.yml @@ -36,9 +36,11 @@ jobs: docker build -t ${{ secrets.DOCKER_USERNAME }}/${{ secrets.DOCKER_DEV_REPO }} . docker push ${{ secrets.DOCKER_USERNAME }}/${{ secrets.DOCKER_DEV_REPO }}:latest - - name: get GitHub IP + - name: Get Public IP id: ip - uses: haythem/public-ip@v1.2 + run: | + response=$(curl -s canhazip.com) + echo "ip='$response'" >> "$GITHUB_OUTPUT" - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v1 From 4ffc14820e16a3916cfefa6a5eee58e08e39a09b Mon Sep 17 00:00:00 2001 From: wjdtkdgns <wjdtkdgns10266@gmail.com> Date: Thu, 29 Jun 2023 15:35:43 +0900 Subject: [PATCH 11/41] =?UTF-8?q?[fix]=20action=20github=20ip=20=EB=B0=9B?= =?UTF-8?q?=EB=8A=94=20=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/DevCICD.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/DevCICD.yml b/.github/workflows/DevCICD.yml index 89bae6d8..ac26c02f 100644 --- a/.github/workflows/DevCICD.yml +++ b/.github/workflows/DevCICD.yml @@ -37,7 +37,7 @@ jobs: docker push ${{ secrets.DOCKER_USERNAME }}/${{ secrets.DOCKER_DEV_REPO }}:latest - name: Get Public IP - id: ip + id: publicip run: | response=$(curl -s canhazip.com) echo "ip='$response'" >> "$GITHUB_OUTPUT" @@ -51,7 +51,7 @@ jobs: - name: Add GitHub IP to AWS run: | - aws ec2 authorize-security-group-ingress --group-id ${{ secrets.AWS_DEV_SG_ID }} --protocol tcp --port 22 --cidr ${{ steps.ip.outputs.ipv4 }}/32 + aws ec2 authorize-security-group-ingress --group-id ${{ secrets.AWS_DEV_SG_ID }} --protocol tcp --port 22 --cidr ${{ steps.publicip.outputs.ip }}/32 - name: Deploy uses: appleboy/ssh-action@master From 9be21ca9bd30ab773425cfc6d821f8cdb19af2bf Mon Sep 17 00:00:00 2001 From: wjdtkdgns <wjdtkdgns10266@gmail.com> Date: Thu, 29 Jun 2023 16:21:18 +0900 Subject: [PATCH 12/41] =?UTF-8?q?[fix]=20ec2=20key=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/DevCICD.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/DevCICD.yml b/.github/workflows/DevCICD.yml index ac26c02f..3b6efa37 100644 --- a/.github/workflows/DevCICD.yml +++ b/.github/workflows/DevCICD.yml @@ -58,7 +58,7 @@ jobs: with: host: ${{ secrets.EC2_DEV_HOST }} username: ${{ secrets.EC2_DEV_USERNAME }} - password: ${{ secrets.EC2_DEV_PASSWORD }} + key: ${{ secrets.EC2_DEV_KEY }} port: ${{ secrets.EC2_SSH_PORT }} timeout: 60s script: | From 1208fec48b87c2b4db4dd516d26e54ecbb142f34 Mon Sep 17 00:00:00 2001 From: wjdtkdgns <wjdtkdgns10266@gmail.com> Date: Thu, 29 Jun 2023 16:25:26 +0900 Subject: [PATCH 13/41] =?UTF-8?q?[fix]=20action=20ip=20remove=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/DevCICD.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/DevCICD.yml b/.github/workflows/DevCICD.yml index 3b6efa37..d70feba2 100644 --- a/.github/workflows/DevCICD.yml +++ b/.github/workflows/DevCICD.yml @@ -76,4 +76,4 @@ jobs: - name: Remove IP FROM security group run: | - aws ec2 revoke-security-group-ingress --group-id ${{ secrets.AWS_SG_ID }} --protocol tcp --port 22 --cidr ${{ steps.ip.outputs.ipv4 }}/32 + aws ec2 revoke-security-group-ingress --group-id ${{ secrets.AWS_SG_ID }} --protocol tcp --port 22 --cidr ${{ steps.publicip.outputs.ip }}/32 From 7128fd72d0a8a5259cf6e3bb7137fb203eab1d1c Mon Sep 17 00:00:00 2001 From: wjdtkdgns <wjdtkdgns10266@gmail.com> Date: Thu, 29 Jun 2023 16:27:48 +0900 Subject: [PATCH 14/41] =?UTF-8?q?[fix]=20action=20ip=20remove=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/DevCICD.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/DevCICD.yml b/.github/workflows/DevCICD.yml index d70feba2..601ba8c6 100644 --- a/.github/workflows/DevCICD.yml +++ b/.github/workflows/DevCICD.yml @@ -76,4 +76,4 @@ jobs: - name: Remove IP FROM security group run: | - aws ec2 revoke-security-group-ingress --group-id ${{ secrets.AWS_SG_ID }} --protocol tcp --port 22 --cidr ${{ steps.publicip.outputs.ip }}/32 + aws ec2 revoke-security-group-ingress --group-id ${{ secrets.AWS_DEV_SG_ID }} --protocol tcp --port 22 --cidr ${{ steps.publicip.outputs.ip }}/32 From 24be0a224c1fa36e35caeddadbe97b7d3079db0e Mon Sep 17 00:00:00 2001 From: wjdtkdgns <wjdtkdgns10266@gmail.com> Date: Thu, 29 Jun 2023 16:49:57 +0900 Subject: [PATCH 15/41] =?UTF-8?q?[fix]=20redis=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../server/infrastructure/config/redis/RedisConfig.java | 4 ++-- config/nginx/default.conf | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Infrastructure/src/main/java/allchive/server/infrastructure/config/redis/RedisConfig.java b/Infrastructure/src/main/java/allchive/server/infrastructure/config/redis/RedisConfig.java index 90fdb726..78e9f1e7 100644 --- a/Infrastructure/src/main/java/allchive/server/infrastructure/config/redis/RedisConfig.java +++ b/Infrastructure/src/main/java/allchive/server/infrastructure/config/redis/RedisConfig.java @@ -31,8 +31,8 @@ public class RedisConfig { public RedisConnectionFactory redisConnectionFactory() { RedisStandaloneConfiguration redisConfig = new RedisStandaloneConfiguration(host, port); - if (redisPassword != null && !redisPassword.isBlank()) - redisConfig.setPassword(redisPassword); +// if (redisPassword != null && !redisPassword.isBlank()) +// redisConfig.setPassword(redisPassword); LettuceClientConfiguration clientConfig = LettuceClientConfiguration.builder() diff --git a/config/nginx/default.conf b/config/nginx/default.conf index e0eb6c00..e3b213a3 100644 --- a/config/nginx/default.conf +++ b/config/nginx/default.conf @@ -1,6 +1,6 @@ server { listen 80; - server_name allchive.co.kr; + server_name staging.allchive.co.kr; autoindex_localtime on; From fb1e351a939af69ac45cb6c6057472734256fd36 Mon Sep 17 00:00:00 2001 From: wjdtkdgns <wjdtkdgns10266@gmail.com> Date: Thu, 29 Jun 2023 20:34:55 +0900 Subject: [PATCH 16/41] =?UTF-8?q?[fix]=20redis=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../server/infrastructure/config/redis/RedisConfig.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Infrastructure/src/main/java/allchive/server/infrastructure/config/redis/RedisConfig.java b/Infrastructure/src/main/java/allchive/server/infrastructure/config/redis/RedisConfig.java index 78e9f1e7..02baaecb 100644 --- a/Infrastructure/src/main/java/allchive/server/infrastructure/config/redis/RedisConfig.java +++ b/Infrastructure/src/main/java/allchive/server/infrastructure/config/redis/RedisConfig.java @@ -14,7 +14,8 @@ @EnableRedisRepositories( basePackages = "allchive.server", - enableKeyspaceEvents = RedisKeyValueAdapter.EnableKeyspaceEvents.ON_STARTUP) + enableKeyspaceEvents = RedisKeyValueAdapter.EnableKeyspaceEvents.ON_STARTUP, + keyspaceNotificationsConfigParameter = "") @Configuration public class RedisConfig { From 4f5abcd467f1f861b55cb00437aa8c821487b25c Mon Sep 17 00:00:00 2001 From: Sanghoon Jeong <67852689+wjdtkdgns@users.noreply.github.com> Date: Sat, 1 Jul 2023 00:28:31 +0900 Subject: [PATCH 17/41] =?UTF-8?q?[feat]=20OIDC=20=EC=85=8B=ED=8C=85,=20kak?= =?UTF-8?q?ao=20oauth=20=EA=B5=AC=ED=98=84=20(#11)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [feat] jwt, security filter 셋팅 #4 * [feat] feign 설정, link api 구현 #4 * [feat] kakao login 구현 #4 * [fix] 개발용 api swagger deprecate 처리 #4 * [feat] kakao register 구현 #4 --- .../api/auth/controller/OauthController.java | 81 ++++++++++++ .../api/auth/controller/authController.java | 15 +++ .../api/auth/model/dto/KakaoUserInfoDto.java | 26 ++++ .../model/dto/request/RegisterRequest.java | 19 +++ .../dto/response/OauthLoginLinkResponse.java | 11 ++ .../dto/response/OauthRegisterResponse.java | 28 ++++ .../dto/response/OauthSignInResponse.java | 48 +++++++ .../dto/response/OauthTokenResponse.java | 28 ++++ .../dto/response/OauthUserInfoResponse.java | 32 +++++ .../server/api/auth/model/vo/TokenVo.java | 38 ++++++ .../api/auth/service/OauthLinkUseCase.java | 33 +++++ .../api/auth/service/OauthLoginUseCase.java | 72 ++++++++++ .../auth/service/OauthRegisterUseCase.java | 42 ++++++ .../auth/service/helper/KakaoOauthHelper.java | 76 +++++++++++ .../auth/service/helper/OauthOIDCHelper.java | 33 +++++ .../service/helper/TokenGenerateHelper.java | 44 +++++++ .../api/config/GlobalExceptionHandler.java | 10 ++ .../server/api/config/SwaggerConfig.java | 6 +- .../config/security/AccessDeniedFilter.java | 59 +++++++++ .../api/config/security/AuthDetails.java | 54 ++++++++ .../api/config/security/CorsConfig.java | 13 +- .../api/config/security/FilterConfig.java | 26 ++++ .../config/security/JwtExceptionFilter.java | 44 +++++++ .../api/config/security/JwtTokenFilter.java | 59 +++++++++ .../api/config/security/SecurityConfig.java | 51 ++++++-- .../server/api/example/ExampleController.java | 6 + Core/build.gradle | 5 +- .../CoreConfigurationPropertiesConfig.java | 11 ++ .../server/core/consts/AllchiveConst.java | 9 ++ .../server/core/dto/OIDCDecodePayload.java | 18 +++ .../core/error/BaseDynamicException.java | 15 +++ .../server/core/error/GlobalErrorCode.java | 23 +++- .../server/core/error/exception/.gitkeep | 0 .../error/exception/ExampleException.java | 14 ++ .../ExpiredRefreshTokenException.java | 14 ++ .../exception/ExpiredTokenException.java | 14 ++ .../error/exception/InternalServerError.java | 14 ++ .../InvalidOauthProviderException.java | 14 ++ .../exception/InvalidTokenException.java | 14 ++ .../OtherServerBadRequestException.java | 14 ++ .../OtherServerExpiredTokenException.java | 14 ++ .../OtherServerForbiddenException.java | 14 ++ .../OtherServerNotFoundException.java | 14 ++ .../OtherServerUnauthorizedException.java | 14 ++ .../core/helper/SpringEnvironmentHelper.java | 5 +- .../server/core/jwt/JwtOIDCProvider.java | 83 ++++++++++++ .../server/core/jwt/JwtProperties.java | 17 +++ .../server/core/jwt/JwtTokenProvider.java | 123 ++++++++++++++++++ .../server/core/jwt/dto/AccessTokenInfo.java | 14 ++ .../core/properties/KakaoOAuthProperties.java | 20 +++ Core/src/main/resources/application-core.yml | 16 +++ .../server/domain/DomainPackageLocation.java | 3 + .../server/domain/config/JpaConfig.java | 14 ++ .../user/adaptor/RefreshTokenAdaptor.java | 17 +++ .../domains/user/adaptor/UserAdaptor.java | 22 +++- .../user/domain/RefreshTokenEntity.java | 29 +++++ .../domain/domains/user/domain/User.java | 30 +++++ .../user/domain/enums/OauthProvider.java | 13 +- .../domains/user/domain/enums/UserRole.java | 14 ++ .../domains/user/exception/UserErrorCode.java | 6 +- .../AlreadySignUpUserException.java | 14 ++ .../exceptions/ForbiddenUserException.java | 14 ++ .../exceptions/UserNotFoundException.java | 14 ++ .../repository/RefreshTokenRepository.java | 10 ++ .../user/repository/UserRepository.java | 6 +- .../user/service/UserDomainService.java | 43 ++++++ .../domains/user/service/UserService.java | 7 - Infrastructure/build.gradle | 3 +- .../feign/config/OpenFeignConfig.java | 24 ++++ .../oauth/kakao/BaseFeignClientClass.java | 3 + .../oauth/kakao/client/KakaoInfoClient.java | 18 +++ .../oauth/kakao/client/KakaoOauthClient.java | 30 +++++ .../oauth/kakao/config/KakaoInfoConfig.java | 23 ++++ .../kakao/config/KakaoInfoErrorDecoder.java | 31 +++++ .../oauth/kakao/config/KakaoKauthConfig.java | 23 ++++ .../oauth/kakao/config/KauthErrorDecoder.java | 23 ++++ .../kakao/dto/KakaoInformationResponse.java | 67 ++++++++++ .../kakao/dto/KakaoKauthErrorResponse.java | 30 +++++ .../oauth/kakao/dto/KakaoTokenResponse.java | 16 +++ .../oauth/kakao/dto/OIDCPublicKeyDto.java | 15 +++ .../kakao/dto/OIDCPublicKeysResponse.java | 12 ++ .../kakao/exception/KakaoKauthErrorCode.java | 56 ++++++++ .../config}/RedisCacheConfig.java | 19 ++- .../redis => redis/config}/RedisConfig.java | 6 +- .../config}/RedissonConfig.java | 2 +- 85 files changed, 2077 insertions(+), 47 deletions(-) create mode 100644 Api/src/main/java/allchive/server/api/auth/controller/OauthController.java create mode 100644 Api/src/main/java/allchive/server/api/auth/controller/authController.java create mode 100644 Api/src/main/java/allchive/server/api/auth/model/dto/KakaoUserInfoDto.java create mode 100644 Api/src/main/java/allchive/server/api/auth/model/dto/request/RegisterRequest.java create mode 100644 Api/src/main/java/allchive/server/api/auth/model/dto/response/OauthLoginLinkResponse.java create mode 100644 Api/src/main/java/allchive/server/api/auth/model/dto/response/OauthRegisterResponse.java create mode 100644 Api/src/main/java/allchive/server/api/auth/model/dto/response/OauthSignInResponse.java create mode 100644 Api/src/main/java/allchive/server/api/auth/model/dto/response/OauthTokenResponse.java create mode 100644 Api/src/main/java/allchive/server/api/auth/model/dto/response/OauthUserInfoResponse.java create mode 100644 Api/src/main/java/allchive/server/api/auth/model/vo/TokenVo.java create mode 100644 Api/src/main/java/allchive/server/api/auth/service/OauthLinkUseCase.java create mode 100644 Api/src/main/java/allchive/server/api/auth/service/OauthLoginUseCase.java create mode 100644 Api/src/main/java/allchive/server/api/auth/service/OauthRegisterUseCase.java create mode 100644 Api/src/main/java/allchive/server/api/auth/service/helper/KakaoOauthHelper.java create mode 100644 Api/src/main/java/allchive/server/api/auth/service/helper/OauthOIDCHelper.java create mode 100644 Api/src/main/java/allchive/server/api/auth/service/helper/TokenGenerateHelper.java create mode 100644 Api/src/main/java/allchive/server/api/config/security/AccessDeniedFilter.java create mode 100644 Api/src/main/java/allchive/server/api/config/security/AuthDetails.java create mode 100644 Api/src/main/java/allchive/server/api/config/security/FilterConfig.java create mode 100644 Api/src/main/java/allchive/server/api/config/security/JwtExceptionFilter.java create mode 100644 Api/src/main/java/allchive/server/api/config/security/JwtTokenFilter.java create mode 100644 Core/src/main/java/allchive/server/core/common/CoreConfigurationPropertiesConfig.java create mode 100644 Core/src/main/java/allchive/server/core/dto/OIDCDecodePayload.java create mode 100644 Core/src/main/java/allchive/server/core/error/BaseDynamicException.java delete mode 100644 Core/src/main/java/allchive/server/core/error/exception/.gitkeep create mode 100644 Core/src/main/java/allchive/server/core/error/exception/ExampleException.java create mode 100644 Core/src/main/java/allchive/server/core/error/exception/ExpiredRefreshTokenException.java create mode 100644 Core/src/main/java/allchive/server/core/error/exception/ExpiredTokenException.java create mode 100644 Core/src/main/java/allchive/server/core/error/exception/InternalServerError.java create mode 100644 Core/src/main/java/allchive/server/core/error/exception/InvalidOauthProviderException.java create mode 100644 Core/src/main/java/allchive/server/core/error/exception/InvalidTokenException.java create mode 100644 Core/src/main/java/allchive/server/core/error/exception/OtherServerBadRequestException.java create mode 100644 Core/src/main/java/allchive/server/core/error/exception/OtherServerExpiredTokenException.java create mode 100644 Core/src/main/java/allchive/server/core/error/exception/OtherServerForbiddenException.java create mode 100644 Core/src/main/java/allchive/server/core/error/exception/OtherServerNotFoundException.java create mode 100644 Core/src/main/java/allchive/server/core/error/exception/OtherServerUnauthorizedException.java create mode 100644 Core/src/main/java/allchive/server/core/jwt/JwtOIDCProvider.java create mode 100644 Core/src/main/java/allchive/server/core/jwt/JwtProperties.java create mode 100644 Core/src/main/java/allchive/server/core/jwt/JwtTokenProvider.java create mode 100644 Core/src/main/java/allchive/server/core/jwt/dto/AccessTokenInfo.java create mode 100644 Core/src/main/java/allchive/server/core/properties/KakaoOAuthProperties.java create mode 100644 Domain/src/main/java/allchive/server/domain/DomainPackageLocation.java create mode 100644 Domain/src/main/java/allchive/server/domain/config/JpaConfig.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/user/adaptor/RefreshTokenAdaptor.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/user/domain/RefreshTokenEntity.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/user/domain/enums/UserRole.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/user/exception/exceptions/AlreadySignUpUserException.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/user/exception/exceptions/ForbiddenUserException.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/user/exception/exceptions/UserNotFoundException.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/user/repository/RefreshTokenRepository.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/user/service/UserDomainService.java delete mode 100644 Domain/src/main/java/allchive/server/domain/domains/user/service/UserService.java create mode 100644 Infrastructure/src/main/java/allchive/server/infrastructure/feign/config/OpenFeignConfig.java create mode 100644 Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/BaseFeignClientClass.java create mode 100644 Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/client/KakaoInfoClient.java create mode 100644 Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/client/KakaoOauthClient.java create mode 100644 Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/config/KakaoInfoConfig.java create mode 100644 Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/config/KakaoInfoErrorDecoder.java create mode 100644 Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/config/KakaoKauthConfig.java create mode 100644 Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/config/KauthErrorDecoder.java create mode 100644 Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/dto/KakaoInformationResponse.java create mode 100644 Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/dto/KakaoKauthErrorResponse.java create mode 100644 Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/dto/KakaoTokenResponse.java create mode 100644 Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/dto/OIDCPublicKeyDto.java create mode 100644 Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/dto/OIDCPublicKeysResponse.java create mode 100644 Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/exception/KakaoKauthErrorCode.java rename Infrastructure/src/main/java/allchive/server/infrastructure/{config/redis => redis/config}/RedisCacheConfig.java (64%) rename Infrastructure/src/main/java/allchive/server/infrastructure/{config/redis => redis/config}/RedisConfig.java (89%) rename Infrastructure/src/main/java/allchive/server/infrastructure/{config/redis => redis/config}/RedissonConfig.java (93%) diff --git a/Api/src/main/java/allchive/server/api/auth/controller/OauthController.java b/Api/src/main/java/allchive/server/api/auth/controller/OauthController.java new file mode 100644 index 00000000..2722542a --- /dev/null +++ b/Api/src/main/java/allchive/server/api/auth/controller/OauthController.java @@ -0,0 +1,81 @@ +package allchive.server.api.auth.controller; + + +import allchive.server.api.auth.model.dto.request.RegisterRequest; +import allchive.server.api.auth.model.dto.response.OauthLoginLinkResponse; +import allchive.server.api.auth.model.dto.response.OauthRegisterResponse; +import allchive.server.api.auth.model.dto.response.OauthSignInResponse; +import allchive.server.api.auth.service.*; +import allchive.server.core.helper.SpringEnvironmentHelper; +import allchive.server.domain.domains.user.domain.enums.OauthProvider; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/auth/oauth") +@RequiredArgsConstructor +@Slf4j +@Tag(name = "1-2. [oauth]") +public class OauthController { + private final OauthLinkUseCase oauthLinkUseCase; + private final OauthLoginUseCase oauthLoginUseCase; + private final OauthRegisterUseCase oauthRegisterUseCase; + private final SpringEnvironmentHelper springEnvironmentHelper; + + @Operation(summary = "oauth 링크발급", description = "oauth 링크를 받아볼수 있습니다.") + @GetMapping("/link/{provider}") + public OauthLoginLinkResponse getOauthLink( + @PathVariable("provider") OauthProvider provider, + @RequestHeader(value = "referer", required = false) String referer, + @RequestHeader(value = "host", required = false) String host) { + if (referer.contains(host)) { + String format = String.format("https://%s/", host); + return oauthLinkUseCase.getKaKaoOauthLink(provider, format); + } + return oauthLinkUseCase.getKaKaoOauthLink(provider, referer); + } + + @Operation(summary = "개발용 oauth 링크발급", deprecated = true) + @GetMapping("/link/{provider}/test") + public OauthLoginLinkResponse getOauthLinkTest( + @PathVariable("provider") OauthProvider provider) { + if (springEnvironmentHelper.isProdProfile()) {} + + return oauthLinkUseCase.getKaKaoOauthLinkTest(provider); + } + + @Operation( + summary = "로그인", + description = "referer, host 입력 안하셔도 됩니다!" + "회원가입 안된 유저일 경우, canLogin=false 값을 보냅니다!") + @PostMapping("/login/{provider}") + public OauthSignInResponse oauthUserLogin( + @PathVariable("provider") OauthProvider provider, + @RequestParam("code") String code, + @RequestHeader(value = "referer", required = false) String referer, + @RequestHeader(value = "host", required = false) String host) { + if (referer.contains(host)) { + String format = String.format("https://%s/", host); + return oauthLoginUseCase.execute(provider, code, format); + } + return oauthLoginUseCase.execute(provider, code, referer); + } + + @Operation(summary = "개발용 로그인", deprecated = true) + @PostMapping("/login/{provider}/test") + public OauthSignInResponse oauthUserLoginTest( + @PathVariable("provider") OauthProvider provider, @RequestParam("code") String code) { + return oauthLoginUseCase.executeTest(provider, code); + } + + @Operation(summary = "회원가입") + @PostMapping("/register/{provider}/test") + public OauthRegisterResponse oauthUserRegister( + @PathVariable("provider") OauthProvider provider, + @RequestParam("idToken") String idToken, + @RequestBody RegisterRequest registerRequest) { + return oauthRegisterUseCase.execute(provider, idToken, registerRequest); + } +} diff --git a/Api/src/main/java/allchive/server/api/auth/controller/authController.java b/Api/src/main/java/allchive/server/api/auth/controller/authController.java new file mode 100644 index 00000000..de042ab0 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/auth/controller/authController.java @@ -0,0 +1,15 @@ +package allchive.server.api.auth.controller; + + +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/auth") +@RequiredArgsConstructor +@Slf4j +@Tag(name = "1-1. [auth]") +public class authController {} diff --git a/Api/src/main/java/allchive/server/api/auth/model/dto/KakaoUserInfoDto.java b/Api/src/main/java/allchive/server/api/auth/model/dto/KakaoUserInfoDto.java new file mode 100644 index 00000000..43dec326 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/auth/model/dto/KakaoUserInfoDto.java @@ -0,0 +1,26 @@ +package allchive.server.api.auth.model.dto; + + +import allchive.server.domain.domains.user.domain.enums.OauthInfo; +import allchive.server.domain.domains.user.domain.enums.OauthProvider; +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class KakaoUserInfoDto { + + // oauth인증한 사용자 고유 아이디 + private final String oauthId; + + private final String email; + private final String phoneNumber; + private final String profileImage; + private final String name; + private final OauthProvider oauthProvider; + + public OauthInfo toOauthInfo() { + return OauthInfo.builder().oid(oauthId).provider(oauthProvider).build(); + } +} +; diff --git a/Api/src/main/java/allchive/server/api/auth/model/dto/request/RegisterRequest.java b/Api/src/main/java/allchive/server/api/auth/model/dto/request/RegisterRequest.java new file mode 100644 index 00000000..49ae7f4b --- /dev/null +++ b/Api/src/main/java/allchive/server/api/auth/model/dto/request/RegisterRequest.java @@ -0,0 +1,19 @@ +package allchive.server.api.auth.model.dto.request; + + +import io.swagger.v3.oas.annotations.media.Schema; +import javax.validation.constraints.NotBlank; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +public class RegisterRequest { + @Schema(defaultValue = "www.s3....", description = "프로필 이미지 url") + @NotBlank(message = "프로필 이미지 url을 입력하세요") + private String profileImgUrl; + + @Schema(defaultValue = "닉네임", description = "닉네임") + @NotBlank(message = "닉네임을 입력하세요") + private String nickname; +} diff --git a/Api/src/main/java/allchive/server/api/auth/model/dto/response/OauthLoginLinkResponse.java b/Api/src/main/java/allchive/server/api/auth/model/dto/response/OauthLoginLinkResponse.java new file mode 100644 index 00000000..3351f25e --- /dev/null +++ b/Api/src/main/java/allchive/server/api/auth/model/dto/response/OauthLoginLinkResponse.java @@ -0,0 +1,11 @@ +package allchive.server.api.auth.model.dto.response; + + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class OauthLoginLinkResponse { + private String link; +} diff --git a/Api/src/main/java/allchive/server/api/auth/model/dto/response/OauthRegisterResponse.java b/Api/src/main/java/allchive/server/api/auth/model/dto/response/OauthRegisterResponse.java new file mode 100644 index 00000000..21480013 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/auth/model/dto/response/OauthRegisterResponse.java @@ -0,0 +1,28 @@ +package allchive.server.api.auth.model.dto.response; + + +import allchive.server.api.auth.model.vo.TokenVo; +import com.fasterxml.jackson.annotation.JsonUnwrapped; +import lombok.Builder; +import lombok.Getter; + +@Getter +public class OauthRegisterResponse { + @JsonUnwrapped private TokenVo tokenVo; + + @Builder + private OauthRegisterResponse(TokenVo tokenVo) { + this.tokenVo = tokenVo; + } + + public static OauthRegisterResponse of( + String accessToken, Long accessTokenAge, String refreshToken, Long refreshTokenAge) { + return OauthRegisterResponse.builder() + .tokenVo(TokenVo.of(accessToken, accessTokenAge, refreshToken, refreshTokenAge)) + .build(); + } + + public static OauthRegisterResponse from(OauthSignInResponse oauthSignInResponse) { + return OauthRegisterResponse.builder().tokenVo(oauthSignInResponse.getTokenVo()).build(); + } +} diff --git a/Api/src/main/java/allchive/server/api/auth/model/dto/response/OauthSignInResponse.java b/Api/src/main/java/allchive/server/api/auth/model/dto/response/OauthSignInResponse.java new file mode 100644 index 00000000..68d7b4b8 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/auth/model/dto/response/OauthSignInResponse.java @@ -0,0 +1,48 @@ +package allchive.server.api.auth.model.dto.response; + + +import allchive.server.api.auth.model.vo.TokenVo; +import com.fasterxml.jackson.annotation.JsonUnwrapped; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Builder; +import lombok.Getter; + +@Getter +public class OauthSignInResponse { + @Schema(description = "로그인 가능 여부") + private boolean canLogin; + + @Schema(description = "idToken, 로그인 되었을 경우 null") + private String idToken; + + @JsonUnwrapped private TokenVo tokenVo; + + @Builder + private OauthSignInResponse(boolean canLogin, String idToken, TokenVo tokenVo) { + this.canLogin = canLogin; + this.idToken = idToken; + this.tokenVo = tokenVo; + } + + public static OauthSignInResponse of( + boolean canLogin, + String idToken, + String accessToken, + Long accessTokenAge, + String refreshToken, + Long refreshTokenAge) { + return OauthSignInResponse.builder() + .canLogin(canLogin) + .idToken(idToken) + .tokenVo(TokenVo.of(accessToken, accessTokenAge, refreshToken, refreshTokenAge)) + .build(); + } + + public static OauthSignInResponse cannotLogin(String idToken) { + return OauthSignInResponse.builder() + .canLogin(false) + .idToken(idToken) + .tokenVo(TokenVo.of(null, null, null, null)) + .build(); + } +} diff --git a/Api/src/main/java/allchive/server/api/auth/model/dto/response/OauthTokenResponse.java b/Api/src/main/java/allchive/server/api/auth/model/dto/response/OauthTokenResponse.java new file mode 100644 index 00000000..2dfe1387 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/auth/model/dto/response/OauthTokenResponse.java @@ -0,0 +1,28 @@ +package allchive.server.api.auth.model.dto.response; + + +import allchive.server.infrastructure.oauth.kakao.dto.KakaoTokenResponse; +import lombok.Builder; +import lombok.Getter; + +@Getter +public class OauthTokenResponse { + private String accessToken; + private String refreshToken; + private String idToken; + + @Builder + private OauthTokenResponse(String accessToken, String refreshToken, String idToken) { + this.accessToken = accessToken; + this.refreshToken = refreshToken; + this.idToken = idToken; + } + + public static OauthTokenResponse from(KakaoTokenResponse kakaoTokenResponse) { + return OauthTokenResponse.builder() + .idToken(kakaoTokenResponse.getIdToken()) + .refreshToken(kakaoTokenResponse.getRefreshToken()) + .accessToken(kakaoTokenResponse.getAccessToken()) + .build(); + } +} diff --git a/Api/src/main/java/allchive/server/api/auth/model/dto/response/OauthUserInfoResponse.java b/Api/src/main/java/allchive/server/api/auth/model/dto/response/OauthUserInfoResponse.java new file mode 100644 index 00000000..e0c97af3 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/auth/model/dto/response/OauthUserInfoResponse.java @@ -0,0 +1,32 @@ +package allchive.server.api.auth.model.dto.response; + + +import allchive.server.api.auth.model.dto.KakaoUserInfoDto; +import lombok.Builder; +import lombok.Getter; + +@Getter +public class OauthUserInfoResponse { + private final String email; + private final String phoneNumber; + private final String profileImage; + private final String name; + + @Builder + public OauthUserInfoResponse( + String email, String phoneNumber, String profileImage, String name) { + this.email = email; + this.phoneNumber = phoneNumber; + this.profileImage = profileImage; + this.name = name; + } + + public static OauthUserInfoResponse from(KakaoUserInfoDto kakaoUserInfoDto) { + return OauthUserInfoResponse.builder() + .email(kakaoUserInfoDto.getEmail()) + .phoneNumber(kakaoUserInfoDto.getPhoneNumber()) + .profileImage(kakaoUserInfoDto.getProfileImage()) + .name(kakaoUserInfoDto.getName()) + .build(); + } +} diff --git a/Api/src/main/java/allchive/server/api/auth/model/vo/TokenVo.java b/Api/src/main/java/allchive/server/api/auth/model/vo/TokenVo.java new file mode 100644 index 00000000..090856e1 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/auth/model/vo/TokenVo.java @@ -0,0 +1,38 @@ +package allchive.server.api.auth.model.vo; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Builder; +import lombok.Getter; + +@Getter +public class TokenVo { + @Schema(description = "어세스 토큰") + private final String accessToken; + + private final Long accessTokenAge; + + @Schema(description = "리프레쉬 토큰") + private final String refreshToken; + + private final Long refreshTokenAge; + + @Builder + private TokenVo( + String accessToken, Long accessTokenAge, String refreshToken, Long refreshTokenAge) { + this.accessToken = accessToken; + this.accessTokenAge = accessTokenAge; + this.refreshToken = refreshToken; + this.refreshTokenAge = refreshTokenAge; + } + + public static TokenVo of( + String accessToken, Long accessTokenAge, String refreshToken, Long refreshTokenAge) { + return TokenVo.builder() + .accessToken(accessToken) + .accessTokenAge(accessTokenAge) + .refreshToken(refreshToken) + .refreshTokenAge(refreshTokenAge) + .build(); + } +} diff --git a/Api/src/main/java/allchive/server/api/auth/service/OauthLinkUseCase.java b/Api/src/main/java/allchive/server/api/auth/service/OauthLinkUseCase.java new file mode 100644 index 00000000..3f04c969 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/auth/service/OauthLinkUseCase.java @@ -0,0 +1,33 @@ +package allchive.server.api.auth.service; + + +import allchive.server.api.auth.model.dto.response.OauthLoginLinkResponse; +import allchive.server.api.auth.service.helper.KakaoOauthHelper; +import allchive.server.core.annotation.UseCase; +import allchive.server.core.error.exception.InvalidOauthProviderException; +import allchive.server.domain.domains.user.domain.enums.OauthProvider; +import lombok.RequiredArgsConstructor; + +@UseCase +@RequiredArgsConstructor +public class OauthLinkUseCase { + private final KakaoOauthHelper kakaoOauthHelper; + + public OauthLoginLinkResponse getKaKaoOauthLinkTest(OauthProvider provider) { + switch (provider) { + case KAKAO: + return new OauthLoginLinkResponse(kakaoOauthHelper.getKaKaoOauthLinkTest()); + default: + throw InvalidOauthProviderException.EXCEPTION; + } + } + + public OauthLoginLinkResponse getKaKaoOauthLink(OauthProvider provider, String referer) { + switch (provider) { + case KAKAO: + return new OauthLoginLinkResponse(kakaoOauthHelper.getKaKaoOauthLink(referer)); + default: + throw InvalidOauthProviderException.EXCEPTION; + } + } +} diff --git a/Api/src/main/java/allchive/server/api/auth/service/OauthLoginUseCase.java b/Api/src/main/java/allchive/server/api/auth/service/OauthLoginUseCase.java new file mode 100644 index 00000000..f39347b9 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/auth/service/OauthLoginUseCase.java @@ -0,0 +1,72 @@ +package allchive.server.api.auth.service; + + +import allchive.server.api.auth.model.dto.response.OauthSignInResponse; +import allchive.server.api.auth.model.dto.response.OauthTokenResponse; +import allchive.server.api.auth.service.helper.KakaoOauthHelper; +import allchive.server.api.auth.service.helper.TokenGenerateHelper; +import allchive.server.core.annotation.UseCase; +import allchive.server.core.error.exception.InvalidOauthProviderException; +import allchive.server.domain.domains.user.domain.User; +import allchive.server.domain.domains.user.domain.enums.OauthInfo; +import allchive.server.domain.domains.user.domain.enums.OauthProvider; +import allchive.server.domain.domains.user.service.UserDomainService; +import lombok.RequiredArgsConstructor; + +@UseCase +@RequiredArgsConstructor +public class OauthLoginUseCase { + + private final KakaoOauthHelper kakaoOauthHelper; + private final UserDomainService userDomainService; + private final TokenGenerateHelper tokenGenerateHelper; + + public OauthSignInResponse execute(OauthProvider provider, String code, String referer) { + final OauthTokenResponse oauthTokenResponse = getCredential(provider, code, referer); + final OauthInfo oauthInfo = getOauthInfo(provider, oauthTokenResponse.getIdToken()); + if (userDomainService.checkUserCanLogin(oauthInfo)) { + User user = userDomainService.loginUser(oauthInfo); + return tokenGenerateHelper.execute(user); + } else { + return OauthSignInResponse.cannotLogin(oauthTokenResponse.getIdToken()); + } + } + + public OauthSignInResponse executeTest(OauthProvider provider, String code) { + final OauthTokenResponse oauthTokenResponse = getCredentialTest(provider, code); + final OauthInfo oauthInfo = getOauthInfo(provider, oauthTokenResponse.getIdToken()); + if (userDomainService.checkUserCanLogin(oauthInfo)) { + User user = userDomainService.loginUser(oauthInfo); + return tokenGenerateHelper.execute(user); + } else { + return OauthSignInResponse.cannotLogin(oauthTokenResponse.getIdToken()); + } + } + + private OauthTokenResponse getCredential(OauthProvider provider, String code, String referer) { + switch (provider) { + case KAKAO: + return OauthTokenResponse.from(kakaoOauthHelper.getKakaoOauthToken(code, referer)); + default: + throw InvalidOauthProviderException.EXCEPTION; + } + } + + private OauthTokenResponse getCredentialTest(OauthProvider provider, String code) { + switch (provider) { + case KAKAO: + return OauthTokenResponse.from(kakaoOauthHelper.getKakaoOauthTokenTest(code)); + default: + throw InvalidOauthProviderException.EXCEPTION; + } + } + + private OauthInfo getOauthInfo(OauthProvider provider, String idToken) { + switch (provider) { + case KAKAO: + return kakaoOauthHelper.getOauthInfoByIdToken(idToken); + default: + throw InvalidOauthProviderException.EXCEPTION; + } + } +} diff --git a/Api/src/main/java/allchive/server/api/auth/service/OauthRegisterUseCase.java b/Api/src/main/java/allchive/server/api/auth/service/OauthRegisterUseCase.java new file mode 100644 index 00000000..aafa813f --- /dev/null +++ b/Api/src/main/java/allchive/server/api/auth/service/OauthRegisterUseCase.java @@ -0,0 +1,42 @@ +package allchive.server.api.auth.service; + + +import allchive.server.api.auth.model.dto.request.RegisterRequest; +import allchive.server.api.auth.model.dto.response.OauthRegisterResponse; +import allchive.server.api.auth.service.helper.KakaoOauthHelper; +import allchive.server.api.auth.service.helper.TokenGenerateHelper; +import allchive.server.core.annotation.UseCase; +import allchive.server.core.error.exception.InvalidOauthProviderException; +import allchive.server.domain.domains.user.domain.User; +import allchive.server.domain.domains.user.domain.enums.OauthInfo; +import allchive.server.domain.domains.user.domain.enums.OauthProvider; +import allchive.server.domain.domains.user.service.UserDomainService; +import lombok.RequiredArgsConstructor; + +@UseCase +@RequiredArgsConstructor +public class OauthRegisterUseCase { + private final KakaoOauthHelper kakaoOauthHelper; + private final UserDomainService userDomainService; + private final TokenGenerateHelper tokenGenerateHelper; + + public OauthRegisterResponse execute( + OauthProvider provider, String idToken, RegisterRequest registerRequest) { + final OauthInfo oauthInfo = getOauthInfo(provider, idToken); + final User user = + userDomainService.registerUser( + registerRequest.getNickname(), + registerRequest.getProfileImgUrl(), + oauthInfo); + return OauthRegisterResponse.from(tokenGenerateHelper.execute(user)); + } + + private OauthInfo getOauthInfo(OauthProvider provider, String idToken) { + switch (provider) { + case KAKAO: + return kakaoOauthHelper.getOauthInfoByIdToken(idToken); + default: + throw InvalidOauthProviderException.EXCEPTION; + } + } +} diff --git a/Api/src/main/java/allchive/server/api/auth/service/helper/KakaoOauthHelper.java b/Api/src/main/java/allchive/server/api/auth/service/helper/KakaoOauthHelper.java new file mode 100644 index 00000000..991fb9ce --- /dev/null +++ b/Api/src/main/java/allchive/server/api/auth/service/helper/KakaoOauthHelper.java @@ -0,0 +1,76 @@ +package allchive.server.api.auth.service.helper; + +import static allchive.server.core.consts.AllchiveConst.KAKAO_OAUTH_QUERY_STRING; + +import allchive.server.core.annotation.Helper; +import allchive.server.core.dto.OIDCDecodePayload; +import allchive.server.core.properties.KakaoOAuthProperties; +import allchive.server.domain.domains.user.domain.enums.OauthInfo; +import allchive.server.domain.domains.user.domain.enums.OauthProvider; +import allchive.server.infrastructure.oauth.kakao.client.KakaoInfoClient; +import allchive.server.infrastructure.oauth.kakao.client.KakaoOauthClient; +import allchive.server.infrastructure.oauth.kakao.dto.KakaoTokenResponse; +import allchive.server.infrastructure.oauth.kakao.dto.OIDCPublicKeysResponse; +import lombok.RequiredArgsConstructor; + +@Helper +@RequiredArgsConstructor +public class KakaoOauthHelper { + private final KakaoOAuthProperties kakaoOauthProperties; + private final KakaoInfoClient kakaoInfoClient; + private final KakaoOauthClient kakaoOauthClient; + private final OauthOIDCHelper oauthOIDCHelper; + + /** link * */ + public String getKaKaoOauthLink(String referer) { + // TODO : 프론트 콜백 URL 알아내면 바꾸기 + return kakaoOauthProperties.getBaseUrl() + + String.format( + KAKAO_OAUTH_QUERY_STRING, + kakaoOauthProperties.getClientId(), + referer + "kakao/callback"); + } + + public String getKaKaoOauthLinkTest() { + return kakaoOauthProperties.getBaseUrl() + + String.format( + KAKAO_OAUTH_QUERY_STRING, + kakaoOauthProperties.getClientId(), + kakaoOauthProperties.getRedirectUrl()); + } + + /** token * */ + public KakaoTokenResponse getKakaoOauthToken(String code, String referer) { + // TODO : 프론트 콜백 URL 알아내면 바꾸기 + return kakaoOauthClient.kakaoAuth( + kakaoOauthProperties.getClientId(), + referer + "/kakao/callback", + code, + kakaoOauthProperties.getClientSecret()); + } + + public KakaoTokenResponse getKakaoOauthTokenTest(String code) { + return kakaoOauthClient.kakaoAuth( + kakaoOauthProperties.getClientId(), + kakaoOauthProperties.getRedirectUrl(), + code, + kakaoOauthProperties.getClientSecret()); + } + + public OauthInfo getOauthInfoByIdToken(String idToken) { + OIDCDecodePayload oidcDecodePayload = getOIDCDecodePayload(idToken); + return OauthInfo.builder() + .provider(OauthProvider.KAKAO) + .oid(oidcDecodePayload.getSub()) + .build(); + } + + public OIDCDecodePayload getOIDCDecodePayload(String token) { + OIDCPublicKeysResponse oidcPublicKeysResponse = kakaoOauthClient.getKakaoOIDCOpenKeys(); + return oauthOIDCHelper.getPayloadFromIdToken( + token, + kakaoOauthProperties.getBaseUrl(), + kakaoOauthProperties.getAppId(), + oidcPublicKeysResponse); + } +} diff --git a/Api/src/main/java/allchive/server/api/auth/service/helper/OauthOIDCHelper.java b/Api/src/main/java/allchive/server/api/auth/service/helper/OauthOIDCHelper.java new file mode 100644 index 00000000..0e1dd043 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/auth/service/helper/OauthOIDCHelper.java @@ -0,0 +1,33 @@ +package allchive.server.api.auth.service.helper; + + +import allchive.server.core.annotation.Helper; +import allchive.server.core.dto.OIDCDecodePayload; +import allchive.server.core.jwt.JwtOIDCProvider; +import allchive.server.infrastructure.oauth.kakao.dto.OIDCPublicKeyDto; +import allchive.server.infrastructure.oauth.kakao.dto.OIDCPublicKeysResponse; +import lombok.RequiredArgsConstructor; + +@Helper +@RequiredArgsConstructor +public class OauthOIDCHelper { + private final JwtOIDCProvider jwtOIDCProvider; + + public OIDCDecodePayload getPayloadFromIdToken( + String token, String iss, String aud, OIDCPublicKeysResponse oidcPublicKeysResponse) { + String kid = getKidFromUnsignedIdToken(token, iss, aud); + + OIDCPublicKeyDto oidcPublicKeyDto = + oidcPublicKeysResponse.getKeys().stream() + .filter(o -> o.getKid().equals(kid)) + .findFirst() + .orElseThrow(); + + return jwtOIDCProvider.getOIDCTokenBody( + token, oidcPublicKeyDto.getN(), oidcPublicKeyDto.getE()); + } + + private String getKidFromUnsignedIdToken(String token, String iss, String aud) { + return jwtOIDCProvider.getKidFromUnsignedTokenHeader(token, iss, aud); + } +} diff --git a/Api/src/main/java/allchive/server/api/auth/service/helper/TokenGenerateHelper.java b/Api/src/main/java/allchive/server/api/auth/service/helper/TokenGenerateHelper.java new file mode 100644 index 00000000..da123129 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/auth/service/helper/TokenGenerateHelper.java @@ -0,0 +1,44 @@ +package allchive.server.api.auth.service.helper; + + +import allchive.server.api.auth.model.dto.response.OauthSignInResponse; +import allchive.server.core.annotation.Helper; +import allchive.server.core.jwt.JwtTokenProvider; +import allchive.server.domain.domains.user.adaptor.RefreshTokenAdaptor; +import allchive.server.domain.domains.user.domain.RefreshTokenEntity; +import allchive.server.domain.domains.user.domain.User; +import allchive.server.domain.domains.user.domain.enums.UserRole; +import lombok.RequiredArgsConstructor; +import org.springframework.transaction.annotation.Transactional; + +@Helper +@RequiredArgsConstructor +public class TokenGenerateHelper { + + private final JwtTokenProvider jwtTokenProvider; + + private final RefreshTokenAdaptor refreshTokenAdaptor; + + @Transactional + public OauthSignInResponse execute(User user) { + String newAccessToken = + jwtTokenProvider.generateAccessToken(user.getId(), UserRole.USER.getValue()); + String newRefreshToken = jwtTokenProvider.generateRefreshToken(user.getId()); + + RefreshTokenEntity newRefreshTokenEntityEntity = + RefreshTokenEntity.builder() + .refreshToken(newRefreshToken) + .id(user.getId()) + .ttl(jwtTokenProvider.getRefreshTokenTTLSecond()) + .build(); + refreshTokenAdaptor.save(newRefreshTokenEntityEntity); + + return OauthSignInResponse.of( + true, + null, + newAccessToken, + jwtTokenProvider.getAccessTokenTTLSecond(), + newRefreshToken, + jwtTokenProvider.getRefreshTokenTTLSecond()); + } +} diff --git a/Api/src/main/java/allchive/server/api/config/GlobalExceptionHandler.java b/Api/src/main/java/allchive/server/api/config/GlobalExceptionHandler.java index 4b29cac6..a1826b6a 100644 --- a/Api/src/main/java/allchive/server/api/config/GlobalExceptionHandler.java +++ b/Api/src/main/java/allchive/server/api/config/GlobalExceptionHandler.java @@ -3,6 +3,7 @@ import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR; import allchive.server.core.dto.ErrorReason; +import allchive.server.core.error.BaseDynamicException; import allchive.server.core.error.BaseErrorException; import allchive.server.core.error.ErrorResponse; import allchive.server.core.error.GlobalErrorCode; @@ -91,6 +92,15 @@ public ResponseEntity<ErrorResponse> handleBaseErrorException( .body(errorResponse); } + // dynamic error 처리 + @ExceptionHandler(BaseDynamicException.class) + public ResponseEntity<ErrorResponse> BaseDynamicExceptionHandler( + BaseDynamicException e, HttpServletRequest request) { + ErrorResponse errorResponse = + ErrorResponse.from(ErrorReason.of(e.getStatus(), e.getCode(), e.getMessage())); + return ResponseEntity.status(HttpStatus.valueOf(e.getStatus())).body(errorResponse); + } + // 위에서 따로 처리하지 않은 에러를 모두 처리해줍니다. @ExceptionHandler(Exception.class) protected ResponseEntity<ErrorResponse> handleException( diff --git a/Api/src/main/java/allchive/server/api/config/SwaggerConfig.java b/Api/src/main/java/allchive/server/api/config/SwaggerConfig.java index 99222fd6..51244bdd 100644 --- a/Api/src/main/java/allchive/server/api/config/SwaggerConfig.java +++ b/Api/src/main/java/allchive/server/api/config/SwaggerConfig.java @@ -26,12 +26,12 @@ public OpenAPI openAPI(ServletContext servletContext) { private Info swaggerInfo() { License license = new License(); license.setUrl("https://github.com/Central-MakeUs"); - license.setName("Dachive"); + license.setName("Allchive"); return new Info() .version("v0.0.1") - .title("\"Dachive 서버 API문서\"") - .description("Dachive 서버의 API 문서 입니다.") + .title("\"Allchive 서버 API문서\"") + .description("Allchive 서버의 API 문서 입니다.") .license(license); } diff --git a/Api/src/main/java/allchive/server/api/config/security/AccessDeniedFilter.java b/Api/src/main/java/allchive/server/api/config/security/AccessDeniedFilter.java new file mode 100644 index 00000000..29b590a3 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/config/security/AccessDeniedFilter.java @@ -0,0 +1,59 @@ +package allchive.server.api.config.security; + +import static allchive.server.core.consts.AllchiveConst.SwaggerPatterns; + +import allchive.server.core.error.BaseErrorException; +import allchive.server.core.error.ErrorResponse; +import allchive.server.core.error.GlobalErrorCode; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.IOException; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.http.MediaType; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.stereotype.Component; +import org.springframework.util.PatternMatchUtils; +import org.springframework.web.filter.OncePerRequestFilter; + +@RequiredArgsConstructor +@Component +public class AccessDeniedFilter extends OncePerRequestFilter { + + private final ObjectMapper objectMapper; + + @Override + protected boolean shouldNotFilter(HttpServletRequest request) { + String servletPath = request.getServletPath(); + return PatternMatchUtils.simpleMatch(SwaggerPatterns, servletPath); + } + + @Override + protected void doFilterInternal( + HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) + throws ServletException, IOException { + try { + filterChain.doFilter(request, response); + } catch (BaseErrorException e) { + handleException(response, getErrorResponse(e)); + } catch (AccessDeniedException e) { + ErrorResponse access_denied = + ErrorResponse.from(GlobalErrorCode.INVALID_ACCESS_TOKEN_ERROR.getErrorReason()); + handleException(response, access_denied); + } + } + + private ErrorResponse getErrorResponse(BaseErrorException e) { + return ErrorResponse.from(e.getErrorReason()); + } + + private void handleException(HttpServletResponse response, ErrorResponse errorResponse) + throws IOException { + response.setCharacterEncoding("UTF-8"); + response.setContentType(MediaType.APPLICATION_JSON_VALUE); + response.setStatus(errorResponse.getStatus()); + response.getWriter().write(objectMapper.writeValueAsString(errorResponse)); + } +} diff --git a/Api/src/main/java/allchive/server/api/config/security/AuthDetails.java b/Api/src/main/java/allchive/server/api/config/security/AuthDetails.java new file mode 100644 index 00000000..345655fc --- /dev/null +++ b/Api/src/main/java/allchive/server/api/config/security/AuthDetails.java @@ -0,0 +1,54 @@ +package allchive.server.api.config.security; + + +import java.util.Collection; +import java.util.Collections; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; + +@AllArgsConstructor +@Getter +public class AuthDetails implements UserDetails { + + private String userId; + + private String role; + + @Override + public Collection<? extends GrantedAuthority> getAuthorities() { + return Collections.singleton(new SimpleGrantedAuthority("ROLE_" + role)); + } + + @Override + public String getPassword() { + return null; + } + + @Override + public String getUsername() { + return userId; + } + + @Override + public boolean isAccountNonExpired() { + return true; + } + + @Override + public boolean isAccountNonLocked() { + return true; + } + + @Override + public boolean isCredentialsNonExpired() { + return true; + } + + @Override + public boolean isEnabled() { + return true; + } +} diff --git a/Api/src/main/java/allchive/server/api/config/security/CorsConfig.java b/Api/src/main/java/allchive/server/api/config/security/CorsConfig.java index 291e1711..6eba6a95 100644 --- a/Api/src/main/java/allchive/server/api/config/security/CorsConfig.java +++ b/Api/src/main/java/allchive/server/api/config/security/CorsConfig.java @@ -1,6 +1,7 @@ package allchive.server.api.config.security; +import allchive.server.core.helper.SpringEnvironmentHelper; import java.util.ArrayList; import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Configuration; @@ -10,19 +11,21 @@ @Configuration @RequiredArgsConstructor public class CorsConfig implements WebMvcConfigurer { + private final SpringEnvironmentHelper springEnvironmentHelper; @Override public void addCorsMappings(CorsRegistry registry) { ArrayList<String> allowedOriginPatterns = new ArrayList<>(); - allowedOriginPatterns.add("http://localhost:3000"); - allowedOriginPatterns.add("http://localhost:8080"); + if (springEnvironmentHelper.isDevProfile()) { + allowedOriginPatterns.add("http://localhost:3000"); + } allowedOriginPatterns.add("https://www.allchive.co.kr"); - + allowedOriginPatterns.add("https://staging.allchive.co.kr"); String[] patterns = allowedOriginPatterns.toArray(String[]::new); registry.addMapping("/**") .allowedMethods("*") .allowedOriginPatterns(patterns) - .allowCredentials(true) - .maxAge(3600L); + .exposedHeaders("Set-Cookie") + .allowCredentials(true); } } diff --git a/Api/src/main/java/allchive/server/api/config/security/FilterConfig.java b/Api/src/main/java/allchive/server/api/config/security/FilterConfig.java new file mode 100644 index 00000000..965e2dfa --- /dev/null +++ b/Api/src/main/java/allchive/server/api/config/security/FilterConfig.java @@ -0,0 +1,26 @@ +package allchive.server.api.config.security; + + +import lombok.RequiredArgsConstructor; +import org.springframework.security.config.annotation.SecurityConfigurerAdapter; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.web.DefaultSecurityFilterChain; +import org.springframework.security.web.access.intercept.FilterSecurityInterceptor; +import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; +import org.springframework.stereotype.Component; + +@RequiredArgsConstructor +@Component +public class FilterConfig + extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> { + private final JwtTokenFilter jwtTokenFilter; + private final AccessDeniedFilter accessDeniedFilter; + private final JwtExceptionFilter jwtExceptionFilter; + + @Override + public void configure(HttpSecurity builder) { + builder.addFilterBefore(jwtTokenFilter, BasicAuthenticationFilter.class); + builder.addFilterBefore(jwtExceptionFilter, JwtTokenFilter.class); + builder.addFilterBefore(accessDeniedFilter, FilterSecurityInterceptor.class); + } +} diff --git a/Api/src/main/java/allchive/server/api/config/security/JwtExceptionFilter.java b/Api/src/main/java/allchive/server/api/config/security/JwtExceptionFilter.java new file mode 100644 index 00000000..173455a9 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/config/security/JwtExceptionFilter.java @@ -0,0 +1,44 @@ +package allchive.server.api.config.security; + + +import allchive.server.core.error.BaseErrorException; +import allchive.server.core.error.ErrorResponse; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.IOException; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +@RequiredArgsConstructor +@Component +public class JwtExceptionFilter extends OncePerRequestFilter { + private final ObjectMapper objectMapper; + + @Override + protected void doFilterInternal( + HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) + throws ServletException, IOException { + try { + filterChain.doFilter(request, response); + } catch (BaseErrorException e) { + responseToClient(response, getErrorResponse(e)); + } + } + + private ErrorResponse getErrorResponse(BaseErrorException e) { + return ErrorResponse.from(e.getErrorReason()); + } + + private void responseToClient(HttpServletResponse response, ErrorResponse errorResponse) + throws IOException { + response.setCharacterEncoding("UTF-8"); + response.setContentType(MediaType.APPLICATION_JSON_VALUE); + response.setStatus(errorResponse.getStatus()); + response.getWriter().write(objectMapper.writeValueAsString(errorResponse)); + } +} diff --git a/Api/src/main/java/allchive/server/api/config/security/JwtTokenFilter.java b/Api/src/main/java/allchive/server/api/config/security/JwtTokenFilter.java new file mode 100644 index 00000000..70951244 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/config/security/JwtTokenFilter.java @@ -0,0 +1,59 @@ +package allchive.server.api.config.security; + +import static allchive.server.core.consts.AllchiveConst.AUTH_HEADER; +import static allchive.server.core.consts.AllchiveConst.BEARER; + +import allchive.server.core.jwt.JwtTokenProvider; +import allchive.server.core.jwt.dto.AccessTokenInfo; +import java.io.IOException; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +@RequiredArgsConstructor +@Component +public class JwtTokenFilter extends OncePerRequestFilter { + private final JwtTokenProvider jwtTokenProvider; + + @Override + protected void doFilterInternal( + HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) + throws ServletException, IOException { + String token = resolveToken(request); + + if (token != null) { + Authentication authentication = getAuthentication(token); + SecurityContextHolder.getContext().setAuthentication(authentication); + } + + filterChain.doFilter(request, response); + } + + private String resolveToken(HttpServletRequest request) { + String rawHeader = request.getHeader(AUTH_HEADER); + + if (rawHeader != null + && rawHeader.length() > BEARER.length() + && rawHeader.startsWith(BEARER)) { + return rawHeader.substring(BEARER.length()); + } + return null; + } + + public Authentication getAuthentication(String token) { + AccessTokenInfo accessTokenInfo = jwtTokenProvider.parseAccessToken(token); + + UserDetails userDetails = + new AuthDetails(accessTokenInfo.getUserId().toString(), accessTokenInfo.getRole()); + return new UsernamePasswordAuthenticationToken( + userDetails, "user", userDetails.getAuthorities()); + } +} diff --git a/Api/src/main/java/allchive/server/api/config/security/SecurityConfig.java b/Api/src/main/java/allchive/server/api/config/security/SecurityConfig.java index fc7a8cd7..5996b057 100644 --- a/Api/src/main/java/allchive/server/api/config/security/SecurityConfig.java +++ b/Api/src/main/java/allchive/server/api/config/security/SecurityConfig.java @@ -1,30 +1,55 @@ package allchive.server.api.config.security; +import static allchive.server.core.consts.AllchiveConst.SwaggerPatterns; +import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler; -@Configuration +@RequiredArgsConstructor @EnableWebSecurity public class SecurityConfig { + private final FilterConfig filterConfig; @Bean - public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { - http.formLogin() - .disable() - .cors() - .disable() - .csrf() - .disable() - .httpBasic() - .disable() - .sessionManagement() - .sessionCreationPolicy(SessionCreationPolicy.NEVER); + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(8); + } + + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + http.formLogin().disable().cors().and().csrf().disable(); + + http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); + http.authorizeRequests().expressionHandler(expressionHandler()); + + http.authorizeRequests() + .antMatchers(SwaggerPatterns) + .permitAll() + .mvcMatchers("/auth/oauth/**") + .permitAll() + .mvcMatchers("/auth/token/refresh") + .permitAll() + .mvcMatchers("/**/health/**") + .permitAll() + .mvcMatchers("/example/**") + .permitAll() + .anyRequest() + .hasRole("USER"); + + http.apply(filterConfig); return http.build(); } + + @Bean + public DefaultWebSecurityExpressionHandler expressionHandler() { + return new DefaultWebSecurityExpressionHandler(); + } } diff --git a/Api/src/main/java/allchive/server/api/example/ExampleController.java b/Api/src/main/java/allchive/server/api/example/ExampleController.java index 3bf9397e..90267d44 100644 --- a/Api/src/main/java/allchive/server/api/example/ExampleController.java +++ b/Api/src/main/java/allchive/server/api/example/ExampleController.java @@ -1,6 +1,7 @@ package allchive.server.api.example; +import allchive.server.core.error.exception.ExampleException; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; @@ -33,4 +34,9 @@ public int responsePatch() { public int responseDelete() { return 1; } + + @GetMapping(value = "/responseError") + public int responseError() { + throw ExampleException.EXCEPTION; + } } diff --git a/Core/build.gradle b/Core/build.gradle index e5f6f7e2..1cc27491 100644 --- a/Core/build.gradle +++ b/Core/build.gradle @@ -2,8 +2,11 @@ bootJar.enabled = false jar.enabled = true dependencies { -// implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.springframework.boot:spring-boot-starter-validation' + implementation 'io.jsonwebtoken:jjwt-api:0.11.5' + runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5' + runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5' + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.2' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.2' } \ No newline at end of file diff --git a/Core/src/main/java/allchive/server/core/common/CoreConfigurationPropertiesConfig.java b/Core/src/main/java/allchive/server/core/common/CoreConfigurationPropertiesConfig.java new file mode 100644 index 00000000..5964118d --- /dev/null +++ b/Core/src/main/java/allchive/server/core/common/CoreConfigurationPropertiesConfig.java @@ -0,0 +1,11 @@ +package allchive.server.core.common; + + +import allchive.server.core.jwt.JwtProperties; +import allchive.server.core.properties.KakaoOAuthProperties; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +@EnableConfigurationProperties({JwtProperties.class, KakaoOAuthProperties.class}) +@Configuration +public class CoreConfigurationPropertiesConfig {} diff --git a/Core/src/main/java/allchive/server/core/consts/AllchiveConst.java b/Core/src/main/java/allchive/server/core/consts/AllchiveConst.java index be822b9d..ac6f8088 100644 --- a/Core/src/main/java/allchive/server/core/consts/AllchiveConst.java +++ b/Core/src/main/java/allchive/server/core/consts/AllchiveConst.java @@ -4,12 +4,18 @@ public class AllchiveConst { public static final String AUTH_HEADER = "Authorization"; public static final String BEARER = "Bearer "; public static final String TOKEN_ROLE = "role"; + public static final String TOKEN_ISSUER = "ALLCHIVE"; public static final String TOKEN_TYPE = "type"; public static final String ACCESS_TOKEN = "ACCESS_TOKEN"; public static final String REFRESH_TOKEN = "REFRESH_TOKEN"; + + public static final String KID = "kid"; public static final String KR_YES = "예"; public static final String KR_NO = "아니요"; + public static final String PROD = "prod"; + public static final String DEV = "dev"; + public static final int MILLI_TO_SECOND = 1000; public static final int BAD_REQUEST = 400; public static final int UNAUTHORIZED = 401; @@ -17,6 +23,9 @@ public class AllchiveConst { public static final int NOT_FOUND = 404; public static final int INTERNAL_SERVER = 500; + public static final String KAKAO_OAUTH_QUERY_STRING = + "/oauth/authorize?client_id=%s&redirect_uri=%s&response_type=code"; + public static final String[] SwaggerPatterns = { "/swagger-resources/**", "/swagger-ui/**", "/v3/api-docs/**", "/v3/api-docs", }; diff --git a/Core/src/main/java/allchive/server/core/dto/OIDCDecodePayload.java b/Core/src/main/java/allchive/server/core/dto/OIDCDecodePayload.java new file mode 100644 index 00000000..056279e4 --- /dev/null +++ b/Core/src/main/java/allchive/server/core/dto/OIDCDecodePayload.java @@ -0,0 +1,18 @@ +package allchive.server.core.dto; + + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class OIDCDecodePayload { + /** issuer ex https://kauth.kakao.com */ + private String iss; + /** client id */ + private String aud; + /** oauth provider account unique id */ + private String sub; + + private String email; +} diff --git a/Core/src/main/java/allchive/server/core/error/BaseDynamicException.java b/Core/src/main/java/allchive/server/core/error/BaseDynamicException.java new file mode 100644 index 00000000..43605888 --- /dev/null +++ b/Core/src/main/java/allchive/server/core/error/BaseDynamicException.java @@ -0,0 +1,15 @@ +package allchive.server.core.error; + + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +@AllArgsConstructor +public class BaseDynamicException extends RuntimeException { + private final int status; + private final String code; + private final String message; +} diff --git a/Core/src/main/java/allchive/server/core/error/GlobalErrorCode.java b/Core/src/main/java/allchive/server/core/error/GlobalErrorCode.java index aa0bd707..82cf18ea 100644 --- a/Core/src/main/java/allchive/server/core/error/GlobalErrorCode.java +++ b/Core/src/main/java/allchive/server/core/error/GlobalErrorCode.java @@ -9,21 +9,34 @@ @Getter @AllArgsConstructor public enum GlobalErrorCode implements BaseErrorCode { + /** example * */ + EXAMPLE_ERROR(BAD_REQUEST, "GLOBAL_400_0", "에러 예시 입니다."), - /* global error */ + /** Server 오류 * */ HTTP_MESSAGE_NOT_READABLE(BAD_REQUEST, "GLOBAL_400_1", "잘못된 형식의 값을 입력했습니다."), _INTERNAL_SERVER_ERROR(INTERNAL_SERVER, "GLOBAL_500_1", "서버 오류. 관리자에게 문의 부탁드립니다."), + INVALID_OAUTH_PROVIDER(INTERNAL_SERVER, "GLOBAL_500_2", "지원하지 않는 OAuth Provider 입니다."), - /*토큰 에러*/ - NO_TOKEN(UNAUTHORIZED, "AUTH_401_1", "토큰이 존재하지 않습니다"), - INVALID_AUTH_TOKEN(UNAUTHORIZED, "AUTH_401_2", "액세스 토큰이 유효하지 않습니다"), + /** 토큰 에러 * */ + // TODO : 에러 코드 정렬 + INVALID_TOKEN(UNAUTHORIZED, "AUTH_401_2", "올바르지 않은 토큰입니다."), + INVALID_ACCESS_TOKEN_ERROR(UNAUTHORIZED, "AUTH_401_4", "알맞은 accessToken 을 넣어주세요."), EXPIRED_TOKEN(UNAUTHORIZED, "AUTH_401_3", "만료된 엑세스 토큰입니다"), - INVALID_REFRESH_TOKEN(UNAUTHORIZED, "AUTH_401_4", "리프레시 토큰이 유효하지 않습니다"), EXPIRED_REFRESH_TOKEN(UNAUTHORIZED, "AUTH_401_5", "만료된 리프레시 토큰입니다"), + INVALID_AUTH_TOKEN(UNAUTHORIZED, "AUTH_401_2", "액세스 토큰이 유효하지 않습니다"), + INVALID_REFRESH_TOKEN(UNAUTHORIZED, "AUTH_401_4", "리프레시 토큰이 유효하지 않습니다"), MISMATCH_REFRESH_TOKEN(UNAUTHORIZED, "AUTH_401_6", "리프레시 토큰의 유저 정보가 일치하지 않습니다"), FORBIDDEN_ADMIN(FORBIDDEN, "AUTH_403_1", "권한이 부여되지 않은 사용자입니다"), UNSUPPORTED_TOKEN(UNAUTHORIZED, "AUTH_401_7", "지원하지 않는 토큰입니다"), INVALID_SIGNATURE(UNAUTHORIZED, "AUTH_401_8", "잘못된 JWT 서명입니다"), + NO_TOKEN(UNAUTHORIZED, "AUTH_401_1", "토큰이 존재하지 않습니다"), + + /** Feign Client 오류 */ + OTHER_SERVER_BAD_REQUEST(BAD_REQUEST, "FEIGN_400_1", "Other server bad request"), + OTHER_SERVER_UNAUTHORIZED(BAD_REQUEST, "FEIGN_400_2", "Other server unauthorized"), + OTHER_SERVER_FORBIDDEN(BAD_REQUEST, "FEIGN_400_3", "Other server forbidden"), + OTHER_SERVER_EXPIRED_TOKEN(BAD_REQUEST, "FEIGN_400_4", "Other server expired token"), + OTHER_SERVER_NOT_FOUND(BAD_REQUEST, "FEIGN_400_5", "Other server not found error"), ; private int status; diff --git a/Core/src/main/java/allchive/server/core/error/exception/.gitkeep b/Core/src/main/java/allchive/server/core/error/exception/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/Core/src/main/java/allchive/server/core/error/exception/ExampleException.java b/Core/src/main/java/allchive/server/core/error/exception/ExampleException.java new file mode 100644 index 00000000..478a21d7 --- /dev/null +++ b/Core/src/main/java/allchive/server/core/error/exception/ExampleException.java @@ -0,0 +1,14 @@ +package allchive.server.core.error.exception; + + +import allchive.server.core.error.BaseErrorException; +import allchive.server.core.error.GlobalErrorCode; + +public class ExampleException extends BaseErrorException { + + public static final BaseErrorException EXCEPTION = new ExampleException(); + + private ExampleException() { + super(GlobalErrorCode.EXAMPLE_ERROR); + } +} diff --git a/Core/src/main/java/allchive/server/core/error/exception/ExpiredRefreshTokenException.java b/Core/src/main/java/allchive/server/core/error/exception/ExpiredRefreshTokenException.java new file mode 100644 index 00000000..beea48b6 --- /dev/null +++ b/Core/src/main/java/allchive/server/core/error/exception/ExpiredRefreshTokenException.java @@ -0,0 +1,14 @@ +package allchive.server.core.error.exception; + + +import allchive.server.core.error.BaseErrorException; +import allchive.server.core.error.GlobalErrorCode; + +public class ExpiredRefreshTokenException extends BaseErrorException { + + public static final BaseErrorException EXCEPTION = new ExpiredRefreshTokenException(); + + private ExpiredRefreshTokenException() { + super(GlobalErrorCode.EXPIRED_REFRESH_TOKEN); + } +} diff --git a/Core/src/main/java/allchive/server/core/error/exception/ExpiredTokenException.java b/Core/src/main/java/allchive/server/core/error/exception/ExpiredTokenException.java new file mode 100644 index 00000000..59c28d9e --- /dev/null +++ b/Core/src/main/java/allchive/server/core/error/exception/ExpiredTokenException.java @@ -0,0 +1,14 @@ +package allchive.server.core.error.exception; + + +import allchive.server.core.error.BaseErrorException; +import allchive.server.core.error.GlobalErrorCode; + +public class ExpiredTokenException extends BaseErrorException { + + public static final BaseErrorException EXCEPTION = new ExpiredTokenException(); + + private ExpiredTokenException() { + super(GlobalErrorCode.EXPIRED_TOKEN); + } +} diff --git a/Core/src/main/java/allchive/server/core/error/exception/InternalServerError.java b/Core/src/main/java/allchive/server/core/error/exception/InternalServerError.java new file mode 100644 index 00000000..3e4694ae --- /dev/null +++ b/Core/src/main/java/allchive/server/core/error/exception/InternalServerError.java @@ -0,0 +1,14 @@ +package allchive.server.core.error.exception; + + +import allchive.server.core.error.BaseErrorException; +import allchive.server.core.error.GlobalErrorCode; + +public class InternalServerError extends BaseErrorException { + + public static final BaseErrorException EXCEPTION = new InternalServerError(); + + private InternalServerError() { + super(GlobalErrorCode._INTERNAL_SERVER_ERROR); + } +} diff --git a/Core/src/main/java/allchive/server/core/error/exception/InvalidOauthProviderException.java b/Core/src/main/java/allchive/server/core/error/exception/InvalidOauthProviderException.java new file mode 100644 index 00000000..0be67540 --- /dev/null +++ b/Core/src/main/java/allchive/server/core/error/exception/InvalidOauthProviderException.java @@ -0,0 +1,14 @@ +package allchive.server.core.error.exception; + + +import allchive.server.core.error.BaseErrorException; +import allchive.server.core.error.GlobalErrorCode; + +public class InvalidOauthProviderException extends BaseErrorException { + + public static final BaseErrorException EXCEPTION = new InvalidOauthProviderException(); + + private InvalidOauthProviderException() { + super(GlobalErrorCode.INVALID_OAUTH_PROVIDER); + } +} diff --git a/Core/src/main/java/allchive/server/core/error/exception/InvalidTokenException.java b/Core/src/main/java/allchive/server/core/error/exception/InvalidTokenException.java new file mode 100644 index 00000000..88e9cf8c --- /dev/null +++ b/Core/src/main/java/allchive/server/core/error/exception/InvalidTokenException.java @@ -0,0 +1,14 @@ +package allchive.server.core.error.exception; + + +import allchive.server.core.error.BaseErrorException; +import allchive.server.core.error.GlobalErrorCode; + +public class InvalidTokenException extends BaseErrorException { + + public static final BaseErrorException EXCEPTION = new InvalidTokenException(); + + private InvalidTokenException() { + super(GlobalErrorCode.INVALID_TOKEN); + } +} diff --git a/Core/src/main/java/allchive/server/core/error/exception/OtherServerBadRequestException.java b/Core/src/main/java/allchive/server/core/error/exception/OtherServerBadRequestException.java new file mode 100644 index 00000000..83954612 --- /dev/null +++ b/Core/src/main/java/allchive/server/core/error/exception/OtherServerBadRequestException.java @@ -0,0 +1,14 @@ +package allchive.server.core.error.exception; + + +import allchive.server.core.error.BaseErrorException; +import allchive.server.core.error.GlobalErrorCode; + +public class OtherServerBadRequestException extends BaseErrorException { + + public static final BaseErrorException EXCEPTION = new OtherServerBadRequestException(); + + private OtherServerBadRequestException() { + super(GlobalErrorCode.OTHER_SERVER_BAD_REQUEST); + } +} diff --git a/Core/src/main/java/allchive/server/core/error/exception/OtherServerExpiredTokenException.java b/Core/src/main/java/allchive/server/core/error/exception/OtherServerExpiredTokenException.java new file mode 100644 index 00000000..c845115a --- /dev/null +++ b/Core/src/main/java/allchive/server/core/error/exception/OtherServerExpiredTokenException.java @@ -0,0 +1,14 @@ +package allchive.server.core.error.exception; + + +import allchive.server.core.error.BaseErrorException; +import allchive.server.core.error.GlobalErrorCode; + +public class OtherServerExpiredTokenException extends BaseErrorException { + + public static final BaseErrorException EXCEPTION = new OtherServerExpiredTokenException(); + + private OtherServerExpiredTokenException() { + super(GlobalErrorCode.OTHER_SERVER_EXPIRED_TOKEN); + } +} diff --git a/Core/src/main/java/allchive/server/core/error/exception/OtherServerForbiddenException.java b/Core/src/main/java/allchive/server/core/error/exception/OtherServerForbiddenException.java new file mode 100644 index 00000000..5cfd1123 --- /dev/null +++ b/Core/src/main/java/allchive/server/core/error/exception/OtherServerForbiddenException.java @@ -0,0 +1,14 @@ +package allchive.server.core.error.exception; + + +import allchive.server.core.error.BaseErrorException; +import allchive.server.core.error.GlobalErrorCode; + +public class OtherServerForbiddenException extends BaseErrorException { + + public static final BaseErrorException EXCEPTION = new OtherServerForbiddenException(); + + private OtherServerForbiddenException() { + super(GlobalErrorCode.OTHER_SERVER_FORBIDDEN); + } +} diff --git a/Core/src/main/java/allchive/server/core/error/exception/OtherServerNotFoundException.java b/Core/src/main/java/allchive/server/core/error/exception/OtherServerNotFoundException.java new file mode 100644 index 00000000..180b8a77 --- /dev/null +++ b/Core/src/main/java/allchive/server/core/error/exception/OtherServerNotFoundException.java @@ -0,0 +1,14 @@ +package allchive.server.core.error.exception; + + +import allchive.server.core.error.BaseErrorException; +import allchive.server.core.error.GlobalErrorCode; + +public class OtherServerNotFoundException extends BaseErrorException { + + public static final BaseErrorException EXCEPTION = new OtherServerNotFoundException(); + + private OtherServerNotFoundException() { + super(GlobalErrorCode.OTHER_SERVER_NOT_FOUND); + } +} diff --git a/Core/src/main/java/allchive/server/core/error/exception/OtherServerUnauthorizedException.java b/Core/src/main/java/allchive/server/core/error/exception/OtherServerUnauthorizedException.java new file mode 100644 index 00000000..8c10f6dc --- /dev/null +++ b/Core/src/main/java/allchive/server/core/error/exception/OtherServerUnauthorizedException.java @@ -0,0 +1,14 @@ +package allchive.server.core.error.exception; + + +import allchive.server.core.error.BaseErrorException; +import allchive.server.core.error.GlobalErrorCode; + +public class OtherServerUnauthorizedException extends BaseErrorException { + + public static final BaseErrorException EXCEPTION = new OtherServerUnauthorizedException(); + + private OtherServerUnauthorizedException() { + super(GlobalErrorCode.OTHER_SERVER_UNAUTHORIZED); + } +} diff --git a/Core/src/main/java/allchive/server/core/helper/SpringEnvironmentHelper.java b/Core/src/main/java/allchive/server/core/helper/SpringEnvironmentHelper.java index 3d93939e..9016edfd 100644 --- a/Core/src/main/java/allchive/server/core/helper/SpringEnvironmentHelper.java +++ b/Core/src/main/java/allchive/server/core/helper/SpringEnvironmentHelper.java @@ -1,5 +1,7 @@ package allchive.server.core.helper; +import static allchive.server.core.consts.AllchiveConst.DEV; +import static allchive.server.core.consts.AllchiveConst.PROD; import java.util.Arrays; import java.util.List; @@ -13,9 +15,6 @@ public class SpringEnvironmentHelper { private final Environment environment; - private final String PROD = "prod"; - private final String DEV = "dev"; - public Boolean isProdProfile() { String[] activeProfiles = environment.getActiveProfiles(); List<String> currentProfile = Arrays.stream(activeProfiles).toList(); diff --git a/Core/src/main/java/allchive/server/core/jwt/JwtOIDCProvider.java b/Core/src/main/java/allchive/server/core/jwt/JwtOIDCProvider.java new file mode 100644 index 00000000..ca3f5d8a --- /dev/null +++ b/Core/src/main/java/allchive/server/core/jwt/JwtOIDCProvider.java @@ -0,0 +1,83 @@ +package allchive.server.core.jwt; + +import static allchive.server.core.consts.AllchiveConst.KID; + +import allchive.server.core.dto.OIDCDecodePayload; +import allchive.server.core.error.exception.ExpiredTokenException; +import allchive.server.core.error.exception.InvalidTokenException; +import io.jsonwebtoken.*; +import java.math.BigInteger; +import java.security.Key; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.RSAPublicKeySpec; +import java.util.Base64; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +@RequiredArgsConstructor +@Component +@Slf4j +public class JwtOIDCProvider { + public String getKidFromUnsignedTokenHeader(String token, String iss, String aud) { + return (String) getUnsignedTokenClaims(token, iss, aud).getHeader().get(KID); + } + + private Jwt<Header, Claims> getUnsignedTokenClaims(String token, String iss, String aud) { + try { + return Jwts.parserBuilder() + .requireAudience(aud) + .requireIssuer(iss) + .build() + .parseClaimsJwt(getUnsignedToken(token)); + } catch (ExpiredJwtException e) { + throw ExpiredTokenException.EXCEPTION; + } catch (Exception e) { + log.error(e.toString()); + throw InvalidTokenException.EXCEPTION; + } + } + + private String getUnsignedToken(String token) { + String[] splitToken = token.split("\\."); + if (splitToken.length != 3) throw InvalidTokenException.EXCEPTION; + return splitToken[0] + "." + splitToken[1] + "."; + } + + public OIDCDecodePayload getOIDCTokenBody(String token, String modulus, String exponent) { + Claims body = getOIDCTokenJws(token, modulus, exponent).getBody(); + return new OIDCDecodePayload( + body.getIssuer(), + body.getAudience(), + body.getSubject(), + body.get("email", String.class)); + } + + public Jws<Claims> getOIDCTokenJws(String token, String modulus, String exponent) { + try { + return Jwts.parserBuilder() + .setSigningKey(getRSAPublicKey(modulus, exponent)) + .build() + .parseClaimsJws(token); + } catch (ExpiredJwtException e) { + throw ExpiredTokenException.EXCEPTION; + } catch (Exception e) { + log.error(e.toString()); + throw InvalidTokenException.EXCEPTION; + } + } + + private Key getRSAPublicKey(String modulus, String exponent) + throws NoSuchAlgorithmException, InvalidKeySpecException { + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + byte[] decodeN = Base64.getUrlDecoder().decode(modulus); + byte[] decodeE = Base64.getUrlDecoder().decode(exponent); + BigInteger n = new BigInteger(1, decodeN); + BigInteger e = new BigInteger(1, decodeE); + + RSAPublicKeySpec keySpec = new RSAPublicKeySpec(n, e); + return keyFactory.generatePublic(keySpec); + } +} diff --git a/Core/src/main/java/allchive/server/core/jwt/JwtProperties.java b/Core/src/main/java/allchive/server/core/jwt/JwtProperties.java new file mode 100644 index 00000000..284548b1 --- /dev/null +++ b/Core/src/main/java/allchive/server/core/jwt/JwtProperties.java @@ -0,0 +1,17 @@ +package allchive.server.core.jwt; + + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.ConstructorBinding; + +@Getter +@AllArgsConstructor +@ConstructorBinding +@ConfigurationProperties(prefix = "auth.jwt") +public class JwtProperties { + private String secretKey; + private Long accessExp; + private Long refreshExp; +} diff --git a/Core/src/main/java/allchive/server/core/jwt/JwtTokenProvider.java b/Core/src/main/java/allchive/server/core/jwt/JwtTokenProvider.java new file mode 100644 index 00000000..acde920c --- /dev/null +++ b/Core/src/main/java/allchive/server/core/jwt/JwtTokenProvider.java @@ -0,0 +1,123 @@ +package allchive.server.core.jwt; + +import static allchive.server.core.consts.AllchiveConst.*; + +import allchive.server.core.error.exception.ExpiredRefreshTokenException; +import allchive.server.core.error.exception.ExpiredTokenException; +import allchive.server.core.error.exception.InvalidTokenException; +import allchive.server.core.jwt.dto.AccessTokenInfo; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.ExpiredJwtException; +import io.jsonwebtoken.Jws; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.security.Keys; +import java.nio.charset.StandardCharsets; +import java.security.Key; +import java.util.Date; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class JwtTokenProvider { + + private final JwtProperties jwtProperties; + + private Jws<Claims> parse(String token) { + return Jwts.parserBuilder().setSigningKey(getSecretKey()).build().parseClaimsJws(token); + } + + private Jws<Claims> getJws(String token) { + try { + return parse(token); + } catch (ExpiredJwtException e) { + throw ExpiredTokenException.EXCEPTION; + } catch (Exception e) { + throw InvalidTokenException.EXCEPTION; + } + } + + private Key getSecretKey() { + return Keys.hmacShaKeyFor(jwtProperties.getSecretKey().getBytes(StandardCharsets.UTF_8)); + } + + private String buildAccessToken( + Long id, Date issuedAt, Date accessTokenExpiresIn, String role) { + final Key encodedKey = getSecretKey(); + return Jwts.builder() + .setIssuer(TOKEN_ISSUER) + .setIssuedAt(issuedAt) + .setSubject(id.toString()) + .claim(TOKEN_TYPE, ACCESS_TOKEN) + .claim(TOKEN_ROLE, role) + .setExpiration(accessTokenExpiresIn) + .signWith(encodedKey) + .compact(); + } + + private String buildRefreshToken(Long id, Date issuedAt, Date accessTokenExpiresIn) { + final Key encodedKey = getSecretKey(); + return Jwts.builder() + .setIssuer(TOKEN_ISSUER) + .setIssuedAt(issuedAt) + .setSubject(id.toString()) + .claim(TOKEN_TYPE, REFRESH_TOKEN) + .setExpiration(accessTokenExpiresIn) + .signWith(encodedKey) + .compact(); + } + + public String generateAccessToken(Long id, String role) { + final Date issuedAt = new Date(); + final Date accessTokenExpiresIn = + new Date(issuedAt.getTime() + jwtProperties.getAccessExp() * MILLI_TO_SECOND); + + return buildAccessToken(id, issuedAt, accessTokenExpiresIn, role); + } + + public String generateRefreshToken(Long id) { + final Date issuedAt = new Date(); + final Date refreshTokenExpiresIn = + new Date(issuedAt.getTime() + jwtProperties.getRefreshExp() * MILLI_TO_SECOND); + return buildRefreshToken(id, issuedAt, refreshTokenExpiresIn); + } + + public boolean isAccessToken(String token) { + return getJws(token).getBody().get(TOKEN_TYPE).equals(ACCESS_TOKEN); + } + + public boolean isRefreshToken(String token) { + return getJws(token).getBody().get(TOKEN_TYPE).equals(REFRESH_TOKEN); + } + + public AccessTokenInfo parseAccessToken(String token) { + if (isAccessToken(token)) { + Claims claims = getJws(token).getBody(); + return AccessTokenInfo.builder() + .userId(Long.parseLong(claims.getSubject())) + .role((String) claims.get(TOKEN_ROLE)) + .build(); + } + throw InvalidTokenException.EXCEPTION; + } + + public Long parseRefreshToken(String token) { + try { + if (isRefreshToken(token)) { + Claims claims = parse(token).getBody(); + return Long.parseLong(claims.getSubject()); + } + } catch (ExpiredJwtException e) { + throw ExpiredRefreshTokenException.EXCEPTION; + } + throw InvalidTokenException.EXCEPTION; + } + + public Long getRefreshTokenTTLSecond() { + return jwtProperties.getRefreshExp(); + } + + public Long getAccessTokenTTLSecond() { + return jwtProperties.getAccessExp(); + } +} diff --git a/Core/src/main/java/allchive/server/core/jwt/dto/AccessTokenInfo.java b/Core/src/main/java/allchive/server/core/jwt/dto/AccessTokenInfo.java new file mode 100644 index 00000000..270d1efa --- /dev/null +++ b/Core/src/main/java/allchive/server/core/jwt/dto/AccessTokenInfo.java @@ -0,0 +1,14 @@ +package allchive.server.core.jwt.dto; + + +import lombok.Builder; +import lombok.Getter; +import lombok.ToString; + +@Getter +@Builder +@ToString +public class AccessTokenInfo { + private final Long userId; + private final String role; +} diff --git a/Core/src/main/java/allchive/server/core/properties/KakaoOAuthProperties.java b/Core/src/main/java/allchive/server/core/properties/KakaoOAuthProperties.java new file mode 100644 index 00000000..42d76217 --- /dev/null +++ b/Core/src/main/java/allchive/server/core/properties/KakaoOAuthProperties.java @@ -0,0 +1,20 @@ +package allchive.server.core.properties; + + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.ConstructorBinding; + +@Getter +@AllArgsConstructor +@ConstructorBinding +@ConfigurationProperties("oauth.kakao") +public class KakaoOAuthProperties { + private String baseUrl; + private String clientId; + private String clientSecret; + private String redirectUrl; + private String appId; + private String adminKey; +} diff --git a/Core/src/main/resources/application-core.yml b/Core/src/main/resources/application-core.yml index e69de29b..355a7d42 100644 --- a/Core/src/main/resources/application-core.yml +++ b/Core/src/main/resources/application-core.yml @@ -0,0 +1,16 @@ + +# jwt 설정 +auth: + jwt: + secret-key: ${JWT_SECRET_KEY:testkeytestkeytestkeytestkeytestkeytestkeytestkeytestkeytestkey} + access-exp: ${JWT_ACCESS_EXP:3600} + refresh-exp: ${JWT_REFRESH_EXP:3600} + +oauth: + kakao: + base-url: ${KAKAO_BASE_URL} + client-id: ${KAKAO_CLIENT} + client-secret: ${KAKAO_SECRET} + redirect-url: ${KAKAO_REDIRECT} + app-id: ${KAKAO_APP_ID} + admin-key: ${KAKAO_ADMIN_KEY} \ No newline at end of file diff --git a/Domain/src/main/java/allchive/server/domain/DomainPackageLocation.java b/Domain/src/main/java/allchive/server/domain/DomainPackageLocation.java new file mode 100644 index 00000000..eafe5767 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/DomainPackageLocation.java @@ -0,0 +1,3 @@ +package allchive.server.domain; + +public interface DomainPackageLocation {} diff --git a/Domain/src/main/java/allchive/server/domain/config/JpaConfig.java b/Domain/src/main/java/allchive/server/domain/config/JpaConfig.java new file mode 100644 index 00000000..9eb8429d --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/config/JpaConfig.java @@ -0,0 +1,14 @@ +package allchive.server.domain.config; + + +import allchive.server.domain.DomainPackageLocation; +import org.springframework.boot.autoconfigure.domain.EntityScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; + +@Configuration +@EnableJpaAuditing +@EntityScan(basePackageClasses = DomainPackageLocation.class) +@EnableJpaRepositories(basePackageClasses = DomainPackageLocation.class) +public class JpaConfig {} diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/RefreshTokenAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/RefreshTokenAdaptor.java new file mode 100644 index 00000000..2e7d2ed9 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/RefreshTokenAdaptor.java @@ -0,0 +1,17 @@ +package allchive.server.domain.domains.user.adaptor; + + +import allchive.server.core.annotation.Adaptor; +import allchive.server.domain.domains.user.domain.RefreshTokenEntity; +import allchive.server.domain.domains.user.repository.RefreshTokenRepository; +import lombok.RequiredArgsConstructor; + +@Adaptor +@RequiredArgsConstructor +public class RefreshTokenAdaptor { + private final RefreshTokenRepository refreshTokenRepository; + + public void save(RefreshTokenEntity newRefreshTokenEntityEntity) { + refreshTokenRepository.save(newRefreshTokenEntityEntity); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/UserAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/UserAdaptor.java index b137276b..e1411274 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/UserAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/UserAdaptor.java @@ -2,8 +2,28 @@ import allchive.server.core.annotation.Adaptor; +import allchive.server.domain.domains.user.domain.User; +import allchive.server.domain.domains.user.domain.enums.OauthInfo; +import allchive.server.domain.domains.user.exception.exceptions.UserNotFoundException; +import allchive.server.domain.domains.user.repository.UserRepository; import lombok.RequiredArgsConstructor; @Adaptor @RequiredArgsConstructor -public class UserAdaptor {} +public class UserAdaptor { + private final UserRepository userRepository; + + public Boolean exist(OauthInfo oauthInfo) { + return userRepository.findByOauthInfo(oauthInfo).isPresent(); + } + + public User queryUserByOauthInfo(OauthInfo oauthInfo) { + return userRepository + .findByOauthInfo(oauthInfo) + .orElseThrow(() -> UserNotFoundException.EXCEPTION); + } + + public void save(User user) { + userRepository.save(user); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/domain/RefreshTokenEntity.java b/Domain/src/main/java/allchive/server/domain/domains/user/domain/RefreshTokenEntity.java new file mode 100644 index 00000000..f47ab20b --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/user/domain/RefreshTokenEntity.java @@ -0,0 +1,29 @@ +package allchive.server.domain.domains.user.domain; + + +import lombok.Builder; +import lombok.Getter; +import org.springframework.data.annotation.Id; +import org.springframework.data.redis.core.RedisHash; +import org.springframework.data.redis.core.TimeToLive; +import org.springframework.data.redis.core.index.Indexed; + +@RedisHash(value = "refreshToken") +@Getter +public class RefreshTokenEntity { + @Id private Long id; + @Indexed private String refreshToken; + @TimeToLive // TTL + private Long ttl; + + @Builder + private RefreshTokenEntity(Long id, String refreshToken, Long ttl) { + this.id = id; + this.refreshToken = refreshToken; + this.ttl = ttl; + } + + public static RefreshTokenEntity of(Long id, String refreshToken, Long ttl) { + return RefreshTokenEntity.builder().id(id).refreshToken(refreshToken).ttl(ttl).build(); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/domain/User.java b/Domain/src/main/java/allchive/server/domain/domains/user/domain/User.java index ec9c2696..c04cac49 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/domain/User.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/domain/User.java @@ -3,11 +3,14 @@ import allchive.server.domain.common.model.BaseTimeEntity; import allchive.server.domain.domains.user.domain.enums.OauthInfo; +import allchive.server.domain.domains.user.domain.enums.UserRole; import allchive.server.domain.domains.user.domain.enums.UserState; +import allchive.server.domain.domains.user.exception.exceptions.ForbiddenUserException; import java.time.LocalDateTime; import javax.persistence.*; import javax.validation.constraints.NotNull; import lombok.AccessLevel; +import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; @@ -21,6 +24,7 @@ public class User extends BaseTimeEntity { private Long id; @NotNull private String nickname; + private String profileImgUrl; @Embedded private OauthInfo oauthInfo; @@ -31,4 +35,30 @@ public class User extends BaseTimeEntity { @Enumerated(EnumType.STRING) private UserState userState = UserState.NORMAL; + + @Enumerated(EnumType.STRING) + private UserRole userRole = UserRole.USER; + + @Builder + private User(String nickname, String profileImgUrl, OauthInfo oauthInfo) { + this.nickname = nickname; + this.profileImgUrl = profileImgUrl; + this.oauthInfo = oauthInfo; + this.lastLoginAt = LocalDateTime.now(); + } + + public static User of(String nickname, String profileImgUrl, OauthInfo oauthInfo) { + return User.builder() + .nickname(nickname) + .profileImgUrl(profileImgUrl) + .oauthInfo(oauthInfo) + .build(); + } + + public void login() { + if (!UserState.NORMAL.equals(this.userState)) { + throw ForbiddenUserException.EXCEPTION; + } + lastLoginAt = LocalDateTime.now(); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/domain/enums/OauthProvider.java b/Domain/src/main/java/allchive/server/domain/domains/user/domain/enums/OauthProvider.java index f16b525e..c72c330b 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/domain/enums/OauthProvider.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/domain/enums/OauthProvider.java @@ -1,6 +1,9 @@ package allchive.server.domain.domains.user.domain.enums; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import java.util.stream.Stream; import lombok.AllArgsConstructor; import lombok.Getter; @@ -9,5 +12,13 @@ public enum OauthProvider { KAKAO("KAKAO"); - private String value; + @JsonValue private String value; + + @JsonCreator + public static OauthProvider parsing(String inputValue) { + return Stream.of(OauthProvider.values()) + .filter(category -> category.getValue().equals(inputValue)) + .findFirst() + .orElse(null); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/domain/enums/UserRole.java b/Domain/src/main/java/allchive/server/domain/domains/user/domain/enums/UserRole.java new file mode 100644 index 00000000..7612efa4 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/user/domain/enums/UserRole.java @@ -0,0 +1,14 @@ +package allchive.server.domain.domains.user.domain.enums; + + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum UserRole { + USER("USER"), + ADMIN("ADMIN"); + + private String value; +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/exception/UserErrorCode.java b/Domain/src/main/java/allchive/server/domain/domains/user/exception/UserErrorCode.java index f62c1091..7ad3dc1b 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/exception/UserErrorCode.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/exception/UserErrorCode.java @@ -1,5 +1,7 @@ package allchive.server.domain.domains.user.exception; +import static org.springframework.http.HttpStatus.BAD_REQUEST; +import static org.springframework.http.HttpStatus.NOT_FOUND; import allchive.server.core.dto.ErrorReason; import allchive.server.core.error.BaseErrorCode; @@ -10,7 +12,9 @@ @Getter @AllArgsConstructor public enum UserErrorCode implements BaseErrorCode { - ; + FORBIDDEN_USER(BAD_REQUEST, "USER_400_1", "접근 제한된 유저입니다."), + USER_ALREADY_SIGNUP(BAD_REQUEST, "USER_400_2", "이미 회원가입한 유저입니다."), + USER_NOT_FOUND(NOT_FOUND, "USER_404_1", "유저 정보를 찾을 수 없습니다."); private HttpStatus status; private String code; private String reason; diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/exception/exceptions/AlreadySignUpUserException.java b/Domain/src/main/java/allchive/server/domain/domains/user/exception/exceptions/AlreadySignUpUserException.java new file mode 100644 index 00000000..93be5de2 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/user/exception/exceptions/AlreadySignUpUserException.java @@ -0,0 +1,14 @@ +package allchive.server.domain.domains.user.exception.exceptions; + + +import allchive.server.core.error.BaseErrorException; +import allchive.server.domain.domains.user.exception.UserErrorCode; + +public class AlreadySignUpUserException extends BaseErrorException { + + public static final BaseErrorException EXCEPTION = new AlreadySignUpUserException(); + + private AlreadySignUpUserException() { + super(UserErrorCode.USER_ALREADY_SIGNUP); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/exception/exceptions/ForbiddenUserException.java b/Domain/src/main/java/allchive/server/domain/domains/user/exception/exceptions/ForbiddenUserException.java new file mode 100644 index 00000000..556452f5 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/user/exception/exceptions/ForbiddenUserException.java @@ -0,0 +1,14 @@ +package allchive.server.domain.domains.user.exception.exceptions; + + +import allchive.server.core.error.BaseErrorException; +import allchive.server.domain.domains.user.exception.UserErrorCode; + +public class ForbiddenUserException extends BaseErrorException { + + public static final BaseErrorException EXCEPTION = new ForbiddenUserException(); + + private ForbiddenUserException() { + super(UserErrorCode.FORBIDDEN_USER); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/exception/exceptions/UserNotFoundException.java b/Domain/src/main/java/allchive/server/domain/domains/user/exception/exceptions/UserNotFoundException.java new file mode 100644 index 00000000..86bdf763 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/user/exception/exceptions/UserNotFoundException.java @@ -0,0 +1,14 @@ +package allchive.server.domain.domains.user.exception.exceptions; + + +import allchive.server.core.error.BaseErrorException; +import allchive.server.domain.domains.user.exception.UserErrorCode; + +public class UserNotFoundException extends BaseErrorException { + + public static final BaseErrorException EXCEPTION = new UserNotFoundException(); + + private UserNotFoundException() { + super(UserErrorCode.USER_NOT_FOUND); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/repository/RefreshTokenRepository.java b/Domain/src/main/java/allchive/server/domain/domains/user/repository/RefreshTokenRepository.java new file mode 100644 index 00000000..5cfe4879 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/user/repository/RefreshTokenRepository.java @@ -0,0 +1,10 @@ +package allchive.server.domain.domains.user.repository; + + +import allchive.server.domain.domains.user.domain.RefreshTokenEntity; +import java.util.Optional; +import org.springframework.data.repository.CrudRepository; + +public interface RefreshTokenRepository extends CrudRepository<RefreshTokenEntity, String> { + Optional<RefreshTokenEntity> findByRefreshToken(String refreshToken); +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/repository/UserRepository.java b/Domain/src/main/java/allchive/server/domain/domains/user/repository/UserRepository.java index 97eed6e2..d38cc4c0 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/repository/UserRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/repository/UserRepository.java @@ -2,6 +2,10 @@ import allchive.server.domain.domains.user.domain.User; +import allchive.server.domain.domains.user.domain.enums.OauthInfo; +import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; -public interface UserRepository extends JpaRepository<User, Long> {} +public interface UserRepository extends JpaRepository<User, Long> { + Optional<User> findByOauthInfo(OauthInfo oauthInfo); +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/service/UserDomainService.java b/Domain/src/main/java/allchive/server/domain/domains/user/service/UserDomainService.java new file mode 100644 index 00000000..f9e8afec --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/user/service/UserDomainService.java @@ -0,0 +1,43 @@ +package allchive.server.domain.domains.user.service; + + +import allchive.server.core.annotation.DomainService; +import allchive.server.domain.domains.user.adaptor.UserAdaptor; +import allchive.server.domain.domains.user.domain.User; +import allchive.server.domain.domains.user.domain.enums.OauthInfo; +import allchive.server.domain.domains.user.exception.exceptions.AlreadySignUpUserException; +import lombok.RequiredArgsConstructor; +import org.springframework.transaction.annotation.Transactional; + +@DomainService +@RequiredArgsConstructor +public class UserDomainService { + private final UserAdaptor userAdaptor; + + public Boolean checkUserCanLogin(OauthInfo oauthInfo) { + return userAdaptor.exist(oauthInfo); + } + + @Transactional + public User registerUser(String nickname, String profileImgUrl, OauthInfo oauthInfo) { + validUserCanRegister(oauthInfo); + final User newUser = User.of(nickname, profileImgUrl, oauthInfo); + userAdaptor.save(newUser); + return newUser; + } + + @Transactional + public User loginUser(OauthInfo oauthInfo) { + User user = userAdaptor.queryUserByOauthInfo(oauthInfo); + user.login(); + return user; + } + + public Boolean checkUserCanRegister(OauthInfo oauthInfo) { + return !userAdaptor.exist(oauthInfo); + } + + public void validUserCanRegister(OauthInfo oauthInfo) { + if (!checkUserCanRegister(oauthInfo)) throw AlreadySignUpUserException.EXCEPTION; + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/service/UserService.java b/Domain/src/main/java/allchive/server/domain/domains/user/service/UserService.java deleted file mode 100644 index 6f2ed7ca..00000000 --- a/Domain/src/main/java/allchive/server/domain/domains/user/service/UserService.java +++ /dev/null @@ -1,7 +0,0 @@ -package allchive.server.domain.domains.user.service; - - -import allchive.server.core.annotation.DomainService; - -@DomainService -public class UserService {} diff --git a/Infrastructure/build.gradle b/Infrastructure/build.gradle index 922e6549..e46ce9b5 100644 --- a/Infrastructure/build.gradle +++ b/Infrastructure/build.gradle @@ -2,9 +2,10 @@ bootJar { enabled = false } jar { enabled = true } dependencies { - api('com.slack.api:slack-api-client:1.28.0') api 'org.springframework.boot:spring-boot-starter-data-redis' api 'org.redisson:redisson:3.19.0' + api 'io.github.openfeign:feign-httpclient:12.1' + api 'org.springframework.cloud:spring-cloud-starter-openfeign:3.1.4' testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.2' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.2' diff --git a/Infrastructure/src/main/java/allchive/server/infrastructure/feign/config/OpenFeignConfig.java b/Infrastructure/src/main/java/allchive/server/infrastructure/feign/config/OpenFeignConfig.java new file mode 100644 index 00000000..e5ed9cbf --- /dev/null +++ b/Infrastructure/src/main/java/allchive/server/infrastructure/feign/config/OpenFeignConfig.java @@ -0,0 +1,24 @@ +package allchive.server.infrastructure.feign.config; + + +import allchive.server.infrastructure.oauth.kakao.BaseFeignClientClass; +import feign.Logger; +import feign.Retryer; +import java.util.concurrent.TimeUnit; +import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +@EnableFeignClients(basePackageClasses = BaseFeignClientClass.class) +public class OpenFeignConfig { + @Bean + Retryer.Default retryer() { + return new Retryer.Default(100L, TimeUnit.SECONDS.toMillis(3L), 5); + } + + @Bean + Logger.Level feignLoggerLevel() { + return Logger.Level.FULL; + } +} diff --git a/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/BaseFeignClientClass.java b/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/BaseFeignClientClass.java new file mode 100644 index 00000000..d5546903 --- /dev/null +++ b/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/BaseFeignClientClass.java @@ -0,0 +1,3 @@ +package allchive.server.infrastructure.oauth.kakao; + +public interface BaseFeignClientClass {} diff --git a/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/client/KakaoInfoClient.java b/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/client/KakaoInfoClient.java new file mode 100644 index 00000000..bc649642 --- /dev/null +++ b/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/client/KakaoInfoClient.java @@ -0,0 +1,18 @@ +package allchive.server.infrastructure.oauth.kakao.client; + + +import allchive.server.infrastructure.oauth.kakao.config.KakaoInfoConfig; +import allchive.server.infrastructure.oauth.kakao.dto.KakaoInformationResponse; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestHeader; + +@FeignClient( + name = "KakaoInfoClient", + url = "https://kapi.kakao.com", + configuration = KakaoInfoConfig.class) +public interface KakaoInfoClient { + + @GetMapping("/v2/user/me") + KakaoInformationResponse kakaoUserInfo(@RequestHeader("Authorization") String accessToken); +} diff --git a/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/client/KakaoOauthClient.java b/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/client/KakaoOauthClient.java new file mode 100644 index 00000000..3d9c1b95 --- /dev/null +++ b/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/client/KakaoOauthClient.java @@ -0,0 +1,30 @@ +package allchive.server.infrastructure.oauth.kakao.client; + + +import allchive.server.infrastructure.oauth.kakao.config.KakaoKauthConfig; +import allchive.server.infrastructure.oauth.kakao.dto.KakaoTokenResponse; +import allchive.server.infrastructure.oauth.kakao.dto.OIDCPublicKeysResponse; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; + +@FeignClient( + name = "KakaoAuthClient", + url = "https://kauth.kakao.com", + configuration = KakaoKauthConfig.class) +public interface KakaoOauthClient { + + @PostMapping( + "/oauth/token?grant_type=authorization_code&client_id={CLIENT_ID}&redirect_uri={REDIRECT_URI}&code={CODE}&client_secret={CLIENT_SECRET}") + KakaoTokenResponse kakaoAuth( + @PathVariable("CLIENT_ID") String clientId, + @PathVariable("REDIRECT_URI") String redirectUri, + @PathVariable("CODE") String code, + @PathVariable("CLIENT_SECRET") String client_secret); + + @Cacheable(cacheNames = "KakaoOIDC", cacheManager = "oidcCacheManager") + @GetMapping("/.well-known/jwks.json") + OIDCPublicKeysResponse getKakaoOIDCOpenKeys(); +} diff --git a/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/config/KakaoInfoConfig.java b/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/config/KakaoInfoConfig.java new file mode 100644 index 00000000..8a1c68fc --- /dev/null +++ b/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/config/KakaoInfoConfig.java @@ -0,0 +1,23 @@ +package allchive.server.infrastructure.oauth.kakao.config; + + +import feign.codec.Encoder; +import feign.codec.ErrorDecoder; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Import; + +@Import(KakaoInfoErrorDecoder.class) +public class KakaoInfoConfig { + + @Bean + @ConditionalOnMissingBean(value = ErrorDecoder.class) + public KakaoInfoErrorDecoder commonFeignErrorDecoder() { + return new KakaoInfoErrorDecoder(); + } + + @Bean + Encoder formEncoder() { + return new feign.form.FormEncoder(); + } +} diff --git a/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/config/KakaoInfoErrorDecoder.java b/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/config/KakaoInfoErrorDecoder.java new file mode 100644 index 00000000..84035733 --- /dev/null +++ b/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/config/KakaoInfoErrorDecoder.java @@ -0,0 +1,31 @@ +package allchive.server.infrastructure.oauth.kakao.config; + + +import allchive.server.core.error.exception.OtherServerBadRequestException; +import allchive.server.core.error.exception.OtherServerExpiredTokenException; +import allchive.server.core.error.exception.OtherServerForbiddenException; +import allchive.server.core.error.exception.OtherServerUnauthorizedException; +import feign.FeignException; +import feign.Response; +import feign.codec.ErrorDecoder; + +public class KakaoInfoErrorDecoder implements ErrorDecoder { + + @Override + public Exception decode(String methodKey, Response response) { + if (response.status() >= 400) { + switch (response.status()) { + case 401: + throw OtherServerUnauthorizedException.EXCEPTION; + case 403: + throw OtherServerForbiddenException.EXCEPTION; + case 419: + throw OtherServerExpiredTokenException.EXCEPTION; + default: + throw OtherServerBadRequestException.EXCEPTION; + } + } + + return FeignException.errorStatus(methodKey, response); + } +} diff --git a/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/config/KakaoKauthConfig.java b/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/config/KakaoKauthConfig.java new file mode 100644 index 00000000..4f8d093e --- /dev/null +++ b/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/config/KakaoKauthConfig.java @@ -0,0 +1,23 @@ +package allchive.server.infrastructure.oauth.kakao.config; + + +import feign.codec.Encoder; +import feign.codec.ErrorDecoder; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Import; + +@Import(KauthErrorDecoder.class) +public class KakaoKauthConfig { + + @Bean + @ConditionalOnMissingBean(value = ErrorDecoder.class) + public KauthErrorDecoder commonFeignErrorDecoder() { + return new KauthErrorDecoder(); + } + + @Bean + Encoder formEncoder() { + return new feign.form.FormEncoder(); + } +} diff --git a/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/config/KauthErrorDecoder.java b/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/config/KauthErrorDecoder.java new file mode 100644 index 00000000..adea2f7c --- /dev/null +++ b/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/config/KauthErrorDecoder.java @@ -0,0 +1,23 @@ +package allchive.server.infrastructure.oauth.kakao.config; + + +import allchive.server.infrastructure.oauth.kakao.dto.KakaoKauthErrorResponse; +import allchive.server.infrastructure.oauth.kakao.exception.KakaoKauthErrorCode; +import feign.Response; +import feign.codec.ErrorDecoder; + +public class KauthErrorDecoder implements ErrorDecoder { + @Override + public Exception decode(String methodKey, Response response) { + KakaoKauthErrorResponse body = KakaoKauthErrorResponse.from(response); + + try { + KakaoKauthErrorCode kakaoKauthErrorCode = + KakaoKauthErrorCode.valueOf(body.getErrorCode()); + throw kakaoKauthErrorCode.getDynamicException(); + } catch (IllegalArgumentException e) { + KakaoKauthErrorCode koeInvalidRequest = KakaoKauthErrorCode.KOE_INVALID_REQUEST; + throw koeInvalidRequest.getDynamicException(); + } + } +} diff --git a/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/dto/KakaoInformationResponse.java b/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/dto/KakaoInformationResponse.java new file mode 100644 index 00000000..ff4a9e9b --- /dev/null +++ b/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/dto/KakaoInformationResponse.java @@ -0,0 +1,67 @@ +package allchive.server.infrastructure.oauth.kakao.dto; + + +import com.fasterxml.jackson.databind.PropertyNamingStrategies.SnakeCaseStrategy; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +@JsonNaming(SnakeCaseStrategy.class) +public class KakaoInformationResponse { + + private Properties properties; + private String id; + + private KakaoAccount kakaoAccount; + + @Getter + @NoArgsConstructor + @JsonNaming(SnakeCaseStrategy.class) + public static class Properties { + private String nickname; + } + + @Getter + @NoArgsConstructor + @JsonNaming(SnakeCaseStrategy.class) + public static class KakaoAccount { + + private Profile profile; + private String email; + private String phoneNumber; + private String name; + + @Getter + @NoArgsConstructor + @JsonNaming(SnakeCaseStrategy.class) + public static class Profile { + private String profileImageUrl; + } + + public String getProfileImageUrl() { + return profile.getProfileImageUrl(); + } + } + + public String getId() { + return id; + } + + public String getEmail() { + return kakaoAccount.getEmail(); + } + + public String getPhoneNumber() { + return kakaoAccount.getPhoneNumber(); + } + + public String getName() { + return kakaoAccount.getName() != null ? kakaoAccount.getName() : properties.getNickname(); + } + + public String getProfileUrl() { + return kakaoAccount.getProfileImageUrl(); + } +} diff --git a/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/dto/KakaoKauthErrorResponse.java b/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/dto/KakaoKauthErrorResponse.java new file mode 100644 index 00000000..e45a713b --- /dev/null +++ b/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/dto/KakaoKauthErrorResponse.java @@ -0,0 +1,30 @@ +package allchive.server.infrastructure.oauth.kakao.dto; + + +import allchive.server.core.error.exception.InternalServerError; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.PropertyNamingStrategies.SnakeCaseStrategy; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import feign.Response; +import java.io.IOException; +import java.io.InputStream; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +@JsonNaming(SnakeCaseStrategy.class) +public class KakaoKauthErrorResponse { + private String error; + private String errorCode; + private String errorDescription; + + public static KakaoKauthErrorResponse from(Response response) { + try (InputStream bodyIs = response.body().asInputStream()) { + ObjectMapper mapper = new ObjectMapper(); + return mapper.readValue(bodyIs, KakaoKauthErrorResponse.class); + } catch (IOException e) { + throw InternalServerError.EXCEPTION; + } + } +} diff --git a/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/dto/KakaoTokenResponse.java b/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/dto/KakaoTokenResponse.java new file mode 100644 index 00000000..67720997 --- /dev/null +++ b/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/dto/KakaoTokenResponse.java @@ -0,0 +1,16 @@ +package allchive.server.infrastructure.oauth.kakao.dto; + + +import com.fasterxml.jackson.databind.PropertyNamingStrategies.SnakeCaseStrategy; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +@JsonNaming(SnakeCaseStrategy.class) +public class KakaoTokenResponse { + private String accessToken; + private String refreshToken; + private String idToken; +} diff --git a/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/dto/OIDCPublicKeyDto.java b/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/dto/OIDCPublicKeyDto.java new file mode 100644 index 00000000..1d877318 --- /dev/null +++ b/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/dto/OIDCPublicKeyDto.java @@ -0,0 +1,15 @@ +package allchive.server.infrastructure.oauth.kakao.dto; + + +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +public class OIDCPublicKeyDto { + private String kid; + private String alg; + private String use; + private String n; + private String e; +} diff --git a/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/dto/OIDCPublicKeysResponse.java b/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/dto/OIDCPublicKeysResponse.java new file mode 100644 index 00000000..44327ee5 --- /dev/null +++ b/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/dto/OIDCPublicKeysResponse.java @@ -0,0 +1,12 @@ +package allchive.server.infrastructure.oauth.kakao.dto; + + +import java.util.List; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +public class OIDCPublicKeysResponse { + List<OIDCPublicKeyDto> keys; +} diff --git a/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/exception/KakaoKauthErrorCode.java b/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/exception/KakaoKauthErrorCode.java new file mode 100644 index 00000000..383cebd7 --- /dev/null +++ b/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/exception/KakaoKauthErrorCode.java @@ -0,0 +1,56 @@ +package allchive.server.infrastructure.oauth.kakao.exception; + +import static allchive.server.core.consts.AllchiveConst.BAD_REQUEST; + +import allchive.server.core.dto.ErrorReason; +import allchive.server.core.error.BaseDynamicException; +import allchive.server.core.error.BaseErrorCode; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum KakaoKauthErrorCode implements BaseErrorCode { + KOE101(BAD_REQUEST, "KAKAO_KOE101", "invalid_client", "잘못된 앱 키 타입을 사용하거나 앱 키에 오타가 있을 경우"), + KOE009(BAD_REQUEST, "KAKAO_KOE009", "misconfigured", "등록되지 않은 플랫폼에서 액세스 토큰을 요청 하는 경우"), + KOE010( + BAD_REQUEST, + "KAKAO_KOE101", + "invalid_client", + "클라이언트 시크릿(Client secret) 기능을 사용하는 앱에서 토큰 요청 시 client_secret 값을 전달하지 않거나 정확하지 않은 값을 전달하는 경우"), + KOE303( + BAD_REQUEST, + "KAKAO_KOE303", + "invalid_grant", + "인가 코드 요청 시 사용한 redirect_uri와 액세스 토큰 요청 시 사용한 redirect_uri가 다른 경우"), + KOE319(BAD_REQUEST, "KAKAO_KOE319", "invalid_grant", "토큰 갱신 요청 시 리프레시 토큰을 전달하지 않는 경우"), + KOE320( + BAD_REQUEST, + "KAKAO_KOE320", + "invalid_grant", + "동일한 인가 코드를 두 번 이상 사용하거나, 이미 만료된 인가 코드를 사용한 경우, 혹은 인가 코드를 찾을 수 없는 경우"), + KOE322( + BAD_REQUEST, + "KAKAO_KOE322", + "invalid_grant", + "refresh_token을 찾을 수 없거나 이미 만료된 리프레시 토큰을 사용한 경우"), + KOE_INVALID_REQUEST(BAD_REQUEST, "KAKAO_KOE_INVALID_REQUEST", "invalid_request", "잘못된 요청인 경우"), + KOE400(BAD_REQUEST, "KAKAO_KOE400", "invalid_token", "ID 토큰 값이 전달되지 않았거나 올바른 형식이 아닌 ID 토큰인 경우"), + KOE401(BAD_REQUEST, "KAKAO_KOE401", "invalid_token", "ID 토큰을 발급한 인증 기관(iss)이 카카오 인증 서버"), + KOE402(BAD_REQUEST, "KAKAO_KOE402", "invalid_token", "서명이 올바르지 않아 유효한 ID 토큰이 아닌 경우"), + KOE403(BAD_REQUEST, "KAKAO_KOE403", "invalid_token", "만료된 ID 토큰인 경우"); + + private Integer status; + private String errorCode; + private String error; + private String reason; + + public BaseDynamicException getDynamicException() { + return new BaseDynamicException(status, errorCode, reason); + } + + @Override + public ErrorReason getErrorReason() { + return ErrorReason.builder().status(status).code(errorCode).reason(reason).build(); + } +} diff --git a/Infrastructure/src/main/java/allchive/server/infrastructure/config/redis/RedisCacheConfig.java b/Infrastructure/src/main/java/allchive/server/infrastructure/redis/config/RedisCacheConfig.java similarity index 64% rename from Infrastructure/src/main/java/allchive/server/infrastructure/config/redis/RedisCacheConfig.java rename to Infrastructure/src/main/java/allchive/server/infrastructure/redis/config/RedisCacheConfig.java index c4ac9bc5..51d3a696 100644 --- a/Infrastructure/src/main/java/allchive/server/infrastructure/config/redis/RedisCacheConfig.java +++ b/Infrastructure/src/main/java/allchive/server/infrastructure/redis/config/RedisCacheConfig.java @@ -1,4 +1,4 @@ -package allchive.server.infrastructure.config.redis; +package allchive.server.infrastructure.redis.config; import java.time.Duration; @@ -34,4 +34,21 @@ public CacheManager redisCacheManager(RedisConnectionFactory cf) { .cacheDefaults(redisCacheConfiguration) .build(); } + + @Bean + public CacheManager oidcCacheManager(RedisConnectionFactory cf) { + RedisCacheConfiguration redisCacheConfiguration = + RedisCacheConfiguration.defaultCacheConfig() + .serializeKeysWith( + RedisSerializationContext.SerializationPair.fromSerializer( + new StringRedisSerializer())) + .serializeValuesWith( + RedisSerializationContext.SerializationPair.fromSerializer( + new GenericJackson2JsonRedisSerializer())) + .entryTtl(Duration.ofDays(7L)); + + return RedisCacheManager.RedisCacheManagerBuilder.fromConnectionFactory(cf) + .cacheDefaults(redisCacheConfiguration) + .build(); + } } diff --git a/Infrastructure/src/main/java/allchive/server/infrastructure/config/redis/RedisConfig.java b/Infrastructure/src/main/java/allchive/server/infrastructure/redis/config/RedisConfig.java similarity index 89% rename from Infrastructure/src/main/java/allchive/server/infrastructure/config/redis/RedisConfig.java rename to Infrastructure/src/main/java/allchive/server/infrastructure/redis/config/RedisConfig.java index 02baaecb..be0883a7 100644 --- a/Infrastructure/src/main/java/allchive/server/infrastructure/config/redis/RedisConfig.java +++ b/Infrastructure/src/main/java/allchive/server/infrastructure/redis/config/RedisConfig.java @@ -1,4 +1,4 @@ -package allchive.server.infrastructure.config.redis; +package allchive.server.infrastructure.redis.config; import java.time.Duration; @@ -32,8 +32,8 @@ public class RedisConfig { public RedisConnectionFactory redisConnectionFactory() { RedisStandaloneConfiguration redisConfig = new RedisStandaloneConfiguration(host, port); -// if (redisPassword != null && !redisPassword.isBlank()) -// redisConfig.setPassword(redisPassword); + // if (redisPassword != null && !redisPassword.isBlank()) + // redisConfig.setPassword(redisPassword); LettuceClientConfiguration clientConfig = LettuceClientConfiguration.builder() diff --git a/Infrastructure/src/main/java/allchive/server/infrastructure/config/redis/RedissonConfig.java b/Infrastructure/src/main/java/allchive/server/infrastructure/redis/config/RedissonConfig.java similarity index 93% rename from Infrastructure/src/main/java/allchive/server/infrastructure/config/redis/RedissonConfig.java rename to Infrastructure/src/main/java/allchive/server/infrastructure/redis/config/RedissonConfig.java index f59f5857..0fc00e30 100644 --- a/Infrastructure/src/main/java/allchive/server/infrastructure/config/redis/RedissonConfig.java +++ b/Infrastructure/src/main/java/allchive/server/infrastructure/redis/config/RedissonConfig.java @@ -1,4 +1,4 @@ -package allchive.server.infrastructure.config.redis; +package allchive.server.infrastructure.redis.config; import org.redisson.Redisson; From bbd4719093cfe8b6290fac8fab77a1d4d2d89785 Mon Sep 17 00:00:00 2001 From: wjdtkdgns <wjdtkdgns10266@gmail.com> Date: Sat, 1 Jul 2023 00:43:40 +0900 Subject: [PATCH 18/41] =?UTF-8?q?[fix]=20oauth=20callback=20url=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20#4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../server/api/auth/service/helper/KakaoOauthHelper.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Api/src/main/java/allchive/server/api/auth/service/helper/KakaoOauthHelper.java b/Api/src/main/java/allchive/server/api/auth/service/helper/KakaoOauthHelper.java index 991fb9ce..afd9410d 100644 --- a/Api/src/main/java/allchive/server/api/auth/service/helper/KakaoOauthHelper.java +++ b/Api/src/main/java/allchive/server/api/auth/service/helper/KakaoOauthHelper.java @@ -28,7 +28,7 @@ public String getKaKaoOauthLink(String referer) { + String.format( KAKAO_OAUTH_QUERY_STRING, kakaoOauthProperties.getClientId(), - referer + "kakao/callback"); + referer + "oauth/callback"); } public String getKaKaoOauthLinkTest() { @@ -44,7 +44,7 @@ public KakaoTokenResponse getKakaoOauthToken(String code, String referer) { // TODO : 프론트 콜백 URL 알아내면 바꾸기 return kakaoOauthClient.kakaoAuth( kakaoOauthProperties.getClientId(), - referer + "/kakao/callback", + referer + "oauth/callback", code, kakaoOauthProperties.getClientSecret()); } From 42ecf64f844bb7b4c137700a9c5173754604a63d Mon Sep 17 00:00:00 2001 From: Sanghoon Jeong <67852689+wjdtkdgns@users.noreply.github.com> Date: Sun, 2 Jul 2023 03:28:13 +0900 Subject: [PATCH 19/41] =?UTF-8?q?[feat]=20=ED=9A=8C=EC=9B=90=20=ED=83=88?= =?UTF-8?q?=ED=87=B4=20=EA=B5=AC=ED=98=84=20(#13)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [feat] jwt, security filter 셋팅 #4 * [feat] feign 설정, link api 구현 #4 * [feat] kakao login 구현 #4 * [fix] 개발용 api swagger deprecate 처리 #4 * [feat] kakao register 구현 #4 * [feat] 계정 탈퇴 구현 #12 * [feat] security permission 추가 #12 --- .../api/auth/controller/OauthController.java | 10 ++--- .../api/auth/controller/authController.java | 15 +++++++- .../api/auth/model/dto/KakaoUserInfoDto.java | 2 +- .../api/auth/service/OauthLinkUseCase.java | 4 +- .../api/auth/service/OauthLoginUseCase.java | 2 + .../auth/service/OauthRegisterUseCase.java | 1 + .../api/auth/service/WithdrawUserUseCase.java | 38 +++++++++++++++++++ .../auth/service/helper/KakaoOauthHelper.java | 27 ++++++++++--- .../service/helper/TokenGenerateHelper.java | 7 ++-- .../server/api/config/SwaggerConfig.java | 13 ++++--- .../api/config/security/JwtTokenFilter.java | 1 - .../api/config/security/SecurityConfig.java | 17 ++++++++- .../api/config/security/SecurityUtil.java | 16 ++++++++ .../server/core/error/GlobalErrorCode.java | 2 + .../SecurityContextNotFoundException.java | 14 +++++++ Domain/build.gradle | 4 +- .../user/adaptor/RefreshTokenAdaptor.java | 4 ++ .../domains/user/adaptor/UserAdaptor.java | 4 ++ .../domain/domains/user/domain/User.java | 14 ++++++- .../domains/user/domain/enums/OauthInfo.java | 11 ++++-- .../user/exception/ScrapErrorCode.java | 5 +-- .../domains/user/exception/UserErrorCode.java | 10 ++--- .../AlreadyDeletedUserException.java | 14 +++++++ .../user/service/UserDomainService.java | 6 +++ .../oauth/kakao/client/KakaoInfoClient.java | 11 +++--- .../oauth/kakao/dto/KakaoUnlinkTarget.java | 22 +++++++++++ 26 files changed, 226 insertions(+), 48 deletions(-) create mode 100644 Api/src/main/java/allchive/server/api/auth/service/WithdrawUserUseCase.java create mode 100644 Api/src/main/java/allchive/server/api/config/security/SecurityUtil.java create mode 100644 Core/src/main/java/allchive/server/core/error/exception/SecurityContextNotFoundException.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/user/exception/exceptions/AlreadyDeletedUserException.java create mode 100644 Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/dto/KakaoUnlinkTarget.java diff --git a/Api/src/main/java/allchive/server/api/auth/controller/OauthController.java b/Api/src/main/java/allchive/server/api/auth/controller/OauthController.java index 2722542a..7463e471 100644 --- a/Api/src/main/java/allchive/server/api/auth/controller/OauthController.java +++ b/Api/src/main/java/allchive/server/api/auth/controller/OauthController.java @@ -33,18 +33,16 @@ public OauthLoginLinkResponse getOauthLink( @RequestHeader(value = "host", required = false) String host) { if (referer.contains(host)) { String format = String.format("https://%s/", host); - return oauthLinkUseCase.getKaKaoOauthLink(provider, format); + return oauthLinkUseCase.getOauthLink(provider, format); } - return oauthLinkUseCase.getKaKaoOauthLink(provider, referer); + return oauthLinkUseCase.getOauthLink(provider, referer); } @Operation(summary = "개발용 oauth 링크발급", deprecated = true) @GetMapping("/link/{provider}/test") public OauthLoginLinkResponse getOauthLinkTest( @PathVariable("provider") OauthProvider provider) { - if (springEnvironmentHelper.isProdProfile()) {} - - return oauthLinkUseCase.getKaKaoOauthLinkTest(provider); + return oauthLinkUseCase.getOauthLinkTest(provider); } @Operation( @@ -71,7 +69,7 @@ public OauthSignInResponse oauthUserLoginTest( } @Operation(summary = "회원가입") - @PostMapping("/register/{provider}/test") + @PostMapping("/register/{provider}") public OauthRegisterResponse oauthUserRegister( @PathVariable("provider") OauthProvider provider, @RequestParam("idToken") String idToken, diff --git a/Api/src/main/java/allchive/server/api/auth/controller/authController.java b/Api/src/main/java/allchive/server/api/auth/controller/authController.java index de042ab0..f29abd38 100644 --- a/Api/src/main/java/allchive/server/api/auth/controller/authController.java +++ b/Api/src/main/java/allchive/server/api/auth/controller/authController.java @@ -1,9 +1,14 @@ package allchive.server.api.auth.controller; +import allchive.server.api.auth.service.WithdrawUserUseCase; +import allchive.server.domain.domains.user.domain.enums.OauthProvider; +import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -12,4 +17,12 @@ @RequiredArgsConstructor @Slf4j @Tag(name = "1-1. [auth]") -public class authController {} +public class authController { + private final WithdrawUserUseCase withdrawUserUseCase; + + @Operation(summary = "회원탈퇴를 합니다.") + @DeleteMapping("/withdrawal/{provider}") + public void withDrawUser(@PathVariable OauthProvider provider) { + withdrawUserUseCase.execute(provider); + } +} diff --git a/Api/src/main/java/allchive/server/api/auth/model/dto/KakaoUserInfoDto.java b/Api/src/main/java/allchive/server/api/auth/model/dto/KakaoUserInfoDto.java index 43dec326..a6be5cc0 100644 --- a/Api/src/main/java/allchive/server/api/auth/model/dto/KakaoUserInfoDto.java +++ b/Api/src/main/java/allchive/server/api/auth/model/dto/KakaoUserInfoDto.java @@ -20,7 +20,7 @@ public class KakaoUserInfoDto { private final OauthProvider oauthProvider; public OauthInfo toOauthInfo() { - return OauthInfo.builder().oid(oauthId).provider(oauthProvider).build(); + return OauthInfo.of(oauthProvider, oauthId); } } ; diff --git a/Api/src/main/java/allchive/server/api/auth/service/OauthLinkUseCase.java b/Api/src/main/java/allchive/server/api/auth/service/OauthLinkUseCase.java index 3f04c969..afee088e 100644 --- a/Api/src/main/java/allchive/server/api/auth/service/OauthLinkUseCase.java +++ b/Api/src/main/java/allchive/server/api/auth/service/OauthLinkUseCase.java @@ -13,7 +13,7 @@ public class OauthLinkUseCase { private final KakaoOauthHelper kakaoOauthHelper; - public OauthLoginLinkResponse getKaKaoOauthLinkTest(OauthProvider provider) { + public OauthLoginLinkResponse getOauthLinkTest(OauthProvider provider) { switch (provider) { case KAKAO: return new OauthLoginLinkResponse(kakaoOauthHelper.getKaKaoOauthLinkTest()); @@ -22,7 +22,7 @@ public OauthLoginLinkResponse getKaKaoOauthLinkTest(OauthProvider provider) { } } - public OauthLoginLinkResponse getKaKaoOauthLink(OauthProvider provider, String referer) { + public OauthLoginLinkResponse getOauthLink(OauthProvider provider, String referer) { switch (provider) { case KAKAO: return new OauthLoginLinkResponse(kakaoOauthHelper.getKaKaoOauthLink(referer)); diff --git a/Api/src/main/java/allchive/server/api/auth/service/OauthLoginUseCase.java b/Api/src/main/java/allchive/server/api/auth/service/OauthLoginUseCase.java index f39347b9..d360ceba 100644 --- a/Api/src/main/java/allchive/server/api/auth/service/OauthLoginUseCase.java +++ b/Api/src/main/java/allchive/server/api/auth/service/OauthLoginUseCase.java @@ -43,6 +43,7 @@ public OauthSignInResponse executeTest(OauthProvider provider, String code) { } } + /** idtoken 가져오기 * */ private OauthTokenResponse getCredential(OauthProvider provider, String code, String referer) { switch (provider) { case KAKAO: @@ -61,6 +62,7 @@ private OauthTokenResponse getCredentialTest(OauthProvider provider, String code } } + /** idtoken 분석 * */ private OauthInfo getOauthInfo(OauthProvider provider, String idToken) { switch (provider) { case KAKAO: diff --git a/Api/src/main/java/allchive/server/api/auth/service/OauthRegisterUseCase.java b/Api/src/main/java/allchive/server/api/auth/service/OauthRegisterUseCase.java index aafa813f..931fcc63 100644 --- a/Api/src/main/java/allchive/server/api/auth/service/OauthRegisterUseCase.java +++ b/Api/src/main/java/allchive/server/api/auth/service/OauthRegisterUseCase.java @@ -31,6 +31,7 @@ public OauthRegisterResponse execute( return OauthRegisterResponse.from(tokenGenerateHelper.execute(user)); } + /** idtoken 분석 * */ private OauthInfo getOauthInfo(OauthProvider provider, String idToken) { switch (provider) { case KAKAO: diff --git a/Api/src/main/java/allchive/server/api/auth/service/WithdrawUserUseCase.java b/Api/src/main/java/allchive/server/api/auth/service/WithdrawUserUseCase.java new file mode 100644 index 00000000..2a33ef96 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/auth/service/WithdrawUserUseCase.java @@ -0,0 +1,38 @@ +package allchive.server.api.auth.service; + + +import allchive.server.api.auth.service.helper.KakaoOauthHelper; +import allchive.server.api.config.security.SecurityUtil; +import allchive.server.core.annotation.UseCase; +import allchive.server.core.error.exception.InvalidOauthProviderException; +import allchive.server.domain.domains.user.adaptor.RefreshTokenAdaptor; +import allchive.server.domain.domains.user.adaptor.UserAdaptor; +import allchive.server.domain.domains.user.domain.User; +import allchive.server.domain.domains.user.domain.enums.OauthProvider; +import allchive.server.domain.domains.user.service.UserDomainService; +import lombok.RequiredArgsConstructor; + +@UseCase +@RequiredArgsConstructor +public class WithdrawUserUseCase { + private final KakaoOauthHelper kakaoOauthHelper; + private final UserAdaptor userAdaptor; + private final RefreshTokenAdaptor refreshTokenAdaptor; + private final UserDomainService userDomainService; + + public void execute(OauthProvider provider) { + Long userId = SecurityUtil.getCurrentUserId(); + User user = userAdaptor.queryUserById(userId); + // 우리쪽 탈퇴 + refreshTokenAdaptor.deleteTokenByUserId(userId); + // oauth쪽 탈퇴 + switch (provider) { + case KAKAO: + kakaoOauthHelper.withdrawOauthUser(user.getOauthInfo().getOid()); + break; + default: + throw InvalidOauthProviderException.EXCEPTION; + } + userDomainService.deleteUserById(userId); + } +} diff --git a/Api/src/main/java/allchive/server/api/auth/service/helper/KakaoOauthHelper.java b/Api/src/main/java/allchive/server/api/auth/service/helper/KakaoOauthHelper.java index afd9410d..559748bd 100644 --- a/Api/src/main/java/allchive/server/api/auth/service/helper/KakaoOauthHelper.java +++ b/Api/src/main/java/allchive/server/api/auth/service/helper/KakaoOauthHelper.java @@ -10,10 +10,13 @@ import allchive.server.infrastructure.oauth.kakao.client.KakaoInfoClient; import allchive.server.infrastructure.oauth.kakao.client.KakaoOauthClient; import allchive.server.infrastructure.oauth.kakao.dto.KakaoTokenResponse; +import allchive.server.infrastructure.oauth.kakao.dto.KakaoUnlinkTarget; import allchive.server.infrastructure.oauth.kakao.dto.OIDCPublicKeysResponse; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; @Helper +@Slf4j @RequiredArgsConstructor public class KakaoOauthHelper { private final KakaoOAuthProperties kakaoOauthProperties; @@ -28,7 +31,7 @@ public String getKaKaoOauthLink(String referer) { + String.format( KAKAO_OAUTH_QUERY_STRING, kakaoOauthProperties.getClientId(), - referer + "oauth/callback"); + referer + "kakao/callback"); } public String getKaKaoOauthLinkTest() { @@ -44,7 +47,7 @@ public KakaoTokenResponse getKakaoOauthToken(String code, String referer) { // TODO : 프론트 콜백 URL 알아내면 바꾸기 return kakaoOauthClient.kakaoAuth( kakaoOauthProperties.getClientId(), - referer + "oauth/callback", + referer + "/kakao/callback", code, kakaoOauthProperties.getClientSecret()); } @@ -57,14 +60,13 @@ public KakaoTokenResponse getKakaoOauthTokenTest(String code) { kakaoOauthProperties.getClientSecret()); } + /** idtoken 분석 * */ public OauthInfo getOauthInfoByIdToken(String idToken) { OIDCDecodePayload oidcDecodePayload = getOIDCDecodePayload(idToken); - return OauthInfo.builder() - .provider(OauthProvider.KAKAO) - .oid(oidcDecodePayload.getSub()) - .build(); + return OauthInfo.of(OauthProvider.KAKAO, oidcDecodePayload.getSub()); } + /** oidc decode * */ public OIDCDecodePayload getOIDCDecodePayload(String token) { OIDCPublicKeysResponse oidcPublicKeysResponse = kakaoOauthClient.getKakaoOIDCOpenKeys(); return oauthOIDCHelper.getPayloadFromIdToken( @@ -73,4 +75,17 @@ public OIDCDecodePayload getOIDCDecodePayload(String token) { kakaoOauthProperties.getAppId(), oidcPublicKeysResponse); } + + /** kakao측 회원 탈퇴 * */ + public void withdrawOauthUser(String oid) { + String kakaoAdminKey = kakaoOauthProperties.getAdminKey(); + KakaoUnlinkTarget unlinkKaKaoTarget = KakaoUnlinkTarget.from(oid); + String header = "KakaoAK " + kakaoAdminKey; + log.info( + "{} {} {}", + header, + unlinkKaKaoTarget.getTargetIdType(), + unlinkKaKaoTarget.getAud()); + kakaoInfoClient.unlinkUser(header, unlinkKaKaoTarget); + } } diff --git a/Api/src/main/java/allchive/server/api/auth/service/helper/TokenGenerateHelper.java b/Api/src/main/java/allchive/server/api/auth/service/helper/TokenGenerateHelper.java index da123129..23ed56db 100644 --- a/Api/src/main/java/allchive/server/api/auth/service/helper/TokenGenerateHelper.java +++ b/Api/src/main/java/allchive/server/api/auth/service/helper/TokenGenerateHelper.java @@ -7,7 +7,6 @@ import allchive.server.domain.domains.user.adaptor.RefreshTokenAdaptor; import allchive.server.domain.domains.user.domain.RefreshTokenEntity; import allchive.server.domain.domains.user.domain.User; -import allchive.server.domain.domains.user.domain.enums.UserRole; import lombok.RequiredArgsConstructor; import org.springframework.transaction.annotation.Transactional; @@ -22,16 +21,16 @@ public class TokenGenerateHelper { @Transactional public OauthSignInResponse execute(User user) { String newAccessToken = - jwtTokenProvider.generateAccessToken(user.getId(), UserRole.USER.getValue()); + jwtTokenProvider.generateAccessToken(user.getId(), user.getUserRole().getValue()); String newRefreshToken = jwtTokenProvider.generateRefreshToken(user.getId()); - RefreshTokenEntity newRefreshTokenEntityEntity = + RefreshTokenEntity newRefreshTokenEntity = RefreshTokenEntity.builder() .refreshToken(newRefreshToken) .id(user.getId()) .ttl(jwtTokenProvider.getRefreshTokenTTLSecond()) .build(); - refreshTokenAdaptor.save(newRefreshTokenEntityEntity); + refreshTokenAdaptor.save(newRefreshTokenEntity); return OauthSignInResponse.of( true, diff --git a/Api/src/main/java/allchive/server/api/config/SwaggerConfig.java b/Api/src/main/java/allchive/server/api/config/SwaggerConfig.java index 51244bdd..676a38d2 100644 --- a/Api/src/main/java/allchive/server/api/config/SwaggerConfig.java +++ b/Api/src/main/java/allchive/server/api/config/SwaggerConfig.java @@ -5,9 +5,8 @@ import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.info.Info; import io.swagger.v3.oas.models.info.License; +import io.swagger.v3.oas.models.security.SecurityRequirement; import io.swagger.v3.oas.models.security.SecurityScheme; -import io.swagger.v3.oas.models.security.SecurityScheme.In; -import io.swagger.v3.oas.models.security.SecurityScheme.Type; import io.swagger.v3.oas.models.servers.Server; import java.util.List; import javax.servlet.ServletContext; @@ -20,7 +19,11 @@ public class SwaggerConfig { public OpenAPI openAPI(ServletContext servletContext) { String contextPath = servletContext.getContextPath(); Server server = new Server().url(contextPath); - return new OpenAPI().servers(List.of(server)).components(authSetting()).info(swaggerInfo()); + return new OpenAPI() + .servers(List.of(server)) + .components(authSetting()) + .info(swaggerInfo()) + .addSecurityItem(new SecurityRequirement().addList("access-token")); } private Info swaggerInfo() { @@ -40,10 +43,10 @@ private Components authSetting() { .addSecuritySchemes( "access-token", new SecurityScheme() - .type(Type.HTTP) + .type(SecurityScheme.Type.HTTP) .scheme("bearer") .bearerFormat("JWT") - .in(In.HEADER) + .in(SecurityScheme.In.HEADER) .name("Authorization")); } } diff --git a/Api/src/main/java/allchive/server/api/config/security/JwtTokenFilter.java b/Api/src/main/java/allchive/server/api/config/security/JwtTokenFilter.java index 70951244..719b11e2 100644 --- a/Api/src/main/java/allchive/server/api/config/security/JwtTokenFilter.java +++ b/Api/src/main/java/allchive/server/api/config/security/JwtTokenFilter.java @@ -33,7 +33,6 @@ protected void doFilterInternal( Authentication authentication = getAuthentication(token); SecurityContextHolder.getContext().setAuthentication(authentication); } - filterChain.doFilter(request, response); } diff --git a/Api/src/main/java/allchive/server/api/config/security/SecurityConfig.java b/Api/src/main/java/allchive/server/api/config/security/SecurityConfig.java index 5996b057..391a2dab 100644 --- a/Api/src/main/java/allchive/server/api/config/security/SecurityConfig.java +++ b/Api/src/main/java/allchive/server/api/config/security/SecurityConfig.java @@ -4,6 +4,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Bean; +import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.http.SessionCreationPolicy; @@ -13,7 +14,7 @@ import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler; @RequiredArgsConstructor -@EnableWebSecurity +@EnableWebSecurity(debug = true) public class SecurityConfig { private final FilterConfig filterConfig; @@ -40,6 +41,8 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { .permitAll() .mvcMatchers("/example/**") .permitAll() + .mvcMatchers("/auth/**") + .permitAll() .anyRequest() .hasRole("USER"); @@ -48,8 +51,18 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { return http.build(); } + @Bean + public RoleHierarchyImpl roleHierarchy() { + RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl(); + roleHierarchy.setHierarchy("ROLE_ADMIN > ROLE_USER"); + return roleHierarchy; + } + @Bean public DefaultWebSecurityExpressionHandler expressionHandler() { - return new DefaultWebSecurityExpressionHandler(); + DefaultWebSecurityExpressionHandler expressionHandler = + new DefaultWebSecurityExpressionHandler(); + expressionHandler.setRoleHierarchy(roleHierarchy()); + return expressionHandler; } } diff --git a/Api/src/main/java/allchive/server/api/config/security/SecurityUtil.java b/Api/src/main/java/allchive/server/api/config/security/SecurityUtil.java new file mode 100644 index 00000000..bf9b4faa --- /dev/null +++ b/Api/src/main/java/allchive/server/api/config/security/SecurityUtil.java @@ -0,0 +1,16 @@ +package allchive.server.api.config.security; + + +import allchive.server.core.error.exception.SecurityContextNotFoundException; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; + +public class SecurityUtil { + public static Long getCurrentUserId() { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if (authentication == null) { + throw SecurityContextNotFoundException.EXCEPTION; + } + return Long.valueOf(authentication.getName()); + } +} diff --git a/Core/src/main/java/allchive/server/core/error/GlobalErrorCode.java b/Core/src/main/java/allchive/server/core/error/GlobalErrorCode.java index 82cf18ea..c7247f2f 100644 --- a/Core/src/main/java/allchive/server/core/error/GlobalErrorCode.java +++ b/Core/src/main/java/allchive/server/core/error/GlobalErrorCode.java @@ -17,6 +17,8 @@ public enum GlobalErrorCode implements BaseErrorCode { _INTERNAL_SERVER_ERROR(INTERNAL_SERVER, "GLOBAL_500_1", "서버 오류. 관리자에게 문의 부탁드립니다."), INVALID_OAUTH_PROVIDER(INTERNAL_SERVER, "GLOBAL_500_2", "지원하지 않는 OAuth Provider 입니다."), + SECURITY_CONTEXT_NOT_FOUND(INTERNAL_SERVER, "GLOBAL_500_3", "security context not found"), + /** 토큰 에러 * */ // TODO : 에러 코드 정렬 INVALID_TOKEN(UNAUTHORIZED, "AUTH_401_2", "올바르지 않은 토큰입니다."), diff --git a/Core/src/main/java/allchive/server/core/error/exception/SecurityContextNotFoundException.java b/Core/src/main/java/allchive/server/core/error/exception/SecurityContextNotFoundException.java new file mode 100644 index 00000000..ede13b6b --- /dev/null +++ b/Core/src/main/java/allchive/server/core/error/exception/SecurityContextNotFoundException.java @@ -0,0 +1,14 @@ +package allchive.server.core.error.exception; + + +import allchive.server.core.error.BaseErrorException; +import allchive.server.core.error.GlobalErrorCode; + +public class SecurityContextNotFoundException extends BaseErrorException { + + public static final BaseErrorException EXCEPTION = new SecurityContextNotFoundException(); + + private SecurityContextNotFoundException() { + super(GlobalErrorCode.SECURITY_CONTEXT_NOT_FOUND); + } +} diff --git a/Domain/build.gradle b/Domain/build.gradle index b1156f5c..1820de04 100644 --- a/Domain/build.gradle +++ b/Domain/build.gradle @@ -1,5 +1,5 @@ -bootJar.enabled = false -jar.enabled = true +bootJar { enabled = false } +jar { enabled = true } dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/RefreshTokenAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/RefreshTokenAdaptor.java index 2e7d2ed9..7c1f80b6 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/RefreshTokenAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/RefreshTokenAdaptor.java @@ -14,4 +14,8 @@ public class RefreshTokenAdaptor { public void save(RefreshTokenEntity newRefreshTokenEntityEntity) { refreshTokenRepository.save(newRefreshTokenEntityEntity); } + + public void deleteTokenByUserId(Long userId) { + refreshTokenRepository.deleteById(userId.toString()); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/UserAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/UserAdaptor.java index e1411274..c205c361 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/UserAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/UserAdaptor.java @@ -26,4 +26,8 @@ public User queryUserByOauthInfo(OauthInfo oauthInfo) { public void save(User user) { userRepository.save(user); } + + public User queryUserById(Long userId) { + return userRepository.findById(userId).orElseThrow(() -> UserNotFoundException.EXCEPTION); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/domain/User.java b/Domain/src/main/java/allchive/server/domain/domains/user/domain/User.java index c04cac49..d9313a3d 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/domain/User.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/domain/User.java @@ -5,6 +5,7 @@ import allchive.server.domain.domains.user.domain.enums.OauthInfo; import allchive.server.domain.domains.user.domain.enums.UserRole; import allchive.server.domain.domains.user.domain.enums.UserState; +import allchive.server.domain.domains.user.exception.exceptions.AlreadyDeletedUserException; import allchive.server.domain.domains.user.exception.exceptions.ForbiddenUserException; import java.time.LocalDateTime; import javax.persistence.*; @@ -31,7 +32,7 @@ public class User extends BaseTimeEntity { @NotNull private LocalDateTime lastLoginAt; - private String Email; + private String email; @Enumerated(EnumType.STRING) private UserState userState = UserState.NORMAL; @@ -61,4 +62,15 @@ public void login() { } lastLoginAt = LocalDateTime.now(); } + + public void withdrawUser() { + if (UserState.DELETED.equals(this.userState)) { + throw AlreadyDeletedUserException.EXCEPTION; + } + this.userState = UserState.DELETED; + this.nickname = LocalDateTime.now() + "삭제한 유저"; + this.profileImgUrl = null; + this.email = null; + this.oauthInfo.withDrawOauthInfo(); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/domain/enums/OauthInfo.java b/Domain/src/main/java/allchive/server/domain/domains/user/domain/enums/OauthInfo.java index 0cf9ed0b..b99a3bc3 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/domain/enums/OauthInfo.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/domain/enums/OauthInfo.java @@ -13,19 +13,22 @@ @Embeddable @NoArgsConstructor(access = AccessLevel.PROTECTED) public class OauthInfo { - @Enumerated(EnumType.STRING) private OauthProvider provider; private String oid; @Builder - public OauthInfo(OauthProvider provider, String oid) { + private OauthInfo(OauthProvider provider, String oid) { this.provider = provider; this.oid = oid; } - public OauthInfo withDrawOauthInfo() { - return OauthInfo.builder().oid(oid).provider(provider).build(); + public static OauthInfo of(OauthProvider provider, String oid) { + return OauthInfo.builder().provider(provider).oid(oid).build(); + } + + public void withDrawOauthInfo() { + this.oid = null; } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/exception/ScrapErrorCode.java b/Domain/src/main/java/allchive/server/domain/domains/user/exception/ScrapErrorCode.java index 2a81084e..795dfaaf 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/exception/ScrapErrorCode.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/exception/ScrapErrorCode.java @@ -5,18 +5,17 @@ import allchive.server.core.error.BaseErrorCode; import lombok.AllArgsConstructor; import lombok.Getter; -import org.springframework.http.HttpStatus; @Getter @AllArgsConstructor public enum ScrapErrorCode implements BaseErrorCode { ; - private HttpStatus status; + private int status; private String code; private String reason; @Override public ErrorReason getErrorReason() { - return ErrorReason.of(status.value(), code, reason); + return ErrorReason.of(status, code, reason); } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/exception/UserErrorCode.java b/Domain/src/main/java/allchive/server/domain/domains/user/exception/UserErrorCode.java index 7ad3dc1b..9cb9f537 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/exception/UserErrorCode.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/exception/UserErrorCode.java @@ -1,26 +1,26 @@ package allchive.server.domain.domains.user.exception; -import static org.springframework.http.HttpStatus.BAD_REQUEST; -import static org.springframework.http.HttpStatus.NOT_FOUND; +import static allchive.server.core.consts.AllchiveConst.BAD_REQUEST; +import static allchive.server.core.consts.AllchiveConst.NOT_FOUND; import allchive.server.core.dto.ErrorReason; import allchive.server.core.error.BaseErrorCode; import lombok.AllArgsConstructor; import lombok.Getter; -import org.springframework.http.HttpStatus; @Getter @AllArgsConstructor public enum UserErrorCode implements BaseErrorCode { FORBIDDEN_USER(BAD_REQUEST, "USER_400_1", "접근 제한된 유저입니다."), USER_ALREADY_SIGNUP(BAD_REQUEST, "USER_400_2", "이미 회원가입한 유저입니다."), + USER_ALREADY_DELETED(BAD_REQUEST, "USER_400_3", "이미 지워진 유저입니다."), USER_NOT_FOUND(NOT_FOUND, "USER_404_1", "유저 정보를 찾을 수 없습니다."); - private HttpStatus status; + private int status; private String code; private String reason; @Override public ErrorReason getErrorReason() { - return ErrorReason.of(status.value(), code, reason); + return ErrorReason.of(status, code, reason); } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/exception/exceptions/AlreadyDeletedUserException.java b/Domain/src/main/java/allchive/server/domain/domains/user/exception/exceptions/AlreadyDeletedUserException.java new file mode 100644 index 00000000..510807cc --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/user/exception/exceptions/AlreadyDeletedUserException.java @@ -0,0 +1,14 @@ +package allchive.server.domain.domains.user.exception.exceptions; + + +import allchive.server.core.error.BaseErrorException; +import allchive.server.domain.domains.user.exception.UserErrorCode; + +public class AlreadyDeletedUserException extends BaseErrorException { + + public static final BaseErrorException EXCEPTION = new AlreadyDeletedUserException(); + + private AlreadyDeletedUserException() { + super(UserErrorCode.USER_ALREADY_DELETED); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/service/UserDomainService.java b/Domain/src/main/java/allchive/server/domain/domains/user/service/UserDomainService.java index f9e8afec..c6acc791 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/service/UserDomainService.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/service/UserDomainService.java @@ -40,4 +40,10 @@ public Boolean checkUserCanRegister(OauthInfo oauthInfo) { public void validUserCanRegister(OauthInfo oauthInfo) { if (!checkUserCanRegister(oauthInfo)) throw AlreadySignUpUserException.EXCEPTION; } + + @Transactional + public void deleteUserById(Long userId) { + User user = userAdaptor.queryUserById(userId); + user.withdrawUser(); + } } diff --git a/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/client/KakaoInfoClient.java b/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/client/KakaoInfoClient.java index bc649642..200cb760 100644 --- a/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/client/KakaoInfoClient.java +++ b/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/client/KakaoInfoClient.java @@ -2,9 +2,10 @@ import allchive.server.infrastructure.oauth.kakao.config.KakaoInfoConfig; -import allchive.server.infrastructure.oauth.kakao.dto.KakaoInformationResponse; +import allchive.server.infrastructure.oauth.kakao.dto.KakaoUnlinkTarget; import org.springframework.cloud.openfeign.FeignClient; -import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestHeader; @FeignClient( @@ -12,7 +13,7 @@ url = "https://kapi.kakao.com", configuration = KakaoInfoConfig.class) public interface KakaoInfoClient { - - @GetMapping("/v2/user/me") - KakaoInformationResponse kakaoUserInfo(@RequestHeader("Authorization") String accessToken); + @PostMapping(path = "/v1/user/unlink", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) + void unlinkUser( + @RequestHeader("Authorization") String adminKey, KakaoUnlinkTarget unlinkKaKaoTarget); } diff --git a/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/dto/KakaoUnlinkTarget.java b/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/dto/KakaoUnlinkTarget.java new file mode 100644 index 00000000..cec7d3ed --- /dev/null +++ b/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/dto/KakaoUnlinkTarget.java @@ -0,0 +1,22 @@ +package allchive.server.infrastructure.oauth.kakao.dto; + + +import lombok.Getter; + +@Getter +public class KakaoUnlinkTarget { + + @feign.form.FormProperty("target_id_type") + private String targetIdType = "user_id"; + + @feign.form.FormProperty("target_id") + private String aud; + + private KakaoUnlinkTarget(String aud) { + this.aud = aud; + } + + public static KakaoUnlinkTarget from(String aud) { + return new KakaoUnlinkTarget(aud); + } +} From 7f2e31c528d906b593b769363bfd0fdf2915dc38 Mon Sep 17 00:00:00 2001 From: Sanghoon Jeong <67852689+wjdtkdgns@users.noreply.github.com> Date: Sun, 2 Jul 2023 21:08:03 +0900 Subject: [PATCH 20/41] =?UTF-8?q?[feat]=20auth=20=EA=B4=80=EB=A0=A8=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84=20(#14)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [feat] logout 구현 #12 * [feat] token refresh 구현 #12 * [chore] spotless 적용 * [fix] 개발 로그인 로직 변경 #12 --- .../api/auth/controller/OauthController.java | 8 ++++-- .../api/auth/controller/authController.java | 23 ++++++++++++--- .../api/auth/service/LogOutUserUseCase.java | 18 ++++++++++++ .../api/auth/service/TokenRefreshUseCase.java | 28 +++++++++++++++++++ .../api/config/security/SecurityConfig.java | 4 +-- .../server/core/error/GlobalErrorCode.java | 2 +- .../user/adaptor/RefreshTokenAdaptor.java | 7 +++++ 7 files changed, 79 insertions(+), 11 deletions(-) create mode 100644 Api/src/main/java/allchive/server/api/auth/service/LogOutUserUseCase.java create mode 100644 Api/src/main/java/allchive/server/api/auth/service/TokenRefreshUseCase.java diff --git a/Api/src/main/java/allchive/server/api/auth/controller/OauthController.java b/Api/src/main/java/allchive/server/api/auth/controller/OauthController.java index 7463e471..abe655b0 100644 --- a/Api/src/main/java/allchive/server/api/auth/controller/OauthController.java +++ b/Api/src/main/java/allchive/server/api/auth/controller/OauthController.java @@ -25,7 +25,9 @@ public class OauthController { private final OauthRegisterUseCase oauthRegisterUseCase; private final SpringEnvironmentHelper springEnvironmentHelper; - @Operation(summary = "oauth 링크발급", description = "oauth 링크를 받아볼수 있습니다.") + @Operation( + summary = "oauth 링크발급", + description = "oauth 링크를 받아볼수 있습니다. referer, host 입력 안하셔도 됩니다!") @GetMapping("/link/{provider}") public OauthLoginLinkResponse getOauthLink( @PathVariable("provider") OauthProvider provider, @@ -47,7 +49,7 @@ public OauthLoginLinkResponse getOauthLinkTest( @Operation( summary = "로그인", - description = "referer, host 입력 안하셔도 됩니다!" + "회원가입 안된 유저일 경우, canLogin=false 값을 보냅니다!") + description = "referer, host 입력 안하셔도 됩니다! 회원가입 안된 유저일 경우, canLogin=false 값을 보냅니다!") @PostMapping("/login/{provider}") public OauthSignInResponse oauthUserLogin( @PathVariable("provider") OauthProvider provider, @@ -62,7 +64,7 @@ public OauthSignInResponse oauthUserLogin( } @Operation(summary = "개발용 로그인", deprecated = true) - @PostMapping("/login/{provider}/test") + @GetMapping("/login/{provider}/test") public OauthSignInResponse oauthUserLoginTest( @PathVariable("provider") OauthProvider provider, @RequestParam("code") String code) { return oauthLoginUseCase.executeTest(provider, code); diff --git a/Api/src/main/java/allchive/server/api/auth/controller/authController.java b/Api/src/main/java/allchive/server/api/auth/controller/authController.java index f29abd38..271838d3 100644 --- a/Api/src/main/java/allchive/server/api/auth/controller/authController.java +++ b/Api/src/main/java/allchive/server/api/auth/controller/authController.java @@ -1,16 +1,16 @@ package allchive.server.api.auth.controller; +import allchive.server.api.auth.model.dto.response.OauthRegisterResponse; +import allchive.server.api.auth.service.LogOutUserUseCase; +import allchive.server.api.auth.service.TokenRefreshUseCase; import allchive.server.api.auth.service.WithdrawUserUseCase; import allchive.server.domain.domains.user.domain.enums.OauthProvider; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/auth") @@ -19,10 +19,25 @@ @Tag(name = "1-1. [auth]") public class authController { private final WithdrawUserUseCase withdrawUserUseCase; + private final LogOutUserUseCase logOutUserUseCase; + private final TokenRefreshUseCase tokenRefreshUseCase; @Operation(summary = "회원탈퇴를 합니다.") @DeleteMapping("/withdrawal/{provider}") public void withDrawUser(@PathVariable OauthProvider provider) { withdrawUserUseCase.execute(provider); } + + @Operation(summary = "로그아웃을 합니다.") + @PostMapping("/logout") + public void logOutUser() { + logOutUserUseCase.execute(); + } + + @Operation(summary = "토큰 재발급을 합니다.") + @PostMapping("/token/refresh") + public OauthRegisterResponse refreshToken( + @RequestParam(value = "refreshToken") String refreshToken) { + return tokenRefreshUseCase.execute(refreshToken); + } } diff --git a/Api/src/main/java/allchive/server/api/auth/service/LogOutUserUseCase.java b/Api/src/main/java/allchive/server/api/auth/service/LogOutUserUseCase.java new file mode 100644 index 00000000..ec842de9 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/auth/service/LogOutUserUseCase.java @@ -0,0 +1,18 @@ +package allchive.server.api.auth.service; + + +import allchive.server.api.config.security.SecurityUtil; +import allchive.server.core.annotation.UseCase; +import allchive.server.domain.domains.user.adaptor.RefreshTokenAdaptor; +import lombok.RequiredArgsConstructor; + +@UseCase +@RequiredArgsConstructor +public class LogOutUserUseCase { + private final RefreshTokenAdaptor refreshTokenAdaptor; + + public void execute() { + Long userId = SecurityUtil.getCurrentUserId(); + refreshTokenAdaptor.deleteTokenByUserId(userId); + } +} diff --git a/Api/src/main/java/allchive/server/api/auth/service/TokenRefreshUseCase.java b/Api/src/main/java/allchive/server/api/auth/service/TokenRefreshUseCase.java new file mode 100644 index 00000000..db69553e --- /dev/null +++ b/Api/src/main/java/allchive/server/api/auth/service/TokenRefreshUseCase.java @@ -0,0 +1,28 @@ +package allchive.server.api.auth.service; + + +import allchive.server.api.auth.model.dto.response.OauthRegisterResponse; +import allchive.server.api.auth.service.helper.TokenGenerateHelper; +import allchive.server.core.annotation.UseCase; +import allchive.server.core.jwt.JwtTokenProvider; +import allchive.server.domain.domains.user.adaptor.RefreshTokenAdaptor; +import allchive.server.domain.domains.user.adaptor.UserAdaptor; +import allchive.server.domain.domains.user.domain.RefreshTokenEntity; +import allchive.server.domain.domains.user.domain.User; +import lombok.RequiredArgsConstructor; + +@UseCase +@RequiredArgsConstructor +public class TokenRefreshUseCase { + private final RefreshTokenAdaptor refreshTokenAdaptor; + private final JwtTokenProvider jwtTokenProvider; + private final UserAdaptor userAdaptor; + private final TokenGenerateHelper tokenGenerateHelper; + + public OauthRegisterResponse execute(String refreshToken) { + RefreshTokenEntity oldToken = refreshTokenAdaptor.findTokenByRefreshToken(refreshToken); + Long userId = jwtTokenProvider.parseRefreshToken(oldToken.getRefreshToken()); + User user = userAdaptor.queryUserById(userId); + return OauthRegisterResponse.from(tokenGenerateHelper.execute(user)); + } +} diff --git a/Api/src/main/java/allchive/server/api/config/security/SecurityConfig.java b/Api/src/main/java/allchive/server/api/config/security/SecurityConfig.java index 391a2dab..42cb8ee5 100644 --- a/Api/src/main/java/allchive/server/api/config/security/SecurityConfig.java +++ b/Api/src/main/java/allchive/server/api/config/security/SecurityConfig.java @@ -14,7 +14,7 @@ import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler; @RequiredArgsConstructor -@EnableWebSecurity(debug = true) +@EnableWebSecurity() public class SecurityConfig { private final FilterConfig filterConfig; @@ -41,8 +41,6 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { .permitAll() .mvcMatchers("/example/**") .permitAll() - .mvcMatchers("/auth/**") - .permitAll() .anyRequest() .hasRole("USER"); diff --git a/Core/src/main/java/allchive/server/core/error/GlobalErrorCode.java b/Core/src/main/java/allchive/server/core/error/GlobalErrorCode.java index c7247f2f..f73bb7a8 100644 --- a/Core/src/main/java/allchive/server/core/error/GlobalErrorCode.java +++ b/Core/src/main/java/allchive/server/core/error/GlobalErrorCode.java @@ -24,7 +24,7 @@ public enum GlobalErrorCode implements BaseErrorCode { INVALID_TOKEN(UNAUTHORIZED, "AUTH_401_2", "올바르지 않은 토큰입니다."), INVALID_ACCESS_TOKEN_ERROR(UNAUTHORIZED, "AUTH_401_4", "알맞은 accessToken 을 넣어주세요."), EXPIRED_TOKEN(UNAUTHORIZED, "AUTH_401_3", "만료된 엑세스 토큰입니다"), - EXPIRED_REFRESH_TOKEN(UNAUTHORIZED, "AUTH_401_5", "만료된 리프레시 토큰입니다"), + EXPIRED_REFRESH_TOKEN(UNAUTHORIZED, "AUTH_403_1", "인증 시간이 만료되었습니다. 재 로그인 해주세요."), INVALID_AUTH_TOKEN(UNAUTHORIZED, "AUTH_401_2", "액세스 토큰이 유효하지 않습니다"), INVALID_REFRESH_TOKEN(UNAUTHORIZED, "AUTH_401_4", "리프레시 토큰이 유효하지 않습니다"), MISMATCH_REFRESH_TOKEN(UNAUTHORIZED, "AUTH_401_6", "리프레시 토큰의 유저 정보가 일치하지 않습니다"), diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/RefreshTokenAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/RefreshTokenAdaptor.java index 7c1f80b6..3b64e519 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/RefreshTokenAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/RefreshTokenAdaptor.java @@ -2,6 +2,7 @@ import allchive.server.core.annotation.Adaptor; +import allchive.server.core.error.exception.ExpiredRefreshTokenException; import allchive.server.domain.domains.user.domain.RefreshTokenEntity; import allchive.server.domain.domains.user.repository.RefreshTokenRepository; import lombok.RequiredArgsConstructor; @@ -18,4 +19,10 @@ public void save(RefreshTokenEntity newRefreshTokenEntityEntity) { public void deleteTokenByUserId(Long userId) { refreshTokenRepository.deleteById(userId.toString()); } + + public RefreshTokenEntity findTokenByRefreshToken(String refreshToken) { + return refreshTokenRepository + .findByRefreshToken(refreshToken) + .orElseThrow(() -> ExpiredRefreshTokenException.EXCEPTION); + } } From 5c41b8ed74ed01c245e024d18ddab953162b7a8f Mon Sep 17 00:00:00 2001 From: Sanghoon Jeong <67852689+wjdtkdgns@users.noreply.github.com> Date: Mon, 3 Jul 2023 01:48:02 +0900 Subject: [PATCH 21/41] =?UTF-8?q?[feat]=20category=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84=20?= =?UTF-8?q?(#16)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [feat] category 엔티티 설계 #15 * [fix] entity 변경 #15 * [feat] enum validator 구현, category 생성 기능 구현 #15 * [feat] category 수정 기능 구현 #15 * [feat] category 삭제 구현, 유저 validation 추가 #15 --- ...uthController.java => AuthController.java} | 2 +- .../controller/CategoryController.java | 42 +++++++++++++++++++ .../dto/request/CreateCategoryRequest.java | 19 +++++++++ .../dto/request/UpdateCategoryRequest.java | 19 +++++++++ .../category/model/mapper/CategoryMapper.java | 12 ++++++ .../service/CreateCategoryUseCase.java | 26 ++++++++++++ .../service/DeleteCategoryUseCase.java | 24 +++++++++++ .../service/UpdateCategoryUseCase.java | 27 ++++++++++++ .../server/core/annotation/Mapper.java | 16 +++++++ .../server/core/annotation/ValidEnum.java | 26 ++++++++++++ .../server/core/validator/EnumValidator.java | 33 +++++++++++++++ .../common/convertor/StringListConverter.java | 36 ++++++++++++++++ .../category/adaptor/CategoryAdaptor.java | 20 ++++++++- ...leAdaptor.java => CategoryPinAdaptor.java} | 2 +- .../domains/category/domain/Category.java | 41 ++++++++++++++++++ .../domain/{Title.java => CategoryPin.java} | 15 +++++-- .../domains/category/domain/enums/.gitkeep | 0 .../domains/category/domain/enums/Topic.java | 36 ++++++++++++++++ .../category/exception/CategoryErrorCode.java | 10 +++-- ...rorCode.java => CategoryPinErrorCode.java} | 2 +- .../exceptions/CategoryNotFoundException.java | 15 +++++++ .../NoAuthurityUpdateCategoryException.java | 14 +++++++ .../repository/CategoryPinRepository.java | 7 ++++ .../category/repository/TitleRepository.java | 7 ---- .../service/CategoryDomainService.java | 27 ++++++++++++ ...ice.java => CategoryPinDomainService.java} | 2 +- .../category/service/TitleService.java | 7 ---- .../category/validator/CategoryValidator.java | 13 +++++- .../domain/domains/user/domain/User.java | 8 ++++ .../domains/user/domain/UserTopicGroup.java | 18 -------- .../repository/UserTopicGroupRepository.java | 7 ---- .../src/main/resources/application-domain.yml | 15 +++++++ 32 files changed, 496 insertions(+), 52 deletions(-) rename Api/src/main/java/allchive/server/api/auth/controller/{authController.java => AuthController.java} (98%) create mode 100644 Api/src/main/java/allchive/server/api/category/controller/CategoryController.java create mode 100644 Api/src/main/java/allchive/server/api/category/model/dto/request/CreateCategoryRequest.java create mode 100644 Api/src/main/java/allchive/server/api/category/model/dto/request/UpdateCategoryRequest.java create mode 100644 Api/src/main/java/allchive/server/api/category/model/mapper/CategoryMapper.java create mode 100644 Api/src/main/java/allchive/server/api/category/service/CreateCategoryUseCase.java create mode 100644 Api/src/main/java/allchive/server/api/category/service/DeleteCategoryUseCase.java create mode 100644 Api/src/main/java/allchive/server/api/category/service/UpdateCategoryUseCase.java create mode 100644 Core/src/main/java/allchive/server/core/annotation/Mapper.java create mode 100644 Core/src/main/java/allchive/server/core/annotation/ValidEnum.java create mode 100644 Core/src/main/java/allchive/server/core/validator/EnumValidator.java create mode 100644 Domain/src/main/java/allchive/server/domain/common/convertor/StringListConverter.java rename Domain/src/main/java/allchive/server/domain/domains/category/adaptor/{TitleAdaptor.java => CategoryPinAdaptor.java} (83%) rename Domain/src/main/java/allchive/server/domain/domains/category/domain/{Title.java => CategoryPin.java} (60%) delete mode 100644 Domain/src/main/java/allchive/server/domain/domains/category/domain/enums/.gitkeep create mode 100644 Domain/src/main/java/allchive/server/domain/domains/category/domain/enums/Topic.java rename Domain/src/main/java/allchive/server/domain/domains/category/exception/{TitleErrorCode.java => CategoryPinErrorCode.java} (89%) create mode 100644 Domain/src/main/java/allchive/server/domain/domains/category/exception/exceptions/CategoryNotFoundException.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/category/exception/exceptions/NoAuthurityUpdateCategoryException.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/category/repository/CategoryPinRepository.java delete mode 100644 Domain/src/main/java/allchive/server/domain/domains/category/repository/TitleRepository.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/category/service/CategoryDomainService.java rename Domain/src/main/java/allchive/server/domain/domains/category/service/{CategoryService.java => CategoryPinDomainService.java} (75%) delete mode 100644 Domain/src/main/java/allchive/server/domain/domains/category/service/TitleService.java delete mode 100644 Domain/src/main/java/allchive/server/domain/domains/user/domain/UserTopicGroup.java delete mode 100644 Domain/src/main/java/allchive/server/domain/domains/user/repository/UserTopicGroupRepository.java diff --git a/Api/src/main/java/allchive/server/api/auth/controller/authController.java b/Api/src/main/java/allchive/server/api/auth/controller/AuthController.java similarity index 98% rename from Api/src/main/java/allchive/server/api/auth/controller/authController.java rename to Api/src/main/java/allchive/server/api/auth/controller/AuthController.java index 271838d3..87e83022 100644 --- a/Api/src/main/java/allchive/server/api/auth/controller/authController.java +++ b/Api/src/main/java/allchive/server/api/auth/controller/AuthController.java @@ -17,7 +17,7 @@ @RequiredArgsConstructor @Slf4j @Tag(name = "1-1. [auth]") -public class authController { +public class AuthController { private final WithdrawUserUseCase withdrawUserUseCase; private final LogOutUserUseCase logOutUserUseCase; private final TokenRefreshUseCase tokenRefreshUseCase; diff --git a/Api/src/main/java/allchive/server/api/category/controller/CategoryController.java b/Api/src/main/java/allchive/server/api/category/controller/CategoryController.java new file mode 100644 index 00000000..00ede3f5 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/category/controller/CategoryController.java @@ -0,0 +1,42 @@ +package allchive.server.api.category.controller; + +import allchive.server.api.category.model.dto.request.CreateCategoryRequest; +import allchive.server.api.category.model.dto.request.UpdateCategoryRequest; +import allchive.server.api.category.service.CreateCategoryUseCase; +import allchive.server.api.category.service.DeleteCategoryUseCase; +import allchive.server.api.category.service.UpdateCategoryUseCase; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/categories") +@RequiredArgsConstructor +@Slf4j +@Tag(name = "3. [category]") +public class CategoryController { + private final CreateCategoryUseCase createCategoryUseCase; + private final UpdateCategoryUseCase updateCategoryUseCase; + private final DeleteCategoryUseCase deleteCategoryUseCase; + + @Operation(summary = "카테고리를 생성합니다.") + @PostMapping() + public void createCategory(@RequestBody CreateCategoryRequest createCategoryRequest) { + createCategoryUseCase.execute(createCategoryRequest); + } + + @Operation(summary = "카테고리를 수정합니다.") + @PatchMapping(value = "/{categoryId}") + public void updateCategory(@RequestParam("categoryId") Long categoryId, + @RequestBody UpdateCategoryRequest updateCategoryRequest) { + updateCategoryUseCase.execute(categoryId, updateCategoryRequest); + } + + @Operation(summary = "카테고리를 수정합니다.") + @DeleteMapping(value = "/{categoryId}") + public void deleteCategory(@RequestParam("categoryId") Long categoryId) { + deleteCategoryUseCase.execute(categoryId); + } +} diff --git a/Api/src/main/java/allchive/server/api/category/model/dto/request/CreateCategoryRequest.java b/Api/src/main/java/allchive/server/api/category/model/dto/request/CreateCategoryRequest.java new file mode 100644 index 00000000..4ac0cc51 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/category/model/dto/request/CreateCategoryRequest.java @@ -0,0 +1,19 @@ +package allchive.server.api.category.model.dto.request; + +import allchive.server.core.annotation.ValidEnum; +import allchive.server.domain.domains.category.domain.enums.Topic; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; + +@Getter +public class CreateCategoryRequest { + @Schema(defaultValue = "카테고리 제목", description = "카테고리 제목") + private String title; + + @Schema(defaultValue = "디자인", description = "주제") + @ValidEnum(target = Topic.class) + private Topic topic; + + @Schema(defaultValue = "false", description = "공개 여부") + private boolean publicStatus; +} diff --git a/Api/src/main/java/allchive/server/api/category/model/dto/request/UpdateCategoryRequest.java b/Api/src/main/java/allchive/server/api/category/model/dto/request/UpdateCategoryRequest.java new file mode 100644 index 00000000..521b78b4 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/category/model/dto/request/UpdateCategoryRequest.java @@ -0,0 +1,19 @@ +package allchive.server.api.category.model.dto.request; + +import allchive.server.core.annotation.ValidEnum; +import allchive.server.domain.domains.category.domain.enums.Topic; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; + +@Getter +public class UpdateCategoryRequest { + @Schema(defaultValue = "카테고리 제목", description = "카테고리 제목") + private String title; + + @Schema(defaultValue = "디자인", description = "주제") + @ValidEnum(target = Topic.class) + private Topic topic; + + @Schema(defaultValue = "false", description = "공개 여부") + private boolean publicStatus; +} diff --git a/Api/src/main/java/allchive/server/api/category/model/mapper/CategoryMapper.java b/Api/src/main/java/allchive/server/api/category/model/mapper/CategoryMapper.java new file mode 100644 index 00000000..fe997527 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/category/model/mapper/CategoryMapper.java @@ -0,0 +1,12 @@ +package allchive.server.api.category.model.mapper; + +import allchive.server.api.category.model.dto.request.CreateCategoryRequest; +import allchive.server.core.annotation.Mapper; +import allchive.server.domain.domains.category.domain.Category; + +@Mapper +public class CategoryMapper { + public Category toEntity(CreateCategoryRequest request, Long userId) { + return Category.of(userId, request.getTitle(), request.isPublicStatus(), request.getTopic()); + } +} diff --git a/Api/src/main/java/allchive/server/api/category/service/CreateCategoryUseCase.java b/Api/src/main/java/allchive/server/api/category/service/CreateCategoryUseCase.java new file mode 100644 index 00000000..4d380eb4 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/category/service/CreateCategoryUseCase.java @@ -0,0 +1,26 @@ +package allchive.server.api.category.service; + +import allchive.server.api.category.model.dto.request.CreateCategoryRequest; +import allchive.server.api.category.model.mapper.CategoryMapper; +import allchive.server.api.config.security.SecurityUtil; +import allchive.server.core.annotation.UseCase; +import allchive.server.domain.domains.category.adaptor.CategoryAdaptor; +import allchive.server.domain.domains.category.domain.Category; +import allchive.server.domain.domains.category.service.CategoryDomainService; +import allchive.server.domain.domains.category.validator.CategoryValidator; +import lombok.RequiredArgsConstructor; +import org.springframework.transaction.annotation.Transactional; + +@UseCase +@RequiredArgsConstructor +public class CreateCategoryUseCase { + private final CategoryMapper categoryMapper; + private final CategoryDomainService categoryDomainService; + + @Transactional + public void execute(CreateCategoryRequest request) { + Long userId = SecurityUtil.getCurrentUserId(); + final Category category = categoryMapper.toEntity(request, userId); + categoryDomainService.createCategory(category); + } +} diff --git a/Api/src/main/java/allchive/server/api/category/service/DeleteCategoryUseCase.java b/Api/src/main/java/allchive/server/api/category/service/DeleteCategoryUseCase.java new file mode 100644 index 00000000..45a1020c --- /dev/null +++ b/Api/src/main/java/allchive/server/api/category/service/DeleteCategoryUseCase.java @@ -0,0 +1,24 @@ +package allchive.server.api.category.service; + +import allchive.server.api.config.security.SecurityUtil; +import allchive.server.core.annotation.UseCase; +import allchive.server.domain.domains.category.adaptor.CategoryAdaptor; +import allchive.server.domain.domains.category.domain.Category; +import allchive.server.domain.domains.category.service.CategoryDomainService; +import allchive.server.domain.domains.category.validator.CategoryValidator; +import lombok.RequiredArgsConstructor; +import org.springframework.transaction.annotation.Transactional; + +@UseCase +@RequiredArgsConstructor +public class DeleteCategoryUseCase { + private final CategoryDomainService categoryDomainService; + private final CategoryValidator categoryValidator; + + @Transactional + public void execute(Long categoryId) { + Long userId = SecurityUtil.getCurrentUserId(); + categoryValidator.verifyUser(userId, categoryId); + categoryDomainService.deleteCategory(categoryId); + } +} diff --git a/Api/src/main/java/allchive/server/api/category/service/UpdateCategoryUseCase.java b/Api/src/main/java/allchive/server/api/category/service/UpdateCategoryUseCase.java new file mode 100644 index 00000000..9a4d292c --- /dev/null +++ b/Api/src/main/java/allchive/server/api/category/service/UpdateCategoryUseCase.java @@ -0,0 +1,27 @@ +package allchive.server.api.category.service; + +import allchive.server.api.category.model.dto.request.UpdateCategoryRequest; +import allchive.server.api.config.security.SecurityUtil; +import allchive.server.core.annotation.UseCase; +import allchive.server.domain.domains.category.adaptor.CategoryAdaptor; +import allchive.server.domain.domains.category.domain.Category; +import allchive.server.domain.domains.category.service.CategoryDomainService; +import allchive.server.domain.domains.category.validator.CategoryValidator; +import lombok.RequiredArgsConstructor; +import org.springframework.transaction.annotation.Transactional; + +@UseCase +@RequiredArgsConstructor +public class UpdateCategoryUseCase { + private final CategoryDomainService categoryDomainService; + private final CategoryAdaptor categoryAdaptor; + private final CategoryValidator categoryValidator; + + @Transactional + public void execute(Long categoryId, UpdateCategoryRequest request) { + Long userId = SecurityUtil.getCurrentUserId(); + categoryValidator.verifyUser(userId, categoryId); + Category category = categoryAdaptor.findById(categoryId); + categoryDomainService.updateCategory(category, request.getTitle(), request.isPublicStatus(), request.getTopic()); + } +} diff --git a/Core/src/main/java/allchive/server/core/annotation/Mapper.java b/Core/src/main/java/allchive/server/core/annotation/Mapper.java new file mode 100644 index 00000000..42442da3 --- /dev/null +++ b/Core/src/main/java/allchive/server/core/annotation/Mapper.java @@ -0,0 +1,16 @@ +package allchive.server.core.annotation; + + +import org.springframework.core.annotation.AliasFor; +import org.springframework.stereotype.Component; + +import java.lang.annotation.*; + +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Component +public @interface Mapper { + @AliasFor(annotation = Component.class) + String value() default ""; +} diff --git a/Core/src/main/java/allchive/server/core/annotation/ValidEnum.java b/Core/src/main/java/allchive/server/core/annotation/ValidEnum.java new file mode 100644 index 00000000..242f8dc9 --- /dev/null +++ b/Core/src/main/java/allchive/server/core/annotation/ValidEnum.java @@ -0,0 +1,26 @@ +package allchive.server.core.annotation; + +import allchive.server.core.validator.EnumValidator; + +import javax.validation.Constraint; +import javax.validation.Payload; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@Documented +@Target({FIELD}) +@Retention(RUNTIME) +@Constraint(validatedBy = {EnumValidator.class}) +public @interface ValidEnum { + String message() default "올바른 값을 입력해주세요"; + + Class<?>[] groups() default { }; + + Class<? extends Payload>[] payload() default { }; + + Class<? extends java.lang.Enum<?>> target(); +} diff --git a/Core/src/main/java/allchive/server/core/validator/EnumValidator.java b/Core/src/main/java/allchive/server/core/validator/EnumValidator.java new file mode 100644 index 00000000..d8186b68 --- /dev/null +++ b/Core/src/main/java/allchive/server/core/validator/EnumValidator.java @@ -0,0 +1,33 @@ +package allchive.server.core.validator; + + +import allchive.server.core.annotation.ValidEnum; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import java.util.Arrays; + +public class EnumValidator implements ConstraintValidator<ValidEnum, Enum> { + private ValidEnum annotation; + + @Override + public void initialize(ValidEnum constraintAnnotation) { + this.annotation = constraintAnnotation; + } + + @Override + public boolean isValid(Enum value, ConstraintValidatorContext context) { + if(value == null) + return false; + + Object[] enumValues = this.annotation.target().getEnumConstants(); + if (enumValues != null) { + for (Object enumValue : enumValues) { + if (value.toString().equals(enumValue.toString())) { + return true; + } + } + } + return false; + } +} diff --git a/Domain/src/main/java/allchive/server/domain/common/convertor/StringListConverter.java b/Domain/src/main/java/allchive/server/domain/common/convertor/StringListConverter.java new file mode 100644 index 00000000..7871c3a4 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/common/convertor/StringListConverter.java @@ -0,0 +1,36 @@ +package allchive.server.domain.common.convertor; + +import allchive.server.core.error.exception.InternalServerError; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; + +import javax.persistence.AttributeConverter; +import java.io.IOException; +import java.util.List; + +public class StringListConverter implements AttributeConverter<List<String>, String> { + private static final ObjectMapper mapper = new ObjectMapper() + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + .configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, false); + + @Override + public String convertToDatabaseColumn(List<String> attribute) { + try { + return mapper.writeValueAsString(attribute); + } catch (JsonProcessingException e) { + throw InternalServerError.EXCEPTION; + } + } + + @Override + public List<String> convertToEntityAttribute(String dbData) { + TypeReference<List<String>> typeReference = new TypeReference<List<String>>() {}; + try { + return mapper.readValue(dbData, typeReference); + } catch (IOException e) { + throw InternalServerError.EXCEPTION; + } + } +} \ No newline at end of file diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/adaptor/CategoryAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/category/adaptor/CategoryAdaptor.java index 4f7bdff8..c95754a1 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/category/adaptor/CategoryAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/category/adaptor/CategoryAdaptor.java @@ -2,8 +2,26 @@ import allchive.server.core.annotation.Adaptor; +import allchive.server.domain.domains.category.domain.Category; +import allchive.server.domain.domains.category.exception.exceptions.CategoryNotFoundException; +import allchive.server.domain.domains.category.repository.CategoryRepository; import lombok.RequiredArgsConstructor; @Adaptor @RequiredArgsConstructor -public class CategoryAdaptor {} +public class CategoryAdaptor { + private final CategoryRepository categoryRepository; + + public void save(Category category) { + categoryRepository.save(category); + } + + public Category findById(Long categoryId) { + return categoryRepository.findById(categoryId) + .orElseThrow(()-> CategoryNotFoundException.EXCEPTION); + } + + public void deleteById(Long categoryId) { + categoryRepository.deleteById(categoryId); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/adaptor/TitleAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/category/adaptor/CategoryPinAdaptor.java similarity index 83% rename from Domain/src/main/java/allchive/server/domain/domains/category/adaptor/TitleAdaptor.java rename to Domain/src/main/java/allchive/server/domain/domains/category/adaptor/CategoryPinAdaptor.java index 9189982d..111cafdc 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/category/adaptor/TitleAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/category/adaptor/CategoryPinAdaptor.java @@ -6,4 +6,4 @@ @Adaptor @RequiredArgsConstructor -public class TitleAdaptor {} +public class CategoryPinAdaptor {} diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/domain/Category.java b/Domain/src/main/java/allchive/server/domain/domains/category/domain/Category.java index 966a1c66..726fe065 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/category/domain/Category.java +++ b/Domain/src/main/java/allchive/server/domain/domains/category/domain/Category.java @@ -3,7 +3,11 @@ import allchive.server.domain.common.model.BaseTimeEntity; import javax.persistence.*; + +import allchive.server.domain.domains.category.domain.enums.Topic; +import allchive.server.domain.domains.category.exception.exceptions.NoAuthurityUpdateCategoryException; import lombok.AccessLevel; +import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; @@ -15,4 +19,41 @@ public class Category extends BaseTimeEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + + // 카테고리 만든 사람 + private Long userId; + private String title; + private boolean publicStatus = Boolean.FALSE; + + @Enumerated(EnumType.STRING) + private Topic topic; + + @Builder + private Category(Long userId, String title, boolean publicStatus, Topic topic) { + this.userId = userId; + this.title = title; + this.publicStatus = publicStatus; + this.topic = topic; + } + + public static Category of(Long userId, String title, boolean publicStatus, Topic topic) { + return Category.builder() + .userId(userId) + .title(title) + .publicStatus(publicStatus) + .topic(topic) + .build(); + } + + public void update(String title, boolean publicStatus, Topic topic) { + this.title = title; + this.publicStatus = publicStatus; + this.topic = topic; + } + + public void validateUser(Long userId) { + if (!this.userId.equals(userId)) { + throw NoAuthurityUpdateCategoryException.EXCEPTION; + } + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/domain/Title.java b/Domain/src/main/java/allchive/server/domain/domains/category/domain/CategoryPin.java similarity index 60% rename from Domain/src/main/java/allchive/server/domain/domains/category/domain/Title.java rename to Domain/src/main/java/allchive/server/domain/domains/category/domain/CategoryPin.java index 6f40fa1e..e73bc36f 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/category/domain/Title.java +++ b/Domain/src/main/java/allchive/server/domain/domains/category/domain/CategoryPin.java @@ -1,18 +1,25 @@ package allchive.server.domain.domains.category.domain; - import allchive.server.domain.common.model.BaseTimeEntity; -import javax.persistence.*; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; +import javax.persistence.*; +import java.time.LocalDateTime; + @Getter -@Table(name = "tbl_category") +@Table(name = "tbl_category_pin") @Entity @NoArgsConstructor(access = AccessLevel.PROTECTED) -public class Title extends BaseTimeEntity { +public class CategoryPin extends BaseTimeEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + private Category category; + + private Long userId; + private LocalDateTime pinnedAt; } diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/domain/enums/.gitkeep b/Domain/src/main/java/allchive/server/domain/domains/category/domain/enums/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/domain/enums/Topic.java b/Domain/src/main/java/allchive/server/domain/domains/category/domain/enums/Topic.java new file mode 100644 index 00000000..41e31cac --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/category/domain/enums/Topic.java @@ -0,0 +1,36 @@ +package allchive.server.domain.domains.category.domain.enums; + + +import allchive.server.domain.domains.user.domain.enums.OauthProvider; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.stream.Stream; + +@Getter +@AllArgsConstructor +public enum Topic { + FOOD("푸드"), + LIFE("라이프"), + HOME_LIVING("홈/리빙"), + SHOPPING("쇼핑"), + SPORT("스포츠"), + SELF_IMPROVEMENT("자기 계발"), + TECH("테크"), + DESIGN("디자인"), + TREND("트렌드") + ; + + @JsonValue + private String value; + + @JsonCreator + public static OauthProvider parsing(String inputValue) { + return Stream.of(OauthProvider.values()) + .filter(category -> category.getValue().equals(inputValue)) + .findFirst() + .orElse(null); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/exception/CategoryErrorCode.java b/Domain/src/main/java/allchive/server/domain/domains/category/exception/CategoryErrorCode.java index aba7d15e..89652f43 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/category/exception/CategoryErrorCode.java +++ b/Domain/src/main/java/allchive/server/domain/domains/category/exception/CategoryErrorCode.java @@ -7,16 +7,20 @@ import lombok.Getter; import org.springframework.http.HttpStatus; +import static allchive.server.core.consts.AllchiveConst.*; + @Getter @AllArgsConstructor public enum CategoryErrorCode implements BaseErrorCode { - ; - private HttpStatus status; + CATEGORY_NOT_FOUND(BAD_REQUEST, "CATEGORY_400_1", "카테고리 정보를 찾을 수 없습니다."), + + NO_AUTHORITY_UPDATE_CATEGORY(FORBIDDEN, "CATEGORY_403_1", "카테고리 수정 권한이 없습니다."); + private int status; private String code; private String reason; @Override public ErrorReason getErrorReason() { - return ErrorReason.of(status.value(), code, reason); + return ErrorReason.of(status, code, reason); } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/exception/TitleErrorCode.java b/Domain/src/main/java/allchive/server/domain/domains/category/exception/CategoryPinErrorCode.java similarity index 89% rename from Domain/src/main/java/allchive/server/domain/domains/category/exception/TitleErrorCode.java rename to Domain/src/main/java/allchive/server/domain/domains/category/exception/CategoryPinErrorCode.java index 66093932..1b4f46e4 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/category/exception/TitleErrorCode.java +++ b/Domain/src/main/java/allchive/server/domain/domains/category/exception/CategoryPinErrorCode.java @@ -9,7 +9,7 @@ @Getter @AllArgsConstructor -public enum TitleErrorCode implements BaseErrorCode { +public enum CategoryPinErrorCode implements BaseErrorCode { ; private HttpStatus status; private String code; diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/exception/exceptions/CategoryNotFoundException.java b/Domain/src/main/java/allchive/server/domain/domains/category/exception/exceptions/CategoryNotFoundException.java new file mode 100644 index 00000000..39390cf4 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/category/exception/exceptions/CategoryNotFoundException.java @@ -0,0 +1,15 @@ +package allchive.server.domain.domains.category.exception.exceptions; + + +import allchive.server.core.error.BaseErrorException; +import allchive.server.domain.domains.category.exception.CategoryErrorCode; +import allchive.server.domain.domains.user.exception.UserErrorCode; + +public class CategoryNotFoundException extends BaseErrorException { + + public static final BaseErrorException EXCEPTION = new CategoryNotFoundException(); + + private CategoryNotFoundException() { + super(CategoryErrorCode.CATEGORY_NOT_FOUND); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/exception/exceptions/NoAuthurityUpdateCategoryException.java b/Domain/src/main/java/allchive/server/domain/domains/category/exception/exceptions/NoAuthurityUpdateCategoryException.java new file mode 100644 index 00000000..5bce8d8b --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/category/exception/exceptions/NoAuthurityUpdateCategoryException.java @@ -0,0 +1,14 @@ +package allchive.server.domain.domains.category.exception.exceptions; + + +import allchive.server.core.error.BaseErrorException; +import allchive.server.domain.domains.category.exception.CategoryErrorCode; + +public class NoAuthurityUpdateCategoryException extends BaseErrorException { + + public static final BaseErrorException EXCEPTION = new NoAuthurityUpdateCategoryException(); + + private NoAuthurityUpdateCategoryException() { + super(CategoryErrorCode.NO_AUTHORITY_UPDATE_CATEGORY); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/repository/CategoryPinRepository.java b/Domain/src/main/java/allchive/server/domain/domains/category/repository/CategoryPinRepository.java new file mode 100644 index 00000000..0ed97972 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/category/repository/CategoryPinRepository.java @@ -0,0 +1,7 @@ +package allchive.server.domain.domains.category.repository; + + +import allchive.server.domain.domains.category.domain.CategoryPin; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface CategoryPinRepository extends JpaRepository<CategoryPin, Long> {} diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/repository/TitleRepository.java b/Domain/src/main/java/allchive/server/domain/domains/category/repository/TitleRepository.java deleted file mode 100644 index 7d415c45..00000000 --- a/Domain/src/main/java/allchive/server/domain/domains/category/repository/TitleRepository.java +++ /dev/null @@ -1,7 +0,0 @@ -package allchive.server.domain.domains.category.repository; - - -import allchive.server.domain.domains.category.domain.Title; -import org.springframework.data.jpa.repository.JpaRepository; - -public interface TitleRepository extends JpaRepository<Title, Long> {} diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/service/CategoryDomainService.java b/Domain/src/main/java/allchive/server/domain/domains/category/service/CategoryDomainService.java new file mode 100644 index 00000000..51d75c17 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/category/service/CategoryDomainService.java @@ -0,0 +1,27 @@ +package allchive.server.domain.domains.category.service; + + +import allchive.server.core.annotation.DomainService; +import allchive.server.domain.domains.category.adaptor.CategoryAdaptor; +import allchive.server.domain.domains.category.domain.Category; +import allchive.server.domain.domains.category.domain.enums.Topic; +import lombok.RequiredArgsConstructor; + +@DomainService +@RequiredArgsConstructor +public class CategoryDomainService { + private final CategoryAdaptor categoryAdaptor; + + public void createCategory(Category category) { + categoryAdaptor.save(category); + } + + public void updateCategory(Category category, String title, boolean publicStatus, Topic topic) { + category.update(title, publicStatus, topic); + categoryAdaptor.save(category); + } + + public void deleteCategory(Long categoryId) { + categoryAdaptor.deleteById(categoryId); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/service/CategoryService.java b/Domain/src/main/java/allchive/server/domain/domains/category/service/CategoryPinDomainService.java similarity index 75% rename from Domain/src/main/java/allchive/server/domain/domains/category/service/CategoryService.java rename to Domain/src/main/java/allchive/server/domain/domains/category/service/CategoryPinDomainService.java index 82a03836..ccea11a8 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/category/service/CategoryService.java +++ b/Domain/src/main/java/allchive/server/domain/domains/category/service/CategoryPinDomainService.java @@ -4,4 +4,4 @@ import allchive.server.core.annotation.DomainService; @DomainService -public class CategoryService {} +public class CategoryPinDomainService {} diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/service/TitleService.java b/Domain/src/main/java/allchive/server/domain/domains/category/service/TitleService.java deleted file mode 100644 index ee7ef4b3..00000000 --- a/Domain/src/main/java/allchive/server/domain/domains/category/service/TitleService.java +++ /dev/null @@ -1,7 +0,0 @@ -package allchive.server.domain.domains.category.service; - - -import allchive.server.core.annotation.DomainService; - -@DomainService -public class TitleService {} diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/validator/CategoryValidator.java b/Domain/src/main/java/allchive/server/domain/domains/category/validator/CategoryValidator.java index fe2ab048..06b1d6c6 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/category/validator/CategoryValidator.java +++ b/Domain/src/main/java/allchive/server/domain/domains/category/validator/CategoryValidator.java @@ -2,6 +2,17 @@ import allchive.server.core.annotation.Validator; +import allchive.server.domain.domains.category.adaptor.CategoryAdaptor; +import allchive.server.domain.domains.category.domain.Category; +import lombok.RequiredArgsConstructor; +import org.apache.catalina.security.SecurityUtil; @Validator -public class CategoryValidator {} +@RequiredArgsConstructor +public class CategoryValidator { + private final CategoryAdaptor categoryAdaptor; + + public void verifyUser(Long userId, Long categoryId) { + categoryAdaptor.findById(categoryId).validateUser(userId); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/domain/User.java b/Domain/src/main/java/allchive/server/domain/domains/user/domain/User.java index d9313a3d..45d251a9 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/domain/User.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/domain/User.java @@ -1,13 +1,17 @@ package allchive.server.domain.domains.user.domain; +import allchive.server.domain.common.convertor.StringListConverter; import allchive.server.domain.common.model.BaseTimeEntity; +import allchive.server.domain.domains.category.domain.enums.Topic; import allchive.server.domain.domains.user.domain.enums.OauthInfo; import allchive.server.domain.domains.user.domain.enums.UserRole; import allchive.server.domain.domains.user.domain.enums.UserState; import allchive.server.domain.domains.user.exception.exceptions.AlreadyDeletedUserException; import allchive.server.domain.domains.user.exception.exceptions.ForbiddenUserException; import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; import javax.persistence.*; import javax.validation.constraints.NotNull; import lombok.AccessLevel; @@ -40,6 +44,9 @@ public class User extends BaseTimeEntity { @Enumerated(EnumType.STRING) private UserRole userRole = UserRole.USER; + @Convert(converter = StringListConverter.class) + private List<Topic> topics = new ArrayList(); + @Builder private User(String nickname, String profileImgUrl, OauthInfo oauthInfo) { this.nickname = nickname; @@ -72,5 +79,6 @@ public void withdrawUser() { this.profileImgUrl = null; this.email = null; this.oauthInfo.withDrawOauthInfo(); + this.topics = new ArrayList<>(); } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/domain/UserTopicGroup.java b/Domain/src/main/java/allchive/server/domain/domains/user/domain/UserTopicGroup.java deleted file mode 100644 index 34dfff5a..00000000 --- a/Domain/src/main/java/allchive/server/domain/domains/user/domain/UserTopicGroup.java +++ /dev/null @@ -1,18 +0,0 @@ -package allchive.server.domain.domains.user.domain; - - -import allchive.server.domain.common.model.BaseTimeEntity; -import javax.persistence.*; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.NoArgsConstructor; - -@Getter -@Table(name = "tbl_user_topic_group") -@Entity -@NoArgsConstructor(access = AccessLevel.PROTECTED) -public class UserTopicGroup extends BaseTimeEntity { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; -} diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/repository/UserTopicGroupRepository.java b/Domain/src/main/java/allchive/server/domain/domains/user/repository/UserTopicGroupRepository.java deleted file mode 100644 index afd0f201..00000000 --- a/Domain/src/main/java/allchive/server/domain/domains/user/repository/UserTopicGroupRepository.java +++ /dev/null @@ -1,7 +0,0 @@ -package allchive.server.domain.domains.user.repository; - - -import allchive.server.domain.domains.user.domain.UserTopicGroup; -import org.springframework.data.jpa.repository.JpaRepository; - -public interface UserTopicGroupRepository extends JpaRepository<UserTopicGroup, Long> {} diff --git a/Domain/src/main/resources/application-domain.yml b/Domain/src/main/resources/application-domain.yml index 24afd8a0..4e921213 100644 --- a/Domain/src/main/resources/application-domain.yml +++ b/Domain/src/main/resources/application-domain.yml @@ -22,6 +22,20 @@ spring: database: mysql --- +# local +spring: + config: + activate: + on-profile: local + +logging: + level: + com.zaxxer.hikari.HikariConfig: DEBUG + com.zaxxer.hikari: TRACE + org.springframework.orm.jpa: DEBUG + org.springframework.transaction: DEBUG + org.hibernate.SQL: debug +--- # dev spring: config: @@ -34,6 +48,7 @@ logging: com.zaxxer.hikari: TRACE org.springframework.orm.jpa: DEBUG org.springframework.transaction: DEBUG + org.hibernate.SQL: debug --- # prod From 564de52719731dcd1cfcf367ab123fa0b197c498 Mon Sep 17 00:00:00 2001 From: Sanghoon Jeong <67852689+wjdtkdgns@users.noreply.github.com> Date: Wed, 5 Jul 2023 01:25:58 +0900 Subject: [PATCH 22/41] =?UTF-8?q?[feat]=20category=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84=20(#21)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [feat] category entity 변경, category 조회 구현, enum validator 구현 #18 * [feat] 내 아카이빙 보기 구현 #18 * [feat] 스크랩 카테고리 가져오기 구현, 카테고리 전체보기 로직 수정 #18 * [feat] 카테고리 제목 보기 기능 구현 #18 * [feat] content 엔티티 구현 #18 * [feat] category content 보기 기능 구현 #18 * [chore] spotless 적용 #18 * [feat] topic 분류 추가, validation 추가 #18 * [chore] spotless 적용 #18 --- .../controller/CategoryController.java | 67 ++++++++- .../dto/request/CreateCategoryRequest.java | 6 +- .../dto/request/UpdateCategoryRequest.java | 6 +- .../response/CategoryContentsResponse.java | 45 ++++++ .../model/dto/response/CategoryResponse.java | 77 ++++++++++ .../dto/response/CategoryTitleResponse.java | 93 ++++++++++++ .../category/model/mapper/CategoryMapper.java | 34 ++++- .../category/model/vo/TitleContentCntVo.java | 29 ++++ .../service/CreateCategoryUseCase.java | 3 +- .../service/DeleteCategoryUseCase.java | 3 +- .../service/GetArchivedCategoryUseCase.java | 33 +++++ .../service/GetCategoryContentsUseCase.java | 47 ++++++ .../service/GetCategoryTitleUseCase.java | 24 ++++ .../category/service/GetCategoryUseCase.java | 44 ++++++ .../service/GetScrapCategoryUseCase.java | 39 +++++ .../service/UpdateCategoryUseCase.java | 8 +- .../api/common/slice/SliceResponse.java | 15 ++ .../model/dto/response/ContentResponse.java | 59 ++++++++ .../content/model/mapper/ContentMapper.java | 22 +++ Core/build.gradle | 1 + .../server/core/annotation/DateFormat.java | 16 +++ .../server/core/annotation/Mapper.java | 3 +- .../server/core/annotation/ValidEnum.java | 15 +- .../server/core/validator/EnumValidator.java | 5 +- .../common/convertor/StringListConverter.java | 13 +- .../server/domain/common/util/SliceUtil.java | 25 ++++ .../domains/block/adaptor/BlockAdaptor.java | 11 +- .../domain/domains/block/domain/Block.java | 7 - .../domains/block/domain/enums/BlockType.java | 6 - .../block/repository/BlockRepository.java | 5 +- .../category/adaptor/CategoryAdaptor.java | 32 ++++- .../category/adaptor/CategoryPinAdaptor.java | 9 -- .../domains/category/domain/Category.java | 52 ++++++- .../domains/category/domain/CategoryPin.java | 25 ---- .../domains/category/domain/enums/Topic.java | 26 ++-- .../category/exception/CategoryErrorCode.java | 6 +- .../exception/CategoryPinErrorCode.java | 22 --- .../exceptions/CategoryNotFoundException.java | 1 - .../exceptions/DeletedCategoryException.java | 14 ++ .../NotPublicCategoryException.java | 14 ++ .../repository/CategoryCustomRepository.java | 19 +++ .../CategoryCustomRepositoryImpl.java | 134 ++++++++++++++++++ .../repository/CategoryPinRepository.java | 7 - .../repository/CategoryRepository.java | 3 +- .../service/CategoryDomainService.java | 5 +- .../service/CategoryPinDomainService.java | 7 - .../category/validator/CategoryValidator.java | 10 +- .../category/validator/TitleValidator.java | 7 - .../content/adaptor/ContentAdaptor.java | 12 +- .../adaptor/ContentTagGroupAdaptor.java | 12 +- .../domains/content/domain/Content.java | 15 ++ .../content/domain/ContentTagGroup.java | 6 + .../domain/domains/content/domain/Tag.java | 3 + .../domains/content/domain/enums/.gitkeep | 0 .../content/domain/enums/ContentType.java | 26 ++++ .../repository/ContentCustomRepository.java | 10 ++ .../ContentCustomRepositoryImpl.java | 40 ++++++ .../content/repository/ContentRepository.java | 2 +- .../ContentTagGroupCustomRepository.java | 10 ++ .../ContentTagGroupCustomRepositoryImpl.java | 37 +++++ .../repository/ContentTagGroupRepository.java | 3 +- .../domains/user/adaptor/ScrapAdaptor.java | 11 +- .../domain/domains/user/domain/Scrap.java | 5 + .../user/repository/ScrapRepository.java | 5 +- 64 files changed, 1189 insertions(+), 162 deletions(-) create mode 100644 Api/src/main/java/allchive/server/api/category/model/dto/response/CategoryContentsResponse.java create mode 100644 Api/src/main/java/allchive/server/api/category/model/dto/response/CategoryResponse.java create mode 100644 Api/src/main/java/allchive/server/api/category/model/dto/response/CategoryTitleResponse.java create mode 100644 Api/src/main/java/allchive/server/api/category/model/vo/TitleContentCntVo.java create mode 100644 Api/src/main/java/allchive/server/api/category/service/GetArchivedCategoryUseCase.java create mode 100644 Api/src/main/java/allchive/server/api/category/service/GetCategoryContentsUseCase.java create mode 100644 Api/src/main/java/allchive/server/api/category/service/GetCategoryTitleUseCase.java create mode 100644 Api/src/main/java/allchive/server/api/category/service/GetCategoryUseCase.java create mode 100644 Api/src/main/java/allchive/server/api/category/service/GetScrapCategoryUseCase.java create mode 100644 Api/src/main/java/allchive/server/api/common/slice/SliceResponse.java create mode 100644 Api/src/main/java/allchive/server/api/content/model/dto/response/ContentResponse.java create mode 100644 Api/src/main/java/allchive/server/api/content/model/mapper/ContentMapper.java create mode 100644 Core/src/main/java/allchive/server/core/annotation/DateFormat.java create mode 100644 Domain/src/main/java/allchive/server/domain/common/util/SliceUtil.java delete mode 100644 Domain/src/main/java/allchive/server/domain/domains/block/domain/enums/BlockType.java delete mode 100644 Domain/src/main/java/allchive/server/domain/domains/category/adaptor/CategoryPinAdaptor.java delete mode 100644 Domain/src/main/java/allchive/server/domain/domains/category/domain/CategoryPin.java delete mode 100644 Domain/src/main/java/allchive/server/domain/domains/category/exception/CategoryPinErrorCode.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/category/exception/exceptions/DeletedCategoryException.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/category/exception/exceptions/NotPublicCategoryException.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/category/repository/CategoryCustomRepository.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/category/repository/CategoryCustomRepositoryImpl.java delete mode 100644 Domain/src/main/java/allchive/server/domain/domains/category/repository/CategoryPinRepository.java delete mode 100644 Domain/src/main/java/allchive/server/domain/domains/category/service/CategoryPinDomainService.java delete mode 100644 Domain/src/main/java/allchive/server/domain/domains/category/validator/TitleValidator.java delete mode 100644 Domain/src/main/java/allchive/server/domain/domains/content/domain/enums/.gitkeep create mode 100644 Domain/src/main/java/allchive/server/domain/domains/content/domain/enums/ContentType.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentCustomRepository.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentCustomRepositoryImpl.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentTagGroupCustomRepository.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentTagGroupCustomRepositoryImpl.java diff --git a/Api/src/main/java/allchive/server/api/category/controller/CategoryController.java b/Api/src/main/java/allchive/server/api/category/controller/CategoryController.java index 00ede3f5..2b24efee 100644 --- a/Api/src/main/java/allchive/server/api/category/controller/CategoryController.java +++ b/Api/src/main/java/allchive/server/api/category/controller/CategoryController.java @@ -1,14 +1,21 @@ package allchive.server.api.category.controller; + import allchive.server.api.category.model.dto.request.CreateCategoryRequest; import allchive.server.api.category.model.dto.request.UpdateCategoryRequest; -import allchive.server.api.category.service.CreateCategoryUseCase; -import allchive.server.api.category.service.DeleteCategoryUseCase; -import allchive.server.api.category.service.UpdateCategoryUseCase; +import allchive.server.api.category.model.dto.response.CategoryContentsResponse; +import allchive.server.api.category.model.dto.response.CategoryResponse; +import allchive.server.api.category.model.dto.response.CategoryTitleResponse; +import allchive.server.api.category.service.*; +import allchive.server.api.common.slice.SliceResponse; +import allchive.server.domain.domains.category.domain.enums.Topic; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springdoc.api.annotations.ParameterObject; +import org.springframework.data.domain.Pageable; +import org.springframework.data.web.PageableDefault; import org.springframework.web.bind.annotation.*; @RestController @@ -20,6 +27,11 @@ public class CategoryController { private final CreateCategoryUseCase createCategoryUseCase; private final UpdateCategoryUseCase updateCategoryUseCase; private final DeleteCategoryUseCase deleteCategoryUseCase; + private final GetCategoryUseCase getCategoryUseCase; + private final GetArchivedCategoryUseCase getArchivedCategoryUseCase; + private final GetScrapCategoryUseCase getScrapCategoryUseCase; + private final GetCategoryTitleUseCase getCategoryTitleUseCase; + private final GetCategoryContentsUseCase getCategoryContentsUseCase; @Operation(summary = "카테고리를 생성합니다.") @PostMapping() @@ -29,14 +41,59 @@ public void createCategory(@RequestBody CreateCategoryRequest createCategoryRequ @Operation(summary = "카테고리를 수정합니다.") @PatchMapping(value = "/{categoryId}") - public void updateCategory(@RequestParam("categoryId") Long categoryId, + public void updateCategory( + @PathVariable("categoryId") Long categoryId, @RequestBody UpdateCategoryRequest updateCategoryRequest) { updateCategoryUseCase.execute(categoryId, updateCategoryRequest); } @Operation(summary = "카테고리를 수정합니다.") @DeleteMapping(value = "/{categoryId}") - public void deleteCategory(@RequestParam("categoryId") Long categoryId) { + public void deleteCategory(@PathVariable("categoryId") Long categoryId) { deleteCategoryUseCase.execute(categoryId); } + + @Operation( + summary = "주제별 카테고리 리스트를 가져옵니다.", + description = "sort parameter는 입력하지 말아주세요! sorting : 스크랩 여부 -> 스크랩 수 -> 생성일자") + @GetMapping() + public SliceResponse<CategoryResponse> getCategory( + @RequestParam("topic") Topic topic, + @ParameterObject @PageableDefault(size = 10) Pageable pageable) { + return getCategoryUseCase.execute(topic, pageable); + } + + @Operation( + summary = "내 아카이빙 주제별 카테고리 리스트를 가져옵니다.", + description = "sort parameter는 입력하지 말아주세요! sorting : 고정 -> 스크랩 수 -> 생성일자") + @GetMapping(value = "/me/archiving") + public SliceResponse<CategoryResponse> getArchivedCategory( + @RequestParam("topic") Topic topic, + @ParameterObject @PageableDefault(size = 10) Pageable pageable) { + return getArchivedCategoryUseCase.execute(topic, pageable); + } + + @Operation( + summary = "스크랩 주제별 카테고리 리스트를 가져옵니다.", + description = "sort parameter는 입력하지 말아주세요! sorting : 스크랩 수 -> 생성일자") + @GetMapping(value = "/me/scrap") + public SliceResponse<CategoryResponse> getScrapCategory( + @RequestParam("topic") Topic topic, + @ParameterObject @PageableDefault(size = 10) Pageable pageable) { + return getScrapCategoryUseCase.execute(topic, pageable); + } + + @Operation(summary = "사용 중인 주제 & 카테고리 리스트를 가져옵니다. (컨텐츠 추가 시 사용)") + @GetMapping(value = "/lists") + public CategoryTitleResponse getScrapCategory() { + return getCategoryTitleUseCase.execute(); + } + + @Operation(summary = "카테고리별 컨텐츠 리스트를 가져옵니다.") + @GetMapping(value = "/{categoryId}/contents") + public CategoryContentsResponse getCategoryContents( + @PathVariable("categoryId") Long categoryId, + @ParameterObject @PageableDefault(size = 10) Pageable pageable) { + return getCategoryContentsUseCase.execute(categoryId, pageable); + } } diff --git a/Api/src/main/java/allchive/server/api/category/model/dto/request/CreateCategoryRequest.java b/Api/src/main/java/allchive/server/api/category/model/dto/request/CreateCategoryRequest.java index 4ac0cc51..7f80704f 100644 --- a/Api/src/main/java/allchive/server/api/category/model/dto/request/CreateCategoryRequest.java +++ b/Api/src/main/java/allchive/server/api/category/model/dto/request/CreateCategoryRequest.java @@ -1,5 +1,6 @@ package allchive.server.api.category.model.dto.request; + import allchive.server.core.annotation.ValidEnum; import allchive.server.domain.domains.category.domain.enums.Topic; import io.swagger.v3.oas.annotations.media.Schema; @@ -10,7 +11,10 @@ public class CreateCategoryRequest { @Schema(defaultValue = "카테고리 제목", description = "카테고리 제목") private String title; - @Schema(defaultValue = "디자인", description = "주제") + @Schema(defaultValue = "카테고리 이미지 url", description = "카테고리 이미지 url") + private String imageUrl; + + @Schema(defaultValue = "DESIGN", description = "주제") @ValidEnum(target = Topic.class) private Topic topic; diff --git a/Api/src/main/java/allchive/server/api/category/model/dto/request/UpdateCategoryRequest.java b/Api/src/main/java/allchive/server/api/category/model/dto/request/UpdateCategoryRequest.java index 521b78b4..c654d841 100644 --- a/Api/src/main/java/allchive/server/api/category/model/dto/request/UpdateCategoryRequest.java +++ b/Api/src/main/java/allchive/server/api/category/model/dto/request/UpdateCategoryRequest.java @@ -1,5 +1,6 @@ package allchive.server.api.category.model.dto.request; + import allchive.server.core.annotation.ValidEnum; import allchive.server.domain.domains.category.domain.enums.Topic; import io.swagger.v3.oas.annotations.media.Schema; @@ -10,7 +11,10 @@ public class UpdateCategoryRequest { @Schema(defaultValue = "카테고리 제목", description = "카테고리 제목") private String title; - @Schema(defaultValue = "디자인", description = "주제") + @Schema(defaultValue = "카테고리 이미지 url", description = "카테고리 이미지 url") + private String imageUrl; + + @Schema(defaultValue = "DESIGN", description = "주제") @ValidEnum(target = Topic.class) private Topic topic; diff --git a/Api/src/main/java/allchive/server/api/category/model/dto/response/CategoryContentsResponse.java b/Api/src/main/java/allchive/server/api/category/model/dto/response/CategoryContentsResponse.java new file mode 100644 index 00000000..925934c0 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/category/model/dto/response/CategoryContentsResponse.java @@ -0,0 +1,45 @@ +package allchive.server.api.category.model.dto.response; + + +import allchive.server.api.common.slice.SliceResponse; +import allchive.server.api.content.model.dto.response.ContentResponse; +import allchive.server.domain.domains.category.domain.Category; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Builder; +import lombok.Getter; + +@Getter +public class CategoryContentsResponse { + private SliceResponse<ContentResponse> contents; + + @Schema(description = "카테고리 제목") + private String categoryTitle; + + @Schema(description = "카테고리 고유번호") + private Long categoryId; + + @Schema(description = "카테고리의 총 컨텐츠 개수") + private Long totalContentsCount; + + @Builder + private CategoryContentsResponse( + SliceResponse<ContentResponse> contents, + String categoryTitle, + Long categoryId, + Long totalContentsCount) { + this.contents = contents; + this.categoryTitle = categoryTitle; + this.categoryId = categoryId; + this.totalContentsCount = totalContentsCount; + } + + public static CategoryContentsResponse of( + SliceResponse<ContentResponse> contentResponseSlice, Category category) { + return CategoryContentsResponse.builder() + .categoryId(category.getId()) + .categoryTitle(category.getTitle()) + .totalContentsCount(category.getScrapCnt() + category.getImgCnt()) + .contents(contentResponseSlice) + .build(); + } +} diff --git a/Api/src/main/java/allchive/server/api/category/model/dto/response/CategoryResponse.java b/Api/src/main/java/allchive/server/api/category/model/dto/response/CategoryResponse.java new file mode 100644 index 00000000..dd453aa4 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/category/model/dto/response/CategoryResponse.java @@ -0,0 +1,77 @@ +package allchive.server.api.category.model.dto.response; + + +import allchive.server.core.annotation.DateFormat; +import allchive.server.domain.domains.category.domain.Category; +import allchive.server.domain.domains.category.domain.enums.Topic; +import io.swagger.v3.oas.annotations.media.Schema; +import java.time.LocalDateTime; +import lombok.Builder; +import lombok.Getter; + +@Getter +public class CategoryResponse { + @Schema(description = "카테고리 고유 아이디") + private Long categoryId; + + @Schema(description = "카테고리 제목") + private String title; + + @Schema(defaultValue = "카테고리 이미지 url", description = "카테고리 이미지 url") + private String imageUrl; + + @Schema(defaultValue = "2023.07.02", description = "카테고리 생성일자") + @DateFormat + private LocalDateTime createdAt; + + @Schema(defaultValue = "카테고리 주제", description = "카테고리 주제") + private Topic topic; + + @Schema(description = "카테고리 컨텐츠 중 이미지 수") + private Long imgCnt; + + @Schema(description = "카테고리 컨텐츠 중 링크 수") + private Long linkCnt; + + @Schema(description = "카테고리 스크랩 수") + private Long scrapCnt; + + @Schema(description = "카테고리 스크랩/고정 여부, true == 스크랩/고정됨") + private boolean markStatus; + + @Builder + private CategoryResponse( + Long categoryId, + String title, + String imageUrl, + LocalDateTime createdAt, + Topic topic, + Long imgCnt, + Long linkCnt, + Long scrapCnt, + boolean markStatus) { + this.categoryId = categoryId; + this.title = title; + this.imageUrl = imageUrl; + this.createdAt = createdAt; + this.topic = topic; + this.imgCnt = imgCnt; + this.linkCnt = linkCnt; + this.scrapCnt = scrapCnt; + this.markStatus = markStatus; + } + + public static CategoryResponse of(Category category, boolean markStatus) { + return CategoryResponse.builder() + .categoryId(category.getId()) + .imageUrl(category.getImageUrl()) + .title(category.getTitle()) + .createdAt(category.getCreatedAt()) + .topic(category.getTopic()) + .imgCnt(category.getImgCnt()) + .linkCnt(category.getLinkCnt()) + .scrapCnt(category.getScrapCnt()) + .markStatus(markStatus) + .build(); + } +} diff --git a/Api/src/main/java/allchive/server/api/category/model/dto/response/CategoryTitleResponse.java b/Api/src/main/java/allchive/server/api/category/model/dto/response/CategoryTitleResponse.java new file mode 100644 index 00000000..af98c0f8 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/category/model/dto/response/CategoryTitleResponse.java @@ -0,0 +1,93 @@ +package allchive.server.api.category.model.dto.response; + + +import allchive.server.api.category.model.vo.TitleContentCntVo; +import java.util.ArrayList; +import java.util.List; +import lombok.Builder; +import lombok.Getter; + +@Getter +public class CategoryTitleResponse { + private List<TitleContentCntVo> food; + private List<TitleContentCntVo> life; + private List<TitleContentCntVo> homeLiving; + private List<TitleContentCntVo> shopping; + private List<TitleContentCntVo> sport; + private List<TitleContentCntVo> selfImprovement; + private List<TitleContentCntVo> tech; + private List<TitleContentCntVo> design; + private List<TitleContentCntVo> trend; + + @Builder + private CategoryTitleResponse( + List<TitleContentCntVo> food, + List<TitleContentCntVo> life, + List<TitleContentCntVo> homeLiving, + List<TitleContentCntVo> shopping, + List<TitleContentCntVo> sport, + List<TitleContentCntVo> selfImprovement, + List<TitleContentCntVo> tech, + List<TitleContentCntVo> design, + List<TitleContentCntVo> trend) { + this.food = food; + this.life = life; + this.homeLiving = homeLiving; + this.shopping = shopping; + this.sport = sport; + this.selfImprovement = selfImprovement; + this.tech = tech; + this.design = design; + this.trend = trend; + } + + public static CategoryTitleResponse init() { + return CategoryTitleResponse.builder() + .food(new ArrayList<>()) + .life(new ArrayList<>()) + .homeLiving(new ArrayList<>()) + .shopping(new ArrayList<>()) + .sport(new ArrayList<>()) + .selfImprovement(new ArrayList<>()) + .tech(new ArrayList<>()) + .design(new ArrayList<>()) + .trend(new ArrayList<>()) + .build(); + } + + public void addFood(TitleContentCntVo vo) { + this.food.add(vo); + } + + public void addLife(TitleContentCntVo vo) { + this.life.add(vo); + } + + public void addHomeLiving(TitleContentCntVo vo) { + this.homeLiving.add(vo); + } + + public void addShopping(TitleContentCntVo vo) { + this.shopping.add(vo); + } + + public void addSport(TitleContentCntVo vo) { + this.sport.add(vo); + } + + public void addSelfImprovement(TitleContentCntVo vo) { + this.selfImprovement.add(vo); + } + + public void addTech(TitleContentCntVo vo) { + this.tech.add(vo); + } + + public void addDesign(TitleContentCntVo vo) { + this.design.add(vo); + } + + public void addTrend(TitleContentCntVo vo) { + this.trend.add(vo); + } +} diff --git a/Api/src/main/java/allchive/server/api/category/model/mapper/CategoryMapper.java b/Api/src/main/java/allchive/server/api/category/model/mapper/CategoryMapper.java index fe997527..4ba34fb0 100644 --- a/Api/src/main/java/allchive/server/api/category/model/mapper/CategoryMapper.java +++ b/Api/src/main/java/allchive/server/api/category/model/mapper/CategoryMapper.java @@ -1,12 +1,44 @@ package allchive.server.api.category.model.mapper; + import allchive.server.api.category.model.dto.request.CreateCategoryRequest; +import allchive.server.api.category.model.dto.response.CategoryTitleResponse; +import allchive.server.api.category.model.vo.TitleContentCntVo; import allchive.server.core.annotation.Mapper; import allchive.server.domain.domains.category.domain.Category; +import java.util.List; +import lombok.extern.slf4j.Slf4j; @Mapper +@Slf4j public class CategoryMapper { public Category toEntity(CreateCategoryRequest request, Long userId) { - return Category.of(userId, request.getTitle(), request.isPublicStatus(), request.getTopic()); + return Category.of( + userId, + request.getTitle(), + request.getImageUrl(), + request.isPublicStatus(), + request.getTopic()); + } + + public CategoryTitleResponse toCategoryTitleResponse(List<Category> categories) { + CategoryTitleResponse response = CategoryTitleResponse.init(); + categories.forEach( + category -> { + switch (category.getTopic()) { + case FOOD -> response.addFood(TitleContentCntVo.from(category)); + case LIFE -> response.addLife(TitleContentCntVo.from(category)); + case HOME_LIVING -> response.addHomeLiving( + TitleContentCntVo.from(category)); + case SHOPPING -> response.addShopping(TitleContentCntVo.from(category)); + case SPORT -> response.addSport(TitleContentCntVo.from(category)); + case SELF_IMPROVEMENT -> response.addSelfImprovement( + TitleContentCntVo.from(category)); + case TECH -> response.addTech(TitleContentCntVo.from(category)); + case DESIGN -> response.addDesign(TitleContentCntVo.from(category)); + case TREND -> response.addTrend(TitleContentCntVo.from(category)); + } + }); + return response; } } diff --git a/Api/src/main/java/allchive/server/api/category/model/vo/TitleContentCntVo.java b/Api/src/main/java/allchive/server/api/category/model/vo/TitleContentCntVo.java new file mode 100644 index 00000000..34d5f03e --- /dev/null +++ b/Api/src/main/java/allchive/server/api/category/model/vo/TitleContentCntVo.java @@ -0,0 +1,29 @@ +package allchive.server.api.category.model.vo; + + +import allchive.server.domain.domains.category.domain.Category; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Builder; +import lombok.Getter; + +@Getter +public class TitleContentCntVo { + @Schema(defaultValue = "카테고리 제목", description = "카테고리 제목") + private String title; + + @Schema(defaultValue = "1", description = "카테고리에 속한 컨텐츠 총 개수") + private Long contentCnt; + + @Builder + private TitleContentCntVo(String title, Long contentCnt) { + this.title = title; + this.contentCnt = contentCnt; + } + + public static TitleContentCntVo from(Category category) { + return TitleContentCntVo.builder() + .contentCnt(category.getImgCnt() + category.getScrapCnt()) + .title(category.getTitle()) + .build(); + } +} diff --git a/Api/src/main/java/allchive/server/api/category/service/CreateCategoryUseCase.java b/Api/src/main/java/allchive/server/api/category/service/CreateCategoryUseCase.java index 4d380eb4..76c39c38 100644 --- a/Api/src/main/java/allchive/server/api/category/service/CreateCategoryUseCase.java +++ b/Api/src/main/java/allchive/server/api/category/service/CreateCategoryUseCase.java @@ -1,13 +1,12 @@ package allchive.server.api.category.service; + import allchive.server.api.category.model.dto.request.CreateCategoryRequest; import allchive.server.api.category.model.mapper.CategoryMapper; import allchive.server.api.config.security.SecurityUtil; import allchive.server.core.annotation.UseCase; -import allchive.server.domain.domains.category.adaptor.CategoryAdaptor; import allchive.server.domain.domains.category.domain.Category; import allchive.server.domain.domains.category.service.CategoryDomainService; -import allchive.server.domain.domains.category.validator.CategoryValidator; import lombok.RequiredArgsConstructor; import org.springframework.transaction.annotation.Transactional; diff --git a/Api/src/main/java/allchive/server/api/category/service/DeleteCategoryUseCase.java b/Api/src/main/java/allchive/server/api/category/service/DeleteCategoryUseCase.java index 45a1020c..8c354df0 100644 --- a/Api/src/main/java/allchive/server/api/category/service/DeleteCategoryUseCase.java +++ b/Api/src/main/java/allchive/server/api/category/service/DeleteCategoryUseCase.java @@ -1,9 +1,8 @@ package allchive.server.api.category.service; + import allchive.server.api.config.security.SecurityUtil; import allchive.server.core.annotation.UseCase; -import allchive.server.domain.domains.category.adaptor.CategoryAdaptor; -import allchive.server.domain.domains.category.domain.Category; import allchive.server.domain.domains.category.service.CategoryDomainService; import allchive.server.domain.domains.category.validator.CategoryValidator; import lombok.RequiredArgsConstructor; diff --git a/Api/src/main/java/allchive/server/api/category/service/GetArchivedCategoryUseCase.java b/Api/src/main/java/allchive/server/api/category/service/GetArchivedCategoryUseCase.java new file mode 100644 index 00000000..efdb23a7 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/category/service/GetArchivedCategoryUseCase.java @@ -0,0 +1,33 @@ +package allchive.server.api.category.service; + + +import allchive.server.api.category.model.dto.response.CategoryResponse; +import allchive.server.api.common.slice.SliceResponse; +import allchive.server.api.config.security.SecurityUtil; +import allchive.server.core.annotation.UseCase; +import allchive.server.domain.domains.category.adaptor.CategoryAdaptor; +import allchive.server.domain.domains.category.domain.enums.Topic; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; +import org.springframework.transaction.annotation.Transactional; + +@UseCase +@RequiredArgsConstructor +public class GetArchivedCategoryUseCase { + private final CategoryAdaptor categoryAdaptor; + + @Transactional(readOnly = true) + public SliceResponse<CategoryResponse> execute(Topic topic, Pageable pageable) { + Long userId = SecurityUtil.getCurrentUserId(); + Slice<CategoryResponse> categorySlices = + categoryAdaptor + .querySliceCategoryByUserId(userId, topic, pageable) + .map( + category -> + CategoryResponse.of( + category, + category.getPinUserId().contains(userId))); + return SliceResponse.of(categorySlices); + } +} diff --git a/Api/src/main/java/allchive/server/api/category/service/GetCategoryContentsUseCase.java b/Api/src/main/java/allchive/server/api/category/service/GetCategoryContentsUseCase.java new file mode 100644 index 00000000..d140db7b --- /dev/null +++ b/Api/src/main/java/allchive/server/api/category/service/GetCategoryContentsUseCase.java @@ -0,0 +1,47 @@ +package allchive.server.api.category.service; + + +import allchive.server.api.category.model.dto.response.CategoryContentsResponse; +import allchive.server.api.common.slice.SliceResponse; +import allchive.server.api.config.security.SecurityUtil; +import allchive.server.api.content.model.dto.response.ContentResponse; +import allchive.server.api.content.model.mapper.ContentMapper; +import allchive.server.core.annotation.UseCase; +import allchive.server.domain.domains.category.adaptor.CategoryAdaptor; +import allchive.server.domain.domains.category.domain.Category; +import allchive.server.domain.domains.category.validator.CategoryValidator; +import allchive.server.domain.domains.content.adaptor.ContentAdaptor; +import allchive.server.domain.domains.content.adaptor.ContentTagGroupAdaptor; +import allchive.server.domain.domains.content.domain.Content; +import allchive.server.domain.domains.content.domain.ContentTagGroup; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; +import org.springframework.transaction.annotation.Transactional; + +@UseCase +@RequiredArgsConstructor +public class GetCategoryContentsUseCase { + private final CategoryAdaptor categoryAdaptor; + private final CategoryValidator categoryValidator; + private final ContentAdaptor contentAdaptor; + private final ContentTagGroupAdaptor contentTagGroupAdaptor; + private final ContentMapper contentMapper; + + @Transactional(readOnly = true) + public CategoryContentsResponse execute(Long categoryId, Pageable pageable) { + Long userId = SecurityUtil.getCurrentUserId(); + categoryValidator.validationPublicStatus(categoryId, userId); + categoryValidator.validationDeleteStatus(categoryId, userId); + Category category = categoryAdaptor.findById(categoryId); + Slice<Content> contentList = + contentAdaptor.querySliceContentByCategoryId(categoryId, pageable); + List<ContentTagGroup> contentTagGroupList = + contentTagGroupAdaptor.queryContentIn(contentList.getContent()); + Slice<ContentResponse> contentResponseSlice = + contentList.map( + content -> contentMapper.toContentResponse(content, contentTagGroupList)); + return CategoryContentsResponse.of(SliceResponse.of(contentResponseSlice), category); + } +} diff --git a/Api/src/main/java/allchive/server/api/category/service/GetCategoryTitleUseCase.java b/Api/src/main/java/allchive/server/api/category/service/GetCategoryTitleUseCase.java new file mode 100644 index 00000000..78ac70d1 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/category/service/GetCategoryTitleUseCase.java @@ -0,0 +1,24 @@ +package allchive.server.api.category.service; + + +import allchive.server.api.category.model.dto.response.CategoryTitleResponse; +import allchive.server.api.category.model.mapper.CategoryMapper; +import allchive.server.api.config.security.SecurityUtil; +import allchive.server.core.annotation.UseCase; +import allchive.server.domain.domains.category.adaptor.CategoryAdaptor; +import lombok.RequiredArgsConstructor; +import org.springframework.transaction.annotation.Transactional; + +@UseCase +@RequiredArgsConstructor +public class GetCategoryTitleUseCase { + private final CategoryAdaptor categoryAdaptor; + private final CategoryMapper categoryMapper; + + @Transactional(readOnly = true) + public CategoryTitleResponse execute() { + Long userId = SecurityUtil.getCurrentUserId(); + return categoryMapper.toCategoryTitleResponse( + categoryAdaptor.queryCategoryByUserId(userId)); + } +} diff --git a/Api/src/main/java/allchive/server/api/category/service/GetCategoryUseCase.java b/Api/src/main/java/allchive/server/api/category/service/GetCategoryUseCase.java new file mode 100644 index 00000000..2cb2893f --- /dev/null +++ b/Api/src/main/java/allchive/server/api/category/service/GetCategoryUseCase.java @@ -0,0 +1,44 @@ +package allchive.server.api.category.service; + + +import allchive.server.api.category.model.dto.response.CategoryResponse; +import allchive.server.api.common.slice.SliceResponse; +import allchive.server.api.config.security.SecurityUtil; +import allchive.server.core.annotation.UseCase; +import allchive.server.domain.domains.block.adaptor.BlockAdaptor; +import allchive.server.domain.domains.block.domain.Block; +import allchive.server.domain.domains.category.adaptor.CategoryAdaptor; +import allchive.server.domain.domains.category.domain.enums.Topic; +import allchive.server.domain.domains.user.adaptor.ScrapAdaptor; +import allchive.server.domain.domains.user.domain.Scrap; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; +import org.springframework.transaction.annotation.Transactional; + +@UseCase +@RequiredArgsConstructor +public class GetCategoryUseCase { + private final ScrapAdaptor scrapAdaptor; + private final BlockAdaptor blockAdaptor; + private final CategoryAdaptor categoryAdaptor; + + @Transactional(readOnly = true) + public SliceResponse<CategoryResponse> execute(Topic topic, Pageable pageable) { + Long userId = SecurityUtil.getCurrentUserId(); + List<Long> categoryIdList = + scrapAdaptor.findAllByUserId(userId).stream().map(Scrap::getCategoryId).toList(); + List<Long> blockList = + blockAdaptor.findByBlockFrom(userId).stream().map(Block::getBlockUser).toList(); + Slice<CategoryResponse> categorySlices = + categoryAdaptor + .querySliceCategoryExceptBlock(categoryIdList, blockList, topic, pageable) + .map( + category -> + CategoryResponse.of( + category, + categoryIdList.contains(category.getId()))); + return SliceResponse.of(categorySlices); + } +} diff --git a/Api/src/main/java/allchive/server/api/category/service/GetScrapCategoryUseCase.java b/Api/src/main/java/allchive/server/api/category/service/GetScrapCategoryUseCase.java new file mode 100644 index 00000000..1772976c --- /dev/null +++ b/Api/src/main/java/allchive/server/api/category/service/GetScrapCategoryUseCase.java @@ -0,0 +1,39 @@ +package allchive.server.api.category.service; + + +import allchive.server.api.category.model.dto.response.CategoryResponse; +import allchive.server.api.common.slice.SliceResponse; +import allchive.server.api.config.security.SecurityUtil; +import allchive.server.core.annotation.UseCase; +import allchive.server.domain.domains.category.adaptor.CategoryAdaptor; +import allchive.server.domain.domains.category.domain.enums.Topic; +import allchive.server.domain.domains.user.adaptor.ScrapAdaptor; +import allchive.server.domain.domains.user.domain.Scrap; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; +import org.springframework.transaction.annotation.Transactional; + +@UseCase +@RequiredArgsConstructor +public class GetScrapCategoryUseCase { + private final ScrapAdaptor scrapAdaptor; + private final CategoryAdaptor categoryAdaptor; + + @Transactional(readOnly = true) + public SliceResponse<CategoryResponse> execute(Topic topic, Pageable pageable) { + Long userId = SecurityUtil.getCurrentUserId(); + List<Long> categoryIdList = + scrapAdaptor.findAllByUserId(userId).stream().map(Scrap::getCategoryId).toList(); + Slice<CategoryResponse> categorySlices = + categoryAdaptor + .querySliceCategoryIn(categoryIdList, topic, pageable) + .map( + category -> + CategoryResponse.of( + category, + categoryIdList.contains(category.getId()))); + return SliceResponse.of(categorySlices); + } +} diff --git a/Api/src/main/java/allchive/server/api/category/service/UpdateCategoryUseCase.java b/Api/src/main/java/allchive/server/api/category/service/UpdateCategoryUseCase.java index 9a4d292c..89b8c276 100644 --- a/Api/src/main/java/allchive/server/api/category/service/UpdateCategoryUseCase.java +++ b/Api/src/main/java/allchive/server/api/category/service/UpdateCategoryUseCase.java @@ -1,5 +1,6 @@ package allchive.server.api.category.service; + import allchive.server.api.category.model.dto.request.UpdateCategoryRequest; import allchive.server.api.config.security.SecurityUtil; import allchive.server.core.annotation.UseCase; @@ -22,6 +23,11 @@ public void execute(Long categoryId, UpdateCategoryRequest request) { Long userId = SecurityUtil.getCurrentUserId(); categoryValidator.verifyUser(userId, categoryId); Category category = categoryAdaptor.findById(categoryId); - categoryDomainService.updateCategory(category, request.getTitle(), request.isPublicStatus(), request.getTopic()); + categoryDomainService.updateCategory( + category, + request.getTitle(), + request.getImageUrl(), + request.isPublicStatus(), + request.getTopic()); } } diff --git a/Api/src/main/java/allchive/server/api/common/slice/SliceResponse.java b/Api/src/main/java/allchive/server/api/common/slice/SliceResponse.java new file mode 100644 index 00000000..a86cf861 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/common/slice/SliceResponse.java @@ -0,0 +1,15 @@ +package allchive.server.api.common.slice; + + +import java.util.List; +import org.springframework.data.domain.Slice; + +public record SliceResponse<T>(List<T> content, long page, int size, boolean hasNext) { + public static <T> SliceResponse<T> of(Slice<T> slice) { + return new SliceResponse<>( + slice.getContent(), + slice.getNumber(), + slice.getNumberOfElements(), + slice.hasNext()); + } +} diff --git a/Api/src/main/java/allchive/server/api/content/model/dto/response/ContentResponse.java b/Api/src/main/java/allchive/server/api/content/model/dto/response/ContentResponse.java new file mode 100644 index 00000000..97ebcb1d --- /dev/null +++ b/Api/src/main/java/allchive/server/api/content/model/dto/response/ContentResponse.java @@ -0,0 +1,59 @@ +package allchive.server.api.content.model.dto.response; + + +import allchive.server.core.annotation.DateFormat; +import allchive.server.domain.domains.content.domain.Content; +import allchive.server.domain.domains.content.domain.enums.ContentType; +import io.swagger.v3.oas.annotations.media.Schema; +import java.time.LocalDateTime; +import lombok.Builder; +import lombok.Getter; + +@Getter +public class ContentResponse { + @Schema(description = "컨텐츠 고유번호") + private Long contentId; + + @Schema(description = "컨텐츠 제목") + private String contentTitle; + + @Schema(description = "컨텐츠 종류") + private ContentType contentType; + + @Schema(defaultValue = "2023.07.02", description = "컨텐츠 생성일자") + @DateFormat + private LocalDateTime contentCreatedAt; + + @Schema(description = "컨텐츠 태그") + private String Tag; + + @Schema(description = "컨텐츠 태그 총 개수") + private Long TagCount; + + @Builder + private ContentResponse( + Long contentId, + String contentTitle, + ContentType contentType, + LocalDateTime contentCreatedAt, + String tag, + Long tagCount) { + this.contentId = contentId; + this.contentTitle = contentTitle; + this.contentType = contentType; + this.contentCreatedAt = contentCreatedAt; + Tag = tag; + TagCount = tagCount; + } + + public static ContentResponse of(Content content, String tag, Long tagCount) { + return ContentResponse.builder() + .contentId(content.getId()) + .contentTitle(content.getTitle()) + .contentType(content.getContentType()) + .contentCreatedAt(content.getCreatedAt()) + .tag(tag) + .tagCount(tagCount) + .build(); + } +} diff --git a/Api/src/main/java/allchive/server/api/content/model/mapper/ContentMapper.java b/Api/src/main/java/allchive/server/api/content/model/mapper/ContentMapper.java new file mode 100644 index 00000000..1881b59b --- /dev/null +++ b/Api/src/main/java/allchive/server/api/content/model/mapper/ContentMapper.java @@ -0,0 +1,22 @@ +package allchive.server.api.content.model.mapper; + + +import allchive.server.api.content.model.dto.response.ContentResponse; +import allchive.server.core.annotation.Mapper; +import allchive.server.domain.domains.content.domain.Content; +import allchive.server.domain.domains.content.domain.ContentTagGroup; +import java.util.List; + +@Mapper +public class ContentMapper { + public ContentResponse toContentResponse( + Content content, List<ContentTagGroup> contentTagGroupList) { + List<ContentTagGroup> tags = + contentTagGroupList.stream() + .filter(contentTagGroup -> contentTagGroup.getContent().equals(content)) + .toList(); + ContentTagGroup contentTagGroup = tags.stream().findFirst().orElse(null); + String tag = contentTagGroup == null ? null : contentTagGroup.getTag().getName(); + return ContentResponse.of(content, tag, (long) tags.size()); + } +} diff --git a/Core/build.gradle b/Core/build.gradle index 1cc27491..f67e5636 100644 --- a/Core/build.gradle +++ b/Core/build.gradle @@ -4,6 +4,7 @@ jar.enabled = true dependencies { implementation 'org.springframework.boot:spring-boot-starter-validation' implementation 'io.jsonwebtoken:jjwt-api:0.11.5' + implementation 'com.fasterxml.jackson.core:jackson-databind:2.14.2' runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5' runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5' diff --git a/Core/src/main/java/allchive/server/core/annotation/DateFormat.java b/Core/src/main/java/allchive/server/core/annotation/DateFormat.java new file mode 100644 index 00000000..44b15b02 --- /dev/null +++ b/Core/src/main/java/allchive/server/core/annotation/DateFormat.java @@ -0,0 +1,16 @@ +package allchive.server.core.annotation; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.TYPE_USE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import com.fasterxml.jackson.annotation.JacksonAnnotationsInside; +import com.fasterxml.jackson.annotation.JsonFormat; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +@Target({TYPE_USE, FIELD}) +@Retention(RUNTIME) +@JacksonAnnotationsInside +@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy.MM.dd", timezone = "Asia/Seoul") +public @interface DateFormat {} diff --git a/Core/src/main/java/allchive/server/core/annotation/Mapper.java b/Core/src/main/java/allchive/server/core/annotation/Mapper.java index 42442da3..99a13dc0 100644 --- a/Core/src/main/java/allchive/server/core/annotation/Mapper.java +++ b/Core/src/main/java/allchive/server/core/annotation/Mapper.java @@ -1,11 +1,10 @@ package allchive.server.core.annotation; +import java.lang.annotation.*; import org.springframework.core.annotation.AliasFor; import org.springframework.stereotype.Component; -import java.lang.annotation.*; - @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented diff --git a/Core/src/main/java/allchive/server/core/annotation/ValidEnum.java b/Core/src/main/java/allchive/server/core/annotation/ValidEnum.java index 242f8dc9..7f6a724b 100644 --- a/Core/src/main/java/allchive/server/core/annotation/ValidEnum.java +++ b/Core/src/main/java/allchive/server/core/annotation/ValidEnum.java @@ -1,15 +1,14 @@ package allchive.server.core.annotation; -import allchive.server.core.validator.EnumValidator; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; -import javax.validation.Constraint; -import javax.validation.Payload; +import allchive.server.core.validator.EnumValidator; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.Target; - -import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.RetentionPolicy.RUNTIME; +import javax.validation.Constraint; +import javax.validation.Payload; @Documented @Target({FIELD}) @@ -18,9 +17,9 @@ public @interface ValidEnum { String message() default "올바른 값을 입력해주세요"; - Class<?>[] groups() default { }; + Class<?>[] groups() default {}; - Class<? extends Payload>[] payload() default { }; + Class<? extends Payload>[] payload() default {}; Class<? extends java.lang.Enum<?>> target(); } diff --git a/Core/src/main/java/allchive/server/core/validator/EnumValidator.java b/Core/src/main/java/allchive/server/core/validator/EnumValidator.java index d8186b68..9c174225 100644 --- a/Core/src/main/java/allchive/server/core/validator/EnumValidator.java +++ b/Core/src/main/java/allchive/server/core/validator/EnumValidator.java @@ -2,10 +2,8 @@ import allchive.server.core.annotation.ValidEnum; - import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; -import java.util.Arrays; public class EnumValidator implements ConstraintValidator<ValidEnum, Enum> { private ValidEnum annotation; @@ -17,8 +15,7 @@ public void initialize(ValidEnum constraintAnnotation) { @Override public boolean isValid(Enum value, ConstraintValidatorContext context) { - if(value == null) - return false; + if (value == null) return false; Object[] enumValues = this.annotation.target().getEnumConstants(); if (enumValues != null) { diff --git a/Domain/src/main/java/allchive/server/domain/common/convertor/StringListConverter.java b/Domain/src/main/java/allchive/server/domain/common/convertor/StringListConverter.java index 7871c3a4..6f1ae89b 100644 --- a/Domain/src/main/java/allchive/server/domain/common/convertor/StringListConverter.java +++ b/Domain/src/main/java/allchive/server/domain/common/convertor/StringListConverter.java @@ -1,19 +1,20 @@ package allchive.server.domain.common.convertor; + import allchive.server.core.error.exception.InternalServerError; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; - -import javax.persistence.AttributeConverter; import java.io.IOException; import java.util.List; +import javax.persistence.AttributeConverter; public class StringListConverter implements AttributeConverter<List<String>, String> { - private static final ObjectMapper mapper = new ObjectMapper() - .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) - .configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, false); + private static final ObjectMapper mapper = + new ObjectMapper() + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + .configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, false); @Override public String convertToDatabaseColumn(List<String> attribute) { @@ -33,4 +34,4 @@ public List<String> convertToEntityAttribute(String dbData) { throw InternalServerError.EXCEPTION; } } -} \ No newline at end of file +} diff --git a/Domain/src/main/java/allchive/server/domain/common/util/SliceUtil.java b/Domain/src/main/java/allchive/server/domain/common/util/SliceUtil.java new file mode 100644 index 00000000..b8bc633e --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/common/util/SliceUtil.java @@ -0,0 +1,25 @@ +package allchive.server.domain.common.util; + + +import java.util.List; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; +import org.springframework.data.domain.SliceImpl; + +public class SliceUtil { + public static <T> Slice<T> toSlice(List<T> contents, Pageable pageable) { + boolean hasNext = hasNext(contents, pageable); + return new SliceImpl<>( + hasNext ? getContent(contents, pageable) : contents, pageable, hasNext); + } + + // 다음 페이지 있는지 확인 + private static <T> boolean hasNext(List<T> content, Pageable pageable) { + return pageable.isPaged() && content.size() > pageable.getPageSize(); + } + + // 데이터 1개 빼고 반환 + private static <T> List<T> getContent(List<T> content, Pageable pageable) { + return content.subList(0, pageable.getPageSize()); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/block/adaptor/BlockAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/block/adaptor/BlockAdaptor.java index 2a748750..5a9dd6f4 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/block/adaptor/BlockAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/block/adaptor/BlockAdaptor.java @@ -2,8 +2,17 @@ import allchive.server.core.annotation.Adaptor; +import allchive.server.domain.domains.block.domain.Block; +import allchive.server.domain.domains.block.repository.BlockRepository; +import java.util.List; import lombok.RequiredArgsConstructor; @Adaptor @RequiredArgsConstructor -public class BlockAdaptor {} +public class BlockAdaptor { + private final BlockRepository blockRepository; + + public List<Block> findByBlockFrom(Long userId) { + return blockRepository.findAllByBlockFrom(userId); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/block/domain/Block.java b/Domain/src/main/java/allchive/server/domain/domains/block/domain/Block.java index b5ffc4c1..6eef9cad 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/block/domain/Block.java +++ b/Domain/src/main/java/allchive/server/domain/domains/block/domain/Block.java @@ -2,7 +2,6 @@ import allchive.server.domain.common.model.BaseTimeEntity; -import allchive.server.domain.domains.block.domain.enums.BlockType; import javax.persistence.*; import javax.validation.constraints.NotNull; import lombok.AccessLevel; @@ -21,12 +20,6 @@ public class Block extends BaseTimeEntity { // block 한 사람 @NotNull private Long blockFrom; - @Enumerated(EnumType.STRING) - private BlockType blockType; - // Block 당한 유저 private Long blockUser; - - // Block 당한 컨텐츠 - private Long blockContent; } diff --git a/Domain/src/main/java/allchive/server/domain/domains/block/domain/enums/BlockType.java b/Domain/src/main/java/allchive/server/domain/domains/block/domain/enums/BlockType.java deleted file mode 100644 index af5695f8..00000000 --- a/Domain/src/main/java/allchive/server/domain/domains/block/domain/enums/BlockType.java +++ /dev/null @@ -1,6 +0,0 @@ -package allchive.server.domain.domains.block.domain.enums; - -public enum BlockType { - USER, - CONTENT -} diff --git a/Domain/src/main/java/allchive/server/domain/domains/block/repository/BlockRepository.java b/Domain/src/main/java/allchive/server/domain/domains/block/repository/BlockRepository.java index aaf0e631..f3107795 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/block/repository/BlockRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/block/repository/BlockRepository.java @@ -2,6 +2,9 @@ import allchive.server.domain.domains.block.domain.Block; +import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; -public interface BlockRepository extends JpaRepository<Block, Long> {} +public interface BlockRepository extends JpaRepository<Block, Long> { + List<Block> findAllByBlockFrom(Long userId); +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/adaptor/CategoryAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/category/adaptor/CategoryAdaptor.java index c95754a1..8a7d9b1f 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/category/adaptor/CategoryAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/category/adaptor/CategoryAdaptor.java @@ -2,10 +2,15 @@ import allchive.server.core.annotation.Adaptor; +import allchive.server.core.error.exception.InternalServerError; import allchive.server.domain.domains.category.domain.Category; +import allchive.server.domain.domains.category.domain.enums.Topic; import allchive.server.domain.domains.category.exception.exceptions.CategoryNotFoundException; import allchive.server.domain.domains.category.repository.CategoryRepository; +import java.util.List; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; @Adaptor @RequiredArgsConstructor @@ -13,15 +18,38 @@ public class CategoryAdaptor { private final CategoryRepository categoryRepository; public void save(Category category) { + if (category.getTopic().equals(Topic.ALL)) { + throw InternalServerError.EXCEPTION; + } categoryRepository.save(category); } public Category findById(Long categoryId) { - return categoryRepository.findById(categoryId) - .orElseThrow(()-> CategoryNotFoundException.EXCEPTION); + return categoryRepository + .findById(categoryId) + .orElseThrow(() -> CategoryNotFoundException.EXCEPTION); } public void deleteById(Long categoryId) { categoryRepository.deleteById(categoryId); } + + public Slice<Category> querySliceCategoryExceptBlock( + List<Long> categoryIdList, List<Long> blockList, Topic topic, Pageable pageable) { + return categoryRepository.querySliceCategoryExceptBlock( + categoryIdList, blockList, topic, pageable); + } + + public Slice<Category> querySliceCategoryByUserId(Long userId, Topic topic, Pageable pageable) { + return categoryRepository.querySliceCategoryByUserId(userId, topic, pageable); + } + + public Slice<Category> querySliceCategoryIn( + List<Long> categoryIdList, Topic topic, Pageable pageable) { + return categoryRepository.querySliceCategoryIn(categoryIdList, topic, pageable); + } + + public List<Category> queryCategoryByUserId(Long userId) { + return categoryRepository.queryCategoryByUserId(userId); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/adaptor/CategoryPinAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/category/adaptor/CategoryPinAdaptor.java deleted file mode 100644 index 111cafdc..00000000 --- a/Domain/src/main/java/allchive/server/domain/domains/category/adaptor/CategoryPinAdaptor.java +++ /dev/null @@ -1,9 +0,0 @@ -package allchive.server.domain.domains.category.adaptor; - - -import allchive.server.core.annotation.Adaptor; -import lombok.RequiredArgsConstructor; - -@Adaptor -@RequiredArgsConstructor -public class CategoryPinAdaptor {} diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/domain/Category.java b/Domain/src/main/java/allchive/server/domain/domains/category/domain/Category.java index 726fe065..b021f6dd 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/category/domain/Category.java +++ b/Domain/src/main/java/allchive/server/domain/domains/category/domain/Category.java @@ -2,10 +2,13 @@ import allchive.server.domain.common.model.BaseTimeEntity; -import javax.persistence.*; - import allchive.server.domain.domains.category.domain.enums.Topic; +import allchive.server.domain.domains.category.exception.exceptions.DeletedCategoryException; import allchive.server.domain.domains.category.exception.exceptions.NoAuthurityUpdateCategoryException; +import allchive.server.domain.domains.category.exception.exceptions.NotPublicCategoryException; +import java.util.ArrayList; +import java.util.List; +import javax.persistence.*; import lombok.AccessLevel; import lombok.Builder; import lombok.Getter; @@ -17,36 +20,61 @@ @NoArgsConstructor(access = AccessLevel.PROTECTED) public class Category extends BaseTimeEntity { @Id + @Column(name = "category_id", nullable = false, updatable = false) @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; // 카테고리 만든 사람 private Long userId; + private String imageUrl; private String title; - private boolean publicStatus = Boolean.FALSE; + private Boolean publicStatus = Boolean.FALSE; + private Long scrapCnt; + private Long linkCnt; + private Long imgCnt; + private Boolean deleteStatus = Boolean.FALSE; + + @ElementCollection + @CollectionTable(name = "tbl_category_pin", joinColumns = @JoinColumn(name = "category_id")) + private List<Long> pinUserId = new ArrayList<>(); @Enumerated(EnumType.STRING) private Topic topic; @Builder - private Category(Long userId, String title, boolean publicStatus, Topic topic) { + private Category( + Long userId, + String imageUrl, + String title, + boolean publicStatus, + boolean deleteStatus, + Topic topic) { this.userId = userId; + this.imageUrl = imageUrl; this.title = title; this.publicStatus = publicStatus; + this.deleteStatus = deleteStatus; this.topic = topic; + this.scrapCnt = 0L; + this.linkCnt = 0L; + this.imgCnt = 0L; } - public static Category of(Long userId, String title, boolean publicStatus, Topic topic) { + public static Category of( + Long userId, String title, String imageUrl, boolean publicStatus, Topic topic) { return Category.builder() .userId(userId) + .imageUrl(imageUrl) .title(title) .publicStatus(publicStatus) + .deleteStatus(Boolean.FALSE) .topic(topic) .build(); } - public void update(String title, boolean publicStatus, Topic topic) { + public void update(String title, String imageUrl, boolean publicStatus, Topic topic) { this.title = title; + this.imageUrl = imageUrl; this.publicStatus = publicStatus; this.topic = topic; } @@ -56,4 +84,16 @@ public void validateUser(Long userId) { throw NoAuthurityUpdateCategoryException.EXCEPTION; } } + + public void validatePublicStatus(Long userId) { + if (!this.publicStatus && !this.userId.equals(userId)) { + throw NotPublicCategoryException.EXCEPTION; + } + } + + public void validateDeleteStatus(Long userId) { + if (!this.publicStatus && !this.userId.equals(userId)) { + throw DeletedCategoryException.EXCEPTION; + } + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/domain/CategoryPin.java b/Domain/src/main/java/allchive/server/domain/domains/category/domain/CategoryPin.java deleted file mode 100644 index e73bc36f..00000000 --- a/Domain/src/main/java/allchive/server/domain/domains/category/domain/CategoryPin.java +++ /dev/null @@ -1,25 +0,0 @@ -package allchive.server.domain.domains.category.domain; - -import allchive.server.domain.common.model.BaseTimeEntity; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.NoArgsConstructor; - -import javax.persistence.*; -import java.time.LocalDateTime; - -@Getter -@Table(name = "tbl_category_pin") -@Entity -@NoArgsConstructor(access = AccessLevel.PROTECTED) -public class CategoryPin extends BaseTimeEntity { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @ManyToOne(fetch = FetchType.LAZY) - private Category category; - - private Long userId; - private LocalDateTime pinnedAt; -} diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/domain/enums/Topic.java b/Domain/src/main/java/allchive/server/domain/domains/category/domain/enums/Topic.java index 41e31cac..1dde5528 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/category/domain/enums/Topic.java +++ b/Domain/src/main/java/allchive/server/domain/domains/category/domain/enums/Topic.java @@ -4,27 +4,25 @@ import allchive.server.domain.domains.user.domain.enums.OauthProvider; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonValue; +import java.util.stream.Stream; import lombok.AllArgsConstructor; import lombok.Getter; -import java.util.stream.Stream; - @Getter @AllArgsConstructor public enum Topic { - FOOD("푸드"), - LIFE("라이프"), - HOME_LIVING("홈/리빙"), - SHOPPING("쇼핑"), - SPORT("스포츠"), - SELF_IMPROVEMENT("자기 계발"), - TECH("테크"), - DESIGN("디자인"), - TREND("트렌드") - ; + ALL("ALL"), + FOOD("FOOD"), + LIFE("LIFE"), + HOME_LIVING("HOME_LIVING"), + SHOPPING("SHOPPING"), + SPORT("SPORT"), + SELF_IMPROVEMENT("SELF_IMPROVEMENT"), + TECH("TECH"), + DESIGN("DESIGN"), + TREND("TREND"); - @JsonValue - private String value; + @JsonValue private String value; @JsonCreator public static OauthProvider parsing(String inputValue) { diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/exception/CategoryErrorCode.java b/Domain/src/main/java/allchive/server/domain/domains/category/exception/CategoryErrorCode.java index 89652f43..b6c9451f 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/category/exception/CategoryErrorCode.java +++ b/Domain/src/main/java/allchive/server/domain/domains/category/exception/CategoryErrorCode.java @@ -1,18 +1,18 @@ package allchive.server.domain.domains.category.exception; +import static allchive.server.core.consts.AllchiveConst.*; import allchive.server.core.dto.ErrorReason; import allchive.server.core.error.BaseErrorCode; import lombok.AllArgsConstructor; import lombok.Getter; -import org.springframework.http.HttpStatus; - -import static allchive.server.core.consts.AllchiveConst.*; @Getter @AllArgsConstructor public enum CategoryErrorCode implements BaseErrorCode { CATEGORY_NOT_FOUND(BAD_REQUEST, "CATEGORY_400_1", "카테고리 정보를 찾을 수 없습니다."), + NOT_PUBLIC_CATEGORY(BAD_REQUEST, "CATEGORY_400_2", "공개된 카테고리가 아닙니다."), + DELETED_CATEGORY(BAD_REQUEST, "CATEGORY_400_2", "삭제된 카테고리입니다."), NO_AUTHORITY_UPDATE_CATEGORY(FORBIDDEN, "CATEGORY_403_1", "카테고리 수정 권한이 없습니다."); private int status; diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/exception/CategoryPinErrorCode.java b/Domain/src/main/java/allchive/server/domain/domains/category/exception/CategoryPinErrorCode.java deleted file mode 100644 index 1b4f46e4..00000000 --- a/Domain/src/main/java/allchive/server/domain/domains/category/exception/CategoryPinErrorCode.java +++ /dev/null @@ -1,22 +0,0 @@ -package allchive.server.domain.domains.category.exception; - - -import allchive.server.core.dto.ErrorReason; -import allchive.server.core.error.BaseErrorCode; -import lombok.AllArgsConstructor; -import lombok.Getter; -import org.springframework.http.HttpStatus; - -@Getter -@AllArgsConstructor -public enum CategoryPinErrorCode implements BaseErrorCode { - ; - private HttpStatus status; - private String code; - private String reason; - - @Override - public ErrorReason getErrorReason() { - return ErrorReason.of(status.value(), code, reason); - } -} diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/exception/exceptions/CategoryNotFoundException.java b/Domain/src/main/java/allchive/server/domain/domains/category/exception/exceptions/CategoryNotFoundException.java index 39390cf4..90160ad8 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/category/exception/exceptions/CategoryNotFoundException.java +++ b/Domain/src/main/java/allchive/server/domain/domains/category/exception/exceptions/CategoryNotFoundException.java @@ -3,7 +3,6 @@ import allchive.server.core.error.BaseErrorException; import allchive.server.domain.domains.category.exception.CategoryErrorCode; -import allchive.server.domain.domains.user.exception.UserErrorCode; public class CategoryNotFoundException extends BaseErrorException { diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/exception/exceptions/DeletedCategoryException.java b/Domain/src/main/java/allchive/server/domain/domains/category/exception/exceptions/DeletedCategoryException.java new file mode 100644 index 00000000..33a66854 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/category/exception/exceptions/DeletedCategoryException.java @@ -0,0 +1,14 @@ +package allchive.server.domain.domains.category.exception.exceptions; + + +import allchive.server.core.error.BaseErrorException; +import allchive.server.domain.domains.category.exception.CategoryErrorCode; + +public class DeletedCategoryException extends BaseErrorException { + + public static final BaseErrorException EXCEPTION = new DeletedCategoryException(); + + private DeletedCategoryException() { + super(CategoryErrorCode.DELETED_CATEGORY); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/exception/exceptions/NotPublicCategoryException.java b/Domain/src/main/java/allchive/server/domain/domains/category/exception/exceptions/NotPublicCategoryException.java new file mode 100644 index 00000000..0a5bc060 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/category/exception/exceptions/NotPublicCategoryException.java @@ -0,0 +1,14 @@ +package allchive.server.domain.domains.category.exception.exceptions; + + +import allchive.server.core.error.BaseErrorException; +import allchive.server.domain.domains.category.exception.CategoryErrorCode; + +public class NotPublicCategoryException extends BaseErrorException { + + public static final BaseErrorException EXCEPTION = new NotPublicCategoryException(); + + private NotPublicCategoryException() { + super(CategoryErrorCode.NOT_PUBLIC_CATEGORY); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/repository/CategoryCustomRepository.java b/Domain/src/main/java/allchive/server/domain/domains/category/repository/CategoryCustomRepository.java new file mode 100644 index 00000000..0bfe0f79 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/category/repository/CategoryCustomRepository.java @@ -0,0 +1,19 @@ +package allchive.server.domain.domains.category.repository; + + +import allchive.server.domain.domains.category.domain.Category; +import allchive.server.domain.domains.category.domain.enums.Topic; +import java.util.List; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; + +public interface CategoryCustomRepository { + Slice<Category> querySliceCategoryExceptBlock( + List<Long> categoryIdList, List<Long> blockList, Topic topic, Pageable pageable); + + Slice<Category> querySliceCategoryByUserId(Long userId, Topic topic, Pageable pageable); + + Slice<Category> querySliceCategoryIn(List<Long> categoryIdList, Topic topic, Pageable pageable); + + List<Category> queryCategoryByUserId(Long userId); +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/repository/CategoryCustomRepositoryImpl.java b/Domain/src/main/java/allchive/server/domain/domains/category/repository/CategoryCustomRepositoryImpl.java new file mode 100644 index 00000000..acdb18d3 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/category/repository/CategoryCustomRepositoryImpl.java @@ -0,0 +1,134 @@ +package allchive.server.domain.domains.category.repository; + +import static allchive.server.domain.domains.category.domain.QCategory.category; + +import allchive.server.domain.common.util.SliceUtil; +import allchive.server.domain.domains.category.domain.Category; +import allchive.server.domain.domains.category.domain.enums.Topic; +import com.querydsl.core.types.OrderSpecifier; +import com.querydsl.core.types.dsl.BooleanExpression; +import com.querydsl.core.types.dsl.CaseBuilder; +import com.querydsl.core.types.dsl.NumberExpression; +import com.querydsl.jpa.impl.JPAQueryFactory; +import java.time.LocalDateTime; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; + +@RequiredArgsConstructor +public class CategoryCustomRepositoryImpl implements CategoryCustomRepository { + private final JPAQueryFactory queryFactory; + + @Override + public Slice<Category> querySliceCategoryExceptBlock( + List<Long> categoryIdList, List<Long> blockList, Topic topic, Pageable pageable) { + List<Category> categories = + queryFactory + .select(category) + .from(category) + .where( + userIdNotIn(blockList), + publicStatusTrue(), + topicEq(topic), + deleteStatusFalse()) + .orderBy(scrabListDesc(categoryIdList), scrapCntDesc(), createdAtDesc()) + .offset(pageable.getOffset()) + .limit(pageable.getPageSize() + 1) + .fetch(); + return SliceUtil.toSlice(categories, pageable); + } + + @Override + public Slice<Category> querySliceCategoryByUserId(Long userId, Topic topic, Pageable pageable) { + List<Category> categories = + queryFactory + .select(category) + .from(category) + .where(userIdEq(userId), topicEq(topic), deleteStatusFalse()) + .orderBy(pinDesc(userId), scrapCntDesc(), createdAtDesc()) + .offset(pageable.getOffset()) + .limit(pageable.getPageSize() + 1) + .fetch(); + return SliceUtil.toSlice(categories, pageable); + } + + @Override + public Slice<Category> querySliceCategoryIn( + List<Long> categoryIdList, Topic topic, Pageable pageable) { + List<Category> categories = + queryFactory + .select(category) + .from(category) + .where( + categoryIdListIn(categoryIdList), + publicStatusTrue(), + topicEq(topic), + deleteStatusFalse()) + .orderBy(scrapCntDesc(), createdAtDesc()) + .offset(pageable.getOffset()) + .limit(pageable.getPageSize() + 1) + .fetch(); + return SliceUtil.toSlice(categories, pageable); + } + + @Override + public List<Category> queryCategoryByUserId(Long userId) { + return queryFactory + .selectFrom(category) + .where(userIdEq(userId), deleteStatusFalse()) + .orderBy(topicDesc(), createdAtDesc()) + .fetch(); + } + + private BooleanExpression userIdNotIn(List<Long> blockList) { + return category.userId.notIn(blockList); + } + + private BooleanExpression publicStatusTrue() { + return category.publicStatus.eq(Boolean.TRUE); + } + + private BooleanExpression topicEq(Topic topic) { + if (topic.equals(Topic.ALL)) { + return null; + } + return category.topic.eq(topic); + } + + private BooleanExpression deleteStatusFalse() { + return category.deleteStatus.eq(Boolean.FALSE); + } + + private BooleanExpression userIdEq(Long userId) { + return category.userId.eq(userId); + } + + private BooleanExpression categoryIdListIn(List<Long> categortIdList) { + return category.id.in(categortIdList); + } + + private OrderSpecifier<Long> scrabListDesc(List<Long> categoryIdList) { + NumberExpression<Long> pinStatus = + new CaseBuilder().when(category.id.in(categoryIdList)).then(1L).otherwise(0L); + return pinStatus.desc(); + } + + private OrderSpecifier<Long> pinDesc(Long userId) { + NumberExpression<Long> pinStatus = + new CaseBuilder().when(category.pinUserId.contains(userId)).then(1L).otherwise(0L); + return pinStatus.desc(); + } + + private OrderSpecifier<Long> scrapCntDesc() { + return category.scrapCnt.desc(); + } + + private OrderSpecifier<LocalDateTime> createdAtDesc() { + return category.createdAt.desc(); + } + + private OrderSpecifier<Topic> topicDesc() { + return category.topic.desc(); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/repository/CategoryPinRepository.java b/Domain/src/main/java/allchive/server/domain/domains/category/repository/CategoryPinRepository.java deleted file mode 100644 index 0ed97972..00000000 --- a/Domain/src/main/java/allchive/server/domain/domains/category/repository/CategoryPinRepository.java +++ /dev/null @@ -1,7 +0,0 @@ -package allchive.server.domain.domains.category.repository; - - -import allchive.server.domain.domains.category.domain.CategoryPin; -import org.springframework.data.jpa.repository.JpaRepository; - -public interface CategoryPinRepository extends JpaRepository<CategoryPin, Long> {} diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/repository/CategoryRepository.java b/Domain/src/main/java/allchive/server/domain/domains/category/repository/CategoryRepository.java index 049f816e..376def57 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/category/repository/CategoryRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/category/repository/CategoryRepository.java @@ -4,4 +4,5 @@ import allchive.server.domain.domains.category.domain.Category; import org.springframework.data.jpa.repository.JpaRepository; -public interface CategoryRepository extends JpaRepository<Category, Long> {} +public interface CategoryRepository + extends JpaRepository<Category, Long>, CategoryCustomRepository {} diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/service/CategoryDomainService.java b/Domain/src/main/java/allchive/server/domain/domains/category/service/CategoryDomainService.java index 51d75c17..d6de9fd2 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/category/service/CategoryDomainService.java +++ b/Domain/src/main/java/allchive/server/domain/domains/category/service/CategoryDomainService.java @@ -16,8 +16,9 @@ public void createCategory(Category category) { categoryAdaptor.save(category); } - public void updateCategory(Category category, String title, boolean publicStatus, Topic topic) { - category.update(title, publicStatus, topic); + public void updateCategory( + Category category, String title, String imageUrl, boolean publicStatus, Topic topic) { + category.update(title, imageUrl, publicStatus, topic); categoryAdaptor.save(category); } diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/service/CategoryPinDomainService.java b/Domain/src/main/java/allchive/server/domain/domains/category/service/CategoryPinDomainService.java deleted file mode 100644 index ccea11a8..00000000 --- a/Domain/src/main/java/allchive/server/domain/domains/category/service/CategoryPinDomainService.java +++ /dev/null @@ -1,7 +0,0 @@ -package allchive.server.domain.domains.category.service; - - -import allchive.server.core.annotation.DomainService; - -@DomainService -public class CategoryPinDomainService {} diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/validator/CategoryValidator.java b/Domain/src/main/java/allchive/server/domain/domains/category/validator/CategoryValidator.java index 06b1d6c6..5e5f619b 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/category/validator/CategoryValidator.java +++ b/Domain/src/main/java/allchive/server/domain/domains/category/validator/CategoryValidator.java @@ -3,9 +3,7 @@ import allchive.server.core.annotation.Validator; import allchive.server.domain.domains.category.adaptor.CategoryAdaptor; -import allchive.server.domain.domains.category.domain.Category; import lombok.RequiredArgsConstructor; -import org.apache.catalina.security.SecurityUtil; @Validator @RequiredArgsConstructor @@ -15,4 +13,12 @@ public class CategoryValidator { public void verifyUser(Long userId, Long categoryId) { categoryAdaptor.findById(categoryId).validateUser(userId); } + + public void validationPublicStatus(Long categoryId, Long userId) { + categoryAdaptor.findById(categoryId).validatePublicStatus(userId); + } + + public void validationDeleteStatus(Long categoryId, Long userId) { + categoryAdaptor.findById(categoryId).validateDeleteStatus(userId); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/validator/TitleValidator.java b/Domain/src/main/java/allchive/server/domain/domains/category/validator/TitleValidator.java deleted file mode 100644 index fd1d6ae7..00000000 --- a/Domain/src/main/java/allchive/server/domain/domains/category/validator/TitleValidator.java +++ /dev/null @@ -1,7 +0,0 @@ -package allchive.server.domain.domains.category.validator; - - -import allchive.server.core.annotation.Validator; - -@Validator -public class TitleValidator {} diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentAdaptor.java index 08e71f81..d4c24532 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentAdaptor.java @@ -2,8 +2,18 @@ import allchive.server.core.annotation.Adaptor; +import allchive.server.domain.domains.content.domain.Content; +import allchive.server.domain.domains.content.repository.ContentRepository; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; @Adaptor @RequiredArgsConstructor -public class ContentAdaptor {} +public class ContentAdaptor { + private final ContentRepository contentRepository; + + public Slice<Content> querySliceContentByCategoryId(Long categoryId, Pageable pageable) { + return contentRepository.querySliceContentByCategoryId(categoryId, pageable); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentTagGroupAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentTagGroupAdaptor.java index 0d51a944..e443ad84 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentTagGroupAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentTagGroupAdaptor.java @@ -2,8 +2,18 @@ import allchive.server.core.annotation.Adaptor; +import allchive.server.domain.domains.content.domain.Content; +import allchive.server.domain.domains.content.domain.ContentTagGroup; +import allchive.server.domain.domains.content.repository.ContentTagGroupRepository; +import java.util.List; import lombok.RequiredArgsConstructor; @Adaptor @RequiredArgsConstructor -public class ContentTagGroupAdaptor {} +public class ContentTagGroupAdaptor { + private final ContentTagGroupRepository contentTagGroupRepository; + + public List<ContentTagGroup> queryContentIn(List<Content> contentList) { + return contentTagGroupRepository.queryContentTagGroupIn(contentList); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/domain/Content.java b/Domain/src/main/java/allchive/server/domain/domains/content/domain/Content.java index bf06c879..9e0606c9 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/domain/Content.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/domain/Content.java @@ -2,6 +2,7 @@ import allchive.server.domain.common.model.BaseTimeEntity; +import allchive.server.domain.domains.content.domain.enums.ContentType; import javax.persistence.*; import lombok.AccessLevel; import lombok.Getter; @@ -15,4 +16,18 @@ public class Content extends BaseTimeEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + + private Long categoryId; + + @Enumerated(EnumType.STRING) + private ContentType contentType; + + private String imageUrl; + private String linkUrl; + private String title; + + @Column(columnDefinition = "TEXT") + private String memo; + + private boolean deleteStatus = Boolean.FALSE; } diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/domain/ContentTagGroup.java b/Domain/src/main/java/allchive/server/domain/domains/content/domain/ContentTagGroup.java index 9195bc7d..4aff2c9c 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/domain/ContentTagGroup.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/domain/ContentTagGroup.java @@ -15,4 +15,10 @@ public class ContentTagGroup extends BaseTimeEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + private Content content; + + @ManyToOne(fetch = FetchType.LAZY) + private Tag tag; } diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/domain/Tag.java b/Domain/src/main/java/allchive/server/domain/domains/content/domain/Tag.java index c846f217..0439c235 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/domain/Tag.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/domain/Tag.java @@ -15,4 +15,7 @@ public class Tag extends BaseTimeEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + + private String name; + private Long userId; } diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/domain/enums/.gitkeep b/Domain/src/main/java/allchive/server/domain/domains/content/domain/enums/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/domain/enums/ContentType.java b/Domain/src/main/java/allchive/server/domain/domains/content/domain/enums/ContentType.java new file mode 100644 index 00000000..d0b206c1 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/content/domain/enums/ContentType.java @@ -0,0 +1,26 @@ +package allchive.server.domain.domains.content.domain.enums; + + +import allchive.server.domain.domains.user.domain.enums.OauthProvider; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import java.util.stream.Stream; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum ContentType { + IMAGE("image"), + LINK("link"); + + @JsonValue private String value; + + @JsonCreator + public static OauthProvider parsing(String inputValue) { + return Stream.of(OauthProvider.values()) + .filter(category -> category.getValue().equals(inputValue)) + .findFirst() + .orElse(null); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentCustomRepository.java b/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentCustomRepository.java new file mode 100644 index 00000000..477f738c --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentCustomRepository.java @@ -0,0 +1,10 @@ +package allchive.server.domain.domains.content.repository; + + +import allchive.server.domain.domains.content.domain.Content; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; + +public interface ContentCustomRepository { + Slice<Content> querySliceContentByCategoryId(Long categoryId, Pageable pageable); +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentCustomRepositoryImpl.java b/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentCustomRepositoryImpl.java new file mode 100644 index 00000000..24165d61 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentCustomRepositoryImpl.java @@ -0,0 +1,40 @@ +package allchive.server.domain.domains.content.repository; + +import static allchive.server.domain.domains.content.domain.QContent.content; + +import allchive.server.domain.common.util.SliceUtil; +import allchive.server.domain.domains.content.domain.Content; +import com.querydsl.core.types.OrderSpecifier; +import com.querydsl.core.types.dsl.BooleanExpression; +import com.querydsl.jpa.impl.JPAQueryFactory; +import java.time.LocalDateTime; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; + +@RequiredArgsConstructor +public class ContentCustomRepositoryImpl implements ContentCustomRepository { + private final JPAQueryFactory queryFactory; + + @Override + public Slice<Content> querySliceContentByCategoryId(Long categoryId, Pageable pageable) { + List<Content> categories = + queryFactory + .selectFrom(content) + .where(categoryIdEq(categoryId)) + .orderBy(createdAtDesc()) + .offset(pageable.getOffset()) + .limit(pageable.getPageSize() + 1) + .fetch(); + return SliceUtil.toSlice(categories, pageable); + } + + private BooleanExpression categoryIdEq(Long categoryId) { + return content.categoryId.eq(categoryId); + } + + private OrderSpecifier<LocalDateTime> createdAtDesc() { + return content.createdAt.desc(); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentRepository.java b/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentRepository.java index 39473620..65eaad9a 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentRepository.java @@ -4,4 +4,4 @@ import allchive.server.domain.domains.content.domain.Content; import org.springframework.data.jpa.repository.JpaRepository; -public interface ContentRepository extends JpaRepository<Content, Long> {} +public interface ContentRepository extends JpaRepository<Content, Long>, ContentCustomRepository {} diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentTagGroupCustomRepository.java b/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentTagGroupCustomRepository.java new file mode 100644 index 00000000..bdafbe86 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentTagGroupCustomRepository.java @@ -0,0 +1,10 @@ +package allchive.server.domain.domains.content.repository; + + +import allchive.server.domain.domains.content.domain.Content; +import allchive.server.domain.domains.content.domain.ContentTagGroup; +import java.util.List; + +public interface ContentTagGroupCustomRepository { + public List<ContentTagGroup> queryContentTagGroupIn(List<Content> contentList); +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentTagGroupCustomRepositoryImpl.java b/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentTagGroupCustomRepositoryImpl.java new file mode 100644 index 00000000..e7a5b4d4 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentTagGroupCustomRepositoryImpl.java @@ -0,0 +1,37 @@ +package allchive.server.domain.domains.content.repository; + +import static allchive.server.domain.domains.content.domain.QContentTagGroup.contentTagGroup; +import static allchive.server.domain.domains.content.domain.QTag.tag; + +import allchive.server.domain.domains.content.domain.Content; +import allchive.server.domain.domains.content.domain.ContentTagGroup; +import com.querydsl.core.types.OrderSpecifier; +import com.querydsl.core.types.dsl.BooleanExpression; +import com.querydsl.jpa.impl.JPAQueryFactory; +import java.time.LocalDateTime; +import java.util.List; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +public class ContentTagGroupCustomRepositoryImpl implements ContentTagGroupCustomRepository { + private final JPAQueryFactory queryFactory; + + @Override + public List<ContentTagGroup> queryContentTagGroupIn(List<Content> contentList) { + return queryFactory + .selectFrom(contentTagGroup) + .join(contentTagGroup.tag, tag) + .fetchJoin() + .where(categoryIdEq(contentList)) + .orderBy(createdAtDesc()) + .fetch(); + } + + private BooleanExpression categoryIdEq(List<Content> contentList) { + return contentTagGroup.content.in(contentList); + } + + private OrderSpecifier<LocalDateTime> createdAtDesc() { + return contentTagGroup.createdAt.desc(); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentTagGroupRepository.java b/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentTagGroupRepository.java index e8705be1..262113e9 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentTagGroupRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentTagGroupRepository.java @@ -4,4 +4,5 @@ import allchive.server.domain.domains.content.domain.ContentTagGroup; import org.springframework.data.jpa.repository.JpaRepository; -public interface ContentTagGroupRepository extends JpaRepository<ContentTagGroup, Long> {} +public interface ContentTagGroupRepository + extends JpaRepository<ContentTagGroup, Long>, ContentTagGroupCustomRepository {} diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/ScrapAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/ScrapAdaptor.java index 6bf5674c..86634d0a 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/ScrapAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/ScrapAdaptor.java @@ -2,8 +2,17 @@ import allchive.server.core.annotation.Adaptor; +import allchive.server.domain.domains.user.domain.Scrap; +import allchive.server.domain.domains.user.repository.ScrapRepository; +import java.util.List; import lombok.RequiredArgsConstructor; @Adaptor @RequiredArgsConstructor -public class ScrapAdaptor {} +public class ScrapAdaptor { + private final ScrapRepository scrapRepository; + + public List<Scrap> findAllByUserId(Long userId) { + return scrapRepository.findAllByUserId(userId); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/domain/Scrap.java b/Domain/src/main/java/allchive/server/domain/domains/user/domain/Scrap.java index 6aaa0d83..12c95798 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/domain/Scrap.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/domain/Scrap.java @@ -15,4 +15,9 @@ public class Scrap extends BaseTimeEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + private User user; + + private Long categoryId; } diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/repository/ScrapRepository.java b/Domain/src/main/java/allchive/server/domain/domains/user/repository/ScrapRepository.java index 29c7012d..c4a9d0f4 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/repository/ScrapRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/repository/ScrapRepository.java @@ -2,6 +2,9 @@ import allchive.server.domain.domains.user.domain.Scrap; +import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; -public interface ScrapRepository extends JpaRepository<Scrap, Long> {} +public interface ScrapRepository extends JpaRepository<Scrap, Long> { + List<Scrap> findAllByUserId(Long userId); +} From ee9311afd9a1604ee51e8247c40a5ec327e8558c Mon Sep 17 00:00:00 2001 From: Sanghoon Jeong <67852689+wjdtkdgns@users.noreply.github.com> Date: Wed, 5 Jul 2023 11:15:12 +0900 Subject: [PATCH 23/41] =?UTF-8?q?[feat]=20category=20=EC=8A=A4=ED=81=AC?= =?UTF-8?q?=EB=9E=A9,=20=EA=B3=A0=EC=A0=95=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20(#22)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [feat] scrap 여부 변경 기능 구현 #19 * [feat] pin 기능 구현 #19 --- .../controller/CategoryController.java | 16 ++++++++ .../service/GetCategoryContentsUseCase.java | 4 +- .../service/UpdateCategoryPinUseCase.java | 30 ++++++++++++++ .../service/UpdateCategoryScrapUseCase.java | 41 +++++++++++++++++++ .../category/adaptor/CategoryAdaptor.java | 4 ++ .../domains/category/domain/Category.java | 14 ++++++- .../category/exception/CategoryErrorCode.java | 9 ++-- .../AlreadyPinnedCategoryException.java | 14 +++++++ .../NotPinnedCategoryException.java | 14 +++++++ .../repository/CategoryCustomRepository.java | 2 + .../CategoryCustomRepositoryImpl.java | 11 +++++ .../service/CategoryDomainService.java | 16 ++++++++ .../category/validator/CategoryValidator.java | 25 ++++++++++- .../domains/user/adaptor/ScrapAdaptor.java | 14 +++++++ .../domain/domains/user/domain/Scrap.java | 11 +++++ .../user/exception/ScrapErrorCode.java | 21 ---------- .../domains/user/exception/UserErrorCode.java | 7 +++- .../AlreadyExistScrapException.java | 14 +++++++ .../exceptions/ScrapNotFoundException.java | 14 +++++++ .../user/repository/ScrapRepository.java | 4 ++ .../user/service/ScrapDomainService.java | 28 +++++++++++++ .../domains/user/service/ScrapService.java | 7 ---- .../user/validator/ScrapValidator.java | 15 ++++++- 23 files changed, 297 insertions(+), 38 deletions(-) create mode 100644 Api/src/main/java/allchive/server/api/category/service/UpdateCategoryPinUseCase.java create mode 100644 Api/src/main/java/allchive/server/api/category/service/UpdateCategoryScrapUseCase.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/category/exception/exceptions/AlreadyPinnedCategoryException.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/category/exception/exceptions/NotPinnedCategoryException.java delete mode 100644 Domain/src/main/java/allchive/server/domain/domains/user/exception/ScrapErrorCode.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/user/exception/exceptions/AlreadyExistScrapException.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/user/exception/exceptions/ScrapNotFoundException.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/user/service/ScrapDomainService.java delete mode 100644 Domain/src/main/java/allchive/server/domain/domains/user/service/ScrapService.java diff --git a/Api/src/main/java/allchive/server/api/category/controller/CategoryController.java b/Api/src/main/java/allchive/server/api/category/controller/CategoryController.java index 2b24efee..9032b0db 100644 --- a/Api/src/main/java/allchive/server/api/category/controller/CategoryController.java +++ b/Api/src/main/java/allchive/server/api/category/controller/CategoryController.java @@ -32,6 +32,8 @@ public class CategoryController { private final GetScrapCategoryUseCase getScrapCategoryUseCase; private final GetCategoryTitleUseCase getCategoryTitleUseCase; private final GetCategoryContentsUseCase getCategoryContentsUseCase; + private final UpdateCategoryScrapUseCase updateCategoryScrapUseCase; + private final UpdateCategoryPinUseCase updateCategoryPinUseCase; @Operation(summary = "카테고리를 생성합니다.") @PostMapping() @@ -96,4 +98,18 @@ public CategoryContentsResponse getCategoryContents( @ParameterObject @PageableDefault(size = 10) Pageable pageable) { return getCategoryContentsUseCase.execute(categoryId, pageable); } + + @Operation(summary = "카테고리를 스크랩합니다.", description = "스크랩 취소면 cancel에 true 값 보내주세요") + @PatchMapping(value = "/{categoryId}/scrap") + public void updateCategoryScrap( + @RequestParam("cancel") Boolean cancel, @PathVariable("categoryId") Long categoryId) { + updateCategoryScrapUseCase.execute(categoryId, cancel); + } + + @Operation(summary = "카테고리를 고정합니다.", description = "고정 취소면 cancel에 true 값 보내주세요") + @PatchMapping(value = "/{categoryId}/pin") + public void updateCategoryPin( + @RequestParam("cancel") Boolean cancel, @PathVariable("categoryId") Long categoryId) { + updateCategoryPinUseCase.execute(categoryId, cancel); + } } diff --git a/Api/src/main/java/allchive/server/api/category/service/GetCategoryContentsUseCase.java b/Api/src/main/java/allchive/server/api/category/service/GetCategoryContentsUseCase.java index d140db7b..c34643e8 100644 --- a/Api/src/main/java/allchive/server/api/category/service/GetCategoryContentsUseCase.java +++ b/Api/src/main/java/allchive/server/api/category/service/GetCategoryContentsUseCase.java @@ -32,8 +32,8 @@ public class GetCategoryContentsUseCase { @Transactional(readOnly = true) public CategoryContentsResponse execute(Long categoryId, Pageable pageable) { Long userId = SecurityUtil.getCurrentUserId(); - categoryValidator.validationPublicStatus(categoryId, userId); - categoryValidator.validationDeleteStatus(categoryId, userId); + categoryValidator.validatePublicStatus(categoryId, userId); + categoryValidator.validateDeleteStatus(categoryId, userId); Category category = categoryAdaptor.findById(categoryId); Slice<Content> contentList = contentAdaptor.querySliceContentByCategoryId(categoryId, pageable); diff --git a/Api/src/main/java/allchive/server/api/category/service/UpdateCategoryPinUseCase.java b/Api/src/main/java/allchive/server/api/category/service/UpdateCategoryPinUseCase.java new file mode 100644 index 00000000..cd1e6792 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/category/service/UpdateCategoryPinUseCase.java @@ -0,0 +1,30 @@ +package allchive.server.api.category.service; + + +import allchive.server.api.config.security.SecurityUtil; +import allchive.server.core.annotation.UseCase; +import allchive.server.domain.domains.category.service.CategoryDomainService; +import allchive.server.domain.domains.category.validator.CategoryValidator; +import lombok.RequiredArgsConstructor; +import org.springframework.transaction.annotation.Transactional; + +@UseCase +@RequiredArgsConstructor +public class UpdateCategoryPinUseCase { + private final CategoryValidator categoryValidator; + private final CategoryDomainService categoryDomainService; + + @Transactional + public void execute(Long categoryId, Boolean cancel) { + Long userId = SecurityUtil.getCurrentUserId(); + categoryValidator.validateExistCategory(categoryId); + categoryValidator.validateDeleteStatus(categoryId, userId); + if (cancel) { + categoryValidator.validateNotPinStatus(categoryId, userId); + categoryDomainService.updatePin(categoryId, userId, false); + } else { + categoryValidator.validateAlreadyPinStatus(categoryId, userId); + categoryDomainService.updatePin(categoryId, userId, true); + } + } +} diff --git a/Api/src/main/java/allchive/server/api/category/service/UpdateCategoryScrapUseCase.java b/Api/src/main/java/allchive/server/api/category/service/UpdateCategoryScrapUseCase.java new file mode 100644 index 00000000..0a3fd39a --- /dev/null +++ b/Api/src/main/java/allchive/server/api/category/service/UpdateCategoryScrapUseCase.java @@ -0,0 +1,41 @@ +package allchive.server.api.category.service; + + +import allchive.server.api.config.security.SecurityUtil; +import allchive.server.core.annotation.UseCase; +import allchive.server.domain.domains.category.service.CategoryDomainService; +import allchive.server.domain.domains.category.validator.CategoryValidator; +import allchive.server.domain.domains.user.adaptor.UserAdaptor; +import allchive.server.domain.domains.user.domain.Scrap; +import allchive.server.domain.domains.user.domain.User; +import allchive.server.domain.domains.user.service.ScrapDomainService; +import allchive.server.domain.domains.user.validator.ScrapValidator; +import lombok.RequiredArgsConstructor; +import org.springframework.transaction.annotation.Transactional; + +@UseCase +@RequiredArgsConstructor +public class UpdateCategoryScrapUseCase { + private final CategoryValidator categoryValidator; + private final UserAdaptor userAdaptor; + private final ScrapDomainService scrapDomainService; + private final CategoryDomainService categoryDomainService; + private final ScrapValidator scrapValidator; + + @Transactional + public void execute(Long categoryId, Boolean cancel) { + categoryValidator.validateExistCategory(categoryId); + Long userId = SecurityUtil.getCurrentUserId(); + categoryValidator.validateDeleteStatus(categoryId, userId); + User user = userAdaptor.queryUserById(userId); + if (cancel) { + scrapDomainService.deleteScrapByUserAndCategoryId(user, categoryId); + categoryDomainService.updateScrapCount(categoryId, -1); + } else { + scrapValidator.validateExistScrap(user, categoryId); + Scrap scrap = Scrap.of(user, categoryId); + scrapDomainService.save(scrap); + categoryDomainService.updateScrapCount(categoryId, 1); + } + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/adaptor/CategoryAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/category/adaptor/CategoryAdaptor.java index 8a7d9b1f..2a30b1d5 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/category/adaptor/CategoryAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/category/adaptor/CategoryAdaptor.java @@ -52,4 +52,8 @@ public Slice<Category> querySliceCategoryIn( public List<Category> queryCategoryByUserId(Long userId) { return categoryRepository.queryCategoryByUserId(userId); } + + public boolean queryCategoryExist(Long categoryId) { + return categoryRepository.queryCategoryExist(categoryId); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/domain/Category.java b/Domain/src/main/java/allchive/server/domain/domains/category/domain/Category.java index b021f6dd..c16b22fc 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/category/domain/Category.java +++ b/Domain/src/main/java/allchive/server/domain/domains/category/domain/Category.java @@ -92,8 +92,20 @@ public void validatePublicStatus(Long userId) { } public void validateDeleteStatus(Long userId) { - if (!this.publicStatus && !this.userId.equals(userId)) { + if (this.deleteStatus && !this.userId.equals(userId)) { throw DeletedCategoryException.EXCEPTION; } } + + public void updateScrapCnt(int i) { + this.scrapCnt += i; + } + + public void addPinUserId(Long userId) { + this.getPinUserId().add(userId); + } + + public void deletePinUserId(Long userId) { + this.getPinUserId().remove(userId); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/exception/CategoryErrorCode.java b/Domain/src/main/java/allchive/server/domain/domains/category/exception/CategoryErrorCode.java index b6c9451f..8d5adefc 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/category/exception/CategoryErrorCode.java +++ b/Domain/src/main/java/allchive/server/domain/domains/category/exception/CategoryErrorCode.java @@ -10,11 +10,14 @@ @Getter @AllArgsConstructor public enum CategoryErrorCode implements BaseErrorCode { - CATEGORY_NOT_FOUND(BAD_REQUEST, "CATEGORY_400_1", "카테고리 정보를 찾을 수 없습니다."), - NOT_PUBLIC_CATEGORY(BAD_REQUEST, "CATEGORY_400_2", "공개된 카테고리가 아닙니다."), + NOT_PUBLIC_CATEGORY(BAD_REQUEST, "CATEGORY_400_1", "공개된 카테고리가 아닙니다."), DELETED_CATEGORY(BAD_REQUEST, "CATEGORY_400_2", "삭제된 카테고리입니다."), + ALREADY_PINNED_CATEGORY(BAD_REQUEST, "CATEGORY_400_3", "이미 고정 카테고리입니다."), + NOT_PINNED_CATEGORY(BAD_REQUEST, "CATEGORY_400_4", "고정되지않은 카테고리입니다."), - NO_AUTHORITY_UPDATE_CATEGORY(FORBIDDEN, "CATEGORY_403_1", "카테고리 수정 권한이 없습니다."); + NO_AUTHORITY_UPDATE_CATEGORY(FORBIDDEN, "CATEGORY_403_1", "카테고리 수정 권한이 없습니다."), + + CATEGORY_NOT_FOUND(NOT_FOUND, "CATEGORY_404_1", "카테고리를 찾을 수 없습니다."); private int status; private String code; private String reason; diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/exception/exceptions/AlreadyPinnedCategoryException.java b/Domain/src/main/java/allchive/server/domain/domains/category/exception/exceptions/AlreadyPinnedCategoryException.java new file mode 100644 index 00000000..ac19dcf7 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/category/exception/exceptions/AlreadyPinnedCategoryException.java @@ -0,0 +1,14 @@ +package allchive.server.domain.domains.category.exception.exceptions; + + +import allchive.server.core.error.BaseErrorException; +import allchive.server.domain.domains.category.exception.CategoryErrorCode; + +public class AlreadyPinnedCategoryException extends BaseErrorException { + + public static final BaseErrorException EXCEPTION = new AlreadyPinnedCategoryException(); + + private AlreadyPinnedCategoryException() { + super(CategoryErrorCode.ALREADY_PINNED_CATEGORY); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/exception/exceptions/NotPinnedCategoryException.java b/Domain/src/main/java/allchive/server/domain/domains/category/exception/exceptions/NotPinnedCategoryException.java new file mode 100644 index 00000000..c30637f2 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/category/exception/exceptions/NotPinnedCategoryException.java @@ -0,0 +1,14 @@ +package allchive.server.domain.domains.category.exception.exceptions; + + +import allchive.server.core.error.BaseErrorException; +import allchive.server.domain.domains.category.exception.CategoryErrorCode; + +public class NotPinnedCategoryException extends BaseErrorException { + + public static final BaseErrorException EXCEPTION = new NotPinnedCategoryException(); + + private NotPinnedCategoryException() { + super(CategoryErrorCode.NOT_PINNED_CATEGORY); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/repository/CategoryCustomRepository.java b/Domain/src/main/java/allchive/server/domain/domains/category/repository/CategoryCustomRepository.java index 0bfe0f79..695dc722 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/category/repository/CategoryCustomRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/category/repository/CategoryCustomRepository.java @@ -16,4 +16,6 @@ Slice<Category> querySliceCategoryExceptBlock( Slice<Category> querySliceCategoryIn(List<Long> categoryIdList, Topic topic, Pageable pageable); List<Category> queryCategoryByUserId(Long userId); + + boolean queryCategoryExist(Long categoryId); } diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/repository/CategoryCustomRepositoryImpl.java b/Domain/src/main/java/allchive/server/domain/domains/category/repository/CategoryCustomRepositoryImpl.java index acdb18d3..4f7393b9 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/category/repository/CategoryCustomRepositoryImpl.java +++ b/Domain/src/main/java/allchive/server/domain/domains/category/repository/CategoryCustomRepositoryImpl.java @@ -81,6 +81,13 @@ public List<Category> queryCategoryByUserId(Long userId) { .fetch(); } + @Override + public boolean queryCategoryExist(Long categoryId) { + Category fetchOne = + queryFactory.selectFrom(category).where(categoryIdEq(categoryId)).fetchFirst(); + return fetchOne != null; + } + private BooleanExpression userIdNotIn(List<Long> blockList) { return category.userId.notIn(blockList); } @@ -108,6 +115,10 @@ private BooleanExpression categoryIdListIn(List<Long> categortIdList) { return category.id.in(categortIdList); } + private BooleanExpression categoryIdEq(Long categoryId) { + return category.id.eq(categoryId); + } + private OrderSpecifier<Long> scrabListDesc(List<Long> categoryIdList) { NumberExpression<Long> pinStatus = new CaseBuilder().when(category.id.in(categoryIdList)).then(1L).otherwise(0L); diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/service/CategoryDomainService.java b/Domain/src/main/java/allchive/server/domain/domains/category/service/CategoryDomainService.java index d6de9fd2..d3e503cc 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/category/service/CategoryDomainService.java +++ b/Domain/src/main/java/allchive/server/domain/domains/category/service/CategoryDomainService.java @@ -25,4 +25,20 @@ public void updateCategory( public void deleteCategory(Long categoryId) { categoryAdaptor.deleteById(categoryId); } + + public void updateScrapCount(Long categoryId, int i) { + Category category = categoryAdaptor.findById(categoryId); + category.updateScrapCnt(i); + categoryAdaptor.save(category); + } + + public void updatePin(Long categoryId, Long userId, boolean pin) { + Category category = categoryAdaptor.findById(categoryId); + if (pin) { + category.addPinUserId(userId); + } else { + category.deletePinUserId(userId); + } + category.updateScrapCnt(pin ? 1 : -1); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/validator/CategoryValidator.java b/Domain/src/main/java/allchive/server/domain/domains/category/validator/CategoryValidator.java index 5e5f619b..51fdbf2b 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/category/validator/CategoryValidator.java +++ b/Domain/src/main/java/allchive/server/domain/domains/category/validator/CategoryValidator.java @@ -3,6 +3,9 @@ import allchive.server.core.annotation.Validator; import allchive.server.domain.domains.category.adaptor.CategoryAdaptor; +import allchive.server.domain.domains.category.exception.exceptions.AlreadyPinnedCategoryException; +import allchive.server.domain.domains.category.exception.exceptions.CategoryNotFoundException; +import allchive.server.domain.domains.category.exception.exceptions.NotPinnedCategoryException; import lombok.RequiredArgsConstructor; @Validator @@ -14,11 +17,29 @@ public void verifyUser(Long userId, Long categoryId) { categoryAdaptor.findById(categoryId).validateUser(userId); } - public void validationPublicStatus(Long categoryId, Long userId) { + public void validatePublicStatus(Long categoryId, Long userId) { categoryAdaptor.findById(categoryId).validatePublicStatus(userId); } - public void validationDeleteStatus(Long categoryId, Long userId) { + public void validateDeleteStatus(Long categoryId, Long userId) { categoryAdaptor.findById(categoryId).validateDeleteStatus(userId); } + + public void validateExistCategory(Long categoryId) { + if (!categoryAdaptor.queryCategoryExist(categoryId)) { + throw CategoryNotFoundException.EXCEPTION; + } + } + + public void validateAlreadyPinStatus(Long categoryId, Long userId) { + if (categoryAdaptor.findById(categoryId).getPinUserId().contains(userId)) { + throw AlreadyPinnedCategoryException.EXCEPTION; + } + } + + public void validateNotPinStatus(Long categoryId, Long userId) { + if (!categoryAdaptor.findById(categoryId).getPinUserId().contains(userId)) { + throw NotPinnedCategoryException.EXCEPTION; + } + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/ScrapAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/ScrapAdaptor.java index 86634d0a..6e626bbe 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/ScrapAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/ScrapAdaptor.java @@ -3,8 +3,10 @@ import allchive.server.core.annotation.Adaptor; import allchive.server.domain.domains.user.domain.Scrap; +import allchive.server.domain.domains.user.domain.User; import allchive.server.domain.domains.user.repository.ScrapRepository; import java.util.List; +import java.util.Optional; import lombok.RequiredArgsConstructor; @Adaptor @@ -15,4 +17,16 @@ public class ScrapAdaptor { public List<Scrap> findAllByUserId(Long userId) { return scrapRepository.findAllByUserId(userId); } + + public Optional<Scrap> findByUserAndCategoryId(User user, Long categoryId) { + return scrapRepository.findAllByUserAndCategoryId(user, categoryId); + } + + public void delete(Scrap scrap) { + scrapRepository.delete(scrap); + } + + public void save(Scrap scrap) { + scrapRepository.save(scrap); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/domain/Scrap.java b/Domain/src/main/java/allchive/server/domain/domains/user/domain/Scrap.java index 12c95798..dd7d98da 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/domain/Scrap.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/domain/Scrap.java @@ -4,6 +4,7 @@ import allchive.server.domain.common.model.BaseTimeEntity; import javax.persistence.*; import lombok.AccessLevel; +import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; @@ -20,4 +21,14 @@ public class Scrap extends BaseTimeEntity { private User user; private Long categoryId; + + @Builder + private Scrap(User user, Long categoryId) { + this.user = user; + this.categoryId = categoryId; + } + + public static Scrap of(User user, Long categoryId) { + return Scrap.builder().user(user).categoryId(categoryId).build(); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/exception/ScrapErrorCode.java b/Domain/src/main/java/allchive/server/domain/domains/user/exception/ScrapErrorCode.java deleted file mode 100644 index 795dfaaf..00000000 --- a/Domain/src/main/java/allchive/server/domain/domains/user/exception/ScrapErrorCode.java +++ /dev/null @@ -1,21 +0,0 @@ -package allchive.server.domain.domains.user.exception; - - -import allchive.server.core.dto.ErrorReason; -import allchive.server.core.error.BaseErrorCode; -import lombok.AllArgsConstructor; -import lombok.Getter; - -@Getter -@AllArgsConstructor -public enum ScrapErrorCode implements BaseErrorCode { - ; - private int status; - private String code; - private String reason; - - @Override - public ErrorReason getErrorReason() { - return ErrorReason.of(status, code, reason); - } -} diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/exception/UserErrorCode.java b/Domain/src/main/java/allchive/server/domain/domains/user/exception/UserErrorCode.java index 9cb9f537..f960ae09 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/exception/UserErrorCode.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/exception/UserErrorCode.java @@ -14,7 +14,12 @@ public enum UserErrorCode implements BaseErrorCode { FORBIDDEN_USER(BAD_REQUEST, "USER_400_1", "접근 제한된 유저입니다."), USER_ALREADY_SIGNUP(BAD_REQUEST, "USER_400_2", "이미 회원가입한 유저입니다."), USER_ALREADY_DELETED(BAD_REQUEST, "USER_400_3", "이미 지워진 유저입니다."), - USER_NOT_FOUND(NOT_FOUND, "USER_404_1", "유저 정보를 찾을 수 없습니다."); + + USER_NOT_FOUND(NOT_FOUND, "USER_404_1", "유저 정보를 찾을 수 없습니다."), + + ALREADY_EXIST_SCRAP(BAD_REQUEST, "SCRAP_400_1", "이미 스크랩 되어있습니다."), + + SCRAP_NOT_FOUND(NOT_FOUND, "SCRAP_404_1", "스크랩 정보를 찾을 수 없습니다."); private int status; private String code; private String reason; diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/exception/exceptions/AlreadyExistScrapException.java b/Domain/src/main/java/allchive/server/domain/domains/user/exception/exceptions/AlreadyExistScrapException.java new file mode 100644 index 00000000..50845602 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/user/exception/exceptions/AlreadyExistScrapException.java @@ -0,0 +1,14 @@ +package allchive.server.domain.domains.user.exception.exceptions; + + +import allchive.server.core.error.BaseErrorException; +import allchive.server.domain.domains.user.exception.UserErrorCode; + +public class AlreadyExistScrapException extends BaseErrorException { + + public static final BaseErrorException EXCEPTION = new AlreadyExistScrapException(); + + private AlreadyExistScrapException() { + super(UserErrorCode.ALREADY_EXIST_SCRAP); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/exception/exceptions/ScrapNotFoundException.java b/Domain/src/main/java/allchive/server/domain/domains/user/exception/exceptions/ScrapNotFoundException.java new file mode 100644 index 00000000..65386ee1 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/user/exception/exceptions/ScrapNotFoundException.java @@ -0,0 +1,14 @@ +package allchive.server.domain.domains.user.exception.exceptions; + + +import allchive.server.core.error.BaseErrorException; +import allchive.server.domain.domains.user.exception.UserErrorCode; + +public class ScrapNotFoundException extends BaseErrorException { + + public static final BaseErrorException EXCEPTION = new ScrapNotFoundException(); + + private ScrapNotFoundException() { + super(UserErrorCode.SCRAP_NOT_FOUND); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/repository/ScrapRepository.java b/Domain/src/main/java/allchive/server/domain/domains/user/repository/ScrapRepository.java index c4a9d0f4..654ec699 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/repository/ScrapRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/repository/ScrapRepository.java @@ -2,9 +2,13 @@ import allchive.server.domain.domains.user.domain.Scrap; +import allchive.server.domain.domains.user.domain.User; import java.util.List; +import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; public interface ScrapRepository extends JpaRepository<Scrap, Long> { List<Scrap> findAllByUserId(Long userId); + + Optional<Scrap> findAllByUserAndCategoryId(User user, Long categoryId); } diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/service/ScrapDomainService.java b/Domain/src/main/java/allchive/server/domain/domains/user/service/ScrapDomainService.java new file mode 100644 index 00000000..1b9c5858 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/user/service/ScrapDomainService.java @@ -0,0 +1,28 @@ +package allchive.server.domain.domains.user.service; + + +import allchive.server.core.annotation.DomainService; +import allchive.server.domain.domains.user.adaptor.ScrapAdaptor; +import allchive.server.domain.domains.user.domain.Scrap; +import allchive.server.domain.domains.user.domain.User; +import allchive.server.domain.domains.user.exception.exceptions.ScrapNotFoundException; +import lombok.RequiredArgsConstructor; + +@DomainService +@RequiredArgsConstructor +public class ScrapDomainService { + private final ScrapAdaptor scrapAdaptor; + + public void deleteScrapByUserAndCategoryId(User user, Long categoryId) { + Scrap scrap = + scrapAdaptor + .findByUserAndCategoryId(user, categoryId) + .orElseThrow(() -> ScrapNotFoundException.EXCEPTION); + ; + scrapAdaptor.delete(scrap); + } + + public void save(Scrap scrap) { + scrapAdaptor.save(scrap); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/service/ScrapService.java b/Domain/src/main/java/allchive/server/domain/domains/user/service/ScrapService.java deleted file mode 100644 index de52b5e4..00000000 --- a/Domain/src/main/java/allchive/server/domain/domains/user/service/ScrapService.java +++ /dev/null @@ -1,7 +0,0 @@ -package allchive.server.domain.domains.user.service; - - -import allchive.server.core.annotation.DomainService; - -@DomainService -public class ScrapService {} diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/validator/ScrapValidator.java b/Domain/src/main/java/allchive/server/domain/domains/user/validator/ScrapValidator.java index 3ee54099..1396b7e1 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/validator/ScrapValidator.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/validator/ScrapValidator.java @@ -2,6 +2,19 @@ import allchive.server.core.annotation.Validator; +import allchive.server.domain.domains.user.adaptor.ScrapAdaptor; +import allchive.server.domain.domains.user.domain.User; +import allchive.server.domain.domains.user.exception.exceptions.AlreadyExistScrapException; +import lombok.RequiredArgsConstructor; @Validator -public class ScrapValidator {} +@RequiredArgsConstructor +public class ScrapValidator { + private final ScrapAdaptor scrapAdaptor; + + public void validateExistScrap(User user, Long categoryId) { + if (scrapAdaptor.findByUserAndCategoryId(user, categoryId).isPresent()) { + throw AlreadyExistScrapException.EXCEPTION; + } + } +} From 02e987c505a4a0082265115d21debf95ca17d952 Mon Sep 17 00:00:00 2001 From: Sanghoon Jeong <67852689+wjdtkdgns@users.noreply.github.com> Date: Fri, 7 Jul 2023 12:28:10 +0900 Subject: [PATCH 24/41] =?UTF-8?q?[feat]=20presigned=20=EA=B5=AC=ED=98=84?= =?UTF-8?q?=20=EB=B0=8F=20topic=20->=20subject=20=EB=84=A4=EC=9D=B4?= =?UTF-8?q?=EB=B0=8D=20=EB=B3=80=EA=B2=BD=20(#24)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [feat] s3 셋팅 #17 * [feat] category presigned url 구현 #17 * [feat] asset url 적용 #17 * [feat] 컨텐츠, 유저 프로필 이미지 presigned url 구현 #17 * [chore] spotless 적용 #17 * [refac] topic -> subject 네이밍 변경 #23 --- .../model/dto/request/RegisterRequest.java | 8 ++- .../auth/service/OauthRegisterUseCase.java | 2 +- .../controller/CategoryController.java | 16 ++--- .../dto/request/CreateCategoryRequest.java | 6 +- .../dto/request/UpdateCategoryRequest.java | 6 +- .../model/dto/response/CategoryResponse.java | 19 ++++-- .../category/model/mapper/CategoryMapper.java | 4 +- .../service/GetArchivedCategoryUseCase.java | 6 +- .../category/service/GetCategoryUseCase.java | 6 +- .../service/GetScrapCategoryUseCase.java | 6 +- .../service/UpdateCategoryUseCase.java | 2 +- .../server/api/common/util/UrlUtil.java | 25 +++++++ .../api/image/controller/ImageController.java | 45 +++++++++++++ .../model/dto/response/ImageUrlResponse.java | 29 ++++++++ .../server/core/consts/AllchiveConst.java | 3 + .../category/adaptor/CategoryAdaptor.java | 17 ++--- .../domains/category/domain/Category.java | 16 ++--- .../domain/enums/{Topic.java => Subject.java} | 2 +- .../repository/CategoryCustomRepository.java | 9 +-- .../CategoryCustomRepositoryImpl.java | 27 ++++---- .../service/CategoryDomainService.java | 10 ++- .../domain/domains/user/domain/User.java | 6 +- Infrastructure/build.gradle | 1 + .../server/infrastructure/s3/ImageUrlDto.java | 21 ++++++ .../infrastructure/s3/PresignedType.java | 7 ++ .../server/infrastructure/s3/S3Config.java | 29 ++++++++ .../s3/S3PresignedUrlService.java | 67 +++++++++++++++++++ .../resources/application-infrastructure.yml | 8 +++ 28 files changed, 326 insertions(+), 77 deletions(-) create mode 100644 Api/src/main/java/allchive/server/api/common/util/UrlUtil.java create mode 100644 Api/src/main/java/allchive/server/api/image/controller/ImageController.java create mode 100644 Api/src/main/java/allchive/server/api/image/model/dto/response/ImageUrlResponse.java rename Domain/src/main/java/allchive/server/domain/domains/category/domain/enums/{Topic.java => Subject.java} (97%) create mode 100644 Infrastructure/src/main/java/allchive/server/infrastructure/s3/ImageUrlDto.java create mode 100644 Infrastructure/src/main/java/allchive/server/infrastructure/s3/PresignedType.java create mode 100644 Infrastructure/src/main/java/allchive/server/infrastructure/s3/S3Config.java create mode 100644 Infrastructure/src/main/java/allchive/server/infrastructure/s3/S3PresignedUrlService.java diff --git a/Api/src/main/java/allchive/server/api/auth/model/dto/request/RegisterRequest.java b/Api/src/main/java/allchive/server/api/auth/model/dto/request/RegisterRequest.java index 49ae7f4b..7b81e4ca 100644 --- a/Api/src/main/java/allchive/server/api/auth/model/dto/request/RegisterRequest.java +++ b/Api/src/main/java/allchive/server/api/auth/model/dto/request/RegisterRequest.java @@ -9,9 +9,11 @@ @Getter @NoArgsConstructor public class RegisterRequest { - @Schema(defaultValue = "www.s3....", description = "프로필 이미지 url") - @NotBlank(message = "프로필 이미지 url을 입력하세요") - private String profileImgUrl; + @Schema( + defaultValue = "staging/category/1/d241218a-a64c-4443-8aa4-ce98017a3d12", + description = "프로필 이미지 url") + @NotBlank(message = "프로필 이미지 key를 입력하세요") + private String profileImgKey; @Schema(defaultValue = "닉네임", description = "닉네임") @NotBlank(message = "닉네임을 입력하세요") diff --git a/Api/src/main/java/allchive/server/api/auth/service/OauthRegisterUseCase.java b/Api/src/main/java/allchive/server/api/auth/service/OauthRegisterUseCase.java index 931fcc63..2265636d 100644 --- a/Api/src/main/java/allchive/server/api/auth/service/OauthRegisterUseCase.java +++ b/Api/src/main/java/allchive/server/api/auth/service/OauthRegisterUseCase.java @@ -26,7 +26,7 @@ public OauthRegisterResponse execute( final User user = userDomainService.registerUser( registerRequest.getNickname(), - registerRequest.getProfileImgUrl(), + registerRequest.getProfileImgKey(), oauthInfo); return OauthRegisterResponse.from(tokenGenerateHelper.execute(user)); } diff --git a/Api/src/main/java/allchive/server/api/category/controller/CategoryController.java b/Api/src/main/java/allchive/server/api/category/controller/CategoryController.java index 9032b0db..1b8c7e3e 100644 --- a/Api/src/main/java/allchive/server/api/category/controller/CategoryController.java +++ b/Api/src/main/java/allchive/server/api/category/controller/CategoryController.java @@ -8,7 +8,7 @@ import allchive.server.api.category.model.dto.response.CategoryTitleResponse; import allchive.server.api.category.service.*; import allchive.server.api.common.slice.SliceResponse; -import allchive.server.domain.domains.category.domain.enums.Topic; +import allchive.server.domain.domains.category.domain.enums.Subject; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; @@ -49,7 +49,7 @@ public void updateCategory( updateCategoryUseCase.execute(categoryId, updateCategoryRequest); } - @Operation(summary = "카테고리를 수정합니다.") + @Operation(summary = "카테고리를 삭제합니다.") @DeleteMapping(value = "/{categoryId}") public void deleteCategory(@PathVariable("categoryId") Long categoryId) { deleteCategoryUseCase.execute(categoryId); @@ -60,9 +60,9 @@ public void deleteCategory(@PathVariable("categoryId") Long categoryId) { description = "sort parameter는 입력하지 말아주세요! sorting : 스크랩 여부 -> 스크랩 수 -> 생성일자") @GetMapping() public SliceResponse<CategoryResponse> getCategory( - @RequestParam("topic") Topic topic, + @RequestParam("subject") Subject subject, @ParameterObject @PageableDefault(size = 10) Pageable pageable) { - return getCategoryUseCase.execute(topic, pageable); + return getCategoryUseCase.execute(subject, pageable); } @Operation( @@ -70,9 +70,9 @@ public SliceResponse<CategoryResponse> getCategory( description = "sort parameter는 입력하지 말아주세요! sorting : 고정 -> 스크랩 수 -> 생성일자") @GetMapping(value = "/me/archiving") public SliceResponse<CategoryResponse> getArchivedCategory( - @RequestParam("topic") Topic topic, + @RequestParam("subject") Subject subject, @ParameterObject @PageableDefault(size = 10) Pageable pageable) { - return getArchivedCategoryUseCase.execute(topic, pageable); + return getArchivedCategoryUseCase.execute(subject, pageable); } @Operation( @@ -80,9 +80,9 @@ public SliceResponse<CategoryResponse> getArchivedCategory( description = "sort parameter는 입력하지 말아주세요! sorting : 스크랩 수 -> 생성일자") @GetMapping(value = "/me/scrap") public SliceResponse<CategoryResponse> getScrapCategory( - @RequestParam("topic") Topic topic, + @RequestParam("subject") Subject subject, @ParameterObject @PageableDefault(size = 10) Pageable pageable) { - return getScrapCategoryUseCase.execute(topic, pageable); + return getScrapCategoryUseCase.execute(subject, pageable); } @Operation(summary = "사용 중인 주제 & 카테고리 리스트를 가져옵니다. (컨텐츠 추가 시 사용)") diff --git a/Api/src/main/java/allchive/server/api/category/model/dto/request/CreateCategoryRequest.java b/Api/src/main/java/allchive/server/api/category/model/dto/request/CreateCategoryRequest.java index 7f80704f..75a16ffc 100644 --- a/Api/src/main/java/allchive/server/api/category/model/dto/request/CreateCategoryRequest.java +++ b/Api/src/main/java/allchive/server/api/category/model/dto/request/CreateCategoryRequest.java @@ -2,7 +2,7 @@ import allchive.server.core.annotation.ValidEnum; -import allchive.server.domain.domains.category.domain.enums.Topic; +import allchive.server.domain.domains.category.domain.enums.Subject; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Getter; @@ -15,8 +15,8 @@ public class CreateCategoryRequest { private String imageUrl; @Schema(defaultValue = "DESIGN", description = "주제") - @ValidEnum(target = Topic.class) - private Topic topic; + @ValidEnum(target = Subject.class) + private Subject subject; @Schema(defaultValue = "false", description = "공개 여부") private boolean publicStatus; diff --git a/Api/src/main/java/allchive/server/api/category/model/dto/request/UpdateCategoryRequest.java b/Api/src/main/java/allchive/server/api/category/model/dto/request/UpdateCategoryRequest.java index c654d841..0c86fc2a 100644 --- a/Api/src/main/java/allchive/server/api/category/model/dto/request/UpdateCategoryRequest.java +++ b/Api/src/main/java/allchive/server/api/category/model/dto/request/UpdateCategoryRequest.java @@ -2,7 +2,7 @@ import allchive.server.core.annotation.ValidEnum; -import allchive.server.domain.domains.category.domain.enums.Topic; +import allchive.server.domain.domains.category.domain.enums.Subject; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Getter; @@ -15,8 +15,8 @@ public class UpdateCategoryRequest { private String imageUrl; @Schema(defaultValue = "DESIGN", description = "주제") - @ValidEnum(target = Topic.class) - private Topic topic; + @ValidEnum(target = Subject.class) + private Subject subject; @Schema(defaultValue = "false", description = "공개 여부") private boolean publicStatus; diff --git a/Api/src/main/java/allchive/server/api/category/model/dto/response/CategoryResponse.java b/Api/src/main/java/allchive/server/api/category/model/dto/response/CategoryResponse.java index dd453aa4..10a10549 100644 --- a/Api/src/main/java/allchive/server/api/category/model/dto/response/CategoryResponse.java +++ b/Api/src/main/java/allchive/server/api/category/model/dto/response/CategoryResponse.java @@ -1,9 +1,10 @@ package allchive.server.api.category.model.dto.response; +import allchive.server.api.common.util.UrlUtil; import allchive.server.core.annotation.DateFormat; import allchive.server.domain.domains.category.domain.Category; -import allchive.server.domain.domains.category.domain.enums.Topic; +import allchive.server.domain.domains.category.domain.enums.Subject; import io.swagger.v3.oas.annotations.media.Schema; import java.time.LocalDateTime; import lombok.Builder; @@ -20,12 +21,16 @@ public class CategoryResponse { @Schema(defaultValue = "카테고리 이미지 url", description = "카테고리 이미지 url") private String imageUrl; - @Schema(defaultValue = "2023.07.02", description = "카테고리 생성일자") + @Schema( + type = "string", + pattern = "yyyy.MM.dd", + defaultValue = "2023.07.02", + description = "카테고리 생성일자") @DateFormat private LocalDateTime createdAt; @Schema(defaultValue = "카테고리 주제", description = "카테고리 주제") - private Topic topic; + private Subject subject; @Schema(description = "카테고리 컨텐츠 중 이미지 수") private Long imgCnt; @@ -45,7 +50,7 @@ private CategoryResponse( String title, String imageUrl, LocalDateTime createdAt, - Topic topic, + Subject subject, Long imgCnt, Long linkCnt, Long scrapCnt, @@ -54,7 +59,7 @@ private CategoryResponse( this.title = title; this.imageUrl = imageUrl; this.createdAt = createdAt; - this.topic = topic; + this.subject = subject; this.imgCnt = imgCnt; this.linkCnt = linkCnt; this.scrapCnt = scrapCnt; @@ -64,10 +69,10 @@ private CategoryResponse( public static CategoryResponse of(Category category, boolean markStatus) { return CategoryResponse.builder() .categoryId(category.getId()) - .imageUrl(category.getImageUrl()) + .imageUrl(UrlUtil.toAssetUrl(category.getImageUrl())) .title(category.getTitle()) .createdAt(category.getCreatedAt()) - .topic(category.getTopic()) + .subject(category.getSubject()) .imgCnt(category.getImgCnt()) .linkCnt(category.getLinkCnt()) .scrapCnt(category.getScrapCnt()) diff --git a/Api/src/main/java/allchive/server/api/category/model/mapper/CategoryMapper.java b/Api/src/main/java/allchive/server/api/category/model/mapper/CategoryMapper.java index 4ba34fb0..3d27544d 100644 --- a/Api/src/main/java/allchive/server/api/category/model/mapper/CategoryMapper.java +++ b/Api/src/main/java/allchive/server/api/category/model/mapper/CategoryMapper.java @@ -18,14 +18,14 @@ public Category toEntity(CreateCategoryRequest request, Long userId) { request.getTitle(), request.getImageUrl(), request.isPublicStatus(), - request.getTopic()); + request.getSubject()); } public CategoryTitleResponse toCategoryTitleResponse(List<Category> categories) { CategoryTitleResponse response = CategoryTitleResponse.init(); categories.forEach( category -> { - switch (category.getTopic()) { + switch (category.getSubject()) { case FOOD -> response.addFood(TitleContentCntVo.from(category)); case LIFE -> response.addLife(TitleContentCntVo.from(category)); case HOME_LIVING -> response.addHomeLiving( diff --git a/Api/src/main/java/allchive/server/api/category/service/GetArchivedCategoryUseCase.java b/Api/src/main/java/allchive/server/api/category/service/GetArchivedCategoryUseCase.java index efdb23a7..1bbce494 100644 --- a/Api/src/main/java/allchive/server/api/category/service/GetArchivedCategoryUseCase.java +++ b/Api/src/main/java/allchive/server/api/category/service/GetArchivedCategoryUseCase.java @@ -6,7 +6,7 @@ import allchive.server.api.config.security.SecurityUtil; import allchive.server.core.annotation.UseCase; import allchive.server.domain.domains.category.adaptor.CategoryAdaptor; -import allchive.server.domain.domains.category.domain.enums.Topic; +import allchive.server.domain.domains.category.domain.enums.Subject; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; @@ -18,11 +18,11 @@ public class GetArchivedCategoryUseCase { private final CategoryAdaptor categoryAdaptor; @Transactional(readOnly = true) - public SliceResponse<CategoryResponse> execute(Topic topic, Pageable pageable) { + public SliceResponse<CategoryResponse> execute(Subject subject, Pageable pageable) { Long userId = SecurityUtil.getCurrentUserId(); Slice<CategoryResponse> categorySlices = categoryAdaptor - .querySliceCategoryByUserId(userId, topic, pageable) + .querySliceCategoryByUserId(userId, subject, pageable) .map( category -> CategoryResponse.of( diff --git a/Api/src/main/java/allchive/server/api/category/service/GetCategoryUseCase.java b/Api/src/main/java/allchive/server/api/category/service/GetCategoryUseCase.java index 2cb2893f..32c89742 100644 --- a/Api/src/main/java/allchive/server/api/category/service/GetCategoryUseCase.java +++ b/Api/src/main/java/allchive/server/api/category/service/GetCategoryUseCase.java @@ -8,7 +8,7 @@ import allchive.server.domain.domains.block.adaptor.BlockAdaptor; import allchive.server.domain.domains.block.domain.Block; import allchive.server.domain.domains.category.adaptor.CategoryAdaptor; -import allchive.server.domain.domains.category.domain.enums.Topic; +import allchive.server.domain.domains.category.domain.enums.Subject; import allchive.server.domain.domains.user.adaptor.ScrapAdaptor; import allchive.server.domain.domains.user.domain.Scrap; import java.util.List; @@ -25,7 +25,7 @@ public class GetCategoryUseCase { private final CategoryAdaptor categoryAdaptor; @Transactional(readOnly = true) - public SliceResponse<CategoryResponse> execute(Topic topic, Pageable pageable) { + public SliceResponse<CategoryResponse> execute(Subject subject, Pageable pageable) { Long userId = SecurityUtil.getCurrentUserId(); List<Long> categoryIdList = scrapAdaptor.findAllByUserId(userId).stream().map(Scrap::getCategoryId).toList(); @@ -33,7 +33,7 @@ public SliceResponse<CategoryResponse> execute(Topic topic, Pageable pageable) { blockAdaptor.findByBlockFrom(userId).stream().map(Block::getBlockUser).toList(); Slice<CategoryResponse> categorySlices = categoryAdaptor - .querySliceCategoryExceptBlock(categoryIdList, blockList, topic, pageable) + .querySliceCategoryExceptBlock(categoryIdList, blockList, subject, pageable) .map( category -> CategoryResponse.of( diff --git a/Api/src/main/java/allchive/server/api/category/service/GetScrapCategoryUseCase.java b/Api/src/main/java/allchive/server/api/category/service/GetScrapCategoryUseCase.java index 1772976c..a1841ca3 100644 --- a/Api/src/main/java/allchive/server/api/category/service/GetScrapCategoryUseCase.java +++ b/Api/src/main/java/allchive/server/api/category/service/GetScrapCategoryUseCase.java @@ -6,7 +6,7 @@ import allchive.server.api.config.security.SecurityUtil; import allchive.server.core.annotation.UseCase; import allchive.server.domain.domains.category.adaptor.CategoryAdaptor; -import allchive.server.domain.domains.category.domain.enums.Topic; +import allchive.server.domain.domains.category.domain.enums.Subject; import allchive.server.domain.domains.user.adaptor.ScrapAdaptor; import allchive.server.domain.domains.user.domain.Scrap; import java.util.List; @@ -22,13 +22,13 @@ public class GetScrapCategoryUseCase { private final CategoryAdaptor categoryAdaptor; @Transactional(readOnly = true) - public SliceResponse<CategoryResponse> execute(Topic topic, Pageable pageable) { + public SliceResponse<CategoryResponse> execute(Subject subject, Pageable pageable) { Long userId = SecurityUtil.getCurrentUserId(); List<Long> categoryIdList = scrapAdaptor.findAllByUserId(userId).stream().map(Scrap::getCategoryId).toList(); Slice<CategoryResponse> categorySlices = categoryAdaptor - .querySliceCategoryIn(categoryIdList, topic, pageable) + .querySliceCategoryIn(categoryIdList, subject, pageable) .map( category -> CategoryResponse.of( diff --git a/Api/src/main/java/allchive/server/api/category/service/UpdateCategoryUseCase.java b/Api/src/main/java/allchive/server/api/category/service/UpdateCategoryUseCase.java index 89b8c276..91420f6c 100644 --- a/Api/src/main/java/allchive/server/api/category/service/UpdateCategoryUseCase.java +++ b/Api/src/main/java/allchive/server/api/category/service/UpdateCategoryUseCase.java @@ -28,6 +28,6 @@ public void execute(Long categoryId, UpdateCategoryRequest request) { request.getTitle(), request.getImageUrl(), request.isPublicStatus(), - request.getTopic()); + request.getSubject()); } } diff --git a/Api/src/main/java/allchive/server/api/common/util/UrlUtil.java b/Api/src/main/java/allchive/server/api/common/util/UrlUtil.java new file mode 100644 index 00000000..ac6d8016 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/common/util/UrlUtil.java @@ -0,0 +1,25 @@ +package allchive.server.api.common.util; + +import static allchive.server.core.consts.AllchiveConst.PROD_ASSET_URL; +import static allchive.server.core.consts.AllchiveConst.STAGING_ASSET_URL; + +import allchive.server.core.helper.SpringEnvironmentHelper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class UrlUtil { + private static SpringEnvironmentHelper springEnvironmentHelper; + + @Autowired + private UrlUtil(SpringEnvironmentHelper springEnvironmentHelper) { + this.springEnvironmentHelper = springEnvironmentHelper; + } + + public static String toAssetUrl(String key) { + if (springEnvironmentHelper.isProdProfile()) { + return PROD_ASSET_URL + key; + } + return STAGING_ASSET_URL + key; + } +} diff --git a/Api/src/main/java/allchive/server/api/image/controller/ImageController.java b/Api/src/main/java/allchive/server/api/image/controller/ImageController.java new file mode 100644 index 00000000..32fa6d7b --- /dev/null +++ b/Api/src/main/java/allchive/server/api/image/controller/ImageController.java @@ -0,0 +1,45 @@ +package allchive.server.api.image.controller; + + +import allchive.server.api.config.security.SecurityUtil; +import allchive.server.api.image.model.dto.response.ImageUrlResponse; +import allchive.server.infrastructure.s3.PresignedType; +import allchive.server.infrastructure.s3.S3PresignedUrlService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/") +@RequiredArgsConstructor +@Slf4j +@Tag(name = "6. [image]") +public class ImageController { + private final S3PresignedUrlService s3PresignedUrlService; + + @Operation(summary = "카테고리 관련 이미지 업로드 url 요청할수 있는 api 입니다.") + @GetMapping(value = "/categories/image") + public ImageUrlResponse getCategoryPresignedUrl() { + Long userId = SecurityUtil.getCurrentUserId(); + return ImageUrlResponse.from( + s3PresignedUrlService.getPreSignedUrl(userId, PresignedType.CATEGORY)); + } + + @Operation(summary = "컨텐츠 관련 이미지 업로드 url 요청할수 있는 api 입니다.") + @GetMapping(value = "/contents/image") + public ImageUrlResponse getContentPresignedUrl() { + Long userId = SecurityUtil.getCurrentUserId(); + return ImageUrlResponse.from( + s3PresignedUrlService.getPreSignedUrl(userId, PresignedType.CONTENT)); + } + + @Operation(summary = "컨텐츠 관련 이미지 업로드 url 요청할수 있는 api 입니다.") + @GetMapping(value = "/user/image") + public ImageUrlResponse getUserPresignedUrl() { + Long userId = SecurityUtil.getCurrentUserId(); + return ImageUrlResponse.from( + s3PresignedUrlService.getPreSignedUrl(userId, PresignedType.USER)); + } +} diff --git a/Api/src/main/java/allchive/server/api/image/model/dto/response/ImageUrlResponse.java b/Api/src/main/java/allchive/server/api/image/model/dto/response/ImageUrlResponse.java new file mode 100644 index 00000000..e2c9d847 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/image/model/dto/response/ImageUrlResponse.java @@ -0,0 +1,29 @@ +package allchive.server.api.image.model.dto.response; + + +import allchive.server.infrastructure.s3.ImageUrlDto; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Builder; +import lombok.Getter; + +@Getter +public class ImageUrlResponse { + @Schema(defaultValue = "이미지 presigned url", description = "이미지 업로드에 사용") + private String url; + + @Schema(defaultValue = "이미지 고유 key 값", description = "서버로 이 값만 보내주시면 됩니다") + private String key; + + @Builder + private ImageUrlResponse(String url, String key) { + this.url = url; + this.key = key; + } + + public static ImageUrlResponse from(ImageUrlDto imageUrlDto) { + return ImageUrlResponse.builder() + .key(imageUrlDto.getKey()) + .url(imageUrlDto.getUrl()) + .build(); + } +} diff --git a/Core/src/main/java/allchive/server/core/consts/AllchiveConst.java b/Core/src/main/java/allchive/server/core/consts/AllchiveConst.java index ac6f8088..4f941163 100644 --- a/Core/src/main/java/allchive/server/core/consts/AllchiveConst.java +++ b/Core/src/main/java/allchive/server/core/consts/AllchiveConst.java @@ -26,6 +26,9 @@ public class AllchiveConst { public static final String KAKAO_OAUTH_QUERY_STRING = "/oauth/authorize?client_id=%s&redirect_uri=%s&response_type=code"; + public static final String STAGING_ASSET_URL = "https://asset.staging.allchive.co.kr/"; + public static final String PROD_ASSET_URL = "https://asset.allchive.co.kr/"; + public static final String[] SwaggerPatterns = { "/swagger-resources/**", "/swagger-ui/**", "/v3/api-docs/**", "/v3/api-docs", }; diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/adaptor/CategoryAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/category/adaptor/CategoryAdaptor.java index 2a30b1d5..9f96c0c7 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/category/adaptor/CategoryAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/category/adaptor/CategoryAdaptor.java @@ -4,7 +4,7 @@ import allchive.server.core.annotation.Adaptor; import allchive.server.core.error.exception.InternalServerError; import allchive.server.domain.domains.category.domain.Category; -import allchive.server.domain.domains.category.domain.enums.Topic; +import allchive.server.domain.domains.category.domain.enums.Subject; import allchive.server.domain.domains.category.exception.exceptions.CategoryNotFoundException; import allchive.server.domain.domains.category.repository.CategoryRepository; import java.util.List; @@ -18,7 +18,7 @@ public class CategoryAdaptor { private final CategoryRepository categoryRepository; public void save(Category category) { - if (category.getTopic().equals(Topic.ALL)) { + if (category.getSubject().equals(Subject.ALL)) { throw InternalServerError.EXCEPTION; } categoryRepository.save(category); @@ -35,18 +35,19 @@ public void deleteById(Long categoryId) { } public Slice<Category> querySliceCategoryExceptBlock( - List<Long> categoryIdList, List<Long> blockList, Topic topic, Pageable pageable) { + List<Long> categoryIdList, List<Long> blockList, Subject subject, Pageable pageable) { return categoryRepository.querySliceCategoryExceptBlock( - categoryIdList, blockList, topic, pageable); + categoryIdList, blockList, subject, pageable); } - public Slice<Category> querySliceCategoryByUserId(Long userId, Topic topic, Pageable pageable) { - return categoryRepository.querySliceCategoryByUserId(userId, topic, pageable); + public Slice<Category> querySliceCategoryByUserId( + Long userId, Subject subject, Pageable pageable) { + return categoryRepository.querySliceCategoryByUserId(userId, subject, pageable); } public Slice<Category> querySliceCategoryIn( - List<Long> categoryIdList, Topic topic, Pageable pageable) { - return categoryRepository.querySliceCategoryIn(categoryIdList, topic, pageable); + List<Long> categoryIdList, Subject subject, Pageable pageable) { + return categoryRepository.querySliceCategoryIn(categoryIdList, subject, pageable); } public List<Category> queryCategoryByUserId(Long userId) { diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/domain/Category.java b/Domain/src/main/java/allchive/server/domain/domains/category/domain/Category.java index c16b22fc..2607abf7 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/category/domain/Category.java +++ b/Domain/src/main/java/allchive/server/domain/domains/category/domain/Category.java @@ -2,7 +2,7 @@ import allchive.server.domain.common.model.BaseTimeEntity; -import allchive.server.domain.domains.category.domain.enums.Topic; +import allchive.server.domain.domains.category.domain.enums.Subject; import allchive.server.domain.domains.category.exception.exceptions.DeletedCategoryException; import allchive.server.domain.domains.category.exception.exceptions.NoAuthurityUpdateCategoryException; import allchive.server.domain.domains.category.exception.exceptions.NotPublicCategoryException; @@ -39,7 +39,7 @@ public class Category extends BaseTimeEntity { private List<Long> pinUserId = new ArrayList<>(); @Enumerated(EnumType.STRING) - private Topic topic; + private Subject subject; @Builder private Category( @@ -48,35 +48,35 @@ private Category( String title, boolean publicStatus, boolean deleteStatus, - Topic topic) { + Subject subject) { this.userId = userId; this.imageUrl = imageUrl; this.title = title; this.publicStatus = publicStatus; this.deleteStatus = deleteStatus; - this.topic = topic; + this.subject = subject; this.scrapCnt = 0L; this.linkCnt = 0L; this.imgCnt = 0L; } public static Category of( - Long userId, String title, String imageUrl, boolean publicStatus, Topic topic) { + Long userId, String title, String imageUrl, boolean publicStatus, Subject subject) { return Category.builder() .userId(userId) .imageUrl(imageUrl) .title(title) .publicStatus(publicStatus) .deleteStatus(Boolean.FALSE) - .topic(topic) + .subject(subject) .build(); } - public void update(String title, String imageUrl, boolean publicStatus, Topic topic) { + public void update(String title, String imageUrl, boolean publicStatus, Subject subject) { this.title = title; this.imageUrl = imageUrl; this.publicStatus = publicStatus; - this.topic = topic; + this.subject = subject; } public void validateUser(Long userId) { diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/domain/enums/Topic.java b/Domain/src/main/java/allchive/server/domain/domains/category/domain/enums/Subject.java similarity index 97% rename from Domain/src/main/java/allchive/server/domain/domains/category/domain/enums/Topic.java rename to Domain/src/main/java/allchive/server/domain/domains/category/domain/enums/Subject.java index 1dde5528..60011fe3 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/category/domain/enums/Topic.java +++ b/Domain/src/main/java/allchive/server/domain/domains/category/domain/enums/Subject.java @@ -10,7 +10,7 @@ @Getter @AllArgsConstructor -public enum Topic { +public enum Subject { ALL("ALL"), FOOD("FOOD"), LIFE("LIFE"), diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/repository/CategoryCustomRepository.java b/Domain/src/main/java/allchive/server/domain/domains/category/repository/CategoryCustomRepository.java index 695dc722..477ea27a 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/category/repository/CategoryCustomRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/category/repository/CategoryCustomRepository.java @@ -2,18 +2,19 @@ import allchive.server.domain.domains.category.domain.Category; -import allchive.server.domain.domains.category.domain.enums.Topic; +import allchive.server.domain.domains.category.domain.enums.Subject; import java.util.List; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; public interface CategoryCustomRepository { Slice<Category> querySliceCategoryExceptBlock( - List<Long> categoryIdList, List<Long> blockList, Topic topic, Pageable pageable); + List<Long> categoryIdList, List<Long> blockList, Subject subject, Pageable pageable); - Slice<Category> querySliceCategoryByUserId(Long userId, Topic topic, Pageable pageable); + Slice<Category> querySliceCategoryByUserId(Long userId, Subject subject, Pageable pageable); - Slice<Category> querySliceCategoryIn(List<Long> categoryIdList, Topic topic, Pageable pageable); + Slice<Category> querySliceCategoryIn( + List<Long> categoryIdList, Subject subject, Pageable pageable); List<Category> queryCategoryByUserId(Long userId); diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/repository/CategoryCustomRepositoryImpl.java b/Domain/src/main/java/allchive/server/domain/domains/category/repository/CategoryCustomRepositoryImpl.java index 4f7393b9..e73d5eec 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/category/repository/CategoryCustomRepositoryImpl.java +++ b/Domain/src/main/java/allchive/server/domain/domains/category/repository/CategoryCustomRepositoryImpl.java @@ -4,7 +4,7 @@ import allchive.server.domain.common.util.SliceUtil; import allchive.server.domain.domains.category.domain.Category; -import allchive.server.domain.domains.category.domain.enums.Topic; +import allchive.server.domain.domains.category.domain.enums.Subject; import com.querydsl.core.types.OrderSpecifier; import com.querydsl.core.types.dsl.BooleanExpression; import com.querydsl.core.types.dsl.CaseBuilder; @@ -22,7 +22,7 @@ public class CategoryCustomRepositoryImpl implements CategoryCustomRepository { @Override public Slice<Category> querySliceCategoryExceptBlock( - List<Long> categoryIdList, List<Long> blockList, Topic topic, Pageable pageable) { + List<Long> categoryIdList, List<Long> blockList, Subject subject, Pageable pageable) { List<Category> categories = queryFactory .select(category) @@ -30,7 +30,7 @@ public Slice<Category> querySliceCategoryExceptBlock( .where( userIdNotIn(blockList), publicStatusTrue(), - topicEq(topic), + subjectEq(subject), deleteStatusFalse()) .orderBy(scrabListDesc(categoryIdList), scrapCntDesc(), createdAtDesc()) .offset(pageable.getOffset()) @@ -40,12 +40,13 @@ public Slice<Category> querySliceCategoryExceptBlock( } @Override - public Slice<Category> querySliceCategoryByUserId(Long userId, Topic topic, Pageable pageable) { + public Slice<Category> querySliceCategoryByUserId( + Long userId, Subject subject, Pageable pageable) { List<Category> categories = queryFactory .select(category) .from(category) - .where(userIdEq(userId), topicEq(topic), deleteStatusFalse()) + .where(userIdEq(userId), subjectEq(subject), deleteStatusFalse()) .orderBy(pinDesc(userId), scrapCntDesc(), createdAtDesc()) .offset(pageable.getOffset()) .limit(pageable.getPageSize() + 1) @@ -55,7 +56,7 @@ public Slice<Category> querySliceCategoryByUserId(Long userId, Topic topic, Page @Override public Slice<Category> querySliceCategoryIn( - List<Long> categoryIdList, Topic topic, Pageable pageable) { + List<Long> categoryIdList, Subject subject, Pageable pageable) { List<Category> categories = queryFactory .select(category) @@ -63,7 +64,7 @@ public Slice<Category> querySliceCategoryIn( .where( categoryIdListIn(categoryIdList), publicStatusTrue(), - topicEq(topic), + subjectEq(subject), deleteStatusFalse()) .orderBy(scrapCntDesc(), createdAtDesc()) .offset(pageable.getOffset()) @@ -77,7 +78,7 @@ public List<Category> queryCategoryByUserId(Long userId) { return queryFactory .selectFrom(category) .where(userIdEq(userId), deleteStatusFalse()) - .orderBy(topicDesc(), createdAtDesc()) + .orderBy(subjectDesc(), createdAtDesc()) .fetch(); } @@ -96,11 +97,11 @@ private BooleanExpression publicStatusTrue() { return category.publicStatus.eq(Boolean.TRUE); } - private BooleanExpression topicEq(Topic topic) { - if (topic.equals(Topic.ALL)) { + private BooleanExpression subjectEq(Subject subject) { + if (subject.equals(Subject.ALL)) { return null; } - return category.topic.eq(topic); + return category.subject.eq(subject); } private BooleanExpression deleteStatusFalse() { @@ -139,7 +140,7 @@ private OrderSpecifier<LocalDateTime> createdAtDesc() { return category.createdAt.desc(); } - private OrderSpecifier<Topic> topicDesc() { - return category.topic.desc(); + private OrderSpecifier<Subject> subjectDesc() { + return category.subject.desc(); } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/service/CategoryDomainService.java b/Domain/src/main/java/allchive/server/domain/domains/category/service/CategoryDomainService.java index d3e503cc..66f47e6d 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/category/service/CategoryDomainService.java +++ b/Domain/src/main/java/allchive/server/domain/domains/category/service/CategoryDomainService.java @@ -4,7 +4,7 @@ import allchive.server.core.annotation.DomainService; import allchive.server.domain.domains.category.adaptor.CategoryAdaptor; import allchive.server.domain.domains.category.domain.Category; -import allchive.server.domain.domains.category.domain.enums.Topic; +import allchive.server.domain.domains.category.domain.enums.Subject; import lombok.RequiredArgsConstructor; @DomainService @@ -17,8 +17,12 @@ public void createCategory(Category category) { } public void updateCategory( - Category category, String title, String imageUrl, boolean publicStatus, Topic topic) { - category.update(title, imageUrl, publicStatus, topic); + Category category, + String title, + String imageUrl, + boolean publicStatus, + Subject subject) { + category.update(title, imageUrl, publicStatus, subject); categoryAdaptor.save(category); } diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/domain/User.java b/Domain/src/main/java/allchive/server/domain/domains/user/domain/User.java index 45d251a9..840c63a8 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/domain/User.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/domain/User.java @@ -3,7 +3,7 @@ import allchive.server.domain.common.convertor.StringListConverter; import allchive.server.domain.common.model.BaseTimeEntity; -import allchive.server.domain.domains.category.domain.enums.Topic; +import allchive.server.domain.domains.category.domain.enums.Subject; import allchive.server.domain.domains.user.domain.enums.OauthInfo; import allchive.server.domain.domains.user.domain.enums.UserRole; import allchive.server.domain.domains.user.domain.enums.UserState; @@ -45,7 +45,7 @@ public class User extends BaseTimeEntity { private UserRole userRole = UserRole.USER; @Convert(converter = StringListConverter.class) - private List<Topic> topics = new ArrayList(); + private List<Subject> subjects = new ArrayList<>(); @Builder private User(String nickname, String profileImgUrl, OauthInfo oauthInfo) { @@ -79,6 +79,6 @@ public void withdrawUser() { this.profileImgUrl = null; this.email = null; this.oauthInfo.withDrawOauthInfo(); - this.topics = new ArrayList<>(); + this.subjects = new ArrayList<>(); } } diff --git a/Infrastructure/build.gradle b/Infrastructure/build.gradle index e46ce9b5..ca0d4e6d 100644 --- a/Infrastructure/build.gradle +++ b/Infrastructure/build.gradle @@ -6,6 +6,7 @@ dependencies { api 'org.redisson:redisson:3.19.0' api 'io.github.openfeign:feign-httpclient:12.1' api 'org.springframework.cloud:spring-cloud-starter-openfeign:3.1.4' + api 'com.amazonaws:aws-java-sdk-s3control:1.12.372' testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.2' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.2' diff --git a/Infrastructure/src/main/java/allchive/server/infrastructure/s3/ImageUrlDto.java b/Infrastructure/src/main/java/allchive/server/infrastructure/s3/ImageUrlDto.java new file mode 100644 index 00000000..37a95152 --- /dev/null +++ b/Infrastructure/src/main/java/allchive/server/infrastructure/s3/ImageUrlDto.java @@ -0,0 +1,21 @@ +package allchive.server.infrastructure.s3; + + +import lombok.Builder; +import lombok.Getter; + +@Getter +public class ImageUrlDto { + private String url; + private String key; + + @Builder + private ImageUrlDto(String url, String key) { + this.url = url; + this.key = key; + } + + public static ImageUrlDto of(String url, String key) { + return ImageUrlDto.builder().key(key).url(url).build(); + } +} diff --git a/Infrastructure/src/main/java/allchive/server/infrastructure/s3/PresignedType.java b/Infrastructure/src/main/java/allchive/server/infrastructure/s3/PresignedType.java new file mode 100644 index 00000000..8b9572c7 --- /dev/null +++ b/Infrastructure/src/main/java/allchive/server/infrastructure/s3/PresignedType.java @@ -0,0 +1,7 @@ +package allchive.server.infrastructure.s3; + +public enum PresignedType { + USER, + CATEGORY, + CONTENT +} diff --git a/Infrastructure/src/main/java/allchive/server/infrastructure/s3/S3Config.java b/Infrastructure/src/main/java/allchive/server/infrastructure/s3/S3Config.java new file mode 100644 index 00000000..670754c5 --- /dev/null +++ b/Infrastructure/src/main/java/allchive/server/infrastructure/s3/S3Config.java @@ -0,0 +1,29 @@ +package allchive.server.infrastructure.s3; + + +import com.amazonaws.auth.AWSStaticCredentialsProvider; +import com.amazonaws.auth.BasicAWSCredentials; +import com.amazonaws.regions.Regions; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.AmazonS3ClientBuilder; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class S3Config { + @Value("${aws.access-key}") + private String accessKey; + + @Value("${aws.secret-key}") + private String secretKey; + + @Bean + public AmazonS3 amazonS3Client() { + BasicAWSCredentials basicAWSCredentials = new BasicAWSCredentials(accessKey, secretKey); + return AmazonS3ClientBuilder.standard() + .withRegion(Regions.AP_NORTHEAST_2) + .withCredentials(new AWSStaticCredentialsProvider(basicAWSCredentials)) + .build(); + } +} diff --git a/Infrastructure/src/main/java/allchive/server/infrastructure/s3/S3PresignedUrlService.java b/Infrastructure/src/main/java/allchive/server/infrastructure/s3/S3PresignedUrlService.java new file mode 100644 index 00000000..e9c3b11f --- /dev/null +++ b/Infrastructure/src/main/java/allchive/server/infrastructure/s3/S3PresignedUrlService.java @@ -0,0 +1,67 @@ +package allchive.server.infrastructure.s3; + + +import allchive.server.core.error.exception.InternalServerError; +import com.amazonaws.HttpMethod; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.Headers; +import com.amazonaws.services.s3.model.CannedAccessControlList; +import com.amazonaws.services.s3.model.GeneratePresignedUrlRequest; +import java.util.Date; +import java.util.UUID; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +@RequiredArgsConstructor +public class S3PresignedUrlService { + private final AmazonS3 amazonS3; + + @Value("${aws.s3.bucket}") + private String bucket; + + @Value("${aws.s3.base-url}") + private String baseUrl; + + public ImageUrlDto getPreSignedUrl(Long id, PresignedType presignedType) { + String fileName = generateFileName(id, presignedType); + GeneratePresignedUrlRequest generatePresignedUrlRequest = + getGeneratePreSignedUrlRequest(fileName); + String url = amazonS3.generatePresignedUrl(generatePresignedUrlRequest).toString(); + return ImageUrlDto.of(url, fileName); + } + + private String generateFileName(Long id, PresignedType presignedType) { + String fileName; + switch (presignedType) { + case USER -> fileName = baseUrl + "/user/"; + case CONTENT -> fileName = baseUrl + "/content/"; + case CATEGORY -> fileName = baseUrl + "/category/"; + default -> throw InternalServerError.EXCEPTION; + } + return fileName + id.toString() + "/" + UUID.randomUUID(); + } + + private GeneratePresignedUrlRequest getGeneratePreSignedUrlRequest(String fileName) { + GeneratePresignedUrlRequest generatePresignedUrlRequest = + new GeneratePresignedUrlRequest(bucket, fileName) + .withMethod(HttpMethod.PUT) + .withKey(fileName) + .withExpiration(getPreSignedUrlExpiration()); + generatePresignedUrlRequest.addRequestParameter( + Headers.S3_CANNED_ACL, CannedAccessControlList.PublicRead.toString()); + return generatePresignedUrlRequest; + } + + private Date getPreSignedUrlExpiration() { + Date expiration = new Date(); + long expTimeMillis = expiration.getTime(); + // 4분 + expTimeMillis += 1000 * 60 * 4; + expiration.setTime(expTimeMillis); + return expiration; + } +} diff --git a/Infrastructure/src/main/resources/application-infrastructure.yml b/Infrastructure/src/main/resources/application-infrastructure.yml index e3f0c896..368bfafc 100644 --- a/Infrastructure/src/main/resources/application-infrastructure.yml +++ b/Infrastructure/src/main/resources/application-infrastructure.yml @@ -1,3 +1,11 @@ +aws: + access-key: ${AWS_ACCESS_KEY:testKey} + secret-key: ${AWS_SECRET_KEY:secretKey} + s3: + bucket: ${AWS_S3_BUCKET:bucket} + base-url: ${AWS_S3_BASEURL:baseUrl} + + --- spring: From 861b2f6ab6fa3c4d31aca58a21aacbfca0d7fe66 Mon Sep 17 00:00:00 2001 From: Sanghoon Jeong <67852689+wjdtkdgns@users.noreply.github.com> Date: Fri, 7 Jul 2023 16:03:11 +0900 Subject: [PATCH 25/41] =?UTF-8?q?[feat]=20user=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84=20?= =?UTF-8?q?(#26)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [feat] 유저 프로파일 제공 기능 구현 #25 * [feat] info 가져오기, 수정하기 기능 구현 #25 * [refac] urlutil 적용 #25 * [feat] nickname 중복 검사 기능 구현 #25 * [fix] transaction 범위 변경 #25 * [chore] spotless 적용 #25 --- .../category/model/mapper/CategoryMapper.java | 5 +- .../service/UpdateCategoryUseCase.java | 3 +- .../server/api/common/util/UrlUtil.java | 18 +++++-- .../server/api/example/ExampleController.java | 42 --------------- .../allchive/server/api/example/dto/.keep | 0 .../allchive/server/api/example/service/.keep | 0 .../api/user/controller/UserController.java | 52 +++++++++++++++++++ .../dto/request/CheckUserNicknameRequest.java | 11 ++++ .../dto/request/UpdateUserInfoRequest.java | 23 ++++++++ .../dto/response/GetUserInfoResponse.java | 40 ++++++++++++++ .../dto/response/GetUserProfileResponse.java | 47 +++++++++++++++++ .../api/user/model/mapper/UserMapper.java | 21 ++++++++ .../service/CheckUserNicknameUseCase.java | 24 +++++++++ .../api/user/service/GetUserInfoUseCase.java | 26 ++++++++++ .../user/service/GetUserProfileUseCase.java | 33 ++++++++++++ .../user/service/UpdateUserInfoUseCase.java | 27 ++++++++++ .../server/core/consts/AllchiveConst.java | 2 + .../category/adaptor/CategoryAdaptor.java | 4 ++ .../repository/CategoryRepository.java | 5 +- .../domains/user/adaptor/UserAdaptor.java | 4 ++ .../user/adaptor/UserTopicAdaptor.java | 9 ---- .../domain/domains/user/domain/User.java | 13 +++++ .../domains/user/exception/UserErrorCode.java | 1 + .../DuplicatedNicknameException.java | 14 +++++ .../user/repository/UserRepository.java | 2 + .../user/service/UserDomainService.java | 26 ++++++---- .../domains/user/validator/UserValidator.java | 25 ++++++++- 27 files changed, 407 insertions(+), 70 deletions(-) delete mode 100644 Api/src/main/java/allchive/server/api/example/ExampleController.java delete mode 100644 Api/src/main/java/allchive/server/api/example/dto/.keep delete mode 100644 Api/src/main/java/allchive/server/api/example/service/.keep create mode 100644 Api/src/main/java/allchive/server/api/user/controller/UserController.java create mode 100644 Api/src/main/java/allchive/server/api/user/model/dto/request/CheckUserNicknameRequest.java create mode 100644 Api/src/main/java/allchive/server/api/user/model/dto/request/UpdateUserInfoRequest.java create mode 100644 Api/src/main/java/allchive/server/api/user/model/dto/response/GetUserInfoResponse.java create mode 100644 Api/src/main/java/allchive/server/api/user/model/dto/response/GetUserProfileResponse.java create mode 100644 Api/src/main/java/allchive/server/api/user/model/mapper/UserMapper.java create mode 100644 Api/src/main/java/allchive/server/api/user/service/CheckUserNicknameUseCase.java create mode 100644 Api/src/main/java/allchive/server/api/user/service/GetUserInfoUseCase.java create mode 100644 Api/src/main/java/allchive/server/api/user/service/GetUserProfileUseCase.java create mode 100644 Api/src/main/java/allchive/server/api/user/service/UpdateUserInfoUseCase.java delete mode 100644 Domain/src/main/java/allchive/server/domain/domains/user/adaptor/UserTopicAdaptor.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/user/exception/exceptions/DuplicatedNicknameException.java diff --git a/Api/src/main/java/allchive/server/api/category/model/mapper/CategoryMapper.java b/Api/src/main/java/allchive/server/api/category/model/mapper/CategoryMapper.java index 3d27544d..a8741533 100644 --- a/Api/src/main/java/allchive/server/api/category/model/mapper/CategoryMapper.java +++ b/Api/src/main/java/allchive/server/api/category/model/mapper/CategoryMapper.java @@ -4,19 +4,18 @@ import allchive.server.api.category.model.dto.request.CreateCategoryRequest; import allchive.server.api.category.model.dto.response.CategoryTitleResponse; import allchive.server.api.category.model.vo.TitleContentCntVo; +import allchive.server.api.common.util.UrlUtil; import allchive.server.core.annotation.Mapper; import allchive.server.domain.domains.category.domain.Category; import java.util.List; -import lombok.extern.slf4j.Slf4j; @Mapper -@Slf4j public class CategoryMapper { public Category toEntity(CreateCategoryRequest request, Long userId) { return Category.of( userId, request.getTitle(), - request.getImageUrl(), + UrlUtil.convertUrlToKey(request.getImageUrl()), request.isPublicStatus(), request.getSubject()); } diff --git a/Api/src/main/java/allchive/server/api/category/service/UpdateCategoryUseCase.java b/Api/src/main/java/allchive/server/api/category/service/UpdateCategoryUseCase.java index 91420f6c..71069837 100644 --- a/Api/src/main/java/allchive/server/api/category/service/UpdateCategoryUseCase.java +++ b/Api/src/main/java/allchive/server/api/category/service/UpdateCategoryUseCase.java @@ -2,6 +2,7 @@ import allchive.server.api.category.model.dto.request.UpdateCategoryRequest; +import allchive.server.api.common.util.UrlUtil; import allchive.server.api.config.security.SecurityUtil; import allchive.server.core.annotation.UseCase; import allchive.server.domain.domains.category.adaptor.CategoryAdaptor; @@ -26,7 +27,7 @@ public void execute(Long categoryId, UpdateCategoryRequest request) { categoryDomainService.updateCategory( category, request.getTitle(), - request.getImageUrl(), + UrlUtil.convertUrlToKey(request.getImageUrl()), request.isPublicStatus(), request.getSubject()); } diff --git a/Api/src/main/java/allchive/server/api/common/util/UrlUtil.java b/Api/src/main/java/allchive/server/api/common/util/UrlUtil.java index ac6d8016..0500a20d 100644 --- a/Api/src/main/java/allchive/server/api/common/util/UrlUtil.java +++ b/Api/src/main/java/allchive/server/api/common/util/UrlUtil.java @@ -1,7 +1,6 @@ package allchive.server.api.common.util; -import static allchive.server.core.consts.AllchiveConst.PROD_ASSET_URL; -import static allchive.server.core.consts.AllchiveConst.STAGING_ASSET_URL; +import static allchive.server.core.consts.AllchiveConst.*; import allchive.server.core.helper.SpringEnvironmentHelper; import org.springframework.beans.factory.annotation.Autowired; @@ -13,7 +12,7 @@ public class UrlUtil { @Autowired private UrlUtil(SpringEnvironmentHelper springEnvironmentHelper) { - this.springEnvironmentHelper = springEnvironmentHelper; + UrlUtil.springEnvironmentHelper = springEnvironmentHelper; } public static String toAssetUrl(String key) { @@ -22,4 +21,17 @@ public static String toAssetUrl(String key) { } return STAGING_ASSET_URL + key; } + + public static String convertUrlToKey(String url) { + if (validateUrl(url)) { + return url.split("/", 4)[3]; + } + return url; + } + + private static Boolean validateUrl(String url) { + return url.contains(STAGING_ASSET_URL) + || url.contains(PROD_ASSET_URL) + || url.contains(S3_ASSET_URL); + } } diff --git a/Api/src/main/java/allchive/server/api/example/ExampleController.java b/Api/src/main/java/allchive/server/api/example/ExampleController.java deleted file mode 100644 index 90267d44..00000000 --- a/Api/src/main/java/allchive/server/api/example/ExampleController.java +++ /dev/null @@ -1,42 +0,0 @@ -package allchive.server.api.example; - - -import allchive.server.core.error.exception.ExampleException; -import lombok.RequiredArgsConstructor; -import org.springframework.web.bind.annotation.*; - -@RestController -@RequestMapping(value = "/example") -@RequiredArgsConstructor -public class ExampleController { - - @GetMapping(value = "/responseGet") - public int responseGet() { - return 1; - } - - @PostMapping(value = "/responsePost") - public int responsePost() { - return 1; - } - - @PutMapping(value = "/responsePut") - public int responsePut() { - return 1; - } - - @PatchMapping(value = "/responsePatch") - public int responsePatch() { - return 1; - } - - @DeleteMapping(value = "/responseDelete") - public int responseDelete() { - return 1; - } - - @GetMapping(value = "/responseError") - public int responseError() { - throw ExampleException.EXCEPTION; - } -} diff --git a/Api/src/main/java/allchive/server/api/example/dto/.keep b/Api/src/main/java/allchive/server/api/example/dto/.keep deleted file mode 100644 index e69de29b..00000000 diff --git a/Api/src/main/java/allchive/server/api/example/service/.keep b/Api/src/main/java/allchive/server/api/example/service/.keep deleted file mode 100644 index e69de29b..00000000 diff --git a/Api/src/main/java/allchive/server/api/user/controller/UserController.java b/Api/src/main/java/allchive/server/api/user/controller/UserController.java new file mode 100644 index 00000000..d44da34c --- /dev/null +++ b/Api/src/main/java/allchive/server/api/user/controller/UserController.java @@ -0,0 +1,52 @@ +package allchive.server.api.user.controller; + + +import allchive.server.api.user.model.dto.request.CheckUserNicknameRequest; +import allchive.server.api.user.model.dto.request.UpdateUserInfoRequest; +import allchive.server.api.user.model.dto.response.GetUserInfoResponse; +import allchive.server.api.user.model.dto.response.GetUserProfileResponse; +import allchive.server.api.user.service.CheckUserNicknameUseCase; +import allchive.server.api.user.service.GetUserInfoUseCase; +import allchive.server.api.user.service.GetUserProfileUseCase; +import allchive.server.api.user.service.UpdateUserInfoUseCase; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/user") +@RequiredArgsConstructor +@Slf4j +@Tag(name = "2. [user]") +public class UserController { + private final GetUserProfileUseCase getUserProfileUseCase; + private final GetUserInfoUseCase getUserInfoUseCase; + private final UpdateUserInfoUseCase updateUserInfoUseCase; + private final CheckUserNicknameUseCase checkUserNicknameUseCase; + + @Operation(summary = "아카이빙 현황과 내 프로필을 가져옵니다.") + @GetMapping() + public GetUserProfileResponse getUserProfile() { + return getUserProfileUseCase.execute(); + } + + @Operation(summary = "내 정보를 가져옵니다.") + @GetMapping(value = "/info") + public GetUserInfoResponse getUserInfo() { + return getUserInfoUseCase.execute(); + } + + @Operation(summary = "내 정보를 수정합니다.") + @PostMapping(value = "/info") + public void getUserInfo(@RequestBody UpdateUserInfoRequest updateUserInfoRequest) { + updateUserInfoUseCase.execute(updateUserInfoRequest); + } + + @Operation(summary = "닉네임 중복체크합니다.") + @PostMapping(value = "/nickname") + public void checkUserNickname(@RequestBody CheckUserNicknameRequest request) { + checkUserNicknameUseCase.execute(request); + } +} diff --git a/Api/src/main/java/allchive/server/api/user/model/dto/request/CheckUserNicknameRequest.java b/Api/src/main/java/allchive/server/api/user/model/dto/request/CheckUserNicknameRequest.java new file mode 100644 index 00000000..817a6ed0 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/user/model/dto/request/CheckUserNicknameRequest.java @@ -0,0 +1,11 @@ +package allchive.server.api.user.model.dto.request; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; + +@Getter +public class CheckUserNicknameRequest { + @Schema(defaultValue = "닉네임", description = "닉네임") + private String nickname; +} diff --git a/Api/src/main/java/allchive/server/api/user/model/dto/request/UpdateUserInfoRequest.java b/Api/src/main/java/allchive/server/api/user/model/dto/request/UpdateUserInfoRequest.java new file mode 100644 index 00000000..825d8041 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/user/model/dto/request/UpdateUserInfoRequest.java @@ -0,0 +1,23 @@ +package allchive.server.api.user.model.dto.request; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; + +@Getter +public class UpdateUserInfoRequest { + @Schema( + defaultValue = + "https://asset.staging.allchive.co.kr.s3.ap-northeast-2.amazonaws.com/staging/user/1/e024eaf4-74a", + description = "프로필 이미지 url") + private String imgUrl; + + @Schema(defaultValue = "asd@asd.com", description = "이메일") + private String email; + + @Schema(defaultValue = "이름", description = "유저 이름") + private String name; + + @Schema(defaultValue = "닉네임", description = "닉네임") + private String nickname; +} diff --git a/Api/src/main/java/allchive/server/api/user/model/dto/response/GetUserInfoResponse.java b/Api/src/main/java/allchive/server/api/user/model/dto/response/GetUserInfoResponse.java new file mode 100644 index 00000000..7b4ce100 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/user/model/dto/response/GetUserInfoResponse.java @@ -0,0 +1,40 @@ +package allchive.server.api.user.model.dto.response; + + +import allchive.server.api.common.util.UrlUtil; +import allchive.server.domain.domains.user.domain.User; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Builder; +import lombok.Getter; + +@Getter +public class GetUserInfoResponse { + @Schema(defaultValue = "프로필 이미지 url", description = "프로필 이미지 url") + private String imgUrl; + + @Schema(defaultValue = "asd@asd.com", description = "이메일") + private String email; + + @Schema(defaultValue = "이름", description = "유저 이름") + private String name; + + @Schema(defaultValue = "닉네임", description = "닉네임") + private String nickname; + + @Builder + public GetUserInfoResponse(String imgUrl, String email, String name, String nickname) { + this.imgUrl = imgUrl; + this.email = email; + this.name = name; + this.nickname = nickname; + } + + public static GetUserInfoResponse from(User user) { + return GetUserInfoResponse.builder() + .imgUrl(UrlUtil.toAssetUrl(user.getProfileImgUrl())) + .email(user.getEmail()) + .name(user.getName()) + .nickname(user.getNickname()) + .build(); + } +} diff --git a/Api/src/main/java/allchive/server/api/user/model/dto/response/GetUserProfileResponse.java b/Api/src/main/java/allchive/server/api/user/model/dto/response/GetUserProfileResponse.java new file mode 100644 index 00000000..1bf62ba0 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/user/model/dto/response/GetUserProfileResponse.java @@ -0,0 +1,47 @@ +package allchive.server.api.user.model.dto.response; + + +import allchive.server.api.common.util.UrlUtil; +import allchive.server.domain.domains.user.domain.User; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Builder; +import lombok.Getter; + +@Getter +public class GetUserProfileResponse { + @Schema(defaultValue = "닉네임", description = "닉네임") + private String nickname; + + @Schema(defaultValue = "프로필 이미지 url", description = "프로필 이미지 url") + private String imgUrl; + + @Schema(defaultValue = "0", description = "링크 개수") + private int linkCount; + + @Schema(defaultValue = "0", description = "이미지 개수") + private int imgCount; + + @Schema(defaultValue = "0", description = "공개 아카이브 개수") + private int publicCategoryCount; + + @Builder + private GetUserProfileResponse( + String nickname, String imgUrl, int linkCount, int imgCount, int publicCategoryCount) { + this.nickname = nickname; + this.imgUrl = imgUrl; + this.linkCount = linkCount; + this.imgCount = imgCount; + this.publicCategoryCount = publicCategoryCount; + } + + public static GetUserProfileResponse of( + User user, int linkCount, int imgCount, int publicCategoryCount) { + return GetUserProfileResponse.builder() + .nickname(user.getNickname()) + .imgUrl(UrlUtil.toAssetUrl(user.getProfileImgUrl())) + .linkCount(linkCount) + .imgCount(imgCount) + .publicCategoryCount(publicCategoryCount) + .build(); + } +} diff --git a/Api/src/main/java/allchive/server/api/user/model/mapper/UserMapper.java b/Api/src/main/java/allchive/server/api/user/model/mapper/UserMapper.java new file mode 100644 index 00000000..d403c4a5 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/user/model/mapper/UserMapper.java @@ -0,0 +1,21 @@ +package allchive.server.api.user.model.mapper; + + +import allchive.server.api.user.model.dto.response.GetUserProfileResponse; +import allchive.server.core.annotation.Mapper; +import allchive.server.domain.domains.category.domain.Category; +import allchive.server.domain.domains.user.domain.User; +import java.util.List; + +@Mapper +public class UserMapper { + public GetUserProfileResponse toGetUserProfileResponse(List<Category> categoryList, User user) { + int linkCount = 0, imgCount = 0, publicCategoryCount = 0; + for (Category category : categoryList) { + linkCount += category.getLinkCnt(); + imgCount += category.getImgCnt(); + publicCategoryCount += category.getPublicStatus() ? 1 : 0; + } + return GetUserProfileResponse.of(user, linkCount, imgCount, publicCategoryCount); + } +} diff --git a/Api/src/main/java/allchive/server/api/user/service/CheckUserNicknameUseCase.java b/Api/src/main/java/allchive/server/api/user/service/CheckUserNicknameUseCase.java new file mode 100644 index 00000000..61118913 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/user/service/CheckUserNicknameUseCase.java @@ -0,0 +1,24 @@ +package allchive.server.api.user.service; + + +import allchive.server.api.config.security.SecurityUtil; +import allchive.server.api.user.model.dto.request.CheckUserNicknameRequest; +import allchive.server.core.annotation.UseCase; +import allchive.server.domain.domains.user.service.UserDomainService; +import allchive.server.domain.domains.user.validator.UserValidator; +import lombok.RequiredArgsConstructor; +import org.springframework.transaction.annotation.Transactional; + +@UseCase +@RequiredArgsConstructor +public class CheckUserNicknameUseCase { + private final UserDomainService userDomainService; + private final UserValidator userValidator; + + @Transactional(readOnly = true) + public void execute(CheckUserNicknameRequest request) { + Long userId = SecurityUtil.getCurrentUserId(); + userValidator.validateUserStatusNormal(userId); + userDomainService.checkUserNickname(request.getNickname()); + } +} diff --git a/Api/src/main/java/allchive/server/api/user/service/GetUserInfoUseCase.java b/Api/src/main/java/allchive/server/api/user/service/GetUserInfoUseCase.java new file mode 100644 index 00000000..6fc7c0c7 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/user/service/GetUserInfoUseCase.java @@ -0,0 +1,26 @@ +package allchive.server.api.user.service; + + +import allchive.server.api.config.security.SecurityUtil; +import allchive.server.api.user.model.dto.response.GetUserInfoResponse; +import allchive.server.core.annotation.UseCase; +import allchive.server.domain.domains.user.adaptor.UserAdaptor; +import allchive.server.domain.domains.user.domain.User; +import allchive.server.domain.domains.user.validator.UserValidator; +import lombok.RequiredArgsConstructor; +import org.springframework.transaction.annotation.Transactional; + +@UseCase +@RequiredArgsConstructor +public class GetUserInfoUseCase { + private final UserAdaptor userAdaptor; + private final UserValidator userValidator; + + @Transactional(readOnly = true) + public GetUserInfoResponse execute() { + Long userId = SecurityUtil.getCurrentUserId(); + userValidator.validateUserStatusNormal(userId); + User user = userAdaptor.queryUserById(userId); + return GetUserInfoResponse.from(user); + } +} diff --git a/Api/src/main/java/allchive/server/api/user/service/GetUserProfileUseCase.java b/Api/src/main/java/allchive/server/api/user/service/GetUserProfileUseCase.java new file mode 100644 index 00000000..23c4fb40 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/user/service/GetUserProfileUseCase.java @@ -0,0 +1,33 @@ +package allchive.server.api.user.service; + + +import allchive.server.api.config.security.SecurityUtil; +import allchive.server.api.user.model.dto.response.GetUserProfileResponse; +import allchive.server.api.user.model.mapper.UserMapper; +import allchive.server.core.annotation.UseCase; +import allchive.server.domain.domains.category.adaptor.CategoryAdaptor; +import allchive.server.domain.domains.category.domain.Category; +import allchive.server.domain.domains.user.adaptor.UserAdaptor; +import allchive.server.domain.domains.user.domain.User; +import allchive.server.domain.domains.user.validator.UserValidator; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.transaction.annotation.Transactional; + +@UseCase +@RequiredArgsConstructor +public class GetUserProfileUseCase { + private final CategoryAdaptor categoryAdaptor; + private final UserAdaptor userAdaptor; + private final UserMapper userMapper; + private final UserValidator userValidator; + + @Transactional(readOnly = true) + public GetUserProfileResponse execute() { + Long userId = SecurityUtil.getCurrentUserId(); + userValidator.validateUserStatusNormal(userId); + User user = userAdaptor.queryUserById(userId); + List<Category> categoryList = categoryAdaptor.findAllByUserId(userId); + return userMapper.toGetUserProfileResponse(categoryList, user); + } +} diff --git a/Api/src/main/java/allchive/server/api/user/service/UpdateUserInfoUseCase.java b/Api/src/main/java/allchive/server/api/user/service/UpdateUserInfoUseCase.java new file mode 100644 index 00000000..9485a883 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/user/service/UpdateUserInfoUseCase.java @@ -0,0 +1,27 @@ +package allchive.server.api.user.service; + + +import allchive.server.api.common.util.UrlUtil; +import allchive.server.api.config.security.SecurityUtil; +import allchive.server.api.user.model.dto.request.UpdateUserInfoRequest; +import allchive.server.core.annotation.UseCase; +import allchive.server.domain.domains.user.service.UserDomainService; +import allchive.server.domain.domains.user.validator.UserValidator; +import lombok.RequiredArgsConstructor; +import org.springframework.transaction.annotation.Transactional; + +@UseCase +@RequiredArgsConstructor +public class UpdateUserInfoUseCase { + private final UserDomainService userDomainService; + private final UserValidator userValidator; + + @Transactional + public void execute(UpdateUserInfoRequest request) { + Long userId = SecurityUtil.getCurrentUserId(); + userValidator.validateUserStatusNormal(userId); + String imgKey = UrlUtil.convertUrlToKey(request.getImgUrl()); + userDomainService.updateUserInfo( + userId, request.getName(), request.getEmail(), request.getNickname(), imgKey); + } +} diff --git a/Core/src/main/java/allchive/server/core/consts/AllchiveConst.java b/Core/src/main/java/allchive/server/core/consts/AllchiveConst.java index 4f941163..fc8a9a18 100644 --- a/Core/src/main/java/allchive/server/core/consts/AllchiveConst.java +++ b/Core/src/main/java/allchive/server/core/consts/AllchiveConst.java @@ -28,6 +28,8 @@ public class AllchiveConst { public static final String STAGING_ASSET_URL = "https://asset.staging.allchive.co.kr/"; public static final String PROD_ASSET_URL = "https://asset.allchive.co.kr/"; + public static final String S3_ASSET_URL = + "https://asset.staging.allchive.co.kr.s3.ap-northeast-2.amazonaws.com/"; public static final String[] SwaggerPatterns = { "/swagger-resources/**", "/swagger-ui/**", "/v3/api-docs/**", "/v3/api-docs", diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/adaptor/CategoryAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/category/adaptor/CategoryAdaptor.java index 9f96c0c7..82b3982c 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/category/adaptor/CategoryAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/category/adaptor/CategoryAdaptor.java @@ -57,4 +57,8 @@ public List<Category> queryCategoryByUserId(Long userId) { public boolean queryCategoryExist(Long categoryId) { return categoryRepository.queryCategoryExist(categoryId); } + + public List<Category> findAllByUserId(Long userId) { + return categoryRepository.findAllByUserId(userId); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/repository/CategoryRepository.java b/Domain/src/main/java/allchive/server/domain/domains/category/repository/CategoryRepository.java index 376def57..caf69829 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/category/repository/CategoryRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/category/repository/CategoryRepository.java @@ -2,7 +2,10 @@ import allchive.server.domain.domains.category.domain.Category; +import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; public interface CategoryRepository - extends JpaRepository<Category, Long>, CategoryCustomRepository {} + extends JpaRepository<Category, Long>, CategoryCustomRepository { + List<Category> findAllByUserId(Long userId); +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/UserAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/UserAdaptor.java index c205c361..135d202f 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/UserAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/UserAdaptor.java @@ -30,4 +30,8 @@ public void save(User user) { public User queryUserById(Long userId) { return userRepository.findById(userId).orElseThrow(() -> UserNotFoundException.EXCEPTION); } + + public Boolean existsByNickname(String nickname) { + return userRepository.existsByNickname(nickname); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/UserTopicAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/UserTopicAdaptor.java deleted file mode 100644 index 48aee243..00000000 --- a/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/UserTopicAdaptor.java +++ /dev/null @@ -1,9 +0,0 @@ -package allchive.server.domain.domains.user.adaptor; - - -import allchive.server.core.annotation.Adaptor; -import lombok.RequiredArgsConstructor; - -@Adaptor -@RequiredArgsConstructor -public class UserTopicAdaptor {} diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/domain/User.java b/Domain/src/main/java/allchive/server/domain/domains/user/domain/User.java index 840c63a8..d8ed940e 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/domain/User.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/domain/User.java @@ -28,6 +28,8 @@ public class User extends BaseTimeEntity { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + private String name; + @NotNull private String nickname; private String profileImgUrl; @@ -80,5 +82,16 @@ public void withdrawUser() { this.email = null; this.oauthInfo.withDrawOauthInfo(); this.subjects = new ArrayList<>(); + this.name = null; + } + + public void updateInfo(String name, String email, String nickname, String imgUrl) { + if (!UserState.NORMAL.equals(this.userState)) { + throw ForbiddenUserException.EXCEPTION; + } + this.name = name; + this.email = email; + this.nickname = nickname; + this.profileImgUrl = imgUrl; } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/exception/UserErrorCode.java b/Domain/src/main/java/allchive/server/domain/domains/user/exception/UserErrorCode.java index f960ae09..62b39b0d 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/exception/UserErrorCode.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/exception/UserErrorCode.java @@ -14,6 +14,7 @@ public enum UserErrorCode implements BaseErrorCode { FORBIDDEN_USER(BAD_REQUEST, "USER_400_1", "접근 제한된 유저입니다."), USER_ALREADY_SIGNUP(BAD_REQUEST, "USER_400_2", "이미 회원가입한 유저입니다."), USER_ALREADY_DELETED(BAD_REQUEST, "USER_400_3", "이미 지워진 유저입니다."), + DUPLICATED_NICKNAME(BAD_REQUEST, "USER_400_4", "중복된 닉네임입니다."), USER_NOT_FOUND(NOT_FOUND, "USER_404_1", "유저 정보를 찾을 수 없습니다."), diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/exception/exceptions/DuplicatedNicknameException.java b/Domain/src/main/java/allchive/server/domain/domains/user/exception/exceptions/DuplicatedNicknameException.java new file mode 100644 index 00000000..5545d0f8 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/user/exception/exceptions/DuplicatedNicknameException.java @@ -0,0 +1,14 @@ +package allchive.server.domain.domains.user.exception.exceptions; + + +import allchive.server.core.error.BaseErrorException; +import allchive.server.domain.domains.user.exception.UserErrorCode; + +public class DuplicatedNicknameException extends BaseErrorException { + + public static final BaseErrorException EXCEPTION = new DuplicatedNicknameException(); + + private DuplicatedNicknameException() { + super(UserErrorCode.DUPLICATED_NICKNAME); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/repository/UserRepository.java b/Domain/src/main/java/allchive/server/domain/domains/user/repository/UserRepository.java index d38cc4c0..af39a107 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/repository/UserRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/repository/UserRepository.java @@ -8,4 +8,6 @@ public interface UserRepository extends JpaRepository<User, Long> { Optional<User> findByOauthInfo(OauthInfo oauthInfo); + + boolean existsByNickname(String nickname); } diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/service/UserDomainService.java b/Domain/src/main/java/allchive/server/domain/domains/user/service/UserDomainService.java index c6acc791..fb8acce1 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/service/UserDomainService.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/service/UserDomainService.java @@ -5,7 +5,8 @@ import allchive.server.domain.domains.user.adaptor.UserAdaptor; import allchive.server.domain.domains.user.domain.User; import allchive.server.domain.domains.user.domain.enums.OauthInfo; -import allchive.server.domain.domains.user.exception.exceptions.AlreadySignUpUserException; +import allchive.server.domain.domains.user.exception.exceptions.DuplicatedNicknameException; +import allchive.server.domain.domains.user.validator.UserValidator; import lombok.RequiredArgsConstructor; import org.springframework.transaction.annotation.Transactional; @@ -13,6 +14,7 @@ @RequiredArgsConstructor public class UserDomainService { private final UserAdaptor userAdaptor; + private final UserValidator userValidator; public Boolean checkUserCanLogin(OauthInfo oauthInfo) { return userAdaptor.exist(oauthInfo); @@ -20,7 +22,7 @@ public Boolean checkUserCanLogin(OauthInfo oauthInfo) { @Transactional public User registerUser(String nickname, String profileImgUrl, OauthInfo oauthInfo) { - validUserCanRegister(oauthInfo); + userValidator.validUserCanRegister(oauthInfo); final User newUser = User.of(nickname, profileImgUrl, oauthInfo); userAdaptor.save(newUser); return newUser; @@ -33,17 +35,21 @@ public User loginUser(OauthInfo oauthInfo) { return user; } - public Boolean checkUserCanRegister(OauthInfo oauthInfo) { - return !userAdaptor.exist(oauthInfo); - } - - public void validUserCanRegister(OauthInfo oauthInfo) { - if (!checkUserCanRegister(oauthInfo)) throw AlreadySignUpUserException.EXCEPTION; - } - @Transactional public void deleteUserById(Long userId) { User user = userAdaptor.queryUserById(userId); user.withdrawUser(); } + + public void updateUserInfo( + Long userId, String name, String email, String nickname, String imgUrl) { + User user = userAdaptor.queryUserById(userId); + user.updateInfo(name, email, nickname, imgUrl); + } + + public void checkUserNickname(String nickname) { + if (userAdaptor.existsByNickname(nickname)) { + throw DuplicatedNicknameException.EXCEPTION; + } + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/validator/UserValidator.java b/Domain/src/main/java/allchive/server/domain/domains/user/validator/UserValidator.java index 6382bacf..8a25182e 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/validator/UserValidator.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/validator/UserValidator.java @@ -2,6 +2,29 @@ import allchive.server.core.annotation.Validator; +import allchive.server.domain.domains.user.adaptor.UserAdaptor; +import allchive.server.domain.domains.user.domain.enums.OauthInfo; +import allchive.server.domain.domains.user.domain.enums.UserState; +import allchive.server.domain.domains.user.exception.exceptions.AlreadySignUpUserException; +import allchive.server.domain.domains.user.exception.exceptions.ForbiddenUserException; +import lombok.RequiredArgsConstructor; @Validator -public class UserValidator {} +@RequiredArgsConstructor +public class UserValidator { + private final UserAdaptor userAdaptor; + + public void validUserCanRegister(OauthInfo oauthInfo) { + if (!checkUserCanRegister(oauthInfo)) throw AlreadySignUpUserException.EXCEPTION; + } + + public Boolean checkUserCanRegister(OauthInfo oauthInfo) { + return !userAdaptor.exist(oauthInfo); + } + + public void validateUserStatusNormal(Long userId) { + if (!userAdaptor.queryUserById(userId).getUserState().equals(UserState.NORMAL)) { + throw ForbiddenUserException.EXCEPTION; + } + } +} From 6b772b8b08b26b7b93edb191fd89622dfc6bf09b Mon Sep 17 00:00:00 2001 From: Sanghoon Jeong <67852689+wjdtkdgns@users.noreply.github.com> Date: Sat, 8 Jul 2023 01:25:35 +0900 Subject: [PATCH 26/41] =?UTF-8?q?[feat]=20content=20=EC=A1=B0=ED=9A=8C,=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C,=20=EC=83=9D=EC=84=B1=20=EA=B8=B0=EB=8A=A5?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84=20(#28)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [feat] content 생성 및 가져오기 구현 #27 * [feat] content 삭제 기능 구현 #27 * [feat] category, content 삭제시 휴지통 기능 구현 #27 * [chore] spotless 적용 #27 * [fix] urlutil 적용 #27 --- .../service/DeleteCategoryUseCase.java | 11 ++- .../content/controller/ContentController.java | 42 +++++++++++ .../dto/request/CreateContentRequest.java | 33 +++++++++ .../model/dto/response/ContentResponse.java | 13 ++++ .../dto/response/ContentTagResponse.java | 71 +++++++++++++++++++ .../content/model/mapper/ContentMapper.java | 23 ++++++ .../content/service/CreateContentUseCase.java | 30 ++++++++ .../content/service/DeleteContentUseCase.java | 36 ++++++++++ .../content/service/GetContentUseCase.java | 34 +++++++++ .../recycle/model/mapper/RecycleMapper.java | 17 +++++ .../tag/model/dto/response/TagResponse.java | 26 +++++++ .../domains/category/domain/Category.java | 4 ++ .../service/CategoryDomainService.java | 10 +-- .../category/validator/CategoryValidator.java | 4 ++ .../content/adaptor/ContentAdaptor.java | 15 ++++ .../adaptor/ContentTagGroupAdaptor.java | 4 ++ .../domains/content/domain/Content.java | 41 +++++++++++ .../content/exception/ContentErrorCode.java | 9 +-- .../exceptions/ContentNotFoundException.java | 14 ++++ .../repository/ContentTagGroupRepository.java | 6 +- .../content/service/ContentDomainService.java | 23 ++++++ .../content/service/ContentService.java | 7 -- .../recycle/adaptor/RecycleAdaptor.java | 10 ++- .../domains/recycle/domain/Recycle.java | 36 ++++++++++ .../domains/recycle/domain/enums/.gitkeep | 0 .../recycle/domain/enums/RecycleType.java | 26 +++++++ .../recycle/service/RecycleDomainService.java | 17 +++++ .../recycle/service/RecycleService.java | 7 -- 28 files changed, 544 insertions(+), 25 deletions(-) create mode 100644 Api/src/main/java/allchive/server/api/content/controller/ContentController.java create mode 100644 Api/src/main/java/allchive/server/api/content/model/dto/request/CreateContentRequest.java create mode 100644 Api/src/main/java/allchive/server/api/content/model/dto/response/ContentTagResponse.java create mode 100644 Api/src/main/java/allchive/server/api/content/service/CreateContentUseCase.java create mode 100644 Api/src/main/java/allchive/server/api/content/service/DeleteContentUseCase.java create mode 100644 Api/src/main/java/allchive/server/api/content/service/GetContentUseCase.java create mode 100644 Api/src/main/java/allchive/server/api/recycle/model/mapper/RecycleMapper.java create mode 100644 Api/src/main/java/allchive/server/tag/model/dto/response/TagResponse.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/content/exception/exceptions/ContentNotFoundException.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/content/service/ContentDomainService.java delete mode 100644 Domain/src/main/java/allchive/server/domain/domains/content/service/ContentService.java delete mode 100644 Domain/src/main/java/allchive/server/domain/domains/recycle/domain/enums/.gitkeep create mode 100644 Domain/src/main/java/allchive/server/domain/domains/recycle/domain/enums/RecycleType.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/recycle/service/RecycleDomainService.java delete mode 100644 Domain/src/main/java/allchive/server/domain/domains/recycle/service/RecycleService.java diff --git a/Api/src/main/java/allchive/server/api/category/service/DeleteCategoryUseCase.java b/Api/src/main/java/allchive/server/api/category/service/DeleteCategoryUseCase.java index 8c354df0..74bf1e26 100644 --- a/Api/src/main/java/allchive/server/api/category/service/DeleteCategoryUseCase.java +++ b/Api/src/main/java/allchive/server/api/category/service/DeleteCategoryUseCase.java @@ -2,9 +2,13 @@ import allchive.server.api.config.security.SecurityUtil; +import allchive.server.api.recycle.model.mapper.RecycleMapper; import allchive.server.core.annotation.UseCase; import allchive.server.domain.domains.category.service.CategoryDomainService; import allchive.server.domain.domains.category.validator.CategoryValidator; +import allchive.server.domain.domains.recycle.domain.Recycle; +import allchive.server.domain.domains.recycle.domain.enums.RecycleType; +import allchive.server.domain.domains.recycle.service.RecycleDomainService; import lombok.RequiredArgsConstructor; import org.springframework.transaction.annotation.Transactional; @@ -13,11 +17,16 @@ public class DeleteCategoryUseCase { private final CategoryDomainService categoryDomainService; private final CategoryValidator categoryValidator; + private final RecycleMapper recycleMapper; + private final RecycleDomainService recycleDomainService; @Transactional public void execute(Long categoryId) { Long userId = SecurityUtil.getCurrentUserId(); categoryValidator.verifyUser(userId, categoryId); - categoryDomainService.deleteCategory(categoryId); + categoryDomainService.deleteById(categoryId); + Recycle recycle = + recycleMapper.toCategoryRecycleEntity(userId, categoryId, RecycleType.CATEGORY); + recycleDomainService.save(recycle); } } diff --git a/Api/src/main/java/allchive/server/api/content/controller/ContentController.java b/Api/src/main/java/allchive/server/api/content/controller/ContentController.java new file mode 100644 index 00000000..565e0742 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/content/controller/ContentController.java @@ -0,0 +1,42 @@ +package allchive.server.api.content.controller; + + +import allchive.server.api.content.model.dto.request.CreateContentRequest; +import allchive.server.api.content.model.dto.response.ContentTagResponse; +import allchive.server.api.content.service.CreateContentUseCase; +import allchive.server.api.content.service.DeleteContentUseCase; +import allchive.server.api.content.service.GetContentUseCase; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/contents") +@RequiredArgsConstructor +@Slf4j +@Tag(name = "4. [content]") +public class ContentController { + private final CreateContentUseCase createContentUseCase; + private final GetContentUseCase getContentUseCase; + private final DeleteContentUseCase deleteContentUseCase; + + @Operation(summary = "컨텐츠를 생성합니다.") + @PostMapping() + public void createContent(@RequestBody CreateContentRequest createContentRequest) { + createContentUseCase.execute(createContentRequest); + } + + @Operation(summary = "컨텐츠 내용을 가져옵니다.") + @GetMapping(value = "/{contentId}") + public ContentTagResponse createContent(@PathVariable Long contentId) { + return getContentUseCase.execute(contentId); + } + + @Operation(summary = "컨텐츠 내용을 가져옵니다.") + @DeleteMapping(value = "/{contentId}") + public void deleteContent(@PathVariable Long contentId) { + deleteContentUseCase.execute(contentId); + } +} diff --git a/Api/src/main/java/allchive/server/api/content/model/dto/request/CreateContentRequest.java b/Api/src/main/java/allchive/server/api/content/model/dto/request/CreateContentRequest.java new file mode 100644 index 00000000..0dc82df5 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/content/model/dto/request/CreateContentRequest.java @@ -0,0 +1,33 @@ +package allchive.server.api.content.model.dto.request; + + +import allchive.server.core.annotation.ValidEnum; +import allchive.server.domain.domains.content.domain.enums.ContentType; +import io.swagger.v3.oas.annotations.media.Schema; +import java.util.List; +import lombok.Getter; + +@Getter +public class CreateContentRequest { + @Schema(defaultValue = "image", description = "컨텐츠 타입") + @ValidEnum(target = ContentType.class) + private ContentType contentType; + + @Schema(defaultValue = "0", description = "카테고리 고유번호") + private Long categoryId; + + @Schema(defaultValue = "제목", description = "제목") + private String title; + + @Schema(defaultValue = "링크", description = "링크") + private String link; + + @Schema(defaultValue = "이미지 url", description = "이미지 url") + private String imgUrl; + + @Schema(description = "태그 고유번호 리스트") + private List<Long> tagIds; + + @Schema(defaultValue = "메모", description = "메모") + private String memo; +} diff --git a/Api/src/main/java/allchive/server/api/content/model/dto/response/ContentResponse.java b/Api/src/main/java/allchive/server/api/content/model/dto/response/ContentResponse.java index 97ebcb1d..dc7d27a7 100644 --- a/Api/src/main/java/allchive/server/api/content/model/dto/response/ContentResponse.java +++ b/Api/src/main/java/allchive/server/api/content/model/dto/response/ContentResponse.java @@ -1,6 +1,7 @@ package allchive.server.api.content.model.dto.response; +import allchive.server.api.common.util.UrlUtil; import allchive.server.core.annotation.DateFormat; import allchive.server.domain.domains.content.domain.Content; import allchive.server.domain.domains.content.domain.enums.ContentType; @@ -20,6 +21,12 @@ public class ContentResponse { @Schema(description = "컨텐츠 종류") private ContentType contentType; + @Schema(defaultValue = "컨텐츠 링크", description = "컨텐츠 링크") + private String link; + + @Schema(defaultValue = "컨텐츠 이미지 url", description = "컨텐츠 이미지 url") + private String imgUrl; + @Schema(defaultValue = "2023.07.02", description = "컨텐츠 생성일자") @DateFormat private LocalDateTime contentCreatedAt; @@ -37,11 +44,15 @@ private ContentResponse( ContentType contentType, LocalDateTime contentCreatedAt, String tag, + String link, + String imgUrl, Long tagCount) { this.contentId = contentId; this.contentTitle = contentTitle; this.contentType = contentType; this.contentCreatedAt = contentCreatedAt; + this.link = link; + this.imgUrl = imgUrl; Tag = tag; TagCount = tagCount; } @@ -51,6 +62,8 @@ public static ContentResponse of(Content content, String tag, Long tagCount) { .contentId(content.getId()) .contentTitle(content.getTitle()) .contentType(content.getContentType()) + .link(content.getLinkUrl()) + .imgUrl(UrlUtil.toAssetUrl(content.getImageUrl())) .contentCreatedAt(content.getCreatedAt()) .tag(tag) .tagCount(tagCount) diff --git a/Api/src/main/java/allchive/server/api/content/model/dto/response/ContentTagResponse.java b/Api/src/main/java/allchive/server/api/content/model/dto/response/ContentTagResponse.java new file mode 100644 index 00000000..99fe1ef1 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/content/model/dto/response/ContentTagResponse.java @@ -0,0 +1,71 @@ +package allchive.server.api.content.model.dto.response; + + +import allchive.server.api.common.util.UrlUtil; +import allchive.server.core.annotation.DateFormat; +import allchive.server.domain.domains.content.domain.Content; +import allchive.server.domain.domains.content.domain.enums.ContentType; +import allchive.server.tag.model.dto.response.TagResponse; +import io.swagger.v3.oas.annotations.media.Schema; +import java.time.LocalDateTime; +import java.util.List; +import lombok.Builder; +import lombok.Getter; + +@Getter +public class ContentTagResponse { + @Schema(description = "컨텐츠 고유번호") + private Long contentId; + + @Schema(description = "컨텐츠 제목") + private String contentTitle; + + @Schema(description = "컨텐츠 종류") + private ContentType contentType; + + @Schema(defaultValue = "컨텐츠 링크", description = "컨텐츠 링크") + private String link; + + @Schema(defaultValue = "컨텐츠 이미지 url", description = "컨텐츠 이미지 url") + private String imgUrl; + + @Schema( + type = "string", + pattern = "yyyy.MM.dd", + defaultValue = "2023.07.02", + description = "컨텐츠 생성일자") + @DateFormat + private LocalDateTime contentCreatedAt; + + private List<TagResponse> tagList; + + @Builder + private ContentTagResponse( + Long contentId, + String contentTitle, + ContentType contentType, + String link, + String imgUrl, + LocalDateTime contentCreatedAt, + List<TagResponse> tagList) { + this.contentId = contentId; + this.contentTitle = contentTitle; + this.contentType = contentType; + this.link = link; + this.imgUrl = imgUrl; + this.contentCreatedAt = contentCreatedAt; + this.tagList = tagList; + } + + public static ContentTagResponse of(Content content, List<TagResponse> tagList) { + return ContentTagResponse.builder() + .contentId(content.getId()) + .contentTitle(content.getTitle()) + .contentType(content.getContentType()) + .link(content.getLinkUrl()) + .imgUrl(UrlUtil.toAssetUrl(content.getImageUrl())) + .contentCreatedAt(content.getCreatedAt()) + .tagList(tagList) + .build(); + } +} diff --git a/Api/src/main/java/allchive/server/api/content/model/mapper/ContentMapper.java b/Api/src/main/java/allchive/server/api/content/model/mapper/ContentMapper.java index 1881b59b..ad80bcdd 100644 --- a/Api/src/main/java/allchive/server/api/content/model/mapper/ContentMapper.java +++ b/Api/src/main/java/allchive/server/api/content/model/mapper/ContentMapper.java @@ -1,10 +1,14 @@ package allchive.server.api.content.model.mapper; +import allchive.server.api.common.util.UrlUtil; +import allchive.server.api.content.model.dto.request.CreateContentRequest; import allchive.server.api.content.model.dto.response.ContentResponse; +import allchive.server.api.content.model.dto.response.ContentTagResponse; import allchive.server.core.annotation.Mapper; import allchive.server.domain.domains.content.domain.Content; import allchive.server.domain.domains.content.domain.ContentTagGroup; +import allchive.server.tag.model.dto.response.TagResponse; import java.util.List; @Mapper @@ -19,4 +23,23 @@ public ContentResponse toContentResponse( String tag = contentTagGroup == null ? null : contentTagGroup.getTag().getName(); return ContentResponse.of(content, tag, (long) tags.size()); } + + public Content toEntity(CreateContentRequest request) { + return Content.of( + request.getCategoryId(), + request.getContentType(), + UrlUtil.convertUrlToKey(request.getImgUrl()), + request.getLink(), + request.getTitle(), + request.getMemo()); + } + + public ContentTagResponse toContentTagResponse( + Content content, List<ContentTagGroup> contentTagGroupList) { + List<TagResponse> tagResponseList = + contentTagGroupList.stream() + .map(contentTagGroup -> TagResponse.from(contentTagGroup.getTag())) + .toList(); + return ContentTagResponse.of(content, tagResponseList); + } } diff --git a/Api/src/main/java/allchive/server/api/content/service/CreateContentUseCase.java b/Api/src/main/java/allchive/server/api/content/service/CreateContentUseCase.java new file mode 100644 index 00000000..13adcfcb --- /dev/null +++ b/Api/src/main/java/allchive/server/api/content/service/CreateContentUseCase.java @@ -0,0 +1,30 @@ +package allchive.server.api.content.service; + + +import allchive.server.api.config.security.SecurityUtil; +import allchive.server.api.content.model.dto.request.CreateContentRequest; +import allchive.server.api.content.model.mapper.ContentMapper; +import allchive.server.core.annotation.UseCase; +import allchive.server.domain.domains.category.validator.CategoryValidator; +import allchive.server.domain.domains.content.domain.Content; +import allchive.server.domain.domains.content.service.ContentDomainService; +import lombok.RequiredArgsConstructor; +import org.springframework.transaction.annotation.Transactional; + +@UseCase +@RequiredArgsConstructor +public class CreateContentUseCase { + private final CategoryValidator categoryValidator; + private final ContentMapper contentMapper; + private final ContentDomainService contentDomainService; + + // TODO : tag 만들면 연결 + @Transactional + public void execute(CreateContentRequest request) { + categoryValidator.validateExistCategory(request.getCategoryId()); + Long userId = SecurityUtil.getCurrentUserId(); + categoryValidator.validateCategoryUser(request.getCategoryId(), userId); + Content content = contentMapper.toEntity(request); + contentDomainService.save(content); + } +} diff --git a/Api/src/main/java/allchive/server/api/content/service/DeleteContentUseCase.java b/Api/src/main/java/allchive/server/api/content/service/DeleteContentUseCase.java new file mode 100644 index 00000000..32ab39fe --- /dev/null +++ b/Api/src/main/java/allchive/server/api/content/service/DeleteContentUseCase.java @@ -0,0 +1,36 @@ +package allchive.server.api.content.service; + + +import allchive.server.api.config.security.SecurityUtil; +import allchive.server.api.recycle.model.mapper.RecycleMapper; +import allchive.server.core.annotation.UseCase; +import allchive.server.domain.domains.category.validator.CategoryValidator; +import allchive.server.domain.domains.content.adaptor.ContentAdaptor; +import allchive.server.domain.domains.content.domain.Content; +import allchive.server.domain.domains.content.service.ContentDomainService; +import allchive.server.domain.domains.recycle.domain.Recycle; +import allchive.server.domain.domains.recycle.domain.enums.RecycleType; +import allchive.server.domain.domains.recycle.service.RecycleDomainService; +import lombok.RequiredArgsConstructor; +import org.springframework.transaction.annotation.Transactional; + +@UseCase +@RequiredArgsConstructor +public class DeleteContentUseCase { + private final ContentAdaptor contentAdaptor; + private final CategoryValidator categoryValidator; + private final ContentDomainService contentDomainService; + private final RecycleMapper recycleMapper; + private final RecycleDomainService recycleDomainService; + + @Transactional + public void execute(Long contentId) { + Long userId = SecurityUtil.getCurrentUserId(); + Content content = contentAdaptor.findById(contentId); + categoryValidator.validateCategoryUser(content.getCategoryId(), userId); + contentDomainService.deleteById(contentId); + Recycle recycle = + recycleMapper.toContentRecycleEntity(userId, contentId, RecycleType.CONTENT); + recycleDomainService.save(recycle); + } +} diff --git a/Api/src/main/java/allchive/server/api/content/service/GetContentUseCase.java b/Api/src/main/java/allchive/server/api/content/service/GetContentUseCase.java new file mode 100644 index 00000000..c812e344 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/content/service/GetContentUseCase.java @@ -0,0 +1,34 @@ +package allchive.server.api.content.service; + + +import allchive.server.api.config.security.SecurityUtil; +import allchive.server.api.content.model.dto.response.ContentTagResponse; +import allchive.server.api.content.model.mapper.ContentMapper; +import allchive.server.core.annotation.UseCase; +import allchive.server.domain.domains.category.validator.CategoryValidator; +import allchive.server.domain.domains.content.adaptor.ContentAdaptor; +import allchive.server.domain.domains.content.adaptor.ContentTagGroupAdaptor; +import allchive.server.domain.domains.content.domain.Content; +import allchive.server.domain.domains.content.domain.ContentTagGroup; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.transaction.annotation.Transactional; + +@UseCase +@RequiredArgsConstructor +public class GetContentUseCase { + private final CategoryValidator categoryValidator; + private final ContentAdaptor contentAdaptor; + private final ContentTagGroupAdaptor contentTagGroupAdaptor; + private final ContentMapper contentMapper; + + @Transactional(readOnly = true) + public ContentTagResponse execute(Long contentId) { + Content content = contentAdaptor.findById(contentId); + Long userId = SecurityUtil.getCurrentUserId(); + categoryValidator.validatePublicStatus(content.getCategoryId(), userId); + List<ContentTagGroup> contentTagGroupList = + contentTagGroupAdaptor.findAllByContent(content); + return contentMapper.toContentTagResponse(content, contentTagGroupList); + } +} diff --git a/Api/src/main/java/allchive/server/api/recycle/model/mapper/RecycleMapper.java b/Api/src/main/java/allchive/server/api/recycle/model/mapper/RecycleMapper.java new file mode 100644 index 00000000..2963b92e --- /dev/null +++ b/Api/src/main/java/allchive/server/api/recycle/model/mapper/RecycleMapper.java @@ -0,0 +1,17 @@ +package allchive.server.api.recycle.model.mapper; + + +import allchive.server.core.annotation.Mapper; +import allchive.server.domain.domains.recycle.domain.Recycle; +import allchive.server.domain.domains.recycle.domain.enums.RecycleType; + +@Mapper +public class RecycleMapper { + public Recycle toContentRecycleEntity(Long userId, Long contentId, RecycleType type) { + return Recycle.of(type, contentId, null, userId); + } + + public Recycle toCategoryRecycleEntity(Long userId, Long categoryId, RecycleType type) { + return Recycle.of(type, null, categoryId, userId); + } +} diff --git a/Api/src/main/java/allchive/server/tag/model/dto/response/TagResponse.java b/Api/src/main/java/allchive/server/tag/model/dto/response/TagResponse.java new file mode 100644 index 00000000..70155dde --- /dev/null +++ b/Api/src/main/java/allchive/server/tag/model/dto/response/TagResponse.java @@ -0,0 +1,26 @@ +package allchive.server.tag.model.dto.response; + + +import allchive.server.domain.domains.content.domain.Tag; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Builder; +import lombok.Getter; + +@Getter +public class TagResponse { + @Schema(description = "태그 고유번호") + private Long tagId; + + @Schema(description = "태그 이름") + private String name; + + @Builder + private TagResponse(Long tagId, String name) { + this.tagId = tagId; + this.name = name; + } + + public static TagResponse from(Tag tag) { + return TagResponse.builder().tagId(tag.getId()).name(tag.getName()).build(); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/domain/Category.java b/Domain/src/main/java/allchive/server/domain/domains/category/domain/Category.java index 2607abf7..285e9c3c 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/category/domain/Category.java +++ b/Domain/src/main/java/allchive/server/domain/domains/category/domain/Category.java @@ -108,4 +108,8 @@ public void addPinUserId(Long userId) { public void deletePinUserId(Long userId) { this.getPinUserId().remove(userId); } + + public void delete() { + this.deleteStatus = Boolean.TRUE; + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/service/CategoryDomainService.java b/Domain/src/main/java/allchive/server/domain/domains/category/service/CategoryDomainService.java index 66f47e6d..70fdcb80 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/category/service/CategoryDomainService.java +++ b/Domain/src/main/java/allchive/server/domain/domains/category/service/CategoryDomainService.java @@ -26,10 +26,6 @@ public void updateCategory( categoryAdaptor.save(category); } - public void deleteCategory(Long categoryId) { - categoryAdaptor.deleteById(categoryId); - } - public void updateScrapCount(Long categoryId, int i) { Category category = categoryAdaptor.findById(categoryId); category.updateScrapCnt(i); @@ -45,4 +41,10 @@ public void updatePin(Long categoryId, Long userId, boolean pin) { } category.updateScrapCnt(pin ? 1 : -1); } + + public void deleteById(Long categoryId) { + Category category = categoryAdaptor.findById(categoryId); + category.delete(); + categoryAdaptor.save(category); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/validator/CategoryValidator.java b/Domain/src/main/java/allchive/server/domain/domains/category/validator/CategoryValidator.java index 51fdbf2b..e25d2a0d 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/category/validator/CategoryValidator.java +++ b/Domain/src/main/java/allchive/server/domain/domains/category/validator/CategoryValidator.java @@ -42,4 +42,8 @@ public void validateNotPinStatus(Long categoryId, Long userId) { throw NotPinnedCategoryException.EXCEPTION; } } + + public void validateCategoryUser(Long categoryId, Long userId) { + categoryAdaptor.findById(categoryId).validateUser(userId); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentAdaptor.java index d4c24532..833898f2 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentAdaptor.java @@ -3,6 +3,7 @@ import allchive.server.core.annotation.Adaptor; import allchive.server.domain.domains.content.domain.Content; +import allchive.server.domain.domains.content.exception.exceptions.ContentNotFoundException; import allchive.server.domain.domains.content.repository.ContentRepository; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Pageable; @@ -16,4 +17,18 @@ public class ContentAdaptor { public Slice<Content> querySliceContentByCategoryId(Long categoryId, Pageable pageable) { return contentRepository.querySliceContentByCategoryId(categoryId, pageable); } + + public void save(Content content) { + contentRepository.save(content); + } + + public Content findById(Long contentId) { + return contentRepository + .findById(contentId) + .orElseThrow(() -> ContentNotFoundException.EXCEPTION); + } + + public void deleteById(Long contentId) { + contentRepository.deleteById(contentId); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentTagGroupAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentTagGroupAdaptor.java index e443ad84..56c8e859 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentTagGroupAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentTagGroupAdaptor.java @@ -16,4 +16,8 @@ public class ContentTagGroupAdaptor { public List<ContentTagGroup> queryContentIn(List<Content> contentList) { return contentTagGroupRepository.queryContentTagGroupIn(contentList); } + + public List<ContentTagGroup> findAllByContent(Content content) { + return contentTagGroupRepository.findAllByContent(content); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/domain/Content.java b/Domain/src/main/java/allchive/server/domain/domains/content/domain/Content.java index 9e0606c9..dd4396d1 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/domain/Content.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/domain/Content.java @@ -5,6 +5,7 @@ import allchive.server.domain.domains.content.domain.enums.ContentType; import javax.persistence.*; import lombok.AccessLevel; +import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; @@ -30,4 +31,44 @@ public class Content extends BaseTimeEntity { private String memo; private boolean deleteStatus = Boolean.FALSE; + + @Builder + private Content( + Long categoryId, + ContentType contentType, + String imageUrl, + String linkUrl, + String title, + String memo, + boolean deleteStatus) { + this.categoryId = categoryId; + this.contentType = contentType; + this.imageUrl = imageUrl; + this.linkUrl = linkUrl; + this.title = title; + this.memo = memo; + this.deleteStatus = deleteStatus; + } + + public static Content of( + Long categoryId, + ContentType contentType, + String imageUrl, + String linkUrl, + String title, + String memo) { + return Content.builder() + .categoryId(categoryId) + .contentType(contentType) + .imageUrl(imageUrl) + .linkUrl(linkUrl) + .title(title) + .memo(memo) + .deleteStatus(Boolean.FALSE) + .build(); + } + + public void delete() { + this.deleteStatus = Boolean.TRUE; + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/exception/ContentErrorCode.java b/Domain/src/main/java/allchive/server/domain/domains/content/exception/ContentErrorCode.java index a068bd46..606aec08 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/exception/ContentErrorCode.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/exception/ContentErrorCode.java @@ -1,22 +1,23 @@ package allchive.server.domain.domains.content.exception; +import static allchive.server.core.consts.AllchiveConst.NOT_FOUND; import allchive.server.core.dto.ErrorReason; import allchive.server.core.error.BaseErrorCode; import lombok.AllArgsConstructor; import lombok.Getter; -import org.springframework.http.HttpStatus; @Getter @AllArgsConstructor public enum ContentErrorCode implements BaseErrorCode { - ; - private HttpStatus status; + CONTENT_NOT_FOUND(NOT_FOUND, "CATEGORY_404_1", "카테고리를 찾을 수 없습니다."); + + private int status; private String code; private String reason; @Override public ErrorReason getErrorReason() { - return ErrorReason.of(status.value(), code, reason); + return ErrorReason.of(status, code, reason); } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/exception/exceptions/ContentNotFoundException.java b/Domain/src/main/java/allchive/server/domain/domains/content/exception/exceptions/ContentNotFoundException.java new file mode 100644 index 00000000..7f7b0b4e --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/content/exception/exceptions/ContentNotFoundException.java @@ -0,0 +1,14 @@ +package allchive.server.domain.domains.content.exception.exceptions; + + +import allchive.server.core.error.BaseErrorException; +import allchive.server.domain.domains.content.exception.ContentErrorCode; + +public class ContentNotFoundException extends BaseErrorException { + + public static final BaseErrorException EXCEPTION = new ContentNotFoundException(); + + private ContentNotFoundException() { + super(ContentErrorCode.CONTENT_NOT_FOUND); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentTagGroupRepository.java b/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentTagGroupRepository.java index 262113e9..a5424f1e 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentTagGroupRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentTagGroupRepository.java @@ -1,8 +1,12 @@ package allchive.server.domain.domains.content.repository; +import allchive.server.domain.domains.content.domain.Content; import allchive.server.domain.domains.content.domain.ContentTagGroup; +import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; public interface ContentTagGroupRepository - extends JpaRepository<ContentTagGroup, Long>, ContentTagGroupCustomRepository {} + extends JpaRepository<ContentTagGroup, Long>, ContentTagGroupCustomRepository { + List<ContentTagGroup> findAllByContent(Content content); +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/service/ContentDomainService.java b/Domain/src/main/java/allchive/server/domain/domains/content/service/ContentDomainService.java new file mode 100644 index 00000000..70b0b16c --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/content/service/ContentDomainService.java @@ -0,0 +1,23 @@ +package allchive.server.domain.domains.content.service; + + +import allchive.server.core.annotation.DomainService; +import allchive.server.domain.domains.content.adaptor.ContentAdaptor; +import allchive.server.domain.domains.content.domain.Content; +import lombok.RequiredArgsConstructor; + +@DomainService +@RequiredArgsConstructor +public class ContentDomainService { + private final ContentAdaptor contentAdaptor; + + public void save(Content content) { + contentAdaptor.save(content); + } + + public void deleteById(Long contentId) { + Content content = contentAdaptor.findById(contentId); + content.delete(); + save(content); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/service/ContentService.java b/Domain/src/main/java/allchive/server/domain/domains/content/service/ContentService.java deleted file mode 100644 index 5de26f57..00000000 --- a/Domain/src/main/java/allchive/server/domain/domains/content/service/ContentService.java +++ /dev/null @@ -1,7 +0,0 @@ -package allchive.server.domain.domains.content.service; - - -import allchive.server.core.annotation.DomainService; - -@DomainService -public class ContentService {} diff --git a/Domain/src/main/java/allchive/server/domain/domains/recycle/adaptor/RecycleAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/recycle/adaptor/RecycleAdaptor.java index bbcdc5bb..00f8ad2c 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/recycle/adaptor/RecycleAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/recycle/adaptor/RecycleAdaptor.java @@ -2,8 +2,16 @@ import allchive.server.core.annotation.Adaptor; +import allchive.server.domain.domains.recycle.domain.Recycle; +import allchive.server.domain.domains.recycle.repository.RecycleRepository; import lombok.RequiredArgsConstructor; @Adaptor @RequiredArgsConstructor -public class RecycleAdaptor {} +public class RecycleAdaptor { + private final RecycleRepository recycleRepository; + + public void save(Recycle recycle) { + recycleRepository.save(recycle); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/recycle/domain/Recycle.java b/Domain/src/main/java/allchive/server/domain/domains/recycle/domain/Recycle.java index 58b3572d..da42a523 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/recycle/domain/Recycle.java +++ b/Domain/src/main/java/allchive/server/domain/domains/recycle/domain/Recycle.java @@ -2,8 +2,11 @@ import allchive.server.domain.common.model.BaseTimeEntity; +import allchive.server.domain.domains.recycle.domain.enums.RecycleType; +import java.time.LocalDateTime; import javax.persistence.*; import lombok.AccessLevel; +import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; @@ -15,4 +18,37 @@ public class Recycle extends BaseTimeEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + + @Enumerated(EnumType.STRING) + private RecycleType recycleType; + + private Long contentId; + private Long categoryId; + private Long userId; + private LocalDateTime deletedAt; + + @Builder + private Recycle( + RecycleType recycleType, + Long contentId, + Long categoryId, + Long userId, + LocalDateTime deletedAt) { + this.recycleType = recycleType; + this.contentId = contentId; + this.categoryId = categoryId; + this.userId = userId; + this.deletedAt = deletedAt; + } + + public static Recycle of( + RecycleType recycleType, Long contentId, Long categoryId, Long userId) { + return Recycle.builder() + .categoryId(categoryId) + .contentId(contentId) + .recycleType(recycleType) + .userId(userId) + .deletedAt(LocalDateTime.now()) + .build(); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/recycle/domain/enums/.gitkeep b/Domain/src/main/java/allchive/server/domain/domains/recycle/domain/enums/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/Domain/src/main/java/allchive/server/domain/domains/recycle/domain/enums/RecycleType.java b/Domain/src/main/java/allchive/server/domain/domains/recycle/domain/enums/RecycleType.java new file mode 100644 index 00000000..384b72f5 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/recycle/domain/enums/RecycleType.java @@ -0,0 +1,26 @@ +package allchive.server.domain.domains.recycle.domain.enums; + + +import allchive.server.domain.domains.user.domain.enums.OauthProvider; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import java.util.stream.Stream; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum RecycleType { + CONTENT("content"), + CATEGORY("category"); + + @JsonValue private String value; + + @JsonCreator + public static OauthProvider parsing(String inputValue) { + return Stream.of(OauthProvider.values()) + .filter(category -> category.getValue().equals(inputValue)) + .findFirst() + .orElse(null); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/recycle/service/RecycleDomainService.java b/Domain/src/main/java/allchive/server/domain/domains/recycle/service/RecycleDomainService.java new file mode 100644 index 00000000..c6721e98 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/recycle/service/RecycleDomainService.java @@ -0,0 +1,17 @@ +package allchive.server.domain.domains.recycle.service; + + +import allchive.server.core.annotation.DomainService; +import allchive.server.domain.domains.recycle.adaptor.RecycleAdaptor; +import allchive.server.domain.domains.recycle.domain.Recycle; +import lombok.RequiredArgsConstructor; + +@DomainService +@RequiredArgsConstructor +public class RecycleDomainService { + private final RecycleAdaptor recycleAdaptor; + + public void save(Recycle recycle) { + recycleAdaptor.save(recycle); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/recycle/service/RecycleService.java b/Domain/src/main/java/allchive/server/domain/domains/recycle/service/RecycleService.java deleted file mode 100644 index bc2e38f4..00000000 --- a/Domain/src/main/java/allchive/server/domain/domains/recycle/service/RecycleService.java +++ /dev/null @@ -1,7 +0,0 @@ -package allchive.server.domain.domains.recycle.service; - - -import allchive.server.core.annotation.DomainService; - -@DomainService -public class RecycleService {} From 512f2ffc8c5b4f121bb104385095d4ee1809687e Mon Sep 17 00:00:00 2001 From: Sanghoon Jeong <67852689+wjdtkdgns@users.noreply.github.com> Date: Tue, 11 Jul 2023 01:01:11 +0900 Subject: [PATCH 27/41] =?UTF-8?q?[refac]=20=EA=B8=B0=ED=9A=8D=20element=20?= =?UTF-8?q?=EB=84=A4=EC=9D=B4=EB=B0=8D=20=EB=B3=80=EA=B2=BD=20=EB=B0=98?= =?UTF-8?q?=EC=98=81=20(#30)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [refac] category -> archiving 네이밍 변경 #29 * [refac] subject -> category 네이밍 변경 #29 --- .../controller/ArchivingController.java | 115 ++++++++++++++ .../dto/request/CreateArchivingRequest.java | 23 +++ .../dto/request/UpdateArchivingRequest.java | 23 +++ .../response/ArchivingContentsResponse.java | 45 ++++++ .../model/dto/response/ArchivingResponse.java | 82 ++++++++++ .../dto/response/ArchivingTitleResponse.java} | 12 +- .../model/mapper/ArchivingMapper.java | 43 ++++++ .../model/vo/TitleContentCntVo.java | 14 +- .../service/CreateArchivingUseCase.java | 25 +++ .../service/DeleteArchivingUseCase.java} | 20 +-- .../service/GetArchivedArchivingUseCase.java | 34 ++++ .../service/GetArchivingContentsUseCase.java} | 28 ++-- .../service/GetArchivingTitleUseCase.java | 24 +++ .../service/GetArchivingUseCase.java} | 34 ++-- .../service/GetScrapArchivingUseCase.java | 39 +++++ .../service/UpdateArchivingPinUseCase.java | 30 ++++ .../service/UpdateArchivingScrapUseCase.java} | 28 ++-- .../service/UpdateArchivingUseCase.java | 34 ++++ .../model/dto/request/RegisterRequest.java | 2 +- .../controller/CategoryController.java | 115 -------------- .../dto/request/CreateCategoryRequest.java | 23 --- .../dto/request/UpdateCategoryRequest.java | 23 --- .../response/CategoryContentsResponse.java | 45 ------ .../model/dto/response/CategoryResponse.java | 82 ---------- .../category/model/mapper/CategoryMapper.java | 43 ------ .../service/CreateCategoryUseCase.java | 25 --- .../service/GetArchivedCategoryUseCase.java | 33 ---- .../service/GetCategoryTitleUseCase.java | 24 --- .../service/GetScrapCategoryUseCase.java | 39 ----- .../service/UpdateCategoryPinUseCase.java | 30 ---- .../service/UpdateCategoryUseCase.java | 34 ---- .../dto/request/CreateContentRequest.java | 4 +- .../content/model/mapper/ContentMapper.java | 2 +- .../content/service/CreateContentUseCase.java | 8 +- .../content/service/DeleteContentUseCase.java | 6 +- .../content/service/GetContentUseCase.java | 6 +- .../api/image/controller/ImageController.java | 6 +- .../recycle/model/mapper/RecycleMapper.java | 4 +- .../dto/response/GetUserProfileResponse.java | 10 +- .../api/user/model/mapper/UserMapper.java | 16 +- .../user/service/GetUserProfileUseCase.java | 10 +- .../archiving/adaptor/ArchivingAdaptor.java | 67 ++++++++ .../domain/Archiving.java} | 44 +++--- .../domain/enums/Category.java} | 4 +- .../exception/ArchivingErrorCode.java | 29 ++++ .../AlreadyPinnedArchivingException.java | 14 ++ .../ArchivingNotFoundException.java | 14 ++ .../exceptions/DeletedArchivingException.java | 14 ++ .../NoAuthurityUpdateArchivingException.java | 14 ++ .../NotPinnedArchivingException.java | 14 ++ .../NotPublicArchivingException.java | 14 ++ .../repository/ArchivingCustomRepository.java | 22 +++ .../ArchivingCustomRepositoryImpl.java | 146 ++++++++++++++++++ .../repository/ArchivingRepository.java | 11 ++ .../service/ArchivingDomainService.java | 50 ++++++ .../validator/ArchivingValidator.java | 49 ++++++ .../category/adaptor/CategoryAdaptor.java | 64 -------- .../category/exception/CategoryErrorCode.java | 29 ---- .../AlreadyPinnedCategoryException.java | 14 -- .../exceptions/CategoryNotFoundException.java | 14 -- .../exceptions/DeletedCategoryException.java | 14 -- .../NoAuthurityUpdateCategoryException.java | 14 -- .../NotPinnedCategoryException.java | 14 -- .../NotPublicCategoryException.java | 14 -- .../repository/CategoryCustomRepository.java | 22 --- .../CategoryCustomRepositoryImpl.java | 146 ------------------ .../repository/CategoryRepository.java | 11 -- .../service/CategoryDomainService.java | 50 ------ .../category/validator/CategoryValidator.java | 49 ------ .../content/adaptor/ContentAdaptor.java | 4 +- .../domains/content/domain/Content.java | 10 +- .../content/domain/enums/ContentType.java | 2 +- .../content/exception/ContentErrorCode.java | 2 +- .../repository/ContentCustomRepository.java | 2 +- .../ContentCustomRepositoryImpl.java | 12 +- .../ContentTagGroupCustomRepositoryImpl.java | 4 +- .../domains/recycle/domain/Recycle.java | 10 +- .../recycle/domain/enums/RecycleType.java | 4 +- .../domains/user/adaptor/ScrapAdaptor.java | 4 +- .../domain/domains/user/domain/Scrap.java | 10 +- .../domain/domains/user/domain/User.java | 6 +- .../user/domain/enums/OauthProvider.java | 2 +- .../user/repository/ScrapRepository.java | 2 +- .../user/service/ScrapDomainService.java | 4 +- .../user/validator/ScrapValidator.java | 4 +- .../infrastructure/s3/PresignedType.java | 2 +- .../s3/S3PresignedUrlService.java | 2 +- 87 files changed, 1147 insertions(+), 1143 deletions(-) create mode 100644 Api/src/main/java/allchive/server/api/archiving/controller/ArchivingController.java create mode 100644 Api/src/main/java/allchive/server/api/archiving/model/dto/request/CreateArchivingRequest.java create mode 100644 Api/src/main/java/allchive/server/api/archiving/model/dto/request/UpdateArchivingRequest.java create mode 100644 Api/src/main/java/allchive/server/api/archiving/model/dto/response/ArchivingContentsResponse.java create mode 100644 Api/src/main/java/allchive/server/api/archiving/model/dto/response/ArchivingResponse.java rename Api/src/main/java/allchive/server/api/{category/model/dto/response/CategoryTitleResponse.java => archiving/model/dto/response/ArchivingTitleResponse.java} (89%) create mode 100644 Api/src/main/java/allchive/server/api/archiving/model/mapper/ArchivingMapper.java rename Api/src/main/java/allchive/server/api/{category => archiving}/model/vo/TitleContentCntVo.java (50%) create mode 100644 Api/src/main/java/allchive/server/api/archiving/service/CreateArchivingUseCase.java rename Api/src/main/java/allchive/server/api/{category/service/DeleteCategoryUseCase.java => archiving/service/DeleteArchivingUseCase.java} (55%) create mode 100644 Api/src/main/java/allchive/server/api/archiving/service/GetArchivedArchivingUseCase.java rename Api/src/main/java/allchive/server/api/{category/service/GetCategoryContentsUseCase.java => archiving/service/GetArchivingContentsUseCase.java} (59%) create mode 100644 Api/src/main/java/allchive/server/api/archiving/service/GetArchivingTitleUseCase.java rename Api/src/main/java/allchive/server/api/{category/service/GetCategoryUseCase.java => archiving/service/GetArchivingUseCase.java} (51%) create mode 100644 Api/src/main/java/allchive/server/api/archiving/service/GetScrapArchivingUseCase.java create mode 100644 Api/src/main/java/allchive/server/api/archiving/service/UpdateArchivingPinUseCase.java rename Api/src/main/java/allchive/server/api/{category/service/UpdateCategoryScrapUseCase.java => archiving/service/UpdateArchivingScrapUseCase.java} (51%) create mode 100644 Api/src/main/java/allchive/server/api/archiving/service/UpdateArchivingUseCase.java delete mode 100644 Api/src/main/java/allchive/server/api/category/controller/CategoryController.java delete mode 100644 Api/src/main/java/allchive/server/api/category/model/dto/request/CreateCategoryRequest.java delete mode 100644 Api/src/main/java/allchive/server/api/category/model/dto/request/UpdateCategoryRequest.java delete mode 100644 Api/src/main/java/allchive/server/api/category/model/dto/response/CategoryContentsResponse.java delete mode 100644 Api/src/main/java/allchive/server/api/category/model/dto/response/CategoryResponse.java delete mode 100644 Api/src/main/java/allchive/server/api/category/model/mapper/CategoryMapper.java delete mode 100644 Api/src/main/java/allchive/server/api/category/service/CreateCategoryUseCase.java delete mode 100644 Api/src/main/java/allchive/server/api/category/service/GetArchivedCategoryUseCase.java delete mode 100644 Api/src/main/java/allchive/server/api/category/service/GetCategoryTitleUseCase.java delete mode 100644 Api/src/main/java/allchive/server/api/category/service/GetScrapCategoryUseCase.java delete mode 100644 Api/src/main/java/allchive/server/api/category/service/UpdateCategoryPinUseCase.java delete mode 100644 Api/src/main/java/allchive/server/api/category/service/UpdateCategoryUseCase.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/archiving/adaptor/ArchivingAdaptor.java rename Domain/src/main/java/allchive/server/domain/domains/{category/domain/Category.java => archiving/domain/Archiving.java} (67%) rename Domain/src/main/java/allchive/server/domain/domains/{category/domain/enums/Subject.java => archiving/domain/enums/Category.java} (90%) create mode 100644 Domain/src/main/java/allchive/server/domain/domains/archiving/exception/ArchivingErrorCode.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/archiving/exception/exceptions/AlreadyPinnedArchivingException.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/archiving/exception/exceptions/ArchivingNotFoundException.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/archiving/exception/exceptions/DeletedArchivingException.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/archiving/exception/exceptions/NoAuthurityUpdateArchivingException.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/archiving/exception/exceptions/NotPinnedArchivingException.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/archiving/exception/exceptions/NotPublicArchivingException.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingCustomRepository.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingCustomRepositoryImpl.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingRepository.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/archiving/service/ArchivingDomainService.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/archiving/validator/ArchivingValidator.java delete mode 100644 Domain/src/main/java/allchive/server/domain/domains/category/adaptor/CategoryAdaptor.java delete mode 100644 Domain/src/main/java/allchive/server/domain/domains/category/exception/CategoryErrorCode.java delete mode 100644 Domain/src/main/java/allchive/server/domain/domains/category/exception/exceptions/AlreadyPinnedCategoryException.java delete mode 100644 Domain/src/main/java/allchive/server/domain/domains/category/exception/exceptions/CategoryNotFoundException.java delete mode 100644 Domain/src/main/java/allchive/server/domain/domains/category/exception/exceptions/DeletedCategoryException.java delete mode 100644 Domain/src/main/java/allchive/server/domain/domains/category/exception/exceptions/NoAuthurityUpdateCategoryException.java delete mode 100644 Domain/src/main/java/allchive/server/domain/domains/category/exception/exceptions/NotPinnedCategoryException.java delete mode 100644 Domain/src/main/java/allchive/server/domain/domains/category/exception/exceptions/NotPublicCategoryException.java delete mode 100644 Domain/src/main/java/allchive/server/domain/domains/category/repository/CategoryCustomRepository.java delete mode 100644 Domain/src/main/java/allchive/server/domain/domains/category/repository/CategoryCustomRepositoryImpl.java delete mode 100644 Domain/src/main/java/allchive/server/domain/domains/category/repository/CategoryRepository.java delete mode 100644 Domain/src/main/java/allchive/server/domain/domains/category/service/CategoryDomainService.java delete mode 100644 Domain/src/main/java/allchive/server/domain/domains/category/validator/CategoryValidator.java diff --git a/Api/src/main/java/allchive/server/api/archiving/controller/ArchivingController.java b/Api/src/main/java/allchive/server/api/archiving/controller/ArchivingController.java new file mode 100644 index 00000000..ef85f68d --- /dev/null +++ b/Api/src/main/java/allchive/server/api/archiving/controller/ArchivingController.java @@ -0,0 +1,115 @@ +package allchive.server.api.archiving.controller; + + +import allchive.server.api.archiving.model.dto.request.CreateArchivingRequest; +import allchive.server.api.archiving.model.dto.request.UpdateArchivingRequest; +import allchive.server.api.archiving.model.dto.response.ArchivingContentsResponse; +import allchive.server.api.archiving.model.dto.response.ArchivingResponse; +import allchive.server.api.archiving.model.dto.response.ArchivingTitleResponse; +import allchive.server.api.archiving.service.*; +import allchive.server.api.common.slice.SliceResponse; +import allchive.server.domain.domains.archiving.domain.enums.Category; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springdoc.api.annotations.ParameterObject; +import org.springframework.data.domain.Pageable; +import org.springframework.data.web.PageableDefault; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/archivings") +@RequiredArgsConstructor +@Slf4j +@Tag(name = "3. [archiving]") +public class ArchivingController { + private final CreateArchivingUseCase createArchivingUseCase; + private final UpdateArchivingUseCase updateArchivingUseCase; + private final DeleteArchivingUseCase deleteArchivingUseCase; + private final GetArchivingUseCase getArchivingUseCase; + private final GetArchivedArchivingUseCase getArchivedArchivingUseCase; + private final GetScrapArchivingUseCase getScrapArchivingUseCase; + private final GetArchivingTitleUseCase getArchivingTitleUseCase; + private final GetArchivingContentsUseCase getArchivingContentsUseCase; + private final UpdateArchivingScrapUseCase updateArchivingScrapUseCase; + private final UpdateArchivingPinUseCase updateArchivingPinUseCase; + + @Operation(summary = "아카이빙을 생성합니다.") + @PostMapping() + public void createArchiving(@RequestBody CreateArchivingRequest createArchivingRequest) { + createArchivingUseCase.execute(createArchivingRequest); + } + + @Operation(summary = "아카이빙을 수정합니다.") + @PatchMapping(value = "/{archivingId}") + public void updateArchiving( + @PathVariable("archivingId") Long archivingId, + @RequestBody UpdateArchivingRequest updateArchivingRequest) { + updateArchivingUseCase.execute(archivingId, updateArchivingRequest); + } + + @Operation(summary = "아카이빙을 삭제합니다.") + @DeleteMapping(value = "/{archivingId}") + public void deleteArchiving(@PathVariable("archivingId") Long archivingId) { + deleteArchivingUseCase.execute(archivingId); + } + + @Operation( + summary = "주제별 아카이빙 리스트를 가져옵니다.", + description = "sort parameter는 입력하지 말아주세요! sorting : 스크랩 여부 -> 스크랩 수 -> 생성일자") + @GetMapping() + public SliceResponse<ArchivingResponse> getArchiving( + @RequestParam("category") Category category, + @ParameterObject @PageableDefault(size = 10) Pageable pageable) { + return getArchivingUseCase.execute(category, pageable); + } + + @Operation( + summary = "내 아카이빙 주제별 아카이빙 리스트를 가져옵니다.", + description = "sort parameter는 입력하지 말아주세요! sorting : 고정 -> 스크랩 수 -> 생성일자") + @GetMapping(value = "/me/archiving") + public SliceResponse<ArchivingResponse> getArchivedArchiving( + @RequestParam("category") Category category, + @ParameterObject @PageableDefault(size = 10) Pageable pageable) { + return getArchivedArchivingUseCase.execute(category, pageable); + } + + @Operation( + summary = "스크랩 주제별 아카이빙 리스트를 가져옵니다.", + description = "sort parameter는 입력하지 말아주세요! sorting : 스크랩 수 -> 생성일자") + @GetMapping(value = "/me/scrap") + public SliceResponse<ArchivingResponse> getScrapArchiving( + @RequestParam("category") Category category, + @ParameterObject @PageableDefault(size = 10) Pageable pageable) { + return getScrapArchivingUseCase.execute(category, pageable); + } + + @Operation(summary = "사용 중인 주제 & 아카이빙 리스트를 가져옵니다. (컨텐츠 추가 시 사용)") + @GetMapping(value = "/lists") + public ArchivingTitleResponse getScrapArchiving() { + return getArchivingTitleUseCase.execute(); + } + + @Operation(summary = "아카이빙별 컨텐츠 리스트를 가져옵니다.") + @GetMapping(value = "/{archivingId}/contents") + public ArchivingContentsResponse getArchivingContents( + @PathVariable("archivingId") Long archivingId, + @ParameterObject @PageableDefault(size = 10) Pageable pageable) { + return getArchivingContentsUseCase.execute(archivingId, pageable); + } + + @Operation(summary = "아카이빙을 스크랩합니다.", description = "스크랩 취소면 cancel에 true 값 보내주세요") + @PatchMapping(value = "/{archivingId}/scrap") + public void updateArchivingScrap( + @RequestParam("cancel") Boolean cancel, @PathVariable("archivingId") Long archivingId) { + updateArchivingScrapUseCase.execute(archivingId, cancel); + } + + @Operation(summary = "아카이빙을 고정합니다.", description = "고정 취소면 cancel에 true 값 보내주세요") + @PatchMapping(value = "/{archivingId}/pin") + public void updateArchivingPin( + @RequestParam("cancel") Boolean cancel, @PathVariable("archivingId") Long archivingId) { + updateArchivingPinUseCase.execute(archivingId, cancel); + } +} diff --git a/Api/src/main/java/allchive/server/api/archiving/model/dto/request/CreateArchivingRequest.java b/Api/src/main/java/allchive/server/api/archiving/model/dto/request/CreateArchivingRequest.java new file mode 100644 index 00000000..f317dc85 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/archiving/model/dto/request/CreateArchivingRequest.java @@ -0,0 +1,23 @@ +package allchive.server.api.archiving.model.dto.request; + + +import allchive.server.core.annotation.ValidEnum; +import allchive.server.domain.domains.archiving.domain.enums.Category; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; + +@Getter +public class CreateArchivingRequest { + @Schema(defaultValue = "아카이빙 제목", description = "아카이빙 제목") + private String title; + + @Schema(defaultValue = "아카이빙 이미지 url", description = "아카이빙 이미지 url") + private String imageUrl; + + @Schema(defaultValue = "DESIGN", description = "주제") + @ValidEnum(target = Category.class) + private Category category; + + @Schema(defaultValue = "false", description = "공개 여부") + private boolean publicStatus; +} diff --git a/Api/src/main/java/allchive/server/api/archiving/model/dto/request/UpdateArchivingRequest.java b/Api/src/main/java/allchive/server/api/archiving/model/dto/request/UpdateArchivingRequest.java new file mode 100644 index 00000000..afa449ac --- /dev/null +++ b/Api/src/main/java/allchive/server/api/archiving/model/dto/request/UpdateArchivingRequest.java @@ -0,0 +1,23 @@ +package allchive.server.api.archiving.model.dto.request; + + +import allchive.server.core.annotation.ValidEnum; +import allchive.server.domain.domains.archiving.domain.enums.Category; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; + +@Getter +public class UpdateArchivingRequest { + @Schema(defaultValue = "아카이빙 제목", description = "아카이빙 제목") + private String title; + + @Schema(defaultValue = "아카이빙 이미지 url", description = "아카이빙 이미지 url") + private String imageUrl; + + @Schema(defaultValue = "DESIGN", description = "주제") + @ValidEnum(target = Category.class) + private Category category; + + @Schema(defaultValue = "false", description = "공개 여부") + private boolean publicStatus; +} diff --git a/Api/src/main/java/allchive/server/api/archiving/model/dto/response/ArchivingContentsResponse.java b/Api/src/main/java/allchive/server/api/archiving/model/dto/response/ArchivingContentsResponse.java new file mode 100644 index 00000000..10479f2d --- /dev/null +++ b/Api/src/main/java/allchive/server/api/archiving/model/dto/response/ArchivingContentsResponse.java @@ -0,0 +1,45 @@ +package allchive.server.api.archiving.model.dto.response; + + +import allchive.server.api.common.slice.SliceResponse; +import allchive.server.api.content.model.dto.response.ContentResponse; +import allchive.server.domain.domains.archiving.domain.Archiving; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Builder; +import lombok.Getter; + +@Getter +public class ArchivingContentsResponse { + private SliceResponse<ContentResponse> contents; + + @Schema(description = "아카이빙 제목") + private String archivingTitle; + + @Schema(description = "아카이빙 고유번호") + private Long archivingId; + + @Schema(description = "아카이빙의 총 컨텐츠 개수") + private Long totalContentsCount; + + @Builder + private ArchivingContentsResponse( + SliceResponse<ContentResponse> contents, + String archivingTitle, + Long archivingId, + Long totalContentsCount) { + this.contents = contents; + this.archivingTitle = archivingTitle; + this.archivingId = archivingId; + this.totalContentsCount = totalContentsCount; + } + + public static ArchivingContentsResponse of( + SliceResponse<ContentResponse> contentResponseSlice, Archiving archiving) { + return ArchivingContentsResponse.builder() + .archivingId(archiving.getId()) + .archivingTitle(archiving.getTitle()) + .totalContentsCount(archiving.getScrapCnt() + archiving.getImgCnt()) + .contents(contentResponseSlice) + .build(); + } +} diff --git a/Api/src/main/java/allchive/server/api/archiving/model/dto/response/ArchivingResponse.java b/Api/src/main/java/allchive/server/api/archiving/model/dto/response/ArchivingResponse.java new file mode 100644 index 00000000..c63ac0d1 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/archiving/model/dto/response/ArchivingResponse.java @@ -0,0 +1,82 @@ +package allchive.server.api.archiving.model.dto.response; + + +import allchive.server.api.common.util.UrlUtil; +import allchive.server.core.annotation.DateFormat; +import allchive.server.domain.domains.archiving.domain.Archiving; +import allchive.server.domain.domains.archiving.domain.enums.Category; +import io.swagger.v3.oas.annotations.media.Schema; +import java.time.LocalDateTime; +import lombok.Builder; +import lombok.Getter; + +@Getter +public class ArchivingResponse { + @Schema(description = "아카이빙 고유 아이디") + private Long archivingId; + + @Schema(description = "아카이빙 제목") + private String title; + + @Schema(defaultValue = "아카이빙 이미지 url", description = "아카이빙 이미지 url") + private String imageUrl; + + @Schema( + type = "string", + pattern = "yyyy.MM.dd", + defaultValue = "2023.07.02", + description = "아카이빙 생성일자") + @DateFormat + private LocalDateTime createdAt; + + @Schema(defaultValue = "아카이빙 주제", description = "아카이빙 주제") + private Category category; + + @Schema(description = "아카이빙 컨텐츠 중 이미지 수") + private Long imgCnt; + + @Schema(description = "아카이빙 컨텐츠 중 링크 수") + private Long linkCnt; + + @Schema(description = "아카이빙 스크랩 수") + private Long scrapCnt; + + @Schema(description = "아카이빙 스크랩/고정 여부, true == 스크랩/고정됨") + private boolean markStatus; + + @Builder + private ArchivingResponse( + Long archivingId, + String title, + String imageUrl, + LocalDateTime createdAt, + Category category, + Long imgCnt, + Long linkCnt, + Long scrapCnt, + boolean markStatus) { + this.archivingId = archivingId; + this.title = title; + this.imageUrl = imageUrl; + this.createdAt = createdAt; + this.category = category; + this.imgCnt = imgCnt; + this.linkCnt = linkCnt; + this.scrapCnt = scrapCnt; + this.markStatus = markStatus; + } + + public static ArchivingResponse of(Archiving archiving, boolean markStatus) { + return ArchivingResponse.builder() + .archivingId(archiving.getId()) + .imageUrl(UrlUtil.toAssetUrl(archiving.getImageUrl())) + .title(archiving.getTitle()) + .createdAt(archiving.getCreatedAt()) + .category(archiving.getCategory()) + .imgCnt(archiving.getImgCnt()) + .linkCnt(archiving.getLinkCnt()) + .scrapCnt(archiving.getScrapCnt()) + .markStatus(markStatus) + .build(); + } +} diff --git a/Api/src/main/java/allchive/server/api/category/model/dto/response/CategoryTitleResponse.java b/Api/src/main/java/allchive/server/api/archiving/model/dto/response/ArchivingTitleResponse.java similarity index 89% rename from Api/src/main/java/allchive/server/api/category/model/dto/response/CategoryTitleResponse.java rename to Api/src/main/java/allchive/server/api/archiving/model/dto/response/ArchivingTitleResponse.java index af98c0f8..038f4893 100644 --- a/Api/src/main/java/allchive/server/api/category/model/dto/response/CategoryTitleResponse.java +++ b/Api/src/main/java/allchive/server/api/archiving/model/dto/response/ArchivingTitleResponse.java @@ -1,14 +1,14 @@ -package allchive.server.api.category.model.dto.response; +package allchive.server.api.archiving.model.dto.response; -import allchive.server.api.category.model.vo.TitleContentCntVo; +import allchive.server.api.archiving.model.vo.TitleContentCntVo; import java.util.ArrayList; import java.util.List; import lombok.Builder; import lombok.Getter; @Getter -public class CategoryTitleResponse { +public class ArchivingTitleResponse { private List<TitleContentCntVo> food; private List<TitleContentCntVo> life; private List<TitleContentCntVo> homeLiving; @@ -20,7 +20,7 @@ public class CategoryTitleResponse { private List<TitleContentCntVo> trend; @Builder - private CategoryTitleResponse( + private ArchivingTitleResponse( List<TitleContentCntVo> food, List<TitleContentCntVo> life, List<TitleContentCntVo> homeLiving, @@ -41,8 +41,8 @@ private CategoryTitleResponse( this.trend = trend; } - public static CategoryTitleResponse init() { - return CategoryTitleResponse.builder() + public static ArchivingTitleResponse init() { + return ArchivingTitleResponse.builder() .food(new ArrayList<>()) .life(new ArrayList<>()) .homeLiving(new ArrayList<>()) diff --git a/Api/src/main/java/allchive/server/api/archiving/model/mapper/ArchivingMapper.java b/Api/src/main/java/allchive/server/api/archiving/model/mapper/ArchivingMapper.java new file mode 100644 index 00000000..678cb2ee --- /dev/null +++ b/Api/src/main/java/allchive/server/api/archiving/model/mapper/ArchivingMapper.java @@ -0,0 +1,43 @@ +package allchive.server.api.archiving.model.mapper; + + +import allchive.server.api.archiving.model.dto.request.CreateArchivingRequest; +import allchive.server.api.archiving.model.dto.response.ArchivingTitleResponse; +import allchive.server.api.archiving.model.vo.TitleContentCntVo; +import allchive.server.api.common.util.UrlUtil; +import allchive.server.core.annotation.Mapper; +import allchive.server.domain.domains.archiving.domain.Archiving; +import java.util.List; + +@Mapper +public class ArchivingMapper { + public Archiving toEntity(CreateArchivingRequest request, Long userId) { + return Archiving.of( + userId, + request.getTitle(), + UrlUtil.convertUrlToKey(request.getImageUrl()), + request.isPublicStatus(), + request.getCategory()); + } + + public ArchivingTitleResponse toArchivingTitleResponse(List<Archiving> archivings) { + ArchivingTitleResponse response = ArchivingTitleResponse.init(); + archivings.forEach( + archiving -> { + switch (archiving.getCategory()) { + case FOOD -> response.addFood(TitleContentCntVo.from(archiving)); + case LIFE -> response.addLife(TitleContentCntVo.from(archiving)); + case HOME_LIVING -> response.addHomeLiving( + TitleContentCntVo.from(archiving)); + case SHOPPING -> response.addShopping(TitleContentCntVo.from(archiving)); + case SPORT -> response.addSport(TitleContentCntVo.from(archiving)); + case SELF_IMPROVEMENT -> response.addSelfImprovement( + TitleContentCntVo.from(archiving)); + case TECH -> response.addTech(TitleContentCntVo.from(archiving)); + case DESIGN -> response.addDesign(TitleContentCntVo.from(archiving)); + case TREND -> response.addTrend(TitleContentCntVo.from(archiving)); + } + }); + return response; + } +} diff --git a/Api/src/main/java/allchive/server/api/category/model/vo/TitleContentCntVo.java b/Api/src/main/java/allchive/server/api/archiving/model/vo/TitleContentCntVo.java similarity index 50% rename from Api/src/main/java/allchive/server/api/category/model/vo/TitleContentCntVo.java rename to Api/src/main/java/allchive/server/api/archiving/model/vo/TitleContentCntVo.java index 34d5f03e..60085516 100644 --- a/Api/src/main/java/allchive/server/api/category/model/vo/TitleContentCntVo.java +++ b/Api/src/main/java/allchive/server/api/archiving/model/vo/TitleContentCntVo.java @@ -1,17 +1,17 @@ -package allchive.server.api.category.model.vo; +package allchive.server.api.archiving.model.vo; -import allchive.server.domain.domains.category.domain.Category; +import allchive.server.domain.domains.archiving.domain.Archiving; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Builder; import lombok.Getter; @Getter public class TitleContentCntVo { - @Schema(defaultValue = "카테고리 제목", description = "카테고리 제목") + @Schema(defaultValue = "아카이빙 제목", description = "아카이빙 제목") private String title; - @Schema(defaultValue = "1", description = "카테고리에 속한 컨텐츠 총 개수") + @Schema(defaultValue = "1", description = "아카이빙에 속한 컨텐츠 총 개수") private Long contentCnt; @Builder @@ -20,10 +20,10 @@ private TitleContentCntVo(String title, Long contentCnt) { this.contentCnt = contentCnt; } - public static TitleContentCntVo from(Category category) { + public static TitleContentCntVo from(Archiving archiving) { return TitleContentCntVo.builder() - .contentCnt(category.getImgCnt() + category.getScrapCnt()) - .title(category.getTitle()) + .contentCnt(archiving.getImgCnt() + archiving.getScrapCnt()) + .title(archiving.getTitle()) .build(); } } diff --git a/Api/src/main/java/allchive/server/api/archiving/service/CreateArchivingUseCase.java b/Api/src/main/java/allchive/server/api/archiving/service/CreateArchivingUseCase.java new file mode 100644 index 00000000..43eaddb6 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/archiving/service/CreateArchivingUseCase.java @@ -0,0 +1,25 @@ +package allchive.server.api.archiving.service; + + +import allchive.server.api.archiving.model.dto.request.CreateArchivingRequest; +import allchive.server.api.archiving.model.mapper.ArchivingMapper; +import allchive.server.api.config.security.SecurityUtil; +import allchive.server.core.annotation.UseCase; +import allchive.server.domain.domains.archiving.domain.Archiving; +import allchive.server.domain.domains.archiving.service.ArchivingDomainService; +import lombok.RequiredArgsConstructor; +import org.springframework.transaction.annotation.Transactional; + +@UseCase +@RequiredArgsConstructor +public class CreateArchivingUseCase { + private final ArchivingMapper archivingMapper; + private final ArchivingDomainService archivingDomainService; + + @Transactional + public void execute(CreateArchivingRequest request) { + Long userId = SecurityUtil.getCurrentUserId(); + final Archiving archiving = archivingMapper.toEntity(request, userId); + archivingDomainService.createArchiving(archiving); + } +} diff --git a/Api/src/main/java/allchive/server/api/category/service/DeleteCategoryUseCase.java b/Api/src/main/java/allchive/server/api/archiving/service/DeleteArchivingUseCase.java similarity index 55% rename from Api/src/main/java/allchive/server/api/category/service/DeleteCategoryUseCase.java rename to Api/src/main/java/allchive/server/api/archiving/service/DeleteArchivingUseCase.java index 74bf1e26..7c8de3c0 100644 --- a/Api/src/main/java/allchive/server/api/category/service/DeleteCategoryUseCase.java +++ b/Api/src/main/java/allchive/server/api/archiving/service/DeleteArchivingUseCase.java @@ -1,11 +1,11 @@ -package allchive.server.api.category.service; +package allchive.server.api.archiving.service; import allchive.server.api.config.security.SecurityUtil; import allchive.server.api.recycle.model.mapper.RecycleMapper; import allchive.server.core.annotation.UseCase; -import allchive.server.domain.domains.category.service.CategoryDomainService; -import allchive.server.domain.domains.category.validator.CategoryValidator; +import allchive.server.domain.domains.archiving.service.ArchivingDomainService; +import allchive.server.domain.domains.archiving.validator.ArchivingValidator; import allchive.server.domain.domains.recycle.domain.Recycle; import allchive.server.domain.domains.recycle.domain.enums.RecycleType; import allchive.server.domain.domains.recycle.service.RecycleDomainService; @@ -14,19 +14,19 @@ @UseCase @RequiredArgsConstructor -public class DeleteCategoryUseCase { - private final CategoryDomainService categoryDomainService; - private final CategoryValidator categoryValidator; +public class DeleteArchivingUseCase { + private final ArchivingDomainService archivingDomainService; + private final ArchivingValidator archivingValidator; private final RecycleMapper recycleMapper; private final RecycleDomainService recycleDomainService; @Transactional - public void execute(Long categoryId) { + public void execute(Long archivingId) { Long userId = SecurityUtil.getCurrentUserId(); - categoryValidator.verifyUser(userId, categoryId); - categoryDomainService.deleteById(categoryId); + archivingValidator.verifyUser(userId, archivingId); + archivingDomainService.deleteById(archivingId); Recycle recycle = - recycleMapper.toCategoryRecycleEntity(userId, categoryId, RecycleType.CATEGORY); + recycleMapper.toArchivingRecycleEntity(userId, archivingId, RecycleType.ARCHIVING); recycleDomainService.save(recycle); } } diff --git a/Api/src/main/java/allchive/server/api/archiving/service/GetArchivedArchivingUseCase.java b/Api/src/main/java/allchive/server/api/archiving/service/GetArchivedArchivingUseCase.java new file mode 100644 index 00000000..e42b8f4c --- /dev/null +++ b/Api/src/main/java/allchive/server/api/archiving/service/GetArchivedArchivingUseCase.java @@ -0,0 +1,34 @@ +package allchive.server.api.archiving.service; + + +import allchive.server.api.archiving.model.dto.response.ArchivingResponse; +import allchive.server.api.common.slice.SliceResponse; +import allchive.server.api.config.security.SecurityUtil; +import allchive.server.core.annotation.UseCase; +import allchive.server.domain.domains.archiving.adaptor.ArchivingAdaptor; +import allchive.server.domain.domains.archiving.domain.enums.Category; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; +import org.springframework.transaction.annotation.Transactional; + +@UseCase +@RequiredArgsConstructor +public class GetArchivedArchivingUseCase { + // TODO : 이거 네이밍 한번 수정해야함 + private final ArchivingAdaptor archivingAdaptor; + + @Transactional(readOnly = true) + public SliceResponse<ArchivingResponse> execute(Category category, Pageable pageable) { + Long userId = SecurityUtil.getCurrentUserId(); + Slice<ArchivingResponse> archivingSlices = + archivingAdaptor + .querySliceArchivingByUserId(userId, category, pageable) + .map( + archiving -> + ArchivingResponse.of( + archiving, + archiving.getPinUserId().contains(userId))); + return SliceResponse.of(archivingSlices); + } +} diff --git a/Api/src/main/java/allchive/server/api/category/service/GetCategoryContentsUseCase.java b/Api/src/main/java/allchive/server/api/archiving/service/GetArchivingContentsUseCase.java similarity index 59% rename from Api/src/main/java/allchive/server/api/category/service/GetCategoryContentsUseCase.java rename to Api/src/main/java/allchive/server/api/archiving/service/GetArchivingContentsUseCase.java index c34643e8..05fc8b9d 100644 --- a/Api/src/main/java/allchive/server/api/category/service/GetCategoryContentsUseCase.java +++ b/Api/src/main/java/allchive/server/api/archiving/service/GetArchivingContentsUseCase.java @@ -1,15 +1,15 @@ -package allchive.server.api.category.service; +package allchive.server.api.archiving.service; -import allchive.server.api.category.model.dto.response.CategoryContentsResponse; +import allchive.server.api.archiving.model.dto.response.ArchivingContentsResponse; import allchive.server.api.common.slice.SliceResponse; import allchive.server.api.config.security.SecurityUtil; import allchive.server.api.content.model.dto.response.ContentResponse; import allchive.server.api.content.model.mapper.ContentMapper; import allchive.server.core.annotation.UseCase; -import allchive.server.domain.domains.category.adaptor.CategoryAdaptor; -import allchive.server.domain.domains.category.domain.Category; -import allchive.server.domain.domains.category.validator.CategoryValidator; +import allchive.server.domain.domains.archiving.adaptor.ArchivingAdaptor; +import allchive.server.domain.domains.archiving.domain.Archiving; +import allchive.server.domain.domains.archiving.validator.ArchivingValidator; import allchive.server.domain.domains.content.adaptor.ContentAdaptor; import allchive.server.domain.domains.content.adaptor.ContentTagGroupAdaptor; import allchive.server.domain.domains.content.domain.Content; @@ -22,26 +22,26 @@ @UseCase @RequiredArgsConstructor -public class GetCategoryContentsUseCase { - private final CategoryAdaptor categoryAdaptor; - private final CategoryValidator categoryValidator; +public class GetArchivingContentsUseCase { + private final ArchivingAdaptor archivingAdaptor; + private final ArchivingValidator archivingValidator; private final ContentAdaptor contentAdaptor; private final ContentTagGroupAdaptor contentTagGroupAdaptor; private final ContentMapper contentMapper; @Transactional(readOnly = true) - public CategoryContentsResponse execute(Long categoryId, Pageable pageable) { + public ArchivingContentsResponse execute(Long archivingId, Pageable pageable) { Long userId = SecurityUtil.getCurrentUserId(); - categoryValidator.validatePublicStatus(categoryId, userId); - categoryValidator.validateDeleteStatus(categoryId, userId); - Category category = categoryAdaptor.findById(categoryId); + archivingValidator.validatePublicStatus(archivingId, userId); + archivingValidator.validateDeleteStatus(archivingId, userId); + Archiving archiving = archivingAdaptor.findById(archivingId); Slice<Content> contentList = - contentAdaptor.querySliceContentByCategoryId(categoryId, pageable); + contentAdaptor.querySliceContentByArchivingId(archivingId, pageable); List<ContentTagGroup> contentTagGroupList = contentTagGroupAdaptor.queryContentIn(contentList.getContent()); Slice<ContentResponse> contentResponseSlice = contentList.map( content -> contentMapper.toContentResponse(content, contentTagGroupList)); - return CategoryContentsResponse.of(SliceResponse.of(contentResponseSlice), category); + return ArchivingContentsResponse.of(SliceResponse.of(contentResponseSlice), archiving); } } diff --git a/Api/src/main/java/allchive/server/api/archiving/service/GetArchivingTitleUseCase.java b/Api/src/main/java/allchive/server/api/archiving/service/GetArchivingTitleUseCase.java new file mode 100644 index 00000000..0b594cb6 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/archiving/service/GetArchivingTitleUseCase.java @@ -0,0 +1,24 @@ +package allchive.server.api.archiving.service; + + +import allchive.server.api.archiving.model.dto.response.ArchivingTitleResponse; +import allchive.server.api.archiving.model.mapper.ArchivingMapper; +import allchive.server.api.config.security.SecurityUtil; +import allchive.server.core.annotation.UseCase; +import allchive.server.domain.domains.archiving.adaptor.ArchivingAdaptor; +import lombok.RequiredArgsConstructor; +import org.springframework.transaction.annotation.Transactional; + +@UseCase +@RequiredArgsConstructor +public class GetArchivingTitleUseCase { + private final ArchivingAdaptor archivingAdaptor; + private final ArchivingMapper archivingMapper; + + @Transactional(readOnly = true) + public ArchivingTitleResponse execute() { + Long userId = SecurityUtil.getCurrentUserId(); + return archivingMapper.toArchivingTitleResponse( + archivingAdaptor.queryArchivingByUserId(userId)); + } +} diff --git a/Api/src/main/java/allchive/server/api/category/service/GetCategoryUseCase.java b/Api/src/main/java/allchive/server/api/archiving/service/GetArchivingUseCase.java similarity index 51% rename from Api/src/main/java/allchive/server/api/category/service/GetCategoryUseCase.java rename to Api/src/main/java/allchive/server/api/archiving/service/GetArchivingUseCase.java index 32c89742..68437292 100644 --- a/Api/src/main/java/allchive/server/api/category/service/GetCategoryUseCase.java +++ b/Api/src/main/java/allchive/server/api/archiving/service/GetArchivingUseCase.java @@ -1,14 +1,14 @@ -package allchive.server.api.category.service; +package allchive.server.api.archiving.service; -import allchive.server.api.category.model.dto.response.CategoryResponse; +import allchive.server.api.archiving.model.dto.response.ArchivingResponse; import allchive.server.api.common.slice.SliceResponse; import allchive.server.api.config.security.SecurityUtil; import allchive.server.core.annotation.UseCase; import allchive.server.domain.domains.block.adaptor.BlockAdaptor; import allchive.server.domain.domains.block.domain.Block; -import allchive.server.domain.domains.category.adaptor.CategoryAdaptor; -import allchive.server.domain.domains.category.domain.enums.Subject; +import allchive.server.domain.domains.archiving.adaptor.ArchivingAdaptor; +import allchive.server.domain.domains.archiving.domain.enums.Category; import allchive.server.domain.domains.user.adaptor.ScrapAdaptor; import allchive.server.domain.domains.user.domain.Scrap; import java.util.List; @@ -19,26 +19,26 @@ @UseCase @RequiredArgsConstructor -public class GetCategoryUseCase { +public class GetArchivingUseCase { private final ScrapAdaptor scrapAdaptor; private final BlockAdaptor blockAdaptor; - private final CategoryAdaptor categoryAdaptor; + private final ArchivingAdaptor archivingAdaptor; @Transactional(readOnly = true) - public SliceResponse<CategoryResponse> execute(Subject subject, Pageable pageable) { + public SliceResponse<ArchivingResponse> execute(Category category, Pageable pageable) { Long userId = SecurityUtil.getCurrentUserId(); - List<Long> categoryIdList = - scrapAdaptor.findAllByUserId(userId).stream().map(Scrap::getCategoryId).toList(); + List<Long> archivingIdList = + scrapAdaptor.findAllByUserId(userId).stream().map(Scrap::getArchivingId).toList(); List<Long> blockList = blockAdaptor.findByBlockFrom(userId).stream().map(Block::getBlockUser).toList(); - Slice<CategoryResponse> categorySlices = - categoryAdaptor - .querySliceCategoryExceptBlock(categoryIdList, blockList, subject, pageable) + Slice<ArchivingResponse> archivingSlices = + archivingAdaptor + .querySliceArchivingExceptBlock(archivingIdList, blockList, category, pageable) .map( - category -> - CategoryResponse.of( - category, - categoryIdList.contains(category.getId()))); - return SliceResponse.of(categorySlices); + archiving -> + ArchivingResponse.of( + archiving, + archivingIdList.contains(archiving.getId()))); + return SliceResponse.of(archivingSlices); } } diff --git a/Api/src/main/java/allchive/server/api/archiving/service/GetScrapArchivingUseCase.java b/Api/src/main/java/allchive/server/api/archiving/service/GetScrapArchivingUseCase.java new file mode 100644 index 00000000..c092d6bc --- /dev/null +++ b/Api/src/main/java/allchive/server/api/archiving/service/GetScrapArchivingUseCase.java @@ -0,0 +1,39 @@ +package allchive.server.api.archiving.service; + + +import allchive.server.api.archiving.model.dto.response.ArchivingResponse; +import allchive.server.api.common.slice.SliceResponse; +import allchive.server.api.config.security.SecurityUtil; +import allchive.server.core.annotation.UseCase; +import allchive.server.domain.domains.archiving.adaptor.ArchivingAdaptor; +import allchive.server.domain.domains.archiving.domain.enums.Category; +import allchive.server.domain.domains.user.adaptor.ScrapAdaptor; +import allchive.server.domain.domains.user.domain.Scrap; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; +import org.springframework.transaction.annotation.Transactional; + +@UseCase +@RequiredArgsConstructor +public class GetScrapArchivingUseCase { + private final ScrapAdaptor scrapAdaptor; + private final ArchivingAdaptor archivingAdaptor; + + @Transactional(readOnly = true) + public SliceResponse<ArchivingResponse> execute(Category category, Pageable pageable) { + Long userId = SecurityUtil.getCurrentUserId(); + List<Long> archivingIdList = + scrapAdaptor.findAllByUserId(userId).stream().map(Scrap::getArchivingId).toList(); + Slice<ArchivingResponse> archivingSlices = + archivingAdaptor + .querySliceArchivingIn(archivingIdList, category, pageable) + .map( + archiving -> + ArchivingResponse.of( + archiving, + archivingIdList.contains(archiving.getId()))); + return SliceResponse.of(archivingSlices); + } +} diff --git a/Api/src/main/java/allchive/server/api/archiving/service/UpdateArchivingPinUseCase.java b/Api/src/main/java/allchive/server/api/archiving/service/UpdateArchivingPinUseCase.java new file mode 100644 index 00000000..0ae77af9 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/archiving/service/UpdateArchivingPinUseCase.java @@ -0,0 +1,30 @@ +package allchive.server.api.archiving.service; + + +import allchive.server.api.config.security.SecurityUtil; +import allchive.server.core.annotation.UseCase; +import allchive.server.domain.domains.archiving.service.ArchivingDomainService; +import allchive.server.domain.domains.archiving.validator.ArchivingValidator; +import lombok.RequiredArgsConstructor; +import org.springframework.transaction.annotation.Transactional; + +@UseCase +@RequiredArgsConstructor +public class UpdateArchivingPinUseCase { + private final ArchivingValidator archivingValidator; + private final ArchivingDomainService archivingDomainService; + + @Transactional + public void execute(Long archivingId, Boolean cancel) { + Long userId = SecurityUtil.getCurrentUserId(); + archivingValidator.validateExistArchiving(archivingId); + archivingValidator.validateDeleteStatus(archivingId, userId); + if (cancel) { + archivingValidator.validateNotPinStatus(archivingId, userId); + archivingDomainService.updatePin(archivingId, userId, false); + } else { + archivingValidator.validateAlreadyPinStatus(archivingId, userId); + archivingDomainService.updatePin(archivingId, userId, true); + } + } +} diff --git a/Api/src/main/java/allchive/server/api/category/service/UpdateCategoryScrapUseCase.java b/Api/src/main/java/allchive/server/api/archiving/service/UpdateArchivingScrapUseCase.java similarity index 51% rename from Api/src/main/java/allchive/server/api/category/service/UpdateCategoryScrapUseCase.java rename to Api/src/main/java/allchive/server/api/archiving/service/UpdateArchivingScrapUseCase.java index 0a3fd39a..4ac4c2ef 100644 --- a/Api/src/main/java/allchive/server/api/category/service/UpdateCategoryScrapUseCase.java +++ b/Api/src/main/java/allchive/server/api/archiving/service/UpdateArchivingScrapUseCase.java @@ -1,10 +1,10 @@ -package allchive.server.api.category.service; +package allchive.server.api.archiving.service; import allchive.server.api.config.security.SecurityUtil; import allchive.server.core.annotation.UseCase; -import allchive.server.domain.domains.category.service.CategoryDomainService; -import allchive.server.domain.domains.category.validator.CategoryValidator; +import allchive.server.domain.domains.archiving.service.ArchivingDomainService; +import allchive.server.domain.domains.archiving.validator.ArchivingValidator; import allchive.server.domain.domains.user.adaptor.UserAdaptor; import allchive.server.domain.domains.user.domain.Scrap; import allchive.server.domain.domains.user.domain.User; @@ -15,27 +15,27 @@ @UseCase @RequiredArgsConstructor -public class UpdateCategoryScrapUseCase { - private final CategoryValidator categoryValidator; +public class UpdateArchivingScrapUseCase { + private final ArchivingValidator archivingValidator; private final UserAdaptor userAdaptor; private final ScrapDomainService scrapDomainService; - private final CategoryDomainService categoryDomainService; + private final ArchivingDomainService archivingDomainService; private final ScrapValidator scrapValidator; @Transactional - public void execute(Long categoryId, Boolean cancel) { - categoryValidator.validateExistCategory(categoryId); + public void execute(Long archivingId, Boolean cancel) { + archivingValidator.validateExistArchiving(archivingId); Long userId = SecurityUtil.getCurrentUserId(); - categoryValidator.validateDeleteStatus(categoryId, userId); + archivingValidator.validateDeleteStatus(archivingId, userId); User user = userAdaptor.queryUserById(userId); if (cancel) { - scrapDomainService.deleteScrapByUserAndCategoryId(user, categoryId); - categoryDomainService.updateScrapCount(categoryId, -1); + scrapDomainService.deleteScrapByUserAndArchivingId(user, archivingId); + archivingDomainService.updateScrapCount(archivingId, -1); } else { - scrapValidator.validateExistScrap(user, categoryId); - Scrap scrap = Scrap.of(user, categoryId); + scrapValidator.validateExistScrap(user, archivingId); + Scrap scrap = Scrap.of(user, archivingId); scrapDomainService.save(scrap); - categoryDomainService.updateScrapCount(categoryId, 1); + archivingDomainService.updateScrapCount(archivingId, 1); } } } diff --git a/Api/src/main/java/allchive/server/api/archiving/service/UpdateArchivingUseCase.java b/Api/src/main/java/allchive/server/api/archiving/service/UpdateArchivingUseCase.java new file mode 100644 index 00000000..65ceabfd --- /dev/null +++ b/Api/src/main/java/allchive/server/api/archiving/service/UpdateArchivingUseCase.java @@ -0,0 +1,34 @@ +package allchive.server.api.archiving.service; + + +import allchive.server.api.archiving.model.dto.request.UpdateArchivingRequest; +import allchive.server.api.common.util.UrlUtil; +import allchive.server.api.config.security.SecurityUtil; +import allchive.server.core.annotation.UseCase; +import allchive.server.domain.domains.archiving.adaptor.ArchivingAdaptor; +import allchive.server.domain.domains.archiving.domain.Archiving; +import allchive.server.domain.domains.archiving.service.ArchivingDomainService; +import allchive.server.domain.domains.archiving.validator.ArchivingValidator; +import lombok.RequiredArgsConstructor; +import org.springframework.transaction.annotation.Transactional; + +@UseCase +@RequiredArgsConstructor +public class UpdateArchivingUseCase { + private final ArchivingDomainService archivingDomainService; + private final ArchivingAdaptor archivingAdaptor; + private final ArchivingValidator archivingValidator; + + @Transactional + public void execute(Long archivingId, UpdateArchivingRequest request) { + Long userId = SecurityUtil.getCurrentUserId(); + archivingValidator.verifyUser(userId, archivingId); + Archiving archiving = archivingAdaptor.findById(archivingId); + archivingDomainService.updateArchiving( + archiving, + request.getTitle(), + UrlUtil.convertUrlToKey(request.getImageUrl()), + request.isPublicStatus(), + request.getCategory()); + } +} diff --git a/Api/src/main/java/allchive/server/api/auth/model/dto/request/RegisterRequest.java b/Api/src/main/java/allchive/server/api/auth/model/dto/request/RegisterRequest.java index 7b81e4ca..039fd65c 100644 --- a/Api/src/main/java/allchive/server/api/auth/model/dto/request/RegisterRequest.java +++ b/Api/src/main/java/allchive/server/api/auth/model/dto/request/RegisterRequest.java @@ -10,7 +10,7 @@ @NoArgsConstructor public class RegisterRequest { @Schema( - defaultValue = "staging/category/1/d241218a-a64c-4443-8aa4-ce98017a3d12", + defaultValue = "staging/archiving/1/d241218a-a64c-4443-8aa4-ce98017a3d12", description = "프로필 이미지 url") @NotBlank(message = "프로필 이미지 key를 입력하세요") private String profileImgKey; diff --git a/Api/src/main/java/allchive/server/api/category/controller/CategoryController.java b/Api/src/main/java/allchive/server/api/category/controller/CategoryController.java deleted file mode 100644 index 1b8c7e3e..00000000 --- a/Api/src/main/java/allchive/server/api/category/controller/CategoryController.java +++ /dev/null @@ -1,115 +0,0 @@ -package allchive.server.api.category.controller; - - -import allchive.server.api.category.model.dto.request.CreateCategoryRequest; -import allchive.server.api.category.model.dto.request.UpdateCategoryRequest; -import allchive.server.api.category.model.dto.response.CategoryContentsResponse; -import allchive.server.api.category.model.dto.response.CategoryResponse; -import allchive.server.api.category.model.dto.response.CategoryTitleResponse; -import allchive.server.api.category.service.*; -import allchive.server.api.common.slice.SliceResponse; -import allchive.server.domain.domains.category.domain.enums.Subject; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springdoc.api.annotations.ParameterObject; -import org.springframework.data.domain.Pageable; -import org.springframework.data.web.PageableDefault; -import org.springframework.web.bind.annotation.*; - -@RestController -@RequestMapping("/categories") -@RequiredArgsConstructor -@Slf4j -@Tag(name = "3. [category]") -public class CategoryController { - private final CreateCategoryUseCase createCategoryUseCase; - private final UpdateCategoryUseCase updateCategoryUseCase; - private final DeleteCategoryUseCase deleteCategoryUseCase; - private final GetCategoryUseCase getCategoryUseCase; - private final GetArchivedCategoryUseCase getArchivedCategoryUseCase; - private final GetScrapCategoryUseCase getScrapCategoryUseCase; - private final GetCategoryTitleUseCase getCategoryTitleUseCase; - private final GetCategoryContentsUseCase getCategoryContentsUseCase; - private final UpdateCategoryScrapUseCase updateCategoryScrapUseCase; - private final UpdateCategoryPinUseCase updateCategoryPinUseCase; - - @Operation(summary = "카테고리를 생성합니다.") - @PostMapping() - public void createCategory(@RequestBody CreateCategoryRequest createCategoryRequest) { - createCategoryUseCase.execute(createCategoryRequest); - } - - @Operation(summary = "카테고리를 수정합니다.") - @PatchMapping(value = "/{categoryId}") - public void updateCategory( - @PathVariable("categoryId") Long categoryId, - @RequestBody UpdateCategoryRequest updateCategoryRequest) { - updateCategoryUseCase.execute(categoryId, updateCategoryRequest); - } - - @Operation(summary = "카테고리를 삭제합니다.") - @DeleteMapping(value = "/{categoryId}") - public void deleteCategory(@PathVariable("categoryId") Long categoryId) { - deleteCategoryUseCase.execute(categoryId); - } - - @Operation( - summary = "주제별 카테고리 리스트를 가져옵니다.", - description = "sort parameter는 입력하지 말아주세요! sorting : 스크랩 여부 -> 스크랩 수 -> 생성일자") - @GetMapping() - public SliceResponse<CategoryResponse> getCategory( - @RequestParam("subject") Subject subject, - @ParameterObject @PageableDefault(size = 10) Pageable pageable) { - return getCategoryUseCase.execute(subject, pageable); - } - - @Operation( - summary = "내 아카이빙 주제별 카테고리 리스트를 가져옵니다.", - description = "sort parameter는 입력하지 말아주세요! sorting : 고정 -> 스크랩 수 -> 생성일자") - @GetMapping(value = "/me/archiving") - public SliceResponse<CategoryResponse> getArchivedCategory( - @RequestParam("subject") Subject subject, - @ParameterObject @PageableDefault(size = 10) Pageable pageable) { - return getArchivedCategoryUseCase.execute(subject, pageable); - } - - @Operation( - summary = "스크랩 주제별 카테고리 리스트를 가져옵니다.", - description = "sort parameter는 입력하지 말아주세요! sorting : 스크랩 수 -> 생성일자") - @GetMapping(value = "/me/scrap") - public SliceResponse<CategoryResponse> getScrapCategory( - @RequestParam("subject") Subject subject, - @ParameterObject @PageableDefault(size = 10) Pageable pageable) { - return getScrapCategoryUseCase.execute(subject, pageable); - } - - @Operation(summary = "사용 중인 주제 & 카테고리 리스트를 가져옵니다. (컨텐츠 추가 시 사용)") - @GetMapping(value = "/lists") - public CategoryTitleResponse getScrapCategory() { - return getCategoryTitleUseCase.execute(); - } - - @Operation(summary = "카테고리별 컨텐츠 리스트를 가져옵니다.") - @GetMapping(value = "/{categoryId}/contents") - public CategoryContentsResponse getCategoryContents( - @PathVariable("categoryId") Long categoryId, - @ParameterObject @PageableDefault(size = 10) Pageable pageable) { - return getCategoryContentsUseCase.execute(categoryId, pageable); - } - - @Operation(summary = "카테고리를 스크랩합니다.", description = "스크랩 취소면 cancel에 true 값 보내주세요") - @PatchMapping(value = "/{categoryId}/scrap") - public void updateCategoryScrap( - @RequestParam("cancel") Boolean cancel, @PathVariable("categoryId") Long categoryId) { - updateCategoryScrapUseCase.execute(categoryId, cancel); - } - - @Operation(summary = "카테고리를 고정합니다.", description = "고정 취소면 cancel에 true 값 보내주세요") - @PatchMapping(value = "/{categoryId}/pin") - public void updateCategoryPin( - @RequestParam("cancel") Boolean cancel, @PathVariable("categoryId") Long categoryId) { - updateCategoryPinUseCase.execute(categoryId, cancel); - } -} diff --git a/Api/src/main/java/allchive/server/api/category/model/dto/request/CreateCategoryRequest.java b/Api/src/main/java/allchive/server/api/category/model/dto/request/CreateCategoryRequest.java deleted file mode 100644 index 75a16ffc..00000000 --- a/Api/src/main/java/allchive/server/api/category/model/dto/request/CreateCategoryRequest.java +++ /dev/null @@ -1,23 +0,0 @@ -package allchive.server.api.category.model.dto.request; - - -import allchive.server.core.annotation.ValidEnum; -import allchive.server.domain.domains.category.domain.enums.Subject; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; - -@Getter -public class CreateCategoryRequest { - @Schema(defaultValue = "카테고리 제목", description = "카테고리 제목") - private String title; - - @Schema(defaultValue = "카테고리 이미지 url", description = "카테고리 이미지 url") - private String imageUrl; - - @Schema(defaultValue = "DESIGN", description = "주제") - @ValidEnum(target = Subject.class) - private Subject subject; - - @Schema(defaultValue = "false", description = "공개 여부") - private boolean publicStatus; -} diff --git a/Api/src/main/java/allchive/server/api/category/model/dto/request/UpdateCategoryRequest.java b/Api/src/main/java/allchive/server/api/category/model/dto/request/UpdateCategoryRequest.java deleted file mode 100644 index 0c86fc2a..00000000 --- a/Api/src/main/java/allchive/server/api/category/model/dto/request/UpdateCategoryRequest.java +++ /dev/null @@ -1,23 +0,0 @@ -package allchive.server.api.category.model.dto.request; - - -import allchive.server.core.annotation.ValidEnum; -import allchive.server.domain.domains.category.domain.enums.Subject; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; - -@Getter -public class UpdateCategoryRequest { - @Schema(defaultValue = "카테고리 제목", description = "카테고리 제목") - private String title; - - @Schema(defaultValue = "카테고리 이미지 url", description = "카테고리 이미지 url") - private String imageUrl; - - @Schema(defaultValue = "DESIGN", description = "주제") - @ValidEnum(target = Subject.class) - private Subject subject; - - @Schema(defaultValue = "false", description = "공개 여부") - private boolean publicStatus; -} diff --git a/Api/src/main/java/allchive/server/api/category/model/dto/response/CategoryContentsResponse.java b/Api/src/main/java/allchive/server/api/category/model/dto/response/CategoryContentsResponse.java deleted file mode 100644 index 925934c0..00000000 --- a/Api/src/main/java/allchive/server/api/category/model/dto/response/CategoryContentsResponse.java +++ /dev/null @@ -1,45 +0,0 @@ -package allchive.server.api.category.model.dto.response; - - -import allchive.server.api.common.slice.SliceResponse; -import allchive.server.api.content.model.dto.response.ContentResponse; -import allchive.server.domain.domains.category.domain.Category; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Builder; -import lombok.Getter; - -@Getter -public class CategoryContentsResponse { - private SliceResponse<ContentResponse> contents; - - @Schema(description = "카테고리 제목") - private String categoryTitle; - - @Schema(description = "카테고리 고유번호") - private Long categoryId; - - @Schema(description = "카테고리의 총 컨텐츠 개수") - private Long totalContentsCount; - - @Builder - private CategoryContentsResponse( - SliceResponse<ContentResponse> contents, - String categoryTitle, - Long categoryId, - Long totalContentsCount) { - this.contents = contents; - this.categoryTitle = categoryTitle; - this.categoryId = categoryId; - this.totalContentsCount = totalContentsCount; - } - - public static CategoryContentsResponse of( - SliceResponse<ContentResponse> contentResponseSlice, Category category) { - return CategoryContentsResponse.builder() - .categoryId(category.getId()) - .categoryTitle(category.getTitle()) - .totalContentsCount(category.getScrapCnt() + category.getImgCnt()) - .contents(contentResponseSlice) - .build(); - } -} diff --git a/Api/src/main/java/allchive/server/api/category/model/dto/response/CategoryResponse.java b/Api/src/main/java/allchive/server/api/category/model/dto/response/CategoryResponse.java deleted file mode 100644 index 10a10549..00000000 --- a/Api/src/main/java/allchive/server/api/category/model/dto/response/CategoryResponse.java +++ /dev/null @@ -1,82 +0,0 @@ -package allchive.server.api.category.model.dto.response; - - -import allchive.server.api.common.util.UrlUtil; -import allchive.server.core.annotation.DateFormat; -import allchive.server.domain.domains.category.domain.Category; -import allchive.server.domain.domains.category.domain.enums.Subject; -import io.swagger.v3.oas.annotations.media.Schema; -import java.time.LocalDateTime; -import lombok.Builder; -import lombok.Getter; - -@Getter -public class CategoryResponse { - @Schema(description = "카테고리 고유 아이디") - private Long categoryId; - - @Schema(description = "카테고리 제목") - private String title; - - @Schema(defaultValue = "카테고리 이미지 url", description = "카테고리 이미지 url") - private String imageUrl; - - @Schema( - type = "string", - pattern = "yyyy.MM.dd", - defaultValue = "2023.07.02", - description = "카테고리 생성일자") - @DateFormat - private LocalDateTime createdAt; - - @Schema(defaultValue = "카테고리 주제", description = "카테고리 주제") - private Subject subject; - - @Schema(description = "카테고리 컨텐츠 중 이미지 수") - private Long imgCnt; - - @Schema(description = "카테고리 컨텐츠 중 링크 수") - private Long linkCnt; - - @Schema(description = "카테고리 스크랩 수") - private Long scrapCnt; - - @Schema(description = "카테고리 스크랩/고정 여부, true == 스크랩/고정됨") - private boolean markStatus; - - @Builder - private CategoryResponse( - Long categoryId, - String title, - String imageUrl, - LocalDateTime createdAt, - Subject subject, - Long imgCnt, - Long linkCnt, - Long scrapCnt, - boolean markStatus) { - this.categoryId = categoryId; - this.title = title; - this.imageUrl = imageUrl; - this.createdAt = createdAt; - this.subject = subject; - this.imgCnt = imgCnt; - this.linkCnt = linkCnt; - this.scrapCnt = scrapCnt; - this.markStatus = markStatus; - } - - public static CategoryResponse of(Category category, boolean markStatus) { - return CategoryResponse.builder() - .categoryId(category.getId()) - .imageUrl(UrlUtil.toAssetUrl(category.getImageUrl())) - .title(category.getTitle()) - .createdAt(category.getCreatedAt()) - .subject(category.getSubject()) - .imgCnt(category.getImgCnt()) - .linkCnt(category.getLinkCnt()) - .scrapCnt(category.getScrapCnt()) - .markStatus(markStatus) - .build(); - } -} diff --git a/Api/src/main/java/allchive/server/api/category/model/mapper/CategoryMapper.java b/Api/src/main/java/allchive/server/api/category/model/mapper/CategoryMapper.java deleted file mode 100644 index a8741533..00000000 --- a/Api/src/main/java/allchive/server/api/category/model/mapper/CategoryMapper.java +++ /dev/null @@ -1,43 +0,0 @@ -package allchive.server.api.category.model.mapper; - - -import allchive.server.api.category.model.dto.request.CreateCategoryRequest; -import allchive.server.api.category.model.dto.response.CategoryTitleResponse; -import allchive.server.api.category.model.vo.TitleContentCntVo; -import allchive.server.api.common.util.UrlUtil; -import allchive.server.core.annotation.Mapper; -import allchive.server.domain.domains.category.domain.Category; -import java.util.List; - -@Mapper -public class CategoryMapper { - public Category toEntity(CreateCategoryRequest request, Long userId) { - return Category.of( - userId, - request.getTitle(), - UrlUtil.convertUrlToKey(request.getImageUrl()), - request.isPublicStatus(), - request.getSubject()); - } - - public CategoryTitleResponse toCategoryTitleResponse(List<Category> categories) { - CategoryTitleResponse response = CategoryTitleResponse.init(); - categories.forEach( - category -> { - switch (category.getSubject()) { - case FOOD -> response.addFood(TitleContentCntVo.from(category)); - case LIFE -> response.addLife(TitleContentCntVo.from(category)); - case HOME_LIVING -> response.addHomeLiving( - TitleContentCntVo.from(category)); - case SHOPPING -> response.addShopping(TitleContentCntVo.from(category)); - case SPORT -> response.addSport(TitleContentCntVo.from(category)); - case SELF_IMPROVEMENT -> response.addSelfImprovement( - TitleContentCntVo.from(category)); - case TECH -> response.addTech(TitleContentCntVo.from(category)); - case DESIGN -> response.addDesign(TitleContentCntVo.from(category)); - case TREND -> response.addTrend(TitleContentCntVo.from(category)); - } - }); - return response; - } -} diff --git a/Api/src/main/java/allchive/server/api/category/service/CreateCategoryUseCase.java b/Api/src/main/java/allchive/server/api/category/service/CreateCategoryUseCase.java deleted file mode 100644 index 76c39c38..00000000 --- a/Api/src/main/java/allchive/server/api/category/service/CreateCategoryUseCase.java +++ /dev/null @@ -1,25 +0,0 @@ -package allchive.server.api.category.service; - - -import allchive.server.api.category.model.dto.request.CreateCategoryRequest; -import allchive.server.api.category.model.mapper.CategoryMapper; -import allchive.server.api.config.security.SecurityUtil; -import allchive.server.core.annotation.UseCase; -import allchive.server.domain.domains.category.domain.Category; -import allchive.server.domain.domains.category.service.CategoryDomainService; -import lombok.RequiredArgsConstructor; -import org.springframework.transaction.annotation.Transactional; - -@UseCase -@RequiredArgsConstructor -public class CreateCategoryUseCase { - private final CategoryMapper categoryMapper; - private final CategoryDomainService categoryDomainService; - - @Transactional - public void execute(CreateCategoryRequest request) { - Long userId = SecurityUtil.getCurrentUserId(); - final Category category = categoryMapper.toEntity(request, userId); - categoryDomainService.createCategory(category); - } -} diff --git a/Api/src/main/java/allchive/server/api/category/service/GetArchivedCategoryUseCase.java b/Api/src/main/java/allchive/server/api/category/service/GetArchivedCategoryUseCase.java deleted file mode 100644 index 1bbce494..00000000 --- a/Api/src/main/java/allchive/server/api/category/service/GetArchivedCategoryUseCase.java +++ /dev/null @@ -1,33 +0,0 @@ -package allchive.server.api.category.service; - - -import allchive.server.api.category.model.dto.response.CategoryResponse; -import allchive.server.api.common.slice.SliceResponse; -import allchive.server.api.config.security.SecurityUtil; -import allchive.server.core.annotation.UseCase; -import allchive.server.domain.domains.category.adaptor.CategoryAdaptor; -import allchive.server.domain.domains.category.domain.enums.Subject; -import lombok.RequiredArgsConstructor; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Slice; -import org.springframework.transaction.annotation.Transactional; - -@UseCase -@RequiredArgsConstructor -public class GetArchivedCategoryUseCase { - private final CategoryAdaptor categoryAdaptor; - - @Transactional(readOnly = true) - public SliceResponse<CategoryResponse> execute(Subject subject, Pageable pageable) { - Long userId = SecurityUtil.getCurrentUserId(); - Slice<CategoryResponse> categorySlices = - categoryAdaptor - .querySliceCategoryByUserId(userId, subject, pageable) - .map( - category -> - CategoryResponse.of( - category, - category.getPinUserId().contains(userId))); - return SliceResponse.of(categorySlices); - } -} diff --git a/Api/src/main/java/allchive/server/api/category/service/GetCategoryTitleUseCase.java b/Api/src/main/java/allchive/server/api/category/service/GetCategoryTitleUseCase.java deleted file mode 100644 index 78ac70d1..00000000 --- a/Api/src/main/java/allchive/server/api/category/service/GetCategoryTitleUseCase.java +++ /dev/null @@ -1,24 +0,0 @@ -package allchive.server.api.category.service; - - -import allchive.server.api.category.model.dto.response.CategoryTitleResponse; -import allchive.server.api.category.model.mapper.CategoryMapper; -import allchive.server.api.config.security.SecurityUtil; -import allchive.server.core.annotation.UseCase; -import allchive.server.domain.domains.category.adaptor.CategoryAdaptor; -import lombok.RequiredArgsConstructor; -import org.springframework.transaction.annotation.Transactional; - -@UseCase -@RequiredArgsConstructor -public class GetCategoryTitleUseCase { - private final CategoryAdaptor categoryAdaptor; - private final CategoryMapper categoryMapper; - - @Transactional(readOnly = true) - public CategoryTitleResponse execute() { - Long userId = SecurityUtil.getCurrentUserId(); - return categoryMapper.toCategoryTitleResponse( - categoryAdaptor.queryCategoryByUserId(userId)); - } -} diff --git a/Api/src/main/java/allchive/server/api/category/service/GetScrapCategoryUseCase.java b/Api/src/main/java/allchive/server/api/category/service/GetScrapCategoryUseCase.java deleted file mode 100644 index a1841ca3..00000000 --- a/Api/src/main/java/allchive/server/api/category/service/GetScrapCategoryUseCase.java +++ /dev/null @@ -1,39 +0,0 @@ -package allchive.server.api.category.service; - - -import allchive.server.api.category.model.dto.response.CategoryResponse; -import allchive.server.api.common.slice.SliceResponse; -import allchive.server.api.config.security.SecurityUtil; -import allchive.server.core.annotation.UseCase; -import allchive.server.domain.domains.category.adaptor.CategoryAdaptor; -import allchive.server.domain.domains.category.domain.enums.Subject; -import allchive.server.domain.domains.user.adaptor.ScrapAdaptor; -import allchive.server.domain.domains.user.domain.Scrap; -import java.util.List; -import lombok.RequiredArgsConstructor; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Slice; -import org.springframework.transaction.annotation.Transactional; - -@UseCase -@RequiredArgsConstructor -public class GetScrapCategoryUseCase { - private final ScrapAdaptor scrapAdaptor; - private final CategoryAdaptor categoryAdaptor; - - @Transactional(readOnly = true) - public SliceResponse<CategoryResponse> execute(Subject subject, Pageable pageable) { - Long userId = SecurityUtil.getCurrentUserId(); - List<Long> categoryIdList = - scrapAdaptor.findAllByUserId(userId).stream().map(Scrap::getCategoryId).toList(); - Slice<CategoryResponse> categorySlices = - categoryAdaptor - .querySliceCategoryIn(categoryIdList, subject, pageable) - .map( - category -> - CategoryResponse.of( - category, - categoryIdList.contains(category.getId()))); - return SliceResponse.of(categorySlices); - } -} diff --git a/Api/src/main/java/allchive/server/api/category/service/UpdateCategoryPinUseCase.java b/Api/src/main/java/allchive/server/api/category/service/UpdateCategoryPinUseCase.java deleted file mode 100644 index cd1e6792..00000000 --- a/Api/src/main/java/allchive/server/api/category/service/UpdateCategoryPinUseCase.java +++ /dev/null @@ -1,30 +0,0 @@ -package allchive.server.api.category.service; - - -import allchive.server.api.config.security.SecurityUtil; -import allchive.server.core.annotation.UseCase; -import allchive.server.domain.domains.category.service.CategoryDomainService; -import allchive.server.domain.domains.category.validator.CategoryValidator; -import lombok.RequiredArgsConstructor; -import org.springframework.transaction.annotation.Transactional; - -@UseCase -@RequiredArgsConstructor -public class UpdateCategoryPinUseCase { - private final CategoryValidator categoryValidator; - private final CategoryDomainService categoryDomainService; - - @Transactional - public void execute(Long categoryId, Boolean cancel) { - Long userId = SecurityUtil.getCurrentUserId(); - categoryValidator.validateExistCategory(categoryId); - categoryValidator.validateDeleteStatus(categoryId, userId); - if (cancel) { - categoryValidator.validateNotPinStatus(categoryId, userId); - categoryDomainService.updatePin(categoryId, userId, false); - } else { - categoryValidator.validateAlreadyPinStatus(categoryId, userId); - categoryDomainService.updatePin(categoryId, userId, true); - } - } -} diff --git a/Api/src/main/java/allchive/server/api/category/service/UpdateCategoryUseCase.java b/Api/src/main/java/allchive/server/api/category/service/UpdateCategoryUseCase.java deleted file mode 100644 index 71069837..00000000 --- a/Api/src/main/java/allchive/server/api/category/service/UpdateCategoryUseCase.java +++ /dev/null @@ -1,34 +0,0 @@ -package allchive.server.api.category.service; - - -import allchive.server.api.category.model.dto.request.UpdateCategoryRequest; -import allchive.server.api.common.util.UrlUtil; -import allchive.server.api.config.security.SecurityUtil; -import allchive.server.core.annotation.UseCase; -import allchive.server.domain.domains.category.adaptor.CategoryAdaptor; -import allchive.server.domain.domains.category.domain.Category; -import allchive.server.domain.domains.category.service.CategoryDomainService; -import allchive.server.domain.domains.category.validator.CategoryValidator; -import lombok.RequiredArgsConstructor; -import org.springframework.transaction.annotation.Transactional; - -@UseCase -@RequiredArgsConstructor -public class UpdateCategoryUseCase { - private final CategoryDomainService categoryDomainService; - private final CategoryAdaptor categoryAdaptor; - private final CategoryValidator categoryValidator; - - @Transactional - public void execute(Long categoryId, UpdateCategoryRequest request) { - Long userId = SecurityUtil.getCurrentUserId(); - categoryValidator.verifyUser(userId, categoryId); - Category category = categoryAdaptor.findById(categoryId); - categoryDomainService.updateCategory( - category, - request.getTitle(), - UrlUtil.convertUrlToKey(request.getImageUrl()), - request.isPublicStatus(), - request.getSubject()); - } -} diff --git a/Api/src/main/java/allchive/server/api/content/model/dto/request/CreateContentRequest.java b/Api/src/main/java/allchive/server/api/content/model/dto/request/CreateContentRequest.java index 0dc82df5..ead4b71f 100644 --- a/Api/src/main/java/allchive/server/api/content/model/dto/request/CreateContentRequest.java +++ b/Api/src/main/java/allchive/server/api/content/model/dto/request/CreateContentRequest.java @@ -13,8 +13,8 @@ public class CreateContentRequest { @ValidEnum(target = ContentType.class) private ContentType contentType; - @Schema(defaultValue = "0", description = "카테고리 고유번호") - private Long categoryId; + @Schema(defaultValue = "0", description = "아카이빙 고유번호") + private Long archivingId; @Schema(defaultValue = "제목", description = "제목") private String title; diff --git a/Api/src/main/java/allchive/server/api/content/model/mapper/ContentMapper.java b/Api/src/main/java/allchive/server/api/content/model/mapper/ContentMapper.java index ad80bcdd..380a2c85 100644 --- a/Api/src/main/java/allchive/server/api/content/model/mapper/ContentMapper.java +++ b/Api/src/main/java/allchive/server/api/content/model/mapper/ContentMapper.java @@ -26,7 +26,7 @@ public ContentResponse toContentResponse( public Content toEntity(CreateContentRequest request) { return Content.of( - request.getCategoryId(), + request.getArchivingId(), request.getContentType(), UrlUtil.convertUrlToKey(request.getImgUrl()), request.getLink(), diff --git a/Api/src/main/java/allchive/server/api/content/service/CreateContentUseCase.java b/Api/src/main/java/allchive/server/api/content/service/CreateContentUseCase.java index 13adcfcb..3cb6833e 100644 --- a/Api/src/main/java/allchive/server/api/content/service/CreateContentUseCase.java +++ b/Api/src/main/java/allchive/server/api/content/service/CreateContentUseCase.java @@ -5,7 +5,7 @@ import allchive.server.api.content.model.dto.request.CreateContentRequest; import allchive.server.api.content.model.mapper.ContentMapper; import allchive.server.core.annotation.UseCase; -import allchive.server.domain.domains.category.validator.CategoryValidator; +import allchive.server.domain.domains.archiving.validator.ArchivingValidator; import allchive.server.domain.domains.content.domain.Content; import allchive.server.domain.domains.content.service.ContentDomainService; import lombok.RequiredArgsConstructor; @@ -14,16 +14,16 @@ @UseCase @RequiredArgsConstructor public class CreateContentUseCase { - private final CategoryValidator categoryValidator; + private final ArchivingValidator archivingValidator; private final ContentMapper contentMapper; private final ContentDomainService contentDomainService; // TODO : tag 만들면 연결 @Transactional public void execute(CreateContentRequest request) { - categoryValidator.validateExistCategory(request.getCategoryId()); + archivingValidator.validateExistArchiving(request.getArchivingId()); Long userId = SecurityUtil.getCurrentUserId(); - categoryValidator.validateCategoryUser(request.getCategoryId(), userId); + archivingValidator.validateArchivingUser(request.getArchivingId(), userId); Content content = contentMapper.toEntity(request); contentDomainService.save(content); } diff --git a/Api/src/main/java/allchive/server/api/content/service/DeleteContentUseCase.java b/Api/src/main/java/allchive/server/api/content/service/DeleteContentUseCase.java index 32ab39fe..56cb974e 100644 --- a/Api/src/main/java/allchive/server/api/content/service/DeleteContentUseCase.java +++ b/Api/src/main/java/allchive/server/api/content/service/DeleteContentUseCase.java @@ -4,7 +4,7 @@ import allchive.server.api.config.security.SecurityUtil; import allchive.server.api.recycle.model.mapper.RecycleMapper; import allchive.server.core.annotation.UseCase; -import allchive.server.domain.domains.category.validator.CategoryValidator; +import allchive.server.domain.domains.archiving.validator.ArchivingValidator; import allchive.server.domain.domains.content.adaptor.ContentAdaptor; import allchive.server.domain.domains.content.domain.Content; import allchive.server.domain.domains.content.service.ContentDomainService; @@ -18,7 +18,7 @@ @RequiredArgsConstructor public class DeleteContentUseCase { private final ContentAdaptor contentAdaptor; - private final CategoryValidator categoryValidator; + private final ArchivingValidator archivingValidator; private final ContentDomainService contentDomainService; private final RecycleMapper recycleMapper; private final RecycleDomainService recycleDomainService; @@ -27,7 +27,7 @@ public class DeleteContentUseCase { public void execute(Long contentId) { Long userId = SecurityUtil.getCurrentUserId(); Content content = contentAdaptor.findById(contentId); - categoryValidator.validateCategoryUser(content.getCategoryId(), userId); + archivingValidator.validateArchivingUser(content.getArchivingId(), userId); contentDomainService.deleteById(contentId); Recycle recycle = recycleMapper.toContentRecycleEntity(userId, contentId, RecycleType.CONTENT); diff --git a/Api/src/main/java/allchive/server/api/content/service/GetContentUseCase.java b/Api/src/main/java/allchive/server/api/content/service/GetContentUseCase.java index c812e344..59115dff 100644 --- a/Api/src/main/java/allchive/server/api/content/service/GetContentUseCase.java +++ b/Api/src/main/java/allchive/server/api/content/service/GetContentUseCase.java @@ -5,7 +5,7 @@ import allchive.server.api.content.model.dto.response.ContentTagResponse; import allchive.server.api.content.model.mapper.ContentMapper; import allchive.server.core.annotation.UseCase; -import allchive.server.domain.domains.category.validator.CategoryValidator; +import allchive.server.domain.domains.archiving.validator.ArchivingValidator; import allchive.server.domain.domains.content.adaptor.ContentAdaptor; import allchive.server.domain.domains.content.adaptor.ContentTagGroupAdaptor; import allchive.server.domain.domains.content.domain.Content; @@ -17,7 +17,7 @@ @UseCase @RequiredArgsConstructor public class GetContentUseCase { - private final CategoryValidator categoryValidator; + private final ArchivingValidator archivingValidator; private final ContentAdaptor contentAdaptor; private final ContentTagGroupAdaptor contentTagGroupAdaptor; private final ContentMapper contentMapper; @@ -26,7 +26,7 @@ public class GetContentUseCase { public ContentTagResponse execute(Long contentId) { Content content = contentAdaptor.findById(contentId); Long userId = SecurityUtil.getCurrentUserId(); - categoryValidator.validatePublicStatus(content.getCategoryId(), userId); + archivingValidator.validatePublicStatus(content.getArchivingId(), userId); List<ContentTagGroup> contentTagGroupList = contentTagGroupAdaptor.findAllByContent(content); return contentMapper.toContentTagResponse(content, contentTagGroupList); diff --git a/Api/src/main/java/allchive/server/api/image/controller/ImageController.java b/Api/src/main/java/allchive/server/api/image/controller/ImageController.java index 32fa6d7b..02a82108 100644 --- a/Api/src/main/java/allchive/server/api/image/controller/ImageController.java +++ b/Api/src/main/java/allchive/server/api/image/controller/ImageController.java @@ -20,11 +20,11 @@ public class ImageController { private final S3PresignedUrlService s3PresignedUrlService; @Operation(summary = "카테고리 관련 이미지 업로드 url 요청할수 있는 api 입니다.") - @GetMapping(value = "/categories/image") - public ImageUrlResponse getCategoryPresignedUrl() { + @GetMapping(value = "/archivings/image") + public ImageUrlResponse getArchivingPresignedUrl() { Long userId = SecurityUtil.getCurrentUserId(); return ImageUrlResponse.from( - s3PresignedUrlService.getPreSignedUrl(userId, PresignedType.CATEGORY)); + s3PresignedUrlService.getPreSignedUrl(userId, PresignedType.ARCHIVING)); } @Operation(summary = "컨텐츠 관련 이미지 업로드 url 요청할수 있는 api 입니다.") diff --git a/Api/src/main/java/allchive/server/api/recycle/model/mapper/RecycleMapper.java b/Api/src/main/java/allchive/server/api/recycle/model/mapper/RecycleMapper.java index 2963b92e..c35534d6 100644 --- a/Api/src/main/java/allchive/server/api/recycle/model/mapper/RecycleMapper.java +++ b/Api/src/main/java/allchive/server/api/recycle/model/mapper/RecycleMapper.java @@ -11,7 +11,7 @@ public Recycle toContentRecycleEntity(Long userId, Long contentId, RecycleType t return Recycle.of(type, contentId, null, userId); } - public Recycle toCategoryRecycleEntity(Long userId, Long categoryId, RecycleType type) { - return Recycle.of(type, null, categoryId, userId); + public Recycle toArchivingRecycleEntity(Long userId, Long archivingId, RecycleType type) { + return Recycle.of(type, null, archivingId, userId); } } diff --git a/Api/src/main/java/allchive/server/api/user/model/dto/response/GetUserProfileResponse.java b/Api/src/main/java/allchive/server/api/user/model/dto/response/GetUserProfileResponse.java index 1bf62ba0..45e64bbb 100644 --- a/Api/src/main/java/allchive/server/api/user/model/dto/response/GetUserProfileResponse.java +++ b/Api/src/main/java/allchive/server/api/user/model/dto/response/GetUserProfileResponse.java @@ -22,26 +22,26 @@ public class GetUserProfileResponse { private int imgCount; @Schema(defaultValue = "0", description = "공개 아카이브 개수") - private int publicCategoryCount; + private int publicArchivingCount; @Builder private GetUserProfileResponse( - String nickname, String imgUrl, int linkCount, int imgCount, int publicCategoryCount) { + String nickname, String imgUrl, int linkCount, int imgCount, int publicArchivingCount) { this.nickname = nickname; this.imgUrl = imgUrl; this.linkCount = linkCount; this.imgCount = imgCount; - this.publicCategoryCount = publicCategoryCount; + this.publicArchivingCount = publicArchivingCount; } public static GetUserProfileResponse of( - User user, int linkCount, int imgCount, int publicCategoryCount) { + User user, int linkCount, int imgCount, int publicArchivingCount) { return GetUserProfileResponse.builder() .nickname(user.getNickname()) .imgUrl(UrlUtil.toAssetUrl(user.getProfileImgUrl())) .linkCount(linkCount) .imgCount(imgCount) - .publicCategoryCount(publicCategoryCount) + .publicArchivingCount(publicArchivingCount) .build(); } } diff --git a/Api/src/main/java/allchive/server/api/user/model/mapper/UserMapper.java b/Api/src/main/java/allchive/server/api/user/model/mapper/UserMapper.java index d403c4a5..ad2adcc5 100644 --- a/Api/src/main/java/allchive/server/api/user/model/mapper/UserMapper.java +++ b/Api/src/main/java/allchive/server/api/user/model/mapper/UserMapper.java @@ -3,19 +3,19 @@ import allchive.server.api.user.model.dto.response.GetUserProfileResponse; import allchive.server.core.annotation.Mapper; -import allchive.server.domain.domains.category.domain.Category; +import allchive.server.domain.domains.archiving.domain.Archiving; import allchive.server.domain.domains.user.domain.User; import java.util.List; @Mapper public class UserMapper { - public GetUserProfileResponse toGetUserProfileResponse(List<Category> categoryList, User user) { - int linkCount = 0, imgCount = 0, publicCategoryCount = 0; - for (Category category : categoryList) { - linkCount += category.getLinkCnt(); - imgCount += category.getImgCnt(); - publicCategoryCount += category.getPublicStatus() ? 1 : 0; + public GetUserProfileResponse toGetUserProfileResponse(List<Archiving> archivingList, User user) { + int linkCount = 0, imgCount = 0, publicArchivingCount = 0; + for (Archiving archiving : archivingList) { + linkCount += archiving.getLinkCnt(); + imgCount += archiving.getImgCnt(); + publicArchivingCount += archiving.getPublicStatus() ? 1 : 0; } - return GetUserProfileResponse.of(user, linkCount, imgCount, publicCategoryCount); + return GetUserProfileResponse.of(user, linkCount, imgCount, publicArchivingCount); } } diff --git a/Api/src/main/java/allchive/server/api/user/service/GetUserProfileUseCase.java b/Api/src/main/java/allchive/server/api/user/service/GetUserProfileUseCase.java index 23c4fb40..95afe46d 100644 --- a/Api/src/main/java/allchive/server/api/user/service/GetUserProfileUseCase.java +++ b/Api/src/main/java/allchive/server/api/user/service/GetUserProfileUseCase.java @@ -5,8 +5,8 @@ import allchive.server.api.user.model.dto.response.GetUserProfileResponse; import allchive.server.api.user.model.mapper.UserMapper; import allchive.server.core.annotation.UseCase; -import allchive.server.domain.domains.category.adaptor.CategoryAdaptor; -import allchive.server.domain.domains.category.domain.Category; +import allchive.server.domain.domains.archiving.adaptor.ArchivingAdaptor; +import allchive.server.domain.domains.archiving.domain.Archiving; import allchive.server.domain.domains.user.adaptor.UserAdaptor; import allchive.server.domain.domains.user.domain.User; import allchive.server.domain.domains.user.validator.UserValidator; @@ -17,7 +17,7 @@ @UseCase @RequiredArgsConstructor public class GetUserProfileUseCase { - private final CategoryAdaptor categoryAdaptor; + private final ArchivingAdaptor archivingAdaptor; private final UserAdaptor userAdaptor; private final UserMapper userMapper; private final UserValidator userValidator; @@ -27,7 +27,7 @@ public GetUserProfileResponse execute() { Long userId = SecurityUtil.getCurrentUserId(); userValidator.validateUserStatusNormal(userId); User user = userAdaptor.queryUserById(userId); - List<Category> categoryList = categoryAdaptor.findAllByUserId(userId); - return userMapper.toGetUserProfileResponse(categoryList, user); + List<Archiving> archivingList = archivingAdaptor.findAllByUserId(userId); + return userMapper.toGetUserProfileResponse(archivingList, user); } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/archiving/adaptor/ArchivingAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/archiving/adaptor/ArchivingAdaptor.java new file mode 100644 index 00000000..79bbb991 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/archiving/adaptor/ArchivingAdaptor.java @@ -0,0 +1,67 @@ +package allchive.server.domain.domains.archiving.adaptor; + + +import allchive.server.core.annotation.Adaptor; +import allchive.server.core.error.exception.InternalServerError; +import allchive.server.domain.common.util.SliceUtil; +import allchive.server.domain.domains.archiving.domain.Archiving; +import allchive.server.domain.domains.archiving.domain.enums.Category; +import allchive.server.domain.domains.archiving.exception.exceptions.ArchivingNotFoundException; +import allchive.server.domain.domains.archiving.repository.ArchivingRepository; + +import java.util.ArrayList; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; + +@Adaptor +@RequiredArgsConstructor +public class ArchivingAdaptor { + private final ArchivingRepository archivingRepository; + + public void save(Archiving archiving) { + if (archiving.getCategory().equals(Category.ALL)) { + throw InternalServerError.EXCEPTION; + } + archivingRepository.save(archiving); + } + + public Archiving findById(Long archivingId) { + return archivingRepository + .findById(archivingId) + .orElseThrow(() -> ArchivingNotFoundException.EXCEPTION); + } + + public void deleteById(Long archivingId) { + archivingRepository.deleteById(archivingId); + } + + public Slice<Archiving> querySliceArchivingExceptBlock( + List<Long> archivingIdList, List<Long> blockList, Category category, Pageable pageable) { + return archivingRepository.querySliceArchivingExceptBlock( + archivingIdList, blockList, category, pageable); + } + + public Slice<Archiving> querySliceArchivingByUserId( + Long userId, Category category, Pageable pageable) { + return archivingRepository.querySliceArchivingByUserId(userId, category, pageable); + } + + public Slice<Archiving> querySliceArchivingIn( + List<Long> archivingIdList, Category category, Pageable pageable) { + return archivingRepository.querySliceArchivingIn(archivingIdList, category, pageable); + } + + public List<Archiving> queryArchivingByUserId(Long userId) { + return archivingRepository.queryArchivingByUserId(userId); + } + + public boolean queryArchivingExist(Long archivingId) { + return archivingRepository.queryArchivingExist(archivingId); + } + + public List<Archiving> findAllByUserId(Long userId) { + return archivingRepository.findAllByUserId(userId); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/domain/Category.java b/Domain/src/main/java/allchive/server/domain/domains/archiving/domain/Archiving.java similarity index 67% rename from Domain/src/main/java/allchive/server/domain/domains/category/domain/Category.java rename to Domain/src/main/java/allchive/server/domain/domains/archiving/domain/Archiving.java index 285e9c3c..4ce2e25b 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/category/domain/Category.java +++ b/Domain/src/main/java/allchive/server/domain/domains/archiving/domain/Archiving.java @@ -1,11 +1,11 @@ -package allchive.server.domain.domains.category.domain; +package allchive.server.domain.domains.archiving.domain; import allchive.server.domain.common.model.BaseTimeEntity; -import allchive.server.domain.domains.category.domain.enums.Subject; -import allchive.server.domain.domains.category.exception.exceptions.DeletedCategoryException; -import allchive.server.domain.domains.category.exception.exceptions.NoAuthurityUpdateCategoryException; -import allchive.server.domain.domains.category.exception.exceptions.NotPublicCategoryException; +import allchive.server.domain.domains.archiving.domain.enums.Category; +import allchive.server.domain.domains.archiving.exception.exceptions.DeletedArchivingException; +import allchive.server.domain.domains.archiving.exception.exceptions.NoAuthurityUpdateArchivingException; +import allchive.server.domain.domains.archiving.exception.exceptions.NotPublicArchivingException; import java.util.ArrayList; import java.util.List; import javax.persistence.*; @@ -15,12 +15,12 @@ import lombok.NoArgsConstructor; @Getter -@Table(name = "tbl_category") +@Table(name = "tbl_archiving") @Entity @NoArgsConstructor(access = AccessLevel.PROTECTED) -public class Category extends BaseTimeEntity { +public class Archiving extends BaseTimeEntity { @Id - @Column(name = "category_id", nullable = false, updatable = false) + @Column(name = "archiving_id", nullable = false, updatable = false) @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @@ -35,65 +35,65 @@ public class Category extends BaseTimeEntity { private Boolean deleteStatus = Boolean.FALSE; @ElementCollection - @CollectionTable(name = "tbl_category_pin", joinColumns = @JoinColumn(name = "category_id")) + @CollectionTable(name = "tbl_archiving_pin", joinColumns = @JoinColumn(name = "archiving_id")) private List<Long> pinUserId = new ArrayList<>(); @Enumerated(EnumType.STRING) - private Subject subject; + private Category category; @Builder - private Category( + private Archiving( Long userId, String imageUrl, String title, boolean publicStatus, boolean deleteStatus, - Subject subject) { + Category category) { this.userId = userId; this.imageUrl = imageUrl; this.title = title; this.publicStatus = publicStatus; this.deleteStatus = deleteStatus; - this.subject = subject; + this.category = category; this.scrapCnt = 0L; this.linkCnt = 0L; this.imgCnt = 0L; } - public static Category of( - Long userId, String title, String imageUrl, boolean publicStatus, Subject subject) { - return Category.builder() + public static Archiving of( + Long userId, String title, String imageUrl, boolean publicStatus, Category category) { + return Archiving.builder() .userId(userId) .imageUrl(imageUrl) .title(title) .publicStatus(publicStatus) .deleteStatus(Boolean.FALSE) - .subject(subject) + .category(category) .build(); } - public void update(String title, String imageUrl, boolean publicStatus, Subject subject) { + public void update(String title, String imageUrl, boolean publicStatus, Category category) { this.title = title; this.imageUrl = imageUrl; this.publicStatus = publicStatus; - this.subject = subject; + this.category = category; } public void validateUser(Long userId) { if (!this.userId.equals(userId)) { - throw NoAuthurityUpdateCategoryException.EXCEPTION; + throw NoAuthurityUpdateArchivingException.EXCEPTION; } } public void validatePublicStatus(Long userId) { if (!this.publicStatus && !this.userId.equals(userId)) { - throw NotPublicCategoryException.EXCEPTION; + throw NotPublicArchivingException.EXCEPTION; } } public void validateDeleteStatus(Long userId) { if (this.deleteStatus && !this.userId.equals(userId)) { - throw DeletedCategoryException.EXCEPTION; + throw DeletedArchivingException.EXCEPTION; } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/domain/enums/Subject.java b/Domain/src/main/java/allchive/server/domain/domains/archiving/domain/enums/Category.java similarity index 90% rename from Domain/src/main/java/allchive/server/domain/domains/category/domain/enums/Subject.java rename to Domain/src/main/java/allchive/server/domain/domains/archiving/domain/enums/Category.java index 60011fe3..00afb9b4 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/category/domain/enums/Subject.java +++ b/Domain/src/main/java/allchive/server/domain/domains/archiving/domain/enums/Category.java @@ -1,4 +1,4 @@ -package allchive.server.domain.domains.category.domain.enums; +package allchive.server.domain.domains.archiving.domain.enums; import allchive.server.domain.domains.user.domain.enums.OauthProvider; @@ -10,7 +10,7 @@ @Getter @AllArgsConstructor -public enum Subject { +public enum Category { ALL("ALL"), FOOD("FOOD"), LIFE("LIFE"), diff --git a/Domain/src/main/java/allchive/server/domain/domains/archiving/exception/ArchivingErrorCode.java b/Domain/src/main/java/allchive/server/domain/domains/archiving/exception/ArchivingErrorCode.java new file mode 100644 index 00000000..3ba9d501 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/archiving/exception/ArchivingErrorCode.java @@ -0,0 +1,29 @@ +package allchive.server.domain.domains.archiving.exception; + +import static allchive.server.core.consts.AllchiveConst.*; + +import allchive.server.core.dto.ErrorReason; +import allchive.server.core.error.BaseErrorCode; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum ArchivingErrorCode implements BaseErrorCode { + NOT_PUBLIC_ARCHIVING(BAD_REQUEST, "ARCHIVING_400_1", "공개된 아카이빙이 아닙니다."), + DELETED_ARCHIVING(BAD_REQUEST, "ARCHIVING_400_2", "삭제된 아카이빙입니다."), + ALREADY_PINNED_ARCHIVING(BAD_REQUEST, "ARCHIVING_400_3", "이미 고정 아카이빙입니다."), + NOT_PINNED_ARCHIVING(BAD_REQUEST, "ARCHIVING_400_4", "고정되지않은 아카이빙입니다."), + + NO_AUTHORITY_UPDATE_ARCHIVING(FORBIDDEN, "ARCHIVING_403_1", "아카이빙 수정 권한이 없습니다."), + + ARCHIVING_NOT_FOUND(NOT_FOUND, "ARCHIVING_404_1", "아카이빙을 찾을 수 없습니다."); + private int status; + private String code; + private String reason; + + @Override + public ErrorReason getErrorReason() { + return ErrorReason.of(status, code, reason); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/archiving/exception/exceptions/AlreadyPinnedArchivingException.java b/Domain/src/main/java/allchive/server/domain/domains/archiving/exception/exceptions/AlreadyPinnedArchivingException.java new file mode 100644 index 00000000..609331d1 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/archiving/exception/exceptions/AlreadyPinnedArchivingException.java @@ -0,0 +1,14 @@ +package allchive.server.domain.domains.archiving.exception.exceptions; + + +import allchive.server.core.error.BaseErrorException; +import allchive.server.domain.domains.archiving.exception.ArchivingErrorCode; + +public class AlreadyPinnedArchivingException extends BaseErrorException { + + public static final BaseErrorException EXCEPTION = new AlreadyPinnedArchivingException(); + + private AlreadyPinnedArchivingException() { + super(ArchivingErrorCode.ALREADY_PINNED_ARCHIVING); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/archiving/exception/exceptions/ArchivingNotFoundException.java b/Domain/src/main/java/allchive/server/domain/domains/archiving/exception/exceptions/ArchivingNotFoundException.java new file mode 100644 index 00000000..4b7e820b --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/archiving/exception/exceptions/ArchivingNotFoundException.java @@ -0,0 +1,14 @@ +package allchive.server.domain.domains.archiving.exception.exceptions; + + +import allchive.server.core.error.BaseErrorException; +import allchive.server.domain.domains.archiving.exception.ArchivingErrorCode; + +public class ArchivingNotFoundException extends BaseErrorException { + + public static final BaseErrorException EXCEPTION = new ArchivingNotFoundException(); + + private ArchivingNotFoundException() { + super(ArchivingErrorCode.ARCHIVING_NOT_FOUND); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/archiving/exception/exceptions/DeletedArchivingException.java b/Domain/src/main/java/allchive/server/domain/domains/archiving/exception/exceptions/DeletedArchivingException.java new file mode 100644 index 00000000..decfbf2a --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/archiving/exception/exceptions/DeletedArchivingException.java @@ -0,0 +1,14 @@ +package allchive.server.domain.domains.archiving.exception.exceptions; + + +import allchive.server.core.error.BaseErrorException; +import allchive.server.domain.domains.archiving.exception.ArchivingErrorCode; + +public class DeletedArchivingException extends BaseErrorException { + + public static final BaseErrorException EXCEPTION = new DeletedArchivingException(); + + private DeletedArchivingException() { + super(ArchivingErrorCode.DELETED_ARCHIVING); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/archiving/exception/exceptions/NoAuthurityUpdateArchivingException.java b/Domain/src/main/java/allchive/server/domain/domains/archiving/exception/exceptions/NoAuthurityUpdateArchivingException.java new file mode 100644 index 00000000..28f17267 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/archiving/exception/exceptions/NoAuthurityUpdateArchivingException.java @@ -0,0 +1,14 @@ +package allchive.server.domain.domains.archiving.exception.exceptions; + + +import allchive.server.core.error.BaseErrorException; +import allchive.server.domain.domains.archiving.exception.ArchivingErrorCode; + +public class NoAuthurityUpdateArchivingException extends BaseErrorException { + + public static final BaseErrorException EXCEPTION = new NoAuthurityUpdateArchivingException(); + + private NoAuthurityUpdateArchivingException() { + super(ArchivingErrorCode.NO_AUTHORITY_UPDATE_ARCHIVING); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/archiving/exception/exceptions/NotPinnedArchivingException.java b/Domain/src/main/java/allchive/server/domain/domains/archiving/exception/exceptions/NotPinnedArchivingException.java new file mode 100644 index 00000000..9addcb92 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/archiving/exception/exceptions/NotPinnedArchivingException.java @@ -0,0 +1,14 @@ +package allchive.server.domain.domains.archiving.exception.exceptions; + + +import allchive.server.core.error.BaseErrorException; +import allchive.server.domain.domains.archiving.exception.ArchivingErrorCode; + +public class NotPinnedArchivingException extends BaseErrorException { + + public static final BaseErrorException EXCEPTION = new NotPinnedArchivingException(); + + private NotPinnedArchivingException() { + super(ArchivingErrorCode.NOT_PINNED_ARCHIVING); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/archiving/exception/exceptions/NotPublicArchivingException.java b/Domain/src/main/java/allchive/server/domain/domains/archiving/exception/exceptions/NotPublicArchivingException.java new file mode 100644 index 00000000..29240e3f --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/archiving/exception/exceptions/NotPublicArchivingException.java @@ -0,0 +1,14 @@ +package allchive.server.domain.domains.archiving.exception.exceptions; + + +import allchive.server.core.error.BaseErrorException; +import allchive.server.domain.domains.archiving.exception.ArchivingErrorCode; + +public class NotPublicArchivingException extends BaseErrorException { + + public static final BaseErrorException EXCEPTION = new NotPublicArchivingException(); + + private NotPublicArchivingException() { + super(ArchivingErrorCode.NOT_PUBLIC_ARCHIVING); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingCustomRepository.java b/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingCustomRepository.java new file mode 100644 index 00000000..c0f61b24 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingCustomRepository.java @@ -0,0 +1,22 @@ +package allchive.server.domain.domains.archiving.repository; + + +import allchive.server.domain.domains.archiving.domain.Archiving; +import allchive.server.domain.domains.archiving.domain.enums.Category; +import java.util.List; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; + +public interface ArchivingCustomRepository { + Slice<Archiving> querySliceArchivingExceptBlock( + List<Long> archivingIdList, List<Long> blockList, Category category, Pageable pageable); + + Slice<Archiving> querySliceArchivingByUserId(Long userId, Category category, Pageable pageable); + + Slice<Archiving> querySliceArchivingIn( + List<Long> archivingIdList, Category category, Pageable pageable); + + List<Archiving> queryArchivingByUserId(Long userId); + + boolean queryArchivingExist(Long archivingId); +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingCustomRepositoryImpl.java b/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingCustomRepositoryImpl.java new file mode 100644 index 00000000..06c0658a --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingCustomRepositoryImpl.java @@ -0,0 +1,146 @@ +package allchive.server.domain.domains.archiving.repository; + +import static allchive.server.domain.domains.archiving.domain.QArchiving.archiving; + +import allchive.server.domain.common.util.SliceUtil; +import allchive.server.domain.domains.archiving.domain.Archiving; +import allchive.server.domain.domains.archiving.domain.enums.Category; +import com.querydsl.core.types.OrderSpecifier; +import com.querydsl.core.types.dsl.BooleanExpression; +import com.querydsl.core.types.dsl.CaseBuilder; +import com.querydsl.core.types.dsl.NumberExpression; +import com.querydsl.jpa.impl.JPAQueryFactory; +import java.time.LocalDateTime; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; + +@RequiredArgsConstructor +public class ArchivingCustomRepositoryImpl implements ArchivingCustomRepository { + private final JPAQueryFactory queryFactory; + + @Override + public Slice<Archiving> querySliceArchivingExceptBlock( + List<Long> archivingIdList, List<Long> blockList, Category category, Pageable pageable) { + List<Archiving> archivings = + queryFactory + .select(archiving) + .from(archiving) + .where( + userIdNotIn(blockList), + publicStatusTrue(), + categoryEq(category), + deleteStatusFalse()) + .orderBy(scrabListDesc(archivingIdList), scrapCntDesc(), createdAtDesc()) + .offset(pageable.getOffset()) + .limit(pageable.getPageSize() + 1) + .fetch(); + return SliceUtil.toSlice(archivings, pageable); + } + + @Override + public Slice<Archiving> querySliceArchivingByUserId( + Long userId, Category category, Pageable pageable) { + List<Archiving> archivings = + queryFactory + .select(archiving) + .from(archiving) + .where(userIdEq(userId), categoryEq(category), deleteStatusFalse()) + .orderBy(pinDesc(userId), scrapCntDesc(), createdAtDesc()) + .offset(pageable.getOffset()) + .limit(pageable.getPageSize() + 1) + .fetch(); + return SliceUtil.toSlice(archivings, pageable); + } + + @Override + public Slice<Archiving> querySliceArchivingIn( + List<Long> archivingIdList, Category category, Pageable pageable) { + List<Archiving> archivings = + queryFactory + .select(archiving) + .from(archiving) + .where( + archivingIdListIn(archivingIdList), + publicStatusTrue(), + categoryEq(category), + deleteStatusFalse()) + .orderBy(scrapCntDesc(), createdAtDesc()) + .offset(pageable.getOffset()) + .limit(pageable.getPageSize() + 1) + .fetch(); + return SliceUtil.toSlice(archivings, pageable); + } + + @Override + public List<Archiving> queryArchivingByUserId(Long userId) { + return queryFactory + .selectFrom(archiving) + .where(userIdEq(userId), deleteStatusFalse()) + .orderBy(categoryDesc(), createdAtDesc()) + .fetch(); + } + + @Override + public boolean queryArchivingExist(Long archivingId) { + Archiving fetchOne = + queryFactory.selectFrom(archiving).where(archivingIdEq(archivingId)).fetchFirst(); + return fetchOne != null; + } + + private BooleanExpression userIdNotIn(List<Long> blockList) { + return archiving.userId.notIn(blockList); + } + + private BooleanExpression publicStatusTrue() { + return archiving.publicStatus.eq(Boolean.TRUE); + } + + private BooleanExpression categoryEq(Category category) { + if (category.equals(Category.ALL)) { + return null; + } + return archiving.category.eq(category); + } + + private BooleanExpression deleteStatusFalse() { + return archiving.deleteStatus.eq(Boolean.FALSE); + } + + private BooleanExpression userIdEq(Long userId) { + return archiving.userId.eq(userId); + } + + private BooleanExpression archivingIdListIn(List<Long> archivingIdList) { + return archiving.id.in(archivingIdList); + } + + private BooleanExpression archivingIdEq(Long archivingId) { + return archiving.id.eq(archivingId); + } + + private OrderSpecifier<Long> scrabListDesc(List<Long> archivingIdList) { + NumberExpression<Long> pinStatus = + new CaseBuilder().when(archiving.id.in(archivingIdList)).then(1L).otherwise(0L); + return pinStatus.desc(); + } + + private OrderSpecifier<Long> pinDesc(Long userId) { + NumberExpression<Long> pinStatus = + new CaseBuilder().when(archiving.pinUserId.contains(userId)).then(1L).otherwise(0L); + return pinStatus.desc(); + } + + private OrderSpecifier<Long> scrapCntDesc() { + return archiving.scrapCnt.desc(); + } + + private OrderSpecifier<LocalDateTime> createdAtDesc() { + return archiving.createdAt.desc(); + } + + private OrderSpecifier<Category> categoryDesc() { + return archiving.category.desc(); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingRepository.java b/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingRepository.java new file mode 100644 index 00000000..e69f1bb8 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingRepository.java @@ -0,0 +1,11 @@ +package allchive.server.domain.domains.archiving.repository; + + +import allchive.server.domain.domains.archiving.domain.Archiving; +import java.util.List; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface ArchivingRepository + extends JpaRepository<Archiving, Long>, ArchivingCustomRepository { + List<Archiving> findAllByUserId(Long userId); +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/archiving/service/ArchivingDomainService.java b/Domain/src/main/java/allchive/server/domain/domains/archiving/service/ArchivingDomainService.java new file mode 100644 index 00000000..85ddf1c6 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/archiving/service/ArchivingDomainService.java @@ -0,0 +1,50 @@ +package allchive.server.domain.domains.archiving.service; + + +import allchive.server.core.annotation.DomainService; +import allchive.server.domain.domains.archiving.adaptor.ArchivingAdaptor; +import allchive.server.domain.domains.archiving.domain.Archiving; +import allchive.server.domain.domains.archiving.domain.enums.Category; +import lombok.RequiredArgsConstructor; + +@DomainService +@RequiredArgsConstructor +public class ArchivingDomainService { + private final ArchivingAdaptor archivingAdaptor; + + public void createArchiving(Archiving archiving) { + archivingAdaptor.save(archiving); + } + + public void updateArchiving( + Archiving archiving, + String title, + String imageUrl, + boolean publicStatus, + Category category) { + archiving.update(title, imageUrl, publicStatus, category); + archivingAdaptor.save(archiving); + } + + public void updateScrapCount(Long archivingId, int i) { + Archiving archiving = archivingAdaptor.findById(archivingId); + archiving.updateScrapCnt(i); + archivingAdaptor.save(archiving); + } + + public void updatePin(Long archivingId, Long userId, boolean pin) { + Archiving archiving = archivingAdaptor.findById(archivingId); + if (pin) { + archiving.addPinUserId(userId); + } else { + archiving.deletePinUserId(userId); + } + archiving.updateScrapCnt(pin ? 1 : -1); + } + + public void deleteById(Long archivingId) { + Archiving archiving = archivingAdaptor.findById(archivingId); + archiving.delete(); + archivingAdaptor.save(archiving); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/archiving/validator/ArchivingValidator.java b/Domain/src/main/java/allchive/server/domain/domains/archiving/validator/ArchivingValidator.java new file mode 100644 index 00000000..136cdc5b --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/archiving/validator/ArchivingValidator.java @@ -0,0 +1,49 @@ +package allchive.server.domain.domains.archiving.validator; + + +import allchive.server.core.annotation.Validator; +import allchive.server.domain.domains.archiving.adaptor.ArchivingAdaptor; +import allchive.server.domain.domains.archiving.exception.exceptions.AlreadyPinnedArchivingException; +import allchive.server.domain.domains.archiving.exception.exceptions.ArchivingNotFoundException; +import allchive.server.domain.domains.archiving.exception.exceptions.NotPinnedArchivingException; +import lombok.RequiredArgsConstructor; + +@Validator +@RequiredArgsConstructor +public class ArchivingValidator { + private final ArchivingAdaptor archivingAdaptor; + + public void verifyUser(Long userId, Long archivingId) { + archivingAdaptor.findById(archivingId).validateUser(userId); + } + + public void validatePublicStatus(Long archivingId, Long userId) { + archivingAdaptor.findById(archivingId).validatePublicStatus(userId); + } + + public void validateDeleteStatus(Long archivingId, Long userId) { + archivingAdaptor.findById(archivingId).validateDeleteStatus(userId); + } + + public void validateExistArchiving(Long archivingId) { + if (!archivingAdaptor.queryArchivingExist(archivingId)) { + throw ArchivingNotFoundException.EXCEPTION; + } + } + + public void validateAlreadyPinStatus(Long archivingId, Long userId) { + if (archivingAdaptor.findById(archivingId).getPinUserId().contains(userId)) { + throw AlreadyPinnedArchivingException.EXCEPTION; + } + } + + public void validateNotPinStatus(Long archivingId, Long userId) { + if (!archivingAdaptor.findById(archivingId).getPinUserId().contains(userId)) { + throw NotPinnedArchivingException.EXCEPTION; + } + } + + public void validateArchivingUser(Long archivingId, Long userId) { + archivingAdaptor.findById(archivingId).validateUser(userId); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/adaptor/CategoryAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/category/adaptor/CategoryAdaptor.java deleted file mode 100644 index 82b3982c..00000000 --- a/Domain/src/main/java/allchive/server/domain/domains/category/adaptor/CategoryAdaptor.java +++ /dev/null @@ -1,64 +0,0 @@ -package allchive.server.domain.domains.category.adaptor; - - -import allchive.server.core.annotation.Adaptor; -import allchive.server.core.error.exception.InternalServerError; -import allchive.server.domain.domains.category.domain.Category; -import allchive.server.domain.domains.category.domain.enums.Subject; -import allchive.server.domain.domains.category.exception.exceptions.CategoryNotFoundException; -import allchive.server.domain.domains.category.repository.CategoryRepository; -import java.util.List; -import lombok.RequiredArgsConstructor; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Slice; - -@Adaptor -@RequiredArgsConstructor -public class CategoryAdaptor { - private final CategoryRepository categoryRepository; - - public void save(Category category) { - if (category.getSubject().equals(Subject.ALL)) { - throw InternalServerError.EXCEPTION; - } - categoryRepository.save(category); - } - - public Category findById(Long categoryId) { - return categoryRepository - .findById(categoryId) - .orElseThrow(() -> CategoryNotFoundException.EXCEPTION); - } - - public void deleteById(Long categoryId) { - categoryRepository.deleteById(categoryId); - } - - public Slice<Category> querySliceCategoryExceptBlock( - List<Long> categoryIdList, List<Long> blockList, Subject subject, Pageable pageable) { - return categoryRepository.querySliceCategoryExceptBlock( - categoryIdList, blockList, subject, pageable); - } - - public Slice<Category> querySliceCategoryByUserId( - Long userId, Subject subject, Pageable pageable) { - return categoryRepository.querySliceCategoryByUserId(userId, subject, pageable); - } - - public Slice<Category> querySliceCategoryIn( - List<Long> categoryIdList, Subject subject, Pageable pageable) { - return categoryRepository.querySliceCategoryIn(categoryIdList, subject, pageable); - } - - public List<Category> queryCategoryByUserId(Long userId) { - return categoryRepository.queryCategoryByUserId(userId); - } - - public boolean queryCategoryExist(Long categoryId) { - return categoryRepository.queryCategoryExist(categoryId); - } - - public List<Category> findAllByUserId(Long userId) { - return categoryRepository.findAllByUserId(userId); - } -} diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/exception/CategoryErrorCode.java b/Domain/src/main/java/allchive/server/domain/domains/category/exception/CategoryErrorCode.java deleted file mode 100644 index 8d5adefc..00000000 --- a/Domain/src/main/java/allchive/server/domain/domains/category/exception/CategoryErrorCode.java +++ /dev/null @@ -1,29 +0,0 @@ -package allchive.server.domain.domains.category.exception; - -import static allchive.server.core.consts.AllchiveConst.*; - -import allchive.server.core.dto.ErrorReason; -import allchive.server.core.error.BaseErrorCode; -import lombok.AllArgsConstructor; -import lombok.Getter; - -@Getter -@AllArgsConstructor -public enum CategoryErrorCode implements BaseErrorCode { - NOT_PUBLIC_CATEGORY(BAD_REQUEST, "CATEGORY_400_1", "공개된 카테고리가 아닙니다."), - DELETED_CATEGORY(BAD_REQUEST, "CATEGORY_400_2", "삭제된 카테고리입니다."), - ALREADY_PINNED_CATEGORY(BAD_REQUEST, "CATEGORY_400_3", "이미 고정 카테고리입니다."), - NOT_PINNED_CATEGORY(BAD_REQUEST, "CATEGORY_400_4", "고정되지않은 카테고리입니다."), - - NO_AUTHORITY_UPDATE_CATEGORY(FORBIDDEN, "CATEGORY_403_1", "카테고리 수정 권한이 없습니다."), - - CATEGORY_NOT_FOUND(NOT_FOUND, "CATEGORY_404_1", "카테고리를 찾을 수 없습니다."); - private int status; - private String code; - private String reason; - - @Override - public ErrorReason getErrorReason() { - return ErrorReason.of(status, code, reason); - } -} diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/exception/exceptions/AlreadyPinnedCategoryException.java b/Domain/src/main/java/allchive/server/domain/domains/category/exception/exceptions/AlreadyPinnedCategoryException.java deleted file mode 100644 index ac19dcf7..00000000 --- a/Domain/src/main/java/allchive/server/domain/domains/category/exception/exceptions/AlreadyPinnedCategoryException.java +++ /dev/null @@ -1,14 +0,0 @@ -package allchive.server.domain.domains.category.exception.exceptions; - - -import allchive.server.core.error.BaseErrorException; -import allchive.server.domain.domains.category.exception.CategoryErrorCode; - -public class AlreadyPinnedCategoryException extends BaseErrorException { - - public static final BaseErrorException EXCEPTION = new AlreadyPinnedCategoryException(); - - private AlreadyPinnedCategoryException() { - super(CategoryErrorCode.ALREADY_PINNED_CATEGORY); - } -} diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/exception/exceptions/CategoryNotFoundException.java b/Domain/src/main/java/allchive/server/domain/domains/category/exception/exceptions/CategoryNotFoundException.java deleted file mode 100644 index 90160ad8..00000000 --- a/Domain/src/main/java/allchive/server/domain/domains/category/exception/exceptions/CategoryNotFoundException.java +++ /dev/null @@ -1,14 +0,0 @@ -package allchive.server.domain.domains.category.exception.exceptions; - - -import allchive.server.core.error.BaseErrorException; -import allchive.server.domain.domains.category.exception.CategoryErrorCode; - -public class CategoryNotFoundException extends BaseErrorException { - - public static final BaseErrorException EXCEPTION = new CategoryNotFoundException(); - - private CategoryNotFoundException() { - super(CategoryErrorCode.CATEGORY_NOT_FOUND); - } -} diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/exception/exceptions/DeletedCategoryException.java b/Domain/src/main/java/allchive/server/domain/domains/category/exception/exceptions/DeletedCategoryException.java deleted file mode 100644 index 33a66854..00000000 --- a/Domain/src/main/java/allchive/server/domain/domains/category/exception/exceptions/DeletedCategoryException.java +++ /dev/null @@ -1,14 +0,0 @@ -package allchive.server.domain.domains.category.exception.exceptions; - - -import allchive.server.core.error.BaseErrorException; -import allchive.server.domain.domains.category.exception.CategoryErrorCode; - -public class DeletedCategoryException extends BaseErrorException { - - public static final BaseErrorException EXCEPTION = new DeletedCategoryException(); - - private DeletedCategoryException() { - super(CategoryErrorCode.DELETED_CATEGORY); - } -} diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/exception/exceptions/NoAuthurityUpdateCategoryException.java b/Domain/src/main/java/allchive/server/domain/domains/category/exception/exceptions/NoAuthurityUpdateCategoryException.java deleted file mode 100644 index 5bce8d8b..00000000 --- a/Domain/src/main/java/allchive/server/domain/domains/category/exception/exceptions/NoAuthurityUpdateCategoryException.java +++ /dev/null @@ -1,14 +0,0 @@ -package allchive.server.domain.domains.category.exception.exceptions; - - -import allchive.server.core.error.BaseErrorException; -import allchive.server.domain.domains.category.exception.CategoryErrorCode; - -public class NoAuthurityUpdateCategoryException extends BaseErrorException { - - public static final BaseErrorException EXCEPTION = new NoAuthurityUpdateCategoryException(); - - private NoAuthurityUpdateCategoryException() { - super(CategoryErrorCode.NO_AUTHORITY_UPDATE_CATEGORY); - } -} diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/exception/exceptions/NotPinnedCategoryException.java b/Domain/src/main/java/allchive/server/domain/domains/category/exception/exceptions/NotPinnedCategoryException.java deleted file mode 100644 index c30637f2..00000000 --- a/Domain/src/main/java/allchive/server/domain/domains/category/exception/exceptions/NotPinnedCategoryException.java +++ /dev/null @@ -1,14 +0,0 @@ -package allchive.server.domain.domains.category.exception.exceptions; - - -import allchive.server.core.error.BaseErrorException; -import allchive.server.domain.domains.category.exception.CategoryErrorCode; - -public class NotPinnedCategoryException extends BaseErrorException { - - public static final BaseErrorException EXCEPTION = new NotPinnedCategoryException(); - - private NotPinnedCategoryException() { - super(CategoryErrorCode.NOT_PINNED_CATEGORY); - } -} diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/exception/exceptions/NotPublicCategoryException.java b/Domain/src/main/java/allchive/server/domain/domains/category/exception/exceptions/NotPublicCategoryException.java deleted file mode 100644 index 0a5bc060..00000000 --- a/Domain/src/main/java/allchive/server/domain/domains/category/exception/exceptions/NotPublicCategoryException.java +++ /dev/null @@ -1,14 +0,0 @@ -package allchive.server.domain.domains.category.exception.exceptions; - - -import allchive.server.core.error.BaseErrorException; -import allchive.server.domain.domains.category.exception.CategoryErrorCode; - -public class NotPublicCategoryException extends BaseErrorException { - - public static final BaseErrorException EXCEPTION = new NotPublicCategoryException(); - - private NotPublicCategoryException() { - super(CategoryErrorCode.NOT_PUBLIC_CATEGORY); - } -} diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/repository/CategoryCustomRepository.java b/Domain/src/main/java/allchive/server/domain/domains/category/repository/CategoryCustomRepository.java deleted file mode 100644 index 477ea27a..00000000 --- a/Domain/src/main/java/allchive/server/domain/domains/category/repository/CategoryCustomRepository.java +++ /dev/null @@ -1,22 +0,0 @@ -package allchive.server.domain.domains.category.repository; - - -import allchive.server.domain.domains.category.domain.Category; -import allchive.server.domain.domains.category.domain.enums.Subject; -import java.util.List; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Slice; - -public interface CategoryCustomRepository { - Slice<Category> querySliceCategoryExceptBlock( - List<Long> categoryIdList, List<Long> blockList, Subject subject, Pageable pageable); - - Slice<Category> querySliceCategoryByUserId(Long userId, Subject subject, Pageable pageable); - - Slice<Category> querySliceCategoryIn( - List<Long> categoryIdList, Subject subject, Pageable pageable); - - List<Category> queryCategoryByUserId(Long userId); - - boolean queryCategoryExist(Long categoryId); -} diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/repository/CategoryCustomRepositoryImpl.java b/Domain/src/main/java/allchive/server/domain/domains/category/repository/CategoryCustomRepositoryImpl.java deleted file mode 100644 index e73d5eec..00000000 --- a/Domain/src/main/java/allchive/server/domain/domains/category/repository/CategoryCustomRepositoryImpl.java +++ /dev/null @@ -1,146 +0,0 @@ -package allchive.server.domain.domains.category.repository; - -import static allchive.server.domain.domains.category.domain.QCategory.category; - -import allchive.server.domain.common.util.SliceUtil; -import allchive.server.domain.domains.category.domain.Category; -import allchive.server.domain.domains.category.domain.enums.Subject; -import com.querydsl.core.types.OrderSpecifier; -import com.querydsl.core.types.dsl.BooleanExpression; -import com.querydsl.core.types.dsl.CaseBuilder; -import com.querydsl.core.types.dsl.NumberExpression; -import com.querydsl.jpa.impl.JPAQueryFactory; -import java.time.LocalDateTime; -import java.util.List; -import lombok.RequiredArgsConstructor; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Slice; - -@RequiredArgsConstructor -public class CategoryCustomRepositoryImpl implements CategoryCustomRepository { - private final JPAQueryFactory queryFactory; - - @Override - public Slice<Category> querySliceCategoryExceptBlock( - List<Long> categoryIdList, List<Long> blockList, Subject subject, Pageable pageable) { - List<Category> categories = - queryFactory - .select(category) - .from(category) - .where( - userIdNotIn(blockList), - publicStatusTrue(), - subjectEq(subject), - deleteStatusFalse()) - .orderBy(scrabListDesc(categoryIdList), scrapCntDesc(), createdAtDesc()) - .offset(pageable.getOffset()) - .limit(pageable.getPageSize() + 1) - .fetch(); - return SliceUtil.toSlice(categories, pageable); - } - - @Override - public Slice<Category> querySliceCategoryByUserId( - Long userId, Subject subject, Pageable pageable) { - List<Category> categories = - queryFactory - .select(category) - .from(category) - .where(userIdEq(userId), subjectEq(subject), deleteStatusFalse()) - .orderBy(pinDesc(userId), scrapCntDesc(), createdAtDesc()) - .offset(pageable.getOffset()) - .limit(pageable.getPageSize() + 1) - .fetch(); - return SliceUtil.toSlice(categories, pageable); - } - - @Override - public Slice<Category> querySliceCategoryIn( - List<Long> categoryIdList, Subject subject, Pageable pageable) { - List<Category> categories = - queryFactory - .select(category) - .from(category) - .where( - categoryIdListIn(categoryIdList), - publicStatusTrue(), - subjectEq(subject), - deleteStatusFalse()) - .orderBy(scrapCntDesc(), createdAtDesc()) - .offset(pageable.getOffset()) - .limit(pageable.getPageSize() + 1) - .fetch(); - return SliceUtil.toSlice(categories, pageable); - } - - @Override - public List<Category> queryCategoryByUserId(Long userId) { - return queryFactory - .selectFrom(category) - .where(userIdEq(userId), deleteStatusFalse()) - .orderBy(subjectDesc(), createdAtDesc()) - .fetch(); - } - - @Override - public boolean queryCategoryExist(Long categoryId) { - Category fetchOne = - queryFactory.selectFrom(category).where(categoryIdEq(categoryId)).fetchFirst(); - return fetchOne != null; - } - - private BooleanExpression userIdNotIn(List<Long> blockList) { - return category.userId.notIn(blockList); - } - - private BooleanExpression publicStatusTrue() { - return category.publicStatus.eq(Boolean.TRUE); - } - - private BooleanExpression subjectEq(Subject subject) { - if (subject.equals(Subject.ALL)) { - return null; - } - return category.subject.eq(subject); - } - - private BooleanExpression deleteStatusFalse() { - return category.deleteStatus.eq(Boolean.FALSE); - } - - private BooleanExpression userIdEq(Long userId) { - return category.userId.eq(userId); - } - - private BooleanExpression categoryIdListIn(List<Long> categortIdList) { - return category.id.in(categortIdList); - } - - private BooleanExpression categoryIdEq(Long categoryId) { - return category.id.eq(categoryId); - } - - private OrderSpecifier<Long> scrabListDesc(List<Long> categoryIdList) { - NumberExpression<Long> pinStatus = - new CaseBuilder().when(category.id.in(categoryIdList)).then(1L).otherwise(0L); - return pinStatus.desc(); - } - - private OrderSpecifier<Long> pinDesc(Long userId) { - NumberExpression<Long> pinStatus = - new CaseBuilder().when(category.pinUserId.contains(userId)).then(1L).otherwise(0L); - return pinStatus.desc(); - } - - private OrderSpecifier<Long> scrapCntDesc() { - return category.scrapCnt.desc(); - } - - private OrderSpecifier<LocalDateTime> createdAtDesc() { - return category.createdAt.desc(); - } - - private OrderSpecifier<Subject> subjectDesc() { - return category.subject.desc(); - } -} diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/repository/CategoryRepository.java b/Domain/src/main/java/allchive/server/domain/domains/category/repository/CategoryRepository.java deleted file mode 100644 index caf69829..00000000 --- a/Domain/src/main/java/allchive/server/domain/domains/category/repository/CategoryRepository.java +++ /dev/null @@ -1,11 +0,0 @@ -package allchive.server.domain.domains.category.repository; - - -import allchive.server.domain.domains.category.domain.Category; -import java.util.List; -import org.springframework.data.jpa.repository.JpaRepository; - -public interface CategoryRepository - extends JpaRepository<Category, Long>, CategoryCustomRepository { - List<Category> findAllByUserId(Long userId); -} diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/service/CategoryDomainService.java b/Domain/src/main/java/allchive/server/domain/domains/category/service/CategoryDomainService.java deleted file mode 100644 index 70fdcb80..00000000 --- a/Domain/src/main/java/allchive/server/domain/domains/category/service/CategoryDomainService.java +++ /dev/null @@ -1,50 +0,0 @@ -package allchive.server.domain.domains.category.service; - - -import allchive.server.core.annotation.DomainService; -import allchive.server.domain.domains.category.adaptor.CategoryAdaptor; -import allchive.server.domain.domains.category.domain.Category; -import allchive.server.domain.domains.category.domain.enums.Subject; -import lombok.RequiredArgsConstructor; - -@DomainService -@RequiredArgsConstructor -public class CategoryDomainService { - private final CategoryAdaptor categoryAdaptor; - - public void createCategory(Category category) { - categoryAdaptor.save(category); - } - - public void updateCategory( - Category category, - String title, - String imageUrl, - boolean publicStatus, - Subject subject) { - category.update(title, imageUrl, publicStatus, subject); - categoryAdaptor.save(category); - } - - public void updateScrapCount(Long categoryId, int i) { - Category category = categoryAdaptor.findById(categoryId); - category.updateScrapCnt(i); - categoryAdaptor.save(category); - } - - public void updatePin(Long categoryId, Long userId, boolean pin) { - Category category = categoryAdaptor.findById(categoryId); - if (pin) { - category.addPinUserId(userId); - } else { - category.deletePinUserId(userId); - } - category.updateScrapCnt(pin ? 1 : -1); - } - - public void deleteById(Long categoryId) { - Category category = categoryAdaptor.findById(categoryId); - category.delete(); - categoryAdaptor.save(category); - } -} diff --git a/Domain/src/main/java/allchive/server/domain/domains/category/validator/CategoryValidator.java b/Domain/src/main/java/allchive/server/domain/domains/category/validator/CategoryValidator.java deleted file mode 100644 index e25d2a0d..00000000 --- a/Domain/src/main/java/allchive/server/domain/domains/category/validator/CategoryValidator.java +++ /dev/null @@ -1,49 +0,0 @@ -package allchive.server.domain.domains.category.validator; - - -import allchive.server.core.annotation.Validator; -import allchive.server.domain.domains.category.adaptor.CategoryAdaptor; -import allchive.server.domain.domains.category.exception.exceptions.AlreadyPinnedCategoryException; -import allchive.server.domain.domains.category.exception.exceptions.CategoryNotFoundException; -import allchive.server.domain.domains.category.exception.exceptions.NotPinnedCategoryException; -import lombok.RequiredArgsConstructor; - -@Validator -@RequiredArgsConstructor -public class CategoryValidator { - private final CategoryAdaptor categoryAdaptor; - - public void verifyUser(Long userId, Long categoryId) { - categoryAdaptor.findById(categoryId).validateUser(userId); - } - - public void validatePublicStatus(Long categoryId, Long userId) { - categoryAdaptor.findById(categoryId).validatePublicStatus(userId); - } - - public void validateDeleteStatus(Long categoryId, Long userId) { - categoryAdaptor.findById(categoryId).validateDeleteStatus(userId); - } - - public void validateExistCategory(Long categoryId) { - if (!categoryAdaptor.queryCategoryExist(categoryId)) { - throw CategoryNotFoundException.EXCEPTION; - } - } - - public void validateAlreadyPinStatus(Long categoryId, Long userId) { - if (categoryAdaptor.findById(categoryId).getPinUserId().contains(userId)) { - throw AlreadyPinnedCategoryException.EXCEPTION; - } - } - - public void validateNotPinStatus(Long categoryId, Long userId) { - if (!categoryAdaptor.findById(categoryId).getPinUserId().contains(userId)) { - throw NotPinnedCategoryException.EXCEPTION; - } - } - - public void validateCategoryUser(Long categoryId, Long userId) { - categoryAdaptor.findById(categoryId).validateUser(userId); - } -} diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentAdaptor.java index 833898f2..96a75669 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentAdaptor.java @@ -14,8 +14,8 @@ public class ContentAdaptor { private final ContentRepository contentRepository; - public Slice<Content> querySliceContentByCategoryId(Long categoryId, Pageable pageable) { - return contentRepository.querySliceContentByCategoryId(categoryId, pageable); + public Slice<Content> querySliceContentByArchivingId(Long archivingId, Pageable pageable) { + return contentRepository.querySliceContentByArchivingId(archivingId, pageable); } public void save(Content content) { diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/domain/Content.java b/Domain/src/main/java/allchive/server/domain/domains/content/domain/Content.java index dd4396d1..1ad3eccb 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/domain/Content.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/domain/Content.java @@ -18,7 +18,7 @@ public class Content extends BaseTimeEntity { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - private Long categoryId; + private Long archivingId; @Enumerated(EnumType.STRING) private ContentType contentType; @@ -34,14 +34,14 @@ public class Content extends BaseTimeEntity { @Builder private Content( - Long categoryId, + Long archivingId, ContentType contentType, String imageUrl, String linkUrl, String title, String memo, boolean deleteStatus) { - this.categoryId = categoryId; + this.archivingId = archivingId; this.contentType = contentType; this.imageUrl = imageUrl; this.linkUrl = linkUrl; @@ -51,14 +51,14 @@ private Content( } public static Content of( - Long categoryId, + Long archivingId, ContentType contentType, String imageUrl, String linkUrl, String title, String memo) { return Content.builder() - .categoryId(categoryId) + .archivingId(archivingId) .contentType(contentType) .imageUrl(imageUrl) .linkUrl(linkUrl) diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/domain/enums/ContentType.java b/Domain/src/main/java/allchive/server/domain/domains/content/domain/enums/ContentType.java index d0b206c1..fbec1562 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/domain/enums/ContentType.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/domain/enums/ContentType.java @@ -19,7 +19,7 @@ public enum ContentType { @JsonCreator public static OauthProvider parsing(String inputValue) { return Stream.of(OauthProvider.values()) - .filter(category -> category.getValue().equals(inputValue)) + .filter(type -> type.getValue().equals(inputValue)) .findFirst() .orElse(null); } diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/exception/ContentErrorCode.java b/Domain/src/main/java/allchive/server/domain/domains/content/exception/ContentErrorCode.java index 606aec08..0e80e34e 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/exception/ContentErrorCode.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/exception/ContentErrorCode.java @@ -10,7 +10,7 @@ @Getter @AllArgsConstructor public enum ContentErrorCode implements BaseErrorCode { - CONTENT_NOT_FOUND(NOT_FOUND, "CATEGORY_404_1", "카테고리를 찾을 수 없습니다."); + CONTENT_NOT_FOUND(NOT_FOUND, "CONTENT_404_1", "카테고리를 찾을 수 없습니다."); private int status; private String code; diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentCustomRepository.java b/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentCustomRepository.java index 477f738c..e43b38ae 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentCustomRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentCustomRepository.java @@ -6,5 +6,5 @@ import org.springframework.data.domain.Slice; public interface ContentCustomRepository { - Slice<Content> querySliceContentByCategoryId(Long categoryId, Pageable pageable); + Slice<Content> querySliceContentByArchivingId(Long archivingId, Pageable pageable); } diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentCustomRepositoryImpl.java b/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentCustomRepositoryImpl.java index 24165d61..a07937c3 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentCustomRepositoryImpl.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentCustomRepositoryImpl.java @@ -18,20 +18,20 @@ public class ContentCustomRepositoryImpl implements ContentCustomRepository { private final JPAQueryFactory queryFactory; @Override - public Slice<Content> querySliceContentByCategoryId(Long categoryId, Pageable pageable) { - List<Content> categories = + public Slice<Content> querySliceContentByArchivingId(Long archivingId, Pageable pageable) { + List<Content> archivings = queryFactory .selectFrom(content) - .where(categoryIdEq(categoryId)) + .where(archivingIdEq(archivingId)) .orderBy(createdAtDesc()) .offset(pageable.getOffset()) .limit(pageable.getPageSize() + 1) .fetch(); - return SliceUtil.toSlice(categories, pageable); + return SliceUtil.toSlice(archivings, pageable); } - private BooleanExpression categoryIdEq(Long categoryId) { - return content.categoryId.eq(categoryId); + private BooleanExpression archivingIdEq(Long archivingId) { + return content.archivingId.eq(archivingId); } private OrderSpecifier<LocalDateTime> createdAtDesc() { diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentTagGroupCustomRepositoryImpl.java b/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentTagGroupCustomRepositoryImpl.java index e7a5b4d4..7d4124b8 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentTagGroupCustomRepositoryImpl.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentTagGroupCustomRepositoryImpl.java @@ -22,12 +22,12 @@ public List<ContentTagGroup> queryContentTagGroupIn(List<Content> contentList) { .selectFrom(contentTagGroup) .join(contentTagGroup.tag, tag) .fetchJoin() - .where(categoryIdEq(contentList)) + .where(archivingIdEq(contentList)) .orderBy(createdAtDesc()) .fetch(); } - private BooleanExpression categoryIdEq(List<Content> contentList) { + private BooleanExpression archivingIdEq(List<Content> contentList) { return contentTagGroup.content.in(contentList); } diff --git a/Domain/src/main/java/allchive/server/domain/domains/recycle/domain/Recycle.java b/Domain/src/main/java/allchive/server/domain/domains/recycle/domain/Recycle.java index da42a523..b888aef8 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/recycle/domain/Recycle.java +++ b/Domain/src/main/java/allchive/server/domain/domains/recycle/domain/Recycle.java @@ -23,7 +23,7 @@ public class Recycle extends BaseTimeEntity { private RecycleType recycleType; private Long contentId; - private Long categoryId; + private Long archivingId; private Long userId; private LocalDateTime deletedAt; @@ -31,20 +31,20 @@ public class Recycle extends BaseTimeEntity { private Recycle( RecycleType recycleType, Long contentId, - Long categoryId, + Long archivingId, Long userId, LocalDateTime deletedAt) { this.recycleType = recycleType; this.contentId = contentId; - this.categoryId = categoryId; + this.archivingId = archivingId; this.userId = userId; this.deletedAt = deletedAt; } public static Recycle of( - RecycleType recycleType, Long contentId, Long categoryId, Long userId) { + RecycleType recycleType, Long contentId, Long archivingId, Long userId) { return Recycle.builder() - .categoryId(categoryId) + .archivingId(archivingId) .contentId(contentId) .recycleType(recycleType) .userId(userId) diff --git a/Domain/src/main/java/allchive/server/domain/domains/recycle/domain/enums/RecycleType.java b/Domain/src/main/java/allchive/server/domain/domains/recycle/domain/enums/RecycleType.java index 384b72f5..4b978a54 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/recycle/domain/enums/RecycleType.java +++ b/Domain/src/main/java/allchive/server/domain/domains/recycle/domain/enums/RecycleType.java @@ -12,14 +12,14 @@ @AllArgsConstructor public enum RecycleType { CONTENT("content"), - CATEGORY("category"); + ARCHIVING("archiving"); @JsonValue private String value; @JsonCreator public static OauthProvider parsing(String inputValue) { return Stream.of(OauthProvider.values()) - .filter(category -> category.getValue().equals(inputValue)) + .filter(type -> type.getValue().equals(inputValue)) .findFirst() .orElse(null); } diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/ScrapAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/ScrapAdaptor.java index 6e626bbe..35e57626 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/ScrapAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/ScrapAdaptor.java @@ -18,8 +18,8 @@ public List<Scrap> findAllByUserId(Long userId) { return scrapRepository.findAllByUserId(userId); } - public Optional<Scrap> findByUserAndCategoryId(User user, Long categoryId) { - return scrapRepository.findAllByUserAndCategoryId(user, categoryId); + public Optional<Scrap> findByUserAndArchivingId(User user, Long archivingId) { + return scrapRepository.findAllByUserAndArchivingId(user, archivingId); } public void delete(Scrap scrap) { diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/domain/Scrap.java b/Domain/src/main/java/allchive/server/domain/domains/user/domain/Scrap.java index dd7d98da..ebb7b2a2 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/domain/Scrap.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/domain/Scrap.java @@ -20,15 +20,15 @@ public class Scrap extends BaseTimeEntity { @ManyToOne(fetch = FetchType.LAZY) private User user; - private Long categoryId; + private Long archivingId; @Builder - private Scrap(User user, Long categoryId) { + private Scrap(User user, Long archivingId) { this.user = user; - this.categoryId = categoryId; + this.archivingId = archivingId; } - public static Scrap of(User user, Long categoryId) { - return Scrap.builder().user(user).categoryId(categoryId).build(); + public static Scrap of(User user, Long archivingId) { + return Scrap.builder().user(user).archivingId(archivingId).build(); } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/domain/User.java b/Domain/src/main/java/allchive/server/domain/domains/user/domain/User.java index d8ed940e..8d3c49c4 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/domain/User.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/domain/User.java @@ -3,7 +3,7 @@ import allchive.server.domain.common.convertor.StringListConverter; import allchive.server.domain.common.model.BaseTimeEntity; -import allchive.server.domain.domains.category.domain.enums.Subject; +import allchive.server.domain.domains.archiving.domain.enums.Category; import allchive.server.domain.domains.user.domain.enums.OauthInfo; import allchive.server.domain.domains.user.domain.enums.UserRole; import allchive.server.domain.domains.user.domain.enums.UserState; @@ -47,7 +47,7 @@ public class User extends BaseTimeEntity { private UserRole userRole = UserRole.USER; @Convert(converter = StringListConverter.class) - private List<Subject> subjects = new ArrayList<>(); + private List<Category> categories = new ArrayList<>(); @Builder private User(String nickname, String profileImgUrl, OauthInfo oauthInfo) { @@ -81,7 +81,7 @@ public void withdrawUser() { this.profileImgUrl = null; this.email = null; this.oauthInfo.withDrawOauthInfo(); - this.subjects = new ArrayList<>(); + this.categories = new ArrayList<>(); this.name = null; } diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/domain/enums/OauthProvider.java b/Domain/src/main/java/allchive/server/domain/domains/user/domain/enums/OauthProvider.java index c72c330b..5a327a6f 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/domain/enums/OauthProvider.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/domain/enums/OauthProvider.java @@ -17,7 +17,7 @@ public enum OauthProvider { @JsonCreator public static OauthProvider parsing(String inputValue) { return Stream.of(OauthProvider.values()) - .filter(category -> category.getValue().equals(inputValue)) + .filter(oauthProvider -> oauthProvider.getValue().equals(inputValue)) .findFirst() .orElse(null); } diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/repository/ScrapRepository.java b/Domain/src/main/java/allchive/server/domain/domains/user/repository/ScrapRepository.java index 654ec699..83e253f2 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/repository/ScrapRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/repository/ScrapRepository.java @@ -10,5 +10,5 @@ public interface ScrapRepository extends JpaRepository<Scrap, Long> { List<Scrap> findAllByUserId(Long userId); - Optional<Scrap> findAllByUserAndCategoryId(User user, Long categoryId); + Optional<Scrap> findAllByUserAndArchivingId(User user, Long archivingId); } diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/service/ScrapDomainService.java b/Domain/src/main/java/allchive/server/domain/domains/user/service/ScrapDomainService.java index 1b9c5858..6fab2c70 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/service/ScrapDomainService.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/service/ScrapDomainService.java @@ -13,10 +13,10 @@ public class ScrapDomainService { private final ScrapAdaptor scrapAdaptor; - public void deleteScrapByUserAndCategoryId(User user, Long categoryId) { + public void deleteScrapByUserAndArchivingId(User user, Long archivingId) { Scrap scrap = scrapAdaptor - .findByUserAndCategoryId(user, categoryId) + .findByUserAndArchivingId(user, archivingId) .orElseThrow(() -> ScrapNotFoundException.EXCEPTION); ; scrapAdaptor.delete(scrap); diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/validator/ScrapValidator.java b/Domain/src/main/java/allchive/server/domain/domains/user/validator/ScrapValidator.java index 1396b7e1..1c7f5b4e 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/validator/ScrapValidator.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/validator/ScrapValidator.java @@ -12,8 +12,8 @@ public class ScrapValidator { private final ScrapAdaptor scrapAdaptor; - public void validateExistScrap(User user, Long categoryId) { - if (scrapAdaptor.findByUserAndCategoryId(user, categoryId).isPresent()) { + public void validateExistScrap(User user, Long archivingId) { + if (scrapAdaptor.findByUserAndArchivingId(user, archivingId).isPresent()) { throw AlreadyExistScrapException.EXCEPTION; } } diff --git a/Infrastructure/src/main/java/allchive/server/infrastructure/s3/PresignedType.java b/Infrastructure/src/main/java/allchive/server/infrastructure/s3/PresignedType.java index 8b9572c7..335ffdff 100644 --- a/Infrastructure/src/main/java/allchive/server/infrastructure/s3/PresignedType.java +++ b/Infrastructure/src/main/java/allchive/server/infrastructure/s3/PresignedType.java @@ -2,6 +2,6 @@ public enum PresignedType { USER, - CATEGORY, + ARCHIVING, CONTENT } diff --git a/Infrastructure/src/main/java/allchive/server/infrastructure/s3/S3PresignedUrlService.java b/Infrastructure/src/main/java/allchive/server/infrastructure/s3/S3PresignedUrlService.java index e9c3b11f..076dd455 100644 --- a/Infrastructure/src/main/java/allchive/server/infrastructure/s3/S3PresignedUrlService.java +++ b/Infrastructure/src/main/java/allchive/server/infrastructure/s3/S3PresignedUrlService.java @@ -39,7 +39,7 @@ private String generateFileName(Long id, PresignedType presignedType) { switch (presignedType) { case USER -> fileName = baseUrl + "/user/"; case CONTENT -> fileName = baseUrl + "/content/"; - case CATEGORY -> fileName = baseUrl + "/category/"; + case ARCHIVING -> fileName = baseUrl + "/archiving/"; default -> throw InternalServerError.EXCEPTION; } return fileName + id.toString() + "/" + UUID.randomUUID(); From 799650eaa87ecb2f8c602ba0f8cd99ec9d9ce365 Mon Sep 17 00:00:00 2001 From: Sanghoon Jeong <67852689+wjdtkdgns@users.noreply.github.com> Date: Wed, 12 Jul 2023 21:33:14 +0900 Subject: [PATCH 28/41] =?UTF-8?q?[feat]=20Tag=20=EC=88=98=EC=A0=95,=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84=20?= =?UTF-8?q?(#32)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [feat] tag 조회 기능 구현 #31 * [feat] tag 생성 기능 조회 #31 * [feat] tag 수정 기능 구현 #31 * [feat] tag 삭제 기능 구현 #31 * [feat] content 생성 기능에 tag 연결 #31 * [chore] spotless 적용 --- .../service/GetArchivingUseCase.java | 7 +-- .../content/controller/ContentController.java | 2 +- .../dto/response/ContentTagResponse.java | 2 +- .../content/model/mapper/ContentMapper.java | 7 ++- .../content/service/CreateContentUseCase.java | 14 +++++ .../api/tag/controller/TagController.java | 52 +++++++++++++++++++ .../model/dto/request/CreateTagRequest.java | 9 ++++ .../model/dto/request/UpdateTagRequest.java | 11 ++++ .../model/dto/response/AllTagResponse.java | 20 +++++++ .../tag/model/dto/response/TagResponse.java | 2 +- .../api/tag/model/mapper/TagMapper.java | 21 ++++++++ .../api/tag/service/CreateTagUseCase.java | 25 +++++++++ .../api/tag/service/DeleteTagUseCase.java | 30 +++++++++++ .../api/tag/service/GetAllTagUseCase.java | 33 ++++++++++++ .../api/tag/service/UpdateTagUseCase.java | 24 +++++++++ .../api/user/model/mapper/UserMapper.java | 3 +- .../archiving/adaptor/ArchivingAdaptor.java | 8 +-- .../ArchivingCustomRepositoryImpl.java | 5 +- .../adaptor/ContentTagGroupAdaptor.java | 9 ++++ .../domains/content/adaptor/TagAdaptor.java | 32 +++++++++++- .../content/domain/ContentTagGroup.java | 11 ++++ .../domain/domains/content/domain/Tag.java | 26 ++++++++++ .../content/exception/ContentErrorCode.java | 7 ++- .../NoAuthorityUpdateException.java | 14 +++++ .../exceptions/TagNotFoundException.java | 14 +++++ .../repository/ContentTagGroupRepository.java | 3 ++ .../repository/TagCustomRepository.java | 11 ++++ .../repository/TagCustomRepositoryImpl.java | 46 ++++++++++++++++ .../content/repository/TagRepository.java | 5 +- .../service/ContentTagGroupService.java | 7 --- .../ContentTagGroupdDomainService.java | 23 ++++++++ .../content/service/TagDomainService.java | 27 ++++++++++ .../domains/content/service/TagService.java | 7 --- .../content/validator/TagValidator.java | 28 +++++++++- .../src/main/resources/application-domain.yml | 3 +- 35 files changed, 516 insertions(+), 32 deletions(-) create mode 100644 Api/src/main/java/allchive/server/api/tag/controller/TagController.java create mode 100644 Api/src/main/java/allchive/server/api/tag/model/dto/request/CreateTagRequest.java create mode 100644 Api/src/main/java/allchive/server/api/tag/model/dto/request/UpdateTagRequest.java create mode 100644 Api/src/main/java/allchive/server/api/tag/model/dto/response/AllTagResponse.java rename Api/src/main/java/allchive/server/{ => api}/tag/model/dto/response/TagResponse.java (92%) create mode 100644 Api/src/main/java/allchive/server/api/tag/model/mapper/TagMapper.java create mode 100644 Api/src/main/java/allchive/server/api/tag/service/CreateTagUseCase.java create mode 100644 Api/src/main/java/allchive/server/api/tag/service/DeleteTagUseCase.java create mode 100644 Api/src/main/java/allchive/server/api/tag/service/GetAllTagUseCase.java create mode 100644 Api/src/main/java/allchive/server/api/tag/service/UpdateTagUseCase.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/content/exception/exceptions/NoAuthorityUpdateException.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/content/exception/exceptions/TagNotFoundException.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/content/repository/TagCustomRepository.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/content/repository/TagCustomRepositoryImpl.java delete mode 100644 Domain/src/main/java/allchive/server/domain/domains/content/service/ContentTagGroupService.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/content/service/ContentTagGroupdDomainService.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/content/service/TagDomainService.java delete mode 100644 Domain/src/main/java/allchive/server/domain/domains/content/service/TagService.java diff --git a/Api/src/main/java/allchive/server/api/archiving/service/GetArchivingUseCase.java b/Api/src/main/java/allchive/server/api/archiving/service/GetArchivingUseCase.java index 68437292..d0757297 100644 --- a/Api/src/main/java/allchive/server/api/archiving/service/GetArchivingUseCase.java +++ b/Api/src/main/java/allchive/server/api/archiving/service/GetArchivingUseCase.java @@ -5,10 +5,10 @@ import allchive.server.api.common.slice.SliceResponse; import allchive.server.api.config.security.SecurityUtil; import allchive.server.core.annotation.UseCase; -import allchive.server.domain.domains.block.adaptor.BlockAdaptor; -import allchive.server.domain.domains.block.domain.Block; import allchive.server.domain.domains.archiving.adaptor.ArchivingAdaptor; import allchive.server.domain.domains.archiving.domain.enums.Category; +import allchive.server.domain.domains.block.adaptor.BlockAdaptor; +import allchive.server.domain.domains.block.domain.Block; import allchive.server.domain.domains.user.adaptor.ScrapAdaptor; import allchive.server.domain.domains.user.domain.Scrap; import java.util.List; @@ -33,7 +33,8 @@ public SliceResponse<ArchivingResponse> execute(Category category, Pageable page blockAdaptor.findByBlockFrom(userId).stream().map(Block::getBlockUser).toList(); Slice<ArchivingResponse> archivingSlices = archivingAdaptor - .querySliceArchivingExceptBlock(archivingIdList, blockList, category, pageable) + .querySliceArchivingExceptBlock( + archivingIdList, blockList, category, pageable) .map( archiving -> ArchivingResponse.of( diff --git a/Api/src/main/java/allchive/server/api/content/controller/ContentController.java b/Api/src/main/java/allchive/server/api/content/controller/ContentController.java index 565e0742..f3059a03 100644 --- a/Api/src/main/java/allchive/server/api/content/controller/ContentController.java +++ b/Api/src/main/java/allchive/server/api/content/controller/ContentController.java @@ -34,7 +34,7 @@ public ContentTagResponse createContent(@PathVariable Long contentId) { return getContentUseCase.execute(contentId); } - @Operation(summary = "컨텐츠 내용을 가져옵니다.") + @Operation(summary = "컨텐츠를 삭제합니다.") @DeleteMapping(value = "/{contentId}") public void deleteContent(@PathVariable Long contentId) { deleteContentUseCase.execute(contentId); diff --git a/Api/src/main/java/allchive/server/api/content/model/dto/response/ContentTagResponse.java b/Api/src/main/java/allchive/server/api/content/model/dto/response/ContentTagResponse.java index 99fe1ef1..b3520294 100644 --- a/Api/src/main/java/allchive/server/api/content/model/dto/response/ContentTagResponse.java +++ b/Api/src/main/java/allchive/server/api/content/model/dto/response/ContentTagResponse.java @@ -2,10 +2,10 @@ import allchive.server.api.common.util.UrlUtil; +import allchive.server.api.tag.model.dto.response.TagResponse; import allchive.server.core.annotation.DateFormat; import allchive.server.domain.domains.content.domain.Content; import allchive.server.domain.domains.content.domain.enums.ContentType; -import allchive.server.tag.model.dto.response.TagResponse; import io.swagger.v3.oas.annotations.media.Schema; import java.time.LocalDateTime; import java.util.List; diff --git a/Api/src/main/java/allchive/server/api/content/model/mapper/ContentMapper.java b/Api/src/main/java/allchive/server/api/content/model/mapper/ContentMapper.java index 380a2c85..a967bfab 100644 --- a/Api/src/main/java/allchive/server/api/content/model/mapper/ContentMapper.java +++ b/Api/src/main/java/allchive/server/api/content/model/mapper/ContentMapper.java @@ -5,10 +5,11 @@ import allchive.server.api.content.model.dto.request.CreateContentRequest; import allchive.server.api.content.model.dto.response.ContentResponse; import allchive.server.api.content.model.dto.response.ContentTagResponse; +import allchive.server.api.tag.model.dto.response.TagResponse; import allchive.server.core.annotation.Mapper; import allchive.server.domain.domains.content.domain.Content; import allchive.server.domain.domains.content.domain.ContentTagGroup; -import allchive.server.tag.model.dto.response.TagResponse; +import allchive.server.domain.domains.content.domain.Tag; import java.util.List; @Mapper @@ -42,4 +43,8 @@ public ContentTagResponse toContentTagResponse( .toList(); return ContentTagResponse.of(content, tagResponseList); } + + public List<ContentTagGroup> toContentTagGroupEntityList(Content content, List<Tag> tags) { + return tags.stream().map(tag -> ContentTagGroup.of(content, tag)).toList(); + } } diff --git a/Api/src/main/java/allchive/server/api/content/service/CreateContentUseCase.java b/Api/src/main/java/allchive/server/api/content/service/CreateContentUseCase.java index 3cb6833e..488a0508 100644 --- a/Api/src/main/java/allchive/server/api/content/service/CreateContentUseCase.java +++ b/Api/src/main/java/allchive/server/api/content/service/CreateContentUseCase.java @@ -6,8 +6,14 @@ import allchive.server.api.content.model.mapper.ContentMapper; import allchive.server.core.annotation.UseCase; import allchive.server.domain.domains.archiving.validator.ArchivingValidator; +import allchive.server.domain.domains.content.adaptor.TagAdaptor; import allchive.server.domain.domains.content.domain.Content; +import allchive.server.domain.domains.content.domain.ContentTagGroup; +import allchive.server.domain.domains.content.domain.Tag; import allchive.server.domain.domains.content.service.ContentDomainService; +import allchive.server.domain.domains.content.service.ContentTagGroupdDomainService; +import allchive.server.domain.domains.content.validator.TagValidator; +import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.transaction.annotation.Transactional; @@ -17,6 +23,9 @@ public class CreateContentUseCase { private final ArchivingValidator archivingValidator; private final ContentMapper contentMapper; private final ContentDomainService contentDomainService; + private final TagValidator tagValidator; + private final TagAdaptor tagAdaptor; + private final ContentTagGroupdDomainService contentTagGroupdDomainService; // TODO : tag 만들면 연결 @Transactional @@ -24,7 +33,12 @@ public void execute(CreateContentRequest request) { archivingValidator.validateExistArchiving(request.getArchivingId()); Long userId = SecurityUtil.getCurrentUserId(); archivingValidator.validateArchivingUser(request.getArchivingId(), userId); + tagValidator.validateExistTagsAndUser(request.getTagIds(), userId); + List<Tag> tags = tagAdaptor.queryTagInTagIdList(request.getTagIds()); Content content = contentMapper.toEntity(request); + List<ContentTagGroup> contentTagGroupList = + contentMapper.toContentTagGroupEntityList(content, tags); + contentTagGroupdDomainService.saveAll(contentTagGroupList); contentDomainService.save(content); } } diff --git a/Api/src/main/java/allchive/server/api/tag/controller/TagController.java b/Api/src/main/java/allchive/server/api/tag/controller/TagController.java new file mode 100644 index 00000000..202d5485 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/tag/controller/TagController.java @@ -0,0 +1,52 @@ +package allchive.server.api.tag.controller; + + +import allchive.server.api.tag.model.dto.request.CreateTagRequest; +import allchive.server.api.tag.model.dto.request.UpdateTagRequest; +import allchive.server.api.tag.model.dto.response.AllTagResponse; +import allchive.server.api.tag.service.CreateTagUseCase; +import allchive.server.api.tag.service.DeleteTagUseCase; +import allchive.server.api.tag.service.GetAllTagUseCase; +import allchive.server.api.tag.service.UpdateTagUseCase; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/tags") +@RequiredArgsConstructor +@Slf4j +@Tag(name = "5. [tag]") +public class TagController { + private final GetAllTagUseCase getAllTagUseCase; + private final CreateTagUseCase createTagUseCase; + private final UpdateTagUseCase updateTagUseCase; + private final DeleteTagUseCase deleteTagUseCase; + + @Operation(summary = "모든 태그를 가져옵니다.", description = "latest = true 면 최근 사용한 태그를 가져옵니다.") + @GetMapping() + public AllTagResponse getAllTag(@RequestParam("latest") Boolean latestStatus) { + return getAllTagUseCase.execute(latestStatus); + } + + @Operation(summary = "태그를 추가합니다.") + @PostMapping() + public void createTag(@RequestBody CreateTagRequest request) { + createTagUseCase.execute(request); + } + + @Operation(summary = "태그를 수정합니다.") + @PatchMapping(value = "/{tagId}") + public void updateTag( + @PathVariable("tagId") Long tagId, @RequestBody UpdateTagRequest request) { + updateTagUseCase.execute(tagId, request); + } + + @Operation(summary = "태그를 삭제합니다.") + @DeleteMapping(value = "/{tagId}") + public void deleteTag(@PathVariable("tagId") Long tagId) { + deleteTagUseCase.execute(tagId); + } +} diff --git a/Api/src/main/java/allchive/server/api/tag/model/dto/request/CreateTagRequest.java b/Api/src/main/java/allchive/server/api/tag/model/dto/request/CreateTagRequest.java new file mode 100644 index 00000000..c47a4b34 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/tag/model/dto/request/CreateTagRequest.java @@ -0,0 +1,9 @@ +package allchive.server.api.tag.model.dto.request; + + +import lombok.Getter; + +@Getter +public class CreateTagRequest { + private String name; +} diff --git a/Api/src/main/java/allchive/server/api/tag/model/dto/request/UpdateTagRequest.java b/Api/src/main/java/allchive/server/api/tag/model/dto/request/UpdateTagRequest.java new file mode 100644 index 00000000..f4c9e25c --- /dev/null +++ b/Api/src/main/java/allchive/server/api/tag/model/dto/request/UpdateTagRequest.java @@ -0,0 +1,11 @@ +package allchive.server.api.tag.model.dto.request; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; + +@Getter +public class UpdateTagRequest { + @Schema(description = "태그 이름") + private String name; +} diff --git a/Api/src/main/java/allchive/server/api/tag/model/dto/response/AllTagResponse.java b/Api/src/main/java/allchive/server/api/tag/model/dto/response/AllTagResponse.java new file mode 100644 index 00000000..4006e20a --- /dev/null +++ b/Api/src/main/java/allchive/server/api/tag/model/dto/response/AllTagResponse.java @@ -0,0 +1,20 @@ +package allchive.server.api.tag.model.dto.response; + + +import java.util.List; +import lombok.Builder; +import lombok.Getter; + +@Getter +public class AllTagResponse { + private List<TagResponse> tags; + + @Builder + private AllTagResponse(List<TagResponse> tags) { + this.tags = tags; + } + + public static AllTagResponse from(List<TagResponse> tags) { + return AllTagResponse.builder().tags(tags).build(); + } +} diff --git a/Api/src/main/java/allchive/server/tag/model/dto/response/TagResponse.java b/Api/src/main/java/allchive/server/api/tag/model/dto/response/TagResponse.java similarity index 92% rename from Api/src/main/java/allchive/server/tag/model/dto/response/TagResponse.java rename to Api/src/main/java/allchive/server/api/tag/model/dto/response/TagResponse.java index 70155dde..b5a2a18d 100644 --- a/Api/src/main/java/allchive/server/tag/model/dto/response/TagResponse.java +++ b/Api/src/main/java/allchive/server/api/tag/model/dto/response/TagResponse.java @@ -1,4 +1,4 @@ -package allchive.server.tag.model.dto.response; +package allchive.server.api.tag.model.dto.response; import allchive.server.domain.domains.content.domain.Tag; diff --git a/Api/src/main/java/allchive/server/api/tag/model/mapper/TagMapper.java b/Api/src/main/java/allchive/server/api/tag/model/mapper/TagMapper.java new file mode 100644 index 00000000..95c495c1 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/tag/model/mapper/TagMapper.java @@ -0,0 +1,21 @@ +package allchive.server.api.tag.model.mapper; + + +import allchive.server.api.tag.model.dto.request.CreateTagRequest; +import allchive.server.api.tag.model.dto.response.AllTagResponse; +import allchive.server.api.tag.model.dto.response.TagResponse; +import allchive.server.core.annotation.Mapper; +import allchive.server.domain.domains.content.domain.Tag; +import java.util.List; + +@Mapper +public class TagMapper { + public AllTagResponse toAllTagResponse(List<Tag> tagList) { + List<TagResponse> tagResponseList = tagList.stream().map(TagResponse::from).toList(); + return AllTagResponse.from(tagResponseList); + } + + public Tag toEntity(CreateTagRequest request, Long userId) { + return Tag.of(request.getName(), userId); + } +} diff --git a/Api/src/main/java/allchive/server/api/tag/service/CreateTagUseCase.java b/Api/src/main/java/allchive/server/api/tag/service/CreateTagUseCase.java new file mode 100644 index 00000000..1583d44c --- /dev/null +++ b/Api/src/main/java/allchive/server/api/tag/service/CreateTagUseCase.java @@ -0,0 +1,25 @@ +package allchive.server.api.tag.service; + + +import allchive.server.api.config.security.SecurityUtil; +import allchive.server.api.tag.model.dto.request.CreateTagRequest; +import allchive.server.api.tag.model.mapper.TagMapper; +import allchive.server.core.annotation.UseCase; +import allchive.server.domain.domains.content.domain.Tag; +import allchive.server.domain.domains.content.service.TagDomainService; +import lombok.RequiredArgsConstructor; +import org.springframework.transaction.annotation.Transactional; + +@UseCase +@RequiredArgsConstructor +public class CreateTagUseCase { + private final TagMapper tagMapper; + private final TagDomainService tagDomainService; + + @Transactional + public void execute(CreateTagRequest request) { + Long userId = SecurityUtil.getCurrentUserId(); + Tag tag = tagMapper.toEntity(request, userId); + tagDomainService.save(tag); + } +} diff --git a/Api/src/main/java/allchive/server/api/tag/service/DeleteTagUseCase.java b/Api/src/main/java/allchive/server/api/tag/service/DeleteTagUseCase.java new file mode 100644 index 00000000..a62ca160 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/tag/service/DeleteTagUseCase.java @@ -0,0 +1,30 @@ +package allchive.server.api.tag.service; + + +import allchive.server.api.config.security.SecurityUtil; +import allchive.server.core.annotation.UseCase; +import allchive.server.domain.domains.content.adaptor.TagAdaptor; +import allchive.server.domain.domains.content.domain.Tag; +import allchive.server.domain.domains.content.service.ContentTagGroupdDomainService; +import allchive.server.domain.domains.content.service.TagDomainService; +import allchive.server.domain.domains.content.validator.TagValidator; +import lombok.RequiredArgsConstructor; +import org.springframework.transaction.annotation.Transactional; + +@UseCase +@RequiredArgsConstructor +public class DeleteTagUseCase { + private final TagValidator tagValidator; + private final TagAdaptor tagAdaptor; + private final ContentTagGroupdDomainService contentTagGroupdDomainService; + private final TagDomainService tagDomainService; + + @Transactional + public void execute(Long tagId) { + Long userId = SecurityUtil.getCurrentUserId(); + tagValidator.verifyUser(tagId, userId); + Tag tag = tagAdaptor.findById(tagId); + contentTagGroupdDomainService.deleteById(tag); + tagDomainService.deleteById(tagId); + } +} diff --git a/Api/src/main/java/allchive/server/api/tag/service/GetAllTagUseCase.java b/Api/src/main/java/allchive/server/api/tag/service/GetAllTagUseCase.java new file mode 100644 index 00000000..499d4926 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/tag/service/GetAllTagUseCase.java @@ -0,0 +1,33 @@ +package allchive.server.api.tag.service; + + +import allchive.server.api.config.security.SecurityUtil; +import allchive.server.api.tag.model.dto.response.AllTagResponse; +import allchive.server.api.tag.model.mapper.TagMapper; +import allchive.server.core.annotation.UseCase; +import allchive.server.domain.domains.content.adaptor.TagAdaptor; +import allchive.server.domain.domains.content.domain.Tag; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.transaction.annotation.Transactional; + +@UseCase +@RequiredArgsConstructor +public class GetAllTagUseCase { + private final TagAdaptor tagAdaptor; + private final TagMapper tagMapper; + + @Transactional(readOnly = true) + public AllTagResponse execute(Boolean latestStatus) { + List<Tag> tags = getTagList(latestStatus); + return tagMapper.toAllTagResponse(tags); + } + + private List<Tag> getTagList(Boolean latestStatus) { + Long userId = SecurityUtil.getCurrentUserId(); + if (latestStatus) { + return tagAdaptor.queryTagByUserIdOrderByUsedAt(userId); + } + return tagAdaptor.findAllByUserIdOrderByCreatedAtDesc(userId); + } +} diff --git a/Api/src/main/java/allchive/server/api/tag/service/UpdateTagUseCase.java b/Api/src/main/java/allchive/server/api/tag/service/UpdateTagUseCase.java new file mode 100644 index 00000000..881ec389 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/tag/service/UpdateTagUseCase.java @@ -0,0 +1,24 @@ +package allchive.server.api.tag.service; + + +import allchive.server.api.config.security.SecurityUtil; +import allchive.server.api.tag.model.dto.request.UpdateTagRequest; +import allchive.server.core.annotation.UseCase; +import allchive.server.domain.domains.content.service.TagDomainService; +import allchive.server.domain.domains.content.validator.TagValidator; +import lombok.RequiredArgsConstructor; +import org.springframework.transaction.annotation.Transactional; + +@UseCase +@RequiredArgsConstructor +public class UpdateTagUseCase { + private final TagValidator tagValidator; + private final TagDomainService tagDomainService; + + @Transactional + public void execute(Long tagId, UpdateTagRequest request) { + Long userId = SecurityUtil.getCurrentUserId(); + tagValidator.verifyUser(tagId, userId); + tagDomainService.updateTag(tagId, request.getName()); + } +} diff --git a/Api/src/main/java/allchive/server/api/user/model/mapper/UserMapper.java b/Api/src/main/java/allchive/server/api/user/model/mapper/UserMapper.java index ad2adcc5..84468718 100644 --- a/Api/src/main/java/allchive/server/api/user/model/mapper/UserMapper.java +++ b/Api/src/main/java/allchive/server/api/user/model/mapper/UserMapper.java @@ -9,7 +9,8 @@ @Mapper public class UserMapper { - public GetUserProfileResponse toGetUserProfileResponse(List<Archiving> archivingList, User user) { + public GetUserProfileResponse toGetUserProfileResponse( + List<Archiving> archivingList, User user) { int linkCount = 0, imgCount = 0, publicArchivingCount = 0; for (Archiving archiving : archivingList) { linkCount += archiving.getLinkCnt(); diff --git a/Domain/src/main/java/allchive/server/domain/domains/archiving/adaptor/ArchivingAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/archiving/adaptor/ArchivingAdaptor.java index 79bbb991..4fce6e48 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/archiving/adaptor/ArchivingAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/archiving/adaptor/ArchivingAdaptor.java @@ -3,13 +3,10 @@ import allchive.server.core.annotation.Adaptor; import allchive.server.core.error.exception.InternalServerError; -import allchive.server.domain.common.util.SliceUtil; import allchive.server.domain.domains.archiving.domain.Archiving; import allchive.server.domain.domains.archiving.domain.enums.Category; import allchive.server.domain.domains.archiving.exception.exceptions.ArchivingNotFoundException; import allchive.server.domain.domains.archiving.repository.ArchivingRepository; - -import java.util.ArrayList; import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Pageable; @@ -38,7 +35,10 @@ public void deleteById(Long archivingId) { } public Slice<Archiving> querySliceArchivingExceptBlock( - List<Long> archivingIdList, List<Long> blockList, Category category, Pageable pageable) { + List<Long> archivingIdList, + List<Long> blockList, + Category category, + Pageable pageable) { return archivingRepository.querySliceArchivingExceptBlock( archivingIdList, blockList, category, pageable); } diff --git a/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingCustomRepositoryImpl.java b/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingCustomRepositoryImpl.java index 06c0658a..6c346be7 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingCustomRepositoryImpl.java +++ b/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingCustomRepositoryImpl.java @@ -22,7 +22,10 @@ public class ArchivingCustomRepositoryImpl implements ArchivingCustomRepository @Override public Slice<Archiving> querySliceArchivingExceptBlock( - List<Long> archivingIdList, List<Long> blockList, Category category, Pageable pageable) { + List<Long> archivingIdList, + List<Long> blockList, + Category category, + Pageable pageable) { List<Archiving> archivings = queryFactory .select(archiving) diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentTagGroupAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentTagGroupAdaptor.java index 56c8e859..5f7947d2 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentTagGroupAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentTagGroupAdaptor.java @@ -4,6 +4,7 @@ import allchive.server.core.annotation.Adaptor; import allchive.server.domain.domains.content.domain.Content; import allchive.server.domain.domains.content.domain.ContentTagGroup; +import allchive.server.domain.domains.content.domain.Tag; import allchive.server.domain.domains.content.repository.ContentTagGroupRepository; import java.util.List; import lombok.RequiredArgsConstructor; @@ -20,4 +21,12 @@ public List<ContentTagGroup> queryContentIn(List<Content> contentList) { public List<ContentTagGroup> findAllByContent(Content content) { return contentTagGroupRepository.findAllByContent(content); } + + public void deleteByTag(Tag tag) { + contentTagGroupRepository.deleteByTag(tag); + } + + public void saveAll(List<ContentTagGroup> contentTagGroupList) { + contentTagGroupRepository.saveAll(contentTagGroupList); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/TagAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/TagAdaptor.java index 97da461b..07f9a813 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/TagAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/TagAdaptor.java @@ -2,8 +2,38 @@ import allchive.server.core.annotation.Adaptor; +import allchive.server.domain.domains.content.domain.Tag; +import allchive.server.domain.domains.content.exception.exceptions.TagNotFoundException; +import allchive.server.domain.domains.content.repository.TagRepository; +import java.util.List; import lombok.RequiredArgsConstructor; @Adaptor @RequiredArgsConstructor -public class TagAdaptor {} +public class TagAdaptor { + private final TagRepository tagRepository; + + public List<Tag> queryTagByUserIdOrderByUsedAt(Long userId) { + return tagRepository.queryTagByUserIdOrderByUsedAt(userId); + } + + public List<Tag> findAllByUserIdOrderByCreatedAtDesc(Long userId) { + return tagRepository.findAllByUserIdOrderByCreatedAtDesc(userId); + } + + public void save(Tag tag) { + tagRepository.save(tag); + } + + public Tag findById(Long tagId) { + return tagRepository.findById(tagId).orElseThrow(() -> TagNotFoundException.EXCEPTION); + } + + public void deleteById(Long tagId) { + tagRepository.deleteById(tagId); + } + + public List<Tag> queryTagInTagIdList(List<Long> tagIds) { + return tagRepository.queryTagInTagIdList(tagIds); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/domain/ContentTagGroup.java b/Domain/src/main/java/allchive/server/domain/domains/content/domain/ContentTagGroup.java index 4aff2c9c..abe83c06 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/domain/ContentTagGroup.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/domain/ContentTagGroup.java @@ -4,6 +4,7 @@ import allchive.server.domain.common.model.BaseTimeEntity; import javax.persistence.*; import lombok.AccessLevel; +import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; @@ -21,4 +22,14 @@ public class ContentTagGroup extends BaseTimeEntity { @ManyToOne(fetch = FetchType.LAZY) private Tag tag; + + @Builder + private ContentTagGroup(Content content, Tag tag) { + this.content = content; + this.tag = tag; + } + + public static ContentTagGroup of(Content content, Tag tag) { + return ContentTagGroup.builder().content(content).tag(tag).build(); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/domain/Tag.java b/Domain/src/main/java/allchive/server/domain/domains/content/domain/Tag.java index 0439c235..dd22dee8 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/domain/Tag.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/domain/Tag.java @@ -2,8 +2,11 @@ import allchive.server.domain.common.model.BaseTimeEntity; +import allchive.server.domain.domains.content.exception.exceptions.NoAuthorityUpdateException; +import java.time.LocalDateTime; import javax.persistence.*; import lombok.AccessLevel; +import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; @@ -18,4 +21,27 @@ public class Tag extends BaseTimeEntity { private String name; private Long userId; + private LocalDateTime usedAt; + + @Builder + private Tag(String name, Long userId, LocalDateTime usedAt) { + this.name = name; + this.userId = userId; + this.usedAt = usedAt; + } + + @Builder + public static Tag of(String name, Long userId) { + return Tag.builder().name(name).userId(userId).usedAt(null).build(); + } + + public void validateUser(Long userId) { + if (!this.userId.equals(userId)) { + throw NoAuthorityUpdateException.EXCEPTION; + } + } + + public void updateName(String name) { + this.name = name; + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/exception/ContentErrorCode.java b/Domain/src/main/java/allchive/server/domain/domains/content/exception/ContentErrorCode.java index 0e80e34e..592614ba 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/exception/ContentErrorCode.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/exception/ContentErrorCode.java @@ -1,5 +1,6 @@ package allchive.server.domain.domains.content.exception; +import static allchive.server.core.consts.AllchiveConst.FORBIDDEN; import static allchive.server.core.consts.AllchiveConst.NOT_FOUND; import allchive.server.core.dto.ErrorReason; @@ -10,7 +11,11 @@ @Getter @AllArgsConstructor public enum ContentErrorCode implements BaseErrorCode { - CONTENT_NOT_FOUND(NOT_FOUND, "CONTENT_404_1", "카테고리를 찾을 수 없습니다."); + CONTENT_NOT_FOUND(NOT_FOUND, "CONTENT_404_1", "카테고리를 찾을 수 없습니다."), + + TAG_NOT_FOUND(NOT_FOUND, "TAG_404_1", "태그를 찾을 수 없습니다."), + + NO_AUTHORITY_UPDATE_TAG(FORBIDDEN, "TAG_403_1", "태그 수정 권한이 없습니다."); private int status; private String code; diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/exception/exceptions/NoAuthorityUpdateException.java b/Domain/src/main/java/allchive/server/domain/domains/content/exception/exceptions/NoAuthorityUpdateException.java new file mode 100644 index 00000000..958a4901 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/content/exception/exceptions/NoAuthorityUpdateException.java @@ -0,0 +1,14 @@ +package allchive.server.domain.domains.content.exception.exceptions; + + +import allchive.server.core.error.BaseErrorException; +import allchive.server.domain.domains.content.exception.ContentErrorCode; + +public class NoAuthorityUpdateException extends BaseErrorException { + + public static final BaseErrorException EXCEPTION = new NoAuthorityUpdateException(); + + private NoAuthorityUpdateException() { + super(ContentErrorCode.NO_AUTHORITY_UPDATE_TAG); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/exception/exceptions/TagNotFoundException.java b/Domain/src/main/java/allchive/server/domain/domains/content/exception/exceptions/TagNotFoundException.java new file mode 100644 index 00000000..ff035a1f --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/content/exception/exceptions/TagNotFoundException.java @@ -0,0 +1,14 @@ +package allchive.server.domain.domains.content.exception.exceptions; + + +import allchive.server.core.error.BaseErrorException; +import allchive.server.domain.domains.content.exception.ContentErrorCode; + +public class TagNotFoundException extends BaseErrorException { + + public static final BaseErrorException EXCEPTION = new TagNotFoundException(); + + private TagNotFoundException() { + super(ContentErrorCode.TAG_NOT_FOUND); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentTagGroupRepository.java b/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentTagGroupRepository.java index a5424f1e..f650152f 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentTagGroupRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentTagGroupRepository.java @@ -3,10 +3,13 @@ import allchive.server.domain.domains.content.domain.Content; import allchive.server.domain.domains.content.domain.ContentTagGroup; +import allchive.server.domain.domains.content.domain.Tag; import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; public interface ContentTagGroupRepository extends JpaRepository<ContentTagGroup, Long>, ContentTagGroupCustomRepository { List<ContentTagGroup> findAllByContent(Content content); + + void deleteByTag(Tag tag); } diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/repository/TagCustomRepository.java b/Domain/src/main/java/allchive/server/domain/domains/content/repository/TagCustomRepository.java new file mode 100644 index 00000000..362e9133 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/content/repository/TagCustomRepository.java @@ -0,0 +1,11 @@ +package allchive.server.domain.domains.content.repository; + + +import allchive.server.domain.domains.content.domain.Tag; +import java.util.List; + +public interface TagCustomRepository { + List<Tag> queryTagByUserIdOrderByUsedAt(Long userId); + + List<Tag> queryTagInTagIdList(List<Long> tagIds); +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/repository/TagCustomRepositoryImpl.java b/Domain/src/main/java/allchive/server/domain/domains/content/repository/TagCustomRepositoryImpl.java new file mode 100644 index 00000000..9cdcbf0a --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/content/repository/TagCustomRepositoryImpl.java @@ -0,0 +1,46 @@ +package allchive.server.domain.domains.content.repository; + +import static allchive.server.domain.domains.content.domain.QTag.tag; + +import allchive.server.domain.domains.content.domain.Tag; +import com.querydsl.core.types.OrderSpecifier; +import com.querydsl.core.types.dsl.BooleanExpression; +import com.querydsl.jpa.impl.JPAQueryFactory; +import java.time.LocalDateTime; +import java.util.List; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +public class TagCustomRepositoryImpl implements TagCustomRepository { + private final JPAQueryFactory queryFactory; + + @Override + public List<Tag> queryTagByUserIdOrderByUsedAt(Long userId) { + return queryFactory + .selectFrom(tag) + .where(usedAtNotNull(), tagUserIdEq(userId)) + .orderBy(createdAtDesc()) + .fetch(); + } + + @Override + public List<Tag> queryTagInTagIdList(List<Long> tagIds) { + return queryFactory.selectFrom(tag).where(tagIdIn(tagIds)).fetch(); + } + + private BooleanExpression tagUserIdEq(Long userId) { + return tag.userId.eq(userId); + } + + private BooleanExpression usedAtNotNull() { + return tag.usedAt.isNotNull(); + } + + private BooleanExpression tagIdIn(List<Long> tagIds) { + return tag.id.in(tagIds); + } + + private OrderSpecifier<LocalDateTime> createdAtDesc() { + return tag.createdAt.desc(); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/repository/TagRepository.java b/Domain/src/main/java/allchive/server/domain/domains/content/repository/TagRepository.java index cc527e65..0cfe5c5f 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/repository/TagRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/repository/TagRepository.java @@ -2,6 +2,9 @@ import allchive.server.domain.domains.content.domain.Tag; +import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; -public interface TagRepository extends JpaRepository<Tag, Long> {} +public interface TagRepository extends JpaRepository<Tag, Long>, TagCustomRepository { + List<Tag> findAllByUserIdOrderByCreatedAtDesc(Long userId); +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/service/ContentTagGroupService.java b/Domain/src/main/java/allchive/server/domain/domains/content/service/ContentTagGroupService.java deleted file mode 100644 index 44718ff7..00000000 --- a/Domain/src/main/java/allchive/server/domain/domains/content/service/ContentTagGroupService.java +++ /dev/null @@ -1,7 +0,0 @@ -package allchive.server.domain.domains.content.service; - - -import allchive.server.core.annotation.DomainService; - -@DomainService -public class ContentTagGroupService {} diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/service/ContentTagGroupdDomainService.java b/Domain/src/main/java/allchive/server/domain/domains/content/service/ContentTagGroupdDomainService.java new file mode 100644 index 00000000..09cbd404 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/content/service/ContentTagGroupdDomainService.java @@ -0,0 +1,23 @@ +package allchive.server.domain.domains.content.service; + + +import allchive.server.core.annotation.DomainService; +import allchive.server.domain.domains.content.adaptor.ContentTagGroupAdaptor; +import allchive.server.domain.domains.content.domain.ContentTagGroup; +import allchive.server.domain.domains.content.domain.Tag; +import java.util.List; +import lombok.RequiredArgsConstructor; + +@DomainService +@RequiredArgsConstructor +public class ContentTagGroupdDomainService { + private final ContentTagGroupAdaptor contentTagGroupAdaptor; + + public void deleteById(Tag tag) { + contentTagGroupAdaptor.deleteByTag(tag); + } + + public void saveAll(List<ContentTagGroup> contentTagGroupList) { + contentTagGroupAdaptor.saveAll(contentTagGroupList); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/service/TagDomainService.java b/Domain/src/main/java/allchive/server/domain/domains/content/service/TagDomainService.java new file mode 100644 index 00000000..a9a75605 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/content/service/TagDomainService.java @@ -0,0 +1,27 @@ +package allchive.server.domain.domains.content.service; + + +import allchive.server.core.annotation.DomainService; +import allchive.server.domain.domains.content.adaptor.TagAdaptor; +import allchive.server.domain.domains.content.domain.Tag; +import lombok.RequiredArgsConstructor; + +@DomainService +@RequiredArgsConstructor +public class TagDomainService { + private final TagAdaptor tagAdaptor; + + public void save(Tag tag) { + tagAdaptor.save(tag); + } + + public void updateTag(Long tagId, String name) { + Tag tag = tagAdaptor.findById(tagId); + tag.updateName(name); + tagAdaptor.save(tag); + } + + public void deleteById(Long tagId) { + tagAdaptor.deleteById(tagId); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/service/TagService.java b/Domain/src/main/java/allchive/server/domain/domains/content/service/TagService.java deleted file mode 100644 index fe798e38..00000000 --- a/Domain/src/main/java/allchive/server/domain/domains/content/service/TagService.java +++ /dev/null @@ -1,7 +0,0 @@ -package allchive.server.domain.domains.content.service; - - -import allchive.server.core.annotation.DomainService; - -@DomainService -public class TagService {} diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/validator/TagValidator.java b/Domain/src/main/java/allchive/server/domain/domains/content/validator/TagValidator.java index ed2064fe..3b75d3e2 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/validator/TagValidator.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/validator/TagValidator.java @@ -2,6 +2,32 @@ import allchive.server.core.annotation.Validator; +import allchive.server.domain.domains.content.adaptor.TagAdaptor; +import allchive.server.domain.domains.content.domain.Tag; +import allchive.server.domain.domains.content.exception.exceptions.NoAuthorityUpdateException; +import allchive.server.domain.domains.content.exception.exceptions.TagNotFoundException; +import java.util.List; +import lombok.RequiredArgsConstructor; @Validator -public class TagValidator {} +@RequiredArgsConstructor +public class TagValidator { + private final TagAdaptor tagAdaptor; + + public void verifyUser(Long tagId, Long userId) { + tagAdaptor.findById(tagId).validateUser(userId); + } + + public void validateExistTagsAndUser(List<Long> tagIds, Long userId) { + List<Tag> tags = tagAdaptor.queryTagInTagIdList(tagIds); + if (tagIds.size() != tags.size()) { + throw TagNotFoundException.EXCEPTION; + } + tags.forEach( + tag -> { + if (!tag.getUserId().equals(userId)) { + throw NoAuthorityUpdateException.EXCEPTION; + } + }); + } +} diff --git a/Domain/src/main/resources/application-domain.yml b/Domain/src/main/resources/application-domain.yml index 4e921213..6ec6aaf7 100644 --- a/Domain/src/main/resources/application-domain.yml +++ b/Domain/src/main/resources/application-domain.yml @@ -34,7 +34,8 @@ logging: com.zaxxer.hikari: TRACE org.springframework.orm.jpa: DEBUG org.springframework.transaction: DEBUG - org.hibernate.SQL: debug +# org.hibernate.SQL: debug +# org.hibernate.type: trace --- # dev spring: From 43d4b098586c7d5c0b5dae6a496a74905a93be89 Mon Sep 17 00:00:00 2001 From: Sanghoon Jeong <67852689+wjdtkdgns@users.noreply.github.com> Date: Wed, 12 Jul 2023 21:53:44 +0900 Subject: [PATCH 29/41] =?UTF-8?q?[feat]=20apple=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=9D=B8=20=EA=B5=AC=ED=98=84=20(#34)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [feat] login 과정 분화 #20 * [chore] spotless 적용 * [feat] apple login link 발급 기능 구현 #20 * [feat] apple 로그인, 회원가입 구현 #20 * [chore] spotless 적용 --- .../api/auth/controller/OauthController.java | 33 ++++--- .../dto/response/OauthTokenResponse.java | 9 ++ .../api/auth/service/OauthLinkUseCase.java | 10 +- .../api/auth/service/OauthLoginUseCase.java | 41 ++++---- .../auth/service/OauthRegisterUseCase.java | 6 +- .../api/auth/service/WithdrawUserUseCase.java | 6 +- .../auth/service/helper/AppleOAuthHelper.java | 94 +++++++++++++++++++ .../auth/service/helper/KakaoOauthHelper.java | 10 +- .../CoreConfigurationPropertiesConfig.java | 7 +- .../server/core/consts/AllchiveConst.java | 3 + .../core/properties/AppleOAuthProperties.java | 24 +++++ Core/src/main/resources/application-core.yml | 14 ++- .../user/domain/enums/OauthProvider.java | 3 +- Infrastructure/build.gradle | 1 + .../feign/config/OpenFeignConfig.java | 2 +- .../oauth/BaseFeignClientClass.java | 3 + .../oauth/apple/client/AppleOAuthClient.java | 27 ++++++ .../oauth/apple/client/AppleOIDCClient.java | 18 ++++ .../oauth/apple/config/AppleOAuthConfig.java | 23 +++++ .../apple/config/AppleOAuthErrorDecoder.java | 32 +++++++ .../apple/config/AppleOAuthErrorResponse.java | 29 ++++++ .../oauth/apple/helper/AppleLoginUtil.java | 83 ++++++++++++++++ .../apple/response/AppleTokenResponse.java | 16 ++++ .../oauth/kakao/BaseFeignClientClass.java | 3 - 24 files changed, 452 insertions(+), 45 deletions(-) create mode 100644 Api/src/main/java/allchive/server/api/auth/service/helper/AppleOAuthHelper.java create mode 100644 Core/src/main/java/allchive/server/core/properties/AppleOAuthProperties.java create mode 100644 Infrastructure/src/main/java/allchive/server/infrastructure/oauth/BaseFeignClientClass.java create mode 100644 Infrastructure/src/main/java/allchive/server/infrastructure/oauth/apple/client/AppleOAuthClient.java create mode 100644 Infrastructure/src/main/java/allchive/server/infrastructure/oauth/apple/client/AppleOIDCClient.java create mode 100644 Infrastructure/src/main/java/allchive/server/infrastructure/oauth/apple/config/AppleOAuthConfig.java create mode 100644 Infrastructure/src/main/java/allchive/server/infrastructure/oauth/apple/config/AppleOAuthErrorDecoder.java create mode 100644 Infrastructure/src/main/java/allchive/server/infrastructure/oauth/apple/config/AppleOAuthErrorResponse.java create mode 100644 Infrastructure/src/main/java/allchive/server/infrastructure/oauth/apple/helper/AppleLoginUtil.java create mode 100644 Infrastructure/src/main/java/allchive/server/infrastructure/oauth/apple/response/AppleTokenResponse.java delete mode 100644 Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/BaseFeignClientClass.java diff --git a/Api/src/main/java/allchive/server/api/auth/controller/OauthController.java b/Api/src/main/java/allchive/server/api/auth/controller/OauthController.java index abe655b0..dfc246a5 100644 --- a/Api/src/main/java/allchive/server/api/auth/controller/OauthController.java +++ b/Api/src/main/java/allchive/server/api/auth/controller/OauthController.java @@ -6,7 +6,6 @@ import allchive.server.api.auth.model.dto.response.OauthRegisterResponse; import allchive.server.api.auth.model.dto.response.OauthSignInResponse; import allchive.server.api.auth.service.*; -import allchive.server.core.helper.SpringEnvironmentHelper; import allchive.server.domain.domains.user.domain.enums.OauthProvider; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; @@ -23,11 +22,11 @@ public class OauthController { private final OauthLinkUseCase oauthLinkUseCase; private final OauthLoginUseCase oauthLoginUseCase; private final OauthRegisterUseCase oauthRegisterUseCase; - private final SpringEnvironmentHelper springEnvironmentHelper; @Operation( summary = "oauth 링크발급", - description = "oauth 링크를 받아볼수 있습니다. referer, host 입력 안하셔도 됩니다!") + description = "oauth 링크를 받아볼수 있습니다. referer, host 입력 안하셔도 됩니다!", + deprecated = true) @GetMapping("/link/{provider}") public OauthLoginLinkResponse getOauthLink( @PathVariable("provider") OauthProvider provider, @@ -41,33 +40,41 @@ public OauthLoginLinkResponse getOauthLink( } @Operation(summary = "개발용 oauth 링크발급", deprecated = true) - @GetMapping("/link/{provider}/test") + @GetMapping("/link/{provider}/dev") public OauthLoginLinkResponse getOauthLinkTest( @PathVariable("provider") OauthProvider provider) { - return oauthLinkUseCase.getOauthLinkTest(provider); + return oauthLinkUseCase.getOauthLinkDev(provider); } @Operation( - summary = "로그인", + summary = "로그인 (code 용)", description = "referer, host 입력 안하셔도 됩니다! 회원가입 안된 유저일 경우, canLogin=false 값을 보냅니다!") - @PostMapping("/login/{provider}") - public OauthSignInResponse oauthUserLogin( + @PostMapping("/login/{provider}/code") + public OauthSignInResponse oauthUserCodeLogin( @PathVariable("provider") OauthProvider provider, @RequestParam("code") String code, @RequestHeader(value = "referer", required = false) String referer, @RequestHeader(value = "host", required = false) String host) { if (referer.contains(host)) { String format = String.format("https://%s/", host); - return oauthLoginUseCase.execute(provider, code, format); + return oauthLoginUseCase.loginWithCode(provider, code, format); } - return oauthLoginUseCase.execute(provider, code, referer); + return oauthLoginUseCase.loginWithCode(provider, code, referer); } @Operation(summary = "개발용 로그인", deprecated = true) - @GetMapping("/login/{provider}/test") - public OauthSignInResponse oauthUserLoginTest( + @GetMapping("/login/{provider}/dev") + public OauthSignInResponse oauthUserLoginDev( @PathVariable("provider") OauthProvider provider, @RequestParam("code") String code) { - return oauthLoginUseCase.executeTest(provider, code); + return oauthLoginUseCase.devLogin(provider, code); + } + + @Operation(summary = "로그인 (idtoken 용)", description = "회원가입 안된 유저일 경우, canLogin=false 값을 보냅니다!") + @PostMapping("/login/{provider}/idtoken") + public OauthSignInResponse oauthUserIdTokenLogin( + @PathVariable("provider") OauthProvider provider, + @RequestParam("idToken") String idToken) { + return oauthLoginUseCase.loginWithIdToken(provider, idToken); } @Operation(summary = "회원가입") diff --git a/Api/src/main/java/allchive/server/api/auth/model/dto/response/OauthTokenResponse.java b/Api/src/main/java/allchive/server/api/auth/model/dto/response/OauthTokenResponse.java index 2dfe1387..e9d6fd73 100644 --- a/Api/src/main/java/allchive/server/api/auth/model/dto/response/OauthTokenResponse.java +++ b/Api/src/main/java/allchive/server/api/auth/model/dto/response/OauthTokenResponse.java @@ -1,6 +1,7 @@ package allchive.server.api.auth.model.dto.response; +import allchive.server.infrastructure.oauth.apple.response.AppleTokenResponse; import allchive.server.infrastructure.oauth.kakao.dto.KakaoTokenResponse; import lombok.Builder; import lombok.Getter; @@ -25,4 +26,12 @@ public static OauthTokenResponse from(KakaoTokenResponse kakaoTokenResponse) { .accessToken(kakaoTokenResponse.getAccessToken()) .build(); } + + public static OauthTokenResponse from(AppleTokenResponse appleTokenResponse) { + return OauthTokenResponse.builder() + .idToken(appleTokenResponse.getIdToken()) + .refreshToken(appleTokenResponse.getRefreshToken()) + .accessToken(appleTokenResponse.getAccessToken()) + .build(); + } } diff --git a/Api/src/main/java/allchive/server/api/auth/service/OauthLinkUseCase.java b/Api/src/main/java/allchive/server/api/auth/service/OauthLinkUseCase.java index afee088e..a746502f 100644 --- a/Api/src/main/java/allchive/server/api/auth/service/OauthLinkUseCase.java +++ b/Api/src/main/java/allchive/server/api/auth/service/OauthLinkUseCase.java @@ -2,6 +2,7 @@ import allchive.server.api.auth.model.dto.response.OauthLoginLinkResponse; +import allchive.server.api.auth.service.helper.AppleOAuthHelper; import allchive.server.api.auth.service.helper.KakaoOauthHelper; import allchive.server.core.annotation.UseCase; import allchive.server.core.error.exception.InvalidOauthProviderException; @@ -12,11 +13,14 @@ @RequiredArgsConstructor public class OauthLinkUseCase { private final KakaoOauthHelper kakaoOauthHelper; + private final AppleOAuthHelper appleOAuthHelper; - public OauthLoginLinkResponse getOauthLinkTest(OauthProvider provider) { + public OauthLoginLinkResponse getOauthLinkDev(OauthProvider provider) { switch (provider) { case KAKAO: - return new OauthLoginLinkResponse(kakaoOauthHelper.getKaKaoOauthLinkTest()); + return new OauthLoginLinkResponse(kakaoOauthHelper.getKaKaoOauthLinkDev()); + case APPLE: + return new OauthLoginLinkResponse(appleOAuthHelper.getAppleOauthLinkDev()); default: throw InvalidOauthProviderException.EXCEPTION; } @@ -26,6 +30,8 @@ public OauthLoginLinkResponse getOauthLink(OauthProvider provider, String refere switch (provider) { case KAKAO: return new OauthLoginLinkResponse(kakaoOauthHelper.getKaKaoOauthLink(referer)); + case APPLE: + return new OauthLoginLinkResponse(appleOAuthHelper.getAppleOAuthLink(referer)); default: throw InvalidOauthProviderException.EXCEPTION; } diff --git a/Api/src/main/java/allchive/server/api/auth/service/OauthLoginUseCase.java b/Api/src/main/java/allchive/server/api/auth/service/OauthLoginUseCase.java index d360ceba..7de13592 100644 --- a/Api/src/main/java/allchive/server/api/auth/service/OauthLoginUseCase.java +++ b/Api/src/main/java/allchive/server/api/auth/service/OauthLoginUseCase.java @@ -3,6 +3,7 @@ import allchive.server.api.auth.model.dto.response.OauthSignInResponse; import allchive.server.api.auth.model.dto.response.OauthTokenResponse; +import allchive.server.api.auth.service.helper.AppleOAuthHelper; import allchive.server.api.auth.service.helper.KakaoOauthHelper; import allchive.server.api.auth.service.helper.TokenGenerateHelper; import allchive.server.core.annotation.UseCase; @@ -16,30 +17,32 @@ @UseCase @RequiredArgsConstructor public class OauthLoginUseCase { - private final KakaoOauthHelper kakaoOauthHelper; + private final AppleOAuthHelper appleOAuthHelper; private final UserDomainService userDomainService; private final TokenGenerateHelper tokenGenerateHelper; - public OauthSignInResponse execute(OauthProvider provider, String code, String referer) { + public OauthSignInResponse loginWithCode(OauthProvider provider, String code, String referer) { final OauthTokenResponse oauthTokenResponse = getCredential(provider, code, referer); - final OauthInfo oauthInfo = getOauthInfo(provider, oauthTokenResponse.getIdToken()); - if (userDomainService.checkUserCanLogin(oauthInfo)) { - User user = userDomainService.loginUser(oauthInfo); - return tokenGenerateHelper.execute(user); - } else { - return OauthSignInResponse.cannotLogin(oauthTokenResponse.getIdToken()); - } + return processLoginWithIdToken(provider, oauthTokenResponse.getIdToken()); + } + + public OauthSignInResponse loginWithIdToken(OauthProvider provider, String idToken) { + return processLoginWithIdToken(provider, idToken); + } + + public OauthSignInResponse devLogin(OauthProvider provider, String code) { + final OauthTokenResponse oauthTokenResponse = getCredentialDev(provider, code); + return processLoginWithIdToken(provider, oauthTokenResponse.getIdToken()); } - public OauthSignInResponse executeTest(OauthProvider provider, String code) { - final OauthTokenResponse oauthTokenResponse = getCredentialTest(provider, code); - final OauthInfo oauthInfo = getOauthInfo(provider, oauthTokenResponse.getIdToken()); + private OauthSignInResponse processLoginWithIdToken(OauthProvider provider, String idToken) { + final OauthInfo oauthInfo = getOauthInfo(provider, idToken); if (userDomainService.checkUserCanLogin(oauthInfo)) { User user = userDomainService.loginUser(oauthInfo); return tokenGenerateHelper.execute(user); } else { - return OauthSignInResponse.cannotLogin(oauthTokenResponse.getIdToken()); + return OauthSignInResponse.cannotLogin(idToken); } } @@ -48,15 +51,19 @@ private OauthTokenResponse getCredential(OauthProvider provider, String code, St switch (provider) { case KAKAO: return OauthTokenResponse.from(kakaoOauthHelper.getKakaoOauthToken(code, referer)); + case APPLE: + return OauthTokenResponse.from(appleOAuthHelper.getAppleOAuthToken(code, referer)); default: throw InvalidOauthProviderException.EXCEPTION; } } - private OauthTokenResponse getCredentialTest(OauthProvider provider, String code) { + private OauthTokenResponse getCredentialDev(OauthProvider provider, String code) { switch (provider) { case KAKAO: - return OauthTokenResponse.from(kakaoOauthHelper.getKakaoOauthTokenTest(code)); + return OauthTokenResponse.from(kakaoOauthHelper.getKakaoOauthTokenDev(code)); + case APPLE: + return OauthTokenResponse.from(appleOAuthHelper.getAppleOAuthTokenDev(code)); default: throw InvalidOauthProviderException.EXCEPTION; } @@ -66,7 +73,9 @@ private OauthTokenResponse getCredentialTest(OauthProvider provider, String code private OauthInfo getOauthInfo(OauthProvider provider, String idToken) { switch (provider) { case KAKAO: - return kakaoOauthHelper.getOauthInfoByIdToken(idToken); + return kakaoOauthHelper.getKakaoOauthInfoByIdToken(idToken); + case APPLE: + return appleOAuthHelper.getAppleOAuthInfoByIdToken(idToken); default: throw InvalidOauthProviderException.EXCEPTION; } diff --git a/Api/src/main/java/allchive/server/api/auth/service/OauthRegisterUseCase.java b/Api/src/main/java/allchive/server/api/auth/service/OauthRegisterUseCase.java index 2265636d..f1259baf 100644 --- a/Api/src/main/java/allchive/server/api/auth/service/OauthRegisterUseCase.java +++ b/Api/src/main/java/allchive/server/api/auth/service/OauthRegisterUseCase.java @@ -3,6 +3,7 @@ import allchive.server.api.auth.model.dto.request.RegisterRequest; import allchive.server.api.auth.model.dto.response.OauthRegisterResponse; +import allchive.server.api.auth.service.helper.AppleOAuthHelper; import allchive.server.api.auth.service.helper.KakaoOauthHelper; import allchive.server.api.auth.service.helper.TokenGenerateHelper; import allchive.server.core.annotation.UseCase; @@ -17,6 +18,7 @@ @RequiredArgsConstructor public class OauthRegisterUseCase { private final KakaoOauthHelper kakaoOauthHelper; + private final AppleOAuthHelper appleOAuthHelper; private final UserDomainService userDomainService; private final TokenGenerateHelper tokenGenerateHelper; @@ -35,7 +37,9 @@ public OauthRegisterResponse execute( private OauthInfo getOauthInfo(OauthProvider provider, String idToken) { switch (provider) { case KAKAO: - return kakaoOauthHelper.getOauthInfoByIdToken(idToken); + return kakaoOauthHelper.getKakaoOauthInfoByIdToken(idToken); + case APPLE: + return appleOAuthHelper.getAppleOAuthInfoByIdToken(idToken); default: throw InvalidOauthProviderException.EXCEPTION; } diff --git a/Api/src/main/java/allchive/server/api/auth/service/WithdrawUserUseCase.java b/Api/src/main/java/allchive/server/api/auth/service/WithdrawUserUseCase.java index 2a33ef96..6adab4c3 100644 --- a/Api/src/main/java/allchive/server/api/auth/service/WithdrawUserUseCase.java +++ b/Api/src/main/java/allchive/server/api/auth/service/WithdrawUserUseCase.java @@ -1,6 +1,7 @@ package allchive.server.api.auth.service; +import allchive.server.api.auth.service.helper.AppleOAuthHelper; import allchive.server.api.auth.service.helper.KakaoOauthHelper; import allchive.server.api.config.security.SecurityUtil; import allchive.server.core.annotation.UseCase; @@ -16,6 +17,7 @@ @RequiredArgsConstructor public class WithdrawUserUseCase { private final KakaoOauthHelper kakaoOauthHelper; + private final AppleOAuthHelper appleOAuthHelper; private final UserAdaptor userAdaptor; private final RefreshTokenAdaptor refreshTokenAdaptor; private final UserDomainService userDomainService; @@ -28,8 +30,10 @@ public void execute(OauthProvider provider) { // oauth쪽 탈퇴 switch (provider) { case KAKAO: - kakaoOauthHelper.withdrawOauthUser(user.getOauthInfo().getOid()); + kakaoOauthHelper.withdrawKakaoOauthUser(user.getOauthInfo().getOid()); break; + // case APPLE: + // appleOAuthHelper.withdrawAppleOauthUser(); default: throw InvalidOauthProviderException.EXCEPTION; } diff --git a/Api/src/main/java/allchive/server/api/auth/service/helper/AppleOAuthHelper.java b/Api/src/main/java/allchive/server/api/auth/service/helper/AppleOAuthHelper.java new file mode 100644 index 00000000..2dbd9acf --- /dev/null +++ b/Api/src/main/java/allchive/server/api/auth/service/helper/AppleOAuthHelper.java @@ -0,0 +1,94 @@ +package allchive.server.api.auth.service.helper; + +import static allchive.server.core.consts.AllchiveConst.APPLE_OAUTH_QUERY_STRING; + +import allchive.server.core.annotation.Helper; +import allchive.server.core.dto.OIDCDecodePayload; +import allchive.server.core.properties.AppleOAuthProperties; +import allchive.server.domain.domains.user.domain.enums.OauthInfo; +import allchive.server.domain.domains.user.domain.enums.OauthProvider; +import allchive.server.infrastructure.oauth.apple.client.AppleOAuthClient; +import allchive.server.infrastructure.oauth.apple.client.AppleOIDCClient; +import allchive.server.infrastructure.oauth.apple.helper.AppleLoginUtil; +import allchive.server.infrastructure.oauth.apple.response.AppleTokenResponse; +import allchive.server.infrastructure.oauth.kakao.dto.OIDCPublicKeysResponse; +import lombok.RequiredArgsConstructor; + +@Helper +@RequiredArgsConstructor +public class AppleOAuthHelper { + private final AppleOAuthProperties appleOAuthProperties; + private final AppleOAuthClient appleOAuthClient; + private final AppleOIDCClient appleOIDCClient; + private final OauthOIDCHelper oAuthOIDCHelper; + + /** Link * */ + public String getAppleOAuthLink(String referer) { + return appleOAuthProperties.getBaseUrl() + + String.format( + APPLE_OAUTH_QUERY_STRING, + appleOAuthProperties.getClientId(), + referer + appleOAuthProperties.getWebCallbackUrl()); + } + + public String getAppleOauthLinkDev() { + return appleOAuthProperties.getBaseUrl() + + String.format( + APPLE_OAUTH_QUERY_STRING, + appleOAuthProperties.getClientId(), + appleOAuthProperties.getRedirectUrl()); + } + + /** token * */ + public AppleTokenResponse getAppleOAuthToken(String code, String referer) { + return appleOAuthClient.appleAuth( + appleOAuthProperties.getClientId(), + referer + appleOAuthProperties.getWebCallbackUrl(), + code, + this.getClientSecret()); + } + + public AppleTokenResponse getAppleOAuthTokenDev(String code) { + return appleOAuthClient.appleAuth( + appleOAuthProperties.getClientId(), + appleOAuthProperties.getRedirectUrl(), + code, + this.getClientSecret()); + } + + /** idtoken 분석 * */ + public OauthInfo getAppleOAuthInfoByIdToken(String idToken) { + OIDCDecodePayload oidcDecodePayload = this.getOIDCDecodePayload(idToken); + return OauthInfo.builder() + .provider(OauthProvider.APPLE) + .oid(oidcDecodePayload.getSub()) + .build(); + } + + /** oidc decode * */ + public OIDCDecodePayload getOIDCDecodePayload(String token) { + OIDCPublicKeysResponse oidcPublicKeysResponse = appleOIDCClient.getAppleOIDCOpenKeys(); + return oAuthOIDCHelper.getPayloadFromIdToken( + token, + appleOAuthProperties.getBaseUrl(), + appleOAuthProperties.getClientId(), + oidcPublicKeysResponse); + } + + /** apple측 회원 탈퇴 * */ + // public void withdrawAppleOauthUser(String appleOAuthAccessToken) { + // appleOAuthClient.revoke( + // appleOAuthProperties.getClientId(), appleOAuthAccessToken, + // this.getClientSecret()); + // } + + /** client secret 가져오기 * */ + private String getClientSecret() { + return AppleLoginUtil.createClientSecret( + appleOAuthProperties.getTeamId(), + appleOAuthProperties.getClientId(), + appleOAuthProperties.getKeyId(), + appleOAuthProperties.getKeyPath(), + appleOAuthProperties.getBaseUrl()); + } +} diff --git a/Api/src/main/java/allchive/server/api/auth/service/helper/KakaoOauthHelper.java b/Api/src/main/java/allchive/server/api/auth/service/helper/KakaoOauthHelper.java index 559748bd..14fe20ec 100644 --- a/Api/src/main/java/allchive/server/api/auth/service/helper/KakaoOauthHelper.java +++ b/Api/src/main/java/allchive/server/api/auth/service/helper/KakaoOauthHelper.java @@ -34,7 +34,7 @@ public String getKaKaoOauthLink(String referer) { referer + "kakao/callback"); } - public String getKaKaoOauthLinkTest() { + public String getKaKaoOauthLinkDev() { return kakaoOauthProperties.getBaseUrl() + String.format( KAKAO_OAUTH_QUERY_STRING, @@ -47,12 +47,12 @@ public KakaoTokenResponse getKakaoOauthToken(String code, String referer) { // TODO : 프론트 콜백 URL 알아내면 바꾸기 return kakaoOauthClient.kakaoAuth( kakaoOauthProperties.getClientId(), - referer + "/kakao/callback", + referer + "kakao/callback", code, kakaoOauthProperties.getClientSecret()); } - public KakaoTokenResponse getKakaoOauthTokenTest(String code) { + public KakaoTokenResponse getKakaoOauthTokenDev(String code) { return kakaoOauthClient.kakaoAuth( kakaoOauthProperties.getClientId(), kakaoOauthProperties.getRedirectUrl(), @@ -61,7 +61,7 @@ public KakaoTokenResponse getKakaoOauthTokenTest(String code) { } /** idtoken 분석 * */ - public OauthInfo getOauthInfoByIdToken(String idToken) { + public OauthInfo getKakaoOauthInfoByIdToken(String idToken) { OIDCDecodePayload oidcDecodePayload = getOIDCDecodePayload(idToken); return OauthInfo.of(OauthProvider.KAKAO, oidcDecodePayload.getSub()); } @@ -77,7 +77,7 @@ public OIDCDecodePayload getOIDCDecodePayload(String token) { } /** kakao측 회원 탈퇴 * */ - public void withdrawOauthUser(String oid) { + public void withdrawKakaoOauthUser(String oid) { String kakaoAdminKey = kakaoOauthProperties.getAdminKey(); KakaoUnlinkTarget unlinkKaKaoTarget = KakaoUnlinkTarget.from(oid); String header = "KakaoAK " + kakaoAdminKey; diff --git a/Core/src/main/java/allchive/server/core/common/CoreConfigurationPropertiesConfig.java b/Core/src/main/java/allchive/server/core/common/CoreConfigurationPropertiesConfig.java index 5964118d..9a99ce02 100644 --- a/Core/src/main/java/allchive/server/core/common/CoreConfigurationPropertiesConfig.java +++ b/Core/src/main/java/allchive/server/core/common/CoreConfigurationPropertiesConfig.java @@ -2,10 +2,15 @@ import allchive.server.core.jwt.JwtProperties; +import allchive.server.core.properties.AppleOAuthProperties; import allchive.server.core.properties.KakaoOAuthProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Configuration; -@EnableConfigurationProperties({JwtProperties.class, KakaoOAuthProperties.class}) +@EnableConfigurationProperties({ + JwtProperties.class, + KakaoOAuthProperties.class, + AppleOAuthProperties.class +}) @Configuration public class CoreConfigurationPropertiesConfig {} diff --git a/Core/src/main/java/allchive/server/core/consts/AllchiveConst.java b/Core/src/main/java/allchive/server/core/consts/AllchiveConst.java index fc8a9a18..9b3973d2 100644 --- a/Core/src/main/java/allchive/server/core/consts/AllchiveConst.java +++ b/Core/src/main/java/allchive/server/core/consts/AllchiveConst.java @@ -26,6 +26,9 @@ public class AllchiveConst { public static final String KAKAO_OAUTH_QUERY_STRING = "/oauth/authorize?client_id=%s&redirect_uri=%s&response_type=code"; + public static final String APPLE_OAUTH_QUERY_STRING = + "/auth/authorize?client_id=%s&redirect_uri=%s&response_type=code"; + public static final String STAGING_ASSET_URL = "https://asset.staging.allchive.co.kr/"; public static final String PROD_ASSET_URL = "https://asset.allchive.co.kr/"; public static final String S3_ASSET_URL = diff --git a/Core/src/main/java/allchive/server/core/properties/AppleOAuthProperties.java b/Core/src/main/java/allchive/server/core/properties/AppleOAuthProperties.java new file mode 100644 index 00000000..5452eeff --- /dev/null +++ b/Core/src/main/java/allchive/server/core/properties/AppleOAuthProperties.java @@ -0,0 +1,24 @@ +package allchive.server.core.properties; + + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.ConstructorBinding; + +@Getter +@AllArgsConstructor +@ConstructorBinding +@ConfigurationProperties("oauth.apple") +public class AppleOAuthProperties { + private String baseUrl; + private String clientId; + private String appClientId; + private String keyId; + private String redirectUrl; + private String teamId; + private String scope; + private String keyPath; + private String webCallbackUrl; + private String appstoreIssuer; +} diff --git a/Core/src/main/resources/application-core.yml b/Core/src/main/resources/application-core.yml index 355a7d42..443a8af9 100644 --- a/Core/src/main/resources/application-core.yml +++ b/Core/src/main/resources/application-core.yml @@ -13,4 +13,16 @@ oauth: client-secret: ${KAKAO_SECRET} redirect-url: ${KAKAO_REDIRECT} app-id: ${KAKAO_APP_ID} - admin-key: ${KAKAO_ADMIN_KEY} \ No newline at end of file + admin-key: ${KAKAO_ADMIN_KEY} + + apple: + baseUrl: ${APPLE_BASE_URL} + clientId: ${APPLE_CLIENT} + appClientId: ${APPLE_APP_CLIENT_ID} + keyId: ${APPLE_KEY_ID} + redirectUrl: ${APPLE_REDIRECT} + teamId: ${APPLE_TEAM_ID} + scope: ${APPLE_SCOPE} + keyPath: ${APPLE_KEY_PATH} + webCallbackUrl: ${APPLE_WEB_CALLBACK} + appstoreIssuer: ${APPLE_APPSTORE_ISSUER} \ No newline at end of file diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/domain/enums/OauthProvider.java b/Domain/src/main/java/allchive/server/domain/domains/user/domain/enums/OauthProvider.java index 5a327a6f..307eceb8 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/domain/enums/OauthProvider.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/domain/enums/OauthProvider.java @@ -10,7 +10,8 @@ @Getter @AllArgsConstructor public enum OauthProvider { - KAKAO("KAKAO"); + KAKAO("KAKAO"), + APPLE("APPLE"); @JsonValue private String value; diff --git a/Infrastructure/build.gradle b/Infrastructure/build.gradle index ca0d4e6d..86d0d1eb 100644 --- a/Infrastructure/build.gradle +++ b/Infrastructure/build.gradle @@ -7,6 +7,7 @@ dependencies { api 'io.github.openfeign:feign-httpclient:12.1' api 'org.springframework.cloud:spring-cloud-starter-openfeign:3.1.4' api 'com.amazonaws:aws-java-sdk-s3control:1.12.372' + api 'com.nimbusds:nimbus-jose-jwt:3.10' testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.2' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.2' diff --git a/Infrastructure/src/main/java/allchive/server/infrastructure/feign/config/OpenFeignConfig.java b/Infrastructure/src/main/java/allchive/server/infrastructure/feign/config/OpenFeignConfig.java index e5ed9cbf..0313e290 100644 --- a/Infrastructure/src/main/java/allchive/server/infrastructure/feign/config/OpenFeignConfig.java +++ b/Infrastructure/src/main/java/allchive/server/infrastructure/feign/config/OpenFeignConfig.java @@ -1,7 +1,7 @@ package allchive.server.infrastructure.feign.config; -import allchive.server.infrastructure.oauth.kakao.BaseFeignClientClass; +import allchive.server.infrastructure.oauth.BaseFeignClientClass; import feign.Logger; import feign.Retryer; import java.util.concurrent.TimeUnit; diff --git a/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/BaseFeignClientClass.java b/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/BaseFeignClientClass.java new file mode 100644 index 00000000..d6ec8cc7 --- /dev/null +++ b/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/BaseFeignClientClass.java @@ -0,0 +1,3 @@ +package allchive.server.infrastructure.oauth; + +public interface BaseFeignClientClass {} diff --git a/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/apple/client/AppleOAuthClient.java b/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/apple/client/AppleOAuthClient.java new file mode 100644 index 00000000..8021d124 --- /dev/null +++ b/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/apple/client/AppleOAuthClient.java @@ -0,0 +1,27 @@ +package allchive.server.infrastructure.oauth.apple.client; + + +import allchive.server.infrastructure.oauth.apple.config.AppleOAuthConfig; +import allchive.server.infrastructure.oauth.apple.response.AppleTokenResponse; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; + +@FeignClient( + name = "AppleOAuthClient", + url = "https://appleid.apple.com", + configuration = AppleOAuthConfig.class) +public interface AppleOAuthClient { + @PostMapping("/auth/token?grant_type=authorization_code") + AppleTokenResponse appleAuth( + @RequestParam("client_id") String clientId, + @RequestParam("redirect_uri") String redirectUri, + @RequestParam("code") String code, + @RequestParam("client_secret") String clientSecret); + + @PostMapping("/auth/revoke") + void revoke( + @RequestParam("client_id") String clientId, + @RequestParam("client_secret") String clientSecret, + @RequestParam("token") String accessToken); +} diff --git a/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/apple/client/AppleOIDCClient.java b/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/apple/client/AppleOIDCClient.java new file mode 100644 index 00000000..2b86819d --- /dev/null +++ b/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/apple/client/AppleOIDCClient.java @@ -0,0 +1,18 @@ +package allchive.server.infrastructure.oauth.apple.client; + + +import allchive.server.infrastructure.oauth.apple.config.AppleOAuthConfig; +import allchive.server.infrastructure.oauth.kakao.dto.OIDCPublicKeysResponse; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; + +@FeignClient( + name = "AppleOIDCClient", + url = "https://appleid.apple.com", + configuration = AppleOAuthConfig.class) +public interface AppleOIDCClient { + @Cacheable(cacheNames = "appleOIDC", cacheManager = "oidcCacheManager") + @GetMapping("/auth/keys") + OIDCPublicKeysResponse getAppleOIDCOpenKeys(); +} diff --git a/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/apple/config/AppleOAuthConfig.java b/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/apple/config/AppleOAuthConfig.java new file mode 100644 index 00000000..29793d41 --- /dev/null +++ b/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/apple/config/AppleOAuthConfig.java @@ -0,0 +1,23 @@ +package allchive.server.infrastructure.oauth.apple.config; + + +import feign.codec.Encoder; +import feign.codec.ErrorDecoder; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Import; + +@Import(AppleOAuthErrorDecoder.class) +public class AppleOAuthConfig { + + @Bean + @ConditionalOnMissingBean(value = ErrorDecoder.class) + public AppleOAuthErrorDecoder commonFeignErrorDecoder() { + return new AppleOAuthErrorDecoder(); + } + + @Bean + Encoder formEncoder() { + return new feign.form.FormEncoder(); + } +} diff --git a/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/apple/config/AppleOAuthErrorDecoder.java b/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/apple/config/AppleOAuthErrorDecoder.java new file mode 100644 index 00000000..94d4e3ff --- /dev/null +++ b/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/apple/config/AppleOAuthErrorDecoder.java @@ -0,0 +1,32 @@ +package allchive.server.infrastructure.oauth.apple.config; + + +import allchive.server.core.error.BaseDynamicException; +import com.amazonaws.util.IOUtils; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import feign.Response; +import feign.codec.ErrorDecoder; +import java.io.InputStream; +import lombok.SneakyThrows; + +public class AppleOAuthErrorDecoder implements ErrorDecoder { + @Override + @SneakyThrows + public Exception decode(String methodKey, Response response) { + InputStream inputStream = response.body().asInputStream(); + byte[] byteArray = IOUtils.toByteArray(inputStream); + String responseBody = new String(byteArray); + ObjectMapper objectMapper = new ObjectMapper(); + JsonNode jsonNode = objectMapper.readTree(responseBody); + + String error = jsonNode.get("error") == null ? null : jsonNode.get("error").asText(); + String errorDescription = + jsonNode.get("error_description") == null + ? null + : jsonNode.get("error_description").asText(); + + System.out.println(jsonNode); + throw new BaseDynamicException(response.status(), error, errorDescription); + } +} diff --git a/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/apple/config/AppleOAuthErrorResponse.java b/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/apple/config/AppleOAuthErrorResponse.java new file mode 100644 index 00000000..cc9ab1c0 --- /dev/null +++ b/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/apple/config/AppleOAuthErrorResponse.java @@ -0,0 +1,29 @@ +package allchive.server.infrastructure.oauth.apple.config; + + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import feign.Response; +import java.io.IOException; +import java.io.InputStream; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +public class AppleOAuthErrorResponse { + private String error; + private String errorCode; + private String errorDescription; + + public static AppleOAuthErrorResponse from(Response response) { + try (InputStream bodyIs = response.body().asInputStream()) { + ObjectMapper mapper = new ObjectMapper(); + return mapper.readValue(bodyIs, AppleOAuthErrorResponse.class); + } catch (IOException e) { + return null; // + } + } +} diff --git a/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/apple/helper/AppleLoginUtil.java b/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/apple/helper/AppleLoginUtil.java new file mode 100644 index 00000000..e55b74e1 --- /dev/null +++ b/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/apple/helper/AppleLoginUtil.java @@ -0,0 +1,83 @@ +package allchive.server.infrastructure.oauth.apple.helper; + + +import com.nimbusds.jose.JOSEException; +import com.nimbusds.jose.JWSAlgorithm; +import com.nimbusds.jose.JWSHeader; +import com.nimbusds.jose.JWSSigner; +import com.nimbusds.jose.crypto.ECDSASigner; +import com.nimbusds.jwt.JWTClaimsSet; +import com.nimbusds.jwt.SignedJWT; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.interfaces.ECPrivateKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.util.Date; +import org.bouncycastle.util.io.pem.PemObject; +import org.bouncycastle.util.io.pem.PemReader; +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.Resource; + +public class AppleLoginUtil { + /** + * client_secret 생성 Apple Document URL ‣ + * https://developer.apple.com/documentation/sign_in_with_apple/generate_and_validate_tokens + * + * @return client_secret(jwt) + */ + public static String createClientSecret( + String teamId, String clientId, String keyId, String keyPath, String authUrl) { + + JWSHeader header = new JWSHeader.Builder(JWSAlgorithm.ES256).keyID(keyId).build(); + JWTClaimsSet claimsSet = new JWTClaimsSet(); + Date now = new Date(); + + claimsSet.setIssuer(teamId); + claimsSet.setIssueTime(now); + claimsSet.setExpirationTime(new Date(now.getTime() + 3600000)); + claimsSet.setAudience(authUrl); + claimsSet.setSubject(clientId); + + SignedJWT jwt = new SignedJWT(header, claimsSet); + + PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(readPrivateKey(keyPath)); + try { + KeyFactory kf = KeyFactory.getInstance("EC"); + ECPrivateKey ecPrivateKey = (ECPrivateKey) kf.generatePrivate(spec); + JWSSigner jwsSigner = new ECDSASigner(ecPrivateKey.getS()); + jwt.sign(jwsSigner); + } catch (JOSEException e) { + e.printStackTrace(); + } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { + throw new RuntimeException(e); + } + + return jwt.serialize(); + } + + /** + * 파일에서 private key 획득 + * + * @return Private Key + */ + private static byte[] readPrivateKey(String keyPath) { + + Resource resource = new ClassPathResource(keyPath); + byte[] content = null; + + try (InputStream keyInputStream = resource.getInputStream(); + InputStreamReader keyReader = new InputStreamReader(keyInputStream); + PemReader pemReader = new PemReader(keyReader)) { + PemObject pemObject = pemReader.readPemObject(); + content = pemObject.getContent(); + } catch (IOException e) { + e.printStackTrace(); + } + + return content; + } +} diff --git a/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/apple/response/AppleTokenResponse.java b/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/apple/response/AppleTokenResponse.java new file mode 100644 index 00000000..90ee72c2 --- /dev/null +++ b/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/apple/response/AppleTokenResponse.java @@ -0,0 +1,16 @@ +package allchive.server.infrastructure.oauth.apple.response; + + +import com.fasterxml.jackson.databind.PropertyNamingStrategies.SnakeCaseStrategy; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +@JsonNaming(SnakeCaseStrategy.class) +public class AppleTokenResponse { + private String accessToken; + private String refreshToken; + private String idToken; +} diff --git a/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/BaseFeignClientClass.java b/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/BaseFeignClientClass.java deleted file mode 100644 index d5546903..00000000 --- a/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/BaseFeignClientClass.java +++ /dev/null @@ -1,3 +0,0 @@ -package allchive.server.infrastructure.oauth.kakao; - -public interface BaseFeignClientClass {} From 027854bb4c65ffbf64709b0a21354297c6e13bbb Mon Sep 17 00:00:00 2001 From: Sanghoon Jeong <67852689+wjdtkdgns@users.noreply.github.com> Date: Fri, 14 Jul 2023 22:56:14 +0900 Subject: [PATCH 30/41] =?UTF-8?q?[feat]=20=ED=9C=B4=EC=A7=80=ED=86=B5=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C,=20=EB=B3=B5=EA=B5=AC=20=EA=B8=B0=EB=8A=A5?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84=20(#36)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [feat] 삭제 항목 복구 기능 추가 #33 * [feat] 휴지통 조회 기능 구현 #33 * [chore] spotless 적용 * [feat] 영구 삭제 기능 구현 #33 * [feat] 휴지통 기한 기능 구현 #33 * [chore] spotless 적용 --- .../server/api/SchedulerPackageLocation.java | 3 + .../service/DeleteArchivingUseCase.java | 2 +- .../server/api/config/SchedulerConfig.java | 12 ++++ .../content/service/CreateContentUseCase.java | 7 +- .../content/service/DeleteContentUseCase.java | 2 +- .../api/image/controller/ImageController.java | 2 +- .../recycle/controller/RecycleController.java | 43 ++++++++++++ .../request/ClearDeletedObjectRequest.java | 16 +++++ .../request/RestoreDeletedObjectRequest.java | 16 +++++ .../dto/response/DeletedObjectResponse.java | 26 +++++++ .../recycle/model/mapper/RecycleMapper.java | 32 +++++++++ .../service/ClearDeletedObjectUseCase.java | 57 +++++++++++++++ .../service/ClearOldDeletedObjectUseCase.java | 69 +++++++++++++++++++ .../service/GetDeletedObjectUseCase.java | 50 ++++++++++++++ .../service/RestoreDeletedObjectUseCase.java | 37 ++++++++++ .../api/tag/service/DeleteTagUseCase.java | 6 +- .../archiving/adaptor/ArchivingAdaptor.java | 16 +++++ .../domains/archiving/domain/Archiving.java | 4 ++ .../repository/ArchivingRepository.java | 4 ++ .../service/ArchivingDomainService.java | 13 +++- .../validator/ArchivingValidator.java | 20 ++++++ .../content/adaptor/ContentAdaptor.java | 17 +++++ .../adaptor/ContentTagGroupAdaptor.java | 4 ++ .../domains/content/domain/Content.java | 4 ++ .../repository/ContentCustomRepository.java | 3 + .../ContentCustomRepositoryImpl.java | 13 ++++ .../content/repository/ContentRepository.java | 5 +- .../repository/ContentTagGroupRepository.java | 2 + .../content/service/ContentDomainService.java | 13 +++- ...java => ContentTagGroupDomainService.java} | 9 ++- .../content/validator/ContentValidator.java | 28 +++++++- .../recycle/adaptor/RecycleAdaptor.java | 20 ++++++ .../recycle/exception/RecycleErrorCode.java | 8 ++- .../RecycleArchivingNotFoundException.java | 14 ++++ .../RecycleContentNotFoundException.java | 14 ++++ .../repository/RecycleCustomRepository.java | 10 +++ .../RecycleCustomRepositoryImpl.java | 35 ++++++++++ .../recycle/repository/RecycleRepository.java | 8 ++- .../recycle/service/RecycleDomainService.java | 13 ++++ .../recycle/validator/RecycleValidator.java | 32 ++++++++- .../domain/domains/report/domain/Report.java | 14 ++++ .../domains/report/domain/enums/.gitkeep | 0 .../report/domain/enums/ReportType.java | 26 +++++++ .../report/domain/enums/ReportedType.java | 26 +++++++ ...tService.java => ReportDomainService.java} | 4 +- .../domains/user/adaptor/ScrapAdaptor.java | 4 ++ .../user/repository/ScrapRepository.java | 2 + .../user/service/ScrapDomainService.java | 5 ++ 48 files changed, 748 insertions(+), 22 deletions(-) create mode 100644 Api/src/main/java/allchive/server/api/SchedulerPackageLocation.java create mode 100644 Api/src/main/java/allchive/server/api/config/SchedulerConfig.java create mode 100644 Api/src/main/java/allchive/server/api/recycle/controller/RecycleController.java create mode 100644 Api/src/main/java/allchive/server/api/recycle/model/dto/request/ClearDeletedObjectRequest.java create mode 100644 Api/src/main/java/allchive/server/api/recycle/model/dto/request/RestoreDeletedObjectRequest.java create mode 100644 Api/src/main/java/allchive/server/api/recycle/model/dto/response/DeletedObjectResponse.java create mode 100644 Api/src/main/java/allchive/server/api/recycle/service/ClearDeletedObjectUseCase.java create mode 100644 Api/src/main/java/allchive/server/api/recycle/service/ClearOldDeletedObjectUseCase.java create mode 100644 Api/src/main/java/allchive/server/api/recycle/service/GetDeletedObjectUseCase.java create mode 100644 Api/src/main/java/allchive/server/api/recycle/service/RestoreDeletedObjectUseCase.java rename Domain/src/main/java/allchive/server/domain/domains/content/service/{ContentTagGroupdDomainService.java => ContentTagGroupDomainService.java} (71%) create mode 100644 Domain/src/main/java/allchive/server/domain/domains/recycle/exception/exceptions/RecycleArchivingNotFoundException.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/recycle/exception/exceptions/RecycleContentNotFoundException.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/recycle/repository/RecycleCustomRepository.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/recycle/repository/RecycleCustomRepositoryImpl.java delete mode 100644 Domain/src/main/java/allchive/server/domain/domains/report/domain/enums/.gitkeep create mode 100644 Domain/src/main/java/allchive/server/domain/domains/report/domain/enums/ReportType.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/report/domain/enums/ReportedType.java rename Domain/src/main/java/allchive/server/domain/domains/report/service/{ReportService.java => ReportDomainService.java} (55%) diff --git a/Api/src/main/java/allchive/server/api/SchedulerPackageLocation.java b/Api/src/main/java/allchive/server/api/SchedulerPackageLocation.java new file mode 100644 index 00000000..e57515ef --- /dev/null +++ b/Api/src/main/java/allchive/server/api/SchedulerPackageLocation.java @@ -0,0 +1,3 @@ +package allchive.server.api; + +public interface SchedulerPackageLocation {} diff --git a/Api/src/main/java/allchive/server/api/archiving/service/DeleteArchivingUseCase.java b/Api/src/main/java/allchive/server/api/archiving/service/DeleteArchivingUseCase.java index 7c8de3c0..b2117a79 100644 --- a/Api/src/main/java/allchive/server/api/archiving/service/DeleteArchivingUseCase.java +++ b/Api/src/main/java/allchive/server/api/archiving/service/DeleteArchivingUseCase.java @@ -24,7 +24,7 @@ public class DeleteArchivingUseCase { public void execute(Long archivingId) { Long userId = SecurityUtil.getCurrentUserId(); archivingValidator.verifyUser(userId, archivingId); - archivingDomainService.deleteById(archivingId); + archivingDomainService.softDeleteById(archivingId); Recycle recycle = recycleMapper.toArchivingRecycleEntity(userId, archivingId, RecycleType.ARCHIVING); recycleDomainService.save(recycle); diff --git a/Api/src/main/java/allchive/server/api/config/SchedulerConfig.java b/Api/src/main/java/allchive/server/api/config/SchedulerConfig.java new file mode 100644 index 00000000..a2b2c71b --- /dev/null +++ b/Api/src/main/java/allchive/server/api/config/SchedulerConfig.java @@ -0,0 +1,12 @@ +package allchive.server.api.config; + + +import allchive.server.api.SchedulerPackageLocation; +import org.springframework.boot.autoconfigure.domain.EntityScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.EnableScheduling; + +@Configuration +@EnableScheduling +@EntityScan(basePackageClasses = SchedulerPackageLocation.class) +public class SchedulerConfig {} diff --git a/Api/src/main/java/allchive/server/api/content/service/CreateContentUseCase.java b/Api/src/main/java/allchive/server/api/content/service/CreateContentUseCase.java index 488a0508..1b758454 100644 --- a/Api/src/main/java/allchive/server/api/content/service/CreateContentUseCase.java +++ b/Api/src/main/java/allchive/server/api/content/service/CreateContentUseCase.java @@ -11,7 +11,7 @@ import allchive.server.domain.domains.content.domain.ContentTagGroup; import allchive.server.domain.domains.content.domain.Tag; import allchive.server.domain.domains.content.service.ContentDomainService; -import allchive.server.domain.domains.content.service.ContentTagGroupdDomainService; +import allchive.server.domain.domains.content.service.ContentTagGroupDomainService; import allchive.server.domain.domains.content.validator.TagValidator; import java.util.List; import lombok.RequiredArgsConstructor; @@ -25,9 +25,8 @@ public class CreateContentUseCase { private final ContentDomainService contentDomainService; private final TagValidator tagValidator; private final TagAdaptor tagAdaptor; - private final ContentTagGroupdDomainService contentTagGroupdDomainService; + private final ContentTagGroupDomainService contentTagGroupDomainService; - // TODO : tag 만들면 연결 @Transactional public void execute(CreateContentRequest request) { archivingValidator.validateExistArchiving(request.getArchivingId()); @@ -38,7 +37,7 @@ public void execute(CreateContentRequest request) { Content content = contentMapper.toEntity(request); List<ContentTagGroup> contentTagGroupList = contentMapper.toContentTagGroupEntityList(content, tags); - contentTagGroupdDomainService.saveAll(contentTagGroupList); + contentTagGroupDomainService.saveAll(contentTagGroupList); contentDomainService.save(content); } } diff --git a/Api/src/main/java/allchive/server/api/content/service/DeleteContentUseCase.java b/Api/src/main/java/allchive/server/api/content/service/DeleteContentUseCase.java index 56cb974e..a09c2ec6 100644 --- a/Api/src/main/java/allchive/server/api/content/service/DeleteContentUseCase.java +++ b/Api/src/main/java/allchive/server/api/content/service/DeleteContentUseCase.java @@ -28,7 +28,7 @@ public void execute(Long contentId) { Long userId = SecurityUtil.getCurrentUserId(); Content content = contentAdaptor.findById(contentId); archivingValidator.validateArchivingUser(content.getArchivingId(), userId); - contentDomainService.deleteById(contentId); + contentDomainService.softDeleteById(contentId); Recycle recycle = recycleMapper.toContentRecycleEntity(userId, contentId, RecycleType.CONTENT); recycleDomainService.save(recycle); diff --git a/Api/src/main/java/allchive/server/api/image/controller/ImageController.java b/Api/src/main/java/allchive/server/api/image/controller/ImageController.java index 02a82108..8077a4d4 100644 --- a/Api/src/main/java/allchive/server/api/image/controller/ImageController.java +++ b/Api/src/main/java/allchive/server/api/image/controller/ImageController.java @@ -15,7 +15,7 @@ @RequestMapping("/") @RequiredArgsConstructor @Slf4j -@Tag(name = "6. [image]") +@Tag(name = "a. [image]") public class ImageController { private final S3PresignedUrlService s3PresignedUrlService; diff --git a/Api/src/main/java/allchive/server/api/recycle/controller/RecycleController.java b/Api/src/main/java/allchive/server/api/recycle/controller/RecycleController.java new file mode 100644 index 00000000..053f8a12 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/recycle/controller/RecycleController.java @@ -0,0 +1,43 @@ +package allchive.server.api.recycle.controller; + + +import allchive.server.api.recycle.model.dto.request.ClearDeletedObjectRequest; +import allchive.server.api.recycle.model.dto.request.RestoreDeletedObjectRequest; +import allchive.server.api.recycle.model.dto.response.DeletedObjectResponse; +import allchive.server.api.recycle.service.ClearDeletedObjectUseCase; +import allchive.server.api.recycle.service.GetDeletedObjectUseCase; +import allchive.server.api.recycle.service.RestoreDeletedObjectUseCase; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/recycles") +@RequiredArgsConstructor +@Slf4j +@Tag(name = "6. [recycle]") +public class RecycleController { + private final RestoreDeletedObjectUseCase restoreDeletedObjectUseCase; + private final GetDeletedObjectUseCase getDeletedObjectUseCase; + private final ClearDeletedObjectUseCase clearDeletedObjectUseCase; + + @Operation(summary = "삭제된 아카이빙, 컨텐츠를 복구합니다.") + @PatchMapping() + public void restoreDeletedObject(@RequestBody RestoreDeletedObjectRequest request) { + restoreDeletedObjectUseCase.execute(request); + } + + @Operation(summary = "삭제된 아카이빙, 컨텐츠를 가져옵니다.") + @GetMapping() + public DeletedObjectResponse getDeletedObject() { + return getDeletedObjectUseCase.execute(); + } + + @Operation(summary = "삭제된 아카이빙, 컨텐츠를 영구적으로 삭제합니다.") + @DeleteMapping() + public void clearDeletedObject(@RequestBody ClearDeletedObjectRequest request) { + clearDeletedObjectUseCase.execute(request); + } +} diff --git a/Api/src/main/java/allchive/server/api/recycle/model/dto/request/ClearDeletedObjectRequest.java b/Api/src/main/java/allchive/server/api/recycle/model/dto/request/ClearDeletedObjectRequest.java new file mode 100644 index 00000000..eca4c645 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/recycle/model/dto/request/ClearDeletedObjectRequest.java @@ -0,0 +1,16 @@ +package allchive.server.api.recycle.model.dto.request; + + +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Schema; +import java.util.List; +import lombok.Getter; + +@Getter +public class ClearDeletedObjectRequest { + @ArraySchema(schema = @Schema(description = "영구 삭제할 아카이빙 id", type = "int", defaultValue = "1")) + private List<Long> archivingIds; + + @ArraySchema(schema = @Schema(description = "영구 삭제할 컨텐츠 id", type = "int", defaultValue = "1")) + private List<Long> contentIds; +} diff --git a/Api/src/main/java/allchive/server/api/recycle/model/dto/request/RestoreDeletedObjectRequest.java b/Api/src/main/java/allchive/server/api/recycle/model/dto/request/RestoreDeletedObjectRequest.java new file mode 100644 index 00000000..609bdb32 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/recycle/model/dto/request/RestoreDeletedObjectRequest.java @@ -0,0 +1,16 @@ +package allchive.server.api.recycle.model.dto.request; + + +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Schema; +import java.util.List; +import lombok.Getter; + +@Getter +public class RestoreDeletedObjectRequest { + @ArraySchema(schema = @Schema(description = "복구할 아카이빙 id", type = "int", defaultValue = "1")) + private List<Long> archivingIds; + + @ArraySchema(schema = @Schema(description = "복구할 컨텐츠 id", type = "int", defaultValue = "1")) + private List<Long> contentIds; +} diff --git a/Api/src/main/java/allchive/server/api/recycle/model/dto/response/DeletedObjectResponse.java b/Api/src/main/java/allchive/server/api/recycle/model/dto/response/DeletedObjectResponse.java new file mode 100644 index 00000000..d5635cc0 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/recycle/model/dto/response/DeletedObjectResponse.java @@ -0,0 +1,26 @@ +package allchive.server.api.recycle.model.dto.response; + + +import allchive.server.api.archiving.model.dto.response.ArchivingResponse; +import allchive.server.api.content.model.dto.response.ContentResponse; +import java.util.List; +import lombok.Builder; +import lombok.Getter; + +@Getter +public class DeletedObjectResponse { + private List<ContentResponse> contents; + private List<ArchivingResponse> archivings; + + @Builder + private DeletedObjectResponse( + List<ContentResponse> contents, List<ArchivingResponse> archivings) { + this.contents = contents; + this.archivings = archivings; + } + + public static DeletedObjectResponse of( + List<ArchivingResponse> archivings, List<ContentResponse> contents) { + return DeletedObjectResponse.builder().archivings(archivings).contents(contents).build(); + } +} diff --git a/Api/src/main/java/allchive/server/api/recycle/model/mapper/RecycleMapper.java b/Api/src/main/java/allchive/server/api/recycle/model/mapper/RecycleMapper.java index c35534d6..a145693d 100644 --- a/Api/src/main/java/allchive/server/api/recycle/model/mapper/RecycleMapper.java +++ b/Api/src/main/java/allchive/server/api/recycle/model/mapper/RecycleMapper.java @@ -1,12 +1,24 @@ package allchive.server.api.recycle.model.mapper; +import allchive.server.api.archiving.model.dto.response.ArchivingResponse; +import allchive.server.api.content.model.dto.response.ContentResponse; +import allchive.server.api.content.model.mapper.ContentMapper; +import allchive.server.api.recycle.model.dto.response.DeletedObjectResponse; import allchive.server.core.annotation.Mapper; +import allchive.server.domain.domains.archiving.domain.Archiving; +import allchive.server.domain.domains.content.domain.Content; +import allchive.server.domain.domains.content.domain.ContentTagGroup; import allchive.server.domain.domains.recycle.domain.Recycle; import allchive.server.domain.domains.recycle.domain.enums.RecycleType; +import java.util.List; +import lombok.RequiredArgsConstructor; @Mapper +@RequiredArgsConstructor public class RecycleMapper { + private final ContentMapper contentMapper; + public Recycle toContentRecycleEntity(Long userId, Long contentId, RecycleType type) { return Recycle.of(type, contentId, null, userId); } @@ -14,4 +26,24 @@ public Recycle toContentRecycleEntity(Long userId, Long contentId, RecycleType t public Recycle toArchivingRecycleEntity(Long userId, Long archivingId, RecycleType type) { return Recycle.of(type, null, archivingId, userId); } + + public DeletedObjectResponse toDeletedObjectResponse( + List<Archiving> archivingList, + Long userId, + List<Content> contents, + List<ContentTagGroup> contentTagGroups) { + List<ArchivingResponse> archivingResponses = + archivingList.stream() + .map( + archiving -> + ArchivingResponse.of( + archiving, + archiving.getPinUserId().contains(userId))) + .toList(); + List<ContentResponse> contentResponses = + contents.stream() + .map(content -> contentMapper.toContentResponse(content, contentTagGroups)) + .toList(); + return DeletedObjectResponse.of(archivingResponses, contentResponses); + } } diff --git a/Api/src/main/java/allchive/server/api/recycle/service/ClearDeletedObjectUseCase.java b/Api/src/main/java/allchive/server/api/recycle/service/ClearDeletedObjectUseCase.java new file mode 100644 index 00000000..453ecc90 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/recycle/service/ClearDeletedObjectUseCase.java @@ -0,0 +1,57 @@ +package allchive.server.api.recycle.service; + + +import allchive.server.api.config.security.SecurityUtil; +import allchive.server.api.recycle.model.dto.request.ClearDeletedObjectRequest; +import allchive.server.core.annotation.UseCase; +import allchive.server.domain.domains.archiving.service.ArchivingDomainService; +import allchive.server.domain.domains.archiving.validator.ArchivingValidator; +import allchive.server.domain.domains.content.adaptor.ContentAdaptor; +import allchive.server.domain.domains.content.domain.Content; +import allchive.server.domain.domains.content.service.ContentDomainService; +import allchive.server.domain.domains.content.service.ContentTagGroupDomainService; +import allchive.server.domain.domains.content.validator.ContentValidator; +import allchive.server.domain.domains.recycle.service.RecycleDomainService; +import allchive.server.domain.domains.recycle.validator.RecycleValidator; +import allchive.server.domain.domains.user.service.ScrapDomainService; +import java.util.ArrayList; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.transaction.annotation.Transactional; + +@UseCase +@RequiredArgsConstructor +public class ClearDeletedObjectUseCase { + private final RecycleValidator recycleValidator; + private final ArchivingValidator archivingValidator; + private final ContentValidator contentValidator; + private final ContentAdaptor contentAdaptor; + private final ContentTagGroupDomainService contentTagGroupDomainService; + private final ScrapDomainService scrapDomainService; + private final ArchivingDomainService archivingDomainService; + private final ContentDomainService contentDomainService; + private final RecycleDomainService recycleDomainService; + + // TODO: report 지우기 + @Transactional + public void execute(ClearDeletedObjectRequest request) { + Long userId = SecurityUtil.getCurrentUserId(); + recycleValidator.validateExist(request.getArchivingIds(), request.getContentIds(), userId); + archivingValidator.verifyUserInIdList(userId, request.getArchivingIds()); + contentValidator.verifyUserInIdList(userId, request.getContentIds()); + List<Content> contents = contentAdaptor.findAllByArchivingIds(request.getArchivingIds()); + List<Long> contentsId = contents.stream().map(Content::getId).toList(); + if (!request.getContentIds().isEmpty()) { + if (contentsId.isEmpty()) { + contentsId = new ArrayList<>(); + } + contentsId.addAll(request.getContentIds()); + } + scrapDomainService.deleteAllByArchivingIdIn(request.getArchivingIds()); + contentTagGroupDomainService.deleteByContentIn(contents); + contentDomainService.deleteAllById(contentsId); + archivingDomainService.deleteAllById(request.getArchivingIds()); + recycleDomainService.deleteAllByUserIdAndArchivingIdOrUserIdAndContentId( + request.getArchivingIds(), request.getContentIds(), userId); + } +} diff --git a/Api/src/main/java/allchive/server/api/recycle/service/ClearOldDeletedObjectUseCase.java b/Api/src/main/java/allchive/server/api/recycle/service/ClearOldDeletedObjectUseCase.java new file mode 100644 index 00000000..07de040e --- /dev/null +++ b/Api/src/main/java/allchive/server/api/recycle/service/ClearOldDeletedObjectUseCase.java @@ -0,0 +1,69 @@ +package allchive.server.api.recycle.service; + + +import allchive.server.core.annotation.UseCase; +import allchive.server.domain.domains.archiving.service.ArchivingDomainService; +import allchive.server.domain.domains.content.adaptor.ContentAdaptor; +import allchive.server.domain.domains.content.domain.Content; +import allchive.server.domain.domains.content.service.ContentDomainService; +import allchive.server.domain.domains.content.service.ContentTagGroupDomainService; +import allchive.server.domain.domains.recycle.adaptor.RecycleAdaptor; +import allchive.server.domain.domains.recycle.domain.Recycle; +import allchive.server.domain.domains.recycle.domain.enums.RecycleType; +import allchive.server.domain.domains.recycle.service.RecycleDomainService; +import allchive.server.domain.domains.user.service.ScrapDomainService; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.transaction.annotation.Transactional; + +@Slf4j +@UseCase +@RequiredArgsConstructor +public class ClearOldDeletedObjectUseCase { + private final RecycleAdaptor recycleAdaptor; + private final ContentAdaptor contentAdaptor; + private final ContentTagGroupDomainService contentTagGroupDomainService; + private final ScrapDomainService scrapDomainService; + private final ArchivingDomainService archivingDomainService; + private final ContentDomainService contentDomainService; + private final RecycleDomainService recycleDomainService; + + /** 삭제 후 30일 지난 항목 제거 스케쥴러 매일 02:30에 수행 */ + @Scheduled(cron = "0 43 22 * * *") + @Transactional + public void executeSchedule() { + log.info("scheduler on"); + LocalDateTime deleteStandard = LocalDateTime.now().minusDays(30); + List<Recycle> recycles = recycleAdaptor.findAllByDeletedAtBefore(deleteStandard); + + List<Long> archivingIds = + recycles.stream() + .filter(recycle -> recycle.getRecycleType().equals(RecycleType.ARCHIVING)) + .map(Recycle::getArchivingId) + .toList(); + List<Long> contentIds = + recycles.stream() + .filter(recycle -> recycle.getRecycleType().equals(RecycleType.CONTENT)) + .map(Recycle::getContentId) + .toList(); + + if (contentIds.isEmpty()) { + contentIds = new ArrayList<>(); + } + List<Content> contents = contentAdaptor.findAllByArchivingIds(archivingIds); + for (Content content : contents) { + contentIds.add(content.getId()); + } + + scrapDomainService.deleteAllByArchivingIdIn(archivingIds); + contentTagGroupDomainService.deleteByContentIn(contents); + contentDomainService.deleteAllById(contentIds); + archivingDomainService.deleteAllById(archivingIds); + recycleDomainService.deleteAll(recycles); + log.info("scheduler off"); + } +} diff --git a/Api/src/main/java/allchive/server/api/recycle/service/GetDeletedObjectUseCase.java b/Api/src/main/java/allchive/server/api/recycle/service/GetDeletedObjectUseCase.java new file mode 100644 index 00000000..7bba3ccc --- /dev/null +++ b/Api/src/main/java/allchive/server/api/recycle/service/GetDeletedObjectUseCase.java @@ -0,0 +1,50 @@ +package allchive.server.api.recycle.service; + + +import allchive.server.api.config.security.SecurityUtil; +import allchive.server.api.recycle.model.dto.response.DeletedObjectResponse; +import allchive.server.api.recycle.model.mapper.RecycleMapper; +import allchive.server.core.annotation.UseCase; +import allchive.server.domain.domains.archiving.adaptor.ArchivingAdaptor; +import allchive.server.domain.domains.archiving.domain.Archiving; +import allchive.server.domain.domains.content.adaptor.ContentAdaptor; +import allchive.server.domain.domains.content.adaptor.ContentTagGroupAdaptor; +import allchive.server.domain.domains.content.domain.Content; +import allchive.server.domain.domains.content.domain.ContentTagGroup; +import allchive.server.domain.domains.recycle.adaptor.RecycleAdaptor; +import allchive.server.domain.domains.recycle.domain.Recycle; +import allchive.server.domain.domains.recycle.domain.enums.RecycleType; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.transaction.annotation.Transactional; + +@UseCase +@RequiredArgsConstructor +public class GetDeletedObjectUseCase { + private final RecycleAdaptor recycleAdaptor; + private final ArchivingAdaptor archivingAdaptor; + private final ContentAdaptor contentAdaptor; + private final RecycleMapper recycleMapper; + private final ContentTagGroupAdaptor contentTagGroupAdaptor; + + @Transactional(readOnly = true) + public DeletedObjectResponse execute() { + Long userId = SecurityUtil.getCurrentUserId(); + List<Recycle> recycles = recycleAdaptor.findAllByUserId(userId); + List<Long> archivingIds = + recycles.stream() + .filter(recycle -> recycle.getRecycleType().equals(RecycleType.ARCHIVING)) + .map(Recycle::getArchivingId) + .toList(); + List<Long> contentIds = + recycles.stream() + .filter(recycle -> recycle.getRecycleType().equals(RecycleType.CONTENT)) + .map(Recycle::getContentId) + .toList(); + List<Archiving> archivings = archivingAdaptor.findAllByIdIn(archivingIds); + List<Content> contents = contentAdaptor.findAllByIdIn(contentIds); + List<ContentTagGroup> contentTagGroups = contentTagGroupAdaptor.queryContentIn(contents); + return recycleMapper.toDeletedObjectResponse( + archivings, userId, contents, contentTagGroups); + } +} diff --git a/Api/src/main/java/allchive/server/api/recycle/service/RestoreDeletedObjectUseCase.java b/Api/src/main/java/allchive/server/api/recycle/service/RestoreDeletedObjectUseCase.java new file mode 100644 index 00000000..aa77aa15 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/recycle/service/RestoreDeletedObjectUseCase.java @@ -0,0 +1,37 @@ +package allchive.server.api.recycle.service; + + +import allchive.server.api.config.security.SecurityUtil; +import allchive.server.api.recycle.model.dto.request.RestoreDeletedObjectRequest; +import allchive.server.core.annotation.UseCase; +import allchive.server.domain.domains.archiving.service.ArchivingDomainService; +import allchive.server.domain.domains.archiving.validator.ArchivingValidator; +import allchive.server.domain.domains.content.service.ContentDomainService; +import allchive.server.domain.domains.content.validator.ContentValidator; +import allchive.server.domain.domains.recycle.service.RecycleDomainService; +import allchive.server.domain.domains.recycle.validator.RecycleValidator; +import lombok.RequiredArgsConstructor; +import org.springframework.transaction.annotation.Transactional; + +@UseCase +@RequiredArgsConstructor +public class RestoreDeletedObjectUseCase { + private final RecycleValidator recycleValidator; + private final ArchivingValidator archivingValidator; + private final ContentValidator contentValidator; + private final ArchivingDomainService archivingDomainService; + private final ContentDomainService contentDomainService; + private final RecycleDomainService recycleDomainService; + + @Transactional + public void execute(RestoreDeletedObjectRequest request) { + Long userId = SecurityUtil.getCurrentUserId(); + recycleValidator.validateExist(request.getArchivingIds(), request.getContentIds(), userId); + archivingValidator.validateExistInIdList(request.getArchivingIds()); + contentValidator.validateExistInIdList(request.getContentIds()); + archivingDomainService.restoreInIdList(request.getArchivingIds()); + contentDomainService.restoreInIdList(request.getContentIds()); + recycleDomainService.deleteAllByUserIdAndArchivingIdOrUserIdAndContentId( + request.getArchivingIds(), request.getContentIds(), userId); + } +} diff --git a/Api/src/main/java/allchive/server/api/tag/service/DeleteTagUseCase.java b/Api/src/main/java/allchive/server/api/tag/service/DeleteTagUseCase.java index a62ca160..a34a8db3 100644 --- a/Api/src/main/java/allchive/server/api/tag/service/DeleteTagUseCase.java +++ b/Api/src/main/java/allchive/server/api/tag/service/DeleteTagUseCase.java @@ -5,7 +5,7 @@ import allchive.server.core.annotation.UseCase; import allchive.server.domain.domains.content.adaptor.TagAdaptor; import allchive.server.domain.domains.content.domain.Tag; -import allchive.server.domain.domains.content.service.ContentTagGroupdDomainService; +import allchive.server.domain.domains.content.service.ContentTagGroupDomainService; import allchive.server.domain.domains.content.service.TagDomainService; import allchive.server.domain.domains.content.validator.TagValidator; import lombok.RequiredArgsConstructor; @@ -16,7 +16,7 @@ public class DeleteTagUseCase { private final TagValidator tagValidator; private final TagAdaptor tagAdaptor; - private final ContentTagGroupdDomainService contentTagGroupdDomainService; + private final ContentTagGroupDomainService contentTagGroupDomainService; private final TagDomainService tagDomainService; @Transactional @@ -24,7 +24,7 @@ public void execute(Long tagId) { Long userId = SecurityUtil.getCurrentUserId(); tagValidator.verifyUser(tagId, userId); Tag tag = tagAdaptor.findById(tagId); - contentTagGroupdDomainService.deleteById(tag); + contentTagGroupDomainService.deleteByTag(tag); tagDomainService.deleteById(tagId); } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/archiving/adaptor/ArchivingAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/archiving/adaptor/ArchivingAdaptor.java index 4fce6e48..c89f22f3 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/archiving/adaptor/ArchivingAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/archiving/adaptor/ArchivingAdaptor.java @@ -64,4 +64,20 @@ public boolean queryArchivingExist(Long archivingId) { public List<Archiving> findAllByUserId(Long userId) { return archivingRepository.findAllByUserId(userId); } + + public List<Archiving> findAllByIdIn(List<Long> archivingIdList) { + return archivingRepository.findAllByIdIn(archivingIdList); + } + + public void saveAll(List<Archiving> archivings) { + archivingRepository.saveAll(archivings); + } + + public List<Archiving> findAllByUserIdAndDeleted(Long userId) { + return archivingRepository.findAllByUserIdAndDeleteStatus(userId, true); + } + + public void deleteAllById(List<Long> archivingIds) { + archivingRepository.deleteAllById(archivingIds); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/archiving/domain/Archiving.java b/Domain/src/main/java/allchive/server/domain/domains/archiving/domain/Archiving.java index 4ce2e25b..57919d98 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/archiving/domain/Archiving.java +++ b/Domain/src/main/java/allchive/server/domain/domains/archiving/domain/Archiving.java @@ -112,4 +112,8 @@ public void deletePinUserId(Long userId) { public void delete() { this.deleteStatus = Boolean.TRUE; } + + public void restore() { + this.deleteStatus = Boolean.FALSE; + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingRepository.java b/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingRepository.java index e69f1bb8..391779ef 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingRepository.java @@ -8,4 +8,8 @@ public interface ArchivingRepository extends JpaRepository<Archiving, Long>, ArchivingCustomRepository { List<Archiving> findAllByUserId(Long userId); + + List<Archiving> findAllByIdIn(List<Long> ids); + + List<Archiving> findAllByUserIdAndDeleteStatus(Long userId, boolean deleteStatus); } diff --git a/Domain/src/main/java/allchive/server/domain/domains/archiving/service/ArchivingDomainService.java b/Domain/src/main/java/allchive/server/domain/domains/archiving/service/ArchivingDomainService.java index 85ddf1c6..5113434b 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/archiving/service/ArchivingDomainService.java +++ b/Domain/src/main/java/allchive/server/domain/domains/archiving/service/ArchivingDomainService.java @@ -5,6 +5,7 @@ import allchive.server.domain.domains.archiving.adaptor.ArchivingAdaptor; import allchive.server.domain.domains.archiving.domain.Archiving; import allchive.server.domain.domains.archiving.domain.enums.Category; +import java.util.List; import lombok.RequiredArgsConstructor; @DomainService @@ -42,9 +43,19 @@ public void updatePin(Long archivingId, Long userId, boolean pin) { archiving.updateScrapCnt(pin ? 1 : -1); } - public void deleteById(Long archivingId) { + public void softDeleteById(Long archivingId) { Archiving archiving = archivingAdaptor.findById(archivingId); archiving.delete(); archivingAdaptor.save(archiving); } + + public void restoreInIdList(List<Long> archivingIds) { + List<Archiving> archivings = archivingAdaptor.findAllByIdIn(archivingIds); + archivings.forEach(Archiving::restore); + archivingAdaptor.saveAll(archivings); + } + + public void deleteAllById(List<Long> archivingIds) { + archivingAdaptor.deleteAllById(archivingIds); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/archiving/validator/ArchivingValidator.java b/Domain/src/main/java/allchive/server/domain/domains/archiving/validator/ArchivingValidator.java index 136cdc5b..cc89741d 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/archiving/validator/ArchivingValidator.java +++ b/Domain/src/main/java/allchive/server/domain/domains/archiving/validator/ArchivingValidator.java @@ -3,9 +3,12 @@ import allchive.server.core.annotation.Validator; import allchive.server.domain.domains.archiving.adaptor.ArchivingAdaptor; +import allchive.server.domain.domains.archiving.domain.Archiving; import allchive.server.domain.domains.archiving.exception.exceptions.AlreadyPinnedArchivingException; import allchive.server.domain.domains.archiving.exception.exceptions.ArchivingNotFoundException; +import allchive.server.domain.domains.archiving.exception.exceptions.NoAuthurityUpdateArchivingException; import allchive.server.domain.domains.archiving.exception.exceptions.NotPinnedArchivingException; +import java.util.List; import lombok.RequiredArgsConstructor; @Validator @@ -46,4 +49,21 @@ public void validateNotPinStatus(Long archivingId, Long userId) { public void validateArchivingUser(Long archivingId, Long userId) { archivingAdaptor.findById(archivingId).validateUser(userId); } + + public void validateExistInIdList(List<Long> archivingIdList) { + List<Archiving> archivingList = archivingAdaptor.findAllByIdIn(archivingIdList); + if (archivingList.size() != archivingIdList.size()) { + throw ArchivingNotFoundException.EXCEPTION; + } + } + + public void verifyUserInIdList(Long userId, List<Long> archivingIds) { + List<Archiving> archivingList = archivingAdaptor.findAllByIdIn(archivingIds); + archivingList.forEach( + archiving -> { + if (!archiving.getUserId().equals(userId)) { + throw NoAuthurityUpdateArchivingException.EXCEPTION; + } + }); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentAdaptor.java index 96a75669..0972bd8f 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentAdaptor.java @@ -5,6 +5,7 @@ import allchive.server.domain.domains.content.domain.Content; import allchive.server.domain.domains.content.exception.exceptions.ContentNotFoundException; import allchive.server.domain.domains.content.repository.ContentRepository; +import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; @@ -31,4 +32,20 @@ public Content findById(Long contentId) { public void deleteById(Long contentId) { contentRepository.deleteById(contentId); } + + public List<Content> findAllByIdIn(List<Long> contentIdList) { + return contentRepository.findAllByIdIn(contentIdList); + } + + public void saveAll(List<Content> contentList) { + contentRepository.saveAll(contentList); + } + + public void deleteAllById(List<Long> contentIds) { + contentRepository.deleteAllById(contentIds); + } + + public List<Content> findAllByArchivingIds(List<Long> archivingIds) { + return contentRepository.queryContentInArchivingIds(archivingIds); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentTagGroupAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentTagGroupAdaptor.java index 5f7947d2..2414717a 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentTagGroupAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentTagGroupAdaptor.java @@ -29,4 +29,8 @@ public void deleteByTag(Tag tag) { public void saveAll(List<ContentTagGroup> contentTagGroupList) { contentTagGroupRepository.saveAll(contentTagGroupList); } + + public void deleteAllByContentIn(List<Content> contents) { + contentTagGroupRepository.deleteAllByContentIn(contents); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/domain/Content.java b/Domain/src/main/java/allchive/server/domain/domains/content/domain/Content.java index 1ad3eccb..8e863066 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/domain/Content.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/domain/Content.java @@ -71,4 +71,8 @@ public static Content of( public void delete() { this.deleteStatus = Boolean.TRUE; } + + public void restore() { + this.deleteStatus = Boolean.FALSE; + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentCustomRepository.java b/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentCustomRepository.java index e43b38ae..1f822736 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentCustomRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentCustomRepository.java @@ -2,9 +2,12 @@ import allchive.server.domain.domains.content.domain.Content; +import java.util.List; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; public interface ContentCustomRepository { Slice<Content> querySliceContentByArchivingId(Long archivingId, Pageable pageable); + + List<Content> queryContentInArchivingIds(List<Long> archivingIds); } diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentCustomRepositoryImpl.java b/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentCustomRepositoryImpl.java index a07937c3..0c3838f3 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentCustomRepositoryImpl.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentCustomRepositoryImpl.java @@ -30,10 +30,23 @@ public Slice<Content> querySliceContentByArchivingId(Long archivingId, Pageable return SliceUtil.toSlice(archivings, pageable); } + @Override + public List<Content> queryContentInArchivingIds(List<Long> archivingIds) { + return queryFactory + .selectFrom(content) + .where(archivingIdIn(archivingIds)) + .orderBy(createdAtDesc()) + .fetch(); + } + private BooleanExpression archivingIdEq(Long archivingId) { return content.archivingId.eq(archivingId); } + private BooleanExpression archivingIdIn(List<Long> archivingIds) { + return content.archivingId.in(archivingIds); + } + private OrderSpecifier<LocalDateTime> createdAtDesc() { return content.createdAt.desc(); } diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentRepository.java b/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentRepository.java index 65eaad9a..79a39685 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentRepository.java @@ -2,6 +2,9 @@ import allchive.server.domain.domains.content.domain.Content; +import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; -public interface ContentRepository extends JpaRepository<Content, Long>, ContentCustomRepository {} +public interface ContentRepository extends JpaRepository<Content, Long>, ContentCustomRepository { + List<Content> findAllByIdIn(List<Long> contentIdList); +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentTagGroupRepository.java b/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentTagGroupRepository.java index f650152f..a6babae0 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentTagGroupRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentTagGroupRepository.java @@ -12,4 +12,6 @@ public interface ContentTagGroupRepository List<ContentTagGroup> findAllByContent(Content content); void deleteByTag(Tag tag); + + void deleteAllByContentIn(List<Content> contents); } diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/service/ContentDomainService.java b/Domain/src/main/java/allchive/server/domain/domains/content/service/ContentDomainService.java index 70b0b16c..7f6c9f56 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/service/ContentDomainService.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/service/ContentDomainService.java @@ -4,6 +4,7 @@ import allchive.server.core.annotation.DomainService; import allchive.server.domain.domains.content.adaptor.ContentAdaptor; import allchive.server.domain.domains.content.domain.Content; +import java.util.List; import lombok.RequiredArgsConstructor; @DomainService @@ -15,9 +16,19 @@ public void save(Content content) { contentAdaptor.save(content); } - public void deleteById(Long contentId) { + public void softDeleteById(Long contentId) { Content content = contentAdaptor.findById(contentId); content.delete(); save(content); } + + public void restoreInIdList(List<Long> contentIds) { + List<Content> contentList = contentAdaptor.findAllByIdIn(contentIds); + contentList.forEach(Content::restore); + contentAdaptor.saveAll(contentList); + } + + public void deleteAllById(List<Long> contentIds) { + contentAdaptor.deleteAllById(contentIds); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/service/ContentTagGroupdDomainService.java b/Domain/src/main/java/allchive/server/domain/domains/content/service/ContentTagGroupDomainService.java similarity index 71% rename from Domain/src/main/java/allchive/server/domain/domains/content/service/ContentTagGroupdDomainService.java rename to Domain/src/main/java/allchive/server/domain/domains/content/service/ContentTagGroupDomainService.java index 09cbd404..9d162118 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/service/ContentTagGroupdDomainService.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/service/ContentTagGroupDomainService.java @@ -3,6 +3,7 @@ import allchive.server.core.annotation.DomainService; import allchive.server.domain.domains.content.adaptor.ContentTagGroupAdaptor; +import allchive.server.domain.domains.content.domain.Content; import allchive.server.domain.domains.content.domain.ContentTagGroup; import allchive.server.domain.domains.content.domain.Tag; import java.util.List; @@ -10,14 +11,18 @@ @DomainService @RequiredArgsConstructor -public class ContentTagGroupdDomainService { +public class ContentTagGroupDomainService { private final ContentTagGroupAdaptor contentTagGroupAdaptor; - public void deleteById(Tag tag) { + public void deleteByTag(Tag tag) { contentTagGroupAdaptor.deleteByTag(tag); } public void saveAll(List<ContentTagGroup> contentTagGroupList) { contentTagGroupAdaptor.saveAll(contentTagGroupList); } + + public void deleteByContentIn(List<Content> contents) { + contentTagGroupAdaptor.deleteAllByContentIn(contents); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/validator/ContentValidator.java b/Domain/src/main/java/allchive/server/domain/domains/content/validator/ContentValidator.java index 93b05fdb..bfe16ad3 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/validator/ContentValidator.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/validator/ContentValidator.java @@ -2,6 +2,32 @@ import allchive.server.core.annotation.Validator; +import allchive.server.domain.domains.archiving.validator.ArchivingValidator; +import allchive.server.domain.domains.content.adaptor.ContentAdaptor; +import allchive.server.domain.domains.content.domain.Content; +import allchive.server.domain.domains.content.exception.exceptions.ContentNotFoundException; +import java.util.List; +import lombok.RequiredArgsConstructor; @Validator -public class ContentValidator {} +@RequiredArgsConstructor +public class ContentValidator { + private final ContentAdaptor contentAdaptor; + private final ArchivingValidator archivingValidator; + + public void validateExistInIdList(List<Long> contentIdList) { + List<Content> contentList = contentAdaptor.findAllByIdIn(contentIdList); + if (contentList.size() != contentIdList.size()) { + throw ContentNotFoundException.EXCEPTION; + } + } + + public void verifyUserInIdList(Long userId, List<Long> contentIds) { + List<Long> archivingIds = + contentAdaptor.findAllByIdIn(contentIds).stream() + .map(Content::getArchivingId) + .distinct() + .toList(); + archivingValidator.verifyUserInIdList(userId, archivingIds); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/recycle/adaptor/RecycleAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/recycle/adaptor/RecycleAdaptor.java index 00f8ad2c..eee460b6 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/recycle/adaptor/RecycleAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/recycle/adaptor/RecycleAdaptor.java @@ -4,6 +4,8 @@ import allchive.server.core.annotation.Adaptor; import allchive.server.domain.domains.recycle.domain.Recycle; import allchive.server.domain.domains.recycle.repository.RecycleRepository; +import java.time.LocalDateTime; +import java.util.List; import lombok.RequiredArgsConstructor; @Adaptor @@ -14,4 +16,22 @@ public class RecycleAdaptor { public void save(Recycle recycle) { recycleRepository.save(recycle); } + + public List<Recycle> queryRecycleByUserIdInArchivingIdListAndContentIdList( + List<Long> archivingIds, List<Long> contentIds, Long userId) { + return recycleRepository.queryRecycleByUserIdInArchivingIdListAndContentIdList( + archivingIds, contentIds, userId); + } + + public void deleteAll(List<Recycle> recycleList) { + recycleRepository.deleteAll(recycleList); + } + + public List<Recycle> findAllByUserId(Long userId) { + return recycleRepository.findAllByUserId(userId); + } + + public List<Recycle> findAllByDeletedAtBefore(LocalDateTime deleteStandard) { + return recycleRepository.findAllByDeletedAtBefore(deleteStandard); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/recycle/exception/RecycleErrorCode.java b/Domain/src/main/java/allchive/server/domain/domains/recycle/exception/RecycleErrorCode.java index d33f7621..16f12274 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/recycle/exception/RecycleErrorCode.java +++ b/Domain/src/main/java/allchive/server/domain/domains/recycle/exception/RecycleErrorCode.java @@ -1,22 +1,24 @@ package allchive.server.domain.domains.recycle.exception; +import static allchive.server.core.consts.AllchiveConst.NOT_FOUND; import allchive.server.core.dto.ErrorReason; import allchive.server.core.error.BaseErrorCode; import lombok.AllArgsConstructor; import lombok.Getter; -import org.springframework.http.HttpStatus; @Getter @AllArgsConstructor public enum RecycleErrorCode implements BaseErrorCode { + RECYCLE_CONTENT_NOT_FOUND(NOT_FOUND, "RECYCLE_404_1", "삭제된 컨텐츠를 찾을 수 없습니다."), + RECYCLE_ARCHIVING_NOT_FOUND(NOT_FOUND, "RECYCLE_404_2", "삭제된 아카이빙을 찾을 수 없습니다."), ; - private HttpStatus status; + private int status; private String code; private String reason; @Override public ErrorReason getErrorReason() { - return ErrorReason.of(status.value(), code, reason); + return ErrorReason.of(status, code, reason); } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/recycle/exception/exceptions/RecycleArchivingNotFoundException.java b/Domain/src/main/java/allchive/server/domain/domains/recycle/exception/exceptions/RecycleArchivingNotFoundException.java new file mode 100644 index 00000000..177b461b --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/recycle/exception/exceptions/RecycleArchivingNotFoundException.java @@ -0,0 +1,14 @@ +package allchive.server.domain.domains.recycle.exception.exceptions; + + +import allchive.server.core.error.BaseErrorException; +import allchive.server.domain.domains.recycle.exception.RecycleErrorCode; + +public class RecycleArchivingNotFoundException extends BaseErrorException { + + public static final BaseErrorException EXCEPTION = new RecycleArchivingNotFoundException(); + + private RecycleArchivingNotFoundException() { + super(RecycleErrorCode.RECYCLE_ARCHIVING_NOT_FOUND); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/recycle/exception/exceptions/RecycleContentNotFoundException.java b/Domain/src/main/java/allchive/server/domain/domains/recycle/exception/exceptions/RecycleContentNotFoundException.java new file mode 100644 index 00000000..0301bce2 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/recycle/exception/exceptions/RecycleContentNotFoundException.java @@ -0,0 +1,14 @@ +package allchive.server.domain.domains.recycle.exception.exceptions; + + +import allchive.server.core.error.BaseErrorException; +import allchive.server.domain.domains.recycle.exception.RecycleErrorCode; + +public class RecycleContentNotFoundException extends BaseErrorException { + + public static final BaseErrorException EXCEPTION = new RecycleContentNotFoundException(); + + private RecycleContentNotFoundException() { + super(RecycleErrorCode.RECYCLE_CONTENT_NOT_FOUND); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/recycle/repository/RecycleCustomRepository.java b/Domain/src/main/java/allchive/server/domain/domains/recycle/repository/RecycleCustomRepository.java new file mode 100644 index 00000000..59d537b8 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/recycle/repository/RecycleCustomRepository.java @@ -0,0 +1,10 @@ +package allchive.server.domain.domains.recycle.repository; + + +import allchive.server.domain.domains.recycle.domain.Recycle; +import java.util.List; + +public interface RecycleCustomRepository { + List<Recycle> queryRecycleByUserIdInArchivingIdListAndContentIdList( + List<Long> archivingIds, List<Long> contentIds, Long userId); +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/recycle/repository/RecycleCustomRepositoryImpl.java b/Domain/src/main/java/allchive/server/domain/domains/recycle/repository/RecycleCustomRepositoryImpl.java new file mode 100644 index 00000000..682ce446 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/recycle/repository/RecycleCustomRepositoryImpl.java @@ -0,0 +1,35 @@ +package allchive.server.domain.domains.recycle.repository; + +import static allchive.server.domain.domains.recycle.domain.QRecycle.recycle; + +import allchive.server.domain.domains.recycle.domain.Recycle; +import com.querydsl.core.types.OrderSpecifier; +import com.querydsl.core.types.dsl.BooleanExpression; +import com.querydsl.jpa.impl.JPAQueryFactory; +import java.time.LocalDateTime; +import java.util.List; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +public class RecycleCustomRepositoryImpl implements RecycleCustomRepository { + private final JPAQueryFactory queryFactory; + + @Override + public List<Recycle> queryRecycleByUserIdInArchivingIdListAndContentIdList( + List<Long> archivingIds, List<Long> contentIds, Long userId) { + return queryFactory + .selectFrom(recycle) + .where(archivingIdInOrContentIdIn(archivingIds, contentIds)) + .orderBy(createdAtDesc()) + .fetch(); + } + + private BooleanExpression archivingIdInOrContentIdIn( + List<Long> archivingIdList, List<Long> contentIdList) { + return recycle.archivingId.in(archivingIdList).or(recycle.contentId.in(contentIdList)); + } + + private OrderSpecifier<LocalDateTime> createdAtDesc() { + return recycle.createdAt.desc(); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/recycle/repository/RecycleRepository.java b/Domain/src/main/java/allchive/server/domain/domains/recycle/repository/RecycleRepository.java index d3768da1..a8dfb3b3 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/recycle/repository/RecycleRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/recycle/repository/RecycleRepository.java @@ -2,6 +2,12 @@ import allchive.server.domain.domains.recycle.domain.Recycle; +import java.time.LocalDateTime; +import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; -public interface RecycleRepository extends JpaRepository<Recycle, Long> {} +public interface RecycleRepository extends JpaRepository<Recycle, Long>, RecycleCustomRepository { + List<Recycle> findAllByUserId(Long userId); + + List<Recycle> findAllByDeletedAtBefore(LocalDateTime time); +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/recycle/service/RecycleDomainService.java b/Domain/src/main/java/allchive/server/domain/domains/recycle/service/RecycleDomainService.java index c6721e98..63531a58 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/recycle/service/RecycleDomainService.java +++ b/Domain/src/main/java/allchive/server/domain/domains/recycle/service/RecycleDomainService.java @@ -4,6 +4,7 @@ import allchive.server.core.annotation.DomainService; import allchive.server.domain.domains.recycle.adaptor.RecycleAdaptor; import allchive.server.domain.domains.recycle.domain.Recycle; +import java.util.List; import lombok.RequiredArgsConstructor; @DomainService @@ -14,4 +15,16 @@ public class RecycleDomainService { public void save(Recycle recycle) { recycleAdaptor.save(recycle); } + + public void deleteAllByUserIdAndArchivingIdOrUserIdAndContentId( + List<Long> archivingIds, List<Long> contentIds, Long userId) { + List<Recycle> recycleList = + recycleAdaptor.queryRecycleByUserIdInArchivingIdListAndContentIdList( + archivingIds, contentIds, userId); + recycleAdaptor.deleteAll(recycleList); + } + + public void deleteAll(List<Recycle> recycles) { + recycleAdaptor.deleteAll(recycles); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/recycle/validator/RecycleValidator.java b/Domain/src/main/java/allchive/server/domain/domains/recycle/validator/RecycleValidator.java index 94824465..18c7e55e 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/recycle/validator/RecycleValidator.java +++ b/Domain/src/main/java/allchive/server/domain/domains/recycle/validator/RecycleValidator.java @@ -2,6 +2,36 @@ import allchive.server.core.annotation.Validator; +import allchive.server.domain.domains.recycle.adaptor.RecycleAdaptor; +import allchive.server.domain.domains.recycle.domain.Recycle; +import allchive.server.domain.domains.recycle.domain.enums.RecycleType; +import allchive.server.domain.domains.recycle.exception.exceptions.RecycleArchivingNotFoundException; +import allchive.server.domain.domains.recycle.exception.exceptions.RecycleContentNotFoundException; +import java.util.List; +import lombok.RequiredArgsConstructor; @Validator -public class RecycleValidator {} +@RequiredArgsConstructor +public class RecycleValidator { + private final RecycleAdaptor recycleAdaptor; + + public void validateExist(List<Long> archivingIds, List<Long> contentIds, Long userId) { + List<Recycle> recycleList = + recycleAdaptor.queryRecycleByUserIdInArchivingIdListAndContentIdList( + archivingIds, contentIds, userId); + Long archivingCnt = + recycleList.stream() + .filter(recycle -> recycle.getRecycleType().equals(RecycleType.ARCHIVING)) + .count(); + if (archivingCnt != archivingIds.size()) { + throw RecycleArchivingNotFoundException.EXCEPTION; + } + Long contentCnt = + recycleList.stream() + .filter(recycle -> recycle.getRecycleType().equals(RecycleType.CONTENT)) + .count(); + if (contentCnt != contentIds.size()) { + throw RecycleContentNotFoundException.EXCEPTION; + } + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/report/domain/Report.java b/Domain/src/main/java/allchive/server/domain/domains/report/domain/Report.java index f86a816d..97dc956d 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/report/domain/Report.java +++ b/Domain/src/main/java/allchive/server/domain/domains/report/domain/Report.java @@ -2,6 +2,8 @@ import allchive.server.domain.common.model.BaseTimeEntity; +import allchive.server.domain.domains.report.domain.enums.ReportType; +import allchive.server.domain.domains.report.domain.enums.ReportedType; import javax.persistence.*; import lombok.AccessLevel; import lombok.Getter; @@ -15,4 +17,16 @@ public class Report extends BaseTimeEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + + @Enumerated(EnumType.STRING) + private ReportType reportType; + + private String reason; + + @Enumerated(EnumType.STRING) + private ReportedType reportedType; + + private Long contentId; + private Long archivingId; + private Long userId; } diff --git a/Domain/src/main/java/allchive/server/domain/domains/report/domain/enums/.gitkeep b/Domain/src/main/java/allchive/server/domain/domains/report/domain/enums/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/Domain/src/main/java/allchive/server/domain/domains/report/domain/enums/ReportType.java b/Domain/src/main/java/allchive/server/domain/domains/report/domain/enums/ReportType.java new file mode 100644 index 00000000..d57c5d1f --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/report/domain/enums/ReportType.java @@ -0,0 +1,26 @@ +package allchive.server.domain.domains.report.domain.enums; + + +import allchive.server.domain.domains.user.domain.enums.OauthProvider; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import java.util.stream.Stream; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum ReportType { + CONTENT("content"), + ARCHIVING("archiving"); + + @JsonValue private String value; + + @JsonCreator + public static OauthProvider parsing(String inputValue) { + return Stream.of(OauthProvider.values()) + .filter(type -> type.getValue().equals(inputValue)) + .findFirst() + .orElse(null); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/report/domain/enums/ReportedType.java b/Domain/src/main/java/allchive/server/domain/domains/report/domain/enums/ReportedType.java new file mode 100644 index 00000000..bdc610d5 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/report/domain/enums/ReportedType.java @@ -0,0 +1,26 @@ +package allchive.server.domain.domains.report.domain.enums; + + +import allchive.server.domain.domains.user.domain.enums.OauthProvider; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import java.util.stream.Stream; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum ReportedType { + CONTENT("content"), + ARCHIVING("archiving"); + + @JsonValue private String value; + + @JsonCreator + public static OauthProvider parsing(String inputValue) { + return Stream.of(OauthProvider.values()) + .filter(type -> type.getValue().equals(inputValue)) + .findFirst() + .orElse(null); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/report/service/ReportService.java b/Domain/src/main/java/allchive/server/domain/domains/report/service/ReportDomainService.java similarity index 55% rename from Domain/src/main/java/allchive/server/domain/domains/report/service/ReportService.java rename to Domain/src/main/java/allchive/server/domain/domains/report/service/ReportDomainService.java index ed7afafd..710f96e7 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/report/service/ReportService.java +++ b/Domain/src/main/java/allchive/server/domain/domains/report/service/ReportDomainService.java @@ -2,6 +2,8 @@ import allchive.server.core.annotation.DomainService; +import lombok.RequiredArgsConstructor; @DomainService -public class ReportService {} +@RequiredArgsConstructor +public class ReportDomainService {} diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/ScrapAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/ScrapAdaptor.java index 35e57626..82a84f9e 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/ScrapAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/ScrapAdaptor.java @@ -29,4 +29,8 @@ public void delete(Scrap scrap) { public void save(Scrap scrap) { scrapRepository.save(scrap); } + + public void deleteAllByArchivingIdIn(List<Long> archivingIds) { + scrapRepository.deleteAllByArchivingIdIn(archivingIds); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/repository/ScrapRepository.java b/Domain/src/main/java/allchive/server/domain/domains/user/repository/ScrapRepository.java index 83e253f2..a6c06431 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/repository/ScrapRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/repository/ScrapRepository.java @@ -11,4 +11,6 @@ public interface ScrapRepository extends JpaRepository<Scrap, Long> { List<Scrap> findAllByUserId(Long userId); Optional<Scrap> findAllByUserAndArchivingId(User user, Long archivingId); + + void deleteAllByArchivingIdIn(List<Long> archivingId); } diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/service/ScrapDomainService.java b/Domain/src/main/java/allchive/server/domain/domains/user/service/ScrapDomainService.java index 6fab2c70..d994bb98 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/service/ScrapDomainService.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/service/ScrapDomainService.java @@ -6,6 +6,7 @@ import allchive.server.domain.domains.user.domain.Scrap; import allchive.server.domain.domains.user.domain.User; import allchive.server.domain.domains.user.exception.exceptions.ScrapNotFoundException; +import java.util.List; import lombok.RequiredArgsConstructor; @DomainService @@ -25,4 +26,8 @@ public void deleteScrapByUserAndArchivingId(User user, Long archivingId) { public void save(Scrap scrap) { scrapAdaptor.save(scrap); } + + public void deleteAllByArchivingIdIn(List<Long> archivingIds) { + scrapAdaptor.deleteAllByArchivingIdIn(archivingIds); + } } From 1b821d08a8d7de14a8a4d912d61d268b07d97659 Mon Sep 17 00:00:00 2001 From: wjdtkdgns <wjdtkdgns10266@gmail.com> Date: Sun, 16 Jul 2023 19:55:30 +0900 Subject: [PATCH 31/41] =?UTF-8?q?[feat]=20=EC=8B=A0=EA=B3=A0=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EA=B5=AC=ED=98=84=20#37?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/UpdateArchivingPinUseCase.java | 2 +- .../service/UpdateArchivingScrapUseCase.java | 2 +- .../content/service/CreateContentUseCase.java | 2 +- .../report/controller/ReportController.java | 28 +++++++++++ .../dto/request/CreateReportRequest.java | 20 ++++++++ .../api/report/model/mapper/ReportMapper.java | 31 +++++++++++++ .../report/service/CreateReportUseCase.java | 37 +++++++++++++++ .../archiving/adaptor/ArchivingAdaptor.java | 2 +- .../repository/ArchivingCustomRepository.java | 2 +- .../ArchivingCustomRepositoryImpl.java | 2 +- .../validator/ArchivingValidator.java | 2 +- .../content/adaptor/ContentAdaptor.java | 4 ++ .../repository/ContentCustomRepository.java | 2 + .../ContentCustomRepositoryImpl.java | 11 +++++ .../content/validator/ContentValidator.java | 6 +++ .../domains/report/adaptor/ReportAdaptor.java | 18 +++++++- .../domain/domains/report/domain/Report.java | 40 ++++++++++++++-- ...{ReportType.java => ReportObjectType.java} | 2 +- .../report/domain/enums/ReportedType.java | 10 +++- .../report/exception/ReportErrorCode.java | 8 ++-- .../exceptions/DuplicatedReportArchiving.java | 14 ++++++ .../exceptions/DuplicatedReportContent.java | 14 ++++++ .../repository/ReportCustomRepository.java | 7 +++ .../ReportCustomRepositoryImpl.java | 46 +++++++++++++++++++ .../report/repository/ReportRepository.java | 2 +- .../report/service/ReportDomainService.java | 10 +++- .../report/validator/ReportValidator.java | 26 ++++++++++- 27 files changed, 330 insertions(+), 20 deletions(-) create mode 100644 Api/src/main/java/allchive/server/api/report/controller/ReportController.java create mode 100644 Api/src/main/java/allchive/server/api/report/model/dto/request/CreateReportRequest.java create mode 100644 Api/src/main/java/allchive/server/api/report/model/mapper/ReportMapper.java create mode 100644 Api/src/main/java/allchive/server/api/report/service/CreateReportUseCase.java rename Domain/src/main/java/allchive/server/domain/domains/report/domain/enums/{ReportType.java => ReportObjectType.java} (95%) create mode 100644 Domain/src/main/java/allchive/server/domain/domains/report/exception/exceptions/DuplicatedReportArchiving.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/report/exception/exceptions/DuplicatedReportContent.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/report/repository/ReportCustomRepository.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/report/repository/ReportCustomRepositoryImpl.java diff --git a/Api/src/main/java/allchive/server/api/archiving/service/UpdateArchivingPinUseCase.java b/Api/src/main/java/allchive/server/api/archiving/service/UpdateArchivingPinUseCase.java index 0ae77af9..a85f5d9e 100644 --- a/Api/src/main/java/allchive/server/api/archiving/service/UpdateArchivingPinUseCase.java +++ b/Api/src/main/java/allchive/server/api/archiving/service/UpdateArchivingPinUseCase.java @@ -17,7 +17,7 @@ public class UpdateArchivingPinUseCase { @Transactional public void execute(Long archivingId, Boolean cancel) { Long userId = SecurityUtil.getCurrentUserId(); - archivingValidator.validateExistArchiving(archivingId); + archivingValidator.validateExistById(archivingId); archivingValidator.validateDeleteStatus(archivingId, userId); if (cancel) { archivingValidator.validateNotPinStatus(archivingId, userId); diff --git a/Api/src/main/java/allchive/server/api/archiving/service/UpdateArchivingScrapUseCase.java b/Api/src/main/java/allchive/server/api/archiving/service/UpdateArchivingScrapUseCase.java index 4ac4c2ef..5e8bcd0d 100644 --- a/Api/src/main/java/allchive/server/api/archiving/service/UpdateArchivingScrapUseCase.java +++ b/Api/src/main/java/allchive/server/api/archiving/service/UpdateArchivingScrapUseCase.java @@ -24,7 +24,7 @@ public class UpdateArchivingScrapUseCase { @Transactional public void execute(Long archivingId, Boolean cancel) { - archivingValidator.validateExistArchiving(archivingId); + archivingValidator.validateExistById(archivingId); Long userId = SecurityUtil.getCurrentUserId(); archivingValidator.validateDeleteStatus(archivingId, userId); User user = userAdaptor.queryUserById(userId); diff --git a/Api/src/main/java/allchive/server/api/content/service/CreateContentUseCase.java b/Api/src/main/java/allchive/server/api/content/service/CreateContentUseCase.java index 1b758454..975c66ff 100644 --- a/Api/src/main/java/allchive/server/api/content/service/CreateContentUseCase.java +++ b/Api/src/main/java/allchive/server/api/content/service/CreateContentUseCase.java @@ -29,7 +29,7 @@ public class CreateContentUseCase { @Transactional public void execute(CreateContentRequest request) { - archivingValidator.validateExistArchiving(request.getArchivingId()); + archivingValidator.validateExistById(request.getArchivingId()); Long userId = SecurityUtil.getCurrentUserId(); archivingValidator.validateArchivingUser(request.getArchivingId(), userId); tagValidator.validateExistTagsAndUser(request.getTagIds(), userId); diff --git a/Api/src/main/java/allchive/server/api/report/controller/ReportController.java b/Api/src/main/java/allchive/server/api/report/controller/ReportController.java new file mode 100644 index 00000000..ac3c0266 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/report/controller/ReportController.java @@ -0,0 +1,28 @@ +package allchive.server.api.report.controller; + + +import allchive.server.api.report.model.dto.request.CreateReportRequest; +import allchive.server.api.report.service.CreateReportUseCase; +import allchive.server.domain.domains.report.domain.enums.ReportObjectType; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/reports") +@RequiredArgsConstructor +@Slf4j +@Tag(name = "7. [report]") +public class ReportController { + private final CreateReportUseCase createReportUseCase; + + @Operation(summary = "아카이빙, 컨텐츠를 신고합니다.") + @PostMapping() + public void createReport( + @RequestParam("type") ReportObjectType type, + @RequestBody CreateReportRequest createReportRequest) { + createReportUseCase.execute(createReportRequest, type); + } +} diff --git a/Api/src/main/java/allchive/server/api/report/model/dto/request/CreateReportRequest.java b/Api/src/main/java/allchive/server/api/report/model/dto/request/CreateReportRequest.java new file mode 100644 index 00000000..de3d048f --- /dev/null +++ b/Api/src/main/java/allchive/server/api/report/model/dto/request/CreateReportRequest.java @@ -0,0 +1,20 @@ +package allchive.server.api.report.model.dto.request; + + +import allchive.server.core.annotation.ValidEnum; +import allchive.server.domain.domains.report.domain.enums.ReportedType; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; + +@Getter +public class CreateReportRequest { + @Schema(defaultValue = "기타기타기타기타기타", description = "사유 ('기타'에만 해당)") + private String reason; + + @Schema(defaultValue = "spam", description = "신고 종류") + @ValidEnum(target = ReportedType.class) + private ReportedType reportedType; + + @Schema(defaultValue = "1", description = "아카이빙, 컨텐츠 고유번호") + private Long id; +} diff --git a/Api/src/main/java/allchive/server/api/report/model/mapper/ReportMapper.java b/Api/src/main/java/allchive/server/api/report/model/mapper/ReportMapper.java new file mode 100644 index 00000000..12a83292 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/report/model/mapper/ReportMapper.java @@ -0,0 +1,31 @@ +package allchive.server.api.report.model.mapper; + + +import allchive.server.api.report.model.dto.request.CreateReportRequest; +import allchive.server.core.annotation.Mapper; +import allchive.server.domain.domains.report.domain.Report; +import allchive.server.domain.domains.report.domain.enums.ReportObjectType; + +@Mapper +public class ReportMapper { + public Report toEntity(CreateReportRequest request, ReportObjectType type, Long userId) { + Report report = null; + switch (type) { + case CONTENT -> report = Report.of( + type, + request.getReason(), + request.getReportedType(), + request.getId(), + null, + userId); + case ARCHIVING -> report = Report.of( + type, + request.getReason(), + request.getReportedType(), + null, + request.getId(), + userId); + } + return report; + } +} diff --git a/Api/src/main/java/allchive/server/api/report/service/CreateReportUseCase.java b/Api/src/main/java/allchive/server/api/report/service/CreateReportUseCase.java new file mode 100644 index 00000000..1f58aa1a --- /dev/null +++ b/Api/src/main/java/allchive/server/api/report/service/CreateReportUseCase.java @@ -0,0 +1,37 @@ +package allchive.server.api.report.service; + + +import allchive.server.api.config.security.SecurityUtil; +import allchive.server.api.report.model.dto.request.CreateReportRequest; +import allchive.server.api.report.model.mapper.ReportMapper; +import allchive.server.core.annotation.UseCase; +import allchive.server.domain.domains.archiving.validator.ArchivingValidator; +import allchive.server.domain.domains.content.validator.ContentValidator; +import allchive.server.domain.domains.report.domain.Report; +import allchive.server.domain.domains.report.domain.enums.ReportObjectType; +import allchive.server.domain.domains.report.service.ReportDomainService; +import allchive.server.domain.domains.report.validator.ReportValidator; +import lombok.RequiredArgsConstructor; +import org.springframework.transaction.annotation.Transactional; + +@UseCase +@RequiredArgsConstructor +public class CreateReportUseCase { + private final ReportValidator reportValidator; + private final ContentValidator contentValidator; + private final ArchivingValidator archivingValidator; + private final ReportMapper reportMapper; + private final ReportDomainService reportDomainService; + + @Transactional + public void execute(CreateReportRequest request, ReportObjectType type) { + Long userId = SecurityUtil.getCurrentUserId(); + reportValidator.validateNotDuplicateReport(userId, request.getId(), type); + switch (type) { + case CONTENT -> contentValidator.validateExistById(request.getId()); + case ARCHIVING -> archivingValidator.validateExistById(request.getId()); + } + Report report = reportMapper.toEntity(request, type, userId); + reportDomainService.save(report); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/archiving/adaptor/ArchivingAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/archiving/adaptor/ArchivingAdaptor.java index c89f22f3..c892093d 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/archiving/adaptor/ArchivingAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/archiving/adaptor/ArchivingAdaptor.java @@ -58,7 +58,7 @@ public List<Archiving> queryArchivingByUserId(Long userId) { } public boolean queryArchivingExist(Long archivingId) { - return archivingRepository.queryArchivingExist(archivingId); + return archivingRepository.queryArchivingExistById(archivingId); } public List<Archiving> findAllByUserId(Long userId) { diff --git a/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingCustomRepository.java b/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingCustomRepository.java index c0f61b24..0385366e 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingCustomRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingCustomRepository.java @@ -18,5 +18,5 @@ Slice<Archiving> querySliceArchivingIn( List<Archiving> queryArchivingByUserId(Long userId); - boolean queryArchivingExist(Long archivingId); + boolean queryArchivingExistById(Long archivingId); } diff --git a/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingCustomRepositoryImpl.java b/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingCustomRepositoryImpl.java index 6c346be7..b7d8ed8f 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingCustomRepositoryImpl.java +++ b/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingCustomRepositoryImpl.java @@ -86,7 +86,7 @@ public List<Archiving> queryArchivingByUserId(Long userId) { } @Override - public boolean queryArchivingExist(Long archivingId) { + public boolean queryArchivingExistById(Long archivingId) { Archiving fetchOne = queryFactory.selectFrom(archiving).where(archivingIdEq(archivingId)).fetchFirst(); return fetchOne != null; diff --git a/Domain/src/main/java/allchive/server/domain/domains/archiving/validator/ArchivingValidator.java b/Domain/src/main/java/allchive/server/domain/domains/archiving/validator/ArchivingValidator.java index cc89741d..0dd6ec08 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/archiving/validator/ArchivingValidator.java +++ b/Domain/src/main/java/allchive/server/domain/domains/archiving/validator/ArchivingValidator.java @@ -28,7 +28,7 @@ public void validateDeleteStatus(Long archivingId, Long userId) { archivingAdaptor.findById(archivingId).validateDeleteStatus(userId); } - public void validateExistArchiving(Long archivingId) { + public void validateExistById(Long archivingId) { if (!archivingAdaptor.queryArchivingExist(archivingId)) { throw ArchivingNotFoundException.EXCEPTION; } diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentAdaptor.java index 0972bd8f..7d789bed 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentAdaptor.java @@ -48,4 +48,8 @@ public void deleteAllById(List<Long> contentIds) { public List<Content> findAllByArchivingIds(List<Long> archivingIds) { return contentRepository.queryContentInArchivingIds(archivingIds); } + + public boolean queryContentExistById(Long contentId) { + return contentRepository.queryContentExistById(contentId); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentCustomRepository.java b/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentCustomRepository.java index 1f822736..34e2fc7a 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentCustomRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentCustomRepository.java @@ -10,4 +10,6 @@ public interface ContentCustomRepository { Slice<Content> querySliceContentByArchivingId(Long archivingId, Pageable pageable); List<Content> queryContentInArchivingIds(List<Long> archivingIds); + + boolean queryContentExistById(Long id); } diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentCustomRepositoryImpl.java b/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentCustomRepositoryImpl.java index 0c3838f3..91ffbc07 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentCustomRepositoryImpl.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentCustomRepositoryImpl.java @@ -39,6 +39,13 @@ public List<Content> queryContentInArchivingIds(List<Long> archivingIds) { .fetch(); } + @Override + public boolean queryContentExistById(Long id) { + Integer fetchOne = + queryFactory.selectOne().from(content).where(idEq(id)).fetchFirst(); // limit 1 + return fetchOne != null; + } + private BooleanExpression archivingIdEq(Long archivingId) { return content.archivingId.eq(archivingId); } @@ -47,6 +54,10 @@ private BooleanExpression archivingIdIn(List<Long> archivingIds) { return content.archivingId.in(archivingIds); } + private BooleanExpression idEq(Long id) { + return content.id.eq(id); + } + private OrderSpecifier<LocalDateTime> createdAtDesc() { return content.createdAt.desc(); } diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/validator/ContentValidator.java b/Domain/src/main/java/allchive/server/domain/domains/content/validator/ContentValidator.java index bfe16ad3..f14abc84 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/validator/ContentValidator.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/validator/ContentValidator.java @@ -30,4 +30,10 @@ public void verifyUserInIdList(Long userId, List<Long> contentIds) { .toList(); archivingValidator.verifyUserInIdList(userId, archivingIds); } + + public void validateExistById(Long contentId) { + if (!contentAdaptor.queryContentExistById(contentId)) { + throw ContentNotFoundException.EXCEPTION; + } + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/report/adaptor/ReportAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/report/adaptor/ReportAdaptor.java index 14ad8dca..95c1d405 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/report/adaptor/ReportAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/report/adaptor/ReportAdaptor.java @@ -2,8 +2,24 @@ import allchive.server.core.annotation.Adaptor; +import allchive.server.domain.domains.report.domain.Report; +import allchive.server.domain.domains.report.repository.ReportRepository; import lombok.RequiredArgsConstructor; @Adaptor @RequiredArgsConstructor -public class ReportAdaptor {} +public class ReportAdaptor { + private final ReportRepository reportRepository; + + public void save(Report report) { + reportRepository.save(report); + } + + public Boolean queryReportExistByUserIdAndContentId(Long userId, Long contentId) { + return reportRepository.queryReportExistByUserIdAndContentId(userId, contentId); + } + + public Boolean queryReportExistByUserIdAndArchivingId(Long userId, Long archivingId) { + return reportRepository.queryReportExistByUserIdAndArchivingId(userId, archivingId); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/report/domain/Report.java b/Domain/src/main/java/allchive/server/domain/domains/report/domain/Report.java index 97dc956d..693b4dc7 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/report/domain/Report.java +++ b/Domain/src/main/java/allchive/server/domain/domains/report/domain/Report.java @@ -2,10 +2,11 @@ import allchive.server.domain.common.model.BaseTimeEntity; -import allchive.server.domain.domains.report.domain.enums.ReportType; +import allchive.server.domain.domains.report.domain.enums.ReportObjectType; import allchive.server.domain.domains.report.domain.enums.ReportedType; import javax.persistence.*; import lombok.AccessLevel; +import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; @@ -19,7 +20,7 @@ public class Report extends BaseTimeEntity { private Long id; @Enumerated(EnumType.STRING) - private ReportType reportType; + private ReportObjectType reportObjectTypeType; private String reason; @@ -28,5 +29,38 @@ public class Report extends BaseTimeEntity { private Long contentId; private Long archivingId; - private Long userId; + private Long userId; // 신고한 유저 + + @Builder + private Report( + ReportObjectType reportObjectTypeType, + String reason, + ReportedType reportedType, + Long contentId, + Long archivingId, + Long userId) { + this.reportObjectTypeType = reportObjectTypeType; + this.reason = reason; + this.reportedType = reportedType; + this.contentId = contentId; + this.archivingId = archivingId; + this.userId = userId; + } + + public static Report of( + ReportObjectType reportObjectTypeType, + String reason, + ReportedType reportedType, + Long contentId, + Long archivingId, + Long userId) { + return Report.builder() + .reportObjectTypeType(reportObjectTypeType) + .reason(reason) + .reportedType(reportedType) + .contentId(contentId) + .archivingId(archivingId) + .userId(userId) + .build(); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/report/domain/enums/ReportType.java b/Domain/src/main/java/allchive/server/domain/domains/report/domain/enums/ReportObjectType.java similarity index 95% rename from Domain/src/main/java/allchive/server/domain/domains/report/domain/enums/ReportType.java rename to Domain/src/main/java/allchive/server/domain/domains/report/domain/enums/ReportObjectType.java index d57c5d1f..5c90c3c7 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/report/domain/enums/ReportType.java +++ b/Domain/src/main/java/allchive/server/domain/domains/report/domain/enums/ReportObjectType.java @@ -10,7 +10,7 @@ @Getter @AllArgsConstructor -public enum ReportType { +public enum ReportObjectType { CONTENT("content"), ARCHIVING("archiving"); diff --git a/Domain/src/main/java/allchive/server/domain/domains/report/domain/enums/ReportedType.java b/Domain/src/main/java/allchive/server/domain/domains/report/domain/enums/ReportedType.java index bdc610d5..ad0f8cc4 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/report/domain/enums/ReportedType.java +++ b/Domain/src/main/java/allchive/server/domain/domains/report/domain/enums/ReportedType.java @@ -11,8 +11,14 @@ @Getter @AllArgsConstructor public enum ReportedType { - CONTENT("content"), - ARCHIVING("archiving"); + SPAM("spam"), // 스팸이에요 + OBSCENE("obscene"), // 음란성 컨텐츠를 담고있어요 + PERSONAL_INFO("personalInfo"), // 개인정보를 노출하고 있어요 + INTELLECTUAL_PROPERTY("intellectualProperty"), // 지식재산권을 침해해요 + VIOLENCE("violence"), // 혐오/폭력 컨텐츠를 담고 있어요 + ILLEGAL_INFO("illegalInformation"), // 불법 정보를 포함하고 있어요 + FRAUD("fraud"), // 사기 또는 피싱성 링크를 포함하고 있어요 + ETC("etc"); // 기타 @JsonValue private String value; diff --git a/Domain/src/main/java/allchive/server/domain/domains/report/exception/ReportErrorCode.java b/Domain/src/main/java/allchive/server/domain/domains/report/exception/ReportErrorCode.java index 711696c3..d3d14c9b 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/report/exception/ReportErrorCode.java +++ b/Domain/src/main/java/allchive/server/domain/domains/report/exception/ReportErrorCode.java @@ -1,22 +1,24 @@ package allchive.server.domain.domains.report.exception; +import static allchive.server.core.consts.AllchiveConst.BAD_REQUEST; import allchive.server.core.dto.ErrorReason; import allchive.server.core.error.BaseErrorCode; import lombok.AllArgsConstructor; import lombok.Getter; -import org.springframework.http.HttpStatus; @Getter @AllArgsConstructor public enum ReportErrorCode implements BaseErrorCode { + DUPLICATED_REPORT_CONTENT(BAD_REQUEST, "REPORT_400_1", "이미 신고한 컨텐츠입니다."), + DUPLICATED_REPORT_ARCHIVING(BAD_REQUEST, "REPORT_400_1", "이미 신고한 아카이빙입니다."), ; - private HttpStatus status; + private int status; private String code; private String reason; @Override public ErrorReason getErrorReason() { - return ErrorReason.of(status.value(), code, reason); + return ErrorReason.of(status, code, reason); } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/report/exception/exceptions/DuplicatedReportArchiving.java b/Domain/src/main/java/allchive/server/domain/domains/report/exception/exceptions/DuplicatedReportArchiving.java new file mode 100644 index 00000000..e66332e9 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/report/exception/exceptions/DuplicatedReportArchiving.java @@ -0,0 +1,14 @@ +package allchive.server.domain.domains.report.exception.exceptions; + + +import allchive.server.core.error.BaseErrorException; +import allchive.server.domain.domains.report.exception.ReportErrorCode; + +public class DuplicatedReportArchiving extends BaseErrorException { + + public static final BaseErrorException EXCEPTION = new DuplicatedReportArchiving(); + + private DuplicatedReportArchiving() { + super(ReportErrorCode.DUPLICATED_REPORT_ARCHIVING); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/report/exception/exceptions/DuplicatedReportContent.java b/Domain/src/main/java/allchive/server/domain/domains/report/exception/exceptions/DuplicatedReportContent.java new file mode 100644 index 00000000..df245952 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/report/exception/exceptions/DuplicatedReportContent.java @@ -0,0 +1,14 @@ +package allchive.server.domain.domains.report.exception.exceptions; + + +import allchive.server.core.error.BaseErrorException; +import allchive.server.domain.domains.report.exception.ReportErrorCode; + +public class DuplicatedReportContent extends BaseErrorException { + + public static final BaseErrorException EXCEPTION = new DuplicatedReportContent(); + + private DuplicatedReportContent() { + super(ReportErrorCode.DUPLICATED_REPORT_CONTENT); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/report/repository/ReportCustomRepository.java b/Domain/src/main/java/allchive/server/domain/domains/report/repository/ReportCustomRepository.java new file mode 100644 index 00000000..8c7a1f08 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/report/repository/ReportCustomRepository.java @@ -0,0 +1,7 @@ +package allchive.server.domain.domains.report.repository; + +public interface ReportCustomRepository { + Boolean queryReportExistByUserIdAndContentId(Long userId, Long contentId); + + Boolean queryReportExistByUserIdAndArchivingId(Long userId, Long archivingId); +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/report/repository/ReportCustomRepositoryImpl.java b/Domain/src/main/java/allchive/server/domain/domains/report/repository/ReportCustomRepositoryImpl.java new file mode 100644 index 00000000..d1ea0a1c --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/report/repository/ReportCustomRepositoryImpl.java @@ -0,0 +1,46 @@ +package allchive.server.domain.domains.report.repository; + +import static allchive.server.domain.domains.report.domain.QReport.report; + +import com.querydsl.core.types.dsl.BooleanExpression; +import com.querydsl.jpa.impl.JPAQueryFactory; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +public class ReportCustomRepositoryImpl implements ReportCustomRepository { + private final JPAQueryFactory queryFactory; + + @Override + public Boolean queryReportExistByUserIdAndContentId(Long userId, Long contentId) { + Integer fetchOne = + queryFactory + .selectOne() + .from(report) + .where(userIdEq(userId), contentIdEq(contentId)) + .fetchFirst(); // limit 1 + return fetchOne != null; + } + + @Override + public Boolean queryReportExistByUserIdAndArchivingId(Long userId, Long archivingId) { + Integer fetchOne = + queryFactory + .selectOne() + .from(report) + .where(userIdEq(userId), archivingIdEq(archivingId)) + .fetchFirst(); // limit 1 + return fetchOne != null; + } + + private BooleanExpression userIdEq(Long userId) { + return report.userId.eq(userId); + } + + private BooleanExpression contentIdEq(Long contentId) { + return report.contentId.eq(contentId); + } + + private BooleanExpression archivingIdEq(Long archivingId) { + return report.archivingId.eq(archivingId); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/report/repository/ReportRepository.java b/Domain/src/main/java/allchive/server/domain/domains/report/repository/ReportRepository.java index 7b6f4638..c1bf33b6 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/report/repository/ReportRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/report/repository/ReportRepository.java @@ -4,4 +4,4 @@ import allchive.server.domain.domains.report.domain.Report; import org.springframework.data.jpa.repository.JpaRepository; -public interface ReportRepository extends JpaRepository<Report, Long> {} +public interface ReportRepository extends JpaRepository<Report, Long>, ReportCustomRepository {} diff --git a/Domain/src/main/java/allchive/server/domain/domains/report/service/ReportDomainService.java b/Domain/src/main/java/allchive/server/domain/domains/report/service/ReportDomainService.java index 710f96e7..6e24e2d6 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/report/service/ReportDomainService.java +++ b/Domain/src/main/java/allchive/server/domain/domains/report/service/ReportDomainService.java @@ -2,8 +2,16 @@ import allchive.server.core.annotation.DomainService; +import allchive.server.domain.domains.report.adaptor.ReportAdaptor; +import allchive.server.domain.domains.report.domain.Report; import lombok.RequiredArgsConstructor; @DomainService @RequiredArgsConstructor -public class ReportDomainService {} +public class ReportDomainService { + private final ReportAdaptor reportAdaptor; + + public void save(Report report) { + reportAdaptor.save(report); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/report/validator/ReportValidator.java b/Domain/src/main/java/allchive/server/domain/domains/report/validator/ReportValidator.java index 1f82f50b..676eae89 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/report/validator/ReportValidator.java +++ b/Domain/src/main/java/allchive/server/domain/domains/report/validator/ReportValidator.java @@ -2,6 +2,30 @@ import allchive.server.core.annotation.Validator; +import allchive.server.domain.domains.report.adaptor.ReportAdaptor; +import allchive.server.domain.domains.report.domain.enums.ReportObjectType; +import allchive.server.domain.domains.report.exception.exceptions.DuplicatedReportArchiving; +import allchive.server.domain.domains.report.exception.exceptions.DuplicatedReportContent; +import lombok.RequiredArgsConstructor; @Validator -public class ReportValidator {} +@RequiredArgsConstructor +public class ReportValidator { + private final ReportAdaptor reportAdaptor; + + public void validateNotDuplicateReport(Long userId, Long objectId, ReportObjectType type) { + Boolean duplicateStatus = Boolean.FALSE; + switch (type) { + case CONTENT -> duplicateStatus = + reportAdaptor.queryReportExistByUserIdAndContentId(userId, objectId); + case ARCHIVING -> duplicateStatus = + reportAdaptor.queryReportExistByUserIdAndArchivingId(userId, objectId); + } + if (duplicateStatus) { + switch (type) { + case CONTENT -> throw DuplicatedReportContent.EXCEPTION; + case ARCHIVING -> throw DuplicatedReportArchiving.EXCEPTION; + } + } + } +} From 6079cadf2093c3cf24cf3752edd96aba6d9eb839 Mon Sep 17 00:00:00 2001 From: wjdtkdgns <wjdtkdgns10266@gmail.com> Date: Sun, 16 Jul 2023 21:36:37 +0900 Subject: [PATCH 32/41] =?UTF-8?q?[feat]=20=EC=9C=A0=EC=A0=80=20=EC=B0=A8?= =?UTF-8?q?=EB=8B=A8=ED=95=98=EA=B8=B0=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20#37?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/block/controller/BlockController.java | 26 +++++++++++++++ .../block/model/dto/request/BlockRequest.java | 11 +++++++ .../model/dto/response/BlockResponse.java | 21 ++++++++++++ .../api/block/model/mapper/BlockMapper.java | 12 +++++++ .../api/block/service/CreateBlockUseCase.java | 33 +++++++++++++++++++ .../api/report/model/mapper/ReportMapper.java | 30 +++++++++-------- .../domains/block/adaptor/BlockAdaptor.java | 8 +++++ .../domain/domains/block/domain/Block.java | 11 +++++++ .../block/exception/BlockErrorCode.java | 8 +++-- .../CannotBlockMyselfException.java | 14 ++++++++ .../exceptions/DuplicatedBlockException.java | 14 ++++++++ .../repository/BlockCustomRepository.java | 5 +++ .../repository/BlockCustomRepositoryImpl.java | 31 +++++++++++++++++ .../block/repository/BlockRepository.java | 2 +- .../block/service/BlockDomainService.java | 17 ++++++++++ .../domains/block/service/BlockService.java | 7 ---- .../block/validator/BlockValidator.java | 21 +++++++++++- 17 files changed, 245 insertions(+), 26 deletions(-) create mode 100644 Api/src/main/java/allchive/server/api/block/controller/BlockController.java create mode 100644 Api/src/main/java/allchive/server/api/block/model/dto/request/BlockRequest.java create mode 100644 Api/src/main/java/allchive/server/api/block/model/dto/response/BlockResponse.java create mode 100644 Api/src/main/java/allchive/server/api/block/model/mapper/BlockMapper.java create mode 100644 Api/src/main/java/allchive/server/api/block/service/CreateBlockUseCase.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/block/exception/exceptions/CannotBlockMyselfException.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/block/exception/exceptions/DuplicatedBlockException.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/block/repository/BlockCustomRepository.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/block/repository/BlockCustomRepositoryImpl.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/block/service/BlockDomainService.java delete mode 100644 Domain/src/main/java/allchive/server/domain/domains/block/service/BlockService.java diff --git a/Api/src/main/java/allchive/server/api/block/controller/BlockController.java b/Api/src/main/java/allchive/server/api/block/controller/BlockController.java new file mode 100644 index 00000000..c444d3d6 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/block/controller/BlockController.java @@ -0,0 +1,26 @@ +package allchive.server.api.block.controller; + + +import allchive.server.api.block.model.dto.request.BlockRequest; +import allchive.server.api.block.model.dto.response.BlockResponse; +import allchive.server.api.block.service.CreateBlockUseCase; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/blocks") +@RequiredArgsConstructor +@Slf4j +@Tag(name = "8. [block]") +public class BlockController { + private final CreateBlockUseCase createBlockUseCase; + + @Operation(summary = "유저를 차단합니다") + @PostMapping() + public BlockResponse createBlock(@RequestBody BlockRequest blockRequest) { + return createBlockUseCase.execute(blockRequest); + } +} diff --git a/Api/src/main/java/allchive/server/api/block/model/dto/request/BlockRequest.java b/Api/src/main/java/allchive/server/api/block/model/dto/request/BlockRequest.java new file mode 100644 index 00000000..f555cf73 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/block/model/dto/request/BlockRequest.java @@ -0,0 +1,11 @@ +package allchive.server.api.block.model.dto.request; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; + +@Getter +public class BlockRequest { + @Schema(defaultValue = "1", description = "차단할 유저의 id") + private Long userId; +} diff --git a/Api/src/main/java/allchive/server/api/block/model/dto/response/BlockResponse.java b/Api/src/main/java/allchive/server/api/block/model/dto/response/BlockResponse.java new file mode 100644 index 00000000..00ccbacf --- /dev/null +++ b/Api/src/main/java/allchive/server/api/block/model/dto/response/BlockResponse.java @@ -0,0 +1,21 @@ +package allchive.server.api.block.model.dto.response; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Builder; +import lombok.Getter; + +@Getter +public class BlockResponse { + @Schema(defaultValue = "이름", description = "차단한 유저의 닉네임") + private String nickname; + + @Builder + private BlockResponse(String nickname) { + this.nickname = nickname; + } + + public static BlockResponse from(String nickname) { + return BlockResponse.builder().nickname(nickname).build(); + } +} diff --git a/Api/src/main/java/allchive/server/api/block/model/mapper/BlockMapper.java b/Api/src/main/java/allchive/server/api/block/model/mapper/BlockMapper.java new file mode 100644 index 00000000..fc070b91 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/block/model/mapper/BlockMapper.java @@ -0,0 +1,12 @@ +package allchive.server.api.block.model.mapper; + + +import allchive.server.core.annotation.Mapper; +import allchive.server.domain.domains.block.domain.Block; + +@Mapper +public class BlockMapper { + public Block toEntity(Long blockFrom, Long blockUser) { + return Block.of(blockFrom, blockUser); + } +} diff --git a/Api/src/main/java/allchive/server/api/block/service/CreateBlockUseCase.java b/Api/src/main/java/allchive/server/api/block/service/CreateBlockUseCase.java new file mode 100644 index 00000000..f66bdbaa --- /dev/null +++ b/Api/src/main/java/allchive/server/api/block/service/CreateBlockUseCase.java @@ -0,0 +1,33 @@ +package allchive.server.api.block.service; + + +import allchive.server.api.block.model.dto.request.BlockRequest; +import allchive.server.api.block.model.dto.response.BlockResponse; +import allchive.server.api.block.model.mapper.BlockMapper; +import allchive.server.api.config.security.SecurityUtil; +import allchive.server.core.annotation.UseCase; +import allchive.server.domain.domains.block.domain.Block; +import allchive.server.domain.domains.block.service.BlockDomainService; +import allchive.server.domain.domains.block.validator.BlockValidator; +import allchive.server.domain.domains.user.adaptor.UserAdaptor; +import lombok.RequiredArgsConstructor; +import org.springframework.transaction.annotation.Transactional; + +@UseCase +@RequiredArgsConstructor +public class CreateBlockUseCase { + private final BlockValidator blockValidator; + private final BlockMapper blockMapper; + private final BlockDomainService blockDomainService; + private final UserAdaptor userAdaptor; + + @Transactional + public BlockResponse execute(BlockRequest request) { + Long userId = SecurityUtil.getCurrentUserId(); + blockValidator.validateNotDuplicate(userId, request.getUserId()); + blockValidator.validateNotMyself(userId, request.getUserId()); + Block block = blockMapper.toEntity(userId, request.getUserId()); + blockDomainService.save(block); + return BlockResponse.from(userAdaptor.queryUserById(request.getUserId()).getNickname()); + } +} diff --git a/Api/src/main/java/allchive/server/api/report/model/mapper/ReportMapper.java b/Api/src/main/java/allchive/server/api/report/model/mapper/ReportMapper.java index 12a83292..3cc73d79 100644 --- a/Api/src/main/java/allchive/server/api/report/model/mapper/ReportMapper.java +++ b/Api/src/main/java/allchive/server/api/report/model/mapper/ReportMapper.java @@ -11,20 +11,22 @@ public class ReportMapper { public Report toEntity(CreateReportRequest request, ReportObjectType type, Long userId) { Report report = null; switch (type) { - case CONTENT -> report = Report.of( - type, - request.getReason(), - request.getReportedType(), - request.getId(), - null, - userId); - case ARCHIVING -> report = Report.of( - type, - request.getReason(), - request.getReportedType(), - null, - request.getId(), - userId); + case CONTENT -> report = + Report.of( + type, + request.getReason(), + request.getReportedType(), + request.getId(), + null, + userId); + case ARCHIVING -> report = + Report.of( + type, + request.getReason(), + request.getReportedType(), + null, + request.getId(), + userId); } return report; } diff --git a/Domain/src/main/java/allchive/server/domain/domains/block/adaptor/BlockAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/block/adaptor/BlockAdaptor.java index 5a9dd6f4..b6268370 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/block/adaptor/BlockAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/block/adaptor/BlockAdaptor.java @@ -15,4 +15,12 @@ public class BlockAdaptor { public List<Block> findByBlockFrom(Long userId) { return blockRepository.findAllByBlockFrom(userId); } + + public boolean queryBlockExistByBlockFromAndBlockUser(Long blockFrom, Long blockUser) { + return blockRepository.queryBlockExistByBlockFromAndBlockUser(blockFrom, blockUser); + } + + public void save(Block block) { + blockRepository.save(block); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/block/domain/Block.java b/Domain/src/main/java/allchive/server/domain/domains/block/domain/Block.java index 6eef9cad..dcc4e2bf 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/block/domain/Block.java +++ b/Domain/src/main/java/allchive/server/domain/domains/block/domain/Block.java @@ -5,6 +5,7 @@ import javax.persistence.*; import javax.validation.constraints.NotNull; import lombok.AccessLevel; +import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; @@ -22,4 +23,14 @@ public class Block extends BaseTimeEntity { // Block 당한 유저 private Long blockUser; + + @Builder + private Block(Long blockFrom, Long blockUser) { + this.blockFrom = blockFrom; + this.blockUser = blockUser; + } + + public static Block of(Long blockFrom, Long blockUser) { + return Block.builder().blockFrom(blockFrom).blockUser(blockUser).build(); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/block/exception/BlockErrorCode.java b/Domain/src/main/java/allchive/server/domain/domains/block/exception/BlockErrorCode.java index 248eb69e..dfb4bccd 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/block/exception/BlockErrorCode.java +++ b/Domain/src/main/java/allchive/server/domain/domains/block/exception/BlockErrorCode.java @@ -1,22 +1,24 @@ package allchive.server.domain.domains.block.exception; +import static allchive.server.core.consts.AllchiveConst.BAD_REQUEST; import allchive.server.core.dto.ErrorReason; import allchive.server.core.error.BaseErrorCode; import lombok.AllArgsConstructor; import lombok.Getter; -import org.springframework.http.HttpStatus; @Getter @AllArgsConstructor public enum BlockErrorCode implements BaseErrorCode { + DUPLICATED_BLOCK(BAD_REQUEST, "BLOCK_400_1", "이미 차단한 유저입니다."), + CANNOT_BLOCK_MYSELF(BAD_REQUEST, "BLOCK_400_2", "본인을 차단할 수 없습니다."), ; - private HttpStatus status; + private int status; private String code; private String reason; @Override public ErrorReason getErrorReason() { - return ErrorReason.of(status.value(), code, reason); + return ErrorReason.of(status, code, reason); } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/block/exception/exceptions/CannotBlockMyselfException.java b/Domain/src/main/java/allchive/server/domain/domains/block/exception/exceptions/CannotBlockMyselfException.java new file mode 100644 index 00000000..aa3c8fec --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/block/exception/exceptions/CannotBlockMyselfException.java @@ -0,0 +1,14 @@ +package allchive.server.domain.domains.block.exception.exceptions; + + +import allchive.server.core.error.BaseErrorException; +import allchive.server.domain.domains.block.exception.BlockErrorCode; + +public class CannotBlockMyselfException extends BaseErrorException { + + public static final BaseErrorException EXCEPTION = new CannotBlockMyselfException(); + + private CannotBlockMyselfException() { + super(BlockErrorCode.CANNOT_BLOCK_MYSELF); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/block/exception/exceptions/DuplicatedBlockException.java b/Domain/src/main/java/allchive/server/domain/domains/block/exception/exceptions/DuplicatedBlockException.java new file mode 100644 index 00000000..b1772083 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/block/exception/exceptions/DuplicatedBlockException.java @@ -0,0 +1,14 @@ +package allchive.server.domain.domains.block.exception.exceptions; + + +import allchive.server.core.error.BaseErrorException; +import allchive.server.domain.domains.block.exception.BlockErrorCode; + +public class DuplicatedBlockException extends BaseErrorException { + + public static final BaseErrorException EXCEPTION = new DuplicatedBlockException(); + + private DuplicatedBlockException() { + super(BlockErrorCode.DUPLICATED_BLOCK); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/block/repository/BlockCustomRepository.java b/Domain/src/main/java/allchive/server/domain/domains/block/repository/BlockCustomRepository.java new file mode 100644 index 00000000..ae169ca0 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/block/repository/BlockCustomRepository.java @@ -0,0 +1,5 @@ +package allchive.server.domain.domains.block.repository; + +public interface BlockCustomRepository { + boolean queryBlockExistByBlockFromAndBlockUser(Long blockFrom, Long blockUser); +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/block/repository/BlockCustomRepositoryImpl.java b/Domain/src/main/java/allchive/server/domain/domains/block/repository/BlockCustomRepositoryImpl.java new file mode 100644 index 00000000..f172ef31 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/block/repository/BlockCustomRepositoryImpl.java @@ -0,0 +1,31 @@ +package allchive.server.domain.domains.block.repository; + +import static allchive.server.domain.domains.block.domain.QBlock.block; + +import com.querydsl.core.types.dsl.BooleanExpression; +import com.querydsl.jpa.impl.JPAQueryFactory; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +public class BlockCustomRepositoryImpl implements BlockCustomRepository { + private final JPAQueryFactory queryFactory; + + @Override + public boolean queryBlockExistByBlockFromAndBlockUser(Long blockFrom, Long blockUser) { + Integer fetchOne = + queryFactory + .selectOne() + .from(block) + .where(blockFromEq(blockFrom), blockUserEq(blockUser)) + .fetchFirst(); // limit 1 + return fetchOne != null; + } + + private BooleanExpression blockFromEq(Long blockFrom) { + return block.blockFrom.eq(blockFrom); + } + + private BooleanExpression blockUserEq(Long blockUser) { + return block.blockUser.eq(blockUser); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/block/repository/BlockRepository.java b/Domain/src/main/java/allchive/server/domain/domains/block/repository/BlockRepository.java index f3107795..43a855f7 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/block/repository/BlockRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/block/repository/BlockRepository.java @@ -5,6 +5,6 @@ import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; -public interface BlockRepository extends JpaRepository<Block, Long> { +public interface BlockRepository extends JpaRepository<Block, Long>, BlockCustomRepository { List<Block> findAllByBlockFrom(Long userId); } diff --git a/Domain/src/main/java/allchive/server/domain/domains/block/service/BlockDomainService.java b/Domain/src/main/java/allchive/server/domain/domains/block/service/BlockDomainService.java new file mode 100644 index 00000000..e9938577 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/block/service/BlockDomainService.java @@ -0,0 +1,17 @@ +package allchive.server.domain.domains.block.service; + + +import allchive.server.core.annotation.DomainService; +import allchive.server.domain.domains.block.adaptor.BlockAdaptor; +import allchive.server.domain.domains.block.domain.Block; +import lombok.RequiredArgsConstructor; + +@DomainService +@RequiredArgsConstructor +public class BlockDomainService { + private final BlockAdaptor blockAdaptor; + + public void save(Block block) { + blockAdaptor.save(block); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/block/service/BlockService.java b/Domain/src/main/java/allchive/server/domain/domains/block/service/BlockService.java deleted file mode 100644 index c2072c33..00000000 --- a/Domain/src/main/java/allchive/server/domain/domains/block/service/BlockService.java +++ /dev/null @@ -1,7 +0,0 @@ -package allchive.server.domain.domains.block.service; - - -import allchive.server.core.annotation.DomainService; - -@DomainService -public class BlockService {} diff --git a/Domain/src/main/java/allchive/server/domain/domains/block/validator/BlockValidator.java b/Domain/src/main/java/allchive/server/domain/domains/block/validator/BlockValidator.java index 31763f39..e9d78138 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/block/validator/BlockValidator.java +++ b/Domain/src/main/java/allchive/server/domain/domains/block/validator/BlockValidator.java @@ -2,6 +2,25 @@ import allchive.server.core.annotation.Validator; +import allchive.server.domain.domains.block.adaptor.BlockAdaptor; +import allchive.server.domain.domains.block.exception.exceptions.CannotBlockMyselfException; +import allchive.server.domain.domains.block.exception.exceptions.DuplicatedBlockException; +import lombok.RequiredArgsConstructor; @Validator -public class BlockValidator {} +@RequiredArgsConstructor +public class BlockValidator { + private final BlockAdaptor blockAdaptor; + + public void validateNotDuplicate(Long blockFrom, Long blockUser) { + if (blockAdaptor.queryBlockExistByBlockFromAndBlockUser(blockFrom, blockUser)) { + throw DuplicatedBlockException.EXCEPTION; + } + } + + public void validateNotMyself(Long blockFrom, Long blockUser) { + if (blockFrom.equals(blockUser)) { + throw CannotBlockMyselfException.EXCEPTION; + } + } +} From c7138dc3f881f787049e7d9e611d56647d16aef9 Mon Sep 17 00:00:00 2001 From: wjdtkdgns <wjdtkdgns10266@gmail.com> Date: Sun, 16 Jul 2023 22:24:33 +0900 Subject: [PATCH 33/41] =?UTF-8?q?[feat]=20=EC=9C=A0=EC=A0=80=20=EC=B0=A8?= =?UTF-8?q?=EB=8B=A8=ED=95=98=EA=B8=B0=20=ED=95=B4=EC=A0=9C=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EA=B5=AC=ED=98=84=20#37?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/block/controller/BlockController.java | 10 ++++++- .../api/block/service/DeleteBlockUseCase.java | 27 +++++++++++++++++++ .../domains/block/adaptor/BlockAdaptor.java | 4 +++ .../block/exception/BlockErrorCode.java | 4 ++- .../exceptions/BlockNotFoundException.java | 14 ++++++++++ .../block/repository/BlockRepository.java | 2 ++ .../block/service/BlockDomainService.java | 4 +++ .../block/validator/BlockValidator.java | 7 +++++ 8 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 Api/src/main/java/allchive/server/api/block/service/DeleteBlockUseCase.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/block/exception/exceptions/BlockNotFoundException.java diff --git a/Api/src/main/java/allchive/server/api/block/controller/BlockController.java b/Api/src/main/java/allchive/server/api/block/controller/BlockController.java index c444d3d6..deddfb66 100644 --- a/Api/src/main/java/allchive/server/api/block/controller/BlockController.java +++ b/Api/src/main/java/allchive/server/api/block/controller/BlockController.java @@ -4,6 +4,7 @@ import allchive.server.api.block.model.dto.request.BlockRequest; import allchive.server.api.block.model.dto.response.BlockResponse; import allchive.server.api.block.service.CreateBlockUseCase; +import allchive.server.api.block.service.DeleteBlockUseCase; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; @@ -17,10 +18,17 @@ @Tag(name = "8. [block]") public class BlockController { private final CreateBlockUseCase createBlockUseCase; + private final DeleteBlockUseCase deleteBlockUseCase; - @Operation(summary = "유저를 차단합니다") + @Operation(summary = "유저를 차단합니다.") @PostMapping() public BlockResponse createBlock(@RequestBody BlockRequest blockRequest) { return createBlockUseCase.execute(blockRequest); } + + @Operation(summary = "유저 차단을 해제합니다.") + @DeleteMapping() + public BlockResponse deleteBlock(@RequestBody BlockRequest blockRequest) { + return deleteBlockUseCase.execute(blockRequest); + } } diff --git a/Api/src/main/java/allchive/server/api/block/service/DeleteBlockUseCase.java b/Api/src/main/java/allchive/server/api/block/service/DeleteBlockUseCase.java new file mode 100644 index 00000000..aabf714c --- /dev/null +++ b/Api/src/main/java/allchive/server/api/block/service/DeleteBlockUseCase.java @@ -0,0 +1,27 @@ +package allchive.server.api.block.service; + +import allchive.server.api.block.model.dto.request.BlockRequest; +import allchive.server.api.block.model.dto.response.BlockResponse; +import allchive.server.api.config.security.SecurityUtil; +import allchive.server.core.annotation.UseCase; +import allchive.server.domain.domains.block.service.BlockDomainService; +import allchive.server.domain.domains.block.validator.BlockValidator; +import allchive.server.domain.domains.user.adaptor.UserAdaptor; +import lombok.RequiredArgsConstructor; +import org.springframework.transaction.annotation.Transactional; + +@UseCase +@RequiredArgsConstructor +public class DeleteBlockUseCase { + private final BlockValidator blockValidator; + private final BlockDomainService blockDomainService; + private final UserAdaptor userAdaptor; + + @Transactional + public BlockResponse execute(BlockRequest request) { + Long userId = SecurityUtil.getCurrentUserId(); + blockValidator.validateExist(userId, request.getUserId()); + blockDomainService.deleteByBlockFromAndBlockUser(userId, request.getUserId()); + return BlockResponse.from(userAdaptor.queryUserById(request.getUserId()).getNickname()); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/block/adaptor/BlockAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/block/adaptor/BlockAdaptor.java index b6268370..2ca80c63 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/block/adaptor/BlockAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/block/adaptor/BlockAdaptor.java @@ -23,4 +23,8 @@ public boolean queryBlockExistByBlockFromAndBlockUser(Long blockFrom, Long block public void save(Block block) { blockRepository.save(block); } + + public void deleteByBlockFromAndBlockUser(Long blockFrom, Long blockUser) { + blockRepository.deleteByBlockFromAndBlockUser(blockFrom, blockUser); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/block/exception/BlockErrorCode.java b/Domain/src/main/java/allchive/server/domain/domains/block/exception/BlockErrorCode.java index dfb4bccd..887dcb38 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/block/exception/BlockErrorCode.java +++ b/Domain/src/main/java/allchive/server/domain/domains/block/exception/BlockErrorCode.java @@ -1,6 +1,7 @@ package allchive.server.domain.domains.block.exception; import static allchive.server.core.consts.AllchiveConst.BAD_REQUEST; +import static allchive.server.core.consts.AllchiveConst.NOT_FOUND; import allchive.server.core.dto.ErrorReason; import allchive.server.core.error.BaseErrorCode; @@ -12,7 +13,8 @@ public enum BlockErrorCode implements BaseErrorCode { DUPLICATED_BLOCK(BAD_REQUEST, "BLOCK_400_1", "이미 차단한 유저입니다."), CANNOT_BLOCK_MYSELF(BAD_REQUEST, "BLOCK_400_2", "본인을 차단할 수 없습니다."), - ; + + BLOCK_NOT_FOUND(NOT_FOUND, "BLOCK_404_1", "차단되지않은 유저입니다."); private int status; private String code; private String reason; diff --git a/Domain/src/main/java/allchive/server/domain/domains/block/exception/exceptions/BlockNotFoundException.java b/Domain/src/main/java/allchive/server/domain/domains/block/exception/exceptions/BlockNotFoundException.java new file mode 100644 index 00000000..ad9696a1 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/block/exception/exceptions/BlockNotFoundException.java @@ -0,0 +1,14 @@ +package allchive.server.domain.domains.block.exception.exceptions; + + +import allchive.server.core.error.BaseErrorException; +import allchive.server.domain.domains.block.exception.BlockErrorCode; + +public class BlockNotFoundException extends BaseErrorException { + + public static final BaseErrorException EXCEPTION = new BlockNotFoundException(); + + private BlockNotFoundException() { + super(BlockErrorCode.BLOCK_NOT_FOUND); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/block/repository/BlockRepository.java b/Domain/src/main/java/allchive/server/domain/domains/block/repository/BlockRepository.java index 43a855f7..d911e687 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/block/repository/BlockRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/block/repository/BlockRepository.java @@ -7,4 +7,6 @@ public interface BlockRepository extends JpaRepository<Block, Long>, BlockCustomRepository { List<Block> findAllByBlockFrom(Long userId); + + void deleteByBlockFromAndBlockUser(Long blockFrom, Long blockUser); } diff --git a/Domain/src/main/java/allchive/server/domain/domains/block/service/BlockDomainService.java b/Domain/src/main/java/allchive/server/domain/domains/block/service/BlockDomainService.java index e9938577..a38fb224 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/block/service/BlockDomainService.java +++ b/Domain/src/main/java/allchive/server/domain/domains/block/service/BlockDomainService.java @@ -14,4 +14,8 @@ public class BlockDomainService { public void save(Block block) { blockAdaptor.save(block); } + + public void deleteByBlockFromAndBlockUser(Long blockFrom, Long blockUser) { + blockAdaptor.deleteByBlockFromAndBlockUser(blockFrom, blockUser); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/block/validator/BlockValidator.java b/Domain/src/main/java/allchive/server/domain/domains/block/validator/BlockValidator.java index e9d78138..b3926ba1 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/block/validator/BlockValidator.java +++ b/Domain/src/main/java/allchive/server/domain/domains/block/validator/BlockValidator.java @@ -3,6 +3,7 @@ import allchive.server.core.annotation.Validator; import allchive.server.domain.domains.block.adaptor.BlockAdaptor; +import allchive.server.domain.domains.block.exception.exceptions.BlockNotFoundException; import allchive.server.domain.domains.block.exception.exceptions.CannotBlockMyselfException; import allchive.server.domain.domains.block.exception.exceptions.DuplicatedBlockException; import lombok.RequiredArgsConstructor; @@ -23,4 +24,10 @@ public void validateNotMyself(Long blockFrom, Long blockUser) { throw CannotBlockMyselfException.EXCEPTION; } } + + public void validateExist(Long blockFrom, Long blockUser) { + if (!blockAdaptor.queryBlockExistByBlockFromAndBlockUser(blockFrom, blockUser)) { + throw BlockNotFoundException.EXCEPTION; + } + } } From 24cb064ddbb36c4e591454e098fc4f59422208d6 Mon Sep 17 00:00:00 2001 From: wjdtkdgns <wjdtkdgns10266@gmail.com> Date: Sun, 16 Jul 2023 22:54:24 +0900 Subject: [PATCH 34/41] =?UTF-8?q?[feat]=20=EC=B0=A8=EB=8B=A8=20=EC=9C=A0?= =?UTF-8?q?=EC=A0=80=20=EC=A0=95=EB=B3=B4=20=EB=B3=B4=EA=B8=B0=20#37?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/block/controller/BlockController.java | 9 ++++++ .../dto/response/BlockUsersResponse.java | 23 ++++++++++++++ .../api/block/model/mapper/BlockMapper.java | 16 ++++++++++ .../api/block/model/vo/BlockUserVo.java | 27 ++++++++++++++++ .../api/block/service/GetBlockUseCase.java | 31 +++++++++++++++++++ .../domains/user/adaptor/UserAdaptor.java | 6 ++++ .../user/repository/UserRepository.java | 4 +++ 7 files changed, 116 insertions(+) create mode 100644 Api/src/main/java/allchive/server/api/block/model/dto/response/BlockUsersResponse.java create mode 100644 Api/src/main/java/allchive/server/api/block/model/vo/BlockUserVo.java create mode 100644 Api/src/main/java/allchive/server/api/block/service/GetBlockUseCase.java diff --git a/Api/src/main/java/allchive/server/api/block/controller/BlockController.java b/Api/src/main/java/allchive/server/api/block/controller/BlockController.java index deddfb66..6ad67635 100644 --- a/Api/src/main/java/allchive/server/api/block/controller/BlockController.java +++ b/Api/src/main/java/allchive/server/api/block/controller/BlockController.java @@ -3,8 +3,10 @@ import allchive.server.api.block.model.dto.request.BlockRequest; import allchive.server.api.block.model.dto.response.BlockResponse; +import allchive.server.api.block.model.dto.response.BlockUsersResponse; import allchive.server.api.block.service.CreateBlockUseCase; import allchive.server.api.block.service.DeleteBlockUseCase; +import allchive.server.api.block.service.GetBlockUseCase; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; @@ -19,6 +21,7 @@ public class BlockController { private final CreateBlockUseCase createBlockUseCase; private final DeleteBlockUseCase deleteBlockUseCase; + private final GetBlockUseCase getBlockUseCase; @Operation(summary = "유저를 차단합니다.") @PostMapping() @@ -31,4 +34,10 @@ public BlockResponse createBlock(@RequestBody BlockRequest blockRequest) { public BlockResponse deleteBlock(@RequestBody BlockRequest blockRequest) { return deleteBlockUseCase.execute(blockRequest); } + + @Operation(summary = "차단한 유저 정보를 가져옵니다.") + @GetMapping() + public BlockUsersResponse getBlock() { + return getBlockUseCase.execute(); + } } diff --git a/Api/src/main/java/allchive/server/api/block/model/dto/response/BlockUsersResponse.java b/Api/src/main/java/allchive/server/api/block/model/dto/response/BlockUsersResponse.java new file mode 100644 index 00000000..3353559c --- /dev/null +++ b/Api/src/main/java/allchive/server/api/block/model/dto/response/BlockUsersResponse.java @@ -0,0 +1,23 @@ +package allchive.server.api.block.model.dto.response; + +import allchive.server.api.block.model.vo.BlockUserVo; +import lombok.Builder; +import lombok.Getter; + +import java.util.List; + +@Getter +public class BlockUsersResponse { + List<BlockUserVo> users; + + @Builder + private BlockUsersResponse(List<BlockUserVo> users) { + this.users = users; + } + + public static BlockUsersResponse from(List<BlockUserVo> users) { + return BlockUsersResponse.builder() + .users(users) + .build(); + } +} diff --git a/Api/src/main/java/allchive/server/api/block/model/mapper/BlockMapper.java b/Api/src/main/java/allchive/server/api/block/model/mapper/BlockMapper.java index fc070b91..725a3369 100644 --- a/Api/src/main/java/allchive/server/api/block/model/mapper/BlockMapper.java +++ b/Api/src/main/java/allchive/server/api/block/model/mapper/BlockMapper.java @@ -1,12 +1,28 @@ package allchive.server.api.block.model.mapper; +import allchive.server.api.block.model.vo.BlockUserVo; import allchive.server.core.annotation.Mapper; +import allchive.server.core.error.exception.InternalServerError; import allchive.server.domain.domains.block.domain.Block; +import allchive.server.domain.domains.user.domain.User; + +import java.util.List; @Mapper public class BlockMapper { public Block toEntity(Long blockFrom, Long blockUser) { return Block.of(blockFrom, blockUser); } + + public List<BlockUserVo> toBlockUserVoList(List<Block> blockList, List<User> users) { + return blockList.stream() + .map(block -> { + User user = users.stream() + .filter(u -> u.getId().equals(block.getBlockUser())) + .findFirst() + .orElseThrow(); + return BlockUserVo.of(user.getNickname(), block.getBlockUser()); + }).toList(); + } } diff --git a/Api/src/main/java/allchive/server/api/block/model/vo/BlockUserVo.java b/Api/src/main/java/allchive/server/api/block/model/vo/BlockUserVo.java new file mode 100644 index 00000000..9f8c7fe0 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/block/model/vo/BlockUserVo.java @@ -0,0 +1,27 @@ +package allchive.server.api.block.model.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Builder; +import lombok.Getter; + +@Getter +public class BlockUserVo { + @Schema(defaultValue = "닉네임", description = "차단한 유저의 닉네임") + private String nickname; + + @Schema(defaultValue = "1", description = "차단한 유저의 고유번호") + private Long id; + + @Builder + private BlockUserVo(String nickname, Long id) { + this.nickname = nickname; + this.id = id; + } + + public static BlockUserVo of(String nickname, Long id) { + return BlockUserVo.builder() + .nickname(nickname) + .id(id) + .build(); + } +} diff --git a/Api/src/main/java/allchive/server/api/block/service/GetBlockUseCase.java b/Api/src/main/java/allchive/server/api/block/service/GetBlockUseCase.java new file mode 100644 index 00000000..c344b437 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/block/service/GetBlockUseCase.java @@ -0,0 +1,31 @@ +package allchive.server.api.block.service; + +import allchive.server.api.block.model.dto.response.BlockUsersResponse; +import allchive.server.api.block.model.mapper.BlockMapper; +import allchive.server.api.config.security.SecurityUtil; +import allchive.server.core.annotation.UseCase; +import allchive.server.domain.domains.block.adaptor.BlockAdaptor; +import allchive.server.domain.domains.block.domain.Block; +import allchive.server.domain.domains.user.adaptor.UserAdaptor; +import allchive.server.domain.domains.user.domain.User; +import lombok.RequiredArgsConstructor; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@UseCase +@RequiredArgsConstructor +public class GetBlockUseCase { + private final BlockAdaptor blockAdaptor; + private final BlockMapper blockMapper; + private final UserAdaptor userAdaptor; + + @Transactional(readOnly = true) + public BlockUsersResponse execute() { + Long userId = SecurityUtil.getCurrentUserId(); + List<Block> blockList = blockAdaptor.findByBlockFrom(userId); + List<Long> userIds = blockList.stream().map(Block::getBlockUser).toList(); + List<User> users = userAdaptor.findAllByIdIn(userIds); + return BlockUsersResponse.from(blockMapper.toBlockUserVoList(blockList, users)); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/UserAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/UserAdaptor.java index 135d202f..01fbe90e 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/UserAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/UserAdaptor.java @@ -8,6 +8,8 @@ import allchive.server.domain.domains.user.repository.UserRepository; import lombok.RequiredArgsConstructor; +import java.util.List; + @Adaptor @RequiredArgsConstructor public class UserAdaptor { @@ -34,4 +36,8 @@ public User queryUserById(Long userId) { public Boolean existsByNickname(String nickname) { return userRepository.existsByNickname(nickname); } + + public List<User> findAllByIdIn(List<Long> userIds) { + return userRepository.findAllByIdIn(userIds); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/repository/UserRepository.java b/Domain/src/main/java/allchive/server/domain/domains/user/repository/UserRepository.java index af39a107..7a2e24af 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/repository/UserRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/repository/UserRepository.java @@ -3,6 +3,8 @@ import allchive.server.domain.domains.user.domain.User; import allchive.server.domain.domains.user.domain.enums.OauthInfo; + +import java.util.List; import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; @@ -10,4 +12,6 @@ public interface UserRepository extends JpaRepository<User, Long> { Optional<User> findByOauthInfo(OauthInfo oauthInfo); boolean existsByNickname(String nickname); + + List<User> findAllByIdIn(List<Long> userIds); } From 81de1a77f25a51609912304ebfbeae68e4a0445f Mon Sep 17 00:00:00 2001 From: Sanghoon Jeong <67852689+wjdtkdgns@users.noreply.github.com> Date: Tue, 18 Jul 2023 14:34:19 +0900 Subject: [PATCH 35/41] =?UTF-8?q?[feat]=20=EA=B2=80=EC=83=89=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EA=B5=AC=ED=98=84=20(#41)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [feat] 검색하기 구현 #38 * [feat] 최근 검색어 조회 기능 구현 #38 * [feat] 연관 검색어 조회 기능 구현 #38 * [feat] 아카이빙 제목 레디스 리뉴얼 스케줄링 구현 #38 * [fix] 제목 리뉴얼 로직 변경 #38 * [fix] 어노테이션 추가 #38 --- .../dto/response/BlockUsersResponse.java | 8 +- .../api/block/model/mapper/BlockMapper.java | 19 ++-- .../api/block/model/vo/BlockUserVo.java | 6 +- .../api/block/service/DeleteBlockUseCase.java | 1 + .../api/block/service/GetBlockUseCase.java | 6 +- .../service/ClearOldDeletedObjectUseCase.java | 2 +- .../search/controller/SearchController.java | 50 +++++++++++ .../model/dto/request/SearchRequest.java | 11 +++ .../dto/response/SearchListResponse.java | 22 +++++ .../model/dto/response/SearchResponse.java | 35 ++++++++ .../api/search/model/enums/ArchivingType.java | 7 ++ .../service/GetLatestSearchListUseCase.java | 27 ++++++ .../service/GetRelativeSearchListUseCase.java | 41 +++++++++ .../service/RenewalTitleDataUseCase.java | 49 ++++++++++ .../service/SearchArchivingUseCase.java | 89 +++++++++++++++++++ .../server/core/consts/AllchiveConst.java | 3 + .../archiving/adaptor/ArchivingAdaptor.java | 16 ++++ .../repository/ArchivingCustomRepository.java | 6 ++ .../ArchivingCustomRepositoryImpl.java | 37 ++++++++ .../repository/ArchivingRepository.java | 2 + .../search/adaptor/LatestSearchAdaptor.java | 26 ++++++ .../domains/search/domain/LatestSearch.java | 33 +++++++ .../repository/LatestSearchRepository.java | 10 +++ .../service/LatestSearchDomainService.java | 21 +++++ .../domains/user/adaptor/UserAdaptor.java | 3 +- .../user/repository/UserRepository.java | 1 - .../redis/config/RedisConfig.java | 11 +++ 27 files changed, 517 insertions(+), 25 deletions(-) create mode 100644 Api/src/main/java/allchive/server/api/search/controller/SearchController.java create mode 100644 Api/src/main/java/allchive/server/api/search/model/dto/request/SearchRequest.java create mode 100644 Api/src/main/java/allchive/server/api/search/model/dto/response/SearchListResponse.java create mode 100644 Api/src/main/java/allchive/server/api/search/model/dto/response/SearchResponse.java create mode 100644 Api/src/main/java/allchive/server/api/search/model/enums/ArchivingType.java create mode 100644 Api/src/main/java/allchive/server/api/search/service/GetLatestSearchListUseCase.java create mode 100644 Api/src/main/java/allchive/server/api/search/service/GetRelativeSearchListUseCase.java create mode 100644 Api/src/main/java/allchive/server/api/search/service/RenewalTitleDataUseCase.java create mode 100644 Api/src/main/java/allchive/server/api/search/service/SearchArchivingUseCase.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/search/adaptor/LatestSearchAdaptor.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/search/domain/LatestSearch.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/search/repository/LatestSearchRepository.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/search/service/LatestSearchDomainService.java diff --git a/Api/src/main/java/allchive/server/api/block/model/dto/response/BlockUsersResponse.java b/Api/src/main/java/allchive/server/api/block/model/dto/response/BlockUsersResponse.java index 3353559c..47553cd9 100644 --- a/Api/src/main/java/allchive/server/api/block/model/dto/response/BlockUsersResponse.java +++ b/Api/src/main/java/allchive/server/api/block/model/dto/response/BlockUsersResponse.java @@ -1,11 +1,11 @@ package allchive.server.api.block.model.dto.response; + import allchive.server.api.block.model.vo.BlockUserVo; +import java.util.List; import lombok.Builder; import lombok.Getter; -import java.util.List; - @Getter public class BlockUsersResponse { List<BlockUserVo> users; @@ -16,8 +16,6 @@ private BlockUsersResponse(List<BlockUserVo> users) { } public static BlockUsersResponse from(List<BlockUserVo> users) { - return BlockUsersResponse.builder() - .users(users) - .build(); + return BlockUsersResponse.builder().users(users).build(); } } diff --git a/Api/src/main/java/allchive/server/api/block/model/mapper/BlockMapper.java b/Api/src/main/java/allchive/server/api/block/model/mapper/BlockMapper.java index 725a3369..547daf83 100644 --- a/Api/src/main/java/allchive/server/api/block/model/mapper/BlockMapper.java +++ b/Api/src/main/java/allchive/server/api/block/model/mapper/BlockMapper.java @@ -3,10 +3,8 @@ import allchive.server.api.block.model.vo.BlockUserVo; import allchive.server.core.annotation.Mapper; -import allchive.server.core.error.exception.InternalServerError; import allchive.server.domain.domains.block.domain.Block; import allchive.server.domain.domains.user.domain.User; - import java.util.List; @Mapper @@ -17,12 +15,15 @@ public Block toEntity(Long blockFrom, Long blockUser) { public List<BlockUserVo> toBlockUserVoList(List<Block> blockList, List<User> users) { return blockList.stream() - .map(block -> { - User user = users.stream() - .filter(u -> u.getId().equals(block.getBlockUser())) - .findFirst() - .orElseThrow(); - return BlockUserVo.of(user.getNickname(), block.getBlockUser()); - }).toList(); + .map( + block -> { + User user = + users.stream() + .filter(u -> u.getId().equals(block.getBlockUser())) + .findFirst() + .orElseThrow(); + return BlockUserVo.of(user.getNickname(), block.getBlockUser()); + }) + .toList(); } } diff --git a/Api/src/main/java/allchive/server/api/block/model/vo/BlockUserVo.java b/Api/src/main/java/allchive/server/api/block/model/vo/BlockUserVo.java index 9f8c7fe0..183694f5 100644 --- a/Api/src/main/java/allchive/server/api/block/model/vo/BlockUserVo.java +++ b/Api/src/main/java/allchive/server/api/block/model/vo/BlockUserVo.java @@ -1,5 +1,6 @@ package allchive.server.api.block.model.vo; + import io.swagger.v3.oas.annotations.media.Schema; import lombok.Builder; import lombok.Getter; @@ -19,9 +20,6 @@ private BlockUserVo(String nickname, Long id) { } public static BlockUserVo of(String nickname, Long id) { - return BlockUserVo.builder() - .nickname(nickname) - .id(id) - .build(); + return BlockUserVo.builder().nickname(nickname).id(id).build(); } } diff --git a/Api/src/main/java/allchive/server/api/block/service/DeleteBlockUseCase.java b/Api/src/main/java/allchive/server/api/block/service/DeleteBlockUseCase.java index aabf714c..fb829c6a 100644 --- a/Api/src/main/java/allchive/server/api/block/service/DeleteBlockUseCase.java +++ b/Api/src/main/java/allchive/server/api/block/service/DeleteBlockUseCase.java @@ -1,5 +1,6 @@ package allchive.server.api.block.service; + import allchive.server.api.block.model.dto.request.BlockRequest; import allchive.server.api.block.model.dto.response.BlockResponse; import allchive.server.api.config.security.SecurityUtil; diff --git a/Api/src/main/java/allchive/server/api/block/service/GetBlockUseCase.java b/Api/src/main/java/allchive/server/api/block/service/GetBlockUseCase.java index c344b437..c0a4f3f2 100644 --- a/Api/src/main/java/allchive/server/api/block/service/GetBlockUseCase.java +++ b/Api/src/main/java/allchive/server/api/block/service/GetBlockUseCase.java @@ -1,5 +1,6 @@ package allchive.server.api.block.service; + import allchive.server.api.block.model.dto.response.BlockUsersResponse; import allchive.server.api.block.model.mapper.BlockMapper; import allchive.server.api.config.security.SecurityUtil; @@ -8,17 +9,16 @@ import allchive.server.domain.domains.block.domain.Block; import allchive.server.domain.domains.user.adaptor.UserAdaptor; import allchive.server.domain.domains.user.domain.User; +import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.transaction.annotation.Transactional; -import java.util.List; - @UseCase @RequiredArgsConstructor public class GetBlockUseCase { private final BlockAdaptor blockAdaptor; private final BlockMapper blockMapper; - private final UserAdaptor userAdaptor; + private final UserAdaptor userAdaptor; @Transactional(readOnly = true) public BlockUsersResponse execute() { diff --git a/Api/src/main/java/allchive/server/api/recycle/service/ClearOldDeletedObjectUseCase.java b/Api/src/main/java/allchive/server/api/recycle/service/ClearOldDeletedObjectUseCase.java index 07de040e..ed855e2f 100644 --- a/Api/src/main/java/allchive/server/api/recycle/service/ClearOldDeletedObjectUseCase.java +++ b/Api/src/main/java/allchive/server/api/recycle/service/ClearOldDeletedObjectUseCase.java @@ -33,7 +33,7 @@ public class ClearOldDeletedObjectUseCase { private final RecycleDomainService recycleDomainService; /** 삭제 후 30일 지난 항목 제거 스케쥴러 매일 02:30에 수행 */ - @Scheduled(cron = "0 43 22 * * *") + @Scheduled(cron = "0 30 2 * * *") @Transactional public void executeSchedule() { log.info("scheduler on"); diff --git a/Api/src/main/java/allchive/server/api/search/controller/SearchController.java b/Api/src/main/java/allchive/server/api/search/controller/SearchController.java new file mode 100644 index 00000000..d476f5a5 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/search/controller/SearchController.java @@ -0,0 +1,50 @@ +package allchive.server.api.search.controller; + + +import allchive.server.api.search.model.dto.request.SearchRequest; +import allchive.server.api.search.model.dto.response.SearchListResponse; +import allchive.server.api.search.model.dto.response.SearchResponse; +import allchive.server.api.search.model.enums.ArchivingType; +import allchive.server.api.search.service.GetLatestSearchListUseCase; +import allchive.server.api.search.service.GetRelativeSearchListUseCase; +import allchive.server.api.search.service.SearchArchivingUseCase; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springdoc.api.annotations.ParameterObject; +import org.springframework.data.domain.Pageable; +import org.springframework.data.web.PageableDefault; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/searches") +@RequiredArgsConstructor +@Slf4j +@Tag(name = "9. [search]") +public class SearchController { + private final SearchArchivingUseCase searchArchivingUseCase; + private final GetLatestSearchListUseCase getLatestSearchListUseCase; + private final GetRelativeSearchListUseCase getRelativeSearchListUseCase; + + @Operation(summary = "검색어를 검색합니다.") + @PostMapping + public SearchResponse searchArchiving( + @ParameterObject @PageableDefault(size = 10) Pageable pageable, + @RequestParam("type") ArchivingType type, + @RequestBody SearchRequest request) { + return searchArchivingUseCase.execute(pageable, type, request); + } + + @Operation(summary = "최근 검색어 목록을 가져옵니다.", description = "5개만 드릴게요") + @GetMapping(value = "/latest") + public SearchListResponse getLatestSearchList() { + return getLatestSearchListUseCase.execute(); + } + + @Operation(summary = "검색어 자동 완성") + @PostMapping(value = "/relation") + public SearchListResponse getRelativeSearchList(@RequestBody SearchRequest request) { + return getRelativeSearchListUseCase.execute(request); + } +} diff --git a/Api/src/main/java/allchive/server/api/search/model/dto/request/SearchRequest.java b/Api/src/main/java/allchive/server/api/search/model/dto/request/SearchRequest.java new file mode 100644 index 00000000..84bfda69 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/search/model/dto/request/SearchRequest.java @@ -0,0 +1,11 @@ +package allchive.server.api.search.model.dto.request; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; + +@Getter +public class SearchRequest { + @Schema(defaultValue = "키워드", description = "검색 내용") + private String keyword; +} diff --git a/Api/src/main/java/allchive/server/api/search/model/dto/response/SearchListResponse.java b/Api/src/main/java/allchive/server/api/search/model/dto/response/SearchListResponse.java new file mode 100644 index 00000000..ac331af8 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/search/model/dto/response/SearchListResponse.java @@ -0,0 +1,22 @@ +package allchive.server.api.search.model.dto.response; + + +import io.swagger.v3.oas.annotations.media.Schema; +import java.util.List; +import lombok.Builder; +import lombok.Getter; + +@Getter +public class SearchListResponse { + @Schema(description = "검색어 목록") + private List<String> keyword; + + @Builder + private SearchListResponse(List<String> keyword) { + this.keyword = keyword; + } + + public static SearchListResponse from(List<String> keywords) { + return SearchListResponse.builder().keyword(keywords).build(); + } +} diff --git a/Api/src/main/java/allchive/server/api/search/model/dto/response/SearchResponse.java b/Api/src/main/java/allchive/server/api/search/model/dto/response/SearchResponse.java new file mode 100644 index 00000000..7d9c7f78 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/search/model/dto/response/SearchResponse.java @@ -0,0 +1,35 @@ +package allchive.server.api.search.model.dto.response; + + +import allchive.server.api.archiving.model.dto.response.ArchivingResponse; +import allchive.server.api.common.slice.SliceResponse; +import lombok.Builder; +import lombok.Getter; + +@Getter +public class SearchResponse { + SliceResponse<ArchivingResponse> archivings; + SliceResponse<ArchivingResponse> community; + + @Builder + private SearchResponse( + SliceResponse<ArchivingResponse> archivings, + SliceResponse<ArchivingResponse> community) { + this.archivings = archivings; + this.community = community; + } + + public static SearchResponse forAll( + SliceResponse<ArchivingResponse> archivings, + SliceResponse<ArchivingResponse> community) { + return SearchResponse.builder().archivings(archivings).community(community).build(); + } + + public static SearchResponse forMy(SliceResponse<ArchivingResponse> archivings) { + return SearchResponse.builder().archivings(archivings).community(null).build(); + } + + public static SearchResponse forCommunity(SliceResponse<ArchivingResponse> community) { + return SearchResponse.builder().archivings(null).community(community).build(); + } +} diff --git a/Api/src/main/java/allchive/server/api/search/model/enums/ArchivingType.java b/Api/src/main/java/allchive/server/api/search/model/enums/ArchivingType.java new file mode 100644 index 00000000..571e9b36 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/search/model/enums/ArchivingType.java @@ -0,0 +1,7 @@ +package allchive.server.api.search.model.enums; + +public enum ArchivingType { + ALL, + MY, + COMMUNITY +} diff --git a/Api/src/main/java/allchive/server/api/search/service/GetLatestSearchListUseCase.java b/Api/src/main/java/allchive/server/api/search/service/GetLatestSearchListUseCase.java new file mode 100644 index 00000000..a392af40 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/search/service/GetLatestSearchListUseCase.java @@ -0,0 +1,27 @@ +package allchive.server.api.search.service; + + +import allchive.server.api.config.security.SecurityUtil; +import allchive.server.api.search.model.dto.response.SearchListResponse; +import allchive.server.core.annotation.UseCase; +import allchive.server.domain.domains.search.adaptor.LatestSearchAdaptor; +import allchive.server.domain.domains.search.domain.LatestSearch; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.transaction.annotation.Transactional; + +@UseCase +@RequiredArgsConstructor +public class GetLatestSearchListUseCase { + private final LatestSearchAdaptor latestSearchAdaptor; + + @Transactional(readOnly = true) + public SearchListResponse execute() { + Long userId = SecurityUtil.getCurrentUserId(); + List<String> keywords = + latestSearchAdaptor.findAllByUserIdOrderByCreatedAt(userId).stream() + .map(LatestSearch::getKeyword) + .toList(); + return SearchListResponse.from(keywords); + } +} diff --git a/Api/src/main/java/allchive/server/api/search/service/GetRelativeSearchListUseCase.java b/Api/src/main/java/allchive/server/api/search/service/GetRelativeSearchListUseCase.java new file mode 100644 index 00000000..744c1305 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/search/service/GetRelativeSearchListUseCase.java @@ -0,0 +1,41 @@ +package allchive.server.api.search.service; + +import static allchive.server.core.consts.AllchiveConst.SEARCH_KEY; +import static jodd.util.StringPool.ASTERISK; + +import allchive.server.api.search.model.dto.request.SearchRequest; +import allchive.server.api.search.model.dto.response.SearchListResponse; +import allchive.server.core.annotation.UseCase; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.StringUtils; +import org.springframework.data.redis.core.*; +import org.springframework.transaction.annotation.Transactional; + +@UseCase +@RequiredArgsConstructor +public class GetRelativeSearchListUseCase { + private final RedisTemplate<String, String> redisTemplate; + + @Transactional + public SearchListResponse execute(SearchRequest request) { + ZSetOperations<String, String> zSetOperations = redisTemplate.opsForZSet(); + List<String> autoCompleteList = new ArrayList<>(); + Long rank = zSetOperations.rank(SEARCH_KEY, request.getKeyword()); + if (rank != null) { + Set<String> rangeList = zSetOperations.range(SEARCH_KEY, rank, rank + 1000); + autoCompleteList = getAutoCompleteList(rangeList, request.getKeyword()); + } + return SearchListResponse.from(autoCompleteList); + } + + private List<String> getAutoCompleteList(Set<String> rangeList, String keyword) { + return rangeList.stream() + .filter(value -> value.endsWith(ASTERISK) && value.startsWith(keyword)) + .map(value -> StringUtils.removeEnd(value, ASTERISK)) + .limit(5) + .toList(); + } +} diff --git a/Api/src/main/java/allchive/server/api/search/service/RenewalTitleDataUseCase.java b/Api/src/main/java/allchive/server/api/search/service/RenewalTitleDataUseCase.java new file mode 100644 index 00000000..3f204ba7 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/search/service/RenewalTitleDataUseCase.java @@ -0,0 +1,49 @@ +package allchive.server.api.search.service; + +import static allchive.server.core.consts.AllchiveConst.ASTERISK; +import static allchive.server.core.consts.AllchiveConst.SEARCH_KEY; + +import allchive.server.core.annotation.UseCase; +import allchive.server.domain.domains.archiving.adaptor.ArchivingAdaptor; +import allchive.server.domain.domains.archiving.domain.Archiving; +import java.util.HashSet; +import java.util.Set; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.transaction.annotation.Transactional; + +@UseCase +@Slf4j +@RequiredArgsConstructor +public class RenewalTitleDataUseCase { + private final ArchivingAdaptor archivingAdaptor; + private final RedisTemplate<String, String> redisTemplate; + + private static final int TIME_LIMIT = 1; + + @Scheduled(cron = "0 0 3 * * *") + @Transactional(readOnly = true) + public void executeSchedule() { + log.info("renewal title scheduler on"); + redisTemplate.delete(SEARCH_KEY); + Set<Archiving> archivings = + new HashSet<>(archivingAdaptor.findAllByPublicStatus(Boolean.TRUE)); + archivings.forEach( + archiving -> { + redisTemplate + .opsForZSet() + .add(SEARCH_KEY, archiving.getTitle().trim() + ASTERISK, 0); + for (int index = 1; index < archiving.getTitle().length(); index++) { + redisTemplate + .opsForZSet() + .add( + SEARCH_KEY, + archiving.getTitle().trim().substring(0, index - 1), + 0); + } + }); + log.info("renewal title scheduler off"); + } +} diff --git a/Api/src/main/java/allchive/server/api/search/service/SearchArchivingUseCase.java b/Api/src/main/java/allchive/server/api/search/service/SearchArchivingUseCase.java new file mode 100644 index 00000000..df4bc9ea --- /dev/null +++ b/Api/src/main/java/allchive/server/api/search/service/SearchArchivingUseCase.java @@ -0,0 +1,89 @@ +package allchive.server.api.search.service; + + +import allchive.server.api.archiving.model.dto.response.ArchivingResponse; +import allchive.server.api.common.slice.SliceResponse; +import allchive.server.api.config.security.SecurityUtil; +import allchive.server.api.search.model.dto.request.SearchRequest; +import allchive.server.api.search.model.dto.response.SearchResponse; +import allchive.server.api.search.model.enums.ArchivingType; +import allchive.server.core.annotation.UseCase; +import allchive.server.domain.domains.archiving.adaptor.ArchivingAdaptor; +import allchive.server.domain.domains.block.adaptor.BlockAdaptor; +import allchive.server.domain.domains.block.domain.Block; +import allchive.server.domain.domains.search.adaptor.LatestSearchAdaptor; +import allchive.server.domain.domains.search.domain.LatestSearch; +import allchive.server.domain.domains.search.service.LatestSearchDomainService; +import allchive.server.domain.domains.user.adaptor.ScrapAdaptor; +import allchive.server.domain.domains.user.domain.Scrap; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; +import org.springframework.transaction.annotation.Transactional; + +@UseCase +@RequiredArgsConstructor +public class SearchArchivingUseCase { + private final ArchivingAdaptor archivingAdaptor; + private final ScrapAdaptor scrapAdaptor; + private final BlockAdaptor blockAdaptor; + private final LatestSearchAdaptor latestSearchAdaptor; + private final LatestSearchDomainService latestSearchDomainService; + + @Transactional + public SearchResponse execute(Pageable pageable, ArchivingType type, SearchRequest request) { + Long userId = SecurityUtil.getCurrentUserId(); + SliceResponse<ArchivingResponse> my = null; + SliceResponse<ArchivingResponse> community = null; + renewalLatestSearch(userId, request.getKeyword()); + switch (type) { + case ALL -> { + my = getMyArchivings(userId, request.getKeyword(), pageable); + community = getCommunityArchivings(userId, request.getKeyword(), pageable); + return SearchResponse.forAll(my, community); + } + case MY -> { + my = getMyArchivings(userId, request.getKeyword(), pageable); + return SearchResponse.forMy(my); + } + case COMMUNITY -> { + community = getCommunityArchivings(userId, request.getKeyword(), pageable); + return SearchResponse.forCommunity(community); + } + } + return null; + } + + private void renewalLatestSearch(Long userId, String keyword) { + List<LatestSearch> searches = latestSearchAdaptor.findAllByUserIdOrderByCreatedAt(userId); + if (searches.size() == 5) { + latestSearchDomainService.delete(searches.get(0)); + } + LatestSearch newSearch = LatestSearch.of(keyword, userId); + latestSearchDomainService.save(newSearch); + } + + private SliceResponse<ArchivingResponse> getMyArchivings( + Long userId, String keyword, Pageable pageable) { + Slice<ArchivingResponse> archivingSlices = + archivingAdaptor + .querySliceArchivingByUserIdAndKeywords(userId, keyword, pageable) + .map(archiving -> ArchivingResponse.of(archiving, Boolean.FALSE)); + return SliceResponse.of(archivingSlices); + } + + private SliceResponse<ArchivingResponse> getCommunityArchivings( + Long userId, String keyword, Pageable pageable) { + List<Long> archivingIdList = + scrapAdaptor.findAllByUserId(userId).stream().map(Scrap::getArchivingId).toList(); + List<Long> blockList = + blockAdaptor.findByBlockFrom(userId).stream().map(Block::getBlockUser).toList(); + Slice<ArchivingResponse> archivingSlices = + archivingAdaptor + .querySliceArchivingByKeywordExceptBlock( + archivingIdList, blockList, keyword, pageable) + .map(archiving -> ArchivingResponse.of(archiving, Boolean.FALSE)); + return SliceResponse.of(archivingSlices); + } +} diff --git a/Core/src/main/java/allchive/server/core/consts/AllchiveConst.java b/Core/src/main/java/allchive/server/core/consts/AllchiveConst.java index 9b3973d2..2a762c23 100644 --- a/Core/src/main/java/allchive/server/core/consts/AllchiveConst.java +++ b/Core/src/main/java/allchive/server/core/consts/AllchiveConst.java @@ -37,4 +37,7 @@ public class AllchiveConst { public static final String[] SwaggerPatterns = { "/swagger-resources/**", "/swagger-ui/**", "/v3/api-docs/**", "/v3/api-docs", }; + + public static final String SEARCH_KEY = "ARCHIVING_TITLE"; + public static final String ASTERISK = "*"; } diff --git a/Domain/src/main/java/allchive/server/domain/domains/archiving/adaptor/ArchivingAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/archiving/adaptor/ArchivingAdaptor.java index c892093d..44fb4161 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/archiving/adaptor/ArchivingAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/archiving/adaptor/ArchivingAdaptor.java @@ -80,4 +80,20 @@ public List<Archiving> findAllByUserIdAndDeleted(Long userId) { public void deleteAllById(List<Long> archivingIds) { archivingRepository.deleteAllById(archivingIds); } + + public Slice<Archiving> querySliceArchivingByUserIdAndKeywords( + Long userId, String keyword, Pageable pageable) { + return archivingRepository.querySliceArchivingByUserIdAndKeywords( + userId, keyword, pageable); + } + + public Slice<Archiving> querySliceArchivingByKeywordExceptBlock( + List<Long> archivingIdList, List<Long> blockList, String keyword, Pageable pageable) { + return archivingRepository.querySliceArchivingByKeywordExceptBlock( + archivingIdList, blockList, keyword, pageable); + } + + public List<Archiving> findAllByPublicStatus(Boolean publicStatus) { + return archivingRepository.findAllByPublicStatus(publicStatus); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingCustomRepository.java b/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingCustomRepository.java index 0385366e..0b98371b 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingCustomRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingCustomRepository.java @@ -19,4 +19,10 @@ Slice<Archiving> querySliceArchivingIn( List<Archiving> queryArchivingByUserId(Long userId); boolean queryArchivingExistById(Long archivingId); + + Slice<Archiving> querySliceArchivingByUserIdAndKeywords( + Long userId, String keyword, Pageable pageable); + + Slice<Archiving> querySliceArchivingByKeywordExceptBlock( + List<Long> archivingIdList, List<Long> blockList, String keyword, Pageable pageable); } diff --git a/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingCustomRepositoryImpl.java b/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingCustomRepositoryImpl.java index b7d8ed8f..04a6b873 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingCustomRepositoryImpl.java +++ b/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingCustomRepositoryImpl.java @@ -92,6 +92,39 @@ public boolean queryArchivingExistById(Long archivingId) { return fetchOne != null; } + @Override + public Slice<Archiving> querySliceArchivingByUserIdAndKeywords( + Long userId, String keyword, Pageable pageable) { + List<Archiving> archivings = + queryFactory + .selectFrom(archiving) + .where(userIdEq(userId), titleContain(keyword)) + .orderBy(createdAtDesc()) + .offset(pageable.getOffset()) + .limit(pageable.getPageSize() + 1) + .fetch(); + return SliceUtil.toSlice(archivings, pageable); + } + + @Override + public Slice<Archiving> querySliceArchivingByKeywordExceptBlock( + List<Long> archivingIdList, List<Long> blockList, String keyword, Pageable pageable) { + List<Archiving> archivings = + queryFactory + .select(archiving) + .from(archiving) + .where( + userIdNotIn(blockList), + publicStatusTrue(), + deleteStatusFalse(), + titleContain(keyword)) + .orderBy(scrabListDesc(archivingIdList), scrapCntDesc(), createdAtDesc()) + .offset(pageable.getOffset()) + .limit(pageable.getPageSize() + 1) + .fetch(); + return SliceUtil.toSlice(archivings, pageable); + } + private BooleanExpression userIdNotIn(List<Long> blockList) { return archiving.userId.notIn(blockList); } @@ -123,6 +156,10 @@ private BooleanExpression archivingIdEq(Long archivingId) { return archiving.id.eq(archivingId); } + private BooleanExpression titleContain(String keyword) { + return archiving.title.contains(keyword); + } + private OrderSpecifier<Long> scrabListDesc(List<Long> archivingIdList) { NumberExpression<Long> pinStatus = new CaseBuilder().when(archiving.id.in(archivingIdList)).then(1L).otherwise(0L); diff --git a/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingRepository.java b/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingRepository.java index 391779ef..87ce7a03 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingRepository.java @@ -12,4 +12,6 @@ public interface ArchivingRepository List<Archiving> findAllByIdIn(List<Long> ids); List<Archiving> findAllByUserIdAndDeleteStatus(Long userId, boolean deleteStatus); + + List<Archiving> findAllByPublicStatus(Boolean publicStatus); } diff --git a/Domain/src/main/java/allchive/server/domain/domains/search/adaptor/LatestSearchAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/search/adaptor/LatestSearchAdaptor.java new file mode 100644 index 00000000..bc354d21 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/search/adaptor/LatestSearchAdaptor.java @@ -0,0 +1,26 @@ +package allchive.server.domain.domains.search.adaptor; + + +import allchive.server.core.annotation.Adaptor; +import allchive.server.domain.domains.search.domain.LatestSearch; +import allchive.server.domain.domains.search.repository.LatestSearchRepository; +import java.util.List; +import lombok.RequiredArgsConstructor; + +@Adaptor +@RequiredArgsConstructor +public class LatestSearchAdaptor { + private final LatestSearchRepository latestSearchRepository; + + public List<LatestSearch> findAllByUserIdOrderByCreatedAt(Long userId) { + return latestSearchRepository.findAllByUserIdOrderByCreatedAt(userId); + } + + public void delete(LatestSearch latestSearch) { + latestSearchRepository.delete(latestSearch); + } + + public void save(LatestSearch newSearch) { + latestSearchRepository.save(newSearch); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/search/domain/LatestSearch.java b/Domain/src/main/java/allchive/server/domain/domains/search/domain/LatestSearch.java new file mode 100644 index 00000000..78b88f36 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/search/domain/LatestSearch.java @@ -0,0 +1,33 @@ +package allchive.server.domain.domains.search.domain; + + +import allchive.server.domain.common.model.BaseTimeEntity; +import javax.persistence.*; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +/** 레디스로 전환 고려? * */ +@Getter +@Table(name = "tbl_latest_search") +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class LatestSearch extends BaseTimeEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String keyword; + private Long userId; + + @Builder + private LatestSearch(String keyword, Long userId) { + this.keyword = keyword; + this.userId = userId; + } + + public static LatestSearch of(String keyword, Long userId) { + return LatestSearch.builder().keyword(keyword).userId(userId).build(); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/search/repository/LatestSearchRepository.java b/Domain/src/main/java/allchive/server/domain/domains/search/repository/LatestSearchRepository.java new file mode 100644 index 00000000..782ea8b6 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/search/repository/LatestSearchRepository.java @@ -0,0 +1,10 @@ +package allchive.server.domain.domains.search.repository; + + +import allchive.server.domain.domains.search.domain.LatestSearch; +import java.util.List; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface LatestSearchRepository extends JpaRepository<LatestSearch, Long> { + List<LatestSearch> findAllByUserIdOrderByCreatedAt(Long userId); +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/search/service/LatestSearchDomainService.java b/Domain/src/main/java/allchive/server/domain/domains/search/service/LatestSearchDomainService.java new file mode 100644 index 00000000..0d9e1407 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/search/service/LatestSearchDomainService.java @@ -0,0 +1,21 @@ +package allchive.server.domain.domains.search.service; + + +import allchive.server.core.annotation.DomainService; +import allchive.server.domain.domains.search.adaptor.LatestSearchAdaptor; +import allchive.server.domain.domains.search.domain.LatestSearch; +import lombok.RequiredArgsConstructor; + +@DomainService +@RequiredArgsConstructor +public class LatestSearchDomainService { + private final LatestSearchAdaptor latestSearchAdaptor; + + public void delete(LatestSearch latestSearch) { + latestSearchAdaptor.delete(latestSearch); + } + + public void save(LatestSearch newSearch) { + latestSearchAdaptor.save(newSearch); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/UserAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/UserAdaptor.java index 01fbe90e..976effb1 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/UserAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/UserAdaptor.java @@ -6,9 +6,8 @@ import allchive.server.domain.domains.user.domain.enums.OauthInfo; import allchive.server.domain.domains.user.exception.exceptions.UserNotFoundException; import allchive.server.domain.domains.user.repository.UserRepository; -import lombok.RequiredArgsConstructor; - import java.util.List; +import lombok.RequiredArgsConstructor; @Adaptor @RequiredArgsConstructor diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/repository/UserRepository.java b/Domain/src/main/java/allchive/server/domain/domains/user/repository/UserRepository.java index 7a2e24af..d8785e5c 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/repository/UserRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/repository/UserRepository.java @@ -3,7 +3,6 @@ import allchive.server.domain.domains.user.domain.User; import allchive.server.domain.domains.user.domain.enums.OauthInfo; - import java.util.List; import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; diff --git a/Infrastructure/src/main/java/allchive/server/infrastructure/redis/config/RedisConfig.java b/Infrastructure/src/main/java/allchive/server/infrastructure/redis/config/RedisConfig.java index be0883a7..346a15d3 100644 --- a/Infrastructure/src/main/java/allchive/server/infrastructure/redis/config/RedisConfig.java +++ b/Infrastructure/src/main/java/allchive/server/infrastructure/redis/config/RedisConfig.java @@ -10,7 +10,9 @@ import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration; import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; import org.springframework.data.redis.core.RedisKeyValueAdapter; +import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.repository.configuration.EnableRedisRepositories; +import org.springframework.data.redis.serializer.StringRedisSerializer; @EnableRedisRepositories( basePackages = "allchive.server", @@ -42,4 +44,13 @@ public RedisConnectionFactory redisConnectionFactory() { .build(); return new LettuceConnectionFactory(redisConfig, clientConfig); } + + @Bean + public RedisTemplate<String, Object> redisTemplate() { + RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>(); + redisTemplate.setConnectionFactory(redisConnectionFactory()); + redisTemplate.setKeySerializer(new StringRedisSerializer()); + redisTemplate.setValueSerializer(new StringRedisSerializer()); + return redisTemplate; + } } From 832a93862c9f41b792f52698843b915d9e335d18 Mon Sep 17 00:00:00 2001 From: Sanghoon Jeong <67852689+wjdtkdgns@users.noreply.github.com> Date: Tue, 18 Jul 2023 22:56:53 +0900 Subject: [PATCH 36/41] =?UTF-8?q?[feat]=20cloudwatch=20=EC=85=8B=ED=8C=85?= =?UTF-8?q?=20#39=20(#42)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose.dev.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index c26bada3..dcf1f3fb 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -11,6 +11,12 @@ services: - TZ=Asia/Seoul expose: - 8080 + logging: + driver: awslogs + options: + awslogs-group: "all-chive-dev" + awslogs-region: "ap-northeast-2" + awslogs-stream: "server" nginx: depends_on: From 985e194865127714a4ff5f7e82c96e84228019b3 Mon Sep 17 00:00:00 2001 From: Sanghoon Jeong <67852689+wjdtkdgns@users.noreply.github.com> Date: Wed, 19 Jul 2023 13:43:26 +0900 Subject: [PATCH 37/41] =?UTF-8?q?[refac]=20auth=20=EB=B6=80=EB=B6=84=20?= =?UTF-8?q?=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81=20=EB=B0=8F=20response=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20(#44)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [refac] oauthHelper 분리 및 회원가입 response 변경 #43 * [feat] 회원 탈퇴 로직 미완 #43 * [refac] url 처리 로직 추가 #43 * [refac] module 의존성 정리 #40 * [refac] 회원 탈퇴 로직 추가 #43 --- Api/build.gradle | 3 - .../service/UpdateArchivingScrapUseCase.java | 2 +- .../api/auth/controller/AuthController.java | 7 +- .../model/dto/request/RegisterRequest.java | 14 ++- .../api/auth/service/OauthLinkUseCase.java | 25 +----- .../api/auth/service/OauthLoginUseCase.java | 49 ++--------- .../auth/service/OauthRegisterUseCase.java | 25 ++---- .../api/auth/service/TokenRefreshUseCase.java | 2 +- .../api/auth/service/WithdrawUserUseCase.java | 69 +++++++++++---- ...OAuthHelper.java => AppleOauthHelper.java} | 11 ++- .../api/auth/service/helper/OauthHelper.java | 88 +++++++++++++++++++ .../api/block/service/CreateBlockUseCase.java | 2 +- .../api/block/service/DeleteBlockUseCase.java | 2 +- .../api/config/security/SecurityConfig.java | 2 + .../api/report/model/mapper/ReportMapper.java | 8 +- .../report/service/CreateReportUseCase.java | 18 +++- .../api/user/service/GetUserInfoUseCase.java | 2 +- .../user/service/GetUserProfileUseCase.java | 2 +- .../server/core/annotation/ValidEnum.java | 3 +- Domain/build.gradle | 1 - .../domains/block/adaptor/BlockAdaptor.java | 4 + .../repository/BlockCustomRepository.java | 2 + .../repository/BlockCustomRepositoryImpl.java | 11 +++ .../block/service/BlockDomainService.java | 4 + .../content/adaptor/ContentAdaptor.java | 4 + .../adaptor/ContentTagGroupAdaptor.java | 4 + .../domains/content/adaptor/TagAdaptor.java | 8 ++ .../content/repository/ContentRepository.java | 2 + .../repository/ContentTagGroupRepository.java | 2 + .../content/repository/TagRepository.java | 2 + .../content/service/ContentDomainService.java | 4 + .../service/ContentTagGroupDomainService.java | 4 + .../content/service/TagDomainService.java | 6 ++ .../recycle/adaptor/RecycleAdaptor.java | 4 + .../recycle/repository/RecycleRepository.java | 2 + .../recycle/service/RecycleDomainService.java | 4 + .../domains/report/adaptor/ReportAdaptor.java | 4 + .../domain/domains/report/domain/Report.java | 9 +- .../report/repository/ReportRepository.java | 4 +- .../report/service/ReportDomainService.java | 4 + .../search/adaptor/LatestSearchAdaptor.java | 4 + .../repository/LatestSearchRepository.java | 2 + .../service/LatestSearchDomainService.java | 4 + .../domains/user/adaptor/ScrapAdaptor.java | 4 + .../domains/user/adaptor/UserAdaptor.java | 2 +- .../domain/domains/user/domain/User.java | 14 ++- .../user/repository/ScrapRepository.java | 2 + .../user/service/ScrapDomainService.java | 4 + .../user/service/UserDomainService.java | 14 ++- .../domains/user/validator/UserValidator.java | 2 +- 50 files changed, 338 insertions(+), 137 deletions(-) rename Api/src/main/java/allchive/server/api/auth/service/helper/{AppleOAuthHelper.java => AppleOauthHelper.java} (92%) create mode 100644 Api/src/main/java/allchive/server/api/auth/service/helper/OauthHelper.java diff --git a/Api/build.gradle b/Api/build.gradle index 92930493..93615c1b 100644 --- a/Api/build.gradle +++ b/Api/build.gradle @@ -11,12 +11,9 @@ repositories { dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' - implementation 'org.springframework.boot:spring-boot-starter-validation' implementation 'org.springframework.boot:spring-boot-starter-security' // swagger -// implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.4' -// implementation 'org.springdoc:springdoc-openapi-starter-webmvc-api:2.0.4' implementation 'org.springdoc:springdoc-openapi-ui:1.6.12' implementation project(':Domain') diff --git a/Api/src/main/java/allchive/server/api/archiving/service/UpdateArchivingScrapUseCase.java b/Api/src/main/java/allchive/server/api/archiving/service/UpdateArchivingScrapUseCase.java index 5e8bcd0d..4bce3f22 100644 --- a/Api/src/main/java/allchive/server/api/archiving/service/UpdateArchivingScrapUseCase.java +++ b/Api/src/main/java/allchive/server/api/archiving/service/UpdateArchivingScrapUseCase.java @@ -27,7 +27,7 @@ public void execute(Long archivingId, Boolean cancel) { archivingValidator.validateExistById(archivingId); Long userId = SecurityUtil.getCurrentUserId(); archivingValidator.validateDeleteStatus(archivingId, userId); - User user = userAdaptor.queryUserById(userId); + User user = userAdaptor.findUserById(userId); if (cancel) { scrapDomainService.deleteScrapByUserAndArchivingId(user, archivingId); archivingDomainService.updateScrapCount(archivingId, -1); diff --git a/Api/src/main/java/allchive/server/api/auth/controller/AuthController.java b/Api/src/main/java/allchive/server/api/auth/controller/AuthController.java index 87e83022..6cb01deb 100644 --- a/Api/src/main/java/allchive/server/api/auth/controller/AuthController.java +++ b/Api/src/main/java/allchive/server/api/auth/controller/AuthController.java @@ -24,8 +24,11 @@ public class AuthController { @Operation(summary = "회원탈퇴를 합니다.") @DeleteMapping("/withdrawal/{provider}") - public void withDrawUser(@PathVariable OauthProvider provider) { - withdrawUserUseCase.execute(provider); + public void withDrawUser( + @PathVariable OauthProvider provider, + @RequestParam(required = false, name = "appleAccessToken", value = "") + String appleAccessToken) { + withdrawUserUseCase.execute(provider, appleAccessToken); } @Operation(summary = "로그아웃을 합니다.") diff --git a/Api/src/main/java/allchive/server/api/auth/model/dto/request/RegisterRequest.java b/Api/src/main/java/allchive/server/api/auth/model/dto/request/RegisterRequest.java index 039fd65c..fe3d10f2 100644 --- a/Api/src/main/java/allchive/server/api/auth/model/dto/request/RegisterRequest.java +++ b/Api/src/main/java/allchive/server/api/auth/model/dto/request/RegisterRequest.java @@ -1,7 +1,11 @@ package allchive.server.api.auth.model.dto.request; +import allchive.server.core.annotation.ValidEnum; +import allchive.server.domain.domains.archiving.domain.enums.Category; +import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.Schema; +import java.util.List; import javax.validation.constraints.NotBlank; import lombok.Getter; import lombok.NoArgsConstructor; @@ -10,12 +14,16 @@ @NoArgsConstructor public class RegisterRequest { @Schema( - defaultValue = "staging/archiving/1/d241218a-a64c-4443-8aa4-ce98017a3d12", + defaultValue = + "https://asset.allchive.co.kr/staging/archiving/1/d241218a-a64c-4443-8aa4-ce98017a3d12", description = "프로필 이미지 url") - @NotBlank(message = "프로필 이미지 key를 입력하세요") - private String profileImgKey; + @NotBlank(message = "프로필 이미지 url을 입력하세요") + private String profileImgUrl; @Schema(defaultValue = "닉네임", description = "닉네임") @NotBlank(message = "닉네임을 입력하세요") private String nickname; + + @ArraySchema(schema = @Schema(description = "관심 주제", defaultValue = "FOOD")) + private List<@ValidEnum(target = Category.class) Category> categories; } diff --git a/Api/src/main/java/allchive/server/api/auth/service/OauthLinkUseCase.java b/Api/src/main/java/allchive/server/api/auth/service/OauthLinkUseCase.java index a746502f..2892db3a 100644 --- a/Api/src/main/java/allchive/server/api/auth/service/OauthLinkUseCase.java +++ b/Api/src/main/java/allchive/server/api/auth/service/OauthLinkUseCase.java @@ -2,38 +2,21 @@ import allchive.server.api.auth.model.dto.response.OauthLoginLinkResponse; -import allchive.server.api.auth.service.helper.AppleOAuthHelper; -import allchive.server.api.auth.service.helper.KakaoOauthHelper; +import allchive.server.api.auth.service.helper.OauthHelper; import allchive.server.core.annotation.UseCase; -import allchive.server.core.error.exception.InvalidOauthProviderException; import allchive.server.domain.domains.user.domain.enums.OauthProvider; import lombok.RequiredArgsConstructor; @UseCase @RequiredArgsConstructor public class OauthLinkUseCase { - private final KakaoOauthHelper kakaoOauthHelper; - private final AppleOAuthHelper appleOAuthHelper; + private final OauthHelper oauthHelper; public OauthLoginLinkResponse getOauthLinkDev(OauthProvider provider) { - switch (provider) { - case KAKAO: - return new OauthLoginLinkResponse(kakaoOauthHelper.getKaKaoOauthLinkDev()); - case APPLE: - return new OauthLoginLinkResponse(appleOAuthHelper.getAppleOauthLinkDev()); - default: - throw InvalidOauthProviderException.EXCEPTION; - } + return oauthHelper.getOauthLinkDev(provider); } public OauthLoginLinkResponse getOauthLink(OauthProvider provider, String referer) { - switch (provider) { - case KAKAO: - return new OauthLoginLinkResponse(kakaoOauthHelper.getKaKaoOauthLink(referer)); - case APPLE: - return new OauthLoginLinkResponse(appleOAuthHelper.getAppleOAuthLink(referer)); - default: - throw InvalidOauthProviderException.EXCEPTION; - } + return oauthHelper.getOauthLink(provider, referer); } } diff --git a/Api/src/main/java/allchive/server/api/auth/service/OauthLoginUseCase.java b/Api/src/main/java/allchive/server/api/auth/service/OauthLoginUseCase.java index 7de13592..2886b644 100644 --- a/Api/src/main/java/allchive/server/api/auth/service/OauthLoginUseCase.java +++ b/Api/src/main/java/allchive/server/api/auth/service/OauthLoginUseCase.java @@ -3,11 +3,9 @@ import allchive.server.api.auth.model.dto.response.OauthSignInResponse; import allchive.server.api.auth.model.dto.response.OauthTokenResponse; -import allchive.server.api.auth.service.helper.AppleOAuthHelper; -import allchive.server.api.auth.service.helper.KakaoOauthHelper; +import allchive.server.api.auth.service.helper.OauthHelper; import allchive.server.api.auth.service.helper.TokenGenerateHelper; import allchive.server.core.annotation.UseCase; -import allchive.server.core.error.exception.InvalidOauthProviderException; import allchive.server.domain.domains.user.domain.User; import allchive.server.domain.domains.user.domain.enums.OauthInfo; import allchive.server.domain.domains.user.domain.enums.OauthProvider; @@ -17,13 +15,13 @@ @UseCase @RequiredArgsConstructor public class OauthLoginUseCase { - private final KakaoOauthHelper kakaoOauthHelper; - private final AppleOAuthHelper appleOAuthHelper; + private final OauthHelper oauthHelper; private final UserDomainService userDomainService; private final TokenGenerateHelper tokenGenerateHelper; public OauthSignInResponse loginWithCode(OauthProvider provider, String code, String referer) { - final OauthTokenResponse oauthTokenResponse = getCredential(provider, code, referer); + final OauthTokenResponse oauthTokenResponse = + oauthHelper.getCredential(provider, code, referer); return processLoginWithIdToken(provider, oauthTokenResponse.getIdToken()); } @@ -32,12 +30,12 @@ public OauthSignInResponse loginWithIdToken(OauthProvider provider, String idTok } public OauthSignInResponse devLogin(OauthProvider provider, String code) { - final OauthTokenResponse oauthTokenResponse = getCredentialDev(provider, code); + final OauthTokenResponse oauthTokenResponse = oauthHelper.getCredentialDev(provider, code); return processLoginWithIdToken(provider, oauthTokenResponse.getIdToken()); } private OauthSignInResponse processLoginWithIdToken(OauthProvider provider, String idToken) { - final OauthInfo oauthInfo = getOauthInfo(provider, idToken); + final OauthInfo oauthInfo = oauthHelper.getOauthInfo(provider, idToken); if (userDomainService.checkUserCanLogin(oauthInfo)) { User user = userDomainService.loginUser(oauthInfo); return tokenGenerateHelper.execute(user); @@ -45,39 +43,4 @@ private OauthSignInResponse processLoginWithIdToken(OauthProvider provider, Stri return OauthSignInResponse.cannotLogin(idToken); } } - - /** idtoken 가져오기 * */ - private OauthTokenResponse getCredential(OauthProvider provider, String code, String referer) { - switch (provider) { - case KAKAO: - return OauthTokenResponse.from(kakaoOauthHelper.getKakaoOauthToken(code, referer)); - case APPLE: - return OauthTokenResponse.from(appleOAuthHelper.getAppleOAuthToken(code, referer)); - default: - throw InvalidOauthProviderException.EXCEPTION; - } - } - - private OauthTokenResponse getCredentialDev(OauthProvider provider, String code) { - switch (provider) { - case KAKAO: - return OauthTokenResponse.from(kakaoOauthHelper.getKakaoOauthTokenDev(code)); - case APPLE: - return OauthTokenResponse.from(appleOAuthHelper.getAppleOAuthTokenDev(code)); - default: - throw InvalidOauthProviderException.EXCEPTION; - } - } - - /** idtoken 분석 * */ - private OauthInfo getOauthInfo(OauthProvider provider, String idToken) { - switch (provider) { - case KAKAO: - return kakaoOauthHelper.getKakaoOauthInfoByIdToken(idToken); - case APPLE: - return appleOAuthHelper.getAppleOAuthInfoByIdToken(idToken); - default: - throw InvalidOauthProviderException.EXCEPTION; - } - } } diff --git a/Api/src/main/java/allchive/server/api/auth/service/OauthRegisterUseCase.java b/Api/src/main/java/allchive/server/api/auth/service/OauthRegisterUseCase.java index f1259baf..377dc16d 100644 --- a/Api/src/main/java/allchive/server/api/auth/service/OauthRegisterUseCase.java +++ b/Api/src/main/java/allchive/server/api/auth/service/OauthRegisterUseCase.java @@ -3,11 +3,10 @@ import allchive.server.api.auth.model.dto.request.RegisterRequest; import allchive.server.api.auth.model.dto.response.OauthRegisterResponse; -import allchive.server.api.auth.service.helper.AppleOAuthHelper; -import allchive.server.api.auth.service.helper.KakaoOauthHelper; +import allchive.server.api.auth.service.helper.OauthHelper; import allchive.server.api.auth.service.helper.TokenGenerateHelper; +import allchive.server.api.common.util.UrlUtil; import allchive.server.core.annotation.UseCase; -import allchive.server.core.error.exception.InvalidOauthProviderException; import allchive.server.domain.domains.user.domain.User; import allchive.server.domain.domains.user.domain.enums.OauthInfo; import allchive.server.domain.domains.user.domain.enums.OauthProvider; @@ -17,31 +16,19 @@ @UseCase @RequiredArgsConstructor public class OauthRegisterUseCase { - private final KakaoOauthHelper kakaoOauthHelper; - private final AppleOAuthHelper appleOAuthHelper; + private final OauthHelper oauthHelper; private final UserDomainService userDomainService; private final TokenGenerateHelper tokenGenerateHelper; public OauthRegisterResponse execute( OauthProvider provider, String idToken, RegisterRequest registerRequest) { - final OauthInfo oauthInfo = getOauthInfo(provider, idToken); + final OauthInfo oauthInfo = oauthHelper.getOauthInfo(provider, idToken); final User user = userDomainService.registerUser( registerRequest.getNickname(), - registerRequest.getProfileImgKey(), + UrlUtil.convertUrlToKey(registerRequest.getProfileImgUrl()), + registerRequest.getCategories(), oauthInfo); return OauthRegisterResponse.from(tokenGenerateHelper.execute(user)); } - - /** idtoken 분석 * */ - private OauthInfo getOauthInfo(OauthProvider provider, String idToken) { - switch (provider) { - case KAKAO: - return kakaoOauthHelper.getKakaoOauthInfoByIdToken(idToken); - case APPLE: - return appleOAuthHelper.getAppleOAuthInfoByIdToken(idToken); - default: - throw InvalidOauthProviderException.EXCEPTION; - } - } } diff --git a/Api/src/main/java/allchive/server/api/auth/service/TokenRefreshUseCase.java b/Api/src/main/java/allchive/server/api/auth/service/TokenRefreshUseCase.java index db69553e..a0178221 100644 --- a/Api/src/main/java/allchive/server/api/auth/service/TokenRefreshUseCase.java +++ b/Api/src/main/java/allchive/server/api/auth/service/TokenRefreshUseCase.java @@ -22,7 +22,7 @@ public class TokenRefreshUseCase { public OauthRegisterResponse execute(String refreshToken) { RefreshTokenEntity oldToken = refreshTokenAdaptor.findTokenByRefreshToken(refreshToken); Long userId = jwtTokenProvider.parseRefreshToken(oldToken.getRefreshToken()); - User user = userAdaptor.queryUserById(userId); + User user = userAdaptor.findUserById(userId); return OauthRegisterResponse.from(tokenGenerateHelper.execute(user)); } } diff --git a/Api/src/main/java/allchive/server/api/auth/service/WithdrawUserUseCase.java b/Api/src/main/java/allchive/server/api/auth/service/WithdrawUserUseCase.java index 6adab4c3..3cc54328 100644 --- a/Api/src/main/java/allchive/server/api/auth/service/WithdrawUserUseCase.java +++ b/Api/src/main/java/allchive/server/api/auth/service/WithdrawUserUseCase.java @@ -1,42 +1,81 @@ package allchive.server.api.auth.service; -import allchive.server.api.auth.service.helper.AppleOAuthHelper; -import allchive.server.api.auth.service.helper.KakaoOauthHelper; +import allchive.server.api.auth.service.helper.OauthHelper; import allchive.server.api.config.security.SecurityUtil; import allchive.server.core.annotation.UseCase; import allchive.server.core.error.exception.InvalidOauthProviderException; +import allchive.server.domain.domains.archiving.adaptor.ArchivingAdaptor; +import allchive.server.domain.domains.archiving.domain.Archiving; +import allchive.server.domain.domains.block.service.BlockDomainService; +import allchive.server.domain.domains.content.adaptor.ContentAdaptor; +import allchive.server.domain.domains.content.adaptor.TagAdaptor; +import allchive.server.domain.domains.content.domain.Tag; +import allchive.server.domain.domains.content.service.ContentDomainService; +import allchive.server.domain.domains.content.service.ContentTagGroupDomainService; +import allchive.server.domain.domains.content.service.TagDomainService; +import allchive.server.domain.domains.recycle.service.RecycleDomainService; +import allchive.server.domain.domains.report.service.ReportDomainService; +import allchive.server.domain.domains.search.service.LatestSearchDomainService; import allchive.server.domain.domains.user.adaptor.RefreshTokenAdaptor; import allchive.server.domain.domains.user.adaptor.UserAdaptor; import allchive.server.domain.domains.user.domain.User; import allchive.server.domain.domains.user.domain.enums.OauthProvider; +import allchive.server.domain.domains.user.service.ScrapDomainService; import allchive.server.domain.domains.user.service.UserDomainService; import lombok.RequiredArgsConstructor; +import java.util.List; + @UseCase @RequiredArgsConstructor public class WithdrawUserUseCase { - private final KakaoOauthHelper kakaoOauthHelper; - private final AppleOAuthHelper appleOAuthHelper; private final UserAdaptor userAdaptor; + private final OauthHelper oauthHelper; private final RefreshTokenAdaptor refreshTokenAdaptor; + private final LatestSearchDomainService latestSearchDomainService; + private final ScrapDomainService scrapDomainService; + private final BlockDomainService blockDomainService; + private final ArchivingAdaptor archivingAdaptor; + private final TagAdaptor tagAdaptor; + private final ContentTagGroupDomainService contentTagGroupDomainService; + private final ContentDomainService contentDomainService; + private final TagDomainService tagDomainService; + private final RecycleDomainService recycleDomainService; + private final ReportDomainService reportDomainService; private final UserDomainService userDomainService; - public void execute(OauthProvider provider) { + public void execute(OauthProvider provider, String appleAccessToken) { Long userId = SecurityUtil.getCurrentUserId(); - User user = userAdaptor.queryUserById(userId); - // 우리쪽 탈퇴 - refreshTokenAdaptor.deleteTokenByUserId(userId); + User user = userAdaptor.findUserById(userId); // oauth쪽 탈퇴 + withdrawOauth(provider, appleAccessToken, user); + // 우리쪽 탈퇴 + withdrawService(userId, user); + } + + private void withdrawOauth(OauthProvider provider, String appleAccessToken, User user) { switch (provider) { - case KAKAO: - kakaoOauthHelper.withdrawKakaoOauthUser(user.getOauthInfo().getOid()); - break; - // case APPLE: - // appleOAuthHelper.withdrawAppleOauthUser(); - default: - throw InvalidOauthProviderException.EXCEPTION; + case KAKAO -> oauthHelper.withdraw(provider, user.getOauthInfo().getOid(), null); + case APPLE -> oauthHelper.withdraw(provider, null, appleAccessToken); + default -> throw InvalidOauthProviderException.EXCEPTION; } + + } + + private void withdrawService(Long userId, User user) { + refreshTokenAdaptor.deleteTokenByUserId(userId); + latestSearchDomainService.deleteAllByUserId(userId); + scrapDomainService.deleteAllByUser(user); + blockDomainService.queryDeleteBlockByBlockFromOrBlockUser(userId); + List<Archiving> archivingList = archivingAdaptor.findAllByUserId(userId); + List<Long> archivingId = archivingList.stream().map(Archiving::getId).toList(); + List<Tag> tagList = tagAdaptor.findAllByUserId(userId); + contentTagGroupDomainService.deleteAllByTagIn(tagList); + tagDomainService.deleteAll(tagList); + contentDomainService.deleteAllByArchivingIdIn(archivingId); + recycleDomainService.deleteAllByUserId(userId); + reportDomainService.deleteAllByReportedUserId(userId); userDomainService.deleteUserById(userId); } } diff --git a/Api/src/main/java/allchive/server/api/auth/service/helper/AppleOAuthHelper.java b/Api/src/main/java/allchive/server/api/auth/service/helper/AppleOauthHelper.java similarity index 92% rename from Api/src/main/java/allchive/server/api/auth/service/helper/AppleOAuthHelper.java rename to Api/src/main/java/allchive/server/api/auth/service/helper/AppleOauthHelper.java index 2dbd9acf..5bdb06e0 100644 --- a/Api/src/main/java/allchive/server/api/auth/service/helper/AppleOAuthHelper.java +++ b/Api/src/main/java/allchive/server/api/auth/service/helper/AppleOauthHelper.java @@ -16,7 +16,7 @@ @Helper @RequiredArgsConstructor -public class AppleOAuthHelper { +public class AppleOauthHelper { private final AppleOAuthProperties appleOAuthProperties; private final AppleOAuthClient appleOAuthClient; private final AppleOIDCClient appleOIDCClient; @@ -76,11 +76,10 @@ public OIDCDecodePayload getOIDCDecodePayload(String token) { } /** apple측 회원 탈퇴 * */ - // public void withdrawAppleOauthUser(String appleOAuthAccessToken) { - // appleOAuthClient.revoke( - // appleOAuthProperties.getClientId(), appleOAuthAccessToken, - // this.getClientSecret()); - // } + public void withdrawAppleOauthUser(String appleOAuthAccessToken) { + appleOAuthClient.revoke( + appleOAuthProperties.getClientId(), appleOAuthAccessToken, this.getClientSecret()); + } /** client secret 가져오기 * */ private String getClientSecret() { diff --git a/Api/src/main/java/allchive/server/api/auth/service/helper/OauthHelper.java b/Api/src/main/java/allchive/server/api/auth/service/helper/OauthHelper.java new file mode 100644 index 00000000..b76acbbb --- /dev/null +++ b/Api/src/main/java/allchive/server/api/auth/service/helper/OauthHelper.java @@ -0,0 +1,88 @@ +package allchive.server.api.auth.service.helper; + + +import allchive.server.api.auth.model.dto.response.OauthLoginLinkResponse; +import allchive.server.api.auth.model.dto.response.OauthTokenResponse; +import allchive.server.core.annotation.Helper; +import allchive.server.core.error.exception.InvalidOauthProviderException; +import allchive.server.domain.domains.user.domain.enums.OauthInfo; +import allchive.server.domain.domains.user.domain.enums.OauthProvider; +import lombok.RequiredArgsConstructor; + +@Helper +@RequiredArgsConstructor +public class OauthHelper { + private final KakaoOauthHelper kakaoOauthHelper; + private final AppleOauthHelper appleOauthHelper; + + /** oauth link 가져오기 * */ + public OauthLoginLinkResponse getOauthLinkDev(OauthProvider provider) { + switch (provider) { + case KAKAO: + return new OauthLoginLinkResponse(kakaoOauthHelper.getKaKaoOauthLinkDev()); + case APPLE: + return new OauthLoginLinkResponse(appleOauthHelper.getAppleOauthLinkDev()); + default: + throw InvalidOauthProviderException.EXCEPTION; + } + } + + public OauthLoginLinkResponse getOauthLink(OauthProvider provider, String referer) { + switch (provider) { + case KAKAO: + return new OauthLoginLinkResponse(kakaoOauthHelper.getKaKaoOauthLink(referer)); + case APPLE: + return new OauthLoginLinkResponse(appleOauthHelper.getAppleOAuthLink(referer)); + default: + throw InvalidOauthProviderException.EXCEPTION; + } + } + + /** idtoken 가져오기 * */ + public OauthTokenResponse getCredential(OauthProvider provider, String code, String referer) { + switch (provider) { + case KAKAO: + return OauthTokenResponse.from(kakaoOauthHelper.getKakaoOauthToken(code, referer)); + case APPLE: + return OauthTokenResponse.from(appleOauthHelper.getAppleOAuthToken(code, referer)); + default: + throw InvalidOauthProviderException.EXCEPTION; + } + } + + public OauthTokenResponse getCredentialDev(OauthProvider provider, String code) { + switch (provider) { + case KAKAO: + return OauthTokenResponse.from(kakaoOauthHelper.getKakaoOauthTokenDev(code)); + case APPLE: + return OauthTokenResponse.from(appleOauthHelper.getAppleOAuthTokenDev(code)); + default: + throw InvalidOauthProviderException.EXCEPTION; + } + } + + /** idtoken 분석 * */ + public OauthInfo getOauthInfo(OauthProvider provider, String idToken) { + switch (provider) { + case KAKAO: + return kakaoOauthHelper.getKakaoOauthInfoByIdToken(idToken); + case APPLE: + return appleOauthHelper.getAppleOAuthInfoByIdToken(idToken); + default: + throw InvalidOauthProviderException.EXCEPTION; + } + } + + /** 회원탈퇴 * */ + public void withdraw(OauthProvider provider, String oid, String appleAccessToken) { + switch (provider) { + case KAKAO: + kakaoOauthHelper.withdrawKakaoOauthUser(oid); + break; + case APPLE: + appleOauthHelper.withdrawAppleOauthUser(appleAccessToken); + default: + throw InvalidOauthProviderException.EXCEPTION; + } + } +} diff --git a/Api/src/main/java/allchive/server/api/block/service/CreateBlockUseCase.java b/Api/src/main/java/allchive/server/api/block/service/CreateBlockUseCase.java index f66bdbaa..6fef2c73 100644 --- a/Api/src/main/java/allchive/server/api/block/service/CreateBlockUseCase.java +++ b/Api/src/main/java/allchive/server/api/block/service/CreateBlockUseCase.java @@ -28,6 +28,6 @@ public BlockResponse execute(BlockRequest request) { blockValidator.validateNotMyself(userId, request.getUserId()); Block block = blockMapper.toEntity(userId, request.getUserId()); blockDomainService.save(block); - return BlockResponse.from(userAdaptor.queryUserById(request.getUserId()).getNickname()); + return BlockResponse.from(userAdaptor.findUserById(request.getUserId()).getNickname()); } } diff --git a/Api/src/main/java/allchive/server/api/block/service/DeleteBlockUseCase.java b/Api/src/main/java/allchive/server/api/block/service/DeleteBlockUseCase.java index fb829c6a..b61eec5c 100644 --- a/Api/src/main/java/allchive/server/api/block/service/DeleteBlockUseCase.java +++ b/Api/src/main/java/allchive/server/api/block/service/DeleteBlockUseCase.java @@ -23,6 +23,6 @@ public BlockResponse execute(BlockRequest request) { Long userId = SecurityUtil.getCurrentUserId(); blockValidator.validateExist(userId, request.getUserId()); blockDomainService.deleteByBlockFromAndBlockUser(userId, request.getUserId()); - return BlockResponse.from(userAdaptor.queryUserById(request.getUserId()).getNickname()); + return BlockResponse.from(userAdaptor.findUserById(request.getUserId()).getNickname()); } } diff --git a/Api/src/main/java/allchive/server/api/config/security/SecurityConfig.java b/Api/src/main/java/allchive/server/api/config/security/SecurityConfig.java index 42cb8ee5..cfd71a98 100644 --- a/Api/src/main/java/allchive/server/api/config/security/SecurityConfig.java +++ b/Api/src/main/java/allchive/server/api/config/security/SecurityConfig.java @@ -35,6 +35,8 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { .permitAll() .mvcMatchers("/auth/oauth/**") .permitAll() + .mvcMatchers("/user/nickname") + .permitAll() .mvcMatchers("/auth/token/refresh") .permitAll() .mvcMatchers("/**/health/**") diff --git a/Api/src/main/java/allchive/server/api/report/model/mapper/ReportMapper.java b/Api/src/main/java/allchive/server/api/report/model/mapper/ReportMapper.java index 3cc73d79..b86c9e1c 100644 --- a/Api/src/main/java/allchive/server/api/report/model/mapper/ReportMapper.java +++ b/Api/src/main/java/allchive/server/api/report/model/mapper/ReportMapper.java @@ -8,7 +8,7 @@ @Mapper public class ReportMapper { - public Report toEntity(CreateReportRequest request, ReportObjectType type, Long userId) { + public Report toEntity(CreateReportRequest request, ReportObjectType type, Long userId, Long reportedUserId) { Report report = null; switch (type) { case CONTENT -> report = @@ -18,7 +18,8 @@ public Report toEntity(CreateReportRequest request, ReportObjectType type, Long request.getReportedType(), request.getId(), null, - userId); + userId, + reportedUserId); case ARCHIVING -> report = Report.of( type, @@ -26,7 +27,8 @@ public Report toEntity(CreateReportRequest request, ReportObjectType type, Long request.getReportedType(), null, request.getId(), - userId); + userId, + reportedUserId); } return report; } diff --git a/Api/src/main/java/allchive/server/api/report/service/CreateReportUseCase.java b/Api/src/main/java/allchive/server/api/report/service/CreateReportUseCase.java index 1f58aa1a..07681e8b 100644 --- a/Api/src/main/java/allchive/server/api/report/service/CreateReportUseCase.java +++ b/Api/src/main/java/allchive/server/api/report/service/CreateReportUseCase.java @@ -5,7 +5,9 @@ import allchive.server.api.report.model.dto.request.CreateReportRequest; import allchive.server.api.report.model.mapper.ReportMapper; import allchive.server.core.annotation.UseCase; +import allchive.server.domain.domains.archiving.adaptor.ArchivingAdaptor; import allchive.server.domain.domains.archiving.validator.ArchivingValidator; +import allchive.server.domain.domains.content.adaptor.ContentAdaptor; import allchive.server.domain.domains.content.validator.ContentValidator; import allchive.server.domain.domains.report.domain.Report; import allchive.server.domain.domains.report.domain.enums.ReportObjectType; @@ -19,6 +21,8 @@ public class CreateReportUseCase { private final ReportValidator reportValidator; private final ContentValidator contentValidator; + private final ContentAdaptor contentAdaptor; + private final ArchivingAdaptor archivingAdaptor; private final ArchivingValidator archivingValidator; private final ReportMapper reportMapper; private final ReportDomainService reportDomainService; @@ -27,11 +31,19 @@ public class CreateReportUseCase { public void execute(CreateReportRequest request, ReportObjectType type) { Long userId = SecurityUtil.getCurrentUserId(); reportValidator.validateNotDuplicateReport(userId, request.getId(), type); + Long reportedUserId = 0L; switch (type) { - case CONTENT -> contentValidator.validateExistById(request.getId()); - case ARCHIVING -> archivingValidator.validateExistById(request.getId()); + case CONTENT -> { + contentValidator.validateExistById(request.getId()); + Long archivingId = contentAdaptor.findById(request.getId()).getArchivingId(); + reportedUserId = archivingAdaptor.findById(archivingId).getUserId(); + } + case ARCHIVING -> { + archivingValidator.validateExistById(request.getId()); + reportedUserId = archivingAdaptor.findById(request.getId()).getUserId(); + } } - Report report = reportMapper.toEntity(request, type, userId); + Report report = reportMapper.toEntity(request, type, userId, reportedUserId); reportDomainService.save(report); } } diff --git a/Api/src/main/java/allchive/server/api/user/service/GetUserInfoUseCase.java b/Api/src/main/java/allchive/server/api/user/service/GetUserInfoUseCase.java index 6fc7c0c7..bff4bdbb 100644 --- a/Api/src/main/java/allchive/server/api/user/service/GetUserInfoUseCase.java +++ b/Api/src/main/java/allchive/server/api/user/service/GetUserInfoUseCase.java @@ -20,7 +20,7 @@ public class GetUserInfoUseCase { public GetUserInfoResponse execute() { Long userId = SecurityUtil.getCurrentUserId(); userValidator.validateUserStatusNormal(userId); - User user = userAdaptor.queryUserById(userId); + User user = userAdaptor.findUserById(userId); return GetUserInfoResponse.from(user); } } diff --git a/Api/src/main/java/allchive/server/api/user/service/GetUserProfileUseCase.java b/Api/src/main/java/allchive/server/api/user/service/GetUserProfileUseCase.java index 95afe46d..28354c88 100644 --- a/Api/src/main/java/allchive/server/api/user/service/GetUserProfileUseCase.java +++ b/Api/src/main/java/allchive/server/api/user/service/GetUserProfileUseCase.java @@ -26,7 +26,7 @@ public class GetUserProfileUseCase { public GetUserProfileResponse execute() { Long userId = SecurityUtil.getCurrentUserId(); userValidator.validateUserStatusNormal(userId); - User user = userAdaptor.queryUserById(userId); + User user = userAdaptor.findUserById(userId); List<Archiving> archivingList = archivingAdaptor.findAllByUserId(userId); return userMapper.toGetUserProfileResponse(archivingList, user); } diff --git a/Core/src/main/java/allchive/server/core/annotation/ValidEnum.java b/Core/src/main/java/allchive/server/core/annotation/ValidEnum.java index 7f6a724b..27ca8ce9 100644 --- a/Core/src/main/java/allchive/server/core/annotation/ValidEnum.java +++ b/Core/src/main/java/allchive/server/core/annotation/ValidEnum.java @@ -5,13 +5,14 @@ import allchive.server.core.validator.EnumValidator; import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.Target; import javax.validation.Constraint; import javax.validation.Payload; @Documented -@Target({FIELD}) +@Target({ElementType.TYPE_USE, FIELD}) @Retention(RUNTIME) @Constraint(validatedBy = {EnumValidator.class}) public @interface ValidEnum { diff --git a/Domain/build.gradle b/Domain/build.gradle index 1820de04..81b129c8 100644 --- a/Domain/build.gradle +++ b/Domain/build.gradle @@ -5,7 +5,6 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'mysql:mysql-connector-java:8.0.33' implementation 'org.springframework.boot:spring-boot-starter-web' - // TODO : Core에 impl 했는데 여기서 이용 못하는 문제 해결해야함 implementation 'org.springframework.boot:spring-boot-starter-validation' implementation project(':Core') diff --git a/Domain/src/main/java/allchive/server/domain/domains/block/adaptor/BlockAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/block/adaptor/BlockAdaptor.java index 2ca80c63..a7068d7c 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/block/adaptor/BlockAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/block/adaptor/BlockAdaptor.java @@ -27,4 +27,8 @@ public void save(Block block) { public void deleteByBlockFromAndBlockUser(Long blockFrom, Long blockUser) { blockRepository.deleteByBlockFromAndBlockUser(blockFrom, blockUser); } + + public void queryDeleteBlockByBlockFromOrBlockUser(Long userId) { + blockRepository.queryDeleteBlockByBlockFromOrBlockUser(userId); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/block/repository/BlockCustomRepository.java b/Domain/src/main/java/allchive/server/domain/domains/block/repository/BlockCustomRepository.java index ae169ca0..fa0e4a09 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/block/repository/BlockCustomRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/block/repository/BlockCustomRepository.java @@ -2,4 +2,6 @@ public interface BlockCustomRepository { boolean queryBlockExistByBlockFromAndBlockUser(Long blockFrom, Long blockUser); + + void queryDeleteBlockByBlockFromOrBlockUser(Long userId); } diff --git a/Domain/src/main/java/allchive/server/domain/domains/block/repository/BlockCustomRepositoryImpl.java b/Domain/src/main/java/allchive/server/domain/domains/block/repository/BlockCustomRepositoryImpl.java index f172ef31..a7b7cf27 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/block/repository/BlockCustomRepositoryImpl.java +++ b/Domain/src/main/java/allchive/server/domain/domains/block/repository/BlockCustomRepositoryImpl.java @@ -21,6 +21,13 @@ public boolean queryBlockExistByBlockFromAndBlockUser(Long blockFrom, Long block return fetchOne != null; } + @Override + public void queryDeleteBlockByBlockFromOrBlockUser(Long userId) { + queryFactory + .delete(block) + .where(blockUserEqOrBlockFromEq(userId)); + } + private BooleanExpression blockFromEq(Long blockFrom) { return block.blockFrom.eq(blockFrom); } @@ -28,4 +35,8 @@ private BooleanExpression blockFromEq(Long blockFrom) { private BooleanExpression blockUserEq(Long blockUser) { return block.blockUser.eq(blockUser); } + + private BooleanExpression blockUserEqOrBlockFromEq(Long userId) { + return blockFromEq(userId).or(blockUserEq(userId)); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/block/service/BlockDomainService.java b/Domain/src/main/java/allchive/server/domain/domains/block/service/BlockDomainService.java index a38fb224..bfc7da06 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/block/service/BlockDomainService.java +++ b/Domain/src/main/java/allchive/server/domain/domains/block/service/BlockDomainService.java @@ -18,4 +18,8 @@ public void save(Block block) { public void deleteByBlockFromAndBlockUser(Long blockFrom, Long blockUser) { blockAdaptor.deleteByBlockFromAndBlockUser(blockFrom, blockUser); } + + public void queryDeleteBlockByBlockFromOrBlockUser(Long userId) { + blockAdaptor.queryDeleteBlockByBlockFromOrBlockUser(userId); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentAdaptor.java index 7d789bed..b14cfdd7 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentAdaptor.java @@ -52,4 +52,8 @@ public List<Content> findAllByArchivingIds(List<Long> archivingIds) { public boolean queryContentExistById(Long contentId) { return contentRepository.queryContentExistById(contentId); } + + public void deleteAllByArchivingIdIn(List<Long> archivingId) { + contentRepository.deleteAllByArchivingIdIn(archivingId); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentTagGroupAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentTagGroupAdaptor.java index 2414717a..d2f4b492 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentTagGroupAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentTagGroupAdaptor.java @@ -33,4 +33,8 @@ public void saveAll(List<ContentTagGroup> contentTagGroupList) { public void deleteAllByContentIn(List<Content> contents) { contentTagGroupRepository.deleteAllByContentIn(contents); } + + public void deleteAllByTagIn(List<Tag> tagList) { + contentTagGroupRepository.deleteAllByTagIn(tagList); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/TagAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/TagAdaptor.java index 07f9a813..5a1df191 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/TagAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/TagAdaptor.java @@ -36,4 +36,12 @@ public void deleteById(Long tagId) { public List<Tag> queryTagInTagIdList(List<Long> tagIds) { return tagRepository.queryTagInTagIdList(tagIds); } + + public List<Tag> findAllByUserId(Long userId) { + return tagRepository.findAllByUserId(userId); + } + + public void deleteAll(List<Tag> tagList) { + tagRepository.deleteAll(tagList); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentRepository.java b/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentRepository.java index 79a39685..88540676 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentRepository.java @@ -7,4 +7,6 @@ public interface ContentRepository extends JpaRepository<Content, Long>, ContentCustomRepository { List<Content> findAllByIdIn(List<Long> contentIdList); + + void deleteAllByArchivingIdIn(List<Long> archivingId); } diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentTagGroupRepository.java b/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentTagGroupRepository.java index a6babae0..f218bdae 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentTagGroupRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentTagGroupRepository.java @@ -14,4 +14,6 @@ public interface ContentTagGroupRepository void deleteByTag(Tag tag); void deleteAllByContentIn(List<Content> contents); + + void deleteAllByTagIn(List<Tag> tagList); } diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/repository/TagRepository.java b/Domain/src/main/java/allchive/server/domain/domains/content/repository/TagRepository.java index 0cfe5c5f..50911c53 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/repository/TagRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/repository/TagRepository.java @@ -7,4 +7,6 @@ public interface TagRepository extends JpaRepository<Tag, Long>, TagCustomRepository { List<Tag> findAllByUserIdOrderByCreatedAtDesc(Long userId); + + List<Tag> findAllByUserId(Long userId); } diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/service/ContentDomainService.java b/Domain/src/main/java/allchive/server/domain/domains/content/service/ContentDomainService.java index 7f6c9f56..2b2fd708 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/service/ContentDomainService.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/service/ContentDomainService.java @@ -31,4 +31,8 @@ public void restoreInIdList(List<Long> contentIds) { public void deleteAllById(List<Long> contentIds) { contentAdaptor.deleteAllById(contentIds); } + + public void deleteAllByArchivingIdIn(List<Long> archivingId) { + contentAdaptor.deleteAllByArchivingIdIn(archivingId); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/service/ContentTagGroupDomainService.java b/Domain/src/main/java/allchive/server/domain/domains/content/service/ContentTagGroupDomainService.java index 9d162118..3f0dbf45 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/service/ContentTagGroupDomainService.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/service/ContentTagGroupDomainService.java @@ -25,4 +25,8 @@ public void saveAll(List<ContentTagGroup> contentTagGroupList) { public void deleteByContentIn(List<Content> contents) { contentTagGroupAdaptor.deleteAllByContentIn(contents); } + + public void deleteAllByTagIn(List<Tag> tagList) { + contentTagGroupAdaptor.deleteAllByTagIn(tagList); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/service/TagDomainService.java b/Domain/src/main/java/allchive/server/domain/domains/content/service/TagDomainService.java index a9a75605..265d3158 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/service/TagDomainService.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/service/TagDomainService.java @@ -6,6 +6,8 @@ import allchive.server.domain.domains.content.domain.Tag; import lombok.RequiredArgsConstructor; +import java.util.List; + @DomainService @RequiredArgsConstructor public class TagDomainService { @@ -24,4 +26,8 @@ public void updateTag(Long tagId, String name) { public void deleteById(Long tagId) { tagAdaptor.deleteById(tagId); } + + public void deleteAll(List<Tag> tagList) { + tagAdaptor.deleteAll(tagList); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/recycle/adaptor/RecycleAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/recycle/adaptor/RecycleAdaptor.java index eee460b6..b2055e21 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/recycle/adaptor/RecycleAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/recycle/adaptor/RecycleAdaptor.java @@ -34,4 +34,8 @@ public List<Recycle> findAllByUserId(Long userId) { public List<Recycle> findAllByDeletedAtBefore(LocalDateTime deleteStandard) { return recycleRepository.findAllByDeletedAtBefore(deleteStandard); } + + public void deleteAllByUserId(Long userId) { + recycleRepository.deleteAllByUserId(userId); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/recycle/repository/RecycleRepository.java b/Domain/src/main/java/allchive/server/domain/domains/recycle/repository/RecycleRepository.java index a8dfb3b3..a94c960a 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/recycle/repository/RecycleRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/recycle/repository/RecycleRepository.java @@ -10,4 +10,6 @@ public interface RecycleRepository extends JpaRepository<Recycle, Long>, Recycle List<Recycle> findAllByUserId(Long userId); List<Recycle> findAllByDeletedAtBefore(LocalDateTime time); + + void deleteAllByUserId(Long userId); } diff --git a/Domain/src/main/java/allchive/server/domain/domains/recycle/service/RecycleDomainService.java b/Domain/src/main/java/allchive/server/domain/domains/recycle/service/RecycleDomainService.java index 63531a58..9763eae5 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/recycle/service/RecycleDomainService.java +++ b/Domain/src/main/java/allchive/server/domain/domains/recycle/service/RecycleDomainService.java @@ -27,4 +27,8 @@ public void deleteAllByUserIdAndArchivingIdOrUserIdAndContentId( public void deleteAll(List<Recycle> recycles) { recycleAdaptor.deleteAll(recycles); } + + public void deleteAllByUserId(Long userId) { + recycleAdaptor.deleteAllByUserId(userId); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/report/adaptor/ReportAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/report/adaptor/ReportAdaptor.java index 95c1d405..16284ef0 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/report/adaptor/ReportAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/report/adaptor/ReportAdaptor.java @@ -22,4 +22,8 @@ public Boolean queryReportExistByUserIdAndContentId(Long userId, Long contentId) public Boolean queryReportExistByUserIdAndArchivingId(Long userId, Long archivingId) { return reportRepository.queryReportExistByUserIdAndArchivingId(userId, archivingId); } + + public void deleteAllByReportedUserId(Long userId) { + reportRepository.deleteAllByReportedUserId(userId); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/report/domain/Report.java b/Domain/src/main/java/allchive/server/domain/domains/report/domain/Report.java index 693b4dc7..4d408633 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/report/domain/Report.java +++ b/Domain/src/main/java/allchive/server/domain/domains/report/domain/Report.java @@ -30,6 +30,7 @@ public class Report extends BaseTimeEntity { private Long contentId; private Long archivingId; private Long userId; // 신고한 유저 + private Long reportedUserId; // 신고 당한 유저 id @Builder private Report( @@ -38,13 +39,15 @@ private Report( ReportedType reportedType, Long contentId, Long archivingId, - Long userId) { + Long userId, + Long reportedUserId) { this.reportObjectTypeType = reportObjectTypeType; this.reason = reason; this.reportedType = reportedType; this.contentId = contentId; this.archivingId = archivingId; this.userId = userId; + this.reportedUserId = reportedUserId; } public static Report of( @@ -53,7 +56,8 @@ public static Report of( ReportedType reportedType, Long contentId, Long archivingId, - Long userId) { + Long userId, + Long reportedUserId) { return Report.builder() .reportObjectTypeType(reportObjectTypeType) .reason(reason) @@ -61,6 +65,7 @@ public static Report of( .contentId(contentId) .archivingId(archivingId) .userId(userId) + .reportedUserId(reportedUserId) .build(); } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/report/repository/ReportRepository.java b/Domain/src/main/java/allchive/server/domain/domains/report/repository/ReportRepository.java index c1bf33b6..763aa660 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/report/repository/ReportRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/report/repository/ReportRepository.java @@ -4,4 +4,6 @@ import allchive.server.domain.domains.report.domain.Report; import org.springframework.data.jpa.repository.JpaRepository; -public interface ReportRepository extends JpaRepository<Report, Long>, ReportCustomRepository {} +public interface ReportRepository extends JpaRepository<Report, Long>, ReportCustomRepository { + void deleteAllByReportedUserId(Long userId); +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/report/service/ReportDomainService.java b/Domain/src/main/java/allchive/server/domain/domains/report/service/ReportDomainService.java index 6e24e2d6..00a0e4ad 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/report/service/ReportDomainService.java +++ b/Domain/src/main/java/allchive/server/domain/domains/report/service/ReportDomainService.java @@ -14,4 +14,8 @@ public class ReportDomainService { public void save(Report report) { reportAdaptor.save(report); } + + public void deleteAllByReportedUserId(Long userId) { + reportAdaptor.deleteAllByReportedUserId(userId); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/search/adaptor/LatestSearchAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/search/adaptor/LatestSearchAdaptor.java index bc354d21..c1087477 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/search/adaptor/LatestSearchAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/search/adaptor/LatestSearchAdaptor.java @@ -23,4 +23,8 @@ public void delete(LatestSearch latestSearch) { public void save(LatestSearch newSearch) { latestSearchRepository.save(newSearch); } + + public void deleteAllByUserId(Long userId) { + latestSearchRepository.deleteAllByUserId(userId); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/search/repository/LatestSearchRepository.java b/Domain/src/main/java/allchive/server/domain/domains/search/repository/LatestSearchRepository.java index 782ea8b6..20ca1d86 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/search/repository/LatestSearchRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/search/repository/LatestSearchRepository.java @@ -7,4 +7,6 @@ public interface LatestSearchRepository extends JpaRepository<LatestSearch, Long> { List<LatestSearch> findAllByUserIdOrderByCreatedAt(Long userId); + + void deleteAllByUserId(Long userId); } diff --git a/Domain/src/main/java/allchive/server/domain/domains/search/service/LatestSearchDomainService.java b/Domain/src/main/java/allchive/server/domain/domains/search/service/LatestSearchDomainService.java index 0d9e1407..533529ac 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/search/service/LatestSearchDomainService.java +++ b/Domain/src/main/java/allchive/server/domain/domains/search/service/LatestSearchDomainService.java @@ -18,4 +18,8 @@ public void delete(LatestSearch latestSearch) { public void save(LatestSearch newSearch) { latestSearchAdaptor.save(newSearch); } + + public void deleteAllByUserId(Long userId) { + latestSearchAdaptor.deleteAllByUserId(userId); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/ScrapAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/ScrapAdaptor.java index 82a84f9e..3b3942f9 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/ScrapAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/ScrapAdaptor.java @@ -33,4 +33,8 @@ public void save(Scrap scrap) { public void deleteAllByArchivingIdIn(List<Long> archivingIds) { scrapRepository.deleteAllByArchivingIdIn(archivingIds); } + + public void deleteAllByUser(User user) { + scrapRepository.deleteAllByUser(user); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/UserAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/UserAdaptor.java index 976effb1..bdb3f7b4 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/UserAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/UserAdaptor.java @@ -28,7 +28,7 @@ public void save(User user) { userRepository.save(user); } - public User queryUserById(Long userId) { + public User findUserById(Long userId) { return userRepository.findById(userId).orElseThrow(() -> UserNotFoundException.EXCEPTION); } diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/domain/User.java b/Domain/src/main/java/allchive/server/domain/domains/user/domain/User.java index 8d3c49c4..c5a87470 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/domain/User.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/domain/User.java @@ -50,17 +50,27 @@ public class User extends BaseTimeEntity { private List<Category> categories = new ArrayList<>(); @Builder - private User(String nickname, String profileImgUrl, OauthInfo oauthInfo) { + private User( + String nickname, + String profileImgUrl, + List<Category> categoryList, + OauthInfo oauthInfo) { this.nickname = nickname; this.profileImgUrl = profileImgUrl; + this.categories = categoryList; this.oauthInfo = oauthInfo; this.lastLoginAt = LocalDateTime.now(); } - public static User of(String nickname, String profileImgUrl, OauthInfo oauthInfo) { + public static User of( + String nickname, + String profileImgUrl, + List<Category> categoryList, + OauthInfo oauthInfo) { return User.builder() .nickname(nickname) .profileImgUrl(profileImgUrl) + .categoryList(categoryList) .oauthInfo(oauthInfo) .build(); } diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/repository/ScrapRepository.java b/Domain/src/main/java/allchive/server/domain/domains/user/repository/ScrapRepository.java index a6c06431..4ccfba30 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/repository/ScrapRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/repository/ScrapRepository.java @@ -13,4 +13,6 @@ public interface ScrapRepository extends JpaRepository<Scrap, Long> { Optional<Scrap> findAllByUserAndArchivingId(User user, Long archivingId); void deleteAllByArchivingIdIn(List<Long> archivingId); + + void deleteAllByUser(User user); } diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/service/ScrapDomainService.java b/Domain/src/main/java/allchive/server/domain/domains/user/service/ScrapDomainService.java index d994bb98..37440ece 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/service/ScrapDomainService.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/service/ScrapDomainService.java @@ -30,4 +30,8 @@ public void save(Scrap scrap) { public void deleteAllByArchivingIdIn(List<Long> archivingIds) { scrapAdaptor.deleteAllByArchivingIdIn(archivingIds); } + + public void deleteAllByUser(User user) { + scrapAdaptor.deleteAllByUser(user); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/service/UserDomainService.java b/Domain/src/main/java/allchive/server/domain/domains/user/service/UserDomainService.java index fb8acce1..997f9236 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/service/UserDomainService.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/service/UserDomainService.java @@ -2,11 +2,13 @@ import allchive.server.core.annotation.DomainService; +import allchive.server.domain.domains.archiving.domain.enums.Category; import allchive.server.domain.domains.user.adaptor.UserAdaptor; import allchive.server.domain.domains.user.domain.User; import allchive.server.domain.domains.user.domain.enums.OauthInfo; import allchive.server.domain.domains.user.exception.exceptions.DuplicatedNicknameException; import allchive.server.domain.domains.user.validator.UserValidator; +import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.transaction.annotation.Transactional; @@ -21,9 +23,13 @@ public Boolean checkUserCanLogin(OauthInfo oauthInfo) { } @Transactional - public User registerUser(String nickname, String profileImgUrl, OauthInfo oauthInfo) { + public User registerUser( + String nickname, + String profileImgUrl, + List<Category> categoryList, + OauthInfo oauthInfo) { userValidator.validUserCanRegister(oauthInfo); - final User newUser = User.of(nickname, profileImgUrl, oauthInfo); + final User newUser = User.of(nickname, profileImgUrl, categoryList, oauthInfo); userAdaptor.save(newUser); return newUser; } @@ -37,13 +43,13 @@ public User loginUser(OauthInfo oauthInfo) { @Transactional public void deleteUserById(Long userId) { - User user = userAdaptor.queryUserById(userId); + User user = userAdaptor.findUserById(userId); user.withdrawUser(); } public void updateUserInfo( Long userId, String name, String email, String nickname, String imgUrl) { - User user = userAdaptor.queryUserById(userId); + User user = userAdaptor.findUserById(userId); user.updateInfo(name, email, nickname, imgUrl); } diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/validator/UserValidator.java b/Domain/src/main/java/allchive/server/domain/domains/user/validator/UserValidator.java index 8a25182e..0a02dfb9 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/validator/UserValidator.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/validator/UserValidator.java @@ -23,7 +23,7 @@ public Boolean checkUserCanRegister(OauthInfo oauthInfo) { } public void validateUserStatusNormal(Long userId) { - if (!userAdaptor.queryUserById(userId).getUserState().equals(UserState.NORMAL)) { + if (!userAdaptor.findUserById(userId).getUserState().equals(UserState.NORMAL)) { throw ForbiddenUserException.EXCEPTION; } } From 3f4d4399a3e18c5dff8967634ad5d2a9435b0141 Mon Sep 17 00:00:00 2001 From: Sanghoon Jeong <67852689+wjdtkdgns@users.noreply.github.com> Date: Thu, 20 Jul 2023 10:31:56 +0900 Subject: [PATCH 38/41] =?UTF-8?q?[fix]=20=EB=8B=89=EB=84=A4=EC=9E=84=20?= =?UTF-8?q?=EA=B2=80=EC=82=AC=20=EB=A1=9C=EC=A7=81=20=EB=B3=80=EA=B2=BD=20?= =?UTF-8?q?(#46)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../server/api/auth/service/WithdrawUserUseCase.java | 5 +---- .../server/api/report/model/mapper/ReportMapper.java | 3 ++- .../server/api/user/service/CheckUserNicknameUseCase.java | 5 ----- .../domains/block/repository/BlockCustomRepositoryImpl.java | 4 +--- .../domain/domains/content/service/TagDomainService.java | 3 +-- 5 files changed, 5 insertions(+), 15 deletions(-) diff --git a/Api/src/main/java/allchive/server/api/auth/service/WithdrawUserUseCase.java b/Api/src/main/java/allchive/server/api/auth/service/WithdrawUserUseCase.java index 3cc54328..42b28a97 100644 --- a/Api/src/main/java/allchive/server/api/auth/service/WithdrawUserUseCase.java +++ b/Api/src/main/java/allchive/server/api/auth/service/WithdrawUserUseCase.java @@ -8,7 +8,6 @@ import allchive.server.domain.domains.archiving.adaptor.ArchivingAdaptor; import allchive.server.domain.domains.archiving.domain.Archiving; import allchive.server.domain.domains.block.service.BlockDomainService; -import allchive.server.domain.domains.content.adaptor.ContentAdaptor; import allchive.server.domain.domains.content.adaptor.TagAdaptor; import allchive.server.domain.domains.content.domain.Tag; import allchive.server.domain.domains.content.service.ContentDomainService; @@ -23,9 +22,8 @@ import allchive.server.domain.domains.user.domain.enums.OauthProvider; import allchive.server.domain.domains.user.service.ScrapDomainService; import allchive.server.domain.domains.user.service.UserDomainService; -import lombok.RequiredArgsConstructor; - import java.util.List; +import lombok.RequiredArgsConstructor; @UseCase @RequiredArgsConstructor @@ -60,7 +58,6 @@ private void withdrawOauth(OauthProvider provider, String appleAccessToken, User case APPLE -> oauthHelper.withdraw(provider, null, appleAccessToken); default -> throw InvalidOauthProviderException.EXCEPTION; } - } private void withdrawService(Long userId, User user) { diff --git a/Api/src/main/java/allchive/server/api/report/model/mapper/ReportMapper.java b/Api/src/main/java/allchive/server/api/report/model/mapper/ReportMapper.java index b86c9e1c..68d61a63 100644 --- a/Api/src/main/java/allchive/server/api/report/model/mapper/ReportMapper.java +++ b/Api/src/main/java/allchive/server/api/report/model/mapper/ReportMapper.java @@ -8,7 +8,8 @@ @Mapper public class ReportMapper { - public Report toEntity(CreateReportRequest request, ReportObjectType type, Long userId, Long reportedUserId) { + public Report toEntity( + CreateReportRequest request, ReportObjectType type, Long userId, Long reportedUserId) { Report report = null; switch (type) { case CONTENT -> report = diff --git a/Api/src/main/java/allchive/server/api/user/service/CheckUserNicknameUseCase.java b/Api/src/main/java/allchive/server/api/user/service/CheckUserNicknameUseCase.java index 61118913..de1a6e84 100644 --- a/Api/src/main/java/allchive/server/api/user/service/CheckUserNicknameUseCase.java +++ b/Api/src/main/java/allchive/server/api/user/service/CheckUserNicknameUseCase.java @@ -1,11 +1,9 @@ package allchive.server.api.user.service; -import allchive.server.api.config.security.SecurityUtil; import allchive.server.api.user.model.dto.request.CheckUserNicknameRequest; import allchive.server.core.annotation.UseCase; import allchive.server.domain.domains.user.service.UserDomainService; -import allchive.server.domain.domains.user.validator.UserValidator; import lombok.RequiredArgsConstructor; import org.springframework.transaction.annotation.Transactional; @@ -13,12 +11,9 @@ @RequiredArgsConstructor public class CheckUserNicknameUseCase { private final UserDomainService userDomainService; - private final UserValidator userValidator; @Transactional(readOnly = true) public void execute(CheckUserNicknameRequest request) { - Long userId = SecurityUtil.getCurrentUserId(); - userValidator.validateUserStatusNormal(userId); userDomainService.checkUserNickname(request.getNickname()); } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/block/repository/BlockCustomRepositoryImpl.java b/Domain/src/main/java/allchive/server/domain/domains/block/repository/BlockCustomRepositoryImpl.java index a7b7cf27..d135db43 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/block/repository/BlockCustomRepositoryImpl.java +++ b/Domain/src/main/java/allchive/server/domain/domains/block/repository/BlockCustomRepositoryImpl.java @@ -23,9 +23,7 @@ public boolean queryBlockExistByBlockFromAndBlockUser(Long blockFrom, Long block @Override public void queryDeleteBlockByBlockFromOrBlockUser(Long userId) { - queryFactory - .delete(block) - .where(blockUserEqOrBlockFromEq(userId)); + queryFactory.delete(block).where(blockUserEqOrBlockFromEq(userId)); } private BooleanExpression blockFromEq(Long blockFrom) { diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/service/TagDomainService.java b/Domain/src/main/java/allchive/server/domain/domains/content/service/TagDomainService.java index 265d3158..1ebbe4b0 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/service/TagDomainService.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/service/TagDomainService.java @@ -4,9 +4,8 @@ import allchive.server.core.annotation.DomainService; import allchive.server.domain.domains.content.adaptor.TagAdaptor; import allchive.server.domain.domains.content.domain.Tag; -import lombok.RequiredArgsConstructor; - import java.util.List; +import lombok.RequiredArgsConstructor; @DomainService @RequiredArgsConstructor From 4e3940aa310f872510fc7d57ed1208d3a01080cf Mon Sep 17 00:00:00 2001 From: Sanghoon Jeong <67852689+wjdtkdgns@users.noreply.github.com> Date: Thu, 20 Jul 2023 17:03:01 +0900 Subject: [PATCH 39/41] =?UTF-8?q?[refac]=20=EC=BB=A8=ED=85=90=EC=B8=A0=20?= =?UTF-8?q?=EB=B6=80=EB=B6=84=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81=20(#4?= =?UTF-8?q?8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [refac] 컨텐츠 생성 부분 api 네이밍 변경, 함수 리팩토링 #47 * [refac] 컨텐츠 불러오기 response 변경 및 로직 정리 #47 * [refac] 컨텐츠 삭제 api validation 로직 분리 #47 * [feat] 컨텐츠 수정 기능 구현 #47 * [refac] 로직 함수로 분리 #47 * [chore] spotless 적용 --- .../service/GetArchivingContentsUseCase.java | 2 +- .../content/controller/ContentController.java | 10 +++ .../dto/request/UpdateContentRequest.java | 33 ++++++++++ .../dto/response/ContentTagResponse.java | 16 ++++- .../content/model/mapper/ContentMapper.java | 4 +- .../content/service/CreateContentUseCase.java | 16 +++-- .../content/service/DeleteContentUseCase.java | 18 +++--- .../content/service/GetContentUseCase.java | 24 ++++++-- .../content/service/UpdateContentUseCase.java | 61 +++++++++++++++++++ .../service/GetDeletedObjectUseCase.java | 3 +- .../adaptor/ContentTagGroupAdaptor.java | 16 +++-- .../domains/content/adaptor/TagAdaptor.java | 4 +- .../domains/content/domain/Content.java | 14 +++++ .../domain/domains/content/domain/Tag.java | 4 +- .../content/exception/ContentErrorCode.java | 2 + .../NoAuthorityUpdateContentException.java | 14 +++++ .../NoAuthorityUpdateTagException.java | 14 +++++ .../ContentTagGroupCustomRepository.java | 4 +- .../ContentTagGroupCustomRepositoryImpl.java | 21 ++++++- .../repository/ContentTagGroupRepository.java | 2 + .../repository/TagCustomRepository.java | 2 +- .../repository/TagCustomRepositoryImpl.java | 2 +- .../content/service/ContentDomainService.java | 17 ++++++ .../service/ContentTagGroupDomainService.java | 4 ++ .../content/validator/ContentValidator.java | 22 ++++++- .../content/validator/TagValidator.java | 6 +- 26 files changed, 292 insertions(+), 43 deletions(-) create mode 100644 Api/src/main/java/allchive/server/api/content/model/dto/request/UpdateContentRequest.java create mode 100644 Api/src/main/java/allchive/server/api/content/service/UpdateContentUseCase.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/content/exception/exceptions/NoAuthorityUpdateContentException.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/content/exception/exceptions/NoAuthorityUpdateTagException.java diff --git a/Api/src/main/java/allchive/server/api/archiving/service/GetArchivingContentsUseCase.java b/Api/src/main/java/allchive/server/api/archiving/service/GetArchivingContentsUseCase.java index 05fc8b9d..04897b73 100644 --- a/Api/src/main/java/allchive/server/api/archiving/service/GetArchivingContentsUseCase.java +++ b/Api/src/main/java/allchive/server/api/archiving/service/GetArchivingContentsUseCase.java @@ -38,7 +38,7 @@ public ArchivingContentsResponse execute(Long archivingId, Pageable pageable) { Slice<Content> contentList = contentAdaptor.querySliceContentByArchivingId(archivingId, pageable); List<ContentTagGroup> contentTagGroupList = - contentTagGroupAdaptor.queryContentIn(contentList.getContent()); + contentTagGroupAdaptor.queryContentTagGroupByContentIn(contentList.getContent()); Slice<ContentResponse> contentResponseSlice = contentList.map( content -> contentMapper.toContentResponse(content, contentTagGroupList)); diff --git a/Api/src/main/java/allchive/server/api/content/controller/ContentController.java b/Api/src/main/java/allchive/server/api/content/controller/ContentController.java index f3059a03..284e286e 100644 --- a/Api/src/main/java/allchive/server/api/content/controller/ContentController.java +++ b/Api/src/main/java/allchive/server/api/content/controller/ContentController.java @@ -2,10 +2,12 @@ import allchive.server.api.content.model.dto.request.CreateContentRequest; +import allchive.server.api.content.model.dto.request.UpdateContentRequest; import allchive.server.api.content.model.dto.response.ContentTagResponse; import allchive.server.api.content.service.CreateContentUseCase; import allchive.server.api.content.service.DeleteContentUseCase; import allchive.server.api.content.service.GetContentUseCase; +import allchive.server.api.content.service.UpdateContentUseCase; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; @@ -21,6 +23,7 @@ public class ContentController { private final CreateContentUseCase createContentUseCase; private final GetContentUseCase getContentUseCase; private final DeleteContentUseCase deleteContentUseCase; + private final UpdateContentUseCase updateContentUseCase; @Operation(summary = "컨텐츠를 생성합니다.") @PostMapping() @@ -39,4 +42,11 @@ public ContentTagResponse createContent(@PathVariable Long contentId) { public void deleteContent(@PathVariable Long contentId) { deleteContentUseCase.execute(contentId); } + + @Operation(summary = "컨텐츠를 수정합니다.") + @PatchMapping(value = "/{contentId}") + public void updateContent( + @PathVariable Long contentId, @RequestBody UpdateContentRequest request) { + updateContentUseCase.execute(contentId, request); + } } diff --git a/Api/src/main/java/allchive/server/api/content/model/dto/request/UpdateContentRequest.java b/Api/src/main/java/allchive/server/api/content/model/dto/request/UpdateContentRequest.java new file mode 100644 index 00000000..0298614e --- /dev/null +++ b/Api/src/main/java/allchive/server/api/content/model/dto/request/UpdateContentRequest.java @@ -0,0 +1,33 @@ +package allchive.server.api.content.model.dto.request; + + +import allchive.server.core.annotation.ValidEnum; +import allchive.server.domain.domains.content.domain.enums.ContentType; +import io.swagger.v3.oas.annotations.media.Schema; +import java.util.List; +import lombok.Getter; + +@Getter +public class UpdateContentRequest { + @Schema(defaultValue = "image", description = "컨텐츠 타입") + @ValidEnum(target = ContentType.class) + private ContentType contentType; + + @Schema(defaultValue = "0", description = "아카이빙 고유번호") + private Long archivingId; + + @Schema(defaultValue = "제목", description = "제목") + private String title; + + @Schema(defaultValue = "링크", description = "링크") + private String link; + + @Schema(defaultValue = "이미지 url", description = "이미지 url") + private String imgUrl; + + @Schema(description = "태그 고유번호 리스트") + private List<Long> tagIds; + + @Schema(defaultValue = "메모", description = "메모") + private String memo; +} diff --git a/Api/src/main/java/allchive/server/api/content/model/dto/response/ContentTagResponse.java b/Api/src/main/java/allchive/server/api/content/model/dto/response/ContentTagResponse.java index b3520294..398b5259 100644 --- a/Api/src/main/java/allchive/server/api/content/model/dto/response/ContentTagResponse.java +++ b/Api/src/main/java/allchive/server/api/content/model/dto/response/ContentTagResponse.java @@ -23,6 +23,9 @@ public class ContentTagResponse { @Schema(description = "컨텐츠 종류") private ContentType contentType; + @Schema(description = "메모") + private String contentMemo; + @Schema(defaultValue = "컨텐츠 링크", description = "컨텐츠 링크") private String link; @@ -39,33 +42,42 @@ public class ContentTagResponse { private List<TagResponse> tagList; + private Boolean isMine; + @Builder private ContentTagResponse( Long contentId, String contentTitle, ContentType contentType, + String contentMemo, String link, String imgUrl, LocalDateTime contentCreatedAt, - List<TagResponse> tagList) { + List<TagResponse> tagList, + Boolean isMine) { this.contentId = contentId; this.contentTitle = contentTitle; this.contentType = contentType; + this.contentMemo = contentMemo; this.link = link; this.imgUrl = imgUrl; this.contentCreatedAt = contentCreatedAt; this.tagList = tagList; + this.isMine = isMine; } - public static ContentTagResponse of(Content content, List<TagResponse> tagList) { + public static ContentTagResponse of( + Content content, List<TagResponse> tagList, Boolean isMine) { return ContentTagResponse.builder() .contentId(content.getId()) .contentTitle(content.getTitle()) .contentType(content.getContentType()) + .contentMemo(content.getMemo()) .link(content.getLinkUrl()) .imgUrl(UrlUtil.toAssetUrl(content.getImageUrl())) .contentCreatedAt(content.getCreatedAt()) .tagList(tagList) + .isMine(isMine) .build(); } } diff --git a/Api/src/main/java/allchive/server/api/content/model/mapper/ContentMapper.java b/Api/src/main/java/allchive/server/api/content/model/mapper/ContentMapper.java index a967bfab..f4dda5ed 100644 --- a/Api/src/main/java/allchive/server/api/content/model/mapper/ContentMapper.java +++ b/Api/src/main/java/allchive/server/api/content/model/mapper/ContentMapper.java @@ -36,12 +36,12 @@ public Content toEntity(CreateContentRequest request) { } public ContentTagResponse toContentTagResponse( - Content content, List<ContentTagGroup> contentTagGroupList) { + Content content, List<ContentTagGroup> contentTagGroupList, Boolean isMine) { List<TagResponse> tagResponseList = contentTagGroupList.stream() .map(contentTagGroup -> TagResponse.from(contentTagGroup.getTag())) .toList(); - return ContentTagResponse.of(content, tagResponseList); + return ContentTagResponse.of(content, tagResponseList, isMine); } public List<ContentTagGroup> toContentTagGroupEntityList(Content content, List<Tag> tags) { diff --git a/Api/src/main/java/allchive/server/api/content/service/CreateContentUseCase.java b/Api/src/main/java/allchive/server/api/content/service/CreateContentUseCase.java index 975c66ff..2afd7967 100644 --- a/Api/src/main/java/allchive/server/api/content/service/CreateContentUseCase.java +++ b/Api/src/main/java/allchive/server/api/content/service/CreateContentUseCase.java @@ -29,15 +29,23 @@ public class CreateContentUseCase { @Transactional public void execute(CreateContentRequest request) { - archivingValidator.validateExistById(request.getArchivingId()); + validateExecution(request); + Content content = contentMapper.toEntity(request); + createContentTagGroup(content, request.getTagIds()); + contentDomainService.save(content); + } + + private void validateExecution(CreateContentRequest request) { Long userId = SecurityUtil.getCurrentUserId(); + archivingValidator.validateExistById(request.getArchivingId()); archivingValidator.validateArchivingUser(request.getArchivingId(), userId); tagValidator.validateExistTagsAndUser(request.getTagIds(), userId); - List<Tag> tags = tagAdaptor.queryTagInTagIdList(request.getTagIds()); - Content content = contentMapper.toEntity(request); + } + + private void createContentTagGroup(Content content, List<Long> tagIds) { + List<Tag> tags = tagAdaptor.queryTagByTagIdIn(tagIds); List<ContentTagGroup> contentTagGroupList = contentMapper.toContentTagGroupEntityList(content, tags); contentTagGroupDomainService.saveAll(contentTagGroupList); - contentDomainService.save(content); } } diff --git a/Api/src/main/java/allchive/server/api/content/service/DeleteContentUseCase.java b/Api/src/main/java/allchive/server/api/content/service/DeleteContentUseCase.java index a09c2ec6..97c4043c 100644 --- a/Api/src/main/java/allchive/server/api/content/service/DeleteContentUseCase.java +++ b/Api/src/main/java/allchive/server/api/content/service/DeleteContentUseCase.java @@ -4,10 +4,8 @@ import allchive.server.api.config.security.SecurityUtil; import allchive.server.api.recycle.model.mapper.RecycleMapper; import allchive.server.core.annotation.UseCase; -import allchive.server.domain.domains.archiving.validator.ArchivingValidator; -import allchive.server.domain.domains.content.adaptor.ContentAdaptor; -import allchive.server.domain.domains.content.domain.Content; import allchive.server.domain.domains.content.service.ContentDomainService; +import allchive.server.domain.domains.content.validator.ContentValidator; import allchive.server.domain.domains.recycle.domain.Recycle; import allchive.server.domain.domains.recycle.domain.enums.RecycleType; import allchive.server.domain.domains.recycle.service.RecycleDomainService; @@ -17,8 +15,7 @@ @UseCase @RequiredArgsConstructor public class DeleteContentUseCase { - private final ContentAdaptor contentAdaptor; - private final ArchivingValidator archivingValidator; + private final ContentValidator contentValidator; private final ContentDomainService contentDomainService; private final RecycleMapper recycleMapper; private final RecycleDomainService recycleDomainService; @@ -26,9 +23,16 @@ public class DeleteContentUseCase { @Transactional public void execute(Long contentId) { Long userId = SecurityUtil.getCurrentUserId(); - Content content = contentAdaptor.findById(contentId); - archivingValidator.validateArchivingUser(content.getArchivingId(), userId); + validateExecution(contentId, userId); contentDomainService.softDeleteById(contentId); + createRecycle(userId, contentId); + } + + private void validateExecution(Long contentId, Long userId) { + contentValidator.verifyUser(contentId, userId); + } + + private void createRecycle(Long userId, Long contentId) { Recycle recycle = recycleMapper.toContentRecycleEntity(userId, contentId, RecycleType.CONTENT); recycleDomainService.save(recycle); diff --git a/Api/src/main/java/allchive/server/api/content/service/GetContentUseCase.java b/Api/src/main/java/allchive/server/api/content/service/GetContentUseCase.java index 59115dff..e61fc1d7 100644 --- a/Api/src/main/java/allchive/server/api/content/service/GetContentUseCase.java +++ b/Api/src/main/java/allchive/server/api/content/service/GetContentUseCase.java @@ -5,6 +5,8 @@ import allchive.server.api.content.model.dto.response.ContentTagResponse; import allchive.server.api.content.model.mapper.ContentMapper; import allchive.server.core.annotation.UseCase; +import allchive.server.domain.domains.archiving.adaptor.ArchivingAdaptor; +import allchive.server.domain.domains.archiving.domain.Archiving; import allchive.server.domain.domains.archiving.validator.ArchivingValidator; import allchive.server.domain.domains.content.adaptor.ContentAdaptor; import allchive.server.domain.domains.content.adaptor.ContentTagGroupAdaptor; @@ -21,14 +23,28 @@ public class GetContentUseCase { private final ContentAdaptor contentAdaptor; private final ContentTagGroupAdaptor contentTagGroupAdaptor; private final ContentMapper contentMapper; + private final ArchivingAdaptor archivingAdaptor; @Transactional(readOnly = true) public ContentTagResponse execute(Long contentId) { - Content content = contentAdaptor.findById(contentId); Long userId = SecurityUtil.getCurrentUserId(); - archivingValidator.validatePublicStatus(content.getArchivingId(), userId); + Content content = contentAdaptor.findById(contentId); + validateExecution(content.getArchivingId(), userId); List<ContentTagGroup> contentTagGroupList = - contentTagGroupAdaptor.findAllByContent(content); - return contentMapper.toContentTagResponse(content, contentTagGroupList); + contentTagGroupAdaptor.queryContentTagGroupByContentWithTag(content); + Boolean isMine = calculateIsMine(content.getArchivingId(), userId); + return contentMapper.toContentTagResponse(content, contentTagGroupList, isMine); + } + + private void validateExecution(Long archivingId, Long userId) { + archivingValidator.validatePublicStatus(archivingId, userId); + } + + private Boolean calculateIsMine(Long archivingId, Long userId) { + Archiving archiving = archivingAdaptor.findById(archivingId); + if (archiving.getUserId().equals(userId)) { + return Boolean.TRUE; + } + return Boolean.FALSE; } } diff --git a/Api/src/main/java/allchive/server/api/content/service/UpdateContentUseCase.java b/Api/src/main/java/allchive/server/api/content/service/UpdateContentUseCase.java new file mode 100644 index 00000000..b2286199 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/content/service/UpdateContentUseCase.java @@ -0,0 +1,61 @@ +package allchive.server.api.content.service; + + +import allchive.server.api.common.util.UrlUtil; +import allchive.server.api.config.security.SecurityUtil; +import allchive.server.api.content.model.dto.request.UpdateContentRequest; +import allchive.server.api.content.model.mapper.ContentMapper; +import allchive.server.core.annotation.UseCase; +import allchive.server.domain.domains.content.adaptor.ContentAdaptor; +import allchive.server.domain.domains.content.adaptor.TagAdaptor; +import allchive.server.domain.domains.content.domain.Content; +import allchive.server.domain.domains.content.domain.ContentTagGroup; +import allchive.server.domain.domains.content.domain.Tag; +import allchive.server.domain.domains.content.service.ContentDomainService; +import allchive.server.domain.domains.content.service.ContentTagGroupDomainService; +import allchive.server.domain.domains.content.validator.ContentValidator; +import allchive.server.domain.domains.content.validator.TagValidator; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.transaction.annotation.Transactional; + +@UseCase +@RequiredArgsConstructor +public class UpdateContentUseCase { + private final ContentValidator contentValidator; + private final TagValidator tagValidator; + private final ContentAdaptor contentAdaptor; + private final TagAdaptor tagAdaptor; + private final ContentMapper contentMapper; + private final ContentDomainService contentDomainService; + private final ContentTagGroupDomainService contentTagGroupDomainService; + + @Transactional + public void execute(Long contentId, UpdateContentRequest request) { + validateExecution(contentId, request); + regenerateContentTagGroup(contentId, request.getTagIds()); + contentDomainService.update( + contentId, + request.getContentType(), + request.getArchivingId(), + request.getLink(), + request.getMemo(), + UrlUtil.convertUrlToKey(request.getImgUrl()), + request.getTitle()); + } + + private void validateExecution(Long contentId, UpdateContentRequest request) { + Long userId = SecurityUtil.getCurrentUserId(); + contentValidator.verifyUser(contentId, userId); + tagValidator.validateExistTagsAndUser(request.getTagIds(), userId); + } + + private void regenerateContentTagGroup(Long contentId, List<Long> tagIds) { + Content content = contentAdaptor.findById(contentId); + contentTagGroupDomainService.deleteAllByContent(content); + List<Tag> tags = tagAdaptor.queryTagByTagIdIn(tagIds); + List<ContentTagGroup> contentTagGroupList = + contentMapper.toContentTagGroupEntityList(content, tags); + contentTagGroupDomainService.saveAll(contentTagGroupList); + } +} diff --git a/Api/src/main/java/allchive/server/api/recycle/service/GetDeletedObjectUseCase.java b/Api/src/main/java/allchive/server/api/recycle/service/GetDeletedObjectUseCase.java index 7bba3ccc..733b5dff 100644 --- a/Api/src/main/java/allchive/server/api/recycle/service/GetDeletedObjectUseCase.java +++ b/Api/src/main/java/allchive/server/api/recycle/service/GetDeletedObjectUseCase.java @@ -43,7 +43,8 @@ public DeletedObjectResponse execute() { .toList(); List<Archiving> archivings = archivingAdaptor.findAllByIdIn(archivingIds); List<Content> contents = contentAdaptor.findAllByIdIn(contentIds); - List<ContentTagGroup> contentTagGroups = contentTagGroupAdaptor.queryContentIn(contents); + List<ContentTagGroup> contentTagGroups = + contentTagGroupAdaptor.queryContentTagGroupByContentIn(contents); return recycleMapper.toDeletedObjectResponse( archivings, userId, contents, contentTagGroups); } diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentTagGroupAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentTagGroupAdaptor.java index d2f4b492..942c917e 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentTagGroupAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentTagGroupAdaptor.java @@ -14,12 +14,8 @@ public class ContentTagGroupAdaptor { private final ContentTagGroupRepository contentTagGroupRepository; - public List<ContentTagGroup> queryContentIn(List<Content> contentList) { - return contentTagGroupRepository.queryContentTagGroupIn(contentList); - } - - public List<ContentTagGroup> findAllByContent(Content content) { - return contentTagGroupRepository.findAllByContent(content); + public List<ContentTagGroup> queryContentTagGroupByContentIn(List<Content> contentList) { + return contentTagGroupRepository.queryContentTagGroupByContentIn(contentList); } public void deleteByTag(Tag tag) { @@ -37,4 +33,12 @@ public void deleteAllByContentIn(List<Content> contents) { public void deleteAllByTagIn(List<Tag> tagList) { contentTagGroupRepository.deleteAllByTagIn(tagList); } + + public List<ContentTagGroup> queryContentTagGroupByContentWithTag(Content content) { + return contentTagGroupRepository.queryContentTagGroupByContentWithTag(content); + } + + public void deleteAllByContent(Content content) { + contentTagGroupRepository.deleteAllByContent(content); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/TagAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/TagAdaptor.java index 5a1df191..5b6e53ae 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/TagAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/TagAdaptor.java @@ -33,8 +33,8 @@ public void deleteById(Long tagId) { tagRepository.deleteById(tagId); } - public List<Tag> queryTagInTagIdList(List<Long> tagIds) { - return tagRepository.queryTagInTagIdList(tagIds); + public List<Tag> queryTagByTagIdIn(List<Long> tagIds) { + return tagRepository.queryTagByTagIdIn(tagIds); } public List<Tag> findAllByUserId(Long userId) { diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/domain/Content.java b/Domain/src/main/java/allchive/server/domain/domains/content/domain/Content.java index 8e863066..7d1280d5 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/domain/Content.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/domain/Content.java @@ -75,4 +75,18 @@ public void delete() { public void restore() { this.deleteStatus = Boolean.FALSE; } + + public void updateLinkContent(Long archivingId, String link, String title, String memo) { + this.archivingId = archivingId; + this.linkUrl = link; + this.title = title; + this.memo = memo; + } + + public void updateImageContent(Long archivingId, String imgUrl, String title, String memo) { + this.archivingId = archivingId; + this.imageUrl = imgUrl; + this.title = title; + this.memo = memo; + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/domain/Tag.java b/Domain/src/main/java/allchive/server/domain/domains/content/domain/Tag.java index dd22dee8..0f0981f6 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/domain/Tag.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/domain/Tag.java @@ -2,7 +2,7 @@ import allchive.server.domain.common.model.BaseTimeEntity; -import allchive.server.domain.domains.content.exception.exceptions.NoAuthorityUpdateException; +import allchive.server.domain.domains.content.exception.exceptions.NoAuthorityUpdateTagException; import java.time.LocalDateTime; import javax.persistence.*; import lombok.AccessLevel; @@ -37,7 +37,7 @@ public static Tag of(String name, Long userId) { public void validateUser(Long userId) { if (!this.userId.equals(userId)) { - throw NoAuthorityUpdateException.EXCEPTION; + throw NoAuthorityUpdateTagException.EXCEPTION; } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/exception/ContentErrorCode.java b/Domain/src/main/java/allchive/server/domain/domains/content/exception/ContentErrorCode.java index 592614ba..58d96d31 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/exception/ContentErrorCode.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/exception/ContentErrorCode.java @@ -13,6 +13,8 @@ public enum ContentErrorCode implements BaseErrorCode { CONTENT_NOT_FOUND(NOT_FOUND, "CONTENT_404_1", "카테고리를 찾을 수 없습니다."), + NO_AUTHORITY_UPDATE_CONTENT(FORBIDDEN, "TAG_403_1", "컨텐츠 수정 권한이 없습니다."), + TAG_NOT_FOUND(NOT_FOUND, "TAG_404_1", "태그를 찾을 수 없습니다."), NO_AUTHORITY_UPDATE_TAG(FORBIDDEN, "TAG_403_1", "태그 수정 권한이 없습니다."); diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/exception/exceptions/NoAuthorityUpdateContentException.java b/Domain/src/main/java/allchive/server/domain/domains/content/exception/exceptions/NoAuthorityUpdateContentException.java new file mode 100644 index 00000000..43c62176 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/content/exception/exceptions/NoAuthorityUpdateContentException.java @@ -0,0 +1,14 @@ +package allchive.server.domain.domains.content.exception.exceptions; + + +import allchive.server.core.error.BaseErrorException; +import allchive.server.domain.domains.content.exception.ContentErrorCode; + +public class NoAuthorityUpdateContentException extends BaseErrorException { + + public static final BaseErrorException EXCEPTION = new NoAuthorityUpdateContentException(); + + private NoAuthorityUpdateContentException() { + super(ContentErrorCode.NO_AUTHORITY_UPDATE_CONTENT); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/exception/exceptions/NoAuthorityUpdateTagException.java b/Domain/src/main/java/allchive/server/domain/domains/content/exception/exceptions/NoAuthorityUpdateTagException.java new file mode 100644 index 00000000..5e1a907a --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/content/exception/exceptions/NoAuthorityUpdateTagException.java @@ -0,0 +1,14 @@ +package allchive.server.domain.domains.content.exception.exceptions; + + +import allchive.server.core.error.BaseErrorException; +import allchive.server.domain.domains.content.exception.ContentErrorCode; + +public class NoAuthorityUpdateTagException extends BaseErrorException { + + public static final BaseErrorException EXCEPTION = new NoAuthorityUpdateTagException(); + + private NoAuthorityUpdateTagException() { + super(ContentErrorCode.NO_AUTHORITY_UPDATE_TAG); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentTagGroupCustomRepository.java b/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentTagGroupCustomRepository.java index bdafbe86..546979cb 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentTagGroupCustomRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentTagGroupCustomRepository.java @@ -6,5 +6,7 @@ import java.util.List; public interface ContentTagGroupCustomRepository { - public List<ContentTagGroup> queryContentTagGroupIn(List<Content> contentList); + public List<ContentTagGroup> queryContentTagGroupByContentIn(List<Content> contentList); + + List<ContentTagGroup> queryContentTagGroupByContentWithTag(Content content); } diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentTagGroupCustomRepositoryImpl.java b/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentTagGroupCustomRepositoryImpl.java index 7d4124b8..57f47f4a 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentTagGroupCustomRepositoryImpl.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentTagGroupCustomRepositoryImpl.java @@ -17,20 +17,35 @@ public class ContentTagGroupCustomRepositoryImpl implements ContentTagGroupCusto private final JPAQueryFactory queryFactory; @Override - public List<ContentTagGroup> queryContentTagGroupIn(List<Content> contentList) { + public List<ContentTagGroup> queryContentTagGroupByContentIn(List<Content> contentList) { return queryFactory .selectFrom(contentTagGroup) .join(contentTagGroup.tag, tag) .fetchJoin() - .where(archivingIdEq(contentList)) + .where(contentIdIn(contentList)) .orderBy(createdAtDesc()) .fetch(); } - private BooleanExpression archivingIdEq(List<Content> contentList) { + @Override + public List<ContentTagGroup> queryContentTagGroupByContentWithTag(Content content) { + return queryFactory + .selectFrom(contentTagGroup) + .join(contentTagGroup.tag, tag) + .fetchJoin() + .where(contentEq(content)) + .orderBy(createdAtDesc()) + .fetch(); + } + + private BooleanExpression contentIdIn(List<Content> contentList) { return contentTagGroup.content.in(contentList); } + private BooleanExpression contentEq(Content content) { + return contentTagGroup.content.eq(content); + } + private OrderSpecifier<LocalDateTime> createdAtDesc() { return contentTagGroup.createdAt.desc(); } diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentTagGroupRepository.java b/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentTagGroupRepository.java index f218bdae..ffa84d62 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentTagGroupRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentTagGroupRepository.java @@ -16,4 +16,6 @@ public interface ContentTagGroupRepository void deleteAllByContentIn(List<Content> contents); void deleteAllByTagIn(List<Tag> tagList); + + void deleteAllByContent(Content content); } diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/repository/TagCustomRepository.java b/Domain/src/main/java/allchive/server/domain/domains/content/repository/TagCustomRepository.java index 362e9133..3c93394b 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/repository/TagCustomRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/repository/TagCustomRepository.java @@ -7,5 +7,5 @@ public interface TagCustomRepository { List<Tag> queryTagByUserIdOrderByUsedAt(Long userId); - List<Tag> queryTagInTagIdList(List<Long> tagIds); + List<Tag> queryTagByTagIdIn(List<Long> tagIds); } diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/repository/TagCustomRepositoryImpl.java b/Domain/src/main/java/allchive/server/domain/domains/content/repository/TagCustomRepositoryImpl.java index 9cdcbf0a..5deb5775 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/repository/TagCustomRepositoryImpl.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/repository/TagCustomRepositoryImpl.java @@ -24,7 +24,7 @@ public List<Tag> queryTagByUserIdOrderByUsedAt(Long userId) { } @Override - public List<Tag> queryTagInTagIdList(List<Long> tagIds) { + public List<Tag> queryTagByTagIdIn(List<Long> tagIds) { return queryFactory.selectFrom(tag).where(tagIdIn(tagIds)).fetch(); } diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/service/ContentDomainService.java b/Domain/src/main/java/allchive/server/domain/domains/content/service/ContentDomainService.java index 2b2fd708..47c902ed 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/service/ContentDomainService.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/service/ContentDomainService.java @@ -4,6 +4,7 @@ import allchive.server.core.annotation.DomainService; import allchive.server.domain.domains.content.adaptor.ContentAdaptor; import allchive.server.domain.domains.content.domain.Content; +import allchive.server.domain.domains.content.domain.enums.ContentType; import java.util.List; import lombok.RequiredArgsConstructor; @@ -35,4 +36,20 @@ public void deleteAllById(List<Long> contentIds) { public void deleteAllByArchivingIdIn(List<Long> archivingId) { contentAdaptor.deleteAllByArchivingIdIn(archivingId); } + + public void update( + Long contentId, + ContentType contentType, + Long archivingId, + String link, + String memo, + String imgUrl, + String title) { + Content content = contentAdaptor.findById(contentId); + switch (contentType) { + case LINK -> content.updateLinkContent(archivingId, link, title, memo); + case IMAGE -> content.updateImageContent(archivingId, imgUrl, title, memo); + } + contentAdaptor.save(content); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/service/ContentTagGroupDomainService.java b/Domain/src/main/java/allchive/server/domain/domains/content/service/ContentTagGroupDomainService.java index 3f0dbf45..55235fc3 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/service/ContentTagGroupDomainService.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/service/ContentTagGroupDomainService.java @@ -29,4 +29,8 @@ public void deleteByContentIn(List<Content> contents) { public void deleteAllByTagIn(List<Tag> tagList) { contentTagGroupAdaptor.deleteAllByTagIn(tagList); } + + public void deleteAllByContent(Content content) { + contentTagGroupAdaptor.deleteAllByContent(content); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/validator/ContentValidator.java b/Domain/src/main/java/allchive/server/domain/domains/content/validator/ContentValidator.java index f14abc84..5aea2a35 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/validator/ContentValidator.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/validator/ContentValidator.java @@ -2,10 +2,12 @@ import allchive.server.core.annotation.Validator; -import allchive.server.domain.domains.archiving.validator.ArchivingValidator; +import allchive.server.domain.domains.archiving.adaptor.ArchivingAdaptor; +import allchive.server.domain.domains.archiving.domain.Archiving; import allchive.server.domain.domains.content.adaptor.ContentAdaptor; import allchive.server.domain.domains.content.domain.Content; import allchive.server.domain.domains.content.exception.exceptions.ContentNotFoundException; +import allchive.server.domain.domains.content.exception.exceptions.NoAuthorityUpdateContentException; import java.util.List; import lombok.RequiredArgsConstructor; @@ -13,7 +15,7 @@ @RequiredArgsConstructor public class ContentValidator { private final ContentAdaptor contentAdaptor; - private final ArchivingValidator archivingValidator; + private final ArchivingAdaptor archivingAdaptor; public void validateExistInIdList(List<Long> contentIdList) { List<Content> contentList = contentAdaptor.findAllByIdIn(contentIdList); @@ -28,7 +30,13 @@ public void verifyUserInIdList(Long userId, List<Long> contentIds) { .map(Content::getArchivingId) .distinct() .toList(); - archivingValidator.verifyUserInIdList(userId, archivingIds); + List<Archiving> archivingList = archivingAdaptor.findAllByIdIn(archivingIds); + archivingList.forEach( + archiving -> { + if (!archiving.getUserId().equals(userId)) { + throw NoAuthorityUpdateContentException.EXCEPTION; + } + }); } public void validateExistById(Long contentId) { @@ -36,4 +44,12 @@ public void validateExistById(Long contentId) { throw ContentNotFoundException.EXCEPTION; } } + + public void verifyUser(Long contentId, Long userId) { + Long archivingId = contentAdaptor.findById(contentId).getArchivingId(); + Long archivingUserId = archivingAdaptor.findById(archivingId).getUserId(); + if (!archivingUserId.equals(userId)) { + throw NoAuthorityUpdateContentException.EXCEPTION; + } + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/validator/TagValidator.java b/Domain/src/main/java/allchive/server/domain/domains/content/validator/TagValidator.java index 3b75d3e2..af25a203 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/validator/TagValidator.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/validator/TagValidator.java @@ -4,7 +4,7 @@ import allchive.server.core.annotation.Validator; import allchive.server.domain.domains.content.adaptor.TagAdaptor; import allchive.server.domain.domains.content.domain.Tag; -import allchive.server.domain.domains.content.exception.exceptions.NoAuthorityUpdateException; +import allchive.server.domain.domains.content.exception.exceptions.NoAuthorityUpdateTagException; import allchive.server.domain.domains.content.exception.exceptions.TagNotFoundException; import java.util.List; import lombok.RequiredArgsConstructor; @@ -19,14 +19,14 @@ public void verifyUser(Long tagId, Long userId) { } public void validateExistTagsAndUser(List<Long> tagIds, Long userId) { - List<Tag> tags = tagAdaptor.queryTagInTagIdList(tagIds); + List<Tag> tags = tagAdaptor.queryTagByTagIdIn(tagIds); if (tagIds.size() != tags.size()) { throw TagNotFoundException.EXCEPTION; } tags.forEach( tag -> { if (!tag.getUserId().equals(userId)) { - throw NoAuthorityUpdateException.EXCEPTION; + throw NoAuthorityUpdateTagException.EXCEPTION; } }); } From 6075e47d69d327b89900ba1cc920c4642ffdf4c4 Mon Sep 17 00:00:00 2001 From: Sanghoon Jeong <67852689+wjdtkdgns@users.noreply.github.com> Date: Fri, 21 Jul 2023 16:28:19 +0900 Subject: [PATCH 40/41] =?UTF-8?q?[refac]=20archiving=20=EB=A6=AC=ED=8C=A9?= =?UTF-8?q?=ED=86=A0=EB=A7=81=20(#51)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [refac] archiving usecase 리팩토링, validation 수정 #49 * [refac] archiving 미사용 함수 정리 및 adaptor 함수 이름 변경 #49 --- .../response/ArchivingContentsResponse.java | 12 ++++++-- .../service/CreateArchivingUseCase.java | 2 +- .../service/DeleteArchivingUseCase.java | 10 ++++--- .../service/GetArchivedArchivingUseCase.java | 1 - .../service/GetArchivingContentsUseCase.java | 28 +++++++++++++++---- .../service/GetScrapArchivingUseCase.java | 2 +- .../service/UpdateArchivingPinUseCase.java | 11 ++++++-- .../service/UpdateArchivingScrapUseCase.java | 14 +++++++--- .../service/UpdateArchivingUseCase.java | 8 ++++-- .../api/auth/service/TokenRefreshUseCase.java | 2 +- .../api/auth/service/WithdrawUserUseCase.java | 2 +- .../api/block/service/CreateBlockUseCase.java | 2 +- .../api/block/service/DeleteBlockUseCase.java | 2 +- .../model/dto/response/ImageUrlResponse.java | 11 ++------ .../recycle/model/mapper/RecycleMapper.java | 4 +-- .../api/user/service/GetUserInfoUseCase.java | 2 +- .../user/service/GetUserProfileUseCase.java | 2 +- .../archiving/adaptor/ArchivingAdaptor.java | 12 ++------ .../repository/ArchivingCustomRepository.java | 2 +- .../ArchivingCustomRepositoryImpl.java | 2 +- .../repository/ArchivingRepository.java | 2 -- .../service/ArchivingDomainService.java | 2 +- .../domains/user/adaptor/ScrapAdaptor.java | 8 ++++-- .../domains/user/adaptor/UserAdaptor.java | 6 ++-- .../user/repository/UserRepository.java | 2 ++ .../user/service/ScrapDomainService.java | 7 +---- .../user/service/UserDomainService.java | 6 ++-- .../user/validator/ScrapValidator.java | 12 ++++++-- .../domains/user/validator/UserValidator.java | 2 +- .../server/infrastructure/s3/ImageUrlDto.java | 8 ++---- .../s3/S3PresignedUrlService.java | 2 +- 31 files changed, 110 insertions(+), 78 deletions(-) diff --git a/Api/src/main/java/allchive/server/api/archiving/model/dto/response/ArchivingContentsResponse.java b/Api/src/main/java/allchive/server/api/archiving/model/dto/response/ArchivingContentsResponse.java index 10479f2d..d4e14344 100644 --- a/Api/src/main/java/allchive/server/api/archiving/model/dto/response/ArchivingContentsResponse.java +++ b/Api/src/main/java/allchive/server/api/archiving/model/dto/response/ArchivingContentsResponse.java @@ -21,25 +21,33 @@ public class ArchivingContentsResponse { @Schema(description = "아카이빙의 총 컨텐츠 개수") private Long totalContentsCount; + @Schema(description = "유저 소유 여부") + private Boolean isMine; + @Builder private ArchivingContentsResponse( SliceResponse<ContentResponse> contents, String archivingTitle, Long archivingId, - Long totalContentsCount) { + Long totalContentsCount, + Boolean isMine) { this.contents = contents; this.archivingTitle = archivingTitle; this.archivingId = archivingId; this.totalContentsCount = totalContentsCount; + this.isMine = isMine; } public static ArchivingContentsResponse of( - SliceResponse<ContentResponse> contentResponseSlice, Archiving archiving) { + SliceResponse<ContentResponse> contentResponseSlice, + Archiving archiving, + Boolean isMine) { return ArchivingContentsResponse.builder() .archivingId(archiving.getId()) .archivingTitle(archiving.getTitle()) .totalContentsCount(archiving.getScrapCnt() + archiving.getImgCnt()) .contents(contentResponseSlice) + .isMine(isMine) .build(); } } diff --git a/Api/src/main/java/allchive/server/api/archiving/service/CreateArchivingUseCase.java b/Api/src/main/java/allchive/server/api/archiving/service/CreateArchivingUseCase.java index 43eaddb6..b52ec1c9 100644 --- a/Api/src/main/java/allchive/server/api/archiving/service/CreateArchivingUseCase.java +++ b/Api/src/main/java/allchive/server/api/archiving/service/CreateArchivingUseCase.java @@ -20,6 +20,6 @@ public class CreateArchivingUseCase { public void execute(CreateArchivingRequest request) { Long userId = SecurityUtil.getCurrentUserId(); final Archiving archiving = archivingMapper.toEntity(request, userId); - archivingDomainService.createArchiving(archiving); + archivingDomainService.save(archiving); } } diff --git a/Api/src/main/java/allchive/server/api/archiving/service/DeleteArchivingUseCase.java b/Api/src/main/java/allchive/server/api/archiving/service/DeleteArchivingUseCase.java index b2117a79..d54ddd61 100644 --- a/Api/src/main/java/allchive/server/api/archiving/service/DeleteArchivingUseCase.java +++ b/Api/src/main/java/allchive/server/api/archiving/service/DeleteArchivingUseCase.java @@ -7,7 +7,6 @@ import allchive.server.domain.domains.archiving.service.ArchivingDomainService; import allchive.server.domain.domains.archiving.validator.ArchivingValidator; import allchive.server.domain.domains.recycle.domain.Recycle; -import allchive.server.domain.domains.recycle.domain.enums.RecycleType; import allchive.server.domain.domains.recycle.service.RecycleDomainService; import lombok.RequiredArgsConstructor; import org.springframework.transaction.annotation.Transactional; @@ -23,10 +22,13 @@ public class DeleteArchivingUseCase { @Transactional public void execute(Long archivingId) { Long userId = SecurityUtil.getCurrentUserId(); - archivingValidator.verifyUser(userId, archivingId); + validateExecution(archivingId, userId); archivingDomainService.softDeleteById(archivingId); - Recycle recycle = - recycleMapper.toArchivingRecycleEntity(userId, archivingId, RecycleType.ARCHIVING); + Recycle recycle = recycleMapper.toArchivingRecycleEntity(userId, archivingId); recycleDomainService.save(recycle); } + + private void validateExecution(Long archivingId, Long userId) { + archivingValidator.verifyUser(userId, archivingId); + } } diff --git a/Api/src/main/java/allchive/server/api/archiving/service/GetArchivedArchivingUseCase.java b/Api/src/main/java/allchive/server/api/archiving/service/GetArchivedArchivingUseCase.java index e42b8f4c..edaaf4f6 100644 --- a/Api/src/main/java/allchive/server/api/archiving/service/GetArchivedArchivingUseCase.java +++ b/Api/src/main/java/allchive/server/api/archiving/service/GetArchivedArchivingUseCase.java @@ -15,7 +15,6 @@ @UseCase @RequiredArgsConstructor public class GetArchivedArchivingUseCase { - // TODO : 이거 네이밍 한번 수정해야함 private final ArchivingAdaptor archivingAdaptor; @Transactional(readOnly = true) diff --git a/Api/src/main/java/allchive/server/api/archiving/service/GetArchivingContentsUseCase.java b/Api/src/main/java/allchive/server/api/archiving/service/GetArchivingContentsUseCase.java index 04897b73..cc628be7 100644 --- a/Api/src/main/java/allchive/server/api/archiving/service/GetArchivingContentsUseCase.java +++ b/Api/src/main/java/allchive/server/api/archiving/service/GetArchivingContentsUseCase.java @@ -32,16 +32,34 @@ public class GetArchivingContentsUseCase { @Transactional(readOnly = true) public ArchivingContentsResponse execute(Long archivingId, Pageable pageable) { Long userId = SecurityUtil.getCurrentUserId(); + validateExecution(archivingId, userId); + Archiving archiving = archivingAdaptor.findById(archivingId); + Slice<ContentResponse> contentResponseSlice = + getContentResponseSlice(archivingId, pageable); + return ArchivingContentsResponse.of( + SliceResponse.of(contentResponseSlice), + archiving, + calculateIsMine(archiving, userId)); + } + + private void validateExecution(Long archivingId, Long userId) { archivingValidator.validatePublicStatus(archivingId, userId); archivingValidator.validateDeleteStatus(archivingId, userId); - Archiving archiving = archivingAdaptor.findById(archivingId); + } + + private Slice<ContentResponse> getContentResponseSlice(Long archivingId, Pageable pageable) { Slice<Content> contentList = contentAdaptor.querySliceContentByArchivingId(archivingId, pageable); List<ContentTagGroup> contentTagGroupList = contentTagGroupAdaptor.queryContentTagGroupByContentIn(contentList.getContent()); - Slice<ContentResponse> contentResponseSlice = - contentList.map( - content -> contentMapper.toContentResponse(content, contentTagGroupList)); - return ArchivingContentsResponse.of(SliceResponse.of(contentResponseSlice), archiving); + return contentList.map( + content -> contentMapper.toContentResponse(content, contentTagGroupList)); + } + + private Boolean calculateIsMine(Archiving archiving, Long userId) { + if (archiving.getUserId().equals(userId)) { + return Boolean.TRUE; + } + return Boolean.FALSE; } } diff --git a/Api/src/main/java/allchive/server/api/archiving/service/GetScrapArchivingUseCase.java b/Api/src/main/java/allchive/server/api/archiving/service/GetScrapArchivingUseCase.java index c092d6bc..9ec09011 100644 --- a/Api/src/main/java/allchive/server/api/archiving/service/GetScrapArchivingUseCase.java +++ b/Api/src/main/java/allchive/server/api/archiving/service/GetScrapArchivingUseCase.java @@ -28,7 +28,7 @@ public SliceResponse<ArchivingResponse> execute(Category category, Pageable page scrapAdaptor.findAllByUserId(userId).stream().map(Scrap::getArchivingId).toList(); Slice<ArchivingResponse> archivingSlices = archivingAdaptor - .querySliceArchivingIn(archivingIdList, category, pageable) + .querySliceArchivingByIdIn(archivingIdList, category, pageable) .map( archiving -> ArchivingResponse.of( diff --git a/Api/src/main/java/allchive/server/api/archiving/service/UpdateArchivingPinUseCase.java b/Api/src/main/java/allchive/server/api/archiving/service/UpdateArchivingPinUseCase.java index a85f5d9e..5108bc34 100644 --- a/Api/src/main/java/allchive/server/api/archiving/service/UpdateArchivingPinUseCase.java +++ b/Api/src/main/java/allchive/server/api/archiving/service/UpdateArchivingPinUseCase.java @@ -17,14 +17,21 @@ public class UpdateArchivingPinUseCase { @Transactional public void execute(Long archivingId, Boolean cancel) { Long userId = SecurityUtil.getCurrentUserId(); + validateExecution(archivingId, userId, cancel); + if (cancel) { + archivingDomainService.updatePin(archivingId, userId, false); + } else { + archivingDomainService.updatePin(archivingId, userId, true); + } + } + + private void validateExecution(Long archivingId, Long userId, Boolean cancel) { archivingValidator.validateExistById(archivingId); archivingValidator.validateDeleteStatus(archivingId, userId); if (cancel) { archivingValidator.validateNotPinStatus(archivingId, userId); - archivingDomainService.updatePin(archivingId, userId, false); } else { archivingValidator.validateAlreadyPinStatus(archivingId, userId); - archivingDomainService.updatePin(archivingId, userId, true); } } } diff --git a/Api/src/main/java/allchive/server/api/archiving/service/UpdateArchivingScrapUseCase.java b/Api/src/main/java/allchive/server/api/archiving/service/UpdateArchivingScrapUseCase.java index 4bce3f22..9ff7468b 100644 --- a/Api/src/main/java/allchive/server/api/archiving/service/UpdateArchivingScrapUseCase.java +++ b/Api/src/main/java/allchive/server/api/archiving/service/UpdateArchivingScrapUseCase.java @@ -24,18 +24,24 @@ public class UpdateArchivingScrapUseCase { @Transactional public void execute(Long archivingId, Boolean cancel) { - archivingValidator.validateExistById(archivingId); Long userId = SecurityUtil.getCurrentUserId(); - archivingValidator.validateDeleteStatus(archivingId, userId); - User user = userAdaptor.findUserById(userId); + validateExecution(archivingId, userId, cancel); + User user = userAdaptor.findById(userId); if (cancel) { scrapDomainService.deleteScrapByUserAndArchivingId(user, archivingId); archivingDomainService.updateScrapCount(archivingId, -1); } else { - scrapValidator.validateExistScrap(user, archivingId); Scrap scrap = Scrap.of(user, archivingId); scrapDomainService.save(scrap); archivingDomainService.updateScrapCount(archivingId, 1); } } + + private void validateExecution(Long archivingId, Long userId, Boolean cancel) { + archivingValidator.validateExistById(archivingId); + archivingValidator.validateDeleteStatus(archivingId, userId); + if (!cancel) { + scrapValidator.validateExistScrap(userId, archivingId); + } + } } diff --git a/Api/src/main/java/allchive/server/api/archiving/service/UpdateArchivingUseCase.java b/Api/src/main/java/allchive/server/api/archiving/service/UpdateArchivingUseCase.java index 65ceabfd..15fe872f 100644 --- a/Api/src/main/java/allchive/server/api/archiving/service/UpdateArchivingUseCase.java +++ b/Api/src/main/java/allchive/server/api/archiving/service/UpdateArchivingUseCase.java @@ -21,8 +21,7 @@ public class UpdateArchivingUseCase { @Transactional public void execute(Long archivingId, UpdateArchivingRequest request) { - Long userId = SecurityUtil.getCurrentUserId(); - archivingValidator.verifyUser(userId, archivingId); + validateExecution(archivingId); Archiving archiving = archivingAdaptor.findById(archivingId); archivingDomainService.updateArchiving( archiving, @@ -31,4 +30,9 @@ public void execute(Long archivingId, UpdateArchivingRequest request) { request.isPublicStatus(), request.getCategory()); } + + private void validateExecution(Long archivingId) { + Long userId = SecurityUtil.getCurrentUserId(); + archivingValidator.verifyUser(userId, archivingId); + } } diff --git a/Api/src/main/java/allchive/server/api/auth/service/TokenRefreshUseCase.java b/Api/src/main/java/allchive/server/api/auth/service/TokenRefreshUseCase.java index a0178221..740c8a5c 100644 --- a/Api/src/main/java/allchive/server/api/auth/service/TokenRefreshUseCase.java +++ b/Api/src/main/java/allchive/server/api/auth/service/TokenRefreshUseCase.java @@ -22,7 +22,7 @@ public class TokenRefreshUseCase { public OauthRegisterResponse execute(String refreshToken) { RefreshTokenEntity oldToken = refreshTokenAdaptor.findTokenByRefreshToken(refreshToken); Long userId = jwtTokenProvider.parseRefreshToken(oldToken.getRefreshToken()); - User user = userAdaptor.findUserById(userId); + User user = userAdaptor.findById(userId); return OauthRegisterResponse.from(tokenGenerateHelper.execute(user)); } } diff --git a/Api/src/main/java/allchive/server/api/auth/service/WithdrawUserUseCase.java b/Api/src/main/java/allchive/server/api/auth/service/WithdrawUserUseCase.java index 42b28a97..9e10da37 100644 --- a/Api/src/main/java/allchive/server/api/auth/service/WithdrawUserUseCase.java +++ b/Api/src/main/java/allchive/server/api/auth/service/WithdrawUserUseCase.java @@ -45,7 +45,7 @@ public class WithdrawUserUseCase { public void execute(OauthProvider provider, String appleAccessToken) { Long userId = SecurityUtil.getCurrentUserId(); - User user = userAdaptor.findUserById(userId); + User user = userAdaptor.findById(userId); // oauth쪽 탈퇴 withdrawOauth(provider, appleAccessToken, user); // 우리쪽 탈퇴 diff --git a/Api/src/main/java/allchive/server/api/block/service/CreateBlockUseCase.java b/Api/src/main/java/allchive/server/api/block/service/CreateBlockUseCase.java index 6fef2c73..fed85f8a 100644 --- a/Api/src/main/java/allchive/server/api/block/service/CreateBlockUseCase.java +++ b/Api/src/main/java/allchive/server/api/block/service/CreateBlockUseCase.java @@ -28,6 +28,6 @@ public BlockResponse execute(BlockRequest request) { blockValidator.validateNotMyself(userId, request.getUserId()); Block block = blockMapper.toEntity(userId, request.getUserId()); blockDomainService.save(block); - return BlockResponse.from(userAdaptor.findUserById(request.getUserId()).getNickname()); + return BlockResponse.from(userAdaptor.findById(request.getUserId()).getNickname()); } } diff --git a/Api/src/main/java/allchive/server/api/block/service/DeleteBlockUseCase.java b/Api/src/main/java/allchive/server/api/block/service/DeleteBlockUseCase.java index b61eec5c..257ad370 100644 --- a/Api/src/main/java/allchive/server/api/block/service/DeleteBlockUseCase.java +++ b/Api/src/main/java/allchive/server/api/block/service/DeleteBlockUseCase.java @@ -23,6 +23,6 @@ public BlockResponse execute(BlockRequest request) { Long userId = SecurityUtil.getCurrentUserId(); blockValidator.validateExist(userId, request.getUserId()); blockDomainService.deleteByBlockFromAndBlockUser(userId, request.getUserId()); - return BlockResponse.from(userAdaptor.findUserById(request.getUserId()).getNickname()); + return BlockResponse.from(userAdaptor.findById(request.getUserId()).getNickname()); } } diff --git a/Api/src/main/java/allchive/server/api/image/model/dto/response/ImageUrlResponse.java b/Api/src/main/java/allchive/server/api/image/model/dto/response/ImageUrlResponse.java index e2c9d847..27d0bfb9 100644 --- a/Api/src/main/java/allchive/server/api/image/model/dto/response/ImageUrlResponse.java +++ b/Api/src/main/java/allchive/server/api/image/model/dto/response/ImageUrlResponse.java @@ -11,19 +11,12 @@ public class ImageUrlResponse { @Schema(defaultValue = "이미지 presigned url", description = "이미지 업로드에 사용") private String url; - @Schema(defaultValue = "이미지 고유 key 값", description = "서버로 이 값만 보내주시면 됩니다") - private String key; - @Builder - private ImageUrlResponse(String url, String key) { + private ImageUrlResponse(String url) { this.url = url; - this.key = key; } public static ImageUrlResponse from(ImageUrlDto imageUrlDto) { - return ImageUrlResponse.builder() - .key(imageUrlDto.getKey()) - .url(imageUrlDto.getUrl()) - .build(); + return ImageUrlResponse.builder().url(imageUrlDto.getUrl()).build(); } } diff --git a/Api/src/main/java/allchive/server/api/recycle/model/mapper/RecycleMapper.java b/Api/src/main/java/allchive/server/api/recycle/model/mapper/RecycleMapper.java index a145693d..6cc87490 100644 --- a/Api/src/main/java/allchive/server/api/recycle/model/mapper/RecycleMapper.java +++ b/Api/src/main/java/allchive/server/api/recycle/model/mapper/RecycleMapper.java @@ -23,8 +23,8 @@ public Recycle toContentRecycleEntity(Long userId, Long contentId, RecycleType t return Recycle.of(type, contentId, null, userId); } - public Recycle toArchivingRecycleEntity(Long userId, Long archivingId, RecycleType type) { - return Recycle.of(type, null, archivingId, userId); + public Recycle toArchivingRecycleEntity(Long userId, Long archivingId) { + return Recycle.of(RecycleType.ARCHIVING, null, archivingId, userId); } public DeletedObjectResponse toDeletedObjectResponse( diff --git a/Api/src/main/java/allchive/server/api/user/service/GetUserInfoUseCase.java b/Api/src/main/java/allchive/server/api/user/service/GetUserInfoUseCase.java index bff4bdbb..0cb03dca 100644 --- a/Api/src/main/java/allchive/server/api/user/service/GetUserInfoUseCase.java +++ b/Api/src/main/java/allchive/server/api/user/service/GetUserInfoUseCase.java @@ -20,7 +20,7 @@ public class GetUserInfoUseCase { public GetUserInfoResponse execute() { Long userId = SecurityUtil.getCurrentUserId(); userValidator.validateUserStatusNormal(userId); - User user = userAdaptor.findUserById(userId); + User user = userAdaptor.findById(userId); return GetUserInfoResponse.from(user); } } diff --git a/Api/src/main/java/allchive/server/api/user/service/GetUserProfileUseCase.java b/Api/src/main/java/allchive/server/api/user/service/GetUserProfileUseCase.java index 28354c88..bf30180f 100644 --- a/Api/src/main/java/allchive/server/api/user/service/GetUserProfileUseCase.java +++ b/Api/src/main/java/allchive/server/api/user/service/GetUserProfileUseCase.java @@ -26,7 +26,7 @@ public class GetUserProfileUseCase { public GetUserProfileResponse execute() { Long userId = SecurityUtil.getCurrentUserId(); userValidator.validateUserStatusNormal(userId); - User user = userAdaptor.findUserById(userId); + User user = userAdaptor.findById(userId); List<Archiving> archivingList = archivingAdaptor.findAllByUserId(userId); return userMapper.toGetUserProfileResponse(archivingList, user); } diff --git a/Domain/src/main/java/allchive/server/domain/domains/archiving/adaptor/ArchivingAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/archiving/adaptor/ArchivingAdaptor.java index 44fb4161..b5c84f34 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/archiving/adaptor/ArchivingAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/archiving/adaptor/ArchivingAdaptor.java @@ -30,10 +30,6 @@ public Archiving findById(Long archivingId) { .orElseThrow(() -> ArchivingNotFoundException.EXCEPTION); } - public void deleteById(Long archivingId) { - archivingRepository.deleteById(archivingId); - } - public Slice<Archiving> querySliceArchivingExceptBlock( List<Long> archivingIdList, List<Long> blockList, @@ -48,9 +44,9 @@ public Slice<Archiving> querySliceArchivingByUserId( return archivingRepository.querySliceArchivingByUserId(userId, category, pageable); } - public Slice<Archiving> querySliceArchivingIn( + public Slice<Archiving> querySliceArchivingByIdIn( List<Long> archivingIdList, Category category, Pageable pageable) { - return archivingRepository.querySliceArchivingIn(archivingIdList, category, pageable); + return archivingRepository.querySliceArchivingByIdIn(archivingIdList, category, pageable); } public List<Archiving> queryArchivingByUserId(Long userId) { @@ -73,10 +69,6 @@ public void saveAll(List<Archiving> archivings) { archivingRepository.saveAll(archivings); } - public List<Archiving> findAllByUserIdAndDeleted(Long userId) { - return archivingRepository.findAllByUserIdAndDeleteStatus(userId, true); - } - public void deleteAllById(List<Long> archivingIds) { archivingRepository.deleteAllById(archivingIds); } diff --git a/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingCustomRepository.java b/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingCustomRepository.java index 0b98371b..03946df8 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingCustomRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingCustomRepository.java @@ -13,7 +13,7 @@ Slice<Archiving> querySliceArchivingExceptBlock( Slice<Archiving> querySliceArchivingByUserId(Long userId, Category category, Pageable pageable); - Slice<Archiving> querySliceArchivingIn( + Slice<Archiving> querySliceArchivingByIdIn( List<Long> archivingIdList, Category category, Pageable pageable); List<Archiving> queryArchivingByUserId(Long userId); diff --git a/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingCustomRepositoryImpl.java b/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingCustomRepositoryImpl.java index 04a6b873..c0cae02f 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingCustomRepositoryImpl.java +++ b/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingCustomRepositoryImpl.java @@ -58,7 +58,7 @@ public Slice<Archiving> querySliceArchivingByUserId( } @Override - public Slice<Archiving> querySliceArchivingIn( + public Slice<Archiving> querySliceArchivingByIdIn( List<Long> archivingIdList, Category category, Pageable pageable) { List<Archiving> archivings = queryFactory diff --git a/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingRepository.java b/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingRepository.java index 87ce7a03..e6d2c4f3 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingRepository.java @@ -11,7 +11,5 @@ public interface ArchivingRepository List<Archiving> findAllByIdIn(List<Long> ids); - List<Archiving> findAllByUserIdAndDeleteStatus(Long userId, boolean deleteStatus); - List<Archiving> findAllByPublicStatus(Boolean publicStatus); } diff --git a/Domain/src/main/java/allchive/server/domain/domains/archiving/service/ArchivingDomainService.java b/Domain/src/main/java/allchive/server/domain/domains/archiving/service/ArchivingDomainService.java index 5113434b..ffcc9f1f 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/archiving/service/ArchivingDomainService.java +++ b/Domain/src/main/java/allchive/server/domain/domains/archiving/service/ArchivingDomainService.java @@ -13,7 +13,7 @@ public class ArchivingDomainService { private final ArchivingAdaptor archivingAdaptor; - public void createArchiving(Archiving archiving) { + public void save(Archiving archiving) { archivingAdaptor.save(archiving); } diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/ScrapAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/ScrapAdaptor.java index 3b3942f9..533d97eb 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/ScrapAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/ScrapAdaptor.java @@ -4,9 +4,9 @@ import allchive.server.core.annotation.Adaptor; import allchive.server.domain.domains.user.domain.Scrap; import allchive.server.domain.domains.user.domain.User; +import allchive.server.domain.domains.user.exception.exceptions.ScrapNotFoundException; import allchive.server.domain.domains.user.repository.ScrapRepository; import java.util.List; -import java.util.Optional; import lombok.RequiredArgsConstructor; @Adaptor @@ -18,8 +18,10 @@ public List<Scrap> findAllByUserId(Long userId) { return scrapRepository.findAllByUserId(userId); } - public Optional<Scrap> findByUserAndArchivingId(User user, Long archivingId) { - return scrapRepository.findAllByUserAndArchivingId(user, archivingId); + public Scrap findByUserAndArchivingId(User user, Long archivingId) { + return scrapRepository + .findAllByUserAndArchivingId(user, archivingId) + .orElseThrow(() -> ScrapNotFoundException.EXCEPTION); } public void delete(Scrap scrap) { diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/UserAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/UserAdaptor.java index bdb3f7b4..b118c6ae 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/UserAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/UserAdaptor.java @@ -15,10 +15,10 @@ public class UserAdaptor { private final UserRepository userRepository; public Boolean exist(OauthInfo oauthInfo) { - return userRepository.findByOauthInfo(oauthInfo).isPresent(); + return userRepository.existsByOauthInfo(oauthInfo); } - public User queryUserByOauthInfo(OauthInfo oauthInfo) { + public User findByOauthInfo(OauthInfo oauthInfo) { return userRepository .findByOauthInfo(oauthInfo) .orElseThrow(() -> UserNotFoundException.EXCEPTION); @@ -28,7 +28,7 @@ public void save(User user) { userRepository.save(user); } - public User findUserById(Long userId) { + public User findById(Long userId) { return userRepository.findById(userId).orElseThrow(() -> UserNotFoundException.EXCEPTION); } diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/repository/UserRepository.java b/Domain/src/main/java/allchive/server/domain/domains/user/repository/UserRepository.java index d8785e5c..938a4009 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/repository/UserRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/repository/UserRepository.java @@ -10,6 +10,8 @@ public interface UserRepository extends JpaRepository<User, Long> { Optional<User> findByOauthInfo(OauthInfo oauthInfo); + boolean existsByOauthInfo(OauthInfo oauthInfo); + boolean existsByNickname(String nickname); List<User> findAllByIdIn(List<Long> userIds); diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/service/ScrapDomainService.java b/Domain/src/main/java/allchive/server/domain/domains/user/service/ScrapDomainService.java index 37440ece..8400ee27 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/service/ScrapDomainService.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/service/ScrapDomainService.java @@ -5,7 +5,6 @@ import allchive.server.domain.domains.user.adaptor.ScrapAdaptor; import allchive.server.domain.domains.user.domain.Scrap; import allchive.server.domain.domains.user.domain.User; -import allchive.server.domain.domains.user.exception.exceptions.ScrapNotFoundException; import java.util.List; import lombok.RequiredArgsConstructor; @@ -15,11 +14,7 @@ public class ScrapDomainService { private final ScrapAdaptor scrapAdaptor; public void deleteScrapByUserAndArchivingId(User user, Long archivingId) { - Scrap scrap = - scrapAdaptor - .findByUserAndArchivingId(user, archivingId) - .orElseThrow(() -> ScrapNotFoundException.EXCEPTION); - ; + Scrap scrap = scrapAdaptor.findByUserAndArchivingId(user, archivingId); scrapAdaptor.delete(scrap); } diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/service/UserDomainService.java b/Domain/src/main/java/allchive/server/domain/domains/user/service/UserDomainService.java index 997f9236..5c49df2b 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/service/UserDomainService.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/service/UserDomainService.java @@ -36,20 +36,20 @@ public User registerUser( @Transactional public User loginUser(OauthInfo oauthInfo) { - User user = userAdaptor.queryUserByOauthInfo(oauthInfo); + User user = userAdaptor.findByOauthInfo(oauthInfo); user.login(); return user; } @Transactional public void deleteUserById(Long userId) { - User user = userAdaptor.findUserById(userId); + User user = userAdaptor.findById(userId); user.withdrawUser(); } public void updateUserInfo( Long userId, String name, String email, String nickname, String imgUrl) { - User user = userAdaptor.findUserById(userId); + User user = userAdaptor.findById(userId); user.updateInfo(name, email, nickname, imgUrl); } diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/validator/ScrapValidator.java b/Domain/src/main/java/allchive/server/domain/domains/user/validator/ScrapValidator.java index 1c7f5b4e..153ee1e7 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/validator/ScrapValidator.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/validator/ScrapValidator.java @@ -3,6 +3,7 @@ import allchive.server.core.annotation.Validator; import allchive.server.domain.domains.user.adaptor.ScrapAdaptor; +import allchive.server.domain.domains.user.adaptor.UserAdaptor; import allchive.server.domain.domains.user.domain.User; import allchive.server.domain.domains.user.exception.exceptions.AlreadyExistScrapException; import lombok.RequiredArgsConstructor; @@ -10,11 +11,18 @@ @Validator @RequiredArgsConstructor public class ScrapValidator { + private final UserAdaptor userAdaptor; private final ScrapAdaptor scrapAdaptor; - public void validateExistScrap(User user, Long archivingId) { - if (scrapAdaptor.findByUserAndArchivingId(user, archivingId).isPresent()) { + public void validateExistScrap(Long userId, Long archivingId) { + User user = userAdaptor.findById(userId); + if (isScrapExist(user, archivingId)) { throw AlreadyExistScrapException.EXCEPTION; } } + + private Boolean isScrapExist(User user, Long archivingId) { + scrapAdaptor.findByUserAndArchivingId(user, archivingId); + return Boolean.TRUE; + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/validator/UserValidator.java b/Domain/src/main/java/allchive/server/domain/domains/user/validator/UserValidator.java index 0a02dfb9..a79f04f3 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/validator/UserValidator.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/validator/UserValidator.java @@ -23,7 +23,7 @@ public Boolean checkUserCanRegister(OauthInfo oauthInfo) { } public void validateUserStatusNormal(Long userId) { - if (!userAdaptor.findUserById(userId).getUserState().equals(UserState.NORMAL)) { + if (!userAdaptor.findById(userId).getUserState().equals(UserState.NORMAL)) { throw ForbiddenUserException.EXCEPTION; } } diff --git a/Infrastructure/src/main/java/allchive/server/infrastructure/s3/ImageUrlDto.java b/Infrastructure/src/main/java/allchive/server/infrastructure/s3/ImageUrlDto.java index 37a95152..38b37418 100644 --- a/Infrastructure/src/main/java/allchive/server/infrastructure/s3/ImageUrlDto.java +++ b/Infrastructure/src/main/java/allchive/server/infrastructure/s3/ImageUrlDto.java @@ -7,15 +7,13 @@ @Getter public class ImageUrlDto { private String url; - private String key; @Builder - private ImageUrlDto(String url, String key) { + private ImageUrlDto(String url) { this.url = url; - this.key = key; } - public static ImageUrlDto of(String url, String key) { - return ImageUrlDto.builder().key(key).url(url).build(); + public static ImageUrlDto of(String url) { + return ImageUrlDto.builder().url(url).build(); } } diff --git a/Infrastructure/src/main/java/allchive/server/infrastructure/s3/S3PresignedUrlService.java b/Infrastructure/src/main/java/allchive/server/infrastructure/s3/S3PresignedUrlService.java index 076dd455..78211363 100644 --- a/Infrastructure/src/main/java/allchive/server/infrastructure/s3/S3PresignedUrlService.java +++ b/Infrastructure/src/main/java/allchive/server/infrastructure/s3/S3PresignedUrlService.java @@ -31,7 +31,7 @@ public ImageUrlDto getPreSignedUrl(Long id, PresignedType presignedType) { GeneratePresignedUrlRequest generatePresignedUrlRequest = getGeneratePreSignedUrlRequest(fileName); String url = amazonS3.generatePresignedUrl(generatePresignedUrlRequest).toString(); - return ImageUrlDto.of(url, fileName); + return ImageUrlDto.of(url); } private String generateFileName(Long id, PresignedType presignedType) { From bfa48b0468adfdefb0281d9b0426abc7c68f84cf Mon Sep 17 00:00:00 2001 From: Sanghoon Jeong <67852689+wjdtkdgns@users.noreply.github.com> Date: Sat, 22 Jul 2023 16:55:50 +0900 Subject: [PATCH 41/41] =?UTF-8?q?[feat]=20prod=20deploy=20=EC=85=8B?= =?UTF-8?q?=ED=8C=85=20(#53)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/DevCICD.yml | 6 +-- .github/workflows/ProdCICD.yml | 79 ++++++++++++++++++++++++++++++++++ config/nginx/default.prod.conf | 18 ++++++++ docker-compose.prod.yml | 29 +++++++++++++ 4 files changed, 129 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/ProdCICD.yml create mode 100644 config/nginx/default.prod.conf create mode 100644 docker-compose.prod.yml diff --git a/.github/workflows/DevCICD.yml b/.github/workflows/DevCICD.yml index 601ba8c6..67c7d771 100644 --- a/.github/workflows/DevCICD.yml +++ b/.github/workflows/DevCICD.yml @@ -1,4 +1,4 @@ -name: CI/CD +name: DEV CI/CD on: push: @@ -51,7 +51,7 @@ jobs: - name: Add GitHub IP to AWS run: | - aws ec2 authorize-security-group-ingress --group-id ${{ secrets.AWS_DEV_SG_ID }} --protocol tcp --port 22 --cidr ${{ steps.publicip.outputs.ip }}/32 + aws ec2 authorize-security-group-ingress --group-id ${{ secrets.AWS_DEV_SG_ID }} --protocol tcp --port ${{ secrets.EC2_SSH_PORT }} --cidr ${{ steps.publicip.outputs.ip }}/32 - name: Deploy uses: appleboy/ssh-action@master @@ -76,4 +76,4 @@ jobs: - name: Remove IP FROM security group run: | - aws ec2 revoke-security-group-ingress --group-id ${{ secrets.AWS_DEV_SG_ID }} --protocol tcp --port 22 --cidr ${{ steps.publicip.outputs.ip }}/32 + aws ec2 revoke-security-group-ingress --group-id ${{ secrets.AWS_DEV_SG_ID }} --protocol tcp --port ${{ secrets.EC2_SSH_PORT }} --cidr ${{ steps.publicip.outputs.ip }}/32 diff --git a/.github/workflows/ProdCICD.yml b/.github/workflows/ProdCICD.yml new file mode 100644 index 00000000..e8173130 --- /dev/null +++ b/.github/workflows/ProdCICD.yml @@ -0,0 +1,79 @@ +name: PROD CI/CD + +on: + push: + branches: prod + +env: + PROFILE_PROD: prod + +permissions: + contents: read + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: checkout + uses: actions/checkout@v2 + + - name: Set up Java + uses: actions/setup-java@v3 + with: + java-version: 17 + distribution: 'corretto' + + - name: Grant execute permission for gradlew + run: chmod +x ./gradlew + + - name: Build with Gradle + run: ./gradlew :Api:build + + - name: Docker build + run: | + docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }} + docker build -t ${{ secrets.DOCKER_USERNAME }}/${{ secrets.DOCKER_PROD_REPO }} . + docker push ${{ secrets.DOCKER_USERNAME }}/${{ secrets.DOCKER_PROD_REPO }}:latest + + - name: Get Public IP + id: publicip + run: | + response=$(curl -s canhazip.com) + echo "ip='$response'" >> "$GITHUB_OUTPUT" + + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v1 + with: + aws-access-key-id: ${{ secrets.AWS_PROD_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_PROD_SECRET_ACCESS_KEY }} + aws-region: ${{ secrets.AWS_REGION }} + + - name: Add GitHub IP to AWS + run: | + aws ec2 authorize-security-group-ingress --group-id ${{ secrets.AWS_PROD_SG_ID }} --protocol tcp --port ${{ secrets.EC2_SSH_PORT }} --cidr ${{ steps.publicip.outputs.ip }}/32 + + - name: Deploy + uses: appleboy/ssh-action@master + with: + host: ${{ secrets.EC2_PROD_HOST }} + username: ${{ secrets.EC2_PROD_USERNAME }} + key: ${{ secrets.EC2_PROD_KEY }} + port: ${{ secrets.EC2_SSH_PORT }} + timeout: 60s + script: | + cd allchive-prod + + sudo touch .env + echo "${{ secrets.ENV_PROD_VARS }}" | sudo tee .env > /dev/null + + sudo docker stop $(sudo docker ps -a -q) + sudo docker rm $(sudo docker ps -a -q) + sudo docker rmi $(sudo docker images -q) + sudo docker pull ${{ secrets.DOCKER_USERNAME }}/${{ secrets.DOCKER_PROD_REPO }}:latest + sudo docker-compose -f ~/allchive-prod/docker-compose.yml --env-file ~/allchive-prod/.env up --build -d + sudo docker system prune --all -f + + - name: Remove IP FROM security group + run: | + aws ec2 revoke-security-group-ingress --group-id ${{ secrets.AWS_PROD_SG_ID }} --protocol tcp --port ${{ secrets.EC2_SSH_PORT }} --cidr ${{ steps.publicip.outputs.ip }}/32 diff --git a/config/nginx/default.prod.conf b/config/nginx/default.prod.conf new file mode 100644 index 00000000..dc7de01b --- /dev/null +++ b/config/nginx/default.prod.conf @@ -0,0 +1,18 @@ +server { + listen 80; + server_name www.allchive.co.kr; + + autoindex_localtime on; + + if ($http_x_forwarded_proto != 'https') { + return 301 https://$host$request_uri; + } + + location /api { + proxy_pass http://server:8080; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Host $host; + proxy_set_header X-Real_IP $remote_addr; + proxy_redirect off; + } +} \ No newline at end of file diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml new file mode 100644 index 00000000..5c348044 --- /dev/null +++ b/docker-compose.prod.yml @@ -0,0 +1,29 @@ +version: '3.7' + +services: + server: + image: sanghoonjeong/allchive-prod:latest + container_name: server + hostname: server + env_file: + - .env + environment: + - TZ=Asia/Seoul + expose: + - 8080 + logging: + driver: awslogs + options: + awslogs-group: "all-chive-prod" + awslogs-region: "ap-northeast-2" + awslogs-stream: "server" + + nginx: + depends_on: + - server + restart: always + build: + dockerfile: Dockerfile + context: './nginx' + ports: + - "80:80" \ No newline at end of file