diff --git a/build.gradle b/build.gradle index e6c5bf7..fb8628c 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,4 @@ plugins { - id 'java' id 'org.springframework.boot' version '3.5.0' id 'io.spring.dependency-management' version '1.1.7' id 'com.diffplug.spotless' version '8.1.0' @@ -7,7 +6,7 @@ plugins { id 'org.jetbrains.kotlin.jvm' version '2.2.21' id 'org.jetbrains.kotlin.plugin.spring' version '2.2.21' id 'org.jetbrains.kotlin.plugin.jpa' version '2.2.21' -// id 'org.jetbrains.kotlin.kapt' version '2.2.21' + id 'org.jetbrains.kotlin.kapt' version '2.2.21' } group = 'com.ject' @@ -19,24 +18,20 @@ java { } } -configurations { - compileOnly { - extendsFrom annotationProcessor - } -} - repositories { mavenCentral() } dependencies { + // Spring Boot Starters implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-batch' implementation 'org.springframework.boot:spring-boot-starter-webflux' - implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.7.0") - compileOnly 'org.projectlombok:lombok' - annotationProcessor 'org.projectlombok:lombok' + + // Swagger (SpringDoc UI 문서화) + implementation 'org.apache.commons:commons-lang3:3.20.0' + implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.7.0' // Validation implementation 'org.springframework.boot:spring-boot-starter-validation' @@ -46,19 +41,14 @@ dependencies { // JWT implementation("io.jsonwebtoken:jjwt-api:0.12.3") - runtimeOnly("io.jsonwebtoken:jjwt-impl:0.12.3") - runtimeOnly("io.jsonwebtoken:jjwt-jackson:0.12.3") + runtimeOnly('io.jsonwebtoken:jjwt-impl:0.12.3') + runtimeOnly('io.jsonwebtoken:jjwt-jackson:0.12.3') - // Querydsl + // Querydsl (Kotlin용 – kapt 추가) 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" - - // Querydsl (Kotlin용 – kapt 추가, Java용 Querydsl과 충돌이 발생하여 주석 처리) -// kapt "com.querydsl:querydsl-apt:5.0.0:jakarta" -// kapt "jakarta.annotation:jakarta.annotation-api" -// kapt "jakarta.persistence:jakarta.persistence-api" + kapt 'com.querydsl:querydsl-apt:5.0.0:jakarta' + kapt 'jakarta.annotation:jakarta.annotation-api' + kapt 'jakarta.persistence:jakarta.persistence-api' // Redis implementation 'org.springframework.boot:spring-boot-starter-data-redis' @@ -74,22 +64,21 @@ dependencies { // AWS SDK implementation 'software.amazon.awssdk:s3:2.25.33' implementation 'software.amazon.awssdk:auth:2.25.33' // IAM 인증 관련 - implementation 'software.amazon.awssdk:sts:2.25.33' // STS (IAM Role 인증 필요시) + implementation 'software.amazon.awssdk:sts:2.25.33' // STS (IAM Role 인증 필요시) // Tika : 이미지 타입 검사 implementation 'org.apache.tika:tika-core:2.5.0' // Kotlin - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" - implementation "org.jetbrains.kotlin:kotlin-reflect" + implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8' + implementation 'org.jetbrains.kotlin:kotlin-reflect' implementation 'io.projectreactor.kotlin:reactor-kotlin-extensions' - testImplementation "org.jetbrains.kotlin:kotlin-test" + testImplementation 'org.jetbrains.kotlin:kotlin-test' // Jackson - implementation "com.fasterxml.jackson.module:jackson-module-kotlin" + implementation 'com.fasterxml.jackson.module:jackson-module-kotlin' implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310' - // Test testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.springframework.batch:spring-batch-test' @@ -97,7 +86,7 @@ dependencies { testRuntimeOnly 'org.junit.platform:junit-platform-launcher' testImplementation 'org.mockito:mockito-core' testImplementation 'org.mockito:mockito-junit-jupiter' - testImplementation "org.mockito.kotlin:mockito-kotlin:6.1.0" + testImplementation 'org.mockito.kotlin:mockito-kotlin:6.1.0' } jar.enabled = false // 일반 JAR 파일 생성 비활성화 @@ -107,15 +96,6 @@ tasks.named('test') { } spotless { - java { - target("**/*.java") // 모든 .java 파일에 적용 - importOrder() // import 정렬 - removeUnusedImports() // 사용하지 않는 import 제거 - trimTrailingWhitespace() // 공백 제거 - endWithNewline() // 파일 끝부분 개행 처리 - googleJavaFormat().aosp() // google java format 스타일 적용 - } - kotlin { target("**/*.kt") // 모든 .kt 파일에 적용 ktlint("1.8.0") // Kotlin 코드 스타일 검사 및 자동 포맷팅 @@ -126,8 +106,25 @@ spotless { // pre-commit spotless check script // 작성한 pre-commit 스크립트를 ./git/hooks/pre-commit 으로 덮어쓰기 -tasks.register('updateGitHooks', Copy) { - from 'scripts/pre-commit' - into '.git/hooks' +tasks.register("updateGitHooks", Copy) { + from "scripts/pre-commit" + into ".git/hooks" +} + +// compileKotlin 작업 시 updateGitHooks task 수행 +compileKotlin.dependsOn updateGitHooks + +// Kotlin 소스셋에 QClass 경로 추가 +sourceSets { + main { + kotlin { + srcDirs += "$buildDir/generated/source/kapt/main" + } + } +} + +kapt { + arguments { + arg("querydsl.useFields", "false") + } } -compileJava.dependsOn updateGitHooks // 컴파일 자바 작업 시 updateGitHooks task 수행 \ No newline at end of file diff --git a/src/main/java/com/ject/studytrip/StudytripApplication.java b/src/main/java/com/ject/studytrip/StudytripApplication.java deleted file mode 100644 index c58013a..0000000 --- a/src/main/java/com/ject/studytrip/StudytripApplication.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.ject.studytrip; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -@SpringBootApplication -public class StudytripApplication { - - public static void main(String[] args) { - SpringApplication.run(StudytripApplication.class, args); - } -} diff --git a/src/main/java/com/ject/studytrip/global/annotation/ClientOrigin.java b/src/main/java/com/ject/studytrip/global/annotation/ClientOrigin.java deleted file mode 100644 index db02dee..0000000 --- a/src/main/java/com/ject/studytrip/global/annotation/ClientOrigin.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.ject.studytrip.global.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Target(ElementType.PARAMETER) -@Retention(RetentionPolicy.RUNTIME) -public @interface ClientOrigin {} diff --git a/src/main/java/com/ject/studytrip/global/batch/scheduler/BatchJobScheduler.java b/src/main/java/com/ject/studytrip/global/batch/scheduler/BatchJobScheduler.java deleted file mode 100644 index 3c04cfc..0000000 --- a/src/main/java/com/ject/studytrip/global/batch/scheduler/BatchJobScheduler.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.ject.studytrip.global.batch.scheduler; - -import lombok.RequiredArgsConstructor; -import org.springframework.batch.core.Job; -import org.springframework.batch.core.JobParameters; -import org.springframework.batch.core.JobParametersBuilder; -import org.springframework.batch.core.launch.JobLauncher; -import org.springframework.context.annotation.Profile; -import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.stereotype.Component; - -@Profile("!test") -@Component -@RequiredArgsConstructor -public class BatchJobScheduler { - private final JobLauncher jobLauncher; - private final Job hardDeleteJob; - - // 매일 04:00 (Asia/Seoul) - @Scheduled(cron = "0 0 4 * * *", zone = "Asia/Seoul") - public void runHardDeleteJob() throws Exception { - JobParameters jobParameters = - new JobParametersBuilder() - .addLong("ts", System.currentTimeMillis()) - .toJobParameters(); - - jobLauncher.run(hardDeleteJob, jobParameters); - } -} diff --git a/src/main/java/com/ject/studytrip/global/batch/tasklet/HardDeleteTasklet.java b/src/main/java/com/ject/studytrip/global/batch/tasklet/HardDeleteTasklet.java deleted file mode 100644 index 0fd0db3..0000000 --- a/src/main/java/com/ject/studytrip/global/batch/tasklet/HardDeleteTasklet.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.ject.studytrip.global.batch.tasklet; - -import com.ject.studytrip.cleanup.application.facade.HardDeleteFacade; -import lombok.RequiredArgsConstructor; -import org.springframework.batch.core.StepContribution; -import org.springframework.batch.core.scope.context.ChunkContext; -import org.springframework.batch.core.step.tasklet.Tasklet; -import org.springframework.batch.repeat.RepeatStatus; -import org.springframework.stereotype.Component; - -@Component -@RequiredArgsConstructor -public class HardDeleteTasklet implements Tasklet { - private final HardDeleteFacade hardDeleteFacade; - - @Override - public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) - throws Exception { - hardDeleteFacade.hardDeleteAll(); - - return RepeatStatus.FINISHED; - } -} diff --git a/src/main/java/com/ject/studytrip/global/common/constants/CacheKeyConstants.java b/src/main/java/com/ject/studytrip/global/common/constants/CacheKeyConstants.java deleted file mode 100644 index e1f8bd9..0000000 --- a/src/main/java/com/ject/studytrip/global/common/constants/CacheKeyConstants.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.ject.studytrip.global.common.constants; - -public final class CacheKeyConstants { - - private CacheKeyConstants() {} - - public static final String AUTH_REISSUE_TOKEN_PREFIX = "auth::reissue::token:"; - public static final String AUTH_LOGOUT_TOKEN_PREFIX = "auth::logout::token:"; - public static final String OAUTH_SIGNUP_PROFILE_PREFIX = "%s::signup::profile:"; -} diff --git a/src/main/java/com/ject/studytrip/global/common/constants/CacheNameConstants.java b/src/main/java/com/ject/studytrip/global/common/constants/CacheNameConstants.java deleted file mode 100644 index e7d3bba..0000000 --- a/src/main/java/com/ject/studytrip/global/common/constants/CacheNameConstants.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.ject.studytrip.global.common.constants; - -public final class CacheNameConstants { - - private CacheNameConstants() {} - - public static final String MEMBER = "member"; - public static final String TRIP = "trip"; - public static final String TRIPS = "trips"; - public static final String STAMP = "stamp"; - public static final String STAMPS = "stamps"; - public static final String MISSIONS = "missions"; - public static final String DAILY_GOAL = "dailyGoal"; - public static final String STUDY_LOGS = "studyLogs"; - public static final String TRIP_REPORT = "tripReport"; - public static final String TRIP_REPORTS = "tripReports"; -} diff --git a/src/main/java/com/ject/studytrip/global/common/constants/CookieConstants.java b/src/main/java/com/ject/studytrip/global/common/constants/CookieConstants.java deleted file mode 100644 index cd35a1f..0000000 --- a/src/main/java/com/ject/studytrip/global/common/constants/CookieConstants.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.ject.studytrip.global.common.constants; - -public final class CookieConstants { - - private CookieConstants() {} - - // 인증 관련 - public static final String AUTH_REFRESH_TOKEN = "auth_refresh"; - - // OAuth 관련 - public static final String OAUTH_SIGNUP_KEY = "oauth_signup_key"; - public static final long OAUTH_SIGNUP_COOKIE_TTL_MILLIS = 900000; -} diff --git a/src/main/java/com/ject/studytrip/global/common/constants/UrlConstants.java b/src/main/java/com/ject/studytrip/global/common/constants/UrlConstants.java deleted file mode 100644 index 871e70e..0000000 --- a/src/main/java/com/ject/studytrip/global/common/constants/UrlConstants.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.ject.studytrip.global.common.constants; - -public final class UrlConstants { - - private UrlConstants() {} - - // CORS 허용 도메인 - public static final String[] CORS_DOMAINS = { - "http://localhost:8080", - "https://dev-api-studytrip.duckdns.org", - "http://localhost:5173", - "https://localhost:5173", - "https://ject-4-client.vercel.app" - }; - - // 정적 리소스 경로 - public static final String[] STATIC_RESOURCES = { - "/favicon.ico", - "/firebase-messaging-sw.js", - "/.well-known/**", - "/v3/api-docs/**", - "/swagger-ui/**", - "/swagger-ui.html" - }; - - // OAuth 콜백 경로 - public static final String[] CALLBACK_PATHS = {"/auth/callback/**"}; - - // Origin 추출이 필요한 경로 - public static final String[] ORIGIN_EXTRACT_PATHS = {"/api/auth/login/kakao"}; - - // 인증이 필요없는 API 경로 - public static final String[] PERMIT_ALL_API_PATHS = { - "/api/auth/**", "/api/trips/categories", "/api/members/me/restore/**" - }; -} diff --git a/src/main/java/com/ject/studytrip/global/common/entity/BaseTimeEntity.java b/src/main/java/com/ject/studytrip/global/common/entity/BaseTimeEntity.java deleted file mode 100644 index 9679211..0000000 --- a/src/main/java/com/ject/studytrip/global/common/entity/BaseTimeEntity.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.ject.studytrip.global.common.entity; - -import jakarta.persistence.Column; -import jakarta.persistence.EntityListeners; -import jakarta.persistence.MappedSuperclass; -import java.time.LocalDateTime; -import org.springframework.data.annotation.CreatedDate; -import org.springframework.data.annotation.LastModifiedDate; -import org.springframework.data.jpa.domain.support.AuditingEntityListener; - -@EntityListeners(AuditingEntityListener.class) -@MappedSuperclass -public abstract class BaseTimeEntity { - - @CreatedDate - @Column(updatable = false) - private LocalDateTime createdAt; - - @LastModifiedDate private LocalDateTime updatedAt; - - protected LocalDateTime deletedAt; - - public boolean isDeleted() { - return deletedAt != null; - } - - public LocalDateTime getCreatedAt() { - return createdAt; - } - - public LocalDateTime getUpdatedAt() { - return updatedAt; - } - - public LocalDateTime getDeletedAt() { - return deletedAt; - } -} diff --git a/src/main/java/com/ject/studytrip/global/common/factory/CacheKeyFactory.java b/src/main/java/com/ject/studytrip/global/common/factory/CacheKeyFactory.java deleted file mode 100644 index 9051ea6..0000000 --- a/src/main/java/com/ject/studytrip/global/common/factory/CacheKeyFactory.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.ject.studytrip.global.common.factory; - -import lombok.AccessLevel; -import lombok.NoArgsConstructor; - -@NoArgsConstructor(access = AccessLevel.PRIVATE) -public class CacheKeyFactory { - public static String member(Long memberId) { - return "member:" + memberId; - } - - public static String trips(Long memberId, int page, int size) { - return "member:" + memberId + ":trips" + ":page:" + page + ":size:" + size; - } - - public static String trip(Long memberId, Long tripId) { - return "member:" + memberId + ":trip:" + tripId; - } - - public static String stamps(Long memberId, Long tripId) { - return "member:" + memberId + ":trip:" + tripId + ":stamps"; - } - - public static String stamp(Long memberId, Long tripId, Long stampId) { - return "member:" + memberId + ":trip:" + tripId + ":stamp:" + stampId; - } - - public static String missions(Long memberId, Long tripId, Long stampId) { - return "member:" + memberId + ":trip:" + tripId + ":stamp:" + stampId + ":missions"; - } - - public static String dailyGoal(Long memberId, Long tripId, Long dailyGoalId) { - return "member:" + memberId + ":trip:" + tripId + ":dailyGoal:" + dailyGoalId; - } - - public static String studyLogs(Long memberId, Long tripId, int page, int size, String order) { - return "member:" - + memberId - + ":trip:" - + tripId - + ":studyLogs" - + ":page:" - + page - + ":size:" - + size - + ":order:" - + order.toLowerCase(); - } - - public static String tripReports(Long memberId) { - return "member:" + memberId + ":tripReports"; - } - - public static String tripReport(Long memberId, Long tripReportId, int page, int size) { - return "member:" - + memberId - + ":tripReport:" - + tripReportId - + ":page:" - + page - + ":size:" - + size; - } -} diff --git a/src/main/java/com/ject/studytrip/global/common/response/StandardResponse.java b/src/main/java/com/ject/studytrip/global/common/response/StandardResponse.java deleted file mode 100644 index 2fe86e7..0000000 --- a/src/main/java/com/ject/studytrip/global/common/response/StandardResponse.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.ject.studytrip.global.common.response; - -import com.ject.studytrip.global.exception.response.ErrorResponse; - -public record StandardResponse(boolean success, int status, Object data) { - - public static StandardResponse success(int status, Object data) { - return new StandardResponse(true, status, data); - } - - public static StandardResponse fail(int status, ErrorResponse errorResponse) { - return new StandardResponse(false, status, errorResponse); - } -} diff --git a/src/main/java/com/ject/studytrip/global/config/BatchJobConfig.java b/src/main/java/com/ject/studytrip/global/config/BatchJobConfig.java deleted file mode 100644 index f16182a..0000000 --- a/src/main/java/com/ject/studytrip/global/config/BatchJobConfig.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.ject.studytrip.global.config; - -import lombok.RequiredArgsConstructor; -import org.springframework.batch.core.Job; -import org.springframework.batch.core.Step; -import org.springframework.batch.core.job.builder.JobBuilder; -import org.springframework.batch.core.launch.support.RunIdIncrementer; -import org.springframework.batch.core.repository.JobRepository; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@Configuration -@RequiredArgsConstructor -public class BatchJobConfig { - private final Step hardDeleteStep; - - private static final String HARD_DELETE_JOB_NAME = "hardDeleteJob"; - - @Bean - public Job hardDeleteJob(JobRepository jobRepository) { - return new JobBuilder(HARD_DELETE_JOB_NAME, jobRepository) - .incrementer(new RunIdIncrementer()) - .start(hardDeleteStep) - .build(); - } -} diff --git a/src/main/java/com/ject/studytrip/global/config/BatchStepConfig.java b/src/main/java/com/ject/studytrip/global/config/BatchStepConfig.java deleted file mode 100644 index 82f2976..0000000 --- a/src/main/java/com/ject/studytrip/global/config/BatchStepConfig.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.ject.studytrip.global.config; - -import lombok.RequiredArgsConstructor; -import org.springframework.batch.core.Step; -import org.springframework.batch.core.repository.JobRepository; -import org.springframework.batch.core.step.builder.StepBuilder; -import org.springframework.batch.core.step.tasklet.Tasklet; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.transaction.PlatformTransactionManager; - -@Configuration -@RequiredArgsConstructor -public class BatchStepConfig { - private final Tasklet hardDeleteTasklet; - - private static final String HARD_DELETE_STEP_NAME = "hardDeleteStep"; - - @Bean - public Step hardDeleteStep( - JobRepository jobRepository, PlatformTransactionManager transactionManager) { - return new StepBuilder(HARD_DELETE_STEP_NAME, jobRepository) - .tasklet(hardDeleteTasklet, transactionManager) - .build(); - } -} diff --git a/src/main/java/com/ject/studytrip/global/config/CdnConfig.java b/src/main/java/com/ject/studytrip/global/config/CdnConfig.java deleted file mode 100644 index d4a02fe..0000000 --- a/src/main/java/com/ject/studytrip/global/config/CdnConfig.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.ject.studytrip.global.config; - -import com.ject.studytrip.global.config.properties.CdnProperties; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.context.annotation.Configuration; - -@Configuration -@EnableConfigurationProperties(CdnProperties.class) -public class CdnConfig {} diff --git a/src/main/java/com/ject/studytrip/global/config/JpaAuditingConfig.java b/src/main/java/com/ject/studytrip/global/config/JpaAuditingConfig.java deleted file mode 100644 index 5abd6de..0000000 --- a/src/main/java/com/ject/studytrip/global/config/JpaAuditingConfig.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.ject.studytrip.global.config; - -import org.springframework.context.annotation.Configuration; -import org.springframework.data.jpa.repository.config.EnableJpaAuditing; - -@Configuration -@EnableJpaAuditing -public class JpaAuditingConfig {} diff --git a/src/main/java/com/ject/studytrip/global/config/QuerydslConfig.java b/src/main/java/com/ject/studytrip/global/config/QuerydslConfig.java deleted file mode 100644 index 69d155a..0000000 --- a/src/main/java/com/ject/studytrip/global/config/QuerydslConfig.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.ject.studytrip.global.config; - -import com.querydsl.jpa.impl.JPAQueryFactory; -import jakarta.persistence.EntityManager; -import lombok.RequiredArgsConstructor; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@Configuration -@RequiredArgsConstructor -public class QuerydslConfig { - - private final EntityManager em; - - @Bean - public JPAQueryFactory queryFactory() { - return new JPAQueryFactory(em); - } -} diff --git a/src/main/java/com/ject/studytrip/global/config/RedisCacheConfig.java b/src/main/java/com/ject/studytrip/global/config/RedisCacheConfig.java deleted file mode 100644 index c856d6c..0000000 --- a/src/main/java/com/ject/studytrip/global/config/RedisCacheConfig.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.ject.studytrip.global.config; - -import static com.ject.studytrip.global.common.constants.CacheNameConstants.*; -import static org.springframework.data.redis.cache.RedisCacheWriter.nonLockingRedisCacheWriter; -import static org.springframework.data.redis.serializer.RedisSerializationContext.SerializationPair.fromSerializer; - -import java.time.Duration; -import java.util.HashMap; -import java.util.Map; -import org.springframework.cache.annotation.EnableCaching; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.data.redis.cache.CacheKeyPrefix; -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.StringRedisSerializer; - -@EnableCaching -@Configuration -public class RedisCacheConfig { - - @Bean - public RedisCacheManager redisCacheManager(RedisConnectionFactory factory) { - RedisCacheConfiguration common = - createRedisCacheConfig().entryTtl(Duration.ofMinutes(30)); // 기본 TTL - - return RedisCacheManager.builder(nonLockingRedisCacheWriter(factory)) - .cacheDefaults(common) - .withInitialCacheConfigurations(redisCacheConfigsByName(common)) - .transactionAware() - .build(); - } - - // 공통 직렬화/프리픽스/널 캐싱 금지 설정 - private RedisCacheConfiguration createRedisCacheConfig() { - return RedisCacheConfiguration.defaultCacheConfig() - .serializeKeysWith(fromSerializer(new StringRedisSerializer())) - .serializeValuesWith(fromSerializer(new GenericJackson2JsonRedisSerializer())) - .disableCachingNullValues() - .computePrefixWith(CacheKeyPrefix.simple()); - } - - // 캐시 이름별 TTL 정책 적용 - private Map redisCacheConfigsByName( - RedisCacheConfiguration common) { - Map configs = new HashMap<>(); - - configs.put(MEMBER, common.entryTtl(Duration.ofMinutes(10))); // 멤버 상세 조회 - configs.put(DAILY_GOAL, common.entryTtl(Duration.ofMinutes(10))); // 데일리 목표 상세 조회 - configs.put(STUDY_LOGS, common.entryTtl(Duration.ofMinutes(10))); // 학습 로그 목록 조회 - configs.put(TRIP_REPORTS, common.entryTtl(Duration.ofMinutes(10))); // 여행 리포트 목록 조회 - - configs.put(TRIP, common.entryTtl(Duration.ofMinutes(5))); // 여행 상세 조회 - configs.put(STAMP, common.entryTtl(Duration.ofMinutes(5))); // 스탬프 상세 조회 - configs.put(TRIP_REPORT, common.entryTtl(Duration.ofMinutes(5))); // 여행 리포트 상세 조회 - - configs.put(TRIPS, common.entryTtl(Duration.ofMinutes(3))); // 여행 목록 조회(페이지) - configs.put(STAMPS, common.entryTtl(Duration.ofMinutes(3))); // 스탬프 목록 조회 - - return configs; - } -} diff --git a/src/main/java/com/ject/studytrip/global/config/RedisConfig.java b/src/main/java/com/ject/studytrip/global/config/RedisConfig.java deleted file mode 100644 index 55c889b..0000000 --- a/src/main/java/com/ject/studytrip/global/config/RedisConfig.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.ject.studytrip.global.config; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.ject.studytrip.global.config.properties.RedisProperties; -import lombok.RequiredArgsConstructor; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -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.lettuce.LettuceConnectionFactory; -import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; -import org.springframework.data.redis.serializer.StringRedisSerializer; - -@EnableConfigurationProperties(RedisProperties.class) -@Configuration -@RequiredArgsConstructor -public class RedisConfig { - - private final RedisProperties redisProperties; - - @Bean - public RedisConnectionFactory redisConnectionFactory() { - return new LettuceConnectionFactory(redisProperties.host(), redisProperties.port()); - } - - @Bean - public RedisTemplate redisTemplate( - RedisConnectionFactory factory, ObjectMapper objectMapper) { - RedisTemplate template = new RedisTemplate<>(); - template.setConnectionFactory(factory); - - // 단순 Key-Value 직렬화 - template.setKeySerializer(new StringRedisSerializer()); - template.setValueSerializer(new GenericJackson2JsonRedisSerializer(objectMapper)); - - // 해시 Key-Value 직렬화 - template.setHashKeySerializer(new StringRedisSerializer()); - template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer(objectMapper)); - - template.afterPropertiesSet(); - return template; - } -} diff --git a/src/main/java/com/ject/studytrip/global/config/S3Config.java b/src/main/java/com/ject/studytrip/global/config/S3Config.java deleted file mode 100644 index becf266..0000000 --- a/src/main/java/com/ject/studytrip/global/config/S3Config.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.ject.studytrip.global.config; - -import com.ject.studytrip.global.config.properties.S3Properties; -import java.time.Duration; -import lombok.RequiredArgsConstructor; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider; -import software.amazon.awssdk.core.retry.RetryMode; -import software.amazon.awssdk.core.retry.RetryPolicy; -import software.amazon.awssdk.regions.Region; -import software.amazon.awssdk.services.s3.S3Client; -import software.amazon.awssdk.services.s3.presigner.S3Presigner; - -@Configuration -@EnableConfigurationProperties(S3Properties.class) -@RequiredArgsConstructor -public class S3Config { - private final S3Properties props; - - @Bean - public S3Client s3Client() { - RetryPolicy retry = - RetryPolicy.builder(RetryMode.STANDARD) - .numRetries(props.retry().maxAttempts()) - .build(); - - return S3Client.builder() - .overrideConfiguration( - config -> - config.retryPolicy(retry) - .apiCallTimeout( - Duration.ofSeconds( - props.timeout().apiCallInSeconds())) - .apiCallAttemptTimeout( - Duration.ofSeconds( - props.timeout().apiCallAttemptInSeconds()))) - .region(Region.of(props.region())) - .credentialsProvider(DefaultCredentialsProvider.create()) - .build(); - } - - @Bean - public S3Presigner s3Presigner() { - return S3Presigner.builder() - .region(Region.of(props.region())) - .credentialsProvider(DefaultCredentialsProvider.create()) - .build(); - } -} diff --git a/src/main/java/com/ject/studytrip/global/config/SchedulerConfig.java b/src/main/java/com/ject/studytrip/global/config/SchedulerConfig.java deleted file mode 100644 index 6149aed..0000000 --- a/src/main/java/com/ject/studytrip/global/config/SchedulerConfig.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.ject.studytrip.global.config; - -import org.springframework.context.annotation.Configuration; -import org.springframework.scheduling.annotation.EnableScheduling; - -@EnableScheduling -@Configuration -public class SchedulerConfig {} diff --git a/src/main/java/com/ject/studytrip/global/config/SwaggerConfig.java b/src/main/java/com/ject/studytrip/global/config/SwaggerConfig.java deleted file mode 100644 index 816024b..0000000 --- a/src/main/java/com/ject/studytrip/global/config/SwaggerConfig.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.ject.studytrip.global.config; - -import com.ject.studytrip.global.config.properties.SwaggerProperties; -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.SecurityRequirement; -import io.swagger.v3.oas.models.security.SecurityScheme; -import io.swagger.v3.oas.models.security.SecurityScheme.*; -import io.swagger.v3.oas.models.servers.Server; -import java.util.List; -import lombok.RequiredArgsConstructor; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Profile; - -@Profile("!prod") // 로컬, 개발환경 활성화 -@EnableConfigurationProperties(SwaggerProperties.class) -@Configuration -@RequiredArgsConstructor -public class SwaggerConfig { - - private static final String SERVER_NAME = "StudyTrip"; - private static final String SERVER_DESCRIPTION = "StudyTrip 서버 URL 입니다."; - private static final String API_TITLE = "StudyTrip 서버 API 문서"; - private static final String API_DESCRIPTION = "StudyTrip 서버 API 문서입니다."; - private static final String GITHUB_URL = "https://github.com/JECT-Study/JECT-4-server"; - - private final SwaggerProperties swaggerProperties; - - @Bean - public OpenAPI openAPI() { - return new OpenAPI() - .servers(swaggerServer()) - .addSecurityItem(securityRequirement()) - .components(authComponents()) - .info(swaggerInfo()); - } - - private List swaggerServer() { - Server server = - new Server().url(swaggerProperties.serverUrl()).description(SERVER_DESCRIPTION); - return List.of(server); - } - - private Components authComponents() { - return new Components() - .addSecuritySchemes( - "accessToken", - new SecurityScheme() - .type(Type.HTTP) - .scheme("bearer") - .bearerFormat("JWT") - .in(In.HEADER) - .name("Authorization")); - } - - private SecurityRequirement securityRequirement() { - SecurityRequirement securityRequirement = new SecurityRequirement(); - securityRequirement.addList("accessToken"); - return securityRequirement; - } - - private Info swaggerInfo() { - License license = new License(); - license.setUrl(GITHUB_URL); - license.setName(SERVER_NAME); - - return new Info() - .version("v" + swaggerProperties.version()) - .title(API_TITLE) - .description(API_DESCRIPTION) - .license(license); - } -} diff --git a/src/main/java/com/ject/studytrip/global/config/TikaConfig.java b/src/main/java/com/ject/studytrip/global/config/TikaConfig.java deleted file mode 100644 index fb946b2..0000000 --- a/src/main/java/com/ject/studytrip/global/config/TikaConfig.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.ject.studytrip.global.config; - -import org.apache.tika.Tika; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@Configuration -public class TikaConfig { - - @Bean - public Tika tika() { - return new Tika(); - } -} diff --git a/src/main/java/com/ject/studytrip/global/config/WebClientConfig.java b/src/main/java/com/ject/studytrip/global/config/WebClientConfig.java deleted file mode 100644 index 7412466..0000000 --- a/src/main/java/com/ject/studytrip/global/config/WebClientConfig.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.ject.studytrip.global.config; - -import com.ject.studytrip.global.config.properties.KakaoOauthProperties; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.web.reactive.function.client.WebClient; - -@Configuration -@EnableConfigurationProperties(KakaoOauthProperties.class) -public class WebClientConfig { - - @Bean - public WebClient webClient() { - return WebClient.builder() - .defaultHeader( - HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE) - .build(); - } -} diff --git a/src/main/java/com/ject/studytrip/global/config/WebSecurityConfig.java b/src/main/java/com/ject/studytrip/global/config/WebSecurityConfig.java deleted file mode 100644 index 65ca7b0..0000000 --- a/src/main/java/com/ject/studytrip/global/config/WebSecurityConfig.java +++ /dev/null @@ -1,144 +0,0 @@ -package com.ject.studytrip.global.config; - -import static com.ject.studytrip.global.common.constants.UrlConstants.*; - -import com.ject.studytrip.auth.application.service.TokenService; -import com.ject.studytrip.global.config.properties.TokenProperties; -import com.ject.studytrip.global.security.*; -import java.util.Arrays; -import java.util.List; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.core.annotation.Order; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; -import org.springframework.security.config.http.SessionCreationPolicy; -import org.springframework.security.web.SecurityFilterChain; -import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; -import org.springframework.web.cors.CorsConfiguration; -import org.springframework.web.cors.UrlBasedCorsConfigurationSource; -import org.springframework.web.filter.CorsFilter; - -@Slf4j -@EnableWebSecurity -@Configuration -@RequiredArgsConstructor -@EnableConfigurationProperties(TokenProperties.class) -public class WebSecurityConfig { - private final CustomAuthenticationEntryPoint authenticationEntryPoint; - private final CustomAccessDeniedHandler accessDeniedHandler; - - private void defaultFilterChain(HttpSecurity http) throws Exception { - http - // csrf 비활성화 - .csrf(AbstractHttpConfigurer::disable) - // http basic 비활성화 - .httpBasic(AbstractHttpConfigurer::disable) - // 폼 로그인 비활성화 - .formLogin(AbstractHttpConfigurer::disable) - // 세션 비활성화 - .sessionManagement( - session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) - // cors 설정 - .cors(cors -> cors.configurationSource(corsConfigurationSource())); - } - - // 퍼블릭 리소스 URL 필터 체인 - @Bean - @Order(0) - public SecurityFilterChain publicFilterChain(HttpSecurity http) throws Exception { - defaultFilterChain(http); - - http.securityMatcher(STATIC_RESOURCES); - http.authorizeHttpRequests(authorize -> authorize.anyRequest().permitAll()); - - return http.build(); - } - - // 콜백 URL 필터 체인 - @Bean - @Order(1) - public SecurityFilterChain callbackFilterChain(HttpSecurity http) throws Exception { - defaultFilterChain(http); - - http.securityMatcher(CALLBACK_PATHS); - http.authorizeHttpRequests(authorize -> authorize.anyRequest().permitAll()); - - return http.build(); - } - - // Origin 추출이 필요한 URL 필터체인 - @Bean - @Order(2) - public SecurityFilterChain originExtractionFilterChain( - HttpSecurity http, SecurityResponseHandler securityResponseHandler) throws Exception { - defaultFilterChain(http); - - http.securityMatcher(ORIGIN_EXTRACT_PATHS); - - // Origin 추출 필터 등록 : CORS 이후 OriginExtractionFilter 실행 - http.addFilterAfter(new OriginExtractionFilter(securityResponseHandler), CorsFilter.class); - - http.authorizeHttpRequests(authorize -> authorize.anyRequest().permitAll()); - - return http.build(); - } - - // 메인 필터체인 - @Bean - @Order(3) - public SecurityFilterChain mainFilterChain(HttpSecurity http, TokenService tokenService) - throws Exception { - defaultFilterChain(http); - - // JWT 필터 등록 : 인증 이전에 동작해야 하므로 UsernamePasswordAuthenticationFilter 앞에 삽입 - http.addFilterBefore( - new JwtAuthenticationFilter(tokenService), - UsernamePasswordAuthenticationFilter.class); - - // 경로 인가 설정 - http.authorizeHttpRequests( - authorize -> - authorize - .requestMatchers(PERMIT_ALL_API_PATHS) - .permitAll() - .anyRequest() - .authenticated()); // 그 외 요청은 모두 인증 수행 - - // 예외 핸들링 - http.exceptionHandling( - exception -> - exception - .authenticationEntryPoint(authenticationEntryPoint) - .accessDeniedHandler(accessDeniedHandler)); - - return http.build(); - } - - // CORS 설정 - @Bean - public UrlBasedCorsConfigurationSource corsConfigurationSource() { - CorsConfiguration config = new CorsConfiguration(); - - config.setAllowCredentials(true); - config.addAllowedHeader("*"); - config.addAllowedMethod("*"); - - // TODO: 환경별 CORS 허용 origin 설정 분기 처리 - // - dev: LOCAL_DOMAIN, LOCAL_SECURE_DOMAIN, DEV_DOMAIN 허용 - // - prod: PROD_DOMAIN 만 허용 - // - Spring Active Profile 기반 분기 필요 - // - 서비스 도메인, 서버 운영 환경 설정 완료 시 작업 - List allowedOrigins = Arrays.asList(CORS_DOMAINS); - config.setAllowedOrigins(allowedOrigins); - - UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); - source.registerCorsConfiguration("/**", config); - - return source; - } -} diff --git a/src/main/java/com/ject/studytrip/global/config/properties/CdnProperties.java b/src/main/java/com/ject/studytrip/global/config/properties/CdnProperties.java deleted file mode 100644 index b88de7a..0000000 --- a/src/main/java/com/ject/studytrip/global/config/properties/CdnProperties.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.ject.studytrip.global.config.properties; - -import org.springframework.boot.context.properties.ConfigurationProperties; - -@ConfigurationProperties(prefix = "cdn") -public record CdnProperties(String domain) {} diff --git a/src/main/java/com/ject/studytrip/global/config/properties/KakaoOauthProperties.java b/src/main/java/com/ject/studytrip/global/config/properties/KakaoOauthProperties.java deleted file mode 100644 index 56e902d..0000000 --- a/src/main/java/com/ject/studytrip/global/config/properties/KakaoOauthProperties.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.ject.studytrip.global.config.properties; - -import org.springframework.boot.context.properties.ConfigurationProperties; - -@ConfigurationProperties(prefix = "oauth.kakao") -public record KakaoOauthProperties( - String clientId, - String clientSecret, - String redirectUri, - String tokenUri, - String userInfoUri) {} diff --git a/src/main/java/com/ject/studytrip/global/config/properties/RedisProperties.java b/src/main/java/com/ject/studytrip/global/config/properties/RedisProperties.java deleted file mode 100644 index 3ca44f0..0000000 --- a/src/main/java/com/ject/studytrip/global/config/properties/RedisProperties.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.ject.studytrip.global.config.properties; - -import org.springframework.boot.context.properties.ConfigurationProperties; - -@ConfigurationProperties(prefix = "spring.data.redis") -public record RedisProperties(int port, String host) {} diff --git a/src/main/java/com/ject/studytrip/global/config/properties/S3Properties.java b/src/main/java/com/ject/studytrip/global/config/properties/S3Properties.java deleted file mode 100644 index 8b09f79..0000000 --- a/src/main/java/com/ject/studytrip/global/config/properties/S3Properties.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.ject.studytrip.global.config.properties; - -import org.springframework.boot.context.properties.ConfigurationProperties; - -@ConfigurationProperties(prefix = "aws.s3") -public record S3Properties( - String bucket, - String region, - long presignExpiresInMinutes, - S3Retry retry, - S3Timeout timeout) { - public record S3Retry(int maxAttempts) {} - - public record S3Timeout(int apiCallInSeconds, int apiCallAttemptInSeconds) {} -} diff --git a/src/main/java/com/ject/studytrip/global/config/properties/SwaggerProperties.java b/src/main/java/com/ject/studytrip/global/config/properties/SwaggerProperties.java deleted file mode 100644 index abffabe..0000000 --- a/src/main/java/com/ject/studytrip/global/config/properties/SwaggerProperties.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.ject.studytrip.global.config.properties; - -import org.springframework.boot.context.properties.ConfigurationProperties; - -@ConfigurationProperties(prefix = "swagger") -public record SwaggerProperties(String version, String serverUrl) {} diff --git a/src/main/java/com/ject/studytrip/global/config/properties/TokenProperties.java b/src/main/java/com/ject/studytrip/global/config/properties/TokenProperties.java deleted file mode 100644 index f5dcee3..0000000 --- a/src/main/java/com/ject/studytrip/global/config/properties/TokenProperties.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.ject.studytrip.global.config.properties; - -import org.springframework.boot.context.properties.ConfigurationProperties; - -@ConfigurationProperties(prefix = "jwt") -public record TokenProperties( - String secret, long accessExpirationTime, long refreshExpirationTime) {} diff --git a/src/main/java/com/ject/studytrip/global/exception/CustomException.java b/src/main/java/com/ject/studytrip/global/exception/CustomException.java deleted file mode 100644 index 68a830a..0000000 --- a/src/main/java/com/ject/studytrip/global/exception/CustomException.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.ject.studytrip.global.exception; - -import com.ject.studytrip.global.exception.error.ErrorCode; -import lombok.Getter; - -@Getter -public class CustomException extends RuntimeException { - - private final ErrorCode errorCode; - - public CustomException(ErrorCode errorCode) { - super(errorCode.getMessage()); - this.errorCode = errorCode; - } -} diff --git a/src/main/java/com/ject/studytrip/global/exception/error/CommonErrorCode.java b/src/main/java/com/ject/studytrip/global/exception/error/CommonErrorCode.java deleted file mode 100644 index 61f0532..0000000 --- a/src/main/java/com/ject/studytrip/global/exception/error/CommonErrorCode.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.ject.studytrip.global.exception.error; - -import lombok.*; -import org.springframework.http.HttpStatus; - -@RequiredArgsConstructor -public enum CommonErrorCode implements ErrorCode { - METHOD_NOT_ALLOWED(HttpStatus.METHOD_NOT_ALLOWED, "지원하지 않는 HTTP 메서드 입니다."), - METHOD_ARGUMENT_NOT_VALID(HttpStatus.BAD_REQUEST, "요청 본문(JSON)의 값 유효성 검증에 실패했습니다."), - INVALID_JSON_FORMAT( - HttpStatus.BAD_REQUEST, "요청 본문(JSON) 형식이 잘못되어 파싱할 수 없습니다. (필드 타입 불일치, 필수 필드 누락 등)"), - CONSTRAINT_VIOLATION(HttpStatus.BAD_REQUEST, "요청 파라미터 또는 경로 변수 유효성 검증에 실패했습니다."), - METHOD_ARGUMENT_TYPE_MISMATCH(HttpStatus.BAD_REQUEST, "요청한 메서드 파마미터의 타입이 일치하지 않습니다."), - INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "서버 에러. 관리자에게 문의하세요."), - - INVALID_ORIGIN(HttpStatus.BAD_REQUEST, "잘못된 Origin입니다."), - UNSUPPORTED_ORIGIN(HttpStatus.BAD_REQUEST, "지원하지 않는 Origin입니다."), - ; - - private final HttpStatus status; - private final String message; - - @Override - public String getName() { - return this.name(); - } - - @Override - public HttpStatus getStatus() { - return this.status; - } - - @Override - public String getMessage() { - return this.message; - } -} diff --git a/src/main/java/com/ject/studytrip/global/exception/error/ErrorCode.java b/src/main/java/com/ject/studytrip/global/exception/error/ErrorCode.java deleted file mode 100644 index 3aaf0cc..0000000 --- a/src/main/java/com/ject/studytrip/global/exception/error/ErrorCode.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.ject.studytrip.global.exception.error; - -import org.springframework.http.HttpStatus; - -public interface ErrorCode { - - String getName(); - - HttpStatus getStatus(); - - String getMessage(); -} diff --git a/src/main/java/com/ject/studytrip/global/exception/handler/GlobalExceptionHandler.java b/src/main/java/com/ject/studytrip/global/exception/handler/GlobalExceptionHandler.java deleted file mode 100644 index d115330..0000000 --- a/src/main/java/com/ject/studytrip/global/exception/handler/GlobalExceptionHandler.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.ject.studytrip.global.exception.handler; - -import com.ject.studytrip.global.common.response.StandardResponse; -import com.ject.studytrip.global.exception.CustomException; -import com.ject.studytrip.global.exception.error.CommonErrorCode; -import com.ject.studytrip.global.exception.error.ErrorCode; -import com.ject.studytrip.global.exception.response.ErrorResponse; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.ExceptionHandler; -import org.springframework.web.bind.annotation.RestControllerAdvice; - -@Slf4j -@RestControllerAdvice -@RequiredArgsConstructor -public class GlobalExceptionHandler { - - /** CustomException 예외 처리 */ - @ExceptionHandler(CustomException.class) - public ResponseEntity handleCustomException(CustomException e) { - log.error("CustomException : {}", e.getMessage(), e); - - final ErrorCode errorCode = e.getErrorCode(); - final ErrorResponse errorResponse = - ErrorResponse.of(errorCode.getName(), errorCode.getMessage()); - final StandardResponse response = - StandardResponse.fail(errorCode.getStatus().value(), errorResponse); - - return ResponseEntity.status(errorCode.getStatus()).body(response); - } - - /** 500번대 에러 처리 */ - @ExceptionHandler(Exception.class) - protected ResponseEntity handleException(Exception e) { - log.error("Internal Server Error : {}", e.getMessage(), e); - - final ErrorCode errorCode = CommonErrorCode.INTERNAL_SERVER_ERROR; - final ErrorResponse errorResponse = - ErrorResponse.of(errorCode.getName(), errorCode.getMessage()); - final StandardResponse response = - StandardResponse.fail(errorCode.getStatus().value(), errorResponse); - - return ResponseEntity.status(errorCode.getStatus()).body(response); - } -} diff --git a/src/main/java/com/ject/studytrip/global/exception/handler/ValidationExceptionHandler.java b/src/main/java/com/ject/studytrip/global/exception/handler/ValidationExceptionHandler.java deleted file mode 100644 index 0c61174..0000000 --- a/src/main/java/com/ject/studytrip/global/exception/handler/ValidationExceptionHandler.java +++ /dev/null @@ -1,160 +0,0 @@ -package com.ject.studytrip.global.exception.handler; - -import com.ject.studytrip.global.common.response.StandardResponse; -import com.ject.studytrip.global.exception.error.CommonErrorCode; -import com.ject.studytrip.global.exception.error.ErrorCode; -import com.ject.studytrip.global.exception.response.ErrorResponse; -import com.ject.studytrip.global.exception.response.FieldErrorResponse; -import jakarta.validation.ConstraintViolationException; -import java.util.List; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.core.Ordered; -import org.springframework.core.annotation.Order; -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.web.HttpRequestMethodNotSupportedException; -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.method.annotation.MethodArgumentTypeMismatchException; -import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; - -@Slf4j -@RestControllerAdvice -@RequiredArgsConstructor -@Order(Ordered.HIGHEST_PRECEDENCE) -public class ValidationExceptionHandler extends ResponseEntityExceptionHandler { - - /** ResponseEntityExceptionHandler 가 기본으로 처리하는 예외를 공통으로 처리 */ - @Override - protected ResponseEntity handleExceptionInternal( - Exception e, - Object body, - HttpHeaders headers, - HttpStatusCode statusCode, - WebRequest webRequest) { - ErrorResponse errorResponse = - ErrorResponse.of(e.getClass().getSimpleName(), e.getMessage()); - - return super.handleExceptionInternal(e, errorResponse, headers, statusCode, webRequest); - } - - /** 요청 본문(Json) 에서 유효성 제약 조건 위반 시 발생(바인딩 실패), 주로 RequestBody, RequestPart 에서 발생 */ - @Override - protected ResponseEntity handleMethodArgumentNotValid( - MethodArgumentNotValidException e, - HttpHeaders headers, - HttpStatusCode statusCode, - WebRequest webRequest) { - log.error("MethodArgumentNotValidException : {}", e.getMessage(), e); - - List fieldErrors = - e.getBindingResult().getFieldErrors().stream() - .map( - fieldError -> - FieldErrorResponse.of( - fieldError.getField(), - fieldError.getDefaultMessage())) - .toList(); - - final ErrorCode errorCode = CommonErrorCode.METHOD_ARGUMENT_NOT_VALID; - final ErrorResponse errorResponse = - ErrorResponse.of(errorCode.getName(), errorCode.getMessage(), fieldErrors); - final StandardResponse response = StandardResponse.fail(statusCode.value(), errorResponse); - - return ResponseEntity.status(statusCode).body(response); - } - - /** 요청 본문(JSON) 형식이 잘못되어 파싱할 수 없는 경우 발생 (필드 타입 불일치, 필수 필드 누락 시 발생) */ - @Override - protected ResponseEntity handleHttpMessageNotReadable( - HttpMessageNotReadableException e, - HttpHeaders headers, - HttpStatusCode statusCode, - WebRequest request) { - log.error("HttpMessageNotReadableException : {}", e.getMessage(), e); - - final ErrorCode errorCode = CommonErrorCode.INVALID_JSON_FORMAT; - final ErrorResponse errorResponse = - ErrorResponse.of(e.getClass().getSimpleName(), errorCode.getMessage()); - final StandardResponse response = - StandardResponse.fail(errorCode.getStatus().value(), errorResponse); - - return ResponseEntity.status(errorCode.getStatus()).body(response); - } - - /** RequestParam, PathVariable 등 메서드 파라미터에서 제약 조건을 위반해 바인딩 실패 시 발생 */ - @ExceptionHandler(ConstraintViolationException.class) - public ResponseEntity handleConstrainViolationException( - ConstraintViolationException e) { - log.error("ConstrainViolationException : {}", e.getMessage(), e); - - List bindingErrors = - e.getConstraintViolations().stream() - .map( - constraintViolation -> { - List propertyPath = - List.of( - constraintViolation - .getPropertyPath() - .toString() - .split("\\.")); - - String path = - propertyPath.stream() - .skip(propertyPath.size() - 1L) - .findFirst() - .orElse(null); - - return FieldErrorResponse.of( - path, constraintViolation.getMessage()); - }) - .toList(); - - final ErrorCode errorCode = CommonErrorCode.CONSTRAINT_VIOLATION; - final ErrorResponse errorResponse = - ErrorResponse.of(errorCode.getName(), errorCode.getMessage(), bindingErrors); - final StandardResponse response = - StandardResponse.fail(HttpStatus.BAD_REQUEST.value(), errorResponse); - - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response); - } - - /** PathVariable, RequestParam, RequestHeader 에서 요청한 메서드 파라미터 타입이 일치하지 않은 경우 발생 */ - @ExceptionHandler(MethodArgumentTypeMismatchException.class) - protected ResponseEntity handleMethodArgumentTypeMismatchException( - MethodArgumentTypeMismatchException e) { - log.error("MethodArgumentTypeMismatchException : {}", e.getMessage(), e); - - final ErrorCode errorCode = CommonErrorCode.METHOD_ARGUMENT_TYPE_MISMATCH; - final ErrorResponse errorResponse = - ErrorResponse.of(e.getClass().getSimpleName(), errorCode.getMessage()); - final StandardResponse response = - StandardResponse.fail(errorCode.getStatus().value(), errorResponse); - - return ResponseEntity.status(errorCode.getStatus()).body(response); - } - - /** 지원하지 않는 HTTP method 요청 시 발생 */ - @Override - protected ResponseEntity handleHttpRequestMethodNotSupported( - HttpRequestMethodNotSupportedException e, - HttpHeaders headers, - HttpStatusCode statusCode, - WebRequest webRequest) { - log.error("HttpRequestMethodNotSupportedException : {}", e.getMethod(), e); - - final ErrorCode errorCode = CommonErrorCode.METHOD_NOT_ALLOWED; - final ErrorResponse errorResponse = - ErrorResponse.of(e.getClass().getSimpleName(), errorCode.getMessage()); - final StandardResponse response = - StandardResponse.fail(errorCode.getStatus().value(), errorResponse); - - return ResponseEntity.status(errorCode.getStatus()).body(response); - } -} diff --git a/src/main/java/com/ject/studytrip/global/exception/response/ErrorResponse.java b/src/main/java/com/ject/studytrip/global/exception/response/ErrorResponse.java deleted file mode 100644 index 2c5dd97..0000000 --- a/src/main/java/com/ject/studytrip/global/exception/response/ErrorResponse.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.ject.studytrip.global.exception.response; - -public record ErrorResponse(String error, String message, Object values) { - - public static ErrorResponse of(String error, String message, Object values) { - return new ErrorResponse(error, message, values); - } - - public static ErrorResponse of(String error, String message) { - return new ErrorResponse(error, message, null); - } -} diff --git a/src/main/java/com/ject/studytrip/global/exception/response/FieldErrorResponse.java b/src/main/java/com/ject/studytrip/global/exception/response/FieldErrorResponse.java deleted file mode 100644 index aded05c..0000000 --- a/src/main/java/com/ject/studytrip/global/exception/response/FieldErrorResponse.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.ject.studytrip.global.exception.response; - -public record FieldErrorResponse(String field, String reason) { - - public static FieldErrorResponse of(String field, String reason) { - return new FieldErrorResponse(field, reason); - } -} diff --git a/src/main/java/com/ject/studytrip/global/security/CustomAccessDeniedHandler.java b/src/main/java/com/ject/studytrip/global/security/CustomAccessDeniedHandler.java deleted file mode 100644 index 3b04e17..0000000 --- a/src/main/java/com/ject/studytrip/global/security/CustomAccessDeniedHandler.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.ject.studytrip.global.security; - -import com.ject.studytrip.auth.domain.error.AuthErrorCode; -import com.ject.studytrip.global.exception.error.ErrorCode; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import java.io.IOException; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.security.access.AccessDeniedException; -import org.springframework.security.web.access.AccessDeniedHandler; -import org.springframework.stereotype.Component; - -@Slf4j -@Component -@RequiredArgsConstructor -public class CustomAccessDeniedHandler implements AccessDeniedHandler { - - private final SecurityResponseHandler securityResponseHandler; - - @Override - public void handle( - HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) - throws IOException, ServletException { - log.error("AccessDeniedException : {}", e.getMessage(), e); - - final ErrorCode errorCode = AuthErrorCode.ACCESS_DENIED; - securityResponseHandler.sendResponse(response, errorCode); - } -} diff --git a/src/main/java/com/ject/studytrip/global/security/CustomAuthenticationEntryPoint.java b/src/main/java/com/ject/studytrip/global/security/CustomAuthenticationEntryPoint.java deleted file mode 100644 index 35690ec..0000000 --- a/src/main/java/com/ject/studytrip/global/security/CustomAuthenticationEntryPoint.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.ject.studytrip.global.security; - -import com.ject.studytrip.auth.domain.error.AuthErrorCode; -import com.ject.studytrip.global.exception.error.ErrorCode; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import java.io.IOException; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.web.AuthenticationEntryPoint; -import org.springframework.stereotype.Component; - -@Slf4j -@Component -@RequiredArgsConstructor -public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint { - - private final SecurityResponseHandler securityResponseHandler; - - @Override - public void commence( - HttpServletRequest request, HttpServletResponse response, AuthenticationException e) - throws IOException, ServletException { - log.error("AuthenticationException : {}", e.getMessage(), e); - - final ErrorCode errorCode = AuthErrorCode.UNAUTHENTICATED; - securityResponseHandler.sendResponse(response, errorCode); - } -} diff --git a/src/main/java/com/ject/studytrip/global/security/JwtAuthenticationFilter.java b/src/main/java/com/ject/studytrip/global/security/JwtAuthenticationFilter.java deleted file mode 100644 index 2d2a5a0..0000000 --- a/src/main/java/com/ject/studytrip/global/security/JwtAuthenticationFilter.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.ject.studytrip.global.security; - -import com.ject.studytrip.auth.application.service.TokenService; -import jakarta.servlet.FilterChain; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import java.io.IOException; -import lombok.NonNull; -import lombok.RequiredArgsConstructor; -import org.springframework.http.HttpHeaders; -import org.springframework.util.StringUtils; -import org.springframework.web.filter.OncePerRequestFilter; - -@RequiredArgsConstructor -public class JwtAuthenticationFilter extends OncePerRequestFilter { - private final TokenService tokenService; - - @Override - protected void doFilterInternal( - @NonNull HttpServletRequest request, - @NonNull HttpServletResponse response, - @NonNull FilterChain filterChain) - throws ServletException, IOException { - String authorizationHeader = request.getHeader(HttpHeaders.AUTHORIZATION); - String accessToken = extractBearerToken(authorizationHeader); - - if (StringUtils.hasText(accessToken)) { - tokenService.validateActiveAccessToken(accessToken); - tokenService.setAuthenticationByAccessToken(accessToken); - } - - filterChain.doFilter(request, response); - } - - private String extractBearerToken(String header) { - return (StringUtils.hasText(header) && header.startsWith("Bearer ")) - ? header.substring(7) - : null; - } -} diff --git a/src/main/java/com/ject/studytrip/global/security/OriginExtractionFilter.java b/src/main/java/com/ject/studytrip/global/security/OriginExtractionFilter.java deleted file mode 100644 index 76655d4..0000000 --- a/src/main/java/com/ject/studytrip/global/security/OriginExtractionFilter.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.ject.studytrip.global.security; - -import static com.ject.studytrip.global.common.constants.UrlConstants.*; - -import com.google.common.net.HttpHeaders; -import com.ject.studytrip.global.exception.error.CommonErrorCode; -import com.ject.studytrip.global.util.OriginArgumentUtil; -import jakarta.servlet.FilterChain; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.util.Arrays; -import java.util.List; -import lombok.RequiredArgsConstructor; -import org.springframework.web.filter.OncePerRequestFilter; - -@RequiredArgsConstructor -public class OriginExtractionFilter extends OncePerRequestFilter { - private static final String ATTRIBUTE_NAME = "origin"; - - private final SecurityResponseHandler securityResponseHandler; - - @Override - protected void doFilterInternal( - HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) - throws ServletException, IOException { - String origin = - OriginArgumentUtil.resolveOrigin( - request.getHeader(HttpHeaders.ORIGIN), - request.getScheme(), - request.getServerName(), - request.getServerPort()); - - // origin null 검증 - if (origin == null) { - securityResponseHandler.sendResponse(response, CommonErrorCode.INVALID_ORIGIN); - return; - } - - // 허용된 origin 검증 - List allowedOrigins = Arrays.asList(CORS_DOMAINS); - if (!allowedOrigins.contains(origin)) { - securityResponseHandler.sendResponse(response, CommonErrorCode.UNSUPPORTED_ORIGIN); - return; - } - - request.setAttribute(ATTRIBUTE_NAME, origin); - filterChain.doFilter(request, response); - } -} diff --git a/src/main/java/com/ject/studytrip/global/security/SecurityResponseHandler.java b/src/main/java/com/ject/studytrip/global/security/SecurityResponseHandler.java deleted file mode 100644 index 6cd8c55..0000000 --- a/src/main/java/com/ject/studytrip/global/security/SecurityResponseHandler.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.ject.studytrip.global.security; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.ject.studytrip.global.common.response.StandardResponse; -import com.ject.studytrip.global.exception.error.ErrorCode; -import com.ject.studytrip.global.exception.response.ErrorResponse; -import jakarta.servlet.http.HttpServletResponse; -import java.io.IOException; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Component; - -@Component -@RequiredArgsConstructor -public class SecurityResponseHandler { - - private static final String CONTENT_TYPE = "application/json;charset=UTF-8"; - - private final ObjectMapper objectMapper; - - public void sendResponse(HttpServletResponse response, ErrorCode errorCode) throws IOException { - final ErrorResponse errorResponse = - ErrorResponse.of(errorCode.getName(), errorCode.getMessage()); - final StandardResponse standardResponse = - StandardResponse.fail(errorCode.getStatus().value(), errorResponse); - final String json = objectMapper.writeValueAsString(standardResponse); - - response.setStatus(standardResponse.status()); - response.setContentType(CONTENT_TYPE); - response.getWriter().write(json); - } -} diff --git a/src/main/java/com/ject/studytrip/global/util/DateUtil.java b/src/main/java/com/ject/studytrip/global/util/DateUtil.java deleted file mode 100644 index 21aa47c..0000000 --- a/src/main/java/com/ject/studytrip/global/util/DateUtil.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.ject.studytrip.global.util; - -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; - -public class DateUtil { - - private static final DateTimeFormatter DEFAULT_DATE_FORMATTER = DateTimeFormatter.ISO_DATE; - private static final DateTimeFormatter DEFAULT_DATETIME_FORMATTER = - DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"); - - public static String formatDate(LocalDate date) { - return date != null ? date.format(DEFAULT_DATE_FORMATTER) : null; - } - - public static LocalDate parseDate(String raw) { - return LocalDate.parse(raw, DEFAULT_DATE_FORMATTER); - } - - public static String formatDateTime(LocalDateTime dateTime) { - return dateTime != null ? dateTime.format(DEFAULT_DATETIME_FORMATTER) : null; - } - - public static LocalDateTime parseDateTime(String raw) { - return LocalDateTime.parse(raw, DEFAULT_DATETIME_FORMATTER); - } -} diff --git a/src/main/java/com/ject/studytrip/global/util/FilenameUtil.java b/src/main/java/com/ject/studytrip/global/util/FilenameUtil.java deleted file mode 100644 index 218271e..0000000 --- a/src/main/java/com/ject/studytrip/global/util/FilenameUtil.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.ject.studytrip.global.util; - -import java.util.UUID; - -public final class FilenameUtil { - private static final String FILENAME_PATTERN = "%s.%s"; - - private FilenameUtil() {} - - public static String createNewFilename(String ext) { - String newFilename = UUID.randomUUID().toString(); - return FILENAME_PATTERN.formatted(newFilename, ext); - } - - public static String extractExtension(String filename) { - if (filename == null) return null; - - int i = filename.lastIndexOf('.'); - if (i < 0 || i == filename.length() - 1) return null; - - return filename.substring(i + 1).toLowerCase(); - } -} diff --git a/src/main/java/com/ject/studytrip/global/util/OriginArgumentUtil.java b/src/main/java/com/ject/studytrip/global/util/OriginArgumentUtil.java deleted file mode 100644 index 11a01a2..0000000 --- a/src/main/java/com/ject/studytrip/global/util/OriginArgumentUtil.java +++ /dev/null @@ -1,80 +0,0 @@ -package com.ject.studytrip.global.util; - -import static org.springframework.util.StringUtils.hasText; - -import java.net.URI; - -public final class OriginArgumentUtil { - private static final String NULL_ORIGIN = "null"; - private static final String HTTP_SCHEME = "http"; - private static final String HTTPS_SCHEME = "https"; - private static final String SCHEME_HOST_SEPARATOR = "://"; - private static final String HOST_PORT_SEPARATOR = ":"; - private static final int DEFAULT_HTTP_PORT = 80; - private static final int DEFAULT_HTTPS_PORT = 443; - private static final int MIN_PORT = 1; - private static final int MAX_PORT = 65535; - - private OriginArgumentUtil() {} - - // Origin 추출 - public static String resolveOrigin(String origin, String scheme, String host, int port) { - // Origin 헤더 정보가 있을 경우 - if (hasText(origin)) { - if (!NULL_ORIGIN.equalsIgnoreCase(origin.trim())) { - return canonicalizeOrigin(origin); - } - } - - // 서버 정보로 구성 - String serverDerivedOrigin = - scheme + SCHEME_HOST_SEPARATOR + host + HOST_PORT_SEPARATOR + port; - return canonicalizeOrigin(serverDerivedOrigin); - } - - private static String canonicalizeOrigin(String origin) { - if (!hasText(origin)) return null; - - try { - URI uri = URI.create(origin.trim()); - - String scheme = toSupportedScheme(uri.getScheme()); - if (!hasText(scheme)) return null; - - String host = uri.getHost(); - if (!hasText(host)) return null; - - int port = uri.getPort(); - if (port != -1 && !isValidPort(port)) return null; - - String result = scheme + SCHEME_HOST_SEPARATOR + host; - if (port != -1 && !isDefaultPort(scheme, port)) { - result += HOST_PORT_SEPARATOR + port; - } - - return result; - } catch (IllegalArgumentException e) { - return null; - } - } - - private static String toSupportedScheme(String scheme) { - if (!hasText(scheme)) return null; - - String lower = scheme.trim().toLowerCase(); - if (HTTP_SCHEME.equals(lower) || HTTPS_SCHEME.equals(lower)) { - return lower; - } - - return null; - } - - private static boolean isValidPort(int port) { - return port >= MIN_PORT && port <= MAX_PORT; - } - - private static boolean isDefaultPort(String scheme, int port) { - return (HTTP_SCHEME.equals(scheme) && port == DEFAULT_HTTP_PORT) - || (HTTPS_SCHEME.equals(scheme) && port == DEFAULT_HTTPS_PORT); - } -} diff --git a/src/main/java/com/ject/studytrip/member/domain/model/Member.java b/src/main/java/com/ject/studytrip/member/domain/model/Member.java deleted file mode 100644 index 9e89f54..0000000 --- a/src/main/java/com/ject/studytrip/member/domain/model/Member.java +++ /dev/null @@ -1,112 +0,0 @@ -package com.ject.studytrip.member.domain.model; - -import static org.springframework.util.StringUtils.hasText; - -import com.ject.studytrip.global.common.entity.BaseTimeEntity; -import jakarta.persistence.*; -import java.time.LocalDateTime; -import lombok.*; - -@Entity -@NoArgsConstructor(access = AccessLevel.PROTECTED) -@AllArgsConstructor(access = AccessLevel.PRIVATE) -@Builder(access = AccessLevel.PRIVATE) -public class Member extends BaseTimeEntity { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @Enumerated(EnumType.STRING) - @Column(nullable = false) - private SocialProvider socialProvider; - - @Column(nullable = false) - private String socialId; - - @Column(nullable = false, unique = true) - private String email; - - @Column(nullable = false) - private String nickname; - - private String profileImage; - - @Enumerated(EnumType.STRING) - private MemberCategory category; - - @Enumerated(EnumType.STRING) - private MemberRole role; - - public static Member of( - SocialProvider socialProvider, - String socialId, - String email, - String nickname, - String profileImage, - MemberCategory category, - MemberRole role) { - return Member.builder() - .socialProvider(socialProvider) - .socialId(socialId) - .email(email) - .nickname(nickname) - .profileImage(profileImage) - .category(category) - .role(role) - .build(); - } - - public void update(String nickname, MemberCategory category) { - if (hasText(nickname) && !nickname.equals(this.nickname)) { // 다른 경우에만 닉네임 수정 - this.nickname = nickname; - } - if (category != null && category != this.category) { // 다른 경우에만 카테고리 수정 - this.category = category; - } - } - - public void updateProfileImage(String profileImage) { - if (hasText(profileImage)) this.profileImage = profileImage; - } - - public void updateDeletedAt() { - this.deletedAt = LocalDateTime.now(); - } - - public void restoreDeletedAt() { - this.deletedAt = null; - } - - public MemberCategory getCategory() { - return category; - } - - public String getEmail() { - return email; - } - - public Long getId() { - return id; - } - - public String getNickname() { - return nickname; - } - - public String getProfileImage() { - return profileImage; - } - - public MemberRole getRole() { - return role; - } - - public String getSocialId() { - return socialId; - } - - public SocialProvider getSocialProvider() { - return socialProvider; - } -} diff --git a/src/main/java/com/ject/studytrip/member/domain/model/MemberCategory.java b/src/main/java/com/ject/studytrip/member/domain/model/MemberCategory.java deleted file mode 100644 index c5b6d99..0000000 --- a/src/main/java/com/ject/studytrip/member/domain/model/MemberCategory.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.ject.studytrip.member.domain.model; - -import com.ject.studytrip.global.exception.CustomException; -import com.ject.studytrip.member.domain.error.MemberErrorCode; - -public enum MemberCategory { - STUDENT, - WORKER, - FREELANCER, - JOBSEEKER, - ; - - public static MemberCategory from(String category) { - try { - return MemberCategory.valueOf(category); - } catch (IllegalArgumentException e) { - throw new CustomException(MemberErrorCode.INVALID_MEMBER_CATEGORY); - } - } -} diff --git a/src/main/java/com/ject/studytrip/member/domain/model/MemberRole.java b/src/main/java/com/ject/studytrip/member/domain/model/MemberRole.java deleted file mode 100644 index 55985e2..0000000 --- a/src/main/java/com/ject/studytrip/member/domain/model/MemberRole.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.ject.studytrip.member.domain.model; - -public enum MemberRole { - ROLE_USER, - ROLE_ADMIN, - ROLE_OWNER -} diff --git a/src/main/java/com/ject/studytrip/member/domain/model/SocialProvider.java b/src/main/java/com/ject/studytrip/member/domain/model/SocialProvider.java deleted file mode 100644 index c7fff05..0000000 --- a/src/main/java/com/ject/studytrip/member/domain/model/SocialProvider.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.ject.studytrip.member.domain.model; - -public enum SocialProvider { - KAKAO, - GOOGLE -} diff --git a/src/main/java/com/ject/studytrip/member/domain/repository/MemberCommandRepository.java b/src/main/java/com/ject/studytrip/member/domain/repository/MemberCommandRepository.java deleted file mode 100644 index 9e7410e..0000000 --- a/src/main/java/com/ject/studytrip/member/domain/repository/MemberCommandRepository.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.ject.studytrip.member.domain.repository; - -public interface MemberCommandRepository { - long deleteAllByDeletedAtIsNotNull(); -} diff --git a/src/main/java/com/ject/studytrip/member/domain/repository/MemberQueryRepository.java b/src/main/java/com/ject/studytrip/member/domain/repository/MemberQueryRepository.java deleted file mode 100644 index 2f65d9a..0000000 --- a/src/main/java/com/ject/studytrip/member/domain/repository/MemberQueryRepository.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.ject.studytrip.member.domain.repository; - -import com.ject.studytrip.member.domain.model.MemberRole; -import java.util.Optional; - -public interface MemberQueryRepository { - Optional findMemberRoleById(Long memberId); -} diff --git a/src/main/java/com/ject/studytrip/member/infra/querydsl/MemberCommandRepositoryAdapter.java b/src/main/java/com/ject/studytrip/member/infra/querydsl/MemberCommandRepositoryAdapter.java deleted file mode 100644 index cf334b9..0000000 --- a/src/main/java/com/ject/studytrip/member/infra/querydsl/MemberCommandRepositoryAdapter.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.ject.studytrip.member.infra.querydsl; - -import static com.ject.studytrip.member.domain.model.QMember.member; - -import com.ject.studytrip.member.domain.repository.MemberCommandRepository; -import com.querydsl.jpa.impl.JPAQueryFactory; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Repository; - -@Repository -@RequiredArgsConstructor -public class MemberCommandRepositoryAdapter implements MemberCommandRepository { - private final JPAQueryFactory queryFactory; - - @Override - public long deleteAllByDeletedAtIsNotNull() { - return queryFactory.delete(member).where(member.deletedAt.isNotNull()).execute(); - } -} diff --git a/src/main/java/com/ject/studytrip/member/infra/querydsl/MemberQueryRepositoryAdapter.java b/src/main/java/com/ject/studytrip/member/infra/querydsl/MemberQueryRepositoryAdapter.java deleted file mode 100644 index 65e4261..0000000 --- a/src/main/java/com/ject/studytrip/member/infra/querydsl/MemberQueryRepositoryAdapter.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.ject.studytrip.member.infra.querydsl; - -import static com.ject.studytrip.member.domain.model.QMember.member; - -import com.ject.studytrip.member.domain.model.MemberRole; -import com.ject.studytrip.member.domain.repository.MemberQueryRepository; -import com.querydsl.jpa.impl.JPAQueryFactory; -import java.util.Optional; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Repository; - -@Repository -@RequiredArgsConstructor -public class MemberQueryRepositoryAdapter implements MemberQueryRepository { - private final JPAQueryFactory queryFactory; - - @Override - public Optional findMemberRoleById(Long memberId) { - MemberRole memberRole = - queryFactory - .select(member.role) - .from(member) - .where(member.id.eq(memberId)) - .fetchOne(); - - return Optional.ofNullable(memberRole); - } -} diff --git a/src/main/java/com/ject/studytrip/mission/domain/model/DailyMission.java b/src/main/java/com/ject/studytrip/mission/domain/model/DailyMission.java deleted file mode 100644 index d337819..0000000 --- a/src/main/java/com/ject/studytrip/mission/domain/model/DailyMission.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.ject.studytrip.mission.domain.model; - -import com.ject.studytrip.global.common.entity.BaseTimeEntity; -import com.ject.studytrip.trip.domain.model.DailyGoal; -import jakarta.persistence.*; -import java.time.LocalDateTime; -import lombok.*; - -@Entity -@NoArgsConstructor(access = AccessLevel.PROTECTED) -@AllArgsConstructor(access = AccessLevel.PRIVATE) -@Builder(access = AccessLevel.PRIVATE) -public class DailyMission extends BaseTimeEntity { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "mission_id", nullable = false) - private Mission mission; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "daily_goal_id", nullable = false) - private DailyGoal dailyGoal; - - public static DailyMission of(Mission mission, DailyGoal dailyGoal) { - return DailyMission.builder().mission(mission).dailyGoal(dailyGoal).build(); - } - - public void updateDeletedAt() { - this.deletedAt = LocalDateTime.now(); - } - - public DailyGoal getDailyGoal() { - return dailyGoal; - } - - public Long getId() { - return id; - } - - public Mission getMission() { - return mission; - } -} diff --git a/src/main/java/com/ject/studytrip/mission/domain/model/Mission.java b/src/main/java/com/ject/studytrip/mission/domain/model/Mission.java deleted file mode 100644 index c5adf5c..0000000 --- a/src/main/java/com/ject/studytrip/mission/domain/model/Mission.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.ject.studytrip.mission.domain.model; - -import static org.springframework.util.StringUtils.hasText; - -import com.ject.studytrip.global.common.entity.BaseTimeEntity; -import com.ject.studytrip.stamp.domain.model.Stamp; -import jakarta.persistence.*; -import java.time.LocalDateTime; -import lombok.*; - -@Entity -@NoArgsConstructor(access = AccessLevel.PROTECTED) -@AllArgsConstructor(access = AccessLevel.PRIVATE) -@Builder(access = AccessLevel.PRIVATE) -public class Mission extends BaseTimeEntity { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "stamp_id", nullable = false) - private Stamp stamp; - - @Column(nullable = false) - private String name; - - private boolean completed; - - public static Mission of(Stamp stamp, String name) { - return Mission.builder().stamp(stamp).name(name).completed(false).build(); - } - - public void updateName(String name) { - if (hasText(name)) this.name = name; - } - - public void updateDeletedAt() { - this.deletedAt = LocalDateTime.now(); - } - - public void updateCompleted() { - this.completed = true; - } - - public boolean isCompleted() { - return completed; - } - - public Long getId() { - return id; - } - - public String getName() { - return name; - } - - public Stamp getStamp() { - return stamp; - } -} diff --git a/src/main/java/com/ject/studytrip/mission/domain/repository/DailyMissionCommandRepository.java b/src/main/java/com/ject/studytrip/mission/domain/repository/DailyMissionCommandRepository.java deleted file mode 100644 index 9bc7737..0000000 --- a/src/main/java/com/ject/studytrip/mission/domain/repository/DailyMissionCommandRepository.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.ject.studytrip.mission.domain.repository; - -public interface DailyMissionCommandRepository { - long deleteAllByDeletedAtIsNotNull(); - - long deleteAllByDeletedMissionOwner(); - - long deleteAllByDeletedDailyGoalOwner(); - - long deleteAllByMemberId(Long memberId); -} diff --git a/src/main/java/com/ject/studytrip/mission/domain/repository/DailyMissionQueryRepository.java b/src/main/java/com/ject/studytrip/mission/domain/repository/DailyMissionQueryRepository.java deleted file mode 100644 index 1e36c63..0000000 --- a/src/main/java/com/ject/studytrip/mission/domain/repository/DailyMissionQueryRepository.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.ject.studytrip.mission.domain.repository; - -import com.ject.studytrip.mission.domain.model.DailyMission; -import java.util.List; - -public interface DailyMissionQueryRepository { - List findAllByDailyGoalIdFetchJoinMission(Long dailyGoalId); - - List findAllWithMissionAndStampByIds(List ids); -} diff --git a/src/main/java/com/ject/studytrip/mission/domain/repository/MissionCommandRepository.java b/src/main/java/com/ject/studytrip/mission/domain/repository/MissionCommandRepository.java deleted file mode 100644 index e89ccaf..0000000 --- a/src/main/java/com/ject/studytrip/mission/domain/repository/MissionCommandRepository.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.ject.studytrip.mission.domain.repository; - -public interface MissionCommandRepository { - boolean existsByStampIdAndCompletedIsFalseAndDeletedAtIsNull(Long stampId); - - long deleteAllByDeletedAtIsNotNull(); - - long deleteAllByDeletedStampOwner(); - - long deleteAllByMemberId(Long memberId); -} diff --git a/src/main/java/com/ject/studytrip/mission/domain/repository/MissionQueryRepository.java b/src/main/java/com/ject/studytrip/mission/domain/repository/MissionQueryRepository.java deleted file mode 100644 index a9b003a..0000000 --- a/src/main/java/com/ject/studytrip/mission/domain/repository/MissionQueryRepository.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.ject.studytrip.mission.domain.repository; - -import com.ject.studytrip.mission.domain.model.Mission; -import java.util.List; - -public interface MissionQueryRepository { - List findAllByIdsInFetchJoinStamp(List ids); -} diff --git a/src/main/java/com/ject/studytrip/mission/infra/querydsl/DailyMissionCommandRepositoryAdapter.java b/src/main/java/com/ject/studytrip/mission/infra/querydsl/DailyMissionCommandRepositoryAdapter.java deleted file mode 100644 index 7e72e62..0000000 --- a/src/main/java/com/ject/studytrip/mission/infra/querydsl/DailyMissionCommandRepositoryAdapter.java +++ /dev/null @@ -1,69 +0,0 @@ -package com.ject.studytrip.mission.infra.querydsl; - -import static com.ject.studytrip.mission.domain.model.QDailyMission.dailyMission; -import static com.ject.studytrip.mission.domain.model.QMission.mission; -import static com.ject.studytrip.stamp.domain.model.QStamp.stamp; -import static com.ject.studytrip.trip.domain.model.QDailyGoal.dailyGoal; -import static com.ject.studytrip.trip.domain.model.QTrip.trip; - -import com.ject.studytrip.mission.domain.repository.DailyMissionCommandRepository; -import com.querydsl.jpa.JPAExpressions; -import com.querydsl.jpa.impl.JPAQueryFactory; -import java.util.List; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Repository; - -@Repository -@RequiredArgsConstructor -public class DailyMissionCommandRepositoryAdapter implements DailyMissionCommandRepository { - private final JPAQueryFactory queryFactory; - - @Override - public long deleteAllByDeletedAtIsNotNull() { - return queryFactory - .delete(dailyMission) - .where(dailyMission.deletedAt.isNotNull()) - .execute(); - } - - @Override - public long deleteAllByDeletedMissionOwner() { - return queryFactory - .delete(dailyMission) - .where( - dailyMission.mission.id.in( - JPAExpressions.select(mission.id) - .from(mission) - .where(mission.deletedAt.isNotNull()))) - .execute(); - } - - @Override - public long deleteAllByDeletedDailyGoalOwner() { - return queryFactory - .delete(dailyMission) - .where( - dailyMission.dailyGoal.id.in( - JPAExpressions.select(dailyGoal.id) - .from(dailyGoal) - .where(dailyGoal.deletedAt.isNotNull()))) - .execute(); - } - - @Override - public long deleteAllByMemberId(Long memberId) { - List ids = - queryFactory - .select(dailyMission.id) - .from(dailyMission) - .join(dailyMission.mission, mission) - .join(mission.stamp, stamp) - .join(stamp.trip, trip) - .where(trip.member.id.eq(memberId)) - .fetch(); - - if (ids.isEmpty()) return 0; - - return queryFactory.delete(dailyMission).where(dailyMission.id.in(ids)).execute(); - } -} diff --git a/src/main/java/com/ject/studytrip/mission/infra/querydsl/DailyMissionQueryRepositoryAdapter.java b/src/main/java/com/ject/studytrip/mission/infra/querydsl/DailyMissionQueryRepositoryAdapter.java deleted file mode 100644 index 473bfbe..0000000 --- a/src/main/java/com/ject/studytrip/mission/infra/querydsl/DailyMissionQueryRepositoryAdapter.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.ject.studytrip.mission.infra.querydsl; - -import static com.ject.studytrip.mission.domain.model.QDailyMission.dailyMission; -import static com.ject.studytrip.mission.domain.model.QMission.mission; -import static com.ject.studytrip.stamp.domain.model.QStamp.stamp; - -import com.ject.studytrip.mission.domain.model.DailyMission; -import com.ject.studytrip.mission.domain.repository.DailyMissionQueryRepository; -import com.querydsl.jpa.impl.JPAQueryFactory; -import java.util.List; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Repository; - -@Repository -@RequiredArgsConstructor -public class DailyMissionQueryRepositoryAdapter implements DailyMissionQueryRepository { - private final JPAQueryFactory queryFactory; - - @Override - public List findAllByDailyGoalIdFetchJoinMission(Long dailyGoalId) { - return queryFactory - .selectFrom(dailyMission) - .join(dailyMission.mission, mission) - .fetchJoin() - .where(dailyMission.dailyGoal.id.eq(dailyGoalId), dailyMission.deletedAt.isNull()) - .fetch(); - } - - @Override - public List findAllWithMissionAndStampByIds(List ids) { - return queryFactory - .selectFrom(dailyMission) - .join(dailyMission.mission, mission) - .fetchJoin() - .join(mission.stamp, stamp) - .fetchJoin() - .where(dailyMission.id.in(ids)) - .fetch(); - } -} diff --git a/src/main/java/com/ject/studytrip/mission/infra/querydsl/MissionCommandRepositoryAdapter.java b/src/main/java/com/ject/studytrip/mission/infra/querydsl/MissionCommandRepositoryAdapter.java deleted file mode 100644 index 758176a..0000000 --- a/src/main/java/com/ject/studytrip/mission/infra/querydsl/MissionCommandRepositoryAdapter.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.ject.studytrip.mission.infra.querydsl; - -import static com.ject.studytrip.mission.domain.model.QMission.mission; -import static com.ject.studytrip.stamp.domain.model.QStamp.stamp; -import static com.ject.studytrip.trip.domain.model.QTrip.trip; - -import com.ject.studytrip.mission.domain.repository.MissionCommandRepository; -import com.querydsl.jpa.JPAExpressions; -import com.querydsl.jpa.impl.JPAQueryFactory; -import java.util.List; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Repository; - -@Repository -@RequiredArgsConstructor -public class MissionCommandRepositoryAdapter implements MissionCommandRepository { - private final JPAQueryFactory queryFactory; - - @Override - public boolean existsByStampIdAndCompletedIsFalseAndDeletedAtIsNull(Long stampId) { - Integer hit = - queryFactory - .selectOne() - .from(mission) - .where( - mission.stamp.id.eq(stampId), - mission.completed.isFalse(), - mission.deletedAt.isNull()) - .fetchFirst(); - - return hit != null; - } - - @Override - public long deleteAllByDeletedAtIsNotNull() { - return queryFactory.delete(mission).where(mission.deletedAt.isNotNull()).execute(); - } - - @Override - public long deleteAllByDeletedStampOwner() { - return queryFactory - .delete(mission) - .where( - mission.stamp.id.in( - JPAExpressions.select(stamp.id) - .from(stamp) - .where(stamp.deletedAt.isNotNull()))) - .execute(); - } - - @Override - public long deleteAllByMemberId(Long memberId) { - List ids = - queryFactory - .select(mission.id) - .from(mission) - .join(mission.stamp, stamp) - .join(stamp.trip, trip) - .where(trip.member.id.eq(memberId)) - .fetch(); - - if (ids.isEmpty()) return 0; - - return queryFactory.delete(mission).where(mission.id.in(ids)).execute(); - } -} diff --git a/src/main/java/com/ject/studytrip/mission/infra/querydsl/MissionQueryRepositoryAdapter.java b/src/main/java/com/ject/studytrip/mission/infra/querydsl/MissionQueryRepositoryAdapter.java deleted file mode 100644 index c7c5c58..0000000 --- a/src/main/java/com/ject/studytrip/mission/infra/querydsl/MissionQueryRepositoryAdapter.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.ject.studytrip.mission.infra.querydsl; - -import static com.ject.studytrip.mission.domain.model.QMission.mission; -import static com.ject.studytrip.stamp.domain.model.QStamp.stamp; - -import com.ject.studytrip.mission.domain.model.Mission; -import com.ject.studytrip.mission.domain.repository.MissionQueryRepository; -import com.querydsl.jpa.impl.JPAQueryFactory; -import java.util.List; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Repository; - -@Repository -@RequiredArgsConstructor -public class MissionQueryRepositoryAdapter implements MissionQueryRepository { - private final JPAQueryFactory queryFactory; - - @Override - public List findAllByIdsInFetchJoinStamp(List ids) { - return queryFactory - .selectFrom(mission) - .join(mission.stamp, stamp) - .fetchJoin() - .where(mission.id.in(ids)) - .fetch(); - } -} diff --git a/src/main/java/com/ject/studytrip/pomodoro/domain/model/Pomodoro.java b/src/main/java/com/ject/studytrip/pomodoro/domain/model/Pomodoro.java deleted file mode 100644 index 0b0e9fb..0000000 --- a/src/main/java/com/ject/studytrip/pomodoro/domain/model/Pomodoro.java +++ /dev/null @@ -1,75 +0,0 @@ -package com.ject.studytrip.pomodoro.domain.model; - -import com.ject.studytrip.global.common.entity.BaseTimeEntity; -import com.ject.studytrip.trip.domain.model.DailyGoal; -import jakarta.persistence.*; -import java.time.LocalDateTime; -import lombok.*; - -@Entity -@NoArgsConstructor(access = AccessLevel.PROTECTED) -@AllArgsConstructor(access = AccessLevel.PRIVATE) -@Builder(access = AccessLevel.PRIVATE) -public class Pomodoro extends BaseTimeEntity { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "daily_goal_id", nullable = false) - private DailyGoal dailyGoal; - - private int focusDurationInSeconds; // 집중 시간(초) - private int focusSessionCount; // 집중 세션 횟수 - private int breakDurationInSeconds; // 휴식 시간(초) - private int totalFocusTimeInSeconds; // 총 집중 시간(초) - - // TODO : 추후 집중 세션 개수와 휴식 시간을 설정할 수 있는 기능이 추가될 경우 리팩토링 - // 우선은 고정 값 세팅 - public static Pomodoro of( - DailyGoal dailyGoal, - int focusDurationInSeconds, - int focusSessionCount, - int breakDurationInSeconds) { - return Pomodoro.builder() - .dailyGoal(dailyGoal) - .focusDurationInSeconds(focusDurationInSeconds) - .focusSessionCount(focusSessionCount) - .breakDurationInSeconds(breakDurationInSeconds) - .totalFocusTimeInSeconds(0) - .build(); - } - - public void updateTotalFocusTimeInSeconds(int totalFocusTimeInSeconds) { - this.totalFocusTimeInSeconds = totalFocusTimeInSeconds; - } - - public void updateDeletedAt() { - this.deletedAt = LocalDateTime.now(); - } - - public Long getId() { - return id; - } - - public DailyGoal getDailyGoal() { - return dailyGoal; - } - - public int getFocusDurationInSeconds() { - return focusDurationInSeconds; - } - - public int getFocusSessionCount() { - return focusSessionCount; - } - - public int getBreakDurationInSeconds() { - return breakDurationInSeconds; - } - - public int getTotalFocusTimeInSeconds() { - return totalFocusTimeInSeconds; - } -} diff --git a/src/main/java/com/ject/studytrip/pomodoro/domain/repository/PomodoroCommandRepository.java b/src/main/java/com/ject/studytrip/pomodoro/domain/repository/PomodoroCommandRepository.java deleted file mode 100644 index 3996ce3..0000000 --- a/src/main/java/com/ject/studytrip/pomodoro/domain/repository/PomodoroCommandRepository.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.ject.studytrip.pomodoro.domain.repository; - -public interface PomodoroCommandRepository { - long deleteAllByDeletedAtIsNotNull(); - - long deleteAllByDeletedDailyGoalOwner(); - - long deleteAllByMemberId(Long memberId); -} diff --git a/src/main/java/com/ject/studytrip/pomodoro/domain/repository/PomodoroQueryRepository.java b/src/main/java/com/ject/studytrip/pomodoro/domain/repository/PomodoroQueryRepository.java deleted file mode 100644 index 4827f21..0000000 --- a/src/main/java/com/ject/studytrip/pomodoro/domain/repository/PomodoroQueryRepository.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.ject.studytrip.pomodoro.domain.repository; - -public interface PomodoroQueryRepository { - long sumFocusHoursByTripId(Long tripId); -} diff --git a/src/main/java/com/ject/studytrip/pomodoro/infra/querydsl/PomodoroCommandRepositoryAdapter.java b/src/main/java/com/ject/studytrip/pomodoro/infra/querydsl/PomodoroCommandRepositoryAdapter.java deleted file mode 100644 index 6f7525c..0000000 --- a/src/main/java/com/ject/studytrip/pomodoro/infra/querydsl/PomodoroCommandRepositoryAdapter.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.ject.studytrip.pomodoro.infra.querydsl; - -import static com.ject.studytrip.pomodoro.domain.model.QPomodoro.pomodoro; -import static com.ject.studytrip.trip.domain.model.QDailyGoal.dailyGoal; -import static com.ject.studytrip.trip.domain.model.QTrip.trip; - -import com.ject.studytrip.pomodoro.domain.repository.PomodoroCommandRepository; -import com.querydsl.jpa.JPAExpressions; -import com.querydsl.jpa.impl.JPAQueryFactory; -import java.util.List; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Repository; - -@Repository -@RequiredArgsConstructor -public class PomodoroCommandRepositoryAdapter implements PomodoroCommandRepository { - private final JPAQueryFactory queryFactory; - - @Override - public long deleteAllByDeletedAtIsNotNull() { - return queryFactory.delete(pomodoro).where(pomodoro.deletedAt.isNotNull()).execute(); - } - - @Override - public long deleteAllByDeletedDailyGoalOwner() { - return queryFactory - .delete(pomodoro) - .where( - pomodoro.dailyGoal.id.in( - JPAExpressions.select(dailyGoal.id) - .from(dailyGoal) - .where(dailyGoal.deletedAt.isNotNull()))) - .execute(); - } - - @Override - public long deleteAllByMemberId(Long memberId) { - List ids = - queryFactory - .select(pomodoro.id) - .from(pomodoro) - .join(pomodoro.dailyGoal, dailyGoal) - .join(dailyGoal.trip, trip) - .where(trip.member.id.eq(memberId)) - .fetch(); - - if (ids.isEmpty()) return 0; - - return queryFactory.delete(pomodoro).where(pomodoro.id.in(ids)).execute(); - } -} diff --git a/src/main/java/com/ject/studytrip/pomodoro/infra/querydsl/PomodoroQueryRepositoryAdapter.java b/src/main/java/com/ject/studytrip/pomodoro/infra/querydsl/PomodoroQueryRepositoryAdapter.java deleted file mode 100644 index 66f9667..0000000 --- a/src/main/java/com/ject/studytrip/pomodoro/infra/querydsl/PomodoroQueryRepositoryAdapter.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.ject.studytrip.pomodoro.infra.querydsl; - -import static com.ject.studytrip.pomodoro.domain.model.QPomodoro.pomodoro; -import static com.ject.studytrip.trip.domain.model.QDailyGoal.dailyGoal; -import static com.ject.studytrip.trip.domain.model.QTrip.trip; - -import com.ject.studytrip.pomodoro.domain.repository.PomodoroQueryRepository; -import com.querydsl.jpa.impl.JPAQueryFactory; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Repository; - -@Repository -@RequiredArgsConstructor -public class PomodoroQueryRepositoryAdapter implements PomodoroQueryRepository { - private final JPAQueryFactory queryFactory; - - @Override - public long sumFocusHoursByTripId(Long tripId) { - Integer totalSeconds = - queryFactory - .select(pomodoro.totalFocusTimeInSeconds.sum()) - .from(pomodoro) - .join(pomodoro.dailyGoal, dailyGoal) - .join(dailyGoal.trip, trip) - .where( - trip.id.eq(tripId), - pomodoro.deletedAt.isNull(), - dailyGoal.deletedAt.isNull(), - trip.deletedAt.isNull()) - .fetchOne(); - - long seconds = totalSeconds == null ? 0L : totalSeconds.longValue(); - - return seconds / 3600L; // 정수 시간(내림) - } -} diff --git a/src/main/java/com/ject/studytrip/sample/SampleController.java b/src/main/java/com/ject/studytrip/sample/SampleController.java deleted file mode 100644 index 2916d53..0000000 --- a/src/main/java/com/ject/studytrip/sample/SampleController.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.ject.studytrip.sample; - -import com.ject.studytrip.global.common.response.StandardResponse; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.validation.Valid; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; - -@Tag(name = "sample", description = "샘플 API") -@RequestMapping("/api/sample") -@RestController -public class SampleController { - - @Operation(summary = "sampleGet", description = "GET 샘플 API 입니다.") - @GetMapping() - public ResponseEntity sampleGet(@RequestParam Long sampleId) { - StandardResponse response = StandardResponse.success(HttpStatus.OK.value(), sampleId); - return ResponseEntity.status(HttpStatus.OK).body(response); - } - - @Operation(summary = "samplePost", description = "POST 샘플 API 입니다.") - @PostMapping() - public ResponseEntity samplePost(@RequestBody @Valid SampleRequest request) { - SampleResponse sampleResponse = SampleResponse.of(request.sample(), request.sampleNum()); - StandardResponse response = - StandardResponse.success(HttpStatus.CREATED.value(), sampleResponse); - return ResponseEntity.status(HttpStatus.CREATED).body(response); - } - - @Operation(summary = "samplePut", description = "PUT 샘플 API 입니다.") - @PutMapping("/{sampleId}") - public ResponseEntity samplePut(@PathVariable Long sampleId) { - StandardResponse response = StandardResponse.success(HttpStatus.OK.value(), null); - return ResponseEntity.status(HttpStatus.OK).body(response); - } - - @Operation(summary = "samplePatch", description = "PATCH 샘플 API 입니다.") - @PatchMapping("/{sampleId}") - public ResponseEntity samplePatch(@PathVariable Long sampleId) { - StandardResponse response = StandardResponse.success(HttpStatus.OK.value(), null); - return ResponseEntity.status(HttpStatus.OK).body(response); - } - - @Operation(summary = "sampleDelete", description = "DELETE 샘플 API 입니다.") - @DeleteMapping("/{sampleId}") - public ResponseEntity sampleDelete(@PathVariable Long sampleId) { - StandardResponse response = StandardResponse.success(HttpStatus.OK.value(), sampleId); - return ResponseEntity.status(HttpStatus.OK).body(response); - } -} diff --git a/src/main/java/com/ject/studytrip/sample/SampleRequest.java b/src/main/java/com/ject/studytrip/sample/SampleRequest.java deleted file mode 100644 index 9fb9272..0000000 --- a/src/main/java/com/ject/studytrip/sample/SampleRequest.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.ject.studytrip.sample; - -import jakarta.validation.constraints.Min; -import jakarta.validation.constraints.NotEmpty; - -public record SampleRequest( - @NotEmpty(message = "sample 은 필수입니다.") String sample, - @Min(value = 1, message = "sampleNum 은 1 이상이여야 합니다.") int sampleNum) {} diff --git a/src/main/java/com/ject/studytrip/sample/SampleResponse.java b/src/main/java/com/ject/studytrip/sample/SampleResponse.java deleted file mode 100644 index 7a2659e..0000000 --- a/src/main/java/com/ject/studytrip/sample/SampleResponse.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.ject.studytrip.sample; - -public record SampleResponse(String sample, int sampleNum) { - - public static SampleResponse of(String sample, int sampleNum) { - return new SampleResponse(sample, sampleNum); - } -} diff --git a/src/main/java/com/ject/studytrip/stamp/domain/model/Stamp.java b/src/main/java/com/ject/studytrip/stamp/domain/model/Stamp.java deleted file mode 100644 index 0b84a16..0000000 --- a/src/main/java/com/ject/studytrip/stamp/domain/model/Stamp.java +++ /dev/null @@ -1,114 +0,0 @@ -package com.ject.studytrip.stamp.domain.model; - -import static org.springframework.util.StringUtils.hasText; - -import com.ject.studytrip.global.common.entity.BaseTimeEntity; -import com.ject.studytrip.trip.domain.model.Trip; -import jakarta.persistence.*; -import java.time.LocalDate; -import java.time.LocalDateTime; -import lombok.*; - -@Entity -@NoArgsConstructor(access = AccessLevel.PROTECTED) -@AllArgsConstructor(access = AccessLevel.PRIVATE) -@Builder(access = AccessLevel.PRIVATE) -public class Stamp extends BaseTimeEntity { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "trip_id", nullable = false) - private Trip trip; - - @Column(nullable = false) - private String name; - - private int stampOrder; - - private LocalDate endDate; - - private int totalMissions; - - private int completedMissions; - - private boolean completed; - - public static Stamp of(Trip trip, String name, int stampOrder, LocalDate endDate) { - return Stamp.builder() - .trip(trip) - .name(name) - .stampOrder(stampOrder) - .endDate(endDate) - .totalMissions(0) - .completedMissions(0) - .completed(false) - .build(); - } - - public void updateName(String name) { - if (hasText(name)) this.name = name; - } - - public void updateStampOrder(int newOrder) { - this.stampOrder = newOrder; - } - - public void updateEndDate(LocalDate endDate) { - this.endDate = endDate; - } - - public void updateCompleted() { - this.completed = true; - } - - public void updateDeletedAt() { - this.deletedAt = LocalDateTime.now(); - } - - public void increaseTotalMissions() { - this.totalMissions += 1; - } - - public void decreaseTotalMissions() { - this.totalMissions -= 1; - } - - public void increaseCompletedMissions(int count) { - this.completedMissions += count; - } - - public boolean isCompleted() { - return completed; - } - - public int getCompletedMissions() { - return completedMissions; - } - - public LocalDate getEndDate() { - return endDate; - } - - public Long getId() { - return id; - } - - public String getName() { - return name; - } - - public int getStampOrder() { - return stampOrder; - } - - public int getTotalMissions() { - return totalMissions; - } - - public Trip getTrip() { - return trip; - } -} diff --git a/src/main/java/com/ject/studytrip/stamp/domain/repository/StampCommandRepository.java b/src/main/java/com/ject/studytrip/stamp/domain/repository/StampCommandRepository.java deleted file mode 100644 index e7dfc50..0000000 --- a/src/main/java/com/ject/studytrip/stamp/domain/repository/StampCommandRepository.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.ject.studytrip.stamp.domain.repository; - -public interface StampCommandRepository { - boolean existsByTripIdAndCompletedIsFalseAndDeletedAtIsNull(Long tripId); - - long deleteAllByDeletedAtIsNotNull(); - - long deleteAllByDeletedTripOwner(); - - long deleteAllByMemberId(Long memberId); -} diff --git a/src/main/java/com/ject/studytrip/stamp/domain/repository/StampQueryRepository.java b/src/main/java/com/ject/studytrip/stamp/domain/repository/StampQueryRepository.java deleted file mode 100644 index b217a0e..0000000 --- a/src/main/java/com/ject/studytrip/stamp/domain/repository/StampQueryRepository.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.ject.studytrip.stamp.domain.repository; - -import com.ject.studytrip.stamp.domain.model.Stamp; -import java.util.List; -import java.util.Optional; - -public interface StampQueryRepository { - List findStampsToShiftAfterOrder(Long tripId, int deletedOrder); - - Optional findFirstIncompleteStampByTripId(Long tripId); - - int findNextStampOrderByTripId(Long tripId); -} diff --git a/src/main/java/com/ject/studytrip/stamp/infra/querydsl/StampCommandRepositoryAdapter.java b/src/main/java/com/ject/studytrip/stamp/infra/querydsl/StampCommandRepositoryAdapter.java deleted file mode 100644 index 46f8146..0000000 --- a/src/main/java/com/ject/studytrip/stamp/infra/querydsl/StampCommandRepositoryAdapter.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.ject.studytrip.stamp.infra.querydsl; - -import static com.ject.studytrip.stamp.domain.model.QStamp.stamp; -import static com.ject.studytrip.trip.domain.model.QTrip.trip; - -import com.ject.studytrip.stamp.domain.repository.StampCommandRepository; -import com.querydsl.jpa.JPAExpressions; -import com.querydsl.jpa.impl.JPAQueryFactory; -import java.util.List; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Repository; - -@Repository -@RequiredArgsConstructor -public class StampCommandRepositoryAdapter implements StampCommandRepository { - private final JPAQueryFactory queryFactory; - - @Override - public boolean existsByTripIdAndCompletedIsFalseAndDeletedAtIsNull(Long tripId) { - Integer hit = - queryFactory - .selectOne() - .from(stamp) - .where( - stamp.trip.id.eq(tripId), - stamp.completed.isFalse(), - stamp.deletedAt.isNull()) - .fetchOne(); - - return hit != null; - } - - @Override - public long deleteAllByDeletedAtIsNotNull() { - return queryFactory.delete(stamp).where(stamp.deletedAt.isNotNull()).execute(); - } - - @Override - public long deleteAllByDeletedTripOwner() { - return queryFactory - .delete(stamp) - .where( - stamp.trip.id.in( - JPAExpressions.select(trip.id) - .from(trip) - .where(trip.deletedAt.isNotNull()))) - .execute(); - } - - @Override - public long deleteAllByMemberId(Long memberId) { - List ids = - queryFactory - .select(stamp.id) - .from(stamp) - .join(stamp.trip, trip) - .where(trip.member.id.eq(memberId)) - .fetch(); - - if (ids.isEmpty()) return 0; - - return queryFactory.delete(stamp).where(stamp.id.in(ids)).execute(); - } -} diff --git a/src/main/java/com/ject/studytrip/stamp/infra/querydsl/StampQueryRepositoryAdapter.java b/src/main/java/com/ject/studytrip/stamp/infra/querydsl/StampQueryRepositoryAdapter.java deleted file mode 100644 index 1cdb838..0000000 --- a/src/main/java/com/ject/studytrip/stamp/infra/querydsl/StampQueryRepositoryAdapter.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.ject.studytrip.stamp.infra.querydsl; - -import static com.ject.studytrip.stamp.domain.model.QStamp.stamp; - -import com.ject.studytrip.stamp.domain.model.Stamp; -import com.ject.studytrip.stamp.domain.repository.StampQueryRepository; -import com.querydsl.jpa.impl.JPAQueryFactory; -import java.util.List; -import java.util.Optional; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Repository; - -@Repository -@RequiredArgsConstructor -public class StampQueryRepositoryAdapter implements StampQueryRepository { - private final JPAQueryFactory queryFactory; - - @Override - public List findStampsToShiftAfterOrder(Long tripId, int deletedOrder) { - return queryFactory - .selectFrom(stamp) - .where( - stamp.trip.id.eq(tripId), - stamp.stampOrder.gt(deletedOrder), - stamp.deletedAt.isNull()) - .orderBy(stamp.stampOrder.asc()) - .fetch(); - } - - // 완료되지 않은 스탬프 중 가장 첫번째 스탬프 조회 - @Override - public Optional findFirstIncompleteStampByTripId(Long tripId) { - return Optional.ofNullable( - queryFactory - .selectFrom(stamp) - .where( - stamp.trip.id.eq(tripId), - stamp.completed.isFalse(), - stamp.deletedAt.isNull()) - .orderBy(stamp.stampOrder.asc()) - .fetchFirst()); - } - - @Override - public int findNextStampOrderByTripId(Long tripId) { - Integer lastOrder = - queryFactory - .select(stamp.stampOrder.max().coalesce(0)) - .from(stamp) - .where(stamp.trip.id.eq(tripId), stamp.deletedAt.isNull()) - .fetchOne(); - - return lastOrder != null ? lastOrder + 1 : 1; - } -} diff --git a/src/main/java/com/ject/studytrip/studylog/domain/model/StudyLog.java b/src/main/java/com/ject/studytrip/studylog/domain/model/StudyLog.java deleted file mode 100644 index 7d6bd3c..0000000 --- a/src/main/java/com/ject/studytrip/studylog/domain/model/StudyLog.java +++ /dev/null @@ -1,79 +0,0 @@ -package com.ject.studytrip.studylog.domain.model; - -import static org.flywaydb.core.internal.util.StringUtils.hasText; - -import com.ject.studytrip.global.common.entity.BaseTimeEntity; -import com.ject.studytrip.member.domain.model.Member; -import com.ject.studytrip.trip.domain.model.DailyGoal; -import jakarta.persistence.*; -import java.time.LocalDateTime; -import lombok.*; - -@Entity -@NoArgsConstructor(access = AccessLevel.PROTECTED) -@AllArgsConstructor(access = AccessLevel.PRIVATE) -@Builder(access = AccessLevel.PRIVATE) -public class StudyLog extends BaseTimeEntity { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "member_id", nullable = false) - private Member member; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "daily_goal_id", nullable = false) - private DailyGoal dailyGoal; - - @Column(nullable = false) - private String title; - - @Column(nullable = false) - private String content; - - private String imageUrl; - - public static StudyLog of(Member member, DailyGoal dailyGoal, String content) { - return StudyLog.builder() - .member(member) - .dailyGoal(dailyGoal) - .title(dailyGoal.getTitle()) - .content(content) - .imageUrl(null) - .build(); - } - - public void updateImageUrl(String imageUrl) { - if (hasText(imageUrl)) this.imageUrl = imageUrl; - } - - public void updateDeletedAt() { - this.deletedAt = LocalDateTime.now(); - } - - public Long getId() { - return id; - } - - public Member getMember() { - return member; - } - - public DailyGoal getDailyGoal() { - return dailyGoal; - } - - public String getTitle() { - return title; - } - - public String getContent() { - return content; - } - - public String getImageUrl() { - return imageUrl; - } -} diff --git a/src/main/java/com/ject/studytrip/studylog/domain/model/StudyLogDailyMission.java b/src/main/java/com/ject/studytrip/studylog/domain/model/StudyLogDailyMission.java deleted file mode 100644 index 0cce2cd..0000000 --- a/src/main/java/com/ject/studytrip/studylog/domain/model/StudyLogDailyMission.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.ject.studytrip.studylog.domain.model; - -import com.ject.studytrip.global.common.entity.BaseTimeEntity; -import com.ject.studytrip.mission.domain.model.DailyMission; -import jakarta.persistence.*; -import lombok.*; - -@Entity -@NoArgsConstructor(access = AccessLevel.PROTECTED) -@AllArgsConstructor(access = AccessLevel.PRIVATE) -@Builder(access = AccessLevel.PRIVATE) -public class StudyLogDailyMission extends BaseTimeEntity { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "study_log_id", nullable = false) - private StudyLog studyLog; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "daily_mission_id", nullable = false) - private DailyMission dailyMission; - - public static StudyLogDailyMission of(StudyLog studyLog, DailyMission dailyMission) { - return StudyLogDailyMission.builder().studyLog(studyLog).dailyMission(dailyMission).build(); - } - - public StudyLog getStudyLog() { - return studyLog; - } - - public Long getId() { - return id; - } - - public DailyMission getDailyMission() { - return dailyMission; - } -} diff --git a/src/main/java/com/ject/studytrip/studylog/domain/repository/StudyLogCommandRepository.java b/src/main/java/com/ject/studytrip/studylog/domain/repository/StudyLogCommandRepository.java deleted file mode 100644 index 2c41692..0000000 --- a/src/main/java/com/ject/studytrip/studylog/domain/repository/StudyLogCommandRepository.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.ject.studytrip.studylog.domain.repository; - -public interface StudyLogCommandRepository { - long deleteAllByDeletedAtIsNotNull(); - - long deleteAllByDeletedMemberOwner(); - - long deleteAllByDeletedDailyGoalOwner(); - - long deleteByMemberId(Long memberId); -} diff --git a/src/main/java/com/ject/studytrip/studylog/domain/repository/StudyLogDailyMissionCommandRepository.java b/src/main/java/com/ject/studytrip/studylog/domain/repository/StudyLogDailyMissionCommandRepository.java deleted file mode 100644 index 84c8181..0000000 --- a/src/main/java/com/ject/studytrip/studylog/domain/repository/StudyLogDailyMissionCommandRepository.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.ject.studytrip.studylog.domain.repository; - -public interface StudyLogDailyMissionCommandRepository { - long deleteAllByDeletedAtIsNotNull(); - - long deleteAllByDeletedDailyMissionOwner(); - - long deleteAllByDeletedStudyLogOwner(); - - long deleteAllByMemberId(Long memberId); -} diff --git a/src/main/java/com/ject/studytrip/studylog/domain/repository/StudyLogDailyMissionQueryRepository.java b/src/main/java/com/ject/studytrip/studylog/domain/repository/StudyLogDailyMissionQueryRepository.java deleted file mode 100644 index 5987135..0000000 --- a/src/main/java/com/ject/studytrip/studylog/domain/repository/StudyLogDailyMissionQueryRepository.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.ject.studytrip.studylog.domain.repository; - -import com.ject.studytrip.studylog.domain.model.StudyLogDailyMission; -import java.util.List; -import java.util.Map; - -public interface StudyLogDailyMissionQueryRepository { - Map> findStudyLogDailyMissionsGroupedByStudyLogId( - List studyLogIds); -} diff --git a/src/main/java/com/ject/studytrip/studylog/domain/repository/StudyLogQueryRepository.java b/src/main/java/com/ject/studytrip/studylog/domain/repository/StudyLogQueryRepository.java deleted file mode 100644 index 6902ab9..0000000 --- a/src/main/java/com/ject/studytrip/studylog/domain/repository/StudyLogQueryRepository.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.ject.studytrip.studylog.domain.repository; - -import com.ject.studytrip.studylog.domain.model.StudyLog; -import java.util.List; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Slice; - -public interface StudyLogQueryRepository { - Slice findSliceByTripId(Long tripId, Pageable pageable, String order); - - Slice findSliceByTripReportIdOrderByCreatedAtDesc( - Long tripReportId, Pageable pageable); - - List findAllIdsByTripIdOrderByCreatedDesc(Long tripId); - - List findImageUrlsByMemberId(Long memberId); - - long countStudyLogsByTripId(Long tripId); - - long countActiveStudyLogsByMemberId(Long memberId); -} diff --git a/src/main/java/com/ject/studytrip/studylog/infra/querydsl/StudyLogCommandRepositoryAdapter.java b/src/main/java/com/ject/studytrip/studylog/infra/querydsl/StudyLogCommandRepositoryAdapter.java deleted file mode 100644 index 5fd6b35..0000000 --- a/src/main/java/com/ject/studytrip/studylog/infra/querydsl/StudyLogCommandRepositoryAdapter.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.ject.studytrip.studylog.infra.querydsl; - -import static com.ject.studytrip.member.domain.model.QMember.member; -import static com.ject.studytrip.studylog.domain.model.QStudyLog.studyLog; -import static com.ject.studytrip.trip.domain.model.QDailyGoal.dailyGoal; - -import com.ject.studytrip.studylog.domain.repository.StudyLogCommandRepository; -import com.querydsl.jpa.JPAExpressions; -import com.querydsl.jpa.impl.JPAQueryFactory; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Repository; - -@Repository -@RequiredArgsConstructor -public class StudyLogCommandRepositoryAdapter implements StudyLogCommandRepository { - private final JPAQueryFactory queryFactory; - - @Override - public long deleteAllByDeletedAtIsNotNull() { - return queryFactory.delete(studyLog).where(studyLog.deletedAt.isNotNull()).execute(); - } - - @Override - public long deleteAllByDeletedMemberOwner() { - return queryFactory - .delete(studyLog) - .where( - studyLog.member.id.in( - JPAExpressions.select(member.id) - .from(member) - .where(member.deletedAt.isNotNull()))) - .execute(); - } - - @Override - public long deleteAllByDeletedDailyGoalOwner() { - return queryFactory - .delete(studyLog) - .where( - studyLog.dailyGoal.id.in( - JPAExpressions.select(dailyGoal.id) - .from(dailyGoal) - .where(dailyGoal.deletedAt.isNotNull()))) - .execute(); - } - - @Override - public long deleteByMemberId(Long memberId) { - return queryFactory.delete(studyLog).where(studyLog.member.id.eq(memberId)).execute(); - } -} diff --git a/src/main/java/com/ject/studytrip/studylog/infra/querydsl/StudyLogDailyMissionCommandRepositoryAdapter.java b/src/main/java/com/ject/studytrip/studylog/infra/querydsl/StudyLogDailyMissionCommandRepositoryAdapter.java deleted file mode 100644 index ea0f1d6..0000000 --- a/src/main/java/com/ject/studytrip/studylog/infra/querydsl/StudyLogDailyMissionCommandRepositoryAdapter.java +++ /dev/null @@ -1,69 +0,0 @@ -package com.ject.studytrip.studylog.infra.querydsl; - -import static com.ject.studytrip.mission.domain.model.QDailyMission.dailyMission; -import static com.ject.studytrip.studylog.domain.model.QStudyLog.studyLog; -import static com.ject.studytrip.studylog.domain.model.QStudyLogDailyMission.studyLogDailyMission; - -import com.ject.studytrip.studylog.domain.repository.StudyLogDailyMissionCommandRepository; -import com.querydsl.jpa.JPAExpressions; -import com.querydsl.jpa.impl.JPAQueryFactory; -import java.util.List; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Repository; - -@Repository -@RequiredArgsConstructor -public class StudyLogDailyMissionCommandRepositoryAdapter - implements StudyLogDailyMissionCommandRepository { - private final JPAQueryFactory queryFactory; - - @Override - public long deleteAllByDeletedAtIsNotNull() { - return queryFactory - .delete(studyLogDailyMission) - .where(studyLogDailyMission.deletedAt.isNotNull()) - .execute(); - } - - @Override - public long deleteAllByDeletedDailyMissionOwner() { - return queryFactory - .delete(studyLogDailyMission) - .where( - studyLogDailyMission.dailyMission.id.in( - JPAExpressions.select(dailyMission.id) - .from(dailyMission) - .where(dailyMission.deletedAt.isNotNull()))) - .execute(); - } - - @Override - public long deleteAllByDeletedStudyLogOwner() { - return queryFactory - .delete(studyLogDailyMission) - .where( - studyLogDailyMission.studyLog.id.in( - JPAExpressions.select(studyLog.id) - .from(studyLog) - .where(studyLog.deletedAt.isNotNull()))) - .execute(); - } - - @Override - public long deleteAllByMemberId(Long memberId) { - List ids = - queryFactory - .select(studyLogDailyMission.id) - .from(studyLogDailyMission) - .join(studyLogDailyMission.studyLog, studyLog) - .where(studyLog.member.id.eq(memberId)) - .fetch(); - - if (ids.isEmpty()) return 0; - - return queryFactory - .delete(studyLogDailyMission) - .where(studyLogDailyMission.id.in(ids)) - .execute(); - } -} diff --git a/src/main/java/com/ject/studytrip/studylog/infra/querydsl/StudyLogDailyMissionQueryRepositoryAdapter.java b/src/main/java/com/ject/studytrip/studylog/infra/querydsl/StudyLogDailyMissionQueryRepositoryAdapter.java deleted file mode 100644 index 23bb7a4..0000000 --- a/src/main/java/com/ject/studytrip/studylog/infra/querydsl/StudyLogDailyMissionQueryRepositoryAdapter.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.ject.studytrip.studylog.infra.querydsl; - -import static com.ject.studytrip.mission.domain.model.QDailyMission.dailyMission; -import static com.ject.studytrip.mission.domain.model.QMission.mission; -import static com.ject.studytrip.studylog.domain.model.QStudyLogDailyMission.studyLogDailyMission; - -import com.ject.studytrip.studylog.domain.model.StudyLogDailyMission; -import com.ject.studytrip.studylog.domain.repository.StudyLogDailyMissionQueryRepository; -import com.querydsl.jpa.impl.JPAQueryFactory; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Repository; - -@Repository -@RequiredArgsConstructor -public class StudyLogDailyMissionQueryRepositoryAdapter - implements StudyLogDailyMissionQueryRepository { - private final JPAQueryFactory queryFactory; - - @Override - public Map> findStudyLogDailyMissionsGroupedByStudyLogId( - List studyLogIds) { - return queryFactory - .selectFrom(studyLogDailyMission) - .join(studyLogDailyMission.dailyMission, dailyMission) - .fetchJoin() - .join(dailyMission.mission, mission) - .fetchJoin() - .where(studyLogDailyMission.studyLog.id.in(studyLogIds)) - .fetch() - .stream() - .collect(Collectors.groupingBy(sldm -> sldm.getStudyLog().getId())); - } -} diff --git a/src/main/java/com/ject/studytrip/studylog/infra/querydsl/StudyLogQueryRepositoryAdapter.java b/src/main/java/com/ject/studytrip/studylog/infra/querydsl/StudyLogQueryRepositoryAdapter.java deleted file mode 100644 index b0f2e71..0000000 --- a/src/main/java/com/ject/studytrip/studylog/infra/querydsl/StudyLogQueryRepositoryAdapter.java +++ /dev/null @@ -1,123 +0,0 @@ -package com.ject.studytrip.studylog.infra.querydsl; - -import static com.ject.studytrip.studylog.domain.model.QStudyLog.studyLog; -import static com.ject.studytrip.trip.domain.model.QDailyGoal.dailyGoal; -import static com.ject.studytrip.trip.domain.model.QTripReportStudyLog.tripReportStudyLog; - -import com.ject.studytrip.studylog.domain.model.StudyLog; -import com.ject.studytrip.studylog.domain.repository.StudyLogQueryRepository; -import com.querydsl.core.types.OrderSpecifier; -import com.querydsl.jpa.impl.JPAQueryFactory; -import java.util.List; -import java.util.Optional; -import lombok.RequiredArgsConstructor; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Slice; -import org.springframework.data.domain.SliceImpl; -import org.springframework.stereotype.Repository; - -@Repository -@RequiredArgsConstructor -public class StudyLogQueryRepositoryAdapter implements StudyLogQueryRepository { - private final JPAQueryFactory queryFactory; - - @Override - public Slice findSliceByTripId(Long tripId, Pageable pageable, String order) { - List content = - queryFactory - .selectFrom(studyLog) - .join(studyLog.dailyGoal, dailyGoal) - .where(dailyGoal.trip.id.eq(tripId), dailyGoal.deletedAt.isNull()) - .offset(pageable.getOffset()) - .limit(pageable.getPageSize() + 1) - .orderBy(orderSpecifiers(order)) - .fetch(); - - List result = content; - boolean hasNext = content.size() > pageable.getPageSize(); - if (hasNext) { - result = content.subList(0, pageable.getPageSize()); - } - - return new SliceImpl<>(result, pageable, hasNext); - } - - @Override - public Slice findSliceByTripReportIdOrderByCreatedAtDesc( - Long tripReportId, Pageable pageable) { - List content = - queryFactory - .select(studyLog) - .from(tripReportStudyLog) - .join(tripReportStudyLog.studyLog, studyLog) - .where( - tripReportStudyLog.tripReport.id.eq(tripReportId), - studyLog.deletedAt.isNull()) - .orderBy(studyLog.createdAt.desc()) - .offset(pageable.getOffset()) - .limit(pageable.getPageSize() + 1) - .fetch(); - - boolean hasNext = content.size() > pageable.getPageSize(); - List result = hasNext ? content.subList(0, pageable.getPageSize()) : content; - - return new SliceImpl<>(result, pageable, hasNext); - } - - @Override - public List findAllIdsByTripIdOrderByCreatedDesc(Long tripId) { - return queryFactory - .select(studyLog.id) - .from(studyLog) - .join(studyLog.dailyGoal, dailyGoal) - .where( - dailyGoal.trip.id.eq(tripId), - studyLog.deletedAt.isNull(), - dailyGoal.deletedAt.isNull()) - .orderBy(studyLog.createdAt.desc()) - .fetch(); - } - - @Override - public List findImageUrlsByMemberId(Long memberId) { - return queryFactory - .select(studyLog.imageUrl) - .from(studyLog) - .where(studyLog.member.id.eq(memberId)) - .fetch(); - } - - @Override - public long countStudyLogsByTripId(Long tripId) { - Long count = - queryFactory - .select(studyLog.count()) - .from(studyLog) - .join(studyLog.dailyGoal, dailyGoal) - .where( - dailyGoal.trip.id.eq(tripId), - studyLog.deletedAt.isNull(), - dailyGoal.deletedAt.isNull()) - .fetchOne(); - - return count == null ? 0L : count; - } - - @Override - public long countActiveStudyLogsByMemberId(Long memberId) { - Long count = - queryFactory - .select(studyLog.count()) - .from(studyLog) - .where(studyLog.member.id.eq(memberId).and(studyLog.deletedAt.isNull())) - .fetchOne(); - - return Optional.ofNullable(count).orElse(0L); - } - - private OrderSpecifier[] orderSpecifiers(String order) { - return (order.equalsIgnoreCase("OLDEST")) - ? new OrderSpecifier[] {studyLog.createdAt.asc(), studyLog.id.asc()} - : new OrderSpecifier[] {studyLog.createdAt.desc(), studyLog.id.desc()}; - } -} diff --git a/src/main/java/com/ject/studytrip/trip/domain/model/DailyGoal.java b/src/main/java/com/ject/studytrip/trip/domain/model/DailyGoal.java deleted file mode 100644 index 7bdaa5a..0000000 --- a/src/main/java/com/ject/studytrip/trip/domain/model/DailyGoal.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.ject.studytrip.trip.domain.model; - -import com.ject.studytrip.global.common.entity.BaseTimeEntity; -import jakarta.persistence.*; -import java.time.LocalDateTime; -import lombok.*; - -@Entity -@NoArgsConstructor(access = AccessLevel.PROTECTED) -@AllArgsConstructor(access = AccessLevel.PRIVATE) -@Builder(access = AccessLevel.PRIVATE) -public class DailyGoal extends BaseTimeEntity { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "trip_id", nullable = false) - private Trip trip; - - @Column(nullable = false) - private String title; - - private boolean completed; - - public static DailyGoal of(Trip trip, String title) { - return DailyGoal.builder().trip(trip).title(title).completed(false).build(); - } - - public void updateDeletedAt() { - this.deletedAt = LocalDateTime.now(); - } - - public void updateCompleted() { - this.completed = true; - } - - public boolean isCompleted() { - return completed; - } - - public Long getId() { - return id; - } - - public String getTitle() { - return title; - } - - public Trip getTrip() { - return trip; - } -} diff --git a/src/main/java/com/ject/studytrip/trip/domain/model/Trip.java b/src/main/java/com/ject/studytrip/trip/domain/model/Trip.java deleted file mode 100644 index 8cf4a86..0000000 --- a/src/main/java/com/ject/studytrip/trip/domain/model/Trip.java +++ /dev/null @@ -1,146 +0,0 @@ -package com.ject.studytrip.trip.domain.model; - -import static org.springframework.util.StringUtils.hasText; - -import com.ject.studytrip.global.common.entity.BaseTimeEntity; -import com.ject.studytrip.member.domain.model.Member; -import jakarta.persistence.*; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.util.Objects; -import lombok.*; - -@Entity -@NoArgsConstructor(access = AccessLevel.PROTECTED) -@AllArgsConstructor(access = AccessLevel.PRIVATE) -@Builder(access = AccessLevel.PRIVATE) -public class Trip extends BaseTimeEntity { - public TripCategory getCategory() { - return category; - } - - public boolean isCompleted() { - return completed; - } - - public int getCompletedStamps() { - return completedStamps; - } - - public LocalDate getEndDate() { - return endDate; - } - - public Long getId() { - return id; - } - - public Member getMember() { - return member; - } - - public String getMemo() { - return memo; - } - - public String getName() { - return name; - } - - public LocalDate getStartDate() { - return startDate; - } - - public int getTotalStamps() { - return totalStamps; - } - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "member_id", nullable = false) - private Member member; - - @Column(nullable = false) - private String name; - - private String memo; - - @Enumerated(EnumType.STRING) - @Column(nullable = false) - private TripCategory category; - - @Column(nullable = false) - private LocalDate startDate; - - private LocalDate endDate; - - private int totalStamps; - - private int completedStamps; - - private boolean completed; - - public static Trip of( - Member member, - String name, - String memo, - TripCategory category, - LocalDate endDate, - int totalStamps) { - return Trip.builder() - .member(member) - .name(name) - .memo(memo) - .category(category) - .startDate(LocalDate.now()) - .endDate(endDate) - .totalStamps(totalStamps) - .completedStamps(0) - .completed(false) - .build(); - } - - public void update(String name, String memo, TripCategory category, LocalDate endDate) { - if (hasText(name)) this.name = name; - if (hasText(memo)) this.memo = memo; - if (Objects.nonNull(category)) this.category = category; - if (Objects.nonNull(endDate)) this.endDate = endDate; - } - - // public void updateName(String name) { - // if (hasText(name)) this.name = name; - // } - // - // public void updateMemo(String memo) { if (hasText(memo)) this.memo = memo; } - // - // public void updateCategory(TripCategory category) { - // if (Objects.nonNull(category)) this.category = category; - // } - // - // public void updateDeadline(LocalDate deadline) { - // if (Objects.nonNull(deadline)) this.deadline = deadline; - // } - - public void increaseTotalStamps() { - this.totalStamps += 1; - } - - public void decreaseTotalStamps() { - this.totalStamps -= 1; - } - - public void updateCompleted() { - this.completed = true; - } - - public void updateDeletedAt() { - this.deletedAt = LocalDateTime.now(); - } - - public void increaseCompletedStamps() { - this.completedStamps += 1; - } -} diff --git a/src/main/java/com/ject/studytrip/trip/domain/model/TripCategory.java b/src/main/java/com/ject/studytrip/trip/domain/model/TripCategory.java deleted file mode 100644 index 8031da4..0000000 --- a/src/main/java/com/ject/studytrip/trip/domain/model/TripCategory.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.ject.studytrip.trip.domain.model; - -import com.ject.studytrip.global.exception.CustomException; -import com.ject.studytrip.trip.domain.error.TripErrorCode; -import java.util.Arrays; -import lombok.RequiredArgsConstructor; - -@RequiredArgsConstructor -public enum TripCategory { - COURSE("코스형"), - EXPLORE("탐험형"), - ; - - private final String value; - - public String getValue() { - return this.value; - } - - public static TripCategory from(String name) { - return Arrays.stream(TripCategory.values()) - .filter(category -> category.name().equalsIgnoreCase(name)) - .findFirst() - .orElseThrow(() -> new CustomException(TripErrorCode.INVALID_TRIP_CATEGORY)); - } -} diff --git a/src/main/java/com/ject/studytrip/trip/domain/model/TripReport.java b/src/main/java/com/ject/studytrip/trip/domain/model/TripReport.java deleted file mode 100644 index bf9966f..0000000 --- a/src/main/java/com/ject/studytrip/trip/domain/model/TripReport.java +++ /dev/null @@ -1,123 +0,0 @@ -package com.ject.studytrip.trip.domain.model; - -import static org.flywaydb.core.internal.util.StringUtils.hasText; - -import com.ject.studytrip.global.common.entity.BaseTimeEntity; -import com.ject.studytrip.member.domain.model.Member; -import jakarta.persistence.*; -import java.time.LocalDateTime; -import lombok.*; - -@Entity -@NoArgsConstructor(access = AccessLevel.PROTECTED) -@AllArgsConstructor(access = AccessLevel.PRIVATE) -@Builder(access = AccessLevel.PRIVATE) -public class TripReport extends BaseTimeEntity { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @Column private String title; - - @Column(nullable = false) - private String content; - - @Column(nullable = false) - private String startDate; - - private String endDate; - - @Column(nullable = false) - private long studyLogCount; - - @Column(nullable = false) - private long totalFocusHours; - - @Column(nullable = false) - private long studyDays; - - private String imageTitle; - - private String imageUrl; - - @ManyToOne(fetch = FetchType.LAZY, optional = false) - @JoinColumn(name = "member_id", nullable = false) - private Member member; - - public static TripReport of( - Member member, - String title, - String content, - String startDate, - String endDate, - long studyLogCount, - long totalFocusHours, - long studyDays, - String imageTitle) { - return TripReport.builder() - .title(title) - .content(content) - .startDate(startDate) - .endDate(endDate) - .studyLogCount(studyLogCount) - .totalFocusHours(totalFocusHours) - .studyDays(studyDays) - .imageTitle(imageTitle) - .imageUrl(null) - .member(member) - .build(); - } - - public void updateImageUrl(String imageUrl) { - if (hasText(imageUrl)) this.imageUrl = imageUrl; - } - - public void updateDeletedAt() { - this.deletedAt = LocalDateTime.now(); - } - - public long getTotalFocusHours() { - return totalFocusHours; - } - - public String getTitle() { - return title; - } - - public long getStudyLogCount() { - return studyLogCount; - } - - public long getStudyDays() { - return studyDays; - } - - public String getStartDate() { - return startDate; - } - - public Member getMember() { - return member; - } - - public String getImageUrl() { - return imageUrl; - } - - public String getImageTitle() { - return imageTitle; - } - - public Long getId() { - return id; - } - - public String getEndDate() { - return endDate; - } - - public String getContent() { - return content; - } -} diff --git a/src/main/java/com/ject/studytrip/trip/domain/model/TripReportStudyLog.java b/src/main/java/com/ject/studytrip/trip/domain/model/TripReportStudyLog.java deleted file mode 100644 index c37e979..0000000 --- a/src/main/java/com/ject/studytrip/trip/domain/model/TripReportStudyLog.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.ject.studytrip.trip.domain.model; - -import static jakarta.persistence.FetchType.LAZY; - -import com.ject.studytrip.global.common.entity.BaseTimeEntity; -import com.ject.studytrip.studylog.domain.model.StudyLog; -import jakarta.persistence.*; -import lombok.*; - -@Entity -@NoArgsConstructor(access = AccessLevel.PROTECTED) -@AllArgsConstructor(access = AccessLevel.PRIVATE) -@Builder(access = AccessLevel.PRIVATE) -public class TripReportStudyLog extends BaseTimeEntity { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @ManyToOne(fetch = LAZY, optional = false) - @JoinColumn(name = "trip_report_id") - private TripReport tripReport; - - @ManyToOne(fetch = LAZY, optional = false) - @JoinColumn(name = "study_log_id") - private StudyLog studyLog; - - public static TripReportStudyLog of(TripReport tripReport, StudyLog studyLog) { - return TripReportStudyLog.builder().tripReport(tripReport).studyLog(studyLog).build(); - } - - public Long getId() { - return id; - } - - public StudyLog getStudyLog() { - return studyLog; - } - - public TripReport getTripReport() { - return tripReport; - } -} diff --git a/src/main/java/com/ject/studytrip/trip/domain/repository/DailyGoalCommandRepository.java b/src/main/java/com/ject/studytrip/trip/domain/repository/DailyGoalCommandRepository.java deleted file mode 100644 index d20660c..0000000 --- a/src/main/java/com/ject/studytrip/trip/domain/repository/DailyGoalCommandRepository.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.ject.studytrip.trip.domain.repository; - -public interface DailyGoalCommandRepository { - long deleteAllByDeletedAtIsNotNull(); - - long deleteAllByDeletedTripOwner(); - - long deleteAllByMemberId(Long memberId); -} diff --git a/src/main/java/com/ject/studytrip/trip/domain/repository/TripCommandRepository.java b/src/main/java/com/ject/studytrip/trip/domain/repository/TripCommandRepository.java deleted file mode 100644 index 245146a..0000000 --- a/src/main/java/com/ject/studytrip/trip/domain/repository/TripCommandRepository.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.ject.studytrip.trip.domain.repository; - -public interface TripCommandRepository { - long deleteAllByDeletedAtIsNotNull(); - - long deleteAllByDeletedMemberOwner(); - - long deleteAllByMemberId(Long memberId); -} diff --git a/src/main/java/com/ject/studytrip/trip/domain/repository/TripQueryRepository.java b/src/main/java/com/ject/studytrip/trip/domain/repository/TripQueryRepository.java deleted file mode 100644 index 20b4e95..0000000 --- a/src/main/java/com/ject/studytrip/trip/domain/repository/TripQueryRepository.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.ject.studytrip.trip.domain.repository; - -import com.ject.studytrip.trip.domain.model.Trip; -import com.ject.studytrip.trip.domain.model.TripCategory; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Slice; - -public interface TripQueryRepository { - Slice findSliceByMemberIdAndCompletedFalseAndDeletedAtIsNull( - Long memberId, Pageable pageable); - - long countActiveTripsByMemberIdAndCategory(Long memberId, TripCategory category); -} diff --git a/src/main/java/com/ject/studytrip/trip/domain/repository/TripReportCommandRepository.java b/src/main/java/com/ject/studytrip/trip/domain/repository/TripReportCommandRepository.java deleted file mode 100644 index a8162b3..0000000 --- a/src/main/java/com/ject/studytrip/trip/domain/repository/TripReportCommandRepository.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.ject.studytrip.trip.domain.repository; - -public interface TripReportCommandRepository { - long deleteAllByDeletedAtIsNotNull(); - - long deleteAllByDeletedMemberOwner(); - - long deleteAllByMemberId(Long memberId); -} diff --git a/src/main/java/com/ject/studytrip/trip/domain/repository/TripReportQueryRepository.java b/src/main/java/com/ject/studytrip/trip/domain/repository/TripReportQueryRepository.java deleted file mode 100644 index 075c91f..0000000 --- a/src/main/java/com/ject/studytrip/trip/domain/repository/TripReportQueryRepository.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.ject.studytrip.trip.domain.repository; - -import com.ject.studytrip.trip.domain.model.TripReport; -import java.util.List; - -public interface TripReportQueryRepository { - List findAllActiveByMemberId(Long memberId); - - List findImageUrlsByMemberId(Long memberId); -} diff --git a/src/main/java/com/ject/studytrip/trip/domain/repository/TripReportStudyLogCommandRepository.java b/src/main/java/com/ject/studytrip/trip/domain/repository/TripReportStudyLogCommandRepository.java deleted file mode 100644 index b6277e6..0000000 --- a/src/main/java/com/ject/studytrip/trip/domain/repository/TripReportStudyLogCommandRepository.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.ject.studytrip.trip.domain.repository; - -public interface TripReportStudyLogCommandRepository { - long deleteAllByDeletedMemberOwner(); - - long deleteAllByMemberId(Long memberId); -} diff --git a/src/main/java/com/ject/studytrip/trip/infra/querydsl/DailyGoalCommandRepositoryAdapter.java b/src/main/java/com/ject/studytrip/trip/infra/querydsl/DailyGoalCommandRepositoryAdapter.java deleted file mode 100644 index 921ef58..0000000 --- a/src/main/java/com/ject/studytrip/trip/infra/querydsl/DailyGoalCommandRepositoryAdapter.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.ject.studytrip.trip.infra.querydsl; - -import static com.ject.studytrip.trip.domain.model.QDailyGoal.dailyGoal; -import static com.ject.studytrip.trip.domain.model.QTrip.trip; - -import com.ject.studytrip.trip.domain.repository.DailyGoalCommandRepository; -import com.querydsl.jpa.JPAExpressions; -import com.querydsl.jpa.impl.JPAQueryFactory; -import java.util.List; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Repository; - -@Repository -@RequiredArgsConstructor -public class DailyGoalCommandRepositoryAdapter implements DailyGoalCommandRepository { - private final JPAQueryFactory queryFactory; - - @Override - public long deleteAllByDeletedAtIsNotNull() { - return queryFactory.delete(dailyGoal).where(dailyGoal.deletedAt.isNotNull()).execute(); - } - - @Override - public long deleteAllByDeletedTripOwner() { - return queryFactory - .delete(dailyGoal) - .where( - dailyGoal.trip.id.in( - JPAExpressions.select(trip.id) - .from(trip) - .where(trip.deletedAt.isNotNull()))) - .execute(); - } - - @Override - public long deleteAllByMemberId(Long memberId) { - List ids = - queryFactory - .select(dailyGoal.id) - .from(dailyGoal) - .join(dailyGoal.trip, trip) - .where(trip.member.id.eq(memberId)) - .fetch(); - - if (ids.isEmpty()) return 0; - - return queryFactory.delete(dailyGoal).where(dailyGoal.id.in(ids)).execute(); - } -} diff --git a/src/main/java/com/ject/studytrip/trip/infra/querydsl/TripCommandRepositoryAdapter.java b/src/main/java/com/ject/studytrip/trip/infra/querydsl/TripCommandRepositoryAdapter.java deleted file mode 100644 index f9757f2..0000000 --- a/src/main/java/com/ject/studytrip/trip/infra/querydsl/TripCommandRepositoryAdapter.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.ject.studytrip.trip.infra.querydsl; - -import static com.ject.studytrip.member.domain.model.QMember.member; -import static com.ject.studytrip.trip.domain.model.QTrip.trip; - -import com.ject.studytrip.trip.domain.repository.TripCommandRepository; -import com.querydsl.jpa.JPAExpressions; -import com.querydsl.jpa.impl.JPAQueryFactory; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Repository; - -@Repository -@RequiredArgsConstructor -public class TripCommandRepositoryAdapter implements TripCommandRepository { - private final JPAQueryFactory queryFactory; - - @Override - public long deleteAllByDeletedAtIsNotNull() { - return queryFactory.delete(trip).where(trip.deletedAt.isNotNull()).execute(); - } - - @Override - public long deleteAllByDeletedMemberOwner() { - return queryFactory - .delete(trip) - .where( - trip.member.id.in( - JPAExpressions.select(member.id) - .from(member) - .where(member.deletedAt.isNotNull()))) - .execute(); - } - - @Override - public long deleteAllByMemberId(Long memberId) { - return queryFactory.delete(trip).where(trip.member.id.eq(memberId)).execute(); - } -} diff --git a/src/main/java/com/ject/studytrip/trip/infra/querydsl/TripQueryRepositoryAdapter.java b/src/main/java/com/ject/studytrip/trip/infra/querydsl/TripQueryRepositoryAdapter.java deleted file mode 100644 index d43e319..0000000 --- a/src/main/java/com/ject/studytrip/trip/infra/querydsl/TripQueryRepositoryAdapter.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.ject.studytrip.trip.infra.querydsl; - -import static com.ject.studytrip.trip.domain.model.QTrip.trip; - -import com.ject.studytrip.trip.domain.model.Trip; -import com.ject.studytrip.trip.domain.model.TripCategory; -import com.ject.studytrip.trip.domain.repository.TripQueryRepository; -import com.querydsl.jpa.impl.JPAQueryFactory; -import java.util.List; -import java.util.Optional; -import lombok.RequiredArgsConstructor; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Slice; -import org.springframework.data.domain.SliceImpl; -import org.springframework.stereotype.Repository; - -@Repository -@RequiredArgsConstructor -public class TripQueryRepositoryAdapter implements TripQueryRepository { - private final JPAQueryFactory queryFactory; - - @Override - public Slice findSliceByMemberIdAndCompletedFalseAndDeletedAtIsNull( - Long memberId, Pageable pageable) { - List content = - queryFactory - .selectFrom(trip) - .where( - trip.member.id.eq(memberId), - trip.completed.isFalse(), - trip.deletedAt.isNull()) - .offset(pageable.getOffset()) - .limit(pageable.getPageSize() + 1) - .fetch(); - - List result = content; - boolean hasNext = content.size() > pageable.getPageSize(); - if (hasNext) { - result = content.subList(0, pageable.getPageSize()); - } - - return new SliceImpl<>(result, pageable, hasNext); - } - - @Override - public long countActiveTripsByMemberIdAndCategory(Long memberId, TripCategory category) { - Long count = - queryFactory - .select(trip.count()) - .from(trip) - .where( - trip.member.id.eq(memberId), - trip.deletedAt.isNull(), - trip.category.eq(category)) - .fetchOne(); - - return Optional.ofNullable(count).orElse(0L); - } -} diff --git a/src/main/java/com/ject/studytrip/trip/infra/querydsl/TripReportCommandRepositoryAdapter.java b/src/main/java/com/ject/studytrip/trip/infra/querydsl/TripReportCommandRepositoryAdapter.java deleted file mode 100644 index e1ae9c0..0000000 --- a/src/main/java/com/ject/studytrip/trip/infra/querydsl/TripReportCommandRepositoryAdapter.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.ject.studytrip.trip.infra.querydsl; - -import static com.ject.studytrip.member.domain.model.QMember.member; -import static com.ject.studytrip.trip.domain.model.QTripReport.tripReport; - -import com.ject.studytrip.trip.domain.repository.TripReportCommandRepository; -import com.querydsl.jpa.JPAExpressions; -import com.querydsl.jpa.impl.JPAQueryFactory; -import java.util.List; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Repository; - -@Repository -@RequiredArgsConstructor -public class TripReportCommandRepositoryAdapter implements TripReportCommandRepository { - private final JPAQueryFactory queryFactory; - - @Override - public long deleteAllByDeletedAtIsNotNull() { - return queryFactory.delete(tripReport).where(tripReport.deletedAt.isNotNull()).execute(); - } - - @Override - public long deleteAllByDeletedMemberOwner() { - return queryFactory - .delete(tripReport) - .where( - tripReport.member.id.in( - JPAExpressions.select(member.id) - .from(member) - .where(member.deletedAt.isNotNull()))) - .execute(); - } - - @Override - public long deleteAllByMemberId(Long memberId) { - List ids = - queryFactory - .select(tripReport.id) - .from(tripReport) - .where(tripReport.member.id.eq(memberId)) - .fetch(); - - if (ids.isEmpty()) return 0; - - return queryFactory.delete(tripReport).where(tripReport.id.in(ids)).execute(); - } -} diff --git a/src/main/java/com/ject/studytrip/trip/infra/querydsl/TripReportQueryRepositoryAdapter.java b/src/main/java/com/ject/studytrip/trip/infra/querydsl/TripReportQueryRepositoryAdapter.java deleted file mode 100644 index a55a889..0000000 --- a/src/main/java/com/ject/studytrip/trip/infra/querydsl/TripReportQueryRepositoryAdapter.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.ject.studytrip.trip.infra.querydsl; - -import static com.ject.studytrip.trip.domain.model.QTripReport.tripReport; - -import com.ject.studytrip.trip.domain.model.TripReport; -import com.ject.studytrip.trip.domain.repository.TripReportQueryRepository; -import com.querydsl.jpa.impl.JPAQueryFactory; -import java.util.List; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Repository; - -@Repository -@RequiredArgsConstructor -public class TripReportQueryRepositoryAdapter implements TripReportQueryRepository { - private final JPAQueryFactory queryFactory; - - @Override - public List findAllActiveByMemberId(Long memberId) { - return queryFactory - .selectFrom(tripReport) - .where(tripReport.member.id.eq(memberId), tripReport.deletedAt.isNull()) - .orderBy(tripReport.createdAt.desc()) - .fetch(); - } - - @Override - public List findImageUrlsByMemberId(Long memberId) { - return queryFactory - .select(tripReport.imageUrl) - .from(tripReport) - .where(tripReport.member.id.eq(memberId)) - .fetch(); - } -} diff --git a/src/main/java/com/ject/studytrip/trip/infra/querydsl/TripReportStudyLogCommandRepositoryAdapter.java b/src/main/java/com/ject/studytrip/trip/infra/querydsl/TripReportStudyLogCommandRepositoryAdapter.java deleted file mode 100644 index f0af492..0000000 --- a/src/main/java/com/ject/studytrip/trip/infra/querydsl/TripReportStudyLogCommandRepositoryAdapter.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.ject.studytrip.trip.infra.querydsl; - -import static com.ject.studytrip.member.domain.model.QMember.member; -import static com.ject.studytrip.studylog.domain.model.QStudyLog.studyLog; -import static com.ject.studytrip.trip.domain.model.QTripReport.tripReport; -import static com.ject.studytrip.trip.domain.model.QTripReportStudyLog.tripReportStudyLog; - -import com.ject.studytrip.trip.domain.repository.TripReportStudyLogCommandRepository; -import com.querydsl.jpa.JPAExpressions; -import com.querydsl.jpa.impl.JPAQueryFactory; -import java.util.List; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Repository; - -@Repository -@RequiredArgsConstructor -public class TripReportStudyLogCommandRepositoryAdapter - implements TripReportStudyLogCommandRepository { - private final JPAQueryFactory queryFactory; - - @Override - public long deleteAllByDeletedMemberOwner() { - return queryFactory - .delete(tripReportStudyLog) - .where( - tripReportStudyLog - .tripReport - .id - .in( - JPAExpressions.select(tripReport.id) - .from(tripReport) - .join(tripReport.member, member) - .where(member.deletedAt.isNotNull())) - .or( - tripReportStudyLog.studyLog.id.in( - JPAExpressions.select(studyLog.id) - .from(studyLog) - .join(studyLog.member, member) - .where(member.deletedAt.isNotNull())))) - .execute(); - } - - @Override - public long deleteAllByMemberId(Long memberId) { - List ids = - queryFactory - .select(tripReportStudyLog.id) - .from(tripReportStudyLog) - .join(tripReportStudyLog.tripReport, tripReport) - .where(tripReport.member.id.eq(memberId)) - .fetch(); - - if (ids.isEmpty()) return 0; - - return queryFactory - .delete(tripReportStudyLog) - .where(tripReportStudyLog.id.in(ids)) - .execute(); - } -} diff --git a/src/main/kotlin/com/ject/studytrip/StudytripApplication.kt b/src/main/kotlin/com/ject/studytrip/StudytripApplication.kt new file mode 100644 index 0000000..dfb6842 --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/StudytripApplication.kt @@ -0,0 +1,11 @@ +package com.ject.studytrip + +import org.springframework.boot.autoconfigure.SpringBootApplication +import org.springframework.boot.runApplication + +@SpringBootApplication +class StudytripApplication + +fun main(args: Array) { + runApplication(*args) +} diff --git a/src/main/kotlin/com/ject/studytrip/auth/application/dto/OAuthLoginOutcome.kt b/src/main/kotlin/com/ject/studytrip/auth/application/dto/OAuthLoginOutcome.kt index ead8532..b619cbc 100644 --- a/src/main/kotlin/com/ject/studytrip/auth/application/dto/OAuthLoginOutcome.kt +++ b/src/main/kotlin/com/ject/studytrip/auth/application/dto/OAuthLoginOutcome.kt @@ -10,14 +10,12 @@ sealed class OAuthLoginOutcome { ) : OAuthLoginOutcome() companion object { - @JvmStatic fun success( accessToken: String, refreshToken: String, refreshTokenExpiresIn: Long, ): OAuthLoginOutcome = Success(TokenInfo(accessToken, refreshToken, refreshTokenExpiresIn)) - @JvmStatic fun signupRequired(signupKey: String): OAuthLoginOutcome = SignupRequired(signupKey) } } diff --git a/src/main/kotlin/com/ject/studytrip/auth/application/facade/AuthFacade.kt b/src/main/kotlin/com/ject/studytrip/auth/application/facade/AuthFacade.kt index ef42c95..4a7f888 100644 --- a/src/main/kotlin/com/ject/studytrip/auth/application/facade/AuthFacade.kt +++ b/src/main/kotlin/com/ject/studytrip/auth/application/facade/AuthFacade.kt @@ -9,6 +9,7 @@ import com.ject.studytrip.auth.infra.dto.KakaoUserInfoResponse import com.ject.studytrip.auth.presentation.dto.request.KakaoLoginRequest import com.ject.studytrip.auth.presentation.dto.request.KakaoSignupRequest import com.ject.studytrip.auth.presentation.dto.request.LogoutRequest +import com.ject.studytrip.global.util.EntityExtensions.requireId import com.ject.studytrip.member.application.dto.CreateMemberCommand import com.ject.studytrip.member.application.service.MemberCommandService import com.ject.studytrip.member.application.service.MemberQueryService @@ -28,7 +29,7 @@ class AuthFacade( request: KakaoSignupRequest, ): TokenInfo { val profile = kakaoSignupProfileService.getSignupProfileByKey(signupKey) - val command = CreateMemberCommand.of(profile.socialId, profile.email, profile.profileImageUrl, request.nickname, request.category) + val command = CreateMemberCommand(profile.socialId, profile.email, profile.profileImageUrl, request.nickname, request.category) val member = memberCommandService.createMemberFromKakao(command) kakaoSignupProfileService.deleteBySignupKey(signupKey) @@ -45,7 +46,7 @@ class AuthFacade( return memberQueryService .getMemberBySocialProviderAndSocialId(SocialProvider.KAKAO, info.kakaoId) .orElse(null) - ?.let { member -> createLoginOutcomeWithIssuedTokens(member.id, member.role.name) } // 가입되어 있는 사용자인 경우 토큰 발급 + ?.let { member -> createLoginOutcomeWithIssuedTokens(member.id.requireId(), member.role.name) } // 가입되어 있는 사용자인 경우 토큰 발급 ?: createSignupRequiredOutcomeWithIssuedSignupKey(info) // 가입이 필요할 경우 가입 키 발급 } diff --git a/src/main/kotlin/com/ject/studytrip/auth/domain/error/AuthErrorCode.kt b/src/main/kotlin/com/ject/studytrip/auth/domain/error/AuthErrorCode.kt index 9f5b7ba..a60a857 100644 --- a/src/main/kotlin/com/ject/studytrip/auth/domain/error/AuthErrorCode.kt +++ b/src/main/kotlin/com/ject/studytrip/auth/domain/error/AuthErrorCode.kt @@ -4,8 +4,8 @@ import com.ject.studytrip.global.exception.error.ErrorCode import org.springframework.http.HttpStatus enum class AuthErrorCode( - private val status: HttpStatus, - private val message: String, + override val status: HttpStatus, + override val message: String, ) : ErrorCode { // 400 INVALID_KAKAO_AUTHORIZATION_CODE(HttpStatus.BAD_REQUEST, "잘못된 카카오 인가 코드입니다."), @@ -27,11 +27,4 @@ enum class AuthErrorCode( // 502 KAKAO_SERVER_ERROR(HttpStatus.BAD_GATEWAY, "카카오 서버에서 오류가 발생했습니다."), - ; - - override fun getName(): String = name - - override fun getStatus(): HttpStatus = status - - override fun getMessage(): String = message } diff --git a/src/main/kotlin/com/ject/studytrip/auth/presentation/dto/response/LoginResponse.kt b/src/main/kotlin/com/ject/studytrip/auth/presentation/dto/response/LoginResponse.kt index 9ea2b80..55dad2c 100644 --- a/src/main/kotlin/com/ject/studytrip/auth/presentation/dto/response/LoginResponse.kt +++ b/src/main/kotlin/com/ject/studytrip/auth/presentation/dto/response/LoginResponse.kt @@ -9,10 +9,8 @@ data class LoginResponse( val accessToken: String?, ) { companion object { - @JvmStatic fun success(accessToken: String): LoginResponse = LoginResponse(false, accessToken) - @JvmStatic fun requiredSignup(): LoginResponse = LoginResponse(true, null) } } diff --git a/src/main/kotlin/com/ject/studytrip/cleanup/application/facade/HardDeleteFacade.kt b/src/main/kotlin/com/ject/studytrip/cleanup/application/facade/HardDeleteFacade.kt index 5cfde3e..062f592 100644 --- a/src/main/kotlin/com/ject/studytrip/cleanup/application/facade/HardDeleteFacade.kt +++ b/src/main/kotlin/com/ject/studytrip/cleanup/application/facade/HardDeleteFacade.kt @@ -92,12 +92,10 @@ class HardDeleteFacade( executor.run(STUDY_LOG_DAILY_MISSIONS_OWNED_BY_DELETED_DAILY_MISSION) { studyLogDailyMissionCommandService.hardDeleteStudyLogDailyMissionsOwnedByDeletedDailyMission() } - phases[STUDY_LOG_DAILY_MISSIONS_OWNED_BY_DELETED_STUDY_LOG] = executor.run(STUDY_LOG_DAILY_MISSIONS_OWNED_BY_DELETED_STUDY_LOG) { studyLogDailyMissionCommandService.hardDeleteStudyLogDailyMissionsOwnedByDeletedStudyLog() } - phases[STUDY_LOG_DAILY_MISSIONS] = executor.run(STUDY_LOG_DAILY_MISSIONS) { studyLogDailyMissionCommandService.hardDeleteStudyLogDailyMissions() @@ -109,12 +107,10 @@ class HardDeleteFacade( executor.run(DAILY_MISSIONS_OWNED_BY_DELETED_MISSION) { dailyMissionCommandService.hardDeleteDailyMissionsOwnedByDeletedMission() } - phases[DAILY_MISSIONS_OWNED_BY_DELETED_DAILY_GOAL] = executor.run(DAILY_MISSIONS_OWNED_BY_DELETED_DAILY_GOAL) { dailyMissionCommandService.hardDeleteDailyMissionsOwnedByDeletedDailyGoal() } - phases[DAILY_MISSIONS] = executor.run(DAILY_MISSIONS) { dailyMissionCommandService.hardDeleteDailyMissions() @@ -133,7 +129,6 @@ class HardDeleteFacade( executor.run(TRIP_REPORTS_OWNED_BY_DELETED_MEMBER) { tripReportCommandService.hardDeleteTripReportsOwnedByDeletedMember() } - phases[TRIP_REPORTS] = executor.run(TRIP_REPORTS) { tripReportCommandService.hardDeleteTripReports() @@ -145,12 +140,10 @@ class HardDeleteFacade( executor.run(STUDY_LOGS_OWNED_BY_DELETED_MEMBER) { studyLogCommandService.hardDeleteStudyLogsOwnedByDeletedMember() } - phases[STUDY_LOGS_OWNED_BY_DELETED_DAILY_GOAL] = executor.run(STUDY_LOGS_OWNED_BY_DELETED_DAILY_GOAL) { studyLogCommandService.hardDeleteStudyLogsOwnedByDeletedDailyGoal() } - phases[STUDY_LOGS] = executor.run(STUDY_LOGS) { studyLogCommandService.hardDeleteStudyLogs() @@ -162,7 +155,6 @@ class HardDeleteFacade( executor.run(DAILY_GOALS_OWNED_BY_DELETED_TRIP) { dailyGoalCommandService.hardDeleteDailyGoalsOwnedByDeletedTrip() } - phases[DAILY_GOALS] = executor.run(DAILY_GOALS) { dailyGoalCommandService.hardDeleteDailyGoals() @@ -174,7 +166,6 @@ class HardDeleteFacade( executor.run(MISSIONS_OWNED_BY_DELETED_STAMP) { missionCommandService.hardDeleteMissionsOwnedByDeletedStamp() } - phases[MISSIONS] = executor.run(MISSIONS) { missionCommandService.hardDeleteMissions() @@ -186,7 +177,6 @@ class HardDeleteFacade( executor.run(STAMPS_OWNED_BY_DELETED_TRIP) { stampCommandService.hardDeleteStampsOwnedByDeletedTrip() } - phases[STAMPS] = executor.run(STAMPS) { stampCommandService.hardDeleteStamps() @@ -198,7 +188,6 @@ class HardDeleteFacade( executor.run(TRIPS_OWNED_BY_DELETED_MEMBER) { tripCommandService.hardDeleteTripsOwnedByDeletedMember() } - phases[TRIPS] = executor.run(TRIPS) { tripCommandService.hardDeleteTrips() diff --git a/src/main/kotlin/com/ject/studytrip/dummy/application/dto/CreateDummyMissionCommand.kt b/src/main/kotlin/com/ject/studytrip/dummy/application/dto/CreateDummyMissionCommand.kt index f77f52e..5abdb00 100644 --- a/src/main/kotlin/com/ject/studytrip/dummy/application/dto/CreateDummyMissionCommand.kt +++ b/src/main/kotlin/com/ject/studytrip/dummy/application/dto/CreateDummyMissionCommand.kt @@ -2,9 +2,4 @@ package com.ject.studytrip.dummy.application.dto data class CreateDummyMissionCommand( val name: String, -) { - companion object { - @JvmStatic - fun of(name: String): CreateDummyMissionCommand = CreateDummyMissionCommand(name) - } -} +) diff --git a/src/main/kotlin/com/ject/studytrip/dummy/application/dto/CreateDummyStampCommand.kt b/src/main/kotlin/com/ject/studytrip/dummy/application/dto/CreateDummyStampCommand.kt index 0e55c92..9c41953 100644 --- a/src/main/kotlin/com/ject/studytrip/dummy/application/dto/CreateDummyStampCommand.kt +++ b/src/main/kotlin/com/ject/studytrip/dummy/application/dto/CreateDummyStampCommand.kt @@ -6,13 +6,4 @@ data class CreateDummyStampCommand( val name: String, val stampOrder: Int, val endDate: LocalDate?, -) { - companion object { - @JvmStatic - fun of( - name: String, - stampOrder: Int, - endDate: LocalDate?, - ): CreateDummyStampCommand = CreateDummyStampCommand(name, stampOrder, endDate) - } -} +) diff --git a/src/main/kotlin/com/ject/studytrip/dummy/application/dto/CreateDummyTripCommand.kt b/src/main/kotlin/com/ject/studytrip/dummy/application/dto/CreateDummyTripCommand.kt index 4391c55..975de6a 100644 --- a/src/main/kotlin/com/ject/studytrip/dummy/application/dto/CreateDummyTripCommand.kt +++ b/src/main/kotlin/com/ject/studytrip/dummy/application/dto/CreateDummyTripCommand.kt @@ -8,14 +8,4 @@ data class CreateDummyTripCommand( val memo: String, val tripCategory: TripCategory, val endDate: LocalDate?, -) { - companion object { - @JvmStatic - fun of( - name: String, - memo: String, - tripCategory: TripCategory, - endDate: LocalDate?, - ): CreateDummyTripCommand = CreateDummyTripCommand(name, memo, tripCategory, endDate) - } -} +) diff --git a/src/main/kotlin/com/ject/studytrip/dummy/application/dto/DummyMissionInfo.kt b/src/main/kotlin/com/ject/studytrip/dummy/application/dto/DummyMissionInfo.kt index 8ec1208..8683040 100644 --- a/src/main/kotlin/com/ject/studytrip/dummy/application/dto/DummyMissionInfo.kt +++ b/src/main/kotlin/com/ject/studytrip/dummy/application/dto/DummyMissionInfo.kt @@ -7,11 +7,6 @@ data class DummyMissionInfo( val completed: Boolean, ) { companion object { - @JvmStatic - fun from(mission: Mission): DummyMissionInfo = - DummyMissionInfo( - mission.name, - mission.isCompleted, - ) + fun from(mission: Mission): DummyMissionInfo = DummyMissionInfo(mission.name, mission.isCompleted()) } } diff --git a/src/main/kotlin/com/ject/studytrip/dummy/application/dto/DummyMissionsInfo.kt b/src/main/kotlin/com/ject/studytrip/dummy/application/dto/DummyMissionsInfo.kt index 050354f..6d4a45c 100644 --- a/src/main/kotlin/com/ject/studytrip/dummy/application/dto/DummyMissionsInfo.kt +++ b/src/main/kotlin/com/ject/studytrip/dummy/application/dto/DummyMissionsInfo.kt @@ -2,9 +2,4 @@ package com.ject.studytrip.dummy.application.dto data class DummyMissionsInfo( val missionInfos: List, -) { - companion object { - @JvmStatic - fun of(missionInfos: List): DummyMissionsInfo = DummyMissionsInfo(missionInfos) - } -} +) diff --git a/src/main/kotlin/com/ject/studytrip/dummy/application/dto/DummyStampInfo.kt b/src/main/kotlin/com/ject/studytrip/dummy/application/dto/DummyStampInfo.kt index 3a0b619..32c96c1 100644 --- a/src/main/kotlin/com/ject/studytrip/dummy/application/dto/DummyStampInfo.kt +++ b/src/main/kotlin/com/ject/studytrip/dummy/application/dto/DummyStampInfo.kt @@ -12,15 +12,14 @@ data class DummyStampInfo( val completed: Boolean, ) { companion object { - @JvmStatic fun from(stamp: Stamp): DummyStampInfo = DummyStampInfo( stamp.name, stamp.stampOrder, - DateUtil.formatDate(stamp.endDate), + stamp.endDate?.let { DateUtil.formatDate(it) }, stamp.totalMissions, stamp.completedMissions, - stamp.isCompleted, + stamp.isCompleted(), ) } } diff --git a/src/main/kotlin/com/ject/studytrip/dummy/application/dto/DummyStampsInfo.kt b/src/main/kotlin/com/ject/studytrip/dummy/application/dto/DummyStampsInfo.kt index 23dfd1d..896b6da 100644 --- a/src/main/kotlin/com/ject/studytrip/dummy/application/dto/DummyStampsInfo.kt +++ b/src/main/kotlin/com/ject/studytrip/dummy/application/dto/DummyStampsInfo.kt @@ -2,9 +2,4 @@ package com.ject.studytrip.dummy.application.dto data class DummyStampsInfo( val stampsInfos: List, -) { - companion object { - @JvmStatic - fun of(stampsInfos: List): DummyStampsInfo = DummyStampsInfo(stampsInfos) - } -} +) diff --git a/src/main/kotlin/com/ject/studytrip/dummy/application/facade/DummyMissionFacade.kt b/src/main/kotlin/com/ject/studytrip/dummy/application/facade/DummyMissionFacade.kt index 7dda983..461517c 100644 --- a/src/main/kotlin/com/ject/studytrip/dummy/application/facade/DummyMissionFacade.kt +++ b/src/main/kotlin/com/ject/studytrip/dummy/application/facade/DummyMissionFacade.kt @@ -27,6 +27,6 @@ class DummyMissionFacade( val stamp = dummyStampCommandService.createDummyStamp(trip, count) val missions = List(count) { dummyMissionCommandService.createDummyMission(stamp) } - return DummyMissionsInfo.of(missions.map { DummyMissionInfo.from(it) }) + return DummyMissionsInfo(missions.map { DummyMissionInfo.from(it) }) } } diff --git a/src/main/kotlin/com/ject/studytrip/dummy/application/facade/DummyStampFacade.kt b/src/main/kotlin/com/ject/studytrip/dummy/application/facade/DummyStampFacade.kt index 03059c1..1835afd 100644 --- a/src/main/kotlin/com/ject/studytrip/dummy/application/facade/DummyStampFacade.kt +++ b/src/main/kotlin/com/ject/studytrip/dummy/application/facade/DummyStampFacade.kt @@ -24,6 +24,6 @@ class DummyStampFacade( val trip = dummyTripCommandService.createDummyTrip(member, category, count) val stamps = (1..count).map { order -> dummyStampCommandService.createDummyStamp(trip, order) } - return DummyStampsInfo.of(stamps.map { DummyStampInfo.from(it) }) + return DummyStampsInfo(stamps.map { DummyStampInfo.from(it) }) } } diff --git a/src/main/kotlin/com/ject/studytrip/dummy/application/service/DummyMissionCommandService.kt b/src/main/kotlin/com/ject/studytrip/dummy/application/service/DummyMissionCommandService.kt index af05ef5..ba47b75 100644 --- a/src/main/kotlin/com/ject/studytrip/dummy/application/service/DummyMissionCommandService.kt +++ b/src/main/kotlin/com/ject/studytrip/dummy/application/service/DummyMissionCommandService.kt @@ -9,7 +9,7 @@ import org.springframework.stereotype.Service @Service class DummyMissionCommandService { fun createDummyMission(stamp: Stamp): Mission { - val command = CreateDummyMissionCommand.of("testMission") + val command = CreateDummyMissionCommand("testMission") return MissionFactory.create(stamp, command.name) } diff --git a/src/main/kotlin/com/ject/studytrip/dummy/application/service/DummyStampCommandService.kt b/src/main/kotlin/com/ject/studytrip/dummy/application/service/DummyStampCommandService.kt index 03c8340..478eb71 100644 --- a/src/main/kotlin/com/ject/studytrip/dummy/application/service/DummyStampCommandService.kt +++ b/src/main/kotlin/com/ject/studytrip/dummy/application/service/DummyStampCommandService.kt @@ -16,8 +16,8 @@ class DummyStampCommandService { ): Stamp { val command = when (trip.category) { - TripCategory.COURSE -> CreateDummyStampCommand.of("testStamp", stampOrder, LocalDate.now().plusDays(10)) - TripCategory.EXPLORE -> CreateDummyStampCommand.of("testStamp", 0, null) + TripCategory.COURSE -> CreateDummyStampCommand("testStamp", stampOrder, LocalDate.now().plusDays(10)) + TripCategory.EXPLORE -> CreateDummyStampCommand("testStamp", 0, null) } return StampFactory.create(trip, command.name, command.stampOrder, command.endDate) diff --git a/src/main/kotlin/com/ject/studytrip/dummy/application/service/DummyTripCommandService.kt b/src/main/kotlin/com/ject/studytrip/dummy/application/service/DummyTripCommandService.kt index 3ba855a..e1bcf25 100644 --- a/src/main/kotlin/com/ject/studytrip/dummy/application/service/DummyTripCommandService.kt +++ b/src/main/kotlin/com/ject/studytrip/dummy/application/service/DummyTripCommandService.kt @@ -19,8 +19,8 @@ class DummyTripCommandService { val command = when (tripCategory) { - TripCategory.COURSE -> CreateDummyTripCommand.of("testTrip", "testMemo", tripCategory, LocalDate.now().plusDays(10)) - TripCategory.EXPLORE -> CreateDummyTripCommand.of("testTrip", "testMemo", tripCategory, null) + TripCategory.COURSE -> CreateDummyTripCommand("testTrip", "testMemo", tripCategory, LocalDate.now().plusDays(10)) + TripCategory.EXPLORE -> CreateDummyTripCommand("testTrip", "testMemo", tripCategory, null) } return TripFactory.create(member, command.name, command.memo, command.tripCategory, command.endDate, count) diff --git a/src/main/kotlin/com/ject/studytrip/dummy/presentation/controller/DummyMissionController.kt b/src/main/kotlin/com/ject/studytrip/dummy/presentation/controller/DummyMissionController.kt index b7758b7..7195c4d 100644 --- a/src/main/kotlin/com/ject/studytrip/dummy/presentation/controller/DummyMissionController.kt +++ b/src/main/kotlin/com/ject/studytrip/dummy/presentation/controller/DummyMissionController.kt @@ -1,7 +1,7 @@ package com.ject.studytrip.dummy.presentation.controller import com.ject.studytrip.dummy.application.facade.DummyMissionFacade -import com.ject.studytrip.dummy.presentation.response.dto.LoadDummyMissionInfoResponse +import com.ject.studytrip.dummy.presentation.dto.response.LoadDummyMissionInfoResponse import com.ject.studytrip.global.common.response.StandardResponse import io.swagger.v3.oas.annotations.Operation import io.swagger.v3.oas.annotations.tags.Tag diff --git a/src/main/kotlin/com/ject/studytrip/dummy/presentation/controller/DummyStampController.kt b/src/main/kotlin/com/ject/studytrip/dummy/presentation/controller/DummyStampController.kt index 02f0e6e..9e65f51 100644 --- a/src/main/kotlin/com/ject/studytrip/dummy/presentation/controller/DummyStampController.kt +++ b/src/main/kotlin/com/ject/studytrip/dummy/presentation/controller/DummyStampController.kt @@ -1,7 +1,7 @@ package com.ject.studytrip.dummy.presentation.controller import com.ject.studytrip.dummy.application.facade.DummyStampFacade -import com.ject.studytrip.dummy.presentation.response.dto.LoadDummyStampInfoResponse +import com.ject.studytrip.dummy.presentation.dto.response.LoadDummyStampInfoResponse import com.ject.studytrip.global.common.response.StandardResponse import io.swagger.v3.oas.annotations.Operation import io.swagger.v3.oas.annotations.tags.Tag diff --git a/src/main/kotlin/com/ject/studytrip/dummy/presentation/response/dto/LoadDummyMissionInfoResponse.kt b/src/main/kotlin/com/ject/studytrip/dummy/presentation/dto/response/LoadDummyMissionInfoResponse.kt similarity index 66% rename from src/main/kotlin/com/ject/studytrip/dummy/presentation/response/dto/LoadDummyMissionInfoResponse.kt rename to src/main/kotlin/com/ject/studytrip/dummy/presentation/dto/response/LoadDummyMissionInfoResponse.kt index 4643c5d..1182447 100644 --- a/src/main/kotlin/com/ject/studytrip/dummy/presentation/response/dto/LoadDummyMissionInfoResponse.kt +++ b/src/main/kotlin/com/ject/studytrip/dummy/presentation/dto/response/LoadDummyMissionInfoResponse.kt @@ -1,4 +1,4 @@ -package com.ject.studytrip.dummy.presentation.response.dto +package com.ject.studytrip.dummy.presentation.dto.response import com.ject.studytrip.dummy.application.dto.DummyMissionInfo import io.swagger.v3.oas.annotations.media.Schema @@ -10,11 +10,7 @@ data class LoadDummyMissionInfoResponse( val completed: Boolean, ) { companion object { - @JvmStatic fun of(dummyMissionInfo: DummyMissionInfo): LoadDummyMissionInfoResponse = - LoadDummyMissionInfoResponse( - dummyMissionInfo.missionName, - dummyMissionInfo.completed, - ) + LoadDummyMissionInfoResponse(dummyMissionInfo.missionName, dummyMissionInfo.completed) } } diff --git a/src/main/kotlin/com/ject/studytrip/dummy/presentation/response/dto/LoadDummyStampInfoResponse.kt b/src/main/kotlin/com/ject/studytrip/dummy/presentation/dto/response/LoadDummyStampInfoResponse.kt similarity index 93% rename from src/main/kotlin/com/ject/studytrip/dummy/presentation/response/dto/LoadDummyStampInfoResponse.kt rename to src/main/kotlin/com/ject/studytrip/dummy/presentation/dto/response/LoadDummyStampInfoResponse.kt index 5dff219..d2df897 100644 --- a/src/main/kotlin/com/ject/studytrip/dummy/presentation/response/dto/LoadDummyStampInfoResponse.kt +++ b/src/main/kotlin/com/ject/studytrip/dummy/presentation/dto/response/LoadDummyStampInfoResponse.kt @@ -1,4 +1,4 @@ -package com.ject.studytrip.dummy.presentation.response.dto +package com.ject.studytrip.dummy.presentation.dto.response import com.ject.studytrip.dummy.application.dto.DummyStampInfo import io.swagger.v3.oas.annotations.media.Schema @@ -18,7 +18,6 @@ data class LoadDummyStampInfoResponse( val completed: Boolean, ) { companion object { - @JvmStatic fun of(dummyStampInfo: DummyStampInfo): LoadDummyStampInfoResponse = LoadDummyStampInfoResponse( dummyStampInfo.stampName, diff --git a/src/main/kotlin/com/ject/studytrip/global/batch/scheduler/BatchJobScheduler.kt b/src/main/kotlin/com/ject/studytrip/global/batch/scheduler/BatchJobScheduler.kt new file mode 100644 index 0000000..0279a4c --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/global/batch/scheduler/BatchJobScheduler.kt @@ -0,0 +1,33 @@ +package com.ject.studytrip.global.batch.scheduler + +import org.springframework.batch.core.Job +import org.springframework.batch.core.JobParameters +import org.springframework.batch.core.JobParametersBuilder +import org.springframework.batch.core.launch.JobLauncher +import org.springframework.context.annotation.Profile +import org.springframework.scheduling.annotation.Scheduled +import org.springframework.stereotype.Component + +@Profile("!test") +@Component +class BatchJobScheduler( + private val jobLauncher: JobLauncher, + private val hardDeleteJob: Job, +) { + companion object { + private const val JOB_PARAMETER_TIMESTAMP = "ts" + } + + /** + * 매일 04:00 (Asia/Seoul) + */ + @Scheduled(cron = "0 0 4 * * *", zone = "Asia/Seoul") + fun runHardDeleteJob() { + jobLauncher.run(hardDeleteJob, createJobParameters()) + } + + private fun createJobParameters(): JobParameters = + JobParametersBuilder() + .addLong(JOB_PARAMETER_TIMESTAMP, System.currentTimeMillis()) + .toJobParameters() +} diff --git a/src/main/kotlin/com/ject/studytrip/global/batch/tasklet/HardDeleteTasklet.kt b/src/main/kotlin/com/ject/studytrip/global/batch/tasklet/HardDeleteTasklet.kt new file mode 100644 index 0000000..5314422 --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/global/batch/tasklet/HardDeleteTasklet.kt @@ -0,0 +1,22 @@ +package com.ject.studytrip.global.batch.tasklet + +import com.ject.studytrip.cleanup.application.facade.HardDeleteFacade +import org.springframework.batch.core.StepContribution +import org.springframework.batch.core.scope.context.ChunkContext +import org.springframework.batch.core.step.tasklet.Tasklet +import org.springframework.batch.repeat.RepeatStatus +import org.springframework.stereotype.Component + +@Component +class HardDeleteTasklet( + private val hardDeleteFacade: HardDeleteFacade, +) : Tasklet { + override fun execute( + contribution: StepContribution, + chunkContext: ChunkContext, + ): RepeatStatus { + hardDeleteFacade.hardDeleteAll() + + return RepeatStatus.FINISHED + } +} diff --git a/src/main/kotlin/com/ject/studytrip/global/common/constants/BatchConstants.kt b/src/main/kotlin/com/ject/studytrip/global/common/constants/BatchConstants.kt new file mode 100644 index 0000000..737790a --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/global/common/constants/BatchConstants.kt @@ -0,0 +1,6 @@ +package com.ject.studytrip.global.common.constants + +object BatchConstants { + const val HARD_DELETE_JOB = "hardDeleteJob" + const val HARD_DELETE_STEP = "hardDeleteStep" +} diff --git a/src/main/kotlin/com/ject/studytrip/global/common/constants/CacheKeyConstants.kt b/src/main/kotlin/com/ject/studytrip/global/common/constants/CacheKeyConstants.kt new file mode 100644 index 0000000..de9f2cf --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/global/common/constants/CacheKeyConstants.kt @@ -0,0 +1,7 @@ +package com.ject.studytrip.global.common.constants + +object CacheKeyConstants { + const val AUTH_REISSUE_TOKEN_PREFIX = "auth::reissue::token:" + const val AUTH_LOGOUT_TOKEN_PREFIX = "auth::logout::token:" + const val OAUTH_SIGNUP_PROFILE_PREFIX = "%s::signup::profile:" +} diff --git a/src/main/kotlin/com/ject/studytrip/global/common/constants/CacheNameConstants.kt b/src/main/kotlin/com/ject/studytrip/global/common/constants/CacheNameConstants.kt new file mode 100644 index 0000000..ee88377 --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/global/common/constants/CacheNameConstants.kt @@ -0,0 +1,14 @@ +package com.ject.studytrip.global.common.constants + +object CacheNameConstants { + const val MEMBER = "member" + const val TRIP = "trip" + const val TRIPS = "trips" + const val STAMP = "stamp" + const val STAMPS = "stamps" + const val MISSIONS = "missions" + const val DAILY_GOAL = "dailyGoal" + const val STUDY_LOGS = "studyLogs" + const val TRIP_REPORT = "tripReport" + const val TRIP_REPORTS = "tripReports" +} diff --git a/src/main/kotlin/com/ject/studytrip/global/common/constants/CookieConstants.kt b/src/main/kotlin/com/ject/studytrip/global/common/constants/CookieConstants.kt new file mode 100644 index 0000000..fc1cc28 --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/global/common/constants/CookieConstants.kt @@ -0,0 +1,10 @@ +package com.ject.studytrip.global.common.constants + +object CookieConstants { + // 인증 관련 + const val AUTH_REFRESH_TOKEN = "auth_refresh" + + // OAuth 관련 + const val OAUTH_SIGNUP_KEY = "oauth_signup_key" + const val OAUTH_SIGNUP_COOKIE_TTL_MILLIS = 900000L +} diff --git a/src/main/kotlin/com/ject/studytrip/global/common/constants/UrlConstants.kt b/src/main/kotlin/com/ject/studytrip/global/common/constants/UrlConstants.kt new file mode 100644 index 0000000..523e697 --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/global/common/constants/UrlConstants.kt @@ -0,0 +1,44 @@ +package com.ject.studytrip.global.common.constants + +object UrlConstants { + // CORS 허용 도메인 + val CORS_DOMAINS = + arrayOf( + "http://localhost:8080", + "https://dev-api-studytrip.duckdns.org", + "http://localhost:5173", + "https://localhost:5173", + "https://ject-4-client.vercel.app", + ) + + // 정적 리소스 경로 + val STATIC_RESOURCES = + arrayOf( + "/favicon.ico", + "/firebase-messaging-sw.js", + "/.well-known/**", + "/v3/api-docs/**", + "/swagger-ui/**", + "/swagger-ui.html", + ) + + // OAuth 콜백 경로 + val CALLBACK_PATHS = + arrayOf( + "/auth/callback/**", + ) + + // Origin 추출이 필요한 경로 + val ORIGIN_EXTRACT_PATHS = + arrayOf( + "/api/auth/login/kakao", + ) + + // 인증이 필요없는 API 경로 + val PERMIT_ALL_API_PATHS = + arrayOf( + "/api/auth/**", + "/api/trips/categories", + "/api/members/me/restore/**", + ) +} diff --git a/src/main/kotlin/com/ject/studytrip/global/common/entity/BaseTimeEntity.kt b/src/main/kotlin/com/ject/studytrip/global/common/entity/BaseTimeEntity.kt new file mode 100644 index 0000000..d93ffbf --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/global/common/entity/BaseTimeEntity.kt @@ -0,0 +1,31 @@ +package com.ject.studytrip.global.common.entity + +import jakarta.persistence.Column +import jakarta.persistence.EntityListeners +import jakarta.persistence.MappedSuperclass +import org.springframework.data.annotation.CreatedDate +import org.springframework.data.annotation.LastModifiedDate +import org.springframework.data.jpa.domain.support.AuditingEntityListener +import java.time.LocalDateTime + +@EntityListeners(AuditingEntityListener::class) +@MappedSuperclass +abstract class BaseTimeEntity { + @CreatedDate + @Column(updatable = false) + var createdAt: LocalDateTime? = null + protected set + + @LastModifiedDate + var updatedAt: LocalDateTime? = null + protected set + + var deletedAt: LocalDateTime? = null + protected set + + fun markDeleted(now: LocalDateTime = LocalDateTime.now()) { + deletedAt = now + } + + fun isDeleted(): Boolean = deletedAt != null +} diff --git a/src/main/kotlin/com/ject/studytrip/global/common/factory/CacheKeyFactory.kt b/src/main/kotlin/com/ject/studytrip/global/common/factory/CacheKeyFactory.kt new file mode 100644 index 0000000..9859d6d --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/global/common/factory/CacheKeyFactory.kt @@ -0,0 +1,66 @@ +package com.ject.studytrip.global.common.factory + +object CacheKeyFactory { + @JvmStatic + fun member(memberId: Long): String = "member:$memberId" + + @JvmStatic + fun trips( + memberId: Long, + page: Int, + size: Int, + ): String = "member:$memberId:trips:page:$page:size:$size" + + @JvmStatic + fun trip( + memberId: Long, + tripId: Long, + ): String = "member:$memberId:trip:$tripId" + + @JvmStatic + fun stamps( + memberId: Long, + tripId: Long, + ): String = "member:$memberId:trip:$tripId:stamps" + + @JvmStatic + fun stamp( + memberId: Long, + tripId: Long, + stampId: Long, + ): String = "member:$memberId:trip:$tripId:stamp:$stampId" + + @JvmStatic + fun missions( + memberId: Long, + tripId: Long, + stampId: Long, + ): String = "member:$memberId:trip:$tripId:stamp:$stampId:missions" + + @JvmStatic + fun dailyGoal( + memberId: Long, + tripId: Long, + dailyGoalId: Long, + ): String = "member:$memberId:trip:$tripId:dailyGoal:$dailyGoalId" + + @JvmStatic + fun studyLogs( + memberId: Long, + tripId: Long, + page: Int, + size: Int, + order: String, + ): String = "member:$memberId:trip:$tripId:studyLogs:page:$page:size:$size:order:${order.lowercase()}" + + @JvmStatic + fun tripReports(memberId: Long): String = "member:$memberId:tripReports" + + @JvmStatic + fun tripReport( + memberId: Long, + tripReportId: Long, + page: Int, + size: Int, + ): String = "member:$memberId:tripReport:$tripReportId:page:$page:size:$size" +} diff --git a/src/main/kotlin/com/ject/studytrip/global/common/response/StandardResponse.kt b/src/main/kotlin/com/ject/studytrip/global/common/response/StandardResponse.kt new file mode 100644 index 0000000..7f19d49 --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/global/common/response/StandardResponse.kt @@ -0,0 +1,21 @@ +package com.ject.studytrip.global.common.response + +import com.ject.studytrip.global.exception.response.ErrorResponse + +data class StandardResponse( + val success: Boolean, + val status: Int, + val data: Any?, +) { + companion object { + fun success( + status: Int, + data: Any?, + ): StandardResponse = StandardResponse(true, status, data) + + fun fail( + status: Int, + errorResponse: ErrorResponse, + ): StandardResponse = StandardResponse(false, status, errorResponse) + } +} diff --git a/src/main/kotlin/com/ject/studytrip/global/config/BatchJobConfig.kt b/src/main/kotlin/com/ject/studytrip/global/config/BatchJobConfig.kt new file mode 100644 index 0000000..19ec730 --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/global/config/BatchJobConfig.kt @@ -0,0 +1,22 @@ +package com.ject.studytrip.global.config + +import com.ject.studytrip.global.common.constants.BatchConstants.HARD_DELETE_JOB +import org.springframework.batch.core.Job +import org.springframework.batch.core.Step +import org.springframework.batch.core.job.builder.JobBuilder +import org.springframework.batch.core.launch.support.RunIdIncrementer +import org.springframework.batch.core.repository.JobRepository +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration + +@Configuration +class BatchJobConfig( + private val hardDeleteStep: Step, +) { + @Bean + fun hardDeleteJob(jobRepository: JobRepository): Job = + JobBuilder(HARD_DELETE_JOB, jobRepository) + .incrementer(RunIdIncrementer()) + .start(hardDeleteStep) + .build() +} diff --git a/src/main/kotlin/com/ject/studytrip/global/config/BatchStepConfig.kt b/src/main/kotlin/com/ject/studytrip/global/config/BatchStepConfig.kt new file mode 100644 index 0000000..93c3cd8 --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/global/config/BatchStepConfig.kt @@ -0,0 +1,24 @@ +package com.ject.studytrip.global.config + +import com.ject.studytrip.global.batch.tasklet.HardDeleteTasklet +import com.ject.studytrip.global.common.constants.BatchConstants.HARD_DELETE_STEP +import org.springframework.batch.core.Step +import org.springframework.batch.core.repository.JobRepository +import org.springframework.batch.core.step.builder.StepBuilder +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.transaction.PlatformTransactionManager + +@Configuration +class BatchStepConfig( + private val hardDeleteTasklet: HardDeleteTasklet, +) { + @Bean + fun hardDeleteStep( + jobRepository: JobRepository, + transactionManager: PlatformTransactionManager, + ): Step = + StepBuilder(HARD_DELETE_STEP, jobRepository) + .tasklet(hardDeleteTasklet, transactionManager) + .build() +} diff --git a/src/main/kotlin/com/ject/studytrip/global/config/CdnConfig.kt b/src/main/kotlin/com/ject/studytrip/global/config/CdnConfig.kt new file mode 100644 index 0000000..e25e51c --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/global/config/CdnConfig.kt @@ -0,0 +1,9 @@ +package com.ject.studytrip.global.config + +import com.ject.studytrip.global.config.properties.CdnProperties +import org.springframework.boot.context.properties.EnableConfigurationProperties +import org.springframework.context.annotation.Configuration + +@Configuration +@EnableConfigurationProperties(CdnProperties::class) +class CdnConfig diff --git a/src/main/kotlin/com/ject/studytrip/global/config/JpaAuditingConfig.kt b/src/main/kotlin/com/ject/studytrip/global/config/JpaAuditingConfig.kt new file mode 100644 index 0000000..7f23897 --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/global/config/JpaAuditingConfig.kt @@ -0,0 +1,8 @@ +package com.ject.studytrip.global.config + +import org.springframework.context.annotation.Configuration +import org.springframework.data.jpa.repository.config.EnableJpaAuditing + +@Configuration +@EnableJpaAuditing +class JpaAuditingConfig diff --git a/src/main/kotlin/com/ject/studytrip/global/config/QuerydslConfig.kt b/src/main/kotlin/com/ject/studytrip/global/config/QuerydslConfig.kt new file mode 100644 index 0000000..634d2a0 --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/global/config/QuerydslConfig.kt @@ -0,0 +1,14 @@ +package com.ject.studytrip.global.config + +import com.querydsl.jpa.impl.JPAQueryFactory +import jakarta.persistence.EntityManager +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration + +@Configuration +class QuerydslConfig( + private val em: EntityManager, +) { + @Bean + fun queryFactory(): JPAQueryFactory = JPAQueryFactory(em) +} diff --git a/src/main/kotlin/com/ject/studytrip/global/config/RedisCacheConfig.kt b/src/main/kotlin/com/ject/studytrip/global/config/RedisCacheConfig.kt new file mode 100644 index 0000000..6d48fc0 --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/global/config/RedisCacheConfig.kt @@ -0,0 +1,62 @@ +package com.ject.studytrip.global.config + +import com.ject.studytrip.global.common.constants.CacheNameConstants.DAILY_GOAL +import com.ject.studytrip.global.common.constants.CacheNameConstants.MEMBER +import com.ject.studytrip.global.common.constants.CacheNameConstants.STAMP +import com.ject.studytrip.global.common.constants.CacheNameConstants.STAMPS +import com.ject.studytrip.global.common.constants.CacheNameConstants.STUDY_LOGS +import com.ject.studytrip.global.common.constants.CacheNameConstants.TRIP +import com.ject.studytrip.global.common.constants.CacheNameConstants.TRIPS +import com.ject.studytrip.global.common.constants.CacheNameConstants.TRIP_REPORT +import com.ject.studytrip.global.common.constants.CacheNameConstants.TRIP_REPORTS +import org.springframework.cache.annotation.EnableCaching +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.data.redis.cache.CacheKeyPrefix +import org.springframework.data.redis.cache.RedisCacheConfiguration +import org.springframework.data.redis.cache.RedisCacheManager +import org.springframework.data.redis.cache.RedisCacheWriter.nonLockingRedisCacheWriter +import org.springframework.data.redis.connection.RedisConnectionFactory +import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer +import org.springframework.data.redis.serializer.RedisSerializationContext.SerializationPair.fromSerializer +import org.springframework.data.redis.serializer.StringRedisSerializer +import java.time.Duration + +@EnableCaching +@Configuration +class RedisCacheConfig { + @Bean + fun redisCacheManager(factory: RedisConnectionFactory): RedisCacheManager { + val common = createRedisCacheConfig().entryTtl(Duration.ofMinutes(30)) // 기본 TTL + + return RedisCacheManager + .builder(nonLockingRedisCacheWriter(factory)) + .cacheDefaults(common) + .withInitialCacheConfigurations(redisCacheConfigsByName(common)) + .transactionAware() + .build() + } + + // 공통 직렬화/프리픽스/널 캐싱 금지 설정 + private fun createRedisCacheConfig(): RedisCacheConfiguration = + RedisCacheConfiguration + .defaultCacheConfig() + .serializeKeysWith(fromSerializer(StringRedisSerializer())) + .serializeValuesWith(fromSerializer(GenericJackson2JsonRedisSerializer())) + .disableCachingNullValues() + .computePrefixWith(CacheKeyPrefix.simple()) + + // 캐시 이름별 TTL 정책 적용 + private fun redisCacheConfigsByName(common: RedisCacheConfiguration): Map = + mutableMapOf().apply { + put(MEMBER, common.entryTtl(Duration.ofMinutes(10))) // 멤버 상세 조회 + put(DAILY_GOAL, common.entryTtl(Duration.ofMinutes(10))) // 데일리 목표 상세 조회 + put(STUDY_LOGS, common.entryTtl(Duration.ofMinutes(10))) // 학습 로그 목록 조회 + put(TRIP_REPORTS, common.entryTtl(Duration.ofMinutes(10))) // 여행 리포트 목록 조회 + put(TRIP, common.entryTtl(Duration.ofMinutes(5))) // 여행 상세 조회 + put(STAMP, common.entryTtl(Duration.ofMinutes(5))) // 스탬프 상세 조회 + put(TRIP_REPORT, common.entryTtl(Duration.ofMinutes(5))) // 여행 리포트 상세 조회 + put(TRIPS, common.entryTtl(Duration.ofMinutes(3))) // 여행 목록 조회(페이지) + put(STAMPS, common.entryTtl(Duration.ofMinutes(3))) // 스탬프 목록 조회 + } +} diff --git a/src/main/kotlin/com/ject/studytrip/global/config/RedisConfig.kt b/src/main/kotlin/com/ject/studytrip/global/config/RedisConfig.kt new file mode 100644 index 0000000..9460866 --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/global/config/RedisConfig.kt @@ -0,0 +1,45 @@ +package com.ject.studytrip.global.config + +import com.fasterxml.jackson.databind.ObjectMapper +import com.ject.studytrip.global.config.properties.RedisProperties +import org.springframework.boot.context.properties.EnableConfigurationProperties +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.lettuce.LettuceConnectionFactory +import org.springframework.data.redis.core.RedisTemplate +import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer +import org.springframework.data.redis.serializer.StringRedisSerializer + +@Configuration +@EnableConfigurationProperties(RedisProperties::class) +class RedisConfig( + private val redisProperties: RedisProperties, +) { + @Bean + fun redisConnectionFactory(): RedisConnectionFactory = + LettuceConnectionFactory( + redisProperties.host, + redisProperties.port, + ) + + @Bean + fun redisTemplate( + factory: RedisConnectionFactory, + objectMapper: ObjectMapper, + ): RedisTemplate { + val template = RedisTemplate() + template.connectionFactory = factory + + // 단순 Key-Value 직렬화 + template.keySerializer = StringRedisSerializer() + template.valueSerializer = GenericJackson2JsonRedisSerializer(objectMapper) + + // 해시 Key-Value 직렬화 + template.hashKeySerializer = StringRedisSerializer() + template.hashValueSerializer = GenericJackson2JsonRedisSerializer(objectMapper) + + template.afterPropertiesSet() + return template + } +} diff --git a/src/main/kotlin/com/ject/studytrip/global/config/S3Config.kt b/src/main/kotlin/com/ject/studytrip/global/config/S3Config.kt new file mode 100644 index 0000000..50c9667 --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/global/config/S3Config.kt @@ -0,0 +1,47 @@ +package com.ject.studytrip.global.config + +import com.ject.studytrip.global.config.properties.S3Properties +import org.springframework.boot.context.properties.EnableConfigurationProperties +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider +import software.amazon.awssdk.core.retry.RetryMode +import software.amazon.awssdk.core.retry.RetryPolicy +import software.amazon.awssdk.regions.Region +import software.amazon.awssdk.services.s3.S3Client +import software.amazon.awssdk.services.s3.presigner.S3Presigner +import java.time.Duration + +@Configuration +@EnableConfigurationProperties(S3Properties::class) +class S3Config( + private val s3Properties: S3Properties, +) { + @Bean + fun s3Client(): S3Client { + val retryPolicy = + RetryPolicy + .builder(RetryMode.STANDARD) + .numRetries(s3Properties.retry.maxAttempts) + .build() + + return S3Client + .builder() + .overrideConfiguration { + it + .retryPolicy(retryPolicy) + .apiCallTimeout(Duration.ofSeconds(s3Properties.timeout.apiCallInSeconds)) + .apiCallAttemptTimeout(Duration.ofSeconds(s3Properties.timeout.apiCallAttemptInSeconds)) + }.region(Region.of(s3Properties.region)) + .credentialsProvider(DefaultCredentialsProvider.create()) + .build() + } + + @Bean + fun s3Presigner(): S3Presigner = + S3Presigner + .builder() + .region(Region.of(s3Properties.region)) + .credentialsProvider(DefaultCredentialsProvider.create()) + .build() +} diff --git a/src/main/kotlin/com/ject/studytrip/global/config/SchedulerConfig.kt b/src/main/kotlin/com/ject/studytrip/global/config/SchedulerConfig.kt new file mode 100644 index 0000000..687f5c8 --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/global/config/SchedulerConfig.kt @@ -0,0 +1,8 @@ +package com.ject.studytrip.global.config + +import org.springframework.context.annotation.Configuration +import org.springframework.scheduling.annotation.EnableScheduling + +@EnableScheduling +@Configuration +class SchedulerConfig diff --git a/src/main/kotlin/com/ject/studytrip/global/config/SwaggerConfig.kt b/src/main/kotlin/com/ject/studytrip/global/config/SwaggerConfig.kt new file mode 100644 index 0000000..c896d0b --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/global/config/SwaggerConfig.kt @@ -0,0 +1,73 @@ +package com.ject.studytrip.global.config + +import com.ject.studytrip.global.config.properties.SwaggerProperties +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.SecurityRequirement +import io.swagger.v3.oas.models.security.SecurityScheme +import io.swagger.v3.oas.models.servers.Server +import org.springframework.boot.context.properties.EnableConfigurationProperties +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.context.annotation.Profile + +@Profile("!prod") // 로컬, 개발환경 활성화 +@Configuration +@EnableConfigurationProperties(SwaggerProperties::class) +class SwaggerConfig( + private val swaggerProperties: SwaggerProperties, +) { + companion object { + private const val SERVER_NAME = "StudyTrip" + private const val SERVER_DESCRIPTION = "StudyTrip 서버 URL 입니다." + private const val API_TITLE = "StudyTrip 서버 API 문서" + private const val API_DESCRIPTION = "StudyTrip 서버 API 문서입니다." + private const val GITHUB_URL = "https://github.com/JECT-Study/JECT-4-server" + } + + @Bean + fun openAPI(): OpenAPI = + OpenAPI() + .servers(swaggerServer()) + .addSecurityItem(securityRequirement()) + .components(authComponents()) + .info(swaggerInfo()) + + private fun swaggerServer(): List { + val server = + Server() + .url(swaggerProperties.serverUrl) + .description(SERVER_DESCRIPTION) + + return listOf(server) + } + + private fun authComponents(): Components = + Components() + .addSecuritySchemes( + "accessToken", + SecurityScheme() + .type(SecurityScheme.Type.HTTP) + .scheme("bearer") + .bearerFormat("JWT") + .`in`(SecurityScheme.In.HEADER) + .name("Authorization"), + ) + + private fun securityRequirement(): SecurityRequirement = SecurityRequirement().addList("accessToken") + + private fun swaggerInfo(): Info { + val license = + License() + .url(GITHUB_URL) + .name(SERVER_NAME) + + return Info() + .version("v${swaggerProperties.version}") + .title(API_TITLE) + .description(API_DESCRIPTION) + .license(license) + } +} diff --git a/src/main/kotlin/com/ject/studytrip/global/config/TikaConfig.kt b/src/main/kotlin/com/ject/studytrip/global/config/TikaConfig.kt new file mode 100644 index 0000000..19c59a6 --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/global/config/TikaConfig.kt @@ -0,0 +1,11 @@ +package com.ject.studytrip.global.config + +import org.apache.tika.Tika +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration + +@Configuration +class TikaConfig { + @Bean + fun tika(): Tika = Tika() +} diff --git a/src/main/kotlin/com/ject/studytrip/global/config/WebClientConfig.kt b/src/main/kotlin/com/ject/studytrip/global/config/WebClientConfig.kt new file mode 100644 index 0000000..a398408 --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/global/config/WebClientConfig.kt @@ -0,0 +1,20 @@ +package com.ject.studytrip.global.config + +import com.ject.studytrip.global.config.properties.KakaoOauthProperties +import org.springframework.boot.context.properties.EnableConfigurationProperties +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.http.HttpHeaders +import org.springframework.http.MediaType +import org.springframework.web.reactive.function.client.WebClient + +@Configuration +@EnableConfigurationProperties(KakaoOauthProperties::class) +class WebClientConfig { + @Bean + fun webClient(): WebClient = + WebClient + .builder() + .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE) + .build() +} diff --git a/src/main/kotlin/com/ject/studytrip/global/config/WebSecurityConfig.kt b/src/main/kotlin/com/ject/studytrip/global/config/WebSecurityConfig.kt new file mode 100644 index 0000000..88bf75d --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/global/config/WebSecurityConfig.kt @@ -0,0 +1,125 @@ +package com.ject.studytrip.global.config + +import com.ject.studytrip.auth.application.service.TokenService +import com.ject.studytrip.global.common.constants.UrlConstants +import com.ject.studytrip.global.config.properties.TokenProperties +import com.ject.studytrip.global.security.CustomAuthenticationEntryPoint +import com.ject.studytrip.global.security.filter.JwtAuthenticationFilter +import com.ject.studytrip.global.security.filter.OriginExtractionFilter +import com.ject.studytrip.global.security.handler.CustomAccessDeniedHandler +import com.ject.studytrip.global.security.handler.SecurityResponseHandler +import org.springframework.boot.context.properties.EnableConfigurationProperties +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.core.annotation.Order +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 +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter +import org.springframework.web.cors.CorsConfiguration +import org.springframework.web.cors.UrlBasedCorsConfigurationSource +import org.springframework.web.filter.CorsFilter + +@EnableWebSecurity +@Configuration +@EnableConfigurationProperties(TokenProperties::class) +class WebSecurityConfig( + private val authenticationEntryPoint: CustomAuthenticationEntryPoint, + private val accessDeniedHandler: CustomAccessDeniedHandler, +) { + // 퍼블릭 리소스 URL 필터 체인 + @Bean + @Order(0) + fun publicFilterChain(http: HttpSecurity): SecurityFilterChain { + defaultFilterChain(http) + + http.securityMatcher(*UrlConstants.STATIC_RESOURCES).authorizeHttpRequests { it.anyRequest().permitAll() } + + return http.build() + } + + // 콜백 URL 필터 체인 + @Bean + @Order(1) + fun callbackFilterChain(http: HttpSecurity): SecurityFilterChain { + defaultFilterChain(http) + + http.securityMatcher(*UrlConstants.CALLBACK_PATHS).authorizeHttpRequests { it.anyRequest().permitAll() } + + return http.build() + } + + // Origin 추출이 필요한 URL 필터 체인 + @Bean + @Order(2) + fun originExtractionFilterChain( + http: HttpSecurity, + securityResponseHandler: SecurityResponseHandler, + ): SecurityFilterChain { + defaultFilterChain(http) + + http.securityMatcher(*UrlConstants.ORIGIN_EXTRACT_PATHS) + + // Origin 추출 필터 등록: CORS 이후 OriginExtractionFilter 실행 + http.addFilterAfter(OriginExtractionFilter(securityResponseHandler), CorsFilter::class.java) + + http.authorizeHttpRequests { it.anyRequest().permitAll() } + + return http.build() + } + + // 메인 필터 체인 + @Bean + @Order(3) + fun mainFilterChain( + http: HttpSecurity, + tokenService: TokenService, + ): SecurityFilterChain { + defaultFilterChain(http) + + // JWT 필터 등록: 인증 이전에 동작해야 하므로 UsernamePasswordAuthenticationFilter 앞에 삽입 + http.addFilterBefore(JwtAuthenticationFilter(tokenService), UsernamePasswordAuthenticationFilter::class.java) + + // 경로 인가 설정 + http.authorizeHttpRequests { + it + .requestMatchers(*UrlConstants.PERMIT_ALL_API_PATHS) + .permitAll() + .anyRequest() + .authenticated() + } + + // 예외 핸들링 + http.exceptionHandling { + it + .authenticationEntryPoint(authenticationEntryPoint) + .accessDeniedHandler(accessDeniedHandler) + } + + return http.build() + } + + // CORS 설정 + @Bean + fun corsConfigurationSource(): UrlBasedCorsConfigurationSource { + val config = + CorsConfiguration().apply { + allowCredentials = true + addAllowedHeader("*") + addAllowedMethod("*") + allowedOrigins = UrlConstants.CORS_DOMAINS.toList() + } + + return UrlBasedCorsConfigurationSource().apply { registerCorsConfiguration("/**", config) } + } + + private fun defaultFilterChain(http: HttpSecurity) { + http + .csrf { it.disable() } // csrf 비활성화 + .httpBasic { it.disable() } // http basic 비활성화 + .formLogin { it.disable() } // 폼 로그인 비활성화 + .sessionManagement { it.sessionCreationPolicy(SessionCreationPolicy.STATELESS) } // 세션 비활성화 + .cors { it.configurationSource(corsConfigurationSource()) } // cors 설정 + } +} diff --git a/src/main/kotlin/com/ject/studytrip/global/config/properties/CdnProperties.kt b/src/main/kotlin/com/ject/studytrip/global/config/properties/CdnProperties.kt new file mode 100644 index 0000000..536d0ac --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/global/config/properties/CdnProperties.kt @@ -0,0 +1,8 @@ +package com.ject.studytrip.global.config.properties + +import org.springframework.boot.context.properties.ConfigurationProperties + +@ConfigurationProperties(prefix = "cdn") +data class CdnProperties( + val domain: String, +) diff --git a/src/main/kotlin/com/ject/studytrip/global/config/properties/KakaoOauthProperties.kt b/src/main/kotlin/com/ject/studytrip/global/config/properties/KakaoOauthProperties.kt new file mode 100644 index 0000000..79f6ae9 --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/global/config/properties/KakaoOauthProperties.kt @@ -0,0 +1,12 @@ +package com.ject.studytrip.global.config.properties + +import org.springframework.boot.context.properties.ConfigurationProperties + +@ConfigurationProperties(prefix = "oauth.kakao") +data class KakaoOauthProperties( + val clientId: String, + val clientSecret: String, + val redirectUri: String, + val tokenUri: String, + val userInfoUri: String, +) diff --git a/src/main/kotlin/com/ject/studytrip/global/config/properties/RedisProperties.kt b/src/main/kotlin/com/ject/studytrip/global/config/properties/RedisProperties.kt new file mode 100644 index 0000000..9f120f1 --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/global/config/properties/RedisProperties.kt @@ -0,0 +1,9 @@ +package com.ject.studytrip.global.config.properties + +import org.springframework.boot.context.properties.ConfigurationProperties + +@ConfigurationProperties(prefix = "spring.data.redis") +data class RedisProperties( + val port: Int, + val host: String, +) diff --git a/src/main/kotlin/com/ject/studytrip/global/config/properties/S3Properties.kt b/src/main/kotlin/com/ject/studytrip/global/config/properties/S3Properties.kt new file mode 100644 index 0000000..81f18a3 --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/global/config/properties/S3Properties.kt @@ -0,0 +1,21 @@ +package com.ject.studytrip.global.config.properties + +import org.springframework.boot.context.properties.ConfigurationProperties + +@ConfigurationProperties(prefix = "aws.s3") +data class S3Properties( + val bucket: String, + val region: String, + val presignExpiresInMinutes: Long, + val retry: S3Retry, + val timeout: S3Timeout, +) { + data class S3Retry( + val maxAttempts: Int, + ) + + data class S3Timeout( + val apiCallInSeconds: Long, + val apiCallAttemptInSeconds: Long, + ) +} diff --git a/src/main/kotlin/com/ject/studytrip/global/config/properties/SwaggerProperties.kt b/src/main/kotlin/com/ject/studytrip/global/config/properties/SwaggerProperties.kt new file mode 100644 index 0000000..48722ce --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/global/config/properties/SwaggerProperties.kt @@ -0,0 +1,9 @@ +package com.ject.studytrip.global.config.properties + +import org.springframework.boot.context.properties.ConfigurationProperties + +@ConfigurationProperties(prefix = "swagger") +data class SwaggerProperties( + val version: String, + val serverUrl: String, +) diff --git a/src/main/kotlin/com/ject/studytrip/global/config/properties/TokenProperties.kt b/src/main/kotlin/com/ject/studytrip/global/config/properties/TokenProperties.kt new file mode 100644 index 0000000..5cfffbe --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/global/config/properties/TokenProperties.kt @@ -0,0 +1,10 @@ +package com.ject.studytrip.global.config.properties + +import org.springframework.boot.context.properties.ConfigurationProperties + +@ConfigurationProperties(prefix = "jwt") +data class TokenProperties( + val secret: String, + val accessExpirationTime: Long, + val refreshExpirationTime: Long, +) diff --git a/src/main/kotlin/com/ject/studytrip/global/exception/CustomException.kt b/src/main/kotlin/com/ject/studytrip/global/exception/CustomException.kt new file mode 100644 index 0000000..8eae038 --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/global/exception/CustomException.kt @@ -0,0 +1,7 @@ +package com.ject.studytrip.global.exception + +import com.ject.studytrip.global.exception.error.ErrorCode + +class CustomException( + val errorCode: ErrorCode, +) : RuntimeException(errorCode.message) diff --git a/src/main/kotlin/com/ject/studytrip/global/exception/error/CommonErrorCode.kt b/src/main/kotlin/com/ject/studytrip/global/exception/error/CommonErrorCode.kt new file mode 100644 index 0000000..2c8e00f --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/global/exception/error/CommonErrorCode.kt @@ -0,0 +1,22 @@ +package com.ject.studytrip.global.exception.error + +import org.springframework.http.HttpStatus + +enum class CommonErrorCode( + override val status: HttpStatus, + override val message: String, +) : ErrorCode { + // 400 + METHOD_ARGUMENT_NOT_VALID(HttpStatus.BAD_REQUEST, "요청 본문(JSON)의 값 유효성 검증에 실패했습니다."), + METHOD_ARGUMENT_TYPE_MISMATCH(HttpStatus.BAD_REQUEST, "요청한 메서드 파라미터의 타입이 일치하지 않습니다."), + CONSTRAINT_VIOLATION(HttpStatus.BAD_REQUEST, "요청 파라미터 또는 경로 변수 유효성 검증에 실패했습니다."), + INVALID_JSON_FORMAT(HttpStatus.BAD_REQUEST, "요청 본문(JSON) 형식이 잘못되어 파싱할 수 없습니다. (필드 타입 불일치, 필수 필드 누락 등)"), + INVALID_ORIGIN(HttpStatus.BAD_REQUEST, "잘못된 Origin입니다."), + UNSUPPORTED_ORIGIN(HttpStatus.BAD_REQUEST, "지원하지 않는 Origin입니다."), + + // 405 + METHOD_NOT_ALLOWED(HttpStatus.METHOD_NOT_ALLOWED, "지원하지 않는 HTTP 메서드 입니다."), + + // 500 + INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "서버 에러. 관리자에게 문의하세요."), +} diff --git a/src/main/kotlin/com/ject/studytrip/global/exception/error/ErrorCode.kt b/src/main/kotlin/com/ject/studytrip/global/exception/error/ErrorCode.kt new file mode 100644 index 0000000..4727c15 --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/global/exception/error/ErrorCode.kt @@ -0,0 +1,9 @@ +package com.ject.studytrip.global.exception.error + +import org.springframework.http.HttpStatus + +interface ErrorCode { + val name: String + val status: HttpStatus + val message: String +} diff --git a/src/main/kotlin/com/ject/studytrip/global/exception/handler/GlobalExceptionHandler.kt b/src/main/kotlin/com/ject/studytrip/global/exception/handler/GlobalExceptionHandler.kt new file mode 100644 index 0000000..aa5ec1a --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/global/exception/handler/GlobalExceptionHandler.kt @@ -0,0 +1,47 @@ +package com.ject.studytrip.global.exception.handler + +import com.ject.studytrip.global.common.response.StandardResponse +import com.ject.studytrip.global.exception.CustomException +import com.ject.studytrip.global.exception.error.CommonErrorCode +import com.ject.studytrip.global.exception.error.ErrorCode +import com.ject.studytrip.global.exception.response.ErrorResponse +import org.slf4j.LoggerFactory +import org.springframework.http.ResponseEntity +import org.springframework.web.bind.annotation.ExceptionHandler +import org.springframework.web.bind.annotation.RestControllerAdvice + +@RestControllerAdvice +class GlobalExceptionHandler { + companion object { + private val log = LoggerFactory.getLogger(GlobalExceptionHandler::class.java) + } + + /** + * CustomException 예외 처리 + */ + @ExceptionHandler(CustomException::class) + fun handleCustomException(e: CustomException): ResponseEntity { + log.warn("CustomException: {}", e.message) + + return buildResponse(e.errorCode) + } + + /** + * 500번대 에러 처리 + */ + @ExceptionHandler(Exception::class) + fun handleException(e: Exception): ResponseEntity { + log.error("Internal Server Error: {}", e.message, e) + + return buildResponse(CommonErrorCode.INTERNAL_SERVER_ERROR) + } + + private fun buildResponse(errorCode: ErrorCode): ResponseEntity { + val errorResponse = ErrorResponse.of(errorCode.name, errorCode.message) + val response = StandardResponse.fail(errorCode.status.value(), errorResponse) + + return ResponseEntity + .status(errorCode.status) + .body(response) + } +} diff --git a/src/main/kotlin/com/ject/studytrip/global/exception/handler/ValidationExceptionHandler.kt b/src/main/kotlin/com/ject/studytrip/global/exception/handler/ValidationExceptionHandler.kt new file mode 100644 index 0000000..dff1ba3 --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/global/exception/handler/ValidationExceptionHandler.kt @@ -0,0 +1,162 @@ +package com.ject.studytrip.global.exception.handler + +import com.ject.studytrip.global.common.response.StandardResponse +import com.ject.studytrip.global.exception.error.CommonErrorCode +import com.ject.studytrip.global.exception.error.ErrorCode +import com.ject.studytrip.global.exception.response.ErrorResponse +import com.ject.studytrip.global.exception.response.FieldErrorResponse +import jakarta.validation.ConstraintViolationException +import org.slf4j.LoggerFactory +import org.springframework.core.Ordered +import org.springframework.core.annotation.Order +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.web.HttpRequestMethodNotSupportedException +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.method.annotation.MethodArgumentTypeMismatchException +import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler + +@RestControllerAdvice +@Order(Ordered.HIGHEST_PRECEDENCE) +class ValidationExceptionHandler : ResponseEntityExceptionHandler() { + companion object { + private val log = LoggerFactory.getLogger(ValidationExceptionHandler::class.java) + } + + /** + * ResponseEntityExceptionHandler 가 기본으로 처리하는 예외를 공통으로 처리 + */ + override fun handleExceptionInternal( + e: Exception, + body: Any?, + headers: HttpHeaders, + statusCode: HttpStatusCode, + request: WebRequest, + ): ResponseEntity { + val errorResponse = ErrorResponse.of(e::class.simpleName ?: "Exception", e.message ?: "") + + return super.handleExceptionInternal( + e, + errorResponse, + headers, + statusCode, + request, + ) ?: ResponseEntity.status(statusCode).body(errorResponse) + } + + /** + * 요청 본문(Json) 에서 유효성 제약 조건 위반 시 발생(바인딩 실패), 주로 RequestBody, RequestPart 에서 발생 + */ + override fun handleMethodArgumentNotValid( + e: MethodArgumentNotValidException, + headers: HttpHeaders, + statusCode: HttpStatusCode, + request: WebRequest, + ): ResponseEntity { + log.warn("MethodArgumentNotValidException: {}", e.message) + + val fieldErrors = + e.bindingResult.fieldErrors.map { + FieldErrorResponse.of( + it.field, + it.defaultMessage ?: "Invalid value", + ) + } + + return buildValidationResponse( + CommonErrorCode.METHOD_ARGUMENT_NOT_VALID, + statusCode.value(), + fieldErrors, + ) + } + + /** + * 요청 본문(JSON) 형식이 잘못되어 파싱할 수 없는 경우 발생 (필드 타입 불일치, 필수 필드 누락 시 발생) + */ + override fun handleHttpMessageNotReadable( + e: HttpMessageNotReadableException, + headers: HttpHeaders, + statusCode: HttpStatusCode, + request: WebRequest, + ): ResponseEntity { + log.warn("HttpMessageNotReadableException: {}", e.message) + + return buildErrorResponse(CommonErrorCode.INVALID_JSON_FORMAT) + } + + /** + * 지원하지 않는 HTTP method 요청 시 발생 + */ + override fun handleHttpRequestMethodNotSupported( + e: HttpRequestMethodNotSupportedException, + headers: HttpHeaders, + statusCode: HttpStatusCode, + request: WebRequest, + ): ResponseEntity { + log.warn("HttpRequestMethodNotSupportedException: {}", e.method) + + return buildErrorResponse(CommonErrorCode.METHOD_NOT_ALLOWED) + } + + /** + * RequestParam, PathVariable 등 메서드 파라미터에서 제약 조건을 위반해 바인딩 실패 시 발생 + */ + @ExceptionHandler(ConstraintViolationException::class) + fun handleConstraintViolationException(e: ConstraintViolationException): ResponseEntity { + log.warn("ConstraintViolationException: {}", e.message) + + val fieldErrors = + e.constraintViolations.map { violation -> + val field = + violation.propertyPath + .toString() + .substringAfterLast(".") + + FieldErrorResponse.of(field, violation.message) + } + + return buildValidationResponse( + CommonErrorCode.CONSTRAINT_VIOLATION, + HttpStatus.BAD_REQUEST.value(), + fieldErrors, + ) + } + + /** + * PathVariable, RequestParam, RequestHeader 에서 요청한 메서드 파라미터 타입이 일치하지 않은 경우 발생 + */ + @ExceptionHandler(MethodArgumentTypeMismatchException::class) + fun handleMethodArgumentTypeMismatchException(e: MethodArgumentTypeMismatchException): ResponseEntity { + log.warn("MethodArgumentTypeMismatchException: {}", e.message) + + return buildErrorResponse(CommonErrorCode.METHOD_ARGUMENT_TYPE_MISMATCH) + } + + private fun buildErrorResponse(errorCode: ErrorCode): ResponseEntity { + val errorResponse = ErrorResponse.of(errorCode.name, errorCode.message) + val response = StandardResponse.fail(errorCode.status.value(), errorResponse) + + return ResponseEntity + .status(errorCode.status) + .body(response) + } + + private fun buildValidationResponse( + errorCode: ErrorCode, + status: Int, + fieldErrors: List, + ): ResponseEntity { + val errorResponse = ErrorResponse.of(errorCode.name, errorCode.message, fieldErrors) + val response = StandardResponse.fail(status, errorResponse) + + return ResponseEntity + .status(status) + .body(response) + } +} diff --git a/src/main/kotlin/com/ject/studytrip/global/exception/response/ErrorResponse.kt b/src/main/kotlin/com/ject/studytrip/global/exception/response/ErrorResponse.kt new file mode 100644 index 0000000..7073a33 --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/global/exception/response/ErrorResponse.kt @@ -0,0 +1,20 @@ +package com.ject.studytrip.global.exception.response + +data class ErrorResponse( + val error: String, + val message: String, + val values: Any?, +) { + companion object { + fun of( + error: String, + message: String, + values: Any?, + ): ErrorResponse = ErrorResponse(error, message, values) + + fun of( + error: String, + message: String, + ): ErrorResponse = ErrorResponse(error, message, null) + } +} diff --git a/src/main/kotlin/com/ject/studytrip/global/exception/response/FieldErrorResponse.kt b/src/main/kotlin/com/ject/studytrip/global/exception/response/FieldErrorResponse.kt new file mode 100644 index 0000000..a7ccb89 --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/global/exception/response/FieldErrorResponse.kt @@ -0,0 +1,13 @@ +package com.ject.studytrip.global.exception.response + +data class FieldErrorResponse( + val field: String, + val reason: String, +) { + companion object { + fun of( + field: String, + reason: String, + ): FieldErrorResponse = FieldErrorResponse(field, reason) + } +} diff --git a/src/main/kotlin/com/ject/studytrip/global/security/CustomAuthenticationEntryPoint.kt b/src/main/kotlin/com/ject/studytrip/global/security/CustomAuthenticationEntryPoint.kt new file mode 100644 index 0000000..61bfc52 --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/global/security/CustomAuthenticationEntryPoint.kt @@ -0,0 +1,29 @@ +package com.ject.studytrip.global.security + +import com.ject.studytrip.auth.domain.error.AuthErrorCode +import com.ject.studytrip.global.security.handler.SecurityResponseHandler +import jakarta.servlet.http.HttpServletRequest +import jakarta.servlet.http.HttpServletResponse +import org.slf4j.LoggerFactory +import org.springframework.security.core.AuthenticationException +import org.springframework.security.web.AuthenticationEntryPoint +import org.springframework.stereotype.Component + +@Component +class CustomAuthenticationEntryPoint( + private val securityResponseHandler: SecurityResponseHandler, +) : AuthenticationEntryPoint { + companion object { + private val log = LoggerFactory.getLogger(CustomAuthenticationEntryPoint::class.java) + } + + override fun commence( + request: HttpServletRequest, + response: HttpServletResponse, + e: AuthenticationException, + ) { + log.warn("AuthenticationException: uri={}, message={}", request.requestURI, e.message) + + securityResponseHandler.sendResponse(response, AuthErrorCode.UNAUTHENTICATED) + } +} diff --git a/src/main/kotlin/com/ject/studytrip/global/security/filter/JwtAuthenticationFilter.kt b/src/main/kotlin/com/ject/studytrip/global/security/filter/JwtAuthenticationFilter.kt new file mode 100644 index 0000000..c7239f3 --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/global/security/filter/JwtAuthenticationFilter.kt @@ -0,0 +1,42 @@ +package com.ject.studytrip.global.security.filter + +import com.ject.studytrip.auth.application.service.TokenService +import jakarta.servlet.FilterChain +import jakarta.servlet.http.HttpServletRequest +import jakarta.servlet.http.HttpServletResponse +import org.springframework.http.HttpHeaders +import org.springframework.web.filter.OncePerRequestFilter + +class JwtAuthenticationFilter( + private val tokenService: TokenService, +) : OncePerRequestFilter() { + companion object { + private const val BEARER_PREFIX = "Bearer " + } + + override fun doFilterInternal( + request: HttpServletRequest, + response: HttpServletResponse, + filterChain: FilterChain, + ) { + extractBearerToken(request) + ?.let { accessToken -> + tokenService.validateActiveAccessToken(accessToken) + tokenService.setAuthenticationByAccessToken(accessToken) + } + + filterChain.doFilter(request, response) + } + + private fun extractBearerToken(request: HttpServletRequest): String? { + val header = request.getHeader(HttpHeaders.AUTHORIZATION) + + if (header.isNullOrBlank()) return null + if (!header.startsWith(BEARER_PREFIX)) return null + + return header + .removePrefix(BEARER_PREFIX) + .trim() + .takeIf { it.isNotBlank() } + } +} diff --git a/src/main/kotlin/com/ject/studytrip/global/security/filter/OriginExtractionFilter.kt b/src/main/kotlin/com/ject/studytrip/global/security/filter/OriginExtractionFilter.kt new file mode 100644 index 0000000..6a4a56c --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/global/security/filter/OriginExtractionFilter.kt @@ -0,0 +1,50 @@ +package com.ject.studytrip.global.security.filter + +import com.google.common.net.HttpHeaders +import com.ject.studytrip.global.common.constants.UrlConstants +import com.ject.studytrip.global.exception.error.CommonErrorCode +import com.ject.studytrip.global.security.handler.SecurityResponseHandler +import com.ject.studytrip.global.util.OriginArgumentUtil +import jakarta.servlet.FilterChain +import jakarta.servlet.http.HttpServletRequest +import jakarta.servlet.http.HttpServletResponse +import org.springframework.web.filter.OncePerRequestFilter + +class OriginExtractionFilter( + private val securityResponseHandler: SecurityResponseHandler, +) : OncePerRequestFilter() { + companion object { + private const val ATTRIBUTE_NAME = "origin" + } + + override fun doFilterInternal( + request: HttpServletRequest, + response: HttpServletResponse, + filterChain: FilterChain, + ) { + val origin = resolveOrigin(request) + + // origin null 검증 + if (origin == null) { + securityResponseHandler.sendResponse(response, CommonErrorCode.INVALID_ORIGIN) + return + } + + // 허용된 origin 검증 + if (origin !in UrlConstants.CORS_DOMAINS) { + securityResponseHandler.sendResponse(response, CommonErrorCode.UNSUPPORTED_ORIGIN) + return + } + + request.setAttribute(ATTRIBUTE_NAME, origin) + filterChain.doFilter(request, response) + } + + private fun resolveOrigin(request: HttpServletRequest): String? = + OriginArgumentUtil.resolveOrigin( + request.getHeader(HttpHeaders.ORIGIN), + request.scheme, + request.serverName, + request.serverPort, + ) +} diff --git a/src/main/kotlin/com/ject/studytrip/global/security/handler/CustomAccessDeniedHandler.kt b/src/main/kotlin/com/ject/studytrip/global/security/handler/CustomAccessDeniedHandler.kt new file mode 100644 index 0000000..4232a88 --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/global/security/handler/CustomAccessDeniedHandler.kt @@ -0,0 +1,28 @@ +package com.ject.studytrip.global.security.handler + +import com.ject.studytrip.auth.domain.error.AuthErrorCode +import jakarta.servlet.http.HttpServletRequest +import jakarta.servlet.http.HttpServletResponse +import org.slf4j.LoggerFactory +import org.springframework.security.access.AccessDeniedException +import org.springframework.security.web.access.AccessDeniedHandler +import org.springframework.stereotype.Component + +@Component +class CustomAccessDeniedHandler( + private val securityResponseHandler: SecurityResponseHandler, +) : AccessDeniedHandler { + companion object { + private val log = LoggerFactory.getLogger(CustomAccessDeniedHandler::class.java) + } + + override fun handle( + request: HttpServletRequest, + response: HttpServletResponse, + e: AccessDeniedException, + ) { + log.warn("AccessDeniedException: uri={}, message={}", request.requestURI, e.message) + + securityResponseHandler.sendResponse(response, AuthErrorCode.ACCESS_DENIED) + } +} diff --git a/src/main/kotlin/com/ject/studytrip/global/security/handler/SecurityResponseHandler.kt b/src/main/kotlin/com/ject/studytrip/global/security/handler/SecurityResponseHandler.kt new file mode 100644 index 0000000..ac6f632 --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/global/security/handler/SecurityResponseHandler.kt @@ -0,0 +1,32 @@ +package com.ject.studytrip.global.security.handler + +import com.fasterxml.jackson.databind.ObjectMapper +import com.ject.studytrip.global.common.response.StandardResponse +import com.ject.studytrip.global.exception.error.ErrorCode +import com.ject.studytrip.global.exception.response.ErrorResponse +import jakarta.servlet.http.HttpServletResponse +import org.springframework.stereotype.Component + +@Component +class SecurityResponseHandler( + private val objectMapper: ObjectMapper, +) { + companion object { + private const val CONTENT_TYPE = "application/json;charset=UTF-8" + } + + fun sendResponse( + response: HttpServletResponse, + errorCode: ErrorCode, + ) { + val errorResponse = ErrorResponse.of(errorCode.name, errorCode.message) + val standardResponse = StandardResponse.fail(errorCode.status.value(), errorResponse) + val json = objectMapper.writeValueAsString(standardResponse) + + response.apply { + status = errorCode.status.value() + contentType = CONTENT_TYPE + writer.write(json) + } + } +} diff --git a/src/main/kotlin/com/ject/studytrip/global/util/DateUtil.kt b/src/main/kotlin/com/ject/studytrip/global/util/DateUtil.kt new file mode 100644 index 0000000..6445993 --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/global/util/DateUtil.kt @@ -0,0 +1,15 @@ +package com.ject.studytrip.global.util + +import java.time.LocalDate +import java.time.LocalDateTime +import java.time.format.DateTimeFormatter + +object DateUtil { + private val DEFAULT_DATE_FORMATTER: DateTimeFormatter = DateTimeFormatter.ISO_DATE // yyyy-MM-dd + + private val DEFAULT_DATETIME_FORMATTER: DateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm") + + fun formatDate(date: LocalDate): String = date.format(DEFAULT_DATE_FORMATTER) + + fun formatDateTime(dateTime: LocalDateTime): String = dateTime.format(DEFAULT_DATETIME_FORMATTER) +} diff --git a/src/main/kotlin/com/ject/studytrip/global/util/EntityExtensions.kt b/src/main/kotlin/com/ject/studytrip/global/util/EntityExtensions.kt new file mode 100644 index 0000000..79b02a4 --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/global/util/EntityExtensions.kt @@ -0,0 +1,8 @@ +package com.ject.studytrip.global.util + +object EntityExtensions { + fun Long?.requireId(): Long = + requireNotNull(this) { + "엔티티가 아직 저장되지 않아 ID가 존재하지 않습니다." + } +} diff --git a/src/main/kotlin/com/ject/studytrip/global/util/FilenameUtil.kt b/src/main/kotlin/com/ject/studytrip/global/util/FilenameUtil.kt new file mode 100644 index 0000000..8934b9f --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/global/util/FilenameUtil.kt @@ -0,0 +1,13 @@ +package com.ject.studytrip.global.util + +import java.util.UUID + +object FilenameUtil { + fun createNewFilename(ext: String): String = "${UUID.randomUUID()}.$ext" + + fun extractExtension(filename: String?): String? = + filename + ?.substringAfterLast('.', "") + ?.takeIf { it.isNotBlank() } + ?.lowercase() +} diff --git a/src/main/kotlin/com/ject/studytrip/global/util/OriginArgumentUtil.kt b/src/main/kotlin/com/ject/studytrip/global/util/OriginArgumentUtil.kt new file mode 100644 index 0000000..9c225a2 --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/global/util/OriginArgumentUtil.kt @@ -0,0 +1,60 @@ +package com.ject.studytrip.global.util + +import java.net.URI + +object OriginArgumentUtil { + private const val NULL_ORIGIN = "null" + private const val HTTP = "http" + private const val HTTPS = "https" + private const val DEFAULT_HTTP_PORT = 80 + private const val DEFAULT_HTTPS_PORT = 443 + private const val MIN_PORT = 1 + private const val MAX_PORT = 65535 + + fun resolveOrigin( + origin: String?, + scheme: String, + host: String, + port: Int, + ): String? { + val headerOrigin = + origin + ?.trim() + ?.takeIf { it.isNotBlank() && !it.equals(NULL_ORIGIN, ignoreCase = true) } + + return canonicalizeOrigin(headerOrigin ?: "$scheme://$host:$port") + } + + private fun canonicalizeOrigin(raw: String?): String? { + val trimmed = raw?.trim().takeUnless { it.isNullOrBlank() } ?: return null + + val uri = runCatching { URI.create(trimmed) }.getOrNull() ?: return null + + val scheme = + uri.scheme + ?.lowercase() + ?.takeIf { it == HTTP || it == HTTPS } + ?: return null + + val host = uri.host ?: return null + + val port = uri.port + if (port != -1 && port !in MIN_PORT..MAX_PORT) return null + + return buildString { + append(scheme) + append("://") + append(host) + + if (port != -1 && !isDefaultPort(scheme, port)) { + append(":") + append(port) + } + } + } + + private fun isDefaultPort( + scheme: String, + port: Int, + ): Boolean = (scheme == HTTP && port == DEFAULT_HTTP_PORT) || (scheme == HTTPS && port == DEFAULT_HTTPS_PORT) +} diff --git a/src/main/kotlin/com/ject/studytrip/image/application/service/ImageService.kt b/src/main/kotlin/com/ject/studytrip/image/application/service/ImageService.kt index 790e778..06c2788 100644 --- a/src/main/kotlin/com/ject/studytrip/image/application/service/ImageService.kt +++ b/src/main/kotlin/com/ject/studytrip/image/application/service/ImageService.kt @@ -42,7 +42,7 @@ class ImageService( ImagePolicy.validateExtension(ext) // 새로운 파일명 생성 - val filename = FilenameUtil.createNewFilename(ext) + val filename = FilenameUtil.createNewFilename(requireNotNull(ext)) // 임시 키 생성 val tmpKey = ImageKeyFactory.createTmpKey(keyPrefix, id, filename) diff --git a/src/main/kotlin/com/ject/studytrip/image/domain/error/ImageErrorCode.kt b/src/main/kotlin/com/ject/studytrip/image/domain/error/ImageErrorCode.kt index 91e641b..3a5a5c9 100644 --- a/src/main/kotlin/com/ject/studytrip/image/domain/error/ImageErrorCode.kt +++ b/src/main/kotlin/com/ject/studytrip/image/domain/error/ImageErrorCode.kt @@ -4,8 +4,8 @@ import com.ject.studytrip.global.exception.error.ErrorCode import org.springframework.http.HttpStatus enum class ImageErrorCode( - private val status: HttpStatus, - private val message: String, + override val status: HttpStatus, + override val message: String, ) : ErrorCode { // 400 INVALID_IMAGE_EXTENSION(HttpStatus.BAD_REQUEST, "유효하지 않은 이미지 확장자 입니다."), @@ -14,11 +14,4 @@ enum class ImageErrorCode( INVALID_IMAGE_KEY(HttpStatus.BAD_REQUEST, "유효하지 않은 이미지 키 입니다."), IMAGE_SIZE_EXCEEDED(HttpStatus.BAD_REQUEST, "이미지 파일 크기가 허용된 최대 크기(5MB)를 초과했습니다."), EMPTY_IMAGE(HttpStatus.BAD_REQUEST, "이미지 파일이 비어 있습니다."), - ; - - override fun getName(): String = name - - override fun getStatus(): HttpStatus = status - - override fun getMessage(): String = message } diff --git a/src/main/kotlin/com/ject/studytrip/image/infra/s3/error/S3ErrorCode.kt b/src/main/kotlin/com/ject/studytrip/image/infra/s3/error/S3ErrorCode.kt index f470e65..bf77a25 100644 --- a/src/main/kotlin/com/ject/studytrip/image/infra/s3/error/S3ErrorCode.kt +++ b/src/main/kotlin/com/ject/studytrip/image/infra/s3/error/S3ErrorCode.kt @@ -4,16 +4,9 @@ import com.ject.studytrip.global.exception.error.ErrorCode import org.springframework.http.HttpStatus enum class S3ErrorCode( - private val status: HttpStatus, - private val message: String, + override val status: HttpStatus, + override val message: String, ) : ErrorCode { // 502 S3_STORAGE_SERVER_ERROR(HttpStatus.BAD_GATEWAY, "Storage 서버 에러가 발생했습니다."), - ; - - override fun getName(): String = name - - override fun getStatus(): HttpStatus = status - - override fun getMessage(): String = message } diff --git a/src/main/kotlin/com/ject/studytrip/image/infra/tika/error/TikaErrorCode.kt b/src/main/kotlin/com/ject/studytrip/image/infra/tika/error/TikaErrorCode.kt index 8e0490f..c070e5d 100644 --- a/src/main/kotlin/com/ject/studytrip/image/infra/tika/error/TikaErrorCode.kt +++ b/src/main/kotlin/com/ject/studytrip/image/infra/tika/error/TikaErrorCode.kt @@ -4,16 +4,9 @@ import com.ject.studytrip.global.exception.error.ErrorCode import org.springframework.http.HttpStatus enum class TikaErrorCode( - private val status: HttpStatus, - private val message: String, + override val status: HttpStatus, + override val message: String, ) : ErrorCode { // 500 TIKA_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "Tika 서버 에러가 발생했습니다."), - ; - - override fun getName(): String = name - - override fun getStatus(): HttpStatus = status - - override fun getMessage(): String = message } diff --git a/src/main/kotlin/com/ject/studytrip/member/application/dto/CreateMemberCommand.kt b/src/main/kotlin/com/ject/studytrip/member/application/dto/CreateMemberCommand.kt index c3de148..61d23d9 100644 --- a/src/main/kotlin/com/ject/studytrip/member/application/dto/CreateMemberCommand.kt +++ b/src/main/kotlin/com/ject/studytrip/member/application/dto/CreateMemberCommand.kt @@ -6,15 +6,4 @@ data class CreateMemberCommand( val profileImage: String?, val nickname: String, val category: String, -) { - companion object { - @JvmStatic - fun of( - socialId: String, - email: String, - profileImage: String?, - nickname: String, - category: String, - ): CreateMemberCommand = CreateMemberCommand(socialId, email, profileImage, nickname, category) - } -} +) diff --git a/src/main/kotlin/com/ject/studytrip/member/application/dto/MemberDetail.kt b/src/main/kotlin/com/ject/studytrip/member/application/dto/MemberDetail.kt index 3fbe471..516da3c 100644 --- a/src/main/kotlin/com/ject/studytrip/member/application/dto/MemberDetail.kt +++ b/src/main/kotlin/com/ject/studytrip/member/application/dto/MemberDetail.kt @@ -6,13 +6,4 @@ data class MemberDetail( val memberInfo: MemberInfo, val tripCount: TripCount, val studyLogCount: Long, -) { - companion object { - @JvmStatic - fun from( - memberInfo: MemberInfo, - tripCount: TripCount, - studyLogCount: Long, - ): MemberDetail = MemberDetail(memberInfo, tripCount, studyLogCount) - } -} +) diff --git a/src/main/kotlin/com/ject/studytrip/member/application/dto/MemberInfo.kt b/src/main/kotlin/com/ject/studytrip/member/application/dto/MemberInfo.kt index 5a130b4..2a4731c 100644 --- a/src/main/kotlin/com/ject/studytrip/member/application/dto/MemberInfo.kt +++ b/src/main/kotlin/com/ject/studytrip/member/application/dto/MemberInfo.kt @@ -1,6 +1,7 @@ package com.ject.studytrip.member.application.dto import com.ject.studytrip.global.util.DateUtil +import com.ject.studytrip.global.util.EntityExtensions.requireId import com.ject.studytrip.member.domain.model.Member import com.ject.studytrip.member.domain.model.MemberCategory import com.ject.studytrip.member.domain.model.MemberRole @@ -20,10 +21,9 @@ data class MemberInfo( val deletedAt: String?, ) { companion object { - @JvmStatic fun from(member: Member): MemberInfo = MemberInfo( - member.id, + member.id.requireId(), member.socialProvider, member.socialId, member.email, @@ -31,8 +31,8 @@ data class MemberInfo( member.profileImage, member.category, member.role, - DateUtil.formatDateTime(member.createdAt), - DateUtil.formatDateTime(member.updatedAt), + DateUtil.formatDateTime(requireNotNull(member.createdAt)), + DateUtil.formatDateTime(requireNotNull(member.updatedAt)), member.deletedAt?.let { DateUtil.formatDateTime(it) }, ) } diff --git a/src/main/kotlin/com/ject/studytrip/member/application/dto/PresignedProfileImageInfo.kt b/src/main/kotlin/com/ject/studytrip/member/application/dto/PresignedProfileImageInfo.kt index 272adf6..ed8a13c 100644 --- a/src/main/kotlin/com/ject/studytrip/member/application/dto/PresignedProfileImageInfo.kt +++ b/src/main/kotlin/com/ject/studytrip/member/application/dto/PresignedProfileImageInfo.kt @@ -4,13 +4,4 @@ data class PresignedProfileImageInfo( val memberId: Long, val tmpKey: String, val presignedUrl: String, -) { - companion object { - @JvmStatic - fun of( - memberId: Long, - tmpKey: String, - presignedUrl: String, - ): PresignedProfileImageInfo = PresignedProfileImageInfo(memberId, tmpKey, presignedUrl) - } -} +) diff --git a/src/main/kotlin/com/ject/studytrip/member/application/facade/MemberFacade.kt b/src/main/kotlin/com/ject/studytrip/member/application/facade/MemberFacade.kt index 48264aa..ae3e8e4 100644 --- a/src/main/kotlin/com/ject/studytrip/member/application/facade/MemberFacade.kt +++ b/src/main/kotlin/com/ject/studytrip/member/application/facade/MemberFacade.kt @@ -1,6 +1,7 @@ package com.ject.studytrip.member.application.facade import com.ject.studytrip.global.common.constants.CacheNameConstants.MEMBER +import com.ject.studytrip.global.util.EntityExtensions.requireId import com.ject.studytrip.image.application.service.ImageService import com.ject.studytrip.member.application.dto.MemberDetail import com.ject.studytrip.member.application.dto.MemberInfo @@ -114,7 +115,7 @@ class MemberFacade( val memberInfo = MemberInfo.from(member) - return MemberDetail.from(memberInfo, tripCount, studyLogCount) + return MemberDetail(memberInfo, tripCount, studyLogCount) } @Transactional(readOnly = true) @@ -126,7 +127,7 @@ class MemberFacade( val info = imageService.presign(MEMBER_PROFILE_IMAGE_KEY_PREFIX, member.id.toString(), request.originFilename) - return PresignedProfileImageInfo.of(member.id, info.tmpKey, info.presignedUrl) + return PresignedProfileImageInfo(member.id.requireId(), info.tmpKey, info.presignedUrl) } @CacheEvict( @@ -148,18 +149,19 @@ class MemberFacade( memberCommandService.updateProfileImage(member, finalKey) } - private fun collectImageUrlsForMember(member: Member): List = - buildList { - // TripReport 이미지 목록 조회 - addAll(tripReportQueryService.getTripReportImageUrlsByMemberId(member.id)) + private fun collectImageUrlsForMember(member: Member): List { + val memberId = member.id.requireId() - // StudyLog 이미지 목록 조회 - addAll(studyLogQueryService.getStudyLogImageUrlsByMemberId(member.id)) + val tripReportImages = tripReportQueryService.getTripReportImageUrlsByMemberId(memberId) + val studyLogImages = studyLogQueryService.getStudyLogImageUrlsByMemberId(memberId) + val profileImage = member.profileImage?.takeUnless { it.isBlank() } - member.profileImage - ?.takeIf { it.isNotBlank() } - ?.let { add(it) } + return buildList { + addAll(tripReportImages) + addAll(studyLogImages) + profileImage?.let { add(it) } } + } private fun cascadeHardDeleteByMemberId(memberId: Long) { // 자식 -> 부모 순으로 삭제 진행 diff --git a/src/main/kotlin/com/ject/studytrip/member/domain/error/MemberErrorCode.kt b/src/main/kotlin/com/ject/studytrip/member/domain/error/MemberErrorCode.kt index 228e955..4d47510 100644 --- a/src/main/kotlin/com/ject/studytrip/member/domain/error/MemberErrorCode.kt +++ b/src/main/kotlin/com/ject/studytrip/member/domain/error/MemberErrorCode.kt @@ -4,24 +4,16 @@ import com.ject.studytrip.global.exception.error.ErrorCode import org.springframework.http.HttpStatus enum class MemberErrorCode( - private val status: HttpStatus, - private val message: String, + override val status: HttpStatus, + override val message: String, ) : ErrorCode { // 400 MEMBER_ALREADY_DELETED(HttpStatus.BAD_REQUEST, "이미 삭제된 멤버입니다."), MEMBER_NOT_DELETED(HttpStatus.BAD_REQUEST, "멤버가 아직 삭제되지 않았습니다."), - INVALID_MEMBER_CATEGORY(HttpStatus.BAD_REQUEST, "멤버 카테고리가 누락되거나 올바르지 않습니다."), // 404 MEMBER_NOT_FOUND(HttpStatus.NOT_FOUND, "요창한 멤버를 찾을 수 없습니다."), // 409 MEMBER_ALREADY_EXISTS(HttpStatus.CONFLICT, "이미 회원가입된 멤버입니다."), - ; - - override fun getName(): String = name - - override fun getStatus(): HttpStatus = status - - override fun getMessage(): String = message } diff --git a/src/main/kotlin/com/ject/studytrip/member/domain/factory/MemberFactory.kt b/src/main/kotlin/com/ject/studytrip/member/domain/factory/MemberFactory.kt index 89f0480..7edda27 100644 --- a/src/main/kotlin/com/ject/studytrip/member/domain/factory/MemberFactory.kt +++ b/src/main/kotlin/com/ject/studytrip/member/domain/factory/MemberFactory.kt @@ -6,7 +6,6 @@ import com.ject.studytrip.member.domain.model.MemberRole import com.ject.studytrip.member.domain.model.SocialProvider object MemberFactory { - @JvmStatic fun createFromKakao( kakaoId: String, email: String, diff --git a/src/main/kotlin/com/ject/studytrip/member/domain/model/Member.kt b/src/main/kotlin/com/ject/studytrip/member/domain/model/Member.kt new file mode 100644 index 0000000..b16e9e9 --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/member/domain/model/Member.kt @@ -0,0 +1,74 @@ +package com.ject.studytrip.member.domain.model + +import com.ject.studytrip.global.common.entity.BaseTimeEntity +import jakarta.persistence.Column +import jakarta.persistence.Entity +import jakarta.persistence.EnumType +import jakarta.persistence.Enumerated +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Id +import org.springframework.util.StringUtils.hasText +import java.time.LocalDateTime + +@Entity +class Member protected constructor( + @Enumerated(EnumType.STRING) + @Column(nullable = false) + var socialProvider: SocialProvider, + @Column(nullable = false) + var socialId: String, + @Column(nullable = false, unique = true) + var email: String, + @Column(nullable = false) + var nickname: String, + var profileImage: String?, + @Enumerated(EnumType.STRING) + var category: MemberCategory, + @Enumerated(EnumType.STRING) + var role: MemberRole, +) : BaseTimeEntity() { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + var id: Long? = null + protected set + + companion object { + fun of( + socialProvider: SocialProvider, + socialId: String, + email: String, + nickname: String, + profileImage: String?, + category: MemberCategory, + role: MemberRole, + ): Member = Member(socialProvider, socialId, email, nickname, profileImage, category, role) + } + + fun update( + nickname: String?, + category: MemberCategory?, + ) { + nickname + ?.takeIf { hasText(it) && it != this.nickname } + ?.let { this.nickname = it } + + category + ?.takeIf { it != this.category } + ?.let { this.category = it } + } + + fun updateProfileImage(profileImage: String) { + if (hasText(profileImage)) { + this.profileImage = profileImage + } + } + + fun updateDeletedAt(now: LocalDateTime = LocalDateTime.now()) { + markDeleted(now) + } + + fun restoreDeletedAt() { + this.deletedAt = null + } +} diff --git a/src/main/kotlin/com/ject/studytrip/member/domain/model/MemberCategory.kt b/src/main/kotlin/com/ject/studytrip/member/domain/model/MemberCategory.kt new file mode 100644 index 0000000..8970bf7 --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/member/domain/model/MemberCategory.kt @@ -0,0 +1,13 @@ +package com.ject.studytrip.member.domain.model + +enum class MemberCategory { + STUDENT, + WORKER, + FREELANCER, + JOBSEEKER, + ; + + companion object { + fun from(category: String): MemberCategory = valueOf(category) + } +} diff --git a/src/main/kotlin/com/ject/studytrip/member/domain/model/MemberRole.kt b/src/main/kotlin/com/ject/studytrip/member/domain/model/MemberRole.kt new file mode 100644 index 0000000..597d179 --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/member/domain/model/MemberRole.kt @@ -0,0 +1,7 @@ +package com.ject.studytrip.member.domain.model + +enum class MemberRole { + ROLE_USER, + ROLE_ADMIN, + ROLE_OWNER, +} diff --git a/src/main/kotlin/com/ject/studytrip/member/domain/model/SocialProvider.kt b/src/main/kotlin/com/ject/studytrip/member/domain/model/SocialProvider.kt new file mode 100644 index 0000000..ea8ca22 --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/member/domain/model/SocialProvider.kt @@ -0,0 +1,6 @@ +package com.ject.studytrip.member.domain.model + +enum class SocialProvider { + KAKAO, + GOOGLE, +} diff --git a/src/main/kotlin/com/ject/studytrip/member/domain/policy/MemberPolicy.kt b/src/main/kotlin/com/ject/studytrip/member/domain/policy/MemberPolicy.kt index cdd82d8..36c2c79 100644 --- a/src/main/kotlin/com/ject/studytrip/member/domain/policy/MemberPolicy.kt +++ b/src/main/kotlin/com/ject/studytrip/member/domain/policy/MemberPolicy.kt @@ -6,13 +6,13 @@ import com.ject.studytrip.member.domain.model.Member object MemberPolicy { fun validateNotDeleted(member: Member) { - if (member.isDeleted) { + if (member.isDeleted()) { throw CustomException(MemberErrorCode.MEMBER_ALREADY_DELETED) } } fun validateDeleted(member: Member) { - if (!member.isDeleted) { + if (!member.isDeleted()) { throw CustomException(MemberErrorCode.MEMBER_NOT_DELETED) } } diff --git a/src/main/kotlin/com/ject/studytrip/member/domain/repository/MemberCommandRepository.kt b/src/main/kotlin/com/ject/studytrip/member/domain/repository/MemberCommandRepository.kt new file mode 100644 index 0000000..cdeb5fe --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/member/domain/repository/MemberCommandRepository.kt @@ -0,0 +1,5 @@ +package com.ject.studytrip.member.domain.repository + +interface MemberCommandRepository { + fun deleteAllByDeletedAtIsNotNull(): Long +} diff --git a/src/main/kotlin/com/ject/studytrip/member/domain/repository/MemberQueryRepository.kt b/src/main/kotlin/com/ject/studytrip/member/domain/repository/MemberQueryRepository.kt new file mode 100644 index 0000000..43449b0 --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/member/domain/repository/MemberQueryRepository.kt @@ -0,0 +1,8 @@ +package com.ject.studytrip.member.domain.repository + +import com.ject.studytrip.member.domain.model.MemberRole +import java.util.Optional + +interface MemberQueryRepository { + fun findMemberRoleById(memberId: Long): Optional +} diff --git a/src/main/kotlin/com/ject/studytrip/member/infra/querydsl/MemberCommandRepositoryAdapter.kt b/src/main/kotlin/com/ject/studytrip/member/infra/querydsl/MemberCommandRepositoryAdapter.kt new file mode 100644 index 0000000..db82e37 --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/member/infra/querydsl/MemberCommandRepositoryAdapter.kt @@ -0,0 +1,17 @@ +package com.ject.studytrip.member.infra.querydsl + +import com.ject.studytrip.member.domain.model.QMember.member +import com.ject.studytrip.member.domain.repository.MemberCommandRepository +import com.querydsl.jpa.impl.JPAQueryFactory +import org.springframework.stereotype.Repository + +@Repository +class MemberCommandRepositoryAdapter( + private val queryFactory: JPAQueryFactory, +) : MemberCommandRepository { + override fun deleteAllByDeletedAtIsNotNull(): Long = + queryFactory + .delete(member) + .where(member.deletedAt.isNotNull) + .execute() +} diff --git a/src/main/kotlin/com/ject/studytrip/member/infra/querydsl/MemberQueryRepositoryAdapter.kt b/src/main/kotlin/com/ject/studytrip/member/infra/querydsl/MemberQueryRepositoryAdapter.kt new file mode 100644 index 0000000..44814d9 --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/member/infra/querydsl/MemberQueryRepositoryAdapter.kt @@ -0,0 +1,24 @@ +package com.ject.studytrip.member.infra.querydsl + +import com.ject.studytrip.member.domain.model.MemberRole +import com.ject.studytrip.member.domain.model.QMember.member +import com.ject.studytrip.member.domain.repository.MemberQueryRepository +import com.querydsl.jpa.impl.JPAQueryFactory +import org.springframework.stereotype.Repository +import java.util.Optional + +@Repository +class MemberQueryRepositoryAdapter( + private val queryFactory: JPAQueryFactory, +) : MemberQueryRepository { + override fun findMemberRoleById(memberId: Long): Optional { + val memberRole: MemberRole? = + queryFactory + .select(member.role) + .from(member) + .where(member.id.eq(memberId)) + .fetchOne() + + return Optional.ofNullable(memberRole) + } +} diff --git a/src/main/kotlin/com/ject/studytrip/member/presentation/controller/MemberController.kt b/src/main/kotlin/com/ject/studytrip/member/presentation/controller/MemberController.kt index 1f70e8c..add49f4 100644 --- a/src/main/kotlin/com/ject/studytrip/member/presentation/controller/MemberController.kt +++ b/src/main/kotlin/com/ject/studytrip/member/presentation/controller/MemberController.kt @@ -128,7 +128,7 @@ class MemberController( .body( StandardResponse.success( HttpStatus.OK.value(), - PresignProfileImageResponse.of(result.memberId, result.tmpKey, result.presignedUrl), + PresignProfileImageResponse(result.memberId, result.tmpKey, result.presignedUrl), ), ) } diff --git a/src/main/kotlin/com/ject/studytrip/member/presentation/dto/response/LoadMemberDetailResponse.kt b/src/main/kotlin/com/ject/studytrip/member/presentation/dto/response/LoadMemberDetailResponse.kt index c24a9b5..e8a62b3 100644 --- a/src/main/kotlin/com/ject/studytrip/member/presentation/dto/response/LoadMemberDetailResponse.kt +++ b/src/main/kotlin/com/ject/studytrip/member/presentation/dto/response/LoadMemberDetailResponse.kt @@ -24,7 +24,6 @@ data class LoadMemberDetailResponse( val studyLogCount: Long, ) { companion object { - @JvmStatic fun of( memberInfo: MemberInfo, tripCount: TripCount, diff --git a/src/main/kotlin/com/ject/studytrip/member/presentation/dto/response/PresignProfileImageResponse.kt b/src/main/kotlin/com/ject/studytrip/member/presentation/dto/response/PresignProfileImageResponse.kt index 69787d4..5dc2450 100644 --- a/src/main/kotlin/com/ject/studytrip/member/presentation/dto/response/PresignProfileImageResponse.kt +++ b/src/main/kotlin/com/ject/studytrip/member/presentation/dto/response/PresignProfileImageResponse.kt @@ -9,13 +9,4 @@ data class PresignProfileImageResponse( val tmpKey: String, @field:Schema(description = "멤버 프로필 이미지 업로드용 Presigned URL") val presignedUrl: String, -) { - companion object { - @JvmStatic - fun of( - memberId: Long, - tmpKey: String, - presignedUrl: String, - ): PresignProfileImageResponse = PresignProfileImageResponse(memberId, tmpKey, presignedUrl) - } -} +) diff --git a/src/main/kotlin/com/ject/studytrip/mission/application/dto/DailyMissionInfo.kt b/src/main/kotlin/com/ject/studytrip/mission/application/dto/DailyMissionInfo.kt index 32da416..c70d972 100644 --- a/src/main/kotlin/com/ject/studytrip/mission/application/dto/DailyMissionInfo.kt +++ b/src/main/kotlin/com/ject/studytrip/mission/application/dto/DailyMissionInfo.kt @@ -1,5 +1,6 @@ package com.ject.studytrip.mission.application.dto +import com.ject.studytrip.global.util.EntityExtensions.requireId import com.ject.studytrip.mission.domain.model.DailyMission data class DailyMissionInfo( @@ -7,11 +8,7 @@ data class DailyMissionInfo( val missionInfo: MissionInfo, ) { companion object { - @JvmStatic fun from(dailyMission: DailyMission): DailyMissionInfo = - DailyMissionInfo( - dailyMission.getId(), - MissionInfo.from(dailyMission.getMission()), - ) + DailyMissionInfo(dailyMission.id.requireId(), MissionInfo.from(dailyMission.mission)) } } diff --git a/src/main/kotlin/com/ject/studytrip/mission/application/dto/MissionInfo.kt b/src/main/kotlin/com/ject/studytrip/mission/application/dto/MissionInfo.kt index cc9581d..ffdf50c 100644 --- a/src/main/kotlin/com/ject/studytrip/mission/application/dto/MissionInfo.kt +++ b/src/main/kotlin/com/ject/studytrip/mission/application/dto/MissionInfo.kt @@ -1,6 +1,7 @@ package com.ject.studytrip.mission.application.dto import com.ject.studytrip.global.util.DateUtil +import com.ject.studytrip.global.util.EntityExtensions.requireId import com.ject.studytrip.mission.domain.model.Mission data class MissionInfo( @@ -12,15 +13,14 @@ data class MissionInfo( val deletedAt: String?, ) { companion object { - @JvmStatic fun from(mission: Mission): MissionInfo = MissionInfo( - mission.getId(), - mission.getName(), + mission.id.requireId(), + mission.name, mission.isCompleted(), - DateUtil.formatDateTime(mission.getCreatedAt()), - DateUtil.formatDateTime(mission.getUpdatedAt()), - mission.getDeletedAt()?.let { DateUtil.formatDateTime(it) }, + DateUtil.formatDateTime(requireNotNull(mission.createdAt)), + DateUtil.formatDateTime(requireNotNull(mission.updatedAt)), + mission.deletedAt?.let { DateUtil.formatDateTime(it) }, ) } } diff --git a/src/main/kotlin/com/ject/studytrip/mission/application/dto/MissionsInfo.kt b/src/main/kotlin/com/ject/studytrip/mission/application/dto/MissionsInfo.kt index 1c07ac3..ba9daa2 100644 --- a/src/main/kotlin/com/ject/studytrip/mission/application/dto/MissionsInfo.kt +++ b/src/main/kotlin/com/ject/studytrip/mission/application/dto/MissionsInfo.kt @@ -2,9 +2,4 @@ package com.ject.studytrip.mission.application.dto data class MissionsInfo( val missionInfos: List, -) { - companion object { - @JvmStatic - fun of(missionInfos: List): MissionsInfo = MissionsInfo(missionInfos) - } -} +) diff --git a/src/main/kotlin/com/ject/studytrip/mission/application/facade/MissionFacade.kt b/src/main/kotlin/com/ject/studytrip/mission/application/facade/MissionFacade.kt index b9c0897..90c995f 100644 --- a/src/main/kotlin/com/ject/studytrip/mission/application/facade/MissionFacade.kt +++ b/src/main/kotlin/com/ject/studytrip/mission/application/facade/MissionFacade.kt @@ -5,6 +5,7 @@ import com.ject.studytrip.global.common.constants.CacheNameConstants.STAMP import com.ject.studytrip.global.common.constants.CacheNameConstants.STAMPS import com.ject.studytrip.global.common.constants.CacheNameConstants.TRIP import com.ject.studytrip.global.common.constants.CacheNameConstants.TRIPS +import com.ject.studytrip.global.util.EntityExtensions.requireId import com.ject.studytrip.mission.application.dto.MissionInfo import com.ject.studytrip.mission.application.dto.MissionsInfo import com.ject.studytrip.mission.application.service.MissionCommandService @@ -93,7 +94,7 @@ class MissionFacade( request: UpdateMissionRequest, ) { val stamp = getValidStampForTripOwnedByMember(memberId, tripId, stampId) - val mission = missionQueryService.getValidMission(stamp.id, missionId) + val mission = missionQueryService.getValidMission(stamp.id.requireId(), missionId) missionCommandService.updateMissionNameIfPresent(mission, request) } @@ -123,7 +124,7 @@ class MissionFacade( missionId: Long, ) { val stamp = getValidStampForTripOwnedByMember(memberId, tripId, stampId) - val mission = missionQueryService.getValidMission(stamp.id, missionId) + val mission = missionQueryService.getValidMission(stamp.id.requireId(), missionId) missionCommandService.deleteMission(mission) stampCommandService.decreaseTotalMissions(stamp) @@ -140,9 +141,9 @@ class MissionFacade( stampId: Long, ): MissionsInfo { val stamp = getValidStampForTripOwnedByMember(memberId, tripId, stampId) - val missions = missionQueryService.getMissionsByStampId(stamp.id) + val missions = missionQueryService.getMissionsByStampId(stamp.id.requireId()) - return MissionsInfo.of(missions.map { MissionInfo.from(it) }) + return MissionsInfo(missions.map { MissionInfo.from(it) }) } private fun getValidStampForTripOwnedByMember( @@ -152,6 +153,6 @@ class MissionFacade( ): Stamp { val trip = tripQueryService.getValidTrip(memberId, tripId) - return stampQueryService.getValidStamp(trip.id, stampId) + return stampQueryService.getValidStamp(trip.id.requireId(), stampId) } } diff --git a/src/main/kotlin/com/ject/studytrip/mission/application/service/DailyMissionCommandService.kt b/src/main/kotlin/com/ject/studytrip/mission/application/service/DailyMissionCommandService.kt index 5b4e388..8cc88a4 100644 --- a/src/main/kotlin/com/ject/studytrip/mission/application/service/DailyMissionCommandService.kt +++ b/src/main/kotlin/com/ject/studytrip/mission/application/service/DailyMissionCommandService.kt @@ -16,11 +16,7 @@ class DailyMissionCommandService( fun createDailyMissions( dailyGoal: DailyGoal, missions: List, - ): List { - val dailyMissions = missions.map { DailyMissionFactory.create(it, dailyGoal) } - - return dailyMissionRepository.saveAll(dailyMissions) - } + ): List = dailyMissionRepository.saveAll(missions.map { DailyMissionFactory.create(it, dailyGoal) }) fun deleteDailyMission(dailyMission: DailyMission) = dailyMission.updateDeletedAt() diff --git a/src/main/kotlin/com/ject/studytrip/mission/application/service/DailyMissionQueryService.kt b/src/main/kotlin/com/ject/studytrip/mission/application/service/DailyMissionQueryService.kt index e3e891b..8185920 100644 --- a/src/main/kotlin/com/ject/studytrip/mission/application/service/DailyMissionQueryService.kt +++ b/src/main/kotlin/com/ject/studytrip/mission/application/service/DailyMissionQueryService.kt @@ -16,6 +16,7 @@ class DailyMissionQueryService( dailyMissionIds: List, ): List { val dailyMissions = dailyMissionRepository.findAllByIdIn(dailyMissionIds) + validateDailyMissions(dailyMissions, dailyMissionIds, dailyGoalId) return dailyMissions @@ -26,6 +27,7 @@ class DailyMissionQueryService( dailyMissionIds: List, ): List { val dailyMissions = dailyMissionQueryRepository.findAllWithMissionAndStampByIds(dailyMissionIds) + validateDailyMissions(dailyMissions, dailyMissionIds, dailyGoalId) return dailyMissions diff --git a/src/main/kotlin/com/ject/studytrip/mission/application/service/MissionCommandService.kt b/src/main/kotlin/com/ject/studytrip/mission/application/service/MissionCommandService.kt index 098cf80..57400ff 100644 --- a/src/main/kotlin/com/ject/studytrip/mission/application/service/MissionCommandService.kt +++ b/src/main/kotlin/com/ject/studytrip/mission/application/service/MissionCommandService.kt @@ -47,6 +47,7 @@ class MissionCommandService( fun validateAllMissionsCompletedByStampId(stampId: Long) { val exists = missionCommandRepository.existsByStampIdAndCompletedIsFalseAndDeletedAtIsNull(stampId) + MissionPolicy.validateNotAllCompleted(exists) } diff --git a/src/main/kotlin/com/ject/studytrip/mission/domain/error/DailyMissionErrorCode.kt b/src/main/kotlin/com/ject/studytrip/mission/domain/error/DailyMissionErrorCode.kt index 40a402a..f80031e 100644 --- a/src/main/kotlin/com/ject/studytrip/mission/domain/error/DailyMissionErrorCode.kt +++ b/src/main/kotlin/com/ject/studytrip/mission/domain/error/DailyMissionErrorCode.kt @@ -4,8 +4,8 @@ import com.ject.studytrip.global.exception.error.ErrorCode import org.springframework.http.HttpStatus enum class DailyMissionErrorCode( - private val status: HttpStatus, - private val message: String, + override val status: HttpStatus, + override val message: String, ) : ErrorCode { // 400 DAILY_MISSION_ALREADY_DELETED(HttpStatus.BAD_REQUEST, "이미 삭제된 데일리 미션입니다."), @@ -15,11 +15,4 @@ enum class DailyMissionErrorCode( // 404 DAILY_MISSION_NOT_FOUND(HttpStatus.NOT_FOUND, "요청한 데일리 미션을 찾을 수 없습니다."), - ; - - override fun getName(): String = name - - override fun getStatus(): HttpStatus = status - - override fun getMessage(): String = message } diff --git a/src/main/kotlin/com/ject/studytrip/mission/domain/error/MissionErrorCode.kt b/src/main/kotlin/com/ject/studytrip/mission/domain/error/MissionErrorCode.kt index c4e739c..38ecf4b 100644 --- a/src/main/kotlin/com/ject/studytrip/mission/domain/error/MissionErrorCode.kt +++ b/src/main/kotlin/com/ject/studytrip/mission/domain/error/MissionErrorCode.kt @@ -4,8 +4,8 @@ import com.ject.studytrip.global.exception.error.ErrorCode import org.springframework.http.HttpStatus enum class MissionErrorCode( - private val status: HttpStatus, - private val message: String, + override val status: HttpStatus, + override val message: String, ) : ErrorCode { // 400 MISSION_ALREADY_DELETED(HttpStatus.BAD_REQUEST, "이미 삭제된 미션입니다."), @@ -17,11 +17,4 @@ enum class MissionErrorCode( // 404 MISSION_NOT_FOUND(HttpStatus.NOT_FOUND, "요청한 미션을 찾을 수 없습니다."), - ; - - override fun getName(): String = name - - override fun getStatus(): HttpStatus = status - - override fun getMessage(): String = message } diff --git a/src/main/kotlin/com/ject/studytrip/mission/domain/factory/DailyMissionFactory.kt b/src/main/kotlin/com/ject/studytrip/mission/domain/factory/DailyMissionFactory.kt index 8eb540a..9f10122 100644 --- a/src/main/kotlin/com/ject/studytrip/mission/domain/factory/DailyMissionFactory.kt +++ b/src/main/kotlin/com/ject/studytrip/mission/domain/factory/DailyMissionFactory.kt @@ -5,7 +5,6 @@ import com.ject.studytrip.mission.domain.model.Mission import com.ject.studytrip.trip.domain.model.DailyGoal object DailyMissionFactory { - @JvmStatic fun create( mission: Mission, dailyGoal: DailyGoal, diff --git a/src/main/kotlin/com/ject/studytrip/mission/domain/factory/MissionFactory.kt b/src/main/kotlin/com/ject/studytrip/mission/domain/factory/MissionFactory.kt index de25df3..603f275 100644 --- a/src/main/kotlin/com/ject/studytrip/mission/domain/factory/MissionFactory.kt +++ b/src/main/kotlin/com/ject/studytrip/mission/domain/factory/MissionFactory.kt @@ -4,7 +4,6 @@ import com.ject.studytrip.mission.domain.model.Mission import com.ject.studytrip.stamp.domain.model.Stamp object MissionFactory { - @JvmStatic fun create( stamp: Stamp, name: String, diff --git a/src/main/kotlin/com/ject/studytrip/mission/domain/model/DailyMission.kt b/src/main/kotlin/com/ject/studytrip/mission/domain/model/DailyMission.kt new file mode 100644 index 0000000..d783d9e --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/mission/domain/model/DailyMission.kt @@ -0,0 +1,38 @@ +package com.ject.studytrip.mission.domain.model + +import com.ject.studytrip.global.common.entity.BaseTimeEntity +import com.ject.studytrip.trip.domain.model.DailyGoal +import jakarta.persistence.Entity +import jakarta.persistence.FetchType +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Id +import jakarta.persistence.JoinColumn +import jakarta.persistence.ManyToOne +import java.time.LocalDateTime + +@Entity +class DailyMission protected constructor( + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "mission_id", nullable = false) + var mission: Mission, + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "daily_goal_id", nullable = false) + var dailyGoal: DailyGoal, +) : BaseTimeEntity() { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + var id: Long? = null + protected set + + companion object { + fun of( + mission: Mission, + dailyGoal: DailyGoal, + ): DailyMission = DailyMission(mission, dailyGoal) + } + + fun updateDeletedAt(now: LocalDateTime = LocalDateTime.now()) { + markDeleted(now) + } +} diff --git a/src/main/kotlin/com/ject/studytrip/mission/domain/model/Mission.kt b/src/main/kotlin/com/ject/studytrip/mission/domain/model/Mission.kt new file mode 100644 index 0000000..ded0e8d --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/mission/domain/model/Mission.kt @@ -0,0 +1,52 @@ +package com.ject.studytrip.mission.domain.model + +import com.ject.studytrip.global.common.entity.BaseTimeEntity +import com.ject.studytrip.stamp.domain.model.Stamp +import jakarta.persistence.Column +import jakarta.persistence.Entity +import jakarta.persistence.FetchType +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Id +import jakarta.persistence.JoinColumn +import jakarta.persistence.ManyToOne +import org.springframework.util.StringUtils.hasText +import java.time.LocalDateTime + +@Entity +class Mission protected constructor( + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "stamp_id", nullable = false) + var stamp: Stamp, + @Column(nullable = false) + var name: String, + var completed: Boolean = false, +) : BaseTimeEntity() { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + var id: Long? = null + protected set + + companion object { + fun of( + stamp: Stamp, + name: String, + ): Mission = Mission(stamp, name, false) + } + + fun updateName(name: String) { + if (hasText(name)) { + this.name = name + } + } + + fun updateDeletedAt(now: LocalDateTime = LocalDateTime.now()) { + markDeleted(now) + } + + fun updateCompleted() { + this.completed = true + } + + fun isCompleted(): Boolean = completed +} diff --git a/src/main/kotlin/com/ject/studytrip/mission/domain/policy/DailyMissionPolicy.kt b/src/main/kotlin/com/ject/studytrip/mission/domain/policy/DailyMissionPolicy.kt index fdf6708..0677ce5 100644 --- a/src/main/kotlin/com/ject/studytrip/mission/domain/policy/DailyMissionPolicy.kt +++ b/src/main/kotlin/com/ject/studytrip/mission/domain/policy/DailyMissionPolicy.kt @@ -6,7 +6,7 @@ import com.ject.studytrip.mission.domain.model.DailyMission object DailyMissionPolicy { fun validateNotDeleted(dailyMission: DailyMission) { - if (dailyMission.isDeleted) { + if (dailyMission.isDeleted()) { throw CustomException(DailyMissionErrorCode.DAILY_MISSION_ALREADY_DELETED) } } diff --git a/src/main/kotlin/com/ject/studytrip/mission/domain/policy/MissionPolicy.kt b/src/main/kotlin/com/ject/studytrip/mission/domain/policy/MissionPolicy.kt index 9ced1e7..2a82a94 100644 --- a/src/main/kotlin/com/ject/studytrip/mission/domain/policy/MissionPolicy.kt +++ b/src/main/kotlin/com/ject/studytrip/mission/domain/policy/MissionPolicy.kt @@ -6,13 +6,13 @@ import com.ject.studytrip.mission.domain.model.Mission object MissionPolicy { fun validateNotDeleted(mission: Mission) { - if (mission.isDeleted) { + if (mission.isDeleted()) { throw CustomException(MissionErrorCode.MISSION_ALREADY_DELETED) } } fun validateNotCompleted(mission: Mission) { - if (mission.isCompleted) { + if (mission.isCompleted()) { throw CustomException(MissionErrorCode.MISSION_ALREADY_COMPLETED) } } diff --git a/src/main/kotlin/com/ject/studytrip/mission/domain/repository/DailyMissionCommandRepository.kt b/src/main/kotlin/com/ject/studytrip/mission/domain/repository/DailyMissionCommandRepository.kt new file mode 100644 index 0000000..b1f486a --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/mission/domain/repository/DailyMissionCommandRepository.kt @@ -0,0 +1,11 @@ +package com.ject.studytrip.mission.domain.repository + +interface DailyMissionCommandRepository { + fun deleteAllByDeletedAtIsNotNull(): Long + + fun deleteAllByDeletedMissionOwner(): Long + + fun deleteAllByDeletedDailyGoalOwner(): Long + + fun deleteAllByMemberId(memberId: Long): Long +} diff --git a/src/main/kotlin/com/ject/studytrip/mission/domain/repository/DailyMissionQueryRepository.kt b/src/main/kotlin/com/ject/studytrip/mission/domain/repository/DailyMissionQueryRepository.kt new file mode 100644 index 0000000..4ccc61c --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/mission/domain/repository/DailyMissionQueryRepository.kt @@ -0,0 +1,9 @@ +package com.ject.studytrip.mission.domain.repository + +import com.ject.studytrip.mission.domain.model.DailyMission + +interface DailyMissionQueryRepository { + fun findAllByDailyGoalIdFetchJoinMission(dailyGoalId: Long): List + + fun findAllWithMissionAndStampByIds(ids: List): List +} diff --git a/src/main/kotlin/com/ject/studytrip/mission/domain/repository/MissionCommandRepository.kt b/src/main/kotlin/com/ject/studytrip/mission/domain/repository/MissionCommandRepository.kt new file mode 100644 index 0000000..e2c926e --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/mission/domain/repository/MissionCommandRepository.kt @@ -0,0 +1,11 @@ +package com.ject.studytrip.mission.domain.repository + +interface MissionCommandRepository { + fun existsByStampIdAndCompletedIsFalseAndDeletedAtIsNull(stampId: Long): Boolean + + fun deleteAllByDeletedAtIsNotNull(): Long + + fun deleteAllByDeletedStampOwner(): Long + + fun deleteAllByMemberId(memberId: Long): Long +} diff --git a/src/main/kotlin/com/ject/studytrip/mission/domain/repository/MissionQueryRepository.kt b/src/main/kotlin/com/ject/studytrip/mission/domain/repository/MissionQueryRepository.kt new file mode 100644 index 0000000..3d230dc --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/mission/domain/repository/MissionQueryRepository.kt @@ -0,0 +1,7 @@ +package com.ject.studytrip.mission.domain.repository + +import com.ject.studytrip.mission.domain.model.Mission + +interface MissionQueryRepository { + fun findAllByIdsInFetchJoinStamp(ids: List): List +} diff --git a/src/main/kotlin/com/ject/studytrip/mission/infra/querydsl/DailyMissionCommandRepositoryAdapter.kt b/src/main/kotlin/com/ject/studytrip/mission/infra/querydsl/DailyMissionCommandRepositoryAdapter.kt new file mode 100644 index 0000000..47a9979 --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/mission/infra/querydsl/DailyMissionCommandRepositoryAdapter.kt @@ -0,0 +1,65 @@ +package com.ject.studytrip.mission.infra.querydsl + +import com.ject.studytrip.mission.domain.model.QDailyMission.dailyMission +import com.ject.studytrip.mission.domain.model.QMission.mission +import com.ject.studytrip.mission.domain.repository.DailyMissionCommandRepository +import com.ject.studytrip.stamp.domain.model.QStamp.stamp +import com.ject.studytrip.trip.domain.model.QDailyGoal.dailyGoal +import com.ject.studytrip.trip.domain.model.QTrip.trip +import com.querydsl.jpa.JPAExpressions +import com.querydsl.jpa.impl.JPAQueryFactory +import org.springframework.stereotype.Repository + +@Repository +class DailyMissionCommandRepositoryAdapter( + private val queryFactory: JPAQueryFactory, +) : DailyMissionCommandRepository { + override fun deleteAllByDeletedAtIsNotNull(): Long = + queryFactory + .delete(dailyMission) + .where(dailyMission.deletedAt.isNotNull) + .execute() + + override fun deleteAllByDeletedMissionOwner(): Long = + queryFactory + .delete(dailyMission) + .where( + dailyMission.mission.id.`in`( + JPAExpressions + .select(mission.id) + .from(mission) + .where(mission.deletedAt.isNotNull), + ), + ).execute() + + override fun deleteAllByDeletedDailyGoalOwner(): Long = + queryFactory + .delete(dailyMission) + .where( + dailyMission.dailyGoal.id.`in`( + JPAExpressions + .select(dailyGoal.id) + .from(dailyGoal) + .where(dailyGoal.deletedAt.isNotNull), + ), + ).execute() + + override fun deleteAllByMemberId(memberId: Long): Long { + val ids = + queryFactory + .select(dailyMission.id) + .from(dailyMission) + .join(dailyMission.mission, mission) + .join(mission.stamp, stamp) + .join(stamp.trip, trip) + .where(trip.member.id.eq(memberId)) + .fetch() + + if (ids.isEmpty()) return 0L + + return queryFactory + .delete(dailyMission) + .where(dailyMission.id.`in`(ids)) + .execute() + } +} diff --git a/src/main/kotlin/com/ject/studytrip/mission/infra/querydsl/DailyMissionQueryRepositoryAdapter.kt b/src/main/kotlin/com/ject/studytrip/mission/infra/querydsl/DailyMissionQueryRepositoryAdapter.kt new file mode 100644 index 0000000..0a91194 --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/mission/infra/querydsl/DailyMissionQueryRepositoryAdapter.kt @@ -0,0 +1,34 @@ +package com.ject.studytrip.mission.infra.querydsl + +import com.ject.studytrip.mission.domain.model.DailyMission +import com.ject.studytrip.mission.domain.model.QDailyMission.dailyMission +import com.ject.studytrip.mission.domain.model.QMission.mission +import com.ject.studytrip.mission.domain.repository.DailyMissionQueryRepository +import com.ject.studytrip.stamp.domain.model.QStamp.stamp +import com.querydsl.jpa.impl.JPAQueryFactory +import org.springframework.stereotype.Repository + +@Repository +class DailyMissionQueryRepositoryAdapter( + private val queryFactory: JPAQueryFactory, +) : DailyMissionQueryRepository { + override fun findAllByDailyGoalIdFetchJoinMission(dailyGoalId: Long): List = + queryFactory + .selectFrom(dailyMission) + .join(dailyMission.mission, mission) + .fetchJoin() + .where( + dailyMission.dailyGoal.id.eq(dailyGoalId), + dailyMission.deletedAt.isNull, + ).fetch() + + override fun findAllWithMissionAndStampByIds(ids: List): List = + queryFactory + .selectFrom(dailyMission) + .join(dailyMission.mission, mission) + .fetchJoin() + .join(mission.stamp, stamp) + .fetchJoin() + .where(dailyMission.id.`in`(ids)) + .fetch() +} diff --git a/src/main/kotlin/com/ject/studytrip/mission/infra/querydsl/MissionCommandRepositoryAdapter.kt b/src/main/kotlin/com/ject/studytrip/mission/infra/querydsl/MissionCommandRepositoryAdapter.kt new file mode 100644 index 0000000..1e8ea92 --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/mission/infra/querydsl/MissionCommandRepositoryAdapter.kt @@ -0,0 +1,64 @@ +package com.ject.studytrip.mission.infra.querydsl + +import com.ject.studytrip.mission.domain.model.QMission.mission +import com.ject.studytrip.mission.domain.repository.MissionCommandRepository +import com.ject.studytrip.stamp.domain.model.QStamp.stamp +import com.ject.studytrip.trip.domain.model.QTrip.trip +import com.querydsl.jpa.JPAExpressions +import com.querydsl.jpa.impl.JPAQueryFactory +import org.springframework.stereotype.Repository + +@Repository +class MissionCommandRepositoryAdapter( + private val queryFactory: JPAQueryFactory, +) : MissionCommandRepository { + override fun existsByStampIdAndCompletedIsFalseAndDeletedAtIsNull(stampId: Long): Boolean { + val hit = + queryFactory + .selectOne() + .from(mission) + .where( + mission.stamp.id.eq(stampId), + mission.completed.isFalse, + mission.deletedAt.isNull, + ).fetchFirst() + + return hit != null + } + + override fun deleteAllByDeletedAtIsNotNull(): Long = + queryFactory + .delete(mission) + .where(mission.deletedAt.isNotNull) + .execute() + + override fun deleteAllByDeletedStampOwner(): Long = + queryFactory + .delete(mission) + .where( + mission.stamp.id.`in`( + JPAExpressions + .select(stamp.id) + .from(stamp) + .where(stamp.deletedAt.isNotNull), + ), + ).execute() + + override fun deleteAllByMemberId(memberId: Long): Long { + val ids = + queryFactory + .select(mission.id) + .from(mission) + .join(mission.stamp, stamp) + .join(stamp.trip, trip) + .where(trip.member.id.eq(memberId)) + .fetch() + + if (ids.isEmpty()) return 0L + + return queryFactory + .delete(mission) + .where(mission.id.`in`(ids)) + .execute() + } +} diff --git a/src/main/kotlin/com/ject/studytrip/mission/infra/querydsl/MissionQueryRepositoryAdapter.kt b/src/main/kotlin/com/ject/studytrip/mission/infra/querydsl/MissionQueryRepositoryAdapter.kt new file mode 100644 index 0000000..03f51f8 --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/mission/infra/querydsl/MissionQueryRepositoryAdapter.kt @@ -0,0 +1,21 @@ +package com.ject.studytrip.mission.infra.querydsl + +import com.ject.studytrip.mission.domain.model.Mission +import com.ject.studytrip.mission.domain.model.QMission.mission +import com.ject.studytrip.mission.domain.repository.MissionQueryRepository +import com.ject.studytrip.stamp.domain.model.QStamp.stamp +import com.querydsl.jpa.impl.JPAQueryFactory +import org.springframework.stereotype.Repository + +@Repository +class MissionQueryRepositoryAdapter( + private val queryFactory: JPAQueryFactory, +) : MissionQueryRepository { + override fun findAllByIdsInFetchJoinStamp(ids: List): List = + queryFactory + .selectFrom(mission) + .join(mission.stamp, stamp) + .fetchJoin() + .where(mission.id.`in`(ids)) + .fetch() +} diff --git a/src/main/kotlin/com/ject/studytrip/mission/presentation/dto/response/CreateMissionResponse.kt b/src/main/kotlin/com/ject/studytrip/mission/presentation/dto/response/CreateMissionResponse.kt index 54d3a30..ff0d996 100644 --- a/src/main/kotlin/com/ject/studytrip/mission/presentation/dto/response/CreateMissionResponse.kt +++ b/src/main/kotlin/com/ject/studytrip/mission/presentation/dto/response/CreateMissionResponse.kt @@ -8,7 +8,6 @@ data class CreateMissionResponse( val missionId: Long, ) { companion object { - @JvmStatic fun of(missionInfo: MissionInfo): CreateMissionResponse = CreateMissionResponse(missionInfo.missionId) } } diff --git a/src/main/kotlin/com/ject/studytrip/mission/presentation/dto/response/LoadMissionInfoResponse.kt b/src/main/kotlin/com/ject/studytrip/mission/presentation/dto/response/LoadMissionInfoResponse.kt index 1245918..693837a 100644 --- a/src/main/kotlin/com/ject/studytrip/mission/presentation/dto/response/LoadMissionInfoResponse.kt +++ b/src/main/kotlin/com/ject/studytrip/mission/presentation/dto/response/LoadMissionInfoResponse.kt @@ -12,7 +12,6 @@ data class LoadMissionInfoResponse( val completed: Boolean, ) { companion object { - @JvmStatic fun of(missionInfo: MissionInfo): LoadMissionInfoResponse = LoadMissionInfoResponse(missionInfo.missionId, missionInfo.missionName, missionInfo.completed) } diff --git a/src/main/kotlin/com/ject/studytrip/pomodoro/application/dto/PomodoroInfo.kt b/src/main/kotlin/com/ject/studytrip/pomodoro/application/dto/PomodoroInfo.kt index 04dbfa7..c44ee89 100644 --- a/src/main/kotlin/com/ject/studytrip/pomodoro/application/dto/PomodoroInfo.kt +++ b/src/main/kotlin/com/ject/studytrip/pomodoro/application/dto/PomodoroInfo.kt @@ -1,5 +1,6 @@ package com.ject.studytrip.pomodoro.application.dto +import com.ject.studytrip.global.util.EntityExtensions.requireId import com.ject.studytrip.pomodoro.domain.model.Pomodoro data class PomodoroInfo( @@ -8,12 +9,11 @@ data class PomodoroInfo( val focusSessionCount: Int, ) { companion object { - @JvmStatic fun from(pomodoro: Pomodoro): PomodoroInfo = PomodoroInfo( - pomodoro.getId(), - pomodoro.getFocusDurationInSeconds() / 60, - pomodoro.getFocusSessionCount(), + pomodoro.id.requireId(), + pomodoro.focusDurationInSeconds / 60, + pomodoro.focusSessionCount, ) } } diff --git a/src/main/kotlin/com/ject/studytrip/pomodoro/domain/error/PomodoroErrorCode.kt b/src/main/kotlin/com/ject/studytrip/pomodoro/domain/error/PomodoroErrorCode.kt index e7c8108..fb55496 100644 --- a/src/main/kotlin/com/ject/studytrip/pomodoro/domain/error/PomodoroErrorCode.kt +++ b/src/main/kotlin/com/ject/studytrip/pomodoro/domain/error/PomodoroErrorCode.kt @@ -4,8 +4,8 @@ import com.ject.studytrip.global.exception.error.ErrorCode import org.springframework.http.HttpStatus enum class PomodoroErrorCode( - private val status: HttpStatus, - private val message: String, + override val status: HttpStatus, + override val message: String, ) : ErrorCode { // 400 POMODORO_ALREADY_DELETED(HttpStatus.BAD_REQUEST, "이미 삭제된 뽀모도로입니다."), @@ -13,11 +13,4 @@ enum class PomodoroErrorCode( // 404 POMODORO_NOT_FOUND(HttpStatus.NOT_FOUND, "요청한 뽀모도로를 찾을 수 없습니다."), - ; - - override fun getName(): String = name - - override fun getStatus(): HttpStatus = status - - override fun getMessage(): String = message } diff --git a/src/main/kotlin/com/ject/studytrip/pomodoro/domain/model/Pomodoro.kt b/src/main/kotlin/com/ject/studytrip/pomodoro/domain/model/Pomodoro.kt new file mode 100644 index 0000000..7c64ca8 --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/pomodoro/domain/model/Pomodoro.kt @@ -0,0 +1,45 @@ +package com.ject.studytrip.pomodoro.domain.model + +import com.ject.studytrip.global.common.entity.BaseTimeEntity +import com.ject.studytrip.trip.domain.model.DailyGoal +import jakarta.persistence.Entity +import jakarta.persistence.FetchType +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Id +import jakarta.persistence.JoinColumn +import jakarta.persistence.ManyToOne +import java.time.LocalDateTime + +@Entity +class Pomodoro protected constructor( + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "daily_goal_id", nullable = false) + var dailyGoal: DailyGoal, + var focusDurationInSeconds: Int, + var focusSessionCount: Int, + var breakDurationInSeconds: Int, + var totalFocusTimeInSeconds: Int = 0, +) : BaseTimeEntity() { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + var id: Long? = null + protected set + + companion object { + fun of( + dailyGoal: DailyGoal, + focusDurationInSeconds: Int, + focusSessionCount: Int, + breakDurationInSeconds: Int, + ): Pomodoro = Pomodoro(dailyGoal, focusDurationInSeconds, focusSessionCount, breakDurationInSeconds, 0) + } + + fun updateTotalFocusTimeInSeconds(totalFocusTimeInSeconds: Int) { + this.totalFocusTimeInSeconds = totalFocusTimeInSeconds + } + + fun updateDeletedAt(now: LocalDateTime = LocalDateTime.now()) { + markDeleted(now) + } +} diff --git a/src/main/kotlin/com/ject/studytrip/pomodoro/domain/policy/PomodoroPolicy.kt b/src/main/kotlin/com/ject/studytrip/pomodoro/domain/policy/PomodoroPolicy.kt index e685a26..4e0beef 100644 --- a/src/main/kotlin/com/ject/studytrip/pomodoro/domain/policy/PomodoroPolicy.kt +++ b/src/main/kotlin/com/ject/studytrip/pomodoro/domain/policy/PomodoroPolicy.kt @@ -6,7 +6,7 @@ import com.ject.studytrip.pomodoro.domain.model.Pomodoro object PomodoroPolicy { fun validateNotDeleted(pomodoro: Pomodoro) { - if (pomodoro.isDeleted) { + if (pomodoro.isDeleted()) { throw CustomException(PomodoroErrorCode.POMODORO_ALREADY_DELETED) } } diff --git a/src/main/kotlin/com/ject/studytrip/pomodoro/domain/repository/PomodoroCommandRepository.kt b/src/main/kotlin/com/ject/studytrip/pomodoro/domain/repository/PomodoroCommandRepository.kt new file mode 100644 index 0000000..912e155 --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/pomodoro/domain/repository/PomodoroCommandRepository.kt @@ -0,0 +1,9 @@ +package com.ject.studytrip.pomodoro.domain.repository + +interface PomodoroCommandRepository { + fun deleteAllByDeletedAtIsNotNull(): Long + + fun deleteAllByDeletedDailyGoalOwner(): Long + + fun deleteAllByMemberId(memberId: Long): Long +} diff --git a/src/main/kotlin/com/ject/studytrip/pomodoro/domain/repository/PomodoroQueryRepository.kt b/src/main/kotlin/com/ject/studytrip/pomodoro/domain/repository/PomodoroQueryRepository.kt new file mode 100644 index 0000000..118ad75 --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/pomodoro/domain/repository/PomodoroQueryRepository.kt @@ -0,0 +1,5 @@ +package com.ject.studytrip.pomodoro.domain.repository + +interface PomodoroQueryRepository { + fun sumFocusHoursByTripId(tripId: Long): Long +} diff --git a/src/main/kotlin/com/ject/studytrip/pomodoro/infra/querydsl/PomodoroCommandRepositoryAdapter.kt b/src/main/kotlin/com/ject/studytrip/pomodoro/infra/querydsl/PomodoroCommandRepositoryAdapter.kt new file mode 100644 index 0000000..557af78 --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/pomodoro/infra/querydsl/PomodoroCommandRepositoryAdapter.kt @@ -0,0 +1,50 @@ +package com.ject.studytrip.pomodoro.infra.querydsl + +import com.ject.studytrip.pomodoro.domain.model.QPomodoro.pomodoro +import com.ject.studytrip.pomodoro.domain.repository.PomodoroCommandRepository +import com.ject.studytrip.trip.domain.model.QDailyGoal.dailyGoal +import com.ject.studytrip.trip.domain.model.QTrip.trip +import com.querydsl.jpa.JPAExpressions +import com.querydsl.jpa.impl.JPAQueryFactory +import org.springframework.stereotype.Repository + +@Repository +class PomodoroCommandRepositoryAdapter( + private val queryFactory: JPAQueryFactory, +) : PomodoroCommandRepository { + override fun deleteAllByDeletedAtIsNotNull(): Long = + queryFactory + .delete(pomodoro) + .where(pomodoro.deletedAt.isNotNull) + .execute() + + override fun deleteAllByDeletedDailyGoalOwner(): Long = + queryFactory + .delete(pomodoro) + .where( + pomodoro.dailyGoal.id.`in`( + JPAExpressions + .select(dailyGoal.id) + .from(dailyGoal) + .where(dailyGoal.deletedAt.isNotNull), + ), + ).execute() + + override fun deleteAllByMemberId(memberId: Long): Long { + val ids: List = + queryFactory + .select(pomodoro.id) + .from(pomodoro) + .join(pomodoro.dailyGoal, dailyGoal) + .join(dailyGoal.trip, trip) + .where(trip.member.id.eq(memberId)) + .fetch() + + if (ids.isEmpty()) return 0L + + return queryFactory + .delete(pomodoro) + .where(pomodoro.id.`in`(ids)) + .execute() + } +} diff --git a/src/main/kotlin/com/ject/studytrip/pomodoro/infra/querydsl/PomodoroQueryRepositoryAdapter.kt b/src/main/kotlin/com/ject/studytrip/pomodoro/infra/querydsl/PomodoroQueryRepositoryAdapter.kt new file mode 100644 index 0000000..4b24f5c --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/pomodoro/infra/querydsl/PomodoroQueryRepositoryAdapter.kt @@ -0,0 +1,29 @@ +package com.ject.studytrip.pomodoro.infra.querydsl + +import com.ject.studytrip.pomodoro.domain.model.QPomodoro.pomodoro +import com.ject.studytrip.pomodoro.domain.repository.PomodoroQueryRepository +import com.ject.studytrip.trip.domain.model.QDailyGoal.dailyGoal +import com.ject.studytrip.trip.domain.model.QTrip.trip +import com.querydsl.jpa.impl.JPAQueryFactory +import org.springframework.stereotype.Repository + +@Repository +class PomodoroQueryRepositoryAdapter( + private val queryFactory: JPAQueryFactory, +) : PomodoroQueryRepository { + override fun sumFocusHoursByTripId(tripId: Long): Long = + queryFactory + .select(pomodoro.totalFocusTimeInSeconds.sum()) + .from(pomodoro) + .join(pomodoro.dailyGoal, dailyGoal) + .join(dailyGoal.trip, trip) + .where( + trip.id.eq(tripId), + pomodoro.deletedAt.isNull, + dailyGoal.deletedAt.isNull, + trip.deletedAt.isNull, + ).fetchOne() + ?.toLong() + ?.div(3600) + ?: 0L +} diff --git a/src/main/kotlin/com/ject/studytrip/stamp/application/dto/StampDetail.kt b/src/main/kotlin/com/ject/studytrip/stamp/application/dto/StampDetail.kt index dd77cd3..0164c4a 100644 --- a/src/main/kotlin/com/ject/studytrip/stamp/application/dto/StampDetail.kt +++ b/src/main/kotlin/com/ject/studytrip/stamp/application/dto/StampDetail.kt @@ -5,12 +5,4 @@ import com.ject.studytrip.mission.application.dto.MissionInfo data class StampDetail( val stampInfo: StampInfo, val missionInfos: List, -) { - companion object { - @JvmStatic - fun from( - stampInfo: StampInfo, - missionInfos: List, - ): StampDetail = StampDetail(stampInfo, missionInfos) - } -} +) diff --git a/src/main/kotlin/com/ject/studytrip/stamp/application/dto/StampInfo.kt b/src/main/kotlin/com/ject/studytrip/stamp/application/dto/StampInfo.kt index 1130574..07bf4db 100644 --- a/src/main/kotlin/com/ject/studytrip/stamp/application/dto/StampInfo.kt +++ b/src/main/kotlin/com/ject/studytrip/stamp/application/dto/StampInfo.kt @@ -1,13 +1,14 @@ package com.ject.studytrip.stamp.application.dto import com.ject.studytrip.global.util.DateUtil +import com.ject.studytrip.global.util.EntityExtensions.requireId import com.ject.studytrip.stamp.domain.model.Stamp data class StampInfo( val stampId: Long, val stampName: String, val stampOrder: Int, - val endDate: String, + val endDate: String?, val totalMissions: Int, val completedMissions: Int, val completed: Boolean, @@ -16,18 +17,17 @@ data class StampInfo( val deletedAt: String?, ) { companion object { - @JvmStatic fun from(stamp: Stamp): StampInfo = StampInfo( - stamp.id, + stamp.id.requireId(), stamp.name, stamp.stampOrder, - DateUtil.formatDate(stamp.endDate), + stamp.endDate?.let { DateUtil.formatDate(it) }, stamp.totalMissions, stamp.completedMissions, - stamp.isCompleted, - DateUtil.formatDateTime(stamp.createdAt), - DateUtil.formatDateTime(stamp.updatedAt), + stamp.isCompleted(), + DateUtil.formatDateTime(requireNotNull(stamp.createdAt)), + DateUtil.formatDateTime(requireNotNull(stamp.updatedAt)), stamp.deletedAt?.let { DateUtil.formatDateTime(it) }, ) } diff --git a/src/main/kotlin/com/ject/studytrip/stamp/application/dto/StampsInfo.kt b/src/main/kotlin/com/ject/studytrip/stamp/application/dto/StampsInfo.kt index 9da91d4..937cbf7 100644 --- a/src/main/kotlin/com/ject/studytrip/stamp/application/dto/StampsInfo.kt +++ b/src/main/kotlin/com/ject/studytrip/stamp/application/dto/StampsInfo.kt @@ -2,9 +2,4 @@ package com.ject.studytrip.stamp.application.dto data class StampsInfo( val stampInfos: List, -) { - companion object { - @JvmStatic - fun of(stampInfos: List): StampsInfo = StampsInfo(stampInfos) - } -} +) diff --git a/src/main/kotlin/com/ject/studytrip/stamp/application/facade/StampFacade.kt b/src/main/kotlin/com/ject/studytrip/stamp/application/facade/StampFacade.kt index cad31f2..fe9fa31 100644 --- a/src/main/kotlin/com/ject/studytrip/stamp/application/facade/StampFacade.kt +++ b/src/main/kotlin/com/ject/studytrip/stamp/application/facade/StampFacade.kt @@ -4,6 +4,7 @@ import com.ject.studytrip.global.common.constants.CacheNameConstants.STAMP import com.ject.studytrip.global.common.constants.CacheNameConstants.STAMPS import com.ject.studytrip.global.common.constants.CacheNameConstants.TRIP import com.ject.studytrip.global.common.constants.CacheNameConstants.TRIPS +import com.ject.studytrip.global.util.EntityExtensions.requireId import com.ject.studytrip.mission.application.dto.MissionInfo import com.ject.studytrip.mission.application.service.MissionCommandService import com.ject.studytrip.mission.application.service.MissionQueryService @@ -89,7 +90,7 @@ class StampFacade( request: UpdateStampRequest, ) { val trip = tripQueryService.getValidTrip(memberId, tripId) - val stamp = stampQueryService.getValidStamp(trip.id, stampId) + val stamp = stampQueryService.getValidStamp(trip.id.requireId(), stampId) stampCommandService.updateStamp(trip, stamp, request) } @@ -143,7 +144,7 @@ class StampFacade( stampId: Long, ) { val trip = tripQueryService.getValidTrip(memberId, tripId) - val stamp = stampQueryService.getValidStamp(trip.id, stampId) + val stamp = stampQueryService.getValidStamp(tripId, stampId) stampCommandService.deleteStamp(stamp) shiftStampOrdersIfTripCategoryIsCourse(trip, stamp.stampOrder) @@ -174,9 +175,9 @@ class StampFacade( stampId: Long, ) { val trip = tripQueryService.getValidTrip(memberId, tripId) - val stamp = stampQueryService.getValidStamp(trip.id, stampId) + val stamp = stampQueryService.getValidStamp(tripId, stampId) - missionCommandService.validateAllMissionsCompletedByStampId(stamp.id) + missionCommandService.validateAllMissionsCompletedByStampId(stampId) stampCommandService.completeStamp(stamp) tripCommandService.increaseCompletedStamps(trip) @@ -192,9 +193,9 @@ class StampFacade( tripId: Long, ): StampsInfo { val trip = tripQueryService.getValidTrip(memberId, tripId) - val stamps = stampQueryService.getStampsByTripId(trip.id) + val stamps = stampQueryService.getStampsByTripId(trip.id.requireId()) - return StampsInfo.of(stamps.map { StampInfo.from(it) }) + return StampsInfo(stamps.map { StampInfo.from(it) }) } @Cacheable( @@ -208,10 +209,10 @@ class StampFacade( stampId: Long, ): StampDetail { val trip = tripQueryService.getValidTrip(memberId, tripId) - val stamp = stampQueryService.getValidStamp(trip.id, stampId) - val missions = missionQueryService.getMissionsByStampId(stamp.id) + val stamp = stampQueryService.getValidStamp(trip.id.requireId(), stampId) + val missions = missionQueryService.getMissionsByStampId(stampId) - return StampDetail.from(StampInfo.from(stamp), missions.map { MissionInfo.from(it) }) + return StampDetail(StampInfo.from(stamp), missions.map { MissionInfo.from(it) }) } private fun shiftStampOrdersIfTripCategoryIsCourse( @@ -220,7 +221,7 @@ class StampFacade( ) { if (trip.category != TripCategory.COURSE) return - val affectedStamps = stampQueryService.getStampsToShiftAfterDeleted(trip.id, stampOrder) + val affectedStamps = stampQueryService.getStampsToShiftAfterDeleted(trip.id.requireId(), stampOrder) stampCommandService.shiftStampOrders(affectedStamps) } diff --git a/src/main/kotlin/com/ject/studytrip/stamp/application/service/StampCommandService.kt b/src/main/kotlin/com/ject/studytrip/stamp/application/service/StampCommandService.kt index eeaa799..95de111 100644 --- a/src/main/kotlin/com/ject/studytrip/stamp/application/service/StampCommandService.kt +++ b/src/main/kotlin/com/ject/studytrip/stamp/application/service/StampCommandService.kt @@ -1,5 +1,6 @@ package com.ject.studytrip.stamp.application.service +import com.ject.studytrip.global.util.EntityExtensions.requireId import com.ject.studytrip.stamp.domain.factory.StampFactory import com.ject.studytrip.stamp.domain.model.Stamp import com.ject.studytrip.stamp.domain.policy.StampPolicy @@ -65,11 +66,14 @@ class StampCommandService( stamp: Stamp, request: UpdateStampRequest, ) { - stamp.updateName(request.name) - - StampPolicy.validateNotStampEndDateAfterTripEndDate(trip.endDate, request.endDate) + request.name?.let { + stamp.updateName(it) + } - stamp.updateEndDate(request.endDate) + request.endDate?.let { + StampPolicy.validateNotStampEndDateAfterTripEndDate(trip.endDate, it) + stamp.updateEndDate(it) + } } fun updateStampOrders( @@ -81,7 +85,7 @@ class StampCommandService( StampPolicy.validateUpdateStampOrders(trip.category, request.orderedStampIds, stamps) stamps.forEach { stamp -> - StampPolicy.validateStampBelongsToTrip(trip.id, stamp) + StampPolicy.validateStampBelongsToTrip(trip.id.requireId(), stamp) StampPolicy.validateNotDeleted(stamp) StampPolicy.validateNotCompleted(stamp) } diff --git a/src/main/kotlin/com/ject/studytrip/stamp/application/service/StampQueryService.kt b/src/main/kotlin/com/ject/studytrip/stamp/application/service/StampQueryService.kt index e01ad3a..6263812 100644 --- a/src/main/kotlin/com/ject/studytrip/stamp/application/service/StampQueryService.kt +++ b/src/main/kotlin/com/ject/studytrip/stamp/application/service/StampQueryService.kt @@ -1,6 +1,7 @@ package com.ject.studytrip.stamp.application.service import com.ject.studytrip.global.exception.CustomException +import com.ject.studytrip.global.util.EntityExtensions.requireId import com.ject.studytrip.stamp.domain.error.StampErrorCode import com.ject.studytrip.stamp.domain.model.Stamp import com.ject.studytrip.stamp.domain.policy.StampPolicy @@ -58,7 +59,7 @@ class StampQueryService( fun getNextStampOrderByTrip(trip: Trip): Int { if (trip.category != TripCategory.COURSE) return 0 - return stampQueryRepository.findNextStampOrderByTripId(trip.id) + return stampQueryRepository.findNextStampOrderByTripId(trip.id.requireId()) } fun getStampsToShiftAfterDeleted( @@ -68,17 +69,25 @@ class StampQueryService( private fun getExploreStampName(stamps: List): String { // 스탬프별 개수 집계 - val stampCountMap = stamps.groupingBy { it }.eachCount().mapValues { it.value.toLong() } + val stampCountMap: Map = + stamps + .groupingBy { it } + .eachCount() + .mapValues { it.value.toLong() } // 최대 개수 - val maxCount = stampCountMap.values.maxOrNull() ?: 0L + val maxCount: Long = stampCountMap.values.maxOrNull() ?: 0L // 최대 개수를 가진 스탬프들 찾기 - val maxCountStamps: List = stampCountMap.filterValues { it == maxCount }.keys.toList() + val maxCountStamps: List = + stampCountMap + .filterValues { it == maxCount } + .keys + .toList() // 가장 빠른 생성 시간을 가진 스탬프 선택 return maxCountStamps - .minByOrNull { it.createdAt } + .minByOrNull { it.createdAt!! } ?.name ?: "" } diff --git a/src/main/kotlin/com/ject/studytrip/stamp/domain/error/StampErrorCode.kt b/src/main/kotlin/com/ject/studytrip/stamp/domain/error/StampErrorCode.kt index 5e70a7e..e5eb319 100644 --- a/src/main/kotlin/com/ject/studytrip/stamp/domain/error/StampErrorCode.kt +++ b/src/main/kotlin/com/ject/studytrip/stamp/domain/error/StampErrorCode.kt @@ -4,8 +4,8 @@ import com.ject.studytrip.global.exception.error.ErrorCode import org.springframework.http.HttpStatus enum class StampErrorCode( - private val status: HttpStatus, - private val message: String, + override val status: HttpStatus, + override val message: String, ) : ErrorCode { // 400 STAMP_ALREADY_DELETED(HttpStatus.BAD_REQUEST, "이미 삭제된 스탬프입니다."), @@ -21,11 +21,4 @@ enum class StampErrorCode( // 404 STAMP_NOT_FOUND(HttpStatus.NOT_FOUND, "요청한 스탬프를 찾을 수 없습니다."), - ; - - override fun getName(): String = name - - override fun getStatus(): HttpStatus = status - - override fun getMessage(): String = message } diff --git a/src/main/kotlin/com/ject/studytrip/stamp/domain/factory/StampFactory.kt b/src/main/kotlin/com/ject/studytrip/stamp/domain/factory/StampFactory.kt index 5f498bd..6fe723c 100644 --- a/src/main/kotlin/com/ject/studytrip/stamp/domain/factory/StampFactory.kt +++ b/src/main/kotlin/com/ject/studytrip/stamp/domain/factory/StampFactory.kt @@ -5,7 +5,6 @@ import com.ject.studytrip.trip.domain.model.Trip import java.time.LocalDate object StampFactory { - @JvmStatic fun create( trip: Trip, name: String, diff --git a/src/main/kotlin/com/ject/studytrip/stamp/domain/model/Stamp.kt b/src/main/kotlin/com/ject/studytrip/stamp/domain/model/Stamp.kt new file mode 100644 index 0000000..69a706d --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/stamp/domain/model/Stamp.kt @@ -0,0 +1,79 @@ +package com.ject.studytrip.stamp.domain.model + +import com.ject.studytrip.global.common.entity.BaseTimeEntity +import com.ject.studytrip.trip.domain.model.Trip +import jakarta.persistence.Column +import jakarta.persistence.Entity +import jakarta.persistence.FetchType +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Id +import jakarta.persistence.JoinColumn +import jakarta.persistence.ManyToOne +import org.springframework.util.StringUtils.hasText +import java.time.LocalDate +import java.time.LocalDateTime + +@Entity +class Stamp protected constructor( + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "trip_id", nullable = false) + var trip: Trip, + @Column(nullable = false) + var name: String, + var stampOrder: Int, + var endDate: LocalDate?, + var totalMissions: Int = 0, + var completedMissions: Int = 0, + var completed: Boolean = false, +) : BaseTimeEntity() { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + var id: Long? = null + protected set + + companion object { + fun of( + trip: Trip, + name: String, + stampOrder: Int, + endDate: LocalDate?, + ): Stamp = Stamp(trip, name, stampOrder, endDate, 0, 0, false) + } + + fun updateName(name: String) { + if (hasText(name)) { + this.name = name + } + } + + fun updateStampOrder(newOrder: Int) { + this.stampOrder = newOrder + } + + fun updateEndDate(endDate: LocalDate) { + this.endDate = endDate + } + + fun updateCompleted() { + this.completed = true + } + + fun updateDeletedAt(now: LocalDateTime = LocalDateTime.now()) { + markDeleted(now) + } + + fun increaseTotalMissions() { + this.totalMissions += 1 + } + + fun decreaseTotalMissions() { + this.totalMissions -= 1 + } + + fun increaseCompletedMissions(count: Int) { + this.completedMissions += count + } + + fun isCompleted(): Boolean = completed +} diff --git a/src/main/kotlin/com/ject/studytrip/stamp/domain/policy/StampPolicy.kt b/src/main/kotlin/com/ject/studytrip/stamp/domain/policy/StampPolicy.kt index 9bdf4a2..da8be30 100644 --- a/src/main/kotlin/com/ject/studytrip/stamp/domain/policy/StampPolicy.kt +++ b/src/main/kotlin/com/ject/studytrip/stamp/domain/policy/StampPolicy.kt @@ -8,13 +8,13 @@ import java.time.LocalDate object StampPolicy { fun validateNotDeleted(stamp: Stamp) { - if (stamp.isDeleted) { + if (stamp.isDeleted()) { throw CustomException(StampErrorCode.STAMP_ALREADY_DELETED) } } fun validateNotCompleted(stamp: Stamp) { - if (stamp.isCompleted) { + if (stamp.isCompleted()) { throw CustomException(StampErrorCode.STAMP_ALREADY_COMPLETED) } } diff --git a/src/main/kotlin/com/ject/studytrip/stamp/domain/repository/StampCommandRepository.kt b/src/main/kotlin/com/ject/studytrip/stamp/domain/repository/StampCommandRepository.kt new file mode 100644 index 0000000..1dea1bc --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/stamp/domain/repository/StampCommandRepository.kt @@ -0,0 +1,11 @@ +package com.ject.studytrip.stamp.domain.repository + +interface StampCommandRepository { + fun existsByTripIdAndCompletedIsFalseAndDeletedAtIsNull(tripId: Long): Boolean + + fun deleteAllByDeletedAtIsNotNull(): Long + + fun deleteAllByDeletedTripOwner(): Long + + fun deleteAllByMemberId(memberId: Long): Long +} diff --git a/src/main/kotlin/com/ject/studytrip/stamp/domain/repository/StampQueryRepository.kt b/src/main/kotlin/com/ject/studytrip/stamp/domain/repository/StampQueryRepository.kt new file mode 100644 index 0000000..b9babed --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/stamp/domain/repository/StampQueryRepository.kt @@ -0,0 +1,15 @@ +package com.ject.studytrip.stamp.domain.repository + +import com.ject.studytrip.stamp.domain.model.Stamp +import java.util.Optional + +interface StampQueryRepository { + fun findStampsToShiftAfterOrder( + tripId: Long, + deletedOrder: Int, + ): List + + fun findFirstIncompleteStampByTripId(tripId: Long): Optional + + fun findNextStampOrderByTripId(tripId: Long): Int +} diff --git a/src/main/kotlin/com/ject/studytrip/stamp/infra/querydsl/StampCommandRepositoryAdapter.kt b/src/main/kotlin/com/ject/studytrip/stamp/infra/querydsl/StampCommandRepositoryAdapter.kt new file mode 100644 index 0000000..b37a9f2 --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/stamp/infra/querydsl/StampCommandRepositoryAdapter.kt @@ -0,0 +1,62 @@ +package com.ject.studytrip.stamp.infra.querydsl + +import com.ject.studytrip.stamp.domain.model.QStamp.stamp +import com.ject.studytrip.stamp.domain.repository.StampCommandRepository +import com.ject.studytrip.trip.domain.model.QTrip.trip +import com.querydsl.jpa.JPAExpressions +import com.querydsl.jpa.impl.JPAQueryFactory +import org.springframework.stereotype.Repository + +@Repository +class StampCommandRepositoryAdapter( + private val queryFactory: JPAQueryFactory, +) : StampCommandRepository { + override fun existsByTripIdAndCompletedIsFalseAndDeletedAtIsNull(tripId: Long): Boolean { + val hit = + queryFactory + .selectOne() + .from(stamp) + .where( + stamp.trip.id.eq(tripId), + stamp.completed.isFalse, + stamp.deletedAt.isNull, + ).fetchOne() + + return hit != null + } + + override fun deleteAllByDeletedAtIsNotNull(): Long = + queryFactory + .delete(stamp) + .where(stamp.deletedAt.isNotNull) + .execute() + + override fun deleteAllByDeletedTripOwner(): Long = + queryFactory + .delete(stamp) + .where( + stamp.trip.id.`in`( + JPAExpressions + .select(trip.id) + .from(trip) + .where(trip.deletedAt.isNotNull), + ), + ).execute() + + override fun deleteAllByMemberId(memberId: Long): Long { + val ids = + queryFactory + .select(stamp.id) + .from(stamp) + .join(stamp.trip, trip) + .where(trip.member.id.eq(memberId)) + .fetch() + + if (ids.isEmpty()) return 0L + + return queryFactory + .delete(stamp) + .where(stamp.id.`in`(ids)) + .execute() + } +} diff --git a/src/main/kotlin/com/ject/studytrip/stamp/infra/querydsl/StampQueryRepositoryAdapter.kt b/src/main/kotlin/com/ject/studytrip/stamp/infra/querydsl/StampQueryRepositoryAdapter.kt new file mode 100644 index 0000000..b9f7950 --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/stamp/infra/querydsl/StampQueryRepositoryAdapter.kt @@ -0,0 +1,52 @@ +package com.ject.studytrip.stamp.infra.querydsl + +import com.ject.studytrip.stamp.domain.model.QStamp.stamp +import com.ject.studytrip.stamp.domain.model.Stamp +import com.ject.studytrip.stamp.domain.repository.StampQueryRepository +import com.querydsl.jpa.impl.JPAQueryFactory +import org.springframework.stereotype.Repository +import java.util.Optional + +@Repository +class StampQueryRepositoryAdapter( + private val queryFactory: JPAQueryFactory, +) : StampQueryRepository { + override fun findStampsToShiftAfterOrder( + tripId: Long, + deletedOrder: Int, + ): List = + queryFactory + .selectFrom(stamp) + .where( + stamp.trip.id.eq(tripId), + stamp.stampOrder.gt(deletedOrder), + stamp.deletedAt.isNull, + ).orderBy(stamp.stampOrder.asc()) + .fetch() + + // 완료되지 않은 스탬프 중 가장 첫번째 스탬프 조회 + override fun findFirstIncompleteStampByTripId(tripId: Long): Optional = + Optional.ofNullable( + queryFactory + .selectFrom(stamp) + .where( + stamp.trip.id.eq(tripId), + stamp.completed.isFalse, + stamp.deletedAt.isNull, + ).orderBy(stamp.stampOrder.asc()) + .fetchFirst(), + ) + + override fun findNextStampOrderByTripId(tripId: Long): Int = + queryFactory + .select( + stamp.stampOrder + .max() + .coalesce(0) + .add(1), + ).from(stamp) + .where( + stamp.trip.id.eq(tripId), + stamp.deletedAt.isNull, + ).fetchOne() ?: 1 +} diff --git a/src/main/kotlin/com/ject/studytrip/stamp/presentation/dto/response/CreateStampResponse.kt b/src/main/kotlin/com/ject/studytrip/stamp/presentation/dto/response/CreateStampResponse.kt index 2da5478..384af2e 100644 --- a/src/main/kotlin/com/ject/studytrip/stamp/presentation/dto/response/CreateStampResponse.kt +++ b/src/main/kotlin/com/ject/studytrip/stamp/presentation/dto/response/CreateStampResponse.kt @@ -8,7 +8,6 @@ data class CreateStampResponse( val stampId: Long, ) { companion object { - @JvmStatic fun of(stampInfo: StampInfo): CreateStampResponse = CreateStampResponse(stampInfo.stampId) } } diff --git a/src/main/kotlin/com/ject/studytrip/stamp/presentation/dto/response/LoadStampDetailResponse.kt b/src/main/kotlin/com/ject/studytrip/stamp/presentation/dto/response/LoadStampDetailResponse.kt index aa51de2..d467d0f 100644 --- a/src/main/kotlin/com/ject/studytrip/stamp/presentation/dto/response/LoadStampDetailResponse.kt +++ b/src/main/kotlin/com/ject/studytrip/stamp/presentation/dto/response/LoadStampDetailResponse.kt @@ -13,7 +13,7 @@ data class LoadStampDetailResponse( @field:Schema(description = "스탬프 순서") val stampOrder: Int, @field:Schema(description = "스탬프 종료일") - val endDate: String, + val endDate: String?, @field:Schema(description = "스탬프에 속한 총 미션 수") val totalMissions: Int, @field:Schema(description = "스탬프에 속한 완료된 미션 수") @@ -24,7 +24,6 @@ data class LoadStampDetailResponse( val missions: List, ) { companion object { - @JvmStatic fun of( stampInfo: StampInfo, missionInfos: List, diff --git a/src/main/kotlin/com/ject/studytrip/stamp/presentation/dto/response/LoadStampInfoResponse.kt b/src/main/kotlin/com/ject/studytrip/stamp/presentation/dto/response/LoadStampInfoResponse.kt index 07d39dd..571fdb8 100644 --- a/src/main/kotlin/com/ject/studytrip/stamp/presentation/dto/response/LoadStampInfoResponse.kt +++ b/src/main/kotlin/com/ject/studytrip/stamp/presentation/dto/response/LoadStampInfoResponse.kt @@ -11,7 +11,7 @@ data class LoadStampInfoResponse( @field:Schema(description = "스탬프 순서") val stampOrder: Int, @field:Schema(description = "스탬프 종료일") - val endDate: String, + val endDate: String?, @field:Schema(description = "스탬프에 속한 총 미션 수") val totalMissions: Int, @field:Schema(description = "스탬프에 속한 완료된 미션 수") @@ -20,7 +20,6 @@ data class LoadStampInfoResponse( val completed: Boolean, ) { companion object { - @JvmStatic fun of(stampInfo: StampInfo): LoadStampInfoResponse = LoadStampInfoResponse( stampInfo.stampId, diff --git a/src/main/kotlin/com/ject/studytrip/studylog/application/dto/PresignedStudyLogImageInfo.kt b/src/main/kotlin/com/ject/studytrip/studylog/application/dto/PresignedStudyLogImageInfo.kt index e67fd79..40b4cc5 100644 --- a/src/main/kotlin/com/ject/studytrip/studylog/application/dto/PresignedStudyLogImageInfo.kt +++ b/src/main/kotlin/com/ject/studytrip/studylog/application/dto/PresignedStudyLogImageInfo.kt @@ -4,18 +4,4 @@ data class PresignedStudyLogImageInfo( val studyLogId: Long, val tmpKey: String, val presignedUrl: String, -) { - companion object { - @JvmStatic - fun of( - studyLogId: Long, - tmpKey: String, - presignedUrl: String, - ): PresignedStudyLogImageInfo = - PresignedStudyLogImageInfo( - studyLogId, - tmpKey, - presignedUrl, - ) - } -} +) diff --git a/src/main/kotlin/com/ject/studytrip/studylog/application/dto/StudyLogDailyMissionInfo.kt b/src/main/kotlin/com/ject/studytrip/studylog/application/dto/StudyLogDailyMissionInfo.kt index 175c950..73909ec 100644 --- a/src/main/kotlin/com/ject/studytrip/studylog/application/dto/StudyLogDailyMissionInfo.kt +++ b/src/main/kotlin/com/ject/studytrip/studylog/application/dto/StudyLogDailyMissionInfo.kt @@ -1,5 +1,6 @@ package com.ject.studytrip.studylog.application.dto +import com.ject.studytrip.global.util.EntityExtensions.requireId import com.ject.studytrip.mission.application.dto.DailyMissionInfo import com.ject.studytrip.studylog.domain.model.StudyLogDailyMission @@ -8,11 +9,7 @@ data class StudyLogDailyMissionInfo( val dailyMissionInfo: DailyMissionInfo, ) { companion object { - @JvmStatic fun from(studyLogDailyMission: StudyLogDailyMission): StudyLogDailyMissionInfo = - StudyLogDailyMissionInfo( - studyLogDailyMission.getId(), - DailyMissionInfo.from(studyLogDailyMission.getDailyMission()), - ) + StudyLogDailyMissionInfo(studyLogDailyMission.id.requireId(), DailyMissionInfo.from(studyLogDailyMission.dailyMission)) } } diff --git a/src/main/kotlin/com/ject/studytrip/studylog/application/dto/StudyLogDetail.kt b/src/main/kotlin/com/ject/studytrip/studylog/application/dto/StudyLogDetail.kt index f7848b3..93e5233 100644 --- a/src/main/kotlin/com/ject/studytrip/studylog/application/dto/StudyLogDetail.kt +++ b/src/main/kotlin/com/ject/studytrip/studylog/application/dto/StudyLogDetail.kt @@ -8,7 +8,6 @@ data class StudyLogDetail( val studyLogDailyMissionInfos: List, ) { companion object { - @JvmStatic fun from( studyLog: StudyLog, studyLogDailyMissions: List?, diff --git a/src/main/kotlin/com/ject/studytrip/studylog/application/dto/StudyLogInfo.kt b/src/main/kotlin/com/ject/studytrip/studylog/application/dto/StudyLogInfo.kt index 1f1142e..60329d8 100644 --- a/src/main/kotlin/com/ject/studytrip/studylog/application/dto/StudyLogInfo.kt +++ b/src/main/kotlin/com/ject/studytrip/studylog/application/dto/StudyLogInfo.kt @@ -1,6 +1,7 @@ package com.ject.studytrip.studylog.application.dto import com.ject.studytrip.global.util.DateUtil +import com.ject.studytrip.global.util.EntityExtensions.requireId import com.ject.studytrip.studylog.domain.model.StudyLog data class StudyLogInfo( @@ -13,16 +14,15 @@ data class StudyLogInfo( val deletedAt: String?, ) { companion object { - @JvmStatic fun from(studyLog: StudyLog): StudyLogInfo = StudyLogInfo( - studyLog.getId(), - studyLog.getTitle(), - studyLog.getContent(), - studyLog.getImageUrl(), - DateUtil.formatDateTime(studyLog.getCreatedAt()), - DateUtil.formatDateTime(studyLog.getUpdatedAt()), - studyLog.getDeletedAt()?.let { DateUtil.formatDateTime(it) }, + studyLog.id.requireId(), + studyLog.title, + studyLog.content, + studyLog.imageUrl, + DateUtil.formatDateTime(requireNotNull(studyLog.createdAt)), + DateUtil.formatDateTime(requireNotNull(studyLog.createdAt)), + studyLog.deletedAt?.let { DateUtil.formatDateTime(it) }, ) } } diff --git a/src/main/kotlin/com/ject/studytrip/studylog/application/dto/StudyLogSliceInfo.kt b/src/main/kotlin/com/ject/studytrip/studylog/application/dto/StudyLogSliceInfo.kt index 58fa30f..5414804 100644 --- a/src/main/kotlin/com/ject/studytrip/studylog/application/dto/StudyLogSliceInfo.kt +++ b/src/main/kotlin/com/ject/studytrip/studylog/application/dto/StudyLogSliceInfo.kt @@ -3,12 +3,4 @@ package com.ject.studytrip.studylog.application.dto data class StudyLogSliceInfo( val studyLogDetails: List, val hasNext: Boolean, -) { - companion object { - @JvmStatic - fun of( - studyLogDetails: List, - hasNext: Boolean, - ): StudyLogSliceInfo = StudyLogSliceInfo(studyLogDetails, hasNext) - } -} +) diff --git a/src/main/kotlin/com/ject/studytrip/studylog/application/facade/StudyLogFacade.kt b/src/main/kotlin/com/ject/studytrip/studylog/application/facade/StudyLogFacade.kt index a017f72..b0b9639 100644 --- a/src/main/kotlin/com/ject/studytrip/studylog/application/facade/StudyLogFacade.kt +++ b/src/main/kotlin/com/ject/studytrip/studylog/application/facade/StudyLogFacade.kt @@ -4,6 +4,7 @@ import com.ject.studytrip.global.common.constants.CacheNameConstants.MISSIONS import com.ject.studytrip.global.common.constants.CacheNameConstants.STAMP import com.ject.studytrip.global.common.constants.CacheNameConstants.STAMPS import com.ject.studytrip.global.common.constants.CacheNameConstants.STUDY_LOGS +import com.ject.studytrip.global.util.EntityExtensions.requireId import com.ject.studytrip.image.application.service.ImageService import com.ject.studytrip.mission.application.service.DailyMissionQueryService import com.ject.studytrip.mission.application.service.MissionCommandService @@ -75,13 +76,13 @@ class StudyLogFacade( ): StudyLogInfo { // 1. 유효성 검증 및 엔티티 조회 val trip = tripQueryService.getValidTrip(memberId, tripId) - val dailyGoal = dailyGoalQueryService.getValidDailyGoal(trip.id, dailyGoalId) + val dailyGoal = dailyGoalQueryService.getValidDailyGoal(tripId, dailyGoalId) val selectedDailyMissions = dailyMissionQueryService.getValidDailyMissionsWithMissionAndStampByIds( - dailyGoal.id, + dailyGoalId, request.selectedDailyMissionIds, ) - val pomodoro = pomodoroQueryService.getValidPomodoroByDailyGoalId(dailyGoal.id) + val pomodoro = pomodoroQueryService.getValidPomodoroByDailyGoalId(dailyGoalId) // 2. 학습 로그 생성 val studyLog = studyLogCommandService.createStudyLog(trip.member, dailyGoal, request.content) @@ -111,7 +112,7 @@ class StudyLogFacade( val trip = tripQueryService.getValidTrip(memberId, tripId) // 2. 페이징된 학습 로그 목록 조회 - val studyLogSlice = studyLogQueryService.getStudyLogsSliceByTripId(trip.id, page, size, order) + val studyLogSlice = studyLogQueryService.getStudyLogsSliceByTripId(trip.id.requireId(), page, size, order) // 3. 학습 로그 상세 정보 구성 return buildStudyLogDetailsSlice(studyLogSlice) @@ -125,7 +126,7 @@ class StudyLogFacade( val studyLog = studyLogQueryService.getValidStudyLog(studyLogId) val info = imageService.presign(STUDY_LOG_IMAGE_KEY_PREFIX, studyLog.id.toString(), request.originFilename) - return PresignedStudyLogImageInfo.of(studyLog.id, info.tmpKey, info.presignedUrl) + return PresignedStudyLogImageInfo(studyLog.id.requireId(), info.tmpKey, info.presignedUrl) } @CacheEvict(cacheNames = [STUDY_LOGS], allEntries = true) @@ -156,9 +157,11 @@ class StudyLogFacade( missions.forEach { mission -> val stamp = mission.stamp - stampById.putIfAbsent(stamp.id, stamp) + val stampId = stamp.id.requireId() + + stampById.putIfAbsent(stampId, stamp) missionCommandService.completeMission(mission) // 미션 완료 처리 - completeMissionCountByStampId.merge(stamp.id, 1) { a, b -> a + b } // 스탬프별 완료한 미션 개수 누적(없으면 1, 있으면 +1) + completeMissionCountByStampId.merge(stampId, 1) { a, b -> a + b } // 스탬프별 완료한 미션 개수 누적(없으면 1, 있으면 +1) } // 스탬프별 완료된 미션 수 증가 @@ -173,12 +176,12 @@ class StudyLogFacade( } private fun buildStudyLogDetailsSlice(studyLogSlice: Slice): StudyLogSliceInfo { - val studyLogIds = studyLogSlice.content.map { it.id } + val studyLogIds = studyLogSlice.content.map { it.id.requireId() } // 학습 로그별 학습 로그 데일리 미션 목록 그룹화 val groupedStudyLogDailyMissions = studyLogDailyMissionQueryService.getGroupedStudyLogDailyMissionsByStudyLogIds(studyLogIds) val studyLogDetails = studyLogSlice.content.map { StudyLogDetail.from(it, groupedStudyLogDailyMissions[it.id]) } - return StudyLogSliceInfo.of(studyLogDetails, studyLogSlice.hasNext()) + return StudyLogSliceInfo(studyLogDetails, studyLogSlice.hasNext()) } } diff --git a/src/main/kotlin/com/ject/studytrip/studylog/application/service/StudyLogQueryService.kt b/src/main/kotlin/com/ject/studytrip/studylog/application/service/StudyLogQueryService.kt index 5d54581..e3e580c 100644 --- a/src/main/kotlin/com/ject/studytrip/studylog/application/service/StudyLogQueryService.kt +++ b/src/main/kotlin/com/ject/studytrip/studylog/application/service/StudyLogQueryService.kt @@ -50,11 +50,7 @@ class StudyLogQueryService( tripReportId: Long, page: Int, size: Int, - ): Slice = - studyLogQueryRepository.findSliceByTripReportIdOrderByCreatedAtDesc( - tripReportId, - PageRequest.of(page, size), - ) + ): Slice = studyLogQueryRepository.findSliceByTripReportIdOrderByCreatedAtDesc(tripReportId, PageRequest.of(page, size)) fun getStudyLogIdsByTripId(tripId: Long): List = studyLogQueryRepository.findAllIdsByTripIdOrderByCreatedDesc(tripId) diff --git a/src/main/kotlin/com/ject/studytrip/studylog/domain/error/StudyLogErrorCode.kt b/src/main/kotlin/com/ject/studytrip/studylog/domain/error/StudyLogErrorCode.kt index 48714ee..7222c26 100644 --- a/src/main/kotlin/com/ject/studytrip/studylog/domain/error/StudyLogErrorCode.kt +++ b/src/main/kotlin/com/ject/studytrip/studylog/domain/error/StudyLogErrorCode.kt @@ -4,19 +4,12 @@ import com.ject.studytrip.global.exception.error.ErrorCode import org.springframework.http.HttpStatus enum class StudyLogErrorCode( - private val status: HttpStatus, - private val message: String, + override val status: HttpStatus, + override val message: String, ) : ErrorCode { // 400 STUDY_LOG_ALREADY_DELETED(HttpStatus.BAD_REQUEST, "이미 삭제된 학습 로그입니다."), // 404 STUDY_LOG_NOT_FOUND(HttpStatus.NOT_FOUND, "요청한 학습 로그를 찾을 수 없습니다."), - ; - - override fun getName(): String = name - - override fun getStatus(): HttpStatus = status - - override fun getMessage(): String = message } diff --git a/src/main/kotlin/com/ject/studytrip/studylog/domain/model/StudyLog.kt b/src/main/kotlin/com/ject/studytrip/studylog/domain/model/StudyLog.kt new file mode 100644 index 0000000..2997a3a --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/studylog/domain/model/StudyLog.kt @@ -0,0 +1,53 @@ +package com.ject.studytrip.studylog.domain.model + +import com.ject.studytrip.global.common.entity.BaseTimeEntity +import com.ject.studytrip.member.domain.model.Member +import com.ject.studytrip.trip.domain.model.DailyGoal +import jakarta.persistence.Column +import jakarta.persistence.Entity +import jakarta.persistence.FetchType +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Id +import jakarta.persistence.JoinColumn +import jakarta.persistence.ManyToOne +import org.springframework.util.StringUtils.hasText +import java.time.LocalDateTime + +@Entity +class StudyLog protected constructor( + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id", nullable = false) + var member: Member, + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "daily_goal_id", nullable = false) + var dailyGoal: DailyGoal, + @Column(nullable = false) + var title: String, + @Column(nullable = false) + var content: String, + var imageUrl: String? = null, +) : BaseTimeEntity() { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + var id: Long? = null + protected set + + companion object { + fun of( + member: Member, + dailyGoal: DailyGoal, + content: String, + ): StudyLog = StudyLog(member, dailyGoal, dailyGoal.title, content, null) + } + + fun updateImageUrl(imageUrl: String) { + if (hasText(imageUrl)) { + this.imageUrl = imageUrl + } + } + + fun updateDeletedAt(now: LocalDateTime = LocalDateTime.now()) { + markDeleted(now) + } +} diff --git a/src/main/kotlin/com/ject/studytrip/studylog/domain/model/StudyLogDailyMission.kt b/src/main/kotlin/com/ject/studytrip/studylog/domain/model/StudyLogDailyMission.kt new file mode 100644 index 0000000..4054acd --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/studylog/domain/model/StudyLogDailyMission.kt @@ -0,0 +1,33 @@ +package com.ject.studytrip.studylog.domain.model + +import com.ject.studytrip.global.common.entity.BaseTimeEntity +import com.ject.studytrip.mission.domain.model.DailyMission +import jakarta.persistence.Entity +import jakarta.persistence.FetchType +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Id +import jakarta.persistence.JoinColumn +import jakarta.persistence.ManyToOne + +@Entity +class StudyLogDailyMission protected constructor( + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "study_log_id", nullable = false) + var studyLog: StudyLog, + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "daily_mission_id", nullable = false) + var dailyMission: DailyMission, +) : BaseTimeEntity() { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + var id: Long? = null + protected set + + companion object { + fun of( + studyLog: StudyLog, + dailyMission: DailyMission, + ): StudyLogDailyMission = StudyLogDailyMission(studyLog, dailyMission) + } +} diff --git a/src/main/kotlin/com/ject/studytrip/studylog/domain/policy/StudyLogPolicy.kt b/src/main/kotlin/com/ject/studytrip/studylog/domain/policy/StudyLogPolicy.kt index c4a11c6..1c89272 100644 --- a/src/main/kotlin/com/ject/studytrip/studylog/domain/policy/StudyLogPolicy.kt +++ b/src/main/kotlin/com/ject/studytrip/studylog/domain/policy/StudyLogPolicy.kt @@ -6,7 +6,7 @@ import com.ject.studytrip.studylog.domain.model.StudyLog object StudyLogPolicy { fun validateNotDeleted(studyLog: StudyLog) { - if (studyLog.isDeleted) { + if (studyLog.isDeleted()) { throw CustomException(StudyLogErrorCode.STUDY_LOG_ALREADY_DELETED) } } diff --git a/src/main/kotlin/com/ject/studytrip/studylog/domain/repository/StudyLogCommandRepository.kt b/src/main/kotlin/com/ject/studytrip/studylog/domain/repository/StudyLogCommandRepository.kt new file mode 100644 index 0000000..0be5853 --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/studylog/domain/repository/StudyLogCommandRepository.kt @@ -0,0 +1,11 @@ +package com.ject.studytrip.studylog.domain.repository + +interface StudyLogCommandRepository { + fun deleteAllByDeletedAtIsNotNull(): Long + + fun deleteAllByDeletedMemberOwner(): Long + + fun deleteAllByDeletedDailyGoalOwner(): Long + + fun deleteByMemberId(memberId: Long): Long +} diff --git a/src/main/kotlin/com/ject/studytrip/studylog/domain/repository/StudyLogDailyMissionCommandRepository.kt b/src/main/kotlin/com/ject/studytrip/studylog/domain/repository/StudyLogDailyMissionCommandRepository.kt new file mode 100644 index 0000000..52bd0ff --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/studylog/domain/repository/StudyLogDailyMissionCommandRepository.kt @@ -0,0 +1,11 @@ +package com.ject.studytrip.studylog.domain.repository + +interface StudyLogDailyMissionCommandRepository { + fun deleteAllByDeletedAtIsNotNull(): Long + + fun deleteAllByDeletedDailyMissionOwner(): Long + + fun deleteAllByDeletedStudyLogOwner(): Long + + fun deleteAllByMemberId(memberId: Long): Long +} diff --git a/src/main/kotlin/com/ject/studytrip/studylog/domain/repository/StudyLogDailyMissionQueryRepository.kt b/src/main/kotlin/com/ject/studytrip/studylog/domain/repository/StudyLogDailyMissionQueryRepository.kt new file mode 100644 index 0000000..7995e0e --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/studylog/domain/repository/StudyLogDailyMissionQueryRepository.kt @@ -0,0 +1,7 @@ +package com.ject.studytrip.studylog.domain.repository + +import com.ject.studytrip.studylog.domain.model.StudyLogDailyMission + +interface StudyLogDailyMissionQueryRepository { + fun findStudyLogDailyMissionsGroupedByStudyLogId(studyLogIds: List): Map> +} diff --git a/src/main/kotlin/com/ject/studytrip/studylog/domain/repository/StudyLogQueryRepository.kt b/src/main/kotlin/com/ject/studytrip/studylog/domain/repository/StudyLogQueryRepository.kt new file mode 100644 index 0000000..d398bff --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/studylog/domain/repository/StudyLogQueryRepository.kt @@ -0,0 +1,26 @@ +package com.ject.studytrip.studylog.domain.repository + +import com.ject.studytrip.studylog.domain.model.StudyLog +import org.springframework.data.domain.Pageable +import org.springframework.data.domain.Slice + +interface StudyLogQueryRepository { + fun findSliceByTripId( + tripId: Long, + pageable: Pageable, + order: String, + ): Slice + + fun findSliceByTripReportIdOrderByCreatedAtDesc( + tripReportId: Long, + pageable: Pageable, + ): Slice + + fun findAllIdsByTripIdOrderByCreatedDesc(tripId: Long): List + + fun findImageUrlsByMemberId(memberId: Long): List + + fun countStudyLogsByTripId(tripId: Long): Long + + fun countActiveStudyLogsByMemberId(memberId: Long): Long +} diff --git a/src/main/kotlin/com/ject/studytrip/studylog/infra/querydsl/StudyLogCommandRepositoryAdapter.kt b/src/main/kotlin/com/ject/studytrip/studylog/infra/querydsl/StudyLogCommandRepositoryAdapter.kt new file mode 100644 index 0000000..f6c79c9 --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/studylog/infra/querydsl/StudyLogCommandRepositoryAdapter.kt @@ -0,0 +1,50 @@ +package com.ject.studytrip.studylog.infra.querydsl + +import com.ject.studytrip.member.domain.model.QMember.member +import com.ject.studytrip.studylog.domain.model.QStudyLog.studyLog +import com.ject.studytrip.studylog.domain.repository.StudyLogCommandRepository +import com.ject.studytrip.trip.domain.model.QDailyGoal.dailyGoal +import com.querydsl.jpa.JPAExpressions +import com.querydsl.jpa.impl.JPAQueryFactory +import org.springframework.stereotype.Repository + +@Repository +class StudyLogCommandRepositoryAdapter( + private val queryFactory: JPAQueryFactory, +) : StudyLogCommandRepository { + override fun deleteAllByDeletedAtIsNotNull(): Long = + queryFactory + .delete(studyLog) + .where(studyLog.deletedAt.isNotNull) + .execute() + + override fun deleteAllByDeletedMemberOwner(): Long = + queryFactory + .delete(studyLog) + .where( + studyLog.member.id.`in`( + JPAExpressions + .select(member.id) + .from(member) + .where(member.deletedAt.isNotNull), + ), + ).execute() + + override fun deleteAllByDeletedDailyGoalOwner(): Long = + queryFactory + .delete(studyLog) + .where( + studyLog.dailyGoal.id.`in`( + JPAExpressions + .select(dailyGoal.id) + .from(dailyGoal) + .where(dailyGoal.deletedAt.isNotNull), + ), + ).execute() + + override fun deleteByMemberId(memberId: Long): Long = + queryFactory + .delete(studyLog) + .where(studyLog.member.id.eq(memberId)) + .execute() +} diff --git a/src/main/kotlin/com/ject/studytrip/studylog/infra/querydsl/StudyLogDailyMissionCommandRepositoryAdapter.kt b/src/main/kotlin/com/ject/studytrip/studylog/infra/querydsl/StudyLogDailyMissionCommandRepositoryAdapter.kt new file mode 100644 index 0000000..6852420 --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/studylog/infra/querydsl/StudyLogDailyMissionCommandRepositoryAdapter.kt @@ -0,0 +1,61 @@ +package com.ject.studytrip.studylog.infra.querydsl + +import com.ject.studytrip.mission.domain.model.QDailyMission.dailyMission +import com.ject.studytrip.studylog.domain.model.QStudyLog.studyLog +import com.ject.studytrip.studylog.domain.model.QStudyLogDailyMission.studyLogDailyMission +import com.ject.studytrip.studylog.domain.repository.StudyLogDailyMissionCommandRepository +import com.querydsl.jpa.JPAExpressions +import com.querydsl.jpa.impl.JPAQueryFactory +import org.springframework.stereotype.Repository + +@Repository +class StudyLogDailyMissionCommandRepositoryAdapter( + private val queryFactory: JPAQueryFactory, +) : StudyLogDailyMissionCommandRepository { + override fun deleteAllByDeletedAtIsNotNull(): Long = + queryFactory + .delete(studyLogDailyMission) + .where(studyLogDailyMission.deletedAt.isNotNull) + .execute() + + override fun deleteAllByDeletedDailyMissionOwner(): Long = + queryFactory + .delete(studyLogDailyMission) + .where( + studyLogDailyMission.dailyMission.id.`in`( + JPAExpressions + .select(dailyMission.id) + .from(dailyMission) + .where(dailyMission.deletedAt.isNotNull), + ), + ).execute() + + override fun deleteAllByDeletedStudyLogOwner(): Long = + queryFactory + .delete(studyLogDailyMission) + .where( + studyLogDailyMission.studyLog.id.`in`( + JPAExpressions + .select(studyLog.id) + .from(studyLog) + .where(studyLog.deletedAt.isNotNull), + ), + ).execute() + + override fun deleteAllByMemberId(memberId: Long): Long { + val ids = + queryFactory + .select(studyLogDailyMission.id) + .from(studyLogDailyMission) + .join(studyLogDailyMission.studyLog, studyLog) + .where(studyLog.member.id.eq(memberId)) + .fetch() + + if (ids.isEmpty()) return 0L + + return queryFactory + .delete(studyLogDailyMission) + .where(studyLogDailyMission.id.`in`(ids)) + .execute() + } +} diff --git a/src/main/kotlin/com/ject/studytrip/studylog/infra/querydsl/StudyLogDailyMissionQueryRepositoryAdapter.kt b/src/main/kotlin/com/ject/studytrip/studylog/infra/querydsl/StudyLogDailyMissionQueryRepositoryAdapter.kt new file mode 100644 index 0000000..608db76 --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/studylog/infra/querydsl/StudyLogDailyMissionQueryRepositoryAdapter.kt @@ -0,0 +1,26 @@ +package com.ject.studytrip.studylog.infra.querydsl + +import com.ject.studytrip.global.util.EntityExtensions.requireId +import com.ject.studytrip.mission.domain.model.QDailyMission.dailyMission +import com.ject.studytrip.mission.domain.model.QMission.mission +import com.ject.studytrip.studylog.domain.model.QStudyLogDailyMission.studyLogDailyMission +import com.ject.studytrip.studylog.domain.model.StudyLogDailyMission +import com.ject.studytrip.studylog.domain.repository.StudyLogDailyMissionQueryRepository +import com.querydsl.jpa.impl.JPAQueryFactory +import org.springframework.stereotype.Repository + +@Repository +class StudyLogDailyMissionQueryRepositoryAdapter( + private val queryFactory: JPAQueryFactory, +) : StudyLogDailyMissionQueryRepository { + override fun findStudyLogDailyMissionsGroupedByStudyLogId(studyLogIds: List): Map> = + queryFactory + .selectFrom(studyLogDailyMission) + .join(studyLogDailyMission.dailyMission, dailyMission) + .fetchJoin() + .join(dailyMission.mission, mission) + .fetchJoin() + .where(studyLogDailyMission.studyLog.id.`in`(studyLogIds)) + .fetch() + .groupBy { it.studyLog.id.requireId() } +} diff --git a/src/main/kotlin/com/ject/studytrip/studylog/infra/querydsl/StudyLogQueryRepositoryAdapter.kt b/src/main/kotlin/com/ject/studytrip/studylog/infra/querydsl/StudyLogQueryRepositoryAdapter.kt new file mode 100644 index 0000000..d337a0c --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/studylog/infra/querydsl/StudyLogQueryRepositoryAdapter.kt @@ -0,0 +1,116 @@ +package com.ject.studytrip.studylog.infra.querydsl + +import com.ject.studytrip.studylog.domain.model.QStudyLog.studyLog +import com.ject.studytrip.studylog.domain.model.StudyLog +import com.ject.studytrip.studylog.domain.repository.StudyLogQueryRepository +import com.ject.studytrip.trip.domain.model.QDailyGoal.dailyGoal +import com.ject.studytrip.trip.domain.model.QTripReportStudyLog.tripReportStudyLog +import com.querydsl.core.types.OrderSpecifier +import com.querydsl.jpa.impl.JPAQueryFactory +import org.springframework.data.domain.Pageable +import org.springframework.data.domain.Slice +import org.springframework.data.domain.SliceImpl +import org.springframework.stereotype.Repository + +@Repository +class StudyLogQueryRepositoryAdapter( + private val queryFactory: JPAQueryFactory, +) : StudyLogQueryRepository { + override fun findSliceByTripId( + tripId: Long, + pageable: Pageable, + order: String, + ): Slice { + val content = + queryFactory + .selectFrom(studyLog) + .join(studyLog.dailyGoal, dailyGoal) + .where( + dailyGoal.trip.id.eq(tripId), + dailyGoal.deletedAt.isNull, + ).offset(pageable.offset) + .limit(pageable.pageSize.toLong() + 1) + .orderBy(*orderSpecifiers(order)) + .fetch() + + return toSlice(content, pageable) + } + + override fun findSliceByTripReportIdOrderByCreatedAtDesc( + tripReportId: Long, + pageable: Pageable, + ): Slice { + val content = + queryFactory + .select(studyLog) + .from(tripReportStudyLog) + .join(tripReportStudyLog.studyLog, studyLog) + .where( + tripReportStudyLog.tripReport.id.eq(tripReportId), + studyLog.deletedAt.isNull, + ).orderBy(studyLog.createdAt.desc()) + .offset(pageable.offset) + .limit(pageable.pageSize.toLong() + 1) + .fetch() + + return toSlice(content, pageable) + } + + override fun findAllIdsByTripIdOrderByCreatedDesc(tripId: Long): List = + queryFactory + .select(studyLog.id) + .from(studyLog) + .join(studyLog.dailyGoal, dailyGoal) + .where( + dailyGoal.trip.id.eq(tripId), + studyLog.deletedAt.isNull, + dailyGoal.deletedAt.isNull, + ).orderBy(studyLog.createdAt.desc()) + .fetch() + + override fun findImageUrlsByMemberId(memberId: Long): List = + queryFactory + .select(studyLog.imageUrl) + .from(studyLog) + .where(studyLog.member.id.eq(memberId)) + .fetch() + + override fun countStudyLogsByTripId(tripId: Long): Long = + queryFactory + .select(studyLog.count()) + .from(studyLog) + .join(studyLog.dailyGoal, dailyGoal) + .where( + dailyGoal.trip.id.eq(tripId), + studyLog.deletedAt.isNull, + dailyGoal.deletedAt.isNull, + ).fetchOne() + ?: 0L + + override fun countActiveStudyLogsByMemberId(memberId: Long): Long = + queryFactory + .select(studyLog.count()) + .from(studyLog) + .where( + studyLog.member.id + .eq(memberId) + .and(studyLog.deletedAt.isNull), + ).fetchOne() + ?: 0L + + private fun orderSpecifiers(order: String): Array> = + if (order.equals("OLDEST", ignoreCase = true)) { + arrayOf(studyLog.createdAt.asc(), studyLog.id.asc()) + } else { + arrayOf(studyLog.createdAt.desc(), studyLog.id.desc()) + } + + private fun toSlice( + content: List, + pageable: Pageable, + ): Slice { + val hasNext = content.size > pageable.pageSize + val result = content.take(pageable.pageSize) + return SliceImpl(result, pageable, hasNext) + } +} diff --git a/src/main/kotlin/com/ject/studytrip/studylog/presentation/controller/StudyLogController.kt b/src/main/kotlin/com/ject/studytrip/studylog/presentation/controller/StudyLogController.kt index 0e38d97..d3c5f19 100644 --- a/src/main/kotlin/com/ject/studytrip/studylog/presentation/controller/StudyLogController.kt +++ b/src/main/kotlin/com/ject/studytrip/studylog/presentation/controller/StudyLogController.kt @@ -101,7 +101,7 @@ class StudyLogController( .body( StandardResponse.success( HttpStatus.OK.value(), - PresignedStudyLogImageResponse.of(result.studyLogId, result.tmpKey, result.presignedUrl), + PresignedStudyLogImageResponse(result.studyLogId, result.tmpKey, result.presignedUrl), ), ) } diff --git a/src/main/kotlin/com/ject/studytrip/studylog/presentation/dto/response/CreateStudyLogResponse.kt b/src/main/kotlin/com/ject/studytrip/studylog/presentation/dto/response/CreateStudyLogResponse.kt index 41defe7..b28b9e3 100644 --- a/src/main/kotlin/com/ject/studytrip/studylog/presentation/dto/response/CreateStudyLogResponse.kt +++ b/src/main/kotlin/com/ject/studytrip/studylog/presentation/dto/response/CreateStudyLogResponse.kt @@ -8,7 +8,6 @@ data class CreateStudyLogResponse( val studyLogId: Long, ) { companion object { - @JvmStatic fun of(studyLogInfo: StudyLogInfo): CreateStudyLogResponse = CreateStudyLogResponse(studyLogInfo.studyLogId) } } diff --git a/src/main/kotlin/com/ject/studytrip/studylog/presentation/dto/response/LoadStudyLogsSliceResponse.kt b/src/main/kotlin/com/ject/studytrip/studylog/presentation/dto/response/LoadStudyLogsSliceResponse.kt index d880644..e0cff65 100644 --- a/src/main/kotlin/com/ject/studytrip/studylog/presentation/dto/response/LoadStudyLogsSliceResponse.kt +++ b/src/main/kotlin/com/ject/studytrip/studylog/presentation/dto/response/LoadStudyLogsSliceResponse.kt @@ -13,20 +13,13 @@ data class LoadStudyLogsSliceResponse( val hasNext: Boolean, ) { companion object { - @JvmStatic fun of( studyLogDetails: List, hasNext: Boolean, ): LoadStudyLogsSliceResponse = LoadStudyLogsSliceResponse( - studyLogs = - studyLogDetails.map { detail -> - StudyLogResponse.of( - studyLogInfo = detail.studyLogInfo, - studyLogDailyMissionInfos = detail.studyLogDailyMissionInfos, - ) - }, - hasNext = hasNext, + studyLogDetails.map { StudyLogResponse.of(it.studyLogInfo, it.studyLogDailyMissionInfos) }, + hasNext, ) } @@ -50,18 +43,12 @@ data class LoadStudyLogsSliceResponse( studyLogDailyMissionInfos: List, ): StudyLogResponse = StudyLogResponse( - studyLogId = studyLogInfo.studyLogId, - dailyMissions = - studyLogDailyMissionInfos.map { info -> - StudyLogDailyMissionResponse.of( - studyLogDailyMissionInfo = info, - missionInfo = info.dailyMissionInfo.missionInfo, - ) - }, - title = studyLogInfo.title, - content = studyLogInfo.content, - imageUrl = studyLogInfo.imageUrl, - createdAt = studyLogInfo.createdAt, + studyLogInfo.studyLogId, + studyLogDailyMissionInfos.map { StudyLogDailyMissionResponse.of(it, it.dailyMissionInfo.missionInfo) }, + studyLogInfo.title, + studyLogInfo.content, + studyLogInfo.imageUrl, + studyLogInfo.createdAt, ) } @@ -76,10 +63,7 @@ data class LoadStudyLogsSliceResponse( studyLogDailyMissionInfo: StudyLogDailyMissionInfo, missionInfo: MissionInfo, ): StudyLogDailyMissionResponse = - StudyLogDailyMissionResponse( - studyLogDailyMissionId = studyLogDailyMissionInfo.studyLogDailyMissionId, - missionName = missionInfo.missionName, - ) + StudyLogDailyMissionResponse(studyLogDailyMissionInfo.studyLogDailyMissionId, missionInfo.missionName) } } } diff --git a/src/main/kotlin/com/ject/studytrip/studylog/presentation/dto/response/PresignedStudyLogImageResponse.kt b/src/main/kotlin/com/ject/studytrip/studylog/presentation/dto/response/PresignedStudyLogImageResponse.kt index 5282a3b..7b36789 100644 --- a/src/main/kotlin/com/ject/studytrip/studylog/presentation/dto/response/PresignedStudyLogImageResponse.kt +++ b/src/main/kotlin/com/ject/studytrip/studylog/presentation/dto/response/PresignedStudyLogImageResponse.kt @@ -9,18 +9,4 @@ data class PresignedStudyLogImageResponse( val tmpKey: String, @field:Schema(description = "학습 로그 이미지 업로드용 Presigned URL") val presignedUrl: String, -) { - companion object { - @JvmStatic - fun of( - studyLogId: Long, - tmpKey: String, - presignedUrl: String, - ): PresignedStudyLogImageResponse = - PresignedStudyLogImageResponse( - studyLogId = studyLogId, - tmpKey = tmpKey, - presignedUrl = presignedUrl, - ) - } -} +) diff --git a/src/main/kotlin/com/ject/studytrip/trip/application/dto/DailyGoalDetail.kt b/src/main/kotlin/com/ject/studytrip/trip/application/dto/DailyGoalDetail.kt index dd44add..a3fd46b 100644 --- a/src/main/kotlin/com/ject/studytrip/trip/application/dto/DailyGoalDetail.kt +++ b/src/main/kotlin/com/ject/studytrip/trip/application/dto/DailyGoalDetail.kt @@ -7,13 +7,4 @@ data class DailyGoalDetail( val dailyGoalInfo: DailyGoalInfo, val pomodoroInfo: PomodoroInfo, val dailyMissionInfos: List, -) { - companion object { - @JvmStatic - fun from( - dailyGoalInfo: DailyGoalInfo, - pomodoroInfo: PomodoroInfo, - dailyMissionInfos: List, - ): DailyGoalDetail = DailyGoalDetail(dailyGoalInfo, pomodoroInfo, dailyMissionInfos) - } -} +) diff --git a/src/main/kotlin/com/ject/studytrip/trip/application/dto/DailyGoalInfo.kt b/src/main/kotlin/com/ject/studytrip/trip/application/dto/DailyGoalInfo.kt index 26af3ee..1fc963f 100644 --- a/src/main/kotlin/com/ject/studytrip/trip/application/dto/DailyGoalInfo.kt +++ b/src/main/kotlin/com/ject/studytrip/trip/application/dto/DailyGoalInfo.kt @@ -1,6 +1,7 @@ package com.ject.studytrip.trip.application.dto import com.ject.studytrip.global.util.DateUtil +import com.ject.studytrip.global.util.EntityExtensions.requireId import com.ject.studytrip.trip.domain.model.DailyGoal data class DailyGoalInfo( @@ -12,14 +13,13 @@ data class DailyGoalInfo( val deletedAt: String?, ) { companion object { - @JvmStatic fun from(dailyGoal: DailyGoal): DailyGoalInfo = DailyGoalInfo( - dailyGoal.id, + dailyGoal.id.requireId(), dailyGoal.title, - dailyGoal.isCompleted, - DateUtil.formatDateTime(dailyGoal.createdAt), - DateUtil.formatDateTime(dailyGoal.updatedAt), + dailyGoal.isCompleted(), + DateUtil.formatDateTime(requireNotNull(dailyGoal.createdAt)), + DateUtil.formatDateTime(requireNotNull(dailyGoal.updatedAt)), dailyGoal.deletedAt?.let { DateUtil.formatDateTime(it) }, ) } diff --git a/src/main/kotlin/com/ject/studytrip/trip/application/dto/PresignedTripReportImageInfo.kt b/src/main/kotlin/com/ject/studytrip/trip/application/dto/PresignedTripReportImageInfo.kt index e5c840f..6a72fa1 100644 --- a/src/main/kotlin/com/ject/studytrip/trip/application/dto/PresignedTripReportImageInfo.kt +++ b/src/main/kotlin/com/ject/studytrip/trip/application/dto/PresignedTripReportImageInfo.kt @@ -4,13 +4,4 @@ data class PresignedTripReportImageInfo( val tripReportId: Long, val tmpKey: String, val presignedUrl: String, -) { - companion object { - @JvmStatic - fun of( - tripReportId: Long, - tmpKey: String, - presignedUrl: String, - ): PresignedTripReportImageInfo = PresignedTripReportImageInfo(tripReportId, tmpKey, presignedUrl) - } -} +) diff --git a/src/main/kotlin/com/ject/studytrip/trip/application/dto/TripCategoryInfo.kt b/src/main/kotlin/com/ject/studytrip/trip/application/dto/TripCategoryInfo.kt index 8e96f10..c816a1a 100644 --- a/src/main/kotlin/com/ject/studytrip/trip/application/dto/TripCategoryInfo.kt +++ b/src/main/kotlin/com/ject/studytrip/trip/application/dto/TripCategoryInfo.kt @@ -7,7 +7,6 @@ data class TripCategoryInfo( val value: String, ) { companion object { - @JvmStatic - fun from(category: TripCategory): TripCategoryInfo = TripCategoryInfo(category.name, category.getValue()) + fun from(category: TripCategory): TripCategoryInfo = TripCategoryInfo(category.name, category.value) } } diff --git a/src/main/kotlin/com/ject/studytrip/trip/application/dto/TripCount.kt b/src/main/kotlin/com/ject/studytrip/trip/application/dto/TripCount.kt index cdd89a5..d2fa6c4 100644 --- a/src/main/kotlin/com/ject/studytrip/trip/application/dto/TripCount.kt +++ b/src/main/kotlin/com/ject/studytrip/trip/application/dto/TripCount.kt @@ -3,12 +3,4 @@ package com.ject.studytrip.trip.application.dto data class TripCount( val course: Long, val explore: Long, -) { - companion object { - @JvmStatic - fun of( - course: Long, - explore: Long, - ): TripCount = TripCount(course, explore) - } -} +) diff --git a/src/main/kotlin/com/ject/studytrip/trip/application/dto/TripDetail.kt b/src/main/kotlin/com/ject/studytrip/trip/application/dto/TripDetail.kt index e5e346e..f4af8d7 100644 --- a/src/main/kotlin/com/ject/studytrip/trip/application/dto/TripDetail.kt +++ b/src/main/kotlin/com/ject/studytrip/trip/application/dto/TripDetail.kt @@ -5,12 +5,4 @@ import com.ject.studytrip.stamp.application.dto.StampInfo data class TripDetail( val tripInfo: TripInfo, val stampInfos: List, -) { - companion object { - @JvmStatic - fun from( - tripInfo: TripInfo, - stampInfos: List, - ): TripDetail = TripDetail(tripInfo, stampInfos) - } -} +) diff --git a/src/main/kotlin/com/ject/studytrip/trip/application/dto/TripInfo.kt b/src/main/kotlin/com/ject/studytrip/trip/application/dto/TripInfo.kt index 51da4cf..aab2d31 100644 --- a/src/main/kotlin/com/ject/studytrip/trip/application/dto/TripInfo.kt +++ b/src/main/kotlin/com/ject/studytrip/trip/application/dto/TripInfo.kt @@ -1,6 +1,7 @@ package com.ject.studytrip.trip.application.dto import com.ject.studytrip.global.util.DateUtil +import com.ject.studytrip.global.util.EntityExtensions.requireId import com.ject.studytrip.trip.domain.model.Trip import com.ject.studytrip.trip.domain.model.TripCategory @@ -21,16 +22,15 @@ data class TripInfo( val deletedAt: String?, ) { companion object { - @JvmStatic fun from( trip: Trip, dDay: Int?, progress: Int?, ): TripInfo = TripInfo( - trip.id, - trip.name, - trip.memo, + trip.id.requireId(), + requireNotNull(trip.name), + requireNotNull(trip.memo), trip.category, DateUtil.formatDate(trip.startDate), trip.endDate?.let { DateUtil.formatDate(it) }, @@ -38,9 +38,9 @@ data class TripInfo( trip.totalStamps, trip.completedStamps, progress, - trip.isCompleted, - DateUtil.formatDateTime(trip.createdAt), - DateUtil.formatDateTime(trip.updatedAt), + trip.isCompleted(), + DateUtil.formatDateTime(requireNotNull(trip.createdAt)), + DateUtil.formatDateTime(requireNotNull(trip.updatedAt)), trip.deletedAt?.let { DateUtil.formatDateTime(it) }, ) } diff --git a/src/main/kotlin/com/ject/studytrip/trip/application/dto/TripReportDetail.kt b/src/main/kotlin/com/ject/studytrip/trip/application/dto/TripReportDetail.kt index 37a2f6b..a8271cc 100644 --- a/src/main/kotlin/com/ject/studytrip/trip/application/dto/TripReportDetail.kt +++ b/src/main/kotlin/com/ject/studytrip/trip/application/dto/TripReportDetail.kt @@ -5,12 +5,4 @@ import com.ject.studytrip.studylog.application.dto.StudyLogSliceInfo class TripReportDetail( val tripReportInfo: TripReportInfo, val studyLogSliceInfo: StudyLogSliceInfo, -) { - companion object { - @JvmStatic - fun from( - tripReportInfo: TripReportInfo, - studyLogSliceInfo: StudyLogSliceInfo, - ) = TripReportDetail(tripReportInfo, studyLogSliceInfo) - } -} +) diff --git a/src/main/kotlin/com/ject/studytrip/trip/application/dto/TripReportInfo.kt b/src/main/kotlin/com/ject/studytrip/trip/application/dto/TripReportInfo.kt index ea2e47d..0ad7414 100644 --- a/src/main/kotlin/com/ject/studytrip/trip/application/dto/TripReportInfo.kt +++ b/src/main/kotlin/com/ject/studytrip/trip/application/dto/TripReportInfo.kt @@ -1,6 +1,7 @@ package com.ject.studytrip.trip.application.dto import com.ject.studytrip.global.util.DateUtil +import com.ject.studytrip.global.util.EntityExtensions.requireId import com.ject.studytrip.trip.domain.model.TripReport data class TripReportInfo( @@ -8,7 +9,7 @@ data class TripReportInfo( val title: String, val content: String, val startDate: String, - val endDate: String, + val endDate: String?, val studyLogCount: Long, val totalFocusHours: Long, val studyDays: Long, @@ -19,11 +20,10 @@ data class TripReportInfo( val deletedAt: String?, ) { companion object { - @JvmStatic fun from(tripReport: TripReport): TripReportInfo = TripReportInfo( - tripReport.id, - tripReport.title, + tripReport.id.requireId(), + requireNotNull(tripReport.title), tripReport.content, tripReport.startDate, tripReport.endDate, @@ -32,8 +32,8 @@ data class TripReportInfo( tripReport.studyDays, tripReport.imageTitle, tripReport.imageUrl, - DateUtil.formatDateTime(tripReport.createdAt), - DateUtil.formatDateTime(tripReport.updatedAt), + DateUtil.formatDateTime(requireNotNull(tripReport.createdAt)), + DateUtil.formatDateTime(requireNotNull(tripReport.updatedAt)), tripReport.deletedAt?.let { DateUtil.formatDateTime(it) }, ) } diff --git a/src/main/kotlin/com/ject/studytrip/trip/application/dto/TripReportsInfo.kt b/src/main/kotlin/com/ject/studytrip/trip/application/dto/TripReportsInfo.kt index 90a17c1..cd55883 100644 --- a/src/main/kotlin/com/ject/studytrip/trip/application/dto/TripReportsInfo.kt +++ b/src/main/kotlin/com/ject/studytrip/trip/application/dto/TripReportsInfo.kt @@ -2,9 +2,4 @@ package com.ject.studytrip.trip.application.dto data class TripReportsInfo( val tripReportInfos: List, -) { - companion object { - @JvmStatic - fun of(tripReportInfos: List): TripReportsInfo = TripReportsInfo(tripReportInfos) - } -} +) diff --git a/src/main/kotlin/com/ject/studytrip/trip/application/dto/TripRetrospectDetail.kt b/src/main/kotlin/com/ject/studytrip/trip/application/dto/TripRetrospectDetail.kt index 699053f..bd9d8e0 100644 --- a/src/main/kotlin/com/ject/studytrip/trip/application/dto/TripRetrospectDetail.kt +++ b/src/main/kotlin/com/ject/studytrip/trip/application/dto/TripRetrospectDetail.kt @@ -6,13 +6,4 @@ data class TripRetrospectDetail( val summary: TripRetrospectSummary, val tripInfo: TripInfo, val studyLogSliceInfo: StudyLogSliceInfo, -) { - companion object { - @JvmStatic - fun from( - summary: TripRetrospectSummary, - tripInfo: TripInfo, - studyLogSliceInfo: StudyLogSliceInfo, - ): TripRetrospectDetail = TripRetrospectDetail(summary, tripInfo, studyLogSliceInfo) - } -} +) diff --git a/src/main/kotlin/com/ject/studytrip/trip/application/dto/TripRetrospectSummary.kt b/src/main/kotlin/com/ject/studytrip/trip/application/dto/TripRetrospectSummary.kt index 53897d1..e2179d6 100644 --- a/src/main/kotlin/com/ject/studytrip/trip/application/dto/TripRetrospectSummary.kt +++ b/src/main/kotlin/com/ject/studytrip/trip/application/dto/TripRetrospectSummary.kt @@ -5,20 +5,4 @@ data class TripRetrospectSummary( val totalFocusHours: Long, // 총 집중 시간 (시간 단위) val studyDays: Long, // 학습한 일수 (중복 날짜 제거) val studyLogIds: List, // 학습 로그 ID 목록 -) { - companion object { - @JvmStatic - fun of( - studyLogCount: Long, - totalFocusHours: Long, - studyDays: Long, - studyLogIds: List, - ): TripRetrospectSummary = - TripRetrospectSummary( - studyLogCount, - totalFocusHours, - studyDays, - studyLogIds, - ) - } -} +) diff --git a/src/main/kotlin/com/ject/studytrip/trip/application/dto/TripSliceInfo.kt b/src/main/kotlin/com/ject/studytrip/trip/application/dto/TripSliceInfo.kt index 6531948..38357e9 100644 --- a/src/main/kotlin/com/ject/studytrip/trip/application/dto/TripSliceInfo.kt +++ b/src/main/kotlin/com/ject/studytrip/trip/application/dto/TripSliceInfo.kt @@ -3,12 +3,4 @@ package com.ject.studytrip.trip.application.dto data class TripSliceInfo( val tripInfos: List, val hasNext: Boolean, -) { - companion object { - @JvmStatic - fun of( - tripInfos: List, - hasNext: Boolean, - ): TripSliceInfo = TripSliceInfo(tripInfos, hasNext) - } -} +) diff --git a/src/main/kotlin/com/ject/studytrip/trip/application/facade/DailyGoalFacade.kt b/src/main/kotlin/com/ject/studytrip/trip/application/facade/DailyGoalFacade.kt index b237a19..0bbed18 100644 --- a/src/main/kotlin/com/ject/studytrip/trip/application/facade/DailyGoalFacade.kt +++ b/src/main/kotlin/com/ject/studytrip/trip/application/facade/DailyGoalFacade.kt @@ -1,6 +1,7 @@ package com.ject.studytrip.trip.application.facade import com.ject.studytrip.global.common.constants.CacheNameConstants.DAILY_GOAL +import com.ject.studytrip.global.util.EntityExtensions.requireId import com.ject.studytrip.mission.application.dto.DailyMissionInfo import com.ject.studytrip.mission.application.service.DailyMissionCommandService import com.ject.studytrip.mission.application.service.DailyMissionQueryService @@ -71,11 +72,11 @@ class DailyGoalFacade( request: UpdateDailyGoalRequest, ) { val trip = tripQueryService.getValidTrip(memberId, tripId) - val dailyGoal = dailyGoalQueryService.getValidDailyGoal(trip.id, dailyGoalId) + val dailyGoal = dailyGoalQueryService.getValidDailyGoal(tripId, dailyGoalId) // 삭제할 데일리 미션이 있을 경우 if (request.deleteDailyMissionIds.isNotEmpty()) { - val deleteDailyMissions = dailyMissionQueryService.getValidDailyMissionsByIds(dailyGoal.id, request.deleteDailyMissionIds) + val deleteDailyMissions = dailyMissionQueryService.getValidDailyMissionsByIds(dailyGoalId, request.deleteDailyMissionIds) deleteDailyMissions.forEach { dailyMissionCommandService.deleteDailyMission(it) } } @@ -97,14 +98,14 @@ class DailyGoalFacade( dailyGoalId: Long, ) { val trip = tripQueryService.getValidTrip(memberId, tripId) - val dailyGoal = dailyGoalQueryService.getValidDailyGoal(trip.id, dailyGoalId) - val pomodoro = pomodoroQueryService.getValidPomodoroByDailyGoalId(dailyGoal.id) + val dailyGoal = dailyGoalQueryService.getValidDailyGoal(trip.id.requireId(), dailyGoalId) + val pomodoro = pomodoroQueryService.getValidPomodoroByDailyGoalId(dailyGoalId) // 뽀모도로 삭제 pomodoroCommandService.deletePomodoro(pomodoro) // 데일리 미션 삭제 - val dailyMissions = dailyMissionQueryService.getDailyMissionsByDailyGoalId(dailyGoal.id) + val dailyMissions = dailyMissionQueryService.getDailyMissionsByDailyGoalId(dailyGoalId) dailyMissions.forEach { dailyMissionCommandService.deleteDailyMission(it) } // 데일리 목표 삭제 @@ -122,27 +123,24 @@ class DailyGoalFacade( dailyGoalId: Long, ): DailyGoalDetail { val trip = tripQueryService.getValidTrip(memberId, tripId) - val dailyGoal = dailyGoalQueryService.getValidDailyGoal(trip.id, dailyGoalId) - val pomodoro = pomodoroQueryService.getValidPomodoroByDailyGoalId(dailyGoal.id) - val dailyMissions = dailyMissionQueryService.getDailyMissionsByDailyGoalId(dailyGoal.id) - - return DailyGoalDetail.from( - DailyGoalInfo.from(dailyGoal), - PomodoroInfo.from(pomodoro), - dailyMissions.map(DailyMissionInfo::from), - ) + val dailyGoal = dailyGoalQueryService.getValidDailyGoal(trip.id.requireId(), dailyGoalId) + val pomodoro = pomodoroQueryService.getValidPomodoroByDailyGoalId(dailyGoalId) + val dailyMissions = dailyMissionQueryService.getDailyMissionsByDailyGoalId(dailyGoalId) + + return DailyGoalDetail(DailyGoalInfo.from(dailyGoal), PomodoroInfo.from(pomodoro), dailyMissions.map(DailyMissionInfo::from)) } private fun getValidMissionsByTripCategory( trip: Trip, missionIds: List, ): List { + val tripId = trip.id.requireId() val missions = missionQueryService.getValidMissionsByIds(missionIds) - missions.forEach { stampCommandService.validateStampBelongsToTrip(trip.id, it.stamp) } + missions.forEach { stampCommandService.validateStampBelongsToTrip(tripId, it.stamp) } if (trip.category == TripCategory.COURSE) { - val stampId = stampQueryService.getFirstInProcessingStampsForCourseTrip(trip.id).id - missionCommandService.validateMissionsBelongToStamp(stampId, missions) + val stamp = stampQueryService.getFirstInProcessingStampsForCourseTrip(tripId) + missionCommandService.validateMissionsBelongToStamp(stamp.id.requireId(), missions) } return missions diff --git a/src/main/kotlin/com/ject/studytrip/trip/application/facade/TripFacade.kt b/src/main/kotlin/com/ject/studytrip/trip/application/facade/TripFacade.kt index 5aeb688..7b835ba 100644 --- a/src/main/kotlin/com/ject/studytrip/trip/application/facade/TripFacade.kt +++ b/src/main/kotlin/com/ject/studytrip/trip/application/facade/TripFacade.kt @@ -4,6 +4,7 @@ import com.ject.studytrip.global.common.constants.CacheNameConstants.STAMP import com.ject.studytrip.global.common.constants.CacheNameConstants.STAMPS import com.ject.studytrip.global.common.constants.CacheNameConstants.TRIP import com.ject.studytrip.global.common.constants.CacheNameConstants.TRIPS +import com.ject.studytrip.global.util.EntityExtensions.requireId import com.ject.studytrip.member.application.service.MemberQueryService import com.ject.studytrip.stamp.application.dto.StampInfo import com.ject.studytrip.stamp.application.service.StampCommandService @@ -75,7 +76,7 @@ class TripFacade( tripCommandService.updateTrip(trip, request) if (request.category != null) { - stampCommandService.updateStampOrdersByTripCategoryChange(trip.id, TripCategory.from(request.category)) + stampCommandService.updateStampOrdersByTripCategoryChange(trip.id.requireId(), TripCategory.from(request.category)) } } @@ -122,7 +123,7 @@ class TripFacade( ) { val trip = tripQueryService.getValidTrip(memberId, tripId) - stampCommandService.validateAllStampsCompletedByTripId(trip.id) + stampCommandService.validateAllStampsCompletedByTripId(trip.id.requireId()) tripCommandService.completeTrip(trip) } @@ -150,7 +151,7 @@ class TripFacade( TripInfo.from(trip, dDay, progress) }.sortedWith(compareBy(nullsLast()) { it.dDay }) - return TripSliceInfo.of(tripInfos, tripSlice.hasNext()) + return TripSliceInfo(tripInfos, tripSlice.hasNext()) } @Cacheable( @@ -163,15 +164,15 @@ class TripFacade( tripId: Long, ): TripDetail { val member = memberQueryService.getValidMember(memberId) - val trip = tripQueryService.getValidTrip(member.id, tripId) + val trip = tripQueryService.getValidTrip(member.id.requireId(), tripId) val dDay = calculateDDay(trip.endDate) val progress: Int = calculateProgress(trip.totalStamps, trip.completedStamps) - val stamps = stampQueryService.getStampsByTripId(trip.id) + val stamps = stampQueryService.getStampsByTripId(trip.id.requireId()) val stampInfos = stamps.map { StampInfo.from(it) } - return TripDetail.from(TripInfo.from(trip, dDay, progress), stampInfos) + return TripDetail(TripInfo.from(trip, dDay, progress), stampInfos) } private fun calculateDDay(endDate: LocalDate?): Int? { diff --git a/src/main/kotlin/com/ject/studytrip/trip/application/facade/TripReportFacade.kt b/src/main/kotlin/com/ject/studytrip/trip/application/facade/TripReportFacade.kt index a23a5c7..000d3c6 100644 --- a/src/main/kotlin/com/ject/studytrip/trip/application/facade/TripReportFacade.kt +++ b/src/main/kotlin/com/ject/studytrip/trip/application/facade/TripReportFacade.kt @@ -2,6 +2,7 @@ package com.ject.studytrip.trip.application.facade import com.ject.studytrip.global.common.constants.CacheNameConstants.TRIP_REPORT import com.ject.studytrip.global.common.constants.CacheNameConstants.TRIP_REPORTS +import com.ject.studytrip.global.util.EntityExtensions.requireId import com.ject.studytrip.image.application.service.ImageService import com.ject.studytrip.member.application.service.MemberQueryService import com.ject.studytrip.pomodoro.application.service.PomodoroQueryService @@ -10,6 +11,7 @@ import com.ject.studytrip.studylog.application.dto.StudyLogSliceInfo import com.ject.studytrip.studylog.application.service.StudyLogDailyMissionQueryService import com.ject.studytrip.studylog.application.service.StudyLogQueryService import com.ject.studytrip.studylog.domain.model.StudyLog +import com.ject.studytrip.trip.application.dto.PresignedTripReportImageInfo import com.ject.studytrip.trip.application.dto.TripInfo import com.ject.studytrip.trip.application.dto.TripReportDetail import com.ject.studytrip.trip.application.dto.TripReportInfo @@ -23,7 +25,6 @@ import com.ject.studytrip.trip.application.service.TripReportStudyLogCommandServ import com.ject.studytrip.trip.presentation.dto.request.ConfirmTripReportImageRequest import com.ject.studytrip.trip.presentation.dto.request.CreateTripReportRequest import com.ject.studytrip.trip.presentation.dto.request.PresignTripReportImageRequest -import com.ject.studytrip.trip.presentation.dto.response.PresignedTripReportImageResponse import org.springframework.cache.annotation.CacheEvict import org.springframework.cache.annotation.Cacheable import org.springframework.cache.annotation.Caching @@ -96,17 +97,17 @@ class TripReportFacade( size: Int, ): TripRetrospectDetail { val trip = tripQueryService.getValidCompletedTrip(memberId, tripId) - val studyLogSlice = studyLogQueryService.getStudyLogsSliceByTripId(trip.id, page, size, "LATEST") - val studyLogCount = studyLogQueryService.getStudyLogCountByTripId(trip.id) - val totalFocusHours = pomodoroQueryService.getTotalFocusHoursByTripId(trip.id) + val studyLogSlice = studyLogQueryService.getStudyLogsSliceByTripId(trip.id.requireId(), page, size, "LATEST") + val studyLogCount = studyLogQueryService.getStudyLogCountByTripId(trip.id.requireId()) + val totalFocusHours = pomodoroQueryService.getTotalFocusHoursByTripId(trip.id.requireId()) val studyDays = trip.endDate?.let { maxOf(0, ChronoUnit.DAYS.between(trip.startDate, it) + 1) } ?: 0L - val studyLogIds = studyLogQueryService.getStudyLogIdsByTripId(trip.id) + val studyLogIds = studyLogQueryService.getStudyLogIdsByTripId(trip.id.requireId()) val summary = TripRetrospectSummary(studyLogCount, totalFocusHours, studyDays, studyLogIds) val tripInfo = TripInfo.from(trip, 0, 100) val studyLogSliceInfo = buildStudyLogSliceInfo(studyLogSlice) - return TripRetrospectDetail.from(summary, tripInfo, studyLogSliceInfo) + return TripRetrospectDetail(summary, tripInfo, studyLogSliceInfo) } @Cacheable(cacheNames = [TRIP_REPORTS], key = "T(com.ject.studytrip.global.common.factory.CacheKeyFactory).tripReports(#memberId)") @@ -114,7 +115,7 @@ class TripReportFacade( fun getTripReportsByMember(memberId: Long): TripReportsInfo { val tripReports = tripReportQueryService.getTripReportsByMemberId(memberId) - return TripReportsInfo.of(tripReports.map { TripReportInfo.from(it) }) + return TripReportsInfo(tripReports.map { TripReportInfo.from(it) }) } @Cacheable( @@ -129,23 +130,23 @@ class TripReportFacade( size: Int, ): TripReportDetail { val tripReport = tripReportQueryService.getValidTripReport(memberId, tripReportId) - val studyLogSlice = studyLogQueryService.getStudyLogsSliceByTripReportId(tripReport.id, page, size) + val studyLogSlice = studyLogQueryService.getStudyLogsSliceByTripReportId(tripReport.id.requireId(), page, size) val tripReportInfo = TripReportInfo.from(tripReport) val studyLogSliceInfo = buildStudyLogSliceInfo(studyLogSlice) - return TripReportDetail.from(tripReportInfo, studyLogSliceInfo) + return TripReportDetail(tripReportInfo, studyLogSliceInfo) } @Transactional(readOnly = true) fun issuePresignedUrl( tripReportId: Long, request: PresignTripReportImageRequest, - ): PresignedTripReportImageResponse { + ): PresignedTripReportImageInfo { val tripReport = tripReportQueryService.getTripReport(tripReportId) val info = imageService.presign(TRIP_REPORT_IMAGE_KEY_PREFIX, tripReport.id.toString(), request.originFilename) - return PresignedTripReportImageResponse.of(tripReport.id, info.tmpKey, info.presignedUrl) + return PresignedTripReportImageInfo(tripReport.id.requireId(), info.tmpKey, info.presignedUrl) } @Transactional @@ -160,12 +161,12 @@ class TripReportFacade( } private fun buildStudyLogSliceInfo(studyLogSlice: Slice): StudyLogSliceInfo { - val studyLogIds = studyLogSlice.content.map { it.id } + val studyLogIds = studyLogSlice.content.map { it.id.requireId() } // 학습 로그별 학습 로그 데일리 미션 목록 그룹화 val groupedStudyLogDailyMissions = studyLogDailyMissionQueryService.getGroupedStudyLogDailyMissionsByStudyLogIds(studyLogIds) val studyLogDetails = studyLogSlice.content.map { StudyLogDetail.from(it, groupedStudyLogDailyMissions[it.id]) } - return StudyLogSliceInfo.of(studyLogDetails, studyLogSlice.hasNext()) + return StudyLogSliceInfo(studyLogDetails, studyLogSlice.hasNext()) } } diff --git a/src/main/kotlin/com/ject/studytrip/trip/application/service/DailyGoalCommandService.kt b/src/main/kotlin/com/ject/studytrip/trip/application/service/DailyGoalCommandService.kt index d952dcd..a693259 100644 --- a/src/main/kotlin/com/ject/studytrip/trip/application/service/DailyGoalCommandService.kt +++ b/src/main/kotlin/com/ject/studytrip/trip/application/service/DailyGoalCommandService.kt @@ -15,11 +15,7 @@ class DailyGoalCommandService( fun createDailyGoal( trip: Trip, title: String, - ): DailyGoal { - val dailyGoal = DailyGoalFactory.create(trip, title) - - return dailyGoalRepository.save(dailyGoal) - } + ): DailyGoal = dailyGoalRepository.save(DailyGoalFactory.create(trip, title)) fun deleteDailyGoal(dailyGoal: DailyGoal) = dailyGoal.updateDeletedAt() diff --git a/src/main/kotlin/com/ject/studytrip/trip/application/service/TripQueryService.kt b/src/main/kotlin/com/ject/studytrip/trip/application/service/TripQueryService.kt index 464b84e..7c57199 100644 --- a/src/main/kotlin/com/ject/studytrip/trip/application/service/TripQueryService.kt +++ b/src/main/kotlin/com/ject/studytrip/trip/application/service/TripQueryService.kt @@ -43,7 +43,7 @@ class TripQueryService( val courseCount = tripQueryRepository.countActiveTripsByMemberIdAndCategory(memberId, TripCategory.COURSE) val exploreCount = tripQueryRepository.countActiveTripsByMemberIdAndCategory(memberId, TripCategory.EXPLORE) - return TripCount.of(courseCount, exploreCount) + return TripCount(courseCount, exploreCount) } fun getValidCompletedTrip( diff --git a/src/main/kotlin/com/ject/studytrip/trip/application/service/TripReportStudyLogCommandService.kt b/src/main/kotlin/com/ject/studytrip/trip/application/service/TripReportStudyLogCommandService.kt index 5b8ce3a..c1ad04f 100644 --- a/src/main/kotlin/com/ject/studytrip/trip/application/service/TripReportStudyLogCommandService.kt +++ b/src/main/kotlin/com/ject/studytrip/trip/application/service/TripReportStudyLogCommandService.kt @@ -16,9 +16,7 @@ class TripReportStudyLogCommandService( tripReport: TripReport, studyLogs: List, ) { - val tripReportStudyLogs = studyLogs.map { TripReportStudyLogFactory.create(tripReport, it) } - - tripReportStudyLogRepository.saveAll(tripReportStudyLogs) + tripReportStudyLogRepository.saveAll(studyLogs.map { TripReportStudyLogFactory.create(tripReport, it) }) } fun hardDeleteTripReportStudyLogsOwnedByDeletedMember(): Long = tripReportStudyLogCommandRepository.deleteAllByDeletedMemberOwner() diff --git a/src/main/kotlin/com/ject/studytrip/trip/domain/error/DailyGoalErrorCode.kt b/src/main/kotlin/com/ject/studytrip/trip/domain/error/DailyGoalErrorCode.kt index 141d7b1..aa76f02 100644 --- a/src/main/kotlin/com/ject/studytrip/trip/domain/error/DailyGoalErrorCode.kt +++ b/src/main/kotlin/com/ject/studytrip/trip/domain/error/DailyGoalErrorCode.kt @@ -4,8 +4,8 @@ import com.ject.studytrip.global.exception.error.ErrorCode import org.springframework.http.HttpStatus enum class DailyGoalErrorCode( - private val status: HttpStatus, - private val message: String, + override val status: HttpStatus, + override val message: String, ) : ErrorCode { // 400 DAILY_GOAL_ALREADY_DELETED(HttpStatus.BAD_REQUEST, "이미 삭제된 데일리 목표입니다."), @@ -15,11 +15,4 @@ enum class DailyGoalErrorCode( // 404 DAILY_GOAL_NOT_FOUND(HttpStatus.NOT_FOUND, "요청한 데일리 목표를 찾을 수 없습니다."), - ; - - override fun getName(): String = name - - override fun getStatus(): HttpStatus = status - - override fun getMessage(): String = message } diff --git a/src/main/kotlin/com/ject/studytrip/trip/domain/error/TripErrorCode.kt b/src/main/kotlin/com/ject/studytrip/trip/domain/error/TripErrorCode.kt index 548d35e..641c71a 100644 --- a/src/main/kotlin/com/ject/studytrip/trip/domain/error/TripErrorCode.kt +++ b/src/main/kotlin/com/ject/studytrip/trip/domain/error/TripErrorCode.kt @@ -4,8 +4,8 @@ import com.ject.studytrip.global.exception.error.ErrorCode import org.springframework.http.HttpStatus enum class TripErrorCode( - private val status: HttpStatus, - private val message: String, + override val status: HttpStatus, + override val message: String, ) : ErrorCode { // 400 TRIP_ALREADY_DELETED(HttpStatus.BAD_REQUEST, "이미 삭제된 여행입니다."), @@ -19,11 +19,4 @@ enum class TripErrorCode( // 404 TRIP_NOT_FOUND(HttpStatus.NOT_FOUND, "요창한 여행을 찾을 수 없습니다."), - ; - - override fun getName(): String = name - - override fun getStatus(): HttpStatus = status - - override fun getMessage(): String = message } diff --git a/src/main/kotlin/com/ject/studytrip/trip/domain/error/TripReportErrorCode.kt b/src/main/kotlin/com/ject/studytrip/trip/domain/error/TripReportErrorCode.kt index 10e3431..3349d2b 100644 --- a/src/main/kotlin/com/ject/studytrip/trip/domain/error/TripReportErrorCode.kt +++ b/src/main/kotlin/com/ject/studytrip/trip/domain/error/TripReportErrorCode.kt @@ -4,8 +4,8 @@ import com.ject.studytrip.global.exception.error.ErrorCode import org.springframework.http.HttpStatus enum class TripReportErrorCode( - private val status: HttpStatus, - private val message: String, + override val status: HttpStatus, + override val message: String, ) : ErrorCode { // 400 TRIP_REPORT_ALREADY_DELETED(HttpStatus.BAD_REQUEST, "이미 삭제된 여행 리포트입니다."), @@ -15,11 +15,4 @@ enum class TripReportErrorCode( // 404 TRIP_REPORT_NOT_FOUND(HttpStatus.NOT_FOUND, "요창한 여행 리포트를 찾을 수 없습니다."), - ; - - override fun getName(): String = name - - override fun getStatus(): HttpStatus = status - - override fun getMessage(): String = message } diff --git a/src/main/kotlin/com/ject/studytrip/trip/domain/factory/DailyGoalFactory.kt b/src/main/kotlin/com/ject/studytrip/trip/domain/factory/DailyGoalFactory.kt index 8c4a162..52e2162 100644 --- a/src/main/kotlin/com/ject/studytrip/trip/domain/factory/DailyGoalFactory.kt +++ b/src/main/kotlin/com/ject/studytrip/trip/domain/factory/DailyGoalFactory.kt @@ -4,7 +4,6 @@ import com.ject.studytrip.trip.domain.model.DailyGoal import com.ject.studytrip.trip.domain.model.Trip object DailyGoalFactory { - @JvmStatic fun create( trip: Trip, title: String, diff --git a/src/main/kotlin/com/ject/studytrip/trip/domain/factory/TripFactory.kt b/src/main/kotlin/com/ject/studytrip/trip/domain/factory/TripFactory.kt index 84a7a81..13475ac 100644 --- a/src/main/kotlin/com/ject/studytrip/trip/domain/factory/TripFactory.kt +++ b/src/main/kotlin/com/ject/studytrip/trip/domain/factory/TripFactory.kt @@ -6,7 +6,6 @@ import com.ject.studytrip.trip.domain.model.TripCategory import java.time.LocalDate object TripFactory { - @JvmStatic fun create( member: Member, name: String, diff --git a/src/main/kotlin/com/ject/studytrip/trip/domain/factory/TripReportFactory.kt b/src/main/kotlin/com/ject/studytrip/trip/domain/factory/TripReportFactory.kt index f8f6458..10b8ae1 100644 --- a/src/main/kotlin/com/ject/studytrip/trip/domain/factory/TripReportFactory.kt +++ b/src/main/kotlin/com/ject/studytrip/trip/domain/factory/TripReportFactory.kt @@ -4,7 +4,6 @@ import com.ject.studytrip.member.domain.model.Member import com.ject.studytrip.trip.domain.model.TripReport object TripReportFactory { - @JvmStatic fun create( member: Member, title: String, diff --git a/src/main/kotlin/com/ject/studytrip/trip/domain/factory/TripReportStudyLogFactory.kt b/src/main/kotlin/com/ject/studytrip/trip/domain/factory/TripReportStudyLogFactory.kt index 58042b9..ac05e58 100644 --- a/src/main/kotlin/com/ject/studytrip/trip/domain/factory/TripReportStudyLogFactory.kt +++ b/src/main/kotlin/com/ject/studytrip/trip/domain/factory/TripReportStudyLogFactory.kt @@ -5,7 +5,6 @@ import com.ject.studytrip.trip.domain.model.TripReport import com.ject.studytrip.trip.domain.model.TripReportStudyLog object TripReportStudyLogFactory { - @JvmStatic fun create( tripReport: TripReport, studyLog: StudyLog, diff --git a/src/main/kotlin/com/ject/studytrip/trip/domain/model/DailyGoal.kt b/src/main/kotlin/com/ject/studytrip/trip/domain/model/DailyGoal.kt new file mode 100644 index 0000000..22e65eb --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/trip/domain/model/DailyGoal.kt @@ -0,0 +1,44 @@ +package com.ject.studytrip.trip.domain.model + +import com.ject.studytrip.global.common.entity.BaseTimeEntity +import jakarta.persistence.Column +import jakarta.persistence.Entity +import jakarta.persistence.FetchType +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Id +import jakarta.persistence.JoinColumn +import jakarta.persistence.ManyToOne +import java.time.LocalDateTime + +@Entity +class DailyGoal protected constructor( + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "trip_id", nullable = false) + var trip: Trip, + @Column(nullable = false) + var title: String, + var completed: Boolean = false, +) : BaseTimeEntity() { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + var id: Long? = null + protected set + + companion object { + fun of( + trip: Trip, + title: String, + ): DailyGoal = DailyGoal(trip, title, false) + } + + fun updateDeletedAt(now: LocalDateTime = LocalDateTime.now()) { + markDeleted(now) + } + + fun updateCompleted() { + this.completed = true + } + + fun isCompleted(): Boolean = completed +} diff --git a/src/main/kotlin/com/ject/studytrip/trip/domain/model/Trip.kt b/src/main/kotlin/com/ject/studytrip/trip/domain/model/Trip.kt new file mode 100644 index 0000000..8d3f6aa --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/trip/domain/model/Trip.kt @@ -0,0 +1,86 @@ +package com.ject.studytrip.trip.domain.model + +import com.ject.studytrip.global.common.entity.BaseTimeEntity +import com.ject.studytrip.member.domain.model.Member +import jakarta.persistence.Column +import jakarta.persistence.Entity +import jakarta.persistence.EnumType +import jakarta.persistence.Enumerated +import jakarta.persistence.FetchType +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Id +import jakarta.persistence.JoinColumn +import jakarta.persistence.ManyToOne +import org.springframework.util.StringUtils.hasText +import java.time.LocalDate +import java.time.LocalDateTime + +@Entity +class Trip protected constructor( + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id", nullable = false) + var member: Member, + @Column(nullable = false) + var name: String, + var memo: String?, + @Enumerated(EnumType.STRING) + @Column(nullable = false) + var category: TripCategory, + @Column(nullable = false) + var startDate: LocalDate, + var endDate: LocalDate?, + var totalStamps: Int = 0, + var completedStamps: Int = 0, + var completed: Boolean = false, +) : BaseTimeEntity() { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + var id: Long? = null + protected set + + companion object { + fun of( + member: Member, + name: String, + memo: String?, + category: TripCategory, + endDate: LocalDate?, + totalStamps: Int, + ): Trip = Trip(member, name, memo, category, LocalDate.now(), endDate, totalStamps, 0, false) + } + + fun update( + name: String?, + memo: String?, + category: TripCategory?, + endDate: LocalDate?, + ) { + name?.takeIf { hasText(it) }?.let { this.name = it } + memo?.takeIf { hasText(it) }?.let { this.memo = it } + category?.let { this.category = it } + endDate?.let { this.endDate = it } + } + + fun updateCompleted() { + this.completed = true + } + + fun updateDeletedAt(now: LocalDateTime = LocalDateTime.now()) { + markDeleted(now) + } + + fun isCompleted(): Boolean = completed + + fun increaseTotalStamps() { + this.totalStamps += 1 + } + + fun decreaseTotalStamps() { + this.totalStamps -= 1 + } + + fun increaseCompletedStamps() { + this.completedStamps += 1 + } +} diff --git a/src/main/kotlin/com/ject/studytrip/trip/domain/model/TripCategory.kt b/src/main/kotlin/com/ject/studytrip/trip/domain/model/TripCategory.kt new file mode 100644 index 0000000..819836b --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/trip/domain/model/TripCategory.kt @@ -0,0 +1,18 @@ +package com.ject.studytrip.trip.domain.model + +import com.ject.studytrip.global.exception.CustomException +import com.ject.studytrip.trip.domain.error.TripErrorCode + +enum class TripCategory( + val value: String, +) { + COURSE("코스형"), + EXPLORE("탐험형"), + ; + + companion object { + fun from(name: String): TripCategory = + entries.firstOrNull { it.name.equals(name, ignoreCase = true) } + ?: throw CustomException(TripErrorCode.INVALID_TRIP_CATEGORY) + } +} diff --git a/src/main/kotlin/com/ject/studytrip/trip/domain/model/TripReport.kt b/src/main/kotlin/com/ject/studytrip/trip/domain/model/TripReport.kt new file mode 100644 index 0000000..c66ee3e --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/trip/domain/model/TripReport.kt @@ -0,0 +1,64 @@ +package com.ject.studytrip.trip.domain.model + +import com.ject.studytrip.global.common.entity.BaseTimeEntity +import com.ject.studytrip.member.domain.model.Member +import jakarta.persistence.Column +import jakarta.persistence.Entity +import jakarta.persistence.FetchType +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Id +import jakarta.persistence.JoinColumn +import jakarta.persistence.ManyToOne +import org.springframework.util.StringUtils.hasText +import java.time.LocalDateTime + +@Entity +class TripReport protected constructor( + var title: String?, + @Column(nullable = false) + var content: String, + @Column(nullable = false) + var startDate: String, + var endDate: String?, + @Column(nullable = false) + var studyLogCount: Long, + @Column(nullable = false) + var totalFocusHours: Long, + @Column(nullable = false) + var studyDays: Long, + var imageTitle: String?, + var imageUrl: String? = null, + @ManyToOne(fetch = FetchType.LAZY, optional = false) + @JoinColumn(name = "member_id", nullable = false) + var member: Member, +) : BaseTimeEntity() { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + var id: Long? = null + protected set + + companion object { + fun of( + member: Member, + title: String, + content: String, + startDate: String, + endDate: String?, + studyLogCount: Long, + totalFocusHours: Long, + studyDays: Long, + imageTitle: String?, + ): TripReport = TripReport(title, content, startDate, endDate, studyLogCount, totalFocusHours, studyDays, imageTitle, null, member) + } + + fun updateImageUrl(imageUrl: String) { + if (hasText(imageUrl)) { + this.imageUrl = imageUrl + } + } + + fun updateDeletedAt(now: LocalDateTime = LocalDateTime.now()) { + markDeleted(now) + } +} diff --git a/src/main/kotlin/com/ject/studytrip/trip/domain/model/TripReportStudyLog.kt b/src/main/kotlin/com/ject/studytrip/trip/domain/model/TripReportStudyLog.kt new file mode 100644 index 0000000..07f505c --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/trip/domain/model/TripReportStudyLog.kt @@ -0,0 +1,33 @@ +package com.ject.studytrip.trip.domain.model + +import com.ject.studytrip.global.common.entity.BaseTimeEntity +import com.ject.studytrip.studylog.domain.model.StudyLog +import jakarta.persistence.Entity +import jakarta.persistence.FetchType +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Id +import jakarta.persistence.JoinColumn +import jakarta.persistence.ManyToOne + +@Entity +class TripReportStudyLog protected constructor( + @ManyToOne(fetch = FetchType.LAZY, optional = false) + @JoinColumn(name = "trip_report_id") + var tripReport: TripReport, + @ManyToOne(fetch = FetchType.LAZY, optional = false) + @JoinColumn(name = "study_log_id") + var studyLog: StudyLog, +) : BaseTimeEntity() { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + var id: Long? = null + protected set + + companion object { + fun of( + tripReport: TripReport, + studyLog: StudyLog, + ): TripReportStudyLog = TripReportStudyLog(tripReport, studyLog) + } +} diff --git a/src/main/kotlin/com/ject/studytrip/trip/domain/policy/DailyGoalPolicy.kt b/src/main/kotlin/com/ject/studytrip/trip/domain/policy/DailyGoalPolicy.kt index 612f3ec..5e84eba 100644 --- a/src/main/kotlin/com/ject/studytrip/trip/domain/policy/DailyGoalPolicy.kt +++ b/src/main/kotlin/com/ject/studytrip/trip/domain/policy/DailyGoalPolicy.kt @@ -6,7 +6,7 @@ import com.ject.studytrip.trip.domain.model.DailyGoal object DailyGoalPolicy { fun validateNotDeleted(dailyGoal: DailyGoal) { - if (dailyGoal.isDeleted) { + if (dailyGoal.isDeleted()) { throw CustomException(DailyGoalErrorCode.DAILY_GOAL_ALREADY_DELETED) } } diff --git a/src/main/kotlin/com/ject/studytrip/trip/domain/policy/TripPolicy.kt b/src/main/kotlin/com/ject/studytrip/trip/domain/policy/TripPolicy.kt index e8d691d..25793eb 100644 --- a/src/main/kotlin/com/ject/studytrip/trip/domain/policy/TripPolicy.kt +++ b/src/main/kotlin/com/ject/studytrip/trip/domain/policy/TripPolicy.kt @@ -8,19 +8,19 @@ import java.time.LocalDate object TripPolicy { fun validateNotDeleted(trip: Trip) { - if (trip.isDeleted) { + if (trip.isDeleted()) { throw CustomException(TripErrorCode.TRIP_ALREADY_DELETED) } } fun validateNotCompleted(trip: Trip) { - if (trip.isCompleted) { + if (trip.isCompleted()) { throw CustomException(TripErrorCode.TRIP_ALREADY_COMPLETED) } } fun validateCompleted(trip: Trip) { - if (!trip.isCompleted) { + if (!trip.isCompleted()) { throw CustomException(TripErrorCode.TRIP_NOT_COMPLETED) } } diff --git a/src/main/kotlin/com/ject/studytrip/trip/domain/policy/TripReportPolicy.kt b/src/main/kotlin/com/ject/studytrip/trip/domain/policy/TripReportPolicy.kt index 893317f..0daf361 100644 --- a/src/main/kotlin/com/ject/studytrip/trip/domain/policy/TripReportPolicy.kt +++ b/src/main/kotlin/com/ject/studytrip/trip/domain/policy/TripReportPolicy.kt @@ -6,7 +6,7 @@ import com.ject.studytrip.trip.domain.model.TripReport object TripReportPolicy { fun validateNotDeleted(tripReport: TripReport) { - if (tripReport.isDeleted) { + if (tripReport.isDeleted()) { throw CustomException(TripReportErrorCode.TRIP_REPORT_ALREADY_DELETED) } } diff --git a/src/main/kotlin/com/ject/studytrip/trip/domain/repository/DailyGoalCommandRepository.kt b/src/main/kotlin/com/ject/studytrip/trip/domain/repository/DailyGoalCommandRepository.kt new file mode 100644 index 0000000..e33dadc --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/trip/domain/repository/DailyGoalCommandRepository.kt @@ -0,0 +1,9 @@ +package com.ject.studytrip.trip.domain.repository + +interface DailyGoalCommandRepository { + fun deleteAllByDeletedAtIsNotNull(): Long + + fun deleteAllByDeletedTripOwner(): Long + + fun deleteAllByMemberId(memberId: Long): Long +} diff --git a/src/main/kotlin/com/ject/studytrip/trip/domain/repository/TripCommandRepository.kt b/src/main/kotlin/com/ject/studytrip/trip/domain/repository/TripCommandRepository.kt new file mode 100644 index 0000000..a70eef8 --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/trip/domain/repository/TripCommandRepository.kt @@ -0,0 +1,9 @@ +package com.ject.studytrip.trip.domain.repository + +interface TripCommandRepository { + fun deleteAllByDeletedAtIsNotNull(): Long + + fun deleteAllByDeletedMemberOwner(): Long + + fun deleteAllByMemberId(memberId: Long): Long +} diff --git a/src/main/kotlin/com/ject/studytrip/trip/domain/repository/TripQueryRepository.kt b/src/main/kotlin/com/ject/studytrip/trip/domain/repository/TripQueryRepository.kt new file mode 100644 index 0000000..001d9be --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/trip/domain/repository/TripQueryRepository.kt @@ -0,0 +1,18 @@ +package com.ject.studytrip.trip.domain.repository + +import com.ject.studytrip.trip.domain.model.Trip +import com.ject.studytrip.trip.domain.model.TripCategory +import org.springframework.data.domain.Pageable +import org.springframework.data.domain.Slice + +interface TripQueryRepository { + fun findSliceByMemberIdAndCompletedFalseAndDeletedAtIsNull( + memberId: Long, + pageable: Pageable, + ): Slice + + fun countActiveTripsByMemberIdAndCategory( + memberId: Long, + category: TripCategory, + ): Long +} diff --git a/src/main/kotlin/com/ject/studytrip/trip/domain/repository/TripReportCommandRepository.kt b/src/main/kotlin/com/ject/studytrip/trip/domain/repository/TripReportCommandRepository.kt new file mode 100644 index 0000000..37bb779 --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/trip/domain/repository/TripReportCommandRepository.kt @@ -0,0 +1,9 @@ +package com.ject.studytrip.trip.domain.repository + +interface TripReportCommandRepository { + fun deleteAllByDeletedAtIsNotNull(): Long + + fun deleteAllByDeletedMemberOwner(): Long + + fun deleteAllByMemberId(memberId: Long): Long +} diff --git a/src/main/kotlin/com/ject/studytrip/trip/domain/repository/TripReportQueryRepository.kt b/src/main/kotlin/com/ject/studytrip/trip/domain/repository/TripReportQueryRepository.kt new file mode 100644 index 0000000..80a37a6 --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/trip/domain/repository/TripReportQueryRepository.kt @@ -0,0 +1,9 @@ +package com.ject.studytrip.trip.domain.repository + +import com.ject.studytrip.trip.domain.model.TripReport + +interface TripReportQueryRepository { + fun findAllActiveByMemberId(memberId: Long): List + + fun findImageUrlsByMemberId(memberId: Long): List +} diff --git a/src/main/kotlin/com/ject/studytrip/trip/domain/repository/TripReportStudyLogCommandRepository.kt b/src/main/kotlin/com/ject/studytrip/trip/domain/repository/TripReportStudyLogCommandRepository.kt new file mode 100644 index 0000000..f4c3ccd --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/trip/domain/repository/TripReportStudyLogCommandRepository.kt @@ -0,0 +1,7 @@ +package com.ject.studytrip.trip.domain.repository + +interface TripReportStudyLogCommandRepository { + fun deleteAllByDeletedMemberOwner(): Long + + fun deleteAllByMemberId(memberId: Long): Long +} diff --git a/src/main/kotlin/com/ject/studytrip/trip/infra/querydsl/DailyGoalCommandRepositoryAdapter.kt b/src/main/kotlin/com/ject/studytrip/trip/infra/querydsl/DailyGoalCommandRepositoryAdapter.kt new file mode 100644 index 0000000..2a3c9f7 --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/trip/infra/querydsl/DailyGoalCommandRepositoryAdapter.kt @@ -0,0 +1,48 @@ +package com.ject.studytrip.trip.infra.querydsl + +import com.ject.studytrip.trip.domain.model.QDailyGoal.dailyGoal +import com.ject.studytrip.trip.domain.model.QTrip.trip +import com.ject.studytrip.trip.domain.repository.DailyGoalCommandRepository +import com.querydsl.jpa.JPAExpressions +import com.querydsl.jpa.impl.JPAQueryFactory +import org.springframework.stereotype.Repository + +@Repository +class DailyGoalCommandRepositoryAdapter( + private val queryFactory: JPAQueryFactory, +) : DailyGoalCommandRepository { + override fun deleteAllByDeletedAtIsNotNull(): Long = + queryFactory + .delete(dailyGoal) + .where(dailyGoal.deletedAt.isNotNull) + .execute() + + override fun deleteAllByDeletedTripOwner(): Long = + queryFactory + .delete(dailyGoal) + .where( + dailyGoal.trip.id.`in`( + JPAExpressions + .select(trip.id) + .from(trip) + .where(trip.deletedAt.isNotNull), + ), + ).execute() + + override fun deleteAllByMemberId(memberId: Long): Long { + val ids = + queryFactory + .select(dailyGoal.id) + .from(dailyGoal) + .join(dailyGoal.trip, trip) + .where(trip.member.id.eq(memberId)) + .fetch() + + if (ids.isEmpty()) return 0L + + return queryFactory + .delete(dailyGoal) + .where(dailyGoal.id.`in`(ids)) + .execute() + } +} diff --git a/src/main/kotlin/com/ject/studytrip/trip/infra/querydsl/TripCommandRepositoryAdapter.kt b/src/main/kotlin/com/ject/studytrip/trip/infra/querydsl/TripCommandRepositoryAdapter.kt new file mode 100644 index 0000000..57a62e3 --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/trip/infra/querydsl/TripCommandRepositoryAdapter.kt @@ -0,0 +1,37 @@ +package com.ject.studytrip.trip.infra.querydsl + +import com.ject.studytrip.member.domain.model.QMember.member +import com.ject.studytrip.trip.domain.model.QTrip.trip +import com.ject.studytrip.trip.domain.repository.TripCommandRepository +import com.querydsl.jpa.JPAExpressions +import com.querydsl.jpa.impl.JPAQueryFactory +import org.springframework.stereotype.Repository + +@Repository +class TripCommandRepositoryAdapter( + private val queryFactory: JPAQueryFactory, +) : TripCommandRepository { + override fun deleteAllByDeletedAtIsNotNull(): Long = + queryFactory + .delete(trip) + .where(trip.deletedAt.isNotNull) + .execute() + + override fun deleteAllByDeletedMemberOwner(): Long = + queryFactory + .delete(trip) + .where( + trip.member.id.`in`( + JPAExpressions + .select(member.id) + .from(member) + .where(member.deletedAt.isNotNull), + ), + ).execute() + + override fun deleteAllByMemberId(memberId: Long): Long = + queryFactory + .delete(trip) + .where(trip.member.id.eq(memberId)) + .execute() +} diff --git a/src/main/kotlin/com/ject/studytrip/trip/infra/querydsl/TripQueryRepositoryAdapter.kt b/src/main/kotlin/com/ject/studytrip/trip/infra/querydsl/TripQueryRepositoryAdapter.kt new file mode 100644 index 0000000..32075df --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/trip/infra/querydsl/TripQueryRepositoryAdapter.kt @@ -0,0 +1,51 @@ +package com.ject.studytrip.trip.infra.querydsl + +import com.ject.studytrip.trip.domain.model.QTrip.trip +import com.ject.studytrip.trip.domain.model.Trip +import com.ject.studytrip.trip.domain.model.TripCategory +import com.ject.studytrip.trip.domain.repository.TripQueryRepository +import com.querydsl.jpa.impl.JPAQueryFactory +import org.springframework.data.domain.Pageable +import org.springframework.data.domain.Slice +import org.springframework.data.domain.SliceImpl +import org.springframework.stereotype.Repository + +@Repository +class TripQueryRepositoryAdapter( + private val queryFactory: JPAQueryFactory, +) : TripQueryRepository { + override fun findSliceByMemberIdAndCompletedFalseAndDeletedAtIsNull( + memberId: Long, + pageable: Pageable, + ): Slice { + val content = + queryFactory + .selectFrom(trip) + .where( + trip.member.id.eq(memberId), + trip.completed.isFalse, + trip.deletedAt.isNull, + ).offset(pageable.offset) + .limit(pageable.pageSize.toLong() + 1) + .fetch() + + val hasNext = content.size > pageable.pageSize + val result = content.take(pageable.pageSize) + + return SliceImpl(result, pageable, hasNext) + } + + override fun countActiveTripsByMemberIdAndCategory( + memberId: Long, + category: TripCategory, + ): Long = + queryFactory + .select(trip.count()) + .from(trip) + .where( + trip.member.id.eq(memberId), + trip.deletedAt.isNull, + trip.category.eq(category), + ).fetchOne() + ?: 0L +} diff --git a/src/main/kotlin/com/ject/studytrip/trip/infra/querydsl/TripReportCommandRepositoryAdapter.kt b/src/main/kotlin/com/ject/studytrip/trip/infra/querydsl/TripReportCommandRepositoryAdapter.kt new file mode 100644 index 0000000..2ea2082 --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/trip/infra/querydsl/TripReportCommandRepositoryAdapter.kt @@ -0,0 +1,47 @@ +package com.ject.studytrip.trip.infra.querydsl + +import com.ject.studytrip.member.domain.model.QMember.member +import com.ject.studytrip.trip.domain.model.QTripReport.tripReport +import com.ject.studytrip.trip.domain.repository.TripReportCommandRepository +import com.querydsl.jpa.JPAExpressions +import com.querydsl.jpa.impl.JPAQueryFactory +import org.springframework.stereotype.Repository + +@Repository +class TripReportCommandRepositoryAdapter( + private val queryFactory: JPAQueryFactory, +) : TripReportCommandRepository { + override fun deleteAllByDeletedAtIsNotNull(): Long = + queryFactory + .delete(tripReport) + .where(tripReport.deletedAt.isNotNull) + .execute() + + override fun deleteAllByDeletedMemberOwner(): Long = + queryFactory + .delete(tripReport) + .where( + tripReport.member.id.`in`( + JPAExpressions + .select(member.id) + .from(member) + .where(member.deletedAt.isNotNull), + ), + ).execute() + + override fun deleteAllByMemberId(memberId: Long): Long { + val ids = + queryFactory + .select(tripReport.id) + .from(tripReport) + .where(tripReport.member.id.eq(memberId)) + .fetch() + + if (ids.isEmpty()) return 0L + + return queryFactory + .delete(tripReport) + .where(tripReport.id.`in`(ids)) + .execute() + } +} diff --git a/src/main/kotlin/com/ject/studytrip/trip/infra/querydsl/TripReportQueryRepositoryAdapter.kt b/src/main/kotlin/com/ject/studytrip/trip/infra/querydsl/TripReportQueryRepositoryAdapter.kt new file mode 100644 index 0000000..c5798ac --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/trip/infra/querydsl/TripReportQueryRepositoryAdapter.kt @@ -0,0 +1,28 @@ +package com.ject.studytrip.trip.infra.querydsl + +import com.ject.studytrip.trip.domain.model.QTripReport.tripReport +import com.ject.studytrip.trip.domain.model.TripReport +import com.ject.studytrip.trip.domain.repository.TripReportQueryRepository +import com.querydsl.jpa.impl.JPAQueryFactory +import org.springframework.stereotype.Repository + +@Repository +class TripReportQueryRepositoryAdapter( + private val queryFactory: JPAQueryFactory, +) : TripReportQueryRepository { + override fun findAllActiveByMemberId(memberId: Long): List = + queryFactory + .selectFrom(tripReport) + .where( + tripReport.member.id.eq(memberId), + tripReport.deletedAt.isNull, + ).orderBy(tripReport.createdAt.desc()) + .fetch() + + override fun findImageUrlsByMemberId(memberId: Long): List = + queryFactory + .select(tripReport.imageUrl) + .from(tripReport) + .where(tripReport.member.id.eq(memberId)) + .fetch() +} diff --git a/src/main/kotlin/com/ject/studytrip/trip/infra/querydsl/TripReportStudyLogCommandRepositoryAdapter.kt b/src/main/kotlin/com/ject/studytrip/trip/infra/querydsl/TripReportStudyLogCommandRepositoryAdapter.kt new file mode 100644 index 0000000..c640ccd --- /dev/null +++ b/src/main/kotlin/com/ject/studytrip/trip/infra/querydsl/TripReportStudyLogCommandRepositoryAdapter.kt @@ -0,0 +1,54 @@ +package com.ject.studytrip.trip.infra.querydsl + +import com.ject.studytrip.member.domain.model.QMember.member +import com.ject.studytrip.studylog.domain.model.QStudyLog.studyLog +import com.ject.studytrip.trip.domain.model.QTripReport.tripReport +import com.ject.studytrip.trip.domain.model.QTripReportStudyLog.tripReportStudyLog +import com.ject.studytrip.trip.domain.repository.TripReportStudyLogCommandRepository +import com.querydsl.jpa.JPAExpressions +import com.querydsl.jpa.impl.JPAQueryFactory +import org.springframework.stereotype.Repository + +@Repository +class TripReportStudyLogCommandRepositoryAdapter( + private val queryFactory: JPAQueryFactory, +) : TripReportStudyLogCommandRepository { + override fun deleteAllByDeletedMemberOwner(): Long = + queryFactory + .delete(tripReportStudyLog) + .where( + tripReportStudyLog.tripReport.id + .`in`( + JPAExpressions + .select(tripReport.id) + .from(tripReport) + .join(tripReport.member, member) + .where(member.deletedAt.isNotNull), + ).or( + tripReportStudyLog.studyLog.id.`in`( + JPAExpressions + .select(studyLog.id) + .from(studyLog) + .join(studyLog.member, member) + .where(member.deletedAt.isNotNull), + ), + ), + ).execute() + + override fun deleteAllByMemberId(memberId: Long): Long { + val ids = + queryFactory + .select(tripReportStudyLog.id) + .from(tripReportStudyLog) + .join(tripReportStudyLog.tripReport, tripReport) + .where(tripReport.member.id.eq(memberId)) + .fetch() + + if (ids.isEmpty()) return 0L + + return queryFactory + .delete(tripReportStudyLog) + .where(tripReportStudyLog.id.`in`(ids)) + .execute() + } +} diff --git a/src/main/kotlin/com/ject/studytrip/trip/presentation/controller/TripController.kt b/src/main/kotlin/com/ject/studytrip/trip/presentation/controller/TripController.kt index a26c09f..81daab5 100644 --- a/src/main/kotlin/com/ject/studytrip/trip/presentation/controller/TripController.kt +++ b/src/main/kotlin/com/ject/studytrip/trip/presentation/controller/TripController.kt @@ -110,7 +110,7 @@ class TripController( return ResponseEntity .status(HttpStatus.OK) - .body(StandardResponse.success(HttpStatus.OK.value(), LoadTripsSliceResponse.of(result.tripInfos, result.hasNext))) + .body(StandardResponse.success(HttpStatus.OK.value(), LoadTripsSliceResponse(result.tripInfos, result.hasNext))) } @Operation(summary = "여행 상세 조회", description = "특정 여행을 상세 조회합니다.") diff --git a/src/main/kotlin/com/ject/studytrip/trip/presentation/controller/TripReportController.kt b/src/main/kotlin/com/ject/studytrip/trip/presentation/controller/TripReportController.kt index 3c7638e..5fc3f83 100644 --- a/src/main/kotlin/com/ject/studytrip/trip/presentation/controller/TripReportController.kt +++ b/src/main/kotlin/com/ject/studytrip/trip/presentation/controller/TripReportController.kt @@ -147,7 +147,7 @@ class TripReportController( .body( StandardResponse.success( HttpStatus.OK.value(), - PresignedTripReportImageResponse.of(result.tripReportId, result.tmpKey, result.presignedUrl), + PresignedTripReportImageResponse(result.tripReportId, result.tmpKey, result.presignedUrl), ), ) } diff --git a/src/main/kotlin/com/ject/studytrip/trip/presentation/dto/response/CreateDailyGoalResponse.kt b/src/main/kotlin/com/ject/studytrip/trip/presentation/dto/response/CreateDailyGoalResponse.kt index 13f3a27..796d360 100644 --- a/src/main/kotlin/com/ject/studytrip/trip/presentation/dto/response/CreateDailyGoalResponse.kt +++ b/src/main/kotlin/com/ject/studytrip/trip/presentation/dto/response/CreateDailyGoalResponse.kt @@ -8,7 +8,6 @@ data class CreateDailyGoalResponse( val dailyGoalId: Long, ) { companion object { - @JvmStatic fun of(dailyGoalInfo: DailyGoalInfo): CreateDailyGoalResponse = CreateDailyGoalResponse(dailyGoalInfo.dailyGoalId) } } diff --git a/src/main/kotlin/com/ject/studytrip/trip/presentation/dto/response/CreateTripReportResponse.kt b/src/main/kotlin/com/ject/studytrip/trip/presentation/dto/response/CreateTripReportResponse.kt index 81d2978..06a93c2 100644 --- a/src/main/kotlin/com/ject/studytrip/trip/presentation/dto/response/CreateTripReportResponse.kt +++ b/src/main/kotlin/com/ject/studytrip/trip/presentation/dto/response/CreateTripReportResponse.kt @@ -8,7 +8,6 @@ data class CreateTripReportResponse( val tripReportId: Long, ) { companion object { - @JvmStatic fun of(tripReportInfo: TripReportInfo): CreateTripReportResponse = CreateTripReportResponse(tripReportInfo.tripReportId) } } diff --git a/src/main/kotlin/com/ject/studytrip/trip/presentation/dto/response/CreateTripResponse.kt b/src/main/kotlin/com/ject/studytrip/trip/presentation/dto/response/CreateTripResponse.kt index 854b383..2ced606 100644 --- a/src/main/kotlin/com/ject/studytrip/trip/presentation/dto/response/CreateTripResponse.kt +++ b/src/main/kotlin/com/ject/studytrip/trip/presentation/dto/response/CreateTripResponse.kt @@ -8,7 +8,6 @@ data class CreateTripResponse( val tripId: Long, ) { companion object { - @JvmStatic fun of(tripInfo: TripInfo): CreateTripResponse = CreateTripResponse(tripInfo.tripId) } } diff --git a/src/main/kotlin/com/ject/studytrip/trip/presentation/dto/response/LoadDailyGoalDetailResponse.kt b/src/main/kotlin/com/ject/studytrip/trip/presentation/dto/response/LoadDailyGoalDetailResponse.kt index 33f6271..dd26812 100644 --- a/src/main/kotlin/com/ject/studytrip/trip/presentation/dto/response/LoadDailyGoalDetailResponse.kt +++ b/src/main/kotlin/com/ject/studytrip/trip/presentation/dto/response/LoadDailyGoalDetailResponse.kt @@ -18,7 +18,6 @@ data class LoadDailyGoalDetailResponse( val dailyMissions: List, ) { companion object { - @JvmStatic fun of( dailyGoalInfo: DailyGoalInfo, pomodoroInfo: PomodoroInfo, diff --git a/src/main/kotlin/com/ject/studytrip/trip/presentation/dto/response/LoadTripCategoryResponse.kt b/src/main/kotlin/com/ject/studytrip/trip/presentation/dto/response/LoadTripCategoryResponse.kt index becdb4d..ff04c1a 100644 --- a/src/main/kotlin/com/ject/studytrip/trip/presentation/dto/response/LoadTripCategoryResponse.kt +++ b/src/main/kotlin/com/ject/studytrip/trip/presentation/dto/response/LoadTripCategoryResponse.kt @@ -10,7 +10,6 @@ data class LoadTripCategoryResponse( val value: String, ) { companion object { - @JvmStatic fun of(tripCategoryInfo: TripCategoryInfo): LoadTripCategoryResponse = LoadTripCategoryResponse(tripCategoryInfo.name, tripCategoryInfo.value) } diff --git a/src/main/kotlin/com/ject/studytrip/trip/presentation/dto/response/LoadTripDetailResponse.kt b/src/main/kotlin/com/ject/studytrip/trip/presentation/dto/response/LoadTripDetailResponse.kt index 03efad3..b1900d9 100644 --- a/src/main/kotlin/com/ject/studytrip/trip/presentation/dto/response/LoadTripDetailResponse.kt +++ b/src/main/kotlin/com/ject/studytrip/trip/presentation/dto/response/LoadTripDetailResponse.kt @@ -33,7 +33,6 @@ data class LoadTripDetailResponse( val stamps: List, ) { companion object { - @JvmStatic fun of( tripInfo: TripInfo, stampInfos: List, diff --git a/src/main/kotlin/com/ject/studytrip/trip/presentation/dto/response/LoadTripReportDetailResponse.kt b/src/main/kotlin/com/ject/studytrip/trip/presentation/dto/response/LoadTripReportDetailResponse.kt index 1aefaac..cffdc87 100644 --- a/src/main/kotlin/com/ject/studytrip/trip/presentation/dto/response/LoadTripReportDetailResponse.kt +++ b/src/main/kotlin/com/ject/studytrip/trip/presentation/dto/response/LoadTripReportDetailResponse.kt @@ -15,7 +15,7 @@ data class LoadTripReportDetailResponse( @field:Schema(description = "여행 시작일 (여행 회고)") val startDate: String, @field:Schema(description = "여행 종료일 (여행 회고)") - val endDate: String, + val endDate: String?, @field:Schema(description = "총 학습 시간") val totalFocusHours: Long, @field:Schema(description = "학습 로그 개수 (세션 성공)") @@ -30,7 +30,6 @@ data class LoadTripReportDetailResponse( val history: LoadStudyLogsSliceResponse, ) { companion object { - @JvmStatic fun of( tripReportInfo: TripReportInfo, studyLogSliceInfo: StudyLogSliceInfo, diff --git a/src/main/kotlin/com/ject/studytrip/trip/presentation/dto/response/LoadTripReportsResponse.kt b/src/main/kotlin/com/ject/studytrip/trip/presentation/dto/response/LoadTripReportsResponse.kt index a861d9c..d1c4e98 100644 --- a/src/main/kotlin/com/ject/studytrip/trip/presentation/dto/response/LoadTripReportsResponse.kt +++ b/src/main/kotlin/com/ject/studytrip/trip/presentation/dto/response/LoadTripReportsResponse.kt @@ -8,7 +8,6 @@ data class LoadTripReportsResponse( val tripReports: List, ) { companion object { - @JvmStatic fun of(tripReportInfos: List): LoadTripReportsResponse = LoadTripReportsResponse( TripReportSummary.of(tripReportInfos), diff --git a/src/main/kotlin/com/ject/studytrip/trip/presentation/dto/response/LoadTripRetrospectDetailResponse.kt b/src/main/kotlin/com/ject/studytrip/trip/presentation/dto/response/LoadTripRetrospectDetailResponse.kt index fde013d..0b69d9e 100644 --- a/src/main/kotlin/com/ject/studytrip/trip/presentation/dto/response/LoadTripRetrospectDetailResponse.kt +++ b/src/main/kotlin/com/ject/studytrip/trip/presentation/dto/response/LoadTripRetrospectDetailResponse.kt @@ -25,7 +25,6 @@ data class LoadTripRetrospectDetailResponse( val history: LoadStudyLogsSliceResponse, ) { companion object { - @JvmStatic fun of( tripRetrospectSummary: TripRetrospectSummary, tripInfo: TripInfo, diff --git a/src/main/kotlin/com/ject/studytrip/trip/presentation/dto/response/LoadTripsSliceResponse.kt b/src/main/kotlin/com/ject/studytrip/trip/presentation/dto/response/LoadTripsSliceResponse.kt index 1e27ea9..d4bca53 100644 --- a/src/main/kotlin/com/ject/studytrip/trip/presentation/dto/response/LoadTripsSliceResponse.kt +++ b/src/main/kotlin/com/ject/studytrip/trip/presentation/dto/response/LoadTripsSliceResponse.kt @@ -8,12 +8,4 @@ data class LoadTripsSliceResponse( val tripInfos: List, @field:Schema(description = "다음 데이터 존재 여부") val hasNext: Boolean, -) { - companion object { - @JvmStatic - fun of( - tripInfos: List, - hasNext: Boolean, - ): LoadTripsSliceResponse = LoadTripsSliceResponse(tripInfos, hasNext) - } -} +) diff --git a/src/main/kotlin/com/ject/studytrip/trip/presentation/dto/response/PresignedTripReportImageResponse.kt b/src/main/kotlin/com/ject/studytrip/trip/presentation/dto/response/PresignedTripReportImageResponse.kt index 5623400..c164c7f 100644 --- a/src/main/kotlin/com/ject/studytrip/trip/presentation/dto/response/PresignedTripReportImageResponse.kt +++ b/src/main/kotlin/com/ject/studytrip/trip/presentation/dto/response/PresignedTripReportImageResponse.kt @@ -9,13 +9,4 @@ data class PresignedTripReportImageResponse( val tmpKey: String, @field:Schema(description = "여행 리포트 이미지 업로드용 Presigned URL") val presignedUrl: String, -) { - companion object { - @JvmStatic - fun of( - tripReportId: Long, - tmpKey: String, - presignedUrl: String, - ): PresignedTripReportImageResponse = PresignedTripReportImageResponse(tripReportId, tmpKey, presignedUrl) - } -} +) diff --git a/src/test/java/com/ject/studytrip/StudytripApplicationTests.java b/src/test/java/com/ject/studytrip/StudytripApplicationTests.java deleted file mode 100644 index c943a9f..0000000 --- a/src/test/java/com/ject/studytrip/StudytripApplicationTests.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.ject.studytrip; - -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.ActiveProfiles; -import org.springframework.test.context.TestPropertySource; - -@ActiveProfiles("test") -@TestPropertySource(locations = "file:.env") -@AutoConfigureTestDatabase( - replace = AutoConfigureTestDatabase.Replace.NONE) // 테스트 시 내장된 인메모리 DB를 사용하지 않는다는 설정 -@SpringBootTest -class StudytripApplicationTests { - - @Test - void contextLoads() { - System.out.println("🌐 DB_HOST = " + System.getenv("DB_HOST")); - } -} diff --git a/src/test/kotlin/com/ject/studytrip/StudytripApplicationTests.kt b/src/test/kotlin/com/ject/studytrip/StudytripApplicationTests.kt new file mode 100644 index 0000000..e8d6dc7 --- /dev/null +++ b/src/test/kotlin/com/ject/studytrip/StudytripApplicationTests.kt @@ -0,0 +1,18 @@ +package com.ject.studytrip + +import org.junit.jupiter.api.Test +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.test.context.ActiveProfiles +import org.springframework.test.context.TestPropertySource + +@ActiveProfiles("test") +@TestPropertySource(locations = ["file:.env"]) +@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) // 테스트 시 내장된 인메모리 DB를 사용하지 않는다는 설정 +@SpringBootTest +class StudytripApplicationTests { + @Test + fun contextLoads() { + println("🌐 DB_HOST = ${System.getenv("DB_HOST")}") + } +} diff --git a/src/test/kotlin/com/ject/studytrip/auth/presentation/controller/AuthControllerIntegrationTest.kt b/src/test/kotlin/com/ject/studytrip/auth/presentation/controller/AuthControllerIntegrationTest.kt index 4ea11fe..f74c492 100644 --- a/src/test/kotlin/com/ject/studytrip/auth/presentation/controller/AuthControllerIntegrationTest.kt +++ b/src/test/kotlin/com/ject/studytrip/auth/presentation/controller/AuthControllerIntegrationTest.kt @@ -18,6 +18,7 @@ import com.ject.studytrip.auth.presentation.dto.request.KakaoSignupRequest import com.ject.studytrip.auth.presentation.dto.request.LogoutRequest import com.ject.studytrip.global.common.constants.CookieConstants.AUTH_REFRESH_TOKEN import com.ject.studytrip.global.common.constants.CookieConstants.OAUTH_SIGNUP_KEY +import com.ject.studytrip.global.util.EntityExtensions.requireId import com.ject.studytrip.member.domain.error.MemberErrorCode import com.ject.studytrip.member.domain.model.Member import com.ject.studytrip.member.helper.MemberTestHelper @@ -66,7 +67,8 @@ class AuthControllerIntegrationTest : BaseIntegrationTest() { member = memberTestHelper.saveMember() accessToken = tokenTestHelper.createAccessToken(member.id.toString(), member.role.name) refreshToken = tokenTestHelper.createRefreshToken() - signupKey = kakaoSignupProfileRedisRepository.saveAndIssueSignupKey(member.socialId, member.email, member.profileImage) + signupKey = + kakaoSignupProfileRedisRepository.saveAndIssueSignupKey(member.socialId, member.email, requireNotNull(member.profileImage)) refreshTokenRedisRepository.saveRefreshToken(member.id.toString(), refreshToken, Duration.ofSeconds(30).toMillis()) } @@ -97,7 +99,7 @@ class AuthControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("회원가입 요청 시 signupKey 쿠키가 존재하지 않으면 400 Bad Request를 반환한다.") fun shouldReturnBadRequestWhenSignupKeyCookieDoesNotExist() { // given - memberTestHelper.deleteMemberById(member.id) + memberTestHelper.deleteMemberById(member.id.requireId()) val request = fixture.build() val cookie = authCookieTestHelper.createKakaoSignupProfileCookie("NULL_COOKIE", signupKey) @@ -116,7 +118,7 @@ class AuthControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("회원가입 요청 시 signupKey가 유효하지 않으면 400 Bad Request를 반환한다.") fun shouldReturnBadRequestWhenSignupKeyIsInvalid() { // given - memberTestHelper.deleteMemberById(member.id) + memberTestHelper.deleteMemberById(member.id.requireId()) val request = fixture.build() val cookie = authCookieTestHelper.createKakaoSignupProfileCookie(OAUTH_SIGNUP_KEY, "invalid.pending.key") @@ -152,7 +154,7 @@ class AuthControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("회원가입 성공 시 토큰이 발급된다.") fun shouldReturnTokenResponseWhenSignupIsSuccessful() { // given - memberTestHelper.deleteMemberById(member.id) + memberTestHelper.deleteMemberById(member.id.requireId()) val request = fixture.build() val cookie = authCookieTestHelper.createKakaoSignupProfileCookie(OAUTH_SIGNUP_KEY, signupKey) @@ -213,7 +215,7 @@ class AuthControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("가입되지 않은 사용자 인가 코드로 로그인 시 회원가입 필요 응답을 반환한다.") fun shouldReturnSignupRequiredWhenMemberDoesNotExist() { // given - memberTestHelper.deleteMemberById(member.id) + memberTestHelper.deleteMemberById(member.id.requireId()) val request = kakaoLoginRequestFixture.build() val kakaoTokenResponse = kakaoTokenResponseFixture.build() val kakaoUserInfoResponse = kakaoUserInfoResponseFixture.build() diff --git a/src/test/kotlin/com/ject/studytrip/image/application/service/ImageServiceTest.kt b/src/test/kotlin/com/ject/studytrip/image/application/service/ImageServiceTest.kt index 2cf104d..c0282a7 100644 --- a/src/test/kotlin/com/ject/studytrip/image/application/service/ImageServiceTest.kt +++ b/src/test/kotlin/com/ject/studytrip/image/application/service/ImageServiceTest.kt @@ -397,7 +397,7 @@ class ImageServiceTest : BaseUnitTest() { // given val imageUrls = listOf(VALID_IMAGE_URL1, "https://wrong-cdn.com/members/1/image1.jpg") val keys = listOf(VALID_KEY1) - given(cdnProperties.domain()).willReturn(IMAGE_BASE_URL) + given(cdnProperties.domain).willReturn(IMAGE_BASE_URL) given(s3Provider.deleteByKeys(keys)).willReturn(CleanupImagesResult(1, emptyList())) // when diff --git a/src/test/kotlin/com/ject/studytrip/member/application/service/MemberCommandServiceTest.kt b/src/test/kotlin/com/ject/studytrip/member/application/service/MemberCommandServiceTest.kt index 7f2ec09..a0c82ac 100644 --- a/src/test/kotlin/com/ject/studytrip/member/application/service/MemberCommandServiceTest.kt +++ b/src/test/kotlin/com/ject/studytrip/member/application/service/MemberCommandServiceTest.kt @@ -2,6 +2,7 @@ package com.ject.studytrip.member.application.service import com.ject.studytrip.BaseUnitTest import com.ject.studytrip.global.exception.CustomException +import com.ject.studytrip.global.util.EntityExtensions.requireId import com.ject.studytrip.member.domain.error.MemberErrorCode import com.ject.studytrip.member.domain.model.Member import com.ject.studytrip.member.domain.model.SocialProvider @@ -66,20 +67,6 @@ class MemberCommandServiceTest : BaseUnitTest() { assertThat(exception.message).isEqualTo(MemberErrorCode.MEMBER_ALREADY_EXISTS.message) } - @Test - @DisplayName("카테고리가 유효하지 않으면 예외가 발생한다.") - fun shouldThrowExceptionWhenCategoryIsInvalid() { - // given - val command = fixture.withCategory("INVALID").build() - given(memberRepository.existsBySocialProviderAndSocialId(SocialProvider.KAKAO, member.socialId)).willReturn(false) - - // when - val exception = assertThrows { memberCommandService.createMemberFromKakao(command) } - - // then - assertThat(exception.message).isEqualTo(MemberErrorCode.INVALID_MEMBER_CATEGORY.message) - } - @Test @DisplayName("CreateMemberCommand가 유효하면 멤버를 생성하고 반환한다.") fun shouldCreateAndReturnMemberWhenCommandIsValid() { @@ -118,19 +105,6 @@ class MemberCommandServiceTest : BaseUnitTest() { inner class UpdateMember { private val fixture = UpdateMemberRequestFixture() - @Test - @DisplayName("카테고리가 유효하지 않으면 예외가 발생한다.") - fun shouldThrowExceptionWhenCategoryIsInvalid() { - // given - val request = fixture.withCategory("INVALID").build() - - // when - val exception = assertThrows { memberCommandService.updateMember(member, request) } - - // then - assertThat(exception.message).isEqualTo(MemberErrorCode.INVALID_MEMBER_CATEGORY.message) - } - @Test @DisplayName("특정 멤버의 닉네임을 수정한다.") fun shouldUpdateMemberWhenNicknameIsPresent() { @@ -273,7 +247,7 @@ class MemberCommandServiceTest : BaseUnitTest() { @DisplayName("특정 멤버를 완전 삭제합니다.") fun shouldHardDeleteMember() { // given - val memberId = member.id + val memberId = member.id.requireId() // when memberCommandService.hardDeleteMember(memberId) diff --git a/src/test/kotlin/com/ject/studytrip/member/application/service/MemberQueryServiceTest.kt b/src/test/kotlin/com/ject/studytrip/member/application/service/MemberQueryServiceTest.kt index 6d80eed..5184724 100644 --- a/src/test/kotlin/com/ject/studytrip/member/application/service/MemberQueryServiceTest.kt +++ b/src/test/kotlin/com/ject/studytrip/member/application/service/MemberQueryServiceTest.kt @@ -2,6 +2,7 @@ package com.ject.studytrip.member.application.service import com.ject.studytrip.BaseUnitTest import com.ject.studytrip.global.exception.CustomException +import com.ject.studytrip.global.util.EntityExtensions.requireId import com.ject.studytrip.member.domain.error.MemberErrorCode import com.ject.studytrip.member.domain.model.Member import com.ject.studytrip.member.domain.model.SocialProvider @@ -92,7 +93,7 @@ class MemberQueryServiceTest : BaseUnitTest() { @DisplayName("멤버가 이미 삭제되었다면 예외가 발생한다.") fun shouldThrowExceptionWhenMemberAlreadyDeleted() { // given - val memberId = member.id + val memberId = member.id.requireId() member.updateDeletedAt() given(memberRepository.findById(memberId)).willReturn(Optional.of(member)) @@ -107,7 +108,7 @@ class MemberQueryServiceTest : BaseUnitTest() { @DisplayName("멤버가 존재하면 멤버를 반환한다.") fun shouldReturnTripWhenMemberExists() { // given - val memberId = member.id + val memberId = member.id.requireId() given(memberRepository.findById(memberId)).willReturn(Optional.of(member)) // when @@ -139,7 +140,7 @@ class MemberQueryServiceTest : BaseUnitTest() { @DisplayName("멤버가 삭제되지 않았다면 예외가 발생한다.") fun shouldThrowExceptionWhenMemberIsNotDeleted() { // given - val memberId = member.id + val memberId = member.id.requireId() given(memberRepository.findById(memberId)).willReturn(Optional.of(member)) // when @@ -153,7 +154,7 @@ class MemberQueryServiceTest : BaseUnitTest() { @DisplayName("멤버가 이미 삭제되었다면 멤버를 반환한다.") fun shouldReturnMemberWhenMemberAlreadyDeleted() { // given - val memberId = member.id + val memberId = member.id.requireId() member.updateDeletedAt() given(memberRepository.findById(memberId)).willReturn(Optional.of(member)) @@ -186,9 +187,9 @@ class MemberQueryServiceTest : BaseUnitTest() { @DisplayName("멤버 ID에 대한 멤버가 존재하면 MemberRole을 반환한다.") fun shouldReturnMemberRoleWhenMemberIdExists() { // given - val memberId = member.id + val memberId = member.id.requireId() val memberRole = member.role - given(memberQueryRepository.findMemberRoleById(memberId.toLong())).willReturn(Optional.of(memberRole)) + given(memberQueryRepository.findMemberRoleById(memberId)).willReturn(Optional.of(memberRole)) // when val result = memberQueryService.getMemberRoleByMemberId(memberId) diff --git a/src/test/kotlin/com/ject/studytrip/member/fixture/CreateMemberCommandFixture.kt b/src/test/kotlin/com/ject/studytrip/member/fixture/CreateMemberCommandFixture.kt index f18760d..1ca0bcc 100644 --- a/src/test/kotlin/com/ject/studytrip/member/fixture/CreateMemberCommandFixture.kt +++ b/src/test/kotlin/com/ject/studytrip/member/fixture/CreateMemberCommandFixture.kt @@ -15,8 +15,5 @@ class CreateMemberCommandFixture( fun withNickname(nickname: String): CreateMemberCommandFixture = CreateMemberCommandFixture(socialId, email, profileImage, nickname, category) - fun withCategory(category: String): CreateMemberCommandFixture = - CreateMemberCommandFixture(socialId, email, profileImage, nickname, category) - fun build(): CreateMemberCommand = CreateMemberCommand(socialId, email, profileImage, nickname, category) } diff --git a/src/test/kotlin/com/ject/studytrip/member/presentation/controller/MemberControllerIntegrationTest.kt b/src/test/kotlin/com/ject/studytrip/member/presentation/controller/MemberControllerIntegrationTest.kt index 8642d54..1da831d 100644 --- a/src/test/kotlin/com/ject/studytrip/member/presentation/controller/MemberControllerIntegrationTest.kt +++ b/src/test/kotlin/com/ject/studytrip/member/presentation/controller/MemberControllerIntegrationTest.kt @@ -5,6 +5,7 @@ import com.ject.studytrip.auth.domain.error.AuthErrorCode import com.ject.studytrip.auth.fixture.TokenFixture import com.ject.studytrip.auth.helper.TokenTestHelper import com.ject.studytrip.global.exception.error.CommonErrorCode +import com.ject.studytrip.global.util.EntityExtensions.requireId import com.ject.studytrip.image.domain.error.ImageErrorCode import com.ject.studytrip.image.infra.s3.provider.S3ImageStorageProvider import com.ject.studytrip.member.domain.error.MemberErrorCode @@ -271,7 +272,7 @@ class MemberControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("멤버가 삭제되지 않았다면 400 Bad Request를 반환한다.") fun shouldReturnBadRequestWhenMemberIsNotDeleted() { // given - val memberId = member.id + val memberId = member.id.requireId() // when val resultActions = getResultActions(memberId) @@ -288,7 +289,7 @@ class MemberControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("삭제된 멤버를 복구한다.") fun shouldRestoreMember() { // given - val memberId = deletedMember.id + val memberId = deletedMember.id.requireId() // when val resultActions = getResultActions(memberId) diff --git a/src/test/kotlin/com/ject/studytrip/mission/application/service/DailyMissionCommandServiceTest.kt b/src/test/kotlin/com/ject/studytrip/mission/application/service/DailyMissionCommandServiceTest.kt index d35f6f6..0e35b01 100644 --- a/src/test/kotlin/com/ject/studytrip/mission/application/service/DailyMissionCommandServiceTest.kt +++ b/src/test/kotlin/com/ject/studytrip/mission/application/service/DailyMissionCommandServiceTest.kt @@ -1,6 +1,7 @@ package com.ject.studytrip.mission.application.service import com.ject.studytrip.BaseUnitTest +import com.ject.studytrip.global.util.EntityExtensions.requireId import com.ject.studytrip.member.domain.model.Member import com.ject.studytrip.member.fixture.MemberFixture import com.ject.studytrip.mission.domain.model.DailyMission @@ -180,7 +181,7 @@ class DailyMissionCommandServiceTest : BaseUnitTest() { @DisplayName("특정 멤버가 소유한 데일리 미션이 하나라도 존재하지 않으면 0을 반환한다.") fun shouldReturnZeroWhenDailyMissionsOwnedByMemberDoNotExist() { // given - val memberId = member.id + val memberId = member.id.requireId() given(dailyMissionCommandRepository.deleteAllByMemberId(memberId)).willReturn(0L) // when @@ -194,7 +195,7 @@ class DailyMissionCommandServiceTest : BaseUnitTest() { @DisplayName("특정 멤버가 소유한 데일리 미션이 하나라도 존재하면 해당 개수를 반환한다.") fun shouldReturnCountWhenDailyMissionsOwnedByMemberExist() { // given - val memberId = member.id + val memberId = member.id.requireId() given(dailyMissionCommandRepository.deleteAllByMemberId(memberId)).willReturn(5L) // when diff --git a/src/test/kotlin/com/ject/studytrip/mission/application/service/DailyMissionQueryServiceTest.kt b/src/test/kotlin/com/ject/studytrip/mission/application/service/DailyMissionQueryServiceTest.kt index 5f57186..42f4326 100644 --- a/src/test/kotlin/com/ject/studytrip/mission/application/service/DailyMissionQueryServiceTest.kt +++ b/src/test/kotlin/com/ject/studytrip/mission/application/service/DailyMissionQueryServiceTest.kt @@ -2,6 +2,7 @@ package com.ject.studytrip.mission.application.service import com.ject.studytrip.BaseUnitTest import com.ject.studytrip.global.exception.CustomException +import com.ject.studytrip.global.util.EntityExtensions.requireId import com.ject.studytrip.member.fixture.MemberFixture import com.ject.studytrip.mission.domain.error.DailyMissionErrorCode import com.ject.studytrip.mission.domain.model.DailyMission @@ -57,12 +58,17 @@ class DailyMissionQueryServiceTest : BaseUnitTest() { @DisplayName("요청한 데일리 미션 ID 개수와 조회된 데일리 미션 개수가 일치하지 않으면 예외가 발생한다.") fun shouldThrowExceptionWhenSomeDailyMissionsDoNotExist() { // given - val dailyMissionIds = listOf(dailyMission.id, 1000L) + val dailyMissionIds = listOf(dailyMission.id.requireId(), 1000L) given(dailyMissionRepository.findAllByIdIn(dailyMissionIds)).willReturn(listOf(dailyMission)) // when val exception = - assertThrows { dailyMissionQueryService.getValidDailyMissionsByIds(dailyGoal.id, dailyMissionIds) } + assertThrows { + dailyMissionQueryService.getValidDailyMissionsByIds( + dailyGoal.id.requireId(), + dailyMissionIds, + ) + } // then assertThat(exception.message).isEqualTo(DailyMissionErrorCode.DAILY_MISSION_NOT_FOUND.message) @@ -73,12 +79,17 @@ class DailyMissionQueryServiceTest : BaseUnitTest() { fun shouldThrowExceptionWhenDailyMissionsNotBelongToDailyGoal() { // given val newDailyGoal = DailyGoalFixture(trip).createWithId(2L) - val dailyMissionIds = listOf(dailyMission.id) + val dailyMissionIds = listOf(dailyMission.id.requireId()) given(dailyMissionRepository.findAllByIdIn(dailyMissionIds)).willReturn(listOf(dailyMission)) // when val exception = - assertThrows { dailyMissionQueryService.getValidDailyMissionsByIds(newDailyGoal.id, dailyMissionIds) } + assertThrows { + dailyMissionQueryService.getValidDailyMissionsByIds( + newDailyGoal.id.requireId(), + dailyMissionIds, + ) + } // then assertThat(exception.message).isEqualTo(DailyMissionErrorCode.DAILY_MISSION_NOT_BELONGS_TO_DAILY_GOAL.message) @@ -88,13 +99,18 @@ class DailyMissionQueryServiceTest : BaseUnitTest() { @DisplayName("데일리 미션이 이미 삭제되었다면 예외가 발생한다.") fun shouldThrowExceptionWhenDailyMissionAlreadyDeleted() { // given - val dailyMissionIds = listOf(dailyMission.id) + val dailyMissionIds = listOf(dailyMission.id.requireId()) dailyMission.updateDeletedAt() given(dailyMissionRepository.findAllByIdIn(dailyMissionIds)).willReturn(listOf(dailyMission)) // when val exception = - assertThrows { dailyMissionQueryService.getValidDailyMissionsByIds(dailyGoal.id, dailyMissionIds) } + assertThrows { + dailyMissionQueryService.getValidDailyMissionsByIds( + dailyGoal.id.requireId(), + dailyMissionIds, + ) + } // then assertThat(exception.message).isEqualTo(DailyMissionErrorCode.DAILY_MISSION_ALREADY_DELETED.message) @@ -104,11 +120,11 @@ class DailyMissionQueryServiceTest : BaseUnitTest() { @DisplayName("데일리 미션 ID 목록과 일치하는 데일리 미션 목록을 조회하고 반환한다.") fun shouldReturnDailyMissionsByIds() { // given - val dailyMissionIds = listOf(dailyMission.id) + val dailyMissionIds = listOf(dailyMission.id.requireId()) given(dailyMissionRepository.findAllByIdIn(dailyMissionIds)).willReturn(listOf(dailyMission)) // when - val result = dailyMissionQueryService.getValidDailyMissionsByIds(dailyGoal.id, dailyMissionIds) + val result = dailyMissionQueryService.getValidDailyMissionsByIds(dailyGoal.id.requireId(), dailyMissionIds) // then assertThat(result).hasSize(1) @@ -123,14 +139,14 @@ class DailyMissionQueryServiceTest : BaseUnitTest() { @DisplayName("요청한 데일리 미션 ID 개수와 조회된 데일리 미션 개수가 일치하지 않으면 예외가 발생한다.") fun shouldThrowExceptionWhenSomeDailyMissionsDoNotExist() { // given - val dailyMissionIds = listOf(dailyMission.id, 1000L) + val dailyMissionIds = listOf(dailyMission.id.requireId(), 1000L) given(dailyMissionQueryRepository.findAllWithMissionAndStampByIds(dailyMissionIds)).willReturn(listOf(dailyMission)) // when val exception = assertThrows { dailyMissionQueryService.getValidDailyMissionsWithMissionAndStampByIds( - dailyGoal.id, + dailyGoal.id.requireId(), dailyMissionIds, ) } @@ -144,14 +160,14 @@ class DailyMissionQueryServiceTest : BaseUnitTest() { fun shouldThrowExceptionWhenDailyMissionsNotBelongToDailyGoal() { // given val newDailyGoal = DailyGoalFixture(trip).createWithId(2L) - val dailyMissionIds = listOf(dailyMission.id) + val dailyMissionIds = listOf(dailyMission.id.requireId()) given(dailyMissionQueryRepository.findAllWithMissionAndStampByIds(dailyMissionIds)).willReturn(listOf(dailyMission)) // when val exception = assertThrows { dailyMissionQueryService.getValidDailyMissionsWithMissionAndStampByIds( - newDailyGoal.id, + newDailyGoal.id.requireId(), dailyMissionIds, ) } @@ -164,7 +180,7 @@ class DailyMissionQueryServiceTest : BaseUnitTest() { @DisplayName("데일리 미션이 이미 삭제되었다면 예외가 발생한다.") fun shouldThrowExceptionWhenDailyMissionAlreadyDeleted() { // given - val dailyMissionIds = listOf(dailyMission.id) + val dailyMissionIds = listOf(dailyMission.id.requireId()) dailyMission.updateDeletedAt() given(dailyMissionQueryRepository.findAllWithMissionAndStampByIds(dailyMissionIds)).willReturn(listOf(dailyMission)) @@ -172,7 +188,7 @@ class DailyMissionQueryServiceTest : BaseUnitTest() { val exception = assertThrows { dailyMissionQueryService.getValidDailyMissionsWithMissionAndStampByIds( - dailyGoal.id, + dailyGoal.id.requireId(), dailyMissionIds, ) } @@ -185,11 +201,11 @@ class DailyMissionQueryServiceTest : BaseUnitTest() { @DisplayName("데일리 미션 ID 목록과 일치하는 데일리 미션 목록을 미션과 스탬프와 함께 조회하고 반환한다.") fun shouldReturnDailyMissionsWithMissionAndStampByIds() { // given - val dailyMissionIds = listOf(dailyMission.id) + val dailyMissionIds = listOf(dailyMission.id.requireId()) given(dailyMissionQueryRepository.findAllWithMissionAndStampByIds(dailyMissionIds)).willReturn(listOf(dailyMission)) // when - val result = dailyMissionQueryService.getValidDailyMissionsWithMissionAndStampByIds(dailyGoal.id, dailyMissionIds) + val result = dailyMissionQueryService.getValidDailyMissionsWithMissionAndStampByIds(dailyGoal.id.requireId(), dailyMissionIds) // then assertThat(result).hasSize(1) @@ -204,7 +220,7 @@ class DailyMissionQueryServiceTest : BaseUnitTest() { @DisplayName("데일리 목표 ID로 데일리 미션 목록을 조회하고 반환한다.") fun shouldReturnDailyMissionsByDailyGoalId() { // given - val dailyGoalId = dailyGoal.id + val dailyGoalId = dailyGoal.id.requireId() given(dailyMissionQueryRepository.findAllByDailyGoalIdFetchJoinMission(dailyGoalId)).willReturn(listOf(dailyMission)) // when diff --git a/src/test/kotlin/com/ject/studytrip/mission/application/service/MissionCommandServiceTest.kt b/src/test/kotlin/com/ject/studytrip/mission/application/service/MissionCommandServiceTest.kt index ff46510..8deba46 100644 --- a/src/test/kotlin/com/ject/studytrip/mission/application/service/MissionCommandServiceTest.kt +++ b/src/test/kotlin/com/ject/studytrip/mission/application/service/MissionCommandServiceTest.kt @@ -2,6 +2,7 @@ package com.ject.studytrip.mission.application.service import com.ject.studytrip.BaseUnitTest import com.ject.studytrip.global.exception.CustomException +import com.ject.studytrip.global.util.EntityExtensions.requireId import com.ject.studytrip.member.domain.model.Member import com.ject.studytrip.member.fixture.MemberFixture import com.ject.studytrip.mission.domain.error.MissionErrorCode @@ -168,7 +169,7 @@ class MissionCommandServiceTest : BaseUnitTest() { missionCommandService.completeMission(exploreMission1) // then - assertThat(exploreMission1.isCompleted).isTrue + assertThat(exploreMission1.isCompleted()).isTrue } } @@ -183,7 +184,7 @@ class MissionCommandServiceTest : BaseUnitTest() { // when val exception = - assertThrows { missionCommandService.validateMissionsBelongToStamp(exploreStamp.id, missions) } + assertThrows { missionCommandService.validateMissionsBelongToStamp(exploreStamp.id.requireId(), missions) } // then assertThat(exception.message).isEqualTo(MissionErrorCode.MISSION_NOT_BELONGS_TO_STAMP.message) @@ -196,7 +197,7 @@ class MissionCommandServiceTest : BaseUnitTest() { val missions = listOf(exploreMission1, exploreMission2) // when & then - assertDoesNotThrow { missionCommandService.validateMissionsBelongToStamp(exploreStamp.id, missions) } + assertDoesNotThrow { missionCommandService.validateMissionsBelongToStamp(exploreStamp.id.requireId(), missions) } } } @@ -207,7 +208,7 @@ class MissionCommandServiceTest : BaseUnitTest() { @DisplayName("특정 스탬프의 어떤 미션이 완료되지 않았다면 예외가 발생한다.") fun shouldThrowExceptionWhenAnyMissionIsNotCompleted() { // given - val stampId = courseStamp.id + val stampId = courseStamp.id.requireId() given(missionCommandRepository.existsByStampIdAndCompletedIsFalseAndDeletedAtIsNull(stampId)).willReturn(true) // when @@ -221,7 +222,7 @@ class MissionCommandServiceTest : BaseUnitTest() { @DisplayName("특정 스탬프의 모든 미션이 완료되었다면 예외가 발생하지 않는다.") fun shouldPassWhenAllMissionsAreCompleted() { // given - val stampId = courseStamp.id + val stampId = courseStamp.id.requireId() given(missionCommandRepository.existsByStampIdAndCompletedIsFalseAndDeletedAtIsNull(stampId)).willReturn(false) // when & then @@ -296,7 +297,7 @@ class MissionCommandServiceTest : BaseUnitTest() { @DisplayName("특정 멤버가 소유한 미션이 하나라도 존재하지 않으면 0을 반환한다.") fun shouldReturnZeroWhenMissionsOwnedByMemberDoNotExist() { // given - val memberId = member.id + val memberId = member.id.requireId() given(missionCommandRepository.deleteAllByMemberId(memberId)).willReturn(0L) // when @@ -310,7 +311,7 @@ class MissionCommandServiceTest : BaseUnitTest() { @DisplayName("특정 멤버가 소유한 미션이 하나라도 존재하면 해당 개수를 반환한다.") fun shouldReturnCountWhenMissionsOwnedByMemberExist() { // given - val memberId = member.id + val memberId = member.id.requireId() given(missionCommandRepository.deleteAllByMemberId(memberId)).willReturn(5L) // when diff --git a/src/test/kotlin/com/ject/studytrip/mission/application/service/MissionQueryServiceTest.kt b/src/test/kotlin/com/ject/studytrip/mission/application/service/MissionQueryServiceTest.kt index 917e626..f0010db 100644 --- a/src/test/kotlin/com/ject/studytrip/mission/application/service/MissionQueryServiceTest.kt +++ b/src/test/kotlin/com/ject/studytrip/mission/application/service/MissionQueryServiceTest.kt @@ -2,6 +2,7 @@ package com.ject.studytrip.mission.application.service import com.ject.studytrip.BaseUnitTest import com.ject.studytrip.global.exception.CustomException +import com.ject.studytrip.global.util.EntityExtensions.requireId import com.ject.studytrip.member.fixture.MemberFixture import com.ject.studytrip.mission.domain.error.MissionErrorCode import com.ject.studytrip.mission.domain.model.Mission @@ -63,7 +64,7 @@ class MissionQueryServiceTest : BaseUnitTest() { given(missionRepository.findById(missionId)).willReturn(Optional.empty()) // when - val exception = assertThrows { missionQueryService.getValidMission(courseStamp.id, missionId) } + val exception = assertThrows { missionQueryService.getValidMission(courseStamp.id.requireId(), missionId) } // then assertThat(exception.message).isEqualTo(MissionErrorCode.MISSION_NOT_FOUND.message) @@ -73,11 +74,11 @@ class MissionQueryServiceTest : BaseUnitTest() { @DisplayName("특정 미션이 다른 스템프에 속한다면 예외가 발생한다.") fun shouldThrowExceptionWhenMissionNotBelongToStamp() { // given - val missionId = exploreMission1.id + val missionId = exploreMission1.id.requireId() given(missionRepository.findById(missionId)).willReturn(Optional.of(exploreMission1)) // when - val exception = assertThrows { missionQueryService.getValidMission(courseStamp.id, missionId) } + val exception = assertThrows { missionQueryService.getValidMission(courseStamp.id.requireId(), missionId) } // then assertThat(exception.message).isEqualTo(MissionErrorCode.MISSION_NOT_BELONGS_TO_STAMP.message) @@ -87,12 +88,12 @@ class MissionQueryServiceTest : BaseUnitTest() { @DisplayName("미션이 이미 삭제되었다면 예외가 발생한다.") fun shouldThrowExceptionWhenMissionAlreadyDeleted() { // given - val missionId = exploreMission1.id + val missionId = exploreMission1.id.requireId() exploreMission1.updateDeletedAt() given(missionRepository.findById(missionId)).willReturn(Optional.of(exploreMission1)) // when - val exception = assertThrows { missionQueryService.getValidMission(exploreStamp.id, missionId) } + val exception = assertThrows { missionQueryService.getValidMission(exploreStamp.id.requireId(), missionId) } // then assertThat(exception.message).isEqualTo(MissionErrorCode.MISSION_ALREADY_DELETED.message) @@ -102,12 +103,12 @@ class MissionQueryServiceTest : BaseUnitTest() { @DisplayName("미션이 이미 완료되었다면 예외가 발생한다.") fun shouldThrowExceptionWhenMissionAlreadyCompleted() { // given - val missionId = exploreMission1.id + val missionId = exploreMission1.id.requireId() exploreMission1.updateCompleted() given(missionRepository.findById(missionId)).willReturn(Optional.of(exploreMission1)) // when - val exception = assertThrows { missionQueryService.getValidMission(exploreStamp.id, missionId) } + val exception = assertThrows { missionQueryService.getValidMission(exploreStamp.id.requireId(), missionId) } // then assertThat(exception.message).isEqualTo(MissionErrorCode.MISSION_ALREADY_COMPLETED.message) @@ -117,11 +118,11 @@ class MissionQueryServiceTest : BaseUnitTest() { @DisplayName("특정 스탬프에 속한 미션이 존재하면 미션을 조회하고 반환한다.") fun shouldReturnMissionWhenMissionBelongsToStamp() { // given - val missionId = exploreMission1.id + val missionId = exploreMission1.id.requireId() given(missionRepository.findById(missionId)).willReturn(Optional.of(exploreMission1)) // when - val result = missionQueryService.getValidMission(exploreStamp.id, missionId) + val result = missionQueryService.getValidMission(exploreStamp.id.requireId(), missionId) // then assertThat(result).isEqualTo(exploreMission1) @@ -135,7 +136,7 @@ class MissionQueryServiceTest : BaseUnitTest() { @DisplayName("특정 스탬프에 속한 미션 목록을 최신순으로 정렬하여 반환한다.") fun shouldReturnMissionsByStampIdSortedByLatest() { // given - val stampId = exploreStamp.id + val stampId = exploreStamp.id.requireId() given( missionRepository.findAllByStampIdAndDeletedAtIsNullOrderByCreatedAt(stampId), ).willReturn(listOf(exploreMission2, exploreMission1)) @@ -156,7 +157,7 @@ class MissionQueryServiceTest : BaseUnitTest() { @DisplayName("요청한 미션 ID 개수와 조회된 미션 개수가 일치하지 않으면 예외가 발생한다.") fun shouldThrowExceptionWhenSomeMissionsDoNotExist() { // given - val missionIds = listOf(exploreMission1.id, exploreMission2.id, 1000L) + val missionIds = listOf(exploreMission1.id.requireId(), exploreMission2.id.requireId(), 1000L) given(missionQueryRepository.findAllByIdsInFetchJoinStamp(missionIds)).willReturn(listOf(exploreMission1, exploreMission2)) // when @@ -170,7 +171,7 @@ class MissionQueryServiceTest : BaseUnitTest() { @DisplayName("미션이 이미 삭제되었다면 예외가 발생한다.") fun shouldThrowExceptionWhenMissionAlreadyDeleted() { // given - val missionIds = listOf(exploreMission1.id, exploreMission2.id) + val missionIds = listOf(exploreMission1.id.requireId(), exploreMission2.id.requireId()) exploreMission1.updateDeletedAt() given(missionQueryRepository.findAllByIdsInFetchJoinStamp(missionIds)).willReturn(listOf(exploreMission1, exploreMission2)) @@ -185,7 +186,7 @@ class MissionQueryServiceTest : BaseUnitTest() { @DisplayName("미션이 이미 완료되었다면 예외가 발생한다.") fun shouldThrowExceptionWhenMissionAlreadyCompleted() { // given - val missionIds = listOf(exploreMission1.id, exploreMission2.id) + val missionIds = listOf(exploreMission1.id.requireId(), exploreMission2.id.requireId()) exploreMission1.updateCompleted() given(missionQueryRepository.findAllByIdsInFetchJoinStamp(missionIds)).willReturn(listOf(exploreMission1, exploreMission2)) @@ -200,7 +201,7 @@ class MissionQueryServiceTest : BaseUnitTest() { @DisplayName("미션 ID 목록과 일치하는 미션 목록을 조회하고 반환한다.") fun shouldReturnMissionsByIds() { // given - val missionIds = listOf(exploreMission1.id, exploreMission2.id) + val missionIds = listOf(exploreMission1.id.requireId(), exploreMission2.id.requireId()) given(missionQueryRepository.findAllByIdsInFetchJoinStamp(missionIds)).willReturn(listOf(exploreMission1, exploreMission2)) // when diff --git a/src/test/kotlin/com/ject/studytrip/mission/fixture/CreateMissionRequestFixture.kt b/src/test/kotlin/com/ject/studytrip/mission/fixture/CreateMissionRequestFixture.kt index 1f777a5..9da5180 100644 --- a/src/test/kotlin/com/ject/studytrip/mission/fixture/CreateMissionRequestFixture.kt +++ b/src/test/kotlin/com/ject/studytrip/mission/fixture/CreateMissionRequestFixture.kt @@ -2,10 +2,10 @@ package com.ject.studytrip.mission.fixture import com.ject.studytrip.mission.presentation.dto.request.CreateMissionRequest -class CreateMissionRequestFixture { - var name: String = "TEST 미션 이름" - - fun withName(name: String): CreateMissionRequestFixture = apply { this.name = name } +class CreateMissionRequestFixture( + private val name: String = "TEST 미션 이름", +) { + fun withName(name: String): CreateMissionRequestFixture = CreateMissionRequestFixture(name) fun build(): CreateMissionRequest = CreateMissionRequest(name) } diff --git a/src/test/kotlin/com/ject/studytrip/mission/fixture/MissionFixture.kt b/src/test/kotlin/com/ject/studytrip/mission/fixture/MissionFixture.kt index 038aa6b..e0ff2ee 100644 --- a/src/test/kotlin/com/ject/studytrip/mission/fixture/MissionFixture.kt +++ b/src/test/kotlin/com/ject/studytrip/mission/fixture/MissionFixture.kt @@ -7,9 +7,8 @@ import org.springframework.test.util.ReflectionTestUtils class MissionFixture( private val stamp: Stamp, + private val name: String = "TEST 미션 이름", ) { - var name: String = "TEST 미션 이름" - fun create(): Mission = MissionFactory.create(stamp, name) fun createWithId(id: Long): Mission = diff --git a/src/test/kotlin/com/ject/studytrip/mission/fixture/UpdateMissionRequestFixture.kt b/src/test/kotlin/com/ject/studytrip/mission/fixture/UpdateMissionRequestFixture.kt index acefe42..4abd6bb 100644 --- a/src/test/kotlin/com/ject/studytrip/mission/fixture/UpdateMissionRequestFixture.kt +++ b/src/test/kotlin/com/ject/studytrip/mission/fixture/UpdateMissionRequestFixture.kt @@ -2,10 +2,10 @@ package com.ject.studytrip.mission.fixture import com.ject.studytrip.mission.presentation.dto.request.UpdateMissionRequest -class UpdateMissionRequestFixture { - var name: String = "TEST 새로운 미션 이름" - - fun withName(name: String): UpdateMissionRequestFixture = apply { this.name = name } +class UpdateMissionRequestFixture( + private val name: String = "TEST 새로운 미션 이름", +) { + fun withName(name: String): UpdateMissionRequestFixture = UpdateMissionRequestFixture(name) fun build(): UpdateMissionRequest = UpdateMissionRequest(name) } diff --git a/src/test/kotlin/com/ject/studytrip/mission/presentation/controller/MissionControllerIntegrationTest.kt b/src/test/kotlin/com/ject/studytrip/mission/presentation/controller/MissionControllerIntegrationTest.kt index da1abcd..295506d 100644 --- a/src/test/kotlin/com/ject/studytrip/mission/presentation/controller/MissionControllerIntegrationTest.kt +++ b/src/test/kotlin/com/ject/studytrip/mission/presentation/controller/MissionControllerIntegrationTest.kt @@ -5,6 +5,7 @@ import com.ject.studytrip.auth.domain.error.AuthErrorCode import com.ject.studytrip.auth.fixture.TokenFixture import com.ject.studytrip.auth.helper.TokenTestHelper import com.ject.studytrip.global.exception.error.CommonErrorCode +import com.ject.studytrip.global.util.EntityExtensions.requireId import com.ject.studytrip.member.domain.model.Member import com.ject.studytrip.member.domain.model.MemberRole import com.ject.studytrip.member.helper.MemberTestHelper @@ -112,7 +113,7 @@ class MissionControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions("", courseTrip.id, courseStamp.id, request) + val resultActions = getResultActions("", courseTrip.id.requireId(), courseStamp.id.requireId(), request) // then resultActions @@ -130,7 +131,7 @@ class MissionControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions(token, tripId, courseStamp.id, request) + val resultActions = getResultActions(token, tripId, courseStamp.id.requireId(), request) // then resultActions @@ -148,7 +149,7 @@ class MissionControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions(token, courseTrip.id, stampId, request) + val resultActions = getResultActions(token, courseTrip.id.requireId(), stampId, request) // then resultActions @@ -165,7 +166,7 @@ class MissionControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.withName(" ").build() // when - val resultActions = getResultActions(token, courseTrip.id, courseStamp.id, request) + val resultActions = getResultActions(token, courseTrip.id.requireId(), courseStamp.id.requireId(), request) // then resultActions @@ -183,7 +184,7 @@ class MissionControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions(token, tripId, courseStamp.id, request) + val resultActions = getResultActions(token, tripId, courseStamp.id.requireId(), request) // then resultActions @@ -200,7 +201,7 @@ class MissionControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions(token, newTrip.id, courseStamp.id, request) + val resultActions = getResultActions(token, newTrip.id.requireId(), courseStamp.id.requireId(), request) // then resultActions @@ -218,7 +219,7 @@ class MissionControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions(token, deletedTrip.id, courseStamp.id, request) + val resultActions = getResultActions(token, deletedTrip.id.requireId(), courseStamp.id.requireId(), request) // then resultActions @@ -236,7 +237,7 @@ class MissionControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions(token, completedTrip.id, courseStamp.id, request) + val resultActions = getResultActions(token, completedTrip.id.requireId(), courseStamp.id.requireId(), request) // then resultActions @@ -254,7 +255,7 @@ class MissionControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions(token, courseTrip.id, stampId, request) + val resultActions = getResultActions(token, courseTrip.id.requireId(), stampId, request) // then resultActions @@ -271,7 +272,7 @@ class MissionControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions(token, courseTrip.id, exploreStamp.id, request) + val resultActions = getResultActions(token, courseTrip.id.requireId(), exploreStamp.id.requireId(), request) // then resultActions @@ -289,7 +290,7 @@ class MissionControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions(token, courseTrip.id, deletedStamp.id, request) + val resultActions = getResultActions(token, courseTrip.id.requireId(), deletedStamp.id.requireId(), request) // then resultActions @@ -307,7 +308,7 @@ class MissionControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions(token, courseTrip.id, completedStamp.id, request) + val resultActions = getResultActions(token, courseTrip.id.requireId(), completedStamp.id.requireId(), request) // then resultActions @@ -324,7 +325,7 @@ class MissionControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions(token, courseTrip.id, courseStamp.id, request) + val resultActions = getResultActions(token, courseTrip.id.requireId(), courseStamp.id.requireId(), request) // then resultActions @@ -361,7 +362,8 @@ class MissionControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions("", courseTrip.id, courseStamp.id, courseMission.id, request) + val resultActions = + getResultActions("", courseTrip.id.requireId(), courseStamp.id.requireId(), courseMission.id.requireId(), request) // then resultActions @@ -379,7 +381,7 @@ class MissionControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions(token, tripId, courseStamp.id, courseMission.id, request) + val resultActions = getResultActions(token, tripId, courseStamp.id.requireId(), courseMission.id.requireId(), request) // then resultActions @@ -397,7 +399,7 @@ class MissionControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions(token, courseTrip.id, stampId, courseMission.id, request) + val resultActions = getResultActions(token, courseTrip.id.requireId(), stampId, courseMission.id.requireId(), request) // then resultActions @@ -415,7 +417,7 @@ class MissionControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions(token, courseTrip.id, courseStamp.id, missionId, request) + val resultActions = getResultActions(token, courseTrip.id.requireId(), courseStamp.id.requireId(), missionId, request) // then resultActions @@ -432,7 +434,8 @@ class MissionControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.withName(" ").build() // when - val resultActions = getResultActions(token, courseTrip.id, courseStamp.id, courseMission.id, request) + val resultActions = + getResultActions(token, courseTrip.id.requireId(), courseStamp.id.requireId(), courseMission.id.requireId(), request) // then resultActions @@ -450,7 +453,7 @@ class MissionControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions(token, tripId, courseStamp.id, courseMission.id, request) + val resultActions = getResultActions(token, tripId, courseStamp.id.requireId(), courseMission.id.requireId(), request) // then resultActions @@ -467,7 +470,8 @@ class MissionControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions(token, newTrip.id, courseStamp.id, courseMission.id, request) + val resultActions = + getResultActions(token, newTrip.id.requireId(), courseStamp.id.requireId(), courseMission.id.requireId(), request) // then resultActions @@ -485,7 +489,8 @@ class MissionControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions(token, deletedTrip.id, courseStamp.id, courseMission.id, request) + val resultActions = + getResultActions(token, deletedTrip.id.requireId(), courseStamp.id.requireId(), courseMission.id.requireId(), request) // then resultActions @@ -503,7 +508,8 @@ class MissionControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions(token, completedTrip.id, courseStamp.id, courseMission.id, request) + val resultActions = + getResultActions(token, completedTrip.id.requireId(), courseStamp.id.requireId(), courseMission.id.requireId(), request) // then resultActions @@ -521,7 +527,7 @@ class MissionControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions(token, courseTrip.id, stampId, courseMission.id, request) + val resultActions = getResultActions(token, courseTrip.id.requireId(), stampId, courseMission.id.requireId(), request) // then resultActions @@ -538,7 +544,8 @@ class MissionControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions(token, courseTrip.id, exploreStamp.id, courseMission.id, request) + val resultActions = + getResultActions(token, courseTrip.id.requireId(), exploreStamp.id.requireId(), courseMission.id.requireId(), request) // then resultActions @@ -556,7 +563,8 @@ class MissionControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions(token, courseTrip.id, deletedStamp.id, courseMission.id, request) + val resultActions = + getResultActions(token, courseTrip.id.requireId(), deletedStamp.id.requireId(), courseMission.id.requireId(), request) // then resultActions @@ -574,7 +582,8 @@ class MissionControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions(token, courseTrip.id, completedStamp.id, courseMission.id, request) + val resultActions = + getResultActions(token, courseTrip.id.requireId(), completedStamp.id.requireId(), courseMission.id.requireId(), request) // then resultActions @@ -592,7 +601,7 @@ class MissionControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions(token, courseTrip.id, courseStamp.id, missionId, request) + val resultActions = getResultActions(token, courseTrip.id.requireId(), courseStamp.id.requireId(), missionId, request) // then resultActions @@ -609,7 +618,8 @@ class MissionControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions(token, courseTrip.id, courseStamp.id, exploreMission.id, request) + val resultActions = + getResultActions(token, courseTrip.id.requireId(), courseStamp.id.requireId(), exploreMission.id.requireId(), request) // then resultActions @@ -627,7 +637,8 @@ class MissionControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions(token, courseTrip.id, courseStamp.id, deletedMission.id, request) + val resultActions = + getResultActions(token, courseTrip.id.requireId(), courseStamp.id.requireId(), deletedMission.id.requireId(), request) // then resultActions @@ -645,7 +656,8 @@ class MissionControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions(token, courseTrip.id, courseStamp.id, completedMission.id, request) + val resultActions = + getResultActions(token, courseTrip.id.requireId(), courseStamp.id.requireId(), completedMission.id.requireId(), request) // then resultActions @@ -662,7 +674,8 @@ class MissionControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions(token, courseTrip.id, courseStamp.id, courseMission.id, request) + val resultActions = + getResultActions(token, courseTrip.id.requireId(), courseStamp.id.requireId(), courseMission.id.requireId(), request) // then resultActions @@ -690,7 +703,7 @@ class MissionControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("인증되지 않은 사용자라면 401 Unauthorized를 반환한다.") fun shouldReturnUnauthorizedWhenUnauthenticated() { // when - val resultActions = getResultActions("", courseTrip.id, courseStamp.id, courseMission.id) + val resultActions = getResultActions("", courseTrip.id.requireId(), courseStamp.id.requireId(), courseMission.id.requireId()) // then resultActions @@ -706,7 +719,7 @@ class MissionControllerIntegrationTest : BaseIntegrationTest() { // given val tripId = "abc" // when - val resultActions = getResultActions(token, tripId, courseStamp.id, courseMission.id) + val resultActions = getResultActions(token, tripId, courseStamp.id.requireId(), courseMission.id.requireId()) // then resultActions @@ -723,7 +736,7 @@ class MissionControllerIntegrationTest : BaseIntegrationTest() { val stampId = "abc" // when - val resultActions = getResultActions(token, courseTrip.id, stampId, courseMission.id) + val resultActions = getResultActions(token, courseTrip.id.requireId(), stampId, courseMission.id.requireId()) // then resultActions @@ -740,7 +753,7 @@ class MissionControllerIntegrationTest : BaseIntegrationTest() { val missionId = "abc" // when - val resultActions = getResultActions(token, courseTrip.id, courseStamp.id, missionId) + val resultActions = getResultActions(token, courseTrip.id.requireId(), courseStamp.id.requireId(), missionId) // then resultActions @@ -757,7 +770,7 @@ class MissionControllerIntegrationTest : BaseIntegrationTest() { val tripId = -1L // when - val resultActions = getResultActions(token, tripId, courseStamp.id, courseMission.id) + val resultActions = getResultActions(token, tripId, courseStamp.id.requireId(), courseMission.id.requireId()) // then resultActions @@ -771,7 +784,7 @@ class MissionControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("여행의 소유자가 아니라면 403 Forbidden을 반환한다.") fun shouldReturnForbiddenWhenMemberIsNotTripOwner() { // when - val resultActions = getResultActions(token, newTrip.id, courseStamp.id, courseMission.id) + val resultActions = getResultActions(token, newTrip.id.requireId(), courseStamp.id.requireId(), courseMission.id.requireId()) // then resultActions @@ -788,7 +801,8 @@ class MissionControllerIntegrationTest : BaseIntegrationTest() { val deletedTrip = tripTestHelper.saveDeletedTrip(member, TripCategory.COURSE) // when - val resultActions = getResultActions(token, deletedTrip.id, courseStamp.id, courseMission.id) + val resultActions = + getResultActions(token, deletedTrip.id.requireId(), courseStamp.id.requireId(), courseMission.id.requireId()) // then resultActions @@ -805,7 +819,8 @@ class MissionControllerIntegrationTest : BaseIntegrationTest() { val completedTrip = tripTestHelper.saveCompletedTrip(member, TripCategory.COURSE) // when - val resultActions = getResultActions(token, completedTrip.id, courseStamp.id, courseMission.id) + val resultActions = + getResultActions(token, completedTrip.id.requireId(), courseStamp.id.requireId(), courseMission.id.requireId()) // then resultActions @@ -822,7 +837,7 @@ class MissionControllerIntegrationTest : BaseIntegrationTest() { val stampId = -1L // when - val resultActions = getResultActions(token, courseTrip.id, stampId, courseMission.id) + val resultActions = getResultActions(token, courseTrip.id.requireId(), stampId, courseMission.id.requireId()) // then resultActions @@ -836,7 +851,8 @@ class MissionControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("스탬프가 요청한 여행에 속하지 않으면 403 Forbidden을 반환한다.") fun shouldReturnForbiddenWhenStampNotBelongToTrip() { // when - val resultActions = getResultActions(token, courseTrip.id, exploreStamp.id, courseMission.id) + val resultActions = + getResultActions(token, courseTrip.id.requireId(), exploreStamp.id.requireId(), courseMission.id.requireId()) // then resultActions @@ -853,7 +869,8 @@ class MissionControllerIntegrationTest : BaseIntegrationTest() { val deletedStamp = stampTestHelper.saveDeletedStamp(courseTrip, 3) // when - val resultActions = getResultActions(token, courseTrip.id, deletedStamp.id, courseMission.id) + val resultActions = + getResultActions(token, courseTrip.id.requireId(), deletedStamp.id.requireId(), courseMission.id.requireId()) // then resultActions @@ -870,7 +887,8 @@ class MissionControllerIntegrationTest : BaseIntegrationTest() { val completedStamp = stampTestHelper.saveCompletedStamp(courseTrip, 3) // when - val resultActions = getResultActions(token, courseTrip.id, completedStamp.id, courseMission.id) + val resultActions = + getResultActions(token, courseTrip.id.requireId(), completedStamp.id.requireId(), courseMission.id.requireId()) // then resultActions @@ -887,7 +905,7 @@ class MissionControllerIntegrationTest : BaseIntegrationTest() { val missionId = -1L // when - val resultActions = getResultActions(token, courseTrip.id, courseStamp.id, missionId) + val resultActions = getResultActions(token, courseTrip.id.requireId(), courseStamp.id.requireId(), missionId) // then resultActions @@ -901,7 +919,8 @@ class MissionControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("미션이 요청한 스탬프에 속하지 않으면 403 Forbidden을 반환한다.") fun shouldReturnForbiddenWhenMissionNotBelongToStamp() { // when - val resultActions = getResultActions(token, courseTrip.id, courseStamp.id, exploreMission.id) + val resultActions = + getResultActions(token, courseTrip.id.requireId(), courseStamp.id.requireId(), exploreMission.id.requireId()) // then resultActions @@ -918,7 +937,8 @@ class MissionControllerIntegrationTest : BaseIntegrationTest() { val deletedMission = missionTestHelper.saveDeletedMission(courseStamp) // when - val resultActions = getResultActions(token, courseTrip.id, courseStamp.id, deletedMission.id) + val resultActions = + getResultActions(token, courseTrip.id.requireId(), courseStamp.id.requireId(), deletedMission.id.requireId()) // then resultActions @@ -935,7 +955,8 @@ class MissionControllerIntegrationTest : BaseIntegrationTest() { val completedMission = missionTestHelper.saveCompletedMission(courseStamp) // when - val resultActions = getResultActions(token, courseTrip.id, courseStamp.id, completedMission.id) + val resultActions = + getResultActions(token, courseTrip.id.requireId(), courseStamp.id.requireId(), completedMission.id.requireId()) // then resultActions @@ -949,7 +970,7 @@ class MissionControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("특정 미션을 삭제한다.") fun shouldDeleteMission() { // when - val resultActions = getResultActions(token, courseTrip.id, courseStamp.id, courseMission.id) + val resultActions = getResultActions(token, courseTrip.id.requireId(), courseStamp.id.requireId(), courseMission.id.requireId()) // then resultActions @@ -976,7 +997,7 @@ class MissionControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("인증되지 않은 사용자라면 401 Unauthorized를 반환한다.") fun shouldReturnUnauthorizedWhenUnauthenticated() { // when - val resultActions = getResultActions("", courseTrip.id, courseStamp.id) + val resultActions = getResultActions("", courseTrip.id.requireId(), courseStamp.id.requireId()) // then resultActions @@ -992,7 +1013,7 @@ class MissionControllerIntegrationTest : BaseIntegrationTest() { // given val tripId = "abc" // when - val resultActions = getResultActions(token, tripId, courseStamp.id) + val resultActions = getResultActions(token, tripId, courseStamp.id.requireId()) // then resultActions @@ -1009,7 +1030,7 @@ class MissionControllerIntegrationTest : BaseIntegrationTest() { val stampId = "abc" // when - val resultActions = getResultActions(token, courseTrip.id, stampId) + val resultActions = getResultActions(token, courseTrip.id.requireId(), stampId) // then resultActions @@ -1026,7 +1047,7 @@ class MissionControllerIntegrationTest : BaseIntegrationTest() { val tripId = -1L // when - val resultActions = getResultActions(token, tripId, courseStamp.id) + val resultActions = getResultActions(token, tripId, courseStamp.id.requireId()) // then resultActions @@ -1040,7 +1061,7 @@ class MissionControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("여행의 소유자가 아니라면 403 Forbidden을 반환한다.") fun shouldReturnForbiddenWhenMemberIsNotTripOwner() { // when - val resultActions = getResultActions(token, newTrip.id, courseStamp.id) + val resultActions = getResultActions(token, newTrip.id.requireId(), courseStamp.id.requireId()) // then resultActions @@ -1057,7 +1078,7 @@ class MissionControllerIntegrationTest : BaseIntegrationTest() { val deletedTrip = tripTestHelper.saveDeletedTrip(member, TripCategory.COURSE) // when - val resultActions = getResultActions(token, deletedTrip.id, courseStamp.id) + val resultActions = getResultActions(token, deletedTrip.id.requireId(), courseStamp.id.requireId()) // then resultActions @@ -1074,7 +1095,7 @@ class MissionControllerIntegrationTest : BaseIntegrationTest() { val completedTrip = tripTestHelper.saveCompletedTrip(member, TripCategory.COURSE) // when - val resultActions = getResultActions(token, completedTrip.id, courseStamp.id) + val resultActions = getResultActions(token, completedTrip.id.requireId(), courseStamp.id.requireId()) // then resultActions @@ -1091,7 +1112,7 @@ class MissionControllerIntegrationTest : BaseIntegrationTest() { val stampId = -1L // when - val resultActions = getResultActions(token, courseTrip.id, stampId) + val resultActions = getResultActions(token, courseTrip.id.requireId(), stampId) // then resultActions @@ -1105,7 +1126,7 @@ class MissionControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("스탬프가 요청한 여행에 속하지 않으면 403 Forbidden을 반환한다.") fun shouldReturnForbiddenWhenStampNotBelongToTrip() { // when - val resultActions = getResultActions(token, courseTrip.id, exploreStamp.id) + val resultActions = getResultActions(token, courseTrip.id.requireId(), exploreStamp.id.requireId()) // then resultActions @@ -1122,7 +1143,7 @@ class MissionControllerIntegrationTest : BaseIntegrationTest() { val deletedStamp = stampTestHelper.saveDeletedStamp(courseTrip, 3) // when - val resultActions = getResultActions(token, courseTrip.id, deletedStamp.id) + val resultActions = getResultActions(token, courseTrip.id.requireId(), deletedStamp.id.requireId()) // then resultActions @@ -1139,7 +1160,7 @@ class MissionControllerIntegrationTest : BaseIntegrationTest() { val completedStamp = stampTestHelper.saveCompletedStamp(courseTrip, 3) // when - val resultActions = getResultActions(token, courseTrip.id, completedStamp.id) + val resultActions = getResultActions(token, courseTrip.id.requireId(), completedStamp.id.requireId()) // then resultActions @@ -1153,7 +1174,7 @@ class MissionControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("특정 스탬프의 미션 목록을 조회하고 반환한다.") fun shouldReturnMissionsByStamp() { // when - val resultActions = getResultActions(token, courseTrip.id, courseStamp.id) + val resultActions = getResultActions(token, courseTrip.id.requireId(), courseStamp.id.requireId()) // then resultActions diff --git a/src/test/kotlin/com/ject/studytrip/pomodoro/application/service/PomodoroCommandServiceTest.kt b/src/test/kotlin/com/ject/studytrip/pomodoro/application/service/PomodoroCommandServiceTest.kt index c1be83f..6c6f3db 100644 --- a/src/test/kotlin/com/ject/studytrip/pomodoro/application/service/PomodoroCommandServiceTest.kt +++ b/src/test/kotlin/com/ject/studytrip/pomodoro/application/service/PomodoroCommandServiceTest.kt @@ -2,6 +2,7 @@ package com.ject.studytrip.pomodoro.application.service import com.ject.studytrip.BaseUnitTest import com.ject.studytrip.global.exception.CustomException +import com.ject.studytrip.global.util.EntityExtensions.requireId import com.ject.studytrip.member.domain.model.Member import com.ject.studytrip.member.fixture.MemberFixture import com.ject.studytrip.pomodoro.domain.error.PomodoroErrorCode @@ -55,12 +56,7 @@ class PomodoroCommandServiceTest : BaseUnitTest() { @DisplayName("유효한 요청이 들어오면 뽀모도로를 생성하고 반환한다.") fun shouldCreateAndReturnPomodoroWhenRequestIsValid() { // given - val request = - CreatePomodoroRequestFixture() - .apply { - focusDurationInMinute = 30 - focusSessionCount = 1 - }.build() + val request = CreatePomodoroRequestFixture().withFocusDurationInMinute(30).withFocusSessionCount(1).build() given(pomodoroRepository.save(any())).willReturn(pomodoro) // when @@ -84,10 +80,7 @@ class PomodoroCommandServiceTest : BaseUnitTest() { pomodoro.updateDeletedAt() // when - val exception = - assertThrows { - pomodoroCommandService.updateTotalFocusTime(pomodoro, totalFocusTimeInSeconds) - } + val exception = assertThrows { pomodoroCommandService.updateTotalFocusTime(pomodoro, totalFocusTimeInSeconds) } // then assertThat(exception.message).isEqualTo(PomodoroErrorCode.POMODORO_ALREADY_DELETED.message) @@ -100,10 +93,7 @@ class PomodoroCommandServiceTest : BaseUnitTest() { val totalFocusTimeInSeconds = -30 // when - val exception = - assertThrows { - pomodoroCommandService.updateTotalFocusTime(pomodoro, totalFocusTimeInSeconds) - } + val exception = assertThrows { pomodoroCommandService.updateTotalFocusTime(pomodoro, totalFocusTimeInSeconds) } // then assertThat(exception.message).isEqualTo(PomodoroErrorCode.POMODORO_NEGATIVE_FOCUS_TIME.message) @@ -133,10 +123,7 @@ class PomodoroCommandServiceTest : BaseUnitTest() { pomodoro.updateDeletedAt() // when - val exception = - assertThrows { - pomodoroCommandService.deletePomodoro(pomodoro) - } + val exception = assertThrows { pomodoroCommandService.deletePomodoro(pomodoro) } // then assertThat(exception.message).isEqualTo(PomodoroErrorCode.POMODORO_ALREADY_DELETED.message) @@ -220,7 +207,7 @@ class PomodoroCommandServiceTest : BaseUnitTest() { @DisplayName("특정 멤버가 소유한 뽀모도로가 하나라도 존재하지 않으면 0을 반환한다.") fun shouldReturnZeroWhenPomodorosOwnedByMemberDoNotExist() { // given - val memberId = member.id + val memberId = member.id.requireId() given(pomodoroCommandRepository.deleteAllByMemberId(memberId)).willReturn(0L) // when @@ -234,7 +221,7 @@ class PomodoroCommandServiceTest : BaseUnitTest() { @DisplayName("특정 멤버가 소유한 뽀모도로가 하나라도 존재하면 해당 개수를 반환한다.") fun shouldReturnCountWhenPomodorosOwnedByMemberExist() { // given - val memberId = member.id + val memberId = member.id.requireId() given(pomodoroCommandRepository.deleteAllByMemberId(memberId)).willReturn(5L) // when diff --git a/src/test/kotlin/com/ject/studytrip/pomodoro/application/service/PomodoroQueryServiceTest.kt b/src/test/kotlin/com/ject/studytrip/pomodoro/application/service/PomodoroQueryServiceTest.kt index ba285cd..e3fa9f5 100644 --- a/src/test/kotlin/com/ject/studytrip/pomodoro/application/service/PomodoroQueryServiceTest.kt +++ b/src/test/kotlin/com/ject/studytrip/pomodoro/application/service/PomodoroQueryServiceTest.kt @@ -2,6 +2,7 @@ package com.ject.studytrip.pomodoro.application.service import com.ject.studytrip.BaseUnitTest import com.ject.studytrip.global.exception.CustomException +import com.ject.studytrip.global.util.EntityExtensions.requireId import com.ject.studytrip.member.fixture.MemberFixture import com.ject.studytrip.pomodoro.domain.error.PomodoroErrorCode import com.ject.studytrip.pomodoro.domain.model.Pomodoro @@ -58,10 +59,7 @@ class PomodoroQueryServiceTest : BaseUnitTest() { given(pomodoroRepository.findByDailyGoalId(dailyGoalId)).willReturn(Optional.empty()) // when - val exception = - assertThrows { - pomodoroQueryService.getValidPomodoroByDailyGoalId(dailyGoalId) - } + val exception = assertThrows { pomodoroQueryService.getValidPomodoroByDailyGoalId(dailyGoalId) } // then assertThat(exception.message).isEqualTo(PomodoroErrorCode.POMODORO_NOT_FOUND.message) @@ -71,15 +69,12 @@ class PomodoroQueryServiceTest : BaseUnitTest() { @DisplayName("뽀모도로가 이미 삭제되었다면 예외가 발생한다.") fun shouldThrowExceptionWhenPomodoroAlreadyDeleted() { // given - val dailyGoalId = dailyGoal.id + val dailyGoalId = dailyGoal.id.requireId() pomodoro.updateDeletedAt() given(pomodoroRepository.findByDailyGoalId(dailyGoalId)).willReturn(Optional.of(pomodoro)) // when - val exception = - assertThrows { - pomodoroQueryService.getValidPomodoroByDailyGoalId(dailyGoalId) - } + val exception = assertThrows { pomodoroQueryService.getValidPomodoroByDailyGoalId(dailyGoalId) } // then assertThat(exception.message).isEqualTo(PomodoroErrorCode.POMODORO_ALREADY_DELETED.message) @@ -89,7 +84,7 @@ class PomodoroQueryServiceTest : BaseUnitTest() { @DisplayName("데일리 목표 ID로 뽀모도로를 조회하고 반환한다.") fun shouldReturnPomodoroByDailyGoalId() { // given - val dailyGoalId = dailyGoal.id + val dailyGoalId = dailyGoal.id.requireId() given(pomodoroRepository.findByDailyGoalId(dailyGoalId)).willReturn(Optional.of(pomodoro)) // when @@ -122,7 +117,7 @@ class PomodoroQueryServiceTest : BaseUnitTest() { @DisplayName("유효한 여행 ID가 들어오면 총 집중 시간(시간 단위)을 반환한다.") fun shouldReturnTotalFocusHoursWhenTripIdIsValid() { // given - val tripId = trip.id + val tripId = trip.id.requireId() given(pomodoroQueryRepository.sumFocusHoursByTripId(tripId)).willReturn(100L) // when diff --git a/src/test/kotlin/com/ject/studytrip/pomodoro/fixture/CreatePomodoroRequestFixture.kt b/src/test/kotlin/com/ject/studytrip/pomodoro/fixture/CreatePomodoroRequestFixture.kt index 607c80a..9861016 100644 --- a/src/test/kotlin/com/ject/studytrip/pomodoro/fixture/CreatePomodoroRequestFixture.kt +++ b/src/test/kotlin/com/ject/studytrip/pomodoro/fixture/CreatePomodoroRequestFixture.kt @@ -2,14 +2,15 @@ package com.ject.studytrip.pomodoro.fixture import com.ject.studytrip.pomodoro.presentation.dto.request.CreatePomodoroRequest -class CreatePomodoroRequestFixture { - var focusDurationInMinute: Int = 25 - var focusSessionCount: Int = 4 - +class CreatePomodoroRequestFixture( + private val focusDurationInMinute: Int = 25, + private val focusSessionCount: Int = 4, +) { fun withFocusDurationInMinute(focusDurationInMinute: Int): CreatePomodoroRequestFixture = - apply { this.focusDurationInMinute = focusDurationInMinute } + CreatePomodoroRequestFixture(focusDurationInMinute, focusSessionCount) - fun withFocusSessionCount(focusSessionCount: Int): CreatePomodoroRequestFixture = apply { this.focusSessionCount = focusSessionCount } + fun withFocusSessionCount(focusSessionCount: Int): CreatePomodoroRequestFixture = + CreatePomodoroRequestFixture(focusDurationInMinute, focusSessionCount) fun build(): CreatePomodoroRequest = CreatePomodoroRequest(focusDurationInMinute, focusSessionCount) } diff --git a/src/test/kotlin/com/ject/studytrip/pomodoro/fixture/PomodoroFixture.kt b/src/test/kotlin/com/ject/studytrip/pomodoro/fixture/PomodoroFixture.kt index 91e2bba..ecbb567 100644 --- a/src/test/kotlin/com/ject/studytrip/pomodoro/fixture/PomodoroFixture.kt +++ b/src/test/kotlin/com/ject/studytrip/pomodoro/fixture/PomodoroFixture.kt @@ -7,11 +7,10 @@ import org.springframework.test.util.ReflectionTestUtils class PomodoroFixture( private val dailyGoal: DailyGoal, + private val focusDurationSeconds: Int = 30 * 60, + private val focusSessionCount: Int = 1, + private val breakDurationSeconds: Int = 0, ) { - var focusDurationSeconds: Int = 30 * 60 - var focusSessionCount: Int = 1 - var breakDurationSeconds: Int = 0 - fun create(): Pomodoro = PomodoroFactory.create(dailyGoal, focusDurationSeconds, focusSessionCount, breakDurationSeconds) fun createWithId(id: Long): Pomodoro = diff --git a/src/test/kotlin/com/ject/studytrip/stamp/application/service/StampCommandServiceTest.kt b/src/test/kotlin/com/ject/studytrip/stamp/application/service/StampCommandServiceTest.kt index 8b2c2e5..d8e1e2e 100644 --- a/src/test/kotlin/com/ject/studytrip/stamp/application/service/StampCommandServiceTest.kt +++ b/src/test/kotlin/com/ject/studytrip/stamp/application/service/StampCommandServiceTest.kt @@ -2,6 +2,7 @@ package com.ject.studytrip.stamp.application.service import com.ject.studytrip.BaseUnitTest import com.ject.studytrip.global.exception.CustomException +import com.ject.studytrip.global.util.EntityExtensions.requireId import com.ject.studytrip.member.domain.model.Member import com.ject.studytrip.member.fixture.MemberFixture import com.ject.studytrip.stamp.domain.error.StampErrorCode @@ -154,7 +155,7 @@ class StampCommandServiceTest : BaseUnitTest() { @DisplayName("스탬프 종료일을 여행 종료일보다 이후 날짜로 수정하면 예외가 발생한다.") fun shouldThrowExceptionWhenEndDateIsAfterTripEndDate() { // given - val request = fixture.withEndTime(LocalDate.now().plusDays(100)).build() + val request = fixture.withEndDate(LocalDate.now().plusDays(100)).build() // when val exception = assertThrows { stampCommandService.updateStamp(courseTrip, courseStamp1, request) } @@ -167,7 +168,7 @@ class StampCommandServiceTest : BaseUnitTest() { @DisplayName("유효한 요청이 들어오면 스탬프 이름을 수정한다.") fun shouldUpdateStampNameWhenRequestIsValid() { // given - val request = fixture.withEndTime(null).build() + val request = fixture.withEndDate(null).build() // when stampCommandService.updateStamp(courseTrip, courseStamp1, request) @@ -303,10 +304,11 @@ class StampCommandServiceTest : BaseUnitTest() { @DisplayName("여행 카테고리가 탐험형으로 변경되면 소속되어 있던 모든 스탬프 순서를 0으로 초기화한다.") fun shouldResetStampOrdersWhenTripCategoryChangesToExplore() { // given - given(stampRepository.findAllByTripIdOrderByCreatedAtAsc(courseTrip.id)).willReturn(listOf(courseStamp1, courseStamp2)) + val tripId = courseTrip.id.requireId() + given(stampRepository.findAllByTripIdOrderByCreatedAtAsc(tripId)).willReturn(listOf(courseStamp1, courseStamp2)) // when - stampCommandService.updateStampOrdersByTripCategoryChange(courseTrip.id, TripCategory.EXPLORE) + stampCommandService.updateStampOrdersByTripCategoryChange(tripId, TripCategory.EXPLORE) // then assertThat(courseStamp1.stampOrder).isEqualTo(0) @@ -317,10 +319,11 @@ class StampCommandServiceTest : BaseUnitTest() { @DisplayName("여행 카테고리가 코스형으로 변경되면 소속되어 있던 모든 스탬프 순서를 생성일 기준으로 1부터 초기화합니다.") fun shouldSetStampOrdersWhenCategoryChangesToCourse() { // given - given(stampRepository.findAllByTripIdOrderByCreatedAtAsc(exploreTrip.id)).willReturn(listOf(exploreStamp1, exploreStamp2)) + val tripId = exploreTrip.id.requireId() + given(stampRepository.findAllByTripIdOrderByCreatedAtAsc(tripId)).willReturn(listOf(exploreStamp1, exploreStamp2)) // when - stampCommandService.updateStampOrdersByTripCategoryChange(exploreTrip.id, TripCategory.COURSE) + stampCommandService.updateStampOrdersByTripCategoryChange(tripId, TripCategory.COURSE) // then assertThat(exploreStamp1.stampOrder).isEqualTo(1) @@ -352,7 +355,7 @@ class StampCommandServiceTest : BaseUnitTest() { stampCommandService.completeStamp(courseStamp1) // then - assertThat(courseStamp1.isCompleted).isTrue + assertThat(courseStamp1.isCompleted()).isTrue } } @@ -439,7 +442,8 @@ class StampCommandServiceTest : BaseUnitTest() { val newTrip = TripFixture(member, TripCategory.COURSE).createWithId(3L) // when - val exception = assertThrows { stampCommandService.validateStampBelongsToTrip(newTrip.id, courseStamp1) } + val exception = + assertThrows { stampCommandService.validateStampBelongsToTrip(newTrip.id.requireId(), courseStamp1) } // then assertThat(exception.message).isEqualTo(StampErrorCode.STAMP_NOT_BELONGS_TO_TRIP.message) @@ -453,7 +457,7 @@ class StampCommandServiceTest : BaseUnitTest() { @DisplayName("특정 여행의 어떤 스탬프가 완료되지 않았다면 예외가 발생한다.") fun shouldThrowExceptionWhenAnyStampIsNotCompleted() { // given - val tripId = courseTrip.id + val tripId = courseTrip.id.requireId() given(stampCommandRepository.existsByTripIdAndCompletedIsFalseAndDeletedAtIsNull(tripId)).willReturn(true) // when @@ -467,7 +471,7 @@ class StampCommandServiceTest : BaseUnitTest() { @DisplayName("특정 여행의 모든 스탬프가 완료되었다면 예외가 발생하지 않는다.") fun shouldPassWhenAllStampsAreCompleted() { // given - val tripId = courseTrip.id + val tripId = courseTrip.id.requireId() given(stampCommandRepository.existsByTripIdAndCompletedIsFalseAndDeletedAtIsNull(tripId)).willReturn(false) // when & then @@ -542,7 +546,7 @@ class StampCommandServiceTest : BaseUnitTest() { @DisplayName("특정 멤버가 소유한 스탬프가 하나라도 존재하지 않으면 0을 반환한다.") fun shouldReturnZeroWhenStampsOwnedByMemberDoNotExist() { // given - val memberId = member.id + val memberId = member.id.requireId() given(stampCommandRepository.deleteAllByMemberId(memberId)).willReturn(0L) // when @@ -556,7 +560,7 @@ class StampCommandServiceTest : BaseUnitTest() { @DisplayName("특정 멤버가 소유한 스탬프가 하나라도 존재하면 해당 개수를 반환한다.") fun shouldReturnCountWhenStampsOwnedByMemberExist() { // given - val memberId = member.id + val memberId = member.id.requireId() given(stampCommandRepository.deleteAllByMemberId(memberId)).willReturn(5L) // when diff --git a/src/test/kotlin/com/ject/studytrip/stamp/application/service/StampQueryServiceTest.kt b/src/test/kotlin/com/ject/studytrip/stamp/application/service/StampQueryServiceTest.kt index 1533630..25b3a1f 100644 --- a/src/test/kotlin/com/ject/studytrip/stamp/application/service/StampQueryServiceTest.kt +++ b/src/test/kotlin/com/ject/studytrip/stamp/application/service/StampQueryServiceTest.kt @@ -2,6 +2,7 @@ package com.ject.studytrip.stamp.application.service import com.ject.studytrip.BaseUnitTest import com.ject.studytrip.global.exception.CustomException +import com.ject.studytrip.global.util.EntityExtensions.requireId import com.ject.studytrip.member.fixture.MemberFixture import com.ject.studytrip.stamp.domain.error.StampErrorCode import com.ject.studytrip.stamp.domain.model.Stamp @@ -63,7 +64,7 @@ class StampQueryServiceTest : BaseUnitTest() { val stampId = -1L // when - val exception = assertThrows { stampQueryService.getValidStamp(courseTrip.id, stampId) } + val exception = assertThrows { stampQueryService.getValidStamp(courseTrip.id.requireId(), stampId) } // then assertThat(exception.message).isEqualTo(StampErrorCode.STAMP_NOT_FOUND.message) @@ -73,11 +74,11 @@ class StampQueryServiceTest : BaseUnitTest() { @DisplayName("특정 스탬프가 다른 여행에 속한다면 예외가 발생한다.") fun shouldThrowExceptionWhenStampNotBelongToTrip() { // given - val stampId = courseStamp1.id + val stampId = courseStamp1.id.requireId() given(stampRepository.findById(stampId)).willReturn(Optional.of(courseStamp1)) // when - val exception = assertThrows { stampQueryService.getValidStamp(exploreTrip.id, stampId) } + val exception = assertThrows { stampQueryService.getValidStamp(exploreTrip.id.requireId(), stampId) } // then assertThat(exception.message).isEqualTo(StampErrorCode.STAMP_NOT_BELONGS_TO_TRIP.message) @@ -87,12 +88,12 @@ class StampQueryServiceTest : BaseUnitTest() { @DisplayName("스탬프가 이미 삭제되었다면 예외가 발생한다.") fun shouldThrowExceptionWhenStampAlreadyDeleted() { // given - val stampId = courseStamp1.id + val stampId = courseStamp1.id.requireId() courseStamp1.updateDeletedAt() given(stampRepository.findById(stampId)).willReturn(Optional.of(courseStamp1)) // when - val exception = assertThrows { stampQueryService.getValidStamp(courseTrip.id, stampId) } + val exception = assertThrows { stampQueryService.getValidStamp(courseTrip.id.requireId(), stampId) } // then assertThat(exception.message).isEqualTo(StampErrorCode.STAMP_ALREADY_DELETED.message) @@ -102,12 +103,12 @@ class StampQueryServiceTest : BaseUnitTest() { @DisplayName("스탬프가 이미 완료되었다면 예외가 발생한다.") fun shouldThrowExceptionWhenStampAlreadyCompleted() { // given - val stampId = courseStamp1.id + val stampId = courseStamp1.id.requireId() courseStamp1.updateCompleted() given(stampRepository.findById(stampId)).willReturn(Optional.of(courseStamp1)) // when - val exception = assertThrows { stampQueryService.getValidStamp(courseTrip.id, stampId) } + val exception = assertThrows { stampQueryService.getValidStamp(courseTrip.id.requireId(), stampId) } // then assertThat(exception.message).isEqualTo(StampErrorCode.STAMP_ALREADY_COMPLETED.message) @@ -117,11 +118,11 @@ class StampQueryServiceTest : BaseUnitTest() { @DisplayName("특정 여행에 속한 스탬프가 존재하면 스탬프를 조회하고 반환한다.") fun shouldReturnStampWhenStampBelongsToTrip() { // given - val stampId = courseStamp1.id + val stampId = courseStamp1.id.requireId() given(stampRepository.findById(stampId)).willReturn(Optional.of(courseStamp1)) // when - val result = stampQueryService.getValidStamp(courseTrip.id, stampId) + val result = stampQueryService.getValidStamp(courseTrip.id.requireId(), stampId) // then assertThat(result).isEqualTo(courseStamp1) @@ -135,7 +136,7 @@ class StampQueryServiceTest : BaseUnitTest() { @DisplayName("특정 여행에 속한 삭제되지 않은 스탬프 목록을 조회하고 반환한다.") fun shouldReturnStampsByTripIdAndDeletedAtIsNull() { // given - val tripId = courseTrip.id + val tripId = courseTrip.id.requireId() given(stampRepository.findAllByTripIdAndDeletedAtIsNull(tripId)).willReturn(listOf(courseStamp1, courseStamp2)) // when @@ -154,7 +155,7 @@ class StampQueryServiceTest : BaseUnitTest() { @DisplayName("특정 코스형 여행에서 진행 중인 스탬프가 존재하지 않으면 예외가 발생한다.") fun shouldThrowExceptionWhenProgressStampDoesNotExistForCourseTrip() { // given - val tripId = courseTrip.id + val tripId = courseTrip.id.requireId() courseStamp1.updateCompleted() courseStamp2.updateCompleted() given(stampQueryRepository.findFirstIncompleteStampByTripId(tripId)).willReturn(Optional.empty()) @@ -170,7 +171,7 @@ class StampQueryServiceTest : BaseUnitTest() { @DisplayName("특정 코스형 여행에서 진행 중인 첫번째 스탬프를 조회하고 반환한다.") fun shouldReturnFirstProcessingStampForCourseTrip() { // given - val tripId = courseTrip.id + val tripId = courseTrip.id.requireId() given(stampQueryRepository.findFirstIncompleteStampByTripId(tripId)).willReturn(Optional.of(courseStamp1)) // when @@ -178,8 +179,8 @@ class StampQueryServiceTest : BaseUnitTest() { // then assertThat(result).isEqualTo(courseStamp1) - assertThat(result.isDeleted).isFalse - assertThat(result.isCompleted).isFalse + assertThat(result.isDeleted()).isFalse + assertThat(result.isCompleted()).isFalse } } @@ -271,7 +272,7 @@ class StampQueryServiceTest : BaseUnitTest() { @DisplayName("코스형 여행이라면 다음 스탬프 순서를 반환한다.") fun shouldReturnNextStampOrderForCourseTrip() { // given - given(stampQueryRepository.findNextStampOrderByTripId(courseTrip.id)).willReturn(3) + given(stampQueryRepository.findNextStampOrderByTripId(courseTrip.id.requireId())).willReturn(3) // when val result = stampQueryService.getNextStampOrderByTrip(courseTrip) @@ -288,7 +289,7 @@ class StampQueryServiceTest : BaseUnitTest() { @DisplayName("시프트할 스탬프가 존재하지 않으면 빈 리스트를 반환한다.") fun shouldReturnEmptyListWhenStampsToShiftDoNotExist() { // given - val tripId = courseTrip.id + val tripId = courseTrip.id.requireId() val deletedOrder = courseStamp2.stampOrder courseStamp2.updateDeletedAt() given(stampQueryRepository.findStampsToShiftAfterOrder(tripId, deletedOrder)).willReturn(emptyList()) @@ -304,7 +305,7 @@ class StampQueryServiceTest : BaseUnitTest() { @DisplayName("시프트할 스탬프 목록을 조회하고 반환한다.") fun shouldReturnStampsToShiftAfterDeleted() { // given - val tripId = courseTrip.id + val tripId = courseTrip.id.requireId() val deletedOrder = courseStamp1.stampOrder given(stampQueryRepository.findStampsToShiftAfterOrder(tripId, deletedOrder)).willReturn(listOf(courseStamp2)) diff --git a/src/test/kotlin/com/ject/studytrip/stamp/fixture/CreateStampRequestFixture.kt b/src/test/kotlin/com/ject/studytrip/stamp/fixture/CreateStampRequestFixture.kt index e65d953..a560919 100644 --- a/src/test/kotlin/com/ject/studytrip/stamp/fixture/CreateStampRequestFixture.kt +++ b/src/test/kotlin/com/ject/studytrip/stamp/fixture/CreateStampRequestFixture.kt @@ -3,17 +3,15 @@ package com.ject.studytrip.stamp.fixture import com.ject.studytrip.stamp.presentation.dto.request.CreateStampRequest import java.time.LocalDate -class CreateStampRequestFixture { - var name: String = "TEST 스탬프 이름" - var endDate: LocalDate? = LocalDate.now().plusDays(7) +class CreateStampRequestFixture( + private val name: String = "TEST 스탬프 이름", + private val endDate: LocalDate? = LocalDate.now().plusDays(7), +) { + fun withName(name: String): CreateStampRequestFixture = CreateStampRequestFixture(name, endDate) - fun withName(name: String): CreateStampRequestFixture = apply { this.name = name } + fun withEndDate(endDate: LocalDate?): CreateStampRequestFixture = CreateStampRequestFixture(name, endDate) - fun withEndDate(endDate: LocalDate?): CreateStampRequestFixture = apply { this.endDate = endDate } - - fun withEndDateInPast(): CreateStampRequestFixture = apply { this.endDate = LocalDate.now().minusDays(1) } - - fun withEndDateAfterTripEndDate(): CreateStampRequestFixture = apply { this.endDate = LocalDate.now().plusDays(100) } + fun withEndDateAfterTripEndDate(): CreateStampRequestFixture = CreateStampRequestFixture(name, LocalDate.now().plusDays(100)) fun build() = CreateStampRequest(name, endDate) } diff --git a/src/test/kotlin/com/ject/studytrip/stamp/fixture/StampFixture.kt b/src/test/kotlin/com/ject/studytrip/stamp/fixture/StampFixture.kt index 76e9fc3..02f3afd 100644 --- a/src/test/kotlin/com/ject/studytrip/stamp/fixture/StampFixture.kt +++ b/src/test/kotlin/com/ject/studytrip/stamp/fixture/StampFixture.kt @@ -9,10 +9,9 @@ import java.time.LocalDate class StampFixture( private val trip: Trip, private val order: Int, + private val name: String = "TEST 스탬프 이름", + private val endTime: LocalDate = LocalDate.now().plusDays(7), ) { - var name: String = "TEST 스탬프 이름" - var endTime: LocalDate = LocalDate.now().plusDays(7) - fun create(): Stamp = StampFactory.create(trip, name, order, endTime) fun createWithId(id: Long): Stamp = diff --git a/src/test/kotlin/com/ject/studytrip/stamp/fixture/UpdateStampOrderRequestFixture.kt b/src/test/kotlin/com/ject/studytrip/stamp/fixture/UpdateStampOrderRequestFixture.kt index 2dbcbe2..ef224d7 100644 --- a/src/test/kotlin/com/ject/studytrip/stamp/fixture/UpdateStampOrderRequestFixture.kt +++ b/src/test/kotlin/com/ject/studytrip/stamp/fixture/UpdateStampOrderRequestFixture.kt @@ -2,10 +2,10 @@ package com.ject.studytrip.stamp.fixture import com.ject.studytrip.stamp.presentation.dto.request.UpdateStampOrderRequest -class UpdateStampOrderRequestFixture { - var orderedStampIds: List = listOf(1L, 2L) - - fun withOrderedStampIds(orderedStampIds: List): UpdateStampOrderRequestFixture = apply { this.orderedStampIds = orderedStampIds } +class UpdateStampOrderRequestFixture( + private val orderedStampIds: List = listOf(1L, 2L), +) { + fun withOrderedStampIds(orderedStampIds: List): UpdateStampOrderRequestFixture = UpdateStampOrderRequestFixture(orderedStampIds) fun build(): UpdateStampOrderRequest = UpdateStampOrderRequest(orderedStampIds) } diff --git a/src/test/kotlin/com/ject/studytrip/stamp/fixture/UpdateStampRequestFixture.kt b/src/test/kotlin/com/ject/studytrip/stamp/fixture/UpdateStampRequestFixture.kt index c5ad086..844c5ec 100644 --- a/src/test/kotlin/com/ject/studytrip/stamp/fixture/UpdateStampRequestFixture.kt +++ b/src/test/kotlin/com/ject/studytrip/stamp/fixture/UpdateStampRequestFixture.kt @@ -3,13 +3,13 @@ package com.ject.studytrip.stamp.fixture import com.ject.studytrip.stamp.presentation.dto.request.UpdateStampRequest import java.time.LocalDate -class UpdateStampRequestFixture { - var name: String? = "TEST 새로운 스탬프 이름" - var endDate: LocalDate? = LocalDate.now().plusDays(7) +class UpdateStampRequestFixture( + private val name: String? = "TEST 새로운 스탬프 이름", + private val endDate: LocalDate? = LocalDate.now().plusDays(7), +) { + fun withName(name: String?): UpdateStampRequestFixture = UpdateStampRequestFixture(name, endDate) - fun withName(name: String?): UpdateStampRequestFixture = apply { this.name = name } - - fun withEndTime(endTime: LocalDate?): UpdateStampRequestFixture = apply { this.endDate = endTime } + fun withEndDate(endDate: LocalDate?): UpdateStampRequestFixture = UpdateStampRequestFixture(name, endDate) fun build() = UpdateStampRequest(name, endDate) } diff --git a/src/test/kotlin/com/ject/studytrip/stamp/helper/StampTestHelper.kt b/src/test/kotlin/com/ject/studytrip/stamp/helper/StampTestHelper.kt index cd54065..0ccad58 100644 --- a/src/test/kotlin/com/ject/studytrip/stamp/helper/StampTestHelper.kt +++ b/src/test/kotlin/com/ject/studytrip/stamp/helper/StampTestHelper.kt @@ -23,12 +23,7 @@ class StampTestHelper( fun saveCompletedStamp( trip: Trip, order: Int, - ): Stamp = - stampRepository.save( - StampFixture(trip, order).create().also { - it.updateCompleted() - }, - ) + ): Stamp = stampRepository.save(StampFixture(trip, order).create().also { it.updateCompleted() }) fun getStamp(stampId: Long): Stamp = stampRepository.findById(stampId).orElseThrow() } diff --git a/src/test/kotlin/com/ject/studytrip/stamp/presentation/controller/StampControllerIntegrationTest.kt b/src/test/kotlin/com/ject/studytrip/stamp/presentation/controller/StampControllerIntegrationTest.kt index c0a7322..c0f7516 100644 --- a/src/test/kotlin/com/ject/studytrip/stamp/presentation/controller/StampControllerIntegrationTest.kt +++ b/src/test/kotlin/com/ject/studytrip/stamp/presentation/controller/StampControllerIntegrationTest.kt @@ -5,6 +5,7 @@ import com.ject.studytrip.auth.domain.error.AuthErrorCode import com.ject.studytrip.auth.fixture.TokenFixture import com.ject.studytrip.auth.helper.TokenTestHelper import com.ject.studytrip.global.exception.error.CommonErrorCode +import com.ject.studytrip.global.util.EntityExtensions.requireId import com.ject.studytrip.member.domain.model.Member import com.ject.studytrip.member.domain.model.MemberRole import com.ject.studytrip.member.helper.MemberTestHelper @@ -113,7 +114,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions("", courseTrip.id, request) + val resultActions = getResultActions("", courseTrip.id.requireId(), request) // then resultActions @@ -148,7 +149,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.withName(" ").build() // when - val resultActions = getResultActions(token, courseTrip.id, request) + val resultActions = getResultActions(token, courseTrip.id.requireId(), request) // then resultActions @@ -183,7 +184,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions(token, newTrip.id, request) + val resultActions = getResultActions(token, newTrip.id.requireId(), request) // then resultActions @@ -201,7 +202,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions(token, deletedTrip.id, request) + val resultActions = getResultActions(token, deletedTrip.id.requireId(), request) // then resultActions @@ -219,7 +220,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions(token, completedTrip.id, request) + val resultActions = getResultActions(token, completedTrip.id.requireId(), request) // then resultActions @@ -236,7 +237,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions(token, courseTrip.id, request) + val resultActions = getResultActions(token, courseTrip.id.requireId(), request) // then resultActions @@ -271,7 +272,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions("", courseTrip.id, courseStamp1.id, request) + val resultActions = getResultActions("", courseTrip.id.requireId(), courseStamp1.id.requireId(), request) // then resultActions @@ -289,7 +290,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions(token, tripId, courseStamp1.id, request) + val resultActions = getResultActions(token, tripId, courseStamp1.id.requireId(), request) // then resultActions @@ -307,7 +308,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions(token, courseTrip.id, stampId, request) + val resultActions = getResultActions(token, courseTrip.id.requireId(), stampId, request) // then resultActions @@ -325,7 +326,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions(token, tripId, courseStamp1.id, request) + val resultActions = getResultActions(token, tripId, courseStamp1.id.requireId(), request) // then resultActions @@ -342,7 +343,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions(token, newTrip.id, courseStamp1.id, request) + val resultActions = getResultActions(token, newTrip.id.requireId(), courseStamp1.id.requireId(), request) // then resultActions @@ -360,7 +361,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions(token, deletedTrip.id, courseStamp1.id, request) + val resultActions = getResultActions(token, deletedTrip.id.requireId(), courseStamp1.id.requireId(), request) // then resultActions @@ -378,7 +379,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions(token, completedTrip.id, courseStamp1.id, request) + val resultActions = getResultActions(token, completedTrip.id.requireId(), courseStamp1.id.requireId(), request) // then resultActions @@ -396,7 +397,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions(token, courseTrip.id, stampId, request) + val resultActions = getResultActions(token, courseTrip.id.requireId(), stampId, request) // then resultActions @@ -413,7 +414,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions(token, courseTrip.id, exploreStamp1.id, request) + val resultActions = getResultActions(token, courseTrip.id.requireId(), exploreStamp1.id.requireId(), request) // then resultActions @@ -431,7 +432,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions(token, courseTrip.id, deletedStamp.id, request) + val resultActions = getResultActions(token, courseTrip.id.requireId(), deletedStamp.id.requireId(), request) // then resultActions @@ -449,7 +450,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions(token, courseTrip.id, completedStamp.id, request) + val resultActions = getResultActions(token, courseTrip.id.requireId(), completedStamp.id.requireId(), request) // then resultActions @@ -466,7 +467,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions(token, courseTrip.id, courseStamp1.id, request) + val resultActions = getResultActions(token, courseTrip.id.requireId(), courseStamp1.id.requireId(), request) // then resultActions @@ -500,7 +501,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions("", courseTrip.id, request) + val resultActions = getResultActions("", courseTrip.id.requireId(), request) // then resultActions @@ -553,7 +554,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions(token, newTrip.id, request) + val resultActions = getResultActions(token, newTrip.id.requireId(), request) // then resultActions @@ -571,7 +572,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions(token, deletedTrip.id, request) + val resultActions = getResultActions(token, deletedTrip.id.requireId(), request) // then resultActions @@ -589,7 +590,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions(token, completedTrip.id, request) + val resultActions = getResultActions(token, completedTrip.id.requireId(), request) // then resultActions @@ -606,7 +607,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions(token, exploreTrip.id, request) + val resultActions = getResultActions(token, exploreTrip.id.requireId(), request) // then resultActions @@ -623,7 +624,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.withOrderedStampIds(listOf(1000L, 2000L)).build() // when - val resultActions = getResultActions(token, courseTrip.id, request) + val resultActions = getResultActions(token, courseTrip.id.requireId(), request) // then resultActions @@ -637,10 +638,14 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("스탬프가 요청한 여행에 속하지 않으면 403 Forbidden을 반환한다.") fun shouldReturnForbiddenWhenStampNotBelongToTrip() { // given - val request = fixture.withOrderedStampIds(listOf(courseStamp1.id, courseStamp2.id, exploreStamp1.id)).build() + val request = + fixture + .withOrderedStampIds( + listOf(courseStamp1.id.requireId(), courseStamp2.id.requireId(), exploreStamp1.id.requireId()), + ).build() // when - val resultActions = getResultActions(token, courseTrip.id, request) + val resultActions = getResultActions(token, courseTrip.id.requireId(), request) // then resultActions @@ -655,10 +660,14 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { fun shouldReturnBadRequestWhenStampAlreadyDeleted() { // given val deletedStamp = stampTestHelper.saveDeletedStamp(courseTrip, 3) - val request = fixture.withOrderedStampIds(listOf(courseStamp1.id, courseStamp2.id, deletedStamp.id)).build() + val request = + fixture + .withOrderedStampIds( + listOf(courseStamp1.id.requireId(), courseStamp2.id.requireId(), deletedStamp.id.requireId()), + ).build() // when - val resultActions = getResultActions(token, courseTrip.id, request) + val resultActions = getResultActions(token, courseTrip.id.requireId(), request) // then resultActions @@ -673,10 +682,14 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { fun shouldReturnBadRequestWhenStampAlreadyCompleted() { // given val completedStamp = stampTestHelper.saveCompletedStamp(courseTrip, 3) - val request = fixture.withOrderedStampIds(listOf(courseStamp1.id, courseStamp2.id, completedStamp.id)).build() + val request = + fixture + .withOrderedStampIds( + listOf(courseStamp1.id.requireId(), courseStamp2.id.requireId(), completedStamp.id.requireId()), + ).build() // when - val resultActions = getResultActions(token, courseTrip.id, request) + val resultActions = getResultActions(token, courseTrip.id.requireId(), request) // then resultActions @@ -690,10 +703,10 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("유효한 요청이 들어오면 스탬프 순서를 변경한다.") fun shouldUpdateStampOrdersWhenRequestIsValid() { // given - val request = fixture.withOrderedStampIds(listOf(courseStamp2.id, courseStamp1.id)).build() + val request = fixture.withOrderedStampIds(listOf(courseStamp2.id.requireId(), courseStamp1.id.requireId())).build() // when - val resultActions = getResultActions(token, courseTrip.id, request) + val resultActions = getResultActions(token, courseTrip.id.requireId(), request) // then resultActions @@ -720,7 +733,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("인증되지 않은 사용자라면 401 Unauthorized를 반환한다.") fun shouldReturnUnauthorizedWhenUnauthenticated() { // when - val resultActions = getResultActions("", courseTrip.id, courseStamp1.id) + val resultActions = getResultActions("", courseTrip.id.requireId(), courseStamp1.id.requireId()) // then resultActions @@ -736,7 +749,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { // given val tripId = "abc" // when - val resultActions = getResultActions(token, tripId, courseStamp1.id) + val resultActions = getResultActions(token, tripId, courseStamp1.id.requireId()) // then resultActions @@ -753,7 +766,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { val stampId = "abc" // when - val resultActions = getResultActions(token, courseTrip.id, stampId) + val resultActions = getResultActions(token, courseTrip.id.requireId(), stampId) // then resultActions @@ -770,7 +783,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { val tripId = -1L // when - val resultActions = getResultActions(token, tripId, courseStamp1.id) + val resultActions = getResultActions(token, tripId, courseStamp1.id.requireId()) // then resultActions @@ -784,7 +797,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("여행의 소유자가 아니라면 403 Forbidden을 반환한다.") fun shouldReturnForbiddenWhenMemberIsNotTripOwner() { // when - val resultActions = getResultActions(token, newTrip.id, courseStamp1.id) + val resultActions = getResultActions(token, newTrip.id.requireId(), courseStamp1.id.requireId()) // then resultActions @@ -801,7 +814,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { val deletedTrip = tripTestHelper.saveDeletedTrip(member, TripCategory.COURSE) // when - val resultActions = getResultActions(token, deletedTrip.id, courseStamp1.id) + val resultActions = getResultActions(token, deletedTrip.id.requireId(), courseStamp1.id.requireId()) // then resultActions @@ -818,7 +831,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { val completedTrip = tripTestHelper.saveCompletedTrip(member, TripCategory.COURSE) // when - val resultActions = getResultActions(token, completedTrip.id, courseStamp1.id) + val resultActions = getResultActions(token, completedTrip.id.requireId(), courseStamp1.id.requireId()) // then resultActions @@ -835,7 +848,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { val stampId = -1L // when - val resultActions = getResultActions(token, courseTrip.id, stampId) + val resultActions = getResultActions(token, courseTrip.id.requireId(), stampId) // then resultActions @@ -849,7 +862,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("스탬프가 요청한 여행에 속하지 않으면 403 Forbidden을 반환한다.") fun shouldReturnForbiddenWhenStampNotBelongToTrip() { // when - val resultActions = getResultActions(token, courseTrip.id, exploreStamp1.id) + val resultActions = getResultActions(token, courseTrip.id.requireId(), exploreStamp1.id.requireId()) // then resultActions @@ -866,7 +879,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { val deletedStamp = stampTestHelper.saveDeletedStamp(courseTrip, 3) // when - val resultActions = getResultActions(token, courseTrip.id, deletedStamp.id) + val resultActions = getResultActions(token, courseTrip.id.requireId(), deletedStamp.id.requireId()) // then resultActions @@ -883,7 +896,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { val completedStamp = stampTestHelper.saveCompletedStamp(courseTrip, 3) // when - val resultActions = getResultActions(token, courseTrip.id, completedStamp.id) + val resultActions = getResultActions(token, courseTrip.id.requireId(), completedStamp.id.requireId()) // then resultActions @@ -897,7 +910,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("특정 스탬프를 삭제한다.") fun shouldDeleteStamp() { // when - val resultActions = getResultActions(token, courseTrip.id, courseStamp1.id) + val resultActions = getResultActions(token, courseTrip.id.requireId(), courseStamp1.id.requireId()) // then resultActions @@ -924,7 +937,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("인증되지 않은 사용자라면 401 Unauthorized를 반환한다.") fun shouldReturnUnauthorizedWhenUnauthenticated() { // when - val resultActions = getResultActions("", courseTrip.id, courseStamp1.id) + val resultActions = getResultActions("", courseTrip.id.requireId(), courseStamp1.id.requireId()) // then resultActions @@ -940,7 +953,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { // given val tripId = "abc" // when - val resultActions = getResultActions(token, tripId, courseStamp1.id) + val resultActions = getResultActions(token, tripId, courseStamp1.id.requireId()) // then resultActions @@ -957,7 +970,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { val stampId = "abc" // when - val resultActions = getResultActions(token, courseTrip.id, stampId) + val resultActions = getResultActions(token, courseTrip.id.requireId(), stampId) // then resultActions @@ -974,7 +987,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { val tripId = -1L // when - val resultActions = getResultActions(token, tripId, courseStamp1.id) + val resultActions = getResultActions(token, tripId, courseStamp1.id.requireId()) // then resultActions @@ -988,7 +1001,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("여행의 소유자가 아니라면 403 Forbidden을 반환한다.") fun shouldReturnForbiddenWhenMemberIsNotTripOwner() { // when - val resultActions = getResultActions(token, newTrip.id, courseStamp1.id) + val resultActions = getResultActions(token, newTrip.id.requireId(), courseStamp1.id.requireId()) // then resultActions @@ -1005,7 +1018,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { val deletedTrip = tripTestHelper.saveDeletedTrip(member, TripCategory.COURSE) // when - val resultActions = getResultActions(token, deletedTrip.id, courseStamp1.id) + val resultActions = getResultActions(token, deletedTrip.id.requireId(), courseStamp1.id.requireId()) // then resultActions @@ -1022,7 +1035,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { val completedTrip = tripTestHelper.saveCompletedTrip(member, TripCategory.COURSE) // when - val resultActions = getResultActions(token, completedTrip.id, courseStamp1.id) + val resultActions = getResultActions(token, completedTrip.id.requireId(), courseStamp1.id.requireId()) // then resultActions @@ -1039,7 +1052,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { val stampId = -1L // when - val resultActions = getResultActions(token, courseTrip.id, stampId) + val resultActions = getResultActions(token, courseTrip.id.requireId(), stampId) // then resultActions @@ -1053,7 +1066,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("스탬프가 요청한 여행에 속하지 않으면 403 Forbidden을 반환한다.") fun shouldReturnForbiddenWhenStampNotBelongToTrip() { // when - val resultActions = getResultActions(token, courseTrip.id, exploreStamp1.id) + val resultActions = getResultActions(token, courseTrip.id.requireId(), exploreStamp1.id.requireId()) // then resultActions @@ -1070,7 +1083,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { val deletedStamp = stampTestHelper.saveDeletedStamp(courseTrip, 3) // when - val resultActions = getResultActions(token, courseTrip.id, deletedStamp.id) + val resultActions = getResultActions(token, courseTrip.id.requireId(), deletedStamp.id.requireId()) // then resultActions @@ -1087,7 +1100,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { val completedStamp = stampTestHelper.saveCompletedStamp(courseTrip, 3) // when - val resultActions = getResultActions(token, courseTrip.id, completedStamp.id) + val resultActions = getResultActions(token, courseTrip.id.requireId(), completedStamp.id.requireId()) // then resultActions @@ -1105,7 +1118,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { missionTestHelper.saveCompletedMission(courseStamp1) // when - val resultActions = getResultActions(token, courseTrip.id, courseStamp1.id) + val resultActions = getResultActions(token, courseTrip.id.requireId(), courseStamp1.id.requireId()) // then resultActions @@ -1123,7 +1136,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { missionTestHelper.saveCompletedMission(courseStamp1) // when - val resultActions = getResultActions(token, courseTrip.id, courseStamp1.id) + val resultActions = getResultActions(token, courseTrip.id.requireId(), courseStamp1.id.requireId()) // then resultActions @@ -1149,7 +1162,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("인증되지 않은 사용자라면 401 Unauthorized를 반환한다.") fun shouldReturnUnauthorizedWhenUnauthenticated() { // when - val resultActions = getResultActions("", courseTrip.id) + val resultActions = getResultActions("", courseTrip.id.requireId()) // then resultActions @@ -1196,7 +1209,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("여행의 소유자가 아니라면 403 Forbidden을 반환한다.") fun shouldReturnForbiddenWhenMemberIsNotTripOwner() { // when - val resultActions = getResultActions(token, newTrip.id) + val resultActions = getResultActions(token, newTrip.id.requireId()) // then resultActions @@ -1213,7 +1226,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { val deletedTrip = tripTestHelper.saveDeletedTrip(member, TripCategory.COURSE) // when - val resultActions = getResultActions(token, deletedTrip.id) + val resultActions = getResultActions(token, deletedTrip.id.requireId()) // then resultActions @@ -1230,7 +1243,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { val completedTrip = tripTestHelper.saveCompletedTrip(member, TripCategory.COURSE) // when - val resultActions = getResultActions(token, completedTrip.id) + val resultActions = getResultActions(token, completedTrip.id.requireId()) // then resultActions @@ -1244,7 +1257,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("특정 스탬프의 미션 목록을 조회하고 반환한다.") fun shouldReturnStampsByTrip() { // when - val resultActions = getResultActions(token, courseTrip.id) + val resultActions = getResultActions(token, courseTrip.id.requireId()) // then resultActions @@ -1272,7 +1285,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("인증되지 않은 사용자라면 401 Unauthorized를 반환한다.") fun shouldReturnUnauthorizedWhenUnauthenticated() { // when - val resultActions = getResultActions("", courseTrip.id, courseStamp1.id) + val resultActions = getResultActions("", courseTrip.id.requireId(), courseStamp1.id.requireId()) // then resultActions @@ -1289,7 +1302,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { val tripId = "abc" // when - val resultActions = getResultActions(token, tripId, courseStamp1.id) + val resultActions = getResultActions(token, tripId, courseStamp1.id.requireId()) // then resultActions @@ -1306,7 +1319,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { val stampId = "abc" // when - val resultActions = getResultActions(token, courseTrip.id, stampId) + val resultActions = getResultActions(token, courseTrip.id.requireId(), stampId) // then resultActions @@ -1323,7 +1336,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { val tripId = -1L // when - val resultActions = getResultActions(token, tripId, courseStamp1.id) + val resultActions = getResultActions(token, tripId, courseStamp1.id.requireId()) // then resultActions @@ -1337,7 +1350,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("여행의 소유자가 아니라면 403 Forbidden을 반환한다.") fun shouldReturnForbiddenWhenMemberIsNotTripOwner() { // when - val resultActions = getResultActions(token, newTrip.id, courseStamp1.id) + val resultActions = getResultActions(token, newTrip.id.requireId(), courseStamp1.id.requireId()) // then resultActions @@ -1354,7 +1367,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { val deletedTrip = tripTestHelper.saveDeletedTrip(member, TripCategory.COURSE) // when - val resultActions = getResultActions(token, deletedTrip.id, courseStamp1.id) + val resultActions = getResultActions(token, deletedTrip.id.requireId(), courseStamp1.id.requireId()) // then resultActions @@ -1371,7 +1384,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { val completedTrip = tripTestHelper.saveCompletedTrip(member, TripCategory.COURSE) // when - val resultActions = getResultActions(token, completedTrip.id, courseStamp1.id) + val resultActions = getResultActions(token, completedTrip.id.requireId(), courseStamp1.id.requireId()) // then resultActions @@ -1388,7 +1401,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { val stampId = -1L // when - val resultActions = getResultActions(token, courseTrip.id, stampId) + val resultActions = getResultActions(token, courseTrip.id.requireId(), stampId) // then resultActions @@ -1402,7 +1415,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("스탬프가 요청한 여행에 속하지 않으면 403 Forbidden을 반환한다.") fun shouldReturnForbiddenWhenStampNotBelongToTrip() { // when - val resultActions = getResultActions(token, courseTrip.id, exploreStamp1.id) + val resultActions = getResultActions(token, courseTrip.id.requireId(), exploreStamp1.id.requireId()) // then resultActions @@ -1419,7 +1432,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { val deletedStamp = stampTestHelper.saveDeletedStamp(courseTrip, 3) // when - val resultActions = getResultActions(token, courseTrip.id, deletedStamp.id) + val resultActions = getResultActions(token, courseTrip.id.requireId(), deletedStamp.id.requireId()) // then resultActions @@ -1436,7 +1449,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { val completedStamp = stampTestHelper.saveCompletedStamp(courseTrip, 3) // when - val resultActions = getResultActions(token, courseTrip.id, completedStamp.id) + val resultActions = getResultActions(token, courseTrip.id.requireId(), completedStamp.id.requireId()) // then resultActions @@ -1450,7 +1463,7 @@ class StampControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("특정 스탬프를 상세 조회하고 반환한다.") fun shouldReturnStamp() { // when - val resultActions = getResultActions(token, courseTrip.id, courseStamp1.id) + val resultActions = getResultActions(token, courseTrip.id.requireId(), courseStamp1.id.requireId()) // then resultActions diff --git a/src/test/kotlin/com/ject/studytrip/studylog/application/service/StudyLogCommandServiceTest.kt b/src/test/kotlin/com/ject/studytrip/studylog/application/service/StudyLogCommandServiceTest.kt index 2f90793..64c1201 100644 --- a/src/test/kotlin/com/ject/studytrip/studylog/application/service/StudyLogCommandServiceTest.kt +++ b/src/test/kotlin/com/ject/studytrip/studylog/application/service/StudyLogCommandServiceTest.kt @@ -2,6 +2,7 @@ package com.ject.studytrip.studylog.application.service import com.ject.studytrip.BaseUnitTest import com.ject.studytrip.global.exception.CustomException +import com.ject.studytrip.global.util.EntityExtensions.requireId import com.ject.studytrip.member.domain.model.Member import com.ject.studytrip.member.fixture.MemberFixture import com.ject.studytrip.studylog.domain.error.StudyLogErrorCode @@ -205,7 +206,7 @@ class StudyLogCommandServiceTest : BaseUnitTest() { @DisplayName("특정 멤버가 소유한 학습 로그가 하나라도 존재하지 않으면 0을 반환한다.") fun shouldReturnZeroWhenStudyLogsOwnedByMemberDoNotExist() { // given - val memberId = member.id + val memberId = member.id.requireId() given(studyLogCommandRepository.deleteByMemberId(memberId)).willReturn(0L) // when @@ -219,7 +220,7 @@ class StudyLogCommandServiceTest : BaseUnitTest() { @DisplayName("특정 멤버가 소유한 학습 로그가 하나라도 존재하면 해당 개수를 반환한다.") fun shouldReturnCountWhenStudyLogsOwnedByMemberExist() { // given - val memberId = member.id + val memberId = member.id.requireId() given(studyLogCommandRepository.deleteByMemberId(memberId)).willReturn(5L) // when diff --git a/src/test/kotlin/com/ject/studytrip/studylog/application/service/StudyLogDailyMissionCommandServiceTest.kt b/src/test/kotlin/com/ject/studytrip/studylog/application/service/StudyLogDailyMissionCommandServiceTest.kt index 26fde1a..d3f1b81 100644 --- a/src/test/kotlin/com/ject/studytrip/studylog/application/service/StudyLogDailyMissionCommandServiceTest.kt +++ b/src/test/kotlin/com/ject/studytrip/studylog/application/service/StudyLogDailyMissionCommandServiceTest.kt @@ -1,6 +1,7 @@ package com.ject.studytrip.studylog.application.service import com.ject.studytrip.BaseUnitTest +import com.ject.studytrip.global.util.EntityExtensions.requireId import com.ject.studytrip.member.domain.model.Member import com.ject.studytrip.member.fixture.MemberFixture import com.ject.studytrip.mission.domain.model.DailyMission @@ -66,8 +67,7 @@ class StudyLogDailyMissionCommandServiceTest : BaseUnitTest() { fun shouldCreateAndReturnStudyLogDailyMissions() { // given val dailyMissions = listOf(dailyMission1, dailyMission2) - given(studyLogDailyMissionRepository.saveAll(anyList())) - .willAnswer { it.getArgument(0) } + given(studyLogDailyMissionRepository.saveAll(anyList())).willAnswer { it.getArgument(0) } // when val result = studyLogDailyMissionCommandService.createStudyLogDailyMissions(studyLog, dailyMissions) @@ -174,7 +174,7 @@ class StudyLogDailyMissionCommandServiceTest : BaseUnitTest() { @DisplayName("특정 멤버가 소유한 학습 로그 데일리 미션이 하나라도 존재하지 않으면 0을 반환한다.") fun shouldReturnZeroWhenStudyLogDailyMissionsOwnedByMemberDoNotExist() { // given - val memberId = member.id + val memberId = member.id.requireId() given(studyLogDailyMissionCommandRepository.deleteAllByMemberId(memberId)).willReturn(0L) // when @@ -188,7 +188,7 @@ class StudyLogDailyMissionCommandServiceTest : BaseUnitTest() { @DisplayName("특정 멤버가 소유한 학습 로그 데일리 미션이 하나라도 존재하면 해당 개수를 반환한다.") fun shouldReturnCountWhenStudyLogDailyMissionsOwnedByMemberExist() { // given - val memberId = member.id + val memberId = member.id.requireId() given(studyLogDailyMissionCommandRepository.deleteAllByMemberId(memberId)).willReturn(5L) // when diff --git a/src/test/kotlin/com/ject/studytrip/studylog/application/service/StudyLogDailyMissionQueryServiceTest.kt b/src/test/kotlin/com/ject/studytrip/studylog/application/service/StudyLogDailyMissionQueryServiceTest.kt index 86a7655..9d5da56 100644 --- a/src/test/kotlin/com/ject/studytrip/studylog/application/service/StudyLogDailyMissionQueryServiceTest.kt +++ b/src/test/kotlin/com/ject/studytrip/studylog/application/service/StudyLogDailyMissionQueryServiceTest.kt @@ -1,6 +1,7 @@ package com.ject.studytrip.studylog.application.service import com.ject.studytrip.BaseUnitTest +import com.ject.studytrip.global.util.EntityExtensions.requireId import com.ject.studytrip.member.fixture.MemberFixture import com.ject.studytrip.mission.fixture.DailyMissionFixture import com.ject.studytrip.mission.fixture.MissionFixture @@ -59,8 +60,8 @@ class StudyLogDailyMissionQueryServiceTest : BaseUnitTest() { @DisplayName("학습 로그 ID 목록으로 그룹화된 StudyLogDailyMission Map을 반환한다.") fun shouldReturnGroupedStudyLogDailyMissionMapByStudyLogIds() { // given - val studyLogId1 = studyLog1.id - val studyLogId2 = studyLog2.id + val studyLogId1 = studyLog1.id.requireId() + val studyLogId2 = studyLog2.id.requireId() val studyLogIds = listOf(studyLogId1, studyLogId2) val studyLogDailyMissionMap = mapOf( diff --git a/src/test/kotlin/com/ject/studytrip/studylog/application/service/StudyLogQueryServiceTest.kt b/src/test/kotlin/com/ject/studytrip/studylog/application/service/StudyLogQueryServiceTest.kt index 993d344..f303e0e 100644 --- a/src/test/kotlin/com/ject/studytrip/studylog/application/service/StudyLogQueryServiceTest.kt +++ b/src/test/kotlin/com/ject/studytrip/studylog/application/service/StudyLogQueryServiceTest.kt @@ -2,6 +2,7 @@ package com.ject.studytrip.studylog.application.service import com.ject.studytrip.BaseUnitTest import com.ject.studytrip.global.exception.CustomException +import com.ject.studytrip.global.util.EntityExtensions.requireId import com.ject.studytrip.member.domain.model.Member import com.ject.studytrip.member.fixture.MemberFixture import com.ject.studytrip.studylog.domain.error.StudyLogErrorCode @@ -72,7 +73,7 @@ class StudyLogQueryServiceTest : BaseUnitTest() { @DisplayName("특정 멤버에 대한 학습 기록이 존재하지 않으면 0을 반환한다.") fun shouldReturnZeroWhenStudyLogForMemberDoesNotExist() { // given - val memberId = member.id + val memberId = member.id.requireId() given(studyLogQueryRepository.countActiveStudyLogsByMemberId(memberId)).willReturn(0L) // when @@ -86,7 +87,7 @@ class StudyLogQueryServiceTest : BaseUnitTest() { @DisplayName("특정 멤버에 대한 학습 기록이 존재하면 그 개수를 반환한다.") fun shouldReturnCountWhenStudyLogForMemberExists() { // given - val memberId = member.id + val memberId = member.id.requireId() given(studyLogQueryRepository.countActiveStudyLogsByMemberId(memberId)).willReturn(2L) // when @@ -104,7 +105,7 @@ class StudyLogQueryServiceTest : BaseUnitTest() { @DisplayName("특정 여행에 대한 학습 로그 목록을 페이징 처리와 최신순으로 정렬하여 반환한다.") fun shouldReturnStudyLogsByTripIdPagedAndSortedByLatest() { // given - val tripId = courseTrip.id + val tripId = courseTrip.id.requireId() val order = "LATEST" val studyLogs = listOf(studyLog1, studyLog2) val mockSlice = SliceImpl(studyLogs, pageable, false) @@ -122,7 +123,7 @@ class StudyLogQueryServiceTest : BaseUnitTest() { @DisplayName("특정 여행에 대한 학습 로그 목록을 페이징 처리와 과거순으로 정렬하여 반환한다.") fun shouldReturnStudyLogsByTripIdPagedAndSortedByOldest() { // given - val tripId = courseTrip.id + val tripId = courseTrip.id.requireId() val order = "OLDEST" val studyLogs = listOf(studyLog2, studyLog1) // 과거순이므로 순서 반대 val mockSlice = SliceImpl(studyLogs, pageable, false) @@ -158,7 +159,7 @@ class StudyLogQueryServiceTest : BaseUnitTest() { @DisplayName("학습 로그가 이미 삭제되었다면 예외가 발생한다.") fun shouldThrowExceptionWhenStudyLogAlreadyDeleted() { // given - val studyLogId = studyLog1.id + val studyLogId = studyLog1.id.requireId() studyLog1.updateDeletedAt() given(studyLogRepository.findById(studyLogId)).willReturn(Optional.of(studyLog1)) @@ -173,7 +174,7 @@ class StudyLogQueryServiceTest : BaseUnitTest() { @DisplayName("학습 로그가 존재하면 학습 로그를 반환한다.") fun shouldReturnStudyLogWhenStudyLogExists() { // given - val studyLogId = studyLog1.id + val studyLogId = studyLog1.id.requireId() given(studyLogRepository.findById(studyLogId)).willReturn(Optional.of(studyLog1)) // when @@ -205,7 +206,7 @@ class StudyLogQueryServiceTest : BaseUnitTest() { @DisplayName("특정 여행에 대한 데일리 목표가 존재하지 않으면 0을 반환한다.") fun shouldReturnZeroWhenDailyGoalForTripDoesNotExist() { // given - val tripId = courseTrip.id + val tripId = courseTrip.id.requireId() dailyGoal.updateDeletedAt() given(studyLogQueryRepository.countStudyLogsByTripId(tripId)).willReturn(0L) @@ -220,7 +221,7 @@ class StudyLogQueryServiceTest : BaseUnitTest() { @DisplayName("특정 여행에 대한 학습 로그가 존재하면 해당 개수를 반환한다.") fun shouldReturnCountWhenStudyLogForTripExists() { // given - val tripId = courseTrip.id + val tripId = courseTrip.id.requireId() given(studyLogQueryRepository.countStudyLogsByTripId(tripId)).willReturn(2L) // when @@ -238,7 +239,7 @@ class StudyLogQueryServiceTest : BaseUnitTest() { @DisplayName("특정 여행 리포트에 대한 학습 로그 목록을 페이징 처리와 최신순으로 정렬하여 반환한다.") fun shouldReturnStudyLogsByTripReportIdPagedAndSortedByLatest() { // given - val tripReportId = tripReport.id + val tripReportId = tripReport.id.requireId() val studyLogs = listOf(studyLog1, studyLog2) val mockSlice = SliceImpl(studyLogs, pageable, false) given(studyLogQueryRepository.findSliceByTripReportIdOrderByCreatedAtDesc(tripReportId, pageable)).willReturn(mockSlice) @@ -273,9 +274,9 @@ class StudyLogQueryServiceTest : BaseUnitTest() { @DisplayName("학습 로그가 하나라도 존재하면 학습 로그 ID 목록을 반환한다.") fun shouldReturnStudyLogIdsWhenStudyLogExists() { // given - val tripId = courseTrip.id - val studyLogId1 = studyLog1.id - val studyLogId2 = studyLog2.id + val tripId = courseTrip.id.requireId() + val studyLogId1 = studyLog1.id.requireId() + val studyLogId2 = studyLog2.id.requireId() val studyLogIds = listOf(studyLogId1, studyLogId2) given(studyLogQueryRepository.findAllIdsByTripIdOrderByCreatedDesc(tripId)).willReturn(studyLogIds) @@ -295,7 +296,7 @@ class StudyLogQueryServiceTest : BaseUnitTest() { @DisplayName("이미지가 존재하지 않으면 빈 리스트를 반환한다.") fun shouldReturnEmptyListWhenImagesDoNotExist() { // given - val memberId = member.id + val memberId = member.id.requireId() given(studyLogQueryRepository.findImageUrlsByMemberId(memberId)).willReturn(emptyList()) // when @@ -309,7 +310,7 @@ class StudyLogQueryServiceTest : BaseUnitTest() { @DisplayName("이미지가 존재하면 학습 로그 이미지 URL 목록을 반환한다.") fun shouldReturnStudyLogImageUrlsWhenImagesExist() { // given - val memberId = member.id + val memberId = member.id.requireId() val imageUrls = listOf("https://cdn.example.com/studylogs/1.jpg", "https://cdn.example.com/studylogs/2.jpg") given(studyLogQueryRepository.findImageUrlsByMemberId(memberId)).willReturn(imageUrls) diff --git a/src/test/kotlin/com/ject/studytrip/studylog/fixture/ConfirmStudyLogImageRequestFixture.kt b/src/test/kotlin/com/ject/studytrip/studylog/fixture/ConfirmStudyLogImageRequestFixture.kt index 0c9f72b..7daf45b 100644 --- a/src/test/kotlin/com/ject/studytrip/studylog/fixture/ConfirmStudyLogImageRequestFixture.kt +++ b/src/test/kotlin/com/ject/studytrip/studylog/fixture/ConfirmStudyLogImageRequestFixture.kt @@ -2,10 +2,10 @@ package com.ject.studytrip.studylog.fixture import com.ject.studytrip.studylog.presentation.dto.request.ConfirmStudyLogImageRequest -class ConfirmStudyLogImageRequestFixture { - var tmpKey: String = "tmp/study-logs/1/test.jpg" - - fun withTmpKey(tmpKey: String): ConfirmStudyLogImageRequestFixture = apply { this.tmpKey = tmpKey } +class ConfirmStudyLogImageRequestFixture( + private val tmpKey: String = "tmp/study-logs/1/test.jpg", +) { + fun withTmpKey(tmpKey: String): ConfirmStudyLogImageRequestFixture = ConfirmStudyLogImageRequestFixture(tmpKey) fun build(): ConfirmStudyLogImageRequest = ConfirmStudyLogImageRequest(tmpKey) } diff --git a/src/test/kotlin/com/ject/studytrip/studylog/fixture/CreateStudyLogRequestFixture.kt b/src/test/kotlin/com/ject/studytrip/studylog/fixture/CreateStudyLogRequestFixture.kt index e0691f9..11961f8 100644 --- a/src/test/kotlin/com/ject/studytrip/studylog/fixture/CreateStudyLogRequestFixture.kt +++ b/src/test/kotlin/com/ject/studytrip/studylog/fixture/CreateStudyLogRequestFixture.kt @@ -2,19 +2,16 @@ package com.ject.studytrip.studylog.fixture import com.ject.studytrip.studylog.presentation.dto.request.CreateStudyLogRequest -class CreateStudyLogRequestFixture { - var totalFocusTimeInSeconds: Int = 60 - var selectedDailyMissionIds: List = emptyList() - var content: String = "TEST 학습 로그 내용" +class CreateStudyLogRequestFixture( + private val totalFocusTimeInSeconds: Int = 60, + private val selectedDailyMissionIds: List = emptyList(), + private val content: String = "TEST 학습 로그 내용", +) { + fun withTotalFocusTimeInMinutes(minutes: Int): CreateStudyLogRequestFixture = + CreateStudyLogRequestFixture(minutes * 60, selectedDailyMissionIds, content) - fun withTotalFocusTimeInMinutes(minutes: Int): CreateStudyLogRequestFixture = apply { this.totalFocusTimeInSeconds = minutes * 60 } + fun withSelectedDailyMissionIds(ids: List): CreateStudyLogRequestFixture = + CreateStudyLogRequestFixture(totalFocusTimeInSeconds, ids.toList(), content) - fun withSelectedDailyMissionIds(ids: List): CreateStudyLogRequestFixture = apply { this.selectedDailyMissionIds = ids.toList() } - - fun build(): CreateStudyLogRequest = - CreateStudyLogRequest( - totalFocusTimeInSeconds, - selectedDailyMissionIds, - content, - ) + fun build(): CreateStudyLogRequest = CreateStudyLogRequest(totalFocusTimeInSeconds, selectedDailyMissionIds, content) } diff --git a/src/test/kotlin/com/ject/studytrip/studylog/fixture/PresignStudyLogImageRequestFixture.kt b/src/test/kotlin/com/ject/studytrip/studylog/fixture/PresignStudyLogImageRequestFixture.kt index b73c7f9..5072945 100644 --- a/src/test/kotlin/com/ject/studytrip/studylog/fixture/PresignStudyLogImageRequestFixture.kt +++ b/src/test/kotlin/com/ject/studytrip/studylog/fixture/PresignStudyLogImageRequestFixture.kt @@ -2,10 +2,10 @@ package com.ject.studytrip.studylog.fixture import com.ject.studytrip.studylog.presentation.dto.request.PresignStudyLogImageRequest -class PresignStudyLogImageRequestFixture { - var originFilename: String = "test.jpg" - - fun withOriginFilename(originFilename: String): PresignStudyLogImageRequestFixture = apply { this.originFilename = originFilename } +class PresignStudyLogImageRequestFixture( + private val originFilename: String = "test.jpg", +) { + fun withOriginFilename(originFilename: String): PresignStudyLogImageRequestFixture = PresignStudyLogImageRequestFixture(originFilename) fun build(): PresignStudyLogImageRequest = PresignStudyLogImageRequest(originFilename) } diff --git a/src/test/kotlin/com/ject/studytrip/studylog/fixture/StudyLogFixture.kt b/src/test/kotlin/com/ject/studytrip/studylog/fixture/StudyLogFixture.kt index 29a270a..ca8a52e 100644 --- a/src/test/kotlin/com/ject/studytrip/studylog/fixture/StudyLogFixture.kt +++ b/src/test/kotlin/com/ject/studytrip/studylog/fixture/StudyLogFixture.kt @@ -9,9 +9,8 @@ import org.springframework.test.util.ReflectionTestUtils class StudyLogFixture( private val member: Member, private val dailyGoal: DailyGoal, + private val content: String = "TEST 학습 로그 내용", ) { - var content: String = "TEST 학습 로그 내용" - fun create(): StudyLog = StudyLogFactory.create(member, dailyGoal, content) fun createWithId(id: Long): StudyLog = diff --git a/src/test/kotlin/com/ject/studytrip/studylog/presentation/controller/StudyLogControllerIntegrationTest.kt b/src/test/kotlin/com/ject/studytrip/studylog/presentation/controller/StudyLogControllerIntegrationTest.kt index 2f6ea96..2da14ed 100644 --- a/src/test/kotlin/com/ject/studytrip/studylog/presentation/controller/StudyLogControllerIntegrationTest.kt +++ b/src/test/kotlin/com/ject/studytrip/studylog/presentation/controller/StudyLogControllerIntegrationTest.kt @@ -5,6 +5,7 @@ import com.ject.studytrip.auth.domain.error.AuthErrorCode import com.ject.studytrip.auth.fixture.TokenFixture import com.ject.studytrip.auth.helper.TokenTestHelper import com.ject.studytrip.global.exception.error.CommonErrorCode +import com.ject.studytrip.global.util.EntityExtensions.requireId import com.ject.studytrip.image.domain.error.ImageErrorCode import com.ject.studytrip.image.infra.s3.provider.S3ImageStorageProvider import com.ject.studytrip.member.domain.model.Member @@ -130,10 +131,10 @@ class StudyLogControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("인증되지 않은 사용자라면 401 Unauthorized를 반환한다.") fun shouldReturnUnauthorizedWhenUnauthenticated() { // given - val request = fixture.withSelectedDailyMissionIds(listOf(dailyMission.id)).build() + val request = fixture.withSelectedDailyMissionIds(listOf(dailyMission.id.requireId())).build() // when - val resultActions = getResultActions("", trip.id, dailyGoal.id, request) + val resultActions = getResultActions("", trip.id.requireId(), dailyGoal.id.requireId(), request) // then resultActions @@ -148,10 +149,10 @@ class StudyLogControllerIntegrationTest : BaseIntegrationTest() { fun shouldReturnBadRequestWhenTripIdTypeMismatch() { // given val tripId = "abc" - val request = fixture.withSelectedDailyMissionIds(listOf(dailyMission.id)).build() + val request = fixture.withSelectedDailyMissionIds(listOf(dailyMission.id.requireId())).build() // when - val resultActions = getResultActions(token, tripId, dailyGoal.id, request) + val resultActions = getResultActions(token, tripId, dailyGoal.id.requireId(), request) // then resultActions @@ -166,10 +167,10 @@ class StudyLogControllerIntegrationTest : BaseIntegrationTest() { fun shouldReturnBadRequestWhenDailyGoalIdTypeMismatch() { // given val dailyGoalId = "abc" - val request = fixture.withSelectedDailyMissionIds(listOf(dailyMission.id)).build() + val request = fixture.withSelectedDailyMissionIds(listOf(dailyMission.id.requireId())).build() // when - val resultActions = getResultActions(token, trip.id, dailyGoalId, request) + val resultActions = getResultActions(token, trip.id.requireId(), dailyGoalId, request) // then resultActions @@ -183,14 +184,10 @@ class StudyLogControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("요청이 유효하지 않으면 400 Bad Request를 반환한다.") fun shouldReturnBadRequestWhenRequestIsInvalid() { // given - val request = - fixture - .withTotalFocusTimeInMinutes(-30) - .withSelectedDailyMissionIds(listOf()) - .build() + val request = fixture.withTotalFocusTimeInMinutes(-30).withSelectedDailyMissionIds(listOf()).build() // when - val resultActions = getResultActions(token, trip.id, dailyGoal.id, request) + val resultActions = getResultActions(token, trip.id.requireId(), dailyGoal.id.requireId(), request) // then resultActions @@ -205,10 +202,10 @@ class StudyLogControllerIntegrationTest : BaseIntegrationTest() { fun shouldReturnNotFoundWhenTripDoesNotExist() { // given val tripId = -1L - val request = fixture.withSelectedDailyMissionIds(listOf(dailyMission.id)).build() + val request = fixture.withSelectedDailyMissionIds(listOf(dailyMission.id.requireId())).build() // when - val resultActions = getResultActions(token, tripId, dailyGoal.id, request) + val resultActions = getResultActions(token, tripId, dailyGoal.id.requireId(), request) // then resultActions @@ -224,10 +221,10 @@ class StudyLogControllerIntegrationTest : BaseIntegrationTest() { // given val newMember = memberTestHelper.saveNewMember("test@gmail.com", "test") val newTrip = tripTestHelper.saveTrip(newMember, TripCategory.COURSE) - val request = fixture.withSelectedDailyMissionIds(listOf(dailyMission.id)).build() + val request = fixture.withSelectedDailyMissionIds(listOf(dailyMission.id.requireId())).build() // when - val resultActions = getResultActions(token, newTrip.id, dailyGoal.id, request) + val resultActions = getResultActions(token, newTrip.id.requireId(), dailyGoal.id.requireId(), request) // then resultActions @@ -242,10 +239,10 @@ class StudyLogControllerIntegrationTest : BaseIntegrationTest() { fun shouldReturnBadRequestWhenTripAlreadyDeleted() { // given val deletedTrip = tripTestHelper.saveDeletedTrip(member, TripCategory.COURSE) - val request = fixture.withSelectedDailyMissionIds(listOf(dailyMission.id)).build() + val request = fixture.withSelectedDailyMissionIds(listOf(dailyMission.id.requireId())).build() // when - val resultActions = getResultActions(token, deletedTrip.id, dailyGoal.id, request) + val resultActions = getResultActions(token, deletedTrip.id.requireId(), dailyGoal.id.requireId(), request) // then resultActions @@ -260,10 +257,10 @@ class StudyLogControllerIntegrationTest : BaseIntegrationTest() { fun shouldReturnBadRequestWhenTripAlreadyCompleted() { // given val completedTrip = tripTestHelper.saveCompletedTrip(member, TripCategory.COURSE) - val request = fixture.withSelectedDailyMissionIds(listOf(dailyMission.id)).build() + val request = fixture.withSelectedDailyMissionIds(listOf(dailyMission.id.requireId())).build() // when - val resultActions = getResultActions(token, completedTrip.id, dailyGoal.id, request) + val resultActions = getResultActions(token, completedTrip.id.requireId(), dailyGoal.id.requireId(), request) // then resultActions @@ -278,10 +275,10 @@ class StudyLogControllerIntegrationTest : BaseIntegrationTest() { fun shouldReturnNotFoundWhenDailyGoalDoesNotExist() { // given val dailyGoalId = -1L - val request = fixture.withSelectedDailyMissionIds(listOf(dailyMission.id)).build() + val request = fixture.withSelectedDailyMissionIds(listOf(dailyMission.id.requireId())).build() // when - val resultActions = getResultActions(token, trip.id, dailyGoalId, request) + val resultActions = getResultActions(token, trip.id.requireId(), dailyGoalId, request) // then resultActions @@ -297,10 +294,10 @@ class StudyLogControllerIntegrationTest : BaseIntegrationTest() { // given val newTrip = tripTestHelper.saveTrip(member, TripCategory.COURSE) val newDailyGoal = dailyGoalTestHelper.saveDailyGoal(newTrip) - val request = fixture.withSelectedDailyMissionIds(listOf(dailyMission.id)).build() + val request = fixture.withSelectedDailyMissionIds(listOf(dailyMission.id.requireId())).build() // when - val resultActions = getResultActions(token, trip.id, newDailyGoal.id, request) + val resultActions = getResultActions(token, trip.id.requireId(), newDailyGoal.id.requireId(), request) // then resultActions @@ -315,10 +312,10 @@ class StudyLogControllerIntegrationTest : BaseIntegrationTest() { fun shouldReturnBadRequestWhenDailyGoalAlreadyDeleted() { // given val deletedDailyGoal = dailyGoalTestHelper.saveDeletedDailyGoal(trip) - val request = fixture.withSelectedDailyMissionIds(listOf(dailyMission.id)).build() + val request = fixture.withSelectedDailyMissionIds(listOf(dailyMission.id.requireId())).build() // when - val resultActions = getResultActions(token, trip.id, deletedDailyGoal.id, request) + val resultActions = getResultActions(token, trip.id.requireId(), deletedDailyGoal.id.requireId(), request) // then resultActions @@ -332,10 +329,10 @@ class StudyLogControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("선택한 데일리 미션 ID 목록 중 존재하지 않는 값이 포함되어 있다면 404 NotFound를 반환한다.") fun shouldReturnNotFoundWhenSelectedDailyMissionIdsContainInvalidId() { // given - val request = fixture.withSelectedDailyMissionIds(listOf(dailyMission.id, 1000L)).build() + val request = fixture.withSelectedDailyMissionIds(listOf(dailyMission.id.requireId(), 1000L)).build() // when - val resultActions = getResultActions(token, trip.id, dailyGoal.id, request) + val resultActions = getResultActions(token, trip.id.requireId(), dailyGoal.id.requireId(), request) // then resultActions @@ -350,10 +347,10 @@ class StudyLogControllerIntegrationTest : BaseIntegrationTest() { fun shouldReturnForbiddenWhenDailyMissionNotBelongToDailyGoal() { // given val newDailyGoal = dailyGoalTestHelper.saveDailyGoal(trip) - val request = fixture.withSelectedDailyMissionIds(listOf(dailyMission.id)).build() + val request = fixture.withSelectedDailyMissionIds(listOf(dailyMission.id.requireId())).build() // when - val resultActions = getResultActions(token, trip.id, newDailyGoal.id, request) + val resultActions = getResultActions(token, trip.id.requireId(), newDailyGoal.id.requireId(), request) // then resultActions @@ -368,10 +365,10 @@ class StudyLogControllerIntegrationTest : BaseIntegrationTest() { fun shouldReturnBadRequestWhenDailyMissionAlreadyDeleted() { // given val deletedDailyMission = dailyMissionTestHelper.saveDeletedDailyMission(mission, dailyGoal) - val request = fixture.withSelectedDailyMissionIds(listOf(deletedDailyMission.id)).build() + val request = fixture.withSelectedDailyMissionIds(listOf(deletedDailyMission.id.requireId())).build() // when - val resultActions = getResultActions(token, trip.id, dailyGoal.id, request) + val resultActions = getResultActions(token, trip.id.requireId(), dailyGoal.id.requireId(), request) // then resultActions @@ -387,10 +384,10 @@ class StudyLogControllerIntegrationTest : BaseIntegrationTest() { // given val newDailyGoal = dailyGoalTestHelper.saveDailyGoal(trip) val dailyMission = dailyMissionTestHelper.saveDailyMission(mission, newDailyGoal) - val request = fixture.withSelectedDailyMissionIds(listOf(dailyMission.id)).build() + val request = fixture.withSelectedDailyMissionIds(listOf(dailyMission.id.requireId())).build() // when - val resultActions = getResultActions(token, trip.id, newDailyGoal.id, request) + val resultActions = getResultActions(token, trip.id.requireId(), newDailyGoal.id.requireId(), request) // then resultActions @@ -407,10 +404,10 @@ class StudyLogControllerIntegrationTest : BaseIntegrationTest() { val newDailyGoal = dailyGoalTestHelper.saveDailyGoal(trip) val newDailyMission = dailyMissionTestHelper.saveDailyMission(mission, newDailyGoal) pomodoroTestHelper.saveDeletedPomodoro(newDailyGoal) - val request = fixture.withSelectedDailyMissionIds(listOf(newDailyMission.id)).build() + val request = fixture.withSelectedDailyMissionIds(listOf(newDailyMission.id.requireId())).build() // when - val resultActions = getResultActions(token, trip.id, newDailyGoal.id, request) + val resultActions = getResultActions(token, trip.id.requireId(), newDailyGoal.id.requireId(), request) // then resultActions @@ -426,10 +423,10 @@ class StudyLogControllerIntegrationTest : BaseIntegrationTest() { // given val deletedMission = missionTestHelper.saveDeletedMission(stamp) val newDailyMission = dailyMissionTestHelper.saveDailyMission(deletedMission, dailyGoal) - val request = fixture.withSelectedDailyMissionIds(listOf(newDailyMission.id)).build() + val request = fixture.withSelectedDailyMissionIds(listOf(newDailyMission.id.requireId())).build() // when - val resultActions = getResultActions(token, trip.id, dailyGoal.id, request) + val resultActions = getResultActions(token, trip.id.requireId(), dailyGoal.id.requireId(), request) // then resultActions @@ -445,10 +442,10 @@ class StudyLogControllerIntegrationTest : BaseIntegrationTest() { // given val completedMission = missionTestHelper.saveCompletedMission(stamp) val newDailyMission = dailyMissionTestHelper.saveDailyMission(completedMission, dailyGoal) - val request = fixture.withSelectedDailyMissionIds(listOf(newDailyMission.id)).build() + val request = fixture.withSelectedDailyMissionIds(listOf(newDailyMission.id.requireId())).build() // when - val resultActions = getResultActions(token, trip.id, dailyGoal.id, request) + val resultActions = getResultActions(token, trip.id.requireId(), dailyGoal.id.requireId(), request) // then resultActions @@ -462,11 +459,11 @@ class StudyLogControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("유효한 요청이 들어오면 학습 로그를 생성하고 반환한다.") fun shouldCreateAndReturnStudyLogWhenRequestIsValid() { // given - val request = fixture.withSelectedDailyMissionIds(listOf(dailyMission.id)).build() + val request = fixture.withSelectedDailyMissionIds(listOf(dailyMission.id.requireId())).build() val initialCompletedMissions = stamp.completedMissions // when - val resultActions = getResultActions(token, trip.id, dailyGoal.id, request) + val resultActions = getResultActions(token, trip.id.requireId(), dailyGoal.id.requireId(), request) // then resultActions @@ -475,7 +472,7 @@ class StudyLogControllerIntegrationTest : BaseIntegrationTest() { .andExpect(jsonPath("$.data.studyLogId").isNumber) // 스탬프의 완료된 미션 수 증가 확인 - val updatedStamp = stampTestHelper.getStamp(stamp.id) + val updatedStamp = stampTestHelper.getStamp(stamp.id.requireId()) assertThat(updatedStamp.completedMissions).isEqualTo(initialCompletedMissions + 1) } @@ -485,14 +482,11 @@ class StudyLogControllerIntegrationTest : BaseIntegrationTest() { // given val newMission = missionTestHelper.saveMission(stamp) val newDailyMission = dailyMissionTestHelper.saveDailyMission(newMission, dailyGoal) - val request = - fixture - .withSelectedDailyMissionIds(listOf(dailyMission.id, newDailyMission.id)) - .build() + val request = fixture.withSelectedDailyMissionIds(listOf(dailyMission.id.requireId(), newDailyMission.id.requireId())).build() val initialCompletedMissions = stamp.completedMissions // when - val resultActions = getResultActions(token, trip.id, dailyGoal.id, request) + val resultActions = getResultActions(token, trip.id.requireId(), dailyGoal.id.requireId(), request) // then resultActions @@ -501,7 +495,7 @@ class StudyLogControllerIntegrationTest : BaseIntegrationTest() { .andExpect(jsonPath("$.data.studyLogId").isNumber) // 스탬프의 완료된 미션 수 증가 확인 - val updatedStamp = stampTestHelper.getStamp(stamp.id) + val updatedStamp = stampTestHelper.getStamp(stamp.id.requireId()) assertThat(updatedStamp.completedMissions).isEqualTo(initialCompletedMissions + 2) } } @@ -528,7 +522,7 @@ class StudyLogControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("인증되지 않은 사용자라면 401 Unauthorized를 반환한다.") fun shouldReturnUnauthorizedWhenUnauthenticated() { // when - val resultActions = getResultActions("", trip.id, DEFAULT_PAGE, DEFAULT_SIZE, ORDER_LATEST) + val resultActions = getResultActions("", trip.id.requireId(), DEFAULT_PAGE, DEFAULT_SIZE, ORDER_LATEST) // then resultActions @@ -563,7 +557,7 @@ class StudyLogControllerIntegrationTest : BaseIntegrationTest() { val size = "abc" // when - val resultActions = getResultActions(token, trip.id, page, size, ORDER_LATEST) + val resultActions = getResultActions(token, trip.id.requireId(), page, size, ORDER_LATEST) // then resultActions @@ -581,7 +575,7 @@ class StudyLogControllerIntegrationTest : BaseIntegrationTest() { val size = "-1" // when - val resultActions = getResultActions(token, trip.id, page, size, ORDER_LATEST) + val resultActions = getResultActions(token, trip.id.requireId(), page, size, ORDER_LATEST) // then resultActions @@ -614,8 +608,9 @@ class StudyLogControllerIntegrationTest : BaseIntegrationTest() { // given val newMember = memberTestHelper.saveNewMember("test@gmail.com", "test") val newTrip = tripTestHelper.saveTrip(newMember, TripCategory.COURSE) + // when - val resultActions = getResultActions(token, newTrip.id, DEFAULT_PAGE, DEFAULT_SIZE, ORDER_LATEST) + val resultActions = getResultActions(token, newTrip.id.requireId(), DEFAULT_PAGE, DEFAULT_SIZE, ORDER_LATEST) // then resultActions @@ -632,7 +627,7 @@ class StudyLogControllerIntegrationTest : BaseIntegrationTest() { val deletedTrip = tripTestHelper.saveDeletedTrip(member, TripCategory.COURSE) // when - val resultActions = getResultActions(token, deletedTrip.id, DEFAULT_PAGE, DEFAULT_SIZE, ORDER_LATEST) + val resultActions = getResultActions(token, deletedTrip.id.requireId(), DEFAULT_PAGE, DEFAULT_SIZE, ORDER_LATEST) // then resultActions @@ -649,7 +644,7 @@ class StudyLogControllerIntegrationTest : BaseIntegrationTest() { val completedTrip = tripTestHelper.saveCompletedTrip(member, TripCategory.COURSE) // when - val resultActions = getResultActions(token, completedTrip.id, DEFAULT_PAGE, DEFAULT_SIZE, ORDER_LATEST) + val resultActions = getResultActions(token, completedTrip.id.requireId(), DEFAULT_PAGE, DEFAULT_SIZE, ORDER_LATEST) // then resultActions @@ -666,7 +661,7 @@ class StudyLogControllerIntegrationTest : BaseIntegrationTest() { val order = "INVALID" // when - val resultActions = getResultActions(token, trip.id, DEFAULT_PAGE, DEFAULT_SIZE, order) + val resultActions = getResultActions(token, trip.id.requireId(), DEFAULT_PAGE, DEFAULT_SIZE, order) // then resultActions @@ -683,7 +678,7 @@ class StudyLogControllerIntegrationTest : BaseIntegrationTest() { studyLogDailyMissionTestHelper.saveStudyLogDailyMissions(studyLog, dailyMission) // when - val resultActions = getResultActions(token, trip.id, DEFAULT_PAGE, DEFAULT_SIZE, ORDER_LATEST) + val resultActions = getResultActions(token, trip.id.requireId(), DEFAULT_PAGE, DEFAULT_SIZE, ORDER_LATEST) // then resultActions @@ -702,7 +697,7 @@ class StudyLogControllerIntegrationTest : BaseIntegrationTest() { studyLogDailyMissionTestHelper.saveStudyLogDailyMissions(studyLog, dailyMission) // when - val resultActions = getResultActions(token, trip.id, DEFAULT_PAGE, DEFAULT_SIZE, ORDER_OLDEST) + val resultActions = getResultActions(token, trip.id.requireId(), DEFAULT_PAGE, DEFAULT_SIZE, ORDER_OLDEST) // then resultActions @@ -739,7 +734,7 @@ class StudyLogControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions("", studyLog.id, request) + val resultActions = getResultActions("", studyLog.id.requireId(), request) // then resultActions @@ -774,7 +769,7 @@ class StudyLogControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.withOriginFilename("").build() // when - val resultActions = getResultActions(token, studyLog.id, request) + val resultActions = getResultActions(token, studyLog.id.requireId(), request) // then resultActions @@ -791,7 +786,7 @@ class StudyLogControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.withOriginFilename("test.pdf").build() // when - val resultActions = getResultActions(token, studyLog.id, request) + val resultActions = getResultActions(token, studyLog.id.requireId(), request) // then resultActions @@ -809,7 +804,7 @@ class StudyLogControllerIntegrationTest : BaseIntegrationTest() { given(s3ImageStorageProvider.issuePresignedUrl(anyString())).willReturn("https://mocked-presigned-url.com") // when - val resultActions = getResultActions(token, studyLog.id, request) + val resultActions = getResultActions(token, studyLog.id.requireId(), request) // then resultActions @@ -849,7 +844,7 @@ class StudyLogControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions("", studyLog.id, request) + val resultActions = getResultActions("", studyLog.id.requireId(), request) // then resultActions @@ -884,7 +879,7 @@ class StudyLogControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.withTmpKey("").build() // when - val resultActions = getResultActions(token, studyLog.id, request) + val resultActions = getResultActions(token, studyLog.id.requireId(), request) // then resultActions diff --git a/src/test/kotlin/com/ject/studytrip/trip/application/service/DailyGoalCommandServiceTest.kt b/src/test/kotlin/com/ject/studytrip/trip/application/service/DailyGoalCommandServiceTest.kt index 3a300bc..cf9ccb8 100644 --- a/src/test/kotlin/com/ject/studytrip/trip/application/service/DailyGoalCommandServiceTest.kt +++ b/src/test/kotlin/com/ject/studytrip/trip/application/service/DailyGoalCommandServiceTest.kt @@ -1,6 +1,7 @@ package com.ject.studytrip.trip.application.service import com.ject.studytrip.BaseUnitTest +import com.ject.studytrip.global.util.EntityExtensions.requireId import com.ject.studytrip.member.domain.model.Member import com.ject.studytrip.member.fixture.MemberFixture import com.ject.studytrip.trip.domain.model.DailyGoal @@ -142,7 +143,7 @@ class DailyGoalCommandServiceTest : BaseUnitTest() { @DisplayName("특정 멤버가 소유한 데일리 목표가 하나라도 존재하지 않으면 0을 반환한다.") fun shouldReturnZeroWhenDailyGoalsOwnedByMemberDoNotExist() { // given - val memberId = member.id + val memberId = member.id.requireId() given(dailyGoalCommandRepository.deleteAllByMemberId(memberId)).willReturn(0L) // when @@ -156,7 +157,7 @@ class DailyGoalCommandServiceTest : BaseUnitTest() { @DisplayName("특정 멤버가 소유한 데일리 목표가 하나라도 존재하면 해당 개수를 반환한다.") fun shouldReturnCountWhenDailyGoalsOwnedByMemberExist() { // given - val memberId = member.id + val memberId = member.id.requireId() given(dailyGoalCommandRepository.deleteAllByMemberId(memberId)).willReturn(5L) // when diff --git a/src/test/kotlin/com/ject/studytrip/trip/application/service/DailyGoalQueryServiceTest.kt b/src/test/kotlin/com/ject/studytrip/trip/application/service/DailyGoalQueryServiceTest.kt index 5d40e16..8e4945c 100644 --- a/src/test/kotlin/com/ject/studytrip/trip/application/service/DailyGoalQueryServiceTest.kt +++ b/src/test/kotlin/com/ject/studytrip/trip/application/service/DailyGoalQueryServiceTest.kt @@ -2,6 +2,7 @@ package com.ject.studytrip.trip.application.service import com.ject.studytrip.BaseUnitTest import com.ject.studytrip.global.exception.CustomException +import com.ject.studytrip.global.util.EntityExtensions.requireId import com.ject.studytrip.member.domain.model.Member import com.ject.studytrip.member.fixture.MemberFixture import com.ject.studytrip.trip.domain.error.DailyGoalErrorCode @@ -52,7 +53,7 @@ class DailyGoalQueryServiceTest : BaseUnitTest() { given(dailyGoalRepository.findById(dailyGoalId)).willReturn(Optional.empty()) // when - val exception = assertThrows { dailyGoalQueryService.getValidDailyGoal(trip.id, dailyGoalId) } + val exception = assertThrows { dailyGoalQueryService.getValidDailyGoal(trip.id.requireId(), dailyGoalId) } // then assertThat(exception.message).isEqualTo(DailyGoalErrorCode.DAILY_GOAL_NOT_FOUND.message) @@ -62,12 +63,12 @@ class DailyGoalQueryServiceTest : BaseUnitTest() { @DisplayName("특정 데일리 목표가 다른 여행에 속한다면 예외가 발생한다.") fun shouldThrowExceptionWhenDailyGoalNotBelongToTrip() { // given - val dailyGoalId = dailyGoal.id + val dailyGoalId = dailyGoal.id.requireId() val newTrip = TripFixture(member, TripCategory.COURSE).createWithId(2L) given(dailyGoalRepository.findById(dailyGoalId)).willReturn(Optional.of(dailyGoal)) // when - val exception = assertThrows { dailyGoalQueryService.getValidDailyGoal(newTrip.id, dailyGoalId) } + val exception = assertThrows { dailyGoalQueryService.getValidDailyGoal(newTrip.id.requireId(), dailyGoalId) } // then assertThat(exception.message).isEqualTo(DailyGoalErrorCode.DAILY_GOAL_NOT_BELONGS_TO_TRIP.message) @@ -77,12 +78,12 @@ class DailyGoalQueryServiceTest : BaseUnitTest() { @DisplayName("데일리 목표가 이미 삭제되었다면 예외가 발생한다.") fun shouldThrowExceptionWhenDailyGoalAlreadyDeleted() { // given - val dailyGoalId = dailyGoal.id + val dailyGoalId = dailyGoal.id.requireId() dailyGoal.updateDeletedAt() given(dailyGoalRepository.findById(dailyGoalId)).willReturn(Optional.of(dailyGoal)) // when - val exception = assertThrows { dailyGoalQueryService.getValidDailyGoal(trip.id, dailyGoalId) } + val exception = assertThrows { dailyGoalQueryService.getValidDailyGoal(trip.id.requireId(), dailyGoalId) } // then assertThat(exception.message).isEqualTo(DailyGoalErrorCode.DAILY_GOAL_ALREADY_DELETED.message) @@ -92,11 +93,11 @@ class DailyGoalQueryServiceTest : BaseUnitTest() { @DisplayName("특정 여행에 속한 데일리 목표가 존재하면 데일리 목표를 조회하고 반환한다.") fun shouldReturnDailyGoalWhenDailyGoalBelongsToTrip() { // given - val dailyGoalId = dailyGoal.id + val dailyGoalId = dailyGoal.id.requireId() given(dailyGoalRepository.findById(dailyGoalId)).willReturn(Optional.of(dailyGoal)) // when - val result = dailyGoalQueryService.getValidDailyGoal(trip.id, dailyGoalId) + val result = dailyGoalQueryService.getValidDailyGoal(trip.id.requireId(), dailyGoalId) // then assertThat(result).isEqualTo(dailyGoal) diff --git a/src/test/kotlin/com/ject/studytrip/trip/application/service/TripCommandServiceTest.kt b/src/test/kotlin/com/ject/studytrip/trip/application/service/TripCommandServiceTest.kt index db76c33..6b9ea1e 100644 --- a/src/test/kotlin/com/ject/studytrip/trip/application/service/TripCommandServiceTest.kt +++ b/src/test/kotlin/com/ject/studytrip/trip/application/service/TripCommandServiceTest.kt @@ -2,6 +2,7 @@ package com.ject.studytrip.trip.application.service import com.ject.studytrip.BaseUnitTest import com.ject.studytrip.global.exception.CustomException +import com.ject.studytrip.global.util.EntityExtensions.requireId import com.ject.studytrip.member.domain.model.Member import com.ject.studytrip.member.fixture.MemberFixture import com.ject.studytrip.trip.domain.error.TripErrorCode @@ -194,7 +195,7 @@ class TripCommandServiceTest : BaseUnitTest() { tripCommandService.completeTrip(courseTrip) // then - assertThat(courseTrip.isCompleted).isTrue + assertThat(courseTrip.isCompleted()).isTrue } } @@ -316,7 +317,7 @@ class TripCommandServiceTest : BaseUnitTest() { @DisplayName("특정 멤버가 소유한 여행이 하나라도 존재하지 않으면 0을 반환한다.") fun shouldReturnZeroWhenTripsOwnedByMemberDoNotExist() { // given - val memberId = member.id + val memberId = member.id.requireId() given(tripCommandRepository.deleteAllByMemberId(memberId)).willReturn(0L) // when @@ -330,7 +331,7 @@ class TripCommandServiceTest : BaseUnitTest() { @DisplayName("특정 멤버가 소유한 여행이 하나라도 존재하면 해당 개수를 반환한다.") fun shouldReturnCountWhenTripsOwnedByMemberExist() { // given - val memberId = member.id + val memberId = member.id.requireId() given(tripCommandRepository.deleteAllByMemberId(memberId)).willReturn(5L) // when diff --git a/src/test/kotlin/com/ject/studytrip/trip/application/service/TripQueryServiceTest.kt b/src/test/kotlin/com/ject/studytrip/trip/application/service/TripQueryServiceTest.kt index c8ffcb1..368c6ad 100644 --- a/src/test/kotlin/com/ject/studytrip/trip/application/service/TripQueryServiceTest.kt +++ b/src/test/kotlin/com/ject/studytrip/trip/application/service/TripQueryServiceTest.kt @@ -2,6 +2,7 @@ package com.ject.studytrip.trip.application.service import com.ject.studytrip.BaseUnitTest import com.ject.studytrip.global.exception.CustomException +import com.ject.studytrip.global.util.EntityExtensions.requireId import com.ject.studytrip.member.domain.model.Member import com.ject.studytrip.member.fixture.MemberFixture import com.ject.studytrip.trip.domain.error.TripErrorCode @@ -62,7 +63,7 @@ class TripQueryServiceTest : BaseUnitTest() { given(tripRepository.findById(tripId)).willReturn(Optional.empty()) // when - val exception = assertThrows { tripQueryService.getValidTrip(member.id, tripId) } + val exception = assertThrows { tripQueryService.getValidTrip(member.id.requireId(), tripId) } // then assertThat(exception.message).isEqualTo(TripErrorCode.TRIP_NOT_FOUND.message) @@ -73,7 +74,7 @@ class TripQueryServiceTest : BaseUnitTest() { fun shouldThrowExceptionWhenMemberIsNotTripOwner() { // given val memberId = -1L - val tripId = trip.id + val tripId = trip.id.requireId() given(tripRepository.findById(tripId)).willReturn(Optional.of(trip)) // when @@ -87,12 +88,12 @@ class TripQueryServiceTest : BaseUnitTest() { @DisplayName("여행이 이미 삭제되었다면 예외가 발생한다.") fun shouldThrowExceptionWhenTripAlreadyDeleted() { // given - val tripId = trip.id + val tripId = trip.id.requireId() trip.updateDeletedAt() given(tripRepository.findById(tripId)).willReturn(Optional.of(trip)) // when - val exception = assertThrows { tripQueryService.getValidTrip(member.id, tripId) } + val exception = assertThrows { tripQueryService.getValidTrip(member.id.requireId(), tripId) } // then assertThat(exception.message).isEqualTo(TripErrorCode.TRIP_ALREADY_DELETED.message) @@ -102,12 +103,12 @@ class TripQueryServiceTest : BaseUnitTest() { @DisplayName("여행이 이미 완료되었다면 예외가 발생한다.") fun shouldThrowExceptionWhenTripAlreadyCompleted() { // given - val tripId = trip.id + val tripId = trip.id.requireId() trip.updateCompleted() given(tripRepository.findById(tripId)).willReturn(Optional.of(trip)) // when - val exception = assertThrows { tripQueryService.getValidTrip(member.id, tripId) } + val exception = assertThrows { tripQueryService.getValidTrip(member.id.requireId(), tripId) } // then assertThat(exception.message).isEqualTo(TripErrorCode.TRIP_ALREADY_COMPLETED.message) @@ -117,11 +118,11 @@ class TripQueryServiceTest : BaseUnitTest() { @DisplayName("여행이 존재하면 여행을 반환한다.") fun shouldReturnTripWhenTripExists() { // given - val tripId = trip.id + val tripId = trip.id.requireId() given(tripRepository.findById(tripId)).willReturn(Optional.of(trip)) // when - val result = tripQueryService.getValidTrip(member.id, tripId) + val result = tripQueryService.getValidTrip(member.id.requireId(), tripId) // then assertThat(result).isEqualTo(trip) @@ -135,7 +136,7 @@ class TripQueryServiceTest : BaseUnitTest() { @DisplayName("특정 멤버에 대한 여행 목록을 페이징 처리하여 반환한다.") fun shouldReturnTripsSliceByMemberIdPaged() { // given - val memberId = member.id + val memberId = member.id.requireId() val trips = listOf(trip) val mockSlice = SliceImpl(trips, pageable, false) given(tripQueryRepository.findSliceByMemberIdAndCompletedFalseAndDeletedAtIsNull(memberId, pageable)).willReturn(mockSlice) @@ -156,7 +157,7 @@ class TripQueryServiceTest : BaseUnitTest() { @DisplayName("특정 멤버의 여행이 하나라도 존재하지 않으면 0을 반환한다.") fun shouldReturnZeroWhenTripDoesNotExistForMember() { // given - val memberId = member.id + val memberId = member.id.requireId() given(tripQueryRepository.countActiveTripsByMemberIdAndCategory(memberId, TripCategory.COURSE)).willReturn(0L) given(tripQueryRepository.countActiveTripsByMemberIdAndCategory(memberId, TripCategory.EXPLORE)).willReturn(0L) @@ -172,7 +173,7 @@ class TripQueryServiceTest : BaseUnitTest() { @DisplayName("특정 멤버의 코스형, 탐험형 여행 개수를 TripCount에 담아서 반환한다.") fun shouldReturnTripCountByMemberId() { // given - val memberId = member.id + val memberId = member.id.requireId() given(tripQueryRepository.countActiveTripsByMemberIdAndCategory(memberId, TripCategory.COURSE)).willReturn(3L) given(tripQueryRepository.countActiveTripsByMemberIdAndCategory(memberId, TripCategory.EXPLORE)).willReturn(2L) @@ -196,7 +197,7 @@ class TripQueryServiceTest : BaseUnitTest() { given(tripRepository.findById(tripId)).willReturn(Optional.empty()) // when - val exception = assertThrows { tripQueryService.getValidCompletedTrip(member.id, tripId) } + val exception = assertThrows { tripQueryService.getValidCompletedTrip(member.id.requireId(), tripId) } // then assertThat(exception.message).isEqualTo(TripErrorCode.TRIP_NOT_FOUND.message) @@ -207,7 +208,7 @@ class TripQueryServiceTest : BaseUnitTest() { fun shouldThrowExceptionWhenMemberIsNotTripOwner() { // given val memberId = -1L - val tripId = trip.id + val tripId = trip.id.requireId() given(tripRepository.findById(tripId)).willReturn(Optional.of(trip)) // when @@ -221,12 +222,12 @@ class TripQueryServiceTest : BaseUnitTest() { @DisplayName("여행이 이미 삭제되었다면 예외가 발생한다.") fun shouldThrowExceptionWhenTripAlreadyDeleted() { // given - val tripId = trip.id + val tripId = trip.id.requireId() trip.updateDeletedAt() given(tripRepository.findById(tripId)).willReturn(Optional.of(trip)) // when - val exception = assertThrows { tripQueryService.getValidCompletedTrip(member.id, tripId) } + val exception = assertThrows { tripQueryService.getValidCompletedTrip(member.id.requireId(), tripId) } // then assertThat(exception.message).isEqualTo(TripErrorCode.TRIP_ALREADY_DELETED.message) @@ -236,11 +237,11 @@ class TripQueryServiceTest : BaseUnitTest() { @DisplayName("여행이 아직 완료되지 않았다면 예외가 발생한다.") fun shouldThrowExceptionWhenTripIsNotCompleted() { // given - val tripId = trip.id + val tripId = trip.id.requireId() given(tripRepository.findById(tripId)).willReturn(Optional.of(trip)) // when - val exception = assertThrows { tripQueryService.getValidCompletedTrip(member.id, tripId) } + val exception = assertThrows { tripQueryService.getValidCompletedTrip(member.id.requireId(), tripId) } // then assertThat(exception.message).isEqualTo(TripErrorCode.TRIP_NOT_COMPLETED.message) @@ -250,12 +251,12 @@ class TripQueryServiceTest : BaseUnitTest() { @DisplayName("여행이 이미 완료되었다면 여행을 반환한다.") fun shouldReturnTripWhenTripAlreadyCompleted() { // given - val tripId = trip.id + val tripId = trip.id.requireId() trip.updateCompleted() given(tripRepository.findById(tripId)).willReturn(Optional.of(trip)) // when - val result = tripQueryService.getValidCompletedTrip(member.id, tripId) + val result = tripQueryService.getValidCompletedTrip(member.id.requireId(), tripId) // then assertThat(result).isEqualTo(trip) diff --git a/src/test/kotlin/com/ject/studytrip/trip/application/service/TripReportCommandServiceTest.kt b/src/test/kotlin/com/ject/studytrip/trip/application/service/TripReportCommandServiceTest.kt index d0808b6..23c0d0e 100644 --- a/src/test/kotlin/com/ject/studytrip/trip/application/service/TripReportCommandServiceTest.kt +++ b/src/test/kotlin/com/ject/studytrip/trip/application/service/TripReportCommandServiceTest.kt @@ -1,6 +1,7 @@ package com.ject.studytrip.trip.application.service import com.ject.studytrip.BaseUnitTest +import com.ject.studytrip.global.util.EntityExtensions.requireId import com.ject.studytrip.member.domain.model.Member import com.ject.studytrip.member.fixture.MemberFixture import com.ject.studytrip.studylog.fixture.StudyLogFixture @@ -44,7 +45,7 @@ class TripReportCommandServiceTest : BaseUnitTest() { val dailyGoal = DailyGoalFixture(trip).create() val studyLog1 = StudyLogFixture(member, dailyGoal).createWithId(1L) val studyLog2 = StudyLogFixture(member, dailyGoal).createWithId(2L) - studyLogIds = listOf(studyLog1.id, studyLog2.id) + studyLogIds = listOf(studyLog1.id.requireId(), studyLog2.id.requireId()) tripReport = TripReportFixture(member).create() } @@ -183,7 +184,7 @@ class TripReportCommandServiceTest : BaseUnitTest() { @DisplayName("특정 멤버가 소유한 여행 리포트가 하나라도 존재하면 해당 개수를 반환한다.") fun shouldReturnCountWhenTripReportsOwnedByMemberExist() { // given - val memberId = member.id + val memberId = member.id.requireId() given(tripReportCommandRepository.deleteAllByMemberId(memberId)).willReturn(5L) // when diff --git a/src/test/kotlin/com/ject/studytrip/trip/application/service/TripReportQueryServiceTest.kt b/src/test/kotlin/com/ject/studytrip/trip/application/service/TripReportQueryServiceTest.kt index f180b3b..b48a8db 100644 --- a/src/test/kotlin/com/ject/studytrip/trip/application/service/TripReportQueryServiceTest.kt +++ b/src/test/kotlin/com/ject/studytrip/trip/application/service/TripReportQueryServiceTest.kt @@ -2,6 +2,7 @@ package com.ject.studytrip.trip.application.service import com.ject.studytrip.BaseUnitTest import com.ject.studytrip.global.exception.CustomException +import com.ject.studytrip.global.util.EntityExtensions.requireId import com.ject.studytrip.member.domain.model.Member import com.ject.studytrip.member.fixture.MemberFixture import com.ject.studytrip.trip.domain.error.TripReportErrorCode @@ -65,7 +66,7 @@ class TripReportQueryServiceTest : BaseUnitTest() { @DisplayName("여행 리포트가 존재하면 여행을 반환한다.") fun shouldReturnTripWhenTripReportExists() { // given - val tripReportId = tripReport1.id + val tripReportId = tripReport1.id.requireId() given(tripReportRepository.findById(tripReportId)).willReturn(Optional.of(tripReport1)) // when @@ -87,7 +88,7 @@ class TripReportQueryServiceTest : BaseUnitTest() { given(tripReportRepository.findById(tripReportId)).willReturn(Optional.empty()) // when - val exception = assertThrows { tripReportQueryService.getValidTripReport(member.id, tripReportId) } + val exception = assertThrows { tripReportQueryService.getValidTripReport(member.id.requireId(), tripReportId) } // then assertThat(exception.message).isEqualTo(TripReportErrorCode.TRIP_REPORT_NOT_FOUND.message) @@ -98,7 +99,7 @@ class TripReportQueryServiceTest : BaseUnitTest() { fun shouldThrowExceptionWhenMemberIsNotTripReportOwner() { // given val memberId = -1L - val tripReportId = tripReport1.id + val tripReportId = tripReport1.id.requireId() given(tripReportRepository.findById(tripReportId)).willReturn(Optional.of(tripReport1)) // when @@ -112,12 +113,12 @@ class TripReportQueryServiceTest : BaseUnitTest() { @DisplayName("여행 리포트가 이미 삭제되었다면 예외가 발생한다.") fun shouldThrowExceptionWhenTripReportAlreadyDeleted() { // given - val tripReportId = tripReport1.id + val tripReportId = tripReport1.id.requireId() tripReport1.updateDeletedAt() given(tripReportRepository.findById(tripReportId)).willReturn(Optional.of(tripReport1)) // when - val exception = assertThrows { tripReportQueryService.getValidTripReport(member.id, tripReportId) } + val exception = assertThrows { tripReportQueryService.getValidTripReport(member.id.requireId(), tripReportId) } // then assertThat(exception.message).isEqualTo(TripReportErrorCode.TRIP_REPORT_ALREADY_DELETED.message) @@ -127,11 +128,11 @@ class TripReportQueryServiceTest : BaseUnitTest() { @DisplayName("여행 리포트가 존재하면 여행을 반환한다.") fun shouldReturnTripWhenTripReportExists() { // given - val tripReportId = tripReport1.id + val tripReportId = tripReport1.id.requireId() given(tripReportRepository.findById(tripReportId)).willReturn(Optional.of(tripReport1)) // when - val result = tripReportQueryService.getValidTripReport(member.id, tripReportId) + val result = tripReportQueryService.getValidTripReport(member.id.requireId(), tripReportId) // then assertThat(result).isEqualTo(tripReport1) @@ -145,7 +146,7 @@ class TripReportQueryServiceTest : BaseUnitTest() { @DisplayName("여행 리포트가 존재하지 않으면 빈 리스트를 반환한다.") fun shouldReturnEmptyListWhenTripReportDoesNotExist() { // given - val memberId = member.id + val memberId = member.id.requireId() given(tripReportQueryRepository.findAllActiveByMemberId(memberId)).willReturn(emptyList()) // when @@ -159,7 +160,7 @@ class TripReportQueryServiceTest : BaseUnitTest() { @DisplayName("여행 리포트가 하나라도 존재하면 특정 멤버가 생성한 여행 리포트 목록을 반환한다.") fun shouldReturnTripReports() { // given - val memberId = member.id + val memberId = member.id.requireId() given(tripReportQueryRepository.findAllActiveByMemberId(memberId)).willReturn(tripReports) // when @@ -178,7 +179,7 @@ class TripReportQueryServiceTest : BaseUnitTest() { @DisplayName("이미지가 존재하지 않으면 빈 리스트를 반환한다.") fun shouldReturnEmptyListWhenImagesDoNotExist() { // given - val memberId = member.id + val memberId = member.id.requireId() given(tripReportQueryRepository.findImageUrlsByMemberId(memberId)).willReturn(emptyList()) // when @@ -192,7 +193,7 @@ class TripReportQueryServiceTest : BaseUnitTest() { @DisplayName("이미지가 존재하면 여행 리포트 이미지 URL 목록을 반환한다.") fun shouldReturnTripReportImageUrlsWhenImagesExist() { // given - val memberId = member.id + val memberId = member.id.requireId() val imageUrls = listOf("https://cdn.example.com/reports/1.jpg", "https://cdn.example.com/reports/2.jpg") given(tripReportQueryRepository.findImageUrlsByMemberId(memberId)).willReturn(imageUrls) diff --git a/src/test/kotlin/com/ject/studytrip/trip/application/service/TripReportStudyLogCommandServiceTest.kt b/src/test/kotlin/com/ject/studytrip/trip/application/service/TripReportStudyLogCommandServiceTest.kt index 0b357c9..c7ae836 100644 --- a/src/test/kotlin/com/ject/studytrip/trip/application/service/TripReportStudyLogCommandServiceTest.kt +++ b/src/test/kotlin/com/ject/studytrip/trip/application/service/TripReportStudyLogCommandServiceTest.kt @@ -1,6 +1,7 @@ package com.ject.studytrip.trip.application.service import com.ject.studytrip.BaseUnitTest +import com.ject.studytrip.global.util.EntityExtensions.requireId import com.ject.studytrip.member.domain.model.Member import com.ject.studytrip.member.fixture.MemberFixture import com.ject.studytrip.studylog.domain.model.StudyLog @@ -100,7 +101,7 @@ class TripReportStudyLogCommandServiceTest : BaseUnitTest() { @DisplayName("특정 멤버가 소유한 여행 리포트 학습 로그가 하나라도 존재하지 않으면 0을 반환한다.") fun shouldReturnZeroWhenTripReportStudyLogsOwnedByMemberDoNotExist() { // given - val memberId = member.id + val memberId = member.id.requireId() given(tripReportStudyLogCommandRepository.deleteAllByMemberId(memberId)).willReturn(0L) // when @@ -114,7 +115,7 @@ class TripReportStudyLogCommandServiceTest : BaseUnitTest() { @DisplayName("특정 멤버가 소유한 여행 리포트 학습 로그가 하나라도 존재하면 해당 개수를 반환한다.") fun shouldReturnCountWhenTripReportStudyLogsOwnedByMemberExist() { // given - val memberId = member.id + val memberId = member.id.requireId() given(tripReportStudyLogCommandRepository.deleteAllByMemberId(memberId)).willReturn(5L) // when diff --git a/src/test/kotlin/com/ject/studytrip/trip/fixture/ConfirmTripReportImageRequestFixture.kt b/src/test/kotlin/com/ject/studytrip/trip/fixture/ConfirmTripReportImageRequestFixture.kt index ae718f3..98ef530 100644 --- a/src/test/kotlin/com/ject/studytrip/trip/fixture/ConfirmTripReportImageRequestFixture.kt +++ b/src/test/kotlin/com/ject/studytrip/trip/fixture/ConfirmTripReportImageRequestFixture.kt @@ -2,10 +2,10 @@ package com.ject.studytrip.trip.fixture import com.ject.studytrip.trip.presentation.dto.request.ConfirmTripReportImageRequest -class ConfirmTripReportImageRequestFixture { - var tmpKey: String = "tmp/trip-reports/1/test.jpg" - - fun withTmpKey(tmpKey: String): ConfirmTripReportImageRequestFixture = apply { this.tmpKey = tmpKey } +class ConfirmTripReportImageRequestFixture( + private val tmpKey: String = "tmp/trip-reports/1/test.jpg", +) { + fun withTmpKey(tmpKey: String): ConfirmTripReportImageRequestFixture = ConfirmTripReportImageRequestFixture(tmpKey) fun build(): ConfirmTripReportImageRequest = ConfirmTripReportImageRequest(tmpKey) } diff --git a/src/test/kotlin/com/ject/studytrip/trip/fixture/CreateDailyGoalRequestFixture.kt b/src/test/kotlin/com/ject/studytrip/trip/fixture/CreateDailyGoalRequestFixture.kt index 586023d..4c3ee77 100644 --- a/src/test/kotlin/com/ject/studytrip/trip/fixture/CreateDailyGoalRequestFixture.kt +++ b/src/test/kotlin/com/ject/studytrip/trip/fixture/CreateDailyGoalRequestFixture.kt @@ -3,13 +3,13 @@ package com.ject.studytrip.trip.fixture import com.ject.studytrip.pomodoro.presentation.dto.request.CreatePomodoroRequest import com.ject.studytrip.trip.presentation.dto.request.CreateDailyGoalRequest -class CreateDailyGoalRequestFixture { - var pomodoro: CreatePomodoroRequest = CreatePomodoroRequest(30, 1) - var missionIds: List = emptyList() +class CreateDailyGoalRequestFixture( + private val pomodoro: CreatePomodoroRequest = CreatePomodoroRequest(30, 1), + private val missionIds: List = emptyList(), +) { + fun withPomodoro(pomodoro: CreatePomodoroRequest): CreateDailyGoalRequestFixture = CreateDailyGoalRequestFixture(pomodoro, missionIds) - fun withPomodoro(pomodoro: CreatePomodoroRequest): CreateDailyGoalRequestFixture = apply { this.pomodoro = pomodoro } - - fun withMissionIds(missionIds: List): CreateDailyGoalRequestFixture = apply { this.missionIds = missionIds } + fun withMissionIds(missionIds: List): CreateDailyGoalRequestFixture = CreateDailyGoalRequestFixture(pomodoro, missionIds) fun build(): CreateDailyGoalRequest = CreateDailyGoalRequest(pomodoro, missionIds) } diff --git a/src/test/kotlin/com/ject/studytrip/trip/fixture/CreateTripReportRequestFixture.kt b/src/test/kotlin/com/ject/studytrip/trip/fixture/CreateTripReportRequestFixture.kt index 10da1b3..8e2d8af 100644 --- a/src/test/kotlin/com/ject/studytrip/trip/fixture/CreateTripReportRequestFixture.kt +++ b/src/test/kotlin/com/ject/studytrip/trip/fixture/CreateTripReportRequestFixture.kt @@ -2,18 +2,29 @@ package com.ject.studytrip.trip.fixture import com.ject.studytrip.trip.presentation.dto.request.CreateTripReportRequest -class CreateTripReportRequestFixture { - var title: String = "TEST 여행 리포트 제목" - var content: String = "TEST 여행 리포트 내용" - var startDate: String = "2018.01.01" - var endDate: String = "2018.01.31" - var studyLogCount: Long = 10L - var totalFocusHours: Long = 100L - var studyDays: Long = 10L - var imageTitle: String = "TEST 여행 리포트 이미지 제목" - var studyLogIds: List = emptyList() - - fun withStudyLogIds(studyLogIds: List): CreateTripReportRequestFixture = apply { this.studyLogIds = studyLogIds } +class CreateTripReportRequestFixture( + private val title: String = "TEST 여행 리포트 제목", + private val content: String = "TEST 여행 리포트 내용", + private val startDate: String = "2018.01.01", + private val endDate: String = "2018.01.31", + private val studyLogCount: Long = 10L, + private val totalFocusHours: Long = 100L, + private val studyDays: Long = 10L, + private val imageTitle: String = "TEST 여행 리포트 이미지 제목", + private val studyLogIds: List = emptyList(), +) { + fun withStudyLogIds(studyLogIds: List): CreateTripReportRequestFixture = + CreateTripReportRequestFixture( + title, + content, + startDate, + endDate, + studyLogCount, + totalFocusHours, + studyDays, + imageTitle, + studyLogIds, + ) fun build(): CreateTripReportRequest = CreateTripReportRequest( diff --git a/src/test/kotlin/com/ject/studytrip/trip/fixture/CreateTripRequestFixture.kt b/src/test/kotlin/com/ject/studytrip/trip/fixture/CreateTripRequestFixture.kt index 0c29c24..bc5fdfe 100644 --- a/src/test/kotlin/com/ject/studytrip/trip/fixture/CreateTripRequestFixture.kt +++ b/src/test/kotlin/com/ject/studytrip/trip/fixture/CreateTripRequestFixture.kt @@ -5,16 +5,16 @@ import com.ject.studytrip.trip.domain.model.TripCategory import com.ject.studytrip.trip.presentation.dto.request.CreateTripRequest import java.time.LocalDate -class CreateTripRequestFixture { - var name: String = "TEST 여행 이름" - var memo: String = "TEST 여행 메모" - var category: String = TripCategory.COURSE.name - var endDate: LocalDate? = LocalDate.now().plusDays(10) - var stamps: List = listOf(CreateStampRequest("TEST 스탬프 이름", LocalDate.now().plusDays(5))) +class CreateTripRequestFixture( + private val name: String = "TEST 여행 이름", + private val memo: String = "TEST 여행 메모", + private val category: String = TripCategory.COURSE.name, + private val endDate: LocalDate? = LocalDate.now().plusDays(10), + private val stamps: List = listOf(CreateStampRequest("TEST 스탬프 이름", LocalDate.now().plusDays(5))), +) { + fun withCategory(category: String): CreateTripRequestFixture = CreateTripRequestFixture(name, memo, category, endDate, stamps) - fun withCategory(category: String): CreateTripRequestFixture = apply { this.category = category } - - fun withEndDate(endDate: LocalDate?): CreateTripRequestFixture = apply { this.endDate = endDate } + fun withEndDate(endDate: LocalDate?): CreateTripRequestFixture = CreateTripRequestFixture(name, memo, category, endDate, stamps) fun build(): CreateTripRequest = CreateTripRequest(name, memo, category, endDate, stamps) } diff --git a/src/test/kotlin/com/ject/studytrip/trip/fixture/DailyGoalFixture.kt b/src/test/kotlin/com/ject/studytrip/trip/fixture/DailyGoalFixture.kt index 3fb57de..96a7154 100644 --- a/src/test/kotlin/com/ject/studytrip/trip/fixture/DailyGoalFixture.kt +++ b/src/test/kotlin/com/ject/studytrip/trip/fixture/DailyGoalFixture.kt @@ -7,9 +7,8 @@ import org.springframework.test.util.ReflectionTestUtils class DailyGoalFixture( private val trip: Trip, + private val title: String = "TEST 데일리 목표 제목", ) { - var title: String = "TEST 데일리 목표 제목" - fun create(): DailyGoal = DailyGoalFactory.create(trip, title) fun createWithId(id: Long): DailyGoal = diff --git a/src/test/kotlin/com/ject/studytrip/trip/fixture/PresignTripReportImageRequestFixture.kt b/src/test/kotlin/com/ject/studytrip/trip/fixture/PresignTripReportImageRequestFixture.kt index 4baf9df..bb2920b 100644 --- a/src/test/kotlin/com/ject/studytrip/trip/fixture/PresignTripReportImageRequestFixture.kt +++ b/src/test/kotlin/com/ject/studytrip/trip/fixture/PresignTripReportImageRequestFixture.kt @@ -2,10 +2,11 @@ package com.ject.studytrip.trip.fixture import com.ject.studytrip.trip.presentation.dto.request.PresignTripReportImageRequest -class PresignTripReportImageRequestFixture { - var originFilename: String = "test.jpg" - - fun withOriginFilename(originFilename: String): PresignTripReportImageRequestFixture = apply { this.originFilename = originFilename } +class PresignTripReportImageRequestFixture( + private val originFilename: String = "test.jpg", +) { + fun withOriginFilename(originFilename: String): PresignTripReportImageRequestFixture = + PresignTripReportImageRequestFixture(originFilename) fun build(): PresignTripReportImageRequest = PresignTripReportImageRequest(originFilename) } diff --git a/src/test/kotlin/com/ject/studytrip/trip/fixture/TripFixture.kt b/src/test/kotlin/com/ject/studytrip/trip/fixture/TripFixture.kt index 74bae7a..375b120 100644 --- a/src/test/kotlin/com/ject/studytrip/trip/fixture/TripFixture.kt +++ b/src/test/kotlin/com/ject/studytrip/trip/fixture/TripFixture.kt @@ -10,12 +10,11 @@ import java.time.LocalDate class TripFixture( private val member: Member, private val category: TripCategory, + private val name: String = "TEST 여행 이름", + private val memo: String = "TEST 여행 메모", + private val endDate: LocalDate = LocalDate.now().plusDays(7), + private val totalStamps: Int = 1, ) { - var name: String = "TEST 여행 이름" - var memo: String = "TEST 여행 메모" - var endDate: LocalDate = LocalDate.now().plusDays(7) - var totalStamps: Int = 1 - fun create(): Trip = TripFactory.create(member, name, memo, category, endDate, totalStamps) fun createWithId(id: Long): Trip = diff --git a/src/test/kotlin/com/ject/studytrip/trip/fixture/TripReportFixture.kt b/src/test/kotlin/com/ject/studytrip/trip/fixture/TripReportFixture.kt index 2f6a160..40a6078 100644 --- a/src/test/kotlin/com/ject/studytrip/trip/fixture/TripReportFixture.kt +++ b/src/test/kotlin/com/ject/studytrip/trip/fixture/TripReportFixture.kt @@ -7,16 +7,15 @@ import org.springframework.test.util.ReflectionTestUtils class TripReportFixture( private val member: Member, + private val title: String = "TEST 여행 리포트 제목", + private val content: String = "TEST 여행 리포트 내용", + private val startDate: String = "2018.01.01", + private val endDate: String = "2018.01.31", + private val studyLogCount: Long = 10L, + private val totalFocusHours: Long = 100L, + private val studyDays: Long = 10L, + private val imageTitle: String = "TEST 여행 리포트 이미지 제목", ) { - var title: String = "TEST 여행 리포트 제목" - var content: String = "TEST 여행 리포트 내용" - var startDate: String = "2018.01.01" - var endDate: String = "2018.01.31" - var studyLogCount: Long = 10L - var totalFocusHours: Long = 100L - var studyDays: Long = 10L - var imageTitle: String = "TEST 여행 리포트 이미지 제목" - fun create(): TripReport = TripReportFactory.create(member, title, content, startDate, endDate, studyLogCount, totalFocusHours, studyDays, imageTitle) diff --git a/src/test/kotlin/com/ject/studytrip/trip/fixture/UpdateDailyGoalRequestFixture.kt b/src/test/kotlin/com/ject/studytrip/trip/fixture/UpdateDailyGoalRequestFixture.kt index 92dee22..f247f63 100644 --- a/src/test/kotlin/com/ject/studytrip/trip/fixture/UpdateDailyGoalRequestFixture.kt +++ b/src/test/kotlin/com/ject/studytrip/trip/fixture/UpdateDailyGoalRequestFixture.kt @@ -2,14 +2,15 @@ package com.ject.studytrip.trip.fixture import com.ject.studytrip.trip.presentation.dto.request.UpdateDailyGoalRequest -class UpdateDailyGoalRequestFixture { - var deleteDailyMissionIds: List = emptyList() - var addMissionIds: List = emptyList() - +class UpdateDailyGoalRequestFixture( + private val deleteDailyMissionIds: List = emptyList(), + private val addMissionIds: List = emptyList(), +) { fun withDeleteDailyMissionIds(deleteDailyMissionIds: List): UpdateDailyGoalRequestFixture = - apply { this.deleteDailyMissionIds = deleteDailyMissionIds } + UpdateDailyGoalRequestFixture(deleteDailyMissionIds, addMissionIds) - fun withAddMissionIds(addMissionIds: List): UpdateDailyGoalRequestFixture = apply { this.addMissionIds = addMissionIds } + fun withAddMissionIds(addMissionIds: List): UpdateDailyGoalRequestFixture = + UpdateDailyGoalRequestFixture(deleteDailyMissionIds, addMissionIds) fun build(): UpdateDailyGoalRequest = UpdateDailyGoalRequest(deleteDailyMissionIds, addMissionIds) } diff --git a/src/test/kotlin/com/ject/studytrip/trip/fixture/UpdateTripRequestFixture.kt b/src/test/kotlin/com/ject/studytrip/trip/fixture/UpdateTripRequestFixture.kt index 194a741..4be715b 100644 --- a/src/test/kotlin/com/ject/studytrip/trip/fixture/UpdateTripRequestFixture.kt +++ b/src/test/kotlin/com/ject/studytrip/trip/fixture/UpdateTripRequestFixture.kt @@ -3,31 +3,19 @@ package com.ject.studytrip.trip.fixture import com.ject.studytrip.trip.presentation.dto.request.UpdateTripRequest import java.time.LocalDate -class UpdateTripRequestFixture { - private var name: String? = null - private var memo: String? = null - private var category: String? = null - private var endDate: LocalDate? = null +class UpdateTripRequestFixture( + private val name: String? = null, + private val memo: String? = null, + private val category: String? = null, + private val endDate: LocalDate? = null, +) { + fun withName(name: String): UpdateTripRequestFixture = UpdateTripRequestFixture(name, memo, category, endDate) - fun withName(name: String): UpdateTripRequestFixture { - this.name = name - return this - } + fun withMemo(memo: String): UpdateTripRequestFixture = UpdateTripRequestFixture(name, memo, category, endDate) - fun withMemo(memo: String): UpdateTripRequestFixture { - this.memo = memo - return this - } + fun withCategory(category: String): UpdateTripRequestFixture = UpdateTripRequestFixture(name, memo, category, endDate) - fun withCategory(category: String): UpdateTripRequestFixture { - this.category = category - return this - } - - fun withEndDate(endDate: LocalDate): UpdateTripRequestFixture { - this.endDate = endDate - return this - } + fun withEndDate(endDate: LocalDate): UpdateTripRequestFixture = UpdateTripRequestFixture(name, memo, category, endDate) fun build(): UpdateTripRequest = UpdateTripRequest(name, memo, category, endDate) } diff --git a/src/test/kotlin/com/ject/studytrip/trip/presentation/controller/DailyGoalControllerIntegrationTest.kt b/src/test/kotlin/com/ject/studytrip/trip/presentation/controller/DailyGoalControllerIntegrationTest.kt index 219edc6..142b03a 100644 --- a/src/test/kotlin/com/ject/studytrip/trip/presentation/controller/DailyGoalControllerIntegrationTest.kt +++ b/src/test/kotlin/com/ject/studytrip/trip/presentation/controller/DailyGoalControllerIntegrationTest.kt @@ -5,6 +5,7 @@ import com.ject.studytrip.auth.domain.error.AuthErrorCode import com.ject.studytrip.auth.fixture.TokenFixture import com.ject.studytrip.auth.helper.TokenTestHelper import com.ject.studytrip.global.exception.error.CommonErrorCode +import com.ject.studytrip.global.util.EntityExtensions.requireId import com.ject.studytrip.member.domain.model.Member import com.ject.studytrip.member.domain.model.MemberRole import com.ject.studytrip.member.helper.MemberTestHelper @@ -117,10 +118,10 @@ class DailyGoalControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("인증되지 않은 사용자라면 401 Unauthorized를 반환한다.") fun shouldReturnUnauthorizedWhenUnauthenticated() { // given - val request = fixture.withMissionIds(listOf(mission1.id, mission2.id)).build() + val request = fixture.withMissionIds(listOf(mission1.id.requireId(), mission2.id.requireId())).build() // when - val resultActions = getResultActions("", trip.id, request) + val resultActions = getResultActions("", trip.id.requireId(), request) // then resultActions @@ -135,7 +136,7 @@ class DailyGoalControllerIntegrationTest : BaseIntegrationTest() { fun shouldReturnBadRequestWhenTripIdTypeMismatch() { // given val tripId = "abc" - val request = fixture.withMissionIds(listOf(mission1.id, mission2.id)).build() + val request = fixture.withMissionIds(listOf(mission1.id.requireId(), mission2.id.requireId())).build() // when val resultActions = getResultActions(token, tripId, request) @@ -153,10 +154,10 @@ class DailyGoalControllerIntegrationTest : BaseIntegrationTest() { fun shouldReturnBadRequestWhenDailyGoalPomodoroFocusTimeInMinuteIsLessThanOneMinute() { // given val pomodoro = CreatePomodoroRequestFixture().withFocusDurationInMinute(0).build() - val request = fixture.withPomodoro(pomodoro).withMissionIds(listOf(mission1.id, mission2.id)).build() + val request = fixture.withPomodoro(pomodoro).withMissionIds(listOf(mission1.id.requireId(), mission2.id.requireId())).build() // when - val resultActions = getResultActions(token, trip.id, request) + val resultActions = getResultActions(token, trip.id.requireId(), request) // then resultActions @@ -171,10 +172,10 @@ class DailyGoalControllerIntegrationTest : BaseIntegrationTest() { fun shouldReturnBadRequestWhenDailyGoalPomodoroFocusSessionCountIsLessThanOne() { // given val pomodoro = CreatePomodoroRequestFixture().withFocusSessionCount(0).build() - val request = fixture.withPomodoro(pomodoro).withMissionIds(listOf(mission1.id, mission2.id)).build() + val request = fixture.withPomodoro(pomodoro).withMissionIds(listOf(mission1.id.requireId(), mission2.id.requireId())).build() // when - val resultActions = getResultActions(token, trip.id, request) + val resultActions = getResultActions(token, trip.id.requireId(), request) // then resultActions @@ -189,7 +190,7 @@ class DailyGoalControllerIntegrationTest : BaseIntegrationTest() { fun shouldReturnNotFoundWhenTripDoesNotExist() { // given val tripId = -1L - val request = fixture.withMissionIds(listOf(mission1.id, mission2.id)).build() + val request = fixture.withMissionIds(listOf(mission1.id.requireId(), mission2.id.requireId())).build() // when val resultActions = getResultActions(token, tripId, request) @@ -206,10 +207,10 @@ class DailyGoalControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("여행의 소유자가 아니라면 403 Forbidden을 반환한다.") fun shouldReturnForbiddenWhenMemberIsNotTripOwner() { // given - val request = fixture.withMissionIds(listOf(mission1.id, mission2.id)).build() + val request = fixture.withMissionIds(listOf(mission1.id.requireId(), mission2.id.requireId())).build() // when - val resultActions = getResultActions(token, newTrip.id, request) + val resultActions = getResultActions(token, newTrip.id.requireId(), request) // then resultActions @@ -224,10 +225,10 @@ class DailyGoalControllerIntegrationTest : BaseIntegrationTest() { fun shouldReturnBadRequestWhenTripAlreadyDeleted() { // given val deletedTrip = tripTestHelper.saveDeletedTrip(member, TripCategory.COURSE) - val request = fixture.withMissionIds(listOf(mission1.id, mission2.id)).build() + val request = fixture.withMissionIds(listOf(mission1.id.requireId(), mission2.id.requireId())).build() // when - val resultActions = getResultActions(token, deletedTrip.id, request) + val resultActions = getResultActions(token, deletedTrip.id.requireId(), request) // then resultActions @@ -242,10 +243,10 @@ class DailyGoalControllerIntegrationTest : BaseIntegrationTest() { fun shouldReturnBadRequestWhenTripAlreadyCompleted() { // given val completedTrip = tripTestHelper.saveCompletedTrip(member, TripCategory.COURSE) - val request = fixture.withMissionIds(listOf(mission1.id, mission2.id)).build() + val request = fixture.withMissionIds(listOf(mission1.id.requireId(), mission2.id.requireId())).build() // when - val resultActions = getResultActions(token, completedTrip.id, request) + val resultActions = getResultActions(token, completedTrip.id.requireId(), request) // then resultActions @@ -259,10 +260,10 @@ class DailyGoalControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("어떤 미션이 존재하지 않으면 404 NotFound를 반환한다.") fun shouldReturnNotFoundWhenAnyMissionDoesNotExist() { // given - val request = fixture.withMissionIds(listOf(mission1.id, -1L)).build() + val request = fixture.withMissionIds(listOf(mission1.id.requireId(), -1L)).build() // when - val resultActions = getResultActions(token, trip.id, request) + val resultActions = getResultActions(token, trip.id.requireId(), request) // then resultActions @@ -277,10 +278,10 @@ class DailyGoalControllerIntegrationTest : BaseIntegrationTest() { fun shouldReturnBadRequestWhenAnyMissionAlreadyDeleted() { // given val deletedMission = missionTestHelper.saveDeletedMission(stamp) - val request = fixture.withMissionIds(listOf(mission1.id, deletedMission.id)).build() + val request = fixture.withMissionIds(listOf(mission1.id.requireId(), deletedMission.id.requireId())).build() // when - val resultActions = getResultActions(token, trip.id, request) + val resultActions = getResultActions(token, trip.id.requireId(), request) // then resultActions @@ -295,10 +296,10 @@ class DailyGoalControllerIntegrationTest : BaseIntegrationTest() { fun shouldReturnBadRequestWhenAnyMissionAlreadyCompleted() { // given val completedMission = missionTestHelper.saveCompletedMission(stamp) - val request = fixture.withMissionIds(listOf(mission1.id, completedMission.id)).build() + val request = fixture.withMissionIds(listOf(mission1.id.requireId(), completedMission.id.requireId())).build() // when - val resultActions = getResultActions(token, trip.id, request) + val resultActions = getResultActions(token, trip.id.requireId(), request) // then resultActions @@ -314,10 +315,10 @@ class DailyGoalControllerIntegrationTest : BaseIntegrationTest() { // given val otherTrip = tripTestHelper.saveTrip(member, TripCategory.COURSE) stampTestHelper.saveStamp(otherTrip, 2) - val request = fixture.withMissionIds(listOf(mission1.id, mission2.id)).build() + val request = fixture.withMissionIds(listOf(mission1.id.requireId(), mission2.id.requireId())).build() // when - val resultActions = getResultActions(token, otherTrip.id, request) + val resultActions = getResultActions(token, otherTrip.id.requireId(), request) // then resultActions @@ -334,10 +335,10 @@ class DailyGoalControllerIntegrationTest : BaseIntegrationTest() { val otherTrip = tripTestHelper.saveTrip(member, TripCategory.COURSE) val completedStamp = stampTestHelper.saveCompletedStamp(otherTrip, 2) val otherMission = missionTestHelper.saveMission(completedStamp) - val request = fixture.withMissionIds(listOf(otherMission.id)).build() + val request = fixture.withMissionIds(listOf(otherMission.id.requireId())).build() // when - val resultActions = getResultActions(token, otherTrip.id, request) + val resultActions = getResultActions(token, otherTrip.id.requireId(), request) // then resultActions @@ -353,10 +354,10 @@ class DailyGoalControllerIntegrationTest : BaseIntegrationTest() { // given val newStamp = stampTestHelper.saveStamp(trip, 2) val newMission = missionTestHelper.saveMission(newStamp) - val request = fixture.withMissionIds(listOf(newMission.id)).build() + val request = fixture.withMissionIds(listOf(newMission.id.requireId())).build() // when - val resultActions = getResultActions(token, trip.id, request) + val resultActions = getResultActions(token, trip.id.requireId(), request) // then resultActions @@ -370,10 +371,10 @@ class DailyGoalControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("유효한 요청이 들어오면 데일리 목표, 뽀모도로, 데일리 미션들을 생성하고, 데일리 목표를 반환한다.") fun shouldCreateAndReturnDailyGoalWhenRequestIsValid() { // given - val request = fixture.withMissionIds(listOf(mission1.id, mission2.id)).build() + val request = fixture.withMissionIds(listOf(mission1.id.requireId(), mission2.id.requireId())).build() // when - val resultActions = getResultActions(token, trip.id, request) + val resultActions = getResultActions(token, trip.id.requireId(), request) // then resultActions @@ -408,7 +409,7 @@ class DailyGoalControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions("", trip.id, dailyGoal.id, request) + val resultActions = getResultActions("", trip.id.requireId(), dailyGoal.id.requireId(), request) // then resultActions @@ -426,7 +427,7 @@ class DailyGoalControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions(token, tripId, dailyGoal.id, request) + val resultActions = getResultActions(token, tripId, dailyGoal.id.requireId(), request) // then resultActions @@ -444,7 +445,7 @@ class DailyGoalControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions(token, trip.id, dailyGoalId, request) + val resultActions = getResultActions(token, trip.id.requireId(), dailyGoalId, request) // then resultActions @@ -462,7 +463,7 @@ class DailyGoalControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions(token, tripId, dailyGoal.id, request) + val resultActions = getResultActions(token, tripId, dailyGoal.id.requireId(), request) // then resultActions @@ -479,7 +480,7 @@ class DailyGoalControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions(token, newTrip.id, dailyGoal.id, request) + val resultActions = getResultActions(token, newTrip.id.requireId(), dailyGoal.id.requireId(), request) // then resultActions @@ -497,7 +498,7 @@ class DailyGoalControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions(token, deletedTrip.id, dailyGoal.id, request) + val resultActions = getResultActions(token, deletedTrip.id.requireId(), dailyGoal.id.requireId(), request) // then resultActions @@ -515,7 +516,7 @@ class DailyGoalControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions(token, completedTrip.id, dailyGoal.id, request) + val resultActions = getResultActions(token, completedTrip.id.requireId(), dailyGoal.id.requireId(), request) // then resultActions @@ -533,7 +534,7 @@ class DailyGoalControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions(token, trip.id, dailyGoalId, request) + val resultActions = getResultActions(token, trip.id.requireId(), dailyGoalId, request) // then resultActions @@ -551,7 +552,7 @@ class DailyGoalControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions(token, otherTrip.id, dailyGoal.id, request) + val resultActions = getResultActions(token, otherTrip.id.requireId(), dailyGoal.id.requireId(), request) // then resultActions @@ -569,7 +570,7 @@ class DailyGoalControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions(token, trip.id, deletedDailyGoal.id, request) + val resultActions = getResultActions(token, trip.id.requireId(), deletedDailyGoal.id.requireId(), request) // then resultActions @@ -583,10 +584,10 @@ class DailyGoalControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("어떤 데일리 미션이 존재하지 않으면 404 NotFound를 반환한다.") fun shouldReturnNotFoundWhenAnyDailyMissionDoesNotExist() { // given - val request = fixture.withDeleteDailyMissionIds(listOf(dailyMission.id, -1L)).build() + val request = fixture.withDeleteDailyMissionIds(listOf(dailyMission.id.requireId(), -1L)).build() // when - val resultActions = getResultActions(token, trip.id, dailyGoal.id, request) + val resultActions = getResultActions(token, trip.id.requireId(), dailyGoal.id.requireId(), request) // then resultActions @@ -602,10 +603,10 @@ class DailyGoalControllerIntegrationTest : BaseIntegrationTest() { // given val otherDailyGoal = dailyGoalTestHelper.saveDeletedDailyGoal(trip) val otherDailyMission = dailyMissionTestHelper.saveDailyMission(mission1, otherDailyGoal) - val request = fixture.withDeleteDailyMissionIds(listOf(otherDailyMission.id)).build() + val request = fixture.withDeleteDailyMissionIds(listOf(otherDailyMission.id.requireId())).build() // when - val resultActions = getResultActions(token, trip.id, dailyGoal.id, request) + val resultActions = getResultActions(token, trip.id.requireId(), dailyGoal.id.requireId(), request) // then resultActions @@ -620,10 +621,10 @@ class DailyGoalControllerIntegrationTest : BaseIntegrationTest() { fun shouldReturnBadRequestWhenAnyDailyMissionAlreadyDeleted() { // given val deletedDailyMission = dailyMissionTestHelper.saveDeletedDailyMission(mission1, dailyGoal) - val request = fixture.withDeleteDailyMissionIds(listOf(dailyMission.id, deletedDailyMission.id)).build() + val request = fixture.withDeleteDailyMissionIds(listOf(dailyMission.id.requireId(), deletedDailyMission.id.requireId())).build() // when - val resultActions = getResultActions(token, trip.id, dailyGoal.id, request) + val resultActions = getResultActions(token, trip.id.requireId(), dailyGoal.id.requireId(), request) // then resultActions @@ -637,10 +638,10 @@ class DailyGoalControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("어떤 미션이 존재하지 않으면 404 NotFound를 반환한다.") fun shouldReturnNotFoundWhenAnyMissionDoesNotExist() { // given - val request = fixture.withAddMissionIds(listOf(mission1.id, mission2.id, -1L)).build() + val request = fixture.withAddMissionIds(listOf(mission1.id.requireId(), mission2.id.requireId(), -1L)).build() // when - val resultActions = getResultActions(token, trip.id, dailyGoal.id, request) + val resultActions = getResultActions(token, trip.id.requireId(), dailyGoal.id.requireId(), request) // then resultActions @@ -655,10 +656,10 @@ class DailyGoalControllerIntegrationTest : BaseIntegrationTest() { fun shouldReturnBadRequestWhenAnyMissionAlreadyDeleted() { // given val deletedMission = missionTestHelper.saveDeletedMission(stamp) - val request = fixture.withAddMissionIds(listOf(mission1.id, deletedMission.id)).build() + val request = fixture.withAddMissionIds(listOf(mission1.id.requireId(), deletedMission.id.requireId())).build() // when - val resultActions = getResultActions(token, trip.id, dailyGoal.id, request) + val resultActions = getResultActions(token, trip.id.requireId(), dailyGoal.id.requireId(), request) // then resultActions @@ -673,10 +674,10 @@ class DailyGoalControllerIntegrationTest : BaseIntegrationTest() { fun shouldReturnBadRequestWhenAnyMissionAlreadyCompleted() { // given val completedMission = missionTestHelper.saveCompletedMission(stamp) - val request = fixture.withAddMissionIds(listOf(mission1.id, completedMission.id)).build() + val request = fixture.withAddMissionIds(listOf(mission1.id.requireId(), completedMission.id.requireId())).build() // when - val resultActions = getResultActions(token, trip.id, dailyGoal.id, request) + val resultActions = getResultActions(token, trip.id.requireId(), dailyGoal.id.requireId(), request) // then resultActions @@ -693,10 +694,10 @@ class DailyGoalControllerIntegrationTest : BaseIntegrationTest() { val otherTrip = tripTestHelper.saveTrip(member, TripCategory.COURSE) val otherStamp = stampTestHelper.saveStamp(otherTrip, 2) val otherMission = missionTestHelper.saveMission(otherStamp) - val request = fixture.withAddMissionIds(listOf(otherMission.id)).build() + val request = fixture.withAddMissionIds(listOf(otherMission.id.requireId())).build() // when - val resultActions = getResultActions(token, trip.id, dailyGoal.id, request) + val resultActions = getResultActions(token, trip.id.requireId(), dailyGoal.id.requireId(), request) // then resultActions @@ -714,10 +715,10 @@ class DailyGoalControllerIntegrationTest : BaseIntegrationTest() { val completedStamp = stampTestHelper.saveCompletedStamp(otherTrip, 2) val otherDailyGoal = dailyGoalTestHelper.saveDailyGoal(otherTrip) val otherMission = missionTestHelper.saveMission(completedStamp) - val request = fixture.withAddMissionIds(listOf(otherMission.id)).build() + val request = fixture.withAddMissionIds(listOf(otherMission.id.requireId())).build() // when - val resultActions = getResultActions(token, otherTrip.id, otherDailyGoal.id, request) + val resultActions = getResultActions(token, otherTrip.id.requireId(), otherDailyGoal.id.requireId(), request) // then resultActions @@ -733,10 +734,10 @@ class DailyGoalControllerIntegrationTest : BaseIntegrationTest() { // given val newStamp = stampTestHelper.saveStamp(trip, 2) val newMission = missionTestHelper.saveMission(newStamp) - val request = fixture.withAddMissionIds(listOf(newMission.id)).build() + val request = fixture.withAddMissionIds(listOf(newMission.id.requireId())).build() // when - val resultActions = getResultActions(token, trip.id, dailyGoal.id, request) + val resultActions = getResultActions(token, trip.id.requireId(), dailyGoal.id.requireId(), request) // then resultActions @@ -750,10 +751,10 @@ class DailyGoalControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("유효한 요청이 들어오면 데일리 목표를 수정한다.") fun shouldUpdateDailyGoalWhenRequestIsValid() { // given - val request = fixture.withDeleteDailyMissionIds(listOf(dailyMission.id)).build() + val request = fixture.withDeleteDailyMissionIds(listOf(dailyMission.id.requireId())).build() // when - val resultActions = getResultActions(token, trip.id, dailyGoal.id, request) + val resultActions = getResultActions(token, trip.id.requireId(), dailyGoal.id.requireId(), request) // then resultActions @@ -780,7 +781,7 @@ class DailyGoalControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("인증되지 않은 사용자라면 401 Unauthorized를 반환한다.") fun shouldReturnUnauthorizedWhenUnauthenticated() { // when - val resultActions = getResultActions("", trip.id, dailyGoal.id) + val resultActions = getResultActions("", trip.id.requireId(), dailyGoal.id.requireId()) // then resultActions @@ -797,7 +798,7 @@ class DailyGoalControllerIntegrationTest : BaseIntegrationTest() { val tripId = "abc" // when - val resultActions = getResultActions(token, tripId, dailyGoal.id) + val resultActions = getResultActions(token, tripId, dailyGoal.id.requireId()) // then resultActions @@ -814,7 +815,7 @@ class DailyGoalControllerIntegrationTest : BaseIntegrationTest() { val dailyGoalId = "abc" // when - val resultActions = getResultActions(token, trip.id, dailyGoalId) + val resultActions = getResultActions(token, trip.id.requireId(), dailyGoalId) // then resultActions @@ -831,7 +832,7 @@ class DailyGoalControllerIntegrationTest : BaseIntegrationTest() { val tripId = -1L // when - val resultActions = getResultActions(token, tripId, dailyGoal.id) + val resultActions = getResultActions(token, tripId, dailyGoal.id.requireId()) // then resultActions @@ -845,7 +846,7 @@ class DailyGoalControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("여행의 소유자가 아니라면 403 Forbidden을 반환한다.") fun shouldReturnForbiddenWhenMemberIsNotTripOwner() { // when - val resultActions = getResultActions(token, newTrip.id, dailyGoal.id) + val resultActions = getResultActions(token, newTrip.id.requireId(), dailyGoal.id.requireId()) // then resultActions @@ -862,7 +863,7 @@ class DailyGoalControllerIntegrationTest : BaseIntegrationTest() { val deletedTrip = tripTestHelper.saveDeletedTrip(member, TripCategory.COURSE) // when - val resultActions = getResultActions(token, deletedTrip.id, dailyGoal.id) + val resultActions = getResultActions(token, deletedTrip.id.requireId(), dailyGoal.id.requireId()) // then resultActions @@ -879,7 +880,7 @@ class DailyGoalControllerIntegrationTest : BaseIntegrationTest() { val completedTrip = tripTestHelper.saveCompletedTrip(member, TripCategory.COURSE) // when - val resultActions = getResultActions(token, completedTrip.id, dailyGoal.id) + val resultActions = getResultActions(token, completedTrip.id.requireId(), dailyGoal.id.requireId()) // then resultActions @@ -896,7 +897,7 @@ class DailyGoalControllerIntegrationTest : BaseIntegrationTest() { val dailyGoalId = -1L // when - val resultActions = getResultActions(token, trip.id, dailyGoalId) + val resultActions = getResultActions(token, trip.id.requireId(), dailyGoalId) // then resultActions @@ -914,7 +915,7 @@ class DailyGoalControllerIntegrationTest : BaseIntegrationTest() { pomodoroTestHelper.savePomodoro(newDailyGoal) // when - val resultActions = getResultActions(token, trip.id, newDailyGoal.id) + val resultActions = getResultActions(token, trip.id.requireId(), newDailyGoal.id.requireId()) // then resultActions @@ -931,7 +932,7 @@ class DailyGoalControllerIntegrationTest : BaseIntegrationTest() { val deletedDailyGoal = dailyGoalTestHelper.saveDeletedDailyGoal(trip) // when - val resultActions = getResultActions(token, trip.id, deletedDailyGoal.id) + val resultActions = getResultActions(token, trip.id.requireId(), deletedDailyGoal.id.requireId()) // then resultActions @@ -945,7 +946,7 @@ class DailyGoalControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("뽀모도로가 존재하지 않으면 404 NotFound를 반환한다.") fun shouldReturnNotFoundWhenPomodoroDoesNotExist() { // when - val resultActions = getResultActions(token, trip.id, dailyGoal.id) + val resultActions = getResultActions(token, trip.id.requireId(), dailyGoal.id.requireId()) // then resultActions @@ -962,7 +963,7 @@ class DailyGoalControllerIntegrationTest : BaseIntegrationTest() { pomodoroTestHelper.saveDeletedPomodoro(dailyGoal) // when - val resultActions = getResultActions(token, trip.id, dailyGoal.id) + val resultActions = getResultActions(token, trip.id.requireId(), dailyGoal.id.requireId()) // then resultActions @@ -979,7 +980,7 @@ class DailyGoalControllerIntegrationTest : BaseIntegrationTest() { pomodoroTestHelper.savePomodoro(dailyGoal) // when - val resultActions = getResultActions(token, trip.id, dailyGoal.id) + val resultActions = getResultActions(token, trip.id.requireId(), dailyGoal.id.requireId()) // then resultActions @@ -1006,7 +1007,7 @@ class DailyGoalControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("인증되지 않은 사용자라면 401 Unauthorized를 반환한다.") fun shouldReturnUnauthorizedWhenUnauthenticated() { // when - val resultActions = getResultActions("", trip.id, dailyGoal.id) + val resultActions = getResultActions("", trip.id.requireId(), dailyGoal.id.requireId()) // then resultActions @@ -1023,7 +1024,7 @@ class DailyGoalControllerIntegrationTest : BaseIntegrationTest() { val tripId = "abc" // when - val resultActions = getResultActions(token, tripId, dailyGoal.id) + val resultActions = getResultActions(token, tripId, dailyGoal.id.requireId()) // then resultActions @@ -1040,7 +1041,7 @@ class DailyGoalControllerIntegrationTest : BaseIntegrationTest() { val dailyGoalId = "abc" // when - val resultActions = getResultActions(token, trip.id, dailyGoalId) + val resultActions = getResultActions(token, trip.id.requireId(), dailyGoalId) // then resultActions @@ -1057,7 +1058,7 @@ class DailyGoalControllerIntegrationTest : BaseIntegrationTest() { val tripId = -1L // when - val resultActions = getResultActions(token, tripId, dailyGoal.id) + val resultActions = getResultActions(token, tripId, dailyGoal.id.requireId()) // then resultActions @@ -1071,7 +1072,7 @@ class DailyGoalControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("여행의 소유자가 아니라면 403 Forbidden을 반환한다.") fun shouldReturnForbiddenWhenMemberIsNotTripOwner() { // when - val resultActions = getResultActions(token, newTrip.id, dailyGoal.id) + val resultActions = getResultActions(token, newTrip.id.requireId(), dailyGoal.id.requireId()) // then resultActions @@ -1088,7 +1089,7 @@ class DailyGoalControllerIntegrationTest : BaseIntegrationTest() { val deletedTrip = tripTestHelper.saveDeletedTrip(member, TripCategory.COURSE) // when - val resultActions = getResultActions(token, deletedTrip.id, dailyGoal.id) + val resultActions = getResultActions(token, deletedTrip.id.requireId(), dailyGoal.id.requireId()) // then resultActions @@ -1105,7 +1106,7 @@ class DailyGoalControllerIntegrationTest : BaseIntegrationTest() { val completedTrip = tripTestHelper.saveCompletedTrip(member, TripCategory.COURSE) // when - val resultActions = getResultActions(token, completedTrip.id, dailyGoal.id) + val resultActions = getResultActions(token, completedTrip.id.requireId(), dailyGoal.id.requireId()) // then resultActions @@ -1122,7 +1123,7 @@ class DailyGoalControllerIntegrationTest : BaseIntegrationTest() { val dailyGoalId = -1L // when - val resultActions = getResultActions(token, trip.id, dailyGoalId) + val resultActions = getResultActions(token, trip.id.requireId(), dailyGoalId) // then resultActions @@ -1140,7 +1141,7 @@ class DailyGoalControllerIntegrationTest : BaseIntegrationTest() { pomodoroTestHelper.savePomodoro(newDailyGoal) // when - val resultActions = getResultActions(token, trip.id, newDailyGoal.id) + val resultActions = getResultActions(token, trip.id.requireId(), newDailyGoal.id.requireId()) // then resultActions @@ -1157,7 +1158,7 @@ class DailyGoalControllerIntegrationTest : BaseIntegrationTest() { val deletedDailyGoal = dailyGoalTestHelper.saveDeletedDailyGoal(trip) // when - val resultActions = getResultActions(token, trip.id, deletedDailyGoal.id) + val resultActions = getResultActions(token, trip.id.requireId(), deletedDailyGoal.id.requireId()) // then resultActions @@ -1171,7 +1172,7 @@ class DailyGoalControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("뽀모도로가 존재하지 않으면 404 NotFound를 반환한다.") fun shouldReturnNotFoundWhenPomodoroDoesNotExist() { // when - val resultActions = getResultActions(token, trip.id, dailyGoal.id) + val resultActions = getResultActions(token, trip.id.requireId(), dailyGoal.id.requireId()) // then resultActions @@ -1188,7 +1189,7 @@ class DailyGoalControllerIntegrationTest : BaseIntegrationTest() { pomodoroTestHelper.saveDeletedPomodoro(dailyGoal) // when - val resultActions = getResultActions(token, trip.id, dailyGoal.id) + val resultActions = getResultActions(token, trip.id.requireId(), dailyGoal.id.requireId()) // then resultActions @@ -1205,7 +1206,7 @@ class DailyGoalControllerIntegrationTest : BaseIntegrationTest() { pomodoroTestHelper.savePomodoro(dailyGoal) // when - val resultActions = getResultActions(token, trip.id, dailyGoal.id) + val resultActions = getResultActions(token, trip.id.requireId(), dailyGoal.id.requireId()) // then resultActions diff --git a/src/test/kotlin/com/ject/studytrip/trip/presentation/controller/TripControllerIntegrationTest.kt b/src/test/kotlin/com/ject/studytrip/trip/presentation/controller/TripControllerIntegrationTest.kt index 5920324..00ba72e 100644 --- a/src/test/kotlin/com/ject/studytrip/trip/presentation/controller/TripControllerIntegrationTest.kt +++ b/src/test/kotlin/com/ject/studytrip/trip/presentation/controller/TripControllerIntegrationTest.kt @@ -5,6 +5,7 @@ import com.ject.studytrip.auth.domain.error.AuthErrorCode import com.ject.studytrip.auth.fixture.TokenFixture import com.ject.studytrip.auth.helper.TokenTestHelper import com.ject.studytrip.global.exception.error.CommonErrorCode +import com.ject.studytrip.global.util.EntityExtensions.requireId import com.ject.studytrip.member.domain.model.Member import com.ject.studytrip.member.domain.model.MemberRole import com.ject.studytrip.member.helper.MemberTestHelper @@ -210,7 +211,7 @@ class TripControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions("", courseTrip.id, request) + val resultActions = getResultActions("", courseTrip.id.requireId(), request) // then resultActions @@ -245,7 +246,7 @@ class TripControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.withCategory("TEST").build() // when - val resultActions = getResultActions(token, courseTrip.id, request) + val resultActions = getResultActions(token, courseTrip.id.requireId(), request) // then resultActions @@ -262,7 +263,7 @@ class TripControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.withEndDate(LocalDate.now().minusDays(10)).build() // when - val resultActions = getResultActions(token, courseTrip.id, request) + val resultActions = getResultActions(token, courseTrip.id.requireId(), request) // then resultActions @@ -297,7 +298,7 @@ class TripControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions(token, newTrip.id, request) + val resultActions = getResultActions(token, newTrip.id.requireId(), request) // then resultActions @@ -315,7 +316,7 @@ class TripControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions(token, deletedTrip.id, request) + val resultActions = getResultActions(token, deletedTrip.id.requireId(), request) // then resultActions @@ -333,7 +334,7 @@ class TripControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions(token, completedTrip.id, request) + val resultActions = getResultActions(token, completedTrip.id.requireId(), request) // then resultActions @@ -350,7 +351,7 @@ class TripControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.withEndDate(LocalDate.now().plusDays(3)).build() // when - val resultActions = getResultActions(token, courseTrip.id, request) + val resultActions = getResultActions(token, courseTrip.id.requireId(), request) // then resultActions @@ -376,7 +377,7 @@ class TripControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("인증되지 않은 사용자라면 401 Unauthorized를 반환한다.") fun shouldReturnUnauthorizedWhenUnauthenticated() { // when - val resultActions = getResultActions("", courseTrip.id) + val resultActions = getResultActions("", courseTrip.id.requireId()) // then resultActions @@ -424,7 +425,7 @@ class TripControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("여행의 소유자가 아니라면 403 Forbidden을 반환한다.") fun shouldReturnForbiddenWhenMemberIsNotTripOwner() { // when - val resultActions = getResultActions(token, newTrip.id) + val resultActions = getResultActions(token, newTrip.id.requireId()) // then resultActions @@ -441,7 +442,7 @@ class TripControllerIntegrationTest : BaseIntegrationTest() { val deletedTrip = tripTestHelper.saveDeletedTrip(member, TripCategory.COURSE) // when - val resultActions = getResultActions(token, deletedTrip.id) + val resultActions = getResultActions(token, deletedTrip.id.requireId()) // then resultActions @@ -458,7 +459,7 @@ class TripControllerIntegrationTest : BaseIntegrationTest() { val completedTrip = tripTestHelper.saveCompletedTrip(member, TripCategory.COURSE) // when - val resultActions = getResultActions(token, completedTrip.id) + val resultActions = getResultActions(token, completedTrip.id.requireId()) // then resultActions @@ -472,7 +473,7 @@ class TripControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("특정 여행을 삭제한다.") fun shouldDeleteTrip() { // when - val resultActions = getResultActions(token, courseTrip.id) + val resultActions = getResultActions(token, courseTrip.id.requireId()) // then resultActions @@ -498,7 +499,7 @@ class TripControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("인증되지 않은 사용자라면 401 Unauthorized를 반환한다.") fun shouldReturnUnauthorizedWhenUnauthenticated() { // when - val resultActions = getResultActions("", courseTrip.id) + val resultActions = getResultActions("", courseTrip.id.requireId()) // then resultActions @@ -546,7 +547,7 @@ class TripControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("여행의 소유자가 아니라면 403 Forbidden을 반환한다.") fun shouldReturnForbiddenWhenMemberIsNotTripOwner() { // when - val resultActions = getResultActions(token, newTrip.id) + val resultActions = getResultActions(token, newTrip.id.requireId()) // then resultActions @@ -563,7 +564,7 @@ class TripControllerIntegrationTest : BaseIntegrationTest() { val deletedTrip = tripTestHelper.saveDeletedTrip(member, TripCategory.COURSE) // when - val resultActions = getResultActions(token, deletedTrip.id) + val resultActions = getResultActions(token, deletedTrip.id.requireId()) // then resultActions @@ -580,7 +581,7 @@ class TripControllerIntegrationTest : BaseIntegrationTest() { val completedTrip = tripTestHelper.saveCompletedTrip(member, TripCategory.COURSE) // when - val resultActions = getResultActions(token, completedTrip.id) + val resultActions = getResultActions(token, completedTrip.id.requireId()) // then resultActions @@ -597,7 +598,7 @@ class TripControllerIntegrationTest : BaseIntegrationTest() { stampTestHelper.saveStamp(courseTrip, 1) // when - val resultActions = getResultActions(token, courseTrip.id) + val resultActions = getResultActions(token, courseTrip.id.requireId()) // then resultActions @@ -615,7 +616,7 @@ class TripControllerIntegrationTest : BaseIntegrationTest() { stampTestHelper.saveCompletedStamp(courseTrip, 2) // when - val resultActions = getResultActions(token, courseTrip.id) + val resultActions = getResultActions(token, courseTrip.id.requireId()) // then resultActions @@ -742,7 +743,7 @@ class TripControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("인증되지 않은 사용자라면 401 Unauthorized를 반환한다.") fun shouldReturnUnauthorizedWhenUnauthenticated() { // when - val resultActions = getResultActions("", courseTrip.id) + val resultActions = getResultActions("", courseTrip.id.requireId()) // then resultActions @@ -790,7 +791,7 @@ class TripControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("여행의 소유자가 아니라면 403 Forbidden을 반환한다.") fun shouldReturnForbiddenWhenMemberIsNotTripOwner() { // when - val resultActions = getResultActions(token, newTrip.id) + val resultActions = getResultActions(token, newTrip.id.requireId()) // then resultActions @@ -807,7 +808,7 @@ class TripControllerIntegrationTest : BaseIntegrationTest() { val deletedTrip = tripTestHelper.saveDeletedTrip(member, TripCategory.COURSE) // when - val resultActions = getResultActions(token, deletedTrip.id) + val resultActions = getResultActions(token, deletedTrip.id.requireId()) // then resultActions @@ -824,7 +825,7 @@ class TripControllerIntegrationTest : BaseIntegrationTest() { val completedTrip = tripTestHelper.saveCompletedTrip(member, TripCategory.COURSE) // when - val resultActions = getResultActions(token, completedTrip.id) + val resultActions = getResultActions(token, completedTrip.id.requireId()) // then resultActions @@ -838,7 +839,7 @@ class TripControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("특정 여행을 상세 조회합니다.") fun shouldReturnTrip() { // when - val resultActions = getResultActions(token, courseTrip.id) + val resultActions = getResultActions(token, courseTrip.id.requireId()) // then resultActions diff --git a/src/test/kotlin/com/ject/studytrip/trip/presentation/controller/TripReportControllerIntegrationTest.kt b/src/test/kotlin/com/ject/studytrip/trip/presentation/controller/TripReportControllerIntegrationTest.kt index 5b4c9ff..acf6a02 100644 --- a/src/test/kotlin/com/ject/studytrip/trip/presentation/controller/TripReportControllerIntegrationTest.kt +++ b/src/test/kotlin/com/ject/studytrip/trip/presentation/controller/TripReportControllerIntegrationTest.kt @@ -5,6 +5,7 @@ import com.ject.studytrip.auth.domain.error.AuthErrorCode import com.ject.studytrip.auth.fixture.TokenFixture import com.ject.studytrip.auth.helper.TokenTestHelper import com.ject.studytrip.global.exception.error.CommonErrorCode +import com.ject.studytrip.global.util.EntityExtensions.requireId import com.ject.studytrip.image.domain.error.ImageErrorCode import com.ject.studytrip.image.infra.s3.provider.S3ImageStorageProvider import com.ject.studytrip.member.domain.model.Member @@ -146,7 +147,7 @@ class TripReportControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("학습 로그 ID 목록 중 존재하지 않는 ID가 존재하면 404 NotFound를 반환한다.") fun shouldReturnNotFoundWhenAnyStudyLogIdDoesNotExist() { // given - val request = fixture.withStudyLogIds(listOf(studyLog1.id, -1L)).build() + val request = fixture.withStudyLogIds(listOf(studyLog1.id.requireId(), -1L)).build() // when val resultActions = getResultActions(token, request) @@ -164,7 +165,7 @@ class TripReportControllerIntegrationTest : BaseIntegrationTest() { fun shouldReturnBadRequestWhenAnyStudyLogAlreadyDeleted() { // given val deletedStudyLog = studyLogTestHelper.saveDeletedStudyLog(member, dailyGoal) - val request = fixture.withStudyLogIds(listOf(deletedStudyLog.id, studyLog2.id)).build() + val request = fixture.withStudyLogIds(listOf(deletedStudyLog.id.requireId(), studyLog2.id.requireId())).build() // when val resultActions = getResultActions(token, request) @@ -181,7 +182,7 @@ class TripReportControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("유효한 요청이 들어오면 여행 리포트를 생성하고 반환한다.") fun shouldCreateTripReportWhenRequestIsValid() { // given - val request = fixture.withStudyLogIds(listOf(studyLog1.id, studyLog2.id)).build() + val request = fixture.withStudyLogIds(listOf(studyLog1.id.requireId(), studyLog2.id.requireId())).build() // when val resultActions = getResultActions(token, request) @@ -211,7 +212,7 @@ class TripReportControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("인증되지 않은 사용자라면 401 Unauthorized를 반환한다.") fun shouldReturnUnauthorizedWhenUnauthenticated() { // when - val resultActions = getResultActions("", tripReport.id) + val resultActions = getResultActions("", tripReport.id.requireId()) // then resultActions @@ -262,7 +263,7 @@ class TripReportControllerIntegrationTest : BaseIntegrationTest() { val newTripReport = tripReportTestHelper.saveTripReport(newMember) // when - val resultActions = getResultActions(token, newTripReport.id) + val resultActions = getResultActions(token, newTripReport.id.requireId()) // then resultActions @@ -279,7 +280,7 @@ class TripReportControllerIntegrationTest : BaseIntegrationTest() { val deletedTripReport = tripReportTestHelper.saveDeletedTripReport(member) // when - val resultActions = getResultActions(token, deletedTripReport.id) + val resultActions = getResultActions(token, deletedTripReport.id.requireId()) // then resultActions @@ -293,7 +294,7 @@ class TripReportControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("특정 여행 리포트를 삭제한다.") fun shouldDeleteTripReport() { // when - val resultActions = getResultActions(token, tripReport.id) + val resultActions = getResultActions(token, tripReport.id.requireId()) // then resultActions @@ -327,7 +328,7 @@ class TripReportControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions("", tripReport.id, request) + val resultActions = getResultActions("", tripReport.id.requireId(), request) // then resultActions @@ -344,7 +345,7 @@ class TripReportControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.withOriginFilename("").build() // when - val resultActions = getResultActions(token, tripReport.id, request) + val resultActions = getResultActions(token, tripReport.id.requireId(), request) // then resultActions @@ -361,7 +362,7 @@ class TripReportControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.withOriginFilename("test.pdf").build() // when - val resultActions = getResultActions(token, tripReport.id, request) + val resultActions = getResultActions(token, tripReport.id.requireId(), request) // then resultActions @@ -379,7 +380,7 @@ class TripReportControllerIntegrationTest : BaseIntegrationTest() { given(s3ImageStorageProvider.issuePresignedUrl(anyString())).willReturn("https://mocked-presigned-url.com") // when - val resultActions = getResultActions(token, tripReport.id, request) + val resultActions = getResultActions(token, tripReport.id.requireId(), request) // then resultActions @@ -419,7 +420,7 @@ class TripReportControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.build() // when - val resultActions = getResultActions("", tripReport.id, request) + val resultActions = getResultActions("", tripReport.id.requireId(), request) // then resultActions @@ -454,7 +455,7 @@ class TripReportControllerIntegrationTest : BaseIntegrationTest() { val request = fixture.withTmpKey("").build() // when - val resultActions = getResultActions(token, tripReport.id, request) + val resultActions = getResultActions(token, tripReport.id.requireId(), request) // then resultActions @@ -485,7 +486,7 @@ class TripReportControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("인증되지 않은 사용자라면 401 Unauthorized를 반환한다.") fun shouldReturnUnauthorizedWhenUnauthenticated() { // when - val resultActions = getResultActions("", completedTrip.id, DEFAULT_PAGE, DEFAULT_SIZE) + val resultActions = getResultActions("", completedTrip.id.requireId(), DEFAULT_PAGE, DEFAULT_SIZE) // then resultActions @@ -503,7 +504,7 @@ class TripReportControllerIntegrationTest : BaseIntegrationTest() { val size = "abc" // when - val resultActions = getResultActions(token, completedTrip.id, page, size) + val resultActions = getResultActions(token, completedTrip.id.requireId(), page, size) // then resultActions @@ -521,7 +522,7 @@ class TripReportControllerIntegrationTest : BaseIntegrationTest() { val size = "-1" // when - val resultActions = getResultActions(token, completedTrip.id, page, size) + val resultActions = getResultActions(token, completedTrip.id.requireId(), page, size) // then resultActions @@ -572,7 +573,7 @@ class TripReportControllerIntegrationTest : BaseIntegrationTest() { val newTrip = tripTestHelper.saveTrip(newMember, TripCategory.COURSE) // when - val resultActions = getResultActions(token, newTrip.id, DEFAULT_PAGE, DEFAULT_SIZE) + val resultActions = getResultActions(token, newTrip.id.requireId(), DEFAULT_PAGE, DEFAULT_SIZE) // then resultActions @@ -589,7 +590,7 @@ class TripReportControllerIntegrationTest : BaseIntegrationTest() { val deletedTrip = tripTestHelper.saveDeletedTrip(member, TripCategory.COURSE) // when - val resultActions = getResultActions(token, deletedTrip.id, DEFAULT_PAGE, DEFAULT_SIZE) + val resultActions = getResultActions(token, deletedTrip.id.requireId(), DEFAULT_PAGE, DEFAULT_SIZE) // then resultActions @@ -606,7 +607,7 @@ class TripReportControllerIntegrationTest : BaseIntegrationTest() { val trip = tripTestHelper.saveTrip(member, TripCategory.COURSE) // when - val resultActions = getResultActions(token, trip.id, DEFAULT_PAGE, DEFAULT_SIZE) + val resultActions = getResultActions(token, trip.id.requireId(), DEFAULT_PAGE, DEFAULT_SIZE) // then resultActions @@ -620,7 +621,7 @@ class TripReportControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("유효한 여행 ID가 들어오면 여행 회고 정보를 반환한다.") fun shouldReturnTripRetrospectWhenTripIdIsValid() { // when - val resultActions = getResultActions(token, completedTrip.id, DEFAULT_PAGE, DEFAULT_SIZE) + val resultActions = getResultActions(token, completedTrip.id.requireId(), DEFAULT_PAGE, DEFAULT_SIZE) // then resultActions @@ -689,7 +690,7 @@ class TripReportControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("인증되지 않은 사용자라면 401 Unauthorized를 반환한다.") fun shouldReturnUnauthorizedWhenUnauthenticated() { // when - val resultActions = getResultActions("", tripReport.id, DEFAULT_PAGE, DEFAULT_SIZE) + val resultActions = getResultActions("", tripReport.id.requireId(), DEFAULT_PAGE, DEFAULT_SIZE) // then resultActions @@ -707,7 +708,7 @@ class TripReportControllerIntegrationTest : BaseIntegrationTest() { val size = "abc" // when - val resultActions = getResultActions(token, tripReport.id, page, size) + val resultActions = getResultActions(token, tripReport.id.requireId(), page, size) // then resultActions @@ -725,7 +726,7 @@ class TripReportControllerIntegrationTest : BaseIntegrationTest() { val size = "-1" // when - val resultActions = getResultActions(token, tripReport.id, page, size) + val resultActions = getResultActions(token, tripReport.id.requireId(), page, size) // then resultActions @@ -776,7 +777,7 @@ class TripReportControllerIntegrationTest : BaseIntegrationTest() { val newTripReport = tripReportTestHelper.saveTripReport(newMember) // when - val resultActions = getResultActions(token, newTripReport.id, DEFAULT_PAGE, DEFAULT_SIZE) + val resultActions = getResultActions(token, newTripReport.id.requireId(), DEFAULT_PAGE, DEFAULT_SIZE) // then resultActions @@ -793,7 +794,7 @@ class TripReportControllerIntegrationTest : BaseIntegrationTest() { val deletedTripReport = tripReportTestHelper.saveDeletedTripReport(member) // when - val resultActions = getResultActions(token, deletedTripReport.id, DEFAULT_PAGE, DEFAULT_SIZE) + val resultActions = getResultActions(token, deletedTripReport.id.requireId(), DEFAULT_PAGE, DEFAULT_SIZE) // then resultActions @@ -807,7 +808,7 @@ class TripReportControllerIntegrationTest : BaseIntegrationTest() { @DisplayName("특정 여행 리포트를 상세 조회하고 반환한다.") fun shouldReturnTripReport() { // when - val resultActions = getResultActions(token, tripReport.id, DEFAULT_PAGE, DEFAULT_SIZE) + val resultActions = getResultActions(token, tripReport.id.requireId(), DEFAULT_PAGE, DEFAULT_SIZE) // then resultActions